2018-05-19 13:38:47 +02:00
|
|
|
"use strict";
|
|
|
|
|
2018-06-11 15:18:13 +02:00
|
|
|
const hasBigInt64 = typeof BigUint64Array !== "undefined";
|
|
|
|
|
2018-05-19 13:38:47 +02:00
|
|
|
/** Instantiates an AssemblyScript module using the specified imports. */
|
|
|
|
function instantiate(module, imports) {
|
|
|
|
|
|
|
|
// Set up the imports object
|
|
|
|
if (!imports) imports = {};
|
|
|
|
if (!imports.env) imports.env = {};
|
|
|
|
if (!imports.env.abort) imports.env.abort = function(mesg, file, line, colm) {
|
|
|
|
mesg = mem ? getString(mesg) : "";
|
|
|
|
file = mem ? getString(file) : "<instantiate>";
|
|
|
|
throw Error("abort: " + mesg + " at " + file + ":" + line + ":" + colm);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Instantiate the module and obtain its (flat) exports
|
|
|
|
var instance = new WebAssembly.Instance(module, imports);
|
|
|
|
var exports = instance.exports;
|
2018-09-18 15:17:44 +02:00
|
|
|
var memory_allocate = exports["memory.allocate"];
|
|
|
|
var memory_fill = exports["memory.fill"];
|
|
|
|
var memory_free = exports["memory.free"];
|
2018-05-19 13:38:47 +02:00
|
|
|
|
|
|
|
// Provide views for all sorts of basic values
|
2018-06-11 15:18:13 +02:00
|
|
|
var mem, I8, U8, I16, U16, I32, U32, F32, F64, I64, U64;
|
2018-05-19 13:38:47 +02:00
|
|
|
|
|
|
|
/** Updates memory views if memory has grown meanwhile. */
|
|
|
|
function checkMem() {
|
|
|
|
// see: https://github.com/WebAssembly/design/issues/1210
|
|
|
|
if (mem !== exports.memory.buffer) {
|
|
|
|
mem = exports.memory.buffer;
|
|
|
|
I8 = new Int8Array(mem);
|
|
|
|
U8 = new Uint8Array(mem);
|
|
|
|
I16 = new Int16Array(mem);
|
|
|
|
U16 = new Uint16Array(mem);
|
|
|
|
I32 = new Int32Array(mem);
|
|
|
|
U32 = new Uint32Array(mem);
|
2018-06-11 15:18:13 +02:00
|
|
|
if (hasBigInt64) {
|
|
|
|
I64 = new BigInt64Array(mem);
|
|
|
|
U64 = new BigUint64Array(mem);
|
|
|
|
}
|
2018-05-19 13:38:47 +02:00
|
|
|
F32 = new Float32Array(mem);
|
|
|
|
F64 = new Float64Array(mem);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
checkMem();
|
|
|
|
|
|
|
|
/** Allocates a new string in the module's memory and returns its pointer. */
|
|
|
|
function newString(str) {
|
|
|
|
var dataLength = str.length;
|
2018-09-18 15:17:44 +02:00
|
|
|
var ptr = memory_allocate(4 + (dataLength << 1));
|
2018-05-19 13:38:47 +02:00
|
|
|
var dataOffset = (4 + ptr) >>> 1;
|
|
|
|
checkMem();
|
|
|
|
U32[ptr >>> 2] = dataLength;
|
|
|
|
for (let i = 0; i < dataLength; ++i) U16[dataOffset + i] = str.charCodeAt(i);
|
|
|
|
return ptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Gets a string from the module's memory by its pointer. */
|
|
|
|
function getString(ptr) {
|
|
|
|
checkMem();
|
|
|
|
var dataLength = U32[ptr >>> 2];
|
|
|
|
var dataOffset = (ptr + 4) >>> 1;
|
|
|
|
var dataRemain = dataLength;
|
|
|
|
var parts = [];
|
|
|
|
const chunkSize = 1024;
|
|
|
|
while (dataRemain > chunkSize) {
|
|
|
|
let last = U16[dataOffset + chunkSize - 1];
|
|
|
|
let size = last >= 0xD800 && last < 0xDC00 ? chunkSize - 1 : chunkSize;
|
|
|
|
let part = U16.subarray(dataOffset, dataOffset += size);
|
|
|
|
parts.push(String.fromCharCode.apply(String, part));
|
|
|
|
dataRemain -= size;
|
|
|
|
}
|
|
|
|
return parts.join("") + String.fromCharCode.apply(String, U16.subarray(dataOffset, dataOffset + dataRemain));
|
|
|
|
}
|
|
|
|
|
2018-09-18 15:17:44 +02:00
|
|
|
function computeBufferSize(byteLength) {
|
|
|
|
const HEADER_SIZE = 8;
|
|
|
|
return 1 << (32 - Math.clz32(byteLength + HEADER_SIZE - 1));
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Creates a new typed array in the module's memory and returns its pointer. */
|
|
|
|
function newArray(view, length, unsafe) {
|
|
|
|
var ctor = view.constructor;
|
|
|
|
if (ctor === Function) { // TypedArray constructor created in memory
|
|
|
|
ctor = view;
|
|
|
|
view = null;
|
|
|
|
} else { // TypedArray instance copied into memory
|
|
|
|
if (length === undefined) length = view.length;
|
|
|
|
}
|
|
|
|
var elementSize = ctor.BYTES_PER_ELEMENT;
|
|
|
|
if (!elementSize) throw Error("not a typed array");
|
|
|
|
var byteLength = elementSize * length;
|
|
|
|
var ptr = memory_allocate(12); // TypedArray header
|
|
|
|
var buf = memory_allocate(computeBufferSize(byteLength)); // ArrayBuffer
|
|
|
|
checkMem();
|
|
|
|
U32[ ptr >>> 2] = buf; // .buffer
|
|
|
|
U32[(ptr + 4) >>> 2] = 0; // .byteOffset
|
|
|
|
U32[(ptr + 8) >>> 2] = byteLength; // .byteLength
|
|
|
|
U32[ buf >>> 2] = byteLength; // .byteLength
|
|
|
|
U32[(buf + 4) >>> 2] = 0; // 0
|
|
|
|
if (view) {
|
|
|
|
new ctor(mem, buf + 8, length).set(view);
|
|
|
|
if (view.length < length && !unsafe) {
|
|
|
|
let setLength = elementSize * view.length;
|
|
|
|
memory_fill(buf + 8 + setLength, 0, byteLength - setLength);
|
|
|
|
}
|
|
|
|
} else if (!unsafe) {
|
|
|
|
memory_fill(buf + 8, 0, byteLength);
|
|
|
|
}
|
|
|
|
return ptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Gets a view on a typed array in the module's memory by its pointer. */
|
|
|
|
function getArray(ctor, ptr) {
|
|
|
|
var elementSize = ctor.BYTES_PER_ELEMENT;
|
|
|
|
if (!elementSize) throw Error("not a typed array");
|
|
|
|
checkMem();
|
|
|
|
var buf = U32[ ptr >>> 2];
|
|
|
|
var byteOffset = U32[(ptr + 4) >>> 2];
|
|
|
|
var byteLength = U32[(ptr + 8) >>> 2];
|
|
|
|
return new ctor(mem, buf + 8 + byteOffset, (byteLength - byteOffset) / elementSize);
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Frees a typed array in the module's memory. Must not be accessed anymore afterwards. */
|
|
|
|
function freeArray(ptr) {
|
|
|
|
checkMem();
|
|
|
|
var buf = U32[ptr >>> 2];
|
|
|
|
memory_free(buf);
|
|
|
|
memory_free(ptr);
|
|
|
|
}
|
|
|
|
|
2018-05-19 13:38:47 +02:00
|
|
|
// Demangle exports and provide the usual utility on the prototype
|
|
|
|
return demangle(exports, {
|
|
|
|
get I8() { checkMem(); return I8; },
|
|
|
|
get U8() { checkMem(); return U8; },
|
|
|
|
get I16() { checkMem(); return I16; },
|
|
|
|
get U16() { checkMem(); return U16; },
|
|
|
|
get I32() { checkMem(); return I32; },
|
|
|
|
get U32() { checkMem(); return U32; },
|
2018-06-11 15:18:13 +02:00
|
|
|
get I64() { checkMem(); return I64; },
|
|
|
|
get U64() { checkMem(); return U64; },
|
2018-05-19 13:38:47 +02:00
|
|
|
get F32() { checkMem(); return F32; },
|
|
|
|
get F64() { checkMem(); return F64; },
|
|
|
|
newString,
|
2018-09-18 15:17:44 +02:00
|
|
|
getString,
|
|
|
|
newArray,
|
|
|
|
getArray,
|
|
|
|
freeArray
|
2018-05-19 13:38:47 +02:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
exports.instantiate = instantiate;
|
|
|
|
|
|
|
|
/** Instantiates an AssemblyScript module from a buffer using the specified imports. */
|
|
|
|
function instantiateBuffer(buffer, imports) {
|
|
|
|
var module = new WebAssembly.Module(buffer);
|
|
|
|
return instantiate(module, imports);
|
|
|
|
}
|
|
|
|
|
|
|
|
exports.instantiateBuffer = instantiateBuffer;
|
|
|
|
|
|
|
|
/** Instantiates an AssemblyScript module from a response using the specified imports. */
|
|
|
|
async function instantiateStreaming(response, imports) {
|
|
|
|
var module = await WebAssembly.compileStreaming(response);
|
|
|
|
return instantiate(module, imports);
|
|
|
|
}
|
|
|
|
|
|
|
|
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 hasOwnProperty(elem, prop) {
|
|
|
|
return Object.prototype.hasOwnProperty.call(elem, prop);
|
|
|
|
}
|
|
|
|
for (let internalName in exports) {
|
|
|
|
if (!hasOwnProperty(exports, internalName)) continue;
|
|
|
|
let elem = exports[internalName];
|
|
|
|
let parts = internalName.split(".");
|
|
|
|
let curr = module;
|
|
|
|
while (parts.length > 1) {
|
|
|
|
let part = parts.shift();
|
|
|
|
if (!hasOwnProperty(curr, part)) curr[part] = {};
|
|
|
|
curr = curr[part];
|
|
|
|
}
|
|
|
|
let name = parts[0];
|
|
|
|
let hash = name.indexOf("#");
|
|
|
|
if (hash >= 0) {
|
|
|
|
let className = name.substring(0, hash);
|
|
|
|
let classElem = curr[className];
|
|
|
|
if (typeof classElem === "undefined" || !classElem.prototype) {
|
|
|
|
let ctor = function(...args) {
|
|
|
|
return ctor.wrap(ctor.prototype.constructor(...args));
|
|
|
|
};
|
|
|
|
ctor.prototype = {};
|
|
|
|
ctor.wrap = function(thisValue) {
|
|
|
|
return Object.create(ctor.prototype, { "this": { value: thisValue, writable: false } });
|
|
|
|
};
|
|
|
|
if (classElem) Object.getOwnPropertyNames(classElem).forEach(name =>
|
|
|
|
Object.defineProperty(ctor, name, Object.getOwnPropertyDescriptor(classElem, name))
|
|
|
|
);
|
|
|
|
curr[className] = ctor;
|
|
|
|
}
|
|
|
|
name = name.substring(hash + 1);
|
|
|
|
curr = curr[className].prototype;
|
|
|
|
if (/^(get|set):/.test(name)) {
|
|
|
|
if (!hasOwnProperty(curr, name = name.substring(4))) {
|
|
|
|
let getter = exports[internalName.replace("set:", "get:")];
|
|
|
|
let setter = exports[internalName.replace("get:", "set:")];
|
|
|
|
Object.defineProperty(curr, name, {
|
|
|
|
get: function() { return getter(this.this); },
|
|
|
|
set: function(value) { setter(this.this, value); },
|
|
|
|
enumerable: true
|
|
|
|
});
|
|
|
|
}
|
|
|
|
} else curr[name] = function(...args) { return elem(this.this, ...args); };
|
|
|
|
} else {
|
|
|
|
if (/^(get|set):/.test(name)) {
|
|
|
|
if (!hasOwnProperty(curr, name = name.substring(4))) {
|
|
|
|
Object.defineProperty(curr, name, {
|
|
|
|
get: exports[internalName.replace("set:", "get:")],
|
|
|
|
set: exports[internalName.replace("get:", "set:")],
|
|
|
|
enumerable: true
|
|
|
|
});
|
|
|
|
}
|
|
|
|
} else curr[name] = elem;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return module;
|
|
|
|
}
|
|
|
|
|
|
|
|
exports.demangle = demangle;
|