4

WebAssembly入门笔记[4]:利用Global传递全局变量

 7 months ago
source link: https://www.cnblogs.com/artech/p/17982720/hello_wasm_4
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

利用WebAssembly的导入导出功能可以灵活地实现宿主JavaScript程序与加载的单个wasm模块之间的交互,那么如何在宿主程序与多个wasm之间传递和共享数据呢?这就需要使用到Global这个重要的对象了。

一、数值类型全局变量
二、将JavaScript函数设置为全局变量
三、利用全局变量处理字符串

一、数值类型全局变量

Global全局变量支持多种值类型,包括数组(i32/i64和f32/f64)、向量和引用类型(externref和funcref)。下面的实例利用Global提供了全局计数的功能。在WebAssembly Text Format (WAT)文件app.wat中,我们从宿主JavaScript应用中导入了一个i32类型的可读写(mut表示可以修改)的全局变量,导入路径为“imports.counter”,我们将其命名为$counter。在用于自增的导出函数increment中,我们通过执行global.get指令读取全局变量的值,并将其加1之后,执行global.set指令对全局变量重新赋值。

(module
   (global $counter (import "imports" "counter") (mut i32))
   (func (export "increment")
       (i32.add (global.get $counter) (i32.const 1))
       (global.set $counter)
   )
)

在index.html文件中,我们在页面中添加了一个“Increment”按钮,并利用一个<span>显式计算器当前的值。JavaScript脚本通过调用WebAssembly.Global构造函数将代表全局变量的Global对象创建出来后,调用WebAssembly.instantiateStreaming加载app.wat编译生成的app.wasm模块文件,并将此Global对象包含在导入对象中。

<html>
    <head></head>
    <body>
        <span id="counter">0</span>
        <button id="btnInc">Increment</button>
        <script>
            const globalCounter = new WebAssembly.Global({ value: "i32", mutable: true }, 0);
            WebAssembly
                .instantiateStreaming(fetch("app.wasm"), {"imports":{"counter":globalCounter}})
                .then(results => {
                   document.getElementById("btnInc").onclick = ()=>{
                        results.instance.exports.increment();
                        document.getElementById("counter").innerText = globalCounter.value;
                   };
                });
        </script>
    </body>
</html>

wasm模块充成功导入后,我们注册了按钮的click事件,使之在调用导出的increment函数后,重新刷新计数器的值。如下图所示,针对“Increment”的每次点击都将计数器加1(源代码)。

image

二、将JavaScript函数设置为全局变量

除了四种数值类型,Global还支持两种引用类型externref和funcref,利用externref可以将宿主应用提供的任意JavaScript对象作为全局变量,下面的实例演示利用这种方式实现了与类似的功能。如下面的代码片段所示,新的app.wat导入了一个类型为externref的全局变量,对应着数组应用提供的一个用来对全局计数自增的Javascript函数。

(module
   (func $apply (import "imports" "apply") (param externref))
   (global $increment (import "imports" "increment") externref)
   (func $main
        (call $apply (global.get $increment))
   )
   (start $main)
)

由于JavaScript函数的引用在.wasm模块中并不能直接执行,所以我们不得不导入一个apply函数“回传”到宿主应用中执行。我们修改的应用用来统计导入的wasm模块的数量,所以我们在入口函数$main中利用apply调用了全局变量$increment引用的函数。

在index.html,我们在页面中添加了一个“Load”按钮来加载app.wat编译生成的app.wasm模块。JavaScript脚本利用counter变量表示加载的wasm模块数量,并通过调用WebAssembly.Global构造函数创建了rexternref类型的全局变量,其值为一个对counter自增的函数。

<html>
    <head></head>
    <body>
        <p>There are totally <span  id="counter" style= "color: read”>0</span> wasm modules loaded. </p>
        <button id="btnLoad">Load</button>
        <script>
            var counter = 0;
            const globalIncrement = new WebAssembly.Global({ value: "externref"}, ()=>counter++);
            var apply = func => func();
            document.getElementById("btnLoad").onclick = ()=>{
                WebAssembly
                    .instantiateStreaming(fetch("app.wasm"), {"imports":{"increment":globalIncrement,"apply": apply }})
                    .then(_=>{
                        document.getElementById("counter").innerText = counter;
                    })
                };
        </script>
    </body>
</html>

我们将这个Global对象包含到导入的对象中,并在导入成功后刷新显式的计数器,所以程序运行后将会显式当前加载的wasm模块数量(源代码)。

image

三、利用全局变量处理字符串

WebAssembly目前并没有提供针对字符串类型的直接支持,而是单纯地将其作为字节序列看到。目前字符串在宿主程序与wasm模块之间的传递只有通过Memory来实现。由于Javascript具有处理字符串的能力,wasm模块可以将字符串作为externref回传到宿主程序进行处理。在接下来演示的程序中,我们在app.wat中定义一个“字符类型(实际上是externref类型)”的全局变量,导出的greet函数通过调用导入的print函数将其输出。

(module
   (func $print (import "imports" "print") (param externref))
   (global $message (import "imports" "message") (mut externref))
   (func (export "greet")
        (call $print (global.get $message))
   )
)

在index.html中,我们在页面上放置了三个按钮,让它们在命名为“greet”的<div>中分别显示“Good Morning”、“Good Afternoon”和“Good Evening”三条问候语。具体的问候语通过函数print输出,它的参数就是代表输出文本的字符串。

<html>
    <head></head>
    <body>
        <div id="greet"></div>
        <button id="btnMorning">Morning</button>
        <button id="btnAfternoon">Afternoon</button>
        <button id="btnEvening">Evening</button>
        <script>
            var print = (msg) => {
                console.log(msg);
                document.getElementById("greet").innerText = msg;
            }
            const globalMsg = new WebAssembly.Global({ value: "externref", mutable: true }, "Good Morning!");
            console.log(globalMsg.value);
            WebAssembly
                .instantiateStreaming(fetch("app.wasm"), {"imports":{"message":globalMsg, "print":print}})
                .then(results => {
                    var greet = results.instance.exports.greet;
                    console.log(greet);
                    document.getElementById("btnMorning").onclick = ()=>{
                        globalMsg.value = "Good Morning!";
                        greet();
                    };
                    document.getElementById("btnAfternoon").onclick = ()=>{
                        globalMsg.value = "Good Afternoon!";
                        greet();
                    };
                    document.getElementById("btnEvening").onclick = ()=>{
                        globalMsg.value = "Good Evening!";
                        greet();
                    };
                });
        </script>
    </body>
</html>

我们定义了一个externref类型的Global对象来引用带输出的问候语文本,并在加载app.wasm木块使将其包含到导入对象中。三个按钮的click事件处理程序通过调用导出的greet函数输出对于的问候语,但是在调用此函数之前会对Global对象进行相应的赋值(源代码)。

image

WebAssembly入门笔记[1]:与JavaScript的交互
WebAssembly入门笔记[2]:利用Memory传递字节数据
WebAssembly入门笔记[3]:利用Table传递引用
WebAssembly入门笔记[4]:利用Global传递全局变量


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK