Skip to content

Support StaticArray in loader #1197

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Mar 28, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 55 additions & 45 deletions lib/loader/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,21 @@ const ARRAYBUFFERVIEW_ID = 2;
// Runtime type information
const ARRAYBUFFERVIEW = 1 << 0;
const ARRAY = 1 << 1;
const SET = 1 << 2;
const MAP = 1 << 3;
const VAL_ALIGN_OFFSET = 5;
const STATICARRAY = 1 << 2;
const SET = 1 << 3;
const MAP = 1 << 4;
const VAL_ALIGN_OFFSET = 6;
const VAL_ALIGN = 1 << VAL_ALIGN_OFFSET;
const VAL_SIGNED = 1 << 10;
const VAL_FLOAT = 1 << 11;
const VAL_NULLABLE = 1 << 12;
const VAL_MANAGED = 1 << 13;
const KEY_ALIGN_OFFSET = 14;
const VAL_SIGNED = 1 << 11;
const VAL_FLOAT = 1 << 12;
const VAL_NULLABLE = 1 << 13;
const VAL_MANAGED = 1 << 14;
const KEY_ALIGN_OFFSET = 15;
const KEY_ALIGN = 1 << KEY_ALIGN_OFFSET;
const KEY_SIGNED = 1 << 19;
const KEY_FLOAT = 1 << 20;
const KEY_NULLABLE = 1 << 21;
const KEY_MANAGED = 1 << 22;
const KEY_SIGNED = 1 << 20;
const KEY_FLOAT = 1 << 21;
const KEY_NULLABLE = 1 << 22;
const KEY_MANAGED = 1 << 23;

// Array(BufferView) layout
const ARRAYBUFFERVIEW_BUFFER_OFFSET = 0;
Expand Down Expand Up @@ -162,23 +163,29 @@ function postInstantiate(extendedExports, instance) {
/** Allocates a new array in the module's memory and returns its retained pointer. */
function __allocArray(id, values) {
const info = getInfo(id);
if (!(info & (ARRAYBUFFERVIEW | ARRAY))) throw Error("not an array: " + id + ", flags= " + info);
if (!(info & (ARRAYBUFFERVIEW | ARRAY | STATICARRAY))) throw Error("not an array: " + id + ", flags= " + info);
const align = getValueAlign(info);
const length = values.length;
const buf = alloc(length << align, ARRAYBUFFER_ID);
const arr = alloc(info & ARRAY ? ARRAY_SIZE : ARRAYBUFFERVIEW_SIZE, id);
const U32 = new Uint32Array(memory.buffer);
U32[arr + ARRAYBUFFERVIEW_BUFFER_OFFSET >>> 2] = retain(buf);
U32[arr + ARRAYBUFFERVIEW_DATASTART_OFFSET >>> 2] = buf;
U32[arr + ARRAYBUFFERVIEW_DATALENGTH_OFFSET >>> 2] = length << align;
if (info & ARRAY) U32[arr + ARRAY_LENGTH_OFFSET >>> 2] = length;
const buf = alloc(length << align, info & STATICARRAY ? id : ARRAYBUFFER_ID);
let result;
if (info & STATICARRAY) {
result = buf;
} else {
const arr = alloc(info & ARRAY ? ARRAY_SIZE : ARRAYBUFFERVIEW_SIZE, id);
const U32 = new Uint32Array(memory.buffer);
U32[arr + ARRAYBUFFERVIEW_BUFFER_OFFSET >>> 2] = retain(buf);
U32[arr + ARRAYBUFFERVIEW_DATASTART_OFFSET >>> 2] = buf;
U32[arr + ARRAYBUFFERVIEW_DATALENGTH_OFFSET >>> 2] = length << align;
if (info & ARRAY) U32[arr + ARRAY_LENGTH_OFFSET >>> 2] = length;
result = arr;
}
const view = getView(align, info & VAL_SIGNED, info & VAL_FLOAT);
if (info & VAL_MANAGED) {
for (let i = 0; i < length; ++i) view[(buf >>> align) + i] = retain(values[i]);
} else {
view.set(values, buf >>> align);
}
return arr;
return result;
}

extendedExports.__allocArray = __allocArray;
Expand All @@ -188,9 +195,11 @@ function postInstantiate(extendedExports, instance) {
const U32 = new Uint32Array(memory.buffer);
const id = U32[arr + ID_OFFSET >>> 2];
const info = getInfo(id);
if (!(info & (ARRAYBUFFERVIEW | ARRAY))) throw Error("not an array: " + id + ", flags=" + info);
if (!(info & (ARRAYBUFFERVIEW | ARRAY | STATICARRAY))) throw Error("not an array: " + id + ", flags=" + info);
const align = getValueAlign(info);
let buf = U32[arr + ARRAYBUFFERVIEW_DATASTART_OFFSET >>> 2];
let buf = info & STATICARRAY
? arr
: U32[arr + ARRAYBUFFERVIEW_DATASTART_OFFSET >>> 2];
const length = info & ARRAY
? U32[arr + ARRAY_LENGTH_OFFSET >>> 2]
: U32[buf + SIZE_OFFSET >>> 2] >>> align;
Expand Down Expand Up @@ -233,30 +242,31 @@ function postInstantiate(extendedExports, instance) {
return new Type(buffer, bufPtr, U32[bufPtr + SIZE_OFFSET >>> 2] >>> alignLog2);
}

extendedExports.__getInt8Array = getTypedArray.bind(null, Int8Array, 0);
extendedExports.__getInt8ArrayView = getTypedArrayView.bind(null, Int8Array, 0);
extendedExports.__getUint8Array = getTypedArray.bind(null, Uint8Array, 0);
extendedExports.__getUint8ArrayView = getTypedArrayView.bind(null, Uint8Array, 0);
extendedExports.__getUint8ClampedArray = getTypedArray.bind(null, Uint8ClampedArray, 0);
extendedExports.__getUint8ClampedArrayView = getTypedArrayView.bind(null, Uint8ClampedArray, 0);
extendedExports.__getInt16Array = getTypedArray.bind(null, Int16Array, 1);
extendedExports.__getInt16ArrayView = getTypedArrayView.bind(null, Int16Array, 1);
extendedExports.__getUint16Array = getTypedArray.bind(null, Uint16Array, 1);
extendedExports.__getUint16ArrayView = getTypedArrayView.bind(null, Uint16Array, 1);
extendedExports.__getInt32Array = getTypedArray.bind(null, Int32Array, 2);
extendedExports.__getInt32ArrayView = getTypedArrayView.bind(null, Int32Array, 2);
extendedExports.__getUint32Array = getTypedArray.bind(null, Uint32Array, 2);
extendedExports.__getUint32ArrayView = getTypedArrayView.bind(null, Uint32Array, 2);
/** Attach a set of get TypedArray and View functions to the exports. */
function attachTypedArrayFunctions(ctor, name, align) {
extendedExports["__get" + name] = getTypedArray.bind(null, ctor, align);
extendedExports["__get" + name + "View"] = getTypedArrayView.bind(null, ctor, align);
}

[
Int8Array,
Uint8Array,
Uint8ClampedArray,
Int16Array,
Uint16Array,
Int32Array,
Uint32Array,
Float32Array,
Float64Array
].forEach(ctor => {
attachTypedArrayFunctions(ctor, ctor.name, 31 - Math.clz32(ctor.BYTES_PER_ELEMENT));
});

if (BIGINT) {
extendedExports.__getInt64Array = getTypedArray.bind(null, BigInt64Array, 3);
extendedExports.__getInt64ArrayView = getTypedArrayView.bind(null, BigInt64Array, 3);
extendedExports.__getUint64Array = getTypedArray.bind(null, BigUint64Array, 3);
extendedExports.__getUint64ArrayView = getTypedArrayView.bind(null, BigUint64Array, 3);
[BigUint64Array, BigInt64Array].forEach(ctor => {
attachTypedArrayFunctions(ctor, ctor.name.slice(3), 3);
});
}
extendedExports.__getFloat32Array = getTypedArray.bind(null, Float32Array, 2);
extendedExports.__getFloat32ArrayView = getTypedArrayView.bind(null, Float32Array, 2);
extendedExports.__getFloat64Array = getTypedArray.bind(null, Float64Array, 3);
extendedExports.__getFloat64ArrayView = getTypedArrayView.bind(null, Float64Array, 3);

/** Tests whether an object is an instance of the class represented by the specified base id. */
function __instanceof(ptr, baseId) {
Expand Down
11 changes: 11 additions & 0 deletions lib/loader/tests/assembly/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,12 @@ export function sum(arr: Int32Array): i32 {
return v;
}

export function sumStatic(arr: StaticArray<i32>): i32 {
var v = 0;
for (let i = 0, k = arr.length; i < k; ++i) v += arr[i];
return v;
}

export function changeLength(arr: Array<i32>, length: i32): void {
arr.length = length;
}
Expand All @@ -70,6 +76,11 @@ export const INT32ARRAY_ID = idof<Int32Array>();
export const UINT32ARRAY_ID = idof<Uint32Array>();
export const FLOAT32ARRAY_ID = idof<Float32Array>();
export const ARRAYI32_ID = idof<Array<i32>>();
export const STATICARRAYI32_ID = idof<StaticArray<i32>>();
export const STATICARRAYU32_ID = idof<StaticArray<u32>>();
export const STATICARRAYU8_ID = idof<StaticArray<u8>>();
export const STATICARRAYI16_ID = idof<StaticArray<i16>>();
export const STATICARRAYF32_ID = idof<StaticArray<f32>>();

export function newFloat32Array(size: i32): Float32Array {
return new Float32Array(size);
Expand Down
6 changes: 6 additions & 0 deletions lib/loader/tests/assembly/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"extends": "../../../../std/assembly.json",
"include": [
"./**/*.ts"
]
}
84 changes: 76 additions & 8 deletions lib/loader/tests/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,26 @@ function test(file) {
try { exports.__release(ref); assert(false); } catch (e) {};
}

// should be able to allocate a typed array
{
var arr = [1, 2, 3, 4, 5, 0x80000000 | 0];
let ref = exports.__retain(exports.__allocArray(exports.STATICARRAYI32_ID, arr));
assert(exports.__instanceof(ref, exports.STATICARRAYI32_ID));

// should be able to get the values of an array
assert.deepEqual(exports.__getArray(ref), arr);

// should be able to get a view on an array
assert.deepEqual(exports.__getArrayView(ref), new Int32Array(arr));

// should be able to sum up its values
assert.strictEqual(exports.sumStatic(ref), arr.reduce((a, b) => (a + b) | 0, 0) | 0);

// should be able to release no longer needed references
exports.__release(ref);
try { exports.__release(ref); assert(false); } catch (e) {};
}

/*
{
let arrU8Arr = new Uint8Array([0, 1, 2]);
Expand Down Expand Up @@ -80,43 +100,91 @@ function test(file) {

// should be able to distinguish between signed and unsigned
{
let arr = new Uint8Array([0, 255, 127]);
let values = [0, 255, 127];
let arr = new Uint8Array(values);
let ref = exports.__retain(exports.__allocArray(exports.UINT8ARRAY_ID, arr));
assert(exports.__instanceof(ref, exports.UINT8ARRAY_ID));
assert.deepEqual(exports.__getUint8Array(ref), arr);
assert.deepEqual(exports.__getUint8ArrayView(ref), arr);
assert.deepEqual(exports.__getArray(ref), values);
exports.__release(ref);
try { exports.__release(ref); assert(false); } catch (e) {};
}

// should be able to distinguish between signed and unsigned for static array layout
{
let arr = [0, 255, 127];
let ref = exports.__retain(exports.__allocArray(exports.STATICARRAYU8_ID, arr));
assert(exports.__instanceof(ref, exports.STATICARRAYU8_ID));
assert.deepEqual(exports.__getArray(ref), arr);
exports.__release(ref);
try { exports.__release(ref); assert(false); } catch (e) {};
}

// should be able to distinguish between signed and unsigned
{
let arr = new Int16Array([0, 0xFFFF, -0x00FF]);
let values = [0, 0xFFFF, -0x00FF];
let arr = new Int16Array(values);
let ref = exports.__retain(exports.__allocArray(exports.INT16ARRAY_ID, arr));
assert(exports.__instanceof(ref, exports.INT16ARRAY_ID));
assert.deepEqual(exports.__getInt16Array(ref), arr);
assert.deepEqual(exports.__getInt16ArrayView(ref), arr);
assert.deepEqual(exports.__getArray(ref), [0, -1, -255]);
exports.__release(ref);
try { exports.__release(ref); assert(false); } catch (e) {};
}

// should be able to distinguish between signed and unsigned for static array layout
{
let arr = [0, 0xFFFF, -0x00FF];
let ref = exports.__retain(exports.__allocArray(exports.STATICARRAYI16_ID, arr));
assert(exports.__instanceof(ref, exports.STATICARRAYI16_ID));
assert.deepEqual(exports.__getArray(ref), [0, -1, -255]);
exports.__release(ref);
try { exports.__release(ref); assert(false); } catch (e) {};
}

// should be able to distinguish between signed and unsigned
{
let arr = [1, -1 >>> 0, 0x80000000];
let values = [1, -1 >>> 0, 0x80000000];
let arr = new Uint32Array(values);
let ref = exports.__retain(exports.__allocArray(exports.UINT32ARRAY_ID, arr));
assert(exports.__instanceof(ref, exports.UINT32ARRAY_ID));
assert.deepEqual(exports.__getUint32Array(ref), new Uint32Array(arr));
assert.deepEqual(exports.__getUint32ArrayView(ref), new Uint32Array(arr));
assert.deepEqual(exports.__getUint32Array(ref), arr);
assert.deepEqual(exports.__getUint32ArrayView(ref), arr);
assert.deepEqual(exports.__getArray(ref), values);
exports.__release(ref);
try { exports.__release(ref); assert(false); } catch (e) {};
}

// should be able to distinguish between signed and unsigned with static array layout
{
let arr = [1, -1 >>> 0, 0x80000000];
let ref = exports.__retain(exports.__allocArray(exports.STATICARRAYU32_ID, arr));
assert(exports.__instanceof(ref, exports.STATICARRAYU32_ID));
assert.deepEqual(exports.__getArray(ref), arr);
exports.__release(ref);
try { exports.__release(ref); assert(false); } catch (e) {};
}

// should be able to distinguish between integer and float
{
let arr = [0.0, 1.5, 2.5];
let values = [0.0, 1.5, 2.5];
let arr = new Float32Array(values)
let ref = exports.__retain(exports.__allocArray(exports.FLOAT32ARRAY_ID, arr));
assert(exports.__instanceof(ref, exports.FLOAT32ARRAY_ID));
assert.deepEqual(exports.__getFloat32Array(ref), new Float32Array(arr));
assert.deepEqual(exports.__getFloat32ArrayView(ref), new Float32Array(arr));
assert.deepEqual(exports.__getFloat32Array(ref), arr);
assert.deepEqual(exports.__getFloat32ArrayView(ref), arr);
assert.deepEqual(exports.__getArray(ref), values);
exports.__release(ref);
try { exports.__release(ref); assert(false); } catch (e) {};
}

// should be able to distinguish between integer and float static arrays
{
let arr = [0.0, 1.5, 2.5];
let ref = exports.__retain(exports.__allocArray(exports.STATICARRAYF32_ID, arr));
assert(exports.__instanceof(ref, exports.STATICARRAYF32_ID));
assert.deepEqual(exports.__getArray(ref), arr);
exports.__release(ref);
try { exports.__release(ref); assert(false); } catch (e) {};
Expand Down
5 changes: 5 additions & 0 deletions src/builtins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7899,6 +7899,7 @@ export function compileRTTI(compiler: Compiler): void {
var arrayPrototype = program.arrayPrototype;
var setPrototype = program.setPrototype;
var mapPrototype = program.mapPrototype;
var staticArrayPrototype = program.staticArrayPrototype;
var lastId = 0;
// TODO: for (let [instanceId, instance] of managedClasses) {
for (let _keys = Map_keys(managedClasses), i = 0, k = _keys.length; i < k; ++i) {
Expand Down Expand Up @@ -7926,6 +7927,10 @@ export function compileRTTI(compiler: Compiler): void {
flags |= TypeinfoFlags.MAP;
flags |= TypeinfoFlags.KEY_ALIGN_0 * typeToRuntimeFlags(typeArguments[0]);
flags |= TypeinfoFlags.VALUE_ALIGN_0 * typeToRuntimeFlags(typeArguments[1]);
} else if (instance.extends(staticArrayPrototype)) {
let valueType = instance.getArrayValueType();
flags |= TypeinfoFlags.STATICARRAY;
flags |= TypeinfoFlags.VALUE_ALIGN_0 * typeToRuntimeFlags(valueType);
}
writeI32(flags, data, off); off += 4;
instance.rttiFlags = flags;
Expand Down
4 changes: 4 additions & 0 deletions src/program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3758,6 +3758,10 @@ export class Class extends TypedElement {
if (this.extends(arrayPrototype)) {
return this.getTypeArgumentsTo(arrayPrototype)![0];
}
var staticArrayPrototype = program.staticArrayPrototype;
if (this.extends(staticArrayPrototype)) {
return this.getTypeArgumentsTo(staticArrayPrototype)![0];
}
var abvInstance = program.arrayBufferViewInstance;
while (current.base !== abvInstance) {
current = assert(current.base);
Expand Down
Loading