porffor 0.17.0-048c6f2ee → 0.17.0-0564424f4

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
 
@@ -291,12 +297,13 @@ const generateIdent = (scope, decl) => {
291
297
  return wasm.slice();
292
298
  }
293
299
 
294
- if (Object.hasOwn(builtinFuncs, name) || Object.hasOwn(internalConstrs, name)) {
295
- // todo: return an actual something
296
- 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);
297
304
  }
298
305
 
299
- if (isExistingProtoFunc(name)) {
306
+ if (isExistingProtoFunc(name) || Object.hasOwn(internalConstrs, name) || Object.hasOwn(builtinFuncs, name)) {
300
307
  // todo: return an actual something
301
308
  return number(1);
302
309
  }
@@ -615,11 +622,14 @@ const compareStrings = (scope, left, right, bytestrings = false) => {
615
622
  };
616
623
 
617
624
  const truthy = (scope, wasm, type, intIn = false, intOut = false, forceTruthyMode = undefined) => {
618
- // if (isIntToFloatOp(wasm[wasm.length - 1])) return [
619
- // ...wasm,
620
- // ...(!intIn && intOut ? [ Opcodes.i32_to_u ] : [])
621
- // ];
622
- // 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
+ ];
623
633
 
624
634
  // todo/perf: use knownType and custom bytecode here instead of typeSwitch
625
635
 
@@ -975,6 +985,33 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
975
985
  };
976
986
 
977
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
+
978
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);
979
1016
 
980
1017
  if (valtype !== 'i32' && ['==', '===', '!=', '!==', '>', '>=', '<', '<='].includes(decl.operator)) out.push(Opcodes.i32_from_u);
@@ -992,12 +1029,13 @@ const asmFuncToAsm = (func, scope) => {
992
1029
  idx = funcIndex[n];
993
1030
  }
994
1031
 
1032
+ if (idx == null) throw new Error(`builtin('${n}') failed to find a func (inside ${scope.name})`);
995
1033
  return idx;
996
1034
  }
997
1035
  });
998
1036
  };
999
1037
 
1000
- 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 }) => {
1001
1039
  const existing = funcs.find(x => x.name === name);
1002
1040
  if (existing) return existing;
1003
1041
 
@@ -1024,13 +1062,17 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
1024
1062
  returnType,
1025
1063
  internal: true,
1026
1064
  index: currentFuncIndex++,
1027
- table
1065
+ table,
1066
+ constr
1028
1067
  };
1029
1068
 
1030
1069
  funcs.push(func);
1031
1070
  funcIndex[name] = func.index;
1032
1071
 
1033
- 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
+ }
1034
1076
 
1035
1077
  let baseGlobalIdx, i = 0;
1036
1078
  for (const type of globalTypes) {
@@ -1051,15 +1093,35 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
1051
1093
 
1052
1094
  if (table) {
1053
1095
  for (const inst of wasm) {
1054
- if (inst[0] === Opcodes.i32_load16_u && inst.at(-1) === 'read_argc') {
1096
+ if (inst.at(-1) === 'read func lut') {
1055
1097
  inst.splice(2, 99);
1056
- inst.push(...unsignedLEB128(allocPage({}, 'func argc lut') * pageSize));
1098
+ inst.push(...unsignedLEB128(allocPage({}, 'func lut') * pageSize));
1057
1099
  }
1058
1100
  }
1059
1101
 
1060
1102
  funcs.table = true;
1061
1103
  }
1062
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
+
1063
1125
  func.wasm = wasm;
1064
1126
 
1065
1127
  return func;
@@ -1186,20 +1248,7 @@ const getNodeType = (scope, node) => {
1186
1248
  return TYPES.number;
1187
1249
  }
1188
1250
 
1189
- if (node.type === 'NewExpression' && builtinFuncs[name + '$constructor']) {
1190
- if (builtinFuncs[name + '$constructor'].typedReturns) {
1191
- if (scope.locals['#last_type']) return getLastType(scope);
1192
-
1193
- // presume
1194
- // todo: warn here?
1195
- return TYPES.number;
1196
- }
1197
-
1198
- return builtinFuncs[name + '$constructor'].returnType ?? TYPES.number;
1199
- }
1200
-
1201
1251
  const func = funcs.find(x => x.name === name);
1202
-
1203
1252
  if (func) {
1204
1253
  if (func.returnType != null) return func.returnType;
1205
1254
  }
@@ -1281,7 +1330,7 @@ const getNodeType = (scope, node) => {
1281
1330
  }
1282
1331
 
1283
1332
  if (node.type === 'BinaryExpression') {
1284
- if (['==', '===', '!=', '!==', '>', '>=', '<', '<='].includes(node.operator)) return TYPES.boolean;
1333
+ if (['==', '===', '!=', '!==', '>', '>=', '<', '<=', 'instanceof'].includes(node.operator)) return TYPES.boolean;
1285
1334
  if (node.operator !== '+') return TYPES.number;
1286
1335
 
1287
1336
  const knownLeft = knownType(scope, getNodeType(scope, node.left));
@@ -1314,7 +1363,7 @@ const getNodeType = (scope, node) => {
1314
1363
  const objectKnownType = knownType(scope, getNodeType(scope, node.object));
1315
1364
  if (objectKnownType != null) {
1316
1365
  if (name === 'length') {
1317
- if ([ TYPES.string, TYPES.bytestring, TYPES.array ].includes(objectKnownType)) return TYPES.number;
1366
+ if (typeHasFlag(objectKnownType, TYPE_FLAGS.length)) return TYPES.number;
1318
1367
  else return TYPES.undefined;
1319
1368
  }
1320
1369
 
@@ -1386,9 +1435,9 @@ const countLeftover = wasm => {
1386
1435
 
1387
1436
  if (depth === 0)
1388
1437
  if ([Opcodes.throw, Opcodes.drop, Opcodes.local_set, Opcodes.global_set].includes(inst[0])) count--;
1389
- 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)) {}
1390
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++;
1391
- 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;
1392
1441
  else if (inst[0] === Opcodes.memory_copy[0] && (inst[1] === Opcodes.memory_copy[1] || inst[1] === Opcodes.memory_init[1])) count -= 3;
1393
1442
  else if (inst[0] === Opcodes.return) count = 0;
1394
1443
  else if (inst[0] === Opcodes.call) {
@@ -1426,6 +1475,18 @@ const generateExp = (scope, decl) => {
1426
1475
  return out;
1427
1476
  };
1428
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
+
1429
1490
  const CTArrayUtil = {
1430
1491
  getLengthI32: pointer => [
1431
1492
  ...number(0, Valtype.i32),
@@ -1485,7 +1546,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1485
1546
  name = func.name;
1486
1547
  }
1487
1548
 
1488
- if (name === 'eval' && decl.arguments[0]?.type === 'Literal') {
1549
+ if (!decl._new && name === 'eval' && decl.arguments[0]?.type === 'Literal') {
1489
1550
  // literal eval hack
1490
1551
  const code = decl.arguments[0]?.value ?? '';
1491
1552
 
@@ -1528,7 +1589,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1528
1589
 
1529
1590
  let protoName, target;
1530
1591
  // ident.func()
1531
- if (name && name.startsWith('__')) {
1592
+ if (!decl._new && name && name.startsWith('__')) {
1532
1593
  const spl = name.slice(2).split('_');
1533
1594
 
1534
1595
  protoName = spl[spl.length - 1];
@@ -1541,12 +1602,12 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1541
1602
  }
1542
1603
 
1543
1604
  // literal.func()
1544
- if (!name && decl.callee.type === 'MemberExpression') {
1605
+ if (!decl._new && !name && decl.callee.type === 'MemberExpression') {
1545
1606
  // megahack for /regex/.func()
1546
1607
  const funcName = decl.callee.property.name;
1547
- if (decl.callee.object.regex && Object.hasOwn(Rhemyn, funcName)) {
1608
+ if (decl.callee.object.regex && ['test'].includes(funcName)) {
1548
1609
  const regex = decl.callee.object.regex.pattern;
1549
- const rhemynName = `regex_${funcName}_${regex}`;
1610
+ const rhemynName = `regex_${funcName}_${sanitize(regex)}`;
1550
1611
 
1551
1612
  if (!funcIndex[rhemynName]) {
1552
1613
  const func = Rhemyn[funcName](regex, currentFuncIndex++, rhemynName);
@@ -1567,7 +1628,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1567
1628
  [ Opcodes.call, idx ],
1568
1629
  Opcodes.i32_from_u,
1569
1630
 
1570
- ...setLastType(scope, TYPES.boolean)
1631
+ ...setLastType(scope, Rhemyn.types[funcName])
1571
1632
  ];
1572
1633
  }
1573
1634
 
@@ -1576,27 +1637,56 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1576
1637
  target = decl.callee.object;
1577
1638
  }
1578
1639
 
1579
- // if (protoName && baseType === TYPES.string && Rhemyn[protoName]) {
1580
- // 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 ],
1581
1648
 
1582
- // funcIndex[func.name] = func.index;
1583
- // funcs.push(func);
1649
+ ...number(Rhemyn.types[protoName] === TYPES.number ? -1 : 0),
1650
+ ...setLastType(scope, Rhemyn.types[protoName])
1651
+ ];
1584
1652
 
1585
- // return [
1586
- // generate(scope, decl.callee.object)
1653
+ const rhemynName = `regex_${protoName}_${sanitize(regex)}`;
1587
1654
 
1588
- // // call regex func
1589
- // [ Opcodes.call, func.index ],
1590
- // Opcodes.i32_from_u
1591
- // ];
1592
- // }
1655
+ if (!funcIndex[rhemynName]) {
1656
+ const func = Rhemyn[protoName](regex, currentFuncIndex++, rhemynName);
1657
+ func.internal = true;
1593
1658
 
1594
- if (protoName) {
1595
- const protoBC = {};
1659
+ funcIndex[func.name] = func.index;
1660
+ funcs.push(func);
1661
+ }
1596
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,
1673
+
1674
+ ...setLastType(scope, Rhemyn.types[protoName])
1675
+ ];
1676
+ }
1677
+
1678
+ const protoBC = {};
1597
1679
  const builtinProtoCands = Object.keys(builtinFuncs).filter(x => x.startsWith('__') && x.endsWith('_prototype_' + protoName));
1598
1680
 
1599
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
+
1600
1690
  for (const x of builtinProtoCands) {
1601
1691
  const type = TYPES[x.split('_prototype_')[0].slice(2).toLowerCase()];
1602
1692
  if (type == null) continue;
@@ -1606,7 +1696,14 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1606
1696
  type: 'Identifier',
1607
1697
  name: x
1608
1698
  },
1609
- arguments: [ target, ...decl.arguments ],
1699
+ arguments: [
1700
+ {
1701
+ type: 'Identifier',
1702
+ name: '#proto_target'
1703
+ },
1704
+
1705
+ ...decl.arguments
1706
+ ],
1610
1707
  _protoInternalCall: true
1611
1708
  });
1612
1709
  }
@@ -1700,29 +1797,37 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1700
1797
  }
1701
1798
 
1702
1799
  if (Object.keys(protoBC).length > 0) {
1703
- return typeSwitch(scope, getNodeType(scope, target), {
1704
- ...protoBC,
1800
+ return [
1801
+ ...out,
1705
1802
 
1706
- // TODO: error better
1707
- default: internalThrow(scope, 'TypeError', `'${protoName}' proto func tried to be called on a type without an impl`)
1708
- }, valtypeBinary);
1803
+ ...typeSwitch(scope, builtinProtoCands.length > 0 ? [ [ Opcodes.local_get, localTmp(scope, '#proto_target#type', Valtype.i32) ] ] : getNodeType(scope, target), {
1804
+ ...protoBC,
1805
+
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
+ ];
1709
1810
  }
1710
1811
  }
1711
1812
 
1712
- // TODO: only allows callee as literal
1713
- 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})`);
1714
1815
 
1715
1816
  let idx = funcIndex[name] ?? importedFuncs[name];
1716
1817
  if (idx === undefined && builtinFuncs[name]) {
1717
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);
1718
1820
 
1719
1821
  includeBuiltin(scope, name);
1720
1822
  idx = funcIndex[name];
1721
1823
  }
1722
1824
 
1723
- 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
+ }
1724
1829
 
1725
- if (idx === undefined && name.startsWith('__Porffor_wasm_')) {
1830
+ if (idx === undefined && !decl._new && name.startsWith('__Porffor_wasm_')) {
1726
1831
  const wasmOps = {
1727
1832
  // pointer, align, offset
1728
1833
  i32_load: { imms: 2, args: [ true ], returns: 1 },
@@ -1777,7 +1882,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1777
1882
 
1778
1883
  const indirectMode = Prefs.indirectCallMode ?? 'vararg';
1779
1884
  // options: vararg, strict
1780
- // - strict: simpler, smaller size usage, no func argc lut needed.
1885
+ // - strict: simpler, smaller size usage, no func lut needed.
1781
1886
  // ONLY works when arg count of call == arg count of function being called
1782
1887
  // - vararg: large size usage, cursed.
1783
1888
  // works when arg count of call != arg count of function being called*
@@ -1787,8 +1892,6 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1787
1892
  scope.table = true;
1788
1893
 
1789
1894
  let args = decl.arguments;
1790
- let out = [];
1791
-
1792
1895
  let locals = [];
1793
1896
 
1794
1897
  if (indirectMode === 'vararg') {
@@ -1852,24 +1955,67 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1852
1955
  // *for argc 0-3, in future (todo:) the max number should be
1853
1956
  // dynamically changed to the max argc of any func in the js file.
1854
1957
 
1855
- 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);
1856
1960
 
1857
1961
  const gen = argc => {
1858
- const out = [];
1962
+ const argsOut = [];
1859
1963
  for (let i = 0; i < argc; i++) {
1860
- out.push(
1964
+ argsOut.push(
1861
1965
  [ Opcodes.local_get, locals[i][0] ],
1862
1966
  [ Opcodes.local_get, locals[i][1] ]
1863
1967
  );
1864
1968
  }
1865
1969
 
1866
- out.push(
1867
- [ Opcodes.local_get, funcLocal ],
1868
- [ Opcodes.call_indirect, argc, 0 ],
1869
- ...setLastType(scope)
1870
- )
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
+ ];
1871
1980
 
1872
- 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
+ );
1873
2019
  };
1874
2020
 
1875
2021
  const tableBc = {};
@@ -1887,13 +2033,32 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1887
2033
  Opcodes.i32_to_u,
1888
2034
  [ Opcodes.local_set, funcLocal ],
1889
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
+
1890
2056
  ...brTable([
1891
2057
  // get argc of func we are calling
1892
2058
  [ Opcodes.local_get, funcLocal ],
1893
- ...number(ValtypeSize.i16, Valtype.i32),
2059
+ ...number(3, Valtype.i32),
1894
2060
  [ Opcodes.i32_mul ],
1895
-
1896
- [ 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' ]
1897
2062
  ], tableBc, valtypeBinary)
1898
2063
  ],
1899
2064
  default: internalThrow(scope, 'TypeError', `${unhackName(name)} is not a function`, true)
@@ -1907,7 +2072,16 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1907
2072
  const userFunc = func && !func.internal;
1908
2073
  const typedParams = userFunc || builtinFuncs[name]?.typedParams;
1909
2074
  const typedReturns = (userFunc && func.returnType == null) || builtinFuncs[name]?.typedReturns;
1910
- 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);
1911
2085
 
1912
2086
  let args = decl.arguments;
1913
2087
  if (func && args.length < paramCount) {
@@ -1922,7 +2096,6 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1922
2096
 
1923
2097
  if (func && func.throws) scope.throws = true;
1924
2098
 
1925
- let out = [];
1926
2099
  for (let i = 0; i < args.length; i++) {
1927
2100
  const arg = args[i];
1928
2101
  out = out.concat(generate(scope, arg));
@@ -1935,13 +2108,13 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1935
2108
  }
1936
2109
 
1937
2110
  if (valtypeBinary !== Valtype.i32 &&
1938
- (func && func.params[i * (typedParams ? 2 : 1)] === Valtype.i32)
2111
+ (func && func.params[paramOffset + i * (typedParams ? 2 : 1)] === Valtype.i32)
1939
2112
  ) {
1940
2113
  out.push(Opcodes.i32_to);
1941
2114
  }
1942
2115
 
1943
2116
  if (valtypeBinary === Valtype.i32 &&
1944
- (func && func.params[i * (typedParams ? 2 : 1)] === Valtype.f64)
2117
+ (func && func.params[paramOffset + i * (typedParams ? 2 : 1)] === Valtype.f64)
1945
2118
  ) {
1946
2119
  out.push([ Opcodes.f64_convert_i32_s ]);
1947
2120
  }
@@ -1974,32 +2147,10 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1974
2147
  return out;
1975
2148
  };
1976
2149
 
1977
- const generateNew = (scope, decl, _global, _name) => {
1978
- // hack: basically treat this as a normal call for builtins for now
1979
- const name = mapName(decl.callee.name);
1980
-
1981
- if (internalConstrs[name] && !internalConstrs[name].notConstr) return internalConstrs[name].generate(scope, decl, _global, _name);
1982
-
1983
- if (builtinFuncs[name + '$constructor']) {
1984
- // custom ...$constructor override builtin func
1985
- return generateCall(scope, {
1986
- ...decl,
1987
- callee: {
1988
- type: 'Identifier',
1989
- name: name + '$constructor'
1990
- }
1991
- }, _global, _name);
1992
- }
1993
-
1994
- if (
1995
- (builtinFuncs[name] && !builtinFuncs[name].constr) ||
1996
- (internalConstrs[name] && builtinFuncs[name].notConstr)
1997
- ) return internalThrow(scope, 'TypeError', `${name} is not a constructor`, true);
1998
-
1999
- 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)})`);
2000
-
2001
- return generateCall(scope, decl, _global, _name);
2002
- };
2150
+ const generateNew = (scope, decl, _global, _name) => generateCall(scope, {
2151
+ ...decl,
2152
+ _new: true
2153
+ }, _global, _name);
2003
2154
 
2004
2155
  // bad hack for undefined and null working without additional logic
2005
2156
  const DEFAULT_VALUE = {
@@ -2007,6 +2158,16 @@ const DEFAULT_VALUE = {
2007
2158
  name: 'undefined'
2008
2159
  };
2009
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
+
2010
2171
  const unhackName = name => {
2011
2172
  if (name.startsWith('__')) return name.slice(2).replaceAll('_', '.');
2012
2173
  return name;
@@ -2014,7 +2175,7 @@ const unhackName = name => {
2014
2175
 
2015
2176
  const knownType = (scope, type) => {
2016
2177
  if (type.length === 1 && type[0][0] === Opcodes.i32_const) {
2017
- return type[0][1];
2178
+ return read_signedLEB128(type[0].slice(1));
2018
2179
  }
2019
2180
 
2020
2181
  if (typedInput && type.length === 1 && type[0][0] === Opcodes.local_get) {
@@ -2301,11 +2462,6 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
2301
2462
  const { type, name } = decl.left;
2302
2463
  const [ local, isGlobal ] = lookupName(scope, name);
2303
2464
 
2304
- if (type === 'ObjectPattern') {
2305
- // hack: ignore object parts of `var a = {} = 2`
2306
- return generate(scope, decl.right);
2307
- }
2308
-
2309
2465
  if (isFuncType(decl.right.type)) {
2310
2466
  // hack for a = function () { ... }
2311
2467
  decl.right.id = { name };
@@ -2378,10 +2534,160 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
2378
2534
  [ Opcodes.i32_load8_u, 0, ValtypeSize.i32 + ValtypeSize[valtype] ]
2379
2535
  ], getNodeType(scope, decl.right), false, name, true)),
2380
2536
  [ Opcodes.local_tee, newValueTmp ],
2381
-
2382
2537
  [ Opcodes.store, 0, ValtypeSize.i32 ]
2383
2538
  ],
2384
2539
 
2540
+ ...wrapBC({
2541
+ [TYPES.uint8array]: [
2542
+ [ Opcodes.i32_add ],
2543
+ ...(op === '=' ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
2544
+
2545
+ ...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
2546
+ [ Opcodes.local_get, pointerTmp ],
2547
+ [ Opcodes.i32_load8_u, 0, 4 ],
2548
+ Opcodes.i32_from_u
2549
+ ], generate(scope, decl.right), number(TYPES.number, Valtype.i32), getNodeType(scope, decl.right), false, name, true)),
2550
+ [ Opcodes.local_tee, newValueTmp ],
2551
+
2552
+ Opcodes.i32_to_u,
2553
+ [ Opcodes.i32_store8, 0, 4 ]
2554
+ ],
2555
+ [TYPES.uint8clampedarray]: [
2556
+ [ Opcodes.i32_add ],
2557
+ ...(op === '=' ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
2558
+
2559
+ ...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
2560
+ [ Opcodes.local_get, pointerTmp ],
2561
+ [ Opcodes.i32_load8_u, 0, 4 ],
2562
+ Opcodes.i32_from_u
2563
+ ], generate(scope, decl.right), number(TYPES.number, Valtype.i32), getNodeType(scope, decl.right), false, name, true)),
2564
+ [ Opcodes.local_tee, newValueTmp ],
2565
+
2566
+ ...number(0),
2567
+ [ Opcodes.f64_max ],
2568
+ ...number(255),
2569
+ [ Opcodes.f64_min ],
2570
+ Opcodes.i32_to_u,
2571
+ [ Opcodes.i32_store8, 0, 4 ]
2572
+ ],
2573
+ [TYPES.int8array]: [
2574
+ [ Opcodes.i32_add ],
2575
+ ...(op === '=' ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
2576
+
2577
+ ...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
2578
+ [ Opcodes.local_get, pointerTmp ],
2579
+ [ Opcodes.i32_load8_s, 0, 4 ],
2580
+ Opcodes.i32_from
2581
+ ], generate(scope, decl.right), number(TYPES.number, Valtype.i32), getNodeType(scope, decl.right), false, name, true)),
2582
+ [ Opcodes.local_tee, newValueTmp ],
2583
+
2584
+ Opcodes.i32_to,
2585
+ [ Opcodes.i32_store8, 0, 4 ]
2586
+ ],
2587
+ [TYPES.uint16array]: [
2588
+ ...number(2, Valtype.i32),
2589
+ [ Opcodes.i32_mul ],
2590
+ [ Opcodes.i32_add ],
2591
+ ...(op === '=' ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
2592
+
2593
+ ...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
2594
+ [ Opcodes.local_get, pointerTmp ],
2595
+ [ Opcodes.i32_load16_u, 0, 4 ],
2596
+ Opcodes.i32_from_u
2597
+ ], generate(scope, decl.right), number(TYPES.number, Valtype.i32), getNodeType(scope, decl.right), false, name, true)),
2598
+ [ Opcodes.local_tee, newValueTmp ],
2599
+
2600
+ Opcodes.i32_to_u,
2601
+ [ Opcodes.i32_store16, 0, 4 ]
2602
+ ],
2603
+ [TYPES.int16array]: [
2604
+ ...number(2, Valtype.i32),
2605
+ [ Opcodes.i32_mul ],
2606
+ [ Opcodes.i32_add ],
2607
+ ...(op === '=' ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
2608
+
2609
+ ...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
2610
+ [ Opcodes.local_get, pointerTmp ],
2611
+ [ Opcodes.i32_load16_s, 0, 4 ],
2612
+ Opcodes.i32_from
2613
+ ], generate(scope, decl.right), number(TYPES.number, Valtype.i32), getNodeType(scope, decl.right), false, name, true)),
2614
+ [ Opcodes.local_tee, newValueTmp ],
2615
+
2616
+ Opcodes.i32_to,
2617
+ [ Opcodes.i32_store16, 0, 4 ]
2618
+ ],
2619
+ [TYPES.uint32array]: [
2620
+ ...number(4, Valtype.i32),
2621
+ [ Opcodes.i32_mul ],
2622
+ [ Opcodes.i32_add ],
2623
+ ...(op === '=' ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
2624
+
2625
+ ...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
2626
+ [ Opcodes.local_get, pointerTmp ],
2627
+ [ Opcodes.i32_load, 0, 4 ],
2628
+ Opcodes.i32_from_u
2629
+ ], generate(scope, decl.right), number(TYPES.number, Valtype.i32), getNodeType(scope, decl.right), false, name, true)),
2630
+ [ Opcodes.local_tee, newValueTmp ],
2631
+
2632
+ Opcodes.i32_to_u,
2633
+ [ Opcodes.i32_store, 0, 4 ]
2634
+ ],
2635
+ [TYPES.int32array]: [
2636
+ ...number(4, Valtype.i32),
2637
+ [ Opcodes.i32_mul ],
2638
+ [ Opcodes.i32_add ],
2639
+ ...(op === '=' ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
2640
+
2641
+ ...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
2642
+ [ Opcodes.local_get, pointerTmp ],
2643
+ [ Opcodes.i32_load, 0, 4 ],
2644
+ Opcodes.i32_from
2645
+ ], generate(scope, decl.right), number(TYPES.number, Valtype.i32), getNodeType(scope, decl.right), false, name, true)),
2646
+ [ Opcodes.local_tee, newValueTmp ],
2647
+
2648
+ Opcodes.i32_to,
2649
+ [ Opcodes.i32_store, 0, 4 ]
2650
+ ],
2651
+ [TYPES.float32array]: [
2652
+ ...number(4, Valtype.i32),
2653
+ [ Opcodes.i32_mul ],
2654
+ [ Opcodes.i32_add ],
2655
+ ...(op === '=' ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
2656
+
2657
+ ...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
2658
+ [ Opcodes.local_get, pointerTmp ],
2659
+ [ Opcodes.f32_load, 0, 4 ],
2660
+ [ Opcodes.f64_promote_f32 ]
2661
+ ], generate(scope, decl.right), number(TYPES.number, Valtype.i32), getNodeType(scope, decl.right), false, name, true)),
2662
+ [ Opcodes.local_tee, newValueTmp ],
2663
+
2664
+ [ Opcodes.f32_demote_f64 ],
2665
+ [ Opcodes.f32_store, 0, 4 ]
2666
+ ],
2667
+ [TYPES.float64array]: [
2668
+ ...number(8, Valtype.i32),
2669
+ [ Opcodes.i32_mul ],
2670
+ [ Opcodes.i32_add ],
2671
+ ...(op === '=' ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
2672
+
2673
+ ...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
2674
+ [ Opcodes.local_get, pointerTmp ],
2675
+ [ Opcodes.f64_load, 0, 4 ]
2676
+ ], generate(scope, decl.right), number(TYPES.number, Valtype.i32), getNodeType(scope, decl.right), false, name, true)),
2677
+ [ Opcodes.local_tee, newValueTmp ],
2678
+
2679
+ [ Opcodes.f64_store, 0, 4 ]
2680
+ ],
2681
+ }, {
2682
+ prelude: [
2683
+ ...generate(scope, decl.left.object),
2684
+ Opcodes.i32_to_u,
2685
+ ...generate(scope, decl.left.property),
2686
+ Opcodes.i32_to_u,
2687
+ ],
2688
+ postlude: setLastType(scope, TYPES.number)
2689
+ }),
2690
+
2385
2691
  default: internalThrow(scope, 'TypeError', `Cannot assign member with non-array`)
2386
2692
  }, Blocktype.void),
2387
2693
 
@@ -2765,7 +3071,9 @@ const generateForOf = (scope, decl) => {
2765
3071
  // // todo: we should only do this for strings but we don't know at compile-time :(
2766
3072
  // hack: this is naughty and will break things!
2767
3073
  let newOut = number(0, Valtype.i32), newPointer = number(0, Valtype.i32);
2768
- if (pages.hasAnyString) {
3074
+
3075
+ const known = knownType(scope, getNodeType(scope, decl.right));
3076
+ if ((known === TYPES.string || known === TYPES.bytestring) || (pages.hasAnyString && known == null)) {
2769
3077
  // todo: we use i16 even for bytestrings which should not make a bad thing happen, just be confusing for debugging?
2770
3078
  0, [ newOut, newPointer ] = makeArray(scope, {
2771
3079
  rawElements: new Array(0)
@@ -2813,6 +3121,7 @@ const generateForOf = (scope, decl) => {
2813
3121
  [ Opcodes.end ],
2814
3122
  [ Opcodes.end ]
2815
3123
  ],
3124
+
2816
3125
  [TYPES.string]: [
2817
3126
  ...setType(scope, leftName, TYPES.string),
2818
3127
 
@@ -2921,6 +3230,7 @@ const generateForOf = (scope, decl) => {
2921
3230
  [ Opcodes.end ],
2922
3231
  [ Opcodes.end ]
2923
3232
  ],
3233
+
2924
3234
  [TYPES.set]: [
2925
3235
  [ Opcodes.loop, Blocktype.void ],
2926
3236
 
@@ -2959,6 +3269,106 @@ const generateForOf = (scope, decl) => {
2959
3269
  [ Opcodes.end ],
2960
3270
  [ Opcodes.end ]
2961
3271
  ],
3272
+
3273
+ ...wrapBC({
3274
+ [TYPES.uint8array]: [
3275
+ [ Opcodes.i32_add ],
3276
+
3277
+ [ Opcodes.i32_load8_u, 0, 4 ],
3278
+ Opcodes.i32_from_u
3279
+ ],
3280
+ [TYPES.uint8clampedarray]: [
3281
+ [ Opcodes.i32_add ],
3282
+
3283
+ [ Opcodes.i32_load8_u, 0, 4 ],
3284
+ Opcodes.i32_from_u
3285
+ ],
3286
+ [TYPES.int8array]: [
3287
+ [ Opcodes.i32_add ],
3288
+
3289
+ [ Opcodes.i32_load8_s, 0, 4 ],
3290
+ Opcodes.i32_from
3291
+ ],
3292
+ [TYPES.uint16array]: [
3293
+ ...number(2, Valtype.i32),
3294
+ [ Opcodes.i32_mul ],
3295
+ [ Opcodes.i32_add ],
3296
+
3297
+ [ Opcodes.i32_load16_u, 0, 4 ],
3298
+ Opcodes.i32_from_u
3299
+ ],
3300
+ [TYPES.int16array]: [
3301
+ ...number(2, Valtype.i32),
3302
+ [ Opcodes.i32_mul ],
3303
+ [ Opcodes.i32_add ],
3304
+
3305
+ [ Opcodes.i32_load16_s, 0, 4 ],
3306
+ Opcodes.i32_from
3307
+ ],
3308
+ [TYPES.uint32array]: [
3309
+ ...number(4, Valtype.i32),
3310
+ [ Opcodes.i32_mul ],
3311
+ [ Opcodes.i32_add ],
3312
+
3313
+ [ Opcodes.i32_load, 0, 4 ],
3314
+ Opcodes.i32_from_u
3315
+ ],
3316
+ [TYPES.int32array]: [
3317
+ ...number(4, Valtype.i32),
3318
+ [ Opcodes.i32_mul ],
3319
+ [ Opcodes.i32_add ],
3320
+
3321
+ [ Opcodes.i32_load, 0, 4 ],
3322
+ Opcodes.i32_from
3323
+ ],
3324
+ [TYPES.float32array]: [
3325
+ ...number(4, Valtype.i32),
3326
+ [ Opcodes.i32_mul ],
3327
+ [ Opcodes.i32_add ],
3328
+
3329
+ [ Opcodes.f32_load, 0, 4 ],
3330
+ [ Opcodes.f64_promote_f32 ]
3331
+ ],
3332
+ [TYPES.float64array]: [
3333
+ ...number(8, Valtype.i32),
3334
+ [ Opcodes.i32_mul ],
3335
+ [ Opcodes.i32_add ],
3336
+
3337
+ [ Opcodes.f64_load, 0, 4 ]
3338
+ ],
3339
+ }, {
3340
+ prelude: [
3341
+ ...setType(scope, leftName, TYPES.number),
3342
+
3343
+ [ Opcodes.loop, Blocktype.void ],
3344
+
3345
+ [ Opcodes.local_get, pointer ],
3346
+ [ Opcodes.local_get, counter ]
3347
+ ],
3348
+ postlude: [
3349
+ [ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx ],
3350
+
3351
+ [ Opcodes.block, Blocktype.void ],
3352
+ [ Opcodes.block, Blocktype.void ],
3353
+ ...generate(scope, decl.body),
3354
+ [ Opcodes.end ],
3355
+
3356
+ // increment counter by 1
3357
+ [ Opcodes.local_get, counter ],
3358
+ ...number(1, Valtype.i32),
3359
+ [ Opcodes.i32_add ],
3360
+ [ Opcodes.local_tee, counter ],
3361
+
3362
+ // loop if counter != length
3363
+ [ Opcodes.local_get, length ],
3364
+ [ Opcodes.i32_ne ],
3365
+ [ Opcodes.br_if, 1 ],
3366
+
3367
+ [ Opcodes.end ],
3368
+ [ Opcodes.end ]
3369
+ ]
3370
+ }),
3371
+
2962
3372
  default: internalThrow(scope, 'TypeError', `Tried for..of on non-iterable type`)
2963
3373
  }, Blocktype.void));
2964
3374
 
@@ -3047,13 +3457,14 @@ const generateThrow = (scope, decl) => {
3047
3457
  idx: tags.length
3048
3458
  });
3049
3459
 
3050
- let exceptId = exceptions.push({ constructor, message }) - 1;
3460
+ let exceptId = exceptions.findIndex(x => x.constructor === constructor && x.message === message);
3461
+ if (exceptId === -1) exceptId = exceptions.push({ constructor, message }) - 1;
3051
3462
 
3052
3463
  scope.exceptions ??= [];
3053
3464
  scope.exceptions.push(exceptId);
3054
3465
 
3055
3466
  return [
3056
- [ Opcodes.i32_const, signedLEB128(exceptId) ],
3467
+ ...number(exceptId, Valtype.i32),
3057
3468
  [ Opcodes.throw, tags[0].idx ]
3058
3469
  ];
3059
3470
  }
@@ -3071,6 +3482,61 @@ const generateThrow = (scope, decl) => {
3071
3482
  [ Opcodes.throw, tags[0].idx ]
3072
3483
  ];
3073
3484
  }
3485
+
3486
+ if (exceptionMode === 'stackest') {
3487
+ let message = decl.argument, constructor = null;
3488
+
3489
+ // support `throw (new)? Error(...)`
3490
+ if (message.type === 'NewExpression' || message.type === 'CallExpression') {
3491
+ constructor = decl.argument.callee;
3492
+ message = decl.argument.arguments[0];
3493
+ }
3494
+
3495
+ message ??= DEFAULT_VALUE;
3496
+
3497
+ if (tags.length === 0) tags.push({
3498
+ params: [ valtypeBinary, valtypeBinary, Valtype.i32 ],
3499
+ results: [],
3500
+ idx: tags.length
3501
+ });
3502
+
3503
+ return [
3504
+ ...(constructor == null ? number(-1) : generate(scope, constructor)),
3505
+ ...generate(scope, message),
3506
+ ...getNodeType(scope, message),
3507
+ [ Opcodes.throw, tags[0].idx ]
3508
+ ];
3509
+ }
3510
+
3511
+ if (exceptionMode === 'partial') {
3512
+ let message = decl.argument, constructor = null;
3513
+
3514
+ // support `throw (new)? Error(...)`
3515
+ if (message.type === 'NewExpression' || message.type === 'CallExpression') {
3516
+ constructor = decl.argument.callee.name;
3517
+ message = decl.argument.arguments[0];
3518
+ }
3519
+
3520
+ message ??= DEFAULT_VALUE;
3521
+
3522
+ if (tags.length === 0) tags.push({
3523
+ params: [ Valtype.i32, valtypeBinary, Valtype.i32 ],
3524
+ results: [],
3525
+ idx: tags.length
3526
+ });
3527
+
3528
+ let exceptId = exceptions.push({ constructor }) - 1;
3529
+
3530
+ scope.exceptions ??= [];
3531
+ scope.exceptions.push(exceptId);
3532
+
3533
+ return [
3534
+ ...number(exceptId, Valtype.i32),
3535
+ ...generate(scope, message),
3536
+ ...getNodeType(scope, message),
3537
+ [ Opcodes.throw, tags[0].idx ]
3538
+ ];
3539
+ }
3074
3540
  };
3075
3541
 
3076
3542
  const generateTry = (scope, decl) => {
@@ -3106,6 +3572,22 @@ const generateEmpty = (scope, decl) => {
3106
3572
  return [];
3107
3573
  };
3108
3574
 
3575
+ const generateMeta = (scope, decl) => {
3576
+ if (decl.meta.name !== 'new') return todo(scope, `meta property object ${decl.meta.name} is not supported yet`, true);
3577
+
3578
+ switch (`${decl.meta.name}.${decl.property.name}`) {
3579
+ case 'new.target': {
3580
+ scope.constr = true;
3581
+
3582
+ return [
3583
+ [ Opcodes.local_get, -1 ],
3584
+ Opcodes.i32_from_u,
3585
+ ...setLastType(scope, TYPES.boolean)
3586
+ ];
3587
+ }
3588
+ }
3589
+ };
3590
+
3109
3591
  let pages = new Map();
3110
3592
  const allocPage = (scope, reason, type) => {
3111
3593
  if (pages.has(reason)) return pages.get(reason).ind;
@@ -3423,6 +3905,19 @@ const withType = (scope, wasm, type) => [
3423
3905
  ...setLastType(scope, type)
3424
3906
  ];
3425
3907
 
3908
+ const wrapBC = (bc, { prelude = [], postlude = [] } = {}) => {
3909
+ const out = {};
3910
+ for (const x in bc) {
3911
+ out[x] = [
3912
+ ...prelude,
3913
+ ...bc[x],
3914
+ ...postlude
3915
+ ];
3916
+ }
3917
+
3918
+ return out;
3919
+ };
3920
+
3426
3921
  const generateMember = (scope, decl, _global, _name) => {
3427
3922
  const name = decl.object.name;
3428
3923
 
@@ -3445,20 +3940,10 @@ const generateMember = (scope, decl, _global, _name) => {
3445
3940
  const func = funcs.find(x => x.name === name);
3446
3941
  if (func) {
3447
3942
  const typedParams = !func.internal || builtinFuncs[name]?.typedParams;
3448
- return withType(scope, number(typedParams ? func.params.length / 2 : func.params.length), TYPES.number);
3943
+ return withType(scope, number(typedParams ? Math.floor(func.params.length / 2) : (func.constr ? (func.params.length - 1) : func.params.length)), TYPES.number);
3449
3944
  }
3450
3945
 
3451
- if (builtinFuncs[name + '$constructor']) {
3452
- const regularFunc = builtinFuncs[name];
3453
- const regularParams = regularFunc.typedParams ? (regularFunc.params.length / 2) : regularFunc.params.length;
3454
-
3455
- const constructorFunc = builtinFuncs[name + '$constructor'];
3456
- const constructorParams = constructorFunc.typedParams ? (constructorFunc.params.length / 2) : constructorFunc.params.length;
3457
-
3458
- return withType(scope, number(Math.max(regularParams, constructorParams)), TYPES.number);
3459
- }
3460
-
3461
- if (builtinFuncs[name]) return withType(scope, number(builtinFuncs[name].typedParams ? (builtinFuncs[name].params.length / 2) : builtinFuncs[name].params.length), TYPES.number);
3946
+ 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);
3462
3947
  if (importedFuncs[name]) return withType(scope, number(importedFuncs[name].params.length ?? importedFuncs[name].params), TYPES.number);
3463
3948
  if (internalConstrs[name]) return withType(scope, number(internalConstrs[name].length ?? 0), TYPES.number);
3464
3949
 
@@ -3476,7 +3961,7 @@ const generateMember = (scope, decl, _global, _name) => {
3476
3961
  const type = getNodeType(scope, decl.object);
3477
3962
  const known = knownType(scope, type);
3478
3963
  if (known != null) {
3479
- if ([ TYPES.string, TYPES.bytestring, TYPES.array ].includes(known)) return [
3964
+ if (typeHasFlag(known, TYPE_FLAGS.length)) return [
3480
3965
  ...generate(scope, decl.object),
3481
3966
  Opcodes.i32_to_u,
3482
3967
 
@@ -3488,7 +3973,9 @@ const generateMember = (scope, decl, _global, _name) => {
3488
3973
  }
3489
3974
 
3490
3975
  return [
3491
- ...typeIsOneOf(getNodeType(scope, decl.object), [ TYPES.string, TYPES.bytestring, TYPES.array ]),
3976
+ ...getNodeType(scope, decl.object),
3977
+ ...number(TYPE_FLAGS.length, Valtype.i32),
3978
+ [ Opcodes.i32_and ],
3492
3979
  [ Opcodes.if, valtypeBinary ],
3493
3980
  ...generate(scope, decl.object),
3494
3981
  Opcodes.i32_to_u,
@@ -3505,7 +3992,7 @@ const generateMember = (scope, decl, _global, _name) => {
3505
3992
  }
3506
3993
 
3507
3994
  // todo: generate this array procedurally during builtinFuncs creation
3508
- if (['size', 'description'].includes(decl.property.name)) {
3995
+ if (['size', 'description', 'byteLength'].includes(decl.property.name)) {
3509
3996
  const bc = {};
3510
3997
  const cands = Object.keys(builtinFuncs).filter(x => x.startsWith('__') && x.endsWith('_prototype_' + decl.property.name + '$get'));
3511
3998
 
@@ -3537,7 +4024,9 @@ const generateMember = (scope, decl, _global, _name) => {
3537
4024
  // // todo: we should only do this for strings but we don't know at compile-time :(
3538
4025
  // hack: this is naughty and will break things!
3539
4026
  let newOut = number(0, Valtype.i32), newPointer = number(0, Valtype.i32);
3540
- if (pages.hasAnyString && knownType(scope, getNodeType(scope, decl.object)) !== TYPES.array) {
4027
+
4028
+ const known = knownType(scope, getNodeType(scope, decl.object));
4029
+ if ((known === TYPES.string || known === TYPES.bytestring) || (pages.hasAnyString && known == null)) {
3541
4030
  // todo: we use i16 even for bytestrings which should not make a bad thing happen, just be confusing for debugging?
3542
4031
  0, [ newOut, newPointer ] = makeArray(scope, {
3543
4032
  rawElements: new Array(0)
@@ -3611,6 +4100,82 @@ const generateMember = (scope, decl, _global, _name) => {
3611
4100
  ...setLastType(scope, TYPES.bytestring)
3612
4101
  ],
3613
4102
 
4103
+ ...wrapBC({
4104
+ [TYPES.uint8array]: [
4105
+ [ Opcodes.i32_add ],
4106
+
4107
+ [ Opcodes.i32_load8_u, 0, 4 ],
4108
+ Opcodes.i32_from_u
4109
+ ],
4110
+ [TYPES.uint8clampedarray]: [
4111
+ [ Opcodes.i32_add ],
4112
+
4113
+ [ Opcodes.i32_load8_u, 0, 4 ],
4114
+ Opcodes.i32_from_u
4115
+ ],
4116
+ [TYPES.int8array]: [
4117
+ [ Opcodes.i32_add ],
4118
+
4119
+ [ Opcodes.i32_load8_s, 0, 4 ],
4120
+ Opcodes.i32_from
4121
+ ],
4122
+ [TYPES.uint16array]: [
4123
+ ...number(2, Valtype.i32),
4124
+ [ Opcodes.i32_mul ],
4125
+ [ Opcodes.i32_add ],
4126
+
4127
+ [ Opcodes.i32_load16_u, 0, 4 ],
4128
+ Opcodes.i32_from_u
4129
+ ],
4130
+ [TYPES.int16array]: [
4131
+ ...number(2, Valtype.i32),
4132
+ [ Opcodes.i32_mul ],
4133
+ [ Opcodes.i32_add ],
4134
+
4135
+ [ Opcodes.i32_load16_s, 0, 4 ],
4136
+ Opcodes.i32_from
4137
+ ],
4138
+ [TYPES.uint32array]: [
4139
+ ...number(4, Valtype.i32),
4140
+ [ Opcodes.i32_mul ],
4141
+ [ Opcodes.i32_add ],
4142
+
4143
+ [ Opcodes.i32_load, 0, 4 ],
4144
+ Opcodes.i32_from_u
4145
+ ],
4146
+ [TYPES.int32array]: [
4147
+ ...number(4, Valtype.i32),
4148
+ [ Opcodes.i32_mul ],
4149
+ [ Opcodes.i32_add ],
4150
+
4151
+ [ Opcodes.i32_load, 0, 4 ],
4152
+ Opcodes.i32_from
4153
+ ],
4154
+ [TYPES.float32array]: [
4155
+ ...number(4, Valtype.i32),
4156
+ [ Opcodes.i32_mul ],
4157
+ [ Opcodes.i32_add ],
4158
+
4159
+ [ Opcodes.f32_load, 0, 4 ],
4160
+ [ Opcodes.f64_promote_f32 ]
4161
+ ],
4162
+ [TYPES.float64array]: [
4163
+ ...number(8, Valtype.i32),
4164
+ [ Opcodes.i32_mul ],
4165
+ [ Opcodes.i32_add ],
4166
+
4167
+ [ Opcodes.f64_load, 0, 4 ]
4168
+ ],
4169
+ }, {
4170
+ prelude: [
4171
+ ...object,
4172
+ Opcodes.i32_to_u,
4173
+ ...property,
4174
+ Opcodes.i32_to_u
4175
+ ],
4176
+ postlude: setLastType(scope, TYPES.number)
4177
+ }),
4178
+
3614
4179
  default: internalThrow(scope, 'TypeError', 'Member expression is not supported for non-string non-array yet', true)
3615
4180
  });
3616
4181
  };
@@ -3633,7 +4198,7 @@ const objectHack = node => {
3633
4198
  if (!objectName) objectName = objectHack(node.object)?.name?.slice?.(2);
3634
4199
 
3635
4200
  // if .name or .length, give up (hack within a hack!)
3636
- if (['name', 'length', 'size', 'description'].includes(node.property.name)) {
4201
+ if (['name', 'length', 'size', 'description', 'byteLength'].includes(node.property.name)) {
3637
4202
  node.object = objectHack(node.object);
3638
4203
  return;
3639
4204
  }
@@ -3686,8 +4251,8 @@ const generateFunc = (scope, decl) => {
3686
4251
 
3687
4252
  if (typedInput && decl.returnType) {
3688
4253
  const { type } = extractTypeAnnotation(decl.returnType);
3689
- // if (type != null && !Prefs.indirectCalls) {
3690
- if (type != null) {
4254
+ if (type != null && !Prefs.indirectCalls) {
4255
+ // if (type != null) {
3691
4256
  func.returnType = type;
3692
4257
  func.returns = [ valtypeBinary ];
3693
4258
  }
@@ -3788,7 +4353,7 @@ const internalConstrs = {
3788
4353
 
3789
4354
  // todo: check in wasm instead of here
3790
4355
  const literalValue = arg.value ?? 0;
3791
- if (literalValue < 0 || !Number.isFinite(literalValue) || literalValue > 4294967295) return internalThrow(scope, 'RangeThrow', 'Invalid array length', true);
4356
+ if (literalValue < 0 || !Number.isFinite(literalValue) || literalValue > 4294967295) return internalThrow(scope, 'RangeError', 'Invalid array length', true);
3792
4357
 
3793
4358
  return [
3794
4359
  ...out,