porffor 0.16.0-f9dde1759 → 0.16.0-fa3914030

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
 
@@ -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;
@@ -1318,7 +1263,15 @@ const getNodeType = (scope, node) => {
1318
1263
  }
1319
1264
 
1320
1265
  if (node.type === 'AssignmentExpression') {
1321
- 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
+ });
1322
1275
  }
1323
1276
 
1324
1277
  if (node.type === 'ArrayExpression') {
@@ -1337,23 +1290,6 @@ const getNodeType = (scope, node) => {
1337
1290
  if (knownLeft === TYPES.bytestring || knownRight === TYPES.bytestring) return TYPES.bytestring;
1338
1291
 
1339
1292
  return TYPES.number;
1340
-
1341
- // todo: string concat types
1342
- // if (node.operator !== '+') return TYPES.number;
1343
- // else return [
1344
- // // if left is string
1345
- // ...getNodeType(scope, node.left),
1346
- // ...number(TYPES.string, Valtype.i32),
1347
- // [ Opcodes.i32_eq ],
1348
-
1349
- // // if right is string
1350
- // ...getNodeType(scope, node.right),
1351
- // ...number(TYPES.string, Valtype.i32),
1352
- // [ Opcodes.i32_eq ],
1353
-
1354
- // // if either are true
1355
- // [ Opcodes.i32_or ],
1356
- // ];
1357
1293
  }
1358
1294
 
1359
1295
  if (node.type === 'UnaryExpression') {
@@ -1373,7 +1309,6 @@ const getNodeType = (scope, node) => {
1373
1309
  if (Prefs.fastLength) return TYPES.number;
1374
1310
  }
1375
1311
 
1376
-
1377
1312
  const objectKnownType = knownType(scope, getNodeType(scope, node.object));
1378
1313
  if (objectKnownType != null) {
1379
1314
  if (name === 'length') {
@@ -1384,7 +1319,6 @@ const getNodeType = (scope, node) => {
1384
1319
  if (node.computed) {
1385
1320
  if (objectKnownType === TYPES.string) return TYPES.string;
1386
1321
  if (objectKnownType === TYPES.bytestring) return TYPES.bytestring;
1387
- if (objectKnownType === TYPES.array) return TYPES.number;
1388
1322
  }
1389
1323
  }
1390
1324
 
@@ -1450,10 +1384,10 @@ const countLeftover = wasm => {
1450
1384
 
1451
1385
  if (depth === 0)
1452
1386
  if ([Opcodes.throw, Opcodes.drop, Opcodes.local_set, Opcodes.global_set].includes(inst[0])) count--;
1453
- 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)) {}
1454
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++;
1455
1389
  else if ([Opcodes.i32_store, Opcodes.i64_store, Opcodes.f64_store, Opcodes.i32_store16, Opcodes.i32_store8].includes(inst[0])) count -= 2;
1456
- 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;
1457
1391
  else if (inst[0] === Opcodes.return) count = 0;
1458
1392
  else if (inst[0] === Opcodes.call) {
1459
1393
  let func = funcs.find(x => x.index === inst[1]);
@@ -1725,7 +1659,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1725
1659
  }, generate(scope, decl.arguments[0] ?? DEFAULT_VALUE), getNodeType(scope, decl.arguments[0] ?? DEFAULT_VALUE), protoLocal, protoLocal2, (length, itemType) => {
1726
1660
  return makeArray(scope, {
1727
1661
  rawElements: new Array(length)
1728
- }, _global, _name, true, itemType);
1662
+ }, _global, _name, true, itemType, true);
1729
1663
  }, () => {
1730
1664
  optUnused = true;
1731
1665
  return unusedValue;
@@ -1998,13 +1932,18 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1998
1932
  continue;
1999
1933
  }
2000
1934
 
2001
- if (valtypeBinary !== Valtype.i32 && (
2002
- (builtinFuncs[name] && builtinFuncs[name].params[i * (typedParams ? 2 : 1)] === Valtype.i32)
2003
- // (importedFuncs[name] && name.startsWith('profile'))
2004
- )) {
1935
+ if (valtypeBinary !== Valtype.i32 &&
1936
+ (func && func.params[i * (typedParams ? 2 : 1)] === Valtype.i32)
1937
+ ) {
2005
1938
  out.push(Opcodes.i32_to);
2006
1939
  }
2007
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
+
2008
1947
  if (typedParams) out = out.concat(getNodeType(scope, arg));
2009
1948
  }
2010
1949
 
@@ -2026,6 +1965,10 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
2026
1965
  out.push(Opcodes.i32_from);
2027
1966
  }
2028
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
+
2029
1972
  return out;
2030
1973
  };
2031
1974
 
@@ -2049,9 +1992,9 @@ const generateNew = (scope, decl, _global, _name) => {
2049
1992
  if (
2050
1993
  (builtinFuncs[name] && !builtinFuncs[name].constr) ||
2051
1994
  (internalConstrs[name] && builtinFuncs[name].notConstr)
2052
- ) return internalThrow(scope, 'TypeError', `${name} is not a constructor`);
1995
+ ) return internalThrow(scope, 'TypeError', `${name} is not a constructor`, true);
2053
1996
 
2054
- 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)})`);
2055
1998
 
2056
1999
  return generateCall(scope, decl, _global, _name);
2057
2000
  };
@@ -2230,11 +2173,11 @@ const allocVar = (scope, name, global = false, type = true) => {
2230
2173
  return target[name].idx;
2231
2174
  }
2232
2175
 
2233
- let idx = global ? globalInd++ : scope.localInd++;
2176
+ let idx = global ? globals['#ind']++ : scope.localInd++;
2234
2177
  target[name] = { idx, type: valtypeBinary };
2235
2178
 
2236
2179
  if (type) {
2237
- let typeIdx = global ? globalInd++ : scope.localInd++;
2180
+ let typeIdx = global ? globals['#ind']++ : scope.localInd++;
2238
2181
  target[name + '#type'] = { idx: typeIdx, type: Valtype.i32, name };
2239
2182
  }
2240
2183
 
@@ -2327,24 +2270,10 @@ const generateVar = (scope, decl) => {
2327
2270
  }
2328
2271
 
2329
2272
  if (x.init) {
2330
- // if (isFuncType(x.init.type)) {
2331
- // // let a = function () { ... }
2332
- // x.init.id = { name };
2333
-
2334
- // const func = generateFunc(scope, x.init);
2335
-
2336
- // out.push(
2337
- // ...number(func.index - importedFuncs.length),
2338
- // [ global ? Opcodes.global_set : Opcodes.local_set, idx ],
2339
-
2340
- // ...setType(scope, name, TYPES.function)
2341
- // );
2342
-
2343
- // continue;
2344
- // }
2273
+ const alreadyArray = scope.arrays?.get(name) != null;
2345
2274
 
2346
2275
  const generated = generate(scope, x.init, global, name);
2347
- if (scope.arrays?.get(name) != null) {
2276
+ if (!alreadyArray && scope.arrays?.get(name) != null) {
2348
2277
  // hack to set local as pointer before
2349
2278
  out.push(...number(scope.arrays.get(name)), [ global ? Opcodes.global_set : Opcodes.local_set, idx ]);
2350
2279
  if (generated.at(-1) == Opcodes.i32_from_u) generated.pop();
@@ -2396,19 +2325,12 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
2396
2325
 
2397
2326
  // hack: .length setter
2398
2327
  if (decl.left.type === 'MemberExpression' && decl.left.property.name === 'length') {
2399
- const name = decl.left.object.name;
2400
- const pointer = scope.arrays?.get(name);
2401
-
2402
- const aotPointer = Prefs.aotPointerOpt && pointer != null;
2403
-
2404
2328
  const newValueTmp = localTmp(scope, '__length_setter_tmp');
2405
2329
  const pointerTmp = op === '=' ? null : localTmp(scope, '__member_setter_ptr_tmp', Valtype.i32);
2406
2330
 
2407
2331
  return [
2408
- ...(aotPointer ? number(0, Valtype.i32) : [
2409
- ...generate(scope, decl.left.object),
2410
- Opcodes.i32_to_u
2411
- ]),
2332
+ ...generate(scope, decl.left.object),
2333
+ Opcodes.i32_to_u,
2412
2334
  ...(!pointerTmp ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
2413
2335
 
2414
2336
  ...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
@@ -2419,7 +2341,7 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
2419
2341
  [ Opcodes.local_tee, newValueTmp ],
2420
2342
 
2421
2343
  Opcodes.i32_to_u,
2422
- [ Opcodes.i32_store, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(aotPointer ? pointer : 0) ],
2344
+ [ Opcodes.i32_store, Math.log2(ValtypeSize.i32) - 1, 0 ],
2423
2345
 
2424
2346
  [ Opcodes.local_get, newValueTmp ]
2425
2347
  ];
@@ -2427,21 +2349,14 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
2427
2349
 
2428
2350
  // arr[i]
2429
2351
  if (decl.left.type === 'MemberExpression' && decl.left.computed) {
2430
- const name = decl.left.object.name;
2431
- const pointer = scope.arrays?.get(name);
2432
-
2433
- const aotPointer = Prefs.aotPointerOpt && pointer != null;
2434
-
2435
2352
  const newValueTmp = localTmp(scope, '__member_setter_val_tmp');
2436
2353
  const pointerTmp = op === '=' ? -1 : localTmp(scope, '__member_setter_ptr_tmp', Valtype.i32);
2437
2354
 
2438
2355
  return [
2439
2356
  ...typeSwitch(scope, getNodeType(scope, decl.left.object), {
2440
2357
  [TYPES.array]: [
2441
- ...(aotPointer ? [] : [
2442
- ...generate(scope, decl.left.object),
2443
- Opcodes.i32_to_u
2444
- ]),
2358
+ ...generate(scope, decl.left.object),
2359
+ Opcodes.i32_to_u,
2445
2360
 
2446
2361
  // get index as valtype
2447
2362
  ...generate(scope, decl.left.property),
@@ -2450,39 +2365,22 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
2450
2365
  // turn into byte offset by * valtypeSize (4 for i32, 8 for i64/f64)
2451
2366
  ...number(ValtypeSize[valtype] + 1, Valtype.i32),
2452
2367
  [ Opcodes.i32_mul ],
2453
- ...(aotPointer ? [] : [ [ Opcodes.i32_add ] ]),
2368
+ [ Opcodes.i32_add ],
2454
2369
  ...(op === '=' ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
2455
2370
 
2456
2371
  ...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
2457
2372
  [ Opcodes.local_get, pointerTmp ],
2458
- [ Opcodes.load, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ]
2373
+ [ Opcodes.load, 0, ValtypeSize.i32 ]
2459
2374
  ], generate(scope, decl.right), [
2460
2375
  [ Opcodes.local_get, pointerTmp ],
2461
- [ Opcodes.i32_load8_u, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32 + ValtypeSize[valtype]) ]
2376
+ [ Opcodes.i32_load8_u, 0, ValtypeSize.i32 + ValtypeSize[valtype] ]
2462
2377
  ], getNodeType(scope, decl.right), false, name, true)),
2463
2378
  [ Opcodes.local_tee, newValueTmp ],
2464
2379
 
2465
- [ Opcodes.store, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ]
2380
+ [ Opcodes.store, 0, ValtypeSize.i32 ]
2466
2381
  ],
2467
2382
 
2468
2383
  default: internalThrow(scope, 'TypeError', `Cannot assign member with non-array`)
2469
-
2470
- // [TYPES.string]: [
2471
- // // turn into byte offset by * sizeof i16
2472
- // ...number(ValtypeSize.i16, Valtype.i32),
2473
- // [ Opcodes.i32_mul ],
2474
- // ...(aotPointer ? [] : [ [ Opcodes.i32_add ] ]),
2475
- // ...(op === '=' ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
2476
-
2477
- // ...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
2478
- // [ Opcodes.local_get, pointerTmp ],
2479
- // [ Opcodes.load, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ]
2480
- // ], generate(scope, decl.right), number(TYPES.string, Valtype.i32), getNodeType(scope, decl.right))),
2481
- // [ Opcodes.local_tee, newValueTmp ],
2482
-
2483
- // Opcodes.i32_to_u,
2484
- // [ StoreOps.i16, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ]
2485
- // ]
2486
2384
  }, Blocktype.void),
2487
2385
 
2488
2386
  [ Opcodes.local_get, newValueTmp ]
@@ -2544,9 +2442,7 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
2544
2442
  [ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx ],
2545
2443
  [ isGlobal ? Opcodes.global_get : Opcodes.local_get, local.idx ],
2546
2444
 
2547
- // todo: string concat types
2548
-
2549
- ...setType(scope, name, TYPES.number)
2445
+ ...setType(scope, name, getNodeType(scope, decl))
2550
2446
  ];
2551
2447
  };
2552
2448
 
@@ -2866,12 +2762,12 @@ const generateForOf = (scope, decl) => {
2866
2762
 
2867
2763
  // // todo: we should only do this for strings but we don't know at compile-time :(
2868
2764
  // hack: this is naughty and will break things!
2869
- let newOut = number(0, Valtype.f64), newPointer = -1;
2765
+ let newOut = number(0, Valtype.i32), newPointer = number(0, Valtype.i32);
2870
2766
  if (pages.hasAnyString) {
2871
2767
  // todo: we use i16 even for bytestrings which should not make a bad thing happen, just be confusing for debugging?
2872
2768
  0, [ newOut, newPointer ] = makeArray(scope, {
2873
- rawElements: new Array(1)
2874
- }, isGlobal, leftName, true, 'i16');
2769
+ rawElements: new Array(0)
2770
+ }, isGlobal, leftName, true, 'i16', true);
2875
2771
  }
2876
2772
 
2877
2773
  // set type for local
@@ -2918,23 +2814,28 @@ const generateForOf = (scope, decl) => {
2918
2814
  [TYPES.string]: [
2919
2815
  ...setType(scope, leftName, TYPES.string),
2920
2816
 
2921
- [ Opcodes.loop, Blocktype.void ],
2922
-
2923
2817
  // setup new/out array
2924
2818
  ...newOut,
2925
- [ Opcodes.drop ],
2926
2819
 
2927
- ...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,
2928
2828
 
2929
2829
  // load current string ind {arg}
2930
2830
  [ Opcodes.local_get, pointer ],
2931
- [ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128(ValtypeSize.i32) ],
2831
+ [ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1, ValtypeSize.i32 ],
2932
2832
 
2933
2833
  // store to new string ind 0
2934
- [ Opcodes.i32_store16, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128(newPointer + ValtypeSize.i32) ],
2834
+ [ Opcodes.i32_store16, Math.log2(ValtypeSize.i16) - 1, ValtypeSize.i32 ],
2935
2835
 
2936
2836
  // return new string (page)
2937
- ...number(newPointer),
2837
+ ...newPointer,
2838
+ Opcodes.i32_from_u,
2938
2839
 
2939
2840
  [ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx ],
2940
2841
 
@@ -2966,25 +2867,30 @@ const generateForOf = (scope, decl) => {
2966
2867
  [TYPES.bytestring]: [
2967
2868
  ...setType(scope, leftName, TYPES.bytestring),
2968
2869
 
2969
- [ Opcodes.loop, Blocktype.void ],
2970
-
2971
2870
  // setup new/out array
2972
2871
  ...newOut,
2973
- [ Opcodes.drop ],
2974
2872
 
2975
- ...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,
2976
2881
 
2977
2882
  // load current string ind {arg}
2978
2883
  [ Opcodes.local_get, pointer ],
2979
2884
  [ Opcodes.local_get, counter ],
2980
2885
  [ Opcodes.i32_add ],
2981
- [ Opcodes.i32_load8_u, 0, ...unsignedLEB128(ValtypeSize.i32) ],
2886
+ [ Opcodes.i32_load8_u, 0, ValtypeSize.i32 ],
2982
2887
 
2983
2888
  // store to new string ind 0
2984
- [ Opcodes.i32_store8, 0, ...unsignedLEB128(newPointer + ValtypeSize.i32) ],
2889
+ [ Opcodes.i32_store8, 0, ValtypeSize.i32 ],
2985
2890
 
2986
2891
  // return new string (page)
2987
- ...number(newPointer),
2892
+ ...newPointer,
2893
+ Opcodes.i32_from_u,
2988
2894
 
2989
2895
  [ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx ],
2990
2896
 
@@ -3199,18 +3105,6 @@ const allocPage = (scope, reason, type) => {
3199
3105
  scope.pages ??= new Map();
3200
3106
  scope.pages.set(reason, { ind, type });
3201
3107
 
3202
- if (Prefs.allocLog) log('alloc', `allocated new page of memory (${ind}) | ${reason} (type: ${type})`);
3203
-
3204
- return ind;
3205
- };
3206
-
3207
- // todo: add scope.pages
3208
- const freePage = reason => {
3209
- const { ind } = pages.get(reason);
3210
- pages.delete(reason);
3211
-
3212
- if (Prefs.allocLog) log('alloc', `freed page of memory (${ind}) | ${reason}`);
3213
-
3214
3108
  return ind;
3215
3109
  };
3216
3110
 
@@ -3248,39 +3142,58 @@ const compileBytes = (val, itemType) => {
3248
3142
  }
3249
3143
  };
3250
3144
 
3251
- const getAllocType = itemType => {
3252
- switch (itemType) {
3253
- case 'i8': return 'bytestring';
3254
- 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;
3255
3150
 
3256
- default: return 'array';
3151
+ let bytes = compileBytes(length, 'i32');
3152
+
3153
+ if (!initEmpty) for (let i = 0; i < length; i++) {
3154
+ if (elements[i] == null) continue;
3155
+
3156
+ bytes.push(...compileBytes(elements[i], itemType));
3257
3157
  }
3258
- };
3259
3158
 
3260
- const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty = false, itemType = valtype, typed = false) => {
3261
- const out = [];
3159
+ const obj = { bytes };
3160
+ if (offset != null) obj.offset = offset;
3262
3161
 
3263
- scope.arrays ??= new Map();
3162
+ const idx = data.push(obj) - 1;
3264
3163
 
3265
- let firstAssign = false;
3266
- if (!scope.arrays.has(name) || name === '$undeclared') {
3267
- firstAssign = true;
3164
+ scope.data ??= [];
3165
+ scope.data.push(idx);
3268
3166
 
3269
- // todo: can we just have 1 undeclared array? probably not? but this is not really memory efficient
3270
- const uniqueName = name === '$undeclared' ? name + randId() : name;
3167
+ return { idx, size: bytes.length };
3168
+ };
3271
3169
 
3272
- let page;
3273
- if (Prefs.scopedPageNames) page = allocPage(scope, `${getAllocType(itemType)}: ${scope.name}/${uniqueName}`, itemType);
3274
- else page = allocPage(scope, `${getAllocType(itemType)}: ${uniqueName}`, itemType);
3170
+ const printStaticStr = str => {
3171
+ const out = [];
3275
3172
 
3276
- // hack: use 1 for page 0 pointer for fast truthiness
3277
- const ptr = page === 0 ? 1 : (page * pageSize);
3278
- 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
+ );
3180
+ }
3181
+
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;
3279
3192
  }
3280
3193
 
3281
- const pointer = scope.arrays.get(name);
3194
+ const out = [];
3282
3195
 
3283
- const local = global ? globals[name] : scope.locals[name];
3196
+ const uniqueName = name === '$undeclared' ? name + randId() : name;
3284
3197
 
3285
3198
  const useRawElements = !!decl.rawElements;
3286
3199
  const elements = useRawElements ? decl.rawElements : decl.elements;
@@ -3288,46 +3201,81 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
3288
3201
  const valtype = itemTypeToValtype[itemType];
3289
3202
  const length = elements.length;
3290
3203
 
3291
- if (firstAssign && useRawElements && !Prefs.noData) {
3292
- // if length is 0 memory/data will just be 0000... anyway
3293
- if (length !== 0) {
3294
- let bytes = compileBytes(length, 'i32');
3204
+ const allocated = allocator.alloc({ scope, pages, globals, asmFunc, funcIndex }, uniqueName, { itemType });
3205
+
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
+ );
3295
3225
 
3296
- if (!initEmpty) for (let i = 0; i < length; i++) {
3297
- if (elements[i] == null) continue;
3226
+ pointer = [ [ Opcodes.local_get, tmp ] ];
3298
3227
 
3299
- bytes.push(...compileBytes(elements[i], itemType));
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
+ );
3300
3238
  }
3301
3239
 
3302
- const ind = data.push({
3303
- offset: pointer,
3304
- bytes
3305
- }) - 1;
3240
+ // return pointer in out
3241
+ out.push(
3242
+ ...pointer,
3243
+ ...(!intOut ? [ Opcodes.i32_from_u ] : [])
3244
+ );
3306
3245
 
3307
- scope.data ??= [];
3308
- scope.data.push(ind);
3246
+ return [ out, pointer ];
3309
3247
  }
3248
+ } else {
3249
+ const rawPtr = read_signedLEB128(pointer[0].slice(1));
3310
3250
 
3311
- // local value as pointer
3312
- out.push(...number(pointer));
3251
+ scope.arrays ??= new Map();
3252
+ const firstAssign = !scope.arrays.has(uniqueName);
3253
+ if (firstAssign) scope.arrays.set(uniqueName, rawPtr);
3313
3254
 
3314
- return [ out, pointer ];
3315
- }
3255
+ if (Prefs.data && firstAssign && useRawElements) {
3256
+ makeData(scope, elements, rawPtr, itemType, initEmpty);
3316
3257
 
3317
- const pointerTmp = local != null ? localTmp(scope, '#makearray_pointer_tmp', Valtype.i32) : null;
3318
- if (pointerTmp != null) {
3319
- out.push(
3320
- [ global ? Opcodes.global_get : Opcodes.local_get, local.idx ],
3321
- Opcodes.i32_to_u,
3322
- [ Opcodes.local_set, pointerTmp ]
3323
- );
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
+ }
3324
3273
  }
3325
3274
 
3326
- const pointerWasm = pointerTmp != null ? [ [ Opcodes.local_get, pointerTmp ] ] : number(pointer, Valtype.i32);
3327
3275
 
3328
3276
  // store length
3329
3277
  out.push(
3330
- ...pointerWasm,
3278
+ ...pointer,
3331
3279
  ...number(length, Valtype.i32),
3332
3280
  [ Opcodes.i32_store, Math.log2(ValtypeSize.i32) - 1, 0 ]
3333
3281
  );
@@ -3339,11 +3287,11 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
3339
3287
 
3340
3288
  const offset = ValtypeSize.i32 + i * sizePerEl;
3341
3289
  out.push(
3342
- ...pointerWasm,
3290
+ ...pointer,
3343
3291
  ...(useRawElements ? number(elements[i], Valtype[valtype]) : generate(scope, elements[i])),
3344
3292
  [ storeOp, 0, ...unsignedLEB128(offset) ],
3345
3293
  ...(!typed ? [] : [ // typed presumes !useRawElements
3346
- ...pointerWasm,
3294
+ ...pointer,
3347
3295
  ...getNodeType(scope, elements[i]),
3348
3296
  [ Opcodes.i32_store8, 0, ...unsignedLEB128(offset + ValtypeSize[itemType]) ]
3349
3297
  ])
@@ -3351,12 +3299,13 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
3351
3299
  }
3352
3300
 
3353
3301
  // local value as pointer
3354
- out.push(...pointerWasm, Opcodes.i32_from_u);
3302
+ out.push(...pointer);
3303
+ if (!intOut) out.push(Opcodes.i32_from_u);
3355
3304
 
3356
3305
  return [ out, pointer ];
3357
3306
  };
3358
3307
 
3359
- const storeArray = (scope, array, index, element, aotPointer = null) => {
3308
+ const storeArray = (scope, array, index, element) => {
3360
3309
  if (!Array.isArray(element)) element = generate(scope, element);
3361
3310
  if (typeof index === 'number') index = number(index);
3362
3311
 
@@ -3368,26 +3317,25 @@ const storeArray = (scope, array, index, element, aotPointer = null) => {
3368
3317
  Opcodes.i32_to_u,
3369
3318
  ...number(ValtypeSize[valtype] + 1, Valtype.i32),
3370
3319
  [ Opcodes.i32_mul ],
3371
- ...(aotPointer ? [] : [
3372
- ...array,
3373
- Opcodes.i32_to_u,
3374
- [ Opcodes.i32_add ],
3375
- ]),
3320
+
3321
+ ...array,
3322
+ Opcodes.i32_to_u,
3323
+ [ Opcodes.i32_add ],
3376
3324
  [ Opcodes.local_set, offset ],
3377
3325
 
3378
3326
  // store value
3379
3327
  [ Opcodes.local_get, offset ],
3380
3328
  ...generate(scope, element),
3381
- [ Opcodes.store, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ],
3329
+ [ Opcodes.store, 0, ValtypeSize.i32 ],
3382
3330
 
3383
3331
  // store type
3384
3332
  [ Opcodes.local_get, offset ],
3385
3333
  ...getNodeType(scope, element),
3386
- [ Opcodes.i32_store8, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32 + ValtypeSize[valtype]) ]
3334
+ [ Opcodes.i32_store8, 0, ValtypeSize.i32 + ValtypeSize[valtype] ]
3387
3335
  ];
3388
3336
  };
3389
3337
 
3390
- const loadArray = (scope, array, index, aotPointer = null) => {
3338
+ const loadArray = (scope, array, index) => {
3391
3339
  if (typeof index === 'number') index = number(index);
3392
3340
 
3393
3341
  const offset = localTmp(scope, '#loadArray_offset', Valtype.i32);
@@ -3398,20 +3346,19 @@ const loadArray = (scope, array, index, aotPointer = null) => {
3398
3346
  Opcodes.i32_to_u,
3399
3347
  ...number(ValtypeSize[valtype] + 1, Valtype.i32),
3400
3348
  [ Opcodes.i32_mul ],
3401
- ...(aotPointer ? [] : [
3402
- ...array,
3403
- Opcodes.i32_to_u,
3404
- [ Opcodes.i32_add ],
3405
- ]),
3349
+
3350
+ ...array,
3351
+ Opcodes.i32_to_u,
3352
+ [ Opcodes.i32_add ],
3406
3353
  [ Opcodes.local_set, offset ],
3407
3354
 
3408
3355
  // load value
3409
3356
  [ Opcodes.local_get, offset ],
3410
- [ Opcodes.load, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ],
3357
+ [ Opcodes.load, 0, ValtypeSize.i32 ],
3411
3358
 
3412
3359
  // load type
3413
3360
  [ Opcodes.local_get, offset ],
3414
- [ Opcodes.i32_load8_u, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32 + ValtypeSize[valtype]) ]
3361
+ [ Opcodes.i32_load8_u, 0, ValtypeSize.i32 + ValtypeSize[valtype] ]
3415
3362
  ];
3416
3363
  };
3417
3364
 
@@ -3443,7 +3390,7 @@ const makeString = (scope, str, global = false, name = '$undeclared', forceBytes
3443
3390
  };
3444
3391
 
3445
3392
  const generateArray = (scope, decl, global = false, name = '$undeclared', initEmpty = false) => {
3446
- return makeArray(scope, decl, global, name, initEmpty, valtype, true)[0];
3393
+ return makeArray(scope, decl, global, name, initEmpty, valtype, false, true)[0];
3447
3394
  };
3448
3395
 
3449
3396
  const generateObject = (scope, decl, global = false, name = '$undeclared') => {
@@ -3462,9 +3409,6 @@ const withType = (scope, wasm, type) => [
3462
3409
 
3463
3410
  const generateMember = (scope, decl, _global, _name) => {
3464
3411
  const name = decl.object.name;
3465
- const pointer = scope.arrays?.get(name);
3466
-
3467
- const aotPointer = Prefs.aotPointerOpt && pointer;
3468
3412
 
3469
3413
  // hack: .name
3470
3414
  if (decl.property.name === 'name') {
@@ -3505,12 +3449,10 @@ const generateMember = (scope, decl, _global, _name) => {
3505
3449
  if (Prefs.fastLength) {
3506
3450
  // presume valid length object
3507
3451
  return [
3508
- ...(aotPointer ? number(0, Valtype.i32) : [
3509
- ...generate(scope, decl.object),
3510
- Opcodes.i32_to_u
3511
- ]),
3452
+ ...generate(scope, decl.object),
3453
+ Opcodes.i32_to_u,
3512
3454
 
3513
- [ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(aotPointer ? pointer : 0) ],
3455
+ [ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, 0 ],
3514
3456
  Opcodes.i32_from_u
3515
3457
  ];
3516
3458
  }
@@ -3519,12 +3461,10 @@ const generateMember = (scope, decl, _global, _name) => {
3519
3461
  const known = knownType(scope, type);
3520
3462
  if (known != null) {
3521
3463
  if ([ TYPES.string, TYPES.bytestring, TYPES.array ].includes(known)) return [
3522
- ...(aotPointer ? number(0, Valtype.i32) : [
3523
- ...generate(scope, decl.object),
3524
- Opcodes.i32_to_u
3525
- ]),
3464
+ ...generate(scope, decl.object),
3465
+ Opcodes.i32_to_u,
3526
3466
 
3527
- [ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(aotPointer ? pointer : 0) ],
3467
+ [ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, 0 ],
3528
3468
  Opcodes.i32_from_u
3529
3469
  ];
3530
3470
 
@@ -3534,12 +3474,10 @@ const generateMember = (scope, decl, _global, _name) => {
3534
3474
  return [
3535
3475
  ...typeIsOneOf(getNodeType(scope, decl.object), [ TYPES.string, TYPES.bytestring, TYPES.array ]),
3536
3476
  [ Opcodes.if, valtypeBinary ],
3537
- ...(aotPointer ? number(0, Valtype.i32) : [
3538
- ...generate(scope, decl.object),
3539
- Opcodes.i32_to_u
3540
- ]),
3477
+ ...generate(scope, decl.object),
3478
+ Opcodes.i32_to_u,
3541
3479
 
3542
- [ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(aotPointer ? pointer : 0) ],
3480
+ [ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, 0 ],
3543
3481
  Opcodes.i32_from_u,
3544
3482
 
3545
3483
  ...setLastType(scope, TYPES.number),
@@ -3582,25 +3520,29 @@ const generateMember = (scope, decl, _global, _name) => {
3582
3520
 
3583
3521
  // // todo: we should only do this for strings but we don't know at compile-time :(
3584
3522
  // hack: this is naughty and will break things!
3585
- let newOut = number(0, valtypeBinary), newPointer = -1;
3586
- 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?
3587
3526
  0, [ newOut, newPointer ] = makeArray(scope, {
3588
- rawElements: new Array(1)
3589
- }, _global, _name, true, 'i16');
3527
+ rawElements: new Array(0)
3528
+ }, _global, _name, true, 'i16', true);
3590
3529
  }
3591
3530
 
3592
3531
  return typeSwitch(scope, getNodeType(scope, decl.object), {
3593
3532
  [TYPES.array]: [
3594
- ...loadArray(scope, object, property, aotPointer),
3533
+ ...loadArray(scope, object, property),
3595
3534
  ...setLastType(scope)
3596
3535
  ],
3597
-
3598
3536
  [TYPES.string]: [
3599
3537
  // setup new/out array
3600
3538
  ...newOut,
3601
- [ Opcodes.drop ],
3602
3539
 
3603
- ...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,
3604
3546
 
3605
3547
  ...property,
3606
3548
  Opcodes.i32_to_u,
@@ -3608,46 +3550,48 @@ const generateMember = (scope, decl, _global, _name) => {
3608
3550
  ...number(ValtypeSize.i16, Valtype.i32),
3609
3551
  [ Opcodes.i32_mul ],
3610
3552
 
3611
- ...(aotPointer ? [] : [
3612
- ...object,
3613
- Opcodes.i32_to_u,
3614
- [ Opcodes.i32_add ]
3615
- ]),
3553
+ ...object,
3554
+ Opcodes.i32_to_u,
3555
+ [ Opcodes.i32_add ],
3616
3556
 
3617
3557
  // load current string ind {arg}
3618
- [ 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 ],
3619
3559
 
3620
3560
  // store to new string ind 0
3621
- [ Opcodes.i32_store16, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128(newPointer + ValtypeSize.i32) ],
3561
+ [ Opcodes.i32_store16, Math.log2(ValtypeSize.i16) - 1, ValtypeSize.i32 ],
3622
3562
 
3623
3563
  // return new string (page)
3624
- ...number(newPointer),
3564
+ ...newPointer,
3565
+ Opcodes.i32_from_u,
3625
3566
  ...setLastType(scope, TYPES.string)
3626
3567
  ],
3627
3568
  [TYPES.bytestring]: [
3628
3569
  // setup new/out array
3629
3570
  ...newOut,
3630
- [ Opcodes.drop ],
3631
3571
 
3632
- ...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,
3633
3578
 
3634
3579
  ...property,
3635
3580
  Opcodes.i32_to_u,
3636
3581
 
3637
- ...(aotPointer ? [] : [
3638
- ...object,
3639
- Opcodes.i32_to_u,
3640
- [ Opcodes.i32_add ]
3641
- ]),
3582
+ ...object,
3583
+ Opcodes.i32_to_u,
3584
+ [ Opcodes.i32_add ],
3642
3585
 
3643
3586
  // load current string ind {arg}
3644
- [ Opcodes.i32_load8_u, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ],
3587
+ [ Opcodes.i32_load8_u, 0, ValtypeSize.i32 ],
3645
3588
 
3646
3589
  // store to new string ind 0
3647
- [ Opcodes.i32_store8, 0, ...unsignedLEB128(newPointer + ValtypeSize.i32) ],
3590
+ [ Opcodes.i32_store8, 0, ValtypeSize.i32 ],
3648
3591
 
3649
3592
  // return new string (page)
3650
- ...number(newPointer),
3593
+ ...newPointer,
3594
+ Opcodes.i32_from_u,
3651
3595
  ...setLastType(scope, TYPES.bytestring)
3652
3596
  ],
3653
3597
 
@@ -3792,9 +3736,9 @@ const internalConstrs = {
3792
3736
 
3793
3737
  // new Array(n)
3794
3738
 
3795
- const [ , pointer ] = makeArray(scope, {
3739
+ const [ out, pointer ] = makeArray(scope, {
3796
3740
  rawElements: new Array(0)
3797
- }, global, name, true);
3741
+ }, global, name, true, undefined, true);
3798
3742
 
3799
3743
  const arg = decl.arguments[0] ?? DEFAULT_VALUE;
3800
3744
 
@@ -3803,12 +3747,13 @@ const internalConstrs = {
3803
3747
  if (literalValue < 0 || !Number.isFinite(literalValue) || literalValue > 4294967295) return internalThrow(scope, 'RangeThrow', 'Invalid array length', true);
3804
3748
 
3805
3749
  return [
3806
- ...number(0, Valtype.i32),
3750
+ ...out,
3807
3751
  ...generate(scope, arg, global, name),
3808
3752
  Opcodes.i32_to_u,
3809
- [ Opcodes.i32_store, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(pointer) ],
3753
+ [ Opcodes.i32_store, Math.log2(ValtypeSize.i32) - 1, 0 ],
3810
3754
 
3811
- ...number(pointer)
3755
+ ...pointer,
3756
+ Opcodes.i32_from_u
3812
3757
  ];
3813
3758
  },
3814
3759
  type: TYPES.array,
@@ -3957,8 +3902,9 @@ const internalConstrs = {
3957
3902
  };
3958
3903
 
3959
3904
  export default program => {
3960
- globals = {};
3961
- globalInd = 0;
3905
+ globals = {
3906
+ ['#ind']: 0
3907
+ };
3962
3908
  tags = [];
3963
3909
  exceptions = [];
3964
3910
  funcs = [];
@@ -3991,6 +3937,7 @@ export default program => {
3991
3937
  builtinFuncs = new BuiltinFuncs();
3992
3938
  builtinVars = new BuiltinVars();
3993
3939
  prototypeFuncs = new PrototypeFuncs();
3940
+ allocator = makeAllocator(Prefs.allocator ?? 'static');
3994
3941
 
3995
3942
  program.id = { name: 'main' };
3996
3943
 
@@ -4033,6 +3980,8 @@ export default program => {
4033
3980
  else main.returns = [];
4034
3981
  }
4035
3982
 
3983
+ delete globals['#ind'];
3984
+
4036
3985
  // if blank main func and other exports, remove it
4037
3986
  if (main.wasm.length === 0 && funcs.reduce((acc, x) => acc + (x.export ? 1 : 0), 0) > 1) funcs.splice(main.index - importedFuncs.length, 1);
4038
3987