porffor 0.16.0-8107e135a → 0.16.0-97bb4f33b

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, ValtypeSize } from './wasmSpec.js';
2
- import { ieee754_binary64, signedLEB128, unsignedLEB128, encodeVector, read_signedLEB128 } from './encoding.js';
1
+ import { Blocktype, Opcodes, Valtype, PageSize, ValtypeSize } from './wasmSpec.js';
2
+ import { ieee754_binary64, signedLEB128, unsignedLEB128, encodeVector } 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,16 +9,15 @@ 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 Allocator from './allocators/index.js';
13
12
 
14
13
  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;
22
21
 
23
22
  class TodoError extends Error {
24
23
  constructor(message) {
@@ -182,6 +181,12 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
182
181
  continue;
183
182
  }
184
183
 
184
+ if (asm[0] === 'memory') {
185
+ allocPage(scope, 'asm instrinsic');
186
+ // todo: add to store/load offset insts
187
+ continue;
188
+ }
189
+
185
190
  let inst = Opcodes[asm[0].replace('.', '_')];
186
191
  if (inst == null) throw new Error(`inline asm: inst ${asm[0]} not found`);
187
192
 
@@ -428,12 +433,63 @@ const concatStrings = (scope, left, right, global, name, assign = false, bytestr
428
433
  const rightLength = localTmp(scope, 'concat_right_length', Valtype.i32);
429
434
  const leftLength = localTmp(scope, 'concat_left_length', Valtype.i32);
430
435
 
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
+
431
487
  const leftPointer = localTmp(scope, 'concat_left_pointer', Valtype.i32);
432
488
 
433
489
  // alloc/assign array
434
- const [ out, pointer ] = makeArray(scope, {
490
+ const [ , pointer ] = makeArray(scope, {
435
491
  rawElements: new Array(0)
436
- }, global, name, true, 'i16', true);
492
+ }, global, name, true, 'i16');
437
493
 
438
494
  return [
439
495
  // setup left
@@ -447,7 +503,7 @@ const concatStrings = (scope, left, right, global, name, assign = false, bytestr
447
503
  [ Opcodes.local_set, rightPointer ],
448
504
 
449
505
  // calculate length
450
- ...out,
506
+ ...number(0, Valtype.i32), // base 0 for store later
451
507
 
452
508
  [ Opcodes.local_get, leftPointer ],
453
509
  [ Opcodes.i32_load, 0, ...unsignedLEB128(0) ],
@@ -460,13 +516,11 @@ const concatStrings = (scope, left, right, global, name, assign = false, bytestr
460
516
  [ Opcodes.i32_add ],
461
517
 
462
518
  // store length
463
- [ Opcodes.i32_store, Math.log2(ValtypeSize.i32) - 1, 0 ],
519
+ [ Opcodes.i32_store, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(pointer) ],
464
520
 
465
521
  // copy left
466
522
  // dst = out pointer + length size
467
- ...pointer,
468
- ...number(ValtypeSize.i32, Valtype.i32),
469
- [ Opcodes.i32_add ],
523
+ ...number(pointer + ValtypeSize.i32, Valtype.i32),
470
524
 
471
525
  // src = left pointer + length size
472
526
  [ Opcodes.local_get, leftPointer ],
@@ -479,9 +533,7 @@ const concatStrings = (scope, left, right, global, name, assign = false, bytestr
479
533
 
480
534
  // copy right
481
535
  // dst = out pointer + length size + left length * sizeof valtype
482
- ...pointer,
483
- ...number(ValtypeSize.i32, Valtype.i32),
484
- [ Opcodes.i32_add ],
536
+ ...number(pointer + ValtypeSize.i32, Valtype.i32),
485
537
 
486
538
  [ Opcodes.local_get, leftLength ],
487
539
  ...number(bytestrings ? ValtypeSize.i8 : ValtypeSize.i16, Valtype.i32),
@@ -501,8 +553,7 @@ const concatStrings = (scope, left, right, global, name, assign = false, bytestr
501
553
  [ ...Opcodes.memory_copy, 0x00, 0x00 ],
502
554
 
503
555
  // return new string (page)
504
- ...pointer,
505
- Opcodes.i32_from_u
556
+ ...number(pointer)
506
557
  ];
507
558
  };
508
559
 
@@ -618,10 +669,10 @@ const compareStrings = (scope, left, right, bytestrings = false) => {
618
669
  };
619
670
 
620
671
  const truthy = (scope, wasm, type, intIn = false, intOut = false, forceTruthyMode = undefined) => {
621
- // if (isIntToFloatOp(wasm[wasm.length - 1])) return [
622
- // ...wasm,
623
- // ...(!intIn && intOut ? [ Opcodes.i32_to_u ] : [])
624
- // ];
672
+ if (isIntToFloatOp(wasm[wasm.length - 1])) return [
673
+ ...wasm,
674
+ ...(!intIn && intOut ? [ Opcodes.i32_to_u ] : [])
675
+ ];
625
676
  // if (isIntOp(wasm[wasm.length - 1])) return [ ...wasm ];
626
677
 
627
678
  // todo/perf: use knownType and custom bytecode here instead of typeSwitch
@@ -1017,7 +1068,7 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
1017
1068
 
1018
1069
  for (const x of _data) {
1019
1070
  const copy = { ...x };
1020
- if (copy.offset != null) copy.offset += pages.size * pageSize;
1071
+ copy.offset += pages.size * pageSize;
1021
1072
  data.push(copy);
1022
1073
  }
1023
1074
 
@@ -1040,9 +1091,9 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
1040
1091
 
1041
1092
  let baseGlobalIdx, i = 0;
1042
1093
  for (const type of globalTypes) {
1043
- if (baseGlobalIdx === undefined) baseGlobalIdx = globals['#ind'];
1094
+ if (baseGlobalIdx === undefined) baseGlobalIdx = globalInd;
1044
1095
 
1045
- globals[globalNames[i] ?? `${name}_global_${i}`] = { idx: globals['#ind']++, type, init: globalInits[i] ?? 0 };
1096
+ globals[globalNames[i] ?? `${name}_global_${i}`] = { idx: globalInd++, type, init: globalInits[i] ?? 0 };
1046
1097
  i++;
1047
1098
  }
1048
1099
 
@@ -1055,15 +1106,11 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
1055
1106
  }
1056
1107
  }
1057
1108
 
1058
- if (table) {
1059
- for (const inst of wasm) {
1060
- if (inst[0] === Opcodes.i32_load16_u && inst.at(-1) === 'read_argc') {
1061
- inst.splice(2, 99);
1062
- inst.push(...unsignedLEB128(allocPage({}, 'func argc lut') * pageSize));
1063
- }
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));
1064
1113
  }
1065
-
1066
- funcs.table = true;
1067
1114
  }
1068
1115
 
1069
1116
  func.wasm = wasm;
@@ -1403,10 +1450,10 @@ const countLeftover = wasm => {
1403
1450
 
1404
1451
  if (depth === 0)
1405
1452
  if ([Opcodes.throw, Opcodes.drop, Opcodes.local_set, Opcodes.global_set].includes(inst[0])) count--;
1406
- 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)) {}
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)) {}
1407
1454
  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++;
1408
1455
  else if ([Opcodes.i32_store, Opcodes.i64_store, Opcodes.f64_store, Opcodes.i32_store16, Opcodes.i32_store8].includes(inst[0])) count -= 2;
1409
- else if (inst[0] === Opcodes.memory_copy[0] && (inst[1] === Opcodes.memory_copy[1] || inst[1] === Opcodes.memory_init[1])) count -= 3;
1456
+ else if (Opcodes.memory_copy[0] === inst[0] && Opcodes.memory_copy[1] === inst[1]) count -= 3;
1410
1457
  else if (inst[0] === Opcodes.return) count = 0;
1411
1458
  else if (inst[0] === Opcodes.call) {
1412
1459
  let func = funcs.find(x => x.index === inst[1]);
@@ -1678,7 +1725,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1678
1725
  }, generate(scope, decl.arguments[0] ?? DEFAULT_VALUE), getNodeType(scope, decl.arguments[0] ?? DEFAULT_VALUE), protoLocal, protoLocal2, (length, itemType) => {
1679
1726
  return makeArray(scope, {
1680
1727
  rawElements: new Array(length)
1681
- }, _global, _name, true, itemType, true);
1728
+ }, _global, _name, true, itemType);
1682
1729
  }, () => {
1683
1730
  optUnused = true;
1684
1731
  return unusedValue;
@@ -2068,6 +2115,7 @@ const brTable = (input, bc, returns) => {
2068
2115
  }
2069
2116
 
2070
2117
  for (let i = 0; i < count; i++) {
2118
+ // if (i === 0) out.push([ Opcodes.block, returns, 'br table start' ]);
2071
2119
  if (i === 0) out.push([ Opcodes.block, returns ]);
2072
2120
  else out.push([ Opcodes.block, Blocktype.void ]);
2073
2121
  }
@@ -2101,8 +2149,10 @@ const brTable = (input, bc, returns) => {
2101
2149
  [ Opcodes.br_table, ...encodeVector(table), 0 ]
2102
2150
  );
2103
2151
 
2104
- // sort the wrong way and then reverse
2105
- // so strings ('default') are at the start before any numbers
2152
+ // if you can guess why we sort the wrong way and then reverse
2153
+ // (instead of just sorting the correct way)
2154
+ // dm me and if you are correct and the first person
2155
+ // I will somehow shout you out or something
2106
2156
  const orderedBc = keys.sort((a, b) => b - a).reverse();
2107
2157
 
2108
2158
  br = count - 1;
@@ -2128,7 +2178,7 @@ const typeSwitch = (scope, type, bc, returns = valtypeBinary) => {
2128
2178
  return bc[known] ?? bc.default;
2129
2179
  }
2130
2180
 
2131
- if (Prefs.typeswitchBrtable)
2181
+ if (Prefs.typeswitchUseBrtable)
2132
2182
  return brTable(type, bc, returns);
2133
2183
 
2134
2184
  const tmp = localTmp(scope, '#typeswitch_tmp' + (Prefs.typeswitchUniqueTmp ? randId() : ''), Valtype.i32);
@@ -2183,11 +2233,11 @@ const allocVar = (scope, name, global = false, type = true) => {
2183
2233
  return target[name].idx;
2184
2234
  }
2185
2235
 
2186
- let idx = global ? globals['#ind']++ : scope.localInd++;
2236
+ let idx = global ? globalInd++ : scope.localInd++;
2187
2237
  target[name] = { idx, type: valtypeBinary };
2188
2238
 
2189
2239
  if (type) {
2190
- let typeIdx = global ? globals['#ind']++ : scope.localInd++;
2240
+ let typeIdx = global ? globalInd++ : scope.localInd++;
2191
2241
  target[name + '#type'] = { idx: typeIdx, type: Valtype.i32, name };
2192
2242
  }
2193
2243
 
@@ -2280,10 +2330,24 @@ const generateVar = (scope, decl) => {
2280
2330
  }
2281
2331
 
2282
2332
  if (x.init) {
2283
- const alreadyArray = scope.arrays?.get(name) != null;
2333
+ // if (isFuncType(x.init.type)) {
2334
+ // // let a = function () { ... }
2335
+ // x.init.id = { name };
2336
+
2337
+ // const func = generateFunc(scope, x.init);
2338
+
2339
+ // out.push(
2340
+ // ...number(func.index - importedFuncs.length),
2341
+ // [ global ? Opcodes.global_set : Opcodes.local_set, idx ],
2342
+
2343
+ // ...setType(scope, name, TYPES.function)
2344
+ // );
2345
+
2346
+ // continue;
2347
+ // }
2284
2348
 
2285
2349
  const generated = generate(scope, x.init, global, name);
2286
- if (!alreadyArray && scope.arrays?.get(name) != null) {
2350
+ if (scope.arrays?.get(name) != null) {
2287
2351
  // hack to set local as pointer before
2288
2352
  out.push(...number(scope.arrays.get(name)), [ global ? Opcodes.global_set : Opcodes.local_set, idx ]);
2289
2353
  if (generated.at(-1) == Opcodes.i32_from_u) generated.pop();
@@ -2335,12 +2399,19 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
2335
2399
 
2336
2400
  // hack: .length setter
2337
2401
  if (decl.left.type === 'MemberExpression' && decl.left.property.name === 'length') {
2402
+ const name = decl.left.object.name;
2403
+ const pointer = scope.arrays?.get(name);
2404
+
2405
+ const aotPointer = Prefs.aotPointerOpt && pointer != null;
2406
+
2338
2407
  const newValueTmp = localTmp(scope, '__length_setter_tmp');
2339
2408
  const pointerTmp = op === '=' ? null : localTmp(scope, '__member_setter_ptr_tmp', Valtype.i32);
2340
2409
 
2341
2410
  return [
2342
- ...generate(scope, decl.left.object),
2343
- Opcodes.i32_to_u,
2411
+ ...(aotPointer ? number(0, Valtype.i32) : [
2412
+ ...generate(scope, decl.left.object),
2413
+ Opcodes.i32_to_u
2414
+ ]),
2344
2415
  ...(!pointerTmp ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
2345
2416
 
2346
2417
  ...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
@@ -2351,7 +2422,7 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
2351
2422
  [ Opcodes.local_tee, newValueTmp ],
2352
2423
 
2353
2424
  Opcodes.i32_to_u,
2354
- [ Opcodes.i32_store, Math.log2(ValtypeSize.i32) - 1, 0 ],
2425
+ [ Opcodes.i32_store, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(aotPointer ? pointer : 0) ],
2355
2426
 
2356
2427
  [ Opcodes.local_get, newValueTmp ]
2357
2428
  ];
@@ -2359,14 +2430,21 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
2359
2430
 
2360
2431
  // arr[i]
2361
2432
  if (decl.left.type === 'MemberExpression' && decl.left.computed) {
2433
+ const name = decl.left.object.name;
2434
+ const pointer = scope.arrays?.get(name);
2435
+
2436
+ const aotPointer = Prefs.aotPointerOpt && pointer != null;
2437
+
2362
2438
  const newValueTmp = localTmp(scope, '__member_setter_val_tmp');
2363
2439
  const pointerTmp = op === '=' ? -1 : localTmp(scope, '__member_setter_ptr_tmp', Valtype.i32);
2364
2440
 
2365
2441
  return [
2366
2442
  ...typeSwitch(scope, getNodeType(scope, decl.left.object), {
2367
2443
  [TYPES.array]: [
2368
- ...generate(scope, decl.left.object),
2369
- Opcodes.i32_to_u,
2444
+ ...(aotPointer ? [] : [
2445
+ ...generate(scope, decl.left.object),
2446
+ Opcodes.i32_to_u
2447
+ ]),
2370
2448
 
2371
2449
  // get index as valtype
2372
2450
  ...generate(scope, decl.left.property),
@@ -2375,22 +2453,39 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
2375
2453
  // turn into byte offset by * valtypeSize (4 for i32, 8 for i64/f64)
2376
2454
  ...number(ValtypeSize[valtype] + 1, Valtype.i32),
2377
2455
  [ Opcodes.i32_mul ],
2378
- [ Opcodes.i32_add ],
2456
+ ...(aotPointer ? [] : [ [ Opcodes.i32_add ] ]),
2379
2457
  ...(op === '=' ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
2380
2458
 
2381
2459
  ...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
2382
2460
  [ Opcodes.local_get, pointerTmp ],
2383
- [ Opcodes.load, 0, ValtypeSize.i32 ]
2461
+ [ Opcodes.load, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ]
2384
2462
  ], generate(scope, decl.right), [
2385
2463
  [ Opcodes.local_get, pointerTmp ],
2386
- [ Opcodes.i32_load8_u, 0, ValtypeSize.i32 + ValtypeSize[valtype] ]
2464
+ [ Opcodes.i32_load8_u, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32 + ValtypeSize[valtype]) ]
2387
2465
  ], getNodeType(scope, decl.right), false, name, true)),
2388
2466
  [ Opcodes.local_tee, newValueTmp ],
2389
2467
 
2390
- [ Opcodes.store, 0, ValtypeSize.i32 ]
2468
+ [ Opcodes.store, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ]
2391
2469
  ],
2392
2470
 
2393
2471
  default: internalThrow(scope, 'TypeError', `Cannot assign member with non-array`)
2472
+
2473
+ // [TYPES.string]: [
2474
+ // // turn into byte offset by * sizeof i16
2475
+ // ...number(ValtypeSize.i16, Valtype.i32),
2476
+ // [ Opcodes.i32_mul ],
2477
+ // ...(aotPointer ? [] : [ [ Opcodes.i32_add ] ]),
2478
+ // ...(op === '=' ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
2479
+
2480
+ // ...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
2481
+ // [ Opcodes.local_get, pointerTmp ],
2482
+ // [ Opcodes.load, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ]
2483
+ // ], generate(scope, decl.right), number(TYPES.string, Valtype.i32), getNodeType(scope, decl.right))),
2484
+ // [ Opcodes.local_tee, newValueTmp ],
2485
+
2486
+ // Opcodes.i32_to_u,
2487
+ // [ StoreOps.i16, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ]
2488
+ // ]
2394
2489
  }, Blocktype.void),
2395
2490
 
2396
2491
  [ Opcodes.local_get, newValueTmp ]
@@ -2774,12 +2869,12 @@ const generateForOf = (scope, decl) => {
2774
2869
 
2775
2870
  // // todo: we should only do this for strings but we don't know at compile-time :(
2776
2871
  // hack: this is naughty and will break things!
2777
- let newOut = number(0, Valtype.i32), newPointer = number(0, Valtype.i32);
2872
+ let newOut = number(0, Valtype.f64), newPointer = -1;
2778
2873
  if (pages.hasAnyString) {
2779
2874
  // todo: we use i16 even for bytestrings which should not make a bad thing happen, just be confusing for debugging?
2780
2875
  0, [ newOut, newPointer ] = makeArray(scope, {
2781
- rawElements: new Array(0)
2782
- }, isGlobal, leftName, true, 'i16', true);
2876
+ rawElements: new Array(1)
2877
+ }, isGlobal, leftName, true, 'i16');
2783
2878
  }
2784
2879
 
2785
2880
  // set type for local
@@ -2826,28 +2921,23 @@ const generateForOf = (scope, decl) => {
2826
2921
  [TYPES.string]: [
2827
2922
  ...setType(scope, leftName, TYPES.string),
2828
2923
 
2924
+ [ Opcodes.loop, Blocktype.void ],
2925
+
2829
2926
  // setup new/out array
2830
2927
  ...newOut,
2928
+ [ Opcodes.drop ],
2831
2929
 
2832
- // set length to 1
2833
- ...number(1, Valtype.i32),
2834
- [ Opcodes.i32_store, 0, 0 ],
2835
-
2836
- [ Opcodes.loop, Blocktype.void ],
2837
-
2838
- // use as pointer for store later
2839
- ...newPointer,
2930
+ ...number(0, Valtype.i32), // base 0 for store after
2840
2931
 
2841
2932
  // load current string ind {arg}
2842
2933
  [ Opcodes.local_get, pointer ],
2843
- [ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1, ValtypeSize.i32 ],
2934
+ [ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128(ValtypeSize.i32) ],
2844
2935
 
2845
2936
  // store to new string ind 0
2846
- [ Opcodes.i32_store16, Math.log2(ValtypeSize.i16) - 1, ValtypeSize.i32 ],
2937
+ [ Opcodes.i32_store16, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128(newPointer + ValtypeSize.i32) ],
2847
2938
 
2848
2939
  // return new string (page)
2849
- ...newPointer,
2850
- Opcodes.i32_from_u,
2940
+ ...number(newPointer),
2851
2941
 
2852
2942
  [ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx ],
2853
2943
 
@@ -2879,30 +2969,25 @@ const generateForOf = (scope, decl) => {
2879
2969
  [TYPES.bytestring]: [
2880
2970
  ...setType(scope, leftName, TYPES.bytestring),
2881
2971
 
2972
+ [ Opcodes.loop, Blocktype.void ],
2973
+
2882
2974
  // setup new/out array
2883
2975
  ...newOut,
2976
+ [ Opcodes.drop ],
2884
2977
 
2885
- // set length to 1
2886
- ...number(1, Valtype.i32),
2887
- [ Opcodes.i32_store, 0, 0 ],
2888
-
2889
- [ Opcodes.loop, Blocktype.void ],
2890
-
2891
- // use as pointer for store later
2892
- ...newPointer,
2978
+ ...number(0, Valtype.i32), // base 0 for store after
2893
2979
 
2894
2980
  // load current string ind {arg}
2895
2981
  [ Opcodes.local_get, pointer ],
2896
2982
  [ Opcodes.local_get, counter ],
2897
2983
  [ Opcodes.i32_add ],
2898
- [ Opcodes.i32_load8_u, 0, ValtypeSize.i32 ],
2984
+ [ Opcodes.i32_load8_u, 0, ...unsignedLEB128(ValtypeSize.i32) ],
2899
2985
 
2900
2986
  // store to new string ind 0
2901
- [ Opcodes.i32_store8, 0, ValtypeSize.i32 ],
2987
+ [ Opcodes.i32_store8, 0, ...unsignedLEB128(newPointer + ValtypeSize.i32) ],
2902
2988
 
2903
2989
  // return new string (page)
2904
- ...newPointer,
2905
- Opcodes.i32_from_u,
2990
+ ...number(newPointer),
2906
2991
 
2907
2992
  [ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx ],
2908
2993
 
@@ -3122,6 +3207,16 @@ const allocPage = (scope, reason, type) => {
3122
3207
  return ind;
3123
3208
  };
3124
3209
 
3210
+ // todo: add scope.pages
3211
+ const freePage = reason => {
3212
+ const { ind } = pages.get(reason);
3213
+ pages.delete(reason);
3214
+
3215
+ if (Prefs.allocLog) log('alloc', `freed page of memory (${ind}) | ${reason}`);
3216
+
3217
+ return ind;
3218
+ };
3219
+
3125
3220
  const itemTypeToValtype = {
3126
3221
  i32: 'i32',
3127
3222
  i64: 'i64',
@@ -3156,43 +3251,39 @@ const compileBytes = (val, itemType) => {
3156
3251
  }
3157
3252
  };
3158
3253
 
3159
- const makeData = (scope, elements, offset = null, itemType, initEmpty) => {
3160
- const length = elements.length;
3161
-
3162
- // if length is 0 memory/data will just be 0000... anyway
3163
- if (length === 0) return false;
3164
-
3165
- let bytes = compileBytes(length, 'i32');
3166
-
3167
- if (!initEmpty) for (let i = 0; i < length; i++) {
3168
- if (elements[i] == null) continue;
3254
+ const getAllocType = itemType => {
3255
+ switch (itemType) {
3256
+ case 'i8': return 'bytestring';
3257
+ case 'i16': return 'string';
3169
3258
 
3170
- bytes.push(...compileBytes(elements[i], itemType));
3259
+ default: return 'array';
3171
3260
  }
3261
+ };
3172
3262
 
3173
- const obj = { bytes };
3174
- if (offset != null) obj.offset = offset;
3263
+ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty = false, itemType = valtype, typed = false) => {
3264
+ const out = [];
3175
3265
 
3176
- const idx = data.push(obj) - 1;
3266
+ scope.arrays ??= new Map();
3177
3267
 
3178
- scope.data ??= [];
3179
- scope.data.push(idx);
3268
+ let firstAssign = false;
3269
+ if (!scope.arrays.has(name) || name === '$undeclared') {
3270
+ firstAssign = true;
3180
3271
 
3181
- return { idx, size: bytes.length };
3182
- };
3272
+ // todo: can we just have 1 undeclared array? probably not? but this is not really memory efficient
3273
+ const uniqueName = name === '$undeclared' ? name + Math.random().toString().slice(2) : name;
3274
+
3275
+ let page;
3276
+ if (Prefs.scopedPageNames) page = allocPage(scope, `${getAllocType(itemType)}: ${scope.name}/${uniqueName}`, itemType);
3277
+ else page = allocPage(scope, `${getAllocType(itemType)}: ${uniqueName}`, itemType);
3183
3278
 
3184
- const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty = false, itemType = valtype, intOut = false, typed = false) => {
3185
- if (itemType !== 'i16' && itemType !== 'i8') {
3186
- pages.hasArray = true;
3187
- } else {
3188
- pages.hasAnyString = true;
3189
- if (itemType === 'i8') pages.hasByteString = true;
3190
- else pages.hasString = true;
3279
+ // hack: use 1 for page 0 pointer for fast truthiness
3280
+ const ptr = page === 0 ? 1 : (page * pageSize);
3281
+ scope.arrays.set(name, ptr);
3191
3282
  }
3192
3283
 
3193
- const out = [];
3284
+ const pointer = scope.arrays.get(name);
3194
3285
 
3195
- const uniqueName = name === '$undeclared' ? name + randId() : name;
3286
+ const local = global ? globals[name] : scope.locals[name];
3196
3287
 
3197
3288
  const useRawElements = !!decl.rawElements;
3198
3289
  const elements = useRawElements ? decl.rawElements : decl.elements;
@@ -3200,70 +3291,46 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
3200
3291
  const valtype = itemTypeToValtype[itemType];
3201
3292
  const length = elements.length;
3202
3293
 
3203
- const allocated = allocator.alloc({ scope, pages, globals }, uniqueName, { itemType });
3294
+ if (firstAssign && useRawElements && !Prefs.noData) {
3295
+ // if length is 0 memory/data will just be 0000... anyway
3296
+ if (length !== 0) {
3297
+ let bytes = compileBytes(length, 'i32');
3204
3298
 
3205
- let pointer = allocated;
3206
- if (allocator.constructor.name !== 'StaticAllocator') {
3207
- // const tmp = localTmp(scope, '#makearray_pointer' + uniqueName, Valtype.i32);
3208
- const tmp = localTmp(scope, '#makearray_pointer' + name, Valtype.i32);
3209
- out.push(
3210
- ...allocated,
3211
- [ Opcodes.local_set, tmp ]
3212
- );
3213
-
3214
- pointer = [ [ Opcodes.local_get, tmp ] ];
3299
+ if (!initEmpty) for (let i = 0; i < length; i++) {
3300
+ if (elements[i] == null) continue;
3215
3301
 
3216
- if (Prefs.data && useRawElements) {
3217
- const data = makeData(scope, elements, null, itemType, initEmpty);
3218
- if (data) {
3219
- // init data
3220
- out.push(
3221
- ...pointer,
3222
- ...number(0, Valtype.i32),
3223
- ...number(data.size, Valtype.i32),
3224
- [ ...Opcodes.memory_init, ...unsignedLEB128(data.idx), 0 ]
3225
- );
3302
+ bytes.push(...compileBytes(elements[i], itemType));
3226
3303
  }
3227
3304
 
3228
- // return pointer in out
3229
- out.push(
3230
- ...pointer,
3231
- ...(!intOut ? [ Opcodes.i32_from_u ] : [])
3232
- );
3305
+ const ind = data.push({
3306
+ offset: pointer,
3307
+ bytes
3308
+ }) - 1;
3233
3309
 
3234
- return [ out, pointer ];
3310
+ scope.data ??= [];
3311
+ scope.data.push(ind);
3235
3312
  }
3236
- } else {
3237
- const rawPtr = read_signedLEB128(pointer[0].slice(1));
3238
-
3239
- scope.arrays ??= new Map();
3240
- const firstAssign = !scope.arrays.has(uniqueName);
3241
- if (firstAssign) scope.arrays.set(uniqueName, rawPtr);
3242
3313
 
3243
- if (Prefs.data && firstAssign && useRawElements) {
3244
- makeData(scope, elements, rawPtr, itemType, initEmpty);
3245
-
3246
- // local value as pointer
3247
- return [ number(rawPtr, intOut ? Valtype.i32 : valtypeBinary), pointer ];
3248
- }
3314
+ // local value as pointer
3315
+ out.push(...number(pointer));
3249
3316
 
3250
- const local = global ? globals[name] : scope.locals[name];
3251
- const pointerTmp = local != null ? localTmp(scope, '#makearray_pointer_tmp', Valtype.i32) : null;
3252
- if (pointerTmp != null) {
3253
- out.push(
3254
- [ global ? Opcodes.global_get : Opcodes.local_get, local.idx ],
3255
- Opcodes.i32_to_u,
3256
- [ Opcodes.local_set, pointerTmp ]
3257
- );
3317
+ return [ out, pointer ];
3318
+ }
3258
3319
 
3259
- pointer = [ [ Opcodes.local_get, pointerTmp ] ];
3260
- }
3320
+ const pointerTmp = local != null ? localTmp(scope, '#makearray_pointer_tmp', Valtype.i32) : null;
3321
+ if (pointerTmp != null) {
3322
+ out.push(
3323
+ [ global ? Opcodes.global_get : Opcodes.local_get, local.idx ],
3324
+ Opcodes.i32_to_u,
3325
+ [ Opcodes.local_set, pointerTmp ]
3326
+ );
3261
3327
  }
3262
3328
 
3329
+ const pointerWasm = pointerTmp != null ? [ [ Opcodes.local_get, pointerTmp ] ] : number(pointer, Valtype.i32);
3263
3330
 
3264
3331
  // store length
3265
3332
  out.push(
3266
- ...pointer,
3333
+ ...pointerWasm,
3267
3334
  ...number(length, Valtype.i32),
3268
3335
  [ Opcodes.i32_store, Math.log2(ValtypeSize.i32) - 1, 0 ]
3269
3336
  );
@@ -3275,11 +3342,11 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
3275
3342
 
3276
3343
  const offset = ValtypeSize.i32 + i * sizePerEl;
3277
3344
  out.push(
3278
- ...pointer,
3345
+ ...pointerWasm,
3279
3346
  ...(useRawElements ? number(elements[i], Valtype[valtype]) : generate(scope, elements[i])),
3280
3347
  [ storeOp, 0, ...unsignedLEB128(offset) ],
3281
3348
  ...(!typed ? [] : [ // typed presumes !useRawElements
3282
- ...pointer,
3349
+ ...pointerWasm,
3283
3350
  ...getNodeType(scope, elements[i]),
3284
3351
  [ Opcodes.i32_store8, 0, ...unsignedLEB128(offset + ValtypeSize[itemType]) ]
3285
3352
  ])
@@ -3287,13 +3354,12 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
3287
3354
  }
3288
3355
 
3289
3356
  // local value as pointer
3290
- out.push(...pointer);
3291
- if (!intOut) out.push(Opcodes.i32_from_u);
3357
+ out.push(...pointerWasm, Opcodes.i32_from_u);
3292
3358
 
3293
3359
  return [ out, pointer ];
3294
3360
  };
3295
3361
 
3296
- const storeArray = (scope, array, index, element) => {
3362
+ const storeArray = (scope, array, index, element, aotPointer = null) => {
3297
3363
  if (!Array.isArray(element)) element = generate(scope, element);
3298
3364
  if (typeof index === 'number') index = number(index);
3299
3365
 
@@ -3305,25 +3371,26 @@ const storeArray = (scope, array, index, element) => {
3305
3371
  Opcodes.i32_to_u,
3306
3372
  ...number(ValtypeSize[valtype] + 1, Valtype.i32),
3307
3373
  [ Opcodes.i32_mul ],
3308
-
3309
- ...array,
3310
- Opcodes.i32_to_u,
3311
- [ Opcodes.i32_add ],
3374
+ ...(aotPointer ? [] : [
3375
+ ...array,
3376
+ Opcodes.i32_to_u,
3377
+ [ Opcodes.i32_add ],
3378
+ ]),
3312
3379
  [ Opcodes.local_set, offset ],
3313
3380
 
3314
3381
  // store value
3315
3382
  [ Opcodes.local_get, offset ],
3316
3383
  ...generate(scope, element),
3317
- [ Opcodes.store, 0, ValtypeSize.i32 ],
3384
+ [ Opcodes.store, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ],
3318
3385
 
3319
3386
  // store type
3320
3387
  [ Opcodes.local_get, offset ],
3321
3388
  ...getNodeType(scope, element),
3322
- [ Opcodes.i32_store8, 0, ValtypeSize.i32 + ValtypeSize[valtype] ]
3389
+ [ Opcodes.i32_store8, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32 + ValtypeSize[valtype]) ]
3323
3390
  ];
3324
3391
  };
3325
3392
 
3326
- const loadArray = (scope, array, index) => {
3393
+ const loadArray = (scope, array, index, aotPointer = null) => {
3327
3394
  if (typeof index === 'number') index = number(index);
3328
3395
 
3329
3396
  const offset = localTmp(scope, '#loadArray_offset', Valtype.i32);
@@ -3334,19 +3401,20 @@ const loadArray = (scope, array, index) => {
3334
3401
  Opcodes.i32_to_u,
3335
3402
  ...number(ValtypeSize[valtype] + 1, Valtype.i32),
3336
3403
  [ Opcodes.i32_mul ],
3337
-
3338
- ...array,
3339
- Opcodes.i32_to_u,
3340
- [ Opcodes.i32_add ],
3404
+ ...(aotPointer ? [] : [
3405
+ ...array,
3406
+ Opcodes.i32_to_u,
3407
+ [ Opcodes.i32_add ],
3408
+ ]),
3341
3409
  [ Opcodes.local_set, offset ],
3342
3410
 
3343
3411
  // load value
3344
3412
  [ Opcodes.local_get, offset ],
3345
- [ Opcodes.load, 0, ValtypeSize.i32 ],
3413
+ [ Opcodes.load, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ],
3346
3414
 
3347
3415
  // load type
3348
3416
  [ Opcodes.local_get, offset ],
3349
- [ Opcodes.i32_load8_u, 0, ValtypeSize.i32 + ValtypeSize[valtype] ]
3417
+ [ Opcodes.i32_load8_u, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32 + ValtypeSize[valtype]) ]
3350
3418
  ];
3351
3419
  };
3352
3420
 
@@ -3378,7 +3446,7 @@ const makeString = (scope, str, global = false, name = '$undeclared', forceBytes
3378
3446
  };
3379
3447
 
3380
3448
  const generateArray = (scope, decl, global = false, name = '$undeclared', initEmpty = false) => {
3381
- return makeArray(scope, decl, global, name, initEmpty, valtype, false, true)[0];
3449
+ return makeArray(scope, decl, global, name, initEmpty, valtype, true)[0];
3382
3450
  };
3383
3451
 
3384
3452
  const generateObject = (scope, decl, global = false, name = '$undeclared') => {
@@ -3397,6 +3465,9 @@ const withType = (scope, wasm, type) => [
3397
3465
 
3398
3466
  const generateMember = (scope, decl, _global, _name) => {
3399
3467
  const name = decl.object.name;
3468
+ const pointer = scope.arrays?.get(name);
3469
+
3470
+ const aotPointer = Prefs.aotPointerOpt && pointer;
3400
3471
 
3401
3472
  // hack: .name
3402
3473
  if (decl.property.name === 'name') {
@@ -3437,10 +3508,12 @@ const generateMember = (scope, decl, _global, _name) => {
3437
3508
  if (Prefs.fastLength) {
3438
3509
  // presume valid length object
3439
3510
  return [
3440
- ...generate(scope, decl.object),
3441
- Opcodes.i32_to_u,
3511
+ ...(aotPointer ? number(0, Valtype.i32) : [
3512
+ ...generate(scope, decl.object),
3513
+ Opcodes.i32_to_u
3514
+ ]),
3442
3515
 
3443
- [ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, 0 ],
3516
+ [ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(aotPointer ? pointer : 0) ],
3444
3517
  Opcodes.i32_from_u
3445
3518
  ];
3446
3519
  }
@@ -3449,10 +3522,12 @@ const generateMember = (scope, decl, _global, _name) => {
3449
3522
  const known = knownType(scope, type);
3450
3523
  if (known != null) {
3451
3524
  if ([ TYPES.string, TYPES.bytestring, TYPES.array ].includes(known)) return [
3452
- ...generate(scope, decl.object),
3453
- Opcodes.i32_to_u,
3525
+ ...(aotPointer ? number(0, Valtype.i32) : [
3526
+ ...generate(scope, decl.object),
3527
+ Opcodes.i32_to_u
3528
+ ]),
3454
3529
 
3455
- [ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, 0 ],
3530
+ [ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(aotPointer ? pointer : 0) ],
3456
3531
  Opcodes.i32_from_u
3457
3532
  ];
3458
3533
 
@@ -3462,10 +3537,12 @@ const generateMember = (scope, decl, _global, _name) => {
3462
3537
  return [
3463
3538
  ...typeIsOneOf(getNodeType(scope, decl.object), [ TYPES.string, TYPES.bytestring, TYPES.array ]),
3464
3539
  [ Opcodes.if, valtypeBinary ],
3465
- ...generate(scope, decl.object),
3466
- Opcodes.i32_to_u,
3540
+ ...(aotPointer ? number(0, Valtype.i32) : [
3541
+ ...generate(scope, decl.object),
3542
+ Opcodes.i32_to_u
3543
+ ]),
3467
3544
 
3468
- [ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, 0 ],
3545
+ [ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(aotPointer ? pointer : 0) ],
3469
3546
  Opcodes.i32_from_u,
3470
3547
 
3471
3548
  ...setLastType(scope, TYPES.number),
@@ -3508,29 +3585,25 @@ const generateMember = (scope, decl, _global, _name) => {
3508
3585
 
3509
3586
  // // todo: we should only do this for strings but we don't know at compile-time :(
3510
3587
  // hack: this is naughty and will break things!
3511
- let newOut = number(0, Valtype.i32), newPointer = number(0, Valtype.i32);
3588
+ let newOut = number(0, valtypeBinary), newPointer = -1;
3512
3589
  if (pages.hasAnyString) {
3513
- // todo: we use i16 even for bytestrings which should not make a bad thing happen, just be confusing for debugging?
3514
3590
  0, [ newOut, newPointer ] = makeArray(scope, {
3515
- rawElements: new Array(0)
3516
- }, _global, _name, true, 'i16', true);
3591
+ rawElements: new Array(1)
3592
+ }, _global, _name, true, 'i16');
3517
3593
  }
3518
3594
 
3519
3595
  return typeSwitch(scope, getNodeType(scope, decl.object), {
3520
3596
  [TYPES.array]: [
3521
- ...loadArray(scope, object, property),
3597
+ ...loadArray(scope, object, property, aotPointer),
3522
3598
  ...setLastType(scope)
3523
3599
  ],
3600
+
3524
3601
  [TYPES.string]: [
3525
3602
  // setup new/out array
3526
3603
  ...newOut,
3604
+ [ Opcodes.drop ],
3527
3605
 
3528
- // set length to 1
3529
- ...number(1, Valtype.i32),
3530
- [ Opcodes.i32_store, 0, 0 ],
3531
-
3532
- // use as pointer for store later
3533
- ...newPointer,
3606
+ ...number(0, Valtype.i32), // base 0 for store later
3534
3607
 
3535
3608
  ...property,
3536
3609
  Opcodes.i32_to_u,
@@ -3538,48 +3611,46 @@ const generateMember = (scope, decl, _global, _name) => {
3538
3611
  ...number(ValtypeSize.i16, Valtype.i32),
3539
3612
  [ Opcodes.i32_mul ],
3540
3613
 
3541
- ...object,
3542
- Opcodes.i32_to_u,
3543
- [ Opcodes.i32_add ],
3614
+ ...(aotPointer ? [] : [
3615
+ ...object,
3616
+ Opcodes.i32_to_u,
3617
+ [ Opcodes.i32_add ]
3618
+ ]),
3544
3619
 
3545
3620
  // load current string ind {arg}
3546
- [ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1, ValtypeSize.i32 ],
3621
+ [ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ],
3547
3622
 
3548
3623
  // store to new string ind 0
3549
- [ Opcodes.i32_store16, Math.log2(ValtypeSize.i16) - 1, ValtypeSize.i32 ],
3624
+ [ Opcodes.i32_store16, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128(newPointer + ValtypeSize.i32) ],
3550
3625
 
3551
3626
  // return new string (page)
3552
- ...newPointer,
3553
- Opcodes.i32_from_u,
3627
+ ...number(newPointer),
3554
3628
  ...setLastType(scope, TYPES.string)
3555
3629
  ],
3556
3630
  [TYPES.bytestring]: [
3557
3631
  // setup new/out array
3558
3632
  ...newOut,
3633
+ [ Opcodes.drop ],
3559
3634
 
3560
- // set length to 1
3561
- ...number(1, Valtype.i32),
3562
- [ Opcodes.i32_store, 0, 0 ],
3563
-
3564
- // use as pointer for store later
3565
- ...newPointer,
3635
+ ...number(0, Valtype.i32), // base 0 for store later
3566
3636
 
3567
3637
  ...property,
3568
3638
  Opcodes.i32_to_u,
3569
3639
 
3570
- ...object,
3571
- Opcodes.i32_to_u,
3572
- [ Opcodes.i32_add ],
3640
+ ...(aotPointer ? [] : [
3641
+ ...object,
3642
+ Opcodes.i32_to_u,
3643
+ [ Opcodes.i32_add ]
3644
+ ]),
3573
3645
 
3574
3646
  // load current string ind {arg}
3575
- [ Opcodes.i32_load8_u, 0, ValtypeSize.i32 ],
3647
+ [ Opcodes.i32_load8_u, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ],
3576
3648
 
3577
3649
  // store to new string ind 0
3578
- [ Opcodes.i32_store8, 0, ValtypeSize.i32 ],
3650
+ [ Opcodes.i32_store8, 0, ...unsignedLEB128(newPointer + ValtypeSize.i32) ],
3579
3651
 
3580
3652
  // return new string (page)
3581
- ...newPointer,
3582
- Opcodes.i32_from_u,
3653
+ ...number(newPointer),
3583
3654
  ...setLastType(scope, TYPES.bytestring)
3584
3655
  ],
3585
3656
 
@@ -3587,7 +3658,7 @@ const generateMember = (scope, decl, _global, _name) => {
3587
3658
  });
3588
3659
  };
3589
3660
 
3590
- const randId = () => Math.random().toString(16).slice(1, -2).padEnd(12, '0');
3661
+ const randId = () => Math.random().toString(16).slice(0, -4);
3591
3662
 
3592
3663
  const objectHack = node => {
3593
3664
  if (!node) return node;
@@ -3639,7 +3710,7 @@ const generateFunc = (scope, decl) => {
3639
3710
  if (decl.async) return todo(scope, 'async functions are not supported');
3640
3711
  if (decl.generator) return todo(scope, 'generator functions are not supported');
3641
3712
 
3642
- const name = decl.id ? decl.id.name : `anonymous${randId()}`;
3713
+ const name = decl.id ? decl.id.name : `anonymous_${randId()}`;
3643
3714
  const params = decl.params ?? [];
3644
3715
 
3645
3716
  // TODO: share scope/locals between !!!
@@ -3724,9 +3795,9 @@ const internalConstrs = {
3724
3795
 
3725
3796
  // new Array(n)
3726
3797
 
3727
- const [ out, pointer ] = makeArray(scope, {
3798
+ const [ , pointer ] = makeArray(scope, {
3728
3799
  rawElements: new Array(0)
3729
- }, global, name, true, undefined, true);
3800
+ }, global, name, true);
3730
3801
 
3731
3802
  const arg = decl.arguments[0] ?? DEFAULT_VALUE;
3732
3803
 
@@ -3735,13 +3806,12 @@ const internalConstrs = {
3735
3806
  if (literalValue < 0 || !Number.isFinite(literalValue) || literalValue > 4294967295) return internalThrow(scope, 'RangeThrow', 'Invalid array length', true);
3736
3807
 
3737
3808
  return [
3738
- ...out,
3809
+ ...number(0, Valtype.i32),
3739
3810
  ...generate(scope, arg, global, name),
3740
3811
  Opcodes.i32_to_u,
3741
- [ Opcodes.i32_store, Math.log2(ValtypeSize.i32) - 1, 0 ],
3812
+ [ Opcodes.i32_store, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(pointer) ],
3742
3813
 
3743
- ...pointer,
3744
- Opcodes.i32_from_u
3814
+ ...number(pointer)
3745
3815
  ];
3746
3816
  },
3747
3817
  type: TYPES.array,
@@ -3890,9 +3960,8 @@ const internalConstrs = {
3890
3960
  };
3891
3961
 
3892
3962
  export default program => {
3893
- globals = {
3894
- ['#ind']: 0
3895
- };
3963
+ globals = {};
3964
+ globalInd = 0;
3896
3965
  tags = [];
3897
3966
  exceptions = [];
3898
3967
  funcs = [];
@@ -3925,7 +3994,6 @@ export default program => {
3925
3994
  builtinFuncs = new BuiltinFuncs();
3926
3995
  builtinVars = new BuiltinVars();
3927
3996
  prototypeFuncs = new PrototypeFuncs();
3928
- allocator = Allocator(Prefs.allocator ?? 'static');
3929
3997
 
3930
3998
  program.id = { name: 'main' };
3931
3999
 
@@ -3968,8 +4036,6 @@ export default program => {
3968
4036
  else main.returns = [];
3969
4037
  }
3970
4038
 
3971
- delete globals['#ind'];
3972
-
3973
4039
  // if blank main func and other exports, remove it
3974
4040
  if (main.wasm.length === 0 && funcs.reduce((acc, x) => acc + (x.export ? 1 : 0), 0) > 1) funcs.splice(main.index - importedFuncs.length, 1);
3975
4041