@geekpi
This commit is contained in:
Xingyu Wang 2021-03-30 09:59:40 +08:00
parent 406416594f
commit 85a196d0eb

View File

@ -3,48 +3,45 @@
[#]: author: (Stephan Avenwedde https://opensource.com/users/hansic99) [#]: author: (Stephan Avenwedde https://opensource.com/users/hansic99)
[#]: collector: (lujun9972) [#]: collector: (lujun9972)
[#]: translator: (geekpi) [#]: translator: (geekpi)
[#]: reviewer: ( ) [#]: reviewer: (wxy)
[#]: publisher: ( ) [#]: publisher: ( )
[#]: url: ( ) [#]: url: ( )
如何在 WebAssembly 中写 “Hello World” 如何在 WebAssembly 中写 “Hello World”
====== ======
通过这个分步教程,开始用人类可读的文本编写 WebAssembly。 > 通过这个分步教程,开始用人类可读的文本编写 WebAssembly。
![Hello World inked on bread][1]
WebAssembly 是一种字节码格式,[几乎所有的浏览器][2]都可以将它编译成其主机系统的机器代码。除了 JavaScript 和 WebGL 之外WebAssembly 还满足了将应用移植到浏览器中以实现平台独立的需求。作为 C++ 和 Rust 的编译目标WebAssembly 使 Web 浏览器能够以接近原生的速度执行代码。 ![](https://img.linux.net.cn/data/attachment/album/202103/30/095907r6ecev48dw0l9w44.jpg)
当你谈论 WebAssembly、应用时你必须区分三种状态 WebAssembly 是一种字节码格式,[几乎所有的浏览器][2] 都可以将它编译成其宿主操作系统的机器代码。除了 JavaScript 和 WebGL 之外WebAssembly 还满足了将应用移植到浏览器中以实现平台独立的需求。作为 C++ 和 Rust 的编译目标WebAssembly 使 Web 浏览器能够以接近原生的速度执行代码。
当谈论 WebAssembly 应用时,你必须区分三种状态:
1. **源码(如 C++ 或 Rust** 你有一个用兼容语言编写的应用,你想把它在浏览器中执行。 1. **源码(如 C++ 或 Rust** 你有一个用兼容语言编写的应用,你想把它在浏览器中执行。
2. **WebAssembly 字节码:** 你选择 WebAssembly 字节码作为编译目标。最后,你得到一个 `.wasm` 文件。 2. **WebAssembly 字节码:** 你选择 WebAssembly 字节码作为编译目标。最后,你得到一个 `.wasm` 文件。
3. **机器码opcode** 浏览器加载 `.wasm` 文件,并将其编译成主机系统的相应机器码。 3. **机器码opcode** 浏览器加载 `.wasm` 文件,并将其编译成主机系统的相应机器码。
WebAssembly 还有一种文本格式,用人类可读的文本表示二进制格式。为了简单起见,我将其称为 **WASM-text**。WASM-text 可以比作高级汇编语言。当然,你不会基于 WASM-text 来编写一个完整的应用,但了解它的底层工作原理是很好的(特别是对于调试和性能优化)。 WebAssembly 还有一种文本格式,用人类可读的文本表示二进制格式。为了简单起见,我将其称为 **WASM-text**。WASM-text 可以比作高级汇编语言。当然,你不会基于 WASM-text 来编写一个完整的应用,但了解它的底层工作原理是很好的(特别是对于调试和性能优化)。
本文将指导你在 WASM-text 中创建经典的 _Hello World_ 程序。 本文将指导你在 WASM-text 中创建经典的 “Hello World” 程序。
### 创建 .wat 文件 ### 创建 .wat 文件
WASM-text 文件通常以 `.wat` 结尾。第一步创建一个名为 `helloworld.wat` 的空文本文件,用你最喜欢的文本编辑器打开它,然后粘贴进去: WASM-text 文件通常以 `.wat` 结尾。第一步创建一个名为 `helloworld.wat` 的空文本文件,用你最喜欢的文本编辑器打开它,然后粘贴进去:
``` ```
(module (module
    ;; Imports from JavaScript namespace     ;; 从 JavaScript 命名空间导入
    (import  "console"  "log" (func  $log (param  i32  i32))) ;; Import log function     (import  "console"  "log" (func  $log (param  i32  i32))) ;; 导入 log 函数
    (import  "js"  "mem" (memory  1)) ;; Import 1 page of memory (54kb)     (import  "js"  "mem" (memory  1)) ;; 导入 1 页 内存64kb
       
    ;; Data section of our module     ;; 我们的模块的数据段
    (data (i32.const 0) "Hello World from WebAssembly!")     (data (i32.const 0) "Hello World from WebAssembly!")
       
    ;; Function declaration: Exported as helloWorld(), no arguments     ;; 函数声明:导出 helloWorld(),无参数
    (func (export  "helloWorld")     (func (export  "helloWorld")
        i32.const 0  ;; pass offset 0 to log         i32.const 0  ;; 传递偏移 0 到 log
        i32.const 29  ;; pass length 29 to log (strlen of sample text)         i32.const 29  ;; 传递长度 29 到 log示例文本的字符串长度
        call  $log         call  $log
        )         )
) )
@ -52,28 +49,26 @@ WASM-text 文件通常以 `.wat` 结尾。第一步创建一个名为 `helloworl
WASM-text 格式是基于 S 表达式的。为了实现交互JavaScript 函数用 `import` 语句导入WebAssembly 函数用 `export` 语句导出。在这个例子中,从 `console` 模块中导入 `log` 函数,它需要两个类型为 `i32` 的参数作为输入以及一页内存64KB来存储字符串。 WASM-text 格式是基于 S 表达式的。为了实现交互JavaScript 函数用 `import` 语句导入WebAssembly 函数用 `export` 语句导出。在这个例子中,从 `console` 模块中导入 `log` 函数,它需要两个类型为 `i32` 的参数作为输入以及一页内存64KB来存储字符串。
字符串将被写入偏移量 `0``data` 部分。`data` 部分是你的内存的覆盖,内存是在 JavaScript 部分分配的。 字符串将被写入偏移量 `0` 的数据段。数据段是你的内存的<ruby>叠加投影<rt>overlay</rt></ruby>,内存是在 JavaScript 部分分配的。
函数用关键字 `func` 标记。当进入函数时,栈是空的。在调用另一个函数之前,函数参数会被压入栈中(这里是偏移量和长度)(见 `call $log`)。当一个函数返回一个 `f32` 类型时(例如),当离开函数时,一个 `f32` 变量必须保留在栈中(但在本例中不是这样)。 函数用关键字 `func` 标记。当进入函数时,栈是空的。在调用另一个函数之前,函数参数会被压入栈中(这里是偏移量和长度)(见 `call $log`)。当一个函数返回一个 `f32` 类型时(例如),当离开函数时,一个 `f32` 变量必须保留在栈中(但在本例中不是这样)。
### 创建 .wasm 文件 ### 创建 .wasm 文件
WASM-text 和 WebAssembly 字节码有 1:1 的对应关系,这意味着你可以将 WASM-text 转换成字节码(反之亦然)。你已经有了 WASM-text现在将创建字节码。 WASM-text 和 WebAssembly 字节码是 1:1 对应的,这意味着你可以将 WASM-text 转换成字节码(反之亦然)。你已经有了 WASM-text现在将创建字节码。
转换可以通过 [WebAssembly Binary Toolkit][3]WABT来完成。从链接克隆仓库并按照安装说明进行安装。 转换可以通过 [WebAssembly Binary Toolkit][3]WABT来完成。从链接克隆仓库,并按照安装说明进行安装。
建立工具链后,打开控制台并输入以下内容,将 WASM-text 转换为字节码: 建立工具链后,打开控制台并输入以下内容,将 WASM-text 转换为字节码:
``` ```
`wat2wasm helloworld.wat -o helloworld.wasm` wat2wasm helloworld.wat -o helloworld.wasm
``` ```
你也可以用以下方法将字节码转换为 WASM-text 你也可以用以下方法将字节码转换为 WASM-text
``` ```
`wasm2wat helloworld.wasm -o helloworld_reverse.wat` wasm2wat helloworld.wasm -o helloworld_reverse.wat
``` ```
一个从 `.wasm` 文件创建的 `.wat` 文件不包括任何函数或参数名称。默认情况下WebAssembly 用它们的索引来识别函数和参数。 一个从 `.wasm` 文件创建的 `.wat` 文件不包括任何函数或参数名称。默认情况下WebAssembly 用它们的索引来识别函数和参数。
@ -84,42 +79,41 @@ WASM-text 和 WebAssembly 字节码有 1:1 的对应关系,这意味着你可
创建一个空的文本文件,并将其命名为 `helloworld.html`,然后打开你喜欢的文本编辑器并粘贴进去: 创建一个空的文本文件,并将其命名为 `helloworld.html`,然后打开你喜欢的文本编辑器并粘贴进去:
``` ```
&lt;!DOCTYPE  html&gt; <!DOCTYPE html>
&lt;html&gt; <html>
  &lt;head&gt; <head>
    &lt;meta  charset="utf-8"&gt; <meta charset="utf-8">
    &lt;title&gt;Simple template&lt;/title&gt; <title>Simple template</title>
  &lt;/head&gt; </head>
  &lt;body&gt; <body>
    &lt;script&gt; <script>
   
      var memory = new  WebAssembly.Memory({initial:1});
      function  consoleLogString(offset, length) { var memory = new WebAssembly.Memory({initial:1});
        var  bytes = new  Uint8Array(memory.buffer, offset, length);
        var  string = new  TextDecoder('utf8').decode(bytes);
        console.log(string);
      };
      var  importObject = { function consoleLogString(offset, length) {
        console: { var bytes = new Uint8Array(memory.buffer, offset, length);
          log:  consoleLogString var string = new TextDecoder('utf8').decode(bytes);
        }, console.log(string);
        js : { };
          mem:  memory
        } var importObject = {
      }; console: {
      log: consoleLogString
      WebAssembly.instantiateStreaming(fetch('helloworld.wasm'), importObject) },
      .then(obj  =&gt; { js : {
        obj.instance.exports.helloWorld(); mem: memory
      }); }
      };
    &lt;/script&gt;
  &lt;/body&gt; WebAssembly.instantiateStreaming(fetch('helloworld.wasm'), importObject)
&lt;/html&gt; .then(obj => {
obj.instance.exports.helloWorld();
});
</script>
</body>
</html>
``` ```
`WebAssembly.Memory(...)` 方法返回一个大小为 64KB 的内存页。函数 `consoleLogString` 根据长度和偏移量从该内存页读取一个字符串。这两个对象作为 `importObject` 的一部分传递给你的 WebAssembly 模块。 `WebAssembly.Memory(...)` 方法返回一个大小为 64KB 的内存页。函数 `consoleLogString` 根据长度和偏移量从该内存页读取一个字符串。这两个对象作为 `importObject` 的一部分传递给你的 WebAssembly 模块。
@ -128,16 +122,12 @@ WASM-text 和 WebAssembly 字节码有 1:1 的对应关系,这意味着你可
![Firefox setting][4] ![Firefox setting][4]
Stephan Avenwedde, [CC BY-SA 4.0][5]
> **注意:** 这样做会使你容易受到 [CVE-2019-11730][6] 安全问题的影响。 > **注意:** 这样做会使你容易受到 [CVE-2019-11730][6] 安全问题的影响。
现在,在 Firefox 中打开 `helloworld.html`,按下 **Ctrl**+**K** 打开开发者控制台。 现在,在 Firefox 中打开 `helloworld.html`,按下 `Ctrl+K` 打开开发者控制台。
![Debugger output][7] ![Debugger output][7]
Stephan Avenwedde, [CC BY-SA 4.0][5]
### 了解更多 ### 了解更多
这个 Hello World 的例子只是 MDN 的 [了解 WebAssembly 文本格式][8] 文档中的教程之一。如果你想了解更多关于 WebAssembly 的知识以及它的工作原理,可以看看这些文档。 这个 Hello World 的例子只是 MDN 的 [了解 WebAssembly 文本格式][8] 文档中的教程之一。如果你想了解更多关于 WebAssembly 的知识以及它的工作原理,可以看看这些文档。
@ -149,7 +139,7 @@ via: https://opensource.com/article/21/3/hello-world-webassembly
作者:[Stephan Avenwedde][a] 作者:[Stephan Avenwedde][a]
选题:[lujun9972][b] 选题:[lujun9972][b]
译者:[geekpi](https://github.com/geekpi) 译者:[geekpi](https://github.com/geekpi)
校对:[校对者ID](https://github.com/校对者ID) 校对:[wxy](https://github.com/wxy)
本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出 本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出