Skip to content

Commit

Permalink
Support StaticArray in loader (AssemblyScript#1197)
Browse files Browse the repository at this point in the history
  • Loading branch information
jtenner authored Mar 28, 2020
1 parent 70e9b15 commit 9e05906
Show file tree
Hide file tree
Showing 29 changed files with 207 additions and 101 deletions.
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

0 comments on commit 9e05906

Please sign in to comment.