porffor 0.16.0-fe07da0f4 → 0.17.0-05070e1f0

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,23 +1,24 @@
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';
6
6
  import { number } from './embedding.js';
7
- import { TYPES, TYPE_NAMES } from './types.js';
7
+ import { TYPES, TYPE_FLAGS, TYPE_NAMES, typeHasFlag } from './types.js';
8
8
  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
 
@@ -76,6 +72,9 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
76
72
  case 'ExpressionStatement':
77
73
  return generateExp(scope, decl);
78
74
 
75
+ case 'SequenceExpression':
76
+ return generateSequence(scope, decl);
77
+
79
78
  case 'CallExpression':
80
79
  return generateCall(scope, decl, global, name, valueUnused);
81
80
 
@@ -181,12 +180,6 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
181
180
  continue;
182
181
  }
183
182
 
184
- if (asm[0] === 'memory') {
185
- allocPage(scope, 'asm instrinsic');
186
- // todo: add to store/load offset insts
187
- continue;
188
- }
189
-
190
183
  let inst = Opcodes[asm[0].replace('.', '_')];
191
184
  if (inst == null) throw new Error(`inline asm: inst ${asm[0]} not found`);
192
185
 
@@ -274,10 +267,12 @@ const internalThrow = (scope, constructor, message, expectsValue = Prefs.alwaysV
274
267
  argument: {
275
268
  type: 'NewExpression',
276
269
  callee: {
270
+ type: 'Identifier',
277
271
  name: constructor
278
272
  },
279
273
  arguments: [
280
274
  {
275
+ type: 'Literal',
281
276
  value: message
282
277
  }
283
278
  ]
@@ -299,12 +294,13 @@ const generateIdent = (scope, decl) => {
299
294
  return wasm.slice();
300
295
  }
301
296
 
302
- if (Object.hasOwn(builtinFuncs, name) || Object.hasOwn(internalConstrs, name)) {
303
- // todo: return an actual something
304
- return number(1);
305
- }
297
+ // todo: enable this by default in future
298
+ // if (!Object.hasOwn(funcIndex, name) && Object.hasOwn(builtinFuncs, name)) {
299
+ // includeBuiltin(scope, name);
300
+ // return number(funcIndex[name] - importedFuncs.length);
301
+ // }
306
302
 
307
- if (isExistingProtoFunc(name)) {
303
+ if (isExistingProtoFunc(name) || Object.hasOwn(internalConstrs, name) || Object.hasOwn(builtinFuncs, name)) {
308
304
  // todo: return an actual something
309
305
  return number(1);
310
306
  }
@@ -433,63 +429,12 @@ const concatStrings = (scope, left, right, global, name, assign = false, bytestr
433
429
  const rightLength = localTmp(scope, 'concat_right_length', Valtype.i32);
434
430
  const leftLength = localTmp(scope, 'concat_left_length', Valtype.i32);
435
431
 
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
432
  const leftPointer = localTmp(scope, 'concat_left_pointer', Valtype.i32);
488
433
 
489
434
  // alloc/assign array
490
- const [ , pointer ] = makeArray(scope, {
435
+ const [ out, pointer ] = makeArray(scope, {
491
436
  rawElements: new Array(0)
492
- }, global, name, true, 'i16');
437
+ }, assign ? false : global, assign ? undefined : name, true, 'i16', true);
493
438
 
494
439
  return [
495
440
  // setup left
@@ -503,7 +448,7 @@ const concatStrings = (scope, left, right, global, name, assign = false, bytestr
503
448
  [ Opcodes.local_set, rightPointer ],
504
449
 
505
450
  // calculate length
506
- ...number(0, Valtype.i32), // base 0 for store later
451
+ ...out,
507
452
 
508
453
  [ Opcodes.local_get, leftPointer ],
509
454
  [ Opcodes.i32_load, 0, ...unsignedLEB128(0) ],
@@ -516,11 +461,13 @@ const concatStrings = (scope, left, right, global, name, assign = false, bytestr
516
461
  [ Opcodes.i32_add ],
517
462
 
518
463
  // store length
519
- [ Opcodes.i32_store, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(pointer) ],
464
+ [ Opcodes.i32_store, Math.log2(ValtypeSize.i32) - 1, 0 ],
520
465
 
521
466
  // copy left
522
467
  // dst = out pointer + length size
523
- ...number(pointer + ValtypeSize.i32, Valtype.i32),
468
+ ...pointer,
469
+ ...number(ValtypeSize.i32, Valtype.i32),
470
+ [ Opcodes.i32_add ],
524
471
 
525
472
  // src = left pointer + length size
526
473
  [ Opcodes.local_get, leftPointer ],
@@ -533,7 +480,9 @@ const concatStrings = (scope, left, right, global, name, assign = false, bytestr
533
480
 
534
481
  // copy right
535
482
  // dst = out pointer + length size + left length * sizeof valtype
536
- ...number(pointer + ValtypeSize.i32, Valtype.i32),
483
+ ...pointer,
484
+ ...number(ValtypeSize.i32, Valtype.i32),
485
+ [ Opcodes.i32_add ],
537
486
 
538
487
  [ Opcodes.local_get, leftLength ],
539
488
  ...number(bytestrings ? ValtypeSize.i8 : ValtypeSize.i16, Valtype.i32),
@@ -553,7 +502,8 @@ const concatStrings = (scope, left, right, global, name, assign = false, bytestr
553
502
  [ ...Opcodes.memory_copy, 0x00, 0x00 ],
554
503
 
555
504
  // return new string (page)
556
- ...number(pointer)
505
+ ...pointer,
506
+ Opcodes.i32_from_u
557
507
  ];
558
508
  };
559
509
 
@@ -673,7 +623,10 @@ const truthy = (scope, wasm, type, intIn = false, intOut = false, forceTruthyMod
673
623
  ...wasm,
674
624
  ...(!intIn && intOut ? [ Opcodes.i32_to_u ] : [])
675
625
  ];
676
- // if (isIntOp(wasm[wasm.length - 1])) return [ ...wasm ];
626
+ if (isIntOp(wasm[wasm.length - 1])) return [
627
+ ...wasm,
628
+ ...(intOut ? [] : [ Opcodes.i32_from ]),
629
+ ];
677
630
 
678
631
  // todo/perf: use knownType and custom bytecode here instead of typeSwitch
679
632
 
@@ -1014,12 +967,9 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
1014
967
  [ Opcodes.end ],
1015
968
  ]));
1016
969
 
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
- // }
970
+ // add a surrounding block
971
+ startOut.push(stringOnly([ Opcodes.block, Valtype.i32 ]));
972
+ endOut.unshift(stringOnly([ Opcodes.end ]));
1023
973
  }
1024
974
 
1025
975
  return finalize([
@@ -1032,6 +982,33 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
1032
982
  };
1033
983
 
1034
984
  const generateBinaryExp = (scope, decl, _global, _name) => {
985
+ if (decl.operator === 'instanceof') {
986
+ // very hacky basic instanceof
987
+ // todo: support dynamic right-hand side
988
+
989
+ const out = generate(scope, decl.left);
990
+ disposeLeftover(out);
991
+
992
+ const rightName = decl.right.name;
993
+ if (!rightName) return todo(scope, 'instanceof dynamic right-hand side is not supported yet', true);
994
+
995
+ const checkType = TYPES[rightName.toLowerCase()];
996
+ if (checkType == null || rightName !== TYPE_NAMES[checkType] || checkType === TYPES.undefined) return todo(scope, 'instanceof right-hand side type unsupported', true);
997
+
998
+ if ([TYPES.number, TYPES.boolean, TYPES.string, TYPES.symbol, TYPES.object].includes(checkType)) {
999
+ out.push(...number(0));
1000
+ } else {
1001
+ out.push(
1002
+ ...getNodeType(scope, decl.left),
1003
+ ...number(checkType, Valtype.i32),
1004
+ [ Opcodes.i32_eq ],
1005
+ Opcodes.i32_from_u
1006
+ );
1007
+ }
1008
+
1009
+ return out;
1010
+ }
1011
+
1035
1012
  const out = performOp(scope, decl.operator, generate(scope, decl.left), generate(scope, decl.right), getNodeType(scope, decl.left), getNodeType(scope, decl.right), _global, _name);
1036
1013
 
1037
1014
  if (valtype !== 'i32' && ['==', '===', '!=', '!==', '>', '>=', '<', '<='].includes(decl.operator)) out.push(Opcodes.i32_from_u);
@@ -1054,7 +1031,7 @@ const asmFuncToAsm = (func, scope) => {
1054
1031
  });
1055
1032
  };
1056
1033
 
1057
- const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes = [], globalInits, returns, returnType, localNames = [], globalNames = [], data: _data = [], table = false }) => {
1034
+ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes = [], globalInits = [], returns, returnType, localNames = [], globalNames = [], data: _data = [], table = false }) => {
1058
1035
  const existing = funcs.find(x => x.name === name);
1059
1036
  if (existing) return existing;
1060
1037
 
@@ -1068,7 +1045,7 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
1068
1045
 
1069
1046
  for (const x of _data) {
1070
1047
  const copy = { ...x };
1071
- copy.offset += pages.size * pageSize;
1048
+ if (copy.offset != null) copy.offset += pages.size * pageSize;
1072
1049
  data.push(copy);
1073
1050
  }
1074
1051
 
@@ -1078,7 +1055,7 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
1078
1055
  locals,
1079
1056
  localInd: allLocals.length,
1080
1057
  returns,
1081
- returnType: returnType ?? TYPES.number,
1058
+ returnType,
1082
1059
  internal: true,
1083
1060
  index: currentFuncIndex++,
1084
1061
  table
@@ -1091,9 +1068,9 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
1091
1068
 
1092
1069
  let baseGlobalIdx, i = 0;
1093
1070
  for (const type of globalTypes) {
1094
- if (baseGlobalIdx === undefined) baseGlobalIdx = globalInd;
1071
+ if (baseGlobalIdx === undefined) baseGlobalIdx = globals['#ind'];
1095
1072
 
1096
- globals[globalNames[i] ?? `${name}_global_${i}`] = { idx: globalInd++, type, init: globalInits[i] ?? 0 };
1073
+ globals[globalNames[i] ?? `${name}_global_${i}`] = { idx: globals['#ind']++, type, init: globalInits[i] ?? 0 };
1097
1074
  i++;
1098
1075
  }
1099
1076
 
@@ -1106,11 +1083,15 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
1106
1083
  }
1107
1084
  }
1108
1085
 
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));
1086
+ if (table) {
1087
+ for (const inst of wasm) {
1088
+ if (inst[0] === Opcodes.i32_load16_u && inst.at(-1) === 'read_argc') {
1089
+ inst.splice(2, 99);
1090
+ inst.push(...unsignedLEB128(allocPage({}, 'func argc lut') * pageSize));
1091
+ }
1113
1092
  }
1093
+
1094
+ funcs.table = true;
1114
1095
  }
1115
1096
 
1116
1097
  func.wasm = wasm;
@@ -1254,7 +1235,7 @@ const getNodeType = (scope, node) => {
1254
1235
  const func = funcs.find(x => x.name === name);
1255
1236
 
1256
1237
  if (func) {
1257
- if (func.returnType) return func.returnType;
1238
+ if (func.returnType != null) return func.returnType;
1258
1239
  }
1259
1240
 
1260
1241
  if (builtinFuncs[name] && !builtinFuncs[name].typedReturns) return builtinFuncs[name].returnType ?? TYPES.number;
@@ -1270,15 +1251,7 @@ const getNodeType = (scope, node) => {
1270
1251
  const func = spl[spl.length - 1];
1271
1252
  const protoFuncs = Object.keys(prototypeFuncs).filter(x => x != TYPES.bytestring && prototypeFuncs[x][func] != null);
1272
1253
  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;
1254
+ if (protoFuncs[0].returnType != null) return protoFuncs[0].returnType;
1282
1255
  }
1283
1256
  }
1284
1257
 
@@ -1326,7 +1299,15 @@ const getNodeType = (scope, node) => {
1326
1299
  }
1327
1300
 
1328
1301
  if (node.type === 'AssignmentExpression') {
1329
- return getNodeType(scope, node.right);
1302
+ const op = node.operator.slice(0, -1) || '=';
1303
+ if (op === '=') return getNodeType(scope, node.right);
1304
+
1305
+ return getNodeType(scope, {
1306
+ type: ['||', '&&', '??'].includes(op) ? 'LogicalExpression' : 'BinaryExpression',
1307
+ left: node.left,
1308
+ right: node.right,
1309
+ operator: op
1310
+ });
1330
1311
  }
1331
1312
 
1332
1313
  if (node.type === 'ArrayExpression') {
@@ -1334,7 +1315,7 @@ const getNodeType = (scope, node) => {
1334
1315
  }
1335
1316
 
1336
1317
  if (node.type === 'BinaryExpression') {
1337
- if (['==', '===', '!=', '!==', '>', '>=', '<', '<='].includes(node.operator)) return TYPES.boolean;
1318
+ if (['==', '===', '!=', '!==', '>', '>=', '<', '<=', 'instanceof'].includes(node.operator)) return TYPES.boolean;
1338
1319
  if (node.operator !== '+') return TYPES.number;
1339
1320
 
1340
1321
  const knownLeft = knownType(scope, getNodeType(scope, node.left));
@@ -1345,23 +1326,6 @@ const getNodeType = (scope, node) => {
1345
1326
  if (knownLeft === TYPES.bytestring || knownRight === TYPES.bytestring) return TYPES.bytestring;
1346
1327
 
1347
1328
  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
1329
  }
1366
1330
 
1367
1331
  if (node.type === 'UnaryExpression') {
@@ -1381,18 +1345,16 @@ const getNodeType = (scope, node) => {
1381
1345
  if (Prefs.fastLength) return TYPES.number;
1382
1346
  }
1383
1347
 
1384
-
1385
1348
  const objectKnownType = knownType(scope, getNodeType(scope, node.object));
1386
1349
  if (objectKnownType != null) {
1387
1350
  if (name === 'length') {
1388
- if ([ TYPES.string, TYPES.bytestring, TYPES.array ].includes(objectKnownType)) return TYPES.number;
1351
+ if (typeHasFlag(objectKnownType, TYPE_FLAGS.length)) return TYPES.number;
1389
1352
  else return TYPES.undefined;
1390
1353
  }
1391
1354
 
1392
1355
  if (node.computed) {
1393
1356
  if (objectKnownType === TYPES.string) return TYPES.string;
1394
1357
  if (objectKnownType === TYPES.bytestring) return TYPES.bytestring;
1395
- if (objectKnownType === TYPES.array) return TYPES.number;
1396
1358
  }
1397
1359
  }
1398
1360
 
@@ -1458,10 +1420,10 @@ const countLeftover = wasm => {
1458
1420
 
1459
1421
  if (depth === 0)
1460
1422
  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)) {}
1423
+ 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.f32_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
1424
  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
- 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;
1425
+ else if ([Opcodes.i32_store, Opcodes.i64_store, Opcodes.f64_store, Opcodes.f32_store, Opcodes.i32_store16, Opcodes.i32_store8].includes(inst[0])) count -= 2;
1426
+ else if (inst[0] === Opcodes.memory_copy[0] && (inst[1] === Opcodes.memory_copy[1] || inst[1] === Opcodes.memory_init[1])) count -= 3;
1465
1427
  else if (inst[0] === Opcodes.return) count = 0;
1466
1428
  else if (inst[0] === Opcodes.call) {
1467
1429
  let func = funcs.find(x => x.index === inst[1]);
@@ -1498,6 +1460,18 @@ const generateExp = (scope, decl) => {
1498
1460
  return out;
1499
1461
  };
1500
1462
 
1463
+ const generateSequence = (scope, decl) => {
1464
+ let out = [];
1465
+
1466
+ const exprs = decl.expressions;
1467
+ for (let i = 0; i < exprs.length; i++) {
1468
+ if (i > 0) disposeLeftover(out);
1469
+ out.push(...generate(scope, exprs[i]));
1470
+ }
1471
+
1472
+ return out;
1473
+ };
1474
+
1501
1475
  const CTArrayUtil = {
1502
1476
  getLengthI32: pointer => [
1503
1477
  ...number(0, Valtype.i32),
@@ -1733,7 +1707,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1733
1707
  }, generate(scope, decl.arguments[0] ?? DEFAULT_VALUE), getNodeType(scope, decl.arguments[0] ?? DEFAULT_VALUE), protoLocal, protoLocal2, (length, itemType) => {
1734
1708
  return makeArray(scope, {
1735
1709
  rawElements: new Array(length)
1736
- }, _global, _name, true, itemType);
1710
+ }, _global, _name, true, itemType, true);
1737
1711
  }, () => {
1738
1712
  optUnused = true;
1739
1713
  return unusedValue;
@@ -1815,7 +1789,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1815
1789
  f64_store: { imms: 2, args: [ true, false ], returns: 0 },
1816
1790
 
1817
1791
  // value
1818
- i32_const: { imms: 1, args: [], returns: 1 },
1792
+ i32_const: { imms: 1, args: [], returns: 0 },
1819
1793
  };
1820
1794
 
1821
1795
  const opName = name.slice('__Porffor_wasm_'.length);
@@ -1900,7 +1874,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1900
1874
  if (indirectMode === 'strict') {
1901
1875
  return typeSwitch(scope, getNodeType(scope, decl.callee), {
1902
1876
  [TYPES.function]: [
1903
- ...argWasm,
1877
+ ...out,
1904
1878
  [ global ? Opcodes.global_get : Opcodes.local_get, local.idx ],
1905
1879
  Opcodes.i32_to_u,
1906
1880
  [ Opcodes.call_indirect, args.length, 0 ],
@@ -1978,7 +1952,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1978
1952
  const func = funcs[idx - importedFuncs.length]; // idx === scope.index ? scope : funcs.find(x => x.index === idx);
1979
1953
  const userFunc = func && !func.internal;
1980
1954
  const typedParams = userFunc || builtinFuncs[name]?.typedParams;
1981
- const typedReturns = (func && func.returnType == null) || builtinFuncs[name]?.typedReturns;
1955
+ const typedReturns = (userFunc && func.returnType == null) || builtinFuncs[name]?.typedReturns;
1982
1956
  const paramCount = func && (typedParams ? func.params.length / 2 : func.params.length);
1983
1957
 
1984
1958
  let args = decl.arguments;
@@ -2006,13 +1980,18 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
2006
1980
  continue;
2007
1981
  }
2008
1982
 
2009
- if (valtypeBinary !== Valtype.i32 && (
2010
- (builtinFuncs[name] && builtinFuncs[name].params[i * (typedParams ? 2 : 1)] === Valtype.i32) ||
2011
- (importedFuncs[name] && name.startsWith('profile'))
2012
- )) {
1983
+ if (valtypeBinary !== Valtype.i32 &&
1984
+ (func && func.params[i * (typedParams ? 2 : 1)] === Valtype.i32)
1985
+ ) {
2013
1986
  out.push(Opcodes.i32_to);
2014
1987
  }
2015
1988
 
1989
+ if (valtypeBinary === Valtype.i32 &&
1990
+ (func && func.params[i * (typedParams ? 2 : 1)] === Valtype.f64)
1991
+ ) {
1992
+ out.push([ Opcodes.f64_convert_i32_s ]);
1993
+ }
1994
+
2016
1995
  if (typedParams) out = out.concat(getNodeType(scope, arg));
2017
1996
  }
2018
1997
 
@@ -2034,6 +2013,10 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
2034
2013
  out.push(Opcodes.i32_from);
2035
2014
  }
2036
2015
 
2016
+ if (builtinFuncs[name] && builtinFuncs[name].returns?.[0] === Valtype.f64 && valtypeBinary === Valtype.i32) {
2017
+ out.push(Opcodes.i32_trunc_sat_f64_s);
2018
+ }
2019
+
2037
2020
  return out;
2038
2021
  };
2039
2022
 
@@ -2057,9 +2040,9 @@ const generateNew = (scope, decl, _global, _name) => {
2057
2040
  if (
2058
2041
  (builtinFuncs[name] && !builtinFuncs[name].constr) ||
2059
2042
  (internalConstrs[name] && builtinFuncs[name].notConstr)
2060
- ) return internalThrow(scope, 'TypeError', `${name} is not a constructor`);
2043
+ ) return internalThrow(scope, 'TypeError', `${name} is not a constructor`, true);
2061
2044
 
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)})`);
2045
+ 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
2046
 
2064
2047
  return generateCall(scope, decl, _global, _name);
2065
2048
  };
@@ -2077,7 +2060,7 @@ const unhackName = name => {
2077
2060
 
2078
2061
  const knownType = (scope, type) => {
2079
2062
  if (type.length === 1 && type[0][0] === Opcodes.i32_const) {
2080
- return type[0][1];
2063
+ return read_signedLEB128(type[0].slice(1));
2081
2064
  }
2082
2065
 
2083
2066
  if (typedInput && type.length === 1 && type[0][0] === Opcodes.local_get) {
@@ -2123,7 +2106,6 @@ const brTable = (input, bc, returns) => {
2123
2106
  }
2124
2107
 
2125
2108
  for (let i = 0; i < count; i++) {
2126
- // if (i === 0) out.push([ Opcodes.block, returns, 'br table start' ]);
2127
2109
  if (i === 0) out.push([ Opcodes.block, returns ]);
2128
2110
  else out.push([ Opcodes.block, Blocktype.void ]);
2129
2111
  }
@@ -2157,10 +2139,8 @@ const brTable = (input, bc, returns) => {
2157
2139
  [ Opcodes.br_table, ...encodeVector(table), 0 ]
2158
2140
  );
2159
2141
 
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
2142
+ // sort the wrong way and then reverse
2143
+ // so strings ('default') are at the start before any numbers
2164
2144
  const orderedBc = keys.sort((a, b) => b - a).reverse();
2165
2145
 
2166
2146
  br = count - 1;
@@ -2186,10 +2166,10 @@ const typeSwitch = (scope, type, bc, returns = valtypeBinary) => {
2186
2166
  return bc[known] ?? bc.default;
2187
2167
  }
2188
2168
 
2189
- if (Prefs.typeswitchUseBrtable)
2169
+ if (Prefs.typeswitchBrtable)
2190
2170
  return brTable(type, bc, returns);
2191
2171
 
2192
- const tmp = localTmp(scope, '#typeswitch_tmp', Valtype.i32);
2172
+ const tmp = localTmp(scope, '#typeswitch_tmp' + (Prefs.typeswitchUniqueTmp ? randId() : ''), Valtype.i32);
2193
2173
  const out = [
2194
2174
  ...type,
2195
2175
  [ Opcodes.local_set, tmp ],
@@ -2241,11 +2221,11 @@ const allocVar = (scope, name, global = false, type = true) => {
2241
2221
  return target[name].idx;
2242
2222
  }
2243
2223
 
2244
- let idx = global ? globalInd++ : scope.localInd++;
2224
+ let idx = global ? globals['#ind']++ : scope.localInd++;
2245
2225
  target[name] = { idx, type: valtypeBinary };
2246
2226
 
2247
2227
  if (type) {
2248
- let typeIdx = global ? globalInd++ : scope.localInd++;
2228
+ let typeIdx = global ? globals['#ind']++ : scope.localInd++;
2249
2229
  target[name + '#type'] = { idx: typeIdx, type: Valtype.i32, name };
2250
2230
  }
2251
2231
 
@@ -2338,24 +2318,10 @@ const generateVar = (scope, decl) => {
2338
2318
  }
2339
2319
 
2340
2320
  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
- // }
2321
+ const alreadyArray = scope.arrays?.get(name) != null;
2356
2322
 
2357
2323
  const generated = generate(scope, x.init, global, name);
2358
- if (scope.arrays?.get(name) != null) {
2324
+ if (!alreadyArray && scope.arrays?.get(name) != null) {
2359
2325
  // hack to set local as pointer before
2360
2326
  out.push(...number(scope.arrays.get(name)), [ global ? Opcodes.global_set : Opcodes.local_set, idx ]);
2361
2327
  if (generated.at(-1) == Opcodes.i32_from_u) generated.pop();
@@ -2381,11 +2347,6 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
2381
2347
  const { type, name } = decl.left;
2382
2348
  const [ local, isGlobal ] = lookupName(scope, name);
2383
2349
 
2384
- if (type === 'ObjectPattern') {
2385
- // hack: ignore object parts of `var a = {} = 2`
2386
- return generate(scope, decl.right);
2387
- }
2388
-
2389
2350
  if (isFuncType(decl.right.type)) {
2390
2351
  // hack for a = function () { ... }
2391
2352
  decl.right.id = { name };
@@ -2407,19 +2368,12 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
2407
2368
 
2408
2369
  // hack: .length setter
2409
2370
  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
2371
  const newValueTmp = localTmp(scope, '__length_setter_tmp');
2416
2372
  const pointerTmp = op === '=' ? null : localTmp(scope, '__member_setter_ptr_tmp', Valtype.i32);
2417
2373
 
2418
2374
  return [
2419
- ...(aotPointer ? number(0, Valtype.i32) : [
2420
- ...generate(scope, decl.left.object),
2421
- Opcodes.i32_to_u
2422
- ]),
2375
+ ...generate(scope, decl.left.object),
2376
+ Opcodes.i32_to_u,
2423
2377
  ...(!pointerTmp ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
2424
2378
 
2425
2379
  ...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
@@ -2430,7 +2384,7 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
2430
2384
  [ Opcodes.local_tee, newValueTmp ],
2431
2385
 
2432
2386
  Opcodes.i32_to_u,
2433
- [ Opcodes.i32_store, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(aotPointer ? pointer : 0) ],
2387
+ [ Opcodes.i32_store, Math.log2(ValtypeSize.i32) - 1, 0 ],
2434
2388
 
2435
2389
  [ Opcodes.local_get, newValueTmp ]
2436
2390
  ];
@@ -2438,21 +2392,14 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
2438
2392
 
2439
2393
  // arr[i]
2440
2394
  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
2395
  const newValueTmp = localTmp(scope, '__member_setter_val_tmp');
2447
2396
  const pointerTmp = op === '=' ? -1 : localTmp(scope, '__member_setter_ptr_tmp', Valtype.i32);
2448
2397
 
2449
2398
  return [
2450
2399
  ...typeSwitch(scope, getNodeType(scope, decl.left.object), {
2451
2400
  [TYPES.array]: [
2452
- ...(aotPointer ? [] : [
2453
- ...generate(scope, decl.left.object),
2454
- Opcodes.i32_to_u
2455
- ]),
2401
+ ...generate(scope, decl.left.object),
2402
+ Opcodes.i32_to_u,
2456
2403
 
2457
2404
  // get index as valtype
2458
2405
  ...generate(scope, decl.left.property),
@@ -2461,39 +2408,172 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
2461
2408
  // turn into byte offset by * valtypeSize (4 for i32, 8 for i64/f64)
2462
2409
  ...number(ValtypeSize[valtype] + 1, Valtype.i32),
2463
2410
  [ Opcodes.i32_mul ],
2464
- ...(aotPointer ? [] : [ [ Opcodes.i32_add ] ]),
2411
+ [ Opcodes.i32_add ],
2465
2412
  ...(op === '=' ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
2466
2413
 
2467
2414
  ...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
2468
2415
  [ Opcodes.local_get, pointerTmp ],
2469
- [ Opcodes.load, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ]
2416
+ [ Opcodes.load, 0, ValtypeSize.i32 ]
2470
2417
  ], generate(scope, decl.right), [
2471
2418
  [ Opcodes.local_get, pointerTmp ],
2472
- [ Opcodes.i32_load8_u, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32 + ValtypeSize[valtype]) ]
2419
+ [ Opcodes.i32_load8_u, 0, ValtypeSize.i32 + ValtypeSize[valtype] ]
2473
2420
  ], getNodeType(scope, decl.right), false, name, true)),
2474
2421
  [ Opcodes.local_tee, newValueTmp ],
2475
-
2476
- [ Opcodes.store, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ]
2422
+ [ Opcodes.store, 0, ValtypeSize.i32 ]
2477
2423
  ],
2478
2424
 
2479
- default: internalThrow(scope, 'TypeError', `Cannot assign member with non-array`)
2425
+ ...wrapBC({
2426
+ [TYPES.uint8array]: [
2427
+ [ Opcodes.i32_add ],
2428
+ ...(op === '=' ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
2429
+
2430
+ ...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
2431
+ [ Opcodes.local_get, pointerTmp ],
2432
+ [ Opcodes.i32_load8_u, 0, 4 ],
2433
+ Opcodes.i32_from_u
2434
+ ], generate(scope, decl.right), number(TYPES.number, Valtype.i32), getNodeType(scope, decl.right), false, name, true)),
2435
+ [ Opcodes.local_tee, newValueTmp ],
2436
+
2437
+ Opcodes.i32_to_u,
2438
+ [ Opcodes.i32_store8, 0, 4 ]
2439
+ ],
2440
+ [TYPES.uint8clampedarray]: [
2441
+ [ Opcodes.i32_add ],
2442
+ ...(op === '=' ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
2443
+
2444
+ ...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
2445
+ [ Opcodes.local_get, pointerTmp ],
2446
+ [ Opcodes.i32_load8_u, 0, 4 ],
2447
+ Opcodes.i32_from_u
2448
+ ], generate(scope, decl.right), number(TYPES.number, Valtype.i32), getNodeType(scope, decl.right), false, name, true)),
2449
+ [ Opcodes.local_tee, newValueTmp ],
2450
+
2451
+ ...number(0),
2452
+ [ Opcodes.f64_max ],
2453
+ ...number(255),
2454
+ [ Opcodes.f64_min ],
2455
+ Opcodes.i32_to_u,
2456
+ [ Opcodes.i32_store8, 0, 4 ]
2457
+ ],
2458
+ [TYPES.int8array]: [
2459
+ [ Opcodes.i32_add ],
2460
+ ...(op === '=' ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
2461
+
2462
+ ...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
2463
+ [ Opcodes.local_get, pointerTmp ],
2464
+ [ Opcodes.i32_load8_s, 0, 4 ],
2465
+ Opcodes.i32_from
2466
+ ], generate(scope, decl.right), number(TYPES.number, Valtype.i32), getNodeType(scope, decl.right), false, name, true)),
2467
+ [ Opcodes.local_tee, newValueTmp ],
2468
+
2469
+ Opcodes.i32_to,
2470
+ [ Opcodes.i32_store8, 0, 4 ]
2471
+ ],
2472
+ [TYPES.uint16array]: [
2473
+ ...number(2, Valtype.i32),
2474
+ [ Opcodes.i32_mul ],
2475
+ [ Opcodes.i32_add ],
2476
+ ...(op === '=' ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
2477
+
2478
+ ...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
2479
+ [ Opcodes.local_get, pointerTmp ],
2480
+ [ Opcodes.i32_load16_u, 0, 4 ],
2481
+ Opcodes.i32_from_u
2482
+ ], generate(scope, decl.right), number(TYPES.number, Valtype.i32), getNodeType(scope, decl.right), false, name, true)),
2483
+ [ Opcodes.local_tee, newValueTmp ],
2484
+
2485
+ Opcodes.i32_to_u,
2486
+ [ Opcodes.i32_store16, 0, 4 ]
2487
+ ],
2488
+ [TYPES.int16array]: [
2489
+ ...number(2, Valtype.i32),
2490
+ [ Opcodes.i32_mul ],
2491
+ [ Opcodes.i32_add ],
2492
+ ...(op === '=' ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
2493
+
2494
+ ...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
2495
+ [ Opcodes.local_get, pointerTmp ],
2496
+ [ Opcodes.i32_load16_s, 0, 4 ],
2497
+ Opcodes.i32_from
2498
+ ], generate(scope, decl.right), number(TYPES.number, Valtype.i32), getNodeType(scope, decl.right), false, name, true)),
2499
+ [ Opcodes.local_tee, newValueTmp ],
2500
+
2501
+ Opcodes.i32_to,
2502
+ [ Opcodes.i32_store16, 0, 4 ]
2503
+ ],
2504
+ [TYPES.uint32array]: [
2505
+ ...number(4, Valtype.i32),
2506
+ [ Opcodes.i32_mul ],
2507
+ [ Opcodes.i32_add ],
2508
+ ...(op === '=' ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
2509
+
2510
+ ...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
2511
+ [ Opcodes.local_get, pointerTmp ],
2512
+ [ Opcodes.i32_load, 0, 4 ],
2513
+ Opcodes.i32_from_u
2514
+ ], generate(scope, decl.right), number(TYPES.number, Valtype.i32), getNodeType(scope, decl.right), false, name, true)),
2515
+ [ Opcodes.local_tee, newValueTmp ],
2516
+
2517
+ Opcodes.i32_to_u,
2518
+ [ Opcodes.i32_store, 0, 4 ]
2519
+ ],
2520
+ [TYPES.int32array]: [
2521
+ ...number(4, Valtype.i32),
2522
+ [ Opcodes.i32_mul ],
2523
+ [ Opcodes.i32_add ],
2524
+ ...(op === '=' ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
2525
+
2526
+ ...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
2527
+ [ Opcodes.local_get, pointerTmp ],
2528
+ [ Opcodes.i32_load, 0, 4 ],
2529
+ Opcodes.i32_from
2530
+ ], generate(scope, decl.right), number(TYPES.number, Valtype.i32), getNodeType(scope, decl.right), false, name, true)),
2531
+ [ Opcodes.local_tee, newValueTmp ],
2532
+
2533
+ Opcodes.i32_to,
2534
+ [ Opcodes.i32_store, 0, 4 ]
2535
+ ],
2536
+ [TYPES.float32array]: [
2537
+ ...number(4, Valtype.i32),
2538
+ [ Opcodes.i32_mul ],
2539
+ [ Opcodes.i32_add ],
2540
+ ...(op === '=' ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
2541
+
2542
+ ...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
2543
+ [ Opcodes.local_get, pointerTmp ],
2544
+ [ Opcodes.f32_load, 0, 4 ],
2545
+ [ Opcodes.f64_promote_f32 ]
2546
+ ], generate(scope, decl.right), number(TYPES.number, Valtype.i32), getNodeType(scope, decl.right), false, name, true)),
2547
+ [ Opcodes.local_tee, newValueTmp ],
2548
+
2549
+ [ Opcodes.f32_demote_f64 ],
2550
+ [ Opcodes.f32_store, 0, 4 ]
2551
+ ],
2552
+ [TYPES.float64array]: [
2553
+ ...number(8, Valtype.i32),
2554
+ [ Opcodes.i32_mul ],
2555
+ [ Opcodes.i32_add ],
2556
+ ...(op === '=' ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
2557
+
2558
+ ...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
2559
+ [ Opcodes.local_get, pointerTmp ],
2560
+ [ Opcodes.f64_load, 0, 4 ]
2561
+ ], generate(scope, decl.right), number(TYPES.number, Valtype.i32), getNodeType(scope, decl.right), false, name, true)),
2562
+ [ Opcodes.local_tee, newValueTmp ],
2563
+
2564
+ [ Opcodes.f64_store, 0, 4 ]
2565
+ ],
2566
+ }, {
2567
+ prelude: [
2568
+ ...generate(scope, decl.left.object),
2569
+ Opcodes.i32_to_u,
2570
+ ...generate(scope, decl.left.property),
2571
+ Opcodes.i32_to_u,
2572
+ ],
2573
+ postlude: setLastType(scope, TYPES.number)
2574
+ }),
2480
2575
 
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
- // ]
2576
+ default: internalThrow(scope, 'TypeError', `Cannot assign member with non-array`)
2497
2577
  }, Blocktype.void),
2498
2578
 
2499
2579
  [ Opcodes.local_get, newValueTmp ]
@@ -2555,9 +2635,7 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
2555
2635
  [ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx ],
2556
2636
  [ isGlobal ? Opcodes.global_get : Opcodes.local_get, local.idx ],
2557
2637
 
2558
- // todo: string concat types
2559
-
2560
- ...setType(scope, name, TYPES.number)
2638
+ ...setType(scope, name, getNodeType(scope, decl))
2561
2639
  ];
2562
2640
  };
2563
2641
 
@@ -2571,7 +2649,7 @@ const generateUnary = (scope, decl) => {
2571
2649
  // * -1
2572
2650
 
2573
2651
  if (decl.prefix && decl.argument.type === 'Literal' && typeof decl.argument.value === 'number') {
2574
- // if -<N>, just return that
2652
+ // if -n, just return that as a const
2575
2653
  return number(-1 * decl.argument.value);
2576
2654
  }
2577
2655
 
@@ -2583,14 +2661,14 @@ const generateUnary = (scope, decl) => {
2583
2661
  case '!':
2584
2662
  const arg = decl.argument;
2585
2663
  if (arg.type === 'UnaryExpression' && arg.operator === '!') {
2586
- // !!x -> is x truthy
2664
+ // opt: !!x -> is x truthy
2587
2665
  return truthy(scope, generate(scope, arg.argument), getNodeType(scope, arg.argument), false, false);
2588
2666
  }
2667
+
2589
2668
  // !=
2590
- return falsy(scope, generate(scope, decl.argument), getNodeType(scope, decl.argument), false, false);
2669
+ return falsy(scope, generate(scope, arg), getNodeType(scope, arg), false, false);
2591
2670
 
2592
2671
  case '~':
2593
- // todo: does not handle Infinity properly (should convert to 0) (but opt const converting saves us sometimes)
2594
2672
  return [
2595
2673
  ...generate(scope, decl.argument),
2596
2674
  Opcodes.i32_to,
@@ -2877,12 +2955,14 @@ const generateForOf = (scope, decl) => {
2877
2955
 
2878
2956
  // // todo: we should only do this for strings but we don't know at compile-time :(
2879
2957
  // hack: this is naughty and will break things!
2880
- let newOut = number(0, Valtype.f64), newPointer = -1;
2881
- if (pages.hasAnyString) {
2958
+ let newOut = number(0, Valtype.i32), newPointer = number(0, Valtype.i32);
2959
+
2960
+ const known = knownType(scope, getNodeType(scope, decl.right));
2961
+ if ((known === TYPES.string || known === TYPES.bytestring) || (pages.hasAnyString && known == null)) {
2882
2962
  // todo: we use i16 even for bytestrings which should not make a bad thing happen, just be confusing for debugging?
2883
2963
  0, [ newOut, newPointer ] = makeArray(scope, {
2884
- rawElements: new Array(1)
2885
- }, isGlobal, leftName, true, 'i16');
2964
+ rawElements: new Array(0)
2965
+ }, isGlobal, leftName, true, 'i16', true);
2886
2966
  }
2887
2967
 
2888
2968
  // set type for local
@@ -2926,26 +3006,32 @@ const generateForOf = (scope, decl) => {
2926
3006
  [ Opcodes.end ],
2927
3007
  [ Opcodes.end ]
2928
3008
  ],
3009
+
2929
3010
  [TYPES.string]: [
2930
3011
  ...setType(scope, leftName, TYPES.string),
2931
3012
 
2932
- [ Opcodes.loop, Blocktype.void ],
2933
-
2934
3013
  // setup new/out array
2935
3014
  ...newOut,
2936
- [ Opcodes.drop ],
2937
3015
 
2938
- ...number(0, Valtype.i32), // base 0 for store after
3016
+ // set length to 1
3017
+ ...number(1, Valtype.i32),
3018
+ [ Opcodes.i32_store, 0, 0 ],
3019
+
3020
+ [ Opcodes.loop, Blocktype.void ],
3021
+
3022
+ // use as pointer for store later
3023
+ ...newPointer,
2939
3024
 
2940
3025
  // load current string ind {arg}
2941
3026
  [ Opcodes.local_get, pointer ],
2942
- [ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128(ValtypeSize.i32) ],
3027
+ [ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1, ValtypeSize.i32 ],
2943
3028
 
2944
3029
  // store to new string ind 0
2945
- [ Opcodes.i32_store16, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128(newPointer + ValtypeSize.i32) ],
3030
+ [ Opcodes.i32_store16, Math.log2(ValtypeSize.i16) - 1, ValtypeSize.i32 ],
2946
3031
 
2947
3032
  // return new string (page)
2948
- ...number(newPointer),
3033
+ ...newPointer,
3034
+ Opcodes.i32_from_u,
2949
3035
 
2950
3036
  [ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx ],
2951
3037
 
@@ -2977,25 +3063,30 @@ const generateForOf = (scope, decl) => {
2977
3063
  [TYPES.bytestring]: [
2978
3064
  ...setType(scope, leftName, TYPES.bytestring),
2979
3065
 
2980
- [ Opcodes.loop, Blocktype.void ],
2981
-
2982
3066
  // setup new/out array
2983
3067
  ...newOut,
2984
- [ Opcodes.drop ],
2985
3068
 
2986
- ...number(0, Valtype.i32), // base 0 for store after
3069
+ // set length to 1
3070
+ ...number(1, Valtype.i32),
3071
+ [ Opcodes.i32_store, 0, 0 ],
3072
+
3073
+ [ Opcodes.loop, Blocktype.void ],
3074
+
3075
+ // use as pointer for store later
3076
+ ...newPointer,
2987
3077
 
2988
3078
  // load current string ind {arg}
2989
3079
  [ Opcodes.local_get, pointer ],
2990
3080
  [ Opcodes.local_get, counter ],
2991
3081
  [ Opcodes.i32_add ],
2992
- [ Opcodes.i32_load8_u, 0, ...unsignedLEB128(ValtypeSize.i32) ],
3082
+ [ Opcodes.i32_load8_u, 0, ValtypeSize.i32 ],
2993
3083
 
2994
3084
  // store to new string ind 0
2995
- [ Opcodes.i32_store8, 0, ...unsignedLEB128(newPointer + ValtypeSize.i32) ],
3085
+ [ Opcodes.i32_store8, 0, ValtypeSize.i32 ],
2996
3086
 
2997
3087
  // return new string (page)
2998
- ...number(newPointer),
3088
+ ...newPointer,
3089
+ Opcodes.i32_from_u,
2999
3090
 
3000
3091
  [ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx ],
3001
3092
 
@@ -3024,6 +3115,7 @@ const generateForOf = (scope, decl) => {
3024
3115
  [ Opcodes.end ],
3025
3116
  [ Opcodes.end ]
3026
3117
  ],
3118
+
3027
3119
  [TYPES.set]: [
3028
3120
  [ Opcodes.loop, Blocktype.void ],
3029
3121
 
@@ -3062,6 +3154,106 @@ const generateForOf = (scope, decl) => {
3062
3154
  [ Opcodes.end ],
3063
3155
  [ Opcodes.end ]
3064
3156
  ],
3157
+
3158
+ ...wrapBC({
3159
+ [TYPES.uint8array]: [
3160
+ [ Opcodes.i32_add ],
3161
+
3162
+ [ Opcodes.i32_load8_u, 0, 4 ],
3163
+ Opcodes.i32_from_u
3164
+ ],
3165
+ [TYPES.uint8clampedarray]: [
3166
+ [ Opcodes.i32_add ],
3167
+
3168
+ [ Opcodes.i32_load8_u, 0, 4 ],
3169
+ Opcodes.i32_from_u
3170
+ ],
3171
+ [TYPES.int8array]: [
3172
+ [ Opcodes.i32_add ],
3173
+
3174
+ [ Opcodes.i32_load8_s, 0, 4 ],
3175
+ Opcodes.i32_from
3176
+ ],
3177
+ [TYPES.uint16array]: [
3178
+ ...number(2, Valtype.i32),
3179
+ [ Opcodes.i32_mul ],
3180
+ [ Opcodes.i32_add ],
3181
+
3182
+ [ Opcodes.i32_load16_u, 0, 4 ],
3183
+ Opcodes.i32_from_u
3184
+ ],
3185
+ [TYPES.int16array]: [
3186
+ ...number(2, Valtype.i32),
3187
+ [ Opcodes.i32_mul ],
3188
+ [ Opcodes.i32_add ],
3189
+
3190
+ [ Opcodes.i32_load16_s, 0, 4 ],
3191
+ Opcodes.i32_from
3192
+ ],
3193
+ [TYPES.uint32array]: [
3194
+ ...number(4, Valtype.i32),
3195
+ [ Opcodes.i32_mul ],
3196
+ [ Opcodes.i32_add ],
3197
+
3198
+ [ Opcodes.i32_load, 0, 4 ],
3199
+ Opcodes.i32_from_u
3200
+ ],
3201
+ [TYPES.int32array]: [
3202
+ ...number(4, Valtype.i32),
3203
+ [ Opcodes.i32_mul ],
3204
+ [ Opcodes.i32_add ],
3205
+
3206
+ [ Opcodes.i32_load, 0, 4 ],
3207
+ Opcodes.i32_from
3208
+ ],
3209
+ [TYPES.float32array]: [
3210
+ ...number(4, Valtype.i32),
3211
+ [ Opcodes.i32_mul ],
3212
+ [ Opcodes.i32_add ],
3213
+
3214
+ [ Opcodes.f32_load, 0, 4 ],
3215
+ [ Opcodes.f64_promote_f32 ]
3216
+ ],
3217
+ [TYPES.float64array]: [
3218
+ ...number(8, Valtype.i32),
3219
+ [ Opcodes.i32_mul ],
3220
+ [ Opcodes.i32_add ],
3221
+
3222
+ [ Opcodes.f64_load, 0, 4 ]
3223
+ ],
3224
+ }, {
3225
+ prelude: [
3226
+ ...setType(scope, leftName, TYPES.number),
3227
+
3228
+ [ Opcodes.loop, Blocktype.void ],
3229
+
3230
+ [ Opcodes.local_get, pointer ],
3231
+ [ Opcodes.local_get, counter ]
3232
+ ],
3233
+ postlude: [
3234
+ [ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx ],
3235
+
3236
+ [ Opcodes.block, Blocktype.void ],
3237
+ [ Opcodes.block, Blocktype.void ],
3238
+ ...generate(scope, decl.body),
3239
+ [ Opcodes.end ],
3240
+
3241
+ // increment counter by 1
3242
+ [ Opcodes.local_get, counter ],
3243
+ ...number(1, Valtype.i32),
3244
+ [ Opcodes.i32_add ],
3245
+ [ Opcodes.local_tee, counter ],
3246
+
3247
+ // loop if counter != length
3248
+ [ Opcodes.local_get, length ],
3249
+ [ Opcodes.i32_ne ],
3250
+ [ Opcodes.br_if, 1 ],
3251
+
3252
+ [ Opcodes.end ],
3253
+ [ Opcodes.end ]
3254
+ ]
3255
+ }),
3256
+
3065
3257
  default: internalThrow(scope, 'TypeError', `Tried for..of on non-iterable type`)
3066
3258
  }, Blocktype.void));
3067
3259
 
@@ -3134,32 +3326,102 @@ const generateLabel = (scope, decl) => {
3134
3326
  const generateThrow = (scope, decl) => {
3135
3327
  scope.throws = true;
3136
3328
 
3137
- let message = decl.argument.value, constructor = null;
3329
+ const exceptionMode = Prefs.exceptionMode ?? 'lut';
3330
+ if (exceptionMode === 'lut') {
3331
+ let message = decl.argument.value, constructor = null;
3332
+
3333
+ // support `throw (new)? Error(...)`
3334
+ if (!message && (decl.argument.type === 'NewExpression' || decl.argument.type === 'CallExpression')) {
3335
+ constructor = decl.argument.callee.name;
3336
+ message = decl.argument.arguments[0]?.value ?? '';
3337
+ }
3338
+
3339
+ if (tags.length === 0) tags.push({
3340
+ params: [ Valtype.i32 ],
3341
+ results: [],
3342
+ idx: tags.length
3343
+ });
3344
+
3345
+ let exceptId = exceptions.findIndex(x => x.constructor === constructor && x.message === message);
3346
+ if (exceptId === -1) exceptId = exceptions.push({ constructor, message }) - 1;
3347
+
3348
+ scope.exceptions ??= [];
3349
+ scope.exceptions.push(exceptId);
3138
3350
 
3139
- // hack: throw new X("...") -> throw "..."
3140
- if (!message && (decl.argument.type === 'NewExpression' || decl.argument.type === 'CallExpression')) {
3141
- constructor = decl.argument.callee.name;
3142
- message = decl.argument.arguments[0]?.value ?? '';
3351
+ return [
3352
+ ...number(exceptId, Valtype.i32),
3353
+ [ Opcodes.throw, tags[0].idx ]
3354
+ ];
3143
3355
  }
3144
3356
 
3145
- if (tags.length === 0) tags.push({
3146
- params: [ Valtype.i32 ],
3147
- results: [],
3148
- idx: tags.length
3149
- });
3357
+ if (exceptionMode === 'stack') {
3358
+ if (tags.length === 0) tags.push({
3359
+ params: [ valtypeBinary, Valtype.i32 ],
3360
+ results: [],
3361
+ idx: tags.length
3362
+ });
3150
3363
 
3151
- let exceptId = exceptions.push({ constructor, message }) - 1;
3152
- let tagIdx = tags[0].idx;
3364
+ return [
3365
+ ...generate(scope, decl.argument),
3366
+ ...getNodeType(scope, decl.argument),
3367
+ [ Opcodes.throw, tags[0].idx ]
3368
+ ];
3369
+ }
3153
3370
 
3154
- scope.exceptions ??= [];
3155
- scope.exceptions.push(exceptId);
3371
+ if (exceptionMode === 'stackest') {
3372
+ let message = decl.argument, constructor = null;
3156
3373
 
3157
- // todo: write a description of how this works lol
3374
+ // support `throw (new)? Error(...)`
3375
+ if (message.type === 'NewExpression' || message.type === 'CallExpression') {
3376
+ constructor = decl.argument.callee;
3377
+ message = decl.argument.arguments[0];
3378
+ }
3158
3379
 
3159
- return [
3160
- [ Opcodes.i32_const, signedLEB128(exceptId) ],
3161
- [ Opcodes.throw, tagIdx ]
3162
- ];
3380
+ message ??= DEFAULT_VALUE;
3381
+
3382
+ if (tags.length === 0) tags.push({
3383
+ params: [ valtypeBinary, valtypeBinary, Valtype.i32 ],
3384
+ results: [],
3385
+ idx: tags.length
3386
+ });
3387
+
3388
+ return [
3389
+ ...(constructor == null ? number(-1) : generate(scope, constructor)),
3390
+ ...generate(scope, message),
3391
+ ...getNodeType(scope, message),
3392
+ [ Opcodes.throw, tags[0].idx ]
3393
+ ];
3394
+ }
3395
+
3396
+ if (exceptionMode === 'partial') {
3397
+ let message = decl.argument, constructor = null;
3398
+
3399
+ // support `throw (new)? Error(...)`
3400
+ if (message.type === 'NewExpression' || message.type === 'CallExpression') {
3401
+ constructor = decl.argument.callee.name;
3402
+ message = decl.argument.arguments[0];
3403
+ }
3404
+
3405
+ message ??= DEFAULT_VALUE;
3406
+
3407
+ if (tags.length === 0) tags.push({
3408
+ params: [ Valtype.i32, valtypeBinary, Valtype.i32 ],
3409
+ results: [],
3410
+ idx: tags.length
3411
+ });
3412
+
3413
+ let exceptId = exceptions.push({ constructor }) - 1;
3414
+
3415
+ scope.exceptions ??= [];
3416
+ scope.exceptions.push(exceptId);
3417
+
3418
+ return [
3419
+ ...number(exceptId, Valtype.i32),
3420
+ ...generate(scope, message),
3421
+ ...getNodeType(scope, message),
3422
+ [ Opcodes.throw, tags[0].idx ]
3423
+ ];
3424
+ }
3163
3425
  };
3164
3426
 
3165
3427
  const generateTry = (scope, decl) => {
@@ -3210,18 +3472,6 @@ const allocPage = (scope, reason, type) => {
3210
3472
  scope.pages ??= new Map();
3211
3473
  scope.pages.set(reason, { ind, type });
3212
3474
 
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
3475
  return ind;
3226
3476
  };
3227
3477
 
@@ -3259,39 +3509,58 @@ const compileBytes = (val, itemType) => {
3259
3509
  }
3260
3510
  };
3261
3511
 
3262
- const getAllocType = itemType => {
3263
- switch (itemType) {
3264
- case 'i8': return 'bytestring';
3265
- case 'i16': return 'string';
3512
+ const makeData = (scope, elements, offset = null, itemType, initEmpty) => {
3513
+ const length = elements.length;
3266
3514
 
3267
- default: return 'array';
3515
+ // if length is 0 memory/data will just be 0000... anyway
3516
+ if (length === 0) return false;
3517
+
3518
+ let bytes = compileBytes(length, 'i32');
3519
+
3520
+ if (!initEmpty) for (let i = 0; i < length; i++) {
3521
+ if (elements[i] == null) continue;
3522
+
3523
+ bytes.push(...compileBytes(elements[i], itemType));
3268
3524
  }
3269
- };
3270
3525
 
3271
- const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty = false, itemType = valtype, typed = false) => {
3272
- const out = [];
3526
+ const obj = { bytes };
3527
+ if (offset != null) obj.offset = offset;
3528
+
3529
+ const idx = data.push(obj) - 1;
3530
+
3531
+ scope.data ??= [];
3532
+ scope.data.push(idx);
3273
3533
 
3274
- scope.arrays ??= new Map();
3534
+ return { idx, size: bytes.length };
3535
+ };
3275
3536
 
3276
- let firstAssign = false;
3277
- if (!scope.arrays.has(name) || name === '$undeclared') {
3278
- firstAssign = true;
3537
+ const printStaticStr = str => {
3538
+ const out = [];
3279
3539
 
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;
3540
+ for (let i = 0; i < str.length; i++) {
3541
+ out.push(
3542
+ // ...number(str.charCodeAt(i)),
3543
+ ...number(str.charCodeAt(i), Valtype.i32),
3544
+ Opcodes.i32_from_u,
3545
+ [ Opcodes.call, importedFuncs.printChar ]
3546
+ );
3547
+ }
3282
3548
 
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);
3549
+ return out;
3550
+ };
3286
3551
 
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);
3552
+ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty = false, itemType = valtype, intOut = false, typed = false) => {
3553
+ if (itemType !== 'i16' && itemType !== 'i8') {
3554
+ pages.hasArray = true;
3555
+ } else {
3556
+ pages.hasAnyString = true;
3557
+ if (itemType === 'i8') pages.hasByteString = true;
3558
+ else pages.hasString = true;
3290
3559
  }
3291
3560
 
3292
- const pointer = scope.arrays.get(name);
3561
+ const out = [];
3293
3562
 
3294
- const local = global ? globals[name] : scope.locals[name];
3563
+ const uniqueName = name === '$undeclared' ? name + randId() : name;
3295
3564
 
3296
3565
  const useRawElements = !!decl.rawElements;
3297
3566
  const elements = useRawElements ? decl.rawElements : decl.elements;
@@ -3299,46 +3568,81 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
3299
3568
  const valtype = itemTypeToValtype[itemType];
3300
3569
  const length = elements.length;
3301
3570
 
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');
3571
+ const allocated = allocator.alloc({ scope, pages, globals, asmFunc, funcIndex }, uniqueName, { itemType });
3572
+
3573
+ let pointer = allocated;
3574
+ if (allocator.constructor.name !== 'StaticAllocator') {
3575
+ // const tmp = localTmp(scope, '#makearray_pointer' + uniqueName, Valtype.i32);
3576
+ const tmp = localTmp(scope, '#makearray_pointer' + name, Valtype.i32);
3577
+ out.push(
3578
+ ...allocated,
3579
+ [ Opcodes.local_set, tmp ]
3580
+ );
3581
+
3582
+ if (Prefs.runtimeAllocLog) out.push(
3583
+ ...printStaticStr(`${name}: `),
3584
+
3585
+ [ Opcodes.local_get, tmp ],
3586
+ Opcodes.i32_from_u,
3587
+ [ Opcodes.call, 0 ],
3588
+
3589
+ ...number(10),
3590
+ [ Opcodes.call, 1 ]
3591
+ );
3306
3592
 
3307
- if (!initEmpty) for (let i = 0; i < length; i++) {
3308
- if (elements[i] == null) continue;
3593
+ pointer = [ [ Opcodes.local_get, tmp ] ];
3309
3594
 
3310
- bytes.push(...compileBytes(elements[i], itemType));
3595
+ if (Prefs.data && useRawElements) {
3596
+ const data = makeData(scope, elements, null, itemType, initEmpty);
3597
+ if (data) {
3598
+ // init data
3599
+ out.push(
3600
+ ...pointer,
3601
+ ...number(0, Valtype.i32),
3602
+ ...number(data.size, Valtype.i32),
3603
+ [ ...Opcodes.memory_init, ...unsignedLEB128(data.idx), 0 ]
3604
+ );
3311
3605
  }
3312
3606
 
3313
- const ind = data.push({
3314
- offset: pointer,
3315
- bytes
3316
- }) - 1;
3607
+ // return pointer in out
3608
+ out.push(
3609
+ ...pointer,
3610
+ ...(!intOut ? [ Opcodes.i32_from_u ] : [])
3611
+ );
3317
3612
 
3318
- scope.data ??= [];
3319
- scope.data.push(ind);
3613
+ return [ out, pointer ];
3320
3614
  }
3615
+ } else {
3616
+ const rawPtr = read_signedLEB128(pointer[0].slice(1));
3321
3617
 
3322
- // local value as pointer
3323
- out.push(...number(pointer));
3618
+ scope.arrays ??= new Map();
3619
+ const firstAssign = !scope.arrays.has(uniqueName);
3620
+ if (firstAssign) scope.arrays.set(uniqueName, rawPtr);
3324
3621
 
3325
- return [ out, pointer ];
3326
- }
3622
+ if (Prefs.data && firstAssign && useRawElements) {
3623
+ makeData(scope, elements, rawPtr, itemType, initEmpty);
3327
3624
 
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
- );
3625
+ // local value as pointer
3626
+ return [ number(rawPtr, intOut ? Valtype.i32 : valtypeBinary), pointer ];
3627
+ }
3628
+
3629
+ const local = global ? globals[name] : scope.locals[name];
3630
+ const pointerTmp = local != null ? localTmp(scope, '#makearray_pointer_tmp', Valtype.i32) : null;
3631
+ if (pointerTmp != null) {
3632
+ out.push(
3633
+ [ global ? Opcodes.global_get : Opcodes.local_get, local.idx ],
3634
+ Opcodes.i32_to_u,
3635
+ [ Opcodes.local_set, pointerTmp ]
3636
+ );
3637
+
3638
+ pointer = [ [ Opcodes.local_get, pointerTmp ] ];
3639
+ }
3335
3640
  }
3336
3641
 
3337
- const pointerWasm = pointerTmp != null ? [ [ Opcodes.local_get, pointerTmp ] ] : number(pointer, Valtype.i32);
3338
3642
 
3339
3643
  // store length
3340
3644
  out.push(
3341
- ...pointerWasm,
3645
+ ...pointer,
3342
3646
  ...number(length, Valtype.i32),
3343
3647
  [ Opcodes.i32_store, Math.log2(ValtypeSize.i32) - 1, 0 ]
3344
3648
  );
@@ -3350,11 +3654,11 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
3350
3654
 
3351
3655
  const offset = ValtypeSize.i32 + i * sizePerEl;
3352
3656
  out.push(
3353
- ...pointerWasm,
3657
+ ...pointer,
3354
3658
  ...(useRawElements ? number(elements[i], Valtype[valtype]) : generate(scope, elements[i])),
3355
3659
  [ storeOp, 0, ...unsignedLEB128(offset) ],
3356
3660
  ...(!typed ? [] : [ // typed presumes !useRawElements
3357
- ...pointerWasm,
3661
+ ...pointer,
3358
3662
  ...getNodeType(scope, elements[i]),
3359
3663
  [ Opcodes.i32_store8, 0, ...unsignedLEB128(offset + ValtypeSize[itemType]) ]
3360
3664
  ])
@@ -3362,12 +3666,13 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
3362
3666
  }
3363
3667
 
3364
3668
  // local value as pointer
3365
- out.push(...pointerWasm, Opcodes.i32_from_u);
3669
+ out.push(...pointer);
3670
+ if (!intOut) out.push(Opcodes.i32_from_u);
3366
3671
 
3367
3672
  return [ out, pointer ];
3368
3673
  };
3369
3674
 
3370
- const storeArray = (scope, array, index, element, aotPointer = null) => {
3675
+ const storeArray = (scope, array, index, element) => {
3371
3676
  if (!Array.isArray(element)) element = generate(scope, element);
3372
3677
  if (typeof index === 'number') index = number(index);
3373
3678
 
@@ -3379,26 +3684,25 @@ const storeArray = (scope, array, index, element, aotPointer = null) => {
3379
3684
  Opcodes.i32_to_u,
3380
3685
  ...number(ValtypeSize[valtype] + 1, Valtype.i32),
3381
3686
  [ Opcodes.i32_mul ],
3382
- ...(aotPointer ? [] : [
3383
- ...array,
3384
- Opcodes.i32_to_u,
3385
- [ Opcodes.i32_add ],
3386
- ]),
3687
+
3688
+ ...array,
3689
+ Opcodes.i32_to_u,
3690
+ [ Opcodes.i32_add ],
3387
3691
  [ Opcodes.local_set, offset ],
3388
3692
 
3389
3693
  // store value
3390
3694
  [ Opcodes.local_get, offset ],
3391
3695
  ...generate(scope, element),
3392
- [ Opcodes.store, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ],
3696
+ [ Opcodes.store, 0, ValtypeSize.i32 ],
3393
3697
 
3394
3698
  // store type
3395
3699
  [ Opcodes.local_get, offset ],
3396
3700
  ...getNodeType(scope, element),
3397
- [ Opcodes.i32_store8, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32 + ValtypeSize[valtype]) ]
3701
+ [ Opcodes.i32_store8, 0, ValtypeSize.i32 + ValtypeSize[valtype] ]
3398
3702
  ];
3399
3703
  };
3400
3704
 
3401
- const loadArray = (scope, array, index, aotPointer = null) => {
3705
+ const loadArray = (scope, array, index) => {
3402
3706
  if (typeof index === 'number') index = number(index);
3403
3707
 
3404
3708
  const offset = localTmp(scope, '#loadArray_offset', Valtype.i32);
@@ -3409,20 +3713,19 @@ const loadArray = (scope, array, index, aotPointer = null) => {
3409
3713
  Opcodes.i32_to_u,
3410
3714
  ...number(ValtypeSize[valtype] + 1, Valtype.i32),
3411
3715
  [ Opcodes.i32_mul ],
3412
- ...(aotPointer ? [] : [
3413
- ...array,
3414
- Opcodes.i32_to_u,
3415
- [ Opcodes.i32_add ],
3416
- ]),
3716
+
3717
+ ...array,
3718
+ Opcodes.i32_to_u,
3719
+ [ Opcodes.i32_add ],
3417
3720
  [ Opcodes.local_set, offset ],
3418
3721
 
3419
3722
  // load value
3420
3723
  [ Opcodes.local_get, offset ],
3421
- [ Opcodes.load, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ],
3724
+ [ Opcodes.load, 0, ValtypeSize.i32 ],
3422
3725
 
3423
3726
  // load type
3424
3727
  [ Opcodes.local_get, offset ],
3425
- [ Opcodes.i32_load8_u, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32 + ValtypeSize[valtype]) ]
3728
+ [ Opcodes.i32_load8_u, 0, ValtypeSize.i32 + ValtypeSize[valtype] ]
3426
3729
  ];
3427
3730
  };
3428
3731
 
@@ -3454,7 +3757,7 @@ const makeString = (scope, str, global = false, name = '$undeclared', forceBytes
3454
3757
  };
3455
3758
 
3456
3759
  const generateArray = (scope, decl, global = false, name = '$undeclared', initEmpty = false) => {
3457
- return makeArray(scope, decl, global, name, initEmpty, valtype, true)[0];
3760
+ return makeArray(scope, decl, global, name, initEmpty, valtype, false, true)[0];
3458
3761
  };
3459
3762
 
3460
3763
  const generateObject = (scope, decl, global = false, name = '$undeclared') => {
@@ -3471,11 +3774,21 @@ const withType = (scope, wasm, type) => [
3471
3774
  ...setLastType(scope, type)
3472
3775
  ];
3473
3776
 
3777
+ const wrapBC = (bc, { prelude = [], postlude = [] } = {}) => {
3778
+ const out = {};
3779
+ for (const x in bc) {
3780
+ out[x] = [
3781
+ ...prelude,
3782
+ ...bc[x],
3783
+ ...postlude
3784
+ ];
3785
+ }
3786
+
3787
+ return out;
3788
+ };
3789
+
3474
3790
  const generateMember = (scope, decl, _global, _name) => {
3475
3791
  const name = decl.object.name;
3476
- const pointer = scope.arrays?.get(name);
3477
-
3478
- const aotPointer = Prefs.aotPointerOpt && pointer;
3479
3792
 
3480
3793
  // hack: .name
3481
3794
  if (decl.property.name === 'name') {
@@ -3510,18 +3823,16 @@ const generateMember = (scope, decl, _global, _name) => {
3510
3823
  }
3511
3824
 
3512
3825
  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);
3826
+ if (importedFuncs[name]) return withType(scope, number(importedFuncs[name].params.length ?? importedFuncs[name].params), TYPES.number);
3514
3827
  if (internalConstrs[name]) return withType(scope, number(internalConstrs[name].length ?? 0), TYPES.number);
3515
3828
 
3516
3829
  if (Prefs.fastLength) {
3517
3830
  // presume valid length object
3518
3831
  return [
3519
- ...(aotPointer ? number(0, Valtype.i32) : [
3520
- ...generate(scope, decl.object),
3521
- Opcodes.i32_to_u
3522
- ]),
3832
+ ...generate(scope, decl.object),
3833
+ Opcodes.i32_to_u,
3523
3834
 
3524
- [ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(aotPointer ? pointer : 0) ],
3835
+ [ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, 0 ],
3525
3836
  Opcodes.i32_from_u
3526
3837
  ];
3527
3838
  }
@@ -3529,13 +3840,11 @@ const generateMember = (scope, decl, _global, _name) => {
3529
3840
  const type = getNodeType(scope, decl.object);
3530
3841
  const known = knownType(scope, type);
3531
3842
  if (known != null) {
3532
- 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
- ]),
3843
+ if (typeHasFlag(known, TYPE_FLAGS.length)) return [
3844
+ ...generate(scope, decl.object),
3845
+ Opcodes.i32_to_u,
3537
3846
 
3538
- [ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(aotPointer ? pointer : 0) ],
3847
+ [ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, 0 ],
3539
3848
  Opcodes.i32_from_u
3540
3849
  ];
3541
3850
 
@@ -3543,14 +3852,14 @@ const generateMember = (scope, decl, _global, _name) => {
3543
3852
  }
3544
3853
 
3545
3854
  return [
3546
- ...typeIsOneOf(getNodeType(scope, decl.object), [ TYPES.string, TYPES.bytestring, TYPES.array ]),
3855
+ ...getNodeType(scope, decl.object),
3856
+ ...number(TYPE_FLAGS.length, Valtype.i32),
3857
+ [ Opcodes.i32_and ],
3547
3858
  [ Opcodes.if, valtypeBinary ],
3548
- ...(aotPointer ? number(0, Valtype.i32) : [
3549
- ...generate(scope, decl.object),
3550
- Opcodes.i32_to_u
3551
- ]),
3859
+ ...generate(scope, decl.object),
3860
+ Opcodes.i32_to_u,
3552
3861
 
3553
- [ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(aotPointer ? pointer : 0) ],
3862
+ [ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, 0 ],
3554
3863
  Opcodes.i32_from_u,
3555
3864
 
3556
3865
  ...setLastType(scope, TYPES.number),
@@ -3593,25 +3902,31 @@ const generateMember = (scope, decl, _global, _name) => {
3593
3902
 
3594
3903
  // // todo: we should only do this for strings but we don't know at compile-time :(
3595
3904
  // hack: this is naughty and will break things!
3596
- let newOut = number(0, valtypeBinary), newPointer = -1;
3597
- if (pages.hasAnyString) {
3905
+ let newOut = number(0, Valtype.i32), newPointer = number(0, Valtype.i32);
3906
+
3907
+ const known = knownType(scope, getNodeType(scope, decl.object));
3908
+ if ((known === TYPES.string || known === TYPES.bytestring) || (pages.hasAnyString && known == null)) {
3909
+ // todo: we use i16 even for bytestrings which should not make a bad thing happen, just be confusing for debugging?
3598
3910
  0, [ newOut, newPointer ] = makeArray(scope, {
3599
- rawElements: new Array(1)
3600
- }, _global, _name, true, 'i16');
3911
+ rawElements: new Array(0)
3912
+ }, _global, _name, true, 'i16', true);
3601
3913
  }
3602
3914
 
3603
3915
  return typeSwitch(scope, getNodeType(scope, decl.object), {
3604
3916
  [TYPES.array]: [
3605
- ...loadArray(scope, object, property, aotPointer),
3917
+ ...loadArray(scope, object, property),
3606
3918
  ...setLastType(scope)
3607
3919
  ],
3608
-
3609
3920
  [TYPES.string]: [
3610
3921
  // setup new/out array
3611
3922
  ...newOut,
3612
- [ Opcodes.drop ],
3613
3923
 
3614
- ...number(0, Valtype.i32), // base 0 for store later
3924
+ // set length to 1
3925
+ ...number(1, Valtype.i32),
3926
+ [ Opcodes.i32_store, 0, 0 ],
3927
+
3928
+ // use as pointer for store later
3929
+ ...newPointer,
3615
3930
 
3616
3931
  ...property,
3617
3932
  Opcodes.i32_to_u,
@@ -3619,54 +3934,132 @@ const generateMember = (scope, decl, _global, _name) => {
3619
3934
  ...number(ValtypeSize.i16, Valtype.i32),
3620
3935
  [ Opcodes.i32_mul ],
3621
3936
 
3622
- ...(aotPointer ? [] : [
3623
- ...object,
3624
- Opcodes.i32_to_u,
3625
- [ Opcodes.i32_add ]
3626
- ]),
3937
+ ...object,
3938
+ Opcodes.i32_to_u,
3939
+ [ Opcodes.i32_add ],
3627
3940
 
3628
3941
  // load current string ind {arg}
3629
- [ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ],
3942
+ [ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1, ValtypeSize.i32 ],
3630
3943
 
3631
3944
  // store to new string ind 0
3632
- [ Opcodes.i32_store16, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128(newPointer + ValtypeSize.i32) ],
3945
+ [ Opcodes.i32_store16, Math.log2(ValtypeSize.i16) - 1, ValtypeSize.i32 ],
3633
3946
 
3634
3947
  // return new string (page)
3635
- ...number(newPointer),
3948
+ ...newPointer,
3949
+ Opcodes.i32_from_u,
3636
3950
  ...setLastType(scope, TYPES.string)
3637
3951
  ],
3638
3952
  [TYPES.bytestring]: [
3639
3953
  // setup new/out array
3640
3954
  ...newOut,
3641
- [ Opcodes.drop ],
3642
3955
 
3643
- ...number(0, Valtype.i32), // base 0 for store later
3956
+ // set length to 1
3957
+ ...number(1, Valtype.i32),
3958
+ [ Opcodes.i32_store, 0, 0 ],
3959
+
3960
+ // use as pointer for store later
3961
+ ...newPointer,
3644
3962
 
3645
3963
  ...property,
3646
3964
  Opcodes.i32_to_u,
3647
3965
 
3648
- ...(aotPointer ? [] : [
3649
- ...object,
3650
- Opcodes.i32_to_u,
3651
- [ Opcodes.i32_add ]
3652
- ]),
3966
+ ...object,
3967
+ Opcodes.i32_to_u,
3968
+ [ Opcodes.i32_add ],
3653
3969
 
3654
3970
  // load current string ind {arg}
3655
- [ Opcodes.i32_load8_u, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ],
3971
+ [ Opcodes.i32_load8_u, 0, ValtypeSize.i32 ],
3656
3972
 
3657
3973
  // store to new string ind 0
3658
- [ Opcodes.i32_store8, 0, ...unsignedLEB128(newPointer + ValtypeSize.i32) ],
3974
+ [ Opcodes.i32_store8, 0, ValtypeSize.i32 ],
3659
3975
 
3660
3976
  // return new string (page)
3661
- ...number(newPointer),
3977
+ ...newPointer,
3978
+ Opcodes.i32_from_u,
3662
3979
  ...setLastType(scope, TYPES.bytestring)
3663
3980
  ],
3664
3981
 
3982
+ ...wrapBC({
3983
+ [TYPES.uint8array]: [
3984
+ [ Opcodes.i32_add ],
3985
+
3986
+ [ Opcodes.i32_load8_u, 0, 4 ],
3987
+ Opcodes.i32_from_u
3988
+ ],
3989
+ [TYPES.uint8clampedarray]: [
3990
+ [ Opcodes.i32_add ],
3991
+
3992
+ [ Opcodes.i32_load8_u, 0, 4 ],
3993
+ Opcodes.i32_from_u
3994
+ ],
3995
+ [TYPES.int8array]: [
3996
+ [ Opcodes.i32_add ],
3997
+
3998
+ [ Opcodes.i32_load8_s, 0, 4 ],
3999
+ Opcodes.i32_from
4000
+ ],
4001
+ [TYPES.uint16array]: [
4002
+ ...number(2, Valtype.i32),
4003
+ [ Opcodes.i32_mul ],
4004
+ [ Opcodes.i32_add ],
4005
+
4006
+ [ Opcodes.i32_load16_u, 0, 4 ],
4007
+ Opcodes.i32_from_u
4008
+ ],
4009
+ [TYPES.int16array]: [
4010
+ ...number(2, Valtype.i32),
4011
+ [ Opcodes.i32_mul ],
4012
+ [ Opcodes.i32_add ],
4013
+
4014
+ [ Opcodes.i32_load16_s, 0, 4 ],
4015
+ Opcodes.i32_from
4016
+ ],
4017
+ [TYPES.uint32array]: [
4018
+ ...number(4, Valtype.i32),
4019
+ [ Opcodes.i32_mul ],
4020
+ [ Opcodes.i32_add ],
4021
+
4022
+ [ Opcodes.i32_load, 0, 4 ],
4023
+ Opcodes.i32_from_u
4024
+ ],
4025
+ [TYPES.int32array]: [
4026
+ ...number(4, Valtype.i32),
4027
+ [ Opcodes.i32_mul ],
4028
+ [ Opcodes.i32_add ],
4029
+
4030
+ [ Opcodes.i32_load, 0, 4 ],
4031
+ Opcodes.i32_from
4032
+ ],
4033
+ [TYPES.float32array]: [
4034
+ ...number(4, Valtype.i32),
4035
+ [ Opcodes.i32_mul ],
4036
+ [ Opcodes.i32_add ],
4037
+
4038
+ [ Opcodes.f32_load, 0, 4 ],
4039
+ [ Opcodes.f64_promote_f32 ]
4040
+ ],
4041
+ [TYPES.float64array]: [
4042
+ ...number(8, Valtype.i32),
4043
+ [ Opcodes.i32_mul ],
4044
+ [ Opcodes.i32_add ],
4045
+
4046
+ [ Opcodes.f64_load, 0, 4 ]
4047
+ ],
4048
+ }, {
4049
+ prelude: [
4050
+ ...object,
4051
+ Opcodes.i32_to_u,
4052
+ ...property,
4053
+ Opcodes.i32_to_u
4054
+ ],
4055
+ postlude: setLastType(scope, TYPES.number)
4056
+ }),
4057
+
3665
4058
  default: internalThrow(scope, 'TypeError', 'Member expression is not supported for non-string non-array yet', true)
3666
4059
  });
3667
4060
  };
3668
4061
 
3669
- const randId = () => Math.random().toString(16).slice(0, -4);
4062
+ const randId = () => Math.random().toString(16).slice(1, -2).padEnd(12, '0');
3670
4063
 
3671
4064
  const objectHack = node => {
3672
4065
  if (!node) return node;
@@ -3718,7 +4111,7 @@ const generateFunc = (scope, decl) => {
3718
4111
  if (decl.async) return todo(scope, 'async functions are not supported');
3719
4112
  if (decl.generator) return todo(scope, 'generator functions are not supported');
3720
4113
 
3721
- const name = decl.id ? decl.id.name : `anonymous_${randId()}`;
4114
+ const name = decl.id ? decl.id.name : `anonymous${randId()}`;
3722
4115
  const params = decl.params ?? [];
3723
4116
 
3724
4117
  // TODO: share scope/locals between !!!
@@ -3732,6 +4125,9 @@ const generateFunc = (scope, decl) => {
3732
4125
  index: currentFuncIndex++
3733
4126
  };
3734
4127
 
4128
+ funcIndex[name] = func.index;
4129
+ funcs.push(func);
4130
+
3735
4131
  if (typedInput && decl.returnType) {
3736
4132
  const { type } = extractTypeAnnotation(decl.returnType);
3737
4133
  // if (type != null && !Prefs.indirectCalls) {
@@ -3741,12 +4137,26 @@ const generateFunc = (scope, decl) => {
3741
4137
  }
3742
4138
  }
3743
4139
 
4140
+ const defaultValues = {};
3744
4141
  for (let i = 0; i < params.length; i++) {
3745
- const name = params[i].name;
4142
+ let name;
4143
+ const x = params[i];
4144
+ switch (x.type) {
4145
+ case 'Identifier': {
4146
+ name = x.name;
4147
+ break;
4148
+ }
4149
+
4150
+ case 'AssignmentPattern': {
4151
+ name = x.left.name;
4152
+ defaultValues[name] = x.right;
4153
+ break;
4154
+ }
4155
+ }
4156
+
3746
4157
  // if (name == null) return todo('non-identifier args are not supported');
3747
4158
 
3748
4159
  allocVar(func, name, false);
3749
-
3750
4160
  if (typedInput && params[i].typeAnnotation) {
3751
4161
  addVarMetadata(func, name, false, extractTypeAnnotation(params[i]));
3752
4162
  }
@@ -3763,11 +4173,22 @@ const generateFunc = (scope, decl) => {
3763
4173
  };
3764
4174
  }
3765
4175
 
3766
- funcIndex[name] = func.index;
3767
- funcs.push(func);
4176
+ const prelude = [];
4177
+ for (const x in defaultValues) {
4178
+ prelude.push(
4179
+ ...getType(func, x),
4180
+ ...number(TYPES.undefined, Valtype.i32),
4181
+ [ Opcodes.i32_eq ],
4182
+ [ Opcodes.if, Blocktype.void ],
4183
+ ...generate(func, defaultValues[x], false, x),
4184
+ [ Opcodes.local_set, func.locals[x].idx ],
3768
4185
 
3769
- const wasm = generate(func, body);
3770
- func.wasm = wasm;
4186
+ ...setType(func, x, getNodeType(scope, defaultValues[x])),
4187
+ [ Opcodes.end ]
4188
+ );
4189
+ }
4190
+
4191
+ const wasm = func.wasm = prelude.concat(generate(func, body));
3771
4192
 
3772
4193
  if (name === 'main') func.gotLastType = true;
3773
4194
 
@@ -3803,23 +4224,24 @@ const internalConstrs = {
3803
4224
 
3804
4225
  // new Array(n)
3805
4226
 
3806
- const [ , pointer ] = makeArray(scope, {
4227
+ const [ out, pointer ] = makeArray(scope, {
3807
4228
  rawElements: new Array(0)
3808
- }, global, name, true);
4229
+ }, global, name, true, undefined, true);
3809
4230
 
3810
4231
  const arg = decl.arguments[0] ?? DEFAULT_VALUE;
3811
4232
 
3812
4233
  // todo: check in wasm instead of here
3813
4234
  const literalValue = arg.value ?? 0;
3814
- if (literalValue < 0 || !Number.isFinite(literalValue) || literalValue > 4294967295) return internalThrow(scope, 'RangeThrow', 'Invalid array length', true);
4235
+ if (literalValue < 0 || !Number.isFinite(literalValue) || literalValue > 4294967295) return internalThrow(scope, 'RangeError', 'Invalid array length', true);
3815
4236
 
3816
4237
  return [
3817
- ...number(0, Valtype.i32),
4238
+ ...out,
3818
4239
  ...generate(scope, arg, global, name),
3819
4240
  Opcodes.i32_to_u,
3820
- [ Opcodes.i32_store, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(pointer) ],
4241
+ [ Opcodes.i32_store, Math.log2(ValtypeSize.i32) - 1, 0 ],
3821
4242
 
3822
- ...number(pointer)
4243
+ ...pointer,
4244
+ Opcodes.i32_from_u
3823
4245
  ];
3824
4246
  },
3825
4247
  type: TYPES.array,
@@ -3968,8 +4390,9 @@ const internalConstrs = {
3968
4390
  };
3969
4391
 
3970
4392
  export default program => {
3971
- globals = {};
3972
- globalInd = 0;
4393
+ globals = {
4394
+ ['#ind']: 0
4395
+ };
3973
4396
  tags = [];
3974
4397
  exceptions = [];
3975
4398
  funcs = [];
@@ -3979,19 +4402,8 @@ export default program => {
3979
4402
  data = [];
3980
4403
  currentFuncIndex = importedFuncs.length;
3981
4404
 
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
4405
  const valtypeInd = ['i32', 'i64', 'f64'].indexOf(valtype);
3990
4406
 
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
4407
  // set generic opcodes for current valtype
3996
4408
  Opcodes.const = [ Opcodes.i32_const, Opcodes.i64_const, Opcodes.f64_const ][valtypeInd];
3997
4409
  Opcodes.eq = [ Opcodes.i32_eq, Opcodes.i64_eq, Opcodes.f64_eq ][valtypeInd];
@@ -4013,6 +4425,7 @@ export default program => {
4013
4425
  builtinFuncs = new BuiltinFuncs();
4014
4426
  builtinVars = new BuiltinVars();
4015
4427
  prototypeFuncs = new PrototypeFuncs();
4428
+ allocator = makeAllocator(Prefs.allocator ?? 'static');
4016
4429
 
4017
4430
  program.id = { name: 'main' };
4018
4431
 
@@ -4055,6 +4468,8 @@ export default program => {
4055
4468
  else main.returns = [];
4056
4469
  }
4057
4470
 
4471
+ delete globals['#ind'];
4472
+
4058
4473
  // if blank main func and other exports, remove it
4059
4474
  if (main.wasm.length === 0 && funcs.reduce((acc, x) => acc + (x.export ? 1 : 0), 0) > 1) funcs.splice(main.index - importedFuncs.length, 1);
4060
4475