porffor 0.60.9 → 0.60.10
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.js +79 -69
- package/compiler/builtins_precompiled.js +1072 -1072
- package/compiler/codegen.js +45 -42
- package/compiler/precompile.js +2 -2
- package/foo.js +34 -0
- package/package.json +1 -1
- package/runtime/index.js +1 -1
package/compiler/codegen.js
CHANGED
@@ -89,7 +89,7 @@ const isFuncType = type =>
|
|
89
89
|
type === 'FunctionDeclaration' || type === 'FunctionExpression' || type === 'ArrowFunctionExpression' ||
|
90
90
|
type === 'ClassDeclaration' || type === 'ClassExpression';
|
91
91
|
const hasFuncWithName = name =>
|
92
|
-
|
92
|
+
name in funcIndex || name in builtinFuncs || name in importedFuncs || name in internalConstrs;
|
93
93
|
|
94
94
|
const astCache = new WeakMap();
|
95
95
|
const cacheAst = (decl, wasm) => {
|
@@ -541,8 +541,8 @@ const generateEnum = (scope, decl) => {
|
|
541
541
|
const optional = (op, clause = op.at(-1)) => clause || clause === 0 ? (Array.isArray(op[0]) ? op : [ op ]) : [];
|
542
542
|
|
543
543
|
const lookupName = (scope, name) => {
|
544
|
-
if (
|
545
|
-
if (
|
544
|
+
if (name in scope.locals) return [ scope.locals[name], false ];
|
545
|
+
if (name in globals) return [ globals[name], true ];
|
546
546
|
|
547
547
|
return [ undefined, undefined ];
|
548
548
|
};
|
@@ -608,7 +608,7 @@ const hoistLookupType = (scope, name) => {
|
|
608
608
|
const lookup = (scope, name, failEarly = false) => {
|
609
609
|
let local = scope.locals[name];
|
610
610
|
|
611
|
-
if (
|
611
|
+
if (name in builtinVars) {
|
612
612
|
let wasm = builtinVars[name];
|
613
613
|
if (wasm.usesImports) scope.usesImports = true;
|
614
614
|
|
@@ -616,9 +616,9 @@ const lookup = (scope, name, failEarly = false) => {
|
|
616
616
|
return wasm.slice();
|
617
617
|
}
|
618
618
|
|
619
|
-
if (!
|
619
|
+
if (!(name in funcIndex) && name in builtinFuncs) {
|
620
620
|
includeBuiltin(scope, name);
|
621
|
-
} else if (
|
621
|
+
} else if (name in internalConstrs) {
|
622
622
|
// todo: return an actual something
|
623
623
|
return [ number(1) ];
|
624
624
|
}
|
@@ -662,16 +662,16 @@ const lookup = (scope, name, failEarly = false) => {
|
|
662
662
|
}
|
663
663
|
|
664
664
|
// no local var with name
|
665
|
-
if (
|
666
|
-
if (
|
667
|
-
if (
|
665
|
+
if (name in globals) return [ [ Opcodes.global_get, globals[name].idx ] ];
|
666
|
+
if (name in funcIndex) return funcRef(funcByName(name));
|
667
|
+
if (name in importedFuncs) return [ number(importedFuncs[name] - importedFuncs.length) ];
|
668
668
|
|
669
669
|
if (name.startsWith('__')) {
|
670
670
|
// return undefined if unknown key in already known var
|
671
671
|
let parent = name.slice(2).split('_').slice(0, -1).join('_');
|
672
672
|
if (parent.includes('_')) parent = '__' + parent;
|
673
673
|
|
674
|
-
if (
|
674
|
+
if ((name + '$get') in builtinFuncs) {
|
675
675
|
// hack: force error as accessors should only be used with objects anyway
|
676
676
|
return internalThrow(scope, 'TypeError', 'Accessor called without object');
|
677
677
|
}
|
@@ -1474,7 +1474,7 @@ const asmFuncToAsm = (scope, func, extra) => func(scope, {
|
|
1474
1474
|
},
|
1475
1475
|
glbl: (opcode, name, type) => {
|
1476
1476
|
const globalName = '#porf#' + name; // avoid potential name clashing with user js
|
1477
|
-
if (!
|
1477
|
+
if (!(globalName in globals)) {
|
1478
1478
|
const idx = globals['#ind']++;
|
1479
1479
|
globals[globalName] = { idx, type };
|
1480
1480
|
|
@@ -1509,7 +1509,7 @@ const asmFuncToAsm = (scope, func, extra) => func(scope, {
|
|
1509
1509
|
return out;
|
1510
1510
|
},
|
1511
1511
|
loc: (name, type) => {
|
1512
|
-
if (!
|
1512
|
+
if (!(name in scope.locals)) {
|
1513
1513
|
const idx = scope.localInd++;
|
1514
1514
|
scope.locals[name] = { idx, type };
|
1515
1515
|
}
|
@@ -1551,7 +1551,7 @@ const asmFunc = (name, func) => {
|
|
1551
1551
|
if (existing) return existing;
|
1552
1552
|
|
1553
1553
|
const allLocals = params.concat(localTypes);
|
1554
|
-
const locals =
|
1554
|
+
const locals = Object.create(null);
|
1555
1555
|
for (let i = 0; i < allLocals.length; i++) {
|
1556
1556
|
locals[localNames[i] ?? `l${i}`] = { idx: i, type: allLocals[i] };
|
1557
1557
|
}
|
@@ -1646,14 +1646,14 @@ const getType = (scope, name, failEarly = false) => {
|
|
1646
1646
|
[ null, () => hoistLookupType(scope, name) ]
|
1647
1647
|
];
|
1648
1648
|
|
1649
|
-
if (
|
1649
|
+
if (name in builtinVars) return [ number(builtinVars[name].type ?? TYPES.number, Valtype.i32) ];
|
1650
1650
|
|
1651
1651
|
let metadata, typeLocal, global = null;
|
1652
|
-
if (
|
1652
|
+
if (name in scope.locals) {
|
1653
1653
|
metadata = scope.locals[name].metadata;
|
1654
1654
|
typeLocal = scope.locals[name + '#type'];
|
1655
1655
|
global = false;
|
1656
|
-
} else if (
|
1656
|
+
} else if (name in globals) {
|
1657
1657
|
metadata = globals[name].metadata;
|
1658
1658
|
typeLocal = globals[name + '#type'];
|
1659
1659
|
global = true;
|
@@ -1687,10 +1687,10 @@ const setType = (scope, name, type, noInfer = false) => {
|
|
1687
1687
|
const out = typeof type === 'number' ? [ number(type, Valtype.i32) ] : type;
|
1688
1688
|
|
1689
1689
|
let metadata, typeLocal, global = false;
|
1690
|
-
if (
|
1690
|
+
if (name in scope.locals) {
|
1691
1691
|
metadata = scope.locals[name].metadata;
|
1692
1692
|
typeLocal = scope.locals[name + '#type'];
|
1693
|
-
} else if (
|
1693
|
+
} else if (name in globals) {
|
1694
1694
|
metadata = globals[name].metadata;
|
1695
1695
|
typeLocal = globals[name + '#type'];
|
1696
1696
|
global = true;
|
@@ -1797,8 +1797,8 @@ const getNodeType = (scope, node) => {
|
|
1797
1797
|
if (func.returnType != null) return func.returnType;
|
1798
1798
|
}
|
1799
1799
|
|
1800
|
-
if (
|
1801
|
-
if (
|
1800
|
+
if (name in builtinFuncs && builtinFuncs[name].returnType != null) return builtinFuncs[name].returnType;
|
1801
|
+
if (name in internalConstrs && internalConstrs[name].type != null) return internalConstrs[name].type;
|
1802
1802
|
|
1803
1803
|
if (name.startsWith('__Porffor_wasm_')) {
|
1804
1804
|
// todo: return undefined for non-returning ops
|
@@ -2171,7 +2171,7 @@ const createThisArg = (scope, decl) => {
|
|
2171
2171
|
const name = decl.callee?.name;
|
2172
2172
|
if (decl._new) {
|
2173
2173
|
// if precompiling or builtin func, just make it null as unused
|
2174
|
-
if (!decl._forceCreateThis && (globalThis.precompile ||
|
2174
|
+
if (!decl._forceCreateThis && (globalThis.precompile || name in builtinFuncs)) return [
|
2175
2175
|
number(NULL),
|
2176
2176
|
number(TYPES.object, Valtype.i32)
|
2177
2177
|
];
|
@@ -2338,7 +2338,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
2338
2338
|
target.name = spl.slice(0, -1).join('_');
|
2339
2339
|
|
2340
2340
|
if (builtinFuncs['__' + target.name + '_' + protoName]) protoName = null;
|
2341
|
-
else if (lookupName(scope, target.name)[0] == null && !
|
2341
|
+
else if (lookupName(scope, target.name)[0] == null && !(target.name in builtinFuncs)) {
|
2342
2342
|
if (lookupName(scope, '__' + target.name)[0] != null || builtinFuncs['__' + target.name]) target.name = '__' + target.name;
|
2343
2343
|
else protoName = null;
|
2344
2344
|
}
|
@@ -2478,18 +2478,18 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
|
|
2478
2478
|
let idx;
|
2479
2479
|
if (decl._funcIdx) {
|
2480
2480
|
idx = decl._funcIdx;
|
2481
|
-
} else if (
|
2481
|
+
} else if (name in internalConstrs && !decl._noInternalConstr) {
|
2482
2482
|
if (decl._new && internalConstrs[name].notConstr) return internalThrow(scope, 'TypeError', `${unhackName(name)} is not a constructor`, true);
|
2483
2483
|
return internalConstrs[name].generate(scope, decl, _global, _name);
|
2484
|
-
} else if (
|
2484
|
+
} else if (name in funcIndex) {
|
2485
2485
|
idx = funcIndex[name];
|
2486
2486
|
} else if (scope.name === name) {
|
2487
2487
|
// fallback for own func but with a different var/id name
|
2488
2488
|
idx = scope.index;
|
2489
|
-
} else if (
|
2489
|
+
} else if (name in importedFuncs) {
|
2490
2490
|
idx = importedFuncs[name];
|
2491
2491
|
scope.usesImports = true;
|
2492
|
-
} else if (
|
2492
|
+
} else if (name in builtinFuncs) {
|
2493
2493
|
if (decl._new && !builtinFuncs[name].constr) return internalThrow(scope, 'TypeError', `${unhackName(name)} is not a constructor`, true);
|
2494
2494
|
|
2495
2495
|
includeBuiltin(scope, name);
|
@@ -3136,7 +3136,7 @@ const allocVar = (scope, name, global = false, type = true, redecl = false, i32
|
|
3136
3136
|
const target = global ? globals : scope.locals;
|
3137
3137
|
|
3138
3138
|
// already declared
|
3139
|
-
if (
|
3139
|
+
if (name in target) {
|
3140
3140
|
if (redecl) {
|
3141
3141
|
// force change old local name(s)
|
3142
3142
|
target['#redecl_' + name + uniqId()] = target[name];
|
@@ -3332,7 +3332,7 @@ const generateVarDstr = (scope, kind, pattern, init, defaultValue, global) => {
|
|
3332
3332
|
const [ _func, out ] = generateFunc(scope, init, true);
|
3333
3333
|
|
3334
3334
|
const funcName = init.id?.name;
|
3335
|
-
if (name !== funcName &&
|
3335
|
+
if (name !== funcName && funcName in funcIndex) {
|
3336
3336
|
funcIndex[name] = funcIndex[funcName];
|
3337
3337
|
delete funcIndex[funcName];
|
3338
3338
|
}
|
@@ -3347,7 +3347,7 @@ const generateVarDstr = (scope, kind, pattern, init, defaultValue, global) => {
|
|
3347
3347
|
}
|
3348
3348
|
|
3349
3349
|
let out = [];
|
3350
|
-
if (topLevel &&
|
3350
|
+
if (topLevel && name in builtinVars) {
|
3351
3351
|
// cannot redeclare
|
3352
3352
|
if (kind !== 'var') return internalThrow(scope, 'SyntaxError', `Identifier '${unhackName(name)}' has already been declared`);
|
3353
3353
|
|
@@ -3395,7 +3395,7 @@ const generateVarDstr = (scope, kind, pattern, init, defaultValue, global) => {
|
|
3395
3395
|
}
|
3396
3396
|
|
3397
3397
|
if (globalThis.precompile && global) {
|
3398
|
-
scope.globalInits ??=
|
3398
|
+
scope.globalInits ??= Object.create(null);
|
3399
3399
|
scope.globalInits[name] = newOut;
|
3400
3400
|
}
|
3401
3401
|
} else {
|
@@ -4214,7 +4214,7 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
|
|
4214
4214
|
];
|
4215
4215
|
}
|
4216
4216
|
|
4217
|
-
if (
|
4217
|
+
if (name in builtinVars) {
|
4218
4218
|
if (scope.strict) return internalThrow(scope, 'TypeError', `Cannot assign to non-writable global ${name}`, true);
|
4219
4219
|
|
4220
4220
|
// just return rhs (eg `NaN = 2`)
|
@@ -5845,7 +5845,7 @@ const wrapBC = (bc, { prelude = [], postlude = [] } = {}) => {
|
|
5845
5845
|
|
5846
5846
|
const countParams = (func, name = undefined) => {
|
5847
5847
|
if (!func) {
|
5848
|
-
if (
|
5848
|
+
if (name in importedFuncs) {
|
5849
5849
|
// reverse lookup then normal lookup
|
5850
5850
|
func = importedFuncs[importedFuncs[name]];
|
5851
5851
|
if (func) return func.params?.length ?? func.params;
|
@@ -6577,6 +6577,7 @@ const generateTemplate = (scope, decl) => {
|
|
6577
6577
|
|
6578
6578
|
const generateTaggedTemplate = (scope, decl, global = false, name = undefined, valueUnused = false) => {
|
6579
6579
|
const intrinsics = {
|
6580
|
+
__proto__: null,
|
6580
6581
|
__Porffor_wasm: str => {
|
6581
6582
|
let out = [];
|
6582
6583
|
|
@@ -6603,12 +6604,12 @@ const generateTaggedTemplate = (scope, decl, global = false, name = undefined, v
|
|
6603
6604
|
const immediates = asm.slice(1).map(x => {
|
6604
6605
|
const n = parseFloat(x);
|
6605
6606
|
if (Number.isNaN(n) && x !== 'NaN') {
|
6606
|
-
if (
|
6607
|
+
if (x in builtinFuncs) {
|
6607
6608
|
if (funcIndex[x] == null) includeBuiltin(scope, x);
|
6608
6609
|
return funcIndex[x];
|
6609
6610
|
}
|
6610
6611
|
|
6611
|
-
if (
|
6612
|
+
if (x in importedFuncs) {
|
6612
6613
|
scope.usesImports = true;
|
6613
6614
|
return importedFuncs[x];
|
6614
6615
|
}
|
@@ -6648,7 +6649,7 @@ const generateTaggedTemplate = (scope, decl, global = false, name = undefined, v
|
|
6648
6649
|
};
|
6649
6650
|
|
6650
6651
|
const { quasis, expressions } = decl.quasi;
|
6651
|
-
if (
|
6652
|
+
if (decl.tag.name in intrinsics) {
|
6652
6653
|
let str = quasis[0].value.raw;
|
6653
6654
|
|
6654
6655
|
for (let i = 0; i < expressions.length; i++) {
|
@@ -6732,7 +6733,7 @@ const objectHack = node => {
|
|
6732
6733
|
if (objectName !== 'Object_prototype' && (node.property.name === 'propertyIsEnumerable' || node.property.name === 'hasOwnProperty' || node.property.name === 'isPrototypeOf')) return abortOut;
|
6733
6734
|
|
6734
6735
|
const name = '__' + objectName + '_' + node.property.name;
|
6735
|
-
if ((!hasFuncWithName(name) && !
|
6736
|
+
if ((!hasFuncWithName(name) && !(name in builtinVars) && !hasFuncWithName(name + '$get')) && (hasFuncWithName(objectName) || objectName in builtinVars || hasFuncWithName('__' + objectName) || ('__' + objectName) in builtinVars)) return abortOut;
|
6736
6737
|
|
6737
6738
|
if (Prefs.codeLog) log('codegen', `object hack! ${node.object.name}.${node.property.name} -> ${name}`);
|
6738
6739
|
|
@@ -6795,7 +6796,7 @@ const generateFunc = (scope, decl, forceNoExpr = false) => {
|
|
6795
6796
|
const arrow = decl.type === 'ArrowFunctionExpression' || decl.type === 'Program';
|
6796
6797
|
const func = {
|
6797
6798
|
start: decl.start,
|
6798
|
-
locals:
|
6799
|
+
locals: Object.create(null),
|
6799
6800
|
localInd: 0,
|
6800
6801
|
returns: [ valtypeBinary, Valtype.i32 ], // value, type
|
6801
6802
|
name,
|
@@ -7005,7 +7006,7 @@ const generateFunc = (scope, decl, forceNoExpr = false) => {
|
|
7005
7006
|
wasm.push(...getNodeType(func, getLastNode(decl.body.body)));
|
7006
7007
|
|
7007
7008
|
// inject promise job runner func at the end of main if promises are made
|
7008
|
-
if (
|
7009
|
+
if (('Promise' in funcIndex) || ('__Promise_resolve' in funcIndex) || ('__Promise_reject' in funcIndex)) {
|
7009
7010
|
wasm.push(
|
7010
7011
|
[ Opcodes.call, includeBuiltin(func, '__Porffor_promise_runJobs').index ]
|
7011
7012
|
);
|
@@ -7180,6 +7181,7 @@ const generateBlock = (scope, decl) => {
|
|
7180
7181
|
};
|
7181
7182
|
|
7182
7183
|
const internalConstrs = {
|
7184
|
+
__proto__: null,
|
7183
7185
|
__Array_of: {
|
7184
7186
|
// this is not a constructor but best fits internal structure here
|
7185
7187
|
generate: (scope, decl, global, name) => {
|
@@ -7369,7 +7371,8 @@ const internalConstrs = {
|
|
7369
7371
|
|
7370
7372
|
let globals, tags, exceptions, funcs, indirectFuncs, funcIndex, currentFuncIndex, depth, pages, data, typeswitchDepth, usedTypes, coctc, globalInfer, builtinFuncs, builtinVars, lastValtype;
|
7371
7373
|
export default program => {
|
7372
|
-
globals =
|
7374
|
+
globals = Object.create(null);
|
7375
|
+
globals['#ind'] = 0;
|
7373
7376
|
tags = [];
|
7374
7377
|
exceptions = [];
|
7375
7378
|
funcs = []; indirectFuncs = [];
|
@@ -7377,7 +7380,7 @@ export default program => {
|
|
7377
7380
|
return indirectFuncs._bytesPerFuncLut ??=
|
7378
7381
|
Math.min(Math.floor((pageSize * 2) / indirectFuncs.length), indirectFuncs.reduce((acc, x) => x.name.length > acc ? x.name.length : acc, 0) + 8);
|
7379
7382
|
};
|
7380
|
-
funcIndex =
|
7383
|
+
funcIndex = Object.create(null);
|
7381
7384
|
depth = [];
|
7382
7385
|
pages = new Map();
|
7383
7386
|
data = [];
|
@@ -7404,8 +7407,8 @@ export default program => {
|
|
7404
7407
|
// keep builtins between compiles as much as possible
|
7405
7408
|
if (lastValtype !== valtypeBinary) {
|
7406
7409
|
lastValtype = valtypeBinary;
|
7407
|
-
builtinFuncs =
|
7408
|
-
builtinVars =
|
7410
|
+
builtinFuncs = BuiltinFuncs();
|
7411
|
+
builtinVars = BuiltinVars({ builtinFuncs });
|
7409
7412
|
|
7410
7413
|
const getObjectName = x => x.startsWith('__') && x.slice(2, x.indexOf('_', 2));
|
7411
7414
|
objectHackers = ['assert', 'compareArray', 'Test262Error', ...new Set(Object.keys(builtinFuncs).map(getObjectName).concat(Object.keys(builtinVars).map(getObjectName)).filter(x => x))];
|
package/compiler/precompile.js
CHANGED
@@ -237,7 +237,7 @@ const precompile = async () => {
|
|
237
237
|
return `// autogenerated by compiler/precompile.js
|
238
238
|
import { number } from './encoding.js';
|
239
239
|
|
240
|
-
export const BuiltinFuncs =
|
240
|
+
export const BuiltinFuncs = x => {
|
241
241
|
${funcs.map(x => {
|
242
242
|
const rewriteWasm = wasm => {
|
243
243
|
const str = JSON.stringify(wasm.filter(x => x.length && (x[0] != null || typeof x[1] === 'string')), (k, v) => {
|
@@ -278,7 +278,7 @@ ${funcs.map(x => {
|
|
278
278
|
const name = x.name.includes('#') ? `['${x.name}']` : `.${x.name}`;
|
279
279
|
|
280
280
|
const returnTypes = [...(x.returnTypes ?? [])].filter(x => ![ TYPES.undefined, TYPES.number, TYPES.boolean, TYPES.function ].includes(x));
|
281
|
-
return `
|
281
|
+
return `x${name} = {
|
282
282
|
wasm:${rewriteWasm(x.wasm)},
|
283
283
|
params:${JSON.stringify(x.params)},typedParams:1,returns:${JSON.stringify(x.returns)},${x.returnType != null ? `returnType:${JSON.stringify(x.returnType)},` : ''}${returnTypes.length > 0 ? `returnTypes:${JSON.stringify(returnTypes)},` : ''}jsLength:${x.jsLength},
|
284
284
|
locals:${JSON.stringify(locals.slice(x.params.length).map(x => x[1].type))},localNames:${JSON.stringify(locals.map(x => x[0]))},
|
package/foo.js
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
// benchmark.js
|
2
|
+
const ITER = 50_000_000; // 50 million iterations
|
3
|
+
const key = "a";
|
4
|
+
|
5
|
+
// Create a null-prototype object
|
6
|
+
const o = Object.create(null);
|
7
|
+
o[key] = 123;
|
8
|
+
|
9
|
+
// --- Benchmark "in" ---
|
10
|
+
const t1 = Date.now();
|
11
|
+
for (let i = 0; i < ITER; i++) {
|
12
|
+
if (key in o) {
|
13
|
+
// noop
|
14
|
+
}
|
15
|
+
}
|
16
|
+
console.log(`in: ${Date.now() - t1}ms`);
|
17
|
+
|
18
|
+
// --- Benchmark Object.hasOwn ---
|
19
|
+
const t2 = Date.now();
|
20
|
+
for (let i = 0; i < ITER; i++) {
|
21
|
+
if (Object.hasOwn(o, key)) {
|
22
|
+
// noop
|
23
|
+
}
|
24
|
+
}
|
25
|
+
console.log(`Object.hasOwn: ${Date.now() - t2}ms`);
|
26
|
+
|
27
|
+
// --- Benchmark o[k] ---
|
28
|
+
const t3 = Date.now();
|
29
|
+
for (let i = 0; i < ITER; i++) {
|
30
|
+
if (o[key] !== undefined) {
|
31
|
+
// noop
|
32
|
+
}
|
33
|
+
}
|
34
|
+
console.log(`o[k]: ${Date.now() - t3}ms`);
|
package/package.json
CHANGED