├── .gitattributes ├── .github └── workflows │ ├── main.yml │ └── mirror.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 │ │ ├── gen-index-instructions.py │ │ ├── implementation.rst │ │ ├── index-instructions.rst │ │ ├── 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 │ ├── 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 │ │ ├── katex_fix.patch │ │ ├── macros.def │ │ ├── mathdef.py │ │ ├── mathdefbs.py │ │ ├── mathjax2katex.py │ │ └── pseudo-lexer.py │ └── valid │ │ ├── conventions.rst │ │ ├── index.rst │ │ ├── instructions.rst │ │ ├── modules.rst │ │ └── types.rst ├── deploy.sh ├── index.html ├── js-api │ ├── Makefile │ └── index.bs ├── util │ └── htmldiff.pl └── web-api │ ├── Makefile │ └── index.bs ├── interpreter ├── .gitignore ├── .merlin ├── LICENSE ├── Makefile ├── README.md ├── binary │ ├── decode.ml │ ├── decode.mli │ ├── encode.ml │ ├── encode.mli │ ├── utf8.ml │ └── utf8.mli ├── 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 ├── main │ ├── flags.ml │ └── main.ml ├── meta │ ├── findlib │ │ └── META │ └── jslib │ │ ├── bsconfig.json │ │ ├── build.sh │ │ └── wasm.ml ├── runtime │ ├── func.ml │ ├── func.mli │ ├── global.ml │ ├── global.mli │ ├── instance.ml │ ├── memory.ml │ ├── memory.mli │ ├── table.ml │ └── table.mli ├── script │ ├── import.ml │ ├── import.mli │ ├── js.ml │ ├── js.mli │ ├── run.ml │ ├── run.mli │ └── script.ml ├── syntax │ ├── ast.ml │ ├── free.ml │ ├── free.mli │ ├── operators.ml │ ├── types.ml │ └── values.ml ├── tests │ └── smallint.ml ├── text │ ├── arrange.ml │ ├── arrange.mli │ ├── lexer.mli │ ├── lexer.mll │ ├── parse.ml │ ├── parse.mli │ ├── parser.mly │ ├── print.ml │ └── print.mli ├── util │ ├── error.ml │ ├── error.mli │ ├── lib.ml │ ├── lib.mli │ ├── sexpr.ml │ ├── sexpr.mli │ ├── source.ml │ └── source.mli ├── valid │ ├── valid.ml │ └── valid.mli └── winmake.bat ├── papers ├── LICENSE ├── README.md ├── oopsla2019.pdf └── pldi2017.pdf ├── proposals ├── README.md ├── bulk-memory-operations │ └── Overview.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 └── stringref │ └── 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_table.wast │ ├── bulk.wast │ ├── call.wast │ ├── call_indirect.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 │ ├── 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_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 │ ├── ref_func.wast │ ├── ref_is_null.wast │ ├── ref_null.wast │ ├── return.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_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.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 │ ├── 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/main.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | # Allows you to run this workflow manually from the Actions tab 10 | workflow_dispatch: 11 | 12 | jobs: 13 | ref-interpreter: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v2 17 | - name: Setup OCaml 18 | uses: ocaml/setup-ocaml@v2 19 | with: 20 | ocaml-compiler: 4.12.x 21 | - run: opam install --yes ocamlbuild.0.14.0 22 | - run: cd interpreter && opam exec make all 23 | 24 | build-js-api-spec: 25 | runs-on: ubuntu-latest 26 | steps: 27 | - uses: actions/checkout@v2 28 | - run: pip install bikeshed && bikeshed update 29 | - run: bikeshed spec "document/js-api/index.bs" "document/js-api/index.html" 30 | - uses: actions/upload-artifact@v2 31 | with: 32 | name: js-api-rendered 33 | path: document/js-api/index.html 34 | 35 | build-web-api-spec: 36 | runs-on: ubuntu-latest 37 | steps: 38 | - uses: actions/checkout@v2 39 | - run: pip install bikeshed && bikeshed update 40 | - run: bikeshed spec "document/web-api/index.bs" "document/web-api/index.html" 41 | - uses: actions/upload-artifact@v2 42 | with: 43 | name: web-api-rendered 44 | path: document/web-api/index.html 45 | 46 | build-core-spec: 47 | runs-on: ubuntu-latest 48 | steps: 49 | - uses: actions/checkout@v2 50 | with: 51 | submodules: "recursive" 52 | - run: pip install bikeshed && bikeshed update 53 | - run: pip install six 54 | - run: sudo apt-get update -y && sudo apt-get install -y latexmk texlive-latex-recommended texlive-latex-extra texlive-fonts-recommended 55 | - run: pip install sphinx==4.0.0 56 | - run: cd document/core && make all 57 | - uses: actions/upload-artifact@v2 58 | with: 59 | name: core-api-rendered 60 | path: document/core/_build/html 61 | 62 | publish-spec: 63 | runs-on: ubuntu-latest 64 | needs: [build-core-spec, build-js-api-spec, build-web-api-spec] 65 | steps: 66 | - uses: actions/checkout@v2 67 | - run: mkdir _output && cp document/index.html _output/index.html 68 | - uses: actions/download-artifact@v2 69 | with: 70 | name: js-api-rendered 71 | path: _output/js-api 72 | - uses: actions/download-artifact@v2 73 | with: 74 | name: web-api-rendered 75 | path: _output/web-api 76 | - uses: actions/download-artifact@v2 77 | with: 78 | name: core-api-rendered 79 | path: _output/core 80 | - name: Publish HTML to GitHub Pages 81 | if: github.ref == 'refs/heads/main' 82 | uses: peaceiris/actions-gh-pages@v3 83 | with: 84 | publish_dir: ./_output 85 | github_token: ${{ secrets.GITHUB_TOKEN }} 86 | -------------------------------------------------------------------------------- /.github/workflows/mirror.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: 4 | - 'main' 5 | 6 | jobs: 7 | mirror_job: 8 | runs-on: ubuntu-latest 9 | name: Mirror main branch to master branch 10 | steps: 11 | - name: Mirror action step 12 | id: mirror 13 | uses: google/mirror-branch-action@v1.0 14 | with: 15 | github-token: ${{ secrets.GITHUB_TOKEN }} 16 | source: 'main' 17 | dest: 'master' 18 | -------------------------------------------------------------------------------- /.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 | ![Build Status](https://github.com/WebAssembly/stringref/actions/workflows/main.yml/badge.svg) 2 | 3 | # Reference-Typed Strings Proposal for WebAssembly 4 | 5 | This repository is a clone of [github.com/WebAssembly/spec/](https://github.com/WebAssembly/spec/). 6 | It is meant for discussion, prototype specification and implementation of a proposal to 7 | add support for stringrefs to WebAssembly. 8 | 9 | * See the [overview](proposals/stringref/Overview.md) for a summary of the proposal. 10 | 11 | * See the [modified spec](https://webassembly.github.io/stringref/) for details. 12 | 13 | Original `README` from upstream repository follows... 14 | 15 | # spec 16 | 17 | This repository holds the sources for the WebAssembly draft specification 18 | (to seed a future 19 | [WebAssembly Working Group](https://lists.w3.org/Archives/Public/public-new-work/2017Jun/0005.html)), 20 | a reference implementation, and the official testsuite. 21 | 22 | A formatted version of the spec is available here: 23 | [webassembly.github.io/spec](https://webassembly.github.io/spec/), 24 | 25 | Participation is welcome. Discussions about new features, significant semantic 26 | changes, or any specification change likely to generate substantial discussion 27 | should take place in 28 | [the WebAssembly design repository](https://github.com/WebAssembly/design) 29 | first, so that this spec repository can remain focused. And please follow the 30 | [guidelines for contributing](Contributing.md). 31 | 32 | # citing 33 | 34 | For citing WebAssembly in LaTeX, use [this bibtex file](wasm-specs.bib). 35 | -------------------------------------------------------------------------------- /document/Makefile: -------------------------------------------------------------------------------- 1 | DIRS = core js-api web-api 2 | FILES = index.html 3 | BUILDDIR = _build 4 | 5 | # Global targets. 6 | 7 | .PHONY: all 8 | all: $(BUILDDIR) root $(DIRS) 9 | 10 | $(BUILDDIR): 11 | mkdir -p $@ 12 | 13 | .PHONY: deploy 14 | deploy: 15 | GIT_DEPLOY_DIR=$(BUILDDIR) bash deploy.sh 16 | 17 | .PHONY: publish 18 | publish: all deploy 19 | 20 | .PHONY: clean 21 | clean: $(DIRS:%=clean-%) 22 | rm -rf $(BUILDDIR) 23 | 24 | .PHONY: diff 25 | diff: $(DIRS:%=diff-%) 26 | 27 | 28 | # Directory-specific targets. 29 | 30 | .PHONY: root 31 | root: $(BUILDDIR) 32 | touch $(BUILDDIR)/.nojekyll 33 | cp -f $(FILES) $(BUILDDIR)/ 34 | 35 | .PHONY: $(DIRS) 36 | $(DIRS): %: $(BUILDDIR) $(DIRS:%=build-%) $(DIRS:%=dir-%) 37 | 38 | .PHONY: $(DIRS:%=build-%) 39 | $(DIRS:%=build-%): build-%: 40 | (cd $(@:build-%=%); make BUILDDIR=$(BUILDDIR) all) 41 | 42 | .PHONY: $(DIRS:%=dir-%) 43 | $(DIRS:%=dir-%): dir-%: 44 | mkdir -p $(BUILDDIR)/$(@:dir-%=%) 45 | rm -rf $(BUILDDIR)/$(@:dir-%=%)/* 46 | cp -R $(@:dir-%=%)/$(BUILDDIR)/html/* $(BUILDDIR)/$(@:dir-%=%)/ 47 | 48 | .PHONY: $(DIRS:%=deploy-%) 49 | $(DIRS:%=deploy-%): deploy-%: 50 | GIT_DEPLOY_DIR=$(BUILDDIR) GIT_DEPLOY_SUBDIR=$(@:deploy-%=%) bash deploy.sh 51 | 52 | .PHONY: $(DIRS:%=publish-%) 53 | $(DIRS:%=publish-%): publish-%: % deploy-% 54 | 55 | .PHONY: $(DIRS:%=clean-%) 56 | $(DIRS:%=clean-%): clean-%: 57 | (cd $(@:clean-%=%); make BUILDDIR=$(BUILDDIR) clean) 58 | rm -rf $(BUILDDIR)/$(@:clean-%=%) 59 | 60 | .PHONY: $(DIRS:%=diff-%) 61 | $(DIRS:%=diff-%): diff-%: 62 | (cd $(@:diff-%=%); make BUILDDIR=$(BUILDDIR) diff) 63 | 64 | 65 | # Help. 66 | 67 | .PHONY: help 68 | help: 69 | @echo "Please use \`make ' where is one of" 70 | @echo " all to build all documents" 71 | @echo " publish to make all and push to gh-pages" 72 | @echo " to build a specific subdirectory" 73 | @echo " publish- to build and push a specific subdirectory" 74 | 75 | .PHONY: usage 76 | usage: help 77 | -------------------------------------------------------------------------------- /document/README.md: -------------------------------------------------------------------------------- 1 | # WebAssembly Specifications 2 | 3 | This directory contains the source code for the WebAssembly spec documents, as served from the [webassembly.github.io/spec](https://webassembly.github.io/spec) pages. 4 | It uses [Sphinx](http://www.sphinx-doc.org/) and [Bikeshed](https://github.com/tabatkins/bikeshed). 5 | 6 | To install Sphinx (and required library six): 7 | ``` 8 | pip install sphinx six 9 | ``` 10 | 11 | To install Bikeshed, see the instructions [here](https://tabatkins.github.io/bikeshed/#installing). 12 | 13 | 14 | To build everything locally (result appears in `_build/`): 15 | ``` 16 | make all 17 | ``` 18 | 19 | To build everything and update [webassembly.github.io/spec](https://webassembly.github.io/spec) with it: 20 | ``` 21 | make publish 22 | ``` 23 | Please make sure to only use that once a change has approval. 24 | 25 | ## Step by step guide to building the spec 26 | 27 | ### Prerequisites 28 | 29 | You will need `python3.7`, and `pip`. `pip` should come with Python, if not follow [these installation instructions for `pip`](https://pip.pypa.io/en/stable/installing/), or check your system package manager for `pip3`. 30 | 31 | > Important: you will need the version of pip that works with `python3.7`. 32 | 33 | 34 | Use something like [`pipenv`](https://pipenv.pypa.io/) to keep your system installation of Python clean. 35 | 36 | ``` 37 | pip install pipenv 38 | pipenv --python 3.7 39 | pipenv shell 40 | ``` 41 | 42 | Install Python dependencies: 43 | 44 | ``` 45 | pipenv install Sphinx==4.0.0 six 46 | ``` 47 | 48 | ### Checking out the repository 49 | 50 | Make sure this repository was cloned with `--recursive`: 51 | 52 | ``` 53 | git clone --recursive https://github.com/WebAssembly/spec 54 | ``` 55 | 56 | If you have already cloned but without `--recursive`, you can delete and re-clone, or `cd` into `spec` and run: 57 | 58 | ``` 59 | git submodule update --init --recursive 60 | ``` 61 | 62 | The rest of these instructions assume you are in the directory where is README is: 63 | 64 | ``` 65 | cd spec/document 66 | ``` 67 | 68 | ### Building the multi-page HTML document 69 | 70 | You can now build the [multi-page html document](https://webassembly.github.io/spec/core/): 71 | 72 | ``` 73 | make -C core html 74 | ``` 75 | 76 | ### Building the single-page HTML document 77 | 78 | To build the [single-page W3C version](https://webassembly.github.io/spec/core/bikeshed/), there are more dependencies to install. First, get [Bikeshed](https://github.com/tabatkins/bikeshed): 79 | 80 | ``` 81 | # cd back to root of git directory 82 | git clone https://github.com/tabatkins/bikeshed.git 83 | pipenv install -e bikeshed 84 | bikeshed update 85 | ``` 86 | 87 | You will also need `npm` and `yarn` for all the LaTeX goodness. `npm` might already be available on your system, you can also use something like [`nvm`](https://github.com/nvm-sh/nvm) to prevent messing with system packages: 88 | 89 | ``` 90 | npm install -g yarn 91 | cd document 92 | make -C core bikeshed 93 | ``` 94 | 95 | ### Building the PDF 96 | 97 | To build the [PDF](https://webassembly.github.io/spec/core/_download/WebAssembly.pdf), you will need `texlive-full`, install it using your system package manager: 98 | 99 | ``` 100 | apt install texlive-full 101 | make -C core pdf 102 | ``` 103 | 104 | ### Building the JavaScript Embedding API 105 | 106 | To build the [JavaScript Embedding API](https://webassembly.github.io/spec/js-api/index.html), you will need `bikeshed` as describe in the section [Building the single-page HTML document](#building-the-single-page-html-document): 107 | 108 | ``` 109 | make -C js-api 110 | ``` 111 | 112 | ### Building the Web Embedding API 113 | 114 | To build the [Web Embedding API](https://webassembly.github.io/spec/web-api/index.html), you will need `bikeshed` as describe in the section [Building the single-page HTML document](#building-the-single-page-html-document): 115 | 116 | ``` 117 | make -C web-api 118 | ``` 119 | -------------------------------------------------------------------------------- /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-types.rst: -------------------------------------------------------------------------------- 1 | .. index:: type 2 | .. _index-type: 3 | 4 | Index of Types 5 | -------------- 6 | 7 | ======================================== =========================================== =============================================================================== 8 | Category Constructor Binary Opcode 9 | ======================================== =========================================== =============================================================================== 10 | :ref:`Type index ` :math:`x` (positive number as |Bs32| or |Bu32|) 11 | :ref:`Number type ` |I32| :math:`\hex{7F}` (-1 as |Bs7|) 12 | :ref:`Number type ` |I64| :math:`\hex{7E}` (-2 as |Bs7|) 13 | :ref:`Number type ` |F32| :math:`\hex{7D}` (-3 as |Bs7|) 14 | :ref:`Number type ` |F64| :math:`\hex{7C}` (-4 as |Bs7|) 15 | :ref:`Vector type ` |V128| :math:`\hex{7B}` (-5 as |Bs7|) 16 | (reserved) :math:`\hex{7A}` .. :math:`\hex{71}` 17 | :ref:`Reference type ` |FUNCREF| :math:`\hex{70}` (-16 as |Bs7|) 18 | :ref:`Reference type ` |EXTERNREF| :math:`\hex{6F}` (-17 as |Bs7|) 19 | (reserved) :math:`\hex{6E}` .. :math:`\hex{61}` 20 | :ref:`Function type ` :math:`[\valtype^\ast] \to [\valtype^\ast]` :math:`\hex{60}` (-32 as |Bs7|) 21 | (reserved) :math:`\hex{5F}` .. :math:`\hex{41}` 22 | :ref:`Result type ` :math:`[\epsilon]` :math:`\hex{40}` (-64 as |Bs7|) 23 | :ref:`Table type ` :math:`\limits~\reftype` (none) 24 | :ref:`Memory type ` :math:`\limits` (none) 25 | :ref:`Global type ` :math:`\mut~\valtype` (none) 26 | ======================================== =========================================== =============================================================================== 27 | -------------------------------------------------------------------------------- /document/core/appendix/index.rst: -------------------------------------------------------------------------------- 1 | .. _appendix: 2 | 3 | Appendix 4 | ======== 5 | 6 | .. toctree:: 7 | :maxdepth: 2 8 | 9 | embedding 10 | implementation 11 | algorithm 12 | custom 13 | properties 14 | changes 15 | 16 | .. only:: singlehtml 17 | 18 | .. toctree:: 19 | 20 | index-types 21 | index-instructions 22 | index-rules 23 | 24 | -------------------------------------------------------------------------------- /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 | instructions 13 | modules 14 | -------------------------------------------------------------------------------- /document/core/index.bs: -------------------------------------------------------------------------------- 1 | 15 | 16 |
17 | {
18 |   "WEBASSEMBLY": {
19 |     "href": "https://webassembly.github.io/spec/",
20 |     "title": "WebAssembly Specification",
21 |     "publisher": "W3C WebAssembly Community Group",
22 |     "status": "Draft"
23 |   }
24 | }
25 | 
26 | 27 |
28 | path: _build/bikeshed_singlehtml/index_fixed.html
29 | 
30 | -------------------------------------------------------------------------------- /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 | } 48 | 49 | div.relations { 50 | display: block; 51 | } 52 | 53 | div.sphinxsidebar { 54 | z-index: 1; 55 | background: #FFF; 56 | margin-top: -30px; 57 | font-size: 13px; 58 | width: 200px; 59 | height: 100%; 60 | } 61 | 62 | div.sphinxsidebarwrapper p.logo { 63 | padding: 30px 40px 10px 0px; 64 | } 65 | 66 | div.sphinxsidebar h3 { 67 | font-size: 0px; 68 | } 69 | 70 | div.sphinxsidebar a { 71 | border-bottom: 0px; 72 | } 73 | 74 | div.sphinxsidebar a:hover { 75 | border-bottom: 1px dotted; 76 | } 77 | -------------------------------------------------------------------------------- /document/core/static/webassembly.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WebAssembly/stringref/a64917cd5346f8704e614c4825ebf05737ac5e64/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/bikeshed_fixup.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | # -*- coding: latin-1 -*- 3 | 4 | import os 5 | import sys 6 | 7 | 8 | SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__)) 9 | 10 | 11 | def Main(): 12 | data = open(sys.argv[1]).read() 13 | 14 | # Don't add more than 3 levels to TOC. 15 | data = data.replace('
', '
') 16 | 17 | # TODO(bradnelson/tabatkins): Fix when bikeshed can do letters. 18 | # Don't number the Appendix. 19 | data = data.replace( 20 | '

Appendix

', 21 | '

A Appendix

') 22 | number = 1 23 | for section in [ 24 | 'Embedding', 25 | 'Implementation Limitations', 26 | 'Validation Algorithm', 27 | 'Custom Sections', 28 | 'Soundness', 29 | 'Index of Types', 30 | 'Index of Instructions', 31 | 'Index of Semantic Rules']: 32 | data = data.replace( 33 | '

' + section + '

', 34 | '

A.' + str(number) + ' ' + section + '

') 35 | number += 1 36 | 37 | 38 | # Drop spurious navigation. 39 | data = data.replace( 40 | """ 41 | """, '') 48 | 49 | # Use bikeshed biblio references for unicode and IEEE754 50 | data = data.replace( 51 | """Unicode""", 52 | "[[!UNICODE]]" 53 | ) 54 | 55 | data = data.replace( 56 | """IEEE 754-2019""", 57 | "[[!IEEE-754-2019]]" 58 | ) 59 | 60 | sys.stdout.write(data) 61 | 62 | Main() 63 | -------------------------------------------------------------------------------- /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/mathdefbs.py: -------------------------------------------------------------------------------- 1 | # Version of mathdef.py for bikeshed build. 2 | # Tweaked to generate single page links. 3 | # TODO(bradnelson): Figure out a way to merge this back into 4 | # mathdef.py controlled by buildername. 5 | 6 | from sphinx.directives.patches import MathDirective 7 | from sphinx.ext.mathjax import html_visit_math 8 | from sphinx.ext.mathjax import html_visit_displaymath 9 | from sphinx.writers.html5 import HTML5Translator 10 | from docutils import nodes 11 | from docutils.nodes import math 12 | from docutils.parsers.rst.directives.misc import Replace 13 | import re 14 | 15 | 16 | # Transform \xref in math nodes 17 | 18 | xref_re = re.compile('\\\\xref\{([^}]*)\}\{([^}]*)\}', re.M) 19 | 20 | def html_hyperlink(file, id): 21 | return '\\href{#%s}' % (id.replace('_', '-')) 22 | 23 | def html_transform_math_xref(node): 24 | new_text = xref_re.sub(lambda m: html_hyperlink(m.group(1), m.group(2)), node.astext()) 25 | node.children[0] = nodes.Text(new_text) 26 | 27 | # Expand mathdef names in math roles and directives 28 | 29 | def_re = re.compile('\\\\[A-Za-z][0-9A-Za-z]*', re.M) 30 | 31 | auxcounter = 0 32 | 33 | def lookup_mathdef(defs, name): 34 | if name in defs: 35 | [arity, s] = defs[name] 36 | if arity > 0: 37 | global auxcounter 38 | auxcounter = auxcounter + 1 39 | name = "\\mathdef%d" % auxcounter 40 | s = "\\def%s#%d{%s}%s" % (name, arity, s, name) 41 | return s 42 | return name 43 | 44 | def replace_mathdefs(doc, s): 45 | if not hasattr(doc, 'mathdefs'): 46 | return s 47 | return def_re.sub(lambda m: lookup_mathdef(doc.mathdefs, m.group(0)), s) 48 | 49 | def ext_math_role(role, raw, text, line, inliner, options = {}, content = []): 50 | text = replace_mathdefs(inliner.document, raw.split('`')[1]) 51 | return [math(raw, text)], [] 52 | 53 | class ExtMathDirective(MathDirective): 54 | def run(self): 55 | doc = self.state.document 56 | for i, s in enumerate(self.content): 57 | self.content[i] = replace_mathdefs(doc, s) 58 | for i, s in enumerate(self.arguments): 59 | self.arguments[i] = replace_mathdefs(doc, s) 60 | return super(ExtMathDirective, self).run() 61 | 62 | class MathdefDirective(Replace): 63 | def run(self): 64 | name = '\\' + self.state.parent.rawsource.split('|')[1] 65 | name = name.split('#') 66 | if len(name) > 1: 67 | arity = int(name[1]) 68 | else: 69 | arity = 0 70 | name = name[0] 71 | doc = self.state.document 72 | if not hasattr(doc, 'mathdefs'): 73 | doc.mathdefs = {} 74 | for i, s in enumerate(self.content): 75 | self.content[i] = replace_mathdefs(doc, s) 76 | doc.mathdefs[name] = [arity, ''.join(self.content)] 77 | self.content[0] = ':math:`' + self.content[0] 78 | self.content[-1] = self.content[-1] + '`' 79 | return super(MathdefDirective, self).run() 80 | 81 | class WebAssemblyHTML5Translator(HTML5Translator): 82 | """ 83 | Customize HTML5Translator. 84 | Convert xref in math and math block nodes to hrefs. 85 | """ 86 | def visit_math(self, node, math_env = ''): 87 | html_transform_math_xref(node) 88 | super().visit_math(node, math_env) 89 | 90 | def visit_math_block(self, node, math_env = ''): 91 | html_transform_math_xref(node) 92 | super().visit_math_block(node, math_env) 93 | 94 | # Setup 95 | 96 | def setup(app): 97 | app.set_translator('singlehtml', WebAssemblyHTML5Translator) 98 | app.add_role('math', ext_math_role) 99 | app.add_directive('math', ExtMathDirective, override = True) 100 | app.add_directive('mathdef', MathdefDirective) 101 | -------------------------------------------------------------------------------- /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"(? 2 | 3 | 4 | 5 | 6 | 7 | WebAssembly Specifications 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 |
16 | 17 |

WebAssembly Specifications

18 | 19 |

20 | To support the embedding of WebAssembly into different environments, its specification is split into layers that are specified in separate documents. 21 |

22 | 23 |

Core specification

24 | 25 |

Defines the semantics of WebAssembly modules independent from a concrete embedding. 26 | The WebAssembly core is specified in a single document:

27 | 28 |
    29 |
  • 30 |

    WebAssembly: 31 | defines the structure of WebAssembly modules, their instruction set, and their representation 32 | in binary and text format, as well as the semantics of validation, 33 | instantiation, and execution. 34 |

    35 | 41 |
  • 42 |
43 | 44 |

Embedder specifications

45 | 46 |

Define application programming interfaces (APIs) enabling the use of WebAssembly modules in concrete embedding environments. 47 | Currently, two APIs are specified:

48 | 49 |
    50 |
  • JavaScript Embedding: defines JavaScript classes and objects for accessing WebAssembly from within JavaScript, including methods for validation, compilation, instantiation, and classes for representing and manipulating imports and exports as JavaScript objects.

    51 | 55 |
  • 56 | 57 |
  • Web Embedding: defines extensions to the JavaScript API made available specifically in web browsers, in particular, an interface for streaming compilation and instantiation from origin-bound Response types.

    58 | 62 |
  • 63 |
64 | 65 |

66 | Source for these documents is available 67 | here. 68 |

69 | 70 |
71 |
72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /document/js-api/Makefile: -------------------------------------------------------------------------------- 1 | BUILDDIR = _build 2 | STATICDIR = _static 3 | DOWNLOADDIR = _download 4 | NAME = WebAssembly 5 | 6 | .PHONY: all 7 | all: 8 | mkdir -p $(BUILDDIR)/html 9 | bikeshed spec index.bs $(BUILDDIR)/html/index.html 10 | @echo "Build finished. The HTML pages are in `pwd`/$(BUILDDIR)/html." 11 | 12 | .PHONY: publish 13 | publish: 14 | (cd ..; make publish-js-api) 15 | 16 | .PHONY: clean 17 | clean: 18 | rm -rf $(BUILDDIR) 19 | rm -rf $(STATICDIR) 20 | 21 | .PHONY: diff 22 | diff: all 23 | @echo "Downloading the old single-file html spec..." 24 | curl `grep "^TR" index.bs | cut -d' ' -f2` -o $(BUILDDIR)/html/old.html 25 | @echo "Done." 26 | @echo "Diffing new against old..." 27 | perl ../util/htmldiff.pl $(BUILDDIR)/html/old.html $(BUILDDIR)/html/index.html $(BUILDDIR)/html/diff.html 28 | @echo "Done. The diff is at $(BUILDDIR)/html/diff.html" 29 | 30 | .PHONY: WD-tar 31 | WD-tar: 32 | bikeshed echidna --just-tar index.bs $(BUILDDIR)/html/index.html 33 | mv test.tar $(BUILDDIR)/WD.tar 34 | @echo "Built $(BUILDDIR)/WD.tar." 35 | 36 | .PHONY: WD-echidna 37 | WD-echidna: 38 | @if [ -z $(W3C_USERNAME) ] || \ 39 | [ -z $(W3C_PASSWORD) ] || \ 40 | [ -z $(DECISION_URL) ] ; then \ 41 | echo "Must provide W3C_USERNAME, W3C_PASSWORD, and DECISION_URL environment variables"; \ 42 | exit 1; \ 43 | fi 44 | bikeshed echidna index.bs --u $(W3C_USERNAME) --p $(W3C_PASSWORD) --d $(DECISION_URL) 45 | -------------------------------------------------------------------------------- /document/web-api/Makefile: -------------------------------------------------------------------------------- 1 | BUILDDIR = _build 2 | STATICDIR = _static 3 | DOWNLOADDIR = _download 4 | NAME = WebAssembly 5 | 6 | .PHONY: all 7 | all: 8 | mkdir -p $(BUILDDIR)/html 9 | bikeshed spec index.bs $(BUILDDIR)/html/index.html 10 | @echo "Build finished. The HTML pages are in `pwd`/$(BUILDDIR)/html." 11 | 12 | .PHONY: publish 13 | publish: 14 | (cd ..; make publish-web-api) 15 | 16 | .PHONY: clean 17 | clean: 18 | rm -rf $(BUILDDIR) 19 | rm -rf $(STATICDIR) 20 | 21 | .PHONY: diff 22 | diff: all 23 | @echo "Downloading the old single-file html spec..." 24 | curl `grep "^TR" index.bs | cut -d' ' -f2` -o $(BUILDDIR)/html/old.html 25 | @echo "Done." 26 | @echo "Diffing new against old..." 27 | perl ../util/htmldiff.pl $(BUILDDIR)/html/old.html $(BUILDDIR)/html/index.html $(BUILDDIR)/html/diff.html 28 | @echo "Done. The diff is at $(BUILDDIR)/html/diff.html" 29 | 30 | .PHONY: WD-tar 31 | WD-tar: 32 | bikeshed echidna --just-tar index.bs $(BUILDDIR)/html/index.html 33 | mv test.tar $(BUILDDIR)/WD.tar 34 | @echo "Built $(BUILDDIR)/WD.tar." 35 | 36 | .PHONY: WD-echidna 37 | WD-echidna: 38 | @if [ -z $(W3C_USERNAME) ] || \ 39 | [ -z $(W3C_PASSWORD) ] || \ 40 | [ -z $(DECISION_URL) ] ; then \ 41 | echo "Must provide W3C_USERNAME, W3C_PASSWORD, and DECISION_URL environment variables"; \ 42 | exit 1; \ 43 | fi 44 | bikeshed echidna index.bs --u $(W3C_USERNAME) --p $(W3C_PASSWORD) --d $(DECISION_URL) 45 | -------------------------------------------------------------------------------- /interpreter/.gitignore: -------------------------------------------------------------------------------- 1 | *.cmo 2 | *.cmx 3 | *.native 4 | *.byte 5 | *.opt 6 | *.unopt 7 | *.js 8 | *.zip 9 | *.mlpack 10 | _build 11 | wasm 12 | wasm.debug 13 | 14 | -------------------------------------------------------------------------------- /interpreter/.merlin: -------------------------------------------------------------------------------- 1 | S binary 2 | S exec 3 | S host 4 | S main 5 | S runtime 6 | S script 7 | S syntax 8 | S text 9 | S util 10 | S valid 11 | 12 | B _build/* 13 | -------------------------------------------------------------------------------- /interpreter/binary/decode.mli: -------------------------------------------------------------------------------- 1 | exception Code of Source.region * string 2 | 3 | val decode : string -> 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 | exception Utf8 2 | 3 | let con n = 0x80 lor (n land 0x3f) 4 | 5 | let rec encode ns = Lib.String.implode (List.map Char.chr (encode' ns)) 6 | and encode' = function 7 | | [] -> [] 8 | | n::ns when n < 0 -> 9 | raise Utf8 10 | | n::ns when n < 0x80 -> 11 | n :: encode' ns 12 | | n::ns when n < 0x800 -> 13 | 0xc0 lor (n lsr 6) :: con n :: encode' ns 14 | | n::ns when n < 0x10000 -> 15 | 0xe0 lor (n lsr 12) :: con (n lsr 6) :: con n :: encode' ns 16 | | n::ns when n < 0x110000 -> 17 | 0xf0 lor (n lsr 18) :: con (n lsr 12) :: con (n lsr 6) :: con n 18 | :: encode' ns 19 | | _ -> 20 | raise Utf8 21 | 22 | let con b = if b land 0xc0 = 0x80 then b land 0x3f else raise Utf8 23 | let code min n = 24 | if n < min || (0xd800 <= n && n < 0xe000) || n >= 0x110000 then raise Utf8 25 | else n 26 | 27 | let rec decode s = decode' (List.map Char.code (Lib.String.explode s)) 28 | and decode' = function 29 | | [] -> [] 30 | | b1::bs when b1 < 0x80 -> 31 | code 0x0 b1 :: decode' bs 32 | | b1::bs when b1 < 0xc0 -> 33 | raise Utf8 34 | | b1::b2::bs when b1 < 0xe0 -> 35 | code 0x80 ((b1 land 0x1f) lsl 6 + con b2) :: decode' bs 36 | | b1::b2::b3::bs when b1 < 0xf0 -> 37 | code 0x800 ((b1 land 0x0f) lsl 12 + con b2 lsl 6 + con b3) :: decode' bs 38 | | b1::b2::b3::b4::bs when b1 < 0xf8 -> 39 | code 0x10000 ((b1 land 0x07) lsl 18 + con b2 lsl 12 + con b3 lsl 6 + con b4) 40 | :: decode' bs 41 | | _ -> 42 | raise Utf8 43 | -------------------------------------------------------------------------------- /interpreter/binary/utf8.mli: -------------------------------------------------------------------------------- 1 | exception Utf8 2 | 3 | val decode : string -> int list (* raises Utf8 *) 4 | val encode : int list -> string (* raises Utf8 *) 5 | -------------------------------------------------------------------------------- /interpreter/exec/eval.mli: -------------------------------------------------------------------------------- 1 | open Values 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 Values 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 Values 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.ml: -------------------------------------------------------------------------------- 1 | (* WebAssembly-compatible type conversions to i64 implementation *) 2 | 3 | let extend_i32_s x = Int64.of_int32 x 4 | 5 | let extend_i32_u x = Int64.logand (Int64.of_int32 x) 0x0000_0000_ffff_ffffL 6 | 7 | let trunc_f32_s x = 8 | if F32.ne x x then 9 | raise Ixx.InvalidConversion 10 | else 11 | let xf = F32.to_float x in 12 | if xf >= -.Int64.(to_float min_int) || xf < Int64.(to_float min_int) then 13 | raise Ixx.Overflow 14 | else 15 | Int64.of_float xf 16 | 17 | let trunc_f32_u x = 18 | if F32.ne x x then 19 | raise Ixx.InvalidConversion 20 | else 21 | let xf = F32.to_float x in 22 | if xf >= -.Int64.(to_float min_int) *. 2.0 || xf <= -1.0 then 23 | raise Ixx.Overflow 24 | else if xf >= -.Int64.(to_float min_int) then 25 | Int64.(logxor (of_float (xf -. 0x1p63)) min_int) 26 | else 27 | Int64.of_float xf 28 | 29 | let trunc_f64_s x = 30 | if F64.ne x x then 31 | raise Ixx.InvalidConversion 32 | else 33 | let xf = F64.to_float x in 34 | if xf >= -.Int64.(to_float min_int) || xf < Int64.(to_float min_int) then 35 | raise Ixx.Overflow 36 | else 37 | Int64.of_float xf 38 | 39 | let trunc_f64_u x = 40 | if F64.ne x x then 41 | raise Ixx.InvalidConversion 42 | else 43 | let xf = F64.to_float x in 44 | if xf >= -.Int64.(to_float min_int) *. 2.0 || xf <= -1.0 then 45 | raise Ixx.Overflow 46 | else if xf >= -.Int64.(to_float min_int) then 47 | Int64.(logxor (of_float (xf -. 0x1p63)) min_int) 48 | else 49 | Int64.of_float xf 50 | 51 | let trunc_sat_f32_s x = 52 | if F32.ne x x then 53 | 0L 54 | else 55 | let xf = F32.to_float x in 56 | if xf < Int64.(to_float min_int) then 57 | Int64.min_int 58 | else if xf >= -.Int64.(to_float min_int) then 59 | Int64.max_int 60 | else 61 | Int64.of_float xf 62 | 63 | let trunc_sat_f32_u x = 64 | if F32.ne x x then 65 | 0L 66 | else 67 | let xf = F32.to_float x in 68 | if xf <= -1.0 then 69 | 0L 70 | else if xf >= -.Int64.(to_float min_int) *. 2.0 then 71 | -1L 72 | else if xf >= -.Int64.(to_float min_int) then 73 | Int64.(logxor (of_float (xf -. 9223372036854775808.0)) min_int) 74 | else 75 | Int64.of_float xf 76 | 77 | let trunc_sat_f64_s x = 78 | if F64.ne x x then 79 | 0L 80 | else 81 | let xf = F64.to_float x in 82 | if xf < Int64.(to_float min_int) then 83 | Int64.min_int 84 | else if xf >= -.Int64.(to_float min_int) then 85 | Int64.max_int 86 | else 87 | Int64.of_float xf 88 | 89 | let trunc_sat_f64_u x = 90 | if F64.ne x x then 91 | 0L 92 | else 93 | let xf = F64.to_float x in 94 | if xf <= -1.0 then 95 | 0L 96 | else if xf >= -.Int64.(to_float min_int) *. 2.0 then 97 | -1L 98 | else if xf >= -.Int64.(to_float min_int) then 99 | Int64.(logxor (of_float (xf -. 9223372036854775808.0)) min_int) 100 | else 101 | Int64.of_float xf 102 | 103 | let reinterpret_f64 = F64.to_bits 104 | -------------------------------------------------------------------------------- /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 Values 8 | open Types 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_value_type t ^ 17 | ", got " ^ string_of_value_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 (NumType I32Type) 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 t = 43 | match Utf8.encode name, t with 44 | | "abort", ExternFuncType t -> ExternFunc (Func.alloc_host t abort) 45 | | "exit", ExternFuncType t -> ExternFunc (Func.alloc_host t 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 Values 7 | open Instance 8 | 9 | 10 | let global (GlobalType (t, _) as gt) = 11 | let v = 12 | match t with 13 | | NumType I32Type -> Num (I32 666l) 14 | | NumType I64Type -> Num (I64 666L) 15 | | NumType F32Type -> Num (F32 (F32.of_float 666.6)) 16 | | NumType F64Type -> Num (F64 (F64.of_float 666.6)) 17 | | VecType V128Type -> Vec (V128 (V128.I32x4.of_lanes [666l; 666l; 666l; 666l])) 18 | | RefType t -> Ref (NullRef t) 19 | in Global.alloc gt v 20 | 21 | let table = 22 | Table.alloc (TableType ({min = 10l; max = Some 20l}, FuncRefType)) 23 | (NullRef FuncRefType) 24 | let memory = Memory.alloc (MemoryType {min = 1l; max = Some 2l}) 25 | let func f t = Func.alloc_host t (f t) 26 | 27 | let print_value v = 28 | Printf.printf "%s : %s\n" 29 | (Values.string_of_value v) 30 | (Types.string_of_value_type (Values.type_of_value v)) 31 | 32 | let print (FuncType (_, out)) vs = 33 | List.iter print_value vs; 34 | flush_all (); 35 | List.map default_value out 36 | 37 | 38 | let lookup name t = 39 | match Utf8.encode name, t with 40 | | "print", _ -> ExternFunc (func print (FuncType ([], []))) 41 | | "print_i32", _ -> ExternFunc (func print (FuncType ([NumType I32Type], []))) 42 | | "print_i64", _ -> ExternFunc (func print (FuncType ([NumType I64Type], []))) 43 | | "print_f32", _ -> ExternFunc (func print (FuncType ([NumType F32Type], []))) 44 | | "print_f64", _ -> ExternFunc (func print (FuncType ([NumType F64Type], []))) 45 | | "print_i32_f32", _ -> 46 | ExternFunc (func print (FuncType ([NumType I32Type; NumType F32Type], []))) 47 | | "print_f64_f64", _ -> 48 | ExternFunc (func print (FuncType ([NumType F64Type; NumType F64Type], []))) 49 | | "global_i32", _ -> ExternGlobal (global (GlobalType (NumType I32Type, Immutable))) 50 | | "global_i64", _ -> ExternGlobal (global (GlobalType (NumType I64Type, Immutable))) 51 | | "global_f32", _ -> ExternGlobal (global (GlobalType (NumType F32Type, Immutable))) 52 | | "global_f64", _ -> ExternGlobal (global (GlobalType (NumType F64Type, Immutable))) 53 | | "table", _ -> ExternTable table 54 | | "memory", _ -> ExternMemory memory 55 | | _ -> raise Not_found 56 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /interpreter/main/main.ml: -------------------------------------------------------------------------------- 1 | let name = "wasm" 2 | let version = "2.0" 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 | "-w", Arg.Int (fun n -> Flags.width := n), 28 | " configure output width (default is 80)"; 29 | "-s", Arg.Set Flags.print_sig, " show module signatures"; 30 | "-u", Arg.Set Flags.unchecked, " unchecked, do not perform validation"; 31 | "-h", Arg.Clear Flags.harness, " exclude harness for JS conversion"; 32 | "-d", Arg.Set Flags.dry, " dry, do not run program"; 33 | "-t", Arg.Set Flags.trace, " trace execution"; 34 | "-v", Arg.Unit banner, " show version" 35 | ] 36 | 37 | let () = 38 | Printexc.record_backtrace true; 39 | try 40 | configure (); 41 | Arg.parse argspec 42 | (fun file -> add_arg ("(input " ^ quote file ^ ")")) usage; 43 | List.iter (fun arg -> if not (Run.run_string arg) then exit 1) !args; 44 | if !args = [] then Flags.interactive := true; 45 | if !Flags.interactive then begin 46 | Flags.print_sig := true; 47 | banner (); 48 | Run.run_stdin () 49 | end 50 | with exn -> 51 | flush_all (); 52 | prerr_endline 53 | (Sys.argv.(0) ^ ": uncaught exception " ^ Printexc.to_string exn); 54 | Printexc.print_backtrace stderr; 55 | exit 2 56 | -------------------------------------------------------------------------------- /interpreter/meta/findlib/META: -------------------------------------------------------------------------------- 1 | description = "A library for writing/reading/running WebAssembly binaries" 2 | requires = "bigarray,str" 3 | archive(byte) = "wasm.cmo" 4 | archive(native) = "wasm.cmx" 5 | -------------------------------------------------------------------------------- /interpreter/meta/jslib/bsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wasm", 3 | "sources": [ 4 | {"dir": "src"}, 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /interpreter/meta/jslib/build.sh: -------------------------------------------------------------------------------- 1 | link () { 2 | echo "// DO NOT EDIT. Generated from WebAssembly spec interpreter" 3 | echo " 4 | let WebAssemblyText = (function() { 5 |   let _registry = {__proto__: null}; 6 | function normalize(file) { 7 | return file.split('/').reverse()[0].split('.')[0]; 8 | } 9 |   function require(file) { 10 |     let name = normalize(file); 11 | if (!(name in _registry)) { 12 | throw new Error('missing module: ' + name) 13 |     } else if (typeof _registry[name] === 'function') { 14 | " 15 | if (($LOG == 1)) 16 | then 17 | echo 1>&2 Logging on 18 | echo " 19 | console.log(name); 20 | " 21 | fi 22 | echo " 23 |       let f = _registry[name]; 24 |       _registry[name] = function() { throw new Error('cyclic module: ' + name) }; 25 |       _registry[name] = f(); 26 |     } 27 |     return _registry[name]; 28 |   } 29 | " 30 | 31 | for file in $* 32 | do 33 | echo 1>&2 Including $file 34 | name=`basename $file | sed s/[.]js//g` 35 | echo " 36 | _registry['$name'] = function() { 37 |     let exports = {}; 38 | //////// start of $name.js ////////" 39 | cat $file 40 | echo "//////// end of $name.js //////// 41 |     return exports; 42 |   }; 43 | " 44 | done 45 | 46 | echo " 47 | function binary(bytes) { 48 | let buffer = new ArrayBuffer(bytes.length); 49 | let view = new Uint8Array(buffer); 50 | for (let i = 0; i < bytes.length; ++i) { 51 | view[i] = bytes.charCodeAt(i); 52 | } 53 | return buffer; 54 | } 55 | function bytes(buffer) { 56 | let string = ''; 57 | let view = new Uint8Array(buffer); 58 | for (let i = 0; i < view.length; ++i) { 59 | string += String.fromCodePoint(view[i]); 60 | } 61 | return string; 62 | } 63 |   let Wasm = require('wasm'); 64 | return { 65 | encode(s) { return binary(Wasm.encode(s)) }, 66 | decode(b, w = 80) { return Wasm.decode(bytes(b), w) } 67 | }; 68 | })(); 69 | 70 | " 71 | } 72 | 73 | echo 1>&2 ==== Preparing ==== 74 | npm link bs-platform 75 | 76 | echo 1>&2 ==== Compiling ==== 77 | bsb || exit 1 78 | 79 | echo 1>&2 ==== Linking full version ==== 80 | LOG=1 81 | FILES='node_modules/bs-platform/lib/js/*.js lib/js/src/*.js' 82 | link $FILES >temp.js || exit 1 83 | 84 | echo 1>&2 ==== Running for dependencies ==== 85 | node temp.js | tee temp.log || exit 1 86 | 87 | echo 1>&2 ==== Linking stripped version ==== 88 | used='' 89 | for file in `ls $FILES` 90 | do 91 | if grep -q `basename $file | sed s/.js//g` temp.log 92 | then 93 | used="$used $file" 94 | fi 95 | done 96 | LOG=0 97 | link $used >$1 || exit 1 98 | -------------------------------------------------------------------------------- /interpreter/meta/jslib/wasm.ml: -------------------------------------------------------------------------------- 1 | let encode s = 2 | let def = Parse.string_to_module s in 3 | match def.Source.it with 4 | | Script.Textual m -> Encode.encode m 5 | | Script.Encoded (_, bs) -> bs 6 | 7 | let decode s width = 8 | let m = Decode.decode "(decode)" s in 9 | Sexpr.to_string width (Arrange.module_ m) 10 | -------------------------------------------------------------------------------- /interpreter/runtime/func.ml: -------------------------------------------------------------------------------- 1 | open Types 2 | open Values 3 | 4 | type 'inst t = 'inst func 5 | and 'inst func = 6 | | AstFunc of func_type * 'inst * Ast.func 7 | | HostFunc of func_type * (value list -> value list) 8 | 9 | let alloc ft inst f = AstFunc (ft, inst, f) 10 | let alloc_host ft f = HostFunc (ft, f) 11 | 12 | let type_of = function 13 | | AstFunc (ft, _, _) -> ft 14 | | HostFunc (ft, _) -> ft 15 | -------------------------------------------------------------------------------- /interpreter/runtime/func.mli: -------------------------------------------------------------------------------- 1 | open Types 2 | open Values 3 | 4 | type 'inst t = 'inst func 5 | and 'inst func = 6 | | AstFunc of func_type * 'inst * Ast.func 7 | | HostFunc of func_type * (value list -> value list) 8 | 9 | val alloc : func_type -> 'inst -> Ast.func -> 'inst func 10 | val alloc_host : func_type -> (value list -> value list) -> 'inst func 11 | val type_of : 'inst func -> func_type 12 | -------------------------------------------------------------------------------- /interpreter/runtime/global.ml: -------------------------------------------------------------------------------- 1 | open Types 2 | open Values 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 (GlobalType (t, _) as ty) v = 11 | if type_of_value v <> t then raise Type; 12 | {ty; content = v} 13 | 14 | let type_of glob = 15 | glob.ty 16 | 17 | let load glob = 18 | glob.content 19 | 20 | let store glob v = 21 | let GlobalType (t, mut) = glob.ty in 22 | if mut <> Mutable then raise NotMutable; 23 | if type_of_value v <> t then raise Type; 24 | glob.content <- v 25 | -------------------------------------------------------------------------------- /interpreter/runtime/global.mli: -------------------------------------------------------------------------------- 1 | open Types 2 | open Values 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/instance.ml: -------------------------------------------------------------------------------- 1 | open Types 2 | 3 | type module_inst = 4 | { 5 | types : func_type list; 6 | funcs : func_inst list; 7 | tables : table_inst list; 8 | memories : memory_inst list; 9 | globals : global_inst list; 10 | exports : export_inst list; 11 | elems : elem_inst list; 12 | datas : data_inst list; 13 | } 14 | 15 | and func_inst = module_inst ref Func.t 16 | and table_inst = Table.t 17 | and memory_inst = Memory.t 18 | and global_inst = Global.t 19 | and export_inst = Ast.name * extern 20 | and elem_inst = Values.ref_ list ref 21 | and data_inst = string ref 22 | 23 | and extern = 24 | | ExternFunc of func_inst 25 | | ExternTable of table_inst 26 | | ExternMemory of memory_inst 27 | | ExternGlobal of global_inst 28 | 29 | 30 | (* Reference types *) 31 | 32 | type Values.ref_ += FuncRef of func_inst 33 | 34 | let () = 35 | let type_of_ref' = !Values.type_of_ref' in 36 | Values.type_of_ref' := function 37 | | FuncRef _ -> FuncRefType 38 | | r -> type_of_ref' r 39 | 40 | let () = 41 | let string_of_ref' = !Values.string_of_ref' in 42 | Values.string_of_ref' := function 43 | | FuncRef _ -> "func" 44 | | r -> string_of_ref' r 45 | 46 | let () = 47 | let eq_ref' = !Values.eq_ref' in 48 | Values.eq_ref' := fun r1 r2 -> 49 | match r1, r2 with 50 | | FuncRef f1, FuncRef f2 -> f1 == f2 51 | | _, _ -> eq_ref' r1 r2 52 | 53 | 54 | (* Auxiliary functions *) 55 | 56 | let empty_module_inst = 57 | { types = []; funcs = []; tables = []; memories = []; globals = []; 58 | exports = []; elems = []; datas = [] } 59 | 60 | let extern_type_of = function 61 | | ExternFunc func -> ExternFuncType (Func.type_of func) 62 | | ExternTable tab -> ExternTableType (Table.type_of tab) 63 | | ExternMemory mem -> ExternMemoryType (Memory.type_of mem) 64 | | ExternGlobal glob -> ExternGlobalType (Global.type_of glob) 65 | 66 | let export inst name = 67 | try Some (List.assoc name inst.exports) with Not_found -> None 68 | -------------------------------------------------------------------------------- /interpreter/runtime/memory.mli: -------------------------------------------------------------------------------- 1 | open Types 2 | open Values 3 | 4 | type memory 5 | type t = memory 6 | 7 | type size = int32 (* number of pages *) 8 | type address = int64 9 | type offset = int32 10 | type count = int32 11 | 12 | exception Type 13 | exception Bounds 14 | exception SizeOverflow 15 | exception SizeLimit 16 | exception OutOfMemory 17 | 18 | val page_size : int64 19 | 20 | val alloc : memory_type -> memory (* raises Type, SizeOverflow, OutOfMemory *) 21 | val type_of : memory -> memory_type 22 | val size : memory -> size 23 | val bound : memory -> address 24 | val grow : memory -> size -> unit 25 | (* raises SizeLimit, SizeOverflow, OutOfMemory *) 26 | 27 | val load_byte : memory -> address -> int (* raises Bounds *) 28 | val store_byte : memory -> address -> int -> unit (* raises Bounds *) 29 | val load_bytes : memory -> address -> int -> string (* raises Bounds *) 30 | val store_bytes : memory -> address -> string -> unit (* raises Bounds *) 31 | 32 | val load_num : 33 | memory -> address -> offset -> num_type -> num (* raises Bounds *) 34 | val store_num : 35 | memory -> address -> offset -> num -> unit (* raises Bounds *) 36 | val load_num_packed : 37 | pack_size -> extension -> memory -> address -> offset -> num_type -> num 38 | (* raises Type, Bounds *) 39 | val store_num_packed : 40 | pack_size -> memory -> address -> offset -> num -> unit 41 | (* raises Type, Bounds *) 42 | 43 | val load_vec : 44 | memory -> address -> offset -> vec_type -> vec (* raises Bounds *) 45 | val store_vec : 46 | memory -> address -> offset -> vec -> unit 47 | (* raises Type, Bounds *) 48 | val load_vec_packed : 49 | pack_size -> vec_extension -> memory -> address -> offset -> vec_type -> vec 50 | (* raises Type, Bounds *) 51 | -------------------------------------------------------------------------------- /interpreter/runtime/table.ml: -------------------------------------------------------------------------------- 1 | open Types 2 | open Values 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 (TableType (lim, _) as ty) r = 27 | if not (valid_limits lim) then raise Type; 28 | {ty; content = create lim.min r} 29 | 30 | let size tab = 31 | Lib.Array32.length tab.content 32 | 33 | let type_of tab = 34 | tab.ty 35 | 36 | let grow tab delta r = 37 | let TableType (lim, t) = tab.ty in 38 | assert (lim.min = size tab); 39 | let old_size = lim.min in 40 | let new_size = Int32.add old_size delta in 41 | if I32.gt_u old_size new_size then raise SizeOverflow else 42 | let lim' = {lim with min = new_size} in 43 | if not (valid_limits lim') then raise SizeLimit else 44 | let after = create new_size r in 45 | Array.blit tab.content 0 after 0 (Array.length tab.content); 46 | tab.ty <- TableType (lim', t); 47 | tab.content <- after 48 | 49 | let load tab i = 50 | try Lib.Array32.get tab.content i with Invalid_argument _ -> raise Bounds 51 | 52 | let store tab i r = 53 | let TableType (lim, t) = tab.ty in 54 | if type_of_ref r <> t then raise Type; 55 | try Lib.Array32.set tab.content i r with Invalid_argument _ -> raise Bounds 56 | 57 | let blit tab offset rs = 58 | let data = Array.of_list rs in 59 | try Lib.Array32.blit data 0l tab.content offset (Lib.Array32.length data) 60 | with Invalid_argument _ -> raise Bounds 61 | -------------------------------------------------------------------------------- /interpreter/runtime/table.mli: -------------------------------------------------------------------------------- 1 | open Types 2 | open Values 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 | 4 | module Unknown = Error.Make () 5 | exception Unknown = Unknown.Error (* indicates unknown import name *) 6 | 7 | module Registry = Map.Make(struct type t = Ast.name let compare = compare end) 8 | let registry = ref Registry.empty 9 | 10 | let register name lookup = registry := Registry.add name lookup !registry 11 | 12 | let lookup (m : module_) (im : import) : Instance.extern = 13 | let {module_name; item_name; idesc} = im.it in 14 | let t = import_type m im in 15 | try Registry.find module_name !registry item_name t with Not_found -> 16 | Unknown.error im.at 17 | ("unknown import \"" ^ string_of_name module_name ^ 18 | "\".\"" ^ string_of_name item_name ^ "\"") 19 | 20 | let link m = List.map (lookup m) m.it.imports 21 | -------------------------------------------------------------------------------- /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 Values.ref_ += ExternRef of int32 4 | type num = Values.num Source.phrase 5 | type ref_ = Values.ref_ Source.phrase 6 | type literal = Values.value 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) Values.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) Values.vecop 29 | 30 | type ref_pat = 31 | | RefPat of ref_ 32 | | RefTypePat of Types.ref_type 33 | 34 | type result = result' Source.phrase 35 | and result' = 36 | | NumResult of num_pat 37 | | VecResult of vec_pat 38 | | RefResult of ref_pat 39 | 40 | type assertion = assertion' Source.phrase 41 | and assertion' = 42 | | AssertMalformed of definition * string 43 | | AssertInvalid of definition * string 44 | | AssertUnlinkable of definition * string 45 | | AssertUninstantiable of definition * string 46 | | AssertReturn of action * result list 47 | | AssertTrap of action * string 48 | | AssertExhaustion of action * string 49 | 50 | type command = command' Source.phrase 51 | and command' = 52 | | Module of var option * definition 53 | | Register of Ast.name * var option 54 | | Action of action 55 | | Assertion of assertion 56 | | Meta of meta 57 | 58 | and meta = meta' Source.phrase 59 | and meta' = 60 | | Input of var option * string 61 | | Output of var option * string option 62 | | Script of var option * script 63 | 64 | and script = command list 65 | 66 | exception Syntax of Source.region * string 67 | 68 | 69 | let () = 70 | let type_of_ref' = !Values.type_of_ref' in 71 | Values.type_of_ref' := function 72 | | ExternRef _ -> Types.ExternRefType 73 | | r -> type_of_ref' r 74 | 75 | let () = 76 | let string_of_ref' = !Values.string_of_ref' in 77 | Values.string_of_ref' := function 78 | | ExternRef n -> "ref " ^ Int32.to_string n 79 | | r -> string_of_ref' r 80 | 81 | let () = 82 | let eq_ref' = !Values.eq_ref' in 83 | Values.eq_ref' := fun r1 r2 -> 84 | match r1, r2 with 85 | | ExternRef n1, ExternRef n2 -> n1 = n2 86 | | _, _ -> eq_ref' r1 r2 87 | -------------------------------------------------------------------------------- /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 instr : Ast.instr -> t 20 | val block : Ast.instr list -> t 21 | val const : Ast.const -> t 22 | 23 | val type_ : Ast.type_ -> t 24 | val global : Ast.global -> t 25 | val func : Ast.func -> t 26 | val table : Ast.table -> t 27 | val memory : Ast.memory -> t 28 | val elem : Ast.elem_segment -> t 29 | val data : Ast.data_segment -> t 30 | val export : Ast.export -> t 31 | val import : Ast.import -> t 32 | val start : Ast.start -> t 33 | 34 | val module_ : Ast.module_ -> t 35 | 36 | val list : ('a -> t) -> 'a list -> t 37 | -------------------------------------------------------------------------------- /interpreter/tests/smallint.ml: -------------------------------------------------------------------------------- 1 | (* Simple, non-exhaustive tests for small ints (i8, i16). *) 2 | 3 | let s32max = 0x7fffffffl 4 | let s32min = 0x80000000l 5 | let u32max = 0xffffffffl 6 | let u32min = 0l 7 | 8 | (* Smaller ints are stored sign extended in an Int32. *) 9 | let s16max = 32767l 10 | let s16min = -32768l 11 | let u16max = u32max 12 | let u16min = 0l 13 | 14 | let s8max = 127l 15 | let s8min = -128l 16 | let u8max = u32max 17 | let u8min = 0l 18 | 19 | let assert_equal x y = 20 | if x <> y then raise (Failure 21 | (Printf.sprintf "Expected: %ld, but got %ld." x y)) 22 | 23 | let () = 24 | (* test addition wrap around *) 25 | assert_equal u32min (I32.add u32max 1l); 26 | assert_equal u16min (I16.add u16max 1l); 27 | assert_equal u8min (I8.add u8max 1l); 28 | assert_equal s32min (I32.add s32max 1l); 29 | assert_equal s16min (I16.add s16max 1l); 30 | assert_equal s8min (I8.add s8max 1l); 31 | 32 | (* test subtraction wrap around *) 33 | assert_equal u32max (I32.sub u32min 1l); 34 | assert_equal u16max (I16.sub u16min 1l); 35 | assert_equal u8max (I8.sub u8min 1l); 36 | assert_equal s32max (I32.sub s32min 1l); 37 | assert_equal s16max (I16.sub s16min 1l); 38 | assert_equal s8max (I8.sub s8min 1l); 39 | 40 | (* test mul wrap around *) 41 | assert_equal 1l (I32.mul u32max u32max); 42 | assert_equal 1l (I16.mul u16max u16max); 43 | assert_equal 1l (I8.mul u8max u8max); 44 | assert_equal 1l (I32.mul s32max s32max); 45 | assert_equal 1l (I16.mul s16max s16max); 46 | assert_equal 1l (I8.mul s8max s8max); 47 | 48 | (* test add_sat_s *) 49 | assert_equal s16max (I16.add_sat_s s16max 1l); 50 | assert_equal s8max (I8.add_sat_s s8max 1l); 51 | assert_equal u16max (I16.add_sat_u u16max 1l); 52 | assert_equal u8max (I8.add_sat_u u8max 1l); 53 | 54 | (* test sub_sat_s *) 55 | assert_equal s16min (I16.sub_sat_s s16min 1l); 56 | assert_equal s8min (I8.sub_sat_s s8min 1l); 57 | assert_equal 0l (I16.sub_sat_u 0l 1l); 58 | assert_equal 0l (I8.sub_sat_u 0l 1l); 59 | 60 | (* test div wrap around *) 61 | try 62 | ignore (I32.div_s s32min (-1l)); 63 | ignore (I16.div_s s16min (-1l)); 64 | ignore (I8.div_s s8min (-1l)); 65 | assert false 66 | with Ixx.Overflow -> 67 | (); 68 | 69 | (* test shifts overflow *) 70 | assert_equal s16min (I16.shl 16384l 1l); 71 | assert_equal s8min (I8.shl 64l 1l); 72 | assert_equal 0x7fffl (I16.shr_u u16max 1l); 73 | assert_equal 0x7fl (I8.shr_u u8max 1l); 74 | (* check that the top bits are not messed with *) 75 | assert_equal u16max (I16.shr_u u16max 0l); 76 | assert_equal u8max (I8.shr_u u8max 0l); 77 | 78 | (* check rotation *) 79 | assert_equal 1l (I16.rotl s16min 1l); 80 | assert_equal 1l (I8.rotl s8min 1l); 81 | assert_equal s16min (I16.rotl 0x4000l 1l); 82 | assert_equal s8min (I8.rotl 0x40l 1l); 83 | 84 | assert_equal s32min (I32.rotr 1l 1l); 85 | assert_equal s16min (I16.rotr 1l 1l); 86 | assert_equal s8min (I8.rotr 1l 1l); 87 | 88 | assert_equal 1l (I32.rotr s32min 31l); 89 | assert_equal 1l (I16.rotr s16min 15l); 90 | assert_equal 1l (I8.rotr s8min 7l); 91 | assert_equal 0x40000000l (I32.rotr s32min 1l); 92 | assert_equal 0x4000l (I16.rotr s16min 1l); 93 | assert_equal 0x40l (I8.rotr s8min 1l); 94 | 95 | (* check clz *) 96 | assert_equal 0l (I16.clz s16min); 97 | assert_equal 0l (I8.clz s8min); 98 | assert_equal 1l (I16.clz s16max); 99 | assert_equal 1l (I8.clz s8max); 100 | 101 | (* check popcnt *) 102 | assert_equal 1l (I32.popcnt s32min); 103 | assert_equal 1l (I16.popcnt s16min); 104 | assert_equal 1l (I8.popcnt s8min); 105 | assert_equal 16l (I16.popcnt (-1l)); 106 | assert_equal 8l (I8.popcnt (-1l)); 107 | assert_equal 15l (I16.popcnt s16max); 108 | assert_equal 7l (I8.popcnt s8max); 109 | -------------------------------------------------------------------------------- /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 | type 'a start = 2 | | Module : (Script.var option * Script.definition) start 3 | | Script : Script.script start 4 | | Script1 : Script.script start 5 | 6 | exception Syntax = Script.Syntax 7 | 8 | let parse' name lexbuf start = 9 | lexbuf.Lexing.lex_curr_p <- 10 | {lexbuf.Lexing.lex_curr_p with Lexing.pos_fname = name}; 11 | try start Lexer.token lexbuf 12 | with Syntax (region, s) -> 13 | let region' = if region <> Source.no_region then region else 14 | {Source.left = Lexer.convert_pos lexbuf.Lexing.lex_start_p; 15 | Source.right = Lexer.convert_pos lexbuf.Lexing.lex_curr_p} in 16 | raise (Syntax (region', s)) 17 | 18 | let parse (type a) name lexbuf : a start -> a = function 19 | | Module -> parse' name lexbuf Parser.module1 20 | | Script -> parse' name lexbuf Parser.script 21 | | Script1 -> parse' name lexbuf Parser.script1 22 | 23 | let string_to start s = 24 | let lexbuf = Lexing.from_string s in 25 | parse "string" lexbuf start 26 | 27 | let string_to_script s = string_to Script s 28 | let string_to_module s = snd (string_to Module s) 29 | -------------------------------------------------------------------------------- /interpreter/text/parse.mli: -------------------------------------------------------------------------------- 1 | type 'a start = 2 | | Module : (Script.var option * Script.definition) start 3 | | Script : Script.script start 4 | | Script1 : Script.script start 5 | 6 | exception Syntax of Source.region * string 7 | 8 | val parse : string -> Lexing.lexbuf -> 'a start -> 'a (* raises Syntax *) 9 | 10 | val string_to_script : string -> Script.script (* raises Syntax *) 11 | val string_to_module : string -> Script.definition (* raises Syntax *) 12 | -------------------------------------------------------------------------------- /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/lib.mli: -------------------------------------------------------------------------------- 1 | (* Things that should be in the OCaml library... *) 2 | 3 | type void = | 4 | 5 | module Fun : 6 | sig 7 | val id : 'a -> 'a 8 | val curry : ('a * 'b -> 'c) -> ('a -> 'b -> 'c) 9 | val uncurry : ('a -> 'b -> 'c) -> ('a * 'b -> 'c) 10 | 11 | val repeat : int -> ('a -> unit) -> 'a -> unit 12 | end 13 | 14 | module List : 15 | sig 16 | val make : int -> 'a -> 'a list 17 | val table : int -> (int -> 'a) -> 'a list 18 | val take : int -> 'a list -> 'a list (* raises Failure *) 19 | val drop : int -> 'a list -> 'a list (* raises Failure *) 20 | 21 | val last : 'a list -> 'a (* raises Failure *) 22 | val split_last : 'a list -> 'a list * 'a (* raises Failure *) 23 | 24 | val index_of : 'a -> 'a list -> int option 25 | val index_where : ('a -> bool) -> 'a list -> int option 26 | val map_filter : ('a -> 'b option) -> 'a list -> 'b list 27 | val concat_map : ('a -> 'b list) -> 'a list -> 'b list 28 | val pairwise : ('a -> 'a -> 'b) -> 'a list -> 'b list 29 | end 30 | 31 | module List32 : 32 | sig 33 | val make : int32 -> 'a -> 'a list 34 | val length : 'a list -> int32 35 | val nth : 'a list -> int32 -> 'a (* raises Failure *) 36 | val take : int32 -> 'a list -> 'a list (* raises Failure *) 37 | val drop : int32 -> 'a list -> 'a list (* raises Failure *) 38 | val mapi : (int32 -> 'a -> 'b) -> 'a list -> 'b list 39 | end 40 | 41 | module Array32 : 42 | sig 43 | val make : int32 -> 'a -> 'a array 44 | val length : 'a array -> int32 45 | val get : 'a array -> int32 -> 'a 46 | val set : 'a array -> int32 -> 'a -> unit 47 | val blit : 'a array -> int32 -> 'a array -> int32 -> int32 -> unit 48 | end 49 | 50 | module Bigarray : 51 | sig 52 | open Bigarray 53 | 54 | module Array1_64 : 55 | sig 56 | val create : ('a, 'b) kind -> 'c layout -> int64 -> ('a, 'b, 'c) Array1.t 57 | val dim : ('a, 'b, 'c) Array1.t -> int64 58 | val get : ('a, 'b, 'c) Array1.t -> int64 -> 'a 59 | val set : ('a, 'b, 'c) Array1.t -> int64 -> 'a -> unit 60 | val sub : ('a, 'b, 'c) Array1.t -> int64 -> int64 -> ('a, 'b, 'c) Array1.t 61 | end 62 | end 63 | 64 | module Option : 65 | sig 66 | val get : 'a option -> 'a -> 'a 67 | val force : 'a option -> 'a (* raises Invalid_argument *) 68 | val map : ('a -> 'b) -> 'a option -> 'b option 69 | val app : ('a -> unit) -> 'a option -> unit 70 | end 71 | 72 | module Int : 73 | sig 74 | val log2 : int -> int 75 | val is_power_of_two : int -> bool 76 | end 77 | 78 | module String : 79 | sig 80 | val implode : char list -> string 81 | val explode : string -> char list 82 | val split : string -> char -> string list 83 | val breakup : string -> int -> string list 84 | val find_from_opt : (char -> bool) -> string -> int -> int option 85 | end 86 | -------------------------------------------------------------------------------- /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 at region x = x @@ region 7 | 8 | 9 | (* Positions and regions *) 10 | 11 | let no_pos = {file = ""; line = 0; column = 0} 12 | let no_region = {left = no_pos; right = no_pos} 13 | 14 | let string_of_pos pos = 15 | if pos.line = -1 then 16 | Printf.sprintf "0x%x" pos.column 17 | else 18 | string_of_int pos.line ^ "." ^ string_of_int (pos.column + 1) 19 | 20 | let string_of_region r = 21 | r.left.file ^ ":" ^ string_of_pos r.left ^ 22 | (if r.right = r.left then "" else "-" ^ string_of_pos r.right) 23 | -------------------------------------------------------------------------------- /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 at : region -> 'a -> 'a phrase 13 | -------------------------------------------------------------------------------- /interpreter/valid/valid.mli: -------------------------------------------------------------------------------- 1 | exception Invalid of Source.region * string 2 | 3 | val check_module : Ast.module_ -> unit (* raises Invalid *) 4 | -------------------------------------------------------------------------------- /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/stringref/a64917cd5346f8704e614c4825ebf05737ac5e64/papers/oopsla2019.pdf -------------------------------------------------------------------------------- /papers/pldi2017.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WebAssembly/stringref/a64917cd5346f8704e614c4825ebf05737ac5e64/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/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/W3CTAG-SIMDExplainer.md: -------------------------------------------------------------------------------- 1 | # WASM SIMD Web Platform explainer 2 | 3 | ## User benefits 4 | Modern processors support running multiple common computations in a single operation, so that instead of multiplying a number with a number, you can add (or multiply, subtract, etc.) the individual components of a fixed sized vector instead. 5 | 6 | ##### Scalar operation ##### 7 | 8 | ``` 9 | Ax + Bx = Cx 10 | Ay + By = Cy 11 | Az + Bz = Cz 12 | Aw + Bw = Cw 13 | ``` 14 | 15 | ##### SIMD Operation of Vector Length 4 ##### 16 | 17 | `Ax`|`Ay`|`Az`|`Aw` 18 | --|--|--|-- 19 | 20 |
21 |             +
22 | 
23 | 24 | `Bx`|`By`|`Bz`|`Bw` 25 | --|--|--|-- 26 | 27 |
28 |             =
29 | 
30 | 31 | `Cx`|`Cy`|`Cz`|`Cw` 32 | --|--|--|-- 33 | 34 | 35 | 36 | 37 | 38 | 39 | This feature is called [Single Instruction Multiple Data (SIMD)](https://en.wikipedia.org/wiki/SIMD), and though hardware support exists for vector lengths up to 512 bits, 128-bit SIMD is the most common and is supported across common hardware architectures. 40 | 41 | SIMD has driven large speed ups in certain cases such as image manipulation, video encoding/decoding, machine learning, game engines and physics engines etc - with some of these use-cases not being usable without SIMD support, making SIMD support for the web platform essential for achieving near-native speed with certain native applications. 42 | 43 | This proposal outlines exposing a commonly available subset of 128-bit SIMD hardware instructions through WebAssembly. 44 | 45 | ## Design principles 46 | This proposal consists of a portable set of widely used SIMD operations mapping closely to instructions available in modern hardware. The proposal draws heavily on inputs from application developers on the usefulness of the instructions and implementer feedback on performance. 47 | 48 | JavaScript applications can access the SIMD values in WebAssembly module memory indirectly as scalar values through Arraybuffers, and manipulate them using function calls into WebAssembly. The 128-bit values are not currently exposed to JavaScript. There are no known accessibility, security or privacy implications specific to this feature. 49 | 50 | ## Prior work 51 | The current proposal builds on top of the [SIMD.js TC39 proposal](https://github.com/tc39/ecmascript_simd), which is no longer under active development. The SIMD.js proposal was abandoned for a few reasons: 52 | 53 | * Significant performance cliffs hidden within its high level abstractions making it challenging for real world applications to gain consistent benefits. 54 | * Gains only seen in carefully crafted asm.js code, which is not representative of the majority of JavaScript code in the wild. 55 | * High cost of implementation and optimization in engines that outweighed performance wins. 56 | 57 | Most of these were offset by the low level abstractions in WebAssembly, where we observed consistent performance across multiple architectures on real world applications. 58 | 59 | ## References 60 | [1] GitHub repo: https://github.com/WebAssembly/simd 61 | 62 | [2] Proposal directory: https://github.com/WebAssembly/simd/tree/master/proposals/simd 63 | 64 | [3] Example usage and demos: https://v8.dev/features/simd 65 | 66 | [4] Tests: https://github.com/WebAssembly/simd/tree/master/test/core/simd 67 | 68 | [5] External status/issue trackers for this feature: https://www.chromestatus.com/feature/6533147810332672 69 | 70 | [6] SIMD.js: https://github.com/tc39/ecmascript_simd 71 | 72 | [7] W3C Tag design reviews - WebAssembly SIMD : https://github.com/w3ctag/design-reviews/issues/487 73 | -------------------------------------------------------------------------------- /proposals/simd/WebAssembly-SIMD-May-2017.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WebAssembly/stringref/a64917cd5346f8704e614c4825ebf05737ac5e64/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/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 | ;; null-byte followed immediately by end-of-comment delimiter 43 | (;;) 44 | 45 | 46 | (;Heiße Würstchen;) 47 | 48 | (;;) 49 | 50 | (;comment 51 | comment;) 52 | 53 | (;comment;) 54 | 55 | (;comment;)((;comment;) 56 | (;comment;)module(;comment;) 57 | (;comment;))(;comment;) 58 | 59 | (;comment(;nested;)comment;) 60 | 61 | (;comment 62 | (;nested 63 | ;)comment 64 | ;) 65 | 66 | (module 67 | (;comment(;nested(;further;)nested;)comment;) 68 | ) 69 | 70 | (;comment;;comment;) 71 | 72 | (;comment;;comment 73 | ;) 74 | 75 | (module 76 | (;comment;;comment(;nested;)comment;) 77 | ) 78 | -------------------------------------------------------------------------------- /test/core/fac.wast: -------------------------------------------------------------------------------- 1 | (module 2 | ;; Recursive factorial 3 | (func (export "fac-rec") (param i64) (result i64) 4 | (if (result i64) (i64.eq (local.get 0) (i64.const 0)) 5 | (then (i64.const 1)) 6 | (else 7 | (i64.mul (local.get 0) (call 0 (i64.sub (local.get 0) (i64.const 1)))) 8 | ) 9 | ) 10 | ) 11 | 12 | ;; Recursive factorial named 13 | (func $fac-rec-named (export "fac-rec-named") (param $n i64) (result i64) 14 | (if (result i64) (i64.eq (local.get $n) (i64.const 0)) 15 | (then (i64.const 1)) 16 | (else 17 | (i64.mul 18 | (local.get $n) 19 | (call $fac-rec-named (i64.sub (local.get $n) (i64.const 1))) 20 | ) 21 | ) 22 | ) 23 | ) 24 | 25 | ;; Iterative factorial 26 | (func (export "fac-iter") (param i64) (result i64) 27 | (local i64 i64) 28 | (local.set 1 (local.get 0)) 29 | (local.set 2 (i64.const 1)) 30 | (block 31 | (loop 32 | (if 33 | (i64.eq (local.get 1) (i64.const 0)) 34 | (then (br 2)) 35 | (else 36 | (local.set 2 (i64.mul (local.get 1) (local.get 2))) 37 | (local.set 1 (i64.sub (local.get 1) (i64.const 1))) 38 | ) 39 | ) 40 | (br 0) 41 | ) 42 | ) 43 | (local.get 2) 44 | ) 45 | 46 | ;; Iterative factorial named 47 | (func (export "fac-iter-named") (param $n i64) (result i64) 48 | (local $i i64) 49 | (local $res i64) 50 | (local.set $i (local.get $n)) 51 | (local.set $res (i64.const 1)) 52 | (block $done 53 | (loop $loop 54 | (if 55 | (i64.eq (local.get $i) (i64.const 0)) 56 | (then (br $done)) 57 | (else 58 | (local.set $res (i64.mul (local.get $i) (local.get $res))) 59 | (local.set $i (i64.sub (local.get $i) (i64.const 1))) 60 | ) 61 | ) 62 | (br $loop) 63 | ) 64 | ) 65 | (local.get $res) 66 | ) 67 | 68 | ;; Optimized factorial. 69 | (func (export "fac-opt") (param i64) (result i64) 70 | (local i64) 71 | (local.set 1 (i64.const 1)) 72 | (block 73 | (br_if 0 (i64.lt_s (local.get 0) (i64.const 2))) 74 | (loop 75 | (local.set 1 (i64.mul (local.get 1) (local.get 0))) 76 | (local.set 0 (i64.add (local.get 0) (i64.const -1))) 77 | (br_if 0 (i64.gt_s (local.get 0) (i64.const 1))) 78 | ) 79 | ) 80 | (local.get 1) 81 | ) 82 | 83 | ;; Iterative factorial without locals. 84 | (func $pick0 (param i64) (result i64 i64) 85 | (local.get 0) (local.get 0) 86 | ) 87 | (func $pick1 (param i64 i64) (result i64 i64 i64) 88 | (local.get 0) (local.get 1) (local.get 0) 89 | ) 90 | (func (export "fac-ssa") (param i64) (result i64) 91 | (i64.const 1) (local.get 0) 92 | (loop $l (param i64 i64) (result i64) 93 | (call $pick1) (call $pick1) (i64.mul) 94 | (call $pick1) (i64.const 1) (i64.sub) 95 | (call $pick0) (i64.const 0) (i64.gt_u) 96 | (br_if $l) 97 | (drop) (return) 98 | ) 99 | ) 100 | ) 101 | 102 | (assert_return (invoke "fac-rec" (i64.const 25)) (i64.const 7034535277573963776)) 103 | (assert_return (invoke "fac-iter" (i64.const 25)) (i64.const 7034535277573963776)) 104 | (assert_return (invoke "fac-rec-named" (i64.const 25)) (i64.const 7034535277573963776)) 105 | (assert_return (invoke "fac-iter-named" (i64.const 25)) (i64.const 7034535277573963776)) 106 | (assert_return (invoke "fac-opt" (i64.const 25)) (i64.const 7034535277573963776)) 107 | (assert_return (invoke "fac-ssa" (i64.const 25)) (i64.const 7034535277573963776)) 108 | 109 | (assert_exhaustion (invoke "fac-rec" (i64.const 1073741824)) "call stack exhausted") 110 | -------------------------------------------------------------------------------- /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/inline-module.wast: -------------------------------------------------------------------------------- 1 | (func) (memory 0) (func (export "f")) 2 | -------------------------------------------------------------------------------- /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/ref_func.wast: -------------------------------------------------------------------------------- 1 | (module 2 | (func (export "f") (param $x i32) (result i32) (local.get $x)) 3 | ) 4 | (register "M") 5 | 6 | (module 7 | (func $f (import "M" "f") (param i32) (result i32)) 8 | (func $g (param $x i32) (result i32) 9 | (i32.add (local.get $x) (i32.const 1)) 10 | ) 11 | 12 | (global funcref (ref.func $f)) 13 | (global funcref (ref.func $g)) 14 | (global $v (mut funcref) (ref.func $f)) 15 | 16 | (global funcref (ref.func $gf1)) 17 | (global funcref (ref.func $gf2)) 18 | (func (drop (ref.func $ff1)) (drop (ref.func $ff2))) 19 | (elem declare func $gf1 $ff1) 20 | (elem declare funcref (ref.func $gf2) (ref.func $ff2)) 21 | (func $gf1) 22 | (func $gf2) 23 | (func $ff1) 24 | (func $ff2) 25 | 26 | (func (export "is_null-f") (result i32) 27 | (ref.is_null (ref.func $f)) 28 | ) 29 | (func (export "is_null-g") (result i32) 30 | (ref.is_null (ref.func $g)) 31 | ) 32 | (func (export "is_null-v") (result i32) 33 | (ref.is_null (global.get $v)) 34 | ) 35 | 36 | (func (export "set-f") (global.set $v (ref.func $f))) 37 | (func (export "set-g") (global.set $v (ref.func $g))) 38 | 39 | (table $t 1 funcref) 40 | (elem declare func $f $g) 41 | 42 | (func (export "call-f") (param $x i32) (result i32) 43 | (table.set $t (i32.const 0) (ref.func $f)) 44 | (call_indirect $t (param i32) (result i32) (local.get $x) (i32.const 0)) 45 | ) 46 | (func (export "call-g") (param $x i32) (result i32) 47 | (table.set $t (i32.const 0) (ref.func $g)) 48 | (call_indirect $t (param i32) (result i32) (local.get $x) (i32.const 0)) 49 | ) 50 | (func (export "call-v") (param $x i32) (result i32) 51 | (table.set $t (i32.const 0) (global.get $v)) 52 | (call_indirect $t (param i32) (result i32) (local.get $x) (i32.const 0)) 53 | ) 54 | ) 55 | 56 | (assert_return (invoke "is_null-f") (i32.const 0)) 57 | (assert_return (invoke "is_null-g") (i32.const 0)) 58 | (assert_return (invoke "is_null-v") (i32.const 0)) 59 | 60 | (assert_return (invoke "call-f" (i32.const 4)) (i32.const 4)) 61 | (assert_return (invoke "call-g" (i32.const 4)) (i32.const 5)) 62 | (assert_return (invoke "call-v" (i32.const 4)) (i32.const 4)) 63 | (invoke "set-g") 64 | (assert_return (invoke "call-v" (i32.const 4)) (i32.const 5)) 65 | (invoke "set-f") 66 | (assert_return (invoke "call-v" (i32.const 4)) (i32.const 4)) 67 | 68 | (assert_invalid 69 | (module 70 | (func $f (import "M" "f") (param i32) (result i32)) 71 | (func $g (import "M" "g") (param i32) (result i32)) 72 | (global funcref (ref.func 7)) 73 | ) 74 | "unknown function 7" 75 | ) 76 | 77 | 78 | ;; Reference declaration 79 | 80 | (module 81 | (func $f1) 82 | (func $f2) 83 | (func $f3) 84 | (func $f4) 85 | (func $f5) 86 | (func $f6) 87 | 88 | (table $t 1 funcref) 89 | 90 | (global funcref (ref.func $f1)) 91 | (export "f" (func $f2)) 92 | (elem (table $t) (i32.const 0) func $f3) 93 | (elem (table $t) (i32.const 0) funcref (ref.func $f4)) 94 | (elem func $f5) 95 | (elem funcref (ref.func $f6)) 96 | 97 | (func 98 | (ref.func $f1) 99 | (ref.func $f2) 100 | (ref.func $f3) 101 | (ref.func $f4) 102 | (ref.func $f5) 103 | (ref.func $f6) 104 | (return) 105 | ) 106 | ) 107 | 108 | (assert_invalid 109 | (module (func $f (drop (ref.func $f)))) 110 | "undeclared function reference" 111 | ) 112 | (assert_invalid 113 | (module (start $f) (func $f (drop (ref.func $f)))) 114 | "undeclared function reference" 115 | ) 116 | -------------------------------------------------------------------------------- /test/core/ref_is_null.wast: -------------------------------------------------------------------------------- 1 | (module 2 | (func $f1 (export "funcref") (param $x funcref) (result i32) 3 | (ref.is_null (local.get $x)) 4 | ) 5 | (func $f2 (export "externref") (param $x externref) (result i32) 6 | (ref.is_null (local.get $x)) 7 | ) 8 | 9 | (table $t1 2 funcref) 10 | (table $t2 2 externref) 11 | (elem (table $t1) (i32.const 1) func $dummy) 12 | (func $dummy) 13 | 14 | (func (export "init") (param $r externref) 15 | (table.set $t2 (i32.const 1) (local.get $r)) 16 | ) 17 | (func (export "deinit") 18 | (table.set $t1 (i32.const 1) (ref.null func)) 19 | (table.set $t2 (i32.const 1) (ref.null extern)) 20 | ) 21 | 22 | (func (export "funcref-elem") (param $x i32) (result i32) 23 | (call $f1 (table.get $t1 (local.get $x))) 24 | ) 25 | (func (export "externref-elem") (param $x i32) (result i32) 26 | (call $f2 (table.get $t2 (local.get $x))) 27 | ) 28 | ) 29 | 30 | (assert_return (invoke "funcref" (ref.null func)) (i32.const 1)) 31 | (assert_return (invoke "externref" (ref.null extern)) (i32.const 1)) 32 | 33 | (assert_return (invoke "externref" (ref.extern 1)) (i32.const 0)) 34 | 35 | (invoke "init" (ref.extern 0)) 36 | 37 | (assert_return (invoke "funcref-elem" (i32.const 0)) (i32.const 1)) 38 | (assert_return (invoke "externref-elem" (i32.const 0)) (i32.const 1)) 39 | 40 | (assert_return (invoke "funcref-elem" (i32.const 1)) (i32.const 0)) 41 | (assert_return (invoke "externref-elem" (i32.const 1)) (i32.const 0)) 42 | 43 | (invoke "deinit") 44 | 45 | (assert_return (invoke "funcref-elem" (i32.const 0)) (i32.const 1)) 46 | (assert_return (invoke "externref-elem" (i32.const 0)) (i32.const 1)) 47 | 48 | (assert_return (invoke "funcref-elem" (i32.const 1)) (i32.const 1)) 49 | (assert_return (invoke "externref-elem" (i32.const 1)) (i32.const 1)) 50 | 51 | (assert_invalid 52 | (module (func $ref-vs-num (param i32) (ref.is_null (local.get 0)))) 53 | "type mismatch" 54 | ) 55 | (assert_invalid 56 | (module (func $ref-vs-empty (ref.is_null))) 57 | "type mismatch" 58 | ) 59 | -------------------------------------------------------------------------------- /test/core/ref_null.wast: -------------------------------------------------------------------------------- 1 | (module 2 | (func (export "externref") (result externref) (ref.null extern)) 3 | (func (export "funcref") (result funcref) (ref.null func)) 4 | 5 | (global externref (ref.null extern)) 6 | (global funcref (ref.null func)) 7 | ) 8 | 9 | (assert_return (invoke "externref") (ref.null extern)) 10 | (assert_return (invoke "funcref") (ref.null func)) 11 | -------------------------------------------------------------------------------- /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_f32x4_pmin_pmax.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | """ 4 | Generate f32x4 [pmin, pmax] cases. 5 | """ 6 | 7 | from simd_f32x4_arith import Simdf32x4ArithmeticCase 8 | from simd_float_op import FloatingPointSimpleOp 9 | from simd import SIMD 10 | from test_assert import AssertReturn 11 | 12 | 13 | class Simdf32x4PminPmaxCase(Simdf32x4ArithmeticCase): 14 | UNARY_OPS = () 15 | BINARY_OPS = ('pmin', 'pmax',) 16 | floatOp = FloatingPointSimpleOp() 17 | 18 | def get_combine_cases(self): 19 | return '' 20 | 21 | def get_normal_case(self): 22 | """Normal test cases from WebAssembly core tests. 23 | """ 24 | cases = [] 25 | binary_test_data = [] 26 | unary_test_data = [] 27 | 28 | for op in self.BINARY_OPS: 29 | op_name = self.full_op_name(op) 30 | for operand1 in self.FLOAT_NUMBERS + self.LITERAL_NUMBERS: 31 | for operand2 in self.FLOAT_NUMBERS + self.LITERAL_NUMBERS: 32 | result = self.floatOp.binary_op(op, operand1, operand2) 33 | binary_test_data.append([op_name, operand1, operand2, result]) 34 | 35 | # pmin and pmax always return operand1 if either operand is a nan 36 | for operand1 in self.NAN_NUMBERS: 37 | for operand2 in self.FLOAT_NUMBERS + self.LITERAL_NUMBERS + self.NAN_NUMBERS: 38 | binary_test_data.append([op_name, operand1, operand2, operand1]) 39 | for operand2 in self.NAN_NUMBERS: 40 | for operand1 in self.FLOAT_NUMBERS + self.LITERAL_NUMBERS: 41 | binary_test_data.append([op_name, operand1, operand2, operand1]) 42 | 43 | for case in binary_test_data: 44 | cases.append(str(AssertReturn(case[0], 45 | [SIMD.v128_const(c, self.LANE_TYPE) for c in case[1:-1]], 46 | SIMD.v128_const(case[-1], self.LANE_TYPE)))) 47 | 48 | self.get_unknown_operator_case(cases) 49 | 50 | return '\n'.join(cases) 51 | 52 | def get_unknown_operator_case(self, cases): 53 | """Unknown operator cases. 54 | """ 55 | 56 | tpl_assert = "(assert_malformed (module quote \"(memory 1) (func (result v128) " \ 57 | "({lane_type}.{op} {value}))\") \"unknown operator\")" 58 | 59 | unknown_op_cases = ['\n\n;; Unknown operators\n'] 60 | cases.extend(unknown_op_cases) 61 | 62 | for lane_type in ['i8x16', 'i16x8', 'i32x4', 'i64x2']: 63 | 64 | for op in self.BINARY_OPS: 65 | cases.append(tpl_assert.format(lane_type=lane_type, op=op, value=' '.join([self.v128_const('i32x4', '0')]*2))) 66 | 67 | def gen_test_cases(self): 68 | wast_filename = '../simd_{lane_type}_pmin_pmax.wast'.format(lane_type=self.LANE_TYPE) 69 | with open(wast_filename, 'w') as fp: 70 | txt_test_case = self.get_all_cases() 71 | txt_test_case = txt_test_case.replace( 72 | self.LANE_TYPE + ' arithmetic', 73 | self.LANE_TYPE + ' [pmin, pmax]') 74 | fp.write(txt_test_case) 75 | 76 | 77 | def gen_test_cases(): 78 | simd_f32x4_pmin_pmax_case = Simdf32x4PminPmaxCase() 79 | simd_f32x4_pmin_pmax_case.gen_test_cases() 80 | 81 | 82 | if __name__ == '__main__': 83 | gen_test_cases() 84 | -------------------------------------------------------------------------------- /test/core/simd/meta/simd_f32x4_rounding.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | """ 4 | Generate f32x4 [ceil, floor, trunc, nearest] cases. 5 | """ 6 | 7 | from simd_f32x4_arith import Simdf32x4ArithmeticCase 8 | from simd_float_op import FloatingPointRoundingOp 9 | from simd import SIMD 10 | from test_assert import AssertReturn 11 | 12 | 13 | class Simdf32x4RoundingCase(Simdf32x4ArithmeticCase): 14 | UNARY_OPS = ('ceil', 'floor', 'trunc', 'nearest') 15 | BINARY_OPS = () 16 | floatOp = FloatingPointRoundingOp() 17 | 18 | def get_combine_cases(self): 19 | return '' 20 | 21 | def get_normal_case(self): 22 | """Normal test cases from WebAssembly core tests. 23 | """ 24 | cases = [] 25 | unary_test_data = [] 26 | 27 | for op in self.UNARY_OPS: 28 | op_name = self.full_op_name(op) 29 | for operand in self.FLOAT_NUMBERS: 30 | result = self.floatOp.unary_op(op, operand) 31 | if 'nan' in result: 32 | unary_test_data.append([op_name, operand, 'nan:canonical']) 33 | else: 34 | unary_test_data.append([op_name, operand, result]) 35 | 36 | for operand in self.LITERAL_NUMBERS: 37 | result = self.floatOp.unary_op(op, operand, hex_form=False) 38 | unary_test_data.append([op_name, operand, result]) 39 | 40 | for operand in self.NAN_NUMBERS: 41 | if 'nan:' in operand: 42 | unary_test_data.append([op_name, operand, 'nan:arithmetic']) 43 | else: 44 | unary_test_data.append([op_name, operand, 'nan:canonical']) 45 | 46 | for case in unary_test_data: 47 | cases.append(str(AssertReturn(case[0], 48 | [SIMD.v128_const(elem, self.LANE_TYPE) for elem in case[1:-1]], 49 | SIMD.v128_const(case[-1], self.LANE_TYPE)))) 50 | 51 | self.get_unknown_operator_case(cases) 52 | 53 | return '\n'.join(cases) 54 | 55 | def get_unknown_operator_case(self, cases): 56 | """Unknown operator cases. 57 | """ 58 | 59 | tpl_assert = "(assert_malformed (module quote \"(memory 1) (func (result v128) " \ 60 | "({lane_type}.{op} {value}))\") \"unknown operator\")" 61 | 62 | unknown_op_cases = ['\n\n;; Unknown operators\n'] 63 | cases.extend(unknown_op_cases) 64 | 65 | for lane_type in ['i8x16', 'i16x8', 'i32x4', 'i64x2']: 66 | for op in self.UNARY_OPS: 67 | cases.append(tpl_assert.format(lane_type=lane_type, op=op, value=self.v128_const('i32x4', '0'))) 68 | 69 | def gen_test_cases(self): 70 | wast_filename = '../simd_{lane_type}_rounding.wast'.format(lane_type=self.LANE_TYPE) 71 | with open(wast_filename, 'w') as fp: 72 | txt_test_case = self.get_all_cases() 73 | txt_test_case = txt_test_case.replace( 74 | self.LANE_TYPE + ' arithmetic', 75 | self.LANE_TYPE + ' [ceil, floor, trunc, nearest]') 76 | fp.write(txt_test_case) 77 | 78 | 79 | def gen_test_cases(): 80 | simd_f32x4_case = Simdf32x4RoundingCase() 81 | simd_f32x4_case.gen_test_cases() 82 | 83 | 84 | if __name__ == '__main__': 85 | gen_test_cases() 86 | -------------------------------------------------------------------------------- /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_int_to_int_extend.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | """ 4 | Generates all integer-to-integer extension test cases. 5 | """ 6 | 7 | from simd import SIMD 8 | from simd_arithmetic import SimdArithmeticCase 9 | from test_assert import AssertReturn, AssertInvalid 10 | 11 | 12 | class SimdIntToIntExtend(SimdArithmeticCase): 13 | LANE_TYPE = "" # unused, can be anything 14 | BINARY_OPS = () 15 | UNARY_OPS = ( 16 | "i16x8.extend_high_i8x16_s", 17 | "i16x8.extend_high_i8x16_u", 18 | "i16x8.extend_low_i8x16_s", 19 | "i16x8.extend_low_i8x16_u", 20 | "i32x4.extend_high_i16x8_s", 21 | "i32x4.extend_high_i16x8_u", 22 | "i32x4.extend_low_i16x8_s", 23 | "i32x4.extend_low_i16x8_u", 24 | "i64x2.extend_high_i32x4_s", 25 | "i64x2.extend_high_i32x4_u", 26 | "i64x2.extend_low_i32x4_s", 27 | "i64x2.extend_low_i32x4_u", 28 | ) 29 | 30 | TEST_FUNC_TEMPLATE_HEADER = ";; Tests for int-to-int extension operations.\n" 31 | 32 | def op_name(self, op): 33 | # Override base class implementation, since the lane type is already 34 | # part of the op name. 35 | return "{op}".format(lane_type=self.LANE_TYPE, op=op) 36 | 37 | def is_unsigned(self, op): 38 | return op.endswith("_u") 39 | 40 | def src_lane_type(self, op): 41 | return op[-7:-2] 42 | 43 | def dst_lane_type(self, op): 44 | return op[0:5] 45 | 46 | def get_test_cases(self, src_value): 47 | return [ 48 | (0, 0), 49 | (0, 1), 50 | (0, -1), 51 | (1, 0), 52 | (-1, 0), 53 | (1, -1), 54 | ((-1, 1)), 55 | ((src_value.max - 1), (src_value.max)), 56 | ((src_value.max), (src_value.max - 1)), 57 | ((src_value.max), (src_value.max)), 58 | ((src_value.min), (src_value.min)), 59 | ((src_value.max), (src_value.min)), 60 | ((src_value.min), (src_value.max)), 61 | ((src_value.max), -1), 62 | (-1, (src_value.max)), 63 | (((src_value.min + 1), (src_value.min))), 64 | ((src_value.min), (src_value.min + 1)), 65 | ((src_value.min), (-1)), 66 | ((-1), (src_value.min)), 67 | ] 68 | 69 | def get_normal_case(self): 70 | cases = [] 71 | 72 | for op in self.UNARY_OPS: 73 | src_lane_type = self.src_lane_type(op) 74 | src_value = self.LANE_VALUE[src_lane_type] 75 | operands = self.get_test_cases(src_value) 76 | 77 | for (low, high) in operands: 78 | result = low if "low" in op else high 79 | 80 | if self.is_unsigned(op): 81 | # Unsign-extend, mask top bits. 82 | result = result & src_value.mask 83 | 84 | cases.append( 85 | str( 86 | AssertReturn( 87 | op, 88 | [SIMD.v128_const([str(low), str(high)], src_lane_type)], 89 | SIMD.v128_const(str(result), self.dst_lane_type(op)), 90 | ) 91 | ) 92 | ) 93 | 94 | cases.append("") 95 | 96 | return "\n".join(cases) 97 | 98 | def gen_test_cases(self): 99 | wast_filename = "../simd_int_to_int_extend.wast" 100 | with open(wast_filename, "w") as fp: 101 | fp.write(self.get_all_cases()) 102 | 103 | def get_combine_cases(self): 104 | return "" 105 | 106 | 107 | def gen_test_cases(): 108 | simd_int_to_int_extend = SimdIntToIntExtend() 109 | simd_int_to_int_extend.gen_test_cases() 110 | 111 | 112 | if __name__ == "__main__": 113 | gen_test_cases() 114 | -------------------------------------------------------------------------------- /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/meta/test_assert.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | 4 | """ 5 | This python file is a tool class for test generation. 6 | Currently only the 'AssertReturn' class that is used 7 | to generate the 'assert_return' assertion. 8 | TODO: Add more assertions 9 | """ 10 | 11 | 12 | # Generate assert_return to test 13 | class AssertReturn: 14 | 15 | op = '' 16 | params = '' 17 | expected_result = '' 18 | 19 | def __init__(self, op, params, expected_result): 20 | 21 | # Convert to list if got str 22 | if isinstance(params, str): 23 | params = [params] 24 | if isinstance(expected_result, str): 25 | expected_result = [expected_result] 26 | 27 | self.op = op 28 | self.params = params 29 | self.expected_result = expected_result 30 | 31 | def __str__(self): 32 | assert_return = '(assert_return (invoke "{}"'.format(self.op) 33 | 34 | head_len = len(assert_return) 35 | 36 | # Add write space to make the test case easier to read 37 | params = [] 38 | for param in self.params: 39 | white_space = ' ' 40 | if len(params) != 0: 41 | white_space = '\n ' + ' ' * head_len 42 | params.append(white_space + param) 43 | 44 | results = [] 45 | for result in self.expected_result: 46 | white_space = ' ' 47 | if len(params) != 0 or len(results) != 0: 48 | white_space = '\n ' + ' ' * head_len 49 | results.append(white_space + result) 50 | 51 | return '{assert_head}{params}){expected_result})'.format(assert_head=assert_return, params=''.join(params), expected_result=''.join(results)) 52 | 53 | 54 | # Generate assert_invalid to test 55 | class AssertInvalid: 56 | 57 | @staticmethod 58 | def get_arg_empty_test(op, extended_name, param_type, result_type, params): 59 | 60 | arg_empty_test = '(assert_invalid' \ 61 | '\n (module' \ 62 | '\n (func ${op}-{extended_name}{param_type}{result_type}' \ 63 | '\n ({op}{params})' \ 64 | '\n )' \ 65 | '\n )' \ 66 | '\n "type mismatch"' \ 67 | '\n)' 68 | 69 | def str_with_space(input_str): 70 | return (' ' if input_str else '') + input_str 71 | 72 | param_map = { 73 | 'op': op, 74 | 'extended_name': extended_name, 75 | 'param_type': str_with_space(param_type), 76 | 'result_type': str_with_space(result_type), 77 | 'params': str_with_space(params), 78 | } 79 | 80 | return arg_empty_test.format(**param_map) 81 | 82 | 83 | class AssertMalformed: 84 | """Generate an assert_malformed test""" 85 | 86 | @staticmethod 87 | def get_unknown_op_test(op, result_type, *params): 88 | malformed_template = '(assert_malformed (module quote "(memory 1) (func (result {result_type}) ({operator} {param}))") "unknown operator")' 89 | return malformed_template.format( 90 | operator=op, result_type=result_type, param=' '.join(params) 91 | ) -------------------------------------------------------------------------------- /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 | (assert_invalid 2 | (module 3 | (table $t1 10 funcref) 4 | (table $t2 10 externref) 5 | (func $f 6 | (table.copy $t1 $t2 (i32.const 0) (i32.const 1) (i32.const 2)) 7 | ) 8 | ) 9 | "type mismatch" 10 | ) 11 | 12 | (assert_invalid 13 | (module 14 | (table $t 10 funcref) 15 | (elem $el externref) 16 | (func $f 17 | (table.init $t $el (i32.const 0) (i32.const 1) (i32.const 2)) 18 | ) 19 | ) 20 | "type mismatch" 21 | ) 22 | -------------------------------------------------------------------------------- /test/core/table.wast: -------------------------------------------------------------------------------- 1 | ;; Test table section structure 2 | 3 | (module (table 0 funcref)) 4 | (module (table 1 funcref)) 5 | (module (table 0 0 funcref)) 6 | (module (table 0 1 funcref)) 7 | (module (table 1 256 funcref)) 8 | (module (table 0 65536 funcref)) 9 | (module (table 0 0xffff_ffff funcref)) 10 | 11 | (module (table 0 funcref) (table 0 funcref)) 12 | (module (table (import "spectest" "table") 0 funcref) (table 0 funcref)) 13 | 14 | (assert_invalid (module (elem (i32.const 0))) "unknown table") 15 | (assert_invalid (module (elem (i32.const 0) $f) (func $f)) "unknown table") 16 | 17 | 18 | (assert_invalid 19 | (module (table 1 0 funcref)) 20 | "size minimum must not be greater than maximum" 21 | ) 22 | (assert_invalid 23 | (module (table 0xffff_ffff 0 funcref)) 24 | "size minimum must not be greater than maximum" 25 | ) 26 | 27 | (assert_malformed 28 | (module quote "(table 0x1_0000_0000 funcref)") 29 | "i32 constant out of range" 30 | ) 31 | (assert_malformed 32 | (module quote "(table 0x1_0000_0000 0x1_0000_0000 funcref)") 33 | "i32 constant out of range" 34 | ) 35 | (assert_malformed 36 | (module quote "(table 0 0x1_0000_0000 funcref)") 37 | "i32 constant out of range" 38 | ) 39 | 40 | 41 | ;; Duplicate table identifiers 42 | 43 | (assert_malformed (module quote 44 | "(table $foo 1 funcref)" 45 | "(table $foo 1 funcref)") 46 | "duplicate table") 47 | (assert_malformed (module quote 48 | "(import \"\" \"\" (table $foo 1 funcref))" 49 | "(table $foo 1 funcref)") 50 | "duplicate table") 51 | (assert_malformed (module quote 52 | "(import \"\" \"\" (table $foo 1 funcref))" 53 | "(import \"\" \"\" (table $foo 1 funcref))") 54 | "duplicate table") 55 | -------------------------------------------------------------------------------- /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 $t2 (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/table_size.wast: -------------------------------------------------------------------------------- 1 | (module 2 | (table $t0 0 externref) 3 | (table $t1 1 externref) 4 | (table $t2 0 2 externref) 5 | (table $t3 3 8 externref) 6 | 7 | (func (export "size-t0") (result i32) (table.size $t0)) 8 | (func (export "size-t1") (result i32) (table.size $t1)) 9 | (func (export "size-t2") (result i32) (table.size $t2)) 10 | (func (export "size-t3") (result i32) (table.size $t3)) 11 | 12 | (func (export "grow-t0") (param $sz i32) 13 | (drop (table.grow $t0 (ref.null extern) (local.get $sz))) 14 | ) 15 | (func (export "grow-t1") (param $sz i32) 16 | (drop (table.grow $t1 (ref.null extern) (local.get $sz))) 17 | ) 18 | (func (export "grow-t2") (param $sz i32) 19 | (drop (table.grow $t2 (ref.null extern) (local.get $sz))) 20 | ) 21 | (func (export "grow-t3") (param $sz i32) 22 | (drop (table.grow $t3 (ref.null extern) (local.get $sz))) 23 | ) 24 | ) 25 | 26 | (assert_return (invoke "size-t0") (i32.const 0)) 27 | (assert_return (invoke "grow-t0" (i32.const 1))) 28 | (assert_return (invoke "size-t0") (i32.const 1)) 29 | (assert_return (invoke "grow-t0" (i32.const 4))) 30 | (assert_return (invoke "size-t0") (i32.const 5)) 31 | (assert_return (invoke "grow-t0" (i32.const 0))) 32 | (assert_return (invoke "size-t0") (i32.const 5)) 33 | 34 | (assert_return (invoke "size-t1") (i32.const 1)) 35 | (assert_return (invoke "grow-t1" (i32.const 1))) 36 | (assert_return (invoke "size-t1") (i32.const 2)) 37 | (assert_return (invoke "grow-t1" (i32.const 4))) 38 | (assert_return (invoke "size-t1") (i32.const 6)) 39 | (assert_return (invoke "grow-t1" (i32.const 0))) 40 | (assert_return (invoke "size-t1") (i32.const 6)) 41 | 42 | (assert_return (invoke "size-t2") (i32.const 0)) 43 | (assert_return (invoke "grow-t2" (i32.const 3))) 44 | (assert_return (invoke "size-t2") (i32.const 0)) 45 | (assert_return (invoke "grow-t2" (i32.const 1))) 46 | (assert_return (invoke "size-t2") (i32.const 1)) 47 | (assert_return (invoke "grow-t2" (i32.const 0))) 48 | (assert_return (invoke "size-t2") (i32.const 1)) 49 | (assert_return (invoke "grow-t2" (i32.const 4))) 50 | (assert_return (invoke "size-t2") (i32.const 1)) 51 | (assert_return (invoke "grow-t2" (i32.const 1))) 52 | (assert_return (invoke "size-t2") (i32.const 2)) 53 | 54 | (assert_return (invoke "size-t3") (i32.const 3)) 55 | (assert_return (invoke "grow-t3" (i32.const 1))) 56 | (assert_return (invoke "size-t3") (i32.const 4)) 57 | (assert_return (invoke "grow-t3" (i32.const 3))) 58 | (assert_return (invoke "size-t3") (i32.const 7)) 59 | (assert_return (invoke "grow-t3" (i32.const 0))) 60 | (assert_return (invoke "size-t3") (i32.const 7)) 61 | (assert_return (invoke "grow-t3" (i32.const 2))) 62 | (assert_return (invoke "size-t3") (i32.const 7)) 63 | (assert_return (invoke "grow-t3" (i32.const 1))) 64 | (assert_return (invoke "size-t3") (i32.const 8)) 65 | 66 | 67 | ;; Type errors 68 | 69 | (assert_invalid 70 | (module 71 | (table $t 1 externref) 72 | (func $type-result-i32-vs-empty 73 | (table.size $t) 74 | ) 75 | ) 76 | "type mismatch" 77 | ) 78 | (assert_invalid 79 | (module 80 | (table $t 1 externref) 81 | (func $type-result-i32-vs-f32 (result f32) 82 | (table.size $t) 83 | ) 84 | ) 85 | "type mismatch" 86 | ) 87 | -------------------------------------------------------------------------------- /test/core/token.wast: -------------------------------------------------------------------------------- 1 | ;; Test tokenization 2 | 3 | (assert_malformed 4 | (module quote "(func (drop (i32.const0)))") 5 | "unknown operator" 6 | ) 7 | (assert_malformed 8 | (module quote "(func br 0drop)") 9 | "unknown operator" 10 | ) 11 | -------------------------------------------------------------------------------- /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_result_1") (result i32) 21 | (unreachable) (i32.add (select)) 22 | ) 23 | 24 | (func (export "select_unreached_result_2") (result i64) 25 | (unreachable) (i64.add (select (i64.const 0) (i32.const 0))) 26 | ) 27 | 28 | (func (export "unreachable-num") 29 | (unreachable) 30 | (select) 31 | (i32.eqz) 32 | (drop) 33 | ) 34 | (func (export "unreachable-ref") 35 | (unreachable) 36 | (select) 37 | (ref.is_null) 38 | (drop) 39 | ) 40 | ) 41 | 42 | (assert_trap (invoke "select-trap-left" (i32.const 1)) "unreachable") 43 | (assert_trap (invoke "select-trap-left" (i32.const 0)) "unreachable") 44 | (assert_trap (invoke "select-trap-right" (i32.const 1)) "unreachable") 45 | (assert_trap (invoke "select-trap-right" (i32.const 0)) "unreachable") 46 | 47 | ;; Validation after unreachable 48 | 49 | (module 50 | (func (export "meet-bottom") 51 | (block (result f64) 52 | (block (result f32) 53 | (unreachable) 54 | (br_table 0 1 1 (i32.const 1)) 55 | ) 56 | (drop) 57 | (f64.const 0) 58 | ) 59 | (drop) 60 | ) 61 | ) 62 | 63 | -------------------------------------------------------------------------------- /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/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{WebAssemblyCoreSpecification, 2 | title = {{WebAssembly Core Specification}}, 3 | editor = {Rossberg, Andreas}, 4 | date = {2019-12-05}, 5 | institution = {{W3C}}, 6 | url = {https://www.w3.org/TR/wasm-core-1/}, 7 | langid = {english}, 8 | note = {https://webassembly.github.io/spec/core/_download/WebAssembly.pdf} 9 | } 10 | --------------------------------------------------------------------------------