From 803a1c2788486f47803f97cc0c6b89f2cdda341e Mon Sep 17 00:00:00 2001 From: Luke Wagner Date: Tue, 3 May 2016 18:41:03 -0500 Subject: [PATCH 01/22] Add Memory, Table and multi-import/export of these --- AstSemantics.md | 130 ++++++++++-------- DynamicLinking.md | 145 +++++++++++++------- FeatureTest.md | 2 +- FutureFeatures.md | 26 ++-- JS.md | 333 +++++++++++++++++++++++++++++++++++++++++----- Modules.md | 240 ++++++++++++++++++++------------- Nondeterminism.md | 2 +- PostMVP.md | 4 - Web.md | 33 +---- 9 files changed, 646 insertions(+), 269 deletions(-) diff --git a/AstSemantics.md b/AstSemantics.md index 94b2f2cc..0311c82a 100644 --- a/AstSemantics.md +++ b/AstSemantics.md @@ -80,29 +80,30 @@ operators. ## Linear Memory -The main storage of a WebAssembly instance, called the *linear memory*, is a -contiguous, byte-addressable range of memory spanning from offset `0` and -extending up to a varying *memory size*. +A WebAssembly [instance](Modules.md) contains zero or more *linear memories*. +A linear memory is is a contiguous, byte-addressable range of memory spanning +from offset `0` and extending up to a varying *memory size*. This size always is a multiple of the WebAssembly page size, -which is 64KiB on all engines (though large page support may be added in the -[future](FutureFeatures.md#large-page-support)). -The initial state of linear memory is specified by the -[module](Modules.md#linear-memory-section), and it can be dynamically grown by +which is fixed to 64KiB (though large page support may be added in an opt-in +manner in the [future](FutureFeatures.md#large-page-support)). +The initial state of a linear memory is specified by the +[module](Modules.md#linear-memory-section), and can be dynamically grown by the [`grow_memory`](AstSemantics.md#resizing) operator. -The linear memory can be considered -to be an untyped array of bytes, and it is unspecified how embedders map this -array into their process' own [virtual memory][]. The linear memory is -sandboxed; it does not alias the execution engine's internal data structures, -the execution stack, local variables, or other process memory. +A module can define or import multiple linear memories and each memory access +operator statically specifies which linear memory to operate on with a +[linear memory index](Modules.md#index-spaces) immediate. + +A linear memory can be considered to be an untyped array of bytes, and it is +unspecified how embedders map this array into their process' own [virtual +memory][]. Linear memory is sandboxed; it does not alias other linear memories, +the execution engine's internal data structures, the execution stack, local +variables, or other process memory. [virtual memory]: https://en.wikipedia.org/wiki/Virtual_memory -In the MVP, linear memory is not shared between threads of execution. Separate -instances can execute in separate threads but have their own linear memory and can -only communicate through messaging, e.g. in browsers using `postMessage`. It -will be possible to share linear memory between threads of execution when -[threads](PostMVP.md#threads) are added. +In the MVP, linear memory cannot be shared between threads of execution. +The addition of [threads](PostMVP.md#threads) will allow this. ### Linear Memory Accesses @@ -111,6 +112,9 @@ Integer loads can specify a *storage size* which is smaller than the result type well as a signedness which determines whether the bytes are sign- or zero- extended into the result type. +Each `load` and `store` specifies the [linear memory index](Modules.md#index-spaces) +to operate on with an unsigned integer immediate. + * `i32.load8_s`: load 1 byte and sign-extend i8 to i32 * `i32.load8_u`: load 1 byte and zero-extend i8 to i32 * `i32.load16_s`: load 2 bytes and sign-extend i16 to i32 @@ -203,21 +207,25 @@ Out of bounds accesses trap. ### Resizing In the MVP, linear memory can be resized by a `grow_memory` operator. The -operand to this operator is in units of the WebAssembly page size, +`grow_memory` operator specifies which memory to resize with a +[linear memory index](Modules.md#index-spaces) immediate. The dynamic +operand to `grow_memory` is in units of the WebAssembly page size, which is defined to be 64KiB (though large page support may be added in the [future](FutureFeatures.md#large-page-support)). * `grow_memory` : grow linear memory by a given unsigned delta of pages. Return the previous memory size in units of pages or -1 on failure. -When a maximum memory size is declared in the [memory section](Module.md#linear-memory-section), +When a maximum memory size is declared in the [memory section](Modules.md#linear-memory-sections), `grow_memory` must fail if it would grow past the maximum. However, `grow_memory` may still fail before the maximum if it was not possible to reserve the space up front or if enabling the reserved memory fails. When there is no maximum memory size declared, `grow_memory` is expected to perform a system allocation which may fail. -The current size of the linear memory can be queried by the following operator: +The current size of a given linear memory (specified by a +[linear memory index](Modules.md#index-spaces)) can be queried by the following +operator: * `current_memory` : return the current memory size in units of pages. @@ -232,6 +240,40 @@ operator may be added. However, due to normal fragmentation, applications are instead expected release unused physical pages from the working set using the [`discard`](FutureFeatures.md#finer-grained-control-over-memory) future feature. +## Tables + +A *table* is similar to a linear memory whose elements, instead of being bytes, +are opaque values of a particular *table element type*. This allows the table to +contain values like GC references, raw OS handles, or native pointers that are +accessed by WebAssembly code indirectly through an integer index. This feature +bridges the gap between low-level, untrusted linear memory and high-level +opaque handles/references at the cost of a bounds-checked table indirection. + +The table's element type dynamically constrains the type of elements stored +in the table and allows engines to avoid some type checks on table use. When +tables are mutated, any stored value is necessarily coerced (possibly trapping) +to the element type. + +In the MVP, the set of operations and types for tables is limited: +* tables may only be accessed via [`call_table`](#calls); +* the only allowed table element types are "function" and + "function with signature `X`"; +* tables may not be directly mutated or resized from WebAssembly code; + initially this must be done through the host environment (e.g., the + the `WebAssembly` [JavaScript API](JS.md#webassemblytable-objects). + +These restrictions should be relaxed in the +[future](FutureFeatures.md#more-table-operators-and-types). In the MVP, +the primary purpose of tables is to implement indirect function calls +in C/C++, using an integer index as the pointer-to-function and the table +to hold the array of indirectly-callable functions. The compiler may either +choose to group functions by signature (into N tables-with-signatures) +or to put all functions into the same table (with no signature). The former +strategy allows the engine to eliminate dynamic signature-mismatch checks while +the latter strategy gives each function a unique index (which may be necessary +for C/C++ compatibility) and admits simpler and potentially more space-efficient +[dynamic linking](DynamicLinking.md). + ## Local variables Each function has a fixed, pre-declared number of local variables which occupy a single @@ -314,43 +356,27 @@ explicit accesses to linear memory. In the MVP, the length of the return types sequence may only be 0 or 1. This restriction may be lifted in the future. -Direct calls to a function specify the callee by index into a *main function table*. +Direct calls to a function specify the callee by index into the +[function index space](Modules.md#index-spaces). * `call`: call function directly A direct call to a function with a mismatched signature is a module verification error. -Like direct calls, calls to [imports](Modules.md#imports-and-exports) specify -the callee by index into an *imported function table* defined by the sequence of import -declarations in the module import section. A direct call to an imported function with a -mismatched signature is a module verification error. - - * `call_import` : call imported function directly - -Indirect calls allow calling target functions that are unknown at compile time. -The target function is an expression of value type `i32` and is always the first -input into the indirect call. - -A `call_indirect` specifies the *expected* signature of the target function with -an index into a *signature table* defined by the module. An indirect call to a -function with a mismatched signature causes a trap. - - * `call_indirect`: call function indirectly - -Functions from the main function table are made addressable by defining an -*indirect function table* that consists of a sequence of indices into the -module's main function table. A function from the main table may appear more -than once in the indirect function table. Functions not appearing in the -indirect function table cannot be called indirectly. - -In the MVP, indices into the indirect function table are local to a single -module, so wasm modules may use `i32` constants to refer to entries in their own -indirect function table. The [dynamic linking](DynamicLinking.md) feature is -necessary for two modules to pass function pointers back and forth. This will -mean concatenating indirect function tables and adding an operator `address_of` -that computes the absolute index into the concatenated table from an index in a -module's local indirect table. JITing may also mean appending more functions to -the end of the indirect function table. +Indirect calls to a function indicate the callee with an `i32` index into +the [table](#tables) specified by a [table index](BinaryEncoding.md#index-spaces) +immediate. The *expected* signature of the target function (specified by its +index in the [types section](BinaryEncoding.md#type-section)) is given as a +second immediate. + + * `call_table`: call function element in a table + +The specified [table](#tables) must have either a "function" or "function with +signature `X`" element type (in the MVP, these are the only two +possibilities, but in the future, there may be other kinds of tables). +If the table element type has a signature, it must match the signature of the +call. Otherwise, the signature is checked at runtime and a signature mismatch +causes a trap. Multiple return value calls will be possible, though possibly not in the MVP. The details of multiple-return-value calls needs clarification. Calling a diff --git a/DynamicLinking.md b/DynamicLinking.md index 5ef24126..98300951 100644 --- a/DynamicLinking.md +++ b/DynamicLinking.md @@ -1,49 +1,102 @@ # Dynamic linking -Dynamic loading of code is in [the MVP](MVP.md) in the form of -[modules](Modules.md), but all loaded modules have their own separate -[linear memory](AstSemantics.md#linear-memory) by default and cannot share -[function pointers](AstSemantics.md#calls). Limited collaboration between -modules is possible in the MVP by having two modules share the same linear -memory and invoke each other through the host environment. - -True dynamic linking will allow developers to share memory, function pointers, -and future non-memory state such as global variables and thread-local variables -between WebAssembly dynamic libraries. - -WebAssembly will support both load-time and run-time (`dlopen`) dynamic linking -of libraries. - -One important requirement of dynamic linking is to allow the linked module -to have its own position-independent global data segment. This could be achieved -by specifying a new kind of link-time-initialized immutable global variable -which would be initialized with the address (in linear memory) of the modules' -global data segment. These immutable globals could also be used to provide -a linked module with the offsets of its function pointers in the instance's -function pointer tables. An important aspect of immutable globals is that they -could either be patched directly as constant values or implemented through a -[Global Offset Table](https://en.wikipedia.org/wiki/Position-independent_code) -in position-independent code. - -Dynamic linking is especially useful when combined with a Content Distribution -Network (CDN) such as [hosted libraries][] because the library is only ever -downloaded and compiled once per user device. It can also allow for smaller -differential updates, which could be implemented in collaboration with -[service workers][]. - -We would like to standardize a single [ABI][] per source language, allowing for -WebAssembly libraries to interface with each other regardless of compiler. While -it is highly recommended for compilers targeting WebAssembly to adhere to the -specified ABI for interoperability, WebAssembly runtimes will be ABI agnostic, -so it will be possible to use a non-standard ABI for specialized purposes. - -Although dynamic linking is not part of the MVP, it has significant implications -on many aspects of the design that do impact the MVP, such as the way linear -memory is managed, how module imports and exports are specified, and how globals -and function pointers work. Therefore we want to have some viable ideas to -ensure we don't standardize a design that unnecessarily complicates the design -or implementation of dynamic linking. - - [hosted libraries]: https://developers.google.com/speed/libraries/ - [service workers]: https://www.w3.org/TR/service-workers/ +WebAssembly supports load-time and run-time (`dlopen`) dynamic linking in the +MVP. This is achieved by having multiple [instantiated modules](Modules.md) +share functions, [linear memory](AstSemantics.md#linear-memory) and +[tables](AstSemantics.md#tables) using module [imports](Modules.md#imports) +and [exports](Modules.md#exports). In particular, since all (non-local) state +that a module can access can be imported and exported and thus shared between +separate modules' instances, toolchains have the building blocks to implement +dynamic loaders. + +Since the manner in which modules are loaded and instantiated is defined by the +host environment (e.g., the [JavaScript API](JS.md)), dynamic linking requires +use of host-specific functionality to link two modules. At a minimum, the host +environment must provide a way to dynamically instantiate modules while +connecting exports to imports. + +The simplest load-time dynamic linking scheme between modules A and B can be +achieved by having module A export functions, tables and memories that are +imported by B. A C++ toolchain can expose this functionality by using the +same function attributes currently used to export/import symbols from +native DSOs/DLLs: +``` +#ifdef _WIN32 +# define EXPORT __declspec(dllexport) +# define IMPORT __declspec(dllimport) +#else +# define EXPORT __attribute__ ((visibility ("default"))) +# define IMPORT __attribute__ ((visibility ("default"))) +#endif + +typedef void (*PF)(); + +IMPORT PF imp(); +EXPORT void exp() { imp()(); } +``` +This code would generate a WebAssembly module with imports for: +* the function `imp` +* the table(s) used for indirectly-callable functions +* the linear memory used to implement the heap + +and exports for: +* the function `exp` +* the imported table(s) and linear memory (so that other modules may + import this one) + +The host-specific loader can then simply connect the imports +and exports at instantantiation-time. + +One extra detail is what +to use as the [module name](Modules.md#imports) for imports (since +WebAssembly has a two-level namespace). One option is to have a single default +module name for all C/C++ imports/exports (which then allows the toolchain to +put implementation-internal names in a separate module, avoiding the need for +`__`-prefix conventions). + +To implement run-time dynamic linking (e.g., `dlopen` and `dlsym`): +* `dlopen` would compile and instantiate a new module, storing the compiled + instance in a host-environment table, returning the index to the caller. +* `dlsym` would be given this index, pull the instance out of the table, + search the instances's exports, append the found function to the function + table (using host-defined functionality in the MVP, but directly from + WebAssembly code in the + [future](FutureFeatures.md#more-table-operators-and-types)) and return the + table index of the appended element to the caller. + +Note that the representation of a C function-pointer in WebAssembly is an index +into a function table, so the above scheme lines up perfectly with the +function-pointer return value of `dlsym`. + +More complicated dynamic linking functionality (e.g., interposition, weak +symbols, etc) can be simulated efficiently by assigning a function table +index to each weak/mutable symbol, calling the symbol via `call_table` on that +index, and mutating the underlying element as needed. + +After the MVP, we would like to standardize a single [ABI][] per source +language, allowing for WebAssembly libraries to interface with each other +regardless of compiler. Specifying an ABI requires that all ABI-related +future features (like SIMD, multiple return values and exception handling) +have been implemented. While it is highly recommended for compilers targeting +WebAssembly to adhere to the specified ABI for interoperability, WebAssembly +runtimes will be ABI agnostic, so it will be possible to use a non-standard ABI +for specialized purposes. + [ABI]: https://en.wikipedia.org/wiki/Application_binary_interface + +TODO: + +The [data](Modules.md#data-section) and [elements](Modules.md#elements-section) +segments currently have fixed integer offsets which is fundamentally +incompatible with general dynamic loading. Without extension, dynamic linking +implementations would either need to statically allocate global data and +elements (which isn't possible in the general case) or use an entirely different +(and less efficient) scheme that doesn't use these sections. + +A better fix (before or after the MVP) is to allow importing/exporting constant +primitive values that can be used as the offsets of data/element segments +and generally read in WebAssembly code through some new `get_const` operator. +This would essentially be equivalent to the +[Global Offset Table](https://en.wikipedia.org/wiki/Position-independent_code) +used in native implementations of position-independent code although an +engine could use direct code patching of the values as well. diff --git a/FeatureTest.md b/FeatureTest.md index 705be528..fea93281 100644 --- a/FeatureTest.md +++ b/FeatureTest.md @@ -3,7 +3,7 @@ See [rationale](Rationale.md#feature-testing---motivating-scenarios) for motivat # Feature Test [PostMVP](PostMVP.md), applications will be able to query which features are -supported via [`has_feature` or a similar API](PostMVP#Feature-Testing). This +supported via [`has_feature` or a similar API](PostMVP.md#feature-testing). This accounts for the pragmatic reality that features are shipped in different orders at different times by different engines. diff --git a/FutureFeatures.md b/FutureFeatures.md index 335bd9ff..99dc38c4 100644 --- a/FutureFeatures.md +++ b/FutureFeatures.md @@ -393,16 +393,6 @@ reduce fragmentation issues. Languages like Fortran which limit aliasing would b one use case. C/C++ compilers could also determine that some global variables never have their address taken. -## Importing linear memory - -In the MVP, functions and [linear memory](Modules.md#linear-memory-section) can -be exported, but only functions can be imported. This feature would additionally -allow importing linear memory. One use case is sharing linear memories between -separate WebAssembly [instances](Modules.md). Another use case is allowing, on -the Web platform, importing a JS `ArrayBuffer` as a linear memory. This would -allow highly efficient, specialized code to be generated for accessing the -`ArrayBuffer`. - ## Streaming Compilation The WebAssembly binary format is designed to allow streaming decoding, @@ -425,3 +415,19 @@ of WebAssembly in browsers: would enable Web apps to perform their own (["layer 1"](BinaryEncoding.md)) custom compression (on top of the spec-defined binary format, under generic HTTP `Content-Encoding` compression). + +## More Table Operators and Types + +In the MVP, [tables](AstSemantics.md#tables) can only store functions +and can only be called. The host-environment can do much more (see, e.g., +the [JavaScript `WebAssembly.Table` API](JS.md#webassemblytable-objects)), +but it would be useful to be able to do everything from within WebAssembly +(so, e.g., it was possible to have a dynamic loader written in WebAssembly). As +a prerequisite, WebAssembly would need first-class support for +[GC references](GC.md) in expressions and locals. Given that, the following could be +added: +* `get_table`/`set_table`: get or set the table element at a given dynamic + index; the got/set value would have a GC reference type +* `grow_table`: grow the current table (up to the optional maximum), similar to + `grow_memory` +* `current_table_length`: like `current_memory`. diff --git a/JS.md b/JS.md index 71713922..03bc97ca 100644 --- a/JS.md +++ b/JS.md @@ -13,6 +13,7 @@ basically equivalent to `new WebAssembly.Instance(new WebAssembly.Module(bytes), imports)` as defined below and will be removed at some point in the future.* + ## The `WebAssembly` object The `WebAssembly` object is the initial value of the `WebAssembly` property of @@ -20,19 +21,24 @@ the global object. Like the `Math` and `JSON` objects, the `WebAssembly` object is a plain JS object (not a constructor or function) that acts like a namespace and has the following properties: + ### Constructor Properties of the `WebAssembly` object The following intrinsic objects are added: -* `WebAssembly.Module` : the [`WebAssembly.Module` constructor](#wasmmodule-constructor) -* `WebAssembly.Instance` : the [`WebAssembly.Instance` constructor](#wasminstance-constructor) +* `WebAssembly.Module` : the [`WebAssembly.Module` constructor](#webassemblymodule-constructor) +* `WebAssembly.Instance` : the [`WebAssembly.Instance` constructor](#webassemblyinstance-constructor) +* `WebAssembly.Memory` : the [`WebAssembly.Memory` constructor](#webassemblymemory-constructor) +* `WebAssembly.Table` : the [`WebAssembly.Table` constructor](#webassemblytable-constructor) * `WebAssembly.CompileError` : a [NativeError](http://tc39.github.io/ecma262/#sec-nativeerror-object-structure) which indicates an error during WebAssembly decoding or validation * `WebAssembly.RuntimeError` : a [NativeError](http://tc39.github.io/ecma262/#sec-nativeerror-object-structure) which indicates an error while running WebAssembly code + ### Function Properties of the `WebAssembly` object + #### `WebAssembly.compile` The `compile` function has the signature: @@ -45,7 +51,7 @@ the returned `Promise` is [rejected](http://tc39.github.io/ecma262/#sec-rejectpr with a `TypeError`. Otherwise, this function starts an asychronous task to compile a `WebAssembly.Module` -as described in the [`WebAssembly.Module` constructor](#wasmmodule-constructor). +as described in the [`WebAssembly.Module` constructor](#webassemblymodule-constructor). On success, the `Promise` is [fulfilled](http://tc39.github.io/ecma262/#sec-fulfillpromise) with the resulting `WebAssembly.Module` instance. On failure, the `Promise` is [rejected](http://tc39.github.io/ecma262/#sec-rejectpromise) with a @@ -59,6 +65,7 @@ In the [future](FutureFeatures.md#streaming-compilation), this function can be extended to accept a [stream](https://streams.spec.whatwg.org), thereby enabling asynchronous, background, streaming compilation. + ## `WebAssembly.Module` Objects A `WebAssembly.Module` object represents the stateless result of compiling a @@ -66,6 +73,7 @@ WebAssembly binary-format module and contains one internal slot: * [[Module]] : an [`Ast.module`](https://github.com/WebAssembly/spec/blob/master/ml-proto/spec/ast.ml#L208) which is the spec definition of a validated module AST + ### `WebAssembly.Module` Constructor The `WebAssembly.Module` constructor has the signature: @@ -84,11 +92,12 @@ Otherwise, this function performs synchronous compilation of the `BufferSource`: an AST according to [BinaryEncoding.md](BinaryEncoding.md) and then validated according to the rules in [spec/check.ml](https://github.com/WebAssembly/spec/blob/master/ml-proto/spec/check.ml#L325). * The spec `string` values inside `Ast.module` are decoded as UTF8 as described in - [Web.md](Web.md#function-names). + [Web.md](Web.md#names). * On success, a new `WebAssembly.Module` instance is returned with [[Module]] set to the validated `Ast.module`. * On failure, a new `WebAssembly.CompileError` is thrown. + ### Structured Clone of a `WebAssembly.Module` A `WebAssembly.Module` is a @@ -105,6 +114,7 @@ Given the above engine optimizations, structured cloning provides developers explicit control over both compiled-code caching and cross-window/worker code sharing. + ## `WebAssembly.Instance` Objects A `WebAssembly.Instance` object represents the instantiation of a @@ -118,6 +128,7 @@ as well as one plain data property (configurable, writable, enumerable) added by the constructor: * exports : a [Module Namespace Object](http://tc39.github.io/ecma262/#sec-module-namespace-objects) + ### `WebAssembly.Instance` Constructor The `WebAssembly.Instance` constructor has the signature: @@ -137,45 +148,105 @@ not Object, a `TypeError` is thrown. If the list of [`module.imports`](https://github.com/WebAssembly/spec/blob/master/ml-proto/spec/ast.ml#L215) is not empty and `Type(importObject)` is not Object, a `TypeError` is thrown. -Let `imports` by an initially-empty list of JS functions. +Let `importedFunctions`, `importedMemories` and `importedTables` be initially-empty +lists of JS functions, `WebAssembly.Memory` objects, and `WebAssembly.Table` +objects, respectively. For each [`import`](https://github.com/WebAssembly/spec/blob/master/ml-proto/spec/kernel.ml#L135) `i` in `module.imports`: * Let `v` be the resultant value of performing [`Get`](http://tc39.github.io/ecma262/#sec-get-o-p)(`importObject`, [`i.module_name`](https://github.com/WebAssembly/spec/blob/master/ml-proto/spec/kernel.ml#L139)). -* If [`i.func_name`](https://github.com/WebAssembly/spec/blob/master/ml-proto/spec/kernel.ml#L140) +* If [`i.export_name`](https://github.com/WebAssembly/spec/blob/master/ml-proto/spec/kernel.ml#L140) is not the empty string: * If `Type(v)` is not Object, throw a `TypeError`. - * Let `v` instead be the value of performing [`Get`](http://tc39.github.io/ecma262/#sec-get-o-p)(`v`, `i.func_name`) -* If `IsCallable(v)` is `false`, throw a `TypeError`. -* Append `v` to `imports`. + * Let `v` instead be the value of performing [`Get`](http://tc39.github.io/ecma262/#sec-get-o-p)(`v`, `i.export_name`) +* If `i` is a function export: + * If `IsCallable(v)` is `false`, throw a `TypeError`. + * Otherwise, append `v` to `importedFunctions`. +* If `i` is a memory export: + * If `v` is not a [`WebAssembly.Memory` object](#webassemblymemory-objects), + throw a `TypeError`. + * Otherwise, append `v` to `importedMemories`. +* If `i` is a table export: + * If `v` is not a [`WebAssembly.Table` object](#webassemblytable-objects), + throw a `TypeError`. + * Otherwise, append `v` to `importedTables`. + +(Note: the ML spec currently doesn't have multiple kinds of exports; we assume here +it will be extended in the future.) Let `instance` be the result of evaluating -[`Eval.init`](https://github.com/WebAssembly/spec/blob/master/ml-proto/spec/eval.ml#L314) -with arguments `module` and `imports`. -Note: this synchronously executes the [`start`](Modules.md#module-start-function) -function, if present. - -Let `exports` be an initially-empty list of (string, JS function) pairs. -Let `exportedFunctions` be an initially-empty map from function indices (integers) to -JS functions. - -For each [exported function](https://github.com/WebAssembly/spec/blob/master/ml-proto/spec/kernel.ml#L128) -`f` in `module.exports`: -* Let `index` be the exported function index of `f`. -* If `index` is not already present in `exportedFunctions`, add a mapping - from `index` to the result of creating a new - [Exported Function](#exported-function-exotic-objects) Exotic Object (given `instance` and `index`). -* Append the pair (`f.name`, `exportedFunctions[index]`) to `exports` - -Let `moduleRecord` be a new [WebAssembly Module Record](#webassembly-module-record) (given `exports`). - -Let `exportStrings` be the projected list of only the first (string) components of `exports`. -Let `moduleNamespace` be the result of calling +[`Eval.init`](https://github.com/WebAssembly/spec/blob/master/ml-proto/spec/eval.ml#L319) +with arguments `module`, `importedFunctions`, `importedMemories` and +`importedTables`. Note: this synchronously executes the +[`start`](Modules.md#module-start-function) function, if present. + +(Note: in the ML spec, `Eval.init` currently doesn't accept imported memories or +tables; we assume here it will be extended in the future.) + +Let `exports` be an initially-empty list of (string, JS value) pairs. + +Let `exportedFunctions` be a map whose initial contents are mappings from +the array indices of `importedFunctions` to `importedFunctions[i]`. + +Let `exportedMemories` be a map whose initial contents are mappings from +the array indices of `exportedMemories` to `exportedMemories[i]`. + +Let `exportedTables` be a map whose initial contents are mappings from +the array indices of `exportedTables` to `exportedTables[i]`. + +For each [export](https://github.com/WebAssembly/spec/blob/master/ml-proto/spec/kernel.ml#L128) +`e` in `module.exports`: +* If `e` is a function export: + * Let `i` be the function index of `e`. + * If `i` is not already present in `exportedFunctions`: + * Let `v` be the result of creating a new [Exported Function](#exported-function-exotic-objects) + Exotic Object (given `instance` and `i`). + * Add a mapping from `i` to `v` in `exportedFunctions`. + * Otherwise, let `v` be `exportedFunctions[i]`. +* If `e` is a memory export: + * Let `i` be the memory index of `e`. + * If `i` is not already present in `exportedMemories`: + * Let `memory` be the `i`th linear memory in the + [memory index space](Modules.md#index-spaces) of `instance`. + * Let `v` be the result of [`CreateMemoryObject`](#creatememoryobject)`(memory)`. + * Add a mapping from `i` to `v` in `exportedMemories`. + * Otherwise, let `v` be `exportedMemories[i]`. +* If `e` is a table export: + * Let `i` be the table index of `e`. + * If `i` is not already present in `exportedTables`: + * Let `table` be the `i`th table in the + [table index space](Modules.md#index-spaces) of `instance`. + * Let `values` be an initially-empty list of Function Objects. + * For each function index `fi` in `table`: + * If `fi` is `None`, then append `null` to `values`. + * Otherwise, if `fi` is not already present in `exportedFunctions`: + * Let `f` be the result of creating a new + [Exported Function](#exported-function-exotic-objects) + Exotic Object (given `instance` and `fi`). + * Add a mapping from `fi` to `f` in `exportedFunctions`. + * Append `f` to `values`. + * Otherwise, append `exportedFunctions[i]` to `values`. + * Let `v` be a new `WebAssembly.Table` instance with [[Table]] + set to `table` and [[Values]] set to `values`. + * Add a mapping from `i` to `v` in `exportedTables`. + * Otherwise, let `v` be `exportedTables[i]`. +* Append the pair (`e.name`, `v`) to `exports` + +(Note: the ML spec currently doesn't have table exports or give all exports an +index; we assume here it will be extended in the future.) + +Let `moduleRecord` be a new [WebAssembly Module Record](#webassembly-module-record) +(given `exports`). + +Let `exportStrings` be the projected list of only the first (string) components +of `exports`. Let `moduleNamespace` be the result of calling [`ModuleNamespaceCreate(moduleRecord, exportStrings)`](http://tc39.github.io/ecma262/#sec-modulenamespacecreate). Set `moduleRecord.[[Namespace]]` to `moduleNamespace`. -Return a new `WebAssembly.Instance` object initializing `[[Instance]]` = `instance` and `exports` = `moduleNamespace`. +Return a new `WebAssembly.Instance` object setting `[[Instance]]` to `instance` +and `exports` to `moduleNamespace`. + ### WebAssembly Module Record @@ -185,7 +256,7 @@ has one concrete subclass, [Source Text Module Record](http://tc39.github.io/ecm which corresponds to a normal ES6 module. These interfaces are used to define the [process of loading a module on the Web](https://html.spec.whatwg.org/multipage/webappapis.html#integration-with-the-javascript-module-system). -When WebAssembly gets [ES6 Module integration](Modules.md#integration-with-es6-modules), +When WebAssembly gets [ES6 Module integration](Modules.md#integration-with-es6-modules), a new *WebAssembly Module Record* subclass would be added which would specify the right thing to do for WebAssembly modules as part of the overall loading process. @@ -199,6 +270,7 @@ More work is needed to flesh out the precise spec interaction here, but the basi idea is to create a [Module Environment Record](http://tc39.github.io/ecma262/#sec-module-environment-records) from `exports` as the [[Environment]] of a new WebAssembly Module Record. + ## Exported Function Exotic Objects Functions exported by WebAssembly modules are reflected in JS via a new kind @@ -242,6 +314,199 @@ WebAssembly Exported Functions have a `[[Call]](this, argValues)` method defined Exported Functions do not have a [[Construct]] method and thus it is not possible to call one with the `new` operator. + +## `WebAssembly.Memory` Objects + +A `WebAssembly.Memory` object contains a single [linear memory](AstSemantics.md#linear-memory) +which can be simultaneously referenced by multiple `Instance` objects. Each +`Memory` object has two internal slots: + * [[Memory]] : a [`Memory.memory`](https://github.com/WebAssembly/spec/blob/master/ml-proto/spec/memory.mli) + * [[BufferObject]] : the current `ArrayBuffer` whose [[ArrayBufferByteLength]] + matches the current byte length of [[Memory]] + + +### `WebAssembly.Memory` Constructor + +The `WebAssembly.Memory` constructor has the signature: +``` +new Memory(memoryDescriptor) +``` +If the NewTarget is `undefined`, a `TypeError` exception is thrown (i.e., this +constructor cannot be called as a function without `new`). + +If `Type(memoryDescriptor)` is not Object, a `TypeError` is thrown. + +Let `initial` be [`ToInteger`](http://tc39.github.io/ecma262/#sec-tointeger)([`Get`](http://tc39.github.io/ecma262/#sec-get-o-p)(`memoryDescriptor`, `"initial"`)). + +If [`HasProperty`](http://tc39.github.io/ecma262/#sec-hasproperty)(`"maximum"`), +then let `maximum` be [`ToInteger`](http://tc39.github.io/ecma262/#sec-tointeger)([`Get`](http://tc39.github.io/ecma262/#sec-get-o-p)(`memoryDescriptor`, `"maximum"`)). +Otherwise, let `maximum` be `None`. + +Let `memory` be the result of calling +[`Memory.create`](https://github.com/WebAssembly/spec/blob/master/ml-proto/spec/memory.mli#L18) +given arguments `initial` and `maximum`. Note that `initial` and `maximum` are +specified in units of WebAssembly pages (64KiB). + +(Note: the ML spec currently doesn't implement the maximum memory limit; we +assume here it will be extended in the future.) + +Return the result of [`CreateMemoryObject`](#creatememoryobject)(`memory`). + + +### CreateMemoryObject + +Given a [`Memory.memory`](https://github.com/WebAssembly/spec/blob/master/ml-proto/spec/memory.mli) +`m`, to create a `WebAssembly.Memory`: + +Let `buffer` be a new `ArrayBuffer` whose +[[[ArrayBufferData]]](http://tc39.github.io/ecma262/#sec-properties-of-the-arraybuffer-prototype-object) +aliases `m` and whose +[[[ArrayBufferByteLength]]](http://tc39.github.io/ecma262/#sec-properties-of-the-arraybuffer-prototype-object) +is set to the byte length of `m`. + +Return a new `WebAssembly.Memory` instance with [[Memory]] set to `m` and +[[BufferObject]] set to `buffer`. + + +### `WebAssembly.Memory.prototype.grow` + +Let `M` be the `this` value. If `M` is not a `WebAssembly.Memory`, +a `TypeError` is thrown. + +This method performs a [`grow_memory`](AstSemantics.md#resizing) on +`M.[[Memory]]`, having first performed [`ToInteger`](http://tc39.github.io/ecma262/#sec-tointeger) +on the first argument. On failure, a `WebAssembly.RuntimeError` is thrown. + +If `M.[[Memory]].maximum` is `None`, perform +[`DetachArrayBuffer`](http://tc39.github.io/ecma262/#sec-detacharraybuffer)(`M.[[BufferObject]]`). + +In either case, assign to `M.[[BufferObject]]` a new `ArrayBuffer` whose +[[[ArrayBufferData]]](http://tc39.github.io/ecma262/#sec-properties-of-the-arraybuffer-prototype-object) +aliases `M.[[Memory]]` and whose +[[[ArrayBufferByteLength]]](http://tc39.github.io/ecma262/#sec-properties-of-the-arraybuffer-prototype-object) +is set to the new byte length of `M.[[Memory]]`. + + +### `WebAssembly.Memory.prototype.buffer` + +This is an accessor property whose [[Set]] is Undefined and whose [[Get]] +accessor function performs the following steps: + +If `this` is not a `WebAssembly.Memory`, a `TypeError` is thrown. Otherwise +return `M.[[BufferObject]]`. + + +## `WebAssembly.Table` Objects + +A `WebAssembly.Table` object contains a single [table](AstSemantics.md#tables) +which can be simultaneously referenced by multiple `Instance` objects. Each +`Table` object has one internal slot: + * [[Table]] : a [`Table.table`](https://github.com/WebAssembly/spec/blob/master/ml-proto/spec/table.ml) + * [[Values]] : an array whose elements are either `null` or Function Objects + + +### `WebAssembly.Table` Constructor + +The `WebAssembly.Table` constructor has the signature: +``` +new Table(tableDescriptor) +``` +If the NewTarget is `undefined`, a `TypeError` exception is thrown (i.e., this +constructor cannot be called as a function without `new`). + +If `Type(tableDescriptor)` is not Object, a `TypeError` is thrown. + +Let `element` be the result of [ToTypeDescriptor](#totypedescriptor)([`Get`](http://tc39.github.io/ecma262/#sec-get-o-p)(`tableDescriptor`, `"element"`)). + +Let `initial` be [`ToInteger`](http://tc39.github.io/ecma262/#sec-tointeger)([`Get`](http://tc39.github.io/ecma262/#sec-get-o-p)(`tableDescriptor`, `"initial"`)). + +If [`HasProperty`](http://tc39.github.io/ecma262/#sec-hasproperty)(`"maximum"`), +then let `maximum` be [`ToInteger`](http://tc39.github.io/ecma262/#sec-tointeger)([`Get`](http://tc39.github.io/ecma262/#sec-get-o-p)(`tableDescriptor`, `"maximum"`)). +Otherwise, let `maximum` be None. + +Let `table` be the result of calling `Table.create` given arguments `element`, +`initial` and `maximum`. + +(Note: the ML spec currently represents tables as a single `int list` of +function indices; we assume here it will be extended in the future with +a more general `Table` similar to +[`Memory`](https://github.com/WebAssembly/spec/blob/master/ml-proto/spec/memory.mli) +that also includes an `element` field of type +[`Type.func_type option`](https://github.com/WebAssembly/spec/blob/master/ml-proto/spec/types.ml#L5).) + +Let `values` be a new empty array of `initial` elements, all with value +`null`. + +Return a new `WebAssemby.Table` instance with [[Table]] set to `table` and +[[Values]] set to `values`. + + +### ToTypeDescriptor + +This is an abstract function which, given a value `td`, returns either +[`Type.func_type`](https://github.com/WebAssembly/spec/blob/master/ml-proto/spec/types.ml#L5) +or `None`. (Note: when tables are generalized to hold non-functions in the +[future](FutureFeatures.md#more-table-operators-and-types), the return type of +`ToTypeDescriptor` would return a more general type.) + +TODO: pick the best way to describe a signature. Maybe some JSON-like object? +Maybe something more future-compatible with [Typed Objects](https://github.com/tschneidereit/typed-objects-explainer/)? + +If `td` is the string `"function"`, return `None`. + +Otherwise throw a `TypeError`. + + +### `WebAssembly.Table.prototype.grow` + +This method calls `Table.grow`, having performed +[`ToInteger`](http://tc39.github.io/ecma262/#sec-tointeger) on the first argument. +On failure, a `WebAssembly.RuntimeError` is thrown. + +(Note: the ML spec currently doesn't support resizing tables; we assume here it +will be extended in the future to have a `grow` operation similar to +[`Memory.grow`](https://github.com/WebAssembly/spec/blob/master/ml-proto/spec/memory.mli#L21).) + + +### `WebAssembly.Table.prototype.get` + +This method has the following signature +``` +get(index) +``` +Let `T` be the `this` value. If `T` is not a `WebAssembly.Table`, a `TypeError` +is thrown. + +Let `i` be the result of [`ToInteger`](http://tc39.github.io/ecma262/#sec-tointeger)(`index`). + +Return `T.[[Values]][i]`. + + +### `WebAssembly.Table.prototype.set` + +This method has the following signature +``` +set(index, value) +``` + +Let `T` be the `this` value. If `T` is not a `WebAssembly.Table`, a `TypeError` +is thrown. + +If [`IsCallable`](http://tc39.github.io/ecma262/#sec-iscallable)(`value`) is +false and `Type(value)` is not Null, throw a type error. + +If `value` is a [Exported Function Exotic Object](#exported-function-exotic-objects) +and if `table.element` is not `None` and `T.element` does not +precisely match the signature of `v.[[FunctionIndex]]` in `v.[[Instance]].[[Module]]` +throw a `TypeError`. + +Let `i` be the result of [`ToInteger`](http://tc39.github.io/ecma262/#sec-tointeger)(`index`). + +Set `T.[[Values]][i]` to `value`. + +Return Undefined. + + ## Sample API Usage Given `demo.was` (encoded to `demo.wasm`): @@ -272,7 +537,5 @@ fetch('demo.wasm').then(response => ``` ## TODO - -* `WebAssembly.Memory`: imports, exports -* `WebAssembly.Table`: imports, exports * `WebAssembly.Module` `exports`/`imports` properties (reflection) +* JS API for cyclic imports diff --git a/Modules.md b/Modules.md index 5c52792e..69c0e851 100644 --- a/Modules.md +++ b/Modules.md @@ -1,58 +1,95 @@ # Modules The distributable, loadable, and executable unit of code in WebAssembly -is called a **module**. At runtime, a module can be loaded by a runtime -to produce an **instance** which encapsulates all the state directly -manipulated by running WebAssembly code. A WebAssembly instance's initial state -is determined by the module it was loaded from. - -A module contains: -* a set of [imports and exports](Modules.md#imports-and-exports); -* an optional start method name or index; -* a section defining [linear memory](Modules.md#linear-memory-section); -* a section containing [code](Modules.md#code-section); -* after the MVP, sections containing [debugging/symbol information](Tooling.md) or - a reference to separate files containing them; and -* possibly other sections in the future. -Sections declare their type and byte-length. Sections with unknown types are -silently ignored. +is called a **module**. At runtime, a module can be **instantiated** +to produce an **instance**, which is an immutable tuple referencing all +the state accessible to the running module. Multiple module instances can +access the same shared state which is the basis for +[dynamic linking](DynamicLinking.md) in WebAssembly. WebAssembly modules +are also designed to [integrate with ES6 modules](#integration-with-es6-modules). + +A module may contain the following sections: +* [imports](#imports) +* [exports](#exports) +* [start](#module-start-function) +* [linear memory](#linear-memory-section) +* [data](#data-section) +* [table](#table-section) +* [elements](#elements-section) +* [code](#code-section) An instance contains: -* the code of the module from which the instance was loaded; -* a [linear memory](AstSemantics.md#linear-memory); -* fully resolved imports; -* host-specific state (for example, the JavaScript function objects that reflect - exported functions to JavaScript); -* (when [threading](PostMVP.md#threads) is added) TLS variable state; -* (when [dynamic linking](DynamicLinking.md) is added) the code of multiple modules - that have been dynamically linked into the same instance; -* and other semantically-visible state added by other future features. - -While WebAssembly modules are designed to interoperate with ES6 modules -in a Web environment (more details [below](Modules.md#integration-with-es6-modules)), -WebAssembly modules are defined independently of JavaScript and do not require -the host environment to include a JavaScript VM. - -## Imports and Exports - -A module defines a set of functions in its -[code section](Modules.md#code-section) and can declare and name a subset of -these functions to be **exports**. The meaning of exports (how and when they are -called) is defined by the host environment. For example, a minimal shell -environment might only probe for and call the start function defined by the start -node of the module when given a module to execute. Exports are exported by name, -where the name is an arbitrary byte string of a given length. The host may need -to mangle these names. - -A module can declare a set of **imports**. An import is a tuple containing a -module name, the name of an exported function to import from the named module, -and the signature to use for that import within the importing module. Within a -module, the import can be [directly called](AstSemantics.md#calls) like a -function (according to the signature of the import). When the imported -module is also WebAssembly, it would be an error if the signature of the import -doesn't match the signature of the export. - -The WebAssembly spec does not define how imports are interpreted: +* the code of the instantiated module; and +* a set of [index spaces](#index-spaces) of linear memories, tables and + functions that were imported or defined by the module. + +## Index Spaces + +Any number of functions, memories and tables can be both imported and +defined in a given module. Imports and definitions are merged into index +spaces (one for each of memories, tables and functions) which allows uniform +treatment of imports and definitions thereafter (via index). + +Specifically, the index space of functions is defined by first giving each of +the M function imports an index \[0, M\) (ordered by their sequence in the +imports section) and then giving each of the N function definitions an index +\[M, M+N\) (ordered by their sequence in the module). The memory and table +index spaces are defined in the same way. + +Thus, any use of a *function index* (e.g., a [`call`](AstSemantics.md#calls)) +can refer to either a function import or definition and similarly for +and *table index* or *memory index*. + +## Imports + +A module can declare a sequence of **imports** which are provided, at +instantiation time, by the host environment. There are several kinds of imports: +* **function imports**, which can be called inside the module by the + [`call`](AstSemantics.md#calls) operator; +* **linear memory imports**, which can be accessed inside the module by the + [memory operators](AstSemantics.md#linear-memory); and +* **table imports**, which can be accessed inside the module by + [call_table](AstSemantics.md#calls) and other + table operators in the + [future](FutureFeatures.md#more-table-operators-and-types). + +In the future, other kinds of imports may be added. Imports are designed to +allow modules to share code and data while still allowing separate compilation +and caching. + +All imports include two opaque names: a *module name* and an *export name*. The +interpretation of these names is up to the host environment but designed to +allow a host environments, like the [Web](Web.md), to support a two-level +namespace. + +Each specific kind of import defines additional fields: + +A *function import* includes a signature to use for the imported +function *inside* the module. The checking of the signature against the +imported function *outside* the module is defined by the host environment. +However, if the imported function is a WebAssembly function, the host +environment must raise an instantiation-time error if there is a signature +mismatch. + +A *linear memory import* includes the same set of fields defined in the +[Linear Memory section](#linear-memory-section): *initial length* and +optional *maximum length*. The host environment must only allow imports +of WebAssembly linear memories that have initial length *greater-or-equal* than +the initial length declared in the import and that have maximum length +*less-or-equal* than the maximum length declared in the import. This ensures +that separate compilation can assume: memory accesses below the declared initial +length are always in-bounds, accesses above the declared maximum length are +always out-of-bounds and if initial equals maximum, the length is fixed. + +A *table import* includes the same set of fields defined in the +[Table section](#table-section): *element type*, *initial length* +and optional *maximum length*. As with the linear memory section, +the host environment must ensure only WebAssembly tables are imported +with exactly-matching element type, greater-or-equal initial length, +and less-or-equal maximum length. + +Since the WebAssembly spec does not define how import names are interpreted: +* the [Web environment](Web.md#names) defines names to be UTF8-encoded strings; * the host environment can interpret the module name as a file path, a URL, a key in a fixed set of builtin modules or the host environment may invoke a user-defined hook to resolve the module name to one of these; @@ -66,14 +103,24 @@ arbitrary host environment functionality to WebAssembly code, similar to a native `syscall`. For example, a shell environment could define a builtin `stdio` module with an export `puts`. -In C/C++, an undefined `extern` declaration (perhaps only when given the -magic `__attribute__` or declared in a separate list of imports) could be -compiled to an import and C/C++ calls to this `extern` would then be compiled -to calls to this import. This is one way low-level C/C++ libraries could call -out of WebAssembly in order to implement portable source-level interfaces -(e.g., POSIX, OpenGL or SDL) in terms of host-specific functionality. +## Exports -### Integration with ES6 modules +A module can declare a sequence of **exports** which are provided, at +instantiation time, to the host environment. There are several kinds of exports: +* **function exports**, which allow the exported function to be called by the + host environment (or other code running in the host environment); +* **linear memory exports**, which allow the exported linear memory to be + aliased by the host environment (or other code running int he host + environment); and +* **table exports**, which allow the elements of the table to be read, written + or called by the host environment (or other code running in the host + environment). + +Exports additionally contain a name and an index into the associated +export's type's [index space](#index-spaces). As with imports, the meaning +of an export name is defined by the host. + +## Integration with ES6 modules While ES6 defines how to parse, link and execute a module, ES6 does not define when this parsing/linking/execution occurs. An additional extension @@ -131,7 +178,8 @@ by the loader after the instance is initialized and before the exported function are called. * The start function must not take any arguments or return anything -* The function can also be exported +* The function is identified by [function index](#index-spaces) and can also be + exported * There can only be at most one start node per module For example, a start node in a module will be: @@ -154,14 +202,13 @@ A module can: ## Linear memory section -A module may contain an optional section declaring the use of linear memory -by the module. If the section is absent, the linear memory operators -`load`, `store`, `memory_size`, and `grow_memory` may not be used in the module. - -The linear memory section declares the initial [memory size](AstSemantics.md#linear-memory) -(which may be subsequently increased by [`grow_memory`](AstSemantics.md#resizing)). +The *linear memory section* may contain zero or more definitions of distinct +[linear memories](AstSemantics.md#linear-memory) which are added to the +[linear memory index space](#index-spaces). Each linear memory section declares +an initial [memory size](AstSemantics.md#linear-memory) (which may be +subsequently increased by [`grow_memory`](AstSemantics.md#resizing)) and an +optional maximum memory size. -The linear memory section may optionally declare a maximum memory size. [`grow_memory`](AstSemantics.md#resizing) is guaranteed to fail if attempting to grow past the declared maximum. When declared, implementations *should* (non-normative) attempt to reserve virtual memory up to the maximum size. While @@ -170,30 +217,45 @@ reserve up to the *maximum* is not. When a maximum memory size is *not* declared on architectures with limited virtual address space, engines should allocate only the initial size and reallocate on demand. -The initial contents of linear memory are zero by default. However, the memory -section contains a possibly-empty array of *segments* (analogous to `.data`) -which can specify the initial contents of fixed `(offset, length)` ranges of -memory. - -The linear memory section may optionally declare that the instance's -linear memory is *externally aliasable*. How linear memory is aliased is up -to the host environment (as with all module exports). The -[Web](Web.md#aliasing-linear-memory-from-JS) would reflect exported linear -memory to JS as an `ArrayBuffer`. The MVP does not currently provide for -*importing* linear memory though this may be added -[in the future](FutureFeatures.md#importing-linear-memory). +## Data section + +The initial contents of linear memory are zero. The *data section* contains a +possibly-empty array of *data segments* which specify the initial contents +of fixed `(offset, length)` ranges of a given memory, specified by its [linear +memory index](#index-space). This section is analogous to the `.data` section +of native executables. + +## Table section + +A *table section* may contain zero or more definitions of distinct +[tables](AstSemantics.md#tables) which are added to the +[table index space](#index-spaces). Each table section declares an *element +type*, *initial length*, and optional *maximum length*. + +In the MVP, tables can only be resized via host-defined APIs (such as +the JavaScript [`WebAssembly.Table.prototype.grow`](JS.md#webassemblytableprototypegrow)). +A `grow_table` may be added in the [future](FutureFeatures.md#more-table-operators-and-types). +In either case, table growth is guaranteed to fail if attempting to grow past +the declared maximum. As with linear memory, when a maximum is declared, +implementations *should* (non-normative) attempt to reserve virtual memory up to +the maximum size. While failure to allocate the *initial* memory size is a +runtime error, failure to reserve up to the *maximum* is not. When a maximum +memory size is *not* declared, on architectures with limited virtual address +space, engines should allocate only the initial size and reallocate on demand. + +## Elements section + +For function tables, the intial contents of the tables' elements are sentinel +values that throw if called. The *elements section* contains a possibly-empty +array of *element segments* which define the initial contents of fixed +`(offset, length)` ranges of a given table, specified by its +[table index](#index-spaces). The elements are specified by their +[function index](#index-spaces). This section is the table analogue of the +linear memory [data section](#data-section). ## Code section -The WebAssembly spec defines the code section of a module in terms of an -[Abstract Syntax Tree](AstSemantics.md) (AST). Additionally, the spec defines -two concrete representations of the AST: a [binary format](BinaryEncoding.md) -which is natively decoded by the browser and a [text format](TextFormat.md) -which is intended to be read and written by humans. A WebAssembly environment -is only required to understand the binary format; the text format is defined so -that WebAssembly modules can be written by hand (and then converted to binary -with an offline tool) and so that developer tools have a well-defined text -projection of a binary WebAssembly module. This design separates the concerns -of specifying and reasoning about behavior, over-the-wire size and compilation -speed, and ergonomic syntax. - +The code section contains a sequence of functions definitions which are added to +the [function index space](#index-spaces). Functions are split into +a sequence of [signature declarations](BinaryEncoding.md#function-section) +and [bodies](BinaryEncoding.md#code-section) as defined in the binary encoding. diff --git a/Nondeterminism.md b/Nondeterminism.md index db738c97..68af0eb9 100644 --- a/Nondeterminism.md +++ b/Nondeterminism.md @@ -48,7 +48,7 @@ currently admits nondeterminism: - Memory allocation may fail. - The runtime can fail to allocate a physical page when a memory location is first accessed (e.g. through a load or store), even if that memory was virtually reserved - by the maximum size property of the [memory section](Module.md#linear-memory-section). + by the maximum size property of the [memory section](Modules.md#linear-memory-section). - Program stack may get exhausted (e.g., because function call depth is too big, or functions have too many locals, or infinite recursion). Note that this stack isn't located in the program-accessible linear memory. diff --git a/PostMVP.md b/PostMVP.md index 9d8ffc1a..2382fe7d 100644 --- a/PostMVP.md +++ b/PostMVP.md @@ -32,10 +32,6 @@ and can thus be represented efficiently by the engine. [PNaCl atomic support]: https://developer.chrome.com/native-client/reference/pnacl-c-cpp-language-support#memory-model-and-atomics [SharedArrayBuffer]: https://github.com/tc39/ecmascript_sharedmem -## Dynamic linking - -This is covered in the [dynamic linking](DynamicLinking.md) section. - ## Fixed-width SIMD Support fixed-width SIMD vectors, initially only for 128-bit wide vectors as diff --git a/Web.md b/Web.md index 737558a1..21b5ff6b 100644 --- a/Web.md +++ b/Web.md @@ -24,10 +24,10 @@ alias the exported memory of instantiated modules, etc. ## Modules -WebAssembly's [modules](Modules.md) allow for natural [integration with +WebAssembly's [modules](Modules.md) allow for natural [integration with the ES6 module system](Modules.md#integration-with-es6-modules). -### Function Names +### Names A WebAssembly module imports and exports functions. WebAssembly names functions using arbitrary-length byte sequences. Any 8-bit values are permitted in a @@ -56,35 +56,6 @@ Transcoding failure is detected by `decodeURIComponent`, which may throw `URIError`. If it does, the WebAssembly module will not validate. This validation rule is only mandatory for Web embedding. -## Aliasing linear memory from JS - -If [allowed by the module](Modules.md#linear-memory-section), JavaScript can -alias a loaded module's linear memory through an exported `ArrayBuffer`. -Module instances would additionally expose methods to JS to copy ranges of -bytes into and out of linear memory as separate (unaliased) `ArrayBuffer`s. - -Since JS semantics and implementations require the `byteLength` of an -`ArrayBuffer` to be constant, [resizing](AstSemantics.md#resizing) the -linear memory cannot simply resize the exported `ArrayBuffer`. Instead, -the `ArrayBuffer` would be [detached](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-detacharraybuffer) -and a new `ArrayBuffer` (with a new `byteLength`) would be exported in -its place. - -When [threads](PostMVP.md#threads) are added, a -[`SharedArrayBuffer`](https://github.com/lars-t-hansen/ecmascript_sharedmem) -would need to be exported instead of an `ArrayBuffer`. However, the -detach-on-resize strategy would pose significant usability and implementation -hazards, since resizing can happen concurrently. One solution would be -to simply not export a `SharedArrayBuffer` when a module declared use of -threads and resizable memory (the copy in/out methods would need to be used -instead). - -Similarly, various [linear memory operations](FutureFeatures.md#finer-grained-control-over-memory) -like `mprotect` conflict with the JS semantics of `ArrayBuffer` and -would inhibit export. In general, `ArrayBuffer` could be viewed as an -optimization of copy in/out that was only available when linear memory -behaved like an `ArrayBuffer` (or `SharedArrayBuffer`). - ## Security WebAssembly's security model should depend on [CORS][] and From b6eb6b99753c32adcd0216b115f70f91051b8d1d Mon Sep 17 00:00:00 2001 From: Luke Wagner Date: Fri, 6 May 2016 10:03:30 -0500 Subject: [PATCH 02/22] Clarify mutation of elements --- AstSemantics.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/AstSemantics.md b/AstSemantics.md index 0311c82a..ceee6ffd 100644 --- a/AstSemantics.md +++ b/AstSemantics.md @@ -251,8 +251,9 @@ opaque handles/references at the cost of a bounds-checked table indirection. The table's element type dynamically constrains the type of elements stored in the table and allows engines to avoid some type checks on table use. When -tables are mutated, any stored value is necessarily coerced (possibly trapping) -to the element type. +tables are mutated, any stored value must match the element type (by one of +static validation constraint, trapping dynamic type validation check, or dynamic +coercion). In the MVP, the set of operations and types for tables is limited: * tables may only be accessed via [`call_table`](#calls); From 7d783080f7bdf684fdf000c27a0579662081d42d Mon Sep 17 00:00:00 2001 From: Luke Wagner Date: Fri, 6 May 2016 10:11:50 -0500 Subject: [PATCH 03/22] Mention CFI use case --- AstSemantics.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/AstSemantics.md b/AstSemantics.md index ceee6ffd..51cd7895 100644 --- a/AstSemantics.md +++ b/AstSemantics.md @@ -273,7 +273,10 @@ or to put all functions into the same table (with no signature). The former strategy allows the engine to eliminate dynamic signature-mismatch checks while the latter strategy gives each function a unique index (which may be necessary for C/C++ compatibility) and admits simpler and potentially more space-efficient -[dynamic linking](DynamicLinking.md). +[dynamic linking](DynamicLinking.md). Toolchains can also create many +minimally-sized tables to implement a variety of [CFI schemes](http://clang.llvm.org/docs/ControlFlowIntegrity.html#forward-edge-cfi-for-virtual-calls) +that constrain the set of valid call targets for a given `call_table`. + ## Local variables From 5c9ba0155c58c993994d29bfc900827cc0b9f6f0 Mon Sep 17 00:00:00 2001 From: Luke Wagner Date: Fri, 6 May 2016 10:19:58 -0500 Subject: [PATCH 04/22] Clarify call_table signature mismatches --- AstSemantics.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/AstSemantics.md b/AstSemantics.md index 51cd7895..0143e3d0 100644 --- a/AstSemantics.md +++ b/AstSemantics.md @@ -379,8 +379,12 @@ The specified [table](#tables) must have either a "function" or "function with signature `X`" element type (in the MVP, these are the only two possibilities, but in the future, there may be other kinds of tables). If the table element type has a signature, it must match the signature of the -call. Otherwise, the signature is checked at runtime and a signature mismatch -causes a trap. +call. Otherwise, the signature is checked each time the `call_table` is executed, +comparing the fixed caller signature and the dynamic callee signature and trapping +if there is a mismatch. Since the callee may be in a different module which +necessarily has a separate [types section](BinaryEncoding.md#type-section), and +thus index space of types, the signature match must compare the underlying +[`func_type`](https://github.com/WebAssembly/spec/blob/master/ml-proto/spec/types.ml#L5). Multiple return value calls will be possible, though possibly not in the MVP. The details of multiple-return-value calls needs clarification. Calling a From 666150c4c5ffa85d8316515bee2907a015ff7e0e Mon Sep 17 00:00:00 2001 From: Luke Wagner Date: Fri, 6 May 2016 10:32:39 -0500 Subject: [PATCH 05/22] Explicate elements section --- Modules.md | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/Modules.md b/Modules.md index 69c0e851..3e78d2f0 100644 --- a/Modules.md +++ b/Modules.md @@ -246,12 +246,18 @@ space, engines should allocate only the initial size and reallocate on demand. ## Elements section For function tables, the intial contents of the tables' elements are sentinel -values that throw if called. The *elements section* contains a possibly-empty -array of *element segments* which define the initial contents of fixed -`(offset, length)` ranges of a given table, specified by its -[table index](#index-spaces). The elements are specified by their -[function index](#index-spaces). This section is the table analogue of the -linear memory [data section](#data-section). +values that throw if called. The *elements section* allows a module to +initialize (at instantiation time) the elements of any imported or defined +table with any function in the module. This is symmetric to how the +[Data section](#data-section) allows a module to initialize the bytes +of any imported or defined memory. + +Specifically, the elements section contains a possibly-empty array of +*element segments*. Each element segment contains a +[table index](#index-spaces), indicating which table to initialize, +an *offset* (where in the table to start initializing) and then +an array of [function indices](#index-spaces) whose corresponding +functions will be stored into the table starting at the offset. ## Code section From 7d0a259ec9217bd8d94600d0e47a26da999876f4 Mon Sep 17 00:00:00 2001 From: Luke Wagner Date: Wed, 11 May 2016 17:34:23 -0500 Subject: [PATCH 06/22] Change support to allow/enable --- DynamicLinking.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/DynamicLinking.md b/DynamicLinking.md index 98300951..6d636b57 100644 --- a/DynamicLinking.md +++ b/DynamicLinking.md @@ -1,7 +1,7 @@ # Dynamic linking -WebAssembly supports load-time and run-time (`dlopen`) dynamic linking in the -MVP. This is achieved by having multiple [instantiated modules](Modules.md) +WebAssembly allows load-time and run-time (`dlopen`) dynamic linking in the +MVP. This is enabled by having multiple [instantiated modules](Modules.md) share functions, [linear memory](AstSemantics.md#linear-memory) and [tables](AstSemantics.md#tables) using module [imports](Modules.md#imports) and [exports](Modules.md#exports). In particular, since all (non-local) state From 7ad54dc47fefaa7aaed1fcd44ca555e1bf424d2d Mon Sep 17 00:00:00 2001 From: Luke Wagner Date: Sat, 28 May 2016 16:25:05 -0500 Subject: [PATCH 07/22] Remove table/memory indices add global imports/exports --- AstSemantics.md | 161 ++++++++++++++----------- DynamicLinking.md | 63 ++++------ FutureFeatures.md | 43 +++++-- JS.md | 239 ++++++++++++++++--------------------- Modules.md | 295 ++++++++++++++++++++++++++++++++-------------- Web.md | 2 +- 6 files changed, 459 insertions(+), 344 deletions(-) diff --git a/AstSemantics.md b/AstSemantics.md index 0143e3d0..12bdb6e4 100644 --- a/AstSemantics.md +++ b/AstSemantics.md @@ -80,19 +80,14 @@ operators. ## Linear Memory -A WebAssembly [instance](Modules.md) contains zero or more *linear memories*. -A linear memory is is a contiguous, byte-addressable range of memory spanning -from offset `0` and extending up to a varying *memory size*. -This size always is a multiple of the WebAssembly page size, -which is fixed to 64KiB (though large page support may be added in an opt-in -manner in the [future](FutureFeatures.md#large-page-support)). -The initial state of a linear memory is specified by the -[module](Modules.md#linear-memory-section), and can be dynamically grown by -the [`grow_memory`](AstSemantics.md#resizing) operator. - -A module can define or import multiple linear memories and each memory access -operator statically specifies which linear memory to operate on with a -[linear memory index](Modules.md#index-spaces) immediate. +A *linear memory* is a contiguous, byte-addressable range of memory spanning +from offset `0` and extending up to a varying *memory size*. This size is always +a multiple of the WebAssembly page size, which is fixed to 64KiB (though large +page support may be added in an opt-in manner in the +[future](FutureFeatures.md#large-page-support)). The initial state of a linear +memory is defined by the module's [linear memory](Modules.md#linear-memory-section) and +[data](Modules.md#data-section) sections. The memory size can be dynamically +increased by the [`grow_memory`](AstSemantics.md#resizing) operator. A linear memory can be considered to be an untyped array of bytes, and it is unspecified how embedders map this array into their process' own [virtual @@ -102,6 +97,18 @@ variables, or other process memory. [virtual memory]: https://en.wikipedia.org/wiki/Virtual_memory +Every WebAssembly [instance](Modules.md) has one specially-designated *default* +linear memory which is the linear memory accessed by all the +[memory operators below](#linear-memory-access). In the MVP, there are *only* +default linear memories but [new memory operators](FutureFeatures.md#multiple-tables-and-memories) +will be added after the MVP which can also access non-default memories. + +Linear memories (default or otherwise) can either be [imported](Modules.md#imports) +or [defined inside the module](Modules.md#linear-memory-section), with defaultness +indicated by a flag on the import or definition. After import or definition, +there is no difference when accessing a linear memory whether it was imported or +defined internally. + In the MVP, linear memory cannot be shared between threads of execution. The addition of [threads](PostMVP.md#threads) will allow this. @@ -112,9 +119,6 @@ Integer loads can specify a *storage size* which is smaller than the result type well as a signedness which determines whether the bytes are sign- or zero- extended into the result type. -Each `load` and `store` specifies the [linear memory index](Modules.md#index-spaces) -to operate on with an unsigned integer immediate. - * `i32.load8_s`: load 1 byte and sign-extend i8 to i32 * `i32.load8_u`: load 1 byte and zero-extend i8 to i32 * `i32.load16_s`: load 2 bytes and sign-extend i16 to i32 @@ -147,6 +151,8 @@ size in which case integer wrapping is implied. In addition to storing to memory, store instructions produce a value which is their `value` input operand before wrapping. +The above operators operate on the [default linear memory](#linear-memory). + ### Addressing Each linear memory access operator has an address operand and an unsigned @@ -207,25 +213,21 @@ Out of bounds accesses trap. ### Resizing In the MVP, linear memory can be resized by a `grow_memory` operator. The -`grow_memory` operator specifies which memory to resize with a -[linear memory index](Modules.md#index-spaces) immediate. The dynamic -operand to `grow_memory` is in units of the WebAssembly page size, +operand to this operator is in units of the WebAssembly page size, which is defined to be 64KiB (though large page support may be added in the [future](FutureFeatures.md#large-page-support)). * `grow_memory` : grow linear memory by a given unsigned delta of pages. Return the previous memory size in units of pages or -1 on failure. -When a maximum memory size is declared in the [memory section](Modules.md#linear-memory-sections), +When a linear memory has a declared [maximum memory size](Modules.md#linear-memory-section), `grow_memory` must fail if it would grow past the maximum. However, `grow_memory` may still fail before the maximum if it was not possible to reserve the space up front or if enabling the reserved memory fails. When there is no maximum memory size declared, `grow_memory` is expected to perform a system allocation which may fail. -The current size of a given linear memory (specified by a -[linear memory index](Modules.md#index-spaces)) can be queried by the following -operator: +The current size of the linear memory can be queried by the following operator: * `current_memory` : return the current memory size in units of pages. @@ -240,50 +242,49 @@ operator may be added. However, due to normal fragmentation, applications are instead expected release unused physical pages from the working set using the [`discard`](FutureFeatures.md#finer-grained-control-over-memory) future feature. -## Tables +The above operators operate on the [default linear memory](#linear-memory). + +## Table A *table* is similar to a linear memory whose elements, instead of being bytes, are opaque values of a particular *table element type*. This allows the table to -contain values like GC references, raw OS handles, or native pointers that are +contain values—like GC references, raw OS handles, or native pointers—that are accessed by WebAssembly code indirectly through an integer index. This feature bridges the gap between low-level, untrusted linear memory and high-level opaque handles/references at the cost of a bounds-checked table indirection. The table's element type dynamically constrains the type of elements stored in the table and allows engines to avoid some type checks on table use. When -tables are mutated, any stored value must match the element type (by one of +tables are mutated, any stored value must match the element type by either a static validation constraint, trapping dynamic type validation check, or dynamic -coercion). - -In the MVP, the set of operations and types for tables is limited: -* tables may only be accessed via [`call_table`](#calls); -* the only allowed table element types are "function" and - "function with signature `X`"; +coercion (depending on where the mutation occurs). + +Every WebAssembly [instance](Modules.md) has one specially-designated *default* +table which is indexed by [`call_indirect`](#calls) and other future +table operators. Tables can either be [imported](Modules.md#imports) or +[defined inside the module](Modules.md#table-section), with defaultness +indicated by a flag on the import or definition. After import or definition, +there is no difference when calling into a table whether it was imported or +defined internally. + +In the MVP, the primary purpose of tables is to implement indirect function +calls in C/C++ using an integer index as the pointer-to-function and the table +to hold the array of indirectly-callable functions. Thus, in the MVP: +* tables may only be accessed from WebAssembly code via [`call_indirect`](#calls); +* the only allowed table element type is "function" (no signature); * tables may not be directly mutated or resized from WebAssembly code; - initially this must be done through the host environment (e.g., the - the `WebAssembly` [JavaScript API](JS.md#webassemblytable-objects). - -These restrictions should be relaxed in the -[future](FutureFeatures.md#more-table-operators-and-types). In the MVP, -the primary purpose of tables is to implement indirect function calls -in C/C++, using an integer index as the pointer-to-function and the table -to hold the array of indirectly-callable functions. The compiler may either -choose to group functions by signature (into N tables-with-signatures) -or to put all functions into the same table (with no signature). The former -strategy allows the engine to eliminate dynamic signature-mismatch checks while -the latter strategy gives each function a unique index (which may be necessary -for C/C++ compatibility) and admits simpler and potentially more space-efficient -[dynamic linking](DynamicLinking.md). Toolchains can also create many -minimally-sized tables to implement a variety of [CFI schemes](http://clang.llvm.org/docs/ControlFlowIntegrity.html#forward-edge-cfi-for-virtual-calls) -that constrain the set of valid call targets for a given `call_table`. + this can only be done through the host environment (e.g., the + the `WebAssembly` [JavaScript API](JS.md#webassemblytable-objects)). +These restrictions will be relaxed in the +[future](FutureFeatures.md#more-table-operators-and-types). ## Local variables -Each function has a fixed, pre-declared number of local variables which occupy a single +Each function has a fixed, pre-declared number of *local variables* which occupy a single index space local to the function. Parameters are addressed as local variables. Local variables do not have addresses and are not aliased by linear memory. Local -variables have value types and are initialized to the appropriate zero value for their +variables have [value types](#types) and are initialized to the appropriate zero value for their type at the beginning of the function, except parameters which are initialized to the values of the arguments passed to the function. @@ -294,6 +295,32 @@ The details of index space for local variables and their types will be further c e.g. whether locals with type `i32` and `i64` must be contiguous and separate from others, etc. +## Global variables + +A *global variable* stores a single value of a fixed [value type](#types) and may be +declared either *mutable* or *immutable*. This provides WebAssembly with memory +locations that are disjoint from any [linear memory](#linear-memory) and thus +cannot be arbitrarily aliased as bits. + +Global variables are accessed via an integer index into the module-defined +[global index space](Modules.md#global-index-space). Global variables can +either be [imported](Modules.md#imports) or [defined inside the module](Modules.md#global-section). +After import or definition, there is no difference when calling into a table. + + * `get_global`: get the current value of a global variable + * `set_global`: set the current value of a global variable + +It is a validation error for a `set_global` to index an immutable global variable. + +In the MVP, the primary use case of global variables is to represent +instantiation-time immutable values as a necessary building block of +[dynamic linking](DynamicLinking.md). + +After the MVP, when [reference types](GC.md) are added to the set of [value types](#types), +global variables will be necessary to allow sharing reference types between +[threads](PostMVP.md#threads) since shared linear memory cannot load or store +references. + ## Control flow structures WebAssembly offers basic structured control flow with the following constructs. @@ -360,32 +387,30 @@ explicit accesses to linear memory. In the MVP, the length of the return types sequence may only be 0 or 1. This restriction may be lifted in the future. -Direct calls to a function specify the callee by index into the -[function index space](Modules.md#index-spaces). +Direct calls to a function specify the callee by an index into the +[function index space](Modules.md#function-index-space). * `call`: call function directly A direct call to a function with a mismatched signature is a module verification error. Indirect calls to a function indicate the callee with an `i32` index into -the [table](#tables) specified by a [table index](BinaryEncoding.md#index-spaces) -immediate. The *expected* signature of the target function (specified by its -index in the [types section](BinaryEncoding.md#type-section)) is given as a -second immediate. - - * `call_table`: call function element in a table - -The specified [table](#tables) must have either a "function" or "function with -signature `X`" element type (in the MVP, these are the only two -possibilities, but in the future, there may be other kinds of tables). -If the table element type has a signature, it must match the signature of the -call. Otherwise, the signature is checked each time the `call_table` is executed, -comparing the fixed caller signature and the dynamic callee signature and trapping -if there is a mismatch. Since the callee may be in a different module which -necessarily has a separate [types section](BinaryEncoding.md#type-section), and -thus index space of types, the signature match must compare the underlying +a [table](#table). The *expected* signature of the target function (specified +by its index in the [types section](BinaryEncoding.md#type-section)) is given as +a second immediate. + + * `call_indirect`: call function indirectly + +Unlike `call`, which checks that the caller and callee signatures match +statically as part of validation, `call_indirect` checks for signature match +*dynamically*, comparing the caller's expected signature with the callee function's +signature and and trapping if there is a mismatch. Since the callee may be in a +different module which necessarily has a separate [types section](BinaryEncoding.md#type-section), +and thus index space of types, the signature match must compare the underlying [`func_type`](https://github.com/WebAssembly/spec/blob/master/ml-proto/spec/types.ml#L5). +In the MVP, the single `call_indirect` operator accesses the [default table](#table). + Multiple return value calls will be possible, though possibly not in the MVP. The details of multiple-return-value calls needs clarification. Calling a function that returns multiple values will likely have to be a statement that diff --git a/DynamicLinking.md b/DynamicLinking.md index 6d636b57..70658629 100644 --- a/DynamicLinking.md +++ b/DynamicLinking.md @@ -2,12 +2,12 @@ WebAssembly allows load-time and run-time (`dlopen`) dynamic linking in the MVP. This is enabled by having multiple [instantiated modules](Modules.md) -share functions, [linear memory](AstSemantics.md#linear-memory) and -[tables](AstSemantics.md#tables) using module [imports](Modules.md#imports) -and [exports](Modules.md#exports). In particular, since all (non-local) state -that a module can access can be imported and exported and thus shared between -separate modules' instances, toolchains have the building blocks to implement -dynamic loaders. +share functions, [linear memories](AstSemantics.md#linear-memory), +[tables](AstSemantics.md#table) and [constants](AstSemantics.md#constants) +using module [imports](Modules.md#imports) and [exports](Modules.md#exports). In +particular, since all (non-local) state that a module can access can be imported +and exported and thus shared between separate modules' instances, toolchains +have the building blocks to implement dynamic loaders. Since the manner in which modules are loaded and instantiated is defined by the host environment (e.g., the [JavaScript API](JS.md)), dynamic linking requires @@ -29,30 +29,32 @@ native DSOs/DLLs: # define IMPORT __attribute__ ((visibility ("default"))) #endif -typedef void (*PF)(); +typedef void (**PF)(); IMPORT PF imp(); -EXPORT void exp() { imp()(); } +EXPORT void exp() { (*imp())(); } ``` -This code would generate a WebAssembly module with imports for: +This code would, at a minimum, generate a WebAssembly module with imports for: * the function `imp` -* the table(s) used for indirectly-callable functions -* the linear memory used to implement the heap +* the heap used to perfom the load, when dereferencing the return value of `imp` +* the table used to perform the pointer-to-function call and exports for: * the function `exp` -* the imported table(s) and linear memory (so that other modules may - import this one) -The host-specific loader can then simply connect the imports -and exports at instantantiation-time. +A more realistic module using libc would have more imports including: +* an immutable `i32` global import for the offset in linear memory to place + global [data segments](Modules.md#data-section) and later use as a constant + base address when loading and storing from globals +* an immutable `i32` global import for the offset into the indirect function + table at which to place the modules' indirectly called functions and later + compute their indices for address-of -One extra detail is what -to use as the [module name](Modules.md#imports) for imports (since -WebAssembly has a two-level namespace). One option is to have a single default -module name for all C/C++ imports/exports (which then allows the toolchain to -put implementation-internal names in a separate module, avoiding the need for -`__`-prefix conventions). +One extra detail is what to use as the [module name](Modules.md#imports) for +imports (since WebAssembly has a two-level namespace). One option is to have a +single default module name for all C/C++ imports/exports (which then allows the +toolchain to put implementation-internal names in a separate namespace, avoiding +the need for `__`-prefix conventions). To implement run-time dynamic linking (e.g., `dlopen` and `dlsym`): * `dlopen` would compile and instantiate a new module, storing the compiled @@ -70,7 +72,7 @@ function-pointer return value of `dlsym`. More complicated dynamic linking functionality (e.g., interposition, weak symbols, etc) can be simulated efficiently by assigning a function table -index to each weak/mutable symbol, calling the symbol via `call_table` on that +index to each weak/mutable symbol, calling the symbol via `call_indirect` on that index, and mutating the underlying element as needed. After the MVP, we would like to standardize a single [ABI][] per source @@ -83,20 +85,3 @@ runtimes will be ABI agnostic, so it will be possible to use a non-standard ABI for specialized purposes. [ABI]: https://en.wikipedia.org/wiki/Application_binary_interface - -TODO: - -The [data](Modules.md#data-section) and [elements](Modules.md#elements-section) -segments currently have fixed integer offsets which is fundamentally -incompatible with general dynamic loading. Without extension, dynamic linking -implementations would either need to statically allocate global data and -elements (which isn't possible in the general case) or use an entirely different -(and less efficient) scheme that doesn't use these sections. - -A better fix (before or after the MVP) is to allow importing/exporting constant -primitive values that can be used as the offsets of data/element segments -and generally read in WebAssembly code through some new `get_const` operator. -This would essentially be equivalent to the -[Global Offset Table](https://en.wikipedia.org/wiki/Position-independent_code) -used in native implementations of position-independent code although an -engine could use direct code patching of the values as well. diff --git a/FutureFeatures.md b/FutureFeatures.md index 99dc38c4..814b502f 100644 --- a/FutureFeatures.md +++ b/FutureFeatures.md @@ -416,18 +416,45 @@ of WebAssembly in browsers: custom compression (on top of the spec-defined binary format, under generic HTTP `Content-Encoding` compression). +## Multiple Tables and Memories + +In the MVP, there can only be (at most one) default table/memory and there are +only operators for accessing the default table/memory. + +After the MVP and after [GC reference types](GC.md) have been added, the default +limitation can be relaxed so that any number of tables and memories could be +imported or internally defined and memories/tables could be passed around as +parameters, return values and locals. New variants of `load`, `store` +and `call_indirect` would then be added which took an additional memory/table +reference operand. + +To access an imported or internally-defined non-default table or memory, a +new `address_of` operator could be added which, given an immediate index into +the module's [definition index space](Modules.md#definition-index-space), would +return a first-class reference to that definition. Beyond tables and memories, +this could also be used for function definitions to get a reference to a function +(which, since opaque, could be implemented as a raw function pointer). + ## More Table Operators and Types -In the MVP, [tables](AstSemantics.md#tables) can only store functions -and can only be called. The host-environment can do much more (see, e.g., -the [JavaScript `WebAssembly.Table` API](JS.md#webassemblytable-objects)), -but it would be useful to be able to do everything from within WebAssembly -(so, e.g., it was possible to have a dynamic loader written in WebAssembly). As -a prerequisite, WebAssembly would need first-class support for -[GC references](GC.md) in expressions and locals. Given that, the following could be -added: +In the MVP, WebAssembly has limited functionality for operating on +[tables](AstSemantics.md#table) and the host-environment can do much more (e.g., +see [JavaScript's `WebAssembly.Table` API](JS.md#webassemblytable-objects)). +It would be useful to be able to do everything from within WebAssembly so, e.g., +it was possible to write a WebAssembly dynamic loader in WebAssembly. As a +prerequisite, WebAssembly would need first-class support for +[GC references](GC.md) in expressions and locals. Given that, the following +could be added: * `get_table`/`set_table`: get or set the table element at a given dynamic index; the got/set value would have a GC reference type * `grow_table`: grow the current table (up to the optional maximum), similar to `grow_memory` * `current_table_length`: like `current_memory`. + +Additionally, in the MVP, tables' an only store untyped functions. This could be +relaxed to: +* functions with a particular signature, allowing code generators to use + multiple homogeneously-typed function tables to replace the implied dynamic + signature check of a heterogeneous table with a static validation check +* any other specific GC reference type, effectively allowing WebAssembly code + to implement a variety of rooting API schemes diff --git a/JS.md b/JS.md index 03bc97ca..c99a0f7c 100644 --- a/JS.md +++ b/JS.md @@ -13,7 +13,6 @@ basically equivalent to `new WebAssembly.Instance(new WebAssembly.Module(bytes), imports)` as defined below and will be removed at some point in the future.* - ## The `WebAssembly` object The `WebAssembly` object is the initial value of the `WebAssembly` property of @@ -21,7 +20,6 @@ the global object. Like the `Math` and `JSON` objects, the `WebAssembly` object is a plain JS object (not a constructor or function) that acts like a namespace and has the following properties: - ### Constructor Properties of the `WebAssembly` object The following intrinsic objects are added: @@ -35,10 +33,8 @@ The following intrinsic objects are added: * `WebAssembly.RuntimeError` : a [NativeError](http://tc39.github.io/ecma262/#sec-nativeerror-object-structure) which indicates an error while running WebAssembly code - ### Function Properties of the `WebAssembly` object - #### `WebAssembly.compile` The `compile` function has the signature: @@ -65,7 +61,6 @@ In the [future](FutureFeatures.md#streaming-compilation), this function can be extended to accept a [stream](https://streams.spec.whatwg.org), thereby enabling asynchronous, background, streaming compilation. - ## `WebAssembly.Module` Objects A `WebAssembly.Module` object represents the stateless result of compiling a @@ -73,7 +68,6 @@ WebAssembly binary-format module and contains one internal slot: * [[Module]] : an [`Ast.module`](https://github.com/WebAssembly/spec/blob/master/ml-proto/spec/ast.ml#L208) which is the spec definition of a validated module AST - ### `WebAssembly.Module` Constructor The `WebAssembly.Module` constructor has the signature: @@ -97,7 +91,6 @@ Otherwise, this function performs synchronous compilation of the `BufferSource`: the validated `Ast.module`. * On failure, a new `WebAssembly.CompileError` is thrown. - ### Structured Clone of a `WebAssembly.Module` A `WebAssembly.Module` is a @@ -114,7 +107,6 @@ Given the above engine optimizations, structured cloning provides developers explicit control over both compiled-code caching and cross-window/worker code sharing. - ## `WebAssembly.Instance` Objects A `WebAssembly.Instance` object represents the instantiation of a @@ -128,7 +120,6 @@ as well as one plain data property (configurable, writable, enumerable) added by the constructor: * exports : a [Module Namespace Object](http://tc39.github.io/ecma262/#sec-module-namespace-objects) - ### `WebAssembly.Instance` Constructor The `WebAssembly.Instance` constructor has the signature: @@ -148,93 +139,78 @@ not Object, a `TypeError` is thrown. If the list of [`module.imports`](https://github.com/WebAssembly/spec/blob/master/ml-proto/spec/ast.ml#L215) is not empty and `Type(importObject)` is not Object, a `TypeError` is thrown. -Let `importedFunctions`, `importedMemories` and `importedTables` be initially-empty -lists of JS functions, `WebAssembly.Memory` objects, and `WebAssembly.Table` -objects, respectively. +Let `imports` be an initially-empty [`import list`](https://github.com/WebAssembly/spec/blob/master/ml-proto/spec/eval.mli#L3) +(assuming the ML spec `Eval.import` type has been extended to be a union of: +* a function [`value list -> value option`](https://github.com/WebAssembly/spec/blob/master/ml-proto/spec/eval.mli#L3) +* a [`Memory.memory`](https://github.com/WebAssembly/spec/blob/master/ml-proto/spec/memory.mli#L1) +* a [`Table.table`](#webassemblytable-objects) +* a [`Values.value`](https://github.com/WebAssembly/spec/blob/master/ml-proto/spec/values.ml#L9) For each [`import`](https://github.com/WebAssembly/spec/blob/master/ml-proto/spec/kernel.ml#L135) -`i` in `module.imports`: +`i` in `module.imports` (assuming the ML spec `import` has been extended to have +function, global, memory and table imports): * Let `v` be the resultant value of performing [`Get`](http://tc39.github.io/ecma262/#sec-get-o-p)(`importObject`, [`i.module_name`](https://github.com/WebAssembly/spec/blob/master/ml-proto/spec/kernel.ml#L139)). * If [`i.export_name`](https://github.com/WebAssembly/spec/blob/master/ml-proto/spec/kernel.ml#L140) is not the empty string: * If `Type(v)` is not Object, throw a `TypeError`. * Let `v` instead be the value of performing [`Get`](http://tc39.github.io/ecma262/#sec-get-o-p)(`v`, `i.export_name`) -* If `i` is a function export: +* If `i` is a function import: * If `IsCallable(v)` is `false`, throw a `TypeError`. - * Otherwise, append `v` to `importedFunctions`. -* If `i` is a memory export: + * Otherwise, append an anonymous function to `imports` + which calls `v` by coercing WebAssembly arguments to JavaScript arguments + via [`ToJSValue`](#tojsvalue) and returns the result by coercing + via [`ToWebAssemblyValue`](#towebassemblyvalue). + * Otherwise, append the function `v` to `imports` +* If `i` is a global import: + * If `i` is not an immutable global, throw a `TypeError`. + * Append [`ToWebAssemblyValue`](#towebassemblyvalue)`(v)` to `imports`. +* If `i` is a memory import: * If `v` is not a [`WebAssembly.Memory` object](#webassemblymemory-objects), throw a `TypeError`. - * Otherwise, append `v` to `importedMemories`. -* If `i` is a table export: + * Otherwise, append `v.[[Memory]]` to `imports`. +* Otherwise (`i` is a table import): * If `v` is not a [`WebAssembly.Table` object](#webassemblytable-objects), throw a `TypeError`. - * Otherwise, append `v` to `importedTables`. - -(Note: the ML spec currently doesn't have multiple kinds of exports; we assume here -it will be extended in the future.) + * Otherwise, append `v.[[Table]]` to `imports`. Let `instance` be the result of evaluating [`Eval.init`](https://github.com/WebAssembly/spec/blob/master/ml-proto/spec/eval.ml#L319) -with arguments `module`, `importedFunctions`, `importedMemories` and -`importedTables`. Note: this synchronously executes the -[`start`](Modules.md#module-start-function) function, if present. - -(Note: in the ML spec, `Eval.init` currently doesn't accept imported memories or -tables; we assume here it will be extended in the future.) - -Let `exports` be an initially-empty list of (string, JS value) pairs. - -Let `exportedFunctions` be a map whose initial contents are mappings from -the array indices of `importedFunctions` to `importedFunctions[i]`. - -Let `exportedMemories` be a map whose initial contents are mappings from -the array indices of `exportedMemories` to `exportedMemories[i]`. - -Let `exportedTables` be a map whose initial contents are mappings from -the array indices of `exportedTables` to `exportedTables[i]`. - -For each [export](https://github.com/WebAssembly/spec/blob/master/ml-proto/spec/kernel.ml#L128) -`e` in `module.exports`: -* If `e` is a function export: - * Let `i` be the function index of `e`. - * If `i` is not already present in `exportedFunctions`: - * Let `v` be the result of creating a new [Exported Function](#exported-function-exotic-objects) - Exotic Object (given `instance` and `i`). - * Add a mapping from `i` to `v` in `exportedFunctions`. - * Otherwise, let `v` be `exportedFunctions[i]`. -* If `e` is a memory export: - * Let `i` be the memory index of `e`. - * If `i` is not already present in `exportedMemories`: - * Let `memory` be the `i`th linear memory in the - [memory index space](Modules.md#index-spaces) of `instance`. - * Let `v` be the result of [`CreateMemoryObject`](#creatememoryobject)`(memory)`. - * Add a mapping from `i` to `v` in `exportedMemories`. - * Otherwise, let `v` be `exportedMemories[i]`. -* If `e` is a table export: - * Let `i` be the table index of `e`. - * If `i` is not already present in `exportedTables`: - * Let `table` be the `i`th table in the - [table index space](Modules.md#index-spaces) of `instance`. - * Let `values` be an initially-empty list of Function Objects. - * For each function index `fi` in `table`: - * If `fi` is `None`, then append `null` to `values`. - * Otherwise, if `fi` is not already present in `exportedFunctions`: - * Let `f` be the result of creating a new - [Exported Function](#exported-function-exotic-objects) - Exotic Object (given `instance` and `fi`). - * Add a mapping from `fi` to `f` in `exportedFunctions`. - * Append `f` to `values`. - * Otherwise, append `exportedFunctions[i]` to `values`. - * Let `v` be a new `WebAssembly.Table` instance with [[Table]] +with arguments `module` and `imports`. +Note: this synchronously executes the [`start`](Modules.md#module-start-function) +function, if present. + +Let `exports` be a list of (string, JS value) pairs that is mapped from +`module.exports` as follows (assuming the ML spec +[`export`](https://github.com/WebAssembly/spec/blob/master/ml-proto/spec/kernel.ml#L128) +has been modified so that each export simply has a `name` and `index` (into +the module's [definition index space](Modules.md#definition-index-space)): +* If `export.index` refers to an imported definition, then simply re-export the + imported JS value. +* Otherwise (an internal definition): + * If `export.index` refers to a function definition, then export + an [Exported Function Exotic Object](#exported-function-exotic-objects), + reusing an existing object if one exists for the given function definition, + otherwise creating a new object. + * If `export.index` refers to a global definition: + * If the global is not immutable, then throw a `TypeError`. + * Let `v` be the global variable's initialized value. + * Otherwise, export [`ToJSValue`](#tojsvalue)`(v)`. + * If `export.index` refers to a memory definition, then export a + `WebAssembly.Memory` object, reusing an existing object if one exists for + the given memory definition, otherwise creating a new object via + [`CreateMemoryObject`](#creatememoryobject). + * Otherwise (`export.index` refers to a table definition), export a + `WebAssembly.Table` object, reusing an existing object if one exists for + the given table definition, otherwise creating a new object via: + * Let `values` be a list of JS values that is mapped from the table's + elements as follows: + * sentinel values (which throw if called) are given the value `null` + * non-sentinel values are given an [Exported Function Exotic Objects](#exported-function-exotic-objects), + reusing an existing object if one exists for the given function, + otherwise creating a new one. + * Create a new `WebAssembly.Table` instance with [[Table]] set to `table` and [[Values]] set to `values`. - * Add a mapping from `i` to `v` in `exportedTables`. - * Otherwise, let `v` be `exportedTables[i]`. -* Append the pair (`e.name`, `v`) to `exports` - -(Note: the ML spec currently doesn't have table exports or give all exports an -index; we assume here it will be extended in the future.) Let `moduleRecord` be a new [WebAssembly Module Record](#webassembly-module-record) (given `exports`). @@ -247,7 +223,6 @@ Set `moduleRecord.[[Namespace]]` to `moduleNamespace`. Return a new `WebAssembly.Instance` object setting `[[Instance]]` to `instance` and `exports` to `moduleNamespace`. - ### WebAssembly Module Record [Abstract Module Record](http://tc39.github.io/ecma262/#sec-abstract-module-records) @@ -270,7 +245,6 @@ More work is needed to flesh out the precise spec interaction here, but the basi idea is to create a [Module Environment Record](http://tc39.github.io/ecma262/#sec-module-environment-records) from `exports` as the [[Environment]] of a new WebAssembly Module Record. - ## Exported Function Exotic Objects Functions exported by WebAssembly modules are reflected in JS via a new kind @@ -280,7 +254,7 @@ Like [Bound Function](http://tc39.github.io/ecma262/#sec-bound-function-exotic-o Exported Functions do not have the normal function internal slots but instead have: * [[Instance]] : the [`Eval.instance`](https://github.com/WebAssembly/spec/blob/master/ml-proto/spec/eval.ml#L15) containing the exported function - * [[FunctionIndex]] : the index of the function inside the module + * [[FunctionIndex]] : an index into the module's [function index space](Modules.md#function-index-space) as well as the internal slots required of all builtin functions: * [[Prototype]] : [%FunctionPrototype%](http://tc39.github.io/ecma262/#sec-well-known-intrinsic-objects) @@ -296,25 +270,14 @@ WebAssembly Exported Functions have a `[[Call]](this, argValues)` method defined * Let `argTypes` be the list of value types defined by the signature of [[FunctionIndex]]. * Let `args` be an empty list of coerced values. * For each value type `t` in `argTypes` and value `v` in `argValues`: - * Append to `args` `v` coerced to `t` as follows: - * coerce `v` to `i32` via [`ToInt32(v)`](http://tc39.github.io/ecma262/#sec-toint32) - * throw a `TypeError` if `t` is `i64` - * coerce `v` to `f32` by first applying [`ToNumber(v)`](http://tc39.github.io/ecma262/#sec-tonumber) - and then converting the resulting IEEE754 64-bit double to a 32-bit float using `roundTiesToEven` - * coerce `v` to `f64` via [`ToNumber(v)`](http://tc39.github.io/ecma262/#sec-tonumber) - * Perform [`Eval.invoke`](https://github.com/WebAssembly/spec/blob/master/ml-proto/spec/eval.ml#L327) + * Append [`ToWebAssemblyValue`](#towebassemblyvalue)`(v)` to `args`. + * Let `ret` be the result of calling [`Eval.invoke`](https://github.com/WebAssembly/spec/blob/master/ml-proto/spec/eval.ml#L327) passing [[Instance]], [[FunctionIndex]], and `args`. - * Coerce the result of `Eval.invoke` as follows: - * if the return value is `None`, return `undefined` - * interpret `i32` as a signed integer and convert that integer to a Number value - * throw a `TypeError` if returning an `i64` - * return `f32`/`f64` as Number values, possibly performing - [canonicalization of NaNs](http://tc39.github.io/ecma262/#sec-setvalueinbuffer) + * Return [`ToJSValue`](#tojsvalue)`(ret)`. Exported Functions do not have a [[Construct]] method and thus it is not possible to call one with the `new` operator. - ## `WebAssembly.Memory` Objects A `WebAssembly.Memory` object contains a single [linear memory](AstSemantics.md#linear-memory) @@ -324,7 +287,6 @@ which can be simultaneously referenced by multiple `Instance` objects. Each * [[BufferObject]] : the current `ArrayBuffer` whose [[ArrayBufferByteLength]] matches the current byte length of [[Memory]] - ### `WebAssembly.Memory` Constructor The `WebAssembly.Memory` constructor has the signature: @@ -352,7 +314,6 @@ assume here it will be extended in the future.) Return the result of [`CreateMemoryObject`](#creatememoryobject)(`memory`). - ### CreateMemoryObject Given a [`Memory.memory`](https://github.com/WebAssembly/spec/blob/master/ml-proto/spec/memory.mli) @@ -367,7 +328,6 @@ is set to the byte length of `m`. Return a new `WebAssembly.Memory` instance with [[Memory]] set to `m` and [[BufferObject]] set to `buffer`. - ### `WebAssembly.Memory.prototype.grow` Let `M` be the `this` value. If `M` is not a `WebAssembly.Memory`, @@ -386,7 +346,6 @@ aliases `M.[[Memory]]` and whose [[[ArrayBufferByteLength]]](http://tc39.github.io/ecma262/#sec-properties-of-the-arraybuffer-prototype-object) is set to the new byte length of `M.[[Memory]]`. - ### `WebAssembly.Memory.prototype.buffer` This is an accessor property whose [[Set]] is Undefined and whose [[Get]] @@ -395,15 +354,18 @@ accessor function performs the following steps: If `this` is not a `WebAssembly.Memory`, a `TypeError` is thrown. Otherwise return `M.[[BufferObject]]`. - ## `WebAssembly.Table` Objects -A `WebAssembly.Table` object contains a single [table](AstSemantics.md#tables) +A `WebAssembly.Table` object contains a single [table](AstSemantics.md#table) which can be simultaneously referenced by multiple `Instance` objects. Each -`Table` object has one internal slot: - * [[Table]] : a [`Table.table`](https://github.com/WebAssembly/spec/blob/master/ml-proto/spec/table.ml) +`Table` object has two internal slots: + * [[Table]] : a [`Table.table`](https://github.com/WebAssembly/spec/blob/master/ml-proto/spec/table.mli) * [[Values]] : an array whose elements are either `null` or Function Objects +(Note: the ML spec currently represents tables as a single `int list` of +function indices; we assume here it will be extended in the future with +a more general `Table` similar to +[`Memory`](https://github.com/WebAssembly/spec/blob/master/ml-proto/spec/memory.mli).) ### `WebAssembly.Table` Constructor @@ -416,7 +378,11 @@ constructor cannot be called as a function without `new`). If `Type(tableDescriptor)` is not Object, a `TypeError` is thrown. -Let `element` be the result of [ToTypeDescriptor](#totypedescriptor)([`Get`](http://tc39.github.io/ecma262/#sec-get-o-p)(`tableDescriptor`, `"element"`)). +Let `element` be the result of calling [`Get`](http://tc39.github.io/ecma262/#sec-get-o-p)(`tableDescriptor`, `"element"`). +If `element` is not the string `"function"`, a `TypeError` is thrown. +(Note: this check is intended to be relaxed in the +[future](FutureFeatures.md#more-table-operators-and-types) to allow different +elemtn types.) Let `initial` be [`ToInteger`](http://tc39.github.io/ecma262/#sec-tointeger)([`Get`](http://tc39.github.io/ecma262/#sec-get-o-p)(`tableDescriptor`, `"initial"`)). @@ -424,15 +390,8 @@ If [`HasProperty`](http://tc39.github.io/ecma262/#sec-hasproperty)(`"maximum"`), then let `maximum` be [`ToInteger`](http://tc39.github.io/ecma262/#sec-tointeger)([`Get`](http://tc39.github.io/ecma262/#sec-get-o-p)(`tableDescriptor`, `"maximum"`)). Otherwise, let `maximum` be None. -Let `table` be the result of calling `Table.create` given arguments `element`, -`initial` and `maximum`. - -(Note: the ML spec currently represents tables as a single `int list` of -function indices; we assume here it will be extended in the future with -a more general `Table` similar to -[`Memory`](https://github.com/WebAssembly/spec/blob/master/ml-proto/spec/memory.mli) -that also includes an `element` field of type -[`Type.func_type option`](https://github.com/WebAssembly/spec/blob/master/ml-proto/spec/types.ml#L5).) +Let `table` be the result of calling `Table.create` given arguments `initial` +and `maximum`. Let `values` be a new empty array of `initial` elements, all with value `null`. @@ -440,23 +399,6 @@ Let `values` be a new empty array of `initial` elements, all with value Return a new `WebAssemby.Table` instance with [[Table]] set to `table` and [[Values]] set to `values`. - -### ToTypeDescriptor - -This is an abstract function which, given a value `td`, returns either -[`Type.func_type`](https://github.com/WebAssembly/spec/blob/master/ml-proto/spec/types.ml#L5) -or `None`. (Note: when tables are generalized to hold non-functions in the -[future](FutureFeatures.md#more-table-operators-and-types), the return type of -`ToTypeDescriptor` would return a more general type.) - -TODO: pick the best way to describe a signature. Maybe some JSON-like object? -Maybe something more future-compatible with [Typed Objects](https://github.com/tschneidereit/typed-objects-explainer/)? - -If `td` is the string `"function"`, return `None`. - -Otherwise throw a `TypeError`. - - ### `WebAssembly.Table.prototype.grow` This method calls `Table.grow`, having performed @@ -467,7 +409,6 @@ On failure, a `WebAssembly.RuntimeError` is thrown. will be extended in the future to have a `grow` operation similar to [`Memory.grow`](https://github.com/WebAssembly/spec/blob/master/ml-proto/spec/memory.mli#L21).) - ### `WebAssembly.Table.prototype.get` This method has the following signature @@ -481,7 +422,6 @@ Let `i` be the result of [`ToInteger`](http://tc39.github.io/ecma262/#sec-tointe Return `T.[[Values]][i]`. - ### `WebAssembly.Table.prototype.set` This method has the following signature @@ -495,17 +435,38 @@ is thrown. If [`IsCallable`](http://tc39.github.io/ecma262/#sec-iscallable)(`value`) is false and `Type(value)` is not Null, throw a type error. -If `value` is a [Exported Function Exotic Object](#exported-function-exotic-objects) -and if `table.element` is not `None` and `T.element` does not -precisely match the signature of `v.[[FunctionIndex]]` in `v.[[Instance]].[[Module]]` -throw a `TypeError`. - Let `i` be the result of [`ToInteger`](http://tc39.github.io/ecma262/#sec-tointeger)(`index`). Set `T.[[Values]][i]` to `value`. Return Undefined. +## ToJSValue + +To coerce a [WebAssembly value](https://github.com/WebAssembly/spec/blob/master/ml-proto/spec/values.ml#L9) +to a JavaScript value: + +* given a WebAssembly `i32` is interpreted as a signed integer, converted (losslessly) to an + IEEE754 double and then returned as a JavaScript Number +* given a WebAssembly `i64`, throw a `TypeError` +* given a WebAssembly `f32` (single-precision IEEE754), convert (losslessly) to + a IEEE754 double, [possibly canonicalize NaN](http://tc39.github.io/ecma262/#sec-setvalueinbuffer), + and return as a JavaScript Number +* given a WebAssembly `f64`, [possibly canonicalize NaN](http://tc39.github.io/ecma262/#sec-setvalueinbuffer) + and return as a JavaScript Number + +If the WebAssembly value is optional, then given `None`, return JavaScript value +`undefined`. + +## ToWebAssemblyValue + +To coerce a JavaScript value to a given [WebAssembly value type](https://github.com/WebAssembly/spec/blob/master/ml-proto/spec/types.ml#L3), + +* coerce to `i32` via [`ToInt32(v)`](http://tc39.github.io/ecma262/#sec-toint32) +* for `i64`, throw a `TypeError` +* coerce to `f32` by first applying [`ToNumber(v)`](http://tc39.github.io/ecma262/#sec-tonumber) + and then converting the resulting IEEE754 64-bit double to a 32-bit float using `roundTiesToEven` +* coerce to `f64` via [`ToNumber(v)`](http://tc39.github.io/ecma262/#sec-tonumber) ## Sample API Usage @@ -538,4 +499,4 @@ fetch('demo.wasm').then(response => ## TODO * `WebAssembly.Module` `exports`/`imports` properties (reflection) -* JS API for cyclic imports +* JS API for cyclic imports (perhaps a Promise-returning `WebAssembly.instantiate`?) diff --git a/Modules.md b/Modules.md index 3e78d2f0..1ae483f6 100644 --- a/Modules.md +++ b/Modules.md @@ -2,43 +2,30 @@ The distributable, loadable, and executable unit of code in WebAssembly is called a **module**. At runtime, a module can be **instantiated** -to produce an **instance**, which is an immutable tuple referencing all -the state accessible to the running module. Multiple module instances can -access the same shared state which is the basis for +with a set of import values to produce an **instance**, which is an immutable +tuple referencing all the state accessible to the running module. Multiple +module instances can access the same shared state which is the basis for [dynamic linking](DynamicLinking.md) in WebAssembly. WebAssembly modules are also designed to [integrate with ES6 modules](#integration-with-es6-modules). -A module may contain the following sections: -* [imports](#imports) -* [exports](#exports) +A module contains the following sections: +* [import](#imports) +* [export](#exports) * [start](#module-start-function) -* [linear memory](#linear-memory-section) +* [global](#global-section) +* [memory](#linear-memory-section) * [data](#data-section) * [table](#table-section) * [elements](#elements-section) -* [code](#code-section) +* [function and code](#function-and-code-sections) -An instance contains: -* the code of the instantiated module; and -* a set of [index spaces](#index-spaces) of linear memories, tables and - functions that were imported or defined by the module. - -## Index Spaces - -Any number of functions, memories and tables can be both imported and -defined in a given module. Imports and definitions are merged into index -spaces (one for each of memories, tables and functions) which allows uniform -treatment of imports and definitions thereafter (via index). - -Specifically, the index space of functions is defined by first giving each of -the M function imports an index \[0, M\) (ordered by their sequence in the -imports section) and then giving each of the N function definitions an index -\[M, M+N\) (ordered by their sequence in the module). The memory and table -index spaces are defined in the same way. - -Thus, any use of a *function index* (e.g., a [`call`](AstSemantics.md#calls)) -can refer to either a function import or definition and similarly for -and *table index* or *memory index*. +A module also defines several *index spaces* which are statically indexed by +various operators and section fields in the module: +* the [definition index space](#definition-index-space) +* the [function index space](#function-index-space) +* the [global index space](#global-index-space) +* the [linear memory index space](#linear-memory-index-space) +* the [table index space](#table-index-space) ## Imports @@ -46,14 +33,16 @@ A module can declare a sequence of **imports** which are provided, at instantiation time, by the host environment. There are several kinds of imports: * **function imports**, which can be called inside the module by the [`call`](AstSemantics.md#calls) operator; +* **global imports**, which can be accessed inside the module by the + [global operators](AstSemantics.md#global-variables); * **linear memory imports**, which can be accessed inside the module by the [memory operators](AstSemantics.md#linear-memory); and * **table imports**, which can be accessed inside the module by - [call_table](AstSemantics.md#calls) and other + [call_indirect](AstSemantics.md#calls) and other table operators in the [future](FutureFeatures.md#more-table-operators-and-types). -In the future, other kinds of imports may be added. Imports are designed to +In the future, other kinds of imports will be added. Imports are designed to allow modules to share code and data while still allowing separate compilation and caching. @@ -71,22 +60,33 @@ However, if the imported function is a WebAssembly function, the host environment must raise an instantiation-time error if there is a signature mismatch. +A *global variable import* includes the *value type* and *mutability* +of the global variable. These fields have the same meaning as in the +[Global section](#global-section). + A *linear memory import* includes the same set of fields defined in the -[Linear Memory section](#linear-memory-section): *initial length* and -optional *maximum length*. The host environment must only allow imports -of WebAssembly linear memories that have initial length *greater-or-equal* than -the initial length declared in the import and that have maximum length -*less-or-equal* than the maximum length declared in the import. This ensures -that separate compilation can assume: memory accesses below the declared initial -length are always in-bounds, accesses above the declared maximum length are -always out-of-bounds and if initial equals maximum, the length is fixed. +[Linear Memory section](#linear-memory-section): *default flag*, *initial +length* and optional *maximum length*. The host environment must only allow +imports of WebAssembly linear memories that have initial length +*greater-or-equal* than the initial length declared in the import and that have +maximum length *less-or-equal* than the maximum length declared in the import. +This ensures that separate compilation can assume: memory accesses below the +declared initial length are always in-bounds, accesses above the declared +maximum length are always out-of-bounds and if initial equals maximum, the +length is fixed. If the default flag is set, the imported memory is used as +the [default memory](AstSemantics.md#linear-memory) and at most one linear +memory definition (import or internal) may have the default flag set. In the +MVP, it is a validation error not to set the default flag. A *table import* includes the same set of fields defined in the -[Table section](#table-section): *element type*, *initial length* -and optional *maximum length*. As with the linear memory section, -the host environment must ensure only WebAssembly tables are imported -with exactly-matching element type, greater-or-equal initial length, -and less-or-equal maximum length. +[Table section](#table-section): *default flag*, *element type*, *initial +length* and optional *maximum length*. As with the linear memory section, the +host environment must ensure only WebAssembly tables are imported with +exactly-matching element type, greater-or-equal initial length, and +less-or-equal maximum length. If the default flag is set, the imported table +is used as the [default table](AstSemantics.md#table) and at most one table +definition (import or internal) may have the default flag set. In the MVP, it is +a validation error not to set the default flag. Since the WebAssembly spec does not define how import names are interpreted: * the [Web environment](Web.md#names) defines names to be UTF8-encoded strings; @@ -105,20 +105,17 @@ native `syscall`. For example, a shell environment could define a builtin ## Exports -A module can declare a sequence of **exports** which are provided, at -instantiation time, to the host environment. There are several kinds of exports: -* **function exports**, which allow the exported function to be called by the - host environment (or other code running in the host environment); -* **linear memory exports**, which allow the exported linear memory to be - aliased by the host environment (or other code running int he host - environment); and -* **table exports**, which allow the elements of the table to be read, written - or called by the host environment (or other code running in the host - environment). - -Exports additionally contain a name and an index into the associated -export's type's [index space](#index-spaces). As with imports, the meaning -of an export name is defined by the host. +A module can declare a sequence of **exports** which are returned at +instantiation time to the host environment. Each export has two fields: +a *name*, whose meaning is defined by the host environment, and an +*index* into the modules [definition index space](definition-index-space), +which indicates which definition to export. + +All definitions are exportable: functions, globals, linear memories and tables. +The meaning an exported definition is defined by the host environment. However, +if another WebAssembly instance imports the definition, then both instances +will share the same definition and the associated state (global variable value, +linear memory bytes, table elements) is shared. ## Integration with ES6 modules @@ -169,7 +166,7 @@ independent libraries would have to hope that all the WebAssembly modules transitively used by those libraries "played well" together (e.g., explicitly shared `malloc` and coordinated global address ranges). Instead, the [dynamic linking future feature](DynamicLinking.md) is intended -to allow *explicitly* injecting multiple modules into the same instance. +to allow *explicitly* sharing state between module instances. ## Module start function @@ -178,7 +175,7 @@ by the loader after the instance is initialized and before the exported function are called. * The start function must not take any arguments or return anything -* The function is identified by [function index](#index-spaces) and can also be +* The function is identified by [function index](#function-index-space) and can also be exported * There can only be at most one start node per module @@ -200,14 +197,31 @@ A module can: * The start function will be called after module loading and before any call to the module function is done +## Global section + +The *global section* provides an internal definition of zero or more +[global variables](AstSemantics.md#global-variables). + +Each global variable internal definition declares its *type* +(a [value type](AstSemantics.md#types)), *mutability* (boolean flag) and +*initializer* (an [initializer expression](#initializer-expression)). + ## Linear memory section -The *linear memory section* may contain zero or more definitions of distinct -[linear memories](AstSemantics.md#linear-memory) which are added to the -[linear memory index space](#index-spaces). Each linear memory section declares -an initial [memory size](AstSemantics.md#linear-memory) (which may be -subsequently increased by [`grow_memory`](AstSemantics.md#resizing)) and an -optional maximum memory size. +The *linear memory section* provides an internal definition of zero or more +[linear memories](AstSemantics.md#linear-memory). In the MVP, the total number +of linear memory definitions is limited to 1, but this may be relaxed in the +[future](FutureFeatures.md#multiple-tables-and-memories). + +A linear memory definition may declare itself to be the +[default](AstSemantics.md#linear-memory) linear memory of the module. At most +one linear memory definition may declare itself to be the default. In the MVP, +if there is a linear memory definition, it *must* declare itself the default +(there is no way to access non-default linear memories anyhow). + +Each linear memory section declares an *initial* [memory size](AstSemantics.md#linear-memory) +(which may be subsequently increased by [`grow_memory`](AstSemantics.md#resizing)) and an +optional *maximum memory size*. [`grow_memory`](AstSemantics.md#resizing) is guaranteed to fail if attempting to grow past the declared maximum. When declared, implementations *should* @@ -221,16 +235,31 @@ only the initial size and reallocate on demand. The initial contents of linear memory are zero. The *data section* contains a possibly-empty array of *data segments* which specify the initial contents -of fixed `(offset, length)` ranges of a given memory, specified by its [linear -memory index](#index-space). This section is analogous to the `.data` section -of native executables. +of fixed `(offset, length)` ranges of a given memory, specified by its +[linear memory index](#linear-memory-index-space). This section is analogous to +the `.data` section of native executables. The `length` is an integer constant +value (defining the length of the given segment). The `offset` is an +[initializer expression](#initializer-expression). ## Table section -A *table section* may contain zero or more definitions of distinct -[tables](AstSemantics.md#tables) which are added to the -[table index space](#index-spaces). Each table section declares an *element -type*, *initial length*, and optional *maximum length*. +The *table section* contains zero or more definitions of distinct +[tables](AstSemantics.md#table). In the MVP, the total number +of table definitions is limited to 1, but this may be relaxed in the +[future](FutureFeatures.md#multiple-tables-and-memories). + +A table definition may declare itself to be the +[default](AstSemantics.md#table) table of the module. At most +one table definition may declare itself to be the default. In the MVP, +if there is a table definition, it *must* declare itself the default +(there is no way to access non-default tables anyhow). + +Each table definition also includes an *element type*, *initial length*, and +optional *maximum length*. + +In the MVP, the only valid element type is `"function"`, but in the +[future](FutureFeatures.md#more-table-operators-and-types), more element +types will be added. In the MVP, tables can only be resized via host-defined APIs (such as the JavaScript [`WebAssembly.Table.prototype.grow`](JS.md#webassemblytableprototypegrow)). @@ -245,23 +274,111 @@ space, engines should allocate only the initial size and reallocate on demand. ## Elements section -For function tables, the intial contents of the tables' elements are sentinel -values that throw if called. The *elements section* allows a module to -initialize (at instantiation time) the elements of any imported or defined -table with any function in the module. This is symmetric to how the +The intial contents of a tables' elements are sentinel values (that would throw +if called). The *elements section* allows a module to initialize (at +instantiation time) the elements of any imported or internally-defined table +with any other definition in the module. This is symmetric to how the [Data section](#data-section) allows a module to initialize the bytes of any imported or defined memory. Specifically, the elements section contains a possibly-empty array of -*element segments*. Each element segment contains a -[table index](#index-spaces), indicating which table to initialize, -an *offset* (where in the table to start initializing) and then -an array of [function indices](#index-spaces) whose corresponding -functions will be stored into the table starting at the offset. - -## Code section - -The code section contains a sequence of functions definitions which are added to -the [function index space](#index-spaces). Functions are split into -a sequence of [signature declarations](BinaryEncoding.md#function-section) -and [bodies](BinaryEncoding.md#code-section) as defined in the binary encoding. +*element segments* which specify the initial contents of fixed +`(offset, length)` ranges of a given table, specified by its +[table index](#table-index-space). The `length` is an integer constant value +(defining the length of the given segment). The `offset` is an +[initializer expression](#initializer-expression). + +## Function and Code sections + +A single logical function definition is defined in two sections: + * the *function* section declares the signatures of each internal function + definition in the module; + * the *code* section contains the [function body](BinaryEncoding.md#function-bodies) + of each function declared by the function section. + +This split aids in streaming compilation by putting the function bodies, +which constitute most of the byte size of the module, near the end so that all +metadata necessary for recursive module loading and parallel compilation is +available before compilation begins. + +## Definition Index Space + +The *definition index space* represents the union of all definitions introduced +by the [import](#imports), [global](#global-section), [memory](#linear-memory-section), +[table](#table-section), and [code](#code-section) sections. Each of these +sections can introduce zero or more definitions and the definition index space +simply assigns monotonically increasing indices to these definitions according +to their absolute order as defined in [BinaryEncoding.md](BinaryEncoding.md). + +The definition index space is used by: +* [exports](#exports), to indicate which definition to export +* [elements sections](#elements-section), to place definitions into tables + +In the future, an `address_of` operator could be added which, given a definition +index immediate, returns a first-class [reference](GC.md) to the definition. + +Note: the definition index spaces is a validation/compile-time concept, not +runtime state of an instance. + +## Function Index Space + +The *function index space* represents the subsequence of the +[definition index space](#definition-index-space) obtained by discarding +non-function definitions. + +The function index space is used by: +* [calls](AstSemantics.md#calls), to identify the callee of a direct call + +## Global Index Space + +The *global index space* represents the subsequence of the +[definition index space](#definition-index-space) obtained by discarding +non-global definitions. + +The global index space is used by: +* [global variable access operators](AstSemantics.md#global-variables), to + identify the global variable to read/write +* [data segments](#data-section), to define the offset of a data segment + (in linear memory) as the value of a global variable + +## Linear Memory Index Space + +The *linear memory index space* represents the subsequence of the +[definition index space](#definition-index-space) obtained by discarding +non-linear-memory definitions. + +The linear memory index space is only used by the +[data section](#data-section). In the MVP, there is at most one linear memory so +this index space is just a placeholder for when there can be +[multiple memories](FutureFeatures.md#multiple-tables-and-memories). + +## Table Index Space + +The *table index space* represents the subsequence of the +[definition index space](#definition-index-space) obtained by discarding +non-table definitions. + +The table index space is only used by the [elements section](#elements-section). +In the MVP, there is at most one table so this index space is just +a placeholder for when there can be +[multiple tables](FutureFeatures.md#multiple-tables-and-memories). + +## Initializer Expression + +Initializer expressions are evaluated at instantiation time and are currently +used to: + * define the initial value of [global variables](#global-section) + * define the offset of a [data segment](#data-section) or + [elements segment](#elements-section) + +An initializer expression is simply the binary encoding of a single +WebAssembly expression, as defined in [BinaryEncoding.md](BinaryEncoding.md). +Clearly, not all WebAssembly operators can be supported in initializer +expressions. In the MVP, to keep things simple while still supporting the needs +of [dynamic linking](DynamicLinking.md), initializer expressions are restricted +to the following nullary operators: + * the four [constant operators](AstSemantics.md#constants); and + * `get_global`, where the global index must refer to an immutable import. + +In the future, operators like `i32.add` could be added to allow more expressive +load-time calculations. diff --git a/Web.md b/Web.md index 21b5ff6b..9b4f34c6 100644 --- a/Web.md +++ b/Web.md @@ -24,7 +24,7 @@ alias the exported memory of instantiated modules, etc. ## Modules -WebAssembly's [modules](Modules.md) allow for natural [integration with +WebAssembly's [modules](Modules.md) allow for natural [integration with the ES6 module system](Modules.md#integration-with-es6-modules). ### Names From 9a28b97eda310ad7377c85aca9adca62d3f6838f Mon Sep 17 00:00:00 2001 From: Luke Wagner Date: Mon, 30 May 2016 12:45:46 -0500 Subject: [PATCH 08/22] Clarify host-defined (i.e., JavaScript) table elements --- AstSemantics.md | 13 +++++++++---- JS.md | 13 +++++++++++++ 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/AstSemantics.md b/AstSemantics.md index 12bdb6e4..1a6a4407 100644 --- a/AstSemantics.md +++ b/AstSemantics.md @@ -254,10 +254,12 @@ bridges the gap between low-level, untrusted linear memory and high-level opaque handles/references at the cost of a bounds-checked table indirection. The table's element type dynamically constrains the type of elements stored -in the table and allows engines to avoid some type checks on table use. When -tables are mutated, any stored value must match the element type by either a -static validation constraint, trapping dynamic type validation check, or dynamic -coercion (depending on where the mutation occurs). +in the table and allows engines to avoid some type checks on table use. +When a WebAssembly value is stored in a table, the value's type must precisely +match the element type. Depending on the operator/API used to store the value, +this check may be static or dynamic. Host environments may also allow storing +non-WebAssembly values in tables in which case, as with [imports](Modules.md#imports), +the meaning of using the value is defined by the host environment. Every WebAssembly [instance](Modules.md) has one specially-designated *default* table which is indexed by [`call_indirect`](#calls) and other future @@ -408,6 +410,9 @@ signature and and trapping if there is a mismatch. Since the callee may be in a different module which necessarily has a separate [types section](BinaryEncoding.md#type-section), and thus index space of types, the signature match must compare the underlying [`func_type`](https://github.com/WebAssembly/spec/blob/master/ml-proto/spec/types.ml#L5). +As noted [above](#table), table elements may also be host-environment-defined +values in which case the meaning of a call is defined by the host-environment, +much like calling an import. In the MVP, the single `call_indirect` operator accesses the [default table](#table). diff --git a/JS.md b/JS.md index c99a0f7c..65ac09eb 100644 --- a/JS.md +++ b/JS.md @@ -437,6 +437,16 @@ false and `Type(value)` is not Null, throw a type error. Let `i` be the result of [`ToInteger`](http://tc39.github.io/ecma262/#sec-tointeger)(`index`). +If `v` is an [Exported Function Exotic Object](#exported-function-exotic-objects): +* Set the `i`th element of `T.[[Table]]` to the `v.[[FunctionIndex]]`th function + in the module's [function index space](Modules.md#function-index-space). + +Otherwise: +* Set the `i`th element of `T.[[Table]]` to a host-defined value that can be + called with *any* signature by coercing its WebAssembly arguments to JavaScript + arguments via [ToJSValue](#tojsvalue) and coercing its JavaScript return value + to a WebAssembly return value via [ToWebAssemblyValue](#towebassemblyvalue). + Set `T.[[Values]][i]` to `value`. Return Undefined. @@ -468,6 +478,9 @@ To coerce a JavaScript value to a given [WebAssembly value type](https://github. and then converting the resulting IEEE754 64-bit double to a 32-bit float using `roundTiesToEven` * coerce to `f64` via [`ToNumber(v)`](http://tc39.github.io/ecma262/#sec-tonumber) +If the value type is optional, then given `None`, the JavaScript value is +ignored. + ## Sample API Usage Given `demo.was` (encoded to `demo.wasm`): From ff3a88f93b476efc7174c1c0167e2226083a6ef5 Mon Sep 17 00:00:00 2001 From: Luke Wagner Date: Tue, 31 May 2016 12:48:12 -0500 Subject: [PATCH 09/22] s/necessary/useful --- AstSemantics.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AstSemantics.md b/AstSemantics.md index 1a6a4407..37574990 100644 --- a/AstSemantics.md +++ b/AstSemantics.md @@ -315,7 +315,7 @@ After import or definition, there is no difference when calling into a table. It is a validation error for a `set_global` to index an immutable global variable. In the MVP, the primary use case of global variables is to represent -instantiation-time immutable values as a necessary building block of +instantiation-time immutable values as a useful building block for [dynamic linking](DynamicLinking.md). After the MVP, when [reference types](GC.md) are added to the set of [value types](#types), From 06d67ca977e4a73e9c52ace43f6fe1c21cd734e3 Mon Sep 17 00:00:00 2001 From: Luke Wagner Date: Tue, 31 May 2016 13:01:49 -0500 Subject: [PATCH 10/22] s/will/may/ --- AstSemantics.md | 4 ++-- Modules.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/AstSemantics.md b/AstSemantics.md index 37574990..8a3581e8 100644 --- a/AstSemantics.md +++ b/AstSemantics.md @@ -101,7 +101,7 @@ Every WebAssembly [instance](Modules.md) has one specially-designated *default* linear memory which is the linear memory accessed by all the [memory operators below](#linear-memory-access). In the MVP, there are *only* default linear memories but [new memory operators](FutureFeatures.md#multiple-tables-and-memories) -will be added after the MVP which can also access non-default memories. +may be added after the MVP which can also access non-default memories. Linear memories (default or otherwise) can either be [imported](Modules.md#imports) or [defined inside the module](Modules.md#linear-memory-section), with defaultness @@ -278,7 +278,7 @@ to hold the array of indirectly-callable functions. Thus, in the MVP: this can only be done through the host environment (e.g., the the `WebAssembly` [JavaScript API](JS.md#webassemblytable-objects)). -These restrictions will be relaxed in the +These restrictions may be relaxed in the [future](FutureFeatures.md#more-table-operators-and-types). ## Local variables diff --git a/Modules.md b/Modules.md index 1ae483f6..d7d8db04 100644 --- a/Modules.md +++ b/Modules.md @@ -42,7 +42,7 @@ instantiation time, by the host environment. There are several kinds of imports: table operators in the [future](FutureFeatures.md#more-table-operators-and-types). -In the future, other kinds of imports will be added. Imports are designed to +In the future, other kinds of imports may be added. Imports are designed to allow modules to share code and data while still allowing separate compilation and caching. @@ -259,7 +259,7 @@ optional *maximum length*. In the MVP, the only valid element type is `"function"`, but in the [future](FutureFeatures.md#more-table-operators-and-types), more element -types will be added. +types may be added. In the MVP, tables can only be resized via host-defined APIs (such as the JavaScript [`WebAssembly.Table.prototype.grow`](JS.md#webassemblytableprototypegrow)). From 9e1809b89bce71f4c46c192038fab57cdae322ba Mon Sep 17 00:00:00 2001 From: Luke Wagner Date: Tue, 31 May 2016 13:03:10 -0500 Subject: [PATCH 11/22] Fix typo --- AstSemantics.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AstSemantics.md b/AstSemantics.md index 8a3581e8..f75dc5b8 100644 --- a/AstSemantics.md +++ b/AstSemantics.md @@ -307,7 +307,7 @@ cannot be arbitrarily aliased as bits. Global variables are accessed via an integer index into the module-defined [global index space](Modules.md#global-index-space). Global variables can either be [imported](Modules.md#imports) or [defined inside the module](Modules.md#global-section). -After import or definition, there is no difference when calling into a table. +After import or definition, there is no difference when accessing a global. * `get_global`: get the current value of a global variable * `set_global`: set the current value of a global variable From 74966aebb13e0062b6d5b27507db58d9bb04d76f Mon Sep 17 00:00:00 2001 From: Luke Wagner Date: Tue, 31 May 2016 16:04:36 -0500 Subject: [PATCH 12/22] Fix typo --- Modules.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules.md b/Modules.md index d7d8db04..d62a639c 100644 --- a/Modules.md +++ b/Modules.md @@ -317,7 +317,7 @@ The definition index space is used by: In the future, an `address_of` operator could be added which, given a definition index immediate, returns a first-class [reference](GC.md) to the definition. -Note: the definition index spaces is a validation/compile-time concept, not +Note: the definition index space is a validation/compile-time concept, not runtime state of an instance. ## Function Index Space From aaaecfa1630306c7742fca32fc7fe0d979a43d69 Mon Sep 17 00:00:00 2001 From: Luke Wagner Date: Wed, 1 Jun 2016 08:48:11 -0500 Subject: [PATCH 13/22] Clarify that table updates are observed by all instances --- AstSemantics.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/AstSemantics.md b/AstSemantics.md index f75dc5b8..2c6e734b 100644 --- a/AstSemantics.md +++ b/AstSemantics.md @@ -256,10 +256,12 @@ opaque handles/references at the cost of a bounds-checked table indirection. The table's element type dynamically constrains the type of elements stored in the table and allows engines to avoid some type checks on table use. When a WebAssembly value is stored in a table, the value's type must precisely -match the element type. Depending on the operator/API used to store the value, -this check may be static or dynamic. Host environments may also allow storing -non-WebAssembly values in tables in which case, as with [imports](Modules.md#imports), -the meaning of using the value is defined by the host environment. +match the element type. Just like linear memory, updates to a table are +observed immediately by all instances that reference the table. Depending on the +operator/API used to store the value, this check may be static or dynamic. Host +environments may also allow storing non-WebAssembly values in tables in which +case, as with [imports](Modules.md#imports), the meaning of using the value is +defined by the host environment. Every WebAssembly [instance](Modules.md) has one specially-designated *default* table which is indexed by [`call_indirect`](#calls) and other future From fb4ecd1535133c868b3d7ee3e2e11049a5948157 Mon Sep 17 00:00:00 2001 From: Luke Wagner Date: Wed, 1 Jun 2016 08:50:31 -0500 Subject: [PATCH 14/22] Clarify that host-defined table element values can have different signature checking --- AstSemantics.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/AstSemantics.md b/AstSemantics.md index 2c6e734b..2160be76 100644 --- a/AstSemantics.md +++ b/AstSemantics.md @@ -413,8 +413,8 @@ different module which necessarily has a separate [types section](BinaryEncoding and thus index space of types, the signature match must compare the underlying [`func_type`](https://github.com/WebAssembly/spec/blob/master/ml-proto/spec/types.ml#L5). As noted [above](#table), table elements may also be host-environment-defined -values in which case the meaning of a call is defined by the host-environment, -much like calling an import. +values in which case the meaning of a call (and how the signature is checked) +is defined by the host-environment, much like calling an import. In the MVP, the single `call_indirect` operator accesses the [default table](#table). From c0141b1883530e50cfbeb7d2ff1e2e52265765c6 Mon Sep 17 00:00:00 2001 From: Luke Wagner Date: Thu, 9 Jun 2016 12:11:17 -0500 Subject: [PATCH 15/22] Just 'constraints' since more detail is below --- AstSemantics.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AstSemantics.md b/AstSemantics.md index 2160be76..0f1dce1b 100644 --- a/AstSemantics.md +++ b/AstSemantics.md @@ -253,7 +253,7 @@ accessed by WebAssembly code indirectly through an integer index. This feature bridges the gap between low-level, untrusted linear memory and high-level opaque handles/references at the cost of a bounds-checked table indirection. -The table's element type dynamically constrains the type of elements stored +The table's element type constrains the type of elements stored in the table and allows engines to avoid some type checks on table use. When a WebAssembly value is stored in a table, the value's type must precisely match the element type. Just like linear memory, updates to a table are From 7e89c8654afb6c02fce5e5a0f1f570a5b825c5c6 Mon Sep 17 00:00:00 2001 From: Luke Wagner Date: Thu, 9 Jun 2016 12:13:13 -0500 Subject: [PATCH 16/22] Wordsmith DynamicLinking.md intro --- DynamicLinking.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/DynamicLinking.md b/DynamicLinking.md index 70658629..aec31e9a 100644 --- a/DynamicLinking.md +++ b/DynamicLinking.md @@ -1,7 +1,7 @@ # Dynamic linking -WebAssembly allows load-time and run-time (`dlopen`) dynamic linking in the -MVP. This is enabled by having multiple [instantiated modules](Modules.md) +WebAssembly enables load-time and run-time (`dlopen`) dynamic linking in the +MVP by having multiple [instantiated modules](Modules.md) share functions, [linear memories](AstSemantics.md#linear-memory), [tables](AstSemantics.md#table) and [constants](AstSemantics.md#constants) using module [imports](Modules.md#imports) and [exports](Modules.md#exports). In From 23375a14f19947bb0ea377fd9be7e5e61f0f3442 Mon Sep 17 00:00:00 2001 From: Luke Wagner Date: Thu, 9 Jun 2016 12:18:45 -0500 Subject: [PATCH 17/22] Wordsmith FutureFeatures.md --- FutureFeatures.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/FutureFeatures.md b/FutureFeatures.md index 814b502f..805d3ce0 100644 --- a/FutureFeatures.md +++ b/FutureFeatures.md @@ -418,8 +418,8 @@ of WebAssembly in browsers: ## Multiple Tables and Memories -In the MVP, there can only be (at most one) default table/memory and there are -only operators for accessing the default table/memory. +The MVP limits modules to at most one memory and at most one table (the default +ones) and there are only operators for accessing the default table and memory. After the MVP and after [GC reference types](GC.md) have been added, the default limitation can be relaxed so that any number of tables and memories could be From f045e0157c866bc71a662b11e636ff81061a4f28 Mon Sep 17 00:00:00 2001 From: Luke Wagner Date: Thu, 9 Jun 2016 15:23:21 -0500 Subject: [PATCH 18/22] Try to clarify wording in FutureFeatures.md --- FutureFeatures.md | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/FutureFeatures.md b/FutureFeatures.md index 805d3ce0..fc5922d8 100644 --- a/FutureFeatures.md +++ b/FutureFeatures.md @@ -451,10 +451,12 @@ could be added: `grow_memory` * `current_table_length`: like `current_memory`. -Additionally, in the MVP, tables' an only store untyped functions. This could be -relaxed to: -* functions with a particular signature, allowing code generators to use - multiple homogeneously-typed function tables to replace the implied dynamic - signature check of a heterogeneous table with a static validation check +Additionally, in the MVP, the only allowed element type of tables is a generic +"function" type which simply means the element can be called but there is no +static signature validation check. This could be improved by allowing: +* functions with a particular signature, allowing wasm generators to use + multiple homogeneously-typed function tables (instead of a single + heterogeneous function table) which eliminates the implied dynamic signature + check of a call to a heterogeneous table; * any other specific GC reference type, effectively allowing WebAssembly code - to implement a variety of rooting API schemes + to implement a variety of rooting API schemes. From 8d8e75d5e9b89f3dea712f69ea134d333ba29004 Mon Sep 17 00:00:00 2001 From: Luke Wagner Date: Mon, 13 Jun 2016 09:46:00 -0500 Subject: [PATCH 19/22] Change 'function' to 'anyfunc' --- AstSemantics.md | 2 +- JS.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/AstSemantics.md b/AstSemantics.md index 0f1dce1b..754f1f41 100644 --- a/AstSemantics.md +++ b/AstSemantics.md @@ -275,7 +275,7 @@ In the MVP, the primary purpose of tables is to implement indirect function calls in C/C++ using an integer index as the pointer-to-function and the table to hold the array of indirectly-callable functions. Thus, in the MVP: * tables may only be accessed from WebAssembly code via [`call_indirect`](#calls); -* the only allowed table element type is "function" (no signature); +* the only allowed table element type is `anyfunc` (function with any signature); * tables may not be directly mutated or resized from WebAssembly code; this can only be done through the host environment (e.g., the the `WebAssembly` [JavaScript API](JS.md#webassemblytable-objects)). diff --git a/JS.md b/JS.md index 65ac09eb..52206f4a 100644 --- a/JS.md +++ b/JS.md @@ -379,7 +379,7 @@ constructor cannot be called as a function without `new`). If `Type(tableDescriptor)` is not Object, a `TypeError` is thrown. Let `element` be the result of calling [`Get`](http://tc39.github.io/ecma262/#sec-get-o-p)(`tableDescriptor`, `"element"`). -If `element` is not the string `"function"`, a `TypeError` is thrown. +If `element` is not the string `"anyfunc"`, a `TypeError` is thrown. (Note: this check is intended to be relaxed in the [future](FutureFeatures.md#more-table-operators-and-types) to allow different elemtn types.) From 902c8f346b672b133d4903444a99f24a8c10d240 Mon Sep 17 00:00:00 2001 From: Luke Wagner Date: Mon, 13 Jun 2016 09:53:49 -0500 Subject: [PATCH 20/22] Fix nits --- Modules.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Modules.md b/Modules.md index d62a639c..bff2fc1b 100644 --- a/Modules.md +++ b/Modules.md @@ -274,7 +274,7 @@ space, engines should allocate only the initial size and reallocate on demand. ## Elements section -The intial contents of a tables' elements are sentinel values (that would throw +The intial contents of a tables' elements are sentinel values (that would trap if called). The *elements section* allows a module to initialize (at instantiation time) the elements of any imported or internally-defined table with any other definition in the module. This is symmetric to how the @@ -305,7 +305,7 @@ available before compilation begins. The *definition index space* represents the union of all definitions introduced by the [import](#imports), [global](#global-section), [memory](#linear-memory-section), -[table](#table-section), and [code](#code-section) sections. Each of these +[table](#table-section), and [function](#code-section) sections. Each of these sections can introduce zero or more definitions and the definition index space simply assigns monotonically increasing indices to these definitions according to their absolute order as defined in [BinaryEncoding.md](BinaryEncoding.md). From 5f7368c116b0db202e39160980c68557a6241abe Mon Sep 17 00:00:00 2001 From: Luke Wagner Date: Mon, 13 Jun 2016 09:58:01 -0500 Subject: [PATCH 21/22] Refine description of initializer expressions --- Modules.md | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/Modules.md b/Modules.md index bff2fc1b..1a1e8710 100644 --- a/Modules.md +++ b/Modules.md @@ -371,14 +371,17 @@ used to: * define the offset of a [data segment](#data-section) or [elements segment](#elements-section) -An initializer expression is simply the binary encoding of a single -WebAssembly expression, as defined in [BinaryEncoding.md](BinaryEncoding.md). -Clearly, not all WebAssembly operators can be supported in initializer -expressions. In the MVP, to keep things simple while still supporting the needs +An initializer expression is a pure WebAssembly expression that is encoded with +the same [binary encoding](BinaryEncoding.md) as WebAssembly expressions. Not +all WebAssembly operators can or should be supported in initializer expressions; +initializer expressions represent a minimal pure subset of WebAssembly +expressions. + +In the MVP, to keep things simple while still supporting the basic needs of [dynamic linking](DynamicLinking.md), initializer expressions are restricted to the following nullary operators: * the four [constant operators](AstSemantics.md#constants); and * `get_global`, where the global index must refer to an immutable import. In the future, operators like `i32.add` could be added to allow more expressive -load-time calculations. +`base + offset` load-time calculations. From c7d34f7486feae7105a455e7f210b5cafc80ad03 Mon Sep 17 00:00:00 2001 From: Luke Wagner Date: Mon, 13 Jun 2016 10:39:42 -0500 Subject: [PATCH 22/22] Remove the Definition Index Space, use (type, index) pairs as necessary --- FutureFeatures.md | 9 +++---- JS.md | 24 ++++++++--------- Modules.md | 66 +++++++++++++++++------------------------------ 3 files changed, 39 insertions(+), 60 deletions(-) diff --git a/FutureFeatures.md b/FutureFeatures.md index fc5922d8..724f4bbe 100644 --- a/FutureFeatures.md +++ b/FutureFeatures.md @@ -429,11 +429,10 @@ and `call_indirect` would then be added which took an additional memory/table reference operand. To access an imported or internally-defined non-default table or memory, a -new `address_of` operator could be added which, given an immediate index into -the module's [definition index space](Modules.md#definition-index-space), would -return a first-class reference to that definition. Beyond tables and memories, -this could also be used for function definitions to get a reference to a function -(which, since opaque, could be implemented as a raw function pointer). +new `address_of` operator could be added which, given an index immediate, +would return a first-class reference. Beyond tables and memories, this could +also be used for function definitions to get a reference to a function (which, +since opaque, could be implemented as a raw function pointer). ## More Table Operators and Types diff --git a/JS.md b/JS.md index 52206f4a..e78dfd05 100644 --- a/JS.md +++ b/JS.md @@ -183,26 +183,24 @@ function, if present. Let `exports` be a list of (string, JS value) pairs that is mapped from `module.exports` as follows (assuming the ML spec [`export`](https://github.com/WebAssembly/spec/blob/master/ml-proto/spec/kernel.ml#L128) -has been modified so that each export simply has a `name` and `index` (into -the module's [definition index space](Modules.md#definition-index-space)): -* If `export.index` refers to an imported definition, then simply re-export the +has been modified so that each export simply has a `name`, `type` and `index`: +* If `index` refers to an imported `type` definition, then simply re-export the imported JS value. * Otherwise (an internal definition): - * If `export.index` refers to a function definition, then export - an [Exported Function Exotic Object](#exported-function-exotic-objects), + * If the `type` is function, then export an + [Exported Function Exotic Object](#exported-function-exotic-objects), reusing an existing object if one exists for the given function definition, otherwise creating a new object. - * If `export.index` refers to a global definition: + * If the `type` is global: * If the global is not immutable, then throw a `TypeError`. * Let `v` be the global variable's initialized value. * Otherwise, export [`ToJSValue`](#tojsvalue)`(v)`. - * If `export.index` refers to a memory definition, then export a - `WebAssembly.Memory` object, reusing an existing object if one exists for - the given memory definition, otherwise creating a new object via - [`CreateMemoryObject`](#creatememoryobject). - * Otherwise (`export.index` refers to a table definition), export a - `WebAssembly.Table` object, reusing an existing object if one exists for - the given table definition, otherwise creating a new object via: + * If the `type` is memory, then export a `WebAssembly.Memory` object, reusing + an existing object if one exists for the given memory definition, otherwise + creating a new object via [`CreateMemoryObject`](#creatememoryobject). + * Otherwise the `type` must be a table so export a `WebAssembly.Table` object, + reusing an existing object if one exists for the given table definition, + otherwise creating a new object via: * Let `values` be a list of JS values that is mapped from the table's elements as follows: * sentinel values (which throw if called) are given the value `null` diff --git a/Modules.md b/Modules.md index 1a1e8710..244722a0 100644 --- a/Modules.md +++ b/Modules.md @@ -21,7 +21,6 @@ A module contains the following sections: A module also defines several *index spaces* which are statically indexed by various operators and section fields in the module: -* the [definition index space](#definition-index-space) * the [function index space](#function-index-space) * the [global index space](#global-index-space) * the [linear memory index space](#linear-memory-index-space) @@ -106,10 +105,10 @@ native `syscall`. For example, a shell environment could define a builtin ## Exports A module can declare a sequence of **exports** which are returned at -instantiation time to the host environment. Each export has two fields: -a *name*, whose meaning is defined by the host environment, and an -*index* into the modules [definition index space](definition-index-space), -which indicates which definition to export. +instantiation time to the host environment. Each export has three fields: +a *name*, whose meaning is defined by the host environment, a *type*, +indicating whether the export is a function, global, memory or table, and +an *index* into the type's corresponding [index space](Modules.md). All definitions are exportable: functions, globals, linear memories and tables. The meaning an exported definition is defined by the host environment. However, @@ -281,12 +280,14 @@ with any other definition in the module. This is symmetric to how the [Data section](#data-section) allows a module to initialize the bytes of any imported or defined memory. -Specifically, the elements section contains a possibly-empty array of -*element segments* which specify the initial contents of fixed -`(offset, length)` ranges of a given table, specified by its -[table index](#table-index-space). The `length` is an integer constant value -(defining the length of the given segment). The `offset` is an -[initializer expression](#initializer-expression). +The elements section contains a possibly-empty array of *element segments* which +specify the initial contents of fixed `(offset, length)` ranges of a given +table, specified by its [table index](#table-index-space). The `length` is an +integer constant value (defining the length of the given segment). The `offset` +is an [initializer expression](#initializer-expression). Elements are specified +with a `(type, index)` pair where `type` is the type of an +[index spaces](Modules.md) that is compatible with the table's element type and +`index` is an integer immediate into `type`s index space. ## Function and Code sections @@ -301,39 +302,20 @@ which constitute most of the byte size of the module, near the end so that all metadata necessary for recursive module loading and parallel compilation is available before compilation begins. -## Definition Index Space - -The *definition index space* represents the union of all definitions introduced -by the [import](#imports), [global](#global-section), [memory](#linear-memory-section), -[table](#table-section), and [function](#code-section) sections. Each of these -sections can introduce zero or more definitions and the definition index space -simply assigns monotonically increasing indices to these definitions according -to their absolute order as defined in [BinaryEncoding.md](BinaryEncoding.md). - -The definition index space is used by: -* [exports](#exports), to indicate which definition to export -* [elements sections](#elements-section), to place definitions into tables - -In the future, an `address_of` operator could be added which, given a definition -index immediate, returns a first-class [reference](GC.md) to the definition. - -Note: the definition index space is a validation/compile-time concept, not -runtime state of an instance. - ## Function Index Space -The *function index space* represents the subsequence of the -[definition index space](#definition-index-space) obtained by discarding -non-function definitions. +The *function index space* indexes all imported and internally-defined +function definitions, assigning monotonically-increasing indices based on the +order of definition in the module (as defined by the [binary encoding](BinaryEncoding.md)). The function index space is used by: * [calls](AstSemantics.md#calls), to identify the callee of a direct call ## Global Index Space -The *global index space* represents the subsequence of the -[definition index space](#definition-index-space) obtained by discarding -non-global definitions. +The *global index space* indexes all imported and internally-defined +global definitions, assigning monotonically-increasing indices based on the +order of definition in the module (as defined by the [binary encoding](BinaryEncoding.md)). The global index space is used by: * [global variable access operators](AstSemantics.md#global-variables), to @@ -343,9 +325,9 @@ The global index space is used by: ## Linear Memory Index Space -The *linear memory index space* represents the subsequence of the -[definition index space](#definition-index-space) obtained by discarding -non-linear-memory definitions. +The *linear memory index space* indexes all imported and internally-defined +linear memory definitions, assigning monotonically-increasing indices based on the +order of definition in the module (as defined by the [binary encoding](BinaryEncoding.md)). The linear memory index space is only used by the [data section](#data-section). In the MVP, there is at most one linear memory so @@ -354,9 +336,9 @@ this index space is just a placeholder for when there can be ## Table Index Space -The *table index space* represents the subsequence of the -[definition index space](#definition-index-space) obtained by discarding -non-table definitions. +The *table index space* indexes all imported and internally-defined +table definitions, assigning monotonically-increasing indices based on the +order of definition in the module (as defined by the [binary encoding](BinaryEncoding.md)). The table index space is only used by the [elements section](#elements-section). In the MVP, there is at most one table so this index space is just