模块

WebAssembly 程序被组织成模块,它是部署、加载和编译的单位。模块收集了对 类型函数表格内存全局变量 的定义。此外,它可以声明 导入导出,并提供初始化形式的 数据元素 段,或一个 开始函数

module::={types vec(functype),funcs vec(func),tables vec(table),mems vec(mem),globals vec(global),elems vec(elem),datas vec(data),start start?,imports vec(import),exports vec(export)}

每个向量(以及整个模块)都可能是空的。

索引

定义用以零为基准的索引来引用。每个定义类别都有自己的索引空间,由以下类别区分。

typeidx::=u32funcidx::=u32tableidx::=u32memidx::=u32globalidx::=u32elemidx::=u32dataidx::=u32localidx::=u32labelidx::=u32

对于 函数表格内存全局变量 的索引空间,包括在同一模块中声明的相应 导入。这些导入的索引在同一索引空间中其他定义的索引之前。

元素索引引用 元素段,而数据索引引用 数据段

对于 局部变量 的索引空间,只能在 函数 内部访问,并包含该函数的参数,它们在局部变量之前。

标签索引引用指令序列中的 结构化控制指令

约定

  • 元变量 l 涵盖标签索引。

  • 元变量 x,y 涵盖任何其他索引空间中的索引。

  • 符号 idx(A) 表示在 A 中自由出现的来自索引空间 idx 的索引集。有时这个集合被重新解释为其元素的 向量

注意

例如,如果 instr(data.drop x)(memory.init y),那么 dataidx(instr)={x,y},或者等效地,向量 x y

类型

模块的 types 组件定义了一个 函数类型 向量。

模块中使用的所有函数类型都必须在此组件中定义。它们通过 类型索引 来引用。

注意

WebAssembly 的未来版本可能会添加其他形式的类型定义。

函数

模块的 funcs 组件定义了一个具有以下结构的函数向量

func::={type typeidx,locals vec(valtype),body expr}

函数的 type 通过引用模块中定义的 类型 来声明其签名。函数的参数通过函数体中的以 0 为基准的 局部变量索引 来引用;它们是可变的。

The locals 声明一个可变局部变量向量及其类型。这些变量通过函数体中的 局部变量索引 来引用。第一个局部变量的索引是最小的不引用参数的索引。

The body 是一个 指令 序列,在终止时必须生成与函数类型 结果类型 匹配的堆栈。

函数通过 函数索引 来引用,从最小的不引用函数 导入 的索引开始。

表格

模块的 tables 组件定义了一个由其 表格类型 描述的表格向量

table::={type tabletype}

表格是特定 引用类型 的不透明值的向量。表格类型 限制 中的 min 大小指定该表格的初始大小,而其 max(如果存在)则限制其以后可以增长到的大小。

表格可以通过 元素段 来初始化。

表格通过 表格索引 来引用,从最小的不引用表格 导入 的索引开始。大多数构造隐式引用表格索引 0

内存

模块的 mems 组件定义了一个由其 内存类型 描述的线性内存(或简称为内存)向量

mem::={type memtype}

内存是原始未解释字节的向量。内存类型 限制 中的 min 大小指定该内存的初始大小,而其 max(如果存在)则限制其以后可以增长到的大小。两者都以 页面大小 为单位。

内存可以通过 数据段 来初始化。

内存通过 内存索引 来引用,从最小的不引用内存 导入 的索引开始。大多数构造隐式引用内存索引 0

注意

在当前版本的 WebAssembly 中,单个模块最多只能定义或导入一个内存,并且 *所有* 结构都隐式引用此内存 0。此限制可能会在未来版本中取消。

全局变量

模块的 globals 组件定义了一个全局变量(简称全局变量)的向量。

global::={type globaltype,init expr}

每个全局变量存储给定 全局类型 的单个值。它的 type 还指定全局变量是不可变还是可变。此外,每个全局变量都使用 常量 初始化器 表达式 给出的 init 值进行初始化。

全局变量通过 全局索引 引用,从不引用全局 导入 的最小索引开始。

元素段

表的初始内容未初始化。*元素段* 可用于从静态 向量 中的元素初始化表的子范围。

模块的 elems 组件定义了一个元素段向量。每个元素段都定义一个 引用类型 和一个相应的 常量 元素 表达式 列表。

元素段具有识别它们是 *被动*、*主动* 还是 *声明式* 的模式。被动元素段的元素可以使用 table.init 指令复制到表中。主动元素段在 实例化 期间将它的元素复制到表中,如 表索引 和定义表中偏移量的 常量 表达式 所指定。声明式元素段在运行时不可用,但仅用于前向声明在使用 ref.func 等指令的代码中形成的引用。

elem::={type reftype,init vec(expr),mode elemmode}elemmode::=passive|active {table tableidx,offset expr}|declarative

offset常量 表达式 给出。

元素段通过 元素索引 引用。

数据段

内存 的初始内容为零字节。*数据段* 可用于从静态 向量 中的 字节 初始化内存范围。

模块的 datas 组件定义了一个数据段向量。

与元素段类似,数据段具有识别它们是 *被动* 还是 *主动* 的模式。被动数据段的内容可以使用 memory.init 指令复制到内存中。主动数据段在 实例化 期间将它的内容复制到内存中,如 内存索引 和定义内存中偏移量的 常量 表达式 所指定。

data::={init vec(byte),mode datamode}datamode::=passive|active {memory memidx,offset expr}

数据段通过 数据索引 引用。

注意

在当前版本的 WebAssembly 中,模块中最多只能允许一个内存。因此,唯一有效的 memidx0

开始函数

模块的 start 组件声明了 *开始函数* 的 函数索引,该函数在模块 实例化 时自动调用,在 内存 初始化后。

start::={func funcidx}

注意

开始函数旨在初始化模块的状态。在初始化完成之前,模块及其导出不能在外部访问。

导出

模块的 exports 组件定义了一组 *导出*,这些导出在模块 实例化 后将可供主机环境访问。

export::={name name,desc exportdesc}exportdesc::=func funcidx|table tableidx|mem memidx|global globalidx

每个导出都用一个唯一的 名称 进行标记。可导出定义是 函数内存全局变量,它们通过相应的描述符引用。

约定

为导出序列定义以下辅助符号,以保持顺序,过滤掉特定类型的索引。

  • funcs(export)=[funcidx | func funcidx(export.desc)]

  • tables(export)=[tableidx | table tableidx(export.desc)]

  • mems(export)=[memidx | mem memidx(export.desc)]

  • globals(export)=[globalidx | global globalidx(export.desc)]

导入

模块的 imports 组件定义了一组 *导入*,这些导入是 实例化 所必需的。

import::={module name,name name,desc importdesc}importdesc::=func typeidx|table tabletype|mem memtype|global globaltype

每个导入都由一个两级的 名称 空间标记,包含一个 名称和一个 ,用于表示该模块中的一个实体。可导入的定义包括 函数表格内存全局变量。每个导入都由一个描述符指定,该描述符具有相应的类型,在实例化期间提供的定义必须与该类型匹配。

每个导入在相应的 索引空间 中定义一个索引。在每个索引空间中,导入的索引位于模块本身中包含的任何定义的第一个索引之前。

注意

与导出名称不同,导入名称不一定唯一。可以多次导入同一个 / 对;这些导入甚至可能具有不同的类型描述,包括不同类型的实体。具有此类导入的模块仍然可以实例化,具体取决于 嵌入器 允许解析和提供导入的方式。但是,嵌入器不需要支持这种重载,而 WebAssembly 模块本身无法实现重载名称。