├── .gitattributes ├── .gitignore ├── .gitmodules ├── .travis.yml ├── Contributing.md ├── LICENSE ├── README.md ├── deploy_key.enc ├── document ├── Makefile ├── README.md ├── core │ ├── .gitignore │ ├── LICENSE │ ├── Makefile │ ├── README.md │ ├── appendix │ │ ├── algorithm.rst │ │ ├── custom.rst │ │ ├── embedding.rst │ │ ├── implementation.rst │ │ ├── index-instructions.rst │ │ ├── index-rules.rst │ │ ├── index-types.rst │ │ ├── index.rst │ │ └── properties.rst │ ├── binary │ │ ├── conventions.rst │ │ ├── index.rst │ │ ├── instructions.rst │ │ ├── modules.rst │ │ ├── types.rst │ │ └── values.rst │ ├── conf.py │ ├── exec │ │ ├── conventions.rst │ │ ├── index.rst │ │ ├── instructions.rst │ │ ├── modules.rst │ │ ├── numerics.rst │ │ └── runtime.rst │ ├── index.bs │ ├── index.rst │ ├── intro │ │ ├── index.rst │ │ ├── introduction.rst │ │ └── overview.rst │ ├── make.bat │ ├── static │ │ ├── custom.css │ │ └── webassembly.png │ ├── syntax │ │ ├── conventions.rst │ │ ├── index.rst │ │ ├── instructions.rst │ │ ├── modules.rst │ │ ├── types.rst │ │ └── values.rst │ ├── text │ │ ├── conventions.rst │ │ ├── index.rst │ │ ├── instructions.rst │ │ ├── lexical.rst │ │ ├── modules.rst │ │ ├── types.rst │ │ └── values.rst │ ├── util │ │ ├── README.htmldiff.pl │ │ ├── bikeshed │ │ │ └── conf.py │ │ ├── bikeshed_fixup.py │ │ ├── katex_fix.patch │ │ ├── macros.def │ │ ├── mathdef.py │ │ ├── mathdefbs.py │ │ ├── mathjax2katex.py │ │ └── pseudo-lexer.py │ └── valid │ │ ├── conventions.rst │ │ ├── index.rst │ │ ├── instructions.rst │ │ ├── modules.rst │ │ └── types.rst ├── deploy.sh ├── index.html ├── js-api │ ├── Makefile │ └── index.bs ├── travis-deploy.sh ├── util │ └── htmldiff.pl └── web-api │ ├── Makefile │ └── index.bs ├── interpreter ├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── binary │ ├── decode.ml │ ├── decode.mli │ ├── encode.ml │ ├── encode.mli │ ├── utf8.ml │ └── utf8.mli ├── exec │ ├── eval.ml │ ├── eval.mli │ ├── eval_numeric.ml │ ├── eval_numeric.mli │ ├── f32.ml │ ├── f32_convert.ml │ ├── f32_convert.mli │ ├── f64.ml │ ├── f64_convert.ml │ ├── f64_convert.mli │ ├── float.ml │ ├── i32.ml │ ├── i32_convert.ml │ ├── i32_convert.mli │ ├── i64.ml │ ├── i64_convert.ml │ ├── i64_convert.mli │ ├── int.ml │ └── numeric_error.ml ├── host │ ├── env.ml │ └── spectest.ml ├── main │ ├── flags.ml │ └── main.ml ├── meta │ ├── findlib │ │ └── META │ ├── jslib │ │ ├── bsconfig.json │ │ ├── build.sh │ │ └── wasm.ml │ └── travis │ │ ├── build-test.sh │ │ └── install-ocaml.sh ├── runtime │ ├── func.ml │ ├── func.mli │ ├── global.ml │ ├── global.mli │ ├── instance.ml │ ├── memory.ml │ ├── memory.mli │ ├── table.ml │ └── table.mli ├── script │ ├── import.ml │ ├── import.mli │ ├── js.ml │ ├── js.mli │ ├── run.ml │ ├── run.mli │ └── script.ml ├── syntax │ ├── ast.ml │ ├── operators.ml │ ├── types.ml │ └── values.ml ├── text │ ├── arrange.ml │ ├── arrange.mli │ ├── lexer.mli │ ├── lexer.mll │ ├── parse.ml │ ├── parse.mli │ ├── parser.mly │ ├── print.ml │ └── print.mli ├── util │ ├── error.ml │ ├── error.mli │ ├── lib.ml │ ├── lib.mli │ ├── sexpr.ml │ ├── sexpr.mli │ ├── source.ml │ └── source.mli ├── valid │ ├── valid.ml │ └── valid.mli └── winmake.bat ├── papers ├── LICENSE └── pldi2017.pdf ├── proposals └── funclets │ └── Overview.md └── test ├── LICENSE ├── README.md ├── Todo.md ├── build.py ├── core ├── .gitignore ├── README.md ├── address.wast ├── align.wast ├── binary.wast ├── block.wast ├── br.wast ├── br_if.wast ├── br_table.wast ├── break-drop.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 ├── get_local.wast ├── globals.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 ├── loop.wast ├── memory.wast ├── memory_grow.wast ├── memory_redundancy.wast ├── memory_trap.wast ├── names.wast ├── nop.wast ├── return.wast ├── run.py ├── select.wast ├── set_local.wast ├── skip-stack-guard-page.wast ├── stack.wast ├── start.wast ├── store_retval.wast ├── switch.wast ├── tee_local.wast ├── token.wast ├── traps.wast ├── type.wast ├── typecheck.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 ├── wasm-constants.js ├── wasm-module-builder.js └── wast.js ├── html └── indexeddb.js └── js-api ├── README.md └── jsapi.js /.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/Khan/KaTeX.git 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c++ 2 | language: python 3 | python: 4 | - "2.7" 5 | 6 | sudo: on 7 | 8 | install: 9 | - ./interpreter/meta/travis/install-ocaml.sh 10 | - sudo pip install sphinx==1.7.9 11 | - sudo apt-get install texlive-full 12 | - git clone https://github.com/tabatkins/bikeshed.git 13 | - pip install --editable $PWD/bikeshed 14 | - bikeshed update 15 | 16 | script: 17 | - ./interpreter/meta/travis/build-test.sh 18 | - bash ./document/travis-deploy.sh 19 | 20 | os: linux 21 | 22 | env: 23 | global: 24 | - ENCRYPTION_LABEL: "304454be9d6c" 25 | - COMMIT_AUTHOR_EMAIL: "noreply@webassembly.org" 26 | -------------------------------------------------------------------------------- /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/spec.svg?branch=master)](https://travis-ci.org/WebAssembly/spec) 2 | 3 | # Funclets Proposal for WebAssembly 4 | 5 | This repository is a clone of [github.com/WebAssembly/spec/](https://github.com/WebAssembly/spec/). 6 | It is meant for discussion, prototype specification and implementation of a proposal to add flexible intraprocedural control flow to WebAssembly. 7 | 8 | See the [overview](proposals/funclets/Overview.md) for a summary of the proposal. 9 | 10 | Original `README` from upstream repository follows... 11 | 12 | # spec 13 | 14 | This repository holds the sources for the WebAssembly draft specification 15 | (to seed a future 16 | [WebAssembly Working Group](https://lists.w3.org/Archives/Public/public-new-work/2017Jun/0005.html)), 17 | a reference implementation, and the official testsuite. 18 | 19 | A formatted version of the spec is available here: 20 | [webassembly.github.io/spec](https://webassembly.github.io/spec/), 21 | 22 | Participation is welcome. Discussions about new features, significant semantic 23 | changes, or any specification change likely to generate substantial discussion 24 | should take place in 25 | [the WebAssembly design repository](https://github.com/WebAssembly/design) 26 | first, so that this spec repository can remain focused. And please follow the 27 | [guidelines for contributing](Contributing.md). 28 | -------------------------------------------------------------------------------- /deploy_key.enc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WebAssembly/funclets/26d82a6a31fb78b2a035be9feb0b72d9091cbcaf/deploy_key.enc -------------------------------------------------------------------------------- /document/Makefile: -------------------------------------------------------------------------------- 1 | DIRS = core js-api web-api 2 | FILES = index.html 3 | BUILDDIR = _build 4 | 5 | # Global targets. 6 | 7 | .PHONY: all 8 | all: $(BUILDDIR) root $(DIRS) 9 | 10 | $(BUILDDIR): 11 | mkdir -p $@ 12 | 13 | .PHONY: deploy 14 | deploy: 15 | GIT_DEPLOY_DIR=$(BUILDDIR) bash deploy.sh 16 | 17 | .PHONY: publish 18 | publish: all deploy 19 | 20 | .PHONY: clean 21 | clean: $(DIRS:%=clean-%) 22 | rm -rf $(BUILDDIR) 23 | 24 | .PHONY: diff 25 | diff: $(DIRS:%=diff-%) 26 | 27 | 28 | # Directory-specific targets. 29 | 30 | .PHONY: root 31 | root: $(BUILDDIR) 32 | touch $(BUILDDIR)/.nojekyll 33 | cp -f $(FILES) $(BUILDDIR)/ 34 | 35 | .PHONY: $(DIRS) 36 | $(DIRS): %: $(BUILDDIR) $(DIRS:%=build-%) $(DIRS:%=dir-%) 37 | 38 | .PHONY: $(DIRS:%=build-%) 39 | $(DIRS:%=build-%): build-%: 40 | (cd $(@:build-%=%); make BUILDDIR=$(BUILDDIR) all) 41 | 42 | .PHONY: $(DIRS:%=dir-%) 43 | $(DIRS:%=dir-%): dir-%: 44 | mkdir -p $(BUILDDIR)/$(@:dir-%=%) 45 | rm -rf $(BUILDDIR)/$(@:dir-%=%)/* 46 | cp -R $(@:dir-%=%)/$(BUILDDIR)/html/* $(BUILDDIR)/$(@:dir-%=%)/ 47 | 48 | .PHONY: $(DIRS:%=deploy-%) 49 | $(DIRS:%=deploy-%): deploy-%: 50 | GIT_DEPLOY_DIR=$(BUILDDIR) GIT_DEPLOY_SUBDIR=$(@:deploy-%=%) bash deploy.sh 51 | 52 | .PHONY: $(DIRS:%=publish-%) 53 | $(DIRS:%=publish-%): publish-%: % deploy-% 54 | 55 | .PHONY: $(DIRS:%=clean-%) 56 | $(DIRS:%=clean-%): clean-%: 57 | (cd $(@:clean-%=%); make BUILDDIR=$(BUILDDIR) clean) 58 | rm -rf $(BUILDDIR)/$(@:clean-%=%) 59 | 60 | .PHONY: $(DIRS:%=diff-%) 61 | $(DIRS:%=diff-%): diff-%: 62 | (cd $(@:diff-%=%); make BUILDDIR=$(BUILDDIR) diff) 63 | 64 | 65 | # Help. 66 | 67 | .PHONY: help 68 | help: 69 | @echo "Please use \`make ' where is one of" 70 | @echo " all to build all documents" 71 | @echo " publish to make all and push to gh-pages" 72 | @echo " to build a specific subdirectory" 73 | @echo " publish- to build and push a specific subdirectory" 74 | 75 | .PHONY: usage 76 | usage: help 77 | -------------------------------------------------------------------------------- /document/README.md: -------------------------------------------------------------------------------- 1 | # WebAssembly Specifications 2 | 3 | This directory contains the source code for the WebAssembly spec documents, as served from the [webassembly.github.io/spec](https://webassembly.github.io/spec) pages. 4 | It uses [Sphinx](http://www.sphinx-doc.org/) and [Bikeshed](https://github.com/tabatkins/bikeshed). 5 | 6 | To install Sphinx: 7 | ``` 8 | pip install sphinx 9 | ``` 10 | 11 | To install Bikeshed, see the instructions [here](https://tabatkins.github.io/bikeshed/#installing). 12 | 13 | 14 | To build everything locally (result appears in `_build/`): 15 | ``` 16 | make all 17 | ``` 18 | 19 | To build everything and update [webassembly.github.io/spec](https://webassembly.github.io/spec) with it: 20 | ``` 21 | make publish 22 | ``` 23 | Please make sure to only use that once a change has approval. 24 | -------------------------------------------------------------------------------- /document/core/.gitignore: -------------------------------------------------------------------------------- 1 | _build 2 | _static 3 | document/*.pyc 4 | -------------------------------------------------------------------------------- /document/core/LICENSE: -------------------------------------------------------------------------------- 1 | W3C SOFTWARE AND DOCUMENT NOTICE AND LICENSE 2 | 3 | This work is being provided by the copyright holders under the following 4 | license. 5 | 6 | 7 | LICENSE 8 | 9 | By obtaining and/or copying this work, you (the licensee) agree that you have 10 | read, understood, and will comply with the following terms and conditions. 11 | 12 | Permission to copy, modify, and distribute this work, with or without 13 | modification, for any purpose and without fee or royalty is hereby granted, 14 | provided that you include the following on ALL copies of the work or portions 15 | thereof, including modifications: 16 | 17 | * The full text of this NOTICE in a location viewable to users of the 18 | redistributed or derivative work. 19 | 20 | * Any pre-existing intellectual property disclaimers, notices, or terms and 21 | conditions. If none exist, the W3C Software and Document Short Notice 22 | (https://www.w3.org/Consortium/Legal/copyright-software-short-notice) should 23 | be included. 24 | 25 | * Notice of any changes or modifications, through a copyright statement on the 26 | new code or document such as "This software or document includes material 27 | copied from or derived from [title and URI of the W3C document]. Copyright © [YEAR] W3C® (MIT, ERCIM, Keio, Beihang)." 28 | 29 | 30 | DISCLAIMERS 31 | 32 | THIS WORK IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS MAKE NO REPRESENTATIONS 33 | OR WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO, WARRANTIES OF 34 | MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE 35 | SOFTWARE OR DOCUMENT WILL NOT INFRINGE ANY THIRD PARTY PATENTS, COPYRIGHTS, 36 | TRADEMARKS OR OTHER RIGHTS. 37 | 38 | COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL OR 39 | CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE SOFTWARE OR DOCUMENT. 40 | 41 | The name and trademarks of copyright holders may NOT be used in advertising or 42 | publicity pertaining to the work without specific, written prior permission. 43 | Title to copyright in this work will at all times remain with copyright 44 | holders. 45 | 46 | 47 | NOTES 48 | 49 | This version: 50 | http://www.w3.org/Consortium/Legal/2015/copyright-software-and-document 51 | -------------------------------------------------------------------------------- /document/core/README.md: -------------------------------------------------------------------------------- 1 | # WebAssembly Core Specification 2 | 3 | This is the official WebAssembly "language" specification. 4 | 5 | It uses [Sphinx](http://www.sphinx-doc.org/). To install that: 6 | ``` 7 | pip install sphinx 8 | ``` 9 | To make HTML (result in `_build/html`): 10 | ``` 11 | make html 12 | ``` 13 | To make PDF (result in `_build/latex`, requires LaTeX): 14 | ``` 15 | make pdf 16 | ``` 17 | To make all: 18 | ``` 19 | make all 20 | ``` 21 | Finally, to make all and update webassembly.github.io/spec with it: 22 | ``` 23 | make publish 24 | ``` 25 | Please make sure to only use that once a change has approval. 26 | -------------------------------------------------------------------------------- /document/core/appendix/custom.rst: -------------------------------------------------------------------------------- 1 | .. index:: custom section, section, binary format 2 | 3 | Custom Sections 4 | --------------- 5 | 6 | This appendix defines dedicated :ref:`custom sections ` for WebAssembly's :ref:`binary format `. 7 | Such sections do not contribute to, or otherwise affect, the WebAssembly semantics, and like any custom section they may be ignored by an implementation. 8 | However, they provide useful meta data that implementations can make use of to improve user experience or take compilation hints. 9 | 10 | Currently, only one dedicated custom section is defined, the :ref:`name section`. 11 | 12 | 13 | .. index:: ! name section, name, Unicode UTF-8 14 | .. _binary-namesec: 15 | 16 | Name Section 17 | ~~~~~~~~~~~~ 18 | 19 | The *name section* is a :ref:`custom section ` whose name string is itself :math:`\text{name}`. 20 | The name section should appear only once in a module, and only after the :ref:`data section `. 21 | 22 | The purpose of this section is to attach printable names to definitions in a module, which e.g. can be used by a debugger or when parts of the module are to be rendered in :ref:`text form `. 23 | 24 | .. note:: 25 | All :ref:`names ` are represented in |Unicode|_ encoded in UTF-8. 26 | Names need not be unique. 27 | 28 | 29 | .. _binary-namesubsection: 30 | 31 | Subsections 32 | ........... 33 | 34 | The :ref:`data ` of a name section consists of a sequence of *subsections*. 35 | Each subsection consists of a 36 | 37 | * a one-byte subsection *id*, 38 | * the |U32| *size* of the contents, in bytes, 39 | * the actual *contents*, whose structure is depended on the subsection id. 40 | 41 | .. math:: 42 | \begin{array}{llcll} 43 | \production{name section} & \Bnamesec &::=& 44 | \Bsection_0(\Bnamedata) \\ 45 | \production{name data} & \Bnamedata &::=& 46 | n{:}\Bname~~\Bnamesubsection^\ast & (\iff n = \text{name}) \\ 47 | \production{name subsection} & \Bnamesubsection_N(\B{B}) &::=& 48 | N{:}\Bbyte~~\X{size}{:}\Bu32~~\B{B} 49 | & (\iff \X{size} = ||\B{B}||) \\ 50 | \end{array} 51 | 52 | The following subsection ids are used: 53 | 54 | == =========================================== 55 | Id Subsection 56 | == =========================================== 57 | 0 :ref:`module name ` 58 | 1 :ref:`function names ` 59 | 2 :ref:`local names ` 60 | == =========================================== 61 | 62 | 63 | .. index:: ! name map, index, index space 64 | .. _binary-indirectnamemap: 65 | .. _binary-namemap: 66 | 67 | Name Maps 68 | ......... 69 | 70 | A *name map* assigns :ref:`names ` to :ref:`indices ` in a given :ref:`index space `. 71 | It consists of a :ref:`vector ` of index/name pairs in order of increasing index value. 72 | Each index must be unique, but the assigned names need not be. 73 | 74 | .. math:: 75 | \begin{array}{llclll} 76 | \production{name map} & \Bnamemap &::=& 77 | \Bvec(\Bnameassoc) \\ 78 | \production{name association} & \Bnameassoc &::=& 79 | \Bidx~\Bname \\ 80 | \end{array} 81 | 82 | An *indirect name map* assigns :ref:`names ` to a two-dimensional :ref:`index space `, where secondary indices are *grouped* by primary indices. 83 | It consists of a vector of primary index/name map pairs in order of increasing index value, where each name map in turn maps secondary indices to names. 84 | Each primary index must be unique, and likewise each secondary index per individual name map. 85 | 86 | .. math:: 87 | \begin{array}{llclll} 88 | \production{indirect name map} & \Bindirectnamemap &::=& 89 | \Bvec(\Bindirectnameassoc) \\ 90 | \production{indirect name association} & \Bindirectnameassoc &::=& 91 | \Bidx~\Bnamemap \\ 92 | \end{array} 93 | 94 | 95 | .. index:: module 96 | .. _binary-modulenamesec: 97 | 98 | Module Names 99 | ............ 100 | 101 | The *module name subsection* has the id 0. 102 | It simply consists of a single :ref:`name ` that is assigned to the module itself. 103 | 104 | .. math:: 105 | \begin{array}{llclll} 106 | \production{module name subsection} & \Bmodulenamesubsec &::=& 107 | \Bnamesubsection_0(\Bname) \\ 108 | \end{array} 109 | 110 | 111 | .. index:: function, function index 112 | .. _binary-funcnamesec: 113 | 114 | Function Names 115 | .............. 116 | 117 | The *function name subsection* has the id 1. 118 | It consists of a :ref:`name map ` assigning function names to :ref:`function indices `. 119 | 120 | .. math:: 121 | \begin{array}{llclll} 122 | \production{function name subsection} & \Bfuncnamesubsec &::=& 123 | \Bnamesubsection_1(\Bnamemap) \\ 124 | \end{array} 125 | 126 | 127 | .. index:: function, local, function index, local index 128 | .. _binary-localnamesec: 129 | 130 | Local Names 131 | ........... 132 | 133 | The *local name subsection* has the id 2. 134 | It consists of an :ref:`indirect name map ` assigning local names to :ref:`local indices ` grouped by :ref:`function indices `. 135 | 136 | .. math:: 137 | \begin{array}{llclll} 138 | \production{local name subsection} & \Blocalnamesubsec &::=& 139 | \Bnamesubsection_2(\Bindirectnamemap) \\ 140 | \end{array} 141 | -------------------------------------------------------------------------------- /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{7C}` .. :math:`\hex{71}` 16 | :ref:`Element type ` |ANYFUNC| :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/conventions.rst: -------------------------------------------------------------------------------- 1 | .. index:: ! binary format, module, byte, file extension, abstract syntax 2 | 3 | Conventions 4 | ----------- 5 | 6 | The binary format for WebAssembly :ref:`modules ` is a dense linear *encoding* of their :ref:`abstract syntax `. 7 | [#compression]_ 8 | 9 | The format is defined by an *attribute grammar* whose only terminal symbols are :ref:`bytes `. 10 | A byte sequence is a well-formed encoding of a module if and only if it is generated by the grammar. 11 | 12 | Each production of this grammar has exactly one synthesized attribute: the abstract syntax that the respective byte sequence encodes. 13 | Thus, the attribute grammar implicitly defines a *decoding* function 14 | (i.e., a parsing function for the binary format). 15 | 16 | Except for a few exceptions, the binary grammar closely mirrors the grammar of the abstract syntax. 17 | 18 | .. note:: 19 | Some phrases of abstract syntax have multiple possible encodings in the binary format. 20 | For example, numbers may be encoded as if they had optional leading zeros. 21 | Implementations of decoders must support all possible alternatives; 22 | implementations of encoders can pick any allowed encoding. 23 | 24 | The recommended extension for files containing WebAssembly modules in binary format is ":math:`\T{.wasm}`" 25 | and the recommended |MediaType|_ is ":math:`\T{application/wasm}`". 26 | 27 | .. [#compression] 28 | Additional encoding layers -- for example, introducing compression -- may be defined on top of the basic representation defined here. 29 | However, such layers are outside the scope of the current specification. 30 | 31 | 32 | .. index:: grammar notation, notation, byte 33 | single: binary format; grammar 34 | pair: binary format; notation 35 | .. _binary-grammar: 36 | 37 | Grammar 38 | ~~~~~~~ 39 | 40 | The following conventions are adopted in defining grammar rules for the binary format. 41 | They mirror the conventions used for :ref:`abstract syntax `. 42 | In order to distinguish symbols of the binary syntax from symbols of the abstract syntax, :math:`\mathtt{typewriter}` font is adopted for the former. 43 | 44 | * Terminal symbols are :ref:`bytes ` expressed in hexadecimal notation: :math:`\hex{0F}`. 45 | 46 | * Nonterminal symbols are written in typewriter font: :math:`\B{valtype}, \B{instr}`. 47 | 48 | * :math:`B^n` is a sequence of :math:`n\geq 0` iterations of :math:`B`. 49 | 50 | * :math:`B^\ast` is a possibly empty sequence of iterations of :math:`B`. 51 | (This is a shorthand for :math:`B^n` used where :math:`n` is not relevant.) 52 | 53 | * :math:`B^?` is an optional occurrence of :math:`B`. 54 | (This is a shorthand for :math:`B^n` where :math:`n \leq 1`.) 55 | 56 | * :math:`x{:}B` denotes the same language as the nonterminal :math:`B`, but also binds the variable :math:`x` to the attribute synthesized for :math:`B`. 57 | 58 | * Productions are written :math:`\B{sym} ::= B_1 \Rightarrow A_1 ~|~ \dots ~|~ B_n \Rightarrow A_n`, where each :math:`A_i` is the attribute that is synthesized for :math:`\B{sym}` in the given case, usually from attribute variables bound in :math:`B_i`. 59 | 60 | * Some productions are augmented by side conditions in parentheses, which restrict the applicability of the production. They provide a shorthand for a combinatorial expansion of the production into many separate cases. 61 | 62 | .. note:: 63 | For example, the :ref:`binary grammar ` for :ref:`value types ` is given as follows: 64 | 65 | .. math:: 66 | \begin{array}{llcll@{\qquad\qquad}l} 67 | \production{value types} & \Bvaltype &::=& 68 | \hex{7F} &\Rightarrow& \I32 \\ &&|& 69 | \hex{7E} &\Rightarrow& \I64 \\ &&|& 70 | \hex{7D} &\Rightarrow& \F32 \\ &&|& 71 | \hex{7C} &\Rightarrow& \F64 \\ 72 | \end{array} 73 | 74 | Consequently, the byte :math:`\hex{7F}` encodes the type |I32|, 75 | :math:`\hex{7E}` encodes the type |I64|, and so forth. 76 | No other byte value is allowed as the encoding of a value type. 77 | 78 | The :ref:`binary grammar ` for :ref:`limits ` is defined as follows: 79 | 80 | .. math:: 81 | \begin{array}{llclll} 82 | \production{limits} & \Blimits &::=& 83 | \hex{00}~~n{:}\Bu32 &\Rightarrow& \{ \LMIN~n, \LMAX~\epsilon \} \\ &&|& 84 | \hex{01}~~n{:}\Bu32~~m{:}\Bu32 &\Rightarrow& \{ \LMIN~n, \LMAX~m \} \\ 85 | \end{array} 86 | 87 | That is, a limits pair is encoded as either the byte :math:`\hex{00}` followed by the encoding of a |U32| value, 88 | or the byte :math:`\hex{01}` followed by two such encodings. 89 | The variables :math:`n` and :math:`m` name the attributes of the respective |Bu32| nonterminals, which in this case are the actual :ref:`unsigned integers ` those decode into. 90 | The attribute of the complete production then is the abstract syntax for the limit, expressed in terms of the former values. 91 | 92 | 93 | .. _binary-notation: 94 | 95 | Auxiliary Notation 96 | ~~~~~~~~~~~~~~~~~~ 97 | 98 | When dealing with binary encodings the following notation is also used: 99 | 100 | * :math:`\epsilon` denotes the empty byte sequence. 101 | 102 | * :math:`||B||` is the length of the byte sequence generated from the production :math:`B` in a derivation. 103 | 104 | 105 | .. index:: vector 106 | pair: binary format; vector 107 | .. _binary-vec: 108 | 109 | Vectors 110 | ~~~~~~~ 111 | 112 | :ref:`Vectors ` are encoded with their |Bu32| length followed by the encoding of their element sequence. 113 | 114 | .. math:: 115 | \begin{array}{llclll@{\qquad\qquad}l} 116 | \production{vector} & \Bvec(\B{B}) &::=& 117 | n{:}\Bu32~~(x{:}\B{B})^n &\Rightarrow& x^n \\ 118 | \end{array} 119 | -------------------------------------------------------------------------------- /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 | In future versions of WebAssembly, value types may include types denoted by :ref:`type indices `. 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-blocktype: 34 | .. _binary-resulttype: 35 | 36 | Result Types 37 | ~~~~~~~~~~~~ 38 | 39 | The only :ref:`result types ` occurring in the binary format are the types of blocks. These are encoded in special compressed form, by either the byte :math:`\hex{40}` indicating the empty type or as a single :ref:`value type `. 40 | 41 | .. math:: 42 | \begin{array}{llclll@{\qquad\qquad}l} 43 | \production{result type} & \Bblocktype &::=& 44 | \hex{40} &\Rightarrow& [] \\ &&|& 45 | t{:}\Bvaltype &\Rightarrow& [t] \\ 46 | \end{array} 47 | 48 | .. note:: 49 | In future versions of WebAssembly, this scheme may be extended to support multiple results or more general block types. 50 | 51 | 52 | .. index:: function type, value type, result type 53 | pair: binary format; function type 54 | .. _binary-functype: 55 | 56 | Function Types 57 | ~~~~~~~~~~~~~~ 58 | 59 | :ref:`Function types ` are encoded by the byte :math:`\hex{60}` followed by the respective :ref:`vectors ` of parameter and result types. 60 | 61 | .. math:: 62 | \begin{array}{llclll@{\qquad\qquad}l} 63 | \production{function type} & \Bfunctype &::=& 64 | \hex{60}~~t_1^\ast{:\,}\Bvec(\Bvaltype)~~t_2^\ast{:\,}\Bvec(\Bvaltype) 65 | &\Rightarrow& [t_1^\ast] \to [t_2^\ast] \\ 66 | \end{array} 67 | 68 | 69 | .. index:: limits 70 | pair: binary format; limits 71 | .. _binary-limits: 72 | 73 | Limits 74 | ~~~~~~ 75 | 76 | :ref:`Limits ` are encoded with a preceding flag indicating whether a maximum is present. 77 | 78 | .. math:: 79 | \begin{array}{llclll} 80 | \production{limits} & \Blimits &::=& 81 | \hex{00}~~n{:}\Bu32 &\Rightarrow& \{ \LMIN~n, \LMAX~\epsilon \} \\ &&|& 82 | \hex{01}~~n{:}\Bu32~~m{:}\Bu32 &\Rightarrow& \{ \LMIN~n, \LMAX~m \} \\ 83 | \end{array} 84 | 85 | 86 | .. index:: memory type, limits, page size 87 | pair: binary format; memory type 88 | .. _binary-memtype: 89 | 90 | Memory Types 91 | ~~~~~~~~~~~~ 92 | 93 | :ref:`Memory types ` are encoded with their :ref:`limits `. 94 | 95 | .. math:: 96 | \begin{array}{llclll@{\qquad\qquad}l} 97 | \production{memory type} & \Bmemtype &::=& 98 | \X{lim}{:}\Blimits &\Rightarrow& \X{lim} \\ 99 | \end{array} 100 | 101 | 102 | .. index:: table type, element type, limits 103 | pair: binary format; table type 104 | pair: binary format; element type 105 | .. _binary-elemtype: 106 | .. _binary-tabletype: 107 | 108 | Table Types 109 | ~~~~~~~~~~~ 110 | 111 | :ref:`Table types ` are encoded with their :ref:`limits ` and a constant byte indicating their :ref:`element type `. 112 | 113 | .. math:: 114 | \begin{array}{llclll} 115 | \production{table type} & \Btabletype &::=& 116 | \X{et}{:}\Belemtype~~\X{lim}{:}\Blimits &\Rightarrow& \X{lim}~\X{et} \\ 117 | \production{element type} & \Belemtype &::=& 118 | \hex{70} &\Rightarrow& \ANYFUNC \\ 119 | \end{array} 120 | 121 | 122 | .. index:: global type, mutability, value type 123 | pair: binary format; global type 124 | pair: binary format; mutability 125 | .. _binary-mut: 126 | .. _binary-globaltype: 127 | 128 | Global Types 129 | ~~~~~~~~~~~~ 130 | 131 | :ref:`Global types ` are encoded by their :ref:`value type ` and a flag for their :ref:`mutability `. 132 | 133 | .. math:: 134 | \begin{array}{llclll} 135 | \production{global type} & \Bglobaltype &::=& 136 | t{:}\Bvaltype~~m{:}\Bmut &\Rightarrow& m~t \\ 137 | \production{mutability} & \Bmut &::=& 138 | \hex{00} &\Rightarrow& \MCONST \\ &&|& 139 | \hex{01} &\Rightarrow& \MVAR \\ 140 | \end{array} 141 | -------------------------------------------------------------------------------- /document/core/binary/values.rst: -------------------------------------------------------------------------------- 1 | .. index:: value 2 | pair: binary format; value 3 | .. _binary-value: 4 | 5 | Values 6 | ------ 7 | 8 | 9 | .. index:: byte 10 | pair: binary format; byte 11 | .. _binary-byte: 12 | 13 | Bytes 14 | ~~~~~ 15 | 16 | :ref:`Bytes ` encode themselves. 17 | 18 | .. math:: 19 | \begin{array}{llcll@{\qquad}l} 20 | \production{byte} & \Bbyte &::=& 21 | \hex{00} &\Rightarrow& \hex{00} \\ &&|&& 22 | \dots \\ &&|& 23 | \hex{FF} &\Rightarrow& \hex{FF} \\ 24 | \end{array} 25 | 26 | 27 | .. index:: integer, unsigned integer, signed integer, uninterpreted integer, LEB128, two's complement 28 | pair: binary format; integer 29 | pair: binary format; unsigned integer 30 | pair: binary format; signed integer 31 | pair: binary format; uninterpreted integer 32 | .. _binary-sint: 33 | .. _binary-uint: 34 | .. _binary-int: 35 | 36 | Integers 37 | ~~~~~~~~ 38 | 39 | All :ref:`integers ` are encoded using the |LEB128|_ variable-length integer encoding, in either unsigned or signed variant. 40 | 41 | :ref:`Unsigned integers ` are encoded in |UnsignedLEB128|_ format. 42 | As an additional constraint, the total number of bytes encoding a value of type :math:`\uN` must not exceed :math:`\F{ceil}(N/7)` bytes. 43 | 44 | .. math:: 45 | \begin{array}{llclll@{\qquad}l} 46 | \production{unsigned integer} & \BuN &::=& 47 | n{:}\Bbyte &\Rightarrow& n & (\iff n < 2^7 \wedge n < 2^N) \\ &&|& 48 | n{:}\Bbyte~~m{:}\BuX{(N\B{-7})} &\Rightarrow& 49 | 2^7\cdot m + (n-2^7) & (\iff n \geq 2^7 \wedge N > 7) \\ 50 | \end{array} 51 | 52 | :ref:`Signed integers ` are encoded in |SignedLEB128|_ format, which uses a two's complement representation. 53 | As an additional constraint, the total number of bytes encoding a value of type :math:`\sN` must not exceed :math:`\F{ceil}(N/7)` bytes. 54 | 55 | .. math:: 56 | \begin{array}{llclll@{\qquad}l} 57 | \production{signed integer} & \BsN &::=& 58 | n{:}\Bbyte &\Rightarrow& n & (\iff n < 2^6 \wedge n < 2^{N-1}) \\ &&|& 59 | n{:}\Bbyte &\Rightarrow& n-2^7 & (\iff 2^6 \leq n < 2^7 \wedge n \geq 2^7-2^{N-1}) \\ &&|& 60 | n{:}\Bbyte~~m{:}\BsX{(N\B{-7})} &\Rightarrow& 61 | 2^7\cdot m + (n-2^7) & (\iff n \geq 2^7 \wedge N > 7) \\ 62 | \end{array} 63 | 64 | :ref:`Uninterpreted integers ` are encoded as signed integers. 65 | 66 | .. math:: 67 | \begin{array}{llclll@{\qquad\qquad}l} 68 | \production{uninterpreted integer} & \BiN &::=& 69 | n{:}\BsN &\Rightarrow& i & (\iff n = \signed_{\iN}(i)) 70 | \end{array} 71 | 72 | .. note:: 73 | The side conditions :math:`N > 7` in the productions for non-terminal bytes of the :math:`\uX{}` and :math:`\sX{}` encodings restrict the encoding's length. 74 | However, "trailing zeros" are still allowed within these bounds. 75 | For example, :math:`\hex{03}` and :math:`\hex{83}~\hex{00}` are both well-formed encodings for the value :math:`3` as a |u8|. 76 | Similarly, either of :math:`\hex{7e}` and :math:`\hex{FE}~\hex{7F}` and :math:`\hex{FE}~\hex{FF}~\hex{7F}` are well-formed encodings of the value :math:`-2` as a |s16|. 77 | 78 | The side conditions on the value :math:`n` of terminal bytes further enforce that 79 | any unused bits in these bytes must be :math:`0` for positive values and :math:`1` for negative ones. 80 | For example, :math:`\hex{83}~\hex{10}` is malformed as a |u8| encoding. 81 | Similarly, both :math:`\hex{83}~\hex{3E}` and :math:`\hex{FF}~\hex{7B}` are malformed as |s8| encodings. 82 | 83 | 84 | .. index:: floating-point number, little endian 85 | pair: binary format; floating-point number 86 | .. _binary-float: 87 | 88 | Floating-Point 89 | ~~~~~~~~~~~~~~ 90 | 91 | :ref:`Floating-point ` values are encoded directly by their |IEEE754|_ bit pattern in |LittleEndian|_ byte order: 92 | 93 | .. math:: 94 | \begin{array}{llclll@{\qquad\qquad}l} 95 | \production{floating-point value} & \BfN &::=& 96 | b^\ast{:\,}\Bbyte^{N/8} &\Rightarrow& \bytes_{\fN}^{-1}(b^\ast) \\ 97 | \end{array} 98 | 99 | 100 | .. index:: name, byte, Unicode, ! UTF-8 101 | pair: binary format; name 102 | .. _binary-utf8: 103 | .. _binary-name: 104 | 105 | Names 106 | ~~~~~ 107 | 108 | :ref:`Names ` are encoded as a :ref:`vector ` of bytes containing the |Unicode|_ UTF-8 encoding of the name's code point sequence. 109 | 110 | .. math:: 111 | \begin{array}{llclllll} 112 | \production{name} & \Bname &::=& 113 | b^\ast{:}\Bvec(\Bbyte) &\Rightarrow& \name 114 | && (\iff \utf8(\name) = b^\ast) \\ 115 | \end{array} 116 | 117 | The auxiliary |utf8| function expressing this encoding is defined as follows: 118 | 119 | .. math:: 120 | \begin{array}{@{}lcl@{\qquad}l} 121 | \utf8(c^\ast) &=& (\utf8(c))^\ast \\[1ex] 122 | \utf8(c) &=& b & 123 | (\begin{array}[t]{@{}c@{~}l@{}} 124 | \iff & c < \unicode{80} \\ 125 | \wedge & c = b) \\ 126 | \end{array} \\ 127 | \utf8(c) &=& b_1~b_2 & 128 | (\begin{array}[t]{@{}c@{~}l@{}} 129 | \iff & \unicode{80} \leq c < \unicode{800} \\ 130 | \wedge & c = 2^6(b_1-\hex{C0})+(b_2-\hex{80})) \\ 131 | \end{array} \\ 132 | \utf8(c) &=& b_1~b_2~b_3 & 133 | (\begin{array}[t]{@{}c@{~}l@{}} 134 | \iff & \unicode{800} \leq c < \unicode{10000} \\ 135 | \wedge & c = 2^{12}(b_1-\hex{E0})+2^6(b_2-\hex{80})+(b_3-\hex{80})) \\ 136 | \end{array} \\ 137 | \utf8(c) &=& b_1~b_2~b_3~b_4 & 138 | (\begin{array}[t]{@{}c@{~}l@{}} 139 | \iff & \unicode{10000} \leq c < \unicode{110000} \\ 140 | \wedge & c = 2^{18}(b_1-\hex{F0})+2^{12}(b_2-\hex{80})+2^6(b_3-\hex{80})+(b_4-\hex{80})) \\ 141 | \end{array} \\ 142 | \end{array} 143 | 144 | .. note:: 145 | Unlike in some other formats, name strings are not 0-terminated. 146 | -------------------------------------------------------------------------------- /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| (Draft, last updated |today|) 7 | 8 | .. toctree:: 9 | :maxdepth: 2 10 | 11 | intro/index 12 | syntax/index 13 | valid/index 14 | exec/index 15 | binary/index 16 | text/index 17 | appendix/index 18 | 19 | .. only:: latex 20 | 21 | .. toctree:: 22 | 23 | appendix/index-types 24 | appendix/index-instructions 25 | appendix/index-rules 26 | 27 | .. only:: html 28 | 29 | * :ref:`index-type` 30 | * :ref:`index-instr` 31 | * :ref:`index-rules` 32 | 33 | * :ref:`genindex` 34 | -------------------------------------------------------------------------------- /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/intro/introduction.rst: -------------------------------------------------------------------------------- 1 | Introduction 2 | ------------ 3 | 4 | WebAssembly (abbreviated Wasm [#wasm]_) is a *safe, portable, low-level code format* 5 | designed for efficient execution and compact representation. 6 | Its main goal is to enable high performance applications on the Web, but it does not make any Web-specific assumptions or provide Web-specific features, so it can be employed in other environments as well. 7 | 8 | WebAssembly is an open standard developed by a `W3C Community Group `_. 9 | 10 | This document describes version |release| of the :ref:`core ` WebAssembly standard. 11 | It is intended that it will be superseded by new incremental releases with additional features in the future. 12 | 13 | 14 | .. _goals: 15 | 16 | Design Goals 17 | ~~~~~~~~~~~~ 18 | 19 | .. index:: design goals, portability 20 | 21 | The design goals of WebAssembly are the following: 22 | 23 | * Fast, safe, and portable *semantics*: 24 | 25 | * **Fast**: executes with near native code performance, taking advantage of capabilities common to all contemporary hardware. 26 | 27 | * **Safe**: code is validated and executes in a memory-safe [#memorysafe]_, sandboxed environment preventing data corruption or security breaches. 28 | 29 | * **Well-defined**: fully and precisely defines valid programs and their behavior in a way that is easy to reason about informally and formally. 30 | 31 | * **Hardware-independent**: can be compiled on all modern architectures, desktop or mobile devices and embedded systems alike. 32 | 33 | * **Language-independent**: does not privilege any particular language, programming model, or object model. 34 | 35 | * **Platform-independent**: can be embedded in browsers, run as a stand-alone VM, or integrated in other environments. 36 | 37 | * **Open**: programs can interoperate with their environment in a simple and universal manner. 38 | 39 | * Efficient and portable *representation*: 40 | 41 | * **Compact**: has a binary format that is fast to transmit by being smaller than typical text or native code formats. 42 | 43 | * **Modular**: programs can be split up in smaller parts that can be transmitted, cached, and consumed separately. 44 | 45 | * **Efficient**: can be decoded, validated, and compiled in a fast single pass, equally with either just-in-time (JIT) or ahead-of-time (AOT) compilation. 46 | 47 | * **Streamable**: allows decoding, validation, and compilation to begin as soon as possible, before all data has been seen. 48 | 49 | * **Parallelizable**: allows decoding, validation, and compilation to be split into many independent parallel tasks. 50 | 51 | * **Portable**: makes no architectural assumptions that are not broadly supported across modern hardware. 52 | 53 | WebAssembly code is also intended to be easy to inspect and debug, especially in environments like web browsers, but such features are beyond the scope of this specification. 54 | 55 | 56 | .. [#wasm] A contraction of "WebAssembly", not an acronym, hence not using all-caps. 57 | 58 | .. [#memorysafe] No program can break WebAssembly's memory model. Of course, it cannot guarantee that an unsafe language compiling to WebAssembly does not corrupt its own memory layout, e.g. inside WebAssembly's linear memory. 59 | 60 | 61 | .. _scope: 62 | 63 | Scope 64 | ~~~~~ 65 | 66 | At its core, WebAssembly is a *virtual instruction set architecture (virtual ISA)*. 67 | As such, it has many use cases and can be embedded in many different environments. 68 | To encompass their variety and enable maximum reuse, the WebAssembly specification is split and layered into several documents. 69 | 70 | This document is concerned with the core ISA layer of WebAssembly. 71 | It defines the instruction set, binary encoding, validation, and execution semantics, as well as a textual representation. 72 | It does not, however, define how WebAssembly programs can interact with a specific environment they execute in, nor how they are invoked from such an environment. 73 | 74 | Instead, this specification is complemented by additional documents defining interfaces to specific embedding environments such as the Web. 75 | These will each define a WebAssembly *application programming interface (API)* suitable for a given environment. 76 | 77 | 78 | .. index:: IEEE 754, floating point, Unicode, name, text format, UTF-8, code point 79 | .. _dependencies: 80 | 81 | Dependencies 82 | ~~~~~~~~~~~~ 83 | 84 | WebAssembly depends on two existing standards: 85 | 86 | * |IEEE754|_, for the representation of :ref:`floating-point data ` and the semantics of respective :ref:`numeric operations `. 87 | 88 | * |Unicode|_, for the representation of import/export :ref:`names ` and the :ref:`text format `. 89 | 90 | However, to make this specification self-contained, relevant aspects of the aforementioned standards are defined and formalized as part of this specification, 91 | such as the :ref:`binary representation ` and :ref:`rounding ` of floating-point values, and the :ref:`value range ` and :ref:`UTF-8 encoding ` of Unicode characters. 92 | 93 | .. note:: 94 | The aforementioned standards are the authoritative source of all respective definitions. 95 | Formalizations given in this specification are intended to match these definitions. 96 | Any discrepancy in the syntax or semantics described is to be considered an error. 97 | -------------------------------------------------------------------------------- /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/funclets/26d82a6a31fb78b2a035be9feb0b72d9091cbcaf/document/core/static/webassembly.png -------------------------------------------------------------------------------- /document/core/syntax/conventions.rst: -------------------------------------------------------------------------------- 1 | .. index:: ! abstract syntax 2 | 3 | Conventions 4 | ----------- 5 | 6 | WebAssembly is a programming language that has multiple concrete representations 7 | (its :ref:`binary format ` and the :ref:`text format `). 8 | Both map to a common structure. 9 | For conciseness, this structure is described in the form of an *abstract syntax*. 10 | All parts of this specification are defined in terms of this abstract syntax. 11 | 12 | 13 | .. index:: ! grammar notation, notation 14 | single: abstract syntax; grammar 15 | pair: abstract syntax; notation 16 | .. _grammar: 17 | 18 | Grammar Notation 19 | ~~~~~~~~~~~~~~~~ 20 | 21 | The following conventions are adopted in defining grammar rules for abstract syntax. 22 | 23 | * Terminal symbols (atoms) are written in sans-serif font: :math:`\K{i32}, \K{end}`. 24 | 25 | * Nonterminal symbols are written in italic font: :math:`\X{valtype}, \X{instr}`. 26 | 27 | * :math:`A^n` is a sequence of :math:`n\geq 0` iterations of :math:`A`. 28 | 29 | * :math:`A^\ast` is a possibly empty sequence of iterations of :math:`A`. 30 | (This is a shorthand for :math:`A^n` used where :math:`n` is not relevant.) 31 | 32 | * :math:`A^+` is a non-empty sequence of iterations of :math:`A`. 33 | (This is a shorthand for :math:`A^n` where :math:`n \geq 1`.) 34 | 35 | * :math:`A^?` is an optional occurrence of :math:`A`. 36 | (This is a shorthand for :math:`A^n` where :math:`n \leq 1`.) 37 | 38 | * Productions are written :math:`\X{sym} ::= A_1 ~|~ \dots ~|~ A_n`. 39 | 40 | * Large productions may be split into multiple definitions, indicated by ending the first one with explicit ellipses, :math:`\X{sym} ::= A_1 ~|~ \dots`, and starting continuations with ellipses, :math:`\X{sym} ::= \dots ~|~ A_2`. 41 | 42 | * Some productions are augmented with side conditions in parentheses, ":math:`(\iff \X{condition})`", that provide a shorthand for a combinatorial expansion of the production into many separate cases. 43 | 44 | 45 | .. _notation-epsilon: 46 | .. _notation-length: 47 | .. _notation-index: 48 | .. _notation-slice: 49 | .. _notation-replace: 50 | .. _notation-record: 51 | .. _notation-project: 52 | .. _notation-concat: 53 | .. _notation-compose: 54 | 55 | Auxiliary Notation 56 | ~~~~~~~~~~~~~~~~~~ 57 | 58 | When dealing with syntactic constructs the following notation is also used: 59 | 60 | * :math:`\epsilon` denotes the empty sequence. 61 | 62 | * :math:`|s|` denotes the length of a sequence :math:`s`. 63 | 64 | * :math:`s[i]` denotes the :math:`i`-th element of a sequence :math:`s`, starting from :math:`0`. 65 | 66 | * :math:`s[i \slice n]` denotes the sub-sequence :math:`s[i]~\dots~s[i+n-1]` of a sequence :math:`s`. 67 | 68 | * :math:`s \with [i] = A` denotes the same sequence as :math:`s`, 69 | except that the :math:`i`-th element is replaced with :math:`A`. 70 | 71 | * :math:`s \with [i \slice n] = A^n` denotes the same sequence as :math:`s`, 72 | except that the sub-sequence :math:`s[i \slice n]` is replaced with :math:`A^n`. 73 | 74 | * :math:`\concat(s^\ast)` denotes the flat sequence formed by concatenating all sequences :math:`s_i` in :math:`s^\ast`. 75 | 76 | Moreover, the following conventions are employed: 77 | 78 | * The notation :math:`x^n`, where :math:`x` is a non-terminal symbol, is treated as a meta variable ranging over respective sequences of :math:`x` (similarly for :math:`x^\ast`, :math:`x^+`, :math:`x^?`). 79 | 80 | * When given a sequence :math:`x^n`, 81 | then the occurrences of :math:`x` in a sequence written :math:`(A_1~x~A_2)^n` are assumed to be in point-wise correspondence with :math:`x^n` 82 | (similarly for :math:`x^\ast`, :math:`x^+`, :math:`x^?`). 83 | This implicitly expresses a form of mapping syntactic constructions over a sequence. 84 | 85 | Productions of the following form are interpreted as *records* that map a fixed set of fields :math:`\K{field}_i` to "values" :math:`A_i`, respectively: 86 | 87 | .. math:: 88 | \X{r} ~::=~ \{ \K{field}_1~A_1, \K{field}_2~A_2, \dots \} 89 | 90 | The following notation is adopted for manipulating such records: 91 | 92 | * :math:`r.\K{field}` denotes the contents of the :math:`\K{field}` component of :math:`r`. 93 | 94 | * :math:`r \with \K{field} = A` denotes the same record as :math:`r`, 95 | except that the contents of the :math:`\K{field}` component is replaced with :math:`A`. 96 | 97 | * :math:`r_1 \compose r_2` denotes the composition of two records with the same fields of sequences by appending each sequence point-wise: 98 | 99 | .. math:: 100 | \{ \K{field}_1\,A_1^\ast, \K{field}_2\,A_2^\ast, \dots \} \compose \{ \K{field}_1\,B_1^\ast, \K{field}_2\,B_2^\ast, \dots \} = \{ \K{field}_1\,A_1^\ast~B_1^\ast, \K{field}_2\,A_2^\ast~B_2^\ast, \dots \} 101 | 102 | * :math:`\bigcompose r^\ast` denotes the composition of a sequence of records, respectively; if the sequence is empty, then all fields of the resulting record are empty. 103 | 104 | The update notation for sequences and records generalizes recursively to nested components accessed by "paths" :math:`\X{pth} ::= ([\dots] \;| \;.\K{field})^+`: 105 | 106 | * :math:`s \with [i]\,\X{pth} = A` is short for :math:`s \with [i] = (s[i] \with \X{pth} = A)`. 107 | 108 | * :math:`r \with \K{field}\,\X{pth} = A` is short for :math:`r \with \K{field} = (r.\K{field} \with \X{pth} = A)`. 109 | 110 | where :math:`r \with~.\K{field} = A` is shortened to :math:`r \with \K{field} = A`. 111 | 112 | 113 | .. index:: ! vector 114 | pair: abstract syntax; vector 115 | .. _syntax-vec: 116 | 117 | Vectors 118 | ~~~~~~~ 119 | 120 | *Vectors* are bounded sequences of the form :math:`A^n` (or :math:`A^\ast`), 121 | where the :math:`A` can either be values or complex constructions. 122 | A vector can have at most :math:`2^{32}-1` elements. 123 | 124 | .. math:: 125 | \begin{array}{lllll} 126 | \production{vector} & \vec(A) &::=& 127 | A^n 128 | & (\iff n < 2^{32})\\ 129 | \end{array} 130 | -------------------------------------------------------------------------------- /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/lexical.rst: -------------------------------------------------------------------------------- 1 | .. index:: lexical format 2 | .. _text-lexical: 3 | 4 | Lexical Format 5 | -------------- 6 | 7 | 8 | .. index:: ! character, Unicode, ASCII, code point, ! source text 9 | pair: text format; character 10 | .. _source: 11 | .. _text-source: 12 | .. _text-char: 13 | 14 | Characters 15 | ~~~~~~~~~~ 16 | 17 | The text format assigns meaning to *source text*, which consists of a sequence of *characters*. 18 | Characters are assumed to be represented as valid |Unicode|_ (Section 2.4) *code points*. 19 | 20 | .. math:: 21 | \begin{array}{llll} 22 | \production{source} & \Tsource &::=& 23 | \Tchar^\ast \\ 24 | \production{character} & \Tchar &::=& 25 | \unicode{00} ~|~ \dots ~|~ \unicode{D7FF} ~|~ \unicode{E000} ~|~ \dots ~|~ \unicode{10FFFF} \\ 26 | \end{array} 27 | 28 | .. note:: 29 | While source text may contain any Unicode character in :ref:`comments ` or :ref:`string ` literals, 30 | the rest of the grammar is formed exclusively from the characters supported by the 7-bit |ASCII|_ subset of Unicode. 31 | 32 | 33 | .. index:: ! token, ! keyword, character, white space, comment, source text 34 | single: text format; token 35 | .. _text-keyword: 36 | .. _text-reserved: 37 | .. _text-token: 38 | 39 | Tokens 40 | ~~~~~~ 41 | 42 | The character stream in the source text is divided, from left to right, into a sequence of *tokens*, as defined by the following grammar. 43 | 44 | .. math:: 45 | \begin{array}{llll} 46 | \production{token} & \Ttoken &::=& 47 | \Tkeyword ~|~ \TuN ~|~ \TsN ~|~ \TfN ~|~ \Tstring ~|~ \Tid ~|~ 48 | \text{(} ~|~ \text{)} ~|~ \Treserved \\ 49 | \production{keyword} & \Tkeyword &::=& 50 | (\text{a} ~|~ \dots ~|~ \text{z})~\Tidchar^\ast 51 | \qquad (\mbox{if occurring as a literal terminal in the grammar}) \\ 52 | \production{reserved} & \Treserved &::=& 53 | \Tidchar^+ \\ 54 | \end{array} 55 | 56 | Tokens are formed from the input character stream according to the *longest match* rule. 57 | That is, the next token always consists of the longest possible sequence of characters that is recognized by the above lexical grammar. 58 | Tokens can be separated by :ref:`white space `, 59 | but except for strings, they cannot themselves contain whitespace. 60 | 61 | The set of *keyword* tokens is defined implicitly, by all occurrences of a :ref:`terminal symbol ` in literal form, such as :math:`\text{keyword}`, in a :ref:`syntactic ` production of this chapter. 62 | 63 | Any token that does not fall into any of the other categories is considered *reserved*, and cannot occur in source text. 64 | 65 | .. note:: 66 | The effect of defining the set of reserved tokens is that all tokens must be separated by either parentheses or :ref:`white space `. 67 | For example, :math:`\text{0\$x}` is a single reserved token. 68 | Consequently, it is not recognized as two separate tokens :math:`\text{0}` and :math:`\text{\$x}`, but instead disallowed. 69 | This property of tokenization is not affected by the fact that the definition of reserved tokens overlaps with other token classes. 70 | 71 | 72 | .. index:: ! white space, character, ASCII 73 | single: text format; white space 74 | .. _text-format: 75 | .. _text-space: 76 | 77 | White Space 78 | ~~~~~~~~~~~ 79 | 80 | *White space* is any sequence of literal space characters, formatting characters, or :ref:`comments `. 81 | The allowed formatting characters correspond to a subset of the |ASCII|_ *format effectors*, namely, *horizontal tabulation* (:math:`\unicode{09}`), *line feed* (:math:`\unicode{0A}`), and *carriage return* (:math:`\unicode{0D}`). 82 | 83 | .. math:: 84 | \begin{array}{llclll@{\qquad\qquad}l} 85 | \production{white space} & \Tspace &::=& 86 | (\text{~~} ~|~ \Tformat ~|~ \Tcomment)^\ast \\ 87 | \production{format} & \Tformat &::=& 88 | \unicode{09} ~|~ \unicode{0A} ~|~ \unicode{0D} \\ 89 | \end{array} 90 | 91 | The only relevance of white space is to separate :ref:`tokens `. It is otherwise ignored. 92 | 93 | 94 | .. index:: ! comment, character 95 | single: text format; comment 96 | .. _text-comment: 97 | 98 | Comments 99 | ~~~~~~~~ 100 | 101 | A *comment* can either be a *line comment*, started with a double semicolon :math:`\Tcommentd` and extending to the end of the line, 102 | or a *block comment*, enclosed in delimiters :math:`\Tcommentl \dots \Tcommentr`. 103 | Block comments can be nested. 104 | 105 | .. math:: 106 | \begin{array}{llclll@{\qquad\qquad}l} 107 | \production{comment} & \Tcomment &::=& 108 | \Tlinecomment ~|~ \Tblockcomment \\ 109 | \production{line comment} & \Tlinecomment &::=& 110 | \Tcommentd~~\Tlinechar^\ast~~(\unicode{0A} ~|~ \T{eof}) \\ 111 | \production{line character} & \Tlinechar &::=& 112 | c{:}\Tchar & (\iff c \neq \unicode{0A}) \\ 113 | \production{block comment} & \Tblockcomment &::=& 114 | \Tcommentl~~\Tblockchar^\ast~~\Tcommentr \\ 115 | \production{block character} & \Tblockchar &::=& 116 | c{:}\Tchar & (\iff c \neq \text{;} \wedge c \neq \text{(}) \\ &&|& 117 | \text{;} & (\iff~\mbox{the next character is not}~\text{)}) \\ &&|& 118 | \text{(} & (\iff~\mbox{the next character is not}~\text{;}) \\ &&|& 119 | \Tblockcomment \\ 120 | \end{array} 121 | 122 | Here, the pseudo token :math:`\T{eof}` indicates the end of the input. 123 | The *look-ahead* restrictions on the productions for |Tblockchar| disambiguate the grammar such that only well-bracketed uses of block comment delimiters are allowed. 124 | 125 | .. note:: 126 | Any formatting and control characters are allowed inside comments. 127 | -------------------------------------------------------------------------------- /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:: result type, value type 26 | pair: text format; result type 27 | .. _text-resulttype: 28 | 29 | Result Types 30 | ~~~~~~~~~~~~ 31 | 32 | .. math:: 33 | \begin{array}{llclll@{\qquad\qquad}l} 34 | \production{result type} & \Tresulttype &::=& 35 | (t{:}\Tresult)^? &\Rightarrow& [t^?] \\ 36 | \end{array} 37 | 38 | .. note:: 39 | In future versions of WebAssembly, this scheme may be extended to support multiple results or more general result types. 40 | 41 | 42 | .. index:: function type, value type, result type 43 | pair: text format; function type 44 | .. _text-param: 45 | .. _text-result: 46 | .. _text-functype: 47 | 48 | Function Types 49 | ~~~~~~~~~~~~~~ 50 | 51 | .. math:: 52 | \begin{array}{llclll@{\qquad\qquad}l} 53 | \production{function type} & \Tfunctype &::=& 54 | \text{(}~\text{func}~~t_1^\ast{:\,}\Tvec(\Tparam)~~t_2^\ast{:\,}\Tvec(\Tresult)~\text{)} 55 | &\Rightarrow& [t_1^\ast] \to [t_2^\ast] \\ 56 | \production{parameter} & \Tparam &::=& 57 | \text{(}~\text{param}~~\Tid^?~~t{:}\Tvaltype~\text{)} 58 | &\Rightarrow& t \\ 59 | \production{result} & \Tresult &::=& 60 | \text{(}~\text{result}~~t{:}\Tvaltype~\text{)} 61 | &\Rightarrow& t \\ 62 | \end{array} 63 | 64 | Abbreviations 65 | ............. 66 | 67 | Multiple anonymous parameters or results may be combined into a single declaration: 68 | 69 | .. math:: 70 | \begin{array}{llclll} 71 | \production{parameter} & 72 | \text{(}~~\text{param}~~\Tvaltype^\ast~~\text{)} &\equiv& 73 | (\text{(}~~\text{param}~~\Tvaltype~~\text{)})^\ast \\ 74 | \production{result} & 75 | \text{(}~~\text{result}~~\Tvaltype^\ast~~\text{)} &\equiv& 76 | (\text{(}~~\text{result}~~\Tvaltype~~\text{)})^\ast \\ 77 | \end{array} 78 | 79 | 80 | .. index:: limits 81 | pair: text format; limits 82 | .. _text-limits: 83 | 84 | Limits 85 | ~~~~~~ 86 | 87 | .. math:: 88 | \begin{array}{llclll} 89 | \production{limits} & \Tlimits &::=& 90 | n{:}\Tu32 &\Rightarrow& \{ \LMIN~n, \LMAX~\epsilon \} \\ &&|& 91 | n{:}\Tu32~~m{:}\Tu32 &\Rightarrow& \{ \LMIN~n, \LMAX~m \} \\ 92 | \end{array} 93 | 94 | 95 | .. index:: memory type, limits, page size 96 | pair: text format; memory type 97 | .. _text-memtype: 98 | 99 | Memory Types 100 | ~~~~~~~~~~~~ 101 | 102 | .. math:: 103 | \begin{array}{llclll@{\qquad\qquad}l} 104 | \production{memory type} & \Tmemtype &::=& 105 | \X{lim}{:}\Tlimits &\Rightarrow& \X{lim} \\ 106 | \end{array} 107 | 108 | 109 | .. index:: table type, element type, limits 110 | pair: text format; table type 111 | pair: text format; element type 112 | .. _text-elemtype: 113 | .. _text-tabletype: 114 | 115 | Table Types 116 | ~~~~~~~~~~~ 117 | 118 | .. math:: 119 | \begin{array}{llclll} 120 | \production{table type} & \Ttabletype &::=& 121 | \X{lim}{:}\Tlimits~~\X{et}{:}\Telemtype &\Rightarrow& \X{lim}~\X{et} \\ 122 | \production{element type} & \Telemtype &::=& 123 | \text{anyfunc} &\Rightarrow& \ANYFUNC \\ 124 | \end{array} 125 | 126 | .. note:: 127 | Additional element types may be introduced in future versions of WebAssembly. 128 | 129 | 130 | .. index:: global type, mutability, value type 131 | pair: text format; global type 132 | pair: text format; mutability 133 | .. _text-globaltype: 134 | 135 | Global Types 136 | ~~~~~~~~~~~~ 137 | 138 | .. math:: 139 | \begin{array}{llclll} 140 | \production{global type} & \Tglobaltype &::=& 141 | t{:}\Tvaltype &\Rightarrow& \MCONST~t \\ &&|& 142 | \text{(}~\text{mut}~~t{:}\Tvaltype~\text{)} &\Rightarrow& \MVAR~t \\ 143 | \end{array} 144 | -------------------------------------------------------------------------------- /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 python 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 | sys.stdout.write(data) 49 | 50 | 51 | Main() 52 | -------------------------------------------------------------------------------- /document/core/util/katex_fix.patch: -------------------------------------------------------------------------------- 1 | 113c113 2 | < .katex-display { 3 | --- 4 | > div > .katex-display { 5 | 123c123,126 6 | < font: normal 1.21em KaTeX_Main, Times New Roman, serif; 7 | --- 8 | > /* font: normal 1.21em KaTeX_Main, Times New Roman, serif; */ 9 | > font-weight: normal; 10 | > font-size: 1.21em; 11 | > font-family: KaTeX_Main, Times New Roman, serif; 12 | 127d129 13 | < text-rendering: auto; 14 | 133c135 15 | < display: inline-block; 16 | --- 17 | > /* display: inline-block; */ 18 | 1060,1065d1061 19 | < .katex svg path { 20 | < fill: currentColor; 21 | < } 22 | < .katex svg line { 23 | < stroke: currentColor; 24 | < } 25 | 1142a1139,1152 26 | > } 27 | > /* Force borders on tables */ 28 | > table { 29 | > border-collapse: collapse; 30 | > } 31 | > .docutils th, td { 32 | > border: 1px solid; 33 | > padding: .4em; 34 | > } 35 | > .footnote td { 36 | > border: 0; 37 | > } 38 | > .codepre { 39 | > white-space: pre; 40 | -------------------------------------------------------------------------------- /document/core/util/mathdef.py: -------------------------------------------------------------------------------- 1 | from sphinx.ext.mathbase import math 2 | from sphinx.ext.mathbase import displaymath 3 | from sphinx.ext.mathbase import math_role 4 | from sphinx.ext.mathbase import MathDirective 5 | from sphinx.ext.mathbase import latex_visit_math 6 | from sphinx.ext.mathbase import latex_visit_displaymath 7 | from sphinx.ext.mathjax import html_visit_math 8 | from sphinx.ext.mathjax import html_visit_displaymath 9 | from sphinx.util.texescape import tex_escape_map, tex_replace_map 10 | from docutils.parsers.rst.directives.misc import Replace 11 | from six import text_type 12 | import re 13 | 14 | 15 | # Transform \xref in math nodes 16 | 17 | xref_re = re.compile('\\\\xref\{([^}]*)\}\{([^}]*)\}', re.M) 18 | 19 | def html_hyperlink(file, id): 20 | return '\\href{../%s.html#%s}' % (file, id.replace('_', '-')) 21 | 22 | def html_transform_math_xref(node): 23 | node['latex'] = \ 24 | xref_re.sub(lambda m: html_hyperlink(m.group(1), m.group(2)), node['latex']) 25 | 26 | def ext_html_visit_math(self, node): 27 | html_transform_math_xref(node) 28 | html_visit_math(self, node) 29 | 30 | def ext_html_visit_displaymath(self, node): 31 | html_transform_math_xref(node) 32 | html_visit_displaymath(self, node) 33 | 34 | # Mirrors sphinx/writers/latex 35 | def latex_hyperlink(file, id): 36 | id = text_type(id).translate(tex_replace_map).\ 37 | encode('ascii', 'backslashreplace').decode('ascii').\ 38 | replace('_', '-').replace('\\', '_') 39 | return '\\hyperref[%s:%s]' % (file, id) 40 | 41 | def latex_transform_math_xref(node): 42 | node['latex'] = \ 43 | xref_re.sub(lambda m: latex_hyperlink(m.group(1), m.group(2)), node['latex']) 44 | 45 | def ext_latex_visit_math(self, node): 46 | latex_transform_math_xref(node) 47 | latex_visit_math(self, node) 48 | 49 | def ext_latex_visit_displaymath(self, node): 50 | latex_transform_math_xref(node) 51 | latex_visit_displaymath(self, node) 52 | 53 | 54 | # Expand mathdef names in math roles and directives 55 | 56 | def_re = re.compile('\\\\[A-Za-z][0-9A-Za-z]*', re.M) 57 | 58 | auxcounter = 0 59 | 60 | def lookup_mathdef(defs, name): 61 | if name in defs: 62 | [arity, s] = defs[name] 63 | if arity > 0: 64 | global auxcounter 65 | auxcounter = auxcounter + 1 66 | name = "\\mathdef%d" % auxcounter 67 | s = "\\def%s#%d{%s}%s" % (name, arity, s, name) 68 | return s 69 | return name 70 | 71 | def replace_mathdefs(doc, s): 72 | if not hasattr(doc, 'mathdefs'): 73 | return s 74 | return def_re.sub(lambda m: lookup_mathdef(doc.mathdefs, m.group(0)), s) 75 | 76 | def ext_math_role(role, raw, text, line, inliner, options = {}, content = []): 77 | text = replace_mathdefs(inliner.document, raw.split('`')[1]) 78 | return math_role(role, raw, text, line, inliner, options = options, 79 | content = content) 80 | 81 | class ExtMathDirective(MathDirective): 82 | def run(self): 83 | doc = self.state.document 84 | for i, s in enumerate(self.content): 85 | self.content[i] = replace_mathdefs(doc, s) 86 | for i, s in enumerate(self.arguments): 87 | self.arguments[i] = replace_mathdefs(doc, s) 88 | return super(ExtMathDirective, self).run() 89 | 90 | class MathdefDirective(Replace): 91 | def run(self): 92 | name = '\\' + self.state.parent.rawsource.split('|')[1] 93 | name = name.split('#') 94 | if len(name) > 1: 95 | arity = int(name[1]) 96 | else: 97 | arity = 0 98 | name = name[0] 99 | doc = self.state.document 100 | if not hasattr(doc, 'mathdefs'): 101 | doc.mathdefs = {} 102 | for i, s in enumerate(self.content): 103 | self.content[i] = replace_mathdefs(doc, s) 104 | doc.mathdefs[name] = [arity, ''.join(self.content)] 105 | self.content[0] = ':math:`' + self.content[0] 106 | self.content[-1] = self.content[-1] + '`' 107 | return super(MathdefDirective, self).run() 108 | 109 | 110 | # Setup 111 | 112 | def setup(app): 113 | app.add_node(math, 114 | html = (ext_html_visit_math, None), 115 | latex = (ext_latex_visit_math, None)) 116 | app.add_node(displaymath, 117 | html = (ext_html_visit_displaymath, None), 118 | latex = (ext_latex_visit_displaymath, None)) 119 | app.add_role('math', ext_math_role) 120 | app.add_directive('math', ExtMathDirective) 121 | app.add_directive('mathdef', MathdefDirective) 122 | -------------------------------------------------------------------------------- /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 displaymath 8 | from sphinx.ext.mathbase import math_role 9 | from sphinx.ext.mathbase import MathDirective 10 | from sphinx.ext.mathbase import latex_visit_math 11 | from sphinx.ext.mathbase import latex_visit_displaymath 12 | from sphinx.ext.mathjax import html_visit_math 13 | from sphinx.ext.mathjax import html_visit_displaymath 14 | from sphinx.util.texescape import tex_escape_map, tex_replace_map 15 | from docutils.parsers.rst.directives.misc import Replace 16 | from six import text_type 17 | import re 18 | 19 | 20 | # Transform \xref in math nodes 21 | 22 | xref_re = re.compile('\\\\xref\{([^}]*)\}\{([^}]*)\}', re.M) 23 | 24 | def html_hyperlink(file, id): 25 | return '\\href{#%s}' % (id.replace('_', '-')) 26 | 27 | def html_transform_math_xref(node): 28 | node['latex'] = \ 29 | xref_re.sub(lambda m: html_hyperlink(m.group(1), m.group(2)), node['latex']) 30 | 31 | def ext_html_visit_math(self, node): 32 | html_transform_math_xref(node) 33 | html_visit_math(self, node) 34 | 35 | def ext_html_visit_displaymath(self, node): 36 | html_transform_math_xref(node) 37 | html_visit_displaymath(self, node) 38 | 39 | # Mirrors sphinx/writers/latex 40 | def latex_hyperlink(file, id): 41 | id = text_type(id).translate(tex_replace_map).\ 42 | encode('ascii', 'backslashreplace').decode('ascii').\ 43 | replace('_', '-').replace('\\', '_') 44 | return '\\hyperref[%s:%s]' % (file, id) 45 | 46 | def latex_transform_math_xref(node): 47 | node['latex'] = \ 48 | xref_re.sub(lambda m: latex_hyperlink(m.group(1), m.group(2)), node['latex']) 49 | 50 | def ext_latex_visit_math(self, node): 51 | latex_transform_math_xref(node) 52 | latex_visit_math(self, node) 53 | 54 | def ext_latex_visit_displaymath(self, node): 55 | latex_transform_math_xref(node) 56 | latex_visit_displaymath(self, node) 57 | 58 | 59 | # Expand mathdef names in math roles and directives 60 | 61 | def_re = re.compile('\\\\[A-Za-z][0-9A-Za-z]*', re.M) 62 | 63 | auxcounter = 0 64 | 65 | def lookup_mathdef(defs, name): 66 | if name in defs: 67 | [arity, s] = defs[name] 68 | if arity > 0: 69 | global auxcounter 70 | auxcounter = auxcounter + 1 71 | name = "\\mathdef%d" % auxcounter 72 | s = "\\def%s#%d{%s}%s" % (name, arity, s, name) 73 | return s 74 | return name 75 | 76 | def replace_mathdefs(doc, s): 77 | if not hasattr(doc, 'mathdefs'): 78 | return s 79 | return def_re.sub(lambda m: lookup_mathdef(doc.mathdefs, m.group(0)), s) 80 | 81 | def ext_math_role(role, raw, text, line, inliner, options = {}, content = []): 82 | text = replace_mathdefs(inliner.document, raw.split('`')[1]) 83 | return math_role(role, raw, text, line, inliner, options = options, 84 | content = content) 85 | 86 | class ExtMathDirective(MathDirective): 87 | def run(self): 88 | doc = self.state.document 89 | for i, s in enumerate(self.content): 90 | self.content[i] = replace_mathdefs(doc, s) 91 | for i, s in enumerate(self.arguments): 92 | self.arguments[i] = replace_mathdefs(doc, s) 93 | return super(ExtMathDirective, self).run() 94 | 95 | class MathdefDirective(Replace): 96 | def run(self): 97 | name = '\\' + self.state.parent.rawsource.split('|')[1] 98 | name = name.split('#') 99 | if len(name) > 1: 100 | arity = int(name[1]) 101 | else: 102 | arity = 0 103 | name = name[0] 104 | doc = self.state.document 105 | if not hasattr(doc, 'mathdefs'): 106 | doc.mathdefs = {} 107 | for i, s in enumerate(self.content): 108 | self.content[i] = replace_mathdefs(doc, s) 109 | doc.mathdefs[name] = [arity, ''.join(self.content)] 110 | self.content[0] = ':math:`' + self.content[0] 111 | self.content[-1] = self.content[-1] + '`' 112 | return super(MathdefDirective, self).run() 113 | 114 | 115 | # Setup 116 | 117 | def setup(app): 118 | app.add_node(math, 119 | html = (ext_html_visit_math, None), 120 | latex = (ext_latex_visit_math, None)) 121 | app.add_node(displaymath, 122 | html = (ext_html_visit_displaymath, None), 123 | latex = (ext_latex_visit_displaymath, None)) 124 | app.add_role('math', ext_math_role) 125 | app.add_directive('math', ExtMathDirective) 126 | app.add_directive('mathdef', MathdefDirective) 127 | -------------------------------------------------------------------------------- /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"(?` are universally valid. 5 | However, restrictions apply to :ref:`function types ` as well as the :ref:`limits ` of :ref:`table types ` and :ref:`memory types `, which must be checked during validation. 6 | 7 | 8 | .. index:: limits 9 | pair: validation; limits 10 | single: abstract syntax; limits 11 | .. _valid-limits: 12 | 13 | Limits 14 | ~~~~~~ 15 | 16 | :ref:`Limits ` must have meaningful bounds that are within a given range. 17 | 18 | :math:`\{ \LMIN~n, \LMAX~m^? \}` 19 | ................................ 20 | 21 | * The value of :math:`n` must not be larger than :math:`k`. 22 | 23 | * If the maximum :math:`m^?` is not empty, then: 24 | 25 | * Its value must not be larger than :math:`k`. 26 | 27 | * Its value must not be smaller than :math:`n`. 28 | 29 | * Then the limit is valid within range :math:`k`. 30 | 31 | .. math:: 32 | \frac{ 33 | n \leq k 34 | \qquad 35 | (m \leq k)^? 36 | \qquad 37 | (n \leq m)^? 38 | }{ 39 | \vdashlimits \{ \LMIN~n, \LMAX~m^? \} : k 40 | } 41 | 42 | 43 | .. index:: function type 44 | pair: validation; function type 45 | single: abstract syntax; function type 46 | .. _valid-functype: 47 | 48 | Function Types 49 | ~~~~~~~~~~~~~~ 50 | 51 | :ref:`Function types ` may not specify more than one result. 52 | 53 | :math:`[t_1^n] \to [t_2^m]` 54 | ........................... 55 | 56 | * The arity :math:`m` must not be larger than :math:`1`. 57 | 58 | * Then the function type is valid. 59 | 60 | .. math:: 61 | \frac{ 62 | }{ 63 | \vdashfunctype [t_1^\ast] \to [t_2^?] \ok 64 | } 65 | 66 | .. note:: 67 | The restriction to at most one result may be removed in future versions of WebAssembly. 68 | 69 | 70 | .. index:: table type, element type, limits 71 | pair: validation; table type 72 | single: abstract syntax; table type 73 | .. _valid-tabletype: 74 | 75 | Table Types 76 | ~~~~~~~~~~~ 77 | 78 | :math:`\limits~\elemtype` 79 | ......................... 80 | 81 | * The limits :math:`\limits` must be :ref:`valid ` within range :math:`2^{32}`. 82 | 83 | * Then the table type is valid. 84 | 85 | .. math:: 86 | \frac{ 87 | \vdashlimits \limits : 2^{32} 88 | }{ 89 | \vdashtabletype \limits~\elemtype \ok 90 | } 91 | 92 | 93 | .. index:: memory type, limits 94 | pair: validation; memory type 95 | single: abstract syntax; memory type 96 | .. _valid-memtype: 97 | 98 | Memory Types 99 | ~~~~~~~~~~~~ 100 | 101 | :math:`\limits` 102 | ............... 103 | 104 | * The limits :math:`\limits` must be :ref:`valid ` within range :math:`2^{16}`. 105 | 106 | * Then the memory type is valid. 107 | 108 | .. math:: 109 | \frac{ 110 | \vdashlimits \limits : 2^{16} 111 | }{ 112 | \vdashmemtype \limits \ok 113 | } 114 | 115 | 116 | .. index:: global type, value type, mutability 117 | pair: validation; global type 118 | single: abstract syntax; global type 119 | .. _valid-globaltype: 120 | 121 | Global Types 122 | ~~~~~~~~~~~~ 123 | 124 | :math:`\mut~\valtype` 125 | ..................... 126 | 127 | * The global type is valid. 128 | 129 | .. math:: 130 | \frac{ 131 | }{ 132 | \vdashglobaltype \mut~\valtype \ok 133 | } 134 | -------------------------------------------------------------------------------- /document/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | WebAssembly Specifications 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 |
16 | 17 |

WebAssembly Specifications

18 | 19 |

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

22 | 23 |

Core specification

24 | 25 |

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

27 | 28 |
    29 |
  • 30 |

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

    35 | 41 |
  • 42 |
43 | 44 |

Embedder specifications

45 | 46 |

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

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

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

    58 | 62 |
  • 63 |
64 | 65 |

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

69 | 70 |
71 |
72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /document/js-api/Makefile: -------------------------------------------------------------------------------- 1 | BUILDDIR = _build 2 | STATICDIR = _static 3 | DOWNLOADDIR = _download 4 | NAME = WebAssembly 5 | 6 | .PHONY: all 7 | all: 8 | mkdir -p $(BUILDDIR)/html 9 | bikeshed spec index.bs $(BUILDDIR)/html/index.html 10 | @echo "Build finished. The HTML pages are in `pwd`/$(BUILDDIR)/html." 11 | 12 | .PHONY: publish 13 | publish: 14 | (cd ..; make publish-js-api) 15 | 16 | .PHONY: clean 17 | clean: 18 | rm -rf $(BUILDDIR) 19 | rm -rf $(STATICDIR) 20 | 21 | .PHONY: diff 22 | diff: all 23 | @echo "Downloading the old single-file html spec..." 24 | curl `grep "^TR" index.bs | cut -d' ' -f2` -o $(BUILDDIR)/html/old.html 25 | @echo "Done." 26 | @echo "Diffing new against old..." 27 | perl ../util/htmldiff.pl $(BUILDDIR)/html/old.html $(BUILDDIR)/html/index.html $(BUILDDIR)/html/diff.html 28 | @echo "Done. The diff is at $(BUILDDIR)/html/diff.html" 29 | 30 | .PHONY: WD-tar 31 | WD-tar: 32 | bikeshed echidna --just-tar index.bs $(BUILDDIR)/html/index.html 33 | mv test.tar $(BUILDDIR)/WD.tar 34 | @echo "Built $(BUILDDIR)/WD.tar." 35 | 36 | .PHONY: WD-echidna 37 | WD-echidna: 38 | @if [ -z $(W3C_USERNAME) ] || \ 39 | [ -z $(W3C_PASSWORD) ] || \ 40 | [ -z $(DECISION_URL) ] ; then \ 41 | echo "Must provide W3C_USERNAME, W3C_PASSWORD, and DECISION_URL environment variables"; \ 42 | exit 1; \ 43 | fi 44 | bikeshed echidna index.bs --u $(W3C_USERNAME) --p $(W3C_PASSWORD) --d $(DECISION_URL) 45 | -------------------------------------------------------------------------------- /document/travis-deploy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Adapted from https://github.com/heycam/webidl/blob/master/deploy.sh 4 | 5 | set -e # Exit with nonzero exit code if anything fails 6 | 7 | SOURCE_BRANCH="master" 8 | TARGET_BRANCH="gh-pages" 9 | 10 | function doCompile { 11 | # TODO(littledan): Integrate with document/deploy.sh 12 | (cd document; make) 13 | } 14 | 15 | # Pull requests and commits to other branches shouldn't try to deploy, just build to verify 16 | if [[ "$TRAVIS_PULL_REQUEST" != "false" || "$TRAVIS_BRANCH" != "$SOURCE_BRANCH" ]]; then 17 | echo "Skipping deploy; just doing a build." 18 | doCompile 19 | exit 0 20 | fi 21 | 22 | # Save some useful information 23 | REPO=`git config remote.origin.url` 24 | SSH_REPO=${REPO/https:\/\/github.com\//git@github.com:} 25 | SHA=`git rev-parse --verify HEAD` 26 | 27 | # Get the deploy key by using Travis's stored variables to decrypt deploy_key.enc 28 | ENCRYPTED_KEY_VAR="encrypted_${ENCRYPTION_LABEL}_key" 29 | ENCRYPTED_IV_VAR="encrypted_${ENCRYPTION_LABEL}_iv" 30 | ENCRYPTED_KEY=${!ENCRYPTED_KEY_VAR} 31 | ENCRYPTED_IV=${!ENCRYPTED_IV_VAR} 32 | openssl aes-256-cbc -K $ENCRYPTED_KEY -iv $ENCRYPTED_IV -in deploy_key.enc -out deploy_key -d || true 33 | chmod 600 deploy_key 34 | eval `ssh-agent -s` 35 | ssh-add deploy_key || true 36 | 37 | # DON'T MOVE ABOVE AS IT WILL REVEAL KEYS! 38 | # Turn on logging from here on. 39 | set -x 40 | 41 | # Clone the existing gh-pages for this repo into _build/ 42 | # Create a new empty branch if gh-pages doesn't exist yet (should only happen 43 | # on first deploy). 44 | # Clean document/*/_build. 45 | (cd document; make clean) 46 | # Ensure no checkout in _build. 47 | rm -rf document/_build 48 | # Clone a second checkout of our repo to _build. 49 | git clone $REPO document/_build 50 | ( 51 | # Checkout a parentless branch of gh-pages. 52 | cd document/_build 53 | git checkout $TARGET_BRANCH || git checkout --orphan $TARGET_BRANCH 54 | 55 | # Clean out existing contents (to be replaced with output from the build 56 | # that happens after this). 57 | rm -rf ./* 58 | ) 59 | 60 | # Run our compile script (output into _build). 61 | doCompile 62 | 63 | # Set user info on the gh-pages repo in _build. 64 | cd document/_build 65 | git config user.name "Travis CI" 66 | git config user.email "$COMMIT_AUTHOR_EMAIL" 67 | 68 | # If there are no changes to the compiled out (e.g. this is a README update) then just bail. 69 | if [[ -z "$(git status --porcelain)" ]]; then 70 | echo "No changes to the output on this push; exiting." 71 | exit 0 72 | fi 73 | 74 | # Commit the "changes", i.e. the new version. 75 | # The delta will show diffs between new and old versions. 76 | git add --all . 77 | git commit -m "Deploy to GitHub Pages: ${SHA}" 78 | 79 | # Now that we're all set up, we can push. 80 | git push $SSH_REPO $TARGET_BRANCH 81 | -------------------------------------------------------------------------------- /document/web-api/Makefile: -------------------------------------------------------------------------------- 1 | BUILDDIR = _build 2 | STATICDIR = _static 3 | DOWNLOADDIR = _download 4 | NAME = WebAssembly 5 | 6 | .PHONY: all 7 | all: 8 | mkdir -p $(BUILDDIR)/html 9 | bikeshed spec index.bs $(BUILDDIR)/html/index.html 10 | @echo "Build finished. The HTML pages are in `pwd`/$(BUILDDIR)/html." 11 | 12 | .PHONY: publish 13 | publish: 14 | (cd ..; make publish-web-api) 15 | 16 | .PHONY: clean 17 | clean: 18 | rm -rf $(BUILDDIR) 19 | rm -rf $(STATICDIR) 20 | 21 | .PHONY: diff 22 | diff: all 23 | @echo "Downloading the old single-file html spec..." 24 | curl `grep "^TR" index.bs | cut -d' ' -f2` -o $(BUILDDIR)/html/old.html 25 | @echo "Done." 26 | @echo "Diffing new against old..." 27 | perl ../util/htmldiff.pl $(BUILDDIR)/html/old.html $(BUILDDIR)/html/index.html $(BUILDDIR)/html/diff.html 28 | @echo "Done. The diff is at $(BUILDDIR)/html/diff.html" 29 | 30 | .PHONY: WD-tar 31 | WD-tar: 32 | bikeshed echidna --just-tar index.bs $(BUILDDIR)/html/index.html 33 | mv test.tar $(BUILDDIR)/WD.tar 34 | @echo "Built $(BUILDDIR)/WD.tar." 35 | 36 | .PHONY: WD-echidna 37 | WD-echidna: 38 | @if [ -z $(W3C_USERNAME) ] || \ 39 | [ -z $(W3C_PASSWORD) ] || \ 40 | [ -z $(DECISION_URL) ] ; then \ 41 | echo "Must provide W3C_USERNAME, W3C_PASSWORD, and DECISION_URL environment variables"; \ 42 | exit 1; \ 43 | fi 44 | bikeshed echidna index.bs --u $(W3C_USERNAME) --p $(W3C_PASSWORD) --d $(DECISION_URL) 45 | -------------------------------------------------------------------------------- /interpreter/.gitignore: -------------------------------------------------------------------------------- 1 | *.cmo 2 | *.cmx 3 | *.native 4 | *.byte 5 | *.opt 6 | *.unopt 7 | *.js 8 | *.zip 9 | *.mlpack 10 | _build 11 | wasm 12 | wasm.debug 13 | 14 | -------------------------------------------------------------------------------- /interpreter/Makefile: -------------------------------------------------------------------------------- 1 | # This Makefile uses ocamlbuild but does not rely on ocamlfind or the Opam 2 | # package manager to build. However, Opam package management is available 3 | # optionally through the check/install/uninstall targets. 4 | # 5 | # The $(JSLIB) target requires node.js and BuckleScript. 6 | # 7 | # See README.me for instructions. 8 | 9 | 10 | # Configuration 11 | 12 | NAME = wasm 13 | UNOPT = $(NAME).debug 14 | OPT = $(NAME) 15 | LIB = $(NAME) 16 | ZIP = $(NAME).zip 17 | JSLIB = wast.js 18 | WINMAKE = winmake.bat 19 | 20 | DIRS = util syntax binary text valid runtime exec script host main 21 | LIBS = bigarray 22 | FLAGS = -cflags '-w +a-4-27-42-44-45 -warn-error +a' 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: all $(WINMAKE) 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.d.byte \ 106 | | grep -v ocamldep \ 107 | | grep -v mkdir \ 108 | | sed s:`which ocaml`:ocaml:g \ 109 | | sed s:host/main.d.byte:%NAME%.exe: \ 110 | >>$@ 111 | 112 | 113 | # Executing test suite 114 | 115 | .PHONY: test debugtest 116 | 117 | test: $(OPT) 118 | ../test/core/run.py --wasm `pwd`/$(OPT) $(if $(JS),--js '$(JS)',) 119 | debugtest: $(UNOPT) 120 | ../test/core/run.py --wasm `pwd`/$(UNOPT) $(if $(JS),--js '$(JS)',) 121 | 122 | test/%: $(OPT) 123 | ../test/core/run.py --wasm `pwd`/$(OPT) $(if $(JS),--js '$(JS)',) $(@:test/%=../test/core/%.wast) 124 | debugtest/%: $(UNOPT) 125 | ../test/core/run.py --wasm `pwd`/$(UNOPT) $(if $(JS),--js '$(JS)',) $(@:debugtest/%=../test/core/%.wast) 126 | 127 | run/%: $(OPT) 128 | ./$(OPT) $(@:run/%=../test/core/%.wast) 129 | debug/%: $(UNOPT) 130 | ./$(UNOPT) $(@:debug/%=../test/core/%.wast) 131 | 132 | 133 | # Miscellaneous targets 134 | 135 | .PHONY: clean 136 | 137 | $(ZIP): $(WINMAKE) 138 | git archive --format=zip --prefix=$(NAME)/ -o $@ HEAD 139 | 140 | clean: 141 | rm -rf _build/jslib $(LIB).mlpack _tags 142 | $(OCB) -clean 143 | 144 | # Opam support 145 | 146 | .PHONY: check install uninstall 147 | 148 | check: 149 | # Check that we can find all relevant libraries 150 | # when using ocamlfind 151 | ocamlfind query $(LIBS) 152 | 153 | install: _build/$(LIB).cmx _build/$(LIB).cmo 154 | ocamlfind install $(LIB) meta/findlib/META _build/$(LIB).o \ 155 | $(wildcard _build/$(LIB).cm*) \ 156 | $(wildcard $(DIRS:%=%/*.mli)) 157 | 158 | uninstall: 159 | ocamlfind remove $(LIB) 160 | -------------------------------------------------------------------------------- /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.ml: -------------------------------------------------------------------------------- 1 | open Types 2 | open Values 3 | 4 | 5 | (* Runtime type errors *) 6 | 7 | exception TypeError of int * value * value_type 8 | 9 | let of_arg f n v = 10 | try f v with Value t -> raise (TypeError (n, v, t)) 11 | 12 | 13 | (* Int operators *) 14 | 15 | module IntOp (IXX : Int.S) (Value : ValueType with type t = IXX.t) = 16 | struct 17 | open Ast.IntOp 18 | 19 | let to_value = Value.to_value 20 | let of_value = of_arg Value.of_value 21 | 22 | let unop op = 23 | let f = match op with 24 | | Clz -> IXX.clz 25 | | Ctz -> IXX.ctz 26 | | Popcnt -> IXX.popcnt 27 | in fun v -> to_value (f (of_value 1 v)) 28 | 29 | let binop op = 30 | let f = match op with 31 | | Add -> IXX.add 32 | | Sub -> IXX.sub 33 | | Mul -> IXX.mul 34 | | DivS -> IXX.div_s 35 | | DivU -> IXX.div_u 36 | | RemS -> IXX.rem_s 37 | | RemU -> IXX.rem_u 38 | | And -> IXX.and_ 39 | | Or -> IXX.or_ 40 | | Xor -> IXX.xor 41 | | Shl -> IXX.shl 42 | | ShrU -> IXX.shr_u 43 | | ShrS -> IXX.shr_s 44 | | Rotl -> IXX.rotl 45 | | Rotr -> IXX.rotr 46 | in fun v1 v2 -> to_value (f (of_value 1 v1) (of_value 2 v2)) 47 | 48 | let testop op = 49 | let f = match op with 50 | | Eqz -> IXX.eqz 51 | in fun v -> f (of_value 1 v) 52 | 53 | let relop op = 54 | let f = match op with 55 | | Eq -> IXX.eq 56 | | Ne -> IXX.ne 57 | | LtS -> IXX.lt_s 58 | | LtU -> IXX.lt_u 59 | | LeS -> IXX.le_s 60 | | LeU -> IXX.le_u 61 | | GtS -> IXX.gt_s 62 | | GtU -> IXX.gt_u 63 | | GeS -> IXX.ge_s 64 | | GeU -> IXX.ge_u 65 | in fun v1 v2 -> f (of_value 1 v1) (of_value 2 v2) 66 | end 67 | 68 | module I32Op = IntOp (I32) (Values.I32Value) 69 | module I64Op = IntOp (I64) (Values.I64Value) 70 | 71 | 72 | (* Float operators *) 73 | 74 | module FloatOp (FXX : Float.S) (Value : ValueType with type t = FXX.t) = 75 | struct 76 | open Ast.FloatOp 77 | 78 | let to_value = Value.to_value 79 | let of_value = of_arg Value.of_value 80 | 81 | let unop op = 82 | let f = match op with 83 | | Neg -> FXX.neg 84 | | Abs -> FXX.abs 85 | | Sqrt -> FXX.sqrt 86 | | Ceil -> FXX.ceil 87 | | Floor -> FXX.floor 88 | | Trunc -> FXX.trunc 89 | | Nearest -> FXX.nearest 90 | in fun v -> to_value (f (of_value 1 v)) 91 | 92 | let binop op = 93 | let f = match op with 94 | | Add -> FXX.add 95 | | Sub -> FXX.sub 96 | | Mul -> FXX.mul 97 | | Div -> FXX.div 98 | | Min -> FXX.min 99 | | Max -> FXX.max 100 | | CopySign -> FXX.copysign 101 | in fun v1 v2 -> to_value (f (of_value 1 v1) (of_value 2 v2)) 102 | 103 | let testop op = assert false 104 | 105 | let relop op = 106 | let f = match op with 107 | | Eq -> FXX.eq 108 | | Ne -> FXX.ne 109 | | Lt -> FXX.lt 110 | | Le -> FXX.le 111 | | Gt -> FXX.gt 112 | | Ge -> FXX.ge 113 | in fun v1 v2 -> f (of_value 1 v1) (of_value 2 v2) 114 | end 115 | 116 | module F32Op = FloatOp (F32) (Values.F32Value) 117 | module F64Op = FloatOp (F64) (Values.F64Value) 118 | 119 | 120 | (* Conversion operators *) 121 | 122 | module I32CvtOp = 123 | struct 124 | open Ast.IntOp 125 | 126 | let cvtop op v = 127 | match op with 128 | | WrapI64 -> I32 (I32_convert.wrap_i64 (I64Op.of_value 1 v)) 129 | | TruncSF32 -> I32 (I32_convert.trunc_s_f32 (F32Op.of_value 1 v)) 130 | | TruncUF32 -> I32 (I32_convert.trunc_u_f32 (F32Op.of_value 1 v)) 131 | | TruncSF64 -> I32 (I32_convert.trunc_s_f64 (F64Op.of_value 1 v)) 132 | | TruncUF64 -> I32 (I32_convert.trunc_u_f64 (F64Op.of_value 1 v)) 133 | | ReinterpretFloat -> I32 (I32_convert.reinterpret_f32 (F32Op.of_value 1 v)) 134 | | ExtendSI32 -> raise (TypeError (1, v, I32Type)) 135 | | ExtendUI32 -> raise (TypeError (1, v, I32Type)) 136 | end 137 | 138 | module I64CvtOp = 139 | struct 140 | open Ast.IntOp 141 | 142 | let cvtop op v = 143 | match op with 144 | | ExtendSI32 -> I64 (I64_convert.extend_s_i32 (I32Op.of_value 1 v)) 145 | | ExtendUI32 -> I64 (I64_convert.extend_u_i32 (I32Op.of_value 1 v)) 146 | | TruncSF32 -> I64 (I64_convert.trunc_s_f32 (F32Op.of_value 1 v)) 147 | | TruncUF32 -> I64 (I64_convert.trunc_u_f32 (F32Op.of_value 1 v)) 148 | | TruncSF64 -> I64 (I64_convert.trunc_s_f64 (F64Op.of_value 1 v)) 149 | | TruncUF64 -> I64 (I64_convert.trunc_u_f64 (F64Op.of_value 1 v)) 150 | | ReinterpretFloat -> I64 (I64_convert.reinterpret_f64 (F64Op.of_value 1 v)) 151 | | WrapI64 -> raise (TypeError (1, v, I64Type)) 152 | end 153 | 154 | module F32CvtOp = 155 | struct 156 | open Ast.FloatOp 157 | 158 | let cvtop op v = 159 | match op with 160 | | DemoteF64 -> F32 (F32_convert.demote_f64 (F64Op.of_value 1 v)) 161 | | ConvertSI32 -> F32 (F32_convert.convert_s_i32 (I32Op.of_value 1 v)) 162 | | ConvertUI32 -> F32 (F32_convert.convert_u_i32 (I32Op.of_value 1 v)) 163 | | ConvertSI64 -> F32 (F32_convert.convert_s_i64 (I64Op.of_value 1 v)) 164 | | ConvertUI64 -> F32 (F32_convert.convert_u_i64 (I64Op.of_value 1 v)) 165 | | ReinterpretInt -> F32 (F32_convert.reinterpret_i32 (I32Op.of_value 1 v)) 166 | | PromoteF32 -> raise (TypeError (1, v, F32Type)) 167 | end 168 | 169 | module F64CvtOp = 170 | struct 171 | open Ast.FloatOp 172 | 173 | let cvtop op v = 174 | match op with 175 | | PromoteF32 -> F64 (F64_convert.promote_f32 (F32Op.of_value 1 v)) 176 | | ConvertSI32 -> F64 (F64_convert.convert_s_i32 (I32Op.of_value 1 v)) 177 | | ConvertUI32 -> F64 (F64_convert.convert_u_i32 (I32Op.of_value 1 v)) 178 | | ConvertSI64 -> F64 (F64_convert.convert_s_i64 (I64Op.of_value 1 v)) 179 | | ConvertUI64 -> F64 (F64_convert.convert_u_i64 (I64Op.of_value 1 v)) 180 | | ReinterpretInt -> F64 (F64_convert.reinterpret_i64 (I64Op.of_value 1 v)) 181 | | DemoteF64 -> raise (TypeError (1, v, F64Type)) 182 | end 183 | 184 | 185 | (* Dispatch *) 186 | 187 | let op i32 i64 f32 f64 = function 188 | | I32 x -> i32 x 189 | | I64 x -> i64 x 190 | | F32 x -> f32 x 191 | | F64 x -> f64 x 192 | 193 | let eval_unop = op I32Op.unop I64Op.unop F32Op.unop F64Op.unop 194 | let eval_binop = op I32Op.binop I64Op.binop F32Op.binop F64Op.binop 195 | let eval_testop = op I32Op.testop I64Op.testop F32Op.testop F64Op.testop 196 | let eval_relop = op I32Op.relop I64Op.relop F32Op.relop F64Op.relop 197 | let eval_cvtop = op I32CvtOp.cvtop I64CvtOp.cvtop F32CvtOp.cvtop F64CvtOp.cvtop 198 | -------------------------------------------------------------------------------- /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 pos_nan = 0x7fc00000l 10 | let neg_nan = 0xffc00000l 11 | let bare_nan = 0x7f800000l 12 | let to_hex_string = Printf.sprintf "%lx" 13 | end) 14 | -------------------------------------------------------------------------------- /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 0x7fc00000l (I32_convert.wrap_i64 fields) in 11 | F32.of_bits nan32bits 12 | 13 | let convert_s_i32 x = 14 | F32.of_float (Int32.to_float x) 15 | 16 | (* 17 | * Similar to convert_u_i64 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_u_i32 x = 22 | F32.of_float 23 | Int32.(if x >= zero then to_float x else 24 | to_float (logor (shift_right_logical x 1) (logand x 1l)) *. 2.0) 25 | 26 | let convert_s_i64 x = 27 | F32.of_float (Int64.to_float x) 28 | 29 | (* 30 | * Values in the low half of the int64 range can be converted with a signed 31 | * conversion. The high half is beyond the range where f32 can represent odd 32 | * numbers, so we can shift the value right, do a conversion, and then scale it 33 | * back up, without worrying about losing the least-significant digit. 34 | *) 35 | let convert_u_i64 x = 36 | F32.of_float (if x >= Int64.zero then 37 | Int64.to_float x 38 | else 39 | Int64.(to_float (shift_right_logical x 1) *. 2.0)) 40 | 41 | let reinterpret_i32 = F32.of_bits 42 | -------------------------------------------------------------------------------- /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_s_i32 : I32.t -> F32.t 5 | val convert_u_i32 : I32.t -> F32.t 6 | val convert_s_i64 : I64.t -> F32.t 7 | val convert_u_i64 : 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 pos_nan = 0x7ff8000000000000L 5 | let neg_nan = 0xfff8000000000000L 6 | let bare_nan = 0x7ff0000000000000L 7 | let to_hex_string = Printf.sprintf "%Lx" 8 | end) 9 | -------------------------------------------------------------------------------- /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_u_i32 (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 0x7ff8000000000000L fields in 11 | F64.of_bits nan64bits 12 | 13 | let convert_s_i32 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_u_i32 x = 22 | F64.of_float Int64.(to_float (logand (of_int32 x) 0x00000000ffffffffL)) 23 | 24 | let convert_s_i64 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_u_i64 x = 34 | F64.of_float 35 | Int64.(if x >= zero then to_float x else 36 | to_float (logor (shift_right_logical x 1) (logand x 1L)) *. 2.0) 37 | 38 | let reinterpret_i64 = F64.of_bits 39 | -------------------------------------------------------------------------------- /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_s_i32 : I32.t -> F64.t 5 | val convert_u_i32 : I32.t -> F64.t 6 | val convert_s_i64 : I64.t -> F64.t 7 | val convert_u_i64 : 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 | end) 8 | -------------------------------------------------------------------------------- /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_s_f32 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_u_f32 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_s_f64 x = 26 | if F64.ne x x then 27 | raise Numeric_error.InvalidConversionToInteger 28 | else 29 | let xf = F64.to_float x in 30 | if xf >= -.Int32.(to_float min_int) || xf < Int32.(to_float min_int) then 31 | raise Numeric_error.IntegerOverflow 32 | else 33 | Int32.of_float xf 34 | 35 | let trunc_u_f64 x = 36 | if F64.ne x x then 37 | raise Numeric_error.InvalidConversionToInteger 38 | else 39 | let xf = F64.to_float x in 40 | if xf >= -.Int32.(to_float min_int) *. 2.0 || xf <= -1.0 then 41 | raise Numeric_error.IntegerOverflow 42 | else 43 | Int64.(to_int32 (of_float xf)) 44 | 45 | let reinterpret_f32 = F32.to_bits 46 | -------------------------------------------------------------------------------- /interpreter/exec/i32_convert.mli: -------------------------------------------------------------------------------- 1 | (* WebAssembly-compatible type conversions to i32 implementation *) 2 | 3 | val wrap_i64 : I64.t -> I32.t 4 | val trunc_s_f32 : F32.t -> I32.t 5 | val trunc_u_f32 : F32.t -> I32.t 6 | val trunc_s_f64 : F64.t -> I32.t 7 | val trunc_u_f64 : F64.t -> I32.t 8 | val reinterpret_f32 : F32.t -> I32.t 9 | -------------------------------------------------------------------------------- /interpreter/exec/i64.ml: -------------------------------------------------------------------------------- 1 | (* WebAssembly-compatible i64 implementation *) 2 | 3 | include Int.Make 4 | (struct 5 | include Int64 6 | let bitwidth = 64 7 | end) 8 | -------------------------------------------------------------------------------- /interpreter/exec/i64_convert.ml: -------------------------------------------------------------------------------- 1 | (* WebAssembly-compatible type conversions to i64 implementation *) 2 | 3 | let extend_s_i32 x = Int64.of_int32 x 4 | 5 | let extend_u_i32 x = Int64.logand (Int64.of_int32 x) 0x00000000ffffffffL 6 | 7 | let trunc_s_f32 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_u_f32 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 -. 9223372036854775808.0)) min_int) 26 | else 27 | Int64.of_float xf 28 | 29 | let trunc_s_f64 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_u_f64 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 -. 9223372036854775808.0)) min_int) 48 | else 49 | Int64.of_float xf 50 | 51 | let reinterpret_f64 = F64.to_bits 52 | -------------------------------------------------------------------------------- /interpreter/exec/i64_convert.mli: -------------------------------------------------------------------------------- 1 | (* WebAssembly-compatible type conversions to i64 implementation *) 2 | 3 | val extend_s_i32 : I32.t -> I64.t 4 | val extend_u_i32 : I32.t -> I64.t 5 | val trunc_s_f32 : F32.t -> I64.t 6 | val trunc_u_f32 : F32.t -> I64.t 7 | val trunc_s_f64 : F64.t -> I64.t 8 | val trunc_u_f64 : F64.t -> I64.t 9 | val reinterpret_f64 : F64.t -> I64.t 10 | -------------------------------------------------------------------------------- /interpreter/exec/numeric_error.ml: -------------------------------------------------------------------------------- 1 | exception IntegerOverflow 2 | exception IntegerDivideByZero 3 | exception InvalidConversionToInteger 4 | -------------------------------------------------------------------------------- /interpreter/host/env.ml: -------------------------------------------------------------------------------- 1 | (* 2 | * Emulation of (a subset of) the `env` module currently used by Binaryen, 3 | * so that we can run modules generated by Binaryen. This is a stopgap until 4 | * we have agreement on what libc should look like. 5 | *) 6 | 7 | open Values 8 | open Types 9 | open Instance 10 | 11 | 12 | let error msg = raise (Eval.Crash (Source.no_region, msg)) 13 | 14 | let type_error v t = 15 | error 16 | ("type error, expected " ^ string_of_value_type t ^ 17 | ", got " ^ string_of_value_type (type_of v)) 18 | 19 | let empty = function 20 | | [] -> () 21 | | vs -> error "type error, too many arguments" 22 | 23 | let single = function 24 | | [] -> error "type error, missing arguments" 25 | | [v] -> v 26 | | vs -> error "type error, too many arguments" 27 | 28 | let int = function 29 | | I32 i -> Int32.to_int i 30 | | v -> type_error v I32Type 31 | 32 | 33 | let abort vs = 34 | empty vs; 35 | print_endline "Abort!"; 36 | exit (-1) 37 | 38 | let exit vs = 39 | exit (int (single vs)) 40 | 41 | 42 | let lookup name t = 43 | match Utf8.encode name, t with 44 | | "abort", ExternFuncType t -> ExternFunc (Func.alloc_host t abort) 45 | | "exit", ExternFuncType t -> ExternFunc (Func.alloc_host t exit) 46 | | _ -> raise Not_found 47 | -------------------------------------------------------------------------------- /interpreter/host/spectest.ml: -------------------------------------------------------------------------------- 1 | (* 2 | * Simple collection of functions useful for writing test cases. 3 | *) 4 | 5 | open Types 6 | open Values 7 | open Instance 8 | 9 | 10 | let global (GlobalType (t, _) as gt) = 11 | let v = 12 | match t with 13 | | I32Type -> I32 666l 14 | | I64Type -> I64 666L 15 | | F32Type -> F32 (F32.of_float 666.6) 16 | | F64Type -> F64 (F64.of_float 666.6) 17 | in Global.alloc gt v 18 | 19 | let table = Table.alloc (TableType ({min = 10l; max = Some 20l}, AnyFuncType)) 20 | let memory = Memory.alloc (MemoryType {min = 1l; max = Some 2l}) 21 | let func f t = Func.alloc_host t (f t) 22 | 23 | let print_value v = 24 | Printf.printf "%s : %s\n" 25 | (Values.string_of_value v) (Types.string_of_value_type (Values.type_of v)) 26 | 27 | let print (FuncType (_, out)) vs = 28 | List.iter print_value vs; 29 | flush_all (); 30 | List.map default_value out 31 | 32 | 33 | let lookup name t = 34 | match Utf8.encode name, t with 35 | | "print", _ -> ExternFunc (func print (FuncType ([], []))) 36 | | "print_i32", _ -> ExternFunc (func print (FuncType ([I32Type], []))) 37 | | "print_i32_f32", _ -> 38 | ExternFunc (func print (FuncType ([I32Type; F32Type], []))) 39 | | "print_f64_f64", _ -> 40 | ExternFunc (func print (FuncType ([F64Type; F64Type], []))) 41 | | "print_f32", _ -> ExternFunc (func print (FuncType ([F32Type], []))) 42 | | "print_f64", _ -> ExternFunc (func print (FuncType ([F64Type], []))) 43 | | "global_i32", _ -> ExternGlobal (global (GlobalType (I32Type, Immutable))) 44 | | "global_f32", _ -> ExternGlobal (global (GlobalType (F32Type, Immutable))) 45 | | "global_f64", _ -> ExternGlobal (global (GlobalType (F64Type, Immutable))) 46 | | "table", _ -> ExternTable table 47 | | "memory", _ -> ExternMemory memory 48 | | _ -> raise Not_found 49 | -------------------------------------------------------------------------------- /interpreter/main/flags.ml: -------------------------------------------------------------------------------- 1 | let interactive = ref false 2 | let trace = ref false 3 | let unchecked = ref false 4 | let print_sig = ref false 5 | let dry = ref false 6 | let width = ref 80 7 | let harness = ref true 8 | -------------------------------------------------------------------------------- /interpreter/main/main.ml: -------------------------------------------------------------------------------- 1 | let name = "wasm" 2 | let version = "1.0" 3 | 4 | let configure () = 5 | Import.register (Utf8.decode "spectest") Spectest.lookup; 6 | Import.register (Utf8.decode "env") Env.lookup 7 | 8 | let banner () = 9 | print_endline (name ^ " " ^ version ^ " reference interpreter") 10 | 11 | let usage = "Usage: " ^ name ^ " [option] [file ...]" 12 | 13 | let args = ref [] 14 | let add_arg source = args := !args @ [source] 15 | 16 | let quote s = "\"" ^ String.escaped s ^ "\"" 17 | 18 | let argspec = Arg.align 19 | [ 20 | "-", Arg.Set Flags.interactive, 21 | " run interactively (default if no files given)"; 22 | "-e", Arg.String add_arg, " evaluate string"; 23 | "-i", Arg.String (fun file -> add_arg ("(input " ^ quote file ^ ")")), 24 | " read script from file"; 25 | "-o", Arg.String (fun file -> add_arg ("(output " ^ quote file ^ ")")), 26 | " write module to file"; 27 | "-w", Arg.Int (fun n -> Flags.width := n), 28 | " configure output width (default is 80)"; 29 | "-s", Arg.Set Flags.print_sig, " show module signatures"; 30 | "-u", Arg.Set Flags.unchecked, " unchecked, do not perform validation"; 31 | "-h", Arg.Clear Flags.harness, " exclude harness for JS convesion"; 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 ==== Compiling ==== 74 | BSPATH=`which bsb` 75 | BPATH=`dirname $BSPATH`/../lib/js 76 | echo 1>&2 BSPATH = $BSPATH 77 | bsb.exe || exit 1 78 | cp `dirname $BSPATH`/../lib/js/*.js lib/js/src 79 | 80 | echo 1>&2 ==== Linking full version ==== 81 | LOG=1 82 | link lib/js/src/*.js >temp.js || exit 1 83 | 84 | echo 1>&2 ==== Running for dependencies ==== 85 | node temp.js >temp.log || exit 1 86 | 87 | echo 1>&2 ==== Linking stripped version ==== 88 | used='' 89 | for file in `ls lib/js/src/*.js` 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 | export PATH=$PWD/../ocaml/install/bin:$PATH 11 | 12 | make all 13 | -------------------------------------------------------------------------------- /interpreter/meta/travis/install-ocaml.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | download_from_gh_archive() { 6 | local project=$1; 7 | local version=$2; 8 | local sha=$3; 9 | 10 | curl https://github.com/ocaml/${project}/archive/${version}.tar.gz -OL 11 | CHECKSUM=$(shasum -a 256 ${version}.tar.gz | awk '{ print $1 }') 12 | if [ ${CHECKSUM} != ${sha} ]; then 13 | echo "Bad checksum ${project} download checksum!" 14 | exit 1 15 | fi 16 | tar xfz ${version}.tar.gz 17 | } 18 | 19 | # Move to a location relative to the script so it runs 20 | # from anywhere. Go three levels down to get out of interpreter/ 21 | # and into the top-level dir, since we'll run ocamlbuild 22 | # inside of interpreter/ and it goes pear-shaped if it 23 | # encounters ocaml's own build directory. 24 | cd $(dirname ${BASH_SOURCE[0]})/../../.. 25 | 26 | PREFIX=$PWD/ocaml/install 27 | 28 | rm -rf ocaml 29 | mkdir ocaml 30 | cd ocaml 31 | mkdir install 32 | 33 | download_from_gh_archive ocaml 4.05.0 e5d8a6f629020c580473d8afcfcb06c3966d01929f7b734f41dc0c737cd8ea3f 34 | cd ocaml-4.05.0 35 | ./configure -prefix ${PREFIX} 36 | make world.opt 37 | make install 38 | cd .. 39 | 40 | PATH=${PREFIX}/bin:${PATH} 41 | 42 | download_from_gh_archive ocamlbuild 0.11.0 1717edc841c9b98072e410f1b0bc8b84444b4b35ed3b4949ce2bec17c60103ee 43 | cd ocamlbuild-0.11.0 44 | make configure 45 | make 46 | make install 47 | -------------------------------------------------------------------------------- /interpreter/runtime/func.ml: -------------------------------------------------------------------------------- 1 | open Types 2 | open Values 3 | 4 | type 'inst t = 'inst func 5 | and 'inst func = 6 | | AstFunc of func_type * 'inst * Ast.func 7 | | HostFunc of func_type * (value list -> value list) 8 | 9 | let alloc ft inst f = AstFunc (ft, inst, f) 10 | let alloc_host ft f = HostFunc (ft, f) 11 | 12 | let type_of = function 13 | | AstFunc (ft, _, _) -> ft 14 | | HostFunc (ft, _) -> ft 15 | -------------------------------------------------------------------------------- /interpreter/runtime/func.mli: -------------------------------------------------------------------------------- 1 | open Types 2 | open Values 3 | 4 | type 'inst t = 'inst func 5 | and 'inst func = 6 | | AstFunc of func_type * 'inst * Ast.func 7 | | HostFunc of func_type * (value list -> value list) 8 | 9 | val alloc : func_type -> 'inst -> Ast.func -> 'inst func 10 | val alloc_host : func_type -> (value list -> value list) -> 'inst func 11 | val type_of : 'inst func -> func_type 12 | -------------------------------------------------------------------------------- /interpreter/runtime/global.ml: -------------------------------------------------------------------------------- 1 | open Types 2 | open Values 3 | 4 | type global = {mutable content : value; mut : mutability} 5 | type t = global 6 | 7 | exception Type 8 | exception NotMutable 9 | 10 | let alloc (GlobalType (t, mut)) v = 11 | if type_of v <> t then raise Type; 12 | {content = v; mut = mut} 13 | 14 | let type_of glob = 15 | GlobalType (type_of glob.content, glob.mut) 16 | 17 | let load glob = glob.content 18 | let store glob v = 19 | if glob.mut <> Mutable then raise NotMutable; 20 | if Values.type_of v <> Values.type_of glob.content then raise Type; 21 | glob.content <- v 22 | -------------------------------------------------------------------------------- /interpreter/runtime/global.mli: -------------------------------------------------------------------------------- 1 | open Types 2 | open Values 3 | 4 | type global 5 | type t = global 6 | 7 | exception Type 8 | exception NotMutable 9 | 10 | val alloc : global_type -> value -> global (* raises Type *) 11 | val type_of : global -> global_type 12 | 13 | val load : global -> value 14 | val store : global -> value -> unit (* raises Type, NotMutable *) 15 | -------------------------------------------------------------------------------- /interpreter/runtime/instance.ml: -------------------------------------------------------------------------------- 1 | open Types 2 | 3 | type module_inst = 4 | { 5 | types : func_type list; 6 | funcs : func_inst list; 7 | tables : table_inst list; 8 | memories : memory_inst list; 9 | globals : global_inst list; 10 | exports : export_inst list; 11 | } 12 | 13 | and func_inst = module_inst ref Func.t 14 | and table_inst = Table.t 15 | and memory_inst = Memory.t 16 | and global_inst = Global.t 17 | and export_inst = Ast.name * extern 18 | 19 | and extern = 20 | | ExternFunc of func_inst 21 | | ExternTable of table_inst 22 | | ExternMemory of memory_inst 23 | | ExternGlobal of global_inst 24 | 25 | type Table.elem += FuncElem of func_inst 26 | 27 | 28 | (* Auxiliary functions *) 29 | 30 | let empty_module_inst = 31 | { types = []; funcs = []; tables = []; memories = []; globals = []; 32 | exports = [] } 33 | 34 | let extern_type_of = function 35 | | ExternFunc func -> ExternFuncType (Func.type_of func) 36 | | ExternTable tab -> ExternTableType (Table.type_of tab) 37 | | ExternMemory mem -> ExternMemoryType (Memory.type_of mem) 38 | | ExternGlobal glob -> ExternGlobalType (Global.type_of glob) 39 | 40 | let export inst name = 41 | try Some (List.assoc name inst.exports) with Not_found -> None 42 | -------------------------------------------------------------------------------- /interpreter/runtime/memory.ml: -------------------------------------------------------------------------------- 1 | open Bigarray 2 | open Lib.Bigarray 3 | open Types 4 | open Values 5 | 6 | type size = int32 (* number of pages *) 7 | type address = int64 8 | type offset = int32 9 | 10 | type pack_size = Pack8 | Pack16 | Pack32 11 | type extension = SX | ZX 12 | 13 | type memory' = (int, int8_unsigned_elt, c_layout) Array1.t 14 | type memory = {mutable content : memory'; max : size option} 15 | type t = memory 16 | 17 | exception Type 18 | exception Bounds 19 | exception SizeOverflow 20 | exception SizeLimit 21 | exception OutOfMemory 22 | 23 | let page_size = 0x10000L (* 64 KiB *) 24 | 25 | let packed_size = function 26 | | Pack8 -> 1 27 | | Pack16 -> 2 28 | | Pack32 -> 4 29 | 30 | let within_limits n = function 31 | | None -> true 32 | | Some max -> I32.le_u n max 33 | 34 | let create n = 35 | if I32.gt_u n 0x10000l then raise SizeOverflow else 36 | try 37 | let size = Int64.(mul (of_int32 n) page_size) in 38 | let mem = Array1_64.create Int8_unsigned C_layout size in 39 | Array1.fill mem 0; 40 | mem 41 | with Out_of_memory -> raise OutOfMemory 42 | 43 | let alloc (MemoryType {min; max}) = 44 | assert (within_limits min max); 45 | {content = create min; max} 46 | 47 | let bound mem = 48 | Array1_64.dim mem.content 49 | 50 | let size mem = 51 | Int64.(to_int32 (div (bound mem) page_size)) 52 | 53 | let type_of mem = 54 | MemoryType {min = size mem; max = mem.max} 55 | 56 | let grow mem delta = 57 | let old_size = size mem in 58 | let new_size = Int32.add old_size delta in 59 | if I32.gt_u old_size new_size then raise SizeOverflow else 60 | if not (within_limits new_size mem.max) then raise SizeLimit else 61 | let after = create new_size in 62 | let dim = Array1_64.dim mem.content in 63 | Array1.blit (Array1_64.sub mem.content 0L dim) (Array1_64.sub after 0L dim); 64 | mem.content <- after 65 | 66 | let load_byte mem a = 67 | try Array1_64.get mem.content a with Invalid_argument _ -> raise Bounds 68 | 69 | let store_byte mem a b = 70 | try Array1_64.set mem.content a b with Invalid_argument _ -> raise Bounds 71 | 72 | let load_bytes mem a n = 73 | let buf = Buffer.create n in 74 | for i = 0 to n - 1 do 75 | Buffer.add_char buf (Char.chr (load_byte mem Int64.(add a (of_int i)))) 76 | done; 77 | Buffer.contents buf 78 | 79 | let store_bytes mem a bs = 80 | for i = String.length bs - 1 downto 0 do 81 | store_byte mem Int64.(add a (of_int i)) (Char.code bs.[i]) 82 | done 83 | 84 | let effective_address a o = 85 | let ea = Int64.(add a (of_int32 o)) in 86 | if I64.lt_u ea a then raise Bounds; 87 | ea 88 | 89 | let loadn mem a o n = 90 | assert (n > 0 && n <= 8); 91 | let rec loop a n = 92 | if n = 0 then 0L else begin 93 | let x = Int64.(shift_left (loop (add a 1L) (n - 1)) 8) in 94 | Int64.logor (Int64.of_int (load_byte mem a)) x 95 | end 96 | in loop (effective_address a o) n 97 | 98 | let storen mem a o n x = 99 | assert (n > 0 && n <= 8); 100 | let rec loop a n x = 101 | if n > 0 then begin 102 | Int64.(loop (add a 1L) (n - 1) (shift_right x 8)); 103 | store_byte mem a (Int64.to_int x land 0xff) 104 | end 105 | in loop (effective_address a o) n x 106 | 107 | let load_value mem a o t = 108 | let n = loadn mem a o (Types.size t) in 109 | match t with 110 | | I32Type -> I32 (Int64.to_int32 n) 111 | | I64Type -> I64 n 112 | | F32Type -> F32 (F32.of_bits (Int64.to_int32 n)) 113 | | F64Type -> F64 (F64.of_bits n) 114 | 115 | let store_value mem a o v = 116 | let x = 117 | match v with 118 | | I32 x -> Int64.of_int32 x 119 | | I64 x -> x 120 | | F32 x -> Int64.of_int32 (F32.to_bits x) 121 | | F64 x -> F64.to_bits x 122 | in storen mem a o (Types.size (Values.type_of v)) x 123 | 124 | let extend x n = function 125 | | ZX -> x 126 | | SX -> let sh = 64 - 8 * n in Int64.(shift_right (shift_left x sh) sh) 127 | 128 | let load_packed sz ext mem a o t = 129 | assert (packed_size sz <= Types.size t); 130 | let n = packed_size sz in 131 | let x = extend (loadn mem a o n) n ext in 132 | match t with 133 | | I32Type -> I32 (Int64.to_int32 x) 134 | | I64Type -> I64 x 135 | | _ -> raise Type 136 | 137 | let store_packed sz mem a o v = 138 | assert (packed_size sz <= Types.size (Values.type_of v)); 139 | let n = packed_size sz in 140 | let x = 141 | match v with 142 | | I32 x -> Int64.of_int32 x 143 | | I64 x -> x 144 | | _ -> raise Type 145 | in storen mem a o n x 146 | -------------------------------------------------------------------------------- /interpreter/runtime/memory.mli: -------------------------------------------------------------------------------- 1 | open Types 2 | open Values 3 | 4 | type memory 5 | type t = memory 6 | 7 | type size = int32 (* number of pages *) 8 | type address = int64 9 | type offset = int32 10 | 11 | type pack_size = Pack8 | Pack16 | Pack32 12 | type extension = SX | ZX 13 | 14 | exception Type 15 | exception Bounds 16 | exception SizeOverflow 17 | exception SizeLimit 18 | exception OutOfMemory 19 | 20 | val page_size : int64 21 | val packed_size : pack_size -> int 22 | 23 | val alloc : memory_type -> memory (* raises SizeOverflow, OutOfMemory *) 24 | val type_of : memory -> memory_type 25 | val size : memory -> size 26 | val bound : memory -> address 27 | val grow : memory -> size -> unit 28 | (* raises SizeLimit, SizeOverflow, OutOfMemory *) 29 | 30 | val load_byte : memory -> address -> int (* raises Bounds *) 31 | val store_byte : memory -> address -> int -> unit (* raises Bounds *) 32 | val load_bytes : memory -> address -> int -> string (* raises Bounds *) 33 | val store_bytes : memory -> address -> string -> unit (* raises Bounds *) 34 | 35 | val load_value : 36 | memory -> address -> offset -> value_type -> value (* raises Bounds *) 37 | val store_value : 38 | memory -> address -> offset -> value -> unit (* raises Bounds *) 39 | val load_packed : 40 | pack_size -> extension -> memory -> address -> offset -> value_type -> value 41 | (* raises Type, Bounds *) 42 | val store_packed : 43 | pack_size -> memory -> address -> offset -> value -> unit 44 | (* raises Type, Bounds *) 45 | -------------------------------------------------------------------------------- /interpreter/runtime/table.ml: -------------------------------------------------------------------------------- 1 | open Types 2 | 3 | type size = int32 4 | type index = int32 5 | 6 | type elem = .. 7 | type elem += Uninitialized 8 | 9 | type table' = elem array 10 | type table = 11 | {mutable content : table'; max : size option; elem_type : elem_type} 12 | type t = table 13 | 14 | exception Bounds 15 | exception SizeOverflow 16 | exception SizeLimit 17 | 18 | let within_limits size = function 19 | | None -> true 20 | | Some max -> I32.le_u size max 21 | 22 | let create size = 23 | try Lib.Array32.make size Uninitialized 24 | with Invalid_argument _ -> raise Out_of_memory 25 | 26 | let alloc (TableType ({min; max}, elem_type)) = 27 | assert (within_limits min max); 28 | {content = create min; max; elem_type} 29 | 30 | let size tab = 31 | Lib.Array32.length tab.content 32 | 33 | let type_of tab = 34 | TableType ({min = size tab; max = tab.max}, tab.elem_type) 35 | 36 | let grow tab delta = 37 | let old_size = size tab in 38 | let new_size = Int32.add old_size delta in 39 | if I32.gt_u old_size new_size then raise SizeOverflow else 40 | if not (within_limits new_size tab.max) then raise SizeLimit else 41 | let after = create new_size in 42 | Array.blit tab.content 0 after 0 (Array.length tab.content); 43 | tab.content <- after 44 | 45 | let load tab i = 46 | try Lib.Array32.get tab.content i with Invalid_argument _ -> raise Bounds 47 | 48 | let store tab i v = 49 | try Lib.Array32.set tab.content i v with Invalid_argument _ -> raise Bounds 50 | 51 | let blit tab offset elems = 52 | let data = Array.of_list elems in 53 | try Lib.Array32.blit data 0l tab.content offset (Lib.Array32.length data) 54 | with Invalid_argument _ -> raise Bounds 55 | -------------------------------------------------------------------------------- /interpreter/runtime/table.mli: -------------------------------------------------------------------------------- 1 | open Types 2 | 3 | type table 4 | type t = table 5 | 6 | type size = int32 7 | type index = int32 8 | 9 | type elem = .. 10 | type elem += Uninitialized 11 | 12 | exception Bounds 13 | exception SizeOverflow 14 | exception SizeLimit 15 | 16 | val alloc : table_type -> table 17 | val type_of : table -> table_type 18 | val size : table -> size 19 | val grow : table -> size -> unit (* raises SizeOverflow, SizeLimit *) 20 | 21 | val load : table -> index -> elem (* raises Bounds *) 22 | val store : table -> index -> elem -> unit (* raises Bounds *) 23 | val blit : table -> index -> elem list -> unit (* raises Bounds *) 24 | -------------------------------------------------------------------------------- /interpreter/script/import.ml: -------------------------------------------------------------------------------- 1 | open Source 2 | open Ast 3 | 4 | module Unknown = Error.Make () 5 | exception Unknown = Unknown.Error (* indicates unknown import name *) 6 | 7 | module Registry = Map.Make(struct type t = Ast.name let compare = compare end) 8 | let registry = ref Registry.empty 9 | 10 | let register name lookup = registry := Registry.add name lookup !registry 11 | 12 | let lookup (m : module_) (im : import) : Instance.extern = 13 | let {module_name; item_name; idesc} = im.it in 14 | let t = import_type m im in 15 | try Registry.find module_name !registry item_name t with Not_found -> 16 | Unknown.error im.at 17 | ("unknown import \"" ^ string_of_name module_name ^ 18 | "\".\"" ^ string_of_name item_name ^ "\"") 19 | 20 | let link m = List.map (lookup m) m.it.imports 21 | -------------------------------------------------------------------------------- /interpreter/script/import.mli: -------------------------------------------------------------------------------- 1 | exception Unknown of Source.region * string 2 | 3 | val link : Ast.module_ -> Instance.extern list (* raises Unknown *) 4 | 5 | val register : 6 | Ast.name -> 7 | (Ast.name -> Types.extern_type -> Instance.extern (* raises Not_found *)) -> 8 | unit 9 | -------------------------------------------------------------------------------- /interpreter/script/js.mli: -------------------------------------------------------------------------------- 1 | val of_script : Script.script -> string 2 | -------------------------------------------------------------------------------- /interpreter/script/run.mli: -------------------------------------------------------------------------------- 1 | exception Abort of Source.region * string 2 | exception Assert of Source.region * string 3 | exception IO of Source.region * string 4 | 5 | val trace : string -> unit 6 | 7 | val run_string : string -> bool 8 | val run_file : string -> bool 9 | val run_stdin : unit -> unit 10 | -------------------------------------------------------------------------------- /interpreter/script/script.ml: -------------------------------------------------------------------------------- 1 | type var = string Source.phrase 2 | 3 | type definition = definition' Source.phrase 4 | and definition' = 5 | | Textual of Ast.module_ 6 | | Encoded of string * string 7 | | Quoted of string * string 8 | 9 | type action = action' Source.phrase 10 | and action' = 11 | | Invoke of var option * Ast.name * Ast.literal list 12 | | Get of var option * Ast.name 13 | 14 | type assertion = assertion' Source.phrase 15 | and assertion' = 16 | | AssertMalformed of definition * string 17 | | AssertInvalid of definition * string 18 | | AssertUnlinkable of definition * string 19 | | AssertUninstantiable of definition * string 20 | | AssertReturn of action * Ast.literal list 21 | | AssertReturnCanonicalNaN of action 22 | | AssertReturnArithmeticNaN of action 23 | | AssertTrap of action * string 24 | | AssertExhaustion of action * string 25 | 26 | type command = command' Source.phrase 27 | and command' = 28 | | Module of var option * definition 29 | | Register of Ast.name * var option 30 | | Action of action 31 | | Assertion of assertion 32 | | Meta of meta 33 | 34 | and meta = meta' Source.phrase 35 | and meta' = 36 | | Input of var option * string 37 | | Output of var option * string option 38 | | Script of var option * script 39 | 40 | and script = command list 41 | 42 | exception Syntax of Source.region * string 43 | -------------------------------------------------------------------------------- /interpreter/syntax/types.ml: -------------------------------------------------------------------------------- 1 | (* Types *) 2 | 3 | type value_type = I32Type | I64Type | F32Type | F64Type 4 | type elem_type = AnyFuncType 5 | type stack_type = value_type list 6 | type func_type = FuncType of stack_type * stack_type 7 | 8 | type 'a limits = {min : 'a; max : 'a option} 9 | type mutability = Immutable | Mutable 10 | type table_type = TableType of Int32.t limits * elem_type 11 | type memory_type = MemoryType of Int32.t limits 12 | type global_type = GlobalType of value_type * mutability 13 | type extern_type = 14 | | ExternFuncType of func_type 15 | | ExternTableType of table_type 16 | | ExternMemoryType of memory_type 17 | | ExternGlobalType of global_type 18 | 19 | 20 | (* Attributes *) 21 | 22 | let size = function 23 | | I32Type | F32Type -> 4 24 | | I64Type | F64Type -> 8 25 | 26 | 27 | (* Subtyping *) 28 | 29 | let match_limits lim1 lim2 = 30 | I32.ge_u lim1.min lim2.min && 31 | match lim1.max, lim2.max with 32 | | _, None -> true 33 | | None, Some _ -> false 34 | | Some i, Some j -> I32.le_u i j 35 | 36 | let match_func_type ft1 ft2 = 37 | ft1 = ft2 38 | 39 | let match_table_type (TableType (lim1, et1)) (TableType (lim2, et2)) = 40 | et1 = et2 && match_limits lim1 lim2 41 | 42 | let match_memory_type (MemoryType lim1) (MemoryType lim2) = 43 | match_limits lim1 lim2 44 | 45 | let match_global_type gt1 gt2 = 46 | gt1 = gt2 47 | 48 | let match_extern_type et1 et2 = 49 | match et1, et2 with 50 | | ExternFuncType ft1, ExternFuncType ft2 -> match_func_type ft1 ft2 51 | | ExternTableType tt1, ExternTableType tt2 -> match_table_type tt1 tt2 52 | | ExternMemoryType mt1, ExternMemoryType mt2 -> match_memory_type mt1 mt2 53 | | ExternGlobalType gt1, ExternGlobalType gt2 -> match_global_type gt1 gt2 54 | | _, _ -> false 55 | 56 | 57 | (* Filters *) 58 | 59 | let funcs = 60 | Lib.List.map_filter (function ExternFuncType t -> Some t | _ -> None) 61 | let tables = 62 | Lib.List.map_filter (function ExternTableType t -> Some t | _ -> None) 63 | let memories = 64 | Lib.List.map_filter (function ExternMemoryType t -> Some t | _ -> None) 65 | let globals = 66 | Lib.List.map_filter (function ExternGlobalType t -> Some t | _ -> None) 67 | 68 | 69 | (* String conversion *) 70 | 71 | let string_of_value_type = function 72 | | I32Type -> "i32" 73 | | I64Type -> "i64" 74 | | F32Type -> "f32" 75 | | F64Type -> "f64" 76 | 77 | let string_of_value_types = function 78 | | [t] -> string_of_value_type t 79 | | ts -> "[" ^ String.concat " " (List.map string_of_value_type ts) ^ "]" 80 | 81 | let string_of_elem_type = function 82 | | AnyFuncType -> "anyfunc" 83 | 84 | let string_of_limits {min; max} = 85 | I32.to_string_u min ^ 86 | (match max with None -> "" | Some n -> " " ^ I32.to_string_u n) 87 | 88 | let string_of_memory_type = function 89 | | MemoryType lim -> string_of_limits lim 90 | 91 | let string_of_table_type = function 92 | | TableType (lim, t) -> string_of_limits lim ^ " " ^ string_of_elem_type t 93 | 94 | let string_of_global_type = function 95 | | GlobalType (t, Immutable) -> string_of_value_type t 96 | | GlobalType (t, Mutable) -> "(mut " ^ string_of_value_type t ^ ")" 97 | 98 | let string_of_stack_type ts = 99 | "[" ^ String.concat " " (List.map string_of_value_type ts) ^ "]" 100 | 101 | let string_of_func_type (FuncType (ins, out)) = 102 | string_of_stack_type ins ^ " -> " ^ string_of_stack_type out 103 | 104 | let string_of_extern_type = function 105 | | ExternFuncType ft -> "func " ^ string_of_func_type ft 106 | | ExternTableType tt -> "table " ^ string_of_table_type tt 107 | | ExternMemoryType mt -> "memory " ^ string_of_memory_type mt 108 | | ExternGlobalType gt -> "global " ^ string_of_global_type gt 109 | -------------------------------------------------------------------------------- /interpreter/syntax/values.ml: -------------------------------------------------------------------------------- 1 | open Types 2 | 3 | 4 | (* Values and operators *) 5 | 6 | type ('i32, 'i64, 'f32, 'f64) op = 7 | I32 of 'i32 | I64 of 'i64 | F32 of 'f32 | F64 of 'f64 8 | 9 | type value = (I32.t, I64.t, F32.t, F64.t) op 10 | 11 | 12 | (* Typing *) 13 | 14 | let type_of = function 15 | | I32 _ -> I32Type 16 | | I64 _ -> I64Type 17 | | F32 _ -> F32Type 18 | | F64 _ -> F64Type 19 | 20 | let default_value = function 21 | | I32Type -> I32 I32.zero 22 | | I64Type -> I64 I64.zero 23 | | F32Type -> F32 F32.zero 24 | | F64Type -> F64 F64.zero 25 | 26 | 27 | (* Conversion *) 28 | 29 | let value_of_bool b = I32 (if b then 1l else 0l) 30 | 31 | let string_of_value = function 32 | | I32 i -> I32.to_string_s i 33 | | I64 i -> I64.to_string_s i 34 | | F32 z -> F32.to_string z 35 | | F64 z -> F64.to_string z 36 | 37 | let string_of_values = function 38 | | [v] -> string_of_value v 39 | | vs -> "[" ^ String.concat " " (List.map string_of_value vs) ^ "]" 40 | 41 | 42 | (* Injection & projection *) 43 | 44 | exception Value of value_type 45 | 46 | module type ValueType = 47 | sig 48 | type t 49 | val to_value : t -> value 50 | val of_value : value -> t (* raise Value *) 51 | end 52 | 53 | module I32Value = 54 | struct 55 | type t = I32.t 56 | let to_value i = I32 i 57 | let of_value = function I32 i -> i | _ -> raise (Value I32Type) 58 | end 59 | 60 | module I64Value = 61 | struct 62 | type t = I64.t 63 | let to_value i = I64 i 64 | let of_value = function I64 i -> i | _ -> raise (Value I64Type) 65 | end 66 | 67 | module F32Value = 68 | struct 69 | type t = F32.t 70 | let to_value i = F32 i 71 | let of_value = function F32 z -> z | _ -> raise (Value F32Type) 72 | end 73 | 74 | module F64Value = 75 | struct 76 | type t = F64.t 77 | let to_value i = F64 i 78 | let of_value = function F64 z -> z | _ -> raise (Value F64Type) 79 | end 80 | -------------------------------------------------------------------------------- /interpreter/text/arrange.mli: -------------------------------------------------------------------------------- 1 | open Sexpr 2 | 3 | val instr : Ast.instr -> sexpr 4 | val func : Ast.func -> sexpr 5 | val module_ : Ast.module_ -> sexpr 6 | val script : [`Textual | `Binary] -> Script.script -> sexpr list 7 | -------------------------------------------------------------------------------- /interpreter/text/lexer.mli: -------------------------------------------------------------------------------- 1 | val convert_pos : Lexing.position -> Source.pos 2 | 3 | val token : Lexing.lexbuf -> Parser.token (* raises Source.Error *) 4 | -------------------------------------------------------------------------------- /interpreter/text/parse.ml: -------------------------------------------------------------------------------- 1 | type 'a start = 2 | | Module : (Script.var option * Script.definition) start 3 | | Script : Script.script start 4 | | Script1 : Script.script start 5 | 6 | exception Syntax = Script.Syntax 7 | 8 | let parse' name lexbuf start = 9 | lexbuf.Lexing.lex_curr_p <- 10 | {lexbuf.Lexing.lex_curr_p with Lexing.pos_fname = name}; 11 | try start Lexer.token lexbuf 12 | with Syntax (region, s) -> 13 | let region' = if region <> Source.no_region then region else 14 | {Source.left = Lexer.convert_pos lexbuf.Lexing.lex_start_p; 15 | Source.right = Lexer.convert_pos lexbuf.Lexing.lex_curr_p} in 16 | raise (Syntax (region', s)) 17 | 18 | let parse (type a) name lexbuf : a start -> a = function 19 | | Module -> parse' name lexbuf Parser.module1 20 | | Script -> parse' name lexbuf Parser.script 21 | | Script1 -> parse' name lexbuf Parser.script1 22 | 23 | let string_to start s = 24 | let lexbuf = Lexing.from_string s in 25 | parse "string" lexbuf start 26 | 27 | let string_to_script s = string_to Script s 28 | let string_to_module s = snd (string_to Module s) 29 | -------------------------------------------------------------------------------- /interpreter/text/parse.mli: -------------------------------------------------------------------------------- 1 | type 'a start = 2 | | Module : (Script.var option * Script.definition) start 3 | | Script : Script.script start 4 | | Script1 : Script.script start 5 | 6 | exception Syntax of Source.region * string 7 | 8 | val parse : string -> Lexing.lexbuf -> 'a start -> 'a (* raises Syntax *) 9 | 10 | val string_to_script : string -> Script.script (* raises Syntax *) 11 | val string_to_module : string -> Script.definition (* raises Syntax *) 12 | -------------------------------------------------------------------------------- /interpreter/text/print.ml: -------------------------------------------------------------------------------- 1 | let instr oc width e = Sexpr.output oc width (Arrange.instr e) 2 | let func oc width f = Sexpr.output oc width (Arrange.func f) 3 | let module_ oc width m = Sexpr.output oc width (Arrange.module_ m) 4 | let script oc width mode s = 5 | List.iter (Sexpr.output oc width) (Arrange.script mode s) 6 | -------------------------------------------------------------------------------- /interpreter/text/print.mli: -------------------------------------------------------------------------------- 1 | val instr : out_channel -> int -> Ast.instr -> unit 2 | val func : out_channel -> int -> Ast.func -> unit 3 | val module_ : out_channel -> int -> Ast.module_ -> unit 4 | val script : out_channel -> int -> [`Textual | `Binary] -> Script.script -> unit 5 | -------------------------------------------------------------------------------- /interpreter/util/error.ml: -------------------------------------------------------------------------------- 1 | module Make () = 2 | struct 3 | exception Error of Source.region * string 4 | 5 | let warn at m = prerr_endline (Source.string_of_region at ^ ": warning: " ^ m) 6 | let error at m = raise (Error (at, m)) 7 | end 8 | 9 | -------------------------------------------------------------------------------- /interpreter/util/error.mli: -------------------------------------------------------------------------------- 1 | module Make () : 2 | sig 3 | exception Error of Source.region * string 4 | 5 | val warn : Source.region -> string -> unit 6 | val error : Source.region -> string -> 'a (* raises Error *) 7 | end 8 | 9 | -------------------------------------------------------------------------------- /interpreter/util/lib.ml: -------------------------------------------------------------------------------- 1 | module Fun = 2 | struct 3 | let curry f x y = f (x, y) 4 | let uncurry f (x, y) = f x y 5 | 6 | let rec repeat n f x = 7 | if n = 0 then () else (f x; repeat (n - 1) f x) 8 | end 9 | 10 | module Int = 11 | struct 12 | let log2 n = 13 | if n <= 0 then failwith "log2"; 14 | let rec loop acc n = if n = 1 then acc else loop (acc + 1) (n lsr 1) in 15 | loop 0 n 16 | 17 | let is_power_of_two n = 18 | if n < 0 then failwith "is_power_of_two"; 19 | n <> 0 && n land (n - 1) = 0 20 | end 21 | 22 | module String = 23 | struct 24 | let implode cs = 25 | let buf = Buffer.create 80 in 26 | List.iter (Buffer.add_char buf) cs; 27 | Buffer.contents buf 28 | 29 | let explode s = 30 | let cs = ref [] in 31 | for i = String.length s - 1 downto 0 do cs := s.[i] :: !cs done; 32 | !cs 33 | 34 | let split s c = 35 | let len = String.length s in 36 | let rec loop i = 37 | if i > len then [] else 38 | let j = try String.index_from s i c with Not_found -> len in 39 | String.sub s i (j - i) :: loop (j + 1) 40 | in loop 0 41 | 42 | let breakup s n = 43 | let rec loop i = 44 | let len = min n (String.length s - i) in 45 | if len = 0 then [] else String.sub s i len :: loop (i + len) 46 | in loop 0 47 | end 48 | 49 | module List = 50 | struct 51 | let rec make n x = make' n x [] 52 | and make' n x xs = 53 | if n = 0 then xs else make' (n - 1) x (x::xs) 54 | 55 | let rec table n f = table' n f [] 56 | and table' n f xs = 57 | if n = 0 then xs else table' (n - 1) f (f (n - 1) :: xs) 58 | 59 | let rec take n xs = 60 | match n, xs with 61 | | 0, _ -> [] 62 | | n, x::xs' when n > 0 -> x :: take (n - 1) xs' 63 | | _ -> failwith "take" 64 | 65 | let rec drop n xs = 66 | match n, xs with 67 | | 0, _ -> xs 68 | | n, _::xs' when n > 0 -> drop (n - 1) xs' 69 | | _ -> failwith "drop" 70 | 71 | let rec last = function 72 | | x::[] -> x 73 | | _::xs -> last xs 74 | | [] -> failwith "last" 75 | 76 | let rec split_last = function 77 | | x::[] -> [], x 78 | | x::xs -> let ys, y = split_last xs in x::ys, y 79 | | [] -> failwith "split_last" 80 | 81 | let rec index_where p xs = index_where' p xs 0 82 | and index_where' p xs i = 83 | match xs with 84 | | [] -> None 85 | | x::xs' when p x -> Some i 86 | | x::xs' -> index_where' p xs' (i+1) 87 | 88 | let index_of x = index_where ((=) x) 89 | 90 | let rec map_filter f = function 91 | | [] -> [] 92 | | x::xs -> 93 | match f x with 94 | | None -> map_filter f xs 95 | | Some y -> y :: map_filter f xs 96 | end 97 | 98 | module List32 = 99 | struct 100 | let rec make n x = make' n x [] 101 | and make' n x xs = 102 | if n = 0l then xs else make' (Int32.sub n 1l) x (x::xs) 103 | 104 | let rec length xs = length' xs 0l 105 | and length' xs n = 106 | match xs with 107 | | [] -> n 108 | | _::xs' when n < Int32.max_int -> length' xs' (Int32.add n 1l) 109 | | _ -> failwith "length" 110 | 111 | let rec nth xs n = 112 | match n, xs with 113 | | 0l, x::_ -> x 114 | | n, _::xs' when n > 0l -> nth xs' (Int32.sub n 1l) 115 | | _ -> failwith "nth" 116 | 117 | let rec take n xs = 118 | match n, xs with 119 | | 0l, _ -> [] 120 | | n, x::xs' when n > 0l -> x :: take (Int32.sub n 1l) xs' 121 | | _ -> failwith "take" 122 | 123 | let rec drop n xs = 124 | match n, xs with 125 | | 0l, _ -> xs 126 | | n, _::xs' when n > 0l -> drop (Int32.sub n 1l) xs' 127 | | _ -> failwith "drop" 128 | end 129 | 130 | module Array32 = 131 | struct 132 | let make n x = 133 | if n < 0l || Int64.of_int32 n > Int64.of_int max_int then 134 | raise (Invalid_argument "Array32.make"); 135 | Array.make (Int32.to_int n) x 136 | 137 | let length a = Int32.of_int (Array.length a) 138 | 139 | let index_of_int32 i = 140 | if i < 0l || Int64.of_int32 i > Int64.of_int max_int then -1 else 141 | Int32.to_int i 142 | 143 | let get a i = Array.get a (index_of_int32 i) 144 | let set a i x = Array.set a (index_of_int32 i) x 145 | let blit a1 i1 a2 i2 n = 146 | Array.blit a1 (index_of_int32 i1) a2 (index_of_int32 i2) (index_of_int32 n) 147 | end 148 | 149 | module Bigarray = 150 | struct 151 | open Bigarray 152 | 153 | module Array1_64 = 154 | struct 155 | let create kind layout n = 156 | if n < 0L || n > Int64.of_int max_int then 157 | raise (Invalid_argument "Bigarray.Array1_64.create"); 158 | Array1.create kind layout (Int64.to_int n) 159 | 160 | let dim a = Int64.of_int (Array1.dim a) 161 | 162 | let index_of_int64 i = 163 | if i < 0L || i > Int64.of_int max_int then -1 else 164 | Int64.to_int i 165 | 166 | let get a i = Array1.get a (index_of_int64 i) 167 | let set a i x = Array1.set a (index_of_int64 i) x 168 | let sub a i n = Array1.sub a (index_of_int64 i) (index_of_int64 n) 169 | end 170 | end 171 | 172 | module Option = 173 | struct 174 | let get o x = 175 | match o with 176 | | Some y -> y 177 | | None -> x 178 | 179 | let map f = function 180 | | Some x -> Some (f x) 181 | | None -> None 182 | 183 | let app f = function 184 | | Some x -> f x 185 | | None -> () 186 | end 187 | -------------------------------------------------------------------------------- /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 | end 25 | 26 | module List32 : 27 | sig 28 | val make : int32 -> 'a -> 'a list 29 | val length : 'a list -> int32 30 | val nth : 'a list -> int32 -> 'a (* raises Failure *) 31 | val take : int32 -> 'a list -> 'a list (* raises Failure *) 32 | val drop : int32 -> 'a list -> 'a list (* raises Failure *) 33 | end 34 | 35 | module Array32 : 36 | sig 37 | val make : int32 -> 'a -> 'a array 38 | val length : 'a array -> int32 39 | val get : 'a array -> int32 -> 'a 40 | val set : 'a array -> int32 -> 'a -> unit 41 | val blit : 'a array -> int32 -> 'a array -> int32 -> int32 -> unit 42 | end 43 | 44 | module Bigarray : 45 | sig 46 | open Bigarray 47 | 48 | module Array1_64 : 49 | sig 50 | val create : ('a, 'b) kind -> 'c layout -> int64 -> ('a, 'b, 'c) Array1.t 51 | val dim : ('a, 'b, 'c) Array1.t -> int64 52 | val get : ('a, 'b, 'c) Array1.t -> int64 -> 'a 53 | val set : ('a, 'b, 'c) Array1.t -> int64 -> 'a -> unit 54 | val sub : ('a, 'b, 'c) Array1.t -> int64 -> int64 -> ('a, 'b, 'c) Array1.t 55 | end 56 | end 57 | 58 | module Option : 59 | sig 60 | val get : 'a option -> 'a -> 'a 61 | val map : ('a -> 'b) -> 'a option -> 'b option 62 | val app : ('a -> unit) -> 'a option -> unit 63 | end 64 | 65 | module Int : 66 | sig 67 | val log2 : int -> int 68 | val is_power_of_two : int -> bool 69 | end 70 | 71 | module String : 72 | sig 73 | val implode : char list -> string 74 | val explode : string -> char list 75 | val split : string -> char -> string list 76 | val breakup : string -> int -> string list 77 | end 78 | -------------------------------------------------------------------------------- /interpreter/util/sexpr.ml: -------------------------------------------------------------------------------- 1 | type sexpr = Atom of string | Node of string * sexpr list 2 | 3 | type rope = Leaf of string | Concat of rope list 4 | let (^+) s r = Concat [Leaf s; r] 5 | let (+^) r s = Concat [r; Leaf s] 6 | 7 | let rec iter f = function 8 | | Leaf s -> f s 9 | | Concat rs -> List.iter (iter f) rs 10 | 11 | let rec concat = function 12 | | Leaf s -> s 13 | | Concat rs -> String.concat "" (List.map concat rs) 14 | 15 | let rec pp off width = function 16 | | Atom s -> String.length s, Leaf s 17 | | Node (s, xs) -> 18 | let lens, rs = List.split (List.map (pp (off + 2) width) xs) in 19 | let len = String.length s + List.length rs + List.fold_left (+) 2 lens in 20 | let sep, fin = 21 | if off + len <= width then " ", "" 22 | else let indent = String.make off ' ' in "\n " ^ indent, "\n" ^ indent 23 | in len, "(" ^+ s ^+ Concat (List.map (fun r -> sep ^+ r) rs) +^ fin +^ ")" 24 | 25 | let output oc width x = 26 | iter (output_string oc) (snd (pp 0 width x)); 27 | output_string oc "\n"; 28 | flush oc 29 | 30 | let print = output stdout 31 | 32 | let to_string width x = concat (snd (pp 0 width x)) ^ "\n" 33 | -------------------------------------------------------------------------------- /interpreter/util/sexpr.mli: -------------------------------------------------------------------------------- 1 | type sexpr = Atom of string | Node of string * sexpr list 2 | 3 | val output : out_channel -> int -> sexpr -> unit 4 | val print : int -> sexpr -> unit 5 | val to_string : int -> sexpr -> string 6 | -------------------------------------------------------------------------------- /interpreter/util/source.ml: -------------------------------------------------------------------------------- 1 | type pos = {file : string; line : int; column : int} 2 | type region = {left : pos; right : pos} 3 | type 'a phrase = {at : region; it : 'a} 4 | 5 | let (@@) x region = {it = x; at = region} 6 | 7 | 8 | (* Positions and regions *) 9 | 10 | let no_pos = {file = ""; line = 0; column = 0} 11 | let no_region = {left = no_pos; right = no_pos} 12 | 13 | let string_of_pos pos = 14 | if pos.line = -1 then 15 | Printf.sprintf "0x%x" pos.column 16 | else 17 | string_of_int pos.line ^ "." ^ string_of_int (pos.column + 1) 18 | 19 | let string_of_region r = 20 | r.left.file ^ ":" ^ string_of_pos r.left ^ 21 | (if r.right = r.left then "" else "-" ^ string_of_pos r.right) 22 | -------------------------------------------------------------------------------- /interpreter/util/source.mli: -------------------------------------------------------------------------------- 1 | type pos = {file : string; line : int; column : int} 2 | type region = {left : pos; right : pos} 3 | type 'a phrase = {at : region; it : 'a} 4 | 5 | val no_pos : pos 6 | val no_region : region 7 | 8 | val string_of_pos : pos -> string 9 | val string_of_region : region -> string 10 | 11 | val (@@) : 'a -> region -> 'a phrase 12 | -------------------------------------------------------------------------------- /interpreter/valid/valid.mli: -------------------------------------------------------------------------------- 1 | exception Invalid of Source.region * string 2 | 3 | val check_module : Ast.module_ -> unit (* raises Invalid *) 4 | -------------------------------------------------------------------------------- /papers/LICENSE: -------------------------------------------------------------------------------- 1 | This work is licensed under the Creative Commons Attribution 4.0 International License. To view a copy of this license, visit http://creativecommons.org/licenses/by/4.0/ or send a letter to Creative Commons, PO Box 1866, Mountain View, CA 94042, USA. 2 | -------------------------------------------------------------------------------- /papers/pldi2017.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WebAssembly/funclets/26d82a6a31fb78b2a035be9feb0b72d9091cbcaf/papers/pldi2017.pdf -------------------------------------------------------------------------------- /test/README.md: -------------------------------------------------------------------------------- 1 | This directory contains the WebAssembly test suite. It is split into two 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 [landing page](out/index.html) contains a condensed version made of all 9 | these tests, converted to HTML. 10 | 11 | A list of to-do's can be found [here](Todo.md). 12 | 13 | ## Multi-stage testing 14 | 15 | The wast tests can be converted to JavaScript, and the JavaScript tests 16 | to HTML tests, using the `build.py` script. It will create a `out/` directory 17 | (checked in in this repository, to be able to use it from github pages), 18 | containing subdirectories with expanded tests, as well as a landing page for 19 | runnning all of them in HTML. 20 | 21 | The HTML tests are just [Web Platform Tests](http://testthewebforward.org) 22 | using the 23 | [testharness.js](http://testthewebforward.org/docs/testharness-library.html) 24 | library. 25 | 26 | Each wast test gets its equivalent JS test, and each JS test (including wast 27 | test) gets its equivalent WPT, to be easily run in browser vendors' automation. 28 | 29 | ## Procedure for adding a new test 30 | 31 | - put the test in the right directory according to the above (top) description. 32 | - ideally, commit here so the actual content commit and build commit are 33 | separated. 34 | - re-run `build.py` so that the landing page is updated and all the cascading 35 | happens. 36 | - re-commit here, if necessary. 37 | 38 | ## Local HTTP serving of the repository 39 | 40 | From the root of your clone of this repository: 41 | 42 | ``` 43 | python -m SimpleHTTPServer 8000 44 | ``` 45 | 46 | Then open your favorite browser and browse to `http://localhost:8000/test/out`. 47 | -------------------------------------------------------------------------------- /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/spec). 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/break-drop.wast: -------------------------------------------------------------------------------- 1 | (module 2 | (func (export "br") (block (br 0))) 3 | (func (export "br_if") (block (br_if 0 (i32.const 1)))) 4 | (func (export "br_table") (block (br_table 0 (i32.const 0)))) 5 | ) 6 | 7 | (assert_return (invoke "br")) 8 | (assert_return (invoke "br_if")) 9 | (assert_return (invoke "br_table")) 10 | -------------------------------------------------------------------------------- /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/const.wast: -------------------------------------------------------------------------------- 1 | ;; Test t.const instructions 2 | 3 | ;; Syntax error 4 | 5 | (module (func (i32.const 0xffffffff) drop)) 6 | (module (func (i32.const -0x80000000) drop)) 7 | (assert_malformed 8 | (module quote "(func (i32.const 0x100000000) drop)") 9 | "constant out of range" 10 | ) 11 | (assert_malformed 12 | (module quote "(func (i32.const -0x80000001) drop)") 13 | "constant out of range" 14 | ) 15 | 16 | (module (func (i32.const 4294967295) drop)) 17 | (module (func (i32.const -2147483648) drop)) 18 | (assert_malformed 19 | (module quote "(func (i32.const 4294967296) drop)") 20 | "constant out of range" 21 | ) 22 | (assert_malformed 23 | (module quote "(func (i32.const -2147483649) drop)") 24 | "constant out of range" 25 | ) 26 | 27 | (module (func (i64.const 0xffffffffffffffff) drop)) 28 | (module (func (i64.const -0x8000000000000000) drop)) 29 | (assert_malformed 30 | (module quote "(func (i64.const 0x10000000000000000) drop)") 31 | "constant out of range" 32 | ) 33 | (assert_malformed 34 | (module quote "(func (i64.const -0x8000000000000001) drop)") 35 | "constant out of range" 36 | ) 37 | 38 | (module (func (i64.const 18446744073709551615) drop)) 39 | (module (func (i64.const -9223372036854775808) drop)) 40 | (assert_malformed 41 | (module quote "(func (i64.const 18446744073709551616) drop)") 42 | "constant out of range" 43 | ) 44 | (assert_malformed 45 | (module quote "(func (i64.const -9223372036854775809) drop)") 46 | "constant out of range" 47 | ) 48 | 49 | (module (func (f32.const 0x1p127) drop)) 50 | (module (func (f32.const -0x1p127) drop)) 51 | (module (func (f32.const 0x1.fffffep127) drop)) 52 | (module (func (f32.const -0x1.fffffep127) drop)) 53 | (module (func (f32.const 0x1.fffffe7p127) drop)) 54 | (module (func (f32.const -0x1.fffffe7p127) drop)) 55 | (assert_malformed 56 | (module quote "(func (f32.const 0x1p128) drop)") 57 | "constant out of range" 58 | ) 59 | (assert_malformed 60 | (module quote "(func (f32.const -0x1p128) drop)") 61 | "constant out of range" 62 | ) 63 | (assert_malformed 64 | (module quote "(func (f32.const 0x1.ffffffp127) drop)") 65 | "constant out of range" 66 | ) 67 | (assert_malformed 68 | (module quote "(func (f32.const -0x1.ffffffp127) drop)") 69 | "constant out of range" 70 | ) 71 | 72 | (module (func (f32.const 1e38) drop)) 73 | (module (func (f32.const -1e38) drop)) 74 | (assert_malformed 75 | (module quote "(func (f32.const 1e39) drop)") 76 | "constant out of range" 77 | ) 78 | (assert_malformed 79 | (module quote "(func (f32.const -1e39) drop)") 80 | "constant out of range" 81 | ) 82 | 83 | (module (func (f32.const 340282356779733623858607532500980858880) drop)) 84 | (module (func (f32.const -340282356779733623858607532500980858880) drop)) 85 | (assert_malformed 86 | (module quote "(func (f32.const 340282356779733661637539395458142568448) drop)") 87 | "constant out of range" 88 | ) 89 | (assert_malformed 90 | (module quote "(func (f32.const -340282356779733661637539395458142568448) drop)") 91 | "constant out of range" 92 | ) 93 | 94 | (module (func (f64.const 0x1p1023) drop)) 95 | (module (func (f64.const -0x1p1023) drop)) 96 | (module (func (f64.const 0x1.fffffffffffffp1023) drop)) 97 | (module (func (f64.const -0x1.fffffffffffffp1023) drop)) 98 | (module (func (f64.const 0x1.fffffffffffff7p1023) drop)) 99 | (module (func (f64.const -0x1.fffffffffffff7p1023) drop)) 100 | (assert_malformed 101 | (module quote "(func (f64.const 0x1p1024) drop)") 102 | "constant out of range" 103 | ) 104 | (assert_malformed 105 | (module quote "(func (f64.const -0x1p1024) drop)") 106 | "constant out of range" 107 | ) 108 | (assert_malformed 109 | (module quote "(func (f64.const 0x1.fffffffffffff8p1023) drop)") 110 | "constant out of range" 111 | ) 112 | (assert_malformed 113 | (module quote "(func (f64.const -0x1.fffffffffffff8p1023) drop)") 114 | "constant out of range" 115 | ) 116 | 117 | (module (func (f64.const 1e308) drop)) 118 | (module (func (f64.const -1e308) drop)) 119 | (assert_malformed 120 | (module quote "(func (f64.const 1e309) drop)") 121 | "constant out of range" 122 | ) 123 | (assert_malformed 124 | (module quote "(func (f64.const -1e309) drop)") 125 | "constant out of range" 126 | ) 127 | 128 | (module (func (f64.const 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368) drop)) 129 | (module (func (f64.const -179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368) drop)) 130 | (assert_malformed 131 | (module quote "(func (f64.const 269653970229347356221791135597556535197105851288767494898376215204735891170042808140884337949150317257310688430271573696351481990334196274152701320055306275479074865864826923114368235135583993416113802762682700913456874855354834422248712838998185022412196739306217084753107265771378949821875606039276187287552) drop)") 132 | "constant out of range" 133 | ) 134 | (assert_malformed 135 | (module quote "(func (f64.const -269653970229347356221791135597556535197105851288767494898376215204735891170042808140884337949150317257310688430271573696351481990334196274152701320055306275479074865864826923114368235135583993416113802762682700913456874855354834422248712838998185022412196739306217084753107265771378949821875606039276187287552) drop)") 136 | "constant out of range" 137 | ) 138 | -------------------------------------------------------------------------------- /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 | "invalid 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" ;; invalid length! 106 | "\03\02\01\00" ;; function section 107 | "\0a\09\01\07\00\20\00\20\01\6a\0b" ;; code section 108 | "\00\1b\07" "custom2" "this is the payload" ;; custom section 109 | ) 110 | "function and code section have inconsistent lengths" 111 | ) 112 | 113 | ;; Test concatenated modules. 114 | (assert_malformed 115 | (module binary 116 | "\00asm\01\00\00\00" 117 | "\00asm\01\00\00\00" 118 | ) 119 | "length out of bounds" 120 | ) 121 | -------------------------------------------------------------------------------- /test/core/fac.wast: -------------------------------------------------------------------------------- 1 | (module 2 | ;; Recursive factorial 3 | (func (export "fac-rec") (param i64) (result i64) 4 | (if (result i64) (i64.eq (get_local 0) (i64.const 0)) 5 | (then (i64.const 1)) 6 | (else 7 | (i64.mul (get_local 0) (call 0 (i64.sub (get_local 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 (get_local $n) (i64.const 0)) 15 | (then (i64.const 1)) 16 | (else 17 | (i64.mul 18 | (get_local $n) 19 | (call $fac-rec-named (i64.sub (get_local $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 | (set_local 1 (get_local 0)) 29 | (set_local 2 (i64.const 1)) 30 | (block 31 | (loop 32 | (if 33 | (i64.eq (get_local 1) (i64.const 0)) 34 | (then (br 2)) 35 | (else 36 | (set_local 2 (i64.mul (get_local 1) (get_local 2))) 37 | (set_local 1 (i64.sub (get_local 1) (i64.const 1))) 38 | ) 39 | ) 40 | (br 0) 41 | ) 42 | ) 43 | (get_local 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 | (set_local $i (get_local $n)) 51 | (set_local $res (i64.const 1)) 52 | (block $done 53 | (loop $loop 54 | (if 55 | (i64.eq (get_local $i) (i64.const 0)) 56 | (then (br $done)) 57 | (else 58 | (set_local $res (i64.mul (get_local $i) (get_local $res))) 59 | (set_local $i (i64.sub (get_local $i) (i64.const 1))) 60 | ) 61 | ) 62 | (br $loop) 63 | ) 64 | ) 65 | (get_local $res) 66 | ) 67 | 68 | ;; Optimized factorial. 69 | (func (export "fac-opt") (param i64) (result i64) 70 | (local i64) 71 | (set_local 1 (i64.const 1)) 72 | (block 73 | (br_if 0 (i64.lt_s (get_local 0) (i64.const 2))) 74 | (loop 75 | (set_local 1 (i64.mul (get_local 1) (get_local 0))) 76 | (set_local 0 (i64.add (get_local 0) (i64.const -1))) 77 | (br_if 0 (i64.gt_s (get_local 0) (i64.const 1))) 78 | ) 79 | ) 80 | (get_local 1) 81 | ) 82 | ) 83 | 84 | (assert_return (invoke "fac-rec" (i64.const 25)) (i64.const 7034535277573963776)) 85 | (assert_return (invoke "fac-iter" (i64.const 25)) (i64.const 7034535277573963776)) 86 | (assert_return (invoke "fac-rec-named" (i64.const 25)) (i64.const 7034535277573963776)) 87 | (assert_return (invoke "fac-iter-named" (i64.const 25)) (i64.const 7034535277573963776)) 88 | (assert_return (invoke "fac-opt" (i64.const 25)) (i64.const 7034535277573963776)) 89 | (assert_exhaustion (invoke "fac-rec" (i64.const 1073741824)) "call stack exhausted") 90 | -------------------------------------------------------------------------------- /test/core/forward.wast: -------------------------------------------------------------------------------- 1 | (module 2 | (func $even (export "even") (param $n i32) (result i32) 3 | (if (result i32) (i32.eq (get_local $n) (i32.const 0)) 4 | (then (i32.const 1)) 5 | (else (call $odd (i32.sub (get_local $n) (i32.const 1)))) 6 | ) 7 | ) 8 | 9 | (func $odd (export "odd") (param $n i32) (result i32) 10 | (if (result i32) (i32.eq (get_local $n) (i32.const 0)) 11 | (then (i32.const 0)) 12 | (else (call $even (i32.sub (get_local $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 (get_local 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 (get_local 0) (i32.const 2)) 22 | ) 23 | 24 | (func (export "four") (type $U) (call $print (get_local 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 anyfunc) (elem (i64.const 0))) 37 | "type mismatch" 38 | ) 39 | (assert_invalid 40 | (module (table 1 anyfunc) (elem (i32.ctz (i32.const 0)))) 41 | "constant expression required" 42 | ) 43 | (assert_invalid 44 | (module (table 1 anyfunc) (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 anyfunc (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) (get_local $i)) 64 | ) 65 | 66 | (func (export "callu") (param $i i32) (result i32) 67 | (call_indirect (type $U) (get_local $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 anyfunc (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) (get_local $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/get_local.wast: -------------------------------------------------------------------------------- 1 | ;; Test `get_local` operator 2 | 3 | (module 4 | ;; Typing 5 | 6 | (func (export "type-local-i32") (result i32) (local i32) (get_local 0)) 7 | (func (export "type-local-i64") (result i64) (local i64) (get_local 0)) 8 | (func (export "type-local-f32") (result f32) (local f32) (get_local 0)) 9 | (func (export "type-local-f64") (result f64) (local f64) (get_local 0)) 10 | 11 | (func (export "type-param-i32") (param i32) (result i32) (get_local 0)) 12 | (func (export "type-param-i64") (param i64) (result i64) (get_local 0)) 13 | (func (export "type-param-f32") (param f32) (result f32) (get_local 0)) 14 | (func (export "type-param-f64") (param f64) (result f64) (get_local 0)) 15 | 16 | (func (export "type-mixed") (param i64 f32 f64 i32 i32) 17 | (local f32 i64 i64 f64) 18 | (drop (i64.eqz (get_local 0))) 19 | (drop (f32.neg (get_local 1))) 20 | (drop (f64.neg (get_local 2))) 21 | (drop (i32.eqz (get_local 3))) 22 | (drop (i32.eqz (get_local 4))) 23 | (drop (f32.neg (get_local 5))) 24 | (drop (i64.eqz (get_local 6))) 25 | (drop (i64.eqz (get_local 7))) 26 | (drop (f64.neg (get_local 8))) 27 | ) 28 | 29 | ;; Reading 30 | 31 | (func (export "read") (param i64 f32 f64 i32 i32) (result f64) 32 | (local f32 i64 i64 f64) 33 | (set_local 5 (f32.const 5.5)) 34 | (set_local 6 (i64.const 6)) 35 | (set_local 8 (f64.const 8)) 36 | (f64.add 37 | (f64.convert_u/i64 (get_local 0)) 38 | (f64.add 39 | (f64.promote/f32 (get_local 1)) 40 | (f64.add 41 | (get_local 2) 42 | (f64.add 43 | (f64.convert_u/i32 (get_local 3)) 44 | (f64.add 45 | (f64.convert_s/i32 (get_local 4)) 46 | (f64.add 47 | (f64.promote/f32 (get_local 5)) 48 | (f64.add 49 | (f64.convert_u/i64 (get_local 6)) 50 | (f64.add 51 | (f64.convert_u/i64 (get_local 7)) 52 | (get_local 8) 53 | ) 54 | ) 55 | ) 56 | ) 57 | ) 58 | ) 59 | ) 60 | ) 61 | ) 62 | ) 63 | 64 | (assert_return (invoke "type-local-i32") (i32.const 0)) 65 | (assert_return (invoke "type-local-i64") (i64.const 0)) 66 | (assert_return (invoke "type-local-f32") (f32.const 0)) 67 | (assert_return (invoke "type-local-f64") (f64.const 0)) 68 | 69 | (assert_return (invoke "type-param-i32" (i32.const 2)) (i32.const 2)) 70 | (assert_return (invoke "type-param-i64" (i64.const 3)) (i64.const 3)) 71 | (assert_return (invoke "type-param-f32" (f32.const 4.4)) (f32.const 4.4)) 72 | (assert_return (invoke "type-param-f64" (f64.const 5.5)) (f64.const 5.5)) 73 | 74 | (assert_return 75 | (invoke "type-mixed" 76 | (i64.const 1) (f32.const 2.2) (f64.const 3.3) (i32.const 4) (i32.const 5) 77 | ) 78 | ) 79 | 80 | (assert_return 81 | (invoke "read" 82 | (i64.const 1) (f32.const 2) (f64.const 3.3) (i32.const 4) (i32.const 5) 83 | ) 84 | (f64.const 34.8) 85 | ) 86 | 87 | 88 | ;; Invalid typing of access to locals 89 | 90 | (assert_invalid 91 | (module (func $type-local-num-vs-num (result i64) (local i32) (get_local 0))) 92 | "type mismatch" 93 | ) 94 | (assert_invalid 95 | (module (func $type-local-num-vs-num (local f32) (i32.eqz (get_local 0)))) 96 | "type mismatch" 97 | ) 98 | (assert_invalid 99 | (module (func $type-local-num-vs-num (local f64 i64) (f64.neg (get_local 1)))) 100 | "type mismatch" 101 | ) 102 | 103 | 104 | ;; Invalid typing of access to parameters 105 | 106 | (assert_invalid 107 | (module (func $type-param-num-vs-num (param i32) (result i64) (get_local 0))) 108 | "type mismatch" 109 | ) 110 | (assert_invalid 111 | (module (func $type-param-num-vs-num (param f32) (i32.eqz (get_local 0)))) 112 | "type mismatch" 113 | ) 114 | (assert_invalid 115 | (module (func $type-param-num-vs-num (param f64 i64) (f64.neg (get_local 1)))) 116 | "type mismatch" 117 | ) 118 | 119 | 120 | ;; Invalid local index 121 | 122 | (assert_invalid 123 | (module (func $unbound-local (local i32 i64) (get_local 3))) 124 | "unknown local" 125 | ) 126 | (assert_invalid 127 | (module (func $large-local (local i32 i64) (get_local 14324343))) 128 | "unknown local" 129 | ) 130 | 131 | (assert_invalid 132 | (module (func $unbound-param (param i32 i64) (get_local 2))) 133 | "unknown local" 134 | ) 135 | (assert_invalid 136 | (module (func $large-param (local i32 i64) (get_local 714324343))) 137 | "unknown local" 138 | ) 139 | 140 | (assert_invalid 141 | (module (func $unbound-mixed (param i32) (local i32 i64) (get_local 3))) 142 | "unknown local" 143 | ) 144 | (assert_invalid 145 | (module (func $large-mixed (param i64) (local i32 i64) (get_local 214324343))) 146 | "unknown local" 147 | ) 148 | 149 | -------------------------------------------------------------------------------- /test/core/inline-module.wast: -------------------------------------------------------------------------------- 1 | (func) (memory 0) (func (export "f")) 2 | -------------------------------------------------------------------------------- /test/core/memory_grow.wast: -------------------------------------------------------------------------------- 1 | (module 2 | (memory 0) 3 | 4 | (func (export "load_at_zero") (result i32) (i32.load (i32.const 0))) 5 | (func (export "store_at_zero") (i32.store (i32.const 0) (i32.const 2))) 6 | 7 | (func (export "load_at_page_size") (result i32) (i32.load (i32.const 0x10000))) 8 | (func (export "store_at_page_size") (i32.store (i32.const 0x10000) (i32.const 3))) 9 | 10 | (func (export "grow") (param $sz i32) (result i32) (memory.grow (get_local $sz))) 11 | (func (export "size") (result i32) (memory.size)) 12 | ) 13 | 14 | (assert_return (invoke "size") (i32.const 0)) 15 | (assert_trap (invoke "store_at_zero") "out of bounds memory access") 16 | (assert_trap (invoke "load_at_zero") "out of bounds memory access") 17 | (assert_trap (invoke "store_at_page_size") "out of bounds memory access") 18 | (assert_trap (invoke "load_at_page_size") "out of bounds memory access") 19 | (assert_return (invoke "grow" (i32.const 1)) (i32.const 0)) 20 | (assert_return (invoke "size") (i32.const 1)) 21 | (assert_return (invoke "load_at_zero") (i32.const 0)) 22 | (assert_return (invoke "store_at_zero")) 23 | (assert_return (invoke "load_at_zero") (i32.const 2)) 24 | (assert_trap (invoke "store_at_page_size") "out of bounds memory access") 25 | (assert_trap (invoke "load_at_page_size") "out of bounds memory access") 26 | (assert_return (invoke "grow" (i32.const 4)) (i32.const 1)) 27 | (assert_return (invoke "size") (i32.const 5)) 28 | (assert_return (invoke "load_at_zero") (i32.const 2)) 29 | (assert_return (invoke "store_at_zero")) 30 | (assert_return (invoke "load_at_zero") (i32.const 2)) 31 | (assert_return (invoke "load_at_page_size") (i32.const 0)) 32 | (assert_return (invoke "store_at_page_size")) 33 | (assert_return (invoke "load_at_page_size") (i32.const 3)) 34 | 35 | 36 | (module 37 | (memory 0) 38 | (func (export "grow") (param i32) (result i32) (memory.grow (get_local 0))) 39 | ) 40 | 41 | (assert_return (invoke "grow" (i32.const 0)) (i32.const 0)) 42 | (assert_return (invoke "grow" (i32.const 1)) (i32.const 0)) 43 | (assert_return (invoke "grow" (i32.const 0)) (i32.const 1)) 44 | (assert_return (invoke "grow" (i32.const 2)) (i32.const 1)) 45 | (assert_return (invoke "grow" (i32.const 800)) (i32.const 3)) 46 | (assert_return (invoke "grow" (i32.const 0x10000)) (i32.const -1)) 47 | (assert_return (invoke "grow" (i32.const 64736)) (i32.const -1)) 48 | (assert_return (invoke "grow" (i32.const 1)) (i32.const 803)) 49 | 50 | (module 51 | (memory 0 10) 52 | (func (export "grow") (param i32) (result i32) (memory.grow (get_local 0))) 53 | ) 54 | 55 | (assert_return (invoke "grow" (i32.const 0)) (i32.const 0)) 56 | (assert_return (invoke "grow" (i32.const 1)) (i32.const 0)) 57 | (assert_return (invoke "grow" (i32.const 1)) (i32.const 1)) 58 | (assert_return (invoke "grow" (i32.const 2)) (i32.const 2)) 59 | (assert_return (invoke "grow" (i32.const 6)) (i32.const 4)) 60 | (assert_return (invoke "grow" (i32.const 0)) (i32.const 10)) 61 | (assert_return (invoke "grow" (i32.const 1)) (i32.const -1)) 62 | (assert_return (invoke "grow" (i32.const 0x10000)) (i32.const -1)) 63 | 64 | ;; Test that newly allocated memory (program start and memory.grow) is zeroed 65 | 66 | (module 67 | (memory 1) 68 | (func (export "grow") (param i32) (result i32) 69 | (memory.grow (get_local 0)) 70 | ) 71 | (func (export "check-memory-zero") (param i32 i32) (result i32) 72 | (local i32) 73 | (set_local 2 (i32.const 1)) 74 | (block 75 | (loop 76 | (set_local 2 (i32.load8_u (get_local 0))) 77 | (br_if 1 (i32.ne (get_local 2) (i32.const 0))) 78 | (br_if 1 (i32.ge_u (get_local 0) (get_local 1))) 79 | (set_local 0 (i32.add (get_local 0) (i32.const 1))) 80 | (br_if 0 (i32.le_u (get_local 0) (get_local 1))) 81 | ) 82 | ) 83 | (get_local 2) 84 | ) 85 | ) 86 | 87 | (assert_return (invoke "check-memory-zero" (i32.const 0) (i32.const 0xffff)) (i32.const 0)) 88 | (assert_return (invoke "grow" (i32.const 1)) (i32.const 1)) 89 | (assert_return (invoke "check-memory-zero" (i32.const 0x10000) (i32.const 0x1_ffff)) (i32.const 0)) 90 | (assert_return (invoke "grow" (i32.const 1)) (i32.const 2)) 91 | (assert_return (invoke "check-memory-zero" (i32.const 0x20000) (i32.const 0x2_ffff)) (i32.const 0)) 92 | (assert_return (invoke "grow" (i32.const 1)) (i32.const 3)) 93 | (assert_return (invoke "check-memory-zero" (i32.const 0x30000) (i32.const 0x3_ffff)) (i32.const 0)) 94 | (assert_return (invoke "grow" (i32.const 1)) (i32.const 4)) 95 | (assert_return (invoke "check-memory-zero" (i32.const 0x40000) (i32.const 0x4_ffff)) (i32.const 0)) 96 | (assert_return (invoke "grow" (i32.const 1)) (i32.const 5)) 97 | (assert_return (invoke "check-memory-zero" (i32.const 0x50000) (i32.const 0x5_ffff)) (i32.const 0)) 98 | -------------------------------------------------------------------------------- /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 | (set_local $t (i32.load (i32.const 8))) 25 | (i32.store (i32.const 5) (i32.const 0x80000000)) 26 | (set_local $s (i32.load (i32.const 8))) 27 | (i32.add (get_local $t) (get_local $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 | (set_local $t (f32.load (i32.const 11))) 34 | (i32.store (i32.const 8) (i32.const 0)) 35 | (get_local $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 | (set_local $x (call $malloc (i32.const 4))) 52 | (set_local $y (call $malloc (i32.const 4))) 53 | (i32.store (get_local $x) (i32.const 42)) 54 | (i32.store (get_local $y) (i32.const 43)) 55 | (i32.load (get_local $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/run.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 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 | 31 | class RunTests(unittest.TestCase): 32 | def _runCommand(self, command, logPath, expectedExitCode = 0): 33 | with open(logPath, 'w+') as out: 34 | exitCode = subprocess.call(command, shell=True, stdout=out, stderr=subprocess.STDOUT) 35 | self.assertEqual(expectedExitCode, exitCode, "failed with exit code %i (expected %i) for %s" % (exitCode, expectedExitCode, command)) 36 | 37 | def _auxFile(self, path): 38 | if os.path.exists(path): 39 | os.remove(path) 40 | return path 41 | 42 | def _compareFile(self, expectFile, actualFile): 43 | if os.path.exists(expectFile): 44 | with open(expectFile) as expect: 45 | with open(actualFile) as actual: 46 | expectText = expect.read() 47 | actualText = actual.read() 48 | self.assertEqual(expectText, actualText) 49 | 50 | def _runTestFile(self, inputPath): 51 | dir, inputFile = os.path.split(inputPath) 52 | outputPath = os.path.join(outputDir, inputFile) 53 | 54 | # Run original file 55 | expectedExitCode = 1 if ".fail." in inputFile else 0 56 | logPath = self._auxFile(outputPath + ".log") 57 | self._runCommand(('%s "%s"') % (wasmCommand, inputPath), logPath, expectedExitCode) 58 | 59 | if expectedExitCode != 0: 60 | return 61 | 62 | # Convert to binary and validate again 63 | wasmPath = self._auxFile(outputPath + ".bin.wast") 64 | logPath = self._auxFile(wasmPath + ".log") 65 | self._runCommand(('%s -d "%s" -o "%s"') % (wasmCommand, inputPath, wasmPath), logPath) 66 | self._runCommand(('%s -d "%s"') % (wasmCommand, wasmPath), logPath) 67 | 68 | # Convert back to text and validate again 69 | wastPath = self._auxFile(wasmPath + ".wast") 70 | logPath = self._auxFile(wastPath + ".log") 71 | self._runCommand(('%s -d "%s" -o "%s"') % (wasmCommand, wasmPath, wastPath), logPath) 72 | self._runCommand(('%s -d "%s" ') % (wasmCommand, wastPath), logPath) 73 | 74 | # Convert back to binary once more and compare 75 | wasm2Path = self._auxFile(wastPath + ".bin.wast") 76 | logPath = self._auxFile(wasm2Path + ".log") 77 | self._runCommand(('%s -d "%s" -o "%s"') % (wasmCommand, wastPath, wasm2Path), logPath) 78 | self._runCommand(('%s -d "%s"') % (wasmCommand, wasm2Path), logPath) 79 | # TODO: The binary should stay the same, but OCaml's float-string conversions are inaccurate. 80 | # Once we upgrade to OCaml 4.03, use sprintf "%s" for printing floats. 81 | # self._compareFile(wasmPath, wasm2Path) 82 | 83 | # Convert to JavaScript 84 | jsPath = self._auxFile(outputPath.replace(".wast", ".js")) 85 | logPath = self._auxFile(jsPath + ".log") 86 | self._runCommand(('%s -d "%s" -o "%s"') % (wasmCommand, inputPath, jsPath), logPath) 87 | if jsCommand != None: 88 | self._runCommand(('%s "%s"') % (jsCommand, jsPath), logPath) 89 | 90 | 91 | if __name__ == "__main__": 92 | if not os.path.exists(outputDir): 93 | os.makedirs(outputDir) 94 | for fileName in inputFiles: 95 | testName = 'test ' + os.path.basename(fileName) 96 | setattr(RunTests, testName, lambda self, file=fileName: self._runTestFile(file)) 97 | unittest.main() 98 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /test/core/store_retval.wast: -------------------------------------------------------------------------------- 1 | (assert_invalid 2 | (module (func (param i32) (result i32) (set_local 0 (i32.const 1)))) 3 | "type mismatch" 4 | ) 5 | (assert_invalid 6 | (module (func (param i64) (result i64) (set_local 0 (i64.const 1)))) 7 | "type mismatch" 8 | ) 9 | (assert_invalid 10 | (module (func (param f32) (result f32) (set_local 0 (f32.const 1)))) 11 | "type mismatch" 12 | ) 13 | (assert_invalid 14 | (module (func (param f64) (result f64) (set_local 0 (f64.const 1)))) 15 | "type mismatch" 16 | ) 17 | 18 | (assert_invalid 19 | (module (memory 1) (func (param i32) (result i32) (i32.store (i32.const 0) (i32.const 1)))) 20 | "type mismatch" 21 | ) 22 | (assert_invalid 23 | (module (memory 1) (func (param i64) (result i64) (i64.store (i32.const 0) (i64.const 1)))) 24 | "type mismatch" 25 | ) 26 | (assert_invalid 27 | (module (memory 1) (func (param f32) (result f32) (f32.store (i32.const 0) (f32.const 1)))) 28 | "type mismatch" 29 | ) 30 | (assert_invalid 31 | (module (memory 1) (func (param f64) (result f64) (f64.store (i32.const 0) (f64.const 1)))) 32 | "type mismatch" 33 | ) 34 | 35 | (assert_invalid 36 | (module (memory 1) (func (param i32) (result i32) (i32.store8 (i32.const 0) (i32.const 1)))) 37 | "type mismatch" 38 | ) 39 | (assert_invalid 40 | (module (memory 1) (func (param i32) (result i32) (i32.store16 (i32.const 0) (i32.const 1)))) 41 | "type mismatch" 42 | ) 43 | (assert_invalid 44 | (module (memory 1) (func (param i64) (result i64) (i64.store8 (i32.const 0) (i64.const 1)))) 45 | "type mismatch" 46 | ) 47 | (assert_invalid 48 | (module (memory 1) (func (param i64) (result i64) (i64.store16 (i32.const 0) (i64.const 1)))) 49 | "type mismatch" 50 | ) 51 | (assert_invalid 52 | (module (memory 1) (func (param i64) (result i64) (i64.store32 (i32.const 0) (i64.const 1)))) 53 | "type mismatch" 54 | ) 55 | 56 | -------------------------------------------------------------------------------- /test/core/switch.wast: -------------------------------------------------------------------------------- 1 | (module 2 | ;; Statement switch 3 | (func (export "stmt") (param $i i32) (result i32) 4 | (local $j i32) 5 | (set_local $j (i32.const 100)) 6 | (block $switch 7 | (block $7 8 | (block $default 9 | (block $6 10 | (block $5 11 | (block $4 12 | (block $3 13 | (block $2 14 | (block $1 15 | (block $0 16 | (br_table $0 $1 $2 $3 $4 $5 $6 $7 $default 17 | (get_local $i) 18 | ) 19 | ) ;; 0 20 | (return (get_local $i)) 21 | ) ;; 1 22 | (nop) 23 | ;; fallthrough 24 | ) ;; 2 25 | ;; fallthrough 26 | ) ;; 3 27 | (set_local $j (i32.sub (i32.const 0) (get_local $i))) 28 | (br $switch) 29 | ) ;; 4 30 | (br $switch) 31 | ) ;; 5 32 | (set_local $j (i32.const 101)) 33 | (br $switch) 34 | ) ;; 6 35 | (set_local $j (i32.const 101)) 36 | ;; fallthrough 37 | ) ;; default 38 | (set_local $j (i32.const 102)) 39 | ) ;; 7 40 | ;; fallthrough 41 | ) 42 | (return (get_local $j)) 43 | ) 44 | 45 | ;; Expression switch 46 | (func (export "expr") (param $i i64) (result i64) 47 | (local $j i64) 48 | (set_local $j (i64.const 100)) 49 | (return 50 | (block $switch (result i64) 51 | (block $7 52 | (block $default 53 | (block $4 54 | (block $5 55 | (block $6 56 | (block $3 57 | (block $2 58 | (block $1 59 | (block $0 60 | (br_table $0 $1 $2 $3 $4 $5 $6 $7 $default 61 | (i32.wrap/i64 (get_local $i)) 62 | ) 63 | ) ;; 0 64 | (return (get_local $i)) 65 | ) ;; 1 66 | (nop) 67 | ;; fallthrough 68 | ) ;; 2 69 | ;; fallthrough 70 | ) ;; 3 71 | (br $switch (i64.sub (i64.const 0) (get_local $i))) 72 | ) ;; 6 73 | (set_local $j (i64.const 101)) 74 | ;; fallthrough 75 | ) ;; 4 76 | ;; fallthrough 77 | ) ;; 5 78 | ;; fallthrough 79 | ) ;; default 80 | (br $switch (get_local $j)) 81 | ) ;; 7 82 | (i64.const -5) 83 | ) 84 | ) 85 | ) 86 | 87 | ;; Argument switch 88 | (func (export "arg") (param $i i32) (result i32) 89 | (return 90 | (block $2 (result i32) 91 | (i32.add (i32.const 10) 92 | (block $1 (result i32) 93 | (i32.add (i32.const 100) 94 | (block $0 (result i32) 95 | (i32.add (i32.const 1000) 96 | (block $default (result i32) 97 | (br_table $0 $1 $2 $default 98 | (i32.mul (i32.const 2) (get_local $i)) 99 | (i32.and (i32.const 3) (get_local $i)) 100 | ) 101 | ) 102 | ) 103 | ) 104 | ) 105 | ) 106 | ) 107 | ) 108 | ) 109 | ) 110 | 111 | ;; Corner cases 112 | (func (export "corner") (result i32) 113 | (block 114 | (br_table 0 (i32.const 0)) 115 | ) 116 | (i32.const 1) 117 | ) 118 | ) 119 | 120 | (assert_return (invoke "stmt" (i32.const 0)) (i32.const 0)) 121 | (assert_return (invoke "stmt" (i32.const 1)) (i32.const -1)) 122 | (assert_return (invoke "stmt" (i32.const 2)) (i32.const -2)) 123 | (assert_return (invoke "stmt" (i32.const 3)) (i32.const -3)) 124 | (assert_return (invoke "stmt" (i32.const 4)) (i32.const 100)) 125 | (assert_return (invoke "stmt" (i32.const 5)) (i32.const 101)) 126 | (assert_return (invoke "stmt" (i32.const 6)) (i32.const 102)) 127 | (assert_return (invoke "stmt" (i32.const 7)) (i32.const 100)) 128 | (assert_return (invoke "stmt" (i32.const -10)) (i32.const 102)) 129 | 130 | (assert_return (invoke "expr" (i64.const 0)) (i64.const 0)) 131 | (assert_return (invoke "expr" (i64.const 1)) (i64.const -1)) 132 | (assert_return (invoke "expr" (i64.const 2)) (i64.const -2)) 133 | (assert_return (invoke "expr" (i64.const 3)) (i64.const -3)) 134 | (assert_return (invoke "expr" (i64.const 6)) (i64.const 101)) 135 | (assert_return (invoke "expr" (i64.const 7)) (i64.const -5)) 136 | (assert_return (invoke "expr" (i64.const -10)) (i64.const 100)) 137 | 138 | (assert_return (invoke "arg" (i32.const 0)) (i32.const 110)) 139 | (assert_return (invoke "arg" (i32.const 1)) (i32.const 12)) 140 | (assert_return (invoke "arg" (i32.const 2)) (i32.const 4)) 141 | (assert_return (invoke "arg" (i32.const 3)) (i32.const 1116)) 142 | (assert_return (invoke "arg" (i32.const 4)) (i32.const 118)) 143 | (assert_return (invoke "arg" (i32.const 5)) (i32.const 20)) 144 | (assert_return (invoke "arg" (i32.const 6)) (i32.const 12)) 145 | (assert_return (invoke "arg" (i32.const 7)) (i32.const 1124)) 146 | (assert_return (invoke "arg" (i32.const 8)) (i32.const 126)) 147 | 148 | (assert_return (invoke "corner") (i32.const 1)) 149 | 150 | (assert_invalid (module (func (br_table 3 (i32.const 0)))) "unknown label") 151 | -------------------------------------------------------------------------------- /test/core/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/traps.wast: -------------------------------------------------------------------------------- 1 | ;; Test that traps are preserved even in instructions which might otherwise 2 | ;; be dead-code-eliminated. These functions all perform an operation and 3 | ;; discard its return value. 4 | 5 | (module 6 | (func (export "no_dce.i32.div_s") (param $x i32) (param $y i32) 7 | (drop (i32.div_s (get_local $x) (get_local $y)))) 8 | (func (export "no_dce.i32.div_u") (param $x i32) (param $y i32) 9 | (drop (i32.div_u (get_local $x) (get_local $y)))) 10 | (func (export "no_dce.i64.div_s") (param $x i64) (param $y i64) 11 | (drop (i64.div_s (get_local $x) (get_local $y)))) 12 | (func (export "no_dce.i64.div_u") (param $x i64) (param $y i64) 13 | (drop (i64.div_u (get_local $x) (get_local $y)))) 14 | ) 15 | 16 | (assert_trap (invoke "no_dce.i32.div_s" (i32.const 1) (i32.const 0)) "integer divide by zero") 17 | (assert_trap (invoke "no_dce.i32.div_u" (i32.const 1) (i32.const 0)) "integer divide by zero") 18 | (assert_trap (invoke "no_dce.i64.div_s" (i64.const 1) (i64.const 0)) "integer divide by zero") 19 | (assert_trap (invoke "no_dce.i64.div_u" (i64.const 1) (i64.const 0)) "integer divide by zero") 20 | (assert_trap (invoke "no_dce.i32.div_s" (i32.const 0x80000000) (i32.const -1)) "integer overflow") 21 | (assert_trap (invoke "no_dce.i64.div_s" (i64.const 0x8000000000000000) (i64.const -1)) "integer overflow") 22 | 23 | (module 24 | (func (export "no_dce.i32.rem_s") (param $x i32) (param $y i32) 25 | (drop (i32.rem_s (get_local $x) (get_local $y)))) 26 | (func (export "no_dce.i32.rem_u") (param $x i32) (param $y i32) 27 | (drop (i32.rem_u (get_local $x) (get_local $y)))) 28 | (func (export "no_dce.i64.rem_s") (param $x i64) (param $y i64) 29 | (drop (i64.rem_s (get_local $x) (get_local $y)))) 30 | (func (export "no_dce.i64.rem_u") (param $x i64) (param $y i64) 31 | (drop (i64.rem_u (get_local $x) (get_local $y)))) 32 | ) 33 | 34 | (assert_trap (invoke "no_dce.i32.rem_s" (i32.const 1) (i32.const 0)) "integer divide by zero") 35 | (assert_trap (invoke "no_dce.i32.rem_u" (i32.const 1) (i32.const 0)) "integer divide by zero") 36 | (assert_trap (invoke "no_dce.i64.rem_s" (i64.const 1) (i64.const 0)) "integer divide by zero") 37 | (assert_trap (invoke "no_dce.i64.rem_u" (i64.const 1) (i64.const 0)) "integer divide by zero") 38 | 39 | (module 40 | (func (export "no_dce.i32.trunc_s_f32") (param $x f32) (drop (i32.trunc_s/f32 (get_local $x)))) 41 | (func (export "no_dce.i32.trunc_u_f32") (param $x f32) (drop (i32.trunc_u/f32 (get_local $x)))) 42 | (func (export "no_dce.i32.trunc_s_f64") (param $x f64) (drop (i32.trunc_s/f64 (get_local $x)))) 43 | (func (export "no_dce.i32.trunc_u_f64") (param $x f64) (drop (i32.trunc_u/f64 (get_local $x)))) 44 | (func (export "no_dce.i64.trunc_s_f32") (param $x f32) (drop (i64.trunc_s/f32 (get_local $x)))) 45 | (func (export "no_dce.i64.trunc_u_f32") (param $x f32) (drop (i64.trunc_u/f32 (get_local $x)))) 46 | (func (export "no_dce.i64.trunc_s_f64") (param $x f64) (drop (i64.trunc_s/f64 (get_local $x)))) 47 | (func (export "no_dce.i64.trunc_u_f64") (param $x f64) (drop (i64.trunc_u/f64 (get_local $x)))) 48 | ) 49 | 50 | (assert_trap (invoke "no_dce.i32.trunc_s_f32" (f32.const nan)) "invalid conversion to integer") 51 | (assert_trap (invoke "no_dce.i32.trunc_u_f32" (f32.const nan)) "invalid conversion to integer") 52 | (assert_trap (invoke "no_dce.i32.trunc_s_f64" (f64.const nan)) "invalid conversion to integer") 53 | (assert_trap (invoke "no_dce.i32.trunc_u_f64" (f64.const nan)) "invalid conversion to integer") 54 | (assert_trap (invoke "no_dce.i64.trunc_s_f32" (f32.const nan)) "invalid conversion to integer") 55 | (assert_trap (invoke "no_dce.i64.trunc_u_f32" (f32.const nan)) "invalid conversion to integer") 56 | (assert_trap (invoke "no_dce.i64.trunc_s_f64" (f64.const nan)) "invalid conversion to integer") 57 | (assert_trap (invoke "no_dce.i64.trunc_u_f64" (f64.const nan)) "invalid conversion to integer") 58 | 59 | (module 60 | (memory 1) 61 | 62 | (func (export "no_dce.i32.load") (param $i i32) (drop (i32.load (get_local $i)))) 63 | (func (export "no_dce.i32.load16_s") (param $i i32) (drop (i32.load16_s (get_local $i)))) 64 | (func (export "no_dce.i32.load16_u") (param $i i32) (drop (i32.load16_u (get_local $i)))) 65 | (func (export "no_dce.i32.load8_s") (param $i i32) (drop (i32.load8_s (get_local $i)))) 66 | (func (export "no_dce.i32.load8_u") (param $i i32) (drop (i32.load8_u (get_local $i)))) 67 | (func (export "no_dce.i64.load") (param $i i32) (drop (i64.load (get_local $i)))) 68 | (func (export "no_dce.i64.load32_s") (param $i i32) (drop (i64.load32_s (get_local $i)))) 69 | (func (export "no_dce.i64.load32_u") (param $i i32) (drop (i64.load32_u (get_local $i)))) 70 | (func (export "no_dce.i64.load16_s") (param $i i32) (drop (i64.load16_s (get_local $i)))) 71 | (func (export "no_dce.i64.load16_u") (param $i i32) (drop (i64.load16_u (get_local $i)))) 72 | (func (export "no_dce.i64.load8_s") (param $i i32) (drop (i64.load8_s (get_local $i)))) 73 | (func (export "no_dce.i64.load8_u") (param $i i32) (drop (i64.load8_u (get_local $i)))) 74 | (func (export "no_dce.f32.load") (param $i i32) (drop (f32.load (get_local $i)))) 75 | (func (export "no_dce.f64.load") (param $i i32) (drop (f64.load (get_local $i)))) 76 | ) 77 | 78 | (assert_trap (invoke "no_dce.i32.load" (i32.const 65536)) "out of bounds memory access") 79 | (assert_trap (invoke "no_dce.i32.load16_s" (i32.const 65536)) "out of bounds memory access") 80 | (assert_trap (invoke "no_dce.i32.load16_u" (i32.const 65536)) "out of bounds memory access") 81 | (assert_trap (invoke "no_dce.i32.load8_s" (i32.const 65536)) "out of bounds memory access") 82 | (assert_trap (invoke "no_dce.i32.load8_u" (i32.const 65536)) "out of bounds memory access") 83 | (assert_trap (invoke "no_dce.i64.load" (i32.const 65536)) "out of bounds memory access") 84 | (assert_trap (invoke "no_dce.i64.load32_s" (i32.const 65536)) "out of bounds memory access") 85 | (assert_trap (invoke "no_dce.i64.load32_u" (i32.const 65536)) "out of bounds memory access") 86 | (assert_trap (invoke "no_dce.i64.load16_s" (i32.const 65536)) "out of bounds memory access") 87 | (assert_trap (invoke "no_dce.i64.load16_u" (i32.const 65536)) "out of bounds memory access") 88 | (assert_trap (invoke "no_dce.i64.load8_s" (i32.const 65536)) "out of bounds memory access") 89 | (assert_trap (invoke "no_dce.i64.load8_u" (i32.const 65536)) "out of bounds memory access") 90 | (assert_trap (invoke "no_dce.f32.load" (i32.const 65536)) "out of bounds memory access") 91 | (assert_trap (invoke "no_dce.f64.load" (i32.const 65536)) "out of bounds memory access") 92 | -------------------------------------------------------------------------------- /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 | 52 | (assert_invalid 53 | (module (type (func (result i32 i32)))) 54 | "invalid result arity" 55 | ) 56 | (assert_invalid 57 | (module (type (func (result i32) (result i32)))) 58 | "invalid result arity" 59 | ) 60 | -------------------------------------------------------------------------------- /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/html/indexeddb.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 WebAssembly Community Group participants 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | (function testIndexedDB() { 18 | const IDB_NAME = "spec-test"; 19 | const OBJECT_STORE_NAME = "wasm"; 20 | 21 | let db = null; 22 | 23 | function openDB() { 24 | console.log('Opening db...'); 25 | return new Promise((resolve, reject) => { 26 | request = indexedDB.open(IDB_NAME, 1); 27 | request.onerror = reject; 28 | request.onsuccess = () => { 29 | db = request.result; 30 | console.log('Retrieved db:', db); 31 | resolve(); 32 | }; 33 | request.onupgradeneeded = () => { 34 | console.log('Creating object store...'); 35 | request.result.createObjectStore(OBJECT_STORE_NAME); 36 | request.onerror = reject; 37 | request.onupgradeneeded = reject; 38 | request.onsuccess = () => { 39 | db = request.result; 40 | console.log('Created db:', db); 41 | resolve(); 42 | }; 43 | }; 44 | }); 45 | } 46 | 47 | function getObjectStore() { 48 | return db.transaction([OBJECT_STORE_NAME], "readwrite").objectStore(OBJECT_STORE_NAME); 49 | } 50 | 51 | function clearStore() { 52 | console.log('Clearing store...'); 53 | return new Promise((resolve, reject) => { 54 | var request = getObjectStore().clear(); 55 | request.onerror = reject; 56 | request.onupgradeneeded = reject; 57 | request.onsuccess = resolve; 58 | }); 59 | } 60 | 61 | function makeModule() { 62 | return new Promise(resolve => { 63 | let builder = new WasmModuleBuilder(); 64 | builder.addFunction('run', kSig_i_v) 65 | .addBody([ 66 | kExprI32Const, 67 | 42, 68 | kExprReturn 69 | ]) 70 | .exportFunc(); 71 | let source = builder.toBuffer(); 72 | 73 | let module = new WebAssembly.Module(source); 74 | let i = new WebAssembly.Instance(module); 75 | assert_equals(i.exports.run(), 42); 76 | 77 | resolve(module); 78 | }); 79 | } 80 | 81 | function storeWasm(module) { 82 | console.log('Storing wasm object...', module); 83 | return new Promise((resolve, reject) => { 84 | request = getObjectStore().add(module, 1); 85 | request.onsuccess = resolve; 86 | request.onerror = reject; 87 | request.onupgradeneeded = reject; 88 | }); 89 | } 90 | 91 | function loadWasm() { 92 | console.log('Loading wasm object...'); 93 | return new Promise((resolve, reject) => { 94 | var request = getObjectStore().get(1); 95 | request.onsuccess = () => { 96 | let i = new WebAssembly.Instance(request.result); 97 | assert_equals(i.exports.run(), 42); 98 | resolve(); 99 | } 100 | request.onerror = reject; 101 | request.onupgradeneeded = reject; 102 | }); 103 | } 104 | 105 | function run() { 106 | return openDB() 107 | .then(() => clearStore()) 108 | .then(() => makeModule()) 109 | .then(wasm => storeWasm(wasm)) 110 | .then(() => loadWasm()); 111 | } 112 | 113 | promise_test(run, "store and load from indexeddb"); 114 | })(); 115 | -------------------------------------------------------------------------------- /test/js-api/README.md: -------------------------------------------------------------------------------- 1 | This directory contains tests specific to the JavaScript API to WebAssembly, as 2 | described in [JS.md](https://github.com/WebAssembly/design/blob/master/JS.md). 3 | 4 | ## Harness 5 | 6 | These tests can be run in a pure JavaScript environment, that is, a JS shell 7 | (like V8 or spidermonkey's shells), provided a few libraries and functions 8 | emulating the 9 | [testharness.js](http://testthewebforward.org/docs/testharness-library.html) 10 | library. 11 | 12 | - The `../harness/index.js`, `../harness/wasm-constants.js` and 13 | `../harness/wasm-module-builder.js` must be imported first. 14 | - A function `test(function, description)` that tries to run the function under 15 | a try/catch and maybe asserts in case of failure. 16 | - A function `promise_test(function, description)` where `function` returns a 17 | `Promise` run by `promise_test`; a rejection means a failure here. 18 | - Assertion functions: `assert_equals(x, y)`, `assert_not_equals(x, y)`, 19 | `assert_true(x)`, `assert_false(x)`, `assert_unreached()`. 20 | --------------------------------------------------------------------------------