Embedding

A WebAssembly implementation will typically be embedded into a host environment. An embedder implements the connection between such a host environment and the WebAssembly semantics as defined in the main body of this specification. An embedder is expected to interact with the semantics in well-defined ways.

This section defines a suitable interface to the WebAssembly semantics in the form of entry points through which an embedder can access it. The interface is intended to be complete, in the sense that an embedder does not need to reference other functional parts of the WebAssembly specification directly.

Note

On the other hand, an embedder does not need to provide the host environment with access to all functionality defined in this interface. For example, an implementation may not support parsing of the text format.

Types

In the description of the embedder interface, syntactic classes from the abstract syntax and the runtime’s abstract machine are used as names for variables that range over the possible objects from that class. Hence, these syntactic classes can also be interpreted as types.

For numeric parameters, notation like n:u32 is used to specify a symbolic name in addition to the respective value range.

Errors

Failure of an interface operation is indicated by an auxiliary syntactic class:

error::=error

In addition to the error conditions specified explicitly in this section, implementations may also return errors when specific implementation limitations are reached.

Note

Errors are abstract and unspecific with this definition. Implementations can refine it to carry suitable classifications and diagnostic messages.

Pre- and Post-Conditions

Some operations state pre-conditions about their arguments or post-conditions about their results. It is the embedder’s responsibility to meet the pre-conditions. If it does, the post conditions are guaranteed by the semantics.

In addition to pre- and post-conditions explicitly stated with each operation, the specification adopts the following conventions for runtime objects (store, moduleinst, externval, addresses):

  • Every runtime object passed as a parameter must be valid per an implicit pre-condition.

  • Every runtime object returned as a result is valid per an implicit post-condition.

Note

As long as an embedder treats runtime objects as abstract and only creates and manipulates them through the interface defined here, all implicit pre-conditions are automatically met.

Store

store_init():store

  1. Return the empty store.

store_init()={funcs ϵ, mems ϵ, tables ϵ, globals ϵ}

Modules

module_decode(byte):module | error

  1. If there exists a derivation for the byte sequence byte as a module according to the binary grammar for modules, yielding a module m, then return m.

  2. Else, return error.

module_decode(b)=m(ifmodulem:b)module_decode(b)=error(otherwise)

module_parse(char):module | error

  1. If there exists a derivation for the source char as a module according to the text grammar for modules, yielding a module m, then return m.

  2. Else, return error.

module_parse(c)=m(ifmodulem:c)module_parse(c)=error(otherwise)

module_validate(module):error?

  1. If module is valid, then return nothing.

  2. Else, return error.

module_validate(m)=ϵ(ifm:externtypeexterntype)module_validate(m)=error(otherwise)

module_instantiate(store,module,externval):(store,moduleinst | error)

  1. Try instantiating module in store with external values externval as imports:

  1. If it succeeds with a module instance moduleinst, then let result be moduleinst.

  2. Else, let result be error.

  1. Return the new store paired with result.

module_instantiate(S,m,ev)=(S,F.module)(ifinstantiate(S,m,ev)S;F;ϵ)module_instantiate(S,m,ev)=(S,error)(ifinstantiate(S,m,ev)S;F;trap)

Note

The store may be modified even in case of an error.

module_imports(module):(name,name,externtype)

  1. Pre-condition: module is valid with external import types externtype and external export types externtype.

  2. Let import be the imports module.imports.

  3. Assert: the length of import equals the length of externtype.

  4. For each importi in import and corresponding externtypei in externtype, do:

  1. Let resulti be the triple (importi.module,importi.name,externtypei).

  1. Return the concatenation of all resulti, in index order.

  2. Post-condition: each externtypei is valid.

 module_imports(m)=(im.module,im.name,externtype)(ifim=m.importsm:externtypeexterntype)

module_exports(module):(name,externtype)

  1. Pre-condition: module is valid with external import types externtype and external export types externtype.

  2. Let export be the exports module.exports.

  3. Assert: the length of export equals the length of externtype.

  4. For each exporti in export and corresponding externtypei in externtype, do:

  1. Let resulti be the pair (exporti.name,externtypei).

  1. Return the concatenation of all resulti, in index order.

  2. Post-condition: each externtypei is valid.

 module_exports(m)=(ex.name,externtype)(ifex=m.exportsm:externtypeexterntype)

Module Instances

instance_export(moduleinst,name):externval | error

  1. Assert: due to validity of the module instance moduleinst, all its export names are different.

  2. If there exists an exportinsti in moduleinst.exports such that name exportinsti.name equals name, then:

    1. Return the external value exportinsti.value.

  3. Else, return error.

 instance_export(m,name)=m.exports[i].value(ifm.exports[i].name=name)instance_export(m,name)=error(otherwise)

Functions

func_alloc(store,functype,hostfunc):(store,funcaddr)

  1. Pre-condition: functype is valid.

  2. Let funcaddr be the result of allocating a host function in store with function type functype and host function code hostfunc.

  3. Return the new store paired with funcaddr.

func_alloc(S,ft,code)=(S,a)(ifallochostfunc(S,ft,code)=S,a)

Note

This operation assumes that hostfunc satisfies the pre- and post-conditions required for a function instance with type functype.

Regular (non-host) function instances can only be created indirectly through module instantiation.

func_type(store,funcaddr):functype

  1. Return S.funcs[a].type.

  2. Post-condition: the returned function type is valid.

func_type(S,a)=S.funcs[a].type

func_invoke(store,funcaddr,val):(store,val | error)

  1. Try invoking the function funcaddr in store with values val as arguments:

  1. If it succeeds with values val as results, then let result be val.

  2. Else it has trapped, hence let result be error.

  1. Return the new store paired with result.

 func_invoke(S,a,v)=(S,v)(ifinvoke(S,a,v)S;F;v)func_invoke(S,a,v)=(S,error)(ifinvoke(S,a,v)S;F;trap)

Note

The store may be modified even in case of an error.

Tables

table_alloc(store,tabletype,ref):(store,tableaddr)

  1. Pre-condition: tabletype is valid.

  2. Let tableaddr be the result of allocating a table in store with table type tabletype and initialization value ref.

  3. Return the new store paired with tableaddr.

table_alloc(S,tt,r)=(S,a)(ifalloctable(S,tt,r)=S,a)

table_type(store,tableaddr):tabletype

  1. Return S.tables[a].type.

  2. Post-condition: the returned table type is valid.

table_type(S,a)=S.tables[a].type

table_read(store,tableaddr,i:u32):ref | error

  1. Let ti be the table instance store.tables[tableaddr].

  2. If i is larger than or equal to the length of ti.elem, then return error.

  3. Else, return the reference value ti.elem[i].

table_read(S,a,i)=r(ifS.tables[a].elem[i]=r)table_read(S,a,i)=error(otherwise)

table_write(store,tableaddr,i:u32,ref):store | error

  1. Let ti be the table instance store.tables[tableaddr].

  2. If i is larger than or equal to the length of ti.elem, then return error.

  3. Replace ti.elem[i] with the reference value ref.

  4. Return the updated store.

table_write(S,a,i,r)=S(ifS=Swithtables[a].elem[i]=r)table_write(S,a,i,r)=error(otherwise)

table_size(store,tableaddr):u32

  1. Return the length of store.tables[tableaddr].elem.

 table_size(S,a)=n(if|S.tables[a].elem|=n)

table_grow(store,tableaddr,n:u32,ref):store | error

  1. Try growing the table instance store.tables[tableaddr] by n elements with initialization value ref:

    1. If it succeeds, return the updated store.

    2. Else, return error.

 table_grow(S,a,n,r)=S(ifS=Swithtables[a]=growtable(S.tables[a],n,r))table_grow(S,a,n,r)=error(otherwise)

Memories

mem_alloc(store,memtype):(store,memaddr)

  1. Pre-condition: memtype is valid.

  2. Let memaddr be the result of allocating a memory in store with memory type memtype.

  3. Return the new store paired with memaddr.

mem_alloc(S,mt)=(S,a)(ifallocmem(S,mt)=S,a)

mem_type(store,memaddr):memtype

  1. Return S.mems[a].type.

  2. Post-condition: the returned memory type is valid.

mem_type(S,a)=S.mems[a].type

mem_read(store,memaddr,i:u32):byte | error

  1. Let mi be the memory instance store.mems[memaddr].

  2. If i is larger than or equal to the length of mi.data, then return error.

  3. Else, return the byte mi.data[i].

mem_read(S,a,i)=b(ifS.mems[a].data[i]=b)mem_read(S,a,i)=error(otherwise)

mem_write(store,memaddr,i:u32,byte):store | error

  1. Let mi be the memory instance store.mems[memaddr].

  2. If u32 is larger than or equal to the length of mi.data, then return error.

  3. Replace mi.data[i] with byte.

  4. Return the updated store.

mem_write(S,a,i,b)=S(ifS=Swithmems[a].data[i]=b)mem_write(S,a,i,b)=error(otherwise)

mem_size(store,memaddr):u32

  1. Return the length of store.mems[memaddr].data divided by the page size.

 mem_size(S,a)=n(if|S.mems[a].data|=n64Ki)

mem_grow(store,memaddr,n:u32):store | error

  1. Try growing the memory instance store.mems[memaddr] by n pages:

    1. If it succeeds, return the updated store.

    2. Else, return error.

 mem_grow(S,a,n)=S(ifS=Swithmems[a]=growmem(S.mems[a],n))mem_grow(S,a,n)=error(otherwise)

Globals

global_alloc(store,globaltype,val):(store,globaladdr)

  1. Pre-condition: globaltype is valid.

  2. Let globaladdr be the result of allocating a global in store with global type globaltype and initialization value val.

  3. Return the new store paired with globaladdr.

global_alloc(S,gt,v)=(S,a)(ifallocglobal(S,gt,v)=S,a)

global_type(store,globaladdr):globaltype

  1. Return S.globals[a].type.

  2. Post-condition: the returned global type is valid.

global_type(S,a)=S.globals[a].type

global_read(store,globaladdr):val

  1. Let gi be the global instance store.globals[globaladdr].

  2. Return the value gi.value.

global_read(S,a)=v(ifS.globals[a].value=v)

global_write(store,globaladdr,val):store | error

  1. Let gi be the global instance store.globals[globaladdr].

  2. Let mut t be the structure of the global type gi.type.

  3. If mut is not var, then return error.

  4. Replace gi.value with the value val.

  5. Return the updated store.

 global_write(S,a,v)=S(ifS.globals[a].type=var tS=Swithglobals[a].value=v)global_write(S,a,v)=error(otherwise)