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.
- package/README.md +6 -4
- package/compiler/assemble.js +19 -4
- package/compiler/builtins/array.ts +57 -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 +358 -120
- package/compiler/generated_builtins.js +2604 -703
- 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,
|
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: [
|
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,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
|
-
|
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
|
-
|
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
|
-
};
|
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
|
-
|
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
|
-
|
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, '
|
2396
|
-
const pointerTmp =
|
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
|
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
|
-
|
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
|
-
|
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
|
}
|