"use strict"; // Runtime header offsets const ID_OFFSET = -8; const SIZE_OFFSET = -4; // Runtime ids const ARRAYBUFFER_ID = 0; const STRING_ID = 1; const ARRAYBUFFERVIEW_ID = 2; // Runtime type information const ARRAY = 1 << 0; const SET = 1 << 1; const MAP = 1 << 2; const VAL_ALIGN = 1 << 4; const VAL_NULLABLE = 1 << 9; const VAL_MANAGED = 1 << 10; const KEY_ALIGN = 1 << 11; const KEY_NULLABLE = 1 << 16; const KEY_MANAGED = 1 << 17; // ArrayBufferView layout const ABV_BUFFER_OFFSET = 0; const ABV_DATASTART_OFFSET = 4; const ABV_DATALENGTH_OFFSET = 8; const ABV_SIZE = 12; const BIGINT = typeof BigUint64Array !== "undefined"; const THIS = Symbol(); /** Gets a string from an U32 and an U16 view on a memory. */ function getStringImpl(U32, U16, ptr) { var length = U32[(ptr + SIZE_OFFSET) >>> 2] >>> 1; var offset = ptr >>> 1; var parts = []; const chunkSize = 1024; while (length > chunkSize) { let last = U16[offset + chunkSize - 1]; let size = last >= 0xD800 && last < 0xDC00 ? chunkSize - 1 : chunkSize; let part = U16.subarray(offset, offset += size); parts.push(String.fromCharCode.apply(String, part)); length -= size; } return parts.join("") + String.fromCharCode.apply(String, U16.subarray(offset, offset + length)); } /** Prepares the base module prior to instantiation. */ function preInstantiate(imports) { var baseModule = {}; function getString(memory, ptr) { if (!memory) return ""; var buffer = memory.buffer; return getStringImpl(new Uint32Array(buffer), new Uint16Array(buffer), ptr); } // add common imports used by stdlib for convenience var env = (imports.env = imports.env || {}); env.abort = env.abort || function abort(mesg, file, line, colm) { var memory = baseModule.memory || env.memory; // prefer exported, otherwise try imported throw Error("abort: " + getString(memory, mesg) + " at " + getString(memory, file) + ":" + line + ":" + colm); } env.trace = env.trace || function trace(mesg, n) { var memory = baseModule.memory || env.memory; console.log("trace: " + getString(memory, mesg) + (n ? " " : "") + Array.prototype.slice.call(arguments, 2, 2 + n).join(", ")); } imports.Math = imports.Math || Math; imports.Date = imports.Date || Date; return baseModule; } /** Prepares the final module once instantiation is complete. */ function postInstantiate(baseModule, instance) { var rawExports = instance.exports; var memory = rawExports.memory; var table = rawExports.table; var alloc = rawExports["__alloc"]; var retain = rawExports["__retain"]; var rtti = rawExports["__rtti"] | 0; // Provide views for all sorts of basic values var buffer, I8, U8, I16, U16, I32, U32, F32, F64, I64, U64; /** Updates memory views if memory has grown meanwhile. */ function checkMem() { // see: https://github.com/WebAssembly/design/issues/1210 if (buffer !== memory.buffer) { buffer = memory.buffer; I8 = new Int8Array(buffer); U8 = new Uint8Array(buffer); I16 = new Int16Array(buffer); U16 = new Uint16Array(buffer); I32 = new Int32Array(buffer); U32 = new Uint32Array(buffer); if (BIGINT) { I64 = new BigInt64Array(buffer); U64 = new BigUint64Array(buffer); } F32 = new Float32Array(buffer); F64 = new Float64Array(buffer); } } checkMem(); /** Gets the runtime type info for the given id. */ function getInfo(id) { const count = U32[rtti >>> 2]; if ((id >>>= 0) >= count) throw Error("invalid id: " + id); return U32[(rtti + 4 >>> 2) + id * 2]; } /** Gets the runtime base id for the given id. */ function getBase(id) { const count = U32[rtti >>> 2]; if ((id >>>= 0) >= count) throw Error("invalid id: " + id); return U32[(rtti + 4 >>> 2) + id * 2 + 1]; } /** Gets the runtime alignment of a collection's values or keys. */ function getAlign(which, info) { return 31 - Math.clz32((info / which) & 31); } /** Allocates a new string in the module's memory and returns its retained pointer. */ function newString(str) { const length = str.length; const ptr = alloc(length << 1, STRING_ID); checkMem(); for (let i = 0, j = ptr >>> 1; i < length; ++i) U16[j + i] = str.charCodeAt(i); return retain(ptr); } baseModule.newString = newString; /** Reads a string from the module's memory by its pointer. */ function getString(ptr) { checkMem(); const id = U32[ptr + ID_OFFSET >>> 2]; if (id !== STRING_ID) throw Error("not a string: " + ptr); return getStringImpl(U32, U16, ptr); } baseModule.getString = getString; /** Allocates a new array in the module's memory and returns its retained pointer. */ function newArray(id, values) { const info = getInfo(id); if (!(info & ARRAY)) throw Error("not an array: " + id + " @ " + info); const align = getAlign(VAL_ALIGN, info); const length = values.length; const buf = alloc(length << align, ARRAYBUFFER_ID); const arr = alloc(ABV_SIZE, id); checkMem(); U32[arr + ABV_BUFFER_OFFSET >>> 2] = retain(buf); U32[arr + ABV_DATASTART_OFFSET >>> 2] = buf; U32[arr + ABV_DATALENGTH_OFFSET >>> 2] = length << align; var view; switch (align) { case 0: view = U8; break; case 1: view = U16; break; case 2: view = U32; break; case 3: view = U64; break; default: throw Error("unsupported align: " + align); } for (let i = 0; i < length; ++i) view[(buf >> align) + i] = values[i]; if (info & VAL_MANAGED) for (let i = 0; i < length; ++i) retain(values[i]); return retain(arr); } baseModule.newArray = newArray; /** Gets the values of an array in the module's memory by its pointer. */ function getArray(arr) { checkMem(); const id = U32[arr + ID_OFFSET >>> 2]; const info = getInfo(id); if (!(info & ARRAY)) throw Error("not an array: " + id); const align = getAlign(VAL_ALIGN, info); var buf = U32[arr + ABV_DATASTART_OFFSET >>> 2]; const length = U32[buf + SIZE_OFFSET >>> 2] >>> align; var view; switch (align) { // TODO: signedness/floats case 0: view = U8; break; case 1: view = U16; break; case 2: view = U32; break; case 3: view = U64; break; default: throw Error("unsupported align: " + align); } return Array.from(view.slice(buf >>>= align, buf + length)); } baseModule.getArray = getArray; // Pull basic exports to baseModule so code in preInstantiate can use them baseModule.memory = baseModule.memory || memory; baseModule.table = baseModule.table || table; // Demangle exports and provide the usual utility on the prototype return demangle(rawExports, Object.defineProperties(baseModule, { I8: { get: function() { checkMem(); return I8; } }, U8: { get: function() { checkMem(); return U8; } }, I16: { get: function() { checkMem(); return I16; } }, U16: { get: function() { checkMem(); return U16; } }, I32: { get: function() { checkMem(); return I32; } }, U32: { get: function() { checkMem(); return U32; } }, I64: { get: function() { checkMem(); return I64; } }, U64: { get: function() { checkMem(); return U64; } }, F32: { get: function() { checkMem(); return F32; } }, F64: { get: function() { checkMem(); return F64; } } })); } /** Wraps a WebAssembly function while also taking care of variable arguments. */ function wrapFunction(fn, setargc) { var wrap = (...args) => { setargc(args.length); return fn(...args); } // adding a function to the table with `newFunction` is limited to actual WebAssembly functions, // hence we can't use the wrapper and instead need to provide a reference to the original wrap.original = fn; return wrap; } /** Instantiates an AssemblyScript module using the specified imports. */ function instantiate(module, imports) { return postInstantiate( preInstantiate(imports || (imports = {})), new WebAssembly.Instance(module, imports) ); } exports.instantiate = instantiate; /** Instantiates an AssemblyScript module from a buffer using the specified imports. */ function instantiateBuffer(buffer, imports) { return instantiate(new WebAssembly.Module(buffer), imports); } exports.instantiateBuffer = instantiateBuffer; /** Instantiates an AssemblyScript module from a response using the specified imports. */ async function instantiateStreaming(response, imports) { return postInstantiate( preInstantiate(imports || (imports = {})), (await WebAssembly.instantiateStreaming(response, imports)).instance ); } exports.instantiateStreaming = instantiateStreaming; /** Demangles an AssemblyScript module's exports to a friendly object structure. */ function demangle(exports, baseModule) { var module = baseModule ? Object.create(baseModule) : {}; var setargc = exports["__setargc"] || function() {}; 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(0, ...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 { if (name === 'constructor') { curr[name] = wrapFunction(elem, setargc); } else { // for methods Object.defineProperty(curr, name, { value: function (...args) { setargc(args.length); 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 if (typeof elem === "function") { curr[name] = wrapFunction(elem, setargc); } else { curr[name] = elem; } } } return module; } exports.demangle = demangle;