porffor 0.14.0-86ac87fa4 → 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/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 +273 -128
- package/compiler/generated_builtins.js +176 -32
- package/compiler/precompile.js +1 -1
- 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
@@ -203,19 +203,11 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
|
|
203
203
|
|
204
204
|
__Porffor_bs: str => [
|
205
205
|
...makeString(scope, str, global, name, true),
|
206
|
-
|
207
|
-
...(name ? setType(scope, name, TYPES.bytestring) : [
|
208
|
-
...number(TYPES.bytestring, Valtype.i32),
|
209
|
-
...setLastType(scope)
|
210
|
-
])
|
206
|
+
...(name ? setType(scope, name, TYPES.bytestring) : setLastType(scope, TYPES.bytestring))
|
211
207
|
],
|
212
208
|
__Porffor_s: str => [
|
213
209
|
...makeString(scope, str, global, name, false),
|
214
|
-
|
215
|
-
...(name ? setType(scope, name, TYPES.string) : [
|
216
|
-
...number(TYPES.string, Valtype.i32),
|
217
|
-
...setLastType(scope)
|
218
|
-
])
|
210
|
+
...(name ? setType(scope, name, TYPES.string) : setLastType(scope, TYPES.string))
|
219
211
|
],
|
220
212
|
};
|
221
213
|
|
@@ -403,13 +395,11 @@ const performLogicOp = (scope, op, left, right, leftType, rightType) => {
|
|
403
395
|
[ Opcodes.if, Valtype.i32 ],
|
404
396
|
...right,
|
405
397
|
// note type
|
406
|
-
...rightType,
|
407
|
-
...setLastType(scope),
|
398
|
+
...setLastType(scope, rightType),
|
408
399
|
[ Opcodes.else ],
|
409
400
|
[ Opcodes.local_get, localTmp(scope, 'logictmpi', Valtype.i32) ],
|
410
401
|
// note type
|
411
|
-
...leftType,
|
412
|
-
...setLastType(scope),
|
402
|
+
...setLastType(scope, leftType),
|
413
403
|
[ Opcodes.end ],
|
414
404
|
Opcodes.i32_from
|
415
405
|
];
|
@@ -422,13 +412,11 @@ const performLogicOp = (scope, op, left, right, leftType, rightType) => {
|
|
422
412
|
[ Opcodes.if, valtypeBinary ],
|
423
413
|
...right,
|
424
414
|
// note type
|
425
|
-
...rightType,
|
426
|
-
...setLastType(scope),
|
415
|
+
...setLastType(scope, rightType),
|
427
416
|
[ Opcodes.else ],
|
428
417
|
[ Opcodes.local_get, localTmp(scope, 'logictmp') ],
|
429
418
|
// note type
|
430
|
-
...leftType,
|
431
|
-
...setLastType(scope),
|
419
|
+
...setLastType(scope, leftType),
|
432
420
|
[ Opcodes.end ]
|
433
421
|
];
|
434
422
|
};
|
@@ -456,11 +444,11 @@ const concatStrings = (scope, left, right, global, name, assign = false, bytestr
|
|
456
444
|
...number(0, Valtype.i32), // base 0 for store later
|
457
445
|
|
458
446
|
...number(pointer, Valtype.i32),
|
459
|
-
[ Opcodes.i32_load,
|
447
|
+
[ Opcodes.i32_load, 0, ...unsignedLEB128(0) ],
|
460
448
|
[ Opcodes.local_tee, leftLength ],
|
461
449
|
|
462
450
|
[ Opcodes.local_get, rightPointer ],
|
463
|
-
[ Opcodes.i32_load,
|
451
|
+
[ Opcodes.i32_load, 0, ...unsignedLEB128(0) ],
|
464
452
|
[ Opcodes.local_tee, rightLength ],
|
465
453
|
|
466
454
|
[ Opcodes.i32_add ],
|
@@ -516,11 +504,11 @@ const concatStrings = (scope, left, right, global, name, assign = false, bytestr
|
|
516
504
|
...number(0, Valtype.i32), // base 0 for store later
|
517
505
|
|
518
506
|
[ Opcodes.local_get, leftPointer ],
|
519
|
-
[ Opcodes.i32_load,
|
507
|
+
[ Opcodes.i32_load, 0, ...unsignedLEB128(0) ],
|
520
508
|
[ Opcodes.local_tee, leftLength ],
|
521
509
|
|
522
510
|
[ Opcodes.local_get, rightPointer ],
|
523
|
-
[ Opcodes.i32_load,
|
511
|
+
[ Opcodes.i32_load, 0, ...unsignedLEB128(0) ],
|
524
512
|
[ Opcodes.local_tee, rightLength ],
|
525
513
|
|
526
514
|
[ Opcodes.i32_add ],
|
@@ -598,11 +586,11 @@ const compareStrings = (scope, left, right, bytestrings = false) => {
|
|
598
586
|
|
599
587
|
// get lengths
|
600
588
|
[ Opcodes.local_get, leftPointer ],
|
601
|
-
[ Opcodes.i32_load,
|
589
|
+
[ Opcodes.i32_load, 0, ...unsignedLEB128(0) ],
|
602
590
|
[ Opcodes.local_tee, leftLength ],
|
603
591
|
|
604
592
|
[ Opcodes.local_get, rightPointer ],
|
605
|
-
[ Opcodes.i32_load,
|
593
|
+
[ Opcodes.i32_load, 0, ...unsignedLEB128(0) ],
|
606
594
|
|
607
595
|
// fast path: check leftLength != rightLength
|
608
596
|
[ Opcodes.i32_ne ],
|
@@ -693,6 +681,25 @@ const truthy = (scope, wasm, type, intIn = false, intOut = false) => {
|
|
693
681
|
...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
|
694
682
|
|
695
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
|
+
|
696
703
|
...(!intOut || (intIn && intOut) ? [] : [ Opcodes.i32_to_u ]),
|
697
704
|
|
698
705
|
/* Opcodes.eqz,
|
@@ -1056,7 +1063,7 @@ const asmFuncToAsm = (func, { name = '#unknown_asm_func', params = [], locals =
|
|
1056
1063
|
});
|
1057
1064
|
};
|
1058
1065
|
|
1059
|
-
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 }) => {
|
1060
1067
|
const existing = funcs.find(x => x.name === name);
|
1061
1068
|
if (existing) return existing;
|
1062
1069
|
|
@@ -1104,6 +1111,12 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
|
|
1104
1111
|
index: currentFuncIndex++
|
1105
1112
|
};
|
1106
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
|
+
|
1107
1120
|
funcs.push(func);
|
1108
1121
|
funcIndex[name] = func.index;
|
1109
1122
|
|
@@ -1197,9 +1210,10 @@ const getLastType = scope => {
|
|
1197
1210
|
return [ [ Opcodes.local_get, localTmp(scope, '#last_type', Valtype.i32) ] ];
|
1198
1211
|
};
|
1199
1212
|
|
1200
|
-
const setLastType = scope =>
|
1201
|
-
|
1202
|
-
|
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
|
+
];
|
1203
1217
|
|
1204
1218
|
const getNodeType = (scope, node) => {
|
1205
1219
|
const ret = (() => {
|
@@ -1260,7 +1274,17 @@ const getNodeType = (scope, node) => {
|
|
1260
1274
|
|
1261
1275
|
const func = spl[spl.length - 1];
|
1262
1276
|
const protoFuncs = Object.keys(prototypeFuncs).filter(x => x != TYPES.bytestring && prototypeFuncs[x][func] != null);
|
1263
|
-
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
|
+
}
|
1264
1288
|
}
|
1265
1289
|
|
1266
1290
|
if (name.startsWith('__Porffor_wasm_')) {
|
@@ -1355,22 +1379,27 @@ const getNodeType = (scope, node) => {
|
|
1355
1379
|
}
|
1356
1380
|
|
1357
1381
|
if (node.type === 'MemberExpression') {
|
1358
|
-
|
1359
|
-
|
1360
|
-
|
1361
|
-
|
1362
|
-
|
1363
|
-
return TYPES.undefined;
|
1364
|
-
}
|
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;
|
1365
1387
|
}
|
1366
1388
|
|
1367
|
-
// hack: if something.length, number type
|
1368
|
-
if (node.property.name === 'length') return TYPES.number;
|
1369
1389
|
|
1370
|
-
|
1371
|
-
if (
|
1372
|
-
|
1373
|
-
|
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
|
+
}
|
1374
1403
|
|
1375
1404
|
if (scope.locals['#last_type']) return getLastType(scope);
|
1376
1405
|
|
@@ -1473,7 +1502,7 @@ const disposeLeftover = wasm => {
|
|
1473
1502
|
const generateExp = (scope, decl) => {
|
1474
1503
|
const expression = decl.expression;
|
1475
1504
|
|
1476
|
-
const out = generate(scope, expression, undefined, undefined,
|
1505
|
+
const out = generate(scope, expression, undefined, undefined, Prefs.optUnused);
|
1477
1506
|
disposeLeftover(out);
|
1478
1507
|
|
1479
1508
|
return out;
|
@@ -1564,16 +1593,10 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1564
1593
|
out.splice(out.length - 1, 1);
|
1565
1594
|
|
1566
1595
|
const finalStatement = parsed.body[parsed.body.length - 1];
|
1567
|
-
out.push(
|
1568
|
-
...getNodeType(scope, finalStatement),
|
1569
|
-
...setLastType(scope)
|
1570
|
-
);
|
1596
|
+
out.push(...setLastType(scope, getNodeType(scope, finalStatement)));
|
1571
1597
|
} else if (countLeftover(out) === 0) {
|
1572
1598
|
out.push(...number(UNDEFINED));
|
1573
|
-
out.push(
|
1574
|
-
...number(TYPES.undefined, Valtype.i32),
|
1575
|
-
...setLastType(scope)
|
1576
|
-
);
|
1599
|
+
out.push(...setLastType(scope, TYPES.undefined));
|
1577
1600
|
}
|
1578
1601
|
|
1579
1602
|
// if (lastInst && lastInst[0] === Opcodes.drop) {
|
@@ -1625,8 +1648,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1625
1648
|
[ Opcodes.call, idx ],
|
1626
1649
|
Opcodes.i32_from_u,
|
1627
1650
|
|
1628
|
-
...
|
1629
|
-
...setLastType(scope)
|
1651
|
+
...setLastType(scope, TYPES.boolean)
|
1630
1652
|
];
|
1631
1653
|
}
|
1632
1654
|
|
@@ -1698,9 +1720,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1698
1720
|
if (protoFunc.noArgRetLength && decl.arguments.length === 0) {
|
1699
1721
|
protoBC[x] = [
|
1700
1722
|
...RTArrayUtil.getLength(getPointer),
|
1701
|
-
|
1702
|
-
...number(TYPES.number, Valtype.i32),
|
1703
|
-
...setLastType(scope)
|
1723
|
+
...setLastType(scope, TYPES.number)
|
1704
1724
|
];
|
1705
1725
|
continue;
|
1706
1726
|
}
|
@@ -1719,7 +1739,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1719
1739
|
getI32: () => RTArrayUtil.getLengthI32(getPointer),
|
1720
1740
|
set: value => RTArrayUtil.setLength(getPointer, value),
|
1721
1741
|
setI32: value => RTArrayUtil.setLengthI32(getPointer, value)
|
1722
|
-
}, 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) => {
|
1723
1743
|
return makeArray(scope, {
|
1724
1744
|
rawElements: new Array(length)
|
1725
1745
|
}, _global, _name, true, itemType);
|
@@ -1733,9 +1753,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1733
1753
|
protoBC[x] = [
|
1734
1754
|
[ Opcodes.block, unusedValue && optUnused ? Blocktype.void : valtypeBinary ],
|
1735
1755
|
...protoOut,
|
1736
|
-
|
1737
|
-
...number(protoFunc.returnType ?? TYPES.number, Valtype.i32),
|
1738
|
-
...setLastType(scope),
|
1756
|
+
...(unusedValue && optUnused ? [] : (protoFunc.returnType != null ? setLastType(scope, protoFunc.returnType) : setLastType(scope))),
|
1739
1757
|
[ Opcodes.end ]
|
1740
1758
|
];
|
1741
1759
|
}
|
@@ -1904,6 +1922,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
1904
1922
|
const arg = args[i];
|
1905
1923
|
out = out.concat(generate(scope, arg));
|
1906
1924
|
|
1925
|
+
// todo: this should be used instead of the too many args thing above (by removing that)
|
1907
1926
|
if (i >= paramCount) {
|
1908
1927
|
// over param count of func, drop arg
|
1909
1928
|
out.push([ Opcodes.drop ]);
|
@@ -1988,8 +2007,11 @@ const knownType = (scope, type) => {
|
|
1988
2007
|
const idx = type[0][1];
|
1989
2008
|
|
1990
2009
|
// type idx = var idx + 1
|
1991
|
-
const
|
1992
|
-
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
|
+
}
|
1993
2015
|
}
|
1994
2016
|
|
1995
2017
|
return null;
|
@@ -2120,6 +2142,17 @@ const typeSwitch = (scope, type, bc, returns = valtypeBinary) => {
|
|
2120
2142
|
return out;
|
2121
2143
|
};
|
2122
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
|
+
|
2123
2156
|
const allocVar = (scope, name, global = false, type = true) => {
|
2124
2157
|
const target = global ? globals : scope.locals;
|
2125
2158
|
|
@@ -2136,7 +2169,7 @@ const allocVar = (scope, name, global = false, type = true) => {
|
|
2136
2169
|
|
2137
2170
|
if (type) {
|
2138
2171
|
let typeIdx = global ? globalInd++ : scope.localInd++;
|
2139
|
-
target[name + '#type'] = { idx: typeIdx, type: Valtype.i32 };
|
2172
|
+
target[name + '#type'] = { idx: typeIdx, type: Valtype.i32, name };
|
2140
2173
|
}
|
2141
2174
|
|
2142
2175
|
return idx;
|
@@ -2349,18 +2382,21 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
|
|
2349
2382
|
Opcodes.i32_to_u,
|
2350
2383
|
|
2351
2384
|
// turn into byte offset by * valtypeSize (4 for i32, 8 for i64/f64)
|
2352
|
-
...number(ValtypeSize[valtype], Valtype.i32),
|
2385
|
+
...number(ValtypeSize[valtype] + 1, Valtype.i32),
|
2353
2386
|
[ Opcodes.i32_mul ],
|
2354
2387
|
...(aotPointer ? [] : [ [ Opcodes.i32_add ] ]),
|
2355
2388
|
...(op === '=' ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
|
2356
2389
|
|
2357
2390
|
...(op === '=' ? generate(scope, decl.right) : performOp(scope, op, [
|
2358
2391
|
[ Opcodes.local_get, pointerTmp ],
|
2359
|
-
[ Opcodes.load,
|
2360
|
-
], 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)),
|
2361
2397
|
[ Opcodes.local_tee, newValueTmp ],
|
2362
2398
|
|
2363
|
-
[ Opcodes.store,
|
2399
|
+
[ Opcodes.store, 0, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ]
|
2364
2400
|
],
|
2365
2401
|
|
2366
2402
|
default: internalThrow(scope, 'TypeError', `Cannot assign member with non-array`)
|
@@ -2614,21 +2650,16 @@ const generateConditional = (scope, decl) => {
|
|
2614
2650
|
out.push([ Opcodes.if, valtypeBinary ]);
|
2615
2651
|
depth.push('if');
|
2616
2652
|
|
2617
|
-
out.push(...generate(scope, decl.consequent));
|
2618
|
-
|
2619
|
-
// note type
|
2620
2653
|
out.push(
|
2621
|
-
...
|
2622
|
-
...setLastType(scope)
|
2654
|
+
...generate(scope, decl.consequent),
|
2655
|
+
...setLastType(scope, getNodeType(scope, decl.consequent))
|
2623
2656
|
);
|
2624
2657
|
|
2625
2658
|
out.push([ Opcodes.else ]);
|
2626
|
-
out.push(...generate(scope, decl.alternate));
|
2627
2659
|
|
2628
|
-
// note type
|
2629
2660
|
out.push(
|
2630
|
-
...
|
2631
|
-
...setLastType(scope)
|
2661
|
+
...generate(scope, decl.alternate),
|
2662
|
+
...setLastType(scope, getNodeType(scope, decl.alternate))
|
2632
2663
|
);
|
2633
2664
|
|
2634
2665
|
out.push([ Opcodes.end ]);
|
@@ -2776,12 +2807,15 @@ const generateForOf = (scope, decl) => {
|
|
2776
2807
|
// todo: optimize away counter and use end pointer
|
2777
2808
|
out.push(...typeSwitch(scope, getNodeType(scope, decl.right), {
|
2778
2809
|
[TYPES.array]: [
|
2779
|
-
...setType(scope, leftName, TYPES.number),
|
2780
|
-
|
2781
2810
|
[ Opcodes.loop, Blocktype.void ],
|
2782
2811
|
|
2783
2812
|
[ Opcodes.local_get, pointer ],
|
2784
|
-
[ 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
|
+
]),
|
2785
2819
|
|
2786
2820
|
[ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx ],
|
2787
2821
|
|
@@ -2790,9 +2824,9 @@ const generateForOf = (scope, decl) => {
|
|
2790
2824
|
...generate(scope, decl.body),
|
2791
2825
|
[ Opcodes.end ],
|
2792
2826
|
|
2793
|
-
// increment iter pointer by valtype size
|
2827
|
+
// increment iter pointer by valtype size + 1
|
2794
2828
|
[ Opcodes.local_get, pointer ],
|
2795
|
-
...number(ValtypeSize[valtype], Valtype.i32),
|
2829
|
+
...number(ValtypeSize[valtype] + 1, Valtype.i32),
|
2796
2830
|
[ Opcodes.i32_add ],
|
2797
2831
|
[ Opcodes.local_set, pointer ],
|
2798
2832
|
|
@@ -3109,7 +3143,7 @@ const getAllocType = itemType => {
|
|
3109
3143
|
}
|
3110
3144
|
};
|
3111
3145
|
|
3112
|
-
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) => {
|
3113
3147
|
const out = [];
|
3114
3148
|
|
3115
3149
|
scope.arrays ??= new Map();
|
@@ -3172,7 +3206,7 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
3172
3206
|
|
3173
3207
|
const pointerWasm = pointerTmp != null ? [ [ Opcodes.local_get, pointerTmp ] ] : number(pointer, Valtype.i32);
|
3174
3208
|
|
3175
|
-
// store length
|
3209
|
+
// store length
|
3176
3210
|
out.push(
|
3177
3211
|
...pointerWasm,
|
3178
3212
|
...number(length, Valtype.i32),
|
@@ -3180,14 +3214,20 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
3180
3214
|
);
|
3181
3215
|
|
3182
3216
|
const storeOp = StoreOps[itemType];
|
3183
|
-
|
3217
|
+
const sizePerEl = ValtypeSize[itemType] + (typed ? 1 : 0);
|
3184
3218
|
if (!initEmpty) for (let i = 0; i < length; i++) {
|
3185
3219
|
if (elements[i] == null) continue;
|
3186
3220
|
|
3221
|
+
const offset = ValtypeSize.i32 + i * sizePerEl;
|
3187
3222
|
out.push(
|
3188
3223
|
...pointerWasm,
|
3189
3224
|
...(useRawElements ? number(elements[i], Valtype[valtype]) : generate(scope, elements[i])),
|
3190
|
-
[ 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
|
+
])
|
3191
3231
|
);
|
3192
3232
|
}
|
3193
3233
|
|
@@ -3197,6 +3237,65 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
|
|
3197
3237
|
return [ out, pointer ];
|
3198
3238
|
};
|
3199
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
|
+
|
3200
3299
|
const byteStringable = str => {
|
3201
3300
|
if (!Prefs.bytestring) return false;
|
3202
3301
|
|
@@ -3225,7 +3324,7 @@ const makeString = (scope, str, global = false, name = '$undeclared', forceBytes
|
|
3225
3324
|
};
|
3226
3325
|
|
3227
3326
|
const generateArray = (scope, decl, global = false, name = '$undeclared', initEmpty = false) => {
|
3228
|
-
return makeArray(scope, decl, global, name, initEmpty, valtype)[0];
|
3327
|
+
return makeArray(scope, decl, global, name, initEmpty, valtype, true)[0];
|
3229
3328
|
};
|
3230
3329
|
|
3231
3330
|
const generateObject = (scope, decl, global = false, name = '$undeclared') => {
|
@@ -3233,17 +3332,20 @@ const generateObject = (scope, decl, global = false, name = '$undeclared') => {
|
|
3233
3332
|
|
3234
3333
|
return [
|
3235
3334
|
...number(1),
|
3236
|
-
|
3237
|
-
...number(TYPES.object, Valtype.i32),
|
3238
|
-
...setLastType(scope)
|
3335
|
+
...setLastType(scope, TYPES.object)
|
3239
3336
|
];
|
3240
3337
|
};
|
3241
3338
|
|
3339
|
+
const withType = (scope, wasm, type) => [
|
3340
|
+
...wasm,
|
3341
|
+
...setLastType(scope, type)
|
3342
|
+
];
|
3343
|
+
|
3242
3344
|
const generateMember = (scope, decl, _global, _name) => {
|
3243
3345
|
const name = decl.object.name;
|
3244
3346
|
const pointer = scope.arrays?.get(name);
|
3245
3347
|
|
3246
|
-
const aotPointer = Prefs.aotPointerOpt && pointer
|
3348
|
+
const aotPointer = Prefs.aotPointerOpt && pointer;
|
3247
3349
|
|
3248
3350
|
// hack: .name
|
3249
3351
|
if (decl.property.name === 'name') {
|
@@ -3253,9 +3355,9 @@ const generateMember = (scope, decl, _global, _name) => {
|
|
3253
3355
|
// eg: __String_prototype_toLowerCase -> toLowerCase
|
3254
3356
|
if (nameProp.startsWith('__')) nameProp = nameProp.split('_').pop();
|
3255
3357
|
|
3256
|
-
return makeString(scope, nameProp, _global, _name, true);
|
3358
|
+
return withType(scope, makeString(scope, nameProp, _global, _name, true), TYPES.bytestring);
|
3257
3359
|
} else {
|
3258
|
-
return
|
3360
|
+
return withType(scope, number(0), TYPES.undefined);
|
3259
3361
|
}
|
3260
3362
|
}
|
3261
3363
|
|
@@ -3265,7 +3367,7 @@ const generateMember = (scope, decl, _global, _name) => {
|
|
3265
3367
|
if (func) {
|
3266
3368
|
const userFunc = funcIndex[name] && !importedFuncs[name] && !builtinFuncs[name] && !internalConstrs[name];
|
3267
3369
|
const typedParams = userFunc || builtinFuncs[name]?.typedParams;
|
3268
|
-
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);
|
3269
3371
|
}
|
3270
3372
|
|
3271
3373
|
if (builtinFuncs[name + '$constructor']) {
|
@@ -3275,24 +3377,88 @@ const generateMember = (scope, decl, _global, _name) => {
|
|
3275
3377
|
const constructorFunc = builtinFuncs[name + '$constructor'];
|
3276
3378
|
const constructorParams = constructorFunc.typedParams ? (constructorFunc.params.length / 2) : constructorFunc.params.length;
|
3277
3379
|
|
3278
|
-
return number(Math.max(regularParams, constructorParams));
|
3380
|
+
return withType(scope, number(Math.max(regularParams, constructorParams)), TYPES.number);
|
3381
|
+
}
|
3382
|
+
|
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
|
+
];
|
3279
3398
|
}
|
3280
3399
|
|
3281
|
-
|
3282
|
-
|
3283
|
-
if (
|
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
|
+
}
|
3284
3415
|
|
3285
3416
|
return [
|
3286
|
-
...(
|
3287
|
-
|
3288
|
-
|
3289
|
-
|
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
|
+
]),
|
3423
|
+
|
3424
|
+
[ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, ...unsignedLEB128(aotPointer ? pointer : 0) ],
|
3425
|
+
Opcodes.i32_from_u,
|
3290
3426
|
|
3291
|
-
|
3292
|
-
Opcodes.
|
3427
|
+
...setLastType(scope, TYPES.number),
|
3428
|
+
[ Opcodes.else ],
|
3429
|
+
...number(0),
|
3430
|
+
...setLastType(scope, TYPES.undefined),
|
3431
|
+
[ Opcodes.end ]
|
3293
3432
|
];
|
3294
3433
|
}
|
3295
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
|
+
|
3296
3462
|
const object = generate(scope, decl.object);
|
3297
3463
|
const property = generate(scope, decl.property);
|
3298
3464
|
|
@@ -3307,24 +3473,7 @@ const generateMember = (scope, decl, _global, _name) => {
|
|
3307
3473
|
|
3308
3474
|
return typeSwitch(scope, getNodeType(scope, decl.object), {
|
3309
3475
|
[TYPES.array]: [
|
3310
|
-
|
3311
|
-
...property,
|
3312
|
-
|
3313
|
-
// convert to i32 and turn into byte offset by * valtypeSize (4 for i32, 8 for i64/f64)
|
3314
|
-
Opcodes.i32_to_u,
|
3315
|
-
...number(ValtypeSize[valtype], Valtype.i32),
|
3316
|
-
[ Opcodes.i32_mul ],
|
3317
|
-
|
3318
|
-
...(aotPointer ? [] : [
|
3319
|
-
...object,
|
3320
|
-
Opcodes.i32_to_u,
|
3321
|
-
[ Opcodes.i32_add ]
|
3322
|
-
]),
|
3323
|
-
|
3324
|
-
// read from memory
|
3325
|
-
[ Opcodes.load, Math.log2(ValtypeSize[valtype]) - 1, ...unsignedLEB128((aotPointer ? pointer : 0) + ValtypeSize.i32) ],
|
3326
|
-
|
3327
|
-
...number(TYPES.number, Valtype.i32),
|
3476
|
+
...loadArray(scope, object, property, aotPointer),
|
3328
3477
|
...setLastType(scope)
|
3329
3478
|
],
|
3330
3479
|
|
@@ -3355,9 +3504,7 @@ const generateMember = (scope, decl, _global, _name) => {
|
|
3355
3504
|
|
3356
3505
|
// return new string (page)
|
3357
3506
|
...number(newPointer),
|
3358
|
-
|
3359
|
-
...number(TYPES.string, Valtype.i32),
|
3360
|
-
...setLastType(scope)
|
3507
|
+
...setLastType(scope, TYPES.string)
|
3361
3508
|
],
|
3362
3509
|
[TYPES.bytestring]: [
|
3363
3510
|
// setup new/out array
|
@@ -3383,9 +3530,7 @@ const generateMember = (scope, decl, _global, _name) => {
|
|
3383
3530
|
|
3384
3531
|
// return new string (page)
|
3385
3532
|
...number(newPointer),
|
3386
|
-
|
3387
|
-
...number(TYPES.bytestring, Valtype.i32),
|
3388
|
-
...setLastType(scope)
|
3533
|
+
...setLastType(scope, TYPES.bytestring)
|
3389
3534
|
],
|
3390
3535
|
|
3391
3536
|
default: internalThrow(scope, 'TypeError', 'Member expression is not supported for non-string non-array yet', true)
|
@@ -3410,7 +3555,7 @@ const objectHack = node => {
|
|
3410
3555
|
if (!objectName) objectName = objectHack(node.object)?.name?.slice?.(2);
|
3411
3556
|
|
3412
3557
|
// if .name or .length, give up (hack within a hack!)
|
3413
|
-
if (['name', 'length'].includes(node.property.name)) {
|
3558
|
+
if (['name', 'length', 'size', 'description'].includes(node.property.name)) {
|
3414
3559
|
node.object = objectHack(node.object);
|
3415
3560
|
return;
|
3416
3561
|
}
|