porffor 0.14.0-a24ac8cf2 → 0.14.0-af9ac5ad4
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/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 +297 -126
- package/compiler/decompile.js +1 -1
- package/compiler/generated_builtins.js +220 -31
- 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
|
|
@@ -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 = [], callsSelf = false }) => {
|
1057
1067
|
const existing = funcs.find(x => x.name === name);
|
1058
1068
|
if (existing) return existing;
|
1059
1069
|
|
@@ -1101,6 +1111,12 @@ 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
|
+
|
1104
1120
|
funcs.push(func);
|
1105
1121
|
funcIndex[name] = func.index;
|
1106
1122
|
|
@@ -1194,9 +1210,10 @@ const getLastType = scope => {
|
|
1194
1210
|
return [ [ Opcodes.local_get, localTmp(scope, '#last_type', Valtype.i32) ] ];
|
1195
1211
|
};
|
1196
1212
|
|
1197
|
-
const setLastType = scope =>
|
1198
|
-
|
1199
|
-
|
1213
|
+
const setLastType = (scope, type = []) => [
|
1214
|
+
...(typeof type === 'number' ? number(type, Valtype.i32) : type),
|
1215
|
+
[ Opcodes.local_set, localTmp(scope, '#last_type', Valtype.i32) ]
|
1216
|
+
];
|
1200
1217
|
|
1201
1218
|
const getNodeType = (scope, node) => {
|
1202
1219
|
const ret = (() => {
|
@@ -1257,7 +1274,17 @@ const getNodeType = (scope, node) => {
|
|
1257
1274
|
|
1258
1275
|
const func = spl[spl.length - 1];
|
1259
1276
|
const protoFuncs = Object.keys(prototypeFuncs).filter(x => x != TYPES.bytestring && prototypeFuncs[x][func] != null);
|
1260
|
-
if (protoFuncs.length === 1)
|
1277
|
+
if (protoFuncs.length === 1) {
|
1278
|
+
if (protoFuncs[0].returnType) return protoFuncs[0].returnType;
|
1279
|
+
}
|
1280
|
+
|
1281
|
+
if (protoFuncs.length > 0) {
|
1282
|
+
if (scope.locals['#last_type']) return getLastType(scope);
|
1283
|
+
|
1284
|
+
// presume
|
1285
|
+
// todo: warn here?
|
1286
|
+
return TYPES.number;
|
1287
|
+
}
|
1261
1288
|
}
|
1262
1289
|
|
1263
1290
|
if (name.startsWith('__Porffor_wasm_')) {
|
@@ -1352,22 +1379,27 @@ const getNodeType = (scope, node) => {
|
|
1352
1379
|
}
|
1353
1380
|
|
1354
1381
|
if (node.type === 'MemberExpression') {
|
1355
|
-
|
1356
|
-
|
1357
|
-
|
1358
|
-
|
1359
|
-
|
1360
|
-
return TYPES.undefined;
|
1361
|
-
}
|
1382
|
+
const name = node.property.name;
|
1383
|
+
|
1384
|
+
if (name === 'length') {
|
1385
|
+
if (hasFuncWithName(node.object.name)) return TYPES.number;
|
1386
|
+
if (Prefs.fastLength) return TYPES.number;
|
1362
1387
|
}
|
1363
1388
|
|
1364
|
-
// hack: if something.length, number type
|
1365
|
-
if (node.property.name === 'length') return TYPES.number;
|
1366
1389
|
|
1367
|
-
|
1368
|
-
if (
|
1369
|
-
|
1370
|
-
|
1390
|
+
const objectKnownType = knownType(scope, getNodeType(scope, node.object));
|
1391
|
+
if (objectKnownType != null) {
|
1392
|
+
if (name === 'length') {
|
1393
|
+
if ([ TYPES.string, TYPES.bytestring, TYPES.array ].includes(objectKnownType)) return TYPES.number;
|
1394
|
+
else return TYPES.undefined;
|
1395
|
+
}
|
1396
|
+
|
1397
|
+
if (node.computed) {
|
1398
|
+
if (objectKnownType === TYPES.string) return TYPES.string;
|
1399
|
+
if (objectKnownType === TYPES.bytestring) return TYPES.bytestring;
|
1400
|
+
if (objectKnownType === TYPES.array) return TYPES.number;
|
1401
|
+
}
|
1402
|
+
}
|
1371
1403
|
|
1372
1404
|
if (scope.locals['#last_type']) return getLastType(scope);
|
1373
1405
|
|
@@ -1470,7 +1502,7 @@ const disposeLeftover = wasm => {
|
|
1470
1502
|
const generateExp = (scope, decl) => {
|
1471
1503
|
const expression = decl.expression;
|
1472
1504
|
|
1473
|
-
const out = generate(scope, expression, undefined, undefined,
|
1505
|
+
const out = generate(scope, expression, undefined, undefined, Prefs.optUnused);
|
1474
1506
|
disposeLeftover(out);
|
1475
1507
|
|
1476
1508
|
return out;
|
@@ -1561,16 +1593,10 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1561
1593
|
out.splice(out.length - 1, 1);
|
1562
1594
|
|
1563
1595
|
const finalStatement = parsed.body[parsed.body.length - 1];
|
1564
|
-
out.push(
|
1565
|
-
...getNodeType(scope, finalStatement),
|
1566
|
-
...setLastType(scope)
|
1567
|
-
);
|
1596
|
+
out.push(...setLastType(scope, getNodeType(scope, finalStatement)));
|
1568
1597
|
} else if (countLeftover(out) === 0) {
|
1569
1598
|
out.push(...number(UNDEFINED));
|
1570
|
-
out.push(
|
1571
|
-
...number(TYPES.undefined, Valtype.i32),
|
1572
|
-
...setLastType(scope)
|
1573
|
-
);
|
1599
|
+
out.push(...setLastType(scope, TYPES.undefined));
|
1574
1600
|
}
|
1575
1601
|
|
1576
1602
|
// if (lastInst && lastInst[0] === Opcodes.drop) {
|
@@ -1622,8 +1648,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1622
1648
|
[ Opcodes.call, idx ],
|
1623
1649
|
Opcodes.i32_from_u,
|
1624
1650
|
|
1625
|
-
...
|
1626
|
-
...setLastType(scope)
|
1651
|
+
...setLastType(scope, TYPES.boolean)
|
1627
1652
|
];
|
1628
1653
|
}
|
1629
1654
|
|
@@ -1695,9 +1720,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1695
1720
|
if (protoFunc.noArgRetLength && decl.arguments.length === 0) {
|
1696
1721
|
protoBC[x] = [
|
1697
1722
|
...RTArrayUtil.getLength(getPointer),
|
1698
|
-
|
1699
|
-
...number(TYPES.number, Valtype.i32),
|
1700
|
-
...setLastType(scope)
|
1723
|
+
...setLastType(scope, TYPES.number)
|
1701
1724
|
];
|
1702
1725
|
continue;
|
1703
1726
|
}
|
@@ -1716,7 +1739,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1716
1739
|
getI32: () => RTArrayUtil.getLengthI32(getPointer),
|
1717
1740
|
set: value => RTArrayUtil.setLength(getPointer, value),
|
1718
1741
|
setI32: value => RTArrayUtil.setLengthI32(getPointer, value)
|
1719
|
-
}, generate(scope, decl.arguments[0] ?? DEFAULT_VALUE), protoLocal, protoLocal2, (length, itemType) => {
|
1742
|
+
}, generate(scope, decl.arguments[0] ?? DEFAULT_VALUE), getNodeType(scope, decl.arguments[0] ?? DEFAULT_VALUE), protoLocal, protoLocal2, (length, itemType) => {
|
1720
1743
|
return makeArray(scope, {
|
1721
1744
|
rawElements: new Array(length)
|
1722
1745
|
}, _global, _name, true, itemType);
|
@@ -1730,9 +1753,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1730
1753
|
protoBC[x] = [
|
1731
1754
|
[ Opcodes.block, unusedValue && optUnused ? Blocktype.void : valtypeBinary ],
|
1732
1755
|
...protoOut,
|
1733
|
-
|
1734
|
-
...number(protoFunc.returnType ?? TYPES.number, Valtype.i32),
|
1735
|
-
...setLastType(scope),
|
1756
|
+
...(unusedValue && optUnused ? [] : (protoFunc.returnType != null ? setLastType(scope, protoFunc.returnType) : setLastType(scope))),
|
1736
1757
|
[ Opcodes.end ]
|
1737
1758
|
];
|
1738
1759
|
}
|
@@ -1901,6 +1922,13 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1901
1922
|
const arg = args[i];
|
1902
1923
|
out = out.concat(generate(scope, arg));
|
1903
1924
|
|
1925
|
+
// todo: this should be used instead of the too many args thing above (by removing that)
|
1926
|
+
if (i >= paramCount) {
|
1927
|
+
// over param count of func, drop arg
|
1928
|
+
out.push([ Opcodes.drop ]);
|
1929
|
+
continue;
|
1930
|
+
}
|
1931
|
+
|
1904
1932
|
if (valtypeBinary !== Valtype.i32 && (
|
1905
1933
|
(builtinFuncs[name] && builtinFuncs[name].params[i * (typedParams ? 2 : 1)] === Valtype.i32) ||
|
1906
1934
|
(importedFuncs[name] && name.startsWith('profile'))
|
@@ -1949,6 +1977,11 @@ const generateNew = (scope, decl, _global, _name) => {
|
|
1949
1977
|
}, _global, _name);
|
1950
1978
|
}
|
1951
1979
|
|
1980
|
+
if (
|
1981
|
+
(builtinFuncs[name] && !builtinFuncs[name].constr) ||
|
1982
|
+
(internalConstrs[name] && builtinFuncs[name].notConstr)
|
1983
|
+
) return internalThrow(scope, 'TypeError', `${name} is not a constructor`);
|
1984
|
+
|
1952
1985
|
if (!builtinFuncs[name]) return todo(scope, `new statement is not supported yet`); // return todo(scope, `new statement is not supported yet (new ${unhackName(name)})`);
|
1953
1986
|
|
1954
1987
|
return generateCall(scope, decl, _global, _name);
|
@@ -1974,8 +2007,11 @@ const knownType = (scope, type) => {
|
|
1974
2007
|
const idx = type[0][1];
|
1975
2008
|
|
1976
2009
|
// type idx = var idx + 1
|
1977
|
-
const
|
1978
|
-
if (
|
2010
|
+
const name = Object.values(scope.locals).find(x => x.idx === idx)?.name;
|
2011
|
+
if (name) {
|
2012
|
+
const local = scope.locals[name];
|
2013
|
+
if (local.metadata?.type != null) return v.metadata.type;
|
2014
|
+
}
|
1979
2015
|
}
|
1980
2016
|
|
1981
2017
|
return null;
|
@@ -2106,6 +2142,17 @@ const typeSwitch = (scope, type, bc, returns = valtypeBinary) => {
|
|
2106
2142
|
return out;
|
2107
2143
|
};
|
2108
2144
|
|
2145
|
+
const typeIsOneOf = (type, types, valtype = Valtype.i32) => {
|
2146
|
+
const out = [];
|
2147
|
+
|
2148
|
+
for (let i = 0; i < types.length; i++) {
|
2149
|
+
out.push(...type, ...number(types[i], valtype), valtype === Valtype.f64 ? [ Opcodes.f64_eq ] : [ Opcodes.i32_eq ]);
|
2150
|
+
if (i !== 0) out.push([ Opcodes.i32_or ]);
|
2151
|
+
}
|
2152
|
+
|
2153
|
+
return out;
|
2154
|
+
};
|
2155
|
+
|
2109
2156
|
const allocVar = (scope, name, global = false, type = true) => {
|
2110
2157
|
const target = global ? globals : scope.locals;
|
2111
2158
|
|
@@ -2122,7 +2169,7 @@ const allocVar = (scope, name, global = false, type = true) => {
|
|
2122
2169
|
|
2123
2170
|
if (type) {
|
2124
2171
|
let typeIdx = global ? globalInd++ : scope.localInd++;
|
2125
|
-
target[name + '#type'] = { idx: typeIdx, type: Valtype.i32 };
|
2172
|
+
target[name + '#type'] = { idx: typeIdx, type: Valtype.i32, name };
|
2126
2173
|
}
|
2127
2174
|
|
2128
2175
|
return idx;
|
@@ -2335,18 +2382,21 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
|
|
2335
2382
|
Opcodes.i32_to_u,
|
2336
2383
|
|
2337
2384
|
// turn into byte offset by * valtypeSize (4 for i32, 8 for i64/f64)
|
2338
|
-
...number(ValtypeSize[valtype], Valtype.i32),
|
2385
|
+
...number(ValtypeSize[valtype] + 1, Valtype.i32),
|
2339
2386
|
[ Opcodes.i32_mul ],
|
2340
2387
|
...(aotPointer ? [] : [ [ Opcodes.i32_add ] ]),
|
2341
2388
|
...(op === '=' ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
|
2342
2389
|
|
2343
2390
|
...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
|
2344
2391
|
[ Opcodes.local_get, pointerTmp ],
|
2345
|
-
[ Opcodes.load,
|
2346
|
-
], generate(scope, decl.right),
|
2392
|
+
[ Opcodes.load, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ]
|
2393
|
+
], generate(scope, decl.right), [
|
2394
|
+
[ Opcodes.local_get, pointerTmp ],
|
2395
|
+
[ Opcodes.i32_load8_u, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32 + ValtypeSize[valtype]) ]
|
2396
|
+
], getNodeType(scope, decl.right), false, name, true)),
|
2347
2397
|
[ Opcodes.local_tee, newValueTmp ],
|
2348
2398
|
|
2349
|
-
[ Opcodes.store,
|
2399
|
+
[ Opcodes.store, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ]
|
2350
2400
|
],
|
2351
2401
|
|
2352
2402
|
default: internalThrow(scope, 'TypeError', `Cannot assign member with non-array`)
|
@@ -2523,6 +2573,7 @@ const generateUnary = (scope, decl) => {
|
|
2523
2573
|
[TYPES.string]: makeString(scope, 'string', false, '#typeof_result'),
|
2524
2574
|
[TYPES.undefined]: makeString(scope, 'undefined', false, '#typeof_result'),
|
2525
2575
|
[TYPES.function]: makeString(scope, 'function', false, '#typeof_result'),
|
2576
|
+
[TYPES.symbol]: makeString(scope, 'symbol', false, '#typeof_result'),
|
2526
2577
|
|
2527
2578
|
[TYPES.bytestring]: makeString(scope, 'string', false, '#typeof_result'),
|
2528
2579
|
|
@@ -2599,21 +2650,16 @@ const generateConditional = (scope, decl) => {
|
|
2599
2650
|
out.push([ Opcodes.if, valtypeBinary ]);
|
2600
2651
|
depth.push('if');
|
2601
2652
|
|
2602
|
-
out.push(...generate(scope, decl.consequent));
|
2603
|
-
|
2604
|
-
// note type
|
2605
2653
|
out.push(
|
2606
|
-
...
|
2607
|
-
...setLastType(scope)
|
2654
|
+
...generate(scope, decl.consequent),
|
2655
|
+
...setLastType(scope, getNodeType(scope, decl.consequent))
|
2608
2656
|
);
|
2609
2657
|
|
2610
2658
|
out.push([ Opcodes.else ]);
|
2611
|
-
out.push(...generate(scope, decl.alternate));
|
2612
2659
|
|
2613
|
-
// note type
|
2614
2660
|
out.push(
|
2615
|
-
...
|
2616
|
-
...setLastType(scope)
|
2661
|
+
...generate(scope, decl.alternate),
|
2662
|
+
...setLastType(scope, getNodeType(scope, decl.alternate))
|
2617
2663
|
);
|
2618
2664
|
|
2619
2665
|
out.push([ Opcodes.end ]);
|
@@ -2761,12 +2807,15 @@ const generateForOf = (scope, decl) => {
|
|
2761
2807
|
// todo: optimize away counter and use end pointer
|
2762
2808
|
out.push(...typeSwitch(scope, getNodeType(scope, decl.right), {
|
2763
2809
|
[TYPES.array]: [
|
2764
|
-
...setType(scope, leftName, TYPES.number),
|
2765
|
-
|
2766
2810
|
[ Opcodes.loop, Blocktype.void ],
|
2767
2811
|
|
2768
2812
|
[ Opcodes.local_get, pointer ],
|
2769
|
-
[ Opcodes.load,
|
2813
|
+
[ Opcodes.load, 0, ...unsignedLEB128(ValtypeSize.i32) ],
|
2814
|
+
|
2815
|
+
...setType(scope, leftName, [
|
2816
|
+
[ Opcodes.local_get, pointer ],
|
2817
|
+
[ Opcodes.i32_load8_u, 0, ...unsignedLEB128(ValtypeSize.i32 + ValtypeSize[valtype]) ],
|
2818
|
+
]),
|
2770
2819
|
|
2771
2820
|
[ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx ],
|
2772
2821
|
|
@@ -2775,9 +2824,9 @@ const generateForOf = (scope, decl) => {
|
|
2775
2824
|
...generate(scope, decl.body),
|
2776
2825
|
[ Opcodes.end ],
|
2777
2826
|
|
2778
|
-
// increment iter pointer by valtype size
|
2827
|
+
// increment iter pointer by valtype size + 1
|
2779
2828
|
[ Opcodes.local_get, pointer ],
|
2780
|
-
...number(ValtypeSize[valtype], Valtype.i32),
|
2829
|
+
...number(ValtypeSize[valtype] + 1, Valtype.i32),
|
2781
2830
|
[ Opcodes.i32_add ],
|
2782
2831
|
[ Opcodes.local_set, pointer ],
|
2783
2832
|
|
@@ -3094,7 +3143,7 @@ const getAllocType = itemType => {
|
|
3094
3143
|
}
|
3095
3144
|
};
|
3096
3145
|
|
3097
|
-
const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty = false, itemType = valtype) => {
|
3146
|
+
const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty = false, itemType = valtype, typed = false) => {
|
3098
3147
|
const out = [];
|
3099
3148
|
|
3100
3149
|
scope.arrays ??= new Map();
|
@@ -3157,7 +3206,7 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
3157
3206
|
|
3158
3207
|
const pointerWasm = pointerTmp != null ? [ [ Opcodes.local_get, pointerTmp ] ] : number(pointer, Valtype.i32);
|
3159
3208
|
|
3160
|
-
// store length
|
3209
|
+
// store length
|
3161
3210
|
out.push(
|
3162
3211
|
...pointerWasm,
|
3163
3212
|
...number(length, Valtype.i32),
|
@@ -3165,14 +3214,20 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
3165
3214
|
);
|
3166
3215
|
|
3167
3216
|
const storeOp = StoreOps[itemType];
|
3168
|
-
|
3217
|
+
const sizePerEl = ValtypeSize[itemType] + (typed ? 1 : 0);
|
3169
3218
|
if (!initEmpty) for (let i = 0; i < length; i++) {
|
3170
3219
|
if (elements[i] == null) continue;
|
3171
3220
|
|
3221
|
+
const offset = ValtypeSize.i32 + i * sizePerEl;
|
3172
3222
|
out.push(
|
3173
3223
|
...pointerWasm,
|
3174
3224
|
...(useRawElements ? number(elements[i], Valtype[valtype]) : generate(scope, elements[i])),
|
3175
|
-
[ storeOp,
|
3225
|
+
[ storeOp, 0, ...unsignedLEB128(offset) ],
|
3226
|
+
...(!typed ? [] : [ // typed presumes !useRawElements
|
3227
|
+
...pointerWasm,
|
3228
|
+
...getNodeType(scope, elements[i]),
|
3229
|
+
[ Opcodes.i32_store8, 0, ...unsignedLEB128(offset + ValtypeSize[itemType]) ]
|
3230
|
+
])
|
3176
3231
|
);
|
3177
3232
|
}
|
3178
3233
|
|
@@ -3182,6 +3237,65 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
3182
3237
|
return [ out, pointer ];
|
3183
3238
|
};
|
3184
3239
|
|
3240
|
+
const storeArray = (scope, array, index, element, aotPointer = null) => {
|
3241
|
+
if (!Array.isArray(element)) element = generate(scope, element);
|
3242
|
+
if (typeof index === 'number') index = number(index);
|
3243
|
+
|
3244
|
+
const offset = localTmp(scope, '#storeArray_offset', Valtype.i32);
|
3245
|
+
|
3246
|
+
return [
|
3247
|
+
// calculate offset
|
3248
|
+
...index,
|
3249
|
+
Opcodes.i32_to_u,
|
3250
|
+
...number(ValtypeSize[valtype] + 1, Valtype.i32),
|
3251
|
+
[ Opcodes.i32_mul ],
|
3252
|
+
...(aotPointer ? [] : [
|
3253
|
+
...array,
|
3254
|
+
Opcodes.i32_to_u,
|
3255
|
+
[ Opcodes.i32_add ],
|
3256
|
+
]),
|
3257
|
+
[ Opcodes.local_set, offset ],
|
3258
|
+
|
3259
|
+
// store value
|
3260
|
+
[ Opcodes.local_get, offset ],
|
3261
|
+
...generate(scope, element),
|
3262
|
+
[ Opcodes.store, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ],
|
3263
|
+
|
3264
|
+
// store type
|
3265
|
+
[ Opcodes.local_get, offset ],
|
3266
|
+
...getNodeType(scope, element),
|
3267
|
+
[ Opcodes.i32_store8, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32 + ValtypeSize[valtype]) ]
|
3268
|
+
];
|
3269
|
+
};
|
3270
|
+
|
3271
|
+
const loadArray = (scope, array, index, aotPointer = null) => {
|
3272
|
+
if (typeof index === 'number') index = number(index);
|
3273
|
+
|
3274
|
+
const offset = localTmp(scope, '#loadArray_offset', Valtype.i32);
|
3275
|
+
|
3276
|
+
return [
|
3277
|
+
// calculate offset
|
3278
|
+
...index,
|
3279
|
+
Opcodes.i32_to_u,
|
3280
|
+
...number(ValtypeSize[valtype] + 1, Valtype.i32),
|
3281
|
+
[ Opcodes.i32_mul ],
|
3282
|
+
...(aotPointer ? [] : [
|
3283
|
+
...array,
|
3284
|
+
Opcodes.i32_to_u,
|
3285
|
+
[ Opcodes.i32_add ],
|
3286
|
+
]),
|
3287
|
+
[ Opcodes.local_set, offset ],
|
3288
|
+
|
3289
|
+
// load value
|
3290
|
+
[ Opcodes.local_get, offset ],
|
3291
|
+
[ Opcodes.load, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ],
|
3292
|
+
|
3293
|
+
// load type
|
3294
|
+
[ Opcodes.local_get, offset ],
|
3295
|
+
[ Opcodes.i32_load8_u, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32 + ValtypeSize[valtype]) ]
|
3296
|
+
];
|
3297
|
+
};
|
3298
|
+
|
3185
3299
|
const byteStringable = str => {
|
3186
3300
|
if (!Prefs.bytestring) return false;
|
3187
3301
|
|
@@ -3210,14 +3324,28 @@ const makeString = (scope, str, global = false, name = '$undeclared', forceBytes
|
|
3210
3324
|
};
|
3211
3325
|
|
3212
3326
|
const generateArray = (scope, decl, global = false, name = '$undeclared', initEmpty = false) => {
|
3213
|
-
return makeArray(scope, decl, global, name, initEmpty, valtype)[0];
|
3327
|
+
return makeArray(scope, decl, global, name, initEmpty, valtype, true)[0];
|
3214
3328
|
};
|
3215
3329
|
|
3216
|
-
|
3330
|
+
const generateObject = (scope, decl, global = false, name = '$undeclared') => {
|
3331
|
+
if (decl.properties.length > 0) return todo(scope, 'objects are not supported yet', true);
|
3332
|
+
|
3333
|
+
return [
|
3334
|
+
...number(1),
|
3335
|
+
...setLastType(scope, TYPES.object)
|
3336
|
+
];
|
3337
|
+
};
|
3338
|
+
|
3339
|
+
const withType = (scope, wasm, type) => [
|
3340
|
+
...wasm,
|
3341
|
+
...setLastType(scope, type)
|
3342
|
+
];
|
3343
|
+
|
3344
|
+
const generateMember = (scope, decl, _global, _name) => {
|
3217
3345
|
const name = decl.object.name;
|
3218
3346
|
const pointer = scope.arrays?.get(name);
|
3219
3347
|
|
3220
|
-
const aotPointer = Prefs.aotPointerOpt && pointer
|
3348
|
+
const aotPointer = Prefs.aotPointerOpt && pointer;
|
3221
3349
|
|
3222
3350
|
// hack: .name
|
3223
3351
|
if (decl.property.name === 'name') {
|
@@ -3227,9 +3355,9 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
3227
3355
|
// eg: __String_prototype_toLowerCase -> toLowerCase
|
3228
3356
|
if (nameProp.startsWith('__')) nameProp = nameProp.split('_').pop();
|
3229
3357
|
|
3230
|
-
return makeString(scope, nameProp, _global, _name, true);
|
3358
|
+
return withType(scope, makeString(scope, nameProp, _global, _name, true), TYPES.bytestring);
|
3231
3359
|
} else {
|
3232
|
-
return
|
3360
|
+
return withType(scope, number(0), TYPES.undefined);
|
3233
3361
|
}
|
3234
3362
|
}
|
3235
3363
|
|
@@ -3239,7 +3367,7 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
3239
3367
|
if (func) {
|
3240
3368
|
const userFunc = funcIndex[name] && !importedFuncs[name] && !builtinFuncs[name] && !internalConstrs[name];
|
3241
3369
|
const typedParams = userFunc || builtinFuncs[name]?.typedParams;
|
3242
|
-
return number(typedParams ? func.params.length / 2 : func.params.length);
|
3370
|
+
return withType(scope, number(typedParams ? func.params.length / 2 : func.params.length), TYPES.number);
|
3243
3371
|
}
|
3244
3372
|
|
3245
3373
|
if (builtinFuncs[name + '$constructor']) {
|
@@ -3249,24 +3377,88 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
3249
3377
|
const constructorFunc = builtinFuncs[name + '$constructor'];
|
3250
3378
|
const constructorParams = constructorFunc.typedParams ? (constructorFunc.params.length / 2) : constructorFunc.params.length;
|
3251
3379
|
|
3252
|
-
return number(Math.max(regularParams, constructorParams));
|
3380
|
+
return withType(scope, number(Math.max(regularParams, constructorParams)), TYPES.number);
|
3253
3381
|
}
|
3254
3382
|
|
3255
|
-
if (builtinFuncs[name]) return number(builtinFuncs[name].typedParams ? (builtinFuncs[name].params.length / 2) : builtinFuncs[name].params.length);
|
3256
|
-
if (importedFuncs[name]) return number(importedFuncs[name].params);
|
3257
|
-
if (internalConstrs[name]) return number(internalConstrs[name].length ?? 0);
|
3383
|
+
if (builtinFuncs[name]) return withType(scope, number(builtinFuncs[name].typedParams ? (builtinFuncs[name].params.length / 2) : builtinFuncs[name].params.length), TYPES.number);
|
3384
|
+
if (importedFuncs[name]) return withType(scope, number(importedFuncs[name].params), TYPES.number);
|
3385
|
+
if (internalConstrs[name]) return withType(scope, number(internalConstrs[name].length ?? 0), TYPES.number);
|
3386
|
+
|
3387
|
+
if (Prefs.fastLength) {
|
3388
|
+
// presume valid length object
|
3389
|
+
return [
|
3390
|
+
...(aotPointer ? number(0, Valtype.i32) : [
|
3391
|
+
...generate(scope, decl.object),
|
3392
|
+
Opcodes.i32_to_u
|
3393
|
+
]),
|
3394
|
+
|
3395
|
+
[ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(aotPointer ? pointer : 0) ],
|
3396
|
+
Opcodes.i32_from_u
|
3397
|
+
];
|
3398
|
+
}
|
3399
|
+
|
3400
|
+
const type = getNodeType(scope, decl.object);
|
3401
|
+
const known = knownType(scope, type);
|
3402
|
+
if (known != null) {
|
3403
|
+
if ([ TYPES.string, TYPES.bytestring, TYPES.array ].includes(known)) return [
|
3404
|
+
...(aotPointer ? number(0, Valtype.i32) : [
|
3405
|
+
...generate(scope, decl.object),
|
3406
|
+
Opcodes.i32_to_u
|
3407
|
+
]),
|
3408
|
+
|
3409
|
+
[ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(aotPointer ? pointer : 0) ],
|
3410
|
+
Opcodes.i32_from_u
|
3411
|
+
];
|
3412
|
+
|
3413
|
+
return number(0);
|
3414
|
+
}
|
3258
3415
|
|
3259
3416
|
return [
|
3260
|
-
...(
|
3261
|
-
|
3262
|
-
|
3263
|
-
|
3417
|
+
...typeIsOneOf(getNodeType(scope, decl.object), [ TYPES.string, TYPES.bytestring, TYPES.array ]),
|
3418
|
+
[ Opcodes.if, valtypeBinary ],
|
3419
|
+
...(aotPointer ? number(0, Valtype.i32) : [
|
3420
|
+
...generate(scope, decl.object),
|
3421
|
+
Opcodes.i32_to_u
|
3422
|
+
]),
|
3264
3423
|
|
3265
|
-
|
3266
|
-
|
3424
|
+
[ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(aotPointer ? pointer : 0) ],
|
3425
|
+
Opcodes.i32_from_u,
|
3426
|
+
|
3427
|
+
...setLastType(scope, TYPES.number),
|
3428
|
+
[ Opcodes.else ],
|
3429
|
+
...number(0),
|
3430
|
+
...setLastType(scope, TYPES.undefined),
|
3431
|
+
[ Opcodes.end ]
|
3267
3432
|
];
|
3268
3433
|
}
|
3269
3434
|
|
3435
|
+
// todo: generate this array procedurally during builtinFuncs creation
|
3436
|
+
if (['size', 'description'].includes(decl.property.name)) {
|
3437
|
+
const bc = {};
|
3438
|
+
const cands = Object.keys(builtinFuncs).filter(x => x.startsWith('__') && x.endsWith('_prototype_' + decl.property.name + '$get'));
|
3439
|
+
|
3440
|
+
if (cands.length > 0) {
|
3441
|
+
for (const x of cands) {
|
3442
|
+
const type = TYPES[x.split('_prototype_')[0].slice(2).toLowerCase()];
|
3443
|
+
if (type == null) continue;
|
3444
|
+
|
3445
|
+
bc[type] = generateCall(scope, {
|
3446
|
+
callee: {
|
3447
|
+
type: 'Identifier',
|
3448
|
+
name: x
|
3449
|
+
},
|
3450
|
+
arguments: [ decl.object ],
|
3451
|
+
_protoInternalCall: true
|
3452
|
+
});
|
3453
|
+
}
|
3454
|
+
}
|
3455
|
+
|
3456
|
+
return typeSwitch(scope, getNodeType(scope, decl.object), {
|
3457
|
+
...bc,
|
3458
|
+
default: withType(scope, number(0), TYPES.undefined)
|
3459
|
+
}, valtypeBinary);
|
3460
|
+
}
|
3461
|
+
|
3270
3462
|
const object = generate(scope, decl.object);
|
3271
3463
|
const property = generate(scope, decl.property);
|
3272
3464
|
|
@@ -3281,24 +3473,7 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
3281
3473
|
|
3282
3474
|
return typeSwitch(scope, getNodeType(scope, decl.object), {
|
3283
3475
|
[TYPES.array]: [
|
3284
|
-
|
3285
|
-
...property,
|
3286
|
-
|
3287
|
-
// convert to i32 and turn into byte offset by * valtypeSize (4 for i32, 8 for i64/f64)
|
3288
|
-
Opcodes.i32_to_u,
|
3289
|
-
...number(ValtypeSize[valtype], Valtype.i32),
|
3290
|
-
[ Opcodes.i32_mul ],
|
3291
|
-
|
3292
|
-
...(aotPointer ? [] : [
|
3293
|
-
...object,
|
3294
|
-
Opcodes.i32_to_u,
|
3295
|
-
[ Opcodes.i32_add ]
|
3296
|
-
]),
|
3297
|
-
|
3298
|
-
// read from memory
|
3299
|
-
[ Opcodes.load, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ],
|
3300
|
-
|
3301
|
-
...number(TYPES.number, Valtype.i32),
|
3476
|
+
...loadArray(scope, object, property, aotPointer),
|
3302
3477
|
...setLastType(scope)
|
3303
3478
|
],
|
3304
3479
|
|
@@ -3329,9 +3504,7 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
3329
3504
|
|
3330
3505
|
// return new string (page)
|
3331
3506
|
...number(newPointer),
|
3332
|
-
|
3333
|
-
...number(TYPES.string, Valtype.i32),
|
3334
|
-
...setLastType(scope)
|
3507
|
+
...setLastType(scope, TYPES.string)
|
3335
3508
|
],
|
3336
3509
|
[TYPES.bytestring]: [
|
3337
3510
|
// setup new/out array
|
@@ -3357,9 +3530,7 @@ export const generateMember = (scope, decl, _global, _name) => {
|
|
3357
3530
|
|
3358
3531
|
// return new string (page)
|
3359
3532
|
...number(newPointer),
|
3360
|
-
|
3361
|
-
...number(TYPES.bytestring, Valtype.i32),
|
3362
|
-
...setLastType(scope)
|
3533
|
+
...setLastType(scope, TYPES.bytestring)
|
3363
3534
|
],
|
3364
3535
|
|
3365
3536
|
default: internalThrow(scope, 'TypeError', 'Member expression is not supported for non-string non-array yet', true)
|
@@ -3384,7 +3555,7 @@ const objectHack = node => {
|
|
3384
3555
|
if (!objectName) objectName = objectHack(node.object)?.name?.slice?.(2);
|
3385
3556
|
|
3386
3557
|
// if .name or .length, give up (hack within a hack!)
|
3387
|
-
if (['name', 'length'].includes(node.property.name)) {
|
3558
|
+
if (['name', 'length', 'size', 'description'].includes(node.property.name)) {
|
3388
3559
|
node.object = objectHack(node.object);
|
3389
3560
|
return;
|
3390
3561
|
}
|