porffor 0.17.0-30b9c5fa5 → 0.17.0-3c2d70d66

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),
1669
+
1670
+ // call regex func
1671
+ [ Opcodes.call, idx ],
1672
+ Opcodes.i32_from,
1673
+
1674
+ ...setLastType(scope, Rhemyn.types[protoName])
1675
+ ];
1676
+ }
1642
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,
1751
1802
 
1752
- // TODO: error better
1753
- default: internalThrow(scope, 'TypeError', `'${protoName}' proto func tried to be called on a type without an impl`)
1754
- }, 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
+ ];
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;
@@ -2288,9 +2403,120 @@ const generateVar = (scope, decl) => {
2288
2403
  const global = topLevel || decl._bare;
2289
2404
 
2290
2405
  for (const x of decl.declarations) {
2291
- 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
+ }
2292
2457
 
2293
- if (!name) return todo(scope, 'destructuring is not supported yet');
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
+ });
2487
+
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')
2294
2520
 
2295
2521
  if (x.init && isFuncType(x.init.type)) {
2296
2522
  // hack for let a = function () { ... }
@@ -2325,7 +2551,9 @@ const generateVar = (scope, decl) => {
2325
2551
  // hack to set local as pointer before
2326
2552
  out.push(...number(scope.arrays.get(name)), [ global ? Opcodes.global_set : Opcodes.local_set, idx ]);
2327
2553
  if (generated.at(-1) == Opcodes.i32_from_u) generated.pop();
2328
- generated.pop();
2554
+ // generated.pop();
2555
+ generated.push([ Opcodes.drop ]);
2556
+
2329
2557
  out = out.concat(generated);
2330
2558
  } else {
2331
2559
  out = out.concat(generated);
@@ -2392,8 +2620,8 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
2392
2620
 
2393
2621
  // arr[i]
2394
2622
  if (decl.left.type === 'MemberExpression' && decl.left.computed) {
2395
- const newValueTmp = localTmp(scope, '__member_setter_val_tmp');
2396
- 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);
2397
2625
 
2398
2626
  return [
2399
2627
  ...typeSwitch(scope, getNodeType(scope, decl.left.object), {
@@ -2405,11 +2633,11 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
2405
2633
  ...generate(scope, decl.left.property),
2406
2634
  Opcodes.i32_to_u,
2407
2635
 
2408
- // turn into byte offset by * valtypeSize (4 for i32, 8 for i64/f64)
2636
+ // turn into byte offset by * valtypeSize + 1
2409
2637
  ...number(ValtypeSize[valtype] + 1, Valtype.i32),
2410
2638
  [ Opcodes.i32_mul ],
2411
2639
  [ Opcodes.i32_add ],
2412
- ...(op === '=' ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
2640
+ [ Opcodes.local_tee, pointerTmp ],
2413
2641
 
2414
2642
  ...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
2415
2643
  [ Opcodes.local_get, pointerTmp ],
@@ -2419,7 +2647,11 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
2419
2647
  [ Opcodes.i32_load8_u, 0, ValtypeSize.i32 + ValtypeSize[valtype] ]
2420
2648
  ], getNodeType(scope, decl.right), false, name, true)),
2421
2649
  [ Opcodes.local_tee, newValueTmp ],
2422
- [ Opcodes.store, 0, ValtypeSize.i32 ]
2650
+ [ Opcodes.store, 0, ValtypeSize.i32 ],
2651
+
2652
+ [ Opcodes.local_get, pointerTmp ],
2653
+ ...getNodeType(scope, decl),
2654
+ [ Opcodes.i32_store8, 0, ValtypeSize.i32 + ValtypeSize[valtype] ],
2423
2655
  ],
2424
2656
 
2425
2657
  ...wrapBC({
@@ -3457,6 +3689,22 @@ const generateEmpty = (scope, decl) => {
3457
3689
  return [];
3458
3690
  };
3459
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
+
3460
3708
  let pages = new Map();
3461
3709
  const allocPage = (scope, reason, type) => {
3462
3710
  if (pages.has(reason)) return pages.get(reason).ind;
@@ -3809,20 +4057,10 @@ const generateMember = (scope, decl, _global, _name) => {
3809
4057
  const func = funcs.find(x => x.name === name);
3810
4058
  if (func) {
3811
4059
  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);
4060
+ return withType(scope, number(typedParams ? Math.floor(func.params.length / 2) : (func.constr ? (func.params.length - 1) : func.params.length)), TYPES.number);
3823
4061
  }
3824
4062
 
3825
- 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);
3826
4064
  if (importedFuncs[name]) return withType(scope, number(importedFuncs[name].params.length ?? importedFuncs[name].params), TYPES.number);
3827
4065
  if (internalConstrs[name]) return withType(scope, number(internalConstrs[name].length ?? 0), TYPES.number);
3828
4066
 
@@ -3871,7 +4109,7 @@ const generateMember = (scope, decl, _global, _name) => {
3871
4109
  }
3872
4110
 
3873
4111
  // todo: generate this array procedurally during builtinFuncs creation
3874
- if (['size', 'description'].includes(decl.property.name)) {
4112
+ if (['size', 'description', 'byteLength'].includes(decl.property.name)) {
3875
4113
  const bc = {};
3876
4114
  const cands = Object.keys(builtinFuncs).filter(x => x.startsWith('__') && x.endsWith('_prototype_' + decl.property.name + '$get'));
3877
4115
 
@@ -4077,7 +4315,7 @@ const objectHack = node => {
4077
4315
  if (!objectName) objectName = objectHack(node.object)?.name?.slice?.(2);
4078
4316
 
4079
4317
  // if .name or .length, give up (hack within a hack!)
4080
- if (['name', 'length', 'size', 'description'].includes(node.property.name)) {
4318
+ if (['name', 'length', 'size', 'description', 'byteLength'].includes(node.property.name)) {
4081
4319
  node.object = objectHack(node.object);
4082
4320
  return;
4083
4321
  }
@@ -4130,8 +4368,8 @@ const generateFunc = (scope, decl) => {
4130
4368
 
4131
4369
  if (typedInput && decl.returnType) {
4132
4370
  const { type } = extractTypeAnnotation(decl.returnType);
4133
- // if (type != null && !Prefs.indirectCalls) {
4134
- if (type != null) {
4371
+ if (type != null && !Prefs.indirectCalls) {
4372
+ // if (type != null) {
4135
4373
  func.returnType = type;
4136
4374
  func.returns = [ valtypeBinary ];
4137
4375
  }