深刻淺出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
      });
複製代碼
相關文章
相關標籤/搜索