[译][A crash course in WebAssembly] 创做并使用 WebAssembly 模块


title: [A crash course in WebAssembly] 创做并使用 WebAssembly 模块javascript

date: 2018-3-22 16:42:00前端

categories: 翻译java

tags: WebAssemblyweb

source: 原文地址npm

auther: Lin Clark编程


[A crash course in WebAssembly] 创做并使用 WebAssembly 模块


这是WebAssembly系列文章的第四部分。若是您尚未阅读其余文章,咱们建议您从头开始后端

WebAssembly是一种在网页上运行JavaScript之外的编程语言的方法。在过去,当您想在浏览器中运行代码以与网页的不一样部分进行交互时,您惟一的选择就是JavaScript。api

因此当人们谈论WebAssembly变得愈来愈快时,比较的对象一般是在说JavaScript。可是,这并不意味着它是一种或者两种状况————您使用的是WebAssembly,或者使用JavaScript。数组

事实上,咱们预计开发人员将在同一个应用程序中同时使用WebAssembly和JavaScript。即便你本身不写WebAssembly,你也能够利用它。浏览器

WebAssembly定义的模块函数可让JavaScript调用,将来有一天你能够像从npm上下载loadsh之类的各类模块同样下载WebAssembly模块并调用它们的api。

so,让咱们来看看如何建立一个WebAssembly模块而后在JS中使用。

Where does WebAssembly fits

在上一篇讲assembly的文章中,咱们谈到了编译器接受讲高级编程语言并将其翻译为机器码

那么WebAssembly因该出如今这张图的什么位置呢?

你可能会以为只是增长了一个目标汇编语言(与x86/ARM相似)。除了不针对某个架构外,基本上是对的。

当您经过Web在用户计算机上传递代码时,您不知道代码将在哪一个目标架构上运行。

因此WebAssembly与其余类型的assembly有点不一样。它是概念机器的机器语言,而不是实际的物理机器。

所以,WebAssembly指令有时被称为虚拟指令。它们比JavaScript代码更直接地映射到机器码。它们表明了能够在普通流行硬件中高效完成的一种交集。但它们不是直接映射到特定硬件的特定机器码。

浏览器下载WebAssembly。而后,它可以快速将WebAssembly翻译为目标机器的汇编代码。

Compiling to .wasm

目前对WebAssembly支持最多的编译器工具链称为LLVM。有许多不一样的前端和后端能够插入到LLVM中。

note:大多数WebAssembly模块开发人员将使用C和Rust等语言进行编码,而后编译为WebAssembly,但还有其余方法能够建立WebAssembly模块。例如,有一个实验工具能够帮助您使用TypeScript构建WebAssembly模块,或者您能够直接编写WebAssembly的文本表示形式。

假设咱们想从C到WebAssembly。咱们可使用clang前端从C到LLVM中间表示。一旦它在LLVM的IR中,LLVM就会理解它,因此LLVM能够执行一些优化。

要从LLVM的IR(中间表示)转到WebAssembly,咱们须要一个后端。目前LLVM项目正在进行中。这个后端重要的部分,会尽快完善。目前来讲使用WebAssembly可能会很是棘手。

还有另外一个名为Emscripten的工具,目前使用起来更容易一些。它有本身的后端,能够经过编译到另外一个目标(称为asm.js),而后将其转换为WebAssembly来生成WebAssembly。不过,它使用LLVM,所以你能够在Emscripten的两个后端之间切换。

!()[https://hacks.mozilla.org/files/2017/02/04-03-toolchain07-768x631.png]

Emscripten包含许多额外的工具和库,容许移植整个 C/C++ 代码库,所以它比编译器更像是软件开发工具包(SDK)。例如,系统开发人员习惯于拥有能够读取和写入的文件系统,而Emscripten使用IndexedDB模拟文件系统。

不管您使用的工具链如何,最终结果都是以.wasm结尾的文件。我将在下面详细解释.wasm文件的结构。首先,让咱们看看如何在JS中使用它。

Loading a .wasm module in JavaScript

.wasm文件就是WebAssembly模块,可使用JavaScript加载。截至目前,加载过程有点复杂。

function fetchAndInstantiate(url, importObject) {
  return fetch(url).then(response =>
    response.arrayBuffer()
  ).then(bytes =>
    WebAssembly.instantiate(bytes, importObject)
  ).then(results =>
    results.instance
  );
}
复制代码

您能够在咱们的文档中更深刻地看到这一点。

咱们正在努力使这个过程更容易。咱们但愿对工具链进行改进,并将其与现有的模块捆绑器(打包器如Webpack或装载器如SystemJS)集成。咱们相信加载WebAssembly模块能够像加载JavaScript模块同样简单。

不过,WebAssembly模块和JS模块之间有一个主要区别。目前,WebAssembly中的函数只能使用数字(整数或浮点数)做为参数或返回值。

参数说明

对于任何更复杂的数据类型(好比字符串),您必须使用WebAssembly模块的内存。

若是你主要使用JavaScript,对直接访问内存不是很熟悉。C/C++和Rust等性能更高的语言每每具备手动内存管理功能。 WebAssembly模块的内存模拟了您在这些语言的堆(heap)。

为此,它使用JavaScript中称为ArrayBuffer的东西。数组缓冲区是一个字节数组。数组的索引则做为内存地址。

若是你想在JavaScript和WebAssembly之间传递一个字符串,你能够将这些字符转换为它们的字符代码。而后你就能够将它写入数组缓冲区(memory array)。因为索引是整数,因此能够将索引传递给WebAssembly函数。所以,字符串的第一个字符的索引能够用做指针。

任何开发WebAssembly模块以供Web开发人员使用的人均可能会为该模块建立一个包装。做为模块的使用者,您就可以不须要了解内存管理。

若是您想了解更多信息,请查看关于使用WebAssembly内存的文档

.wasm 文件的结构

若是您使用更高级别的语言编写代码,而后将其编译为WebAssembly,则无需知道WebAssembly模块的结构。但它能够帮助你理解基础。

若是你尚未准备好,咱们建议阅读关于装配的文章(本系列的第3部分)。

这是一个咱们将翻译为WebAssembly的C函数:

int add42(int num) {
  return num + 42;
}
复制代码

您能够尝试使用WASM资源管理器来编译此功能。

若是你打开.wasm文件(若是你的编辑器支持显示它),你会看到相似的东西。

00 61 73 6D 0D 00 00 00 01 86 80 80 80 00 01 60
01 7F 01 7F 03 82 80 80 80 00 01 00 04 84 80 80
80 00 01 70 00 00 05 83 80 80 80 00 01 00 01 06
81 80 80 80 00 00 07 96 80 80 80 00 02 06 6D 65
6D 6F 72 79 02 00 09 5F 5A 35 61 64 64 34 32 69
00 00 0A 8D 80 80 80 00 01 87 80 80 80 00 00 20
00 41 2A 6A 0B
复制代码

这是“二进制(binary)”表示的模块。我将引号放在二进制文件中,由于它一般以十六进制表示法显示,但能够很容易地转换为二进制符号或可读格式。

例如,这里是num + 42的样子:

形态对比图

代码如何工做:一个堆栈机器 a stack machine

以防万一您想知道,那么这些会说明它们作了什么

代码注解

您可能已经注意到,add操做没有说明其值应来自哪里。这是由于WebAssembly就像一个堆栈机器。这意味着在执行操做以前,操做所需的全部值都在堆栈中排队。

add操做知道他们须要多少个值。因为add须要两个,所以它会从堆栈的顶部取两个值。这意味着add指令能够很短(一个字节),由于指令不须要指定源寄存器或目标寄存器。这会减少.wasm文件的大小,从而减小它的下载时间。

尽管WebAssembly是根据堆栈计算机指定的,但这并非它在物理机器上的工做方式。当浏览器将WebAssembly转换为正在运行浏览器的机器的机器码时,它将使用寄存器。因为WebAssembly代码不指定寄存器,所以可使浏览器更灵活地为该机器分配寄存器,达到最佳的分配方案。

模块部分

除了add42函数自己,.wasm文件中还有其余部分。这些被称为sections。某些sections对于任何模块都是必需的,有些是可选的。

必须的:

  • 类型Type 包含此模块中定义的函数的函数签名和任何导入的函数。

  • 函数Function 为此模块中定义的每一个函数提供索引。

  • 代码Code 此模块中每一个函数的实际函数体。

可选的:

  • Export 使函数,内存,表和全局变量对其余WebAssembly模块和JavaScript可用。这容许单独编译的模块动态连接在一块儿。这是Web组件版的.dll。

  • Import 指定要从其余WebAssembly模块或JavaScript导入的函数,内存,表(tables)和全局变量。

  • Start 加载WebAssembly模块时会自动运行的函数(基本上相似于main函数​​)。

  • Global 为模块声明全局变量。

  • Memory 定义此模块将使用的内存。

  • Table 使映射到WebAssembly模块以外的值成为可能,例如JavaScript对象。 这对容许间接函数调用特别有用。

  • Data 初始化导入的或本地的内存。

  • Element 初始化导入的或本地的表。

有关章节的更多信息,请参阅sections的工做原理

coming up next

如今您已经知道如何使用WebAssembly模块,让咱们来看看为何WebAssembly速度很快

相关文章
相关标签/搜索