├── .gitattributes ├── .gitignore ├── .gitmodules ├── .travis.yml ├── Contributing.md ├── LICENSE ├── README.md ├── deploy_key.enc ├── document ├── Makefile ├── README.md ├── core │ ├── .gitignore │ ├── LICENSE │ ├── Makefile │ ├── README.md │ ├── appendix │ │ ├── algorithm.rst │ │ ├── custom.rst │ │ ├── embedding.rst │ │ ├── implementation.rst │ │ ├── index-instructions.rst │ │ ├── index-rules.rst │ │ ├── index-types.rst │ │ ├── index.rst │ │ └── properties.rst │ ├── binary │ │ ├── conventions.rst │ │ ├── index.rst │ │ ├── instructions.rst │ │ ├── modules.rst │ │ ├── types.rst │ │ └── values.rst │ ├── conf.py │ ├── exec │ │ ├── conventions.rst │ │ ├── index.rst │ │ ├── instructions.rst │ │ ├── modules.rst │ │ ├── numerics.rst │ │ └── runtime.rst │ ├── index.bs │ ├── index.rst │ ├── intro │ │ ├── index.rst │ │ ├── introduction.rst │ │ └── overview.rst │ ├── make.bat │ ├── static │ │ ├── custom.css │ │ └── webassembly.png │ ├── syntax │ │ ├── conventions.rst │ │ ├── index.rst │ │ ├── instructions.rst │ │ ├── modules.rst │ │ ├── types.rst │ │ └── values.rst │ ├── text │ │ ├── conventions.rst │ │ ├── index.rst │ │ ├── instructions.rst │ │ ├── lexical.rst │ │ ├── modules.rst │ │ ├── types.rst │ │ └── values.rst │ ├── util │ │ ├── README.htmldiff.pl │ │ ├── bikeshed │ │ │ └── conf.py │ │ ├── bikeshed_fixup.py │ │ ├── katex_fix.patch │ │ ├── macros.def │ │ ├── mathdef.py │ │ ├── mathdefbs.py │ │ ├── mathjax2katex.py │ │ └── pseudo-lexer.py │ └── valid │ │ ├── conventions.rst │ │ ├── index.rst │ │ ├── instructions.rst │ │ ├── modules.rst │ │ └── types.rst ├── deploy.sh ├── index.html ├── js-api │ ├── Makefile │ └── index.bs ├── travis-deploy.sh ├── util │ └── htmldiff.pl └── web-api │ ├── Makefile │ └── index.bs ├── interpreter ├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── binary │ ├── decode.ml │ ├── decode.mli │ ├── encode.ml │ ├── encode.mli │ ├── utf8.ml │ └── utf8.mli ├── exec │ ├── eval.ml │ ├── eval.mli │ ├── eval_numeric.ml │ ├── eval_numeric.mli │ ├── f32.ml │ ├── f32_convert.ml │ ├── f32_convert.mli │ ├── f64.ml │ ├── f64_convert.ml │ ├── f64_convert.mli │ ├── float.ml │ ├── i32.ml │ ├── i32_convert.ml │ ├── i32_convert.mli │ ├── i64.ml │ ├── i64_convert.ml │ ├── i64_convert.mli │ ├── int.ml │ └── numeric_error.ml ├── host │ ├── env.ml │ └── spectest.ml ├── main │ ├── flags.ml │ └── main.ml ├── meta │ ├── findlib │ │ └── META │ ├── jslib │ │ ├── bsconfig.json │ │ ├── build.sh │ │ └── wasm.ml │ └── travis │ │ ├── build-test.sh │ │ └── install-ocaml.sh ├── runtime │ ├── func.ml │ ├── func.mli │ ├── global.ml │ ├── global.mli │ ├── instance.ml │ ├── memory.ml │ ├── memory.mli │ ├── table.ml │ └── table.mli ├── script │ ├── import.ml │ ├── import.mli │ ├── js.ml │ ├── js.mli │ ├── run.ml │ ├── run.mli │ └── script.ml ├── syntax │ ├── ast.ml │ ├── operators.ml │ ├── types.ml │ └── values.ml ├── text │ ├── arrange.ml │ ├── arrange.mli │ ├── lexer.mli │ ├── lexer.mll │ ├── parse.ml │ ├── parse.mli │ ├── parser.mly │ ├── print.ml │ └── print.mli ├── util │ ├── error.ml │ ├── error.mli │ ├── lib.ml │ ├── lib.mli │ ├── sexpr.ml │ ├── sexpr.mli │ ├── source.ml │ └── source.mli ├── valid │ ├── valid.ml │ └── valid.mli └── winmake.bat ├── papers ├── LICENSE ├── README.md ├── oopsla2019.pdf └── pldi2017.pdf ├── proposals └── multi-value │ └── Overview.md ├── test ├── LICENSE ├── README.md ├── Todo.md ├── build.py ├── core │ ├── .gitignore │ ├── README.md │ ├── address.wast │ ├── align.wast │ ├── binary-leb128.wast │ ├── binary.wast │ ├── block.wast │ ├── br.wast │ ├── br_if.wast │ ├── br_table.wast │ ├── call.wast │ ├── call_indirect.wast │ ├── comments.wast │ ├── const.wast │ ├── conversions.wast │ ├── custom.wast │ ├── data.wast │ ├── elem.wast │ ├── endianness.wast │ ├── exports.wast │ ├── f32.wast │ ├── f32_bitwise.wast │ ├── f32_cmp.wast │ ├── f64.wast │ ├── f64_bitwise.wast │ ├── f64_cmp.wast │ ├── fac.wast │ ├── float_exprs.wast │ ├── float_literals.wast │ ├── float_memory.wast │ ├── float_misc.wast │ ├── forward.wast │ ├── func.wast │ ├── func_ptrs.wast │ ├── global.wast │ ├── i32.wast │ ├── i64.wast │ ├── if.wast │ ├── imports.wast │ ├── inline-module.wast │ ├── int_exprs.wast │ ├── int_literals.wast │ ├── labels.wast │ ├── left-to-right.wast │ ├── linking.wast │ ├── load.wast │ ├── local_get.wast │ ├── local_set.wast │ ├── local_tee.wast │ ├── loop.wast │ ├── memory.wast │ ├── memory_grow.wast │ ├── memory_redundancy.wast │ ├── memory_size.wast │ ├── memory_trap.wast │ ├── names.wast │ ├── nop.wast │ ├── return.wast │ ├── run.py │ ├── select.wast │ ├── skip-stack-guard-page.wast │ ├── stack.wast │ ├── start.wast │ ├── store.wast │ ├── switch.wast │ ├── table.wast │ ├── token.wast │ ├── traps.wast │ ├── type.wast │ ├── unreachable.wast │ ├── unreached-invalid.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 │ ├── global │ ├── constructor.any.js │ ├── toString.any.js │ ├── value-get-set.any.js │ └── valueOf.any.js │ ├── instance │ ├── constructor-bad-imports.any.js │ ├── constructor.any.js │ ├── exports.any.js │ └── toString.any.js │ ├── instanceTestFactory.js │ ├── interface.any.js │ ├── limits.any.js │ ├── memory │ ├── 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 │ ├── table │ ├── assertions.js │ ├── constructor.any.js │ ├── get-set.any.js │ ├── grow.any.js │ ├── length.any.js │ └── toString.any.js │ └── wasm-module-builder.js └── w3c.json /.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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3 | - "3.7" 4 | dist: bionic 5 | 6 | addons: 7 | apt: 8 | sources: 9 | - sourceline: 'deb https://dl.yarnpkg.com/debian/ stable main' 10 | key_url: 'https://dl.yarnpkg.com/debian/pubkey.gpg' 11 | packages: 12 | - ocaml 13 | - ocamlbuild 14 | - texlive-full 15 | - yarn 16 | 17 | install: 18 | - pip install Sphinx==2.4.4 19 | - git clone https://github.com/tabatkins/bikeshed.git 20 | - pip install --editable $PWD/bikeshed 21 | - bikeshed update 22 | 23 | script: 24 | - ./interpreter/meta/travis/build-test.sh 25 | - bash ./document/travis-deploy.sh 26 | 27 | os: linux 28 | 29 | env: 30 | global: 31 | - ENCRYPTION_LABEL: "336792f917f6" 32 | - COMMIT_AUTHOR_EMAIL: "noreply@webassembly.org" 33 | -------------------------------------------------------------------------------- /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/master/Contributing.md 7 | 8 | Also, please be sure to read [the README.md](README.md) for this repository. 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Please see the LICENSE file in each top-level directory for the terms applicable to that directory and its relative sub-directories. 2 | 3 | The relevant directories and licenses are: 4 | 5 | document/ - W3C Software and Document Notice and License 6 | interpreter/ - Apache License 2.0 7 | test/ - Apache License 2.0 8 | papers/ - Creative Commons Attribution 4.0 International License 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/WebAssembly/multi-value.svg?branch=master)](https://travis-ci.org/WebAssembly/multi-value) 2 | 3 | # Multi-value Proposal for WebAssembly 4 | 5 | **Note: This proposal has been merged into the WebAssembly standard on 2020/04/09.** 6 | 7 | This repository is a clone of [github.com/WebAssembly/spec/](https://github.com/WebAssembly/spec/). 8 | It is meant for discussion, prototype specification and implementation of a proposal to add support for returning multiple values to WebAssembly. 9 | 10 | * See the [overview](proposals/multi-value/Overview.md) for a summary of the proposal. 11 | 12 | * See the [modified spec](https://webassembly.github.io/multi-value/) for details. 13 | 14 | Original `README` from upstream repository follows... 15 | 16 | # spec 17 | 18 | This repository holds a prototypical reference implementation for WebAssembly, 19 | which is currently serving as the official specification. Eventually, we expect 20 | to produce a specification either written in human-readable prose or in a formal 21 | specification language. 22 | 23 | It also holds the WebAssembly testsuite, which tests numerous aspects of 24 | conformance to the spec. 25 | 26 | View the work-in-progress spec at [webassembly.github.io/spec](https://webassembly.github.io/spec/). 27 | 28 | At this time, the contents of this repository are under development and known 29 | to be "incomplet and inkorrect". 30 | 31 | Participation is welcome. Discussions about new features, significant semantic 32 | changes, or any specification change likely to generate substantial discussion 33 | should take place in 34 | [the WebAssembly design repository](https://github.com/WebAssembly/design) 35 | first, so that this spec repository can remain focused. And please follow the 36 | [guidelines for contributing](Contributing.md). 37 | -------------------------------------------------------------------------------- /deploy_key.enc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WebAssembly/multi-value/61975c83a286b96517659eded062f4e11dbac4c7/deploy_key.enc -------------------------------------------------------------------------------- /document/Makefile: -------------------------------------------------------------------------------- 1 | DIRS = core js-api web-api 2 | FILES = index.html 3 | BUILDDIR = _build 4 | 5 | # Global targets. 6 | 7 | .PHONY: all 8 | all: $(BUILDDIR) root $(DIRS) 9 | 10 | $(BUILDDIR): 11 | mkdir -p $@ 12 | 13 | .PHONY: deploy 14 | deploy: 15 | GIT_DEPLOY_DIR=$(BUILDDIR) bash deploy.sh 16 | 17 | .PHONY: publish 18 | publish: all deploy 19 | 20 | .PHONY: clean 21 | clean: $(DIRS:%=clean-%) 22 | rm -rf $(BUILDDIR) 23 | 24 | .PHONY: diff 25 | diff: $(DIRS:%=diff-%) 26 | 27 | 28 | # Directory-specific targets. 29 | 30 | .PHONY: root 31 | root: $(BUILDDIR) 32 | touch $(BUILDDIR)/.nojekyll 33 | cp -f $(FILES) $(BUILDDIR)/ 34 | 35 | .PHONY: $(DIRS) 36 | $(DIRS): %: $(BUILDDIR) $(DIRS:%=build-%) $(DIRS:%=dir-%) 37 | 38 | .PHONY: $(DIRS:%=build-%) 39 | $(DIRS:%=build-%): build-%: 40 | (cd $(@:build-%=%); make BUILDDIR=$(BUILDDIR) all) 41 | 42 | .PHONY: $(DIRS:%=dir-%) 43 | $(DIRS:%=dir-%): dir-%: 44 | mkdir -p $(BUILDDIR)/$(@:dir-%=%) 45 | rm -rf $(BUILDDIR)/$(@:dir-%=%)/* 46 | cp -R $(@:dir-%=%)/$(BUILDDIR)/html/* $(BUILDDIR)/$(@:dir-%=%)/ 47 | 48 | .PHONY: $(DIRS:%=deploy-%) 49 | $(DIRS:%=deploy-%): deploy-%: 50 | GIT_DEPLOY_DIR=$(BUILDDIR) GIT_DEPLOY_SUBDIR=$(@:deploy-%=%) bash deploy.sh 51 | 52 | .PHONY: $(DIRS:%=publish-%) 53 | $(DIRS:%=publish-%): publish-%: % deploy-% 54 | 55 | .PHONY: $(DIRS:%=clean-%) 56 | $(DIRS:%=clean-%): clean-%: 57 | (cd $(@:clean-%=%); make BUILDDIR=$(BUILDDIR) clean) 58 | rm -rf $(BUILDDIR)/$(@:clean-%=%) 59 | 60 | .PHONY: $(DIRS:%=diff-%) 61 | $(DIRS:%=diff-%): diff-%: 62 | (cd $(@:diff-%=%); make BUILDDIR=$(BUILDDIR) diff) 63 | 64 | 65 | # Help. 66 | 67 | .PHONY: help 68 | help: 69 | @echo "Please use \`make ' where is one of" 70 | @echo " all to build all documents" 71 | @echo " publish to make all and push to gh-pages" 72 | @echo " to build a specific subdirectory" 73 | @echo " publish- to build and push a specific subdirectory" 74 | 75 | .PHONY: usage 76 | usage: help 77 | -------------------------------------------------------------------------------- /document/README.md: -------------------------------------------------------------------------------- 1 | # WebAssembly Specifications 2 | 3 | This directory contains the source code for the WebAssembly spec documents, as served from the [webassembly.github.io/spec](https://webassembly.github.io/spec) pages. 4 | It uses [Sphinx](http://www.sphinx-doc.org/) and [Bikeshed](https://github.com/tabatkins/bikeshed). 5 | 6 | To install Sphinx: 7 | ``` 8 | pip install sphinx 9 | ``` 10 | 11 | To install Bikeshed, see the instructions [here](https://tabatkins.github.io/bikeshed/#installing). 12 | 13 | 14 | To build everything locally (result appears in `_build/`): 15 | ``` 16 | make all 17 | ``` 18 | 19 | To build everything and update [webassembly.github.io/spec](https://webassembly.github.io/spec) with it: 20 | ``` 21 | make publish 22 | ``` 23 | Please make sure to only use that once a change has approval. 24 | -------------------------------------------------------------------------------- /document/core/.gitignore: -------------------------------------------------------------------------------- 1 | _build 2 | _static 3 | document/*.pyc 4 | -------------------------------------------------------------------------------- /document/core/LICENSE: -------------------------------------------------------------------------------- 1 | W3C SOFTWARE AND DOCUMENT NOTICE AND LICENSE 2 | 3 | This work is being provided by the copyright holders under the following 4 | license. 5 | 6 | 7 | LICENSE 8 | 9 | By obtaining and/or copying this work, you (the licensee) agree that you have 10 | read, understood, and will comply with the following terms and conditions. 11 | 12 | Permission to copy, modify, and distribute this work, with or without 13 | modification, for any purpose and without fee or royalty is hereby granted, 14 | provided that you include the following on ALL copies of the work or portions 15 | thereof, including modifications: 16 | 17 | * The full text of this NOTICE in a location viewable to users of the 18 | redistributed or derivative work. 19 | 20 | * Any pre-existing intellectual property disclaimers, notices, or terms and 21 | conditions. If none exist, the W3C Software and Document Short Notice 22 | (https://www.w3.org/Consortium/Legal/copyright-software-short-notice) should 23 | be included. 24 | 25 | * Notice of any changes or modifications, through a copyright statement on the 26 | new code or document such as "This software or document includes material 27 | copied from or derived from [title and URI of the W3C document]. Copyright © [YEAR] W3C® (MIT, ERCIM, Keio, Beihang)." 28 | 29 | 30 | DISCLAIMERS 31 | 32 | THIS WORK IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS MAKE NO REPRESENTATIONS 33 | OR WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO, WARRANTIES OF 34 | MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE 35 | SOFTWARE OR DOCUMENT WILL NOT INFRINGE ANY THIRD PARTY PATENTS, COPYRIGHTS, 36 | TRADEMARKS OR OTHER RIGHTS. 37 | 38 | COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL OR 39 | CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE SOFTWARE OR DOCUMENT. 40 | 41 | The name and trademarks of copyright holders may NOT be used in advertising or 42 | publicity pertaining to the work without specific, written prior permission. 43 | Title to copyright in this work will at all times remain with copyright 44 | holders. 45 | 46 | 47 | NOTES 48 | 49 | This version: 50 | http://www.w3.org/Consortium/Legal/2015/copyright-software-and-document 51 | -------------------------------------------------------------------------------- /document/core/README.md: -------------------------------------------------------------------------------- 1 | # WebAssembly Core Specification 2 | 3 | This is the official WebAssembly "language" specification. 4 | 5 | It uses [Sphinx](http://www.sphinx-doc.org/). To install that: 6 | ``` 7 | pip install sphinx 8 | ``` 9 | To make HTML (result in `_build/html`): 10 | ``` 11 | make html 12 | ``` 13 | To make PDF (result in `_build/latex`, requires LaTeX): 14 | ``` 15 | make pdf 16 | ``` 17 | To make all: 18 | ``` 19 | make all 20 | ``` 21 | Finally, to make all and update webassembly.github.io/spec with it: 22 | ``` 23 | make publish 24 | ``` 25 | Please make sure to only use that once a change has approval. 26 | -------------------------------------------------------------------------------- /document/core/appendix/index-types.rst: -------------------------------------------------------------------------------- 1 | .. index:: type 2 | .. _index-type: 3 | 4 | Index of Types 5 | -------------- 6 | 7 | ======================================== =========================================== =============================================================================== 8 | Category Constructor Binary Opcode 9 | ======================================== =========================================== =============================================================================== 10 | :ref:`Type index ` :math:`x` (positive number as |Bs32| or |Bu32|) 11 | :ref:`Value type ` |I32| :math:`\hex{7F}` (-1 as |Bs7|) 12 | :ref:`Value type ` |I64| :math:`\hex{7E}` (-2 as |Bs7|) 13 | :ref:`Value type ` |F32| :math:`\hex{7D}` (-3 as |Bs7|) 14 | :ref:`Value type ` |F64| :math:`\hex{7C}` (-4 as |Bs7|) 15 | (reserved) :math:`\hex{7B}` .. :math:`\hex{71}` 16 | :ref:`Element type ` |FUNCREF| :math:`\hex{70}` (-16 as |Bs7|) 17 | (reserved) :math:`\hex{6F}` .. :math:`\hex{61}` 18 | :ref:`Function type ` :math:`[\valtype^\ast] \to [\valtype^\ast]` :math:`\hex{60}` (-32 as |Bs7|) 19 | (reserved) :math:`\hex{5F}` .. :math:`\hex{41}` 20 | :ref:`Result type ` :math:`[\epsilon]` :math:`\hex{40}` (-64 as |Bs7|) 21 | :ref:`Table type ` :math:`\limits~\elemtype` (none) 22 | :ref:`Memory type ` :math:`\limits` (none) 23 | :ref:`Global type ` :math:`\mut~\valtype` (none) 24 | ======================================== =========================================== =============================================================================== 25 | -------------------------------------------------------------------------------- /document/core/appendix/index.rst: -------------------------------------------------------------------------------- 1 | .. _appendix: 2 | 3 | Appendix 4 | ======== 5 | 6 | .. toctree:: 7 | :maxdepth: 2 8 | 9 | embedding 10 | implementation 11 | algorithm 12 | custom 13 | properties 14 | 15 | .. only:: singlehtml 16 | 17 | .. toctree:: 18 | 19 | index-types 20 | index-instructions 21 | index-rules 22 | 23 | -------------------------------------------------------------------------------- /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/binary/types.rst: -------------------------------------------------------------------------------- 1 | .. index:: type 2 | pair: binary format; type 3 | .. _binary-type: 4 | 5 | Types 6 | ----- 7 | 8 | .. index:: value type 9 | pair: binary format; value type 10 | .. _binary-valtype: 11 | 12 | Value Types 13 | ~~~~~~~~~~~ 14 | 15 | :ref:`Value types ` are encoded by a single byte. 16 | 17 | .. math:: 18 | \begin{array}{llclll@{\qquad\qquad}l} 19 | \production{value type} & \Bvaltype &::=& 20 | \hex{7F} &\Rightarrow& \I32 \\ &&|& 21 | \hex{7E} &\Rightarrow& \I64 \\ &&|& 22 | \hex{7D} &\Rightarrow& \F32 \\ &&|& 23 | \hex{7C} &\Rightarrow& \F64 \\ 24 | \end{array} 25 | 26 | .. note:: 27 | Value types can occur in contexts where :ref:`type indices ` are also allowed, such as in the case of :ref:`block types `. 28 | Thus, the binary format for types corresponds to the |SignedLEB128|_ :ref:`encoding ` of small negative :math:`\sN` values, so that they can coexist with (positive) type indices in the future. 29 | 30 | 31 | .. index:: result type, value type 32 | pair: binary format; result type 33 | .. _binary-resulttype: 34 | 35 | Result Types 36 | ~~~~~~~~~~~~ 37 | 38 | :ref:`Result types ` are encoded by the respective :ref:`vectors ` of :ref:`value types ``. 39 | 40 | .. math:: 41 | \begin{array}{llclll@{\qquad\qquad}l} 42 | \production{result type} & \Bresulttype &::=& 43 | t^\ast{:\,}\Bvec(\Bvaltype) &\Rightarrow& [t^\ast] \\ 44 | \end{array} 45 | 46 | 47 | .. index:: function type, value type, result type 48 | pair: binary format; function type 49 | .. _binary-functype: 50 | 51 | Function Types 52 | ~~~~~~~~~~~~~~ 53 | 54 | :ref:`Function types ` are encoded by the byte :math:`\hex{60}` followed by the respective :ref:`vectors ` of parameter and result types. 55 | 56 | .. math:: 57 | \begin{array}{llclll@{\qquad\qquad}l} 58 | \production{function type} & \Bfunctype &::=& 59 | \hex{60}~~\X{rt}_1{:\,}\Bresulttype~~\X{rt}_2{:\,}\Bresulttype 60 | &\Rightarrow& \X{rt}_1 \to \X{rt}_2 \\ 61 | \end{array} 62 | 63 | 64 | .. index:: limits 65 | pair: binary format; limits 66 | .. _binary-limits: 67 | 68 | Limits 69 | ~~~~~~ 70 | 71 | :ref:`Limits ` are encoded with a preceding flag indicating whether a maximum is present. 72 | 73 | .. math:: 74 | \begin{array}{llclll} 75 | \production{limits} & \Blimits &::=& 76 | \hex{00}~~n{:}\Bu32 &\Rightarrow& \{ \LMIN~n, \LMAX~\epsilon \} \\ &&|& 77 | \hex{01}~~n{:}\Bu32~~m{:}\Bu32 &\Rightarrow& \{ \LMIN~n, \LMAX~m \} \\ 78 | \end{array} 79 | 80 | 81 | .. index:: memory type, limits, page size 82 | pair: binary format; memory type 83 | .. _binary-memtype: 84 | 85 | Memory Types 86 | ~~~~~~~~~~~~ 87 | 88 | :ref:`Memory types ` are encoded with their :ref:`limits `. 89 | 90 | .. math:: 91 | \begin{array}{llclll@{\qquad\qquad}l} 92 | \production{memory type} & \Bmemtype &::=& 93 | \X{lim}{:}\Blimits &\Rightarrow& \X{lim} \\ 94 | \end{array} 95 | 96 | 97 | .. index:: table type, element type, limits 98 | pair: binary format; table type 99 | pair: binary format; element type 100 | .. _binary-elemtype: 101 | .. _binary-tabletype: 102 | 103 | Table Types 104 | ~~~~~~~~~~~ 105 | 106 | :ref:`Table types ` are encoded with their :ref:`limits ` and a constant byte indicating their :ref:`element type `. 107 | 108 | .. math:: 109 | \begin{array}{llclll} 110 | \production{table type} & \Btabletype &::=& 111 | \X{et}{:}\Belemtype~~\X{lim}{:}\Blimits &\Rightarrow& \X{lim}~\X{et} \\ 112 | \production{element type} & \Belemtype &::=& 113 | \hex{70} &\Rightarrow& \FUNCREF \\ 114 | \end{array} 115 | 116 | 117 | .. index:: global type, mutability, value type 118 | pair: binary format; global type 119 | pair: binary format; mutability 120 | .. _binary-mut: 121 | .. _binary-globaltype: 122 | 123 | Global Types 124 | ~~~~~~~~~~~~ 125 | 126 | :ref:`Global types ` are encoded by their :ref:`value type ` and a flag for their :ref:`mutability `. 127 | 128 | .. math:: 129 | \begin{array}{llclll} 130 | \production{global type} & \Bglobaltype &::=& 131 | t{:}\Bvaltype~~m{:}\Bmut &\Rightarrow& m~t \\ 132 | \production{mutability} & \Bmut &::=& 133 | \hex{00} &\Rightarrow& \MCONST \\ &&|& 134 | \hex{01} &\Rightarrow& \MVAR \\ 135 | \end{array} 136 | -------------------------------------------------------------------------------- /document/core/exec/index.rst: -------------------------------------------------------------------------------- 1 | .. _exec: 2 | 3 | Execution 4 | ========= 5 | 6 | .. toctree:: 7 | :maxdepth: 2 8 | 9 | conventions 10 | runtime 11 | numerics 12 | instructions 13 | modules 14 | -------------------------------------------------------------------------------- /document/core/index.bs: -------------------------------------------------------------------------------- 1 | 15 | 16 |
17 | {
18 |   "WEBASSEMBLY": {
19 |     "href": "https://webassembly.github.io/spec/",
20 |     "title": "WebAssembly Specification",
21 |     "publisher": "W3C WebAssembly Community Group",
22 |     "status": "Draft"
23 |   }
24 | }
25 | 
26 | 27 |
28 | path: _build/bikeshed_singlehtml/index_fixed.html
29 | 
30 | -------------------------------------------------------------------------------- /document/core/index.rst: -------------------------------------------------------------------------------- 1 | WebAssembly Specification 2 | ========================= 3 | 4 | .. only:: html 5 | 6 | | Release |release| + multiple values (Draft, |today|) 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 | .. only:: html 33 | 34 | * :ref:`index-type` 35 | * :ref:`index-instr` 36 | * :ref:`index-rules` 37 | 38 | * :ref:`genindex` 39 | -------------------------------------------------------------------------------- /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 | 45 | div.relations { 46 | display: block; 47 | } 48 | 49 | div.sphinxsidebar { 50 | z-index: 1; 51 | background: #FFF; 52 | margin-top: -30px; 53 | font-size: 13px; 54 | width: 200px; 55 | height: 100%; 56 | } 57 | 58 | div.sphinxsidebarwrapper p.logo { 59 | padding: 30px 40px 10px 0px; 60 | } 61 | 62 | div.sphinxsidebar h3 { 63 | font-size: 0px; 64 | } 65 | 66 | div.sphinxsidebar a { 67 | border-bottom: 0px; 68 | } 69 | 70 | div.sphinxsidebar a:hover { 71 | border-bottom: 1px dotted; 72 | } 73 | -------------------------------------------------------------------------------- /document/core/static/webassembly.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WebAssembly/multi-value/61975c83a286b96517659eded062f4e11dbac4c7/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/text/types.rst: -------------------------------------------------------------------------------- 1 | .. index:: type 2 | pair: text format; type 3 | .. _text-type: 4 | 5 | Types 6 | ----- 7 | 8 | .. index:: value type 9 | pair: text format; value type 10 | .. _text-valtype: 11 | 12 | Value Types 13 | ~~~~~~~~~~~ 14 | 15 | .. math:: 16 | \begin{array}{llcll@{\qquad\qquad}l} 17 | \production{value type} & \Tvaltype &::=& 18 | \text{i32} &\Rightarrow& \I32 \\ &&|& 19 | \text{i64} &\Rightarrow& \I64 \\ &&|& 20 | \text{f32} &\Rightarrow& \F32 \\ &&|& 21 | \text{f64} &\Rightarrow& \F64 \\ 22 | \end{array} 23 | 24 | 25 | .. index:: function type, value type, result type 26 | pair: text format; function type 27 | .. _text-param: 28 | .. _text-result: 29 | .. _text-functype: 30 | 31 | Function Types 32 | ~~~~~~~~~~~~~~ 33 | 34 | .. math:: 35 | \begin{array}{llclll@{\qquad\qquad}l} 36 | \production{function type} & \Tfunctype &::=& 37 | \text{(}~\text{func}~~t_1^\ast{:\,}\Tvec(\Tparam)~~t_2^\ast{:\,}\Tvec(\Tresult)~\text{)} 38 | &\Rightarrow& [t_1^\ast] \to [t_2^\ast] \\ 39 | \production{parameter} & \Tparam &::=& 40 | \text{(}~\text{param}~~\Tid^?~~t{:}\Tvaltype~\text{)} 41 | &\Rightarrow& t \\ 42 | \production{result} & \Tresult &::=& 43 | \text{(}~\text{result}~~t{:}\Tvaltype~\text{)} 44 | &\Rightarrow& t \\ 45 | \end{array} 46 | 47 | 48 | Abbreviations 49 | ............. 50 | 51 | Multiple anonymous parameters or results may be combined into a single declaration: 52 | 53 | .. math:: 54 | \begin{array}{llclll} 55 | \production{parameter} & 56 | \text{(}~~\text{param}~~\Tvaltype^\ast~~\text{)} &\equiv& 57 | (\text{(}~~\text{param}~~\Tvaltype~~\text{)})^\ast \\ 58 | \production{result} & 59 | \text{(}~~\text{result}~~\Tvaltype^\ast~~\text{)} &\equiv& 60 | (\text{(}~~\text{result}~~\Tvaltype~~\text{)})^\ast \\ 61 | \end{array} 62 | 63 | 64 | .. index:: limits 65 | pair: text format; limits 66 | .. _text-limits: 67 | 68 | Limits 69 | ~~~~~~ 70 | 71 | .. math:: 72 | \begin{array}{llclll} 73 | \production{limits} & \Tlimits &::=& 74 | n{:}\Tu32 &\Rightarrow& \{ \LMIN~n, \LMAX~\epsilon \} \\ &&|& 75 | n{:}\Tu32~~m{:}\Tu32 &\Rightarrow& \{ \LMIN~n, \LMAX~m \} \\ 76 | \end{array} 77 | 78 | 79 | .. index:: memory type, limits, page size 80 | pair: text format; memory type 81 | .. _text-memtype: 82 | 83 | Memory Types 84 | ~~~~~~~~~~~~ 85 | 86 | .. math:: 87 | \begin{array}{llclll@{\qquad\qquad}l} 88 | \production{memory type} & \Tmemtype &::=& 89 | \X{lim}{:}\Tlimits &\Rightarrow& \X{lim} \\ 90 | \end{array} 91 | 92 | 93 | .. index:: table type, element type, limits 94 | pair: text format; table type 95 | pair: text format; element type 96 | .. _text-elemtype: 97 | .. _text-tabletype: 98 | 99 | Table Types 100 | ~~~~~~~~~~~ 101 | 102 | .. math:: 103 | \begin{array}{llclll} 104 | \production{table type} & \Ttabletype &::=& 105 | \X{lim}{:}\Tlimits~~\X{et}{:}\Telemtype &\Rightarrow& \X{lim}~\X{et} \\ 106 | \production{element type} & \Telemtype &::=& 107 | \text{funcref} &\Rightarrow& \FUNCREF \\ 108 | \end{array} 109 | 110 | .. note:: 111 | Additional element types may be introduced in future versions of WebAssembly. 112 | 113 | 114 | .. index:: global type, mutability, value type 115 | pair: text format; global type 116 | pair: text format; mutability 117 | .. _text-globaltype: 118 | 119 | Global Types 120 | ~~~~~~~~~~~~ 121 | 122 | .. math:: 123 | \begin{array}{llclll} 124 | \production{global type} & \Tglobaltype &::=& 125 | t{:}\Tvaltype &\Rightarrow& \MCONST~t \\ &&|& 126 | \text{(}~\text{mut}~~t{:}\Tvaltype~\text{)} &\Rightarrow& \MVAR~t \\ 127 | \end{array} 128 | -------------------------------------------------------------------------------- /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_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 | """, '') 47 | 48 | # Use bikeshed biblio references for unicode and IEEE754 49 | data = data.replace( 50 | """Unicode""", 51 | "[[!UNICODE]]" 52 | ) 53 | 54 | data = data.replace( 55 | """IEEE 754-2019""", 56 | "[[!IEEE-754-2019]]" 57 | ) 58 | 59 | sys.stdout.write(data) 60 | 61 | Main() 62 | -------------------------------------------------------------------------------- /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/mathdef.py: -------------------------------------------------------------------------------- 1 | from sphinx.ext.mathbase import math 2 | from sphinx.ext.mathbase import MathDirective 3 | from sphinx.util.texescape import tex_replace_map 4 | from sphinx.writers.html5 import HTML5Translator 5 | from sphinx.writers.latex import LaTeXTranslator 6 | from docutils import nodes 7 | from docutils.parsers.rst.directives.misc import Replace 8 | from six import text_type 9 | import re 10 | 11 | 12 | # Transform \xref in math nodes 13 | 14 | xref_re = re.compile('\\\\xref\{([^}]*)\}\{([^}]*)\}', re.M) 15 | 16 | def html_hyperlink(file, id): 17 | return '\\href{../%s.html#%s}' % (file, id.replace('_', '-')) 18 | 19 | def html_transform_math_xref(node): 20 | new_text = xref_re.sub(lambda m: html_hyperlink(m.group(1), m.group(2)), node.astext()) 21 | node.children[0] = nodes.Text(new_text) 22 | 23 | # Mirrors sphinx/writers/latex 24 | def latex_hyperlink(file, id): 25 | id = text_type(id).translate(tex_replace_map).\ 26 | encode('ascii', 'backslashreplace').decode('ascii').\ 27 | replace('_', '-').replace('\\', '_') 28 | return '\\hyperref[%s:%s]' % (file, id) 29 | 30 | def latex_transform_math_xref(node): 31 | new_text = xref_re.sub(lambda m: latex_hyperlink(m.group(1), m.group(2)), node.astext()) 32 | node.children[0] = nodes.Text(new_text) 33 | 34 | # Expand mathdef names in math roles and directives 35 | 36 | def_re = re.compile('\\\\[A-Za-z][0-9A-Za-z]*', re.M) 37 | 38 | auxcounter = 0 39 | 40 | def lookup_mathdef(defs, name): 41 | if name in defs: 42 | [arity, s] = defs[name] 43 | if arity > 0: 44 | global auxcounter 45 | auxcounter = auxcounter + 1 46 | name = "\\mathdef%d" % auxcounter 47 | s = "\\def%s#%d{%s}%s" % (name, arity, s, name) 48 | return s 49 | return name 50 | 51 | def replace_mathdefs(doc, s): 52 | if not hasattr(doc, 'mathdefs'): 53 | return s 54 | return def_re.sub(lambda m: lookup_mathdef(doc.mathdefs, m.group(0)), s) 55 | 56 | def ext_math_role(role, raw, text, line, inliner, options = {}, content = []): 57 | text = replace_mathdefs(inliner.document, raw.split('`')[1]) 58 | return [math(raw, text)], [] 59 | 60 | class ExtMathDirective(MathDirective): 61 | def run(self): 62 | doc = self.state.document 63 | for i, s in enumerate(self.content): 64 | self.content[i] = replace_mathdefs(doc, s) 65 | for i, s in enumerate(self.arguments): 66 | self.arguments[i] = replace_mathdefs(doc, s) 67 | return super().run() 68 | 69 | class MathdefDirective(Replace): 70 | def run(self): 71 | name = '\\' + self.state.parent.rawsource.split('|')[1] 72 | name = name.split('#') 73 | if len(name) > 1: 74 | arity = int(name[1]) 75 | else: 76 | arity = 0 77 | name = name[0] 78 | doc = self.state.document 79 | if not hasattr(doc, 'mathdefs'): 80 | doc.mathdefs = {} 81 | # TODO: we don't ever hit the case where len(self.content) > 1 82 | for i, s in enumerate(self.content): 83 | self.content[i] = replace_mathdefs(doc, s) 84 | doc.mathdefs[name] = [arity, ''.join(self.content)] 85 | self.content[0] = ':math:`' + self.content[0] 86 | self.content[-1] = self.content[-1] + '`' 87 | return super().run() 88 | 89 | class WebAssemblyHTML5Translator(HTML5Translator): 90 | """ 91 | Customize HTML5Translator. 92 | Convert xref in math and math block nodes to hrefs. 93 | """ 94 | def visit_math(self, node, math_env = ''): 95 | html_transform_math_xref(node) 96 | super().visit_math(node, math_env) 97 | 98 | def visit_math_block(self, node, math_env = ''): 99 | html_transform_math_xref(node) 100 | super().visit_math_block(node, math_env) 101 | 102 | class WebAssemblyLaTeXTranslator(LaTeXTranslator): 103 | """ 104 | Customize LaTeXTranslator. 105 | Convert xref in math and math block nodes to hyperrefs. 106 | """ 107 | def visit_math(self, node): 108 | latex_transform_math_xref(node) 109 | super().visit_math(node) 110 | 111 | def visit_math_block(self, node): 112 | latex_transform_math_xref(node) 113 | super().visit_math_block(node) 114 | 115 | # Setup 116 | 117 | def setup(app): 118 | app.set_translator('html', WebAssemblyHTML5Translator) 119 | app.set_translator('latex', WebAssemblyLaTeXTranslator) 120 | app.add_role('math', ext_math_role) 121 | app.add_directive('math', ExtMathDirective, override = True) 122 | app.add_directive('mathdef', MathdefDirective) 123 | -------------------------------------------------------------------------------- /document/core/util/mathdefbs.py: -------------------------------------------------------------------------------- 1 | # Version of mathdef.py for bikeshed build. 2 | # Tweaked to generate single page links. 3 | # TODO(bradnelson): Figure out a way to merge this back into 4 | # mathdef.py controlled by buildername. 5 | 6 | from sphinx.ext.mathbase import math 7 | from sphinx.ext.mathbase import MathDirective 8 | from sphinx.ext.mathjax import html_visit_math 9 | from sphinx.ext.mathjax import html_visit_displaymath 10 | from sphinx.writers.html5 import HTML5Translator 11 | from docutils import nodes 12 | from docutils.parsers.rst.directives.misc import Replace 13 | import re 14 | 15 | 16 | # Transform \xref in math nodes 17 | 18 | xref_re = re.compile('\\\\xref\{([^}]*)\}\{([^}]*)\}', re.M) 19 | 20 | def html_hyperlink(file, id): 21 | return '\\href{#%s}' % (id.replace('_', '-')) 22 | 23 | def html_transform_math_xref(node): 24 | new_text = xref_re.sub(lambda m: html_hyperlink(m.group(1), m.group(2)), node.astext()) 25 | node.children[0] = nodes.Text(new_text) 26 | 27 | # Expand mathdef names in math roles and directives 28 | 29 | def_re = re.compile('\\\\[A-Za-z][0-9A-Za-z]*', re.M) 30 | 31 | auxcounter = 0 32 | 33 | def lookup_mathdef(defs, name): 34 | if name in defs: 35 | [arity, s] = defs[name] 36 | if arity > 0: 37 | global auxcounter 38 | auxcounter = auxcounter + 1 39 | name = "\\mathdef%d" % auxcounter 40 | s = "\\def%s#%d{%s}%s" % (name, arity, s, name) 41 | return s 42 | return name 43 | 44 | def replace_mathdefs(doc, s): 45 | if not hasattr(doc, 'mathdefs'): 46 | return s 47 | return def_re.sub(lambda m: lookup_mathdef(doc.mathdefs, m.group(0)), s) 48 | 49 | def ext_math_role(role, raw, text, line, inliner, options = {}, content = []): 50 | text = replace_mathdefs(inliner.document, raw.split('`')[1]) 51 | return [math(raw, text)], [] 52 | 53 | class ExtMathDirective(MathDirective): 54 | def run(self): 55 | doc = self.state.document 56 | for i, s in enumerate(self.content): 57 | self.content[i] = replace_mathdefs(doc, s) 58 | for i, s in enumerate(self.arguments): 59 | self.arguments[i] = replace_mathdefs(doc, s) 60 | return super(ExtMathDirective, self).run() 61 | 62 | class MathdefDirective(Replace): 63 | def run(self): 64 | name = '\\' + self.state.parent.rawsource.split('|')[1] 65 | name = name.split('#') 66 | if len(name) > 1: 67 | arity = int(name[1]) 68 | else: 69 | arity = 0 70 | name = name[0] 71 | doc = self.state.document 72 | if not hasattr(doc, 'mathdefs'): 73 | doc.mathdefs = {} 74 | for i, s in enumerate(self.content): 75 | self.content[i] = replace_mathdefs(doc, s) 76 | doc.mathdefs[name] = [arity, ''.join(self.content)] 77 | self.content[0] = ':math:`' + self.content[0] 78 | self.content[-1] = self.content[-1] + '`' 79 | return super(MathdefDirective, self).run() 80 | 81 | class WebAssemblyHTML5Translator(HTML5Translator): 82 | """ 83 | Customize HTML5Translator. 84 | Convert xref in math and math block nodes to hrefs. 85 | """ 86 | def visit_math(self, node, math_env = ''): 87 | html_transform_math_xref(node) 88 | super().visit_math(node, math_env) 89 | 90 | def visit_math_block(self, node, math_env = ''): 91 | html_transform_math_xref(node) 92 | super().visit_math_block(node, math_env) 93 | 94 | # Setup 95 | 96 | def setup(app): 97 | app.set_translator('singlehtml', WebAssemblyHTML5Translator) 98 | app.add_role('math', ext_math_role) 99 | app.add_directive('math', ExtMathDirective, override = True) 100 | app.add_directive('mathdef', MathdefDirective) 101 | -------------------------------------------------------------------------------- /document/core/util/pseudo-lexer.py: -------------------------------------------------------------------------------- 1 | from pygments.lexer import RegexLexer 2 | from pygments.token import * 3 | from sphinx.highlighting import lexers 4 | 5 | class PseudoLexer(RegexLexer): 6 | name = 'Pseudo' 7 | aliases = ['pseudo'] 8 | filenames = ['*.pseudo'] 9 | 10 | tokens = { 11 | 'root': [ 12 | (r"(? 2 | 3 | 4 | 5 | 6 | 7 | WebAssembly Specifications 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 |
16 | 17 |

WebAssembly Specifications

18 | 19 |

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

22 | 23 |

Core specification

24 | 25 |

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

27 | 28 |
    29 |
  • 30 |

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

    35 | 41 |
  • 42 |
43 | 44 |

Embedder specifications

45 | 46 |

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

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

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

    58 | 62 |
  • 63 |
64 | 65 |

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

69 | 70 |
71 |
72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /document/js-api/Makefile: -------------------------------------------------------------------------------- 1 | BUILDDIR = _build 2 | STATICDIR = _static 3 | DOWNLOADDIR = _download 4 | NAME = WebAssembly 5 | 6 | .PHONY: all 7 | all: 8 | mkdir -p $(BUILDDIR)/html 9 | bikeshed spec index.bs $(BUILDDIR)/html/index.html 10 | @echo "Build finished. The HTML pages are in `pwd`/$(BUILDDIR)/html." 11 | 12 | .PHONY: publish 13 | publish: 14 | (cd ..; make publish-js-api) 15 | 16 | .PHONY: clean 17 | clean: 18 | rm -rf $(BUILDDIR) 19 | rm -rf $(STATICDIR) 20 | 21 | .PHONY: diff 22 | diff: all 23 | @echo "Downloading the old single-file html spec..." 24 | curl `grep "^TR" index.bs | cut -d' ' -f2` -o $(BUILDDIR)/html/old.html 25 | @echo "Done." 26 | @echo "Diffing new against old..." 27 | perl ../util/htmldiff.pl $(BUILDDIR)/html/old.html $(BUILDDIR)/html/index.html $(BUILDDIR)/html/diff.html 28 | @echo "Done. The diff is at $(BUILDDIR)/html/diff.html" 29 | 30 | .PHONY: WD-tar 31 | WD-tar: 32 | bikeshed echidna --just-tar index.bs $(BUILDDIR)/html/index.html 33 | mv test.tar $(BUILDDIR)/WD.tar 34 | @echo "Built $(BUILDDIR)/WD.tar." 35 | 36 | .PHONY: WD-echidna 37 | WD-echidna: 38 | @if [ -z $(W3C_USERNAME) ] || \ 39 | [ -z $(W3C_PASSWORD) ] || \ 40 | [ -z $(DECISION_URL) ] ; then \ 41 | echo "Must provide W3C_USERNAME, W3C_PASSWORD, and DECISION_URL environment variables"; \ 42 | exit 1; \ 43 | fi 44 | bikeshed echidna index.bs --u $(W3C_USERNAME) --p $(W3C_PASSWORD) --d $(DECISION_URL) 45 | -------------------------------------------------------------------------------- /document/travis-deploy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Adapted from https://github.com/heycam/webidl/blob/master/deploy.sh 4 | 5 | set -e # Exit with nonzero exit code if anything fails 6 | 7 | SOURCE_BRANCH="master" 8 | TARGET_BRANCH="gh-pages" 9 | 10 | function doCompile { 11 | # TODO(littledan): Integrate with document/deploy.sh 12 | (cd document; make) 13 | } 14 | 15 | # Pull requests and commits to other branches shouldn't try to deploy, just build to verify 16 | if [[ "$TRAVIS_PULL_REQUEST" != "false" || "$TRAVIS_BRANCH" != "$SOURCE_BRANCH" ]]; then 17 | echo "Skipping deploy; just doing a build." 18 | doCompile 19 | exit 0 20 | fi 21 | 22 | # Save some useful information 23 | REPO=`git config remote.origin.url` 24 | SSH_REPO=${REPO/https:\/\/github.com\//git@github.com:} 25 | SHA=`git rev-parse --verify HEAD` 26 | 27 | # Get the deploy key by using Travis's stored variables to decrypt deploy_key.enc 28 | ENCRYPTED_KEY_VAR="encrypted_${ENCRYPTION_LABEL}_key" 29 | ENCRYPTED_IV_VAR="encrypted_${ENCRYPTION_LABEL}_iv" 30 | ENCRYPTED_KEY=${!ENCRYPTED_KEY_VAR} 31 | ENCRYPTED_IV=${!ENCRYPTED_IV_VAR} 32 | openssl aes-256-cbc -K $ENCRYPTED_KEY -iv $ENCRYPTED_IV -in deploy_key.enc -out deploy_key -d || true 33 | chmod 600 deploy_key 34 | eval `ssh-agent -s` 35 | ssh-add deploy_key || true 36 | 37 | # DON'T MOVE ABOVE AS IT WILL REVEAL KEYS! 38 | # Turn on logging from here on. 39 | set -x 40 | 41 | # Clone the existing gh-pages for this repo into _build/ 42 | # Create a new empty branch if gh-pages doesn't exist yet (should only happen 43 | # on first deploy). 44 | # Clean document/*/_build. 45 | (cd document; make clean) 46 | # Ensure no checkout in _build. 47 | rm -rf document/_build 48 | # Clone a second checkout of our repo to _build. 49 | git clone $REPO document/_build 50 | ( 51 | # Checkout a parentless branch of gh-pages. 52 | cd document/_build 53 | git checkout $TARGET_BRANCH || git checkout --orphan $TARGET_BRANCH 54 | 55 | # Clean out existing contents (to be replaced with output from the build 56 | # that happens after this). 57 | rm -rf ./* 58 | ) 59 | 60 | # Run our compile script (output into _build). 61 | doCompile 62 | 63 | # Set user info on the gh-pages repo in _build. 64 | cd document/_build 65 | git config user.name "Travis CI" 66 | git config user.email "$COMMIT_AUTHOR_EMAIL" 67 | 68 | # If there are no changes to the compiled out (e.g. this is a README update) then just bail. 69 | if [[ -z "$(git status --porcelain)" ]]; then 70 | echo "No changes to the output on this push; exiting." 71 | exit 0 72 | fi 73 | 74 | # Commit the "changes", i.e. the new version. 75 | # The delta will show diffs between new and old versions. 76 | git add --all . 77 | git commit -m "Deploy to GitHub Pages: ${SHA}" 78 | 79 | # Now that we're all set up, we can push. 80 | git push $SSH_REPO $TARGET_BRANCH 81 | -------------------------------------------------------------------------------- /document/web-api/Makefile: -------------------------------------------------------------------------------- 1 | BUILDDIR = _build 2 | STATICDIR = _static 3 | DOWNLOADDIR = _download 4 | NAME = WebAssembly 5 | 6 | .PHONY: all 7 | all: 8 | mkdir -p $(BUILDDIR)/html 9 | bikeshed spec index.bs $(BUILDDIR)/html/index.html 10 | @echo "Build finished. The HTML pages are in `pwd`/$(BUILDDIR)/html." 11 | 12 | .PHONY: publish 13 | publish: 14 | (cd ..; make publish-web-api) 15 | 16 | .PHONY: clean 17 | clean: 18 | rm -rf $(BUILDDIR) 19 | rm -rf $(STATICDIR) 20 | 21 | .PHONY: diff 22 | diff: all 23 | @echo "Downloading the old single-file html spec..." 24 | curl `grep "^TR" index.bs | cut -d' ' -f2` -o $(BUILDDIR)/html/old.html 25 | @echo "Done." 26 | @echo "Diffing new against old..." 27 | perl ../util/htmldiff.pl $(BUILDDIR)/html/old.html $(BUILDDIR)/html/index.html $(BUILDDIR)/html/diff.html 28 | @echo "Done. The diff is at $(BUILDDIR)/html/diff.html" 29 | 30 | .PHONY: WD-tar 31 | WD-tar: 32 | bikeshed echidna --just-tar index.bs $(BUILDDIR)/html/index.html 33 | mv test.tar $(BUILDDIR)/WD.tar 34 | @echo "Built $(BUILDDIR)/WD.tar." 35 | 36 | .PHONY: WD-echidna 37 | WD-echidna: 38 | @if [ -z $(W3C_USERNAME) ] || \ 39 | [ -z $(W3C_PASSWORD) ] || \ 40 | [ -z $(DECISION_URL) ] ; then \ 41 | echo "Must provide W3C_USERNAME, W3C_PASSWORD, and DECISION_URL environment variables"; \ 42 | exit 1; \ 43 | fi 44 | bikeshed echidna index.bs --u $(W3C_USERNAME) --p $(W3C_PASSWORD) --d $(DECISION_URL) 45 | -------------------------------------------------------------------------------- /interpreter/.gitignore: -------------------------------------------------------------------------------- 1 | *.cmo 2 | *.cmx 3 | *.native 4 | *.byte 5 | *.opt 6 | *.unopt 7 | *.js 8 | *.zip 9 | *.mlpack 10 | _build 11 | wasm 12 | wasm.debug 13 | 14 | -------------------------------------------------------------------------------- /interpreter/Makefile: -------------------------------------------------------------------------------- 1 | # This Makefile uses ocamlbuild but does not rely on ocamlfind or the Opam 2 | # package manager to build. However, Opam package management is available 3 | # optionally through the check/install/uninstall targets. 4 | # 5 | # The $(JSLIB) target requires node.js and BuckleScript. 6 | # 7 | # See README.me for instructions. 8 | 9 | 10 | # Configuration 11 | 12 | NAME = wasm 13 | UNOPT = $(NAME).debug 14 | OPT = $(NAME) 15 | LIB = $(NAME) 16 | ZIP = $(NAME).zip 17 | JSLIB = wast.js 18 | WINMAKE = winmake.bat 19 | 20 | DIRS = util syntax binary text valid runtime exec script host main 21 | LIBS = bigarray 22 | FLAGS = -cflags '-w +a-4-27-42-44-45 -warn-error +a-3' 23 | OCB = ocamlbuild $(FLAGS) $(DIRS:%=-I %) $(LIBS:%=-libs %) 24 | JS = # set to JS shell command to run JS tests 25 | 26 | 27 | # Main targets 28 | 29 | .PHONY: default opt unopt libopt libunopt jslib all land zip 30 | 31 | default: opt 32 | debug: unopt 33 | opt: $(OPT) 34 | unopt: $(UNOPT) 35 | libopt: _build/$(LIB).cmx 36 | libunopt: _build/$(LIB).cmo 37 | jslib: $(JSLIB) 38 | all: unopt opt libunopt libopt test 39 | land: $(WINMAKE) all 40 | zip: $(ZIP) 41 | 42 | 43 | # Building executable 44 | 45 | empty = 46 | space = $(empty) $(empty) 47 | comma = , 48 | 49 | .INTERMEDIATE: _tags 50 | _tags: 51 | echo >$@ "true: bin_annot" 52 | echo >>$@ "true: debug" 53 | echo >>$@ "<{$(subst $(space),$(comma),$(DIRS))}/*.cmx>: for-pack($(PACK))" 54 | 55 | $(UNOPT): main.byte 56 | mv $< $@ 57 | 58 | $(OPT): main.native 59 | mv $< $@ 60 | 61 | .PHONY: main.byte main.native 62 | main.byte: _tags 63 | $(OCB) -quiet $@ 64 | 65 | main.native: _tags 66 | $(OCB) -quiet $@ 67 | 68 | 69 | # Building library 70 | 71 | PACK = $(shell echo `echo $(LIB) | sed 's/^\(.\).*$$/\\1/g' | tr [:lower:] [:upper:]``echo $(LIB) | sed 's/^.\(.*\)$$/\\1/g'`) 72 | 73 | .INTERMEDIATE: $(LIB).mlpack 74 | $(LIB).mlpack: $(DIRS) 75 | ls $(DIRS:%=%/*.ml*) \ 76 | | sed 's:\(.*/\)\{0,1\}\(.*\)\.[^\.]*:\2:' \ 77 | | grep -v main \ 78 | | sort | uniq \ 79 | >$@ 80 | 81 | _build/$(LIB).cmo: $(LIB).mlpack _tags 82 | $(OCB) -quiet $(LIB).cmo 83 | 84 | _build/$(LIB).cmx: $(LIB).mlpack _tags 85 | $(OCB) -quiet $(LIB).cmx 86 | 87 | 88 | # Building JavaScript library 89 | 90 | .PHONY: $(JSLIB) 91 | $(JSLIB): $(UNOPT) 92 | mkdir -p _build/jslib/src 93 | cp meta/jslib/* _build/jslib 94 | cp $(DIRS:%=_build/%/*.ml*) meta/jslib/*.ml _build/jslib/src 95 | rm _build/jslib/src/*.ml[^i] 96 | (cd _build/jslib; ./build.sh ../../$@) 97 | 98 | 99 | # Building Windows build file 100 | 101 | $(WINMAKE): clean 102 | echo rem Auto-generated from Makefile! >$@ 103 | echo set NAME=$(NAME) >>$@ 104 | echo if \'%1\' neq \'\' set NAME=%1 >>$@ 105 | $(OCB) main.byte \ 106 | | grep -v ocamldep \ 107 | | grep -v mkdir \ 108 | | sed s:`which ocaml`:ocaml:g \ 109 | | sed s:main/main.d.byte:%NAME%.exe: \ 110 | >>$@ 111 | 112 | 113 | # Executing test suite 114 | 115 | .PHONY: test debugtest 116 | 117 | test: $(OPT) 118 | ../test/core/run.py --wasm `pwd`/$(OPT) $(if $(JS),--js '$(JS)',) 119 | debugtest: $(UNOPT) 120 | ../test/core/run.py --wasm `pwd`/$(UNOPT) $(if $(JS),--js '$(JS)',) 121 | 122 | test/%: $(OPT) 123 | ../test/core/run.py --wasm `pwd`/$(OPT) $(if $(JS),--js '$(JS)',) $(@:test/%=../test/core/%.wast) 124 | debugtest/%: $(UNOPT) 125 | ../test/core/run.py --wasm `pwd`/$(UNOPT) $(if $(JS),--js '$(JS)',) $(@:debugtest/%=../test/core/%.wast) 126 | 127 | run/%: $(OPT) 128 | ./$(OPT) $(@:run/%=../test/core/%.wast) 129 | debug/%: $(UNOPT) 130 | ./$(UNOPT) $(@:debug/%=../test/core/%.wast) 131 | 132 | 133 | # Miscellaneous targets 134 | 135 | .PHONY: clean 136 | 137 | $(ZIP): $(WINMAKE) 138 | git archive --format=zip --prefix=$(NAME)/ -o $@ HEAD 139 | 140 | clean: 141 | rm -rf _build/jslib $(LIB).mlpack _tags 142 | $(OCB) -clean 143 | 144 | 145 | # Opam support 146 | 147 | .PHONY: check install uninstall 148 | 149 | check: 150 | # Check that we can find all relevant libraries 151 | # when using ocamlfind 152 | ocamlfind query $(LIBS) 153 | 154 | install: _build/$(LIB).cmx _build/$(LIB).cmo 155 | ocamlfind install $(LIB) meta/findlib/META _build/$(LIB).o \ 156 | $(wildcard _build/$(LIB).cm*) \ 157 | $(wildcard $(DIRS:%=%/*.mli)) 158 | 159 | uninstall: 160 | ocamlfind remove $(LIB) 161 | -------------------------------------------------------------------------------- /interpreter/binary/decode.mli: -------------------------------------------------------------------------------- 1 | exception Code of Source.region * string 2 | 3 | val decode : string -> string -> Ast.module_ (* raises Code *) 4 | -------------------------------------------------------------------------------- /interpreter/binary/encode.mli: -------------------------------------------------------------------------------- 1 | exception Code of Source.region * string 2 | 3 | val version : int32 4 | val encode : Ast.module_ -> string 5 | 6 | -------------------------------------------------------------------------------- /interpreter/binary/utf8.ml: -------------------------------------------------------------------------------- 1 | exception Utf8 2 | 3 | let con n = 0x80 lor (n land 0x3f) 4 | 5 | let rec encode ns = Lib.String.implode (List.map Char.chr (encode' ns)) 6 | and encode' = function 7 | | [] -> [] 8 | | n::ns when n < 0 -> 9 | raise Utf8 10 | | n::ns when n < 0x80 -> 11 | n :: encode' ns 12 | | n::ns when n < 0x800 -> 13 | 0xc0 lor (n lsr 6) :: con n :: encode' ns 14 | | n::ns when n < 0x10000 -> 15 | 0xe0 lor (n lsr 12) :: con (n lsr 6) :: con n :: encode' ns 16 | | n::ns when n < 0x110000 -> 17 | 0xf0 lor (n lsr 18) :: con (n lsr 12) :: con (n lsr 6) :: con n 18 | :: encode' ns 19 | | _ -> 20 | raise Utf8 21 | 22 | let con b = if b land 0xc0 = 0x80 then b land 0x3f else raise Utf8 23 | let code min n = 24 | if n < min || (0xd800 <= n && n < 0xe000) || n >= 0x110000 then raise Utf8 25 | else n 26 | 27 | let rec decode s = decode' (List.map Char.code (Lib.String.explode s)) 28 | and decode' = function 29 | | [] -> [] 30 | | b1::bs when b1 < 0x80 -> 31 | code 0x0 b1 :: decode' bs 32 | | b1::bs when b1 < 0xc0 -> 33 | raise Utf8 34 | | b1::b2::bs when b1 < 0xe0 -> 35 | code 0x80 ((b1 land 0x1f) lsl 6 + con b2) :: decode' bs 36 | | b1::b2::b3::bs when b1 < 0xf0 -> 37 | code 0x800 ((b1 land 0x0f) lsl 12 + con b2 lsl 6 + con b3) :: decode' bs 38 | | b1::b2::b3::b4::bs when b1 < 0xf8 -> 39 | code 0x10000 ((b1 land 0x07) lsl 18 + con b2 lsl 12 + con b3 lsl 6 + con b4) 40 | :: decode' bs 41 | | _ -> 42 | raise Utf8 43 | -------------------------------------------------------------------------------- /interpreter/binary/utf8.mli: -------------------------------------------------------------------------------- 1 | exception Utf8 2 | 3 | val decode : string -> int list (* raises Utf8 *) 4 | val encode : int list -> string (* raises Utf8 *) 5 | -------------------------------------------------------------------------------- /interpreter/exec/eval.mli: -------------------------------------------------------------------------------- 1 | open Values 2 | open Instance 3 | 4 | exception Link of Source.region * string 5 | exception Trap of Source.region * string 6 | exception Crash of Source.region * string 7 | exception Exhaustion of Source.region * string 8 | 9 | val init : Ast.module_ -> extern list -> module_inst (* raises Link, Trap *) 10 | val invoke : func_inst -> value list -> value list (* raises Trap *) 11 | -------------------------------------------------------------------------------- /interpreter/exec/eval_numeric.mli: -------------------------------------------------------------------------------- 1 | open Values 2 | 3 | exception TypeError of int * value * Types.value_type 4 | 5 | val eval_unop : Ast.unop -> value -> value 6 | val eval_binop : Ast.binop -> value -> value -> value 7 | val eval_testop : Ast.testop -> value -> bool 8 | val eval_relop : Ast.relop -> value -> value -> bool 9 | val eval_cvtop : Ast.cvtop -> value -> value 10 | -------------------------------------------------------------------------------- /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 Float.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 signifant 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) *. (* TODO(ocaml-4.03): 0x1p12 *) 4096.0 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) *. (* TODO(ocaml-4.03): 0x1p12 *) 4096.0 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 Float.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/i32.ml: -------------------------------------------------------------------------------- 1 | (* WebAssembly-compatible i32 implementation *) 2 | 3 | include Int.Make 4 | (struct 5 | include Int32 6 | let bitwidth = 32 7 | let to_hex_string = Printf.sprintf "%lx" 8 | end) 9 | -------------------------------------------------------------------------------- /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 Numeric_error.InvalidConversionToInteger 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 Numeric_error.IntegerOverflow 12 | else 13 | Int32.of_float xf 14 | 15 | let trunc_f32_u x = 16 | if F32.ne x x then 17 | raise Numeric_error.InvalidConversionToInteger 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 Numeric_error.IntegerOverflow 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 Numeric_error.InvalidConversionToInteger 28 | else 29 | let xf = F64.to_float x in 30 | if xf >= -.Int32.(to_float min_int) || xf < Int32.(to_float min_int) then 31 | raise Numeric_error.IntegerOverflow 32 | else 33 | Int32.of_float xf 34 | 35 | let trunc_f64_u x = 36 | if F64.ne x x then 37 | raise Numeric_error.InvalidConversionToInteger 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 Numeric_error.IntegerOverflow 42 | else 43 | Int64.(to_int32 (of_float xf)) 44 | 45 | let reinterpret_f32 = F32.to_bits 46 | -------------------------------------------------------------------------------- /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 reinterpret_f32 : F32.t -> I32.t 9 | -------------------------------------------------------------------------------- /interpreter/exec/i64.ml: -------------------------------------------------------------------------------- 1 | (* WebAssembly-compatible i64 implementation *) 2 | 3 | include Int.Make 4 | (struct 5 | include Int64 6 | let bitwidth = 64 7 | let to_hex_string = Printf.sprintf "%Lx" 8 | end) 9 | -------------------------------------------------------------------------------- /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 Numeric_error.InvalidConversionToInteger 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 Numeric_error.IntegerOverflow 14 | else 15 | Int64.of_float xf 16 | 17 | let trunc_f32_u x = 18 | if F32.ne x x then 19 | raise Numeric_error.InvalidConversionToInteger 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 Numeric_error.IntegerOverflow 24 | else if xf >= -.Int64.(to_float min_int) then 25 | Int64.(logxor (of_float (xf -. (* TODO(ocaml-4.03): 0x1p63 *) 9223372036854775808.0)) 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 Numeric_error.InvalidConversionToInteger 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 Numeric_error.IntegerOverflow 36 | else 37 | Int64.of_float xf 38 | 39 | let trunc_f64_u x = 40 | if F64.ne x x then 41 | raise Numeric_error.InvalidConversionToInteger 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 Numeric_error.IntegerOverflow 46 | else if xf >= -.Int64.(to_float min_int) then 47 | Int64.(logxor (of_float (xf -. (* TODO(ocaml-4.03): 0x1p63 *) 9223372036854775808.0)) min_int) 48 | else 49 | Int64.of_float xf 50 | 51 | let reinterpret_f64 = F64.to_bits 52 | -------------------------------------------------------------------------------- /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 reinterpret_f64 : F64.t -> I64.t 10 | -------------------------------------------------------------------------------- /interpreter/exec/numeric_error.ml: -------------------------------------------------------------------------------- 1 | exception IntegerOverflow 2 | exception IntegerDivideByZero 3 | exception InvalidConversionToInteger 4 | -------------------------------------------------------------------------------- /interpreter/host/env.ml: -------------------------------------------------------------------------------- 1 | (* 2 | * Emulation of (a subset of) the `env` module currently used by Binaryen, 3 | * so that we can run modules generated by Binaryen. This is a stopgap until 4 | * we have agreement on what libc should look like. 5 | *) 6 | 7 | open Values 8 | open Types 9 | open Instance 10 | 11 | 12 | let error msg = raise (Eval.Crash (Source.no_region, msg)) 13 | 14 | let type_error v t = 15 | error 16 | ("type error, expected " ^ string_of_value_type t ^ 17 | ", got " ^ string_of_value_type (type_of 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 | | I32 i -> Int32.to_int i 30 | | v -> type_error v I32Type 31 | 32 | 33 | let abort vs = 34 | empty vs; 35 | print_endline "Abort!"; 36 | exit (-1) 37 | 38 | let exit vs = 39 | exit (int (single vs)) 40 | 41 | 42 | let lookup name t = 43 | match Utf8.encode name, t with 44 | | "abort", ExternFuncType t -> ExternFunc (Func.alloc_host t abort) 45 | | "exit", ExternFuncType t -> ExternFunc (Func.alloc_host t exit) 46 | | _ -> raise Not_found 47 | -------------------------------------------------------------------------------- /interpreter/host/spectest.ml: -------------------------------------------------------------------------------- 1 | (* 2 | * Simple collection of functions useful for writing test cases. 3 | *) 4 | 5 | open Types 6 | open Values 7 | open Instance 8 | 9 | 10 | let global (GlobalType (t, _) as gt) = 11 | let v = 12 | match t with 13 | | I32Type -> I32 666l 14 | | I64Type -> I64 666L 15 | | F32Type -> F32 (F32.of_float 666.6) 16 | | F64Type -> F64 (F64.of_float 666.6) 17 | in Global.alloc gt v 18 | 19 | let table = Table.alloc (TableType ({min = 10l; max = Some 20l}, FuncRefType)) 20 | let memory = Memory.alloc (MemoryType {min = 1l; max = Some 2l}) 21 | let func f t = Func.alloc_host t (f t) 22 | 23 | let print_value v = 24 | Printf.printf "%s : %s\n" 25 | (Values.string_of_value v) (Types.string_of_value_type (Values.type_of v)) 26 | 27 | let print (FuncType (_, out)) vs = 28 | List.iter print_value vs; 29 | flush_all (); 30 | List.map default_value out 31 | 32 | 33 | let lookup name t = 34 | match Utf8.encode name, t with 35 | | "print", _ -> ExternFunc (func print (FuncType ([], []))) 36 | | "print_i32", _ -> ExternFunc (func print (FuncType ([I32Type], []))) 37 | | "print_i32_f32", _ -> 38 | ExternFunc (func print (FuncType ([I32Type; F32Type], []))) 39 | | "print_f64_f64", _ -> 40 | ExternFunc (func print (FuncType ([F64Type; F64Type], []))) 41 | | "print_f32", _ -> ExternFunc (func print (FuncType ([F32Type], []))) 42 | | "print_f64", _ -> ExternFunc (func print (FuncType ([F64Type], []))) 43 | | "global_i32", _ -> ExternGlobal (global (GlobalType (I32Type, Immutable))) 44 | | "global_f32", _ -> ExternGlobal (global (GlobalType (F32Type, Immutable))) 45 | | "global_f64", _ -> ExternGlobal (global (GlobalType (F64Type, Immutable))) 46 | | "table", _ -> ExternTable table 47 | | "memory", _ -> ExternMemory memory 48 | | _ -> raise Not_found 49 | -------------------------------------------------------------------------------- /interpreter/main/flags.ml: -------------------------------------------------------------------------------- 1 | let interactive = ref false 2 | let trace = ref false 3 | let unchecked = ref false 4 | let print_sig = ref false 5 | let dry = ref false 6 | let width = ref 80 7 | let harness = ref true 8 | -------------------------------------------------------------------------------- /interpreter/main/main.ml: -------------------------------------------------------------------------------- 1 | let name = "wasm" 2 | let version = "1.0" 3 | 4 | let configure () = 5 | Import.register (Utf8.decode "spectest") Spectest.lookup; 6 | Import.register (Utf8.decode "env") Env.lookup 7 | 8 | let banner () = 9 | print_endline (name ^ " " ^ version ^ " reference interpreter") 10 | 11 | let usage = "Usage: " ^ name ^ " [option] [file ...]" 12 | 13 | let args = ref [] 14 | let add_arg source = args := !args @ [source] 15 | 16 | let quote s = "\"" ^ String.escaped s ^ "\"" 17 | 18 | let argspec = Arg.align 19 | [ 20 | "-", Arg.Set Flags.interactive, 21 | " run interactively (default if no files given)"; 22 | "-e", Arg.String add_arg, " evaluate string"; 23 | "-i", Arg.String (fun file -> add_arg ("(input " ^ quote file ^ ")")), 24 | " read script from file"; 25 | "-o", Arg.String (fun file -> add_arg ("(output " ^ quote file ^ ")")), 26 | " write module to file"; 27 | "-w", Arg.Int (fun n -> Flags.width := n), 28 | " configure output width (default is 80)"; 29 | "-s", Arg.Set Flags.print_sig, " show module signatures"; 30 | "-u", Arg.Set Flags.unchecked, " unchecked, do not perform validation"; 31 | "-h", Arg.Clear Flags.harness, " exclude harness for JS conversion"; 32 | "-d", Arg.Set Flags.dry, " dry, do not run program"; 33 | "-t", Arg.Set Flags.trace, " trace execution"; 34 | "-v", Arg.Unit banner, " show version" 35 | ] 36 | 37 | let () = 38 | Printexc.record_backtrace true; 39 | try 40 | configure (); 41 | Arg.parse argspec 42 | (fun file -> add_arg ("(input " ^ quote file ^ ")")) usage; 43 | List.iter (fun arg -> if not (Run.run_string arg) then exit 1) !args; 44 | if !args = [] then Flags.interactive := true; 45 | if !Flags.interactive then begin 46 | Flags.print_sig := true; 47 | banner (); 48 | Run.run_stdin () 49 | end 50 | with exn -> 51 | flush_all (); 52 | prerr_endline 53 | (Sys.argv.(0) ^ ": uncaught exception " ^ Printexc.to_string exn); 54 | Printexc.print_backtrace stderr; 55 | exit 2 56 | -------------------------------------------------------------------------------- /interpreter/meta/findlib/META: -------------------------------------------------------------------------------- 1 | description = "A library for writing/reading/running WebAssembly binaries" 2 | requires = "bigarray,str" 3 | archive(byte) = "wasm.cmo" 4 | archive(native) = "wasm.cmx" 5 | -------------------------------------------------------------------------------- /interpreter/meta/jslib/bsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wasm", 3 | "sources": [ 4 | {"dir": "src"}, 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /interpreter/meta/jslib/build.sh: -------------------------------------------------------------------------------- 1 | link () { 2 | echo "// DO NOT EDIT. Generated from WebAssembly spec interpreter" 3 | echo " 4 | let WebAssemblyText = (function() { 5 |   let _registry = {__proto__: null}; 6 | function normalize(file) { 7 | return file.split('/').reverse()[0].split('.')[0]; 8 | } 9 |   function require(file) { 10 |     let name = normalize(file); 11 | if (!(name in _registry)) { 12 | throw new Error('missing module: ' + name) 13 |     } else if (typeof _registry[name] === 'function') { 14 | " 15 | if (($LOG == 1)) 16 | then 17 | echo 1>&2 Logging on 18 | echo " 19 | console.log(name); 20 | " 21 | fi 22 | echo " 23 |       let f = _registry[name]; 24 |       _registry[name] = function() { throw new Error('cyclic module: ' + name) }; 25 |       _registry[name] = f(); 26 |     } 27 |     return _registry[name]; 28 |   } 29 | " 30 | 31 | for file in $* 32 | do 33 | echo 1>&2 Including $file 34 | name=`basename $file | sed s/[.]js//g` 35 | echo " 36 | _registry['$name'] = function() { 37 |     let exports = {}; 38 | //////// start of $name.js ////////" 39 | cat $file 40 | echo "//////// end of $name.js //////// 41 |     return exports; 42 |   }; 43 | " 44 | done 45 | 46 | echo " 47 | function binary(bytes) { 48 | let buffer = new ArrayBuffer(bytes.length); 49 | let view = new Uint8Array(buffer); 50 | for (let i = 0; i < bytes.length; ++i) { 51 | view[i] = bytes.charCodeAt(i); 52 | } 53 | return buffer; 54 | } 55 | function bytes(buffer) { 56 | let string = ''; 57 | let view = new Uint8Array(buffer); 58 | for (let i = 0; i < view.length; ++i) { 59 | string += String.fromCodePoint(view[i]); 60 | } 61 | return string; 62 | } 63 |   let Wasm = require('wasm'); 64 | return { 65 | encode(s) { return binary(Wasm.encode(s)) }, 66 | decode(b, w = 80) { return Wasm.decode(bytes(b), w) } 67 | }; 68 | })(); 69 | 70 | " 71 | } 72 | 73 | echo 1>&2 ==== Preparing ==== 74 | npm link bs-platform 75 | 76 | echo 1>&2 ==== Compiling ==== 77 | bsb || exit 1 78 | 79 | echo 1>&2 ==== Linking full version ==== 80 | LOG=1 81 | FILES='node_modules/bs-platform/lib/js/*.js lib/js/src/*.js' 82 | link $FILES >temp.js || exit 1 83 | 84 | echo 1>&2 ==== Running for dependencies ==== 85 | node temp.js | tee temp.log || exit 1 86 | 87 | echo 1>&2 ==== Linking stripped version ==== 88 | used='' 89 | for file in `ls $FILES` 90 | do 91 | if grep -q `basename $file | sed s/.js//g` temp.log 92 | then 93 | used="$used $file" 94 | fi 95 | done 96 | LOG=0 97 | link $used >$1 || exit 1 98 | -------------------------------------------------------------------------------- /interpreter/meta/jslib/wasm.ml: -------------------------------------------------------------------------------- 1 | let encode s = 2 | let def = Parse.string_to_module s in 3 | match def.Source.it with 4 | | Script.Textual m -> Encode.encode m 5 | | Script.Encoded (_, bs) -> bs 6 | 7 | let decode s width = 8 | let m = Decode.decode "(decode)" s in 9 | Sexpr.to_string width (Arrange.module_ m) 10 | -------------------------------------------------------------------------------- /interpreter/meta/travis/build-test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | set -x 5 | 6 | # Move to a location relative to the script so it runs 7 | # from anywhere. 8 | cd $(dirname ${BASH_SOURCE[0]})/../.. 9 | 10 | make all 11 | -------------------------------------------------------------------------------- /interpreter/meta/travis/install-ocaml.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | download_from_gh_archive() { 6 | local project=$1; 7 | local version=$2; 8 | local sha=$3; 9 | 10 | curl https://github.com/ocaml/${project}/archive/${version}.tar.gz -OL 11 | CHECKSUM=$(shasum -a 256 ${version}.tar.gz | awk '{ print $1 }') 12 | if [ ${CHECKSUM} != ${sha} ]; then 13 | echo "Bad checksum ${project} download checksum!" 14 | exit 1 15 | fi 16 | tar xfz ${version}.tar.gz 17 | } 18 | 19 | # Move to a location relative to the script so it runs 20 | # from anywhere. Go three levels down to get out of interpreter/ 21 | # and into the top-level dir, since we'll run ocamlbuild 22 | # inside of interpreter/ and it goes pear-shaped if it 23 | # encounters ocaml's own build directory. 24 | cd $(dirname ${BASH_SOURCE[0]})/../../.. 25 | 26 | PREFIX=$PWD/ocaml/install 27 | 28 | rm -rf ocaml 29 | mkdir ocaml 30 | cd ocaml 31 | mkdir install 32 | 33 | download_from_gh_archive ocaml 4.05.0 e5d8a6f629020c580473d8afcfcb06c3966d01929f7b734f41dc0c737cd8ea3f 34 | cd ocaml-4.05.0 35 | ./configure -prefix ${PREFIX} 36 | make world.opt 37 | make install 38 | cd .. 39 | 40 | PATH=${PREFIX}/bin:${PATH} 41 | 42 | download_from_gh_archive ocamlbuild 0.11.0 1717edc841c9b98072e410f1b0bc8b84444b4b35ed3b4949ce2bec17c60103ee 43 | cd ocamlbuild-0.11.0 44 | make configure 45 | make 46 | make install 47 | -------------------------------------------------------------------------------- /interpreter/runtime/func.ml: -------------------------------------------------------------------------------- 1 | open Types 2 | open Values 3 | 4 | type 'inst t = 'inst func 5 | and 'inst func = 6 | | AstFunc of func_type * 'inst * Ast.func 7 | | HostFunc of func_type * (value list -> value list) 8 | 9 | let alloc ft inst f = AstFunc (ft, inst, f) 10 | let alloc_host ft f = HostFunc (ft, f) 11 | 12 | let type_of = function 13 | | AstFunc (ft, _, _) -> ft 14 | | HostFunc (ft, _) -> ft 15 | -------------------------------------------------------------------------------- /interpreter/runtime/func.mli: -------------------------------------------------------------------------------- 1 | open Types 2 | open Values 3 | 4 | type 'inst t = 'inst func 5 | and 'inst func = 6 | | AstFunc of func_type * 'inst * Ast.func 7 | | HostFunc of func_type * (value list -> value list) 8 | 9 | val alloc : func_type -> 'inst -> Ast.func -> 'inst func 10 | val alloc_host : func_type -> (value list -> value list) -> 'inst func 11 | val type_of : 'inst func -> func_type 12 | -------------------------------------------------------------------------------- /interpreter/runtime/global.ml: -------------------------------------------------------------------------------- 1 | open Types 2 | open Values 3 | 4 | type global = {mutable content : value; mut : mutability} 5 | type t = global 6 | 7 | exception Type 8 | exception NotMutable 9 | 10 | let alloc (GlobalType (t, mut)) v = 11 | if type_of v <> t then raise Type; 12 | {content = v; mut = mut} 13 | 14 | let type_of glob = 15 | GlobalType (type_of glob.content, glob.mut) 16 | 17 | let load glob = glob.content 18 | let store glob v = 19 | if glob.mut <> Mutable then raise NotMutable; 20 | if Values.type_of v <> Values.type_of glob.content then raise Type; 21 | glob.content <- v 22 | -------------------------------------------------------------------------------- /interpreter/runtime/global.mli: -------------------------------------------------------------------------------- 1 | open Types 2 | open Values 3 | 4 | type global 5 | type t = global 6 | 7 | exception Type 8 | exception NotMutable 9 | 10 | val alloc : global_type -> value -> global (* raises Type *) 11 | val type_of : global -> global_type 12 | 13 | val load : global -> value 14 | val store : global -> value -> unit (* raises Type, NotMutable *) 15 | -------------------------------------------------------------------------------- /interpreter/runtime/instance.ml: -------------------------------------------------------------------------------- 1 | open Types 2 | 3 | type module_inst = 4 | { 5 | types : func_type list; 6 | funcs : func_inst list; 7 | tables : table_inst list; 8 | memories : memory_inst list; 9 | globals : global_inst list; 10 | exports : export_inst list; 11 | } 12 | 13 | and func_inst = module_inst ref Func.t 14 | and table_inst = Table.t 15 | and memory_inst = Memory.t 16 | and global_inst = Global.t 17 | and export_inst = Ast.name * extern 18 | 19 | and extern = 20 | | ExternFunc of func_inst 21 | | ExternTable of table_inst 22 | | ExternMemory of memory_inst 23 | | ExternGlobal of global_inst 24 | 25 | type Table.elem += FuncElem of func_inst 26 | 27 | 28 | (* Auxiliary functions *) 29 | 30 | let empty_module_inst = 31 | { types = []; funcs = []; tables = []; memories = []; globals = []; 32 | exports = [] } 33 | 34 | let extern_type_of = function 35 | | ExternFunc func -> ExternFuncType (Func.type_of func) 36 | | ExternTable tab -> ExternTableType (Table.type_of tab) 37 | | ExternMemory mem -> ExternMemoryType (Memory.type_of mem) 38 | | ExternGlobal glob -> ExternGlobalType (Global.type_of glob) 39 | 40 | let export inst name = 41 | try Some (List.assoc name inst.exports) with Not_found -> None 42 | -------------------------------------------------------------------------------- /interpreter/runtime/memory.ml: -------------------------------------------------------------------------------- 1 | open Bigarray 2 | open Lib.Bigarray 3 | open Types 4 | open Values 5 | 6 | type size = int32 (* number of pages *) 7 | type address = int64 8 | type offset = int32 9 | 10 | type pack_size = Pack8 | Pack16 | Pack32 11 | type extension = SX | ZX 12 | 13 | type memory' = (int, int8_unsigned_elt, c_layout) Array1.t 14 | type memory = {mutable content : memory'; max : size option} 15 | type t = memory 16 | 17 | exception Type 18 | exception Bounds 19 | exception SizeOverflow 20 | exception SizeLimit 21 | exception OutOfMemory 22 | 23 | let page_size = 0x10000L (* 64 KiB *) 24 | 25 | let packed_size = function 26 | | Pack8 -> 1 27 | | Pack16 -> 2 28 | | Pack32 -> 4 29 | 30 | let within_limits n = function 31 | | None -> true 32 | | Some max -> I32.le_u n max 33 | 34 | let create n = 35 | if I32.gt_u n 0x10000l then raise SizeOverflow else 36 | try 37 | let size = Int64.(mul (of_int32 n) page_size) in 38 | let mem = Array1_64.create Int8_unsigned C_layout size in 39 | Array1.fill mem 0; 40 | mem 41 | with Out_of_memory -> raise OutOfMemory 42 | 43 | let alloc (MemoryType {min; max}) = 44 | assert (within_limits min max); 45 | {content = create min; max} 46 | 47 | let bound mem = 48 | Array1_64.dim mem.content 49 | 50 | let size mem = 51 | Int64.(to_int32 (div (bound mem) page_size)) 52 | 53 | let type_of mem = 54 | MemoryType {min = size mem; max = mem.max} 55 | 56 | let grow mem delta = 57 | let old_size = size mem in 58 | let new_size = Int32.add old_size delta in 59 | if I32.gt_u old_size new_size then raise SizeOverflow else 60 | if not (within_limits new_size mem.max) then raise SizeLimit else 61 | let after = create new_size in 62 | let dim = Array1_64.dim mem.content in 63 | Array1.blit (Array1_64.sub mem.content 0L dim) (Array1_64.sub after 0L dim); 64 | mem.content <- after 65 | 66 | let load_byte mem a = 67 | try Array1_64.get mem.content a with Invalid_argument _ -> raise Bounds 68 | 69 | let store_byte mem a b = 70 | try Array1_64.set mem.content a b with Invalid_argument _ -> raise Bounds 71 | 72 | let load_bytes mem a n = 73 | let buf = Buffer.create n in 74 | for i = 0 to n - 1 do 75 | Buffer.add_char buf (Char.chr (load_byte mem Int64.(add a (of_int i)))) 76 | done; 77 | Buffer.contents buf 78 | 79 | let store_bytes mem a bs = 80 | for i = String.length bs - 1 downto 0 do 81 | store_byte mem Int64.(add a (of_int i)) (Char.code bs.[i]) 82 | done 83 | 84 | let effective_address a o = 85 | let ea = Int64.(add a (of_int32 o)) in 86 | if I64.lt_u ea a then raise Bounds; 87 | ea 88 | 89 | let loadn mem a o n = 90 | assert (n > 0 && n <= 8); 91 | let rec loop a n = 92 | if n = 0 then 0L else begin 93 | let x = Int64.(shift_left (loop (add a 1L) (n - 1)) 8) in 94 | Int64.logor (Int64.of_int (load_byte mem a)) x 95 | end 96 | in loop (effective_address a o) n 97 | 98 | let storen mem a o n x = 99 | assert (n > 0 && n <= 8); 100 | let rec loop a n x = 101 | if n > 0 then begin 102 | Int64.(loop (add a 1L) (n - 1) (shift_right x 8)); 103 | store_byte mem a (Int64.to_int x land 0xff) 104 | end 105 | in loop (effective_address a o) n x 106 | 107 | let load_value mem a o t = 108 | let n = loadn mem a o (Types.size t) in 109 | match t with 110 | | I32Type -> I32 (Int64.to_int32 n) 111 | | I64Type -> I64 n 112 | | F32Type -> F32 (F32.of_bits (Int64.to_int32 n)) 113 | | F64Type -> F64 (F64.of_bits n) 114 | 115 | let store_value mem a o v = 116 | let x = 117 | match v with 118 | | I32 x -> Int64.of_int32 x 119 | | I64 x -> x 120 | | F32 x -> Int64.of_int32 (F32.to_bits x) 121 | | F64 x -> F64.to_bits x 122 | in storen mem a o (Types.size (Values.type_of v)) x 123 | 124 | let extend x n = function 125 | | ZX -> x 126 | | SX -> let sh = 64 - 8 * n in Int64.(shift_right (shift_left x sh) sh) 127 | 128 | let load_packed sz ext mem a o t = 129 | assert (packed_size sz <= Types.size t); 130 | let n = packed_size sz in 131 | let x = extend (loadn mem a o n) n ext in 132 | match t with 133 | | I32Type -> I32 (Int64.to_int32 x) 134 | | I64Type -> I64 x 135 | | _ -> raise Type 136 | 137 | let store_packed sz mem a o v = 138 | assert (packed_size sz <= Types.size (Values.type_of v)); 139 | let n = packed_size sz in 140 | let x = 141 | match v with 142 | | I32 x -> Int64.of_int32 x 143 | | I64 x -> x 144 | | _ -> raise Type 145 | in storen mem a o n x 146 | -------------------------------------------------------------------------------- /interpreter/runtime/memory.mli: -------------------------------------------------------------------------------- 1 | open Types 2 | open Values 3 | 4 | type memory 5 | type t = memory 6 | 7 | type size = int32 (* number of pages *) 8 | type address = int64 9 | type offset = int32 10 | 11 | type pack_size = Pack8 | Pack16 | Pack32 12 | type extension = SX | ZX 13 | 14 | exception Type 15 | exception Bounds 16 | exception SizeOverflow 17 | exception SizeLimit 18 | exception OutOfMemory 19 | 20 | val page_size : int64 21 | val packed_size : pack_size -> int 22 | 23 | val alloc : memory_type -> memory (* raises SizeOverflow, OutOfMemory *) 24 | val type_of : memory -> memory_type 25 | val size : memory -> size 26 | val bound : memory -> address 27 | val grow : memory -> size -> unit 28 | (* raises SizeLimit, SizeOverflow, OutOfMemory *) 29 | 30 | val load_byte : memory -> address -> int (* raises Bounds *) 31 | val store_byte : memory -> address -> int -> unit (* raises Bounds *) 32 | val load_bytes : memory -> address -> int -> string (* raises Bounds *) 33 | val store_bytes : memory -> address -> string -> unit (* raises Bounds *) 34 | 35 | val load_value : 36 | memory -> address -> offset -> value_type -> value (* raises Bounds *) 37 | val store_value : 38 | memory -> address -> offset -> value -> unit (* raises Bounds *) 39 | val load_packed : 40 | pack_size -> extension -> memory -> address -> offset -> value_type -> value 41 | (* raises Type, Bounds *) 42 | val store_packed : 43 | pack_size -> memory -> address -> offset -> value -> unit 44 | (* raises Type, Bounds *) 45 | -------------------------------------------------------------------------------- /interpreter/runtime/table.ml: -------------------------------------------------------------------------------- 1 | open Types 2 | 3 | type size = int32 4 | type index = int32 5 | 6 | type elem = .. 7 | type elem += Uninitialized 8 | 9 | type table' = elem array 10 | type table = 11 | {mutable content : table'; max : size option; elem_type : elem_type} 12 | type t = table 13 | 14 | exception Bounds 15 | exception SizeOverflow 16 | exception SizeLimit 17 | 18 | let within_limits size = function 19 | | None -> true 20 | | Some max -> I32.le_u size max 21 | 22 | let create size = 23 | try Lib.Array32.make size Uninitialized 24 | with Invalid_argument _ -> raise Out_of_memory 25 | 26 | let alloc (TableType ({min; max}, elem_type)) = 27 | assert (within_limits min max); 28 | {content = create min; max; elem_type} 29 | 30 | let size tab = 31 | Lib.Array32.length tab.content 32 | 33 | let type_of tab = 34 | TableType ({min = size tab; max = tab.max}, tab.elem_type) 35 | 36 | let grow tab delta = 37 | let old_size = size tab in 38 | let new_size = Int32.add old_size delta in 39 | if I32.gt_u old_size new_size then raise SizeOverflow else 40 | if not (within_limits new_size tab.max) then raise SizeLimit else 41 | let after = create new_size in 42 | Array.blit tab.content 0 after 0 (Array.length tab.content); 43 | tab.content <- after 44 | 45 | let load tab i = 46 | try Lib.Array32.get tab.content i with Invalid_argument _ -> raise Bounds 47 | 48 | let store tab i v = 49 | try Lib.Array32.set tab.content i v with Invalid_argument _ -> raise Bounds 50 | 51 | let blit tab offset elems = 52 | let data = Array.of_list elems in 53 | try Lib.Array32.blit data 0l tab.content offset (Lib.Array32.length data) 54 | with Invalid_argument _ -> raise Bounds 55 | -------------------------------------------------------------------------------- /interpreter/runtime/table.mli: -------------------------------------------------------------------------------- 1 | open Types 2 | 3 | type table 4 | type t = table 5 | 6 | type size = int32 7 | type index = int32 8 | 9 | type elem = .. 10 | type elem += Uninitialized 11 | 12 | exception Bounds 13 | exception SizeOverflow 14 | exception SizeLimit 15 | 16 | val alloc : table_type -> table 17 | val type_of : table -> table_type 18 | val size : table -> size 19 | val grow : table -> size -> unit (* raises SizeOverflow, SizeLimit *) 20 | 21 | val load : table -> index -> elem (* raises Bounds *) 22 | val store : table -> index -> elem -> unit (* raises Bounds *) 23 | val blit : table -> index -> elem list -> unit (* raises Bounds *) 24 | -------------------------------------------------------------------------------- /interpreter/script/import.ml: -------------------------------------------------------------------------------- 1 | open Source 2 | open Ast 3 | 4 | module Unknown = Error.Make () 5 | exception Unknown = Unknown.Error (* indicates unknown import name *) 6 | 7 | module Registry = Map.Make(struct type t = Ast.name let compare = compare end) 8 | let registry = ref Registry.empty 9 | 10 | let register name lookup = registry := Registry.add name lookup !registry 11 | 12 | let lookup (m : module_) (im : import) : Instance.extern = 13 | let {module_name; item_name; idesc} = im.it in 14 | let t = import_type m im in 15 | try Registry.find module_name !registry item_name t with Not_found -> 16 | Unknown.error im.at 17 | ("unknown import \"" ^ string_of_name module_name ^ 18 | "\".\"" ^ string_of_name item_name ^ "\"") 19 | 20 | let link m = List.map (lookup m) m.it.imports 21 | -------------------------------------------------------------------------------- /interpreter/script/import.mli: -------------------------------------------------------------------------------- 1 | exception Unknown of Source.region * string 2 | 3 | val link : Ast.module_ -> Instance.extern list (* raises Unknown *) 4 | 5 | val register : 6 | Ast.name -> 7 | (Ast.name -> Types.extern_type -> Instance.extern (* raises Not_found *)) -> 8 | unit 9 | -------------------------------------------------------------------------------- /interpreter/script/js.mli: -------------------------------------------------------------------------------- 1 | val of_script : Script.script -> string 2 | -------------------------------------------------------------------------------- /interpreter/script/run.mli: -------------------------------------------------------------------------------- 1 | exception Abort of Source.region * string 2 | exception Assert of Source.region * string 3 | exception IO of Source.region * string 4 | 5 | val trace : string -> unit 6 | 7 | val run_string : string -> bool 8 | val run_file : string -> bool 9 | val run_stdin : unit -> unit 10 | -------------------------------------------------------------------------------- /interpreter/script/script.ml: -------------------------------------------------------------------------------- 1 | type var = string Source.phrase 2 | 3 | type definition = definition' Source.phrase 4 | and definition' = 5 | | Textual of Ast.module_ 6 | | Encoded of string * string 7 | | Quoted of string * string 8 | 9 | type action = action' Source.phrase 10 | and action' = 11 | | Invoke of var option * Ast.name * Ast.literal list 12 | | Get of var option * Ast.name 13 | 14 | type nanop = nanop' Source.phrase 15 | and nanop' = (unit, unit, nan, nan) Values.op 16 | and nan = CanonicalNan | ArithmeticNan 17 | 18 | type result = result' Source.phrase 19 | and result' = 20 | | LitResult of Ast.literal 21 | | NanResult of nanop 22 | 23 | type assertion = assertion' Source.phrase 24 | and assertion' = 25 | | AssertMalformed of definition * string 26 | | AssertInvalid of definition * string 27 | | AssertUnlinkable of definition * string 28 | | AssertUninstantiable of definition * string 29 | | AssertReturn of action * result list 30 | | AssertTrap of action * string 31 | | AssertExhaustion of action * string 32 | 33 | type command = command' Source.phrase 34 | and command' = 35 | | Module of var option * definition 36 | | Register of Ast.name * var option 37 | | Action of action 38 | | Assertion of assertion 39 | | Meta of meta 40 | 41 | and meta = meta' Source.phrase 42 | and meta' = 43 | | Input of var option * string 44 | | Output of var option * string option 45 | | Script of var option * script 46 | 47 | and script = command list 48 | 49 | exception Syntax of Source.region * string 50 | -------------------------------------------------------------------------------- /interpreter/syntax/types.ml: -------------------------------------------------------------------------------- 1 | (* Types *) 2 | 3 | type value_type = I32Type | I64Type | F32Type | F64Type 4 | type elem_type = FuncRefType 5 | type stack_type = value_type list 6 | type func_type = FuncType of stack_type * stack_type 7 | 8 | type 'a limits = {min : 'a; max : 'a option} 9 | type mutability = Immutable | Mutable 10 | type table_type = TableType of Int32.t limits * elem_type 11 | type memory_type = MemoryType of Int32.t limits 12 | type global_type = GlobalType of value_type * mutability 13 | type extern_type = 14 | | ExternFuncType of func_type 15 | | ExternTableType of table_type 16 | | ExternMemoryType of memory_type 17 | | ExternGlobalType of global_type 18 | 19 | 20 | (* Attributes *) 21 | 22 | let size = function 23 | | I32Type | F32Type -> 4 24 | | I64Type | F64Type -> 8 25 | 26 | 27 | (* Subtyping *) 28 | 29 | let match_limits lim1 lim2 = 30 | I32.ge_u lim1.min lim2.min && 31 | match lim1.max, lim2.max with 32 | | _, None -> true 33 | | None, Some _ -> false 34 | | Some i, Some j -> I32.le_u i j 35 | 36 | let match_func_type ft1 ft2 = 37 | ft1 = ft2 38 | 39 | let match_table_type (TableType (lim1, et1)) (TableType (lim2, et2)) = 40 | et1 = et2 && match_limits lim1 lim2 41 | 42 | let match_memory_type (MemoryType lim1) (MemoryType lim2) = 43 | match_limits lim1 lim2 44 | 45 | let match_global_type gt1 gt2 = 46 | gt1 = gt2 47 | 48 | let match_extern_type et1 et2 = 49 | match et1, et2 with 50 | | ExternFuncType ft1, ExternFuncType ft2 -> match_func_type ft1 ft2 51 | | ExternTableType tt1, ExternTableType tt2 -> match_table_type tt1 tt2 52 | | ExternMemoryType mt1, ExternMemoryType mt2 -> match_memory_type mt1 mt2 53 | | ExternGlobalType gt1, ExternGlobalType gt2 -> match_global_type gt1 gt2 54 | | _, _ -> false 55 | 56 | 57 | (* Filters *) 58 | 59 | let funcs = 60 | Lib.List.map_filter (function ExternFuncType t -> Some t | _ -> None) 61 | let tables = 62 | Lib.List.map_filter (function ExternTableType t -> Some t | _ -> None) 63 | let memories = 64 | Lib.List.map_filter (function ExternMemoryType t -> Some t | _ -> None) 65 | let globals = 66 | Lib.List.map_filter (function ExternGlobalType t -> Some t | _ -> None) 67 | 68 | 69 | (* String conversion *) 70 | 71 | let string_of_value_type = function 72 | | I32Type -> "i32" 73 | | I64Type -> "i64" 74 | | F32Type -> "f32" 75 | | F64Type -> "f64" 76 | 77 | let string_of_value_types = function 78 | | [t] -> string_of_value_type t 79 | | ts -> "[" ^ String.concat " " (List.map string_of_value_type ts) ^ "]" 80 | 81 | let string_of_elem_type = function 82 | | FuncRefType -> "funcref" 83 | 84 | let string_of_limits {min; max} = 85 | I32.to_string_u min ^ 86 | (match max with None -> "" | Some n -> " " ^ I32.to_string_u n) 87 | 88 | let string_of_memory_type = function 89 | | MemoryType lim -> string_of_limits lim 90 | 91 | let string_of_table_type = function 92 | | TableType (lim, t) -> string_of_limits lim ^ " " ^ string_of_elem_type t 93 | 94 | let string_of_global_type = function 95 | | GlobalType (t, Immutable) -> string_of_value_type t 96 | | GlobalType (t, Mutable) -> "(mut " ^ string_of_value_type t ^ ")" 97 | 98 | let string_of_stack_type ts = 99 | "[" ^ String.concat " " (List.map string_of_value_type ts) ^ "]" 100 | 101 | let string_of_func_type (FuncType (ins, out)) = 102 | string_of_stack_type ins ^ " -> " ^ string_of_stack_type out 103 | 104 | let string_of_extern_type = function 105 | | ExternFuncType ft -> "func " ^ string_of_func_type ft 106 | | ExternTableType tt -> "table " ^ string_of_table_type tt 107 | | ExternMemoryType mt -> "memory " ^ string_of_memory_type mt 108 | | ExternGlobalType gt -> "global " ^ string_of_global_type gt 109 | -------------------------------------------------------------------------------- /interpreter/syntax/values.ml: -------------------------------------------------------------------------------- 1 | open Types 2 | 3 | 4 | (* Values and operators *) 5 | 6 | type ('i32, 'i64, 'f32, 'f64) op = 7 | I32 of 'i32 | I64 of 'i64 | F32 of 'f32 | F64 of 'f64 8 | 9 | type value = (I32.t, I64.t, F32.t, F64.t) op 10 | 11 | 12 | (* Typing *) 13 | 14 | let type_of = function 15 | | I32 _ -> I32Type 16 | | I64 _ -> I64Type 17 | | F32 _ -> F32Type 18 | | F64 _ -> F64Type 19 | 20 | let default_value = function 21 | | I32Type -> I32 I32.zero 22 | | I64Type -> I64 I64.zero 23 | | F32Type -> F32 F32.zero 24 | | F64Type -> F64 F64.zero 25 | 26 | 27 | (* Conversion *) 28 | 29 | let value_of_bool b = I32 (if b then 1l else 0l) 30 | 31 | let string_of_value = function 32 | | I32 i -> I32.to_string_s i 33 | | I64 i -> I64.to_string_s i 34 | | F32 z -> F32.to_string z 35 | | F64 z -> F64.to_string z 36 | 37 | let string_of_values = function 38 | | [v] -> string_of_value v 39 | | vs -> "[" ^ String.concat " " (List.map string_of_value vs) ^ "]" 40 | 41 | 42 | (* Injection & projection *) 43 | 44 | exception Value of value_type 45 | 46 | module type ValueType = 47 | sig 48 | type t 49 | val to_value : t -> value 50 | val of_value : value -> t (* raise Value *) 51 | end 52 | 53 | module I32Value = 54 | struct 55 | type t = I32.t 56 | let to_value i = I32 i 57 | let of_value = function I32 i -> i | _ -> raise (Value I32Type) 58 | end 59 | 60 | module I64Value = 61 | struct 62 | type t = I64.t 63 | let to_value i = I64 i 64 | let of_value = function I64 i -> i | _ -> raise (Value I64Type) 65 | end 66 | 67 | module F32Value = 68 | struct 69 | type t = F32.t 70 | let to_value i = F32 i 71 | let of_value = function F32 z -> z | _ -> raise (Value F32Type) 72 | end 73 | 74 | module F64Value = 75 | struct 76 | type t = F64.t 77 | let to_value i = F64 i 78 | let of_value = function F64 z -> z | _ -> raise (Value F64Type) 79 | end 80 | -------------------------------------------------------------------------------- /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 | module Fun : 4 | sig 5 | val curry : ('a * 'b -> 'c) -> ('a -> 'b -> 'c) 6 | val uncurry : ('a -> 'b -> 'c) -> ('a * 'b -> 'c) 7 | 8 | val repeat : int -> ('a -> unit) -> 'a -> unit 9 | end 10 | 11 | module List : 12 | sig 13 | val make : int -> 'a -> 'a list 14 | val table : int -> (int -> 'a) -> 'a list 15 | val take : int -> 'a list -> 'a list (* raises Failure *) 16 | val drop : int -> 'a list -> 'a list (* raises Failure *) 17 | 18 | val last : 'a list -> 'a (* raises Failure *) 19 | val split_last : 'a list -> 'a list * 'a (* raises Failure *) 20 | 21 | val index_of : 'a -> 'a list -> int option 22 | val index_where : ('a -> bool) -> 'a list -> int option 23 | val map_filter : ('a -> 'b option) -> 'a list -> 'b list 24 | val concat_map : ('a -> 'b list) -> 'a list -> 'b list 25 | end 26 | 27 | module List32 : 28 | sig 29 | val make : int32 -> 'a -> 'a list 30 | val length : 'a list -> int32 31 | val nth : 'a list -> int32 -> 'a (* raises Failure *) 32 | val take : int32 -> 'a list -> 'a list (* raises Failure *) 33 | val drop : int32 -> 'a list -> 'a list (* raises Failure *) 34 | end 35 | 36 | module Array32 : 37 | sig 38 | val make : int32 -> 'a -> 'a array 39 | val length : 'a array -> int32 40 | val get : 'a array -> int32 -> 'a 41 | val set : 'a array -> int32 -> 'a -> unit 42 | val blit : 'a array -> int32 -> 'a array -> int32 -> int32 -> unit 43 | end 44 | 45 | module Bigarray : 46 | sig 47 | open Bigarray 48 | 49 | module Array1_64 : 50 | sig 51 | val create : ('a, 'b) kind -> 'c layout -> int64 -> ('a, 'b, 'c) Array1.t 52 | val dim : ('a, 'b, 'c) Array1.t -> int64 53 | val get : ('a, 'b, 'c) Array1.t -> int64 -> 'a 54 | val set : ('a, 'b, 'c) Array1.t -> int64 -> 'a -> unit 55 | val sub : ('a, 'b, 'c) Array1.t -> int64 -> int64 -> ('a, 'b, 'c) Array1.t 56 | end 57 | end 58 | 59 | module Option : 60 | sig 61 | val get : 'a option -> 'a -> 'a 62 | val map : ('a -> 'b) -> 'a option -> 'b option 63 | val app : ('a -> unit) -> 'a option -> unit 64 | end 65 | 66 | module Int : 67 | sig 68 | val log2 : int -> int 69 | val is_power_of_two : int -> bool 70 | end 71 | 72 | module String : 73 | sig 74 | val implode : char list -> string 75 | val explode : string -> char list 76 | val split : string -> char -> string list 77 | val breakup : string -> int -> string list 78 | val find_from_opt : (char -> bool) -> string -> int -> int option 79 | end 80 | -------------------------------------------------------------------------------- /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 | 7 | 8 | (* Positions and regions *) 9 | 10 | let no_pos = {file = ""; line = 0; column = 0} 11 | let no_region = {left = no_pos; right = no_pos} 12 | 13 | let string_of_pos pos = 14 | if pos.line = -1 then 15 | Printf.sprintf "0x%x" pos.column 16 | else 17 | string_of_int pos.line ^ "." ^ string_of_int (pos.column + 1) 18 | 19 | let string_of_region r = 20 | r.left.file ^ ":" ^ string_of_pos r.left ^ 21 | (if r.right = r.left then "" else "-" ^ string_of_pos r.right) 22 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /interpreter/valid/valid.mli: -------------------------------------------------------------------------------- 1 | exception Invalid of Source.region * string 2 | 3 | val check_module : Ast.module_ -> unit (* raises Invalid *) 4 | -------------------------------------------------------------------------------- /papers/LICENSE: -------------------------------------------------------------------------------- 1 | This work is licensed under the Creative Commons Attribution 4.0 International License. To view a copy of this license, visit http://creativecommons.org/licenses/by/4.0/ or send a letter to Creative Commons, PO Box 1866, Mountain View, CA 94042, USA. 2 | -------------------------------------------------------------------------------- /papers/README.md: -------------------------------------------------------------------------------- 1 | * [Bringing the Web up to Speed with WebAssembly](pldi2017.pdf) 2 | 3 | Andreas Haas, Andreas Rossberg, Derek Schuff, Ben Titzer, Dan Gohman, Luke Wagner, Alon Zakai, JF Bastien, Michael Holman 4 | 5 | ACM-SIGPLAN Conference on Programming Language Design and Implementation (PLDI 2017) 6 | 7 | *Describes the WebAssembly design, its formalisation, and initial implementations.* 8 | 9 | * [Weakening WebAssembly](oopsla2019.pdf) 10 | 11 | Conrad Watt, Andreas Rossberg, Jean Pichon-Pharabod 12 | 13 | ACM-SIGPLAN Conference on Object-Oriented Programming, Systems, Language and Architectures (OOSPLA 2019) 14 | 15 | *Describes and formalises the extension of WebAssembly with threads and a suitable memory model.* 16 | -------------------------------------------------------------------------------- /papers/oopsla2019.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WebAssembly/multi-value/61975c83a286b96517659eded062f4e11dbac4c7/papers/oopsla2019.pdf -------------------------------------------------------------------------------- /papers/pldi2017.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WebAssembly/multi-value/61975c83a286b96517659eded062f4e11dbac4c7/papers/pldi2017.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/master/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/master/Semantics.md) and specified by the [spec interpreter](https://github.com/WebAssembly/spec/blob/master/interpreter). 2 | 3 | Tests are written in the [S-Expression script format](https://github.com/WebAssembly/spec/blob/master/interpreter/README.md#s-expression-syntax) defined by the interpreter. 4 | 5 | The test suite can be run with the spec interpreter as follows: 6 | ``` 7 | ./run.py --wasm 8 | ``` 9 | where the path points to the spec interpreter executable (or a tool that understands similar options). If the binary is in the working directory, this option can be omitted. 10 | 11 | In addition, the option `--js ` can be given to point to a stand-alone JavaScript interpreter supporting the WebAssembly API. If provided, all tests are also executed in JavaScript. 12 | -------------------------------------------------------------------------------- /test/core/comments.wast: -------------------------------------------------------------------------------- 1 | ;; Test comment syntax 2 | 3 | ;;comment 4 | 5 | ;;;;;;;;;;; 6 | 7 | ;;comment 8 | 9 | ( ;;comment 10 | module;;comment 11 | );;comment 12 | 13 | ;;) 14 | ;;;) 15 | ;; ;) 16 | ;; (; 17 | 18 | (;;) 19 | 20 | (;comment;) 21 | 22 | (;;comment;) 23 | 24 | (;;;comment;) 25 | 26 | (;;;;;;;;;;;;;;) 27 | 28 | (;(((((((((( ;) 29 | 30 | (;)))))))))));) 31 | 32 | (;comment";) 33 | 34 | (;comment"";) 35 | 36 | (;comment""";) 37 | 38 | ;; ASCII 00-1F, 7F 39 | (; 40 | ;) 41 | 42 | (;Heiße Würstchen;) 43 | 44 | (;;) 45 | 46 | (;comment 47 | comment;) 48 | 49 | (;comment;) 50 | 51 | (;comment;)((;comment;) 52 | (;comment;)module(;comment;) 53 | (;comment;))(;comment;) 54 | 55 | (;comment(;nested;)comment;) 56 | 57 | (;comment 58 | (;nested 59 | ;)comment 60 | ;) 61 | 62 | (module 63 | (;comment(;nested(;further;)nested;)comment;) 64 | ) 65 | 66 | (;comment;;comment;) 67 | 68 | (;comment;;comment 69 | ;) 70 | 71 | (module 72 | (;comment;;comment(;nested;)comment;) 73 | ) -------------------------------------------------------------------------------- /test/core/custom.wast: -------------------------------------------------------------------------------- 1 | (module binary 2 | "\00asm" "\01\00\00\00" 3 | "\00\24\10" "a custom section" "this is the payload" 4 | "\00\20\10" "a custom section" "this is payload" 5 | "\00\11\10" "a custom section" "" 6 | "\00\10\00" "" "this is payload" 7 | "\00\01\00" "" "" 8 | "\00\24\10" "\00\00custom sectio\00" "this is the payload" 9 | "\00\24\10" "\ef\bb\bfa custom sect" "this is the payload" 10 | "\00\24\10" "a custom sect\e2\8c\a3" "this is the payload" 11 | "\00\1f\16" "module within a module" "\00asm" "\01\00\00\00" 12 | ) 13 | 14 | (module binary 15 | "\00asm" "\01\00\00\00" 16 | "\00\0e\06" "custom" "payload" 17 | "\00\0e\06" "custom" "payload" 18 | "\01\01\00" ;; type section 19 | "\00\0e\06" "custom" "payload" 20 | "\00\0e\06" "custom" "payload" 21 | "\02\01\00" ;; import section 22 | "\00\0e\06" "custom" "payload" 23 | "\00\0e\06" "custom" "payload" 24 | "\03\01\00" ;; function section 25 | "\00\0e\06" "custom" "payload" 26 | "\00\0e\06" "custom" "payload" 27 | "\04\01\00" ;; table section 28 | "\00\0e\06" "custom" "payload" 29 | "\00\0e\06" "custom" "payload" 30 | "\05\01\00" ;; memory section 31 | "\00\0e\06" "custom" "payload" 32 | "\00\0e\06" "custom" "payload" 33 | "\06\01\00" ;; global section 34 | "\00\0e\06" "custom" "payload" 35 | "\00\0e\06" "custom" "payload" 36 | "\07\01\00" ;; export section 37 | "\00\0e\06" "custom" "payload" 38 | "\00\0e\06" "custom" "payload" 39 | "\09\01\00" ;; element section 40 | "\00\0e\06" "custom" "payload" 41 | "\00\0e\06" "custom" "payload" 42 | "\0a\01\00" ;; code section 43 | "\00\0e\06" "custom" "payload" 44 | "\00\0e\06" "custom" "payload" 45 | "\0b\01\00" ;; data section 46 | "\00\0e\06" "custom" "payload" 47 | "\00\0e\06" "custom" "payload" 48 | ) 49 | 50 | (module binary 51 | "\00asm" "\01\00\00\00" 52 | "\01\07\01\60\02\7f\7f\01\7f" ;; type section 53 | "\00\1a\06" "custom" "this is the payload" ;; custom section 54 | "\03\02\01\00" ;; function section 55 | "\07\0a\01\06\61\64\64\54\77\6f\00\00" ;; export section 56 | "\0a\09\01\07\00\20\00\20\01\6a\0b" ;; code section 57 | "\00\1b\07" "custom2" "this is the payload" ;; custom section 58 | ) 59 | 60 | (assert_malformed 61 | (module binary 62 | "\00asm" "\01\00\00\00" 63 | "\00" 64 | ) 65 | "unexpected end" 66 | ) 67 | 68 | (assert_malformed 69 | (module binary 70 | "\00asm" "\01\00\00\00" 71 | "\00\00" 72 | ) 73 | "unexpected end" 74 | ) 75 | 76 | (assert_malformed 77 | (module binary 78 | "\00asm" "\01\00\00\00" 79 | "\00\00\00\05\01\00\07\00\00" 80 | ) 81 | "unexpected end" 82 | ) 83 | 84 | (assert_malformed 85 | (module binary 86 | "\00asm" "\01\00\00\00" 87 | "\00\26\10" "a custom section" "this is the payload" 88 | ) 89 | "unexpected end" 90 | ) 91 | 92 | (assert_malformed 93 | (module binary 94 | "\00asm" "\01\00\00\00" 95 | "\00\25\10" "a custom section" "this is the payload" 96 | "\00\24\10" "a custom section" "this is the payload" 97 | ) 98 | "malformed section id" 99 | ) 100 | 101 | (assert_malformed 102 | (module binary 103 | "\00asm" "\01\00\00\00" 104 | "\01\07\01\60\02\7f\7f\01\7f" ;; type section 105 | "\00\25\10" "a custom section" "this is the payload" ;; wrong length! 106 | "\03\02\01\00" ;; function section 107 | "\0a\09\01\07\00\20\00\20\01\6a\0b" ;; code section 108 | "\00\1b\07" "custom2" "this is the payload" ;; custom section 109 | ) 110 | "function and code section have inconsistent lengths" 111 | ) 112 | 113 | ;; Test concatenated modules. 114 | (assert_malformed 115 | (module binary 116 | "\00asm\01\00\00\00" 117 | "\00asm\01\00\00\00" 118 | ) 119 | "length out of bounds" 120 | ) 121 | -------------------------------------------------------------------------------- /test/core/fac.wast: -------------------------------------------------------------------------------- 1 | (module 2 | ;; Recursive factorial 3 | (func (export "fac-rec") (param i64) (result i64) 4 | (if (result i64) (i64.eq (local.get 0) (i64.const 0)) 5 | (then (i64.const 1)) 6 | (else 7 | (i64.mul (local.get 0) (call 0 (i64.sub (local.get 0) (i64.const 1)))) 8 | ) 9 | ) 10 | ) 11 | 12 | ;; Recursive factorial named 13 | (func $fac-rec-named (export "fac-rec-named") (param $n i64) (result i64) 14 | (if (result i64) (i64.eq (local.get $n) (i64.const 0)) 15 | (then (i64.const 1)) 16 | (else 17 | (i64.mul 18 | (local.get $n) 19 | (call $fac-rec-named (i64.sub (local.get $n) (i64.const 1))) 20 | ) 21 | ) 22 | ) 23 | ) 24 | 25 | ;; Iterative factorial 26 | (func (export "fac-iter") (param i64) (result i64) 27 | (local i64 i64) 28 | (local.set 1 (local.get 0)) 29 | (local.set 2 (i64.const 1)) 30 | (block 31 | (loop 32 | (if 33 | (i64.eq (local.get 1) (i64.const 0)) 34 | (then (br 2)) 35 | (else 36 | (local.set 2 (i64.mul (local.get 1) (local.get 2))) 37 | (local.set 1 (i64.sub (local.get 1) (i64.const 1))) 38 | ) 39 | ) 40 | (br 0) 41 | ) 42 | ) 43 | (local.get 2) 44 | ) 45 | 46 | ;; Iterative factorial named 47 | (func (export "fac-iter-named") (param $n i64) (result i64) 48 | (local $i i64) 49 | (local $res i64) 50 | (local.set $i (local.get $n)) 51 | (local.set $res (i64.const 1)) 52 | (block $done 53 | (loop $loop 54 | (if 55 | (i64.eq (local.get $i) (i64.const 0)) 56 | (then (br $done)) 57 | (else 58 | (local.set $res (i64.mul (local.get $i) (local.get $res))) 59 | (local.set $i (i64.sub (local.get $i) (i64.const 1))) 60 | ) 61 | ) 62 | (br $loop) 63 | ) 64 | ) 65 | (local.get $res) 66 | ) 67 | 68 | ;; Optimized factorial. 69 | (func (export "fac-opt") (param i64) (result i64) 70 | (local i64) 71 | (local.set 1 (i64.const 1)) 72 | (block 73 | (br_if 0 (i64.lt_s (local.get 0) (i64.const 2))) 74 | (loop 75 | (local.set 1 (i64.mul (local.get 1) (local.get 0))) 76 | (local.set 0 (i64.add (local.get 0) (i64.const -1))) 77 | (br_if 0 (i64.gt_s (local.get 0) (i64.const 1))) 78 | ) 79 | ) 80 | (local.get 1) 81 | ) 82 | 83 | ;; Iterative factorial without locals. 84 | (func $pick0 (param i64) (result i64 i64) 85 | (local.get 0) (local.get 0) 86 | ) 87 | (func $pick1 (param i64 i64) (result i64 i64 i64) 88 | (local.get 0) (local.get 1) (local.get 0) 89 | ) 90 | (func (export "fac-ssa") (param i64) (result i64) 91 | (i64.const 1) (local.get 0) 92 | (loop $l (param i64 i64) (result i64) 93 | (call $pick1) (call $pick1) (i64.mul) 94 | (call $pick1) (i64.const 1) (i64.sub) 95 | (call $pick0) (i64.const 0) (i64.gt_u) 96 | (br_if $l) 97 | (drop) (return) 98 | ) 99 | ) 100 | ) 101 | 102 | (assert_return (invoke "fac-rec" (i64.const 25)) (i64.const 7034535277573963776)) 103 | (assert_return (invoke "fac-iter" (i64.const 25)) (i64.const 7034535277573963776)) 104 | (assert_return (invoke "fac-rec-named" (i64.const 25)) (i64.const 7034535277573963776)) 105 | (assert_return (invoke "fac-iter-named" (i64.const 25)) (i64.const 7034535277573963776)) 106 | (assert_return (invoke "fac-opt" (i64.const 25)) (i64.const 7034535277573963776)) 107 | (assert_return (invoke "fac-ssa" (i64.const 25)) (i64.const 7034535277573963776)) 108 | 109 | (assert_exhaustion (invoke "fac-rec" (i64.const 1073741824)) "call stack exhausted") 110 | -------------------------------------------------------------------------------- /test/core/forward.wast: -------------------------------------------------------------------------------- 1 | (module 2 | (func $even (export "even") (param $n i32) (result i32) 3 | (if (result i32) (i32.eq (local.get $n) (i32.const 0)) 4 | (then (i32.const 1)) 5 | (else (call $odd (i32.sub (local.get $n) (i32.const 1)))) 6 | ) 7 | ) 8 | 9 | (func $odd (export "odd") (param $n i32) (result i32) 10 | (if (result i32) (i32.eq (local.get $n) (i32.const 0)) 11 | (then (i32.const 0)) 12 | (else (call $even (i32.sub (local.get $n) (i32.const 1)))) 13 | ) 14 | ) 15 | ) 16 | 17 | (assert_return (invoke "even" (i32.const 13)) (i32.const 0)) 18 | (assert_return (invoke "even" (i32.const 20)) (i32.const 1)) 19 | (assert_return (invoke "odd" (i32.const 13)) (i32.const 1)) 20 | (assert_return (invoke "odd" (i32.const 20)) (i32.const 0)) 21 | -------------------------------------------------------------------------------- /test/core/func_ptrs.wast: -------------------------------------------------------------------------------- 1 | (module 2 | (type (func)) ;; 0: void -> void 3 | (type $S (func)) ;; 1: void -> void 4 | (type (func (param))) ;; 2: void -> void 5 | (type (func (result i32))) ;; 3: void -> i32 6 | (type (func (param) (result i32))) ;; 4: void -> i32 7 | (type $T (func (param i32) (result i32))) ;; 5: i32 -> i32 8 | (type $U (func (param i32))) ;; 6: i32 -> void 9 | 10 | (func $print (import "spectest" "print_i32") (type 6)) 11 | 12 | (func (type 0)) 13 | (func (type $S)) 14 | 15 | (func (export "one") (type 4) (i32.const 13)) 16 | (func (export "two") (type $T) (i32.add (local.get 0) (i32.const 1))) 17 | 18 | ;; Both signature and parameters are allowed (and required to match) 19 | ;; since this allows the naming of parameters. 20 | (func (export "three") (type $T) (param $a i32) (result i32) 21 | (i32.sub (local.get 0) (i32.const 2)) 22 | ) 23 | 24 | (func (export "four") (type $U) (call $print (local.get 0))) 25 | ) 26 | 27 | (assert_return (invoke "one") (i32.const 13)) 28 | (assert_return (invoke "two" (i32.const 13)) (i32.const 14)) 29 | (assert_return (invoke "three" (i32.const 13)) (i32.const 11)) 30 | (invoke "four" (i32.const 83)) 31 | 32 | (assert_invalid (module (elem (i32.const 0))) "unknown table") 33 | (assert_invalid (module (elem (i32.const 0) 0) (func)) "unknown table") 34 | 35 | (assert_invalid 36 | (module (table 1 funcref) (elem (i64.const 0))) 37 | "type mismatch" 38 | ) 39 | (assert_invalid 40 | (module (table 1 funcref) (elem (i32.ctz (i32.const 0)))) 41 | "constant expression required" 42 | ) 43 | (assert_invalid 44 | (module (table 1 funcref) (elem (nop))) 45 | "constant expression required" 46 | ) 47 | 48 | (assert_invalid (module (func (type 42))) "unknown type") 49 | (assert_invalid (module (import "spectest" "print_i32" (func (type 43)))) "unknown type") 50 | 51 | (module 52 | (type $T (func (param) (result i32))) 53 | (type $U (func (param) (result i32))) 54 | (table funcref (elem $t1 $t2 $t3 $u1 $u2 $t1 $t3)) 55 | 56 | (func $t1 (type $T) (i32.const 1)) 57 | (func $t2 (type $T) (i32.const 2)) 58 | (func $t3 (type $T) (i32.const 3)) 59 | (func $u1 (type $U) (i32.const 4)) 60 | (func $u2 (type $U) (i32.const 5)) 61 | 62 | (func (export "callt") (param $i i32) (result i32) 63 | (call_indirect (type $T) (local.get $i)) 64 | ) 65 | 66 | (func (export "callu") (param $i i32) (result i32) 67 | (call_indirect (type $U) (local.get $i)) 68 | ) 69 | ) 70 | 71 | (assert_return (invoke "callt" (i32.const 0)) (i32.const 1)) 72 | (assert_return (invoke "callt" (i32.const 1)) (i32.const 2)) 73 | (assert_return (invoke "callt" (i32.const 2)) (i32.const 3)) 74 | (assert_return (invoke "callt" (i32.const 3)) (i32.const 4)) 75 | (assert_return (invoke "callt" (i32.const 4)) (i32.const 5)) 76 | (assert_return (invoke "callt" (i32.const 5)) (i32.const 1)) 77 | (assert_return (invoke "callt" (i32.const 6)) (i32.const 3)) 78 | (assert_trap (invoke "callt" (i32.const 7)) "undefined element") 79 | (assert_trap (invoke "callt" (i32.const 100)) "undefined element") 80 | (assert_trap (invoke "callt" (i32.const -1)) "undefined element") 81 | 82 | (assert_return (invoke "callu" (i32.const 0)) (i32.const 1)) 83 | (assert_return (invoke "callu" (i32.const 1)) (i32.const 2)) 84 | (assert_return (invoke "callu" (i32.const 2)) (i32.const 3)) 85 | (assert_return (invoke "callu" (i32.const 3)) (i32.const 4)) 86 | (assert_return (invoke "callu" (i32.const 4)) (i32.const 5)) 87 | (assert_return (invoke "callu" (i32.const 5)) (i32.const 1)) 88 | (assert_return (invoke "callu" (i32.const 6)) (i32.const 3)) 89 | (assert_trap (invoke "callu" (i32.const 7)) "undefined element") 90 | (assert_trap (invoke "callu" (i32.const 100)) "undefined element") 91 | (assert_trap (invoke "callu" (i32.const -1)) "undefined element") 92 | 93 | (module 94 | (type $T (func (result i32))) 95 | (table funcref (elem 0 1)) 96 | 97 | (func $t1 (type $T) (i32.const 1)) 98 | (func $t2 (type $T) (i32.const 2)) 99 | 100 | (func (export "callt") (param $i i32) (result i32) 101 | (call_indirect (type $T) (local.get $i)) 102 | ) 103 | ) 104 | 105 | (assert_return (invoke "callt" (i32.const 0)) (i32.const 1)) 106 | (assert_return (invoke "callt" (i32.const 1)) (i32.const 2)) 107 | -------------------------------------------------------------------------------- /test/core/inline-module.wast: -------------------------------------------------------------------------------- 1 | (func) (memory 0) (func (export "f")) 2 | -------------------------------------------------------------------------------- /test/core/memory_redundancy.wast: -------------------------------------------------------------------------------- 1 | ;; Test that optimizers don't do redundant-load, store-to-load, or dead-store 2 | ;; optimizations when there are interfering stores, even of different types 3 | ;; and to non-identical addresses. 4 | 5 | (module 6 | (memory 1 1) 7 | 8 | (func (export "zero_everything") 9 | (i32.store (i32.const 0) (i32.const 0)) 10 | (i32.store (i32.const 4) (i32.const 0)) 11 | (i32.store (i32.const 8) (i32.const 0)) 12 | (i32.store (i32.const 12) (i32.const 0)) 13 | ) 14 | 15 | (func (export "test_store_to_load") (result i32) 16 | (i32.store (i32.const 8) (i32.const 0)) 17 | (f32.store (i32.const 5) (f32.const -0.0)) 18 | (i32.load (i32.const 8)) 19 | ) 20 | 21 | (func (export "test_redundant_load") (result i32) 22 | (local $t i32) 23 | (local $s i32) 24 | (local.set $t (i32.load (i32.const 8))) 25 | (i32.store (i32.const 5) (i32.const 0x80000000)) 26 | (local.set $s (i32.load (i32.const 8))) 27 | (i32.add (local.get $t) (local.get $s)) 28 | ) 29 | 30 | (func (export "test_dead_store") (result f32) 31 | (local $t f32) 32 | (i32.store (i32.const 8) (i32.const 0x23232323)) 33 | (local.set $t (f32.load (i32.const 11))) 34 | (i32.store (i32.const 8) (i32.const 0)) 35 | (local.get $t) 36 | ) 37 | 38 | ;; A function named "malloc" which implementations nonetheless shouldn't 39 | ;; assume behaves like C malloc. 40 | (func $malloc (export "malloc") 41 | (param $size i32) 42 | (result i32) 43 | (i32.const 16) 44 | ) 45 | 46 | ;; Call malloc twice, but unlike C malloc, we don't get non-aliasing pointers. 47 | (func (export "malloc_aliasing") 48 | (result i32) 49 | (local $x i32) 50 | (local $y i32) 51 | (local.set $x (call $malloc (i32.const 4))) 52 | (local.set $y (call $malloc (i32.const 4))) 53 | (i32.store (local.get $x) (i32.const 42)) 54 | (i32.store (local.get $y) (i32.const 43)) 55 | (i32.load (local.get $x)) 56 | ) 57 | ) 58 | 59 | (assert_return (invoke "test_store_to_load") (i32.const 0x00000080)) 60 | (invoke "zero_everything") 61 | (assert_return (invoke "test_redundant_load") (i32.const 0x00000080)) 62 | (invoke "zero_everything") 63 | (assert_return (invoke "test_dead_store") (f32.const 0x1.18p-144)) 64 | (invoke "zero_everything") 65 | (assert_return (invoke "malloc_aliasing") (i32.const 43)) 66 | -------------------------------------------------------------------------------- /test/core/memory_size.wast: -------------------------------------------------------------------------------- 1 | (module 2 | (memory 0) 3 | (func (export "size") (result i32) (memory.size)) 4 | (func (export "grow") (param $sz i32) (drop (memory.grow (local.get $sz)))) 5 | ) 6 | 7 | (assert_return (invoke "size") (i32.const 0)) 8 | (assert_return (invoke "grow" (i32.const 1))) 9 | (assert_return (invoke "size") (i32.const 1)) 10 | (assert_return (invoke "grow" (i32.const 4))) 11 | (assert_return (invoke "size") (i32.const 5)) 12 | (assert_return (invoke "grow" (i32.const 0))) 13 | (assert_return (invoke "size") (i32.const 5)) 14 | 15 | (module 16 | (memory 1) 17 | (func (export "size") (result i32) (memory.size)) 18 | (func (export "grow") (param $sz i32) (drop (memory.grow (local.get $sz)))) 19 | ) 20 | 21 | (assert_return (invoke "size") (i32.const 1)) 22 | (assert_return (invoke "grow" (i32.const 1))) 23 | (assert_return (invoke "size") (i32.const 2)) 24 | (assert_return (invoke "grow" (i32.const 4))) 25 | (assert_return (invoke "size") (i32.const 6)) 26 | (assert_return (invoke "grow" (i32.const 0))) 27 | (assert_return (invoke "size") (i32.const 6)) 28 | 29 | (module 30 | (memory 0 2) 31 | (func (export "size") (result i32) (memory.size)) 32 | (func (export "grow") (param $sz i32) (drop (memory.grow (local.get $sz)))) 33 | ) 34 | 35 | (assert_return (invoke "size") (i32.const 0)) 36 | (assert_return (invoke "grow" (i32.const 3))) 37 | (assert_return (invoke "size") (i32.const 0)) 38 | (assert_return (invoke "grow" (i32.const 1))) 39 | (assert_return (invoke "size") (i32.const 1)) 40 | (assert_return (invoke "grow" (i32.const 0))) 41 | (assert_return (invoke "size") (i32.const 1)) 42 | (assert_return (invoke "grow" (i32.const 4))) 43 | (assert_return (invoke "size") (i32.const 1)) 44 | (assert_return (invoke "grow" (i32.const 1))) 45 | (assert_return (invoke "size") (i32.const 2)) 46 | 47 | (module 48 | (memory 3 8) 49 | (func (export "size") (result i32) (memory.size)) 50 | (func (export "grow") (param $sz i32) (drop (memory.grow (local.get $sz)))) 51 | ) 52 | 53 | (assert_return (invoke "size") (i32.const 3)) 54 | (assert_return (invoke "grow" (i32.const 1))) 55 | (assert_return (invoke "size") (i32.const 4)) 56 | (assert_return (invoke "grow" (i32.const 3))) 57 | (assert_return (invoke "size") (i32.const 7)) 58 | (assert_return (invoke "grow" (i32.const 0))) 59 | (assert_return (invoke "size") (i32.const 7)) 60 | (assert_return (invoke "grow" (i32.const 2))) 61 | (assert_return (invoke "size") (i32.const 7)) 62 | (assert_return (invoke "grow" (i32.const 1))) 63 | (assert_return (invoke "size") (i32.const 8)) 64 | 65 | 66 | ;; Type errors 67 | 68 | (assert_invalid 69 | (module 70 | (memory 1) 71 | (func $type-result-i32-vs-empty 72 | (memory.size) 73 | ) 74 | ) 75 | "type mismatch" 76 | ) 77 | (assert_invalid 78 | (module 79 | (memory 1) 80 | (func $type-result-i32-vs-f32 (result f32) 81 | (memory.size) 82 | ) 83 | ) 84 | "type mismatch" 85 | ) 86 | -------------------------------------------------------------------------------- /test/core/run.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from __future__ import print_function 4 | import argparse 5 | import os 6 | import os.path 7 | import unittest 8 | import subprocess 9 | import glob 10 | import sys 11 | 12 | 13 | ownDir = os.path.dirname(os.path.abspath(sys.argv[0])) 14 | inputDir = ownDir 15 | outputDir = os.path.join(inputDir, "_output") 16 | 17 | parser = argparse.ArgumentParser() 18 | parser.add_argument("--wasm", metavar="", default=os.path.join(os.getcwd(), "wasm")) 19 | parser.add_argument("--js", metavar="") 20 | parser.add_argument("--out", metavar="", default=outputDir) 21 | parser.add_argument("file", nargs='*') 22 | arguments = parser.parse_args() 23 | sys.argv = sys.argv[:1] 24 | 25 | wasmCommand = arguments.wasm 26 | jsCommand = arguments.js 27 | outputDir = arguments.out 28 | inputFiles = arguments.file if arguments.file else glob.glob(os.path.join(inputDir, "*.wast")) 29 | 30 | if not os.path.exists(wasmCommand): 31 | sys.stderr.write("""\ 32 | Error: The executable '%s' does not exist. 33 | Provide the correct path with the '--wasm' flag. 34 | 35 | """ % (wasmCommand)) 36 | parser.print_help() 37 | sys.exit(1) 38 | 39 | 40 | class RunTests(unittest.TestCase): 41 | def _runCommand(self, command, logPath, expectedExitCode = 0): 42 | with open(logPath, 'w+') as out: 43 | exitCode = subprocess.call(command, shell=True, stdout=out, stderr=subprocess.STDOUT) 44 | self.assertEqual(expectedExitCode, exitCode, "failed with exit code %i (expected %i) for %s" % (exitCode, expectedExitCode, command)) 45 | 46 | def _auxFile(self, path): 47 | if os.path.exists(path): 48 | os.remove(path) 49 | return path 50 | 51 | def _compareFile(self, expectFile, actualFile): 52 | if os.path.exists(expectFile): 53 | with open(expectFile) as expect: 54 | with open(actualFile) as actual: 55 | expectText = expect.read() 56 | actualText = actual.read() 57 | self.assertEqual(expectText, actualText) 58 | 59 | def _runTestFile(self, inputPath): 60 | dir, inputFile = os.path.split(inputPath) 61 | outputPath = os.path.join(outputDir, inputFile) 62 | 63 | # Run original file 64 | expectedExitCode = 1 if ".fail." in inputFile else 0 65 | logPath = self._auxFile(outputPath + ".log") 66 | self._runCommand(('%s "%s"') % (wasmCommand, inputPath), logPath, expectedExitCode) 67 | 68 | if expectedExitCode != 0: 69 | return 70 | 71 | # Convert to binary and run again 72 | wasmPath = self._auxFile(outputPath + ".bin.wast") 73 | logPath = self._auxFile(wasmPath + ".log") 74 | self._runCommand(('%s -d "%s" -o "%s"') % (wasmCommand, inputPath, wasmPath), logPath) 75 | self._runCommand(('%s "%s"') % (wasmCommand, wasmPath), logPath) 76 | 77 | # Convert back to text and run again 78 | wastPath = self._auxFile(wasmPath + ".wast") 79 | logPath = self._auxFile(wastPath + ".log") 80 | self._runCommand(('%s -d "%s" -o "%s"') % (wasmCommand, wasmPath, wastPath), logPath) 81 | self._runCommand(('%s "%s"') % (wasmCommand, wastPath), logPath) 82 | 83 | # Convert back to binary once more and compare 84 | wasm2Path = self._auxFile(wastPath + ".bin.wast") 85 | logPath = self._auxFile(wasm2Path + ".log") 86 | self._runCommand(('%s -d "%s" -o "%s"') % (wasmCommand, wastPath, wasm2Path), logPath) 87 | self._compareFile(wasmPath, wasm2Path) 88 | 89 | # Convert back to text once more and compare 90 | wast2Path = self._auxFile(wasm2Path + ".wast") 91 | logPath = self._auxFile(wast2Path + ".log") 92 | self._runCommand(('%s -d "%s" -o "%s"') % (wasmCommand, wasm2Path, wast2Path), logPath) 93 | self._compareFile(wastPath, wast2Path) 94 | 95 | # Convert to JavaScript 96 | jsPath = self._auxFile(outputPath.replace(".wast", ".js")) 97 | logPath = self._auxFile(jsPath + ".log") 98 | self._runCommand(('%s -d "%s" -o "%s"') % (wasmCommand, inputPath, jsPath), logPath) 99 | if jsCommand != None: 100 | self._runCommand(('%s "%s"') % (jsCommand, jsPath), logPath) 101 | 102 | 103 | if __name__ == "__main__": 104 | if not os.path.exists(outputDir): 105 | os.makedirs(outputDir) 106 | for fileName in inputFiles: 107 | testName = 'test ' + os.path.basename(fileName) 108 | setattr(RunTests, testName, lambda self, file=fileName: self._runTestFile(file)) 109 | unittest.main() 110 | -------------------------------------------------------------------------------- /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/switch.wast: -------------------------------------------------------------------------------- 1 | (module 2 | ;; Statement switch 3 | (func (export "stmt") (param $i i32) (result i32) 4 | (local $j i32) 5 | (local.set $j (i32.const 100)) 6 | (block $switch 7 | (block $7 8 | (block $default 9 | (block $6 10 | (block $5 11 | (block $4 12 | (block $3 13 | (block $2 14 | (block $1 15 | (block $0 16 | (br_table $0 $1 $2 $3 $4 $5 $6 $7 $default 17 | (local.get $i) 18 | ) 19 | ) ;; 0 20 | (return (local.get $i)) 21 | ) ;; 1 22 | (nop) 23 | ;; fallthrough 24 | ) ;; 2 25 | ;; fallthrough 26 | ) ;; 3 27 | (local.set $j (i32.sub (i32.const 0) (local.get $i))) 28 | (br $switch) 29 | ) ;; 4 30 | (br $switch) 31 | ) ;; 5 32 | (local.set $j (i32.const 101)) 33 | (br $switch) 34 | ) ;; 6 35 | (local.set $j (i32.const 101)) 36 | ;; fallthrough 37 | ) ;; default 38 | (local.set $j (i32.const 102)) 39 | ) ;; 7 40 | ;; fallthrough 41 | ) 42 | (return (local.get $j)) 43 | ) 44 | 45 | ;; Expression switch 46 | (func (export "expr") (param $i i64) (result i64) 47 | (local $j i64) 48 | (local.set $j (i64.const 100)) 49 | (return 50 | (block $switch (result i64) 51 | (block $7 52 | (block $default 53 | (block $4 54 | (block $5 55 | (block $6 56 | (block $3 57 | (block $2 58 | (block $1 59 | (block $0 60 | (br_table $0 $1 $2 $3 $4 $5 $6 $7 $default 61 | (i32.wrap_i64 (local.get $i)) 62 | ) 63 | ) ;; 0 64 | (return (local.get $i)) 65 | ) ;; 1 66 | (nop) 67 | ;; fallthrough 68 | ) ;; 2 69 | ;; fallthrough 70 | ) ;; 3 71 | (br $switch (i64.sub (i64.const 0) (local.get $i))) 72 | ) ;; 6 73 | (local.set $j (i64.const 101)) 74 | ;; fallthrough 75 | ) ;; 4 76 | ;; fallthrough 77 | ) ;; 5 78 | ;; fallthrough 79 | ) ;; default 80 | (br $switch (local.get $j)) 81 | ) ;; 7 82 | (i64.const -5) 83 | ) 84 | ) 85 | ) 86 | 87 | ;; Argument switch 88 | (func (export "arg") (param $i i32) (result i32) 89 | (return 90 | (block $2 (result i32) 91 | (i32.add (i32.const 10) 92 | (block $1 (result i32) 93 | (i32.add (i32.const 100) 94 | (block $0 (result i32) 95 | (i32.add (i32.const 1000) 96 | (block $default (result i32) 97 | (br_table $0 $1 $2 $default 98 | (i32.mul (i32.const 2) (local.get $i)) 99 | (i32.and (i32.const 3) (local.get $i)) 100 | ) 101 | ) 102 | ) 103 | ) 104 | ) 105 | ) 106 | ) 107 | ) 108 | ) 109 | ) 110 | 111 | ;; Corner cases 112 | (func (export "corner") (result i32) 113 | (block 114 | (br_table 0 (i32.const 0)) 115 | ) 116 | (i32.const 1) 117 | ) 118 | ) 119 | 120 | (assert_return (invoke "stmt" (i32.const 0)) (i32.const 0)) 121 | (assert_return (invoke "stmt" (i32.const 1)) (i32.const -1)) 122 | (assert_return (invoke "stmt" (i32.const 2)) (i32.const -2)) 123 | (assert_return (invoke "stmt" (i32.const 3)) (i32.const -3)) 124 | (assert_return (invoke "stmt" (i32.const 4)) (i32.const 100)) 125 | (assert_return (invoke "stmt" (i32.const 5)) (i32.const 101)) 126 | (assert_return (invoke "stmt" (i32.const 6)) (i32.const 102)) 127 | (assert_return (invoke "stmt" (i32.const 7)) (i32.const 100)) 128 | (assert_return (invoke "stmt" (i32.const -10)) (i32.const 102)) 129 | 130 | (assert_return (invoke "expr" (i64.const 0)) (i64.const 0)) 131 | (assert_return (invoke "expr" (i64.const 1)) (i64.const -1)) 132 | (assert_return (invoke "expr" (i64.const 2)) (i64.const -2)) 133 | (assert_return (invoke "expr" (i64.const 3)) (i64.const -3)) 134 | (assert_return (invoke "expr" (i64.const 6)) (i64.const 101)) 135 | (assert_return (invoke "expr" (i64.const 7)) (i64.const -5)) 136 | (assert_return (invoke "expr" (i64.const -10)) (i64.const 100)) 137 | 138 | (assert_return (invoke "arg" (i32.const 0)) (i32.const 110)) 139 | (assert_return (invoke "arg" (i32.const 1)) (i32.const 12)) 140 | (assert_return (invoke "arg" (i32.const 2)) (i32.const 4)) 141 | (assert_return (invoke "arg" (i32.const 3)) (i32.const 1116)) 142 | (assert_return (invoke "arg" (i32.const 4)) (i32.const 118)) 143 | (assert_return (invoke "arg" (i32.const 5)) (i32.const 20)) 144 | (assert_return (invoke "arg" (i32.const 6)) (i32.const 12)) 145 | (assert_return (invoke "arg" (i32.const 7)) (i32.const 1124)) 146 | (assert_return (invoke "arg" (i32.const 8)) (i32.const 126)) 147 | 148 | (assert_return (invoke "corner") (i32.const 1)) 149 | 150 | (assert_invalid (module (func (br_table 3 (i32.const 0)))) "unknown label") 151 | -------------------------------------------------------------------------------- /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 | (assert_invalid (module (table 0 funcref) (table 0 funcref)) "multiple tables") 12 | (assert_invalid (module (table (import "spectest" "table") 0 funcref) (table 0 funcref)) "multiple tables") 13 | 14 | (assert_invalid (module (elem (i32.const 0))) "unknown table") 15 | (assert_invalid (module (elem (i32.const 0) $f) (func $f)) "unknown table") 16 | 17 | 18 | (assert_invalid 19 | (module (table 1 0 funcref)) 20 | "size minimum must not be greater than maximum" 21 | ) 22 | (assert_invalid 23 | (module (table 0xffff_ffff 0 funcref)) 24 | "size minimum must not be greater than maximum" 25 | ) 26 | 27 | (assert_malformed 28 | (module quote "(table 0x1_0000_0000 funcref)") 29 | "i32 constant out of range" 30 | ) 31 | (assert_malformed 32 | (module quote "(table 0x1_0000_0000 0x1_0000_0000 funcref)") 33 | "i32 constant out of range" 34 | ) 35 | (assert_malformed 36 | (module quote "(table 0 0x1_0000_0000 funcref)") 37 | "i32 constant out of range" 38 | ) 39 | 40 | 41 | ;; Duplicate table identifiers 42 | 43 | (assert_malformed (module quote 44 | "(table $foo 1 funcref)" 45 | "(table $foo 1 funcref)") 46 | "duplicate table") 47 | (assert_malformed (module quote 48 | "(import \"\" \"\" (table $foo 1 funcref))" 49 | "(table $foo 1 funcref)") 50 | "duplicate table") 51 | (assert_malformed (module quote 52 | "(import \"\" \"\" (table $foo 1 funcref))" 53 | "(import \"\" \"\" (table $foo 1 funcref))") 54 | "duplicate table") 55 | -------------------------------------------------------------------------------- /test/core/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 | "result before parameter" 46 | ) 47 | (assert_malformed 48 | (module quote "(type (func (result $x i32)))") 49 | "unexpected token" 50 | ) 51 | -------------------------------------------------------------------------------- /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/LICENSE.md: -------------------------------------------------------------------------------- 1 | # Dual-License for W3C Test Suites 2 | 3 | All documents in this Repository are licensed by contributors to be distributed under both the [W3C Test Suite License](#w3c-test-suite-license) and the [W3C 3-clause BSD License](#w3c-3-clause-bsd-license), reproduced below. The choice of license is up to the licensee. For more information, see [Licenses for W3C Test Suites](https://www.w3.org/Consortium/Legal/2008/04-testsuite-copyright.html) 4 | 5 | # W3C Test Suite License 6 | 7 | This document, Test Suites and other documents that link to this statement are provided by the copyright holders under the following license: By using and/or copying this document, or the W3C document from which this statement is linked, you (the licensee) agree that you have read, understood, and will comply with the following terms and conditions: 8 | 9 | Permission to copy, and distribute the contents of this document, or the W3C document from which this statement is linked, in any medium for any purpose and without fee or royalty is hereby granted, provided that you include the following on ALL copies of the document, or portions thereof, that you use: 10 | 11 | * A link or URL to the original W3C document. 12 | * The pre-existing copyright notice of the original author, or if it doesn't exist, a notice (hypertext is preferred, but a textual representation is permitted) of the form: "Copyright © [$date-of-document] World Wide Web Consortium, (MIT, ERCIM, Keio, Beihang) and others. All Rights Reserved. http://www.w3.org/Consortium/Legal/2008/04-testsuite-copyright.html" 13 | * If it exists, the STATUS of the W3C document. 14 | 15 | When space permits, inclusion of the full text of this NOTICE should be provided. We request that authorship attribution be provided in any software, documents, or other items or products that you create pursuant to the implementation of the contents of this document, or any portion thereof. 16 | 17 | No right to create modifications or derivatives of W3C documents is granted pursuant to this license. However, if additional requirements (documented in the Copyright FAQ) are satisfied, the right to create modifications or derivatives is sometimes granted by the W3C to individuals complying with those requirements. 18 | 19 | If a Test Suite distinguishes the test harness (or, framework for navigation) and the actual tests, permission is given to remove or alter the harness or navigation if the Test Suite in question allows to do so. The tests themselves shall NOT be changed in any way. 20 | 21 | The name and trademarks of W3C and other copyright holders may NOT be used in advertising or publicity pertaining to this document or other documents that link to this statement without specific, written prior permission. Title to copyright in this document will at all times remain with copyright holders. Permission is given to use the trademarked string "W3C" within claims of performance concerning W3C Specifications or features described therein, and there only, if the test suite so authorizes. 22 | 23 | THIS WORK IS PROVIDED BY W3C, MIT, ERCIM, KEIO, BEIHANG, THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL W3C, MIT, ERCIM, KEIO, BEIHANG, THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | # W3C 3-clause BSD License 26 | 27 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 28 | 29 | * Redistributions of works must retain the original copyright notice, this list of conditions and the following disclaimer. 30 | * Redistributions in binary form must reproduce the original copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 31 | * Neither the name of the W3C nor the names of its contributors may be used to endorse or promote products derived from this work without specific prior written permission. 32 | 33 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | -------------------------------------------------------------------------------- /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/assertions.js: -------------------------------------------------------------------------------- 1 | function assert_function_name(fn, name, description) { 2 | const propdesc = Object.getOwnPropertyDescriptor(fn, "name"); 3 | assert_equals(typeof propdesc, "object", `${description} should have name property`); 4 | assert_false(propdesc.writable, "writable", `${description} name should not be writable`); 5 | assert_false(propdesc.enumerable, "enumerable", `${description} name should not be enumerable`); 6 | assert_true(propdesc.configurable, "configurable", `${description} name should be configurable`); 7 | assert_equals(propdesc.value, name, `${description} name should be ${name}`); 8 | } 9 | 10 | function assert_function_length(fn, length, description) { 11 | const propdesc = Object.getOwnPropertyDescriptor(fn, "length"); 12 | assert_equals(typeof propdesc, "object", `${description} should have length property`); 13 | assert_false(propdesc.writable, "writable", `${description} length should not be writable`); 14 | assert_false(propdesc.enumerable, "enumerable", `${description} length should not be enumerable`); 15 | assert_true(propdesc.configurable, "configurable", `${description} length should be configurable`); 16 | assert_equals(propdesc.value, length, `${description} length should be ${length}`); 17 | } 18 | 19 | function assert_exported_function(fn, { name, length }, description) { 20 | assert_equals(Object.getPrototypeOf(fn), Function.prototype, 21 | `${description}: prototype`); 22 | 23 | assert_function_name(fn, name, description); 24 | assert_function_length(fn, length, description); 25 | } 26 | 27 | function assert_Instance(instance, expected_exports) { 28 | assert_equals(Object.getPrototypeOf(instance), WebAssembly.Instance.prototype, 29 | "prototype"); 30 | assert_true(Object.isExtensible(instance), "extensible"); 31 | 32 | assert_equals(instance.exports, instance.exports, "exports should be idempotent"); 33 | const exports = instance.exports; 34 | 35 | assert_equals(Object.getPrototypeOf(exports), null, "exports prototype"); 36 | assert_false(Object.isExtensible(exports), "extensible exports"); 37 | for (const [key, expected] of Object.entries(expected_exports)) { 38 | const property = Object.getOwnPropertyDescriptor(exports, key); 39 | assert_equals(typeof property, "object", `${key} should be present`); 40 | assert_false(property.writable, `${key}: writable`); 41 | assert_true(property.enumerable, `${key}: enumerable`); 42 | assert_false(property.configurable, `${key}: configurable`); 43 | const actual = property.value; 44 | assert_true(Object.isExtensible(actual), `${key}: extensible`); 45 | 46 | switch (expected.kind) { 47 | case "function": 48 | assert_exported_function(actual, expected, `value of ${key}`); 49 | break; 50 | case "global": 51 | assert_equals(Object.getPrototypeOf(actual), WebAssembly.Global.prototype, 52 | `value of ${key}: prototype`); 53 | assert_equals(actual.value, expected.value, `value of ${key}: value`); 54 | assert_equals(actual.valueOf(), expected.value, `value of ${key}: valueOf()`); 55 | break; 56 | case "memory": 57 | assert_equals(Object.getPrototypeOf(actual), WebAssembly.Memory.prototype, 58 | `value of ${key}: prototype`); 59 | assert_equals(Object.getPrototypeOf(actual.buffer), ArrayBuffer.prototype, 60 | `value of ${key}: prototype of buffer`); 61 | assert_equals(actual.buffer.byteLength, 0x10000 * expected.size, `value of ${key}: size of buffer`); 62 | const array = new Uint8Array(actual.buffer); 63 | assert_equals(array[0], 0, `value of ${key}: first element of buffer`); 64 | assert_equals(array[array.byteLength - 1], 0, `value of ${key}: last element of buffer`); 65 | break; 66 | case "table": 67 | assert_equals(Object.getPrototypeOf(actual), WebAssembly.Table.prototype, 68 | `value of ${key}: prototype`); 69 | assert_equals(actual.length, expected.length, `value of ${key}: length of table`); 70 | break; 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /test/js-api/bad-imports.js: -------------------------------------------------------------------------------- 1 | /** 2 | * `t` should be a function that takes at least three arguments: 3 | * 4 | * - the name of the test; 5 | * - the expected error (to be passed to `assert_throws` or similar); 6 | * - a function that takes a `WasmModuleBuilder` and initializes it; 7 | * - (optionally) an options object. 8 | * 9 | * The function is expected to create a test that checks if instantiating a 10 | * module with the result of the `WasmModuleBuilder` and the options object 11 | * (if any) yields the correct error. 12 | */ 13 | function test_bad_imports(t) { 14 | for (const value of [null, true, "", Symbol(), 1, 0.1, NaN]) { 15 | t(`Non-object imports argument: ${format_value(value)}`, 16 | new TypeError(), 17 | builder => {}, 18 | value); 19 | } 20 | 21 | for (const value of [undefined, null, true, "", Symbol(), 1, 0.1, NaN]) { 22 | const imports = { 23 | "module": value, 24 | }; 25 | t(`Non-object module: ${format_value(value)}`, 26 | new TypeError(), 27 | builder => { 28 | builder.addImport("module", "fn", kSig_v_v); 29 | }, 30 | imports); 31 | } 32 | 33 | t(`Missing imports argument`, 34 | new TypeError(), 35 | builder => { 36 | builder.addImport("module", "fn", kSig_v_v); 37 | }); 38 | 39 | for (const [value, name] of [[undefined, "undefined"], [{}, "empty object"], [{ "module\0": null }, "wrong property"]]) { 40 | t(`Imports argument with missing property: ${name}`, 41 | new TypeError(), 42 | builder => { 43 | builder.addImport("module", "fn", kSig_v_v); 44 | }, 45 | value); 46 | } 47 | 48 | t(`Importing an i64 global`, 49 | new WebAssembly.LinkError(), 50 | builder => { 51 | builder.addImportedGlobal("module", "global", kWasmI64); 52 | }, 53 | { 54 | "module": { 55 | "global": 0, 56 | }, 57 | }); 58 | 59 | for (const value of [undefined, null, true, "", Symbol(), 1, 0.1, NaN, {}]) { 60 | t(`Importing a function with an incorrectly-typed value: ${format_value(value)}`, 61 | new WebAssembly.LinkError(), 62 | builder => { 63 | builder.addImport("module", "fn", kSig_v_v); 64 | }, 65 | { 66 | "module": { 67 | "fn": value, 68 | }, 69 | }); 70 | } 71 | 72 | const nonGlobals = [ 73 | [undefined], 74 | [null], 75 | [true], 76 | [""], 77 | [Symbol()], 78 | [{}, "plain object"], 79 | [WebAssembly.Global, "WebAssembly.Global"], 80 | [WebAssembly.Global.prototype, "WebAssembly.Global.prototype"], 81 | [Object.create(WebAssembly.Global.prototype), "Object.create(WebAssembly.Global.prototype)"], 82 | ]; 83 | 84 | for (const [value, name = format_value(value)] of nonGlobals) { 85 | t(`Importing a global with an incorrectly-typed value: ${name}`, 86 | new WebAssembly.LinkError(), 87 | builder => { 88 | builder.addImportedGlobal("module", "global", kWasmI32); 89 | }, 90 | { 91 | "module": { 92 | "global": value, 93 | }, 94 | }); 95 | } 96 | 97 | const nonMemories = [ 98 | [undefined], 99 | [null], 100 | [true], 101 | [""], 102 | [Symbol()], 103 | [1], 104 | [0.1], 105 | [NaN], 106 | [{}, "plain object"], 107 | [WebAssembly.Memory, "WebAssembly.Memory"], 108 | [WebAssembly.Memory.prototype, "WebAssembly.Memory.prototype"], 109 | [Object.create(WebAssembly.Memory.prototype), "Object.create(WebAssembly.Memory.prototype)"], 110 | ]; 111 | 112 | for (const [value, name = format_value(value)] of nonMemories) { 113 | t(`Importing memory with an incorrectly-typed value: ${name}`, 114 | new WebAssembly.LinkError(), 115 | builder => { 116 | builder.addImportedMemory("module", "memory", 0, 128); 117 | }, 118 | { 119 | "module": { 120 | "memory": value, 121 | }, 122 | }); 123 | } 124 | 125 | const nonTables = [ 126 | [undefined], 127 | [null], 128 | [true], 129 | [""], 130 | [Symbol()], 131 | [1], 132 | [0.1], 133 | [NaN], 134 | [{}, "plain object"], 135 | [WebAssembly.Table, "WebAssembly.Table"], 136 | [WebAssembly.Table.prototype, "WebAssembly.Table.prototype"], 137 | [Object.create(WebAssembly.Table.prototype), "Object.create(WebAssembly.Table.prototype)"], 138 | ]; 139 | 140 | for (const [value, name = format_value(value)] of nonTables) { 141 | t(`Importing table with an incorrectly-typed value: ${name}`, 142 | new WebAssembly.LinkError(), 143 | builder => { 144 | builder.addImportedTable("module", "table", 0, 128); 145 | }, 146 | { 147 | "module": { 148 | "table": value, 149 | }, 150 | }); 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /test/js-api/constructor/compile.any.js: -------------------------------------------------------------------------------- 1 | // META: global=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(t, new 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(t, new 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(t, new WebAssembly.CompileError(), WebAssembly.compile(buffer)); 64 | }, "Invalid code"); 65 | 66 | promise_test(() => { 67 | return WebAssembly.compile(emptyModuleBinary).then(assert_Module); 68 | }, "Result type"); 69 | 70 | promise_test(() => { 71 | return WebAssembly.compile(emptyModuleBinary, {}).then(assert_Module); 72 | }, "Stray argument"); 73 | 74 | promise_test(() => { 75 | const buffer = new WasmModuleBuilder().toBuffer(); 76 | assert_equals(buffer[0], 0); 77 | const promise = WebAssembly.compile(buffer); 78 | buffer[0] = 1; 79 | return promise.then(assert_Module); 80 | }, "Changing the buffer"); 81 | -------------------------------------------------------------------------------- /test/js-api/constructor/instantiate-bad-imports.any.js: -------------------------------------------------------------------------------- 1 | // META: global=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(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(t, error, WebAssembly.instantiate(buffer, ...arguments)); 21 | }, `WebAssembly.instantiate(buffer): ${name}`); 22 | }); 23 | -------------------------------------------------------------------------------- /test/js-api/constructor/validate.any.js: -------------------------------------------------------------------------------- 1 | // META: global=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(new 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(new 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/global/constructor.any.js: -------------------------------------------------------------------------------- 1 | // META: global=jsshell 2 | // META: script=/wasm/jsapi/assertions.js 3 | 4 | function assert_Global(actual, expected) { 5 | assert_equals(Object.getPrototypeOf(actual), WebAssembly.Global.prototype, 6 | "prototype"); 7 | assert_true(Object.isExtensible(actual), "extensible"); 8 | 9 | assert_equals(actual.value, expected, "value"); 10 | assert_equals(actual.valueOf(), expected, "valueOf"); 11 | } 12 | 13 | test(() => { 14 | assert_function_name(WebAssembly.Global, "Global", "WebAssembly.Global"); 15 | }, "name"); 16 | 17 | test(() => { 18 | assert_function_length(WebAssembly.Global, 1, "WebAssembly.Global"); 19 | }, "length"); 20 | 21 | test(() => { 22 | assert_throws(new TypeError(), () => new WebAssembly.Global()); 23 | }, "No arguments"); 24 | 25 | test(() => { 26 | const argument = { "value": "i32" }; 27 | assert_throws(new TypeError(), () => WebAssembly.Global(argument)); 28 | }, "Calling"); 29 | 30 | test(() => { 31 | const order = []; 32 | 33 | new WebAssembly.Global({ 34 | get value() { 35 | order.push("descriptor value"); 36 | return { 37 | toString() { 38 | order.push("descriptor value toString"); 39 | return "f64"; 40 | }, 41 | }; 42 | }, 43 | 44 | get mutable() { 45 | order.push("descriptor mutable"); 46 | return false; 47 | }, 48 | }, { 49 | valueOf() { 50 | order.push("value valueOf()"); 51 | } 52 | }); 53 | 54 | assert_array_equals(order, [ 55 | "descriptor mutable", 56 | "descriptor value", 57 | "descriptor value toString", 58 | "value valueOf()", 59 | ]); 60 | }, "Order of evaluation"); 61 | 62 | test(() => { 63 | const invalidArguments = [ 64 | undefined, 65 | null, 66 | false, 67 | true, 68 | "", 69 | "test", 70 | Symbol(), 71 | 1, 72 | NaN, 73 | {}, 74 | ]; 75 | for (const invalidArgument of invalidArguments) { 76 | assert_throws(new TypeError(), 77 | () => new WebAssembly.Global(invalidArgument), 78 | `new Global(${format_value(invalidArgument)})`); 79 | } 80 | }, "Invalid descriptor argument"); 81 | 82 | test(() => { 83 | const invalidTypes = ["i16", "i128", "f16", "f128", "u32", "u64", "i32\0"]; 84 | for (const value of invalidTypes) { 85 | const argument = { value }; 86 | assert_throws(new TypeError(), () => new WebAssembly.Global(argument)); 87 | } 88 | }, "Invalid type argument"); 89 | 90 | test(() => { 91 | const argument = { "value": "i64" }; 92 | const global = new WebAssembly.Global(argument); 93 | assert_throws(new TypeError(), () => global.value); 94 | assert_throws(new TypeError(), () => global.valueOf()); 95 | }, "i64 with default"); 96 | 97 | for (const type of ["i32", "f32", "f64"]) { 98 | test(() => { 99 | const argument = { "value": type }; 100 | const global = new WebAssembly.Global(argument); 101 | assert_Global(global, 0); 102 | }, `Default value for type ${type}`); 103 | 104 | const valueArguments = [ 105 | [undefined, 0], 106 | [null, 0], 107 | [true, 1], 108 | [false, 0], 109 | [2, 2], 110 | ["3", 3], 111 | [{ toString() { return "5" } }, 5, "object with toString"], 112 | [{ valueOf() { return "8" } }, 8, "object with valueOf"], 113 | ]; 114 | for (const [value, expected, name = format_value(value)] of valueArguments) { 115 | test(() => { 116 | const argument = { "value": type }; 117 | const global = new WebAssembly.Global(argument, value); 118 | assert_Global(global, expected); 119 | }, `Explicit value ${name} for type ${type}`); 120 | } 121 | } 122 | 123 | test(() => { 124 | const argument = { "value": "i32" }; 125 | const global = new WebAssembly.Global(argument, 0, {}); 126 | assert_Global(global, 0); 127 | }, "Stray argument"); 128 | -------------------------------------------------------------------------------- /test/js-api/global/toString.any.js: -------------------------------------------------------------------------------- 1 | // META: global=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 | -------------------------------------------------------------------------------- /test/js-api/global/value-get-set.any.js: -------------------------------------------------------------------------------- 1 | // META: global=jsshell 2 | 3 | test(() => { 4 | const thisValues = [ 5 | undefined, 6 | null, 7 | true, 8 | "", 9 | Symbol(), 10 | 1, 11 | {}, 12 | WebAssembly.Global, 13 | WebAssembly.Global.prototype, 14 | ]; 15 | 16 | const desc = Object.getOwnPropertyDescriptor(WebAssembly.Global.prototype, "value"); 17 | assert_equals(typeof desc, "object"); 18 | 19 | const getter = desc.get; 20 | assert_equals(typeof getter, "function"); 21 | 22 | const setter = desc.set; 23 | assert_equals(typeof setter, "function"); 24 | 25 | for (const thisValue of thisValues) { 26 | assert_throws(new TypeError(), () => getter.call(thisValue), `getter with this=${format_value(thisValue)}`); 27 | assert_throws(new TypeError(), () => setter.call(thisValue, 1), `setter with this=${format_value(thisValue)}`); 28 | } 29 | }, "Branding"); 30 | 31 | for (const type of ["i32", "f32", "f64"]) { 32 | const immutableOptions = [ 33 | [{}, "missing"], 34 | [{ "mutable": undefined }, "undefined"], 35 | [{ "mutable": null }, "null"], 36 | [{ "mutable": false }, "false"], 37 | [{ "mutable": "" }, "empty string"], 38 | [{ "mutable": 0 }, "zero"], 39 | ]; 40 | for (const [opts, name] of immutableOptions) { 41 | test(() => { 42 | opts.value = type; 43 | const global = new WebAssembly.Global(opts); 44 | assert_equals(global.value, 0, "initial value"); 45 | assert_equals(global.valueOf(), 0, "initial valueOf"); 46 | 47 | assert_throws(new TypeError(), () => global.value = 1); 48 | 49 | assert_equals(global.value, 0, "post-set value"); 50 | assert_equals(global.valueOf(), 0, "post-set valueOf"); 51 | }, `Immutable ${type} (${name})`); 52 | } 53 | 54 | const mutableOptions = [ 55 | [{ "mutable": true }, "true"], 56 | [{ "mutable": 1 }, "one"], 57 | [{ "mutable": "x" }, "string"], 58 | [Object.create({ "mutable": true }), "true on prototype"], 59 | ]; 60 | for (const [opts, name] of mutableOptions) { 61 | test(() => { 62 | opts.value = type; 63 | const global = new WebAssembly.Global(opts); 64 | assert_equals(global.value, 0, "initial value"); 65 | assert_equals(global.valueOf(), 0, "initial valueOf"); 66 | 67 | global.value = 1; 68 | 69 | assert_equals(global.value, 1, "post-set value"); 70 | assert_equals(global.valueOf(), 1, "post-set valueOf"); 71 | }, `Mutable ${type} (${name})`); 72 | } 73 | } 74 | 75 | test(() => { 76 | const argument = { "value": "i64", "mutable": true }; 77 | const global = new WebAssembly.Global(argument); 78 | assert_throws(new TypeError(), () => global.value); 79 | assert_throws(new TypeError(), () => global.value = 0); 80 | assert_throws(new TypeError(), () => global.valueOf()); 81 | }, "i64 with default"); 82 | 83 | 84 | test(() => { 85 | const argument = { "value": "i32", "mutable": true }; 86 | const global = new WebAssembly.Global(argument); 87 | const desc = Object.getOwnPropertyDescriptor(WebAssembly.Global.prototype, "value"); 88 | assert_equals(typeof desc, "object"); 89 | 90 | const setter = desc.set; 91 | assert_equals(typeof setter, "function"); 92 | 93 | assert_throws(new TypeError(), () => setter.call(global)); 94 | }, "Calling setter without argument"); 95 | 96 | test(() => { 97 | const argument = { "value": "i32", "mutable": true }; 98 | const global = new WebAssembly.Global(argument); 99 | const desc = Object.getOwnPropertyDescriptor(WebAssembly.Global.prototype, "value"); 100 | assert_equals(typeof desc, "object"); 101 | 102 | const getter = desc.get; 103 | assert_equals(typeof getter, "function"); 104 | 105 | const setter = desc.set; 106 | assert_equals(typeof setter, "function"); 107 | 108 | assert_equals(getter.call(global, {}), 0); 109 | assert_equals(setter.call(global, 1, {}), undefined); 110 | assert_equals(global.value, 1); 111 | }, "Stray argument"); 112 | -------------------------------------------------------------------------------- /test/js-api/global/valueOf.any.js: -------------------------------------------------------------------------------- 1 | // META: global=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(new 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=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(error, () => new WebAssembly.Instance(module, ...arguments)); 12 | }, `new WebAssembly.Instance(module): ${name}`); 13 | }); 14 | -------------------------------------------------------------------------------- /test/js-api/instance/constructor.any.js: -------------------------------------------------------------------------------- 1 | // META: global=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(new 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(new 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(new 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=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(new 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(new 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=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 | -------------------------------------------------------------------------------- /test/js-api/memory/buffer.any.js: -------------------------------------------------------------------------------- 1 | // META: global=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(new 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(new 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/constructor.any.js: -------------------------------------------------------------------------------- 1 | // META: global=jsshell 2 | // META: script=/wasm/jsapi/assertions.js 3 | 4 | function assert_Memory(memory, expected) { 5 | assert_equals(Object.getPrototypeOf(memory), WebAssembly.Memory.prototype, 6 | "prototype"); 7 | assert_true(Object.isExtensible(memory), "extensible"); 8 | 9 | // https://github.com/WebAssembly/spec/issues/840 10 | assert_equals(memory.buffer, memory.buffer, "buffer should be idempotent"); 11 | assert_equals(Object.getPrototypeOf(memory.buffer), ArrayBuffer.prototype, 12 | "prototype of buffer"); 13 | assert_true(Object.isExtensible(memory.buffer), "buffer extensibility"); 14 | assert_equals(memory.buffer.byteLength, 0x10000 * expected.size, "size of buffer"); 15 | if (expected.size > 0) { 16 | const array = new Uint8Array(memory.buffer); 17 | assert_equals(array[0], 0, "first element of buffer"); 18 | assert_equals(array[array.byteLength - 1], 0, "last element of buffer"); 19 | } 20 | } 21 | 22 | test(() => { 23 | assert_function_name(WebAssembly.Memory, "Memory", "WebAssembly.Memory"); 24 | }, "name"); 25 | 26 | test(() => { 27 | assert_function_length(WebAssembly.Memory, 1, "WebAssembly.Memory"); 28 | }, "length"); 29 | 30 | test(() => { 31 | assert_throws(new TypeError(), () => new WebAssembly.Memory()); 32 | }, "No arguments"); 33 | 34 | test(() => { 35 | const argument = { "initial": 0 }; 36 | assert_throws(new TypeError(), () => WebAssembly.Memory(argument)); 37 | }, "Calling"); 38 | 39 | test(() => { 40 | const invalidArguments = [ 41 | undefined, 42 | null, 43 | false, 44 | true, 45 | "", 46 | "test", 47 | Symbol(), 48 | 1, 49 | NaN, 50 | {}, 51 | ]; 52 | for (const invalidArgument of invalidArguments) { 53 | assert_throws(new TypeError(), 54 | () => new WebAssembly.Memory(invalidArgument), 55 | `new Memory(${format_value(invalidArgument)})`); 56 | } 57 | }, "Invalid descriptor argument"); 58 | 59 | test(() => { 60 | assert_throws(new TypeError(), () => new WebAssembly.Memory({ "initial": undefined })); 61 | }, "Undefined initial value in descriptor"); 62 | 63 | const outOfRangeValues = [ 64 | NaN, 65 | Infinity, 66 | -Infinity, 67 | -1, 68 | 0x100000000, 69 | 0x1000000000, 70 | ]; 71 | 72 | for (const value of outOfRangeValues) { 73 | test(() => { 74 | assert_throws(new TypeError(), () => new WebAssembly.Memory({ "initial": value })); 75 | }, `Out-of-range initial value in descriptor: ${format_value(value)}`); 76 | 77 | test(() => { 78 | assert_throws(new TypeError(), () => new WebAssembly.Memory({ "initial": 0, "maximum": value })); 79 | }, `Out-of-range maximum value in descriptor: ${format_value(value)}`); 80 | } 81 | 82 | test(() => { 83 | assert_throws(new RangeError(), () => new WebAssembly.Memory({ "initial": 10, "maximum": 9 })); 84 | }, "Initial value exceeds maximum"); 85 | 86 | test(() => { 87 | const proxy = new Proxy({}, { 88 | has(o, x) { 89 | assert_unreached(`Should not call [[HasProperty]] with ${x}`); 90 | }, 91 | get(o, x) { 92 | return 0; 93 | }, 94 | }); 95 | new WebAssembly.Memory(proxy); 96 | }, "Proxy descriptor"); 97 | 98 | test(() => { 99 | const order = []; 100 | 101 | new WebAssembly.Memory({ 102 | get maximum() { 103 | order.push("maximum"); 104 | return { 105 | valueOf() { 106 | order.push("maximum valueOf"); 107 | return 1; 108 | }, 109 | }; 110 | }, 111 | 112 | get initial() { 113 | order.push("initial"); 114 | return { 115 | valueOf() { 116 | order.push("initial valueOf"); 117 | return 1; 118 | }, 119 | }; 120 | }, 121 | }); 122 | 123 | assert_array_equals(order, [ 124 | "initial", 125 | "initial valueOf", 126 | "maximum", 127 | "maximum valueOf", 128 | ]); 129 | }, "Order of evaluation for descriptor"); 130 | 131 | test(() => { 132 | const argument = { "initial": 0 }; 133 | const memory = new WebAssembly.Memory(argument); 134 | assert_Memory(memory, { "size": 0 }); 135 | }, "Zero initial"); 136 | 137 | test(() => { 138 | const argument = { "initial": 4 }; 139 | const memory = new WebAssembly.Memory(argument); 140 | assert_Memory(memory, { "size": 4 }); 141 | }, "Non-zero initial"); 142 | 143 | test(() => { 144 | const argument = { "initial": 0 }; 145 | const memory = new WebAssembly.Memory(argument, {}); 146 | assert_Memory(memory, { "size": 0 }); 147 | }, "Stray argument"); 148 | -------------------------------------------------------------------------------- /test/js-api/memory/toString.any.js: -------------------------------------------------------------------------------- 1 | // META: global=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 | -------------------------------------------------------------------------------- /test/js-api/module/constructor.any.js: -------------------------------------------------------------------------------- 1 | // META: global=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(new TypeError(), () => new WebAssembly.Module()); 20 | }, "No arguments"); 21 | 22 | test(() => { 23 | assert_throws(new 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(new 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(new WebAssembly.CompileError(), () => new WebAssembly.Module(buffer)); 49 | }, "Empty buffer"); 50 | 51 | test(() => { 52 | const module = new WebAssembly.Module(emptyModuleBinary); 53 | assert_equals(Object.getPrototypeOf(module), WebAssembly.Module.prototype); 54 | }, "Prototype"); 55 | 56 | test(() => { 57 | const module = new WebAssembly.Module(emptyModuleBinary); 58 | assert_true(Object.isExtensible(module)); 59 | }, "Extensibility"); 60 | 61 | test(() => { 62 | const module = new WebAssembly.Module(emptyModuleBinary, {}); 63 | assert_equals(Object.getPrototypeOf(module), WebAssembly.Module.prototype); 64 | }, "Stray argument"); 65 | -------------------------------------------------------------------------------- /test/js-api/module/exports.any.js: -------------------------------------------------------------------------------- 1 | // META: global=jsshell 2 | // META: script=/wasm/jsapi/wasm-module-builder.js 3 | 4 | let emptyModuleBinary; 5 | setup(() => { 6 | emptyModuleBinary = new WasmModuleBuilder().toBuffer(); 7 | }); 8 | 9 | function assert_ModuleExportDescriptor(export_, expected) { 10 | assert_equals(Object.getPrototypeOf(export_), Object.prototype, "Prototype"); 11 | assert_true(Object.isExtensible(export_), "isExtensible"); 12 | 13 | const name = Object.getOwnPropertyDescriptor(export_, "name"); 14 | assert_true(name.writable, "name: writable"); 15 | assert_true(name.enumerable, "name: enumerable"); 16 | assert_true(name.configurable, "name: configurable"); 17 | assert_equals(name.value, expected.name); 18 | 19 | const kind = Object.getOwnPropertyDescriptor(export_, "kind"); 20 | assert_true(kind.writable, "kind: writable"); 21 | assert_true(kind.enumerable, "kind: enumerable"); 22 | assert_true(kind.configurable, "kind: configurable"); 23 | assert_equals(kind.value, expected.kind); 24 | } 25 | 26 | function assert_exports(exports, expected) { 27 | assert_true(Array.isArray(exports), "Should be array"); 28 | assert_equals(Object.getPrototypeOf(exports), Array.prototype, "Prototype"); 29 | assert_true(Object.isExtensible(exports), "isExtensible"); 30 | 31 | assert_equals(exports.length, expected.length); 32 | for (let i = 0; i < expected.length; ++i) { 33 | assert_ModuleExportDescriptor(exports[i], expected[i]); 34 | } 35 | } 36 | 37 | test(() => { 38 | assert_throws(new TypeError(), () => WebAssembly.Module.exports()); 39 | }, "Missing arguments"); 40 | 41 | test(() => { 42 | const invalidArguments = [ 43 | undefined, 44 | null, 45 | true, 46 | "", 47 | Symbol(), 48 | 1, 49 | {}, 50 | WebAssembly.Module, 51 | WebAssembly.Module.prototype, 52 | ]; 53 | for (const argument of invalidArguments) { 54 | assert_throws(new TypeError(), () => WebAssembly.Module.exports(argument), 55 | `exports(${format_value(argument)})`); 56 | } 57 | }, "Non-Module arguments"); 58 | 59 | test(() => { 60 | const module = new WebAssembly.Module(emptyModuleBinary); 61 | const fn = WebAssembly.Module.exports; 62 | const thisValues = [ 63 | undefined, 64 | null, 65 | true, 66 | "", 67 | Symbol(), 68 | 1, 69 | {}, 70 | WebAssembly.Module, 71 | WebAssembly.Module.prototype, 72 | ]; 73 | for (const thisValue of thisValues) { 74 | assert_array_equals(fn.call(thisValue, module), []); 75 | } 76 | }, "Branding"); 77 | 78 | test(() => { 79 | const module = new WebAssembly.Module(emptyModuleBinary); 80 | const exports = WebAssembly.Module.exports(module); 81 | assert_exports(exports, []); 82 | }, "Empty module"); 83 | 84 | test(() => { 85 | const module = new WebAssembly.Module(emptyModuleBinary); 86 | assert_not_equals(WebAssembly.Module.exports(module), WebAssembly.Module.exports(module)); 87 | }, "Empty module: array caching"); 88 | 89 | test(() => { 90 | const builder = new WasmModuleBuilder(); 91 | 92 | builder 93 | .addFunction("fn", kSig_v_v) 94 | .addBody([]) 95 | .exportFunc(); 96 | builder 97 | .addFunction("fn2", kSig_v_v) 98 | .addBody([]) 99 | .exportFunc(); 100 | 101 | builder.setTableBounds(1); 102 | builder.addExportOfKind("table", kExternalTable, 0); 103 | 104 | builder.addGlobal(kWasmI32, true) 105 | .exportAs("global") 106 | .init = 7; 107 | builder.addGlobal(kWasmF64, true) 108 | .exportAs("global2") 109 | .init = 1.2; 110 | 111 | builder.addMemory(0, 256, true); 112 | 113 | const buffer = builder.toBuffer() 114 | const module = new WebAssembly.Module(buffer); 115 | const exports = WebAssembly.Module.exports(module); 116 | const expected = [ 117 | { "kind": "function", "name": "fn" }, 118 | { "kind": "function", "name": "fn2" }, 119 | { "kind": "table", "name": "table" }, 120 | { "kind": "global", "name": "global" }, 121 | { "kind": "global", "name": "global2" }, 122 | { "kind": "memory", "name": "memory" }, 123 | ]; 124 | assert_exports(exports, expected); 125 | }, "exports"); 126 | 127 | test(() => { 128 | const module = new WebAssembly.Module(emptyModuleBinary); 129 | const exports = WebAssembly.Module.exports(module, {}); 130 | assert_exports(exports, []); 131 | }, "Stray argument"); 132 | -------------------------------------------------------------------------------- /test/js-api/module/imports.any.js: -------------------------------------------------------------------------------- 1 | // META: global=jsshell 2 | // META: script=/wasm/jsapi/wasm-module-builder.js 3 | 4 | function assert_ModuleImportDescriptor(import_, expected) { 5 | assert_equals(Object.getPrototypeOf(import_), Object.prototype, "Prototype"); 6 | assert_true(Object.isExtensible(import_), "isExtensible"); 7 | 8 | const module = Object.getOwnPropertyDescriptor(import_, "module"); 9 | assert_true(module.writable, "module: writable"); 10 | assert_true(module.enumerable, "module: enumerable"); 11 | assert_true(module.configurable, "module: configurable"); 12 | assert_equals(module.value, expected.module); 13 | 14 | const name = Object.getOwnPropertyDescriptor(import_, "name"); 15 | assert_true(name.writable, "name: writable"); 16 | assert_true(name.enumerable, "name: enumerable"); 17 | assert_true(name.configurable, "name: configurable"); 18 | assert_equals(name.value, expected.name); 19 | 20 | const kind = Object.getOwnPropertyDescriptor(import_, "kind"); 21 | assert_true(kind.writable, "kind: writable"); 22 | assert_true(kind.enumerable, "kind: enumerable"); 23 | assert_true(kind.configurable, "kind: configurable"); 24 | assert_equals(kind.value, expected.kind); 25 | } 26 | 27 | function assert_imports(imports, expected) { 28 | assert_true(Array.isArray(imports), "Should be array"); 29 | assert_equals(Object.getPrototypeOf(imports), Array.prototype, "Prototype"); 30 | assert_true(Object.isExtensible(imports), "isExtensible"); 31 | 32 | assert_equals(imports.length, expected.length); 33 | for (let i = 0; i < expected.length; ++i) { 34 | assert_ModuleImportDescriptor(imports[i], expected[i]); 35 | } 36 | } 37 | 38 | let emptyModuleBinary; 39 | setup(() => { 40 | emptyModuleBinary = new WasmModuleBuilder().toBuffer(); 41 | }); 42 | 43 | test(() => { 44 | assert_throws(new TypeError(), () => WebAssembly.Module.imports()); 45 | }, "Missing arguments"); 46 | 47 | test(() => { 48 | const invalidArguments = [ 49 | undefined, 50 | null, 51 | true, 52 | "", 53 | Symbol(), 54 | 1, 55 | {}, 56 | WebAssembly.Module, 57 | WebAssembly.Module.prototype, 58 | ]; 59 | for (const argument of invalidArguments) { 60 | assert_throws(new TypeError(), () => WebAssembly.Module.imports(argument), 61 | `imports(${format_value(argument)})`); 62 | } 63 | }, "Non-Module arguments"); 64 | 65 | test(() => { 66 | const module = new WebAssembly.Module(emptyModuleBinary); 67 | const fn = WebAssembly.Module.imports; 68 | const thisValues = [ 69 | undefined, 70 | null, 71 | true, 72 | "", 73 | Symbol(), 74 | 1, 75 | {}, 76 | WebAssembly.Module, 77 | WebAssembly.Module.prototype, 78 | ]; 79 | for (const thisValue of thisValues) { 80 | assert_array_equals(fn.call(thisValue, module), []); 81 | } 82 | }, "Branding"); 83 | 84 | test(() => { 85 | const module = new WebAssembly.Module(emptyModuleBinary); 86 | const imports = WebAssembly.Module.imports(module); 87 | assert_true(Array.isArray(imports)); 88 | }, "Return type"); 89 | 90 | test(() => { 91 | const module = new WebAssembly.Module(emptyModuleBinary); 92 | const imports = WebAssembly.Module.imports(module); 93 | assert_imports(imports, []); 94 | }, "Empty module"); 95 | 96 | test(() => { 97 | const module = new WebAssembly.Module(emptyModuleBinary); 98 | assert_not_equals(WebAssembly.Module.imports(module), WebAssembly.Module.imports(module)); 99 | }, "Empty module: array caching"); 100 | 101 | test(() => { 102 | const builder = new WasmModuleBuilder(); 103 | 104 | builder.addImport("module", "fn", kSig_v_v); 105 | builder.addImportedGlobal("module", "global", kWasmI32); 106 | builder.addImportedMemory("module", "memory", 0, 128); 107 | builder.addImportedTable("module", "table", 0, 128); 108 | 109 | const buffer = builder.toBuffer() 110 | const module = new WebAssembly.Module(buffer); 111 | const imports = WebAssembly.Module.imports(module); 112 | const expected = [ 113 | { "module": "module", "kind": "function", "name": "fn" }, 114 | { "module": "module", "kind": "global", "name": "global" }, 115 | { "module": "module", "kind": "memory", "name": "memory" }, 116 | { "module": "module", "kind": "table", "name": "table" }, 117 | ]; 118 | assert_imports(imports, expected); 119 | }, "imports"); 120 | 121 | test(() => { 122 | const module = new WebAssembly.Module(emptyModuleBinary); 123 | const imports = WebAssembly.Module.imports(module, {}); 124 | assert_imports(imports, []); 125 | }, "Stray argument"); 126 | -------------------------------------------------------------------------------- /test/js-api/module/toString.any.js: -------------------------------------------------------------------------------- 1 | // META: global=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 | -------------------------------------------------------------------------------- /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(new 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(new RangeError(), () => table.get(expected.length), 10 | `${message}: table.get(${expected.length} of ${expected.length})`); 11 | assert_throws(new RangeError(), () => table.get(expected.length + 1), 12 | `${message}: table.get(${expected.length + 1} of ${expected.length})`); 13 | } 14 | -------------------------------------------------------------------------------- /test/js-api/table/constructor.any.js: -------------------------------------------------------------------------------- 1 | // META: global=jsshell 2 | // META: script=/wasm/jsapi/assertions.js 3 | 4 | function assert_Table(actual, expected) { 5 | assert_equals(Object.getPrototypeOf(actual), WebAssembly.Table.prototype, 6 | "prototype"); 7 | assert_true(Object.isExtensible(actual), "extensible"); 8 | 9 | assert_equals(actual.length, expected.length, "length"); 10 | for (let i = 0; i < expected.length; ++i) { 11 | assert_equals(actual.get(i), null, `actual.get(${i})`); 12 | } 13 | } 14 | 15 | test(() => { 16 | assert_function_name(WebAssembly.Table, "Table", "WebAssembly.Table"); 17 | }, "name"); 18 | 19 | test(() => { 20 | assert_function_length(WebAssembly.Table, 1, "WebAssembly.Table"); 21 | }, "length"); 22 | 23 | test(() => { 24 | assert_throws(new TypeError(), () => new WebAssembly.Table()); 25 | }, "No arguments"); 26 | 27 | test(() => { 28 | const argument = { "element": "anyfunc", "initial": 0 }; 29 | assert_throws(new TypeError(), () => WebAssembly.Table(argument)); 30 | }, "Calling"); 31 | 32 | test(() => { 33 | assert_throws(new TypeError(), () => new WebAssembly.Table({})); 34 | }, "Empty descriptor"); 35 | 36 | test(() => { 37 | const invalidArguments = [ 38 | undefined, 39 | null, 40 | false, 41 | true, 42 | "", 43 | "test", 44 | Symbol(), 45 | 1, 46 | NaN, 47 | {}, 48 | ]; 49 | for (const invalidArgument of invalidArguments) { 50 | assert_throws(new TypeError(), 51 | () => new WebAssembly.Table(invalidArgument), 52 | `new Table(${format_value(invalidArgument)})`); 53 | } 54 | }, "Invalid descriptor argument"); 55 | 56 | test(() => { 57 | assert_throws(new TypeError(), () => new WebAssembly.Table({ "element": "anyfunc", "initial": undefined })); 58 | }, "Undefined initial value in descriptor"); 59 | 60 | test(() => { 61 | assert_throws(new TypeError(), () => new WebAssembly.Table({ "element": undefined, "initial": 0 })); 62 | }, "Undefined element value in descriptor"); 63 | 64 | const outOfRangeValues = [ 65 | NaN, 66 | Infinity, 67 | -Infinity, 68 | -1, 69 | 0x100000000, 70 | 0x1000000000, 71 | ]; 72 | 73 | for (const value of outOfRangeValues) { 74 | test(() => { 75 | assert_throws(new TypeError(), () => new WebAssembly.Table({ "element": "anyfunc", "initial": value })); 76 | }, `Out-of-range initial value in descriptor: ${format_value(value)}`); 77 | 78 | test(() => { 79 | assert_throws(new TypeError(), () => new WebAssembly.Table({ "element": "anyfunc", "initial": 0, "maximum": value })); 80 | }, `Out-of-range maximum value in descriptor: ${format_value(value)}`); 81 | } 82 | 83 | test(() => { 84 | assert_throws(new RangeError(), () => new WebAssembly.Table({ "element": "anyfunc", "initial": 10, "maximum": 9 })); 85 | }, "Initial value exceeds maximum"); 86 | 87 | test(() => { 88 | const argument = { "element": "anyfunc", "initial": 0 }; 89 | const table = new WebAssembly.Table(argument); 90 | assert_Table(table, { "length": 0 }); 91 | }, "Basic (zero)"); 92 | 93 | test(() => { 94 | const argument = { "element": "anyfunc", "initial": 5 }; 95 | const table = new WebAssembly.Table(argument); 96 | assert_Table(table, { "length": 5 }); 97 | }, "Basic (non-zero)"); 98 | 99 | test(() => { 100 | const argument = { "element": "anyfunc", "initial": 0 }; 101 | const table = new WebAssembly.Table(argument, {}); 102 | assert_Table(table, { "length": 0 }); 103 | }, "Stray argument"); 104 | 105 | test(() => { 106 | const proxy = new Proxy({}, { 107 | has(o, x) { 108 | assert_unreached(`Should not call [[HasProperty]] with ${x}`); 109 | }, 110 | get(o, x) { 111 | switch (x) { 112 | case "element": 113 | return "anyfunc"; 114 | case "initial": 115 | case "maximum": 116 | return 0; 117 | default: 118 | return undefined; 119 | } 120 | }, 121 | }); 122 | const table = new WebAssembly.Table(proxy); 123 | assert_Table(table, { "length": 0 }); 124 | }, "Proxy descriptor"); 125 | 126 | test(() => { 127 | const table = new WebAssembly.Table({ 128 | "element": { 129 | toString() { return "anyfunc"; }, 130 | }, 131 | "initial": 1, 132 | }); 133 | assert_Table(table, { "length": 1 }); 134 | }, "Type conversion for descriptor.element"); 135 | 136 | test(() => { 137 | const order = []; 138 | 139 | new WebAssembly.Table({ 140 | get maximum() { 141 | order.push("maximum"); 142 | return { 143 | valueOf() { 144 | order.push("maximum valueOf"); 145 | return 1; 146 | }, 147 | }; 148 | }, 149 | 150 | get initial() { 151 | order.push("initial"); 152 | return { 153 | valueOf() { 154 | order.push("initial valueOf"); 155 | return 1; 156 | }, 157 | }; 158 | }, 159 | 160 | get element() { 161 | order.push("element"); 162 | return { 163 | toString() { 164 | order.push("element toString"); 165 | return "anyfunc"; 166 | }, 167 | }; 168 | }, 169 | }); 170 | 171 | assert_array_equals(order, [ 172 | "element", 173 | "element toString", 174 | "initial", 175 | "initial valueOf", 176 | "maximum", 177 | "maximum valueOf", 178 | ]); 179 | }, "Order of evaluation for descriptor"); 180 | -------------------------------------------------------------------------------- /test/js-api/table/grow.any.js: -------------------------------------------------------------------------------- 1 | // META: global=jsshell 2 | // META: script=assertions.js 3 | 4 | function nulls(n) { 5 | return Array(n).fill(null); 6 | } 7 | 8 | test(() => { 9 | const argument = { "element": "anyfunc", "initial": 5 }; 10 | const table = new WebAssembly.Table(argument); 11 | assert_throws(new TypeError(), () => table.grow()); 12 | }, "Missing arguments"); 13 | 14 | test(t => { 15 | const thisValues = [ 16 | undefined, 17 | null, 18 | true, 19 | "", 20 | Symbol(), 21 | 1, 22 | {}, 23 | WebAssembly.Table, 24 | WebAssembly.Table.prototype, 25 | ]; 26 | 27 | const argument = { 28 | valueOf: t.unreached_func("Should not touch the argument (valueOf)"), 29 | toString: t.unreached_func("Should not touch the argument (toString)"), 30 | }; 31 | 32 | const fn = WebAssembly.Table.prototype.grow; 33 | 34 | for (const thisValue of thisValues) { 35 | assert_throws(new TypeError(), () => fn.call(thisValue, argument), `this=${format_value(thisValue)}`); 36 | } 37 | }, "Branding"); 38 | 39 | test(() => { 40 | const argument = { "element": "anyfunc", "initial": 5 }; 41 | const table = new WebAssembly.Table(argument); 42 | assert_equal_to_array(table, nulls(5), "before"); 43 | 44 | const result = table.grow(3); 45 | assert_equals(result, 5); 46 | assert_equal_to_array(table, nulls(8), "after"); 47 | }, "Basic"); 48 | 49 | test(() => { 50 | const argument = { "element": "anyfunc", "initial": 3, "maximum": 5 }; 51 | const table = new WebAssembly.Table(argument); 52 | assert_equal_to_array(table, nulls(3), "before"); 53 | 54 | const result = table.grow(2); 55 | assert_equals(result, 3); 56 | assert_equal_to_array(table, nulls(5), "after"); 57 | }, "Reached maximum"); 58 | 59 | test(() => { 60 | const argument = { "element": "anyfunc", "initial": 2, "maximum": 5 }; 61 | const table = new WebAssembly.Table(argument); 62 | assert_equal_to_array(table, nulls(2), "before"); 63 | 64 | assert_throws(new RangeError(), () => table.grow(4)); 65 | assert_equal_to_array(table, nulls(2), "after"); 66 | }, "Exceeded maximum"); 67 | 68 | const outOfRangeValues = [ 69 | undefined, 70 | NaN, 71 | Infinity, 72 | -Infinity, 73 | -1, 74 | 0x100000000, 75 | 0x1000000000, 76 | "0x100000000", 77 | { valueOf() { return 0x100000000; } }, 78 | ]; 79 | 80 | for (const value of outOfRangeValues) { 81 | test(() => { 82 | const argument = { "element": "anyfunc", "initial": 1 }; 83 | const table = new WebAssembly.Table(argument); 84 | assert_throws(new TypeError(), () => table.grow(value)); 85 | }, `Out-of-range argument: ${format_value(value)}`); 86 | } 87 | 88 | test(() => { 89 | const argument = { "element": "anyfunc", "initial": 5 }; 90 | const table = new WebAssembly.Table(argument); 91 | assert_equal_to_array(table, nulls(5), "before"); 92 | 93 | const result = table.grow(3, {}); 94 | assert_equals(result, 5); 95 | assert_equal_to_array(table, nulls(8), "after"); 96 | }, "Stray argument"); 97 | -------------------------------------------------------------------------------- /test/js-api/table/length.any.js: -------------------------------------------------------------------------------- 1 | // META: global=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(new 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(new 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=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 | -------------------------------------------------------------------------------- /w3c.json: -------------------------------------------------------------------------------- 1 | { 2 | "group": [78073] 3 | , "contacts": ["ericprud"] 4 | , "repo-type": "cg-report" 5 | } 6 | --------------------------------------------------------------------------------