mirror of
https://github.com/fluencelabs/assemblyscript
synced 2025-06-22 19:21:47 +00:00
layout rtti with fixed ids for buffers and strings
This commit is contained in:
@ -1 +0,0 @@
|
||||
These source files are used by both the standard library *and* compiler. Must remain portable.
|
2
std/assembly/index.d.ts
vendored
2
std/assembly/index.d.ts
vendored
@ -112,6 +112,8 @@ declare function sizeof<T>(): usize;
|
||||
declare function alignof<T>(): usize;
|
||||
/** Determines the offset of the specified field within the given class type. Returns the class type's end offset if field name has been omitted. Compiles to a constant. */
|
||||
declare function offsetof<T>(fieldName?: string): usize;
|
||||
/** Determines the unique runtime id of a class type. Compiles to a constant. */
|
||||
declare function idof<T>(): u32;
|
||||
/** Changes the type of any value of `usize` kind to another one of `usize` kind. Useful for casting class instances to their pointer values and vice-versa. Beware that this is unsafe.*/
|
||||
declare function changetype<T>(value: any): T;
|
||||
/** Explicitly requests no bounds checks on the provided expression. Useful for array accesses. */
|
||||
|
@ -1,7 +1,8 @@
|
||||
import { itoa, dtoa } from "./util/number";
|
||||
import { isNaN as builtin_isNaN, isFinite as builtin_isFinite } from "./builtins";
|
||||
|
||||
@sealed export abstract class I8 {
|
||||
@sealed @unmanaged
|
||||
export abstract class I8 {
|
||||
|
||||
// @ts-ignore: decorator
|
||||
@lazy
|
||||
@ -21,7 +22,8 @@ import { isNaN as builtin_isNaN, isFinite as builtin_isFinite } from "./builtins
|
||||
}
|
||||
}
|
||||
|
||||
@sealed export abstract class I16 {
|
||||
@sealed @unmanaged
|
||||
export abstract class I16 {
|
||||
|
||||
// @ts-ignore: decorator
|
||||
@lazy
|
||||
@ -41,7 +43,8 @@ import { isNaN as builtin_isNaN, isFinite as builtin_isFinite } from "./builtins
|
||||
}
|
||||
}
|
||||
|
||||
@sealed export abstract class I32 {
|
||||
@sealed @unmanaged
|
||||
export abstract class I32 {
|
||||
|
||||
// @ts-ignore: decorator
|
||||
@lazy
|
||||
@ -61,7 +64,8 @@ import { isNaN as builtin_isNaN, isFinite as builtin_isFinite } from "./builtins
|
||||
}
|
||||
}
|
||||
|
||||
@sealed export abstract class I64 {
|
||||
@sealed @unmanaged
|
||||
export abstract class I64 {
|
||||
|
||||
// @ts-ignore: decorator
|
||||
@lazy
|
||||
@ -81,7 +85,8 @@ import { isNaN as builtin_isNaN, isFinite as builtin_isFinite } from "./builtins
|
||||
}
|
||||
}
|
||||
|
||||
@sealed export abstract class Isize {
|
||||
@sealed @unmanaged
|
||||
export abstract class Isize {
|
||||
|
||||
// @ts-ignore: decorator
|
||||
@lazy
|
||||
@ -101,7 +106,8 @@ import { isNaN as builtin_isNaN, isFinite as builtin_isFinite } from "./builtins
|
||||
}
|
||||
}
|
||||
|
||||
@sealed export abstract class U8 {
|
||||
@sealed @unmanaged
|
||||
export abstract class U8 {
|
||||
|
||||
// @ts-ignore: decorator
|
||||
@lazy
|
||||
@ -121,7 +127,8 @@ import { isNaN as builtin_isNaN, isFinite as builtin_isFinite } from "./builtins
|
||||
}
|
||||
}
|
||||
|
||||
@sealed export abstract class U16 {
|
||||
@sealed @unmanaged
|
||||
export abstract class U16 {
|
||||
|
||||
// @ts-ignore: decorator
|
||||
@lazy
|
||||
@ -141,7 +148,8 @@ import { isNaN as builtin_isNaN, isFinite as builtin_isFinite } from "./builtins
|
||||
}
|
||||
}
|
||||
|
||||
@sealed export abstract class U32 {
|
||||
@sealed @unmanaged
|
||||
export abstract class U32 {
|
||||
|
||||
// @ts-ignore: decorator
|
||||
@lazy
|
||||
@ -161,7 +169,8 @@ import { isNaN as builtin_isNaN, isFinite as builtin_isFinite } from "./builtins
|
||||
}
|
||||
}
|
||||
|
||||
@sealed export abstract class U64 {
|
||||
@sealed @unmanaged
|
||||
export abstract class U64 {
|
||||
|
||||
// @ts-ignore: decorator
|
||||
@lazy
|
||||
@ -181,7 +190,8 @@ import { isNaN as builtin_isNaN, isFinite as builtin_isFinite } from "./builtins
|
||||
}
|
||||
}
|
||||
|
||||
@sealed export abstract class Usize {
|
||||
@sealed @unmanaged
|
||||
export abstract class Usize {
|
||||
|
||||
// @ts-ignore: decorator
|
||||
@lazy
|
||||
@ -201,7 +211,8 @@ import { isNaN as builtin_isNaN, isFinite as builtin_isFinite } from "./builtins
|
||||
}
|
||||
}
|
||||
|
||||
@sealed export abstract class Bool {
|
||||
@sealed @unmanaged
|
||||
export abstract class Bool {
|
||||
|
||||
// @ts-ignore: decorator
|
||||
@lazy
|
||||
@ -219,7 +230,8 @@ import { isNaN as builtin_isNaN, isFinite as builtin_isFinite } from "./builtins
|
||||
|
||||
export { Bool as Boolean };
|
||||
|
||||
@sealed export abstract class F32 {
|
||||
@sealed @unmanaged
|
||||
export abstract class F32 {
|
||||
|
||||
// @ts-ignore: decorator
|
||||
@lazy
|
||||
@ -283,7 +295,8 @@ export { Bool as Boolean };
|
||||
}
|
||||
}
|
||||
|
||||
@sealed export abstract class F64 {
|
||||
@sealed @unmanaged
|
||||
export abstract class F64 {
|
||||
|
||||
// @ts-ignore: decorator
|
||||
@lazy
|
||||
|
@ -1,5 +1,4 @@
|
||||
import { idof } from "./builtins";
|
||||
import { RTTIData, RTTIFlags } from "./common/rtti";
|
||||
import { Typeinfo, TypeinfoFlags } from "./shared/typeinfo";
|
||||
import { E_INDEXOUTOFRANGE } from "./util/error";
|
||||
import { BLOCK, BLOCK_OVERHEAD } from "./rt/common";
|
||||
import { ArrayBufferView } from "./arraybuffer";
|
||||
@ -18,10 +17,10 @@ export declare function __visit_members(ref: usize, cookie: u32): void;
|
||||
|
||||
// @ts-ignore: decorator
|
||||
@unsafe
|
||||
export function __typeinfo(id: u32): RTTIFlags {
|
||||
export function __typeinfo(id: u32): TypeinfoFlags {
|
||||
var ptr = RTTI_BASE;
|
||||
if (!id || id > load<u32>(ptr)) throw new Error(E_INDEXOUTOFRANGE);
|
||||
return changetype<RTTIData>(ptr + id * offsetof<RTTIData>()).flags;
|
||||
if (id > load<u32>(ptr)) throw new Error(E_INDEXOUTOFRANGE);
|
||||
return changetype<Typeinfo>(ptr + sizeof<u32>() + id * offsetof<Typeinfo>()).flags;
|
||||
}
|
||||
|
||||
// @ts-ignore: decorator
|
||||
@ -29,9 +28,9 @@ export function __typeinfo(id: u32): RTTIFlags {
|
||||
export function __instanceof(ref: usize, superId: u32): bool { // keyword
|
||||
var id = changetype<BLOCK>(ref - BLOCK_OVERHEAD).rtId;
|
||||
var ptr = RTTI_BASE;
|
||||
if (id && id <= load<u32>(ptr)) {
|
||||
if (id <= load<u32>(ptr)) {
|
||||
do if (id == superId) return true;
|
||||
while (id = changetype<RTTIData>(ptr + id * offsetof<RTTIData>()).base);
|
||||
while (id = changetype<Typeinfo>(ptr + sizeof<u32>() + id * offsetof<Typeinfo>()).base);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -38,12 +38,19 @@ Interface
|
||||
* **__typeinfo**(id: `u32`): `RTTIFlags`<br />
|
||||
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.
|
||||
|
||||
* **__instanceof**
|
||||
|
||||
* **__visit_globals**(cookie: `u32`): `void`<br />
|
||||
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.
|
||||
|
||||
* **__visit_members**(ref: `usize`, cookie: `u32`): `void`<br />
|
||||
Calls `__visit` on each member of the object pointed to by `ref`.
|
||||
|
||||
### Related
|
||||
|
||||
* **idof**<`T`>(): `u32`<br />
|
||||
Obtains the unique internal class id of a reference type.
|
||||
|
||||
Full/half
|
||||
---------
|
||||
|
||||
@ -68,22 +75,23 @@ Differences to other implementations include:
|
||||
|
||||
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. The pointer is untracked before, and the reference becomes tracked afterwards, but there's nothing inserted in between.
|
||||
* `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))`.
|
||||
|
||||
**GOOD:** In case of doubt, the following pattern is universal:
|
||||
|
||||
```ts
|
||||
var ref = changetype<Ref>(__alloc(SIZE, idof<Ref>())); // retains the object in `ref`
|
||||
// here, the __retain is inserted by the assignment to ref, not by changetype or __alloc
|
||||
...
|
||||
var ref = changetype<Ref>(__alloc(SIZE, idof<Ref>())); // assignment retains, RC=1
|
||||
// ... safe ...
|
||||
return ref; // knows `ref` is already retained and simply returns it
|
||||
```
|
||||
|
||||
**BAD:** A pattern one shouldn't use is:
|
||||
|
||||
```ts
|
||||
someFunc(changetype<Ref>(__alloc(SIZE, idof<Ref>()))); // might free the object before the call returns
|
||||
var ptr = __alloc(SIZE, idof<Ref>()); // RC=0
|
||||
// ... not safe while RC=0 ... e.g.:
|
||||
someFunc(changetype<Ref>(ptr)); // might, or might not, free the object!
|
||||
```
|
||||
|
||||
**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.
|
||||
**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.
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { DEBUG, BLOCK_OVERHEAD } from "rt/common";
|
||||
import { Block, freeBlock, ROOT } from "rt/tlsf";
|
||||
import { RTTIFlags } from "common/rtti";
|
||||
import { TypeinfoFlags } from "shared/typeinfo";
|
||||
|
||||
/////////////////////////// A Pure Reference Counting Garbage Collector ///////////////////////////
|
||||
// see: https://researcher.watson.ibm.com/researcher/files/us-bacon/Bacon03Pure.pdf
|
||||
@ -121,7 +121,7 @@ function decrement(s: Block): void {
|
||||
}
|
||||
} else {
|
||||
if (DEBUG) assert(rc > 0);
|
||||
if (!(__typeinfo(s.rtId) & RTTIFlags.ACYCLIC)) {
|
||||
if (!(__typeinfo(s.rtId) & TypeinfoFlags.ACYCLIC)) {
|
||||
s.gcInfo = BUFFERED_MASK | COLOR_PURPLE | (rc - 1);
|
||||
if (!(info & BUFFERED_MASK)) {
|
||||
appendRoot(s);
|
||||
|
@ -1,3 +1,5 @@
|
||||
// This file is shared with the compiler and must remain portable
|
||||
|
||||
/** Indicates specific features to activate. */
|
||||
export const enum Feature {
|
||||
/** No additional features. */
|
@ -1,3 +1,5 @@
|
||||
// This file is shared with the compiler and must remain portable
|
||||
|
||||
/** Compilation target. */
|
||||
export enum Target {
|
||||
/** WebAssembly with 32-bit pointers. */
|
@ -1,26 +1,28 @@
|
||||
// ╒═════════════════════ RTTI interpretation ═════════════════════╕
|
||||
// This file is shared with the compiler and must remain portable
|
||||
|
||||
// ╒═══════════════════ Typeinfo interpretation ═══════════════════╕
|
||||
// 3 2 1
|
||||
// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 bits
|
||||
// ├─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┤ ◄─ RTTI_BASE
|
||||
// │ count │
|
||||
// ├ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤
|
||||
// │ reserved │
|
||||
// ╞═══════════════════════════════════════════════════════════════╡ ┐
|
||||
// │ RTTIData#flags [id=1] │ id=1..count
|
||||
// │ Typeinfo#flags [id=0] │ id < count
|
||||
// ├ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤
|
||||
// │ RTTIData#base [id=1] │
|
||||
// │ Typeinfo#base [id=0] │
|
||||
// ├───────────────────────────────────────────────────────────────┤
|
||||
// │ ... │
|
||||
|
||||
/** Runtime type information data structure. */
|
||||
@unmanaged
|
||||
export class RTTIData {
|
||||
flags: RTTIFlags;
|
||||
export class Typeinfo {
|
||||
/** Flags describing the shape of this class type. */
|
||||
flags: TypeinfoFlags;
|
||||
/** Base class id or `0` if none. */
|
||||
base: u32;
|
||||
}
|
||||
|
||||
/** Runtime type information flags. */
|
||||
export const enum RTTIFlags {
|
||||
export const enum TypeinfoFlags {
|
||||
/** No specific flags. */
|
||||
NONE = 0,
|
||||
/** Type is an `Array`. */
|
Reference in New Issue
Block a user