Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 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
3 changes: 3 additions & 0 deletions lib/loader/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,9 @@ export interface ASUtil {
/** Gets a live view on a Float64Array's values in the module's memory. */
__getFloat64ArrayView(ptr: number): Float64Array;

/** Gets a function from poiner which contain table's index. */
__getFunction(ptr: number): ((...args: unknown[]) => unknown) | null;

/** Tests whether a managed object is an instance of the class represented by the specified base id. */
__instanceof(ptr: number, baseId: number): boolean;
/** Allocates a new string in the module's memory and returns a reference (pointer) to it. */
Expand Down
52 changes: 30 additions & 22 deletions lib/loader/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ const ARRAYBUFFERVIEW_SIZE = 12;
const ARRAY_LENGTH_OFFSET = 12;
const ARRAY_SIZE = 16;

const E_NO_EXPORT_TABLE = "Operation requires compiling with --exportTable";
const E_NO_EXPORT_RUNTIME = "Operation requires compiling with --exportRuntime";
const F_NO_EXPORT_RUNTIME = function() { throw Error(E_NO_EXPORT_RUNTIME); };

const BIGINT = typeof BigUint64Array !== "undefined";
const THIS = Symbol();

Expand Down Expand Up @@ -83,51 +87,46 @@ function preInstantiate(imports) {
return extendedExports;
}

const E_NOEXPORTRUNTIME = "Operation requires compiling with --exportRuntime";
const F_NOEXPORTRUNTIME = function() { throw Error(E_NOEXPORTRUNTIME); };

/** Prepares the final module once instantiation is complete. */
function postInstantiate(extendedExports, instance) {
const exports = instance.exports;
const memory = exports.memory;
const table = exports.table;
const __new = exports.__new || F_NOEXPORTRUNTIME;
const __pin = exports.__pin || F_NOEXPORTRUNTIME;
const __unpin = exports.__unpin || F_NOEXPORTRUNTIME;
const __collect = exports.__collect || F_NOEXPORTRUNTIME;
const __new = exports.__new || F_NO_EXPORT_RUNTIME;
const __pin = exports.__pin || F_NO_EXPORT_RUNTIME;
const __unpin = exports.__unpin || F_NO_EXPORT_RUNTIME;
const __collect = exports.__collect || F_NO_EXPORT_RUNTIME;
const __rtti_base = exports.__rtti_base;
const getRttiCount = __rtti_base
? function (arr) { return arr[__rtti_base >>> 2]; }
: F_NOEXPORTRUNTIME;
: F_NO_EXPORT_RUNTIME;

extendedExports.__new = __new;
extendedExports.__pin = __pin;
extendedExports.__unpin = __unpin;
extendedExports.__collect = __collect;

/** Gets the runtime type info for the given id. */
function getInfo(id) {
function getRttInfo(id) {
const U32 = new Uint32Array(memory.buffer);
const count = getRttiCount(U32);
if ((id >>>= 0) >= count) throw Error(`invalid id: ${id}`);
return U32[(__rtti_base + 4 >>> 2) + id * 2];
if ((id >>>= 0) >= getRttiCount(U32)) throw Error(`invalid id: ${id}`);
return U32[(__rtti_base + 4 >>> 2) + (id << 1)];
}

/** Gets the runtime base id for the given id. */
function getRttBase(id) {
const U32 = new Uint32Array(memory.buffer);
if ((id >>>= 0) >= getRttiCount(U32)) throw Error(`invalid id: ${id}`);
return U32[(__rtti_base + 4 >>> 2) + (id << 1) + 1];
}

/** Gets and validate runtime type info for the given id for array like objects */
function getArrayInfo(id) {
const info = getInfo(id);
const info = getRttInfo(id);
if (!(info & (ARRAYBUFFERVIEW | ARRAY | STATICARRAY))) throw Error(`not an array: ${id}, flags=${info}`);
return info;
}

/** Gets the runtime base id for the given id. */
function getBase(id) {
const U32 = new Uint32Array(memory.buffer);
const count = getRttiCount(U32);
if ((id >>>= 0) >= count) throw Error(`invalid id: ${id}`);
return U32[(__rtti_base + 4 >>> 2) + id * 2 + 1];
}

/** Gets the runtime alignment of a collection's values. */
function getValueAlign(info) {
return 31 - Math.clz32((info >>> VAL_ALIGN_OFFSET) & 31); // -1 if none
Expand Down Expand Up @@ -263,6 +262,15 @@ function postInstantiate(extendedExports, instance) {

extendedExports.__getArrayBuffer = __getArrayBuffer;

/** Gets a function from poiner which contain table's index. */
function __getFunction(ptr) {
if (!table) throw Error(E_NO_EXPORT_TABLE);
const index = new Uint32Array(memory.buffer)[ptr >>> 2];
return table.get(index);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this slower than passing the fn.index from AS to JS? It could be

  function __getFunction(index) {
    if (!table) throw Error(E_NO_EXPORT_TABLE);
    return table.get(index);
  }


extendedExports.__getFunction = __getFunction;

/** Copies a typed array's values from the module's memory. */
function getTypedArray(Type, alignLog2, ptr) {
return new Type(getTypedArrayView(Type, alignLog2, ptr));
Expand Down Expand Up @@ -309,7 +317,7 @@ function postInstantiate(extendedExports, instance) {
if (id <= getRttiCount(U32)) {
do {
if (id == baseId) return true;
id = getBase(id);
id = getRttBase(id);
} while (id);
}
return false;
Expand Down
7 changes: 5 additions & 2 deletions lib/loader/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
],
"version": "0.0.0",
"author": "Daniel Wirtz <[email protected]>",
"contributors": [
"MaxGraey <[email protected]>"
],
"license": "Apache-2.0",
"homepage": "https://assemblyscript.org",
"repository": {
Expand All @@ -30,8 +33,8 @@
},
"scripts": {
"asbuild": "npm run asbuild:default && npm run asbuild:legacy",
"asbuild:default": "node ../../bin/asc tests/assembly/index.ts --binaryFile tests/build/default.wasm --exportRuntime",
"asbuild:legacy": "node ../../bin/asc tests/assembly/index.ts --disable mutable-globals --binaryFile tests/build/legacy.wasm --exportRuntime",
"asbuild:default": "node ../../bin/asc tests/assembly/index.ts --binaryFile tests/build/default.wasm --exportRuntime --exportTable",
"asbuild:legacy": "node ../../bin/asc tests/assembly/index.ts --disable mutable-globals --binaryFile tests/build/legacy.wasm --exportRuntime --exportTable",
"build": "npx esm2umd loader index.js > umd/index.js",
"test": "node tests && node tests/umd"
},
Expand Down
4 changes: 4 additions & 0 deletions lib/loader/tests/assembly/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@ export function dotrace(num: f64): void {
trace("The answer is", 1, num);
}

export function getVaraddFunc(): (a: i32, b: i32) => i32 {
return varadd;
}

export const UINT8ARRAY_ID = idof<Uint8Array>();
export const INT16ARRAY_ID = idof<Int16Array>();
export const UINT16ARRAY_ID = idof<Uint16Array>();
Expand Down
Binary file modified lib/loader/tests/build/default.wasm
Binary file not shown.
Binary file modified lib/loader/tests/build/legacy.wasm
Binary file not shown.
31 changes: 26 additions & 5 deletions lib/loader/tests/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,16 @@ function test(file) {
assert.strictEqual(car.isDoorsOpen, 0);
assert(typeof +car === "number"); // uses Car.prototype.valueOf to obtain `thisPtr`

// should be able to return a function
{
const addFunc = exports.__getFunction(exports.getVaraddFunc());
assert(typeof addFunc === "function");
assert.strictEqual(addFunc(1, 2), 3);

const invalidFunc = exports.__getFunction(0);
assert(invalidFunc == null);
}

// should be able to use trace
exports.dotrace(42);

Expand Down Expand Up @@ -261,32 +271,43 @@ function test(file) {
function testInstantiate(file) {
// should be able to instantiate from a buffer
(async () => {
const { exports, instance, module } = await loader.instantiate(fs.readFileSync(__dirname + "/build/" + file), {});
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 { exports, instance, module } = await loader.instantiate(wasmModule, {});
const { exports, instance, module } = await loader.instantiate(
new WebAssembly.Module(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 promise yielding a buffer
(async () => {
const { exports, instance, module } = await loader.instantiate(fs.promises.readFile(__dirname + "/build/" + file), {});
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 { exports, instance, module } = await loader.instantiateStreaming(fs.promises.readFile(__dirname + "/build/" + file), {});
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);
Expand Down