porffor 0.55.18 → 0.55.19

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.
@@ -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), 1 ] ];
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
- }, 0 ] ];
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 ? [ number(TYPES.undefined, Valtype.i32) ] : [ [ null, () => hoistLookupType(scope, name), 1 ] ];
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
- }, 0 ],
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
- }, 0 ]);
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 const
4109
- return [ number(-1 * decl.argument.value) ];
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
- ...generate(scope, {
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
- export const generateTemplate = (scope, decl) => {
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();
@@ -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
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "porffor",
3
3
  "description": "An ahead-of-time JavaScript compiler",
4
- "version": "0.55.18",
4
+ "version": "0.55.19",
5
5
  "author": "Oliver Medhurst <honk@goose.icu>",
6
6
  "license": "MIT",
7
7
  "scripts": {},
package/r.cjs CHANGED
@@ -1,4 +1,4 @@
1
- Array.from.call(null, 0);
1
+
2
2
 
3
3
  // let o = { set foo(x) { console.log('set foo', x); }, __proto__: { set bar(x) { console.log('set bar', x); } } };
4
4
 
package/runner/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import fs from 'node:fs';
3
- globalThis.version = '0.55.18';
3
+ globalThis.version = '0.55.19';
4
4
 
5
5
  // deno compat
6
6
  if (typeof process === 'undefined' && typeof Deno !== 'undefined') {