porffor 0.17.0-05070e1f0 → 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.
@@ -123,6 +123,9 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
123
123
  case 'EmptyStatement':
124
124
  return generateEmpty(scope, decl);
125
125
 
126
+ case 'MetaProperty':
127
+ return generateMeta(scope, decl);
128
+
126
129
  case 'ConditionalExpression':
127
130
  return generateConditional(scope, decl);
128
131
 
@@ -295,10 +298,10 @@ const generateIdent = (scope, decl) => {
295
298
  }
296
299
 
297
300
  // todo: enable this by default in future
298
- // if (!Object.hasOwn(funcIndex, name) && Object.hasOwn(builtinFuncs, name)) {
299
- // includeBuiltin(scope, name);
300
- // return number(funcIndex[name] - importedFuncs.length);
301
- // }
301
+ if (!Object.hasOwn(funcIndex, name) && Object.hasOwn(builtinFuncs, name)) {
302
+ includeBuiltin(scope, name);
303
+ return number(funcIndex[name] - importedFuncs.length);
304
+ }
302
305
 
303
306
  if (isExistingProtoFunc(name) || Object.hasOwn(internalConstrs, name) || Object.hasOwn(builtinFuncs, name)) {
304
307
  // todo: return an actual something
@@ -1026,12 +1029,13 @@ const asmFuncToAsm = (func, scope) => {
1026
1029
  idx = funcIndex[n];
1027
1030
  }
1028
1031
 
1032
+ if (idx == null) throw new Error(`builtin('${n}') failed to find a func (inside ${scope.name})`);
1029
1033
  return idx;
1030
1034
  }
1031
1035
  });
1032
1036
  };
1033
1037
 
1034
- 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 }) => {
1035
1039
  const existing = funcs.find(x => x.name === name);
1036
1040
  if (existing) return existing;
1037
1041
 
@@ -1058,13 +1062,17 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
1058
1062
  returnType,
1059
1063
  internal: true,
1060
1064
  index: currentFuncIndex++,
1061
- table
1065
+ table,
1066
+ constr
1062
1067
  };
1063
1068
 
1064
1069
  funcs.push(func);
1065
1070
  funcIndex[name] = func.index;
1066
1071
 
1067
- 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
+ }
1068
1076
 
1069
1077
  let baseGlobalIdx, i = 0;
1070
1078
  for (const type of globalTypes) {
@@ -1085,15 +1093,35 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
1085
1093
 
1086
1094
  if (table) {
1087
1095
  for (const inst of wasm) {
1088
- if (inst[0] === Opcodes.i32_load16_u && inst.at(-1) === 'read_argc') {
1096
+ if (inst.at(-1) === 'read func lut') {
1089
1097
  inst.splice(2, 99);
1090
- inst.push(...unsignedLEB128(allocPage({}, 'func argc lut') * pageSize));
1098
+ inst.push(...unsignedLEB128(allocPage({}, 'func lut') * pageSize));
1091
1099
  }
1092
1100
  }
1093
1101
 
1094
1102
  funcs.table = true;
1095
1103
  }
1096
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
+
1097
1125
  func.wasm = wasm;
1098
1126
 
1099
1127
  return func;
@@ -1220,20 +1248,7 @@ const getNodeType = (scope, node) => {
1220
1248
  return TYPES.number;
1221
1249
  }
1222
1250
 
1223
- if (node.type === 'NewExpression' && builtinFuncs[name + '$constructor']) {
1224
- if (builtinFuncs[name + '$constructor'].typedReturns) {
1225
- if (scope.locals['#last_type']) return getLastType(scope);
1226
-
1227
- // presume
1228
- // todo: warn here?
1229
- return TYPES.number;
1230
- }
1231
-
1232
- return builtinFuncs[name + '$constructor'].returnType ?? TYPES.number;
1233
- }
1234
-
1235
1251
  const func = funcs.find(x => x.name === name);
1236
-
1237
1252
  if (func) {
1238
1253
  if (func.returnType != null) return func.returnType;
1239
1254
  }
@@ -1531,7 +1546,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1531
1546
  name = func.name;
1532
1547
  }
1533
1548
 
1534
- if (name === 'eval' && decl.arguments[0]?.type === 'Literal') {
1549
+ if (!decl._new && name === 'eval' && decl.arguments[0]?.type === 'Literal') {
1535
1550
  // literal eval hack
1536
1551
  const code = decl.arguments[0]?.value ?? '';
1537
1552
 
@@ -1574,7 +1589,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1574
1589
 
1575
1590
  let protoName, target;
1576
1591
  // ident.func()
1577
- if (name && name.startsWith('__')) {
1592
+ if (!decl._new && name && name.startsWith('__')) {
1578
1593
  const spl = name.slice(2).split('_');
1579
1594
 
1580
1595
  protoName = spl[spl.length - 1];
@@ -1587,12 +1602,12 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1587
1602
  }
1588
1603
 
1589
1604
  // literal.func()
1590
- if (!name && decl.callee.type === 'MemberExpression') {
1605
+ if (!decl._new && !name && decl.callee.type === 'MemberExpression') {
1591
1606
  // megahack for /regex/.func()
1592
1607
  const funcName = decl.callee.property.name;
1593
- if (decl.callee.object.regex && Object.hasOwn(Rhemyn, funcName)) {
1608
+ if (decl.callee.object.regex && ['test'].includes(funcName)) {
1594
1609
  const regex = decl.callee.object.regex.pattern;
1595
- const rhemynName = `regex_${funcName}_${regex}`;
1610
+ const rhemynName = `regex_${funcName}_${sanitize(regex)}`;
1596
1611
 
1597
1612
  if (!funcIndex[rhemynName]) {
1598
1613
  const func = Rhemyn[funcName](regex, currentFuncIndex++, rhemynName);
@@ -1613,7 +1628,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1613
1628
  [ Opcodes.call, idx ],
1614
1629
  Opcodes.i32_from_u,
1615
1630
 
1616
- ...setLastType(scope, TYPES.boolean)
1631
+ ...setLastType(scope, Rhemyn.types[funcName])
1617
1632
  ];
1618
1633
  }
1619
1634
 
@@ -1622,27 +1637,56 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1622
1637
  target = decl.callee.object;
1623
1638
  }
1624
1639
 
1625
- // if (protoName && baseType === TYPES.string && Rhemyn[protoName]) {
1626
- // 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 ],
1627
1648
 
1628
- // funcIndex[func.name] = func.index;
1629
- // funcs.push(func);
1649
+ ...number(Rhemyn.types[protoName] === TYPES.number ? -1 : 0),
1650
+ ...setLastType(scope, Rhemyn.types[protoName])
1651
+ ];
1630
1652
 
1631
- // return [
1632
- // generate(scope, decl.callee.object)
1653
+ const rhemynName = `regex_${protoName}_${sanitize(regex)}`;
1633
1654
 
1634
- // // call regex func
1635
- // [ Opcodes.call, func.index ],
1636
- // Opcodes.i32_from_u
1637
- // ];
1638
- // }
1655
+ if (!funcIndex[rhemynName]) {
1656
+ const func = Rhemyn[protoName](regex, currentFuncIndex++, rhemynName);
1657
+ func.internal = true;
1639
1658
 
1640
- if (protoName) {
1641
- 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),
1642
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 = {};
1643
1679
  const builtinProtoCands = Object.keys(builtinFuncs).filter(x => x.startsWith('__') && x.endsWith('_prototype_' + protoName));
1644
1680
 
1645
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
+
1646
1690
  for (const x of builtinProtoCands) {
1647
1691
  const type = TYPES[x.split('_prototype_')[0].slice(2).toLowerCase()];
1648
1692
  if (type == null) continue;
@@ -1652,7 +1696,14 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1652
1696
  type: 'Identifier',
1653
1697
  name: x
1654
1698
  },
1655
- arguments: [ target, ...decl.arguments ],
1699
+ arguments: [
1700
+ {
1701
+ type: 'Identifier',
1702
+ name: '#proto_target'
1703
+ },
1704
+
1705
+ ...decl.arguments
1706
+ ],
1656
1707
  _protoInternalCall: true
1657
1708
  });
1658
1709
  }
@@ -1746,29 +1797,37 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1746
1797
  }
1747
1798
 
1748
1799
  if (Object.keys(protoBC).length > 0) {
1749
- return typeSwitch(scope, getNodeType(scope, target), {
1750
- ...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,
1751
1805
 
1752
- // TODO: error better
1753
- default: internalThrow(scope, 'TypeError', `'${protoName}' proto func tried to be called on a type without an impl`)
1754
- }, 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
+ ];
1755
1810
  }
1756
1811
  }
1757
1812
 
1758
- // TODO: only allows callee as literal
1759
- 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})`);
1760
1815
 
1761
1816
  let idx = funcIndex[name] ?? importedFuncs[name];
1762
1817
  if (idx === undefined && builtinFuncs[name]) {
1763
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);
1764
1820
 
1765
1821
  includeBuiltin(scope, name);
1766
1822
  idx = funcIndex[name];
1767
1823
  }
1768
1824
 
1769
- 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
+ }
1770
1829
 
1771
- if (idx === undefined && name.startsWith('__Porffor_wasm_')) {
1830
+ if (idx === undefined && !decl._new && name.startsWith('__Porffor_wasm_')) {
1772
1831
  const wasmOps = {
1773
1832
  // pointer, align, offset
1774
1833
  i32_load: { imms: 2, args: [ true ], returns: 1 },
@@ -1823,7 +1882,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1823
1882
 
1824
1883
  const indirectMode = Prefs.indirectCallMode ?? 'vararg';
1825
1884
  // options: vararg, strict
1826
- // - strict: simpler, smaller size usage, no func argc lut needed.
1885
+ // - strict: simpler, smaller size usage, no func lut needed.
1827
1886
  // ONLY works when arg count of call == arg count of function being called
1828
1887
  // - vararg: large size usage, cursed.
1829
1888
  // works when arg count of call != arg count of function being called*
@@ -1833,8 +1892,6 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1833
1892
  scope.table = true;
1834
1893
 
1835
1894
  let args = decl.arguments;
1836
- let out = [];
1837
-
1838
1895
  let locals = [];
1839
1896
 
1840
1897
  if (indirectMode === 'vararg') {
@@ -1898,24 +1955,67 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1898
1955
  // *for argc 0-3, in future (todo:) the max number should be
1899
1956
  // dynamically changed to the max argc of any func in the js file.
1900
1957
 
1901
- 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);
1902
1960
 
1903
1961
  const gen = argc => {
1904
- const out = [];
1962
+ const argsOut = [];
1905
1963
  for (let i = 0; i < argc; i++) {
1906
- out.push(
1964
+ argsOut.push(
1907
1965
  [ Opcodes.local_get, locals[i][0] ],
1908
1966
  [ Opcodes.local_get, locals[i][1] ]
1909
1967
  );
1910
1968
  }
1911
1969
 
1912
- out.push(
1913
- [ Opcodes.local_get, funcLocal ],
1914
- [ Opcodes.call_indirect, argc, 0 ],
1915
- ...setLastType(scope)
1916
- )
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
+ ];
1917
1980
 
1918
- 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
+ );
1919
2019
  };
1920
2020
 
1921
2021
  const tableBc = {};
@@ -1933,13 +2033,32 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1933
2033
  Opcodes.i32_to_u,
1934
2034
  [ Opcodes.local_set, funcLocal ],
1935
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
+
1936
2056
  ...brTable([
1937
2057
  // get argc of func we are calling
1938
2058
  [ Opcodes.local_get, funcLocal ],
1939
- ...number(ValtypeSize.i16, Valtype.i32),
2059
+ ...number(3, Valtype.i32),
1940
2060
  [ Opcodes.i32_mul ],
1941
-
1942
- [ 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' ]
1943
2062
  ], tableBc, valtypeBinary)
1944
2063
  ],
1945
2064
  default: internalThrow(scope, 'TypeError', `${unhackName(name)} is not a function`, true)
@@ -1953,7 +2072,16 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1953
2072
  const userFunc = func && !func.internal;
1954
2073
  const typedParams = userFunc || builtinFuncs[name]?.typedParams;
1955
2074
  const typedReturns = (userFunc && func.returnType == null) || builtinFuncs[name]?.typedReturns;
1956
- 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);
1957
2085
 
1958
2086
  let args = decl.arguments;
1959
2087
  if (func && args.length < paramCount) {
@@ -1968,7 +2096,6 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1968
2096
 
1969
2097
  if (func && func.throws) scope.throws = true;
1970
2098
 
1971
- let out = [];
1972
2099
  for (let i = 0; i < args.length; i++) {
1973
2100
  const arg = args[i];
1974
2101
  out = out.concat(generate(scope, arg));
@@ -1981,13 +2108,13 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1981
2108
  }
1982
2109
 
1983
2110
  if (valtypeBinary !== Valtype.i32 &&
1984
- (func && func.params[i * (typedParams ? 2 : 1)] === Valtype.i32)
2111
+ (func && func.params[paramOffset + i * (typedParams ? 2 : 1)] === Valtype.i32)
1985
2112
  ) {
1986
2113
  out.push(Opcodes.i32_to);
1987
2114
  }
1988
2115
 
1989
2116
  if (valtypeBinary === Valtype.i32 &&
1990
- (func && func.params[i * (typedParams ? 2 : 1)] === Valtype.f64)
2117
+ (func && func.params[paramOffset + i * (typedParams ? 2 : 1)] === Valtype.f64)
1991
2118
  ) {
1992
2119
  out.push([ Opcodes.f64_convert_i32_s ]);
1993
2120
  }
@@ -2020,32 +2147,10 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
2020
2147
  return out;
2021
2148
  };
2022
2149
 
2023
- const generateNew = (scope, decl, _global, _name) => {
2024
- // hack: basically treat this as a normal call for builtins for now
2025
- const name = mapName(decl.callee.name);
2026
-
2027
- if (internalConstrs[name] && !internalConstrs[name].notConstr) return internalConstrs[name].generate(scope, decl, _global, _name);
2028
-
2029
- if (builtinFuncs[name + '$constructor']) {
2030
- // custom ...$constructor override builtin func
2031
- return generateCall(scope, {
2032
- ...decl,
2033
- callee: {
2034
- type: 'Identifier',
2035
- name: name + '$constructor'
2036
- }
2037
- }, _global, _name);
2038
- }
2039
-
2040
- if (
2041
- (builtinFuncs[name] && !builtinFuncs[name].constr) ||
2042
- (internalConstrs[name] && builtinFuncs[name].notConstr)
2043
- ) return internalThrow(scope, 'TypeError', `${name} is not a constructor`, true);
2044
-
2045
- if (!builtinFuncs[name]) return todo(scope, `new statement is not supported yet`, true); // return todo(scope, `new statement is not supported yet (new ${unhackName(name)})`);
2046
-
2047
- return generateCall(scope, decl, _global, _name);
2048
- };
2150
+ const generateNew = (scope, decl, _global, _name) => generateCall(scope, {
2151
+ ...decl,
2152
+ _new: true
2153
+ }, _global, _name);
2049
2154
 
2050
2155
  // bad hack for undefined and null working without additional logic
2051
2156
  const DEFAULT_VALUE = {
@@ -2053,6 +2158,16 @@ const DEFAULT_VALUE = {
2053
2158
  name: 'undefined'
2054
2159
  };
2055
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
+
2056
2171
  const unhackName = name => {
2057
2172
  if (name.startsWith('__')) return name.slice(2).replaceAll('_', '.');
2058
2173
  return name;
@@ -3457,6 +3572,22 @@ const generateEmpty = (scope, decl) => {
3457
3572
  return [];
3458
3573
  };
3459
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
+
3460
3591
  let pages = new Map();
3461
3592
  const allocPage = (scope, reason, type) => {
3462
3593
  if (pages.has(reason)) return pages.get(reason).ind;
@@ -3809,20 +3940,10 @@ const generateMember = (scope, decl, _global, _name) => {
3809
3940
  const func = funcs.find(x => x.name === name);
3810
3941
  if (func) {
3811
3942
  const typedParams = !func.internal || builtinFuncs[name]?.typedParams;
3812
- return withType(scope, number(typedParams ? func.params.length / 2 : func.params.length), TYPES.number);
3813
- }
3814
-
3815
- if (builtinFuncs[name + '$constructor']) {
3816
- const regularFunc = builtinFuncs[name];
3817
- const regularParams = regularFunc.typedParams ? (regularFunc.params.length / 2) : regularFunc.params.length;
3818
-
3819
- const constructorFunc = builtinFuncs[name + '$constructor'];
3820
- const constructorParams = constructorFunc.typedParams ? (constructorFunc.params.length / 2) : constructorFunc.params.length;
3821
-
3822
- return withType(scope, number(Math.max(regularParams, constructorParams)), 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);
3823
3944
  }
3824
3945
 
3825
- 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);
3826
3947
  if (importedFuncs[name]) return withType(scope, number(importedFuncs[name].params.length ?? importedFuncs[name].params), TYPES.number);
3827
3948
  if (internalConstrs[name]) return withType(scope, number(internalConstrs[name].length ?? 0), TYPES.number);
3828
3949
 
@@ -3871,7 +3992,7 @@ const generateMember = (scope, decl, _global, _name) => {
3871
3992
  }
3872
3993
 
3873
3994
  // todo: generate this array procedurally during builtinFuncs creation
3874
- if (['size', 'description'].includes(decl.property.name)) {
3995
+ if (['size', 'description', 'byteLength'].includes(decl.property.name)) {
3875
3996
  const bc = {};
3876
3997
  const cands = Object.keys(builtinFuncs).filter(x => x.startsWith('__') && x.endsWith('_prototype_' + decl.property.name + '$get'));
3877
3998
 
@@ -4077,7 +4198,7 @@ const objectHack = node => {
4077
4198
  if (!objectName) objectName = objectHack(node.object)?.name?.slice?.(2);
4078
4199
 
4079
4200
  // if .name or .length, give up (hack within a hack!)
4080
- if (['name', 'length', 'size', 'description'].includes(node.property.name)) {
4201
+ if (['name', 'length', 'size', 'description', 'byteLength'].includes(node.property.name)) {
4081
4202
  node.object = objectHack(node.object);
4082
4203
  return;
4083
4204
  }
@@ -4130,8 +4251,8 @@ const generateFunc = (scope, decl) => {
4130
4251
 
4131
4252
  if (typedInput && decl.returnType) {
4132
4253
  const { type } = extractTypeAnnotation(decl.returnType);
4133
- // if (type != null && !Prefs.indirectCalls) {
4134
- if (type != null) {
4254
+ if (type != null && !Prefs.indirectCalls) {
4255
+ // if (type != null) {
4135
4256
  func.returnType = type;
4136
4257
  func.returns = [ valtypeBinary ];
4137
4258
  }