WebAssembly规范精读(第三篇)——验证

序言

出于对成为编译器工程师的向往,我开始深入挖掘各项编译技术的细节。作为一名前端工程师,我决定首先从 WebAssembly 技术开始学习。本系列文章记录了我阅读 WebAssembly 规范的重点笔记。

你可以在此链接查阅完整的 WebAssembly 规范:WebAssembly Spec

约定

验证阶段检查 WebAssembly 模块格式的有效性。只有有效的模块才能被实例化。

有效性是通过模块及其内容的抽象语法上的类型系统来定义的。对于每段抽象语法,都有一个类型规则来指定适用于它的约束条件。所有规则都以两种等效形式给出:

  • 在描述性符号中,以直观的形式描述规则。
  • 在形式化符号中,以数学形式描述规则。

描述性和形式化规则是等价的,因此阅读本规范不需要理解形式化符号。形式化符号提供了更简洁的描述,广泛用于编程语言语义学,并且很容易进行数学证明。

上下文

规则的有效性是相对于上下文决定的,上下文收集周围模块和作用域内定义的相关信息:

  • T y p e s Types Types:当前模块中定义的类型列表。
  • F u n c t i o n s Functions Functions:当前模块中声明的函数列表,由其函数类型表示。
  • T a b l e s Tables Tables:当前模块中声明的表列表,由其表类型表示。
  • M e m o r i e s Memories Memories:当前模块中声明的内存列表,由其内存类型表示。
  • G l o b a l s Globals Globals:当前模块中声明的全局变量列表,由它们的全局类型表示。
  • E l e m e n t S e g e m e n t s Element Segements ElementSegements:当前模块中声明的元素段列表,由其元素类型表示。
  • D a t a S e g e m e n t s Data Segements DataSegements:当前模块中声明的数据段列表,每个段由一个 ok 条目表示。
  • L o c a l s Locals Locals:当前函数中声明的局部变量列表(包括参数),由它们值的类型表示。
  • L a b e l s Labels Labels:当前位置可访问的标签栈,由它们的结果类型表示。
  • R e t u r n Return Return:当前函数的返回类型,为可选的结果类型,当不允许返回时不存在,如在独立表达式中。
  • R e f e r e n c e s References References:模块外部函数的函数索引列表,可以通过引用使用它们。

换句话说,上下文包含每个索引空间的类型列表,描述该空间中定义的每个条目。局部变量、标签和返回类型仅用于验证函数体中的指令,在其他地方为空。标签栈是上下文中唯一随指令序列验证而更改的部分。

非形式化符号

验证由每个抽象语法相关部分的规则定义。这些规则不仅定义了短语有效性的约束条件,还将其定义为一种类型。在描述这些规则时采用以下约定。

  • 当且仅当满足相应规则的所有约束时,短语 A A A 称为 "对类型 T T T 有效"。 T T T 的形式取决于 A A A 是什么。

例如,如果 A A A 是 function,则 T T T 是 function type;如果 A A A 是 global,则 T T T 是 global type。

  • 规则隐式假设了一个给定的上下文 C C C。
  • 在某些地方,这个上下文被扩展为带有额外信息的上下文 C ′ C' C′。采用"在上下文 C ′ C' C′,...语句..."这个表述,来表示以下语句必须适用于扩展上下文所体现的假设。

形式化符号

一个短语 A A A 有相应类型的 T T T,该命题写为: A : T A:T A:T。然而,通常情况下,类型依赖于上下文 C C C。为了明确表达这一点,完整形式是一个判断: C ⊢ A : T C \vdash A:T C⊢A:T,它表示假定上下文为 C C C, A : T A:T A:T 成立。

正式的类型规则使用标准方法来定义类型系统,使用演绎规则表示它们。每个规则都具有以下一般形式:

这样的规则被称为大含意:如果所有前提都成立,则结论成立。有些规则没有前提;它们是公理,其结论无条件成立。结论总是一个判断 C ⊢ A : T C \vdash A:T C⊢A:T,每个抽象语法结构都有一个相应的规则。

类型

Limit

Limit 必须具有有意义的边界,且在给定范围内。

  • n n n 不能大于 k k k
  • 如果 m m m 不为空,则:
    • 它不能大于 k k k
    • 它不能小于 n n n
  • 则 limit 在 k k k 范围内有效。

块类型(Block Types)

块类型可以用两种形式表示,这两种形式都将按照以下规则转换为普通函数类型。

t y p e i d x typeidx typeidx

  • 类型 C . t y p e s t y p e i d x C.typestypeidx C.typestypeidx 必须在上下文中定义。
  • 则块类型 C . t y p e s t y p e i d x C.typestypeidx C.typestypeidx 有效,表示函数类型。

v a l t y p e ? valtype\^? valtype?

  • 块类型有效,表示函数类型 v a l t y p e ? \[\]\tovaltype\^? \[\]→valtype?

函数类型(Function Types)

函数类型总是有效。

表类型

内存类型

全局类型(Global Types)

外部类型(External Types)

func f u n c t y p e \textsf{func}~functype func functype

table t a b l e t y p e \textsf{table}~tabletype table tabletype

mem m e m t y p e \textsf{mem}~memtype mem memtype

global g l o b a l t y p e \textsf{global}~globaltype global globaltype

导入子类型(Import Subtyping)

指令

指令通过栈类型(stack types) t 1 ∗ t 2 ∗ t_1\^\*t_2\^\* t1∗t2∗ 进行定义,描述指令如何处理操作数栈。

t 1 ∗ t_1^* t1∗ 表示输入的类型,即从栈中弹出的数据。 t 2 ∗ t_2^* t2∗ 表示输出的类型,及推入到栈中的数据。栈类型类似于函数类型,除了它们允许将单个操作数分类为(底部),这表明该类型不受约束。作为辅助概念,操作数类型1匹配另一个操作数类型2,如果1是或等于2。这以逐点方式扩展到堆栈类型。

数值指令

模块

函数

内存

全局

元素段

数据段

导出

导入(Imports)

导入 i m p o r t import import 和导入的描述 i m p o r t d e s c import~desc import desc 使用外部类型定义。
{ module n a m e 1 , name n a m e 2 , desc i m p o r t d e s c } \{\textsf{module}~name_1,\textsf{name}~name_2,\textsf{desc}~importdesc\} {module name1,name name2,desc importdesc}

  • 导入描述 i m p o r t d e s c importdesc importdesc 必须是有效的 e x t e r n t y p e externtype externtype 。
  • 然后导入是有效的,类型为 e x t e r n t y p e externtype externtype。

模块

模块根据导入的外部类型到导出类型的映射进行分类。

一个模块是完全封闭的,也就是说,它的组件只能引用出现在模块本身中的定义。因此,不需要初始上下文。相反,模块内容的验证上下文是从模块中的定义构建的。

  • m o d u l e module module 为被验证的模块。
  • C C C 为上下文,其中:
    • C . t y p e s C.types C.types 等于 m o d u l e . t y p e s module.types module.types,
    • C . f u n c s C.funcs C.funcs
    • C . t a b l e s C.tables C.tables
    • C . m e m s C.mems C.mems
    • C . g l o b a l s C.globals C.globals
    • C . e l e m s C.elems C.elems
    • C . d a t a s C.datas C.datas
    • C . l o c a l s C.locals C.locals 为空,
    • C . l a b e l s C.labels C.labels 为空,
    • C . r e t u r n C.return C.return 为空,
    • C . r e f s C.refs C.refs
  • C ′ C^\prime C′ 为上下文,其中:
    • C ′ . g l o b a l s C^\prime .globals C′.globals
    • C ′ . f u n c s C^\prime .funcs C′.funcs
    • C ′ . r e f s C^\prime .refs C′.refs
    • 其他字段为空。
相关推荐
世界哪有真情4 小时前
拿人类意识卡 AI?等于用 bug 验收正式产品
前端·人工智能·后端
Listen·Rain5 小时前
Vue3:setup详解
前端·javascript·vue.js
Patrick_Wilson5 小时前
修好 bug 只是开始:一次由监控需求反向重构日志结构的复盘
前端·监控·数据可视化
kyriewen6 小时前
面试官让我优化一个卡死的表格,我打开 Claude 五秒重写,他放下咖啡问我要不要来当组长
前端·javascript·面试
小粉粉hhh6 小时前
React(二)——dom、组件间通信、useEffect
前端·javascript·react.js
Csvn7 小时前
🤖 AI 生成前端代码的 5 个典型翻车现场及修复指南
前端
IT_陈寒8 小时前
SpringBoot自动装配坑了我一天,原来问题出在这
前端·人工智能·后端
索西引擎8 小时前
【React】key 属性:协调算法中的元素标识机制与最佳实践
前端·javascript·react.js
只会cv的前端攻城狮9 小时前
动态组件架构设计:从配置到事件驱动全链路解析
前端·vue.js
飞天狗9 小时前
Next.js 16 App Router 实战:服务端组件与流式渲染
前端·next.js