porffor 0.39.1 → 0.39.3
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/__internal_object.ts +1 -1
- package/compiler/builtins/array.ts +37 -3
- package/compiler/builtins/error.js +3 -1
- package/compiler/builtins/promise.ts +14 -9
- package/compiler/builtins/reflect.ts +1 -19
- package/compiler/builtins/string.ts +1 -1
- package/compiler/builtins_precompiled.js +155 -95
- package/compiler/codegen.js +102 -50
- package/compiler/precompile.js +10 -7
- package/compiler/prototype.js +1 -1
- package/compiler/types.js +2 -0
- package/compiler/wrap.js +5 -3
- package/package.json +1 -1
- package/runner/index.js +1 -1
- package/runner/repl.js +1 -1
package/compiler/codegen.js
CHANGED
@@ -335,7 +335,7 @@ const internalThrow = (scope, constructor, message, expectsValue = Prefs.alwaysV
|
|
335
335
|
];
|
336
336
|
|
337
337
|
const generateIdent = (scope, decl) => {
|
338
|
-
const lookup = rawName => {
|
338
|
+
const lookup = (rawName, failEarly = false) => {
|
339
339
|
const name = mapName(rawName);
|
340
340
|
let local = scope.locals[rawName];
|
341
341
|
|
@@ -376,11 +376,18 @@ const generateIdent = (scope, decl) => {
|
|
376
376
|
let parent = rawName.slice(2).split('_').slice(0, -1).join('_');
|
377
377
|
if (parent.includes('_')) parent = '__' + parent;
|
378
378
|
|
379
|
-
const parentLookup = lookup(parent);
|
379
|
+
const parentLookup = lookup(parent, true);
|
380
380
|
if (!parentLookup[1]) return number(UNDEFINED);
|
381
381
|
}
|
382
382
|
|
383
|
-
if (local?.idx === undefined)
|
383
|
+
if (local?.idx === undefined) {
|
384
|
+
if (failEarly) return internalThrow(scope, 'ReferenceError', `${unhackName(name)} is not defined`, true);
|
385
|
+
|
386
|
+
return [ [ null, () => {
|
387
|
+
// try generating again at the end
|
388
|
+
return lookup(rawName, true);
|
389
|
+
}, 1 ] ];
|
390
|
+
}
|
384
391
|
|
385
392
|
return [
|
386
393
|
[ Opcodes.local_get, local.idx ],
|
@@ -397,16 +404,14 @@ const generateReturn = (scope, decl) => {
|
|
397
404
|
const arg = decl.argument ?? DEFAULT_VALUE();
|
398
405
|
|
399
406
|
if (scope.async) {
|
400
|
-
typeUsed(scope, TYPES.promise);
|
401
|
-
|
402
407
|
return [
|
403
408
|
// resolve promise with return value
|
404
|
-
[ Opcodes.local_get, scope.locals['#async_out_promise'].idx ],
|
405
|
-
...number(TYPES.promise, Valtype.i32),
|
406
|
-
|
407
409
|
...generate(scope, arg),
|
408
410
|
...(scope.returnType != null ? [] : getNodeType(scope, arg)),
|
409
411
|
|
412
|
+
[ Opcodes.local_get, scope.locals['#async_out_promise'].idx ],
|
413
|
+
...number(TYPES.promise, Valtype.i32),
|
414
|
+
|
410
415
|
[ Opcodes.call, includeBuiltin(scope, '__Porffor_promise_resolve').index ],
|
411
416
|
[ Opcodes.drop ],
|
412
417
|
[ Opcodes.drop ],
|
@@ -889,12 +894,17 @@ const generateBinaryExp = (scope, decl, _global, _name) => {
|
|
889
894
|
// try hacky version for built-ins first
|
890
895
|
const rightName = decl.right.name;
|
891
896
|
if (rightName) {
|
892
|
-
|
897
|
+
let checkType = TYPES[rightName.toLowerCase()];
|
893
898
|
if (checkType != null && rightName === TYPE_NAMES[checkType] && !rightName.endsWith('Error')) {
|
894
899
|
const out = generate(scope, decl.left);
|
895
900
|
disposeLeftover(out);
|
896
901
|
|
897
|
-
|
902
|
+
// switch primitive types to primitive object types
|
903
|
+
if (checkType === TYPES.number) checkType = TYPES.numberobject;
|
904
|
+
if (checkType === TYPES.boolean) checkType = TYPES.booleanobject;
|
905
|
+
|
906
|
+
// currently unsupported types
|
907
|
+
if ([TYPES.string].includes(checkType)) {
|
898
908
|
out.push(...number(0));
|
899
909
|
} else {
|
900
910
|
out.push(
|
@@ -1032,7 +1042,7 @@ const asmFuncToAsm = (scope, func) => {
|
|
1032
1042
|
return [ [ null, () => {
|
1033
1043
|
if (types.some(x => usedTypes.has(x))) return wasm();
|
1034
1044
|
return [];
|
1035
|
-
} ] ];
|
1045
|
+
}, 0 ] ];
|
1036
1046
|
}
|
1037
1047
|
}
|
1038
1048
|
});
|
@@ -1160,7 +1170,11 @@ const isExistingProtoFunc = name => {
|
|
1160
1170
|
return false;
|
1161
1171
|
};
|
1162
1172
|
|
1163
|
-
const getType = (scope, _name) => {
|
1173
|
+
const getType = (scope, _name, failEarly = false) => {
|
1174
|
+
const fallback = failEarly ? number(TYPES.undefined, Valtype.i32) : [ [ null, () => {
|
1175
|
+
return getType(scope, _name, true);
|
1176
|
+
}, 1 ] ];
|
1177
|
+
|
1164
1178
|
const name = mapName(_name);
|
1165
1179
|
|
1166
1180
|
if (Object.hasOwn(builtinVars, name)) return number(builtinVars[name].type ?? TYPES.number, Valtype.i32);
|
@@ -1172,7 +1186,7 @@ const getType = (scope, _name) => {
|
|
1172
1186
|
if (typeLocal) return [ [ Opcodes.local_get, typeLocal.idx ] ];
|
1173
1187
|
|
1174
1188
|
// todo: warn here?
|
1175
|
-
return
|
1189
|
+
return fallback;
|
1176
1190
|
}
|
1177
1191
|
|
1178
1192
|
if (name === 'arguments' && scope.name !== 'main' && !scope.arrow) {
|
@@ -1186,7 +1200,7 @@ const getType = (scope, _name) => {
|
|
1186
1200
|
if (typeLocal) return [ [ Opcodes.global_get, typeLocal.idx ] ];
|
1187
1201
|
|
1188
1202
|
// todo: warn here?
|
1189
|
-
return
|
1203
|
+
return fallback;
|
1190
1204
|
}
|
1191
1205
|
|
1192
1206
|
if (Object.hasOwn(builtinFuncs, name) || Object.hasOwn(importedFuncs, name) ||
|
@@ -1195,7 +1209,7 @@ const getType = (scope, _name) => {
|
|
1195
1209
|
|
1196
1210
|
if (isExistingProtoFunc(name)) return number(TYPES.function, Valtype.i32);
|
1197
1211
|
|
1198
|
-
return
|
1212
|
+
return fallback;
|
1199
1213
|
};
|
1200
1214
|
|
1201
1215
|
const setType = (scope, _name, type) => {
|
@@ -1507,7 +1521,10 @@ const countLeftover = wasm => {
|
|
1507
1521
|
|
1508
1522
|
for (let i = 0; i < wasm.length; i++) {
|
1509
1523
|
const inst = wasm[i];
|
1510
|
-
if (inst[0] == null)
|
1524
|
+
if (depth === 0 && inst[0] == null) {
|
1525
|
+
if (typeof inst[1] === 'function' && typeof inst[2] === 'number') count += inst[2];
|
1526
|
+
continue;
|
1527
|
+
}
|
1511
1528
|
|
1512
1529
|
if (depth === 0 && (inst[0] === Opcodes.if || inst[0] === Opcodes.block || inst[0] === Opcodes.loop)) {
|
1513
1530
|
if (inst[0] === Opcodes.if) count--;
|
@@ -1538,7 +1555,7 @@ const countLeftover = wasm => {
|
|
1538
1555
|
count += 2; // fixed return (value, type)
|
1539
1556
|
} else count--;
|
1540
1557
|
|
1541
|
-
// console.log(count, decompile([ inst ]).slice(0, -1));
|
1558
|
+
// console.log(count, depth, decompile([ inst ]).slice(0, -1));
|
1542
1559
|
}
|
1543
1560
|
|
1544
1561
|
return count;
|
@@ -2861,7 +2878,7 @@ const typeSwitch = (scope, type, bc, returns = valtypeBinary, fallthrough = fals
|
|
2861
2878
|
out = [];
|
2862
2879
|
if (types.some(x => usedTypes.has(x))) add();
|
2863
2880
|
return out;
|
2864
|
-
}]);
|
2881
|
+
}, 0 ]);
|
2865
2882
|
}
|
2866
2883
|
}
|
2867
2884
|
}
|
@@ -4542,16 +4559,7 @@ const generateForIn = (scope, decl) => {
|
|
4542
4559
|
};
|
4543
4560
|
|
4544
4561
|
const generateSwitch = (scope, decl) => {
|
4545
|
-
|
4546
|
-
const out = [
|
4547
|
-
...generate(scope, decl.discriminant),
|
4548
|
-
[ Opcodes.local_set, tmp ],
|
4549
|
-
|
4550
|
-
[ Opcodes.block, Blocktype.void ]
|
4551
|
-
];
|
4552
|
-
|
4553
|
-
depth.push('switch');
|
4554
|
-
|
4562
|
+
// special fast path just for `switch (Porffor.rawType(...))`
|
4555
4563
|
if (decl.discriminant.type === 'CallExpression' && decl.discriminant.callee.type === 'Identifier' && decl.discriminant.callee.name === '__Porffor_rawType') {
|
4556
4564
|
const cases = []
|
4557
4565
|
let canTypeCheck = true;
|
@@ -4574,28 +4582,42 @@ const generateSwitch = (scope, decl) => {
|
|
4574
4582
|
}
|
4575
4583
|
|
4576
4584
|
if (canTypeCheck) {
|
4577
|
-
|
4578
|
-
|
4579
|
-
|
4580
|
-
|
4581
|
-
|
4582
|
-
|
4583
|
-
|
4584
|
-
|
4585
|
-
|
4586
|
-
|
4587
|
-
|
4588
|
-
}
|
4585
|
+
depth.push('switch');
|
4586
|
+
|
4587
|
+
const out = typeSwitch(scope, getNodeType(scope, decl.discriminant.arguments[0]), () => {
|
4588
|
+
const bc = [];
|
4589
|
+
let types = [];
|
4590
|
+
for (const [ type, consequent ] of cases) {
|
4591
|
+
types.push(type);
|
4592
|
+
|
4593
|
+
if (consequent.length !== 0) {
|
4594
|
+
bc.push([ types, () => generate(scope, { type: 'BlockStatement', body: consequent }) ]);
|
4595
|
+
types = [];
|
4589
4596
|
}
|
4597
|
+
}
|
4590
4598
|
|
4591
|
-
|
4592
|
-
|
4599
|
+
return bc;
|
4600
|
+
}, Blocktype.void, true);
|
4593
4601
|
|
4594
4602
|
depth.pop();
|
4595
4603
|
return out;
|
4596
4604
|
}
|
4597
4605
|
}
|
4598
4606
|
|
4607
|
+
const tmpName = '#switch' + uniqId();
|
4608
|
+
const tmp = localTmp(scope, tmpName);
|
4609
|
+
localTmp(scope, tmpName + '#type', Valtype.i32);
|
4610
|
+
|
4611
|
+
const out = [
|
4612
|
+
...generate(scope, decl.discriminant),
|
4613
|
+
[ Opcodes.local_set, tmp ],
|
4614
|
+
...setType(scope, tmpName, getNodeType(scope, decl.discriminant)),
|
4615
|
+
|
4616
|
+
[ Opcodes.block, Blocktype.void ]
|
4617
|
+
];
|
4618
|
+
|
4619
|
+
depth.push('switch');
|
4620
|
+
|
4599
4621
|
const cases = decl.cases.slice();
|
4600
4622
|
const defaultCase = cases.findIndex(x => x.test == null);
|
4601
4623
|
if (defaultCase != -1) {
|
@@ -4613,9 +4635,16 @@ const generateSwitch = (scope, decl) => {
|
|
4613
4635
|
if (x.test) {
|
4614
4636
|
// todo: this should use same value zero
|
4615
4637
|
out.push(
|
4616
|
-
|
4617
|
-
|
4618
|
-
|
4638
|
+
...generate(scope, {
|
4639
|
+
type: 'BinaryExpression',
|
4640
|
+
operator: '===',
|
4641
|
+
left: {
|
4642
|
+
type: 'Identifier',
|
4643
|
+
name: tmpName
|
4644
|
+
},
|
4645
|
+
right: x.test
|
4646
|
+
}),
|
4647
|
+
Opcodes.i32_to_u,
|
4619
4648
|
[ Opcodes.br_if, i ]
|
4620
4649
|
);
|
4621
4650
|
} else {
|
@@ -4704,7 +4733,9 @@ const generateLabel = (scope, decl) => {
|
|
4704
4733
|
const generateThrow = (scope, decl) => {
|
4705
4734
|
scope.throws = true;
|
4706
4735
|
|
4707
|
-
|
4736
|
+
let exceptionMode = Prefs.exceptionMode ?? 'stack';
|
4737
|
+
if (globalThis.precompile) exceptionMode = decl.argument.callee != null ? 'lut' : 'stack';
|
4738
|
+
|
4708
4739
|
if (exceptionMode === 'lut') {
|
4709
4740
|
let message = decl.argument.value, constructor = null;
|
4710
4741
|
|
@@ -4730,7 +4761,7 @@ const generateThrow = (scope, decl) => {
|
|
4730
4761
|
|
4731
4762
|
return [
|
4732
4763
|
...number(exceptId, Valtype.i32),
|
4733
|
-
[ Opcodes.throw,
|
4764
|
+
[ Opcodes.throw, 0 ]
|
4734
4765
|
];
|
4735
4766
|
}
|
4736
4767
|
|
@@ -4744,7 +4775,7 @@ const generateThrow = (scope, decl) => {
|
|
4744
4775
|
return [
|
4745
4776
|
...generate(scope, decl.argument),
|
4746
4777
|
...getNodeType(scope, decl.argument),
|
4747
|
-
[ Opcodes.throw,
|
4778
|
+
[ Opcodes.throw, globalThis.precompile ? 1 : 0 ]
|
4748
4779
|
];
|
4749
4780
|
}
|
4750
4781
|
};
|
@@ -5992,7 +6023,9 @@ const generateFunc = (scope, decl) => {
|
|
5992
6023
|
if (decl.async) {
|
5993
6024
|
// make out promise local
|
5994
6025
|
allocVar(func, '#async_out_promise', false, false);
|
6026
|
+
|
5995
6027
|
func.async = true;
|
6028
|
+
typeUsed(func, TYPES.promise);
|
5996
6029
|
}
|
5997
6030
|
|
5998
6031
|
const wasm = prelude.concat(generate(func, body));
|
@@ -6002,10 +6035,29 @@ const generateFunc = (scope, decl) => {
|
|
6002
6035
|
wasm.unshift(
|
6003
6036
|
[ Opcodes.call, includeBuiltin(func, '__Porffor_promise_create').index ],
|
6004
6037
|
[ Opcodes.drop ],
|
6005
|
-
[ Opcodes.local_set, func.locals['#async_out_promise'].idx ]
|
6038
|
+
[ Opcodes.local_set, func.locals['#async_out_promise'].idx ],
|
6039
|
+
|
6040
|
+
// wrap in try for later catch
|
6041
|
+
[ Opcodes.try, Blocktype.void ]
|
6006
6042
|
);
|
6007
6043
|
|
6008
|
-
//
|
6044
|
+
// reject with thrown value if caught error
|
6045
|
+
wasm.push(
|
6046
|
+
[ Opcodes.catch, 0 ],
|
6047
|
+
|
6048
|
+
[ Opcodes.local_get, func.locals['#async_out_promise'].idx ],
|
6049
|
+
...number(TYPES.promise, Valtype.i32),
|
6050
|
+
|
6051
|
+
[ Opcodes.call, includeBuiltin(func, '__Porffor_promise_reject').index ],
|
6052
|
+
[ Opcodes.drop ],
|
6053
|
+
[ Opcodes.drop ],
|
6054
|
+
[ Opcodes.end ],
|
6055
|
+
|
6056
|
+
// return promise at the end of func
|
6057
|
+
[ Opcodes.local_get, func.locals['#async_out_promise'].idx ],
|
6058
|
+
...number(TYPES.promise, Valtype.i32),
|
6059
|
+
[ Opcodes.return ]
|
6060
|
+
);
|
6009
6061
|
}
|
6010
6062
|
|
6011
6063
|
if (name === 'main') {
|
package/compiler/precompile.js
CHANGED
@@ -25,7 +25,7 @@ const compile = async (file, _funcs) => {
|
|
25
25
|
first = source.slice(0, source.indexOf('\n'));
|
26
26
|
}
|
27
27
|
|
28
|
-
let args = ['--module', '--todo-time=compile', '--truthy=no_nan_negative', '--no-rm-unused-types', '--scoped-page-names', '--funsafe-no-unlikely-proto-checks', '--fast-length', '--parse-types', '--opt-types', '--no-passive-data', '--active-data'
|
28
|
+
let args = ['--module', '--todo-time=compile', '--truthy=no_nan_negative', '--no-rm-unused-types', '--scoped-page-names', '--funsafe-no-unlikely-proto-checks', '--zero-checks=charCodeAt', '--fast-length', '--parse-types', '--opt-types', '--no-passive-data', '--active-data'];
|
29
29
|
if (first.startsWith('// @porf')) {
|
30
30
|
args = first.slice('// @porf '.length).split(' ').concat(args);
|
31
31
|
}
|
@@ -152,13 +152,16 @@ const compile = async (file, _funcs) => {
|
|
152
152
|
y.splice(0, 10, 'alloc', pageName, x.pages.get(pageName).type, valtypeBinary);
|
153
153
|
}
|
154
154
|
|
155
|
+
if (n[0] === Opcodes.throw) {
|
156
|
+
if (y[0] === Opcodes.i32_const && n[1] === 0) {
|
157
|
+
const id = read_signedLEB128(y.slice(1));
|
158
|
+
y.splice(0, 10, 'throw', exceptions[id].constructor, exceptions[id].message);
|
155
159
|
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
wasm.splice(i + 1, 1);
|
160
|
+
// remove throw inst
|
161
|
+
wasm.splice(i + 1, 1);
|
162
|
+
} else {
|
163
|
+
n[1]--;
|
164
|
+
}
|
162
165
|
}
|
163
166
|
}
|
164
167
|
};
|
package/compiler/prototype.js
CHANGED
@@ -10,7 +10,7 @@ export const PrototypeFuncs = function() {
|
|
10
10
|
const noUnlikelyChecks = Prefs.funsafeNoUnlikelyProtoChecks;
|
11
11
|
|
12
12
|
let zeroChecks;
|
13
|
-
if (Prefs.zeroChecks) zeroChecks = Prefs.zeroChecks.split('
|
13
|
+
if (Prefs.zeroChecks) zeroChecks = Prefs.zeroChecks.split(',').reduce((acc, x) => { acc[x.toLowerCase()] = true; return acc; }, {});
|
14
14
|
else zeroChecks = {};
|
15
15
|
|
16
16
|
this[TYPES.array] = {
|
package/compiler/types.js
CHANGED
@@ -85,6 +85,8 @@ registerInternalType('SyntaxError');
|
|
85
85
|
registerInternalType('RangeError');
|
86
86
|
registerInternalType('EvalError');
|
87
87
|
registerInternalType('URIError');
|
88
|
+
registerInternalType('Test262Error');
|
89
|
+
registerInternalType('__Porffor_TodoError');
|
88
90
|
|
89
91
|
if (Prefs.largestTypes) {
|
90
92
|
const typeKeys = Object.keys(TYPES);
|
package/compiler/wrap.js
CHANGED
@@ -312,10 +312,12 @@ ${flags & 0b0001 ? ` get func idx: ${get}
|
|
312
312
|
case TYPES.syntaxerror:
|
313
313
|
case TYPES.rangeerror:
|
314
314
|
case TYPES.evalerror:
|
315
|
-
case TYPES.urierror:
|
315
|
+
case TYPES.urierror:
|
316
|
+
case TYPES.test262error:
|
317
|
+
case TYPES.__porffor_todoerror: {
|
316
318
|
const obj = porfToJSValue({ memory, funcs, pages }, value, TYPES.object);
|
317
|
-
const
|
318
|
-
|
319
|
+
const err = new (globalThis[TYPE_NAMES[type]] ?? Error)(obj.message);
|
320
|
+
|
319
321
|
err.name = obj.name;
|
320
322
|
err.stack = `${TYPE_NAMES[type]}: ${obj.message}`;
|
321
323
|
return err;
|
package/package.json
CHANGED
package/runner/index.js
CHANGED
package/runner/repl.js
CHANGED