深刻浅出WebAssembly(2) Basic API

这系列主要是我对WASM研究的笔记,可能内容比较简略。总共包括:html

  1. 深刻浅出WebAssembly(1) Compilation
  2. 深刻浅出WebAssembly(2) Basic Api
  3. 深刻浅出WebAssembly(3) Instructions
  4. 深刻浅出WebAssembly(4) Validation
  5. 深刻浅出WebAssembly(5) Memory
  6. 深刻浅出WebAssembly(6) Binary Format
  7. 深刻浅出WebAssembly(7) Future
  8. 深刻浅出WebAssembly(8) Wasm in Rust(TODO)

API总览

//async version
WebAssembly.compile(bufferSource: ArrayBuffer): Promise<WebAssembly.Module>
WebAssembly.instantiate(bufferSource: ArrayBuffer, importObj?: any): Promise<{module: WebAssembly.Module, instance: WebAssembly.Instance}>
WebAssembly.instantiate(module: WebAssembly.Module, importObj?: any): Promise<WebAssembly.Instance>
WebAssembly.compileStreaming(source: Promise<Responce>): Promise<WebAssembly.Module> // wasm 请求头:Context-type: application/wasm

//sync version
new WebAssembly.Module(bufferSource: ArrayBuffer)
new WebAssembly.Instance(module: WebAssembly.Module, importObj?: any)

// helper
WebAssembly.Module.customSections(module:WebAssembly.Module, sectionName: string): ArrayBuffer[]
WebAssembly.Module.exports(module: WebAssembly.Module): { name: string, kind: "function|table|memory|global" }][]
WebAssembly.Module.imports(module: WebAssembly.Module): { module: string, name: string, kind: "function|table|memory|global" }][]

// validation and error
WebAssembly.validate(bufferSource: ArrayBuffer):boolean

interface CommonError {
  message: string
  filename: string
  lineNumber: number
}
new WebAssembly.CompileError(message: string, fileName: string, lineNumber: number) // 解码,验证阶段
new WebAssembly.LinkError(message: string, fileName: string, lineNumber: number)  // 实例化阶段
new WebAssembly.RuntimeError(message: string, fileName: string, lineNumber: number) // 运行阶段
复制代码

如何初始化一个模块?

Async way:

fetch('./index.wasm').then(response =>
  response.arrayBuffer()
).then(bytes => WebAssembly.instantiate(bytes, {
  env: {
    yyy: xxx
  }
})).then(wasm => {
  const { module, instance } = wasm;
}).catch(console.error);
复制代码

Streaming way:

const source = fetch('./index.wasm')
WebAssembly.compileStreaming(source).then(module =>
  WebAssembly.instantiate(module, {
    env: {
      xxx: yyy
    }
  })
).then(instance => {
  //xxx
}).catch(console.error);
复制代码

Sync way:

const source = fetch('./index.wasm')
WebAssembly.compileStreaming(source).then(module =>
  WebAssembly.instantiate(module, {
    env: {
      xxx: yyy
    }
  })
).then(instance => {
  //xxx
}).catch(console.error);
复制代码

最好的方式是经过 WebAssembly.compileStreaming 的方式来加载。能够对wasm模块提早进行编译验证。git

TypedArray

JavaScript类型化数组是一种相似数组的对象,并提供了一种用于访问原始二进制数据的机制。 正如你可能已经知道,Array 存储的对象能动态增多和减小,而且能够存储任何JavaScript值。JavaScript引擎会作一些内部优化,以便对数组的操做能够很快。然而,随着Web应用程序变得愈来愈强大,尤为一些新增长的功能例如:音频视频编辑,访问WebSockets的原始数据等,很明显有些时候若是使用JavaScript代码能够快速方便地经过类型化数组来操做原始的二进制数据将会很是有帮助。 可是,不要把类型化数组与正常数组混淆,由于在类型数组上调用 Array.isArray() 会返回false。此外,并非全部可用于正常数组的方法都能被类型化数组所支持(如 pushpop)。github

CustomSections

Custom Sections — WebAssembly 1.0web

custom section 是section id为零的段,不惟一。目前只实现了 name sectionapi

Memory

wasm-ld 有关memory的参数:(WebAssembly lld port — lld 10 documentation数组

--import-memory

    Import memory from the environment.

--initial-memory=<value>

    Initial size of the linear memory. Default: static data size.

--max-memory=<value>
   
    Maximum size of the linear memory. Default: unlimited.
复制代码

通常来讲不须要限制,可是在某些安全场景下能够在js或者wasm里面设置。安全

WebAssembly.Memory

编译的时候启用--import-memory,而后在js中构造一个memory对象经过env传给wasm模块。bash

const memory = new WebAssembly.Memory({
  initial: 10,
  maxium: 100,
});

fetch('../a.wasm').then(buffer => WebAssembly.instantiate(buffer, {env: { memory }})).then((wasm) => {
  console.log(WebAssembly.imports(wasm.module));
})
复制代码

实际上不必定须要经过env传递,只是llvm编译器如此指定的,能够传如任意对象,而后手动修改wasm模块(github.com/mdn/webasse…markdown

memory单位是page,pageSize为64KB, 初始内存不够的时候能够调用memory.grow(n)来增大,但总共不能超过最大内存maxium, maxium设置以后不可更改(同stack)。app

wasm指令

  1. Instructions — WebAssembly 1.0
  2. Clang: clang/BuiltinsWebAssembly.def at 86d4513d3e0daa4d5a29b0b1de7c854ca15f9fe5 · microsoft/clang · GitHub 这些__builtin_开头的符号实际上是一些编译器内置的函数或者编译优化处理开关等,其做用相似于宏。宏是高级语言用于预编译时进行替换的源代码块,而内置函数则是用于在编译阶段进行替换的机器指令块。所以编译器的这些内置函数其实并非真实的函数,而只是一段指令块,起到编译时的内联功能。
  3. Rust: stdarch/memory.rs at ef6b0690192f1cfc753af698695c2ecde0c7b991 · rust-lang/stdarch · GitHub

Table

A table is an array of opaque values of a particular element type. It allows programs to select such values indirectly through a dynamic index operand. Currently, the only available element type is an untyped function reference. Thereby, a program can call functions indirectly through a dynamic index into a table. For example, this allows emulating function pointers by way of table indices.

js下,WebAssembly.Table 表明着wasm模块中的表结构实体。

const table = new WebAssembly.Table({
  initial: 2,  //初始时可储存的表项的数量
  element: "anyfunc",
  maxium: 10, //可选
});

table.length // 2
table.get(0) // null
table.grow(n) 
table.set(index: number, value: elem) // 只能设置类型为anyfunc的表项
复制代码

Exmaple

(module
    (import "js" "tbl" (table 2 anyfunc))
    (func $f42 (result i32) i32.const 42)
    (func $f83 (result i32) i32.const 83)
    (elem (i32.const 0) $f42 $f83)
)
复制代码
var tbl = new WebAssembly.Table({initial:2, element:"anyfunc"});
      console.log(tbl.length);
      console.log(tbl.get(0));
      console.log(tbl.get(1));
      var importObject = {
        js: {
          tbl:tbl
        }
      };
      WebAssembly.instantiateStreaming(fetch('table2.wasm'), importObject)
      .then(function(obj) {
        console.log(tbl.length);
        console.log(tbl.get(0)()); // 42
        console.log(tbl.get(1)()); // 83
      });
复制代码
相关文章
相关标签/搜索