porffor 0.14.0-4e46400a9 → 0.14.0-549a04f84
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/compiler/assemble.js +12 -1
- package/compiler/builtins/array.ts +6 -4
- package/compiler/builtins/boolean.ts +1 -1
- package/compiler/builtins/error.js +22 -0
- package/compiler/builtins/set.ts +5 -6
- package/compiler/builtins/symbol.ts +61 -0
- package/compiler/builtins.js +13 -6
- package/compiler/codegen.js +316 -136
- package/compiler/decompile.js +1 -1
- package/compiler/generated_builtins.js +220 -31
- package/compiler/index.js +3 -8
- package/compiler/precompile.js +1 -1
- package/compiler/prefs.js +6 -2
- package/compiler/prototype.js +180 -157
- package/compiler/wrap.js +82 -40
- package/package.json +1 -1
- package/runner/index.js +1 -1
- package/runner/repl.js +18 -2
package/compiler/codegen.js
CHANGED
@@ -140,6 +140,9 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
|
|
140
140
|
case 'ArrayExpression':
|
141
141
|
return generateArray(scope, decl, global, name);
|
142
142
|
|
143
|
+
case 'ObjectExpression':
|
144
|
+
return generateObject(scope, decl, global, name);
|
145
|
+
|
143
146
|
case 'MemberExpression':
|
144
147
|
return generateMember(scope, decl, global, name);
|
145
148
|
|
@@ -200,19 +203,11 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
|
|
200
203
|
|
201
204
|
__Porffor_bs: str => [
|
202
205
|
...makeString(scope, str, global, name, true),
|
203
|
-
|
204
|
-
...(name ? setType(scope, name, TYPES.bytestring) : [
|
205
|
-
...number(TYPES.bytestring, Valtype.i32),
|
206
|
-
...setLastType(scope)
|
207
|
-
])
|
206
|
+
...(name ? setType(scope, name, TYPES.bytestring) : setLastType(scope, TYPES.bytestring))
|
208
207
|
],
|
209
208
|
__Porffor_s: str => [
|
210
209
|
...makeString(scope, str, global, name, false),
|
211
|
-
|
212
|
-
...(name ? setType(scope, name, TYPES.string) : [
|
213
|
-
...number(TYPES.string, Valtype.i32),
|
214
|
-
...setLastType(scope)
|
215
|
-
])
|
210
|
+
...(name ? setType(scope, name, TYPES.string) : setLastType(scope, TYPES.string))
|
216
211
|
],
|
217
212
|
};
|
218
213
|
|
@@ -351,9 +346,7 @@ const generateReturn = (scope, decl) => {
|
|
351
346
|
|
352
347
|
return [
|
353
348
|
...generate(scope, decl.argument),
|
354
|
-
...(scope.returnType != null ? [] :
|
355
|
-
...getNodeType(scope, decl.argument)
|
356
|
-
]),
|
349
|
+
...(scope.returnType != null ? [] : getNodeType(scope, decl.argument)),
|
357
350
|
[ Opcodes.return ]
|
358
351
|
];
|
359
352
|
};
|
@@ -402,13 +395,11 @@ const performLogicOp = (scope, op, left, right, leftType, rightType) => {
|
|
402
395
|
[ Opcodes.if, Valtype.i32 ],
|
403
396
|
...right,
|
404
397
|
// note type
|
405
|
-
...rightType,
|
406
|
-
...setLastType(scope),
|
398
|
+
...setLastType(scope, rightType),
|
407
399
|
[ Opcodes.else ],
|
408
400
|
[ Opcodes.local_get, localTmp(scope, 'logictmpi', Valtype.i32) ],
|
409
401
|
// note type
|
410
|
-
...leftType,
|
411
|
-
...setLastType(scope),
|
402
|
+
...setLastType(scope, leftType),
|
412
403
|
[ Opcodes.end ],
|
413
404
|
Opcodes.i32_from
|
414
405
|
];
|
@@ -421,13 +412,11 @@ const performLogicOp = (scope, op, left, right, leftType, rightType) => {
|
|
421
412
|
[ Opcodes.if, valtypeBinary ],
|
422
413
|
...right,
|
423
414
|
// note type
|
424
|
-
...rightType,
|
425
|
-
...setLastType(scope),
|
415
|
+
...setLastType(scope, rightType),
|
426
416
|
[ Opcodes.else ],
|
427
417
|
[ Opcodes.local_get, localTmp(scope, 'logictmp') ],
|
428
418
|
// note type
|
429
|
-
...leftType,
|
430
|
-
...setLastType(scope),
|
419
|
+
...setLastType(scope, leftType),
|
431
420
|
[ Opcodes.end ]
|
432
421
|
];
|
433
422
|
};
|
@@ -455,11 +444,11 @@ const concatStrings = (scope, left, right, global, name, assign = false, bytestr
|
|
455
444
|
...number(0, Valtype.i32), // base 0 for store later
|
456
445
|
|
457
446
|
...number(pointer, Valtype.i32),
|
458
|
-
[ Opcodes.i32_load,
|
447
|
+
[ Opcodes.i32_load, 0, ...unsignedLEB128(0) ],
|
459
448
|
[ Opcodes.local_tee, leftLength ],
|
460
449
|
|
461
450
|
[ Opcodes.local_get, rightPointer ],
|
462
|
-
[ Opcodes.i32_load,
|
451
|
+
[ Opcodes.i32_load, 0, ...unsignedLEB128(0) ],
|
463
452
|
[ Opcodes.local_tee, rightLength ],
|
464
453
|
|
465
454
|
[ Opcodes.i32_add ],
|
@@ -515,11 +504,11 @@ const concatStrings = (scope, left, right, global, name, assign = false, bytestr
|
|
515
504
|
...number(0, Valtype.i32), // base 0 for store later
|
516
505
|
|
517
506
|
[ Opcodes.local_get, leftPointer ],
|
518
|
-
[ Opcodes.i32_load,
|
507
|
+
[ Opcodes.i32_load, 0, ...unsignedLEB128(0) ],
|
519
508
|
[ Opcodes.local_tee, leftLength ],
|
520
509
|
|
521
510
|
[ Opcodes.local_get, rightPointer ],
|
522
|
-
[ Opcodes.i32_load,
|
511
|
+
[ Opcodes.i32_load, 0, ...unsignedLEB128(0) ],
|
523
512
|
[ Opcodes.local_tee, rightLength ],
|
524
513
|
|
525
514
|
[ Opcodes.i32_add ],
|
@@ -597,11 +586,11 @@ const compareStrings = (scope, left, right, bytestrings = false) => {
|
|
597
586
|
|
598
587
|
// get lengths
|
599
588
|
[ Opcodes.local_get, leftPointer ],
|
600
|
-
[ Opcodes.i32_load,
|
589
|
+
[ Opcodes.i32_load, 0, ...unsignedLEB128(0) ],
|
601
590
|
[ Opcodes.local_tee, leftLength ],
|
602
591
|
|
603
592
|
[ Opcodes.local_get, rightPointer ],
|
604
|
-
[ Opcodes.i32_load,
|
593
|
+
[ Opcodes.i32_load, 0, ...unsignedLEB128(0) ],
|
605
594
|
|
606
595
|
// fast path: check leftLength != rightLength
|
607
596
|
[ Opcodes.i32_ne ],
|
@@ -1055,7 +1044,7 @@ const asmFuncToAsm = (func, { name = '#unknown_asm_func', params = [], locals =
|
|
1055
1044
|
});
|
1056
1045
|
};
|
1057
1046
|
|
1058
|
-
const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes = [], globalInits, returns, returnType, localNames = [], globalNames = [], data: _data = [] }) => {
|
1047
|
+
const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes = [], globalInits, returns, returnType, localNames = [], globalNames = [], data: _data = [], callsSelf = false }) => {
|
1059
1048
|
const existing = funcs.find(x => x.name === name);
|
1060
1049
|
if (existing) return existing;
|
1061
1050
|
|
@@ -1103,6 +1092,12 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
|
|
1103
1092
|
index: currentFuncIndex++
|
1104
1093
|
};
|
1105
1094
|
|
1095
|
+
if (callsSelf) for (const inst of wasm) {
|
1096
|
+
if (inst[0] === Opcodes.call && inst[1] === -1) {
|
1097
|
+
inst[1] = func.index;
|
1098
|
+
}
|
1099
|
+
}
|
1100
|
+
|
1106
1101
|
funcs.push(func);
|
1107
1102
|
funcIndex[name] = func.index;
|
1108
1103
|
|
@@ -1196,9 +1191,10 @@ const getLastType = scope => {
|
|
1196
1191
|
return [ [ Opcodes.local_get, localTmp(scope, '#last_type', Valtype.i32) ] ];
|
1197
1192
|
};
|
1198
1193
|
|
1199
|
-
const setLastType = scope =>
|
1200
|
-
|
1201
|
-
|
1194
|
+
const setLastType = (scope, type = []) => [
|
1195
|
+
...(typeof type === 'number' ? number(type, Valtype.i32) : type),
|
1196
|
+
[ Opcodes.local_set, localTmp(scope, '#last_type', Valtype.i32) ]
|
1197
|
+
];
|
1202
1198
|
|
1203
1199
|
const getNodeType = (scope, node) => {
|
1204
1200
|
const ret = (() => {
|
@@ -1259,7 +1255,17 @@ const getNodeType = (scope, node) => {
|
|
1259
1255
|
|
1260
1256
|
const func = spl[spl.length - 1];
|
1261
1257
|
const protoFuncs = Object.keys(prototypeFuncs).filter(x => x != TYPES.bytestring && prototypeFuncs[x][func] != null);
|
1262
|
-
if (protoFuncs.length === 1)
|
1258
|
+
if (protoFuncs.length === 1) {
|
1259
|
+
if (protoFuncs[0].returnType) return protoFuncs[0].returnType;
|
1260
|
+
}
|
1261
|
+
|
1262
|
+
if (protoFuncs.length > 0) {
|
1263
|
+
if (scope.locals['#last_type']) return getLastType(scope);
|
1264
|
+
|
1265
|
+
// presume
|
1266
|
+
// todo: warn here?
|
1267
|
+
return TYPES.number;
|
1268
|
+
}
|
1263
1269
|
}
|
1264
1270
|
|
1265
1271
|
if (name.startsWith('__Porffor_wasm_')) {
|
@@ -1354,22 +1360,27 @@ const getNodeType = (scope, node) => {
|
|
1354
1360
|
}
|
1355
1361
|
|
1356
1362
|
if (node.type === 'MemberExpression') {
|
1357
|
-
|
1358
|
-
|
1359
|
-
|
1360
|
-
|
1361
|
-
|
1362
|
-
return TYPES.undefined;
|
1363
|
-
}
|
1363
|
+
const name = node.property.name;
|
1364
|
+
|
1365
|
+
if (name === 'length') {
|
1366
|
+
if (hasFuncWithName(node.object.name)) return TYPES.number;
|
1367
|
+
if (Prefs.fastLength) return TYPES.number;
|
1364
1368
|
}
|
1365
1369
|
|
1366
|
-
// hack: if something.length, number type
|
1367
|
-
if (node.property.name === 'length') return TYPES.number;
|
1368
1370
|
|
1369
|
-
|
1370
|
-
if (
|
1371
|
-
|
1372
|
-
|
1371
|
+
const objectKnownType = knownType(scope, getNodeType(scope, node.object));
|
1372
|
+
if (objectKnownType != null) {
|
1373
|
+
if (name === 'length') {
|
1374
|
+
if ([ TYPES.string, TYPES.bytestring, TYPES.array ].includes(objectKnownType)) return TYPES.number;
|
1375
|
+
else return TYPES.undefined;
|
1376
|
+
}
|
1377
|
+
|
1378
|
+
if (node.computed) {
|
1379
|
+
if (objectKnownType === TYPES.string) return TYPES.string;
|
1380
|
+
if (objectKnownType === TYPES.bytestring) return TYPES.bytestring;
|
1381
|
+
if (objectKnownType === TYPES.array) return TYPES.number;
|
1382
|
+
}
|
1383
|
+
}
|
1373
1384
|
|
1374
1385
|
if (scope.locals['#last_type']) return getLastType(scope);
|
1375
1386
|
|
@@ -1451,6 +1462,10 @@ const countLeftover = wasm => {
|
|
1451
1462
|
} else count--;
|
1452
1463
|
if (func) count += func.returns.length;
|
1453
1464
|
}
|
1465
|
+
} else if (inst[0] === Opcodes.call_indirect) {
|
1466
|
+
count--; // funcidx
|
1467
|
+
count -= inst[1] * 2; // params * 2 (typed)
|
1468
|
+
count += 2; // fixed return (value, type)
|
1454
1469
|
} else count--;
|
1455
1470
|
|
1456
1471
|
// console.log(count, decompile([ inst ]).slice(0, -1));
|
@@ -1468,7 +1483,7 @@ const disposeLeftover = wasm => {
|
|
1468
1483
|
const generateExp = (scope, decl) => {
|
1469
1484
|
const expression = decl.expression;
|
1470
1485
|
|
1471
|
-
const out = generate(scope, expression, undefined, undefined,
|
1486
|
+
const out = generate(scope, expression, undefined, undefined, Prefs.optUnused);
|
1472
1487
|
disposeLeftover(out);
|
1473
1488
|
|
1474
1489
|
return out;
|
@@ -1559,16 +1574,10 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1559
1574
|
out.splice(out.length - 1, 1);
|
1560
1575
|
|
1561
1576
|
const finalStatement = parsed.body[parsed.body.length - 1];
|
1562
|
-
out.push(
|
1563
|
-
...getNodeType(scope, finalStatement),
|
1564
|
-
...setLastType(scope)
|
1565
|
-
);
|
1577
|
+
out.push(...setLastType(scope, getNodeType(scope, finalStatement)));
|
1566
1578
|
} else if (countLeftover(out) === 0) {
|
1567
1579
|
out.push(...number(UNDEFINED));
|
1568
|
-
out.push(
|
1569
|
-
...number(TYPES.undefined, Valtype.i32),
|
1570
|
-
...setLastType(scope)
|
1571
|
-
);
|
1580
|
+
out.push(...setLastType(scope, TYPES.undefined));
|
1572
1581
|
}
|
1573
1582
|
|
1574
1583
|
// if (lastInst && lastInst[0] === Opcodes.drop) {
|
@@ -1620,8 +1629,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1620
1629
|
[ Opcodes.call, idx ],
|
1621
1630
|
Opcodes.i32_from_u,
|
1622
1631
|
|
1623
|
-
...
|
1624
|
-
...setLastType(scope)
|
1632
|
+
...setLastType(scope, TYPES.boolean)
|
1625
1633
|
];
|
1626
1634
|
}
|
1627
1635
|
|
@@ -1693,9 +1701,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1693
1701
|
if (protoFunc.noArgRetLength && decl.arguments.length === 0) {
|
1694
1702
|
protoBC[x] = [
|
1695
1703
|
...RTArrayUtil.getLength(getPointer),
|
1696
|
-
|
1697
|
-
...number(TYPES.number, Valtype.i32),
|
1698
|
-
...setLastType(scope)
|
1704
|
+
...setLastType(scope, TYPES.number)
|
1699
1705
|
];
|
1700
1706
|
continue;
|
1701
1707
|
}
|
@@ -1714,7 +1720,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1714
1720
|
getI32: () => RTArrayUtil.getLengthI32(getPointer),
|
1715
1721
|
set: value => RTArrayUtil.setLength(getPointer, value),
|
1716
1722
|
setI32: value => RTArrayUtil.setLengthI32(getPointer, value)
|
1717
|
-
}, generate(scope, decl.arguments[0] ?? DEFAULT_VALUE), protoLocal, protoLocal2, (length, itemType) => {
|
1723
|
+
}, generate(scope, decl.arguments[0] ?? DEFAULT_VALUE), getNodeType(scope, decl.arguments[0] ?? DEFAULT_VALUE), protoLocal, protoLocal2, (length, itemType) => {
|
1718
1724
|
return makeArray(scope, {
|
1719
1725
|
rawElements: new Array(length)
|
1720
1726
|
}, _global, _name, true, itemType);
|
@@ -1728,9 +1734,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1728
1734
|
protoBC[x] = [
|
1729
1735
|
[ Opcodes.block, unusedValue && optUnused ? Blocktype.void : valtypeBinary ],
|
1730
1736
|
...protoOut,
|
1731
|
-
|
1732
|
-
...number(protoFunc.returnType ?? TYPES.number, Valtype.i32),
|
1733
|
-
...setLastType(scope),
|
1737
|
+
...(unusedValue && optUnused ? [] : (protoFunc.returnType != null ? setLastType(scope, protoFunc.returnType) : setLastType(scope))),
|
1734
1738
|
[ Opcodes.end ]
|
1735
1739
|
];
|
1736
1740
|
}
|
@@ -1833,22 +1837,44 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1833
1837
|
|
1834
1838
|
if (idx === undefined) {
|
1835
1839
|
if (scope.locals[name] !== undefined || globals[name] !== undefined || builtinVars[name] !== undefined) {
|
1836
|
-
if (!Prefs.indirectCalls) return internalThrow(scope, 'TypeError', `${unhackName(name)} is not a function`, true);
|
1837
|
-
|
1838
1840
|
const [ local, global ] = lookupName(scope, name);
|
1839
|
-
|
1841
|
+
if (!Prefs.indirectCalls || local == null) return internalThrow(scope, 'TypeError', `${unhackName(name)} is not a function`, true);
|
1840
1842
|
|
1841
1843
|
// todo: only works when:
|
1842
1844
|
// 1. arg count matches arg count of function
|
1843
1845
|
// 2. function uses typedParams and typedReturns
|
1844
|
-
|
1846
|
+
|
1847
|
+
funcs.table = true;
|
1848
|
+
|
1849
|
+
let args = decl.arguments;
|
1850
|
+
let argWasm = [];
|
1851
|
+
|
1852
|
+
for (let i = 0; i < args.length; i++) {
|
1853
|
+
const arg = args[i];
|
1854
|
+
argWasm = argWasm.concat(generate(scope, arg));
|
1855
|
+
|
1856
|
+
if (valtypeBinary !== Valtype.i32 && (
|
1857
|
+
(builtinFuncs[name] && builtinFuncs[name].params[i * (typedParams ? 2 : 1)] === Valtype.i32) ||
|
1858
|
+
(importedFuncs[name] && name.startsWith('profile'))
|
1859
|
+
)) {
|
1860
|
+
argWasm.push(Opcodes.i32_to);
|
1861
|
+
}
|
1862
|
+
|
1863
|
+
argWasm = argWasm.concat(getNodeType(scope, arg));
|
1864
|
+
}
|
1865
|
+
|
1866
|
+
return typeSwitch(scope, getNodeType(scope, decl.callee), {
|
1845
1867
|
[TYPES.function]: [
|
1868
|
+
...argWasm,
|
1846
1869
|
[ global ? Opcodes.global_get : Opcodes.local_get, local.idx ],
|
1847
|
-
|
1870
|
+
Opcodes.i32_to_u,
|
1871
|
+
[ Opcodes.call_indirect, args.length, 0 ],
|
1872
|
+
...setLastType(scope)
|
1848
1873
|
],
|
1849
1874
|
default: internalThrow(scope, 'TypeError', `${unhackName(name)} is not a function`, true)
|
1850
1875
|
});
|
1851
1876
|
}
|
1877
|
+
|
1852
1878
|
return internalThrow(scope, 'ReferenceError', `${unhackName(name)} is not defined`, true);
|
1853
1879
|
}
|
1854
1880
|
|
@@ -1877,6 +1903,13 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1877
1903
|
const arg = args[i];
|
1878
1904
|
out = out.concat(generate(scope, arg));
|
1879
1905
|
|
1906
|
+
// todo: this should be used instead of the too many args thing above (by removing that)
|
1907
|
+
if (i >= paramCount) {
|
1908
|
+
// over param count of func, drop arg
|
1909
|
+
out.push([ Opcodes.drop ]);
|
1910
|
+
continue;
|
1911
|
+
}
|
1912
|
+
|
1880
1913
|
if (valtypeBinary !== Valtype.i32 && (
|
1881
1914
|
(builtinFuncs[name] && builtinFuncs[name].params[i * (typedParams ? 2 : 1)] === Valtype.i32) ||
|
1882
1915
|
(importedFuncs[name] && name.startsWith('profile'))
|
@@ -1925,6 +1958,11 @@ const generateNew = (scope, decl, _global, _name) => {
|
|
1925
1958
|
}, _global, _name);
|
1926
1959
|
}
|
1927
1960
|
|
1961
|
+
if (
|
1962
|
+
(builtinFuncs[name] && !builtinFuncs[name].constr) ||
|
1963
|
+
(internalConstrs[name] && builtinFuncs[name].notConstr)
|
1964
|
+
) return internalThrow(scope, 'TypeError', `${name} is not a constructor`);
|
1965
|
+
|
1928
1966
|
if (!builtinFuncs[name]) return todo(scope, `new statement is not supported yet`); // return todo(scope, `new statement is not supported yet (new ${unhackName(name)})`);
|
1929
1967
|
|
1930
1968
|
return generateCall(scope, decl, _global, _name);
|
@@ -1950,8 +1988,11 @@ const knownType = (scope, type) => {
|
|
1950
1988
|
const idx = type[0][1];
|
1951
1989
|
|
1952
1990
|
// type idx = var idx + 1
|
1953
|
-
const
|
1954
|
-
if (
|
1991
|
+
const name = Object.values(scope.locals).find(x => x.idx === idx)?.name;
|
1992
|
+
if (name) {
|
1993
|
+
const local = scope.locals[name];
|
1994
|
+
if (local.metadata?.type != null) return v.metadata.type;
|
1995
|
+
}
|
1955
1996
|
}
|
1956
1997
|
|
1957
1998
|
return null;
|
@@ -2082,6 +2123,17 @@ const typeSwitch = (scope, type, bc, returns = valtypeBinary) => {
|
|
2082
2123
|
return out;
|
2083
2124
|
};
|
2084
2125
|
|
2126
|
+
const typeIsOneOf = (type, types, valtype = Valtype.i32) => {
|
2127
|
+
const out = [];
|
2128
|
+
|
2129
|
+
for (let i = 0; i < types.length; i++) {
|
2130
|
+
out.push(...type, ...number(types[i], valtype), valtype === Valtype.f64 ? [ Opcodes.f64_eq ] : [ Opcodes.i32_eq ]);
|
2131
|
+
if (i !== 0) out.push([ Opcodes.i32_or ]);
|
2132
|
+
}
|
2133
|
+
|
2134
|
+
return out;
|
2135
|
+
};
|
2136
|
+
|
2085
2137
|
const allocVar = (scope, name, global = false, type = true) => {
|
2086
2138
|
const target = global ? globals : scope.locals;
|
2087
2139
|
|
@@ -2098,7 +2150,7 @@ const allocVar = (scope, name, global = false, type = true) => {
|
|
2098
2150
|
|
2099
2151
|
if (type) {
|
2100
2152
|
let typeIdx = global ? globalInd++ : scope.localInd++;
|
2101
|
-
target[name + '#type'] = { idx: typeIdx, type: Valtype.i32 };
|
2153
|
+
target[name + '#type'] = { idx: typeIdx, type: Valtype.i32, name };
|
2102
2154
|
}
|
2103
2155
|
|
2104
2156
|
return idx;
|
@@ -2311,18 +2363,21 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
|
|
2311
2363
|
Opcodes.i32_to_u,
|
2312
2364
|
|
2313
2365
|
// turn into byte offset by * valtypeSize (4 for i32, 8 for i64/f64)
|
2314
|
-
...number(ValtypeSize[valtype], Valtype.i32),
|
2366
|
+
...number(ValtypeSize[valtype] + 1, Valtype.i32),
|
2315
2367
|
[ Opcodes.i32_mul ],
|
2316
2368
|
...(aotPointer ? [] : [ [ Opcodes.i32_add ] ]),
|
2317
2369
|
...(op === '=' ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
|
2318
2370
|
|
2319
2371
|
...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
|
2320
2372
|
[ Opcodes.local_get, pointerTmp ],
|
2321
|
-
[ Opcodes.load,
|
2322
|
-
], generate(scope, decl.right),
|
2373
|
+
[ Opcodes.load, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ]
|
2374
|
+
], generate(scope, decl.right), [
|
2375
|
+
[ Opcodes.local_get, pointerTmp ],
|
2376
|
+
[ Opcodes.i32_load8_u, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32 + ValtypeSize[valtype]) ]
|
2377
|
+
], getNodeType(scope, decl.right), false, name, true)),
|
2323
2378
|
[ Opcodes.local_tee, newValueTmp ],
|
2324
2379
|
|
2325
|
-
[ Opcodes.store,
|
2380
|
+
[ Opcodes.store, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ]
|
2326
2381
|
],
|
2327
2382
|
|
2328
2383
|
default: internalThrow(scope, 'TypeError', `Cannot assign member with non-array`)
|
@@ -2499,6 +2554,7 @@ const generateUnary = (scope, decl) => {
|
|
2499
2554
|
[TYPES.string]: makeString(scope, 'string', false, '#typeof_result'),
|
2500
2555
|
[TYPES.undefined]: makeString(scope, 'undefined', false, '#typeof_result'),
|
2501
2556
|
[TYPES.function]: makeString(scope, 'function', false, '#typeof_result'),
|
2557
|
+
[TYPES.symbol]: makeString(scope, 'symbol', false, '#typeof_result'),
|
2502
2558
|
|
2503
2559
|
[TYPES.bytestring]: makeString(scope, 'string', false, '#typeof_result'),
|
2504
2560
|
|
@@ -2575,21 +2631,16 @@ const generateConditional = (scope, decl) => {
|
|
2575
2631
|
out.push([ Opcodes.if, valtypeBinary ]);
|
2576
2632
|
depth.push('if');
|
2577
2633
|
|
2578
|
-
out.push(...generate(scope, decl.consequent));
|
2579
|
-
|
2580
|
-
// note type
|
2581
2634
|
out.push(
|
2582
|
-
...
|
2583
|
-
...setLastType(scope)
|
2635
|
+
...generate(scope, decl.consequent),
|
2636
|
+
...setLastType(scope, getNodeType(scope, decl.consequent))
|
2584
2637
|
);
|
2585
2638
|
|
2586
2639
|
out.push([ Opcodes.else ]);
|
2587
|
-
out.push(...generate(scope, decl.alternate));
|
2588
2640
|
|
2589
|
-
// note type
|
2590
2641
|
out.push(
|
2591
|
-
...
|
2592
|
-
...setLastType(scope)
|
2642
|
+
...generate(scope, decl.alternate),
|
2643
|
+
...setLastType(scope, getNodeType(scope, decl.alternate))
|
2593
2644
|
);
|
2594
2645
|
|
2595
2646
|
out.push([ Opcodes.end ]);
|
@@ -2737,12 +2788,15 @@ const generateForOf = (scope, decl) => {
|
|
2737
2788
|
// todo: optimize away counter and use end pointer
|
2738
2789
|
out.push(...typeSwitch(scope, getNodeType(scope, decl.right), {
|
2739
2790
|
[TYPES.array]: [
|
2740
|
-
...setType(scope, leftName, TYPES.number),
|
2741
|
-
|
2742
2791
|
[ Opcodes.loop, Blocktype.void ],
|
2743
2792
|
|
2744
2793
|
[ Opcodes.local_get, pointer ],
|
2745
|
-
[ Opcodes.load,
|
2794
|
+
[ Opcodes.load, 0, ...unsignedLEB128(ValtypeSize.i32) ],
|
2795
|
+
|
2796
|
+
...setType(scope, leftName, [
|
2797
|
+
[ Opcodes.local_get, pointer ],
|
2798
|
+
[ Opcodes.i32_load8_u, 0, ...unsignedLEB128(ValtypeSize.i32 + ValtypeSize[valtype]) ],
|
2799
|
+
]),
|
2746
2800
|
|
2747
2801
|
[ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx ],
|
2748
2802
|
|
@@ -2751,9 +2805,9 @@ const generateForOf = (scope, decl) => {
|
|
2751
2805
|
...generate(scope, decl.body),
|
2752
2806
|
[ Opcodes.end ],
|
2753
2807
|
|
2754
|
-
// increment iter pointer by valtype size
|
2808
|
+
// increment iter pointer by valtype size + 1
|
2755
2809
|
[ Opcodes.local_get, pointer ],
|
2756
|
-
...number(ValtypeSize[valtype], Valtype.i32),
|
2810
|
+
...number(ValtypeSize[valtype] + 1, Valtype.i32),
|
2757
2811
|
[ Opcodes.i32_add ],
|
2758
2812
|
[ Opcodes.local_set, pointer ],
|
2759
2813
|
|
@@ -3070,7 +3124,7 @@ const getAllocType = itemType => {
|
|
3070
3124
|
}
|
3071
3125
|
};
|
3072
3126
|
|
3073
|
-
const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty = false, itemType = valtype) => {
|
3127
|
+
const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty = false, itemType = valtype, typed = false) => {
|
3074
3128
|
const out = [];
|
3075
3129
|
|
3076
3130
|
scope.arrays ??= new Map();
|
@@ -3133,7 +3187,7 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
3133
3187
|
|
3134
3188
|
const pointerWasm = pointerTmp != null ? [ [ Opcodes.local_get, pointerTmp ] ] : number(pointer, Valtype.i32);
|
3135
3189
|
|
3136
|
-
// store length
|
3190
|
+
// store length
|
3137
3191
|
out.push(
|
3138
3192
|
...pointerWasm,
|
3139
3193
|
...number(length, Valtype.i32),
|
@@ -3141,14 +3195,20 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
3141
3195
|
);
|
3142
3196
|
|
3143
3197
|
const storeOp = StoreOps[itemType];
|
3144
|
-
|
3198
|
+
const sizePerEl = ValtypeSize[itemType] + (typed ? 1 : 0);
|
3145
3199
|
if (!initEmpty) for (let i = 0; i < length; i++) {
|
3146
3200
|
if (elements[i] == null) continue;
|
3147
3201
|
|
3202
|
+
const offset = ValtypeSize.i32 + i * sizePerEl;
|
3148
3203
|
out.push(
|
3149
3204
|
...pointerWasm,
|
3150
3205
|
...(useRawElements ? number(elements[i], Valtype[valtype]) : generate(scope, elements[i])),
|
3151
|
-
[ storeOp,
|
3206
|
+
[ storeOp, 0, ...unsignedLEB128(offset) ],
|
3207
|
+
...(!typed ? [] : [ // typed presumes !useRawElements
|
3208
|
+
...pointerWasm,
|
3209
|
+
...getNodeType(scope, elements[i]),
|
3210
|
+
[ Opcodes.i32_store8, 0, ...unsignedLEB128(offset + ValtypeSize[itemType]) ]
|
3211
|
+
])
|
3152
3212
|
);
|
3153
3213
|
}
|
3154
3214
|
|
@@ -3158,6 +3218,65 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
3158
3218
|
return [ out, pointer ];
|
3159
3219
|
};
|
3160
3220
|
|
3221
|
+
const storeArray = (scope, array, index, element, aotPointer = null) => {
|
3222
|
+
if (!Array.isArray(element)) element = generate(scope, element);
|
3223
|
+
if (typeof index === 'number') index = number(index);
|
3224
|
+
|
3225
|
+
const offset = localTmp(scope, '#storeArray_offset', Valtype.i32);
|
3226
|
+
|
3227
|
+
return [
|
3228
|
+
// calculate offset
|
3229
|
+
...index,
|
3230
|
+
Opcodes.i32_to_u,
|
3231
|
+
...number(ValtypeSize[valtype] + 1, Valtype.i32),
|
3232
|
+
[ Opcodes.i32_mul ],
|
3233
|
+
...(aotPointer ? [] : [
|
3234
|
+
...array,
|
3235
|
+
Opcodes.i32_to_u,
|
3236
|
+
[ Opcodes.i32_add ],
|
3237
|
+
]),
|
3238
|
+
[ Opcodes.local_set, offset ],
|
3239
|
+
|
3240
|
+
// store value
|
3241
|
+
[ Opcodes.local_get, offset ],
|
3242
|
+
...generate(scope, element),
|
3243
|
+
[ Opcodes.store, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ],
|
3244
|
+
|
3245
|
+
// store type
|
3246
|
+
[ Opcodes.local_get, offset ],
|
3247
|
+
...getNodeType(scope, element),
|
3248
|
+
[ Opcodes.i32_store8, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32 + ValtypeSize[valtype]) ]
|
3249
|
+
];
|
3250
|
+
};
|
3251
|
+
|
3252
|
+
const loadArray = (scope, array, index, aotPointer = null) => {
|
3253
|
+
if (typeof index === 'number') index = number(index);
|
3254
|
+
|
3255
|
+
const offset = localTmp(scope, '#loadArray_offset', Valtype.i32);
|
3256
|
+
|
3257
|
+
return [
|
3258
|
+
// calculate offset
|
3259
|
+
...index,
|
3260
|
+
Opcodes.i32_to_u,
|
3261
|
+
...number(ValtypeSize[valtype] + 1, Valtype.i32),
|
3262
|
+
[ Opcodes.i32_mul ],
|
3263
|
+
...(aotPointer ? [] : [
|
3264
|
+
...array,
|
3265
|
+
Opcodes.i32_to_u,
|
3266
|
+
[ Opcodes.i32_add ],
|
3267
|
+
]),
|
3268
|
+
[ Opcodes.local_set, offset ],
|
3269
|
+
|
3270
|
+
// load value
|
3271
|
+
[ Opcodes.local_get, offset ],
|
3272
|
+
[ Opcodes.load, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ],
|
3273
|
+
|
3274
|
+
// load type
|
3275
|
+
[ Opcodes.local_get, offset ],
|
3276
|
+
[ Opcodes.i32_load8_u, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32 + ValtypeSize[valtype]) ]
|
3277
|
+
];
|
3278
|
+
};
|
3279
|
+
|
3161
3280
|
const byteStringable = str => {
|
3162
3281
|
if (!Prefs.bytestring) return false;
|
3163
3282
|
|
@@ -3186,14 +3305,28 @@ const makeString = (scope, str, global = false, name = '$undeclared', forceBytes
|
|
3186
3305
|
};
|
3187
3306
|
|
3188
3307
|
const generateArray = (scope, decl, global = false, name = '$undeclared', initEmpty = false) => {
|
3189
|
-
return makeArray(scope, decl, global, name, initEmpty, valtype)[0];
|
3308
|
+
return makeArray(scope, decl, global, name, initEmpty, valtype, true)[0];
|
3309
|
+
};
|
3310
|
+
|
3311
|
+
const generateObject = (scope, decl, global = false, name = '$undeclared') => {
|
3312
|
+
if (decl.properties.length > 0) return todo(scope, 'objects are not supported yet', true);
|
3313
|
+
|
3314
|
+
return [
|
3315
|
+
...number(1),
|
3316
|
+
...setLastType(scope, TYPES.object)
|
3317
|
+
];
|
3190
3318
|
};
|
3191
3319
|
|
3192
|
-
|
3320
|
+
const withType = (scope, wasm, type) => [
|
3321
|
+
...wasm,
|
3322
|
+
...setLastType(scope, type)
|
3323
|
+
];
|
3324
|
+
|
3325
|
+
const generateMember = (scope, decl, _global, _name) => {
|
3193
3326
|
const name = decl.object.name;
|
3194
3327
|
const pointer = scope.arrays?.get(name);
|
3195
3328
|
|
3196
|
-
const aotPointer = Prefs.aotPointerOpt && pointer
|
3329
|
+
const aotPointer = Prefs.aotPointerOpt && pointer;
|
3197
3330
|
|
3198
3331
|
// hack: .name
|
3199
3332
|
if (decl.property.name === 'name') {
|
@@ -3203,9 +3336,9 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
3203
3336
|
// eg: __String_prototype_toLowerCase -> toLowerCase
|
3204
3337
|
if (nameProp.startsWith('__')) nameProp = nameProp.split('_').pop();
|
3205
3338
|
|
3206
|
-
return makeString(scope, nameProp, _global, _name, true);
|
3339
|
+
return withType(scope, makeString(scope, nameProp, _global, _name, true), TYPES.bytestring);
|
3207
3340
|
} else {
|
3208
|
-
return
|
3341
|
+
return withType(scope, number(0), TYPES.undefined);
|
3209
3342
|
}
|
3210
3343
|
}
|
3211
3344
|
|
@@ -3215,7 +3348,7 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
3215
3348
|
if (func) {
|
3216
3349
|
const userFunc = funcIndex[name] && !importedFuncs[name] && !builtinFuncs[name] && !internalConstrs[name];
|
3217
3350
|
const typedParams = userFunc || builtinFuncs[name]?.typedParams;
|
3218
|
-
return number(typedParams ? func.params.length / 2 : func.params.length);
|
3351
|
+
return withType(scope, number(typedParams ? func.params.length / 2 : func.params.length), TYPES.number);
|
3219
3352
|
}
|
3220
3353
|
|
3221
3354
|
if (builtinFuncs[name + '$constructor']) {
|
@@ -3225,24 +3358,88 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
3225
3358
|
const constructorFunc = builtinFuncs[name + '$constructor'];
|
3226
3359
|
const constructorParams = constructorFunc.typedParams ? (constructorFunc.params.length / 2) : constructorFunc.params.length;
|
3227
3360
|
|
3228
|
-
return number(Math.max(regularParams, constructorParams));
|
3361
|
+
return withType(scope, number(Math.max(regularParams, constructorParams)), TYPES.number);
|
3362
|
+
}
|
3363
|
+
|
3364
|
+
if (builtinFuncs[name]) return withType(scope, number(builtinFuncs[name].typedParams ? (builtinFuncs[name].params.length / 2) : builtinFuncs[name].params.length), TYPES.number);
|
3365
|
+
if (importedFuncs[name]) return withType(scope, number(importedFuncs[name].params), TYPES.number);
|
3366
|
+
if (internalConstrs[name]) return withType(scope, number(internalConstrs[name].length ?? 0), TYPES.number);
|
3367
|
+
|
3368
|
+
if (Prefs.fastLength) {
|
3369
|
+
// presume valid length object
|
3370
|
+
return [
|
3371
|
+
...(aotPointer ? number(0, Valtype.i32) : [
|
3372
|
+
...generate(scope, decl.object),
|
3373
|
+
Opcodes.i32_to_u
|
3374
|
+
]),
|
3375
|
+
|
3376
|
+
[ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(aotPointer ? pointer : 0) ],
|
3377
|
+
Opcodes.i32_from_u
|
3378
|
+
];
|
3229
3379
|
}
|
3230
3380
|
|
3231
|
-
|
3232
|
-
|
3233
|
-
if (
|
3381
|
+
const type = getNodeType(scope, decl.object);
|
3382
|
+
const known = knownType(scope, type);
|
3383
|
+
if (known != null) {
|
3384
|
+
if ([ TYPES.string, TYPES.bytestring, TYPES.array ].includes(known)) return [
|
3385
|
+
...(aotPointer ? number(0, Valtype.i32) : [
|
3386
|
+
...generate(scope, decl.object),
|
3387
|
+
Opcodes.i32_to_u
|
3388
|
+
]),
|
3389
|
+
|
3390
|
+
[ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(aotPointer ? pointer : 0) ],
|
3391
|
+
Opcodes.i32_from_u
|
3392
|
+
];
|
3393
|
+
|
3394
|
+
return number(0);
|
3395
|
+
}
|
3234
3396
|
|
3235
3397
|
return [
|
3236
|
-
...(
|
3237
|
-
|
3238
|
-
|
3239
|
-
|
3398
|
+
...typeIsOneOf(getNodeType(scope, decl.object), [ TYPES.string, TYPES.bytestring, TYPES.array ]),
|
3399
|
+
[ Opcodes.if, valtypeBinary ],
|
3400
|
+
...(aotPointer ? number(0, Valtype.i32) : [
|
3401
|
+
...generate(scope, decl.object),
|
3402
|
+
Opcodes.i32_to_u
|
3403
|
+
]),
|
3240
3404
|
|
3241
|
-
|
3242
|
-
|
3405
|
+
[ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(aotPointer ? pointer : 0) ],
|
3406
|
+
Opcodes.i32_from_u,
|
3407
|
+
|
3408
|
+
...setLastType(scope, TYPES.number),
|
3409
|
+
[ Opcodes.else ],
|
3410
|
+
...number(0),
|
3411
|
+
...setLastType(scope, TYPES.undefined),
|
3412
|
+
[ Opcodes.end ]
|
3243
3413
|
];
|
3244
3414
|
}
|
3245
3415
|
|
3416
|
+
// todo: generate this array procedurally during builtinFuncs creation
|
3417
|
+
if (['size', 'description'].includes(decl.property.name)) {
|
3418
|
+
const bc = {};
|
3419
|
+
const cands = Object.keys(builtinFuncs).filter(x => x.startsWith('__') && x.endsWith('_prototype_' + decl.property.name + '$get'));
|
3420
|
+
|
3421
|
+
if (cands.length > 0) {
|
3422
|
+
for (const x of cands) {
|
3423
|
+
const type = TYPES[x.split('_prototype_')[0].slice(2).toLowerCase()];
|
3424
|
+
if (type == null) continue;
|
3425
|
+
|
3426
|
+
bc[type] = generateCall(scope, {
|
3427
|
+
callee: {
|
3428
|
+
type: 'Identifier',
|
3429
|
+
name: x
|
3430
|
+
},
|
3431
|
+
arguments: [ decl.object ],
|
3432
|
+
_protoInternalCall: true
|
3433
|
+
});
|
3434
|
+
}
|
3435
|
+
}
|
3436
|
+
|
3437
|
+
return typeSwitch(scope, getNodeType(scope, decl.object), {
|
3438
|
+
...bc,
|
3439
|
+
default: withType(scope, number(0), TYPES.undefined)
|
3440
|
+
}, valtypeBinary);
|
3441
|
+
}
|
3442
|
+
|
3246
3443
|
const object = generate(scope, decl.object);
|
3247
3444
|
const property = generate(scope, decl.property);
|
3248
3445
|
|
@@ -3257,24 +3454,7 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
3257
3454
|
|
3258
3455
|
return typeSwitch(scope, getNodeType(scope, decl.object), {
|
3259
3456
|
[TYPES.array]: [
|
3260
|
-
|
3261
|
-
...property,
|
3262
|
-
|
3263
|
-
// convert to i32 and turn into byte offset by * valtypeSize (4 for i32, 8 for i64/f64)
|
3264
|
-
Opcodes.i32_to_u,
|
3265
|
-
...number(ValtypeSize[valtype], Valtype.i32),
|
3266
|
-
[ Opcodes.i32_mul ],
|
3267
|
-
|
3268
|
-
...(aotPointer ? [] : [
|
3269
|
-
...object,
|
3270
|
-
Opcodes.i32_to_u,
|
3271
|
-
[ Opcodes.i32_add ]
|
3272
|
-
]),
|
3273
|
-
|
3274
|
-
// read from memory
|
3275
|
-
[ Opcodes.load, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ],
|
3276
|
-
|
3277
|
-
...number(TYPES.number, Valtype.i32),
|
3457
|
+
...loadArray(scope, object, property, aotPointer),
|
3278
3458
|
...setLastType(scope)
|
3279
3459
|
],
|
3280
3460
|
|
@@ -3305,9 +3485,7 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
3305
3485
|
|
3306
3486
|
// return new string (page)
|
3307
3487
|
...number(newPointer),
|
3308
|
-
|
3309
|
-
...number(TYPES.string, Valtype.i32),
|
3310
|
-
...setLastType(scope)
|
3488
|
+
...setLastType(scope, TYPES.string)
|
3311
3489
|
],
|
3312
3490
|
[TYPES.bytestring]: [
|
3313
3491
|
// setup new/out array
|
@@ -3333,9 +3511,7 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
3333
3511
|
|
3334
3512
|
// return new string (page)
|
3335
3513
|
...number(newPointer),
|
3336
|
-
|
3337
|
-
...number(TYPES.bytestring, Valtype.i32),
|
3338
|
-
...setLastType(scope)
|
3514
|
+
...setLastType(scope, TYPES.bytestring)
|
3339
3515
|
],
|
3340
3516
|
|
3341
3517
|
default: internalThrow(scope, 'TypeError', 'Member expression is not supported for non-string non-array yet', true)
|
@@ -3360,7 +3536,7 @@ const objectHack = node => {
|
|
3360
3536
|
if (!objectName) objectName = objectHack(node.object)?.name?.slice?.(2);
|
3361
3537
|
|
3362
3538
|
// if .name or .length, give up (hack within a hack!)
|
3363
|
-
if (['name', 'length'].includes(node.property.name)) {
|
3539
|
+
if (['name', 'length', 'size', 'description'].includes(node.property.name)) {
|
3364
3540
|
node.object = objectHack(node.object);
|
3365
3541
|
return;
|
3366
3542
|
}
|
@@ -3628,8 +3804,10 @@ const internalConstrs = {
|
|
3628
3804
|
}),
|
3629
3805
|
|
3630
3806
|
// print space
|
3631
|
-
...
|
3632
|
-
|
3807
|
+
...(i !== decl.arguments.length - 1 ? [
|
3808
|
+
...number(32),
|
3809
|
+
[ Opcodes.call, importedFuncs.printChar ]
|
3810
|
+
] : [])
|
3633
3811
|
);
|
3634
3812
|
}
|
3635
3813
|
|
@@ -3639,6 +3817,8 @@ const internalConstrs = {
|
|
3639
3817
|
[ Opcodes.call, importedFuncs.printChar ]
|
3640
3818
|
);
|
3641
3819
|
|
3820
|
+
out.push(...number(UNDEFINED));
|
3821
|
+
|
3642
3822
|
return out;
|
3643
3823
|
},
|
3644
3824
|
type: TYPES.undefined,
|