index.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427
  1. "use strict";
  2. // Runtime header offsets
  3. const ID_OFFSET = -8;
  4. const SIZE_OFFSET = -4;
  5. // Runtime ids
  6. const ARRAYBUFFER_ID = 0;
  7. const STRING_ID = 1;
  8. // const ARRAYBUFFERVIEW_ID = 2;
  9. // Runtime type information
  10. const ARRAYBUFFERVIEW = 1 << 0;
  11. const ARRAY = 1 << 1;
  12. const STATICARRAY = 1 << 2;
  13. // const SET = 1 << 3;
  14. // const MAP = 1 << 4;
  15. const VAL_ALIGN_OFFSET = 6;
  16. // const VAL_ALIGN = 1 << VAL_ALIGN_OFFSET;
  17. const VAL_SIGNED = 1 << 11;
  18. const VAL_FLOAT = 1 << 12;
  19. // const VAL_NULLABLE = 1 << 13;
  20. const VAL_MANAGED = 1 << 14;
  21. // const KEY_ALIGN_OFFSET = 15;
  22. // const KEY_ALIGN = 1 << KEY_ALIGN_OFFSET;
  23. // const KEY_SIGNED = 1 << 20;
  24. // const KEY_FLOAT = 1 << 21;
  25. // const KEY_NULLABLE = 1 << 22;
  26. // const KEY_MANAGED = 1 << 23;
  27. // Array(BufferView) layout
  28. const ARRAYBUFFERVIEW_BUFFER_OFFSET = 0;
  29. const ARRAYBUFFERVIEW_DATASTART_OFFSET = 4;
  30. const ARRAYBUFFERVIEW_DATALENGTH_OFFSET = 8;
  31. const ARRAYBUFFERVIEW_SIZE = 12;
  32. const ARRAY_LENGTH_OFFSET = 12;
  33. const ARRAY_SIZE = 16;
  34. const BIGINT = typeof BigUint64Array !== "undefined";
  35. const THIS = Symbol();
  36. const CHUNKSIZE = 1024;
  37. /** Gets a string from an U32 and an U16 view on a memory. */
  38. function getStringImpl(buffer, ptr) {
  39. const U32 = new Uint32Array(buffer);
  40. const U16 = new Uint16Array(buffer);
  41. let length = U32[(ptr + SIZE_OFFSET) >>> 2] >>> 1;
  42. let offset = ptr >>> 1;
  43. if (length <= CHUNKSIZE) return String.fromCharCode.apply(String, U16.subarray(offset, offset + length));
  44. const parts = [];
  45. do {
  46. const last = U16[offset + CHUNKSIZE - 1];
  47. const size = last >= 0xD800 && last < 0xDC00 ? CHUNKSIZE - 1 : CHUNKSIZE;
  48. parts.push(String.fromCharCode.apply(String, U16.subarray(offset, offset += size)));
  49. length -= size;
  50. } while (length > CHUNKSIZE);
  51. return parts.join("") + String.fromCharCode.apply(String, U16.subarray(offset, offset + length));
  52. }
  53. /** Prepares the base module prior to instantiation. */
  54. function preInstantiate(imports) {
  55. const extendedExports = {};
  56. function getString(memory, ptr) {
  57. if (!memory) return "<yet unknown>";
  58. return getStringImpl(memory.buffer, ptr);
  59. }
  60. // add common imports used by stdlib for convenience
  61. const env = (imports.env = imports.env || {});
  62. env.abort = env.abort || function abort(msg, file, line, colm) {
  63. const memory = extendedExports.memory || env.memory; // prefer exported, otherwise try imported
  64. throw Error("abort: " + getString(memory, msg) + " at " + getString(memory, file) + ":" + line + ":" + colm);
  65. };
  66. env.trace = env.trace || function trace(msg, n) {
  67. const memory = extendedExports.memory || env.memory;
  68. console.log("trace: " + getString(memory, msg) + (n ? " " : "") + Array.prototype.slice.call(arguments, 2, 2 + n).join(", "));
  69. };
  70. env.seed = env.seed || function seed() {
  71. return Date.now();
  72. };
  73. imports.Math = imports.Math || Math;
  74. imports.Date = imports.Date || Date;
  75. return extendedExports;
  76. }
  77. /** Prepares the final module once instantiation is complete. */
  78. function postInstantiate(extendedExports, instance) {
  79. const exports = instance.exports;
  80. const memory = exports.memory;
  81. const table = exports.table;
  82. const alloc = exports["__alloc"];
  83. const retain = exports["__retain"];
  84. const rttiBase = exports["__rtti_base"] || ~0; // oob if not present
  85. /** Gets the runtime type info for the given id. */
  86. function getInfo(id) {
  87. const U32 = new Uint32Array(memory.buffer);
  88. const count = U32[rttiBase >>> 2];
  89. if ((id >>>= 0) >= count) throw Error("invalid id: " + id);
  90. return U32[(rttiBase + 4 >>> 2) + id * 2];
  91. }
  92. /** Gets the runtime base id for the given id. */
  93. function getBase(id) {
  94. const U32 = new Uint32Array(memory.buffer);
  95. const count = U32[rttiBase >>> 2];
  96. if ((id >>>= 0) >= count) throw Error("invalid id: " + id);
  97. return U32[(rttiBase + 4 >>> 2) + id * 2 + 1];
  98. }
  99. /** Gets the runtime alignment of a collection's values. */
  100. function getValueAlign(info) {
  101. return 31 - Math.clz32((info >>> VAL_ALIGN_OFFSET) & 31); // -1 if none
  102. }
  103. /** Gets the runtime alignment of a collection's keys. */
  104. // function getKeyAlign(info) {
  105. // return 31 - Math.clz32((info >>> KEY_ALIGN_OFFSET) & 31); // -1 if none
  106. // }
  107. /** Allocates a new string in the module's memory and returns its retained pointer. */
  108. function __allocString(str) {
  109. const length = str.length;
  110. const ptr = alloc(length << 1, STRING_ID);
  111. const U16 = new Uint16Array(memory.buffer);
  112. for (var i = 0, p = ptr >>> 1; i < length; ++i) U16[p + i] = str.charCodeAt(i);
  113. return ptr;
  114. }
  115. extendedExports.__allocString = __allocString;
  116. /** Reads a string from the module's memory by its pointer. */
  117. function __getString(ptr) {
  118. const buffer = memory.buffer;
  119. const id = new Uint32Array(buffer)[ptr + ID_OFFSET >>> 2];
  120. if (id !== STRING_ID) throw Error("not a string: " + ptr);
  121. return getStringImpl(buffer, ptr);
  122. }
  123. extendedExports.__getString = __getString;
  124. /** Gets the view matching the specified alignment, signedness and floatness. */
  125. function getView(alignLog2, signed, float) {
  126. const buffer = memory.buffer;
  127. if (float) {
  128. switch (alignLog2) {
  129. case 2: return new Float32Array(buffer);
  130. case 3: return new Float64Array(buffer);
  131. }
  132. } else {
  133. switch (alignLog2) {
  134. case 0: return new (signed ? Int8Array : Uint8Array)(buffer);
  135. case 1: return new (signed ? Int16Array : Uint16Array)(buffer);
  136. case 2: return new (signed ? Int32Array : Uint32Array)(buffer);
  137. case 3: return new (signed ? BigInt64Array : BigUint64Array)(buffer);
  138. }
  139. }
  140. throw Error("unsupported align: " + alignLog2);
  141. }
  142. /** Allocates a new array in the module's memory and returns its retained pointer. */
  143. function __allocArray(id, values) {
  144. const info = getInfo(id);
  145. if (!(info & (ARRAYBUFFERVIEW | ARRAY | STATICARRAY))) throw Error("not an array: " + id + ", flags= " + info);
  146. const align = getValueAlign(info);
  147. const length = values.length;
  148. const buf = alloc(length << align, info & STATICARRAY ? id : ARRAYBUFFER_ID);
  149. let result;
  150. if (info & STATICARRAY) {
  151. result = buf;
  152. } else {
  153. const arr = alloc(info & ARRAY ? ARRAY_SIZE : ARRAYBUFFERVIEW_SIZE, id);
  154. const U32 = new Uint32Array(memory.buffer);
  155. U32[arr + ARRAYBUFFERVIEW_BUFFER_OFFSET >>> 2] = retain(buf);
  156. U32[arr + ARRAYBUFFERVIEW_DATASTART_OFFSET >>> 2] = buf;
  157. U32[arr + ARRAYBUFFERVIEW_DATALENGTH_OFFSET >>> 2] = length << align;
  158. if (info & ARRAY) U32[arr + ARRAY_LENGTH_OFFSET >>> 2] = length;
  159. result = arr;
  160. }
  161. const view = getView(align, info & VAL_SIGNED, info & VAL_FLOAT);
  162. if (info & VAL_MANAGED) {
  163. for (let i = 0; i < length; ++i) view[(buf >>> align) + i] = retain(values[i]);
  164. } else {
  165. view.set(values, buf >>> align);
  166. }
  167. return result;
  168. }
  169. extendedExports.__allocArray = __allocArray;
  170. /** Gets a live view on an array's values in the module's memory. Infers the array type from RTTI. */
  171. function __getArrayView(arr) {
  172. const U32 = new Uint32Array(memory.buffer);
  173. const id = U32[arr + ID_OFFSET >>> 2];
  174. const info = getInfo(id);
  175. if (!(info & (ARRAYBUFFERVIEW | ARRAY | STATICARRAY))) throw Error("not an array: " + id + ", flags=" + info);
  176. const align = getValueAlign(info);
  177. let buf = info & STATICARRAY
  178. ? arr
  179. : U32[arr + ARRAYBUFFERVIEW_DATASTART_OFFSET >>> 2];
  180. const length = info & ARRAY
  181. ? U32[arr + ARRAY_LENGTH_OFFSET >>> 2]
  182. : U32[buf + SIZE_OFFSET >>> 2] >>> align;
  183. return getView(align, info & VAL_SIGNED, info & VAL_FLOAT).subarray(buf >>>= align, buf + length);
  184. }
  185. extendedExports.__getArrayView = __getArrayView;
  186. /** Copies an array's values from the module's memory. Infers the array type from RTTI. */
  187. function __getArray(arr) {
  188. const input = __getArrayView(arr);
  189. const len = input.length;
  190. const out = new Array(len);
  191. for (let i = 0; i < len; i++) out[i] = input[i];
  192. return out;
  193. }
  194. extendedExports.__getArray = __getArray;
  195. /** Copies an ArrayBuffer's value from the module's memory. */
  196. function __getArrayBuffer(ptr) {
  197. const buffer = memory.buffer;
  198. const length = new Uint32Array(buffer)[ptr + SIZE_OFFSET >>> 2];
  199. return buffer.slice(ptr, ptr + length);
  200. }
  201. extendedExports.__getArrayBuffer = __getArrayBuffer;
  202. /** Copies a typed array's values from the module's memory. */
  203. function getTypedArray(Type, alignLog2, ptr) {
  204. return new Type(getTypedArrayView(Type, alignLog2, ptr));
  205. }
  206. /** Gets a live view on a typed array's values in the module's memory. */
  207. function getTypedArrayView(Type, alignLog2, ptr) {
  208. const buffer = memory.buffer;
  209. const U32 = new Uint32Array(buffer);
  210. const bufPtr = U32[ptr + ARRAYBUFFERVIEW_DATASTART_OFFSET >>> 2];
  211. return new Type(buffer, bufPtr, U32[bufPtr + SIZE_OFFSET >>> 2] >>> alignLog2);
  212. }
  213. /** Attach a set of get TypedArray and View functions to the exports. */
  214. function attachTypedArrayFunctions(ctor, name, align) {
  215. extendedExports["__get" + name] = getTypedArray.bind(null, ctor, align);
  216. extendedExports["__get" + name + "View"] = getTypedArrayView.bind(null, ctor, align);
  217. }
  218. [
  219. Int8Array,
  220. Uint8Array,
  221. Uint8ClampedArray,
  222. Int16Array,
  223. Uint16Array,
  224. Int32Array,
  225. Uint32Array,
  226. Float32Array,
  227. Float64Array
  228. ].forEach(ctor => {
  229. attachTypedArrayFunctions(ctor, ctor.name, 31 - Math.clz32(ctor.BYTES_PER_ELEMENT));
  230. });
  231. if (BIGINT) {
  232. [BigUint64Array, BigInt64Array].forEach(ctor => {
  233. attachTypedArrayFunctions(ctor, ctor.name.slice(3), 3);
  234. });
  235. }
  236. /** Tests whether an object is an instance of the class represented by the specified base id. */
  237. function __instanceof(ptr, baseId) {
  238. const U32 = new Uint32Array(memory.buffer);
  239. let id = U32[(ptr + ID_OFFSET) >>> 2];
  240. if (id <= U32[rttiBase >>> 2]) {
  241. do {
  242. if (id == baseId) return true;
  243. id = getBase(id);
  244. } while (id);
  245. }
  246. return false;
  247. }
  248. extendedExports.__instanceof = __instanceof;
  249. // Pull basic exports to extendedExports so code in preInstantiate can use them
  250. extendedExports.memory = extendedExports.memory || memory;
  251. extendedExports.table = extendedExports.table || table;
  252. // Demangle exports and provide the usual utility on the prototype
  253. return demangle(exports, extendedExports);
  254. }
  255. function isResponse(src) {
  256. return typeof Response !== "undefined" && src instanceof Response;
  257. }
  258. function isModule(src) {
  259. return src instanceof WebAssembly.Module;
  260. }
  261. /** Asynchronously instantiates an AssemblyScript module from anything that can be instantiated. */
  262. async function instantiate(source, imports = {}) {
  263. if (isResponse(source = await source)) return instantiateStreaming(source, imports);
  264. const module = isModule(source) ? source : await WebAssembly.compile(source);
  265. const extended = preInstantiate(imports);
  266. const instance = await WebAssembly.instantiate(module, imports);
  267. const exports = postInstantiate(extended, instance);
  268. return { module, instance, exports };
  269. }
  270. exports.instantiate = instantiate;
  271. /** Synchronously instantiates an AssemblyScript module from a WebAssembly.Module or binary buffer. */
  272. function instantiateSync(source, imports = {}) {
  273. const module = isModule(source) ? source : new WebAssembly.Module(source);
  274. const extended = preInstantiate(imports);
  275. const instance = new WebAssembly.Instance(module, imports);
  276. const exports = postInstantiate(extended, instance);
  277. return { module, instance, exports };
  278. }
  279. exports.instantiateSync = instantiateSync;
  280. /** Asynchronously instantiates an AssemblyScript module from a response, i.e. as obtained by `fetch`. */
  281. async function instantiateStreaming(source, imports = {}) {
  282. if (!WebAssembly.instantiateStreaming) {
  283. return instantiate(
  284. isResponse(source = await source)
  285. ? source.arrayBuffer()
  286. : source,
  287. imports
  288. );
  289. }
  290. const extended = preInstantiate(imports);
  291. const result = await WebAssembly.instantiateStreaming(source, imports);
  292. const exports = postInstantiate(extended, result.instance);
  293. return { ...result, exports };
  294. }
  295. exports.instantiateStreaming = instantiateStreaming;
  296. /** Demangles an AssemblyScript module's exports to a friendly object structure. */
  297. function demangle(exports, extendedExports = {}) {
  298. extendedExports = Object.create(extendedExports);
  299. const setArgumentsLength = exports["__argumentsLength"]
  300. ? length => { exports["__argumentsLength"].value = length; }
  301. : exports["__setArgumentsLength"] || exports["__setargc"] || (() => { /* nop */ });
  302. for (let internalName in exports) {
  303. if (!Object.prototype.hasOwnProperty.call(exports, internalName)) continue;
  304. const elem = exports[internalName];
  305. let parts = internalName.split(".");
  306. let curr = extendedExports;
  307. while (parts.length > 1) {
  308. let part = parts.shift();
  309. if (!Object.prototype.hasOwnProperty.call(curr, part)) curr[part] = {};
  310. curr = curr[part];
  311. }
  312. let name = parts[0];
  313. let hash = name.indexOf("#");
  314. if (hash >= 0) {
  315. const className = name.substring(0, hash);
  316. const classElem = curr[className];
  317. if (typeof classElem === "undefined" || !classElem.prototype) {
  318. const ctor = function(...args) {
  319. return ctor.wrap(ctor.prototype.constructor(0, ...args));
  320. };
  321. ctor.prototype = {
  322. valueOf: function valueOf() {
  323. return this[THIS];
  324. }
  325. };
  326. ctor.wrap = function(thisValue) {
  327. return Object.create(ctor.prototype, { [THIS]: { value: thisValue, writable: false } });
  328. };
  329. if (classElem) Object.getOwnPropertyNames(classElem).forEach(name =>
  330. Object.defineProperty(ctor, name, Object.getOwnPropertyDescriptor(classElem, name))
  331. );
  332. curr[className] = ctor;
  333. }
  334. name = name.substring(hash + 1);
  335. curr = curr[className].prototype;
  336. if (/^(get|set):/.test(name)) {
  337. if (!Object.prototype.hasOwnProperty.call(curr, name = name.substring(4))) {
  338. let getter = exports[internalName.replace("set:", "get:")];
  339. let setter = exports[internalName.replace("get:", "set:")];
  340. Object.defineProperty(curr, name, {
  341. get: function() { return getter(this[THIS]); },
  342. set: function(value) { setter(this[THIS], value); },
  343. enumerable: true
  344. });
  345. }
  346. } else {
  347. if (name === 'constructor') {
  348. (curr[name] = (...args) => {
  349. setArgumentsLength(args.length);
  350. return elem(...args);
  351. }).original = elem;
  352. } else { // instance method
  353. (curr[name] = function(...args) { // !
  354. setArgumentsLength(args.length);
  355. return elem(this[THIS], ...args);
  356. }).original = elem;
  357. }
  358. }
  359. } else {
  360. if (/^(get|set):/.test(name)) {
  361. if (!Object.prototype.hasOwnProperty.call(curr, name = name.substring(4))) {
  362. Object.defineProperty(curr, name, {
  363. get: exports[internalName.replace("set:", "get:")],
  364. set: exports[internalName.replace("get:", "set:")],
  365. enumerable: true
  366. });
  367. }
  368. } else if (typeof elem === "function" && elem !== setArgumentsLength) {
  369. (curr[name] = (...args) => {
  370. setArgumentsLength(args.length);
  371. return elem(...args);
  372. }).original = elem;
  373. } else {
  374. curr[name] = elem;
  375. }
  376. }
  377. }
  378. return extendedExports;
  379. }
  380. exports.demangle = demangle;