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
    • 其他字段为空。
相关推荐
一拳一个娘娘腔1 分钟前
【第七期】漏洞攻防-前端篇:XSS 与 CSRF —— 当浏览器成为攻击者的“肉鸡”
前端·xss·csrf
不吃鱼的羊23 分钟前
DaVinci配置NVM模块
前端·javascript·网络
excel33 分钟前
为什么需要构建工具(Webpack / Vite 的本质)
前端
lang2015092833 分钟前
Java SAX 流式解析全解:从原理到 EasyExcel 实战
java·前端·javascript
Rain50941 分钟前
2.4. PostgreSQL 数据库连接与实战指南
前端·数据库·人工智能·后端·postgresql·数据分析
console.log('npc')42 分钟前
Codex 桌面端接入 Headroom 压缩代理完整教程
前端·vscode
独泪了无痕1 小时前
Vue集成uuid生成唯一标识实践指南
前端·vue.js
yuanyxh9 小时前
Mac 软件推荐
前端·javascript·程序员
万少9 小时前
AtomCode开发微信小程序《谁去呀》 全流程
前端·javascript·后端
某人辛木9 小时前
Web自动化测试
前端·python·pycharm·pytest