├── .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 │ │ ├── gen-index-instructions.py │ │ ├── implementation.rst │ │ ├── index-instructions.rst │ │ ├── index-rules.rst │ │ ├── index-types.rst │ │ ├── index.rst │ │ └── properties.rst │ ├── binary │ │ ├── conventions.rst │ │ ├── index.rst │ │ ├── instructions.rst │ │ ├── modules.rst │ │ ├── types.rst │ │ └── values.rst │ ├── conf.py │ ├── exec │ │ ├── conventions.rst │ │ ├── index.rst │ │ ├── instructions.rst │ │ ├── modules.rst │ │ ├── numerics.rst │ │ └── runtime.rst │ ├── index.bs │ ├── index.rst │ ├── intro │ │ ├── index.rst │ │ ├── introduction.rst │ │ └── overview.rst │ ├── make.bat │ ├── static │ │ ├── custom.css │ │ └── webassembly.png │ ├── syntax │ │ ├── conventions.rst │ │ ├── index.rst │ │ ├── instructions.rst │ │ ├── modules.rst │ │ ├── types.rst │ │ └── values.rst │ ├── text │ │ ├── conventions.rst │ │ ├── index.rst │ │ ├── instructions.rst │ │ ├── lexical.rst │ │ ├── modules.rst │ │ ├── types.rst │ │ └── values.rst │ ├── util │ │ ├── README.htmldiff.pl │ │ ├── bikeshed │ │ │ └── conf.py │ │ ├── bikeshed_fixup.py │ │ ├── katex_fix.patch │ │ ├── macros.def │ │ ├── mathdef.py │ │ ├── mathdefbs.py │ │ ├── mathjax2katex.py │ │ └── pseudo-lexer.py │ └── valid │ │ ├── conventions.rst │ │ ├── index.rst │ │ ├── instructions.rst │ │ ├── modules.rst │ │ └── types.rst ├── deploy.sh ├── index.html ├── js-api │ ├── Makefile │ └── index.bs ├── travis-deploy.sh ├── util │ └── htmldiff.pl └── web-api │ ├── Makefile │ └── index.bs ├── interpreter ├── .gitignore ├── .merlin ├── LICENSE ├── Makefile ├── README.md ├── binary │ ├── decode.ml │ ├── decode.mli │ ├── encode.ml │ ├── encode.mli │ ├── utf8.ml │ └── utf8.mli ├── exec │ ├── eval.ml │ ├── eval.mli │ ├── eval_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 │ ├── free.ml │ ├── free.mli │ ├── 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 ├── README.md ├── bulk-memory-operations │ └── Overview.md ├── multi-value │ └── Overview.md ├── nontrapping-float-to-int-conversion │ └── Overview.md └── sign-extension-ops │ └── Overview.md ├── test ├── LICENSE ├── README.md ├── Todo.md ├── build.py ├── core │ ├── .gitignore │ ├── README.md │ ├── address.wast │ ├── align.wast │ ├── binary-leb128.wast │ ├── binary.wast │ ├── block.wast │ ├── br.wast │ ├── br_if.wast │ ├── br_table.wast │ ├── bulk.wast │ ├── call.wast │ ├── call_indirect.wast │ ├── comments.wast │ ├── const.wast │ ├── conversions.wast │ ├── custom.wast │ ├── data.wast │ ├── elem.wast │ ├── endianness.wast │ ├── exports.wast │ ├── f32.wast │ ├── f32_bitwise.wast │ ├── f32_cmp.wast │ ├── f64.wast │ ├── f64_bitwise.wast │ ├── f64_cmp.wast │ ├── fac.wast │ ├── float_exprs.wast │ ├── float_literals.wast │ ├── float_memory.wast │ ├── float_misc.wast │ ├── forward.wast │ ├── func.wast │ ├── func_ptrs.wast │ ├── global.wast │ ├── i32.wast │ ├── i64.wast │ ├── if.wast │ ├── imports.wast │ ├── inline-module.wast │ ├── int_exprs.wast │ ├── int_literals.wast │ ├── labels.wast │ ├── left-to-right.wast │ ├── linking.wast │ ├── load.wast │ ├── local_get.wast │ ├── local_set.wast │ ├── local_tee.wast │ ├── loop.wast │ ├── memory.wast │ ├── memory_copy.wast │ ├── memory_fill.wast │ ├── memory_grow.wast │ ├── memory_init.wast │ ├── memory_redundancy.wast │ ├── memory_size.wast │ ├── memory_trap.wast │ ├── names.wast │ ├── nop.wast │ ├── return.wast │ ├── run.py │ ├── select.wast │ ├── skip-stack-guard-page.wast │ ├── stack.wast │ ├── start.wast │ ├── store.wast │ ├── switch.wast │ ├── table.wast │ ├── table_copy.wast │ ├── table_init.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 │ ├── error-interfaces-no-symbol-tostringtag.js │ ├── global │ │ ├── constructor.any.js │ │ ├── toString.any.js │ │ ├── value-get-set.any.js │ │ └── valueOf.any.js │ ├── instance │ │ ├── constructor-bad-imports.any.js │ │ ├── constructor-caching.any.js │ │ ├── constructor.any.js │ │ ├── exports.any.js │ │ └── toString.any.js │ ├── instanceTestFactory.js │ ├── interface.any.js │ ├── limits.any.js │ ├── memory │ │ ├── assertions.js │ │ ├── buffer.any.js │ │ ├── constructor.any.js │ │ ├── grow.any.js │ │ └── toString.any.js │ ├── module │ │ ├── constructor.any.js │ │ ├── customSections.any.js │ │ ├── exports.any.js │ │ ├── imports.any.js │ │ └── toString.any.js │ ├── prototypes.any.js │ ├── table │ │ ├── assertions.js │ │ ├── constructor.any.js │ │ ├── get-set.any.js │ │ ├── grow.any.js │ │ ├── length.any.js │ │ └── toString.any.js │ └── wasm-module-builder.js ├── meta │ ├── Makefile │ ├── README.md │ ├── common.js │ ├── generate_memory_copy.js │ ├── generate_memory_fill.js │ ├── generate_memory_init.js │ ├── generate_table_copy.js │ ├── generate_table_init.js │ └── noderun.sh └── sync-js-api.py ├── w3c.json └── wasm-specs.bib /.gitattributes: -------------------------------------------------------------------------------- 1 | *.rst linguist-documentation=false 2 | document/* linguist-documentation=false 3 | document/*.rst linguist-documentation=false 4 | document/*/*.rst linguist-documentation=false 5 | test/harness/wast.js linguist-vendored 6 | test/harness/testharness* linguist-vendored 7 | 8 | -------------------------------------------------------------------------------- /.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: 'ppa:avsm/ppa' 10 | - sourceline: 'deb https://dl.yarnpkg.com/debian/ stable main' 11 | key_url: 'https://dl.yarnpkg.com/debian/pubkey.gpg' 12 | update: true 13 | packages: 14 | - opam 15 | - texlive-full 16 | - yarn 17 | 18 | install: 19 | - opam init --auto-setup --compiler=4.07.1 20 | - eval $(opam env) 21 | - opam --version 22 | - ocaml --version 23 | - opam install --yes ocamlbuild.0.14.0 24 | - pip install Sphinx==2.4.4 25 | - git clone https://github.com/tabatkins/bikeshed.git 26 | - pip install --editable $PWD/bikeshed 27 | - bikeshed update 28 | 29 | script: 30 | - ./interpreter/meta/travis/build-test.sh 31 | - bash ./document/travis-deploy.sh 32 | 33 | os: linux 34 | 35 | env: 36 | global: 37 | - ENCRYPTION_LABEL: "2cede2ad97c0" 38 | - COMMIT_AUTHOR_EMAIL: "noreply@webassembly.org" 39 | -------------------------------------------------------------------------------- /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/bulk-memory-operations.svg?branch=master)](https://travis-ci.org/WebAssembly/bulk-memory-operations) 2 | 3 | # Bulk Memory Operations Proposal for WebAssembly 4 | 5 | This repository is a clone of github.com/WebAssembly/spec/. It is meant for 6 | discussion, prototype specification and implementation of a proposal to add 7 | bulk memory operations (e.g. instructions with behavior similar to `memmove` 8 | and `memset`) to WebAssembly. 9 | 10 | See the [overview](proposals/bulk-memory-operations/Overview.md) for a summary of the 11 | proposal. 12 | 13 | Original README from upstream repository follows... 14 | 15 | # spec 16 | 17 | This repository holds the sources for the WebAssembly draft specification 18 | (to seed a future 19 | [WebAssembly Working Group](https://lists.w3.org/Archives/Public/public-new-work/2017Jun/0005.html)), 20 | a reference implementation, and the official testsuite. 21 | 22 | A formatted version of the spec is available here: 23 | [webassembly.github.io/spec](https://webassembly.github.io/spec/), 24 | 25 | Participation is welcome. Discussions about new features, significant semantic 26 | changes, or any specification change likely to generate substantial discussion 27 | should take place in 28 | [the WebAssembly design repository](https://github.com/WebAssembly/design) 29 | first, so that this spec repository can remain focused. And please follow the 30 | [guidelines for contributing](Contributing.md). 31 | 32 | # citing 33 | 34 | For citing WebAssembly in LaTeX, use [this bibtex file](wasm-specs.bib). 35 | -------------------------------------------------------------------------------- /deploy_key.enc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WebAssembly/bulk-memory-operations/dcaa1b6791401c29b67e8cd7929ec80949f1f849/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 | 25 | ## Step by step guide to building the spec 26 | 27 | ### Prerequisites 28 | 29 | You will need `python3.7`, and `pip`. `pip` should come with Python, if not follow [these installation instructions for `pip`](https://pip.pypa.io/en/stable/installing/), or check your system package manager for `pip3`. 30 | 31 | > Important: you will need the version of pip that works with `python3.7`. 32 | 33 | 34 | Use something like [`pipenv`](https://pipenv.pypa.io/) to keep your system installation of Python clean. 35 | 36 | ``` 37 | pip install pipenv 38 | pipenv --python 3.7 39 | pipenv shell 40 | ``` 41 | 42 | Install Python dependencies: 43 | 44 | ``` 45 | pip install Sphinx==2.4.4 46 | ``` 47 | 48 | ### Checking out the repository 49 | 50 | Make sure this repository was cloned with `--recursive`: 51 | 52 | ``` 53 | git clone --recursive https://github.com/WebAssembly/spec 54 | ``` 55 | 56 | If you have already cloned but without `--recursive`, you can delete and re-clone, or `cd` into `spec` and run: 57 | 58 | ``` 59 | git submodule update --init --recursive 60 | ``` 61 | 62 | The rest of these instructions assume you are in the directory where is README is: 63 | 64 | ``` 65 | cd spec/document 66 | ``` 67 | 68 | ### Building the multi-page HTML document 69 | 70 | You can now build the [multi-page html document](https://webassembly.github.io/spec/core/): 71 | 72 | ``` 73 | make -C core html 74 | ``` 75 | 76 | ### Building the single-page HTML document 77 | 78 | To build the [single-page W3C version](https://webassembly.github.io/spec/core/bikeshed/), there are more dependencies to install. First, get [Bikeshed](https://github.com/tabatkins/bikeshed): 79 | 80 | ``` 81 | # cd back to root of git directory 82 | git clone https://github.com/tabatkins/bikeshed.git 83 | pip install --editable bikeshed 84 | bikeshed update 85 | ``` 86 | 87 | You will also need `npm` and `yarn` for all the LaTeX goodness. `npm` might already be available on your system, you can also use something like [`nvm`](https://github.com/nvm-sh/nvm) to prevent messing with system packages: 88 | 89 | ``` 90 | npm install -g yarn 91 | cd document 92 | make -C core bikeshed 93 | ``` 94 | 95 | ### Building the PDF 96 | 97 | To build the [PDF](https://webassembly.github.io/spec/core/_download/WebAssembly.pdf), you will need `texlive-full`, install it using your system package manager: 98 | 99 | ``` 100 | apt install texlive-full 101 | make -C core pdf 102 | ``` 103 | 104 | ### Building the JavaScript Embedding API 105 | 106 | To build the [JavaScript Embedding API](https://webassembly.github.io/spec/js-api/index.html), you will need `bikeshed` as describe in the section [Building the single-page HTML document](#building-the-single-page-html-document): 107 | 108 | ``` 109 | make -C js-api 110 | ``` 111 | 112 | ### Building the Web Embedding API 113 | 114 | To build the [Web Embedding API](https://webassembly.github.io/spec/web-api/index.html), you will need `bikeshed` as describe in the section [Building the single-page HTML document](#building-the-single-page-html-document): 115 | 116 | ``` 117 | make -C web-api 118 | ``` 119 | -------------------------------------------------------------------------------- /document/core/.gitignore: -------------------------------------------------------------------------------- 1 | _build 2 | _static 3 | document/*.pyc 4 | -------------------------------------------------------------------------------- /document/core/LICENSE: -------------------------------------------------------------------------------- 1 | W3C SOFTWARE AND DOCUMENT NOTICE AND LICENSE 2 | 3 | This work is being provided by the copyright holders under the following 4 | license. 5 | 6 | 7 | LICENSE 8 | 9 | By obtaining and/or copying this work, you (the licensee) agree that you have 10 | read, understood, and will comply with the following terms and conditions. 11 | 12 | Permission to copy, modify, and distribute this work, with or without 13 | modification, for any purpose and without fee or royalty is hereby granted, 14 | provided that you include the following on ALL copies of the work or portions 15 | thereof, including modifications: 16 | 17 | * The full text of this NOTICE in a location viewable to users of the 18 | redistributed or derivative work. 19 | 20 | * Any pre-existing intellectual property disclaimers, notices, or terms and 21 | conditions. If none exist, the W3C Software and Document Short Notice 22 | (https://www.w3.org/Consortium/Legal/copyright-software-short-notice) should 23 | be included. 24 | 25 | * Notice of any changes or modifications, through a copyright statement on the 26 | new code or document such as "This software or document includes material 27 | copied from or derived from [title and URI of the W3C document]. Copyright © [YEAR] W3C® (MIT, ERCIM, Keio, Beihang)." 28 | 29 | 30 | DISCLAIMERS 31 | 32 | THIS WORK IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS MAKE NO REPRESENTATIONS 33 | OR WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO, WARRANTIES OF 34 | MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE 35 | SOFTWARE OR DOCUMENT WILL NOT INFRINGE ANY THIRD PARTY PATENTS, COPYRIGHTS, 36 | TRADEMARKS OR OTHER RIGHTS. 37 | 38 | COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL OR 39 | CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE SOFTWARE OR DOCUMENT. 40 | 41 | The name and trademarks of copyright holders may NOT be used in advertising or 42 | publicity pertaining to the work without specific, written prior permission. 43 | Title to copyright in this work will at all times remain with copyright 44 | holders. 45 | 46 | 47 | NOTES 48 | 49 | This version: 50 | http://www.w3.org/Consortium/Legal/2015/copyright-software-and-document 51 | -------------------------------------------------------------------------------- /document/core/README.md: -------------------------------------------------------------------------------- 1 | # WebAssembly Core Specification 2 | 3 | This is the official WebAssembly "language" specification. 4 | 5 | It uses [Sphinx](http://www.sphinx-doc.org/). To install that: 6 | ``` 7 | pip install sphinx 8 | ``` 9 | To make HTML (result in `_build/html`): 10 | ``` 11 | make html 12 | ``` 13 | To make PDF (result in `_build/latex`, requires LaTeX): 14 | ``` 15 | make pdf 16 | ``` 17 | To make all: 18 | ``` 19 | make all 20 | ``` 21 | Finally, to make all and update webassembly.github.io/spec with it: 22 | ``` 23 | make publish 24 | ``` 25 | Please make sure to only use that once a change has approval. 26 | -------------------------------------------------------------------------------- /document/core/appendix/index-types.rst: -------------------------------------------------------------------------------- 1 | .. index:: type 2 | .. _index-type: 3 | 4 | Index of Types 5 | -------------- 6 | 7 | ======================================== =========================================== =============================================================================== 8 | Category Constructor Binary Opcode 9 | ======================================== =========================================== =============================================================================== 10 | :ref:`Type index ` :math:`x` (positive number as |Bs32| or |Bu32|) 11 | :ref:`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| + bulk memory operations (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/bulk-memory-operations/dcaa1b6791401c29b67e8cd7929ec80949f1f849/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/.merlin: -------------------------------------------------------------------------------- 1 | S binary 2 | S exec 3 | S host 4 | S main 5 | S runtime 6 | S script 7 | S syntax 8 | S text 9 | S util 10 | S valid 11 | 12 | B _build/* 13 | -------------------------------------------------------------------------------- /interpreter/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 = -lexflags -ml -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 | TESTDIR = ../test/core 116 | TESTFILES = $(shell cd $(TESTDIR); ls *.wast) 117 | TESTS = $(TESTFILES:%.wast=%) 118 | 119 | .PHONY: test debugtest partest 120 | 121 | test: $(OPT) 122 | $(TESTDIR)/run.py --wasm `pwd`/$(OPT) $(if $(JS),--js '$(JS)',) 123 | debugtest: $(UNOPT) 124 | $(TESTDIR)/run.py --wasm `pwd`/$(UNOPT) $(if $(JS),--js '$(JS)',) 125 | 126 | test/%: $(OPT) 127 | $(TESTDIR)/run.py --wasm `pwd`/$(OPT) $(if $(JS),--js '$(JS)',) $(TESTDIR)/$(@F).wast 128 | debugtest/%: $(UNOPT) 129 | $(TESTDIR)/run.py --wasm `pwd`/$(UNOPT) $(if $(JS),--js '$(JS)',) $(TESTDIR)/$(@F).wast 130 | 131 | run/%: $(OPT) 132 | ./$(OPT) $(TESTDIR)/$(@F).wast 133 | debug/%: $(UNOPT) 134 | ./$(UNOPT) $(TESTDIR)/$(@F).wast 135 | 136 | partest: $(TESTS:%=quiettest/%) 137 | @echo All tests passed. 138 | 139 | quiettest/%: $(OPT) 140 | @ ( \ 141 | $(TESTDIR)/run.py 2>$(@F).out --wasm `pwd`/$(OPT) $(if $(JS),--js '$(JS)',) $(@F:%=$(TESTDIR)/%.wast) && \ 142 | rm $(@F).out \ 143 | ) || \ 144 | cat $(@F).out || rm $(@F).out || exit 1 145 | 146 | 147 | # Miscellaneous targets 148 | 149 | .PHONY: clean 150 | 151 | $(ZIP): $(WINMAKE) 152 | git archive --format=zip --prefix=$(NAME)/ -o $@ HEAD 153 | 154 | clean: 155 | rm -rf _build/jslib $(LIB).mlpack _tags 156 | $(OCB) -clean 157 | 158 | 159 | # Opam support 160 | 161 | .PHONY: check install uninstall 162 | 163 | check: 164 | # Check that we can find all relevant libraries 165 | # when using ocamlfind 166 | ocamlfind query $(LIBS) 167 | 168 | install: _build/$(LIB).cmx _build/$(LIB).cmo 169 | ocamlfind install $(LIB) meta/findlib/META _build/$(LIB).o \ 170 | $(wildcard _build/$(LIB).cm*) \ 171 | $(wildcard $(DIRS:%=%/*.mli)) 172 | 173 | uninstall: 174 | ocamlfind remove $(LIB) 175 | -------------------------------------------------------------------------------- /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 significant bit, such that rounding still is correct. 32 | *) 33 | let convert_i64_s x = 34 | F32.of_float Int64.( 35 | if abs x < 0x10_0000_0000_0000L then to_float x else 36 | let r = if logand x 0xfffL = 0L then 0L else 1L in 37 | to_float (logor (shift_right x 12) r) *. 0x1p12 38 | ) 39 | 40 | let convert_i64_u x = 41 | F32.of_float Int64.( 42 | if I64.lt_u x 0x10_0000_0000_0000L then to_float x else 43 | let r = if logand x 0xfffL = 0L then 0L else 1L in 44 | to_float (logor (shift_right_logical x 12) r) *. 0x1p12 45 | ) 46 | 47 | let reinterpret_i32 = F32.of_bits 48 | -------------------------------------------------------------------------------- /interpreter/exec/f32_convert.mli: -------------------------------------------------------------------------------- 1 | (* WebAssembly-compatible type conversions to f32 implementation *) 2 | 3 | val demote_f64 : F64.t -> F32.t 4 | val convert_i32_s : I32.t -> F32.t 5 | val convert_i32_u : I32.t -> F32.t 6 | val convert_i64_s : I64.t -> F32.t 7 | val convert_i64_u : I64.t -> F32.t 8 | val reinterpret_i32 : I32.t -> F32.t 9 | -------------------------------------------------------------------------------- /interpreter/exec/f64.ml: -------------------------------------------------------------------------------- 1 | include 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) -. 1.0 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 trunc_sat_f32_s x = 46 | if F32.ne x x then 47 | 0l 48 | else 49 | let xf = F32.to_float x in 50 | if xf < Int32.(to_float min_int) then 51 | Int32.min_int 52 | else if xf >= -.Int32.(to_float min_int) then 53 | Int32.max_int 54 | else 55 | Int32.of_float xf 56 | 57 | let trunc_sat_f32_u x = 58 | if F32.ne x x then 59 | 0l 60 | else 61 | let xf = F32.to_float x in 62 | if xf <= -1.0 then 63 | 0l 64 | else if xf >= -.Int32.(to_float min_int) *. 2.0 then 65 | -1l 66 | else 67 | Int64.(to_int32 (of_float xf)) 68 | 69 | let trunc_sat_f64_s x = 70 | if F64.ne x x then 71 | 0l 72 | else 73 | let xf = F64.to_float x in 74 | if xf < Int32.(to_float min_int) then 75 | Int32.min_int 76 | else if xf >= -.Int32.(to_float min_int) then 77 | Int32.max_int 78 | else 79 | Int32.of_float xf 80 | 81 | let trunc_sat_f64_u x = 82 | if F64.ne x x then 83 | 0l 84 | else 85 | let xf = F64.to_float x in 86 | if xf <= -1.0 then 87 | 0l 88 | else if xf >= -.Int32.(to_float min_int) *. 2.0 then 89 | -1l 90 | else 91 | Int64.(to_int32 (of_float xf)) 92 | 93 | let reinterpret_f32 = F32.to_bits 94 | -------------------------------------------------------------------------------- /interpreter/exec/i32_convert.mli: -------------------------------------------------------------------------------- 1 | (* WebAssembly-compatible type conversions to i32 implementation *) 2 | 3 | val wrap_i64 : I64.t -> I32.t 4 | val trunc_f32_s : F32.t -> I32.t 5 | val trunc_f32_u : F32.t -> I32.t 6 | val trunc_f64_s : F64.t -> I32.t 7 | val trunc_f64_u : F64.t -> I32.t 8 | val trunc_sat_f32_s : F32.t -> I32.t 9 | val trunc_sat_f32_u : F32.t -> I32.t 10 | val trunc_sat_f64_s : F64.t -> I32.t 11 | val trunc_sat_f64_u : F64.t -> I32.t 12 | val reinterpret_f32 : F32.t -> I32.t 13 | -------------------------------------------------------------------------------- /interpreter/exec/i64.ml: -------------------------------------------------------------------------------- 1 | (* WebAssembly-compatible i64 implementation *) 2 | 3 | include 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 -. 0x1p63)) min_int) 26 | else 27 | Int64.of_float xf 28 | 29 | let trunc_f64_s x = 30 | if F64.ne x x then 31 | raise 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 -. 0x1p63)) min_int) 48 | else 49 | Int64.of_float xf 50 | 51 | let trunc_sat_f32_s x = 52 | if F32.ne x x then 53 | 0L 54 | else 55 | let xf = F32.to_float x in 56 | if xf < Int64.(to_float min_int) then 57 | Int64.min_int 58 | else if xf >= -.Int64.(to_float min_int) then 59 | Int64.max_int 60 | else 61 | Int64.of_float xf 62 | 63 | let trunc_sat_f32_u x = 64 | if F32.ne x x then 65 | 0L 66 | else 67 | let xf = F32.to_float x in 68 | if xf <= -1.0 then 69 | 0L 70 | else if xf >= -.Int64.(to_float min_int) *. 2.0 then 71 | -1L 72 | else if xf >= -.Int64.(to_float min_int) then 73 | Int64.(logxor (of_float (xf -. 9223372036854775808.0)) min_int) 74 | else 75 | Int64.of_float xf 76 | 77 | let trunc_sat_f64_s x = 78 | if F64.ne x x then 79 | 0L 80 | else 81 | let xf = F64.to_float x in 82 | if xf < Int64.(to_float min_int) then 83 | Int64.min_int 84 | else if xf >= -.Int64.(to_float min_int) then 85 | Int64.max_int 86 | else 87 | Int64.of_float xf 88 | 89 | let trunc_sat_f64_u x = 90 | if F64.ne x x then 91 | 0L 92 | else 93 | let xf = F64.to_float x in 94 | if xf <= -1.0 then 95 | 0L 96 | else if xf >= -.Int64.(to_float min_int) *. 2.0 then 97 | -1L 98 | else if xf >= -.Int64.(to_float min_int) then 99 | Int64.(logxor (of_float (xf -. 9223372036854775808.0)) min_int) 100 | else 101 | Int64.of_float xf 102 | 103 | let reinterpret_f64 = F64.to_bits 104 | -------------------------------------------------------------------------------- /interpreter/exec/i64_convert.mli: -------------------------------------------------------------------------------- 1 | (* WebAssembly-compatible type conversions to i64 implementation *) 2 | 3 | val extend_i32_s : I32.t -> I64.t 4 | val extend_i32_u : I32.t -> I64.t 5 | val trunc_f32_s : F32.t -> I64.t 6 | val trunc_f32_u : F32.t -> I64.t 7 | val trunc_f64_s : F64.t -> I64.t 8 | val trunc_f64_u : F64.t -> I64.t 9 | val trunc_sat_f32_s : F32.t -> I64.t 10 | val trunc_sat_f32_u : F32.t -> I64.t 11 | val trunc_sat_f64_s : F64.t -> I64.t 12 | val trunc_sat_f64_u : F64.t -> I64.t 13 | val reinterpret_f64 : F64.t -> I64.t 14 | -------------------------------------------------------------------------------- /interpreter/exec/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_i64", _ -> ExternFunc (func print (FuncType ([I64Type], []))) 38 | | "print_f32", _ -> ExternFunc (func print (FuncType ([F32Type], []))) 39 | | "print_f64", _ -> ExternFunc (func print (FuncType ([F64Type], []))) 40 | | "print_i32_f32", _ -> 41 | ExternFunc (func print (FuncType ([I32Type; F32Type], []))) 42 | | "print_f64_f64", _ -> 43 | ExternFunc (func print (FuncType ([F64Type; F64Type], []))) 44 | | "global_i32", _ -> ExternGlobal (global (GlobalType (I32Type, Immutable))) 45 | | "global_i64", _ -> ExternGlobal (global (GlobalType (I64Type, Immutable))) 46 | | "global_f32", _ -> ExternGlobal (global (GlobalType (F32Type, Immutable))) 47 | | "global_f64", _ -> ExternGlobal (global (GlobalType (F64Type, Immutable))) 48 | | "table", _ -> ExternTable table 49 | | "memory", _ -> ExternMemory memory 50 | | _ -> raise Not_found 51 | -------------------------------------------------------------------------------- /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 | elems : elem_inst list; 12 | datas : data_inst list; 13 | } 14 | 15 | and func_inst = module_inst ref Func.t 16 | and table_inst = Table.t 17 | and memory_inst = Memory.t 18 | and global_inst = Global.t 19 | and export_inst = Ast.name * extern 20 | and elem_inst = Table.elem list ref 21 | and data_inst = string ref 22 | 23 | and extern = 24 | | ExternFunc of func_inst 25 | | ExternTable of table_inst 26 | | ExternMemory of memory_inst 27 | | ExternGlobal of global_inst 28 | 29 | type Table.elem += FuncElem of func_inst 30 | 31 | 32 | (* Auxiliary functions *) 33 | 34 | let empty_module_inst = 35 | { types = []; funcs = []; tables = []; memories = []; globals = []; 36 | exports = []; elems = []; datas = [] } 37 | 38 | let extern_type_of = function 39 | | ExternFunc func -> ExternFuncType (Func.type_of func) 40 | | ExternTable tab -> ExternTableType (Table.type_of tab) 41 | | ExternMemory mem -> ExternMemoryType (Memory.type_of mem) 42 | | ExternGlobal glob -> ExternGlobalType (Global.type_of glob) 43 | 44 | let export inst name = 45 | try Some (List.assoc name inst.exports) with Not_found -> None 46 | -------------------------------------------------------------------------------- /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 | type count = int32 10 | 11 | type memory' = (int, int8_unsigned_elt, c_layout) Array1.t 12 | type memory = {mutable content : memory'; max : size option} 13 | type t = memory 14 | 15 | exception Type 16 | exception Bounds 17 | exception SizeOverflow 18 | exception SizeLimit 19 | exception OutOfMemory 20 | 21 | let page_size = 0x10000L (* 64 KiB *) 22 | 23 | let within_limits n = function 24 | | None -> true 25 | | Some max -> I32.le_u n max 26 | 27 | let create n = 28 | if I32.gt_u n 0x10000l then raise SizeOverflow else 29 | try 30 | let size = Int64.(mul (of_int32 n) page_size) in 31 | let mem = Array1_64.create Int8_unsigned C_layout size in 32 | Array1.fill mem 0; 33 | mem 34 | with Out_of_memory -> raise OutOfMemory 35 | 36 | let alloc (MemoryType {min; max}) = 37 | assert (within_limits min max); 38 | {content = create min; max} 39 | 40 | let bound mem = 41 | Array1_64.dim mem.content 42 | 43 | let size mem = 44 | Int64.(to_int32 (div (bound mem) page_size)) 45 | 46 | let type_of mem = 47 | MemoryType {min = size mem; max = mem.max} 48 | 49 | let grow mem delta = 50 | let old_size = size mem in 51 | let new_size = Int32.add old_size delta in 52 | if I32.gt_u old_size new_size then raise SizeOverflow else 53 | if not (within_limits new_size mem.max) then raise SizeLimit else 54 | let after = create new_size in 55 | let dim = Array1_64.dim mem.content in 56 | Array1.blit (Array1_64.sub mem.content 0L dim) (Array1_64.sub after 0L dim); 57 | mem.content <- after 58 | 59 | let load_byte mem a = 60 | try Array1_64.get mem.content a with Invalid_argument _ -> raise Bounds 61 | 62 | let store_byte mem a b = 63 | try Array1_64.set mem.content a b with Invalid_argument _ -> raise Bounds 64 | 65 | let load_bytes mem a n = 66 | let buf = Buffer.create n in 67 | for i = 0 to n - 1 do 68 | Buffer.add_char buf (Char.chr (load_byte mem Int64.(add a (of_int i)))) 69 | done; 70 | Buffer.contents buf 71 | 72 | let store_bytes mem a bs = 73 | for i = String.length bs - 1 downto 0 do 74 | store_byte mem Int64.(add a (of_int i)) (Char.code bs.[i]) 75 | done 76 | 77 | let effective_address a o = 78 | let ea = Int64.(add a (of_int32 o)) in 79 | if I64.lt_u ea a then raise Bounds; 80 | ea 81 | 82 | let loadn mem a o n = 83 | assert (n > 0 && n <= 8); 84 | let rec loop a n = 85 | if n = 0 then 0L else begin 86 | let x = Int64.(shift_left (loop (add a 1L) (n - 1)) 8) in 87 | Int64.logor (Int64.of_int (load_byte mem a)) x 88 | end 89 | in loop (effective_address a o) n 90 | 91 | let storen mem a o n x = 92 | assert (n > 0 && n <= 8); 93 | let rec loop a n x = 94 | if n > 0 then begin 95 | Int64.(loop (add a 1L) (n - 1) (shift_right x 8)); 96 | store_byte mem a (Int64.to_int x land 0xff) 97 | end 98 | in loop (effective_address a o) n x 99 | 100 | let load_value mem a o t = 101 | let n = loadn mem a o (Types.size t) in 102 | match t with 103 | | I32Type -> I32 (Int64.to_int32 n) 104 | | I64Type -> I64 n 105 | | F32Type -> F32 (F32.of_bits (Int64.to_int32 n)) 106 | | F64Type -> F64 (F64.of_bits n) 107 | 108 | let store_value mem a o v = 109 | let x = 110 | match v with 111 | | I32 x -> Int64.of_int32 x 112 | | I64 x -> x 113 | | F32 x -> Int64.of_int32 (F32.to_bits x) 114 | | F64 x -> F64.to_bits x 115 | in storen mem a o (Types.size (Values.type_of v)) x 116 | 117 | let extend x n = function 118 | | ZX -> x 119 | | SX -> let sh = 64 - 8 * n in Int64.(shift_right (shift_left x sh) sh) 120 | 121 | let load_packed sz ext mem a o t = 122 | assert (packed_size sz <= Types.size t); 123 | let n = packed_size sz in 124 | let x = extend (loadn mem a o n) n ext in 125 | match t with 126 | | I32Type -> I32 (Int64.to_int32 x) 127 | | I64Type -> I64 x 128 | | _ -> raise Type 129 | 130 | let store_packed sz mem a o v = 131 | assert (packed_size sz <= Types.size (Values.type_of v)); 132 | let n = packed_size sz in 133 | let x = 134 | match v with 135 | | I32 x -> Int64.of_int32 x 136 | | I64 x -> x 137 | | _ -> raise Type 138 | in storen mem a o n x 139 | -------------------------------------------------------------------------------- /interpreter/runtime/memory.mli: -------------------------------------------------------------------------------- 1 | open Types 2 | open Values 3 | 4 | type memory 5 | type t = memory 6 | 7 | type size = int32 (* number of pages *) 8 | type address = int64 9 | type offset = int32 10 | type count = int32 11 | 12 | exception Type 13 | exception Bounds 14 | exception SizeOverflow 15 | exception SizeLimit 16 | exception OutOfMemory 17 | 18 | val page_size : int64 19 | 20 | val alloc : memory_type -> memory (* raises SizeOverflow, OutOfMemory *) 21 | val type_of : memory -> memory_type 22 | val size : memory -> size 23 | val bound : memory -> address 24 | val grow : memory -> size -> unit 25 | (* raises SizeLimit, SizeOverflow, OutOfMemory *) 26 | 27 | val load_byte : memory -> address -> int (* raises Bounds *) 28 | val store_byte : memory -> address -> int -> unit (* raises Bounds *) 29 | val load_bytes : memory -> address -> int -> string (* raises Bounds *) 30 | val store_bytes : memory -> address -> string -> unit (* raises Bounds *) 31 | 32 | val load_value : 33 | memory -> address -> offset -> value_type -> value (* raises Bounds *) 34 | val store_value : 35 | memory -> address -> offset -> value -> unit (* raises Bounds *) 36 | val load_packed : 37 | pack_size -> extension -> memory -> address -> offset -> value_type -> value 38 | (* raises Type, Bounds *) 39 | val store_packed : 40 | pack_size -> memory -> address -> offset -> value -> unit 41 | (* raises Type, Bounds *) 42 | -------------------------------------------------------------------------------- /interpreter/runtime/table.ml: -------------------------------------------------------------------------------- 1 | open Types 2 | 3 | type size = int32 4 | type index = int32 5 | type count = int32 6 | 7 | type elem = .. 8 | type elem += Uninitialized 9 | 10 | type table' = elem array 11 | type table = 12 | {mutable content : table'; max : size option; elem_type : elem_type} 13 | type t = table 14 | 15 | exception Bounds 16 | exception SizeOverflow 17 | exception SizeLimit 18 | 19 | let within_limits size = function 20 | | None -> true 21 | | Some max -> I32.le_u size max 22 | 23 | let create size = 24 | try Lib.Array32.make size Uninitialized 25 | with Invalid_argument _ -> raise Out_of_memory 26 | 27 | let alloc (TableType ({min; max}, elem_type)) = 28 | assert (within_limits min max); 29 | {content = create min; max; elem_type} 30 | 31 | let size tab = 32 | Lib.Array32.length tab.content 33 | 34 | let type_of tab = 35 | TableType ({min = size tab; max = tab.max}, tab.elem_type) 36 | 37 | let grow tab delta = 38 | let old_size = size tab in 39 | let new_size = Int32.add old_size delta in 40 | if I32.gt_u old_size new_size then raise SizeOverflow else 41 | if not (within_limits new_size tab.max) then raise SizeLimit else 42 | let after = create new_size in 43 | Array.blit tab.content 0 after 0 (Array.length tab.content); 44 | tab.content <- after 45 | 46 | let load tab i = 47 | try Lib.Array32.get tab.content i with Invalid_argument _ -> raise Bounds 48 | 49 | let store tab i v = 50 | try Lib.Array32.set tab.content i v with Invalid_argument _ -> raise Bounds 51 | 52 | let init tab es d s n = 53 | let rec loop es d s n = 54 | match s, n, es with 55 | | s, 0l, _ -> () 56 | | 0l, n, e::es' -> 57 | store tab d e; 58 | loop es' (Int32.add d 1l) 0l (Int32.sub n 1l) 59 | | s, n, _::es' -> loop es' d (Int32.sub s 1l) n 60 | | _ -> raise Bounds 61 | in loop es d s n 62 | 63 | let copy tab d s n = 64 | let rec loop d s n dx = 65 | if I32.gt_u n 0l then begin 66 | store tab d (load tab s); 67 | loop (Int32.add d dx) (Int32.add s dx) (Int32.sub n 1l) dx 68 | end 69 | in (if s < d then 70 | loop Int32.(add d (sub n 1l)) Int32.(add s (sub n 1l)) n (-1l) 71 | else 72 | loop d s n 1l) 73 | -------------------------------------------------------------------------------- /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 | type count = int32 9 | 10 | type elem = .. 11 | type elem += Uninitialized 12 | 13 | exception Bounds 14 | exception SizeOverflow 15 | exception SizeLimit 16 | 17 | val alloc : table_type -> table 18 | val type_of : table -> table_type 19 | val size : table -> size 20 | val grow : table -> size -> unit (* raises SizeOverflow, SizeLimit *) 21 | 22 | val load : table -> index -> elem (* raises Bounds *) 23 | val store : table -> index -> elem -> unit (* raises Bounds *) 24 | 25 | val init : 26 | table -> elem list -> index -> index -> count -> unit (* raises Bounds *) 27 | val copy : table -> index -> index -> count -> unit (* raises Bounds *) 28 | -------------------------------------------------------------------------------- /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/free.ml: -------------------------------------------------------------------------------- 1 | open Source 2 | open Ast 3 | 4 | module Set = Set.Make(Int32) 5 | 6 | type t = 7 | { 8 | types : Set.t; 9 | globals : Set.t; 10 | tables : Set.t; 11 | memories : Set.t; 12 | funcs : Set.t; 13 | elems : Set.t; 14 | datas : Set.t; 15 | locals : Set.t; 16 | labels : Set.t; 17 | } 18 | 19 | let empty : t = 20 | { 21 | types = Set.empty; 22 | globals = Set.empty; 23 | tables = Set.empty; 24 | memories = Set.empty; 25 | funcs = Set.empty; 26 | elems = Set.empty; 27 | datas = Set.empty; 28 | locals = Set.empty; 29 | labels = Set.empty; 30 | } 31 | 32 | let union (s1 : t) (s2 : t) : t = 33 | { 34 | types = Set.union s1.types s2.types; 35 | globals = Set.union s1.globals s2.globals; 36 | tables = Set.union s1.tables s2.tables; 37 | memories = Set.union s1.memories s2.memories; 38 | funcs = Set.union s1.funcs s2.funcs; 39 | elems = Set.union s1.elems s2.elems; 40 | datas = Set.union s1.datas s2.datas; 41 | locals = Set.union s1.locals s2.locals; 42 | labels = Set.union s1.labels s2.labels; 43 | } 44 | 45 | let types s = {empty with types = s} 46 | let globals s = {empty with globals = s} 47 | let tables s = {empty with tables = s} 48 | let memories s = {empty with memories = s} 49 | let funcs s = {empty with funcs = s} 50 | let elems s = {empty with elems = s} 51 | let datas s = {empty with datas = s} 52 | let locals s = {empty with locals = s} 53 | let labels s = {empty with labels = s} 54 | 55 | let var x = Set.singleton x.it 56 | let zero = Set.singleton 0l 57 | let shift s = Set.map (Int32.add (-1l)) (Set.remove 0l s) 58 | 59 | let (++) = union 60 | let list free xs = List.fold_left union empty (List.map free xs) 61 | 62 | let rec instr (e : instr) = 63 | match e.it with 64 | | Unreachable | Nop | Drop | Select -> empty 65 | | Const _ | Test _ | Compare _ | Unary _ | Binary _ | Convert _ -> empty 66 | | Block (_, es) | Loop (_, es) -> block es 67 | | If (_, es1, es2) -> block es1 ++ block es2 68 | | Br x | BrIf x -> labels (var x) 69 | | BrTable (xs, x) -> list (fun x -> labels (var x)) (x::xs) 70 | | Return -> empty 71 | | Call x -> funcs (var x) 72 | | CallIndirect x -> types (var x) ++ tables zero 73 | | LocalGet x | LocalSet x | LocalTee x -> locals (var x) 74 | | GlobalGet x | GlobalSet x -> globals (var x) 75 | | Load _ | Store _ | MemorySize | MemoryGrow | MemoryCopy | MemoryFill -> 76 | memories zero 77 | | MemoryInit x -> memories zero ++ datas (var x) 78 | | TableCopy -> tables zero 79 | | TableInit x -> tables zero ++ elems (var x) 80 | | DataDrop x -> datas (var x) 81 | | ElemDrop x -> elems (var x) 82 | 83 | and block (es : instr list) = 84 | let free = list instr es in {free with labels = shift free.labels} 85 | 86 | let const (c : const) = block c.it 87 | 88 | let global (g : global) = const g.it.ginit 89 | let func (f : func) = {(block f.it.body) with locals = Set.empty} 90 | let table (t : table) = empty 91 | let memory (m : memory) = empty 92 | 93 | let segment_mode f (m : segment_mode) = 94 | match m.it with 95 | | Passive -> empty 96 | | Active {index; offset} -> f (var index) ++ const offset 97 | 98 | let elem_expr (e : elem_expr) = 99 | match e.it with 100 | | RefNull -> empty 101 | | RefFunc x -> funcs (var x) 102 | 103 | let elem (s : elem_segment) = 104 | list elem_expr s.it.einit ++ segment_mode tables s.it.emode 105 | 106 | let data (s : data_segment) = 107 | segment_mode memories s.it.dmode 108 | 109 | let type_ (t : type_) = empty 110 | 111 | let export_desc (d : export_desc) = 112 | match d.it with 113 | | FuncExport x -> funcs (var x) 114 | | TableExport x -> tables (var x) 115 | | MemoryExport x -> memories (var x) 116 | | GlobalExport x -> globals (var x) 117 | 118 | let import_desc (d : import_desc) = 119 | match d.it with 120 | | FuncImport x -> types (var x) 121 | | TableImport tt -> empty 122 | | MemoryImport mt -> empty 123 | | GlobalImport gt -> empty 124 | 125 | let export (e : export) = export_desc e.it.edesc 126 | let import (i : import) = import_desc i.it.idesc 127 | 128 | let start (s : var option) = 129 | funcs (Lib.Option.get (Lib.Option.map var s) Set.empty) 130 | 131 | let module_ (m : module_) = 132 | list type_ m.it.types ++ 133 | list global m.it.globals ++ 134 | list table m.it.tables ++ 135 | list memory m.it.memories ++ 136 | list func m.it.funcs ++ 137 | start m.it.start ++ 138 | list elem m.it.elems ++ 139 | list data m.it.datas ++ 140 | list import m.it.imports ++ 141 | list export m.it.exports 142 | -------------------------------------------------------------------------------- /interpreter/syntax/free.mli: -------------------------------------------------------------------------------- 1 | module Set : Set.S with type elt = int32 2 | 3 | type t = 4 | { 5 | types : Set.t; 6 | globals : Set.t; 7 | tables : Set.t; 8 | memories : Set.t; 9 | funcs : Set.t; 10 | elems : Set.t; 11 | datas : Set.t; 12 | locals : Set.t; 13 | labels : Set.t; 14 | } 15 | 16 | val empty : t 17 | val union : t -> t -> t 18 | 19 | val instr : Ast.instr -> t 20 | val block : Ast.instr list -> t 21 | val const : Ast.const -> t 22 | 23 | val type_ : Ast.type_ -> t 24 | val global : Ast.global -> t 25 | val func : Ast.func -> t 26 | val table : Ast.table -> t 27 | val memory : Ast.memory -> t 28 | val elem : Ast.elem_segment -> t 29 | val data : Ast.data_segment -> t 30 | val export : Ast.export -> t 31 | val import : Ast.import -> t 32 | val start : Ast.var option -> t 33 | 34 | val module_ : Ast.module_ -> t 35 | -------------------------------------------------------------------------------- /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 | type pack_size = Pack8 | Pack16 | Pack32 20 | type extension = SX | ZX 21 | 22 | 23 | (* Attributes *) 24 | 25 | let size = function 26 | | I32Type | F32Type -> 4 27 | | I64Type | F64Type -> 8 28 | 29 | let packed_size = function 30 | | Pack8 -> 1 31 | | Pack16 -> 2 32 | | Pack32 -> 4 33 | 34 | 35 | (* Subtyping *) 36 | 37 | let match_limits lim1 lim2 = 38 | I32.ge_u lim1.min lim2.min && 39 | match lim1.max, lim2.max with 40 | | _, None -> true 41 | | None, Some _ -> false 42 | | Some i, Some j -> I32.le_u i j 43 | 44 | let match_func_type ft1 ft2 = 45 | ft1 = ft2 46 | 47 | let match_table_type (TableType (lim1, et1)) (TableType (lim2, et2)) = 48 | et1 = et2 && match_limits lim1 lim2 49 | 50 | let match_memory_type (MemoryType lim1) (MemoryType lim2) = 51 | match_limits lim1 lim2 52 | 53 | let match_global_type gt1 gt2 = 54 | gt1 = gt2 55 | 56 | let match_extern_type et1 et2 = 57 | match et1, et2 with 58 | | ExternFuncType ft1, ExternFuncType ft2 -> match_func_type ft1 ft2 59 | | ExternTableType tt1, ExternTableType tt2 -> match_table_type tt1 tt2 60 | | ExternMemoryType mt1, ExternMemoryType mt2 -> match_memory_type mt1 mt2 61 | | ExternGlobalType gt1, ExternGlobalType gt2 -> match_global_type gt1 gt2 62 | | _, _ -> false 63 | 64 | 65 | (* Filters *) 66 | 67 | let funcs = 68 | Lib.List.map_filter (function ExternFuncType t -> Some t | _ -> None) 69 | let tables = 70 | Lib.List.map_filter (function ExternTableType t -> Some t | _ -> None) 71 | let memories = 72 | Lib.List.map_filter (function ExternMemoryType t -> Some t | _ -> None) 73 | let globals = 74 | Lib.List.map_filter (function ExternGlobalType t -> Some t | _ -> None) 75 | 76 | 77 | (* String conversion *) 78 | 79 | let string_of_value_type = function 80 | | I32Type -> "i32" 81 | | I64Type -> "i64" 82 | | F32Type -> "f32" 83 | | F64Type -> "f64" 84 | 85 | let string_of_value_types = function 86 | | [t] -> string_of_value_type t 87 | | ts -> "[" ^ String.concat " " (List.map string_of_value_type ts) ^ "]" 88 | 89 | let string_of_elem_type = function 90 | | FuncRefType -> "funcref" 91 | 92 | let string_of_limits {min; max} = 93 | I32.to_string_u min ^ 94 | (match max with None -> "" | Some n -> " " ^ I32.to_string_u n) 95 | 96 | let string_of_memory_type = function 97 | | MemoryType lim -> string_of_limits lim 98 | 99 | let string_of_table_type = function 100 | | TableType (lim, t) -> string_of_limits lim ^ " " ^ string_of_elem_type t 101 | 102 | let string_of_global_type = function 103 | | GlobalType (t, Immutable) -> string_of_value_type t 104 | | GlobalType (t, Mutable) -> "(mut " ^ string_of_value_type t ^ ")" 105 | 106 | let string_of_stack_type ts = 107 | "[" ^ String.concat " " (List.map string_of_value_type ts) ^ "]" 108 | 109 | let string_of_func_type (FuncType (ins, out)) = 110 | string_of_stack_type ins ^ " -> " ^ string_of_stack_type out 111 | 112 | let string_of_extern_type = function 113 | | ExternFuncType ft -> "func " ^ string_of_func_type ft 114 | | ExternTableType tt -> "table " ^ string_of_table_type tt 115 | | ExternMemoryType mt -> "memory " ^ string_of_memory_type mt 116 | | ExternGlobalType gt -> "global " ^ string_of_global_type gt 117 | -------------------------------------------------------------------------------- /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 | val mapi : (int32 -> 'a -> 'b) -> 'a list -> 'b list 35 | end 36 | 37 | module Array32 : 38 | sig 39 | val make : int32 -> 'a -> 'a array 40 | val length : 'a array -> int32 41 | val get : 'a array -> int32 -> 'a 42 | val set : 'a array -> int32 -> 'a -> unit 43 | val blit : 'a array -> int32 -> 'a array -> int32 -> int32 -> unit 44 | end 45 | 46 | module Bigarray : 47 | sig 48 | open Bigarray 49 | 50 | module Array1_64 : 51 | sig 52 | val create : ('a, 'b) kind -> 'c layout -> int64 -> ('a, 'b, 'c) Array1.t 53 | val dim : ('a, 'b, 'c) Array1.t -> int64 54 | val get : ('a, 'b, 'c) Array1.t -> int64 -> 'a 55 | val set : ('a, 'b, 'c) Array1.t -> int64 -> 'a -> unit 56 | val sub : ('a, 'b, 'c) Array1.t -> int64 -> int64 -> ('a, 'b, 'c) Array1.t 57 | end 58 | end 59 | 60 | module Option : 61 | sig 62 | val get : 'a option -> 'a -> 'a 63 | val map : ('a -> 'b) -> 'a option -> 'b option 64 | val app : ('a -> unit) -> 'a option -> unit 65 | end 66 | 67 | module Int : 68 | sig 69 | val log2 : int -> int 70 | val is_power_of_two : int -> bool 71 | end 72 | 73 | module String : 74 | sig 75 | val implode : char list -> string 76 | val explode : string -> char list 77 | val split : string -> char -> string list 78 | val breakup : string -> int -> string list 79 | val find_from_opt : (char -> bool) -> string -> int -> int option 80 | end 81 | -------------------------------------------------------------------------------- /interpreter/util/sexpr.ml: -------------------------------------------------------------------------------- 1 | type sexpr = Atom of string | Node of string * sexpr list 2 | 3 | type rope = Leaf of string | Concat of rope list 4 | let (^+) s r = Concat [Leaf s; r] 5 | let (+^) r s = Concat [r; Leaf s] 6 | 7 | let rec iter f = function 8 | | Leaf s -> f s 9 | | Concat rs -> List.iter (iter f) rs 10 | 11 | let rec concat = function 12 | | Leaf s -> s 13 | | Concat rs -> String.concat "" (List.map concat rs) 14 | 15 | let rec pp off width = function 16 | | Atom s -> String.length s, Leaf s 17 | | Node (s, xs) -> 18 | let lens, rs = List.split (List.map (pp (off + 2) width) xs) in 19 | let len = String.length s + List.length rs + List.fold_left (+) 2 lens in 20 | let sep, fin = 21 | if off + len <= width then " ", "" 22 | else let indent = String.make off ' ' in "\n " ^ indent, "\n" ^ indent 23 | in len, "(" ^+ s ^+ Concat (List.map (fun r -> sep ^+ r) rs) +^ fin +^ ")" 24 | 25 | let output oc width x = 26 | iter (output_string oc) (snd (pp 0 width x)); 27 | output_string oc "\n"; 28 | flush oc 29 | 30 | let print = output stdout 31 | 32 | let to_string width x = concat (snd (pp 0 width x)) ^ "\n" 33 | -------------------------------------------------------------------------------- /interpreter/util/sexpr.mli: -------------------------------------------------------------------------------- 1 | type sexpr = Atom of string | Node of string * sexpr list 2 | 3 | val output : out_channel -> int -> sexpr -> unit 4 | val print : int -> sexpr -> unit 5 | val to_string : int -> sexpr -> string 6 | -------------------------------------------------------------------------------- /interpreter/util/source.ml: -------------------------------------------------------------------------------- 1 | type pos = {file : string; line : int; column : int} 2 | type region = {left : pos; right : pos} 3 | type 'a phrase = {at : region; it : 'a} 4 | 5 | let (@@) x region = {it = x; at = region} 6 | let at region x = x @@ region 7 | 8 | 9 | (* Positions and regions *) 10 | 11 | let no_pos = {file = ""; line = 0; column = 0} 12 | let no_region = {left = no_pos; right = no_pos} 13 | 14 | let string_of_pos pos = 15 | if pos.line = -1 then 16 | Printf.sprintf "0x%x" pos.column 17 | else 18 | string_of_int pos.line ^ "." ^ string_of_int (pos.column + 1) 19 | 20 | let string_of_region r = 21 | r.left.file ^ ":" ^ string_of_pos r.left ^ 22 | (if r.right = r.left then "" else "-" ^ string_of_pos r.right) 23 | -------------------------------------------------------------------------------- /interpreter/util/source.mli: -------------------------------------------------------------------------------- 1 | type pos = {file : string; line : int; column : int} 2 | type region = {left : pos; right : pos} 3 | type 'a phrase = {at : region; it : 'a} 4 | 5 | val no_pos : pos 6 | val no_region : region 7 | 8 | val string_of_pos : pos -> string 9 | val string_of_region : region -> string 10 | 11 | val (@@) : 'a -> region -> 'a phrase 12 | val at : region -> 'a -> 'a phrase 13 | -------------------------------------------------------------------------------- /interpreter/valid/valid.mli: -------------------------------------------------------------------------------- 1 | exception Invalid of Source.region * string 2 | 3 | val check_module : Ast.module_ -> unit (* raises Invalid *) 4 | -------------------------------------------------------------------------------- /papers/LICENSE: -------------------------------------------------------------------------------- 1 | This work is licensed under the Creative Commons Attribution 4.0 International License. To view a copy of this license, visit http://creativecommons.org/licenses/by/4.0/ or send a letter to Creative Commons, PO Box 1866, Mountain View, CA 94042, USA. 2 | -------------------------------------------------------------------------------- /papers/README.md: -------------------------------------------------------------------------------- 1 | * [Bringing the Web up to Speed with WebAssembly](pldi2017.pdf) 2 | 3 | Andreas Haas, Andreas Rossberg, Derek Schuff, Ben Titzer, Dan Gohman, Luke Wagner, Alon Zakai, JF Bastien, Michael Holman 4 | 5 | ACM-SIGPLAN Conference on Programming Language Design and Implementation (PLDI 2017) 6 | 7 | *Describes the WebAssembly design, its formalisation, and initial implementations.* 8 | 9 | * [Weakening WebAssembly](oopsla2019.pdf) 10 | 11 | Conrad Watt, Andreas Rossberg, Jean Pichon-Pharabod 12 | 13 | ACM-SIGPLAN Conference on Object-Oriented Programming, Systems, Language and Architectures (OOSPLA 2019) 14 | 15 | *Describes and formalises the extension of WebAssembly with threads and a suitable memory model.* 16 | -------------------------------------------------------------------------------- /papers/oopsla2019.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WebAssembly/bulk-memory-operations/dcaa1b6791401c29b67e8cd7929ec80949f1f849/papers/oopsla2019.pdf -------------------------------------------------------------------------------- /papers/pldi2017.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WebAssembly/bulk-memory-operations/dcaa1b6791401c29b67e8cd7929ec80949f1f849/papers/pldi2017.pdf -------------------------------------------------------------------------------- /proposals/README.md: -------------------------------------------------------------------------------- 1 | # Proposals 2 | 3 | This directory contains overviews for post-MVP proposals that are included in this repository. 4 | -------------------------------------------------------------------------------- /proposals/nontrapping-float-to-int-conversion/Overview.md: -------------------------------------------------------------------------------- 1 | # Non-trapping Float-to-int Conversions 2 | 3 | ## Introduction 4 | 5 | ### Motivation 6 | 7 | The primary motivations are: 8 | 9 | - LLVM’s float-to-int conversion has an undefined result, rather than undefined behavior, and it seems LLVM does speculate it under certain conditions. 10 | - For the SIMD proposal, it’s more SIMD-hardware-like if no SIMD operations trap. This proposal would establish a convention for saturating operations which SIMD could share, to avoid introducing trapping. 11 | 12 | This proposal is not motivated by performance data. 13 | 14 | It's plausible that LLVM could be changed, to have something like an "nsw" flag for the "fptosi" instruction, especially with some of the recent proposals to modify the poison concept in LLVM. However, no one is currently working on this. 15 | 16 | ### Background 17 | 18 | This issue has been discussed in several areas including here: 19 | 20 | https://github.com/WebAssembly/design/issues/986 21 | 22 | While performance concerns initially motivated the discussion, the performance 23 | effects were due to a particular implementation detail which has since been 24 | fixed. 25 | 26 | Since then, no real-world performance problems related to this issue have 27 | been reported. 28 | 29 | This topic was discussed at the CG-05 meeting: 30 | 31 | https://github.com/WebAssembly/meetings/pull/3 32 | 33 | and 34 | 35 | https://github.com/WebAssembly/meetings/blob/master/2017/CG-05.md#non-trapping-float-to-int 36 | 37 | which made decisions about which semantics to choose, and which encoding strategy. 38 | 39 | These decisions were captured in a design repo PR: 40 | 41 | https://github.com/WebAssembly/design/pull/1089 42 | 43 | At the CG-07-06 meeting it was decided that a full spec repo fork should be 44 | created to follow the new process for new features: 45 | 46 | https://github.com/WebAssembly/meetings/blob/master/2017/CG-07-06.md#float-to-int-conversion 47 | 48 | This led to the creation of the present repo: 49 | 50 | https://github.com/WebAssembly/nontrapping-float-to-int-conversions 51 | 52 | ### Design 53 | 54 | This proposal introduces 8 new instructions: 55 | 56 | - `i32.trunc_sat_f32_s` 57 | - `i32.trunc_sat_f32_u` 58 | - `i32.trunc_sat_f64_s` 59 | - `i32.trunc_sat_f64_u` 60 | - `i64.trunc_sat_f32_s` 61 | - `i64.trunc_sat_f32_u` 62 | - `i64.trunc_sat_f64_s` 63 | - `i64.trunc_sat_f64_u` 64 | 65 | The semantics are the same as the corresponding non-`_sat` instructions, except: 66 | - Instead of trapping on positive or negative overflow, they return the maximum 67 | or minimum integer value, respectively, and do not trap. (This behavior is 68 | also referred to as "saturating".) 69 | - Instead of trapping on NaN, they return 0 and do not trap. 70 | 71 | ### Encoding 72 | 73 | This proposal introduces a new prefix byte: 74 | 75 | | Prefix | Name | Description | 76 | | ------ | ------- | ----------- | 77 | | `0xfc` | misc | Miscellaneous operations :bowling: | 78 | 79 | which is intended to be used as a prefix for other future miscellaneous operations 80 | as well. 81 | 82 | The encodings for the new instructions use this new prefix and are as follows: 83 | 84 | | Name | Opcode | Immediate | Description | 85 | | ---- | ---- | ---- | ---- | 86 | | `i32.trunc_sat_f32_s` | `0xfc` `0x00` | | :bowling: saturating form of `i32.trunc_f32_s` | 87 | | `i32.trunc_sat_f32_u` | `0xfc` `0x01` | | :bowling: saturating form of `i32.trunc_f32_u` | 88 | | `i32.trunc_sat_f64_s` | `0xfc` `0x02` | | :bowling: saturating form of `i32.trunc_f64_s` | 89 | | `i32.trunc_sat_f64_u` | `0xfc` `0x03` | | :bowling: saturating form of `i32.trunc_f64_u` | 90 | | `i64.trunc_sat_f32_s` | `0xfc` `0x04` | | :bowling: saturating form of `i64.trunc_f32_s` | 91 | | `i64.trunc_sat_f32_u` | `0xfc` `0x05` | | :bowling: saturating form of `i64.trunc_f32_u` | 92 | | `i64.trunc_sat_f64_s` | `0xfc` `0x06` | | :bowling: saturating form of `i64.trunc_f64_s` | 93 | | `i64.trunc_sat_f64_u` | `0xfc` `0x07` | | :bowling: saturating form of `i64.trunc_f64_u` | 94 | -------------------------------------------------------------------------------- /proposals/sign-extension-ops/Overview.md: -------------------------------------------------------------------------------- 1 | # Sign-extension operators proposal for WebAssembly 2 | 3 | This page describes a proposal for the post-MVP 4 | [sign-extension operator feature][future sext]. 5 | 6 | This proposal adds five new integer instructions for sign-extending 8-bit, 7 | 16-bit, and 32-bit values. 8 | 9 | ## New Sign-extending Operators 10 | 11 | To support sign-extending, five new sign-extension operators are added: 12 | 13 | * `i32.extend8_s`: extend a signed 8-bit integer to a 32-bit integer 14 | * `i32.extend16_s`: extend a signed 16-bit integer to a 32-bit integer 15 | * `i64.extend8_s`: extend a signed 8-bit integer to a 64-bit integer 16 | * `i64.extend16_s`: extend a signed 16-bit integer to a 64-bit integer 17 | * `i64.extend32_s`: extend a signed 32-bit integer to a 64-bit integer 18 | 19 | Note that `i64.extend32_s` was not originally included when this proposal was 20 | discussed in the May 2017 CG meeting. The reason given was that 21 | the behavior matches `i64.extend_s/i32`. It was later discovered that this is 22 | not correct, as `i64.extend_s/i32` sign-extends an `i32` value to `i64`, 23 | whereas `i64.extend32_s` sign-extends an `i64` value to `i64`. The behavior 24 | of `i64.extend32_s` can be emulated with `i32.wrap/i64` followed by 25 | `i64.extend_s/i32`, but the same can be said of the sign-extending load 26 | operations. Therefore, `i64.extend32_s` has been added for consistency. 27 | 28 | ## [Spec Changes][spec] 29 | 30 | The [instruction syntax][] is modified as follows: 31 | 32 | ``` 33 | instr ::= ... | 34 | inn.extend8_s | inn.extend16_s | i64.extend32_s 35 | ``` 36 | 37 | The [instruction binary format][] is modified as follows: 38 | 39 | ``` 40 | instr ::= ... 41 | | 0xC0 => i32.extend8_s 42 | | 0xC1 => i32.extend16_s 43 | | 0xC2 => i64.extend8_s 44 | | 0xC3 => i64.extend16_s 45 | | 0xC4 => i64.extend32_s 46 | ``` 47 | 48 | [future sext]: https://github.com/WebAssembly/design/blob/master/FutureFeatures.md#additional-integer-operators 49 | [instruction syntax]: https://webassembly.github.io/spec/syntax/instructions.html 50 | [instruction binary format]: https://webassembly.github.io/spec/binary/instructions.html 51 | [spec]: https://webassembly.github.io/sign-extension-ops/ 52 | -------------------------------------------------------------------------------- /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 | 122 | (assert_malformed 123 | (module binary 124 | "\00asm" "\01\00\00\00" 125 | "\05\03\01\00\01" ;; memory section 126 | "\0c\01\02" ;; data count section (2 segments) 127 | "\0b\06\01\00\41\00\0b\00" ;; data section (1 segment) 128 | ) 129 | "data count and data section have inconsistent lengths" 130 | ) 131 | -------------------------------------------------------------------------------- /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/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/README.md: -------------------------------------------------------------------------------- 1 | This directory contains tests specific to the [JavaScript API] to WebAssembly. 2 | 3 | These tests exist in the [web-platform-tests project], and are included here 4 | primarily to simplify sharing them between JavaScript engine implementers. 5 | 6 | These tests can be run in a pure JavaScript environment, that is, a JS shell 7 | (like V8 or spidermonkey's shells), as well as in the browser. 8 | 9 | The tests use the [testharness.js] library and the [multi-global tests] setup 10 | for smooth integrations with the web-platform-tests project and the existing 11 | test infrastructure in implementations. 12 | 13 | ## Metadata 14 | 15 | All tests must have the `.any.js` extension. 16 | 17 | In order to be run in the JavaScript shell, a metadata comment to add the 18 | `jsshell` scope to the default set of scopes (`window` and `dedicatedworker`) 19 | is required at the start of the file: 20 | 21 | ```js 22 | // META: global=jsshell 23 | ``` 24 | 25 | Additional JavaScript files can be imported with 26 | 27 | ```js 28 | // META: script=helper-file.js 29 | 30 | ``` 31 | 32 | ## Harness 33 | 34 | A single test file contains multiple subtests, which are created with one of 35 | the following functions: 36 | 37 | - For synchronous tests: `test(function, name)` runs the function immediately. 38 | The test fails if any assertion fails or an exception is thrown while calling 39 | the function. 40 | - For asynchronous tests: `promise_test(function, name)` where `function` 41 | returns a `Promise`. The test fails if the returned `Promise` rejects. 42 | 43 | All assertions must be in one of those subtests. 44 | 45 | A number of assertion functions are provided, e.g.: 46 | 47 | - `assert_equals(x, y)`; 48 | - `assert_not_equals(x, y)`; 49 | - `assert_true(x)`; 50 | - `assert_false(x)`; 51 | - `assert_unreached()`; 52 | - `assert_throws(error, function)`: checks if `function` throws an appropriate 53 | exception (a typical value for `error` would be `new TypeError()`); 54 | - `assert_class_string(object, class_name)`: checks if the result of calling 55 | `Object.prototype.toString` on `object` uses `class_name`; 56 | - `promise_rejects`: `assert_throws`, but for `Promise`s. 57 | 58 | All the above functions also take an optional trailing description argument. 59 | 60 | Non-trivial code that runs before the subtests should be put in the function 61 | argument to `setup(function)`, to ensure any exceptions are handled gracefully. 62 | 63 | Finally, the harness also exposes a `format_value` function that provides a 64 | helpful stringification of its argument. 65 | 66 | See the [testharness.js] documentation for more information. 67 | 68 | [JavaScript API]: https://webassembly.github.io/spec/js-api/ 69 | [web-platform-tests project]: https://github.com/web-platform-tests/wpt/tree/master/wasm/jsapi 70 | [testharness.js]: https://web-platform-tests.org/writing-tests/testharness-api.html 71 | [multi-global tests]: https://web-platform-tests.org/writing-tests/testharness.html#multi-global-tests 72 | -------------------------------------------------------------------------------- /test/js-api/constructor/compile.any.js: -------------------------------------------------------------------------------- 1 | // META: global=window,dedicatedworker,jsshell 2 | // META: script=/wasm/jsapi/wasm-module-builder.js 3 | 4 | function assert_Module(module) { 5 | assert_equals(Object.getPrototypeOf(module), WebAssembly.Module.prototype, 6 | "Prototype"); 7 | assert_true(Object.isExtensible(module), "Extensibility"); 8 | } 9 | 10 | let emptyModuleBinary; 11 | setup(() => { 12 | emptyModuleBinary = new WasmModuleBuilder().toBuffer(); 13 | }); 14 | 15 | promise_test(t => { 16 | return promise_rejects_js(t, TypeError, WebAssembly.compile()); 17 | }, "Missing argument"); 18 | 19 | promise_test(t => { 20 | const invalidArguments = [ 21 | undefined, 22 | null, 23 | true, 24 | "", 25 | Symbol(), 26 | 1, 27 | {}, 28 | ArrayBuffer, 29 | ArrayBuffer.prototype, 30 | Array.from(emptyModuleBinary), 31 | ]; 32 | return Promise.all(invalidArguments.map(argument => { 33 | return promise_rejects_js(t, TypeError, WebAssembly.compile(argument), 34 | `compile(${format_value(argument)})`); 35 | })); 36 | }, "Invalid arguments"); 37 | 38 | promise_test(() => { 39 | const fn = WebAssembly.compile; 40 | const thisValues = [ 41 | undefined, 42 | null, 43 | true, 44 | "", 45 | Symbol(), 46 | 1, 47 | {}, 48 | WebAssembly, 49 | ]; 50 | return Promise.all(thisValues.map(thisValue => { 51 | return fn.call(thisValue, emptyModuleBinary).then(assert_Module); 52 | })); 53 | }, "Branding"); 54 | 55 | test(() => { 56 | const promise = WebAssembly.compile(emptyModuleBinary); 57 | assert_equals(Object.getPrototypeOf(promise), Promise.prototype, "prototype"); 58 | assert_true(Object.isExtensible(promise), "extensibility"); 59 | }, "Promise type"); 60 | 61 | promise_test(t => { 62 | const buffer = new Uint8Array(); 63 | return promise_rejects_js(t, WebAssembly.CompileError, WebAssembly.compile(buffer)); 64 | }, "Empty buffer"); 65 | 66 | promise_test(t => { 67 | const buffer = new Uint8Array(Array.from(emptyModuleBinary).concat([0, 0])); 68 | return promise_rejects_js(t, WebAssembly.CompileError, WebAssembly.compile(buffer)); 69 | }, "Invalid code"); 70 | 71 | promise_test(() => { 72 | return WebAssembly.compile(emptyModuleBinary).then(assert_Module); 73 | }, "Result type"); 74 | 75 | promise_test(() => { 76 | return WebAssembly.compile(emptyModuleBinary, {}).then(assert_Module); 77 | }, "Stray argument"); 78 | 79 | promise_test(() => { 80 | const buffer = new WasmModuleBuilder().toBuffer(); 81 | assert_equals(buffer[0], 0); 82 | const promise = WebAssembly.compile(buffer); 83 | buffer[0] = 1; 84 | return promise.then(assert_Module); 85 | }, "Changing the buffer"); 86 | -------------------------------------------------------------------------------- /test/js-api/constructor/instantiate-bad-imports.any.js: -------------------------------------------------------------------------------- 1 | // META: global=window,dedicatedworker,jsshell 2 | // META: script=/wasm/jsapi/wasm-module-builder.js 3 | // META: script=/wasm/jsapi/bad-imports.js 4 | 5 | test_bad_imports((name, error, build, ...arguments) => { 6 | promise_test(t => { 7 | const builder = new WasmModuleBuilder(); 8 | build(builder); 9 | const buffer = builder.toBuffer(); 10 | const module = new WebAssembly.Module(buffer); 11 | return promise_rejects_js(t, error, WebAssembly.instantiate(module, ...arguments)); 12 | }, `WebAssembly.instantiate(module): ${name}`); 13 | }); 14 | 15 | test_bad_imports((name, error, build, ...arguments) => { 16 | promise_test(t => { 17 | const builder = new WasmModuleBuilder(); 18 | build(builder); 19 | const buffer = builder.toBuffer(); 20 | return promise_rejects_js(t, error, WebAssembly.instantiate(buffer, ...arguments)); 21 | }, `WebAssembly.instantiate(buffer): ${name}`); 22 | }); 23 | -------------------------------------------------------------------------------- /test/js-api/constructor/validate.any.js: -------------------------------------------------------------------------------- 1 | // META: global=window,dedicatedworker,jsshell 2 | // META: script=/wasm/jsapi/wasm-module-builder.js 3 | 4 | let emptyModuleBinary; 5 | setup(() => { 6 | emptyModuleBinary = new WasmModuleBuilder().toBuffer(); 7 | }); 8 | 9 | test(() => { 10 | assert_throws_js(TypeError, () => WebAssembly.validate()); 11 | }, "Missing argument"); 12 | 13 | test(() => { 14 | const invalidArguments = [ 15 | undefined, 16 | null, 17 | true, 18 | "", 19 | Symbol(), 20 | 1, 21 | {}, 22 | ArrayBuffer, 23 | ArrayBuffer.prototype, 24 | Array.from(emptyModuleBinary), 25 | ]; 26 | for (const argument of invalidArguments) { 27 | assert_throws_js(TypeError, () => WebAssembly.validate(argument), 28 | `validate(${format_value(argument)})`); 29 | } 30 | }, "Invalid arguments"); 31 | 32 | test(() => { 33 | const fn = WebAssembly.validate; 34 | const thisValues = [ 35 | undefined, 36 | null, 37 | true, 38 | "", 39 | Symbol(), 40 | 1, 41 | {}, 42 | WebAssembly, 43 | ]; 44 | for (const thisValue of thisValues) { 45 | assert_true(fn.call(thisValue, emptyModuleBinary), `this=${format_value(thisValue)}`); 46 | } 47 | }, "Branding"); 48 | 49 | const modules = [ 50 | // Incomplete header. 51 | [[], false], 52 | [[0x00], false], 53 | [[0x00, 0x61], false], 54 | [[0x00, 0x61, 0x73], false], 55 | [[0x00, 0x61, 0x73, 0x6d], false], 56 | [[0x00, 0x61, 0x73, 0x6d, 0x01], false], 57 | [[0x00, 0x61, 0x73, 0x6d, 0x01, 0x00], false], 58 | [[0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00], false], 59 | 60 | // Complete header. 61 | [[0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00], true], 62 | 63 | // Invalid version. 64 | [[0x00, 0x61, 0x73, 0x6d, 0x00, 0x00, 0x00, 0x00], false], 65 | [[0x00, 0x61, 0x73, 0x6d, 0x02, 0x00, 0x00, 0x00], false], 66 | 67 | // Nameless custom section. 68 | [[0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00], false], 69 | 70 | // Custom section with empty name. 71 | [[0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00], true], 72 | 73 | // Custom section with name "a". 74 | [[0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x01, 0x61], true], 75 | ]; 76 | const bufferTypes = [ 77 | Uint8Array, 78 | Int8Array, 79 | Uint16Array, 80 | Int16Array, 81 | Uint32Array, 82 | Int32Array, 83 | ]; 84 | for (const [module, expected] of modules) { 85 | const name = module.map(n => n.toString(16)).join(" "); 86 | for (const bufferType of bufferTypes) { 87 | if (module.length % bufferType.BYTES_PER_ELEMENT === 0) { 88 | test(() => { 89 | const bytes = new Uint8Array(module); 90 | const moduleBuffer = new bufferType(bytes.buffer); 91 | assert_equals(WebAssembly.validate(moduleBuffer), expected); 92 | }, `Validating module [${name}] in ${bufferType.name}`); 93 | } 94 | } 95 | } 96 | 97 | test(() => { 98 | assert_true(WebAssembly.validate(emptyModuleBinary, {})); 99 | }, "Stray argument"); 100 | -------------------------------------------------------------------------------- /test/js-api/error-interfaces-no-symbol-tostringtag.js: -------------------------------------------------------------------------------- 1 | // META: global=jsshell 2 | 3 | test(() => { 4 | assert_not_own_property(WebAssembly.CompileError.prototype, Symbol.toStringTag); 5 | }, "WebAssembly.CompileError"); 6 | 7 | test(() => { 8 | assert_not_own_property(WebAssembly.LinkError.prototype, Symbol.toStringTag); 9 | }, "WebAssembly.LinkError"); 10 | 11 | test(() => { 12 | assert_not_own_property(WebAssembly.RuntimeError.prototype, Symbol.toStringTag); 13 | }, "WebAssembly.RuntimeError"); 14 | -------------------------------------------------------------------------------- /test/js-api/global/toString.any.js: -------------------------------------------------------------------------------- 1 | // META: global=window,dedicatedworker,jsshell 2 | 3 | test(() => { 4 | const argument = { "value": "i32" }; 5 | const global = new WebAssembly.Global(argument); 6 | assert_class_string(global, "WebAssembly.Global"); 7 | }, "Object.prototype.toString on an Global"); 8 | 9 | test(() => { 10 | assert_own_property(WebAssembly.Global.prototype, Symbol.toStringTag); 11 | 12 | const propDesc = Object.getOwnPropertyDescriptor(WebAssembly.Global.prototype, Symbol.toStringTag); 13 | assert_equals(propDesc.value, "WebAssembly.Global", "value"); 14 | assert_equals(propDesc.configurable, true, "configurable"); 15 | assert_equals(propDesc.enumerable, false, "enumerable"); 16 | assert_equals(propDesc.writable, false, "writable"); 17 | }, "@@toStringTag exists on the prototype with the appropriate descriptor"); 18 | -------------------------------------------------------------------------------- /test/js-api/global/valueOf.any.js: -------------------------------------------------------------------------------- 1 | // META: global=window,dedicatedworker,jsshell 2 | 3 | test(() => { 4 | const argument = { "value": "i32" }; 5 | const thisValues = [ 6 | undefined, 7 | null, 8 | true, 9 | "", 10 | Symbol(), 11 | 1, 12 | {}, 13 | WebAssembly.Global, 14 | WebAssembly.Global.prototype, 15 | ]; 16 | 17 | const fn = WebAssembly.Global.prototype.valueOf; 18 | 19 | for (const thisValue of thisValues) { 20 | assert_throws_js(TypeError, () => fn.call(thisValue), `this=${format_value(thisValue)}`); 21 | } 22 | }, "Branding"); 23 | 24 | test(() => { 25 | const argument = { "value": "i32" }; 26 | const global = new WebAssembly.Global(argument, 0); 27 | assert_equals(global.valueOf({}), 0); 28 | }, "Stray argument"); 29 | -------------------------------------------------------------------------------- /test/js-api/instance/constructor-bad-imports.any.js: -------------------------------------------------------------------------------- 1 | // META: global=window,dedicatedworker,jsshell 2 | // META: script=/wasm/jsapi/wasm-module-builder.js 3 | // META: script=/wasm/jsapi/bad-imports.js 4 | 5 | test_bad_imports((name, error, build, ...arguments) => { 6 | test(() => { 7 | const builder = new WasmModuleBuilder(); 8 | build(builder); 9 | const buffer = builder.toBuffer(); 10 | const module = new WebAssembly.Module(buffer); 11 | assert_throws_js(error, () => new WebAssembly.Instance(module, ...arguments)); 12 | }, `new WebAssembly.Instance(module): ${name}`); 13 | }); 14 | -------------------------------------------------------------------------------- /test/js-api/instance/constructor-caching.any.js: -------------------------------------------------------------------------------- 1 | // META: global=window,dedicatedworker,jsshell 2 | // META: script=/wasm/jsapi/wasm-module-builder.js 3 | 4 | function getExports() { 5 | const builder = new WasmModuleBuilder(); 6 | builder 7 | .addFunction("fn", kSig_v_d) 8 | .addBody([]) 9 | .exportFunc(); 10 | 11 | builder.setTableBounds(1); 12 | builder.addExportOfKind("table", kExternalTable, 0); 13 | builder.addGlobal(kWasmI32, false).exportAs("global"); 14 | builder.addMemory(4, 8, true); 15 | 16 | const buffer = builder.toBuffer(); 17 | const module = new WebAssembly.Module(buffer); 18 | const instance = new WebAssembly.Instance(module); 19 | return instance.exports; 20 | } 21 | 22 | test(() => { 23 | const exports = getExports(); 24 | 25 | const builder = new WasmModuleBuilder(); 26 | const functionIndex = builder.addImport("module", "imported", kSig_v_d); 27 | builder.addExport("exportedFunction", functionIndex); 28 | 29 | const globalIndex = builder.addImportedGlobal("module", "global", kWasmI32); 30 | builder.addExportOfKind("exportedGlobal", kExternalGlobal, globalIndex); 31 | 32 | builder.addImportedMemory("module", "memory", 4); 33 | builder.exportMemoryAs("exportedMemory"); 34 | 35 | const tableIndex = builder.addImportedTable("module", "table", 1); 36 | builder.addExportOfKind("exportedTable", kExternalTable, tableIndex); 37 | 38 | const buffer = builder.toBuffer(); 39 | 40 | const module = new WebAssembly.Module(buffer); 41 | const instance = new WebAssembly.Instance(module, { 42 | "module": { 43 | "imported": exports.fn, 44 | "global": exports.global, 45 | "memory": exports.memory, 46 | "table": exports.table, 47 | } 48 | }); 49 | 50 | assert_equals(instance.exports.exportedFunction, exports.fn); 51 | assert_equals(instance.exports.exportedGlobal, exports.global); 52 | assert_equals(instance.exports.exportedMemory, exports.memory); 53 | assert_equals(instance.exports.exportedTable, exports.table); 54 | }); 55 | -------------------------------------------------------------------------------- /test/js-api/instance/constructor.any.js: -------------------------------------------------------------------------------- 1 | // META: global=window,dedicatedworker,jsshell 2 | // META: script=/wasm/jsapi/wasm-module-builder.js 3 | // META: script=/wasm/jsapi/assertions.js 4 | // META: script=/wasm/jsapi/instanceTestFactory.js 5 | 6 | let emptyModuleBinary; 7 | setup(() => { 8 | emptyModuleBinary = new WasmModuleBuilder().toBuffer(); 9 | }); 10 | 11 | test(() => { 12 | assert_function_name(WebAssembly.Instance, "Instance", "WebAssembly.Instance"); 13 | }, "name"); 14 | 15 | test(() => { 16 | assert_function_length(WebAssembly.Instance, 1, "WebAssembly.Instance"); 17 | }, "length"); 18 | 19 | test(() => { 20 | assert_throws_js(TypeError, () => new WebAssembly.Instance()); 21 | }, "No arguments"); 22 | 23 | test(() => { 24 | const invalidArguments = [ 25 | undefined, 26 | null, 27 | true, 28 | "", 29 | Symbol(), 30 | 1, 31 | {}, 32 | WebAssembly.Module, 33 | WebAssembly.Module.prototype, 34 | ]; 35 | for (const argument of invalidArguments) { 36 | assert_throws_js(TypeError, () => new WebAssembly.Instance(argument), 37 | `new Instance(${format_value(argument)})`); 38 | } 39 | }, "Non-Module arguments"); 40 | 41 | test(() => { 42 | const module = new WebAssembly.Module(emptyModuleBinary); 43 | assert_throws_js(TypeError, () => WebAssembly.Instance(module)); 44 | }, "Calling"); 45 | 46 | for (const [name, fn] of instanceTestFactory) { 47 | test(() => { 48 | const { buffer, args, exports, verify } = fn(); 49 | const module = new WebAssembly.Module(buffer); 50 | const instance = new WebAssembly.Instance(module, ...args); 51 | assert_Instance(instance, exports); 52 | verify(instance); 53 | }, name); 54 | } 55 | -------------------------------------------------------------------------------- /test/js-api/instance/exports.any.js: -------------------------------------------------------------------------------- 1 | // META: global=window,dedicatedworker,jsshell 2 | // META: script=/wasm/jsapi/wasm-module-builder.js 3 | 4 | let emptyModuleBinary; 5 | setup(() => { 6 | emptyModuleBinary = new WasmModuleBuilder().toBuffer(); 7 | }); 8 | 9 | test(() => { 10 | const thisValues = [ 11 | undefined, 12 | null, 13 | true, 14 | "", 15 | Symbol(), 16 | 1, 17 | {}, 18 | WebAssembly.Instance, 19 | WebAssembly.Instance.prototype, 20 | ]; 21 | 22 | const desc = Object.getOwnPropertyDescriptor(WebAssembly.Instance.prototype, "exports"); 23 | assert_equals(typeof desc, "object"); 24 | 25 | const getter = desc.get; 26 | assert_equals(typeof getter, "function"); 27 | 28 | assert_equals(typeof desc.set, "undefined"); 29 | 30 | for (const thisValue of thisValues) { 31 | assert_throws_js(TypeError, () => getter.call(thisValue), `this=${format_value(thisValue)}`); 32 | } 33 | }, "Branding"); 34 | 35 | test(() => { 36 | const module = new WebAssembly.Module(emptyModuleBinary); 37 | const instance = new WebAssembly.Instance(module); 38 | const exports = instance.exports; 39 | 40 | const desc = Object.getOwnPropertyDescriptor(WebAssembly.Instance.prototype, "exports"); 41 | assert_equals(typeof desc, "object"); 42 | 43 | const getter = desc.get; 44 | assert_equals(typeof getter, "function"); 45 | 46 | assert_equals(getter.call(instance, {}), exports); 47 | }, "Stray argument"); 48 | 49 | test(() => { 50 | const module = new WebAssembly.Module(emptyModuleBinary); 51 | const instance = new WebAssembly.Instance(module); 52 | const exports = instance.exports; 53 | instance.exports = {}; 54 | assert_equals(instance.exports, exports, "Should not change the exports"); 55 | }, "Setting (sloppy mode)"); 56 | 57 | test(() => { 58 | const module = new WebAssembly.Module(emptyModuleBinary); 59 | const instance = new WebAssembly.Instance(module); 60 | const exports = instance.exports; 61 | assert_throws_js(TypeError, () => { 62 | "use strict"; 63 | instance.exports = {}; 64 | }); 65 | assert_equals(instance.exports, exports, "Should not change the exports"); 66 | }, "Setting (strict mode)"); 67 | -------------------------------------------------------------------------------- /test/js-api/instance/toString.any.js: -------------------------------------------------------------------------------- 1 | // META: global=window,dedicatedworker,jsshell 2 | // META: script=/wasm/jsapi/wasm-module-builder.js 3 | 4 | test(() => { 5 | const emptyModuleBinary = new WasmModuleBuilder().toBuffer(); 6 | const module = new WebAssembly.Module(emptyModuleBinary); 7 | const instance = new WebAssembly.Instance(module); 8 | assert_class_string(instance, "WebAssembly.Instance"); 9 | }, "Object.prototype.toString on an Instance"); 10 | 11 | test(() => { 12 | assert_own_property(WebAssembly.Instance.prototype, Symbol.toStringTag); 13 | 14 | const propDesc = Object.getOwnPropertyDescriptor(WebAssembly.Instance.prototype, Symbol.toStringTag); 15 | assert_equals(propDesc.value, "WebAssembly.Instance", "value"); 16 | assert_equals(propDesc.configurable, true, "configurable"); 17 | assert_equals(propDesc.enumerable, false, "enumerable"); 18 | assert_equals(propDesc.writable, false, "writable"); 19 | }, "@@toStringTag exists on the prototype with the appropriate descriptor"); 20 | -------------------------------------------------------------------------------- /test/js-api/memory/assertions.js: -------------------------------------------------------------------------------- 1 | function assert_ArrayBuffer(actual, { size=0, shared=false, detached=false }, message) { 2 | // https://github.com/WebAssembly/spec/issues/840 3 | // See https://github.com/whatwg/html/issues/5380 for why not `self.SharedArrayBuffer` 4 | const isShared = !("isView" in actual.constructor); 5 | assert_equals(isShared, shared, `${message}: constructor`); 6 | const sharedString = shared ? "Shared" : ""; 7 | assert_equals(actual.toString(), `[object ${sharedString}ArrayBuffer]`, `${message}: toString()`); 8 | assert_equals(Object.getPrototypeOf(actual).toString(), `[object ${sharedString}ArrayBuffer]`, `${message}: prototype toString()`); 9 | if (detached) { 10 | // https://github.com/tc39/ecma262/issues/678 11 | let byteLength; 12 | try { 13 | byteLength = actual.byteLength; 14 | } catch (e) { 15 | byteLength = 0; 16 | } 17 | assert_equals(byteLength, 0, `${message}: detached size`); 18 | } else { 19 | assert_equals(actual.byteLength, 0x10000 * size, `${message}: size`); 20 | if (size > 0) { 21 | const array = new Uint8Array(actual); 22 | assert_equals(array[0], 0, `${message}: first element`); 23 | assert_equals(array[array.byteLength - 1], 0, `${message}: last element`); 24 | } 25 | } 26 | assert_equals(Object.isFrozen(actual), shared, "buffer frozen"); 27 | assert_equals(Object.isExtensible(actual), !shared, "buffer extensibility"); 28 | } 29 | 30 | function assert_Memory(memory, { size=0, shared=false }) { 31 | assert_equals(Object.getPrototypeOf(memory), WebAssembly.Memory.prototype, 32 | "prototype"); 33 | assert_true(Object.isExtensible(memory), "extensible"); 34 | 35 | // https://github.com/WebAssembly/spec/issues/840 36 | assert_equals(memory.buffer, memory.buffer, "buffer should be idempotent"); 37 | assert_ArrayBuffer(memory.buffer, { size, shared }); 38 | } 39 | -------------------------------------------------------------------------------- /test/js-api/memory/buffer.any.js: -------------------------------------------------------------------------------- 1 | // META: global=window,dedicatedworker,jsshell 2 | 3 | test(() => { 4 | const thisValues = [ 5 | undefined, 6 | null, 7 | true, 8 | "", 9 | Symbol(), 10 | 1, 11 | {}, 12 | WebAssembly.Memory, 13 | WebAssembly.Memory.prototype, 14 | ]; 15 | 16 | const desc = Object.getOwnPropertyDescriptor(WebAssembly.Memory.prototype, "buffer"); 17 | assert_equals(typeof desc, "object"); 18 | 19 | const getter = desc.get; 20 | assert_equals(typeof getter, "function"); 21 | 22 | assert_equals(typeof desc.set, "undefined"); 23 | 24 | for (const thisValue of thisValues) { 25 | assert_throws_js(TypeError, () => getter.call(thisValue), `this=${format_value(thisValue)}`); 26 | } 27 | }, "Branding"); 28 | 29 | test(() => { 30 | const argument = { "initial": 0 }; 31 | const memory = new WebAssembly.Memory(argument); 32 | const buffer = memory.buffer; 33 | 34 | const desc = Object.getOwnPropertyDescriptor(WebAssembly.Memory.prototype, "buffer"); 35 | assert_equals(typeof desc, "object"); 36 | 37 | const getter = desc.get; 38 | assert_equals(typeof getter, "function"); 39 | 40 | assert_equals(getter.call(memory, {}), buffer); 41 | }, "Stray argument"); 42 | 43 | test(() => { 44 | const argument = { "initial": 0 }; 45 | const memory = new WebAssembly.Memory(argument); 46 | const memory2 = new WebAssembly.Memory(argument); 47 | const buffer = memory.buffer; 48 | assert_not_equals(buffer, memory2.buffer, "Need two distinct buffers"); 49 | memory.buffer = memory2.buffer; 50 | assert_equals(memory.buffer, buffer, "Should not change the buffer"); 51 | }, "Setting (sloppy mode)"); 52 | 53 | test(() => { 54 | const argument = { "initial": 0 }; 55 | const memory = new WebAssembly.Memory(argument); 56 | const memory2 = new WebAssembly.Memory(argument); 57 | const buffer = memory.buffer; 58 | assert_not_equals(buffer, memory2.buffer, "Need two distinct buffers"); 59 | assert_throws_js(TypeError, () => { 60 | "use strict"; 61 | memory.buffer = memory2.buffer; 62 | }); 63 | assert_equals(memory.buffer, buffer, "Should not change the buffer"); 64 | }, "Setting (strict mode)"); 65 | -------------------------------------------------------------------------------- /test/js-api/memory/constructor.any.js: -------------------------------------------------------------------------------- 1 | // META: global=window,dedicatedworker,jsshell 2 | // META: script=/wasm/jsapi/assertions.js 3 | // META: script=/wasm/jsapi/memory/assertions.js 4 | 5 | test(() => { 6 | assert_function_name(WebAssembly.Memory, "Memory", "WebAssembly.Memory"); 7 | }, "name"); 8 | 9 | test(() => { 10 | assert_function_length(WebAssembly.Memory, 1, "WebAssembly.Memory"); 11 | }, "length"); 12 | 13 | test(() => { 14 | assert_throws_js(TypeError, () => new WebAssembly.Memory()); 15 | }, "No arguments"); 16 | 17 | test(() => { 18 | const argument = { "initial": 0 }; 19 | assert_throws_js(TypeError, () => WebAssembly.Memory(argument)); 20 | }, "Calling"); 21 | 22 | test(() => { 23 | const invalidArguments = [ 24 | undefined, 25 | null, 26 | false, 27 | true, 28 | "", 29 | "test", 30 | Symbol(), 31 | 1, 32 | NaN, 33 | {}, 34 | ]; 35 | for (const invalidArgument of invalidArguments) { 36 | assert_throws_js(TypeError, 37 | () => new WebAssembly.Memory(invalidArgument), 38 | `new Memory(${format_value(invalidArgument)})`); 39 | } 40 | }, "Invalid descriptor argument"); 41 | 42 | test(() => { 43 | assert_throws_js(TypeError, () => new WebAssembly.Memory({ "initial": undefined })); 44 | }, "Undefined initial value in descriptor"); 45 | 46 | const outOfRangeValues = [ 47 | NaN, 48 | Infinity, 49 | -Infinity, 50 | -1, 51 | 0x100000000, 52 | 0x1000000000, 53 | ]; 54 | 55 | for (const value of outOfRangeValues) { 56 | test(() => { 57 | assert_throws_js(TypeError, () => new WebAssembly.Memory({ "initial": value })); 58 | }, `Out-of-range initial value in descriptor: ${format_value(value)}`); 59 | 60 | test(() => { 61 | assert_throws_js(TypeError, () => new WebAssembly.Memory({ "initial": 0, "maximum": value })); 62 | }, `Out-of-range maximum value in descriptor: ${format_value(value)}`); 63 | } 64 | 65 | test(() => { 66 | assert_throws_js(RangeError, () => new WebAssembly.Memory({ "initial": 10, "maximum": 9 })); 67 | }, "Initial value exceeds maximum"); 68 | 69 | test(() => { 70 | const proxy = new Proxy({}, { 71 | has(o, x) { 72 | assert_unreached(`Should not call [[HasProperty]] with ${x}`); 73 | }, 74 | get(o, x) { 75 | return 0; 76 | }, 77 | }); 78 | new WebAssembly.Memory(proxy); 79 | }, "Proxy descriptor"); 80 | 81 | test(() => { 82 | const order = []; 83 | 84 | new WebAssembly.Memory({ 85 | get maximum() { 86 | order.push("maximum"); 87 | return { 88 | valueOf() { 89 | order.push("maximum valueOf"); 90 | return 1; 91 | }, 92 | }; 93 | }, 94 | 95 | get initial() { 96 | order.push("initial"); 97 | return { 98 | valueOf() { 99 | order.push("initial valueOf"); 100 | return 1; 101 | }, 102 | }; 103 | }, 104 | }); 105 | 106 | assert_array_equals(order, [ 107 | "initial", 108 | "initial valueOf", 109 | "maximum", 110 | "maximum valueOf", 111 | ]); 112 | }, "Order of evaluation for descriptor"); 113 | 114 | test(() => { 115 | const argument = { "initial": 0 }; 116 | const memory = new WebAssembly.Memory(argument); 117 | assert_Memory(memory, { "size": 0 }); 118 | }, "Zero initial"); 119 | 120 | test(() => { 121 | const argument = { "initial": 4 }; 122 | const memory = new WebAssembly.Memory(argument); 123 | assert_Memory(memory, { "size": 4 }); 124 | }, "Non-zero initial"); 125 | 126 | test(() => { 127 | const argument = { "initial": 0 }; 128 | const memory = new WebAssembly.Memory(argument, {}); 129 | assert_Memory(memory, { "size": 0 }); 130 | }, "Stray argument"); 131 | -------------------------------------------------------------------------------- /test/js-api/memory/toString.any.js: -------------------------------------------------------------------------------- 1 | // META: global=window,dedicatedworker,jsshell 2 | 3 | test(() => { 4 | const argument = { "initial": 0 }; 5 | const memory = new WebAssembly.Memory(argument); 6 | assert_class_string(memory, "WebAssembly.Memory"); 7 | }, "Object.prototype.toString on an Memory"); 8 | 9 | test(() => { 10 | assert_own_property(WebAssembly.Memory.prototype, Symbol.toStringTag); 11 | 12 | const propDesc = Object.getOwnPropertyDescriptor(WebAssembly.Memory.prototype, Symbol.toStringTag); 13 | assert_equals(propDesc.value, "WebAssembly.Memory", "value"); 14 | assert_equals(propDesc.configurable, true, "configurable"); 15 | assert_equals(propDesc.enumerable, false, "enumerable"); 16 | assert_equals(propDesc.writable, false, "writable"); 17 | }, "@@toStringTag exists on the prototype with the appropriate descriptor"); 18 | -------------------------------------------------------------------------------- /test/js-api/module/constructor.any.js: -------------------------------------------------------------------------------- 1 | // META: global=window,dedicatedworker,jsshell 2 | // META: script=/wasm/jsapi/wasm-module-builder.js 3 | // META: script=/wasm/jsapi/assertions.js 4 | 5 | let emptyModuleBinary; 6 | setup(() => { 7 | emptyModuleBinary = new WasmModuleBuilder().toBuffer(); 8 | }); 9 | 10 | test(() => { 11 | assert_function_name(WebAssembly.Module, "Module", "WebAssembly.Module"); 12 | }, "name"); 13 | 14 | test(() => { 15 | assert_function_length(WebAssembly.Module, 1, "WebAssembly.Module"); 16 | }, "length"); 17 | 18 | test(() => { 19 | assert_throws_js(TypeError, () => new WebAssembly.Module()); 20 | }, "No arguments"); 21 | 22 | test(() => { 23 | assert_throws_js(TypeError, () => WebAssembly.Module(emptyModuleBinary)); 24 | }, "Calling"); 25 | 26 | test(() => { 27 | const invalidArguments = [ 28 | undefined, 29 | null, 30 | true, 31 | "test", 32 | Symbol(), 33 | 7, 34 | NaN, 35 | {}, 36 | ArrayBuffer, 37 | ArrayBuffer.prototype, 38 | Array.from(emptyModuleBinary), 39 | ]; 40 | for (const argument of invalidArguments) { 41 | assert_throws_js(TypeError, () => new WebAssembly.Module(argument), 42 | `new Module(${format_value(argument)})`); 43 | } 44 | }, "Invalid arguments"); 45 | 46 | test(() => { 47 | const buffer = new Uint8Array(); 48 | assert_throws_js(WebAssembly.CompileError, () => new WebAssembly.Module(buffer)); 49 | }, "Empty buffer"); 50 | 51 | test(() => { 52 | const buffer = new Uint8Array(Array.from(emptyModuleBinary).concat([0, 0])); 53 | assert_throws_js(WebAssembly.CompileError, () => new WebAssembly.Module(buffer)); 54 | }, "Invalid code"); 55 | 56 | test(() => { 57 | const module = new WebAssembly.Module(emptyModuleBinary); 58 | assert_equals(Object.getPrototypeOf(module), WebAssembly.Module.prototype); 59 | }, "Prototype"); 60 | 61 | test(() => { 62 | const module = new WebAssembly.Module(emptyModuleBinary); 63 | assert_true(Object.isExtensible(module)); 64 | }, "Extensibility"); 65 | 66 | test(() => { 67 | const module = new WebAssembly.Module(emptyModuleBinary, {}); 68 | assert_equals(Object.getPrototypeOf(module), WebAssembly.Module.prototype); 69 | }, "Stray argument"); 70 | -------------------------------------------------------------------------------- /test/js-api/module/exports.any.js: -------------------------------------------------------------------------------- 1 | // META: global=window,dedicatedworker,jsshell 2 | // META: script=/wasm/jsapi/wasm-module-builder.js 3 | 4 | let emptyModuleBinary; 5 | setup(() => { 6 | emptyModuleBinary = new WasmModuleBuilder().toBuffer(); 7 | }); 8 | 9 | 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_js(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_js(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=window,dedicatedworker,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_js(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_js(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=window,dedicatedworker,jsshell 2 | // META: script=/wasm/jsapi/wasm-module-builder.js 3 | 4 | test(() => { 5 | const emptyModuleBinary = new WasmModuleBuilder().toBuffer(); 6 | const module = new WebAssembly.Module(emptyModuleBinary); 7 | assert_class_string(module, "WebAssembly.Module"); 8 | }, "Object.prototype.toString on an Module"); 9 | 10 | test(() => { 11 | assert_own_property(WebAssembly.Module.prototype, Symbol.toStringTag); 12 | 13 | const propDesc = Object.getOwnPropertyDescriptor(WebAssembly.Module.prototype, Symbol.toStringTag); 14 | assert_equals(propDesc.value, "WebAssembly.Module", "value"); 15 | assert_equals(propDesc.configurable, true, "configurable"); 16 | assert_equals(propDesc.enumerable, false, "enumerable"); 17 | assert_equals(propDesc.writable, false, "writable"); 18 | }, "@@toStringTag exists on the prototype with the appropriate descriptor"); 19 | -------------------------------------------------------------------------------- /test/js-api/prototypes.any.js: -------------------------------------------------------------------------------- 1 | // META: global=window,dedicatedworker,jsshell 2 | // META: script=/wasm/jsapi/assertions.js 3 | // META: script=/wasm/jsapi/wasm-module-builder.js 4 | 5 | let emptyModuleBinary; 6 | setup(() => { 7 | emptyModuleBinary = new WasmModuleBuilder().toBuffer(); 8 | }); 9 | 10 | test(() => { 11 | class _Module extends WebAssembly.Module {} 12 | let module = new _Module(emptyModuleBinary); 13 | assert_true(module instanceof _Module, "_Module instanceof _Module"); 14 | assert_true(module instanceof WebAssembly.Module, "_Module instanceof WebAssembly.Module"); 15 | }, "_Module"); 16 | 17 | test(() => { 18 | class _Instance extends WebAssembly.Instance {} 19 | let instance = new _Instance(new WebAssembly.Module(emptyModuleBinary)); 20 | assert_true(instance instanceof _Instance, "_Instance instanceof _Instance"); 21 | assert_true(instance instanceof WebAssembly.Instance, "_Instance instanceof WebAssembly.Instance"); 22 | }, "_Instance"); 23 | 24 | test(() => { 25 | class _Memory extends WebAssembly.Memory {} 26 | let memory = new _Memory({initial: 0, maximum: 1}); 27 | assert_true(memory instanceof _Memory, "_Memory instanceof _Memory"); 28 | assert_true(memory instanceof WebAssembly.Memory, "_Memory instanceof WebAssembly.Memory"); 29 | }, "_Memory"); 30 | 31 | test(() => { 32 | class _Table extends WebAssembly.Table {} 33 | let table = new _Table({initial: 0, element: "anyfunc"}); 34 | assert_true(table instanceof _Table, "_Table instanceof _Table"); 35 | assert_true(table instanceof WebAssembly.Table, "_Table instanceof WebAssembly.Table"); 36 | }, "_Table"); 37 | 38 | test(() => { 39 | class _Global extends WebAssembly.Global {} 40 | let global = new _Global({value: "i32", mutable: false}, 0); 41 | assert_true(global instanceof _Global, "_Global instanceof _Global"); 42 | assert_true(global instanceof WebAssembly.Global, "_Global instanceof WebAssembly.Global"); 43 | }, "_Global"); 44 | -------------------------------------------------------------------------------- /test/js-api/table/assertions.js: -------------------------------------------------------------------------------- 1 | function assert_equal_to_array(table, expected, message) { 2 | assert_equals(table.length, expected.length, `${message}: length`); 3 | // The argument check in get() happens before the range check, and negative numbers 4 | // are illegal, hence will throw TypeError per spec. 5 | assert_throws_js(TypeError, () => table.get(-1), `${message}: table.get(-1)`); 6 | for (let i = 0; i < expected.length; ++i) { 7 | assert_equals(table.get(i), expected[i], `${message}: table.get(${i} of ${expected.length})`); 8 | } 9 | assert_throws_js(RangeError, () => table.get(expected.length), 10 | `${message}: table.get(${expected.length} of ${expected.length})`); 11 | assert_throws_js(RangeError, () => table.get(expected.length + 1), 12 | `${message}: table.get(${expected.length + 1} of ${expected.length})`); 13 | } 14 | -------------------------------------------------------------------------------- /test/js-api/table/grow.any.js: -------------------------------------------------------------------------------- 1 | // META: global=window,dedicatedworker,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_js(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_js(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_js(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_js(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, null, {}); 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=window,dedicatedworker,jsshell 2 | 3 | test(() => { 4 | const thisValues = [ 5 | undefined, 6 | null, 7 | true, 8 | "", 9 | Symbol(), 10 | 1, 11 | {}, 12 | WebAssembly.Table, 13 | WebAssembly.Table.prototype, 14 | ]; 15 | 16 | const desc = Object.getOwnPropertyDescriptor(WebAssembly.Table.prototype, "length"); 17 | assert_equals(typeof desc, "object"); 18 | 19 | const getter = desc.get; 20 | assert_equals(typeof getter, "function"); 21 | 22 | assert_equals(typeof desc.set, "undefined"); 23 | 24 | for (const thisValue of thisValues) { 25 | assert_throws_js(TypeError, () => getter.call(thisValue), `this=${format_value(thisValue)}`); 26 | } 27 | }, "Branding"); 28 | 29 | test(() => { 30 | const argument = { "element": "anyfunc", "initial": 2 }; 31 | const table = new WebAssembly.Table(argument); 32 | assert_equals(table.length, 2, "Initial length"); 33 | 34 | const desc = Object.getOwnPropertyDescriptor(WebAssembly.Table.prototype, "length"); 35 | assert_equals(typeof desc, "object"); 36 | 37 | const getter = desc.get; 38 | assert_equals(typeof getter, "function"); 39 | 40 | assert_equals(getter.call(table, {}), 2); 41 | }, "Stray argument"); 42 | 43 | test(() => { 44 | const argument = { "element": "anyfunc", "initial": 2 }; 45 | const table = new WebAssembly.Table(argument); 46 | assert_equals(table.length, 2, "Initial length"); 47 | table.length = 4; 48 | assert_equals(table.length, 2, "Should not change the length"); 49 | }, "Setting (sloppy mode)"); 50 | 51 | test(() => { 52 | const argument = { "element": "anyfunc", "initial": 2 }; 53 | const table = new WebAssembly.Table(argument); 54 | assert_equals(table.length, 2, "Initial length"); 55 | assert_throws_js(TypeError, () => { 56 | "use strict"; 57 | table.length = 4; 58 | }); 59 | assert_equals(table.length, 2, "Should not change the length"); 60 | }, "Setting (strict mode)"); 61 | -------------------------------------------------------------------------------- /test/js-api/table/toString.any.js: -------------------------------------------------------------------------------- 1 | // META: global=window,dedicatedworker,jsshell 2 | 3 | test(() => { 4 | const argument = { "element": "anyfunc", "initial": 0 }; 5 | const table = new WebAssembly.Table(argument); 6 | assert_class_string(table, "WebAssembly.Table"); 7 | }, "Object.prototype.toString on an Table"); 8 | 9 | test(() => { 10 | assert_own_property(WebAssembly.Table.prototype, Symbol.toStringTag); 11 | 12 | const propDesc = Object.getOwnPropertyDescriptor(WebAssembly.Table.prototype, Symbol.toStringTag); 13 | assert_equals(propDesc.value, "WebAssembly.Table", "value"); 14 | assert_equals(propDesc.configurable, true, "configurable"); 15 | assert_equals(propDesc.enumerable, false, "enumerable"); 16 | assert_equals(propDesc.writable, false, "writable"); 17 | }, "@@toStringTag exists on the prototype with the appropriate descriptor"); 18 | -------------------------------------------------------------------------------- /test/meta/Makefile: -------------------------------------------------------------------------------- 1 | SHARED_MEM=false 2 | 3 | # SpiderMonkey shell 4 | JSSHELL=~/m-i/js/src/build-debug/dist/bin/js -e 'const WITH_SHARED_MEMORY=$(SHARED_MEM);' -f common.js 5 | 6 | # Node.js 7 | #JSSHELL=./noderun.sh $(SHARED_MEM) 8 | 9 | TARGETDIR=../core 10 | 11 | .PHONY: all 12 | 13 | all: $(TARGETDIR)/memory_copy.wast \ 14 | $(TARGETDIR)/memory_init.wast \ 15 | $(TARGETDIR)/memory_fill.wast \ 16 | $(TARGETDIR)/table_copy.wast \ 17 | $(TARGETDIR)/table_init.wast 18 | 19 | $(TARGETDIR)/memory_copy.wast: generate_memory_copy.js common.js Makefile 20 | $(JSSHELL) $< > $@ 21 | 22 | $(TARGETDIR)/memory_init.wast: generate_memory_init.js common.js Makefile 23 | $(JSSHELL) $< > $@ 24 | 25 | $(TARGETDIR)/memory_fill.wast: generate_memory_fill.js common.js Makefile 26 | $(JSSHELL) $< > $@ 27 | 28 | $(TARGETDIR)/table_copy.wast: generate_table_copy.js common.js Makefile 29 | $(JSSHELL) $< > $@ 30 | 31 | $(TARGETDIR)/table_init.wast: generate_table_init.js common.js Makefile 32 | $(JSSHELL) $< > $@ 33 | -------------------------------------------------------------------------------- /test/meta/README.md: -------------------------------------------------------------------------------- 1 | These programs generate test cases. See Makefile for details. 2 | -------------------------------------------------------------------------------- /test/meta/common.js: -------------------------------------------------------------------------------- 1 | const PAGESIZE = 65536; 2 | 3 | function print_origin(origin) { 4 | print(";;"); 5 | print(";; Generated by ../meta/" + origin); 6 | print(";;"); 7 | } 8 | 9 | function checkRangeCode() { 10 | return ` 11 | (func (export "checkRange") (param $from i32) (param $to i32) (param $expected i32) (result i32) 12 | (loop $cont 13 | (if (i32.eq (local.get $from) (local.get $to)) 14 | (then 15 | (return (i32.const -1)))) 16 | (if (i32.eq (i32.load8_u (local.get $from)) (local.get $expected)) 17 | (then 18 | (local.set $from (i32.add (local.get $from) (i32.const 1))) 19 | (br $cont)))) 20 | (return (local.get $from))) 21 | `; 22 | } 23 | 24 | function checkRange(from, to, expected) { 25 | print( 26 | `(assert_return (invoke "checkRange" (i32.const ${from}) (i32.const ${to}) (i32.const ${expected})) 27 | (i32.const -1))`); 28 | } 29 | -------------------------------------------------------------------------------- /test/meta/noderun.sh: -------------------------------------------------------------------------------- 1 | if [ $# -ne 2 ]; then 2 | echo "Bad args" 3 | exit 1 4 | fi 5 | 6 | rm -f nodeprog.js 7 | cat <> nodeprog.js 8 | const WITH_SHARED_MEMORY=$1; 9 | function print(x) { 10 | console.log(x); 11 | } 12 | EOF 13 | cat common.js >> nodeprog.js 14 | cat $2 >> nodeprog.js 15 | node nodeprog.js 16 | rm nodeprog.js 17 | -------------------------------------------------------------------------------- /test/sync-js-api.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import glob 4 | import os 5 | import shutil 6 | import subprocess 7 | 8 | LOCAL_FILES = [ 9 | "LICENSE.md", 10 | "README.md", 11 | 12 | # Currently doesn't pass the stability checker in wpt. 13 | "limits.any.js", 14 | ] 15 | 16 | 17 | def copy_from_local(local_dir, out): 18 | for local_file in LOCAL_FILES: 19 | shutil.copy(os.path.join(local_dir, local_file), out) 20 | 21 | 22 | def copy_from_upstream(upstream, out): 23 | upstream = os.path.abspath(upstream) 24 | paths = glob.glob(os.path.join(upstream, "**", "*.js"), recursive=True) 25 | for path in paths: 26 | relpath = os.path.relpath(path, upstream) 27 | 28 | # Tests for proposals that have not merged here yet. 29 | if ".tentative" in relpath: 30 | continue 31 | 32 | # Requires `fetch()` and various wpt infrastructure. 33 | if os.path.basename(relpath) == "idlharness.any.js": 34 | continue 35 | 36 | dest = os.path.join(out, relpath) 37 | os.makedirs(os.path.dirname(dest), exist_ok=True) 38 | shutil.copy(path, dest) 39 | 40 | 41 | def main(upstream): 42 | local_dir = os.path.join("test", "js-api") 43 | scratch = os.path.join("test", "js-api-temp") 44 | os.mkdir(scratch) 45 | copy_from_local(local_dir, scratch) 46 | copy_from_upstream(os.path.join(upstream, "wasm", "jsapi"), scratch) 47 | shutil.rmtree(local_dir) 48 | os.rename(scratch, local_dir) 49 | subprocess.check_call(["git", "add", local_dir]) 50 | 51 | 52 | if __name__ == "__main__": 53 | import sys 54 | main(*sys.argv[1:]) 55 | -------------------------------------------------------------------------------- /w3c.json: -------------------------------------------------------------------------------- 1 | { 2 | "group": [78073] 3 | , "contacts": ["ericprud"] 4 | , "repo-type": "cg-report" 5 | } 6 | -------------------------------------------------------------------------------- /wasm-specs.bib: -------------------------------------------------------------------------------- 1 | @report{WebAssemblyCoreSpecification, 2 | title = {{WebAssembly Core Specification}}, 3 | editor = {Rossberg, Andreas}, 4 | date = {2019-12-05}, 5 | institution = {{W3C}}, 6 | url = {https://www.w3.org/TR/wasm-core-1/}, 7 | langid = {english}, 8 | note = {https://webassembly.github.io/spec/core/_download/WebAssembly.pdf} 9 | } 10 | --------------------------------------------------------------------------------