The runtime provides the functionality necessary to dynamically allocate and deallocate memory of objects, arrays and buffers, as well as keep track of references that are no longer used.
Retains a reference to an object. The object doesn't become collected as long as there's at least one retained reference. Returns the retained reference.
Obtains the runtime type information for objects with the specified runtime id. Runtime type information is a set of flags indicating whether a reference type is managed, an array or similar, and what the relevant alignments when creating an instance externally are etc.
Calls `__visit` on each global that is of a reference type. Not used anymore (originally provided to support tracing GCs) but still here for possible future use.
The [full](./index-full.ts) runtime is based on [the TLSF memory manager](./tlsf.ts) and [a pure reference counting garbage collector](./pure.ts) and provides all the functionality necessary. The [half](./index-half.ts) alias is essentially the same, but doesn't expose the runtime API so unused runtime code can be DCE'ed.
The [stub](./index-stub.ts) runtime, though fully functional, provides minimal dynamic memory allocation but no deallocation. Useful for prototyping or very short-lived programs with hardly any memory footprint. The [none](./index-none.ts) alias is the same, but doesn't expose the runtime API so unused runtime code can be DCE'ed.
The underlying reference counting implementation works very similar to other implementations. When an object is stored in a local, global or field, its reference becomes retained (reference count is incremented by 1), respectively when it becomes deleted, it is released (reference count is decremented by 1). Once the reference count reaches zero, the object is considered for collection and the reference count of all contained objects (fields, array elements etc.) is decremented by 1. Now, if an object is inherently acyclic (most objects are), it is free'd right away, while otherwise it is added to a cycle pool and considered for cycle collection on the next `__collect`.
* Functions with reference type arguments retain each such argument when called, and release it again when exited. This can't be counted on, however, because the compiler may decide to eliminate these where unnecessary.
Even though the rules are simple, working with the runtime internals within standard library code can be tricky and requires knowledge of where the compiler will insert runtime calls automatically. For instance
*`changetype`ing a pointer to a reference type or vice-versa has no side-effects. For example in a `changetype<Ref>(ptr)` the pointer is untracked before, and the reference becomes tracked afterwards, but there's nothing inserted in between.
*`load`, `store` and similar built-ins aren't functions but intrinsics and as such have no side-effects. For example a `load<Ref>(...)` is equivalent to a `changetype<Ref>(load<usize>(...))` and a `store<Ref>(..., ref)` equivalent to a `store<usize>(..., changetype<usize>(ref))`.
**BONUS:** Beware of runtime calls in conditional expressions like a ternary IF, logical AND or OR. Each arm can be in either of two states: Either in-flight if immediately retained/returned or not if the expression or the target doesn't support it. Don't fight the compiler there.