porffor 0.17.0-55999d22b → 0.17.0-6a05ca3ad
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/2c.js +28 -11
- package/compiler/assemble.js +19 -4
- package/compiler/builtins/array.ts +79 -9
- package/compiler/builtins/date.ts +104 -106
- package/compiler/builtins/error.js +2 -5
- package/compiler/builtins/math.ts +6 -2
- package/compiler/builtins/set.ts +6 -14
- package/compiler/builtins/string_f64.ts +4 -6
- package/compiler/builtins/symbol.ts +2 -1
- package/compiler/builtins/typedarray.js +94 -0
- package/compiler/builtins/z_ecma262.ts +1 -0
- package/compiler/builtins.js +25 -0
- package/compiler/codegen.js +858 -160
- package/compiler/generated_builtins.js +2752 -669
- package/compiler/opt.js +3 -0
- package/compiler/pgo.js +9 -1
- package/compiler/precompile.js +4 -2
- package/compiler/prototype.js +0 -69
- package/compiler/types.js +31 -5
- package/compiler/wasmSpec.js +2 -0
- package/compiler/wrap.js +69 -12
- package/package.json +1 -1
- package/rhemyn/README.md +7 -4
- package/rhemyn/compile.js +170 -76
- package/runner/debug.js +1 -1
- package/runner/index.js +5 -3
- package/runner/profile.js +1 -1
- package/runner/repl.js +16 -11
package/compiler/codegen.js
CHANGED
@@ -4,7 +4,7 @@ import { operatorOpcode } from './expression.js';
|
|
4
4
|
import { BuiltinFuncs, BuiltinVars, importedFuncs, NULL, UNDEFINED } from './builtins.js';
|
5
5
|
import { PrototypeFuncs } from './prototype.js';
|
6
6
|
import { number } from './embedding.js';
|
7
|
-
import { TYPES, TYPE_NAMES } from './types.js';
|
7
|
+
import { TYPES, TYPE_FLAGS, TYPE_NAMES, typeHasFlag } from './types.js';
|
8
8
|
import * as Rhemyn from '../rhemyn/compile.js';
|
9
9
|
import parse from './parse.js';
|
10
10
|
import { log } from './log.js';
|
@@ -72,6 +72,9 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
|
|
72
72
|
case 'ExpressionStatement':
|
73
73
|
return generateExp(scope, decl);
|
74
74
|
|
75
|
+
case 'SequenceExpression':
|
76
|
+
return generateSequence(scope, decl);
|
77
|
+
|
75
78
|
case 'CallExpression':
|
76
79
|
return generateCall(scope, decl, global, name, valueUnused);
|
77
80
|
|
@@ -120,6 +123,9 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
|
|
120
123
|
case 'EmptyStatement':
|
121
124
|
return generateEmpty(scope, decl);
|
122
125
|
|
126
|
+
case 'MetaProperty':
|
127
|
+
return generateMeta(scope, decl);
|
128
|
+
|
123
129
|
case 'ConditionalExpression':
|
124
130
|
return generateConditional(scope, decl);
|
125
131
|
|
@@ -264,10 +270,12 @@ const internalThrow = (scope, constructor, message, expectsValue = Prefs.alwaysV
|
|
264
270
|
argument: {
|
265
271
|
type: 'NewExpression',
|
266
272
|
callee: {
|
273
|
+
type: 'Identifier',
|
267
274
|
name: constructor
|
268
275
|
},
|
269
276
|
arguments: [
|
270
277
|
{
|
278
|
+
type: 'Literal',
|
271
279
|
value: message
|
272
280
|
}
|
273
281
|
]
|
@@ -289,12 +297,13 @@ const generateIdent = (scope, decl) => {
|
|
289
297
|
return wasm.slice();
|
290
298
|
}
|
291
299
|
|
292
|
-
|
293
|
-
|
294
|
-
|
300
|
+
// todo: enable this by default in future
|
301
|
+
if (!Object.hasOwn(funcIndex, name) && Object.hasOwn(builtinFuncs, name)) {
|
302
|
+
includeBuiltin(scope, name);
|
303
|
+
return number(funcIndex[name] - importedFuncs.length);
|
295
304
|
}
|
296
305
|
|
297
|
-
if (isExistingProtoFunc(name)) {
|
306
|
+
if (isExistingProtoFunc(name) || Object.hasOwn(internalConstrs, name) || Object.hasOwn(builtinFuncs, name)) {
|
298
307
|
// todo: return an actual something
|
299
308
|
return number(1);
|
300
309
|
}
|
@@ -613,11 +622,14 @@ const compareStrings = (scope, left, right, bytestrings = false) => {
|
|
613
622
|
};
|
614
623
|
|
615
624
|
const truthy = (scope, wasm, type, intIn = false, intOut = false, forceTruthyMode = undefined) => {
|
616
|
-
|
617
|
-
|
618
|
-
|
619
|
-
|
620
|
-
|
625
|
+
if (isIntToFloatOp(wasm[wasm.length - 1])) return [
|
626
|
+
...wasm,
|
627
|
+
...(!intIn && intOut ? [ Opcodes.i32_to_u ] : [])
|
628
|
+
];
|
629
|
+
if (isIntOp(wasm[wasm.length - 1])) return [
|
630
|
+
...wasm,
|
631
|
+
...(intOut ? [] : [ Opcodes.i32_from ]),
|
632
|
+
];
|
621
633
|
|
622
634
|
// todo/perf: use knownType and custom bytecode here instead of typeSwitch
|
623
635
|
|
@@ -973,6 +985,33 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
|
|
973
985
|
};
|
974
986
|
|
975
987
|
const generateBinaryExp = (scope, decl, _global, _name) => {
|
988
|
+
if (decl.operator === 'instanceof') {
|
989
|
+
// very hacky basic instanceof
|
990
|
+
// todo: support dynamic right-hand side
|
991
|
+
|
992
|
+
const out = generate(scope, decl.left);
|
993
|
+
disposeLeftover(out);
|
994
|
+
|
995
|
+
const rightName = decl.right.name;
|
996
|
+
if (!rightName) return todo(scope, 'instanceof dynamic right-hand side is not supported yet', true);
|
997
|
+
|
998
|
+
const checkType = TYPES[rightName.toLowerCase()];
|
999
|
+
if (checkType == null || rightName !== TYPE_NAMES[checkType] || checkType === TYPES.undefined) return todo(scope, 'instanceof right-hand side type unsupported', true);
|
1000
|
+
|
1001
|
+
if ([TYPES.number, TYPES.boolean, TYPES.string, TYPES.symbol, TYPES.object].includes(checkType)) {
|
1002
|
+
out.push(...number(0));
|
1003
|
+
} else {
|
1004
|
+
out.push(
|
1005
|
+
...getNodeType(scope, decl.left),
|
1006
|
+
...number(checkType, Valtype.i32),
|
1007
|
+
[ Opcodes.i32_eq ],
|
1008
|
+
Opcodes.i32_from_u
|
1009
|
+
);
|
1010
|
+
}
|
1011
|
+
|
1012
|
+
return out;
|
1013
|
+
}
|
1014
|
+
|
976
1015
|
const out = performOp(scope, decl.operator, generate(scope, decl.left), generate(scope, decl.right), getNodeType(scope, decl.left), getNodeType(scope, decl.right), _global, _name);
|
977
1016
|
|
978
1017
|
if (valtype !== 'i32' && ['==', '===', '!=', '!==', '>', '>=', '<', '<='].includes(decl.operator)) out.push(Opcodes.i32_from_u);
|
@@ -990,12 +1029,13 @@ const asmFuncToAsm = (func, scope) => {
|
|
990
1029
|
idx = funcIndex[n];
|
991
1030
|
}
|
992
1031
|
|
1032
|
+
if (idx == null) throw new Error(`builtin('${n}') failed to find a func (inside ${scope.name})`);
|
993
1033
|
return idx;
|
994
1034
|
}
|
995
1035
|
});
|
996
1036
|
};
|
997
1037
|
|
998
|
-
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 }) => {
|
999
1039
|
const existing = funcs.find(x => x.name === name);
|
1000
1040
|
if (existing) return existing;
|
1001
1041
|
|
@@ -1022,13 +1062,17 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
|
|
1022
1062
|
returnType,
|
1023
1063
|
internal: true,
|
1024
1064
|
index: currentFuncIndex++,
|
1025
|
-
table
|
1065
|
+
table,
|
1066
|
+
constr
|
1026
1067
|
};
|
1027
1068
|
|
1028
1069
|
funcs.push(func);
|
1029
1070
|
funcIndex[name] = func.index;
|
1030
1071
|
|
1031
|
-
if (typeof wasm === 'function')
|
1072
|
+
if (typeof wasm === 'function') {
|
1073
|
+
if (globalThis.precompile) wasm = [];
|
1074
|
+
else wasm = asmFuncToAsm(wasm, func);
|
1075
|
+
}
|
1032
1076
|
|
1033
1077
|
let baseGlobalIdx, i = 0;
|
1034
1078
|
for (const type of globalTypes) {
|
@@ -1049,15 +1093,35 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
|
|
1049
1093
|
|
1050
1094
|
if (table) {
|
1051
1095
|
for (const inst of wasm) {
|
1052
|
-
if (inst
|
1096
|
+
if (inst.at(-1) === 'read func lut') {
|
1053
1097
|
inst.splice(2, 99);
|
1054
|
-
inst.push(...unsignedLEB128(allocPage({}, 'func
|
1098
|
+
inst.push(...unsignedLEB128(allocPage({}, 'func lut') * pageSize));
|
1055
1099
|
}
|
1056
1100
|
}
|
1057
1101
|
|
1058
1102
|
funcs.table = true;
|
1059
1103
|
}
|
1060
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
|
+
|
1061
1125
|
func.wasm = wasm;
|
1062
1126
|
|
1063
1127
|
return func;
|
@@ -1184,20 +1248,7 @@ const getNodeType = (scope, node) => {
|
|
1184
1248
|
return TYPES.number;
|
1185
1249
|
}
|
1186
1250
|
|
1187
|
-
if (node.type === 'NewExpression' && builtinFuncs[name + '$constructor']) {
|
1188
|
-
if (builtinFuncs[name + '$constructor'].typedReturns) {
|
1189
|
-
if (scope.locals['#last_type']) return getLastType(scope);
|
1190
|
-
|
1191
|
-
// presume
|
1192
|
-
// todo: warn here?
|
1193
|
-
return TYPES.number;
|
1194
|
-
}
|
1195
|
-
|
1196
|
-
return builtinFuncs[name + '$constructor'].returnType ?? TYPES.number;
|
1197
|
-
}
|
1198
|
-
|
1199
1251
|
const func = funcs.find(x => x.name === name);
|
1200
|
-
|
1201
1252
|
if (func) {
|
1202
1253
|
if (func.returnType != null) return func.returnType;
|
1203
1254
|
}
|
@@ -1279,7 +1330,7 @@ const getNodeType = (scope, node) => {
|
|
1279
1330
|
}
|
1280
1331
|
|
1281
1332
|
if (node.type === 'BinaryExpression') {
|
1282
|
-
if (['==', '===', '!=', '!==', '>', '>=', '<', '<='].includes(node.operator)) return TYPES.boolean;
|
1333
|
+
if (['==', '===', '!=', '!==', '>', '>=', '<', '<=', 'instanceof'].includes(node.operator)) return TYPES.boolean;
|
1283
1334
|
if (node.operator !== '+') return TYPES.number;
|
1284
1335
|
|
1285
1336
|
const knownLeft = knownType(scope, getNodeType(scope, node.left));
|
@@ -1312,7 +1363,7 @@ const getNodeType = (scope, node) => {
|
|
1312
1363
|
const objectKnownType = knownType(scope, getNodeType(scope, node.object));
|
1313
1364
|
if (objectKnownType != null) {
|
1314
1365
|
if (name === 'length') {
|
1315
|
-
if (
|
1366
|
+
if (typeHasFlag(objectKnownType, TYPE_FLAGS.length)) return TYPES.number;
|
1316
1367
|
else return TYPES.undefined;
|
1317
1368
|
}
|
1318
1369
|
|
@@ -1384,9 +1435,9 @@ const countLeftover = wasm => {
|
|
1384
1435
|
|
1385
1436
|
if (depth === 0)
|
1386
1437
|
if ([Opcodes.throw, Opcodes.drop, Opcodes.local_set, Opcodes.global_set].includes(inst[0])) count--;
|
1387
|
-
else if ([null, Opcodes.i32_eqz, Opcodes.i64_eqz, Opcodes.f64_ceil, Opcodes.f64_floor, Opcodes.f64_trunc, Opcodes.f64_nearest, Opcodes.f64_sqrt, Opcodes.local_tee, Opcodes.i32_wrap_i64, Opcodes.i64_extend_i32_s, Opcodes.i64_extend_i32_u, Opcodes.f32_demote_f64, Opcodes.f64_promote_f32, Opcodes.f64_convert_i32_s, Opcodes.f64_convert_i32_u, Opcodes.i32_clz, Opcodes.i32_ctz, Opcodes.i32_popcnt, Opcodes.f64_neg, Opcodes.end, Opcodes.i32_trunc_sat_f64_s[0], Opcodes.i32x4_extract_lane, Opcodes.i16x8_extract_lane, Opcodes.i32_load, Opcodes.i64_load, Opcodes.f64_load, Opcodes.v128_load, Opcodes.i32_load16_u, Opcodes.i32_load16_s, Opcodes.i32_load8_u, Opcodes.i32_load8_s, Opcodes.memory_grow].includes(inst[0]) && (inst[0] !== 0xfc || inst[1] < 0x04)) {}
|
1438
|
+
else if ([null, Opcodes.i32_eqz, Opcodes.i64_eqz, Opcodes.f64_ceil, Opcodes.f64_floor, Opcodes.f64_trunc, Opcodes.f64_nearest, Opcodes.f64_sqrt, Opcodes.local_tee, Opcodes.i32_wrap_i64, Opcodes.i64_extend_i32_s, Opcodes.i64_extend_i32_u, Opcodes.f32_demote_f64, Opcodes.f64_promote_f32, Opcodes.f64_convert_i32_s, Opcodes.f64_convert_i32_u, Opcodes.i32_clz, Opcodes.i32_ctz, Opcodes.i32_popcnt, Opcodes.f64_neg, Opcodes.end, Opcodes.i32_trunc_sat_f64_s[0], Opcodes.i32x4_extract_lane, Opcodes.i16x8_extract_lane, Opcodes.i32_load, Opcodes.i64_load, Opcodes.f64_load, Opcodes.f32_load, Opcodes.v128_load, Opcodes.i32_load16_u, Opcodes.i32_load16_s, Opcodes.i32_load8_u, Opcodes.i32_load8_s, Opcodes.memory_grow].includes(inst[0]) && (inst[0] !== 0xfc || inst[1] < 0x04)) {}
|
1388
1439
|
else if ([Opcodes.local_get, Opcodes.global_get, Opcodes.f64_const, Opcodes.i32_const, Opcodes.i64_const, Opcodes.v128_const, Opcodes.memory_size].includes(inst[0])) count++;
|
1389
|
-
else if ([Opcodes.i32_store, Opcodes.i64_store, Opcodes.f64_store, Opcodes.i32_store16, Opcodes.i32_store8].includes(inst[0])) count -= 2;
|
1440
|
+
else if ([Opcodes.i32_store, Opcodes.i64_store, Opcodes.f64_store, Opcodes.f32_store, Opcodes.i32_store16, Opcodes.i32_store8].includes(inst[0])) count -= 2;
|
1390
1441
|
else if (inst[0] === Opcodes.memory_copy[0] && (inst[1] === Opcodes.memory_copy[1] || inst[1] === Opcodes.memory_init[1])) count -= 3;
|
1391
1442
|
else if (inst[0] === Opcodes.return) count = 0;
|
1392
1443
|
else if (inst[0] === Opcodes.call) {
|
@@ -1424,6 +1475,18 @@ const generateExp = (scope, decl) => {
|
|
1424
1475
|
return out;
|
1425
1476
|
};
|
1426
1477
|
|
1478
|
+
const generateSequence = (scope, decl) => {
|
1479
|
+
let out = [];
|
1480
|
+
|
1481
|
+
const exprs = decl.expressions;
|
1482
|
+
for (let i = 0; i < exprs.length; i++) {
|
1483
|
+
if (i > 0) disposeLeftover(out);
|
1484
|
+
out.push(...generate(scope, exprs[i]));
|
1485
|
+
}
|
1486
|
+
|
1487
|
+
return out;
|
1488
|
+
};
|
1489
|
+
|
1427
1490
|
const CTArrayUtil = {
|
1428
1491
|
getLengthI32: pointer => [
|
1429
1492
|
...number(0, Valtype.i32),
|
@@ -1483,7 +1546,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1483
1546
|
name = func.name;
|
1484
1547
|
}
|
1485
1548
|
|
1486
|
-
if (name === 'eval' && decl.arguments[0]?.type === 'Literal') {
|
1549
|
+
if (!decl._new && name === 'eval' && decl.arguments[0]?.type === 'Literal') {
|
1487
1550
|
// literal eval hack
|
1488
1551
|
const code = decl.arguments[0]?.value ?? '';
|
1489
1552
|
|
@@ -1526,7 +1589,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1526
1589
|
|
1527
1590
|
let protoName, target;
|
1528
1591
|
// ident.func()
|
1529
|
-
if (name && name.startsWith('__')) {
|
1592
|
+
if (!decl._new && name && name.startsWith('__')) {
|
1530
1593
|
const spl = name.slice(2).split('_');
|
1531
1594
|
|
1532
1595
|
protoName = spl[spl.length - 1];
|
@@ -1539,12 +1602,12 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1539
1602
|
}
|
1540
1603
|
|
1541
1604
|
// literal.func()
|
1542
|
-
if (!name && decl.callee.type === 'MemberExpression') {
|
1605
|
+
if (!decl._new && !name && decl.callee.type === 'MemberExpression') {
|
1543
1606
|
// megahack for /regex/.func()
|
1544
1607
|
const funcName = decl.callee.property.name;
|
1545
|
-
if (decl.callee.object.regex &&
|
1608
|
+
if (decl.callee.object.regex && ['test'].includes(funcName)) {
|
1546
1609
|
const regex = decl.callee.object.regex.pattern;
|
1547
|
-
const rhemynName = `regex_${funcName}_${regex}`;
|
1610
|
+
const rhemynName = `regex_${funcName}_${sanitize(regex)}`;
|
1548
1611
|
|
1549
1612
|
if (!funcIndex[rhemynName]) {
|
1550
1613
|
const func = Rhemyn[funcName](regex, currentFuncIndex++, rhemynName);
|
@@ -1565,7 +1628,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1565
1628
|
[ Opcodes.call, idx ],
|
1566
1629
|
Opcodes.i32_from_u,
|
1567
1630
|
|
1568
|
-
...setLastType(scope,
|
1631
|
+
...setLastType(scope, Rhemyn.types[funcName])
|
1569
1632
|
];
|
1570
1633
|
}
|
1571
1634
|
|
@@ -1574,27 +1637,56 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1574
1637
|
target = decl.callee.object;
|
1575
1638
|
}
|
1576
1639
|
|
1577
|
-
|
1578
|
-
|
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 ],
|
1579
1648
|
|
1580
|
-
|
1581
|
-
|
1649
|
+
...number(Rhemyn.types[protoName] === TYPES.number ? -1 : 0),
|
1650
|
+
...setLastType(scope, Rhemyn.types[protoName])
|
1651
|
+
];
|
1582
1652
|
|
1583
|
-
|
1584
|
-
// generate(scope, decl.callee.object)
|
1653
|
+
const rhemynName = `regex_${protoName}_${sanitize(regex)}`;
|
1585
1654
|
|
1586
|
-
|
1587
|
-
|
1588
|
-
|
1589
|
-
// ];
|
1590
|
-
// }
|
1655
|
+
if (!funcIndex[rhemynName]) {
|
1656
|
+
const func = Rhemyn[protoName](regex, currentFuncIndex++, rhemynName);
|
1657
|
+
func.internal = true;
|
1591
1658
|
|
1592
|
-
|
1593
|
-
|
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,
|
1594
1673
|
|
1674
|
+
...setLastType(scope, Rhemyn.types[protoName])
|
1675
|
+
];
|
1676
|
+
}
|
1677
|
+
|
1678
|
+
const protoBC = {};
|
1595
1679
|
const builtinProtoCands = Object.keys(builtinFuncs).filter(x => x.startsWith('__') && x.endsWith('_prototype_' + protoName));
|
1596
1680
|
|
1597
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
|
+
|
1598
1690
|
for (const x of builtinProtoCands) {
|
1599
1691
|
const type = TYPES[x.split('_prototype_')[0].slice(2).toLowerCase()];
|
1600
1692
|
if (type == null) continue;
|
@@ -1604,7 +1696,14 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1604
1696
|
type: 'Identifier',
|
1605
1697
|
name: x
|
1606
1698
|
},
|
1607
|
-
arguments: [
|
1699
|
+
arguments: [
|
1700
|
+
{
|
1701
|
+
type: 'Identifier',
|
1702
|
+
name: '#proto_target'
|
1703
|
+
},
|
1704
|
+
|
1705
|
+
...decl.arguments
|
1706
|
+
],
|
1608
1707
|
_protoInternalCall: true
|
1609
1708
|
});
|
1610
1709
|
}
|
@@ -1698,29 +1797,37 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1698
1797
|
}
|
1699
1798
|
|
1700
1799
|
if (Object.keys(protoBC).length > 0) {
|
1701
|
-
return
|
1702
|
-
...
|
1800
|
+
return [
|
1801
|
+
...out,
|
1802
|
+
|
1803
|
+
...typeSwitch(scope, builtinProtoCands.length > 0 ? [ [ Opcodes.local_get, localTmp(scope, '#proto_target#type', Valtype.i32) ] ] : getNodeType(scope, target), {
|
1804
|
+
...protoBC,
|
1703
1805
|
|
1704
|
-
|
1705
|
-
|
1706
|
-
|
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
|
+
];
|
1707
1810
|
}
|
1708
1811
|
}
|
1709
1812
|
|
1710
|
-
// TODO: only allows callee as
|
1711
|
-
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})`);
|
1712
1815
|
|
1713
1816
|
let idx = funcIndex[name] ?? importedFuncs[name];
|
1714
1817
|
if (idx === undefined && builtinFuncs[name]) {
|
1715
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);
|
1716
1820
|
|
1717
1821
|
includeBuiltin(scope, name);
|
1718
1822
|
idx = funcIndex[name];
|
1719
1823
|
}
|
1720
1824
|
|
1721
|
-
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
|
+
}
|
1722
1829
|
|
1723
|
-
if (idx === undefined && name.startsWith('__Porffor_wasm_')) {
|
1830
|
+
if (idx === undefined && !decl._new && name.startsWith('__Porffor_wasm_')) {
|
1724
1831
|
const wasmOps = {
|
1725
1832
|
// pointer, align, offset
|
1726
1833
|
i32_load: { imms: 2, args: [ true ], returns: 1 },
|
@@ -1775,7 +1882,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1775
1882
|
|
1776
1883
|
const indirectMode = Prefs.indirectCallMode ?? 'vararg';
|
1777
1884
|
// options: vararg, strict
|
1778
|
-
// - strict: simpler, smaller size usage, no func
|
1885
|
+
// - strict: simpler, smaller size usage, no func lut needed.
|
1779
1886
|
// ONLY works when arg count of call == arg count of function being called
|
1780
1887
|
// - vararg: large size usage, cursed.
|
1781
1888
|
// works when arg count of call != arg count of function being called*
|
@@ -1785,8 +1892,6 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1785
1892
|
scope.table = true;
|
1786
1893
|
|
1787
1894
|
let args = decl.arguments;
|
1788
|
-
let out = [];
|
1789
|
-
|
1790
1895
|
let locals = [];
|
1791
1896
|
|
1792
1897
|
if (indirectMode === 'vararg') {
|
@@ -1850,24 +1955,67 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1850
1955
|
// *for argc 0-3, in future (todo:) the max number should be
|
1851
1956
|
// dynamically changed to the max argc of any func in the js file.
|
1852
1957
|
|
1853
|
-
const funcLocal = localTmp(scope,
|
1958
|
+
const funcLocal = localTmp(scope, '#indirect_func', Valtype.i32);
|
1959
|
+
const flags = localTmp(scope, '#indirect_flags', Valtype.i32);
|
1854
1960
|
|
1855
1961
|
const gen = argc => {
|
1856
|
-
const
|
1962
|
+
const argsOut = [];
|
1857
1963
|
for (let i = 0; i < argc; i++) {
|
1858
|
-
|
1964
|
+
argsOut.push(
|
1859
1965
|
[ Opcodes.local_get, locals[i][0] ],
|
1860
1966
|
[ Opcodes.local_get, locals[i][1] ]
|
1861
1967
|
);
|
1862
1968
|
}
|
1863
1969
|
|
1864
|
-
|
1865
|
-
[ Opcodes.local_get,
|
1866
|
-
|
1867
|
-
|
1868
|
-
|
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
|
+
];
|
1869
1980
|
|
1870
|
-
|
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
|
+
);
|
1871
2019
|
};
|
1872
2020
|
|
1873
2021
|
const tableBc = {};
|
@@ -1885,13 +2033,32 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1885
2033
|
Opcodes.i32_to_u,
|
1886
2034
|
[ Opcodes.local_set, funcLocal ],
|
1887
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
|
+
|
1888
2056
|
...brTable([
|
1889
2057
|
// get argc of func we are calling
|
1890
2058
|
[ Opcodes.local_get, funcLocal ],
|
1891
|
-
...number(
|
2059
|
+
...number(3, Valtype.i32),
|
1892
2060
|
[ Opcodes.i32_mul ],
|
1893
|
-
|
1894
|
-
[ 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' ]
|
1895
2062
|
], tableBc, valtypeBinary)
|
1896
2063
|
],
|
1897
2064
|
default: internalThrow(scope, 'TypeError', `${unhackName(name)} is not a function`, true)
|
@@ -1905,7 +2072,16 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1905
2072
|
const userFunc = func && !func.internal;
|
1906
2073
|
const typedParams = userFunc || builtinFuncs[name]?.typedParams;
|
1907
2074
|
const typedReturns = (userFunc && func.returnType == null) || builtinFuncs[name]?.typedReturns;
|
1908
|
-
|
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);
|
1909
2085
|
|
1910
2086
|
let args = decl.arguments;
|
1911
2087
|
if (func && args.length < paramCount) {
|
@@ -1920,7 +2096,6 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1920
2096
|
|
1921
2097
|
if (func && func.throws) scope.throws = true;
|
1922
2098
|
|
1923
|
-
let out = [];
|
1924
2099
|
for (let i = 0; i < args.length; i++) {
|
1925
2100
|
const arg = args[i];
|
1926
2101
|
out = out.concat(generate(scope, arg));
|
@@ -1933,13 +2108,13 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1933
2108
|
}
|
1934
2109
|
|
1935
2110
|
if (valtypeBinary !== Valtype.i32 &&
|
1936
|
-
(func && func.params[i * (typedParams ? 2 : 1)] === Valtype.i32)
|
2111
|
+
(func && func.params[paramOffset + i * (typedParams ? 2 : 1)] === Valtype.i32)
|
1937
2112
|
) {
|
1938
2113
|
out.push(Opcodes.i32_to);
|
1939
2114
|
}
|
1940
2115
|
|
1941
2116
|
if (valtypeBinary === Valtype.i32 &&
|
1942
|
-
(func && func.params[i * (typedParams ? 2 : 1)] === Valtype.f64)
|
2117
|
+
(func && func.params[paramOffset + i * (typedParams ? 2 : 1)] === Valtype.f64)
|
1943
2118
|
) {
|
1944
2119
|
out.push([ Opcodes.f64_convert_i32_s ]);
|
1945
2120
|
}
|
@@ -1972,32 +2147,10 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1972
2147
|
return out;
|
1973
2148
|
};
|
1974
2149
|
|
1975
|
-
const generateNew = (scope, decl, _global, _name) => {
|
1976
|
-
|
1977
|
-
|
1978
|
-
|
1979
|
-
if (internalConstrs[name] && !internalConstrs[name].notConstr) return internalConstrs[name].generate(scope, decl, _global, _name);
|
1980
|
-
|
1981
|
-
if (builtinFuncs[name + '$constructor']) {
|
1982
|
-
// custom ...$constructor override builtin func
|
1983
|
-
return generateCall(scope, {
|
1984
|
-
...decl,
|
1985
|
-
callee: {
|
1986
|
-
type: 'Identifier',
|
1987
|
-
name: name + '$constructor'
|
1988
|
-
}
|
1989
|
-
}, _global, _name);
|
1990
|
-
}
|
1991
|
-
|
1992
|
-
if (
|
1993
|
-
(builtinFuncs[name] && !builtinFuncs[name].constr) ||
|
1994
|
-
(internalConstrs[name] && builtinFuncs[name].notConstr)
|
1995
|
-
) return internalThrow(scope, 'TypeError', `${name} is not a constructor`, true);
|
1996
|
-
|
1997
|
-
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)})`);
|
1998
|
-
|
1999
|
-
return generateCall(scope, decl, _global, _name);
|
2000
|
-
};
|
2150
|
+
const generateNew = (scope, decl, _global, _name) => generateCall(scope, {
|
2151
|
+
...decl,
|
2152
|
+
_new: true
|
2153
|
+
}, _global, _name);
|
2001
2154
|
|
2002
2155
|
// bad hack for undefined and null working without additional logic
|
2003
2156
|
const DEFAULT_VALUE = {
|
@@ -2005,6 +2158,16 @@ const DEFAULT_VALUE = {
|
|
2005
2158
|
name: 'undefined'
|
2006
2159
|
};
|
2007
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
|
+
|
2008
2171
|
const unhackName = name => {
|
2009
2172
|
if (name.startsWith('__')) return name.slice(2).replaceAll('_', '.');
|
2010
2173
|
return name;
|
@@ -2012,7 +2175,7 @@ const unhackName = name => {
|
|
2012
2175
|
|
2013
2176
|
const knownType = (scope, type) => {
|
2014
2177
|
if (type.length === 1 && type[0][0] === Opcodes.i32_const) {
|
2015
|
-
return type[0]
|
2178
|
+
return read_signedLEB128(type[0].slice(1));
|
2016
2179
|
}
|
2017
2180
|
|
2018
2181
|
if (typedInput && type.length === 1 && type[0][0] === Opcodes.local_get) {
|
@@ -2240,9 +2403,120 @@ const generateVar = (scope, decl) => {
|
|
2240
2403
|
const global = topLevel || decl._bare;
|
2241
2404
|
|
2242
2405
|
for (const x of decl.declarations) {
|
2243
|
-
|
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
|
+
}
|
2457
|
+
|
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
|
+
});
|
2244
2487
|
|
2245
|
-
|
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')
|
2246
2520
|
|
2247
2521
|
if (x.init && isFuncType(x.init.type)) {
|
2248
2522
|
// hack for let a = function () { ... }
|
@@ -2277,7 +2551,9 @@ const generateVar = (scope, decl) => {
|
|
2277
2551
|
// hack to set local as pointer before
|
2278
2552
|
out.push(...number(scope.arrays.get(name)), [ global ? Opcodes.global_set : Opcodes.local_set, idx ]);
|
2279
2553
|
if (generated.at(-1) == Opcodes.i32_from_u) generated.pop();
|
2280
|
-
generated.pop();
|
2554
|
+
// generated.pop();
|
2555
|
+
generated.push([ Opcodes.drop ]);
|
2556
|
+
|
2281
2557
|
out = out.concat(generated);
|
2282
2558
|
} else {
|
2283
2559
|
out = out.concat(generated);
|
@@ -2299,11 +2575,6 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
|
|
2299
2575
|
const { type, name } = decl.left;
|
2300
2576
|
const [ local, isGlobal ] = lookupName(scope, name);
|
2301
2577
|
|
2302
|
-
if (type === 'ObjectPattern') {
|
2303
|
-
// hack: ignore object parts of `var a = {} = 2`
|
2304
|
-
return generate(scope, decl.right);
|
2305
|
-
}
|
2306
|
-
|
2307
2578
|
if (isFuncType(decl.right.type)) {
|
2308
2579
|
// hack for a = function () { ... }
|
2309
2580
|
decl.right.id = { name };
|
@@ -2349,8 +2620,8 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
|
|
2349
2620
|
|
2350
2621
|
// arr[i]
|
2351
2622
|
if (decl.left.type === 'MemberExpression' && decl.left.computed) {
|
2352
|
-
const newValueTmp = localTmp(scope, '
|
2353
|
-
const pointerTmp =
|
2623
|
+
const newValueTmp = localTmp(scope, '#member_setter_val_tmp');
|
2624
|
+
const pointerTmp = localTmp(scope, '#member_setter_ptr_tmp', Valtype.i32);
|
2354
2625
|
|
2355
2626
|
return [
|
2356
2627
|
...typeSwitch(scope, getNodeType(scope, decl.left.object), {
|
@@ -2362,11 +2633,11 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
|
|
2362
2633
|
...generate(scope, decl.left.property),
|
2363
2634
|
Opcodes.i32_to_u,
|
2364
2635
|
|
2365
|
-
// turn into byte offset by * valtypeSize
|
2636
|
+
// turn into byte offset by * valtypeSize + 1
|
2366
2637
|
...number(ValtypeSize[valtype] + 1, Valtype.i32),
|
2367
2638
|
[ Opcodes.i32_mul ],
|
2368
2639
|
[ Opcodes.i32_add ],
|
2369
|
-
|
2640
|
+
[ Opcodes.local_tee, pointerTmp ],
|
2370
2641
|
|
2371
2642
|
...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
|
2372
2643
|
[ Opcodes.local_get, pointerTmp ],
|
@@ -2376,10 +2647,164 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
|
|
2376
2647
|
[ Opcodes.i32_load8_u, 0, ValtypeSize.i32 + ValtypeSize[valtype] ]
|
2377
2648
|
], getNodeType(scope, decl.right), false, name, true)),
|
2378
2649
|
[ Opcodes.local_tee, newValueTmp ],
|
2650
|
+
[ Opcodes.store, 0, ValtypeSize.i32 ],
|
2379
2651
|
|
2380
|
-
[ Opcodes.
|
2652
|
+
[ Opcodes.local_get, pointerTmp ],
|
2653
|
+
...getNodeType(scope, decl),
|
2654
|
+
[ Opcodes.i32_store8, 0, ValtypeSize.i32 + ValtypeSize[valtype] ],
|
2381
2655
|
],
|
2382
2656
|
|
2657
|
+
...wrapBC({
|
2658
|
+
[TYPES.uint8array]: [
|
2659
|
+
[ Opcodes.i32_add ],
|
2660
|
+
...(op === '=' ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
|
2661
|
+
|
2662
|
+
...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
|
2663
|
+
[ Opcodes.local_get, pointerTmp ],
|
2664
|
+
[ Opcodes.i32_load8_u, 0, 4 ],
|
2665
|
+
Opcodes.i32_from_u
|
2666
|
+
], generate(scope, decl.right), number(TYPES.number, Valtype.i32), getNodeType(scope, decl.right), false, name, true)),
|
2667
|
+
[ Opcodes.local_tee, newValueTmp ],
|
2668
|
+
|
2669
|
+
Opcodes.i32_to_u,
|
2670
|
+
[ Opcodes.i32_store8, 0, 4 ]
|
2671
|
+
],
|
2672
|
+
[TYPES.uint8clampedarray]: [
|
2673
|
+
[ Opcodes.i32_add ],
|
2674
|
+
...(op === '=' ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
|
2675
|
+
|
2676
|
+
...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
|
2677
|
+
[ Opcodes.local_get, pointerTmp ],
|
2678
|
+
[ Opcodes.i32_load8_u, 0, 4 ],
|
2679
|
+
Opcodes.i32_from_u
|
2680
|
+
], generate(scope, decl.right), number(TYPES.number, Valtype.i32), getNodeType(scope, decl.right), false, name, true)),
|
2681
|
+
[ Opcodes.local_tee, newValueTmp ],
|
2682
|
+
|
2683
|
+
...number(0),
|
2684
|
+
[ Opcodes.f64_max ],
|
2685
|
+
...number(255),
|
2686
|
+
[ Opcodes.f64_min ],
|
2687
|
+
Opcodes.i32_to_u,
|
2688
|
+
[ Opcodes.i32_store8, 0, 4 ]
|
2689
|
+
],
|
2690
|
+
[TYPES.int8array]: [
|
2691
|
+
[ Opcodes.i32_add ],
|
2692
|
+
...(op === '=' ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
|
2693
|
+
|
2694
|
+
...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
|
2695
|
+
[ Opcodes.local_get, pointerTmp ],
|
2696
|
+
[ Opcodes.i32_load8_s, 0, 4 ],
|
2697
|
+
Opcodes.i32_from
|
2698
|
+
], generate(scope, decl.right), number(TYPES.number, Valtype.i32), getNodeType(scope, decl.right), false, name, true)),
|
2699
|
+
[ Opcodes.local_tee, newValueTmp ],
|
2700
|
+
|
2701
|
+
Opcodes.i32_to,
|
2702
|
+
[ Opcodes.i32_store8, 0, 4 ]
|
2703
|
+
],
|
2704
|
+
[TYPES.uint16array]: [
|
2705
|
+
...number(2, Valtype.i32),
|
2706
|
+
[ Opcodes.i32_mul ],
|
2707
|
+
[ Opcodes.i32_add ],
|
2708
|
+
...(op === '=' ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
|
2709
|
+
|
2710
|
+
...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
|
2711
|
+
[ Opcodes.local_get, pointerTmp ],
|
2712
|
+
[ Opcodes.i32_load16_u, 0, 4 ],
|
2713
|
+
Opcodes.i32_from_u
|
2714
|
+
], generate(scope, decl.right), number(TYPES.number, Valtype.i32), getNodeType(scope, decl.right), false, name, true)),
|
2715
|
+
[ Opcodes.local_tee, newValueTmp ],
|
2716
|
+
|
2717
|
+
Opcodes.i32_to_u,
|
2718
|
+
[ Opcodes.i32_store16, 0, 4 ]
|
2719
|
+
],
|
2720
|
+
[TYPES.int16array]: [
|
2721
|
+
...number(2, Valtype.i32),
|
2722
|
+
[ Opcodes.i32_mul ],
|
2723
|
+
[ Opcodes.i32_add ],
|
2724
|
+
...(op === '=' ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
|
2725
|
+
|
2726
|
+
...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
|
2727
|
+
[ Opcodes.local_get, pointerTmp ],
|
2728
|
+
[ Opcodes.i32_load16_s, 0, 4 ],
|
2729
|
+
Opcodes.i32_from
|
2730
|
+
], generate(scope, decl.right), number(TYPES.number, Valtype.i32), getNodeType(scope, decl.right), false, name, true)),
|
2731
|
+
[ Opcodes.local_tee, newValueTmp ],
|
2732
|
+
|
2733
|
+
Opcodes.i32_to,
|
2734
|
+
[ Opcodes.i32_store16, 0, 4 ]
|
2735
|
+
],
|
2736
|
+
[TYPES.uint32array]: [
|
2737
|
+
...number(4, Valtype.i32),
|
2738
|
+
[ Opcodes.i32_mul ],
|
2739
|
+
[ Opcodes.i32_add ],
|
2740
|
+
...(op === '=' ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
|
2741
|
+
|
2742
|
+
...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
|
2743
|
+
[ Opcodes.local_get, pointerTmp ],
|
2744
|
+
[ Opcodes.i32_load, 0, 4 ],
|
2745
|
+
Opcodes.i32_from_u
|
2746
|
+
], generate(scope, decl.right), number(TYPES.number, Valtype.i32), getNodeType(scope, decl.right), false, name, true)),
|
2747
|
+
[ Opcodes.local_tee, newValueTmp ],
|
2748
|
+
|
2749
|
+
Opcodes.i32_to_u,
|
2750
|
+
[ Opcodes.i32_store, 0, 4 ]
|
2751
|
+
],
|
2752
|
+
[TYPES.int32array]: [
|
2753
|
+
...number(4, Valtype.i32),
|
2754
|
+
[ Opcodes.i32_mul ],
|
2755
|
+
[ Opcodes.i32_add ],
|
2756
|
+
...(op === '=' ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
|
2757
|
+
|
2758
|
+
...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
|
2759
|
+
[ Opcodes.local_get, pointerTmp ],
|
2760
|
+
[ Opcodes.i32_load, 0, 4 ],
|
2761
|
+
Opcodes.i32_from
|
2762
|
+
], generate(scope, decl.right), number(TYPES.number, Valtype.i32), getNodeType(scope, decl.right), false, name, true)),
|
2763
|
+
[ Opcodes.local_tee, newValueTmp ],
|
2764
|
+
|
2765
|
+
Opcodes.i32_to,
|
2766
|
+
[ Opcodes.i32_store, 0, 4 ]
|
2767
|
+
],
|
2768
|
+
[TYPES.float32array]: [
|
2769
|
+
...number(4, Valtype.i32),
|
2770
|
+
[ Opcodes.i32_mul ],
|
2771
|
+
[ Opcodes.i32_add ],
|
2772
|
+
...(op === '=' ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
|
2773
|
+
|
2774
|
+
...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
|
2775
|
+
[ Opcodes.local_get, pointerTmp ],
|
2776
|
+
[ Opcodes.f32_load, 0, 4 ],
|
2777
|
+
[ Opcodes.f64_promote_f32 ]
|
2778
|
+
], generate(scope, decl.right), number(TYPES.number, Valtype.i32), getNodeType(scope, decl.right), false, name, true)),
|
2779
|
+
[ Opcodes.local_tee, newValueTmp ],
|
2780
|
+
|
2781
|
+
[ Opcodes.f32_demote_f64 ],
|
2782
|
+
[ Opcodes.f32_store, 0, 4 ]
|
2783
|
+
],
|
2784
|
+
[TYPES.float64array]: [
|
2785
|
+
...number(8, Valtype.i32),
|
2786
|
+
[ Opcodes.i32_mul ],
|
2787
|
+
[ Opcodes.i32_add ],
|
2788
|
+
...(op === '=' ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
|
2789
|
+
|
2790
|
+
...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
|
2791
|
+
[ Opcodes.local_get, pointerTmp ],
|
2792
|
+
[ Opcodes.f64_load, 0, 4 ]
|
2793
|
+
], generate(scope, decl.right), number(TYPES.number, Valtype.i32), getNodeType(scope, decl.right), false, name, true)),
|
2794
|
+
[ Opcodes.local_tee, newValueTmp ],
|
2795
|
+
|
2796
|
+
[ Opcodes.f64_store, 0, 4 ]
|
2797
|
+
],
|
2798
|
+
}, {
|
2799
|
+
prelude: [
|
2800
|
+
...generate(scope, decl.left.object),
|
2801
|
+
Opcodes.i32_to_u,
|
2802
|
+
...generate(scope, decl.left.property),
|
2803
|
+
Opcodes.i32_to_u,
|
2804
|
+
],
|
2805
|
+
postlude: setLastType(scope, TYPES.number)
|
2806
|
+
}),
|
2807
|
+
|
2383
2808
|
default: internalThrow(scope, 'TypeError', `Cannot assign member with non-array`)
|
2384
2809
|
}, Blocktype.void),
|
2385
2810
|
|
@@ -2763,7 +3188,9 @@ const generateForOf = (scope, decl) => {
|
|
2763
3188
|
// // todo: we should only do this for strings but we don't know at compile-time :(
|
2764
3189
|
// hack: this is naughty and will break things!
|
2765
3190
|
let newOut = number(0, Valtype.i32), newPointer = number(0, Valtype.i32);
|
2766
|
-
|
3191
|
+
|
3192
|
+
const known = knownType(scope, getNodeType(scope, decl.right));
|
3193
|
+
if ((known === TYPES.string || known === TYPES.bytestring) || (pages.hasAnyString && known == null)) {
|
2767
3194
|
// todo: we use i16 even for bytestrings which should not make a bad thing happen, just be confusing for debugging?
|
2768
3195
|
0, [ newOut, newPointer ] = makeArray(scope, {
|
2769
3196
|
rawElements: new Array(0)
|
@@ -2811,6 +3238,7 @@ const generateForOf = (scope, decl) => {
|
|
2811
3238
|
[ Opcodes.end ],
|
2812
3239
|
[ Opcodes.end ]
|
2813
3240
|
],
|
3241
|
+
|
2814
3242
|
[TYPES.string]: [
|
2815
3243
|
...setType(scope, leftName, TYPES.string),
|
2816
3244
|
|
@@ -2919,6 +3347,7 @@ const generateForOf = (scope, decl) => {
|
|
2919
3347
|
[ Opcodes.end ],
|
2920
3348
|
[ Opcodes.end ]
|
2921
3349
|
],
|
3350
|
+
|
2922
3351
|
[TYPES.set]: [
|
2923
3352
|
[ Opcodes.loop, Blocktype.void ],
|
2924
3353
|
|
@@ -2957,6 +3386,106 @@ const generateForOf = (scope, decl) => {
|
|
2957
3386
|
[ Opcodes.end ],
|
2958
3387
|
[ Opcodes.end ]
|
2959
3388
|
],
|
3389
|
+
|
3390
|
+
...wrapBC({
|
3391
|
+
[TYPES.uint8array]: [
|
3392
|
+
[ Opcodes.i32_add ],
|
3393
|
+
|
3394
|
+
[ Opcodes.i32_load8_u, 0, 4 ],
|
3395
|
+
Opcodes.i32_from_u
|
3396
|
+
],
|
3397
|
+
[TYPES.uint8clampedarray]: [
|
3398
|
+
[ Opcodes.i32_add ],
|
3399
|
+
|
3400
|
+
[ Opcodes.i32_load8_u, 0, 4 ],
|
3401
|
+
Opcodes.i32_from_u
|
3402
|
+
],
|
3403
|
+
[TYPES.int8array]: [
|
3404
|
+
[ Opcodes.i32_add ],
|
3405
|
+
|
3406
|
+
[ Opcodes.i32_load8_s, 0, 4 ],
|
3407
|
+
Opcodes.i32_from
|
3408
|
+
],
|
3409
|
+
[TYPES.uint16array]: [
|
3410
|
+
...number(2, Valtype.i32),
|
3411
|
+
[ Opcodes.i32_mul ],
|
3412
|
+
[ Opcodes.i32_add ],
|
3413
|
+
|
3414
|
+
[ Opcodes.i32_load16_u, 0, 4 ],
|
3415
|
+
Opcodes.i32_from_u
|
3416
|
+
],
|
3417
|
+
[TYPES.int16array]: [
|
3418
|
+
...number(2, Valtype.i32),
|
3419
|
+
[ Opcodes.i32_mul ],
|
3420
|
+
[ Opcodes.i32_add ],
|
3421
|
+
|
3422
|
+
[ Opcodes.i32_load16_s, 0, 4 ],
|
3423
|
+
Opcodes.i32_from
|
3424
|
+
],
|
3425
|
+
[TYPES.uint32array]: [
|
3426
|
+
...number(4, Valtype.i32),
|
3427
|
+
[ Opcodes.i32_mul ],
|
3428
|
+
[ Opcodes.i32_add ],
|
3429
|
+
|
3430
|
+
[ Opcodes.i32_load, 0, 4 ],
|
3431
|
+
Opcodes.i32_from_u
|
3432
|
+
],
|
3433
|
+
[TYPES.int32array]: [
|
3434
|
+
...number(4, Valtype.i32),
|
3435
|
+
[ Opcodes.i32_mul ],
|
3436
|
+
[ Opcodes.i32_add ],
|
3437
|
+
|
3438
|
+
[ Opcodes.i32_load, 0, 4 ],
|
3439
|
+
Opcodes.i32_from
|
3440
|
+
],
|
3441
|
+
[TYPES.float32array]: [
|
3442
|
+
...number(4, Valtype.i32),
|
3443
|
+
[ Opcodes.i32_mul ],
|
3444
|
+
[ Opcodes.i32_add ],
|
3445
|
+
|
3446
|
+
[ Opcodes.f32_load, 0, 4 ],
|
3447
|
+
[ Opcodes.f64_promote_f32 ]
|
3448
|
+
],
|
3449
|
+
[TYPES.float64array]: [
|
3450
|
+
...number(8, Valtype.i32),
|
3451
|
+
[ Opcodes.i32_mul ],
|
3452
|
+
[ Opcodes.i32_add ],
|
3453
|
+
|
3454
|
+
[ Opcodes.f64_load, 0, 4 ]
|
3455
|
+
],
|
3456
|
+
}, {
|
3457
|
+
prelude: [
|
3458
|
+
...setType(scope, leftName, TYPES.number),
|
3459
|
+
|
3460
|
+
[ Opcodes.loop, Blocktype.void ],
|
3461
|
+
|
3462
|
+
[ Opcodes.local_get, pointer ],
|
3463
|
+
[ Opcodes.local_get, counter ]
|
3464
|
+
],
|
3465
|
+
postlude: [
|
3466
|
+
[ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx ],
|
3467
|
+
|
3468
|
+
[ Opcodes.block, Blocktype.void ],
|
3469
|
+
[ Opcodes.block, Blocktype.void ],
|
3470
|
+
...generate(scope, decl.body),
|
3471
|
+
[ Opcodes.end ],
|
3472
|
+
|
3473
|
+
// increment counter by 1
|
3474
|
+
[ Opcodes.local_get, counter ],
|
3475
|
+
...number(1, Valtype.i32),
|
3476
|
+
[ Opcodes.i32_add ],
|
3477
|
+
[ Opcodes.local_tee, counter ],
|
3478
|
+
|
3479
|
+
// loop if counter != length
|
3480
|
+
[ Opcodes.local_get, length ],
|
3481
|
+
[ Opcodes.i32_ne ],
|
3482
|
+
[ Opcodes.br_if, 1 ],
|
3483
|
+
|
3484
|
+
[ Opcodes.end ],
|
3485
|
+
[ Opcodes.end ]
|
3486
|
+
]
|
3487
|
+
}),
|
3488
|
+
|
2960
3489
|
default: internalThrow(scope, 'TypeError', `Tried for..of on non-iterable type`)
|
2961
3490
|
}, Blocktype.void));
|
2962
3491
|
|
@@ -3029,32 +3558,102 @@ const generateLabel = (scope, decl) => {
|
|
3029
3558
|
const generateThrow = (scope, decl) => {
|
3030
3559
|
scope.throws = true;
|
3031
3560
|
|
3032
|
-
|
3561
|
+
const exceptionMode = Prefs.exceptionMode ?? 'lut';
|
3562
|
+
if (exceptionMode === 'lut') {
|
3563
|
+
let message = decl.argument.value, constructor = null;
|
3033
3564
|
|
3034
|
-
|
3035
|
-
|
3036
|
-
|
3037
|
-
|
3565
|
+
// support `throw (new)? Error(...)`
|
3566
|
+
if (!message && (decl.argument.type === 'NewExpression' || decl.argument.type === 'CallExpression')) {
|
3567
|
+
constructor = decl.argument.callee.name;
|
3568
|
+
message = decl.argument.arguments[0]?.value ?? '';
|
3569
|
+
}
|
3570
|
+
|
3571
|
+
if (tags.length === 0) tags.push({
|
3572
|
+
params: [ Valtype.i32 ],
|
3573
|
+
results: [],
|
3574
|
+
idx: tags.length
|
3575
|
+
});
|
3576
|
+
|
3577
|
+
let exceptId = exceptions.findIndex(x => x.constructor === constructor && x.message === message);
|
3578
|
+
if (exceptId === -1) exceptId = exceptions.push({ constructor, message }) - 1;
|
3579
|
+
|
3580
|
+
scope.exceptions ??= [];
|
3581
|
+
scope.exceptions.push(exceptId);
|
3582
|
+
|
3583
|
+
return [
|
3584
|
+
...number(exceptId, Valtype.i32),
|
3585
|
+
[ Opcodes.throw, tags[0].idx ]
|
3586
|
+
];
|
3038
3587
|
}
|
3039
3588
|
|
3040
|
-
if (
|
3041
|
-
|
3042
|
-
|
3043
|
-
|
3044
|
-
|
3589
|
+
if (exceptionMode === 'stack') {
|
3590
|
+
if (tags.length === 0) tags.push({
|
3591
|
+
params: [ valtypeBinary, Valtype.i32 ],
|
3592
|
+
results: [],
|
3593
|
+
idx: tags.length
|
3594
|
+
});
|
3045
3595
|
|
3046
|
-
|
3047
|
-
|
3596
|
+
return [
|
3597
|
+
...generate(scope, decl.argument),
|
3598
|
+
...getNodeType(scope, decl.argument),
|
3599
|
+
[ Opcodes.throw, tags[0].idx ]
|
3600
|
+
];
|
3601
|
+
}
|
3048
3602
|
|
3049
|
-
|
3050
|
-
|
3603
|
+
if (exceptionMode === 'stackest') {
|
3604
|
+
let message = decl.argument, constructor = null;
|
3051
3605
|
|
3052
|
-
|
3606
|
+
// support `throw (new)? Error(...)`
|
3607
|
+
if (message.type === 'NewExpression' || message.type === 'CallExpression') {
|
3608
|
+
constructor = decl.argument.callee;
|
3609
|
+
message = decl.argument.arguments[0];
|
3610
|
+
}
|
3053
3611
|
|
3054
|
-
|
3055
|
-
|
3056
|
-
|
3057
|
-
|
3612
|
+
message ??= DEFAULT_VALUE;
|
3613
|
+
|
3614
|
+
if (tags.length === 0) tags.push({
|
3615
|
+
params: [ valtypeBinary, valtypeBinary, Valtype.i32 ],
|
3616
|
+
results: [],
|
3617
|
+
idx: tags.length
|
3618
|
+
});
|
3619
|
+
|
3620
|
+
return [
|
3621
|
+
...(constructor == null ? number(-1) : generate(scope, constructor)),
|
3622
|
+
...generate(scope, message),
|
3623
|
+
...getNodeType(scope, message),
|
3624
|
+
[ Opcodes.throw, tags[0].idx ]
|
3625
|
+
];
|
3626
|
+
}
|
3627
|
+
|
3628
|
+
if (exceptionMode === 'partial') {
|
3629
|
+
let message = decl.argument, constructor = null;
|
3630
|
+
|
3631
|
+
// support `throw (new)? Error(...)`
|
3632
|
+
if (message.type === 'NewExpression' || message.type === 'CallExpression') {
|
3633
|
+
constructor = decl.argument.callee.name;
|
3634
|
+
message = decl.argument.arguments[0];
|
3635
|
+
}
|
3636
|
+
|
3637
|
+
message ??= DEFAULT_VALUE;
|
3638
|
+
|
3639
|
+
if (tags.length === 0) tags.push({
|
3640
|
+
params: [ Valtype.i32, valtypeBinary, Valtype.i32 ],
|
3641
|
+
results: [],
|
3642
|
+
idx: tags.length
|
3643
|
+
});
|
3644
|
+
|
3645
|
+
let exceptId = exceptions.push({ constructor }) - 1;
|
3646
|
+
|
3647
|
+
scope.exceptions ??= [];
|
3648
|
+
scope.exceptions.push(exceptId);
|
3649
|
+
|
3650
|
+
return [
|
3651
|
+
...number(exceptId, Valtype.i32),
|
3652
|
+
...generate(scope, message),
|
3653
|
+
...getNodeType(scope, message),
|
3654
|
+
[ Opcodes.throw, tags[0].idx ]
|
3655
|
+
];
|
3656
|
+
}
|
3058
3657
|
};
|
3059
3658
|
|
3060
3659
|
const generateTry = (scope, decl) => {
|
@@ -3090,6 +3689,22 @@ const generateEmpty = (scope, decl) => {
|
|
3090
3689
|
return [];
|
3091
3690
|
};
|
3092
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
|
+
|
3093
3708
|
let pages = new Map();
|
3094
3709
|
const allocPage = (scope, reason, type) => {
|
3095
3710
|
if (pages.has(reason)) return pages.get(reason).ind;
|
@@ -3407,6 +4022,19 @@ const withType = (scope, wasm, type) => [
|
|
3407
4022
|
...setLastType(scope, type)
|
3408
4023
|
];
|
3409
4024
|
|
4025
|
+
const wrapBC = (bc, { prelude = [], postlude = [] } = {}) => {
|
4026
|
+
const out = {};
|
4027
|
+
for (const x in bc) {
|
4028
|
+
out[x] = [
|
4029
|
+
...prelude,
|
4030
|
+
...bc[x],
|
4031
|
+
...postlude
|
4032
|
+
];
|
4033
|
+
}
|
4034
|
+
|
4035
|
+
return out;
|
4036
|
+
};
|
4037
|
+
|
3410
4038
|
const generateMember = (scope, decl, _global, _name) => {
|
3411
4039
|
const name = decl.object.name;
|
3412
4040
|
|
@@ -3429,20 +4057,10 @@ const generateMember = (scope, decl, _global, _name) => {
|
|
3429
4057
|
const func = funcs.find(x => x.name === name);
|
3430
4058
|
if (func) {
|
3431
4059
|
const typedParams = !func.internal || builtinFuncs[name]?.typedParams;
|
3432
|
-
return withType(scope, number(typedParams ? func.params.length / 2 : func.params.length), TYPES.number);
|
3433
|
-
}
|
3434
|
-
|
3435
|
-
if (builtinFuncs[name + '$constructor']) {
|
3436
|
-
const regularFunc = builtinFuncs[name];
|
3437
|
-
const regularParams = regularFunc.typedParams ? (regularFunc.params.length / 2) : regularFunc.params.length;
|
3438
|
-
|
3439
|
-
const constructorFunc = builtinFuncs[name + '$constructor'];
|
3440
|
-
const constructorParams = constructorFunc.typedParams ? (constructorFunc.params.length / 2) : constructorFunc.params.length;
|
3441
|
-
|
3442
|
-
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);
|
3443
4061
|
}
|
3444
4062
|
|
3445
|
-
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);
|
3446
4064
|
if (importedFuncs[name]) return withType(scope, number(importedFuncs[name].params.length ?? importedFuncs[name].params), TYPES.number);
|
3447
4065
|
if (internalConstrs[name]) return withType(scope, number(internalConstrs[name].length ?? 0), TYPES.number);
|
3448
4066
|
|
@@ -3460,7 +4078,7 @@ const generateMember = (scope, decl, _global, _name) => {
|
|
3460
4078
|
const type = getNodeType(scope, decl.object);
|
3461
4079
|
const known = knownType(scope, type);
|
3462
4080
|
if (known != null) {
|
3463
|
-
if (
|
4081
|
+
if (typeHasFlag(known, TYPE_FLAGS.length)) return [
|
3464
4082
|
...generate(scope, decl.object),
|
3465
4083
|
Opcodes.i32_to_u,
|
3466
4084
|
|
@@ -3472,7 +4090,9 @@ const generateMember = (scope, decl, _global, _name) => {
|
|
3472
4090
|
}
|
3473
4091
|
|
3474
4092
|
return [
|
3475
|
-
...
|
4093
|
+
...getNodeType(scope, decl.object),
|
4094
|
+
...number(TYPE_FLAGS.length, Valtype.i32),
|
4095
|
+
[ Opcodes.i32_and ],
|
3476
4096
|
[ Opcodes.if, valtypeBinary ],
|
3477
4097
|
...generate(scope, decl.object),
|
3478
4098
|
Opcodes.i32_to_u,
|
@@ -3489,7 +4109,7 @@ const generateMember = (scope, decl, _global, _name) => {
|
|
3489
4109
|
}
|
3490
4110
|
|
3491
4111
|
// todo: generate this array procedurally during builtinFuncs creation
|
3492
|
-
if (['size', 'description'].includes(decl.property.name)) {
|
4112
|
+
if (['size', 'description', 'byteLength'].includes(decl.property.name)) {
|
3493
4113
|
const bc = {};
|
3494
4114
|
const cands = Object.keys(builtinFuncs).filter(x => x.startsWith('__') && x.endsWith('_prototype_' + decl.property.name + '$get'));
|
3495
4115
|
|
@@ -3521,7 +4141,9 @@ const generateMember = (scope, decl, _global, _name) => {
|
|
3521
4141
|
// // todo: we should only do this for strings but we don't know at compile-time :(
|
3522
4142
|
// hack: this is naughty and will break things!
|
3523
4143
|
let newOut = number(0, Valtype.i32), newPointer = number(0, Valtype.i32);
|
3524
|
-
|
4144
|
+
|
4145
|
+
const known = knownType(scope, getNodeType(scope, decl.object));
|
4146
|
+
if ((known === TYPES.string || known === TYPES.bytestring) || (pages.hasAnyString && known == null)) {
|
3525
4147
|
// todo: we use i16 even for bytestrings which should not make a bad thing happen, just be confusing for debugging?
|
3526
4148
|
0, [ newOut, newPointer ] = makeArray(scope, {
|
3527
4149
|
rawElements: new Array(0)
|
@@ -3595,6 +4217,82 @@ const generateMember = (scope, decl, _global, _name) => {
|
|
3595
4217
|
...setLastType(scope, TYPES.bytestring)
|
3596
4218
|
],
|
3597
4219
|
|
4220
|
+
...wrapBC({
|
4221
|
+
[TYPES.uint8array]: [
|
4222
|
+
[ Opcodes.i32_add ],
|
4223
|
+
|
4224
|
+
[ Opcodes.i32_load8_u, 0, 4 ],
|
4225
|
+
Opcodes.i32_from_u
|
4226
|
+
],
|
4227
|
+
[TYPES.uint8clampedarray]: [
|
4228
|
+
[ Opcodes.i32_add ],
|
4229
|
+
|
4230
|
+
[ Opcodes.i32_load8_u, 0, 4 ],
|
4231
|
+
Opcodes.i32_from_u
|
4232
|
+
],
|
4233
|
+
[TYPES.int8array]: [
|
4234
|
+
[ Opcodes.i32_add ],
|
4235
|
+
|
4236
|
+
[ Opcodes.i32_load8_s, 0, 4 ],
|
4237
|
+
Opcodes.i32_from
|
4238
|
+
],
|
4239
|
+
[TYPES.uint16array]: [
|
4240
|
+
...number(2, Valtype.i32),
|
4241
|
+
[ Opcodes.i32_mul ],
|
4242
|
+
[ Opcodes.i32_add ],
|
4243
|
+
|
4244
|
+
[ Opcodes.i32_load16_u, 0, 4 ],
|
4245
|
+
Opcodes.i32_from_u
|
4246
|
+
],
|
4247
|
+
[TYPES.int16array]: [
|
4248
|
+
...number(2, Valtype.i32),
|
4249
|
+
[ Opcodes.i32_mul ],
|
4250
|
+
[ Opcodes.i32_add ],
|
4251
|
+
|
4252
|
+
[ Opcodes.i32_load16_s, 0, 4 ],
|
4253
|
+
Opcodes.i32_from
|
4254
|
+
],
|
4255
|
+
[TYPES.uint32array]: [
|
4256
|
+
...number(4, Valtype.i32),
|
4257
|
+
[ Opcodes.i32_mul ],
|
4258
|
+
[ Opcodes.i32_add ],
|
4259
|
+
|
4260
|
+
[ Opcodes.i32_load, 0, 4 ],
|
4261
|
+
Opcodes.i32_from_u
|
4262
|
+
],
|
4263
|
+
[TYPES.int32array]: [
|
4264
|
+
...number(4, Valtype.i32),
|
4265
|
+
[ Opcodes.i32_mul ],
|
4266
|
+
[ Opcodes.i32_add ],
|
4267
|
+
|
4268
|
+
[ Opcodes.i32_load, 0, 4 ],
|
4269
|
+
Opcodes.i32_from
|
4270
|
+
],
|
4271
|
+
[TYPES.float32array]: [
|
4272
|
+
...number(4, Valtype.i32),
|
4273
|
+
[ Opcodes.i32_mul ],
|
4274
|
+
[ Opcodes.i32_add ],
|
4275
|
+
|
4276
|
+
[ Opcodes.f32_load, 0, 4 ],
|
4277
|
+
[ Opcodes.f64_promote_f32 ]
|
4278
|
+
],
|
4279
|
+
[TYPES.float64array]: [
|
4280
|
+
...number(8, Valtype.i32),
|
4281
|
+
[ Opcodes.i32_mul ],
|
4282
|
+
[ Opcodes.i32_add ],
|
4283
|
+
|
4284
|
+
[ Opcodes.f64_load, 0, 4 ]
|
4285
|
+
],
|
4286
|
+
}, {
|
4287
|
+
prelude: [
|
4288
|
+
...object,
|
4289
|
+
Opcodes.i32_to_u,
|
4290
|
+
...property,
|
4291
|
+
Opcodes.i32_to_u
|
4292
|
+
],
|
4293
|
+
postlude: setLastType(scope, TYPES.number)
|
4294
|
+
}),
|
4295
|
+
|
3598
4296
|
default: internalThrow(scope, 'TypeError', 'Member expression is not supported for non-string non-array yet', true)
|
3599
4297
|
});
|
3600
4298
|
};
|
@@ -3617,7 +4315,7 @@ const objectHack = node => {
|
|
3617
4315
|
if (!objectName) objectName = objectHack(node.object)?.name?.slice?.(2);
|
3618
4316
|
|
3619
4317
|
// if .name or .length, give up (hack within a hack!)
|
3620
|
-
if (['name', 'length', 'size', 'description'].includes(node.property.name)) {
|
4318
|
+
if (['name', 'length', 'size', 'description', 'byteLength'].includes(node.property.name)) {
|
3621
4319
|
node.object = objectHack(node.object);
|
3622
4320
|
return;
|
3623
4321
|
}
|
@@ -3670,8 +4368,8 @@ const generateFunc = (scope, decl) => {
|
|
3670
4368
|
|
3671
4369
|
if (typedInput && decl.returnType) {
|
3672
4370
|
const { type } = extractTypeAnnotation(decl.returnType);
|
3673
|
-
|
3674
|
-
if (type != null) {
|
4371
|
+
if (type != null && !Prefs.indirectCalls) {
|
4372
|
+
// if (type != null) {
|
3675
4373
|
func.returnType = type;
|
3676
4374
|
func.returns = [ valtypeBinary ];
|
3677
4375
|
}
|
@@ -3772,7 +4470,7 @@ const internalConstrs = {
|
|
3772
4470
|
|
3773
4471
|
// todo: check in wasm instead of here
|
3774
4472
|
const literalValue = arg.value ?? 0;
|
3775
|
-
if (literalValue < 0 || !Number.isFinite(literalValue) || literalValue > 4294967295) return internalThrow(scope, '
|
4473
|
+
if (literalValue < 0 || !Number.isFinite(literalValue) || literalValue > 4294967295) return internalThrow(scope, 'RangeError', 'Invalid array length', true);
|
3776
4474
|
|
3777
4475
|
return [
|
3778
4476
|
...out,
|