Skip to content

Commit 88cc73b

Browse files
authored
Closely align the loader's instantiateXY with WebAssembly's (#1143)
1 parent ab71d93 commit 88cc73b

File tree

4 files changed

+196
-174
lines changed

4 files changed

+196
-174
lines changed

lib/loader/index.d.ts

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
/// <reference lib="esnext.bigint" />
22

3+
interface ResultObject {
4+
module: WebAssembly.Module,
5+
instance: WebAssembly.Instance
6+
};
7+
38
/** WebAssembly imports with two levels of nesting. */
49
interface ImportsObject extends Record<string, any> {
510
env?: {
@@ -92,13 +97,22 @@ interface ASUtil {
9297
}
9398

9499
/** Asynchronously instantiates an AssemblyScript module from anything that can be instantiated. */
95-
export declare function instantiate<T extends {}>(source: WebAssembly.Module | BufferSource | Response | PromiseLike<WebAssembly.Module | BufferSource | Response>, imports?: ImportsObject): Promise<ASUtil & T>;
100+
export declare function instantiate<T extends {}>(
101+
source: WebAssembly.Module | BufferSource | Response | PromiseLike<WebAssembly.Module | BufferSource | Response>,
102+
imports?: ImportsObject
103+
): Promise<ResultObject & { exports: ASUtil & T }>;
96104

97105
/** Synchronously instantiates an AssemblyScript module from a WebAssembly.Module or binary buffer. */
98-
export declare function instantiateSync<T extends {}>(source: WebAssembly.Module | BufferSource, imports?: ImportsObject): ASUtil & T;
106+
export declare function instantiateSync<T extends {}>(
107+
source: WebAssembly.Module | BufferSource,
108+
imports?: ImportsObject
109+
): ResultObject & { exports: ASUtil & T };
99110

100111
/** Asynchronously instantiates an AssemblyScript module from a response, i.e. as obtained by `fetch`. */
101-
export declare function instantiateStreaming<T extends {}>(source: Response | PromiseLike<Response>, imports?: ImportsObject): Promise<ASUtil & T>;
112+
export declare function instantiateStreaming<T extends {}>(
113+
source: Response | PromiseLike<Response>,
114+
imports?: ImportsObject
115+
): Promise<ResultObject & { exports: ASUtil & T }>;
102116

103117
/** Demangles an AssemblyScript module's exports to a friendly object structure. */
104-
export declare function demangle<T extends {}>(exports: {}, baseModule?: {}): T;
118+
export declare function demangle<T extends {}>(exports: {}, extendedExports?: {}): T;

lib/loader/index.js

Lines changed: 85 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,8 @@ const CHUNKSIZE = 1024;
4343
function getStringImpl(buffer, ptr) {
4444
const U32 = new Uint32Array(buffer);
4545
const U16 = new Uint16Array(buffer);
46-
var length = U32[(ptr + SIZE_OFFSET) >>> 2] >>> 1;
47-
var offset = ptr >>> 1;
46+
let length = U32[(ptr + SIZE_OFFSET) >>> 2] >>> 1;
47+
let offset = ptr >>> 1;
4848
if (length <= CHUNKSIZE) return String.fromCharCode.apply(String, U16.subarray(offset, offset + length));
4949
const parts = [];
5050
do {
@@ -58,7 +58,7 @@ function getStringImpl(buffer, ptr) {
5858

5959
/** Prepares the base module prior to instantiation. */
6060
function preInstantiate(imports) {
61-
const baseModule = {};
61+
const extendedExports = {};
6262

6363
function getString(memory, ptr) {
6464
if (!memory) return "<yet unknown>";
@@ -67,31 +67,31 @@ function preInstantiate(imports) {
6767

6868
// add common imports used by stdlib for convenience
6969
const env = (imports.env = imports.env || {});
70-
env.abort = env.abort || function abort(mesg, file, line, colm) {
71-
const memory = baseModule.memory || env.memory; // prefer exported, otherwise try imported
72-
throw Error("abort: " + getString(memory, mesg) + " in " + getString(memory, file) + "(" + line + ":" + colm + ")");
70+
env.abort = env.abort || function abort(msg, file, line, colm) {
71+
const memory = extendedExports.memory || env.memory; // prefer exported, otherwise try imported
72+
throw Error("abort: " + getString(memory, msg) + " at " + getString(memory, file) + ":" + line + ":" + colm);
7373
};
74-
env.trace = env.trace || function trace(mesg, n) {
75-
const memory = baseModule.memory || env.memory;
76-
console.log("trace: " + getString(memory, mesg) + (n ? " " : "") + Array.prototype.slice.call(arguments, 2, 2 + n).join(", "));
74+
env.trace = env.trace || function trace(msg, n) {
75+
const memory = extendedExports.memory || env.memory;
76+
console.log("trace: " + getString(memory, msg) + (n ? " " : "") + Array.prototype.slice.call(arguments, 2, 2 + n).join(", "));
7777
};
7878
env.seed = env.seed || function seed() {
7979
return Date.now();
8080
};
8181
imports.Math = imports.Math || Math;
8282
imports.Date = imports.Date || Date;
8383

84-
return baseModule;
84+
return extendedExports;
8585
}
8686

8787
/** Prepares the final module once instantiation is complete. */
88-
function postInstantiate(baseModule, instance) {
89-
const rawExports = instance.exports;
90-
const memory = rawExports.memory;
91-
const table = rawExports.table;
92-
const alloc = rawExports["__alloc"];
93-
const retain = rawExports["__retain"];
94-
const rttiBase = rawExports["__rtti_base"] || ~0; // oob if not present
88+
function postInstantiate(extendedExports, instance) {
89+
const exports = instance.exports;
90+
const memory = exports.memory;
91+
const table = exports.table;
92+
const alloc = exports["__alloc"];
93+
const retain = exports["__retain"];
94+
const rttiBase = exports["__rtti_base"] || ~0; // oob if not present
9595

9696
/** Gets the runtime type info for the given id. */
9797
function getInfo(id) {
@@ -128,7 +128,7 @@ function postInstantiate(baseModule, instance) {
128128
return ptr;
129129
}
130130

131-
baseModule.__allocString = __allocString;
131+
extendedExports.__allocString = __allocString;
132132

133133
/** Reads a string from the module's memory by its pointer. */
134134
function __getString(ptr) {
@@ -138,7 +138,7 @@ function postInstantiate(baseModule, instance) {
138138
return getStringImpl(buffer, ptr);
139139
}
140140

141-
baseModule.__getString = __getString;
141+
extendedExports.__getString = __getString;
142142

143143
/** Gets the view matching the specified alignment, signedness and floatness. */
144144
function getView(alignLog2, signed, float) {
@@ -181,7 +181,7 @@ function postInstantiate(baseModule, instance) {
181181
return arr;
182182
}
183183

184-
baseModule.__allocArray = __allocArray;
184+
extendedExports.__allocArray = __allocArray;
185185

186186
/** Gets a live view on an array's values in the module's memory. Infers the array type from RTTI. */
187187
function __getArrayView(arr) {
@@ -190,15 +190,15 @@ function postInstantiate(baseModule, instance) {
190190
const info = getInfo(id);
191191
if (!(info & (ARRAYBUFFERVIEW | ARRAY))) throw Error("not an array: " + id + ", flags=" + info);
192192
const align = getValueAlign(info);
193-
var buf = U32[arr + ARRAYBUFFERVIEW_DATASTART_OFFSET >>> 2];
193+
let buf = U32[arr + ARRAYBUFFERVIEW_DATASTART_OFFSET >>> 2];
194194
const length = info & ARRAY
195195
? U32[arr + ARRAY_LENGTH_OFFSET >>> 2]
196196
: U32[buf + SIZE_OFFSET >>> 2] >>> align;
197197
return getView(align, info & VAL_SIGNED, info & VAL_FLOAT)
198198
.subarray(buf >>>= align, buf + length);
199199
}
200200

201-
baseModule.__getArrayView = __getArrayView;
201+
extendedExports.__getArrayView = __getArrayView;
202202

203203
/** Copies an array's values from the module's memory. Infers the array type from RTTI. */
204204
function __getArray(arr) {
@@ -209,7 +209,7 @@ function postInstantiate(baseModule, instance) {
209209
return out;
210210
}
211211

212-
baseModule.__getArray = __getArray;
212+
extendedExports.__getArray = __getArray;
213213

214214
/** Copies an ArrayBuffer's value from the module's memory. */
215215
function __getArrayBuffer(ptr) {
@@ -218,7 +218,7 @@ function postInstantiate(baseModule, instance) {
218218
return buffer.slice(ptr, ptr + length);
219219
}
220220

221-
baseModule.__getArrayBuffer = __getArrayBuffer;
221+
extendedExports.__getArrayBuffer = __getArrayBuffer;
222222

223223
/** Copies a typed array's values from the module's memory. */
224224
function getTypedArray(Type, alignLog2, ptr) {
@@ -233,89 +233,85 @@ function postInstantiate(baseModule, instance) {
233233
return new Type(buffer, bufPtr, U32[bufPtr + SIZE_OFFSET >>> 2] >>> alignLog2);
234234
}
235235

236-
baseModule.__getInt8Array = getTypedArray.bind(null, Int8Array, 0);
237-
baseModule.__getInt8ArrayView = getTypedArrayView.bind(null, Int8Array, 0);
238-
baseModule.__getUint8Array = getTypedArray.bind(null, Uint8Array, 0);
239-
baseModule.__getUint8ArrayView = getTypedArrayView.bind(null, Uint8Array, 0);
240-
baseModule.__getUint8ClampedArray = getTypedArray.bind(null, Uint8ClampedArray, 0);
241-
baseModule.__getUint8ClampedArrayView = getTypedArrayView.bind(null, Uint8ClampedArray, 0);
242-
baseModule.__getInt16Array = getTypedArray.bind(null, Int16Array, 1);
243-
baseModule.__getInt16ArrayView = getTypedArrayView.bind(null, Int16Array, 1);
244-
baseModule.__getUint16Array = getTypedArray.bind(null, Uint16Array, 1);
245-
baseModule.__getUint16ArrayView = getTypedArrayView.bind(null, Uint16Array, 1);
246-
baseModule.__getInt32Array = getTypedArray.bind(null, Int32Array, 2);
247-
baseModule.__getInt32ArrayView = getTypedArrayView.bind(null, Int32Array, 2);
248-
baseModule.__getUint32Array = getTypedArray.bind(null, Uint32Array, 2);
249-
baseModule.__getUint32ArrayView = getTypedArrayView.bind(null, Uint32Array, 2);
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);
250250
if (BIGINT) {
251-
baseModule.__getInt64Array = getTypedArray.bind(null, BigInt64Array, 3);
252-
baseModule.__getInt64ArrayView = getTypedArrayView.bind(null, BigInt64Array, 3);
253-
baseModule.__getUint64Array = getTypedArray.bind(null, BigUint64Array, 3);
254-
baseModule.__getUint64ArrayView = getTypedArrayView.bind(null, BigUint64Array, 3);
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);
255255
}
256-
baseModule.__getFloat32Array = getTypedArray.bind(null, Float32Array, 2);
257-
baseModule.__getFloat32ArrayView = getTypedArrayView.bind(null, Float32Array, 2);
258-
baseModule.__getFloat64Array = getTypedArray.bind(null, Float64Array, 3);
259-
baseModule.__getFloat64ArrayView = getTypedArrayView.bind(null, Float64Array, 3);
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);
260260

261261
/** Tests whether an object is an instance of the class represented by the specified base id. */
262262
function __instanceof(ptr, baseId) {
263263
const U32 = new Uint32Array(memory.buffer);
264-
var id = U32[(ptr + ID_OFFSET) >>> 2];
264+
let id = U32[(ptr + ID_OFFSET) >>> 2];
265265
if (id <= U32[rttiBase >>> 2]) {
266266
do if (id == baseId) return true;
267267
while (id = getBase(id));
268268
}
269269
return false;
270270
}
271271

272-
baseModule.__instanceof = __instanceof;
272+
extendedExports.__instanceof = __instanceof;
273273

274-
// Pull basic exports to baseModule so code in preInstantiate can use them
275-
baseModule.memory = baseModule.memory || memory;
276-
baseModule.table = baseModule.table || table;
274+
// Pull basic exports to extendedExports so code in preInstantiate can use them
275+
extendedExports.memory = extendedExports.memory || memory;
276+
extendedExports.table = extendedExports.table || table;
277277

278278
// Demangle exports and provide the usual utility on the prototype
279-
return demangle(rawExports, baseModule);
279+
return demangle(exports, extendedExports);
280280
}
281281

282-
function isResponse(o) {
283-
return typeof Response !== "undefined" && o instanceof Response;
282+
function isResponse(src) {
283+
return typeof Response !== "undefined" && src instanceof Response;
284+
}
285+
286+
function isModule(src) {
287+
return src instanceof WebAssembly.Module;
284288
}
285289

286290
/** Asynchronously instantiates an AssemblyScript module from anything that can be instantiated. */
287-
async function instantiate(source, imports) {
291+
async function instantiate(source, imports = {}) {
288292
if (isResponse(source = await source)) return instantiateStreaming(source, imports);
289-
return postInstantiate(
290-
preInstantiate(imports || (imports = {})),
291-
await WebAssembly.instantiate(
292-
source instanceof WebAssembly.Module
293-
? source
294-
: await WebAssembly.compile(source),
295-
imports
296-
)
297-
);
293+
const module = isModule(source) ? source : await WebAssembly.compile(source);
294+
const extended = preInstantiate(imports);
295+
const instance = await WebAssembly.instantiate(module, imports);
296+
const exports = postInstantiate(extended, instance);
297+
return { module, instance, exports };
298298
}
299299

300300
exports.instantiate = instantiate;
301301

302302
/** Synchronously instantiates an AssemblyScript module from a WebAssembly.Module or binary buffer. */
303-
function instantiateSync(source, imports) {
304-
return postInstantiate(
305-
preInstantiate(imports || (imports = {})),
306-
new WebAssembly.Instance(
307-
source instanceof WebAssembly.Module
308-
? source
309-
: new WebAssembly.Module(source),
310-
imports
311-
)
312-
)
303+
function instantiateSync(source, imports = {}) {
304+
const module = isModule(source) ? source : new WebAssembly.Module(source);
305+
const extended = preInstantiate(imports);
306+
const instance = new WebAssembly.Instance(module, imports);
307+
const exports = postInstantiate(extended, instance);
308+
return { module, instance, exports };
313309
}
314310

315311
exports.instantiateSync = instantiateSync;
316312

317313
/** Asynchronously instantiates an AssemblyScript module from a response, i.e. as obtained by `fetch`. */
318-
async function instantiateStreaming(source, imports) {
314+
async function instantiateStreaming(source, imports = {}) {
319315
if (!WebAssembly.instantiateStreaming) {
320316
return instantiate(
321317
isResponse(source = await source)
@@ -324,25 +320,25 @@ async function instantiateStreaming(source, imports) {
324320
imports
325321
);
326322
}
327-
return postInstantiate(
328-
preInstantiate(imports || (imports = {})),
329-
(await WebAssembly.instantiateStreaming(source, imports)).instance
330-
);
323+
const extended = preInstantiate(imports);
324+
const result = await WebAssembly.instantiateStreaming(source, imports);
325+
const exports = postInstantiate(extended, result.instance);
326+
return { ...result, exports };
331327
}
332328

333329
exports.instantiateStreaming = instantiateStreaming;
334330

335331
/** Demangles an AssemblyScript module's exports to a friendly object structure. */
336-
function demangle(exports, baseModule) {
337-
var module = baseModule ? Object.create(baseModule) : {};
338-
var setArgumentsLength = exports["__argumentsLength"]
339-
? function(length) { exports["__argumentsLength"].value = length; }
340-
: exports["__setArgumentsLength"] || exports["__setargc"] || function() {};
332+
function demangle(exports, extendedExports = {}) {
333+
extendedExports = Object.create(extendedExports);
334+
const setArgumentsLength = exports["__argumentsLength"]
335+
? length => { exports["__argumentsLength"].value = length; }
336+
: exports["__setArgumentsLength"] || exports["__setargc"] || (() => {});
341337
for (let internalName in exports) {
342338
if (!Object.prototype.hasOwnProperty.call(exports, internalName)) continue;
343339
const elem = exports[internalName];
344340
let parts = internalName.split(".");
345-
let curr = module;
341+
let curr = extendedExports;
346342
while (parts.length > 1) {
347343
let part = parts.shift();
348344
if (!Object.prototype.hasOwnProperty.call(curr, part)) curr[part] = {};
@@ -351,10 +347,10 @@ function demangle(exports, baseModule) {
351347
let name = parts[0];
352348
let hash = name.indexOf("#");
353349
if (hash >= 0) {
354-
let className = name.substring(0, hash);
355-
let classElem = curr[className];
350+
const className = name.substring(0, hash);
351+
const classElem = curr[className];
356352
if (typeof classElem === "undefined" || !classElem.prototype) {
357-
let ctor = function(...args) {
353+
const ctor = function(...args) {
358354
return ctor.wrap(ctor.prototype.constructor(0, ...args));
359355
};
360356
ctor.prototype = {
@@ -414,7 +410,7 @@ function demangle(exports, baseModule) {
414410
}
415411
}
416412
}
417-
return module;
413+
return extendedExports;
418414
}
419415

420416
exports.demangle = demangle;

0 commit comments

Comments
 (0)