mirror of
https://github.com/fluencelabs/assemblyscript
synced 2025-04-26 07:22:21 +00:00
layout rtti with fixed ids for buffers and strings
This commit is contained in:
parent
c92ca0d8cb
commit
d94b4fca50
@ -3,7 +3,7 @@
|
||||
|
||||
[](https://travis-ci.org/AssemblyScript/assemblyscript)
|
||||
|
||||
**AssemblyScript** compiles strictly typed [TypeScript](http://www.typescriptlang.org) (basically JavaScript with types) to [WebAssembly](http://webassembly.org) using [Binaryen](https://github.com/WebAssembly/binaryen). It generates lean and mean WebAssembly modules while being just an `npm install` away.
|
||||
**AssemblyScript** compiles a strict subset of [TypeScript](http://www.typescriptlang.org) (basically JavaScript with types) to [WebAssembly](http://webassembly.org) using [Binaryen](https://github.com/WebAssembly/binaryen). It generates lean and mean WebAssembly modules while being just an `npm install` away.
|
||||
|
||||
Try it out in [WebAssembly Studio](https://webassembly.studio)!
|
||||
|
||||
|
@ -6,7 +6,8 @@
|
||||
import {
|
||||
Compiler,
|
||||
ContextualFlags,
|
||||
RuntimeFeatures
|
||||
RuntimeFeatures,
|
||||
flatten
|
||||
} from "./compiler";
|
||||
|
||||
import {
|
||||
@ -73,7 +74,7 @@ import {
|
||||
import {
|
||||
CommonFlags,
|
||||
Feature,
|
||||
RTTIFlags
|
||||
TypeinfoFlags
|
||||
} from "./common";
|
||||
|
||||
import {
|
||||
@ -4133,7 +4134,6 @@ export function compileVisitMembers(compiler: Compiler): void {
|
||||
var managedClasses = program.managedClasses;
|
||||
var visitInstance = assert(program.visitInstance);
|
||||
var blocks = new Array<RelooperBlockRef>();
|
||||
var lastId = 0;
|
||||
var relooper = Relooper.create(module);
|
||||
|
||||
var outer = relooper.addBlockWithSwitch(
|
||||
@ -4153,17 +4153,10 @@ export function compileVisitMembers(compiler: Compiler): void {
|
||||
)
|
||||
);
|
||||
|
||||
blocks.push(
|
||||
relooper.addBlock(
|
||||
module.createUnreachable()
|
||||
)
|
||||
);
|
||||
|
||||
relooper.addBranchForSwitch(outer, blocks[0], []);
|
||||
|
||||
var lastId = 0;
|
||||
for (let [id, instance] of managedClasses) {
|
||||
assert(instance.type.isManaged);
|
||||
assert(id == ++lastId);
|
||||
assert(id == lastId++);
|
||||
|
||||
let visitImpl: Element | null;
|
||||
|
||||
@ -4234,7 +4227,9 @@ export function compileVisitMembers(compiler: Compiler): void {
|
||||
}
|
||||
}
|
||||
if (!instance.base) code.push(module.createReturn());
|
||||
let block = relooper.addBlock(module.createBlock(null, code)); // TODO: flatten?
|
||||
let block = relooper.addBlock(
|
||||
flatten(module, code, NativeType.None)
|
||||
);
|
||||
relooper.addBranchForSwitch(outer, block, [ id ]);
|
||||
blocks.push(block);
|
||||
}
|
||||
@ -4245,15 +4240,21 @@ export function compileVisitMembers(compiler: Compiler): void {
|
||||
relooper.addBranch(blocks[id], blocks[base.id]);
|
||||
}
|
||||
}
|
||||
blocks.push(
|
||||
relooper.addBlock(
|
||||
module.createUnreachable()
|
||||
)
|
||||
);
|
||||
relooper.addBranchForSwitch(outer, blocks[blocks.length - 1], []); // default
|
||||
compiler.compileFunction(visitInstance);
|
||||
module.addFunction(BuiltinSymbols.visit_members, ftype, [ nativeSizeType ], relooper.renderAndDispose(outer, 2));
|
||||
}
|
||||
|
||||
function typeToRuntimeFlags(type: Type): RTTIFlags {
|
||||
var flags = RTTIFlags.VALUE_ALIGN_0 * (1 << type.alignLog2);
|
||||
if (type.is(TypeFlags.NULLABLE)) flags |= RTTIFlags.VALUE_NULLABLE;
|
||||
if (type.isManaged) flags |= RTTIFlags.VALUE_MANAGED;
|
||||
return flags / RTTIFlags.VALUE_ALIGN_0;
|
||||
function typeToRuntimeFlags(type: Type): TypeinfoFlags {
|
||||
var flags = TypeinfoFlags.VALUE_ALIGN_0 * (1 << type.alignLog2);
|
||||
if (type.is(TypeFlags.NULLABLE)) flags |= TypeinfoFlags.VALUE_NULLABLE;
|
||||
if (type.isManaged) flags |= TypeinfoFlags.VALUE_MANAGED;
|
||||
return flags / TypeinfoFlags.VALUE_ALIGN_0;
|
||||
}
|
||||
|
||||
/** Compiles runtime type information for use by stdlib. */
|
||||
@ -4262,34 +4263,34 @@ export function compileRTTI(compiler: Compiler): void {
|
||||
var module = compiler.module;
|
||||
var managedClasses = program.managedClasses;
|
||||
var count = managedClasses.size;
|
||||
var size = 8 + 8 * count;
|
||||
var size = 4 + 8 * count;
|
||||
var data = new Uint8Array(size);
|
||||
writeI32(count, data, 0);
|
||||
var off = 8;
|
||||
var off = 4;
|
||||
var arrayPrototype = program.arrayPrototype;
|
||||
var setPrototype = program.setPrototype;
|
||||
var mapPrototype = program.mapPrototype;
|
||||
var lastId = 0;
|
||||
for (let [id, instance] of managedClasses) {
|
||||
assert(id == ++lastId);
|
||||
let flags: RTTIFlags = 0;
|
||||
if (instance.isAcyclic) flags |= RTTIFlags.ACYCLIC;
|
||||
assert(id == lastId++);
|
||||
let flags: TypeinfoFlags = 0;
|
||||
if (instance.isAcyclic) flags |= TypeinfoFlags.ACYCLIC;
|
||||
if (instance.prototype.extends(arrayPrototype)) {
|
||||
let typeArguments = assert(instance.getTypeArgumentsTo(arrayPrototype));
|
||||
assert(typeArguments.length == 1);
|
||||
flags |= RTTIFlags.ARRAY;
|
||||
flags |= RTTIFlags.VALUE_ALIGN_0 * typeToRuntimeFlags(typeArguments[0]);
|
||||
flags |= TypeinfoFlags.ARRAY;
|
||||
flags |= TypeinfoFlags.VALUE_ALIGN_0 * typeToRuntimeFlags(typeArguments[0]);
|
||||
} else if (instance.prototype.extends(setPrototype)) {
|
||||
let typeArguments = assert(instance.getTypeArgumentsTo(setPrototype));
|
||||
assert(typeArguments.length == 1);
|
||||
flags |= RTTIFlags.SET;
|
||||
flags |= RTTIFlags.VALUE_ALIGN_0 * typeToRuntimeFlags(typeArguments[0]);
|
||||
flags |= TypeinfoFlags.SET;
|
||||
flags |= TypeinfoFlags.VALUE_ALIGN_0 * typeToRuntimeFlags(typeArguments[0]);
|
||||
} else if (instance.prototype.extends(mapPrototype)) {
|
||||
let typeArguments = assert(instance.getTypeArgumentsTo(mapPrototype));
|
||||
assert(typeArguments.length == 2);
|
||||
flags |= RTTIFlags.MAP;
|
||||
flags |= RTTIFlags.KEY_ALIGN_0 * typeToRuntimeFlags(typeArguments[0]);
|
||||
flags |= RTTIFlags.VALUE_ALIGN_0 * typeToRuntimeFlags(typeArguments[1]);
|
||||
flags |= TypeinfoFlags.MAP;
|
||||
flags |= TypeinfoFlags.KEY_ALIGN_0 * typeToRuntimeFlags(typeArguments[0]);
|
||||
flags |= TypeinfoFlags.VALUE_ALIGN_0 * typeToRuntimeFlags(typeArguments[1]);
|
||||
}
|
||||
writeI32(flags, data, off); off += 4;
|
||||
let base = instance.base;
|
||||
|
@ -196,7 +196,6 @@ export namespace CommonSymbols {
|
||||
}
|
||||
|
||||
// shared
|
||||
import { Feature } from "../std/assembly/common/feature";
|
||||
import { RTTIData, RTTIFlags } from "../std/assembly/common/rtti";
|
||||
import { Target } from "../std/assembly/common/target";
|
||||
export { Feature, RTTIData, RTTIFlags, Target };
|
||||
export { Feature } from "../std/assembly/shared/feature";
|
||||
export { Target } from "../std/assembly/shared/target";
|
||||
export { Typeinfo, TypeinfoFlags } from "../std/assembly/shared/typeinfo";
|
||||
|
@ -9033,7 +9033,7 @@ var mangleImportName_moduleName: string;
|
||||
var mangleImportName_elementName: string;
|
||||
|
||||
/** Flattens a series of expressions to a nop, a single statement or a block depending on statement count. */
|
||||
function flatten(module: Module, stmts: ExpressionRef[], type: NativeType): ExpressionRef {
|
||||
export function flatten(module: Module, stmts: ExpressionRef[], type: NativeType): ExpressionRef {
|
||||
var length = stmts.length;
|
||||
if (length == 0) return module.createNop(); // usually filtered out again
|
||||
if (length == 1) return stmts[0];
|
||||
|
@ -280,8 +280,8 @@ export abstract class DiagnosticEmitter {
|
||||
var message = DiagnosticMessage.create(code, category, arg0, arg1, arg2).withRange(range);
|
||||
if (relatedRange) message.relatedRange = relatedRange;
|
||||
this.diagnostics.push(message);
|
||||
console.log(formatDiagnosticMessage(message, true, true) + "\n"); // temporary
|
||||
console.log(<string>new Error("stack").stack);
|
||||
// console.log(formatDiagnosticMessage(message, true, true) + "\n"); // temporary
|
||||
// console.log(<string>new Error("stack").stack);
|
||||
}
|
||||
|
||||
/** Emits an informatory diagnostic message. */
|
||||
|
@ -387,7 +387,7 @@ export class Program extends DiagnosticEmitter {
|
||||
allocArrayInstance: Function;
|
||||
|
||||
/** Next class id. */
|
||||
nextClassId: u32 = 1;
|
||||
nextClassId: u32 = 0;
|
||||
|
||||
/** Constructs a new program, optionally inheriting parser diagnostics. */
|
||||
constructor(
|
||||
@ -732,6 +732,13 @@ export class Program extends DiagnosticEmitter {
|
||||
}
|
||||
}
|
||||
|
||||
// register ArrayBuffer (id=0) and String (id=1)
|
||||
assert(this.nextClassId == 0);
|
||||
this.arrayBufferInstance = this.requireClass(CommonSymbols.ArrayBuffer);
|
||||
assert(this.arrayBufferInstance.id == 0);
|
||||
this.stringInstance = this.requireClass(CommonSymbols.String);
|
||||
assert(this.stringInstance.id == 1);
|
||||
|
||||
// register classes backing basic types
|
||||
this.registerNativeTypeClass(TypeKind.I8, CommonSymbols.I8);
|
||||
this.registerNativeTypeClass(TypeKind.I16, CommonSymbols.I16);
|
||||
@ -805,8 +812,6 @@ export class Program extends DiagnosticEmitter {
|
||||
|
||||
// register stdlib components
|
||||
this.arrayBufferViewInstance = this.requireClass(CommonSymbols.ArrayBufferView);
|
||||
this.arrayBufferInstance = this.requireClass(CommonSymbols.ArrayBuffer);
|
||||
this.stringInstance = this.requireClass(CommonSymbols.String);
|
||||
this.arrayPrototype = <ClassPrototype>this.require(CommonSymbols.Array, ElementKind.CLASS_PROTOTYPE);
|
||||
this.fixedArrayPrototype = <ClassPrototype>this.require(CommonSymbols.FixedArray, ElementKind.CLASS_PROTOTYPE);
|
||||
this.setPrototype = <ClassPrototype>this.require(CommonSymbols.Set, ElementKind.CLASS_PROTOTYPE);
|
||||
@ -2997,11 +3002,9 @@ export class Class extends TypedElement {
|
||||
/** Remembers acyclic state. */
|
||||
private _acyclic: AcyclicState = AcyclicState.UNKNOWN;
|
||||
|
||||
/** Gets the unique runtime id of this class. Must be a managed class. */
|
||||
/** Gets the unique runtime id of this class. */
|
||||
get id(): u32 {
|
||||
var id = this._id;
|
||||
assert(id); // must be managed to have an id
|
||||
return id;
|
||||
return this._id; // unmanaged remains 0 (=ArrayBuffer)
|
||||
}
|
||||
|
||||
/** Tests if this class is of a builtin array type (Array/TypedArray). */
|
||||
|
@ -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`. */
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,6 @@
|
||||
export function foo(): string {
|
||||
return "";
|
||||
}
|
||||
|
||||
var a = foo();
|
||||
__release(changetype<usize>(a));
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,9 +1,8 @@
|
||||
/// <reference path="../../../std/assembly/rt/index.d.ts" />
|
||||
|
||||
import { idof } from "builtins";
|
||||
import { RTTIFlags } from "common/rtti";
|
||||
import { TypeinfoFlags } from "shared/typeinfo";
|
||||
|
||||
function test<T>(flags: RTTIFlags): void {
|
||||
function test<T>(flags: TypeinfoFlags): void {
|
||||
assert(
|
||||
__typeinfo(idof<T>())
|
||||
==
|
||||
@ -15,35 +14,35 @@ function test<T>(flags: RTTIFlags): void {
|
||||
|
||||
class Ref {}
|
||||
|
||||
const VALUE_ALIGN_REF = sizeof<usize>() == 4 ? RTTIFlags.VALUE_ALIGN_2 : RTTIFlags.VALUE_ALIGN_3;
|
||||
const KEY_ALIGN_REF = sizeof<usize>() == 4 ? RTTIFlags.KEY_ALIGN_2 : RTTIFlags.KEY_ALIGN_3;
|
||||
const VALUE_ALIGN_REF = sizeof<usize>() == 4 ? TypeinfoFlags.VALUE_ALIGN_2 : TypeinfoFlags.VALUE_ALIGN_3;
|
||||
const KEY_ALIGN_REF = sizeof<usize>() == 4 ? TypeinfoFlags.KEY_ALIGN_2 : TypeinfoFlags.KEY_ALIGN_3;
|
||||
|
||||
test<Array<i8>>(RTTIFlags.ARRAY | RTTIFlags.ACYCLIC | RTTIFlags.VALUE_ALIGN_0);
|
||||
test<Array<i16>>(RTTIFlags.ARRAY | RTTIFlags.ACYCLIC | RTTIFlags.VALUE_ALIGN_1);
|
||||
test<Array<i32>>(RTTIFlags.ARRAY | RTTIFlags.ACYCLIC | RTTIFlags.VALUE_ALIGN_2);
|
||||
test<Array<i64>>(RTTIFlags.ARRAY | RTTIFlags.ACYCLIC | RTTIFlags.VALUE_ALIGN_3);
|
||||
test<Array<v128>>(RTTIFlags.ARRAY | RTTIFlags.ACYCLIC | RTTIFlags.VALUE_ALIGN_4);
|
||||
test<Array<Ref>>(RTTIFlags.ARRAY | RTTIFlags.ACYCLIC | VALUE_ALIGN_REF | RTTIFlags.VALUE_MANAGED);
|
||||
test<Array<Ref | null>>(RTTIFlags.ARRAY | RTTIFlags.ACYCLIC | VALUE_ALIGN_REF | RTTIFlags.VALUE_NULLABLE | RTTIFlags.VALUE_MANAGED);
|
||||
test<Array<i8>>(TypeinfoFlags.ARRAY | TypeinfoFlags.ACYCLIC | TypeinfoFlags.VALUE_ALIGN_0);
|
||||
test<Array<i16>>(TypeinfoFlags.ARRAY | TypeinfoFlags.ACYCLIC | TypeinfoFlags.VALUE_ALIGN_1);
|
||||
test<Array<i32>>(TypeinfoFlags.ARRAY | TypeinfoFlags.ACYCLIC | TypeinfoFlags.VALUE_ALIGN_2);
|
||||
test<Array<i64>>(TypeinfoFlags.ARRAY | TypeinfoFlags.ACYCLIC | TypeinfoFlags.VALUE_ALIGN_3);
|
||||
test<Array<v128>>(TypeinfoFlags.ARRAY | TypeinfoFlags.ACYCLIC | TypeinfoFlags.VALUE_ALIGN_4);
|
||||
test<Array<Ref>>(TypeinfoFlags.ARRAY | TypeinfoFlags.ACYCLIC | VALUE_ALIGN_REF | TypeinfoFlags.VALUE_MANAGED);
|
||||
test<Array<Ref | null>>(TypeinfoFlags.ARRAY | TypeinfoFlags.ACYCLIC | VALUE_ALIGN_REF | TypeinfoFlags.VALUE_NULLABLE | TypeinfoFlags.VALUE_MANAGED);
|
||||
|
||||
test<Set<i8>>(RTTIFlags.SET | RTTIFlags.ACYCLIC | RTTIFlags.VALUE_ALIGN_0);
|
||||
test<Set<i16>>(RTTIFlags.SET | RTTIFlags.ACYCLIC | RTTIFlags.VALUE_ALIGN_1);
|
||||
test<Set<i32>>(RTTIFlags.SET | RTTIFlags.ACYCLIC | RTTIFlags.VALUE_ALIGN_2);
|
||||
test<Set<i64>>(RTTIFlags.SET | RTTIFlags.ACYCLIC | RTTIFlags.VALUE_ALIGN_3);
|
||||
test<Set<v128>>(RTTIFlags.SET | RTTIFlags.ACYCLIC | RTTIFlags.VALUE_ALIGN_4);
|
||||
test<Set<Ref>>(RTTIFlags.SET | RTTIFlags.ACYCLIC | VALUE_ALIGN_REF | RTTIFlags.VALUE_MANAGED);
|
||||
test<Set<Ref | null>>(RTTIFlags.SET | RTTIFlags.ACYCLIC | VALUE_ALIGN_REF | RTTIFlags.VALUE_NULLABLE | RTTIFlags.VALUE_MANAGED);
|
||||
test<Set<i8>>(TypeinfoFlags.SET | TypeinfoFlags.ACYCLIC | TypeinfoFlags.VALUE_ALIGN_0);
|
||||
test<Set<i16>>(TypeinfoFlags.SET | TypeinfoFlags.ACYCLIC | TypeinfoFlags.VALUE_ALIGN_1);
|
||||
test<Set<i32>>(TypeinfoFlags.SET | TypeinfoFlags.ACYCLIC | TypeinfoFlags.VALUE_ALIGN_2);
|
||||
test<Set<i64>>(TypeinfoFlags.SET | TypeinfoFlags.ACYCLIC | TypeinfoFlags.VALUE_ALIGN_3);
|
||||
test<Set<v128>>(TypeinfoFlags.SET | TypeinfoFlags.ACYCLIC | TypeinfoFlags.VALUE_ALIGN_4);
|
||||
test<Set<Ref>>(TypeinfoFlags.SET | TypeinfoFlags.ACYCLIC | VALUE_ALIGN_REF | TypeinfoFlags.VALUE_MANAGED);
|
||||
test<Set<Ref | null>>(TypeinfoFlags.SET | TypeinfoFlags.ACYCLIC | VALUE_ALIGN_REF | TypeinfoFlags.VALUE_NULLABLE | TypeinfoFlags.VALUE_MANAGED);
|
||||
|
||||
test<Map<v128,i8>>(RTTIFlags.MAP | RTTIFlags.ACYCLIC | RTTIFlags.KEY_ALIGN_4 | RTTIFlags.VALUE_ALIGN_0);
|
||||
test<Map<i64,i16>>(RTTIFlags.MAP | RTTIFlags.ACYCLIC | RTTIFlags.KEY_ALIGN_3 | RTTIFlags.VALUE_ALIGN_1);
|
||||
test<Map<i32,i32>>(RTTIFlags.MAP | RTTIFlags.ACYCLIC | RTTIFlags.KEY_ALIGN_2 | RTTIFlags.VALUE_ALIGN_2);
|
||||
test<Map<i16,i64>>(RTTIFlags.MAP | RTTIFlags.ACYCLIC | RTTIFlags.KEY_ALIGN_1 | RTTIFlags.VALUE_ALIGN_3);
|
||||
test<Map<i8,v128>>(RTTIFlags.MAP | RTTIFlags.ACYCLIC | RTTIFlags.KEY_ALIGN_0 | RTTIFlags.VALUE_ALIGN_4);
|
||||
test<Map<Ref,i8>>(RTTIFlags.MAP | RTTIFlags.ACYCLIC | KEY_ALIGN_REF | RTTIFlags.KEY_MANAGED | RTTIFlags.VALUE_ALIGN_0);
|
||||
test<Map<Ref | null,i8>>(RTTIFlags.MAP | RTTIFlags.ACYCLIC |KEY_ALIGN_REF | RTTIFlags.KEY_NULLABLE | RTTIFlags.KEY_MANAGED | RTTIFlags.VALUE_ALIGN_0);
|
||||
test<Map<i8,Ref>>(RTTIFlags.MAP | RTTIFlags.ACYCLIC | RTTIFlags.KEY_ALIGN_0 | RTTIFlags.VALUE_MANAGED | VALUE_ALIGN_REF);
|
||||
test<Map<i8,Ref | null>>(RTTIFlags.MAP | RTTIFlags.ACYCLIC | RTTIFlags.KEY_ALIGN_0 | RTTIFlags.VALUE_NULLABLE | RTTIFlags.VALUE_MANAGED | VALUE_ALIGN_REF);
|
||||
test<Map<Ref | null,Ref | null>>(RTTIFlags.MAP | RTTIFlags.ACYCLIC | RTTIFlags.KEY_NULLABLE | RTTIFlags.KEY_MANAGED | KEY_ALIGN_REF | RTTIFlags.VALUE_NULLABLE | RTTIFlags.VALUE_MANAGED | VALUE_ALIGN_REF);
|
||||
test<Map<v128,i8>>(TypeinfoFlags.MAP | TypeinfoFlags.ACYCLIC | TypeinfoFlags.KEY_ALIGN_4 | TypeinfoFlags.VALUE_ALIGN_0);
|
||||
test<Map<i64,i16>>(TypeinfoFlags.MAP | TypeinfoFlags.ACYCLIC | TypeinfoFlags.KEY_ALIGN_3 | TypeinfoFlags.VALUE_ALIGN_1);
|
||||
test<Map<i32,i32>>(TypeinfoFlags.MAP | TypeinfoFlags.ACYCLIC | TypeinfoFlags.KEY_ALIGN_2 | TypeinfoFlags.VALUE_ALIGN_2);
|
||||
test<Map<i16,i64>>(TypeinfoFlags.MAP | TypeinfoFlags.ACYCLIC | TypeinfoFlags.KEY_ALIGN_1 | TypeinfoFlags.VALUE_ALIGN_3);
|
||||
test<Map<i8,v128>>(TypeinfoFlags.MAP | TypeinfoFlags.ACYCLIC | TypeinfoFlags.KEY_ALIGN_0 | TypeinfoFlags.VALUE_ALIGN_4);
|
||||
test<Map<Ref,i8>>(TypeinfoFlags.MAP | TypeinfoFlags.ACYCLIC | KEY_ALIGN_REF | TypeinfoFlags.KEY_MANAGED | TypeinfoFlags.VALUE_ALIGN_0);
|
||||
test<Map<Ref | null,i8>>(TypeinfoFlags.MAP | TypeinfoFlags.ACYCLIC |KEY_ALIGN_REF | TypeinfoFlags.KEY_NULLABLE | TypeinfoFlags.KEY_MANAGED | TypeinfoFlags.VALUE_ALIGN_0);
|
||||
test<Map<i8,Ref>>(TypeinfoFlags.MAP | TypeinfoFlags.ACYCLIC | TypeinfoFlags.KEY_ALIGN_0 | TypeinfoFlags.VALUE_MANAGED | VALUE_ALIGN_REF);
|
||||
test<Map<i8,Ref | null>>(TypeinfoFlags.MAP | TypeinfoFlags.ACYCLIC | TypeinfoFlags.KEY_ALIGN_0 | TypeinfoFlags.VALUE_NULLABLE | TypeinfoFlags.VALUE_MANAGED | VALUE_ALIGN_REF);
|
||||
test<Map<Ref | null,Ref | null>>(TypeinfoFlags.MAP | TypeinfoFlags.ACYCLIC | TypeinfoFlags.KEY_NULLABLE | TypeinfoFlags.KEY_MANAGED | KEY_ALIGN_REF | TypeinfoFlags.VALUE_NULLABLE | TypeinfoFlags.VALUE_MANAGED | VALUE_ALIGN_REF);
|
||||
|
||||
// cycle detection
|
||||
|
||||
@ -51,13 +50,13 @@ class NoCycle {
|
||||
a: i32;
|
||||
}
|
||||
|
||||
test<NoCycle>(RTTIFlags.ACYCLIC);
|
||||
test<NoCycle>(TypeinfoFlags.ACYCLIC);
|
||||
|
||||
class DirectCycle {
|
||||
a: DirectCycle;
|
||||
}
|
||||
|
||||
test<DirectCycle>(RTTIFlags.NONE);
|
||||
test<DirectCycle>(TypeinfoFlags.NONE);
|
||||
|
||||
class IndirectCycle {
|
||||
a: IndirectCycleBack;
|
||||
@ -66,7 +65,7 @@ class IndirectCycleBack {
|
||||
a: IndirectCycle;
|
||||
}
|
||||
|
||||
test<IndirectCycle>(RTTIFlags.NONE);
|
||||
test<IndirectCycle>(TypeinfoFlags.NONE);
|
||||
|
||||
// array
|
||||
|
||||
@ -74,13 +73,13 @@ class IndirectCycleArray {
|
||||
a: Array<IndirectCycleArray>;
|
||||
}
|
||||
|
||||
test<IndirectCycleArray>(RTTIFlags.NONE);
|
||||
test<IndirectCycleArray>(TypeinfoFlags.NONE);
|
||||
|
||||
class InnerCycleArray {
|
||||
a: IndirectCycleArray;
|
||||
}
|
||||
|
||||
test<InnerCycleArray>(RTTIFlags.ACYCLIC);
|
||||
test<InnerCycleArray>(TypeinfoFlags.ACYCLIC);
|
||||
|
||||
// set
|
||||
|
||||
@ -88,13 +87,13 @@ class IndirectCycleSet {
|
||||
a: Set<IndirectCycleSet>;
|
||||
}
|
||||
|
||||
test<IndirectCycleSet>(RTTIFlags.NONE);
|
||||
test<IndirectCycleSet>(TypeinfoFlags.NONE);
|
||||
|
||||
class InnerCycleSet {
|
||||
a: IndirectCycleSet;
|
||||
}
|
||||
|
||||
test<InnerCycleSet>(RTTIFlags.ACYCLIC);
|
||||
test<InnerCycleSet>(TypeinfoFlags.ACYCLIC);
|
||||
|
||||
// map
|
||||
|
||||
@ -102,22 +101,22 @@ class IndirectCycleMapKey {
|
||||
a: Map<IndirectCycleMapKey,i32>;
|
||||
}
|
||||
|
||||
test<IndirectCycleMapKey>(RTTIFlags.NONE);
|
||||
test<IndirectCycleMapKey>(TypeinfoFlags.NONE);
|
||||
|
||||
class IndirectCycleMapValue {
|
||||
a: Map<i32,IndirectCycleMapValue>;
|
||||
}
|
||||
|
||||
test<IndirectCycleMapValue>(RTTIFlags.NONE);
|
||||
test<IndirectCycleMapValue>(TypeinfoFlags.NONE);
|
||||
|
||||
class InnerCycleMapKey {
|
||||
a: IndirectCycleMapKey;
|
||||
}
|
||||
|
||||
test<InnerCycleMapKey>(RTTIFlags.ACYCLIC);
|
||||
test<InnerCycleMapKey>(TypeinfoFlags.ACYCLIC);
|
||||
|
||||
class InnerCycleMapValue {
|
||||
a: IndirectCycleMapValue;
|
||||
}
|
||||
|
||||
test<InnerCycleMapValue>(RTTIFlags.ACYCLIC);
|
||||
test<InnerCycleMapValue>(TypeinfoFlags.ACYCLIC);
|
||||
|
File diff suppressed because it is too large
Load Diff
5
tests/compiler/rt/ids.json
Normal file
5
tests/compiler/rt/ids.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"asc_flags": [
|
||||
"--runtime none"
|
||||
]
|
||||
}
|
9
tests/compiler/rt/ids.optimized.wat
Normal file
9
tests/compiler/rt/ids.optimized.wat
Normal file
@ -0,0 +1,9 @@
|
||||
(module
|
||||
(type $FUNCSIG$v (func))
|
||||
(memory $0 1)
|
||||
(data (i32.const 8) "\12\00\00\00\01\00\00\00\01\00\00\00\12\00\00\00r\00t\00/\00i\00d\00s\00.\00t\00s")
|
||||
(export "memory" (memory $0))
|
||||
(func $start (; 0 ;) (type $FUNCSIG$v)
|
||||
nop
|
||||
)
|
||||
)
|
2
tests/compiler/rt/ids.ts
Normal file
2
tests/compiler/rt/ids.ts
Normal file
@ -0,0 +1,2 @@
|
||||
assert(idof<ArrayBuffer>() == 0);
|
||||
assert(idof<String>() == 1);
|
42
tests/compiler/rt/ids.untouched.wat
Normal file
42
tests/compiler/rt/ids.untouched.wat
Normal file
@ -0,0 +1,42 @@
|
||||
(module
|
||||
(type $FUNCSIG$viiii (func (param i32 i32 i32 i32)))
|
||||
(type $FUNCSIG$v (func))
|
||||
(import "env" "abort" (func $~lib/builtins/abort (param i32 i32 i32 i32)))
|
||||
(memory $0 1)
|
||||
(data (i32.const 8) "\12\00\00\00\01\00\00\00\01\00\00\00\12\00\00\00r\00t\00/\00i\00d\00s\00.\00t\00s\00")
|
||||
(table $0 1 funcref)
|
||||
(elem (i32.const 0) $null)
|
||||
(export "memory" (memory $0))
|
||||
(start $start)
|
||||
(func $start:rt/ids (; 1 ;) (type $FUNCSIG$v)
|
||||
i32.const 0
|
||||
i32.const 0
|
||||
i32.eq
|
||||
i32.eqz
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 24
|
||||
i32.const 1
|
||||
i32.const 0
|
||||
call $~lib/builtins/abort
|
||||
unreachable
|
||||
end
|
||||
i32.const 1
|
||||
i32.const 1
|
||||
i32.eq
|
||||
i32.eqz
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 24
|
||||
i32.const 2
|
||||
i32.const 0
|
||||
call $~lib/builtins/abort
|
||||
unreachable
|
||||
end
|
||||
)
|
||||
(func $start (; 2 ;) (type $FUNCSIG$v)
|
||||
call $start:rt/ids
|
||||
)
|
||||
(func $null (; 3 ;) (type $FUNCSIG$v)
|
||||
)
|
||||
)
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user