porffor 0.16.0-fe07da0f4 → 0.17.0-93ba20045

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
 
@@ -433,63 +423,12 @@ const concatStrings = (scope, left, right, global, name, assign = false, bytestr
433
423
  const rightLength = localTmp(scope, 'concat_right_length', Valtype.i32);
434
424
  const leftLength = localTmp(scope, 'concat_left_length', Valtype.i32);
435
425
 
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
426
  const leftPointer = localTmp(scope, 'concat_left_pointer', Valtype.i32);
488
427
 
489
428
  // alloc/assign array
490
- const [ , pointer ] = makeArray(scope, {
429
+ const [ out, pointer ] = makeArray(scope, {
491
430
  rawElements: new Array(0)
492
- }, global, name, true, 'i16');
431
+ }, assign ? false : global, assign ? undefined : name, true, 'i16', true);
493
432
 
494
433
  return [
495
434
  // setup left
@@ -503,7 +442,7 @@ const concatStrings = (scope, left, right, global, name, assign = false, bytestr
503
442
  [ Opcodes.local_set, rightPointer ],
504
443
 
505
444
  // calculate length
506
- ...number(0, Valtype.i32), // base 0 for store later
445
+ ...out,
507
446
 
508
447
  [ Opcodes.local_get, leftPointer ],
509
448
  [ Opcodes.i32_load, 0, ...unsignedLEB128(0) ],
@@ -516,11 +455,13 @@ const concatStrings = (scope, left, right, global, name, assign = false, bytestr
516
455
  [ Opcodes.i32_add ],
517
456
 
518
457
  // store length
519
- [ Opcodes.i32_store, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(pointer) ],
458
+ [ Opcodes.i32_store, Math.log2(ValtypeSize.i32) - 1, 0 ],
520
459
 
521
460
  // copy left
522
461
  // dst = out pointer + length size
523
- ...number(pointer + ValtypeSize.i32, Valtype.i32),
462
+ ...pointer,
463
+ ...number(ValtypeSize.i32, Valtype.i32),
464
+ [ Opcodes.i32_add ],
524
465
 
525
466
  // src = left pointer + length size
526
467
  [ Opcodes.local_get, leftPointer ],
@@ -533,7 +474,9 @@ const concatStrings = (scope, left, right, global, name, assign = false, bytestr
533
474
 
534
475
  // copy right
535
476
  // dst = out pointer + length size + left length * sizeof valtype
536
- ...number(pointer + ValtypeSize.i32, Valtype.i32),
477
+ ...pointer,
478
+ ...number(ValtypeSize.i32, Valtype.i32),
479
+ [ Opcodes.i32_add ],
537
480
 
538
481
  [ Opcodes.local_get, leftLength ],
539
482
  ...number(bytestrings ? ValtypeSize.i8 : ValtypeSize.i16, Valtype.i32),
@@ -553,7 +496,8 @@ const concatStrings = (scope, left, right, global, name, assign = false, bytestr
553
496
  [ ...Opcodes.memory_copy, 0x00, 0x00 ],
554
497
 
555
498
  // return new string (page)
556
- ...number(pointer)
499
+ ...pointer,
500
+ Opcodes.i32_from_u
557
501
  ];
558
502
  };
559
503
 
@@ -669,10 +613,10 @@ const compareStrings = (scope, left, right, bytestrings = false) => {
669
613
  };
670
614
 
671
615
  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
- ];
616
+ // if (isIntToFloatOp(wasm[wasm.length - 1])) return [
617
+ // ...wasm,
618
+ // ...(!intIn && intOut ? [ Opcodes.i32_to_u ] : [])
619
+ // ];
676
620
  // if (isIntOp(wasm[wasm.length - 1])) return [ ...wasm ];
677
621
 
678
622
  // todo/perf: use knownType and custom bytecode here instead of typeSwitch
@@ -1014,12 +958,9 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
1014
958
  [ Opcodes.end ],
1015
959
  ]));
1016
960
 
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
- // }
961
+ // add a surrounding block
962
+ startOut.push(stringOnly([ Opcodes.block, Valtype.i32 ]));
963
+ endOut.unshift(stringOnly([ Opcodes.end ]));
1023
964
  }
1024
965
 
1025
966
  return finalize([
@@ -1054,7 +995,7 @@ const asmFuncToAsm = (func, scope) => {
1054
995
  });
1055
996
  };
1056
997
 
1057
- const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes = [], globalInits, returns, returnType, localNames = [], globalNames = [], data: _data = [], table = false }) => {
998
+ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes = [], globalInits = [], returns, returnType, localNames = [], globalNames = [], data: _data = [], table = false }) => {
1058
999
  const existing = funcs.find(x => x.name === name);
1059
1000
  if (existing) return existing;
1060
1001
 
@@ -1068,7 +1009,7 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
1068
1009
 
1069
1010
  for (const x of _data) {
1070
1011
  const copy = { ...x };
1071
- copy.offset += pages.size * pageSize;
1012
+ if (copy.offset != null) copy.offset += pages.size * pageSize;
1072
1013
  data.push(copy);
1073
1014
  }
1074
1015
 
@@ -1078,7 +1019,7 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
1078
1019
  locals,
1079
1020
  localInd: allLocals.length,
1080
1021
  returns,
1081
- returnType: returnType ?? TYPES.number,
1022
+ returnType,
1082
1023
  internal: true,
1083
1024
  index: currentFuncIndex++,
1084
1025
  table
@@ -1091,9 +1032,9 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
1091
1032
 
1092
1033
  let baseGlobalIdx, i = 0;
1093
1034
  for (const type of globalTypes) {
1094
- if (baseGlobalIdx === undefined) baseGlobalIdx = globalInd;
1035
+ if (baseGlobalIdx === undefined) baseGlobalIdx = globals['#ind'];
1095
1036
 
1096
- globals[globalNames[i] ?? `${name}_global_${i}`] = { idx: globalInd++, type, init: globalInits[i] ?? 0 };
1037
+ globals[globalNames[i] ?? `${name}_global_${i}`] = { idx: globals['#ind']++, type, init: globalInits[i] ?? 0 };
1097
1038
  i++;
1098
1039
  }
1099
1040
 
@@ -1106,11 +1047,15 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
1106
1047
  }
1107
1048
  }
1108
1049
 
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));
1050
+ if (table) {
1051
+ for (const inst of wasm) {
1052
+ if (inst[0] === Opcodes.i32_load16_u && inst.at(-1) === 'read_argc') {
1053
+ inst.splice(2, 99);
1054
+ inst.push(...unsignedLEB128(allocPage({}, 'func argc lut') * pageSize));
1055
+ }
1113
1056
  }
1057
+
1058
+ funcs.table = true;
1114
1059
  }
1115
1060
 
1116
1061
  func.wasm = wasm;
@@ -1254,7 +1199,7 @@ const getNodeType = (scope, node) => {
1254
1199
  const func = funcs.find(x => x.name === name);
1255
1200
 
1256
1201
  if (func) {
1257
- if (func.returnType) return func.returnType;
1202
+ if (func.returnType != null) return func.returnType;
1258
1203
  }
1259
1204
 
1260
1205
  if (builtinFuncs[name] && !builtinFuncs[name].typedReturns) return builtinFuncs[name].returnType ?? TYPES.number;
@@ -1270,15 +1215,7 @@ const getNodeType = (scope, node) => {
1270
1215
  const func = spl[spl.length - 1];
1271
1216
  const protoFuncs = Object.keys(prototypeFuncs).filter(x => x != TYPES.bytestring && prototypeFuncs[x][func] != null);
1272
1217
  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;
1218
+ if (protoFuncs[0].returnType != null) return protoFuncs[0].returnType;
1282
1219
  }
1283
1220
  }
1284
1221
 
@@ -1326,7 +1263,15 @@ const getNodeType = (scope, node) => {
1326
1263
  }
1327
1264
 
1328
1265
  if (node.type === 'AssignmentExpression') {
1329
- return getNodeType(scope, node.right);
1266
+ const op = node.operator.slice(0, -1) || '=';
1267
+ if (op === '=') return getNodeType(scope, node.right);
1268
+
1269
+ return getNodeType(scope, {
1270
+ type: ['||', '&&', '??'].includes(op) ? 'LogicalExpression' : 'BinaryExpression',
1271
+ left: node.left,
1272
+ right: node.right,
1273
+ operator: op
1274
+ });
1330
1275
  }
1331
1276
 
1332
1277
  if (node.type === 'ArrayExpression') {
@@ -1345,23 +1290,6 @@ const getNodeType = (scope, node) => {
1345
1290
  if (knownLeft === TYPES.bytestring || knownRight === TYPES.bytestring) return TYPES.bytestring;
1346
1291
 
1347
1292
  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
1293
  }
1366
1294
 
1367
1295
  if (node.type === 'UnaryExpression') {
@@ -1381,7 +1309,6 @@ const getNodeType = (scope, node) => {
1381
1309
  if (Prefs.fastLength) return TYPES.number;
1382
1310
  }
1383
1311
 
1384
-
1385
1312
  const objectKnownType = knownType(scope, getNodeType(scope, node.object));
1386
1313
  if (objectKnownType != null) {
1387
1314
  if (name === 'length') {
@@ -1392,7 +1319,6 @@ const getNodeType = (scope, node) => {
1392
1319
  if (node.computed) {
1393
1320
  if (objectKnownType === TYPES.string) return TYPES.string;
1394
1321
  if (objectKnownType === TYPES.bytestring) return TYPES.bytestring;
1395
- if (objectKnownType === TYPES.array) return TYPES.number;
1396
1322
  }
1397
1323
  }
1398
1324
 
@@ -1458,10 +1384,10 @@ const countLeftover = wasm => {
1458
1384
 
1459
1385
  if (depth === 0)
1460
1386
  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)) {}
1387
+ 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
1388
  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
1389
  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;
1390
+ else if (inst[0] === Opcodes.memory_copy[0] && (inst[1] === Opcodes.memory_copy[1] || inst[1] === Opcodes.memory_init[1])) count -= 3;
1465
1391
  else if (inst[0] === Opcodes.return) count = 0;
1466
1392
  else if (inst[0] === Opcodes.call) {
1467
1393
  let func = funcs.find(x => x.index === inst[1]);
@@ -1733,7 +1659,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1733
1659
  }, generate(scope, decl.arguments[0] ?? DEFAULT_VALUE), getNodeType(scope, decl.arguments[0] ?? DEFAULT_VALUE), protoLocal, protoLocal2, (length, itemType) => {
1734
1660
  return makeArray(scope, {
1735
1661
  rawElements: new Array(length)
1736
- }, _global, _name, true, itemType);
1662
+ }, _global, _name, true, itemType, true);
1737
1663
  }, () => {
1738
1664
  optUnused = true;
1739
1665
  return unusedValue;
@@ -1815,7 +1741,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1815
1741
  f64_store: { imms: 2, args: [ true, false ], returns: 0 },
1816
1742
 
1817
1743
  // value
1818
- i32_const: { imms: 1, args: [], returns: 1 },
1744
+ i32_const: { imms: 1, args: [], returns: 0 },
1819
1745
  };
1820
1746
 
1821
1747
  const opName = name.slice('__Porffor_wasm_'.length);
@@ -1900,7 +1826,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1900
1826
  if (indirectMode === 'strict') {
1901
1827
  return typeSwitch(scope, getNodeType(scope, decl.callee), {
1902
1828
  [TYPES.function]: [
1903
- ...argWasm,
1829
+ ...out,
1904
1830
  [ global ? Opcodes.global_get : Opcodes.local_get, local.idx ],
1905
1831
  Opcodes.i32_to_u,
1906
1832
  [ Opcodes.call_indirect, args.length, 0 ],
@@ -1978,7 +1904,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1978
1904
  const func = funcs[idx - importedFuncs.length]; // idx === scope.index ? scope : funcs.find(x => x.index === idx);
1979
1905
  const userFunc = func && !func.internal;
1980
1906
  const typedParams = userFunc || builtinFuncs[name]?.typedParams;
1981
- const typedReturns = (func && func.returnType == null) || builtinFuncs[name]?.typedReturns;
1907
+ const typedReturns = (userFunc && func.returnType == null) || builtinFuncs[name]?.typedReturns;
1982
1908
  const paramCount = func && (typedParams ? func.params.length / 2 : func.params.length);
1983
1909
 
1984
1910
  let args = decl.arguments;
@@ -2006,13 +1932,18 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
2006
1932
  continue;
2007
1933
  }
2008
1934
 
2009
- if (valtypeBinary !== Valtype.i32 && (
2010
- (builtinFuncs[name] && builtinFuncs[name].params[i * (typedParams ? 2 : 1)] === Valtype.i32) ||
2011
- (importedFuncs[name] && name.startsWith('profile'))
2012
- )) {
1935
+ if (valtypeBinary !== Valtype.i32 &&
1936
+ (func && func.params[i * (typedParams ? 2 : 1)] === Valtype.i32)
1937
+ ) {
2013
1938
  out.push(Opcodes.i32_to);
2014
1939
  }
2015
1940
 
1941
+ if (valtypeBinary === Valtype.i32 &&
1942
+ (func && func.params[i * (typedParams ? 2 : 1)] === Valtype.f64)
1943
+ ) {
1944
+ out.push([ Opcodes.f64_convert_i32_s ]);
1945
+ }
1946
+
2016
1947
  if (typedParams) out = out.concat(getNodeType(scope, arg));
2017
1948
  }
2018
1949
 
@@ -2034,6 +1965,10 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
2034
1965
  out.push(Opcodes.i32_from);
2035
1966
  }
2036
1967
 
1968
+ if (builtinFuncs[name] && builtinFuncs[name].returns?.[0] === Valtype.f64 && valtypeBinary === Valtype.i32) {
1969
+ out.push(Opcodes.i32_trunc_sat_f64_s);
1970
+ }
1971
+
2037
1972
  return out;
2038
1973
  };
2039
1974
 
@@ -2057,9 +1992,9 @@ const generateNew = (scope, decl, _global, _name) => {
2057
1992
  if (
2058
1993
  (builtinFuncs[name] && !builtinFuncs[name].constr) ||
2059
1994
  (internalConstrs[name] && builtinFuncs[name].notConstr)
2060
- ) return internalThrow(scope, 'TypeError', `${name} is not a constructor`);
1995
+ ) return internalThrow(scope, 'TypeError', `${name} is not a constructor`, true);
2061
1996
 
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)})`);
1997
+ 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
1998
 
2064
1999
  return generateCall(scope, decl, _global, _name);
2065
2000
  };
@@ -2123,7 +2058,6 @@ const brTable = (input, bc, returns) => {
2123
2058
  }
2124
2059
 
2125
2060
  for (let i = 0; i < count; i++) {
2126
- // if (i === 0) out.push([ Opcodes.block, returns, 'br table start' ]);
2127
2061
  if (i === 0) out.push([ Opcodes.block, returns ]);
2128
2062
  else out.push([ Opcodes.block, Blocktype.void ]);
2129
2063
  }
@@ -2157,10 +2091,8 @@ const brTable = (input, bc, returns) => {
2157
2091
  [ Opcodes.br_table, ...encodeVector(table), 0 ]
2158
2092
  );
2159
2093
 
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
2094
+ // sort the wrong way and then reverse
2095
+ // so strings ('default') are at the start before any numbers
2164
2096
  const orderedBc = keys.sort((a, b) => b - a).reverse();
2165
2097
 
2166
2098
  br = count - 1;
@@ -2186,10 +2118,10 @@ const typeSwitch = (scope, type, bc, returns = valtypeBinary) => {
2186
2118
  return bc[known] ?? bc.default;
2187
2119
  }
2188
2120
 
2189
- if (Prefs.typeswitchUseBrtable)
2121
+ if (Prefs.typeswitchBrtable)
2190
2122
  return brTable(type, bc, returns);
2191
2123
 
2192
- const tmp = localTmp(scope, '#typeswitch_tmp', Valtype.i32);
2124
+ const tmp = localTmp(scope, '#typeswitch_tmp' + (Prefs.typeswitchUniqueTmp ? randId() : ''), Valtype.i32);
2193
2125
  const out = [
2194
2126
  ...type,
2195
2127
  [ Opcodes.local_set, tmp ],
@@ -2241,11 +2173,11 @@ const allocVar = (scope, name, global = false, type = true) => {
2241
2173
  return target[name].idx;
2242
2174
  }
2243
2175
 
2244
- let idx = global ? globalInd++ : scope.localInd++;
2176
+ let idx = global ? globals['#ind']++ : scope.localInd++;
2245
2177
  target[name] = { idx, type: valtypeBinary };
2246
2178
 
2247
2179
  if (type) {
2248
- let typeIdx = global ? globalInd++ : scope.localInd++;
2180
+ let typeIdx = global ? globals['#ind']++ : scope.localInd++;
2249
2181
  target[name + '#type'] = { idx: typeIdx, type: Valtype.i32, name };
2250
2182
  }
2251
2183
 
@@ -2338,24 +2270,10 @@ const generateVar = (scope, decl) => {
2338
2270
  }
2339
2271
 
2340
2272
  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
- // }
2273
+ const alreadyArray = scope.arrays?.get(name) != null;
2356
2274
 
2357
2275
  const generated = generate(scope, x.init, global, name);
2358
- if (scope.arrays?.get(name) != null) {
2276
+ if (!alreadyArray && scope.arrays?.get(name) != null) {
2359
2277
  // hack to set local as pointer before
2360
2278
  out.push(...number(scope.arrays.get(name)), [ global ? Opcodes.global_set : Opcodes.local_set, idx ]);
2361
2279
  if (generated.at(-1) == Opcodes.i32_from_u) generated.pop();
@@ -2407,19 +2325,12 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
2407
2325
 
2408
2326
  // hack: .length setter
2409
2327
  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
2328
  const newValueTmp = localTmp(scope, '__length_setter_tmp');
2416
2329
  const pointerTmp = op === '=' ? null : localTmp(scope, '__member_setter_ptr_tmp', Valtype.i32);
2417
2330
 
2418
2331
  return [
2419
- ...(aotPointer ? number(0, Valtype.i32) : [
2420
- ...generate(scope, decl.left.object),
2421
- Opcodes.i32_to_u
2422
- ]),
2332
+ ...generate(scope, decl.left.object),
2333
+ Opcodes.i32_to_u,
2423
2334
  ...(!pointerTmp ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
2424
2335
 
2425
2336
  ...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
@@ -2430,7 +2341,7 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
2430
2341
  [ Opcodes.local_tee, newValueTmp ],
2431
2342
 
2432
2343
  Opcodes.i32_to_u,
2433
- [ Opcodes.i32_store, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(aotPointer ? pointer : 0) ],
2344
+ [ Opcodes.i32_store, Math.log2(ValtypeSize.i32) - 1, 0 ],
2434
2345
 
2435
2346
  [ Opcodes.local_get, newValueTmp ]
2436
2347
  ];
@@ -2438,21 +2349,14 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
2438
2349
 
2439
2350
  // arr[i]
2440
2351
  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
2352
  const newValueTmp = localTmp(scope, '__member_setter_val_tmp');
2447
2353
  const pointerTmp = op === '=' ? -1 : localTmp(scope, '__member_setter_ptr_tmp', Valtype.i32);
2448
2354
 
2449
2355
  return [
2450
2356
  ...typeSwitch(scope, getNodeType(scope, decl.left.object), {
2451
2357
  [TYPES.array]: [
2452
- ...(aotPointer ? [] : [
2453
- ...generate(scope, decl.left.object),
2454
- Opcodes.i32_to_u
2455
- ]),
2358
+ ...generate(scope, decl.left.object),
2359
+ Opcodes.i32_to_u,
2456
2360
 
2457
2361
  // get index as valtype
2458
2362
  ...generate(scope, decl.left.property),
@@ -2461,39 +2365,22 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
2461
2365
  // turn into byte offset by * valtypeSize (4 for i32, 8 for i64/f64)
2462
2366
  ...number(ValtypeSize[valtype] + 1, Valtype.i32),
2463
2367
  [ Opcodes.i32_mul ],
2464
- ...(aotPointer ? [] : [ [ Opcodes.i32_add ] ]),
2368
+ [ Opcodes.i32_add ],
2465
2369
  ...(op === '=' ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
2466
2370
 
2467
2371
  ...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
2468
2372
  [ Opcodes.local_get, pointerTmp ],
2469
- [ Opcodes.load, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ]
2373
+ [ Opcodes.load, 0, ValtypeSize.i32 ]
2470
2374
  ], generate(scope, decl.right), [
2471
2375
  [ Opcodes.local_get, pointerTmp ],
2472
- [ Opcodes.i32_load8_u, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32 + ValtypeSize[valtype]) ]
2376
+ [ Opcodes.i32_load8_u, 0, ValtypeSize.i32 + ValtypeSize[valtype] ]
2473
2377
  ], getNodeType(scope, decl.right), false, name, true)),
2474
2378
  [ Opcodes.local_tee, newValueTmp ],
2475
2379
 
2476
- [ Opcodes.store, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ]
2380
+ [ Opcodes.store, 0, ValtypeSize.i32 ]
2477
2381
  ],
2478
2382
 
2479
2383
  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
2384
  }, Blocktype.void),
2498
2385
 
2499
2386
  [ Opcodes.local_get, newValueTmp ]
@@ -2555,9 +2442,7 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
2555
2442
  [ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx ],
2556
2443
  [ isGlobal ? Opcodes.global_get : Opcodes.local_get, local.idx ],
2557
2444
 
2558
- // todo: string concat types
2559
-
2560
- ...setType(scope, name, TYPES.number)
2445
+ ...setType(scope, name, getNodeType(scope, decl))
2561
2446
  ];
2562
2447
  };
2563
2448
 
@@ -2571,7 +2456,7 @@ const generateUnary = (scope, decl) => {
2571
2456
  // * -1
2572
2457
 
2573
2458
  if (decl.prefix && decl.argument.type === 'Literal' && typeof decl.argument.value === 'number') {
2574
- // if -<N>, just return that
2459
+ // if -n, just return that as a const
2575
2460
  return number(-1 * decl.argument.value);
2576
2461
  }
2577
2462
 
@@ -2583,14 +2468,14 @@ const generateUnary = (scope, decl) => {
2583
2468
  case '!':
2584
2469
  const arg = decl.argument;
2585
2470
  if (arg.type === 'UnaryExpression' && arg.operator === '!') {
2586
- // !!x -> is x truthy
2471
+ // opt: !!x -> is x truthy
2587
2472
  return truthy(scope, generate(scope, arg.argument), getNodeType(scope, arg.argument), false, false);
2588
2473
  }
2474
+
2589
2475
  // !=
2590
- return falsy(scope, generate(scope, decl.argument), getNodeType(scope, decl.argument), false, false);
2476
+ return falsy(scope, generate(scope, arg), getNodeType(scope, arg), false, false);
2591
2477
 
2592
2478
  case '~':
2593
- // todo: does not handle Infinity properly (should convert to 0) (but opt const converting saves us sometimes)
2594
2479
  return [
2595
2480
  ...generate(scope, decl.argument),
2596
2481
  Opcodes.i32_to,
@@ -2877,12 +2762,12 @@ const generateForOf = (scope, decl) => {
2877
2762
 
2878
2763
  // // todo: we should only do this for strings but we don't know at compile-time :(
2879
2764
  // hack: this is naughty and will break things!
2880
- let newOut = number(0, Valtype.f64), newPointer = -1;
2765
+ let newOut = number(0, Valtype.i32), newPointer = number(0, Valtype.i32);
2881
2766
  if (pages.hasAnyString) {
2882
2767
  // todo: we use i16 even for bytestrings which should not make a bad thing happen, just be confusing for debugging?
2883
2768
  0, [ newOut, newPointer ] = makeArray(scope, {
2884
- rawElements: new Array(1)
2885
- }, isGlobal, leftName, true, 'i16');
2769
+ rawElements: new Array(0)
2770
+ }, isGlobal, leftName, true, 'i16', true);
2886
2771
  }
2887
2772
 
2888
2773
  // set type for local
@@ -2929,23 +2814,28 @@ const generateForOf = (scope, decl) => {
2929
2814
  [TYPES.string]: [
2930
2815
  ...setType(scope, leftName, TYPES.string),
2931
2816
 
2932
- [ Opcodes.loop, Blocktype.void ],
2933
-
2934
2817
  // setup new/out array
2935
2818
  ...newOut,
2936
- [ Opcodes.drop ],
2937
2819
 
2938
- ...number(0, Valtype.i32), // base 0 for store after
2820
+ // set length to 1
2821
+ ...number(1, Valtype.i32),
2822
+ [ Opcodes.i32_store, 0, 0 ],
2823
+
2824
+ [ Opcodes.loop, Blocktype.void ],
2825
+
2826
+ // use as pointer for store later
2827
+ ...newPointer,
2939
2828
 
2940
2829
  // load current string ind {arg}
2941
2830
  [ Opcodes.local_get, pointer ],
2942
- [ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128(ValtypeSize.i32) ],
2831
+ [ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1, ValtypeSize.i32 ],
2943
2832
 
2944
2833
  // store to new string ind 0
2945
- [ Opcodes.i32_store16, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128(newPointer + ValtypeSize.i32) ],
2834
+ [ Opcodes.i32_store16, Math.log2(ValtypeSize.i16) - 1, ValtypeSize.i32 ],
2946
2835
 
2947
2836
  // return new string (page)
2948
- ...number(newPointer),
2837
+ ...newPointer,
2838
+ Opcodes.i32_from_u,
2949
2839
 
2950
2840
  [ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx ],
2951
2841
 
@@ -2977,25 +2867,30 @@ const generateForOf = (scope, decl) => {
2977
2867
  [TYPES.bytestring]: [
2978
2868
  ...setType(scope, leftName, TYPES.bytestring),
2979
2869
 
2980
- [ Opcodes.loop, Blocktype.void ],
2981
-
2982
2870
  // setup new/out array
2983
2871
  ...newOut,
2984
- [ Opcodes.drop ],
2985
2872
 
2986
- ...number(0, Valtype.i32), // base 0 for store after
2873
+ // set length to 1
2874
+ ...number(1, Valtype.i32),
2875
+ [ Opcodes.i32_store, 0, 0 ],
2876
+
2877
+ [ Opcodes.loop, Blocktype.void ],
2878
+
2879
+ // use as pointer for store later
2880
+ ...newPointer,
2987
2881
 
2988
2882
  // load current string ind {arg}
2989
2883
  [ Opcodes.local_get, pointer ],
2990
2884
  [ Opcodes.local_get, counter ],
2991
2885
  [ Opcodes.i32_add ],
2992
- [ Opcodes.i32_load8_u, 0, ...unsignedLEB128(ValtypeSize.i32) ],
2886
+ [ Opcodes.i32_load8_u, 0, ValtypeSize.i32 ],
2993
2887
 
2994
2888
  // store to new string ind 0
2995
- [ Opcodes.i32_store8, 0, ...unsignedLEB128(newPointer + ValtypeSize.i32) ],
2889
+ [ Opcodes.i32_store8, 0, ValtypeSize.i32 ],
2996
2890
 
2997
2891
  // return new string (page)
2998
- ...number(newPointer),
2892
+ ...newPointer,
2893
+ Opcodes.i32_from_u,
2999
2894
 
3000
2895
  [ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx ],
3001
2896
 
@@ -3210,18 +3105,6 @@ const allocPage = (scope, reason, type) => {
3210
3105
  scope.pages ??= new Map();
3211
3106
  scope.pages.set(reason, { ind, type });
3212
3107
 
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
3108
  return ind;
3226
3109
  };
3227
3110
 
@@ -3259,39 +3142,58 @@ const compileBytes = (val, itemType) => {
3259
3142
  }
3260
3143
  };
3261
3144
 
3262
- const getAllocType = itemType => {
3263
- switch (itemType) {
3264
- case 'i8': return 'bytestring';
3265
- case 'i16': return 'string';
3145
+ const makeData = (scope, elements, offset = null, itemType, initEmpty) => {
3146
+ const length = elements.length;
3147
+
3148
+ // if length is 0 memory/data will just be 0000... anyway
3149
+ if (length === 0) return false;
3150
+
3151
+ let bytes = compileBytes(length, 'i32');
3152
+
3153
+ if (!initEmpty) for (let i = 0; i < length; i++) {
3154
+ if (elements[i] == null) continue;
3266
3155
 
3267
- default: return 'array';
3156
+ bytes.push(...compileBytes(elements[i], itemType));
3268
3157
  }
3269
- };
3270
3158
 
3271
- const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty = false, itemType = valtype, typed = false) => {
3272
- const out = [];
3159
+ const obj = { bytes };
3160
+ if (offset != null) obj.offset = offset;
3273
3161
 
3274
- scope.arrays ??= new Map();
3162
+ const idx = data.push(obj) - 1;
3275
3163
 
3276
- let firstAssign = false;
3277
- if (!scope.arrays.has(name) || name === '$undeclared') {
3278
- firstAssign = true;
3164
+ scope.data ??= [];
3165
+ scope.data.push(idx);
3279
3166
 
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;
3167
+ return { idx, size: bytes.length };
3168
+ };
3282
3169
 
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);
3170
+ const printStaticStr = str => {
3171
+ const out = [];
3286
3172
 
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);
3173
+ for (let i = 0; i < str.length; i++) {
3174
+ out.push(
3175
+ // ...number(str.charCodeAt(i)),
3176
+ ...number(str.charCodeAt(i), Valtype.i32),
3177
+ Opcodes.i32_from_u,
3178
+ [ Opcodes.call, importedFuncs.printChar ]
3179
+ );
3290
3180
  }
3291
3181
 
3292
- const pointer = scope.arrays.get(name);
3182
+ return out;
3183
+ };
3184
+
3185
+ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty = false, itemType = valtype, intOut = false, typed = false) => {
3186
+ if (itemType !== 'i16' && itemType !== 'i8') {
3187
+ pages.hasArray = true;
3188
+ } else {
3189
+ pages.hasAnyString = true;
3190
+ if (itemType === 'i8') pages.hasByteString = true;
3191
+ else pages.hasString = true;
3192
+ }
3193
+
3194
+ const out = [];
3293
3195
 
3294
- const local = global ? globals[name] : scope.locals[name];
3196
+ const uniqueName = name === '$undeclared' ? name + randId() : name;
3295
3197
 
3296
3198
  const useRawElements = !!decl.rawElements;
3297
3199
  const elements = useRawElements ? decl.rawElements : decl.elements;
@@ -3299,46 +3201,81 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
3299
3201
  const valtype = itemTypeToValtype[itemType];
3300
3202
  const length = elements.length;
3301
3203
 
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');
3204
+ const allocated = allocator.alloc({ scope, pages, globals, asmFunc, funcIndex }, uniqueName, { itemType });
3306
3205
 
3307
- if (!initEmpty) for (let i = 0; i < length; i++) {
3308
- if (elements[i] == null) continue;
3206
+ let pointer = allocated;
3207
+ if (allocator.constructor.name !== 'StaticAllocator') {
3208
+ // const tmp = localTmp(scope, '#makearray_pointer' + uniqueName, Valtype.i32);
3209
+ const tmp = localTmp(scope, '#makearray_pointer' + name, Valtype.i32);
3210
+ out.push(
3211
+ ...allocated,
3212
+ [ Opcodes.local_set, tmp ]
3213
+ );
3214
+
3215
+ if (Prefs.runtimeAllocLog) out.push(
3216
+ ...printStaticStr(`${name}: `),
3217
+
3218
+ [ Opcodes.local_get, tmp ],
3219
+ Opcodes.i32_from_u,
3220
+ [ Opcodes.call, 0 ],
3221
+
3222
+ ...number(10),
3223
+ [ Opcodes.call, 1 ]
3224
+ );
3309
3225
 
3310
- bytes.push(...compileBytes(elements[i], itemType));
3226
+ pointer = [ [ Opcodes.local_get, tmp ] ];
3227
+
3228
+ if (Prefs.data && useRawElements) {
3229
+ const data = makeData(scope, elements, null, itemType, initEmpty);
3230
+ if (data) {
3231
+ // init data
3232
+ out.push(
3233
+ ...pointer,
3234
+ ...number(0, Valtype.i32),
3235
+ ...number(data.size, Valtype.i32),
3236
+ [ ...Opcodes.memory_init, ...unsignedLEB128(data.idx), 0 ]
3237
+ );
3311
3238
  }
3312
3239
 
3313
- const ind = data.push({
3314
- offset: pointer,
3315
- bytes
3316
- }) - 1;
3240
+ // return pointer in out
3241
+ out.push(
3242
+ ...pointer,
3243
+ ...(!intOut ? [ Opcodes.i32_from_u ] : [])
3244
+ );
3317
3245
 
3318
- scope.data ??= [];
3319
- scope.data.push(ind);
3246
+ return [ out, pointer ];
3320
3247
  }
3248
+ } else {
3249
+ const rawPtr = read_signedLEB128(pointer[0].slice(1));
3321
3250
 
3322
- // local value as pointer
3323
- out.push(...number(pointer));
3251
+ scope.arrays ??= new Map();
3252
+ const firstAssign = !scope.arrays.has(uniqueName);
3253
+ if (firstAssign) scope.arrays.set(uniqueName, rawPtr);
3324
3254
 
3325
- return [ out, pointer ];
3326
- }
3255
+ if (Prefs.data && firstAssign && useRawElements) {
3256
+ makeData(scope, elements, rawPtr, itemType, initEmpty);
3327
3257
 
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
- );
3258
+ // local value as pointer
3259
+ return [ number(rawPtr, intOut ? Valtype.i32 : valtypeBinary), pointer ];
3260
+ }
3261
+
3262
+ const local = global ? globals[name] : scope.locals[name];
3263
+ const pointerTmp = local != null ? localTmp(scope, '#makearray_pointer_tmp', Valtype.i32) : null;
3264
+ if (pointerTmp != null) {
3265
+ out.push(
3266
+ [ global ? Opcodes.global_get : Opcodes.local_get, local.idx ],
3267
+ Opcodes.i32_to_u,
3268
+ [ Opcodes.local_set, pointerTmp ]
3269
+ );
3270
+
3271
+ pointer = [ [ Opcodes.local_get, pointerTmp ] ];
3272
+ }
3335
3273
  }
3336
3274
 
3337
- const pointerWasm = pointerTmp != null ? [ [ Opcodes.local_get, pointerTmp ] ] : number(pointer, Valtype.i32);
3338
3275
 
3339
3276
  // store length
3340
3277
  out.push(
3341
- ...pointerWasm,
3278
+ ...pointer,
3342
3279
  ...number(length, Valtype.i32),
3343
3280
  [ Opcodes.i32_store, Math.log2(ValtypeSize.i32) - 1, 0 ]
3344
3281
  );
@@ -3350,11 +3287,11 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
3350
3287
 
3351
3288
  const offset = ValtypeSize.i32 + i * sizePerEl;
3352
3289
  out.push(
3353
- ...pointerWasm,
3290
+ ...pointer,
3354
3291
  ...(useRawElements ? number(elements[i], Valtype[valtype]) : generate(scope, elements[i])),
3355
3292
  [ storeOp, 0, ...unsignedLEB128(offset) ],
3356
3293
  ...(!typed ? [] : [ // typed presumes !useRawElements
3357
- ...pointerWasm,
3294
+ ...pointer,
3358
3295
  ...getNodeType(scope, elements[i]),
3359
3296
  [ Opcodes.i32_store8, 0, ...unsignedLEB128(offset + ValtypeSize[itemType]) ]
3360
3297
  ])
@@ -3362,12 +3299,13 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
3362
3299
  }
3363
3300
 
3364
3301
  // local value as pointer
3365
- out.push(...pointerWasm, Opcodes.i32_from_u);
3302
+ out.push(...pointer);
3303
+ if (!intOut) out.push(Opcodes.i32_from_u);
3366
3304
 
3367
3305
  return [ out, pointer ];
3368
3306
  };
3369
3307
 
3370
- const storeArray = (scope, array, index, element, aotPointer = null) => {
3308
+ const storeArray = (scope, array, index, element) => {
3371
3309
  if (!Array.isArray(element)) element = generate(scope, element);
3372
3310
  if (typeof index === 'number') index = number(index);
3373
3311
 
@@ -3379,26 +3317,25 @@ const storeArray = (scope, array, index, element, aotPointer = null) => {
3379
3317
  Opcodes.i32_to_u,
3380
3318
  ...number(ValtypeSize[valtype] + 1, Valtype.i32),
3381
3319
  [ Opcodes.i32_mul ],
3382
- ...(aotPointer ? [] : [
3383
- ...array,
3384
- Opcodes.i32_to_u,
3385
- [ Opcodes.i32_add ],
3386
- ]),
3320
+
3321
+ ...array,
3322
+ Opcodes.i32_to_u,
3323
+ [ Opcodes.i32_add ],
3387
3324
  [ Opcodes.local_set, offset ],
3388
3325
 
3389
3326
  // store value
3390
3327
  [ Opcodes.local_get, offset ],
3391
3328
  ...generate(scope, element),
3392
- [ Opcodes.store, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ],
3329
+ [ Opcodes.store, 0, ValtypeSize.i32 ],
3393
3330
 
3394
3331
  // store type
3395
3332
  [ Opcodes.local_get, offset ],
3396
3333
  ...getNodeType(scope, element),
3397
- [ Opcodes.i32_store8, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32 + ValtypeSize[valtype]) ]
3334
+ [ Opcodes.i32_store8, 0, ValtypeSize.i32 + ValtypeSize[valtype] ]
3398
3335
  ];
3399
3336
  };
3400
3337
 
3401
- const loadArray = (scope, array, index, aotPointer = null) => {
3338
+ const loadArray = (scope, array, index) => {
3402
3339
  if (typeof index === 'number') index = number(index);
3403
3340
 
3404
3341
  const offset = localTmp(scope, '#loadArray_offset', Valtype.i32);
@@ -3409,20 +3346,19 @@ const loadArray = (scope, array, index, aotPointer = null) => {
3409
3346
  Opcodes.i32_to_u,
3410
3347
  ...number(ValtypeSize[valtype] + 1, Valtype.i32),
3411
3348
  [ Opcodes.i32_mul ],
3412
- ...(aotPointer ? [] : [
3413
- ...array,
3414
- Opcodes.i32_to_u,
3415
- [ Opcodes.i32_add ],
3416
- ]),
3349
+
3350
+ ...array,
3351
+ Opcodes.i32_to_u,
3352
+ [ Opcodes.i32_add ],
3417
3353
  [ Opcodes.local_set, offset ],
3418
3354
 
3419
3355
  // load value
3420
3356
  [ Opcodes.local_get, offset ],
3421
- [ Opcodes.load, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ],
3357
+ [ Opcodes.load, 0, ValtypeSize.i32 ],
3422
3358
 
3423
3359
  // load type
3424
3360
  [ Opcodes.local_get, offset ],
3425
- [ Opcodes.i32_load8_u, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32 + ValtypeSize[valtype]) ]
3361
+ [ Opcodes.i32_load8_u, 0, ValtypeSize.i32 + ValtypeSize[valtype] ]
3426
3362
  ];
3427
3363
  };
3428
3364
 
@@ -3454,7 +3390,7 @@ const makeString = (scope, str, global = false, name = '$undeclared', forceBytes
3454
3390
  };
3455
3391
 
3456
3392
  const generateArray = (scope, decl, global = false, name = '$undeclared', initEmpty = false) => {
3457
- return makeArray(scope, decl, global, name, initEmpty, valtype, true)[0];
3393
+ return makeArray(scope, decl, global, name, initEmpty, valtype, false, true)[0];
3458
3394
  };
3459
3395
 
3460
3396
  const generateObject = (scope, decl, global = false, name = '$undeclared') => {
@@ -3473,9 +3409,6 @@ const withType = (scope, wasm, type) => [
3473
3409
 
3474
3410
  const generateMember = (scope, decl, _global, _name) => {
3475
3411
  const name = decl.object.name;
3476
- const pointer = scope.arrays?.get(name);
3477
-
3478
- const aotPointer = Prefs.aotPointerOpt && pointer;
3479
3412
 
3480
3413
  // hack: .name
3481
3414
  if (decl.property.name === 'name') {
@@ -3510,18 +3443,16 @@ const generateMember = (scope, decl, _global, _name) => {
3510
3443
  }
3511
3444
 
3512
3445
  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);
3446
+ if (importedFuncs[name]) return withType(scope, number(importedFuncs[name].params.length ?? importedFuncs[name].params), TYPES.number);
3514
3447
  if (internalConstrs[name]) return withType(scope, number(internalConstrs[name].length ?? 0), TYPES.number);
3515
3448
 
3516
3449
  if (Prefs.fastLength) {
3517
3450
  // presume valid length object
3518
3451
  return [
3519
- ...(aotPointer ? number(0, Valtype.i32) : [
3520
- ...generate(scope, decl.object),
3521
- Opcodes.i32_to_u
3522
- ]),
3452
+ ...generate(scope, decl.object),
3453
+ Opcodes.i32_to_u,
3523
3454
 
3524
- [ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(aotPointer ? pointer : 0) ],
3455
+ [ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, 0 ],
3525
3456
  Opcodes.i32_from_u
3526
3457
  ];
3527
3458
  }
@@ -3530,12 +3461,10 @@ const generateMember = (scope, decl, _global, _name) => {
3530
3461
  const known = knownType(scope, type);
3531
3462
  if (known != null) {
3532
3463
  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
- ]),
3464
+ ...generate(scope, decl.object),
3465
+ Opcodes.i32_to_u,
3537
3466
 
3538
- [ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(aotPointer ? pointer : 0) ],
3467
+ [ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, 0 ],
3539
3468
  Opcodes.i32_from_u
3540
3469
  ];
3541
3470
 
@@ -3545,12 +3474,10 @@ const generateMember = (scope, decl, _global, _name) => {
3545
3474
  return [
3546
3475
  ...typeIsOneOf(getNodeType(scope, decl.object), [ TYPES.string, TYPES.bytestring, TYPES.array ]),
3547
3476
  [ Opcodes.if, valtypeBinary ],
3548
- ...(aotPointer ? number(0, Valtype.i32) : [
3549
- ...generate(scope, decl.object),
3550
- Opcodes.i32_to_u
3551
- ]),
3477
+ ...generate(scope, decl.object),
3478
+ Opcodes.i32_to_u,
3552
3479
 
3553
- [ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(aotPointer ? pointer : 0) ],
3480
+ [ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, 0 ],
3554
3481
  Opcodes.i32_from_u,
3555
3482
 
3556
3483
  ...setLastType(scope, TYPES.number),
@@ -3593,25 +3520,29 @@ const generateMember = (scope, decl, _global, _name) => {
3593
3520
 
3594
3521
  // // todo: we should only do this for strings but we don't know at compile-time :(
3595
3522
  // hack: this is naughty and will break things!
3596
- let newOut = number(0, valtypeBinary), newPointer = -1;
3597
- if (pages.hasAnyString) {
3523
+ let newOut = number(0, Valtype.i32), newPointer = number(0, Valtype.i32);
3524
+ if (pages.hasAnyString && knownType(scope, getNodeType(scope, decl.object)) !== TYPES.array) {
3525
+ // todo: we use i16 even for bytestrings which should not make a bad thing happen, just be confusing for debugging?
3598
3526
  0, [ newOut, newPointer ] = makeArray(scope, {
3599
- rawElements: new Array(1)
3600
- }, _global, _name, true, 'i16');
3527
+ rawElements: new Array(0)
3528
+ }, _global, _name, true, 'i16', true);
3601
3529
  }
3602
3530
 
3603
3531
  return typeSwitch(scope, getNodeType(scope, decl.object), {
3604
3532
  [TYPES.array]: [
3605
- ...loadArray(scope, object, property, aotPointer),
3533
+ ...loadArray(scope, object, property),
3606
3534
  ...setLastType(scope)
3607
3535
  ],
3608
-
3609
3536
  [TYPES.string]: [
3610
3537
  // setup new/out array
3611
3538
  ...newOut,
3612
- [ Opcodes.drop ],
3613
3539
 
3614
- ...number(0, Valtype.i32), // base 0 for store later
3540
+ // set length to 1
3541
+ ...number(1, Valtype.i32),
3542
+ [ Opcodes.i32_store, 0, 0 ],
3543
+
3544
+ // use as pointer for store later
3545
+ ...newPointer,
3615
3546
 
3616
3547
  ...property,
3617
3548
  Opcodes.i32_to_u,
@@ -3619,46 +3550,48 @@ const generateMember = (scope, decl, _global, _name) => {
3619
3550
  ...number(ValtypeSize.i16, Valtype.i32),
3620
3551
  [ Opcodes.i32_mul ],
3621
3552
 
3622
- ...(aotPointer ? [] : [
3623
- ...object,
3624
- Opcodes.i32_to_u,
3625
- [ Opcodes.i32_add ]
3626
- ]),
3553
+ ...object,
3554
+ Opcodes.i32_to_u,
3555
+ [ Opcodes.i32_add ],
3627
3556
 
3628
3557
  // load current string ind {arg}
3629
- [ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ],
3558
+ [ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1, ValtypeSize.i32 ],
3630
3559
 
3631
3560
  // store to new string ind 0
3632
- [ Opcodes.i32_store16, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128(newPointer + ValtypeSize.i32) ],
3561
+ [ Opcodes.i32_store16, Math.log2(ValtypeSize.i16) - 1, ValtypeSize.i32 ],
3633
3562
 
3634
3563
  // return new string (page)
3635
- ...number(newPointer),
3564
+ ...newPointer,
3565
+ Opcodes.i32_from_u,
3636
3566
  ...setLastType(scope, TYPES.string)
3637
3567
  ],
3638
3568
  [TYPES.bytestring]: [
3639
3569
  // setup new/out array
3640
3570
  ...newOut,
3641
- [ Opcodes.drop ],
3642
3571
 
3643
- ...number(0, Valtype.i32), // base 0 for store later
3572
+ // set length to 1
3573
+ ...number(1, Valtype.i32),
3574
+ [ Opcodes.i32_store, 0, 0 ],
3575
+
3576
+ // use as pointer for store later
3577
+ ...newPointer,
3644
3578
 
3645
3579
  ...property,
3646
3580
  Opcodes.i32_to_u,
3647
3581
 
3648
- ...(aotPointer ? [] : [
3649
- ...object,
3650
- Opcodes.i32_to_u,
3651
- [ Opcodes.i32_add ]
3652
- ]),
3582
+ ...object,
3583
+ Opcodes.i32_to_u,
3584
+ [ Opcodes.i32_add ],
3653
3585
 
3654
3586
  // load current string ind {arg}
3655
- [ Opcodes.i32_load8_u, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ],
3587
+ [ Opcodes.i32_load8_u, 0, ValtypeSize.i32 ],
3656
3588
 
3657
3589
  // store to new string ind 0
3658
- [ Opcodes.i32_store8, 0, ...unsignedLEB128(newPointer + ValtypeSize.i32) ],
3590
+ [ Opcodes.i32_store8, 0, ValtypeSize.i32 ],
3659
3591
 
3660
3592
  // return new string (page)
3661
- ...number(newPointer),
3593
+ ...newPointer,
3594
+ Opcodes.i32_from_u,
3662
3595
  ...setLastType(scope, TYPES.bytestring)
3663
3596
  ],
3664
3597
 
@@ -3666,7 +3599,7 @@ const generateMember = (scope, decl, _global, _name) => {
3666
3599
  });
3667
3600
  };
3668
3601
 
3669
- const randId = () => Math.random().toString(16).slice(0, -4);
3602
+ const randId = () => Math.random().toString(16).slice(1, -2).padEnd(12, '0');
3670
3603
 
3671
3604
  const objectHack = node => {
3672
3605
  if (!node) return node;
@@ -3718,7 +3651,7 @@ const generateFunc = (scope, decl) => {
3718
3651
  if (decl.async) return todo(scope, 'async functions are not supported');
3719
3652
  if (decl.generator) return todo(scope, 'generator functions are not supported');
3720
3653
 
3721
- const name = decl.id ? decl.id.name : `anonymous_${randId()}`;
3654
+ const name = decl.id ? decl.id.name : `anonymous${randId()}`;
3722
3655
  const params = decl.params ?? [];
3723
3656
 
3724
3657
  // TODO: share scope/locals between !!!
@@ -3732,6 +3665,9 @@ const generateFunc = (scope, decl) => {
3732
3665
  index: currentFuncIndex++
3733
3666
  };
3734
3667
 
3668
+ funcIndex[name] = func.index;
3669
+ funcs.push(func);
3670
+
3735
3671
  if (typedInput && decl.returnType) {
3736
3672
  const { type } = extractTypeAnnotation(decl.returnType);
3737
3673
  // if (type != null && !Prefs.indirectCalls) {
@@ -3741,12 +3677,26 @@ const generateFunc = (scope, decl) => {
3741
3677
  }
3742
3678
  }
3743
3679
 
3680
+ const defaultValues = {};
3744
3681
  for (let i = 0; i < params.length; i++) {
3745
- const name = params[i].name;
3682
+ let name;
3683
+ const x = params[i];
3684
+ switch (x.type) {
3685
+ case 'Identifier': {
3686
+ name = x.name;
3687
+ break;
3688
+ }
3689
+
3690
+ case 'AssignmentPattern': {
3691
+ name = x.left.name;
3692
+ defaultValues[name] = x.right;
3693
+ break;
3694
+ }
3695
+ }
3696
+
3746
3697
  // if (name == null) return todo('non-identifier args are not supported');
3747
3698
 
3748
3699
  allocVar(func, name, false);
3749
-
3750
3700
  if (typedInput && params[i].typeAnnotation) {
3751
3701
  addVarMetadata(func, name, false, extractTypeAnnotation(params[i]));
3752
3702
  }
@@ -3763,11 +3713,22 @@ const generateFunc = (scope, decl) => {
3763
3713
  };
3764
3714
  }
3765
3715
 
3766
- funcIndex[name] = func.index;
3767
- funcs.push(func);
3716
+ const prelude = [];
3717
+ for (const x in defaultValues) {
3718
+ prelude.push(
3719
+ ...getType(func, x),
3720
+ ...number(TYPES.undefined, Valtype.i32),
3721
+ [ Opcodes.i32_eq ],
3722
+ [ Opcodes.if, Blocktype.void ],
3723
+ ...generate(func, defaultValues[x], false, x),
3724
+ [ Opcodes.local_set, func.locals[x].idx ],
3768
3725
 
3769
- const wasm = generate(func, body);
3770
- func.wasm = wasm;
3726
+ ...setType(func, x, getNodeType(scope, defaultValues[x])),
3727
+ [ Opcodes.end ]
3728
+ );
3729
+ }
3730
+
3731
+ const wasm = func.wasm = prelude.concat(generate(func, body));
3771
3732
 
3772
3733
  if (name === 'main') func.gotLastType = true;
3773
3734
 
@@ -3803,9 +3764,9 @@ const internalConstrs = {
3803
3764
 
3804
3765
  // new Array(n)
3805
3766
 
3806
- const [ , pointer ] = makeArray(scope, {
3767
+ const [ out, pointer ] = makeArray(scope, {
3807
3768
  rawElements: new Array(0)
3808
- }, global, name, true);
3769
+ }, global, name, true, undefined, true);
3809
3770
 
3810
3771
  const arg = decl.arguments[0] ?? DEFAULT_VALUE;
3811
3772
 
@@ -3814,12 +3775,13 @@ const internalConstrs = {
3814
3775
  if (literalValue < 0 || !Number.isFinite(literalValue) || literalValue > 4294967295) return internalThrow(scope, 'RangeThrow', 'Invalid array length', true);
3815
3776
 
3816
3777
  return [
3817
- ...number(0, Valtype.i32),
3778
+ ...out,
3818
3779
  ...generate(scope, arg, global, name),
3819
3780
  Opcodes.i32_to_u,
3820
- [ Opcodes.i32_store, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(pointer) ],
3781
+ [ Opcodes.i32_store, Math.log2(ValtypeSize.i32) - 1, 0 ],
3821
3782
 
3822
- ...number(pointer)
3783
+ ...pointer,
3784
+ Opcodes.i32_from_u
3823
3785
  ];
3824
3786
  },
3825
3787
  type: TYPES.array,
@@ -3968,8 +3930,9 @@ const internalConstrs = {
3968
3930
  };
3969
3931
 
3970
3932
  export default program => {
3971
- globals = {};
3972
- globalInd = 0;
3933
+ globals = {
3934
+ ['#ind']: 0
3935
+ };
3973
3936
  tags = [];
3974
3937
  exceptions = [];
3975
3938
  funcs = [];
@@ -3979,19 +3942,8 @@ export default program => {
3979
3942
  data = [];
3980
3943
  currentFuncIndex = importedFuncs.length;
3981
3944
 
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
3945
  const valtypeInd = ['i32', 'i64', 'f64'].indexOf(valtype);
3990
3946
 
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
3947
  // set generic opcodes for current valtype
3996
3948
  Opcodes.const = [ Opcodes.i32_const, Opcodes.i64_const, Opcodes.f64_const ][valtypeInd];
3997
3949
  Opcodes.eq = [ Opcodes.i32_eq, Opcodes.i64_eq, Opcodes.f64_eq ][valtypeInd];
@@ -4013,6 +3965,7 @@ export default program => {
4013
3965
  builtinFuncs = new BuiltinFuncs();
4014
3966
  builtinVars = new BuiltinVars();
4015
3967
  prototypeFuncs = new PrototypeFuncs();
3968
+ allocator = makeAllocator(Prefs.allocator ?? 'static');
4016
3969
 
4017
3970
  program.id = { name: 'main' };
4018
3971
 
@@ -4055,6 +4008,8 @@ export default program => {
4055
4008
  else main.returns = [];
4056
4009
  }
4057
4010
 
4011
+ delete globals['#ind'];
4012
+
4058
4013
  // if blank main func and other exports, remove it
4059
4014
  if (main.wasm.length === 0 && funcs.reduce((acc, x) => acc + (x.export ? 1 : 0), 0) > 1) funcs.splice(main.index - importedFuncs.length, 1);
4060
4015