# 运行


AssemblyScript 运行时实现内存管理和垃圾收集所需的位。它对于开发者来说基本上是不可见的,但在高级用例中可能会变得相关,例如在新环境中集成 AssemblyScript 时。

¥The AssemblyScript runtime implements the necessary bits for memory management and garbage collection. It is largely invisible to a developer but can become relevant in advanced use cases, for example when integrating AssemblyScript in new environments.



¥If not applicable to your use case, i.e. it is not necessary to interact with the runtime directly, you can safely skip this section.

# 变体


运行时有不同的风格,每种风格在不同的用例中都有用。可以使用 --runtime 编译器选项指定所需的运行时:

¥The runtime comes in different flavors, each useful in different use cases. The desired runtime can be specified with the --runtime compiler option:

  --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       Exports the runtime helpers (__new, __collect etc.).

默认的 incremental 运行时提供了大多数用例中推荐的完整包。minimal 运行时是一个精简变体(没有影子堆栈,没有启发式,算法更简单,更小,更高效),它不是自动化的,需要在适当的时候从外部调用 __collect(当 WebAssembly 堆栈上没有更多值时,这将导致 当 WebAssembly 直接或间接调用主机时就是这种情况),而 stub 运行时根本不提供垃圾收集器并且从不释放(简单的冲突分配,非常小)。

¥The default incremental runtime provides the full package recommended in most use cases. The minimal runtime is a stripped down variant (no shadow stack, no heuristic, simpler algorithm, smaller and more efficient) that is not automated and requires calling __collect externally at appropriate times (when there are no more values on the WebAssembly stack, which would be the case when WebAssembly calls out to the host, directly or indirectly), whereas the stub runtime does not provide a garbage collector at all and never frees (simple bump allocation, extremely small).

例如,stub 运行时在模块生命周期较短并作为一个整体进行收集的情况下很有用,而 minimal 运行时则为手动收集垃圾就足够的用例(偶尔,例如模块执行固定操作的情况)提供了折衷方案。 再次调用之前的工作量。

¥For example, the stub runtime can be useful where modules are short-lived and collected as a whole anyhow, while the minimal runtime provides a compromise for use cases where it is sufficient to collect garbage manually, occasionally, say where a module performs a fixed amount of work before being invoked again.

如有疑问,请使用 incremental,但在用例允许的情况下,请随意尝试其他可能更有效的变体。

¥In case of doubt, use incremental, but feel free to experiment with the other potentially more efficient variants where a use case permits.

# 接口


使用 --exportRuntime 编译器选项,可以将运行时接口从模块导出到主机,因此可以分配新的托管对象并在外部调用垃圾收集器。

¥Using the --exportRuntime compiler option, the runtime interface can be exported from the module to the host, so it becomes possible to allocate new managed objects and invoke the garbage collector externally.


¥It is typically not necessary to invoke the runtime interface manually since generated bindings take care of all the internals. In environments where bindings are not yet available, however, the runtime interface can be utilized to provide the necessary integration.

  • function __new(size: usize, id: u32): usize

    分配由指定类 ID 表示的对象的新垃圾收集实例,其大小至少为指定大小。返回指向对象的指针(指向其数据,而不是其内部标头)。

    ¥Allocates a new garbage collected instance of the object represented by the specified class id, of at least the specified size. Returns the pointer to the object (pointing at its data, not its internal header).

  • function __pin(ptr: usize): usize

    ptr 指向的对象固定在外部,以便它和它引用的任何对象不会被垃圾收集。请注意,同一对象不能被固定多次。

    ¥Pins the object pointed to by ptr externally so it and any object it references do not become garbage collected. Note that the same object cannot be pinned more than once.

    每当在分配它和将其传递给 WebAssembly 之间可能发生分配时,必须固定未从 WebAssembly 内部引用的外部对象。如果未固定,分配可能会触发垃圾收集器进行步进,这将过早收集对象并导致未定义的行为。

    ¥An external object that is not referenced from within WebAssembly must be pinned whenever an allocation might happen in between allocating it and passing it to WebAssembly. If not pinned, the allocation may trigger the garbage collector to step, which would prematurely collect the object and lead to undefined behavior.

  • function __unpin(ptr: usize): void

    从外部取消固定 ptr 指向的对象,以便它可以被垃圾回收。请注意,相应的对象之前必须已被固定,取消固定操作才能成功。

    ¥Unpins the object pointed to by ptr externally so it can become garbage collected. Note that the respective object must have been pinned before for the unpin operation to succeed.

  • function __collect(): void


    ¥Performs a full garbage collection.

  • const __rtti_base: usize

    指向线性内存中运行时类型信息的指针。RTTI 包含有关二进制文件中使用的各种类的信息、将类 ID 映射到对象类型、它们的键和值布局以及基类。其布局在 shared/typeinfo (opens new window) 中有详细描述。使用 RTTI,可以实现 instanceof,或者区分字符串、数组等。

    ¥Pointer to runtime type information in linear memory. RTTI contains information about the various classes utilized in a binary, mapping class ids to object kinds, their key and value layout, and base classes. Its layout is described in detail in shared/typeinfo (opens new window). Using RTTI, it becomes possible to implement instanceof for example, or to tell strings, arrays etc. apart.

# 内存布局

¥Memory layout

总的来说,AssemblyScript 按如下方式划分线性内存:

¥Overall, AssemblyScript partitions linear memory as follows:

地区 起始偏移 结束偏移 描述
静态数据 0 __data_end 静态字符串、数组等的内容
托管堆栈 __data_end __heap_base 仅当使用增量运行时间时才存在。
__heap_base memory.size() << 16 剩余空间用于动态分配。可以成长。

# 标题布局

¥Header layout

AssemblyScript 中的任何类型的托管对象都利用托管标头供运行时进行操作:

¥Any kind of managed object in AssemblyScript utilizes a managed header for the runtime to operate on:

名称 偏移 类型 描述
mmInfo -20 乌斯泽 内存管理器信息
gcInfo -16 乌斯泽 垃圾收集器信息
gcInfo2 -12 乌斯泽 垃圾收集器信息
rtId -8 u32 具体类的唯一 ID
rtSize -4 u32 标头后面的数据大小
0 有效负载从这里开始

对对象的引用始终指向有效负载的开头,标头在前面 20 个字节处开始。空引用只是值 0。在外部使用 AssemblyScript 模块时,了解内存布局有助于获取对象的类 ID 或大小等。调用 __new 会自动在前面添加一个托管标头,并向 GC 注册对象,使用为 rtId 提供的类 ID 和为 rtSize 提供的大小。

¥References to an object always point at the start of the payload, with the header beginning 20 bytes before. Null references are just the value 0. When working with an AssemblyScript module externally, knowing the memory layout can be helpful to for example obtain an object's class id or size. Invoking __new automatically prepends a managed header and registers the object with the GC, using the provided class id for rtId and the provided size for rtSize.

# 类布局

¥Class layout

类字段的布局与 C 结构类似,按顺序且不打包。每个字段都与其类型的原生对齐方式对齐,可能会在其间留下填充。如果一个类具有 @unmanaged 装饰器,那么它实际上仅描述了一个内存区域,就好像它是一个不使用托管标头、不被垃圾回收并且可以与 heap.free 一起使用的结构一样。具有托管标头的托管类无法手动释放,并且托管类和非托管类不能混合(例如,相互扩展)。

¥Class fields are laid out similarly to C structs, sequentially and without packing. Each field is aligned to its type's native alignment, potentially leaving padding in between. If a class has the @unmanaged decorator, it effectively only describes a region of memory as if it were a struct that does not utilize a managed header, is not garbage collected, and can be used with heap.free. Managed classes with a managed header cannot be manually freed, and managed and unmanaged classes cannot be mixed (e.g. extend from each other).


¥Standard library data types use the following layouts:

对象 Object 是所有托管类的基类,其类 ID 为 0
ArrayBuffer 缓冲区始终使用类 ID 1,其无类型数据作为有效负载。
字符串 字符串始终使用类 id 2 及其 16 位字符代码(UTF-16 代码单元,允许像 JS 这样的隔离代理)作为有效负载。例如,如果 rtSize8,则字符串的 .length4
TypedArray 类型化数组是由 buffer(对所查看的 ArrayBuffer 的引用)、dataStart(指向 buffer 的起始指针)和 byteLength 字段(按此顺序)组成的对象。相应的 id 是按顺序选取的,而不是预先确定的。
数组<T> 普通数组使用与类型化数组相同的布局,最后有一个额外的可变 length 字段。
静态数组<T> 静态数组由于不可调整大小而不需要间接寻址,并且它们的数据位于有效负载中,根据 T 对齐。可以被认为是一个类型化的缓冲区。
函数 函数是由表 index 和词法 env(当前始终是 0)组成的对象。
Map/Set/... 其他集合使用更复杂的布局。请参阅各自的来源。

还可以通过实现 __visit 接口(迭代此类对象中包含的所有引用)来构建与 GC 集成的自定义数据类型。请参阅预先存在的数据类型的来源以获取示例。

¥It is also possible to build custom data types that integrate with the GC by implementing the __visit interface (that iterates all references contained in an object of this kind). Please refer to the sources of preexisting data types for examples.

# 调用约定

¥Calling convention

AssemblyScript 的调用约定相对简单,因为它不会向函数或其他幕后魔法添加额外的参数。请注意,生成的绑定会自动处理调用约定,但其他环境可能希望专门遵守它。

¥AssemblyScript's calling convention is relatively straight forward, as it does not add additional parameters to functions or other behind-the-scenes magic. Note that generated bindings take care of the calling convention automatically, but other environments may want to adhere to it specifically.

# 基本值

¥Basic values


¥Exported functions wrap basic numeric return values to their respective value ranges. Basic numeric values passed to exported functions are wrapped to their respective value ranges on demand.

# 管理值

¥Managed values

任何类型的对象都作为指针传递到内存中,并且主机应该执行在线性内存和主机系统之间交换值所需的步骤。例如,当字符串从 WebAssembly 传递到主机时(反之亦然),传递的不是其数据,而是指向线性内存中字符串数据的指针。为了读取数据,可以评估有效负载之前的托管标头以获得字符串的字节长度。请注意,主机绑定 针对常见数据类型自动执行此过程。

¥Any kind of object is passed as a pointer into memory, and the host is expected to perform the steps necessary to exchange the value between linear memory and the host system. For example, when a string is passed from WebAssembly to the host or vice-versa, it is not its data that is passed but a pointer to the string's data in linear memory. In order to read the data, the managed header before the payload can be evaluated to obtain the string's byte length. Note that host bindings automate this process for common data types.

# 可选参数

¥Optional arguments

当导出函数允许省略一个或多个参数时,在调用导出之前必须通过调用 exports.__setArgumentsLength(numArgs) 告知模块重要参数的数量,以便它可以填充默认值。省略的参数不会被求值,即可以传递零。不调用助手会导致未定义的行为。如果函数具有固定数量的参数,则无需调用助手。如果模块仅导出具有固定数量参数的函数,则辅助程序可能不存在。

¥When an exported function allows one or multiple arguments to be omitted, the module must be informed of the number of significant arguments by calling exports.__setArgumentsLength(numArgs) before calling the export, so it can fill in default values. Omitted arguments are not evaluated, i.e. zeroes can be passed. Not calling the helper leads to undefined behavior. If a function has a fixed number of arguments, it is not necessary to call the helper. The helper may not be present if a module only exports functions with a fixed number of arguments.