WebAsm:具有Lisp语法的Forth

2020-12-04 20:38:48

(家庭)第四种语义和s-expr语法的结合使WebAsm文本格式(WAT / .wat)可能是当今最强大的编程语言。很快,我希望它会消除所有其他语言。玩笑

关于webasm的说法是一个普遍的神话,它只是javascript AST(抽象语法树)的二进制格式。那真的是卖webasm短。

webasm就如同它是一台堆栈机一样。 webasm虚拟机中没有寄存器。

与之不同的是,webasm函数具有显式参数,局部变量,并且仅返回一个当前值(当前)。webasm还缺少用于堆栈操作的丰富单词集[dup swap roll ...]

webasm的文本格式使用lisp / s-expr语法。语言语法也具有许多Lisp样式的影响。令人印象深刻!最终,某个有权力的人意识到我们应该对所有内容使用s-expr,并丢弃xml和json。

所有webasm全局变量,局部变量,参数和函数都是强静态类型的。仅允许使用几种简单类型(整数和浮点数)和引用类型。 [类型:: == i32,i64,f32,f64]。

请注意,没有字符串,甚至没有数组。也没有无符号类型,尽管有些指令可以标记为有符号/无符号[右移,更大等号...]。

wasm的内存布局就像您在简单的80台计算机或不太现代的微控制器上找到的那样。内存从零开始,一直到指定的最大值(最大值是可动态增长的,请参阅C的brk())。

内存指针只是内存数组中的i32 / i64整数偏移量(与真实指针完全相同...)。

wasm指令在i32.add,f32.neg之类的代码中处于汇编程序的最末尾,还有一些更高级别(更受控制)的控制形式,例如(block)和(loop)。不能任意跳跃[安全功能]。实际上,代码和数据不在同一地址空间中,并且对于wasm程序员而言,代码空间是完全不透明的。

顶层形式是(模块...),可以包含全局变量和函数。还有导入,导出,内存大小规范,甚至类型定义。

(模块(func $ malloc(参数$ len i32)(结果i32);; ...返回i32的主体))

规范地将指令平放,但文本格式也允许嵌套更好的s-expr语法。

参数,局部变量甚至函数都可以通过索引进行访问,但是它们也可以被赋予可选的标签。这和s-expr选项使直接用语言本身编码变得轻而易举。玩笑

整个wasm代码是从javascript调用的,javascript具有旨在从wasm文件加载和导入函数的api(例如,请参见下文)

webasm函数可以像这样导出到javascript:注意wasm返回值如何隐式地退出时栈上的内容; wat编译器检查堆栈Arity和类型。

(func(export" addNumbers")(param $ a i32)(param $ b i32)(结果i32)(i32.add(local.get $ a)(local.get $ b)))

//从javascript这样调用(经过一些咒语)后,var someNumber = 17; const sum = wasm.instance.exports.addNumbers(12,someNumber);

Javscript还可以使用传递给WebAssembly咒语的2级对象将函数导出到wasm-space中。

//导出console.log(i32)functionvar importObject = {console:{log:function(num){console.log(num +" 0x" +(num>>>>> 0).toString (16)); }},}; WebAssembly.instantiateStreaming(fetch(" malloc.wasm"),importObject).then(function(wasm){....在这里访问wasm函数});

javascript导出必须由wasm模块显式导入,并提供本地函数签名(和名称)。与js对应项不同,wasm函数是强类型的静态类型,仅使用单个i32参数

(导入"控制台""日志(#func $ js_log(参数$ i i32)))...(调用$ js_log(i32.const 15));;调用javascript的示例

最好有另一个wasm类型,一个不透明,只读的javascript参考对象,我们可以从javascript,store中接收该对象,然后在我们的标注/回调中将其传递回javascript。

几乎在编写第一个基本的wat程序后,您都会错过此功能。

遗憾的是,externref规范不是原始webasm规范的一部分,而是一个较新的功能。我不确定它的普及程度。

(便宜的)malloc()示例函数的WAT源代码,以及另一个(甚至更便宜的)dump_range()函数的WAT源代码,该函数调用javascript将字节写入调试控制台。

(模块(内存(导入(" js"" mem")10)(导入"控制台"" log" i i32)))(全局$ memend i32(i32.const 4096))(全局$ sbrk(mut i32)(i32.const 0));; -----在wasm空间中分配一些内存(func(export " malloc")(参数$ len i32)(结果i32)(本地$ newbrk i32)(本地$ mem i32)(local.set $ newbrk(i32.add(global.get $ sbrk))(本地.get $ len)))(if(i32.ge_u(local.get $ newbrk)(global.get $ memend))(返回(i32.const 0)))(local.set $ mem(global.get $ sbrk ))(global.set $ sbrk(local.get $ newbrk))(local.get $ mem));; -----使用js回调将字节写入wasm空间内存中(func(export" dump_range& #34;)(param $ start i32)(param $ len i32)(local $ i i32)(local $ end i32)(local.set $ end(i32.add(local.get $ start)(local.get $ len)))(local.set $ i(local.get $ start))(块$ break2(循环$ head1(br_if $ break2(i32.eq(local.get $ i)(local.get $ end)))) (致电$ js_log(i32.load8_u(local.get $ i)))(local.set $ i(i32.add( i32.const 1)(local.get $ i)))(br $ head1))))))

https://webassembly.github.io/wabt/doc/wat2wasm.1.html用于设置webasm实例并调用我们编写的函数的HTML(以下链接为完整文件):

实时示例:(打开您的JavaScript控制台查看情况,例如,在Firefox上按[F12])/src/malloc.html < script>"使用严格的&#34 ;;(function(){var wasm; var memory = new WebAssembly.Memory({initial:10}); // 640K,比任何人都可以使用var importObject = {console:{log:function(arg){console.log(arg);}},js:{mem:memory}}; WebAssembly.instantiateStreaming(fetch(" malloc.wasm"),importObject) .then(function(_wasm){wasm = _wasm;});函数write_seq(ptr,len){var bytes = new Uint8Array(memory.buffer,ptr,len); for(var i = 0; i< len; + + i){bytes [i] = i;}}函数malloc_test(e){const nbytes = 16; const ptr = wasm.instance.exports.malloc(nbytes); console.log("写入前:& #34;); wasm.instance.exports.dump_range(ptr,nbytes); write_seq(ptr,nbytes); console.log("写后:"); wasm.instance.exports.dump_range( ptr,nbytes);} runbtn.addEventListener(" click&#34 ;, malloc_test);})();< / script>