WebAssembly与JavaScript交互

一、概述

WebAssembly可以视为一种采用精简的”二进制格式”的“低等级”、“类汇编”语言。目前主流的浏览器均提供了对WebAssembly的支持,虽然WebAssembly的执行性能(它能够提供near-native的执行性能)是JavaScript无法比拟的,但是在表达能力和灵活性还是不如JavaScript,所以WebAssembly的出现并不是要取代JavaScript,而是作为JavaScript的“助手”,两者配合,各自发挥自身的优势,进而开发出更高质量的Web应用。

与其说WebAssembly像汇编,不如说它更像MSIL,因为它不是面向开发者的高级语言,而是高级语言的“编译目标”。虽然.NET 的运行时CLR不能直接执行C#、VB.NET和F#这样的高级语言编写的代码,而执行执行MSIL指令,但是我们可以将前者编译成后者。与之类似,我们也可以使用C、C++、C#、Rust和AssemblyScript(面向WebAssembly的TypeScript)作为开发语言,然后将代码编译成WebAssembly。

与汇编一样,虽然WebAssembly采用的是单纯的二进制格式,但是可以转换成文本形式。不仅如此,我们还可以按照这样的文本格式来编写程序(在接下来的内容中,我们提供的WebAssembly程序均采用这样的编写方式),并利用相应的工具将其编程成WebAssembly。WebAssembly文本采用一种名为S-expression的树形结构,我们定义的内容都存在与一个具体的“节点”中,每个节点通过小括号包裹起来,子节点直接内嵌于父节点中。由于模块是WebAssembly的基本部署和加载单元,所以module总是它们的根节点,如下所示的就是一个合法的最简单的WebAssembly程序。

(module)

为了让大家对WebAssembly文本的S-expression有一个大致的了解,我们采用这样的形势定义了一个进行整数加法运算的add函数。如下面的代码片段所示,我们定义的函数通过内嵌于module节点的(func)节点表示。它具有一些子节点,其中(export "add") 表示将这个函数以名称“add”进行导出,这意味着加载此模块的JavaScript应用可以直接调用此函数。额外三个节点(param $x i32) (param $y i32) (result i32)定义了函数的签名,揭示了此函数包含两个Int32(i32)的输入参数,返回值(结果)的类型也是Int32(i32)。为了提供可读性,同时也方面传参方面,我们将参数进行了命名($x和$y)

(module
  (func (export "add") (param $x i32) (param $y i32) (result i32)
    local.get $x
    local.get $y
    i32.add)
)

值得一提的是,WebAssembly仅仅支持4种数据类型,分别是32和64位的整数(i32和i64)和浮点数(f32和f64),连我们最常使用的字符串类型都支持。由于字符串本质上就是一组字符序列,而一个字符本质上是采用某种编码的一段字节序列,所以WebAssembly中针对字符串的处理需要自行解决编解码的工作,所以直接以S-expression文本的形式编写WebAssembly程序并不容易。

上面说WebAssembly更像MSIL,还因为执行WebAssembly的VM是一个“堆栈机(Stack Machine)”,意味着它采用“压栈”的方式传递参数。而汇编面向的是真正的机器语言,是“堆栈机(Stack Machine)”和“寄存器机(Register Machine)”的结合,不仅参数传递可以采用堆栈,也可以采用寄存器。这一点可以从add函数的实现看出来,由于最终需要执行i32.add指令,我们需要在这之前调用local.get指令将两个参数压入栈中。i32.add执行后的结果也将入栈,并成为函数返回值。

在对WebAssembly以及基于S-expression的文本形式有了基本了解之后,我们通过一个简单的例子来演示一下一个WebAssembly程序大体上如何编写,我们着重关注JavaScript应用和WebAssembly之间的功能交互。我们创建一个空的目录,并创建两个文本文件app.wat和index.html,前者代表以S-expression文本形式编写的WebAssembly程序,后者是一个空的HTML页面,我们利用它提供的JavaScript程序加载并执行编译后的WebAssembly模块。

二、WebAssembly程序(app.wat)

如下所示的是app.wat的内容,看起来很长,好在我们现在对S-expression有了基本的了解,所以理解起来没有什么问题。模块的第一个节点(func $print (import "js" "print") (param $op1 i32) (param $op2 i32) (param $op i32) (param $result i32))同样是定义了一个函数,我们将其命名为$print, 子节点(import "js" "print") 表明该函数是从作为宿主的Javascript应用中导入的,具体的路径为js->print。如果导入的Javascript对象通过变量imports表示,意味着imports.js.print表示的就是这个导入的对象。至于其余的四个节点(param $op1 i32) (param $op2 i32) (param $op i32) (param $result i32))同样是提供了函数的签名:个i32类型的参数,分别表示第一个操作数、第二个操作数、操作符(1,2,3,4分别代表加、减、乘、除)和执行结果。由于这个函数仅仅打印运算表达式,所以没有返回值。

 
 

(module

;; import js func print (op1, op2, op, result)

  (func $print (import "js" "print") (param $op1 i32) (param $op2 i32) (param $op i32) (param $result i32))

;; int32 add (int32 x, int32 y) (func $add (param $x i32) (param $y i32) (result i32) local.get $x local.get $y i32.add)

;; int32 sub(int32 x, int32 y) (func $sub (param $x i32) (param $y i32) (result i32) local.get $x local.get $y i32.sub)

;; int32 mul(int32 x, int32 y) (func $mul (param $x i32) (param $y i32) (result i32) local.get $x local.get $y i32.mul)

;; int32 div(int32 x, int32 y) (func $div (param $x i32) (param $y i32) (result i32) local.get $x local.get $y i32.div_u) (func $main ;; call print(1, 2, add(1,2), 1) i32.const 1 i32.const 2 i32.const 1 i32.const 1 i32.const 2 call $add call $print ;; call print(1, 2, sub(1,2), 2) i32.const 1 i32.const 2 i32.const 2 i32.const 1 i32.const 2 call $sub call $print ;; call print(1, 2, mul(1,2), 3) i32.const 1 i32.const 2 i32.const 3 i32.const 1 i32.const 2 call $mul call $print ;; call print(1, 2, div(1,2), 4) i32.const 1 i32.const 2 i32.const 4 i32.const 1 i32.const 2 call $div call $print ) (start $main) )

接下来我们定义了四个进行加、减、乘和除运算的函数add、sub、mul和div,它们与上面定义的add函数类似,不过由于缺少了(export “{funcname}”)节点,所以它们仅仅是四个内部函数而已。接下来,我们定义了一个$main函数,它会传入相同的参数(1、2)调用上述4个函数,并调用导入的print方法将包含结果的运算表达式打印出来。虽然命名为$main,但是它也仅仅是一个普通的函数而已,所以我们需要利用(start $main)节点将其作为入口函数,这样它就会在加载的时候自动执行了。

三、编译生成app.wasm

以文本形式编写的WebAssembly程序需要编译成二进制模块才能被加载执行,这里我们使用的wat2wasm这个工具,这个工具可以从这里下载。除了将wat文件转化成wasm文件的wat2wasm,下载包里还包含了其他一些有用的工具,比如进行反向操作的wasm2wat。上面编写的app.wat文件利用如下的命令就可以编译生成WebAssembly目标文件app.wasm。

wat2wasm app.wat -o app.wasm

四、JavaScript程序

如下所示的就是index.html文件的内容,我们着重关注其提供的JavaScript代码。我们首先定义被WebAssembly导入的用来输出运行表达式的函数print,然后按照导入路径js.print将其封装到一个导入对象中({"js":{"print":print}})。我们调用WebAssembly.instantiateStreaming函数以异步方式加载app.wasm模块,并创建对应的实例。具体下载app.wasm模块通过第一个参数提供的Promise(fetch("app.wasm"))完成,第二个参数代表导入对象。

<!DOCTYPE html>
<html>
<head></head>
<body>
    <script>
        var url="app.wasm";
        var print = (op1, op2, op, result) => {
            switch (op) {
                case 1:
                    console.log(`${op1} + ${op2} = ${result}`);
                    break;
                case 2:
                    console.log(`${op1} - ${op2} = ${result}`);
                    break;
                case 3:
                    console.log(`${op1} * ${op2} = ${result}`);
                    break;
                case 4:
                    console.log(`${op1} / ${op2} = ${result}`);
                    break;
                default:
                    console.log("invalid operator.");
}
        };
        WebAssembly.instantiateStreaming(fetch("app.wasm"), {"js":{"print":print}});
    </script>
</body>
</html>

五、跑起来看看

我们采用相应的方式将当前目录发布为本地web站点,比如执行Python命令(python -m http.server),然后我们利用浏览器访问此站点的默认文件index.html,浏览器的控制台输出就会看到WebAssembly模块初始化输出的四个运算表达式。

image

六、调用导出函数

上面我们演示了WebAssembly模块在初始化的时候调用导入的JavaScript函数,现在我们来演示JavaScript应用如何调用从WebAssembly导出的函数,为此我们将app.wat的代码改写成如下的形式,仅仅保留4个导出的函数add、sub、mul和div。

(module
  (func (export "add") (param $x i32) (param $y i32) (result i32)
    local.get $x
    local.get $y
    i32.add)

  (func (export "sub")  (param $x i32) (param $y i32) (result i32)
    local.get $x
    local.get $y
    i32.sub)

  (func (export "mul")  (param $x i32) (param $y i32) (result i32)
    local.get $x
    local.get $y
    i32.mul)

   (func (export "div")  (param $x i32) (param $y i32) (result i32)
    local.get $x
    local.get $y
    i32.div_u)
)

index.html中的Javascript代码也做了如下的修改:我们在调用WebAssembly.instantiateStreaming函数成功加载WebAssembly模块并创建对应模块实例后,利用返回结果的instance属性得到这个模块实例。模块导出的成员都保存在该实例的exports属性返回的集合中,为此我们从中提取出导出的四个返回,并利用它们完成对应的运算后,调用console.log函数将包含结果的运算表达式输出到控制台上。由于WebAssembly模块不在需要调用导入的函数,所以调用instantiateStreaming函数的时候不需要在指定导入对象。

 
 

<!DOCTYPE html> <html> <head></head> <body> <script> var url="app.wasm"; WebAssembly .instantiateStreaming(fetch("app.wasm")) .then(result => { var exports = result.instance.exports;
var result = exports.add(1,2); console.log(`1 + 2 = ${result}`); result = exports.sub(1,2); console.log(`1 - 2 = ${result}`); result = exports.mul(1,2); console.log(`1 * 2 = ${result}`); result = exports.div(1,2); console.log(`1 / 2 = ${result}`); }); </script> </body> </html>

程序运行之后,浏览器的控制台上依然会输出相同的结果。

image

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/350822.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

xxl-job之API的方式接入

文章目录 1 xxl-job1.1 简介1.2 分析1.3 学习xxl-job源码1.4 改造项目1.4.1 接口调用1.4.1.1 对接登录接口1.4.1.2 对接执行器接口1.4.1.3 对接任务接口 1.4.2 创建新注解1.4.3 自动注册核心1.4.4 自动装配 1 xxl-job 1.1 简介 xxl-job是一款非常优秀的任务调度中间件&#x…

《安富莱嵌入式周报》第331期:单片机实现全功能软件无线电,开源电源EEZ升级主控,ARM 汇编用户指南,UDS统一诊断服务解析,半导体可靠性设计手册

周报汇总地址&#xff1a;嵌入式周报 - uCOS & uCGUI & emWin & embOS & TouchGFX & ThreadX - 硬汉嵌入式论坛 - Powered by Discuz! 目录&#xff1a; 1、单片机实现低配版全功能软件无线电&#xff0c;范围0.5-30 MHz&#xff0c;支持SSB、AM、FM和CW …

Ubuntu20.4 Mono C# gtk 编程习练笔记(四)

连续实时绘图 图看上去不是很清晰&#xff0c;KAZAM录屏AVI尺寸80MB&#xff0c; 转换成gif后10MB, 按CSDN对GIF要求&#xff0c;把它剪裁缩小压缩成了上面的GIF&#xff0c;图像质量大不如原屏AVI&#xff0c;但应该能说明原意&#xff1a;随机数据随时间绘制在 gtk 的 drawin…

jQuery之ajax发送请求(table数据)

一般后端给我们的数据是这样的 比如下面是所有学员信息 访问网址&#xff1a;http://localhost:8080/student/all 前端&#xff0c;我们需要通过点击查询所有学员信息即可显示到下面列表中&#xff0c; 给查询全部学员按钮设置点击事件&#xff0c;点击就发送请求 $("…

基于LLaMA-Factory的微调记录

文章目录 数据模型准备基于网页的简单微调基于网页的简单评测基于网页的简单聊天 LLaMA-Factory是一个非常好用的无代码微调框架&#xff0c;不管是在模型、微调方式还是参数设置上都提供了非常完备的支持&#xff0c;下面是对微调全过程的一个记录。 数据模型准备 微调时一般…

【网络安全】2024年暗网威胁分析及发展预测

暗网因其非法活动而臭名昭著&#xff0c;现已发展成为一个用于各种非法目的的地下网络市场。 它是网络犯罪分子的中心&#xff0c;为被盗数据交易、黑客服务和邪恶活动合作提供了机会。为了帮助企业组织更好地了解暗网发展形势&#xff0c;近日&#xff0c;卡巴斯基的安全研究…

css3表格练习

1.效果图 2.html <div class"line"></div><h3>获奖名单</h3><!-- 表格 cellspacing内边距 cellpadding外边距--><table cellspacing"0" cellpadding"0" ><!-- thead表头 --><thead><tr>…

Linux篇:线程

一、线程概念&#xff1a;是进程内的一个执行分支&#xff0c;线程的执行粒度要比进程要细。 1、Linux中线程该如何理解&#xff1a; ①在Linux中&#xff0c;线程在进程“内部”执行&#xff0c;线程在进程的地址空间中进行。任何执行流要执行&#xff0c;都要有资源&#xf…

深度学习(6)--Keras项目详解

目录 一.项目介绍 二.项目流程详解 2.1.导入所需要的工具包 2.2.输入参数 2.3.获取图像路径并遍历读取数据 2.4.数据集的切分和标签转换 2.5.网络模型构建 2.6.绘制结果曲线并将结果保存到本地 三.完整代码 四.首次运行结果 五.学习率对结果的影响 六.Dropout操作…

IS-IS:06 ISIS路由汇总

与OSPF 协议相同&#xff0c; IS-IS 也能够通过路由聚合来减少路由条目。不同的是&#xff0c;OSPF 只能在ABR 和ASBR 路由器上进行路由聚合&#xff0c;而IS-IS 路由器能否进行路由聚合以及对什么样的路由才能进行聚合取决于路由器的类型及路由的类型。 在IS-IS 网络中&#x…

【shell-10】shell实现的各种kafka脚本

kafka-shell工具 背景日志 log一.启动kafka->(start-kafka)二.停止kafka->(stop-kafka)三.创建topic->(create-topic)四.删除topic->(delete-topic)五.获取topic列表->(list-topic)六. 将文件数据 录入到kafka->(file-to-kafka)七.将kafka数据 下载到文件-&g…

GPT应用程序的应用场景

GPT&#xff08;Generative Pre-trained Transformer&#xff09;应用程序具有广泛的应用场景&#xff0c;其强大的自然语言生成能力使其适用于多个领域。以下是一些常见的GPT应用场景&#xff0c;希望对大家有所帮助。北京木奇移动技术有限公司&#xff0c;专业的软件外包开发…

AI数字人-数字人视频创作数字人直播效果媲美真人

在科技的不断革新下&#xff0c;数字人技术正日益融入到人们的生活中。近年来&#xff0c;随着AI技术的进一步发展&#xff0c;数字人视频创作领域出现了一种新的创新方式——AI数字人。数字人视频通过AI算法生成虚拟主播&#xff0c;其外貌、动作、语音等方面可与真实人类媲美…

【开源】基于JAVA+Vue+SpringBoot的数据可视化的智慧河南大屏

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块三、系统展示四、核心代码4.1 数据模块 A4.2 数据模块 B4.3 数据模块 C4.4 数据模块 D4.5 数据模块 E 五、免责说明 一、摘要 1.1 项目介绍 基于JAVAVueSpringBootMySQL的数据可视化的智慧河南大屏&#xff0c;包含了GDP、…

Python实战项目Excel拆分与合并——合并篇

在实际工作中&#xff0c;我们经常会遇到各种表格的拆分与合并的情况。如果只是少量表&#xff0c;手动操作还算可行&#xff0c;但是如果是几十上百张表&#xff0c;最好使用Python编程进行自动化处理。下面介绍两种拆分案例场景&#xff0c;如何用Pandas实现Excel文件的合并。…

Android App开发基础(1)—— App的开发特点

本文介绍基于Android系统的App开发常识&#xff0c;包括以下几个方面&#xff1a;App开发与其他软件开发有什么不一样&#xff0c;App工程是怎样的组织结构又是怎样配置的&#xff0c;App开发的前后端分离设计是如何运作实现的&#xff0c;App的活动页面是如何创建又是如何跳转…

代码随想录算法刷题训练营day16

代码随想录算法刷题训练营day16&#xff1a;LeetCode(104)二叉树的最大深度 、LeetCode(559)n叉树的最大深度、LeetCode(111)二叉树的最小深度、LeetCode(222)完全二叉树的节点个数 LeetCode(104)二叉树的最大深度 题目 代码 /*** Definition for a binary tree node.* publ…

Web3.0投票如何做到公平公正且不泄露个人隐私

在当前的数字时代&#xff0c;社交平台举办投票活动已成为了一种普遍现象。然而&#xff0c;随之而来的是一些隐私和安全方面的顾虑&#xff0c;特别是关于个人信息泄露和电话骚扰的问题。期望建立一个既公平公正又能保护个人隐私的投票系统。Web3.0的出现为实现这一目标提供了…

qt学习:实战 http请求获取qq的吉凶

目录 利用的api是 聚合数据 的qq号码测吉凶 编程步骤 配置ui界面 添加头文件&#xff0c;定义网络管理者和http响应槽函数 在界面的构造函数里创建管理者对象&#xff0c;关联http响应槽函数 实现按钮点击事件 实现槽函数 效果 利用的api是 聚合数据 的qq号码测吉凶 先…

架构整洁之道-设计原则

4 设计原则 通常来说&#xff0c;要想构建一个好的软件系统&#xff0c;应该从写整洁的代码开始做起。这就是SOLID设计原则所要解决的问题。 SOLID原则的主要作用就是告诉我们如何将数据和函数组织成为类&#xff0c;以及如何将这些类链接起来成为程序。请注意&#xff0c;这里…