虽然说只要高级语言能转换成 LLVM IR,就能被编译成 WebAssembly 字节码,官方也推荐c/c++的方式,但是让一个前端工程师去熟练使用c/c++显然是有点困难,那么TypeScript 的方式便是前端编写 WebAssembly 最佳选择。

要将TypeScript 编译为WebAssembly,就要用到AssemblyScript编译器了。

AssemblyScript使用Binaryen(Emscripten的WebAssembly后端)将严格类型化的TypeScript(基本的带有类型的JavaScript)编译为WebAssembly,虽然它提供了几种新的特定于WebAssembly的类型和内置函数,但它本身并不是一种真正的语言,而是一种编译器变体。 它生成精简的WebAssembly模块,只需要一个npm安装。

1.安装

推荐使用cnpm安装,npm太慢了。

 cnpm install --save-dev AssemblyScript/assemblyscript

执行过程如下,安装完成之后只有一个node_modules文件夹

 E:\Code\assembly>cnpm install --save-dev AssemblyScript/assemblyscript
- [@AssemblyScript/assemblyscript] install from git github:AssemblyScript/assemblyscript, may be very slow, please keep patience
√ Installed packages
√ Linked latest versions
√ Run scripts
Recently updated (since --): packages (detail see file E:\Code\assembly\node_modules\.recently_updates.txt)
√ All packages installed ( packages installed from npm registry, packages installed from git, used 1m(network 1m), speed .27kB/s, json (.43kB), tarball .23kB)

2.创建项目

正确安装AssemblyScript后,它会提供一个名为asinit的小型实用工具来构建一个新项目,例如,在当前目录中:

 npx asinit .

就像使用express初始化项目一样,执行过程如下

 E:\Code\assembly>npx asinit .
Version: 0.6. This command will make sure that the following files exist in the project
directory 'E:\Code\assembly': ./assembly
Directory holding the AssemblyScript sources being compiled to WebAssembly. ./assembly/tsconfig.json
TypeScript configuration inheriting recommended AssemblyScript settings. ./assembly/index.ts
Exemplary entry file being compiled to WebAssembly to get you started. ./build
Build artifact directory where compiled WebAssembly files are stored. ./build/.gitignore
Git configuration that excludes compiled binaries from source control. ./index.js
Main file loading the WebAssembly module and exporting its exports. ./package.json
Package info containing the necessary commands to compile to WebAssembly. The command will try to update existing files to match the correct settings
for this instance of the compiler in 'E:\Code\assembly\node_modules\_assemblyscript@0.6.0@assemblyscript'. Do you want to proceed? [Y/n] y - Making sure that the project directory exists...
Exists: E:\Code\assembly - Making sure that the 'assembly' directory exists...
Created: E:\Code\assembly\assembly - Making sure that 'assembly/tsconfig.json' is set up...
Created: E:\Code\assembly\assembly\tsconfig.json - Making sure that 'assembly/index.ts' exists...
Created: E:\Code\assembly\assembly\index.ts - Making sure that the 'build' directory exists...
Created: E:\Code\assembly\build - Making sure that 'build/.gitignore' is set up...
Created: E:\Code\assembly\build\.gitignore - Making sure that 'package.json' contains the build commands...
Updated: E:\Code\assembly\package.json - Making sure that 'index.js' exists...
Created: E:\Code\assembly\index.js Done! To edit the entry file, open 'assembly/index.ts' in your editor of choice.
Create as many additional files as necessary and use them as imports. To build the entry file to WebAssembly when you are ready, run: npm run asbuild Running the command above creates the following binaries incl. their respective
text format representations and source maps: ./build/untouched.wasm
./build/untouched.wasm.map
./build/untouched.wat ^ The untouched WebAssembly module as generated by the compiler.
This one matches your sources exactly, without any optimizations. ./build/optimized.wasm
./build/optimized.wasm.map
./build/optimized.wat ^ The optimized WebAssembly module using default optimization settings (-O2s).
You can change the optimization settings in 'package.json'. Additional documentation is available at the AssemblyScript wiki: https://github.com/AssemblyScript/assemblyscript/wiki Have a nice day! E:\Code\assembly>

完成的目录结构是这样的:

目录结构含义:

assembly / index.ts上的示例性条目文件

index.js中的通用索引文件,用于加载已编译的二进制文件和必要的配置文件,如package.json和tsconfig.json

初始化后,只需在编码时使用现有的TypeScript工具,并使用编译器手动构建到WebAssembly,或者使用生成的构建任务:按照提示,运行以下命令:

 npm run asbuild

build文件夹初始化只有一个.gitignore,该命令会在build文件夹中创建.wat与.wasm文件

以上只是构建一个示例项目,如果需要适合开发的安装,可以通过克隆GitHub存储库来实现:

 $> git clone https://github.com/AssemblyScript/assemblyscript.git
$> cd assemblyscript
$> npm install
$> npm link

请注意,编译器的新克隆将使用dist /中的分发文件,但它也可以在npm运行清理后直接通过ts节点运行源,这在开发中很有用。 也可以通过运行asc -v来检查这种情况(如果它声明-dev则运行源)。

3.使用编译器

类似于TypeScript的tsc编译为JavaScript,AssemblyScript的asc编译为WebAssembly:

例如,用 TypeScript 实现斐波那契序列计算的模块 f.ts 如下:

 export function f(x: i32): i32 {
if (x === 1 || x === 2) {
return 1;
}
return f(x - 1) + f(x - 2)
}

执行以下asc命令,就能把以上代码编译成可运行的 WebAssembly 模块,比起c/c++的编译方式,这种人性化多了。

 asc f.ts -o f.wasm

以上代码中出现了一个新的内置类型 i32,这是 AssemblyScript 在 TypeScript 的基础上内置的类型。 AssemblyScript 和 TypeScript 有细微区别,AssemblyScript 是 TypeScript 的子集,为了方便编译成 WebAssembly 在 TypeScript 的基础上加了更严格的类型限制, 区别如下:

  • 比 TypeScript 多了很多更细致的内置类型,以优化性能和内存占用,详情文档;
  • 不能使用 any 和 undefined 类型,以及枚举类型;
  • 可空类型的变量必须是引用类型,而不能是基本数据类型如 string、number、boolean;
  • 函数中的可选参数必须提供默认值,函数必须有返回类型,无返回值的函数返回类型需要是 void;
  • 不能使用 JS 环境中的内置函数,只能使用 AssemblyScript 提供的内置函数

总体来说 AssemblyScript 比 TypeScript 又多了很多限制,编写起来会觉得局限性很大; 用 AssemblyScript 来写 WebAssembly 经常会出现 tsc 编译通过但运行 WebAssembly 时出错的情况,这很可能就是你没有遵守以上限制导致的;但 AssemblyScript 通过修改 TypeScript 编译器默认配置能在编译阶段找出大多错误。

AssemblyScript 的实现原理其实也借助了 LLVM,它通过 TypeScript 编译器把 TS 源码解析成 AST,再把 AST 翻译成 IR,再通过 LLVM 编译成 WebAssembly 字节码实现; 上面提到的各种限制都是为了方便把 AST 转换成 LLVM IR。

asc编译器具体API如下

 SYNTAX
asc [entryFile ...] [options] EXAMPLES
asc hello.ts
asc hello.ts -b hello.wasm -t hello.wat
asc hello1.ts hello2.ts -b -O > hello.wasm OPTIONS
--version, -v Prints just the compiler's version and exits.
--help, -h Prints this message and exits.
--optimize, -O Optimizes the module. Also has the usual shorthands: -O Uses defaults. Equivalent to -O2s
-O0 Equivalent to --optimizeLevel 0
-O1 Equivalent to --optimizeLevel 1
-O2 Equivalent to --optimizeLevel 2
-O3 Equivalent to --optimizeLevel 3
-Oz Equivalent to -O but with --shrinkLevel 2
-O3s Equivalent to -O3 with --shrinkLevel 1 etc. --optimizeLevel How much to focus on optimizing code. [0-3]
--shrinkLevel How much to focus on shrinking code size. [0-2, s=1, z=2]
--validate, -c Validates the module using Binaryen. Exits if invalid.
--baseDir Specifies the base directory of input and output files.
--outFile, -o Specifies the output file. File extension indicates format.
--binaryFile, -b Specifies the binary output file (.wasm).
--textFile, -t Specifies the text output file (.wat).
--asmjsFile, -a Specifies the asm.js output file (.js).
--idlFile, -i Specifies the WebIDL output file (.webidl).
--tsdFile, -d Specifies the TypeScript definition output file (.d.ts).
--sourceMap Enables source map generation. Optionally takes the URL
used to reference the source map from the binary file.
--debug Enables debug information in emitted binaries.
--noAssert Replaces assertions with just their value without trapping.
--noEmit Performs compilation as usual but does not emit code.
--importMemory Imports the memory instance provided by the embedder.
--sharedMemory Declare memory as shared by settings the max shared memory.
--memoryBase Sets the start offset of compiler-generated static memory.
--importTable Imports the function table instance provided by the embedder.
--noLib Does not include the shipped standard library.
--lib Adds one or multiple paths to custom library components and
uses exports of all top-level files at this path as globals.
--use, -u Aliases a global object under another name, e.g., to switch
the default 'Math' implementation used: --use Math=JSMath
--trapMode Sets the trap mode to use. allow Allow trapping operations. This is the default.
clamp Replace trapping operations with clamping semantics.
js Replace trapping operations with JS semantics. --runPasses Specifies additional Binaryen passes to run after other
optimizations, if any. See: Binaryen/src/passes/pass.cpp
--enable Enables additional (experimental) WebAssembly features. sign-extension Enables sign-extension operations
mutable-global Enables mutable global imports and exports
bulk-memory Enables bulk memory operations
simd Enables SIMD types and operations.
threads Enables threading and atomic operations. --transform Specifies the path to a custom transform to 'require'.
--measure Prints measuring information on I/O and compile times.
--noColors Disables terminal colors.

编译器API也可以以编程方式使用。 它接受与CLI相同的选项,但也允许您覆盖stdout和stderr和/或提供回调:

 const asc = require("assemblyscript/cli/asc");
asc.main([
"myModule.ts",
"--binaryFile", "myModule.wasm",
"--optimize",
"--sourceMap",
"--measure"
], {
stdout: process.stdout,
stderr: process.stderr
}, function(err) {
if (err)
throw err;
...
});

可以通过编程方式获取可用的命令行选项:

 const options = require("assemblyscript/cli/asc.json");
...

您也可以直接编译源字符串,例如在浏览器环境中:

 const { binary, text, stdout, stderr } = asc.compileString(`...`, { optimize: 2 });
... 

4.使用编译的.wasm模块

index.js示例代码如下:

 const fs = require("fs");
const compiled = new WebAssembly.Module(fs.readFileSync(__dirname + "/build/optimized.wasm"));
const imports = {};
Object.defineProperty(module, "exports", {
get: () => new WebAssembly.Instance(compiled, imports).exports
});

使用上面编译的f.wasm

 fetch('f.wasm') // 网络加载 f.wasm 文件
.then(res => res.arrayBuffer()) // 转成 ArrayBuffer
.then(WebAssembly.instantiate) // 编译为当前 CPU 架构的机器码 + 实例化
.then(mod => { // 调用模块实例上的 f 函数计算
console.log(mod.instance.f(50));
});

5.参考文献:

GitHub—AssemblyScript 开源项目

吴浩麟—WebAssembly 现状与实战

WebAssembly学习(三):AssemblyScript - TypeScript到WebAssembly的编译的更多相关文章

  1. WebAssembly学习(二):Windows10下WebAssembly C/C++编译环境的搭建与Hello World尝试

    首先,不论是在Windows.Linux还是Mac上,Webassembly的编译都是主要依赖于Emscripten SDK这个工具的.但是,在这里必须要吐槽一下,不论是WebAssembly官网.W ...

  2. WebAssembly学习(六):AssemblyScript - 限制与类型

    一.限制 将无类型的JavaScript编译为WebAssembly没有意义,因为它最终会导致运行其中较慢的一个JavaScript. 相反,AssemblyScript专注于WebAssembly擅 ...

  3. WebAssembly学习(一):认识WebAssembly

    WebAssembly作为一门新兴起的技术,在 JavaScript 圈非常的火!人们都在谈论它多么多么快,怎样怎样改变 Web 开发领域,被各大巨头所推广,这篇文章对其做一个简单的了解认识,本文非原 ...

  4. HTTP学习三:HTTPS

    HTTP学习三:HTTPS 1 HTTP安全问题 HTTP1.0/1.1在网络中是明文传输的,因此会被黑客进行攻击. 1.1 窃取数据 因为HTTP1.0/1.1是明文的,黑客很容易获得用户的重要数据 ...

  5. TweenMax动画库学习(三)

    目录               TweenMax动画库学习(一)            TweenMax动画库学习(二)            TweenMax动画库学习(三)           ...

  6. Struts2框架学习(三) 数据处理

    Struts2框架学习(三) 数据处理 Struts2框架框架使用OGNL语言和值栈技术实现数据的流转处理. 值栈就相当于一个容器,用来存放数据,而OGNL是一种快速查询数据的语言. 值栈:Value ...

  7. vue.js 学习笔记3——TypeScript

    目录 vue.js 学习笔记3--TypeScript 工具 基础类型 数组 元组 枚举 字面量 接口 类类型 类类型要素 函数 函数参数 this对象和类型 重载 迭代器 Symbol.iterat ...

  8. 4.机器学习——统计学习三要素与最大似然估计、最大后验概率估计及L1、L2正则化

    1.前言 之前我一直对于“最大似然估计”犯迷糊,今天在看了陶轻松.忆臻.nebulaf91等人的博客以及李航老师的<统计学习方法>后,豁然开朗,于是在此记下一些心得体会. “最大似然估计” ...

  9. DjangoRestFramework学习三之认证组件、权限组件、频率组件、url注册器、响应器、分页组件

    DjangoRestFramework学习三之认证组件.权限组件.频率组件.url注册器.响应器.分页组件   本节目录 一 认证组件 二 权限组件 三 频率组件 四 URL注册器 五 响应器 六 分 ...

随机推荐

  1. iOS gzip解压

    1. 导入libz库(如:libz 1.2.5.dylib) 2. 引入头文件 #import "zlib.h" 3. 实现解压(输入输出都为NSData对象) -(NSData ...

  2. android开发之Animation(五)

    android开发之Animation的使用(五) 本博文主要讲述的是Animation中的AnimationLisenter的用法,以及此类的一些生命周期函数的调用,代码实比例如以下: MainAc ...

  3. 1.MySQL与MongoDB的操作对比,以及区别

    转自:https://www.cnblogs.com/chris-oil/p/4982490.html MySQL与MongoDB都是开源的常用数据库,但是MySQL是传统的关系型数据库,MongoD ...

  4. isInstanceOf,asInstanceOf,classOf[T]

    一.scala中把classOf[T]看成Java里的T.class, obj.isInstanceOf[T]看成 obj instanceof T, obj.asInstanceOf[T]看成(T) ...

  5. 关于iOS声音识别的框架

    你好,我现在的项目中需要用到"声纹识别"这方面的需求,以前没做过,请教了.有没有这方面的框架和工具? 关于iOS声音识别的框架 >> ios这个答案描述的挺清楚的:ht ...

  6. OpenGL编程(四)改变窗口大小时保持图形的原形

    前面的例子,当我们通过拖拉的方法改变窗口的长宽比例时,窗口里的图形的长宽也相应地伸缩,导致图形变形.如下图: 正如上图所示,当我们把窗口宽度拉长后,图形就会显得比较胖.同样,当我们把窗口的高度拉长后, ...

  7. POJ-1001 Exponentiation 高精度算法

    题目链接:https://cn.vjudge.net/problem/POJ-1001 以前写过一个高精度乘法,但是没有小数点,实现起来也没什么难得, 现在把代码都般过来,等会把旧电脑弄一弄,暂时就不 ...

  8. [转载]-分布式之redis复习精讲

    原创地址:https://www.cnblogs.com/rjzheng/p/9096228.html 看这篇文章前,我看的是另一个人博客上的文章.看到最后(评论这一块)很多人就指出这并非原创而是抄袭 ...

  9. nl---统计行号

    nl命令读取 file 参数(缺省情况下标准输入),计算输入中的行号,将计算过的行号写入标准输出.在输出中,nl命令根据您在命令行中指定的标志来计算左边的行.输入文本必须写在逻辑页中.每个逻辑页有头. ...

  10. python main函数

    关于Python的主(main)函数问题 2007-07-23 19:14 初次接触Python的人会很不习惯Python没有main主函数.这里简单的介绍一下,在Python中使用main函数的方法 ...