├── test ├── core │ ├── .gitignore │ ├── inline-module.wast │ ├── token.wast │ ├── break-drop.wast │ ├── forward.wast │ ├── align.wast │ ├── README.md │ ├── comments.wast │ ├── store_retval.wast │ ├── type.wast │ ├── memory_redundancy.wast │ ├── start.wast │ ├── binary.wast │ ├── resizing.wast │ ├── fac.wast │ ├── stack.wast │ ├── custom_section.wast │ ├── run.py │ ├── select.wast │ ├── func_ptrs.wast │ ├── get_local.wast │ ├── address.wast │ ├── switch.wast │ ├── const.wast │ ├── globals.wast │ ├── elem.wast │ └── traps.wast ├── harness │ ├── testharnessreport.js │ └── testharness.css ├── js-api │ └── README.md ├── README.md ├── Todo.md └── html │ └── indexeddb.js ├── document └── core │ ├── .gitignore │ ├── static │ ├── webassembly.png │ └── custom.css │ ├── intro │ ├── index.rst │ └── introduction.rst │ ├── valid │ ├── index.rst │ └── types.rst │ ├── exec │ └── index.rst │ ├── syntax │ ├── index.rst │ └── conventions.rst │ ├── appendix │ ├── index.rst │ ├── index-types.rst │ └── custom.rst │ ├── binary │ ├── index.rst │ ├── types.rst │ ├── values.rst │ └── conventions.rst │ ├── text │ ├── index.rst │ ├── types.rst │ └── lexical.rst │ ├── README.md │ ├── index.rst │ ├── util │ ├── pseudo-lexer.py │ └── mathdef.py │ └── LICENSE ├── 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 ├── binary │ ├── decode.mli │ ├── encode.mli │ ├── utf8.mli │ └── utf8.ml ├── valid │ └── valid.mli ├── .gitignore ├── text │ ├── lexer.mli │ ├── arrange.mli │ ├── print.mli │ ├── print.ml │ ├── parse.mli │ └── parse.ml ├── 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 ├── papers ├── pldi2017.pdf └── LICENSE ├── .travis.yml ├── .gitattributes ├── Contributing.md ├── LICENSE ├── README.md └── proposals └── interface-types └── working-notes └── WorkingNotes.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 | -------------------------------------------------------------------------------- /papers/pldi2017.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/WebAssembly/interface-types/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/interface-types/HEAD/document/core/static/webassembly.png -------------------------------------------------------------------------------- /interpreter/exec/numeric_error.ml: -------------------------------------------------------------------------------- 1 | exception IntegerOverflow 2 | exception IntegerDivideByZero 3 | exception InvalidConversionToInteger 4 | -------------------------------------------------------------------------------- /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/.gitignore: -------------------------------------------------------------------------------- 1 | *.cmo 2 | *.cmx 3 | *.native 4 | *.byte 5 | *.opt 6 | *.unopt 7 | *.js 8 | *.zip 9 | *.mlpack 10 | _build 11 | wasm 12 | 13 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c++ 2 | 3 | sudo: off 4 | 5 | install: ./interpreter/meta/travis/install-ocaml.sh 6 | 7 | script: ./interpreter/meta/travis/build-test.sh 8 | 9 | os: 10 | - linux 11 | - osx 12 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 Specification 2 | 3 | This is (meant to become) 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 | -------------------------------------------------------------------------------- /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: 3 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Interface Types Proposal for WebAssembly 2 | 3 | **Note: This proposal is currently inactive, with work having moved on to 4 | the [component-model](https://github.com/WebAssembly/component-model) repo. This 5 | proposal repo could become active again in the future to resume work on the 6 | [Adapter Functions](https://github.com/WebAssembly/component-model/blob/main/design/mvp/FutureFeatures.md#custom-abis-via-adapter-functions) 7 | future feature.** 8 | 9 | ---- 10 | 11 | This repository is a clone of github.com/WebAssembly/spec/. It is meant for 12 | discussion, prototype specification and implementation of the Interface Types 13 | proposal. 14 | 15 | See the [explainer](proposals/interface-types/Explainer.md) for an in-progress 16 | summary of the proposal. 17 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /test/core/align.wast: -------------------------------------------------------------------------------- 1 | (assert_malformed 2 | (module quote 3 | "(module (memory 0) (func (drop (i64.load align=0 (i32.const 0)))))" 4 | ) 5 | "alignment" 6 | ) 7 | (assert_malformed 8 | (module quote 9 | "(module (memory 0) (func (drop (i64.load align=7 (i32.const 0)))))" 10 | ) 11 | "alignment" 12 | ) 13 | (assert_invalid 14 | (module (memory 0) (func (drop (i64.load align=16 (i32.const 0))))) 15 | "alignment" 16 | ) 17 | 18 | (assert_malformed 19 | (module quote 20 | "(module (memory 0) (func (i64.store align=0 (i32.const 0) (i64.const 0))))" 21 | ) 22 | "alignment" 23 | ) 24 | (assert_malformed 25 | (module quote 26 | "(module (memory 0) (func (i64.store align=5 (i32.const 0) (i64.const 0))))" 27 | ) 28 | "alignment" 29 | ) 30 | (assert_invalid 31 | (module (memory 0) (func (i64.store align=16 (i32.const 0) (i64.const 0)))) 32 | "alignment" 33 | ) 34 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /proposals/interface-types/working-notes/WorkingNotes.md: -------------------------------------------------------------------------------- 1 | # Working Notes 2 | 3 | The purpose of this directory is to hold notes about the current design of the 4 | Interface Types proposal. These notes are intended as analogous to the 5 | [WebAssembly design repo](https://github.com/WebAssembly/design/), as a 6 | less-formal way of designing components of the proposal. Documents should 7 | reflect our current consensus, but the ideas don't need to be fully fleshed out. 8 | These documents should inform the final spec. 9 | 10 | Sample topics: 11 | * How we should handle Wasm exceptions 12 | * How engines might need to optimize combinations of adapters 13 | * What is necessary for MVP, and what can be deferred 14 | 15 | In general, we should try to match the conventions that are already established, 16 | but when inventing some new topic, just making up syntax/instructions is the 17 | right way to go. 18 | 19 | ### Q: Why not add these to the Explainer? 20 | These aren't necessarily things that the explainer needs to spell out. If the 21 | purpose of the explainer is to convey the information to a reader from scratch, 22 | the nuances of a given design detail is likely to be distracting detail. It is 23 | likely that some subsets will wind up in the explainer over time. 24 | -------------------------------------------------------------------------------- /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/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", ExternFuncType t -> ExternFunc (func print t) 36 | | "print", _ -> 37 | let t = FuncType ([], []) in ExternFunc (func print t) 38 | | "global", ExternGlobalType t -> ExternGlobal (global t) 39 | | "global", _ -> ExternGlobal (global (GlobalType (I32Type, Immutable))) 40 | | "table", _ -> ExternTable table 41 | | "memory", _ -> ExternMemory memory 42 | | _ -> raise Not_found 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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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 mem_size = Mem8 | Mem16 | Mem32 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 mem_size : mem_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 | mem_size -> extension -> memory -> address -> offset -> value_type -> value 41 | (* raises Type, Bounds *) 42 | val store_packed : 43 | mem_size -> memory -> address -> offset -> value -> unit 44 | (* raises Type, Bounds *) 45 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /interpreter/util/lib.mli: -------------------------------------------------------------------------------- 1 | (* Things that should be in the OCaml library... *) 2 | 3 | module Fun : 4 | sig 5 | val repeat : int -> ('a -> unit) -> 'a -> unit 6 | end 7 | 8 | module List : 9 | sig 10 | val make : int -> 'a -> 'a list 11 | val table : int -> (int -> 'a) -> 'a list 12 | val take : int -> 'a list -> 'a list (* raises Failure *) 13 | val drop : int -> 'a list -> 'a list (* raises Failure *) 14 | 15 | val last : 'a list -> 'a (* raises Failure *) 16 | val split_last : 'a list -> 'a list * 'a (* raises Failure *) 17 | 18 | val index_of : 'a -> 'a list -> int option 19 | val index_where : ('a -> bool) -> 'a list -> int option 20 | val map_filter : ('a -> 'b option) -> 'a list -> 'b list 21 | end 22 | 23 | module List32 : 24 | sig 25 | val length : 'a list -> int32 26 | val nth : 'a list -> int32 -> 'a (* raises Failure *) 27 | val take : int32 -> 'a list -> 'a list (* raises Failure *) 28 | val drop : int32 -> 'a list -> 'a list (* raises Failure *) 29 | end 30 | 31 | module Array32 : 32 | sig 33 | val make : int32 -> 'a -> 'a array 34 | val length : 'a array -> int32 35 | val get : 'a array -> int32 -> 'a 36 | val set : 'a array -> int32 -> 'a -> unit 37 | val blit : 'a array -> int32 -> 'a array -> int32 -> int32 -> unit 38 | end 39 | 40 | module Bigarray : 41 | sig 42 | open Bigarray 43 | 44 | module Array1_64 : 45 | sig 46 | val create : ('a, 'b) kind -> 'c layout -> int64 -> ('a, 'b, 'c) Array1.t 47 | val dim : ('a, 'b, 'c) Array1.t -> int64 48 | val get : ('a, 'b, 'c) Array1.t -> int64 -> 'a 49 | val set : ('a, 'b, 'c) Array1.t -> int64 -> 'a -> unit 50 | val sub : ('a, 'b, 'c) Array1.t -> int64 -> int64 -> ('a, 'b, 'c) Array1.t 51 | end 52 | end 53 | 54 | module Option : 55 | sig 56 | val get : 'a option -> 'a -> 'a 57 | val map : ('a -> 'b) -> 'a option -> 'b option 58 | val app : ('a -> unit) -> 'a option -> unit 59 | end 60 | 61 | module Int : 62 | sig 63 | val log2 : int -> int 64 | val is_power_of_two : int -> bool 65 | end 66 | 67 | module String : 68 | sig 69 | val implode : char list -> string 70 | val explode : string -> char list 71 | val split : string -> char -> string list 72 | val breakup : string -> int -> string list 73 | end 74 | -------------------------------------------------------------------------------- /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 newly allocated memory (program start and `grow_memory`) is zeroed 8 | - test that `grow_memory` does a full 32-bit unsigned check for page-size divisibility 9 | - test that load/store addreses are full int32 (or int64), and not OCaml int 10 | - test that when allocating 4GiB, accessing index -1 fails 11 | - test that too-big `grow_memory` fails appropriately 12 | - test that too-big linear memory initial allocation fails 13 | - test that one can clobber the entire contents of the linear memory without corrupting: call stack, local variables, program execution. 14 | - test that an i64 store with 4-byte alignment that's 4 bytes out of bounds traps without storing anything. 15 | 16 | Misc optimizer bait: 17 | - test that the scheduler doesn't move a trapping div past a call which may not return 18 | - test that linearized multidimensional array accesses can have overindexing in interesting ways 19 | - test that 32-bit loop induction variables that wrap aren't promoted to 64-bit 20 | - test that code after a non-obviously infinite loop is not executed 21 | 22 | Misc x86 optimizer bait: 23 | - test that oeq handles NaN right in if, if-else, and setcc cases 24 | 25 | SIMD (post-MVP): 26 | - test that SIMD insert/extract don't canonicalize NaNs 27 | - test that SIMD lanes are in little-endian order 28 | - test non-constant-index and out-of-bounds shuffle masks 29 | - test that subnormals work as intended 30 | - test that byte-misaligned accesses work 31 | 32 | Threads (post-MVP): 33 | - test that thread-local variables are actually thread-local 34 | - test that atomic operations that isLockFree says are lock-free actually are 35 | (is this possible?) 36 | - test that isLockFree is true for datatypes that the spec says should 37 | always be lock-free 38 | - test that 16-bit and 8-bit cmpxchg does a wrapped 8-bit or 16-bit compare 39 | 40 | FMA (post-MVP): 41 | - http://www.vinc17.org/software/fma-tests.c 42 | -------------------------------------------------------------------------------- /test/core/memory_redundancy.wast: -------------------------------------------------------------------------------- 1 | ;; Test that optimizers don't do redundant-load, store-to-load, or dead-store 2 | ;; optimizations when there are interfering stores, even of different types 3 | ;; and to non-identical addresses. 4 | 5 | (module 6 | (memory 1 1) 7 | 8 | (func (export "zero_everything") 9 | (i32.store (i32.const 0) (i32.const 0)) 10 | (i32.store (i32.const 4) (i32.const 0)) 11 | (i32.store (i32.const 8) (i32.const 0)) 12 | (i32.store (i32.const 12) (i32.const 0)) 13 | ) 14 | 15 | (func (export "test_store_to_load") (result i32) 16 | (i32.store (i32.const 8) (i32.const 0)) 17 | (f32.store (i32.const 5) (f32.const -0.0)) 18 | (i32.load (i32.const 8)) 19 | ) 20 | 21 | (func (export "test_redundant_load") (result i32) 22 | (local $t i32) 23 | (local $s i32) 24 | (set_local $t (i32.load (i32.const 8))) 25 | (i32.store (i32.const 5) (i32.const 0x80000000)) 26 | (set_local $s (i32.load (i32.const 8))) 27 | (i32.add (get_local $t) (get_local $s)) 28 | ) 29 | 30 | (func (export "test_dead_store") (result f32) 31 | (local $t f32) 32 | (i32.store (i32.const 8) (i32.const 0x23232323)) 33 | (set_local $t (f32.load (i32.const 11))) 34 | (i32.store (i32.const 8) (i32.const 0)) 35 | (get_local $t) 36 | ) 37 | 38 | ;; A function named "malloc" which implementations nonetheless shouldn't 39 | ;; assume behaves like C malloc. 40 | (func $malloc (export "malloc") 41 | (param $size i32) 42 | (result i32) 43 | (i32.const 16) 44 | ) 45 | 46 | ;; Call malloc twice, but unlike C malloc, we don't get non-aliasing pointers. 47 | (func (export "malloc_aliasing") 48 | (result i32) 49 | (local $x i32) 50 | (local $y i32) 51 | (set_local $x (call $malloc (i32.const 4))) 52 | (set_local $y (call $malloc (i32.const 4))) 53 | (i32.store (get_local $x) (i32.const 42)) 54 | (i32.store (get_local $y) (i32.const 43)) 55 | (i32.load (get_local $x)) 56 | ) 57 | ) 58 | 59 | (assert_return (invoke "test_store_to_load") (i32.const 0x00000080)) 60 | (invoke "zero_everything") 61 | (assert_return (invoke "test_redundant_load") (i32.const 0x00000080)) 62 | (invoke "zero_everything") 63 | (assert_return (invoke "test_dead_store") (f32.const 0x1.18p-144)) 64 | (invoke "zero_everything") 65 | (assert_return (invoke "malloc_aliasing") (i32.const 43)) 66 | -------------------------------------------------------------------------------- /test/core/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") (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") (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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /test/core/binary.wast: -------------------------------------------------------------------------------- 1 | (module binary "\00asm\01\00\00\00") 2 | (module binary "\00asm" "\01\00\00\00") 3 | (module $M1 binary "\00asm\01\00\00\00") 4 | (module $M2 binary "\00asm" "\01\00\00\00") 5 | 6 | (assert_malformed (module binary "") "unexpected end") 7 | (assert_malformed (module binary "\01") "unexpected end") 8 | (assert_malformed (module binary "\00as") "unexpected end") 9 | (assert_malformed (module binary "asm\00") "magic header not detected") 10 | (assert_malformed (module binary "msa\00") "magic header not detected") 11 | (assert_malformed (module binary "msa\00\01\00\00\00") "magic header not detected") 12 | (assert_malformed (module binary "msa\00\00\00\00\01") "magic header not detected") 13 | (assert_malformed (module binary "asm\01\00\00\00\00") "magic header not detected") 14 | (assert_malformed (module binary "wasm\01\00\00\00") "magic header not detected") 15 | (assert_malformed (module binary "\7fasm\01\00\00\00") "magic header not detected") 16 | (assert_malformed (module binary "\80asm\01\00\00\00") "magic header not detected") 17 | (assert_malformed (module binary "\82asm\01\00\00\00") "magic header not detected") 18 | (assert_malformed (module binary "\ffasm\01\00\00\00") "magic header not detected") 19 | 20 | ;; 8-byte endian-reversed. 21 | (assert_malformed (module binary "\00\00\00\01msa\00") "magic header not detected") 22 | 23 | ;; Middle-endian byte orderings. 24 | (assert_malformed (module binary "a\00ms\00\01\00\00") "magic header not detected") 25 | (assert_malformed (module binary "sm\00a\00\00\01\00") "magic header not detected") 26 | 27 | ;; Upper-cased. 28 | (assert_malformed (module binary "\00ASM\01\00\00\00") "magic header not detected") 29 | 30 | ;; EBCDIC-encoded magic. 31 | (assert_malformed (module binary "\00\81\a2\94\01\00\00\00") "magic header not detected") 32 | 33 | ;; Leading UTF-8 BOM. 34 | (assert_malformed (module binary "\ef\bb\bf\00asm\01\00\00\00") "magic header not detected") 35 | 36 | (assert_malformed (module binary "\00asm") "unexpected end") 37 | (assert_malformed (module binary "\00asm\01") "unexpected end") 38 | (assert_malformed (module binary "\00asm\01\00\00") "unexpected end") 39 | (assert_malformed (module binary "\00asm\00\00\00\00") "unknown binary version") 40 | (assert_malformed (module binary "\00asm\0d\00\00\00") "unknown binary version") 41 | (assert_malformed (module binary "\00asm\0e\00\00\00") "unknown binary version") 42 | (assert_malformed (module binary "\00asm\00\01\00\00") "unknown binary version") 43 | (assert_malformed (module binary "\00asm\00\00\01\00") "unknown binary version") 44 | (assert_malformed (module binary "\00asm\00\00\00\01") "unknown binary version") 45 | -------------------------------------------------------------------------------- /test/core/resizing.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) (grow_memory (get_local $sz))) 11 | (func (export "size") (result i32) (current_memory)) 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) (grow_memory (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 | 48 | (module 49 | (memory 0 10) 50 | (func (export "grow") (param i32) (result i32) (grow_memory (get_local 0))) 51 | ) 52 | 53 | (assert_return (invoke "grow" (i32.const 0)) (i32.const 0)) 54 | (assert_return (invoke "grow" (i32.const 1)) (i32.const 0)) 55 | (assert_return (invoke "grow" (i32.const 1)) (i32.const 1)) 56 | (assert_return (invoke "grow" (i32.const 2)) (i32.const 2)) 57 | (assert_return (invoke "grow" (i32.const 6)) (i32.const 4)) 58 | (assert_return (invoke "grow" (i32.const 0)) (i32.const 10)) 59 | (assert_return (invoke "grow" (i32.const 1)) (i32.const -1)) 60 | (assert_return (invoke "grow" (i32.const 0x10000)) (i32.const -1)) 61 | -------------------------------------------------------------------------------- /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/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. 17 | 18 | :math:`\{ \LMIN~n, \LMAX~m^? \}` 19 | ................................ 20 | 21 | * If the maximum :math:`m^?` is not empty, then its value must not be smaller than :math:`n`. 22 | 23 | * Then the limit is valid. 24 | 25 | .. math:: 26 | \frac{ 27 | (n \leq m)^? 28 | }{ 29 | \vdashlimits \{ \LMIN~n, \LMAX~m^? \} \ok 30 | } 31 | 32 | 33 | .. index:: function type 34 | pair: validation; function type 35 | single: abstract syntax; function type 36 | .. _valid-functype: 37 | 38 | Function Types 39 | ~~~~~~~~~~~~~~ 40 | 41 | :ref:`Function types ` may not specify more than one result. 42 | 43 | :math:`[t_1^n] \to [t_2^m]` 44 | ........................... 45 | 46 | * The arity :math:`m` must not be larger than :math:`1`. 47 | 48 | * Then the function type is valid. 49 | 50 | .. math:: 51 | \frac{ 52 | }{ 53 | \vdashfunctype [t_1^\ast] \to [t_2^?] \ok 54 | } 55 | 56 | .. note:: 57 | This restriction may be removed in future versions of WebAssembly. 58 | 59 | 60 | .. index:: table type, element type, limits 61 | pair: validation; table type 62 | single: abstract syntax; table type 63 | .. _valid-tabletype: 64 | 65 | Table Types 66 | ~~~~~~~~~~~ 67 | 68 | :math:`\limits~\elemtype` 69 | ......................... 70 | 71 | * The limits :math:`\limits` must be :ref:`valid `. 72 | 73 | * Then the table type is valid. 74 | 75 | .. math:: 76 | \frac{ 77 | \vdashlimits \limits \ok 78 | }{ 79 | \vdashtabletype \limits~\elemtype \ok 80 | } 81 | 82 | 83 | .. index:: memory type, limits 84 | pair: validation; memory type 85 | single: abstract syntax; memory type 86 | .. _valid-memtype: 87 | 88 | Memory Types 89 | ~~~~~~~~~~~~ 90 | 91 | :math:`\limits` 92 | ............... 93 | 94 | * The limits :math:`\limits` must be :ref:`valid `. 95 | 96 | * Then the memory type is valid. 97 | 98 | .. math:: 99 | \frac{ 100 | \vdashlimits \limits \ok 101 | }{ 102 | \vdashmemtype \limits \ok 103 | } 104 | 105 | 106 | .. index:: global type, value type, mutability 107 | pair: validation; global type 108 | single: abstract syntax; global type 109 | .. _valid-globaltype: 110 | 111 | Global Types 112 | ~~~~~~~~~~~~ 113 | 114 | :math:`\mut~\valtype` 115 | ..................... 116 | 117 | * The global type is valid. 118 | 119 | .. math:: 120 | \frac{ 121 | }{ 122 | \vdashglobaltype \mut~\valtype \ok 123 | } 124 | -------------------------------------------------------------------------------- /test/core/stack.wast: -------------------------------------------------------------------------------- 1 | (module 2 | (func (export "fac-expr") (param $n i64) (result i64) 3 | (local $i i64) 4 | (local $res i64) 5 | (set_local $i (get_local $n)) 6 | (set_local $res (i64.const 1)) 7 | (block $done 8 | (loop $loop 9 | (if 10 | (i64.eq (get_local $i) (i64.const 0)) 11 | (then (br $done)) 12 | (else 13 | (set_local $res (i64.mul (get_local $i) (get_local $res))) 14 | (set_local $i (i64.sub (get_local $i) (i64.const 1))) 15 | ) 16 | ) 17 | (br $loop) 18 | ) 19 | ) 20 | (get_local $res) 21 | ) 22 | 23 | (func (export "fac-stack") (param $n i64) (result i64) 24 | (local $i i64) 25 | (local $res i64) 26 | (get_local $n) 27 | (set_local $i) 28 | (i64.const 1) 29 | (set_local $res) 30 | (block $done 31 | (loop $loop 32 | (get_local $i) 33 | (i64.const 0) 34 | (i64.eq) 35 | (if 36 | (then (br $done)) 37 | (else 38 | (get_local $i) 39 | (get_local $res) 40 | (i64.mul) 41 | (set_local $res) 42 | (get_local $i) 43 | (i64.const 1) 44 | (i64.sub) 45 | (set_local $i) 46 | ) 47 | ) 48 | (br $loop) 49 | ) 50 | ) 51 | (get_local $res) 52 | ) 53 | 54 | (func (export "fac-stack-raw") (param $n i64) (result i64) 55 | (local $i i64) 56 | (local $res i64) 57 | get_local $n 58 | set_local $i 59 | i64.const 1 60 | set_local $res 61 | block $done 62 | loop $loop 63 | get_local $i 64 | i64.const 0 65 | i64.eq 66 | if $body 67 | br $done 68 | else $body 69 | get_local $i 70 | get_local $res 71 | i64.mul 72 | set_local $res 73 | get_local $i 74 | i64.const 1 75 | i64.sub 76 | set_local $i 77 | end $body 78 | br $loop 79 | end $loop 80 | end $done 81 | get_local $res 82 | ) 83 | 84 | (func (export "fac-mixed") (param $n i64) (result i64) 85 | (local $i i64) 86 | (local $res i64) 87 | (set_local $i (get_local $n)) 88 | (set_local $res (i64.const 1)) 89 | (block $done 90 | (loop $loop 91 | (i64.eq (get_local $i) (i64.const 0)) 92 | (if 93 | (then (br $done)) 94 | (else 95 | (i64.mul (get_local $i) (get_local $res)) 96 | (set_local $res) 97 | (i64.sub (get_local $i) (i64.const 1)) 98 | (set_local $i) 99 | ) 100 | ) 101 | (br $loop) 102 | ) 103 | ) 104 | (get_local $res) 105 | ) 106 | 107 | (func (export "fac-mixed-raw") (param $n i64) (result i64) 108 | (local $i i64) 109 | (local $res i64) 110 | (set_local $i (get_local $n)) 111 | (set_local $res (i64.const 1)) 112 | block $done 113 | loop $loop 114 | (i64.eq (get_local $i) (i64.const 0)) 115 | if 116 | br $done 117 | else 118 | (i64.mul (get_local $i) (get_local $res)) 119 | set_local $res 120 | (i64.sub (get_local $i) (i64.const 1)) 121 | set_local $i 122 | end 123 | br $loop 124 | end 125 | end 126 | get_local $res 127 | ) 128 | ) 129 | 130 | (assert_return (invoke "fac-expr" (i64.const 25)) (i64.const 7034535277573963776)) 131 | (assert_return (invoke "fac-stack" (i64.const 25)) (i64.const 7034535277573963776)) 132 | (assert_return (invoke "fac-mixed" (i64.const 25)) (i64.const 7034535277573963776)) 133 | -------------------------------------------------------------------------------- /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_section.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\26\10" "a custom section" "this is the payload" 80 | ) 81 | "unexpected end" 82 | ) 83 | 84 | (assert_malformed 85 | (module binary 86 | "\00asm" "\01\00\00\00" 87 | "\00\25\10" "a custom section" "this is the payload" 88 | "\00\24\10" "a custom section" "this is the payload" 89 | ) 90 | "invalid section id" 91 | ) 92 | 93 | (assert_malformed 94 | (module binary 95 | "\00asm" "\01\00\00\00" 96 | "\01\07\01\60\02\7f\7f\01\7f" ;; type section 97 | "\00\25\10" "a custom section" "this is the payload" ;; invalid length! 98 | "\03\02\01\00" ;; function section 99 | "\0a\09\01\07\00\20\00\20\01\6a\0b" ;; code section 100 | "\00\1b\07" "custom2" "this is the payload" ;; custom section 101 | ) 102 | "function and code section have inconsistent lengths" 103 | ) 104 | 105 | ;; Test concatenated modules. 106 | (assert_malformed 107 | (module binary 108 | "\00asm\01\00\00\00" 109 | "\00asm\01\00\00\00" 110 | ) 111 | "length out of bounds" 112 | ) 113 | -------------------------------------------------------------------------------- /test/core/run.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from __future__ import print_function 4 | import argparse 5 | import os 6 | import os.path 7 | import unittest 8 | import subprocess 9 | import glob 10 | import sys 11 | 12 | 13 | ownDir = os.path.dirname(os.path.abspath(sys.argv[0])) 14 | inputDir = ownDir 15 | outputDir = os.path.join(inputDir, "_output") 16 | 17 | parser = argparse.ArgumentParser() 18 | parser.add_argument("--wasm", metavar="", default=os.path.join(os.getcwd(), "wasm")) 19 | parser.add_argument("--js", metavar="") 20 | parser.add_argument("--out", metavar="", default=outputDir) 21 | parser.add_argument("file", nargs='*') 22 | arguments = parser.parse_args() 23 | sys.argv = sys.argv[:1] 24 | 25 | wasmCommand = arguments.wasm 26 | jsCommand = arguments.js 27 | outputDir = arguments.out 28 | inputFiles = arguments.file if arguments.file else glob.glob(os.path.join(inputDir, "*.wast")) 29 | 30 | 31 | class RunTests(unittest.TestCase): 32 | def _runCommand(self, command, logPath, expectedExitCode = 0): 33 | with open(logPath, 'w+') as out: 34 | exitCode = subprocess.call(command, shell=True, stdout=out, stderr=subprocess.STDOUT) 35 | self.assertEqual(expectedExitCode, exitCode, "failed with exit code %i (expected %i) for %s" % (exitCode, expectedExitCode, command)) 36 | 37 | def _auxFile(self, path): 38 | if os.path.exists(path): 39 | os.remove(path) 40 | return path 41 | 42 | def _compareFile(self, expectFile, actualFile): 43 | if os.path.exists(expectFile): 44 | with open(expectFile) as expect: 45 | with open(actualFile) as actual: 46 | expectText = expect.read() 47 | actualText = actual.read() 48 | self.assertEqual(expectText, actualText) 49 | 50 | def _runTestFile(self, inputPath): 51 | dir, inputFile = os.path.split(inputPath) 52 | outputPath = os.path.join(outputDir, inputFile) 53 | 54 | # Run original file 55 | expectedExitCode = 1 if ".fail." in inputFile else 0 56 | logPath = self._auxFile(outputPath + ".log") 57 | self._runCommand(('%s "%s"') % (wasmCommand, inputPath), logPath, expectedExitCode) 58 | 59 | if expectedExitCode != 0: 60 | return 61 | 62 | # Convert to binary and validate again 63 | wasmPath = self._auxFile(outputPath + ".bin.wast") 64 | logPath = self._auxFile(wasmPath + ".log") 65 | self._runCommand(('%s -d "%s" -o "%s"') % (wasmCommand, inputPath, wasmPath), logPath) 66 | self._runCommand(('%s -d "%s"') % (wasmCommand, wasmPath), logPath) 67 | 68 | # Convert back to text and validate again 69 | wastPath = self._auxFile(wasmPath + ".wast") 70 | logPath = self._auxFile(wastPath + ".log") 71 | self._runCommand(('%s -d "%s" -o "%s"') % (wasmCommand, wasmPath, wastPath), logPath) 72 | self._runCommand(('%s -d "%s" ') % (wasmCommand, wastPath), logPath) 73 | 74 | # Convert back to binary once more and compare 75 | wasm2Path = self._auxFile(wastPath + ".bin.wast") 76 | logPath = self._auxFile(wasm2Path + ".log") 77 | self._runCommand(('%s -d "%s" -o "%s"') % (wasmCommand, wastPath, wasm2Path), logPath) 78 | self._runCommand(('%s -d "%s"') % (wasmCommand, wasm2Path), logPath) 79 | # TODO: The binary should stay the same, but OCaml's float-string conversions are inaccurate. 80 | # Once we upgrade to OCaml 4.03, use sprintf "%s" for printing floats. 81 | # self._compareFile(wasmPath, wasm2Path) 82 | 83 | # Convert to JavaScript 84 | jsPath = self._auxFile(outputPath.replace(".wast", ".js")) 85 | logPath = self._auxFile(jsPath + ".log") 86 | self._runCommand(('%s -d "%s" -o "%s"') % (wasmCommand, inputPath, jsPath), logPath) 87 | if jsCommand != None: 88 | self._runCommand(('%s "%s"') % (jsCommand, jsPath), logPath) 89 | 90 | 91 | if __name__ == "__main__": 92 | if not os.path.exists(outputDir): 93 | os.makedirs(outputDir) 94 | for fileName in inputFiles: 95 | testName = 'test ' + os.path.basename(fileName) 96 | setattr(RunTests, testName, lambda self, file=fileName: self._runTestFile(file)) 97 | unittest.main() 98 | -------------------------------------------------------------------------------- /test/core/select.wast: -------------------------------------------------------------------------------- 1 | (module 2 | (func (export "select_i32") (param $lhs i32) (param $rhs i32) (param $cond i32) (result i32) 3 | (select (get_local $lhs) (get_local $rhs) (get_local $cond))) 4 | 5 | (func (export "select_i64") (param $lhs i64) (param $rhs i64) (param $cond i32) (result i64) 6 | (select (get_local $lhs) (get_local $rhs) (get_local $cond))) 7 | 8 | (func (export "select_f32") (param $lhs f32) (param $rhs f32) (param $cond i32) (result f32) 9 | (select (get_local $lhs) (get_local $rhs) (get_local $cond))) 10 | 11 | (func (export "select_f64") (param $lhs f64) (param $rhs f64) (param $cond i32) (result f64) 12 | (select (get_local $lhs) (get_local $rhs) (get_local $cond))) 13 | 14 | ;; Check that both sides of the select are evaluated 15 | (func (export "select_trap_l") (param $cond i32) (result i32) 16 | (select (unreachable) (i32.const 0) (get_local $cond)) 17 | ) 18 | (func (export "select_trap_r") (param $cond i32) (result i32) 19 | (select (i32.const 0) (unreachable) (get_local $cond)) 20 | ) 21 | 22 | (func (export "select_unreached") 23 | (unreachable) (select) 24 | (unreachable) (i32.const 0) (select) 25 | (unreachable) (i32.const 0) (i32.const 0) (select) 26 | (unreachable) (f32.const 0) (i32.const 0) (select) 27 | (unreachable) 28 | ) 29 | ) 30 | 31 | (assert_return (invoke "select_i32" (i32.const 1) (i32.const 2) (i32.const 1)) (i32.const 1)) 32 | (assert_return (invoke "select_i64" (i64.const 2) (i64.const 1) (i32.const 1)) (i64.const 2)) 33 | (assert_return (invoke "select_f32" (f32.const 1) (f32.const 2) (i32.const 1)) (f32.const 1)) 34 | (assert_return (invoke "select_f64" (f64.const 1) (f64.const 2) (i32.const 1)) (f64.const 1)) 35 | 36 | (assert_return (invoke "select_i32" (i32.const 1) (i32.const 2) (i32.const 0)) (i32.const 2)) 37 | (assert_return (invoke "select_i32" (i32.const 2) (i32.const 1) (i32.const 0)) (i32.const 1)) 38 | (assert_return (invoke "select_i64" (i64.const 2) (i64.const 1) (i32.const -1)) (i64.const 2)) 39 | (assert_return (invoke "select_i64" (i64.const 2) (i64.const 1) (i32.const 0xf0f0f0f0)) (i64.const 2)) 40 | 41 | (assert_return (invoke "select_f32" (f32.const nan) (f32.const 1) (i32.const 1)) (f32.const nan)) 42 | (assert_return (invoke "select_f32" (f32.const nan:0x20304) (f32.const 1) (i32.const 1)) (f32.const nan:0x20304)) 43 | (assert_return (invoke "select_f32" (f32.const nan) (f32.const 1) (i32.const 0)) (f32.const 1)) 44 | (assert_return (invoke "select_f32" (f32.const nan:0x20304) (f32.const 1) (i32.const 0)) (f32.const 1)) 45 | (assert_return (invoke "select_f32" (f32.const 2) (f32.const nan) (i32.const 1)) (f32.const 2)) 46 | (assert_return (invoke "select_f32" (f32.const 2) (f32.const nan:0x20304) (i32.const 1)) (f32.const 2)) 47 | (assert_return (invoke "select_f32" (f32.const 2) (f32.const nan) (i32.const 0)) (f32.const nan)) 48 | (assert_return (invoke "select_f32" (f32.const 2) (f32.const nan:0x20304) (i32.const 0)) (f32.const nan:0x20304)) 49 | 50 | (assert_return (invoke "select_f64" (f64.const nan) (f64.const 1) (i32.const 1)) (f64.const nan)) 51 | (assert_return (invoke "select_f64" (f64.const nan:0x20304) (f64.const 1) (i32.const 1)) (f64.const nan:0x20304)) 52 | (assert_return (invoke "select_f64" (f64.const nan) (f64.const 1) (i32.const 0)) (f64.const 1)) 53 | (assert_return (invoke "select_f64" (f64.const nan:0x20304) (f64.const 1) (i32.const 0)) (f64.const 1)) 54 | (assert_return (invoke "select_f64" (f64.const 2) (f64.const nan) (i32.const 1)) (f64.const 2)) 55 | (assert_return (invoke "select_f64" (f64.const 2) (f64.const nan:0x20304) (i32.const 1)) (f64.const 2)) 56 | (assert_return (invoke "select_f64" (f64.const 2) (f64.const nan) (i32.const 0)) (f64.const nan)) 57 | (assert_return (invoke "select_f64" (f64.const 2) (f64.const nan:0x20304) (i32.const 0)) (f64.const nan:0x20304)) 58 | 59 | (assert_trap (invoke "select_trap_l" (i32.const 1)) "unreachable executed") 60 | (assert_trap (invoke "select_trap_l" (i32.const 0)) "unreachable executed") 61 | (assert_trap (invoke "select_trap_r" (i32.const 1)) "unreachable executed") 62 | (assert_trap (invoke "select_trap_r" (i32.const 0)) "unreachable executed") 63 | 64 | (assert_invalid 65 | (module (func $arity-0 (select (nop) (nop) (i32.const 1)))) 66 | "type mismatch" 67 | ) 68 | -------------------------------------------------------------------------------- /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 | kExprEnd 70 | ]) 71 | .exportFunc(); 72 | let source = builder.toBuffer(); 73 | 74 | let module = new WebAssembly.Module(source); 75 | let i = new WebAssembly.Instance(module); 76 | assert_equals(i.exports.run(), 42); 77 | 78 | resolve(module); 79 | }); 80 | } 81 | 82 | function storeWasm(module) { 83 | console.log('Storing wasm object...', module); 84 | return new Promise((resolve, reject) => { 85 | request = getObjectStore().add(module, 1); 86 | request.onsuccess = resolve; 87 | request.onerror = reject; 88 | request.onupgradeneeded = reject; 89 | }); 90 | } 91 | 92 | function loadWasm() { 93 | console.log('Loading wasm object...'); 94 | return new Promise((resolve, reject) => { 95 | var request = getObjectStore().get(1); 96 | request.onsuccess = () => { 97 | let i = new WebAssembly.Instance(request.result); 98 | assert_equals(i.exports.run(), 42); 99 | resolve(); 100 | } 101 | request.onerror = reject; 102 | request.onupgradeneeded = reject; 103 | }); 104 | } 105 | 106 | function run() { 107 | return openDB() 108 | .then(() => clearStore()) 109 | .then(() => makeModule()) 110 | .then(wasm => storeWasm(wasm)) 111 | .then(() => loadWasm()); 112 | } 113 | 114 | promise_test(run, "store and load from indexeddb"); 115 | })(); 116 | -------------------------------------------------------------------------------- /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 | opt: $(OPT) 33 | unopt: $(UNOPT) 34 | libopt: _build/$(LIB).cmx 35 | libunopt: _build/$(LIB).cmo 36 | jslib: $(JSLIB) 37 | all: unopt opt libunopt libopt test 38 | land: all $(WINMAKE) 39 | zip: $(ZIP) 40 | 41 | 42 | # Building executable 43 | 44 | empty = 45 | space = $(empty) $(empty) 46 | comma = , 47 | 48 | .INTERMEDIATE: _tags 49 | _tags: 50 | echo >$@ "true: bin_annot" 51 | echo >>$@ "<{$(subst $(space),$(comma),$(DIRS))}/*.cmx>: for-pack($(PACK))" 52 | 53 | $(UNOPT): main.d.byte 54 | mv $< $@ 55 | 56 | $(OPT): main.native 57 | mv $< $@ 58 | 59 | .PHONY: main.d.byte main.native 60 | main.d.byte: _tags 61 | $(OCB) -quiet $@ 62 | 63 | main.native: _tags 64 | $(OCB) -quiet $@ 65 | 66 | 67 | # Building library 68 | 69 | PACK = $(shell echo `echo $(LIB) | sed 's/^\(.\).*$$/\\1/g' | tr [:lower:] [:upper:]``echo $(LIB) | sed 's/^.\(.*\)$$/\\1/g'`) 70 | 71 | .INTERMEDIATE: $(LIB).mlpack 72 | $(LIB).mlpack: $(DIRS) 73 | ls $(DIRS:%=%/*.ml*) \ 74 | | sed 's:\(.*/\)\{0,1\}\(.*\)\.[^\.]*:\2:' \ 75 | | grep -v main \ 76 | | sort | uniq \ 77 | >$@ 78 | 79 | _build/$(LIB).cmo: $(LIB).mlpack _tags 80 | $(OCB) -quiet $(LIB).cmo 81 | 82 | _build/$(LIB).cmx: $(LIB).mlpack _tags 83 | $(OCB) -quiet $(LIB).cmx 84 | 85 | 86 | # Building JavaScript library 87 | 88 | .PHONY: $(JSLIB) 89 | $(JSLIB): $(UNOPT) 90 | mkdir -p _build/jslib/src 91 | cp meta/jslib/* _build/jslib 92 | cp $(DIRS:%=_build/%/*.ml*) meta/jslib/*.ml _build/jslib/src 93 | rm _build/jslib/src/*.ml[^i] 94 | (cd _build/jslib; ./build.sh ../../$@) 95 | 96 | 97 | # Building Windows build file 98 | 99 | $(WINMAKE): clean 100 | echo rem Auto-generated from Makefile! >$@ 101 | echo set NAME=$(NAME) >>$@ 102 | echo if \'%1\' neq \'\' set NAME=%1 >>$@ 103 | $(OCB) main.d.byte \ 104 | | grep -v ocamldep \ 105 | | grep -v mkdir \ 106 | | sed s:`which ocaml`:ocaml:g \ 107 | | sed s:host/main.d.byte:%NAME%.exe: \ 108 | >>$@ 109 | 110 | 111 | # Executing test suite 112 | 113 | .PHONY: test debugtest 114 | 115 | test: $(OPT) 116 | ../test/core/run.py --wasm `pwd`/$(OPT) $(if $(JS),--js '$(JS)',) 117 | debugtest: $(UNOPT) 118 | ../test/core/run.py --wasm `pwd`/$(UNOPT) $(if $(JS),--js '$(JS)',) 119 | 120 | test/%: $(OPT) 121 | ../test/core/run.py --wasm `pwd`/$(OPT) $(if $(JS),--js '$(JS)',) $(@:test/%=../test/core/%.wast) 122 | debugtest/%: $(UNOPT) 123 | ../test/core/run.py --wasm `pwd`/$(UNOPT) $(if $(JS),--js '$(JS)',) $(@:debugtest/%=../test/core/%.wast) 124 | 125 | run/%: $(OPT) 126 | ./$(OPT) $(@:run/%=../test/core/%.wast) 127 | debug/%: $(UNOPT) 128 | ./$(UNOPT) $(@:debug/%=../test/core/%.wast) 129 | 130 | 131 | # Miscellaneous targets 132 | 133 | .PHONY: clean 134 | 135 | $(ZIP): $(WINMAKE) 136 | git archive --format=zip --prefix=$(NAME)/ -o $@ HEAD 137 | 138 | clean: 139 | rm -rf _build/jslib $(LIB).mlpack _tags 140 | $(OCB) -clean 141 | 142 | # Opam support 143 | 144 | .PHONY: check install uninstall 145 | 146 | check: 147 | # Check that we can find all relevant libraries 148 | # when using ocamlfind 149 | ocamlfind query $(LIBS) 150 | 151 | install: _build/$(LIB).cmx _build/$(LIB).cmo 152 | ocamlfind install $(LIB) meta/findlib/META _build/$(LIB).o \ 153 | $(wildcard _build/$(LIB).cm*) \ 154 | $(wildcard $(DIRS:%=%/*.mli)) 155 | 156 | uninstall: 157 | ocamlfind remove $(LIB) 158 | -------------------------------------------------------------------------------- /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") (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" (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 $T (get_local $i)) 64 | ) 65 | 66 | (func (export "callu") (param $i i32) (result i32) 67 | (call_indirect $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 $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 mem_size = Mem8 | Mem16 | Mem32 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 mem_size = function 26 | | Mem8 -> 1 27 | | Mem16 -> 2 28 | | Mem32 -> 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 (mem_size sz <= Types.size t); 130 | let n = mem_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 (mem_size sz <= Types.size (Values.type_of v)); 139 | let n = mem_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 | -------------------------------------------------------------------------------- /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 encodings of small negative :math:`\xref{binary/values}{binary-sint}{\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 | -------------------------------------------------------------------------------- /test/core/address.wast: -------------------------------------------------------------------------------- 1 | (module 2 | (memory 1) 3 | (data (i32.const 0) "abcdefghijklmnopqrstuvwxyz") 4 | 5 | (func (export "good1") (param $i i32) (result i32) 6 | (i32.load8_u offset=0 (get_local $i)) ;; 97 'a' 7 | ) 8 | (func (export "good2") (param $i i32) (result i32) 9 | (i32.load8_u offset=1 (get_local $i)) ;; 98 'b' 10 | ) 11 | (func (export "good3") (param $i i32) (result i32) 12 | (i32.load8_u offset=2 (get_local $i)) ;; 99 'c' 13 | ) 14 | (func (export "good4") (param $i i32) (result i32) 15 | (i32.load8_u offset=25 (get_local $i)) ;; 122 'z' 16 | ) 17 | 18 | (func (export "good5") (param $i i32) (result i32) 19 | (i32.load16_u offset=0 (get_local $i)) ;; 25185 'ab' 20 | ) 21 | (func (export "good6") (param $i i32) (result i32) 22 | (i32.load16_u align=1 (get_local $i)) ;; 25185 'ab' 23 | ) 24 | (func (export "good7") (param $i i32) (result i32) 25 | (i32.load16_u offset=1 align=1 (get_local $i)) ;; 25442 'bc' 26 | ) 27 | (func (export "good8") (param $i i32) (result i32) 28 | (i32.load16_u offset=2 (get_local $i)) ;; 25699 'cd' 29 | ) 30 | (func (export "good9") (param $i i32) (result i32) 31 | (i32.load16_u offset=25 align=1 (get_local $i)) ;; 122 'z\0' 32 | ) 33 | 34 | (func (export "good10") (param $i i32) (result i32) 35 | (i32.load offset=0 (get_local $i)) ;; 1684234849 'abcd' 36 | ) 37 | (func (export "good11") (param $i i32) (result i32) 38 | (i32.load offset=1 align=1 (get_local $i)) ;; 1701077858 'bcde' 39 | ) 40 | (func (export "good12") (param $i i32) (result i32) 41 | (i32.load offset=2 align=2 (get_local $i)) ;; 1717920867 'cdef' 42 | ) 43 | (func (export "good13") (param $i i32) (result i32) 44 | (i32.load offset=25 align=1 (get_local $i)) ;; 122 'z\0\0\0' 45 | ) 46 | 47 | (func (export "bad") (param $i i32) 48 | (drop (i32.load offset=4294967295 (get_local $i))) 49 | ) 50 | ) 51 | 52 | (assert_return (invoke "good1" (i32.const 0)) (i32.const 97)) 53 | (assert_return (invoke "good2" (i32.const 0)) (i32.const 98)) 54 | (assert_return (invoke "good3" (i32.const 0)) (i32.const 99)) 55 | (assert_return (invoke "good4" (i32.const 0)) (i32.const 122)) 56 | (assert_return (invoke "good5" (i32.const 0)) (i32.const 25185)) 57 | (assert_return (invoke "good6" (i32.const 0)) (i32.const 25185)) 58 | (assert_return (invoke "good7" (i32.const 0)) (i32.const 25442)) 59 | (assert_return (invoke "good8" (i32.const 0)) (i32.const 25699)) 60 | (assert_return (invoke "good9" (i32.const 0)) (i32.const 122)) 61 | (assert_return (invoke "good10" (i32.const 0)) (i32.const 1684234849)) 62 | (assert_return (invoke "good11" (i32.const 0)) (i32.const 1701077858)) 63 | (assert_return (invoke "good12" (i32.const 0)) (i32.const 1717920867)) 64 | (assert_return (invoke "good13" (i32.const 0)) (i32.const 122)) 65 | 66 | (assert_return (invoke "good1" (i32.const 65507)) (i32.const 0)) 67 | (assert_return (invoke "good2" (i32.const 65507)) (i32.const 0)) 68 | (assert_return (invoke "good3" (i32.const 65507)) (i32.const 0)) 69 | (assert_return (invoke "good4" (i32.const 65507)) (i32.const 0)) 70 | (assert_return (invoke "good5" (i32.const 65507)) (i32.const 0)) 71 | (assert_return (invoke "good6" (i32.const 65507)) (i32.const 0)) 72 | (assert_return (invoke "good7" (i32.const 65507)) (i32.const 0)) 73 | (assert_return (invoke "good8" (i32.const 65507)) (i32.const 0)) 74 | (assert_return (invoke "good9" (i32.const 65507)) (i32.const 0)) 75 | (assert_return (invoke "good10" (i32.const 65507)) (i32.const 0)) 76 | (assert_return (invoke "good11" (i32.const 65507)) (i32.const 0)) 77 | (assert_return (invoke "good12" (i32.const 65507)) (i32.const 0)) 78 | (assert_return (invoke "good13" (i32.const 65507)) (i32.const 0)) 79 | 80 | (assert_return (invoke "good1" (i32.const 65508)) (i32.const 0)) 81 | (assert_return (invoke "good2" (i32.const 65508)) (i32.const 0)) 82 | (assert_return (invoke "good3" (i32.const 65508)) (i32.const 0)) 83 | (assert_return (invoke "good4" (i32.const 65508)) (i32.const 0)) 84 | (assert_return (invoke "good5" (i32.const 65508)) (i32.const 0)) 85 | (assert_return (invoke "good6" (i32.const 65508)) (i32.const 0)) 86 | (assert_return (invoke "good7" (i32.const 65508)) (i32.const 0)) 87 | (assert_return (invoke "good8" (i32.const 65508)) (i32.const 0)) 88 | (assert_return (invoke "good9" (i32.const 65508)) (i32.const 0)) 89 | (assert_return (invoke "good10" (i32.const 65508)) (i32.const 0)) 90 | (assert_return (invoke "good11" (i32.const 65508)) (i32.const 0)) 91 | (assert_return (invoke "good12" (i32.const 65508)) (i32.const 0)) 92 | (assert_trap (invoke "good13" (i32.const 65508)) "out of bounds memory access") 93 | 94 | (assert_trap (invoke "bad" (i32.const 0)) "out of bounds memory access") 95 | (assert_trap (invoke "bad" (i32.const 1)) "out of bounds memory access") 96 | 97 | (assert_malformed 98 | (module quote 99 | "(memory 1)" 100 | "(func (drop (i32.load offset=4294967296 (i32.const 0))))" 101 | ) 102 | "i32 constant" 103 | ) 104 | -------------------------------------------------------------------------------- /interpreter/util/lib.ml: -------------------------------------------------------------------------------- 1 | module Fun = 2 | struct 3 | let rec repeat n f x = 4 | if n = 0 then () else (f x; repeat (n - 1) f x) 5 | end 6 | 7 | module Int = 8 | struct 9 | let log2 n = 10 | if n <= 0 then failwith "log2"; 11 | let rec loop acc n = if n = 1 then acc else loop (acc + 1) (n lsr 1) in 12 | loop 0 n 13 | 14 | let is_power_of_two n = 15 | if n < 0 then failwith "is_power_of_two"; 16 | n <> 0 && n land (n - 1) = 0 17 | end 18 | 19 | module String = 20 | struct 21 | let implode cs = 22 | let buf = Buffer.create 80 in 23 | List.iter (Buffer.add_char buf) cs; 24 | Buffer.contents buf 25 | 26 | let explode s = 27 | let cs = ref [] in 28 | for i = String.length s - 1 downto 0 do cs := s.[i] :: !cs done; 29 | !cs 30 | 31 | let split s c = 32 | let len = String.length s in 33 | let rec loop i = 34 | if i > len then [] else 35 | let j = try String.index_from s i c with Not_found -> len in 36 | String.sub s i (j - i) :: loop (j + 1) 37 | in loop 0 38 | 39 | let breakup s n = 40 | let rec loop i = 41 | let len = min n (String.length s - i) in 42 | if len = 0 then [] else String.sub s i len :: loop (i + len) 43 | in loop 0 44 | end 45 | 46 | module List = 47 | struct 48 | let rec make n x = 49 | if n = 0 then [] else x :: make (n - 1) x 50 | 51 | let rec table n f = table' 0 n f 52 | and table' i n f = 53 | if i = n then [] else f i :: table' (i + 1) n f 54 | 55 | let rec take n xs = 56 | match n, xs with 57 | | 0, _ -> [] 58 | | n, x::xs' when n > 0 -> x :: take (n - 1) xs' 59 | | _ -> failwith "take" 60 | 61 | let rec drop n xs = 62 | match n, xs with 63 | | 0, _ -> xs 64 | | n, _::xs' when n > 0 -> drop (n - 1) xs' 65 | | _ -> failwith "drop" 66 | 67 | let rec last = function 68 | | x::[] -> x 69 | | _::xs -> last xs 70 | | [] -> failwith "last" 71 | 72 | let rec split_last = function 73 | | x::[] -> [], x 74 | | x::xs -> let ys, y = split_last xs in x::ys, y 75 | | [] -> failwith "split_last" 76 | 77 | let rec index_where p xs = index_where' p xs 0 78 | and index_where' p xs i = 79 | match xs with 80 | | [] -> None 81 | | x::xs' when p x -> Some i 82 | | x::xs' -> index_where' p xs' (i+1) 83 | 84 | let index_of x = index_where ((=) x) 85 | 86 | let rec map_filter f = function 87 | | [] -> [] 88 | | x::xs -> 89 | match f x with 90 | | None -> map_filter f xs 91 | | Some y -> y :: map_filter f xs 92 | end 93 | 94 | module List32 = 95 | struct 96 | let rec length xs = length' xs 0l 97 | and length' xs n = 98 | match xs with 99 | | [] -> n 100 | | _::xs' when n < Int32.max_int -> length' xs' (Int32.add n 1l) 101 | | _ -> failwith "length" 102 | 103 | let rec nth xs n = 104 | match n, xs with 105 | | 0l, x::_ -> x 106 | | n, _::xs' when n > 0l -> nth xs' (Int32.sub n 1l) 107 | | _ -> failwith "nth" 108 | 109 | let rec take n xs = 110 | match n, xs with 111 | | 0l, _ -> [] 112 | | n, x::xs' when n > 0l -> x :: take (Int32.sub n 1l) xs' 113 | | _ -> failwith "take" 114 | 115 | let rec drop n xs = 116 | match n, xs with 117 | | 0l, _ -> xs 118 | | n, _::xs' when n > 0l -> drop (Int32.sub n 1l) xs' 119 | | _ -> failwith "drop" 120 | end 121 | 122 | module Array32 = 123 | struct 124 | let make n x = 125 | if n < 0l || Int64.of_int32 n > Int64.of_int max_int then 126 | raise (Invalid_argument "Array32.make"); 127 | Array.make (Int32.to_int n) x 128 | 129 | let length a = Int32.of_int (Array.length a) 130 | 131 | let index_of_int32 i = 132 | if i < 0l || Int64.of_int32 i > Int64.of_int max_int then -1 else 133 | Int32.to_int i 134 | 135 | let get a i = Array.get a (index_of_int32 i) 136 | let set a i x = Array.set a (index_of_int32 i) x 137 | let blit a1 i1 a2 i2 n = 138 | Array.blit a1 (index_of_int32 i1) a2 (index_of_int32 i2) (index_of_int32 n) 139 | end 140 | 141 | module Bigarray = 142 | struct 143 | open Bigarray 144 | 145 | module Array1_64 = 146 | struct 147 | let create kind layout n = 148 | if n < 0L || n > Int64.of_int max_int then 149 | raise (Invalid_argument "Bigarray.Array1_64.create"); 150 | Array1.create kind layout (Int64.to_int n) 151 | 152 | let dim a = Int64.of_int (Array1.dim a) 153 | 154 | let index_of_int64 i = 155 | if i < 0L || i > Int64.of_int max_int then -1 else 156 | Int64.to_int i 157 | 158 | let get a i = Array1.get a (index_of_int64 i) 159 | let set a i x = Array1.set a (index_of_int64 i) x 160 | let sub a i n = Array1.sub a (index_of_int64 i) (index_of_int64 n) 161 | end 162 | end 163 | 164 | module Option = 165 | struct 166 | let get o x = 167 | match o with 168 | | Some y -> y 169 | | None -> x 170 | 171 | let map f = function 172 | | Some x -> Some (f x) 173 | | None -> None 174 | 175 | let app f = function 176 | | Some x -> f x 177 | | None -> () 178 | end 179 | -------------------------------------------------------------------------------- /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 `_ that includes representatives of all major browser vendors. 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 authorative 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 sections ` 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/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 | * 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. 41 | 42 | 43 | 44 | .. _notation-slice: 45 | .. _notation-replace: 46 | .. _notation-record: 47 | .. _notation-concat: 48 | .. _notation-compose: 49 | 50 | Auxiliary Notation 51 | ~~~~~~~~~~~~~~~~~~ 52 | 53 | When dealing with syntactic constructs the following notation is also used: 54 | 55 | * :math:`\epsilon` denotes the empty sequence. 56 | 57 | * :math:`|s|` denotes the length of a sequence :math:`s`. 58 | 59 | * :math:`s[i]` denotes the :math:`i`-th element of a sequence :math:`s`, starting from :math:`0`. 60 | 61 | * :math:`s[i \slice n]` denotes the sub-sequence :math:`s[i]~\dots~s[i+n-1]` of a sequence :math:`s`. 62 | 63 | * :math:`s \with [i] = A` denotes the same sequence as :math:`s`, 64 | except that the :math:`i`-the element is replaced with :math:`A`. 65 | 66 | * :math:`s \with [i \slice n] = A^n` denotes the same sequence as :math:`s`, 67 | except that the sub-sequence :math:`s[i \slice n]` is replaced with :math:`A^n`. 68 | 69 | * :math:`\concat(s^\ast)` denotes the flat sequence formed by concatenating all sequences :math:`s_i` in :math:`s^\ast`. 70 | 71 | Moreover, the following conventions are employed: 72 | 73 | * 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^?`). 74 | 75 | * When given a sequence :math:`x^n`, 76 | 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` 77 | (similarly for :math:`x^\ast`, :math:`x^+`, :math:`x^?`). 78 | This implicitly expresses a form of mapping syntactic constructions over a sequence. 79 | 80 | 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: 81 | 82 | .. math:: 83 | \X{r} ~::=~ \{ \K{field}_1~A_1, \K{field}_2~A_2, \dots \} 84 | 85 | The following notation is adopted for manipulating such records: 86 | 87 | * :math:`r.\K{field}` denotes the contents of the :math:`\K{field}` component of :math:`r`. 88 | 89 | * :math:`r \with \K{field} = A` denotes the same record as :math:`r`, 90 | except that the contents of the :math:`\K{field}` component is replaced with :math:`A`. 91 | 92 | * :math:`r_1 \compose r_2` denotes the composition of two records with the same fields of sequences by appending each sequence point-wise: 93 | 94 | .. math:: 95 | \{ \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 \} 96 | 97 | * :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. 98 | 99 | The update notation for sequences and records generalizes recursively to nested components accessed by "paths" :math:`\X{pth} ::= ([\dots] \;| \;.\K{field})^+`: 100 | 101 | * :math:`s \with [i]\,\X{pth} = A` is short for :math:`s \with [i] = (s[i] \with \X{pth} = A)`. 102 | 103 | * :math:`r \with \K{field}\,\X{pth} = A` is short for :math:`r \with \K{field} = (r.\K{field} \with \X{pth} = A)`. 104 | 105 | where :math:`r \with~.\K{field} = A` is shortened to :math:`r \with \K{field} = A`. 106 | 107 | 108 | .. index:: ! vector 109 | pair: abstract syntax; vector 110 | .. _syntax-vec: 111 | 112 | Vectors 113 | ~~~~~~~ 114 | 115 | *Vectors* are bounded sequences of the form :math:`A^n` (or :math:`A^\ast`), 116 | where the :math:`A` can either be values or complex constructions. 117 | A vector can have at most :math:`2^{32}-1` elements. 118 | 119 | .. math:: 120 | \begin{array}{lllll} 121 | \production{vector} & \vec(A) &::=& 122 | A^n 123 | & (\iff n < 2^{32})\\ 124 | \end{array} 125 | -------------------------------------------------------------------------------- /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 :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 ignored otherwise. 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/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{C0})+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{C0})+2^{12}(b_2-\hex{80})+2^6(b_3-\hex{80})+(b_4-\hex{80})) \\ 141 | \end{array} \\ 142 | \end{array} 143 | -------------------------------------------------------------------------------- /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 | 15 | Except for a few exceptions, the binary grammar closely mirrors the grammar of the abstract syntax. 16 | 17 | .. note:: 18 | Some phrases of abstract syntax have multiple possible encodings in the binary format. 19 | For example, numbers may be encoded as if they had optional leading zeros. 20 | Implementations of decoders must support all possible alternatives; 21 | implementations of encoders can pick any allowed encoding. 22 | 23 | The recommended extension for files containing WebAssembly modules in binary format is ":math:`\T{.wasm}`" 24 | and the recommended |MediaType|_ is ":math:`\T{application/wasm}`". 25 | 26 | .. [#compression] 27 | Additional encoding layers -- for example, introducing compression -- may be defined on top of the basic representation defined here. 28 | However, such layers are outside the scope of the current specification. 29 | 30 | 31 | .. index:: grammar notation, notation, byte 32 | single: binary format; grammar 33 | pair: binary format; notation 34 | .. _binary-grammar: 35 | 36 | Grammar 37 | ~~~~~~~ 38 | 39 | The following conventions are adopted in defining grammar rules for the binary format. 40 | They mirror the conventions used for :ref:`abstract syntax `. 41 | In order to distinguish symbols of the binary syntax from symbols of the abstract syntax, :math:`\mathtt{typewriter}` font is adopted for the former. 42 | 43 | * Terminal symbols are :ref:`bytes ` expressed in hexadecimal notation: :math:`\hex{0F}`. 44 | 45 | * Nonterminal symbols are written in typewriter font: :math:`\B{valtype}, \B{instr}`. 46 | 47 | * :math:`B^n` is a sequence of :math:`n\geq 0` iterations of :math:`B`. 48 | 49 | * :math:`B^\ast` is a possibly empty sequence of iterations of :math:`B`. 50 | (This is a shorthand for :math:`B^n` used where :math:`n` is not relevant.) 51 | 52 | * :math:`B^?` is an optional occurrence of :math:`B`. 53 | (This is a shorthand for :math:`B^n` where :math:`n \leq 1`.) 54 | 55 | * :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`. 56 | 57 | * 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`. 58 | 59 | * 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. 60 | 61 | .. note:: 62 | For example, the :ref:`binary grammar ` for :ref:`value types ` is given as follows: 63 | 64 | .. math:: 65 | \begin{array}{llcll@{\qquad\qquad}l} 66 | \production{value types} & \Bvaltype &::=& 67 | \hex{7F} &\Rightarrow& \I32 \\ &&|& 68 | \hex{7E} &\Rightarrow& \I64 \\ &&|& 69 | \hex{7D} &\Rightarrow& \F32 \\ &&|& 70 | \hex{7C} &\Rightarrow& \F64 \\ 71 | \end{array} 72 | 73 | Consequently, the byte :math:`\hex{7F}` encodes the type |I32|, 74 | :math:`\hex{7E}` encodes the type |I64|, and so forth. 75 | No other byte value is allowed as the encoding of a value type. 76 | 77 | The :ref:`binary grammar ` for :ref:`limits ` is defined as follows: 78 | 79 | .. math:: 80 | \begin{array}{llclll} 81 | \production{limits} & \Blimits &::=& 82 | \hex{00}~~n{:}\Bu32 &\Rightarrow& \{ \LMIN~n, \LMAX~\epsilon \} \\ &&|& 83 | \hex{01}~~n{:}\Bu32~~m{:}\Bu32 &\Rightarrow& \{ \LMIN~n, \LMAX~m \} \\ 84 | \end{array} 85 | 86 | That is, a limits pair is encoded as either the byte :math:`\hex{00}` followed by the encoding of a |U32| value, 87 | or the byte :math:`\hex{01}` followed by two such encodings. 88 | 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. 89 | The attribute of the complete production then is the abstract syntax for the limit, expressed in terms of the former values. 90 | 91 | 92 | .. _binary-notation: 93 | 94 | Auxiliary Notation 95 | ~~~~~~~~~~~~~~~~~~ 96 | 97 | When dealing with binary encodings the following notation is also used: 98 | 99 | * :math:`\epsilon` denotes the empty byte sequence. 100 | 101 | * :math:`||B||` is the length of the byte sequence generated from the production :math:`B` in a derivation. 102 | 103 | 104 | .. index:: vector 105 | pair: binary format; vector 106 | .. _binary-vec: 107 | 108 | Vectors 109 | ~~~~~~~ 110 | 111 | :ref:`Vectors ` are encoded with their |Bu32| length followed by the encoding of their element sequence. 112 | 113 | .. math:: 114 | \begin{array}{llclll@{\qquad\qquad}l} 115 | \production{vector} & \Bvec(\B{B}) &::=& 116 | n{:}\Bu32~~(x{:}\B{B})^n &\Rightarrow& x^n \\ 117 | \end{array} 118 | -------------------------------------------------------------------------------- /test/core/globals.wast: -------------------------------------------------------------------------------- 1 | ;; Test globals 2 | 3 | (module 4 | (global $a i32 (i32.const -2)) 5 | (global (;1;) f32 (f32.const -3)) 6 | (global (;2;) f64 (f64.const -4)) 7 | (global $b i64 (i64.const -5)) 8 | 9 | (global $x (mut i32) (i32.const -12)) 10 | (global (;5;) (mut f32) (f32.const -13)) 11 | (global (;6;) (mut f64) (f64.const -14)) 12 | (global $y (mut i64) (i64.const -15)) 13 | 14 | (func (export "get-a") (result i32) (get_global $a)) 15 | (func (export "get-b") (result i64) (get_global $b)) 16 | (func (export "get-x") (result i32) (get_global $x)) 17 | (func (export "get-y") (result i64) (get_global $y)) 18 | (func (export "set-x") (param i32) (set_global $x (get_local 0))) 19 | (func (export "set-y") (param i64) (set_global $y (get_local 0))) 20 | 21 | (func (export "get-1") (result f32) (get_global 1)) 22 | (func (export "get-2") (result f64) (get_global 2)) 23 | (func (export "get-5") (result f32) (get_global 5)) 24 | (func (export "get-6") (result f64) (get_global 6)) 25 | (func (export "set-5") (param f32) (set_global 5 (get_local 0))) 26 | (func (export "set-6") (param f64) (set_global 6 (get_local 0))) 27 | ) 28 | 29 | (assert_return (invoke "get-a") (i32.const -2)) 30 | (assert_return (invoke "get-b") (i64.const -5)) 31 | (assert_return (invoke "get-x") (i32.const -12)) 32 | (assert_return (invoke "get-y") (i64.const -15)) 33 | 34 | (assert_return (invoke "get-1") (f32.const -3)) 35 | (assert_return (invoke "get-2") (f64.const -4)) 36 | (assert_return (invoke "get-5") (f32.const -13)) 37 | (assert_return (invoke "get-6") (f64.const -14)) 38 | 39 | (assert_return (invoke "set-x" (i32.const 6))) 40 | (assert_return (invoke "set-y" (i64.const 7))) 41 | (assert_return (invoke "set-5" (f32.const 8))) 42 | (assert_return (invoke "set-6" (f64.const 9))) 43 | 44 | (assert_return (invoke "get-x") (i32.const 6)) 45 | (assert_return (invoke "get-y") (i64.const 7)) 46 | (assert_return (invoke "get-5") (f32.const 8)) 47 | (assert_return (invoke "get-6") (f64.const 9)) 48 | 49 | (assert_invalid 50 | (module (global f32 (f32.const 0)) (func (set_global 0 (i32.const 1)))) 51 | "global is immutable" 52 | ) 53 | 54 | (assert_invalid 55 | (module (import "m" "a" (global (mut i32)))) 56 | "mutable globals cannot be imported" 57 | ) 58 | 59 | (assert_invalid 60 | (module (global (import "m" "a") (mut i32))) 61 | "mutable globals cannot be imported" 62 | ) 63 | 64 | (assert_invalid 65 | (module (global (mut f32) (f32.const 0)) (export "a" (global 0))) 66 | "mutable globals cannot be exported" 67 | ) 68 | 69 | (assert_invalid 70 | (module (global (export "a") (mut f32) (f32.const 0))) 71 | "mutable globals cannot be exported" 72 | ) 73 | 74 | (assert_invalid 75 | (module (global f32 (f32.neg (f32.const 0)))) 76 | "constant expression required" 77 | ) 78 | 79 | (assert_invalid 80 | (module (global f32 (get_local 0))) 81 | "constant expression required" 82 | ) 83 | 84 | (assert_invalid 85 | (module (global f32 (f32.neg (f32.const 1)))) 86 | "constant expression required" 87 | ) 88 | 89 | (assert_invalid 90 | (module (global i32 (i32.const 0) (nop))) 91 | "constant expression required" 92 | ) 93 | 94 | (assert_invalid 95 | (module (global i32 (nop))) 96 | "constant expression required" 97 | ) 98 | 99 | (assert_invalid 100 | (module (global i32 (f32.const 0))) 101 | "type mismatch" 102 | ) 103 | 104 | (assert_invalid 105 | (module (global i32 (i32.const 0) (i32.const 0))) 106 | "type mismatch" 107 | ) 108 | 109 | (assert_invalid 110 | (module (global i32 (;empty instruction sequence;))) 111 | "type mismatch" 112 | ) 113 | 114 | (assert_invalid 115 | (module (global i32 (get_global 0))) 116 | "unknown global" 117 | ) 118 | 119 | (assert_invalid 120 | (module (global i32 (get_global 1)) (global i32 (i32.const 0))) 121 | "unknown global" 122 | ) 123 | 124 | (module 125 | (import "spectest" "global" (global i32)) 126 | ) 127 | (assert_malformed 128 | (module binary 129 | "\00asm" "\01\00\00\00" 130 | "\02\94\80\80\80\00" ;; import section 131 | "\01" ;; length 1 132 | "\08\73\70\65\63\74\65\73\74" ;; "spectest" 133 | "\06\67\6c\6f\62\61\6c" ;; "global" 134 | "\03" ;; GlobalImport 135 | "\7f" ;; i32 136 | "\02" ;; invalid mutability 137 | ) 138 | "invalid mutability" 139 | ) 140 | (assert_malformed 141 | (module binary 142 | "\00asm" "\01\00\00\00" 143 | "\02\94\80\80\80\00" ;; import section 144 | "\01" ;; length 1 145 | "\08\73\70\65\63\74\65\73\74" ;; "spectest" 146 | "\06\67\6c\6f\62\61\6c" ;; "global" 147 | "\03" ;; GlobalImport 148 | "\7f" ;; i32 149 | "\ff" ;; invalid mutability 150 | ) 151 | "invalid mutability" 152 | ) 153 | 154 | (module 155 | (global i32 (i32.const 0)) 156 | ) 157 | (assert_malformed 158 | (module binary 159 | "\00asm" "\01\00\00\00" 160 | "\06\86\80\80\80\00" ;; global section 161 | "\01" ;; length 1 162 | "\7f" ;; i32 163 | "\02" ;; invalid mutability 164 | "\41\00" ;; i32.const 0 165 | "\0b" ;; end 166 | ) 167 | "invalid mutability" 168 | ) 169 | (assert_malformed 170 | (module binary 171 | "\00asm" "\01\00\00\00" 172 | "\06\86\80\80\80\00" ;; global section 173 | "\01" ;; length 1 174 | "\7f" ;; i32 175 | "\ff" ;; invalid mutability 176 | "\41\00" ;; i32.const 0 177 | "\0b" ;; end 178 | ) 179 | "invalid mutability" 180 | ) 181 | -------------------------------------------------------------------------------- /test/core/elem.wast: -------------------------------------------------------------------------------- 1 | ;; Test the element section 2 | 3 | (module 4 | (table 10 anyfunc) 5 | (elem (i32.const 0) $f) 6 | (func $f) 7 | ) 8 | 9 | (module 10 | (table 10 anyfunc) 11 | (elem (i32.const 9) $f) 12 | (func $f) 13 | ) 14 | 15 | (module 16 | (type $out-i32 (func (result i32))) 17 | (table 10 anyfunc) 18 | (elem (i32.const 7) $const-i32-a) 19 | (elem (i32.const 9) $const-i32-b) 20 | (func $const-i32-a (type $out-i32) (i32.const 65)) 21 | (func $const-i32-b (type $out-i32) (i32.const 66)) 22 | (func (export "call-7") (type $out-i32) 23 | (call_indirect $out-i32 (i32.const 7)) 24 | ) 25 | (func (export "call-9") (type $out-i32) 26 | (call_indirect $out-i32 (i32.const 9)) 27 | ) 28 | ) 29 | 30 | (assert_return (invoke "call-7") (i32.const 65)) 31 | (assert_return (invoke "call-9") (i32.const 66)) 32 | 33 | ;; Two elements target the same slot 34 | 35 | (module 36 | (type $out-i32 (func (result i32))) 37 | (table 10 anyfunc) 38 | (elem (i32.const 9) $const-i32-a) 39 | (elem (i32.const 9) $const-i32-b) 40 | (func $const-i32-a (type $out-i32) (i32.const 65)) 41 | (func $const-i32-b (type $out-i32) (i32.const 66)) 42 | (func (export "call-overwritten-element") (type $out-i32) 43 | (call_indirect $out-i32 (i32.const 9)) 44 | ) 45 | ) 46 | 47 | (assert_return (invoke "call-overwritten-element") (i32.const 66)) 48 | 49 | ;; Invalid bounds for elements 50 | 51 | (assert_unlinkable 52 | (module 53 | (table 10 anyfunc) 54 | (elem (i32.const 10) $f) 55 | (func $f) 56 | ) 57 | "elements segment does not fit" 58 | ) 59 | 60 | (assert_unlinkable 61 | (module 62 | (table 10 20 anyfunc) 63 | (elem (i32.const 10) $f) 64 | (func $f) 65 | ) 66 | "elements segment does not fit" 67 | ) 68 | 69 | (assert_unlinkable 70 | (module 71 | (table 10 anyfunc) 72 | (elem (i32.const -1) $f) 73 | (func $f) 74 | ) 75 | "elements segment does not fit" 76 | ) 77 | 78 | (assert_unlinkable 79 | (module 80 | (table 10 anyfunc) 81 | (elem (i32.const -10) $f) 82 | (func $f) 83 | ) 84 | "elements segment does not fit" 85 | ) 86 | 87 | ;; Tests with an imported table 88 | 89 | (module 90 | (import "spectest" "table" (table 10 anyfunc)) 91 | (elem (i32.const 0) $f) 92 | (func $f) 93 | ) 94 | 95 | (module 96 | (import "spectest" "table" (table 10 anyfunc)) 97 | (elem (i32.const 9) $f) 98 | (func $f) 99 | ) 100 | 101 | ;; Two elements target the same slot 102 | 103 | (module 104 | (type $out-i32 (func (result i32))) 105 | (import "spectest" "table" (table 10 anyfunc)) 106 | (elem (i32.const 9) $const-i32-a) 107 | (elem (i32.const 9) $const-i32-b) 108 | (func $const-i32-a (type $out-i32) (i32.const 65)) 109 | (func $const-i32-b (type $out-i32) (i32.const 66)) 110 | (func (export "call-overwritten-element") (type $out-i32) 111 | (call_indirect $out-i32 (i32.const 9)) 112 | ) 113 | ) 114 | 115 | (assert_return (invoke "call-overwritten-element") (i32.const 66)) 116 | 117 | ;; Invalid bounds for elements 118 | 119 | (assert_unlinkable 120 | (module 121 | (import "spectest" "table" (table 10 anyfunc)) 122 | (elem (i32.const 10) $f) 123 | (func $f) 124 | ) 125 | "elements segment does not fit" 126 | ) 127 | 128 | (assert_unlinkable 129 | (module 130 | (import "spectest" "table" (table 10 20 anyfunc)) 131 | (elem (i32.const 10) $f) 132 | (func $f) 133 | ) 134 | "elements segment does not fit" 135 | ) 136 | 137 | (assert_unlinkable 138 | (module 139 | (import "spectest" "table" (table 10 anyfunc)) 140 | (elem (i32.const -1) $f) 141 | (func $f) 142 | ) 143 | "elements segment does not fit" 144 | ) 145 | 146 | (assert_unlinkable 147 | (module 148 | (import "spectest" "table" (table 10 anyfunc)) 149 | (elem (i32.const -10) $f) 150 | (func $f) 151 | ) 152 | "elements segment does not fit" 153 | ) 154 | 155 | ;; Element without table 156 | 157 | (assert_invalid 158 | (module 159 | (elem (i32.const 0) $f) 160 | (func $f) 161 | ) 162 | "unknown table 0" 163 | ) 164 | 165 | ;; Test element sections across multiple modules change the same table 166 | 167 | (module $module1 168 | (type $out-i32 (func (result i32))) 169 | (table (export "shared-table") 10 anyfunc) 170 | (elem (i32.const 8) $const-i32-a) 171 | (elem (i32.const 9) $const-i32-b) 172 | (func $const-i32-a (type $out-i32) (i32.const 65)) 173 | (func $const-i32-b (type $out-i32) (i32.const 66)) 174 | (func (export "call-7") (type $out-i32) 175 | (call_indirect $out-i32 (i32.const 7)) 176 | ) 177 | (func (export "call-8") (type $out-i32) 178 | (call_indirect $out-i32 (i32.const 8)) 179 | ) 180 | (func (export "call-9") (type $out-i32) 181 | (call_indirect $out-i32 (i32.const 9)) 182 | ) 183 | ) 184 | 185 | (register "module1" $module1) 186 | 187 | (assert_trap (invoke $module1 "call-7") "uninitialized element 7") 188 | (assert_return (invoke $module1 "call-8") (i32.const 65)) 189 | (assert_return (invoke $module1 "call-9") (i32.const 66)) 190 | 191 | (module $module2 192 | (type $out-i32 (func (result i32))) 193 | (import "module1" "shared-table" (table 10 anyfunc)) 194 | (elem (i32.const 7) $const-i32-c) 195 | (elem (i32.const 8) $const-i32-d) 196 | (func $const-i32-c (type $out-i32) (i32.const 67)) 197 | (func $const-i32-d (type $out-i32) (i32.const 68)) 198 | ) 199 | 200 | (assert_return (invoke $module1 "call-7") (i32.const 67)) 201 | (assert_return (invoke $module1 "call-8") (i32.const 68)) 202 | (assert_return (invoke $module1 "call-9") (i32.const 66)) 203 | 204 | (module $module3 205 | (type $out-i32 (func (result i32))) 206 | (import "module1" "shared-table" (table 10 anyfunc)) 207 | (elem (i32.const 8) $const-i32-e) 208 | (elem (i32.const 9) $const-i32-f) 209 | (func $const-i32-e (type $out-i32) (i32.const 69)) 210 | (func $const-i32-f (type $out-i32) (i32.const 70)) 211 | ) 212 | 213 | (assert_return (invoke $module1 "call-7") (i32.const 67)) 214 | (assert_return (invoke $module1 "call-8") (i32.const 69)) 215 | (assert_return (invoke $module1 "call-9") (i32.const 70)) 216 | -------------------------------------------------------------------------------- /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 | --------------------------------------------------------------------------------