diff --git a/design/mvp/Binary.md b/design/mvp/Binary.md index 508f870f..e3fbb226 100644 --- a/design/mvp/Binary.md +++ b/design/mvp/Binary.md @@ -325,12 +325,12 @@ canon ::= 0x00 0x00 f: opts: ft: => (canon lift | 0x1d opts: => (canon error-context.debug-message opts (core func)) πŸ“ | 0x1e => (canon error-context.drop (core func)) πŸ“ | 0x1f => (canon waitable-set.new (core func)) πŸ”€ - | 0x20 cancel?: m: => (canon waitable-set.wait cancel? (memory m) (core func)) πŸ”€ - | 0x21 cancel?: m: => (canon waitable-set.poll cancel? (memory m) (core func)) πŸ”€ + | 0x20 cancel?: m: => (canon waitable-set.wait cancel? (memory m) (core func)) πŸ”€ + | 0x21 cancel?: m: => (canon waitable-set.poll cancel? (memory m) (core func)) πŸ”€ | 0x22 => (canon waitable-set.drop (core func)) πŸ”€ | 0x23 => (canon waitable.join (core func)) πŸ”€ | 0x26 => (canon thread.index (core func)) 🧡 - | 0x27 ft: tbl: => (canon thread.new-indirect ft tbl (core func)) 🧡 + | 0x27 ft: tbl: => (canon thread.new-indirect ft tbl (core func)) 🧡 | 0x28 => (canon thread.resume-later (core func)) 🧡 | 0x29 cancel?: => (canon thread.suspend cancel? (core func)) 🧡 | 0x0c cancel?: => (canon thread.yield cancel? (core func)) πŸ”€ @@ -338,8 +338,8 @@ canon ::= 0x00 0x00 f: opts: ft: => (canon lift | 0x2b cancel?: => (canon thread.yield-then-resume cancel? (core func)) 🧡 | 0x2c cancel?: => (canon thread.suspend-then-promote cancel? (core func)) 🧡 | 0x2d cancel?: => (canon thread.yield-then-promote cancel? (core func)) 🧡 - | 0x40 shared?: ft: => (canon thread.spawn-ref shared? ft (core func)) πŸ§΅β‘‘ - | 0x41 shared?: ft: tbl: => (canon thread.spawn-indirect shared? ft tbl (core func)) πŸ§΅β‘‘ + | 0x40 shared?: ft: => (canon thread.spawn-ref shared? ft (core func)) πŸ§΅β‘‘ + | 0x41 shared?: ft: tbl: => (canon thread.spawn-indirect shared? ft tbl (core func)) πŸ§΅β‘‘ | 0x42 shared?: => (canon thread.available-parallelism shared? (core func)) πŸ§΅β‘‘ async? ::= 0x00 => | 0x01 => async @@ -351,7 +351,7 @@ opts ::= opt*:vec() => opt* canonopt ::= 0x00 => string-encoding=utf8 | 0x01 => string-encoding=utf16 | 0x02 => string-encoding=latin1+utf16 - | 0x03 m: => (memory m) + | 0x03 m: => (memory m) | 0x04 f: => (realloc f) | 0x05 f: => (post-return f) | 0x06 => async πŸ”€ diff --git a/design/mvp/CanonicalABI.md b/design/mvp/CanonicalABI.md index 31a20d2a..936f4465 100644 --- a/design/mvp/CanonicalABI.md +++ b/design/mvp/CanonicalABI.md @@ -3556,7 +3556,7 @@ performed for a component. These are defined as: For a canonical definition: ```wat -(canon lift $callee: $opts:* (func $f (type $ft))) +(canon lift $callee $opts (func $f (type $ft))) ``` In addition to [general validation of `$opts`](#canonopt-validation) the additional diff --git a/design/mvp/Explainer.md b/design/mvp/Explainer.md index f08fd36a..3ca26d55 100644 --- a/design/mvp/Explainer.md +++ b/design/mvp/Explainer.md @@ -7,8 +7,8 @@ more user-focused explanation, take a look at the * [Gated features](#gated-features) * [Grammar](#grammar) + * [Index spaces](#index-spaces) * [Component definitions](#component-definitions) - * [Index spaces](#index-spaces) * [Instance definitions](#instance-definitions) * [Alias definitions](#alias-definitions) * [Type definitions](#type-definitions) @@ -81,17 +81,80 @@ succinctness, with just enough detail to write examples and define a [binary format](Binary.md) in the style of the [Binary Format Section], deferring full precision to the [formal specification](../../spec/). -The main way the grammar hand-waves is regarding definition uses, where indices -referring to `X` definitions (written ``) should, in the real text -format, explicitly allow identifiers (``), checking at parse time that the -identifier resolves to an `X` definition and then embedding the resolved index -into the AST. - Additionally, standard [abbreviations] defined by the Core WebAssembly text format (e.g., inline export definitions) are assumed but not explicitly defined below. +### Index Spaces + +As with Core WebAssembly, the Component Model appends each definition to an +[index space], allowing earlier definitions to be referred to by later +definitions in the text and binary format via unsigned integer index. + +There are 5 component-level index spaces (components, component instances, +functions, types and values πŸͺ™), 6 core index spaces already in the Core +WebAssembly specification (functions, tables, memories, globals, tags and types) +and 2 additional core index spaces (modules and module instances) that are not in +the Core WebAssembly specification at the moment, but, with [module-linking], +could be in the future. These 13 index spaces correspond 1:1 with the terminals +of `sort` shown below and so "sort" and "index space" can be used +interchangeably. The grammar rule for `sort` prefixes the Core WebAssembly sorts +with `core` to disambiguate current and future overlaps. +```ebnf +core:sort ::= func + | table + | memory + | global + | tag + | type + | module + | instance +componentsort ::= component + | instance + | func + | type + | value πŸͺ™ +sort ::= componentsort + | core +``` + +As with Core WebAssembly, for each index space `X`, there is an `{X}idx` grammar +rule that parses indices as either a direct `u32` or a [`core:id`] that is +resolved to a `u32` according to the usual Core WebAssembly text format rules. +When the text format accepts indices in one of several different index spaces, +the sort is required to be written explicitly using the `externidx` rule, which +embeds an expanded version of [`core:externidx`] prefixed with `core`. +```ebnf +idx ::= | +core:{Xα΅’}sortidx ::= (Xα΅’ ) (for Xα΅’ ∈ ) +core:{Xα΅’}idx ::= | (for Xα΅’ ∈ ) +core:externidx ::= | ... | (for X₁,…,Xβ‚™ ∈ ) +{Xα΅’}sortidx ::= (Xα΅’ ) (for Xα΅’ ∈ ) +{Xα΅’}idx ::= | <{Xα΅’}sortidx> (for Xα΅’ ∈ ) +externidx ::= <{X₁}sortidx> | ... | <{Xβ‚™}sortidx> (for X₁,…,Xβ‚™ ∈ ) + | core-prefix() + +where core-prefix() is defined: + if parses (), then core-prefix() parses (core ) + if parses , then core-prefix() parses +``` +The -`sortidx` schemas generate rules like `funcsortidx` and `core:funcsortidx` +which both parse `(func $foo)` and `(func 0)`. The -`idx` schemas generate rules +like `funcidx`/`core:funcidx` that extend `funcsortidx`/`core:funcsortidx` to +also parse plain identifiers (like `$foo`) and `u32` indices (like `0`). + +The `core-prefix` meta-function is used here and below to transform a rule for +parsing a parenthesized definition without `core` into the same definition but +with a `core` token right after the leftmost parenthesis. For example, since +`` parses `(func $f)`, `core-prefix()` parses +`(core func $f)`. This allows `` rules (which don't include a `core` +token) to be used inside `(core ...)` expressions and `core-prefix()` +rules to be used outside `(core ...)` expressions (at component-level) to +explicitly mark where a `core` token might be required. The exception in +`core-prefix` for `` means that, for example, `core-prefix()` +accepts both `(core func $foo)` and plain `$foo`. + ### Component Definitions At the top-level, a `component` is a sequence of definitions of various kinds: @@ -109,21 +172,17 @@ definition ::= core-prefix() | | | πŸͺ™ - -where core-prefix(X) parses '(' 'core' Y ')' when X parses '(' Y ')' ``` Components are like Core WebAssembly modules in that their contained definitions are acyclic: definitions can only refer to preceding definitions (in the AST, text format and binary format). However, unlike modules, components can arbitrarily interleave different kinds of definitions. -The `core-prefix` meta-function transforms a grammatical rule for parsing a -Core WebAssembly definition into a grammatical rule for parsing the same -definition, but with a `core` token added right after the leftmost paren. -For example, `core:module` accepts `(module (func))` so -`core-prefix()` accepts `(core module (func))`. Note that the -inner `func` doesn't need a `core` prefix; the `core` token is used to mark the -*transition* from parsing component definitions into core definitions. +As defined in the previous section, `core-prefix()` adds a `core` token after +the leftmost parenthesis of `` and so, e.g., `core-prefix()` +parses `(core module ...)` instead of `(module ...)`. The `core` prefix +disambiguates current and future overlaps between core- and component-level +definitions (like `func`, `instance` and `type`). The [`core:module`] production is unmodified by the Component Model and thus components embed Core WebAssembly (text and binary format) modules as currently @@ -163,64 +222,6 @@ next), nothing will be instantiated or executed at runtime; everything here is dead code. -#### Index Spaces - -[Like Core WebAssembly][Core Indices], the Component Model places each -`definition` into one of a fixed set of *index spaces*, allowing the -definition to be referred to by subsequent definitions (in the text and binary -format) via a nonnegative integral *index*. When defining, validating and -executing a component, there are 5 component-level index spaces: -* (component) functions -* (component) values πŸͺ™ -* (component) types -* component instances -* components - -6 core index spaces that also exist in the Core WebAssembly specification: -* (core) functions -* (core) tables -* (core) memories -* (core) globals -* (core) tags -* (core) types - -and 2 additional core index spaces that contain core definition introduced by -the Component Model that are not in Core WebAssembly (yet: the [module-linking] -proposal would add them): -* module instances -* modules - -for a total of 13 index spaces that need to be maintained by an implementation -when, e.g., validating a component. These 13 index spaces correspond 1:1 with -the terminals of the `sort` production defined below and thus "sort" and -"index space" can be used interchangeably. - -Also [like Core WebAssembly][Core Identifiers], the Component Model text format -allows *identifiers* to be used in place of these indices, which are resolved -when parsing into indices in the AST (upon which validation and execution is -defined). Thus, the following two components are equivalent: -```wat -(component - (core module (; empty ;)) - (component (; empty ;)) - (core module (; empty ;)) - (export "C" (component 0)) - (export "M1" (core module 0)) - (export "M2" (core module 1)) -) -``` -```wat -(component - (core module $M1 (; empty ;)) - (component $C (; empty ;)) - (core module $M2 (; empty ;)) - (export "C" (component $C)) - (export "M1" (core module $M1)) - (export "M2" (core module $M2)) -) -``` - - ### Instance Definitions Whereas modules and components represent immutable *code*, instances associate @@ -234,21 +235,12 @@ multiple different styles of traditional [linking](Linking.md). The syntax for defining a core module instance is: ```ebnf -core:instance ::= (instance ? ) +core:instance ::= (instance ? ) core:instanceexpr ::= (instantiate *) | * -core:instantiatearg ::= (with (instance )) +core:instantiatearg ::= (with (instance )) | (with (instance *)) -core:sortidx ::= ( ) -core:sort ::= func - | table - | memory - | global - | tag - | type - | module - | instance -core:inlineexport ::= (export ) +core:inlineexport ::= (export ) ``` where [`core:name`] is the WebAssembly text format's quoted string literal. @@ -261,10 +253,6 @@ modules are resolved as follows: module instance found by the first step to select the imported core definition. -Each `core:sort` corresponds 1:1 with a distinct [index space] that contains -only core definitions of that *sort*. The `u32` field of `core:sortidx` -indexes into the sort's associated index space to select a definition. - Based on this, we can link two core modules `$A` and `$B` together with the following component: ```wat @@ -291,40 +279,26 @@ an example of these, we'll also need the `alias` definitions introduced in the next section. The syntax for defining component instances is symmetric to core module -instances, but with an expanded component-level definition of `sort`: +instances above: ```ebnf instance ::= (instance ? ) instanceexpr ::= (instantiate *) | * -instantiatearg ::= (with ) +instantiatearg ::= (with ) | (with (instance *)) name ::= -sortidx ::= ( ) -sort ::= core - | func - | value πŸͺ™ - | type - | component - | instance -inlineexport ::= (export * ) -``` -Because component-level function, type and instance definitions are different -than core-level function, type and instance definitions, they are put into -disjoint index spaces which are indexed separately. Components may import -and export various core definitions (when they are compatible with the -[shared-nothing] model, which currently means only `module`, but may in the -future include `data`). Thus, component-level `sort` injects the full set -of `core:sort`, so that they may be referenced (leaving it up to validation -rules to throw out the core sorts that aren't allowed in various contexts). +inlineexport ::= (export ) + | (export ) πŸ”— +``` +Components may import and export various core definitions when they are +compatible with the [shared-nothing] model, which currently means only `module` +(since modules are pure immutable code) but may in the future include, e.g., +`data` (since data segments are pure immutable data). Component-level `name` literals reuse the Core WebAssembly text format's [`core:name`] quoted-string-literal syntax which includes literals such as `"a"`, `"β˜ƒοΈŽ"`, `"\7f"` and `"\u{7fff}"`. -πŸͺ™ The `value` sort refers to a value that is provided and consumed during -instantiation. How this works is described in the -[value definitions](#value-definitions) section. - To see a non-trivial example of component instantiation, we'll first need to introduce a few other definitions below that allow components to import, define and export component functions. @@ -338,24 +312,66 @@ there are three kinds of "targets" for an alias: the `export` of a component instance, the `core export` of a core module instance and a definition of an `outer` component (containing the current component): ```ebnf -alias ::= (alias export ( ?)) - | (alias core export (core ?)) - | (alias outer ( ?)) +alias ::= (alias export ( ?)) + | (alias core export (core-prefix() ?)) + | (alias outer ( ?)) outeraliassort ::= core module | core type | component | type ``` -If present, the `id` of the alias is bound to the new index added by the alias -and can be used anywhere a normal `id` can be used. +An `alias` definition adds a new index into the index space of the declared +sort, binding the optional `?`, if present, to the new index. -In the case of `export` aliases, validation ensures `name` is an export in the -target instance and has a matching sort. +In the case of `export` aliases, validation requires `` to select an +instance in the instance index space and `name` must match an export in the type +of that instance. For example, the following export alias allows `$P` to extract +the `foo` function of its child component `$C` and re-export it directly from +`$P`: +```wat +(component $P + (component $C + ... + (export "foo" (func $foo-impl)) + ) + (instance $c (instantiate $C)) + (alias export $c "foo" (func $foo-alias)) + (export "foo" (func $foo-alias)) +) +``` -In the case of `outer` aliases, the `u32` pair serves as a [de Bruijn index], -with the first `u32` being the number of enclosing `component`s or `type`s to -skip and the second `u32` being an index into the target's `outeraliassort`'s -index space. In particular, the first `u32` can be `0`, in which case the outer +Additional syntactic sugar is added for allowing export aliases to be defined +*inline* as a syntactic generalization of the `{X}sortidx` grammar rules +defined [above](#index-spaces) for each core- and component-level sort `X`: +```ebnf +core:{Xα΅’}sortidx ::= ... | (Xα΅’ +) (for Xα΅’ ∈ ) +{Xα΅’}sortidx ::= ... | (Xα΅’ +) (for Xα΅’ ∈ ) +``` +Each `` projects an export of an instance: the first `` projects +from `` (which must select an instance) and each subsequent `` +projects from the (instance) result of the preceding alias. Because the +`{X}sortidx` rules are included in all the core- and component-level `{X}idx` +and `externidx` rules defined [above](#index-spaces), an inline export alias `(X +$id "name1" "name2" ...)` can be used anywhere that a normal `(X $id)` can be +used, and with the same `core`-prefixing rules. + +For example, the following example is equivalent to the previous example, +avoiding the need to explicitly define `$foo-alias`: +```wat +(component $P + (component $C + ... + (export "foo" (func $foo-impl)) + ) + (instance $c (instantiate $C)) + (export "foo" (func $c "foo")) +) +``` + +In the case of `outer` aliases, the `` pair serves as a [de Bruijn index], +with the first `` being the number of enclosing `component`s or `type`s to +skip and the second `` being an index into the target's `outeraliassort`'s +index space. In particular, the first `` can be `0`, in which case the outer alias refers to the current `component`/`type`. To maintain the acyclicity of module instantiation, outer aliases are only allowed to refer to *preceding* outer definitions. `outer` aliases are currently restricted to only refer to @@ -396,37 +412,9 @@ aliased by `$E`: ) ``` -Both kinds of aliases come with syntactic sugar for implicitly declaring them -inline: - -For `export` aliases, the inline sugar extends the definition of `sortidx` -and the various sort-specific indices: -```ebnf -sortidx ::= ( ) ;; as above - | -Xidx ::= ;; as above - | -inlinealias ::= ( +) -``` -If `` refers to a ``, then the `` of `inlinealias` is a -``; otherwise it's an ``. For example, the -following snippet uses two inline function aliases: -```wat -(instance $j (instantiate $J (with "f" (func $i "f")))) -(export "x" (func $j "g" "h")) -``` -which are desugared into: -```wat -(alias export $i "f" (func $f_alias)) -(instance $j (instantiate $J (with "f" (func $f_alias)))) -(alias export $j "g" (instance $g_alias)) -(alias export $g_alias "h" (func $h_alias)) -(export "x" (func $h_alias)) -``` - -For `outer` aliases, the inline sugar is simply the identifier of the outer -definition, resolved using normal lexical scoping rules. For example, the -following component: +For `outer` aliases, there is also inline syntactic sugar, which is simply to +use the identifier of the outer definition, resolved using normal lexical +scoping rules. For example, the following component: ```wat (component (component $C ...) @@ -478,7 +466,7 @@ With what's defined so far, we're able to link modules with arbitrary renamings: )) (core instance $b3 (instantiate $B (with "a" (instance - (export "one" (func $a "three")) ;; renaming, using + (export "one" (func $a "three")) ;; renaming, using inline alias sugar )) )) ) @@ -502,7 +490,7 @@ core:moduledecl ::= | | | -core:alias ::= (alias outer (type ?)) +core:alias ::= (alias outer (type ?)) core:importdecl ::= (import ) core:exportdecl ::= (export strip-id()) @@ -605,8 +593,8 @@ defvaltype ::= bool valtype ::= | keytype ::= bool | s8 | u8 | s16 | u16 | s32 | u32 | s64 | u64 | char | string πŸ—ΊοΈ -resourcetype ::= (resource (rep i32) (dtor )?) - | (resource (rep i64) (dtor )?) 🐘 +resourcetype ::= (resource (rep i32) (dtor core-prefix())?) + | (resource (rep i64) (dtor core-prefix())?) 🐘 functype ::= (func async? (param )* (result )?) componenttype ::= (component *) instancetype ::= (instance *) @@ -618,7 +606,7 @@ instancedecl ::= core-prefix() | importdecl ::= (import * bind-id()) exportdecl ::= (export * bind-id()) -externtype ::= ( (type ) ) +externtype ::= ( (type ) ) | core-prefix() | | @@ -1361,16 +1349,16 @@ two directions: Canonical definitions specify one of these two wrapping directions, the function to wrap and a list of configuration options: ```ebnf -canon ::= (canon lift core-prefix() * bind-id()) - | (canon lower * (core func ?)) +canon ::= (canon lift core-prefix() * bind-id()) + | (canon lower * (core func ?)) canonopt ::= string-encoding=utf8 | string-encoding=utf16 | string-encoding=latin1+utf16 - | (memory ) - | (realloc ) - | (post-return ) + | (memory core-prefix()) + | (realloc core-prefix()) + | (post-return core-prefix()) | async πŸ”€ - | (callback ) πŸ”€ + | (callback core-prefix()) πŸ”€ ``` While the production `externtype` accepts any `sort`, the validation rules for `canon lift` would only allow the `func` sort. In the future, other sorts @@ -1448,8 +1436,22 @@ definitions can also be written in an inverted form that puts the sort first: (func $g ...type... (canon lift ...)) ≑ (canon lift ... (func $g ...type...)) (core func $h (canon lower ...)) ≑ (canon lower ... (core func $h)) ``` -Note: in the future, `canon` may be generalized to define other sorts than -functions (such as types), hence the explicit `sort`. + +For example: +```wat +(canon lower (func $f) + (realloc (core func $libc "realloc")) (memory (core memory $libc "mem")) + (core func $f_lower)) +``` +can be equivalently written: +```wat +(core func $f_lower + (canon lower (func $f) + (realloc (core func $libc "realloc")) (memory (core memory $libc "mem")))) +``` +As the latter demonstrates, the `core-prefix`ed `canonopt` rules defined above +still require a `core` prefix even though they are now lexically nested inside a +`(core ...)` expression (which the text format otherwise avoids). Using canonical function definitions, we can finally write a non-trivial component that takes a string, does some logging, then returns a string. @@ -1465,7 +1467,7 @@ component that takes a string, does some logging, then returns a string. (core instance $libc (instantiate $Libc)) (core func $log (canon lower (func $logging "log") - (memory (core memory $libc "mem")) (realloc (func $libc "realloc")) + (memory (core memory $libc "mem")) (realloc (core func $libc "realloc")) )) (core module $Main (import "libc" "memory" (memory 1)) @@ -1481,7 +1483,7 @@ component that takes a string, does some logging, then returns a string. )) (func $run (param string) (result string) (canon lift (core func $main "run") - (memory (core memory $libc "mem")) (realloc (func $libc "realloc")) + (memory (core memory $libc "mem")) (realloc (core func $libc "realloc")) )) (export "run" (func $run)) ) @@ -1515,8 +1517,8 @@ canon ::= ... | (canon task.return (result )? * (core func ?)) πŸ”€ | (canon task.cancel (core func ?)) πŸ”€ | (canon waitable-set.new (core func ?)) πŸ”€ - | (canon waitable-set.wait cancellable? (memory ) (core func ?)) πŸ”€ - | (canon waitable-set.poll cancellable? (memory ) (core func ?)) πŸ”€ + | (canon waitable-set.wait cancellable? (memory core-prefix()) (core func ?)) πŸ”€ + | (canon waitable-set.poll cancellable? (memory core-prefix()) (core func ?)) πŸ”€ | (canon waitable-set.drop (core func ?)) πŸ”€ | (canon waitable.join (core func ?)) πŸ”€ | (canon subtask.cancel async? (core func ?)) πŸ”€ @@ -1536,7 +1538,7 @@ canon ::= ... | (canon future.drop-readable (core func ?)) πŸ”€ | (canon future.drop-writable (core func ?)) πŸ”€ | (canon thread.index (core func ?)) 🧡 - | (canon thread.new-indirect (core func ?)) 🧡 + | (canon thread.new-indirect core-prefix() core-prefix() (core func ?)) 🧡 | (canon thread.resume-later (core func ?)) 🧡 | (canon thread.suspend cancellable? (core func ?)) 🧡 | (canon thread.yield cancellable? (core func ?)) πŸ”€ @@ -1547,8 +1549,8 @@ canon ::= ... | (canon error-context.new * (core func ?)) πŸ“ | (canon error-context.debug-message * (core func ?)) πŸ“ | (canon error-context.drop (core func ?)) πŸ“ - | (canon thread.spawn-ref shared? (core func ?)) πŸ§΅β‘‘ - | (canon thread.spawn-indirect shared? (core func ?)) πŸ§΅β‘‘ + | (canon thread.spawn-ref shared? core-prefix() (core func ?)) πŸ§΅β‘‘ + | (canon thread.spawn-indirect shared? core-prefix() core-prefix() (core func ?)) πŸ§΅β‘‘ | (canon thread.available-parallelism shared? (core func ?)) πŸ§΅β‘‘ ``` @@ -1609,7 +1611,7 @@ allowing it to create and return new resources to its client: (component (import "Libc" (core module $Libc ...)) (core instance $libc (instantiate $Libc)) - (type $R (resource (rep i32) (dtor (func $libc "free")))) + (type $R (resource (rep i32) (dtor (core func $libc "free")))) (core func $R_new (param i32) (result i32) (canon resource.new $R) ) @@ -2554,7 +2556,7 @@ exported string at instantiation time: (core instance $main (instantiate $Main (with "libc" (instance $libc)))) (func $start (param string) (result string) (canon lift (core func $main "start") - (memory (core memory $libc "mem")) (realloc (func $libc "realloc")) + (memory (core memory $libc "mem")) (realloc (core func $libc "realloc")) )) (start $start (value $name) (result (value $greeting))) (export "greeting" (value $greeting)) @@ -2590,13 +2592,13 @@ definitions append a new element to the index space of the imported/exported case of imports, the identifier is bound just like Core WebAssembly, as part of the `externtype` (e.g., `(import "x" (func $x))` binds the identifier `$x`). In the case of exports, the `?` right after the `export` is bound while the -`` inside the `` is a reference to the preceding definition being +`` inside the `` is a reference to the preceding definition being exported (e.g., `(export $x "x" (func $f))` binds a new identifier `$x`). Given this, the syntax of imports and exports are defined as follows: ```ebnf import ::= (import * bind-id()) -export ::= (export ? * ?) +export ::= (export ? * ?) attribute ::= πŸ”— | 🏷️ | 🏷️ @@ -2606,8 +2608,8 @@ externid ::= (external-id ) 🏷️ ``` Imports and exports both have a *name* (`externnamelit`) and a list of *attributes* (`attribute*`). Imports always include a type (`externtype`). -Exports always refer to a preceding definition (`sortidx`) whose type is used by -default as the type of the export. However, this "inferred" type can be +Exports always refer to a preceding definition (`externidx`) whose type is used +by default as the type of the export. However, this "inferred" type can be overridden with an optional type ascription (`externtype`) which must be compatible with the exported definition's type. @@ -3233,12 +3235,11 @@ For some use-case-focused, worked examples, see: [Structure Section]: https://webassembly.github.io/spec/core/syntax/index.html [Text Format Section]: https://webassembly.github.io/spec/core/text/index.html [Binary Format Section]: https://webassembly.github.io/spec/core/binary/index.html -[Core Indices]: https://webassembly.github.io/spec/core/syntax/modules.html#indices -[Core Identifiers]: https://webassembly.github.io/spec/core/text/values.html#text-id - [Index Space]: https://webassembly.github.io/spec/core/syntax/modules.html#indices [Abbreviations]: https://webassembly.github.io/spec/core/text/conventions.html#abbreviations +[`core:id`]: https://webassembly.github.io/spec/core/text/values.html#text-id +[`core:externidx`]: https://webassembly.github.io/spec/core/text/modules.html#text-externidx [`core:i64`]: https://webassembly.github.io/spec/core/text/values.html#text-int [`core:f64`]: https://webassembly.github.io/spec/core/syntax/values.html#floating-point [`core:stringchar`]: https://webassembly.github.io/spec/core/text/values.html#text-string diff --git a/design/mvp/examples/SharedEverythingDynamicLinking.md b/design/mvp/examples/SharedEverythingDynamicLinking.md index bfa19854..44f7fc86 100644 --- a/design/mvp/examples/SharedEverythingDynamicLinking.md +++ b/design/mvp/examples/SharedEverythingDynamicLinking.md @@ -159,7 +159,7 @@ would look like: )) (func $zip (param (list u8)) (result (list u8)) (canon lift (core func $main "zip") - (memory (core memory $libc "memory")) (realloc (func $libc "realloc")) + (memory (core memory $libc "memory")) (realloc (core func $libc "realloc")) )) (export "zip" (func $zip)) ) @@ -238,7 +238,7 @@ component-aware `clang`, the resulting component would look like: )) (func $transform (param (list u8)) (result (list u8)) (canon lift (core func $main "transform") - (memory (core memory $libc "memory")) (realloc (func $libc "realloc")) + (memory (core memory $libc "memory")) (realloc (core func $libc "realloc")) )) (export "transform" (func $transform)) ) @@ -285,11 +285,11 @@ components. The resulting component could look like: (core instance $libc (instantiate (module $Libc))) (core func $zip (canon lower (func $zipper "zip") - (memory (core memory $libc "memory")) (realloc (func $libc "realloc")) + (memory (core memory $libc "memory")) (realloc (core func $libc "realloc")) )) (core func $transform (canon lower (func $imgmgk "transform") - (memory (core memory $libc "memory")) (realloc (func $libc "realloc")) + (memory (core memory $libc "memory")) (realloc (core func $libc "realloc")) )) (core instance $main (instantiate (module $Main) (with "libc" (instance $libc)) @@ -298,7 +298,7 @@ components. The resulting component could look like: )) (func $run (param string) (result string) (canon lift (core func $main "run") - (memory (core memory $libc "memory")) (realloc (func $libc "realloc")) + (memory (core memory $libc "memory")) (realloc (core func $libc "realloc")) )) (export "run" (func $run)) ) diff --git a/test/syntax/indices.wast b/test/syntax/indices.wast new file mode 100644 index 00000000..46548c82 --- /dev/null +++ b/test/syntax/indices.wast @@ -0,0 +1,249 @@ +;; test component instantiate parsing +(component + (component + (component $C (import "x" (func))) + (import "f" (func $f)) + (instance (instantiate $C (with "x" (func $f)))) + (instance (instantiate $C (with "x" (func 0)))) + (import "i" (instance $i (export "f" (func)))) + (instance (instantiate $C (with "x" (func $i "f")))) + (import "j" (instance $j + (export "a" (instance + (export "b" (instance + (export "f" (func)))))))) + (instance (instantiate $C (with "x" (func $j "a" "b" "f")))) + ) + + (component + (component $C (import "x" (instance))) + (import "i" (instance $i)) + (instance (instantiate $C (with "x" (instance $i)))) + (import "j" (instance $j (export "k" (instance)))) + (instance (instantiate $C (with "x" (instance $j "k")))) + ) + + (component + (component $C (import "x" (component))) + (import "c" (component $c)) + (instance (instantiate $C (with "x" (component $c)))) + (import "i" (instance $i (export "c" (component)))) + (instance (instantiate $C (with "x" (component $i "c")))) + ) + + (component + (component $C (import "x" (type (sub resource)))) + (import "t" (type $t (sub resource))) + (instance (instantiate $C (with "x" (type $t)))) + (instance (instantiate $C (with "x" (type 0)))) + (import "i" (instance $i (export "t" (type (sub resource))))) + (instance (instantiate $C (with "x" (type $i "t")))) + ) + + (component + (component $C (import "x" (core module))) + (import "m" (core module $m)) + (instance (instantiate $C (with "x" (core module $m)))) + (instance (instantiate $C (with "x" (core module 0)))) + (import "i" (instance $i (export "m" (core module)))) + (instance (instantiate $C (with "x" (core module $i "m")))) + (import "j" (instance $j + (export "a" (instance + (export "m" (core module)))))) + (instance (instantiate $C (with "x" (core module $j "a" "m")))) + ) +) + +;; test core instantiate parsing +(component + (component + (core module $M (import "" "" (func))) + (core module $E (func (export ""))) + (core instance $e (instantiate $E)) + (core instance (instantiate $M (with "" (instance $e)))) + (core instance (instantiate $M (with "" (instance 0)))) + (core instance (instantiate $M + (with "" (instance (export "" (func $e "")))))) + ) +) + +;; test canon lower parsing +(component + (component + (import "f" (func $f)) + (import "i" (instance $i (export "f" (func)))) + (import "j" (instance $j + (export "n" (instance + (export "f" (func)))))) + (canon lower (func $f) (core func)) + (canon lower (func $i "f") (core func)) + (canon lower (func 0) (core func)) + (canon lower (func $j "n" "f") (core func)) + (core func (canon lower (func 0))) + (core func (canon lower (func $f))) + (core func (canon lower (func $i "f"))) + (core func (canon lower (func $j "n" "f"))) + ) + + (component + (import "f" (func $f (param "x" string))) + (core module $L + (memory (export "mem") 1) + (func (export "realloc") (param i32 i32 i32 i32) (result i32) unreachable)) + (core instance $libc (instantiate $L)) + (alias core export $libc "mem" (core memory $mem)) + (alias core export $libc "realloc" (core func $realloc)) + (canon lower (func $f) + (memory 0) (realloc 0) + (core func)) + (canon lower (func $f) + (memory $mem) (realloc $realloc) + (core func)) + ;;(canon lower (func 0) + ;; (memory (core memory 0)) (realloc 0) + ;; (core func)) + ;;(canon lower (func 0) + ;; (memory 0) (realloc (core func 0) + ;; (core func)) + ;;(canon lower (func 0) + ;; (memory (core memory 0)) (realloc (core func 0)) + ;; (core func)) + ;;(canon lower (func $f) + ;; (memory 0) (realloc (core func $realloc)) + ;; (core func)) + ;;(canon lower (func $f) + ;; (memory (core memory $mem)) (realloc 0) + ;; (core func)) + ;;(canon lower (func $f) + ;; (memory (core memory $mem)) (realloc (core func $realloc)) + ;; (core func)) + ;;(canon lower (func $f) + ;; (memory (core memory $libc "mem")) (realloc (core func $libc "realloc")) + ;; (core func)) + (core func (canon lower (func $f) + (memory 0) (realloc 0))) + (core func (canon lower (func $f) + (memory $mem) (realloc $realloc))) + ;;(core func (canon lower (func 0) + ;; (memory (core memory 0)) + ;; (realloc (core func 0)))) + ;;(core func (canon lower (func $f) + ;; (memory (core memory $mem)) (realloc (core func $realloc)))) + ;;(core func (canon lower (func $f) + ;; (memory (core memory $libc "mem")) (realloc (core func $libc "realloc")))) + ) +) + +;; test canon lift parsing +(component + (component + (core module $M (func (export "f"))) + (core instance $m (instantiate $M)) + (alias core export $m "f" (core func $f)) + (canon lift (core func $f) (func)) + (canon lift (core func 0) (func)) + (canon lift (core func $m "f") (func)) + (func (canon lift (core func $f))) + (func (canon lift (core func 0))) + (func (canon lift (core func $m "f"))) + ) + + (component + (core module $M + (memory (export "mem") 1) + (func (export "realloc") (param i32 i32 i32 i32) (result i32) unreachable) + (func (export "run") (param i32 i32) (result i32) unreachable) + (func (export "post") (param i32)) + ) + (core instance $m (instantiate $M)) + (alias core export $m "mem" (core memory $mem)) + (alias core export $m "realloc" (core func $realloc)) + (alias core export $m "post" (core func $post)) + (canon lift (core func $m "run") + (memory 0) + (realloc 0) + (post-return 1) + (func (param "s" string) (result string))) + (canon lift (core func $m "run") + (memory $mem) + (realloc $realloc) + (post-return $post) + (func (param "s" string) (result string))) + ;;(canon lift (core func $m "run") + ;; (memory (core memory $m "mem")) + ;; (realloc $realloc) + ;; (post-return $post) + ;; (func (param "s" string) (result string))) + ;;(canon lift (core func $m "run") + ;; (memory $mem) + ;; (realloc (core func $m "realloc")) + ;; (post-return $post) + ;; (func (param "s" string) (result string))) + ;;(canon lift (core func $m "run") + ;; (memory $mem) + ;; (realloc $realloc) + ;; (post-return (core func $m "post")) + ;; (func (param "s" string) (result string))) + ;;(canon lift (core func $m "run") + ;; (memory (core memory $m "mem")) + ;; (realloc (core func $m "realloc")) + ;; (post-return (core func $m "post")) + ;; (func (param "s" string) (result string))) + ) +) + +;; test resource type parsing +(component + (component + (core module $M (func (export "dtor") (param i32))) + (core instance $m (instantiate $M)) + (alias core export $m "dtor" (core func $dtor)) + ;;(type $R1 (resource (rep i32) (dtor $dtor))) + ;;(type $R2 (resource (rep i32) (dtor 0))) + ;;(type $R3 (resource (rep i32) (dtor (core func $dtor)))) + ;;(type $R4 (resource (rep i32) (dtor (core func $m "dtor")))) + ) +) + +;; test waitable-set.wait/poll parsing +(component + (component + (core module $M (memory (export "mem") 1)) + (core instance $m (instantiate $M)) + (alias core export $m "mem" (core memory $mem)) + (canon waitable-set.wait (memory $mem) (core func)) + (canon waitable-set.wait (memory 0) (core func)) + ;;(canon waitable-set.wait (memory (core memory $mem)) (core func)) + ;;(canon waitable-set.wait (memory (core memory $m "mem")) (core func)) + ;;(canon waitable-set.poll (memory (core memory $mem)) (core func)) + ;;(canon waitable-set.poll (memory (core memory $m "mem")) (core func)) + ) +) + +;; test thread.new-indirect +(component + (component + (core type $ft (func (param i32))) + (core module $M (table (export "tbl") 1 funcref)) + (core instance $m (instantiate $M)) + (alias core export $m "tbl" (core table $tbl)) + ;;(canon thread.new-indirect 0 0 (core func)) + ;;(canon thread.new-indirect $ft $tbl (core func)) + ;;(canon thread.new-indirect $ft (core table $tbl) (core func)) + ;;(canon thread.new-indirect (core type $ft) $tbl (core func)) + ;;(canon thread.new-indirect (core type $ft) (core table $tbl) (core func)) + ;;(canon thread.new-indirect (core type $ft) (core table $m "tbl") (core func)) + ) +) + +;; test component-level typeidx in canon built-ins +(component + (component + (type $T (future u32)) + (import "i" (instance $i (export "T" (type (eq $T))))) + (canon future.new $T (core func)) + (canon future.new 0 (core func)) + ;;(canon future.new (type $T) (core func)) + ;;(canon future.new (type 0) (core func)) + ;;(canon future.new (type $i "T") (core func)) + ) +)