porffor 0.16.0-fe07da0f4 → 0.17.0-048c6f2ee

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
- import { Blocktype, Opcodes, Valtype, PageSize, ValtypeSize } from './wasmSpec.js';
2
- import { ieee754_binary64, signedLEB128, unsignedLEB128, encodeVector } from './encoding.js';
1
+ import { Blocktype, Opcodes, Valtype, ValtypeSize } from './wasmSpec.js';
2
+ import { ieee754_binary64, signedLEB128, unsignedLEB128, encodeVector, read_signedLEB128 } from './encoding.js';
3
3
  import { operatorOpcode } from './expression.js';
4
4
  import { BuiltinFuncs, BuiltinVars, importedFuncs, NULL, UNDEFINED } from './builtins.js';
5
5
  import { PrototypeFuncs } from './prototype.js';
@@ -9,15 +9,16 @@ import * as Rhemyn from '../rhemyn/compile.js';
9
9
  import parse from './parse.js';
10
10
  import { log } from './log.js';
11
11
  import Prefs from './prefs.js';
12
+ import makeAllocator from './allocators.js';
12
13
 
13
14
  let globals = {};
14
- let globalInd = 0;
15
15
  let tags = [];
16
16
  let funcs = [];
17
17
  let exceptions = [];
18
18
  let funcIndex = {};
19
19
  let currentFuncIndex = importedFuncs.length;
20
20
  let builtinFuncs = {}, builtinVars = {}, prototypeFuncs = {};
21
+ let allocator;
21
22
 
22
23
  class TodoError extends Error {
23
24
  constructor(message) {
@@ -32,11 +33,6 @@ const todo = (scope, msg, expectsValue = undefined) => {
32
33
 
33
34
  case 'runtime':
34
35
  return internalThrow(scope, 'TodoError', msg, expectsValue);
35
-
36
- // return [
37
- // ...debug(`todo! ${msg}`),
38
- // [ Opcodes.unreachable ]
39
- // ];
40
36
  }
41
37
  };
42
38
 
@@ -181,12 +177,6 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
181
177
  continue;
182
178
  }
183
179
 
184
- if (asm[0] === 'memory') {
185
- allocPage(scope, 'asm instrinsic');
186
- // todo: add to store/load offset insts
187
- continue;
188
- }
189
-
190
180
  let inst = Opcodes[asm[0].replace('.', '_')];
191
181
  if (inst == null) throw new Error(`inline asm: inst ${asm[0]} not found`);
192
182
 
@@ -274,10 +264,12 @@ const internalThrow = (scope, constructor, message, expectsValue = Prefs.alwaysV
274
264
  argument: {
275
265
  type: 'NewExpression',
276
266
  callee: {
267
+ type: 'Identifier',
277
268
  name: constructor
278
269
  },
279
270
  arguments: [
280
271
  {
272
+ type: 'Literal',
281
273
  value: message
282
274
  }
283
275
  ]
@@ -433,63 +425,12 @@ const concatStrings = (scope, left, right, global, name, assign = false, bytestr
433
425
  const rightLength = localTmp(scope, 'concat_right_length', Valtype.i32);
434
426
  const leftLength = localTmp(scope, 'concat_left_length', Valtype.i32);
435
427
 
436
- if (assign && Prefs.aotPointerOpt) {
437
- const pointer = scope.arrays?.get(name ?? '$undeclared');
438
-
439
- return [
440
- // setup right
441
- ...right,
442
- Opcodes.i32_to_u,
443
- [ Opcodes.local_set, rightPointer ],
444
-
445
- // calculate length
446
- ...number(0, Valtype.i32), // base 0 for store later
447
-
448
- ...number(pointer, Valtype.i32),
449
- [ Opcodes.i32_load, 0, ...unsignedLEB128(0) ],
450
- [ Opcodes.local_tee, leftLength ],
451
-
452
- [ Opcodes.local_get, rightPointer ],
453
- [ Opcodes.i32_load, 0, ...unsignedLEB128(0) ],
454
- [ Opcodes.local_tee, rightLength ],
455
-
456
- [ Opcodes.i32_add ],
457
-
458
- // store length
459
- [ Opcodes.i32_store, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(pointer) ],
460
-
461
- // copy right
462
- // dst = out pointer + length size + current length * sizeof valtype
463
- ...number(pointer + ValtypeSize.i32, Valtype.i32),
464
-
465
- [ Opcodes.local_get, leftLength ],
466
- ...number(bytestrings ? ValtypeSize.i8 : ValtypeSize.i16, Valtype.i32),
467
- [ Opcodes.i32_mul ],
468
- [ Opcodes.i32_add ],
469
-
470
- // src = right pointer + length size
471
- [ Opcodes.local_get, rightPointer ],
472
- ...number(ValtypeSize.i32, Valtype.i32),
473
- [ Opcodes.i32_add ],
474
-
475
- // size = right length * sizeof valtype
476
- [ Opcodes.local_get, rightLength ],
477
- ...number(bytestrings ? ValtypeSize.i8 : ValtypeSize.i16, Valtype.i32),
478
- [ Opcodes.i32_mul ],
479
-
480
- [ ...Opcodes.memory_copy, 0x00, 0x00 ],
481
-
482
- // return new string (page)
483
- ...number(pointer)
484
- ];
485
- }
486
-
487
428
  const leftPointer = localTmp(scope, 'concat_left_pointer', Valtype.i32);
488
429
 
489
430
  // alloc/assign array
490
- const [ , pointer ] = makeArray(scope, {
431
+ const [ out, pointer ] = makeArray(scope, {
491
432
  rawElements: new Array(0)
492
- }, global, name, true, 'i16');
433
+ }, assign ? false : global, assign ? undefined : name, true, 'i16', true);
493
434
 
494
435
  return [
495
436
  // setup left
@@ -503,7 +444,7 @@ const concatStrings = (scope, left, right, global, name, assign = false, bytestr
503
444
  [ Opcodes.local_set, rightPointer ],
504
445
 
505
446
  // calculate length
506
- ...number(0, Valtype.i32), // base 0 for store later
447
+ ...out,
507
448
 
508
449
  [ Opcodes.local_get, leftPointer ],
509
450
  [ Opcodes.i32_load, 0, ...unsignedLEB128(0) ],
@@ -516,11 +457,13 @@ const concatStrings = (scope, left, right, global, name, assign = false, bytestr
516
457
  [ Opcodes.i32_add ],
517
458
 
518
459
  // store length
519
- [ Opcodes.i32_store, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(pointer) ],
460
+ [ Opcodes.i32_store, Math.log2(ValtypeSize.i32) - 1, 0 ],
520
461
 
521
462
  // copy left
522
463
  // dst = out pointer + length size
523
- ...number(pointer + ValtypeSize.i32, Valtype.i32),
464
+ ...pointer,
465
+ ...number(ValtypeSize.i32, Valtype.i32),
466
+ [ Opcodes.i32_add ],
524
467
 
525
468
  // src = left pointer + length size
526
469
  [ Opcodes.local_get, leftPointer ],
@@ -533,7 +476,9 @@ const concatStrings = (scope, left, right, global, name, assign = false, bytestr
533
476
 
534
477
  // copy right
535
478
  // dst = out pointer + length size + left length * sizeof valtype
536
- ...number(pointer + ValtypeSize.i32, Valtype.i32),
479
+ ...pointer,
480
+ ...number(ValtypeSize.i32, Valtype.i32),
481
+ [ Opcodes.i32_add ],
537
482
 
538
483
  [ Opcodes.local_get, leftLength ],
539
484
  ...number(bytestrings ? ValtypeSize.i8 : ValtypeSize.i16, Valtype.i32),
@@ -553,7 +498,8 @@ const concatStrings = (scope, left, right, global, name, assign = false, bytestr
553
498
  [ ...Opcodes.memory_copy, 0x00, 0x00 ],
554
499
 
555
500
  // return new string (page)
556
- ...number(pointer)
501
+ ...pointer,
502
+ Opcodes.i32_from_u
557
503
  ];
558
504
  };
559
505
 
@@ -669,10 +615,10 @@ const compareStrings = (scope, left, right, bytestrings = false) => {
669
615
  };
670
616
 
671
617
  const truthy = (scope, wasm, type, intIn = false, intOut = false, forceTruthyMode = undefined) => {
672
- if (isIntToFloatOp(wasm[wasm.length - 1])) return [
673
- ...wasm,
674
- ...(!intIn && intOut ? [ Opcodes.i32_to_u ] : [])
675
- ];
618
+ // if (isIntToFloatOp(wasm[wasm.length - 1])) return [
619
+ // ...wasm,
620
+ // ...(!intIn && intOut ? [ Opcodes.i32_to_u ] : [])
621
+ // ];
676
622
  // if (isIntOp(wasm[wasm.length - 1])) return [ ...wasm ];
677
623
 
678
624
  // todo/perf: use knownType and custom bytecode here instead of typeSwitch
@@ -1014,12 +960,9 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
1014
960
  [ Opcodes.end ],
1015
961
  ]));
1016
962
 
1017
- // if not already in block, add a block
1018
- // if (endOut.length === 0) {
1019
- startOut.push(stringOnly([ Opcodes.block, Valtype.i32 ]));
1020
- // endOut.push(stringOnly([ Opcodes.end ]));
1021
- endOut.unshift(stringOnly([ Opcodes.end ]));
1022
- // }
963
+ // add a surrounding block
964
+ startOut.push(stringOnly([ Opcodes.block, Valtype.i32 ]));
965
+ endOut.unshift(stringOnly([ Opcodes.end ]));
1023
966
  }
1024
967
 
1025
968
  return finalize([
@@ -1054,7 +997,7 @@ const asmFuncToAsm = (func, scope) => {
1054
997
  });
1055
998
  };
1056
999
 
1057
- const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes = [], globalInits, returns, returnType, localNames = [], globalNames = [], data: _data = [], table = false }) => {
1000
+ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes = [], globalInits = [], returns, returnType, localNames = [], globalNames = [], data: _data = [], table = false }) => {
1058
1001
  const existing = funcs.find(x => x.name === name);
1059
1002
  if (existing) return existing;
1060
1003
 
@@ -1068,7 +1011,7 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
1068
1011
 
1069
1012
  for (const x of _data) {
1070
1013
  const copy = { ...x };
1071
- copy.offset += pages.size * pageSize;
1014
+ if (copy.offset != null) copy.offset += pages.size * pageSize;
1072
1015
  data.push(copy);
1073
1016
  }
1074
1017
 
@@ -1078,7 +1021,7 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
1078
1021
  locals,
1079
1022
  localInd: allLocals.length,
1080
1023
  returns,
1081
- returnType: returnType ?? TYPES.number,
1024
+ returnType,
1082
1025
  internal: true,
1083
1026
  index: currentFuncIndex++,
1084
1027
  table
@@ -1091,9 +1034,9 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
1091
1034
 
1092
1035
  let baseGlobalIdx, i = 0;
1093
1036
  for (const type of globalTypes) {
1094
- if (baseGlobalIdx === undefined) baseGlobalIdx = globalInd;
1037
+ if (baseGlobalIdx === undefined) baseGlobalIdx = globals['#ind'];
1095
1038
 
1096
- globals[globalNames[i] ?? `${name}_global_${i}`] = { idx: globalInd++, type, init: globalInits[i] ?? 0 };
1039
+ globals[globalNames[i] ?? `${name}_global_${i}`] = { idx: globals['#ind']++, type, init: globalInits[i] ?? 0 };
1097
1040
  i++;
1098
1041
  }
1099
1042
 
@@ -1106,11 +1049,15 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
1106
1049
  }
1107
1050
  }
1108
1051
 
1109
- if (table) for (const inst of wasm) {
1110
- if (inst[0] === Opcodes.i32_load16_u && inst.at(-1) === 'read_argc') {
1111
- inst.splice(2, 99);
1112
- inst.push(...unsignedLEB128(allocPage({}, 'func argc lut') * pageSize));
1052
+ if (table) {
1053
+ for (const inst of wasm) {
1054
+ if (inst[0] === Opcodes.i32_load16_u && inst.at(-1) === 'read_argc') {
1055
+ inst.splice(2, 99);
1056
+ inst.push(...unsignedLEB128(allocPage({}, 'func argc lut') * pageSize));
1057
+ }
1113
1058
  }
1059
+
1060
+ funcs.table = true;
1114
1061
  }
1115
1062
 
1116
1063
  func.wasm = wasm;
@@ -1254,7 +1201,7 @@ const getNodeType = (scope, node) => {
1254
1201
  const func = funcs.find(x => x.name === name);
1255
1202
 
1256
1203
  if (func) {
1257
- if (func.returnType) return func.returnType;
1204
+ if (func.returnType != null) return func.returnType;
1258
1205
  }
1259
1206
 
1260
1207
  if (builtinFuncs[name] && !builtinFuncs[name].typedReturns) return builtinFuncs[name].returnType ?? TYPES.number;
@@ -1270,15 +1217,7 @@ const getNodeType = (scope, node) => {
1270
1217
  const func = spl[spl.length - 1];
1271
1218
  const protoFuncs = Object.keys(prototypeFuncs).filter(x => x != TYPES.bytestring && prototypeFuncs[x][func] != null);
1272
1219
  if (protoFuncs.length === 1) {
1273
- if (protoFuncs[0].returnType) return protoFuncs[0].returnType;
1274
- }
1275
-
1276
- if (protoFuncs.length > 0) {
1277
- if (scope.locals['#last_type']) return getLastType(scope);
1278
-
1279
- // presume
1280
- // todo: warn here?
1281
- return TYPES.number;
1220
+ if (protoFuncs[0].returnType != null) return protoFuncs[0].returnType;
1282
1221
  }
1283
1222
  }
1284
1223
 
@@ -1326,7 +1265,15 @@ const getNodeType = (scope, node) => {
1326
1265
  }
1327
1266
 
1328
1267
  if (node.type === 'AssignmentExpression') {
1329
- return getNodeType(scope, node.right);
1268
+ const op = node.operator.slice(0, -1) || '=';
1269
+ if (op === '=') return getNodeType(scope, node.right);
1270
+
1271
+ return getNodeType(scope, {
1272
+ type: ['||', '&&', '??'].includes(op) ? 'LogicalExpression' : 'BinaryExpression',
1273
+ left: node.left,
1274
+ right: node.right,
1275
+ operator: op
1276
+ });
1330
1277
  }
1331
1278
 
1332
1279
  if (node.type === 'ArrayExpression') {
@@ -1345,23 +1292,6 @@ const getNodeType = (scope, node) => {
1345
1292
  if (knownLeft === TYPES.bytestring || knownRight === TYPES.bytestring) return TYPES.bytestring;
1346
1293
 
1347
1294
  return TYPES.number;
1348
-
1349
- // todo: string concat types
1350
- // if (node.operator !== '+') return TYPES.number;
1351
- // else return [
1352
- // // if left is string
1353
- // ...getNodeType(scope, node.left),
1354
- // ...number(TYPES.string, Valtype.i32),
1355
- // [ Opcodes.i32_eq ],
1356
-
1357
- // // if right is string
1358
- // ...getNodeType(scope, node.right),
1359
- // ...number(TYPES.string, Valtype.i32),
1360
- // [ Opcodes.i32_eq ],
1361
-
1362
- // // if either are true
1363
- // [ Opcodes.i32_or ],
1364
- // ];
1365
1295
  }
1366
1296
 
1367
1297
  if (node.type === 'UnaryExpression') {
@@ -1381,7 +1311,6 @@ const getNodeType = (scope, node) => {
1381
1311
  if (Prefs.fastLength) return TYPES.number;
1382
1312
  }
1383
1313
 
1384
-
1385
1314
  const objectKnownType = knownType(scope, getNodeType(scope, node.object));
1386
1315
  if (objectKnownType != null) {
1387
1316
  if (name === 'length') {
@@ -1392,7 +1321,6 @@ const getNodeType = (scope, node) => {
1392
1321
  if (node.computed) {
1393
1322
  if (objectKnownType === TYPES.string) return TYPES.string;
1394
1323
  if (objectKnownType === TYPES.bytestring) return TYPES.bytestring;
1395
- if (objectKnownType === TYPES.array) return TYPES.number;
1396
1324
  }
1397
1325
  }
1398
1326
 
@@ -1458,10 +1386,10 @@ const countLeftover = wasm => {
1458
1386
 
1459
1387
  if (depth === 0)
1460
1388
  if ([Opcodes.throw, Opcodes.drop, Opcodes.local_set, Opcodes.global_set].includes(inst[0])) count--;
1461
- else if ([null, Opcodes.i32_eqz, Opcodes.i64_eqz, Opcodes.f64_ceil, Opcodes.f64_floor, Opcodes.f64_trunc, Opcodes.f64_nearest, Opcodes.f64_sqrt, Opcodes.local_tee, Opcodes.i32_wrap_i64, Opcodes.i64_extend_i32_s, Opcodes.i64_extend_i32_u, Opcodes.f32_demote_f64, Opcodes.f64_promote_f32, Opcodes.f64_convert_i32_s, Opcodes.f64_convert_i32_u, Opcodes.i32_clz, Opcodes.i32_ctz, Opcodes.i32_popcnt, Opcodes.f64_neg, Opcodes.end, Opcodes.i32_trunc_sat_f64_s[0], Opcodes.i32x4_extract_lane, Opcodes.i16x8_extract_lane, Opcodes.i32_load, Opcodes.i64_load, Opcodes.f64_load, Opcodes.v128_load, Opcodes.i32_load16_u, Opcodes.i32_load16_s, Opcodes.i32_load8_u, Opcodes.i32_load8_s, Opcodes.memory_grow].includes(inst[0]) && (inst[0] !== 0xfc || inst[1] < 0x0a)) {}
1389
+ else if ([null, Opcodes.i32_eqz, Opcodes.i64_eqz, Opcodes.f64_ceil, Opcodes.f64_floor, Opcodes.f64_trunc, Opcodes.f64_nearest, Opcodes.f64_sqrt, Opcodes.local_tee, Opcodes.i32_wrap_i64, Opcodes.i64_extend_i32_s, Opcodes.i64_extend_i32_u, Opcodes.f32_demote_f64, Opcodes.f64_promote_f32, Opcodes.f64_convert_i32_s, Opcodes.f64_convert_i32_u, Opcodes.i32_clz, Opcodes.i32_ctz, Opcodes.i32_popcnt, Opcodes.f64_neg, Opcodes.end, Opcodes.i32_trunc_sat_f64_s[0], Opcodes.i32x4_extract_lane, Opcodes.i16x8_extract_lane, Opcodes.i32_load, Opcodes.i64_load, Opcodes.f64_load, Opcodes.v128_load, Opcodes.i32_load16_u, Opcodes.i32_load16_s, Opcodes.i32_load8_u, Opcodes.i32_load8_s, Opcodes.memory_grow].includes(inst[0]) && (inst[0] !== 0xfc || inst[1] < 0x04)) {}
1462
1390
  else if ([Opcodes.local_get, Opcodes.global_get, Opcodes.f64_const, Opcodes.i32_const, Opcodes.i64_const, Opcodes.v128_const, Opcodes.memory_size].includes(inst[0])) count++;
1463
1391
  else if ([Opcodes.i32_store, Opcodes.i64_store, Opcodes.f64_store, Opcodes.i32_store16, Opcodes.i32_store8].includes(inst[0])) count -= 2;
1464
- else if (Opcodes.memory_copy[0] === inst[0] && Opcodes.memory_copy[1] === inst[1]) count -= 3;
1392
+ else if (inst[0] === Opcodes.memory_copy[0] && (inst[1] === Opcodes.memory_copy[1] || inst[1] === Opcodes.memory_init[1])) count -= 3;
1465
1393
  else if (inst[0] === Opcodes.return) count = 0;
1466
1394
  else if (inst[0] === Opcodes.call) {
1467
1395
  let func = funcs.find(x => x.index === inst[1]);
@@ -1733,7 +1661,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1733
1661
  }, generate(scope, decl.arguments[0] ?? DEFAULT_VALUE), getNodeType(scope, decl.arguments[0] ?? DEFAULT_VALUE), protoLocal, protoLocal2, (length, itemType) => {
1734
1662
  return makeArray(scope, {
1735
1663
  rawElements: new Array(length)
1736
- }, _global, _name, true, itemType);
1664
+ }, _global, _name, true, itemType, true);
1737
1665
  }, () => {
1738
1666
  optUnused = true;
1739
1667
  return unusedValue;
@@ -1815,7 +1743,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1815
1743
  f64_store: { imms: 2, args: [ true, false ], returns: 0 },
1816
1744
 
1817
1745
  // value
1818
- i32_const: { imms: 1, args: [], returns: 1 },
1746
+ i32_const: { imms: 1, args: [], returns: 0 },
1819
1747
  };
1820
1748
 
1821
1749
  const opName = name.slice('__Porffor_wasm_'.length);
@@ -1900,7 +1828,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1900
1828
  if (indirectMode === 'strict') {
1901
1829
  return typeSwitch(scope, getNodeType(scope, decl.callee), {
1902
1830
  [TYPES.function]: [
1903
- ...argWasm,
1831
+ ...out,
1904
1832
  [ global ? Opcodes.global_get : Opcodes.local_get, local.idx ],
1905
1833
  Opcodes.i32_to_u,
1906
1834
  [ Opcodes.call_indirect, args.length, 0 ],
@@ -1978,7 +1906,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1978
1906
  const func = funcs[idx - importedFuncs.length]; // idx === scope.index ? scope : funcs.find(x => x.index === idx);
1979
1907
  const userFunc = func && !func.internal;
1980
1908
  const typedParams = userFunc || builtinFuncs[name]?.typedParams;
1981
- const typedReturns = (func && func.returnType == null) || builtinFuncs[name]?.typedReturns;
1909
+ const typedReturns = (userFunc && func.returnType == null) || builtinFuncs[name]?.typedReturns;
1982
1910
  const paramCount = func && (typedParams ? func.params.length / 2 : func.params.length);
1983
1911
 
1984
1912
  let args = decl.arguments;
@@ -2006,13 +1934,18 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
2006
1934
  continue;
2007
1935
  }
2008
1936
 
2009
- if (valtypeBinary !== Valtype.i32 && (
2010
- (builtinFuncs[name] && builtinFuncs[name].params[i * (typedParams ? 2 : 1)] === Valtype.i32) ||
2011
- (importedFuncs[name] && name.startsWith('profile'))
2012
- )) {
1937
+ if (valtypeBinary !== Valtype.i32 &&
1938
+ (func && func.params[i * (typedParams ? 2 : 1)] === Valtype.i32)
1939
+ ) {
2013
1940
  out.push(Opcodes.i32_to);
2014
1941
  }
2015
1942
 
1943
+ if (valtypeBinary === Valtype.i32 &&
1944
+ (func && func.params[i * (typedParams ? 2 : 1)] === Valtype.f64)
1945
+ ) {
1946
+ out.push([ Opcodes.f64_convert_i32_s ]);
1947
+ }
1948
+
2016
1949
  if (typedParams) out = out.concat(getNodeType(scope, arg));
2017
1950
  }
2018
1951
 
@@ -2034,6 +1967,10 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
2034
1967
  out.push(Opcodes.i32_from);
2035
1968
  }
2036
1969
 
1970
+ if (builtinFuncs[name] && builtinFuncs[name].returns?.[0] === Valtype.f64 && valtypeBinary === Valtype.i32) {
1971
+ out.push(Opcodes.i32_trunc_sat_f64_s);
1972
+ }
1973
+
2037
1974
  return out;
2038
1975
  };
2039
1976
 
@@ -2057,9 +1994,9 @@ const generateNew = (scope, decl, _global, _name) => {
2057
1994
  if (
2058
1995
  (builtinFuncs[name] && !builtinFuncs[name].constr) ||
2059
1996
  (internalConstrs[name] && builtinFuncs[name].notConstr)
2060
- ) return internalThrow(scope, 'TypeError', `${name} is not a constructor`);
1997
+ ) return internalThrow(scope, 'TypeError', `${name} is not a constructor`, true);
2061
1998
 
2062
- if (!builtinFuncs[name]) return todo(scope, `new statement is not supported yet`); // return todo(scope, `new statement is not supported yet (new ${unhackName(name)})`);
1999
+ if (!builtinFuncs[name]) return todo(scope, `new statement is not supported yet`, true); // return todo(scope, `new statement is not supported yet (new ${unhackName(name)})`);
2063
2000
 
2064
2001
  return generateCall(scope, decl, _global, _name);
2065
2002
  };
@@ -2123,7 +2060,6 @@ const brTable = (input, bc, returns) => {
2123
2060
  }
2124
2061
 
2125
2062
  for (let i = 0; i < count; i++) {
2126
- // if (i === 0) out.push([ Opcodes.block, returns, 'br table start' ]);
2127
2063
  if (i === 0) out.push([ Opcodes.block, returns ]);
2128
2064
  else out.push([ Opcodes.block, Blocktype.void ]);
2129
2065
  }
@@ -2157,10 +2093,8 @@ const brTable = (input, bc, returns) => {
2157
2093
  [ Opcodes.br_table, ...encodeVector(table), 0 ]
2158
2094
  );
2159
2095
 
2160
- // if you can guess why we sort the wrong way and then reverse
2161
- // (instead of just sorting the correct way)
2162
- // dm me and if you are correct and the first person
2163
- // I will somehow shout you out or something
2096
+ // sort the wrong way and then reverse
2097
+ // so strings ('default') are at the start before any numbers
2164
2098
  const orderedBc = keys.sort((a, b) => b - a).reverse();
2165
2099
 
2166
2100
  br = count - 1;
@@ -2186,10 +2120,10 @@ const typeSwitch = (scope, type, bc, returns = valtypeBinary) => {
2186
2120
  return bc[known] ?? bc.default;
2187
2121
  }
2188
2122
 
2189
- if (Prefs.typeswitchUseBrtable)
2123
+ if (Prefs.typeswitchBrtable)
2190
2124
  return brTable(type, bc, returns);
2191
2125
 
2192
- const tmp = localTmp(scope, '#typeswitch_tmp', Valtype.i32);
2126
+ const tmp = localTmp(scope, '#typeswitch_tmp' + (Prefs.typeswitchUniqueTmp ? randId() : ''), Valtype.i32);
2193
2127
  const out = [
2194
2128
  ...type,
2195
2129
  [ Opcodes.local_set, tmp ],
@@ -2241,11 +2175,11 @@ const allocVar = (scope, name, global = false, type = true) => {
2241
2175
  return target[name].idx;
2242
2176
  }
2243
2177
 
2244
- let idx = global ? globalInd++ : scope.localInd++;
2178
+ let idx = global ? globals['#ind']++ : scope.localInd++;
2245
2179
  target[name] = { idx, type: valtypeBinary };
2246
2180
 
2247
2181
  if (type) {
2248
- let typeIdx = global ? globalInd++ : scope.localInd++;
2182
+ let typeIdx = global ? globals['#ind']++ : scope.localInd++;
2249
2183
  target[name + '#type'] = { idx: typeIdx, type: Valtype.i32, name };
2250
2184
  }
2251
2185
 
@@ -2338,24 +2272,10 @@ const generateVar = (scope, decl) => {
2338
2272
  }
2339
2273
 
2340
2274
  if (x.init) {
2341
- // if (isFuncType(x.init.type)) {
2342
- // // let a = function () { ... }
2343
- // x.init.id = { name };
2344
-
2345
- // const func = generateFunc(scope, x.init);
2346
-
2347
- // out.push(
2348
- // ...number(func.index - importedFuncs.length),
2349
- // [ global ? Opcodes.global_set : Opcodes.local_set, idx ],
2350
-
2351
- // ...setType(scope, name, TYPES.function)
2352
- // );
2353
-
2354
- // continue;
2355
- // }
2275
+ const alreadyArray = scope.arrays?.get(name) != null;
2356
2276
 
2357
2277
  const generated = generate(scope, x.init, global, name);
2358
- if (scope.arrays?.get(name) != null) {
2278
+ if (!alreadyArray && scope.arrays?.get(name) != null) {
2359
2279
  // hack to set local as pointer before
2360
2280
  out.push(...number(scope.arrays.get(name)), [ global ? Opcodes.global_set : Opcodes.local_set, idx ]);
2361
2281
  if (generated.at(-1) == Opcodes.i32_from_u) generated.pop();
@@ -2407,19 +2327,12 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
2407
2327
 
2408
2328
  // hack: .length setter
2409
2329
  if (decl.left.type === 'MemberExpression' && decl.left.property.name === 'length') {
2410
- const name = decl.left.object.name;
2411
- const pointer = scope.arrays?.get(name);
2412
-
2413
- const aotPointer = Prefs.aotPointerOpt && pointer != null;
2414
-
2415
2330
  const newValueTmp = localTmp(scope, '__length_setter_tmp');
2416
2331
  const pointerTmp = op === '=' ? null : localTmp(scope, '__member_setter_ptr_tmp', Valtype.i32);
2417
2332
 
2418
2333
  return [
2419
- ...(aotPointer ? number(0, Valtype.i32) : [
2420
- ...generate(scope, decl.left.object),
2421
- Opcodes.i32_to_u
2422
- ]),
2334
+ ...generate(scope, decl.left.object),
2335
+ Opcodes.i32_to_u,
2423
2336
  ...(!pointerTmp ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
2424
2337
 
2425
2338
  ...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
@@ -2430,7 +2343,7 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
2430
2343
  [ Opcodes.local_tee, newValueTmp ],
2431
2344
 
2432
2345
  Opcodes.i32_to_u,
2433
- [ Opcodes.i32_store, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(aotPointer ? pointer : 0) ],
2346
+ [ Opcodes.i32_store, Math.log2(ValtypeSize.i32) - 1, 0 ],
2434
2347
 
2435
2348
  [ Opcodes.local_get, newValueTmp ]
2436
2349
  ];
@@ -2438,21 +2351,14 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
2438
2351
 
2439
2352
  // arr[i]
2440
2353
  if (decl.left.type === 'MemberExpression' && decl.left.computed) {
2441
- const name = decl.left.object.name;
2442
- const pointer = scope.arrays?.get(name);
2443
-
2444
- const aotPointer = Prefs.aotPointerOpt && pointer != null;
2445
-
2446
2354
  const newValueTmp = localTmp(scope, '__member_setter_val_tmp');
2447
2355
  const pointerTmp = op === '=' ? -1 : localTmp(scope, '__member_setter_ptr_tmp', Valtype.i32);
2448
2356
 
2449
2357
  return [
2450
2358
  ...typeSwitch(scope, getNodeType(scope, decl.left.object), {
2451
2359
  [TYPES.array]: [
2452
- ...(aotPointer ? [] : [
2453
- ...generate(scope, decl.left.object),
2454
- Opcodes.i32_to_u
2455
- ]),
2360
+ ...generate(scope, decl.left.object),
2361
+ Opcodes.i32_to_u,
2456
2362
 
2457
2363
  // get index as valtype
2458
2364
  ...generate(scope, decl.left.property),
@@ -2461,39 +2367,22 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
2461
2367
  // turn into byte offset by * valtypeSize (4 for i32, 8 for i64/f64)
2462
2368
  ...number(ValtypeSize[valtype] + 1, Valtype.i32),
2463
2369
  [ Opcodes.i32_mul ],
2464
- ...(aotPointer ? [] : [ [ Opcodes.i32_add ] ]),
2370
+ [ Opcodes.i32_add ],
2465
2371
  ...(op === '=' ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
2466
2372
 
2467
2373
  ...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
2468
2374
  [ Opcodes.local_get, pointerTmp ],
2469
- [ Opcodes.load, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ]
2375
+ [ Opcodes.load, 0, ValtypeSize.i32 ]
2470
2376
  ], generate(scope, decl.right), [
2471
2377
  [ Opcodes.local_get, pointerTmp ],
2472
- [ Opcodes.i32_load8_u, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32 + ValtypeSize[valtype]) ]
2378
+ [ Opcodes.i32_load8_u, 0, ValtypeSize.i32 + ValtypeSize[valtype] ]
2473
2379
  ], getNodeType(scope, decl.right), false, name, true)),
2474
2380
  [ Opcodes.local_tee, newValueTmp ],
2475
2381
 
2476
- [ Opcodes.store, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ]
2382
+ [ Opcodes.store, 0, ValtypeSize.i32 ]
2477
2383
  ],
2478
2384
 
2479
2385
  default: internalThrow(scope, 'TypeError', `Cannot assign member with non-array`)
2480
-
2481
- // [TYPES.string]: [
2482
- // // turn into byte offset by * sizeof i16
2483
- // ...number(ValtypeSize.i16, Valtype.i32),
2484
- // [ Opcodes.i32_mul ],
2485
- // ...(aotPointer ? [] : [ [ Opcodes.i32_add ] ]),
2486
- // ...(op === '=' ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
2487
-
2488
- // ...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
2489
- // [ Opcodes.local_get, pointerTmp ],
2490
- // [ Opcodes.load, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ]
2491
- // ], generate(scope, decl.right), number(TYPES.string, Valtype.i32), getNodeType(scope, decl.right))),
2492
- // [ Opcodes.local_tee, newValueTmp ],
2493
-
2494
- // Opcodes.i32_to_u,
2495
- // [ StoreOps.i16, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ]
2496
- // ]
2497
2386
  }, Blocktype.void),
2498
2387
 
2499
2388
  [ Opcodes.local_get, newValueTmp ]
@@ -2555,9 +2444,7 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
2555
2444
  [ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx ],
2556
2445
  [ isGlobal ? Opcodes.global_get : Opcodes.local_get, local.idx ],
2557
2446
 
2558
- // todo: string concat types
2559
-
2560
- ...setType(scope, name, TYPES.number)
2447
+ ...setType(scope, name, getNodeType(scope, decl))
2561
2448
  ];
2562
2449
  };
2563
2450
 
@@ -2571,7 +2458,7 @@ const generateUnary = (scope, decl) => {
2571
2458
  // * -1
2572
2459
 
2573
2460
  if (decl.prefix && decl.argument.type === 'Literal' && typeof decl.argument.value === 'number') {
2574
- // if -<N>, just return that
2461
+ // if -n, just return that as a const
2575
2462
  return number(-1 * decl.argument.value);
2576
2463
  }
2577
2464
 
@@ -2583,14 +2470,14 @@ const generateUnary = (scope, decl) => {
2583
2470
  case '!':
2584
2471
  const arg = decl.argument;
2585
2472
  if (arg.type === 'UnaryExpression' && arg.operator === '!') {
2586
- // !!x -> is x truthy
2473
+ // opt: !!x -> is x truthy
2587
2474
  return truthy(scope, generate(scope, arg.argument), getNodeType(scope, arg.argument), false, false);
2588
2475
  }
2476
+
2589
2477
  // !=
2590
- return falsy(scope, generate(scope, decl.argument), getNodeType(scope, decl.argument), false, false);
2478
+ return falsy(scope, generate(scope, arg), getNodeType(scope, arg), false, false);
2591
2479
 
2592
2480
  case '~':
2593
- // todo: does not handle Infinity properly (should convert to 0) (but opt const converting saves us sometimes)
2594
2481
  return [
2595
2482
  ...generate(scope, decl.argument),
2596
2483
  Opcodes.i32_to,
@@ -2877,12 +2764,12 @@ const generateForOf = (scope, decl) => {
2877
2764
 
2878
2765
  // // todo: we should only do this for strings but we don't know at compile-time :(
2879
2766
  // hack: this is naughty and will break things!
2880
- let newOut = number(0, Valtype.f64), newPointer = -1;
2767
+ let newOut = number(0, Valtype.i32), newPointer = number(0, Valtype.i32);
2881
2768
  if (pages.hasAnyString) {
2882
2769
  // todo: we use i16 even for bytestrings which should not make a bad thing happen, just be confusing for debugging?
2883
2770
  0, [ newOut, newPointer ] = makeArray(scope, {
2884
- rawElements: new Array(1)
2885
- }, isGlobal, leftName, true, 'i16');
2771
+ rawElements: new Array(0)
2772
+ }, isGlobal, leftName, true, 'i16', true);
2886
2773
  }
2887
2774
 
2888
2775
  // set type for local
@@ -2929,23 +2816,28 @@ const generateForOf = (scope, decl) => {
2929
2816
  [TYPES.string]: [
2930
2817
  ...setType(scope, leftName, TYPES.string),
2931
2818
 
2932
- [ Opcodes.loop, Blocktype.void ],
2933
-
2934
2819
  // setup new/out array
2935
2820
  ...newOut,
2936
- [ Opcodes.drop ],
2937
2821
 
2938
- ...number(0, Valtype.i32), // base 0 for store after
2822
+ // set length to 1
2823
+ ...number(1, Valtype.i32),
2824
+ [ Opcodes.i32_store, 0, 0 ],
2825
+
2826
+ [ Opcodes.loop, Blocktype.void ],
2827
+
2828
+ // use as pointer for store later
2829
+ ...newPointer,
2939
2830
 
2940
2831
  // load current string ind {arg}
2941
2832
  [ Opcodes.local_get, pointer ],
2942
- [ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128(ValtypeSize.i32) ],
2833
+ [ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1, ValtypeSize.i32 ],
2943
2834
 
2944
2835
  // store to new string ind 0
2945
- [ Opcodes.i32_store16, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128(newPointer + ValtypeSize.i32) ],
2836
+ [ Opcodes.i32_store16, Math.log2(ValtypeSize.i16) - 1, ValtypeSize.i32 ],
2946
2837
 
2947
2838
  // return new string (page)
2948
- ...number(newPointer),
2839
+ ...newPointer,
2840
+ Opcodes.i32_from_u,
2949
2841
 
2950
2842
  [ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx ],
2951
2843
 
@@ -2977,25 +2869,30 @@ const generateForOf = (scope, decl) => {
2977
2869
  [TYPES.bytestring]: [
2978
2870
  ...setType(scope, leftName, TYPES.bytestring),
2979
2871
 
2980
- [ Opcodes.loop, Blocktype.void ],
2981
-
2982
2872
  // setup new/out array
2983
2873
  ...newOut,
2984
- [ Opcodes.drop ],
2985
2874
 
2986
- ...number(0, Valtype.i32), // base 0 for store after
2875
+ // set length to 1
2876
+ ...number(1, Valtype.i32),
2877
+ [ Opcodes.i32_store, 0, 0 ],
2878
+
2879
+ [ Opcodes.loop, Blocktype.void ],
2880
+
2881
+ // use as pointer for store later
2882
+ ...newPointer,
2987
2883
 
2988
2884
  // load current string ind {arg}
2989
2885
  [ Opcodes.local_get, pointer ],
2990
2886
  [ Opcodes.local_get, counter ],
2991
2887
  [ Opcodes.i32_add ],
2992
- [ Opcodes.i32_load8_u, 0, ...unsignedLEB128(ValtypeSize.i32) ],
2888
+ [ Opcodes.i32_load8_u, 0, ValtypeSize.i32 ],
2993
2889
 
2994
2890
  // store to new string ind 0
2995
- [ Opcodes.i32_store8, 0, ...unsignedLEB128(newPointer + ValtypeSize.i32) ],
2891
+ [ Opcodes.i32_store8, 0, ValtypeSize.i32 ],
2996
2892
 
2997
2893
  // return new string (page)
2998
- ...number(newPointer),
2894
+ ...newPointer,
2895
+ Opcodes.i32_from_u,
2999
2896
 
3000
2897
  [ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx ],
3001
2898
 
@@ -3134,32 +3031,46 @@ const generateLabel = (scope, decl) => {
3134
3031
  const generateThrow = (scope, decl) => {
3135
3032
  scope.throws = true;
3136
3033
 
3137
- let message = decl.argument.value, constructor = null;
3034
+ const exceptionMode = Prefs.exceptionMode ?? 'lut';
3035
+ if (exceptionMode === 'lut') {
3036
+ let message = decl.argument.value, constructor = null;
3138
3037
 
3139
- // hack: throw new X("...") -> throw "..."
3140
- if (!message && (decl.argument.type === 'NewExpression' || decl.argument.type === 'CallExpression')) {
3141
- constructor = decl.argument.callee.name;
3142
- message = decl.argument.arguments[0]?.value ?? '';
3143
- }
3038
+ // support `throw (new)? Error(...)`
3039
+ if (!message && (decl.argument.type === 'NewExpression' || decl.argument.type === 'CallExpression')) {
3040
+ constructor = decl.argument.callee.name;
3041
+ message = decl.argument.arguments[0]?.value ?? '';
3042
+ }
3144
3043
 
3145
- if (tags.length === 0) tags.push({
3146
- params: [ Valtype.i32 ],
3147
- results: [],
3148
- idx: tags.length
3149
- });
3044
+ if (tags.length === 0) tags.push({
3045
+ params: [ Valtype.i32 ],
3046
+ results: [],
3047
+ idx: tags.length
3048
+ });
3150
3049
 
3151
- let exceptId = exceptions.push({ constructor, message }) - 1;
3152
- let tagIdx = tags[0].idx;
3050
+ let exceptId = exceptions.push({ constructor, message }) - 1;
3153
3051
 
3154
- scope.exceptions ??= [];
3155
- scope.exceptions.push(exceptId);
3052
+ scope.exceptions ??= [];
3053
+ scope.exceptions.push(exceptId);
3156
3054
 
3157
- // todo: write a description of how this works lol
3055
+ return [
3056
+ [ Opcodes.i32_const, signedLEB128(exceptId) ],
3057
+ [ Opcodes.throw, tags[0].idx ]
3058
+ ];
3059
+ }
3158
3060
 
3159
- return [
3160
- [ Opcodes.i32_const, signedLEB128(exceptId) ],
3161
- [ Opcodes.throw, tagIdx ]
3162
- ];
3061
+ if (exceptionMode === 'stack') {
3062
+ if (tags.length === 0) tags.push({
3063
+ params: [ valtypeBinary, Valtype.i32 ],
3064
+ results: [],
3065
+ idx: tags.length
3066
+ });
3067
+
3068
+ return [
3069
+ ...generate(scope, decl.argument),
3070
+ ...getNodeType(scope, decl.argument),
3071
+ [ Opcodes.throw, tags[0].idx ]
3072
+ ];
3073
+ }
3163
3074
  };
3164
3075
 
3165
3076
  const generateTry = (scope, decl) => {
@@ -3210,18 +3121,6 @@ const allocPage = (scope, reason, type) => {
3210
3121
  scope.pages ??= new Map();
3211
3122
  scope.pages.set(reason, { ind, type });
3212
3123
 
3213
- if (Prefs.allocLog) log('alloc', `allocated new page of memory (${ind}) | ${reason} (type: ${type})`);
3214
-
3215
- return ind;
3216
- };
3217
-
3218
- // todo: add scope.pages
3219
- const freePage = reason => {
3220
- const { ind } = pages.get(reason);
3221
- pages.delete(reason);
3222
-
3223
- if (Prefs.allocLog) log('alloc', `freed page of memory (${ind}) | ${reason}`);
3224
-
3225
3124
  return ind;
3226
3125
  };
3227
3126
 
@@ -3259,39 +3158,58 @@ const compileBytes = (val, itemType) => {
3259
3158
  }
3260
3159
  };
3261
3160
 
3262
- const getAllocType = itemType => {
3263
- switch (itemType) {
3264
- case 'i8': return 'bytestring';
3265
- case 'i16': return 'string';
3161
+ const makeData = (scope, elements, offset = null, itemType, initEmpty) => {
3162
+ const length = elements.length;
3163
+
3164
+ // if length is 0 memory/data will just be 0000... anyway
3165
+ if (length === 0) return false;
3166
+
3167
+ let bytes = compileBytes(length, 'i32');
3168
+
3169
+ if (!initEmpty) for (let i = 0; i < length; i++) {
3170
+ if (elements[i] == null) continue;
3266
3171
 
3267
- default: return 'array';
3172
+ bytes.push(...compileBytes(elements[i], itemType));
3268
3173
  }
3269
- };
3270
3174
 
3271
- const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty = false, itemType = valtype, typed = false) => {
3272
- const out = [];
3175
+ const obj = { bytes };
3176
+ if (offset != null) obj.offset = offset;
3273
3177
 
3274
- scope.arrays ??= new Map();
3178
+ const idx = data.push(obj) - 1;
3275
3179
 
3276
- let firstAssign = false;
3277
- if (!scope.arrays.has(name) || name === '$undeclared') {
3278
- firstAssign = true;
3180
+ scope.data ??= [];
3181
+ scope.data.push(idx);
3279
3182
 
3280
- // todo: can we just have 1 undeclared array? probably not? but this is not really memory efficient
3281
- const uniqueName = name === '$undeclared' ? name + Math.random().toString().slice(2) : name;
3183
+ return { idx, size: bytes.length };
3184
+ };
3282
3185
 
3283
- let page;
3284
- if (Prefs.scopedPageNames) page = allocPage(scope, `${getAllocType(itemType)}: ${scope.name}/${uniqueName}`, itemType);
3285
- else page = allocPage(scope, `${getAllocType(itemType)}: ${uniqueName}`, itemType);
3186
+ const printStaticStr = str => {
3187
+ const out = [];
3286
3188
 
3287
- // hack: use 1 for page 0 pointer for fast truthiness
3288
- const ptr = page === 0 ? 1 : (page * pageSize);
3289
- scope.arrays.set(name, ptr);
3189
+ for (let i = 0; i < str.length; i++) {
3190
+ out.push(
3191
+ // ...number(str.charCodeAt(i)),
3192
+ ...number(str.charCodeAt(i), Valtype.i32),
3193
+ Opcodes.i32_from_u,
3194
+ [ Opcodes.call, importedFuncs.printChar ]
3195
+ );
3290
3196
  }
3291
3197
 
3292
- const pointer = scope.arrays.get(name);
3198
+ return out;
3199
+ };
3200
+
3201
+ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty = false, itemType = valtype, intOut = false, typed = false) => {
3202
+ if (itemType !== 'i16' && itemType !== 'i8') {
3203
+ pages.hasArray = true;
3204
+ } else {
3205
+ pages.hasAnyString = true;
3206
+ if (itemType === 'i8') pages.hasByteString = true;
3207
+ else pages.hasString = true;
3208
+ }
3209
+
3210
+ const out = [];
3293
3211
 
3294
- const local = global ? globals[name] : scope.locals[name];
3212
+ const uniqueName = name === '$undeclared' ? name + randId() : name;
3295
3213
 
3296
3214
  const useRawElements = !!decl.rawElements;
3297
3215
  const elements = useRawElements ? decl.rawElements : decl.elements;
@@ -3299,46 +3217,81 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
3299
3217
  const valtype = itemTypeToValtype[itemType];
3300
3218
  const length = elements.length;
3301
3219
 
3302
- if (firstAssign && useRawElements && !Prefs.noData) {
3303
- // if length is 0 memory/data will just be 0000... anyway
3304
- if (length !== 0) {
3305
- let bytes = compileBytes(length, 'i32');
3220
+ const allocated = allocator.alloc({ scope, pages, globals, asmFunc, funcIndex }, uniqueName, { itemType });
3221
+
3222
+ let pointer = allocated;
3223
+ if (allocator.constructor.name !== 'StaticAllocator') {
3224
+ // const tmp = localTmp(scope, '#makearray_pointer' + uniqueName, Valtype.i32);
3225
+ const tmp = localTmp(scope, '#makearray_pointer' + name, Valtype.i32);
3226
+ out.push(
3227
+ ...allocated,
3228
+ [ Opcodes.local_set, tmp ]
3229
+ );
3230
+
3231
+ if (Prefs.runtimeAllocLog) out.push(
3232
+ ...printStaticStr(`${name}: `),
3233
+
3234
+ [ Opcodes.local_get, tmp ],
3235
+ Opcodes.i32_from_u,
3236
+ [ Opcodes.call, 0 ],
3237
+
3238
+ ...number(10),
3239
+ [ Opcodes.call, 1 ]
3240
+ );
3306
3241
 
3307
- if (!initEmpty) for (let i = 0; i < length; i++) {
3308
- if (elements[i] == null) continue;
3242
+ pointer = [ [ Opcodes.local_get, tmp ] ];
3309
3243
 
3310
- bytes.push(...compileBytes(elements[i], itemType));
3244
+ if (Prefs.data && useRawElements) {
3245
+ const data = makeData(scope, elements, null, itemType, initEmpty);
3246
+ if (data) {
3247
+ // init data
3248
+ out.push(
3249
+ ...pointer,
3250
+ ...number(0, Valtype.i32),
3251
+ ...number(data.size, Valtype.i32),
3252
+ [ ...Opcodes.memory_init, ...unsignedLEB128(data.idx), 0 ]
3253
+ );
3311
3254
  }
3312
3255
 
3313
- const ind = data.push({
3314
- offset: pointer,
3315
- bytes
3316
- }) - 1;
3256
+ // return pointer in out
3257
+ out.push(
3258
+ ...pointer,
3259
+ ...(!intOut ? [ Opcodes.i32_from_u ] : [])
3260
+ );
3317
3261
 
3318
- scope.data ??= [];
3319
- scope.data.push(ind);
3262
+ return [ out, pointer ];
3320
3263
  }
3264
+ } else {
3265
+ const rawPtr = read_signedLEB128(pointer[0].slice(1));
3321
3266
 
3322
- // local value as pointer
3323
- out.push(...number(pointer));
3267
+ scope.arrays ??= new Map();
3268
+ const firstAssign = !scope.arrays.has(uniqueName);
3269
+ if (firstAssign) scope.arrays.set(uniqueName, rawPtr);
3324
3270
 
3325
- return [ out, pointer ];
3326
- }
3271
+ if (Prefs.data && firstAssign && useRawElements) {
3272
+ makeData(scope, elements, rawPtr, itemType, initEmpty);
3327
3273
 
3328
- const pointerTmp = local != null ? localTmp(scope, '#makearray_pointer_tmp', Valtype.i32) : null;
3329
- if (pointerTmp != null) {
3330
- out.push(
3331
- [ global ? Opcodes.global_get : Opcodes.local_get, local.idx ],
3332
- Opcodes.i32_to_u,
3333
- [ Opcodes.local_set, pointerTmp ]
3334
- );
3274
+ // local value as pointer
3275
+ return [ number(rawPtr, intOut ? Valtype.i32 : valtypeBinary), pointer ];
3276
+ }
3277
+
3278
+ const local = global ? globals[name] : scope.locals[name];
3279
+ const pointerTmp = local != null ? localTmp(scope, '#makearray_pointer_tmp', Valtype.i32) : null;
3280
+ if (pointerTmp != null) {
3281
+ out.push(
3282
+ [ global ? Opcodes.global_get : Opcodes.local_get, local.idx ],
3283
+ Opcodes.i32_to_u,
3284
+ [ Opcodes.local_set, pointerTmp ]
3285
+ );
3286
+
3287
+ pointer = [ [ Opcodes.local_get, pointerTmp ] ];
3288
+ }
3335
3289
  }
3336
3290
 
3337
- const pointerWasm = pointerTmp != null ? [ [ Opcodes.local_get, pointerTmp ] ] : number(pointer, Valtype.i32);
3338
3291
 
3339
3292
  // store length
3340
3293
  out.push(
3341
- ...pointerWasm,
3294
+ ...pointer,
3342
3295
  ...number(length, Valtype.i32),
3343
3296
  [ Opcodes.i32_store, Math.log2(ValtypeSize.i32) - 1, 0 ]
3344
3297
  );
@@ -3350,11 +3303,11 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
3350
3303
 
3351
3304
  const offset = ValtypeSize.i32 + i * sizePerEl;
3352
3305
  out.push(
3353
- ...pointerWasm,
3306
+ ...pointer,
3354
3307
  ...(useRawElements ? number(elements[i], Valtype[valtype]) : generate(scope, elements[i])),
3355
3308
  [ storeOp, 0, ...unsignedLEB128(offset) ],
3356
3309
  ...(!typed ? [] : [ // typed presumes !useRawElements
3357
- ...pointerWasm,
3310
+ ...pointer,
3358
3311
  ...getNodeType(scope, elements[i]),
3359
3312
  [ Opcodes.i32_store8, 0, ...unsignedLEB128(offset + ValtypeSize[itemType]) ]
3360
3313
  ])
@@ -3362,12 +3315,13 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
3362
3315
  }
3363
3316
 
3364
3317
  // local value as pointer
3365
- out.push(...pointerWasm, Opcodes.i32_from_u);
3318
+ out.push(...pointer);
3319
+ if (!intOut) out.push(Opcodes.i32_from_u);
3366
3320
 
3367
3321
  return [ out, pointer ];
3368
3322
  };
3369
3323
 
3370
- const storeArray = (scope, array, index, element, aotPointer = null) => {
3324
+ const storeArray = (scope, array, index, element) => {
3371
3325
  if (!Array.isArray(element)) element = generate(scope, element);
3372
3326
  if (typeof index === 'number') index = number(index);
3373
3327
 
@@ -3379,26 +3333,25 @@ const storeArray = (scope, array, index, element, aotPointer = null) => {
3379
3333
  Opcodes.i32_to_u,
3380
3334
  ...number(ValtypeSize[valtype] + 1, Valtype.i32),
3381
3335
  [ Opcodes.i32_mul ],
3382
- ...(aotPointer ? [] : [
3383
- ...array,
3384
- Opcodes.i32_to_u,
3385
- [ Opcodes.i32_add ],
3386
- ]),
3336
+
3337
+ ...array,
3338
+ Opcodes.i32_to_u,
3339
+ [ Opcodes.i32_add ],
3387
3340
  [ Opcodes.local_set, offset ],
3388
3341
 
3389
3342
  // store value
3390
3343
  [ Opcodes.local_get, offset ],
3391
3344
  ...generate(scope, element),
3392
- [ Opcodes.store, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ],
3345
+ [ Opcodes.store, 0, ValtypeSize.i32 ],
3393
3346
 
3394
3347
  // store type
3395
3348
  [ Opcodes.local_get, offset ],
3396
3349
  ...getNodeType(scope, element),
3397
- [ Opcodes.i32_store8, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32 + ValtypeSize[valtype]) ]
3350
+ [ Opcodes.i32_store8, 0, ValtypeSize.i32 + ValtypeSize[valtype] ]
3398
3351
  ];
3399
3352
  };
3400
3353
 
3401
- const loadArray = (scope, array, index, aotPointer = null) => {
3354
+ const loadArray = (scope, array, index) => {
3402
3355
  if (typeof index === 'number') index = number(index);
3403
3356
 
3404
3357
  const offset = localTmp(scope, '#loadArray_offset', Valtype.i32);
@@ -3409,20 +3362,19 @@ const loadArray = (scope, array, index, aotPointer = null) => {
3409
3362
  Opcodes.i32_to_u,
3410
3363
  ...number(ValtypeSize[valtype] + 1, Valtype.i32),
3411
3364
  [ Opcodes.i32_mul ],
3412
- ...(aotPointer ? [] : [
3413
- ...array,
3414
- Opcodes.i32_to_u,
3415
- [ Opcodes.i32_add ],
3416
- ]),
3365
+
3366
+ ...array,
3367
+ Opcodes.i32_to_u,
3368
+ [ Opcodes.i32_add ],
3417
3369
  [ Opcodes.local_set, offset ],
3418
3370
 
3419
3371
  // load value
3420
3372
  [ Opcodes.local_get, offset ],
3421
- [ Opcodes.load, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ],
3373
+ [ Opcodes.load, 0, ValtypeSize.i32 ],
3422
3374
 
3423
3375
  // load type
3424
3376
  [ Opcodes.local_get, offset ],
3425
- [ Opcodes.i32_load8_u, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32 + ValtypeSize[valtype]) ]
3377
+ [ Opcodes.i32_load8_u, 0, ValtypeSize.i32 + ValtypeSize[valtype] ]
3426
3378
  ];
3427
3379
  };
3428
3380
 
@@ -3454,7 +3406,7 @@ const makeString = (scope, str, global = false, name = '$undeclared', forceBytes
3454
3406
  };
3455
3407
 
3456
3408
  const generateArray = (scope, decl, global = false, name = '$undeclared', initEmpty = false) => {
3457
- return makeArray(scope, decl, global, name, initEmpty, valtype, true)[0];
3409
+ return makeArray(scope, decl, global, name, initEmpty, valtype, false, true)[0];
3458
3410
  };
3459
3411
 
3460
3412
  const generateObject = (scope, decl, global = false, name = '$undeclared') => {
@@ -3473,9 +3425,6 @@ const withType = (scope, wasm, type) => [
3473
3425
 
3474
3426
  const generateMember = (scope, decl, _global, _name) => {
3475
3427
  const name = decl.object.name;
3476
- const pointer = scope.arrays?.get(name);
3477
-
3478
- const aotPointer = Prefs.aotPointerOpt && pointer;
3479
3428
 
3480
3429
  // hack: .name
3481
3430
  if (decl.property.name === 'name') {
@@ -3510,18 +3459,16 @@ const generateMember = (scope, decl, _global, _name) => {
3510
3459
  }
3511
3460
 
3512
3461
  if (builtinFuncs[name]) return withType(scope, number(builtinFuncs[name].typedParams ? (builtinFuncs[name].params.length / 2) : builtinFuncs[name].params.length), TYPES.number);
3513
- if (importedFuncs[name]) return withType(scope, number(importedFuncs[name].params), TYPES.number);
3462
+ if (importedFuncs[name]) return withType(scope, number(importedFuncs[name].params.length ?? importedFuncs[name].params), TYPES.number);
3514
3463
  if (internalConstrs[name]) return withType(scope, number(internalConstrs[name].length ?? 0), TYPES.number);
3515
3464
 
3516
3465
  if (Prefs.fastLength) {
3517
3466
  // presume valid length object
3518
3467
  return [
3519
- ...(aotPointer ? number(0, Valtype.i32) : [
3520
- ...generate(scope, decl.object),
3521
- Opcodes.i32_to_u
3522
- ]),
3468
+ ...generate(scope, decl.object),
3469
+ Opcodes.i32_to_u,
3523
3470
 
3524
- [ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(aotPointer ? pointer : 0) ],
3471
+ [ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, 0 ],
3525
3472
  Opcodes.i32_from_u
3526
3473
  ];
3527
3474
  }
@@ -3530,12 +3477,10 @@ const generateMember = (scope, decl, _global, _name) => {
3530
3477
  const known = knownType(scope, type);
3531
3478
  if (known != null) {
3532
3479
  if ([ TYPES.string, TYPES.bytestring, TYPES.array ].includes(known)) return [
3533
- ...(aotPointer ? number(0, Valtype.i32) : [
3534
- ...generate(scope, decl.object),
3535
- Opcodes.i32_to_u
3536
- ]),
3480
+ ...generate(scope, decl.object),
3481
+ Opcodes.i32_to_u,
3537
3482
 
3538
- [ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(aotPointer ? pointer : 0) ],
3483
+ [ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, 0 ],
3539
3484
  Opcodes.i32_from_u
3540
3485
  ];
3541
3486
 
@@ -3545,12 +3490,10 @@ const generateMember = (scope, decl, _global, _name) => {
3545
3490
  return [
3546
3491
  ...typeIsOneOf(getNodeType(scope, decl.object), [ TYPES.string, TYPES.bytestring, TYPES.array ]),
3547
3492
  [ Opcodes.if, valtypeBinary ],
3548
- ...(aotPointer ? number(0, Valtype.i32) : [
3549
- ...generate(scope, decl.object),
3550
- Opcodes.i32_to_u
3551
- ]),
3493
+ ...generate(scope, decl.object),
3494
+ Opcodes.i32_to_u,
3552
3495
 
3553
- [ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(aotPointer ? pointer : 0) ],
3496
+ [ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, 0 ],
3554
3497
  Opcodes.i32_from_u,
3555
3498
 
3556
3499
  ...setLastType(scope, TYPES.number),
@@ -3593,25 +3536,29 @@ const generateMember = (scope, decl, _global, _name) => {
3593
3536
 
3594
3537
  // // todo: we should only do this for strings but we don't know at compile-time :(
3595
3538
  // hack: this is naughty and will break things!
3596
- let newOut = number(0, valtypeBinary), newPointer = -1;
3597
- if (pages.hasAnyString) {
3539
+ let newOut = number(0, Valtype.i32), newPointer = number(0, Valtype.i32);
3540
+ if (pages.hasAnyString && knownType(scope, getNodeType(scope, decl.object)) !== TYPES.array) {
3541
+ // todo: we use i16 even for bytestrings which should not make a bad thing happen, just be confusing for debugging?
3598
3542
  0, [ newOut, newPointer ] = makeArray(scope, {
3599
- rawElements: new Array(1)
3600
- }, _global, _name, true, 'i16');
3543
+ rawElements: new Array(0)
3544
+ }, _global, _name, true, 'i16', true);
3601
3545
  }
3602
3546
 
3603
3547
  return typeSwitch(scope, getNodeType(scope, decl.object), {
3604
3548
  [TYPES.array]: [
3605
- ...loadArray(scope, object, property, aotPointer),
3549
+ ...loadArray(scope, object, property),
3606
3550
  ...setLastType(scope)
3607
3551
  ],
3608
-
3609
3552
  [TYPES.string]: [
3610
3553
  // setup new/out array
3611
3554
  ...newOut,
3612
- [ Opcodes.drop ],
3613
3555
 
3614
- ...number(0, Valtype.i32), // base 0 for store later
3556
+ // set length to 1
3557
+ ...number(1, Valtype.i32),
3558
+ [ Opcodes.i32_store, 0, 0 ],
3559
+
3560
+ // use as pointer for store later
3561
+ ...newPointer,
3615
3562
 
3616
3563
  ...property,
3617
3564
  Opcodes.i32_to_u,
@@ -3619,46 +3566,48 @@ const generateMember = (scope, decl, _global, _name) => {
3619
3566
  ...number(ValtypeSize.i16, Valtype.i32),
3620
3567
  [ Opcodes.i32_mul ],
3621
3568
 
3622
- ...(aotPointer ? [] : [
3623
- ...object,
3624
- Opcodes.i32_to_u,
3625
- [ Opcodes.i32_add ]
3626
- ]),
3569
+ ...object,
3570
+ Opcodes.i32_to_u,
3571
+ [ Opcodes.i32_add ],
3627
3572
 
3628
3573
  // load current string ind {arg}
3629
- [ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ],
3574
+ [ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1, ValtypeSize.i32 ],
3630
3575
 
3631
3576
  // store to new string ind 0
3632
- [ Opcodes.i32_store16, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128(newPointer + ValtypeSize.i32) ],
3577
+ [ Opcodes.i32_store16, Math.log2(ValtypeSize.i16) - 1, ValtypeSize.i32 ],
3633
3578
 
3634
3579
  // return new string (page)
3635
- ...number(newPointer),
3580
+ ...newPointer,
3581
+ Opcodes.i32_from_u,
3636
3582
  ...setLastType(scope, TYPES.string)
3637
3583
  ],
3638
3584
  [TYPES.bytestring]: [
3639
3585
  // setup new/out array
3640
3586
  ...newOut,
3641
- [ Opcodes.drop ],
3642
3587
 
3643
- ...number(0, Valtype.i32), // base 0 for store later
3588
+ // set length to 1
3589
+ ...number(1, Valtype.i32),
3590
+ [ Opcodes.i32_store, 0, 0 ],
3591
+
3592
+ // use as pointer for store later
3593
+ ...newPointer,
3644
3594
 
3645
3595
  ...property,
3646
3596
  Opcodes.i32_to_u,
3647
3597
 
3648
- ...(aotPointer ? [] : [
3649
- ...object,
3650
- Opcodes.i32_to_u,
3651
- [ Opcodes.i32_add ]
3652
- ]),
3598
+ ...object,
3599
+ Opcodes.i32_to_u,
3600
+ [ Opcodes.i32_add ],
3653
3601
 
3654
3602
  // load current string ind {arg}
3655
- [ Opcodes.i32_load8_u, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ],
3603
+ [ Opcodes.i32_load8_u, 0, ValtypeSize.i32 ],
3656
3604
 
3657
3605
  // store to new string ind 0
3658
- [ Opcodes.i32_store8, 0, ...unsignedLEB128(newPointer + ValtypeSize.i32) ],
3606
+ [ Opcodes.i32_store8, 0, ValtypeSize.i32 ],
3659
3607
 
3660
3608
  // return new string (page)
3661
- ...number(newPointer),
3609
+ ...newPointer,
3610
+ Opcodes.i32_from_u,
3662
3611
  ...setLastType(scope, TYPES.bytestring)
3663
3612
  ],
3664
3613
 
@@ -3666,7 +3615,7 @@ const generateMember = (scope, decl, _global, _name) => {
3666
3615
  });
3667
3616
  };
3668
3617
 
3669
- const randId = () => Math.random().toString(16).slice(0, -4);
3618
+ const randId = () => Math.random().toString(16).slice(1, -2).padEnd(12, '0');
3670
3619
 
3671
3620
  const objectHack = node => {
3672
3621
  if (!node) return node;
@@ -3718,7 +3667,7 @@ const generateFunc = (scope, decl) => {
3718
3667
  if (decl.async) return todo(scope, 'async functions are not supported');
3719
3668
  if (decl.generator) return todo(scope, 'generator functions are not supported');
3720
3669
 
3721
- const name = decl.id ? decl.id.name : `anonymous_${randId()}`;
3670
+ const name = decl.id ? decl.id.name : `anonymous${randId()}`;
3722
3671
  const params = decl.params ?? [];
3723
3672
 
3724
3673
  // TODO: share scope/locals between !!!
@@ -3732,6 +3681,9 @@ const generateFunc = (scope, decl) => {
3732
3681
  index: currentFuncIndex++
3733
3682
  };
3734
3683
 
3684
+ funcIndex[name] = func.index;
3685
+ funcs.push(func);
3686
+
3735
3687
  if (typedInput && decl.returnType) {
3736
3688
  const { type } = extractTypeAnnotation(decl.returnType);
3737
3689
  // if (type != null && !Prefs.indirectCalls) {
@@ -3741,12 +3693,26 @@ const generateFunc = (scope, decl) => {
3741
3693
  }
3742
3694
  }
3743
3695
 
3696
+ const defaultValues = {};
3744
3697
  for (let i = 0; i < params.length; i++) {
3745
- const name = params[i].name;
3698
+ let name;
3699
+ const x = params[i];
3700
+ switch (x.type) {
3701
+ case 'Identifier': {
3702
+ name = x.name;
3703
+ break;
3704
+ }
3705
+
3706
+ case 'AssignmentPattern': {
3707
+ name = x.left.name;
3708
+ defaultValues[name] = x.right;
3709
+ break;
3710
+ }
3711
+ }
3712
+
3746
3713
  // if (name == null) return todo('non-identifier args are not supported');
3747
3714
 
3748
3715
  allocVar(func, name, false);
3749
-
3750
3716
  if (typedInput && params[i].typeAnnotation) {
3751
3717
  addVarMetadata(func, name, false, extractTypeAnnotation(params[i]));
3752
3718
  }
@@ -3763,11 +3729,22 @@ const generateFunc = (scope, decl) => {
3763
3729
  };
3764
3730
  }
3765
3731
 
3766
- funcIndex[name] = func.index;
3767
- funcs.push(func);
3732
+ const prelude = [];
3733
+ for (const x in defaultValues) {
3734
+ prelude.push(
3735
+ ...getType(func, x),
3736
+ ...number(TYPES.undefined, Valtype.i32),
3737
+ [ Opcodes.i32_eq ],
3738
+ [ Opcodes.if, Blocktype.void ],
3739
+ ...generate(func, defaultValues[x], false, x),
3740
+ [ Opcodes.local_set, func.locals[x].idx ],
3768
3741
 
3769
- const wasm = generate(func, body);
3770
- func.wasm = wasm;
3742
+ ...setType(func, x, getNodeType(scope, defaultValues[x])),
3743
+ [ Opcodes.end ]
3744
+ );
3745
+ }
3746
+
3747
+ const wasm = func.wasm = prelude.concat(generate(func, body));
3771
3748
 
3772
3749
  if (name === 'main') func.gotLastType = true;
3773
3750
 
@@ -3803,9 +3780,9 @@ const internalConstrs = {
3803
3780
 
3804
3781
  // new Array(n)
3805
3782
 
3806
- const [ , pointer ] = makeArray(scope, {
3783
+ const [ out, pointer ] = makeArray(scope, {
3807
3784
  rawElements: new Array(0)
3808
- }, global, name, true);
3785
+ }, global, name, true, undefined, true);
3809
3786
 
3810
3787
  const arg = decl.arguments[0] ?? DEFAULT_VALUE;
3811
3788
 
@@ -3814,12 +3791,13 @@ const internalConstrs = {
3814
3791
  if (literalValue < 0 || !Number.isFinite(literalValue) || literalValue > 4294967295) return internalThrow(scope, 'RangeThrow', 'Invalid array length', true);
3815
3792
 
3816
3793
  return [
3817
- ...number(0, Valtype.i32),
3794
+ ...out,
3818
3795
  ...generate(scope, arg, global, name),
3819
3796
  Opcodes.i32_to_u,
3820
- [ Opcodes.i32_store, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(pointer) ],
3797
+ [ Opcodes.i32_store, Math.log2(ValtypeSize.i32) - 1, 0 ],
3821
3798
 
3822
- ...number(pointer)
3799
+ ...pointer,
3800
+ Opcodes.i32_from_u
3823
3801
  ];
3824
3802
  },
3825
3803
  type: TYPES.array,
@@ -3968,8 +3946,9 @@ const internalConstrs = {
3968
3946
  };
3969
3947
 
3970
3948
  export default program => {
3971
- globals = {};
3972
- globalInd = 0;
3949
+ globals = {
3950
+ ['#ind']: 0
3951
+ };
3973
3952
  tags = [];
3974
3953
  exceptions = [];
3975
3954
  funcs = [];
@@ -3979,19 +3958,8 @@ export default program => {
3979
3958
  data = [];
3980
3959
  currentFuncIndex = importedFuncs.length;
3981
3960
 
3982
- globalThis.valtype = 'f64';
3983
-
3984
- const valtypeOpt = process.argv.find(x => x.startsWith('--valtype='));
3985
- if (valtypeOpt) valtype = valtypeOpt.split('=')[1];
3986
-
3987
- globalThis.valtypeBinary = Valtype[valtype];
3988
-
3989
3961
  const valtypeInd = ['i32', 'i64', 'f64'].indexOf(valtype);
3990
3962
 
3991
- globalThis.pageSize = PageSize;
3992
- const pageSizeOpt = process.argv.find(x => x.startsWith('--page-size='));
3993
- if (pageSizeOpt) pageSize = parseInt(pageSizeOpt.split('=')[1]) * 1024;
3994
-
3995
3963
  // set generic opcodes for current valtype
3996
3964
  Opcodes.const = [ Opcodes.i32_const, Opcodes.i64_const, Opcodes.f64_const ][valtypeInd];
3997
3965
  Opcodes.eq = [ Opcodes.i32_eq, Opcodes.i64_eq, Opcodes.f64_eq ][valtypeInd];
@@ -4013,6 +3981,7 @@ export default program => {
4013
3981
  builtinFuncs = new BuiltinFuncs();
4014
3982
  builtinVars = new BuiltinVars();
4015
3983
  prototypeFuncs = new PrototypeFuncs();
3984
+ allocator = makeAllocator(Prefs.allocator ?? 'static');
4016
3985
 
4017
3986
  program.id = { name: 'main' };
4018
3987
 
@@ -4055,6 +4024,8 @@ export default program => {
4055
4024
  else main.returns = [];
4056
4025
  }
4057
4026
 
4027
+ delete globals['#ind'];
4028
+
4058
4029
  // if blank main func and other exports, remove it
4059
4030
  if (main.wasm.length === 0 && funcs.reduce((acc, x) => acc + (x.export ? 1 : 0), 0) > 1) funcs.splice(main.index - importedFuncs.length, 1);
4060
4031