# 使用编译器

¥Using the compiler

与 TypeScript 的 tsc 转换为 JavaScript 类似,AssemblyScript 的 asc 编译为 WebAssembly。

¥Similar to TypeScript's tsc transpiling to JavaScript, AssemblyScript's asc compiles to WebAssembly.

# 编译器选项

¥Compiler options

编译器支持命令行、配置文件中和以编程方式提供的各种选项。在命令行上,它看起来像这样:

¥The compiler supports various options available on the command line, in a configuration file and programmatically. On the command line, it looks like this:

# 入口文件

¥Entry file(s)

非选项参数被视为条目文件的名称。单个程序可以有多个条目,每个条目的导出都会成为 WebAssembly 模块的导出。非入口文件的导入文件的导出不会成为 WebAssembly 模块导出。

¥Non-option arguments are treated as the names of entry files. A single program can have multiple entries, with the exports of each entry becoming the exports of the WebAssembly module. Exports of imported files that are not entry files do not become WebAssembly module exports.

asc entryFile.ts

# 一般的

¥General

--version, -v         Prints just the compiler's version and exits.
--help, -h            Prints this message and exits.
--config              Configuration file to apply. CLI arguments take precedence.
--target              Configuration file target to use. Defaults to 'release'.

# 优化

¥Optimization

编译器可以优化速度 (-Ospeed) 和大小 (-Osize),并生成调试版本 (--debug)。

¥The compiler can optimize for both speed (-Ospeed) and size (-Osize), as well as produce a debug build (--debug).

--optimize, -O        Optimizes the module. Typical shorthands are:

                        Default optimizations   -O
                        Make a release build    -O --noAssert
                        Make a debug build      --debug
                        Optimize for speed      -Ospeed
                        Optimize for size       -Osize

--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]
--converge            Re-optimizes until no further improvements can be made.
--noAssert            Replaces assertions with just their value without trapping.
--uncheckedBehavior   Changes the behavior of unchecked() expressions.
                      Using this option can potentially cause breakage.

                        default  The default behavior: unchecked operations are
                                 only used inside of unchecked().
                        never    Unchecked operations are never used, even when,
                                 inside of unchecked().
                        always   Unchecked operations are always used if possible,
                                 whether or not unchecked() is used.

优化级别也可以手动调整:--optimizeLevel (0-3) 表示编译器对优化代码的关注程度,而 --shrinkLevel (0-2, 1=s, 2=z) 表示编译器在代码生成和优化期间保持较小尺寸的程度。两者的简写都是 -O[optimizeLevel][shrinkLevel] ,收缩级别可以通过可选地附加字母 s (1) 或 z (2) 来指示。

¥Optimization levels can also be tweaked manually: --optimizeLevel (0-3) indicates how much the compiler focuses on optimizing the code with --shrinkLevel (0-2, 1=s, 2=z) indicating how much it focuses on keeping the size low during code generation and while optimizing. A shorthand for both is -O[optimizeLevel][shrinkLevel] , with shrink level indicated by optionally appending the letter s (1) or z (2).

# 输出

¥Output

典型的输出格式是 WebAssembly 二进制(.wasm,--outFile)和/或文本格式(.wat,--textFile)。通常,两者一起使用来运行和检查生成的代码。

¥Typical output formats are WebAssembly binary (.wasm, --outFile) and/or text format (.wat, --textFile). Often, both are used in tandem to run and also inspect generated code.

--outFile, -o         Specifies the WebAssembly output file (.wasm).
--textFile, -t        Specifies the WebAssembly text output file (.wat).
--bindings, -b        Specifies the bindings to generate (.js + .d.ts).

                        esm  JavaScript bindings & typings for ESM integration.
                        raw  Like esm, but exports just the instantiate function.
                             Useful where modules are meant to be instantiated
                             multiple times or non-ESM imports must be provided.

# 调试

¥Debugging

为了在开发过程中更轻松地进行调试,可以与 WebAssembly 二进制文件一起触发 源源映射图,并且可以嵌入调试符号:

¥For easier debugging during development, a source map can be emitted alongside the WebAssembly binary, and debug symbols can be embedded:

--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.

# 特性

¥Features

有几个标志可以启用或禁用特定的 WebAssembly 或编译器功能。默认情况下,仅公开最低限度的功能,并且将使用完全标准化的 WebAssembly 功能。

¥There are several flags that enable or disable specific WebAssembly or compiler features. By default, only the bare minimum is exposed, and fully standardized WebAssembly features will be used.

--importMemory        Imports the memory from 'env.memory'.
--noExportMemory      Does not export the memory as 'memory'.
--initialMemory       Sets the initial memory size in pages.
--maximumMemory       Sets the maximum memory size in pages.
--sharedMemory        Declare memory as shared. Requires maximumMemory.
--zeroFilledMemory    Assume imported memory is zeroed. Requires importMemory.
--importTable         Imports the function table from 'env.table'.
--exportTable         Exports the function table as 'table'.
--exportStart         Exports the start function using the specified name instead
                      of calling it implicitly. Useful for WASI or to obtain the
                      exported memory before executing any code accessing it.
--runtime             Specifies the runtime variant to include in the program.

                        incremental  TLSF + incremental GC (default)
                        minimal      TLSF + lightweight GC invoked externally
                        stub         Minimal runtime stub (never frees)
                        ...          Path to a custom runtime implementation

--exportRuntime       Always exports the runtime helpers (__new, __collect, __pin etc.).
                      Automatically determined when generation of --bindings is enabled.
--stackSize           Overrides the stack size. Only relevant for incremental GC
                      or when using a custom runtime that requires stack space.
                      Defaults to 0 without and to 16384 with incremental GC.
--enable              Enables WebAssembly features being disabled by default.

                        threads             Threading and atomic operations.
                        simd                SIMD types and operations.
                        reference-types     Reference types and operations.
                        gc                  Garbage collection (WIP).
                        stringref           String reference types.
                        relaxed-simd        Relaxed SIMD operations.

--disable             Disables WebAssembly features being enabled by default.

                        mutable-globals     Mutable global imports and exports.
                        sign-extension      Sign-extension operations
                        nontrapping-f2i     Non-trapping float to integer ops.
                        bulk-memory         Bulk memory operations.

--use, -u             Aliases a global object under another name, e.g., to switch
                      the default 'Math' implementation used: --use Math=JSMath
                      Can also be used to introduce an integer constant.
--lowMemoryLimit      Enforces very low (<64k) memory constraints.

# 链接

¥Linking

分别指定编译器生成的内存的基本偏移量,表为前面的其他数据留出一些空间。在当前形式中,这对于在编译后将附加数据链接到 AssemblyScript 二进制文件非常有用,无论是通过填充二进制文件本身还是在初始化时从外部初始化数据。一个很好的例子是为帧缓冲区留一些暂存空间。

¥Specifying the base offsets of compiler-generated memory respectively the table leaves some space for other data in front. In its current form this is mostly useful to link additional data into an AssemblyScript binary after compilation, be it by populating the binary itself or initializing the data externally upon initialization. One good example is leaving some scratch space for a frame buffer.

--memoryBase          Sets the start offset of emitted memory segments.
--tableBase           Sets the start offset of emitted table elements.

# API

为了与编译器集成,例如对 AST 进行后处理,可以指定一个或多个自定义 transforms

¥To integrate with the compiler, for example to post-process the AST, one or multiple custom transforms can be specified.

--transform           Specifies the path to a custom transform to load.

# 其他

¥Other

其他选项包括转发到 Binaryen 的选项以及在某些情况下有用的各种标志。

¥Other options include those forwarded to Binaryen and various flags useful in certain situations.

# 二进制

¥Binaryen

--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
--noValidate          Skips validating the module using Binaryen.

# 还有厨房的水槽

¥And the kitchen sink

--baseDir             Specifies the base directory of input and output files.
--noColors            Disables terminal colors.
--noUnsafe            Disallows the use of unsafe features in user code.
                      Does not affect library files and external modules.
--disableWarning      Disables warnings matching the given diagnostic code.
                      If no diagnostic code is given, all warnings are disabled.
--noEmit              Performs compilation as usual but does not emit code.
--stats               Prints statistics on I/O and compile times.
--pedantic            Make yourself sad for no good reason.
--lib                 Adds one or multiple paths to custom library components and
                      uses exports of all top-level files at this path as globals.
--path                Adds one or multiple paths to package resolution, similar
                      to node_modules. Prefers an 'ascMain' entry in a package's
                      package.json and falls back to an inner 'assembly/' folder.
--wasm                Uses the specified Wasm binary of the compiler.
-- ...                Specifies node.js options (CLI only). See: node --help

# 配置文件

¥Configuration file

可以使用通常名为 asconfig.json 的配置文件,而不是在命令行上提供上面概述的选项。它可能类似于以下示例(不包括注释):

¥Instead of providing the options outlined above on the command line, a configuration file typically named asconfig.json can be used. It may look like in the following example, excluding comments:

{
  "extends": "./path/to/other/asconfig.json", // (optional) base config
  "entries": [
    // (optional) entry files, e.g.
    "./assembly/index.ts"
  ],
  "options": {
    // common options, e.g.
    "importTable": true
  },
  "targets": {
    // (optional) targets
    "release": {
      // additional options for the "release" target, e.g.
      "optimize": true,
      "outFile": "myModule.release.wasm"
    },
    "debug": {
      // additional options for the "debug" target, e.g.
      "debug": true,
      "outFile": "myModule.debug.wasm"
    }
  }
}

每个目标选项,例如 targets.release,添加并覆盖顶层 options。命令行上提供的选项会覆盖配置文件中的任何选项。用法例如:

¥Per-target options, e.g. targets.release, add to and override top-level options. Options provided on the command line override any options in the configuration file. Usage is, for example:

asc --config asconfig.json --target release

# 程序化用法

¥Programmatic usage

编译器 API 也可以通过编程方式使用:

¥The compiler API can also be used programmatically:

import asc from "assemblyscript/asc";

const { error, stdout, stderr, stats } = await asc.main([
  // Command line options
  "myModule.ts",
  "--outFile", "myModule.wasm",
  "--optimize",
  "--sourceMap",
  "--stats"
], {
  // Additional API options
  stdout?: ...,
  stderr?: ...,
  readFile?: ...,
  writeFile?: ...,
  listFiles?: ...,
  reportDiagnostic?: ...,
  transforms?: ...
});
if (error) {
  console.log("Compilation failed: " + error.message);
  console.log(stderr.toString());
} else {
  console.log(stdout.toString());
}

编译器也可以在浏览器中运行。最简单的设置方法是包含生成的 web.js (opens new window),以便编译器可以与 Web 上的 import 一起使用:

¥The compiler runs in browsers as well. The simplest way to set it up is to include the generated web.js (opens new window) so the compiler can be used with an import on the Web:

<script src="https://cdn.jsdelivr.net/npm/assemblyscript@x.x.x/dist/web.js"></script>
<script type="module">
import asc from "assemblyscript/asc";
...
</script>

这里,x.x.x 必须替换为 各自使用的版本 (opens new window)latest 以始终使用最新版本(不建议在生产中使用)。默认情况下,该脚本安装 必要的导入映射 (opens new window),对于尚不支持导入映射的浏览器,安装 导入映射垫片 (opens new window)。如果只需要执行部分设置,它还接受以下选项:

¥Here, x.x.x must be replaced with the respective version to use (opens new window), or latest to always use the latest version (not recommended in production). By default, the script installs the necessary import map (opens new window) and, for browsers that do not yet support import maps, an import map shim (opens new window). It also accepts the following options in case there is a need to only perform part of the setup:

脚本网址 影响
web.js?noinstall 不安装导入映射。
web.js?noshim 不安装导入映射垫片。
web.js?noinstall,noshim 不安装导入映射或垫片。

无论使用什么选项,脚本始终声明以下全局变量:

¥Regardless of the options used, the script always declares the following global variables:

多变的 描述
ASSEMBLYSCRIPT_VERSION 使用的编译器版本的版本字符串
ASSEMBLYSCRIPT_IMPORTMAP JSON 格式的发行版各自的导入映射

# 主机绑定

¥Host bindings

仅 WebAssembly 还无法跨模块边界传输更高级别的数据类型,例如字符串、数组和对象,因此目前需要一些粘合代码来与主机/JavaScript 交换这些数据结构。

¥WebAssembly alone cannot yet transfer higher level data types like strings, arrays and objects over module boundaries, so for now some amount of glue code is required to exchange these data structures with the host / JavaScript.

编译器可以使用 --bindings 命令行选项(作为 ES 模块或原始实例化函数)生成必要的绑定,从而实现以下交换:

¥The compiler can generate the necessary bindings using the --bindings command line option (either as an ES module or a raw instantiate function), enabling exchange of:

类型 策略 描述
数字 按值 除 64 位整数外的基本数字类型。
大整型 按值 通过 js-bigint-integration 的 64 位整数。
布尔值 按值 强制为 truefalse
外部引用 按引用 使用引用类型。
字符串 复制
ArrayBuffer 复制
TypedArray 复制 任意 Int8ArrayFloat64Array 等。
数组 复制 任何 Array<T>
StaticArray 复制 任何 StaticArray<T>
对象 复制 如果是普通的对象。那是:没有构造函数或非公共字段。
对象 按引用 如果不是一个普通的对象。作为不透明的引用计数指针传递。

请注意用于对象的两种不同策略:在某些情况下,例如在调用 Web API 时,最好逐个字段复制整个对象,这是为没有构造函数或非公共字段的普通对象选择的策略:

¥Note the two different strategies used for Object: In some situations, say when calling a Web API, it may be preferable to copy the object as a whole, field by field, which is the strategy chosen for plain objects with no constructor or non-public fields:

// Copied to a JS object
class PlainObject {
  field: string;
}

export function getObject(): PlainObject {
  return {
    field: "hello world"
  };
}

然而,复制可能并不适合所有情况,例如当要在外部修改单个对象属性时,将对象作为一个整体进行序列化/反序列化会导致不必要的开销。为了支持此用例,编译器可以仅传递对对象的不透明引用,这可以通过提供空 constructor (不再是普通对象)来强制执行:

¥However, copying may not be desirable in every situation, say when individual object properties are meant to be modified externally where serializing/deserializing the object as a whole would result in unnecessary overhead. To support this use case, the compiler can pass just an opaque reference to the object, which can be enforced by providing an empty constructor (not a plain object anymore):

// Not copied to a JS object
class ComplexObject {
  constructor() {} // !
  field: string | null;
}

export function newObject(): ComplexObject {
  return new ComplexObject();
}

export function setObjectField(target: ComplexObject, field: string | null): void {
  target.field = field;
}

export function getObjectField(target: ComplexObject): string | null {
  return target.field;
}

另请注意,导出整个 class 对模块边界没有影响(目前),建议仅公开所需的功能,如上例所示。边界支持的元素是全局元素、函数和枚举。

¥Also note that exporting an entire class has no effect at the module boundary (yet), and it is instead recommended to expose only the needed functionality as shown in the example above. Supported elements at the boundary are globals, functions and enums.

# 使用 ESM 绑定

¥Using ESM bindings

使用 --bindings esm 生成的绑定执行从编译到实例化到导出最终接口的所有步骤。为此,必须做出一些假设:

¥Bindings generated with --bindings esm perform all the steps from compilation over instantiation to exporting the final interface. To do so, a few assumptions had to be made:

  • WebAssembly 二进制文件位于 JavaScript 绑定文件旁边,使用相同的名称但具有 .wasm 扩展名。

    ¥The WebAssembly binary is located next to the JavaScript bindings file using the same name but with a .wasm extension.

    build/mymodule.js
    build/mymodule.wasm
    
    import * as myModule from "./build/mymodule.js"
    
  • globalThis 中的 JavaScript 全局变量可以通过 env 模块命名空间直接访问。例如,console.log 可以通过以下方式手动导入:

    ¥JavaScript globals in globalThis can be accessed directly via the env module namespace. For example, console.log can be manually imported through:

    @external("env", "console.log")
    declare function consoleLog(s: string): void
    

    请注意,这只是一个示例,当从 AssemblyScript 文件调用时,标准库已经提供了 console.log。不过,标准库尚未提供的其他全局函数可能需要从本示例开始导入。

    ¥Note that this is just an example and console.log is already provided by the standard library when called from an AssemblyScript file. Other global functions not already provided by the standard library may require an import as of this example, though.

  • env 以外的其他名称空间(即 (import "module" "name"))导入的内容将成为绑定内的 import { name } from "module"。从绑定文件旁边的 JavaScript 文件导入自定义函数可以通过以下方式实现:

    ¥Imports from other namespaces than env, i.e. (import "module" "name"), become an import { name } from "module" within the binding. Importing a custom function from a JavaScript file next to the bindings file can be achieved through:

    @external("./otherfile.js", "myFunction")
    declare function myFunction(...): ...
    

    同样,从 Node.js 依赖导入自定义函数可以通过以下方式实现:

    ¥Similarly, importing a custom function from, say, a Node.js dependency can be achieved through:

    @external("othermodule", "myFunction")
    declare function myFunction(...): ...
    

这些假设无法被拦截或自定义,因为要直接从绑定文件提供静态 ESM 导出,实例化必须在导入绑定文件时立即启动。如果需要定制,可以使用 --bindings raw 代替。

¥These assumptions cannot be intercepted or customized since, to provide static ESM exports from the bindings file directly, instantiation must start immediately when the bindings file is imported. If customization is required, --bindings raw can be used instead.

# 使用原始绑定

¥Using raw bindings

--bindings raw 导出的单个 instantiate 函数的签名为:

¥The signature of the single instantiate function exported by --bindings raw is:

export async function instantiate(module: WebAssembly.Module, imports?: WebAssembly.Imports): AdaptedExports

请注意,该函数不会对模块的编译方式做出任何假设,而是期望一个易于编译的 WebAssembly.Module,如下例所示:

¥Note that the function does not make any assumptions on how the module is to be compiled, but instead expects a readily compiled WebAssembly.Module as in this example:

import { instantiate } from "./module.js"
const exports = await instantiate(await WebAssembly.compileStreaming(fetch("./module.wasm")), { /* imports */ })

--bindings esm 不同,原始绑定也不对如何解析导入做出任何假设,因此必须将这些作为导入对象的一部分手动提供。例如,要实现与 ESM 绑定类似的结果,但现在可自定义:

¥Unlike --bindings esm, raw bindings also do not make any assumptions on how imports are resolved, so these must be provided manually as part of the imports object. For example, to achieve a similar result as with ESM bindings, but now customizable:

import { instantiate } from "./module.js"
import * as other from "./otherfile.js"
export const {
  export1,
  export2,
  ...
} = await instantiate(await WebAssembly.compileStreaming(fetch("./module.wasm")), {
  "./otherfile.js": other
})

# 调试

¥Debugging

调试工作流程与调试 JavaScript 类似,因为 Wasm 和 JS 在同一引擎中执行,并且编译器提供了各种选项来设置其他特定于 WebAssembly 的调试信息。请注意,在调试版本中应禁用任何类型的优化。

¥The debugging workflow is similar to debugging JavaScript since both Wasm and JS execute in the same engine, and the compiler provides various options to set up additional WebAssembly-specific debug information. Note that any sort of optimization should be disabled in debug builds.

# 调试符号

¥Debug symbols

当使用 --debug 选项进行编译时,编译器会在二进制文件中附加一个名称部分,其中包含函数名称、全局变量、局部变量等。这些名称将显示在堆栈跟踪中。

¥When compiling with the --debug option, the compiler appends a name section to the binary, containing names of functions, globals, locals and so on. These names will show up in stack traces.

# 源映射

¥Source maps

编译器可以使用 --sourceMap 选项生成源映射和二进制文件。默认情况下,相对源映射路径将嵌入到二进制文件中,浏览器可以在从 fetch 响应实例化模块时拾取该路径。在不提供 fetch 或等效机制的环境中(例如 Node.js),也可以通过 --sourceMap path/to/source/map 嵌入绝对源映射路径。

¥The compiler can generate a source map alongside a binary using the --sourceMap option. By default, a relative source map path will be embedded in the binary which browsers can pick up when instantiating a module from a fetch response. In environments that do not provide fetch or an equivalent mechanism, like in Node.js, it is alternatively possible to embed an absolute source map path through --sourceMap path/to/source/map.

# 断点

¥Breakpoints

一些 JavaScript 引擎还支持直接在 WebAssembly 代码中添加断点。请查阅你的引擎的文档:Chrome (opens new window)火狐浏览器 (opens new window)Node.js (opens new window)苹果浏览器 (opens new window)

¥Some JavaScript engines also support adding break points directly in WebAssembly code. Please consult your engine's documentation: Chrome (opens new window), Firefox (opens new window), Node.js (opens new window), Safari (opens new window).

# 转换

¥Transforms

AssemblyScript 是静态编译的,因此代码转换不能在运行时完成,而必须在编译时执行。为了实现这一点,编译器前端 (asc) 提供了一种机制,可以在模块编译之前、期间和之后钩子编译过程。

¥AssemblyScript is compiled statically, so code transformation cannot be done at runtime but must instead be performed at compile-time. To enable this, the compiler frontend (asc) provides a mechanism to hook into the compilation process before, while and after the module is being compiled.

在命令行中指定 --transform ./myTransform.js 将加载 ./myTransform.js 指向的节点模块。

¥Specifying --transform ./myTransform.js on the command line will load the node module pointed to by ./myTransform.js.

import * as assemblyscript from "assemblyscript"
import { Transform } from "assemblyscript/transform"
class MyTransform extends Transform {
  ...
}
export default MyTransform

# 属性

¥Properties

转换是一个 ES6 类/节点模块,具有以下继承属性:

¥A transform is an ES6 class/node module with the following inherited properties:

  • readonly program: Program
    

    参考 Program 实例。

    ¥Reference to the Program instance.

  • readonly baseDir: string
    

    编译器使用的基目录。

    ¥Base directory used by the compiler.

  • readonly stdout: OutputStream
    

    编译器使用的输出流。

    ¥Output stream used by the compiler.

  • readonly stderr: OutputStream
    

    编译器使用的错误流。

    ¥Error stream uses by the compiler.

  • readonly log: typeof console.log
    

    将消息记录到控制台。

    ¥Logs a message to console.

  • function writeFile(filename: string, contents: string | Uint8Array, baseDir: string): boolean
    

    将文件写入磁盘。

    ¥Writes a file to disk.

  • function readFile(filename: string, baseDir: string): string | null
    

    从磁盘读取文件。

    ¥Reads a file from disk.

  • function listFiles(dirname: string, baseDir: string): string[] | null
    

    列出目录中的所有文件。

    ¥Lists all files in a directory.

# 钩子

¥Hooks

在编译过程中,前端将调用多个钩子(如果存在于转换中):

¥The frontend will call several hooks, if present on the transform, during the compilation process:

  • function afterParse(parser: Parser): void
    

    解析完成时、程序从 AST 初始化之前调用。请注意,现阶段尚不知道类型,并且没有简单的方法来获取它们。

    ¥Called when parsing is complete, before a program is initialized from the AST. Note that types are not yet known at this stage and there is no easy way to obtain them.

  • function afterInitialize(program: Program): void
    

    程序初始化后、编译之前调用。类型在这个阶段是已知的,可以在必要时分别进行解析。

    ¥Called once the program is initialized, before it is being compiled. Types are known at this stage, respectively can be resolved where necessary.

  • function afterCompile(module: Module): void
    

    在生成的模块被触发之前调用它。在写入任何输出之前修改 IR 很有用,例如用实际功能替换导入或添加自定义部分。

    ¥Called with the resulting module before it is being emitted. Useful to modify the IR before writing any output, for example to replace imports with actual functionality or to add custom sections.

转换是一个非常强大的功能,但可能需要对编译器有深入的了解才能充分利用它们,因此阅读编译器源代码是一个优势。

¥Transforms are a very powerful feature, but may require profound knowledge of the compiler to utilize them to their full extent, so reading through the compiler sources is a plus.

# 可移植性

¥Portability

由于 AssemblyScript 与 TypeScript 非常相似,因此有机会使用 tsc 将相同的代码编译为 JavaScript,使用 asc 将相同的代码编译为 WebAssembly。AssemblyScript 编译器本身是可移植代码。编写可移植代码很大程度上需要仔细检查意图,确保其意图在严格类型的 AssemblyScript 和类型剥离的 TypeScript 世界中转换为相同的结果。

¥With AssemblyScript being very similar to TypeScript, there comes the opportunity to compile the same code to JavaScript with tsc and WebAssembly with asc. The AssemblyScript compiler itself is portable code. Writing portable code is largely a matter of double-checking that the intent translates to the same outcome in both the strictly typed AssemblyScript and the types-stripped-away TypeScript worlds.

# 可移植标准库

¥Portable standard library

除了完整的标准库之外,AssemblyScript 还提供 JavaScript 和 WebAssembly 中存在的功能的可移植变体。除此之外,可移植库还将一些仅适用于 asc 的功能提升为 JavaScript,例如下面提到的可移植转换。

¥Besides the full standard library, AssemblyScript provides a portable variant of the functionality that is present in both JavaScript and WebAssembly. In addition to that, the portable library lifts some of the functionality that is only available with asc to JavaScript, like the portable conversions mentioned below.

另请注意,JavaScript 标准库的某些部分的功能比编译为 WebAssembly 时的功能要宽松一些。虽然可移植定义试图解决这个问题,但可能发生这种情况的一个例子是,当 JavaScript 中找不到密钥时,Map#get 返回 undefined,同时导致 WebAssembly 中止,需要首先使用以下命令检查密钥是否存在 Map#has

¥Also note that some parts of JavaScript's standard library function a little more loosely than how they would when compiling to WebAssembly. While the portable definitions try to take care of this, one example where this can happen is Map#get returning undefined when a key cannot be found in JavaScript, while resulting in an abort in WebAssembly, where it is necessary to first check that the key exists using Map#has.

要使用可移植库,请在 tsconfig.json 中扩展 assemblyscript/std/portable.json 而不是 assemblyscript/std/assembly.json,并在构建步骤中的某个位置添加以下内容,以便环境中存在可移植功能:

¥To use the portable library, extend assemblyscript/std/portable.json instead of assemblyscript/std/assembly.json within tsconfig.json and add the following somewhere along your build step so the portable features are present in the environment:

import "assemblyscript/std/portable.js"

请注意,可移植标准库仍在进行中,到目前为止,重点关注使编译器本身可移植的有用功能,因此,如果你需要特定的东西,请随时改进 它的定义和功能集 (opens new window)

¥Note that the portable standard library is still a work in progress and so far focuses on functionality useful to make the compiler itself portable, so if you need something specific, feel free to improve its definitions and feature set (opens new window).

# 可移植转换

¥Portable conversions

虽然 asc 理解的意思

¥While asc understands the meaning of

// non-portable
let someFloat: f32 = 1.5
let someInt: i32 = <i32>someFloat

然后插入正确的转换步骤,tsc 不会,因为所有数字类型都只是 number 的别名。因此,当使用 tsc 来定位 JavaScript 时,上述结果将导致

¥and then inserts the correct conversion steps, tsc does not because all numeric types are just aliases of number. Hence, when targeting JavaScript with tsc, the above will result in

var someFloat = 1.5
var someInt = someFloat

这显然是错误的。为了解决这个问题,可以使用可移植的转换,从而产生实际上可移植的代码。例如

¥which is obviously wrong. To account for this, portable conversions can be used, resulting in actually portable code. For example

// portable
let someFloat: f32 = 1.5
let someInt: i32 = i32(someFloat)

本质上会导致

¥will essentially result in

var someFloat = 1.5
var someInt = someFloat | 0

哪个是对的。处理这个问题的最好方法是问自己这个问题:这段代码编译成 JavaScript 后会做什么?

¥which is correct. The best way of dealing with this is asking yourself the question: What would this code do when compiled to JavaScript?

# 可移植溢流器

¥Portable overflows

同样,由于 asc 知道含义但 tsc 不知道,因此必须显式处理溢出:

¥Likewise, again because asc knows the meaning but tsc does not, overflows must be handled explicitly:

// non-portable
let someU8: u8 = 255
let someOtherU8: u8 = someU8 + 1
// portable
let someU8: u8 = 255
let someOtherU8: u8 = u8(someU8 + 1)

本质上导致

¥essentially resulting in

let someU8 = 255
let someOtherU8 = (someU8 + 1) & 0xff

# 不可移植代码

¥Non-portable code

在 JavaScript 中,所有数值均为 IEEE754 双精度数,无法表示适合 64 位整数的完整值范围(最大限度。安全整数 (opens new window)2^53 - 1)。因此 i64u64 不可移植并且不存在于 std/portable 中。有几种方法可以解决这个问题。一种是使用像 在这个例子中 (opens new window) 这样的 i64 polyfill。

¥In JavaScript, all numeric values are IEEE754 doubles that cannot represent the full range of values fitting in a 64-bit integer (max. safe integer (opens new window) is 2^53 - 1). Hence i64 and u64 are not portable and not present in std/portable. There are several ways to deal with this. One is to use an i64 polyfill like in this example (opens new window).

除此之外,可移植代码(JavaScript)没有内存的概念,因此可移植标准库中没有 loadstore 实现。从技术上讲,这可以通过多种方式进行填充,但没有提供默认值,因为实际的实现预计是相对具体的(例如:可移植编译器访问 Binaryen 的内存)。

¥Other than that, portable code (JavaScript) does not have a concept of memory, so there are no load and store implementations in the portable standard library. Technically this can be polyfilled in various ways, but no default is provided since actual implementations are expected to be relatively specific (for instance: the portable compiler accesses Binaryen's memory).