porffor 0.60.22 → 0.60.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/compiler/builtins_precompiled.js +7 -7
- package/compiler/codegen.js +66 -107
- package/compiler/parse.js +1 -1
- package/compiler/precompile.js +1 -1
- package/compiler/prefs.js +1 -1
- package/compiler/semantic.js +184 -0
- package/jsr.json +1 -1
- package/package.json +1 -1
- package/runtime/index.js +1 -1
- package/foo.js +0 -1
package/compiler/codegen.js
CHANGED
|
@@ -3,6 +3,7 @@ import { number, ieee754_binary64, signedLEB128, unsignedLEB128, encodeVector }
|
|
|
3
3
|
import { operatorOpcode } from './expression.js';
|
|
4
4
|
import { BuiltinFuncs, BuiltinVars, importedFuncs, NULL, UNDEFINED } from './builtins.js';
|
|
5
5
|
import { TYPES, TYPE_FLAGS, TYPE_NAMES } from './types.js';
|
|
6
|
+
import semantic from './semantic.js';
|
|
6
7
|
import parse from './parse.js';
|
|
7
8
|
import { log } from './log.js';
|
|
8
9
|
import './prefs.js';
|
|
@@ -1198,6 +1199,22 @@ const nullish = (scope, wasm, type, nonbinary = true, intIn = false) => {
|
|
|
1198
1199
|
];
|
|
1199
1200
|
};
|
|
1200
1201
|
|
|
1202
|
+
const eitherStringType = (leftType, rightType) => [
|
|
1203
|
+
...leftType,
|
|
1204
|
+
number(TYPE_FLAGS.parity, Valtype.i32),
|
|
1205
|
+
[ Opcodes.i32_or ],
|
|
1206
|
+
number(TYPES.bytestring, Valtype.i32),
|
|
1207
|
+
[ Opcodes.i32_eq ],
|
|
1208
|
+
|
|
1209
|
+
...rightType,
|
|
1210
|
+
number(TYPE_FLAGS.parity, Valtype.i32),
|
|
1211
|
+
[ Opcodes.i32_or ],
|
|
1212
|
+
number(TYPES.bytestring, Valtype.i32),
|
|
1213
|
+
[ Opcodes.i32_eq ],
|
|
1214
|
+
|
|
1215
|
+
[ Opcodes.i32_or ]
|
|
1216
|
+
];
|
|
1217
|
+
|
|
1201
1218
|
const performOp = (scope, op, left, right, leftType, rightType) => {
|
|
1202
1219
|
if (op === '||' || op === '&&' || op === '??') {
|
|
1203
1220
|
return performLogicOp(scope, op, left, right, leftType, rightType);
|
|
@@ -1294,19 +1311,7 @@ const performOp = (scope, op, left, right, leftType, rightType) => {
|
|
|
1294
1311
|
|
|
1295
1312
|
ops.unshift(
|
|
1296
1313
|
// if left or right are string or bytestring
|
|
1297
|
-
...leftType,
|
|
1298
|
-
number(TYPE_FLAGS.parity, Valtype.i32),
|
|
1299
|
-
[ Opcodes.i32_or ],
|
|
1300
|
-
number(TYPES.bytestring, Valtype.i32),
|
|
1301
|
-
[ Opcodes.i32_eq ],
|
|
1302
|
-
|
|
1303
|
-
...rightType,
|
|
1304
|
-
number(TYPE_FLAGS.parity, Valtype.i32),
|
|
1305
|
-
[ Opcodes.i32_or ],
|
|
1306
|
-
number(TYPES.bytestring, Valtype.i32),
|
|
1307
|
-
[ Opcodes.i32_eq ],
|
|
1308
|
-
|
|
1309
|
-
[ Opcodes.i32_or ],
|
|
1314
|
+
...eitherStringType(leftType, rightType),
|
|
1310
1315
|
[ Opcodes.if, Blocktype.void ],
|
|
1311
1316
|
...concatStrings(scope, [ [ Opcodes.local_get, tmpLeft ] ], [ [ Opcodes.local_get, tmpRight ] ], leftType, rightType),
|
|
1312
1317
|
[ Opcodes.br, 1 ],
|
|
@@ -1326,19 +1331,7 @@ const performOp = (scope, op, left, right, leftType, rightType) => {
|
|
|
1326
1331
|
|
|
1327
1332
|
ops.unshift(
|
|
1328
1333
|
// if left or right are string or bytestring
|
|
1329
|
-
...leftType,
|
|
1330
|
-
number(TYPE_FLAGS.parity, Valtype.i32),
|
|
1331
|
-
[ Opcodes.i32_or ],
|
|
1332
|
-
number(TYPES.bytestring, Valtype.i32),
|
|
1333
|
-
[ Opcodes.i32_eq ],
|
|
1334
|
-
|
|
1335
|
-
...rightType,
|
|
1336
|
-
number(TYPE_FLAGS.parity, Valtype.i32),
|
|
1337
|
-
[ Opcodes.i32_or ],
|
|
1338
|
-
number(TYPES.bytestring, Valtype.i32),
|
|
1339
|
-
[ Opcodes.i32_eq ],
|
|
1340
|
-
|
|
1341
|
-
[ Opcodes.i32_or ],
|
|
1334
|
+
...eitherStringType(leftType, rightType),
|
|
1342
1335
|
[ Opcodes.if, Blocktype.void ],
|
|
1343
1336
|
...compareStrings(scope, [ [ Opcodes.local_get, tmpLeft ] ], [ [ Opcodes.local_get, tmpRight ] ], leftType, rightType),
|
|
1344
1337
|
...(op === '!==' || op === '!=' ? [ [ Opcodes.i32_eqz ] ] : []),
|
|
@@ -1599,11 +1592,11 @@ const generateLogicExp = (scope, decl) =>
|
|
|
1599
1592
|
const getInferred = (scope, name, global = false) => {
|
|
1600
1593
|
const isConst = getVarMetadata(scope, name, global)?.kind === 'const';
|
|
1601
1594
|
if (global) {
|
|
1602
|
-
if (
|
|
1595
|
+
if (name in globalInfer && (isConst || inferLoopPrev.length === 0)) return globalInfer[name];
|
|
1603
1596
|
} else if (scope.inferTree) {
|
|
1604
1597
|
for (let i = scope.inferTree.length - 1; i >= 0; i--) {
|
|
1605
1598
|
const x = scope.inferTree[i];
|
|
1606
|
-
if (x
|
|
1599
|
+
if (name in x) return x[name];
|
|
1607
1600
|
}
|
|
1608
1601
|
}
|
|
1609
1602
|
|
|
@@ -1612,21 +1605,20 @@ const getInferred = (scope, name, global = false) => {
|
|
|
1612
1605
|
|
|
1613
1606
|
const setInferred = (scope, name, type, global = false) => {
|
|
1614
1607
|
const isConst = getVarMetadata(scope, name, global)?.kind === 'const';
|
|
1615
|
-
scope.inferTree ??= [];
|
|
1608
|
+
scope.inferTree ??= [ Object.create(null) ];
|
|
1616
1609
|
|
|
1617
1610
|
if (global) {
|
|
1618
1611
|
// set inferred type in global if not already and not in a loop, else make it null
|
|
1619
|
-
globalInfer
|
|
1612
|
+
globalInfer[name] = name in globalInfer || (!isConst && inferLoopPrev.length > 0) ? null : type;
|
|
1620
1613
|
} else {
|
|
1621
1614
|
// set inferred type in top
|
|
1622
1615
|
const top = scope.inferTree.at(-1);
|
|
1623
|
-
top
|
|
1624
|
-
top._infer.set(name, type);
|
|
1616
|
+
top[name] = type;
|
|
1625
1617
|
|
|
1626
1618
|
// invalidate inferred type above if mismatched
|
|
1627
1619
|
for (let i = scope.inferTree.length - 2; i >= 0; i--) {
|
|
1628
1620
|
const x = scope.inferTree[i];
|
|
1629
|
-
if (x
|
|
1621
|
+
if (name in x && x[name] !== type) x[name] = null;
|
|
1630
1622
|
}
|
|
1631
1623
|
}
|
|
1632
1624
|
};
|
|
@@ -2051,31 +2043,6 @@ const generateChain = (scope, decl) => {
|
|
|
2051
2043
|
return out;
|
|
2052
2044
|
};
|
|
2053
2045
|
|
|
2054
|
-
const ArrayUtil = {
|
|
2055
|
-
getLengthI32: pointer => [
|
|
2056
|
-
...pointer,
|
|
2057
|
-
[ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, 0 ]
|
|
2058
|
-
],
|
|
2059
|
-
|
|
2060
|
-
getLength: pointer => [
|
|
2061
|
-
...pointer,
|
|
2062
|
-
[ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, 0 ],
|
|
2063
|
-
Opcodes.i32_from_u
|
|
2064
|
-
],
|
|
2065
|
-
|
|
2066
|
-
setLengthI32: (pointer, value) => [
|
|
2067
|
-
...pointer,
|
|
2068
|
-
...value,
|
|
2069
|
-
[ Opcodes.i32_store, Math.log2(ValtypeSize.i32) - 1, 0 ]
|
|
2070
|
-
],
|
|
2071
|
-
|
|
2072
|
-
setLength: (pointer, value) => [
|
|
2073
|
-
...pointer,
|
|
2074
|
-
...value,
|
|
2075
|
-
Opcodes.i32_to_u,
|
|
2076
|
-
[ Opcodes.i32_store, Math.log2(ValtypeSize.i32) - 1, 0 ]
|
|
2077
|
-
]
|
|
2078
|
-
};
|
|
2079
2046
|
|
|
2080
2047
|
const createNewTarget = (scope, decl, idx = 0, force = false) => {
|
|
2081
2048
|
if (decl._new || force) {
|
|
@@ -2264,7 +2231,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
|
2264
2231
|
try {
|
|
2265
2232
|
parsed = {
|
|
2266
2233
|
type: 'BlockStatement',
|
|
2267
|
-
body: parse(code).body
|
|
2234
|
+
body: semantic(objectHack(parse(code)), decl._semanticScopes).body
|
|
2268
2235
|
};
|
|
2269
2236
|
} catch (e) {
|
|
2270
2237
|
if (e.name === 'SyntaxError') {
|
|
@@ -2309,7 +2276,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
|
2309
2276
|
|
|
2310
2277
|
let parsed;
|
|
2311
2278
|
try {
|
|
2312
|
-
parsed = objectHack(parse(`(function(${args.join(',')}){${code}})`));
|
|
2279
|
+
parsed = semantic(objectHack(parse(`(function(${args.join(',')}){${code}})`)), decl._semanticScopes);
|
|
2313
2280
|
} catch (e) {
|
|
2314
2281
|
if (e.name === 'SyntaxError') {
|
|
2315
2282
|
// throw syntax errors of evals at runtime instead
|
|
@@ -2824,16 +2791,6 @@ const DEFAULT_VALUE = () => ({
|
|
|
2824
2791
|
name: 'undefined'
|
|
2825
2792
|
});
|
|
2826
2793
|
|
|
2827
|
-
const codeToSanitizedStr = code => {
|
|
2828
|
-
let out = '';
|
|
2829
|
-
while (code > 0) {
|
|
2830
|
-
out += String.fromCharCode(97 + code % 26);
|
|
2831
|
-
code -= 26;
|
|
2832
|
-
}
|
|
2833
|
-
return out;
|
|
2834
|
-
};
|
|
2835
|
-
const sanitize = str => str.replace(/[^0-9a-zA-Z_]/g, _ => codeToSanitizedStr(_.charCodeAt(0)));
|
|
2836
|
-
|
|
2837
2794
|
const unhackName = name => {
|
|
2838
2795
|
if (!name) return name;
|
|
2839
2796
|
|
|
@@ -3284,6 +3241,7 @@ const setDefaultFuncName = (decl, name) => {
|
|
|
3284
3241
|
}
|
|
3285
3242
|
}
|
|
3286
3243
|
|
|
3244
|
+
name = name.split('#')[0];
|
|
3287
3245
|
decl.id = { name };
|
|
3288
3246
|
};
|
|
3289
3247
|
|
|
@@ -3299,8 +3257,7 @@ const generateVarDstr = (scope, kind, pattern, init, defaultValue, global) => {
|
|
|
3299
3257
|
try {
|
|
3300
3258
|
let usedNames = [];
|
|
3301
3259
|
for (const x of pattern.properties) {
|
|
3302
|
-
|
|
3303
|
-
usedNames.push(name);
|
|
3260
|
+
usedNames.push(x.key.name);
|
|
3304
3261
|
}
|
|
3305
3262
|
|
|
3306
3263
|
let path = init.arguments[0].value;
|
|
@@ -3327,11 +3284,11 @@ const generateVarDstr = (scope, kind, pattern, init, defaultValue, global) => {
|
|
|
3327
3284
|
|
|
3328
3285
|
// mock ffi function
|
|
3329
3286
|
asmFunc(name, {
|
|
3330
|
-
wasm: [],
|
|
3287
|
+
wasm: () => [],
|
|
3331
3288
|
params: parameters.map(x => Valtype.i32),
|
|
3332
3289
|
returns: result ? [ Valtype.i32 ] : [],
|
|
3333
3290
|
returnType: TYPES.number
|
|
3334
|
-
})
|
|
3291
|
+
});
|
|
3335
3292
|
}
|
|
3336
3293
|
|
|
3337
3294
|
return [ [ null, 'dlopen', path, symbols ] ];
|
|
@@ -4523,27 +4480,26 @@ const generateUpdate = (scope, decl, _global, _name, valueUnused = false) => {
|
|
|
4523
4480
|
];
|
|
4524
4481
|
};
|
|
4525
4482
|
|
|
4526
|
-
const inferBranchStart =
|
|
4527
|
-
scope.inferTree ??= [];
|
|
4528
|
-
scope.inferTree.push(
|
|
4483
|
+
const inferBranchStart = scope => {
|
|
4484
|
+
scope.inferTree ??= [ Object.create(null) ];
|
|
4485
|
+
scope.inferTree.push(Object.create(null));
|
|
4529
4486
|
};
|
|
4530
4487
|
|
|
4531
4488
|
const inferBranchEnd = scope => {
|
|
4532
4489
|
scope.inferTree.pop();
|
|
4533
4490
|
};
|
|
4534
4491
|
|
|
4535
|
-
const inferBranchElse =
|
|
4492
|
+
const inferBranchElse = scope => {
|
|
4493
|
+
// todo/opt: at end of else, find inferences in common and keep them?
|
|
4536
4494
|
inferBranchEnd(scope);
|
|
4537
|
-
inferBranchStart(scope
|
|
4495
|
+
inferBranchStart(scope);
|
|
4538
4496
|
};
|
|
4539
4497
|
|
|
4540
4498
|
const inferLoopPrev = [];
|
|
4541
|
-
const inferLoopStart =
|
|
4542
|
-
scope.inferTree ??= [];
|
|
4543
|
-
|
|
4499
|
+
const inferLoopStart = scope => {
|
|
4544
4500
|
// todo/opt: do not just wipe the infer tree for loops
|
|
4545
|
-
inferLoopPrev.push(scope.inferTree);
|
|
4546
|
-
scope.inferTree = [
|
|
4501
|
+
inferLoopPrev.push(scope.inferTree ?? [ Object.create(null) ]);
|
|
4502
|
+
scope.inferTree = [ Object.create(null) ];
|
|
4547
4503
|
};
|
|
4548
4504
|
|
|
4549
4505
|
const inferLoopEnd = scope => {
|
|
@@ -4562,23 +4518,23 @@ const generateIf = (scope, decl) => {
|
|
|
4562
4518
|
const out = truthy(scope, generate(scope, decl.test), getNodeType(scope, decl.test));
|
|
4563
4519
|
out.push([ Opcodes.if, Blocktype.void ]);
|
|
4564
4520
|
depth.push('if');
|
|
4565
|
-
inferBranchStart(scope
|
|
4521
|
+
inferBranchStart(scope);
|
|
4566
4522
|
|
|
4567
4523
|
out.push(
|
|
4568
4524
|
...generate(scope, decl.consequent),
|
|
4569
4525
|
[ Opcodes.drop ]
|
|
4570
4526
|
);
|
|
4571
4527
|
|
|
4572
|
-
|
|
4528
|
+
|
|
4573
4529
|
if (decl.alternate) {
|
|
4574
|
-
|
|
4530
|
+
inferBranchElse(scope);
|
|
4575
4531
|
out.push(
|
|
4576
4532
|
[ Opcodes.else ],
|
|
4577
4533
|
...generate(scope, decl.alternate),
|
|
4578
4534
|
[ Opcodes.drop ]
|
|
4579
4535
|
);
|
|
4580
4536
|
inferBranchEnd(scope);
|
|
4581
|
-
}
|
|
4537
|
+
} else inferBranchEnd(scope);
|
|
4582
4538
|
|
|
4583
4539
|
out.push(
|
|
4584
4540
|
[ Opcodes.end ],
|
|
@@ -4594,7 +4550,7 @@ const generateConditional = (scope, decl) => {
|
|
|
4594
4550
|
|
|
4595
4551
|
out.push([ Opcodes.if, valtypeBinary ]);
|
|
4596
4552
|
depth.push('if');
|
|
4597
|
-
inferBranchStart(scope
|
|
4553
|
+
inferBranchStart(scope);
|
|
4598
4554
|
|
|
4599
4555
|
out.push(
|
|
4600
4556
|
...generate(scope, decl.consequent),
|
|
@@ -4602,7 +4558,7 @@ const generateConditional = (scope, decl) => {
|
|
|
4602
4558
|
);
|
|
4603
4559
|
|
|
4604
4560
|
out.push([ Opcodes.else ]);
|
|
4605
|
-
inferBranchElse(scope
|
|
4561
|
+
inferBranchElse(scope);
|
|
4606
4562
|
|
|
4607
4563
|
out.push(
|
|
4608
4564
|
...generate(scope, decl.alternate),
|
|
@@ -4624,7 +4580,7 @@ const generateFor = (scope, decl) => {
|
|
|
4624
4580
|
[ Opcodes.drop ]
|
|
4625
4581
|
);
|
|
4626
4582
|
|
|
4627
|
-
inferLoopStart(scope
|
|
4583
|
+
inferLoopStart(scope);
|
|
4628
4584
|
out.push([ Opcodes.loop, Blocktype.void ]);
|
|
4629
4585
|
depth.push('for');
|
|
4630
4586
|
|
|
@@ -4663,7 +4619,7 @@ const generateFor = (scope, decl) => {
|
|
|
4663
4619
|
|
|
4664
4620
|
const generateWhile = (scope, decl) => {
|
|
4665
4621
|
const out = [];
|
|
4666
|
-
inferLoopStart(scope
|
|
4622
|
+
inferLoopStart(scope);
|
|
4667
4623
|
|
|
4668
4624
|
out.push([ Opcodes.loop, Blocktype.void ]);
|
|
4669
4625
|
depth.push('while');
|
|
@@ -4691,7 +4647,7 @@ const generateWhile = (scope, decl) => {
|
|
|
4691
4647
|
|
|
4692
4648
|
const generateDoWhile = (scope, decl) => {
|
|
4693
4649
|
const out = [];
|
|
4694
|
-
inferLoopStart(scope
|
|
4650
|
+
inferLoopStart(scope);
|
|
4695
4651
|
|
|
4696
4652
|
out.push([ Opcodes.loop, Blocktype.void ]);
|
|
4697
4653
|
|
|
@@ -4764,7 +4720,7 @@ const generateForOf = (scope, decl) => {
|
|
|
4764
4720
|
[ Opcodes.local_set, length ]
|
|
4765
4721
|
);
|
|
4766
4722
|
|
|
4767
|
-
inferLoopStart(scope
|
|
4723
|
+
inferLoopStart(scope);
|
|
4768
4724
|
depth.push('forof');
|
|
4769
4725
|
depth.push('block');
|
|
4770
4726
|
|
|
@@ -5104,7 +5060,7 @@ const generateForIn = (scope, decl) => {
|
|
|
5104
5060
|
[ Opcodes.if, Blocktype.void ]
|
|
5105
5061
|
);
|
|
5106
5062
|
|
|
5107
|
-
inferLoopStart(scope
|
|
5063
|
+
inferLoopStart(scope);
|
|
5108
5064
|
depth.push('if');
|
|
5109
5065
|
depth.push('forin');
|
|
5110
5066
|
depth.push('block');
|
|
@@ -6769,9 +6725,13 @@ const objectHack = node => {
|
|
|
6769
6725
|
}
|
|
6770
6726
|
|
|
6771
6727
|
for (const x in node) {
|
|
6772
|
-
if (node[x] != null && typeof node[x] === 'object') {
|
|
6728
|
+
if (node[x] != null && typeof node[x] === 'object' && x[0] !== '_') {
|
|
6773
6729
|
if (node[x].type) node[x] = objectHack(node[x]);
|
|
6774
|
-
if (Array.isArray(node[x]))
|
|
6730
|
+
if (Array.isArray(node[x])) {
|
|
6731
|
+
for (let i = 0; i < node[x].length; i++) {
|
|
6732
|
+
node[x][i] = objectHack(node[x][i]);
|
|
6733
|
+
}
|
|
6734
|
+
}
|
|
6775
6735
|
}
|
|
6776
6736
|
}
|
|
6777
6737
|
|
|
@@ -6829,7 +6789,6 @@ const generateFunc = (scope, decl, forceNoExpr = false) => {
|
|
|
6829
6789
|
async: decl.async,
|
|
6830
6790
|
subclass: decl._subclass, _onlyConstr: decl._onlyConstr, _onlyThisMethod: decl._onlyThisMethod,
|
|
6831
6791
|
strict: scope.strict || decl.strict,
|
|
6832
|
-
inferTree: [ decl ],
|
|
6833
6792
|
|
|
6834
6793
|
generate() {
|
|
6835
6794
|
if (func.wasm) return func.wasm;
|
|
@@ -6837,7 +6796,7 @@ const generateFunc = (scope, decl, forceNoExpr = false) => {
|
|
|
6837
6796
|
// generating, stub _wasm
|
|
6838
6797
|
let wasm = func.wasm = [];
|
|
6839
6798
|
|
|
6840
|
-
let body =
|
|
6799
|
+
let body = decl.body;
|
|
6841
6800
|
if (decl.type === 'ArrowFunctionExpression' && decl.expression) {
|
|
6842
6801
|
// hack: () => 0 -> () => return 0
|
|
6843
6802
|
body = {
|
|
@@ -7185,8 +7144,7 @@ const generateFunc = (scope, decl, forceNoExpr = false) => {
|
|
|
7185
7144
|
const generateBlock = (scope, decl) => {
|
|
7186
7145
|
let out = [];
|
|
7187
7146
|
|
|
7188
|
-
scope
|
|
7189
|
-
scope.inferTree.push(decl);
|
|
7147
|
+
inferBranchStart(scope);
|
|
7190
7148
|
|
|
7191
7149
|
let len = decl.body.length, j = 0;
|
|
7192
7150
|
for (let i = 0; i < len; i++) {
|
|
@@ -7197,7 +7155,7 @@ const generateBlock = (scope, decl) => {
|
|
|
7197
7155
|
out = out.concat(generate(scope, x));
|
|
7198
7156
|
}
|
|
7199
7157
|
|
|
7200
|
-
scope
|
|
7158
|
+
inferBranchEnd(scope);
|
|
7201
7159
|
|
|
7202
7160
|
if (out.length === 0) out.push(number(UNDEFINED));
|
|
7203
7161
|
return out;
|
|
@@ -7403,7 +7361,7 @@ export default program => {
|
|
|
7403
7361
|
typeswitchDepth = 0;
|
|
7404
7362
|
usedTypes = new Set([ TYPES.undefined, TYPES.number, TYPES.boolean, TYPES.function ]);
|
|
7405
7363
|
coctc = new Map();
|
|
7406
|
-
globalInfer =
|
|
7364
|
+
globalInfer = Object.create(null);
|
|
7407
7365
|
|
|
7408
7366
|
// set generic opcodes for current valtype
|
|
7409
7367
|
Opcodes.const = valtypeBinary === Valtype.i32 ? Opcodes.i32_const : Opcodes.f64_const;
|
|
@@ -7429,7 +7387,11 @@ export default program => {
|
|
|
7429
7387
|
objectHackers = ['assert', 'compareArray', 'Test262Error', ...new Set(Object.keys(builtinFuncs).map(getObjectName).concat(Object.keys(builtinVars).map(getObjectName)).filter(x => x))];
|
|
7430
7388
|
}
|
|
7431
7389
|
|
|
7432
|
-
|
|
7390
|
+
// todo/perf: make this lazy per func (again)
|
|
7391
|
+
program = objectHack(program);
|
|
7392
|
+
if (Prefs.closures) program = semantic(program);
|
|
7393
|
+
|
|
7394
|
+
generateFunc({}, {
|
|
7433
7395
|
type: 'Program',
|
|
7434
7396
|
id: { name: '#main' },
|
|
7435
7397
|
body: {
|
|
@@ -7438,9 +7400,6 @@ export default program => {
|
|
|
7438
7400
|
}
|
|
7439
7401
|
});
|
|
7440
7402
|
|
|
7441
|
-
// if wanted and blank main func and other exports, remove it
|
|
7442
|
-
if (Prefs.rmBlankMain && main.wasm.length === 0 && funcs.some(x => x.export)) funcs.splice(main.index - importedFuncs.length, 1);
|
|
7443
|
-
|
|
7444
7403
|
for (let i = 0; i < funcs.length; i++) {
|
|
7445
7404
|
const f = funcs[i];
|
|
7446
7405
|
|
package/compiler/parse.js
CHANGED
|
@@ -17,7 +17,7 @@ globalThis.parser = '';
|
|
|
17
17
|
let parse;
|
|
18
18
|
const loadParser = async (fallbackParser = 'acorn', forceParser) => {
|
|
19
19
|
parser = forceParser ?? Prefs.parser ?? fallbackParser;
|
|
20
|
-
const mod = (await import((globalThis.document
|
|
20
|
+
const mod = (await import((globalThis.document ? 'https://esm.sh/' : '') + parser));
|
|
21
21
|
if (mod.parseSync) parse = mod.parseSync;
|
|
22
22
|
else parse = mod.parse;
|
|
23
23
|
};
|
package/compiler/precompile.js
CHANGED
|
@@ -57,7 +57,7 @@ const compile = async (file, _funcs) => {
|
|
|
57
57
|
first = source.slice(0, source.indexOf('\n'));
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
-
let args = ['--module', '--truthy=no_nan_negative', '--no-rm-unused-types', '--fast-length', '--parse-types', '--opt-types', '--no-passive-data', '--active-data', '--no-treeshake-wasm-imports', '--no-coctc'];
|
|
60
|
+
let args = ['--module', '--truthy=no_nan_negative', '--no-rm-unused-types', '--fast-length', '--parse-types', '--opt-types', '--no-passive-data', '--active-data', '--no-treeshake-wasm-imports', '--no-coctc', '--no-closures'];
|
|
61
61
|
if (first.startsWith('// @porf')) {
|
|
62
62
|
args = first.slice('// @porf '.length).split(' ').concat(args);
|
|
63
63
|
}
|
package/compiler/prefs.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const onByDefault = [ 'treeshakeWasmImports', 'alwaysMemory', 'indirectCalls', 'optUnused', 'data', 'passiveData', 'rmUnusedTypes', 'optTypes', 'coctc', 'ctHash' ];
|
|
1
|
+
const onByDefault = [ 'treeshakeWasmImports', 'alwaysMemory', 'indirectCalls', 'optUnused', 'data', 'passiveData', 'rmUnusedTypes', 'optTypes', 'coctc', 'ctHash', 'closures' ];
|
|
2
2
|
|
|
3
3
|
const nameToKey = x => x.replace(/[a-z]\-[a-z]/g, y => `${y[0]}${y[2].toUpperCase()}`);
|
|
4
4
|
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
// todo: sloppy vs strict mode
|
|
2
|
+
// todo: function/class decls ?
|
|
3
|
+
// todo: function params
|
|
4
|
+
|
|
5
|
+
const varId = name => {
|
|
6
|
+
const lastFunc = scopes[scopes.lastFuncs.at(-1)];
|
|
7
|
+
lastFunc._variableIds ??= Object.create(null);
|
|
8
|
+
lastFunc._variableIds[name] ??= 0;
|
|
9
|
+
return lastFunc._variableIds[name]++;
|
|
10
|
+
|
|
11
|
+
// scopes._varId ??= Object.create(null);
|
|
12
|
+
// scopes._varId[name] ??= 0;
|
|
13
|
+
// return scopes._varId[name]++;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const declVar = (name, kind, node) => {
|
|
17
|
+
const parent = kind === 'var' ? scopes[scopes.lastFuncs.at(-1)] : scopes.at(-1);
|
|
18
|
+
parent._variables ??= Object.create(null);
|
|
19
|
+
parent._variables[name] = { node, id: varId(name) };
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const analyzePattern = (kind, node) => {
|
|
23
|
+
if (!node) return;
|
|
24
|
+
switch (node.type) {
|
|
25
|
+
case 'Identifier':
|
|
26
|
+
declVar(node.name, kind, node);
|
|
27
|
+
break;
|
|
28
|
+
|
|
29
|
+
case 'RestElement':
|
|
30
|
+
analyzePattern(kind, node.argument);
|
|
31
|
+
break;
|
|
32
|
+
|
|
33
|
+
case 'AssignmentPattern':
|
|
34
|
+
analyzePattern(kind, node.left);
|
|
35
|
+
break;
|
|
36
|
+
|
|
37
|
+
case 'Property':
|
|
38
|
+
analyzePattern(kind, node.value);
|
|
39
|
+
break;
|
|
40
|
+
|
|
41
|
+
case 'ObjectPattern':
|
|
42
|
+
for (const x of node.properties) {
|
|
43
|
+
analyzePattern(kind, x.value);
|
|
44
|
+
}
|
|
45
|
+
break;
|
|
46
|
+
|
|
47
|
+
case 'ArrayPattern':
|
|
48
|
+
for (const x of node.elements) {
|
|
49
|
+
analyzePattern(kind, x);
|
|
50
|
+
}
|
|
51
|
+
break;
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
let scopes;
|
|
56
|
+
const analyze = node => {
|
|
57
|
+
if (!node) return;
|
|
58
|
+
|
|
59
|
+
let openedScope = false;
|
|
60
|
+
switch (node.type) {
|
|
61
|
+
case 'ForStatement':
|
|
62
|
+
case 'ForInStatement':
|
|
63
|
+
case 'ForOfStatement':
|
|
64
|
+
case 'SwitchStatement':
|
|
65
|
+
case 'BlockStatement':
|
|
66
|
+
scopes.push(node);
|
|
67
|
+
openedScope = true;
|
|
68
|
+
break;
|
|
69
|
+
|
|
70
|
+
case 'CatchClause':
|
|
71
|
+
scopes.push(node);
|
|
72
|
+
if (node.param) analyzePattern('let', node.param);
|
|
73
|
+
openedScope = true;
|
|
74
|
+
break;
|
|
75
|
+
|
|
76
|
+
case 'VariableDeclaration':
|
|
77
|
+
for (const x of node.declarations) analyzePattern(node.kind, x.id);
|
|
78
|
+
break;
|
|
79
|
+
|
|
80
|
+
case 'FunctionDeclaration':
|
|
81
|
+
case 'FunctionExpression':
|
|
82
|
+
case 'ArrowFunctionExpression':
|
|
83
|
+
scopes.lastFuncs.push(scopes.length);
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
for (const x in node) {
|
|
88
|
+
if (node[x] != null && typeof node[x] === 'object') {
|
|
89
|
+
if (node[x].type) analyze(node[x]);
|
|
90
|
+
if (Array.isArray(node[x])) {
|
|
91
|
+
for (const y of node[x]) analyze(y);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (openedScope) {
|
|
97
|
+
scopes.pop();
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (node.type === 'FunctionDeclaration' || node.type === 'FunctionExpression' || node.type === 'ArrowFunctionExpression') {
|
|
101
|
+
scopes.lastFuncs.pop();
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
const objectHackers = ["assert","compareArray","Test262Error","Number","Math","Porffor","performance","String","ByteString","Array","ArrayBuffer","SharedArrayBuffer","Atomics","ecma262","BigInt","Boolean","console","crypto","DataView","Date","Error","AggregateError","TypeError","ReferenceError","SyntaxError","RangeError","EvalError","URIError","Test262Error","Function","JSON","Map","Object","Promise","Reflect","RegExp","Set","Symbol","Uint8Array","Int8Array","Uint8ClampedArray","Uint16Array","Int16Array","Uint32Array","Int32Array","Float32Array","Float64Array","BigInt64Array","BigUint64Array","WeakMap","WeakRef","WeakSet","navigator"];
|
|
106
|
+
const annotate = node => {
|
|
107
|
+
if (!node) return;
|
|
108
|
+
|
|
109
|
+
let openedScope = false;
|
|
110
|
+
if (node._variables) {
|
|
111
|
+
scopes.push(node);
|
|
112
|
+
openedScope = true;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
switch (node.type) {
|
|
116
|
+
case 'Identifier':
|
|
117
|
+
if (objectHackers.includes(node.name)) break;
|
|
118
|
+
for (let i = scopes.length - 1; i >= 0; i--) {
|
|
119
|
+
if (scopes[i]._variables?.[node.name]) {
|
|
120
|
+
const variable = scopes[i]._variables[node.name];
|
|
121
|
+
if (variable.id > 0) node.name = node.name + '#' + variable.id;
|
|
122
|
+
break;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
break;
|
|
126
|
+
|
|
127
|
+
case 'MemberExpression':
|
|
128
|
+
if (node.computed) annotate(node.property);
|
|
129
|
+
annotate(node.object);
|
|
130
|
+
return;
|
|
131
|
+
|
|
132
|
+
case 'PropertyDefinition':
|
|
133
|
+
case 'Property':
|
|
134
|
+
if (node.computed) annotate(node.key);
|
|
135
|
+
annotate(node.value);
|
|
136
|
+
return;
|
|
137
|
+
|
|
138
|
+
case 'CallExpression':
|
|
139
|
+
if (node.callee.name === 'eval' || (node.callee.type === 'SequenceExpression' && node.callee.expressions.at(-1)?.name === 'eval')) {
|
|
140
|
+
if (node.callee.type === 'SequenceExpression' || node.optional) {
|
|
141
|
+
// indirect eval, no scopes
|
|
142
|
+
node._semanticScopes = [ node ];
|
|
143
|
+
node._semanticScopes.lastFuncs = [ 0 ];
|
|
144
|
+
} else {
|
|
145
|
+
// direct eval, use existing scope
|
|
146
|
+
node._semanticScopes = Object.assign([], scopes);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
case 'NewExpression':
|
|
151
|
+
if (node.callee.name === 'Function') {
|
|
152
|
+
// todo: this is probably wrong and needs to add own new scope
|
|
153
|
+
node._semanticScopes = Object.assign([], scopes);
|
|
154
|
+
}
|
|
155
|
+
break;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
for (const x in node) {
|
|
159
|
+
if (node[x] != null && typeof node[x] === 'object' && x[0] !== '_') {
|
|
160
|
+
if (node[x].type) annotate(node[x]);
|
|
161
|
+
if (Array.isArray(node[x])) {
|
|
162
|
+
for (const y of node[x]) annotate(y);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (openedScope) {
|
|
168
|
+
scopes.pop();
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
export default (node, _scopes = null) => {
|
|
173
|
+
if (!_scopes) {
|
|
174
|
+
_scopes = [ node ];
|
|
175
|
+
_scopes.lastFuncs = [ 0 ];
|
|
176
|
+
}
|
|
177
|
+
scopes = _scopes;
|
|
178
|
+
|
|
179
|
+
analyze(node);
|
|
180
|
+
if (scopes.length !== _scopes.length) throw new Error('Scope mismatch');
|
|
181
|
+
|
|
182
|
+
annotate(node);
|
|
183
|
+
return node;
|
|
184
|
+
};
|
package/jsr.json
CHANGED
package/package.json
CHANGED
package/runtime/index.js
CHANGED
package/foo.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
(typeof console === 'undefined' ? print : console.log)('lol');
|