Modules

Indices

Indices can be given either in raw numeric form or as symbolic identifiers when bound by a respective construct. Such identifiers are looked up in the suitable space of the identifier context I.

typeidxI::=x:u32x|v:idx(ifI.types[x]=v)funcidxI::=x:u32x|v:idx(ifI.funcs[x]=v)tableidxI::=x:u32x|v:idx(ifI.tables[x]=v)memidxI::=x:u32x|v:idx(ifI.mems[x]=v)globalidxI::=x:u32x|v:idx(ifI.globals[x]=v)elemidxI::=x:u32x|v:idx(ifI.elem[x]=v)dataidxI::=x:u32x|v:idx(ifI.data[x]=v)localidxI::=x:u32x|v:idx(ifI.locals[x]=v)labelidxI::=l:u32l|v:idl(ifI.labels[l]=v)

Types

Type definitions can bind a symbolic type identifier.

type::=( type  id?  ft:functype )ft

Type Uses

A type use is a reference to a type definition. It may optionally be augmented by explicit inlined parameter and result declarations. That allows binding symbolic identifiers to name the local indices of parameters. If inline declarations are given, then their types must match the referenced function type.

typeuseI::=( type  x:typeidxI )x,I(ifI.typedefs[x]=[t1n][t2]I={locals (ϵ)n})|( type  x:typeidxI )  (t1:param)  (t2:result)x,I(ifI.typedefs[x]=[t1][t2]I={locals id(param)} well-formed)

The synthesized attribute of a typeuse is a pair consisting of both the used type index and the local identifier context containing possible parameter identifiers. The following auxiliary function extracts optional identifiers from parameters:

id(( param id?  ))=id?

Note

Both productions overlap for the case that the function type is [][]. However, in that case, they also produce the same results, so that the choice is immaterial.

The well-formedness condition on I ensures that the parameters do not contain duplicate identifiers.

Abbreviations

A typeuse may also be replaced entirely by inline parameter and result declarations. In that case, a type index is automatically inserted:

(t1:param)  (t2:result)( type  x )  param  result

where x is the smallest existing type index whose definition in the current module is the function type [t1][t2]. If no such index exists, then a new type definition of the form

( type  ( func  param  result ) )

is inserted at the end of the module.

Abbreviations are expanded in the order they appear, such that previously inserted type definitions are reused by consecutive expansions.

Imports

The descriptors in imports can bind a symbolic function, table, memory, or global identifier.

importI::=( import  mod:name  nm:name  d:importdescI ){module mod,name nm,desc d}importdescI::=( func  id?  x,I:typeuseI )func x|( table  id?  tt:tabletype )table tt|( memory  id?  mt:memtype )mem  mt|( global  id?  gt:globaltype )global gt

Abbreviations

As an abbreviation, imports may also be specified inline with function, table, memory, or global definitions; see the respective sections.

Functions

Function definitions can bind a symbolic function identifier, and local identifiers for its parameters and locals.

funcI::=( func  id?  x,I:typeuseI  (t:local)  (in:instrI) ){type x,locals t,body in end}(ifI=II{locals id(local)} well-formed)local::=( local  id?  t:valtype )t

The definition of the local identifier context I uses the following auxiliary function to extract optional identifiers from locals:

id(( local id?  ))=id?

Note

The well-formedness condition on I ensures that parameters and locals do not contain duplicate identifiers.

Abbreviations

Multiple anonymous locals may be combined into a single declaration:

(  local  valtype  )((  local  valtype  ))

Functions can be defined as imports or exports inline:

( func  id?  ( import  name1  name2 )  typeuse )( import  name1  name2  ( func  id?  typeuse ) )( func  id?  ( export  name )   )( export  name  ( func  id ) )  ( func  id   )(ifid?ϵid=id?id?=ϵid fresh)

Note

The latter abbreviation can be applied repeatedly, if “” contains additional export clauses. Consequently, a function declaration can contain any number of exports, possibly followed by an import.

Tables

Table definitions can bind a symbolic table identifier.

tableI::=( table  id?  tt:tabletype ){type tt}

Abbreviations

An element segment can be given inline with a table definition, in which case its offset is 0 and the limits of the table type are inferred from the length of the given segment:

( table  id?  reftype  ( elem  exprn:vec(elemexpr) ) )( table  id  n  n  reftype )( elem  ( table  id )  ( i32.const  0 )  reftype  vec(elemexpr) )(ifid?ϵid=id?id?=ϵid fresh)
( table  id?  reftype  ( elem  xn:vec(funcidx) ) )( table  id  n  n  reftype )( elem  ( table  id )  ( i32.const  0 )  func  vec(funcidx) )(ifid?ϵid=id?id?=ϵid fresh)

Tables can be defined as imports or exports inline:

( table  id?  ( import  name1  name2 )  tabletype )( import  name1  name2  ( table  id?  tabletype ) )( table  id?  ( export  name )   )( export  name  ( table  id ) )  ( table  id   )(ifid?ϵid=id?id?=ϵid fresh)

Note

The latter abbreviation can be applied repeatedly, if “” contains additional export clauses. Consequently, a table declaration can contain any number of exports, possibly followed by an import.

Memories

Memory definitions can bind a symbolic memory identifier.

memI::=( memory  id?  mt:memtype ){type mt}

Abbreviations

A data segment can be given inline with a memory definition, in which case its offset is 0 and the limits of the memory type are inferred from the length of the data, rounded up to page size:

( memory  id?  ( data  bn:datastring )  )( memory  id  m  m )( data  ( memory  id )  ( i32.const  0 )  datastring )(ifid?ϵid=id?id?=ϵid fresh,m=ceil(n/64Ki))

Memories can be defined as imports or exports inline:

( memory  id?  ( import  name1  name2 )  memtype )( import  name1  name2  ( memory  id?  memtype ) )( memory  id?  ( export  name )   )( export  name  ( memory  id ) )  ( memory  id   )(ifid?ϵid=id?id?=ϵid fresh)

Note

The latter abbreviation can be applied repeatedly, if “” contains additional export clauses. Consequently, a memory declaration can contain any number of exports, possibly followed by an import.

Globals

Global definitions can bind a symbolic global identifier.

globalI::=( global  id?  gt:globaltype  e:exprI ){type gt,init e}

Abbreviations

Globals can be defined as imports or exports inline:

( global  id?  ( import  name1  name2 )  globaltype )( import  name1  name2  ( global  id?  globaltype ) )( global  id?  ( export  name )   )( export  name  ( global  id ) )  ( global  id   )(ifid?ϵid=id?id?=ϵid fresh)

Note

The latter abbreviation can be applied repeatedly, if “” contains additional export clauses. Consequently, a global declaration can contain any number of exports, possibly followed by an import.

Exports

The syntax for exports mirrors their abstract syntax directly.

exportI::=( export  nm:name  d:exportdescI ){name nm,desc d}exportdescI::=( func  x:funcidxI )func x|( table  x:tableidxI )table x|( memory  x:memidxI )mem x|( global  x:globalidxI )global x

Abbreviations

As an abbreviation, exports may also be specified inline with function, table, memory, or global definitions; see the respective sections.

Start Function

A start function is defined in terms of its index.

startI::=( start  x:funcidxI ){func x}

Note

At most one start function may occur in a module, which is ensured by a suitable side condition on the module grammar.

Element Segments

Element segments allow for an optional table index to identify the table to initialize.

elemI::=( elem  id?  (et,y):elemlistI ){type et,init y,mode passive}|( elem  id?  x:tableuseI  ( offset  e:exprI )  (et,y):elemlistI ){type et,init y,mode active {table x,offset e}}( elem  id?  declare  (et,y):elemlistI ){type et,init y,mode declarative}elemlistI::=t:reftype  y:vec(elemexprI)(type t,init y)elemexprI::=( item  e:exprI )etableuseI::=( table  x:tableidxI )x

Abbreviations

As an abbreviation, a single instruction may occur in place of the offset of an active element segment or as an element expression:

( instr )( offset  instr )( instr )( item  instr )

Also, the element list may be written as just a sequence of function indices:

func  vec(funcidxI)funcref  vec(( ref.func  funcidxI ))

A table use can be omitted, defaulting to 0. Furthermore, for backwards compatibility with earlier versions of WebAssembly, if the table use is omitted, the func keyword can be omitted as well.

ϵ( table  0 )( elem  id?  ( offset  exprI )  vec(funcidxI) )( elem  id?  ( table  0 )  ( offset  exprI )  func  vec(funcidxI) )

As another abbreviation, element segments may also be specified inline with table definitions; see the respective section.

Data Segments

Data segments allow for an optional memory index to identify the memory to initialize. The data is written as a string, which may be split up into a possibly empty sequence of individual string literals.

dataI::=( data  id?  b:datastring ){init b,mode passive}|( data  id?  x:memuseI  ( offset  e:exprI )  b:datastring ){init b,mode active {memory x,offset e}}datastring::=(b:string)concat((b))memuseI::=( memory  x:memidxI )x

Note

In the current version of WebAssembly, the only valid memory index is 0 or a symbolic memory identifier resolving to the same value.

Abbreviations

As an abbreviation, a single instruction may occur in place of the offset of an active data segment:

( instr )( offset  instr )

Also, a memory use can be omitted, defaulting to 0.

ϵ( memory  0 )

As another abbreviation, data segments may also be specified inline with memory definitions; see the respective section.

Modules

A module consists of a sequence of fields that can occur in any order. All definitions and their respective bound identifiers scope over the entire module, including the text preceding them.

A module may optionally bind an identifier that names the module. The name serves a documentary role only.

Note

Tools may include the module name in the name section of the binary format.

module::=( module  id?  (m:modulefieldI) )m(ifI=idc(modulefield) well-formed)modulefieldI::=ty:type{types ty}|im:importI{imports im}|fn:funcI{funcs fn}|ta:tableI{tables ta}|me:memI{mems me}|gl:globalI{globals gl}|ex:exportI{exports ex}|st:startI{start st}|el:elemI{elems el}|da:dataI{datas da}

The following restrictions are imposed on the composition of modules: m1m2 is defined if and only if

  • m1.start=ϵm2.start=ϵ

  • m1.funcs=m1.tables=m1.mems=m1.globals=ϵm2.imports=ϵ

Note

The first condition ensures that there is at most one start function. The second condition enforces that all imports must occur before any regular definition of a function, table, memory, or global, thereby maintaining the ordering of the respective index spaces.

The well-formedness condition on I in the grammar for module ensures that no namespace contains duplicate identifiers.

The definition of the initial identifier context I uses the following auxiliary definition which maps each relevant definition to a singular context with one (possibly empty) identifier:

idc(( type id? ft:functype ))={types (id?),typedefs ft}idc(( func id?  ))={funcs (id?)}idc(( table id?  ))={tables (id?)}idc(( memory id?  ))={mems (id?)}idc(( global id?  ))={globals (id?)}idc(( elem id?  ))={elem (id?)}idc(( data id?  ))={data (id?)}idc(( import  ( func id?  ) ))={funcs (id?)}idc(( import  ( table id?  ) ))={tables (id?)}idc(( import  ( memory id?  ) ))={mems (id?)}idc(( import  ( global id?  ) ))={globals (id?)}idc((  ))={}

Abbreviations

In a source file, the toplevel (module ) surrounding the module body may be omitted.

modulefield( module  modulefield )