porffor 0.17.0-55999d22b → 0.17.0-6a05ca3ad

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.
@@ -4,7 +4,7 @@ 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';
@@ -72,6 +72,9 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
72
72
  case 'ExpressionStatement':
73
73
  return generateExp(scope, decl);
74
74
 
75
+ case 'SequenceExpression':
76
+ return generateSequence(scope, decl);
77
+
75
78
  case 'CallExpression':
76
79
  return generateCall(scope, decl, global, name, valueUnused);
77
80
 
@@ -120,6 +123,9 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
120
123
  case 'EmptyStatement':
121
124
  return generateEmpty(scope, decl);
122
125
 
126
+ case 'MetaProperty':
127
+ return generateMeta(scope, decl);
128
+
123
129
  case 'ConditionalExpression':
124
130
  return generateConditional(scope, decl);
125
131
 
@@ -264,10 +270,12 @@ const internalThrow = (scope, constructor, message, expectsValue = Prefs.alwaysV
264
270
  argument: {
265
271
  type: 'NewExpression',
266
272
  callee: {
273
+ type: 'Identifier',
267
274
  name: constructor
268
275
  },
269
276
  arguments: [
270
277
  {
278
+ type: 'Literal',
271
279
  value: message
272
280
  }
273
281
  ]
@@ -289,12 +297,13 @@ const generateIdent = (scope, decl) => {
289
297
  return wasm.slice();
290
298
  }
291
299
 
292
- if (Object.hasOwn(builtinFuncs, name) || Object.hasOwn(internalConstrs, name)) {
293
- // todo: return an actual something
294
- return number(1);
300
+ // todo: enable this by default in future
301
+ if (!Object.hasOwn(funcIndex, name) && Object.hasOwn(builtinFuncs, name)) {
302
+ includeBuiltin(scope, name);
303
+ return number(funcIndex[name] - importedFuncs.length);
295
304
  }
296
305
 
297
- if (isExistingProtoFunc(name)) {
306
+ if (isExistingProtoFunc(name) || Object.hasOwn(internalConstrs, name) || Object.hasOwn(builtinFuncs, name)) {
298
307
  // todo: return an actual something
299
308
  return number(1);
300
309
  }
@@ -613,11 +622,14 @@ const compareStrings = (scope, left, right, bytestrings = false) => {
613
622
  };
614
623
 
615
624
  const truthy = (scope, wasm, type, intIn = false, intOut = false, forceTruthyMode = undefined) => {
616
- // if (isIntToFloatOp(wasm[wasm.length - 1])) return [
617
- // ...wasm,
618
- // ...(!intIn && intOut ? [ Opcodes.i32_to_u ] : [])
619
- // ];
620
- // if (isIntOp(wasm[wasm.length - 1])) return [ ...wasm ];
625
+ if (isIntToFloatOp(wasm[wasm.length - 1])) return [
626
+ ...wasm,
627
+ ...(!intIn && intOut ? [ Opcodes.i32_to_u ] : [])
628
+ ];
629
+ if (isIntOp(wasm[wasm.length - 1])) return [
630
+ ...wasm,
631
+ ...(intOut ? [] : [ Opcodes.i32_from ]),
632
+ ];
621
633
 
622
634
  // todo/perf: use knownType and custom bytecode here instead of typeSwitch
623
635
 
@@ -973,6 +985,33 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
973
985
  };
974
986
 
975
987
  const generateBinaryExp = (scope, decl, _global, _name) => {
988
+ if (decl.operator === 'instanceof') {
989
+ // very hacky basic instanceof
990
+ // todo: support dynamic right-hand side
991
+
992
+ const out = generate(scope, decl.left);
993
+ disposeLeftover(out);
994
+
995
+ const rightName = decl.right.name;
996
+ if (!rightName) return todo(scope, 'instanceof dynamic right-hand side is not supported yet', true);
997
+
998
+ const checkType = TYPES[rightName.toLowerCase()];
999
+ if (checkType == null || rightName !== TYPE_NAMES[checkType] || checkType === TYPES.undefined) return todo(scope, 'instanceof right-hand side type unsupported', true);
1000
+
1001
+ if ([TYPES.number, TYPES.boolean, TYPES.string, TYPES.symbol, TYPES.object].includes(checkType)) {
1002
+ out.push(...number(0));
1003
+ } else {
1004
+ out.push(
1005
+ ...getNodeType(scope, decl.left),
1006
+ ...number(checkType, Valtype.i32),
1007
+ [ Opcodes.i32_eq ],
1008
+ Opcodes.i32_from_u
1009
+ );
1010
+ }
1011
+
1012
+ return out;
1013
+ }
1014
+
976
1015
  const out = performOp(scope, decl.operator, generate(scope, decl.left), generate(scope, decl.right), getNodeType(scope, decl.left), getNodeType(scope, decl.right), _global, _name);
977
1016
 
978
1017
  if (valtype !== 'i32' && ['==', '===', '!=', '!==', '>', '>=', '<', '<='].includes(decl.operator)) out.push(Opcodes.i32_from_u);
@@ -990,12 +1029,13 @@ const asmFuncToAsm = (func, scope) => {
990
1029
  idx = funcIndex[n];
991
1030
  }
992
1031
 
1032
+ if (idx == null) throw new Error(`builtin('${n}') failed to find a func (inside ${scope.name})`);
993
1033
  return idx;
994
1034
  }
995
1035
  });
996
1036
  };
997
1037
 
998
- const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes = [], globalInits = [], returns, returnType, localNames = [], globalNames = [], data: _data = [], table = false }) => {
1038
+ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes = [], globalInits = [], returns, returnType, localNames = [], globalNames = [], data: _data = [], table = false, constr = false }) => {
999
1039
  const existing = funcs.find(x => x.name === name);
1000
1040
  if (existing) return existing;
1001
1041
 
@@ -1022,13 +1062,17 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
1022
1062
  returnType,
1023
1063
  internal: true,
1024
1064
  index: currentFuncIndex++,
1025
- table
1065
+ table,
1066
+ constr
1026
1067
  };
1027
1068
 
1028
1069
  funcs.push(func);
1029
1070
  funcIndex[name] = func.index;
1030
1071
 
1031
- if (typeof wasm === 'function') wasm = asmFuncToAsm(wasm, func);
1072
+ if (typeof wasm === 'function') {
1073
+ if (globalThis.precompile) wasm = [];
1074
+ else wasm = asmFuncToAsm(wasm, func);
1075
+ }
1032
1076
 
1033
1077
  let baseGlobalIdx, i = 0;
1034
1078
  for (const type of globalTypes) {
@@ -1049,15 +1093,35 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
1049
1093
 
1050
1094
  if (table) {
1051
1095
  for (const inst of wasm) {
1052
- if (inst[0] === Opcodes.i32_load16_u && inst.at(-1) === 'read_argc') {
1096
+ if (inst.at(-1) === 'read func lut') {
1053
1097
  inst.splice(2, 99);
1054
- inst.push(...unsignedLEB128(allocPage({}, 'func argc lut') * pageSize));
1098
+ inst.push(...unsignedLEB128(allocPage({}, 'func lut') * pageSize));
1055
1099
  }
1056
1100
  }
1057
1101
 
1058
1102
  funcs.table = true;
1059
1103
  }
1060
1104
 
1105
+ if (constr) {
1106
+ func.params = [...func.params];
1107
+ func.params.unshift(Valtype.i32);
1108
+
1109
+ // move all locals +1 idx (sigh)
1110
+ func.localInd++;
1111
+ const locals = func.locals;
1112
+ for (const x in locals) {
1113
+ locals[x].idx++;
1114
+ }
1115
+
1116
+ locals['#newtarget'] = { idx: 0, type: Valtype.i32 };
1117
+
1118
+ for (const inst of wasm) {
1119
+ if (inst[0] === Opcodes.local_get || inst[0] === Opcodes.local_set || inst[0] === Opcodes.local_tee) {
1120
+ inst[1]++;
1121
+ }
1122
+ }
1123
+ }
1124
+
1061
1125
  func.wasm = wasm;
1062
1126
 
1063
1127
  return func;
@@ -1184,20 +1248,7 @@ const getNodeType = (scope, node) => {
1184
1248
  return TYPES.number;
1185
1249
  }
1186
1250
 
1187
- if (node.type === 'NewExpression' && builtinFuncs[name + '$constructor']) {
1188
- if (builtinFuncs[name + '$constructor'].typedReturns) {
1189
- if (scope.locals['#last_type']) return getLastType(scope);
1190
-
1191
- // presume
1192
- // todo: warn here?
1193
- return TYPES.number;
1194
- }
1195
-
1196
- return builtinFuncs[name + '$constructor'].returnType ?? TYPES.number;
1197
- }
1198
-
1199
1251
  const func = funcs.find(x => x.name === name);
1200
-
1201
1252
  if (func) {
1202
1253
  if (func.returnType != null) return func.returnType;
1203
1254
  }
@@ -1279,7 +1330,7 @@ const getNodeType = (scope, node) => {
1279
1330
  }
1280
1331
 
1281
1332
  if (node.type === 'BinaryExpression') {
1282
- if (['==', '===', '!=', '!==', '>', '>=', '<', '<='].includes(node.operator)) return TYPES.boolean;
1333
+ if (['==', '===', '!=', '!==', '>', '>=', '<', '<=', 'instanceof'].includes(node.operator)) return TYPES.boolean;
1283
1334
  if (node.operator !== '+') return TYPES.number;
1284
1335
 
1285
1336
  const knownLeft = knownType(scope, getNodeType(scope, node.left));
@@ -1312,7 +1363,7 @@ const getNodeType = (scope, node) => {
1312
1363
  const objectKnownType = knownType(scope, getNodeType(scope, node.object));
1313
1364
  if (objectKnownType != null) {
1314
1365
  if (name === 'length') {
1315
- if ([ TYPES.string, TYPES.bytestring, TYPES.array ].includes(objectKnownType)) return TYPES.number;
1366
+ if (typeHasFlag(objectKnownType, TYPE_FLAGS.length)) return TYPES.number;
1316
1367
  else return TYPES.undefined;
1317
1368
  }
1318
1369
 
@@ -1384,9 +1435,9 @@ const countLeftover = wasm => {
1384
1435
 
1385
1436
  if (depth === 0)
1386
1437
  if ([Opcodes.throw, Opcodes.drop, Opcodes.local_set, Opcodes.global_set].includes(inst[0])) count--;
1387
- else if ([null, Opcodes.i32_eqz, Opcodes.i64_eqz, Opcodes.f64_ceil, Opcodes.f64_floor, Opcodes.f64_trunc, Opcodes.f64_nearest, Opcodes.f64_sqrt, Opcodes.local_tee, Opcodes.i32_wrap_i64, Opcodes.i64_extend_i32_s, Opcodes.i64_extend_i32_u, Opcodes.f32_demote_f64, Opcodes.f64_promote_f32, Opcodes.f64_convert_i32_s, Opcodes.f64_convert_i32_u, Opcodes.i32_clz, Opcodes.i32_ctz, Opcodes.i32_popcnt, Opcodes.f64_neg, Opcodes.end, Opcodes.i32_trunc_sat_f64_s[0], Opcodes.i32x4_extract_lane, Opcodes.i16x8_extract_lane, Opcodes.i32_load, Opcodes.i64_load, Opcodes.f64_load, Opcodes.v128_load, Opcodes.i32_load16_u, Opcodes.i32_load16_s, Opcodes.i32_load8_u, Opcodes.i32_load8_s, Opcodes.memory_grow].includes(inst[0]) && (inst[0] !== 0xfc || inst[1] < 0x04)) {}
1438
+ 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)) {}
1388
1439
  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++;
1389
- else if ([Opcodes.i32_store, Opcodes.i64_store, Opcodes.f64_store, Opcodes.i32_store16, Opcodes.i32_store8].includes(inst[0])) count -= 2;
1440
+ 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;
1390
1441
  else if (inst[0] === Opcodes.memory_copy[0] && (inst[1] === Opcodes.memory_copy[1] || inst[1] === Opcodes.memory_init[1])) count -= 3;
1391
1442
  else if (inst[0] === Opcodes.return) count = 0;
1392
1443
  else if (inst[0] === Opcodes.call) {
@@ -1424,6 +1475,18 @@ const generateExp = (scope, decl) => {
1424
1475
  return out;
1425
1476
  };
1426
1477
 
1478
+ const generateSequence = (scope, decl) => {
1479
+ let out = [];
1480
+
1481
+ const exprs = decl.expressions;
1482
+ for (let i = 0; i < exprs.length; i++) {
1483
+ if (i > 0) disposeLeftover(out);
1484
+ out.push(...generate(scope, exprs[i]));
1485
+ }
1486
+
1487
+ return out;
1488
+ };
1489
+
1427
1490
  const CTArrayUtil = {
1428
1491
  getLengthI32: pointer => [
1429
1492
  ...number(0, Valtype.i32),
@@ -1483,7 +1546,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1483
1546
  name = func.name;
1484
1547
  }
1485
1548
 
1486
- if (name === 'eval' && decl.arguments[0]?.type === 'Literal') {
1549
+ if (!decl._new && name === 'eval' && decl.arguments[0]?.type === 'Literal') {
1487
1550
  // literal eval hack
1488
1551
  const code = decl.arguments[0]?.value ?? '';
1489
1552
 
@@ -1526,7 +1589,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1526
1589
 
1527
1590
  let protoName, target;
1528
1591
  // ident.func()
1529
- if (name && name.startsWith('__')) {
1592
+ if (!decl._new && name && name.startsWith('__')) {
1530
1593
  const spl = name.slice(2).split('_');
1531
1594
 
1532
1595
  protoName = spl[spl.length - 1];
@@ -1539,12 +1602,12 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1539
1602
  }
1540
1603
 
1541
1604
  // literal.func()
1542
- if (!name && decl.callee.type === 'MemberExpression') {
1605
+ if (!decl._new && !name && decl.callee.type === 'MemberExpression') {
1543
1606
  // megahack for /regex/.func()
1544
1607
  const funcName = decl.callee.property.name;
1545
- if (decl.callee.object.regex && Object.hasOwn(Rhemyn, funcName)) {
1608
+ if (decl.callee.object.regex && ['test'].includes(funcName)) {
1546
1609
  const regex = decl.callee.object.regex.pattern;
1547
- const rhemynName = `regex_${funcName}_${regex}`;
1610
+ const rhemynName = `regex_${funcName}_${sanitize(regex)}`;
1548
1611
 
1549
1612
  if (!funcIndex[rhemynName]) {
1550
1613
  const func = Rhemyn[funcName](regex, currentFuncIndex++, rhemynName);
@@ -1565,7 +1628,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1565
1628
  [ Opcodes.call, idx ],
1566
1629
  Opcodes.i32_from_u,
1567
1630
 
1568
- ...setLastType(scope, TYPES.boolean)
1631
+ ...setLastType(scope, Rhemyn.types[funcName])
1569
1632
  ];
1570
1633
  }
1571
1634
 
@@ -1574,27 +1637,56 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1574
1637
  target = decl.callee.object;
1575
1638
  }
1576
1639
 
1577
- // if (protoName && baseType === TYPES.string && Rhemyn[protoName]) {
1578
- // const func = Rhemyn[protoName](decl.arguments[0].regex.pattern, currentFuncIndex++);
1640
+ let out = [];
1641
+ if (protoName) {
1642
+ if (['search'].includes(protoName)) {
1643
+ const regex = decl.arguments[0]?.regex?.pattern;
1644
+ if (!regex) return [
1645
+ // no/bad regex arg, return -1/0 for now
1646
+ ...generate(scope, target),
1647
+ [ Opcodes.drop ],
1579
1648
 
1580
- // funcIndex[func.name] = func.index;
1581
- // funcs.push(func);
1649
+ ...number(Rhemyn.types[protoName] === TYPES.number ? -1 : 0),
1650
+ ...setLastType(scope, Rhemyn.types[protoName])
1651
+ ];
1582
1652
 
1583
- // return [
1584
- // generate(scope, decl.callee.object)
1653
+ const rhemynName = `regex_${protoName}_${sanitize(regex)}`;
1585
1654
 
1586
- // // call regex func
1587
- // [ Opcodes.call, func.index ],
1588
- // Opcodes.i32_from_u
1589
- // ];
1590
- // }
1655
+ if (!funcIndex[rhemynName]) {
1656
+ const func = Rhemyn[protoName](regex, currentFuncIndex++, rhemynName);
1657
+ func.internal = true;
1591
1658
 
1592
- if (protoName) {
1593
- const protoBC = {};
1659
+ funcIndex[func.name] = func.index;
1660
+ funcs.push(func);
1661
+ }
1662
+
1663
+ const idx = funcIndex[rhemynName];
1664
+ return [
1665
+ // make string arg
1666
+ ...generate(scope, target),
1667
+ Opcodes.i32_to_u,
1668
+ ...getNodeType(scope, target),
1669
+
1670
+ // call regex func
1671
+ [ Opcodes.call, idx ],
1672
+ Opcodes.i32_from,
1594
1673
 
1674
+ ...setLastType(scope, Rhemyn.types[protoName])
1675
+ ];
1676
+ }
1677
+
1678
+ const protoBC = {};
1595
1679
  const builtinProtoCands = Object.keys(builtinFuncs).filter(x => x.startsWith('__') && x.endsWith('_prototype_' + protoName));
1596
1680
 
1597
1681
  if (!decl._protoInternalCall && builtinProtoCands.length > 0) {
1682
+ out.push(
1683
+ ...generate(scope, target),
1684
+ [ Opcodes.local_set, localTmp(scope, '#proto_target') ],
1685
+
1686
+ ...getNodeType(scope, target),
1687
+ [ Opcodes.local_set, localTmp(scope, '#proto_target#type', Valtype.i32) ],
1688
+ );
1689
+
1598
1690
  for (const x of builtinProtoCands) {
1599
1691
  const type = TYPES[x.split('_prototype_')[0].slice(2).toLowerCase()];
1600
1692
  if (type == null) continue;
@@ -1604,7 +1696,14 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1604
1696
  type: 'Identifier',
1605
1697
  name: x
1606
1698
  },
1607
- arguments: [ target, ...decl.arguments ],
1699
+ arguments: [
1700
+ {
1701
+ type: 'Identifier',
1702
+ name: '#proto_target'
1703
+ },
1704
+
1705
+ ...decl.arguments
1706
+ ],
1608
1707
  _protoInternalCall: true
1609
1708
  });
1610
1709
  }
@@ -1698,29 +1797,37 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1698
1797
  }
1699
1798
 
1700
1799
  if (Object.keys(protoBC).length > 0) {
1701
- return typeSwitch(scope, getNodeType(scope, target), {
1702
- ...protoBC,
1800
+ return [
1801
+ ...out,
1802
+
1803
+ ...typeSwitch(scope, builtinProtoCands.length > 0 ? [ [ Opcodes.local_get, localTmp(scope, '#proto_target#type', Valtype.i32) ] ] : getNodeType(scope, target), {
1804
+ ...protoBC,
1703
1805
 
1704
- // TODO: error better
1705
- default: internalThrow(scope, 'TypeError', `'${protoName}' proto func tried to be called on a type without an impl`)
1706
- }, valtypeBinary);
1806
+ // TODO: error better
1807
+ default: internalThrow(scope, 'TypeError', `'${protoName}' proto func tried to be called on a type without an impl`)
1808
+ }, valtypeBinary)
1809
+ ];
1707
1810
  }
1708
1811
  }
1709
1812
 
1710
- // TODO: only allows callee as literal
1711
- if (!name) return todo(scope, `only literal callees (got ${decl.callee.type})`);
1813
+ // TODO: only allows callee as identifier
1814
+ if (!name) return todo(scope, `only identifier callees (got ${decl.callee.type})`);
1712
1815
 
1713
1816
  let idx = funcIndex[name] ?? importedFuncs[name];
1714
1817
  if (idx === undefined && builtinFuncs[name]) {
1715
1818
  if (builtinFuncs[name].floatOnly && valtype !== 'f64') throw new Error(`Cannot use built-in ${unhackName(name)} with integer valtype`);
1819
+ if (decl._new && !builtinFuncs[name].constr) return internalThrow(scope, 'TypeError', `${unhackName(name)} is not a constructor`, true);
1716
1820
 
1717
1821
  includeBuiltin(scope, name);
1718
1822
  idx = funcIndex[name];
1719
1823
  }
1720
1824
 
1721
- if (idx === undefined && internalConstrs[name]) return internalConstrs[name].generate(scope, decl, _global, _name);
1825
+ if (idx === undefined && internalConstrs[name]) {
1826
+ if (decl._new && internalConstrs[name].notConstr) return internalThrow(scope, 'TypeError', `${unhackName(name)} is not a constructor`, true);
1827
+ return internalConstrs[name].generate(scope, decl, _global, _name);
1828
+ }
1722
1829
 
1723
- if (idx === undefined && name.startsWith('__Porffor_wasm_')) {
1830
+ if (idx === undefined && !decl._new && name.startsWith('__Porffor_wasm_')) {
1724
1831
  const wasmOps = {
1725
1832
  // pointer, align, offset
1726
1833
  i32_load: { imms: 2, args: [ true ], returns: 1 },
@@ -1775,7 +1882,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1775
1882
 
1776
1883
  const indirectMode = Prefs.indirectCallMode ?? 'vararg';
1777
1884
  // options: vararg, strict
1778
- // - strict: simpler, smaller size usage, no func argc lut needed.
1885
+ // - strict: simpler, smaller size usage, no func lut needed.
1779
1886
  // ONLY works when arg count of call == arg count of function being called
1780
1887
  // - vararg: large size usage, cursed.
1781
1888
  // works when arg count of call != arg count of function being called*
@@ -1785,8 +1892,6 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1785
1892
  scope.table = true;
1786
1893
 
1787
1894
  let args = decl.arguments;
1788
- let out = [];
1789
-
1790
1895
  let locals = [];
1791
1896
 
1792
1897
  if (indirectMode === 'vararg') {
@@ -1850,24 +1955,67 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1850
1955
  // *for argc 0-3, in future (todo:) the max number should be
1851
1956
  // dynamically changed to the max argc of any func in the js file.
1852
1957
 
1853
- const funcLocal = localTmp(scope, `#indirect_func`, Valtype.i32);
1958
+ const funcLocal = localTmp(scope, '#indirect_func', Valtype.i32);
1959
+ const flags = localTmp(scope, '#indirect_flags', Valtype.i32);
1854
1960
 
1855
1961
  const gen = argc => {
1856
- const out = [];
1962
+ const argsOut = [];
1857
1963
  for (let i = 0; i < argc; i++) {
1858
- out.push(
1964
+ argsOut.push(
1859
1965
  [ Opcodes.local_get, locals[i][0] ],
1860
1966
  [ Opcodes.local_get, locals[i][1] ]
1861
1967
  );
1862
1968
  }
1863
1969
 
1864
- out.push(
1865
- [ Opcodes.local_get, funcLocal ],
1866
- [ Opcodes.call_indirect, argc, 0 ],
1867
- ...setLastType(scope)
1868
- )
1970
+ const checkFlag = (flag, pass, fail) => [
1971
+ [ Opcodes.local_get, flags ],
1972
+ ...number(flag, Valtype.i32),
1973
+ [ Opcodes.i32_and ],
1974
+ [ Opcodes.if, valtypeBinary ],
1975
+ ...pass,
1976
+ [ Opcodes.else ],
1977
+ ...fail,
1978
+ [ Opcodes.end ]
1979
+ ];
1869
1980
 
1870
- return out;
1981
+ // pain.
1982
+ // return checkFlag(0b10, [ // constr
1983
+ // [ Opcodes.i32_const, decl._new ? 1 : 0 ],
1984
+ // ...argsOut,
1985
+ // [ Opcodes.local_get, funcLocal ],
1986
+ // [ Opcodes.call_indirect, argc, 0, 'constr' ],
1987
+ // ...setLastType(scope),
1988
+ // ], [
1989
+ // ...argsOut,
1990
+ // [ Opcodes.local_get, funcLocal ],
1991
+ // [ Opcodes.call_indirect, argc, 0 ],
1992
+ // ...setLastType(scope),
1993
+ // ]);
1994
+
1995
+ return checkFlag(0b1, // no type return
1996
+ checkFlag(0b10, [ // no type return & constr
1997
+ [ Opcodes.i32_const, decl._new ? 1 : 0 ],
1998
+ ...argsOut,
1999
+ [ Opcodes.local_get, funcLocal ],
2000
+ [ Opcodes.call_indirect, argc, 0, 'no_type_return', 'constr' ],
2001
+ ], [
2002
+ ...argsOut,
2003
+ [ Opcodes.local_get, funcLocal ],
2004
+ [ Opcodes.call_indirect, argc, 0, 'no_type_return' ]
2005
+ ]),
2006
+ checkFlag(0b10, [ // type return & constr
2007
+ [ Opcodes.i32_const, decl._new ? 1 : 0 ],
2008
+ ...argsOut,
2009
+ [ Opcodes.local_get, funcLocal ],
2010
+ [ Opcodes.call_indirect, argc, 0, 'constr' ],
2011
+ ...setLastType(scope),
2012
+ ], [
2013
+ ...argsOut,
2014
+ [ Opcodes.local_get, funcLocal ],
2015
+ [ Opcodes.call_indirect, argc, 0 ],
2016
+ ...setLastType(scope),
2017
+ ]),
2018
+ );
1871
2019
  };
1872
2020
 
1873
2021
  const tableBc = {};
@@ -1885,13 +2033,32 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1885
2033
  Opcodes.i32_to_u,
1886
2034
  [ Opcodes.local_set, funcLocal ],
1887
2035
 
2036
+ // get if func we are calling is a constructor or not
2037
+ [ Opcodes.local_get, funcLocal ],
2038
+ ...number(3, Valtype.i32),
2039
+ [ Opcodes.i32_mul ],
2040
+ ...number(2, Valtype.i32),
2041
+ [ Opcodes.i32_add ],
2042
+ [ Opcodes.i32_load8_u, 0, ...unsignedLEB128(allocPage(scope, 'func lut') * pageSize), 'read func lut' ],
2043
+ [ Opcodes.local_set, flags ],
2044
+
2045
+ // check if non-constructor was called with new, if so throw
2046
+ [ Opcodes.local_get, flags ],
2047
+ ...number(0b10, Valtype.i32),
2048
+ [ Opcodes.i32_and ],
2049
+ [ Opcodes.i32_eqz ],
2050
+ [ Opcodes.i32_const, decl._new ? 1 : 0 ],
2051
+ [ Opcodes.i32_and ],
2052
+ [ Opcodes.if, Blocktype.void ],
2053
+ ...internalThrow(scope, 'TypeError', `${unhackName(name)} is not a constructor`),
2054
+ [ Opcodes.end ],
2055
+
1888
2056
  ...brTable([
1889
2057
  // get argc of func we are calling
1890
2058
  [ Opcodes.local_get, funcLocal ],
1891
- ...number(ValtypeSize.i16, Valtype.i32),
2059
+ ...number(3, Valtype.i32),
1892
2060
  [ Opcodes.i32_mul ],
1893
-
1894
- [ Opcodes.i32_load16_u, 0, ...unsignedLEB128(allocPage(scope, 'func argc lut') * pageSize), 'read_argc' ]
2061
+ [ Opcodes.i32_load16_u, 0, ...unsignedLEB128(allocPage(scope, 'func lut') * pageSize), 'read func lut' ]
1895
2062
  ], tableBc, valtypeBinary)
1896
2063
  ],
1897
2064
  default: internalThrow(scope, 'TypeError', `${unhackName(name)} is not a function`, true)
@@ -1905,7 +2072,16 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1905
2072
  const userFunc = func && !func.internal;
1906
2073
  const typedParams = userFunc || builtinFuncs[name]?.typedParams;
1907
2074
  const typedReturns = (userFunc && func.returnType == null) || builtinFuncs[name]?.typedReturns;
1908
- const paramCount = func && (typedParams ? func.params.length / 2 : func.params.length);
2075
+ let paramCount = func && (typedParams ? Math.floor(func.params.length / 2) : func.params.length);
2076
+
2077
+ let paramOffset = 0;
2078
+ if (func && func.constr) {
2079
+ // new.target arg
2080
+ if (func.internal) paramOffset = 1;
2081
+ if (!typedParams) paramCount--;
2082
+ out.push([ Opcodes.i32_const, decl._new ? 1 : 0 ]);
2083
+ } else if (decl._new)
2084
+ return internalThrow(scope, 'TypeError', `${unhackName(name)} is not a constructor`, true);
1909
2085
 
1910
2086
  let args = decl.arguments;
1911
2087
  if (func && args.length < paramCount) {
@@ -1920,7 +2096,6 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1920
2096
 
1921
2097
  if (func && func.throws) scope.throws = true;
1922
2098
 
1923
- let out = [];
1924
2099
  for (let i = 0; i < args.length; i++) {
1925
2100
  const arg = args[i];
1926
2101
  out = out.concat(generate(scope, arg));
@@ -1933,13 +2108,13 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1933
2108
  }
1934
2109
 
1935
2110
  if (valtypeBinary !== Valtype.i32 &&
1936
- (func && func.params[i * (typedParams ? 2 : 1)] === Valtype.i32)
2111
+ (func && func.params[paramOffset + i * (typedParams ? 2 : 1)] === Valtype.i32)
1937
2112
  ) {
1938
2113
  out.push(Opcodes.i32_to);
1939
2114
  }
1940
2115
 
1941
2116
  if (valtypeBinary === Valtype.i32 &&
1942
- (func && func.params[i * (typedParams ? 2 : 1)] === Valtype.f64)
2117
+ (func && func.params[paramOffset + i * (typedParams ? 2 : 1)] === Valtype.f64)
1943
2118
  ) {
1944
2119
  out.push([ Opcodes.f64_convert_i32_s ]);
1945
2120
  }
@@ -1972,32 +2147,10 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1972
2147
  return out;
1973
2148
  };
1974
2149
 
1975
- const generateNew = (scope, decl, _global, _name) => {
1976
- // hack: basically treat this as a normal call for builtins for now
1977
- const name = mapName(decl.callee.name);
1978
-
1979
- if (internalConstrs[name] && !internalConstrs[name].notConstr) return internalConstrs[name].generate(scope, decl, _global, _name);
1980
-
1981
- if (builtinFuncs[name + '$constructor']) {
1982
- // custom ...$constructor override builtin func
1983
- return generateCall(scope, {
1984
- ...decl,
1985
- callee: {
1986
- type: 'Identifier',
1987
- name: name + '$constructor'
1988
- }
1989
- }, _global, _name);
1990
- }
1991
-
1992
- if (
1993
- (builtinFuncs[name] && !builtinFuncs[name].constr) ||
1994
- (internalConstrs[name] && builtinFuncs[name].notConstr)
1995
- ) return internalThrow(scope, 'TypeError', `${name} is not a constructor`, true);
1996
-
1997
- if (!builtinFuncs[name]) return todo(scope, `new statement is not supported yet`, true); // return todo(scope, `new statement is not supported yet (new ${unhackName(name)})`);
1998
-
1999
- return generateCall(scope, decl, _global, _name);
2000
- };
2150
+ const generateNew = (scope, decl, _global, _name) => generateCall(scope, {
2151
+ ...decl,
2152
+ _new: true
2153
+ }, _global, _name);
2001
2154
 
2002
2155
  // bad hack for undefined and null working without additional logic
2003
2156
  const DEFAULT_VALUE = {
@@ -2005,6 +2158,16 @@ const DEFAULT_VALUE = {
2005
2158
  name: 'undefined'
2006
2159
  };
2007
2160
 
2161
+ const codeToSanitizedStr = code => {
2162
+ let out = '';
2163
+ while (code > 0) {
2164
+ out += String.fromCharCode(97 + code % 26);
2165
+ code -= 26;
2166
+ }
2167
+ return out;
2168
+ };
2169
+ const sanitize = str => str.replace(/[^0-9a-zA-Z_]/g, _ => codeToSanitizedStr(_.charCodeAt(0)));
2170
+
2008
2171
  const unhackName = name => {
2009
2172
  if (name.startsWith('__')) return name.slice(2).replaceAll('_', '.');
2010
2173
  return name;
@@ -2012,7 +2175,7 @@ const unhackName = name => {
2012
2175
 
2013
2176
  const knownType = (scope, type) => {
2014
2177
  if (type.length === 1 && type[0][0] === Opcodes.i32_const) {
2015
- return type[0][1];
2178
+ return read_signedLEB128(type[0].slice(1));
2016
2179
  }
2017
2180
 
2018
2181
  if (typedInput && type.length === 1 && type[0][0] === Opcodes.local_get) {
@@ -2240,9 +2403,120 @@ const generateVar = (scope, decl) => {
2240
2403
  const global = topLevel || decl._bare;
2241
2404
 
2242
2405
  for (const x of decl.declarations) {
2243
- const name = mapName(x.id.name);
2406
+ if (x.id.type === 'ArrayPattern') {
2407
+ const decls = [];
2408
+ const tmpName = '#destructure' + randId();
2409
+
2410
+ let i = 0;
2411
+ const elements = [...x.id.elements];
2412
+ for (const e of elements) {
2413
+ switch (e?.type) {
2414
+ case 'RestElement': { // let [ ...foo ] = []
2415
+ if (e.argument.type === 'ArrayPattern') {
2416
+ // let [ ...[a, b, c] ] = []
2417
+ elements.push(...e.argument.elements);
2418
+ } else {
2419
+ decls.push({
2420
+ type: 'VariableDeclarator',
2421
+ id: { type: 'Identifier', name: e.argument.name },
2422
+ init: {
2423
+ type: 'CallExpression',
2424
+ callee: {
2425
+ type: 'Identifier',
2426
+ name: '__Array_prototype_slice'
2427
+ },
2428
+ arguments: [
2429
+ { type: 'Identifier', name: tmpName },
2430
+ { type: 'Literal', value: i },
2431
+ {
2432
+ type: 'MemberExpression',
2433
+ object: { type: 'Identifier', name: tmpName, },
2434
+ property: { type: 'Identifier', name: 'length', }
2435
+ }
2436
+ ]
2437
+ }
2438
+ });
2439
+ }
2440
+
2441
+ continue; // skip i++
2442
+ }
2443
+
2444
+ case 'Identifier': { // let [ foo ] = []
2445
+ decls.push({
2446
+ type: 'VariableDeclarator',
2447
+ id: e,
2448
+ init: {
2449
+ type: 'MemberExpression',
2450
+ object: { type: 'Identifier', name: tmpName },
2451
+ property: { type: 'Literal', value: i }
2452
+ }
2453
+ });
2454
+
2455
+ break;
2456
+ }
2457
+
2458
+ case 'AssignmentPattern': { // let [ foo = defaultValue ] = []
2459
+ decls.push({
2460
+ type: 'VariableDeclarator',
2461
+ id: e.left,
2462
+ init: {
2463
+ type: 'LogicalExpression',
2464
+ operator: '??',
2465
+ left: {
2466
+ type: 'MemberExpression',
2467
+ object: { type: 'Identifier', name: tmpName },
2468
+ property: { type: 'Literal', value: i }
2469
+ },
2470
+ right: e.right
2471
+ }
2472
+ });
2473
+
2474
+ break;
2475
+ }
2476
+
2477
+ case 'ArrayPattern': { // let [ [ foo, bar ] ] = []
2478
+ decls.push({
2479
+ type: 'VariableDeclarator',
2480
+ id: e,
2481
+ init: {
2482
+ type: 'MemberExpression',
2483
+ object: { type: 'Identifier', name: tmpName },
2484
+ property: { type: 'Literal', value: i }
2485
+ }
2486
+ });
2244
2487
 
2245
- if (!name) return todo(scope, 'destructuring is not supported yet');
2488
+ break;
2489
+ }
2490
+
2491
+ case 'ObjectPattern':
2492
+ return todo(scope, 'object destructuring is not supported yet')
2493
+ }
2494
+
2495
+ i++;
2496
+ }
2497
+
2498
+ out = out.concat([
2499
+ ...generateVar(scope, {
2500
+ type: 'VariableDeclaration',
2501
+ declarations: [{
2502
+ type: 'VariableDeclarator',
2503
+ id: { type: 'Identifier', name: tmpName },
2504
+ init: x.init
2505
+ }],
2506
+ kind: decl.kind
2507
+ }),
2508
+ ...generateVar(scope, {
2509
+ type: 'VariableDeclaration',
2510
+ declarations: decls,
2511
+ kind: decl.kind
2512
+ })
2513
+ ]);
2514
+
2515
+ continue;
2516
+ }
2517
+
2518
+ const name = mapName(x.id.name);
2519
+ if (!name) return todo(scope, 'object destructuring is not supported yet')
2246
2520
 
2247
2521
  if (x.init && isFuncType(x.init.type)) {
2248
2522
  // hack for let a = function () { ... }
@@ -2277,7 +2551,9 @@ const generateVar = (scope, decl) => {
2277
2551
  // hack to set local as pointer before
2278
2552
  out.push(...number(scope.arrays.get(name)), [ global ? Opcodes.global_set : Opcodes.local_set, idx ]);
2279
2553
  if (generated.at(-1) == Opcodes.i32_from_u) generated.pop();
2280
- generated.pop();
2554
+ // generated.pop();
2555
+ generated.push([ Opcodes.drop ]);
2556
+
2281
2557
  out = out.concat(generated);
2282
2558
  } else {
2283
2559
  out = out.concat(generated);
@@ -2299,11 +2575,6 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
2299
2575
  const { type, name } = decl.left;
2300
2576
  const [ local, isGlobal ] = lookupName(scope, name);
2301
2577
 
2302
- if (type === 'ObjectPattern') {
2303
- // hack: ignore object parts of `var a = {} = 2`
2304
- return generate(scope, decl.right);
2305
- }
2306
-
2307
2578
  if (isFuncType(decl.right.type)) {
2308
2579
  // hack for a = function () { ... }
2309
2580
  decl.right.id = { name };
@@ -2349,8 +2620,8 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
2349
2620
 
2350
2621
  // arr[i]
2351
2622
  if (decl.left.type === 'MemberExpression' && decl.left.computed) {
2352
- const newValueTmp = localTmp(scope, '__member_setter_val_tmp');
2353
- const pointerTmp = op === '=' ? -1 : localTmp(scope, '__member_setter_ptr_tmp', Valtype.i32);
2623
+ const newValueTmp = localTmp(scope, '#member_setter_val_tmp');
2624
+ const pointerTmp = localTmp(scope, '#member_setter_ptr_tmp', Valtype.i32);
2354
2625
 
2355
2626
  return [
2356
2627
  ...typeSwitch(scope, getNodeType(scope, decl.left.object), {
@@ -2362,11 +2633,11 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
2362
2633
  ...generate(scope, decl.left.property),
2363
2634
  Opcodes.i32_to_u,
2364
2635
 
2365
- // turn into byte offset by * valtypeSize (4 for i32, 8 for i64/f64)
2636
+ // turn into byte offset by * valtypeSize + 1
2366
2637
  ...number(ValtypeSize[valtype] + 1, Valtype.i32),
2367
2638
  [ Opcodes.i32_mul ],
2368
2639
  [ Opcodes.i32_add ],
2369
- ...(op === '=' ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
2640
+ [ Opcodes.local_tee, pointerTmp ],
2370
2641
 
2371
2642
  ...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
2372
2643
  [ Opcodes.local_get, pointerTmp ],
@@ -2376,10 +2647,164 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
2376
2647
  [ Opcodes.i32_load8_u, 0, ValtypeSize.i32 + ValtypeSize[valtype] ]
2377
2648
  ], getNodeType(scope, decl.right), false, name, true)),
2378
2649
  [ Opcodes.local_tee, newValueTmp ],
2650
+ [ Opcodes.store, 0, ValtypeSize.i32 ],
2379
2651
 
2380
- [ Opcodes.store, 0, ValtypeSize.i32 ]
2652
+ [ Opcodes.local_get, pointerTmp ],
2653
+ ...getNodeType(scope, decl),
2654
+ [ Opcodes.i32_store8, 0, ValtypeSize.i32 + ValtypeSize[valtype] ],
2381
2655
  ],
2382
2656
 
2657
+ ...wrapBC({
2658
+ [TYPES.uint8array]: [
2659
+ [ Opcodes.i32_add ],
2660
+ ...(op === '=' ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
2661
+
2662
+ ...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
2663
+ [ Opcodes.local_get, pointerTmp ],
2664
+ [ Opcodes.i32_load8_u, 0, 4 ],
2665
+ Opcodes.i32_from_u
2666
+ ], generate(scope, decl.right), number(TYPES.number, Valtype.i32), getNodeType(scope, decl.right), false, name, true)),
2667
+ [ Opcodes.local_tee, newValueTmp ],
2668
+
2669
+ Opcodes.i32_to_u,
2670
+ [ Opcodes.i32_store8, 0, 4 ]
2671
+ ],
2672
+ [TYPES.uint8clampedarray]: [
2673
+ [ Opcodes.i32_add ],
2674
+ ...(op === '=' ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
2675
+
2676
+ ...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
2677
+ [ Opcodes.local_get, pointerTmp ],
2678
+ [ Opcodes.i32_load8_u, 0, 4 ],
2679
+ Opcodes.i32_from_u
2680
+ ], generate(scope, decl.right), number(TYPES.number, Valtype.i32), getNodeType(scope, decl.right), false, name, true)),
2681
+ [ Opcodes.local_tee, newValueTmp ],
2682
+
2683
+ ...number(0),
2684
+ [ Opcodes.f64_max ],
2685
+ ...number(255),
2686
+ [ Opcodes.f64_min ],
2687
+ Opcodes.i32_to_u,
2688
+ [ Opcodes.i32_store8, 0, 4 ]
2689
+ ],
2690
+ [TYPES.int8array]: [
2691
+ [ Opcodes.i32_add ],
2692
+ ...(op === '=' ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
2693
+
2694
+ ...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
2695
+ [ Opcodes.local_get, pointerTmp ],
2696
+ [ Opcodes.i32_load8_s, 0, 4 ],
2697
+ Opcodes.i32_from
2698
+ ], generate(scope, decl.right), number(TYPES.number, Valtype.i32), getNodeType(scope, decl.right), false, name, true)),
2699
+ [ Opcodes.local_tee, newValueTmp ],
2700
+
2701
+ Opcodes.i32_to,
2702
+ [ Opcodes.i32_store8, 0, 4 ]
2703
+ ],
2704
+ [TYPES.uint16array]: [
2705
+ ...number(2, Valtype.i32),
2706
+ [ Opcodes.i32_mul ],
2707
+ [ Opcodes.i32_add ],
2708
+ ...(op === '=' ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
2709
+
2710
+ ...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
2711
+ [ Opcodes.local_get, pointerTmp ],
2712
+ [ Opcodes.i32_load16_u, 0, 4 ],
2713
+ Opcodes.i32_from_u
2714
+ ], generate(scope, decl.right), number(TYPES.number, Valtype.i32), getNodeType(scope, decl.right), false, name, true)),
2715
+ [ Opcodes.local_tee, newValueTmp ],
2716
+
2717
+ Opcodes.i32_to_u,
2718
+ [ Opcodes.i32_store16, 0, 4 ]
2719
+ ],
2720
+ [TYPES.int16array]: [
2721
+ ...number(2, Valtype.i32),
2722
+ [ Opcodes.i32_mul ],
2723
+ [ Opcodes.i32_add ],
2724
+ ...(op === '=' ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
2725
+
2726
+ ...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
2727
+ [ Opcodes.local_get, pointerTmp ],
2728
+ [ Opcodes.i32_load16_s, 0, 4 ],
2729
+ Opcodes.i32_from
2730
+ ], generate(scope, decl.right), number(TYPES.number, Valtype.i32), getNodeType(scope, decl.right), false, name, true)),
2731
+ [ Opcodes.local_tee, newValueTmp ],
2732
+
2733
+ Opcodes.i32_to,
2734
+ [ Opcodes.i32_store16, 0, 4 ]
2735
+ ],
2736
+ [TYPES.uint32array]: [
2737
+ ...number(4, Valtype.i32),
2738
+ [ Opcodes.i32_mul ],
2739
+ [ Opcodes.i32_add ],
2740
+ ...(op === '=' ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
2741
+
2742
+ ...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
2743
+ [ Opcodes.local_get, pointerTmp ],
2744
+ [ Opcodes.i32_load, 0, 4 ],
2745
+ Opcodes.i32_from_u
2746
+ ], generate(scope, decl.right), number(TYPES.number, Valtype.i32), getNodeType(scope, decl.right), false, name, true)),
2747
+ [ Opcodes.local_tee, newValueTmp ],
2748
+
2749
+ Opcodes.i32_to_u,
2750
+ [ Opcodes.i32_store, 0, 4 ]
2751
+ ],
2752
+ [TYPES.int32array]: [
2753
+ ...number(4, Valtype.i32),
2754
+ [ Opcodes.i32_mul ],
2755
+ [ Opcodes.i32_add ],
2756
+ ...(op === '=' ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
2757
+
2758
+ ...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
2759
+ [ Opcodes.local_get, pointerTmp ],
2760
+ [ Opcodes.i32_load, 0, 4 ],
2761
+ Opcodes.i32_from
2762
+ ], generate(scope, decl.right), number(TYPES.number, Valtype.i32), getNodeType(scope, decl.right), false, name, true)),
2763
+ [ Opcodes.local_tee, newValueTmp ],
2764
+
2765
+ Opcodes.i32_to,
2766
+ [ Opcodes.i32_store, 0, 4 ]
2767
+ ],
2768
+ [TYPES.float32array]: [
2769
+ ...number(4, Valtype.i32),
2770
+ [ Opcodes.i32_mul ],
2771
+ [ Opcodes.i32_add ],
2772
+ ...(op === '=' ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
2773
+
2774
+ ...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
2775
+ [ Opcodes.local_get, pointerTmp ],
2776
+ [ Opcodes.f32_load, 0, 4 ],
2777
+ [ Opcodes.f64_promote_f32 ]
2778
+ ], generate(scope, decl.right), number(TYPES.number, Valtype.i32), getNodeType(scope, decl.right), false, name, true)),
2779
+ [ Opcodes.local_tee, newValueTmp ],
2780
+
2781
+ [ Opcodes.f32_demote_f64 ],
2782
+ [ Opcodes.f32_store, 0, 4 ]
2783
+ ],
2784
+ [TYPES.float64array]: [
2785
+ ...number(8, Valtype.i32),
2786
+ [ Opcodes.i32_mul ],
2787
+ [ Opcodes.i32_add ],
2788
+ ...(op === '=' ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
2789
+
2790
+ ...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
2791
+ [ Opcodes.local_get, pointerTmp ],
2792
+ [ Opcodes.f64_load, 0, 4 ]
2793
+ ], generate(scope, decl.right), number(TYPES.number, Valtype.i32), getNodeType(scope, decl.right), false, name, true)),
2794
+ [ Opcodes.local_tee, newValueTmp ],
2795
+
2796
+ [ Opcodes.f64_store, 0, 4 ]
2797
+ ],
2798
+ }, {
2799
+ prelude: [
2800
+ ...generate(scope, decl.left.object),
2801
+ Opcodes.i32_to_u,
2802
+ ...generate(scope, decl.left.property),
2803
+ Opcodes.i32_to_u,
2804
+ ],
2805
+ postlude: setLastType(scope, TYPES.number)
2806
+ }),
2807
+
2383
2808
  default: internalThrow(scope, 'TypeError', `Cannot assign member with non-array`)
2384
2809
  }, Blocktype.void),
2385
2810
 
@@ -2763,7 +3188,9 @@ const generateForOf = (scope, decl) => {
2763
3188
  // // todo: we should only do this for strings but we don't know at compile-time :(
2764
3189
  // hack: this is naughty and will break things!
2765
3190
  let newOut = number(0, Valtype.i32), newPointer = number(0, Valtype.i32);
2766
- if (pages.hasAnyString) {
3191
+
3192
+ const known = knownType(scope, getNodeType(scope, decl.right));
3193
+ if ((known === TYPES.string || known === TYPES.bytestring) || (pages.hasAnyString && known == null)) {
2767
3194
  // todo: we use i16 even for bytestrings which should not make a bad thing happen, just be confusing for debugging?
2768
3195
  0, [ newOut, newPointer ] = makeArray(scope, {
2769
3196
  rawElements: new Array(0)
@@ -2811,6 +3238,7 @@ const generateForOf = (scope, decl) => {
2811
3238
  [ Opcodes.end ],
2812
3239
  [ Opcodes.end ]
2813
3240
  ],
3241
+
2814
3242
  [TYPES.string]: [
2815
3243
  ...setType(scope, leftName, TYPES.string),
2816
3244
 
@@ -2919,6 +3347,7 @@ const generateForOf = (scope, decl) => {
2919
3347
  [ Opcodes.end ],
2920
3348
  [ Opcodes.end ]
2921
3349
  ],
3350
+
2922
3351
  [TYPES.set]: [
2923
3352
  [ Opcodes.loop, Blocktype.void ],
2924
3353
 
@@ -2957,6 +3386,106 @@ const generateForOf = (scope, decl) => {
2957
3386
  [ Opcodes.end ],
2958
3387
  [ Opcodes.end ]
2959
3388
  ],
3389
+
3390
+ ...wrapBC({
3391
+ [TYPES.uint8array]: [
3392
+ [ Opcodes.i32_add ],
3393
+
3394
+ [ Opcodes.i32_load8_u, 0, 4 ],
3395
+ Opcodes.i32_from_u
3396
+ ],
3397
+ [TYPES.uint8clampedarray]: [
3398
+ [ Opcodes.i32_add ],
3399
+
3400
+ [ Opcodes.i32_load8_u, 0, 4 ],
3401
+ Opcodes.i32_from_u
3402
+ ],
3403
+ [TYPES.int8array]: [
3404
+ [ Opcodes.i32_add ],
3405
+
3406
+ [ Opcodes.i32_load8_s, 0, 4 ],
3407
+ Opcodes.i32_from
3408
+ ],
3409
+ [TYPES.uint16array]: [
3410
+ ...number(2, Valtype.i32),
3411
+ [ Opcodes.i32_mul ],
3412
+ [ Opcodes.i32_add ],
3413
+
3414
+ [ Opcodes.i32_load16_u, 0, 4 ],
3415
+ Opcodes.i32_from_u
3416
+ ],
3417
+ [TYPES.int16array]: [
3418
+ ...number(2, Valtype.i32),
3419
+ [ Opcodes.i32_mul ],
3420
+ [ Opcodes.i32_add ],
3421
+
3422
+ [ Opcodes.i32_load16_s, 0, 4 ],
3423
+ Opcodes.i32_from
3424
+ ],
3425
+ [TYPES.uint32array]: [
3426
+ ...number(4, Valtype.i32),
3427
+ [ Opcodes.i32_mul ],
3428
+ [ Opcodes.i32_add ],
3429
+
3430
+ [ Opcodes.i32_load, 0, 4 ],
3431
+ Opcodes.i32_from_u
3432
+ ],
3433
+ [TYPES.int32array]: [
3434
+ ...number(4, Valtype.i32),
3435
+ [ Opcodes.i32_mul ],
3436
+ [ Opcodes.i32_add ],
3437
+
3438
+ [ Opcodes.i32_load, 0, 4 ],
3439
+ Opcodes.i32_from
3440
+ ],
3441
+ [TYPES.float32array]: [
3442
+ ...number(4, Valtype.i32),
3443
+ [ Opcodes.i32_mul ],
3444
+ [ Opcodes.i32_add ],
3445
+
3446
+ [ Opcodes.f32_load, 0, 4 ],
3447
+ [ Opcodes.f64_promote_f32 ]
3448
+ ],
3449
+ [TYPES.float64array]: [
3450
+ ...number(8, Valtype.i32),
3451
+ [ Opcodes.i32_mul ],
3452
+ [ Opcodes.i32_add ],
3453
+
3454
+ [ Opcodes.f64_load, 0, 4 ]
3455
+ ],
3456
+ }, {
3457
+ prelude: [
3458
+ ...setType(scope, leftName, TYPES.number),
3459
+
3460
+ [ Opcodes.loop, Blocktype.void ],
3461
+
3462
+ [ Opcodes.local_get, pointer ],
3463
+ [ Opcodes.local_get, counter ]
3464
+ ],
3465
+ postlude: [
3466
+ [ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx ],
3467
+
3468
+ [ Opcodes.block, Blocktype.void ],
3469
+ [ Opcodes.block, Blocktype.void ],
3470
+ ...generate(scope, decl.body),
3471
+ [ Opcodes.end ],
3472
+
3473
+ // increment counter by 1
3474
+ [ Opcodes.local_get, counter ],
3475
+ ...number(1, Valtype.i32),
3476
+ [ Opcodes.i32_add ],
3477
+ [ Opcodes.local_tee, counter ],
3478
+
3479
+ // loop if counter != length
3480
+ [ Opcodes.local_get, length ],
3481
+ [ Opcodes.i32_ne ],
3482
+ [ Opcodes.br_if, 1 ],
3483
+
3484
+ [ Opcodes.end ],
3485
+ [ Opcodes.end ]
3486
+ ]
3487
+ }),
3488
+
2960
3489
  default: internalThrow(scope, 'TypeError', `Tried for..of on non-iterable type`)
2961
3490
  }, Blocktype.void));
2962
3491
 
@@ -3029,32 +3558,102 @@ const generateLabel = (scope, decl) => {
3029
3558
  const generateThrow = (scope, decl) => {
3030
3559
  scope.throws = true;
3031
3560
 
3032
- let message = decl.argument.value, constructor = null;
3561
+ const exceptionMode = Prefs.exceptionMode ?? 'lut';
3562
+ if (exceptionMode === 'lut') {
3563
+ let message = decl.argument.value, constructor = null;
3033
3564
 
3034
- // hack: throw new X("...") -> throw "..."
3035
- if (!message && (decl.argument.type === 'NewExpression' || decl.argument.type === 'CallExpression')) {
3036
- constructor = decl.argument.callee.name;
3037
- message = decl.argument.arguments[0]?.value ?? '';
3565
+ // support `throw (new)? Error(...)`
3566
+ if (!message && (decl.argument.type === 'NewExpression' || decl.argument.type === 'CallExpression')) {
3567
+ constructor = decl.argument.callee.name;
3568
+ message = decl.argument.arguments[0]?.value ?? '';
3569
+ }
3570
+
3571
+ if (tags.length === 0) tags.push({
3572
+ params: [ Valtype.i32 ],
3573
+ results: [],
3574
+ idx: tags.length
3575
+ });
3576
+
3577
+ let exceptId = exceptions.findIndex(x => x.constructor === constructor && x.message === message);
3578
+ if (exceptId === -1) exceptId = exceptions.push({ constructor, message }) - 1;
3579
+
3580
+ scope.exceptions ??= [];
3581
+ scope.exceptions.push(exceptId);
3582
+
3583
+ return [
3584
+ ...number(exceptId, Valtype.i32),
3585
+ [ Opcodes.throw, tags[0].idx ]
3586
+ ];
3038
3587
  }
3039
3588
 
3040
- if (tags.length === 0) tags.push({
3041
- params: [ Valtype.i32 ],
3042
- results: [],
3043
- idx: tags.length
3044
- });
3589
+ if (exceptionMode === 'stack') {
3590
+ if (tags.length === 0) tags.push({
3591
+ params: [ valtypeBinary, Valtype.i32 ],
3592
+ results: [],
3593
+ idx: tags.length
3594
+ });
3045
3595
 
3046
- let exceptId = exceptions.push({ constructor, message }) - 1;
3047
- let tagIdx = tags[0].idx;
3596
+ return [
3597
+ ...generate(scope, decl.argument),
3598
+ ...getNodeType(scope, decl.argument),
3599
+ [ Opcodes.throw, tags[0].idx ]
3600
+ ];
3601
+ }
3048
3602
 
3049
- scope.exceptions ??= [];
3050
- scope.exceptions.push(exceptId);
3603
+ if (exceptionMode === 'stackest') {
3604
+ let message = decl.argument, constructor = null;
3051
3605
 
3052
- // todo: write a description of how this works lol
3606
+ // support `throw (new)? Error(...)`
3607
+ if (message.type === 'NewExpression' || message.type === 'CallExpression') {
3608
+ constructor = decl.argument.callee;
3609
+ message = decl.argument.arguments[0];
3610
+ }
3053
3611
 
3054
- return [
3055
- [ Opcodes.i32_const, signedLEB128(exceptId) ],
3056
- [ Opcodes.throw, tagIdx ]
3057
- ];
3612
+ message ??= DEFAULT_VALUE;
3613
+
3614
+ if (tags.length === 0) tags.push({
3615
+ params: [ valtypeBinary, valtypeBinary, Valtype.i32 ],
3616
+ results: [],
3617
+ idx: tags.length
3618
+ });
3619
+
3620
+ return [
3621
+ ...(constructor == null ? number(-1) : generate(scope, constructor)),
3622
+ ...generate(scope, message),
3623
+ ...getNodeType(scope, message),
3624
+ [ Opcodes.throw, tags[0].idx ]
3625
+ ];
3626
+ }
3627
+
3628
+ if (exceptionMode === 'partial') {
3629
+ let message = decl.argument, constructor = null;
3630
+
3631
+ // support `throw (new)? Error(...)`
3632
+ if (message.type === 'NewExpression' || message.type === 'CallExpression') {
3633
+ constructor = decl.argument.callee.name;
3634
+ message = decl.argument.arguments[0];
3635
+ }
3636
+
3637
+ message ??= DEFAULT_VALUE;
3638
+
3639
+ if (tags.length === 0) tags.push({
3640
+ params: [ Valtype.i32, valtypeBinary, Valtype.i32 ],
3641
+ results: [],
3642
+ idx: tags.length
3643
+ });
3644
+
3645
+ let exceptId = exceptions.push({ constructor }) - 1;
3646
+
3647
+ scope.exceptions ??= [];
3648
+ scope.exceptions.push(exceptId);
3649
+
3650
+ return [
3651
+ ...number(exceptId, Valtype.i32),
3652
+ ...generate(scope, message),
3653
+ ...getNodeType(scope, message),
3654
+ [ Opcodes.throw, tags[0].idx ]
3655
+ ];
3656
+ }
3058
3657
  };
3059
3658
 
3060
3659
  const generateTry = (scope, decl) => {
@@ -3090,6 +3689,22 @@ const generateEmpty = (scope, decl) => {
3090
3689
  return [];
3091
3690
  };
3092
3691
 
3692
+ const generateMeta = (scope, decl) => {
3693
+ if (decl.meta.name !== 'new') return todo(scope, `meta property object ${decl.meta.name} is not supported yet`, true);
3694
+
3695
+ switch (`${decl.meta.name}.${decl.property.name}`) {
3696
+ case 'new.target': {
3697
+ scope.constr = true;
3698
+
3699
+ return [
3700
+ [ Opcodes.local_get, -1 ],
3701
+ Opcodes.i32_from_u,
3702
+ ...setLastType(scope, TYPES.boolean)
3703
+ ];
3704
+ }
3705
+ }
3706
+ };
3707
+
3093
3708
  let pages = new Map();
3094
3709
  const allocPage = (scope, reason, type) => {
3095
3710
  if (pages.has(reason)) return pages.get(reason).ind;
@@ -3407,6 +4022,19 @@ const withType = (scope, wasm, type) => [
3407
4022
  ...setLastType(scope, type)
3408
4023
  ];
3409
4024
 
4025
+ const wrapBC = (bc, { prelude = [], postlude = [] } = {}) => {
4026
+ const out = {};
4027
+ for (const x in bc) {
4028
+ out[x] = [
4029
+ ...prelude,
4030
+ ...bc[x],
4031
+ ...postlude
4032
+ ];
4033
+ }
4034
+
4035
+ return out;
4036
+ };
4037
+
3410
4038
  const generateMember = (scope, decl, _global, _name) => {
3411
4039
  const name = decl.object.name;
3412
4040
 
@@ -3429,20 +4057,10 @@ const generateMember = (scope, decl, _global, _name) => {
3429
4057
  const func = funcs.find(x => x.name === name);
3430
4058
  if (func) {
3431
4059
  const typedParams = !func.internal || builtinFuncs[name]?.typedParams;
3432
- return withType(scope, number(typedParams ? func.params.length / 2 : func.params.length), TYPES.number);
3433
- }
3434
-
3435
- if (builtinFuncs[name + '$constructor']) {
3436
- const regularFunc = builtinFuncs[name];
3437
- const regularParams = regularFunc.typedParams ? (regularFunc.params.length / 2) : regularFunc.params.length;
3438
-
3439
- const constructorFunc = builtinFuncs[name + '$constructor'];
3440
- const constructorParams = constructorFunc.typedParams ? (constructorFunc.params.length / 2) : constructorFunc.params.length;
3441
-
3442
- return withType(scope, number(Math.max(regularParams, constructorParams)), TYPES.number);
4060
+ return withType(scope, number(typedParams ? Math.floor(func.params.length / 2) : (func.constr ? (func.params.length - 1) : func.params.length)), TYPES.number);
3443
4061
  }
3444
4062
 
3445
- if (builtinFuncs[name]) return withType(scope, number(builtinFuncs[name].typedParams ? (builtinFuncs[name].params.length / 2) : builtinFuncs[name].params.length), TYPES.number);
4063
+ if (builtinFuncs[name]) return withType(scope, number(builtinFuncs[name].typedParams ? Math.floor(builtinFuncs[name].params.length / 2) : (builtinFuncs[name].constr ? (builtinFuncs[name].params.length - 1) : builtinFuncs[name].params.length)), TYPES.number);
3446
4064
  if (importedFuncs[name]) return withType(scope, number(importedFuncs[name].params.length ?? importedFuncs[name].params), TYPES.number);
3447
4065
  if (internalConstrs[name]) return withType(scope, number(internalConstrs[name].length ?? 0), TYPES.number);
3448
4066
 
@@ -3460,7 +4078,7 @@ const generateMember = (scope, decl, _global, _name) => {
3460
4078
  const type = getNodeType(scope, decl.object);
3461
4079
  const known = knownType(scope, type);
3462
4080
  if (known != null) {
3463
- if ([ TYPES.string, TYPES.bytestring, TYPES.array ].includes(known)) return [
4081
+ if (typeHasFlag(known, TYPE_FLAGS.length)) return [
3464
4082
  ...generate(scope, decl.object),
3465
4083
  Opcodes.i32_to_u,
3466
4084
 
@@ -3472,7 +4090,9 @@ const generateMember = (scope, decl, _global, _name) => {
3472
4090
  }
3473
4091
 
3474
4092
  return [
3475
- ...typeIsOneOf(getNodeType(scope, decl.object), [ TYPES.string, TYPES.bytestring, TYPES.array ]),
4093
+ ...getNodeType(scope, decl.object),
4094
+ ...number(TYPE_FLAGS.length, Valtype.i32),
4095
+ [ Opcodes.i32_and ],
3476
4096
  [ Opcodes.if, valtypeBinary ],
3477
4097
  ...generate(scope, decl.object),
3478
4098
  Opcodes.i32_to_u,
@@ -3489,7 +4109,7 @@ const generateMember = (scope, decl, _global, _name) => {
3489
4109
  }
3490
4110
 
3491
4111
  // todo: generate this array procedurally during builtinFuncs creation
3492
- if (['size', 'description'].includes(decl.property.name)) {
4112
+ if (['size', 'description', 'byteLength'].includes(decl.property.name)) {
3493
4113
  const bc = {};
3494
4114
  const cands = Object.keys(builtinFuncs).filter(x => x.startsWith('__') && x.endsWith('_prototype_' + decl.property.name + '$get'));
3495
4115
 
@@ -3521,7 +4141,9 @@ const generateMember = (scope, decl, _global, _name) => {
3521
4141
  // // todo: we should only do this for strings but we don't know at compile-time :(
3522
4142
  // hack: this is naughty and will break things!
3523
4143
  let newOut = number(0, Valtype.i32), newPointer = number(0, Valtype.i32);
3524
- if (pages.hasAnyString && knownType(scope, getNodeType(scope, decl.object)) !== TYPES.array) {
4144
+
4145
+ const known = knownType(scope, getNodeType(scope, decl.object));
4146
+ if ((known === TYPES.string || known === TYPES.bytestring) || (pages.hasAnyString && known == null)) {
3525
4147
  // todo: we use i16 even for bytestrings which should not make a bad thing happen, just be confusing for debugging?
3526
4148
  0, [ newOut, newPointer ] = makeArray(scope, {
3527
4149
  rawElements: new Array(0)
@@ -3595,6 +4217,82 @@ const generateMember = (scope, decl, _global, _name) => {
3595
4217
  ...setLastType(scope, TYPES.bytestring)
3596
4218
  ],
3597
4219
 
4220
+ ...wrapBC({
4221
+ [TYPES.uint8array]: [
4222
+ [ Opcodes.i32_add ],
4223
+
4224
+ [ Opcodes.i32_load8_u, 0, 4 ],
4225
+ Opcodes.i32_from_u
4226
+ ],
4227
+ [TYPES.uint8clampedarray]: [
4228
+ [ Opcodes.i32_add ],
4229
+
4230
+ [ Opcodes.i32_load8_u, 0, 4 ],
4231
+ Opcodes.i32_from_u
4232
+ ],
4233
+ [TYPES.int8array]: [
4234
+ [ Opcodes.i32_add ],
4235
+
4236
+ [ Opcodes.i32_load8_s, 0, 4 ],
4237
+ Opcodes.i32_from
4238
+ ],
4239
+ [TYPES.uint16array]: [
4240
+ ...number(2, Valtype.i32),
4241
+ [ Opcodes.i32_mul ],
4242
+ [ Opcodes.i32_add ],
4243
+
4244
+ [ Opcodes.i32_load16_u, 0, 4 ],
4245
+ Opcodes.i32_from_u
4246
+ ],
4247
+ [TYPES.int16array]: [
4248
+ ...number(2, Valtype.i32),
4249
+ [ Opcodes.i32_mul ],
4250
+ [ Opcodes.i32_add ],
4251
+
4252
+ [ Opcodes.i32_load16_s, 0, 4 ],
4253
+ Opcodes.i32_from
4254
+ ],
4255
+ [TYPES.uint32array]: [
4256
+ ...number(4, Valtype.i32),
4257
+ [ Opcodes.i32_mul ],
4258
+ [ Opcodes.i32_add ],
4259
+
4260
+ [ Opcodes.i32_load, 0, 4 ],
4261
+ Opcodes.i32_from_u
4262
+ ],
4263
+ [TYPES.int32array]: [
4264
+ ...number(4, Valtype.i32),
4265
+ [ Opcodes.i32_mul ],
4266
+ [ Opcodes.i32_add ],
4267
+
4268
+ [ Opcodes.i32_load, 0, 4 ],
4269
+ Opcodes.i32_from
4270
+ ],
4271
+ [TYPES.float32array]: [
4272
+ ...number(4, Valtype.i32),
4273
+ [ Opcodes.i32_mul ],
4274
+ [ Opcodes.i32_add ],
4275
+
4276
+ [ Opcodes.f32_load, 0, 4 ],
4277
+ [ Opcodes.f64_promote_f32 ]
4278
+ ],
4279
+ [TYPES.float64array]: [
4280
+ ...number(8, Valtype.i32),
4281
+ [ Opcodes.i32_mul ],
4282
+ [ Opcodes.i32_add ],
4283
+
4284
+ [ Opcodes.f64_load, 0, 4 ]
4285
+ ],
4286
+ }, {
4287
+ prelude: [
4288
+ ...object,
4289
+ Opcodes.i32_to_u,
4290
+ ...property,
4291
+ Opcodes.i32_to_u
4292
+ ],
4293
+ postlude: setLastType(scope, TYPES.number)
4294
+ }),
4295
+
3598
4296
  default: internalThrow(scope, 'TypeError', 'Member expression is not supported for non-string non-array yet', true)
3599
4297
  });
3600
4298
  };
@@ -3617,7 +4315,7 @@ const objectHack = node => {
3617
4315
  if (!objectName) objectName = objectHack(node.object)?.name?.slice?.(2);
3618
4316
 
3619
4317
  // if .name or .length, give up (hack within a hack!)
3620
- if (['name', 'length', 'size', 'description'].includes(node.property.name)) {
4318
+ if (['name', 'length', 'size', 'description', 'byteLength'].includes(node.property.name)) {
3621
4319
  node.object = objectHack(node.object);
3622
4320
  return;
3623
4321
  }
@@ -3670,8 +4368,8 @@ const generateFunc = (scope, decl) => {
3670
4368
 
3671
4369
  if (typedInput && decl.returnType) {
3672
4370
  const { type } = extractTypeAnnotation(decl.returnType);
3673
- // if (type != null && !Prefs.indirectCalls) {
3674
- if (type != null) {
4371
+ if (type != null && !Prefs.indirectCalls) {
4372
+ // if (type != null) {
3675
4373
  func.returnType = type;
3676
4374
  func.returns = [ valtypeBinary ];
3677
4375
  }
@@ -3772,7 +4470,7 @@ const internalConstrs = {
3772
4470
 
3773
4471
  // todo: check in wasm instead of here
3774
4472
  const literalValue = arg.value ?? 0;
3775
- if (literalValue < 0 || !Number.isFinite(literalValue) || literalValue > 4294967295) return internalThrow(scope, 'RangeThrow', 'Invalid array length', true);
4473
+ if (literalValue < 0 || !Number.isFinite(literalValue) || literalValue > 4294967295) return internalThrow(scope, 'RangeError', 'Invalid array length', true);
3776
4474
 
3777
4475
  return [
3778
4476
  ...out,