这是有关WebAssembly的系列文章的第四部分,若是您尚未阅读其余文章,咱们建议从头开始。html
WebAssembly是一种在网页上运行JavaScript之外的编程语言的方法。过去,当您想在浏览器中运行代码以与网页的不一样部分进行交互时,惟一的选择就是JavaScript。前端
所以,当人们谈论WebAssembly的速度很快时,Apple与Apple的比较就是JavaScript。但这并不意味着这是两种状况,要么您正在使用WebAssembly,要么您正在使用JavaScript。webpack
实际上,咱们指望开发人员将在同一应用程序中同时使用WebAssembly和JavaScript。即便您本身不编写WebAssembly,也能够利用它。git
WebAssembly模块定义可从JavaScript使用的功能。所以,就像您今天从npm下载lodash之类的模块并调用其API中的函数同样,未来您将可以下载WebAssembly模块。github
所以,让咱们看看如何建立WebAssembly模块,而后如何从JavaScript中使用它们。web
在有关汇编的文章中,我谈到了编译器如何采用高级编程语言并将其转换为机器代码。npm
WebAssembly在哪里适合这张图片?编程
您可能会认为这只是目标汇编语言中的另外一种。这是真的,只是每种语言(x86,ARM)对应于特定的计算机体系结构。后端
当您经过网络交付要在用户计算机上执行的代码时,您不知道代码将在您的目标体系结构上运行。数组
所以,WebAssembly与其余类型的程序集有所不一样。它是概念性机器的机器语言,而不是实际的物理机器。
所以,WebAssembly指令有时称为虚拟指令。与JavaScript源代码相比,它们对机器代码的映射要直接得多。它们表明了能够在常见的流行硬件上有效完成的工做的一种交集。可是它们并非直接映射到一种特定硬件的特定机器代码。
浏览器下载WebAssembly。而后,它可使从WebAssembly到目标计算机的汇编代码的距离很短。
当前对WebAssembly的支持最多的编译器工具链称为LLVM。LLVM能够插入许多不一样的前端和后端。
注意:大多数WebAssembly模块开发人员将使用C和Rust等语言进行编码,而后编译为WebAssembly,可是还有其余方法能够建立WebAssembly模块。例如,有一个实验性工具能够帮助您使用TypeScript构建WebAssembly模块,或者您能够直接在WebAssembly的文本表示形式中进行编码。
假设咱们想从C到WebAssembly。咱们可使用clang前端从C到LLVM中间表示。一旦进入LLVM的IR,LLVM就会理解它,所以LLVM能够执行一些优化。
为了从LLVM的IR(中间表示)过渡到WebAssembly,咱们须要一个后端。LLVM项目中正在进行中的一项。后端是其中的大部分方式,应尽快完成。可是,要使其今天开始工做可能会很棘手。
还有另外一个名为Emscripten的工具,此工具如今更易于使用。它有本身的后端,能够经过编译到另外一个目标(称为asm.js)并将其转换为WebAssembly来生成WebAssembly。可是,它在后台使用LLVM,所以您能够在Emscripten的两个后端之间切换。
Emscripten包括许多其余工具和库,可用于移植整个C / C ++代码库,所以,它比编译器更像是软件开发人员工具包(SDK)。例如,系统开发人员习惯于拥有能够读取和写入的文件系统,所以Emscripten可使用IndexedDB模拟文件系统。
不管使用哪一种工具链,最终结果都是以.wasm结尾的文件。我将在下面解释有关.wasm文件结构的更多信息。首先,让咱们看看如何在JS中使用它。
.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模块的内存模拟了您会在这些语言中找到的堆。
为此,它使用JavaScript中的ArrayBuffer。数组缓冲区是字节数组。所以,数组的索引用做内存地址。
若是要在JavaScript和WebAssembly之间传递字符串,请将字符转换为等效的字符代码。而后将其写入内存阵列。因为索引是整数,所以能够将索引传递给WebAssembly函数。所以,字符串的第一个字符的索引能够用做指针。
可能正在开发要由Web开发人员使用的WebAssembly模块的任何人都将围绕该模块建立包装器。这样,您做为模块的使用者就无需了解内存管理。
若是您想了解更多信息,请查阅有关使用WebAssembly的内存的文档。
若是您要使用高级语言编写代码,而后将其编译为WebAssembly,则无需了解WebAssembly模块的结构。但这有助于了解基本知识。
若是您尚未的话,咱们建议您阅读有关汇编的文章(该系列的第3部分)。
这是一个C函数,咱们将其转换为WebAssembly:
int add42(int num) { return num + 42; }
您能够尝试使用WASM Explorer来编译此功能。
若是打开.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
这就是其“二进制”表示形式的模块。我用双引号引发来,由于它一般以十六进制表示法显示,可是能够很容易地转换为二进制表示法或人类可读的格式。
例如,这是什么num + 42
样子。
若是您想知道,如下是这些说明的操做。
您可能已经注意到,该add
操做没有说明其值应从何而来。这是由于WebAssembly是称为堆栈机的示例。这意味着在执行操做以前,操做须要的全部值都在堆栈中排队。
像这样的操做项add
知道他们须要多少个值。因为add
须要两个值,所以它将从堆栈顶部获取两个值。这意味着该add
指令能够很短(一个字节),由于该指令不须要指定源或目标寄存器。这样能够减少.wasm文件的大小,这意味着下载时间更少。
即便WebAssembly是根据堆栈计算机指定的,但这也不是它在物理计算机上的工做方式。当浏览器将WebAssembly转换为运行浏览器的机器的机器代码时,它将使用寄存器。因为WebAssembly代码未指定寄存器,所以它为浏览器提供了更大的灵活性,能够为该计算机使用最佳的寄存器分配。
除了add42
函数自己,.wasm文件中还有其余部分。这些称为段。对于任何模块,某些段都是必需的,而某些则是可选的。
须要:
可选的:
既然您知道如何使用WebAssembly模块,那么让咱们看看为何WebAssembly很快。
转自:https://hacks.mozilla.org/2017/02/creating-and-working-with-webassembly-modules/