├── .gitattributes ├── .github └── workflows │ ├── ci-interpreter.yml │ ├── ci-spec.yml │ ├── mirror-to-master.yml │ └── w3c-publish.yml ├── .gitignore ├── .gitmodules ├── Contributing.md ├── LICENSE ├── README.md ├── document ├── Makefile ├── README.md ├── core │ ├── .gitignore │ ├── LICENSE │ ├── Makefile │ ├── README.md │ ├── appendix │ │ ├── algorithm.rst │ │ ├── changes.rst │ │ ├── custom.rst │ │ ├── embedding.rst │ │ ├── implementation.rst │ │ ├── index-instructions.py │ │ ├── index-rules.rst │ │ ├── index-types.rst │ │ ├── index.rst │ │ └── properties.rst │ ├── binary │ │ ├── conventions.rst │ │ ├── index.rst │ │ ├── instructions.rst │ │ ├── modules.rst │ │ ├── types.rst │ │ └── values.rst │ ├── conf.py │ ├── exec │ │ ├── conventions.rst │ │ ├── index.rst │ │ ├── instructions.rst │ │ ├── modules.rst │ │ ├── numerics.rst │ │ ├── runtime.rst │ │ ├── types.rst │ │ └── values.rst │ ├── index.bs │ ├── index.rst │ ├── intro │ │ ├── index.rst │ │ ├── introduction.rst │ │ └── overview.rst │ ├── make.bat │ ├── static │ │ ├── custom.css │ │ └── webassembly.png │ ├── syntax │ │ ├── conventions.rst │ │ ├── index.rst │ │ ├── instructions.rst │ │ ├── modules.rst │ │ ├── types.rst │ │ └── values.rst │ ├── text │ │ ├── conventions.rst │ │ ├── index.rst │ │ ├── instructions.rst │ │ ├── lexical.rst │ │ ├── modules.rst │ │ ├── types.rst │ │ └── values.rst │ ├── util │ │ ├── README.htmldiff.pl │ │ ├── bikeshed │ │ │ └── conf.py │ │ ├── bikeshed_fixup.py │ │ ├── check_macros.sh │ │ ├── katex_fix.patch │ │ ├── macros.def │ │ ├── mathdef.py │ │ ├── mathdefbs.py │ │ ├── mathjax2katex.py │ │ └── pseudo-lexer.py │ └── valid │ │ ├── conventions.rst │ │ ├── index.rst │ │ ├── instructions.rst │ │ ├── matching.rst │ │ ├── modules.rst │ │ └── types.rst ├── deploy.sh ├── index.html ├── js-api │ ├── Makefile │ └── index.bs ├── util │ └── htmldiff.pl └── web-api │ ├── Makefile │ └── index.bs ├── interpreter ├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── binary │ ├── decode.ml │ ├── decode.mli │ ├── encode.ml │ ├── encode.mli │ ├── utf8.ml │ └── utf8.mli ├── dune ├── dune-project ├── exec │ ├── eval.ml │ ├── eval.mli │ ├── eval_num.ml │ ├── eval_num.mli │ ├── eval_vec.ml │ ├── eval_vec.mli │ ├── f32.ml │ ├── f32_convert.ml │ ├── f32_convert.mli │ ├── f64.ml │ ├── f64_convert.ml │ ├── f64_convert.mli │ ├── fxx.ml │ ├── i16.ml │ ├── i32.ml │ ├── i32_convert.ml │ ├── i32_convert.mli │ ├── i64.ml │ ├── i64_convert.ml │ ├── i64_convert.mli │ ├── i8.ml │ ├── ixx.ml │ ├── v128.ml │ └── v128.mli ├── host │ ├── env.ml │ └── spectest.ml ├── jslib │ └── wast.ml ├── main │ ├── flags.ml │ └── main.ml ├── runtime │ ├── aggr.ml │ ├── aggr.mli │ ├── data.ml │ ├── data.mli │ ├── elem.ml │ ├── elem.mli │ ├── extern.ml │ ├── extern.mli │ ├── func.ml │ ├── func.mli │ ├── global.ml │ ├── global.mli │ ├── i31.ml │ ├── i31.mli │ ├── instance.ml │ ├── memory.ml │ ├── memory.mli │ ├── table.ml │ ├── table.mli │ └── value.ml ├── script │ ├── import.ml │ ├── import.mli │ ├── js.ml │ ├── js.mli │ ├── run.ml │ ├── run.mli │ └── script.ml ├── syntax │ ├── ast.ml │ ├── free.ml │ ├── free.mli │ ├── operators.ml │ ├── pack.ml │ └── types.ml ├── text │ ├── arrange.ml │ ├── arrange.mli │ ├── lexer.mli │ ├── lexer.mll │ ├── parse.ml │ ├── parse.mli │ ├── parse_error.ml │ ├── parser.mly │ ├── print.ml │ └── print.mli ├── unittest │ └── smallint.ml ├── util │ ├── error.ml │ ├── error.mli │ ├── lib.ml │ ├── lib.mli │ ├── sexpr.ml │ ├── sexpr.mli │ ├── source.ml │ └── source.mli ├── valid │ ├── match.ml │ ├── match.mli │ ├── valid.ml │ └── valid.mli └── wasm.opam ├── papers ├── LICENSE ├── README.md ├── oopsla2019.pdf └── pldi2017.pdf ├── proposals ├── README.md ├── bulk-memory-operations │ └── Overview.md ├── function-references │ └── Overview.md ├── gc │ ├── Charter.md │ ├── MVP-JS.md │ ├── MVP.md │ ├── Overview.md │ └── Post-MVP.md ├── multi-value │ └── Overview.md ├── nontrapping-float-to-int-conversion │ └── Overview.md ├── reference-types │ └── Overview.md ├── sign-extension-ops │ └── Overview.md ├── simd │ ├── BinarySIMD.md │ ├── ImplementationStatus.md │ ├── NewOpcodes.md │ ├── SIMD.md │ ├── TextSIMD.md │ ├── W3CTAG-SIMDExplainer.md │ └── WebAssembly-SIMD-May-2017.pdf └── tail-call │ └── Overview.md ├── test ├── LICENSE ├── README.md ├── Todo.md ├── build.py ├── core │ ├── .gitignore │ ├── README.md │ ├── address.wast │ ├── align.wast │ ├── binary-leb128.wast │ ├── binary.wast │ ├── block.wast │ ├── br.wast │ ├── br_if.wast │ ├── br_on_non_null.wast │ ├── br_on_null.wast │ ├── br_table.wast │ ├── bulk.wast │ ├── call.wast │ ├── call_indirect.wast │ ├── call_ref.wast │ ├── comments.wast │ ├── const.wast │ ├── conversions.wast │ ├── custom.wast │ ├── data.wast │ ├── elem.wast │ ├── endianness.wast │ ├── exports.wast │ ├── f32.wast │ ├── f32_bitwise.wast │ ├── f32_cmp.wast │ ├── f64.wast │ ├── f64_bitwise.wast │ ├── f64_cmp.wast │ ├── fac.wast │ ├── float_exprs.wast │ ├── float_literals.wast │ ├── float_memory.wast │ ├── float_misc.wast │ ├── forward.wast │ ├── func.wast │ ├── func_ptrs.wast │ ├── gc │ │ ├── array.wast │ │ ├── array_copy.wast │ │ ├── array_fill.wast │ │ ├── array_init_data.wast │ │ ├── array_init_elem.wast │ │ ├── array_new_data.wast │ │ ├── array_new_elem.wast │ │ ├── binary-gc.wast │ │ ├── br_on_cast.wast │ │ ├── br_on_cast_fail.wast │ │ ├── extern.wast │ │ ├── i31.wast │ │ ├── ref_cast.wast │ │ ├── ref_eq.wast │ │ ├── ref_test.wast │ │ ├── struct.wast │ │ ├── type-subtyping-invalid.wast │ │ └── type-subtyping.wast │ ├── global.wast │ ├── i32.wast │ ├── i64.wast │ ├── if.wast │ ├── imports.wast │ ├── inline-module.wast │ ├── int_exprs.wast │ ├── int_literals.wast │ ├── labels.wast │ ├── left-to-right.wast │ ├── linking.wast │ ├── load.wast │ ├── local_get.wast │ ├── local_init.wast │ ├── local_set.wast │ ├── local_tee.wast │ ├── loop.wast │ ├── memory.wast │ ├── memory_copy.wast │ ├── memory_fill.wast │ ├── memory_grow.wast │ ├── memory_init.wast │ ├── memory_redundancy.wast │ ├── memory_size.wast │ ├── memory_trap.wast │ ├── names.wast │ ├── nop.wast │ ├── obsolete-keywords.wast │ ├── ref.wast │ ├── ref_as_non_null.wast │ ├── ref_func.wast │ ├── ref_is_null.wast │ ├── ref_null.wast │ ├── return.wast │ ├── return_call.wast │ ├── return_call_indirect.wast │ ├── return_call_ref.wast │ ├── run.py │ ├── select.wast │ ├── simd │ │ ├── meta │ │ │ ├── README.md │ │ │ ├── gen_tests.py │ │ │ ├── simd.py │ │ │ ├── simd_arithmetic.py │ │ │ ├── simd_bitwise.py │ │ │ ├── simd_compare.py │ │ │ ├── simd_ext_mul.py │ │ │ ├── simd_extadd_pairwise.py │ │ │ ├── simd_f32x4.py │ │ │ ├── simd_f32x4_arith.py │ │ │ ├── simd_f32x4_cmp.py │ │ │ ├── simd_f32x4_pmin_pmax.py │ │ │ ├── simd_f32x4_rounding.py │ │ │ ├── simd_f64x2.py │ │ │ ├── simd_f64x2_arith.py │ │ │ ├── simd_f64x2_cmp.py │ │ │ ├── simd_f64x2_pmin_pmax.py │ │ │ ├── simd_f64x2_rounding.py │ │ │ ├── simd_float_op.py │ │ │ ├── simd_i16x8_arith.py │ │ │ ├── simd_i16x8_cmp.py │ │ │ ├── simd_i16x8_q15mulr_sat_s.py │ │ │ ├── simd_i32x4_arith.py │ │ │ ├── simd_i32x4_cmp.py │ │ │ ├── simd_i32x4_dot_i16x8.py │ │ │ ├── simd_i64x2_arith.py │ │ │ ├── simd_i64x2_cmp.py │ │ │ ├── simd_i8x16_arith.py │ │ │ ├── simd_i8x16_cmp.py │ │ │ ├── simd_int_arith2.py │ │ │ ├── simd_int_to_int_extend.py │ │ │ ├── simd_int_trunc_sat_float.py │ │ │ ├── simd_integer_op.py │ │ │ ├── simd_lane_value.py │ │ │ ├── simd_load_lane.py │ │ │ ├── simd_sat_arith.py │ │ │ ├── simd_store_lane.py │ │ │ └── test_assert.py │ │ ├── simd_address.wast │ │ ├── simd_align.wast │ │ ├── simd_bit_shift.wast │ │ ├── simd_bitwise.wast │ │ ├── simd_boolean.wast │ │ ├── simd_const.wast │ │ ├── simd_conversions.wast │ │ ├── simd_f32x4.wast │ │ ├── simd_f32x4_arith.wast │ │ ├── simd_f32x4_cmp.wast │ │ ├── simd_f32x4_pmin_pmax.wast │ │ ├── simd_f32x4_rounding.wast │ │ ├── simd_f64x2.wast │ │ ├── simd_f64x2_arith.wast │ │ ├── simd_f64x2_cmp.wast │ │ ├── simd_f64x2_pmin_pmax.wast │ │ ├── simd_f64x2_rounding.wast │ │ ├── simd_i16x8_arith.wast │ │ ├── simd_i16x8_arith2.wast │ │ ├── simd_i16x8_cmp.wast │ │ ├── simd_i16x8_extadd_pairwise_i8x16.wast │ │ ├── simd_i16x8_extmul_i8x16.wast │ │ ├── simd_i16x8_q15mulr_sat_s.wast │ │ ├── simd_i16x8_sat_arith.wast │ │ ├── simd_i32x4_arith.wast │ │ ├── simd_i32x4_arith2.wast │ │ ├── simd_i32x4_cmp.wast │ │ ├── simd_i32x4_dot_i16x8.wast │ │ ├── simd_i32x4_extadd_pairwise_i16x8.wast │ │ ├── simd_i32x4_extmul_i16x8.wast │ │ ├── simd_i32x4_trunc_sat_f32x4.wast │ │ ├── simd_i32x4_trunc_sat_f64x2.wast │ │ ├── simd_i64x2_arith.wast │ │ ├── simd_i64x2_arith2.wast │ │ ├── simd_i64x2_cmp.wast │ │ ├── simd_i64x2_extmul_i32x4.wast │ │ ├── simd_i8x16_arith.wast │ │ ├── simd_i8x16_arith2.wast │ │ ├── simd_i8x16_cmp.wast │ │ ├── simd_i8x16_sat_arith.wast │ │ ├── simd_int_to_int_extend.wast │ │ ├── simd_lane.wast │ │ ├── simd_linking.wast │ │ ├── simd_load.wast │ │ ├── simd_load16_lane.wast │ │ ├── simd_load32_lane.wast │ │ ├── simd_load64_lane.wast │ │ ├── simd_load8_lane.wast │ │ ├── simd_load_extend.wast │ │ ├── simd_load_splat.wast │ │ ├── simd_load_zero.wast │ │ ├── simd_splat.wast │ │ ├── simd_store.wast │ │ ├── simd_store16_lane.wast │ │ ├── simd_store32_lane.wast │ │ ├── simd_store64_lane.wast │ │ └── simd_store8_lane.wast │ ├── skip-stack-guard-page.wast │ ├── stack.wast │ ├── start.wast │ ├── store.wast │ ├── switch.wast │ ├── table-sub.wast │ ├── table.wast │ ├── table_copy.wast │ ├── table_fill.wast │ ├── table_get.wast │ ├── table_grow.wast │ ├── table_init.wast │ ├── table_set.wast │ ├── table_size.wast │ ├── token.wast │ ├── traps.wast │ ├── type-canon.wast │ ├── type-equivalence.wast │ ├── type-rec.wast │ ├── type.wast │ ├── unreachable.wast │ ├── unreached-invalid.wast │ ├── unreached-valid.wast │ ├── unwind.wast │ ├── utf8-custom-section-id.wast │ ├── utf8-import-field.wast │ ├── utf8-import-module.wast │ └── utf8-invalid-encoding.wast ├── harness │ ├── async_index.js │ ├── sync_index.js │ ├── testharness.css │ ├── testharness.js │ └── testharnessreport.js ├── js-api │ ├── LICENSE.md │ ├── README.md │ ├── assertions.js │ ├── bad-imports.js │ ├── constructor │ ├── error-interfaces-no-symbol-tostringtag.js │ ├── functions │ │ └── helper.js │ ├── gc │ │ ├── casts.tentative.any.js │ │ ├── default-value.tentative.any.js │ │ ├── exported-object.tentative.any.js │ │ └── i31.tentative.any.js │ ├── global │ │ ├── constructor.any.js │ │ ├── toString.any.js │ │ ├── value-get-set.any.js │ │ └── valueOf.any.js │ ├── instance │ │ ├── constructor-bad-imports.any.js │ │ ├── constructor-caching.any.js │ │ ├── constructor.any.js │ │ ├── exports.any.js │ │ └── toString.any.js │ ├── instanceTestFactory.js │ ├── interface.any.js │ ├── limits.any.js │ ├── memory │ │ ├── assertions.js │ │ ├── buffer.any.js │ │ ├── constructor.any.js │ │ ├── grow.any.js │ │ └── toString.any.js │ ├── module │ │ ├── constructor.any.js │ │ ├── customSections.any.js │ │ ├── exports.any.js │ │ ├── imports.any.js │ │ └── toString.any.js │ ├── prototypes.any.js │ ├── table │ │ ├── assertions.js │ │ ├── constructor.any.js │ │ ├── get-set.any.js │ │ ├── grow.any.js │ │ ├── length.any.js │ │ └── toString.any.js │ └── wasm-module-builder.js ├── meta │ ├── Makefile │ ├── README.md │ ├── common.js │ ├── generate_memory_copy.js │ ├── generate_memory_fill.js │ ├── generate_memory_init.js │ ├── generate_table_copy.js │ ├── generate_table_init.js │ └── noderun.sh └── sync-js-api.py ├── w3c.json └── wasm-specs.bib /.gitattributes: -------------------------------------------------------------------------------- 1 | *.rst linguist-documentation=false 2 | document/* linguist-documentation=false 3 | document/*.rst linguist-documentation=false 4 | document/*/*.rst linguist-documentation=false 5 | test/harness/wast.js linguist-vendored 6 | test/harness/testharness* linguist-vendored 7 | 8 | -------------------------------------------------------------------------------- /.github/workflows/ci-interpreter.yml: -------------------------------------------------------------------------------- 1 | name: CI for interpreter & tests 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | paths: [ .github/**, interpreter/**, test/** ] 7 | 8 | pull_request: 9 | branches: [ main ] 10 | paths: [ .github/**, interpreter/**, test/** ] 11 | 12 | # Allows you to run this workflow manually from the Actions tab 13 | workflow_dispatch: 14 | 15 | jobs: 16 | interpreter: 17 | runs-on: ubuntu-latest 18 | steps: 19 | - name: Checkout repo 20 | uses: actions/checkout@v2 21 | - name: Setup OCaml 22 | uses: ocaml/setup-ocaml@v2 23 | with: 24 | ocaml-compiler: 4.14.x 25 | - name: Setup OCaml tools 26 | run: opam install --yes ocamlfind.1.9.5 js_of_ocaml.4.0.0 js_of_ocaml-ppx.4.0.0 27 | - name: Setup Node.js 28 | uses: actions/setup-node@v2 29 | with: 30 | node-version: 19.x 31 | - name: Build interpreter 32 | run: cd interpreter && opam exec make 33 | - name: Run tests 34 | # TODO: disable node.js run until it fully implements proposal 35 | # run: cd interpreter && opam exec make JS=node ci 36 | run: cd interpreter && opam exec make ci 37 | -------------------------------------------------------------------------------- /.github/workflows/mirror-to-master.yml: -------------------------------------------------------------------------------- 1 | name: Mirror main branch to master branch 2 | 3 | on: 4 | push: 5 | branches: 6 | - 'main' 7 | 8 | jobs: 9 | mirror_job: 10 | runs-on: ubuntu-latest 11 | name: Mirror main branch to master branch 12 | steps: 13 | - name: Mirror branch 14 | uses: google/mirror-branch-action@v1.0 15 | with: 16 | github-token: ${{ secrets.GITHUB_TOKEN }} 17 | source: 'main' 18 | dest: 'master' 19 | -------------------------------------------------------------------------------- /.github/workflows/w3c-publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish to W3C TR space 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | paths: [ .github/**, document/** ] 7 | 8 | # Allows you to run this workflow manually from the Actions tab 9 | workflow_dispatch: 10 | 11 | jobs: 12 | publish-to-w3c-TR: 13 | if: github.repository == 'WebAssembly/spec' 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: Checkout repo 17 | uses: actions/checkout@v2 18 | with: 19 | submodules: "recursive" 20 | - name: Setup Node.js 21 | uses: actions/setup-node@v3 22 | with: 23 | node-version: 16 24 | - name: Setup Bikeshed 25 | run: pip install bikeshed && bikeshed update 26 | - name: Setup TexLive 27 | run: sudo apt-get update -y && sudo apt-get install -y latexmk texlive-latex-recommended texlive-latex-extra texlive-fonts-recommended 28 | - name: Setup Sphinx 29 | run: pip install six && pip install sphinx==5.1.0 30 | - name: Publish all specs to their https://www.w3.org/TR/ URLs 31 | run: cd document && make -e WD-echidna-CI 32 | env: 33 | STATUS: --md-status=WD 34 | W3C_ECHIDNA_TOKEN_CORE: ${{ secrets.W3C_ECHIDNA_TOKEN_CORE }} 35 | W3C_ECHIDNA_TOKEN_JSAPI: ${{ secrets.W3C_ECHIDNA_TOKEN_JSAPI }} 36 | W3C_ECHIDNA_TOKEN_WEBAPI: ${{ secrets.W3C_ECHIDNA_TOKEN_WEBAPI }} 37 | YARN_ENABLE_IMMUTABLE_INSTALLS: false 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/*~ 2 | **/*.tmproj 3 | **/*.pyc 4 | **/_build 5 | **/_output 6 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "document/core/util/katex"] 2 | path = document/core/util/katex 3 | url = https://github.com/KaTeX/KaTeX.git 4 | -------------------------------------------------------------------------------- /Contributing.md: -------------------------------------------------------------------------------- 1 | # Contributing to WebAssembly 2 | 3 | Interested in participating? Please follow 4 | [the same contributing guidelines as the design repository][]. 5 | 6 | [the same contributing guidelines as the design repository]: https://github.com/WebAssembly/design/blob/main/Contributing.md 7 | 8 | Also, please be sure to read [the README.md](README.md) for this repository. 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Please see the LICENSE file in each top-level directory for the terms applicable to that directory and its relative sub-directories. 2 | 3 | The relevant directories and licenses are: 4 | 5 | document/ - W3C Software and Document Notice and License 6 | interpreter/ - Apache License 2.0 7 | test/ - Apache License 2.0 8 | papers/ - Creative Commons Attribution 4.0 International License 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![CI for specs](https://github.com/WebAssembly/gc/actions/workflows/ci-spec.yml/badge.svg)](https://github.com/WebAssembly/gc/actions/workflows/ci-spec.yml) 2 | [![CI for interpreter & tests](https://github.com/WebAssembly/gc/actions/workflows/ci-interpreter.yml/badge.svg)](https://github.com/WebAssembly/gc/actions/workflows/ci-interpreter.yml) 3 | 4 | # GC Proposal for WebAssembly 5 | 6 | This repository is a clone of [github.com/WebAssembly/spec/](https://github.com/WebAssembly/spec/). 7 | It is meant for discussion, prototype specification and implementation of a proposal to add garbage collection (GC) support to WebAssembly. 8 | 9 | * See the [overview](proposals/gc/Overview.md) for a high-level summary and rationale of the proposal. *Note:* the concrete details here are out of date. 10 | 11 | * See the [MVP](proposals/gc/MVP.md) for an up-to-date overview of the concrete language extensions that are proposed for the first stage of GC support in Wasm. 12 | 13 | * See the [Post-MVP](proposals/gc/Post-MVP.md) for possible future extensions in later stages. 14 | 15 | * See the [modified spec](https://webassembly.github.io/gc/core) for the completed spec for the first-stage proposal described in MVP.md. 16 | 17 | This repository is based on the [function references proposal](proposals/function-references/Overview.md) as a baseline and includes all respective changes. 18 | 19 | Original `README` from upstream repository follows... 20 | 21 | # spec 22 | 23 | This repository holds a prototypical reference implementation for WebAssembly, 24 | which is currently serving as the official specification. Eventually, we expect 25 | to produce a specification either written in human-readable prose or in a formal 26 | specification language. 27 | 28 | It also holds the WebAssembly testsuite, which tests numerous aspects of 29 | conformance to the spec. 30 | 31 | View the work-in-progress spec at [webassembly.github.io/spec](https://webassembly.github.io/spec/). 32 | 33 | At this time, the contents of this repository are under development and known 34 | to be "incomplet and inkorrect". 35 | 36 | Participation is welcome. Discussions about new features, significant semantic 37 | changes, or any specification change likely to generate substantial discussion 38 | should take place in 39 | [the WebAssembly design repository](https://github.com/WebAssembly/design) 40 | first, so that this spec repository can remain focused. And please follow the 41 | [guidelines for contributing](Contributing.md). 42 | 43 | # citing 44 | 45 | For citing WebAssembly in LaTeX, use [this bibtex file](wasm-specs.bib). 46 | -------------------------------------------------------------------------------- /document/Makefile: -------------------------------------------------------------------------------- 1 | DIRS = js-api web-api core 2 | FILES = index.html 3 | BUILDDIR = _build 4 | TAR = tar 5 | 6 | # Global targets. 7 | 8 | .PHONY: all 9 | all: $(BUILDDIR) root $(DIRS) 10 | 11 | $(BUILDDIR): 12 | mkdir -p $@ 13 | 14 | .PHONY: deploy 15 | deploy: 16 | GIT_DEPLOY_DIR=$(BUILDDIR) bash deploy.sh 17 | 18 | .PHONY: publish 19 | publish: all deploy 20 | 21 | .PHONY: clean 22 | clean: $(DIRS:%=clean-%) 23 | rm -rf $(BUILDDIR) 24 | 25 | .PHONY: diff 26 | diff: $(DIRS:%=diff-%) 27 | 28 | # macOS: do “brew install tar” & run “make” as: TAR=gtar make -e WD-tar 29 | .PHONY: WD-tar 30 | WD-tar: 31 | for dir in $(DIRS); \ 32 | do STATUS=--md-status=WD TAR=$(TAR) $(MAKE) -e -C $$dir $@;\ 33 | done 34 | 35 | # macOS: do “brew install tar” & run “make” as: TAR=gtar make -e WD-echidna 36 | .PHONY: WD-echidna 37 | WD-echidna: 38 | for dir in $(DIRS); \ 39 | do $(MAKE) -e -C $$dir $@;\ 40 | done 41 | 42 | .PHONY: WD-echidna-CI 43 | WD-echidna-CI: 44 | for dir in $(DIRS); \ 45 | do $(MAKE) -e -C $$dir $@;\ 46 | done 47 | 48 | # Directory-specific targets. 49 | 50 | .PHONY: root 51 | root: $(BUILDDIR) 52 | touch $(BUILDDIR)/.nojekyll 53 | cp -f $(FILES) $(BUILDDIR)/ 54 | 55 | .PHONY: $(DIRS) 56 | $(DIRS): %: $(BUILDDIR) $(DIRS:%=build-%) $(DIRS:%=dir-%) 57 | 58 | .PHONY: $(DIRS:%=build-%) 59 | $(DIRS:%=build-%): build-%: 60 | (cd $(@:build-%=%); make BUILDDIR=$(BUILDDIR) all) 61 | 62 | .PHONY: $(DIRS:%=dir-%) 63 | $(DIRS:%=dir-%): dir-%: 64 | mkdir -p $(BUILDDIR)/$(@:dir-%=%) 65 | rm -rf $(BUILDDIR)/$(@:dir-%=%)/* 66 | cp -R $(@:dir-%=%)/$(BUILDDIR)/html/* $(BUILDDIR)/$(@:dir-%=%)/ 67 | 68 | .PHONY: $(DIRS:%=deploy-%) 69 | $(DIRS:%=deploy-%): deploy-%: 70 | GIT_DEPLOY_DIR=$(BUILDDIR) GIT_DEPLOY_SUBDIR=$(@:deploy-%=%) bash deploy.sh 71 | 72 | .PHONY: $(DIRS:%=publish-%) 73 | $(DIRS:%=publish-%): publish-%: % deploy-% 74 | 75 | .PHONY: $(DIRS:%=clean-%) 76 | $(DIRS:%=clean-%): clean-%: 77 | (cd $(@:clean-%=%); make BUILDDIR=$(BUILDDIR) clean) 78 | rm -rf $(BUILDDIR)/$(@:clean-%=%) 79 | 80 | .PHONY: $(DIRS:%=diff-%) 81 | $(DIRS:%=diff-%): diff-%: 82 | (cd $(@:diff-%=%); make BUILDDIR=$(BUILDDIR) diff) 83 | 84 | 85 | # Help. 86 | 87 | .PHONY: help 88 | help: 89 | @echo "Please use \`make ' where is one of" 90 | @echo " all to build all documents" 91 | @echo " publish to make all and push to gh-pages" 92 | @echo " to build a specific subdirectory" 93 | @echo " publish- to build and push a specific subdirectory" 94 | 95 | .PHONY: usage 96 | usage: help 97 | -------------------------------------------------------------------------------- /document/core/.gitignore: -------------------------------------------------------------------------------- 1 | _build 2 | _static 3 | document/*.pyc 4 | -------------------------------------------------------------------------------- /document/core/LICENSE: -------------------------------------------------------------------------------- 1 | W3C SOFTWARE AND DOCUMENT NOTICE AND LICENSE 2 | 3 | This work is being provided by the copyright holders under the following 4 | license. 5 | 6 | 7 | LICENSE 8 | 9 | By obtaining and/or copying this work, you (the licensee) agree that you have 10 | read, understood, and will comply with the following terms and conditions. 11 | 12 | Permission to copy, modify, and distribute this work, with or without 13 | modification, for any purpose and without fee or royalty is hereby granted, 14 | provided that you include the following on ALL copies of the work or portions 15 | thereof, including modifications: 16 | 17 | * The full text of this NOTICE in a location viewable to users of the 18 | redistributed or derivative work. 19 | 20 | * Any pre-existing intellectual property disclaimers, notices, or terms and 21 | conditions. If none exist, the W3C Software and Document Short Notice 22 | (https://www.w3.org/Consortium/Legal/copyright-software-short-notice) should 23 | be included. 24 | 25 | * Notice of any changes or modifications, through a copyright statement on the 26 | new code or document such as "This software or document includes material 27 | copied from or derived from [title and URI of the W3C document]. Copyright © [YEAR] W3C® (MIT, ERCIM, Keio, Beihang)." 28 | 29 | 30 | DISCLAIMERS 31 | 32 | THIS WORK IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS MAKE NO REPRESENTATIONS 33 | OR WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO, WARRANTIES OF 34 | MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE 35 | SOFTWARE OR DOCUMENT WILL NOT INFRINGE ANY THIRD PARTY PATENTS, COPYRIGHTS, 36 | TRADEMARKS OR OTHER RIGHTS. 37 | 38 | COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL OR 39 | CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE SOFTWARE OR DOCUMENT. 40 | 41 | The name and trademarks of copyright holders may NOT be used in advertising or 42 | publicity pertaining to the work without specific, written prior permission. 43 | Title to copyright in this work will at all times remain with copyright 44 | holders. 45 | 46 | 47 | NOTES 48 | 49 | This version: 50 | http://www.w3.org/Consortium/Legal/2015/copyright-software-and-document 51 | -------------------------------------------------------------------------------- /document/core/README.md: -------------------------------------------------------------------------------- 1 | # WebAssembly Core Specification 2 | 3 | This is the official WebAssembly "language" specification. 4 | 5 | It uses [Sphinx](http://www.sphinx-doc.org/). To install that: 6 | ``` 7 | pip install sphinx 8 | ``` 9 | To make HTML (result in `_build/html`): 10 | ``` 11 | make html 12 | ``` 13 | To make PDF (result in `_build/latex`, requires LaTeX): 14 | ``` 15 | make pdf 16 | ``` 17 | To make all: 18 | ``` 19 | make all 20 | ``` 21 | Finally, to make all and update webassembly.github.io/spec with it: 22 | ``` 23 | make publish 24 | ``` 25 | Please make sure to only use that once a change has approval. 26 | -------------------------------------------------------------------------------- /document/core/appendix/index.rst: -------------------------------------------------------------------------------- 1 | .. _appendix: 2 | 3 | Appendix 4 | ======== 5 | 6 | .. toctree:: 7 | :maxdepth: 2 8 | 9 | embedding 10 | implementation 11 | properties 12 | algorithm 13 | custom 14 | changes 15 | 16 | index-types 17 | index-instructions 18 | index-rules 19 | -------------------------------------------------------------------------------- /document/core/binary/index.rst: -------------------------------------------------------------------------------- 1 | .. _binary: 2 | 3 | Binary Format 4 | ============= 5 | 6 | .. toctree:: 7 | :maxdepth: 2 8 | 9 | conventions 10 | values 11 | types 12 | instructions 13 | modules 14 | -------------------------------------------------------------------------------- /document/core/exec/index.rst: -------------------------------------------------------------------------------- 1 | .. _exec: 2 | 3 | Execution 4 | ========= 5 | 6 | .. toctree:: 7 | :maxdepth: 2 8 | 9 | conventions 10 | runtime 11 | numerics 12 | types 13 | values 14 | instructions 15 | modules 16 | -------------------------------------------------------------------------------- /document/core/exec/types.rst: -------------------------------------------------------------------------------- 1 | .. index:: type, dynamic type 2 | .. _exec-type: 3 | 4 | Types 5 | ----- 6 | 7 | Execution has to check and compare :ref:`types ` in a few places, such as :ref:`executing ` |CALLINDIRECT| or :ref:`instantiating ` :ref:`modules `. 8 | 9 | It is an invariant of the semantics that all types occurring during execution are :ref:`closed `. 10 | 11 | .. note:: 12 | Runtime type checks generally involve types from multiple modules or types not defined by a module at all, such that module-local :ref:`type indices ` are not meaningful. 13 | 14 | 15 | 16 | .. index:: type index, defined type, type instantiation, module instance, dynamic type 17 | 18 | .. _type-inst: 19 | 20 | Instantiation 21 | ~~~~~~~~~~~~~ 22 | 23 | Any form of :ref:`type ` can be *instantiated* into a :ref:`closed ` type inside a :ref:`module instance ` by :ref:`substituting ` each :ref:`type index ` :math:`x` occurring in it with the corresponding :ref:`defined type ` :math:`\moduleinst.\MITYPES[x]`. 24 | 25 | .. math:: 26 | \insttype_{\moduleinst}(t) = t[\subst \moduleinst.\MITYPES] 27 | 28 | .. note:: 29 | This is the runtime equivalent to :ref:`type closure `. 30 | -------------------------------------------------------------------------------- /document/core/index.bs: -------------------------------------------------------------------------------- 1 | 17 | 18 |
19 | {
20 |   "WEBASSEMBLY": {
21 |     "href": "https://webassembly.github.io/spec/",
22 |     "title": "WebAssembly Specification",
23 |     "publisher": "W3C WebAssembly Community Group",
24 |     "status": "Draft"
25 |   }
26 | }
27 | 
28 | 29 |
30 | path: _build/bikeshed_singlehtml/index_fixed.html
31 | 
32 | -------------------------------------------------------------------------------- /document/core/index.rst: -------------------------------------------------------------------------------- 1 | WebAssembly Specification 2 | ========================= 3 | 4 | .. only:: html 5 | 6 | | Release |release| 7 | 8 | | Editor: Andreas Rossberg 9 | 10 | | Latest Draft: |WasmDraft| 11 | | Issue Tracker: |WasmIssues| 12 | 13 | .. toctree:: 14 | :maxdepth: 2 15 | 16 | intro/index 17 | syntax/index 18 | valid/index 19 | exec/index 20 | binary/index 21 | text/index 22 | appendix/index 23 | 24 | .. only:: latex 25 | 26 | .. toctree:: 27 | 28 | appendix/index-types 29 | appendix/index-instructions 30 | appendix/index-rules 31 | 32 | .. 33 | Only include these links when using (multi-page) html builder. 34 | (The singlepage html builder is called builder_singlehtml.) 35 | 36 | .. only:: builder_html 37 | 38 | * :ref:`index-type` 39 | * :ref:`index-instr` 40 | * :ref:`index-rules` 41 | 42 | * :ref:`genindex` 43 | -------------------------------------------------------------------------------- /document/core/intro/index.rst: -------------------------------------------------------------------------------- 1 | .. _intro: 2 | 3 | Introduction 4 | ============ 5 | 6 | .. toctree:: 7 | :maxdepth: 2 8 | 9 | introduction 10 | overview 11 | -------------------------------------------------------------------------------- /document/core/static/custom.css: -------------------------------------------------------------------------------- 1 | a { 2 | color: #004BAB; 3 | text-decoration: none; 4 | } 5 | 6 | a.reference { 7 | border-bottom: none; 8 | } 9 | 10 | a.reference:hover { 11 | border-bottom: 1px dotted #004BAB; 12 | } 13 | 14 | body { 15 | font-size: 15px; 16 | } 17 | 18 | div.document { width: 1000px; } 19 | div.bodywrapper { margin: 0 0 0 200px; } 20 | div.body { padding: 0 10px 0 10px; } 21 | div.footer { width: 1000px; } 22 | 23 | div.body h1 { font-size: 200%; } 24 | div.body h2 { font-size: 150%; } 25 | div.body h3 { font-size: 120%; } 26 | div.body h4 { font-size: 110%; } 27 | 28 | div.note { 29 | border: 0px; 30 | font-size: 90%; 31 | background-color: #F6F8FF; 32 | } 33 | 34 | div.admonition { 35 | padding: 10px; 36 | } 37 | 38 | div.admonition p.admonition-title { 39 | margin: 0px 0px 0px 0px; 40 | font-size: 100%; 41 | font-weight: bold; 42 | } 43 | 44 | div.math { 45 | background-color: #F0F0F0; 46 | padding: 3px 0 3px 0; 47 | overflow-x: auto; 48 | overflow-y: hidden; 49 | } 50 | 51 | div.relations { 52 | display: block; 53 | } 54 | 55 | div.sphinxsidebar { 56 | z-index: 1; 57 | background: #FFF; 58 | margin-top: -30px; 59 | font-size: 13px; 60 | width: 200px; 61 | height: 100%; 62 | } 63 | 64 | div.sphinxsidebarwrapper p.logo { 65 | padding: 30px 40px 10px 0px; 66 | } 67 | 68 | div.sphinxsidebar h3 { 69 | font-size: 0px; 70 | } 71 | 72 | div.sphinxsidebar a { 73 | border-bottom: 0px; 74 | } 75 | 76 | div.sphinxsidebar a:hover { 77 | border-bottom: 1px dotted; 78 | } 79 | -------------------------------------------------------------------------------- /document/core/static/webassembly.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WebAssembly/gc/756060f5816c7e2159f4817fbdee76cf52f9c923/document/core/static/webassembly.png -------------------------------------------------------------------------------- /document/core/syntax/index.rst: -------------------------------------------------------------------------------- 1 | .. _syntax: 2 | 3 | Structure 4 | ========= 5 | 6 | .. toctree:: 7 | :maxdepth: 2 8 | 9 | conventions 10 | values 11 | types 12 | instructions 13 | modules 14 | -------------------------------------------------------------------------------- /document/core/text/index.rst: -------------------------------------------------------------------------------- 1 | .. _text: 2 | 3 | Text Format 4 | =========== 5 | 6 | .. toctree:: 7 | :maxdepth: 2 8 | 9 | conventions 10 | lexical 11 | values 12 | types 13 | instructions 14 | modules 15 | -------------------------------------------------------------------------------- /document/core/util/README.htmldiff.pl: -------------------------------------------------------------------------------- 1 | This file is a copy of the HTML diff script found here: 2 | https://dev.w3.org/cvsweb/2009/htmldiff/ 3 | -------------------------------------------------------------------------------- /document/core/util/bikeshed/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # Sphinx configuration for bikeshed builds (make bikeshed). 4 | # This imports the main document/core/conf.py, then overwrites certain config 5 | # values for the bikeshed build. 6 | 7 | import os 8 | import sys 9 | original_pwd = os.path.abspath('../..') 10 | # Change sys.path so that we can find the main 'conf.py'. 11 | sys.path.insert(0, original_pwd) 12 | from conf import * 13 | main_conf_pwd = pwd 14 | 15 | # Now that we have imported all the settings, we need to overwrite some of 16 | # them. 17 | 18 | # The first is `pwd`, we want to reset it to document/core, because rst_prolog 19 | # below depends on this to find macros.def. 20 | pwd = original_pwd 21 | 22 | # The bikeshed build requires the mathdefbs extension. 23 | extensions[extensions.index('util.mathdef')] = 'util.mathdefbs' 24 | 25 | # Overwrite html themes and configurations. 26 | html_theme = 'classic' 27 | html_permalinks = False 28 | html_theme_options = { 29 | 'nosidebar': True, 30 | } 31 | html_show_copyright = False 32 | 33 | # Overwrite the prolog to make sure the include directive has the correct path. 34 | 35 | main_macros_def = "/" + main_conf_pwd + "/util/macros.def" 36 | # If we hit this assertion, the configuration files probably moved, or files 37 | # are renamed, and we have to update rst_prolog accordingly. 38 | assert(main_macros_def in rst_prolog) 39 | rst_prolog = rst_prolog.replace(main_macros_def, "/" + pwd + "/util/macros.def") 40 | 41 | del mathjax3_config 42 | -------------------------------------------------------------------------------- /document/core/util/check_macros.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | cd `dirname $0`/.. 4 | 5 | FILES=`ls */*.rst` 6 | ERRORS=0 7 | 8 | for XREF in `grep "[\\]xref" util/macros.def`; do 9 | if echo $XREF | grep -q "[|]"; then 10 | MACRO=`echo $XREF | sed 's/^[^|]*[|]//g' | sed 's/[|].*$//g'` 11 | elif echo $XREF | grep -q xref; then 12 | FILE=`echo $XREF | sed 's/^.*xref{//g' | sed 's/}.*$//g'`.rst 13 | LABEL=`echo $XREF | sed 's/^[^}]*}{//g' | sed 's/}.*$//g'` 14 | TARGET=".. _$LABEL:" 15 | if ! [ -f $FILE ] || ! grep -q "$TARGET" $FILE; then 16 | ERRORS=1 17 | echo Undefined cross-reference $FILE:$LABEL in macro "|$MACRO|" 18 | if ! [ -f $FILE ]; then 19 | echo ...non-existent file $FILE 20 | fi 21 | if grep -q "$TARGET" $FILES; then 22 | echo ...defined in `grep -l "$TARGET" $FILES` 23 | fi 24 | fi 25 | fi 26 | done 27 | 28 | if [ $ERRORS -eq 0 ]; then 29 | echo All cross-references okay. 30 | else 31 | exit 1; 32 | fi 33 | -------------------------------------------------------------------------------- /document/core/util/katex_fix.patch: -------------------------------------------------------------------------------- 1 | 123c123,126 2 | < font: normal 1.21em KaTeX_Main, Times New Roman, serif; 3 | --- 4 | > /* font: normal 1.21em KaTeX_Main, Times New Roman, serif; */ 5 | > font-weight: normal; 6 | > font-size: 1.21em; 7 | > font-family: KaTeX_Main, Times New Roman, serif; 8 | 126d128 9 | < text-rendering: auto; 10 | 989c991 11 | < .katex-display { 12 | --- 13 | > div > .katex-display { 14 | 994c996 15 | < .katex-display > .katex { 16 | --- 17 | > div > .katex-display > .katex { 18 | 999c1001 19 | < .katex-display > .katex > .katex-html { 20 | --- 21 | > div > .katex-display > .katex > .katex-html { 22 | 1003c1005 23 | < .katex-display > .katex > .katex-html > .tag { 24 | --- 25 | > div > .katex-display > .katex > .katex-html > .tag { 26 | 1007c1009,1022 27 | < 28 | --- 29 | > /* Force borders on tables */ 30 | > table { 31 | > border-collapse: collapse; 32 | > } 33 | > .docutils th, td { 34 | > border: 1px solid; 35 | > padding: .4em; 36 | > } 37 | > .footnote td { 38 | > border: 0; 39 | > } 40 | > .codepre { 41 | > white-space: pre; 42 | > } 43 | -------------------------------------------------------------------------------- /document/core/util/pseudo-lexer.py: -------------------------------------------------------------------------------- 1 | from pygments.lexer import RegexLexer 2 | from pygments.token import * 3 | from sphinx.highlighting import lexers 4 | 5 | class PseudoLexer(RegexLexer): 6 | name = 'Pseudo' 7 | aliases = ['pseudo'] 8 | filenames = ['*.pseudo'] 9 | 10 | tokens = { 11 | 'root': [ 12 | (r"(? string -> Ast.module_ (* raises Code *) 4 | 5 | val decode_custom : Ast.name -> string -> string -> string list (* raises Code *) 6 | -------------------------------------------------------------------------------- /interpreter/binary/encode.mli: -------------------------------------------------------------------------------- 1 | exception Code of Source.region * string 2 | 3 | val version : int32 4 | val encode : Ast.module_ -> string 5 | val encode_custom : Ast.name -> string -> string 6 | -------------------------------------------------------------------------------- /interpreter/binary/utf8.ml: -------------------------------------------------------------------------------- 1 | type codepoint = int 2 | type unicode = codepoint list 3 | 4 | exception Utf8 5 | 6 | let con n = 0x80 lor (n land 0x3f) 7 | 8 | let rec encode ns = Lib.String.implode (List.map Char.chr (encode' ns)) 9 | and encode' = function 10 | | [] -> [] 11 | | n::ns when n < 0 -> 12 | raise Utf8 13 | | n::ns when n < 0x80 -> 14 | n :: encode' ns 15 | | n::ns when n < 0x800 -> 16 | 0xc0 lor (n lsr 6) :: con n :: encode' ns 17 | | n::ns when n < 0x10000 -> 18 | 0xe0 lor (n lsr 12) :: con (n lsr 6) :: con n :: encode' ns 19 | | n::ns when n < 0x110000 -> 20 | 0xf0 lor (n lsr 18) :: con (n lsr 12) :: con (n lsr 6) :: con n 21 | :: encode' ns 22 | | _ -> 23 | raise Utf8 24 | 25 | let con b = if b land 0xc0 = 0x80 then b land 0x3f else raise Utf8 26 | let code min n = 27 | if n < min || (0xd800 <= n && n < 0xe000) || n >= 0x110000 then raise Utf8 28 | else n 29 | 30 | let rec decode s = decode' (List.map Char.code (Lib.String.explode s)) 31 | and decode' = function 32 | | [] -> [] 33 | | b1::bs when b1 < 0x80 -> 34 | code 0x0 b1 :: decode' bs 35 | | b1::bs when b1 < 0xc0 -> 36 | raise Utf8 37 | | b1::b2::bs when b1 < 0xe0 -> 38 | code 0x80 ((b1 land 0x1f) lsl 6 + con b2) :: decode' bs 39 | | b1::b2::b3::bs when b1 < 0xf0 -> 40 | code 0x800 ((b1 land 0x0f) lsl 12 + con b2 lsl 6 + con b3) :: decode' bs 41 | | b1::b2::b3::b4::bs when b1 < 0xf8 -> 42 | code 0x10000 ((b1 land 0x07) lsl 18 + con b2 lsl 12 + con b3 lsl 6 + con b4) 43 | :: decode' bs 44 | | _ -> 45 | raise Utf8 46 | -------------------------------------------------------------------------------- /interpreter/binary/utf8.mli: -------------------------------------------------------------------------------- 1 | type codepoint = int 2 | type unicode = codepoint list 3 | 4 | exception Utf8 5 | 6 | val decode : string -> unicode (* raises Utf8 *) 7 | val encode : unicode -> string (* raises Utf8 *) 8 | -------------------------------------------------------------------------------- /interpreter/dune: -------------------------------------------------------------------------------- 1 | (include_subdirs unqualified) 2 | 3 | (library 4 | (public_name wasm) 5 | (modules :standard \ main wasm wast smallint) 6 | ) 7 | 8 | (executable 9 | (public_name wasm) 10 | (modules wasm) 11 | (libraries wasm) 12 | (flags (-open Wasm)) 13 | ) 14 | 15 | (executable 16 | (name smallint) 17 | (modules smallint) 18 | (libraries wasm) 19 | (flags (-open Wasm)) 20 | ) 21 | 22 | (executable 23 | (name wast) 24 | (modules wast) 25 | (modes js) 26 | (libraries js_of_ocaml wasm) 27 | (preprocess (pps js_of_ocaml-ppx)) 28 | ) 29 | 30 | (env (_ (flags (-w +a-4-27-42-44-45-70 -warn-error +a-3)))) 31 | 32 | (subdir text 33 | (ocamllex (modules lexer)) 34 | (menhir (modules parser)) 35 | ) 36 | 37 | (rule 38 | (targets wasm.ml) 39 | (deps main/main.ml) 40 | (action (copy main/main.ml wasm.ml)) 41 | ) 42 | 43 | (rule 44 | (alias runtest) 45 | (deps 46 | ./wasm.exe 47 | ./smallint.exe 48 | (source_tree ../test) 49 | ) 50 | (action 51 | (progn 52 | (run ../test/core/run.py --wasm ./wasm.exe) 53 | (run ./smallint.exe) 54 | ) 55 | ) 56 | ) 57 | -------------------------------------------------------------------------------- /interpreter/dune-project: -------------------------------------------------------------------------------- 1 | (lang dune 2.9) 2 | 3 | (name wasm) 4 | (license Apache-2.0) 5 | (source (github WebAssembly/spec)) 6 | 7 | (authors "Andreas Rossberg = 4.12)) 20 | (menhir (>= 20220210)) 21 | ) 22 | ) 23 | -------------------------------------------------------------------------------- /interpreter/exec/eval.mli: -------------------------------------------------------------------------------- 1 | open Value 2 | open Instance 3 | 4 | exception Link of Source.region * string 5 | exception Trap of Source.region * string 6 | exception Crash of Source.region * string 7 | exception Exhaustion of Source.region * string 8 | 9 | val init : Ast.module_ -> extern list -> module_inst (* raises Link, Trap *) 10 | val invoke : func_inst -> value list -> value list (* raises Trap *) 11 | -------------------------------------------------------------------------------- /interpreter/exec/eval_num.mli: -------------------------------------------------------------------------------- 1 | open Value 2 | 3 | val eval_unop : Ast.unop -> num -> num 4 | val eval_binop : Ast.binop -> num -> num -> num 5 | val eval_testop : Ast.testop -> num -> bool 6 | val eval_relop : Ast.relop -> num -> num -> bool 7 | val eval_cvtop : Ast.cvtop -> num -> num 8 | -------------------------------------------------------------------------------- /interpreter/exec/eval_vec.mli: -------------------------------------------------------------------------------- 1 | open Value 2 | 3 | val eval_testop : Ast.vec_testop -> vec -> bool 4 | val eval_unop : Ast.vec_unop -> vec -> vec 5 | val eval_binop : Ast.vec_binop -> vec -> vec -> vec 6 | val eval_relop : Ast.vec_relop -> vec -> vec -> vec 7 | val eval_cvtop : Ast.vec_cvtop -> vec -> vec 8 | val eval_shiftop : Ast.vec_shiftop -> vec -> num -> vec 9 | val eval_bitmaskop : Ast.vec_bitmaskop -> vec -> num 10 | val eval_vtestop : Ast.vec_vtestop -> vec -> bool 11 | val eval_vunop : Ast.vec_vunop -> vec -> vec 12 | val eval_vbinop : Ast.vec_vbinop -> vec -> vec -> vec 13 | val eval_vternop : Ast.vec_vternop -> vec -> vec -> vec -> vec 14 | val eval_splatop : Ast.vec_splatop -> num -> vec 15 | val eval_extractop : Ast.vec_extractop -> vec -> num 16 | val eval_replaceop : Ast.vec_replaceop -> vec -> num -> vec 17 | -------------------------------------------------------------------------------- /interpreter/exec/f32.ml: -------------------------------------------------------------------------------- 1 | (* 2 | * OCaml lacks 32-bit floats, however we can emulate all the basic operations 3 | * using 64-bit floats, as described in the paper 4 | * "When is double rounding innocuous?" by Samuel A. Figueroa. 5 | *) 6 | include Fxx.Make 7 | (struct 8 | include Int32 9 | let mantissa = 23 10 | let pos_nan = 0x7fc0_0000l 11 | let neg_nan = 0xffc0_0000l 12 | let bare_nan = 0x7f80_0000l 13 | let to_hex_string = Printf.sprintf "%lx" 14 | end) 15 | -------------------------------------------------------------------------------- /interpreter/exec/f32_convert.ml: -------------------------------------------------------------------------------- 1 | (* WebAssembly-compatible type conversions to f32 implementation *) 2 | 3 | let demote_f64 x = 4 | let xf = F64.to_float x in 5 | if xf = xf then F32.of_float xf else 6 | let nan64bits = F64.to_bits x in 7 | let sign_field = Int64.(shift_left (shift_right_logical nan64bits 63) 31) in 8 | let significand_field = Int64.(shift_right_logical (shift_left nan64bits 12) 41) in 9 | let fields = Int64.logor sign_field significand_field in 10 | let nan32bits = Int32.logor 0x7fc0_0000l (I32_convert.wrap_i64 fields) in 11 | F32.of_bits nan32bits 12 | 13 | let convert_i32_s x = 14 | F32.of_float (Int32.to_float x) 15 | 16 | (* 17 | * Similar to convert_i64_u below, the high half of the i32 range are beyond 18 | * the range where f32 can represent odd numbers, though we do need to adjust 19 | * the least significant bit to round correctly. 20 | *) 21 | let convert_i32_u x = 22 | F32.of_float Int32.( 23 | if x >= zero then to_float x else 24 | to_float (logor (shift_right_logical x 1) (logand x 1l)) *. 2.0 25 | ) 26 | 27 | (* 28 | * Values that are too large would get rounded when represented in f64, 29 | * but double rounding via i64->f64->f32 can produce inaccurate results. 30 | * Hence, for large values we shift right but make sure to accumulate the lost 31 | * bits in the least significant bit, such that rounding still is correct. 32 | *) 33 | let convert_i64_s x = 34 | F32.of_float Int64.( 35 | if abs x < 0x10_0000_0000_0000L then to_float x else 36 | let r = if logand x 0xfffL = 0L then 0L else 1L in 37 | to_float (logor (shift_right x 12) r) *. 0x1p12 38 | ) 39 | 40 | let convert_i64_u x = 41 | F32.of_float Int64.( 42 | if I64.lt_u x 0x10_0000_0000_0000L then to_float x else 43 | let r = if logand x 0xfffL = 0L then 0L else 1L in 44 | to_float (logor (shift_right_logical x 12) r) *. 0x1p12 45 | ) 46 | 47 | let reinterpret_i32 = F32.of_bits 48 | -------------------------------------------------------------------------------- /interpreter/exec/f32_convert.mli: -------------------------------------------------------------------------------- 1 | (* WebAssembly-compatible type conversions to f32 implementation *) 2 | 3 | val demote_f64 : F64.t -> F32.t 4 | val convert_i32_s : I32.t -> F32.t 5 | val convert_i32_u : I32.t -> F32.t 6 | val convert_i64_s : I64.t -> F32.t 7 | val convert_i64_u : I64.t -> F32.t 8 | val reinterpret_i32 : I32.t -> F32.t 9 | -------------------------------------------------------------------------------- /interpreter/exec/f64.ml: -------------------------------------------------------------------------------- 1 | include Fxx.Make 2 | (struct 3 | include Int64 4 | let mantissa = 52 5 | let pos_nan = 0x7ff8_0000_0000_0000L 6 | let neg_nan = 0xfff8_0000_0000_0000L 7 | let bare_nan = 0x7ff0_0000_0000_0000L 8 | let to_hex_string = Printf.sprintf "%Lx" 9 | end) 10 | -------------------------------------------------------------------------------- /interpreter/exec/f64_convert.ml: -------------------------------------------------------------------------------- 1 | (* WebAssembly-compatible type conversions to f64 implementation *) 2 | 3 | let promote_f32 x = 4 | let xf = F32.to_float x in 5 | if xf = xf then F64.of_float xf else 6 | let nan32bits = I64_convert.extend_i32_u (F32.to_bits x) in 7 | let sign_field = Int64.(shift_left (shift_right_logical nan32bits 31) 63) in 8 | let significand_field = Int64.(shift_right_logical (shift_left nan32bits 41) 12) in 9 | let fields = Int64.logor sign_field significand_field in 10 | let nan64bits = Int64.logor 0x7ff8_0000_0000_0000L fields in 11 | F64.of_bits nan64bits 12 | 13 | let convert_i32_s x = 14 | F64.of_float (Int32.to_float x) 15 | 16 | (* 17 | * Unlike the other convert_u functions, the high half of the i32 range is 18 | * within the range where f32 can represent odd numbers, so we can't do the 19 | * shift. Instead, we can use int64 signed arithmetic. 20 | *) 21 | let convert_i32_u x = 22 | F64.of_float Int64.(to_float (logand (of_int32 x) 0x0000_0000_ffff_ffffL)) 23 | 24 | let convert_i64_s x = 25 | F64.of_float (Int64.to_float x) 26 | 27 | (* 28 | * Values in the low half of the int64 range can be converted with a signed 29 | * conversion. The high half is beyond the range where f64 can represent odd 30 | * numbers, so we can shift the value right, adjust the least significant 31 | * bit to round correctly, do a conversion, and then scale it back up. 32 | *) 33 | let convert_i64_u x = 34 | F64.of_float Int64.( 35 | if x >= zero then to_float x else 36 | to_float (logor (shift_right_logical x 1) (logand x 1L)) *. 2.0 37 | ) 38 | 39 | let reinterpret_i64 = F64.of_bits 40 | -------------------------------------------------------------------------------- /interpreter/exec/f64_convert.mli: -------------------------------------------------------------------------------- 1 | (* WebAssembly-compatible type conversions to f64 implementation *) 2 | 3 | val promote_f32 : F32.t -> F64.t 4 | val convert_i32_s : I32.t -> F64.t 5 | val convert_i32_u : I32.t -> F64.t 6 | val convert_i64_s : I64.t -> F64.t 7 | val convert_i64_u : I64.t -> F64.t 8 | val reinterpret_i64 : I64.t -> F64.t 9 | -------------------------------------------------------------------------------- /interpreter/exec/i16.ml: -------------------------------------------------------------------------------- 1 | (* Uses Int32 as the underlying storage. All int16 values will be 2 | * stored signed-extended. E.g. -1 will be stored with all high bits set. 3 | *) 4 | include Ixx.Make (struct 5 | include Int32 6 | 7 | let bitwidth = 16 8 | let to_hex_string i = Printf.sprintf "%lx" (Int32.logand i 0xffffl) 9 | 10 | let of_int64 = Int64.to_int32 11 | let to_int64 = Int64.of_int32 12 | end) 13 | -------------------------------------------------------------------------------- /interpreter/exec/i32.ml: -------------------------------------------------------------------------------- 1 | (* WebAssembly-compatible i32 implementation *) 2 | 3 | include Ixx.Make 4 | (struct 5 | include Int32 6 | let bitwidth = 32 7 | let to_hex_string = Printf.sprintf "%lx" 8 | 9 | let of_int64 = Int64.to_int32 10 | let to_int64 = Int64.of_int32 11 | end) 12 | -------------------------------------------------------------------------------- /interpreter/exec/i32_convert.ml: -------------------------------------------------------------------------------- 1 | (* WebAssembly-compatible type conversions to i32 implementation *) 2 | 3 | let wrap_i64 x = Int64.to_int32 x 4 | 5 | let trunc_f32_s x = 6 | if F32.ne x x then 7 | raise Ixx.InvalidConversion 8 | else 9 | let xf = F32.to_float x in 10 | if xf >= -.Int32.(to_float min_int) || xf < Int32.(to_float min_int) then 11 | raise Ixx.Overflow 12 | else 13 | Int32.of_float xf 14 | 15 | let trunc_f32_u x = 16 | if F32.ne x x then 17 | raise Ixx.InvalidConversion 18 | else 19 | let xf = F32.to_float x in 20 | if xf >= -.Int32.(to_float min_int) *. 2.0 || xf <= -1.0 then 21 | raise Ixx.Overflow 22 | else 23 | Int64.(to_int32 (of_float xf)) 24 | 25 | let trunc_f64_s x = 26 | if F64.ne x x then 27 | raise Ixx.InvalidConversion 28 | else 29 | let xf = F64.to_float x in 30 | if xf >= -.Int32.(to_float min_int) || xf <= Int32.(to_float min_int) -. 1.0 then 31 | raise Ixx.Overflow 32 | else 33 | Int32.of_float xf 34 | 35 | let trunc_f64_u x = 36 | if F64.ne x x then 37 | raise Ixx.InvalidConversion 38 | else 39 | let xf = F64.to_float x in 40 | if xf >= -.Int32.(to_float min_int) *. 2.0 || xf <= -1.0 then 41 | raise Ixx.Overflow 42 | else 43 | Int64.(to_int32 (of_float xf)) 44 | 45 | let trunc_sat_f32_s x = 46 | if F32.ne x x then 47 | 0l 48 | else 49 | let xf = F32.to_float x in 50 | if xf < Int32.(to_float min_int) then 51 | Int32.min_int 52 | else if xf >= -.Int32.(to_float min_int) then 53 | Int32.max_int 54 | else 55 | Int32.of_float xf 56 | 57 | let trunc_sat_f32_u x = 58 | if F32.ne x x then 59 | 0l 60 | else 61 | let xf = F32.to_float x in 62 | if xf <= -1.0 then 63 | 0l 64 | else if xf >= -.Int32.(to_float min_int) *. 2.0 then 65 | -1l 66 | else 67 | Int64.(to_int32 (of_float xf)) 68 | 69 | let trunc_sat_f64_s x = 70 | if F64.ne x x then 71 | 0l 72 | else 73 | let xf = F64.to_float x in 74 | if xf < Int32.(to_float min_int) then 75 | Int32.min_int 76 | else if xf >= -.Int32.(to_float min_int) then 77 | Int32.max_int 78 | else 79 | Int32.of_float xf 80 | 81 | let trunc_sat_f64_u x = 82 | if F64.ne x x then 83 | 0l 84 | else 85 | let xf = F64.to_float x in 86 | if xf <= -1.0 then 87 | 0l 88 | else if xf >= -.Int32.(to_float min_int) *. 2.0 then 89 | -1l 90 | else 91 | Int64.(to_int32 (of_float xf)) 92 | 93 | let reinterpret_f32 = F32.to_bits 94 | -------------------------------------------------------------------------------- /interpreter/exec/i32_convert.mli: -------------------------------------------------------------------------------- 1 | (* WebAssembly-compatible type conversions to i32 implementation *) 2 | 3 | val wrap_i64 : I64.t -> I32.t 4 | val trunc_f32_s : F32.t -> I32.t 5 | val trunc_f32_u : F32.t -> I32.t 6 | val trunc_f64_s : F64.t -> I32.t 7 | val trunc_f64_u : F64.t -> I32.t 8 | val trunc_sat_f32_s : F32.t -> I32.t 9 | val trunc_sat_f32_u : F32.t -> I32.t 10 | val trunc_sat_f64_s : F64.t -> I32.t 11 | val trunc_sat_f64_u : F64.t -> I32.t 12 | val reinterpret_f32 : F32.t -> I32.t 13 | -------------------------------------------------------------------------------- /interpreter/exec/i64.ml: -------------------------------------------------------------------------------- 1 | (* WebAssembly-compatible i64 implementation *) 2 | 3 | include Ixx.Make 4 | (struct 5 | include Int64 6 | let bitwidth = 64 7 | let to_hex_string = Printf.sprintf "%Lx" 8 | 9 | let of_int64 i = i 10 | let to_int64 i = i 11 | end) 12 | -------------------------------------------------------------------------------- /interpreter/exec/i64_convert.mli: -------------------------------------------------------------------------------- 1 | (* WebAssembly-compatible type conversions to i64 implementation *) 2 | 3 | val extend_i32_s : I32.t -> I64.t 4 | val extend_i32_u : I32.t -> I64.t 5 | val trunc_f32_s : F32.t -> I64.t 6 | val trunc_f32_u : F32.t -> I64.t 7 | val trunc_f64_s : F64.t -> I64.t 8 | val trunc_f64_u : F64.t -> I64.t 9 | val trunc_sat_f32_s : F32.t -> I64.t 10 | val trunc_sat_f32_u : F32.t -> I64.t 11 | val trunc_sat_f64_s : F64.t -> I64.t 12 | val trunc_sat_f64_u : F64.t -> I64.t 13 | val reinterpret_f64 : F64.t -> I64.t 14 | -------------------------------------------------------------------------------- /interpreter/exec/i8.ml: -------------------------------------------------------------------------------- 1 | (* Uses Int32 as the underlying storage. All int8 values will be 2 | * stored signed-extended. E.g. -1 will be stored with all high bits set. 3 | *) 4 | include Ixx.Make (struct 5 | include Int32 6 | 7 | let bitwidth = 8 8 | let to_hex_string i = Printf.sprintf "%lx" (Int32.logand i 0xffl) 9 | 10 | let of_int64 = Int64.to_int32 11 | let to_int64 = Int64.of_int32 12 | end) 13 | -------------------------------------------------------------------------------- /interpreter/host/env.ml: -------------------------------------------------------------------------------- 1 | (* 2 | * Emulation of (a subset of) the `env` module currently used by Binaryen, 3 | * so that we can run modules generated by Binaryen. This is a stopgap until 4 | * we have agreement on what libc should look like. 5 | *) 6 | 7 | open Types 8 | open Value 9 | open Instance 10 | 11 | 12 | let error msg = raise (Eval.Crash (Source.no_region, msg)) 13 | 14 | let type_error v t = 15 | error 16 | ("type error, expected " ^ string_of_val_type t ^ 17 | ", got " ^ string_of_val_type (type_of_value v)) 18 | 19 | let empty = function 20 | | [] -> () 21 | | vs -> error "type error, too many arguments" 22 | 23 | let single = function 24 | | [] -> error "type error, missing arguments" 25 | | [v] -> v 26 | | vs -> error "type error, too many arguments" 27 | 28 | let int = function 29 | | Num (I32 i) -> Int32.to_int i 30 | | v -> type_error v (NumT I32T) 31 | 32 | 33 | let abort vs = 34 | empty vs; 35 | print_endline "Abort!"; 36 | exit (-1) 37 | 38 | let exit vs = 39 | exit (int (single vs)) 40 | 41 | 42 | let lookup name et = 43 | match Utf8.encode name, et with 44 | | "abort", ExternFuncT ct -> ExternFunc (Func.alloc_host ct abort) 45 | | "exit", ExternFuncT ct -> ExternFunc (Func.alloc_host ct exit) 46 | | _ -> raise Not_found 47 | -------------------------------------------------------------------------------- /interpreter/host/spectest.ml: -------------------------------------------------------------------------------- 1 | (* 2 | * Simple collection of functions useful for writing test cases. 3 | *) 4 | 5 | open Types 6 | open Value 7 | open Instance 8 | 9 | 10 | let global (GlobalT (_, t) as gt) = 11 | let v = 12 | match t with 13 | | NumT I32T -> Num (I32 666l) 14 | | NumT I64T -> Num (I64 666L) 15 | | NumT F32T -> Num (F32 (F32.of_float 666.6)) 16 | | NumT F64T -> Num (F64 (F64.of_float 666.6)) 17 | | VecT V128T -> Vec (V128 (V128.I32x4.of_lanes [666l; 666l; 666l; 666l])) 18 | | RefT (_, t) -> Ref (NullRef t) 19 | | BotT -> assert false 20 | in ExternGlobal (Global.alloc gt v) 21 | 22 | let table = 23 | let tt = TableT ({min = 10l; max = Some 20l}, (Null, FuncHT)) in 24 | ExternTable (Table.alloc tt (NullRef FuncHT)) 25 | 26 | let memory = 27 | let mt = MemoryT {min = 1l; max = Some 2l} in 28 | ExternMemory (Memory.alloc mt) 29 | 30 | let func f ft = 31 | let dt = DefT (RecT [SubT (Final, [], DefFuncT ft)], 0l) in 32 | ExternFunc (Func.alloc_host dt (f ft)) 33 | 34 | let print_value v = 35 | Printf.printf "%s : %s\n" 36 | (string_of_value v) (string_of_val_type (type_of_value v)) 37 | 38 | let print _ vs = 39 | List.iter print_value vs; 40 | flush_all (); 41 | [] 42 | 43 | 44 | let lookup name t = 45 | match Utf8.encode name, t with 46 | | "print", _ -> func print (FuncT ([], [])) 47 | | "print_i32", _ -> func print (FuncT ([NumT I32T], [])) 48 | | "print_i64", _ -> func print (FuncT ([NumT I64T], [])) 49 | | "print_f32", _ -> func print (FuncT ([NumT F32T], [])) 50 | | "print_f64", _ -> func print (FuncT ([NumT F64T], [])) 51 | | "print_i32_f32", _ -> func print (FuncT ([NumT I32T; NumT F32T], [])) 52 | | "print_f64_f64", _ -> func print (FuncT ([NumT F64T; NumT F64T], [])) 53 | | "global_i32", _ -> global (GlobalT (Cons, NumT I32T)) 54 | | "global_i64", _ -> global (GlobalT (Cons, NumT I64T)) 55 | | "global_f32", _ -> global (GlobalT (Cons, NumT F32T)) 56 | | "global_f64", _ -> global (GlobalT (Cons, NumT F64T)) 57 | | "table", _ -> table 58 | | "memory", _ -> memory 59 | | _ -> raise Not_found 60 | -------------------------------------------------------------------------------- /interpreter/jslib/wast.ml: -------------------------------------------------------------------------------- 1 | (* Implements a wrapper library that allows the use of the reference 2 | * interpreter's encode/decode functionality in JavaScript. 3 | *) 4 | open Wasm 5 | open Js_of_ocaml 6 | 7 | let () = 8 | Js.export "WebAssemblyText" 9 | (object%js (_self) 10 | 11 | method encode (s : Js.js_string Js.t) : (Typed_array.arrayBuffer Js.t) = 12 | let _, def = Parse.Module.parse_string (Js.to_string s) in 13 | let bs = 14 | match def.Source.it with 15 | | Script.Textual m -> Encode.encode m 16 | | Script.Encoded (_, bs) -> bs 17 | | Script.Quoted (_, _) -> failwith "Unsupported" in 18 | let buf = new%js Typed_array.arrayBuffer (String.length bs) in 19 | let u8arr = new%js Typed_array.uint8Array_fromBuffer buf in 20 | String.iteri (fun i c -> Typed_array.set u8arr i (int_of_char c)) bs; buf 21 | 22 | method decode (buf : Typed_array.arrayBuffer Js.t) width : (Js.js_string Js.t) = 23 | let s = Typed_array.String.of_uint8Array (new%js Typed_array.uint8Array_fromBuffer buf) in 24 | let m = Decode.decode "(decode)" s in 25 | Js.string (Sexpr.to_string width (Arrange.module_ m)) 26 | 27 | end) 28 | -------------------------------------------------------------------------------- /interpreter/main/flags.ml: -------------------------------------------------------------------------------- 1 | let interactive = ref false 2 | let trace = ref false 3 | let unchecked = ref false 4 | let print_sig = ref false 5 | let dry = ref false 6 | let width = ref 80 7 | let harness = ref true 8 | let budget = ref 256 9 | -------------------------------------------------------------------------------- /interpreter/main/main.ml: -------------------------------------------------------------------------------- 1 | let name = "wasm" 2 | let version = "2.1" 3 | 4 | let configure () = 5 | Import.register (Utf8.decode "spectest") Spectest.lookup; 6 | Import.register (Utf8.decode "env") Env.lookup 7 | 8 | let banner () = 9 | print_endline (name ^ " " ^ version ^ " reference interpreter") 10 | 11 | let usage = "Usage: " ^ name ^ " [option] [file ...]" 12 | 13 | let args = ref [] 14 | let add_arg source = args := !args @ [source] 15 | 16 | let quote s = "\"" ^ String.escaped s ^ "\"" 17 | 18 | let argspec = Arg.align 19 | [ 20 | "-", Arg.Set Flags.interactive, 21 | " run interactively (default if no files given)"; 22 | "-e", Arg.String add_arg, " evaluate string"; 23 | "-i", Arg.String (fun file -> add_arg ("(input " ^ quote file ^ ")")), 24 | " read script from file"; 25 | "-o", Arg.String (fun file -> add_arg ("(output " ^ quote file ^ ")")), 26 | " write module to file"; 27 | "-b", Arg.Int (fun n -> Flags.budget := n), 28 | " configure call depth budget (default is " ^ string_of_int !Flags.budget ^ ")"; 29 | "-w", Arg.Int (fun n -> Flags.width := n), 30 | " configure output width (default is " ^ string_of_int !Flags.width ^ ")"; 31 | "-s", Arg.Set Flags.print_sig, " show module signatures"; 32 | "-u", Arg.Set Flags.unchecked, " unchecked, do not perform validation"; 33 | "-j", Arg.Clear Flags.harness, " exclude harness for JS conversion"; 34 | "-d", Arg.Set Flags.dry, " dry, do not run program"; 35 | "-t", Arg.Set Flags.trace, " trace execution"; 36 | "-v", Arg.Unit banner, " show version" 37 | ] 38 | 39 | let () = 40 | Printexc.record_backtrace true; 41 | try 42 | configure (); 43 | Arg.parse argspec 44 | (fun file -> add_arg ("(input " ^ quote file ^ ")")) usage; 45 | List.iter (fun arg -> if not (Run.run_string arg) then exit 1) !args; 46 | if !args = [] then Flags.interactive := true; 47 | if !Flags.interactive then begin 48 | Flags.print_sig := true; 49 | banner (); 50 | Run.run_stdin () 51 | end 52 | with exn -> 53 | flush_all (); 54 | prerr_endline 55 | (Sys.argv.(0) ^ ": uncaught exception " ^ Printexc.to_string exn); 56 | Printexc.print_backtrace stderr; 57 | exit 2 58 | -------------------------------------------------------------------------------- /interpreter/runtime/aggr.ml: -------------------------------------------------------------------------------- 1 | open Types 2 | open Value 3 | 4 | type field = 5 | | ValField of value ref 6 | | PackField of Pack.pack_size * int ref 7 | 8 | type struct_ = Struct of def_type * field list 9 | type array = Array of def_type * field list 10 | 11 | type ref_ += StructRef of struct_ 12 | type ref_ += ArrayRef of array 13 | 14 | 15 | let gap sz = 32 - 8 * Pack.packed_size sz 16 | let wrap sz i = Int32.(to_int (logand i (shift_right_logical (-1l) (gap sz)))) 17 | let extend_u sz i = Int32.of_int i 18 | let extend_s sz i = Int32.(shift_right (shift_left (of_int i) (gap sz)) (gap sz)) 19 | 20 | let alloc_field ft v = 21 | let FieldT (_mut, st) = ft in 22 | match st, v with 23 | | ValStorageT _, v -> ValField (ref v) 24 | | PackStorageT sz, Num (I32 i) -> PackField (sz, ref (wrap sz i)) 25 | | _, _ -> failwith "alloc_field" 26 | 27 | let write_field fld v = 28 | match fld, v with 29 | | ValField vr, v -> vr := v 30 | | PackField (sz, ir), Num (I32 i) -> ir := wrap sz i 31 | | _, _ -> failwith "write_field" 32 | 33 | let read_field fld exto = 34 | match fld, exto with 35 | | ValField vr, None -> !vr 36 | | PackField (sz, ir), Some Pack.ZX -> Num (I32 (extend_u sz !ir)) 37 | | PackField (sz, ir), Some Pack.SX -> Num (I32 (extend_s sz !ir)) 38 | | _, _ -> failwith "read_field" 39 | 40 | let array_length (Array (_, fs)) = Lib.List32.length fs 41 | 42 | 43 | let alloc_struct dt vs = 44 | assert Free.((def_type dt).types = Set.empty); 45 | let StructT fts = as_struct_str_type (expand_def_type dt) in 46 | Struct (dt, List.map2 alloc_field fts vs) 47 | 48 | let alloc_array dt vs = 49 | assert Free.((def_type dt).types = Set.empty); 50 | let ArrayT ft = as_array_str_type (expand_def_type dt) in 51 | Array (dt, List.map (alloc_field ft) vs) 52 | 53 | 54 | let type_of_struct (Struct (dt, _)) = dt 55 | let type_of_array (Array (dt, _)) = dt 56 | 57 | 58 | let () = 59 | let type_of_ref' = !Value.type_of_ref' in 60 | Value.type_of_ref' := function 61 | | StructRef s -> DefHT (type_of_struct s) 62 | | ArrayRef a -> DefHT (type_of_array a) 63 | | r -> type_of_ref' r 64 | 65 | let string_of_field = function 66 | | ValField vr -> string_of_value !vr 67 | | PackField (_, ir) -> string_of_int !ir 68 | 69 | let string_of_fields nest fs = 70 | if fs = [] then "" else 71 | if !nest > 0 then " ..." else 72 | let fs', ell = 73 | if List.length fs > 5 74 | then Lib.List.take 5 fs, ["..."] 75 | else fs, [] 76 | in " " ^ String.concat " " (List.map string_of_field fs' @ ell) 77 | 78 | let string_of_aggr name nest fs = 79 | Fun.protect (fun () -> incr nest; "(" ^ name ^ string_of_fields nest fs ^ ")") 80 | ~finally:(fun () -> decr nest) 81 | 82 | let () = 83 | let string_of_ref' = !Value.string_of_ref' in 84 | let nest = ref 0 in 85 | Value.string_of_ref' := function 86 | | StructRef (Struct (_, fs)) -> string_of_aggr "struct" nest fs 87 | | ArrayRef (Array (_, fs)) -> string_of_aggr "array" nest fs 88 | | r -> string_of_ref' r 89 | -------------------------------------------------------------------------------- /interpreter/runtime/aggr.mli: -------------------------------------------------------------------------------- 1 | open Types 2 | open Value 3 | 4 | type field = 5 | | ValField of value ref 6 | | PackField of Pack.pack_size * int ref 7 | 8 | type struct_ = Struct of def_type * field list 9 | type array = Array of def_type * field list 10 | 11 | type ref_ += StructRef of struct_ 12 | type ref_ += ArrayRef of array 13 | 14 | val alloc_struct : def_type -> value list -> struct_ 15 | val alloc_array : def_type -> value list -> array 16 | 17 | val type_of_struct : struct_ -> def_type 18 | val type_of_array : array -> def_type 19 | 20 | val read_field : field -> Pack.extension option -> value (* raises Failure *) 21 | val write_field : field -> value -> unit (* raises Falure *) 22 | 23 | val array_length : array -> int32 24 | -------------------------------------------------------------------------------- /interpreter/runtime/data.ml: -------------------------------------------------------------------------------- 1 | type data = string ref 2 | type t = data 3 | type address = Memory.address 4 | 5 | exception Bounds 6 | 7 | let alloc bs = ref bs 8 | 9 | let size seg = I64.of_int_u (String.length !seg) 10 | 11 | let drop seg = seg := "" 12 | 13 | let load_byte seg a = 14 | let i = Int64.to_int a in 15 | if i < 0 || i >= String.length !seg then raise Bounds; 16 | !seg.[i] 17 | 18 | let load_bytes seg a n = 19 | let i = Int64.to_int a in 20 | if i < 0 || i + n < 0 || i + n > String.length !seg then raise Bounds; 21 | String.sub !seg i n 22 | 23 | 24 | (* Typed accessors *) 25 | 26 | let load_num seg a nt = 27 | let bs = load_bytes seg a (Types.num_size nt) in 28 | Value.num_of_bits nt bs 29 | 30 | let load_num_packed sz ext seg a nt = 31 | let bs = load_bytes seg a (Pack.packed_size sz) in 32 | Value.num_of_packed_bits nt sz ext bs 33 | 34 | let load_vec seg a vt = 35 | let bs = load_bytes seg a (Types.vec_size vt) in 36 | Value.vec_of_bits vt bs 37 | 38 | let load_vec_packed sz ext seg a t = 39 | let bs = load_bytes seg a (Pack.packed_size sz) in 40 | Value.vec_of_packed_bits t sz ext bs 41 | 42 | let load_val seg a t = 43 | let bs = load_bytes seg a (Types.val_size t) in 44 | Value.val_of_bits t bs 45 | 46 | let load_val_storage seg a st = 47 | let bs = load_bytes seg a (Types.storage_size st) in 48 | Value.val_of_storage_bits st bs 49 | -------------------------------------------------------------------------------- /interpreter/runtime/data.mli: -------------------------------------------------------------------------------- 1 | type data 2 | type t = data 3 | type address = Memory.address 4 | 5 | exception Bounds 6 | 7 | val alloc : string -> data 8 | val size : data -> address 9 | val drop : data -> unit 10 | 11 | val load_byte : data -> address -> char (* raises Bounds *) 12 | val load_bytes : data -> address -> int -> string (* raises Bounds *) 13 | 14 | 15 | (* Typed accessors *) 16 | 17 | open Types 18 | open Value 19 | 20 | val load_num : data -> address -> num_type -> num (* raises Bounds *) 21 | val load_vec : data -> address -> vec_type -> vec (* raises Bounds *) 22 | val load_val : data -> address -> val_type -> value (* raises Type, Bounds *) 23 | 24 | val load_num_packed : 25 | Pack.pack_size -> Pack.extension -> data -> address -> num_type -> num 26 | (* raises Type, Bounds *) 27 | val load_vec_packed : 28 | Pack.pack_size -> Pack.vec_extension -> data -> address -> vec_type -> vec 29 | (* raises Type, Bounds *) 30 | val load_val_storage : 31 | data -> address -> storage_type -> value (* raises Type, Bounds *) 32 | -------------------------------------------------------------------------------- /interpreter/runtime/elem.ml: -------------------------------------------------------------------------------- 1 | type elem = Value.ref_ list ref 2 | type t = elem 3 | 4 | exception Bounds 5 | 6 | let alloc rs = ref rs 7 | let size seg = Lib.List32.length !seg 8 | 9 | let load seg i = 10 | if i < 0l || i >= Lib.List32.length !seg then raise Bounds; 11 | Lib.List32.nth !seg i 12 | 13 | let drop seg = seg := [] 14 | -------------------------------------------------------------------------------- /interpreter/runtime/elem.mli: -------------------------------------------------------------------------------- 1 | open Value 2 | 3 | type elem 4 | type t = elem 5 | 6 | exception Bounds 7 | 8 | val alloc : ref_ list -> elem 9 | val size : elem -> Table.size 10 | val load : elem -> Table.index -> ref_ (* raises Bounds *) 11 | val drop : elem -> unit 12 | -------------------------------------------------------------------------------- /interpreter/runtime/extern.ml: -------------------------------------------------------------------------------- 1 | open Types 2 | open Value 3 | 4 | type extern = ref_ 5 | type t = extern 6 | 7 | type ref_ += ExternRef of extern 8 | 9 | let () = 10 | let eq_ref' = !Value.eq_ref' in 11 | Value.eq_ref' := fun r1 r2 -> 12 | match r1, r2 with 13 | | ExternRef r1', ExternRef r2' -> Value.eq_ref r1' r2' 14 | | _, _ -> eq_ref' r1 r2 15 | 16 | let () = 17 | let type_of_ref' = !Value.type_of_ref' in 18 | Value.type_of_ref' := function 19 | | ExternRef _ -> ExternHT 20 | | r -> type_of_ref' r 21 | 22 | let () = 23 | let string_of_ref' = !Value.string_of_ref' in 24 | Value.string_of_ref' := function 25 | | ExternRef r -> "(extern " ^ string_of_ref r ^ ")" 26 | | r -> string_of_ref' r 27 | -------------------------------------------------------------------------------- /interpreter/runtime/extern.mli: -------------------------------------------------------------------------------- 1 | open Value 2 | 3 | type extern = ref_ 4 | type t = extern 5 | 6 | type ref_ += ExternRef of extern 7 | -------------------------------------------------------------------------------- /interpreter/runtime/func.ml: -------------------------------------------------------------------------------- 1 | open Types 2 | open Value 3 | 4 | type 'inst t = 'inst func 5 | and 'inst func = 6 | | AstFunc of def_type * 'inst * Ast.func 7 | | HostFunc of def_type * (value list -> value list) 8 | 9 | let alloc dt inst f = 10 | ignore (as_func_str_type (expand_def_type dt)); 11 | assert Free.((def_type dt).types = Set.empty); 12 | AstFunc (dt, inst, f) 13 | 14 | let alloc_host dt f = 15 | ignore (as_func_str_type (expand_def_type dt)); 16 | HostFunc (dt, f) 17 | 18 | let type_of = function 19 | | AstFunc (dt, _, _) -> dt 20 | | HostFunc (dt, _) -> dt 21 | -------------------------------------------------------------------------------- /interpreter/runtime/func.mli: -------------------------------------------------------------------------------- 1 | open Types 2 | open Value 3 | 4 | type 'inst t = 'inst func 5 | and 'inst func = 6 | | AstFunc of def_type * 'inst * Ast.func 7 | | HostFunc of def_type * (value list -> value list) 8 | 9 | val alloc : def_type -> 'inst -> Ast.func -> 'inst func 10 | val alloc_host : def_type -> (value list -> value list) -> 'inst func 11 | 12 | val type_of : 'inst func -> def_type 13 | -------------------------------------------------------------------------------- /interpreter/runtime/global.ml: -------------------------------------------------------------------------------- 1 | open Types 2 | open Value 3 | 4 | type global = {ty : global_type; mutable content : value} 5 | type t = global 6 | 7 | exception Type 8 | exception NotMutable 9 | 10 | let alloc (GlobalT (_mut, t) as ty) v = 11 | assert Free.((val_type t).types = Set.empty); 12 | if not (Match.match_val_type [] (type_of_value v) t) then raise Type; 13 | {ty; content = v} 14 | 15 | let type_of glob = 16 | glob.ty 17 | 18 | let load glob = 19 | glob.content 20 | 21 | let store glob v = 22 | let GlobalT (mut, t) = glob.ty in 23 | if mut <> Var then raise NotMutable; 24 | if not (Match.match_val_type [] (type_of_value v) t) then raise Type; 25 | glob.content <- v 26 | -------------------------------------------------------------------------------- /interpreter/runtime/global.mli: -------------------------------------------------------------------------------- 1 | open Types 2 | open Value 3 | 4 | type global 5 | type t = global 6 | 7 | exception Type 8 | exception NotMutable 9 | 10 | val alloc : global_type -> value -> global (* raises Type *) 11 | val type_of : global -> global_type 12 | 13 | val load : global -> value 14 | val store : global -> value -> unit (* raises Type, NotMutable *) 15 | -------------------------------------------------------------------------------- /interpreter/runtime/i31.ml: -------------------------------------------------------------------------------- 1 | open Types 2 | open Value 3 | 4 | type i31 = int 5 | type t = i31 6 | 7 | type ref_ += I31Ref of i31 8 | 9 | let of_i32 i = Int32.to_int i land 0x7fff_ffff 10 | let to_i32 ext i = 11 | let i' = Int32.of_int i in 12 | match ext with 13 | | Pack.ZX -> i' 14 | | Pack.SX -> Int32.(shift_right (shift_left i' 1) 1) 15 | 16 | let () = 17 | let eq_ref' = !Value.eq_ref' in 18 | Value.eq_ref' := fun r1 r2 -> 19 | match r1, r2 with 20 | | I31Ref i1, I31Ref i2 -> i1 = i2 21 | | _, _ -> eq_ref' r1 r2 22 | 23 | let () = 24 | let type_of_ref' = !Value.type_of_ref' in 25 | Value.type_of_ref' := function 26 | | I31Ref f -> I31HT 27 | | r -> type_of_ref' r 28 | 29 | let () = 30 | let string_of_ref' = !Value.string_of_ref' in 31 | Value.string_of_ref' := function 32 | | I31Ref i -> "(i31 " ^ string_of_int i ^ ")" 33 | | r -> string_of_ref' r 34 | -------------------------------------------------------------------------------- /interpreter/runtime/i31.mli: -------------------------------------------------------------------------------- 1 | open Value 2 | 3 | type i31 = int 4 | type t = i31 5 | 6 | type ref_ += I31Ref of i31 7 | 8 | val of_i32 : int32 -> i31 9 | val to_i32 : Pack.extension -> i31 -> int32 10 | -------------------------------------------------------------------------------- /interpreter/runtime/instance.ml: -------------------------------------------------------------------------------- 1 | open Types 2 | 3 | type module_inst = 4 | { 5 | types : type_inst list; 6 | funcs : func_inst list; 7 | tables : table_inst list; 8 | memories : memory_inst list; 9 | globals : global_inst list; 10 | elems : elem_inst list; 11 | datas : data_inst list; 12 | exports : export_inst list; 13 | } 14 | 15 | and type_inst = def_type 16 | and func_inst = module_inst Lib.Promise.t Func.t 17 | and table_inst = Table.t 18 | and memory_inst = Memory.t 19 | and global_inst = Global.t 20 | and elem_inst = Elem.t 21 | and data_inst = Data.t 22 | and export_inst = Ast.name * extern 23 | 24 | and extern = 25 | | ExternFunc of func_inst 26 | | ExternTable of table_inst 27 | | ExternMemory of memory_inst 28 | | ExternGlobal of global_inst 29 | 30 | 31 | (* Reference types *) 32 | 33 | type Value.ref_ += FuncRef of func_inst 34 | 35 | let () = 36 | let eq_ref' = !Value.eq_ref' in 37 | Value.eq_ref' := fun r1 r2 -> 38 | match r1, r2 with 39 | | FuncRef _, FuncRef _ -> failwith "eq_ref" 40 | | _, _ -> eq_ref' r1 r2 41 | 42 | let () = 43 | let type_of_ref' = !Value.type_of_ref' in 44 | Value.type_of_ref' := function 45 | | FuncRef f -> DefHT (Func.type_of f) 46 | | r -> type_of_ref' r 47 | 48 | let () = 49 | let string_of_ref' = !Value.string_of_ref' in 50 | Value.string_of_ref' := function 51 | | FuncRef _ -> "func" 52 | | r -> string_of_ref' r 53 | 54 | let () = 55 | let eq_ref' = !Value.eq_ref' in 56 | Value.eq_ref' := fun r1 r2 -> 57 | match r1, r2 with 58 | | FuncRef f1, FuncRef f2 -> f1 == f2 59 | | _, _ -> eq_ref' r1 r2 60 | 61 | 62 | (* Projections *) 63 | 64 | let func_inst_of_extern = function ExternFunc f -> f | _ -> failwith "func_inst_of_extern" 65 | let table_inst_of_extern = function ExternTable f -> f | _ -> failwith "table_inst_of_extern" 66 | let memory_inst_of_extern = function ExternMemory f -> f | _ -> failwith "memory_inst_of_extern" 67 | let global_inst_of_extern = function ExternGlobal f -> f | _ -> failwith "global_inst_of_extern" 68 | 69 | 70 | (* Auxiliary functions *) 71 | 72 | let empty_module_inst = 73 | { types = []; funcs = []; tables = []; memories = []; globals = []; 74 | elems = []; datas = []; exports = [] } 75 | 76 | let extern_type_of c = function 77 | | ExternFunc func -> ExternFuncT (Func.type_of func) 78 | | ExternTable tab -> ExternTableT (Table.type_of tab) 79 | | ExternMemory mem -> ExternMemoryT (Memory.type_of mem) 80 | | ExternGlobal glob -> ExternGlobalT (Global.type_of glob) 81 | 82 | let export inst name = 83 | try Some (List.assoc name inst.exports) with Not_found -> None 84 | -------------------------------------------------------------------------------- /interpreter/runtime/memory.mli: -------------------------------------------------------------------------------- 1 | open Types 2 | 3 | type memory 4 | type t = memory 5 | 6 | type size = int32 (* number of pages *) 7 | type address = int64 8 | type offset = int32 9 | type count = int32 10 | 11 | exception Type 12 | exception Bounds 13 | exception SizeOverflow 14 | exception SizeLimit 15 | exception OutOfMemory 16 | 17 | val page_size : int64 18 | 19 | val alloc : memory_type -> memory (* raises Type, SizeOverflow, OutOfMemory *) 20 | val type_of : memory -> memory_type 21 | val size : memory -> size 22 | val bound : memory -> address 23 | val grow : memory -> size -> unit 24 | (* raises SizeLimit, SizeOverflow, OutOfMemory *) 25 | 26 | val load_byte : memory -> address -> int (* raises Bounds *) 27 | val store_byte : memory -> address -> int -> unit (* raises Bounds *) 28 | val load_bytes : memory -> address -> int -> string (* raises Bounds *) 29 | val store_bytes : memory -> address -> string -> unit (* raises Bounds *) 30 | 31 | 32 | (* Typed accessors *) 33 | 34 | open Value 35 | 36 | val load_num : 37 | memory -> address -> offset -> num_type -> num (* raises Bounds *) 38 | val store_num : 39 | memory -> address -> offset -> num -> unit (* raises Bounds *) 40 | val load_num_packed : 41 | Pack.pack_size -> Pack.extension -> memory -> address -> offset -> num_type -> num 42 | (* raises Type, Bounds *) 43 | val store_num_packed : 44 | Pack.pack_size -> memory -> address -> offset -> num -> unit 45 | (* raises Type, Bounds *) 46 | 47 | val load_vec : 48 | memory -> address -> offset -> vec_type -> vec (* raises Bounds *) 49 | val store_vec : 50 | memory -> address -> offset -> vec -> unit 51 | (* raises Type, Bounds *) 52 | val load_vec_packed : 53 | Pack.pack_size -> Pack.vec_extension -> memory -> address -> offset -> vec_type -> vec 54 | (* raises Type, Bounds *) 55 | 56 | val load_val : 57 | memory -> address -> offset -> val_type -> value (* raises Type, Bounds *) 58 | val store_val : 59 | memory -> address -> offset -> value -> unit (* raises Type, Bounds *) 60 | val load_val_storage : 61 | memory -> address -> offset -> storage_type -> value (* raises Type, Bounds *) 62 | val store_val_storage : 63 | memory -> address -> offset -> storage_type -> value -> unit (* raises Type, Bounds *) 64 | -------------------------------------------------------------------------------- /interpreter/runtime/table.ml: -------------------------------------------------------------------------------- 1 | open Types 2 | open Value 3 | 4 | type size = int32 5 | type index = int32 6 | type count = int32 7 | 8 | type table = {mutable ty : table_type; mutable content : ref_ array} 9 | type t = table 10 | 11 | exception Type 12 | exception Bounds 13 | exception SizeOverflow 14 | exception SizeLimit 15 | exception OutOfMemory 16 | 17 | let valid_limits {min; max} = 18 | match max with 19 | | None -> true 20 | | Some m -> I32.le_u min m 21 | 22 | let create size r = 23 | try Lib.Array32.make size r 24 | with Out_of_memory | Invalid_argument _ -> raise OutOfMemory 25 | 26 | let alloc (TableT (lim, t) as ty) r = 27 | assert Free.((ref_type t).types = Set.empty); 28 | if not (valid_limits lim) then raise Type; 29 | {ty; content = create lim.min r} 30 | 31 | let size tab = 32 | Lib.Array32.length tab.content 33 | 34 | let type_of tab = 35 | tab.ty 36 | 37 | let grow tab delta r = 38 | let TableT (lim, t) = tab.ty in 39 | assert (lim.min = size tab); 40 | let old_size = lim.min in 41 | let new_size = Int32.add old_size delta in 42 | if I32.gt_u old_size new_size then raise SizeOverflow else 43 | let lim' = {lim with min = new_size} in 44 | if not (valid_limits lim') then raise SizeLimit else 45 | let after = create new_size r in 46 | Array.blit tab.content 0 after 0 (Array.length tab.content); 47 | tab.ty <- TableT (lim', t); 48 | tab.content <- after 49 | 50 | let load tab i = 51 | if i < 0l || i >= Lib.Array32.length tab.content then raise Bounds; 52 | Lib.Array32.get tab.content i 53 | 54 | let store tab i r = 55 | let TableT (lim, t) = tab.ty in 56 | if not (Match.match_ref_type [] (type_of_ref r) t) then raise Type; 57 | if i < 0l || i >= Lib.Array32.length tab.content then raise Bounds; 58 | Lib.Array32.set tab.content i r 59 | 60 | let blit tab offset rs = 61 | let data = Array.of_list rs in 62 | let len = Lib.Array32.length data in 63 | if offset < 0l || offset > Int32.sub (Lib.Array32.length tab.content) len then raise Bounds; 64 | Lib.Array32.blit data 0l tab.content offset len 65 | -------------------------------------------------------------------------------- /interpreter/runtime/table.mli: -------------------------------------------------------------------------------- 1 | open Types 2 | open Value 3 | 4 | type table 5 | type t = table 6 | 7 | type size = int32 8 | type index = int32 9 | type count = int32 10 | 11 | exception Type 12 | exception Bounds 13 | exception SizeOverflow 14 | exception SizeLimit 15 | exception OutOfMemory 16 | 17 | val alloc : table_type -> ref_ -> table (* raises Type, OutOfMemory *) 18 | val type_of : table -> table_type 19 | val size : table -> size 20 | val grow : table -> size -> ref_ -> unit 21 | (* raises SizeOverflow, SizeLimit, OutOfMemory *) 22 | 23 | val load : table -> index -> ref_ (* raises Bounds *) 24 | val store : table -> index -> ref_ -> unit (* raises Type, Bounds *) 25 | val blit : table -> index -> ref_ list -> unit (* raises Bounds *) 26 | -------------------------------------------------------------------------------- /interpreter/script/import.ml: -------------------------------------------------------------------------------- 1 | open Source 2 | open Ast 3 | open Types 4 | 5 | module Unknown = Error.Make () 6 | exception Unknown = Unknown.Error (* indicates unknown import name *) 7 | 8 | module Registry = Map.Make(struct type t = Ast.name let compare = compare end) 9 | let registry = ref Registry.empty 10 | 11 | let register name lookup = registry := Registry.add name lookup !registry 12 | 13 | let lookup (ImportT (et, module_name, item_name)) at : Instance.extern = 14 | try Registry.find module_name !registry item_name et with Not_found -> 15 | Unknown.error at 16 | ("unknown import \"" ^ Types.string_of_name module_name ^ 17 | "\".\"" ^ Types.string_of_name item_name ^ "\"") 18 | 19 | let link m = 20 | let ModuleT (its, _) = module_type_of m in 21 | List.map2 lookup its (List.map Source.at m.it.imports) 22 | -------------------------------------------------------------------------------- /interpreter/script/import.mli: -------------------------------------------------------------------------------- 1 | exception Unknown of Source.region * string 2 | 3 | val link : Ast.module_ -> Instance.extern list (* raises Unknown *) 4 | 5 | val register : 6 | Ast.name -> 7 | (Ast.name -> Types.extern_type -> Instance.extern (* raises Not_found *)) -> 8 | unit 9 | -------------------------------------------------------------------------------- /interpreter/script/js.mli: -------------------------------------------------------------------------------- 1 | val of_script : Script.script -> string 2 | -------------------------------------------------------------------------------- /interpreter/script/run.mli: -------------------------------------------------------------------------------- 1 | exception Abort of Source.region * string 2 | exception Assert of Source.region * string 3 | exception IO of Source.region * string 4 | 5 | val trace : string -> unit 6 | 7 | val run_string : string -> bool 8 | val run_file : string -> bool 9 | val run_stdin : unit -> unit 10 | -------------------------------------------------------------------------------- /interpreter/script/script.ml: -------------------------------------------------------------------------------- 1 | type var = string Source.phrase 2 | 3 | type Value.ref_ += HostRef of int32 4 | type num = Value.num Source.phrase 5 | type ref_ = Value.ref_ Source.phrase 6 | type literal = Value.t Source.phrase 7 | 8 | type definition = definition' Source.phrase 9 | and definition' = 10 | | Textual of Ast.module_ 11 | | Encoded of string * string 12 | | Quoted of string * string 13 | 14 | type action = action' Source.phrase 15 | and action' = 16 | | Invoke of var option * Ast.name * literal list 17 | | Get of var option * Ast.name 18 | 19 | type nanop = nanop' Source.phrase 20 | and nanop' = (Lib.void, Lib.void, nan, nan) Value.op 21 | and nan = CanonicalNan | ArithmeticNan 22 | 23 | type num_pat = 24 | | NumPat of num 25 | | NanPat of nanop 26 | 27 | type vec_pat = 28 | | VecPat of (V128.shape * num_pat list) Value.vecop 29 | 30 | type ref_pat = 31 | | RefPat of ref_ 32 | | RefTypePat of Types.heap_type 33 | | NullPat 34 | 35 | type result = result' Source.phrase 36 | and result' = 37 | | NumResult of num_pat 38 | | VecResult of vec_pat 39 | | RefResult of ref_pat 40 | 41 | type assertion = assertion' Source.phrase 42 | and assertion' = 43 | | AssertMalformed of definition * string 44 | | AssertInvalid of definition * string 45 | | AssertUnlinkable of definition * string 46 | | AssertUninstantiable of definition * string 47 | | AssertReturn of action * result list 48 | | AssertTrap of action * string 49 | | AssertExhaustion of action * string 50 | 51 | type command = command' Source.phrase 52 | and command' = 53 | | Module of var option * definition 54 | | Register of Ast.name * var option 55 | | Action of action 56 | | Assertion of assertion 57 | | Meta of meta 58 | 59 | and meta = meta' Source.phrase 60 | and meta' = 61 | | Input of var option * string 62 | | Output of var option * string option 63 | | Script of var option * script 64 | 65 | and script = command list 66 | 67 | 68 | let () = 69 | let type_of_ref' = !Value.type_of_ref' in 70 | Value.type_of_ref' := function 71 | | HostRef _ -> Types.AnyHT 72 | | r -> type_of_ref' r 73 | 74 | let () = 75 | let string_of_ref' = !Value.string_of_ref' in 76 | Value.string_of_ref' := function 77 | | HostRef n -> "(host " ^ Int32.to_string n ^ ")" 78 | | r -> string_of_ref' r 79 | 80 | let () = 81 | let eq_ref' = !Value.eq_ref' in 82 | Value.eq_ref' := fun r1 r2 -> 83 | match r1, r2 with 84 | | HostRef n1, HostRef n2 -> n1 = n2 85 | | _, _ -> eq_ref' r1 r2 86 | -------------------------------------------------------------------------------- /interpreter/syntax/free.mli: -------------------------------------------------------------------------------- 1 | module Set : Set.S with type elt = int32 2 | 3 | type t = 4 | { 5 | types : Set.t; 6 | globals : Set.t; 7 | tables : Set.t; 8 | memories : Set.t; 9 | funcs : Set.t; 10 | elems : Set.t; 11 | datas : Set.t; 12 | locals : Set.t; 13 | labels : Set.t; 14 | } 15 | 16 | val empty : t 17 | val union : t -> t -> t 18 | 19 | val num_type : Types.num_type -> t 20 | val vec_type : Types.vec_type -> t 21 | val ref_type : Types.ref_type -> t 22 | val val_type : Types.val_type -> t 23 | 24 | val func_type : Types.func_type -> t 25 | val global_type : Types.global_type -> t 26 | val table_type : Types.table_type -> t 27 | val memory_type : Types.memory_type -> t 28 | val extern_type : Types.extern_type -> t 29 | 30 | val str_type : Types.str_type -> t 31 | val sub_type : Types.sub_type -> t 32 | val rec_type : Types.rec_type -> t 33 | val def_type : Types.def_type -> t 34 | 35 | val instr : Ast.instr -> t 36 | val block : Ast.instr list -> t 37 | val const : Ast.const -> t 38 | 39 | val type_ : Ast.type_ -> t 40 | val global : Ast.global -> t 41 | val func : Ast.func -> t 42 | val table : Ast.table -> t 43 | val memory : Ast.memory -> t 44 | val elem : Ast.elem_segment -> t 45 | val data : Ast.data_segment -> t 46 | val export : Ast.export -> t 47 | val import : Ast.import -> t 48 | val start : Ast.start -> t 49 | 50 | val module_ : Ast.module_ -> t 51 | 52 | val opt : ('a -> t) -> 'a option -> t 53 | val list : ('a -> t) -> 'a list -> t 54 | -------------------------------------------------------------------------------- /interpreter/syntax/pack.ml: -------------------------------------------------------------------------------- 1 | type pack_size = Pack8 | Pack16 | Pack32 | Pack64 2 | type extension = SX | ZX 3 | 4 | type pack_shape = Pack8x8 | Pack16x4 | Pack32x2 5 | type vec_extension = 6 | | ExtLane of pack_shape * extension 7 | | ExtSplat 8 | | ExtZero 9 | 10 | let packed_size = function 11 | | Pack8 -> 1 12 | | Pack16 -> 2 13 | | Pack32 -> 4 14 | | Pack64 -> 8 15 | 16 | let packed_shape_size = function 17 | | Pack8x8 | Pack16x4 | Pack32x2 -> 8 18 | -------------------------------------------------------------------------------- /interpreter/text/arrange.mli: -------------------------------------------------------------------------------- 1 | open Sexpr 2 | 3 | val instr : Ast.instr -> sexpr 4 | val func : Ast.func -> sexpr 5 | val module_ : Ast.module_ -> sexpr 6 | val script : [`Textual | `Binary] -> Script.script -> sexpr list 7 | -------------------------------------------------------------------------------- /interpreter/text/lexer.mli: -------------------------------------------------------------------------------- 1 | val convert_pos : Lexing.position -> Source.pos 2 | 3 | val token : Lexing.lexbuf -> Parser.token (* raises Source.Error *) 4 | -------------------------------------------------------------------------------- /interpreter/text/parse.ml: -------------------------------------------------------------------------------- 1 | exception Syntax = Parse_error.Syntax 2 | 3 | module type S = 4 | sig 5 | type t 6 | val parse : string -> Lexing.lexbuf -> t 7 | val parse_file : string -> t 8 | val parse_string : string -> t 9 | val parse_channel : in_channel -> t 10 | end 11 | 12 | let convert_pos lexbuf = 13 | { Source.left = Lexer.convert_pos lexbuf.Lexing.lex_start_p; 14 | Source.right = Lexer.convert_pos lexbuf.Lexing.lex_curr_p 15 | } 16 | 17 | let make (type a) (start : _ -> _ -> a) : (module S with type t = a) = 18 | (module struct 19 | type t = a 20 | 21 | let parse name lexbuf = 22 | Lexing.set_filename lexbuf name; 23 | try start Lexer.token lexbuf with Parser.Error -> 24 | raise (Syntax (convert_pos lexbuf, "unexpected token")) 25 | 26 | let parse_string s = 27 | parse "string" (Lexing.from_string ~with_positions:true s) 28 | 29 | let parse_channel oc = 30 | parse "channel" (Lexing.from_channel ~with_positions:true oc) 31 | 32 | let parse_file name = 33 | let oc = open_in name in 34 | Fun.protect ~finally:(fun () -> close_in oc) (fun () -> 35 | parse name (Lexing.from_channel ~with_positions:true oc) 36 | ) 37 | end) 38 | 39 | module Module = (val make Parser.module1) 40 | module Script = (val make Parser.script) 41 | module Script1 = (val make Parser.script1) 42 | -------------------------------------------------------------------------------- /interpreter/text/parse.mli: -------------------------------------------------------------------------------- 1 | exception Syntax of Source.region * string 2 | 3 | module type S = 4 | sig 5 | type t 6 | val parse : string -> Lexing.lexbuf -> t (* raises Syntax *) 7 | val parse_file : string -> t (* raises Syntax *) 8 | val parse_string : string -> t (* raises Syntax *) 9 | val parse_channel : in_channel -> t (* raises Syntax *) 10 | end 11 | 12 | module Module : S with type t = Script.var option * Script.definition 13 | module Script1 : S with type t = Script.script 14 | module Script : S with type t = Script.script 15 | -------------------------------------------------------------------------------- /interpreter/text/parse_error.ml: -------------------------------------------------------------------------------- 1 | (* This is here since both Lexer, Parser, and Parse need it, 2 | * but menhir cannot create a Parser that exports it. *) 3 | exception Syntax of Source.region * string 4 | -------------------------------------------------------------------------------- /interpreter/text/print.ml: -------------------------------------------------------------------------------- 1 | let instr oc width e = Sexpr.output oc width (Arrange.instr e) 2 | let func oc width f = Sexpr.output oc width (Arrange.func f) 3 | let module_ oc width m = Sexpr.output oc width (Arrange.module_ m) 4 | let script oc width mode s = 5 | List.iter (Sexpr.output oc width) (Arrange.script mode s) 6 | -------------------------------------------------------------------------------- /interpreter/text/print.mli: -------------------------------------------------------------------------------- 1 | val instr : out_channel -> int -> Ast.instr -> unit 2 | val func : out_channel -> int -> Ast.func -> unit 3 | val module_ : out_channel -> int -> Ast.module_ -> unit 4 | val script : out_channel -> int -> [`Textual | `Binary] -> Script.script -> unit 5 | -------------------------------------------------------------------------------- /interpreter/util/error.ml: -------------------------------------------------------------------------------- 1 | module Make () = 2 | struct 3 | exception Error of Source.region * string 4 | 5 | let warn at m = prerr_endline (Source.string_of_region at ^ ": warning: " ^ m) 6 | let error at m = raise (Error (at, m)) 7 | end 8 | 9 | -------------------------------------------------------------------------------- /interpreter/util/error.mli: -------------------------------------------------------------------------------- 1 | module Make () : 2 | sig 3 | exception Error of Source.region * string 4 | 5 | val warn : Source.region -> string -> unit 6 | val error : Source.region -> string -> 'a (* raises Error *) 7 | end 8 | 9 | -------------------------------------------------------------------------------- /interpreter/util/sexpr.ml: -------------------------------------------------------------------------------- 1 | type sexpr = Atom of string | Node of string * sexpr list 2 | 3 | type rope = Leaf of string | Concat of rope list 4 | let (^+) s r = Concat [Leaf s; r] 5 | let (+^) r s = Concat [r; Leaf s] 6 | 7 | let rec iter f = function 8 | | Leaf s -> f s 9 | | Concat rs -> List.iter (iter f) rs 10 | 11 | let rec concat = function 12 | | Leaf s -> s 13 | | Concat rs -> String.concat "" (List.map concat rs) 14 | 15 | let rec pp off width = function 16 | | Atom s -> String.length s, Leaf s 17 | | Node (s, xs) -> 18 | let lens, rs = List.split (List.map (pp (off + 2) width) xs) in 19 | let len = String.length s + List.length rs + List.fold_left (+) 2 lens in 20 | let sep, fin = 21 | if off + len <= width then " ", "" 22 | else let indent = String.make off ' ' in "\n " ^ indent, "\n" ^ indent 23 | in len, "(" ^+ s ^+ Concat (List.map (fun r -> sep ^+ r) rs) +^ fin +^ ")" 24 | 25 | let output oc width x = 26 | iter (output_string oc) (snd (pp 0 width x)); 27 | output_string oc "\n"; 28 | flush oc 29 | 30 | let print = output stdout 31 | 32 | let to_string width x = concat (snd (pp 0 width x)) ^ "\n" 33 | -------------------------------------------------------------------------------- /interpreter/util/sexpr.mli: -------------------------------------------------------------------------------- 1 | type sexpr = Atom of string | Node of string * sexpr list 2 | 3 | val output : out_channel -> int -> sexpr -> unit 4 | val print : int -> sexpr -> unit 5 | val to_string : int -> sexpr -> string 6 | -------------------------------------------------------------------------------- /interpreter/util/source.ml: -------------------------------------------------------------------------------- 1 | type pos = {file : string; line : int; column : int} 2 | type region = {left : pos; right : pos} 3 | type 'a phrase = {at : region; it : 'a} 4 | 5 | let (@@) x region = {it = x; at = region} 6 | let it phrase = phrase.it 7 | let at phrase = phrase.at 8 | 9 | 10 | (* Positions and regions *) 11 | 12 | let no_pos = {file = ""; line = 0; column = 0} 13 | let no_region = {left = no_pos; right = no_pos} 14 | 15 | let string_of_pos pos = 16 | if pos.line = -1 then 17 | Printf.sprintf "0x%x" pos.column 18 | else 19 | string_of_int pos.line ^ "." ^ string_of_int (pos.column + 1) 20 | 21 | let string_of_region r = 22 | r.left.file ^ ":" ^ string_of_pos r.left ^ 23 | (if r.right = r.left then "" else "-" ^ string_of_pos r.right) 24 | -------------------------------------------------------------------------------- /interpreter/util/source.mli: -------------------------------------------------------------------------------- 1 | type pos = {file : string; line : int; column : int} 2 | type region = {left : pos; right : pos} 3 | type 'a phrase = {at : region; it : 'a} 4 | 5 | val no_pos : pos 6 | val no_region : region 7 | 8 | val string_of_pos : pos -> string 9 | val string_of_region : region -> string 10 | 11 | val (@@) : 'a -> region -> 'a phrase 12 | val it : 'a phrase -> 'a 13 | val at : 'a phrase -> region 14 | -------------------------------------------------------------------------------- /interpreter/valid/match.mli: -------------------------------------------------------------------------------- 1 | open Types 2 | 3 | 4 | (* Context *) 5 | 6 | type context = def_type list 7 | 8 | 9 | (* Extremas *) 10 | 11 | val top_of_heap_type : context -> heap_type -> heap_type 12 | val bot_of_heap_type : context -> heap_type -> heap_type 13 | val top_of_str_type : context -> str_type -> heap_type 14 | val bot_of_str_type : context -> str_type -> heap_type 15 | 16 | 17 | (* Subtyping *) 18 | 19 | val match_num_type : context -> num_type -> num_type -> bool 20 | val match_ref_type : context -> ref_type -> ref_type -> bool 21 | val match_val_type : context -> val_type -> val_type -> bool 22 | 23 | val match_result_type : context -> result_type -> result_type -> bool 24 | 25 | val match_storage_type : context -> storage_type -> storage_type -> bool 26 | 27 | val match_str_type : context -> str_type -> str_type -> bool 28 | val match_def_type : context -> def_type -> def_type -> bool 29 | 30 | val match_func_type : context -> func_type -> func_type -> bool 31 | 32 | val match_table_type : context -> table_type -> table_type -> bool 33 | val match_memory_type : context -> memory_type -> memory_type -> bool 34 | val match_global_type : context -> global_type -> global_type -> bool 35 | 36 | val match_extern_type : context -> extern_type -> extern_type -> bool 37 | -------------------------------------------------------------------------------- /interpreter/valid/valid.mli: -------------------------------------------------------------------------------- 1 | exception Invalid of Source.region * string 2 | 3 | val check_module : Ast.module_ -> unit (* raises Invalid *) 4 | -------------------------------------------------------------------------------- /interpreter/wasm.opam: -------------------------------------------------------------------------------- 1 | # This file is generated by dune, edit dune-project instead 2 | opam-version: "2.0" 3 | synopsis: 4 | "Library to read and write WebAssembly (Wasm) files and manipulate their AST" 5 | maintainer: ["Andreas Rossberg = "2.9"} 13 | "ocaml" {>= "4.12"} 14 | "menhir" {>= "20220210"} 15 | "odoc" {with-doc} 16 | ] 17 | build: [ 18 | ["dune" "subst"] {dev} 19 | [ 20 | "dune" 21 | "build" 22 | "-p" 23 | name 24 | "-j" 25 | jobs 26 | "--promote-install-files=false" 27 | "@install" 28 | "@runtest" {with-test} 29 | "@doc" {with-doc} 30 | ] 31 | ["dune" "install" "-p" name "--create-install-files" name] 32 | ] 33 | dev-repo: "git+https://github.com/WebAssembly/spec.git" 34 | -------------------------------------------------------------------------------- /papers/LICENSE: -------------------------------------------------------------------------------- 1 | This work is licensed under the Creative Commons Attribution 4.0 International License. To view a copy of this license, visit http://creativecommons.org/licenses/by/4.0/ or send a letter to Creative Commons, PO Box 1866, Mountain View, CA 94042, USA. 2 | -------------------------------------------------------------------------------- /papers/README.md: -------------------------------------------------------------------------------- 1 | * [Bringing the Web up to Speed with WebAssembly](pldi2017.pdf) 2 | 3 | Andreas Haas, Andreas Rossberg, Derek Schuff, Ben Titzer, Dan Gohman, Luke Wagner, Alon Zakai, JF Bastien, Michael Holman 4 | 5 | ACM-SIGPLAN Conference on Programming Language Design and Implementation (PLDI 2017) 6 | 7 | *Describes the WebAssembly design, its formalisation, and initial implementations.* 8 | 9 | * [Weakening WebAssembly](oopsla2019.pdf) 10 | 11 | Conrad Watt, Andreas Rossberg, Jean Pichon-Pharabod 12 | 13 | ACM-SIGPLAN Conference on Object-Oriented Programming, Systems, Language and Architectures (OOSPLA 2019) 14 | 15 | *Describes and formalises the extension of WebAssembly with threads and a suitable memory model.* 16 | -------------------------------------------------------------------------------- /papers/oopsla2019.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WebAssembly/gc/756060f5816c7e2159f4817fbdee76cf52f9c923/papers/oopsla2019.pdf -------------------------------------------------------------------------------- /papers/pldi2017.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WebAssembly/gc/756060f5816c7e2159f4817fbdee76cf52f9c923/papers/pldi2017.pdf -------------------------------------------------------------------------------- /proposals/README.md: -------------------------------------------------------------------------------- 1 | # Proposals 2 | 3 | This directory contains overviews for post-MVP proposals that have been finished and merged into the spec. 4 | Proposals that are not yet finished can be found at https://github.com/WebAssembly/proposals. 5 | 6 | **Note:** The design documents in this folder are archived here for historical purposes and are no longer actively maintained. 7 | Consequently, they may be outdated, contain unfixed errors, or provide insufficient context. 8 | -------------------------------------------------------------------------------- /proposals/gc/Charter.md: -------------------------------------------------------------------------------- 1 | # WebAssembly Garbage Collection Subgroup Charter 2 | 3 | The Garbage Collection Subgroup is a sub-organization of the 4 | [WebAssembly Community Group](https://www.w3.org/community/webassembly/) 5 | of the W3C. 6 | As such, it is intended that its charter align with that of the CG. In particular, 7 | the sections of the [CG charter](https://webassembly.github.io/cg-charter/) relating to 8 | [Community and Business Group Process](https://webassembly.github.io/cg-charter/#process), 9 | [Contribution Mechanics](https://webassembly.github.io/cg-charter/#contrib), 10 | [Transparency](https://webassembly.github.io/cg-charter/#transparency), 11 | and 12 | [Decision Process](https://webassembly.github.io/cg-charter/#decision) 13 | also apply to the Subgroup. 14 | 15 | ## Goals 16 | 17 | The mission of this subgroup is to provide a forum for collaboration on the standardisation of garbage collection support for WebAssembly. 18 | 19 | ## Scope 20 | 21 | The Subgroup will consider topics related to garbage collection for Wasm, including: 22 | 23 | - an instruction set for defining and manipulating managed data types, 24 | - type system rules for validating such instructions, 25 | - APIs for accessing managed data types outside Wasm or a Wasm engine, 26 | - tool and language assists for user-space GC in linear memory, 27 | - code generation for compilers targetting Wasm with GC extensions. 28 | 29 | ## Deliverables 30 | 31 | ### Specifications 32 | 33 | The Subgroup may produce several kinds of specification-related work output: 34 | 35 | - new specifications in standards bodies or working groups 36 | (e.g. W3C WebAssembly WG or Ecma TC39), 37 | 38 | - new specifications outside of standards bodies 39 | (e.g. similar to the LLVM object file format documentation in Wasm tool conventions). 40 | 41 | ### Non-normative reports 42 | 43 | The Subgroup may produce non-normative material such as requirements 44 | documents, recommendations, and case studies. 45 | 46 | ### Software 47 | 48 | The Subgroup may produce software related to garbage collection in Wasm 49 | (either as standalone libraries, tooling, or integration of interface-related functionality in existing CG software). 50 | These may include 51 | 52 | - extensions to the Wasm reference interpreter, 53 | - extensions to the Wasm test suite, 54 | - compilers and tools for producing code that uses Wasm GC extensions, 55 | - tools for implementing Wasm with GC, 56 | - tools for debugging programs using Wasm GC extensions. 57 | 58 | ## Amendments to this Charter and Chair Selection 59 | 60 | This charter may be amended, and Subgroup Chairs may be selected by vote of the full WebAssembly Community Group. 61 | -------------------------------------------------------------------------------- /proposals/sign-extension-ops/Overview.md: -------------------------------------------------------------------------------- 1 | # Sign-extension operators proposal for WebAssembly 2 | 3 | This page describes a proposal for the post-MVP 4 | [sign-extension operator feature][future sext]. 5 | 6 | This proposal adds five new integer instructions for sign-extending 8-bit, 7 | 16-bit, and 32-bit values. 8 | 9 | ## New Sign-extending Operators 10 | 11 | To support sign-extending, five new sign-extension operators are added: 12 | 13 | * `i32.extend8_s`: extend a signed 8-bit integer to a 32-bit integer 14 | * `i32.extend16_s`: extend a signed 16-bit integer to a 32-bit integer 15 | * `i64.extend8_s`: extend a signed 8-bit integer to a 64-bit integer 16 | * `i64.extend16_s`: extend a signed 16-bit integer to a 64-bit integer 17 | * `i64.extend32_s`: extend a signed 32-bit integer to a 64-bit integer 18 | 19 | Note that `i64.extend32_s` was not originally included when this proposal was 20 | discussed in the May 2017 CG meeting. The reason given was that 21 | the behavior matches `i64.extend_s/i32`. It was later discovered that this is 22 | not correct, as `i64.extend_s/i32` sign-extends an `i32` value to `i64`, 23 | whereas `i64.extend32_s` sign-extends an `i64` value to `i64`. The behavior 24 | of `i64.extend32_s` can be emulated with `i32.wrap/i64` followed by 25 | `i64.extend_s/i32`, but the same can be said of the sign-extending load 26 | operations. Therefore, `i64.extend32_s` has been added for consistency. 27 | 28 | ## [Spec Changes][spec] 29 | 30 | The [instruction syntax][] is modified as follows: 31 | 32 | ``` 33 | instr ::= ... | 34 | inn.extend8_s | inn.extend16_s | i64.extend32_s 35 | ``` 36 | 37 | The [instruction binary format][] is modified as follows: 38 | 39 | ``` 40 | instr ::= ... 41 | | 0xC0 => i32.extend8_s 42 | | 0xC1 => i32.extend16_s 43 | | 0xC2 => i64.extend8_s 44 | | 0xC3 => i64.extend16_s 45 | | 0xC4 => i64.extend32_s 46 | ``` 47 | 48 | [future sext]: https://github.com/WebAssembly/design/blob/main/FutureFeatures.md#additional-integer-operators 49 | [instruction syntax]: https://webassembly.github.io/spec/syntax/instructions.html 50 | [instruction binary format]: https://webassembly.github.io/spec/binary/instructions.html 51 | [spec]: https://webassembly.github.io/sign-extension-ops/ 52 | -------------------------------------------------------------------------------- /proposals/simd/TextSIMD.md: -------------------------------------------------------------------------------- 1 | # Text format for SIMD 2 | 3 | ### v128.const 4 | 5 | The `v128.const` instruction has multiple valid text formats corresponding to 6 | different lane interpretations. The valid text formats are 7 | 8 | ``` 9 | v128.const i8x16 i8 i8 i8 i8 i8 i8 i8 i8 i8 i8 i8 i8 i8 i8 i8 i8 10 | v128.const i16x8 i16 i16 i16 i16 i16 i16 i16 i16 11 | v128.const i32x4 i32 i32 i32 i32 12 | v128.const i64x2 i64 i64 13 | v128.const f32x4 f32 f32 f32 f32 14 | v128.const f64x2 f64 f64 15 | ``` 16 | 17 | The canonical text format used for printing `v128.const` instructions is 18 | 19 | ``` 20 | v128.const i32x4 0xNNNNNNNN 0xNNNNNNNN 0xNNNNNNNN 0xNNNNNNNN 21 | ``` 22 | 23 | ### i8x16.shuffle 24 | 25 | ``` 26 | i8x16.shuffle i5 i5 i5 i5 i5 i5 i5 i5 i5 i5 i5 i5 i5 i5 i5 i5 27 | ``` 28 | -------------------------------------------------------------------------------- /proposals/simd/WebAssembly-SIMD-May-2017.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WebAssembly/gc/756060f5816c7e2159f4817fbdee76cf52f9c923/proposals/simd/WebAssembly-SIMD-May-2017.pdf -------------------------------------------------------------------------------- /test/README.md: -------------------------------------------------------------------------------- 1 | This directory contains the WebAssembly test suite. It is split into three 2 | directories: 3 | 4 | * [`core/`](core/), tests for the core semantics 5 | * [`js-api/`](js-api/), tests for the JavaScript API. 6 | * [`html/`](html/), tests for the JavaScript API in a DOM environment. 7 | 8 | A list of to-do's can be found [here](Todo.md). 9 | 10 | ## Multi-stage testing 11 | 12 | The wast tests can be converted to JavaScript, and the JavaScript tests 13 | to HTML tests, using the `build.py` script. It will create a `out/` directory 14 | (checked in in this repository, to be able to use it from github pages), 15 | containing subdirectories with expanded tests, as well as a landing page for 16 | runnning all of them in HTML. 17 | 18 | The HTML tests are just [Web Platform Tests](http://testthewebforward.org) 19 | using the 20 | [testharness.js](https://web-platform-tests.org/writing-tests/testharness-api.html) 21 | library. 22 | 23 | Each wast test gets its equivalent JS test, and each JS test (including wast 24 | test) gets its equivalent WPT, to be easily run in browser vendors' automation. 25 | 26 | ## Procedure for adding a new test 27 | 28 | - put the test in the right directory according to the above (top) description. 29 | - ideally, commit here so the actual content commit and build commit are 30 | separated. 31 | - re-run `build.py` so that the landing page is updated and all the cascading 32 | happens. 33 | - re-commit here, if necessary. 34 | 35 | ## Local HTTP serving of the repository 36 | 37 | From the root of your clone of this repository: 38 | 39 | ``` 40 | python -m SimpleHTTPServer 8000 41 | ``` 42 | 43 | Then open your favorite browser and browse to `http://localhost:8000/test/out`. 44 | -------------------------------------------------------------------------------- /test/Todo.md: -------------------------------------------------------------------------------- 1 | This is a rough list of "tests to write". Everything here should either be 2 | specified in [Semantics.md](https://github.com/WebAssembly/design/blob/main/Semantics.md), 3 | have a link to an open issue/PR, or be obvious. Comments/corrections/additions 4 | welcome. 5 | 6 | Linear memory semantics: 7 | - test that one can clobber the entire contents of the linear memory without corrupting: call stack, local variables, program execution. 8 | 9 | Misc optimizer bait: 10 | - test that the scheduler doesn't move a trapping div past a call which may not return 11 | - test that linearized multidimensional array accesses can have overindexing in interesting ways 12 | - test that 32-bit loop induction variables that wrap aren't promoted to 64-bit 13 | - test that code after a non-obviously infinite loop is not executed 14 | 15 | Misc x86 optimizer bait: 16 | - test that oeq handles NaN right in if, if-else, and setcc cases 17 | 18 | SIMD (post-MVP): 19 | - test that SIMD insert/extract don't canonicalize NaNs 20 | - test that SIMD lanes are in little-endian order 21 | - test non-constant-index and out-of-bounds shuffle masks 22 | - test that subnormals work as intended 23 | - test that byte-misaligned accesses work 24 | 25 | Threads (post-MVP): 26 | - test that thread-local variables are actually thread-local 27 | - test that atomic operations that isLockFree says are lock-free actually are 28 | (is this possible?) 29 | - test that isLockFree is true for datatypes that the spec says should 30 | always be lock-free 31 | - test that 16-bit and 8-bit cmpxchg does a wrapped 8-bit or 16-bit compare 32 | 33 | FMA (post-MVP): 34 | - http://www.vinc17.org/software/fma-tests.c 35 | -------------------------------------------------------------------------------- /test/core/.gitignore: -------------------------------------------------------------------------------- 1 | output -------------------------------------------------------------------------------- /test/core/README.md: -------------------------------------------------------------------------------- 1 | This directory contains tests for the core WebAssembly semantics, as described in [Semantics.md](https://github.com/WebAssembly/design/blob/main/Semantics.md) and specified by the [spec interpreter](https://github.com/WebAssembly/spec/blob/main/interpreter). 2 | 3 | Tests are written in the [S-Expression script format](https://github.com/WebAssembly/spec/blob/main/interpreter/README.md#s-expression-syntax) defined by the interpreter. 4 | 5 | The test suite can be run with the spec interpreter as follows: 6 | ``` 7 | ./run.py --wasm 8 | ``` 9 | where the path points to the spec interpreter executable (or a tool that understands similar options). If the binary is in the working directory, this option can be omitted. 10 | 11 | In addition, the option `--js ` can be given to point to a stand-alone JavaScript interpreter supporting the WebAssembly API. If provided, all tests are also executed in JavaScript. 12 | -------------------------------------------------------------------------------- /test/core/br_on_non_null.wast: -------------------------------------------------------------------------------- 1 | (module 2 | (type $t (func (result i32))) 3 | 4 | (func $nn (param $r (ref $t)) (result i32) 5 | (call_ref $t 6 | (block $l (result (ref $t)) 7 | (br_on_non_null $l (local.get $r)) 8 | (return (i32.const -1)) 9 | ) 10 | ) 11 | ) 12 | (func $n (param $r (ref null $t)) (result i32) 13 | (call_ref $t 14 | (block $l (result (ref $t)) 15 | (br_on_non_null $l (local.get $r)) 16 | (return (i32.const -1)) 17 | ) 18 | ) 19 | ) 20 | 21 | (elem func $f) 22 | (func $f (result i32) (i32.const 7)) 23 | 24 | (func (export "nullable-null") (result i32) (call $n (ref.null $t))) 25 | (func (export "nonnullable-f") (result i32) (call $nn (ref.func $f))) 26 | (func (export "nullable-f") (result i32) (call $n (ref.func $f))) 27 | 28 | (func (export "unreachable") (result i32) 29 | (block $l (result (ref $t)) 30 | (br_on_non_null $l (unreachable)) 31 | (return (i32.const -1)) 32 | ) 33 | (call_ref $t) 34 | ) 35 | ) 36 | 37 | (assert_trap (invoke "unreachable") "unreachable") 38 | 39 | (assert_return (invoke "nullable-null") (i32.const -1)) 40 | (assert_return (invoke "nonnullable-f") (i32.const 7)) 41 | (assert_return (invoke "nullable-f") (i32.const 7)) 42 | 43 | (module 44 | (type $t (func)) 45 | (func (param $r (ref null $t)) (drop (block (result (ref $t)) (br_on_non_null 0 (local.get $r)) (unreachable)))) 46 | (func (param $r (ref null func)) (drop (block (result (ref func)) (br_on_non_null 0 (local.get $r)) (unreachable)))) 47 | (func (param $r (ref null extern)) (drop (block (result (ref extern)) (br_on_non_null 0 (local.get $r)) (unreachable)))) 48 | ) 49 | 50 | 51 | (module 52 | (type $t (func (param i32) (result i32))) 53 | (elem func $f) 54 | (func $f (param i32) (result i32) (i32.mul (local.get 0) (local.get 0))) 55 | 56 | (func $a (param $n i32) (param $r (ref null $t)) (result i32) 57 | (call_ref $t 58 | (block $l (result i32 (ref $t)) 59 | (return (br_on_non_null $l (local.get $n) (local.get $r))) 60 | ) 61 | ) 62 | ) 63 | 64 | (func (export "args-null") (param $n i32) (result i32) 65 | (call $a (local.get $n) (ref.null $t)) 66 | ) 67 | (func (export "args-f") (param $n i32) (result i32) 68 | (call $a (local.get $n) (ref.func $f)) 69 | ) 70 | ) 71 | 72 | (assert_return (invoke "args-null" (i32.const 3)) (i32.const 3)) 73 | (assert_return (invoke "args-f" (i32.const 3)) (i32.const 9)) 74 | 75 | 76 | ;; https://github.com/WebAssembly/gc/issues/516 77 | (assert_invalid 78 | (module 79 | (type $t (func)) 80 | (func $f (param (ref null $t)) (result funcref) (local.get 0)) 81 | (func (param funcref) (result funcref funcref) 82 | (ref.null $t) 83 | (local.get 0) 84 | (br_on_non_null 0) ;; only leaves a funcref on the stack 85 | (call $f) 86 | (local.get 0) 87 | ) 88 | ) 89 | "type mismatch" 90 | ) 91 | -------------------------------------------------------------------------------- /test/core/br_on_null.wast: -------------------------------------------------------------------------------- 1 | (module 2 | (type $t (func (result i32))) 3 | 4 | (func $nn (param $r (ref $t)) (result i32) 5 | (block $l 6 | (return (call_ref $t (br_on_null $l (local.get $r)))) 7 | ) 8 | (i32.const -1) 9 | ) 10 | (func $n (param $r (ref null $t)) (result i32) 11 | (block $l 12 | (return (call_ref $t (br_on_null $l (local.get $r)))) 13 | ) 14 | (i32.const -1) 15 | ) 16 | 17 | (elem func $f) 18 | (func $f (result i32) (i32.const 7)) 19 | 20 | (func (export "nullable-null") (result i32) (call $n (ref.null $t))) 21 | (func (export "nonnullable-f") (result i32) (call $nn (ref.func $f))) 22 | (func (export "nullable-f") (result i32) (call $n (ref.func $f))) 23 | 24 | (func (export "unreachable") (result i32) 25 | (block $l 26 | (return (call_ref $t (br_on_null $l (unreachable)))) 27 | ) 28 | (i32.const -1) 29 | ) 30 | ) 31 | 32 | (assert_trap (invoke "unreachable") "unreachable") 33 | 34 | (assert_return (invoke "nullable-null") (i32.const -1)) 35 | (assert_return (invoke "nonnullable-f") (i32.const 7)) 36 | (assert_return (invoke "nullable-f") (i32.const 7)) 37 | 38 | (module 39 | (type $t (func)) 40 | (func (param $r (ref null $t)) (drop (br_on_null 0 (local.get $r)))) 41 | (func (param $r (ref null func)) (drop (br_on_null 0 (local.get $r)))) 42 | (func (param $r (ref null extern)) (drop (br_on_null 0 (local.get $r)))) 43 | ) 44 | 45 | 46 | (module 47 | (type $t (func (param i32) (result i32))) 48 | (elem func $f) 49 | (func $f (param i32) (result i32) (i32.mul (local.get 0) (local.get 0))) 50 | 51 | (func $a (param $n i32) (param $r (ref null $t)) (result i32) 52 | (block $l (result i32) 53 | (return (call_ref $t (br_on_null $l (local.get $n) (local.get $r)))) 54 | ) 55 | ) 56 | 57 | (func (export "args-null") (param $n i32) (result i32) 58 | (call $a (local.get $n) (ref.null $t)) 59 | ) 60 | (func (export "args-f") (param $n i32) (result i32) 61 | (call $a (local.get $n) (ref.func $f)) 62 | ) 63 | ) 64 | 65 | (assert_return (invoke "args-null" (i32.const 3)) (i32.const 3)) 66 | (assert_return (invoke "args-f" (i32.const 3)) (i32.const 9)) 67 | 68 | 69 | ;; https://github.com/WebAssembly/gc/issues/516 70 | ;; Tests that validators are correctly doing 71 | ;; 72 | ;; pop_operands(label_types) 73 | ;; push_operands(label_types) 74 | ;; 75 | ;; for validating br_on_null, rather than incorrectly doing either 76 | ;; 77 | ;; push_operands(pop_operands(label_types)) 78 | ;; 79 | ;; or just inspecting the types on the stack without any pushing or 80 | ;; popping, neither of which handle subtyping correctly. 81 | (assert_invalid 82 | (module 83 | (type $t (func)) 84 | (func $f (param (ref null $t)) (result funcref) (local.get 0)) 85 | (func (param funcref) (result funcref) 86 | (ref.null $t) 87 | (local.get 0) 88 | (br_on_null 0) ;; only leaves two funcref's on the stack 89 | (drop) 90 | (call $f) 91 | ) 92 | ) 93 | "type mismatch" 94 | ) 95 | -------------------------------------------------------------------------------- /test/core/comments.wast: -------------------------------------------------------------------------------- 1 | ;; Test comment syntax 2 | 3 | ;;comment 4 | 5 | ;;;;;;;;;;; 6 | 7 | ;;comment 8 | 9 | ( ;;comment 10 | module;;comment 11 | );;comment 12 | 13 | ;;) 14 | ;;;) 15 | ;; ;) 16 | ;; (; 17 | 18 | (;;) 19 | 20 | (;comment;) 21 | 22 | (;;comment;) 23 | 24 | (;;;comment;) 25 | 26 | (;;;;;;;;;;;;;;) 27 | 28 | (;(((((((((( ;) 29 | 30 | (;)))))))))));) 31 | 32 | (;comment";) 33 | 34 | (;comment"";) 35 | 36 | (;comment""";) 37 | 38 | ;; ASCII 00-1F, 7F 39 | (; 40 | 41 | ;) 42 | 43 | ;; null-byte followed immediately by end-of-comment delimiter 44 | (;;) 45 | 46 | 47 | (;Heiße Würstchen;) 48 | 49 | (;;) 50 | 51 | (;comment 52 | comment;) 53 | 54 | (;comment;) 55 | 56 | (;comment;)((;comment;) 57 | (;comment;)module(;comment;) 58 | (;comment;))(;comment;) 59 | 60 | (;comment(;nested;)comment;) 61 | 62 | (;comment 63 | (;nested 64 | ;)comment 65 | ;) 66 | 67 | (module 68 | (;comment(;nested(;further;)nested;)comment;) 69 | ) 70 | 71 | (;comment;;comment;) 72 | 73 | (;comment;;comment 74 | ;) 75 | 76 | (module 77 | (;comment;;comment(;nested;)comment;) 78 | ) 79 | 80 | 81 | ;; Newline recognition 82 | 83 | (module quote 84 | "(func (export \"f1\") (result i32)" 85 | " (i32.const 1)" 86 | " ;; comment\0a" 87 | " (return (i32.const 2))" 88 | "\0a" 89 | ")" 90 | "(func (export \"f2\") (result i32)" 91 | " (i32.const 1)" 92 | " ;; comment\0d" 93 | " (return (i32.const 2))" 94 | "\0a" 95 | ")" 96 | "(func (export \"f3\") (result i32)" 97 | " (i32.const 1)" 98 | " ;; comment\0d\0a" 99 | " (return (i32.const 2))" 100 | "\0a" 101 | ")" 102 | ) 103 | 104 | (assert_return (invoke "f1") (i32.const 2)) 105 | (assert_return (invoke "f2") (i32.const 2)) 106 | (assert_return (invoke "f3") (i32.const 2)) 107 | -------------------------------------------------------------------------------- /test/core/forward.wast: -------------------------------------------------------------------------------- 1 | (module 2 | (func $even (export "even") (param $n i32) (result i32) 3 | (if (result i32) (i32.eq (local.get $n) (i32.const 0)) 4 | (then (i32.const 1)) 5 | (else (call $odd (i32.sub (local.get $n) (i32.const 1)))) 6 | ) 7 | ) 8 | 9 | (func $odd (export "odd") (param $n i32) (result i32) 10 | (if (result i32) (i32.eq (local.get $n) (i32.const 0)) 11 | (then (i32.const 0)) 12 | (else (call $even (i32.sub (local.get $n) (i32.const 1)))) 13 | ) 14 | ) 15 | ) 16 | 17 | (assert_return (invoke "even" (i32.const 13)) (i32.const 0)) 18 | (assert_return (invoke "even" (i32.const 20)) (i32.const 1)) 19 | (assert_return (invoke "odd" (i32.const 13)) (i32.const 1)) 20 | (assert_return (invoke "odd" (i32.const 20)) (i32.const 0)) 21 | -------------------------------------------------------------------------------- /test/core/gc/array_fill.wast: -------------------------------------------------------------------------------- 1 | ;; Bulk instructions 2 | 3 | ;; invalid uses 4 | 5 | (assert_invalid 6 | (module 7 | (type $a (array i8)) 8 | 9 | (func (export "array.fill-immutable") (param $1 (ref $a)) (param $2 i32) 10 | (array.fill $a (local.get $1) (i32.const 0) (local.get $2) (i32.const 0)) 11 | ) 12 | ) 13 | "array is immutable" 14 | ) 15 | 16 | (assert_invalid 17 | (module 18 | (type $a (array (mut i8))) 19 | 20 | (func (export "array.fill-invalid-1") (param $1 (ref $a)) (param $2 funcref) 21 | (array.fill $a (local.get $1) (i32.const 0) (local.get $2) (i32.const 0)) 22 | ) 23 | ) 24 | "type mismatch" 25 | ) 26 | 27 | (assert_invalid 28 | (module 29 | (type $b (array (mut funcref))) 30 | 31 | (func (export "array.fill-invalid-1") (param $1 (ref $b)) (param $2 i32) 32 | (array.fill $b (local.get $1) (i32.const 0) (local.get $2) (i32.const 0)) 33 | ) 34 | ) 35 | "type mismatch" 36 | ) 37 | 38 | (module 39 | (type $arr8 (array i8)) 40 | (type $arr8_mut (array (mut i8))) 41 | 42 | (global $g_arr8 (ref $arr8) (array.new $arr8 (i32.const 10) (i32.const 12))) 43 | (global $g_arr8_mut (mut (ref $arr8_mut)) (array.new_default $arr8_mut (i32.const 12))) 44 | 45 | (func (export "array_get_nth") (param $1 i32) (result i32) 46 | (array.get_u $arr8_mut (global.get $g_arr8_mut) (local.get $1)) 47 | ) 48 | 49 | (func (export "array_fill-null") 50 | (array.fill $arr8_mut (ref.null $arr8_mut) (i32.const 0) (i32.const 0) (i32.const 0)) 51 | ) 52 | 53 | (func (export "array_fill") (param $1 i32) (param $2 i32) (param $3 i32) 54 | (array.fill $arr8_mut (global.get $g_arr8_mut) (local.get $1) (local.get $2) (local.get $3)) 55 | ) 56 | ) 57 | 58 | ;; null array argument traps 59 | (assert_trap (invoke "array_fill-null") "null array reference") 60 | 61 | ;; OOB initial index traps 62 | (assert_trap (invoke "array_fill" (i32.const 13) (i32.const 0) (i32.const 0)) "out of bounds array access") 63 | 64 | ;; OOB length traps 65 | (assert_trap (invoke "array_fill" (i32.const 0) (i32.const 0) (i32.const 13)) "out of bounds array access") 66 | 67 | ;; start index = array size, len = 0 doesn't trap 68 | (assert_return (invoke "array_fill" (i32.const 12) (i32.const 0) (i32.const 0))) 69 | 70 | ;; check arrays were not modified 71 | (assert_return (invoke "array_get_nth" (i32.const 0)) (i32.const 0)) 72 | (assert_return (invoke "array_get_nth" (i32.const 5)) (i32.const 0)) 73 | (assert_return (invoke "array_get_nth" (i32.const 11)) (i32.const 0)) 74 | (assert_trap (invoke "array_get_nth" (i32.const 12)) "out of bounds array access") 75 | 76 | ;; normal case 77 | (assert_return (invoke "array_fill" (i32.const 2) (i32.const 11) (i32.const 2))) 78 | (assert_return (invoke "array_get_nth" (i32.const 1)) (i32.const 0)) 79 | (assert_return (invoke "array_get_nth" (i32.const 2)) (i32.const 11)) 80 | (assert_return (invoke "array_get_nth" (i32.const 3)) (i32.const 11)) 81 | (assert_return (invoke "array_get_nth" (i32.const 4)) (i32.const 0)) 82 | -------------------------------------------------------------------------------- /test/core/gc/array_new_data.wast: -------------------------------------------------------------------------------- 1 | (module 2 | (type $arr (array (mut i8))) 3 | 4 | (data $d "abcd") 5 | 6 | (func (export "array-new-data") (param i32 i32) (result (ref $arr)) 7 | (array.new_data $arr $d (local.get 0) (local.get 1)) 8 | ) 9 | ) 10 | 11 | ;; In-bounds data segment accesses. 12 | (assert_return (invoke "array-new-data" (i32.const 0) (i32.const 0)) (ref.array)) 13 | (assert_return (invoke "array-new-data" (i32.const 0) (i32.const 4)) (ref.array)) 14 | (assert_return (invoke "array-new-data" (i32.const 1) (i32.const 2)) (ref.array)) 15 | (assert_return (invoke "array-new-data" (i32.const 4) (i32.const 0)) (ref.array)) 16 | 17 | ;; Out-of-bounds data segment accesses. 18 | (assert_trap (invoke "array-new-data" (i32.const 0) (i32.const 5)) "out of bounds memory access") 19 | (assert_trap (invoke "array-new-data" (i32.const 5) (i32.const 0)) "out of bounds memory access") 20 | (assert_trap (invoke "array-new-data" (i32.const 1) (i32.const 4)) "out of bounds memory access") 21 | (assert_trap (invoke "array-new-data" (i32.const 4) (i32.const 1)) "out of bounds memory access") 22 | 23 | 24 | (module 25 | (type $arr (array (mut i8))) 26 | 27 | (data $d "\aa\bb\cc\dd") 28 | 29 | (func (export "array-new-data-contents") (result i32 i32) 30 | (local (ref $arr)) 31 | (local.set 0 (array.new_data $arr $d (i32.const 1) (i32.const 2))) 32 | (array.get_u $arr (local.get 0) (i32.const 0)) 33 | (array.get_u $arr (local.get 0) (i32.const 1)) 34 | ) 35 | ) 36 | 37 | ;; Array is initialized with the correct contents. 38 | (assert_return (invoke "array-new-data-contents") (i32.const 0xbb) (i32.const 0xcc)) 39 | 40 | (module 41 | (type $arr (array (mut i32))) 42 | 43 | (data $d "\aa\bb\cc\dd") 44 | 45 | (func (export "array-new-data-little-endian") (result i32) 46 | (array.get $arr 47 | (array.new_data $arr $d (i32.const 0) (i32.const 1)) 48 | (i32.const 0)) 49 | ) 50 | ) 51 | 52 | ;; Data segments are interpreted as little-endian. 53 | (assert_return (invoke "array-new-data-little-endian") (i32.const 0xddccbbaa)) 54 | 55 | (module 56 | (type $arr (array (mut i16))) 57 | 58 | (data $d "\00\11\22") 59 | 60 | (func (export "array-new-data-unaligned") (result i32) 61 | (array.get_u $arr 62 | (array.new_data $arr $d (i32.const 1) (i32.const 1)) 63 | (i32.const 0)) 64 | ) 65 | ) 66 | 67 | ;; Data inside the segment doesn't need to be aligned to the element size. 68 | (assert_return (invoke "array-new-data-unaligned") (i32.const 0x2211)) 69 | -------------------------------------------------------------------------------- /test/core/gc/binary-gc.wast: -------------------------------------------------------------------------------- 1 | (assert_malformed 2 | (module binary 3 | "\00asm" "\01\00\00\00" 4 | "\01" ;; Type section id 5 | "\04" ;; Type section length 6 | "\01" ;; Types vector length 7 | "\5e" ;; Array type, -0x22 8 | "\78" ;; Storage type: i8 or -0x08 9 | "\02" ;; Mutability, should be 0 or 1, but isn't 10 | ) 11 | "malformed mutability" 12 | ) 13 | -------------------------------------------------------------------------------- /test/core/gc/extern.wast: -------------------------------------------------------------------------------- 1 | (module 2 | (type $ft (func)) 3 | (type $st (struct)) 4 | (type $at (array i8)) 5 | 6 | (table 10 anyref) 7 | 8 | (elem declare func $f) 9 | (func $f) 10 | 11 | (func (export "init") (param $x externref) 12 | (table.set (i32.const 0) (ref.null any)) 13 | (table.set (i32.const 1) (ref.i31 (i32.const 7))) 14 | (table.set (i32.const 2) (struct.new_default $st)) 15 | (table.set (i32.const 3) (array.new_default $at (i32.const 0))) 16 | (table.set (i32.const 4) (any.convert_extern (local.get $x))) 17 | ) 18 | 19 | (func (export "internalize") (param externref) (result anyref) 20 | (any.convert_extern (local.get 0)) 21 | ) 22 | (func (export "externalize") (param anyref) (result externref) 23 | (extern.convert_any (local.get 0)) 24 | ) 25 | 26 | (func (export "externalize-i") (param i32) (result externref) 27 | (extern.convert_any (table.get (local.get 0))) 28 | ) 29 | (func (export "externalize-ii") (param i32) (result anyref) 30 | (any.convert_extern (extern.convert_any (table.get (local.get 0)))) 31 | ) 32 | ) 33 | 34 | (invoke "init" (ref.extern 0)) 35 | 36 | (assert_return (invoke "internalize" (ref.extern 1)) (ref.host 1)) 37 | (assert_return (invoke "internalize" (ref.null extern)) (ref.null any)) 38 | 39 | (assert_return (invoke "externalize" (ref.host 2)) (ref.extern 2)) 40 | (assert_return (invoke "externalize" (ref.null any)) (ref.null extern)) 41 | 42 | (assert_return (invoke "externalize-i" (i32.const 0)) (ref.null extern)) 43 | (assert_return (invoke "externalize-i" (i32.const 1)) (ref.extern)) 44 | (assert_return (invoke "externalize-i" (i32.const 2)) (ref.extern)) 45 | (assert_return (invoke "externalize-i" (i32.const 3)) (ref.extern)) 46 | (assert_return (invoke "externalize-i" (i32.const 4)) (ref.extern)) 47 | (assert_return (invoke "externalize-i" (i32.const 5)) (ref.null extern)) 48 | 49 | (assert_return (invoke "externalize-ii" (i32.const 0)) (ref.null any)) 50 | (assert_return (invoke "externalize-ii" (i32.const 1)) (ref.i31)) 51 | (assert_return (invoke "externalize-ii" (i32.const 2)) (ref.struct)) 52 | (assert_return (invoke "externalize-ii" (i32.const 3)) (ref.array)) 53 | (assert_return (invoke "externalize-ii" (i32.const 4)) (ref.host 0)) 54 | (assert_return (invoke "externalize-ii" (i32.const 5)) (ref.null any)) 55 | -------------------------------------------------------------------------------- /test/core/gc/type-subtyping-invalid.wast: -------------------------------------------------------------------------------- 1 | (assert_invalid 2 | (module 3 | ;; When fields are mutable, a subtype's reference fields cannot be subtypes of 4 | ;; the supertype's fields, they must match exactly. 5 | (type $a (sub (struct (field (mut (ref null any)))))) 6 | (type $b (sub $a (struct (field (mut (ref null none)))))) 7 | ) 8 | "sub type 1 does not match super type" 9 | ) 10 | 11 | (assert_invalid 12 | (module 13 | ;; When fields are const, a subtype's reference fields cannot be supertypes of 14 | ;; the supertype's fields, they must be subtypes. 15 | (type $a (sub (struct (field (ref null none))))) 16 | (type $b (sub $a (struct (field (ref null any))))) 17 | ) 18 | "sub type 1 does not match super type" 19 | ) 20 | 21 | (assert_invalid 22 | (module 23 | ;; The mutability of fields must be the same. 24 | (type $c (sub (struct (field (mut (ref null any)))))) 25 | (type $d (sub $c (struct (field (ref null any))))) 26 | ) 27 | "sub type 1 does not match super type" 28 | ) 29 | 30 | (assert_invalid 31 | (module 32 | ;; The mutability of fields must be the same. 33 | (type $c (sub (struct (field (ref null any))))) 34 | (type $d (sub $c (struct (field (mut (ref null any)))))) 35 | ) 36 | "sub type 1 does not match super type" 37 | ) 38 | -------------------------------------------------------------------------------- /test/core/inline-module.wast: -------------------------------------------------------------------------------- 1 | (func) (memory 0) (func (export "f")) 2 | -------------------------------------------------------------------------------- /test/core/local_init.wast: -------------------------------------------------------------------------------- 1 | ;; Uninitialized undefaulted locals 2 | 3 | (module 4 | (func (export "get-after-set") (param $p (ref extern)) (result (ref extern)) 5 | (local $x (ref extern)) 6 | (local.set $x (local.get $p)) 7 | (local.get $x) 8 | ) 9 | (func (export "get-after-tee") (param $p (ref extern)) (result (ref extern)) 10 | (local $x (ref extern)) 11 | (drop (local.tee $x (local.get $p))) 12 | (local.get $x) 13 | ) 14 | (func (export "get-in-block-after-set") (param $p (ref extern)) (result (ref extern)) 15 | (local $x (ref extern)) 16 | (local.set $x (local.get $p)) 17 | (block (result (ref extern)) (local.get $x)) 18 | ) 19 | ) 20 | 21 | (assert_return (invoke "get-after-set" (ref.extern 1)) (ref.extern 1)) 22 | (assert_return (invoke "get-after-tee" (ref.extern 2)) (ref.extern 2)) 23 | (assert_return (invoke "get-in-block-after-set" (ref.extern 3)) (ref.extern 3)) 24 | 25 | (assert_invalid 26 | (module (func $uninit (local $x (ref extern)) (drop (local.get $x)))) 27 | "uninitialized local" 28 | ) 29 | (assert_invalid 30 | (module 31 | (func $uninit-after-end (param $p (ref extern)) 32 | (local $x (ref extern)) 33 | (block (local.set $x (local.get $p)) (drop (local.tee $x (local.get $p)))) 34 | (drop (local.get $x)) 35 | ) 36 | ) 37 | "uninitialized local" 38 | ) 39 | (assert_invalid 40 | (module 41 | (func $uninit-in-else (param $p (ref extern)) 42 | (local $x (ref extern)) 43 | (if (i32.const 0) 44 | (then (local.set $x (local.get $p))) 45 | (else (local.get $x)) 46 | ) 47 | ) 48 | ) 49 | "uninitialized local" 50 | ) 51 | 52 | (assert_invalid 53 | (module 54 | (func $uninit-from-if (param $p (ref extern)) 55 | (local $x (ref extern)) 56 | (if (i32.const 0) 57 | (then (local.set $x (local.get $p))) 58 | (else (local.set $x (local.get $p))) 59 | ) 60 | (drop (local.get $x)) 61 | ) 62 | ) 63 | "uninitialized local" 64 | ) 65 | 66 | (module 67 | (func (export "tee-init") (param $p (ref extern)) (result (ref extern)) 68 | (local $x (ref extern)) 69 | (drop (local.tee $x (local.get $p))) 70 | (local.get $x) 71 | ) 72 | ) 73 | 74 | (assert_return (invoke "tee-init" (ref.extern 1)) (ref.extern 1)) 75 | -------------------------------------------------------------------------------- /test/core/memory_redundancy.wast: -------------------------------------------------------------------------------- 1 | ;; Test that optimizers don't do redundant-load, store-to-load, or dead-store 2 | ;; optimizations when there are interfering stores, even of different types 3 | ;; and to non-identical addresses. 4 | 5 | (module 6 | (memory 1 1) 7 | 8 | (func (export "zero_everything") 9 | (i32.store (i32.const 0) (i32.const 0)) 10 | (i32.store (i32.const 4) (i32.const 0)) 11 | (i32.store (i32.const 8) (i32.const 0)) 12 | (i32.store (i32.const 12) (i32.const 0)) 13 | ) 14 | 15 | (func (export "test_store_to_load") (result i32) 16 | (i32.store (i32.const 8) (i32.const 0)) 17 | (f32.store (i32.const 5) (f32.const -0.0)) 18 | (i32.load (i32.const 8)) 19 | ) 20 | 21 | (func (export "test_redundant_load") (result i32) 22 | (local $t i32) 23 | (local $s i32) 24 | (local.set $t (i32.load (i32.const 8))) 25 | (i32.store (i32.const 5) (i32.const 0x80000000)) 26 | (local.set $s (i32.load (i32.const 8))) 27 | (i32.add (local.get $t) (local.get $s)) 28 | ) 29 | 30 | (func (export "test_dead_store") (result f32) 31 | (local $t f32) 32 | (i32.store (i32.const 8) (i32.const 0x23232323)) 33 | (local.set $t (f32.load (i32.const 11))) 34 | (i32.store (i32.const 8) (i32.const 0)) 35 | (local.get $t) 36 | ) 37 | 38 | ;; A function named "malloc" which implementations nonetheless shouldn't 39 | ;; assume behaves like C malloc. 40 | (func $malloc (export "malloc") 41 | (param $size i32) 42 | (result i32) 43 | (i32.const 16) 44 | ) 45 | 46 | ;; Call malloc twice, but unlike C malloc, we don't get non-aliasing pointers. 47 | (func (export "malloc_aliasing") 48 | (result i32) 49 | (local $x i32) 50 | (local $y i32) 51 | (local.set $x (call $malloc (i32.const 4))) 52 | (local.set $y (call $malloc (i32.const 4))) 53 | (i32.store (local.get $x) (i32.const 42)) 54 | (i32.store (local.get $y) (i32.const 43)) 55 | (i32.load (local.get $x)) 56 | ) 57 | ) 58 | 59 | (assert_return (invoke "test_store_to_load") (i32.const 0x00000080)) 60 | (invoke "zero_everything") 61 | (assert_return (invoke "test_redundant_load") (i32.const 0x00000080)) 62 | (invoke "zero_everything") 63 | (assert_return (invoke "test_dead_store") (f32.const 0x1.18p-144)) 64 | (invoke "zero_everything") 65 | (assert_return (invoke "malloc_aliasing") (i32.const 43)) 66 | -------------------------------------------------------------------------------- /test/core/memory_size.wast: -------------------------------------------------------------------------------- 1 | (module 2 | (memory 0) 3 | (func (export "size") (result i32) (memory.size)) 4 | (func (export "grow") (param $sz i32) (drop (memory.grow (local.get $sz)))) 5 | ) 6 | 7 | (assert_return (invoke "size") (i32.const 0)) 8 | (assert_return (invoke "grow" (i32.const 1))) 9 | (assert_return (invoke "size") (i32.const 1)) 10 | (assert_return (invoke "grow" (i32.const 4))) 11 | (assert_return (invoke "size") (i32.const 5)) 12 | (assert_return (invoke "grow" (i32.const 0))) 13 | (assert_return (invoke "size") (i32.const 5)) 14 | 15 | (module 16 | (memory 1) 17 | (func (export "size") (result i32) (memory.size)) 18 | (func (export "grow") (param $sz i32) (drop (memory.grow (local.get $sz)))) 19 | ) 20 | 21 | (assert_return (invoke "size") (i32.const 1)) 22 | (assert_return (invoke "grow" (i32.const 1))) 23 | (assert_return (invoke "size") (i32.const 2)) 24 | (assert_return (invoke "grow" (i32.const 4))) 25 | (assert_return (invoke "size") (i32.const 6)) 26 | (assert_return (invoke "grow" (i32.const 0))) 27 | (assert_return (invoke "size") (i32.const 6)) 28 | 29 | (module 30 | (memory 0 2) 31 | (func (export "size") (result i32) (memory.size)) 32 | (func (export "grow") (param $sz i32) (drop (memory.grow (local.get $sz)))) 33 | ) 34 | 35 | (assert_return (invoke "size") (i32.const 0)) 36 | (assert_return (invoke "grow" (i32.const 3))) 37 | (assert_return (invoke "size") (i32.const 0)) 38 | (assert_return (invoke "grow" (i32.const 1))) 39 | (assert_return (invoke "size") (i32.const 1)) 40 | (assert_return (invoke "grow" (i32.const 0))) 41 | (assert_return (invoke "size") (i32.const 1)) 42 | (assert_return (invoke "grow" (i32.const 4))) 43 | (assert_return (invoke "size") (i32.const 1)) 44 | (assert_return (invoke "grow" (i32.const 1))) 45 | (assert_return (invoke "size") (i32.const 2)) 46 | 47 | (module 48 | (memory 3 8) 49 | (func (export "size") (result i32) (memory.size)) 50 | (func (export "grow") (param $sz i32) (drop (memory.grow (local.get $sz)))) 51 | ) 52 | 53 | (assert_return (invoke "size") (i32.const 3)) 54 | (assert_return (invoke "grow" (i32.const 1))) 55 | (assert_return (invoke "size") (i32.const 4)) 56 | (assert_return (invoke "grow" (i32.const 3))) 57 | (assert_return (invoke "size") (i32.const 7)) 58 | (assert_return (invoke "grow" (i32.const 0))) 59 | (assert_return (invoke "size") (i32.const 7)) 60 | (assert_return (invoke "grow" (i32.const 2))) 61 | (assert_return (invoke "size") (i32.const 7)) 62 | (assert_return (invoke "grow" (i32.const 1))) 63 | (assert_return (invoke "size") (i32.const 8)) 64 | 65 | 66 | ;; Type errors 67 | 68 | (assert_invalid 69 | (module 70 | (memory 1) 71 | (func $type-result-i32-vs-empty 72 | (memory.size) 73 | ) 74 | ) 75 | "type mismatch" 76 | ) 77 | (assert_invalid 78 | (module 79 | (memory 1) 80 | (func $type-result-i32-vs-f32 (result f32) 81 | (memory.size) 82 | ) 83 | ) 84 | "type mismatch" 85 | ) 86 | -------------------------------------------------------------------------------- /test/core/obsolete-keywords.wast: -------------------------------------------------------------------------------- 1 | ;; Renamed in https://github.com/WebAssembly/spec/pull/720 2 | (assert_malformed 3 | (module quote 4 | "(memory 1)" 5 | "(func (drop (current_memory)))" 6 | ) 7 | "unknown operator current_memory" 8 | ) 9 | 10 | (assert_malformed 11 | (module quote 12 | "(memory 1)" 13 | "(func (drop (grow_memory (i32.const 0))))" 14 | ) 15 | "unknown operator grow_memory" 16 | ) 17 | 18 | ;; Renamed in https://github.com/WebAssembly/spec/pull/926 19 | (assert_malformed 20 | (module quote 21 | "(func (local $i i32) (drop (get_local $i)))" 22 | ) 23 | "unknown operator get_local" 24 | ) 25 | 26 | (assert_malformed 27 | (module quote 28 | "(func (local $i i32) (set_local $i (i32.const 0)))" 29 | ) 30 | "unknown operator set_local" 31 | ) 32 | 33 | (assert_malformed 34 | (module quote 35 | "(func (local $i i32) (drop (tee_local $i (i32.const 0))))" 36 | ) 37 | "unknown operator tee_local" 38 | ) 39 | 40 | (assert_malformed 41 | (module quote 42 | "(global $g anyfunc (ref.null func))" 43 | ) 44 | "unknown operator anyfunc" 45 | ) 46 | 47 | (assert_malformed 48 | (module quote 49 | "(global $g i32 (i32.const 0))" 50 | "(func (drop (get_global $g)))" 51 | ) 52 | "unknown operator get_global" 53 | ) 54 | 55 | (assert_malformed 56 | (module quote 57 | "(global $g (mut i32) (i32.const 0))" 58 | "(func (set_global $g (i32.const 0)))" 59 | ) 60 | "unknown operator set_global" 61 | ) 62 | 63 | (assert_malformed 64 | (module quote 65 | "(func (drop (i32.wrap/i64 (i64.const 0))))" 66 | ) 67 | "unknown operator i32.wrap/i64" 68 | ) 69 | 70 | (assert_malformed 71 | (module quote 72 | "(func (drop (i32.trunc_s:sat/f32 (f32.const 0))))" 73 | ) 74 | "unknown operator i32.trunc_s:sat/f32" 75 | ) 76 | 77 | (assert_malformed 78 | (module quote 79 | "(func (drop (f32x4.convert_s/i32x4 (v128.const i64x2 0 0))))" 80 | ) 81 | "unknown operator f32x4.convert_s/i32x4" 82 | ) 83 | -------------------------------------------------------------------------------- /test/core/ref.wast: -------------------------------------------------------------------------------- 1 | ;; Syntax 2 | 3 | (module 4 | (type $t (func)) 5 | 6 | (func 7 | (param 8 | funcref 9 | externref 10 | (ref func) 11 | (ref extern) 12 | (ref 0) 13 | (ref $t) 14 | (ref 0) 15 | (ref $t) 16 | (ref null func) 17 | (ref null extern) 18 | (ref null 0) 19 | (ref null $t) 20 | ) 21 | ) 22 | ) 23 | 24 | 25 | ;; Undefined type index. 26 | 27 | (assert_invalid 28 | (module (type $type-func-param-invalid (func (param (ref 1))))) 29 | "unknown type" 30 | ) 31 | (assert_invalid 32 | (module (type $type-func-result-invalid (func (result (ref 1))))) 33 | "unknown type" 34 | ) 35 | 36 | (assert_invalid 37 | (module (global $global-invalid (ref null 1) (ref.null 1))) 38 | "unknown type" 39 | ) 40 | 41 | (assert_invalid 42 | (module (table $table-invalid 10 (ref null 1))) 43 | "unknown type" 44 | ) 45 | 46 | (assert_invalid 47 | (module (elem $elem-invalid (ref 1))) 48 | "unknown type" 49 | ) 50 | 51 | (assert_invalid 52 | (module (func $func-param-invalid (param (ref 1)))) 53 | "unknown type" 54 | ) 55 | (assert_invalid 56 | (module (func $func-result-invalid (result (ref 1)))) 57 | "unknown type" 58 | ) 59 | (assert_invalid 60 | (module (func $func-local-invalid (local (ref null 1)))) 61 | "unknown type" 62 | ) 63 | 64 | (assert_invalid 65 | (module (func $block-result-invalid (drop (block (result (ref 1)) (unreachable))))) 66 | "unknown type" 67 | ) 68 | (assert_invalid 69 | (module (func $loop-result-invalid (drop (loop (result (ref 1)) (unreachable))))) 70 | "unknown type" 71 | ) 72 | (assert_invalid 73 | (module (func $if-invalid (drop (if (result (ref 1)) (then) (else))))) 74 | "unknown type" 75 | ) 76 | 77 | (assert_invalid 78 | (module (func $select-result-invalid (drop (select (result (ref 1)) (unreachable))))) 79 | "unknown type" 80 | ) 81 | -------------------------------------------------------------------------------- /test/core/ref_as_non_null.wast: -------------------------------------------------------------------------------- 1 | (module 2 | (type $t (func (result i32))) 3 | 4 | (func $nn (param $r (ref $t)) (result i32) 5 | (call_ref $t (ref.as_non_null (local.get $r))) 6 | ) 7 | (func $n (param $r (ref null $t)) (result i32) 8 | (call_ref $t (ref.as_non_null (local.get $r))) 9 | ) 10 | 11 | (elem func $f) 12 | (func $f (result i32) (i32.const 7)) 13 | 14 | (func (export "nullable-null") (result i32) (call $n (ref.null $t))) 15 | (func (export "nonnullable-f") (result i32) (call $nn (ref.func $f))) 16 | (func (export "nullable-f") (result i32) (call $n (ref.func $f))) 17 | 18 | (func (export "unreachable") (result i32) 19 | (unreachable) 20 | (ref.as_non_null) 21 | (call $nn) 22 | ) 23 | ) 24 | 25 | (assert_trap (invoke "unreachable") "unreachable") 26 | 27 | (assert_trap (invoke "nullable-null") "null reference") 28 | (assert_return (invoke "nonnullable-f") (i32.const 7)) 29 | (assert_return (invoke "nullable-f") (i32.const 7)) 30 | 31 | (assert_invalid 32 | (module 33 | (type $t (func (result i32))) 34 | (func $g (param $r (ref $t)) (drop (ref.as_non_null (local.get $r)))) 35 | (func (call $g (ref.null $t))) 36 | ) 37 | "type mismatch" 38 | ) 39 | 40 | 41 | (module 42 | (type $t (func)) 43 | (func (param $r (ref $t)) (drop (ref.as_non_null (local.get $r)))) 44 | (func (param $r (ref func)) (drop (ref.as_non_null (local.get $r)))) 45 | (func (param $r (ref extern)) (drop (ref.as_non_null (local.get $r)))) 46 | ) 47 | -------------------------------------------------------------------------------- /test/core/ref_is_null.wast: -------------------------------------------------------------------------------- 1 | (module 2 | (type $t (func)) 3 | (func $dummy) 4 | 5 | (func $f1 (export "funcref") (param $x funcref) (result i32) 6 | (ref.is_null (local.get $x)) 7 | ) 8 | (func $f2 (export "externref") (param $x externref) (result i32) 9 | (ref.is_null (local.get $x)) 10 | ) 11 | (func $f3 (param $x (ref null $t)) (result i32) 12 | (ref.is_null (local.get $x)) 13 | ) 14 | (func $f3' (export "ref-null") (result i32) 15 | (call $f3 (ref.null $t)) 16 | ) 17 | 18 | (table $t1 2 funcref) 19 | (table $t2 2 externref) 20 | (table $t3 2 (ref null $t)) 21 | (elem (table $t1) (i32.const 1) func $dummy) 22 | (elem (table $t3) (i32.const 1) (ref $t) (ref.func $dummy)) 23 | 24 | (func (export "init") (param $r externref) 25 | (table.set $t2 (i32.const 1) (local.get $r)) 26 | ) 27 | (func (export "deinit") 28 | (table.set $t1 (i32.const 1) (ref.null func)) 29 | (table.set $t2 (i32.const 1) (ref.null extern)) 30 | (table.set $t3 (i32.const 1) (ref.null $t)) 31 | ) 32 | 33 | (func (export "funcref-elem") (param $x i32) (result i32) 34 | (call $f1 (table.get $t1 (local.get $x))) 35 | ) 36 | (func (export "externref-elem") (param $x i32) (result i32) 37 | (call $f2 (table.get $t2 (local.get $x))) 38 | ) 39 | (func (export "ref-elem") (param $x i32) (result i32) 40 | (call $f3 (table.get $t3 (local.get $x))) 41 | ) 42 | ) 43 | 44 | (assert_return (invoke "funcref" (ref.null func)) (i32.const 1)) 45 | (assert_return (invoke "externref" (ref.null extern)) (i32.const 1)) 46 | (assert_return (invoke "ref-null") (i32.const 1)) 47 | 48 | (assert_return (invoke "externref" (ref.extern 1)) (i32.const 0)) 49 | 50 | (invoke "init" (ref.extern 0)) 51 | 52 | (assert_return (invoke "funcref-elem" (i32.const 0)) (i32.const 1)) 53 | (assert_return (invoke "externref-elem" (i32.const 0)) (i32.const 1)) 54 | (assert_return (invoke "ref-elem" (i32.const 0)) (i32.const 1)) 55 | 56 | (assert_return (invoke "funcref-elem" (i32.const 1)) (i32.const 0)) 57 | (assert_return (invoke "externref-elem" (i32.const 1)) (i32.const 0)) 58 | (assert_return (invoke "ref-elem" (i32.const 1)) (i32.const 0)) 59 | 60 | (invoke "deinit") 61 | 62 | (assert_return (invoke "funcref-elem" (i32.const 0)) (i32.const 1)) 63 | (assert_return (invoke "externref-elem" (i32.const 0)) (i32.const 1)) 64 | (assert_return (invoke "ref-elem" (i32.const 0)) (i32.const 1)) 65 | 66 | (assert_return (invoke "funcref-elem" (i32.const 1)) (i32.const 1)) 67 | (assert_return (invoke "externref-elem" (i32.const 1)) (i32.const 1)) 68 | (assert_return (invoke "ref-elem" (i32.const 1)) (i32.const 1)) 69 | 70 | 71 | (module 72 | (type $t (func)) 73 | (func (param $r (ref $t)) (drop (ref.is_null (local.get $r)))) 74 | (func (param $r (ref func)) (drop (ref.is_null (local.get $r)))) 75 | (func (param $r (ref extern)) (drop (ref.is_null (local.get $r)))) 76 | ) 77 | 78 | (assert_invalid 79 | (module (func $ref-vs-num (param i32) (ref.is_null (local.get 0)))) 80 | "type mismatch" 81 | ) 82 | (assert_invalid 83 | (module (func $ref-vs-empty (ref.is_null))) 84 | "type mismatch" 85 | ) 86 | -------------------------------------------------------------------------------- /test/core/ref_null.wast: -------------------------------------------------------------------------------- 1 | (module 2 | (type $t (func)) 3 | (func (export "anyref") (result anyref) (ref.null any)) 4 | (func (export "funcref") (result funcref) (ref.null func)) 5 | (func (export "ref") (result (ref null $t)) (ref.null $t)) 6 | 7 | (global anyref (ref.null any)) 8 | (global funcref (ref.null func)) 9 | (global (ref null $t) (ref.null $t)) 10 | ) 11 | 12 | (assert_return (invoke "anyref") (ref.null any)) 13 | (assert_return (invoke "funcref") (ref.null func)) 14 | (assert_return (invoke "ref") (ref.null)) 15 | 16 | 17 | (module 18 | (type $t (func)) 19 | (global $null nullref (ref.null none)) 20 | (global $nullfunc nullfuncref (ref.null nofunc)) 21 | (global $nullextern nullexternref (ref.null noextern)) 22 | (func (export "anyref") (result anyref) (global.get $null)) 23 | (func (export "nullref") (result nullref) (global.get $null)) 24 | (func (export "funcref") (result funcref) (global.get $nullfunc)) 25 | (func (export "nullfuncref") (result nullfuncref) (global.get $nullfunc)) 26 | (func (export "externref") (result externref) (global.get $nullextern)) 27 | (func (export "nullexternref") (result nullexternref) (global.get $nullextern)) 28 | (func (export "ref") (result (ref null $t)) (global.get $nullfunc)) 29 | 30 | (global anyref (ref.null any)) 31 | (global anyref (ref.null none)) 32 | (global funcref (ref.null func)) 33 | (global funcref (ref.null nofunc)) 34 | (global externref (ref.null extern)) 35 | (global externref (ref.null noextern)) 36 | (global nullref (ref.null none)) 37 | (global nullfuncref (ref.null nofunc)) 38 | (global nullexternref (ref.null noextern)) 39 | (global (ref null $t) (ref.null $t)) 40 | (global (ref null $t) (ref.null nofunc)) 41 | ) 42 | 43 | (assert_return (invoke "anyref") (ref.null any)) 44 | (assert_return (invoke "anyref") (ref.null none)) 45 | (assert_return (invoke "anyref") (ref.null)) 46 | (assert_return (invoke "nullref") (ref.null any)) 47 | (assert_return (invoke "nullref") (ref.null none)) 48 | (assert_return (invoke "nullref") (ref.null)) 49 | (assert_return (invoke "funcref") (ref.null func)) 50 | (assert_return (invoke "funcref") (ref.null nofunc)) 51 | (assert_return (invoke "funcref") (ref.null)) 52 | (assert_return (invoke "nullfuncref") (ref.null func)) 53 | (assert_return (invoke "nullfuncref") (ref.null nofunc)) 54 | (assert_return (invoke "nullfuncref") (ref.null)) 55 | (assert_return (invoke "externref") (ref.null extern)) 56 | (assert_return (invoke "externref") (ref.null noextern)) 57 | (assert_return (invoke "externref") (ref.null)) 58 | (assert_return (invoke "nullexternref") (ref.null extern)) 59 | (assert_return (invoke "nullexternref") (ref.null noextern)) 60 | (assert_return (invoke "nullexternref") (ref.null)) 61 | (assert_return (invoke "ref") (ref.null func)) 62 | (assert_return (invoke "ref") (ref.null nofunc)) 63 | (assert_return (invoke "ref") (ref.null)) 64 | -------------------------------------------------------------------------------- /test/core/simd/meta/README.md: -------------------------------------------------------------------------------- 1 | # Generated SIMD Spec Tests from gen_tests.py 2 | 3 | `gen_tests.py` builds partial SIMD spec tests using templates in `simd_*.py`. 4 | Currently it only support following simd test files generation. 5 | 6 | - 'simd_i8x16_cmp.wast' 7 | - 'simd_i16x8_cmp.wast' 8 | - 'simd_i32x4_cmp.wast' 9 | - 'simd_f32x4_cmp.wast' 10 | - 'simd_f64x2_cmp.wast' 11 | - 'simd_i8x16_arith.wast' 12 | - 'simd_i8x16_arith2.wast' 13 | - 'simd_i16x8_arith.wast' 14 | - 'simd_i16x8_arith2.wast' 15 | - 'simd_i32x4_arith.wast' 16 | - 'simd_i32x4_arith2.wast' 17 | - 'simd_f32x4_arith.wast' 18 | - 'simd_i64x2_arith.wast' 19 | - 'simd_f64x2_arith.wast' 20 | - 'simd_bitwise.wast' 21 | - 'simd_i8x16_sat_arith.wast' 22 | - 'simd_i16x8_sat_arith.wast' 23 | - 'simd_f32x4.wast' 24 | - 'simd_f64x2.wast' 25 | - 'simd_f32x4_rounding.wast' 26 | - 'simd_f64x2_rounding.wast' 27 | - 'simd_f32x4_pmin_pmax.wast' 28 | - 'simd_f64x2_pmin_pmax.wast' 29 | - 'simd_i32x4_dot_i16x8.wast' 30 | - 'simd_load8_lane.wast' 31 | - 'simd_load16_lane.wast' 32 | - 'simd_load32_lane.wast' 33 | - 'simd_load64_lane.wast, 34 | - 'simd_store8_lane.wast' 35 | - 'simd_store16_lane.wast' 36 | - 'simd_store32_lane.wast' 37 | - 'simd_store64_lane.wast, 38 | - 'simd_i16x8_extmul_i8x16.wast' 39 | - 'simd_i32x4_extmul_i16x8.wast' 40 | - 'simd_i64x2_extmul_i32x4.wast' 41 | - 'simd_int_to_int_widen.wast' 42 | - 'simd_i32x4_trunc_sat_f32x4.wast' 43 | - 'simd_i32x4_trunc_sat_f64x2.wast' 44 | - 'simd_i16x8_q15mulr_sat_s.wast', 45 | - 'simd_i16x8_extadd_pairwise_i8x16.wast', 46 | - 'simd_i32x4_extadd_pairwise_i16x8.wast', 47 | 48 | 49 | Usage: 50 | 51 | ``` 52 | $ python gen_tests.py -a 53 | ``` 54 | 55 | This script requires Python 3.6+, more details are documented in `gen_tests.py`. 56 | -------------------------------------------------------------------------------- /test/core/simd/meta/gen_tests.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | """ 4 | This script is used for generating WebAssembly SIMD test cases. 5 | It requires Python 3.6+. 6 | """ 7 | import sys 8 | import argparse 9 | import importlib 10 | 11 | 12 | SUBMODULES = ( 13 | 'simd_i8x16_cmp', 14 | 'simd_i16x8_cmp', 15 | 'simd_i32x4_cmp', 16 | 'simd_i64x2_cmp', 17 | 'simd_f32x4_cmp', 18 | 'simd_f64x2_cmp', 19 | 'simd_i8x16_arith', 20 | 'simd_i16x8_arith', 21 | 'simd_i32x4_arith', 22 | 'simd_f32x4_arith', 23 | 'simd_i64x2_arith', 24 | 'simd_f64x2_arith', 25 | 'simd_sat_arith', 26 | 'simd_bitwise', 27 | 'simd_f32x4', 28 | 'simd_f64x2', 29 | 'simd_int_arith2', 30 | 'simd_f32x4_rounding', 31 | 'simd_f64x2_rounding', 32 | 'simd_f32x4_pmin_pmax', 33 | 'simd_f64x2_pmin_pmax', 34 | 'simd_i32x4_dot_i16x8', 35 | 'simd_load_lane', 36 | 'simd_store_lane', 37 | 'simd_ext_mul', 38 | 'simd_int_to_int_extend', 39 | 'simd_int_trunc_sat_float', 40 | 'simd_i16x8_q15mulr_sat_s', 41 | 'simd_extadd_pairwise', 42 | ) 43 | 44 | 45 | def gen_group_tests(mod_name): 46 | """mod_name is the back-end script name without the.py extension. 47 | There must be a gen_test_cases() function in each module.""" 48 | mod = importlib.import_module(mod_name) 49 | mod.gen_test_cases() 50 | 51 | 52 | def main(): 53 | """ 54 | Default program entry 55 | """ 56 | 57 | parser = argparse.ArgumentParser( 58 | description='Front-end script to call other modules to generate SIMD tests') 59 | parser.add_argument('-a', '--all', dest='gen_all', action='store_true', 60 | default=False, help='Generate all the tests') 61 | parser.add_argument('-i', '--inst', dest='inst_group', choices=SUBMODULES, 62 | help='Back-end scripts that generate the SIMD tests') 63 | args = parser.parse_args() 64 | 65 | if len(sys.argv) < 2: 66 | parser.print_help() 67 | 68 | if args.inst_group: 69 | gen_group_tests(args.inst_group) 70 | if args.gen_all: 71 | for mod_name in SUBMODULES: 72 | gen_group_tests(mod_name) 73 | 74 | 75 | if __name__ == '__main__': 76 | main() 77 | print('Done.') 78 | -------------------------------------------------------------------------------- /test/core/simd/meta/simd.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | """ 4 | This python file is a tool class for SIMD and 5 | currently only supports generating v128 const constant data. 6 | """ 7 | 8 | 9 | class SIMD: 10 | 11 | # Constant template 12 | CONST = '({value_type}.const {value})' 13 | 14 | # v128 Constant template 15 | V128_CONST = '(v128.const {lane_type} {value})' 16 | 17 | @staticmethod 18 | def const(value, value_type): 19 | """ 20 | generation constant data, [e.g. i32, i64, f32, f64] 21 | Params: 22 | value: constant data, string or list, 23 | lane_type: lane type, [i32, i64, f32, f64] 24 | """ 25 | return SIMD.CONST.format(value_type=value_type, value=''.join(str(value))) 26 | 27 | @staticmethod 28 | def v128_const(value, lane_type): 29 | """ 30 | generation v128 constant data, [e.g. i8x16, i16x8, i32x4, f32x4] 31 | Params: 32 | value: constant data, string or list, 33 | lane_type: lane type, [e.g. i8x16, i16x8, i32x4, f32x4] 34 | """ 35 | if lane_type.lower().find('x') == -1: 36 | return SIMD.const(value, lane_type) 37 | 38 | lane_cnt = int(lane_type[1:].split('x')[1]) 39 | 40 | # value is a string type, generating constant data 41 | # of value according to the number of lanes 42 | if isinstance(value, str): 43 | data_elem = [value] * lane_cnt 44 | 45 | # If value is type of list, generate constant data 46 | # according to combination of list contents and number of lanes 47 | elif isinstance(value, list): 48 | 49 | # If it is an empty list, generate all constant data with 0x00 50 | if len(value) == 0: 51 | return SIMD.v128_const('0x00', lane_type) 52 | 53 | data_elem = [] 54 | 55 | # Calculate the number of times each element in value is copied 56 | times = lane_cnt // len(value) 57 | 58 | # Calculate whether the data needs to be filled according to 59 | # the number of elements in the value list and the number of lanes. 60 | complement = lane_cnt % len(value) 61 | complement_item = '' 62 | 63 | # If the number of elements in the value list is greater than the number of lanes, 64 | # paste data with the number of lanes from the value list. 65 | if times == 0: 66 | times = 1 67 | complement = 0 68 | 69 | value = value[0:lane_cnt] 70 | 71 | # Copy data 72 | for item in value: 73 | data_elem.extend([item] * times) 74 | complement_item = item 75 | 76 | # Fill in the data 77 | if complement > 0: 78 | data_elem.extend([complement_item] * complement) 79 | 80 | # Get string 81 | data_elem = ' '.join(data_elem) 82 | 83 | # Returns v128 constant text 84 | return SIMD.V128_CONST.format(lane_type=lane_type, value=data_elem) 85 | -------------------------------------------------------------------------------- /test/core/simd/meta/simd_ext_mul.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | """ Base class for generating extended multiply instructions. These 4 | instructions 2 inputs of the same (narrower) lane shape, multiplies 5 | corresponding lanes with extension (no overflow/wraparound), producing 1 output 6 | of a (wider) shape. These instructions can choose to work on the low or high 7 | halves of the inputs, and perform signed or unsigned multiply. 8 | 9 | Subclasses need to define 3 attributes: 10 | - LANE_TYPE (this is the output shape) 11 | - SRC_LANE_TYPE (this is the input (narrower) shape) 12 | - BINARY_OPS (list of operations) 13 | """ 14 | 15 | from simd_arithmetic import SimdArithmeticCase 16 | 17 | 18 | class SimdExtMulCase(SimdArithmeticCase): 19 | UNARY_OPS = () 20 | 21 | @property 22 | def full_bin_test_data(self): 23 | return [] 24 | 25 | def get_combine_cases(self): 26 | return '' 27 | 28 | @property 29 | def bin_test_data(self): 30 | lane_forms = [self.SRC_LANE_TYPE, self.SRC_LANE_TYPE, self.LANE_TYPE] 31 | return [(self.normal_binary_op_test_data, lane_forms)] 32 | 33 | @property 34 | def hex_binary_op_test_data(self): 35 | return [] 36 | 37 | def gen_test_cases(self): 38 | wast_filename = '../simd_{wide}_extmul_{narrow}.wast'.format( 39 | wide=self.LANE_TYPE, narrow=self.SRC_LANE_TYPE) 40 | with open(wast_filename, 'w') as fp: 41 | fp.write(self.get_all_cases()) 42 | 43 | 44 | class SimdI16x8ExtMulCase(SimdExtMulCase): 45 | LANE_TYPE = 'i16x8' 46 | SRC_LANE_TYPE = 'i8x16' 47 | BINARY_OPS = ('extmul_low_i8x16_s', 'extmul_high_i8x16_s', 48 | 'extmul_low_i8x16_u', 'extmul_high_i8x16_u') 49 | 50 | 51 | class SimdI32x4ExtMulCase(SimdExtMulCase): 52 | LANE_TYPE = 'i32x4' 53 | SRC_LANE_TYPE = 'i16x8' 54 | BINARY_OPS = ('extmul_low_i16x8_s', 'extmul_high_i16x8_s', 55 | 'extmul_low_i16x8_u', 'extmul_high_i16x8_u') 56 | 57 | 58 | class SimdI64x2ExtMulCase(SimdExtMulCase): 59 | LANE_TYPE = 'i64x2' 60 | SRC_LANE_TYPE = 'i32x4' 61 | BINARY_OPS = ('extmul_low_i32x4_s', 'extmul_high_i32x4_s', 62 | 'extmul_low_i32x4_u', 'extmul_high_i32x4_u') 63 | 64 | 65 | def gen_test_cases(): 66 | simd_i16x8_ext_mul_case = SimdI16x8ExtMulCase() 67 | simd_i16x8_ext_mul_case.gen_test_cases() 68 | simd_i32x4_ext_mul_case = SimdI32x4ExtMulCase() 69 | simd_i32x4_ext_mul_case.gen_test_cases() 70 | simd_i64x2_ext_mul_case = SimdI64x2ExtMulCase() 71 | simd_i64x2_ext_mul_case.gen_test_cases() 72 | 73 | 74 | if __name__ == '__main__': 75 | gen_test_cases() 76 | -------------------------------------------------------------------------------- /test/core/simd/meta/simd_extadd_pairwise.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from simd_arithmetic import SimdArithmeticCase, i16 4 | from simd_integer_op import ArithmeticOp 5 | 6 | 7 | class SimdExtAddPairwise(SimdArithmeticCase): 8 | BINARY_OPS = () 9 | 10 | def unary_op(self, x, signed): 11 | # For test data we always splat a single value to the 12 | # entire v128, so doubling the input works. 13 | return ArithmeticOp.get_valid_value(x, self.src_lane, signed=signed) * 2 14 | 15 | @property 16 | def hex_unary_op_test_data(self): 17 | return [] 18 | 19 | @property 20 | def unary_test_data(self): 21 | return [ 22 | (self.normal_unary_op_test_data, [self.SRC_LANE_TYPE,self.LANE_TYPE]), 23 | ] 24 | 25 | def get_case_data(self): 26 | case_data = [] 27 | for op in self.UNARY_OPS: 28 | op_name = self.op_name(op) 29 | case_data.append(['#', op_name]) 30 | for data_group, v128_forms in self.unary_test_data: 31 | for data in data_group: 32 | case_data.append([op_name, [str(data)], 33 | str(self.unary_op(data, op.endswith('s'))), 34 | v128_forms]) 35 | return case_data 36 | 37 | def get_combine_cases(self): 38 | return '' 39 | 40 | def gen_test_cases(self): 41 | wast_filename = '../simd_{}_extadd_pairwise_{}.wast'.format(self.LANE_TYPE, self.SRC_LANE_TYPE) 42 | with open(wast_filename, 'w') as fp: 43 | fp.write(self.get_all_cases()) 44 | 45 | class SimdI16x8ExtAddPairwise(SimdExtAddPairwise): 46 | UNARY_OPS = ('extadd_pairwise_i8x16_s','extadd_pairwise_i8x16_u') 47 | LANE_TYPE = 'i16x8' 48 | SRC_LANE_TYPE = 'i8x16' 49 | 50 | class SimdI32x4ExtAddPairwise(SimdExtAddPairwise): 51 | UNARY_OPS = ('extadd_pairwise_i16x8_s','extadd_pairwise_i16x8_u') 52 | LANE_TYPE = 'i32x4' 53 | SRC_LANE_TYPE = 'i16x8' 54 | 55 | def gen_test_cases(): 56 | simd_i16x8_arith = SimdI16x8ExtAddPairwise() 57 | simd_i32x4_arith = SimdI32x4ExtAddPairwise() 58 | simd_i16x8_arith.gen_test_cases() 59 | simd_i32x4_arith.gen_test_cases() 60 | 61 | if __name__ == '__main__': 62 | gen_test_cases() 63 | -------------------------------------------------------------------------------- /test/core/simd/meta/simd_f64x2_pmin_pmax.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | """ 4 | Generate f64x2 [pmin, pmax] cases. 5 | """ 6 | 7 | from simd_f32x4_pmin_pmax import Simdf32x4PminPmaxCase 8 | from simd_f64x2_arith import Simdf64x2ArithmeticCase 9 | from simd_float_op import FloatingPointSimpleOp 10 | from simd import SIMD 11 | from test_assert import AssertReturn 12 | 13 | 14 | class Simdf64x2PminPmaxCase(Simdf32x4PminPmaxCase): 15 | LANE_TYPE = 'f64x2' 16 | FLOAT_NUMBERS = Simdf64x2ArithmeticCase.FLOAT_NUMBERS 17 | LITERAL_NUMBERS = Simdf64x2ArithmeticCase.LITERAL_NUMBERS 18 | NAN_NUMBERS = Simdf64x2ArithmeticCase.NAN_NUMBERS 19 | 20 | 21 | def gen_test_cases(): 22 | simd_f64x2_pmin_pmax_case = Simdf64x2PminPmaxCase() 23 | simd_f64x2_pmin_pmax_case.gen_test_cases() 24 | 25 | 26 | if __name__ == '__main__': 27 | gen_test_cases() 28 | -------------------------------------------------------------------------------- /test/core/simd/meta/simd_f64x2_rounding.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | """ 4 | Generate f64x2 [ceil, floor, trunc, nearest] cases. 5 | """ 6 | 7 | from simd_f32x4_rounding import Simdf32x4RoundingCase 8 | from simd_f64x2 import Simdf64x2Case 9 | from simd_f64x2_arith import Simdf64x2ArithmeticCase 10 | from simd_float_op import FloatingPointRoundingOp 11 | from simd import SIMD 12 | from test_assert import AssertReturn 13 | 14 | 15 | class Simdf64x2RoundingCase(Simdf32x4RoundingCase): 16 | 17 | LANE_TYPE = 'f64x2' 18 | FLOAT_NUMBERS = Simdf64x2ArithmeticCase.FLOAT_NUMBERS 19 | LITERAL_NUMBERS = Simdf64x2ArithmeticCase.LITERAL_NUMBERS 20 | NAN_NUMBERS = Simdf64x2ArithmeticCase.NAN_NUMBERS 21 | 22 | 23 | def gen_test_cases(): 24 | simd_f64x2_case = Simdf64x2RoundingCase() 25 | simd_f64x2_case.gen_test_cases() 26 | 27 | 28 | if __name__ == '__main__': 29 | gen_test_cases() 30 | -------------------------------------------------------------------------------- /test/core/simd/meta/simd_i16x8_q15mulr_sat_s.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from simd_arithmetic import SimdArithmeticCase 4 | 5 | 6 | """Generate test cases for i16x8.mulr_sat_s 7 | """ 8 | class SimdI16x8Q15MulRSatS(SimdArithmeticCase): 9 | LANE_TYPE = 'i16x8' 10 | UNARY_OPS = () 11 | BINARY_OPS = ('q15mulr_sat_s',) 12 | 13 | @property 14 | def full_bin_test_data(self): 15 | return [] 16 | 17 | @property 18 | def hex_binary_op_test_data(self): 19 | return [] 20 | 21 | def get_combine_cases(self): 22 | return '' 23 | 24 | def gen_test_cases(self): 25 | wast_filename = '../simd_i16x8_q15mulr_sat_s.wast' 26 | with open(wast_filename, 'w') as fp: 27 | fp.write(self.get_all_cases()) 28 | 29 | 30 | def gen_test_cases(): 31 | simd_i16x8_q16mulr_sat_s = SimdI16x8Q15MulRSatS() 32 | simd_i16x8_q16mulr_sat_s.gen_test_cases() 33 | 34 | 35 | if __name__ == '__main__': 36 | gen_test_cases() 37 | -------------------------------------------------------------------------------- /test/core/simd/meta/simd_i32x4_dot_i16x8.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from simd_arithmetic import SimdArithmeticCase, i16 4 | from simd_integer_op import ArithmeticOp 5 | 6 | 7 | class SimdI32x4DotI16x8TestCase(SimdArithmeticCase): 8 | LANE_TYPE = 'i32x4' 9 | UNARY_OPS = () 10 | BINARY_OPS = ('dot_i16x8_s',) 11 | 12 | @property 13 | def lane(self): 14 | return i16 15 | 16 | def binary_op(self, x, y, lane): 17 | # For test data we always splat a single value to the 18 | # entire v128, so '* 2' will work here. 19 | return ArithmeticOp.get_valid_value(x, i16) * ArithmeticOp.get_valid_value(y, i16) * 2 20 | 21 | @property 22 | def hex_binary_op_test_data(self): 23 | return [] 24 | 25 | @property 26 | def bin_test_data(self): 27 | return [ 28 | (self.normal_binary_op_test_data, ['i16x8', 'i16x8', 'i32x4']), 29 | (self.hex_binary_op_test_data, ['i16x8', 'i16x8', 'i32x4']) 30 | ] 31 | 32 | def get_case_data(self): 33 | case_data = [] 34 | op_name = 'i32x4.dot_i16x8_s' 35 | case_data.append(['#', op_name]) 36 | for data_group, v128_forms in self.bin_test_data: 37 | for data in data_group: 38 | case_data.append([op_name, [str(data[0]), str(data[1])], 39 | str(self.binary_op(data[0], data[1], self.lane)), 40 | v128_forms]) 41 | return case_data 42 | 43 | def get_combine_cases(self): 44 | return '' 45 | 46 | def gen_test_cases(self): 47 | wast_filename = '../simd_i32x4_dot_i16x8.wast' 48 | with open(wast_filename, 'w') as fp: 49 | fp.write(self.get_all_cases()) 50 | 51 | def gen_test_cases(): 52 | simd_i16x8_arith = SimdI32x4DotI16x8TestCase() 53 | simd_i16x8_arith.gen_test_cases() 54 | 55 | if __name__ == '__main__': 56 | gen_test_cases() 57 | -------------------------------------------------------------------------------- /test/core/simd/meta/simd_lane_value.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | 4 | class LaneValue: 5 | """This class stands for the value of signed integer represented by a lane in v128. 6 | Suppose a bit number of the lane is n, then: 7 | For signed integer: 8 | minimum = -pow(2, n - 1), maximum = pow(2, n - 1) - 1 9 | The bit number of the lane can be 8, 16, 32, 64""" 10 | def __init__(self, lane_width): 11 | """lane_width: bit number of each lane in SIMD v128""" 12 | self.lane_width = lane_width 13 | 14 | @property 15 | def min(self): 16 | return -pow(2, self.lane_width - 1) 17 | 18 | @property 19 | def max(self): 20 | return pow(2, self.lane_width - 1) - 1 21 | 22 | @property 23 | def mask(self): 24 | return pow(2, self.lane_width) - 1 25 | 26 | @property 27 | def mod(self): 28 | return pow(2, self.lane_width) 29 | 30 | @property 31 | def quarter(self): 32 | return pow(2, self.lane_width - 2) 33 | 34 | def sat_s(self, v): 35 | return max(self.min, min(v, self.max)) 36 | 37 | def sat_u(self, v): 38 | return max(0, min(v, self.mask)) 39 | -------------------------------------------------------------------------------- /test/core/simd/simd_linking.wast: -------------------------------------------------------------------------------- 1 | (module 2 | (global (export "g-v128") v128 (v128.const i64x2 0 0)) 3 | (global (export "mg-v128") (mut v128) (v128.const i64x2 0 0)) 4 | ) 5 | (register "Mv128") 6 | 7 | (module 8 | ;; TODO: Reactivate once the fix for https://bugs.chromium.org/p/v8/issues/detail?id=13732 9 | ;; has made it to the downstream node.js that we use on CI. 10 | ;; (import "Mv128" "g-v128" (global v128)) 11 | (import "Mv128" "mg-v128" (global (mut v128))) 12 | ) 13 | -------------------------------------------------------------------------------- /test/core/start.wast: -------------------------------------------------------------------------------- 1 | (assert_invalid 2 | (module (func) (start 1)) 3 | "unknown function" 4 | ) 5 | 6 | (assert_invalid 7 | (module 8 | (func $main (result i32) (return (i32.const 0))) 9 | (start $main) 10 | ) 11 | "start function" 12 | ) 13 | (assert_invalid 14 | (module 15 | (func $main (param $a i32)) 16 | (start $main) 17 | ) 18 | "start function" 19 | ) 20 | 21 | (module 22 | (memory (data "A")) 23 | (func $inc 24 | (i32.store8 25 | (i32.const 0) 26 | (i32.add 27 | (i32.load8_u (i32.const 0)) 28 | (i32.const 1) 29 | ) 30 | ) 31 | ) 32 | (func $get (result i32) 33 | (return (i32.load8_u (i32.const 0))) 34 | ) 35 | (func $main 36 | (call $inc) 37 | (call $inc) 38 | (call $inc) 39 | ) 40 | 41 | (start $main) 42 | (export "inc" (func $inc)) 43 | (export "get" (func $get)) 44 | ) 45 | (assert_return (invoke "get") (i32.const 68)) 46 | (invoke "inc") 47 | (assert_return (invoke "get") (i32.const 69)) 48 | (invoke "inc") 49 | (assert_return (invoke "get") (i32.const 70)) 50 | 51 | (module 52 | (memory (data "A")) 53 | (func $inc 54 | (i32.store8 55 | (i32.const 0) 56 | (i32.add 57 | (i32.load8_u (i32.const 0)) 58 | (i32.const 1) 59 | ) 60 | ) 61 | ) 62 | (func $get (result i32) 63 | (return (i32.load8_u (i32.const 0))) 64 | ) 65 | (func $main 66 | (call $inc) 67 | (call $inc) 68 | (call $inc) 69 | ) 70 | (start 2) 71 | (export "inc" (func $inc)) 72 | (export "get" (func $get)) 73 | ) 74 | (assert_return (invoke "get") (i32.const 68)) 75 | (invoke "inc") 76 | (assert_return (invoke "get") (i32.const 69)) 77 | (invoke "inc") 78 | (assert_return (invoke "get") (i32.const 70)) 79 | 80 | (module 81 | (func $print_i32 (import "spectest" "print_i32") (param i32)) 82 | (func $main (call $print_i32 (i32.const 1))) 83 | (start 1) 84 | ) 85 | 86 | (module 87 | (func $print_i32 (import "spectest" "print_i32") (param i32)) 88 | (func $main (call $print_i32 (i32.const 2))) 89 | (start $main) 90 | ) 91 | 92 | (module 93 | (func $print (import "spectest" "print")) 94 | (start $print) 95 | ) 96 | 97 | (assert_trap 98 | (module (func $main (unreachable)) (start $main)) 99 | "unreachable" 100 | ) 101 | 102 | (assert_malformed 103 | (module quote "(module (func $a (unreachable)) (func $b (unreachable)) (start $a) (start $b))") 104 | "multiple start sections" 105 | ) 106 | -------------------------------------------------------------------------------- /test/core/table-sub.wast: -------------------------------------------------------------------------------- 1 | (module 2 | (type $t (func)) 3 | (table $t1 10 (ref null func)) 4 | (table $t2 10 (ref null $t)) 5 | (elem $el funcref) 6 | (func $f 7 | (table.init $t1 $el (i32.const 0) (i32.const 1) (i32.const 2)) 8 | (table.copy $t1 $t2 (i32.const 0) (i32.const 1) (i32.const 2)) 9 | ) 10 | ) 11 | 12 | (assert_invalid 13 | (module 14 | (table $t1 10 funcref) 15 | (table $t2 10 externref) 16 | (func $f 17 | (table.copy $t1 $t2 (i32.const 0) (i32.const 1) (i32.const 2)) 18 | ) 19 | ) 20 | "type mismatch" 21 | ) 22 | 23 | (assert_invalid 24 | (module 25 | (table $t 10 funcref) 26 | (elem $el externref) 27 | (func $f 28 | (table.init $t $el (i32.const 0) (i32.const 1) (i32.const 2)) 29 | ) 30 | ) 31 | "type mismatch" 32 | ) 33 | -------------------------------------------------------------------------------- /test/core/table_get.wast: -------------------------------------------------------------------------------- 1 | (module 2 | (table $t2 2 externref) 3 | (table $t3 3 funcref) 4 | (elem (table $t3) (i32.const 1) func $dummy) 5 | (func $dummy) 6 | 7 | (func (export "init") (param $r externref) 8 | (table.set $t2 (i32.const 1) (local.get $r)) 9 | (table.set $t3 (i32.const 2) (table.get $t3 (i32.const 1))) 10 | ) 11 | 12 | (func (export "get-externref") (param $i i32) (result externref) 13 | (table.get (local.get $i)) 14 | ) 15 | (func $f3 (export "get-funcref") (param $i i32) (result funcref) 16 | (table.get $t3 (local.get $i)) 17 | ) 18 | 19 | (func (export "is_null-funcref") (param $i i32) (result i32) 20 | (ref.is_null (call $f3 (local.get $i))) 21 | ) 22 | ) 23 | 24 | (invoke "init" (ref.extern 1)) 25 | 26 | (assert_return (invoke "get-externref" (i32.const 0)) (ref.null extern)) 27 | (assert_return (invoke "get-externref" (i32.const 1)) (ref.extern 1)) 28 | 29 | (assert_return (invoke "get-funcref" (i32.const 0)) (ref.null func)) 30 | (assert_return (invoke "is_null-funcref" (i32.const 1)) (i32.const 0)) 31 | (assert_return (invoke "is_null-funcref" (i32.const 2)) (i32.const 0)) 32 | 33 | (assert_trap (invoke "get-externref" (i32.const 2)) "out of bounds table access") 34 | (assert_trap (invoke "get-funcref" (i32.const 3)) "out of bounds table access") 35 | (assert_trap (invoke "get-externref" (i32.const -1)) "out of bounds table access") 36 | (assert_trap (invoke "get-funcref" (i32.const -1)) "out of bounds table access") 37 | 38 | 39 | ;; Type errors 40 | 41 | (assert_invalid 42 | (module 43 | (table $t 10 externref) 44 | (func $type-index-empty-vs-i32 (result externref) 45 | (table.get $t) 46 | ) 47 | ) 48 | "type mismatch" 49 | ) 50 | (assert_invalid 51 | (module 52 | (table $t 10 externref) 53 | (func $type-index-f32-vs-i32 (result externref) 54 | (table.get $t (f32.const 1)) 55 | ) 56 | ) 57 | "type mismatch" 58 | ) 59 | 60 | (assert_invalid 61 | (module 62 | (table $t 10 externref) 63 | (func $type-result-externref-vs-empty 64 | (table.get $t (i32.const 0)) 65 | ) 66 | ) 67 | "type mismatch" 68 | ) 69 | (assert_invalid 70 | (module 71 | (table $t 10 externref) 72 | (func $type-result-externref-vs-funcref (result funcref) 73 | (table.get $t (i32.const 1)) 74 | ) 75 | ) 76 | "type mismatch" 77 | ) 78 | 79 | (assert_invalid 80 | (module 81 | (table $t1 1 funcref) 82 | (table $t2 1 externref) 83 | (func $type-result-externref-vs-funcref-multi (result funcref) 84 | (table.get $t2 (i32.const 0)) 85 | ) 86 | ) 87 | "type mismatch" 88 | ) 89 | -------------------------------------------------------------------------------- /test/core/type-canon.wast: -------------------------------------------------------------------------------- 1 | (module 2 | (rec 3 | (type $t1 (func (param i32 (ref $t3)))) 4 | (type $t2 (func (param i32 (ref $t1)))) 5 | (type $t3 (func (param i32 (ref $t2)))) 6 | ) 7 | ) 8 | 9 | (module 10 | (rec 11 | (type $t0 (func (param i32 (ref $t2) (ref $t3)))) 12 | (type $t1 (func (param i32 (ref $t0) i32 (ref $t4)))) 13 | (type $t2 (func (param i32 (ref $t2) (ref $t1)))) 14 | (type $t3 (func (param i32 (ref $t2) i32 (ref $t4)))) 15 | (type $t4 (func (param (ref $t0) (ref $t2)))) 16 | ) 17 | ) 18 | -------------------------------------------------------------------------------- /test/core/type.wast: -------------------------------------------------------------------------------- 1 | ;; Test type definitions 2 | 3 | (module 4 | (type (func)) 5 | (type $t (func)) 6 | 7 | (type (func (param i32))) 8 | (type (func (param $x i32))) 9 | (type (func (result i32))) 10 | (type (func (param i32) (result i32))) 11 | (type (func (param $x i32) (result i32))) 12 | 13 | (type (func (param f32 f64))) 14 | (type (func (result i64 f32))) 15 | (type (func (param i32 i64) (result f32 f64))) 16 | 17 | (type (func (param f32) (param f64))) 18 | (type (func (param $x f32) (param f64))) 19 | (type (func (param f32) (param $y f64))) 20 | (type (func (param $x f32) (param $y f64))) 21 | (type (func (result i64) (result f32))) 22 | (type (func (param i32) (param i64) (result f32) (result f64))) 23 | (type (func (param $x i32) (param $y i64) (result f32) (result f64))) 24 | 25 | (type (func (param f32 f64) (param $x i32) (param f64 i32 i32))) 26 | (type (func (result i64 i64 f32) (result f32 i32))) 27 | (type 28 | (func (param i32 i32) (param i64 i32) (result f32 f64) (result f64 i32)) 29 | ) 30 | 31 | (type (func (param) (param $x f32) (param) (param) (param f64 i32) (param))) 32 | (type 33 | (func (result) (result) (result i64 i64) (result) (result f32) (result)) 34 | ) 35 | (type 36 | (func 37 | (param i32 i32) (param i64 i32) (param) (param $x i32) (param) 38 | (result) (result f32 f64) (result f64 i32) (result) 39 | ) 40 | ) 41 | ) 42 | 43 | (assert_malformed 44 | (module quote "(type (func (result i32) (param i32)))") 45 | "unexpected token" 46 | ) 47 | (assert_malformed 48 | (module quote "(type (func (result $x i32)))") 49 | "unexpected token" 50 | ) 51 | -------------------------------------------------------------------------------- /test/core/unreached-valid.wast: -------------------------------------------------------------------------------- 1 | (module 2 | 3 | ;; Check that both sides of the select are evaluated 4 | (func (export "select-trap-left") (param $cond i32) (result i32) 5 | (select (unreachable) (i32.const 0) (local.get $cond)) 6 | ) 7 | (func (export "select-trap-right") (param $cond i32) (result i32) 8 | (select (i32.const 0) (unreachable) (local.get $cond)) 9 | ) 10 | 11 | (func (export "select-unreached") 12 | (unreachable) (select) 13 | (unreachable) (i32.const 0) (select) 14 | (unreachable) (i32.const 0) (i32.const 0) (select) 15 | (unreachable) (i32.const 0) (i32.const 0) (i32.const 0) (select) 16 | (unreachable) (f32.const 0) (i32.const 0) (select) 17 | (unreachable) 18 | ) 19 | 20 | (func (export "select-unreached-result1") (result i32) 21 | (unreachable) (i32.add (select)) 22 | ) 23 | 24 | (func (export "select-unreached-result2") (result i64) 25 | (unreachable) (i64.add (select (i64.const 0) (i32.const 0))) 26 | ) 27 | 28 | (func (export "select-unreached-num") 29 | (unreachable) 30 | (select) 31 | (i32.eqz) 32 | (drop) 33 | ) 34 | (func (export "select-unreached-ref") 35 | (unreachable) 36 | (select) 37 | (ref.is_null) 38 | (drop) 39 | ) 40 | 41 | (type $t (func (param i32) (result i32))) 42 | (func (export "call_ref-unreached") (result i32) 43 | (unreachable) 44 | (call_ref $t) 45 | ) 46 | ) 47 | 48 | (assert_trap (invoke "select-trap-left" (i32.const 1)) "unreachable") 49 | (assert_trap (invoke "select-trap-left" (i32.const 0)) "unreachable") 50 | (assert_trap (invoke "select-trap-right" (i32.const 1)) "unreachable") 51 | (assert_trap (invoke "select-trap-right" (i32.const 0)) "unreachable") 52 | 53 | (assert_trap (invoke "select-unreached-result1") "unreachable") 54 | (assert_trap (invoke "select-unreached-result2") "unreachable") 55 | (assert_trap (invoke "select-unreached-num") "unreachable") 56 | (assert_trap (invoke "select-unreached-ref") "unreachable") 57 | 58 | (assert_trap (invoke "call_ref-unreached") "unreachable") 59 | 60 | 61 | ;; Validation after unreachable 62 | 63 | (module 64 | (func (export "meet-bottom") 65 | (block (result f64) 66 | (block (result f32) 67 | (unreachable) 68 | (br_table 0 1 1 (i32.const 1)) 69 | ) 70 | (drop) 71 | (f64.const 0) 72 | ) 73 | (drop) 74 | ) 75 | ) 76 | 77 | (assert_trap (invoke "meet-bottom") "unreachable") 78 | 79 | 80 | ;; Bottom heap type 81 | 82 | (module 83 | (func (result (ref func)) 84 | (unreachable) 85 | (ref.as_non_null) 86 | ) 87 | (func (result (ref extern)) 88 | (unreachable) 89 | (ref.as_non_null) 90 | ) 91 | 92 | (func (result (ref func)) 93 | (block (result funcref) 94 | (unreachable) 95 | (br_on_null 0) 96 | (return) 97 | ) 98 | (unreachable) 99 | ) 100 | (func (result (ref extern)) 101 | (block (result externref) 102 | (unreachable) 103 | (br_on_null 0) 104 | (return) 105 | ) 106 | (unreachable) 107 | ) 108 | ) 109 | -------------------------------------------------------------------------------- /test/harness/testharness.css: -------------------------------------------------------------------------------- 1 | html { 2 | font-family:DejaVu Sans, Bitstream Vera Sans, Arial, Sans; 3 | } 4 | 5 | #log .warning, 6 | #log .warning a { 7 | color: black; 8 | background: yellow; 9 | } 10 | 11 | #log .error, 12 | #log .error a { 13 | color: white; 14 | background: red; 15 | } 16 | 17 | section#summary { 18 | margin-bottom:1em; 19 | } 20 | 21 | table#results { 22 | border-collapse:collapse; 23 | table-layout:fixed; 24 | width:100%; 25 | } 26 | 27 | table#results th:first-child, 28 | table#results td:first-child { 29 | width:4em; 30 | } 31 | 32 | table#results th:last-child, 33 | table#results td:last-child { 34 | width:50%; 35 | } 36 | 37 | table#results.assertions th:last-child, 38 | table#results.assertions td:last-child { 39 | width:35%; 40 | } 41 | 42 | table#results th { 43 | padding:0; 44 | padding-bottom:0.5em; 45 | border-bottom:medium solid black; 46 | } 47 | 48 | table#results td { 49 | padding:1em; 50 | padding-bottom:0.5em; 51 | border-bottom:thin solid black; 52 | } 53 | 54 | tr.pass > td:first-child { 55 | color:green; 56 | } 57 | 58 | tr.fail > td:first-child { 59 | color:red; 60 | } 61 | 62 | tr.timeout > td:first-child { 63 | color:red; 64 | } 65 | 66 | tr.notrun > td:first-child { 67 | color:blue; 68 | } 69 | 70 | .pass > td:first-child, .fail > td:first-child, .timeout > td:first-child, .notrun > td:first-child { 71 | font-variant:small-caps; 72 | } 73 | 74 | table#results span { 75 | display:block; 76 | } 77 | 78 | table#results span.expected { 79 | font-family:DejaVu Sans Mono, Bitstream Vera Sans Mono, Monospace; 80 | white-space:pre; 81 | } 82 | 83 | table#results span.actual { 84 | font-family:DejaVu Sans Mono, Bitstream Vera Sans Mono, Monospace; 85 | white-space:pre; 86 | } 87 | 88 | span.ok { 89 | color:green; 90 | } 91 | 92 | tr.error { 93 | color:red; 94 | } 95 | 96 | span.timeout { 97 | color:red; 98 | } 99 | 100 | span.ok, span.timeout, span.error { 101 | font-variant:small-caps; 102 | } -------------------------------------------------------------------------------- /test/harness/testharnessreport.js: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | var props = {output: true, 6 | explicit_timeout: true, 7 | message_events: ["completion"]}; 8 | 9 | if (window.opener && "timeout_multiplier" in window.opener) { 10 | props["timeout_multiplier"] = window.opener.timeout_multiplier; 11 | } 12 | 13 | if (window.opener && window.opener.explicit_timeout) { 14 | props["explicit_timeout"] = window.opener.explicit_timeout; 15 | } 16 | 17 | setup(props); 18 | -------------------------------------------------------------------------------- /test/js-api/README.md: -------------------------------------------------------------------------------- 1 | This directory contains tests specific to the [JavaScript API] to WebAssembly. 2 | 3 | These tests exist in the [web-platform-tests project], and are included here 4 | primarily to simplify sharing them between JavaScript engine implementers. 5 | 6 | These tests can be run in a pure JavaScript environment, that is, a JS shell 7 | (like V8 or spidermonkey's shells), as well as in the browser. 8 | 9 | The tests use the [testharness.js] library and the [multi-global tests] setup 10 | for smooth integrations with the web-platform-tests project and the existing 11 | test infrastructure in implementations. 12 | 13 | ## Metadata 14 | 15 | All tests must have the `.any.js` extension. 16 | 17 | In order to be run in the JavaScript shell, a metadata comment to add the 18 | `jsshell` scope to the default set of scopes (`window` and `dedicatedworker`) 19 | is required at the start of the file: 20 | 21 | ```js 22 | // META: global=jsshell 23 | ``` 24 | 25 | Additional JavaScript files can be imported with 26 | 27 | ```js 28 | // META: script=helper-file.js 29 | 30 | ``` 31 | 32 | ## Harness 33 | 34 | A single test file contains multiple subtests, which are created with one of 35 | the following functions: 36 | 37 | - For synchronous tests: `test(function, name)` runs the function immediately. 38 | The test fails if any assertion fails or an exception is thrown while calling 39 | the function. 40 | - For asynchronous tests: `promise_test(function, name)` where `function` 41 | returns a `Promise`. The test fails if the returned `Promise` rejects. 42 | 43 | All assertions must be in one of those subtests. 44 | 45 | A number of assertion functions are provided, e.g.: 46 | 47 | - `assert_equals(x, y)`; 48 | - `assert_not_equals(x, y)`; 49 | - `assert_true(x)`; 50 | - `assert_false(x)`; 51 | - `assert_unreached()`; 52 | - `assert_throws(error, function)`: checks if `function` throws an appropriate 53 | exception (a typical value for `error` would be `new TypeError()`); 54 | - `assert_class_string(object, class_name)`: checks if the result of calling 55 | `Object.prototype.toString` on `object` uses `class_name`; 56 | - `promise_rejects`: `assert_throws`, but for `Promise`s. 57 | 58 | All the above functions also take an optional trailing description argument. 59 | 60 | Non-trivial code that runs before the subtests should be put in the function 61 | argument to `setup(function)`, to ensure any exceptions are handled gracefully. 62 | 63 | Finally, the harness also exposes a `format_value` function that provides a 64 | helpful stringification of its argument. 65 | 66 | See the [testharness.js] documentation for more information. 67 | 68 | [JavaScript API]: https://webassembly.github.io/spec/js-api/ 69 | [web-platform-tests project]: https://github.com/web-platform-tests/wpt/tree/master/wasm/jsapi 70 | [testharness.js]: https://web-platform-tests.org/writing-tests/testharness-api.html 71 | [multi-global tests]: https://web-platform-tests.org/writing-tests/testharness.html#multi-global-tests 72 | -------------------------------------------------------------------------------- /test/js-api/constructor/compile.any.js: -------------------------------------------------------------------------------- 1 | // META: global=window,dedicatedworker,jsshell 2 | // META: script=/wasm/jsapi/wasm-module-builder.js 3 | 4 | function assert_Module(module) { 5 | assert_equals(Object.getPrototypeOf(module), WebAssembly.Module.prototype, 6 | "Prototype"); 7 | assert_true(Object.isExtensible(module), "Extensibility"); 8 | } 9 | 10 | let emptyModuleBinary; 11 | setup(() => { 12 | emptyModuleBinary = new WasmModuleBuilder().toBuffer(); 13 | }); 14 | 15 | promise_test(t => { 16 | return promise_rejects_js(t, TypeError, WebAssembly.compile()); 17 | }, "Missing argument"); 18 | 19 | promise_test(t => { 20 | const invalidArguments = [ 21 | undefined, 22 | null, 23 | true, 24 | "", 25 | Symbol(), 26 | 1, 27 | {}, 28 | ArrayBuffer, 29 | ArrayBuffer.prototype, 30 | Array.from(emptyModuleBinary), 31 | ]; 32 | return Promise.all(invalidArguments.map(argument => { 33 | return promise_rejects_js(t, TypeError, WebAssembly.compile(argument), 34 | `compile(${format_value(argument)})`); 35 | })); 36 | }, "Invalid arguments"); 37 | 38 | promise_test(() => { 39 | const fn = WebAssembly.compile; 40 | const thisValues = [ 41 | undefined, 42 | null, 43 | true, 44 | "", 45 | Symbol(), 46 | 1, 47 | {}, 48 | WebAssembly, 49 | ]; 50 | return Promise.all(thisValues.map(thisValue => { 51 | return fn.call(thisValue, emptyModuleBinary).then(assert_Module); 52 | })); 53 | }, "Branding"); 54 | 55 | test(() => { 56 | const promise = WebAssembly.compile(emptyModuleBinary); 57 | assert_equals(Object.getPrototypeOf(promise), Promise.prototype, "prototype"); 58 | assert_true(Object.isExtensible(promise), "extensibility"); 59 | }, "Promise type"); 60 | 61 | promise_test(t => { 62 | const buffer = new Uint8Array(); 63 | return promise_rejects_js(t, WebAssembly.CompileError, WebAssembly.compile(buffer)); 64 | }, "Empty buffer"); 65 | 66 | promise_test(t => { 67 | const buffer = new Uint8Array(Array.from(emptyModuleBinary).concat([0, 0])); 68 | return promise_rejects_js(t, WebAssembly.CompileError, WebAssembly.compile(buffer)); 69 | }, "Invalid code"); 70 | 71 | promise_test(() => { 72 | return WebAssembly.compile(emptyModuleBinary).then(assert_Module); 73 | }, "Result type"); 74 | 75 | promise_test(() => { 76 | return WebAssembly.compile(emptyModuleBinary, {}).then(assert_Module); 77 | }, "Stray argument"); 78 | 79 | promise_test(() => { 80 | const buffer = new WasmModuleBuilder().toBuffer(); 81 | assert_equals(buffer[0], 0); 82 | const promise = WebAssembly.compile(buffer); 83 | buffer[0] = 1; 84 | return promise.then(assert_Module); 85 | }, "Changing the buffer"); 86 | -------------------------------------------------------------------------------- /test/js-api/constructor/instantiate-bad-imports.any.js: -------------------------------------------------------------------------------- 1 | // META: global=window,dedicatedworker,jsshell 2 | // META: script=/wasm/jsapi/wasm-module-builder.js 3 | // META: script=/wasm/jsapi/bad-imports.js 4 | 5 | test_bad_imports((name, error, build, ...arguments) => { 6 | promise_test(t => { 7 | const builder = new WasmModuleBuilder(); 8 | build(builder); 9 | const buffer = builder.toBuffer(); 10 | const module = new WebAssembly.Module(buffer); 11 | return promise_rejects_js(t, error, WebAssembly.instantiate(module, ...arguments)); 12 | }, `WebAssembly.instantiate(module): ${name}`); 13 | }); 14 | 15 | test_bad_imports((name, error, build, ...arguments) => { 16 | promise_test(t => { 17 | const builder = new WasmModuleBuilder(); 18 | build(builder); 19 | const buffer = builder.toBuffer(); 20 | return promise_rejects_js(t, error, WebAssembly.instantiate(buffer, ...arguments)); 21 | }, `WebAssembly.instantiate(buffer): ${name}`); 22 | }); 23 | -------------------------------------------------------------------------------- /test/js-api/constructor/toStringTag.any.js: -------------------------------------------------------------------------------- 1 | // META: global=window,dedicatedworker,jsshell 2 | 3 | "use strict"; 4 | // https://webidl.spec.whatwg.org/#es-namespaces 5 | // https://webassembly.github.io/spec/js-api/#namespacedef-webassembly 6 | 7 | test(() => { 8 | assert_own_property(WebAssembly, Symbol.toStringTag); 9 | 10 | const propDesc = Object.getOwnPropertyDescriptor(WebAssembly, Symbol.toStringTag); 11 | assert_equals(propDesc.value, "WebAssembly", "value"); 12 | assert_equals(propDesc.writable, false, "writable"); 13 | assert_equals(propDesc.enumerable, false, "enumerable"); 14 | assert_equals(propDesc.configurable, true, "configurable"); 15 | }, "@@toStringTag exists on the namespace object with the appropriate descriptor"); 16 | 17 | test(() => { 18 | assert_equals(WebAssembly.toString(), "[object WebAssembly]"); 19 | assert_equals(Object.prototype.toString.call(WebAssembly), "[object WebAssembly]"); 20 | }, "Object.prototype.toString applied to the namespace object"); 21 | 22 | test(t => { 23 | assert_own_property(WebAssembly, Symbol.toStringTag, "Precondition: @@toStringTag on the namespace object"); 24 | t.add_cleanup(() => { 25 | Object.defineProperty(WebAssembly, Symbol.toStringTag, { value: "WebAssembly" }); 26 | }); 27 | 28 | Object.defineProperty(WebAssembly, Symbol.toStringTag, { value: "Test" }); 29 | assert_equals(WebAssembly.toString(), "[object Test]"); 30 | assert_equals(Object.prototype.toString.call(WebAssembly), "[object Test]"); 31 | }, "Object.prototype.toString applied after modifying the namespace object's @@toStringTag"); 32 | 33 | test(t => { 34 | assert_own_property(WebAssembly, Symbol.toStringTag, "Precondition: @@toStringTag on the namespace object"); 35 | t.add_cleanup(() => { 36 | Object.defineProperty(WebAssembly, Symbol.toStringTag, { value: "WebAssembly" }); 37 | }); 38 | 39 | assert_true(delete WebAssembly[Symbol.toStringTag]); 40 | assert_equals(WebAssembly.toString(), "[object Object]"); 41 | assert_equals(Object.prototype.toString.call(WebAssembly), "[object Object]"); 42 | }, "Object.prototype.toString applied after deleting @@toStringTag"); 43 | -------------------------------------------------------------------------------- /test/js-api/constructor/validate.any.js: -------------------------------------------------------------------------------- 1 | // META: global=window,dedicatedworker,jsshell 2 | // META: script=/wasm/jsapi/wasm-module-builder.js 3 | 4 | let emptyModuleBinary; 5 | setup(() => { 6 | emptyModuleBinary = new WasmModuleBuilder().toBuffer(); 7 | }); 8 | 9 | test(() => { 10 | assert_throws_js(TypeError, () => WebAssembly.validate()); 11 | }, "Missing argument"); 12 | 13 | test(() => { 14 | const invalidArguments = [ 15 | undefined, 16 | null, 17 | true, 18 | "", 19 | Symbol(), 20 | 1, 21 | {}, 22 | ArrayBuffer, 23 | ArrayBuffer.prototype, 24 | Array.from(emptyModuleBinary), 25 | ]; 26 | for (const argument of invalidArguments) { 27 | assert_throws_js(TypeError, () => WebAssembly.validate(argument), 28 | `validate(${format_value(argument)})`); 29 | } 30 | }, "Invalid arguments"); 31 | 32 | test(() => { 33 | const fn = WebAssembly.validate; 34 | const thisValues = [ 35 | undefined, 36 | null, 37 | true, 38 | "", 39 | Symbol(), 40 | 1, 41 | {}, 42 | WebAssembly, 43 | ]; 44 | for (const thisValue of thisValues) { 45 | assert_true(fn.call(thisValue, emptyModuleBinary), `this=${format_value(thisValue)}`); 46 | } 47 | }, "Branding"); 48 | 49 | const modules = [ 50 | // Incomplete header. 51 | [[], false], 52 | [[0x00], false], 53 | [[0x00, 0x61], false], 54 | [[0x00, 0x61, 0x73], false], 55 | [[0x00, 0x61, 0x73, 0x6d], false], 56 | [[0x00, 0x61, 0x73, 0x6d, 0x01], false], 57 | [[0x00, 0x61, 0x73, 0x6d, 0x01, 0x00], false], 58 | [[0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00], false], 59 | 60 | // Complete header. 61 | [[0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00], true], 62 | 63 | // Invalid version. 64 | [[0x00, 0x61, 0x73, 0x6d, 0x00, 0x00, 0x00, 0x00], false], 65 | [[0x00, 0x61, 0x73, 0x6d, 0x02, 0x00, 0x00, 0x00], false], 66 | 67 | // Nameless custom section. 68 | [[0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00], false], 69 | 70 | // Custom section with empty name. 71 | [[0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00], true], 72 | 73 | // Custom section with name "a". 74 | [[0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x01, 0x61], true], 75 | ]; 76 | const bufferTypes = [ 77 | Uint8Array, 78 | Int8Array, 79 | Uint16Array, 80 | Int16Array, 81 | Uint32Array, 82 | Int32Array, 83 | ]; 84 | for (const [module, expected] of modules) { 85 | const name = module.map(n => n.toString(16)).join(" "); 86 | for (const bufferType of bufferTypes) { 87 | if (module.length % bufferType.BYTES_PER_ELEMENT === 0) { 88 | test(() => { 89 | const bytes = new Uint8Array(module); 90 | const moduleBuffer = new bufferType(bytes.buffer); 91 | assert_equals(WebAssembly.validate(moduleBuffer), expected); 92 | }, `Validating module [${name}] in ${bufferType.name}`); 93 | } 94 | } 95 | } 96 | 97 | test(() => { 98 | assert_true(WebAssembly.validate(emptyModuleBinary, {})); 99 | }, "Stray argument"); 100 | -------------------------------------------------------------------------------- /test/js-api/error-interfaces-no-symbol-tostringtag.js: -------------------------------------------------------------------------------- 1 | // META: global=jsshell 2 | 3 | test(() => { 4 | assert_not_own_property(WebAssembly.CompileError.prototype, Symbol.toStringTag); 5 | }, "WebAssembly.CompileError"); 6 | 7 | test(() => { 8 | assert_not_own_property(WebAssembly.LinkError.prototype, Symbol.toStringTag); 9 | }, "WebAssembly.LinkError"); 10 | 11 | test(() => { 12 | assert_not_own_property(WebAssembly.RuntimeError.prototype, Symbol.toStringTag); 13 | }, "WebAssembly.RuntimeError"); 14 | -------------------------------------------------------------------------------- /test/js-api/functions/helper.js: -------------------------------------------------------------------------------- 1 | function call_later(f) { 2 | const builder = new WasmModuleBuilder(); 3 | const functionIndex = builder.addImport("module", "imported", kSig_v_v); 4 | builder.addStart(functionIndex); 5 | const buffer = builder.toBuffer(); 6 | 7 | WebAssembly.instantiate(buffer, { 8 | "module": { 9 | "imported": f, 10 | } 11 | }); 12 | } 13 | -------------------------------------------------------------------------------- /test/js-api/gc/default-value.tentative.any.js: -------------------------------------------------------------------------------- 1 | // META: global=window,dedicatedworker,jsshell 2 | // META: script=/wasm/jsapi/wasm-module-builder.js 3 | 4 | let exports = {}; 5 | setup(() => { 6 | const builder = new WasmModuleBuilder(); 7 | 8 | builder.addTable(wasmRefType(kWasmAnyRef), 10, 20, [...wasmI32Const(42), ...GCInstr(kExprRefI31)]) 9 | .exportAs("tableAnyNonNullable"); 10 | builder.addTable(wasmRefNullType(kWasmAnyRef), 10, 20) 11 | .exportAs("tableAnyNullable"); 12 | 13 | const buffer = builder.toBuffer(); 14 | const module = new WebAssembly.Module(buffer); 15 | const instance = new WebAssembly.Instance(module, {}); 16 | exports = instance.exports; 17 | }); 18 | 19 | test(() => { 20 | exports.tableAnyNullable.grow(5); 21 | for (let i = 0; i < 5; i++) 22 | assert_equals(exports.tableAnyNullable.get(10 + i), null); 23 | }, "grow (nullable anyref)"); 24 | 25 | test(() => { 26 | assert_throws_js(TypeError, () => { exports.tableAnyNonNullable.grow(5); }); 27 | exports.tableAnyNonNullable.grow(5, "foo"); 28 | for (let i = 0; i < 5; i++) 29 | assert_equals(exports.tableAnyNonNullable.get(10 + i), "foo"); 30 | }, "grow (non-nullable anyref)"); 31 | 32 | test(() => { 33 | for (let i = 0; i < exports.tableAnyNullable.length; i++) { 34 | exports.tableAnyNullable.set(i); 35 | assert_equals(exports.tableAnyNullable.get(i), null); 36 | } 37 | }, "set (nullable anyref)"); 38 | 39 | test(() => { 40 | for (let i = 0; i < exports.tableAnyNonNullable.length; i++) { 41 | assert_throws_js(TypeError, () => { exports.tableAnyNonNullable.set(i); }); 42 | } 43 | }, "set (non-nullable anyref)"); 44 | -------------------------------------------------------------------------------- /test/js-api/global/toString.any.js: -------------------------------------------------------------------------------- 1 | // META: global=window,dedicatedworker,jsshell 2 | 3 | test(() => { 4 | const argument = { "value": "i32" }; 5 | const global = new WebAssembly.Global(argument); 6 | assert_class_string(global, "WebAssembly.Global"); 7 | }, "Object.prototype.toString on an Global"); 8 | 9 | test(() => { 10 | assert_own_property(WebAssembly.Global.prototype, Symbol.toStringTag); 11 | 12 | const propDesc = Object.getOwnPropertyDescriptor(WebAssembly.Global.prototype, Symbol.toStringTag); 13 | assert_equals(propDesc.value, "WebAssembly.Global", "value"); 14 | assert_equals(propDesc.configurable, true, "configurable"); 15 | assert_equals(propDesc.enumerable, false, "enumerable"); 16 | assert_equals(propDesc.writable, false, "writable"); 17 | }, "@@toStringTag exists on the prototype with the appropriate descriptor"); 18 | -------------------------------------------------------------------------------- /test/js-api/global/valueOf.any.js: -------------------------------------------------------------------------------- 1 | // META: global=window,dedicatedworker,jsshell 2 | 3 | test(() => { 4 | const argument = { "value": "i32" }; 5 | const thisValues = [ 6 | undefined, 7 | null, 8 | true, 9 | "", 10 | Symbol(), 11 | 1, 12 | {}, 13 | WebAssembly.Global, 14 | WebAssembly.Global.prototype, 15 | ]; 16 | 17 | const fn = WebAssembly.Global.prototype.valueOf; 18 | 19 | for (const thisValue of thisValues) { 20 | assert_throws_js(TypeError, () => fn.call(thisValue), `this=${format_value(thisValue)}`); 21 | } 22 | }, "Branding"); 23 | 24 | test(() => { 25 | const argument = { "value": "i32" }; 26 | const global = new WebAssembly.Global(argument, 0); 27 | assert_equals(global.valueOf({}), 0); 28 | }, "Stray argument"); 29 | -------------------------------------------------------------------------------- /test/js-api/instance/constructor-bad-imports.any.js: -------------------------------------------------------------------------------- 1 | // META: global=window,dedicatedworker,jsshell 2 | // META: script=/wasm/jsapi/wasm-module-builder.js 3 | // META: script=/wasm/jsapi/bad-imports.js 4 | 5 | test_bad_imports((name, error, build, ...arguments) => { 6 | test(() => { 7 | const builder = new WasmModuleBuilder(); 8 | build(builder); 9 | const buffer = builder.toBuffer(); 10 | const module = new WebAssembly.Module(buffer); 11 | assert_throws_js(error, () => new WebAssembly.Instance(module, ...arguments)); 12 | }, `new WebAssembly.Instance(module): ${name}`); 13 | }); 14 | -------------------------------------------------------------------------------- /test/js-api/instance/constructor-caching.any.js: -------------------------------------------------------------------------------- 1 | // META: global=window,dedicatedworker,jsshell 2 | // META: script=/wasm/jsapi/wasm-module-builder.js 3 | 4 | function getExports() { 5 | const builder = new WasmModuleBuilder(); 6 | builder 7 | .addFunction("fn", kSig_v_d) 8 | .addBody([]) 9 | .exportFunc(); 10 | 11 | builder.setTableBounds(1); 12 | builder.addExportOfKind("table", kExternalTable, 0); 13 | builder.addGlobal(kWasmI32, false).exportAs("global"); 14 | builder.addMemory(4, 8, true); 15 | 16 | const buffer = builder.toBuffer(); 17 | const module = new WebAssembly.Module(buffer); 18 | const instance = new WebAssembly.Instance(module); 19 | return instance.exports; 20 | } 21 | 22 | test(() => { 23 | const exports = getExports(); 24 | 25 | const builder = new WasmModuleBuilder(); 26 | const functionIndex = builder.addImport("module", "imported", kSig_v_d); 27 | builder.addExport("exportedFunction", functionIndex); 28 | 29 | const globalIndex = builder.addImportedGlobal("module", "global", kWasmI32); 30 | builder.addExportOfKind("exportedGlobal", kExternalGlobal, globalIndex); 31 | 32 | builder.addImportedMemory("module", "memory", 4); 33 | builder.exportMemoryAs("exportedMemory"); 34 | 35 | const tableIndex = builder.addImportedTable("module", "table", 1); 36 | builder.addExportOfKind("exportedTable", kExternalTable, tableIndex); 37 | 38 | const buffer = builder.toBuffer(); 39 | 40 | const module = new WebAssembly.Module(buffer); 41 | const instance = new WebAssembly.Instance(module, { 42 | "module": { 43 | "imported": exports.fn, 44 | "global": exports.global, 45 | "memory": exports.memory, 46 | "table": exports.table, 47 | } 48 | }); 49 | 50 | assert_equals(instance.exports.exportedFunction, exports.fn); 51 | assert_equals(instance.exports.exportedGlobal, exports.global); 52 | assert_equals(instance.exports.exportedMemory, exports.memory); 53 | assert_equals(instance.exports.exportedTable, exports.table); 54 | }); 55 | -------------------------------------------------------------------------------- /test/js-api/instance/constructor.any.js: -------------------------------------------------------------------------------- 1 | // META: global=window,dedicatedworker,jsshell 2 | // META: script=/wasm/jsapi/wasm-module-builder.js 3 | // META: script=/wasm/jsapi/assertions.js 4 | // META: script=/wasm/jsapi/instanceTestFactory.js 5 | 6 | let emptyModuleBinary; 7 | setup(() => { 8 | emptyModuleBinary = new WasmModuleBuilder().toBuffer(); 9 | }); 10 | 11 | test(() => { 12 | assert_function_name(WebAssembly.Instance, "Instance", "WebAssembly.Instance"); 13 | }, "name"); 14 | 15 | test(() => { 16 | assert_function_length(WebAssembly.Instance, 1, "WebAssembly.Instance"); 17 | }, "length"); 18 | 19 | test(() => { 20 | assert_throws_js(TypeError, () => new WebAssembly.Instance()); 21 | }, "No arguments"); 22 | 23 | test(() => { 24 | const invalidArguments = [ 25 | undefined, 26 | null, 27 | true, 28 | "", 29 | Symbol(), 30 | 1, 31 | {}, 32 | WebAssembly.Module, 33 | WebAssembly.Module.prototype, 34 | ]; 35 | for (const argument of invalidArguments) { 36 | assert_throws_js(TypeError, () => new WebAssembly.Instance(argument), 37 | `new Instance(${format_value(argument)})`); 38 | } 39 | }, "Non-Module arguments"); 40 | 41 | test(() => { 42 | const module = new WebAssembly.Module(emptyModuleBinary); 43 | assert_throws_js(TypeError, () => WebAssembly.Instance(module)); 44 | }, "Calling"); 45 | 46 | for (const [name, fn] of instanceTestFactory) { 47 | test(() => { 48 | const { buffer, args, exports, verify } = fn(); 49 | const module = new WebAssembly.Module(buffer); 50 | const instance = new WebAssembly.Instance(module, ...args); 51 | assert_Instance(instance, exports); 52 | verify(instance); 53 | }, name); 54 | } 55 | -------------------------------------------------------------------------------- /test/js-api/instance/exports.any.js: -------------------------------------------------------------------------------- 1 | // META: global=window,dedicatedworker,jsshell 2 | // META: script=/wasm/jsapi/wasm-module-builder.js 3 | 4 | let emptyModuleBinary; 5 | setup(() => { 6 | emptyModuleBinary = new WasmModuleBuilder().toBuffer(); 7 | }); 8 | 9 | test(() => { 10 | const thisValues = [ 11 | undefined, 12 | null, 13 | true, 14 | "", 15 | Symbol(), 16 | 1, 17 | {}, 18 | WebAssembly.Instance, 19 | WebAssembly.Instance.prototype, 20 | ]; 21 | 22 | const desc = Object.getOwnPropertyDescriptor(WebAssembly.Instance.prototype, "exports"); 23 | assert_equals(typeof desc, "object"); 24 | 25 | const getter = desc.get; 26 | assert_equals(typeof getter, "function"); 27 | 28 | assert_equals(typeof desc.set, "undefined"); 29 | 30 | for (const thisValue of thisValues) { 31 | assert_throws_js(TypeError, () => getter.call(thisValue), `this=${format_value(thisValue)}`); 32 | } 33 | }, "Branding"); 34 | 35 | test(() => { 36 | const module = new WebAssembly.Module(emptyModuleBinary); 37 | const instance = new WebAssembly.Instance(module); 38 | const exports = instance.exports; 39 | 40 | const desc = Object.getOwnPropertyDescriptor(WebAssembly.Instance.prototype, "exports"); 41 | assert_equals(typeof desc, "object"); 42 | 43 | const getter = desc.get; 44 | assert_equals(typeof getter, "function"); 45 | 46 | assert_equals(getter.call(instance, {}), exports); 47 | }, "Stray argument"); 48 | 49 | test(() => { 50 | const module = new WebAssembly.Module(emptyModuleBinary); 51 | const instance = new WebAssembly.Instance(module); 52 | const exports = instance.exports; 53 | instance.exports = {}; 54 | assert_equals(instance.exports, exports, "Should not change the exports"); 55 | }, "Setting (sloppy mode)"); 56 | 57 | test(() => { 58 | const module = new WebAssembly.Module(emptyModuleBinary); 59 | const instance = new WebAssembly.Instance(module); 60 | const exports = instance.exports; 61 | assert_throws_js(TypeError, () => { 62 | "use strict"; 63 | instance.exports = {}; 64 | }); 65 | assert_equals(instance.exports, exports, "Should not change the exports"); 66 | }, "Setting (strict mode)"); 67 | -------------------------------------------------------------------------------- /test/js-api/instance/toString.any.js: -------------------------------------------------------------------------------- 1 | // META: global=window,dedicatedworker,jsshell 2 | // META: script=/wasm/jsapi/wasm-module-builder.js 3 | 4 | test(() => { 5 | const emptyModuleBinary = new WasmModuleBuilder().toBuffer(); 6 | const module = new WebAssembly.Module(emptyModuleBinary); 7 | const instance = new WebAssembly.Instance(module); 8 | assert_class_string(instance, "WebAssembly.Instance"); 9 | }, "Object.prototype.toString on an Instance"); 10 | 11 | test(() => { 12 | assert_own_property(WebAssembly.Instance.prototype, Symbol.toStringTag); 13 | 14 | const propDesc = Object.getOwnPropertyDescriptor(WebAssembly.Instance.prototype, Symbol.toStringTag); 15 | assert_equals(propDesc.value, "WebAssembly.Instance", "value"); 16 | assert_equals(propDesc.configurable, true, "configurable"); 17 | assert_equals(propDesc.enumerable, false, "enumerable"); 18 | assert_equals(propDesc.writable, false, "writable"); 19 | }, "@@toStringTag exists on the prototype with the appropriate descriptor"); 20 | -------------------------------------------------------------------------------- /test/js-api/memory/assertions.js: -------------------------------------------------------------------------------- 1 | function assert_ArrayBuffer(actual, { size=0, shared=false, detached=false }, message) { 2 | // https://github.com/WebAssembly/spec/issues/840 3 | // See https://github.com/whatwg/html/issues/5380 for why not `self.SharedArrayBuffer` 4 | const isShared = !("isView" in actual.constructor); 5 | assert_equals(isShared, shared, `${message}: constructor`); 6 | const sharedString = shared ? "Shared" : ""; 7 | assert_equals(actual.toString(), `[object ${sharedString}ArrayBuffer]`, `${message}: toString()`); 8 | assert_equals(Object.getPrototypeOf(actual).toString(), `[object ${sharedString}ArrayBuffer]`, `${message}: prototype toString()`); 9 | if (detached) { 10 | // https://github.com/tc39/ecma262/issues/678 11 | let byteLength; 12 | try { 13 | byteLength = actual.byteLength; 14 | } catch (e) { 15 | byteLength = 0; 16 | } 17 | assert_equals(byteLength, 0, `${message}: detached size`); 18 | } else { 19 | assert_equals(actual.byteLength, 0x10000 * size, `${message}: size`); 20 | if (size > 0) { 21 | const array = new Uint8Array(actual); 22 | assert_equals(array[0], 0, `${message}: first element`); 23 | assert_equals(array[array.byteLength - 1], 0, `${message}: last element`); 24 | } 25 | } 26 | assert_equals(Object.isFrozen(actual), shared, "buffer frozen"); 27 | assert_equals(Object.isExtensible(actual), !shared, "buffer extensibility"); 28 | } 29 | 30 | function assert_Memory(memory, { size=0, shared=false }) { 31 | assert_equals(Object.getPrototypeOf(memory), WebAssembly.Memory.prototype, 32 | "prototype"); 33 | assert_true(Object.isExtensible(memory), "extensible"); 34 | 35 | // https://github.com/WebAssembly/spec/issues/840 36 | assert_equals(memory.buffer, memory.buffer, "buffer should be idempotent"); 37 | assert_ArrayBuffer(memory.buffer, { size, shared }); 38 | } 39 | -------------------------------------------------------------------------------- /test/js-api/memory/buffer.any.js: -------------------------------------------------------------------------------- 1 | // META: global=window,dedicatedworker,jsshell 2 | 3 | test(() => { 4 | const thisValues = [ 5 | undefined, 6 | null, 7 | true, 8 | "", 9 | Symbol(), 10 | 1, 11 | {}, 12 | WebAssembly.Memory, 13 | WebAssembly.Memory.prototype, 14 | ]; 15 | 16 | const desc = Object.getOwnPropertyDescriptor(WebAssembly.Memory.prototype, "buffer"); 17 | assert_equals(typeof desc, "object"); 18 | 19 | const getter = desc.get; 20 | assert_equals(typeof getter, "function"); 21 | 22 | assert_equals(typeof desc.set, "undefined"); 23 | 24 | for (const thisValue of thisValues) { 25 | assert_throws_js(TypeError, () => getter.call(thisValue), `this=${format_value(thisValue)}`); 26 | } 27 | }, "Branding"); 28 | 29 | test(() => { 30 | const argument = { "initial": 0 }; 31 | const memory = new WebAssembly.Memory(argument); 32 | const buffer = memory.buffer; 33 | 34 | const desc = Object.getOwnPropertyDescriptor(WebAssembly.Memory.prototype, "buffer"); 35 | assert_equals(typeof desc, "object"); 36 | 37 | const getter = desc.get; 38 | assert_equals(typeof getter, "function"); 39 | 40 | assert_equals(getter.call(memory, {}), buffer); 41 | }, "Stray argument"); 42 | 43 | test(() => { 44 | const argument = { "initial": 0 }; 45 | const memory = new WebAssembly.Memory(argument); 46 | const memory2 = new WebAssembly.Memory(argument); 47 | const buffer = memory.buffer; 48 | assert_not_equals(buffer, memory2.buffer, "Need two distinct buffers"); 49 | memory.buffer = memory2.buffer; 50 | assert_equals(memory.buffer, buffer, "Should not change the buffer"); 51 | }, "Setting (sloppy mode)"); 52 | 53 | test(() => { 54 | const argument = { "initial": 0 }; 55 | const memory = new WebAssembly.Memory(argument); 56 | const memory2 = new WebAssembly.Memory(argument); 57 | const buffer = memory.buffer; 58 | assert_not_equals(buffer, memory2.buffer, "Need two distinct buffers"); 59 | assert_throws_js(TypeError, () => { 60 | "use strict"; 61 | memory.buffer = memory2.buffer; 62 | }); 63 | assert_equals(memory.buffer, buffer, "Should not change the buffer"); 64 | }, "Setting (strict mode)"); 65 | -------------------------------------------------------------------------------- /test/js-api/memory/toString.any.js: -------------------------------------------------------------------------------- 1 | // META: global=window,dedicatedworker,jsshell 2 | 3 | test(() => { 4 | const argument = { "initial": 0 }; 5 | const memory = new WebAssembly.Memory(argument); 6 | assert_class_string(memory, "WebAssembly.Memory"); 7 | }, "Object.prototype.toString on an Memory"); 8 | 9 | test(() => { 10 | assert_own_property(WebAssembly.Memory.prototype, Symbol.toStringTag); 11 | 12 | const propDesc = Object.getOwnPropertyDescriptor(WebAssembly.Memory.prototype, Symbol.toStringTag); 13 | assert_equals(propDesc.value, "WebAssembly.Memory", "value"); 14 | assert_equals(propDesc.configurable, true, "configurable"); 15 | assert_equals(propDesc.enumerable, false, "enumerable"); 16 | assert_equals(propDesc.writable, false, "writable"); 17 | }, "@@toStringTag exists on the prototype with the appropriate descriptor"); 18 | -------------------------------------------------------------------------------- /test/js-api/module/constructor.any.js: -------------------------------------------------------------------------------- 1 | // META: global=window,dedicatedworker,jsshell 2 | // META: script=/wasm/jsapi/wasm-module-builder.js 3 | // META: script=/wasm/jsapi/assertions.js 4 | 5 | let emptyModuleBinary; 6 | setup(() => { 7 | emptyModuleBinary = new WasmModuleBuilder().toBuffer(); 8 | }); 9 | 10 | test(() => { 11 | assert_function_name(WebAssembly.Module, "Module", "WebAssembly.Module"); 12 | }, "name"); 13 | 14 | test(() => { 15 | assert_function_length(WebAssembly.Module, 1, "WebAssembly.Module"); 16 | }, "length"); 17 | 18 | test(() => { 19 | assert_throws_js(TypeError, () => new WebAssembly.Module()); 20 | }, "No arguments"); 21 | 22 | test(() => { 23 | assert_throws_js(TypeError, () => WebAssembly.Module(emptyModuleBinary)); 24 | }, "Calling"); 25 | 26 | test(() => { 27 | const invalidArguments = [ 28 | undefined, 29 | null, 30 | true, 31 | "test", 32 | Symbol(), 33 | 7, 34 | NaN, 35 | {}, 36 | ArrayBuffer, 37 | ArrayBuffer.prototype, 38 | Array.from(emptyModuleBinary), 39 | ]; 40 | for (const argument of invalidArguments) { 41 | assert_throws_js(TypeError, () => new WebAssembly.Module(argument), 42 | `new Module(${format_value(argument)})`); 43 | } 44 | }, "Invalid arguments"); 45 | 46 | test(() => { 47 | const buffer = new Uint8Array(); 48 | assert_throws_js(WebAssembly.CompileError, () => new WebAssembly.Module(buffer)); 49 | }, "Empty buffer"); 50 | 51 | test(() => { 52 | const buffer = new Uint8Array(Array.from(emptyModuleBinary).concat([0, 0])); 53 | assert_throws_js(WebAssembly.CompileError, () => new WebAssembly.Module(buffer)); 54 | }, "Invalid code"); 55 | 56 | test(() => { 57 | const module = new WebAssembly.Module(emptyModuleBinary); 58 | assert_equals(Object.getPrototypeOf(module), WebAssembly.Module.prototype); 59 | }, "Prototype"); 60 | 61 | test(() => { 62 | const module = new WebAssembly.Module(emptyModuleBinary); 63 | assert_true(Object.isExtensible(module)); 64 | }, "Extensibility"); 65 | 66 | test(() => { 67 | const module = new WebAssembly.Module(emptyModuleBinary, {}); 68 | assert_equals(Object.getPrototypeOf(module), WebAssembly.Module.prototype); 69 | }, "Stray argument"); 70 | -------------------------------------------------------------------------------- /test/js-api/module/toString.any.js: -------------------------------------------------------------------------------- 1 | // META: global=window,dedicatedworker,jsshell 2 | // META: script=/wasm/jsapi/wasm-module-builder.js 3 | 4 | test(() => { 5 | const emptyModuleBinary = new WasmModuleBuilder().toBuffer(); 6 | const module = new WebAssembly.Module(emptyModuleBinary); 7 | assert_class_string(module, "WebAssembly.Module"); 8 | }, "Object.prototype.toString on an Module"); 9 | 10 | test(() => { 11 | assert_own_property(WebAssembly.Module.prototype, Symbol.toStringTag); 12 | 13 | const propDesc = Object.getOwnPropertyDescriptor(WebAssembly.Module.prototype, Symbol.toStringTag); 14 | assert_equals(propDesc.value, "WebAssembly.Module", "value"); 15 | assert_equals(propDesc.configurable, true, "configurable"); 16 | assert_equals(propDesc.enumerable, false, "enumerable"); 17 | assert_equals(propDesc.writable, false, "writable"); 18 | }, "@@toStringTag exists on the prototype with the appropriate descriptor"); 19 | -------------------------------------------------------------------------------- /test/js-api/prototypes.any.js: -------------------------------------------------------------------------------- 1 | // META: global=window,dedicatedworker,jsshell 2 | // META: script=/wasm/jsapi/assertions.js 3 | // META: script=/wasm/jsapi/wasm-module-builder.js 4 | 5 | let emptyModuleBinary; 6 | setup(() => { 7 | emptyModuleBinary = new WasmModuleBuilder().toBuffer(); 8 | }); 9 | 10 | test(() => { 11 | class _Module extends WebAssembly.Module {} 12 | let module = new _Module(emptyModuleBinary); 13 | assert_true(module instanceof _Module, "_Module instanceof _Module"); 14 | assert_true(module instanceof WebAssembly.Module, "_Module instanceof WebAssembly.Module"); 15 | }, "_Module"); 16 | 17 | test(() => { 18 | class _Instance extends WebAssembly.Instance {} 19 | let instance = new _Instance(new WebAssembly.Module(emptyModuleBinary)); 20 | assert_true(instance instanceof _Instance, "_Instance instanceof _Instance"); 21 | assert_true(instance instanceof WebAssembly.Instance, "_Instance instanceof WebAssembly.Instance"); 22 | }, "_Instance"); 23 | 24 | test(() => { 25 | class _Memory extends WebAssembly.Memory {} 26 | let memory = new _Memory({initial: 0, maximum: 1}); 27 | assert_true(memory instanceof _Memory, "_Memory instanceof _Memory"); 28 | assert_true(memory instanceof WebAssembly.Memory, "_Memory instanceof WebAssembly.Memory"); 29 | }, "_Memory"); 30 | 31 | test(() => { 32 | class _Table extends WebAssembly.Table {} 33 | let table = new _Table({initial: 0, element: "anyfunc"}); 34 | assert_true(table instanceof _Table, "_Table instanceof _Table"); 35 | assert_true(table instanceof WebAssembly.Table, "_Table instanceof WebAssembly.Table"); 36 | }, "_Table"); 37 | 38 | test(() => { 39 | class _Global extends WebAssembly.Global {} 40 | let global = new _Global({value: "i32", mutable: false}, 0); 41 | assert_true(global instanceof _Global, "_Global instanceof _Global"); 42 | assert_true(global instanceof WebAssembly.Global, "_Global instanceof WebAssembly.Global"); 43 | }, "_Global"); 44 | -------------------------------------------------------------------------------- /test/js-api/table/assertions.js: -------------------------------------------------------------------------------- 1 | function assert_equal_to_array(table, expected, message) { 2 | assert_equals(table.length, expected.length, `${message}: length`); 3 | // The argument check in get() happens before the range check, and negative numbers 4 | // are illegal, hence will throw TypeError per spec. 5 | assert_throws_js(TypeError, () => table.get(-1), `${message}: table.get(-1)`); 6 | for (let i = 0; i < expected.length; ++i) { 7 | assert_equals(table.get(i), expected[i], `${message}: table.get(${i} of ${expected.length})`); 8 | } 9 | assert_throws_js(RangeError, () => table.get(expected.length), 10 | `${message}: table.get(${expected.length} of ${expected.length})`); 11 | assert_throws_js(RangeError, () => table.get(expected.length + 1), 12 | `${message}: table.get(${expected.length + 1} of ${expected.length})`); 13 | } 14 | 15 | function assert_Table(actual, expected) { 16 | assert_equals(Object.getPrototypeOf(actual), WebAssembly.Table.prototype, 17 | "prototype"); 18 | assert_true(Object.isExtensible(actual), "extensible"); 19 | 20 | assert_equals(actual.length, expected.length, "length"); 21 | for (let i = 0; i < expected.length; ++i) { 22 | assert_equals(actual.get(i), null, `actual.get(${i})`); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /test/js-api/table/length.any.js: -------------------------------------------------------------------------------- 1 | // META: global=window,dedicatedworker,jsshell 2 | 3 | test(() => { 4 | const thisValues = [ 5 | undefined, 6 | null, 7 | true, 8 | "", 9 | Symbol(), 10 | 1, 11 | {}, 12 | WebAssembly.Table, 13 | WebAssembly.Table.prototype, 14 | ]; 15 | 16 | const desc = Object.getOwnPropertyDescriptor(WebAssembly.Table.prototype, "length"); 17 | assert_equals(typeof desc, "object"); 18 | 19 | const getter = desc.get; 20 | assert_equals(typeof getter, "function"); 21 | 22 | assert_equals(typeof desc.set, "undefined"); 23 | 24 | for (const thisValue of thisValues) { 25 | assert_throws_js(TypeError, () => getter.call(thisValue), `this=${format_value(thisValue)}`); 26 | } 27 | }, "Branding"); 28 | 29 | test(() => { 30 | const argument = { "element": "anyfunc", "initial": 2 }; 31 | const table = new WebAssembly.Table(argument); 32 | assert_equals(table.length, 2, "Initial length"); 33 | 34 | const desc = Object.getOwnPropertyDescriptor(WebAssembly.Table.prototype, "length"); 35 | assert_equals(typeof desc, "object"); 36 | 37 | const getter = desc.get; 38 | assert_equals(typeof getter, "function"); 39 | 40 | assert_equals(getter.call(table, {}), 2); 41 | }, "Stray argument"); 42 | 43 | test(() => { 44 | const argument = { "element": "anyfunc", "initial": 2 }; 45 | const table = new WebAssembly.Table(argument); 46 | assert_equals(table.length, 2, "Initial length"); 47 | table.length = 4; 48 | assert_equals(table.length, 2, "Should not change the length"); 49 | }, "Setting (sloppy mode)"); 50 | 51 | test(() => { 52 | const argument = { "element": "anyfunc", "initial": 2 }; 53 | const table = new WebAssembly.Table(argument); 54 | assert_equals(table.length, 2, "Initial length"); 55 | assert_throws_js(TypeError, () => { 56 | "use strict"; 57 | table.length = 4; 58 | }); 59 | assert_equals(table.length, 2, "Should not change the length"); 60 | }, "Setting (strict mode)"); 61 | -------------------------------------------------------------------------------- /test/js-api/table/toString.any.js: -------------------------------------------------------------------------------- 1 | // META: global=window,dedicatedworker,jsshell 2 | 3 | test(() => { 4 | const argument = { "element": "anyfunc", "initial": 0 }; 5 | const table = new WebAssembly.Table(argument); 6 | assert_class_string(table, "WebAssembly.Table"); 7 | }, "Object.prototype.toString on an Table"); 8 | 9 | test(() => { 10 | assert_own_property(WebAssembly.Table.prototype, Symbol.toStringTag); 11 | 12 | const propDesc = Object.getOwnPropertyDescriptor(WebAssembly.Table.prototype, Symbol.toStringTag); 13 | assert_equals(propDesc.value, "WebAssembly.Table", "value"); 14 | assert_equals(propDesc.configurable, true, "configurable"); 15 | assert_equals(propDesc.enumerable, false, "enumerable"); 16 | assert_equals(propDesc.writable, false, "writable"); 17 | }, "@@toStringTag exists on the prototype with the appropriate descriptor"); 18 | -------------------------------------------------------------------------------- /test/meta/Makefile: -------------------------------------------------------------------------------- 1 | SHARED_MEM=false 2 | 3 | # SpiderMonkey shell 4 | JSSHELL=~/mozilla-central/js/src/build-debug/dist/bin/js -e 'const WITH_SHARED_MEMORY=$(SHARED_MEM);' -f common.js 5 | 6 | # Node.js 7 | #JSSHELL=./noderun.sh $(SHARED_MEM) 8 | 9 | TARGETDIR=../core 10 | 11 | .PHONY: all 12 | 13 | all: $(TARGETDIR)/memory_copy.wast \ 14 | $(TARGETDIR)/memory_init.wast \ 15 | $(TARGETDIR)/memory_fill.wast \ 16 | $(TARGETDIR)/table_copy.wast \ 17 | $(TARGETDIR)/table_init.wast 18 | 19 | $(TARGETDIR)/memory_copy.wast: generate_memory_copy.js common.js Makefile 20 | $(JSSHELL) $< > $@ 21 | 22 | $(TARGETDIR)/memory_init.wast: generate_memory_init.js common.js Makefile 23 | $(JSSHELL) $< > $@ 24 | 25 | $(TARGETDIR)/memory_fill.wast: generate_memory_fill.js common.js Makefile 26 | $(JSSHELL) $< > $@ 27 | 28 | $(TARGETDIR)/table_copy.wast: generate_table_copy.js common.js Makefile 29 | $(JSSHELL) $< > $@ 30 | 31 | $(TARGETDIR)/table_init.wast: generate_table_init.js common.js Makefile 32 | $(JSSHELL) $< > $@ 33 | -------------------------------------------------------------------------------- /test/meta/README.md: -------------------------------------------------------------------------------- 1 | These programs generate test cases. See Makefile for details. 2 | -------------------------------------------------------------------------------- /test/meta/common.js: -------------------------------------------------------------------------------- 1 | const PAGESIZE = 65536; 2 | 3 | function print_origin(origin) { 4 | print(";;"); 5 | print(";; Generated by ../meta/" + origin); 6 | print(";; DO NOT EDIT THIS FILE. CHANGE THE SOURCE AND REGENERATE."); 7 | print(";;"); 8 | } 9 | 10 | function checkRangeCode() { 11 | return ` 12 | (func (export "checkRange") (param $from i32) (param $to i32) (param $expected i32) (result i32) 13 | (loop $cont 14 | (if (i32.eq (local.get $from) (local.get $to)) 15 | (then 16 | (return (i32.const -1)))) 17 | (if (i32.eq (i32.load8_u (local.get $from)) (local.get $expected)) 18 | (then 19 | (local.set $from (i32.add (local.get $from) (i32.const 1))) 20 | (br $cont)))) 21 | (return (local.get $from))) 22 | `; 23 | } 24 | 25 | function checkRange(from, to, expected) { 26 | print( 27 | `(assert_return (invoke "checkRange" (i32.const ${from}) (i32.const ${to}) (i32.const ${expected})) 28 | (i32.const -1))`); 29 | } 30 | -------------------------------------------------------------------------------- /test/meta/noderun.sh: -------------------------------------------------------------------------------- 1 | if [ $# -ne 2 ]; then 2 | echo "Bad args" 3 | exit 1 4 | fi 5 | 6 | rm -f nodeprog.js 7 | cat <> nodeprog.js 8 | const WITH_SHARED_MEMORY=$1; 9 | function print(x) { 10 | console.log(x); 11 | } 12 | EOF 13 | cat common.js >> nodeprog.js 14 | cat $2 >> nodeprog.js 15 | node nodeprog.js 16 | rm nodeprog.js 17 | -------------------------------------------------------------------------------- /test/sync-js-api.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import glob 4 | import os 5 | import shutil 6 | import subprocess 7 | 8 | LOCAL_FILES = [ 9 | "LICENSE.md", 10 | "README.md", 11 | 12 | # Currently doesn't pass the stability checker in wpt. 13 | "limits.any.js", 14 | ] 15 | 16 | 17 | def copy_from_local(local_dir, out): 18 | for local_file in LOCAL_FILES: 19 | shutil.copy(os.path.join(local_dir, local_file), out) 20 | 21 | 22 | def copy_from_upstream(upstream, out): 23 | upstream = os.path.abspath(upstream) 24 | paths = glob.glob(os.path.join(upstream, "**", "*.js"), recursive=True) 25 | for path in paths: 26 | relpath = os.path.relpath(path, upstream) 27 | 28 | # Tests for proposals that have not merged here yet. 29 | if ".tentative" in relpath: 30 | continue 31 | 32 | # Requires `fetch()` and various wpt infrastructure. 33 | if os.path.basename(relpath) == "idlharness.any.js": 34 | continue 35 | 36 | dest = os.path.join(out, relpath) 37 | os.makedirs(os.path.dirname(dest), exist_ok=True) 38 | shutil.copy(path, dest) 39 | 40 | 41 | def main(upstream): 42 | local_dir = os.path.join("test", "js-api") 43 | scratch = os.path.join("test", "js-api-temp") 44 | os.mkdir(scratch) 45 | copy_from_local(local_dir, scratch) 46 | copy_from_upstream(os.path.join(upstream, "wasm", "jsapi"), scratch) 47 | shutil.rmtree(local_dir) 48 | os.rename(scratch, local_dir) 49 | subprocess.check_call(["git", "add", local_dir]) 50 | 51 | 52 | if __name__ == "__main__": 53 | import sys 54 | main(*sys.argv[1:]) 55 | -------------------------------------------------------------------------------- /w3c.json: -------------------------------------------------------------------------------- 1 | { 2 | "group": [78073] 3 | , "contacts": ["ericprud"] 4 | , "repo-type": "cg-report" 5 | } 6 | -------------------------------------------------------------------------------- /wasm-specs.bib: -------------------------------------------------------------------------------- 1 | @report{WebAssemblyCoreSpecification1, 2 | title = {{WebAssembly Core Specification}}, 3 | version = {1.0}, 4 | editor = {Rossberg, Andreas}, 5 | date = {2019-12-05}, 6 | institution = {{W3C}}, 7 | url = {https://www.w3.org/TR/wasm-core-1/}, 8 | langid = {english} 9 | } 10 | 11 | @report{WebAssemblyCoreSpecification2, 12 | title = {{WebAssembly Core Specification}}, 13 | version = {2.0}, 14 | editor = {Rossberg, Andreas}, 15 | date = {2022-04-19}, 16 | institution = {{W3C}}, 17 | url = {https://www.w3.org/TR/wasm-core-2/}, 18 | langid = {english}, 19 | note = {https://webassembly.github.io/spec/core/_download/WebAssembly.pdf} 20 | } 21 | --------------------------------------------------------------------------------