porffor 0.17.0-30b9c5fa5 → 0.17.0-3d806f394
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.
- package/README.md +6 -4
- package/compiler/assemble.js +19 -4
- package/compiler/builtins/array.ts +102 -9
- package/compiler/builtins/date.ts +104 -106
- package/compiler/builtins/error.js +2 -5
- package/compiler/builtins/set.ts +4 -5
- package/compiler/builtins/string_f64.ts +4 -6
- package/compiler/builtins/symbol.ts +2 -1
- package/compiler/builtins/typedarray.js +58 -6
- package/compiler/builtins/z_ecma262.ts +1 -0
- package/compiler/builtins.js +2 -2
- package/compiler/codegen.js +379 -123
- package/compiler/generated_builtins.js +2703 -702
- package/compiler/opt.js +3 -0
- package/compiler/precompile.js +4 -2
- package/compiler/prototype.js +0 -69
- package/package.json +1 -1
- package/rhemyn/compile.js +49 -16
package/compiler/codegen.js
CHANGED
@@ -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
|
-
|
299
|
-
|
300
|
-
|
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')
|
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
|
1096
|
+
if (inst.at(-1) === 'read func lut') {
|
1089
1097
|
inst.splice(2, 99);
|
1090
|
-
inst.push(...unsignedLEB128(allocPage({}, 'func
|
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 &&
|
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,
|
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
|
-
|
1626
|
-
|
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
|
-
|
1629
|
-
|
1649
|
+
...number(Rhemyn.types[protoName] === TYPES.number ? -1 : 0),
|
1650
|
+
...setLastType(scope, Rhemyn.types[protoName])
|
1651
|
+
];
|
1630
1652
|
|
1631
|
-
|
1632
|
-
// generate(scope, decl.callee.object)
|
1653
|
+
const rhemynName = `regex_${protoName}_${sanitize(regex)}`;
|
1633
1654
|
|
1634
|
-
|
1635
|
-
|
1636
|
-
|
1637
|
-
// ];
|
1638
|
-
// }
|
1655
|
+
if (!funcIndex[rhemynName]) {
|
1656
|
+
const func = Rhemyn[protoName](regex, currentFuncIndex++, rhemynName);
|
1657
|
+
func.internal = true;
|
1639
1658
|
|
1640
|
-
|
1641
|
-
|
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,
|
1642
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: [
|
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
|
1750
|
-
...
|
1800
|
+
return [
|
1801
|
+
...out,
|
1751
1802
|
|
1752
|
-
|
1753
|
-
|
1754
|
-
|
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
|
1759
|
-
if (!name) return todo(scope, `only
|
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])
|
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
|
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,
|
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
|
1962
|
+
const argsOut = [];
|
1905
1963
|
for (let i = 0; i < argc; i++) {
|
1906
|
-
|
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
|
-
|
1913
|
-
[ Opcodes.local_get,
|
1914
|
-
|
1915
|
-
|
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
|
-
|
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(
|
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,14 +2072,35 @@ 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
|
-
|
1957
|
-
|
1958
|
-
let
|
1959
|
-
if (func &&
|
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);
|
2085
|
+
|
2086
|
+
let args = [...decl.arguments];
|
2087
|
+
if (func && !func.hasRestArgument && args.length < paramCount) {
|
1960
2088
|
// too little args, push undefineds
|
1961
2089
|
args = args.concat(new Array(paramCount - args.length).fill(DEFAULT_VALUE));
|
1962
2090
|
}
|
1963
2091
|
|
2092
|
+
if (func && func.hasRestArgument) {
|
2093
|
+
if (args.length < paramCount) {
|
2094
|
+
args = args.concat(new Array(paramCount - 1 - args.length).fill(DEFAULT_VALUE));
|
2095
|
+
}
|
2096
|
+
const restArgs = args.slice(paramCount - 1);
|
2097
|
+
args = args.slice(0, paramCount - 1);
|
2098
|
+
args.push({
|
2099
|
+
type: 'ArrayExpression',
|
2100
|
+
elements: restArgs
|
2101
|
+
})
|
2102
|
+
}
|
2103
|
+
|
1964
2104
|
if (func && args.length > paramCount) {
|
1965
2105
|
// too many args, slice extras off
|
1966
2106
|
args = args.slice(0, paramCount);
|
@@ -1968,7 +2108,6 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1968
2108
|
|
1969
2109
|
if (func && func.throws) scope.throws = true;
|
1970
2110
|
|
1971
|
-
let out = [];
|
1972
2111
|
for (let i = 0; i < args.length; i++) {
|
1973
2112
|
const arg = args[i];
|
1974
2113
|
out = out.concat(generate(scope, arg));
|
@@ -1981,13 +2120,13 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1981
2120
|
}
|
1982
2121
|
|
1983
2122
|
if (valtypeBinary !== Valtype.i32 &&
|
1984
|
-
(func && func.params[i * (typedParams ? 2 : 1)] === Valtype.i32)
|
2123
|
+
(func && func.params[paramOffset + i * (typedParams ? 2 : 1)] === Valtype.i32)
|
1985
2124
|
) {
|
1986
2125
|
out.push(Opcodes.i32_to);
|
1987
2126
|
}
|
1988
2127
|
|
1989
2128
|
if (valtypeBinary === Valtype.i32 &&
|
1990
|
-
(func && func.params[i * (typedParams ? 2 : 1)] === Valtype.f64)
|
2129
|
+
(func && func.params[paramOffset + i * (typedParams ? 2 : 1)] === Valtype.f64)
|
1991
2130
|
) {
|
1992
2131
|
out.push([ Opcodes.f64_convert_i32_s ]);
|
1993
2132
|
}
|
@@ -2020,32 +2159,10 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
2020
2159
|
return out;
|
2021
2160
|
};
|
2022
2161
|
|
2023
|
-
const generateNew = (scope, decl, _global, _name) => {
|
2024
|
-
|
2025
|
-
|
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
|
-
};
|
2162
|
+
const generateNew = (scope, decl, _global, _name) => generateCall(scope, {
|
2163
|
+
...decl,
|
2164
|
+
_new: true
|
2165
|
+
}, _global, _name);
|
2049
2166
|
|
2050
2167
|
// bad hack for undefined and null working without additional logic
|
2051
2168
|
const DEFAULT_VALUE = {
|
@@ -2053,6 +2170,16 @@ const DEFAULT_VALUE = {
|
|
2053
2170
|
name: 'undefined'
|
2054
2171
|
};
|
2055
2172
|
|
2173
|
+
const codeToSanitizedStr = code => {
|
2174
|
+
let out = '';
|
2175
|
+
while (code > 0) {
|
2176
|
+
out += String.fromCharCode(97 + code % 26);
|
2177
|
+
code -= 26;
|
2178
|
+
}
|
2179
|
+
return out;
|
2180
|
+
};
|
2181
|
+
const sanitize = str => str.replace(/[^0-9a-zA-Z_]/g, _ => codeToSanitizedStr(_.charCodeAt(0)));
|
2182
|
+
|
2056
2183
|
const unhackName = name => {
|
2057
2184
|
if (name.startsWith('__')) return name.slice(2).replaceAll('_', '.');
|
2058
2185
|
return name;
|
@@ -2288,9 +2415,120 @@ const generateVar = (scope, decl) => {
|
|
2288
2415
|
const global = topLevel || decl._bare;
|
2289
2416
|
|
2290
2417
|
for (const x of decl.declarations) {
|
2291
|
-
|
2418
|
+
if (x.id.type === 'ArrayPattern') {
|
2419
|
+
const decls = [];
|
2420
|
+
const tmpName = '#destructure' + randId();
|
2421
|
+
|
2422
|
+
let i = 0;
|
2423
|
+
const elements = [...x.id.elements];
|
2424
|
+
for (const e of elements) {
|
2425
|
+
switch (e?.type) {
|
2426
|
+
case 'RestElement': { // let [ ...foo ] = []
|
2427
|
+
if (e.argument.type === 'ArrayPattern') {
|
2428
|
+
// let [ ...[a, b, c] ] = []
|
2429
|
+
elements.push(...e.argument.elements);
|
2430
|
+
} else {
|
2431
|
+
decls.push({
|
2432
|
+
type: 'VariableDeclarator',
|
2433
|
+
id: { type: 'Identifier', name: e.argument.name },
|
2434
|
+
init: {
|
2435
|
+
type: 'CallExpression',
|
2436
|
+
callee: {
|
2437
|
+
type: 'Identifier',
|
2438
|
+
name: '__Array_prototype_slice'
|
2439
|
+
},
|
2440
|
+
arguments: [
|
2441
|
+
{ type: 'Identifier', name: tmpName },
|
2442
|
+
{ type: 'Literal', value: i },
|
2443
|
+
{
|
2444
|
+
type: 'MemberExpression',
|
2445
|
+
object: { type: 'Identifier', name: tmpName, },
|
2446
|
+
property: { type: 'Identifier', name: 'length', }
|
2447
|
+
}
|
2448
|
+
]
|
2449
|
+
}
|
2450
|
+
});
|
2451
|
+
}
|
2292
2452
|
|
2293
|
-
|
2453
|
+
continue; // skip i++
|
2454
|
+
}
|
2455
|
+
|
2456
|
+
case 'Identifier': { // let [ foo ] = []
|
2457
|
+
decls.push({
|
2458
|
+
type: 'VariableDeclarator',
|
2459
|
+
id: e,
|
2460
|
+
init: {
|
2461
|
+
type: 'MemberExpression',
|
2462
|
+
object: { type: 'Identifier', name: tmpName },
|
2463
|
+
property: { type: 'Literal', value: i }
|
2464
|
+
}
|
2465
|
+
});
|
2466
|
+
|
2467
|
+
break;
|
2468
|
+
}
|
2469
|
+
|
2470
|
+
case 'AssignmentPattern': { // let [ foo = defaultValue ] = []
|
2471
|
+
decls.push({
|
2472
|
+
type: 'VariableDeclarator',
|
2473
|
+
id: e.left,
|
2474
|
+
init: {
|
2475
|
+
type: 'LogicalExpression',
|
2476
|
+
operator: '??',
|
2477
|
+
left: {
|
2478
|
+
type: 'MemberExpression',
|
2479
|
+
object: { type: 'Identifier', name: tmpName },
|
2480
|
+
property: { type: 'Literal', value: i }
|
2481
|
+
},
|
2482
|
+
right: e.right
|
2483
|
+
}
|
2484
|
+
});
|
2485
|
+
|
2486
|
+
break;
|
2487
|
+
}
|
2488
|
+
|
2489
|
+
case 'ArrayPattern': { // let [ [ foo, bar ] ] = []
|
2490
|
+
decls.push({
|
2491
|
+
type: 'VariableDeclarator',
|
2492
|
+
id: e,
|
2493
|
+
init: {
|
2494
|
+
type: 'MemberExpression',
|
2495
|
+
object: { type: 'Identifier', name: tmpName },
|
2496
|
+
property: { type: 'Literal', value: i }
|
2497
|
+
}
|
2498
|
+
});
|
2499
|
+
|
2500
|
+
break;
|
2501
|
+
}
|
2502
|
+
|
2503
|
+
case 'ObjectPattern':
|
2504
|
+
return todo(scope, 'object destructuring is not supported yet')
|
2505
|
+
}
|
2506
|
+
|
2507
|
+
i++;
|
2508
|
+
}
|
2509
|
+
|
2510
|
+
out = out.concat([
|
2511
|
+
...generateVar(scope, {
|
2512
|
+
type: 'VariableDeclaration',
|
2513
|
+
declarations: [{
|
2514
|
+
type: 'VariableDeclarator',
|
2515
|
+
id: { type: 'Identifier', name: tmpName },
|
2516
|
+
init: x.init
|
2517
|
+
}],
|
2518
|
+
kind: decl.kind
|
2519
|
+
}),
|
2520
|
+
...generateVar(scope, {
|
2521
|
+
type: 'VariableDeclaration',
|
2522
|
+
declarations: decls,
|
2523
|
+
kind: decl.kind
|
2524
|
+
})
|
2525
|
+
]);
|
2526
|
+
|
2527
|
+
continue;
|
2528
|
+
}
|
2529
|
+
|
2530
|
+
const name = mapName(x.id.name);
|
2531
|
+
if (!name) return todo(scope, 'object destructuring is not supported yet')
|
2294
2532
|
|
2295
2533
|
if (x.init && isFuncType(x.init.type)) {
|
2296
2534
|
// hack for let a = function () { ... }
|
@@ -2325,7 +2563,9 @@ const generateVar = (scope, decl) => {
|
|
2325
2563
|
// hack to set local as pointer before
|
2326
2564
|
out.push(...number(scope.arrays.get(name)), [ global ? Opcodes.global_set : Opcodes.local_set, idx ]);
|
2327
2565
|
if (generated.at(-1) == Opcodes.i32_from_u) generated.pop();
|
2328
|
-
generated.pop();
|
2566
|
+
// generated.pop();
|
2567
|
+
generated.push([ Opcodes.drop ]);
|
2568
|
+
|
2329
2569
|
out = out.concat(generated);
|
2330
2570
|
} else {
|
2331
2571
|
out = out.concat(generated);
|
@@ -2392,8 +2632,8 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
|
|
2392
2632
|
|
2393
2633
|
// arr[i]
|
2394
2634
|
if (decl.left.type === 'MemberExpression' && decl.left.computed) {
|
2395
|
-
const newValueTmp = localTmp(scope, '
|
2396
|
-
const pointerTmp =
|
2635
|
+
const newValueTmp = localTmp(scope, '#member_setter_val_tmp');
|
2636
|
+
const pointerTmp = localTmp(scope, '#member_setter_ptr_tmp', Valtype.i32);
|
2397
2637
|
|
2398
2638
|
return [
|
2399
2639
|
...typeSwitch(scope, getNodeType(scope, decl.left.object), {
|
@@ -2405,11 +2645,11 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
|
|
2405
2645
|
...generate(scope, decl.left.property),
|
2406
2646
|
Opcodes.i32_to_u,
|
2407
2647
|
|
2408
|
-
// turn into byte offset by * valtypeSize
|
2648
|
+
// turn into byte offset by * valtypeSize + 1
|
2409
2649
|
...number(ValtypeSize[valtype] + 1, Valtype.i32),
|
2410
2650
|
[ Opcodes.i32_mul ],
|
2411
2651
|
[ Opcodes.i32_add ],
|
2412
|
-
|
2652
|
+
[ Opcodes.local_tee, pointerTmp ],
|
2413
2653
|
|
2414
2654
|
...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
|
2415
2655
|
[ Opcodes.local_get, pointerTmp ],
|
@@ -2419,7 +2659,11 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
|
|
2419
2659
|
[ Opcodes.i32_load8_u, 0, ValtypeSize.i32 + ValtypeSize[valtype] ]
|
2420
2660
|
], getNodeType(scope, decl.right), false, name, true)),
|
2421
2661
|
[ Opcodes.local_tee, newValueTmp ],
|
2422
|
-
[ Opcodes.store, 0, ValtypeSize.i32 ]
|
2662
|
+
[ Opcodes.store, 0, ValtypeSize.i32 ],
|
2663
|
+
|
2664
|
+
[ Opcodes.local_get, pointerTmp ],
|
2665
|
+
...getNodeType(scope, decl),
|
2666
|
+
[ Opcodes.i32_store8, 0, ValtypeSize.i32 + ValtypeSize[valtype] ],
|
2423
2667
|
],
|
2424
2668
|
|
2425
2669
|
...wrapBC({
|
@@ -3457,6 +3701,22 @@ const generateEmpty = (scope, decl) => {
|
|
3457
3701
|
return [];
|
3458
3702
|
};
|
3459
3703
|
|
3704
|
+
const generateMeta = (scope, decl) => {
|
3705
|
+
if (decl.meta.name !== 'new') return todo(scope, `meta property object ${decl.meta.name} is not supported yet`, true);
|
3706
|
+
|
3707
|
+
switch (`${decl.meta.name}.${decl.property.name}`) {
|
3708
|
+
case 'new.target': {
|
3709
|
+
scope.constr = true;
|
3710
|
+
|
3711
|
+
return [
|
3712
|
+
[ Opcodes.local_get, -1 ],
|
3713
|
+
Opcodes.i32_from_u,
|
3714
|
+
...setLastType(scope, TYPES.boolean)
|
3715
|
+
];
|
3716
|
+
}
|
3717
|
+
}
|
3718
|
+
};
|
3719
|
+
|
3460
3720
|
let pages = new Map();
|
3461
3721
|
const allocPage = (scope, reason, type) => {
|
3462
3722
|
if (pages.has(reason)) return pages.get(reason).ind;
|
@@ -3809,20 +4069,10 @@ const generateMember = (scope, decl, _global, _name) => {
|
|
3809
4069
|
const func = funcs.find(x => x.name === name);
|
3810
4070
|
if (func) {
|
3811
4071
|
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);
|
4072
|
+
return withType(scope, number(typedParams ? Math.floor(func.params.length / 2) : (func.constr ? (func.params.length - 1) : func.params.length)), TYPES.number);
|
3823
4073
|
}
|
3824
4074
|
|
3825
|
-
if (builtinFuncs[name]) return withType(scope, number(builtinFuncs[name].typedParams ? (builtinFuncs[name].params.length / 2) : builtinFuncs[name].params.length), TYPES.number);
|
4075
|
+
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
4076
|
if (importedFuncs[name]) return withType(scope, number(importedFuncs[name].params.length ?? importedFuncs[name].params), TYPES.number);
|
3827
4077
|
if (internalConstrs[name]) return withType(scope, number(internalConstrs[name].length ?? 0), TYPES.number);
|
3828
4078
|
|
@@ -3871,7 +4121,7 @@ const generateMember = (scope, decl, _global, _name) => {
|
|
3871
4121
|
}
|
3872
4122
|
|
3873
4123
|
// todo: generate this array procedurally during builtinFuncs creation
|
3874
|
-
if (['size', 'description'].includes(decl.property.name)) {
|
4124
|
+
if (['size', 'description', 'byteLength'].includes(decl.property.name)) {
|
3875
4125
|
const bc = {};
|
3876
4126
|
const cands = Object.keys(builtinFuncs).filter(x => x.startsWith('__') && x.endsWith('_prototype_' + decl.property.name + '$get'));
|
3877
4127
|
|
@@ -4077,7 +4327,7 @@ const objectHack = node => {
|
|
4077
4327
|
if (!objectName) objectName = objectHack(node.object)?.name?.slice?.(2);
|
4078
4328
|
|
4079
4329
|
// if .name or .length, give up (hack within a hack!)
|
4080
|
-
if (['name', 'length', 'size', 'description'].includes(node.property.name)) {
|
4330
|
+
if (['name', 'length', 'size', 'description', 'byteLength'].includes(node.property.name)) {
|
4081
4331
|
node.object = objectHack(node.object);
|
4082
4332
|
return;
|
4083
4333
|
}
|
@@ -4130,8 +4380,8 @@ const generateFunc = (scope, decl) => {
|
|
4130
4380
|
|
4131
4381
|
if (typedInput && decl.returnType) {
|
4132
4382
|
const { type } = extractTypeAnnotation(decl.returnType);
|
4133
|
-
|
4134
|
-
if (type != null) {
|
4383
|
+
if (type != null && !Prefs.indirectCalls) {
|
4384
|
+
// if (type != null) {
|
4135
4385
|
func.returnType = type;
|
4136
4386
|
func.returns = [ valtypeBinary ];
|
4137
4387
|
}
|
@@ -4152,6 +4402,12 @@ const generateFunc = (scope, decl) => {
|
|
4152
4402
|
defaultValues[name] = x.right;
|
4153
4403
|
break;
|
4154
4404
|
}
|
4405
|
+
|
4406
|
+
case 'RestElement': {
|
4407
|
+
name = x.argument.name;
|
4408
|
+
func.hasRestArgument = true;
|
4409
|
+
break;
|
4410
|
+
}
|
4155
4411
|
}
|
4156
4412
|
|
4157
4413
|
// if (name == null) return todo('non-identifier args are not supported');
|