├── test
├── core
│ ├── .gitignore
│ ├── inline-module.wast
│ ├── token.wast
│ ├── break-drop.wast
│ ├── forward.wast
│ ├── README.md
│ ├── comments.wast
│ ├── store_retval.wast
│ ├── type.wast
│ ├── memory_redundancy.wast
│ ├── start.wast
│ ├── fac.wast
│ ├── custom.wast
│ ├── run.py
│ ├── func_ptrs.wast
│ ├── memory_grow.wast
│ ├── get_local.wast
│ ├── switch.wast
│ ├── const.wast
│ └── traps.wast
├── harness
│ ├── testharnessreport.js
│ └── testharness.css
├── js-api
│ └── README.md
├── Todo.md
├── README.md
└── html
│ └── indexeddb.js
├── document
├── core
│ ├── .gitignore
│ ├── static
│ │ ├── webassembly.png
│ │ └── custom.css
│ ├── util
│ │ ├── README.htmldiff.pl
│ │ ├── katex_fix.patch
│ │ ├── pseudo-lexer.py
│ │ ├── bikeshed_fixup.py
│ │ ├── mathdef.py
│ │ └── mathdefbs.py
│ ├── intro
│ │ ├── index.rst
│ │ └── introduction.rst
│ ├── valid
│ │ ├── index.rst
│ │ └── types.rst
│ ├── exec
│ │ └── index.rst
│ ├── syntax
│ │ ├── index.rst
│ │ └── conventions.rst
│ ├── binary
│ │ ├── index.rst
│ │ ├── types.rst
│ │ ├── conventions.rst
│ │ └── values.rst
│ ├── text
│ │ ├── index.rst
│ │ ├── types.rst
│ │ └── lexical.rst
│ ├── appendix
│ │ ├── index.rst
│ │ ├── index-types.rst
│ │ └── custom.rst
│ ├── README.md
│ ├── index.rst
│ ├── index.bs
│ └── LICENSE
├── README.md
├── js-api
│ └── Makefile
├── web-api
│ └── Makefile
├── Makefile
├── travis-deploy.sh
└── index.html
├── interpreter
├── script
│ ├── js.mli
│ ├── import.mli
│ ├── run.mli
│ ├── import.ml
│ └── script.ml
├── meta
│ ├── jslib
│ │ ├── bsconfig.json
│ │ ├── wasm.ml
│ │ └── build.sh
│ ├── findlib
│ │ └── META
│ └── travis
│ │ ├── build-test.sh
│ │ └── install-ocaml.sh
├── exec
│ ├── numeric_error.ml
│ ├── i32.ml
│ ├── i64.ml
│ ├── f64.ml
│ ├── i32_convert.mli
│ ├── f32_convert.mli
│ ├── f64_convert.mli
│ ├── eval_numeric.mli
│ ├── i64_convert.mli
│ ├── eval.mli
│ ├── f32.ml
│ ├── i32_convert.ml
│ ├── f64_convert.ml
│ ├── f32_convert.ml
│ ├── i64_convert.ml
│ └── eval_numeric.ml
├── binary
│ ├── decode.mli
│ ├── encode.mli
│ ├── utf8.mli
│ └── utf8.ml
├── valid
│ └── valid.mli
├── text
│ ├── lexer.mli
│ ├── arrange.mli
│ ├── print.mli
│ ├── print.ml
│ ├── parse.mli
│ └── parse.ml
├── .gitignore
├── main
│ ├── flags.ml
│ └── main.ml
├── util
│ ├── sexpr.mli
│ ├── error.mli
│ ├── error.ml
│ ├── source.mli
│ ├── source.ml
│ ├── sexpr.ml
│ ├── lib.mli
│ └── lib.ml
├── runtime
│ ├── global.mli
│ ├── func.mli
│ ├── func.ml
│ ├── global.ml
│ ├── table.mli
│ ├── instance.ml
│ ├── memory.mli
│ ├── table.ml
│ └── memory.ml
├── host
│ ├── env.ml
│ └── spectest.ml
├── syntax
│ ├── values.ml
│ └── types.ml
└── Makefile
├── .gitignore
├── deploy_key.enc
├── papers
├── pldi2017.pdf
└── LICENSE
├── .gitmodules
├── .gitattributes
├── Contributing.md
├── LICENSE
├── .travis.yml
└── README.md
/test/core/.gitignore:
--------------------------------------------------------------------------------
1 | output
--------------------------------------------------------------------------------
/document/core/.gitignore:
--------------------------------------------------------------------------------
1 | _build
2 | _static
3 | document/*.pyc
4 |
--------------------------------------------------------------------------------
/interpreter/script/js.mli:
--------------------------------------------------------------------------------
1 | val of_script : Script.script -> string
2 |
--------------------------------------------------------------------------------
/test/core/inline-module.wast:
--------------------------------------------------------------------------------
1 | (func) (memory 0) (func (export "f"))
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | **/*~
2 | **/*.tmproj
3 | **/*.pyc
4 | **/_build
5 | **/_output
6 |
--------------------------------------------------------------------------------
/deploy_key.enc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WebAssembly/funclets/HEAD/deploy_key.enc
--------------------------------------------------------------------------------
/papers/pldi2017.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WebAssembly/funclets/HEAD/papers/pldi2017.pdf
--------------------------------------------------------------------------------
/interpreter/meta/jslib/bsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "wasm",
3 | "sources": [
4 | {"dir": "src"},
5 | ]
6 | }
7 |
--------------------------------------------------------------------------------
/document/core/static/webassembly.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WebAssembly/funclets/HEAD/document/core/static/webassembly.png
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "document/core/util/katex"]
2 | path = document/core/util/katex
3 | url = https://github.com/Khan/KaTeX.git
4 |
--------------------------------------------------------------------------------
/interpreter/exec/numeric_error.ml:
--------------------------------------------------------------------------------
1 | exception IntegerOverflow
2 | exception IntegerDivideByZero
3 | exception InvalidConversionToInteger
4 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/valid/valid.mli:
--------------------------------------------------------------------------------
1 | exception Invalid of Source.region * string
2 |
3 | val check_module : Ast.module_ -> unit (* raises Invalid *)
4 |
--------------------------------------------------------------------------------
/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/binary/utf8.mli:
--------------------------------------------------------------------------------
1 | exception Utf8
2 |
3 | val decode : string -> int list (* raises UTf8 *)
4 | val encode : int list -> string (* raises Utf8 *)
5 |
--------------------------------------------------------------------------------
/document/core/intro/index.rst:
--------------------------------------------------------------------------------
1 | .. _intro:
2 |
3 | Introduction
4 | ============
5 |
6 | .. toctree::
7 | :maxdepth: 2
8 |
9 | introduction
10 | overview
11 |
--------------------------------------------------------------------------------
/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/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/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/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 |
--------------------------------------------------------------------------------
/document/core/valid/index.rst:
--------------------------------------------------------------------------------
1 | .. _valid:
2 |
3 | Validation
4 | ==========
5 |
6 | .. toctree::
7 | :maxdepth: 2
8 |
9 | conventions
10 | types
11 | instructions
12 | modules
13 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/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/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/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/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/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/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/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_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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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/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/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/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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/index.bs:
--------------------------------------------------------------------------------
1 |
2 | Title: WebAssembly Core Specification
3 | Shortname: wasm-core
4 | Group: wasm
5 | Status: ED
6 | Level: 1
7 | TR: https://www.w3.org/TR/wasm-core-1/
8 | ED: https://webassembly.github.io/spec/core/bikeshed/
9 | Editor: Andreas Rossberg (Dfinity Stiftung)
10 | Repository: WebAssembly/spec
11 | Markup Shorthands: css no, markdown yes, algorithm no
12 | Abstract: This document describes version 1.0 of the core WebAssembly standard, a safe, portable, low-level code format designed for efficient execution and compact representation.
13 | Prepare For TR: true
14 |
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 |
--------------------------------------------------------------------------------
/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/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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"(? 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/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/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/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/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 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](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 |
--------------------------------------------------------------------------------
/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/js-api/Makefile:
--------------------------------------------------------------------------------
1 | BUILDDIR = _build
2 | STATICDIR = _static
3 | DOWNLOADDIR = _download
4 | NAME = WebAssembly
5 |
6 | .PHONY: all
7 | all:
8 | mkdir -p $(BUILDDIR)/html
9 | bikeshed spec index.bs $(BUILDDIR)/html/index.html
10 | @echo "Build finished. The HTML pages are in `pwd`/$(BUILDDIR)/html."
11 |
12 | .PHONY: publish
13 | publish:
14 | (cd ..; make publish-js-api)
15 |
16 | .PHONY: clean
17 | clean:
18 | rm -rf $(BUILDDIR)
19 | rm -rf $(STATICDIR)
20 |
21 | .PHONY: diff
22 | diff: all
23 | @echo "Downloading the old single-file html spec..."
24 | curl `grep "^TR" index.bs | cut -d' ' -f2` -o $(BUILDDIR)/html/old.html
25 | @echo "Done."
26 | @echo "Diffing new against old..."
27 | perl ../util/htmldiff.pl $(BUILDDIR)/html/old.html $(BUILDDIR)/html/index.html $(BUILDDIR)/html/diff.html
28 | @echo "Done. The diff is at $(BUILDDIR)/html/diff.html"
29 |
30 | .PHONY: WD-tar
31 | WD-tar:
32 | bikeshed echidna --just-tar index.bs $(BUILDDIR)/html/index.html
33 | mv test.tar $(BUILDDIR)/WD.tar
34 | @echo "Built $(BUILDDIR)/WD.tar."
35 |
36 | .PHONY: WD-echidna
37 | WD-echidna:
38 | @if [ -z $(W3C_USERNAME) ] || \
39 | [ -z $(W3C_PASSWORD) ] || \
40 | [ -z $(DECISION_URL) ] ; then \
41 | echo "Must provide W3C_USERNAME, W3C_PASSWORD, and DECISION_URL environment variables"; \
42 | exit 1; \
43 | fi
44 | bikeshed echidna index.bs --u $(W3C_USERNAME) --p $(W3C_PASSWORD) --d $(DECISION_URL)
45 |
--------------------------------------------------------------------------------
/document/web-api/Makefile:
--------------------------------------------------------------------------------
1 | BUILDDIR = _build
2 | STATICDIR = _static
3 | DOWNLOADDIR = _download
4 | NAME = WebAssembly
5 |
6 | .PHONY: all
7 | all:
8 | mkdir -p $(BUILDDIR)/html
9 | bikeshed spec index.bs $(BUILDDIR)/html/index.html
10 | @echo "Build finished. The HTML pages are in `pwd`/$(BUILDDIR)/html."
11 |
12 | .PHONY: publish
13 | publish:
14 | (cd ..; make publish-web-api)
15 |
16 | .PHONY: clean
17 | clean:
18 | rm -rf $(BUILDDIR)
19 | rm -rf $(STATICDIR)
20 |
21 | .PHONY: diff
22 | diff: all
23 | @echo "Downloading the old single-file html spec..."
24 | curl `grep "^TR" index.bs | cut -d' ' -f2` -o $(BUILDDIR)/html/old.html
25 | @echo "Done."
26 | @echo "Diffing new against old..."
27 | perl ../util/htmldiff.pl $(BUILDDIR)/html/old.html $(BUILDDIR)/html/index.html $(BUILDDIR)/html/diff.html
28 | @echo "Done. The diff is at $(BUILDDIR)/html/diff.html"
29 |
30 | .PHONY: WD-tar
31 | WD-tar:
32 | bikeshed echidna --just-tar index.bs $(BUILDDIR)/html/index.html
33 | mv test.tar $(BUILDDIR)/WD.tar
34 | @echo "Built $(BUILDDIR)/WD.tar."
35 |
36 | .PHONY: WD-echidna
37 | WD-echidna:
38 | @if [ -z $(W3C_USERNAME) ] || \
39 | [ -z $(W3C_PASSWORD) ] || \
40 | [ -z $(DECISION_URL) ] ; then \
41 | echo "Must provide W3C_USERNAME, W3C_PASSWORD, and DECISION_URL environment variables"; \
42 | exit 1; \
43 | fi
44 | bikeshed echidna index.bs --u $(W3C_USERNAME) --p $(W3C_PASSWORD) --d $(DECISION_URL)
45 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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/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/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/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 | }
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/core/valid/types.rst:
--------------------------------------------------------------------------------
1 | Types
2 | -----
3 |
4 | Most :ref:`types ` 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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------