porffor 0.55.18 → 0.55.20
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/bigint.ts +331 -0
- package/compiler/builtins/json.ts +5 -0
- package/compiler/builtins/number.ts +7 -2
- package/compiler/builtins/promise.ts +1 -1
- package/compiler/builtins/z_ecma262.ts +8 -3
- package/compiler/builtins_precompiled.js +526 -404
- package/compiler/codegen.js +91 -41
- package/compiler/precompile.js +4 -3
- package/compiler/wrap.js +19 -0
- package/package.json +1 -1
- package/r.cjs +1 -1
- package/runner/index.js +1 -1
package/compiler/codegen.js
CHANGED
@@ -505,7 +505,7 @@ const lookup = (scope, name, failEarly = false) => {
|
|
505
505
|
|
506
506
|
if (failEarly) return null;
|
507
507
|
|
508
|
-
return [ [ null, () => hoistLookup(scope, name)
|
508
|
+
return [ [ null, () => hoistLookup(scope, name) ] ];
|
509
509
|
}
|
510
510
|
|
511
511
|
return [
|
@@ -1004,6 +1004,18 @@ const performOp = (scope, op, left, right, leftType, rightType) => {
|
|
1004
1004
|
);
|
1005
1005
|
}
|
1006
1006
|
|
1007
|
+
if (!eqOp && (knownLeft === TYPES.bigint || knownRight === TYPES.bigint) && !(knownLeft === TYPES.bigint && knownRight === TYPES.bigint)) {
|
1008
|
+
const unknownType = knownLeft === TYPES.bigint ? rightType : leftType;
|
1009
|
+
startOut.push(
|
1010
|
+
...unknownType,
|
1011
|
+
number(TYPES.bigint, Valtype.i32),
|
1012
|
+
[ Opcodes.i32_ne ],
|
1013
|
+
[ Opcodes.if, Blocktype.void ],
|
1014
|
+
...internalThrow(scope, 'TypeError', 'Cannot mix BigInts and non-BigInts in numeric expressions'),
|
1015
|
+
[ Opcodes.end ]
|
1016
|
+
);
|
1017
|
+
}
|
1018
|
+
|
1007
1019
|
// todo: if equality op and an operand is undefined, return false
|
1008
1020
|
// todo: niche null hell with 0
|
1009
1021
|
|
@@ -1218,7 +1230,7 @@ const generateBinaryExp = (scope, decl) => {
|
|
1218
1230
|
|
1219
1231
|
const asmFuncToAsm = (scope, func) => {
|
1220
1232
|
return func(scope, {
|
1221
|
-
Valtype, Opcodes, TYPES, TYPE_NAMES, typeSwitch, makeString, internalThrow,
|
1233
|
+
Valtype, Opcodes, TYPES, TYPE_NAMES, usedTypes, typeSwitch, makeString, internalThrow,
|
1222
1234
|
getNodeType, generate, generateIdent,
|
1223
1235
|
builtin: (n, offset = false) => {
|
1224
1236
|
let idx = funcIndex[n] ?? importedFuncs[n];
|
@@ -1289,7 +1301,7 @@ const asmFuncToAsm = (scope, func) => {
|
|
1289
1301
|
return [ [ null, () => {
|
1290
1302
|
if (types.some(x => usedTypes.has(x))) return wasm();
|
1291
1303
|
return [];
|
1292
|
-
}
|
1304
|
+
} ] ];
|
1293
1305
|
}
|
1294
1306
|
},
|
1295
1307
|
i32ify: wasm => {
|
@@ -1439,7 +1451,11 @@ const setInferred = (scope, name, type, global = false) => {
|
|
1439
1451
|
};
|
1440
1452
|
|
1441
1453
|
const getType = (scope, name, failEarly = false) => {
|
1442
|
-
const fallback = failEarly ? [
|
1454
|
+
const fallback = failEarly ? [
|
1455
|
+
number(TYPES.undefined, Valtype.i32)
|
1456
|
+
] : [
|
1457
|
+
[ null, () => hoistLookupType(scope, name) ]
|
1458
|
+
];
|
1443
1459
|
|
1444
1460
|
if (Object.hasOwn(builtinVars, name)) return [ number(builtinVars[name].type ?? TYPES.number, Valtype.i32) ];
|
1445
1461
|
|
@@ -1636,13 +1652,15 @@ const getNodeType = (scope, node) => {
|
|
1636
1652
|
|
1637
1653
|
if (node.type === 'BinaryExpression') {
|
1638
1654
|
if (['==', '===', '!=', '!==', '>', '>=', '<', '<=', 'instanceof', 'in'].includes(node.operator)) return TYPES.boolean;
|
1639
|
-
if (node.operator !== '+') return TYPES.number;
|
1640
1655
|
|
1641
1656
|
const leftType = getNodeType(scope, node.left);
|
1642
1657
|
const rightType = getNodeType(scope, node.right);
|
1643
1658
|
const knownLeft = knownTypeWithGuess(scope, leftType);
|
1644
1659
|
const knownRight = knownTypeWithGuess(scope, rightType);
|
1645
1660
|
|
1661
|
+
if (knownLeft === TYPES.bigint || knownRight === TYPES.bigint) return TYPES.bigint;
|
1662
|
+
if (node.operator !== '+') return TYPES.number;
|
1663
|
+
|
1646
1664
|
if ((knownLeft != null || knownRight != null) && !(
|
1647
1665
|
(knownLeft === TYPES.string || knownRight === TYPES.string) ||
|
1648
1666
|
(knownLeft === TYPES.bytestring || knownRight === TYPES.bytestring) ||
|
@@ -1667,6 +1685,11 @@ const getNodeType = (scope, node) => {
|
|
1667
1685
|
if (node.operator === 'delete') return TYPES.boolean;
|
1668
1686
|
if (node.operator === 'typeof') return TYPES.bytestring;
|
1669
1687
|
|
1688
|
+
// todo: proper bigint support
|
1689
|
+
const type = getNodeType(scope, node.argument);
|
1690
|
+
const known = knownType(scope, type);
|
1691
|
+
if (known === TYPES.bigint) return TYPES.bigint;
|
1692
|
+
|
1670
1693
|
return TYPES.number;
|
1671
1694
|
}
|
1672
1695
|
|
@@ -1773,6 +1796,29 @@ const generateLiteral = (scope, decl, global, name) => {
|
|
1773
1796
|
case 'string':
|
1774
1797
|
return makeString(scope, decl.value);
|
1775
1798
|
|
1799
|
+
case 'bigint':
|
1800
|
+
let n = decl.value;
|
1801
|
+
|
1802
|
+
// inline if small enough
|
1803
|
+
if ((n < 0 ? -n : n) < 0x8000000000000n) {
|
1804
|
+
return [ number(Number(n)) ];
|
1805
|
+
}
|
1806
|
+
|
1807
|
+
// todo/opt: calculate and statically store digits
|
1808
|
+
return generate(scope, {
|
1809
|
+
type: 'CallExpression',
|
1810
|
+
callee: {
|
1811
|
+
type: 'Identifier',
|
1812
|
+
name: '__Porffor_bigint_fromString'
|
1813
|
+
},
|
1814
|
+
arguments: [
|
1815
|
+
{
|
1816
|
+
type: 'Literal',
|
1817
|
+
value: decl.value.toString()
|
1818
|
+
}
|
1819
|
+
]
|
1820
|
+
});
|
1821
|
+
|
1776
1822
|
default:
|
1777
1823
|
return todo(scope, `cannot generate literal of type ${typeof decl.value}`, true);
|
1778
1824
|
}
|
@@ -1833,14 +1879,6 @@ const ArrayUtil = {
|
|
1833
1879
|
]
|
1834
1880
|
};
|
1835
1881
|
|
1836
|
-
const getLastInst = wasm => {
|
1837
|
-
for (let i = wasm.length - 1; i >= 0; i--) {
|
1838
|
-
if (wasm[i]?.[0] != null) return wasm[i];
|
1839
|
-
}
|
1840
|
-
|
1841
|
-
return null;
|
1842
|
-
};
|
1843
|
-
|
1844
1882
|
const createNewTarget = (scope, decl, idx = 0, force = false) => {
|
1845
1883
|
if (decl._new || force) {
|
1846
1884
|
return [
|
@@ -2774,7 +2812,7 @@ const generateThis = (scope, decl) => {
|
|
2774
2812
|
[ Opcodes.local_set, scope.locals['#this#type'].idx ],
|
2775
2813
|
[ Opcodes.end ]
|
2776
2814
|
];
|
2777
|
-
}
|
2815
|
+
} ],
|
2778
2816
|
|
2779
2817
|
[ Opcodes.local_get, scope.locals['#this'].idx ]
|
2780
2818
|
];
|
@@ -3056,7 +3094,7 @@ const typeSwitch = (scope, type, bc, returns = valtypeBinary, fallthrough = fals
|
|
3056
3094
|
depth = oldDepth;
|
3057
3095
|
}
|
3058
3096
|
return out;
|
3059
|
-
}
|
3097
|
+
} ]);
|
3060
3098
|
}
|
3061
3099
|
}
|
3062
3100
|
}
|
@@ -4079,6 +4117,24 @@ const ifIdentifierErrors = (scope, decl) => {
|
|
4079
4117
|
};
|
4080
4118
|
|
4081
4119
|
const generateUnary = (scope, decl) => {
|
4120
|
+
const toNumeric = () => {
|
4121
|
+
// opt: skip if already known as number type
|
4122
|
+
generate(scope, decl.argument); // hack: fix last type not being defined for getNodeType before generation
|
4123
|
+
const known = knownType(scope, getNodeType(scope, decl.argument));
|
4124
|
+
if (known === TYPES.number) return generate(scope, decl.argument);
|
4125
|
+
|
4126
|
+
return generate(scope, {
|
4127
|
+
type: 'CallExpression',
|
4128
|
+
callee: {
|
4129
|
+
type: 'Identifier',
|
4130
|
+
name: '__ecma262_ToNumeric'
|
4131
|
+
},
|
4132
|
+
arguments: [
|
4133
|
+
decl.argument
|
4134
|
+
]
|
4135
|
+
});
|
4136
|
+
};
|
4137
|
+
|
4082
4138
|
switch (decl.operator) {
|
4083
4139
|
case '+':
|
4084
4140
|
// opt: skip ToNumber if already known as number type
|
@@ -4104,21 +4160,31 @@ const generateUnary = (scope, decl) => {
|
|
4104
4160
|
case '-':
|
4105
4161
|
// +x * -1
|
4106
4162
|
|
4107
|
-
if (decl.prefix && decl.argument.type === 'Literal' && typeof decl.argument.value === 'number') {
|
4108
|
-
// if -n, just return that as a
|
4109
|
-
return
|
4163
|
+
if (decl.prefix && decl.argument.type === 'Literal' && (typeof decl.argument.value === 'number' || typeof decl.argument.value === 'bigint')) {
|
4164
|
+
// if -n, just return that as a literal
|
4165
|
+
return generate(scope, {
|
4166
|
+
type: 'Literal',
|
4167
|
+
value: -decl.argument.value
|
4168
|
+
});
|
4110
4169
|
}
|
4111
4170
|
|
4171
|
+
// todo: proper bigint support
|
4112
4172
|
return [
|
4113
|
-
...
|
4114
|
-
type: 'UnaryExpression',
|
4115
|
-
operator: '+',
|
4116
|
-
prefix: true,
|
4117
|
-
argument: decl.argument
|
4118
|
-
}),
|
4173
|
+
...toNumeric(),
|
4119
4174
|
...(valtype === 'f64' ? [ [ Opcodes.f64_neg ] ] : [ number(-1), [ Opcodes.mul ] ])
|
4120
4175
|
];
|
4121
4176
|
|
4177
|
+
case '~':
|
4178
|
+
// todo: proper bigint support
|
4179
|
+
return [
|
4180
|
+
...toNumeric(),
|
4181
|
+
Opcodes.i32_to,
|
4182
|
+
[ Opcodes.i32_const, ...signedLEB128(-1) ],
|
4183
|
+
[ Opcodes.i32_xor ],
|
4184
|
+
Opcodes.i32_from
|
4185
|
+
];
|
4186
|
+
|
4187
|
+
|
4122
4188
|
case '!':
|
4123
4189
|
const arg = decl.argument;
|
4124
4190
|
if (arg.type === 'UnaryExpression' && arg.operator === '!') {
|
@@ -4129,20 +4195,6 @@ const generateUnary = (scope, decl) => {
|
|
4129
4195
|
// !=
|
4130
4196
|
return falsy(scope, generate(scope, arg), getNodeType(scope, arg), false, false);
|
4131
4197
|
|
4132
|
-
case '~':
|
4133
|
-
return [
|
4134
|
-
...generate(scope, {
|
4135
|
-
type: 'UnaryExpression',
|
4136
|
-
operator: '+',
|
4137
|
-
prefix: true,
|
4138
|
-
argument: decl.argument
|
4139
|
-
}),
|
4140
|
-
Opcodes.i32_to,
|
4141
|
-
[ Opcodes.i32_const, ...signedLEB128(-1) ],
|
4142
|
-
[ Opcodes.i32_xor ],
|
4143
|
-
Opcodes.i32_from
|
4144
|
-
];
|
4145
|
-
|
4146
4198
|
case 'void': {
|
4147
4199
|
// drop current expression value after running, give undefined
|
4148
4200
|
const out = generate(scope, decl.argument);
|
@@ -6299,7 +6351,7 @@ const generateClass = (scope, decl) => {
|
|
6299
6351
|
return out;
|
6300
6352
|
};
|
6301
6353
|
|
6302
|
-
|
6354
|
+
const generateTemplate = (scope, decl) => {
|
6303
6355
|
let current = null;
|
6304
6356
|
const append = val => {
|
6305
6357
|
if (!current) {
|
@@ -7013,8 +7065,6 @@ export default program => {
|
|
7013
7065
|
Opcodes.load = [ Opcodes.i32_load, Opcodes.i64_load, Opcodes.f64_load ][valtypeInd];
|
7014
7066
|
Opcodes.store = [ Opcodes.i32_store, Opcodes.i64_store, Opcodes.f64_store ][valtypeInd];
|
7015
7067
|
|
7016
|
-
Opcodes.lt = [ Opcodes.i32_lt_s, Opcodes.i64_lt_s, Opcodes.f64_lt ][valtypeInd];
|
7017
|
-
|
7018
7068
|
builtinFuncs = new BuiltinFuncs();
|
7019
7069
|
builtinVars = new BuiltinVars({ builtinFuncs });
|
7020
7070
|
prototypeFuncs = new PrototypeFuncs();
|
package/compiler/precompile.js
CHANGED
@@ -211,7 +211,8 @@ const precompile = async () => {
|
|
211
211
|
console.log(`\r${' '.repeat(80)}\r\u001b[2m${`[${total.toFixed(2)}ms]`.padEnd(12, ' ')}\u001b[0m\u001b[92mcompiled ${funcs.length} funcs from ${fileCount} files\u001b[0m \u001b[90m(${['parse', 'codegen', 'opt'].map(x => `${x}: ${((timing[x] / total) * 100).toFixed(0)}%`).join(', ')})\u001b[0m`);
|
212
212
|
|
213
213
|
const comptimeFlagChecks = {
|
214
|
-
hasFunc: x => `hasFunc('${x}')
|
214
|
+
hasFunc: x => `hasFunc('${x}')`,
|
215
|
+
hasType: x => `usedTypes.has(${TYPES[x]})`
|
215
216
|
};
|
216
217
|
|
217
218
|
return `// autogenerated by compiler/precompile.js
|
@@ -242,10 +243,10 @@ ${funcs.map(x => {
|
|
242
243
|
});
|
243
244
|
|
244
245
|
const [ id, extra ] = flag.split('.');
|
245
|
-
return `[null,()=>{if(${comptimeFlagChecks[id](extra)}){const r=()=>{valtype=Prefs.valtype??'f64';valtypeBinary=Valtype[valtype];const valtypeInd=['i32','i64','f64'].indexOf(valtype);Opcodes.i32_to=[[],[Opcodes.i32_wrap_i64],Opcodes.i32_trunc_sat_f64_s][valtypeInd];Opcodes.i32_to_u=[[],[Opcodes.i32_wrap_i64],Opcodes.i32_trunc_sat_f64_u][valtypeInd];Opcodes.i32_from=[[],[Opcodes.i64_extend_i32_s],[Opcodes.f64_convert_i32_s]][valtypeInd];Opcodes.i32_from_u=[[],[Opcodes.i64_extend_i32_u],[ Opcodes.f64_convert_i32_u]][valtypeInd]};const a=Prefs;Prefs=${prefs};r();const b=generate(_,${ast});Prefs=a;r();return b;}return []}]`;
|
246
|
+
return `[null,()=>{if(${comptimeFlagChecks[id](extra)}){const r=()=>{valtype=Prefs.valtype??'f64';valtypeBinary=Valtype[valtype];const valtypeInd=['i32','i64','f64'].indexOf(valtype);Opcodes.i32_to=[[],[Opcodes.i32_wrap_i64],Opcodes.i32_trunc_sat_f64_s][valtypeInd];Opcodes.i32_to_u=[[],[Opcodes.i32_wrap_i64],Opcodes.i32_trunc_sat_f64_u][valtypeInd];Opcodes.i32_from=[[],[Opcodes.i64_extend_i32_s],[Opcodes.f64_convert_i32_s]][valtypeInd];Opcodes.i32_from_u=[[],[Opcodes.i64_extend_i32_u],[ Opcodes.f64_convert_i32_u]][valtypeInd]};const a=Prefs;Prefs=${prefs};r();const b=generate(_,${ast}).slice(0,-1);Prefs=a;r();return b;}return []}]`;
|
246
247
|
});
|
247
248
|
|
248
|
-
return `(_,{${str.includes('hasFunc(') ? 'hasFunc,' : ''}${str.includes('Valtype[') ? 'Valtype,' : ''}${str.includes('i32ify') ? 'i32ify,' : ''}${str.includes('Opcodes.') ? 'Opcodes,' : ''}${str.includes('...t(') ? 't,' : ''}${`${str.includes('allocPage(') ? 'allocPage,' : ''}${str.includes('makeString(') ? 'makeString,' : ''}${str.includes('glbl(') ? 'glbl,' : ''}${str.includes('loc(') ? 'loc,' : ''}${str.includes('builtin(') ? 'builtin,' : ''}${str.includes('funcRef(') ? 'funcRef,' : ''}${str.includes('internalThrow(') ? 'internalThrow,' : ''}${str.includes('generateIdent(') ? 'generateIdent,' : ''}${str.includes('generate(') ? 'generate,' : ''}`.slice(0, -1)}})=>`.replace('_,{}', '') + str;
|
249
|
+
return `(_,{${str.includes('usedTypes.') ? 'usedTypes,' : ''}${str.includes('hasFunc(') ? 'hasFunc,' : ''}${str.includes('Valtype[') ? 'Valtype,' : ''}${str.includes('i32ify') ? 'i32ify,' : ''}${str.includes('Opcodes.') ? 'Opcodes,' : ''}${str.includes('...t(') ? 't,' : ''}${`${str.includes('allocPage(') ? 'allocPage,' : ''}${str.includes('makeString(') ? 'makeString,' : ''}${str.includes('glbl(') ? 'glbl,' : ''}${str.includes('loc(') ? 'loc,' : ''}${str.includes('builtin(') ? 'builtin,' : ''}${str.includes('funcRef(') ? 'funcRef,' : ''}${str.includes('internalThrow(') ? 'internalThrow,' : ''}${str.includes('generateIdent(') ? 'generateIdent,' : ''}${str.includes('generate(') ? 'generate,' : ''}`.slice(0, -1)}})=>`.replace('_,{}', '') + str;
|
249
250
|
};
|
250
251
|
|
251
252
|
const locals = Object.entries(x.locals).sort((a,b) => a[1].idx - b[1].idx)
|
package/compiler/wrap.js
CHANGED
@@ -335,6 +335,25 @@ ${flags & 0b0001 ? ` get func idx: ${get}
|
|
335
335
|
return out;
|
336
336
|
}
|
337
337
|
|
338
|
+
case TYPES.bigint: {
|
339
|
+
if (Math.abs(value) < 0x8000000000000) {
|
340
|
+
return BigInt(value);
|
341
|
+
}
|
342
|
+
value -= 0x8000000000000;
|
343
|
+
|
344
|
+
const negative = read(Uint8Array, memory, value, 1)[0] !== 0;
|
345
|
+
const len = read(Uint16Array, memory, value + 2, 1)[0];
|
346
|
+
const digits = read(Uint32Array, memory, value + 4, len);
|
347
|
+
|
348
|
+
if (Prefs.d) console.log(digits);
|
349
|
+
|
350
|
+
let result = 0n;
|
351
|
+
for (let i = 0; i < digits.length; i++) {
|
352
|
+
result = result * 0x100000000n + BigInt(digits[i]);
|
353
|
+
}
|
354
|
+
return negative ? -result : result;
|
355
|
+
}
|
356
|
+
|
338
357
|
default: return value;
|
339
358
|
}
|
340
359
|
};
|
package/package.json
CHANGED
package/r.cjs
CHANGED