From a6896d7bc2746fd84468f8fc611a0ca06ae605aa Mon Sep 17 00:00:00 2001 From: dcode Date: Fri, 24 May 2019 20:31:52 +0200 Subject: [PATCH] more loader updates --- lib/loader/README.md | 35 ++--- lib/loader/index.js | 193 +++++++++++++------------- lib/loader/tests/assembly/index.ts | 2 + lib/loader/tests/build/untouched.wasm | Bin 6404 -> 8182 bytes lib/loader/tests/index.js | 18 +-- src/builtins.ts | 14 +- src/common.ts | 11 ++ src/program.ts | 66 ++++++++- std/assembly/rt/index-full.ts | 4 +- std/assembly/rt/index-stub.ts | 4 +- tests/compiler/rt/ids.ts | 3 + tests/compiler/rt/ids.untouched.wat | 16 ++- 12 files changed, 225 insertions(+), 141 deletions(-) diff --git a/lib/loader/README.md b/lib/loader/README.md index 7bc145af..87694830 100644 --- a/lib/loader/README.md +++ b/lib/loader/README.md @@ -61,32 +61,19 @@ Instances are automatically populated with useful utility: A 64-bit float view on the memory. * **newString**(str: `string`): `number`
- Allocates a new string in the module's memory and returns its pointer.2 + Allocates a new string in the module's memory and returns its retained pointer. When done with the string, make sure to `Module#__release` it. * **getString**(ptr: `number`): `string`
- Gets a string from the module's memory by its pointer. + Reads a string from the module's memory by its pointer. -* **newArray**(view: `TypedArray`, length?: `number`, unsafe?: `boolean`): `number`
- Copies a typed array into the module's memory and returns its pointer.2 +* **newArray**(id: `number`, values: `number[]`): `number`
+ Allocates a new array in the module's memory and returns its retained pointer. + The `id` is the unique runtime id of the respective array class. If you are using `Int32Array` for example, the best way to know the relevant value is an `export const INT32ARRAY_ID = idof()`. When done with the array, make sure to `Module#__release` it. -* **newArray**(ctor: `TypedArrayConstructor`, length: `number`, unsafe?: `boolean`): `number`
- Creates a typed array in the module's memory and returns its pointer.2 +* **getArray**(ptr: `number`): `number[]`
+ Gets the values of an array in the module's memory by its pointer. -* **getArray**(ctor: `TypedArrayConstructor`, ptr: `number`): `TypedArray`
- Gets a view on a typed array in the module's memory by its pointer. - -* **freeArray**(ptr: `number`): `void`
- Frees a typed array in the module's memory. Must not be accessed anymore afterwards. - -* **getFunction**(ptr: `number`): `function`
- Gets a function by its pointer. - -* **newFunction**(fn: `function`): `number`
- Creates a new function in the module's table and returns its pointer. Note that only actual - WebAssembly functions, i.e. as exported by the module, are supported. - -1 This feature has not yet landed in any VM as of this writing.
-2 Requires that memory utilities have been exported through `export { memory }` within the entry file. +1 This feature has not yet landed in any VM as of this writing. Examples -------- @@ -141,7 +128,11 @@ Strings and arrays cannot yet flow in and out of WebAssembly naturally, hence it var str = "Hello world!"; var ptr = module.newString(str); -// ... do something with ptr, i.e. call a WebAssembly export ... +// do something with ptr, i.e. call a WebAssembly export +... + +// when done, allow the runtime to collect it +module.__release(ptr); ``` Similarly, when a string or array is returned from a WebAssembly function, a reference (pointer) is received on the JS side and the `getString` and `getArray` helpers can be used to obtain their values: diff --git a/lib/loader/index.js b/lib/loader/index.js index b46473ee..6b068902 100644 --- a/lib/loader/index.js +++ b/lib/loader/index.js @@ -1,18 +1,32 @@ "use strict"; -/** Relative header `id` offset. */ +// Runtime header offsets const ID_OFFSET = -8; -/** Relative header `size` offset. */ const SIZE_OFFSET = -4; -/** Unique runtime id of an `ArrayBuffer`. */ +// Runtime ids const ARRAYBUFFER_ID = 0; -/** Unique runtime id of a `String`. */ const STRING_ID = 1; +const ARRAYBUFFERVIEW_ID = 2; -/** Whether BigInt arrays are supported. */ -const SUPPORTS_BIGINT = typeof BigUint64Array !== "undefined"; -/** Unique symbol for memoized 'this'. */ +// Runtime type information +const ARRAY = 1 << 0; +const SET = 1 << 1; +const MAP = 1 << 2; +const VAL_ALIGN = 1 << 4; +const VAL_NULLABLE = 1 << 9; +const VAL_MANAGED = 1 << 10; +const KEY_ALIGN = 1 << 11; +const KEY_NULLABLE = 1 << 16; +const KEY_MANAGED = 1 << 17; + +// ArrayBufferView layout +const ABV_BUFFER_OFFSET = 0; +const ABV_DATASTART_OFFSET = 4; +const ABV_DATALENGTH_OFFSET = 8; +const ABV_SIZE = 12; + +const BIGINT = typeof BigUint64Array !== "undefined"; const THIS = Symbol(); /** Gets a string from an U32 and an U16 view on a memory. */ @@ -61,11 +75,10 @@ function preInstantiate(imports) { function postInstantiate(baseModule, instance) { var rawExports = instance.exports; var memory = rawExports.memory; - var alloc = rawExports["__alloc"]; - var free = rawExports["__free"]; - var setargc = rawExports["__setargc"] || function() {}; - var memory_fill = rawExports["memory.fill"]; var table = rawExports.table; + var alloc = rawExports["__alloc"]; + var retain = rawExports["__retain"]; + var rtti = rawExports["__rtti"] | 0; // Provide views for all sorts of basic values var buffer, I8, U8, I16, U16, I32, U32, F32, F64, I64, U64; @@ -81,7 +94,7 @@ function postInstantiate(baseModule, instance) { U16 = new Uint16Array(buffer); I32 = new Int32Array(buffer); U32 = new Uint32Array(buffer); - if (SUPPORTS_BIGINT) { + if (BIGINT) { I64 = new BigInt64Array(buffer); U64 = new BigUint64Array(buffer); } @@ -91,107 +104,95 @@ function postInstantiate(baseModule, instance) { } checkMem(); - /** Allocates a new string in the module's memory and returns its pointer. */ + /** Gets the runtime type info for the given id. */ + function getInfo(id) { + const count = U32[rtti >>> 2]; + if ((id >>>= 0) >= count) throw Error("invalid id: " + id); + return U32[(rtti + 4 >>> 2) + id * 2]; + } + + /** Gets the runtime base id for the given id. */ + function getBase(id) { + const count = U32[rtti >>> 2]; + if ((id >>>= 0) >= count) throw Error("invalid id: " + id); + return U32[(rtti + 4 >>> 2) + id * 2 + 1]; + } + + /** Gets the runtime alignment of a collection's values or keys. */ + function getAlign(which, info) { + return 31 - Math.clz32((info / which) & 31); + } + + /** Allocates a new string in the module's memory and returns its retained pointer. */ function newString(str) { - var length = str.length; - var ptr = alloc(length << 1, STRING_ID); + const length = str.length; + const ptr = alloc(length << 1, STRING_ID); checkMem(); for (let i = 0, j = ptr >>> 1; i < length; ++i) U16[j + i] = str.charCodeAt(i); - return ptr; + return retain(ptr); } baseModule.newString = newString; - /** Gets a string from the module's memory by its pointer. */ + /** Reads a string from the module's memory by its pointer. */ function getString(ptr) { checkMem(); + const id = U32[ptr + ID_OFFSET >>> 2]; + if (id !== STRING_ID) throw Error("not a string: " + ptr); return getStringImpl(U32, U16, ptr); } baseModule.getString = getString; - // function computeBufferSize(byteLength) { - // const HEADER_SIZE = 8; - // return 1 << (32 - Math.clz32(byteLength + HEADER_SIZE - 1)); - // } + /** Allocates a new array in the module's memory and returns its retained pointer. */ + function newArray(id, values) { + const info = getInfo(id); + if (!(info & ARRAY)) throw Error("not an array: " + id + " @ " + info); + const align = getAlign(VAL_ALIGN, info); + const length = values.length; + const buf = alloc(length << align, ARRAYBUFFER_ID); + const arr = alloc(ABV_SIZE, id); + checkMem(); + U32[arr + ABV_BUFFER_OFFSET >>> 2] = retain(buf); + U32[arr + ABV_DATASTART_OFFSET >>> 2] = buf; + U32[arr + ABV_DATALENGTH_OFFSET >>> 2] = length << align; + var view; + switch (align) { + case 0: view = U8; break; + case 1: view = U16; break; + case 2: view = U32; break; + case 3: view = U64; break; + default: throw Error("unsupported align: " + align); + } + for (let i = 0; i < length; ++i) view[(buf >> align) + i] = values[i]; + if (info & VAL_MANAGED) for (let i = 0; i < length; ++i) retain(values[i]); + return retain(arr); + } - // /** Creates a new typed array in the module's memory and returns its pointer. */ - // function newArray(view, length, unsafe) { - // var ctor = view.constructor; - // if (ctor === Function) { // TypedArray constructor created in memory - // ctor = view; - // view = null; - // } else { // TypedArray instance copied into memory - // if (length === undefined) length = view.length; - // } - // var elementSize = ctor.BYTES_PER_ELEMENT; - // if (!elementSize) throw Error("not a typed array"); - // var byteLength = elementSize * length; - // var ptr = alloc(12); // TypedArray header - // var buf = alloc(computeBufferSize(byteLength)); // ArrayBuffer - // checkMem(); - // U32[ ptr >>> 2] = buf; // .buffer - // U32[(ptr + 4) >>> 2] = 0; // .byteOffset - // U32[(ptr + 8) >>> 2] = byteLength; // .byteLength - // U32[ buf >>> 2] = byteLength; // .byteLength - // U32[(buf + 4) >>> 2] = 0; // 0 - // if (view) { - // new ctor(buffer, buf + 8, length).set(view); - // if (view.length < length && !unsafe) { - // let setLength = elementSize * view.length; - // memory_fill(buf + 8 + setLength, 0, byteLength - setLength); - // } - // } else if (!unsafe) { - // memory_fill(buf + 8, 0, byteLength); - // } - // return ptr; - // } + baseModule.newArray = newArray; - // baseModule.newArray = newArray; + /** Gets the values of an array in the module's memory by its pointer. */ + function getArray(arr) { + checkMem(); + const id = U32[arr + ID_OFFSET >>> 2]; + const info = getInfo(id); + if (!(info & ARRAY)) throw Error("not an array: " + id); + const align = getAlign(VAL_ALIGN, info); + var buf = U32[arr + ABV_DATASTART_OFFSET >>> 2]; + const length = U32[buf + SIZE_OFFSET >>> 2] >>> align; + var view; + switch (align) { + // TODO: signedness/floats + case 0: view = U8; break; + case 1: view = U16; break; + case 2: view = U32; break; + case 3: view = U64; break; + default: throw Error("unsupported align: " + align); + } + return Array.from(view.slice(buf >>>= align, buf + length)); + } - // /** Gets a view on a typed array in the module's memory by its pointer. */ - // function getArray(ctor, ptr) { - // var elementSize = ctor.BYTES_PER_ELEMENT; - // if (!elementSize) throw Error("not a typed array"); - // checkMem(); - // var buf = U32[ ptr >>> 2]; - // var byteOffset = U32[(ptr + 4) >>> 2]; - // var byteLength = U32[(ptr + 8) >>> 2]; - // return new ctor(buffer, buf + 8 + byteOffset, (byteLength - byteOffset) / elementSize); - // } - - // baseModule.getArray = getArray; - - // /** Frees a typed array in the module's memory. Must not be accessed anymore afterwards. */ - // function freeArray(ptr) { - // checkMem(); - // var buf = U32[ptr >>> 2]; - // free(buf); - // free(ptr); - // } - - // baseModule.freeArray = freeArray; - - // /** - // * Creates a new function in the module's table and returns its pointer. Note that only actual - // * WebAssembly functions, i.e. as exported by the module, are supported. - // */ - // function newFunction(fn) { - // if (typeof fn.original === "function") fn = fn.original; - // var index = table.length; - // table.grow(1); - // table.set(index, fn); - // return index; - // } - - // baseModule.newFunction = newFunction; - - // /** Gets a function by its pointer. */ - // function getFunction(ptr) { - // return wrapFunction(table.get(ptr), setargc); - // } - - // baseModule.getFunction = getFunction; + baseModule.getArray = getArray; // Pull basic exports to baseModule so code in preInstantiate can use them baseModule.memory = baseModule.memory || memory; diff --git a/lib/loader/tests/assembly/index.ts b/lib/loader/tests/assembly/index.ts index 770be909..42552342 100644 --- a/lib/loader/tests/assembly/index.ts +++ b/lib/loader/tests/assembly/index.ts @@ -58,3 +58,5 @@ export function calladd(fn: (a: i32, b: i32) => i32, a: i32, b: i32): i32 { export function dotrace(num: f64): void { trace("The answer is", 1, num); } + +export const INT32ARRAY_ID = idof(); diff --git a/lib/loader/tests/build/untouched.wasm b/lib/loader/tests/build/untouched.wasm index 5bdfc74da246b651109ca751671dc3ceff531e3c..1de1f6e217a1fd0b56f45241373953397f466c57 100644 GIT binary patch literal 8182 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 delta 1710 zcmah{No*Tc7=Hho)qZw8O`61UlIBg^l(e+&lGd##kPK-Q8@m4Zf8oe-T8)i5*-jQ9iO!kLZj$y6I0!u~6WbDpv`aWp9uDWDTW zoEy>mB{g;#A1Jmy@6G$cX-bT8*{#?83So(IIq+hAjb1LF3_OomrZk3bwL!Wff8A@l zEsyl!qvF@=UL_=hsdBm6XoYU0;`t}ZP_kSOPdB}4cV>o+C{u2rFbidV)(h`xEY9!u{h&p5N2bHg4rFn}n;gvY)f|{1Fz}1A=$oWC3C*%1|HdwNSi=#{dCuG^72?wyhi&$i1+DM zxF~MY$KWloPq}4Y1t)D0!o-_&9eg1s)c4>g@rT+P{JrDg;8-thlLt1LwrYdI4S(zv;W+6_Gd2LUQR1BMtD8xNHjeN<5HQgzv?NiBI9SIFtMY zz7zMd^>AH0$x8G`Dl$ETOAq%nV$XBUpB zlbmqiij_b~i!{X{!^D@#Y}?kkE&&W~AOOaxj^eai3$hO*R%^D#HLjE*5Z$5(fk6vg z3yM^tMtCdm8OD%{T1ngFrYue^QZ?4hB1M}-j>bf-6n|7=K?H;dmcmUoMe=01kGIuM zAGn)?YMT=$voNjUo|XeHsScGJ$rJQha|n?}e347#9)G#xk`=C|VE3;gc<28J-u*X$ zl@XWN4!9-0W%>NiB(9EiBYQsipd1huVXegpumV6kpAg4Vn}q1y4JmP@HxKK?uf6we zzSGH*bsdP4Y7SWAIF@txd3pi6lMNx+BYh*8zp!iBBPV0jw7SI$eOsYNEcMy!AIe_w zL*E39i{We@$plg9OKaiiw+Sg+;X~ho;lO5ev_7a}7ap&NZ8M+5_xGvM4M=9J;|2hXa!M43VCBTugS+6GU_&FkpQV%) t$3Tio-RT_iIdE=>BSWKXdKL?t3v}+Tpu<~lMq7j_cu5}X$b%T diff --git a/lib/loader/tests/index.js b/lib/loader/tests/index.js index 3b0ce510..9994af99 100644 --- a/lib/loader/tests/index.js +++ b/lib/loader/tests/index.js @@ -33,17 +33,19 @@ assert.strictEqual(module.getString(ptr), str); assert.strictEqual(module.strlen(ptr), str.length); // should be able to allocate a typed array and sum up its values -// var arr = [1, 2, 3, 4, 5, 0x7fffffff]; -// ptr = module.newArray(new Int32Array(arr)); -// assert.strictEqual(module.sum(ptr), arr.reduce((a, b) => a + b, 0) | 0); +var arr = [1, 2, 3, 4, 5, 0x7fffffff]; +ptr = module.newArray(module.INT32ARRAY_ID, arr); +assert.strictEqual(module.sum(ptr), arr.reduce((a, b) => a + b, 0) | 0); // should be able to get a view on an internal typed array -// assert.deepEqual(module.getArray(Int32Array, ptr), arr); +assert.deepEqual(Array.from(module.getArray(ptr)), arr); -// should be able to free and reuse the space of an internal typed array -// module.freeArray(ptr); -// var ptr2 = module.newArray(new Int32Array(arr)); -// assert.strictEqual(ptr, ptr2); +// should be able to release no longer needed references +module.__release(ptr); +try { + module.__release(ptr); + assert(false); +} catch (e) {}; // should be able to just call a function with variable arguments assert.strictEqual(module.varadd(), 3); diff --git a/src/builtins.ts b/src/builtins.ts index de465b07..348f6326 100644 --- a/src/builtins.ts +++ b/src/builtins.ts @@ -4160,7 +4160,8 @@ export function compileRTTI(compiler: Compiler): void { var data = new Uint8Array(size); writeI32(count, data, 0); var off = 4; - var arrayPrototype = program.arrayPrototype; + var abvInstance = program.arrayBufferViewInstance; + var abvPrototype = abvInstance.prototype; var setPrototype = program.setPrototype; var mapPrototype = program.mapPrototype; var lastId = 0; @@ -4168,17 +4169,16 @@ export function compileRTTI(compiler: Compiler): void { 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); + if (instance !== abvInstance && instance.extends(abvPrototype)) { + let valueType = instance.getArrayValueType(); flags |= TypeinfoFlags.ARRAY; - flags |= TypeinfoFlags.VALUE_ALIGN_0 * typeToRuntimeFlags(typeArguments[0]); - } else if (instance.prototype.extends(setPrototype)) { + flags |= TypeinfoFlags.VALUE_ALIGN_0 * typeToRuntimeFlags(valueType); + } else if (instance.extends(setPrototype)) { let typeArguments = assert(instance.getTypeArgumentsTo(setPrototype)); assert(typeArguments.length == 1); flags |= TypeinfoFlags.SET; flags |= TypeinfoFlags.VALUE_ALIGN_0 * typeToRuntimeFlags(typeArguments[0]); - } else if (instance.prototype.extends(mapPrototype)) { + } else if (instance.extends(mapPrototype)) { let typeArguments = assert(instance.getTypeArgumentsTo(mapPrototype)); assert(typeArguments.length == 2); flags |= TypeinfoFlags.MAP; diff --git a/src/common.ts b/src/common.ts index e36680ec..336e4a54 100644 --- a/src/common.ts +++ b/src/common.ts @@ -178,6 +178,17 @@ export namespace CommonSymbols { export const ArrayBuffer = "ArrayBuffer"; export const Math = "Math"; export const Mathf = "Mathf"; + export const Int8Array = "Int8Array"; + export const Int16Array = "Int16Array"; + export const Int32Array = "Int32Array"; + export const Int64Array = "Int64Array"; + export const Uint8Array = "Uint8Array"; + export const Uint8ClampedArray = "Uint8ClampedArray"; + export const Uint16Array = "Uint16Array"; + export const Uint32Array = "Uint32Array"; + export const Uint64Array = "Uint64Array"; + export const Float32Array = "Float32Array"; + export const Float64Array = "Float64Array"; // runtime export const abort = "abort"; export const pow = "pow"; diff --git a/src/program.ts b/src/program.ts index 56a9730e..3269b448 100644 --- a/src/program.ts +++ b/src/program.ts @@ -356,6 +356,28 @@ export class Program extends DiagnosticEmitter { mapPrototype: ClassPrototype; /** Fixed array prototype reference. */ fixedArrayPrototype: ClassPrototype; + /** Int8Array prototype. */ + i8ArrayPrototype: ClassPrototype; + /** Int16Array prototype. */ + i16ArrayPrototype: ClassPrototype; + /** Int32Array prototype. */ + i32ArrayPrototype: ClassPrototype; + /** Int64Array prototype. */ + i64ArrayPrototype: ClassPrototype; + /** Uint8Array prototype. */ + u8ArrayPrototype: ClassPrototype; + /** Uint8ClampedArray prototype. */ + u8ClampedArrayPrototype: ClassPrototype; + /** Uint16Array prototype. */ + u16ArrayPrototype: ClassPrototype; + /** Uint32Array prototype. */ + u32ArrayPrototype: ClassPrototype; + /** Uint64Array prototype. */ + u64ArrayPrototype: ClassPrototype; + /** Float32Array prototype. */ + f32ArrayPrototype: ClassPrototype; + /** Float64Array prototype. */ + f64ArrayPrototype: ClassPrototype; /** String instance reference. */ stringInstance: Class; /** Abort function reference, if present. */ @@ -734,12 +756,14 @@ export class Program extends DiagnosticEmitter { } } - // register ArrayBuffer (id=0) and String (id=1) + // register ArrayBuffer (id=0), String (id=1), ArrayBufferView (id=2) 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); + this.arrayBufferViewInstance = this.requireClass(CommonSymbols.ArrayBufferView); + assert(this.arrayBufferViewInstance.id == 2); // register classes backing basic types this.registerNativeTypeClass(TypeKind.I8, CommonSymbols.I8); @@ -757,6 +781,19 @@ export class Program extends DiagnosticEmitter { this.registerNativeTypeClass(TypeKind.F64, CommonSymbols.F64); if (options.hasFeature(Feature.SIMD)) this.registerNativeTypeClass(TypeKind.V128, CommonSymbols.V128); + // register views but don't instantiate them yet + this.i8ArrayPrototype = this.require(CommonSymbols.Int8Array, ElementKind.CLASS_PROTOTYPE); + this.i16ArrayPrototype = this.require(CommonSymbols.Int16Array, ElementKind.CLASS_PROTOTYPE); + this.i32ArrayPrototype = this.require(CommonSymbols.Int32Array, ElementKind.CLASS_PROTOTYPE); + this.i64ArrayPrototype = this.require(CommonSymbols.Int64Array, ElementKind.CLASS_PROTOTYPE); + this.u8ArrayPrototype = this.require(CommonSymbols.Uint8Array, ElementKind.CLASS_PROTOTYPE); + this.u8ClampedArrayPrototype = this.require(CommonSymbols.Uint8ClampedArray, ElementKind.CLASS_PROTOTYPE); + this.u16ArrayPrototype = this.require(CommonSymbols.Uint16Array, ElementKind.CLASS_PROTOTYPE); + this.u32ArrayPrototype = this.require(CommonSymbols.Uint32Array, ElementKind.CLASS_PROTOTYPE); + this.u64ArrayPrototype = this.require(CommonSymbols.Uint64Array, ElementKind.CLASS_PROTOTYPE); + this.f32ArrayPrototype = this.require(CommonSymbols.Float32Array, ElementKind.CLASS_PROTOTYPE); + this.f64ArrayPrototype = this.require(CommonSymbols.Float64Array, ElementKind.CLASS_PROTOTYPE); + // resolve base prototypes of derived classes var resolver = this.resolver; for (let i = 0, k = queuedExtends.length; i < k; ++i) { @@ -813,7 +850,6 @@ export class Program extends DiagnosticEmitter { } // register stdlib components - this.arrayBufferViewInstance = this.requireClass(CommonSymbols.ArrayBufferView); this.arrayPrototype = this.require(CommonSymbols.Array, ElementKind.CLASS_PROTOTYPE); this.fixedArrayPrototype = this.require(CommonSymbols.FixedArray, ElementKind.CLASS_PROTOTYPE); this.setPrototype = this.require(CommonSymbols.Set, ElementKind.CLASS_PROTOTYPE); @@ -3200,6 +3236,32 @@ export class Class extends TypedElement { return null; } + /** Gets the value type of an array. Must be an array. */ + getArrayValueType(): Type { + var current: Class = this; + var program = this.program; + var abvInstance = program.arrayBufferViewInstance; + while (current.base !== abvInstance) { + current = assert(current.base); + } + switch (current.prototype) { + case program.i8ArrayPrototype: return Type.i8; + case program.i16ArrayPrototype: return Type.i16; + case program.i32ArrayPrototype: return Type.i32; + case program.i64ArrayPrototype: return Type.i64; + case program.u8ArrayPrototype: + case program.u8ClampedArrayPrototype: return Type.u8; + case program.u16ArrayPrototype: return Type.u16; + case program.u32ArrayPrototype: return Type.u32; + case program.u64ArrayPrototype: return Type.u64; + case program.f32ArrayPrototype: return Type.f32; + case program.f64ArrayPrototype: return Type.f64; + case program.arrayPrototype: return assert(this.getTypeArgumentsTo(program.arrayPrototype))[0]; + default: assert(false); + } + return Type.void; + } + /** Tests if this class is inherently acyclic. */ get isAcyclic(): bool { var acyclic = this._acyclic; diff --git a/std/assembly/rt/index-full.ts b/std/assembly/rt/index-full.ts index 16acb279..ceed8e44 100644 --- a/std/assembly/rt/index-full.ts +++ b/std/assembly/rt/index-full.ts @@ -1,3 +1,3 @@ -export { __alloc, __realloc, __free } from "rt/tlsf"; +export { __alloc } from "rt/tlsf"; export { __retain, __release, __collect } from "rt/pure"; -export { __instanceof, __typeinfo } from "rt"; +export { RTTI_BASE as __rtti } from "rt"; diff --git a/std/assembly/rt/index-stub.ts b/std/assembly/rt/index-stub.ts index 01fffcb8..b6956efa 100644 --- a/std/assembly/rt/index-stub.ts +++ b/std/assembly/rt/index-stub.ts @@ -1,2 +1,2 @@ -export { __alloc, __realloc, __free, __retain, __release, __collect } from "rt/stub"; -export { __instanceof, __typeinfo } from "rt"; +export { __alloc, __retain, __release, __collect } from "rt/stub"; +export { RTTI_BASE as __rtti } from "rt"; diff --git a/tests/compiler/rt/ids.ts b/tests/compiler/rt/ids.ts index ccc5caac..962bbb2d 100644 --- a/tests/compiler/rt/ids.ts +++ b/tests/compiler/rt/ids.ts @@ -1,2 +1,5 @@ +import { ArrayBufferView } from "arraybuffer"; + assert(idof() == 0); assert(idof() == 1); +assert(idof() == 2); diff --git a/tests/compiler/rt/ids.untouched.wat b/tests/compiler/rt/ids.untouched.wat index 30a6e7e7..aa86317b 100644 --- a/tests/compiler/rt/ids.untouched.wat +++ b/tests/compiler/rt/ids.untouched.wat @@ -16,7 +16,7 @@ if i32.const 0 i32.const 24 - i32.const 1 + i32.const 3 i32.const 0 call $~lib/builtins/abort unreachable @@ -28,7 +28,19 @@ if i32.const 0 i32.const 24 - i32.const 2 + i32.const 4 + i32.const 0 + call $~lib/builtins/abort + unreachable + end + i32.const 2 + i32.const 2 + i32.eq + i32.eqz + if + i32.const 0 + i32.const 24 + i32.const 5 i32.const 0 call $~lib/builtins/abort unreachable