porffor 0.42.5 → 0.42.7

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.
@@ -279,7 +279,7 @@ export const __Math_cbrt = (y: number): number => {
279
279
  if (!Number.isFinite(y)) return y;
280
280
 
281
281
  // Babylonian method
282
- let x = Math.abs(y);
282
+ let x:number = Math.abs(y);
283
283
 
284
284
  let prev: number;
285
285
 
@@ -1780,9 +1780,9 @@ params:[124,127],typedParams:1,returns:[124,127],typedReturns:1,
1780
1780
  locals:[124,124],localNames:["y","y#type","x","prev"],
1781
1781
  }
1782
1782
  this.__Math_cbrt = {
1783
- wasm:(_,{builtin})=>[[32,0],[68,0],[97],[4,64],[68,0],[65,1],[15],[11],[32,0],[16,builtin('__Number_isFinite')],[68,0],[97],[4,64],[32,0],[65,1],[15],[11],[32,0],[16,builtin('__Math_abs')],[33,2],[65,1],[33,3],[3,64],[2,64],[32,2],[33,4],[68,2],[32,2],[162],[32,0],[32,2],[32,2],[162],[163],[160],[68,3],[163],[34,2],[65,1],[33,3],[26],[32,4],[32,2],[161],[16,builtin('__Math_abs')],[68,1e-15],[100],[13,1],[11],[11],[32,0],[68,0],[99],[4,124],[32,2],[154],[65,1],[33,5],[5],[32,2],[32,3],[33,5],[11],[32,5],[15]],
1783
+ wasm:(_,{builtin})=>[[32,0],[68,0],[97],[4,64],[68,0],[65,1],[15],[11],[32,0],[16,builtin('__Number_isFinite')],[68,0],[97],[4,64],[32,0],[65,1],[15],[11],[32,0],[16,builtin('__Math_abs')],[33,2],[3,64],[2,64],[32,2],[33,3],[68,2],[32,2],[162],[32,0],[32,2],[32,2],[162],[163],[160],[68,3],[163],[33,2],[32,3],[32,2],[161],[16,builtin('__Math_abs')],[68,1e-15],[100],[13,1],[11],[11],[32,0],[68,0],[99],[4,124],[32,2],[154],[65,1],[33,4],[5],[32,2],[65,1],[33,4],[11],[32,4],[15]],
1784
1784
  params:[124,127],typedParams:1,returns:[124,127],typedReturns:1,
1785
- locals:[124,127,124,127],localNames:["y","y#type","x","x#type","prev","#last_type"],
1785
+ locals:[124,124,127],localNames:["y","y#type","x","prev","#last_type"],
1786
1786
  }
1787
1787
  this.__Math_hypot = {
1788
1788
  wasm:(_,{builtin})=>[[32,0],[32,0],[162],[32,2],[32,2],[162],[160],[65,1],[16,builtin('__Math_sqrt')],[34,4],[15]],
@@ -1340,8 +1340,12 @@ const setType = (scope, name, type) => {
1340
1340
  };
1341
1341
 
1342
1342
  const getLastType = scope => {
1343
+ if (!scope.locals['#last_type']) return number(TYPES.number, Valtype.i32);
1344
+
1343
1345
  scope.gotLastType = true;
1344
- return [ [ Opcodes.local_get, localTmp(scope, '#last_type', Valtype.i32) ] ];
1346
+ return [
1347
+ [ Opcodes.local_get, localTmp(scope, '#last_type', Valtype.i32) ]
1348
+ ];
1345
1349
  };
1346
1350
 
1347
1351
  const setLastType = (scope, type = []) => {
@@ -1388,11 +1392,7 @@ const getNodeType = (scope, node) => {
1388
1392
 
1389
1393
  if (name == null) {
1390
1394
  // iife
1391
- if (scope.locals['#last_type']) return getLastType(scope);
1392
-
1393
- // presume
1394
- if (Prefs.warnAssumedType) console.warn(`Indirect call assumed to be number`);
1395
- return TYPES.number;
1395
+ return getLastType(scope);
1396
1396
  }
1397
1397
 
1398
1398
  const func = funcByName(name);
@@ -1426,38 +1426,7 @@ const getNodeType = (scope, node) => {
1426
1426
  return TYPES.number;
1427
1427
  }
1428
1428
 
1429
- if (scope.locals['#last_type']) return getLastType(scope);
1430
-
1431
- // presume
1432
- if (Prefs.warnAssumedType) console.warn(`Call to ${name} assumed to be number`);
1433
- return TYPES.number;
1434
-
1435
- // let protoFunc;
1436
- // // ident.func()
1437
- // if (name && name.startsWith('__')) {
1438
- // const spl = name.slice(2).split('_');
1439
-
1440
- // const baseName = spl.slice(0, -1).join('_');
1441
- // const baseType = getType(scope, baseName);
1442
-
1443
- // const func = spl[spl.length - 1];
1444
- // protoFunc = prototypeFuncs[baseType]?.[func];
1445
- // }
1446
-
1447
- // // literal.func()
1448
- // if (!name && node.callee.type === 'MemberExpression') {
1449
- // if (node.callee.object.regex) {
1450
- // const funcName = node.callee.property.name;
1451
- // return Rhemyn[funcName] ? TYPES.boolean : TYPES.undefined;
1452
- // }
1453
-
1454
- // const baseType = getNodeType(scope, node.callee.object);
1455
-
1456
- // const func = node.callee.property.name;
1457
- // protoFunc = prototypeFuncs[baseType]?.[func];
1458
- // }
1459
-
1460
- // if (protoFunc) return protoFunc.returnType;
1429
+ return getLastType(scope);
1461
1430
  }
1462
1431
 
1463
1432
  if (node.type === 'ExpressionStatement') {
@@ -1505,10 +1474,7 @@ const getNodeType = (scope, node) => {
1505
1474
  if (knownLeft === TYPES.bytestring || knownRight === TYPES.bytestring)
1506
1475
  guess = TYPES.bytestring;
1507
1476
 
1508
- if (scope.locals['#last_type']) return getLastType(scope);
1509
-
1510
- // presume
1511
- return TYPES.number;
1477
+ return getLastType(scope);
1512
1478
  }
1513
1479
 
1514
1480
  if (node.type === 'UnaryExpression') {
@@ -1542,11 +1508,7 @@ const getNodeType = (scope, node) => {
1542
1508
  }
1543
1509
  }
1544
1510
 
1545
- if (scope.locals['#last_type']) return getLastType(scope);
1546
-
1547
- // presume
1548
- if (Prefs.warnAssumedType) console.warn(`Member access to field .${name} assumed to be number`);
1549
- return TYPES.number;
1511
+ return getLastType(scope);
1550
1512
  }
1551
1513
 
1552
1514
  if (node.type === 'TemplateLiteral') {
@@ -1579,11 +1541,7 @@ const getNodeType = (scope, node) => {
1579
1541
  }
1580
1542
  }
1581
1543
 
1582
- if (scope.locals['#last_type']) return getLastType(scope);
1583
-
1584
- // presume
1585
- if (Prefs.warnAssumedType) console.warn(`AST node ${node.type} assumed to be number`);
1586
- return TYPES.number;
1544
+ return getLastType(scope);
1587
1545
  })();
1588
1546
 
1589
1547
  const out = typeof ret === 'number' ? number(ret, Valtype.i32) : ret;
@@ -2447,6 +2405,31 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
2447
2405
  paramOffset += 4;
2448
2406
  }
2449
2407
 
2408
+ if (args.at(-1)?.type === 'SpreadElement') {
2409
+ // hack: support spread element if last by doing essentially:
2410
+ // const foo = (a, b, c, d) => ...
2411
+ // foo(a, b, ...c) -> _ = c; foo(a, b, _[0], _[1])
2412
+ const arg = args.at(-1).argument;
2413
+ out.push(
2414
+ ...generate(scope, arg),
2415
+ [ Opcodes.local_set, localTmp(scope, '#spread') ],
2416
+ ...getNodeType(scope, arg),
2417
+ [ Opcodes.local_set, localTmp(scope, '#spread#type', Valtype.i32) ]
2418
+ );
2419
+
2420
+ args.pop();
2421
+ const leftover = paramCount - args.length;
2422
+ for (let i = 0; i < leftover; i++) {
2423
+ args.push({
2424
+ type: 'MemberExpression',
2425
+ object: { type: 'Identifier', name: '#spread' },
2426
+ property: { type: 'Literal', value: i },
2427
+ computed: true,
2428
+ optional: false
2429
+ });
2430
+ }
2431
+ }
2432
+
2450
2433
  if (func && args.length < paramCount) {
2451
2434
  // too little args, push undefineds
2452
2435
  args = args.concat(new Array(paramCount - (func.hasRestArgument ? 1 : 0) - args.length).fill(DEFAULT_VALUE()));
@@ -3689,6 +3672,11 @@ const ifIdentifierErrors = (scope, decl) => {
3689
3672
  const generateUnary = (scope, decl) => {
3690
3673
  switch (decl.operator) {
3691
3674
  case '+':
3675
+ // opt: skip ToNumber if already known as number type
3676
+ generate(scope, decl.argument); // hack: fix last type not being defined for getNodeType before generation
3677
+ const known = knownType(scope, getNodeType(scope, decl.argument));
3678
+ if (known === TYPES.number) return generate(scope, decl.argument);
3679
+
3692
3680
  // 13.5.4 Unary + Operator, 13.5.4.1 Runtime Semantics: Evaluation
3693
3681
  // https://tc39.es/ecma262/#sec-unary-plus-operator-runtime-semantics-evaluation
3694
3682
  // 1. Let expr be ? Evaluation of UnaryExpression.
@@ -3705,7 +3693,7 @@ const generateUnary = (scope, decl) => {
3705
3693
  });
3706
3694
 
3707
3695
  case '-':
3708
- // * -1
3696
+ // +x * -1
3709
3697
 
3710
3698
  if (decl.prefix && decl.argument.type === 'Literal' && typeof decl.argument.value === 'number') {
3711
3699
  // if -n, just return that as a const
@@ -3713,7 +3701,12 @@ const generateUnary = (scope, decl) => {
3713
3701
  }
3714
3702
 
3715
3703
  return [
3716
- ...generate(scope, decl.argument),
3704
+ ...generate(scope, {
3705
+ type: 'UnaryExpression',
3706
+ operator: '+',
3707
+ prefix: true,
3708
+ argument: decl.argument
3709
+ }),
3717
3710
  ...(valtype === 'f64' ? [ [ Opcodes.f64_neg ] ] : [ ...number(-1), [ Opcodes.mul ] ])
3718
3711
  ];
3719
3712
 
@@ -3729,7 +3722,12 @@ const generateUnary = (scope, decl) => {
3729
3722
 
3730
3723
  case '~':
3731
3724
  return [
3732
- ...generate(scope, decl.argument),
3725
+ ...generate(scope, {
3726
+ type: 'UnaryExpression',
3727
+ operator: '+',
3728
+ prefix: true,
3729
+ argument: decl.argument
3730
+ }),
3733
3731
  Opcodes.i32_to,
3734
3732
  [ Opcodes.i32_const, ...signedLEB128(-1) ],
3735
3733
  [ Opcodes.i32_xor ],
@@ -3821,33 +3819,62 @@ const generateUnary = (scope, decl) => {
3821
3819
 
3822
3820
  const generateUpdate = (scope, decl, _global, _name, valueUnused = false) => {
3823
3821
  const { name } = decl.argument;
3824
-
3825
3822
  const [ local, isGlobal ] = lookupName(scope, name);
3823
+ if (local != null) {
3824
+ // fast path: just a local
3825
+ // todo: not as compliant as slow path (non numbers)
3826
+ const idx = local.idx;
3827
+ const out = [];
3828
+
3829
+ out.push([ isGlobal ? Opcodes.global_get : Opcodes.local_get, idx ]);
3830
+ if (!decl.prefix && !valueUnused) out.push([ isGlobal ? Opcodes.global_get : Opcodes.local_get, idx ]);
3831
+
3832
+ switch (decl.operator) {
3833
+ case '++':
3834
+ out.push(...number(1), [ Opcodes.add ]);
3835
+ break;
3826
3836
 
3827
- if (local === undefined) {
3828
- return todo(scope, `update expression with undefined variable`, true);
3829
- }
3830
-
3831
- const idx = local.idx;
3832
- const out = [];
3833
-
3834
- out.push([ isGlobal ? Opcodes.global_get : Opcodes.local_get, idx ]);
3835
- if (!decl.prefix && !valueUnused) out.push([ isGlobal ? Opcodes.global_get : Opcodes.local_get, idx ]);
3837
+ case '--':
3838
+ out.push(...number(1), [ Opcodes.sub ]);
3839
+ break;
3840
+ }
3836
3841
 
3837
- switch (decl.operator) {
3838
- case '++':
3839
- out.push(...number(1), [ Opcodes.add ]);
3840
- break;
3842
+ out.push([ isGlobal ? Opcodes.global_set : Opcodes.local_set, idx ]);
3843
+ if (decl.prefix && !valueUnused) out.push([ isGlobal ? Opcodes.global_get : Opcodes.local_get, idx ]);
3841
3844
 
3842
- case '--':
3843
- out.push(...number(1), [ Opcodes.sub ]);
3844
- break;
3845
+ return out;
3845
3846
  }
3846
3847
 
3847
- out.push([ isGlobal ? Opcodes.global_set : Opcodes.local_set, idx ]);
3848
- if (decl.prefix && !valueUnused) out.push([ isGlobal ? Opcodes.global_get : Opcodes.local_get, idx ]);
3848
+ // ++x: tmp = +x; x = tmp + 1
3849
+ // x++: tmp = +x; x = tmp + 1; tmp
3850
+ const tmp = localTmp(scope, '#updatetmp');
3851
+ addVarMetadata(scope, '#updatetmp', false, { type: TYPES.number });
3849
3852
 
3850
- return out;
3853
+ return [
3854
+ // tmp = +x
3855
+ // if postfix, tee to keep on stack as return value
3856
+ ...generate(scope, {
3857
+ type: 'UnaryExpression',
3858
+ operator: '+',
3859
+ prefix: true,
3860
+ argument: decl.argument
3861
+ }),
3862
+ [ decl.prefix ? Opcodes.local_set : Opcodes.local_tee, tmp ],
3863
+
3864
+ // x = tmp + 1
3865
+ ...generate(scope, {
3866
+ type: 'AssignmentExpression',
3867
+ operator: '=',
3868
+ left: decl.argument,
3869
+ right: {
3870
+ type: 'BinaryExpression',
3871
+ operator: decl.operator[0],
3872
+ left: { type: 'Identifier', name: '#updatetmp' },
3873
+ right: { type: 'Literal', value: 1 }
3874
+ }
3875
+ }),
3876
+ ...(decl.prefix ? [] : [ [ Opcodes.drop ] ])
3877
+ ];
3851
3878
  };
3852
3879
 
3853
3880
  const generateIf = (scope, decl) => {
package/compiler/wrap.js CHANGED
@@ -320,7 +320,7 @@ ${flags & 0b0001 ? ` get func idx: ${get}
320
320
  const err = new (globalThis[TYPE_NAMES[type]] ?? Error)(obj.message);
321
321
 
322
322
  err.name = obj.name;
323
- err.stack = `${TYPE_NAMES[type]}: ${obj.message}`;
323
+ err.stack = `${obj.name}: ${obj.message}`;
324
324
  return err;
325
325
  }
326
326
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "porffor",
3
3
  "description": "a basic experimental wip aot optimizing js -> wasm engine/compiler/runtime in js",
4
- "version": "0.42.5+a3af054f9",
4
+ "version": "0.42.7+5472762e8",
5
5
  "author": "CanadaHonk",
6
6
  "license": "MIT",
7
7
  "scripts": {},
package/r.js ADDED
@@ -0,0 +1,4 @@
1
+ const foo = (a, b, c, d) => console.log(a, b, c, d);
2
+
3
+ const arr = [ 7, 8, 9 ];
4
+ foo(1, ...arr);
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.42.5+a3af054f9';
3
+ globalThis.version = '0.42.7+5472762e8';
4
4
 
5
5
  // deno compat
6
6
  if (typeof process === 'undefined' && typeof Deno !== 'undefined') {
package/runner/repl.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { TYPE_NAMES } from '../compiler/types.js';
2
2
  import compile from '../compiler/wrap.js';
3
+ import parse from '../compiler/parse.js';
3
4
 
4
5
  import util from 'node:util';
5
6
 
@@ -83,7 +84,13 @@ const run = (source, _context, _filename, callback, run = true) => {
83
84
  // hack: print "secret" before latest code ran to only enable printing for new code
84
85
 
85
86
  source = source.trim();
86
- if (source.startsWith('{') && source.endsWith('}')) source = '(' + source + ')';
87
+ if (source.startsWith('{') && source.endsWith('}')) {
88
+ const wrapped = '(' + source + ')';
89
+ try {
90
+ parse(wrapped);
91
+ source = wrapped;
92
+ } catch {}
93
+ }
87
94
 
88
95
  let toRun = (prev ? (prev + `;\nprint(-0x1337);\n`) : '') + source;
89
96