# Environment
A WebAssembly environment is more limited than a usual browser environment, but AssemblyScript tries to fill the gaps by reimplementing commonly known functionality, besides providing direct access to WebAssembly instructions through built-ins.
# Standard library
AssemblyScript comes with its own standard library very much resembling what developers became used to when writing JavaScript, with a few specialized classes, like StaticArray
, that exist in AssemblyScript only to tackle very specific problems.
Additional rather low-level WebAssembly functionality that the standard library makes extensive use of is described below.
# Static type checks
By making use of the following special type checks, especially in generic contexts, untaken branches can be eliminated statically, leading to concrete WebAssembly functions that handle one type specificially.
function isInteger<T>(value?: T): bool
Tests if the specified type or expression is of an integer type and not a reference. Compiles to a constant.
function isFloat<T>(value?: T): bool
Tests if the specified type or expression is of a float type. Compiles to a constant.
function isSigned<T>(value?: T): bool
Tests if the specified type or expression can represent negative numbers. Compiles to a constant.
function isReference<T>(value?: T): bool
Tests if the specified type or expression is of a reference type. Compiles to a constant.
function isString<T>(value?: T): bool
Tests if the specified type or expression can be used as a string. Compiles to a constant.
function isArray<T>(value?: T): bool
Tests if the specified type or expression can be used as an array. Compiles to a constant.
function isFunction<T>(value?: T): bool
Tests if the specified type or expression is of a function type. Compiles to a constant.
function isNullable<T>(value?: T): bool
Tests if the specified type or expression is of a nullable reference type. Compiles to a constant.
function isDefined(expression: auto): bool
Tests if the specified expression resolves to a defined element. Compiles to a constant.
function isConstant(expression: auto): bool
Tests if the specified expression evaluates to a constant value. Compiles to a constant.
function isManaged<T>(expression: auto): bool
Tests if the specified type or expression is of a managed type. Compiles to a constant. Usually only relevant when implementing custom collection-like classes.
# Example
function add<T>(a: T, b: T): T {
return a + b // addition if numeric, string concatenation if a string
}
function add<T>(a: T, b: T): T {
if (isString<T>()) { // eliminated if T is not a string
return parseInt(a) + parseInt(b)
} else { // eliminated if T is a string
return a + b
}
}
TIP
If you are not going to use low-level WebAssembly in the foreseeable future, feel free to come back to the following paragraphs at a later time and continue at the next pageright away.
# Sizes and alignments
function sizeof<T>(): usize
Determines the byte size of the respective basic type. Means: If
T
is a class type, the size ofusize
is returned. To obtain the size of a class in memory, useoffsetof<T>()
instead. Compiles to a constant.function offsetof<T>(fieldName?: string): usize
Determines the offset of the specified field within the given class type. Returns the class type's end offset (means: where the next field would be located, before alignment) if field name has been omitted. Compiles to a constant. The
fieldName
argument must be a compile-time constantstring
because there is no information about field names anymore in the final binary. Hence, the field's name must be known at the time the returned constant is computed.function alignof<T>(): usize
Determines the alignment (log2) of the specified underlying basic type. Means: If
T
is a class type, the alignment ofusize
is returned. Compiles to a constant.
# Utility
function assert<T>(isTrueish: T, message?: string): T
Traps if the specified value is not true-ish, otherwise returns the non-nullable value. Like assertions in C, aborting the entire program if the expectation fails, with the
--noAssert
option to disable all assertions in production.function instantiate<T>(...args: auto[]): T
Instantiates a new instance of
T
using the specified constructor arguments.function changetype<T>(value: auto): T
Changes the type of a value to another one. Useful for casting class instances to their pointer values and vice-versa.
function idof<T>(): u32
Obtains the computed unique id of a class type. Usually only relevant when allocating objects or dealing with RTTI externally.
function nameof<T>(value?: T): string
Determines the name of a given type.
# Low-level WebAssembly operations
# Math
The following generic built-ins compile to WebAssembly instructions directly.
function clz<T>(value: T): T
Performs the sign-agnostic count leading zero bits operation on a 32-bit or 64-bit integer. All zero bits are considered leading if the value is zero.
function ctz<T>(value: T): T
Performs the sign-agnostic count tailing zero bits operation on a 32-bit or 64-bit integer. All zero bits are considered trailing if the value is zero.
function popcnt<T>(value: T): T
Performs the sign-agnostic count number of one bits operation on a 32-bit or 64-bit integer.
function rotl<T>(value: T, shift: T): T
Performs the sign-agnostic rotate left operation on a 32-bit or 64-bit integer.
function rotr<T>(value: T, shift: T): T
Performs the sign-agnostic rotate right operation on a 32-bit or 64-bit integer.
function abs<T>(value: T): T
Computes the absolute value of an integer or float.
function max<T>(left: T, right: T): T
Determines the maximum of two integers or floats. If either operand is
NaN
, returnsNaN
.function min<T>(left: T, right: T): T
Determines the minimum of two integers or floats. If either operand is
NaN
, returnsNaN
.function ceil<T>(value: T): T
Performs the ceiling operation on a 32-bit or 64-bit float.
function floor<T>(value: T): T
Performs the floor operation on a 32-bit or 64-bit float.
function copysign<T>(x: T , y: T): T
Composes a 32-bit or 64-bit float from the magnitude of
x
and the sign ofy
.function nearest<T>(value: T): T
Rounds to the nearest integer tied to even of a 32-bit or 64-bit float.
function reinterpret<T>(value: auto): T
Reinterprets the bits of the specified value as type
T
. Valid reinterpretations are u32/i32 to/from f32 and u64/i64 to/from f64.function sqrt<T>(value: T): T
Calculates the square root of a 32-bit or 64-bit float.
function trunc<T>(value: T): T
Rounds to the nearest integer towards zero of a 32-bit or 64-bit float.
# Memory
Similarly, the following built-ins emit WebAssembly instructions accessing or otherwise modifying memory.
function load<T>(ptr: usize, immOffset?: usize): T
Loads a value of the specified type from memory. Equivalent to dereferencing a pointer in other languages.
function store<T>(ptr: usize, value: auto, immOffset?: usize): void
Stores a value of the specified type to memory. Equivalent to dereferencing a pointer in other languages when assigning a value.
function memory.size(): i32
Returns the current size of the memory in units of pages. One page is 64kb.
function memory.grow(value: i32): i32
Grows linear memory by a given unsigned delta of pages. One page is 64kb. Returns the previous size of the memory in units of pages or
-1
on failure.WARNING
Calling
memory.grow
where a memory manager is present might break it.function memory.copy(dst: usize, src: usize, n: usize): void
Copies
n
bytes fromsrc
todst
. Regions may overlap. Emits the respective instruction if bulk-memory is enabled, otherwise ships a polyfill.function memory.fill(dst: usize, value: u8, n: usize): void
Fills
n
bytes atdst
with the given bytevalue
. Emits the respective instruction if bulk-memory is enabled, otherwise ships a polyfill.function memory.repeat(dst: usize, src: usize, srcLength: usize, count: usize): void
Repeats a sequence of bytes given as
src
withsrcLength
count
times into destinationdst
.function memory.compare(lhs: usize, rhs: usize, n: usize): i32
Compares the first
n
bytes ofleft
andrigth
and returns a value that indicates their relationship:- Negative value if the first differing byte in
lhs
is less than the corresponding byte inrhs
. - Positive value if the first differing byte in
lhs
is greater than the corresponding byte inrhs
. - Zero if all
n
bytes oflhs
andrhs
are equal.
- Negative value if the first differing byte in
function memory.data(size: i32, align?: i32): usize
Gets a pointer to a zeroed static chunk of memory of the given size. Alignment defaults to
16
. Arguments must be compile-time constants.function memory.data<T>(values: T[], align?: i32): usize
Gets a pointer to a pre-initialized static chunk of memory. Alignment defaults to the size of
T
. Arguments must be compile-time constants.
The immOffset
argument is a bit special here, because it becomes an actual immediate of the respective WebAssembly instruction instead of a normal operand. Thus it must be provided as a compile time constant value. This can be a literal or the value of a const
variable that the compiler can precompute.
# Memory Manager
An unsafe interface to use the dynamic memory manager directly, resembling malloc
, realloc
and free
in C. Manual memory management can be used in parallel to garbage collection, which can be quite handy, but manually managed blocks cannot be mixed with garbage collected objects (i.e. trying to heap.free
a GC object or casting a block to a managed object respectively would break since one has a GC header and the other does not).
function heap.alloc(size: usize): usize
Allocates a chunk of memory of at least the specified size.
function heap.realloc(ptr: usize, size: usize): usize
Reallocates a chunk of memory to have at least the specified size.
function heap.free(ptr: usize): void
Frees a chunk of memory.
function heap.reset(): void
Dangerously resets the entire heap. Specific to the "stub" runtime.
# Memory Utility
Sign-agnostic endian conversions (reverse bytes).
function bswap<T>(value: T): T
Reverses the byte order of the specified integer.
function bswap16<T>(value: T): T
Reverses only the last 2 bytes regardless of the type argument.
# Control flow
function select<T>(ifTrue: T, ifFalse: T, condition: bool): T
Selects one of two pre-evaluated values depending on the condition. Differs from an
if/else
in that both arms are always executed and the final value is picked based on the condition afterwards. Performs better than anif/else
only if the condition is random (means: branch prediction is not going to perform well) and both alternatives are cheap. It is also worth to note that Binaryen will do relevant optimizations like switching to aselect
automatically, so using a ternary? :
for example is just fine.function unreachable(): auto
Emits an unreachable instruction that results in a runtime error (trap) when executed. Both a statement and an expression of any type. Beware that trapping in managed code will most likely lead to memory leaks or even break the program because it ends execution prematurely.
# Atomics 🦄
The following instructions represent the WebAssembly threads and atomics (opens new window) specification. Must be enabled with --enable threads
.
function atomic.load<T>(ptr: usize, immOffset?: usize): T
Atomically loads an integer value from memory and returns it.
function atomic.store<T>(ptr: usize, value: auto, immOffset?: usize): void
Atomically stores an integer value to memory.
function atomic.add<T>(ptr: usize, value: T, immOffset?: usize): T
Atomically adds an integer value in memory.
function atomic.sub<T>(ptr: usize, value: T, immOffset?: usize): T
Atomically subtracts an integer value in memory.
function atomic.and<T>(ptr: usize, value: T, immOffset?: usize): T
Atomically performs a bitwise AND operation on an integer value in memory.
function atomic.or<T>(ptr: usize, value: T, immOffset?: usize): T
Atomically performs a bitwise OR operation on an integer value in memory.
function atomic.xor<T>(ptr: usize, value: T, immOffset?: usize): T
Atomically performs a bitwise XOR operation on an integer value in memory.
function atomic.xchg<T>(ptr: usize, value: T, immOffset?: usize): T
Atomically exchanges an integer value in memory.
function atomic.cmpxchg<T>(ptr: usize, expected: T, replacement: T, immOffset?: usize): T
Atomically compares and exchanges an integer value in memory if the condition is met.
function atomic.wait<T>(ptr: usize, expected: T, timeout: i64): AtomicWaitResult
Performs a wait operation on an address in memory suspending this agent if the integer condition is met. Return values are
Value Description 0 OK - Woken by another agent. 1 NOT_EQUAL - Loaded value did not match the expected value. 2 TIMED_OUT - Not woken before the timeout expired. function atomic.notify(ptr: usize, count: i32): i32
Performs a notify operation on an address in memory waking up suspended agents.
function atomic.fence(): void
Performs a fence operation, preserving synchronization guarantees of higher level languages.
Again, the immOffset
argument must be a compile time constant value.
# SIMD 🦄
Likewise, these represent the WebAssembly SIMD (opens new window) specification. Must be enabled with --enable simd
.
function v128(a: i8, ... , p: i8): v128
Initializes a 128-bit vector from sixteen 8-bit integer values. Arguments must be compile-time constants.
function v128.splat<T>(x: T): v128
Creates a vector with identical lanes.
function v128.extract_lane<T>(x: v128, idx: u8): T
Extracts one lane as a scalar.
function v128.replace_lane<T>(x: v128, idx: u8, value: T): v128
Replaces one lane.
function v128.shuffle<T>(a: v128, b: v128, ...lanes: u8[]): v128
Selects lanes from either vector according to the specified lane indexes.
function v128.swizzle(a: v128, s: v128): v128
Selects 8-bit lanes from the first vector according to the indexes [0-15] specified by the 8-bit lanes of the second vector.
function v128.load(ptr: usize, immOffset?: usize, immAlign?: usize): v128
Loads a vector from memory.
function v128.load_splat<T>(ptr: usize, immOffset?: usize, immAlign?: usize): v128
Creates a vector with identical lanes by loading the splatted value.
function v128.load_ext<TFrom>(ptr: usize, immOffset?: usize, immAlign?: usize): v128
Creates a vector by loading the lanes of the specified integer type and extending each to the next larger type.
function v128.store(ptr: usize, value: v128, immOffset?: usize, immAlign?: usize): void
Stores a vector to memory.
function v128.add<T>(a: v128, b: v128): v128
Adds each lane.
function v128.sub<T>(a: v128, b: v128): v128
Subtracts each lane.
function v128.mul<T>(a: v128, b: v128): v128
Multiplies each lane.
function v128.div<T>(a: v128, b: v128): v128
Divides each floating point lane.
function v128.neg<T>(a: v128): v128
Negates each lane.
function v128.add_sat<T>(a: v128, b: v128): v128
Adds each signed small integer lane using saturation.
function v128.sub_sat<T>(a: v128, b: v128): v128
Subtracts each signed small integer lane using saturation.
function v128.shl<T>(a: v128, b: i32): v128
Performs a bitwise left shift by a scalar on each integer lane.
function v128.shr<T>(a: v128, b: i32): v128
Performs a bitwise right shift by a scalar on each integer lane.
function v128.and(a: v128, b: v128): v128
Performs the bitwise
a & b
operation on each lane.function v128.or(a: v128, b: v128): v128
Performs the bitwise
a | b
operation on each lane.function v128.xor(a: v128, b: v128): v128
Performs the bitwise
a ^ b
operation on each lane.function v128.andnot(a: v128, b: v128): v128
Performs the bitwise
!a & b
operation on each lane.function v128.not(a: v128): v128
Performs the bitwise
!a
operation on each lane.function v128.bitselect(a: v128, b: v128, mask: v128): v128
Selects bits of either vector according to the specified mask.
function v128.any_true(a: v128): bool
Reduces a vector to a scalar indicating whether any lane is considered
true
.function v128.all_true<T>(a: v128): bool
Reduces a vector to a scalar indicating whether all lanes are considered
true
.function v128.bitmask<T>(a: v128): bool
Extracts the high bit of each integer lane (except 64-bit) and produces a scalar mask with all bits concatenated.
function v128.max<T>(a: v128, b: v128): v128
Computes the maximum of each lane.
function v128.min<T>(a: v128, b: v128): v128
Computes the minimum of each lane.
function v128.pmax<T>(a: v128, b: v128): v128
Computes the pseudo-maximum of each lane.
function v128.pmin<T>(a: v128, b: v128): v128
Computes the psuedo-minimum of each lane.
function v128.dot<T>(a: v128, b: v128): v128
Computes the dot product of two 16-bit integer lanes each, yielding lanes one size wider than the input.
function v128.avgr<T>(a: v128, b: v128): v128)
Computes the rounding average of each unsigned small integer lane.
function v128.abs<T>(a: v128): v128
Computes the absolute value of each lane (except 64-bit integers).
function v128.sqrt<T>(a: v128): v128
Computes the square root of each floating point lane.
function v128.ceil<T>(a: v128): v128
Performs the ceiling operation on each lane.
function v128.floor<T>(a: v128): v128
Performs the floor operation on each lane.
function v128.trunc<T>(a: v128): v128
Rounds to the nearest integer towards zero of each lane.
function v128.nearest<T>(a: v128): v128
Rounds to the nearest integer tied to even of each lane.
function v128.eq<T>(a: v128, b: v128): v128
Computes which lanes are equal.
function v128.ne<T>(a: v128, b: v128): v128
Computes which lanes are not equal.
function v128.lt<T>(a: v128, b: v128): v128
Computes which lanes of the first vector are less than those of the second.
function v128.le<T>(a: v128, b: v128): v128
Computes which lanes of the first vector are less than or equal those of the second.
function v128.gt<T>(a: v128, b: v128): v128
Computes which lanes of the first vector are greater than those of the second.
function v128.ge<T>(a: v128, b: v128): v128
Computes which lanes of the first vector are greater than or equal those of the second.
function v128.convert<TFrom>(a: v128): v128
Converts each lane from integer to floating point.
function v128.trunc_sat<TTo>(a: v128): v128
Truncates each lane from floating point to integer with saturation.
function v128.narrow<TFrom>(a: v128, b: v128): v128
Narrows wider integer lanes to their respective narrower lanes.
function v128.extend_low<TFrom>(a: v128): v128
Extends the low half of narrower integer lanes to their respective wider lanes.
function v128.extend_high<TFrom>(a: v128): v128
Extends the high half of narrower integer lanes to their respective wider lanes.
In addition, the namespaces i8x16
, i16x8
, i32x4
, i64x2
, f32x4
and f64x2
provide their respective non-generic instructions, like i32x4.splat
etc. Each of them can also be used to create a literal directly:
function i8x16(a: i8, ... , p: i8): v128
Initializes a 128-bit vector from sixteen 8-bit integer values. Arguments must be compile-time constants.
function i16x8(a: i16, ..., h: i16): v128
Initializes a 128-bit vector from eight 16-bit integer values. Arguments must be compile-time constants.
function i32x4(a: i32, b: i32, c: i32, d: i32): v128
Initializes a 128-bit vector from four 32-bit integer values. Arguments must be compile-time constants.
function i64x2(a: i64, b: i64): v128
Initializes a 128-bit vector from two 64-bit integer values. Arguments must be compile-time constants.
function f32x4(a: f32, b: f32, c: f32, d: f32): v128
Initializes a 128-bit vector from four 32-bit float values. Arguments must be compile-time constants.
function f64x2(a: f64, b: f64): v128
Initializes a 128-bit vector from two 64-bit float values. Arguments must be compile-time constants.