├── .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 │ │ ├── implementation.rst │ │ ├── index-instructions.py │ │ ├── index-rules.rst │ │ ├── index-types.rst │ │ ├── index.rst │ │ └── properties.rst │ ├── binary │ │ ├── conventions.rst │ │ ├── index.rst │ │ ├── instructions.rst │ │ ├── modules.rst │ │ ├── types.rst │ │ └── values.rst │ ├── conf.py │ ├── exec │ │ ├── conventions.rst │ │ ├── index.rst │ │ ├── instructions.rst │ │ ├── modules.rst │ │ ├── numerics.rst │ │ ├── runtime.rst │ │ ├── types.rst │ │ └── values.rst │ ├── index.bs │ ├── index.rst │ ├── intro │ │ ├── index.rst │ │ ├── introduction.rst │ │ └── overview.rst │ ├── make.bat │ ├── static │ │ ├── custom.css │ │ └── webassembly.png │ ├── syntax │ │ ├── conventions.rst │ │ ├── index.rst │ │ ├── instructions.rst │ │ ├── modules.rst │ │ ├── types.rst │ │ └── values.rst │ ├── text │ │ ├── conventions.rst │ │ ├── index.rst │ │ ├── instructions.rst │ │ ├── lexical.rst │ │ ├── modules.rst │ │ ├── types.rst │ │ └── values.rst │ ├── util │ │ ├── README.htmldiff.pl │ │ ├── bikeshed │ │ │ └── conf.py │ │ ├── bikeshed_fixup.py │ │ ├── katex_fix.patch │ │ ├── macros.def │ │ ├── mathdef.py │ │ ├── mathdefbs.py │ │ ├── mathjax2katex.py │ │ └── pseudo-lexer.py │ └── valid │ │ ├── conventions.rst │ │ ├── index.rst │ │ ├── instructions.rst │ │ ├── matching.rst │ │ ├── modules.rst │ │ └── types.rst ├── deploy.sh ├── index.html ├── js-api │ ├── Makefile │ └── index.bs ├── util │ └── htmldiff.pl └── web-api │ ├── Makefile │ └── index.bs ├── dune-project ├── interpreter ├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── binary │ ├── decode.ml │ ├── decode.mli │ ├── encode.ml │ ├── encode.mli │ ├── utf8.ml │ └── utf8.mli ├── dune ├── 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 │ │ └── wast.ml ├── runtime │ ├── data.ml │ ├── data.mli │ ├── elem.ml │ ├── elem.mli │ ├── func.ml │ ├── func.mli │ ├── global.ml │ ├── global.mli │ ├── instance.ml │ ├── memory.ml │ ├── memory.mli │ ├── table.ml │ ├── table.mli │ ├── tag.ml │ ├── tag.mli │ └── value.ml ├── script │ ├── import.ml │ ├── import.mli │ ├── js.ml │ ├── js.mli │ ├── run.ml │ ├── run.mli │ └── script.ml ├── syntax │ ├── ast.ml │ ├── free.ml │ ├── free.mli │ ├── operators.ml │ ├── pack.ml │ └── types.ml ├── 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 │ ├── match.ml │ ├── match.mli │ ├── valid.ml │ └── valid.mli └── winmake.bat ├── papers ├── LICENSE ├── README.md ├── oopsla2019.pdf └── pldi2017.pdf ├── proposals ├── README.md ├── bulk-memory-operations │ └── Overview.md ├── continuations │ ├── Explainer.md │ ├── Overview.md │ └── examples │ │ ├── actor-lwt.wast │ │ ├── actor.wast │ │ ├── async-await.wast │ │ ├── control-lwt.wast │ │ ├── fun-actor-lwt.wast │ │ ├── fun-lwt.wast │ │ ├── fun-pipes.wast │ │ ├── fun-state.wast │ │ ├── generators.wast │ │ ├── lwt.wast │ │ ├── pipes.wast │ │ └── static-lwt.wast ├── function-references │ └── 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 └── tail-call │ └── Overview.md ├── test ├── LICENSE ├── README.md ├── Todo.md ├── build.py ├── core │ ├── .gitignore │ ├── README.md │ ├── address.wast │ ├── align.wast │ ├── binary-leb128.wast │ ├── binary.wast │ ├── block.wast │ ├── br.wast │ ├── br_if.wast │ ├── br_on_non_null.wast │ ├── br_on_null.wast │ ├── br_table.wast │ ├── bulk.wast │ ├── call.wast │ ├── call_indirect.wast │ ├── call_ref.wast │ ├── comments.wast │ ├── const.wast │ ├── cont.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_init.wast │ ├── local_set.wast │ ├── local_tee.wast │ ├── loop.wast │ ├── memory.wast │ ├── memory_copy.wast │ ├── memory_fill.wast │ ├── memory_grow.wast │ ├── memory_init.wast │ ├── memory_redundancy.wast │ ├── memory_size.wast │ ├── memory_trap.wast │ ├── names.wast │ ├── nop.wast │ ├── ref.wast │ ├── ref_as_non_null.wast │ ├── ref_func.wast │ ├── ref_is_null.wast │ ├── ref_null.wast │ ├── return.wast │ ├── return_call.wast │ ├── return_call_indirect.wast │ ├── return_call_ref.wast │ ├── run.py │ ├── select.wast │ ├── simd │ │ ├── meta │ │ │ ├── README.md │ │ │ ├── gen_tests.py │ │ │ ├── simd.py │ │ │ ├── simd_arithmetic.py │ │ │ ├── simd_bitwise.py │ │ │ ├── simd_compare.py │ │ │ ├── simd_ext_mul.py │ │ │ ├── simd_extadd_pairwise.py │ │ │ ├── simd_f32x4.py │ │ │ ├── simd_f32x4_arith.py │ │ │ ├── simd_f32x4_cmp.py │ │ │ ├── simd_f32x4_pmin_pmax.py │ │ │ ├── simd_f32x4_rounding.py │ │ │ ├── simd_f64x2.py │ │ │ ├── simd_f64x2_arith.py │ │ │ ├── simd_f64x2_cmp.py │ │ │ ├── simd_f64x2_pmin_pmax.py │ │ │ ├── simd_f64x2_rounding.py │ │ │ ├── simd_float_op.py │ │ │ ├── simd_i16x8_arith.py │ │ │ ├── simd_i16x8_cmp.py │ │ │ ├── simd_i16x8_q15mulr_sat_s.py │ │ │ ├── simd_i32x4_arith.py │ │ │ ├── simd_i32x4_cmp.py │ │ │ ├── simd_i32x4_dot_i16x8.py │ │ │ ├── simd_i64x2_arith.py │ │ │ ├── simd_i64x2_cmp.py │ │ │ ├── simd_i8x16_arith.py │ │ │ ├── simd_i8x16_cmp.py │ │ │ ├── simd_int_arith2.py │ │ │ ├── simd_int_to_int_extend.py │ │ │ ├── simd_int_trunc_sat_float.py │ │ │ ├── simd_integer_op.py │ │ │ ├── simd_lane_value.py │ │ │ ├── simd_load_lane.py │ │ │ ├── simd_sat_arith.py │ │ │ ├── simd_store_lane.py │ │ │ └── test_assert.py │ │ ├── simd_address.wast │ │ ├── simd_align.wast │ │ ├── simd_bit_shift.wast │ │ ├── simd_bitwise.wast │ │ ├── simd_boolean.wast │ │ ├── simd_const.wast │ │ ├── simd_conversions.wast │ │ ├── simd_f32x4.wast │ │ ├── simd_f32x4_arith.wast │ │ ├── simd_f32x4_cmp.wast │ │ ├── simd_f32x4_pmin_pmax.wast │ │ ├── simd_f32x4_rounding.wast │ │ ├── simd_f64x2.wast │ │ ├── simd_f64x2_arith.wast │ │ ├── simd_f64x2_cmp.wast │ │ ├── simd_f64x2_pmin_pmax.wast │ │ ├── simd_f64x2_rounding.wast │ │ ├── simd_i16x8_arith.wast │ │ ├── simd_i16x8_arith2.wast │ │ ├── simd_i16x8_cmp.wast │ │ ├── simd_i16x8_extadd_pairwise_i8x16.wast │ │ ├── simd_i16x8_extmul_i8x16.wast │ │ ├── simd_i16x8_q15mulr_sat_s.wast │ │ ├── simd_i16x8_sat_arith.wast │ │ ├── simd_i32x4_arith.wast │ │ ├── simd_i32x4_arith2.wast │ │ ├── simd_i32x4_cmp.wast │ │ ├── simd_i32x4_dot_i16x8.wast │ │ ├── simd_i32x4_extadd_pairwise_i16x8.wast │ │ ├── simd_i32x4_extmul_i16x8.wast │ │ ├── simd_i32x4_trunc_sat_f32x4.wast │ │ ├── simd_i32x4_trunc_sat_f64x2.wast │ │ ├── simd_i64x2_arith.wast │ │ ├── simd_i64x2_arith2.wast │ │ ├── simd_i64x2_cmp.wast │ │ ├── simd_i64x2_extmul_i32x4.wast │ │ ├── simd_i8x16_arith.wast │ │ ├── simd_i8x16_arith2.wast │ │ ├── simd_i8x16_cmp.wast │ │ ├── simd_i8x16_sat_arith.wast │ │ ├── simd_int_to_int_extend.wast │ │ ├── simd_lane.wast │ │ ├── simd_linking.wast │ │ ├── simd_load.wast │ │ ├── simd_load16_lane.wast │ │ ├── simd_load32_lane.wast │ │ ├── simd_load64_lane.wast │ │ ├── simd_load8_lane.wast │ │ ├── simd_load_extend.wast │ │ ├── simd_load_splat.wast │ │ ├── simd_load_zero.wast │ │ ├── simd_splat.wast │ │ ├── simd_store.wast │ │ ├── simd_store16_lane.wast │ │ ├── simd_store32_lane.wast │ │ ├── simd_store64_lane.wast │ │ └── simd_store8_lane.wast │ ├── skip-stack-guard-page.wast │ ├── stack.wast │ ├── start.wast │ ├── store.wast │ ├── switch.wast │ ├── table-sub.wast │ ├── table.wast │ ├── table_copy.wast │ ├── table_fill.wast │ ├── table_get.wast │ ├── table_grow.wast │ ├── table_init.wast │ ├── table_set.wast │ ├── table_size.wast │ ├── token.wast │ ├── tokens.wast │ ├── traps.wast │ ├── try_catch.wast │ ├── type-equivalence.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/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 |

Development has moved to https://github.com/wasmfx/specfx

2 |
3 | 4 | # Typed Continuations Proposal for WebAssembly 5 | 6 | This repository is a clone of 7 | [github.com/WebAssembly/spec/](https://github.com/WebAssembly/spec/). 8 | It is meant for discussion, prototype specification and implementation 9 | of a proposal to add support for different patterns of non-local 10 | control flow to WebAssembly. 11 | 12 | The proposal is fully implemented as part of the reference interpreter. 13 | 14 | * See the [explainer](proposals/continuations/Explainer.md) for a high-level summary of the proposal. 15 | 16 | * See the [overview](proposals/continuations/Overview.md) for a more formal description of the proposal. 17 | 18 | * See the [examples](proposals/continuations/examples) for Wasm code for implementing various different features including lightweight threads, actors, and async/await. 19 | 20 | Original `README` from upstream repository follows. 21 | 22 | # spec 23 | 24 | This repository holds a prototypical reference implementation for WebAssembly, 25 | which is currently serving as the official specification. Eventually, we expect 26 | to produce a specification either written in human-readable prose or in a formal 27 | specification language. 28 | 29 | It also holds the WebAssembly testsuite, which tests numerous aspects of 30 | conformance to the spec. 31 | 32 | View the work-in-progress spec at [webassembly.github.io/spec](https://webassembly.github.io/spec/). 33 | 34 | At this time, the contents of this repository are under development and known 35 | to be "incomplet and inkorrect". 36 | 37 | Participation is welcome. Discussions about new features, significant semantic 38 | changes, or any specification change likely to generate substantial discussion 39 | should take place in 40 | [the WebAssembly design repository](https://github.com/WebAssembly/design) 41 | first, so that this spec repository can remain focused. And please follow the 42 | [guidelines for contributing](Contributing.md). 43 | 44 | # citing 45 | 46 | For citing WebAssembly in LaTeX, use [this bibtex file](wasm-specs.bib). 47 | -------------------------------------------------------------------------------- /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/core/.gitignore: -------------------------------------------------------------------------------- 1 | _build 2 | _static 3 | document/*.pyc 4 | appendix/index-instructions.rst 5 | -------------------------------------------------------------------------------- /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:`Heap type ` |FUNC| :math:`\hex{70}` (-16 as |Bs7|) 18 | :ref:`Heap type ` |EXTERN| :math:`\hex{6F}` (-17 as |Bs7|) 19 | (reserved) :math:`\hex{6E}` .. :math:`\hex{6D}` 20 | :ref:`Reference type ` |REF| |NULL| :math:`\hex{6C}` (-20 as |Bs7|) 21 | :ref:`Reference type ` |REF| :math:`\hex{6B}` (-21 as |Bs7|) 22 | (reserved) :math:`\hex{6A}` .. :math:`\hex{61}` 23 | :ref:`Function type ` :math:`[\valtype^\ast] \toF[\valtype^\ast]` :math:`\hex{60}` (-32 as |Bs7|) 24 | (reserved) :math:`\hex{5F}` .. :math:`\hex{41}` 25 | :ref:`Result type ` :math:`[\epsilon]` :math:`\hex{40}` (-64 as |Bs7|) 26 | :ref:`Table type ` :math:`\limits~\reftype` (none) 27 | :ref:`Memory type ` :math:`\limits` (none) 28 | :ref:`Global type ` :math:`\mut~\valtype` (none) 29 | ======================================== =========================================== =============================================================================== 30 | -------------------------------------------------------------------------------- /document/core/appendix/index.rst: -------------------------------------------------------------------------------- 1 | .. _appendix: 2 | 3 | Appendix 4 | ======== 5 | 6 | .. toctree:: 7 | :maxdepth: 2 8 | 9 | embedding 10 | implementation 11 | properties 12 | algorithm 13 | custom 14 | changes 15 | 16 | .. 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 | types 13 | values 14 | instructions 15 | modules 16 | -------------------------------------------------------------------------------- /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 | overflow-x: auto; 48 | overflow-y: hidden; 49 | } 50 | 51 | div.relations { 52 | display: block; 53 | } 54 | 55 | div.sphinxsidebar { 56 | z-index: 1; 57 | background: #FFF; 58 | margin-top: -30px; 59 | font-size: 13px; 60 | width: 200px; 61 | height: 100%; 62 | } 63 | 64 | div.sphinxsidebarwrapper p.logo { 65 | padding: 30px 40px 10px 0px; 66 | } 67 | 68 | div.sphinxsidebar h3 { 69 | font-size: 0px; 70 | } 71 | 72 | div.sphinxsidebar a { 73 | border-bottom: 0px; 74 | } 75 | 76 | div.sphinxsidebar a:hover { 77 | border-bottom: 1px dotted; 78 | } 79 | -------------------------------------------------------------------------------- /document/core/static/webassembly.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/effect-handlers/wasm-spec/8c5d4f66db6405d57a7346ebbf2d207fe78d73fd/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/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 | -------------------------------------------------------------------------------- /dune-project: -------------------------------------------------------------------------------- 1 | (lang dune 2.9) 2 | -------------------------------------------------------------------------------- /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/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 | type codepoint = int 2 | type unicode = codepoint list 3 | 4 | exception Utf8 5 | 6 | let con n = 0x80 lor (n land 0x3f) 7 | 8 | let rec encode ns = Lib.String.implode (List.map Char.chr (encode' ns)) 9 | and encode' = function 10 | | [] -> [] 11 | | n::ns when n < 0 -> 12 | raise Utf8 13 | | n::ns when n < 0x80 -> 14 | n :: encode' ns 15 | | n::ns when n < 0x800 -> 16 | 0xc0 lor (n lsr 6) :: con n :: encode' ns 17 | | n::ns when n < 0x10000 -> 18 | 0xe0 lor (n lsr 12) :: con (n lsr 6) :: con n :: encode' ns 19 | | n::ns when n < 0x110000 -> 20 | 0xf0 lor (n lsr 18) :: con (n lsr 12) :: con (n lsr 6) :: con n 21 | :: encode' ns 22 | | _ -> 23 | raise Utf8 24 | 25 | let con b = if b land 0xc0 = 0x80 then b land 0x3f else raise Utf8 26 | let code min n = 27 | if n < min || (0xd800 <= n && n < 0xe000) || n >= 0x110000 then raise Utf8 28 | else n 29 | 30 | let rec decode s = decode' (List.map Char.code (Lib.String.explode s)) 31 | and decode' = function 32 | | [] -> [] 33 | | b1::bs when b1 < 0x80 -> 34 | code 0x0 b1 :: decode' bs 35 | | b1::bs when b1 < 0xc0 -> 36 | raise Utf8 37 | | b1::b2::bs when b1 < 0xe0 -> 38 | code 0x80 ((b1 land 0x1f) lsl 6 + con b2) :: decode' bs 39 | | b1::b2::b3::bs when b1 < 0xf0 -> 40 | code 0x800 ((b1 land 0x0f) lsl 12 + con b2 lsl 6 + con b3) :: decode' bs 41 | | b1::b2::b3::b4::bs when b1 < 0xf8 -> 42 | code 0x10000 ((b1 land 0x07) lsl 18 + con b2 lsl 12 + con b3 lsl 6 + con b4) 43 | :: decode' bs 44 | | _ -> 45 | raise Utf8 46 | -------------------------------------------------------------------------------- /interpreter/binary/utf8.mli: -------------------------------------------------------------------------------- 1 | type codepoint = int 2 | type unicode = codepoint list 3 | 4 | exception Utf8 5 | 6 | val decode : string -> unicode (* raises Utf8 *) 7 | val encode : unicode -> string (* raises Utf8 *) 8 | -------------------------------------------------------------------------------- /interpreter/dune: -------------------------------------------------------------------------------- 1 | (include_subdirs unqualified) 2 | 3 | (library 4 | (name wasm) 5 | ; The 'main' module shall not be part of the library, as it would start the 6 | ; Wasm REPL every time in all the dependencies. 7 | ; We exclude the 'wast' module as it is only used for the JS build. 8 | ; 'smallint' is a separate test module. 9 | (modules :standard \ main smallint wast)) 10 | 11 | (executable 12 | (name main) 13 | (modules main) 14 | (libraries wasm) 15 | (flags 16 | (-open Wasm))) 17 | 18 | (executable 19 | (name smallint) 20 | (modules smallint) 21 | (libraries wasm) 22 | (flags 23 | (-open Wasm))) 24 | 25 | (subdir 26 | text 27 | (rule 28 | (target lexer.ml) 29 | (deps lexer.mll) 30 | (action 31 | (chdir 32 | %{workspace_root} 33 | (run %{bin:ocamllex} -ml -q -o %{target} %{deps})))) 34 | (ocamlyacc 35 | (modules parser))) 36 | 37 | (env 38 | (_ 39 | (flags 40 | (-w +a-4-27-42-44-45-70 -warn-error +a-3)))) 41 | 42 | (rule 43 | (alias runtest) 44 | (deps 45 | ./main.exe 46 | ./smallint.exe 47 | (source_tree ../test)) 48 | (action 49 | (progn 50 | (run ../test/core/run.py --wasm ./main.exe) 51 | (run ./smallint.exe)))) 52 | -------------------------------------------------------------------------------- /interpreter/exec/eval.mli: -------------------------------------------------------------------------------- 1 | open Value 2 | open Instance 3 | 4 | exception Link of Source.region * string 5 | exception Trap of Source.region * string 6 | exception Exception of Source.region * string 7 | exception Suspension of Source.region * string 8 | exception Exhaustion of Source.region * string 9 | exception Crash of Source.region * string 10 | 11 | val init : Ast.module_ -> extern list -> module_inst (* raises Link, Trap *) 12 | val invoke : func_inst -> value list -> value list (* raises Trap *) 13 | -------------------------------------------------------------------------------- /interpreter/exec/eval_num.mli: -------------------------------------------------------------------------------- 1 | open Value 2 | 3 | val eval_unop : Ast.unop -> num -> num 4 | val eval_binop : Ast.binop -> num -> num -> num 5 | val eval_testop : Ast.testop -> num -> bool 6 | val eval_relop : Ast.relop -> num -> num -> bool 7 | val eval_cvtop : Ast.cvtop -> num -> num 8 | -------------------------------------------------------------------------------- /interpreter/exec/eval_vec.mli: -------------------------------------------------------------------------------- 1 | open Value 2 | 3 | val eval_testop : Ast.vec_testop -> vec -> bool 4 | val eval_unop : Ast.vec_unop -> vec -> vec 5 | val eval_binop : Ast.vec_binop -> vec -> vec -> vec 6 | val eval_relop : Ast.vec_relop -> vec -> vec -> vec 7 | val eval_cvtop : Ast.vec_cvtop -> vec -> vec 8 | val eval_shiftop : Ast.vec_shiftop -> vec -> num -> vec 9 | val eval_bitmaskop : Ast.vec_bitmaskop -> vec -> num 10 | val eval_vtestop : Ast.vec_vtestop -> vec -> bool 11 | val eval_vunop : Ast.vec_vunop -> vec -> vec 12 | val eval_vbinop : Ast.vec_vbinop -> vec -> vec -> vec 13 | val eval_vternop : Ast.vec_vternop -> vec -> vec -> vec -> vec 14 | val eval_splatop : Ast.vec_splatop -> num -> vec 15 | val eval_extractop : Ast.vec_extractop -> vec -> num 16 | val eval_replaceop : Ast.vec_replaceop -> vec -> num -> vec 17 | -------------------------------------------------------------------------------- /interpreter/exec/f32.ml: -------------------------------------------------------------------------------- 1 | (* 2 | * OCaml lacks 32-bit floats, however we can emulate all the basic operations 3 | * using 64-bit floats, as described in the paper 4 | * "When is double rounding innocuous?" by Samuel A. Figueroa. 5 | *) 6 | include Fxx.Make 7 | (struct 8 | include Int32 9 | let mantissa = 23 10 | let pos_nan = 0x7fc0_0000l 11 | let neg_nan = 0xffc0_0000l 12 | let bare_nan = 0x7f80_0000l 13 | let to_hex_string = Printf.sprintf "%lx" 14 | end) 15 | -------------------------------------------------------------------------------- /interpreter/exec/f32_convert.ml: -------------------------------------------------------------------------------- 1 | (* WebAssembly-compatible type conversions to f32 implementation *) 2 | 3 | let demote_f64 x = 4 | let xf = F64.to_float x in 5 | if xf = xf then F32.of_float xf else 6 | let nan64bits = F64.to_bits x in 7 | let sign_field = Int64.(shift_left (shift_right_logical nan64bits 63) 31) in 8 | let significand_field = Int64.(shift_right_logical (shift_left nan64bits 12) 41) in 9 | let fields = Int64.logor sign_field significand_field in 10 | let nan32bits = Int32.logor 0x7fc0_0000l (I32_convert.wrap_i64 fields) in 11 | F32.of_bits nan32bits 12 | 13 | let convert_i32_s x = 14 | F32.of_float (Int32.to_float x) 15 | 16 | (* 17 | * Similar to convert_i64_u below, the high half of the i32 range are beyond 18 | * the range where f32 can represent odd numbers, though we do need to adjust 19 | * the least significant bit to round correctly. 20 | *) 21 | let convert_i32_u x = 22 | F32.of_float Int32.( 23 | if x >= zero then to_float x else 24 | to_float (logor (shift_right_logical x 1) (logand x 1l)) *. 2.0 25 | ) 26 | 27 | (* 28 | * Values that are too large would get rounded when represented in f64, 29 | * but double rounding via i64->f64->f32 can produce inaccurate results. 30 | * Hence, for large values we shift right but make sure to accumulate the lost 31 | * bits in the least significant bit, such that rounding still is correct. 32 | *) 33 | let convert_i64_s x = 34 | F32.of_float Int64.( 35 | if abs x < 0x10_0000_0000_0000L then to_float x else 36 | let r = if logand x 0xfffL = 0L then 0L else 1L in 37 | to_float (logor (shift_right x 12) r) *. 0x1p12 38 | ) 39 | 40 | let convert_i64_u x = 41 | F32.of_float Int64.( 42 | if I64.lt_u x 0x10_0000_0000_0000L then to_float x else 43 | let r = if logand x 0xfffL = 0L then 0L else 1L in 44 | to_float (logor (shift_right_logical x 12) r) *. 0x1p12 45 | ) 46 | 47 | let reinterpret_i32 = F32.of_bits 48 | -------------------------------------------------------------------------------- /interpreter/exec/f32_convert.mli: -------------------------------------------------------------------------------- 1 | (* WebAssembly-compatible type conversions to f32 implementation *) 2 | 3 | val demote_f64 : F64.t -> F32.t 4 | val convert_i32_s : I32.t -> F32.t 5 | val convert_i32_u : I32.t -> F32.t 6 | val convert_i64_s : I64.t -> F32.t 7 | val convert_i64_u : I64.t -> F32.t 8 | val reinterpret_i32 : I32.t -> F32.t 9 | -------------------------------------------------------------------------------- /interpreter/exec/f64.ml: -------------------------------------------------------------------------------- 1 | include Fxx.Make 2 | (struct 3 | include Int64 4 | let mantissa = 52 5 | let pos_nan = 0x7ff8_0000_0000_0000L 6 | let neg_nan = 0xfff8_0000_0000_0000L 7 | let bare_nan = 0x7ff0_0000_0000_0000L 8 | let to_hex_string = Printf.sprintf "%Lx" 9 | end) 10 | -------------------------------------------------------------------------------- /interpreter/exec/f64_convert.ml: -------------------------------------------------------------------------------- 1 | (* WebAssembly-compatible type conversions to f64 implementation *) 2 | 3 | let promote_f32 x = 4 | let xf = F32.to_float x in 5 | if xf = xf then F64.of_float xf else 6 | let nan32bits = I64_convert.extend_i32_u (F32.to_bits x) in 7 | let sign_field = Int64.(shift_left (shift_right_logical nan32bits 31) 63) in 8 | let significand_field = Int64.(shift_right_logical (shift_left nan32bits 41) 12) in 9 | let fields = Int64.logor sign_field significand_field in 10 | let nan64bits = Int64.logor 0x7ff8_0000_0000_0000L fields in 11 | F64.of_bits nan64bits 12 | 13 | let convert_i32_s x = 14 | F64.of_float (Int32.to_float x) 15 | 16 | (* 17 | * Unlike the other convert_u functions, the high half of the i32 range is 18 | * within the range where f32 can represent odd numbers, so we can't do the 19 | * shift. Instead, we can use int64 signed arithmetic. 20 | *) 21 | let convert_i32_u x = 22 | F64.of_float Int64.(to_float (logand (of_int32 x) 0x0000_0000_ffff_ffffL)) 23 | 24 | let convert_i64_s x = 25 | F64.of_float (Int64.to_float x) 26 | 27 | (* 28 | * Values in the low half of the int64 range can be converted with a signed 29 | * conversion. The high half is beyond the range where f64 can represent odd 30 | * numbers, so we can shift the value right, adjust the least significant 31 | * bit to round correctly, do a conversion, and then scale it back up. 32 | *) 33 | let convert_i64_u x = 34 | F64.of_float Int64.( 35 | if x >= zero then to_float x else 36 | to_float (logor (shift_right_logical x 1) (logand x 1L)) *. 2.0 37 | ) 38 | 39 | let reinterpret_i64 = F64.of_bits 40 | -------------------------------------------------------------------------------- /interpreter/exec/f64_convert.mli: -------------------------------------------------------------------------------- 1 | (* WebAssembly-compatible type conversions to f64 implementation *) 2 | 3 | val promote_f32 : F32.t -> F64.t 4 | val convert_i32_s : I32.t -> F64.t 5 | val convert_i32_u : I32.t -> F64.t 6 | val convert_i64_s : I64.t -> F64.t 7 | val convert_i64_u : I64.t -> F64.t 8 | val reinterpret_i64 : I64.t -> F64.t 9 | -------------------------------------------------------------------------------- /interpreter/exec/i16.ml: -------------------------------------------------------------------------------- 1 | (* Uses Int32 as the underlying storage. All int16 values will be 2 | * stored signed-extended. E.g. -1 will be stored with all high bits set. 3 | *) 4 | include Ixx.Make (struct 5 | include Int32 6 | 7 | let bitwidth = 16 8 | let to_hex_string i = Printf.sprintf "%lx" (Int32.logand i 0xffffl) 9 | 10 | let of_int64 = Int64.to_int32 11 | let to_int64 = Int64.of_int32 12 | end) 13 | -------------------------------------------------------------------------------- /interpreter/exec/i32.ml: -------------------------------------------------------------------------------- 1 | (* WebAssembly-compatible i32 implementation *) 2 | 3 | include Ixx.Make 4 | (struct 5 | include Int32 6 | let bitwidth = 32 7 | let to_hex_string = Printf.sprintf "%lx" 8 | 9 | let of_int64 = Int64.to_int32 10 | let to_int64 = Int64.of_int32 11 | end) 12 | -------------------------------------------------------------------------------- /interpreter/exec/i32_convert.ml: -------------------------------------------------------------------------------- 1 | (* WebAssembly-compatible type conversions to i32 implementation *) 2 | 3 | let wrap_i64 x = Int64.to_int32 x 4 | 5 | let trunc_f32_s x = 6 | if F32.ne x x then 7 | raise Ixx.InvalidConversion 8 | else 9 | let xf = F32.to_float x in 10 | if xf >= -.Int32.(to_float min_int) || xf < Int32.(to_float min_int) then 11 | raise Ixx.Overflow 12 | else 13 | Int32.of_float xf 14 | 15 | let trunc_f32_u x = 16 | if F32.ne x x then 17 | raise Ixx.InvalidConversion 18 | else 19 | let xf = F32.to_float x in 20 | if xf >= -.Int32.(to_float min_int) *. 2.0 || xf <= -1.0 then 21 | raise Ixx.Overflow 22 | else 23 | Int64.(to_int32 (of_float xf)) 24 | 25 | let trunc_f64_s x = 26 | if F64.ne x x then 27 | raise Ixx.InvalidConversion 28 | else 29 | let xf = F64.to_float x in 30 | if xf >= -.Int32.(to_float min_int) || xf <= Int32.(to_float min_int) -. 1.0 then 31 | raise Ixx.Overflow 32 | else 33 | Int32.of_float xf 34 | 35 | let trunc_f64_u x = 36 | if F64.ne x x then 37 | raise Ixx.InvalidConversion 38 | else 39 | let xf = F64.to_float x in 40 | if xf >= -.Int32.(to_float min_int) *. 2.0 || xf <= -1.0 then 41 | raise Ixx.Overflow 42 | else 43 | Int64.(to_int32 (of_float xf)) 44 | 45 | let trunc_sat_f32_s x = 46 | if F32.ne x x then 47 | 0l 48 | else 49 | let xf = F32.to_float x in 50 | if xf < Int32.(to_float min_int) then 51 | Int32.min_int 52 | else if xf >= -.Int32.(to_float min_int) then 53 | Int32.max_int 54 | else 55 | Int32.of_float xf 56 | 57 | let trunc_sat_f32_u x = 58 | if F32.ne x x then 59 | 0l 60 | else 61 | let xf = F32.to_float x in 62 | if xf <= -1.0 then 63 | 0l 64 | else if xf >= -.Int32.(to_float min_int) *. 2.0 then 65 | -1l 66 | else 67 | Int64.(to_int32 (of_float xf)) 68 | 69 | let trunc_sat_f64_s x = 70 | if F64.ne x x then 71 | 0l 72 | else 73 | let xf = F64.to_float x in 74 | if xf < Int32.(to_float min_int) then 75 | Int32.min_int 76 | else if xf >= -.Int32.(to_float min_int) then 77 | Int32.max_int 78 | else 79 | Int32.of_float xf 80 | 81 | let trunc_sat_f64_u x = 82 | if F64.ne x x then 83 | 0l 84 | else 85 | let xf = F64.to_float x in 86 | if xf <= -1.0 then 87 | 0l 88 | else if xf >= -.Int32.(to_float min_int) *. 2.0 then 89 | -1l 90 | else 91 | Int64.(to_int32 (of_float xf)) 92 | 93 | let reinterpret_f32 = F32.to_bits 94 | -------------------------------------------------------------------------------- /interpreter/exec/i32_convert.mli: -------------------------------------------------------------------------------- 1 | (* WebAssembly-compatible type conversions to i32 implementation *) 2 | 3 | val wrap_i64 : I64.t -> I32.t 4 | val trunc_f32_s : F32.t -> I32.t 5 | val trunc_f32_u : F32.t -> I32.t 6 | val trunc_f64_s : F64.t -> I32.t 7 | val trunc_f64_u : F64.t -> I32.t 8 | val trunc_sat_f32_s : F32.t -> I32.t 9 | val trunc_sat_f32_u : F32.t -> I32.t 10 | val trunc_sat_f64_s : F64.t -> I32.t 11 | val trunc_sat_f64_u : F64.t -> I32.t 12 | val reinterpret_f32 : F32.t -> I32.t 13 | -------------------------------------------------------------------------------- /interpreter/exec/i64.ml: -------------------------------------------------------------------------------- 1 | (* WebAssembly-compatible i64 implementation *) 2 | 3 | include Ixx.Make 4 | (struct 5 | include Int64 6 | let bitwidth = 64 7 | let to_hex_string = Printf.sprintf "%Lx" 8 | 9 | let of_int64 i = i 10 | let to_int64 i = i 11 | end) 12 | -------------------------------------------------------------------------------- /interpreter/exec/i64_convert.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 Types 8 | open Value 9 | open Instance 10 | 11 | 12 | let error msg = raise (Eval.Crash (Source.no_region, msg)) 13 | 14 | let type_error v t = 15 | error 16 | ("type error, expected " ^ string_of_val_type t ^ 17 | ", got " ^ string_of_val_type (type_of_value v)) 18 | 19 | let empty = function 20 | | [] -> () 21 | | vs -> error "type error, too many arguments" 22 | 23 | let single = function 24 | | [] -> error "type error, missing arguments" 25 | | [v] -> v 26 | | vs -> error "type error, too many arguments" 27 | 28 | let int = function 29 | | Num (I32 i) -> Int32.to_int i 30 | | v -> type_error v (NumT I32T) 31 | 32 | 33 | let abort vs = 34 | empty vs; 35 | print_endline "Abort!"; 36 | exit (-1) 37 | 38 | let exit vs = 39 | exit (int (single vs)) 40 | 41 | 42 | let lookup name et = 43 | match Utf8.encode name, et with 44 | | "abort", ExternFuncT ft -> 45 | ExternFunc (Func.alloc_host (Types.alloc (DefFuncT ft)) abort) 46 | | "exit", ExternFuncT ft -> 47 | ExternFunc (Func.alloc_host (Types.alloc (DefFuncT ft)) exit) 48 | | _ -> raise Not_found 49 | -------------------------------------------------------------------------------- /interpreter/host/spectest.ml: -------------------------------------------------------------------------------- 1 | (* 2 | * Simple collection of functions useful for writing test cases. 3 | *) 4 | 5 | open Types 6 | open Value 7 | open Instance 8 | 9 | 10 | let global (GlobalT (_, t) as gt) = 11 | let v = 12 | match t with 13 | | NumT I32T -> Num (I32 666l) 14 | | NumT I64T -> Num (I64 666L) 15 | | NumT F32T -> Num (F32 (F32.of_float 666.6)) 16 | | NumT F64T -> Num (F64 (F64.of_float 666.6)) 17 | | VecT V128T -> Vec (V128 (V128.I32x4.of_lanes [666l; 666l; 666l; 666l])) 18 | | RefT (_, t) -> Ref (NullRef t) 19 | | BotT -> assert false 20 | in Global.alloc gt v 21 | 22 | let table = 23 | Table.alloc (TableT ({min = 10l; max = Some 20l}, (Null, FuncHT))) 24 | (NullRef FuncHT) 25 | let memory = Memory.alloc (MemoryT {min = 1l; max = Some 2l}) 26 | let func f ft = Func.alloc_host (Types.alloc (DefFuncT ft)) (f ft) 27 | 28 | let tag = 29 | let p = Types.alloc (DefFuncT (FuncT ([NumT I32T], [NumT I32T]))) in 30 | Tag.alloc (TagT (Dyn p)) 31 | let except = 32 | let p = Types.alloc (DefFuncT (FuncT ([NumT I32T], []))) in 33 | Tag.alloc (TagT (Dyn p)) 34 | 35 | let print_value v = 36 | Printf.printf "%s : %s\n" 37 | (string_of_value v) (string_of_val_type (type_of_value v)) 38 | 39 | let print _ vs = 40 | List.iter print_value vs; 41 | flush_all (); 42 | [] 43 | 44 | 45 | let lookup name t = 46 | match Utf8.encode name, t with 47 | | "print", _ -> ExternFunc (func print (FuncT ([], []))) 48 | | "print_i32", _ -> ExternFunc (func print (FuncT ([NumT I32T], []))) 49 | | "print_i64", _ -> ExternFunc (func print (FuncT ([NumT I64T], []))) 50 | | "print_f32", _ -> ExternFunc (func print (FuncT ([NumT F32T], []))) 51 | | "print_f64", _ -> ExternFunc (func print (FuncT ([NumT F64T], []))) 52 | | "print_i32_f32", _ -> ExternFunc (func print (FuncT ([NumT I32T; NumT F32T], []))) 53 | | "print_f64_f64", _ -> ExternFunc (func print (FuncT ([NumT F64T; NumT F64T], []))) 54 | | "global_i32", _ -> ExternGlobal (global (GlobalT (Cons, NumT I32T))) 55 | | "global_i64", _ -> ExternGlobal (global (GlobalT (Cons, NumT I64T))) 56 | | "global_f32", _ -> ExternGlobal (global (GlobalT (Cons, NumT F32T))) 57 | | "global_f64", _ -> ExternGlobal (global (GlobalT (Cons, NumT F64T))) 58 | | "table", _ -> ExternTable table 59 | | "memory", _ -> ExternMemory memory 60 | | "tag", _ -> ExternTag tag 61 | | "exception", _ -> ExternTag except 62 | | _ -> raise Not_found 63 | -------------------------------------------------------------------------------- /interpreter/main/flags.ml: -------------------------------------------------------------------------------- 1 | let interactive = ref false 2 | let trace = ref false 3 | let unchecked = ref false 4 | let print_sig = ref false 5 | let dry = ref false 6 | let width = ref 80 7 | let harness = ref true 8 | let budget = ref 256 9 | -------------------------------------------------------------------------------- /interpreter/main/main.ml: -------------------------------------------------------------------------------- 1 | let name = "wasm" 2 | let version = "2.1" 3 | 4 | let configure () = 5 | Import.register (Utf8.decode "spectest") Spectest.lookup; 6 | Import.register (Utf8.decode "env") Env.lookup 7 | 8 | let banner () = 9 | print_endline (name ^ " " ^ version ^ " reference interpreter") 10 | 11 | let usage = "Usage: " ^ name ^ " [option] [file ...]" 12 | 13 | let args = ref [] 14 | let add_arg source = args := !args @ [source] 15 | 16 | let quote s = "\"" ^ String.escaped s ^ "\"" 17 | 18 | let argspec = Arg.align 19 | [ 20 | "-", Arg.Set Flags.interactive, 21 | " run interactively (default if no files given)"; 22 | "-e", Arg.String add_arg, " evaluate string"; 23 | "-i", Arg.String (fun file -> add_arg ("(input " ^ quote file ^ ")")), 24 | " read script from file"; 25 | "-o", Arg.String (fun file -> add_arg ("(output " ^ quote file ^ ")")), 26 | " write module to file"; 27 | "-b", Arg.Int (fun n -> Flags.budget := n), 28 | " configure call depth budget (default is " ^ string_of_int !Flags.budget ^ ")"; 29 | "-w", Arg.Int (fun n -> Flags.width := n), 30 | " configure output width (default is " ^ string_of_int !Flags.width ^ ")"; 31 | "-s", Arg.Set Flags.print_sig, " show module signatures"; 32 | "-u", Arg.Set Flags.unchecked, " unchecked, do not perform validation"; 33 | "-j", Arg.Clear Flags.harness, " exclude harness for JS conversion"; 34 | "-d", Arg.Set Flags.dry, " dry, do not run program"; 35 | "-t", Arg.Set Flags.trace, " trace execution"; 36 | "-v", Arg.Unit banner, " show version" 37 | ] 38 | 39 | let () = 40 | Printexc.record_backtrace true; 41 | try 42 | configure (); 43 | Arg.parse argspec 44 | (fun file -> add_arg ("(input " ^ quote file ^ ")")) usage; 45 | List.iter (fun arg -> if not (Run.run_string arg) then exit 1) !args; 46 | if !args = [] then Flags.interactive := true; 47 | if !Flags.interactive then begin 48 | Flags.print_sig := true; 49 | banner (); 50 | Run.run_stdin () 51 | end 52 | with exn -> 53 | flush_all (); 54 | prerr_endline 55 | (Sys.argv.(0) ^ ": uncaught exception " ^ Printexc.to_string exn); 56 | Printexc.print_backtrace stderr; 57 | exit 2 58 | -------------------------------------------------------------------------------- /interpreter/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/wast.ml: -------------------------------------------------------------------------------- 1 | (* Implements a wrapper library that allows the use of the reference 2 | * interpreter's encode/decode functionality in JavaScript. 3 | *) 4 | open Js_of_ocaml 5 | 6 | let _ = 7 | Js.export "WebAssemblyText" 8 | (object%js (_self) 9 | 10 | method encode (s : Js.js_string Js.t) : (Typed_array.arrayBuffer Js.t) = 11 | let def = Parse.string_to_module (Js.to_string s) in 12 | let bs = 13 | match def.Source.it with 14 | | Script.Textual m -> (Encode.encode m) 15 | | Script.Encoded (_, bs) -> bs 16 | | Script.Quoted (_, _) -> failwith "Unsupported" in 17 | let buf = new%js Typed_array.arrayBuffer (String.length bs) in 18 | let u8arr = new%js Typed_array.uint8Array_fromBuffer buf in 19 | String.iteri (fun i c -> Typed_array.set u8arr i (int_of_char c)) bs; buf 20 | 21 | method decode (buf : Typed_array.arrayBuffer Js.t) width : (Js.js_string Js.t) = 22 | let s = Typed_array.String.of_uint8Array (new%js Typed_array.uint8Array_fromBuffer buf) in 23 | let m = Decode.decode "(decode)" s in 24 | Js.string (Sexpr.to_string width (Arrange.module_ m)) 25 | 26 | end) 27 | -------------------------------------------------------------------------------- /interpreter/runtime/data.ml: -------------------------------------------------------------------------------- 1 | type data = string ref 2 | type t = data 3 | 4 | let alloc bs = ref bs 5 | let size seg = I64.of_int_u (String.length !seg) 6 | let load seg i = (!seg).[Int64.to_int i] 7 | let drop seg = seg := "" 8 | -------------------------------------------------------------------------------- /interpreter/runtime/data.mli: -------------------------------------------------------------------------------- 1 | type data 2 | type t = data 3 | 4 | val alloc : string -> data 5 | val size : data -> Memory.address 6 | val load : data -> Memory.address -> char 7 | val drop : data -> unit 8 | -------------------------------------------------------------------------------- /interpreter/runtime/elem.ml: -------------------------------------------------------------------------------- 1 | type elem = Value.ref_ list ref 2 | type t = elem 3 | 4 | let alloc rs = ref rs 5 | let size seg = Lib.List32.length !seg 6 | let load seg i = Lib.List32.nth !seg i 7 | let drop seg = seg := [] 8 | -------------------------------------------------------------------------------- /interpreter/runtime/elem.mli: -------------------------------------------------------------------------------- 1 | open Value 2 | 3 | type elem 4 | type t = elem 5 | 6 | val alloc : ref_ list -> elem 7 | val size : elem -> Table.size 8 | val load : elem -> Table.index -> ref_ 9 | val drop : elem -> unit 10 | -------------------------------------------------------------------------------- /interpreter/runtime/func.ml: -------------------------------------------------------------------------------- 1 | open Types 2 | open Value 3 | 4 | type 'inst t = 'inst func 5 | and 'inst func = 6 | | AstFunc of type_addr * 'inst * Ast.func 7 | | HostFunc of type_addr * (value list -> value list) 8 | 9 | let alloc x inst f = AstFunc (x, inst, f) 10 | let alloc_host x f = HostFunc (x, f) 11 | 12 | let type_inst_of = function 13 | | AstFunc (x, _, _) -> x 14 | | HostFunc (x, _) -> x 15 | 16 | let type_of f = as_func_def_type (def_of (type_inst_of f)) 17 | -------------------------------------------------------------------------------- /interpreter/runtime/func.mli: -------------------------------------------------------------------------------- 1 | open Types 2 | open Value 3 | 4 | type 'inst t = 'inst func 5 | and 'inst func = 6 | | AstFunc of type_addr * 'inst * Ast.func 7 | | HostFunc of type_addr * (value list -> value list) 8 | 9 | val alloc : type_addr -> 'inst -> Ast.func -> 'inst func 10 | val alloc_host : type_addr -> (value list -> value list) -> 'inst func 11 | 12 | val type_of : 'inst func -> func_type 13 | val type_inst_of : 'inst func -> type_addr 14 | -------------------------------------------------------------------------------- /interpreter/runtime/global.ml: -------------------------------------------------------------------------------- 1 | open Types 2 | open Value 3 | 4 | type global = {ty : global_type; mutable content : value} 5 | type t = global 6 | 7 | exception Type 8 | exception NotMutable 9 | 10 | let alloc (GlobalT (_mut, t) as ty) v = 11 | if not (Match.match_val_type [] (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 GlobalT (mut, t) = glob.ty in 22 | if mut <> Var then raise NotMutable; 23 | if not (Match.match_val_type [] (type_of_value v) t) then raise Type; 24 | glob.content <- v 25 | -------------------------------------------------------------------------------- /interpreter/runtime/global.mli: -------------------------------------------------------------------------------- 1 | open Types 2 | open Value 3 | 4 | type global 5 | type t = global 6 | 7 | exception Type 8 | exception NotMutable 9 | 10 | val alloc : global_type -> value -> global (* raises Type *) 11 | val type_of : global -> global_type 12 | 13 | val load : global -> value 14 | val store : global -> value -> unit (* raises Type, NotMutable *) 15 | -------------------------------------------------------------------------------- /interpreter/runtime/instance.ml: -------------------------------------------------------------------------------- 1 | open Types 2 | 3 | type module_inst = 4 | { 5 | types : type_inst list; 6 | funcs : func_inst list; 7 | tables : table_inst list; 8 | memories : memory_inst list; 9 | globals : global_inst list; 10 | tags : tag_inst list; 11 | elems : elem_inst list; 12 | datas : data_inst list; 13 | exports : export_inst list; 14 | } 15 | 16 | and type_inst = type_addr 17 | and func_inst = module_inst Lib.Promise.t Func.t 18 | and table_inst = Table.t 19 | and memory_inst = Memory.t 20 | and global_inst = Global.t 21 | and tag_inst = Tag.t 22 | and elem_inst = Elem.t 23 | and data_inst = Data.t 24 | and export_inst = Ast.name * extern 25 | 26 | and extern = 27 | | ExternFunc of func_inst 28 | | ExternTable of table_inst 29 | | ExternMemory of memory_inst 30 | | ExternGlobal of global_inst 31 | | ExternTag of tag_inst 32 | 33 | 34 | (* Reference types *) 35 | 36 | type Value.ref_ += FuncRef of func_inst 37 | 38 | let () = 39 | let type_of_ref' = !Value.type_of_ref' in 40 | Value.type_of_ref' := function 41 | | FuncRef f -> DefHT (Dyn (Func.type_inst_of f)) 42 | | r -> type_of_ref' r 43 | 44 | let () = 45 | let string_of_ref' = !Value.string_of_ref' in 46 | Value.string_of_ref' := function 47 | | FuncRef _ -> "func" 48 | | r -> string_of_ref' r 49 | 50 | let () = 51 | let eq_ref' = !Value.eq_ref' in 52 | Value.eq_ref' := fun r1 r2 -> 53 | match r1, r2 with 54 | | FuncRef f1, FuncRef f2 -> f1 == f2 55 | | _, _ -> eq_ref' r1 r2 56 | 57 | 58 | (* Auxiliary functions *) 59 | 60 | let empty_module_inst = 61 | { types = []; funcs = []; tables = []; memories = []; globals = []; tags = []; 62 | elems = []; datas = []; exports = [] } 63 | 64 | let extern_type_of c = function 65 | | ExternFunc func -> ExternFuncT (Func.type_of func) 66 | | ExternTable tab -> ExternTableT (Table.type_of tab) 67 | | ExternMemory mem -> ExternMemoryT (Memory.type_of mem) 68 | | ExternGlobal glob -> ExternGlobalT (Global.type_of glob) 69 | | ExternTag tag -> ExternTagT (Tag.type_of tag) 70 | 71 | let export inst name = 72 | try Some (List.assoc name inst.exports) with Not_found -> None 73 | -------------------------------------------------------------------------------- /interpreter/runtime/memory.mli: -------------------------------------------------------------------------------- 1 | open Types 2 | open Value 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.pack_size -> Pack.extension -> memory -> address -> offset -> num_type -> num 38 | (* raises Type, Bounds *) 39 | val store_num_packed : 40 | Pack.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.pack_size -> Pack.vec_extension -> memory -> address -> offset -> vec_type -> vec 50 | (* raises Type, Bounds *) 51 | -------------------------------------------------------------------------------- /interpreter/runtime/table.ml: -------------------------------------------------------------------------------- 1 | open Types 2 | open Value 3 | 4 | type size = int32 5 | type index = int32 6 | type count = int32 7 | 8 | type table = {mutable ty : table_type; mutable content : ref_ array} 9 | type t = table 10 | 11 | exception Type 12 | exception Bounds 13 | exception SizeOverflow 14 | exception SizeLimit 15 | exception OutOfMemory 16 | 17 | let valid_limits {min; max} = 18 | match max with 19 | | None -> true 20 | | Some m -> I32.le_u min m 21 | 22 | let create size r = 23 | try Lib.Array32.make size r 24 | with Out_of_memory | Invalid_argument _ -> raise OutOfMemory 25 | 26 | let alloc (TableT (lim, _t) as ty) r = 27 | 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 TableT (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 <- TableT (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 TableT (lim, t) = tab.ty in 54 | if not (Match.match_ref_type [] (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 Value 3 | 4 | type table 5 | type t = table 6 | 7 | type size = int32 8 | type index = int32 9 | type count = int32 10 | 11 | exception Type 12 | exception Bounds 13 | exception SizeOverflow 14 | exception SizeLimit 15 | exception OutOfMemory 16 | 17 | val alloc : table_type -> ref_ -> table (* raises Type, OutOfMemory *) 18 | val type_of : table -> table_type 19 | val size : table -> size 20 | val grow : table -> size -> ref_ -> unit 21 | (* raises SizeOverflow, SizeLimit, OutOfMemory *) 22 | 23 | val load : table -> index -> ref_ (* raises Bounds *) 24 | val store : table -> index -> ref_ -> unit (* raises Type, Bounds *) 25 | val blit : table -> index -> ref_ list -> unit (* raises Bounds *) 26 | -------------------------------------------------------------------------------- /interpreter/runtime/tag.ml: -------------------------------------------------------------------------------- 1 | open Types 2 | 3 | type tag = {ty : tag_type} 4 | type t = tag 5 | 6 | let alloc ty = 7 | {ty} 8 | 9 | let type_of tag = 10 | tag.ty 11 | -------------------------------------------------------------------------------- /interpreter/runtime/tag.mli: -------------------------------------------------------------------------------- 1 | open Types 2 | 3 | type tag 4 | type t = tag 5 | 6 | val alloc : tag_type -> tag 7 | val type_of : tag -> tag_type 8 | -------------------------------------------------------------------------------- /interpreter/script/import.ml: -------------------------------------------------------------------------------- 1 | open Source 2 | open Ast 3 | open Types 4 | 5 | module Unknown = Error.Make () 6 | exception Unknown = Unknown.Error (* indicates unknown import name *) 7 | 8 | module Registry = Map.Make(struct type t = Ast.name let compare = compare end) 9 | let registry = ref Registry.empty 10 | 11 | let register name lookup = registry := Registry.add name lookup !registry 12 | 13 | let lookup (ImportT (et, module_name, item_name)) at : Instance.extern = 14 | try Registry.find module_name !registry item_name et with Not_found -> 15 | Unknown.error at 16 | ("unknown import \"" ^ Types.string_of_name module_name ^ 17 | "\".\"" ^ Types.string_of_name item_name ^ "\"") 18 | 19 | let link m = 20 | let ModuleT (_, its, _) = Types.dyn_module_type (module_type_of m) in 21 | List.map2 lookup its (List.map Source.at m.it.imports) 22 | -------------------------------------------------------------------------------- /interpreter/script/import.mli: -------------------------------------------------------------------------------- 1 | exception Unknown of Source.region * string 2 | 3 | val link : Ast.module_ -> Instance.extern list (* raises Unknown *) 4 | 5 | val register : 6 | Ast.name -> 7 | (Ast.name -> Types.extern_type -> Instance.extern (* raises Not_found *)) -> 8 | unit 9 | -------------------------------------------------------------------------------- /interpreter/script/js.mli: -------------------------------------------------------------------------------- 1 | val of_script : Script.script -> string 2 | -------------------------------------------------------------------------------- /interpreter/script/run.mli: -------------------------------------------------------------------------------- 1 | exception Abort of Source.region * string 2 | exception Assert of Source.region * string 3 | exception IO of Source.region * string 4 | 5 | val trace : string -> unit 6 | 7 | val run_string : string -> bool 8 | val run_file : string -> bool 9 | val run_stdin : unit -> unit 10 | -------------------------------------------------------------------------------- /interpreter/script/script.ml: -------------------------------------------------------------------------------- 1 | type var = string Source.phrase 2 | 3 | type Value.ref_ += ExternRef of int32 4 | type num = Value.num Source.phrase 5 | type ref_ = Value.ref_ Source.phrase 6 | type literal = Value.t Source.phrase 7 | 8 | type definition = definition' Source.phrase 9 | and definition' = 10 | | Textual of Ast.module_ 11 | | Encoded of string * string 12 | | Quoted of string * string 13 | 14 | type action = action' Source.phrase 15 | and action' = 16 | | Invoke of var option * Ast.name * literal list 17 | | Get of var option * Ast.name 18 | 19 | type nanop = nanop' Source.phrase 20 | and nanop' = (Lib.void, Lib.void, nan, nan) Value.op 21 | and nan = CanonicalNan | ArithmeticNan 22 | 23 | type num_pat = 24 | | NumPat of num 25 | | NanPat of nanop 26 | 27 | type vec_pat = 28 | | VecPat of (V128.shape * num_pat list) Value.vecop 29 | 30 | type ref_pat = 31 | | RefPat of ref_ 32 | | RefTypePat of Types.heap_type 33 | | NullPat 34 | 35 | type result = result' Source.phrase 36 | and result' = 37 | | NumResult of num_pat 38 | | VecResult of vec_pat 39 | | RefResult of ref_pat 40 | 41 | type assertion = assertion' Source.phrase 42 | and assertion' = 43 | | AssertMalformed of definition * string 44 | | AssertInvalid of definition * string 45 | | AssertUnlinkable of definition * string 46 | | AssertUninstantiable of definition * string 47 | | AssertReturn of action * result list 48 | | AssertTrap of action * string 49 | | AssertException of action * string 50 | | AssertSuspension of action * string 51 | | AssertExhaustion of action * string 52 | 53 | type command = command' Source.phrase 54 | and command' = 55 | | Module of var option * definition 56 | | Register of Ast.name * var option 57 | | Action of action 58 | | Assertion of assertion 59 | | Meta of meta 60 | 61 | and meta = meta' Source.phrase 62 | and meta' = 63 | | Input of var option * string 64 | | Output of var option * string option 65 | | Script of var option * script 66 | 67 | and script = command list 68 | 69 | exception Syntax of Source.region * string 70 | 71 | 72 | let () = 73 | let type_of_ref' = !Value.type_of_ref' in 74 | Value.type_of_ref' := function 75 | | ExternRef _ -> Types.ExternHT 76 | | r -> type_of_ref' r 77 | 78 | let () = 79 | let string_of_ref' = !Value.string_of_ref' in 80 | Value.string_of_ref' := function 81 | | ExternRef n -> "ref " ^ Int32.to_string n 82 | | r -> string_of_ref' r 83 | 84 | let () = 85 | let eq_ref' = !Value.eq_ref' in 86 | Value.eq_ref' := fun r1 r2 -> 87 | match r1, r2 with 88 | | ExternRef n1, ExternRef n2 -> n1 = n2 89 | | _, _ -> eq_ref' r1 r2 90 | -------------------------------------------------------------------------------- /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 | tags : Set.t; 10 | funcs : Set.t; 11 | elems : Set.t; 12 | datas : Set.t; 13 | locals : Set.t; 14 | labels : Set.t; 15 | } 16 | 17 | val empty : t 18 | val union : t -> t -> t 19 | 20 | val instr : Ast.instr -> t 21 | val block : Ast.instr list -> t 22 | val const : Ast.const -> t 23 | 24 | val type_ : Ast.type_ -> t 25 | val global : Ast.global -> t 26 | val func : Ast.func -> t 27 | val table : Ast.table -> t 28 | val memory : Ast.memory -> t 29 | val tag : Ast.tag -> t 30 | val elem : Ast.elem_segment -> t 31 | val data : Ast.data_segment -> t 32 | val export : Ast.export -> t 33 | val import : Ast.import -> t 34 | val start : Ast.start -> t 35 | 36 | val module_ : Ast.module_ -> t 37 | 38 | val opt : ('a -> t) -> 'a option -> t 39 | val list : ('a -> t) -> 'a list -> t 40 | -------------------------------------------------------------------------------- /interpreter/syntax/pack.ml: -------------------------------------------------------------------------------- 1 | type pack_size = Pack8 | Pack16 | Pack32 | Pack64 2 | type extension = SX | ZX 3 | 4 | type pack_shape = Pack8x8 | Pack16x4 | Pack32x2 5 | type vec_extension = 6 | | ExtLane of pack_shape * extension 7 | | ExtSplat 8 | | ExtZero 9 | 10 | let packed_size = function 11 | | Pack8 -> 1 12 | | Pack16 -> 2 13 | | Pack32 -> 4 14 | | Pack64 -> 8 15 | 16 | let packed_shape_size = function 17 | | Pack8x8 | Pack16x4 | Pack32x2 -> 8 18 | -------------------------------------------------------------------------------- /interpreter/text/arrange.mli: -------------------------------------------------------------------------------- 1 | open Sexpr 2 | 3 | val instr : Ast.instr -> sexpr 4 | val func : Ast.func -> sexpr 5 | val module_ : Ast.module_ -> sexpr 6 | val script : [`Textual | `Binary] -> Script.script -> sexpr list 7 | -------------------------------------------------------------------------------- /interpreter/text/lexer.mli: -------------------------------------------------------------------------------- 1 | val convert_pos : Lexing.position -> Source.pos 2 | 3 | val token : Lexing.lexbuf -> Parser.token (* raises Source.Error *) 4 | -------------------------------------------------------------------------------- /interpreter/text/parse.ml: -------------------------------------------------------------------------------- 1 | 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 flip : ('a -> 'b -> 'c) -> ('b -> 'a -> 'c) 9 | val curry : ('a * 'b -> 'c) -> ('a -> 'b -> 'c) 10 | val uncurry : ('a -> 'b -> 'c) -> ('a * 'b -> 'c) 11 | 12 | val repeat : int -> ('a -> 'a) -> 'a -> 'a 13 | end 14 | 15 | module List : 16 | sig 17 | val make : int -> 'a -> 'a list 18 | val table : int -> (int -> 'a) -> 'a list 19 | val take : int -> 'a list -> 'a list (* raises Failure *) 20 | val drop : int -> 'a list -> 'a list (* raises Failure *) 21 | val split : int -> 'a list -> 'a list * 'a list (* raises Failure *) 22 | 23 | val last_opt : 'a list -> 'a option 24 | val last : 'a list -> 'a (* raises Failure *) 25 | val split_last : 'a list -> 'a list * 'a (* raises Failure *) 26 | 27 | val index_of : 'a -> 'a list -> int option 28 | val index_where : ('a -> bool) -> 'a list -> int option 29 | val map_filter : ('a -> 'b option) -> 'a list -> 'b list 30 | val concat_map : ('a -> 'b list) -> 'a list -> 'b list 31 | val pairwise : ('a -> 'a -> 'b) -> 'a list -> 'b list 32 | end 33 | 34 | module List32 : 35 | sig 36 | val make : int32 -> 'a -> 'a list 37 | val length : 'a list -> int32 38 | val nth : 'a list -> int32 -> 'a (* raises Failure *) 39 | val replace : 'a list -> int32 -> 'a -> 'a list (* raises Failure *) 40 | val take : int32 -> 'a list -> 'a list (* raises Failure *) 41 | val drop : int32 -> 'a list -> 'a list (* raises Failure *) 42 | val mapi : (int32 -> 'a -> 'b) -> 'a list -> 'b list 43 | 44 | val index_of : 'a -> 'a list -> int32 option 45 | val index_where : ('a -> bool) -> 'a list -> int32 option 46 | end 47 | 48 | module Array32 : 49 | sig 50 | val make : int32 -> 'a -> 'a array 51 | val length : 'a array -> int32 52 | val get : 'a array -> int32 -> 'a 53 | val set : 'a array -> int32 -> 'a -> unit 54 | val blit : 'a array -> int32 -> 'a array -> int32 -> int32 -> unit 55 | end 56 | 57 | module Bigarray : 58 | sig 59 | open Bigarray 60 | 61 | module Array1_64 : 62 | sig 63 | val create : ('a, 'b) kind -> 'c layout -> int64 -> ('a, 'b, 'c) Array1.t 64 | val dim : ('a, 'b, 'c) Array1.t -> int64 65 | val get : ('a, 'b, 'c) Array1.t -> int64 -> 'a 66 | val set : ('a, 'b, 'c) Array1.t -> int64 -> 'a -> unit 67 | val sub : ('a, 'b, 'c) Array1.t -> int64 -> int64 -> ('a, 'b, 'c) Array1.t 68 | end 69 | end 70 | 71 | module Option : 72 | sig 73 | val get : 'a option -> 'a -> 'a 74 | val force : 'a option -> 'a (* raises Invalid_argument *) 75 | val map : ('a -> 'b) -> 'a option -> 'b option 76 | val app : ('a -> unit) -> 'a option -> unit 77 | end 78 | 79 | module Int : 80 | sig 81 | val log2 : int -> int 82 | val is_power_of_two : int -> bool 83 | end 84 | 85 | module String : 86 | sig 87 | val implode : char list -> string 88 | val explode : string -> char list 89 | val split : string -> char -> string list 90 | val breakup : string -> int -> string list 91 | val find_from_opt : (char -> bool) -> string -> int -> int option 92 | end 93 | 94 | module Promise : 95 | sig 96 | type 'a t 97 | exception Promise 98 | val make : unit -> 'a t 99 | val fulfill : 'a t -> 'a -> unit 100 | val value : 'a t -> 'a 101 | val value_opt : 'a t -> 'a option 102 | end 103 | -------------------------------------------------------------------------------- /interpreter/util/sexpr.ml: -------------------------------------------------------------------------------- 1 | type sexpr = Atom of string | Node of string * sexpr list 2 | 3 | type rope = Leaf of string | Concat of rope list 4 | let (^+) s r = Concat [Leaf s; r] 5 | let (+^) r s = Concat [r; Leaf s] 6 | 7 | let rec iter f = function 8 | | Leaf s -> f s 9 | | Concat rs -> List.iter (iter f) rs 10 | 11 | let rec concat = function 12 | | Leaf s -> s 13 | | Concat rs -> String.concat "" (List.map concat rs) 14 | 15 | let rec pp off width = function 16 | | Atom s -> String.length s, Leaf s 17 | | Node (s, xs) -> 18 | let lens, rs = List.split (List.map (pp (off + 2) width) xs) in 19 | let len = String.length s + List.length rs + List.fold_left (+) 2 lens in 20 | let sep, fin = 21 | if off + len <= width then " ", "" 22 | else let indent = String.make off ' ' in "\n " ^ indent, "\n" ^ indent 23 | in len, "(" ^+ s ^+ Concat (List.map (fun r -> sep ^+ r) rs) +^ fin +^ ")" 24 | 25 | let output oc width x = 26 | iter (output_string oc) (snd (pp 0 width x)); 27 | output_string oc "\n"; 28 | flush oc 29 | 30 | let print = output stdout 31 | 32 | let to_string width x = concat (snd (pp 0 width x)) ^ "\n" 33 | -------------------------------------------------------------------------------- /interpreter/util/sexpr.mli: -------------------------------------------------------------------------------- 1 | type sexpr = Atom of string | Node of string * sexpr list 2 | 3 | val output : out_channel -> int -> sexpr -> unit 4 | val print : int -> sexpr -> unit 5 | val to_string : int -> sexpr -> string 6 | -------------------------------------------------------------------------------- /interpreter/util/source.ml: -------------------------------------------------------------------------------- 1 | type pos = {file : string; line : int; column : int} 2 | type region = {left : pos; right : pos} 3 | type 'a phrase = {at : region; it : 'a} 4 | 5 | let (@@) x region = {it = x; at = region} 6 | let it phrase = phrase.it 7 | let at phrase = phrase.at 8 | 9 | 10 | (* Positions and regions *) 11 | 12 | let no_pos = {file = ""; line = 0; column = 0} 13 | let no_region = {left = no_pos; right = no_pos} 14 | 15 | let string_of_pos pos = 16 | if pos.line = -1 then 17 | Printf.sprintf "0x%x" pos.column 18 | else 19 | string_of_int pos.line ^ "." ^ string_of_int (pos.column + 1) 20 | 21 | let string_of_region r = 22 | r.left.file ^ ":" ^ string_of_pos r.left ^ 23 | (if r.right = r.left then "" else "-" ^ string_of_pos r.right) 24 | -------------------------------------------------------------------------------- /interpreter/util/source.mli: -------------------------------------------------------------------------------- 1 | type pos = {file : string; line : int; column : int} 2 | type region = {left : pos; right : pos} 3 | type 'a phrase = {at : region; it : 'a} 4 | 5 | val no_pos : pos 6 | val no_region : region 7 | 8 | val string_of_pos : pos -> string 9 | val string_of_region : region -> string 10 | 11 | val (@@) : 'a -> region -> 'a phrase 12 | val it : 'a phrase -> 'a 13 | val at : 'a phrase -> region 14 | -------------------------------------------------------------------------------- /interpreter/valid/match.mli: -------------------------------------------------------------------------------- 1 | open Types 2 | 3 | (* Context *) 4 | 5 | type context = def_type list 6 | 7 | (* Equivalence *) 8 | 9 | val eq_num_type : context -> num_type -> num_type -> bool 10 | val eq_ref_type : context -> ref_type -> ref_type -> bool 11 | val eq_val_type : context -> val_type -> val_type -> bool 12 | 13 | val eq_result_type : context -> result_type -> result_type -> bool 14 | 15 | val eq_func_type : context -> func_type -> func_type -> bool 16 | val eq_table_type : context -> table_type -> table_type -> bool 17 | val eq_memory_type : context -> memory_type -> memory_type -> bool 18 | val eq_global_type : context -> global_type -> global_type -> bool 19 | 20 | val eq_extern_type : context -> extern_type -> extern_type -> bool 21 | 22 | val eq_def_type : context -> def_type -> def_type -> bool 23 | 24 | (* Subtyping *) 25 | 26 | val match_num_type : context -> num_type -> num_type -> bool 27 | val match_ref_type : context -> ref_type -> ref_type -> bool 28 | val match_val_type : context -> val_type -> val_type -> bool 29 | 30 | val match_result_type : context -> result_type -> result_type -> bool 31 | 32 | val match_func_type : context -> func_type -> func_type -> bool 33 | val match_table_type : context -> table_type -> table_type -> bool 34 | val match_memory_type : context -> memory_type -> memory_type -> bool 35 | val match_global_type : context -> global_type -> global_type -> bool 36 | 37 | val match_extern_type : context -> extern_type -> extern_type -> bool 38 | 39 | val match_def_type : context -> def_type -> def_type -> bool 40 | -------------------------------------------------------------------------------- /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/effect-handlers/wasm-spec/8c5d4f66db6405d57a7346ebbf2d207fe78d73fd/papers/oopsla2019.pdf -------------------------------------------------------------------------------- /papers/pldi2017.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/effect-handlers/wasm-spec/8c5d4f66db6405d57a7346ebbf2d207fe78d73fd/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/continuations/examples/fun-pipes.wast: -------------------------------------------------------------------------------- 1 | ;; Simple pipes example (functional version) 2 | (module $pipes 3 | (type $pfun (func (result i32))) 4 | (type $cfun (func (param i32) (result i32))) 5 | (type $producer (cont $pfun)) 6 | (type $consumer (cont $cfun)) 7 | 8 | (tag $send (export "send") (param i32)) 9 | (tag $receive (export "receive") (result i32)) 10 | 11 | (func $piper (param $n i32) (param $p (ref $producer)) (param $c (ref $consumer)) 12 | (block $on-receive (result (ref $consumer)) 13 | (resume $consumer (tag $receive $on-receive) (local.get $n) (local.get $c)) 14 | (return) 15 | ) ;; receive 16 | (local.set $c) 17 | (return_call $copiper (local.get $c) (local.get $p)) 18 | ) 19 | 20 | (func $copiper (param $c (ref $consumer)) (param $p (ref $producer)) 21 | (local $n i32) 22 | (block $on-send (result i32 (ref $producer)) 23 | (resume $producer (tag $send $on-send) (local.get $p)) 24 | (return) 25 | ) ;; send 26 | (local.set $p) 27 | (local.set $n) 28 | (return_call $piper (local.get $n) (local.get $p) (local.get $c)) 29 | ) 30 | 31 | (func $pipe (export "pipe") (param $p (ref $producer)) (param $c (ref $consumer)) 32 | (call $piper (i32.const -1) (local.get $p) (local.get $c)) 33 | ) 34 | ) 35 | (register "pipes") 36 | 37 | (module 38 | (type $pfun (func (result i32))) 39 | (type $cfun (func (param i32) (result i32))) 40 | 41 | (type $producer (cont $pfun)) 42 | (type $consumer (cont $cfun)) 43 | 44 | (tag $send (import "pipes" "send") (param i32)) 45 | (tag $receive (import "pipes" "receive") (result i32)) 46 | 47 | (func $pipe (import "pipes" "pipe") (param $p (ref $producer)) (param $c (ref $consumer))) 48 | 49 | (func $log (import "spectest" "print_i32") (param i32)) 50 | 51 | (elem declare func $nats $sum) 52 | 53 | ;; send n, n+1, ... 54 | (func $nats (param $n i32) (result i32) 55 | (loop $l 56 | (call $log (i32.const -1)) 57 | (call $log (local.get $n)) 58 | (suspend $send (local.get $n)) 59 | (local.set $n (i32.add (local.get $n) (i32.const 1))) 60 | (br $l) 61 | ) 62 | (unreachable) 63 | ) 64 | 65 | ;; receive 10 nats and return their sum 66 | (func $sum (param $dummy i32) (result i32) 67 | (local $i i32) 68 | (local $a i32) 69 | (local.set $i (i32.const 10)) 70 | (local.set $a (i32.const 0)) 71 | (loop $l 72 | (local.set $a (i32.add (local.get $a) (suspend $receive))) 73 | (call $log (i32.const -2)) 74 | (call $log (local.get $a)) 75 | (local.set $i (i32.sub (local.get $i) (i32.const 1))) 76 | (br_if $l (i32.ne (local.get $i) (i32.const 0))) 77 | ) 78 | (return (local.get $a)) 79 | ) 80 | 81 | (func (export "run") (param $n i32) 82 | (call $pipe (cont.bind $consumer $producer (local.get $n) (cont.new $consumer (ref.func $nats))) 83 | (cont.new $consumer (ref.func $sum)) 84 | ) 85 | ) 86 | ) 87 | 88 | (invoke "run" (i32.const 0)) 89 | -------------------------------------------------------------------------------- /proposals/continuations/examples/fun-state.wast: -------------------------------------------------------------------------------- 1 | ;; Simple state example - functional with heterogeneous continuations 2 | (module $state 3 | (tag $get (result i32)) 4 | (tag $set (param i32)) 5 | 6 | (type $gf (func (param i32) (result i32))) 7 | (type $sf (func (result i32))) 8 | 9 | (type $gk (cont $gf)) 10 | (type $sk (cont $sf)) 11 | 12 | (func $getting (param $k (ref $gk)) (param $s i32) (result i32) 13 | (block $on_get (result (ref $gk)) 14 | (block $on_set (result i32 (ref $sk)) 15 | (resume $gk (tag $get $on_get) (tag $set $on_set) 16 | (local.get $s) (local.get $k) 17 | ) 18 | (return) 19 | ) ;; $on_set (result i32 (ref $sk)) 20 | (return_call $setting) 21 | ) ;; $on_get (result (ref $gk)) 22 | (local.get $s) 23 | (return_call $getting) 24 | ) 25 | 26 | (func $setting (param $s i32) (param $k (ref $sk)) (result i32) 27 | (block $on_get (result (ref $gk)) 28 | (block $on_set (result i32 (ref $sk)) 29 | (resume $sk (tag $get $on_get) (tag $set $on_set) 30 | (local.get $k) 31 | ) 32 | (return) 33 | ) ;; $on_set (result i32 (ref $sk)) 34 | (return_call $setting) 35 | ) ;; $on_get (result (ref $gk)) 36 | (local.get $s) 37 | (return_call $getting) 38 | ) 39 | 40 | (func $f (result i32) 41 | (suspend $set (i32.const 7)) 42 | (i32.add 43 | (suspend $get) 44 | (i32.mul 45 | (i32.const 2) 46 | (suspend $set (i32.const 3)) 47 | (i32.add 48 | (i32.const 3) 49 | (suspend $get) 50 | ) 51 | ) 52 | ) 53 | ) 54 | 55 | (elem declare func $f) 56 | (func (export "run") (result i32) 57 | (call $setting (i32.const 0) (cont.new $sk (ref.func $f))) 58 | ) 59 | ) 60 | 61 | (assert_return (invoke "run") (i32.const 19)) 62 | -------------------------------------------------------------------------------- /proposals/continuations/examples/pipes.wast: -------------------------------------------------------------------------------- 1 | ;; Simple pipes example 2 | (module $pipes 3 | (type $pfun (func (result i32))) 4 | (type $cfun (func (param i32) (result i32))) 5 | (type $producer (cont $pfun)) 6 | (type $consumer (cont $cfun)) 7 | 8 | (tag $send (export "send") (param i32)) 9 | (tag $receive (export "receive") (result i32)) 10 | 11 | (func $piper (export "pipe") (param $p (ref $producer)) (param $c (ref $consumer)) 12 | (local $n i32) 13 | (local $consuming i32) 14 | 15 | (local.set $n (i32.const -1)) 16 | (local.set $consuming (i32.const 1)) 17 | 18 | (loop $l 19 | (if (local.get $consuming) 20 | (then 21 | (block $on-receive (result (ref $consumer)) 22 | (resume $consumer (tag $receive $on-receive) (local.get $n) (local.get $c)) 23 | (return) 24 | ) ;; receive 25 | (local.set $c) 26 | (local.set $consuming (i32.const 0)) 27 | (br $l) 28 | ) 29 | ) ;; else producing 30 | (block $on-send (result i32 (ref $producer)) 31 | (resume $producer (tag $send $on-send) (local.get $p)) 32 | (return) 33 | ) ;; send 34 | (local.set $p) 35 | (local.set $n) 36 | (local.set $consuming (i32.const 1)) 37 | (br $l) 38 | ) 39 | ) 40 | ) 41 | 42 | (register "pipes") 43 | 44 | (module 45 | (type $pfun (func (result i32))) 46 | (type $cfun (func (param i32) (result i32))) 47 | 48 | (type $producer (cont $pfun)) 49 | (type $consumer (cont $cfun)) 50 | 51 | (tag $send (import "pipes" "send") (param i32)) 52 | (tag $receive (import "pipes" "receive") (result i32)) 53 | 54 | (func $pipe (import "pipes" "pipe") (param $p (ref $producer)) (param $c (ref $consumer))) 55 | 56 | (func $log (import "spectest" "print_i32") (param i32)) 57 | 58 | (elem declare func $nats $sum) 59 | 60 | ;; send n, n+1, ... 61 | (func $nats (param $n i32) (result i32) 62 | (loop $l 63 | (call $log (i32.const -1)) 64 | (call $log (local.get $n)) 65 | (suspend $send (local.get $n)) 66 | (local.set $n (i32.add (local.get $n) (i32.const 1))) 67 | (br $l) 68 | ) 69 | (unreachable) 70 | ) 71 | 72 | ;; receive 10 nats and return their sum 73 | (func $sum (param $dummy i32) (result i32) 74 | (local $i i32) 75 | (local $a i32) 76 | (local.set $i (i32.const 10)) 77 | (local.set $a (i32.const 0)) 78 | (loop $l 79 | (local.set $a (i32.add (local.get $a) (suspend $receive))) 80 | (call $log (i32.const -2)) 81 | (call $log (local.get $a)) 82 | (local.set $i (i32.sub (local.get $i) (i32.const 1))) 83 | (br_if $l (i32.ne (local.get $i) (i32.const 0))) 84 | ) 85 | (return (local.get $a)) 86 | ) 87 | 88 | (func (export "run") (param $n i32) 89 | (call $pipe (cont.bind $consumer $producer (local.get $n) (cont.new $consumer (ref.func $nats))) 90 | (cont.new $consumer (ref.func $sum)) 91 | ) 92 | ) 93 | ) 94 | 95 | (invoke "run" (i32.const 0)) 96 | -------------------------------------------------------------------------------- /proposals/sign-extension-ops/Overview.md: -------------------------------------------------------------------------------- 1 | # Sign-extension operators proposal for WebAssembly 2 | 3 | This page describes a proposal for the post-MVP 4 | [sign-extension operator feature][future sext]. 5 | 6 | This proposal adds five new integer instructions for sign-extending 8-bit, 7 | 16-bit, and 32-bit values. 8 | 9 | ## New Sign-extending Operators 10 | 11 | To support sign-extending, five new sign-extension operators are added: 12 | 13 | * `i32.extend8_s`: extend a signed 8-bit integer to a 32-bit integer 14 | * `i32.extend16_s`: extend a signed 16-bit integer to a 32-bit integer 15 | * `i64.extend8_s`: extend a signed 8-bit integer to a 64-bit integer 16 | * `i64.extend16_s`: extend a signed 16-bit integer to a 64-bit integer 17 | * `i64.extend32_s`: extend a signed 32-bit integer to a 64-bit integer 18 | 19 | Note that `i64.extend32_s` was not originally included when this proposal was 20 | discussed in the May 2017 CG meeting. The reason given was that 21 | the behavior matches `i64.extend_s/i32`. It was later discovered that this is 22 | not correct, as `i64.extend_s/i32` sign-extends an `i32` value to `i64`, 23 | whereas `i64.extend32_s` sign-extends an `i64` value to `i64`. The behavior 24 | of `i64.extend32_s` can be emulated with `i32.wrap/i64` followed by 25 | `i64.extend_s/i32`, but the same can be said of the sign-extending load 26 | operations. Therefore, `i64.extend32_s` has been added for consistency. 27 | 28 | ## [Spec Changes][spec] 29 | 30 | The [instruction syntax][] is modified as follows: 31 | 32 | ``` 33 | instr ::= ... | 34 | inn.extend8_s | inn.extend16_s | i64.extend32_s 35 | ``` 36 | 37 | The [instruction binary format][] is modified as follows: 38 | 39 | ``` 40 | instr ::= ... 41 | | 0xC0 => i32.extend8_s 42 | | 0xC1 => i32.extend16_s 43 | | 0xC2 => i64.extend8_s 44 | | 0xC3 => i64.extend16_s 45 | | 0xC4 => i64.extend32_s 46 | ``` 47 | 48 | [future sext]: https://github.com/WebAssembly/design/blob/main/FutureFeatures.md#additional-integer-operators 49 | [instruction syntax]: https://webassembly.github.io/spec/syntax/instructions.html 50 | [instruction binary format]: https://webassembly.github.io/spec/binary/instructions.html 51 | [spec]: https://webassembly.github.io/sign-extension-ops/ 52 | -------------------------------------------------------------------------------- /proposals/simd/TextSIMD.md: -------------------------------------------------------------------------------- 1 | # Text format for SIMD 2 | 3 | ### v128.const 4 | 5 | The `v128.const` instruction has multiple valid text formats corresponding to 6 | different lane interpretations. The valid text formats are 7 | 8 | ``` 9 | v128.const i8x16 i8 i8 i8 i8 i8 i8 i8 i8 i8 i8 i8 i8 i8 i8 i8 i8 10 | v128.const i16x8 i16 i16 i16 i16 i16 i16 i16 i16 11 | v128.const i32x4 i32 i32 i32 i32 12 | v128.const i64x2 i64 i64 13 | v128.const f32x4 f32 f32 f32 f32 14 | v128.const f64x2 f64 f64 15 | ``` 16 | 17 | The canonical text format used for printing `v128.const` instructions is 18 | 19 | ``` 20 | v128.const i32x4 0xNNNNNNNN 0xNNNNNNNN 0xNNNNNNNN 0xNNNNNNNN 21 | ``` 22 | 23 | ### i8x16.shuffle 24 | 25 | ``` 26 | i8x16.shuffle i5 i5 i5 i5 i5 i5 i5 i5 i5 i5 i5 i5 i5 i5 i5 i5 27 | ``` 28 | -------------------------------------------------------------------------------- /proposals/simd/WebAssembly-SIMD-May-2017.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/effect-handlers/wasm-spec/8c5d4f66db6405d57a7346ebbf2d207fe78d73fd/proposals/simd/WebAssembly-SIMD-May-2017.pdf -------------------------------------------------------------------------------- /test/README.md: -------------------------------------------------------------------------------- 1 | This directory contains the WebAssembly test suite. It is split into three 2 | directories: 3 | 4 | * [`core/`](core/), tests for the core semantics 5 | * [`js-api/`](js-api/), tests for the JavaScript API. 6 | * [`html/`](html/), tests for the JavaScript API in a DOM environment. 7 | 8 | A list of to-do's can be found [here](Todo.md). 9 | 10 | ## Multi-stage testing 11 | 12 | The wast tests can be converted to JavaScript, and the JavaScript tests 13 | to HTML tests, using the `build.py` script. It will create a `out/` directory 14 | (checked in in this repository, to be able to use it from github pages), 15 | containing subdirectories with expanded tests, as well as a landing page for 16 | runnning all of them in HTML. 17 | 18 | The HTML tests are just [Web Platform Tests](http://testthewebforward.org) 19 | using the 20 | [testharness.js](https://web-platform-tests.org/writing-tests/testharness-api.html) 21 | library. 22 | 23 | Each wast test gets its equivalent JS test, and each JS test (including wast 24 | test) gets its equivalent WPT, to be easily run in browser vendors' automation. 25 | 26 | ## Procedure for adding a new test 27 | 28 | - put the test in the right directory according to the above (top) description. 29 | - ideally, commit here so the actual content commit and build commit are 30 | separated. 31 | - re-run `build.py` so that the landing page is updated and all the cascading 32 | happens. 33 | - re-commit here, if necessary. 34 | 35 | ## Local HTTP serving of the repository 36 | 37 | From the root of your clone of this repository: 38 | 39 | ``` 40 | python -m SimpleHTTPServer 8000 41 | ``` 42 | 43 | Then open your favorite browser and browse to `http://localhost:8000/test/out`. 44 | -------------------------------------------------------------------------------- /test/Todo.md: -------------------------------------------------------------------------------- 1 | This is a rough list of "tests to write". Everything here should either be 2 | specified in [Semantics.md](https://github.com/WebAssembly/design/blob/main/Semantics.md), 3 | have a link to an open issue/PR, or be obvious. Comments/corrections/additions 4 | welcome. 5 | 6 | Linear memory semantics: 7 | - test that one can clobber the entire contents of the linear memory without corrupting: call stack, local variables, program execution. 8 | 9 | Misc optimizer bait: 10 | - test that the scheduler doesn't move a trapping div past a call which may not return 11 | - test that linearized multidimensional array accesses can have overindexing in interesting ways 12 | - test that 32-bit loop induction variables that wrap aren't promoted to 64-bit 13 | - test that code after a non-obviously infinite loop is not executed 14 | 15 | Misc x86 optimizer bait: 16 | - test that oeq handles NaN right in if, if-else, and setcc cases 17 | 18 | SIMD (post-MVP): 19 | - test that SIMD insert/extract don't canonicalize NaNs 20 | - test that SIMD lanes are in little-endian order 21 | - test non-constant-index and out-of-bounds shuffle masks 22 | - test that subnormals work as intended 23 | - test that byte-misaligned accesses work 24 | 25 | Threads (post-MVP): 26 | - test that thread-local variables are actually thread-local 27 | - test that atomic operations that isLockFree says are lock-free actually are 28 | (is this possible?) 29 | - test that isLockFree is true for datatypes that the spec says should 30 | always be lock-free 31 | - test that 16-bit and 8-bit cmpxchg does a wrapped 8-bit or 16-bit compare 32 | 33 | FMA (post-MVP): 34 | - http://www.vinc17.org/software/fma-tests.c 35 | -------------------------------------------------------------------------------- /test/core/.gitignore: -------------------------------------------------------------------------------- 1 | output -------------------------------------------------------------------------------- /test/core/README.md: -------------------------------------------------------------------------------- 1 | This directory contains tests for the core WebAssembly semantics, as described in [Semantics.md](https://github.com/WebAssembly/design/blob/main/Semantics.md) and specified by the [spec interpreter](https://github.com/WebAssembly/spec/blob/main/interpreter). 2 | 3 | Tests are written in the [S-Expression script format](https://github.com/WebAssembly/spec/blob/main/interpreter/README.md#s-expression-syntax) defined by the interpreter. 4 | 5 | The test suite can be run with the spec interpreter as follows: 6 | ``` 7 | ./run.py --wasm 8 | ``` 9 | where the path points to the spec interpreter executable (or a tool that understands similar options). If the binary is in the working directory, this option can be omitted. 10 | 11 | In addition, the option `--js ` can be given to point to a stand-alone JavaScript interpreter supporting the WebAssembly API. If provided, all tests are also executed in JavaScript. 12 | -------------------------------------------------------------------------------- /test/core/br_on_non_null.wast: -------------------------------------------------------------------------------- 1 | (module 2 | (type $t (func (result i32))) 3 | 4 | (func $nn (param $r (ref $t)) (result i32) 5 | (call_ref $t 6 | (block $l (result (ref $t)) 7 | (br_on_non_null $l (local.get $r)) 8 | (return (i32.const -1)) 9 | ) 10 | ) 11 | ) 12 | (func $n (param $r (ref null $t)) (result i32) 13 | (call_ref $t 14 | (block $l (result (ref $t)) 15 | (br_on_non_null $l (local.get $r)) 16 | (return (i32.const -1)) 17 | ) 18 | ) 19 | ) 20 | 21 | (elem func $f) 22 | (func $f (result i32) (i32.const 7)) 23 | 24 | (func (export "nullable-null") (result i32) (call $n (ref.null $t))) 25 | (func (export "nonnullable-f") (result i32) (call $nn (ref.func $f))) 26 | (func (export "nullable-f") (result i32) (call $n (ref.func $f))) 27 | 28 | (func (export "unreachable") (result i32) 29 | (block $l (result (ref $t)) 30 | (br_on_non_null $l (unreachable)) 31 | (return (i32.const -1)) 32 | ) 33 | (call_ref $t) 34 | ) 35 | ) 36 | 37 | (assert_trap (invoke "unreachable") "unreachable") 38 | 39 | (assert_return (invoke "nullable-null") (i32.const -1)) 40 | (assert_return (invoke "nonnullable-f") (i32.const 7)) 41 | (assert_return (invoke "nullable-f") (i32.const 7)) 42 | 43 | (module 44 | (type $t (func)) 45 | (func (param $r (ref null $t)) (drop (block (result (ref $t)) (br_on_non_null 0 (local.get $r)) (unreachable)))) 46 | (func (param $r (ref null func)) (drop (block (result (ref func)) (br_on_non_null 0 (local.get $r)) (unreachable)))) 47 | (func (param $r (ref null extern)) (drop (block (result (ref extern)) (br_on_non_null 0 (local.get $r)) (unreachable)))) 48 | ) 49 | 50 | 51 | (module 52 | (type $t (func (param i32) (result i32))) 53 | (elem func $f) 54 | (func $f (param i32) (result i32) (i32.mul (local.get 0) (local.get 0))) 55 | 56 | (func $a (param $n i32) (param $r (ref null $t)) (result i32) 57 | (call_ref $t 58 | (block $l (result i32 (ref $t)) 59 | (return (br_on_non_null $l (local.get $n) (local.get $r))) 60 | ) 61 | ) 62 | ) 63 | 64 | (func (export "args-null") (param $n i32) (result i32) 65 | (call $a (local.get $n) (ref.null $t)) 66 | ) 67 | (func (export "args-f") (param $n i32) (result i32) 68 | (call $a (local.get $n) (ref.func $f)) 69 | ) 70 | ) 71 | 72 | (assert_return (invoke "args-null" (i32.const 3)) (i32.const 3)) 73 | (assert_return (invoke "args-f" (i32.const 3)) (i32.const 9)) 74 | -------------------------------------------------------------------------------- /test/core/br_on_null.wast: -------------------------------------------------------------------------------- 1 | (module 2 | (type $t (func (result i32))) 3 | 4 | (func $nn (param $r (ref $t)) (result i32) 5 | (block $l 6 | (return (call_ref $t (br_on_null $l (local.get $r)))) 7 | ) 8 | (i32.const -1) 9 | ) 10 | (func $n (param $r (ref null $t)) (result i32) 11 | (block $l 12 | (return (call_ref $t (br_on_null $l (local.get $r)))) 13 | ) 14 | (i32.const -1) 15 | ) 16 | 17 | (elem func $f) 18 | (func $f (result i32) (i32.const 7)) 19 | 20 | (func (export "nullable-null") (result i32) (call $n (ref.null $t))) 21 | (func (export "nonnullable-f") (result i32) (call $nn (ref.func $f))) 22 | (func (export "nullable-f") (result i32) (call $n (ref.func $f))) 23 | 24 | (func (export "unreachable") (result i32) 25 | (block $l 26 | (return (call_ref $t (br_on_null $l (unreachable)))) 27 | ) 28 | (i32.const -1) 29 | ) 30 | ) 31 | 32 | (assert_trap (invoke "unreachable") "unreachable") 33 | 34 | (assert_return (invoke "nullable-null") (i32.const -1)) 35 | (assert_return (invoke "nonnullable-f") (i32.const 7)) 36 | (assert_return (invoke "nullable-f") (i32.const 7)) 37 | 38 | (module 39 | (type $t (func)) 40 | (func (param $r (ref null $t)) (drop (br_on_null 0 (local.get $r)))) 41 | (func (param $r (ref null func)) (drop (br_on_null 0 (local.get $r)))) 42 | (func (param $r (ref null extern)) (drop (br_on_null 0 (local.get $r)))) 43 | ) 44 | 45 | 46 | (module 47 | (type $t (func (param i32) (result i32))) 48 | (elem func $f) 49 | (func $f (param i32) (result i32) (i32.mul (local.get 0) (local.get 0))) 50 | 51 | (func $a (param $n i32) (param $r (ref null $t)) (result i32) 52 | (block $l (result i32) 53 | (return (call_ref $t (br_on_null $l (local.get $n) (local.get $r)))) 54 | ) 55 | ) 56 | 57 | (func (export "args-null") (param $n i32) (result i32) 58 | (call $a (local.get $n) (ref.null $t)) 59 | ) 60 | (func (export "args-f") (param $n i32) (result i32) 61 | (call $a (local.get $n) (ref.func $f)) 62 | ) 63 | ) 64 | 65 | (assert_return (invoke "args-null" (i32.const 3)) (i32.const 3)) 66 | (assert_return (invoke "args-f" (i32.const 3)) (i32.const 9)) 67 | -------------------------------------------------------------------------------- /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/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/local_init.wast: -------------------------------------------------------------------------------- 1 | ;; Uninitialized undefaulted locals 2 | 3 | (module 4 | (func (export "get-after-set") (param $p (ref extern)) (result (ref extern)) 5 | (local $x (ref extern)) 6 | (local.set $x (local.get $p)) 7 | (local.get $x) 8 | ) 9 | (func (export "get-after-tee") (param $p (ref extern)) (result (ref extern)) 10 | (local $x (ref extern)) 11 | (drop (local.tee $x (local.get $p))) 12 | (local.get $x) 13 | ) 14 | (func (export "get-in-block-after-set") (param $p (ref extern)) (result (ref extern)) 15 | (local $x (ref extern)) 16 | (local.set $x (local.get $p)) 17 | (block (result (ref extern)) (local.get $x)) 18 | ) 19 | ) 20 | 21 | (assert_return (invoke "get-after-set" (ref.extern 1)) (ref.extern 1)) 22 | (assert_return (invoke "get-after-tee" (ref.extern 2)) (ref.extern 2)) 23 | (assert_return (invoke "get-in-block-after-set" (ref.extern 3)) (ref.extern 3)) 24 | 25 | (assert_invalid 26 | (module (func $uninit (local $x (ref extern)) (drop (local.get $x)))) 27 | "uninitialized local" 28 | ) 29 | (assert_invalid 30 | (module 31 | (func $uninit-after-end (param $p (ref extern)) 32 | (local $x (ref extern)) 33 | (block (local.set $x (local.get $p)) (drop (local.tee $x (local.get $p)))) 34 | (drop (local.get $x)) 35 | ) 36 | ) 37 | "uninitialized local" 38 | ) 39 | (assert_invalid 40 | (module 41 | (func $uninit-in-else (param $p (ref extern)) 42 | (local $x (ref extern)) 43 | (if (i32.const 0) 44 | (then (local.set $x (local.get $p))) 45 | (else (local.get $x)) 46 | ) 47 | ) 48 | ) 49 | "uninitialized local" 50 | ) 51 | 52 | (assert_invalid 53 | (module 54 | (func $uninit-from-if (param $p (ref extern)) 55 | (local $x (ref extern)) 56 | (if (i32.const 0) 57 | (then (local.set $x (local.get $p))) 58 | (else (local.set $x (local.get $p))) 59 | ) 60 | (drop (local.get $x)) 61 | ) 62 | ) 63 | "uninitialized local" 64 | ) 65 | 66 | (module 67 | (func (export "tee-init") (param $p (ref extern)) (result (ref extern)) 68 | (local $x (ref extern)) 69 | (drop (local.tee $x (local.get $p))) 70 | (local.get $x) 71 | ) 72 | ) 73 | 74 | (assert_return (invoke "tee-init" (ref.extern 1)) (ref.extern 1)) 75 | -------------------------------------------------------------------------------- /test/core/memory_redundancy.wast: -------------------------------------------------------------------------------- 1 | ;; Test that optimizers don't do redundant-load, store-to-load, or dead-store 2 | ;; optimizations when there are interfering stores, even of different types 3 | ;; and to non-identical addresses. 4 | 5 | (module 6 | (memory 1 1) 7 | 8 | (func (export "zero_everything") 9 | (i32.store (i32.const 0) (i32.const 0)) 10 | (i32.store (i32.const 4) (i32.const 0)) 11 | (i32.store (i32.const 8) (i32.const 0)) 12 | (i32.store (i32.const 12) (i32.const 0)) 13 | ) 14 | 15 | (func (export "test_store_to_load") (result i32) 16 | (i32.store (i32.const 8) (i32.const 0)) 17 | (f32.store (i32.const 5) (f32.const -0.0)) 18 | (i32.load (i32.const 8)) 19 | ) 20 | 21 | (func (export "test_redundant_load") (result i32) 22 | (local $t i32) 23 | (local $s i32) 24 | (local.set $t (i32.load (i32.const 8))) 25 | (i32.store (i32.const 5) (i32.const 0x80000000)) 26 | (local.set $s (i32.load (i32.const 8))) 27 | (i32.add (local.get $t) (local.get $s)) 28 | ) 29 | 30 | (func (export "test_dead_store") (result f32) 31 | (local $t f32) 32 | (i32.store (i32.const 8) (i32.const 0x23232323)) 33 | (local.set $t (f32.load (i32.const 11))) 34 | (i32.store (i32.const 8) (i32.const 0)) 35 | (local.get $t) 36 | ) 37 | 38 | ;; A function named "malloc" which implementations nonetheless shouldn't 39 | ;; assume behaves like C malloc. 40 | (func $malloc (export "malloc") 41 | (param $size i32) 42 | (result i32) 43 | (i32.const 16) 44 | ) 45 | 46 | ;; Call malloc twice, but unlike C malloc, we don't get non-aliasing pointers. 47 | (func (export "malloc_aliasing") 48 | (result i32) 49 | (local $x i32) 50 | (local $y i32) 51 | (local.set $x (call $malloc (i32.const 4))) 52 | (local.set $y (call $malloc (i32.const 4))) 53 | (i32.store (local.get $x) (i32.const 42)) 54 | (i32.store (local.get $y) (i32.const 43)) 55 | (i32.load (local.get $x)) 56 | ) 57 | ) 58 | 59 | (assert_return (invoke "test_store_to_load") (i32.const 0x00000080)) 60 | (invoke "zero_everything") 61 | (assert_return (invoke "test_redundant_load") (i32.const 0x00000080)) 62 | (invoke "zero_everything") 63 | (assert_return (invoke "test_dead_store") (f32.const 0x1.18p-144)) 64 | (invoke "zero_everything") 65 | (assert_return (invoke "malloc_aliasing") (i32.const 43)) 66 | -------------------------------------------------------------------------------- /test/core/memory_size.wast: -------------------------------------------------------------------------------- 1 | (module 2 | (memory 0) 3 | (func (export "size") (result i32) (memory.size)) 4 | (func (export "grow") (param $sz i32) (drop (memory.grow (local.get $sz)))) 5 | ) 6 | 7 | (assert_return (invoke "size") (i32.const 0)) 8 | (assert_return (invoke "grow" (i32.const 1))) 9 | (assert_return (invoke "size") (i32.const 1)) 10 | (assert_return (invoke "grow" (i32.const 4))) 11 | (assert_return (invoke "size") (i32.const 5)) 12 | (assert_return (invoke "grow" (i32.const 0))) 13 | (assert_return (invoke "size") (i32.const 5)) 14 | 15 | (module 16 | (memory 1) 17 | (func (export "size") (result i32) (memory.size)) 18 | (func (export "grow") (param $sz i32) (drop (memory.grow (local.get $sz)))) 19 | ) 20 | 21 | (assert_return (invoke "size") (i32.const 1)) 22 | (assert_return (invoke "grow" (i32.const 1))) 23 | (assert_return (invoke "size") (i32.const 2)) 24 | (assert_return (invoke "grow" (i32.const 4))) 25 | (assert_return (invoke "size") (i32.const 6)) 26 | (assert_return (invoke "grow" (i32.const 0))) 27 | (assert_return (invoke "size") (i32.const 6)) 28 | 29 | (module 30 | (memory 0 2) 31 | (func (export "size") (result i32) (memory.size)) 32 | (func (export "grow") (param $sz i32) (drop (memory.grow (local.get $sz)))) 33 | ) 34 | 35 | (assert_return (invoke "size") (i32.const 0)) 36 | (assert_return (invoke "grow" (i32.const 3))) 37 | (assert_return (invoke "size") (i32.const 0)) 38 | (assert_return (invoke "grow" (i32.const 1))) 39 | (assert_return (invoke "size") (i32.const 1)) 40 | (assert_return (invoke "grow" (i32.const 0))) 41 | (assert_return (invoke "size") (i32.const 1)) 42 | (assert_return (invoke "grow" (i32.const 4))) 43 | (assert_return (invoke "size") (i32.const 1)) 44 | (assert_return (invoke "grow" (i32.const 1))) 45 | (assert_return (invoke "size") (i32.const 2)) 46 | 47 | (module 48 | (memory 3 8) 49 | (func (export "size") (result i32) (memory.size)) 50 | (func (export "grow") (param $sz i32) (drop (memory.grow (local.get $sz)))) 51 | ) 52 | 53 | (assert_return (invoke "size") (i32.const 3)) 54 | (assert_return (invoke "grow" (i32.const 1))) 55 | (assert_return (invoke "size") (i32.const 4)) 56 | (assert_return (invoke "grow" (i32.const 3))) 57 | (assert_return (invoke "size") (i32.const 7)) 58 | (assert_return (invoke "grow" (i32.const 0))) 59 | (assert_return (invoke "size") (i32.const 7)) 60 | (assert_return (invoke "grow" (i32.const 2))) 61 | (assert_return (invoke "size") (i32.const 7)) 62 | (assert_return (invoke "grow" (i32.const 1))) 63 | (assert_return (invoke "size") (i32.const 8)) 64 | 65 | 66 | ;; Type errors 67 | 68 | (assert_invalid 69 | (module 70 | (memory 1) 71 | (func $type-result-i32-vs-empty 72 | (memory.size) 73 | ) 74 | ) 75 | "type mismatch" 76 | ) 77 | (assert_invalid 78 | (module 79 | (memory 1) 80 | (func $type-result-i32-vs-f32 (result f32) 81 | (memory.size) 82 | ) 83 | ) 84 | "type mismatch" 85 | ) 86 | -------------------------------------------------------------------------------- /test/core/ref.wast: -------------------------------------------------------------------------------- 1 | ;; Syntax 2 | 3 | (module 4 | (type $t (func)) 5 | 6 | (func 7 | (param 8 | funcref 9 | externref 10 | (ref func) 11 | (ref extern) 12 | (ref 0) 13 | (ref $t) 14 | (ref 0) 15 | (ref $t) 16 | (ref null func) 17 | (ref null extern) 18 | (ref null 0) 19 | (ref null $t) 20 | ) 21 | ) 22 | ) 23 | 24 | 25 | ;; Undefined type index. 26 | 27 | (assert_invalid 28 | (module (type $type-func-param-invalid (func (param (ref 1))))) 29 | "unknown type" 30 | ) 31 | (assert_invalid 32 | (module (type $type-func-result-invalid (func (result (ref 1))))) 33 | "unknown type" 34 | ) 35 | 36 | (assert_invalid 37 | (module (global $global-invalid (ref null 1) (ref.null 1))) 38 | "unknown type" 39 | ) 40 | 41 | (assert_invalid 42 | (module (table $table-invalid 10 (ref null 1))) 43 | "unknown type" 44 | ) 45 | 46 | (assert_invalid 47 | (module (elem $elem-invalid (ref 1))) 48 | "unknown type" 49 | ) 50 | 51 | (assert_invalid 52 | (module (func $func-param-invalid (param (ref 1)))) 53 | "unknown type" 54 | ) 55 | (assert_invalid 56 | (module (func $func-result-invalid (result (ref 1)))) 57 | "unknown type" 58 | ) 59 | (assert_invalid 60 | (module (func $func-local-invalid (local (ref null 1)))) 61 | "unknown type" 62 | ) 63 | 64 | (assert_invalid 65 | (module (func $block-result-invalid (drop (block (result (ref 1)) (unreachable))))) 66 | "unknown type" 67 | ) 68 | (assert_invalid 69 | (module (func $loop-result-invalid (drop (loop (result (ref 1)) (unreachable))))) 70 | "unknown type" 71 | ) 72 | (assert_invalid 73 | (module (func $if-invalid (drop (if (result (ref 1)) (then) (else))))) 74 | "unknown type" 75 | ) 76 | 77 | (assert_invalid 78 | (module (func $select-result-invalid (drop (select (result (ref 1)) (unreachable))))) 79 | "unknown type" 80 | ) 81 | -------------------------------------------------------------------------------- /test/core/ref_as_non_null.wast: -------------------------------------------------------------------------------- 1 | (module 2 | (type $t (func (result i32))) 3 | 4 | (func $nn (param $r (ref $t)) (result i32) 5 | (call_ref $t (ref.as_non_null (local.get $r))) 6 | ) 7 | (func $n (param $r (ref null $t)) (result i32) 8 | (call_ref $t (ref.as_non_null (local.get $r))) 9 | ) 10 | 11 | (elem func $f) 12 | (func $f (result i32) (i32.const 7)) 13 | 14 | (func (export "nullable-null") (result i32) (call $n (ref.null $t))) 15 | (func (export "nonnullable-f") (result i32) (call $nn (ref.func $f))) 16 | (func (export "nullable-f") (result i32) (call $n (ref.func $f))) 17 | 18 | (func (export "unreachable") (result i32) 19 | (unreachable) 20 | (ref.as_non_null) 21 | (call $nn) 22 | ) 23 | ) 24 | 25 | (assert_trap (invoke "unreachable") "unreachable") 26 | 27 | (assert_trap (invoke "nullable-null") "null reference") 28 | (assert_return (invoke "nonnullable-f") (i32.const 7)) 29 | (assert_return (invoke "nullable-f") (i32.const 7)) 30 | 31 | (assert_invalid 32 | (module 33 | (type $t (func (result i32))) 34 | (func $g (param $r (ref $t)) (drop (ref.as_non_null (local.get $r)))) 35 | (func (call $g (ref.null $t))) 36 | ) 37 | "type mismatch" 38 | ) 39 | 40 | 41 | (module 42 | (type $t (func)) 43 | (func (param $r (ref $t)) (drop (ref.as_non_null (local.get $r)))) 44 | (func (param $r (ref func)) (drop (ref.as_non_null (local.get $r)))) 45 | (func (param $r (ref extern)) (drop (ref.as_non_null (local.get $r)))) 46 | ) 47 | -------------------------------------------------------------------------------- /test/core/ref_is_null.wast: -------------------------------------------------------------------------------- 1 | (module 2 | (type $t (func)) 3 | (func $dummy) 4 | 5 | (func $f1 (export "funcref") (param $x funcref) (result i32) 6 | (ref.is_null (local.get $x)) 7 | ) 8 | (func $f2 (export "externref") (param $x externref) (result i32) 9 | (ref.is_null (local.get $x)) 10 | ) 11 | (func $f3 (param $x (ref null $t)) (result i32) 12 | (ref.is_null (local.get $x)) 13 | ) 14 | (func $f3' (export "ref-null") (result i32) 15 | (call $f3 (ref.null $t)) 16 | ) 17 | 18 | (table $t1 2 funcref) 19 | (table $t2 2 externref) 20 | (table $t3 2 (ref null $t)) 21 | (elem (table $t1) (i32.const 1) func $dummy) 22 | (elem (table $t3) (i32.const 1) (ref $t) (ref.func $dummy)) 23 | 24 | (func (export "init") (param $r externref) 25 | (table.set $t2 (i32.const 1) (local.get $r)) 26 | ) 27 | (func (export "deinit") 28 | (table.set $t1 (i32.const 1) (ref.null func)) 29 | (table.set $t2 (i32.const 1) (ref.null extern)) 30 | (table.set $t3 (i32.const 1) (ref.null $t)) 31 | ) 32 | 33 | (func (export "funcref-elem") (param $x i32) (result i32) 34 | (call $f1 (table.get $t1 (local.get $x))) 35 | ) 36 | (func (export "externref-elem") (param $x i32) (result i32) 37 | (call $f2 (table.get $t2 (local.get $x))) 38 | ) 39 | (func (export "ref-elem") (param $x i32) (result i32) 40 | (call $f3 (table.get $t3 (local.get $x))) 41 | ) 42 | ) 43 | 44 | (assert_return (invoke "funcref" (ref.null func)) (i32.const 1)) 45 | (assert_return (invoke "externref" (ref.null extern)) (i32.const 1)) 46 | (assert_return (invoke "ref-null") (i32.const 1)) 47 | 48 | (assert_return (invoke "externref" (ref.extern 1)) (i32.const 0)) 49 | 50 | (invoke "init" (ref.extern 0)) 51 | 52 | (assert_return (invoke "funcref-elem" (i32.const 0)) (i32.const 1)) 53 | (assert_return (invoke "externref-elem" (i32.const 0)) (i32.const 1)) 54 | (assert_return (invoke "ref-elem" (i32.const 0)) (i32.const 1)) 55 | 56 | (assert_return (invoke "funcref-elem" (i32.const 1)) (i32.const 0)) 57 | (assert_return (invoke "externref-elem" (i32.const 1)) (i32.const 0)) 58 | (assert_return (invoke "ref-elem" (i32.const 1)) (i32.const 0)) 59 | 60 | (invoke "deinit") 61 | 62 | (assert_return (invoke "funcref-elem" (i32.const 0)) (i32.const 1)) 63 | (assert_return (invoke "externref-elem" (i32.const 0)) (i32.const 1)) 64 | (assert_return (invoke "ref-elem" (i32.const 0)) (i32.const 1)) 65 | 66 | (assert_return (invoke "funcref-elem" (i32.const 1)) (i32.const 1)) 67 | (assert_return (invoke "externref-elem" (i32.const 1)) (i32.const 1)) 68 | (assert_return (invoke "ref-elem" (i32.const 1)) (i32.const 1)) 69 | 70 | 71 | (module 72 | (type $t (func)) 73 | (func (param $r (ref $t)) (drop (ref.is_null (local.get $r)))) 74 | (func (param $r (ref func)) (drop (ref.is_null (local.get $r)))) 75 | (func (param $r (ref extern)) (drop (ref.is_null (local.get $r)))) 76 | ) 77 | 78 | (assert_invalid 79 | (module (func $ref-vs-num (param i32) (ref.is_null (local.get 0)))) 80 | "type mismatch" 81 | ) 82 | (assert_invalid 83 | (module (func $ref-vs-empty (ref.is_null))) 84 | "type mismatch" 85 | ) 86 | -------------------------------------------------------------------------------- /test/core/ref_null.wast: -------------------------------------------------------------------------------- 1 | (module 2 | (type $t (func)) 3 | (func (export "externref") (result externref) (ref.null extern)) 4 | (func (export "funcref") (result funcref) (ref.null func)) 5 | (func (export "ref") (result (ref null $t)) (ref.null $t)) 6 | 7 | (global externref (ref.null extern)) 8 | (global funcref (ref.null func)) 9 | (global (ref null $t) (ref.null $t)) 10 | ) 11 | 12 | (assert_return (invoke "externref") (ref.null extern)) 13 | (assert_return (invoke "funcref") (ref.null func)) 14 | (assert_return (invoke "ref") (ref.null)) 15 | -------------------------------------------------------------------------------- /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_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/simd/simd_linking.wast: -------------------------------------------------------------------------------- 1 | (module 2 | (global (export "g-v128") v128 (v128.const i64x2 0 0)) 3 | (global (export "mg-v128") (mut v128) (v128.const i64x2 0 0)) 4 | ) 5 | (register "Mv128") 6 | 7 | (module 8 | ;; TODO: Reactivate once the fix for https://bugs.chromium.org/p/v8/issues/detail?id=13732 9 | ;; has made it to the downstream node.js that we use on CI. 10 | ;; (import "Mv128" "g-v128" (global v128)) 11 | (import "Mv128" "mg-v128" (global (mut v128))) 12 | ) 13 | -------------------------------------------------------------------------------- /test/core/start.wast: -------------------------------------------------------------------------------- 1 | (assert_invalid 2 | (module (func) (start 1)) 3 | "unknown function" 4 | ) 5 | 6 | (assert_invalid 7 | (module 8 | (func $main (result i32) (return (i32.const 0))) 9 | (start $main) 10 | ) 11 | "start function" 12 | ) 13 | (assert_invalid 14 | (module 15 | (func $main (param $a i32)) 16 | (start $main) 17 | ) 18 | "start function" 19 | ) 20 | 21 | (module 22 | (memory (data "A")) 23 | (func $inc 24 | (i32.store8 25 | (i32.const 0) 26 | (i32.add 27 | (i32.load8_u (i32.const 0)) 28 | (i32.const 1) 29 | ) 30 | ) 31 | ) 32 | (func $get (result i32) 33 | (return (i32.load8_u (i32.const 0))) 34 | ) 35 | (func $main 36 | (call $inc) 37 | (call $inc) 38 | (call $inc) 39 | ) 40 | 41 | (start $main) 42 | (export "inc" (func $inc)) 43 | (export "get" (func $get)) 44 | ) 45 | (assert_return (invoke "get") (i32.const 68)) 46 | (invoke "inc") 47 | (assert_return (invoke "get") (i32.const 69)) 48 | (invoke "inc") 49 | (assert_return (invoke "get") (i32.const 70)) 50 | 51 | (module 52 | (memory (data "A")) 53 | (func $inc 54 | (i32.store8 55 | (i32.const 0) 56 | (i32.add 57 | (i32.load8_u (i32.const 0)) 58 | (i32.const 1) 59 | ) 60 | ) 61 | ) 62 | (func $get (result i32) 63 | (return (i32.load8_u (i32.const 0))) 64 | ) 65 | (func $main 66 | (call $inc) 67 | (call $inc) 68 | (call $inc) 69 | ) 70 | (start 2) 71 | (export "inc" (func $inc)) 72 | (export "get" (func $get)) 73 | ) 74 | (assert_return (invoke "get") (i32.const 68)) 75 | (invoke "inc") 76 | (assert_return (invoke "get") (i32.const 69)) 77 | (invoke "inc") 78 | (assert_return (invoke "get") (i32.const 70)) 79 | 80 | (module 81 | (func $print_i32 (import "spectest" "print_i32") (param i32)) 82 | (func $main (call $print_i32 (i32.const 1))) 83 | (start 1) 84 | ) 85 | 86 | (module 87 | (func $print_i32 (import "spectest" "print_i32") (param i32)) 88 | (func $main (call $print_i32 (i32.const 2))) 89 | (start $main) 90 | ) 91 | 92 | (module 93 | (func $print (import "spectest" "print")) 94 | (start $print) 95 | ) 96 | 97 | (assert_trap 98 | (module (func $main (unreachable)) (start $main)) 99 | "unreachable" 100 | ) 101 | 102 | (assert_malformed 103 | (module quote "(module (func $a (unreachable)) (func $b (unreachable)) (start $a) (start $b))") 104 | "multiple start sections" 105 | ) 106 | -------------------------------------------------------------------------------- /test/core/table-sub.wast: -------------------------------------------------------------------------------- 1 | (module 2 | (type $t (func)) 3 | (table $t1 10 (ref null func)) 4 | (table $t2 10 (ref null $t)) 5 | (elem $el funcref) 6 | (func $f 7 | (table.init $t1 $el (i32.const 0) (i32.const 1) (i32.const 2)) 8 | (table.copy $t1 $t2 (i32.const 0) (i32.const 1) (i32.const 2)) 9 | ) 10 | ) 11 | 12 | (assert_invalid 13 | (module 14 | (table $t1 10 funcref) 15 | (table $t2 10 externref) 16 | (func $f 17 | (table.copy $t1 $t2 (i32.const 0) (i32.const 1) (i32.const 2)) 18 | ) 19 | ) 20 | "type mismatch" 21 | ) 22 | 23 | (assert_invalid 24 | (module 25 | (table $t 10 funcref) 26 | (elem $el externref) 27 | (func $f 28 | (table.init $t $el (i32.const 0) (i32.const 1) (i32.const 2)) 29 | ) 30 | ) 31 | "type mismatch" 32 | ) 33 | -------------------------------------------------------------------------------- /test/core/table.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 1 (ref null func))) 12 | (module (table 1 (ref null extern))) 13 | (module (table 1 (ref null $t)) (type $t (func))) 14 | 15 | (module (table 0 funcref) (table 0 funcref)) 16 | (module (table (import "spectest" "table") 0 funcref) (table 0 funcref)) 17 | 18 | (module (table 0 funcref (ref.null func))) 19 | (module (table 1 funcref (ref.null func))) 20 | (module (table 1 (ref null func) (ref.null func))) 21 | 22 | (assert_invalid (module (elem (i32.const 0))) "unknown table") 23 | (assert_invalid (module (elem (i32.const 0) $f) (func $f)) "unknown table") 24 | 25 | 26 | (assert_invalid 27 | (module (table 1 0 funcref)) 28 | "size minimum must not be greater than maximum" 29 | ) 30 | (assert_invalid 31 | (module (table 0xffff_ffff 0 funcref)) 32 | "size minimum must not be greater than maximum" 33 | ) 34 | 35 | (assert_malformed 36 | (module quote "(table 0x1_0000_0000 funcref)") 37 | "i32 constant out of range" 38 | ) 39 | (assert_malformed 40 | (module quote "(table 0x1_0000_0000 0x1_0000_0000 funcref)") 41 | "i32 constant out of range" 42 | ) 43 | (assert_malformed 44 | (module quote "(table 0 0x1_0000_0000 funcref)") 45 | "i32 constant out of range" 46 | ) 47 | 48 | (assert_invalid 49 | (module (table 1 (ref null func) (i32.const 0))) 50 | "type mismatch" 51 | ) 52 | (assert_invalid 53 | (module (table 1 (ref func) (ref.null extern))) 54 | "type mismatch" 55 | ) 56 | (assert_invalid 57 | (module (type $t (func)) (table 1 (ref $t) (ref.null func))) 58 | "type mismatch" 59 | ) 60 | (assert_invalid 61 | (module (table 1 (ref func) (ref.null func))) 62 | "type mismatch" 63 | ) 64 | (assert_invalid 65 | (module (table 0 (ref func))) 66 | "type mismatch" 67 | ) 68 | (assert_invalid 69 | (module (table 0 (ref extern))) 70 | "type mismatch" 71 | ) 72 | (assert_invalid 73 | (module (type $t (func)) (table 0 (ref $t))) 74 | "type mismatch" 75 | ) 76 | 77 | 78 | ;; Table initializer 79 | 80 | (module 81 | (type $dummy (func)) 82 | (func $dummy) 83 | 84 | (table $t1 10 funcref) 85 | (table $t2 10 funcref (ref.func $dummy)) 86 | (table $t3 10 (ref $dummy) (ref.func $dummy)) 87 | 88 | (func (export "get1") (result funcref) (table.get $t1 (i32.const 1))) 89 | (func (export "get2") (result funcref) (table.get $t2 (i32.const 4))) 90 | (func (export "get3") (result funcref) (table.get $t3 (i32.const 7))) 91 | ) 92 | 93 | (assert_return (invoke "get1") (ref.null)) 94 | (assert_return (invoke "get2") (ref.func)) 95 | (assert_return (invoke "get3") (ref.func)) 96 | 97 | 98 | ;; Duplicate table identifiers 99 | 100 | (assert_malformed 101 | (module quote 102 | "(table $foo 1 funcref)" 103 | "(table $foo 1 funcref)" 104 | ) 105 | "duplicate table" 106 | ) 107 | (assert_malformed 108 | (module quote 109 | "(import \"\" \"\" (table $foo 1 funcref))" 110 | "(table $foo 1 funcref)" 111 | ) 112 | "duplicate table" 113 | ) 114 | (assert_malformed 115 | (module quote 116 | "(import \"\" \"\" (table $foo 1 funcref))" 117 | "(import \"\" \"\" (table $foo 1 funcref))" 118 | ) 119 | "duplicate table" 120 | ) 121 | -------------------------------------------------------------------------------- /test/core/table_get.wast: -------------------------------------------------------------------------------- 1 | (module 2 | (table $t2 2 externref) 3 | (table $t3 3 funcref) 4 | (elem (table $t3) (i32.const 1) func $dummy) 5 | (func $dummy) 6 | 7 | (func (export "init") (param $r externref) 8 | (table.set $t2 (i32.const 1) (local.get $r)) 9 | (table.set $t3 (i32.const 2) (table.get $t3 (i32.const 1))) 10 | ) 11 | 12 | (func (export "get-externref") (param $i i32) (result externref) 13 | (table.get (local.get $i)) 14 | ) 15 | (func $f3 (export "get-funcref") (param $i i32) (result funcref) 16 | (table.get $t3 (local.get $i)) 17 | ) 18 | 19 | (func (export "is_null-funcref") (param $i i32) (result i32) 20 | (ref.is_null (call $f3 (local.get $i))) 21 | ) 22 | ) 23 | 24 | (invoke "init" (ref.extern 1)) 25 | 26 | (assert_return (invoke "get-externref" (i32.const 0)) (ref.null extern)) 27 | (assert_return (invoke "get-externref" (i32.const 1)) (ref.extern 1)) 28 | 29 | (assert_return (invoke "get-funcref" (i32.const 0)) (ref.null func)) 30 | (assert_return (invoke "is_null-funcref" (i32.const 1)) (i32.const 0)) 31 | (assert_return (invoke "is_null-funcref" (i32.const 2)) (i32.const 0)) 32 | 33 | (assert_trap (invoke "get-externref" (i32.const 2)) "out of bounds table access") 34 | (assert_trap (invoke "get-funcref" (i32.const 3)) "out of bounds table access") 35 | (assert_trap (invoke "get-externref" (i32.const -1)) "out of bounds table access") 36 | (assert_trap (invoke "get-funcref" (i32.const -1)) "out of bounds table access") 37 | 38 | 39 | ;; Type errors 40 | 41 | (assert_invalid 42 | (module 43 | (table $t 10 externref) 44 | (func $type-index-empty-vs-i32 (result externref) 45 | (table.get $t) 46 | ) 47 | ) 48 | "type mismatch" 49 | ) 50 | (assert_invalid 51 | (module 52 | (table $t 10 externref) 53 | (func $type-index-f32-vs-i32 (result externref) 54 | (table.get $t (f32.const 1)) 55 | ) 56 | ) 57 | "type mismatch" 58 | ) 59 | 60 | (assert_invalid 61 | (module 62 | (table $t 10 externref) 63 | (func $type-result-externref-vs-empty 64 | (table.get $t (i32.const 0)) 65 | ) 66 | ) 67 | "type mismatch" 68 | ) 69 | (assert_invalid 70 | (module 71 | (table $t 10 externref) 72 | (func $type-result-externref-vs-funcref (result funcref) 73 | (table.get $t (i32.const 1)) 74 | ) 75 | ) 76 | "type mismatch" 77 | ) 78 | 79 | (assert_invalid 80 | (module 81 | (table $t1 1 funcref) 82 | (table $t2 1 externref) 83 | (func $type-result-externref-vs-funcref-multi (result funcref) 84 | (table.get $t2 (i32.const 0)) 85 | ) 86 | ) 87 | "type mismatch" 88 | ) 89 | -------------------------------------------------------------------------------- /test/core/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) 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-result1") (result i32) 21 | (unreachable) (i32.add (select)) 22 | ) 23 | 24 | (func (export "select-unreached-result2") (result i64) 25 | (unreachable) (i64.add (select (i64.const 0) (i32.const 0))) 26 | ) 27 | 28 | (func (export "select-unreached-num") 29 | (unreachable) 30 | (select) 31 | (i32.eqz) 32 | (drop) 33 | ) 34 | (func (export "select-unreached-ref") 35 | (unreachable) 36 | (select) 37 | (ref.is_null) 38 | (drop) 39 | ) 40 | 41 | (type $t (func (param i32) (result i32))) 42 | (func (export "call_ref-unreached") (result i32) 43 | (unreachable) 44 | (call_ref $t) 45 | ) 46 | ) 47 | 48 | (assert_trap (invoke "select-trap-left" (i32.const 1)) "unreachable") 49 | (assert_trap (invoke "select-trap-left" (i32.const 0)) "unreachable") 50 | (assert_trap (invoke "select-trap-right" (i32.const 1)) "unreachable") 51 | (assert_trap (invoke "select-trap-right" (i32.const 0)) "unreachable") 52 | 53 | (assert_trap (invoke "select-unreached-result1") "unreachable") 54 | (assert_trap (invoke "select-unreached-result2") "unreachable") 55 | (assert_trap (invoke "select-unreached-num") "unreachable") 56 | (assert_trap (invoke "select-unreached-ref") "unreachable") 57 | 58 | (assert_trap (invoke "call_ref-unreached") "unreachable") 59 | 60 | 61 | ;; Validation after unreachable 62 | 63 | (module 64 | (func (export "meet-bottom") 65 | (block (result f64) 66 | (block (result f32) 67 | (unreachable) 68 | (br_table 0 1 1 (i32.const 1)) 69 | ) 70 | (drop) 71 | (f64.const 0) 72 | ) 73 | (drop) 74 | ) 75 | ) 76 | 77 | (assert_trap (invoke "meet-bottom") "unreachable") 78 | -------------------------------------------------------------------------------- /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{WebAssemblyCoreSpecification1, 2 | title = {{WebAssembly Core Specification}}, 3 | version = {1.0}, 4 | editor = {Rossberg, Andreas}, 5 | date = {2019-12-05}, 6 | institution = {{W3C}}, 7 | url = {https://www.w3.org/TR/wasm-core-1/}, 8 | langid = {english} 9 | } 10 | 11 | @report{WebAssemblyCoreSpecification2, 12 | title = {{WebAssembly Core Specification}}, 13 | version = {2.0}, 14 | editor = {Rossberg, Andreas}, 15 | date = {2022-04-19}, 16 | institution = {{W3C}}, 17 | url = {https://www.w3.org/TR/wasm-core-2/}, 18 | langid = {english}, 19 | note = {https://webassembly.github.io/spec/core/_download/WebAssembly.pdf} 20 | } 21 | --------------------------------------------------------------------------------