网页嵌入

毫不奇怪,WebAssembly 的主要用途之一是在 Web 上运行,例如嵌入在 Web 浏览器中(尽管这 并非其唯一目的)。

这意味着要与 Web 生态系统集成,利用 Web API,支持 Web 的安全模型,保留 Web 的可移植性,并在设计中为演进式开发留出空间。许多这些目标都清楚地反映在 WebAssembly 的 高级目标 中。特别是,从安全角度来看,WebAssembly MVP 将不会比模块是 JavaScript 时更宽松。

更具体地说,以下是 WebAssembly 与 Web 平台其余部分之间的联系点,这些联系点已被考虑在内

JavaScript API

提供了一个 JavaScript API,它允许 JavaScript 编译 WebAssembly 模块,对已编译的模块执行有限的反射,从离线存储中存储和检索已编译的模块,使用 JavaScript 导入实例化已编译的模块,调用实例化模块的导出函数,别名实例化模块的导出内存,等等。

网页嵌入包括在该上下文中很有用的其他方法。在非网页嵌入中,这些 API 可能不存在。

其他网页嵌入 API

在网页嵌入中,添加了以下方法。

请注意,预计 compileStreaminginstantiateStreaming 既存在也都不存在。

WebAssembly.compileStreaming

:cyclone: 添加到里程碑 2,开发人员必须进行功能检测。

Promise<WebAssembly.Module> compileStreaming(source)

此函数接受一个 Response 对象或对该对象的承诺,并编译响应的生成字节。此编译可以在后台以流式方式执行。如果 Response 不是 CORS-同源,不代表 ok 状态,或者与 `application/wasm` MIME 类型不匹配,则返回的承诺将被拒绝,并出现 TypeError;如果编译失败,则返回的承诺将被拒绝,并出现 WebAssembly.CompileError

  1. 返回 处理潜在的 WebAssembly 响应 的结果,给定 source,执行以下步骤
    • 给定 bytes 的处理步骤
      1. 编译 bytes,方式与为 WebAssembly.Module 构造函数 指定的方式相同。
      2. 如果编译成功完成,则使用已验证的 Ast.module 编译结果返回成功。
      3. 否则,如果编译失败,则使用失败原因返回失败。
    • 成功步骤,给定 module
      1. 返回一个新的 WebAssembly.Module 实例,其 [[Module]] 设置为 module
    • 失败步骤,给定 reason
      1. 返回一个新的 WebAssembly.CompileError,其中包含存储在 reason 中的编译失败信息。

WebAssembly.instantiateStreaming

:cyclone: 添加到里程碑 2,开发人员必须进行功能检测。

dictionary WebAssemblyInstantiatedSource {
   required WebAssembly.Module module;
   required WebAssembly.Instance instance;
};

Promise<InstantiatedSource> instantiateStreaming(source [, importObject])

此函数接受一个 Response 对象或对该对象的承诺,并编译和实例化响应的生成字节。此编译可以在后台以流式方式执行。如果 Response 不是 CORS-同源,不代表 ok 状态,或者与 `application/wasm` MIME 类型不匹配,则返回的承诺将被拒绝,并出现 TypeError;如果编译或实例化失败,则返回的承诺将被拒绝,并出现 WebAssembly.CompileErrorWebAssembly.LinkErrorWebAssembly.RuntimeError,具体取决于失败的原因。

  1. 返回 处理潜在的 WebAssembly 响应 的结果,给定 source,执行以下步骤
    • 给定 bytes 的处理步骤
      1. 编译 bytes,方式与为 WebAssembly.Module 构造函数 指定的方式相同。
      2. 如果编译成功完成
        1. moduleAst.module 编译结果。
        2. 以与为 WebAssembly.Instance 构造函数 指定的方式相同的方式实例化 module
        3. 如果实例化成功完成,则使用包含 module、结果实例和结果导出的 元组 返回成功。
        4. 否则,如果实例化失败,则使用失败原因返回失败。
      3. 否则,如果编译失败,则使用失败原因返回失败。
    • 成功步骤,给定元组 (module, instance, exports)
      1. exports 创建 exportsObject,方式与为 WebAssembly.Instance 构造函数 指定的方式相同。
      2. exportsObjectinstance 创建 instanceObject,方式与为 WebAssembly.Instance 构造函数 指定的方式相同。
      3. moduleObject 为一个新的 WebAssembly.Module 实例,其 [[Module]] 设置为 module
      4. resultObjectCreate(%ObjectPrototype%).
      5. 执行 CreateDataProperty(result, “module”, moduleObject).
      6. 执行 CreateDataProperty(result, “instance”, instanceObject).
      7. 返回 result
    • 失败步骤,给定 reason
      1. 返回一个新的 WebAssembly.CompileErrorWebAssembly.LinkErrorWebAssembly.RuntimeError,具体取决于存储在 reason 中的失败信息。

处理潜在的 WebAssembly 响应

以上两个函数都重复使用相同的基础设施来从适当的 Response 对象中提取字节,仅在最后对这些字节执行的操作不同。因此,我们定义以下共享规范级过程

处理潜在的 WebAssembly 响应接受一个输入 argument 和三组步骤 processingStepssuccessStepsfailureSteps

  • argument 是作者提供的任意 JavaScript 值。
  • processingSteps并行(即“在主线程之外”)发生,接受 字节序列,并且必须返回一个与 Realm 无关的成功或失败值。
  • successStepsfailureSteps 将在发布到主事件循环的任务中发生;它们接受从 processingSteps 返回的与 Realm 无关的值,并且必须将它们转换为可以用来适当地实现或拒绝返回承诺的 JavaScript 对象。

给定这些值,要处理潜在的 WebAssembly 响应

  1. returnValue一个新的承诺
  2. sourceAsPromise使用 argument 解析的承诺。
  3. sourceAsPromise 使用值 unwrappedSource 实现时
    1. 如果 unwrappedSource 不是 Response 对象,则使用 TypeError 异常拒绝 returnValue,并中止这些子步骤。
    2. responseunwrappedSource响应
    3. mimeType 为从 response头列表提取 MIME 类型 的结果。
    4. 如果 mimeType 不是 `application/wasm`,则使用 TypeError 拒绝 returnValue,并中止这些子步骤。(注意:不允许额外参数,包括空 `application/wasm;`。)
    5. 如果 response 不是 CORS-同源,则使用 TypeError 拒绝 returnValue,并中止这些子步骤。
    6. 如果 response状态 不是 ok 状态,则使用 TypeError 拒绝 returnValue,并中止这些子步骤。
    7. response 的正文作为 ArrayBuffer 使用,并令 bodyPromise 为结果。(注意:尽管这里指定在 processingSteps 继续之前完全使用响应,但这纯粹是为了说明方便;实现可能会以流式方式执行处理。不同之处是不可观察的,因此指定了更简单的模型。)
    8. bodyPromise 使用值 bodyArrayBuffer 实现时
      1. bytesbodyArrayBuffer 的底层 字节序列
      2. 并行执行 processingSteps,给定 bytes
      3. 如果 processingSteps 使用值 processingSuccessValue 成功,则在 网络任务源排队一个任务,以在给定 processingSuccessValue 的情况下运行 successSteps,并令 successResult 为这些步骤的结果。使用 successResult 解析 returnValue
      4. 否则,如果 processingSteps 使用原因 processingFailureReason 失败,则在 网络任务源排队一个任务,以在给定 processingFailureReason 的情况下运行 failureSteps,并令 failureResult 为这些步骤的结果。使用 failureResult 拒绝 returnValue
    9. bodyPromise 使用原因 reason 拒绝时
      1. 使用 reason 拒绝 returnValue
  4. sourceAsPromise 使用原因 reason 拒绝时
    1. 使用 reason 拒绝 returnValue
  5. 返回 returnValue

面向开发人员的显示约定

浏览器、JavaScript 引擎和离线工具有常见的引用 JavaScript 工件和语言结构的方式。例如,JavaScript 源代码中的位置在堆栈跟踪或错误消息中打印,并且自然地表示为文本文件中的十进制格式的行和列。函数和变量的名称直接取自源代码。因此(例如),即使 Error.stack 字符串的精确格式并不总是匹配,但位置很容易理解,并且在浏览器之间是相同的。

为了实现 WebAssembly 结构的共同表示的相同目标,采用以下约定。

WebAssembly 位置是对二进制文件中特定指令的引用,浏览器或引擎可以在类似于 JavaScript 源位置的上下文中显示它。它具有以下格式

${url}:wasm-function[${funcIndex}]:${pcOffset}

其中

  • ${url} 是与模块关联的 URL(如果适用)(请参阅注释)。
  • ${funcIndex}函数索引空间 中的索引。
  • ${pcOffset} 是模块二进制文件中指令首字节的偏移量,以十六进制格式(使用小写数字)打印,并带有前导 0x 前缀。

注释

  • URL 字段的解释可能因上下文而异。当在浏览器中使用基于响应的实例化 API 时,应使用关联的 URL;或者当使用基于 ArrayBuffer 的实例化 API 时,浏览器应表示 API 调用的位置。这种实例化类似于使用 eval 执行 JavaScript;因此,如果浏览器具有表示 eval 调用位置的现有方法,则可以对 WebAssembly.instantiate 使用类似的方法。例如,如果浏览器对 eval 使用 foo.js line 10 > evaleval at bar (foo.js:10:3),则它可以分别使用 foo.js line 10 > WebAssembly.instantiateWebAssembly.instantiate at bar (foo.js:10:3)。脱机工具可以使用文件名代替。
  • 使用十六进制表示模块偏移量符合 native 工具(如 objdump,其中地址以十六进制打印)中的通用约定,并且使其在视觉上与 JavaScript 行号区分开来。其他数字以十进制表示。

虽然 导出 WebAssembly 函数name 属性由 JS API 指定,但合成函数名称也显示在其他上下文中,如开发者工具调用栈和 Error.stack。如果 WebAssembly 模块包含 “name” 部分,则应使用这些名称来合成函数名称,如下所示

  • 如果存在函数名称子部分,则显示的名称应为 ${module_name}.${function_name}${function_name},具体取决于模块名称是否存在。
  • 否则,输出可能取决于上下文
    • 如果函数名称与堆栈跟踪中的位置一起显示,则可以使用模块名称(如果存在)或空字符串(因为函数索引已在位置中)。
    • 否则,应使用 ${module_name}.wasm-function[${funcIndex}]wasm-function[${funcIndex}] 来传达函数索引。

请注意,本文档未指定字符串(如堆栈帧表示)的完整格式;这允许引擎继续对 JavaScript 使用其现有格式(现有代码可能已经依赖于它),同时仍以与 JavaScript 一致的格式打印 WebAssembly 帧。

模块

WebAssembly 的 模块 允许自然地 与 ES6 模块系统集成.

名称

WebAssembly 模块可以有导入和导出,它们使用 UTF-8 字节序列标识。导出名称到导出的映射的最自然的 Web 表示是一个 JS 对象,其中每个导出都是一个属性,其名称以 UTF-16 编码。如果 WebAssembly 模块的导入或导出的名称不能根据以下转换算法干净地转换为 UTF-16,则该模块在 Web 上无法通过验证,假设 WebAssembly 名称位于名为 arrayUint8Array

function convertToJSString(array)
{
  var string = "";
  for (var i = 0; i < array.length; ++i)
    string += String.fromCharCode(array[i]);
  return decodeURIComponent(escape(string));
}

这使用 常见的 JS 习惯用法 执行 UTF8 解码 (decodeURIComponent(escape(string)))。转换失败由 decodeURIComponent 检测到,它可能会抛出 URIError。如果这样做,WebAssembly 模块将无法通过验证。此验证规则仅对 Web 嵌入是强制性的。

安全性

WebAssembly 的 安全 模型应依赖于 同源策略,并使用 跨域资源共享 (CORS)子资源完整性 来启用通过内容分发网络的分发以及实现 动态链接.

SIMD

一旦 SIMD 获得支持,WebAssembly 将

  • SIMD.js-in-asm.js 相似,进行静态类型化;
  • 复用操作语义的规范(与 TC39 一起);
  • 复用后端实现(相同的 IR 节点)。

GC

一旦 GC 获得支持,WebAssembly 代码将能够引用和访问 JavaScript、DOM 和通用 WebIDL 定义的对象。