This document defines the binary format for the AST defined in the
explainer. The top-level production is component and the
convention is that a file suffixed in .wasm may contain either a
core:module or a component, using the layer field to discriminate
between the two in the first 8 bytes (see below for
more details).
Note: this document is not meant to completely define the decoding or validation rules, but rather merge the minimal need-to-know elements of both, with just enough detail to create a prototype. A complete definition of the binary format and validation will be present in the formal specification.
See the explainer introduction for an explanation of 🪙.
(See Component Definitions in the explainer.)
component ::= <preamble> s*:<section>* => (component flatten(s*))
preamble ::= <magic> <version> <layer>
magic ::= 0x00 0x61 0x73 0x6D
version ::= 0x0d 0x00
layer ::= 0x01 0x00
section ::= section_0(<core:custom>) => ϵ
| m:section_1(<core:module>) => [core-prefix(m)]
| i*:section_2(vec(<core:instance>)) => core-prefix(i)*
| t*:section_3(vec(<core:type>)) => core-prefix(t)*
| c: section_4(<component>) => [c]
| i*:section_5(vec(<instance>)) => i*
| a*:section_6(vec(<alias>)) => a*
| t*:section_7(vec(<type>)) => t*
| c*:section_8(vec(<canon>)) => c*
| s: section_9(<start>) => [s]
| i*:section_10(vec(<import>)) => i*
| e*:section_11(vec(<export>)) => e*
| v*:section_12(vec(<value>)) => v* 🪙
Notes:
core:section, core:custom, core:modulecore-prefix(t) meta-function inserts a core token after the leftmost
paren of t (e.g., core-prefix( (module (func)) ) is (core module (func))).version given above is pre-standard. As the proposal changes before
final standardization, version will be bumped from 0x0d upwards to
coordinate prototypes. When the standard is finalized, version will be
changed one last time to 0x1. (This mirrors the path taken for the Core
WebAssembly 1.0 spec.)layer field is meant to distinguish modules from components early in
the binary format. (Core WebAssembly modules already implicitly have a
layer field of 0x0 if the existing 4-byte core:version field is
reinterpreted as two 2-byte fields. This implies that the Core WebAssembly
spec needs to make a backwards-compatible spec change to split core:version
and fix layer to forever be 0x0.)(See Instance Definitions in the explainer.)
core:instance ::= ie:<core:instanceexpr> => (instance ie)
core:instanceexpr ::= 0x00 m:<moduleidx> arg*:vec(<core:instantiatearg>) => (instantiate m arg*)
| 0x01 e*:vec(<core:inlineexport>) => e*
core:instantiatearg ::= n:<core:name> 0x12 i:<instanceidx> => (with n (instance i))
core:sortidx ::= sort:<core:sort> idx:<u32> => (sort idx)
core:sort ::= 0x00 => func
| 0x01 => table
| 0x02 => memory
| 0x03 => global
| 0x10 => type
| 0x11 => module
| 0x12 => instance
core:inlineexport ::= n:<core:name> si:<core:sortidx> => (export n si)
instance ::= ie:<instanceexpr> => (instance ie)
instanceexpr ::= 0x00 c:<componentidx> arg*:vec(<instantiatearg>) => (instantiate c arg*)
| 0x01 e*:vec(<inlineexport>) => e*
instantiatearg ::= n:<name> si:<sortidx> => (with n si)
name ::= n:<core:name> => n
sortidx ::= sort:<sort> idx:<u32> => (sort idx)
sort ::= 0x00 cs:<core:sort> => core cs
| 0x01 => func
| 0x02 => value 🪙
| 0x03 => type
| 0x04 => component
| 0x05 => instance
inlineexport ::= n:<exportname> si:<sortidx> => (export n si)
Notes:
core:name, (variable-length encoded) core:u32core:sort values are chosen to match the discriminant opcodes of
core:importdesc.type is added to core:sort in anticipation of the type-imports proposal. Until that
proposal, core modules won’t be able to actually import or export types, however, the
type sort is allowed as part of outer aliases (below).module and instance are added to core:sort in anticipation of the module-linking
proposal, which would add these types to Core WebAssembly. Until then, they are useful
for aliases (below).core:instantiatearg initially only allows the instance
sort, but would be extended to accept other sorts as core wasm is extended.instantiate requires each <importname> in c to match a
name in a with argument (compared as strings) and for the types to
match.instantiate, after each individual type-import is supplied
via with, the actual type supplied is immediately substituted for all uses
of the import, so that subsequent imports and all exports are now specialized
to the actual type.sortidx are validated according to their sort’s index
spaces, which are built incrementally as each definition is validated.(See Alias Definitions in the explainer.)
alias ::= s:<sort> t:<aliastarget> => (alias t (s))
aliastarget ::= 0x00 i:<instanceidx> n:<name> => export i n
| 0x01 i:<core:instanceidx> n:<core:name> => core export i n
| 0x02 ct:<u32> idx:<u32> => outer ct idx
Notes:
core:u32export aliases, i is validated to refer to an instance in the
instance index space that exports n with the specified sort.outer aliases, ct is validated to be less or equal than the number
of enclosing components and i is validated to be a valid
index in the sort index space of the ith enclosing component (counting
outward, starting with 0 referring to the current component).outer aliases, validation restricts the sort to one
of type, module or component and additionally requires that the
outer-aliased type is not a resource type (which is generative).(See Type Definitions in the explainer.)
core:type ::= dt:<core:deftype> => (type dt) (GC proposal)
core:deftype ::= ft:<core:functype> => ft (WebAssembly 1.0)
| st:<core:structtype> => st (GC proposal)
| at:<core:arraytype> => at (GC proposal)
| mt:<core:moduletype> => mt
core:moduletype ::= 0x50 md*:vec(<core:moduledecl>) => (module md*)
core:moduledecl ::= 0x00 i:<core:import> => i
| 0x01 t:<core:type> => t
| 0x02 a:<core:alias> => a
| 0x03 e:<core:exportdecl> => e
core:alias ::= s:<core:sort> t:<core:aliastarget> => (alias t (s))
core:aliastarget ::= 0x01 ct:<u32> idx:<u32> => outer ct idx
core:importdecl ::= i:<core:import> => i
core:exportdecl ::= n:<core:name> d:<core:importdesc> => (export n d)
Notes:
core:import, core:importdesc, core:functypecore:moduledecl rejects core:moduletype definitions
and outer aliases of core:moduletype definitions inside type
declarators. Thus, as an invariant, when validating a core:moduletype, the
core type index space will not contain any core module types.alias declarators currently only allow outer type aliases but
would add export aliases when core wasm adds type exports.type ::= dt:<deftype> => (type dt)
deftype ::= dvt:<defvaltype> => dvt
| ft:<functype> => ft
| ct:<componenttype> => ct
| it:<instancetype> => it
primvaltype ::= 0x7f => bool
| 0x7e => s8
| 0x7d => u8
| 0x7c => s16
| 0x7b => u16
| 0x7a => s32
| 0x79 => u32
| 0x78 => s64
| 0x77 => u64
| 0x76 => f32
| 0x75 => f64
| 0x74 => char
| 0x73 => string
defvaltype ::= pvt:<primvaltype> => pvt
| 0x72 lt*:vec(<labelvaltype>) => (record (field lt)*) (if |lt*| > 0)
| 0x71 case*:vec(<case>) => (variant case+) (if |case*| > 0)
| 0x70 t:<valtype> => (list t)
| 0x6f t*:vec(<valtype>) => (tuple t+) (if |t*| > 0)
| 0x6e l*:vec(<label'>) => (flags l+) (if |l*| > 0)
| 0x6d l*:vec(<label'>) => (enum l+) (if |l*| > 0)
| 0x6b t:<valtype> => (option t)
| 0x6a t?:<valtype>? u?:<valtype>? => (result t? (error u)?)
| 0x69 i:<typeidx> => (own i)
| 0x68 i:<typeidx> => (borrow i)
labelvaltype ::= l:<label'> t:<valtype> => l t
case ::= l:<label'> t?:<valtype>? 0x00 => (case l t?)
label' ::= len:<u32> l:<label> => l (if len = |l|)
<T>? ::= 0x00 =>
| 0x01 t:<T> => t
valtype ::= i:<typeidx> => i
| pvt:<primvaltype> => pvt
resourcetype ::= 0x3f 0x7f f?:<funcidx>? => (resource (rep i32) (dtor f)?)
functype ::= 0x40 ps:<paramlist> rs:<resultlist> => (func ps rs)
paramlist ::= lt*:vec(<labelvaltype>) => (param lt)*
resultlist ::= 0x00 t:<valtype> => (result t)
| 0x01 lt*:vec(<labelvaltype>) => (result lt)*
componenttype ::= 0x41 cd*:vec(<componentdecl>) => (component cd*)
instancetype ::= 0x42 id*:vec(<instancedecl>) => (instance id*)
componentdecl ::= 0x03 id:<importdecl> => id
| id:<instancedecl> => id
instancedecl ::= 0x00 t:<core:type> => t
| 0x01 t:<type> => t
| 0x02 a:<alias> => a
| 0x04 ed:<exportdecl> => ed
importdecl ::= in:<importname'> ed:<externdesc> => (import in ed)
exportdecl ::= en:<exportname'> ed:<externdesc> => (export en ed)
externdesc ::= 0x00 0x11 i:<core:typeidx> => (core module (type i))
| 0x01 i:<typeidx> => (func (type i))
| 0x02 b:<valuebound> => (value b) 🪙
| 0x03 b:<typebound> => (type b)
| 0x04 i:<typeidx> => (component (type i))
| 0x05 i:<typeidx> => (instance (type i))
typebound ::= 0x00 i:<typeidx> => (eq i)
| 0x01 => (sub resource)
valuebound ::= 0x00 i:<valueidx> => (eq i) 🪙
| 0x01 t:<valtype> => t 🪙
Notes:
0x7f) and going down,
reserving the nonnegative SLEB128s for type indices.valtype requires the typeidx to refer to a defvaltype.own and borrow requires the typeidx to refer to a
resource type.functype rejects any transitive use of borrow in a
result type. Similarly, validation of components and component types
rejects any transitive use of borrow in an exported value type.resourcetype requires the destructor (if present) to have
type [i32] -> [].instancedecl (currently) only allows the type and
instance sorts in alias declarators.exportdecl introduces a new type index that can be used by subsequent type
definitions. In the (eq i) case, the new type index is effectively an alias
to type i. In the (sub resource) case, the new type index refers to a
fresh abstract type unequal to every existing type in all existing type
index spaces. (Note: subsequent aliases can introduce new type indices
equivalent to this fresh type.)resourcetype type definitions inside componenttype and
instancettype. Thus, handle types inside a componenttype can only refer
to resource types that are imported or exported.externdesc requires the various typeidx type constructors
to match the preceding sort.0x00 immediate of case may be reinterpreted in the future as the
none case of an optional immediate.)(See Canonical Definitions in the explainer.)
canon ::= 0x00 0x00 f:<core:funcidx> opts:<opts> ft:<typeidx> => (canon lift f opts type-index-space[ft])
| 0x01 0x00 f:<funcidx> opts:<opts> => (canon lower f opts (core func))
| 0x02 rt:<typeidx> => (canon resource.new rt (core func))
| 0x03 rt:<typeidx> => (canon resource.drop rt (core func))
| 0x04 rt:<typeidx> => (canon resource.rep rt (core func))
| 0x05 ft:<typeidx> => (canon thread.spawn ft (core func))
| 0x06 => (canon thread.hw_concurrency (core func))
opts ::= opt*:vec(<canonopt>) => opt*
canonopt ::= 0x00 => string-encoding=utf8
| 0x01 => string-encoding=utf16
| 0x02 => string-encoding=latin1+utf16
| 0x03 m:<core:memidx> => (memory m)
| 0x04 f:<core:funcidx> => (realloc f)
| 0x05 f:<core:funcidx> => (post-return f)
Notes:
0x00 byte in canon stands for the func sort and thus the
0x00 <u32> pair standards for a func sortidx or core:sortidx.canonopt.CanonicalABI.md.(See Start Definitions in the explainer.)
start ::= f:<funcidx> arg*:vec(<valueidx>) r:<u32> => (start f (value arg)* (result (value))ʳ)
Notes:
f have functype with param arity and types matching arg*
and result arity r.result types of f to the value index space (making
them available for reference by subsequent definitions).In addition to the type-compatibility checks mentioned above, the validation
rules for value definitions additionally require that each value is consumed
exactly once. Thus, during validation, each value has an associated “consumed”
boolean flag. When a value is first added to the value index space (via
import, instance, alias or start), the flag is clear. When a value is
used (via export, instantiate or start), the flag is set. After
validating the last definition of a component, validation requires all values’
flags are set.
(See Import and Export Definitions in the explainer.)
import ::= in:<importname'> ed:<externdesc> => (import in ed)
export ::= en:<exportname'> si:<sortidx> ed?:<externdesc>? => (export en si ed?)
importname' ::= 0x00 len:<u32> in:<importname> => in (if len = |in|)
exportname' ::= 0x00 len:<u32> en:<exportname> => en (if len = |en|)
Notes:
sorts) introduce a new index that aliases the exported
definition and can be used by all subsequent definitions just like an alias.importdecl or exportdecl.sortidx to have a valid externdesc
(which disallows core sorts other than core module). When the optional
externdesc immediate is present, validation requires it to be a supertype
of the inferred externdesc of the sortidx.<importname> and <exportname> refer to the productions defined in the
text format.<importname>s of a component must be unique and the <exportname>s of
a component must be unique as well (defined in terms of raw string equality).plainnames only occur on func imports
or exports and that the first label of a [constructor], [method] or
[static] matches the plainname of a preceding resource import or
export, respectively, in the same scope (component, component type or
instance type).[constructor] names requires that the func returns a
(result (own $R)), where $R is the resource labeled r.[method] names requires the first parameter of the function
to be (param "self" (borrow $R)), where $R is the resource labeled r.[method] and [static] names ensures that all field names
are disjoint.<valid semver> is as defined by https://semver.org<integrity-metadata> is as defined by the
SRI spec.(See Value Definitions in the explainer.)
value ::= t:<valtype> len:<core:u32> v:<val(t)> => (value t v) (where len = ||v||)
val(bool) ::= 0x00 => false
| 0x01 => true
val(u8) ::= v:<core:byte> => v
val(s8) ::= v:<core:byte> => v' (where v' = v if v < 128 else (v - 256))
val(s16) ::= v:<core:s16> => v
val(u16) ::= v:<core:u16> => v
val(s32) ::= v:<core:s32> => v
val(u32) ::= v:<core:u32> => v
val(s64) ::= v:<core:s64> => v
val(u64) ::= v:<core:u64> => v
val(f32) ::= v:<core:f32> => v (if !isnan(v))
| 0x00 0x00 0xC0 0x7F => nan
val(f64) ::= v:<core:f64> => v (if !isnan(v))
| 0x00 0x00 0x00 0x00 0x00 0x00 0xF8 0x7F => nan
val(char) ::= b*:<core:byte>* => c (where b* = core:utf8(c))
val(string) ::= v:<core:name> => v
val(i:<typeidx>) ::= v:<val(type-index-space[i])> => v
val((record (field l t)+)) ::= v+:<val(t)>+ => (record v+)
val((variant (case l t?)+) ::= i:<core:u32> v?:<val(t[i])>? => (variant l[i] v?)
val((list t)) ::= v:vec(<val(t)>) => (list v)
val((tuple t+)) ::= v+:<val(t)>+ => (tuple v+)
val((flags l+)) ::= (v:<core:byte>)^N => (flags (l[i] for i in 0..|l+|-1 if v[floor(i / 8)] & 2^(i mod 8) > 0)) (where N = ceil(|l+| / 8))
val((enum l+)) ::= i:<core:u32> => (enum l[i])
val((option t)) ::= 0x00 => none
| 0x01 v:<val(t)> => (some v)
val((result)) ::= 0x00 => ok
| 0x01 => error
val((result t)) ::= 0x00 v:<val(t)> => (ok v)
| 0x01 => error
val((result (error u))) ::= 0x00 => ok
| 0x01 v:<val(u)> => (error v)
val((result t (error u))) ::= 0x00 v:<val(t)> => (ok v)
| 0x01 v:<val(u)> => (error v)
Notes:
& operator is used to denote bitwise AND operation, which performs AND on every bit of two numbers in their binary formisnan is a function, which takes a floating point number as a parameter and returns true iff it represents a NaN as defined in IEEE 754 standard||B|| is the length of the byte sequence generated from the production B in a derivation as defined in Core convention auxilary notationLike the core wasm name
section
a similar name custom section is specified here for components to be able to
name all the declarations that can happen within a component. Similarly like its
core wasm counterpart validity of this custom section is not required and
engines should not reject components which have an invalid name section.
namesec ::= section_0(namedata)
namedata ::= n:<name> (if n = 'component-name')
name:<componentnamesubsec>?
sortnames*:<sortnamesubsec>*
namesubsection_N(B) ::= N:<byte> size:<u32> B (if size == |B|)
componentnamesubsec ::= namesubsection_0(<name>)
sortnamesubsec ::= namesubsection_1(<sortnames>)
sortnames ::= sort:<sort> names:<namemap>
namemap ::= names:vec(<nameassoc>)
nameassoc ::= idx:<u32> name:<name>
where namemap is the same as for core wasm. A particular sort should only
appear once within a name section, for example component instances can only be
named once.