将无类型的JavaScript编译为WebAssembly没有意义,因为它最终会导致运行其中较慢的一个JavaScript。

相反,AssemblyScript专注于WebAssembly擅长的地方,并且不支持回退到无法提前有效编译的JavaScript的动态特性:

1.强制类型

  • 比 TypeScript 多了很多更细致的内置类型,以优化性能和内存占用,详情 文档 ;
  • 没有联合类型,因为所有内容都必须按设计进行静态类型化;
  • 不能使用 any 和 undefined 类型,以及枚举类型;
  • 可空类型的变量必须是引用类型,例如 ClassType | null,而不能是基本数据类型如 string、number、boolean;
  • 函数中的可选参数必须提供默认值,函数必须有返回类型,无返回值的函数返回类型需要是 void;
  • 如果初始化是整数字面量,则假定i32适合32位,否则假设为i64。
  • 如果初始化是浮点数,则假定为f64。
  • 否则,将评估初始化程序以获取其类型。
  • 可选功能参数需要初始化程序。
  • 总体来说 AssemblyScript 比 TypeScript 又多了很多限制,编写起来会觉得局限性很大; 用 AssemblyScript 来写 WebAssembly 经常会出现 tsc 编译通过但运行 WebAssembly 时出错的情况,这很可能就是你没有遵守以上限制导致的;但 AssemblyScript 通过修改 TypeScript 编译器默认配置能在编译阶段找出大多错误。

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

    2.详细说明

  • const和static readonly关键字当前与其他语言中的static const类似,需要编译时常量初始化程序,否则会回退到可变变量。
  • 在JavaScript中,类的静态字段在定义时始终进行求值,而在AssemblyScript中,内置树抖动时会在引用时懒惰地编译静态字段。
  • 当调用一个从Js中导出的可变参数长度的WebAssembly函数时,例如:

    1 export function add(a: i32 = 1, b: i32 = 2): i32 {
    2   return a + b;
    

    必须明确指定调用的实际参数数量,因为WebAssembly端没有与arguments.length等效的内容。 例如:

    1 exports._setargc(0);
    2 exports.sum(); // 3
    4 exports._setargc(1);
    5 exports.sum(2); // 4
    7 exports._setargc(2);
    8 exports.sum(2, 3); // 5

    与在JS中使用各种数值的数字类型不同,AssemblyScript将它们拆分为更精确的整数和浮点类型,这些类型直接映射到WebAssembly类型。

    1.原生类型

  • i32/u32  一个32位有符号/无符号整数。
  • i64/u64  一个64位有符号/无符号整数。
  • f32  一个32为浮点数。
  • f64 一个64位浮点数。
  • 2.模拟类型

  • i8/u8  一个8字节的有符号/无符号整数。
  • i16/u16  一个16字节的有符号/无符号整数。
  • bool  一个1字节无符号整数。
  • 注意:模拟类型被隐式符号扩展,分别被屏蔽以处理溢出。 在进行大量数学运算时,建议在计算中使用原生类型,然后将值存储回模拟类型,上面的"位"即byte,字节。

    3.针对特定类型

  • isize/usize  当目标是32位WebAssembly时,一个32位有符号/无符号整数;当目标是64位WebAssembly时,一个64位有符号/无符号整数。在其他语言中相当于void*, size_t等。
  • 注意,使用目标特定类型可能需要也可能不需要显式强制类型,具体取决于目标。

    注意,WASM64是未来的功能🦄,现在在任何地方都不受支持。

    4.特殊类型

  • void  表示函数无返回值。
  • 5.范围限制

    为方便起见,以下范围限制作为全局常量出现:

    ConstantValue i8.MIN_VALUE: i8 i8.MAX_VALUE: i8 i16.MIN_VALUE: i16 -32768 i16.MAX_VALUE: i16 32767 i32.MIN_VALUE: i32 -2147483648 i32.MAX_VALUE: i32 2147483647 i64.MIN_VALUE: i64 -9223372036854775808 i64.MAX_VALUE: i64 9223372036854775807 isize.MIN_VALUE: isize target specific: either i32.MIN_VALUE or i64.MIN_VALUE isize.MAX_VALUE: isize target specific: either i32.MAX_VALUE or i64.MAX_VALUE u8.MIN_VALUE: u8 u8.MAX_VALUE: u8 u16.MIN_VALUE: u16 u16.MAX_VALUE: u16 65535 u32.MIN_VALUE: u32 u32.MAX_VALUE: u32 4294967295 u64.MIN_VALUE: u64 u64.MAX_VALUE: u64 18446744073709551615 usize.MIN_VALUE: usize usize.MAX_VALUE: usize target specific: either u32.MAX_VALUE or u64.MAX_VALUE bool.MIN_VALUE: bool bool.MAX_VALUE: bool f32.MIN_VALUE: f32 -3.40282347e+38 f32.MAX_VALUE: f32 3.40282347e+38 f32.MIN_SAFE_INTEGER: f32 -16777215 f32.MAX_SAFE_INTEGER: f32 16777215 f32.EPSILON: f32 1.19209290e-07 f64.MIN_VALUE: f64 -1.7976931348623157e+308 f64.MAX_VALUE: f64 1.7976931348623157e+308 f64.MIN_SAFE_INTEGER: f64 -9007199254740991 f64.MAX_SAFE_INTEGER: f64 9007199254740991 f64.EPSILON: f64 2.2204460492503131e-16
    1 var i8val  : i8  = -128;  // 0x80
    2 var u8val  : u8  = i8val; // becomes 128 (0x80)
    3 var i16val : i16 = i8val; // becomes -128 through sign-extension (0xFF80)
    4 var u16val : u16 = i8val; // becomes 65408 through masking (0xFF80)
    5 var f32val : f32 = i8val; // becomes -128.0

    7.可比性

    可以在可分配性中概述的相同规则下执行比较不同类型的两个值而无需显式转换

    1.如果比较是绝对的 (==!=)

    2.如果比较是相对的  (><>=<=)  并且两种类型都具有相同的签名

    因为WebAssembly对签名和未签名的比较具有不同的操作。 比较使用较大的类型并返回一个bool。

    8.除法和余数

    划分或取出除以不同类型的两个值的余数,可以在可转让性中概述的相同规则下进行显式转换。

  • 如果两种类型都具有相同的签名
  • 因为WebAssembly对有符号和无符号除法和余数有不同的操作。 结果是更大的类型。

    9.位运算

    位运算(<<,>>)的结果是左类型,右类型隐式转换为左类型,如果左类型是有符号的,则执行算术移位;如果左类型是无符号,则执行逻辑移位。

    无符号右移(>>>)的结果是左类型(保留了符号),右类型隐式转换为左类型,但始终执行逻辑移位。

    如果左类型是浮点数,则会发出错误。

    WebAssembly基本概念到此结束,虽然有很多不理解的东西,但还是先记录在此,慢慢学习。

    参考原文:

    https://github.com/AssemblyScript/assemblyscript/wiki/Types

    WebAssembly 现状与实战