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.
@@ -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) return internalThrow(scope, 'ReferenceError', `${unhackName(name)} is not defined`, true);
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
- const checkType = TYPES[rightName.toLowerCase()];
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
- if ([TYPES.number, TYPES.boolean, TYPES.string].includes(checkType)) {
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 number(TYPES.undefined, Valtype.i32);
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 number(TYPES.undefined, Valtype.i32);
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 number(TYPES.undefined, Valtype.i32);
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) continue;
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
- const tmp = localTmp(scope, '#switch_disc');
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
- const out = typeSwitch(scope,
4578
- getNodeType(scope, decl.discriminant.arguments[0]),
4579
- () => {
4580
- const bc = [];
4581
- let types = [];
4582
- for (const [ type, consequent ] of cases) {
4583
- types.push(type);
4584
-
4585
- if (consequent.length !== 0) {
4586
- bc.push([ types, () => generate(scope, { type: 'BlockStatement', body: consequent }) ]);
4587
- types = [];
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
- return bc;
4592
- }, Blocktype.void, true);
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
- [ Opcodes.local_get, tmp ],
4617
- ...generate(scope, x.test),
4618
- [ Opcodes.eq ],
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
- const exceptionMode = Prefs.exceptionMode ?? 'stack';
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, tags[0].idx ]
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, tags[0].idx ]
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
- // todo: wrap in try and reject thrown value once supported
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') {
@@ -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', '--exception-mode=lut'];
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
- if (y[0] === Opcodes.i32_const && n[0] === Opcodes.throw) {
157
- const id = read_signedLEB128(y.slice(1));
158
- y.splice(0, 10, 'throw', exceptions[id].constructor, exceptions[id].message);
159
-
160
- // remove throw inst
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
  };
@@ -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('=')[1].split(',').reduce((acc, x) => { acc[x.toLowerCase()] = true; return acc; }, {});
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 constr = globalThis[TYPE_NAMES[type]];
318
- const err = new constr(obj.message);
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
@@ -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.39.1+bd1e18648",
4
+ "version": "0.39.3+c13e1a532",
5
5
  "author": "CanadaHonk",
6
6
  "license": "MIT",
7
7
  "scripts": {},
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.39.1+bd1e18648';
3
+ globalThis.version = '0.39.3+c13e1a532';
4
4
 
5
5
  // deno compat
6
6
  if (typeof process === 'undefined' && typeof Deno !== 'undefined') {
package/runner/repl.js CHANGED
@@ -111,7 +111,7 @@ const run = (source, _context, _filename, callback, run = true) => {
111
111
 
112
112
  prev = prev + ';\n' + source.trim();
113
113
  } catch (e) {
114
- console.log('Uncaught', e.stack);
114
+ console.log('Uncaught', e.stack ? e.stack : e);
115
115
  }
116
116
 
117
117
  callback();