From 97f8c19e942d0f151bf2ca32917c67353012d2ca Mon Sep 17 00:00:00 2001 From: MaxGraey Date: Tue, 3 Mar 2020 02:51:07 +0200 Subject: [PATCH 1/8] Refactor loader. Now instanciate methods return ResultObject + extended exports --- lib/loader/index.d.ts | 22 +++++-- lib/loader/index.js | 136 +++++++++++++++++++----------------------- 2 files changed, 81 insertions(+), 77 deletions(-) diff --git a/lib/loader/index.d.ts b/lib/loader/index.d.ts index e494b13a28..5e2f883c2e 100644 --- a/lib/loader/index.d.ts +++ b/lib/loader/index.d.ts @@ -1,5 +1,10 @@ /// +interface ResultObject { + module: WebAssembly.Module, + instance: WebAssembly.Instance +}; + /** WebAssembly imports with two levels of nesting. */ interface ImportsObject extends Record { env?: { @@ -92,13 +97,22 @@ interface ASUtil { } /** Asynchronously instantiates an AssemblyScript module from anything that can be instantiated. */ -export declare function instantiate(source: WebAssembly.Module | BufferSource | Response | PromiseLike, imports?: ImportsObject): Promise; +export declare function instantiate( + source: WebAssembly.Module | BufferSource | Response | PromiseLike, + imports?: ImportsObject +): Promise; /** Synchronously instantiates an AssemblyScript module from a WebAssembly.Module or binary buffer. */ -export declare function instantiateSync(source: WebAssembly.Module | BufferSource, imports?: ImportsObject): ASUtil & T; +export declare function instantiateSync( + source: WebAssembly.Module | BufferSource, + imports?: ImportsObject +): ResultObject & { exports: ASUtil & T }; /** Asynchronously instantiates an AssemblyScript module from a response, i.e. as obtained by `fetch`. */ -export declare function instantiateStreaming(source: Response | PromiseLike, imports?: ImportsObject): Promise; +export declare function instantiateStreaming( + source: Response | PromiseLike, + imports?: ImportsObject +): Promise; /** Demangles an AssemblyScript module's exports to a friendly object structure. */ -export declare function demangle(exports: {}, baseModule?: {}): T; +export declare function demangle(exports: {}, extendedExports?: {}): T; diff --git a/lib/loader/index.js b/lib/loader/index.js index 7e5d56b42b..2edf772bdf 100644 --- a/lib/loader/index.js +++ b/lib/loader/index.js @@ -58,7 +58,7 @@ function getStringImpl(buffer, ptr) { /** Prepares the base module prior to instantiation. */ function preInstantiate(imports) { - const baseModule = {}; + const extendedExports = {}; function getString(memory, ptr) { if (!memory) return ""; @@ -67,28 +67,28 @@ function preInstantiate(imports) { // add common imports used by stdlib for convenience const env = (imports.env = imports.env || {}); - env.abort = env.abort || function abort(mesg, file, line, colm) { - const memory = baseModule.memory || env.memory; // prefer exported, otherwise try imported - throw Error("abort: " + getString(memory, mesg) + " at " + getString(memory, file) + ":" + line + ":" + colm); + env.abort = env.abort || function abort(msg, file, line, colm) { + const memory = extendedExports.memory || env.memory; // prefer exported, otherwise try imported + throw Error("abort: " + getString(memory, msg) + " at " + getString(memory, file) + ":" + line + ":" + colm); } - env.trace = env.trace || function trace(mesg, n) { - const memory = baseModule.memory || env.memory; - console.log("trace: " + getString(memory, mesg) + (n ? " " : "") + Array.prototype.slice.call(arguments, 2, 2 + n).join(", ")); + env.trace = env.trace || function trace(msg, n) { + const memory = extendedExports.memory || env.memory; + console.log("trace: " + getString(memory, msg) + (n ? " " : "") + Array.prototype.slice.call(arguments, 2, 2 + n).join(", ")); } imports.Math = imports.Math || Math; imports.Date = imports.Date || Date; - return baseModule; + return extendedExports; } /** Prepares the final module once instantiation is complete. */ -function postInstantiate(baseModule, instance) { - const rawExports = instance.exports; - const memory = rawExports.memory; - const table = rawExports.table; - const alloc = rawExports["__alloc"]; - const retain = rawExports["__retain"]; - const rttiBase = rawExports["__rtti_base"] || ~0; // oob if not present +function postInstantiate(extendedExports, instance) { + const exports = instance.exports; + const memory = exports.memory; + const table = exports.table; + const alloc = exports["__alloc"]; + const retain = exports["__retain"]; + const rttiBase = exports["__rtti_base"] || ~0; // oob if not present /** Gets the runtime type info for the given id. */ function getInfo(id) { @@ -125,7 +125,7 @@ function postInstantiate(baseModule, instance) { return ptr; } - baseModule.__allocString = __allocString; + extendedExports.__allocString = __allocString; /** Reads a string from the module's memory by its pointer. */ function __getString(ptr) { @@ -135,7 +135,7 @@ function postInstantiate(baseModule, instance) { return getStringImpl(buffer, ptr); } - baseModule.__getString = __getString; + extendedExports.__getString = __getString; /** Gets the view matching the specified alignment, signedness and floatness. */ function getView(alignLog2, signed, float) { @@ -178,7 +178,7 @@ function postInstantiate(baseModule, instance) { return arr; } - baseModule.__allocArray = __allocArray; + extendedExports.__allocArray = __allocArray; /** Gets a live view on an array's values in the module's memory. Infers the array type from RTTI. */ function __getArrayView(arr) { @@ -195,7 +195,7 @@ function postInstantiate(baseModule, instance) { .subarray(buf >>>= align, buf + length); } - baseModule.__getArrayView = __getArrayView; + extendedExports.__getArrayView = __getArrayView; /** Copies an array's values from the module's memory. Infers the array type from RTTI. */ function __getArray(arr) { @@ -206,7 +206,7 @@ function postInstantiate(baseModule, instance) { return out; } - baseModule.__getArray = __getArray; + extendedExports.__getArray = __getArray; /** Copies an ArrayBuffer's value from the module's memory. */ function __getArrayBuffer(ptr) { @@ -215,7 +215,7 @@ function postInstantiate(baseModule, instance) { return buffer.slice(ptr, ptr + length); } - baseModule.__getArrayBuffer = __getArrayBuffer; + extendedExports.__getArrayBuffer = __getArrayBuffer; /** Copies a typed array's values from the module's memory. */ function getTypedArray(Type, alignLog2, ptr) { @@ -230,30 +230,30 @@ function postInstantiate(baseModule, instance) { return new Type(buffer, bufPtr, U32[bufPtr + SIZE_OFFSET >>> 2] >>> alignLog2); } - baseModule.__getInt8Array = getTypedArray.bind(null, Int8Array, 0); - baseModule.__getInt8ArrayView = getTypedArrayView.bind(null, Int8Array, 0); - baseModule.__getUint8Array = getTypedArray.bind(null, Uint8Array, 0); - baseModule.__getUint8ArrayView = getTypedArrayView.bind(null, Uint8Array, 0); - baseModule.__getUint8ClampedArray = getTypedArray.bind(null, Uint8ClampedArray, 0); - baseModule.__getUint8ClampedArrayView = getTypedArrayView.bind(null, Uint8ClampedArray, 0); - baseModule.__getInt16Array = getTypedArray.bind(null, Int16Array, 1); - baseModule.__getInt16ArrayView = getTypedArrayView.bind(null, Int16Array, 1); - baseModule.__getUint16Array = getTypedArray.bind(null, Uint16Array, 1); - baseModule.__getUint16ArrayView = getTypedArrayView.bind(null, Uint16Array, 1); - baseModule.__getInt32Array = getTypedArray.bind(null, Int32Array, 2); - baseModule.__getInt32ArrayView = getTypedArrayView.bind(null, Int32Array, 2); - baseModule.__getUint32Array = getTypedArray.bind(null, Uint32Array, 2); - baseModule.__getUint32ArrayView = getTypedArrayView.bind(null, Uint32Array, 2); + 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); if (BIGINT) { - baseModule.__getInt64Array = getTypedArray.bind(null, BigInt64Array, 3); - baseModule.__getInt64ArrayView = getTypedArrayView.bind(null, BigInt64Array, 3); - baseModule.__getUint64Array = getTypedArray.bind(null, BigUint64Array, 3); - baseModule.__getUint64ArrayView = getTypedArrayView.bind(null, BigUint64Array, 3); + 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); } - baseModule.__getFloat32Array = getTypedArray.bind(null, Float32Array, 2); - baseModule.__getFloat32ArrayView = getTypedArrayView.bind(null, Float32Array, 2); - baseModule.__getFloat64Array = getTypedArray.bind(null, Float64Array, 3); - baseModule.__getFloat64ArrayView = getTypedArrayView.bind(null, Float64Array, 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) { @@ -266,14 +266,14 @@ function postInstantiate(baseModule, instance) { return false; } - baseModule.__instanceof = __instanceof; + extendedExports.__instanceof = __instanceof; // Pull basic exports to baseModule so code in preInstantiate can use them - baseModule.memory = baseModule.memory || memory; - baseModule.table = baseModule.table || table; + extendedExports.memory = baseModule.memory || memory; + extendedExports.table = baseModule.table || table; // Demangle exports and provide the usual utility on the prototype - return demangle(rawExports, baseModule); + return demangle(exports, extendedExports); } function isResponse(o) { @@ -283,30 +283,20 @@ function isResponse(o) { /** Asynchronously instantiates an AssemblyScript module from anything that can be instantiated. */ async function instantiate(source, imports) { if (isResponse(source = await source)) return instantiateStreaming(source, imports); - return postInstantiate( - preInstantiate(imports || (imports = {})), - await WebAssembly.instantiate( - source instanceof WebAssembly.Module - ? source - : await WebAssembly.compile(source), - imports - ) - ); + const module = source instanceof WebAssembly.Module ? source : await WebAssembly.compile(source); + const instance = await WebAssembly.instantiate(module, imports); + const exports = postInstantiate(preInstantiate(imports || (imports = {})), instance); + return { module, instance, exports }; } exports.instantiate = instantiate; /** Synchronously instantiates an AssemblyScript module from a WebAssembly.Module or binary buffer. */ function instantiateSync(source, imports) { - return postInstantiate( - preInstantiate(imports || (imports = {})), - new WebAssembly.Instance( - source instanceof WebAssembly.Module - ? source - : new WebAssembly.Module(source), - imports - ) - ) + const module = source instanceof WebAssembly.Module ? source : new WebAssembly.Module(source); + const instance = new WebAssembly.Instance(module, imports); + const exports = postInstantiate(preInstantiate(imports || (imports = {})), instance); + return { module, instance, exports }; } exports.instantiateSync = instantiateSync; @@ -321,17 +311,17 @@ async function instantiateStreaming(source, imports) { imports ); } - return postInstantiate( - preInstantiate(imports || (imports = {})), - (await WebAssembly.instantiateStreaming(source, imports)).instance - ); + const result = await WebAssembly.instantiateStreaming(source, imports); + const instance = result.instance; + const exports = postInstantiate(preInstantiate(imports || (imports = {})), instance); + return { module: result.module, instance, exports }; } exports.instantiateStreaming = instantiateStreaming; /** Demangles an AssemblyScript module's exports to a friendly object structure. */ -function demangle(exports, baseModule) { - var module = baseModule ? Object.create(baseModule) : {}; +function demangle(exports, extendedExports) { + extendedExports = extendedExports ? Object.create(extendedExports) : {}; var setArgumentsLength = exports["__argumentsLength"] ? function(length) { exports["__argumentsLength"].value = length; } : exports["__setArgumentsLength"] || exports["__setargc"] || function() {}; @@ -339,7 +329,7 @@ function demangle(exports, baseModule) { if (!Object.prototype.hasOwnProperty.call(exports, internalName)) continue; const elem = exports[internalName]; let parts = internalName.split("."); - let curr = module; + let curr = extendedExports; while (parts.length > 1) { let part = parts.shift(); if (!Object.prototype.hasOwnProperty.call(curr, part)) curr[part] = {}; @@ -411,7 +401,7 @@ function demangle(exports, baseModule) { } } } - return module; + return extendedExports; } exports.demangle = demangle; From c37da649747aa693ba1fae353d085830264ef23c Mon Sep 17 00:00:00 2001 From: MaxGraey Date: Tue, 3 Mar 2020 03:14:58 +0200 Subject: [PATCH 2/8] fixes --- lib/loader/index.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/loader/index.js b/lib/loader/index.js index 2edf772bdf..8633904863 100644 --- a/lib/loader/index.js +++ b/lib/loader/index.js @@ -284,8 +284,8 @@ function isResponse(o) { async function instantiate(source, imports) { if (isResponse(source = await source)) return instantiateStreaming(source, imports); const module = source instanceof WebAssembly.Module ? source : await WebAssembly.compile(source); - const instance = await WebAssembly.instantiate(module, imports); - const exports = postInstantiate(preInstantiate(imports || (imports = {})), instance); + const instance = await WebAssembly.instantiate(module, imports || (imports = {})); + const exports = postInstantiate(preInstantiate(imports), instance); return { module, instance, exports }; } @@ -294,8 +294,8 @@ exports.instantiate = instantiate; /** Synchronously instantiates an AssemblyScript module from a WebAssembly.Module or binary buffer. */ function instantiateSync(source, imports) { const module = source instanceof WebAssembly.Module ? source : new WebAssembly.Module(source); - const instance = new WebAssembly.Instance(module, imports); - const exports = postInstantiate(preInstantiate(imports || (imports = {})), instance); + const instance = new WebAssembly.Instance(module, imports || (imports = {})); + const exports = postInstantiate(preInstantiate(imports), instance); return { module, instance, exports }; } @@ -311,9 +311,9 @@ async function instantiateStreaming(source, imports) { imports ); } - const result = await WebAssembly.instantiateStreaming(source, imports); + const result = await WebAssembly.instantiateStreaming(source, imports || (imports = {})); const instance = result.instance; - const exports = postInstantiate(preInstantiate(imports || (imports = {})), instance); + const exports = postInstantiate(preInstantiate(imports), instance); return { module: result.module, instance, exports }; } From 5db542e82b9d5dd81d8561e9090acd604720d52b Mon Sep 17 00:00:00 2001 From: MaxGraey Date: Tue, 3 Mar 2020 03:25:07 +0200 Subject: [PATCH 3/8] refactoring --- lib/loader/index.js | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/lib/loader/index.js b/lib/loader/index.js index 8633904863..af8cc8585b 100644 --- a/lib/loader/index.js +++ b/lib/loader/index.js @@ -187,7 +187,7 @@ function postInstantiate(extendedExports, instance) { const info = getInfo(id); if (!(info & ARRAYBUFFERVIEW)) throw Error("not an array: " + id); const align = getValueAlign(info); - var buf = U32[arr + ARRAYBUFFERVIEW_DATASTART_OFFSET >>> 2]; + const buf = U32[arr + ARRAYBUFFERVIEW_DATASTART_OFFSET >>> 2]; const length = info & ARRAY ? U32[arr + ARRAY_LENGTH_OFFSET >>> 2] : U32[buf + SIZE_OFFSET >>> 2] >>> align; @@ -281,10 +281,10 @@ function isResponse(o) { } /** Asynchronously instantiates an AssemblyScript module from anything that can be instantiated. */ -async function instantiate(source, imports) { +async function instantiate(source, imports = {}) { if (isResponse(source = await source)) return instantiateStreaming(source, imports); const module = source instanceof WebAssembly.Module ? source : await WebAssembly.compile(source); - const instance = await WebAssembly.instantiate(module, imports || (imports = {})); + const instance = await WebAssembly.instantiate(module, imports); const exports = postInstantiate(preInstantiate(imports), instance); return { module, instance, exports }; } @@ -292,9 +292,9 @@ async function instantiate(source, imports) { exports.instantiate = instantiate; /** Synchronously instantiates an AssemblyScript module from a WebAssembly.Module or binary buffer. */ -function instantiateSync(source, imports) { +function instantiateSync(source, imports = {}) { const module = source instanceof WebAssembly.Module ? source : new WebAssembly.Module(source); - const instance = new WebAssembly.Instance(module, imports || (imports = {})); + const instance = new WebAssembly.Instance(module, imports); const exports = postInstantiate(preInstantiate(imports), instance); return { module, instance, exports }; } @@ -302,7 +302,7 @@ function instantiateSync(source, imports) { exports.instantiateSync = instantiateSync; /** Asynchronously instantiates an AssemblyScript module from a response, i.e. as obtained by `fetch`. */ -async function instantiateStreaming(source, imports) { +async function instantiateStreaming(source, imports = {}) { if (!WebAssembly.instantiateStreaming) { return instantiate( isResponse(source = await source) @@ -311,10 +311,9 @@ async function instantiateStreaming(source, imports) { imports ); } - const result = await WebAssembly.instantiateStreaming(source, imports || (imports = {})); - const instance = result.instance; - const exports = postInstantiate(preInstantiate(imports), instance); - return { module: result.module, instance, exports }; + const result = await WebAssembly.instantiateStreaming(source, imports); + const exports = postInstantiate(preInstantiate(imports), result.instance); + return { ...result, exports }; } exports.instantiateStreaming = instantiateStreaming; @@ -322,9 +321,9 @@ exports.instantiateStreaming = instantiateStreaming; /** Demangles an AssemblyScript module's exports to a friendly object structure. */ function demangle(exports, extendedExports) { extendedExports = extendedExports ? Object.create(extendedExports) : {}; - var setArgumentsLength = exports["__argumentsLength"] - ? function(length) { exports["__argumentsLength"].value = length; } - : exports["__setArgumentsLength"] || exports["__setargc"] || function() {}; + const setArgumentsLength = exports["__argumentsLength"] + ? length => { exports["__argumentsLength"].value = length; } + : exports["__setArgumentsLength"] || exports["__setargc"] || (() => {}); for (let internalName in exports) { if (!Object.prototype.hasOwnProperty.call(exports, internalName)) continue; const elem = exports[internalName]; @@ -338,10 +337,10 @@ function demangle(exports, extendedExports) { let name = parts[0]; let hash = name.indexOf("#"); if (hash >= 0) { - let className = name.substring(0, hash); - let classElem = curr[className]; + const className = name.substring(0, hash); + const classElem = curr[className]; if (typeof classElem === "undefined" || !classElem.prototype) { - let ctor = function(...args) { + const ctor = function(...args) { return ctor.wrap(ctor.prototype.constructor(0, ...args)); }; ctor.prototype = { From e7b63bcbdabe184d1072ff42ffbae8963650f7a0 Mon Sep 17 00:00:00 2001 From: MaxGraey Date: Tue, 3 Mar 2020 06:19:22 +0200 Subject: [PATCH 4/8] refactor --- lib/loader/index.js | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/lib/loader/index.js b/lib/loader/index.js index af8cc8585b..fe5cebb26c 100644 --- a/lib/loader/index.js +++ b/lib/loader/index.js @@ -43,8 +43,8 @@ const CHUNKSIZE = 1024; function getStringImpl(buffer, ptr) { const U32 = new Uint32Array(buffer); const U16 = new Uint16Array(buffer); - var length = U32[(ptr + SIZE_OFFSET) >>> 2] >>> 1; - var offset = ptr >>> 1; + let length = U32[(ptr + SIZE_OFFSET) >>> 2] >>> 1; + let offset = ptr >>> 1; if (length <= CHUNKSIZE) return String.fromCharCode.apply(String, U16.subarray(offset, offset + length)); const parts = []; do { @@ -258,7 +258,7 @@ function postInstantiate(extendedExports, instance) { /** Tests whether an object is an instance of the class represented by the specified base id. */ function __instanceof(ptr, baseId) { const U32 = new Uint32Array(memory.buffer); - var id = U32[(ptr + ID_OFFSET) >>> 2]; + let id = U32[(ptr + ID_OFFSET) >>> 2]; if (id <= U32[rttiBase >>> 2]) { do if (id == baseId) return true; while (id = getBase(id)); @@ -276,14 +276,18 @@ function postInstantiate(extendedExports, instance) { return demangle(exports, extendedExports); } -function isResponse(o) { - return typeof Response !== "undefined" && o instanceof Response; +function isResponse(src) { + return typeof Response !== "undefined" && src instanceof Response; +} + +function isModule(src) { + return src instanceof WebAssembly.Module; } /** Asynchronously instantiates an AssemblyScript module from anything that can be instantiated. */ async function instantiate(source, imports = {}) { if (isResponse(source = await source)) return instantiateStreaming(source, imports); - const module = source instanceof WebAssembly.Module ? source : await WebAssembly.compile(source); + const module = isModule(source) ? source : await WebAssembly.compile(source); const instance = await WebAssembly.instantiate(module, imports); const exports = postInstantiate(preInstantiate(imports), instance); return { module, instance, exports }; @@ -293,7 +297,7 @@ exports.instantiate = instantiate; /** Synchronously instantiates an AssemblyScript module from a WebAssembly.Module or binary buffer. */ function instantiateSync(source, imports = {}) { - const module = source instanceof WebAssembly.Module ? source : new WebAssembly.Module(source); + const module = isModule(source) ? source : new WebAssembly.Module(source); const instance = new WebAssembly.Instance(module, imports); const exports = postInstantiate(preInstantiate(imports), instance); return { module, instance, exports }; @@ -319,8 +323,8 @@ async function instantiateStreaming(source, imports = {}) { exports.instantiateStreaming = instantiateStreaming; /** Demangles an AssemblyScript module's exports to a friendly object structure. */ -function demangle(exports, extendedExports) { - extendedExports = extendedExports ? Object.create(extendedExports) : {}; +function demangle(exports, extendedExports = {}) { + extendedExports = Object.create(extendedExports); const setArgumentsLength = exports["__argumentsLength"] ? length => { exports["__argumentsLength"].value = length; } : exports["__setArgumentsLength"] || exports["__setargc"] || (() => {}); From c951092c8b6f96f002669ed494752aad57cb9930 Mon Sep 17 00:00:00 2001 From: MaxGraey Date: Tue, 3 Mar 2020 19:01:42 +0200 Subject: [PATCH 5/8] fix tests --- lib/loader/index.js | 17 +++-- lib/loader/tests/index.js | 151 ++++++++++++++++++++------------------ 2 files changed, 90 insertions(+), 78 deletions(-) diff --git a/lib/loader/index.js b/lib/loader/index.js index fe5cebb26c..abd587b327 100644 --- a/lib/loader/index.js +++ b/lib/loader/index.js @@ -187,7 +187,7 @@ function postInstantiate(extendedExports, instance) { const info = getInfo(id); if (!(info & ARRAYBUFFERVIEW)) throw Error("not an array: " + id); const align = getValueAlign(info); - const buf = U32[arr + ARRAYBUFFERVIEW_DATASTART_OFFSET >>> 2]; + let buf = U32[arr + ARRAYBUFFERVIEW_DATASTART_OFFSET >>> 2]; const length = info & ARRAY ? U32[arr + ARRAY_LENGTH_OFFSET >>> 2] : U32[buf + SIZE_OFFSET >>> 2] >>> align; @@ -268,9 +268,9 @@ function postInstantiate(extendedExports, instance) { extendedExports.__instanceof = __instanceof; - // Pull basic exports to baseModule so code in preInstantiate can use them - extendedExports.memory = baseModule.memory || memory; - extendedExports.table = baseModule.table || table; + // Pull basic exports to extendedExports so code in preInstantiate can use them + extendedExports.memory = extendedExports.memory || memory; + extendedExports.table = extendedExports.table || table; // Demangle exports and provide the usual utility on the prototype return demangle(exports, extendedExports); @@ -288,8 +288,9 @@ function isModule(src) { async function instantiate(source, imports = {}) { if (isResponse(source = await source)) return instantiateStreaming(source, imports); const module = isModule(source) ? source : await WebAssembly.compile(source); + const extended = preInstantiate(imports); const instance = await WebAssembly.instantiate(module, imports); - const exports = postInstantiate(preInstantiate(imports), instance); + const exports = postInstantiate(extended, instance); return { module, instance, exports }; } @@ -298,8 +299,9 @@ exports.instantiate = instantiate; /** Synchronously instantiates an AssemblyScript module from a WebAssembly.Module or binary buffer. */ function instantiateSync(source, imports = {}) { const module = isModule(source) ? source : new WebAssembly.Module(source); + const extended = preInstantiate(imports); const instance = new WebAssembly.Instance(module, imports); - const exports = postInstantiate(preInstantiate(imports), instance); + const exports = postInstantiate(extended, instance); return { module, instance, exports }; } @@ -315,8 +317,9 @@ async function instantiateStreaming(source, imports = {}) { imports ); } + const extended = preInstantiate(imports); const result = await WebAssembly.instantiateStreaming(source, imports); - const exports = postInstantiate(preInstantiate(imports), result.instance); + const exports = postInstantiate(extended, result.instance); return { ...result, exports }; } diff --git a/lib/loader/tests/index.js b/lib/loader/tests/index.js index bef4129e5a..8dee745204 100644 --- a/lib/loader/tests/index.js +++ b/lib/loader/tests/index.js @@ -12,44 +12,45 @@ testInstantiate("legacy.wasm"); function test(file) { var buffer = fs.readFileSync(__dirname + "/build/" + file); - var module = loader.instantiateSync(buffer, {}); + var result = loader.instantiateSync(buffer, {}); + const exports = result.exports; - console.log(inspect(module, true, 100, true)); + console.log(inspect(exports, true, 100, true)); // should export memory - assert(module.memory instanceof WebAssembly.Memory); - assert(typeof module.memory.copy === "function"); + assert(exports.memory instanceof WebAssembly.Memory); + assert(typeof exports.memory.copy === "function"); // should be able to get an exported string - assert.strictEqual(module.__getString(module.COLOR), "red"); + assert.strictEqual(exports.__getString(exports.COLOR), "red"); // should be able to allocate and work with a new string { let str = "Hello world!𤭢"; - let ref = module.__retain(module.__allocString(str)); - assert.strictEqual(module.__getString(ref), str); - assert.strictEqual(module.strlen(ref), str.length); - module.__release(ref); + let ref = exports.__retain(exports.__allocString(str)); + assert.strictEqual(exports.__getString(ref), str); + assert.strictEqual(exports.strlen(ref), str.length); + exports.__release(ref); } // should be able to allocate a typed array { var arr = [1, 2, 3, 4, 5, 0x80000000 | 0]; - let ref = module.__retain(module.__allocArray(module.INT32ARRAY_ID, arr)); - assert(module.__instanceof(ref, module.INT32ARRAY_ID)); + let ref = exports.__retain(exports.__allocArray(exports.INT32ARRAY_ID, arr)); + assert(exports.__instanceof(ref, exports.INT32ARRAY_ID)); // should be able to get the values of an array - assert.deepEqual(module.__getArray(ref), arr); + assert.deepEqual(exports.__getArray(ref), arr); // should be able to get a view on an array - assert.deepEqual(module.__getArrayView(ref), new Int32Array(arr)); + assert.deepEqual(exports.__getArrayView(ref), new Int32Array(arr)); // should be able to sum up its values - assert.strictEqual(module.sum(ref), arr.reduce((a, b) => (a + b) | 0, 0) | 0); + assert.strictEqual(exports.sum(ref), arr.reduce((a, b) => (a + b) | 0, 0) | 0); // should be able to release no longer needed references - module.__release(ref); - try { module.__release(ref); assert(false); } catch (e) {}; + exports.__release(ref); + try { exports.__release(ref); assert(false); } catch (e) {}; } /* @@ -80,62 +81,62 @@ function test(file) { // should be able to distinguish between signed and unsigned { let arr = new Uint8Array([0, 255, 127]); - let ref = module.__retain(module.__allocArray(module.UINT8ARRAY_ID, arr)); - assert(module.__instanceof(ref, module.UINT8ARRAY_ID)); - assert.deepEqual(module.__getUint8Array(ref), arr); - module.__release(ref); - try { module.__release(ref); assert(false); } catch (e) {}; + let ref = exports.__retain(exports.__allocArray(exports.UINT8ARRAY_ID, arr)); + assert(exports.__instanceof(ref, exports.UINT8ARRAY_ID)); + assert.deepEqual(exports.__getUint8Array(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 ref = module.__retain(module.__allocArray(module.INT16ARRAY_ID, arr)); - assert(module.__instanceof(ref, module.INT16ARRAY_ID)); - assert.deepEqual(module.__getInt16Array(ref), arr); - module.__release(ref); - try { module.__release(ref); assert(false); } catch (e) {}; + let ref = exports.__retain(exports.__allocArray(exports.INT16ARRAY_ID, arr)); + assert(exports.__instanceof(ref, exports.INT16ARRAY_ID)); + assert.deepEqual(exports.__getInt16Array(ref), arr); + 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 ref = module.__retain(module.__allocArray(module.UINT32ARRAY_ID, arr)); - assert(module.__instanceof(ref, module.UINT32ARRAY_ID)); - assert.deepEqual(module.__getUint32Array(ref), new Uint32Array(arr)); - assert.deepEqual(module.__getUint32ArrayView(ref), new Uint32Array(arr)); - assert.deepEqual(module.__getArray(ref), arr); - module.__release(ref); - try { module.__release(ref); assert(false); } catch (e) {}; + 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.__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 ref = module.__retain(module.__allocArray(module.FLOAT32ARRAY_ID, arr)); - assert(module.__instanceof(ref, module.FLOAT32ARRAY_ID)); - assert.deepEqual(module.__getFloat32Array(ref), new Float32Array(arr)); - assert.deepEqual(module.__getFloat32ArrayView(ref), new Float32Array(arr)); - assert.deepEqual(module.__getArray(ref), arr); - module.__release(ref); - try { module.__release(ref); assert(false); } catch (e) {}; + 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.__getArray(ref), arr); + exports.__release(ref); + try { exports.__release(ref); assert(false); } catch (e) {}; } // should be able to work with normal arrays { let arr = [1, 2, 3, 4, 5]; - let ref = module.__retain(module.__allocArray(module.ARRAYI32_ID, arr)); - assert(module.__instanceof(ref, module.ARRAYI32_ID)); - module.changeLength(ref, 3); - assert.deepEqual(module.__getArray(ref), [1, 2, 3]); - module.__release(ref); - try { module.__release(ref); assert(false); } catch (e) {}; + let ref = exports.__retain(exports.__allocArray(exports.ARRAYI32_ID, arr)); + assert(exports.__instanceof(ref, exports.ARRAYI32_ID)); + exports.changeLength(ref, 3); + assert.deepEqual(exports.__getArray(ref), [1, 2, 3]); + exports.__release(ref); + try { exports.__release(ref); assert(false); } catch (e) {}; } // should be able to correctly call a function with variable arguments - assert.strictEqual(module.varadd(), 3); - assert.strictEqual(module.varadd(2, 3), 5); - assert.strictEqual(module.varadd(2), 4); + assert.strictEqual(exports.varadd(), 3); + assert.strictEqual(exports.varadd(2, 3), 5); + assert.strictEqual(exports.varadd(2), 4); // TBD: table is no more exported by default to allow more optimizations @@ -150,67 +151,75 @@ function test(file) { // assert.strictEqual(module.calladd(ref, 2, 3), 5); // should be able to use a class - var car = new module.Car(5); + var car = new exports.Car(5); assert.strictEqual(car.numDoors, 5); assert.strictEqual(car.isDoorsOpen, 0); car.openDoors(); assert.strictEqual(car.isDoorsOpen, 1); car.closeDoors(); assert.strictEqual(car.isDoorsOpen, 0); - module.__release(car); // uses Car.prototype.valueOf to obtain `thisPtr` + exports.__release(car); // uses Car.prototype.valueOf to obtain `thisPtr` // should be able to use trace - module.dotrace(42); + exports.dotrace(42); // should be able to mutate an array in place using getArrayView { - let ptr = module.__retain(module.__allocArray(module.FLOAT32ARRAY_ID, [1, 2, 3])); - let view = module.__getArrayView(ptr); + let ptr = exports.__retain(exports.__allocArray(exports.FLOAT32ARRAY_ID, [1, 2, 3])); + let view = exports.__getArrayView(ptr); assert.deepEqual(view, new Float32Array([1, 2, 3])); - module.modifyFloat32Array(ptr, 0, 4); + exports.modifyFloat32Array(ptr, 0, 4); assert.deepEqual(view, new Float32Array([4, 2, 3])); - module.__release(ptr); + exports.__release(ptr); } // should be able to mutate an array in place using getFloat32Array { - let ptr = module.newFloat32Array(3); // returns are pre-retained - let view = module.__getFloat32ArrayView(ptr); - let arr = module.__getFloat32Array(ptr); + let ptr = exports.newFloat32Array(3); // returns are pre-retained + let view = exports.__getFloat32ArrayView(ptr); + let arr = exports.__getFloat32Array(ptr); assert.deepEqual(view, new Float32Array([0, 0, 0])); assert.deepEqual(arr, new Float32Array([0, 0, 0])); - module.modifyFloat32Array(ptr, 0, 3); - module.modifyFloat32Array(ptr, 1, 2); - module.modifyFloat32Array(ptr, 2, 1); + exports.modifyFloat32Array(ptr, 0, 3); + exports.modifyFloat32Array(ptr, 1, 2); + exports.modifyFloat32Array(ptr, 2, 1); assert.deepEqual(view, new Float32Array([3, 2, 1])); assert.deepEqual(arr, new Float32Array([0, 0, 0])); - module.__release(ptr); + exports.__release(ptr); } } function testInstantiate(file) { // should be able to instantiate from a buffer (async () => { - const module = await loader.instantiate(fs.readFileSync(__dirname + "/build/" + file), {}); - assert(module.memory); + const { exports, instance, module } = await loader.instantiate(fs.readFileSync(__dirname + "/build/" + file), {}); + assert(exports.memory); + assert(instance && instance instanceof WebAssembly.Instance); + assert(module && module instanceof WebAssembly.Module); })(); // should be able to instantiate from a wasm module (async () => { const wasmModule = new WebAssembly.Module(fs.readFileSync(__dirname + "/build/" + file)); - const module = await loader.instantiate(wasmModule, {}); - assert(module.memory); + const { exports, instance, module } = await loader.instantiate(wasmModule, {}); + assert(exports.memory); + assert(instance && instance instanceof WebAssembly.Instance); + assert(module && module instanceof WebAssembly.Module); })(); // should be able to instantiate from a promise yielding a buffer (async () => { - const module = await loader.instantiate(fs.promises.readFile(__dirname + "/build/" + file), {}); - assert(module.memory); + const { exports, instance, module } = await loader.instantiate(fs.promises.readFile(__dirname + "/build/" + file), {}); + assert(exports.memory); + assert(instance && instance instanceof WebAssembly.Instance); + assert(module && module instanceof WebAssembly.Module); })(); // should be able to mimic instantiateStreaming under node (for now) (async () => { - const module = await loader.instantiateStreaming(fs.promises.readFile(__dirname + "/build/" + file), {}); - assert(module.memory); + const { exports, instance, module } = await loader.instantiateStreaming(fs.promises.readFile(__dirname + "/build/" + file), {}); + assert(exports.memory); + assert(instance && instance instanceof WebAssembly.Instance); + assert(module && module instanceof WebAssembly.Module); })(); } \ No newline at end of file From b0d941689b0569bf3eda22d7d99cef8b3a329993 Mon Sep 17 00:00:00 2001 From: MaxGraey Date: Tue, 3 Mar 2020 19:10:22 +0200 Subject: [PATCH 6/8] fix bootstrap test --- tests/bootstrap/index.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/bootstrap/index.ts b/tests/bootstrap/index.ts index 673bc0a358..c1918116dc 100644 --- a/tests/bootstrap/index.ts +++ b/tests/bootstrap/index.ts @@ -5,7 +5,10 @@ import AssemblyScript from "../../out/assemblyscript"; async function test(build: string): Promise { await binaryen.ready; - const assemblyscript = await loader.instantiate(fs.promises.readFile(__dirname + "/../../out/assemblyscript." + build + ".wasm"), { binaryen }); + const { exports: assemblyscript } = await loader.instantiate( + fs.promises.readFile(`${ __dirname }/../../out/assemblyscript.${ build }.wasm`), + { binaryen } + ); console.log(assemblyscript); const optionsPtr = assemblyscript.newOptions(); const programPtr = assemblyscript.newProgram(optionsPtr); From ccf86cf9a4d4aa2d1a10306ffb9528f7ef99310f Mon Sep 17 00:00:00 2001 From: MaxGraey Date: Wed, 4 Mar 2020 01:34:35 +0200 Subject: [PATCH 7/8] refactor bootstrap test --- tests/bootstrap/index.ts | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/bootstrap/index.ts b/tests/bootstrap/index.ts index c1918116dc..15805d9c0d 100644 --- a/tests/bootstrap/index.ts +++ b/tests/bootstrap/index.ts @@ -5,20 +5,20 @@ import AssemblyScript from "../../out/assemblyscript"; async function test(build: string): Promise { await binaryen.ready; - const { exports: assemblyscript } = await loader.instantiate( + const { exports: asc } = await loader.instantiate( fs.promises.readFile(`${ __dirname }/../../out/assemblyscript.${ build }.wasm`), { binaryen } ); - console.log(assemblyscript); - const optionsPtr = assemblyscript.newOptions(); - const programPtr = assemblyscript.newProgram(optionsPtr); - const textPtr = assemblyscript.__allocString("export function add(a: i32, b: i32): i32 { return a + b; }\n"); - const pathPtr = assemblyscript.__allocString("index.ts"); - assemblyscript.parse(programPtr, textPtr, pathPtr, true); - var nextFilePtr = assemblyscript.nextFile(programPtr); + console.log(asc); + const optionsPtr = asc.newOptions(); + const programPtr = asc.newProgram(optionsPtr); + const textPtr = asc.__allocString("export function add(a: i32, b: i32): i32 { return a + b; }\n"); + const pathPtr = asc.__allocString("index.ts"); + asc.parse(programPtr, textPtr, pathPtr, true); + var nextFilePtr = asc.nextFile(programPtr); while (nextFilePtr) { - console.log("nextFile: " + assemblyscript.__getString(nextFilePtr)); - nextFilePtr = assemblyscript.nextFile(programPtr); + console.log("nextFile: " + asc.__getString(nextFilePtr)); + nextFilePtr = asc.nextFile(programPtr); } // assemblyscript.compile(programPtr); // ^ abort: missing ArrayBuffer at src/program.ts:1015:18 From 36e7470b24c900f6438ebd5fedaf5aa30f261c6f Mon Sep 17 00:00:00 2001 From: MaxGraey Date: Sat, 14 Mar 2020 03:57:13 +0200 Subject: [PATCH 8/8] post-merge fixes --- lib/loader/index.js | 4 ++-- tests/bootstrap/index.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/loader/index.js b/lib/loader/index.js index a639ee1747..2f9e38e46f 100644 --- a/lib/loader/index.js +++ b/lib/loader/index.js @@ -70,11 +70,11 @@ function preInstantiate(imports) { env.abort = env.abort || function abort(msg, file, line, colm) { const memory = extendedExports.memory || env.memory; // prefer exported, otherwise try imported throw Error("abort: " + getString(memory, msg) + " at " + getString(memory, file) + ":" + line + ":" + colm); - } + }; env.trace = env.trace || function trace(msg, n) { const memory = extendedExports.memory || env.memory; console.log("trace: " + getString(memory, msg) + (n ? " " : "") + Array.prototype.slice.call(arguments, 2, 2 + n).join(", ")); - } + }; env.seed = env.seed || function seed() { return Date.now(); }; diff --git a/tests/bootstrap/index.ts b/tests/bootstrap/index.ts index 98002d1d38..bafb9ff1b7 100644 --- a/tests/bootstrap/index.ts +++ b/tests/bootstrap/index.ts @@ -10,13 +10,13 @@ async function test(build: string): Promise { fs.promises.readFile(`${ __dirname }/../../out/assemblyscript.${ build }.wasm`), { binaryen } ); + console.log(util.inspect(asc, true)); const optionsPtr = asc.newOptions(); const programPtr = asc.newProgram(optionsPtr); const textPtr = asc.__allocString("export function add(a: i32, b: i32): i32 { return a + b; }\n"); const pathPtr = asc.__allocString("index.ts"); asc.parse(programPtr, textPtr, pathPtr, true); var nextFilePtr = asc.nextFile(programPtr); - console.log(util.inspect(assemblyscript, true)); while (nextFilePtr) { console.log("nextFile: " + asc.__getString(nextFilePtr)); nextFilePtr = asc.nextFile(programPtr);