layout rtti with fixed ids for buffers and strings

This commit is contained in:
dcode 2019-05-22 00:14:44 +02:00
parent c92ca0d8cb
commit d94b4fca50
27 changed files with 7579 additions and 9975 deletions

View File

@ -3,7 +3,7 @@
[![Build Status](https://travis-ci.org/AssemblyScript/assemblyscript.svg?branch=master)](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)!

View File

@ -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;

View File

@ -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";

View File

@ -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];

View File

@ -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. */

View File

@ -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). */

View File

@ -1 +0,0 @@
These source files are used by both the standard library *and* compiler. Must remain portable.

View File

@ -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. */

View File

@ -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

View File

@ -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;
}

View File

@ -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.

View File

@ -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);

View File

@ -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. */

View File

@ -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. */

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,5 @@
{
"asc_flags": [
"--runtime none"
]
}

View 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
View File

@ -0,0 +1,2 @@
assert(idof<ArrayBuffer>() == 0);
assert(idof<String>() == 1);

View 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