Skip to content

Commit 9e05906

Browse files
authored
Support StaticArray in loader (#1197)
1 parent 70e9b15 commit 9e05906

29 files changed

+207
-101
lines changed

lib/loader/index.js

+55-45
Original file line numberDiff line numberDiff line change
@@ -12,20 +12,21 @@ const ARRAYBUFFERVIEW_ID = 2;
1212
// Runtime type information
1313
const ARRAYBUFFERVIEW = 1 << 0;
1414
const ARRAY = 1 << 1;
15-
const SET = 1 << 2;
16-
const MAP = 1 << 3;
17-
const VAL_ALIGN_OFFSET = 5;
15+
const STATICARRAY = 1 << 2;
16+
const SET = 1 << 3;
17+
const MAP = 1 << 4;
18+
const VAL_ALIGN_OFFSET = 6;
1819
const VAL_ALIGN = 1 << VAL_ALIGN_OFFSET;
19-
const VAL_SIGNED = 1 << 10;
20-
const VAL_FLOAT = 1 << 11;
21-
const VAL_NULLABLE = 1 << 12;
22-
const VAL_MANAGED = 1 << 13;
23-
const KEY_ALIGN_OFFSET = 14;
20+
const VAL_SIGNED = 1 << 11;
21+
const VAL_FLOAT = 1 << 12;
22+
const VAL_NULLABLE = 1 << 13;
23+
const VAL_MANAGED = 1 << 14;
24+
const KEY_ALIGN_OFFSET = 15;
2425
const KEY_ALIGN = 1 << KEY_ALIGN_OFFSET;
25-
const KEY_SIGNED = 1 << 19;
26-
const KEY_FLOAT = 1 << 20;
27-
const KEY_NULLABLE = 1 << 21;
28-
const KEY_MANAGED = 1 << 22;
26+
const KEY_SIGNED = 1 << 20;
27+
const KEY_FLOAT = 1 << 21;
28+
const KEY_NULLABLE = 1 << 22;
29+
const KEY_MANAGED = 1 << 23;
2930

3031
// Array(BufferView) layout
3132
const ARRAYBUFFERVIEW_BUFFER_OFFSET = 0;
@@ -162,23 +163,29 @@ function postInstantiate(extendedExports, instance) {
162163
/** Allocates a new array in the module's memory and returns its retained pointer. */
163164
function __allocArray(id, values) {
164165
const info = getInfo(id);
165-
if (!(info & (ARRAYBUFFERVIEW | ARRAY))) throw Error("not an array: " + id + ", flags= " + info);
166+
if (!(info & (ARRAYBUFFERVIEW | ARRAY | STATICARRAY))) throw Error("not an array: " + id + ", flags= " + info);
166167
const align = getValueAlign(info);
167168
const length = values.length;
168-
const buf = alloc(length << align, ARRAYBUFFER_ID);
169-
const arr = alloc(info & ARRAY ? ARRAY_SIZE : ARRAYBUFFERVIEW_SIZE, id);
170-
const U32 = new Uint32Array(memory.buffer);
171-
U32[arr + ARRAYBUFFERVIEW_BUFFER_OFFSET >>> 2] = retain(buf);
172-
U32[arr + ARRAYBUFFERVIEW_DATASTART_OFFSET >>> 2] = buf;
173-
U32[arr + ARRAYBUFFERVIEW_DATALENGTH_OFFSET >>> 2] = length << align;
174-
if (info & ARRAY) U32[arr + ARRAY_LENGTH_OFFSET >>> 2] = length;
169+
const buf = alloc(length << align, info & STATICARRAY ? id : ARRAYBUFFER_ID);
170+
let result;
171+
if (info & STATICARRAY) {
172+
result = buf;
173+
} else {
174+
const arr = alloc(info & ARRAY ? ARRAY_SIZE : ARRAYBUFFERVIEW_SIZE, id);
175+
const U32 = new Uint32Array(memory.buffer);
176+
U32[arr + ARRAYBUFFERVIEW_BUFFER_OFFSET >>> 2] = retain(buf);
177+
U32[arr + ARRAYBUFFERVIEW_DATASTART_OFFSET >>> 2] = buf;
178+
U32[arr + ARRAYBUFFERVIEW_DATALENGTH_OFFSET >>> 2] = length << align;
179+
if (info & ARRAY) U32[arr + ARRAY_LENGTH_OFFSET >>> 2] = length;
180+
result = arr;
181+
}
175182
const view = getView(align, info & VAL_SIGNED, info & VAL_FLOAT);
176183
if (info & VAL_MANAGED) {
177184
for (let i = 0; i < length; ++i) view[(buf >>> align) + i] = retain(values[i]);
178185
} else {
179186
view.set(values, buf >>> align);
180187
}
181-
return arr;
188+
return result;
182189
}
183190

184191
extendedExports.__allocArray = __allocArray;
@@ -188,9 +195,11 @@ function postInstantiate(extendedExports, instance) {
188195
const U32 = new Uint32Array(memory.buffer);
189196
const id = U32[arr + ID_OFFSET >>> 2];
190197
const info = getInfo(id);
191-
if (!(info & (ARRAYBUFFERVIEW | ARRAY))) throw Error("not an array: " + id + ", flags=" + info);
198+
if (!(info & (ARRAYBUFFERVIEW | ARRAY | STATICARRAY))) throw Error("not an array: " + id + ", flags=" + info);
192199
const align = getValueAlign(info);
193-
let buf = U32[arr + ARRAYBUFFERVIEW_DATASTART_OFFSET >>> 2];
200+
let buf = info & STATICARRAY
201+
? arr
202+
: U32[arr + ARRAYBUFFERVIEW_DATASTART_OFFSET >>> 2];
194203
const length = info & ARRAY
195204
? U32[arr + ARRAY_LENGTH_OFFSET >>> 2]
196205
: U32[buf + SIZE_OFFSET >>> 2] >>> align;
@@ -233,30 +242,31 @@ function postInstantiate(extendedExports, instance) {
233242
return new Type(buffer, bufPtr, U32[bufPtr + SIZE_OFFSET >>> 2] >>> alignLog2);
234243
}
235244

236-
extendedExports.__getInt8Array = getTypedArray.bind(null, Int8Array, 0);
237-
extendedExports.__getInt8ArrayView = getTypedArrayView.bind(null, Int8Array, 0);
238-
extendedExports.__getUint8Array = getTypedArray.bind(null, Uint8Array, 0);
239-
extendedExports.__getUint8ArrayView = getTypedArrayView.bind(null, Uint8Array, 0);
240-
extendedExports.__getUint8ClampedArray = getTypedArray.bind(null, Uint8ClampedArray, 0);
241-
extendedExports.__getUint8ClampedArrayView = getTypedArrayView.bind(null, Uint8ClampedArray, 0);
242-
extendedExports.__getInt16Array = getTypedArray.bind(null, Int16Array, 1);
243-
extendedExports.__getInt16ArrayView = getTypedArrayView.bind(null, Int16Array, 1);
244-
extendedExports.__getUint16Array = getTypedArray.bind(null, Uint16Array, 1);
245-
extendedExports.__getUint16ArrayView = getTypedArrayView.bind(null, Uint16Array, 1);
246-
extendedExports.__getInt32Array = getTypedArray.bind(null, Int32Array, 2);
247-
extendedExports.__getInt32ArrayView = getTypedArrayView.bind(null, Int32Array, 2);
248-
extendedExports.__getUint32Array = getTypedArray.bind(null, Uint32Array, 2);
249-
extendedExports.__getUint32ArrayView = getTypedArrayView.bind(null, Uint32Array, 2);
245+
/** Attach a set of get TypedArray and View functions to the exports. */
246+
function attachTypedArrayFunctions(ctor, name, align) {
247+
extendedExports["__get" + name] = getTypedArray.bind(null, ctor, align);
248+
extendedExports["__get" + name + "View"] = getTypedArrayView.bind(null, ctor, align);
249+
}
250+
251+
[
252+
Int8Array,
253+
Uint8Array,
254+
Uint8ClampedArray,
255+
Int16Array,
256+
Uint16Array,
257+
Int32Array,
258+
Uint32Array,
259+
Float32Array,
260+
Float64Array
261+
].forEach(ctor => {
262+
attachTypedArrayFunctions(ctor, ctor.name, 31 - Math.clz32(ctor.BYTES_PER_ELEMENT));
263+
});
264+
250265
if (BIGINT) {
251-
extendedExports.__getInt64Array = getTypedArray.bind(null, BigInt64Array, 3);
252-
extendedExports.__getInt64ArrayView = getTypedArrayView.bind(null, BigInt64Array, 3);
253-
extendedExports.__getUint64Array = getTypedArray.bind(null, BigUint64Array, 3);
254-
extendedExports.__getUint64ArrayView = getTypedArrayView.bind(null, BigUint64Array, 3);
266+
[BigUint64Array, BigInt64Array].forEach(ctor => {
267+
attachTypedArrayFunctions(ctor, ctor.name.slice(3), 3);
268+
});
255269
}
256-
extendedExports.__getFloat32Array = getTypedArray.bind(null, Float32Array, 2);
257-
extendedExports.__getFloat32ArrayView = getTypedArrayView.bind(null, Float32Array, 2);
258-
extendedExports.__getFloat64Array = getTypedArray.bind(null, Float64Array, 3);
259-
extendedExports.__getFloat64ArrayView = getTypedArrayView.bind(null, Float64Array, 3);
260270

261271
/** Tests whether an object is an instance of the class represented by the specified base id. */
262272
function __instanceof(ptr, baseId) {

lib/loader/tests/assembly/index.ts

+11
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,12 @@ export function sum(arr: Int32Array): i32 {
4545
return v;
4646
}
4747

48+
export function sumStatic(arr: StaticArray<i32>): i32 {
49+
var v = 0;
50+
for (let i = 0, k = arr.length; i < k; ++i) v += arr[i];
51+
return v;
52+
}
53+
4854
export function changeLength(arr: Array<i32>, length: i32): void {
4955
arr.length = length;
5056
}
@@ -70,6 +76,11 @@ export const INT32ARRAY_ID = idof<Int32Array>();
7076
export const UINT32ARRAY_ID = idof<Uint32Array>();
7177
export const FLOAT32ARRAY_ID = idof<Float32Array>();
7278
export const ARRAYI32_ID = idof<Array<i32>>();
79+
export const STATICARRAYI32_ID = idof<StaticArray<i32>>();
80+
export const STATICARRAYU32_ID = idof<StaticArray<u32>>();
81+
export const STATICARRAYU8_ID = idof<StaticArray<u8>>();
82+
export const STATICARRAYI16_ID = idof<StaticArray<i16>>();
83+
export const STATICARRAYF32_ID = idof<StaticArray<f32>>();
7384

7485
export function newFloat32Array(size: i32): Float32Array {
7586
return new Float32Array(size);
+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"extends": "../../../../std/assembly.json",
3+
"include": [
4+
"./**/*.ts"
5+
]
6+
}

lib/loader/tests/index.js

+76-8
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,26 @@ function test(file) {
5353
try { exports.__release(ref); assert(false); } catch (e) {};
5454
}
5555

56+
// should be able to allocate a typed array
57+
{
58+
var arr = [1, 2, 3, 4, 5, 0x80000000 | 0];
59+
let ref = exports.__retain(exports.__allocArray(exports.STATICARRAYI32_ID, arr));
60+
assert(exports.__instanceof(ref, exports.STATICARRAYI32_ID));
61+
62+
// should be able to get the values of an array
63+
assert.deepEqual(exports.__getArray(ref), arr);
64+
65+
// should be able to get a view on an array
66+
assert.deepEqual(exports.__getArrayView(ref), new Int32Array(arr));
67+
68+
// should be able to sum up its values
69+
assert.strictEqual(exports.sumStatic(ref), arr.reduce((a, b) => (a + b) | 0, 0) | 0);
70+
71+
// should be able to release no longer needed references
72+
exports.__release(ref);
73+
try { exports.__release(ref); assert(false); } catch (e) {};
74+
}
75+
5676
/*
5777
{
5878
let arrU8Arr = new Uint8Array([0, 1, 2]);
@@ -80,43 +100,91 @@ function test(file) {
80100

81101
// should be able to distinguish between signed and unsigned
82102
{
83-
let arr = new Uint8Array([0, 255, 127]);
103+
let values = [0, 255, 127];
104+
let arr = new Uint8Array(values);
84105
let ref = exports.__retain(exports.__allocArray(exports.UINT8ARRAY_ID, arr));
85106
assert(exports.__instanceof(ref, exports.UINT8ARRAY_ID));
86107
assert.deepEqual(exports.__getUint8Array(ref), arr);
108+
assert.deepEqual(exports.__getUint8ArrayView(ref), arr);
109+
assert.deepEqual(exports.__getArray(ref), values);
110+
exports.__release(ref);
111+
try { exports.__release(ref); assert(false); } catch (e) {};
112+
}
113+
114+
// should be able to distinguish between signed and unsigned for static array layout
115+
{
116+
let arr = [0, 255, 127];
117+
let ref = exports.__retain(exports.__allocArray(exports.STATICARRAYU8_ID, arr));
118+
assert(exports.__instanceof(ref, exports.STATICARRAYU8_ID));
119+
assert.deepEqual(exports.__getArray(ref), arr);
87120
exports.__release(ref);
88121
try { exports.__release(ref); assert(false); } catch (e) {};
89122
}
90123

91124
// should be able to distinguish between signed and unsigned
92125
{
93-
let arr = new Int16Array([0, 0xFFFF, -0x00FF]);
126+
let values = [0, 0xFFFF, -0x00FF];
127+
let arr = new Int16Array(values);
94128
let ref = exports.__retain(exports.__allocArray(exports.INT16ARRAY_ID, arr));
95129
assert(exports.__instanceof(ref, exports.INT16ARRAY_ID));
96130
assert.deepEqual(exports.__getInt16Array(ref), arr);
131+
assert.deepEqual(exports.__getInt16ArrayView(ref), arr);
132+
assert.deepEqual(exports.__getArray(ref), [0, -1, -255]);
133+
exports.__release(ref);
134+
try { exports.__release(ref); assert(false); } catch (e) {};
135+
}
136+
137+
// should be able to distinguish between signed and unsigned for static array layout
138+
{
139+
let arr = [0, 0xFFFF, -0x00FF];
140+
let ref = exports.__retain(exports.__allocArray(exports.STATICARRAYI16_ID, arr));
141+
assert(exports.__instanceof(ref, exports.STATICARRAYI16_ID));
142+
assert.deepEqual(exports.__getArray(ref), [0, -1, -255]);
97143
exports.__release(ref);
98144
try { exports.__release(ref); assert(false); } catch (e) {};
99145
}
100146

101147
// should be able to distinguish between signed and unsigned
102148
{
103-
let arr = [1, -1 >>> 0, 0x80000000];
149+
let values = [1, -1 >>> 0, 0x80000000];
150+
let arr = new Uint32Array(values);
104151
let ref = exports.__retain(exports.__allocArray(exports.UINT32ARRAY_ID, arr));
105152
assert(exports.__instanceof(ref, exports.UINT32ARRAY_ID));
106-
assert.deepEqual(exports.__getUint32Array(ref), new Uint32Array(arr));
107-
assert.deepEqual(exports.__getUint32ArrayView(ref), new Uint32Array(arr));
153+
assert.deepEqual(exports.__getUint32Array(ref), arr);
154+
assert.deepEqual(exports.__getUint32ArrayView(ref), arr);
155+
assert.deepEqual(exports.__getArray(ref), values);
156+
exports.__release(ref);
157+
try { exports.__release(ref); assert(false); } catch (e) {};
158+
}
159+
160+
// should be able to distinguish between signed and unsigned with static array layout
161+
{
162+
let arr = [1, -1 >>> 0, 0x80000000];
163+
let ref = exports.__retain(exports.__allocArray(exports.STATICARRAYU32_ID, arr));
164+
assert(exports.__instanceof(ref, exports.STATICARRAYU32_ID));
108165
assert.deepEqual(exports.__getArray(ref), arr);
109166
exports.__release(ref);
110167
try { exports.__release(ref); assert(false); } catch (e) {};
111168
}
112169

113170
// should be able to distinguish between integer and float
114171
{
115-
let arr = [0.0, 1.5, 2.5];
172+
let values = [0.0, 1.5, 2.5];
173+
let arr = new Float32Array(values)
116174
let ref = exports.__retain(exports.__allocArray(exports.FLOAT32ARRAY_ID, arr));
117175
assert(exports.__instanceof(ref, exports.FLOAT32ARRAY_ID));
118-
assert.deepEqual(exports.__getFloat32Array(ref), new Float32Array(arr));
119-
assert.deepEqual(exports.__getFloat32ArrayView(ref), new Float32Array(arr));
176+
assert.deepEqual(exports.__getFloat32Array(ref), arr);
177+
assert.deepEqual(exports.__getFloat32ArrayView(ref), arr);
178+
assert.deepEqual(exports.__getArray(ref), values);
179+
exports.__release(ref);
180+
try { exports.__release(ref); assert(false); } catch (e) {};
181+
}
182+
183+
// should be able to distinguish between integer and float static arrays
184+
{
185+
let arr = [0.0, 1.5, 2.5];
186+
let ref = exports.__retain(exports.__allocArray(exports.STATICARRAYF32_ID, arr));
187+
assert(exports.__instanceof(ref, exports.STATICARRAYF32_ID));
120188
assert.deepEqual(exports.__getArray(ref), arr);
121189
exports.__release(ref);
122190
try { exports.__release(ref); assert(false); } catch (e) {};

src/builtins.ts

+5
Original file line numberDiff line numberDiff line change
@@ -7899,6 +7899,7 @@ export function compileRTTI(compiler: Compiler): void {
78997899
var arrayPrototype = program.arrayPrototype;
79007900
var setPrototype = program.setPrototype;
79017901
var mapPrototype = program.mapPrototype;
7902+
var staticArrayPrototype = program.staticArrayPrototype;
79027903
var lastId = 0;
79037904
// TODO: for (let [instanceId, instance] of managedClasses) {
79047905
for (let _keys = Map_keys(managedClasses), i = 0, k = _keys.length; i < k; ++i) {
@@ -7926,6 +7927,10 @@ export function compileRTTI(compiler: Compiler): void {
79267927
flags |= TypeinfoFlags.MAP;
79277928
flags |= TypeinfoFlags.KEY_ALIGN_0 * typeToRuntimeFlags(typeArguments[0]);
79287929
flags |= TypeinfoFlags.VALUE_ALIGN_0 * typeToRuntimeFlags(typeArguments[1]);
7930+
} else if (instance.extends(staticArrayPrototype)) {
7931+
let valueType = instance.getArrayValueType();
7932+
flags |= TypeinfoFlags.STATICARRAY;
7933+
flags |= TypeinfoFlags.VALUE_ALIGN_0 * typeToRuntimeFlags(valueType);
79297934
}
79307935
writeI32(flags, data, off); off += 4;
79317936
instance.rttiFlags = flags;

src/program.ts

+4
Original file line numberDiff line numberDiff line change
@@ -3758,6 +3758,10 @@ export class Class extends TypedElement {
37583758
if (this.extends(arrayPrototype)) {
37593759
return this.getTypeArgumentsTo(arrayPrototype)![0];
37603760
}
3761+
var staticArrayPrototype = program.staticArrayPrototype;
3762+
if (this.extends(staticArrayPrototype)) {
3763+
return this.getTypeArgumentsTo(staticArrayPrototype)![0];
3764+
}
37613765
var abvInstance = program.arrayBufferViewInstance;
37623766
while (current.base !== abvInstance) {
37633767
current = assert(current.base);

0 commit comments

Comments
 (0)