porffor 0.14.0-cdebd5442 → 0.14.0-db4b90996
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/CONTRIBUTING.md +5 -1
- package/asur/index.js +1 -1
- package/compiler/assemble.js +14 -0
- package/compiler/builtins/array.ts +28 -4
- package/compiler/builtins/error.js +22 -0
- package/compiler/builtins/set.ts +5 -6
- package/compiler/builtins/symbol.ts +1 -1
- package/compiler/builtins.js +9 -4
- package/compiler/codegen.js +405 -144
- package/compiler/generated_builtins.js +223 -59
- package/compiler/precompile.js +4 -3
- package/compiler/prefs.js +1 -1
- package/compiler/prototype.js +180 -157
- package/compiler/wrap.js +66 -37
- 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
|
|
@@ -400,13 +395,11 @@ const performLogicOp = (scope, op, left, right, leftType, rightType) => {
|
|
400
395
|
[ Opcodes.if, Valtype.i32 ],
|
401
396
|
...right,
|
402
397
|
// note type
|
403
|
-
...rightType,
|
404
|
-
...setLastType(scope),
|
398
|
+
...setLastType(scope, rightType),
|
405
399
|
[ Opcodes.else ],
|
406
400
|
[ Opcodes.local_get, localTmp(scope, 'logictmpi', Valtype.i32) ],
|
407
401
|
// note type
|
408
|
-
...leftType,
|
409
|
-
...setLastType(scope),
|
402
|
+
...setLastType(scope, leftType),
|
410
403
|
[ Opcodes.end ],
|
411
404
|
Opcodes.i32_from
|
412
405
|
];
|
@@ -419,13 +412,11 @@ const performLogicOp = (scope, op, left, right, leftType, rightType) => {
|
|
419
412
|
[ Opcodes.if, valtypeBinary ],
|
420
413
|
...right,
|
421
414
|
// note type
|
422
|
-
...rightType,
|
423
|
-
...setLastType(scope),
|
415
|
+
...setLastType(scope, rightType),
|
424
416
|
[ Opcodes.else ],
|
425
417
|
[ Opcodes.local_get, localTmp(scope, 'logictmp') ],
|
426
418
|
// note type
|
427
|
-
...leftType,
|
428
|
-
...setLastType(scope),
|
419
|
+
...setLastType(scope, leftType),
|
429
420
|
[ Opcodes.end ]
|
430
421
|
];
|
431
422
|
};
|
@@ -453,11 +444,11 @@ const concatStrings = (scope, left, right, global, name, assign = false, bytestr
|
|
453
444
|
...number(0, Valtype.i32), // base 0 for store later
|
454
445
|
|
455
446
|
...number(pointer, Valtype.i32),
|
456
|
-
[ Opcodes.i32_load,
|
447
|
+
[ Opcodes.i32_load, 0, ...unsignedLEB128(0) ],
|
457
448
|
[ Opcodes.local_tee, leftLength ],
|
458
449
|
|
459
450
|
[ Opcodes.local_get, rightPointer ],
|
460
|
-
[ Opcodes.i32_load,
|
451
|
+
[ Opcodes.i32_load, 0, ...unsignedLEB128(0) ],
|
461
452
|
[ Opcodes.local_tee, rightLength ],
|
462
453
|
|
463
454
|
[ Opcodes.i32_add ],
|
@@ -513,11 +504,11 @@ const concatStrings = (scope, left, right, global, name, assign = false, bytestr
|
|
513
504
|
...number(0, Valtype.i32), // base 0 for store later
|
514
505
|
|
515
506
|
[ Opcodes.local_get, leftPointer ],
|
516
|
-
[ Opcodes.i32_load,
|
507
|
+
[ Opcodes.i32_load, 0, ...unsignedLEB128(0) ],
|
517
508
|
[ Opcodes.local_tee, leftLength ],
|
518
509
|
|
519
510
|
[ Opcodes.local_get, rightPointer ],
|
520
|
-
[ Opcodes.i32_load,
|
511
|
+
[ Opcodes.i32_load, 0, ...unsignedLEB128(0) ],
|
521
512
|
[ Opcodes.local_tee, rightLength ],
|
522
513
|
|
523
514
|
[ Opcodes.i32_add ],
|
@@ -595,11 +586,11 @@ const compareStrings = (scope, left, right, bytestrings = false) => {
|
|
595
586
|
|
596
587
|
// get lengths
|
597
588
|
[ Opcodes.local_get, leftPointer ],
|
598
|
-
[ Opcodes.i32_load,
|
589
|
+
[ Opcodes.i32_load, 0, ...unsignedLEB128(0) ],
|
599
590
|
[ Opcodes.local_tee, leftLength ],
|
600
591
|
|
601
592
|
[ Opcodes.local_get, rightPointer ],
|
602
|
-
[ Opcodes.i32_load,
|
593
|
+
[ Opcodes.i32_load, 0, ...unsignedLEB128(0) ],
|
603
594
|
|
604
595
|
// fast path: check leftLength != rightLength
|
605
596
|
[ Opcodes.i32_ne ],
|
@@ -690,6 +681,25 @@ const truthy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
690
681
|
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
691
682
|
|
692
683
|
// ...(intIn ? [ [ Opcodes.i32_eqz ] ] : [ ...Opcodes.eqz ]),
|
684
|
+
// [ Opcodes.i32_eqz ],
|
685
|
+
|
686
|
+
// ...(intIn ? [
|
687
|
+
// ...number(0, Valtype.i32),
|
688
|
+
// [ Opcodes.i32_gt_s ]
|
689
|
+
// ] : [
|
690
|
+
// ...number(0),
|
691
|
+
// [ Opcodes.f64_gt ]
|
692
|
+
// ]),
|
693
|
+
|
694
|
+
// ...(intOut ? [] : [ Opcodes.i32_from ]),
|
695
|
+
|
696
|
+
// ...(intIn ? [] : [ Opcodes.i32_to ]),
|
697
|
+
// [ Opcodes.if, intOut ? Valtype.i32 : valtypeBinary ],
|
698
|
+
// ...number(1, intOut ? Valtype.i32 : valtypeBinary),
|
699
|
+
// [ Opcodes.else ],
|
700
|
+
// ...number(0, intOut ? Valtype.i32 : valtypeBinary),
|
701
|
+
// [ Opcodes.end ],
|
702
|
+
|
693
703
|
...(!intOut || (intIn && intOut) ? [] : [ Opcodes.i32_to_u ]),
|
694
704
|
|
695
705
|
/* Opcodes.eqz,
|
@@ -1053,7 +1063,7 @@ const asmFuncToAsm = (func, { name = '#unknown_asm_func', params = [], locals =
|
|
1053
1063
|
});
|
1054
1064
|
};
|
1055
1065
|
|
1056
|
-
const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes = [], globalInits, returns, returnType, localNames = [], globalNames = [], data: _data = [] }) => {
|
1066
|
+
const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes = [], globalInits, returns, returnType, localNames = [], globalNames = [], data: _data = [], table = false, callsSelf = false }) => {
|
1057
1067
|
const existing = funcs.find(x => x.name === name);
|
1058
1068
|
if (existing) return existing;
|
1059
1069
|
|
@@ -1101,9 +1111,24 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
|
|
1101
1111
|
index: currentFuncIndex++
|
1102
1112
|
};
|
1103
1113
|
|
1114
|
+
if (callsSelf) for (const inst of wasm) {
|
1115
|
+
if (inst[0] === Opcodes.call && inst[1] === -1) {
|
1116
|
+
inst[1] = func.index;
|
1117
|
+
}
|
1118
|
+
}
|
1119
|
+
|
1120
|
+
if (table) for (const inst of wasm) {
|
1121
|
+
if (inst[0] === Opcodes.i32_load16_u && inst.at(-1) === 'read_argc') {
|
1122
|
+
inst.splice(2, 99);
|
1123
|
+
inst.push(...unsignedLEB128(allocPage({}, 'func argc lut') * pageSize));
|
1124
|
+
}
|
1125
|
+
}
|
1126
|
+
|
1104
1127
|
funcs.push(func);
|
1105
1128
|
funcIndex[name] = func.index;
|
1106
1129
|
|
1130
|
+
if (table) funcs.table = true;
|
1131
|
+
|
1107
1132
|
return func;
|
1108
1133
|
};
|
1109
1134
|
|
@@ -1194,9 +1219,10 @@ const getLastType = scope => {
|
|
1194
1219
|
return [ [ Opcodes.local_get, localTmp(scope, '#last_type', Valtype.i32) ] ];
|
1195
1220
|
};
|
1196
1221
|
|
1197
|
-
const setLastType = scope =>
|
1198
|
-
|
1199
|
-
|
1222
|
+
const setLastType = (scope, type = []) => [
|
1223
|
+
...(typeof type === 'number' ? number(type, Valtype.i32) : type),
|
1224
|
+
[ Opcodes.local_set, localTmp(scope, '#last_type', Valtype.i32) ]
|
1225
|
+
];
|
1200
1226
|
|
1201
1227
|
const getNodeType = (scope, node) => {
|
1202
1228
|
const ret = (() => {
|
@@ -1257,7 +1283,17 @@ const getNodeType = (scope, node) => {
|
|
1257
1283
|
|
1258
1284
|
const func = spl[spl.length - 1];
|
1259
1285
|
const protoFuncs = Object.keys(prototypeFuncs).filter(x => x != TYPES.bytestring && prototypeFuncs[x][func] != null);
|
1260
|
-
if (protoFuncs.length === 1)
|
1286
|
+
if (protoFuncs.length === 1) {
|
1287
|
+
if (protoFuncs[0].returnType) return protoFuncs[0].returnType;
|
1288
|
+
}
|
1289
|
+
|
1290
|
+
if (protoFuncs.length > 0) {
|
1291
|
+
if (scope.locals['#last_type']) return getLastType(scope);
|
1292
|
+
|
1293
|
+
// presume
|
1294
|
+
// todo: warn here?
|
1295
|
+
return TYPES.number;
|
1296
|
+
}
|
1261
1297
|
}
|
1262
1298
|
|
1263
1299
|
if (name.startsWith('__Porffor_wasm_')) {
|
@@ -1352,22 +1388,27 @@ const getNodeType = (scope, node) => {
|
|
1352
1388
|
}
|
1353
1389
|
|
1354
1390
|
if (node.type === 'MemberExpression') {
|
1355
|
-
|
1356
|
-
|
1357
|
-
|
1358
|
-
|
1359
|
-
|
1360
|
-
return TYPES.undefined;
|
1361
|
-
}
|
1391
|
+
const name = node.property.name;
|
1392
|
+
|
1393
|
+
if (name === 'length') {
|
1394
|
+
if (hasFuncWithName(node.object.name)) return TYPES.number;
|
1395
|
+
if (Prefs.fastLength) return TYPES.number;
|
1362
1396
|
}
|
1363
1397
|
|
1364
|
-
// hack: if something.length, number type
|
1365
|
-
if (node.property.name === 'length') return TYPES.number;
|
1366
1398
|
|
1367
|
-
|
1368
|
-
if (
|
1369
|
-
|
1370
|
-
|
1399
|
+
const objectKnownType = knownType(scope, getNodeType(scope, node.object));
|
1400
|
+
if (objectKnownType != null) {
|
1401
|
+
if (name === 'length') {
|
1402
|
+
if ([ TYPES.string, TYPES.bytestring, TYPES.array ].includes(objectKnownType)) return TYPES.number;
|
1403
|
+
else return TYPES.undefined;
|
1404
|
+
}
|
1405
|
+
|
1406
|
+
if (node.computed) {
|
1407
|
+
if (objectKnownType === TYPES.string) return TYPES.string;
|
1408
|
+
if (objectKnownType === TYPES.bytestring) return TYPES.bytestring;
|
1409
|
+
if (objectKnownType === TYPES.array) return TYPES.number;
|
1410
|
+
}
|
1411
|
+
}
|
1371
1412
|
|
1372
1413
|
if (scope.locals['#last_type']) return getLastType(scope);
|
1373
1414
|
|
@@ -1470,7 +1511,7 @@ const disposeLeftover = wasm => {
|
|
1470
1511
|
const generateExp = (scope, decl) => {
|
1471
1512
|
const expression = decl.expression;
|
1472
1513
|
|
1473
|
-
const out = generate(scope, expression, undefined, undefined,
|
1514
|
+
const out = generate(scope, expression, undefined, undefined, Prefs.optUnused);
|
1474
1515
|
disposeLeftover(out);
|
1475
1516
|
|
1476
1517
|
return out;
|
@@ -1561,16 +1602,10 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1561
1602
|
out.splice(out.length - 1, 1);
|
1562
1603
|
|
1563
1604
|
const finalStatement = parsed.body[parsed.body.length - 1];
|
1564
|
-
out.push(
|
1565
|
-
...getNodeType(scope, finalStatement),
|
1566
|
-
...setLastType(scope)
|
1567
|
-
);
|
1605
|
+
out.push(...setLastType(scope, getNodeType(scope, finalStatement)));
|
1568
1606
|
} else if (countLeftover(out) === 0) {
|
1569
1607
|
out.push(...number(UNDEFINED));
|
1570
|
-
out.push(
|
1571
|
-
...number(TYPES.undefined, Valtype.i32),
|
1572
|
-
...setLastType(scope)
|
1573
|
-
);
|
1608
|
+
out.push(...setLastType(scope, TYPES.undefined));
|
1574
1609
|
}
|
1575
1610
|
|
1576
1611
|
// if (lastInst && lastInst[0] === Opcodes.drop) {
|
@@ -1622,8 +1657,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1622
1657
|
[ Opcodes.call, idx ],
|
1623
1658
|
Opcodes.i32_from_u,
|
1624
1659
|
|
1625
|
-
...
|
1626
|
-
...setLastType(scope)
|
1660
|
+
...setLastType(scope, TYPES.boolean)
|
1627
1661
|
];
|
1628
1662
|
}
|
1629
1663
|
|
@@ -1695,9 +1729,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1695
1729
|
if (protoFunc.noArgRetLength && decl.arguments.length === 0) {
|
1696
1730
|
protoBC[x] = [
|
1697
1731
|
...RTArrayUtil.getLength(getPointer),
|
1698
|
-
|
1699
|
-
...number(TYPES.number, Valtype.i32),
|
1700
|
-
...setLastType(scope)
|
1732
|
+
...setLastType(scope, TYPES.number)
|
1701
1733
|
];
|
1702
1734
|
continue;
|
1703
1735
|
}
|
@@ -1716,7 +1748,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1716
1748
|
getI32: () => RTArrayUtil.getLengthI32(getPointer),
|
1717
1749
|
set: value => RTArrayUtil.setLength(getPointer, value),
|
1718
1750
|
setI32: value => RTArrayUtil.setLengthI32(getPointer, value)
|
1719
|
-
}, generate(scope, decl.arguments[0] ?? DEFAULT_VALUE), protoLocal, protoLocal2, (length, itemType) => {
|
1751
|
+
}, generate(scope, decl.arguments[0] ?? DEFAULT_VALUE), getNodeType(scope, decl.arguments[0] ?? DEFAULT_VALUE), protoLocal, protoLocal2, (length, itemType) => {
|
1720
1752
|
return makeArray(scope, {
|
1721
1753
|
rawElements: new Array(length)
|
1722
1754
|
}, _global, _name, true, itemType);
|
@@ -1730,9 +1762,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1730
1762
|
protoBC[x] = [
|
1731
1763
|
[ Opcodes.block, unusedValue && optUnused ? Blocktype.void : valtypeBinary ],
|
1732
1764
|
...protoOut,
|
1733
|
-
|
1734
|
-
...number(protoFunc.returnType ?? TYPES.number, Valtype.i32),
|
1735
|
-
...setLastType(scope),
|
1765
|
+
...(unusedValue && optUnused ? [] : (protoFunc.returnType != null ? setLastType(scope, protoFunc.returnType) : setLastType(scope))),
|
1736
1766
|
[ Opcodes.end ]
|
1737
1767
|
];
|
1738
1768
|
}
|
@@ -1838,36 +1868,128 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1838
1868
|
const [ local, global ] = lookupName(scope, name);
|
1839
1869
|
if (!Prefs.indirectCalls || local == null) return internalThrow(scope, 'TypeError', `${unhackName(name)} is not a function`, true);
|
1840
1870
|
|
1841
|
-
// todo: only works when
|
1842
|
-
|
1843
|
-
|
1871
|
+
// todo: only works when function uses typedParams and typedReturns
|
1872
|
+
|
1873
|
+
const indirectMode = Prefs.indirectCallMode ?? 'vararg';
|
1874
|
+
// options: vararg, strict
|
1875
|
+
// - strict: simpler, smaller size usage, no func argc lut needed.
|
1876
|
+
// ONLY works when arg count of call == arg count of function being called
|
1877
|
+
// - vararg: large size usage, cursed.
|
1878
|
+
// works when arg count of call != arg count of function being called*
|
1879
|
+
// * most of the time, some edgecases
|
1844
1880
|
|
1845
1881
|
funcs.table = true;
|
1882
|
+
scope.table = true;
|
1846
1883
|
|
1847
1884
|
let args = decl.arguments;
|
1848
|
-
let
|
1885
|
+
let out = [];
|
1886
|
+
|
1887
|
+
let locals = [];
|
1888
|
+
|
1889
|
+
if (indirectMode === 'vararg') {
|
1890
|
+
const minArgc = Prefs.indirectCallMinArgc ?? 3;
|
1891
|
+
|
1892
|
+
if (args.length < minArgc) {
|
1893
|
+
args = args.concat(new Array(minArgc - args.length).fill(DEFAULT_VALUE));
|
1894
|
+
}
|
1895
|
+
}
|
1849
1896
|
|
1850
1897
|
for (let i = 0; i < args.length; i++) {
|
1851
1898
|
const arg = args[i];
|
1852
|
-
|
1899
|
+
out = out.concat(generate(scope, arg));
|
1853
1900
|
|
1854
1901
|
if (valtypeBinary !== Valtype.i32 && (
|
1855
1902
|
(builtinFuncs[name] && builtinFuncs[name].params[i * (typedParams ? 2 : 1)] === Valtype.i32) ||
|
1856
1903
|
(importedFuncs[name] && name.startsWith('profile'))
|
1857
1904
|
)) {
|
1858
|
-
|
1905
|
+
out.push(Opcodes.i32_to);
|
1859
1906
|
}
|
1860
1907
|
|
1861
|
-
|
1908
|
+
out = out.concat(getNodeType(scope, arg));
|
1909
|
+
|
1910
|
+
if (indirectMode === 'vararg') {
|
1911
|
+
const typeLocal = localTmp(scope, `#indirect_arg${i}_type`, Valtype.i32);
|
1912
|
+
const valLocal = localTmp(scope, `#indirect_arg${i}_val`);
|
1913
|
+
|
1914
|
+
locals.push([valLocal, typeLocal]);
|
1915
|
+
|
1916
|
+
out.push(
|
1917
|
+
[ Opcodes.local_set, typeLocal ],
|
1918
|
+
[ Opcodes.local_set, valLocal ]
|
1919
|
+
);
|
1920
|
+
}
|
1862
1921
|
}
|
1863
1922
|
|
1923
|
+
if (indirectMode === 'strict') {
|
1924
|
+
return typeSwitch(scope, getNodeType(scope, decl.callee), {
|
1925
|
+
[TYPES.function]: [
|
1926
|
+
...argWasm,
|
1927
|
+
[ global ? Opcodes.global_get : Opcodes.local_get, local.idx ],
|
1928
|
+
Opcodes.i32_to_u,
|
1929
|
+
[ Opcodes.call_indirect, args.length, 0 ],
|
1930
|
+
...setLastType(scope)
|
1931
|
+
],
|
1932
|
+
default: internalThrow(scope, 'TypeError', `${unhackName(name)} is not a function`, true)
|
1933
|
+
});
|
1934
|
+
}
|
1935
|
+
|
1936
|
+
// hi, I will now explain how vararg mode works:
|
1937
|
+
// wasm's indirect_call instruction requires you know the func type at compile-time
|
1938
|
+
// since we have varargs (variable argument count), we do not know it.
|
1939
|
+
// we could just store args in memory and not use wasm func args,
|
1940
|
+
// but that is slow (probably) and breaks js exports.
|
1941
|
+
// instead, we generate every* possibility of argc and use different indirect_call
|
1942
|
+
// ops for each one, with type depending on argc for that branch.
|
1943
|
+
// then we load the argc for the wanted function from a memory lut,
|
1944
|
+
// and call the branch with the matching argc we require.
|
1945
|
+
// sorry, yes it is very cursed (and size inefficient), but indirect calls
|
1946
|
+
// are kind of rare anyway (mostly callbacks) so I am not concerned atm.
|
1947
|
+
// *for argc 0-3, in future (todo:) the max number should be
|
1948
|
+
// dynamically changed to the max argc of any func in the js file.
|
1949
|
+
|
1950
|
+
const funcLocal = localTmp(scope, `#indirect_func`, Valtype.i32);
|
1951
|
+
|
1952
|
+
const gen = argc => {
|
1953
|
+
const out = [];
|
1954
|
+
for (let i = 0; i < argc; i++) {
|
1955
|
+
out.push(
|
1956
|
+
[ Opcodes.local_get, locals[i][0] ],
|
1957
|
+
[ Opcodes.local_get, locals[i][1] ]
|
1958
|
+
);
|
1959
|
+
}
|
1960
|
+
|
1961
|
+
out.push(
|
1962
|
+
[ Opcodes.local_get, funcLocal ],
|
1963
|
+
[ Opcodes.call_indirect, argc, 0 ],
|
1964
|
+
...setLastType(scope)
|
1965
|
+
)
|
1966
|
+
|
1967
|
+
return out;
|
1968
|
+
};
|
1969
|
+
|
1970
|
+
const tableBc = {};
|
1971
|
+
for (let i = 0; i <= args.length; i++) {
|
1972
|
+
tableBc[i] = gen(i);
|
1973
|
+
}
|
1974
|
+
|
1975
|
+
// todo/perf: check if we should use br_table here or just generate our own big if..elses
|
1976
|
+
|
1864
1977
|
return typeSwitch(scope, getNodeType(scope, decl.callee), {
|
1865
1978
|
[TYPES.function]: [
|
1866
|
-
...
|
1979
|
+
...out,
|
1980
|
+
|
1867
1981
|
[ global ? Opcodes.global_get : Opcodes.local_get, local.idx ],
|
1868
1982
|
Opcodes.i32_to_u,
|
1869
|
-
[ Opcodes.
|
1870
|
-
|
1983
|
+
[ Opcodes.local_set, funcLocal ],
|
1984
|
+
|
1985
|
+
...brTable([
|
1986
|
+
// get argc of func we are calling
|
1987
|
+
[ Opcodes.local_get, funcLocal ],
|
1988
|
+
...number(ValtypeSize.i16, Valtype.i32),
|
1989
|
+
[ Opcodes.i32_mul ],
|
1990
|
+
|
1991
|
+
[ Opcodes.i32_load16_u, 0, ...unsignedLEB128(allocPage(scope, 'func argc lut') * pageSize), 'read_argc' ]
|
1992
|
+
], tableBc, valtypeBinary)
|
1871
1993
|
],
|
1872
1994
|
default: internalThrow(scope, 'TypeError', `${unhackName(name)} is not a function`, true)
|
1873
1995
|
});
|
@@ -1901,6 +2023,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1901
2023
|
const arg = args[i];
|
1902
2024
|
out = out.concat(generate(scope, arg));
|
1903
2025
|
|
2026
|
+
// todo: this should be used instead of the too many args thing above (by removing that)
|
1904
2027
|
if (i >= paramCount) {
|
1905
2028
|
// over param count of func, drop arg
|
1906
2029
|
out.push([ Opcodes.drop ]);
|
@@ -1985,8 +2108,11 @@ const knownType = (scope, type) => {
|
|
1985
2108
|
const idx = type[0][1];
|
1986
2109
|
|
1987
2110
|
// type idx = var idx + 1
|
1988
|
-
const
|
1989
|
-
if (
|
2111
|
+
const name = Object.values(scope.locals).find(x => x.idx === idx)?.name;
|
2112
|
+
if (name) {
|
2113
|
+
const local = scope.locals[name];
|
2114
|
+
if (local.metadata?.type != null) return v.metadata.type;
|
2115
|
+
}
|
1990
2116
|
}
|
1991
2117
|
|
1992
2118
|
return null;
|
@@ -2021,16 +2147,17 @@ const brTable = (input, bc, returns) => {
|
|
2021
2147
|
}
|
2022
2148
|
|
2023
2149
|
for (let i = 0; i < count; i++) {
|
2024
|
-
if (i === 0) out.push([ Opcodes.block, returns, 'br table start' ]);
|
2150
|
+
// if (i === 0) out.push([ Opcodes.block, returns, 'br table start' ]);
|
2151
|
+
if (i === 0) out.push([ Opcodes.block, returns ]);
|
2025
2152
|
else out.push([ Opcodes.block, Blocktype.void ]);
|
2026
2153
|
}
|
2027
2154
|
|
2028
|
-
const nums = keys.filter(x => +x);
|
2155
|
+
const nums = keys.filter(x => +x >= 0);
|
2029
2156
|
const offset = Math.min(...nums);
|
2030
2157
|
const max = Math.max(...nums);
|
2031
2158
|
|
2032
2159
|
const table = [];
|
2033
|
-
let br =
|
2160
|
+
let br = 0;
|
2034
2161
|
|
2035
2162
|
for (let i = offset; i <= max; i++) {
|
2036
2163
|
// if branch for this num, go to that block
|
@@ -2070,10 +2197,9 @@ const brTable = (input, bc, returns) => {
|
|
2070
2197
|
br--;
|
2071
2198
|
}
|
2072
2199
|
|
2073
|
-
|
2074
|
-
|
2075
|
-
|
2076
|
-
];
|
2200
|
+
out.push([ Opcodes.end ]);
|
2201
|
+
|
2202
|
+
return out;
|
2077
2203
|
};
|
2078
2204
|
|
2079
2205
|
const typeSwitch = (scope, type, bc, returns = valtypeBinary) => {
|
@@ -2117,6 +2243,17 @@ const typeSwitch = (scope, type, bc, returns = valtypeBinary) => {
|
|
2117
2243
|
return out;
|
2118
2244
|
};
|
2119
2245
|
|
2246
|
+
const typeIsOneOf = (type, types, valtype = Valtype.i32) => {
|
2247
|
+
const out = [];
|
2248
|
+
|
2249
|
+
for (let i = 0; i < types.length; i++) {
|
2250
|
+
out.push(...type, ...number(types[i], valtype), valtype === Valtype.f64 ? [ Opcodes.f64_eq ] : [ Opcodes.i32_eq ]);
|
2251
|
+
if (i !== 0) out.push([ Opcodes.i32_or ]);
|
2252
|
+
}
|
2253
|
+
|
2254
|
+
return out;
|
2255
|
+
};
|
2256
|
+
|
2120
2257
|
const allocVar = (scope, name, global = false, type = true) => {
|
2121
2258
|
const target = global ? globals : scope.locals;
|
2122
2259
|
|
@@ -2133,7 +2270,7 @@ const allocVar = (scope, name, global = false, type = true) => {
|
|
2133
2270
|
|
2134
2271
|
if (type) {
|
2135
2272
|
let typeIdx = global ? globalInd++ : scope.localInd++;
|
2136
|
-
target[name + '#type'] = { idx: typeIdx, type: Valtype.i32 };
|
2273
|
+
target[name + '#type'] = { idx: typeIdx, type: Valtype.i32, name };
|
2137
2274
|
}
|
2138
2275
|
|
2139
2276
|
return idx;
|
@@ -2346,18 +2483,21 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
|
|
2346
2483
|
Opcodes.i32_to_u,
|
2347
2484
|
|
2348
2485
|
// turn into byte offset by * valtypeSize (4 for i32, 8 for i64/f64)
|
2349
|
-
...number(ValtypeSize[valtype], Valtype.i32),
|
2486
|
+
...number(ValtypeSize[valtype] + 1, Valtype.i32),
|
2350
2487
|
[ Opcodes.i32_mul ],
|
2351
2488
|
...(aotPointer ? [] : [ [ Opcodes.i32_add ] ]),
|
2352
2489
|
...(op === '=' ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
|
2353
2490
|
|
2354
2491
|
...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
|
2355
2492
|
[ Opcodes.local_get, pointerTmp ],
|
2356
|
-
[ Opcodes.load,
|
2357
|
-
], generate(scope, decl.right),
|
2493
|
+
[ Opcodes.load, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ]
|
2494
|
+
], generate(scope, decl.right), [
|
2495
|
+
[ Opcodes.local_get, pointerTmp ],
|
2496
|
+
[ Opcodes.i32_load8_u, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32 + ValtypeSize[valtype]) ]
|
2497
|
+
], getNodeType(scope, decl.right), false, name, true)),
|
2358
2498
|
[ Opcodes.local_tee, newValueTmp ],
|
2359
2499
|
|
2360
|
-
[ Opcodes.store,
|
2500
|
+
[ Opcodes.store, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ]
|
2361
2501
|
],
|
2362
2502
|
|
2363
2503
|
default: internalThrow(scope, 'TypeError', `Cannot assign member with non-array`)
|
@@ -2611,21 +2751,16 @@ const generateConditional = (scope, decl) => {
|
|
2611
2751
|
out.push([ Opcodes.if, valtypeBinary ]);
|
2612
2752
|
depth.push('if');
|
2613
2753
|
|
2614
|
-
out.push(...generate(scope, decl.consequent));
|
2615
|
-
|
2616
|
-
// note type
|
2617
2754
|
out.push(
|
2618
|
-
...
|
2619
|
-
...setLastType(scope)
|
2755
|
+
...generate(scope, decl.consequent),
|
2756
|
+
...setLastType(scope, getNodeType(scope, decl.consequent))
|
2620
2757
|
);
|
2621
2758
|
|
2622
2759
|
out.push([ Opcodes.else ]);
|
2623
|
-
out.push(...generate(scope, decl.alternate));
|
2624
2760
|
|
2625
|
-
// note type
|
2626
2761
|
out.push(
|
2627
|
-
...
|
2628
|
-
...setLastType(scope)
|
2762
|
+
...generate(scope, decl.alternate),
|
2763
|
+
...setLastType(scope, getNodeType(scope, decl.alternate))
|
2629
2764
|
);
|
2630
2765
|
|
2631
2766
|
out.push([ Opcodes.end ]);
|
@@ -2773,12 +2908,15 @@ const generateForOf = (scope, decl) => {
|
|
2773
2908
|
// todo: optimize away counter and use end pointer
|
2774
2909
|
out.push(...typeSwitch(scope, getNodeType(scope, decl.right), {
|
2775
2910
|
[TYPES.array]: [
|
2776
|
-
...setType(scope, leftName, TYPES.number),
|
2777
|
-
|
2778
2911
|
[ Opcodes.loop, Blocktype.void ],
|
2779
2912
|
|
2780
2913
|
[ Opcodes.local_get, pointer ],
|
2781
|
-
[ Opcodes.load,
|
2914
|
+
[ Opcodes.load, 0, ...unsignedLEB128(ValtypeSize.i32) ],
|
2915
|
+
|
2916
|
+
...setType(scope, leftName, [
|
2917
|
+
[ Opcodes.local_get, pointer ],
|
2918
|
+
[ Opcodes.i32_load8_u, 0, ...unsignedLEB128(ValtypeSize.i32 + ValtypeSize[valtype]) ],
|
2919
|
+
]),
|
2782
2920
|
|
2783
2921
|
[ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx ],
|
2784
2922
|
|
@@ -2787,9 +2925,9 @@ const generateForOf = (scope, decl) => {
|
|
2787
2925
|
...generate(scope, decl.body),
|
2788
2926
|
[ Opcodes.end ],
|
2789
2927
|
|
2790
|
-
// increment iter pointer by valtype size
|
2928
|
+
// increment iter pointer by valtype size + 1
|
2791
2929
|
[ Opcodes.local_get, pointer ],
|
2792
|
-
...number(ValtypeSize[valtype], Valtype.i32),
|
2930
|
+
...number(ValtypeSize[valtype] + 1, Valtype.i32),
|
2793
2931
|
[ Opcodes.i32_add ],
|
2794
2932
|
[ Opcodes.local_set, pointer ],
|
2795
2933
|
|
@@ -3106,7 +3244,7 @@ const getAllocType = itemType => {
|
|
3106
3244
|
}
|
3107
3245
|
};
|
3108
3246
|
|
3109
|
-
const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty = false, itemType = valtype) => {
|
3247
|
+
const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty = false, itemType = valtype, typed = false) => {
|
3110
3248
|
const out = [];
|
3111
3249
|
|
3112
3250
|
scope.arrays ??= new Map();
|
@@ -3169,7 +3307,7 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
3169
3307
|
|
3170
3308
|
const pointerWasm = pointerTmp != null ? [ [ Opcodes.local_get, pointerTmp ] ] : number(pointer, Valtype.i32);
|
3171
3309
|
|
3172
|
-
// store length
|
3310
|
+
// store length
|
3173
3311
|
out.push(
|
3174
3312
|
...pointerWasm,
|
3175
3313
|
...number(length, Valtype.i32),
|
@@ -3177,14 +3315,20 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
3177
3315
|
);
|
3178
3316
|
|
3179
3317
|
const storeOp = StoreOps[itemType];
|
3180
|
-
|
3318
|
+
const sizePerEl = ValtypeSize[itemType] + (typed ? 1 : 0);
|
3181
3319
|
if (!initEmpty) for (let i = 0; i < length; i++) {
|
3182
3320
|
if (elements[i] == null) continue;
|
3183
3321
|
|
3322
|
+
const offset = ValtypeSize.i32 + i * sizePerEl;
|
3184
3323
|
out.push(
|
3185
3324
|
...pointerWasm,
|
3186
3325
|
...(useRawElements ? number(elements[i], Valtype[valtype]) : generate(scope, elements[i])),
|
3187
|
-
[ storeOp,
|
3326
|
+
[ storeOp, 0, ...unsignedLEB128(offset) ],
|
3327
|
+
...(!typed ? [] : [ // typed presumes !useRawElements
|
3328
|
+
...pointerWasm,
|
3329
|
+
...getNodeType(scope, elements[i]),
|
3330
|
+
[ Opcodes.i32_store8, 0, ...unsignedLEB128(offset + ValtypeSize[itemType]) ]
|
3331
|
+
])
|
3188
3332
|
);
|
3189
3333
|
}
|
3190
3334
|
|
@@ -3194,6 +3338,65 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
3194
3338
|
return [ out, pointer ];
|
3195
3339
|
};
|
3196
3340
|
|
3341
|
+
const storeArray = (scope, array, index, element, aotPointer = null) => {
|
3342
|
+
if (!Array.isArray(element)) element = generate(scope, element);
|
3343
|
+
if (typeof index === 'number') index = number(index);
|
3344
|
+
|
3345
|
+
const offset = localTmp(scope, '#storeArray_offset', Valtype.i32);
|
3346
|
+
|
3347
|
+
return [
|
3348
|
+
// calculate offset
|
3349
|
+
...index,
|
3350
|
+
Opcodes.i32_to_u,
|
3351
|
+
...number(ValtypeSize[valtype] + 1, Valtype.i32),
|
3352
|
+
[ Opcodes.i32_mul ],
|
3353
|
+
...(aotPointer ? [] : [
|
3354
|
+
...array,
|
3355
|
+
Opcodes.i32_to_u,
|
3356
|
+
[ Opcodes.i32_add ],
|
3357
|
+
]),
|
3358
|
+
[ Opcodes.local_set, offset ],
|
3359
|
+
|
3360
|
+
// store value
|
3361
|
+
[ Opcodes.local_get, offset ],
|
3362
|
+
...generate(scope, element),
|
3363
|
+
[ Opcodes.store, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ],
|
3364
|
+
|
3365
|
+
// store type
|
3366
|
+
[ Opcodes.local_get, offset ],
|
3367
|
+
...getNodeType(scope, element),
|
3368
|
+
[ Opcodes.i32_store8, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32 + ValtypeSize[valtype]) ]
|
3369
|
+
];
|
3370
|
+
};
|
3371
|
+
|
3372
|
+
const loadArray = (scope, array, index, aotPointer = null) => {
|
3373
|
+
if (typeof index === 'number') index = number(index);
|
3374
|
+
|
3375
|
+
const offset = localTmp(scope, '#loadArray_offset', Valtype.i32);
|
3376
|
+
|
3377
|
+
return [
|
3378
|
+
// calculate offset
|
3379
|
+
...index,
|
3380
|
+
Opcodes.i32_to_u,
|
3381
|
+
...number(ValtypeSize[valtype] + 1, Valtype.i32),
|
3382
|
+
[ Opcodes.i32_mul ],
|
3383
|
+
...(aotPointer ? [] : [
|
3384
|
+
...array,
|
3385
|
+
Opcodes.i32_to_u,
|
3386
|
+
[ Opcodes.i32_add ],
|
3387
|
+
]),
|
3388
|
+
[ Opcodes.local_set, offset ],
|
3389
|
+
|
3390
|
+
// load value
|
3391
|
+
[ Opcodes.local_get, offset ],
|
3392
|
+
[ Opcodes.load, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ],
|
3393
|
+
|
3394
|
+
// load type
|
3395
|
+
[ Opcodes.local_get, offset ],
|
3396
|
+
[ Opcodes.i32_load8_u, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32 + ValtypeSize[valtype]) ]
|
3397
|
+
];
|
3398
|
+
};
|
3399
|
+
|
3197
3400
|
const byteStringable = str => {
|
3198
3401
|
if (!Prefs.bytestring) return false;
|
3199
3402
|
|
@@ -3222,14 +3425,28 @@ const makeString = (scope, str, global = false, name = '$undeclared', forceBytes
|
|
3222
3425
|
};
|
3223
3426
|
|
3224
3427
|
const generateArray = (scope, decl, global = false, name = '$undeclared', initEmpty = false) => {
|
3225
|
-
return makeArray(scope, decl, global, name, initEmpty, valtype)[0];
|
3428
|
+
return makeArray(scope, decl, global, name, initEmpty, valtype, true)[0];
|
3226
3429
|
};
|
3227
3430
|
|
3228
|
-
|
3431
|
+
const generateObject = (scope, decl, global = false, name = '$undeclared') => {
|
3432
|
+
if (decl.properties.length > 0) return todo(scope, 'objects are not supported yet', true);
|
3433
|
+
|
3434
|
+
return [
|
3435
|
+
...number(1),
|
3436
|
+
...setLastType(scope, TYPES.object)
|
3437
|
+
];
|
3438
|
+
};
|
3439
|
+
|
3440
|
+
const withType = (scope, wasm, type) => [
|
3441
|
+
...wasm,
|
3442
|
+
...setLastType(scope, type)
|
3443
|
+
];
|
3444
|
+
|
3445
|
+
const generateMember = (scope, decl, _global, _name) => {
|
3229
3446
|
const name = decl.object.name;
|
3230
3447
|
const pointer = scope.arrays?.get(name);
|
3231
3448
|
|
3232
|
-
const aotPointer = Prefs.aotPointerOpt && pointer
|
3449
|
+
const aotPointer = Prefs.aotPointerOpt && pointer;
|
3233
3450
|
|
3234
3451
|
// hack: .name
|
3235
3452
|
if (decl.property.name === 'name') {
|
@@ -3239,9 +3456,9 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
3239
3456
|
// eg: __String_prototype_toLowerCase -> toLowerCase
|
3240
3457
|
if (nameProp.startsWith('__')) nameProp = nameProp.split('_').pop();
|
3241
3458
|
|
3242
|
-
return makeString(scope, nameProp, _global, _name, true);
|
3459
|
+
return withType(scope, makeString(scope, nameProp, _global, _name, true), TYPES.bytestring);
|
3243
3460
|
} else {
|
3244
|
-
return
|
3461
|
+
return withType(scope, number(0), TYPES.undefined);
|
3245
3462
|
}
|
3246
3463
|
}
|
3247
3464
|
|
@@ -3251,7 +3468,7 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
3251
3468
|
if (func) {
|
3252
3469
|
const userFunc = funcIndex[name] && !importedFuncs[name] && !builtinFuncs[name] && !internalConstrs[name];
|
3253
3470
|
const typedParams = userFunc || builtinFuncs[name]?.typedParams;
|
3254
|
-
return number(typedParams ? func.params.length / 2 : func.params.length);
|
3471
|
+
return withType(scope, number(typedParams ? func.params.length / 2 : func.params.length), TYPES.number);
|
3255
3472
|
}
|
3256
3473
|
|
3257
3474
|
if (builtinFuncs[name + '$constructor']) {
|
@@ -3261,24 +3478,88 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
3261
3478
|
const constructorFunc = builtinFuncs[name + '$constructor'];
|
3262
3479
|
const constructorParams = constructorFunc.typedParams ? (constructorFunc.params.length / 2) : constructorFunc.params.length;
|
3263
3480
|
|
3264
|
-
return number(Math.max(regularParams, constructorParams));
|
3481
|
+
return withType(scope, number(Math.max(regularParams, constructorParams)), TYPES.number);
|
3265
3482
|
}
|
3266
3483
|
|
3267
|
-
if (builtinFuncs[name]) return number(builtinFuncs[name].typedParams ? (builtinFuncs[name].params.length / 2) : builtinFuncs[name].params.length);
|
3268
|
-
if (importedFuncs[name]) return number(importedFuncs[name].params);
|
3269
|
-
if (internalConstrs[name]) return number(internalConstrs[name].length ?? 0);
|
3484
|
+
if (builtinFuncs[name]) return withType(scope, number(builtinFuncs[name].typedParams ? (builtinFuncs[name].params.length / 2) : builtinFuncs[name].params.length), TYPES.number);
|
3485
|
+
if (importedFuncs[name]) return withType(scope, number(importedFuncs[name].params), TYPES.number);
|
3486
|
+
if (internalConstrs[name]) return withType(scope, number(internalConstrs[name].length ?? 0), TYPES.number);
|
3487
|
+
|
3488
|
+
if (Prefs.fastLength) {
|
3489
|
+
// presume valid length object
|
3490
|
+
return [
|
3491
|
+
...(aotPointer ? number(0, Valtype.i32) : [
|
3492
|
+
...generate(scope, decl.object),
|
3493
|
+
Opcodes.i32_to_u
|
3494
|
+
]),
|
3495
|
+
|
3496
|
+
[ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(aotPointer ? pointer : 0) ],
|
3497
|
+
Opcodes.i32_from_u
|
3498
|
+
];
|
3499
|
+
}
|
3500
|
+
|
3501
|
+
const type = getNodeType(scope, decl.object);
|
3502
|
+
const known = knownType(scope, type);
|
3503
|
+
if (known != null) {
|
3504
|
+
if ([ TYPES.string, TYPES.bytestring, TYPES.array ].includes(known)) return [
|
3505
|
+
...(aotPointer ? number(0, Valtype.i32) : [
|
3506
|
+
...generate(scope, decl.object),
|
3507
|
+
Opcodes.i32_to_u
|
3508
|
+
]),
|
3509
|
+
|
3510
|
+
[ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(aotPointer ? pointer : 0) ],
|
3511
|
+
Opcodes.i32_from_u
|
3512
|
+
];
|
3513
|
+
|
3514
|
+
return number(0);
|
3515
|
+
}
|
3270
3516
|
|
3271
3517
|
return [
|
3272
|
-
...(
|
3273
|
-
|
3274
|
-
|
3275
|
-
|
3518
|
+
...typeIsOneOf(getNodeType(scope, decl.object), [ TYPES.string, TYPES.bytestring, TYPES.array ]),
|
3519
|
+
[ Opcodes.if, valtypeBinary ],
|
3520
|
+
...(aotPointer ? number(0, Valtype.i32) : [
|
3521
|
+
...generate(scope, decl.object),
|
3522
|
+
Opcodes.i32_to_u
|
3523
|
+
]),
|
3524
|
+
|
3525
|
+
[ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(aotPointer ? pointer : 0) ],
|
3526
|
+
Opcodes.i32_from_u,
|
3276
3527
|
|
3277
|
-
|
3278
|
-
Opcodes.
|
3528
|
+
...setLastType(scope, TYPES.number),
|
3529
|
+
[ Opcodes.else ],
|
3530
|
+
...number(0),
|
3531
|
+
...setLastType(scope, TYPES.undefined),
|
3532
|
+
[ Opcodes.end ]
|
3279
3533
|
];
|
3280
3534
|
}
|
3281
3535
|
|
3536
|
+
// todo: generate this array procedurally during builtinFuncs creation
|
3537
|
+
if (['size', 'description'].includes(decl.property.name)) {
|
3538
|
+
const bc = {};
|
3539
|
+
const cands = Object.keys(builtinFuncs).filter(x => x.startsWith('__') && x.endsWith('_prototype_' + decl.property.name + '$get'));
|
3540
|
+
|
3541
|
+
if (cands.length > 0) {
|
3542
|
+
for (const x of cands) {
|
3543
|
+
const type = TYPES[x.split('_prototype_')[0].slice(2).toLowerCase()];
|
3544
|
+
if (type == null) continue;
|
3545
|
+
|
3546
|
+
bc[type] = generateCall(scope, {
|
3547
|
+
callee: {
|
3548
|
+
type: 'Identifier',
|
3549
|
+
name: x
|
3550
|
+
},
|
3551
|
+
arguments: [ decl.object ],
|
3552
|
+
_protoInternalCall: true
|
3553
|
+
});
|
3554
|
+
}
|
3555
|
+
}
|
3556
|
+
|
3557
|
+
return typeSwitch(scope, getNodeType(scope, decl.object), {
|
3558
|
+
...bc,
|
3559
|
+
default: withType(scope, number(0), TYPES.undefined)
|
3560
|
+
}, valtypeBinary);
|
3561
|
+
}
|
3562
|
+
|
3282
3563
|
const object = generate(scope, decl.object);
|
3283
3564
|
const property = generate(scope, decl.property);
|
3284
3565
|
|
@@ -3293,24 +3574,7 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
3293
3574
|
|
3294
3575
|
return typeSwitch(scope, getNodeType(scope, decl.object), {
|
3295
3576
|
[TYPES.array]: [
|
3296
|
-
|
3297
|
-
...property,
|
3298
|
-
|
3299
|
-
// convert to i32 and turn into byte offset by * valtypeSize (4 for i32, 8 for i64/f64)
|
3300
|
-
Opcodes.i32_to_u,
|
3301
|
-
...number(ValtypeSize[valtype], Valtype.i32),
|
3302
|
-
[ Opcodes.i32_mul ],
|
3303
|
-
|
3304
|
-
...(aotPointer ? [] : [
|
3305
|
-
...object,
|
3306
|
-
Opcodes.i32_to_u,
|
3307
|
-
[ Opcodes.i32_add ]
|
3308
|
-
]),
|
3309
|
-
|
3310
|
-
// read from memory
|
3311
|
-
[ Opcodes.load, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ],
|
3312
|
-
|
3313
|
-
...number(TYPES.number, Valtype.i32),
|
3577
|
+
...loadArray(scope, object, property, aotPointer),
|
3314
3578
|
...setLastType(scope)
|
3315
3579
|
],
|
3316
3580
|
|
@@ -3341,9 +3605,7 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
3341
3605
|
|
3342
3606
|
// return new string (page)
|
3343
3607
|
...number(newPointer),
|
3344
|
-
|
3345
|
-
...number(TYPES.string, Valtype.i32),
|
3346
|
-
...setLastType(scope)
|
3608
|
+
...setLastType(scope, TYPES.string)
|
3347
3609
|
],
|
3348
3610
|
[TYPES.bytestring]: [
|
3349
3611
|
// setup new/out array
|
@@ -3369,9 +3631,7 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
3369
3631
|
|
3370
3632
|
// return new string (page)
|
3371
3633
|
...number(newPointer),
|
3372
|
-
|
3373
|
-
...number(TYPES.bytestring, Valtype.i32),
|
3374
|
-
...setLastType(scope)
|
3634
|
+
...setLastType(scope, TYPES.bytestring)
|
3375
3635
|
],
|
3376
3636
|
|
3377
3637
|
default: internalThrow(scope, 'TypeError', 'Member expression is not supported for non-string non-array yet', true)
|
@@ -3396,7 +3656,7 @@ const objectHack = node => {
|
|
3396
3656
|
if (!objectName) objectName = objectHack(node.object)?.name?.slice?.(2);
|
3397
3657
|
|
3398
3658
|
// if .name or .length, give up (hack within a hack!)
|
3399
|
-
if (['name', 'length'].includes(node.property.name)) {
|
3659
|
+
if (['name', 'length', 'size', 'description'].includes(node.property.name)) {
|
3400
3660
|
node.object = objectHack(node.object);
|
3401
3661
|
return;
|
3402
3662
|
}
|
@@ -3446,7 +3706,8 @@ const generateFunc = (scope, decl) => {
|
|
3446
3706
|
|
3447
3707
|
if (typedInput && decl.returnType) {
|
3448
3708
|
const { type } = extractTypeAnnotation(decl.returnType);
|
3449
|
-
if (type != null && !Prefs.indirectCalls) {
|
3709
|
+
// if (type != null && !Prefs.indirectCalls) {
|
3710
|
+
if (type != null) {
|
3450
3711
|
innerScope.returnType = type;
|
3451
3712
|
innerScope.returns = [ valtypeBinary ];
|
3452
3713
|
}
|