porffor 0.59.8 → 0.59.9

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.
@@ -553,9 +553,7 @@ const lookup = (scope, name, failEarly = false) => {
553
553
 
554
554
  if (!Object.hasOwn(funcIndex, name) && Object.hasOwn(builtinFuncs, name)) {
555
555
  includeBuiltin(scope, name);
556
- }
557
-
558
- if (Object.hasOwn(internalConstrs, name)) {
556
+ } else if (Object.hasOwn(internalConstrs, name)) {
559
557
  // todo: return an actual something
560
558
  return [ number(1) ];
561
559
  }
@@ -892,7 +890,21 @@ const performLogicOp = (scope, op, left, right, leftType, rightType) => {
892
890
  ];
893
891
  };
894
892
 
895
- const concatStrings = (scope, left, right, leftType, rightType) => [
893
+ const concatStrings = (scope, left, right, leftType, rightType) => ((knownType(scope, leftType) | TYPE_FLAGS.parity) === TYPES.bytestring && (knownType(scope, rightType) | TYPE_FLAGS.parity) === TYPES.bytestring) ? [
894
+ // known types, use strcat direct
895
+ ...left,
896
+ Opcodes.i32_to_u,
897
+ ...leftType,
898
+
899
+ ...right,
900
+ Opcodes.i32_to_u,
901
+ ...rightType,
902
+
903
+ [ Opcodes.call, includeBuiltin(scope, '__Porffor_strcat').index ],
904
+ ...setLastType(scope),
905
+ Opcodes.i32_from_u
906
+ ] : [
907
+ // unknown types, check if need to coerce
896
908
  ...left,
897
909
  ...(valtypeBinary === Valtype.i32 ? [ [ Opcodes.f64_convert_i32_s ] ] : []),
898
910
  ...leftType,
@@ -1528,8 +1540,9 @@ const generateLogicExp = (scope, decl) =>
1528
1540
  performLogicOp(scope, decl.operator, generate(scope, decl.left), generate(scope, decl.right), getNodeType(scope, decl.left), getNodeType(scope, decl.right));
1529
1541
 
1530
1542
  const getInferred = (scope, name, global = false) => {
1543
+ const isConst = getVarMetadata(scope, name, global)?.kind === 'const';
1531
1544
  if (global) {
1532
- if (globalInfer.has(name) && inferLoopPrev.length === 0) return globalInfer.get(name);
1545
+ if (globalInfer.has(name) && (isConst || inferLoopPrev.length === 0)) return globalInfer.get(name);
1533
1546
  } else if (scope.inferTree) {
1534
1547
  for (let i = scope.inferTree.length - 1; i >= 0; i--) {
1535
1548
  const x = scope.inferTree[i];
@@ -1541,11 +1554,12 @@ const getInferred = (scope, name, global = false) => {
1541
1554
  };
1542
1555
 
1543
1556
  const setInferred = (scope, name, type, global = false) => {
1557
+ const isConst = getVarMetadata(scope, name, global)?.kind === 'const';
1544
1558
  scope.inferTree ??= [];
1545
1559
 
1546
1560
  if (global) {
1547
1561
  // set inferred type in global if not already and not in a loop, else make it null
1548
- globalInfer.set(name, globalInfer.has(name) || inferLoopPrev.length > 0 ? null : type);
1562
+ globalInfer.set(name, globalInfer.has(name) || (!isConst && inferLoopPrev.length > 0) ? null : type);
1549
1563
  } else {
1550
1564
  // set inferred type in top
1551
1565
  const top = scope.inferTree.at(-1);
@@ -1771,6 +1785,8 @@ const getNodeType = (scope, node) => {
1771
1785
  (knownLeft === TYPES.stringobject || knownRight === TYPES.stringobject)
1772
1786
  ) return TYPES.string;
1773
1787
 
1788
+ if (knownLeft === TYPES.bytestring && knownRight === TYPES.bytestring) return TYPES.bytestring;
1789
+
1774
1790
  // guess bytestring, could really be bytestring or string
1775
1791
  if (knownLeft === TYPES.bytestring || knownRight === TYPES.bytestring)
1776
1792
  guess = TYPES.bytestring;
@@ -2397,6 +2413,9 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
2397
2413
  let idx;
2398
2414
  if (decl._funcIdx) {
2399
2415
  idx = decl._funcIdx;
2416
+ } else if (Object.hasOwn(internalConstrs, name) && !decl._noInternalConstr) {
2417
+ if (decl._new && internalConstrs[name].notConstr) return internalThrow(scope, 'TypeError', `${unhackName(name)} is not a constructor`, true);
2418
+ return internalConstrs[name].generate(scope, decl, _global, _name);
2400
2419
  } else if (Object.hasOwn(funcIndex, name)) {
2401
2420
  idx = funcIndex[name];
2402
2421
  } else if (scope.name === name) {
@@ -2410,9 +2429,6 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
2410
2429
 
2411
2430
  includeBuiltin(scope, name);
2412
2431
  idx = funcIndex[name];
2413
- } else if (Object.hasOwn(internalConstrs, name)) {
2414
- if (decl._new && internalConstrs[name].notConstr) return internalThrow(scope, 'TypeError', `${unhackName(name)} is not a constructor`, true);
2415
- return internalConstrs[name].generate(scope, decl, _global, _name);
2416
2432
  } else if (!decl._new && name && name.startsWith('__Porffor_wasm_')) {
2417
2433
  const wasmOps = {
2418
2434
  // pointer, align, offset
@@ -2559,7 +2575,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
2559
2575
  ...setLastType(scope)
2560
2576
  ],
2561
2577
 
2562
- default: decl.optional ? withType(scope, [ number(UNDEFINED, Valtype.f64) ], TYPES.undefined)
2578
+ default: () => decl.optional ? withType(scope, [ number(UNDEFINED, Valtype.f64) ], TYPES.undefined)
2563
2579
  : internalThrow(scope, 'TypeError', `${unhackName(name)} is not a function`, Valtype.f64)
2564
2580
  }, Valtype.f64)
2565
2581
  ];
@@ -3075,6 +3091,11 @@ const allocVar = (scope, name, global = false, type = true, redecl = false, i32
3075
3091
  return idx;
3076
3092
  };
3077
3093
 
3094
+ const getVarMetadata = (scope, name, global = false) => {
3095
+ const target = global ? globals : scope.locals;
3096
+ return target[name]?.metadata;
3097
+ };
3098
+
3078
3099
  const setVarMetadata = (scope, name, global = false, metadata = {}) => {
3079
3100
  const target = global ? globals : scope.locals;
3080
3101
  target[name].metadata = metadata;
@@ -4041,9 +4062,8 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
4041
4062
  }),
4042
4063
  } : {}),
4043
4064
 
4044
- [TYPES.undefined]: internalThrow(scope, 'TypeError', 'Cannot set property of undefined', !valueUnused),
4065
+ [TYPES.undefined]: () => internalThrow(scope, 'TypeError', 'Cannot set property of undefined', !valueUnused),
4045
4066
 
4046
- // default: internalThrow(scope, 'TypeError', `Cannot assign member with this type`)
4047
4067
  default: () => [
4048
4068
  objectGet,
4049
4069
  Opcodes.i32_to,
@@ -4948,7 +4968,7 @@ const generateForOf = (scope, decl) => {
4948
4968
  setVar = generateVarDstr(scope, 'var', decl.left, { type: 'Wasm', wasm: nextWasm }, undefined, true);
4949
4969
  } else {
4950
4970
  // todo: verify this is correct
4951
- const global = scope.name === '#main' && decl.left.kind === 'var';
4971
+ const global = scope.name === '#main';
4952
4972
  setVar = generateVarDstr(scope, decl.left.kind, decl.left?.declarations?.[0]?.id ?? decl.left, { type: 'Wasm', wasm: nextWasm }, undefined, global);
4953
4973
  }
4954
4974
 
@@ -5015,7 +5035,7 @@ const generateForIn = (scope, decl) => {
5015
5035
  setVar = generateVarDstr(scope, 'var', decl.left, { type: 'Identifier', name: tmpName }, undefined, true);
5016
5036
  } else {
5017
5037
  // todo: verify this is correct
5018
- const global = scope.name === '#main' && decl.left.kind === 'var';
5038
+ const global = scope.name === '#main';
5019
5039
  setVar = generateVarDstr(scope, decl.left.kind, decl.left?.declarations?.[0]?.id ?? decl.left, { type: 'Identifier', name: tmpName }, undefined, global);
5020
5040
  }
5021
5041
 
@@ -5512,14 +5532,13 @@ const makeData = (scope, elements, page = null, itemType = 'i8') => {
5512
5532
  return { idx, size: bytes.length };
5513
5533
  };
5514
5534
 
5515
- const printStaticStr = str => {
5535
+ const printStaticStr = (scope, str) => {
5536
+ scope.usesImports = true;
5516
5537
  const out = [];
5517
5538
 
5518
5539
  for (let i = 0; i < str.length; i++) {
5519
5540
  out.push(
5520
- // number(str.charCodeAt(i)),
5521
- number(str.charCodeAt(i), Valtype.i32),
5522
- [ Opcodes.f64_convert_i32_u ],
5541
+ number(str.charCodeAt(i)),
5523
5542
  [ Opcodes.call, importedFuncs.printChar ]
5524
5543
  );
5525
5544
  }
@@ -6103,7 +6122,7 @@ const generateMember = (scope, decl, _global, _name) => {
6103
6122
  })
6104
6123
  } : {}),
6105
6124
 
6106
- [TYPES.undefined]: internalThrow(scope, 'TypeError', `Cannot read property of undefined`, true),
6125
+ [TYPES.undefined]: () => internalThrow(scope, 'TypeError', `Cannot read property of undefined`, true),
6107
6126
 
6108
6127
  default: () => [
6109
6128
  ...(coctc > 0 && known === TYPES.object ? [
@@ -7197,10 +7216,8 @@ const internalConstrs = {
7197
7216
 
7198
7217
  __Porffor_printStatic: {
7199
7218
  generate: (scope, decl) => {
7200
- scope.usesImports = true;
7201
-
7202
7219
  const str = decl.arguments[0].value;
7203
- const out = printStaticStr(str);
7220
+ const out = printStaticStr(scope, str);
7204
7221
  out.push(number(UNDEFINED));
7205
7222
  return out;
7206
7223
  },
@@ -7248,6 +7265,46 @@ const internalConstrs = {
7248
7265
  }),
7249
7266
  notConstr: true,
7250
7267
  length: 1
7268
+ },
7269
+
7270
+ __console_log: {
7271
+ // compile-time aware console.log to optimize fast paths
7272
+ // todo: this breaks console.group, etc - disable this if those are used but edge case for now
7273
+ generate: (scope, decl) => {
7274
+ const slow = () => {
7275
+ decl._noInternalConstr = true;
7276
+ return generate(scope, decl);
7277
+ };
7278
+ const fast = name => {
7279
+ return [
7280
+ ...generate(scope, {
7281
+ ...decl,
7282
+ callee: {
7283
+ type: 'Identifier',
7284
+ name
7285
+ }
7286
+ }),
7287
+ ...printStaticStr(scope, '\n')
7288
+ ];
7289
+ };
7290
+ if (decl.arguments.length !== 1) return slow();
7291
+
7292
+ generate(scope, decl.arguments[0]); // generate first to get accurate type
7293
+ const type = knownTypeWithGuess(scope, getNodeType(scope, decl.arguments[0]));
7294
+
7295
+ // if we know the type skip the entire print logic, use type's func directly
7296
+ if (type === TYPES.string || type === TYPES.bytestring) {
7297
+ return fast('__Porffor_printString');
7298
+ } else if (type === TYPES.number) {
7299
+ return fast('print');
7300
+ }
7301
+
7302
+ // one arg, skip most of console to avoid rest arg etc
7303
+ return fast('__Porffor_consolePrint');
7304
+ },
7305
+ type: TYPES.undefined,
7306
+ notConstr: true,
7307
+ length: 0
7251
7308
  }
7252
7309
  };
7253
7310
 
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.59.8",
4
+ "version": "0.59.9",
5
5
  "author": "Oliver Medhurst <honk@goose.icu>",
6
6
  "license": "MIT",
7
7
  "scripts": {},
package/runtime/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import fs from 'node:fs';
3
- globalThis.version = '0.59.8';
3
+ globalThis.version = '0.59.9';
4
4
 
5
5
  // deno compat
6
6
  if (typeof process === 'undefined' && typeof Deno !== 'undefined') {