porffor 0.50.18 → 0.50.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.
@@ -219,7 +219,7 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
219
219
  return cacheAst(decl, generateFunc(scope, decl)[1]);
220
220
 
221
221
  case 'BlockStatement':
222
- return cacheAst(decl, generateCode(scope, decl));
222
+ return cacheAst(decl, generateBlock(scope, decl));
223
223
 
224
224
  case 'ReturnStatement':
225
225
  return cacheAst(decl, generateReturn(scope, decl));
@@ -304,7 +304,10 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
304
304
  return cacheAst(decl, generateTry(scope, decl));
305
305
 
306
306
  case 'DebuggerStatement':
307
- return cacheAst(decl, [[ Opcodes.call, importedFuncs.debugger ]]);
307
+ return cacheAst(decl, [
308
+ // [ Opcodes.call, importedFuncs.debugger ],
309
+ number(UNDEFINED)
310
+ ]);
308
311
 
309
312
  case 'ArrayExpression':
310
313
  return cacheAst(decl, generateArray(scope, decl, global, name, globalThis.precompile));
@@ -329,10 +332,10 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
329
332
  return cacheAst(decl, generateTemplate(scope, decl));
330
333
 
331
334
  case 'TaggedTemplateExpression':
332
- return cacheAst(decl, generateTaggedTemplate(scope, decl, global, name));
335
+ return cacheAst(decl, generateTaggedTemplate(scope, decl, global, name, valueUnused));
333
336
 
334
337
  case 'ExportNamedDeclaration':
335
- if (!decl.declaration) return todo(scope, 'unsupported export declaration');
338
+ if (!decl.declaration) return todo(scope, 'unsupported export declaration', true);
336
339
 
337
340
  const funcsBefore = funcs.map(x => x.name);
338
341
  generate(scope, decl.declaration);
@@ -349,16 +352,16 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
349
352
  }
350
353
  }
351
354
 
352
- return cacheAst(decl, []);
355
+ return cacheAst(decl, [ number(UNDEFINED) ]);
353
356
 
354
357
  default:
355
358
  // ignore typescript nodes
356
359
  if (decl.type.startsWith('TS') ||
357
360
  decl.type === 'ImportDeclaration' && decl.importKind === 'type') {
358
- return cacheAst(decl, []);
361
+ return cacheAst(decl, [ number(UNDEFINED) ]);
359
362
  }
360
363
 
361
- return cacheAst(decl, todo(scope, `no generation for ${decl.type}!`, !decl.type.endsWith('Statement') && !decl.type.endsWith('Declaration')));
364
+ return cacheAst(decl, todo(scope, `no generation for ${decl.type}!`, true));
362
365
  }
363
366
  };
364
367
 
@@ -1110,7 +1113,7 @@ const generateBinaryExp = (scope, decl, _global, _name) => {
1110
1113
  let checkType = TYPES[rightName.toLowerCase()];
1111
1114
  if (checkType != null && rightName === TYPE_NAMES[checkType] && !rightName.endsWith('Error')) {
1112
1115
  const out = generate(scope, decl.left);
1113
- disposeLeftover(out);
1116
+ out.push([ Opcodes.drop ]);
1114
1117
 
1115
1118
  // switch primitive types to primitive object types
1116
1119
  if (checkType === TYPES.number) checkType = TYPES.numberobject;
@@ -1532,6 +1535,7 @@ const getNodeType = (scope, node) => {
1532
1535
  }
1533
1536
 
1534
1537
  if (isFuncType(node.type)) {
1538
+ if (node.type.endsWith('Declaration')) return TYPES.undefined;
1535
1539
  return TYPES.function;
1536
1540
  }
1537
1541
 
@@ -1711,6 +1715,18 @@ const getNodeType = (scope, node) => {
1711
1715
  return getNodeType(scope, node.expression);
1712
1716
  }
1713
1717
 
1718
+ if (node.type === 'BlockStatement') {
1719
+ return getNodeType(scope, getLastNode(node.body));
1720
+ }
1721
+
1722
+ if (node.type === 'LabeledStatement') {
1723
+ return getNodeType(scope, node.body);
1724
+ }
1725
+
1726
+ if (node.type.endsWith('Statement') || node.type.endsWith('Declaration')) {
1727
+ return TYPES.undefined;
1728
+ }
1729
+
1714
1730
  return getLastType(scope);
1715
1731
  })();
1716
1732
 
@@ -1745,68 +1761,13 @@ const generateLiteral = (scope, decl, global, name) => {
1745
1761
  }
1746
1762
  };
1747
1763
 
1748
- const countLeftover = wasm => {
1749
- let count = 0, depth = 0;
1750
-
1751
- for (let i = 0; i < wasm.length; i++) {
1752
- const inst = wasm[i];
1753
- if (depth === 0 && inst[0] == null) {
1754
- if (typeof inst[1] === 'function' && typeof inst[2] === 'number') count += inst[2];
1755
- continue;
1756
- }
1757
-
1758
- if (depth === 0 && (inst[0] === Opcodes.if || inst[0] === Opcodes.block || inst[0] === Opcodes.loop)) {
1759
- if (inst[0] === Opcodes.if) count--;
1760
- if (inst[1] !== Blocktype.void) count++;
1761
- }
1762
- if ([Opcodes.if, Opcodes.try, Opcodes.loop, Opcodes.block].includes(inst[0])) depth++;
1763
- if (inst[0] === Opcodes.end) depth--;
1764
-
1765
- if (depth === 0)
1766
- if ([Opcodes.drop, Opcodes.local_set, Opcodes.global_set].includes(inst[0])) count--;
1767
- else if ([Opcodes.i32_eqz, Opcodes.i64_eqz, Opcodes.f64_abs, Opcodes.f64_ceil, Opcodes.f64_floor, Opcodes.f64_trunc, Opcodes.f64_nearest, Opcodes.f64_sqrt, Opcodes.local_tee, Opcodes.i32_wrap_i64, Opcodes.i64_extend_i32_s, Opcodes.i64_extend_i32_u, Opcodes.f32_demote_f64, Opcodes.f64_promote_f32, Opcodes.f64_convert_i32_s, Opcodes.f64_convert_i32_u, Opcodes.i32_clz, Opcodes.i32_ctz, Opcodes.i32_popcnt, Opcodes.f64_neg, Opcodes.end, Opcodes.i32_trunc_sat_f64_s[0], Opcodes.i32x4_extract_lane, Opcodes.i16x8_extract_lane, Opcodes.i32_load, Opcodes.i64_load, Opcodes.f64_load, Opcodes.f32_load, Opcodes.v128_load, Opcodes.i32_load16_u, Opcodes.i32_load16_s, Opcodes.i32_load8_u, Opcodes.i32_load8_s, Opcodes.memory_grow].includes(inst[0]) && (inst[0] !== 0xfc || inst[1] < 0x04)) {}
1768
- else if ([Opcodes.local_get, Opcodes.global_get, Opcodes.f64_const, Opcodes.i32_const, Opcodes.i64_const, Opcodes.v128_const, Opcodes.memory_size].includes(inst[0])) count++;
1769
- else if ([Opcodes.i32_store, Opcodes.i64_store, Opcodes.f64_store, Opcodes.f32_store, Opcodes.i32_store16, Opcodes.i32_store8, Opcodes.select].includes(inst[0])) count -= 2;
1770
- else if (inst[0] === Opcodes.memory_copy[0] && (inst[1] === Opcodes.memory_copy[1] || inst[1] === Opcodes.memory_init[1])) count -= 3;
1771
- else if (inst[0] === Opcodes.return) count = 0;
1772
- else if (inst[0] === Opcodes.catch) count += 2;
1773
- else if (inst[0] === Opcodes.throw) {
1774
- count--;
1775
- if ((Prefs.exceptionMode ?? 'stack') === 'stack' || (globalThis.precompile && inst[1] === 1)) count--;
1776
- } else if (inst[0] === Opcodes.call) {
1777
- if (inst[1] < importedFuncs.length) {
1778
- const func = importedFuncs[inst[1]];
1779
- count = count - func.params + func.returns;
1780
- } else {
1781
- const func = funcByIndex(inst[1]);
1782
- count = count - func.params.length + func.returns.length;
1783
- }
1784
- } else if (inst[0] === Opcodes.call_indirect) {
1785
- count--; // funcidx
1786
- count -= inst[1] * 2; // params * 2 (typed)
1787
- count += 2; // fixed return (value, type)
1788
- } else count--;
1789
-
1790
- // console.log(count, depth, decompile([ inst ], undefined, undefined, undefined, undefined, undefined, funcs).slice(0, -1));
1791
- }
1792
-
1793
- return count;
1794
- };
1795
-
1796
- const disposeLeftover = wasm => {
1797
- const leftover = countLeftover(wasm);
1798
- for (let i = 0; i < leftover; i++) wasm.push([ Opcodes.drop ]);
1799
- };
1800
-
1801
1764
  const generateExp = (scope, decl) => {
1802
1765
  if (decl.directive === 'use strict') {
1803
1766
  scope.strict = true;
1767
+ return [ number(UNDEFINED) ];
1804
1768
  }
1805
1769
 
1806
- const out = generate(scope, decl.expression, undefined, undefined, !scope.inEval);
1807
- disposeLeftover(out);
1808
-
1809
- return out;
1770
+ return generate(scope, decl.expression, undefined, undefined, !scope.inEval);
1810
1771
  };
1811
1772
 
1812
1773
  const generateSequence = (scope, decl) => {
@@ -1814,8 +1775,8 @@ const generateSequence = (scope, decl) => {
1814
1775
 
1815
1776
  const exprs = decl.expressions;
1816
1777
  for (let i = 0; i < exprs.length; i++) {
1817
- if (i > 0) disposeLeftover(out);
1818
1778
  out.push(...generate(scope, exprs[i]));
1779
+ if (i !== exprs.length - 1) out.push([ Opcodes.drop ]);
1819
1780
  }
1820
1781
 
1821
1782
  return out;
@@ -1932,19 +1893,16 @@ const setObjProp = (obj, prop, value) => {
1932
1893
  };
1933
1894
 
1934
1895
  return objectHack({
1935
- type: 'ExpressionStatement',
1936
- expression: {
1937
- type: 'AssignmentExpression',
1938
- operator: '=',
1939
- left: {
1940
- type: 'MemberExpression',
1941
- object: obj,
1942
- property: prop,
1943
- computed: false,
1944
- optional: false
1945
- },
1946
- right: value
1947
- }
1896
+ type: 'AssignmentExpression',
1897
+ operator: '=',
1898
+ left: {
1899
+ type: 'MemberExpression',
1900
+ object: obj,
1901
+ property: prop,
1902
+ computed: false,
1903
+ optional: false
1904
+ },
1905
+ right: value
1948
1906
  });
1949
1907
  };
1950
1908
 
@@ -2058,6 +2016,14 @@ const createThisArg = (scope, decl) => {
2058
2016
  }
2059
2017
  };
2060
2018
 
2019
+ const isEmptyNode = x => x && (x.type === 'EmptyStatement' || (x.type === 'BlockStatement' && x.body.length === 0));
2020
+ const getLastNode = body => {
2021
+ let offset = 1, node = body[body.length - offset];
2022
+ while (isEmptyNode(node)) node = body[body.length - ++offset];
2023
+
2024
+ return node ?? { type: 'EmptyStatement' };
2025
+ };
2026
+
2061
2027
  const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
2062
2028
  if (decl.type === 'NewExpression') decl._new = true;
2063
2029
 
@@ -2095,17 +2061,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
2095
2061
  });
2096
2062
  scope.inEval = false;
2097
2063
 
2098
- const lastInst = getLastInst(out);
2099
- if (lastInst && lastInst[0] === Opcodes.drop) {
2100
- out.splice(out.length - 1, 1);
2101
-
2102
- const finalStatement = parsed.body[parsed.body.length - 1];
2103
- out.push(...setLastType(scope, getNodeType(scope, finalStatement)));
2104
- } else if (countLeftover(out) === 0) {
2105
- out.push(number(UNDEFINED));
2106
- out.push(...setLastType(scope, TYPES.undefined));
2107
- }
2108
-
2064
+ out.push(...setLastType(scope, getNodeType(scope, getLastNode(parsed.body))));
2109
2065
  return out;
2110
2066
  }
2111
2067
  }
@@ -2406,6 +2362,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
2406
2362
  // TODO: error better
2407
2363
  default: internalThrow(scope, 'TypeError', `'${protoName}' proto func tried to be called on a type without an impl`, !unusedValue)
2408
2364
  }, unusedValue ? Blocktype.void : valtypeBinary),
2365
+ ...(unusedValue ? [ number(UNDEFINED) ] : [])
2409
2366
  ];
2410
2367
  }
2411
2368
 
@@ -2481,26 +2438,26 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
2481
2438
  // pointer, align, offset
2482
2439
  i32_load: { imms: 2, args: [ true ], returns: 1 },
2483
2440
  // pointer, value, align, offset
2484
- i32_store: { imms: 2, args: [ true, true ], returns: 0 },
2441
+ i32_store: { imms: 2, args: [ true, true ], returns: 0, addValue: true },
2485
2442
  // pointer, align, offset
2486
2443
  i32_load8_u: { imms: 2, args: [ true ], returns: 1 },
2487
2444
  // pointer, value, align, offset
2488
- i32_store8: { imms: 2, args: [ true, true ], returns: 0 },
2445
+ i32_store8: { imms: 2, args: [ true, true ], returns: 0, addValue: true },
2489
2446
  // pointer, align, offset
2490
2447
  i32_load16_u: { imms: 2, args: [ true ], returns: 1 },
2491
2448
  // pointer, value, align, offset
2492
- i32_store16: { imms: 2, args: [ true, true ], returns: 0 },
2449
+ i32_store16: { imms: 2, args: [ true, true ], returns: 0, addValue: true },
2493
2450
 
2494
2451
  // pointer, align, offset
2495
2452
  f64_load: { imms: 2, args: [ true ], returns: 0 }, // 0 due to not i32
2496
2453
  // pointer, value, align, offset
2497
- f64_store: { imms: 2, args: [ true, false ], returns: 0 },
2454
+ f64_store: { imms: 2, args: [ true, false ], returns: 0, addValue: true },
2498
2455
 
2499
2456
  // value
2500
2457
  i32_const: { imms: 1, args: [], returns: 0 },
2501
2458
 
2502
2459
  // dst, src, size, _, _
2503
- memory_copy: { imms: 2, args: [ true, true, true ], returns: 0 }
2460
+ memory_copy: { imms: 2, args: [ true, true, true ], returns: 0, addValue: true }
2504
2461
  };
2505
2462
 
2506
2463
  const opName = name.slice('__Porffor_wasm_'.length);
@@ -2529,7 +2486,8 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
2529
2486
  return [
2530
2487
  ...argOut,
2531
2488
  [ ...opcode, ...imms ],
2532
- ...(new Array(op.returns).fill(Opcodes.i32_from))
2489
+ ...(new Array(op.returns).fill(Opcodes.i32_from)),
2490
+ ...(op.addValue ? [ number(UNDEFINED) ] : [])
2533
2491
  ];
2534
2492
  }
2535
2493
  } else {
@@ -2732,6 +2690,11 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
2732
2690
  out.push(Opcodes.i32_trunc_sat_f64_s);
2733
2691
  }
2734
2692
 
2693
+ if ((builtinFuncs[name] && builtinFuncs[name].returns?.length === 0) ||
2694
+ (importedFuncs[name] != null && importedFuncs[importedFuncs[name]]?.returns === 0)) {
2695
+ out.push(number(UNDEFINED));
2696
+ }
2697
+
2735
2698
  return out;
2736
2699
  };
2737
2700
 
@@ -3060,6 +3023,7 @@ const typeSwitch = (scope, type, bc, returns = valtypeBinary, fallthrough = fals
3060
3023
 
3061
3024
  out.push([ Opcodes.end ]);
3062
3025
 
3026
+ // todo: sometimes gets stuck?
3063
3027
  if (depth.at(-1) === 'typeswitch') {
3064
3028
  depth.pop();
3065
3029
  }
@@ -3091,15 +3055,18 @@ const typeIsNotOneOf = (type, types, valtype = Valtype.i32) => {
3091
3055
  return out;
3092
3056
  };
3093
3057
 
3094
- const allocVar = (scope, name, global = false, type = true) => {
3058
+ const allocVar = (scope, name, global = false, type = true, redecl = false) => {
3095
3059
  const target = global ? globals : scope.locals;
3096
3060
 
3097
3061
  // already declared
3098
3062
  if (Object.hasOwn(target, name)) {
3099
- // parser should catch this but sanity check anyway
3100
- // if (decl.kind !== 'var') return internalThrow(scope, 'SyntaxError', `Identifier '${unhackName(name)}' has already been declared`);
3101
-
3102
- return target[name].idx;
3063
+ if (redecl) {
3064
+ // force change old local name(s)
3065
+ target['#redecl_' + name + uniqId()] = target[name];
3066
+ if (type) target['#redecl_' + name + '#type' + uniqId()] = target[name + '#type'];
3067
+ } else {
3068
+ return target[name].idx;
3069
+ }
3103
3070
  }
3104
3071
 
3105
3072
  let idx = global ? globals['#ind']++ : scope.localInd++;
@@ -3524,6 +3491,7 @@ const generateVar = (scope, decl) => {
3524
3491
  out = out.concat(generateVarDstr(scope, decl.kind, x.id, x.init, undefined, global));
3525
3492
  }
3526
3493
 
3494
+ out.push(number(UNDEFINED));
3527
3495
  return out;
3528
3496
  };
3529
3497
 
@@ -3704,7 +3672,7 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
3704
3672
  ...decl,
3705
3673
  _internalAssign: true
3706
3674
  });
3707
- if (valueUnused) disposeLeftover(slow);
3675
+ if (valueUnused) slow.push([ Opcodes.drop ]);
3708
3676
 
3709
3677
  return [
3710
3678
  ...out,
@@ -4053,7 +4021,6 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
4053
4021
  [ isGlobal ? Opcodes.global_set : Opcodes.local_set, local.idx ],
4054
4022
  [ isGlobal ? Opcodes.global_get : Opcodes.local_get, local.idx ]
4055
4023
  ], getType(scope, name), getNodeType(scope, decl.right), isGlobal, name, true),
4056
- [ isGlobal ? Opcodes.global_get : Opcodes.local_get, local.idx ],
4057
4024
 
4058
4025
  ...setType(scope, name, getLastType(scope), true)
4059
4026
  ];
@@ -4148,7 +4115,7 @@ const generateUnary = (scope, decl) => {
4148
4115
  case 'void': {
4149
4116
  // drop current expression value after running, give undefined
4150
4117
  const out = generate(scope, decl.argument);
4151
- disposeLeftover(out);
4118
+ out.push([ Opcodes.drop ]);
4152
4119
 
4153
4120
  out.push(number(UNDEFINED));
4154
4121
  return out;
@@ -4215,7 +4182,7 @@ const generateUnary = (scope, decl) => {
4215
4182
  }
4216
4183
 
4217
4184
  const out = toGenerate ? generate(scope, decl.argument) : [];
4218
- disposeLeftover(out);
4185
+ if (toGenerate) out.push([ Opcodes.drop ]);
4219
4186
 
4220
4187
  out.push(number(toReturn ? 1 : 0));
4221
4188
  return out;
@@ -4229,7 +4196,7 @@ const generateUnary = (scope, decl) => {
4229
4196
  }
4230
4197
 
4231
4198
  const out = toGenerate ? generate(scope, decl.argument) : [];
4232
- disposeLeftover(out);
4199
+ if (toGenerate) out.push([ Opcodes.drop ]);
4233
4200
 
4234
4201
  out.push(...typeSwitch(scope, overrideType ?? getNodeType(scope, decl.argument), [
4235
4202
  [ TYPES.number, () => makeString(scope, 'number') ],
@@ -4308,8 +4275,7 @@ const generateUpdate = (scope, decl, _global, _name, valueUnused = false) => {
4308
4275
  right: { type: 'Literal', value: 1 }
4309
4276
  }
4310
4277
  }, _global, _name, valueUnused),
4311
- ...(decl.prefix || valueUnused ? [] : [ [ Opcodes.drop ] ]),
4312
- ...optional([ number(UNDEFINED) ], valueUnused)
4278
+ ...(decl.prefix || valueUnused ? [] : [ [ Opcodes.drop ] ])
4313
4279
  ];
4314
4280
  };
4315
4281
 
@@ -4343,7 +4309,10 @@ const inferLoopEnd = scope => {
4343
4309
  const generateIf = (scope, decl) => {
4344
4310
  if (globalThis.precompile && decl.test?.tag?.name === '__Porffor_comptime_flag') {
4345
4311
  const flag = decl.test.quasi.quasis[0].value.raw;
4346
- return [ [ null, 'comptime_flag', flag, decl.consequent, '#', Prefs ] ];
4312
+ return [
4313
+ [ null, 'comptime_flag', flag, decl.consequent, '#', Prefs ],
4314
+ number(UNDEFINED)
4315
+ ];
4347
4316
  }
4348
4317
 
4349
4318
  const out = truthy(scope, generate(scope, decl.test), getNodeType(scope, decl.test), false, true);
@@ -4351,22 +4320,26 @@ const generateIf = (scope, decl) => {
4351
4320
  depth.push('if');
4352
4321
  inferBranchStart(scope, decl.consequent);
4353
4322
 
4354
- const consOut = generate(scope, decl.consequent);
4355
- disposeLeftover(consOut);
4356
- out.push(...consOut);
4323
+ out.push(
4324
+ ...generate(scope, decl.consequent),
4325
+ [ Opcodes.drop ]
4326
+ );
4357
4327
 
4358
4328
  inferBranchEnd(scope);
4359
4329
  if (decl.alternate) {
4360
- out.push([ Opcodes.else ]);
4361
4330
  inferBranchStart(scope, decl.alternate);
4362
-
4363
- const altOut = generate(scope, decl.alternate);
4364
- disposeLeftover(altOut);
4365
- out.push(...altOut);
4331
+ out.push(
4332
+ [ Opcodes.else ],
4333
+ ...generate(scope, decl.alternate),
4334
+ [ Opcodes.drop ]
4335
+ );
4366
4336
  inferBranchEnd(scope);
4367
4337
  }
4368
4338
 
4369
- out.push([ Opcodes.end ]);
4339
+ out.push(
4340
+ [ Opcodes.end ],
4341
+ number(UNDEFINED)
4342
+ );
4370
4343
  depth.pop();
4371
4344
 
4372
4345
  return out;
@@ -4402,10 +4375,10 @@ let depth = [];
4402
4375
  const generateFor = (scope, decl) => {
4403
4376
  const out = [];
4404
4377
 
4405
- if (decl.init) {
4406
- out.push(...generate(scope, decl.init, false, undefined, true));
4407
- disposeLeftover(out);
4408
- }
4378
+ if (decl.init) out.push(
4379
+ ...generate(scope, decl.init, false, undefined, true),
4380
+ [ Opcodes.drop ]
4381
+ );
4409
4382
 
4410
4383
  inferLoopStart(scope, decl);
4411
4384
  out.push([ Opcodes.loop, Blocktype.void ]);
@@ -4414,19 +4387,31 @@ const generateFor = (scope, decl) => {
4414
4387
  if (decl.test) out.push(...generate(scope, decl.test), Opcodes.i32_to);
4415
4388
  else out.push(number(1, Valtype.i32));
4416
4389
 
4417
- out.push([ Opcodes.if, Blocktype.void ]);
4418
- depth.push('if');
4390
+ out.push(
4391
+ [ Opcodes.if, Blocktype.void ],
4392
+ [ Opcodes.block, Blocktype.void ]
4393
+ );
4394
+ depth.push('if', 'block');
4419
4395
 
4420
- out.push([ Opcodes.block, Blocktype.void ]);
4421
- depth.push('block');
4422
- out.push(...generate(scope, decl.body));
4423
- out.push([ Opcodes.end ]);
4396
+ out.push(
4397
+ ...generate(scope, decl.body),
4398
+ [ Opcodes.drop ],
4399
+ [ Opcodes.end ]
4400
+ );
4401
+ depth.pop();
4424
4402
 
4425
- if (decl.update) out.push(...generate(scope, decl.update, false, undefined, true));
4403
+ if (decl.update) out.push(
4404
+ ...generate(scope, decl.update, false, undefined, true),
4405
+ [ Opcodes.drop ]
4406
+ );
4426
4407
 
4427
- out.push([ Opcodes.br, 1 ]);
4428
- out.push([ Opcodes.end ], [ Opcodes.end ]);
4429
- depth.pop(); depth.pop(); depth.pop();
4408
+ out.push(
4409
+ [ Opcodes.br, 1 ],
4410
+ [ Opcodes.end ],
4411
+ [ Opcodes.end ],
4412
+ number(UNDEFINED)
4413
+ );
4414
+ depth.pop(); depth.pop();
4430
4415
 
4431
4416
  inferLoopEnd(scope);
4432
4417
  return out;
@@ -4439,14 +4424,21 @@ const generateWhile = (scope, decl) => {
4439
4424
  out.push([ Opcodes.loop, Blocktype.void ]);
4440
4425
  depth.push('while');
4441
4426
 
4442
- out.push(...generate(scope, decl.test));
4443
- out.push(Opcodes.i32_to, [ Opcodes.if, Blocktype.void ]);
4427
+ out.push(
4428
+ ...generate(scope, decl.test),
4429
+ Opcodes.i32_to,
4430
+ [ Opcodes.if, Blocktype.void ]
4431
+ );
4444
4432
  depth.push('if');
4445
4433
 
4446
- out.push(...generate(scope, decl.body));
4447
-
4448
- out.push([ Opcodes.br, 1 ]);
4449
- out.push([ Opcodes.end ], [ Opcodes.end ]);
4434
+ out.push(
4435
+ ...generate(scope, decl.body),
4436
+ [ Opcodes.drop ],
4437
+ [ Opcodes.br, 1 ],
4438
+ [ Opcodes.end ],
4439
+ [ Opcodes.end ],
4440
+ number(UNDEFINED)
4441
+ );
4450
4442
  depth.pop(); depth.pop();
4451
4443
 
4452
4444
  inferLoopEnd(scope);
@@ -4458,27 +4450,31 @@ const generateDoWhile = (scope, decl) => {
4458
4450
  inferLoopStart(scope, decl);
4459
4451
 
4460
4452
  out.push([ Opcodes.loop, Blocktype.void ]);
4461
- depth.push('dowhile');
4462
4453
 
4463
4454
  // block for break (includes all)
4464
4455
  out.push([ Opcodes.block, Blocktype.void ]);
4465
- depth.push('block');
4466
4456
 
4467
4457
  // block for continue
4468
4458
  // includes body but not test+loop so we can exit body at anytime
4469
4459
  // and still test+loop after
4470
4460
  out.push([ Opcodes.block, Blocktype.void ]);
4471
- depth.push('block');
4461
+ depth.push('dowhile', 'block', 'block');
4472
4462
 
4473
- out.push(...generate(scope, decl.body));
4474
-
4475
- out.push([ Opcodes.end ]);
4463
+ out.push(
4464
+ ...generate(scope, decl.body),
4465
+ [ Opcodes.drop ],
4466
+ [ Opcodes.end ]
4467
+ );
4476
4468
  depth.pop();
4477
4469
 
4478
- out.push(...generate(scope, decl.test), Opcodes.i32_to);
4479
- out.push([ Opcodes.br_if, 1 ]);
4480
-
4481
- out.push([ Opcodes.end ], [ Opcodes.end ]);
4470
+ out.push(
4471
+ ...generate(scope, decl.test),
4472
+ Opcodes.i32_to,
4473
+ [ Opcodes.br_if, 1 ],
4474
+ [ Opcodes.end ],
4475
+ [ Opcodes.end ],
4476
+ number(UNDEFINED)
4477
+ );
4482
4478
  depth.pop(); depth.pop();
4483
4479
 
4484
4480
  inferLoopEnd(scope);
@@ -4811,17 +4807,17 @@ const generateForOf = (scope, decl) => {
4811
4807
  }
4812
4808
 
4813
4809
  // next and set local
4814
- out.push(...setVar);
4815
-
4816
- // generate body
4817
- out.push(...generate(scope, decl.body));
4818
-
4819
- out.push([ Opcodes.br, 1 ]); // continue
4820
- out.push([ Opcodes.end ]); // end block
4821
- out.push([ Opcodes.end ]); // end loop
4810
+ out.push(
4811
+ ...setVar,
4812
+ ...generate(scope, decl.body),
4813
+ [ Opcodes.drop ],
4814
+ [ Opcodes.br, 1 ], // continue
4815
+ [ Opcodes.end ], // end block
4816
+ [ Opcodes.end ], // end loop
4817
+ number(UNDEFINED)
4818
+ );
4822
4819
 
4823
- depth.pop();
4824
- depth.pop();
4820
+ depth.pop(); depth.pop();
4825
4821
 
4826
4822
  inferLoopEnd(scope);
4827
4823
  return out;
@@ -4921,6 +4917,7 @@ const generateForIn = (scope, decl) => {
4921
4917
  [ Opcodes.i32_and ],
4922
4918
  [ Opcodes.if, Blocktype.void ],
4923
4919
  ...generate(scope, decl.body),
4920
+ [ Opcodes.drop ],
4924
4921
  [ Opcodes.end ],
4925
4922
 
4926
4923
  // increment pointer by 14
@@ -4953,33 +4950,41 @@ const generateForIn = (scope, decl) => {
4953
4950
 
4954
4951
  inferLoopEnd(scope);
4955
4952
 
4956
- return typeSwitch(scope, getNodeType(scope, decl.right), {
4953
+ const final = typeSwitch(scope, getNodeType(scope, decl.right), {
4957
4954
  // fast path for objects
4958
4955
  [TYPES.object]: out,
4959
4956
 
4960
4957
  // wrap for of object.keys
4961
- default: () => generate(scope, {
4962
- type: 'ForOfStatement',
4963
- left: decl.left,
4964
- body: decl.body,
4965
- right: {
4966
- type: 'CallExpression',
4967
- callee: {
4968
- type: 'Identifier',
4969
- name: '__Object_keys'
4970
- },
4971
- arguments: [ {
4972
- type: 'LogicalExpression',
4973
- left: decl.right,
4974
- operator: '??',
4975
- right: {
4976
- type: 'Literal',
4977
- value: 0
4978
- }
4979
- } ]
4980
- }
4981
- })
4958
+ default: () => {
4959
+ const out = generate(scope, {
4960
+ type: 'ForOfStatement',
4961
+ left: decl.left,
4962
+ body: decl.body,
4963
+ right: {
4964
+ type: 'CallExpression',
4965
+ callee: {
4966
+ type: 'Identifier',
4967
+ name: '__Object_keys'
4968
+ },
4969
+ arguments: [ {
4970
+ type: 'LogicalExpression',
4971
+ left: decl.right,
4972
+ operator: '??',
4973
+ right: {
4974
+ type: 'Literal',
4975
+ value: 0
4976
+ }
4977
+ } ]
4978
+ }
4979
+ });
4980
+
4981
+ out.push([ Opcodes.drop ]);
4982
+ return out;
4983
+ }
4982
4984
  }, Blocktype.void);
4985
+
4986
+ final.push(number(UNDEFINED));
4987
+ return final;
4983
4988
  };
4984
4989
 
4985
4990
  const generateSwitch = (scope, decl) => {
@@ -5024,6 +5029,7 @@ const generateSwitch = (scope, decl) => {
5024
5029
  }, Blocktype.void, true);
5025
5030
 
5026
5031
  depth.pop();
5032
+ out.push(number(UNDEFINED));
5027
5033
  return out;
5028
5034
  }
5029
5035
  }
@@ -5082,13 +5088,15 @@ const generateSwitch = (scope, decl) => {
5082
5088
  depth.pop();
5083
5089
  out.push(
5084
5090
  [ Opcodes.end ],
5085
- ...generate(scope, { type: 'BlockStatement', body: cases[i].consequent })
5091
+ ...generate(scope, { type: 'BlockStatement', body: cases[i].consequent }),
5092
+ [ Opcodes.drop ]
5086
5093
  );
5087
5094
  }
5088
5095
 
5089
5096
  out.push([ Opcodes.end ]);
5090
5097
  depth.pop();
5091
5098
 
5099
+ out.push(number(UNDEFINED));
5092
5100
  return out;
5093
5101
  };
5094
5102
 
@@ -5152,7 +5160,19 @@ const generateLabel = (scope, decl) => {
5152
5160
  const name = decl.label.name;
5153
5161
  scope.labels.set(name, depth.length);
5154
5162
 
5155
- return generate(scope, decl.body);
5163
+ const out = generate(scope, decl.body);
5164
+
5165
+ // if block statement, wrap in block to allow for breaking
5166
+ if (decl.body.type === 'BlockStatement') {
5167
+ out.unshift([ Opcodes.block, Blocktype.void ]);
5168
+ out.push(
5169
+ [ Opcodes.drop ],
5170
+ [ Opcodes.end ],
5171
+ number(UNDEFINED)
5172
+ );
5173
+ }
5174
+
5175
+ return out;
5156
5176
  };
5157
5177
 
5158
5178
  const ensureTag = (exceptionMode = Prefs.exceptionMode ?? 'stack') => {
@@ -5211,13 +5231,19 @@ const generateTry = (scope, decl) => {
5211
5231
 
5212
5232
  const out = [];
5213
5233
 
5214
- const finalizer = decl.finalizer ? generate(scope, decl.finalizer) : [];
5234
+ const finalizer = decl.finalizer ? [
5235
+ ...generate(scope, decl.finalizer),
5236
+ [ Opcodes.drop ]
5237
+ ]: [];
5215
5238
 
5216
5239
  out.push([ Opcodes.try, Blocktype.void ]);
5217
5240
  depth.push('try');
5218
5241
 
5219
- out.push(...generate(scope, decl.block));
5220
- out.push(...finalizer);
5242
+ out.push(
5243
+ ...generate(scope, decl.block),
5244
+ [ Opcodes.drop ],
5245
+ ...finalizer
5246
+ );
5221
5247
 
5222
5248
  if (decl.handler) {
5223
5249
  depth.pop();
@@ -5252,6 +5278,7 @@ const generateTry = (scope, decl) => {
5252
5278
 
5253
5279
  out.push(
5254
5280
  ...generate(scope, decl.handler.body),
5281
+ [ Opcodes.drop ],
5255
5282
  ...finalizer
5256
5283
  );
5257
5284
  }
@@ -5259,11 +5286,12 @@ const generateTry = (scope, decl) => {
5259
5286
  out.push([ Opcodes.end ]);
5260
5287
  depth.pop();
5261
5288
 
5289
+ out.push(number(UNDEFINED));
5262
5290
  return out;
5263
5291
  };
5264
5292
 
5265
5293
  const generateEmpty = (scope, decl) => {
5266
- return [];
5294
+ return [ number(UNDEFINED) ];
5267
5295
  };
5268
5296
 
5269
5297
  const generateMeta = (scope, decl) => {
@@ -6143,13 +6171,15 @@ const generateClass = (scope, decl) => {
6143
6171
  // Bar.__proto__ = Foo
6144
6172
  // Bar.prototype.__proto__ = Foo.prototype
6145
6173
  ...generate(scope, setObjProp(root, '__proto__', decl.superClass)),
6146
- ...generate(scope, setObjProp(proto, '__proto__', getObjProp(decl.superClass, 'prototype')))
6174
+ [ Opcodes.drop ],
6175
+ ...generate(scope, setObjProp(proto, '__proto__', getObjProp(decl.superClass, 'prototype'))),
6176
+ [ Opcodes.drop ]
6147
6177
  );
6148
6178
  }
6149
6179
 
6150
6180
  for (const x of body) {
6151
6181
  let { type, key, value, kind, static: _static, computed } = x;
6152
- if (type !== 'MethodDefinition' && type !== 'PropertyDefinition') return todo(scope, `class body type ${type} is not supported yet`, expr);
6182
+ if (type !== 'MethodDefinition' && type !== 'PropertyDefinition') return todo(scope, `class body type ${type} is not supported yet`, true);
6153
6183
 
6154
6184
  if (kind === 'constructor') continue;
6155
6185
 
@@ -6254,7 +6284,7 @@ export const generateTemplate = (scope, decl) => {
6254
6284
  return generate(scope, current);
6255
6285
  };
6256
6286
 
6257
- const generateTaggedTemplate = (scope, decl, global = false, name = undefined) => {
6287
+ const generateTaggedTemplate = (scope, decl, global = false, name = undefined, valueUnused = false) => {
6258
6288
  const intrinsics = {
6259
6289
  __Porffor_wasm: str => {
6260
6290
  let out = [];
@@ -6301,6 +6331,11 @@ const generateTaggedTemplate = (scope, decl, global = false, name = undefined) =
6301
6331
  out.push([ ...inst, ...immediates.flatMap(x => encodeFunc(x)) ]);
6302
6332
  }
6303
6333
 
6334
+ // add value to stack if value unused as 99% typically means
6335
+ // no value on stack at end of wasm
6336
+ // unless final op is return
6337
+ if (valueUnused && out.at(-1)[0] !== Opcodes.return) out.push(number(UNDEFINED));
6338
+
6304
6339
  return out;
6305
6340
  },
6306
6341
 
@@ -6483,7 +6518,7 @@ const generateFunc = (scope, decl, forceNoExpr = false) => {
6483
6518
  const { name, def, destr, type } = args[i];
6484
6519
 
6485
6520
  func.localInd = i * 2;
6486
- allocVar(func, name, false);
6521
+ allocVar(func, name, false, true, true);
6487
6522
 
6488
6523
  func.localInd = localInd;
6489
6524
  if (type) {
@@ -6584,6 +6619,30 @@ const generateFunc = (scope, decl, forceNoExpr = false) => {
6584
6619
  wasm = generate(func, body);
6585
6620
  wasm.unshift(...preface);
6586
6621
 
6622
+ if (name === '#main') {
6623
+ func.gotLastType = true;
6624
+ func.export = true;
6625
+
6626
+ wasm.push(...getNodeType(func, getLastNode(decl.body.body)));
6627
+
6628
+ // inject promise job runner func at the end of main if promises are made
6629
+ if (Object.hasOwn(funcIndex, 'Promise') || Object.hasOwn(funcIndex, '__Promise_resolve') || Object.hasOwn(funcIndex, '__Promise_reject')) {
6630
+ wasm.push(
6631
+ [ Opcodes.call, includeBuiltin(func, '__Porffor_promise_runJobs').index ],
6632
+ [ Opcodes.drop ],
6633
+ [ Opcodes.drop ]
6634
+ );
6635
+ }
6636
+ } else {
6637
+ // add end empty return if not found
6638
+ if (wasm[wasm.length - 1]?.[0] !== Opcodes.return) {
6639
+ wasm.push(
6640
+ [ Opcodes.drop ],
6641
+ ...generateReturn(func, {})
6642
+ );
6643
+ }
6644
+ }
6645
+
6587
6646
  if (func.generator) {
6588
6647
  // make generator at the start
6589
6648
  wasm.unshift(
@@ -6628,57 +6687,6 @@ const generateFunc = (scope, decl, forceNoExpr = false) => {
6628
6687
  ensureTag();
6629
6688
  }
6630
6689
 
6631
- if (name === '#main') {
6632
- func.gotLastType = true;
6633
- func.export = true;
6634
-
6635
- let finalStatement = decl.body.body[decl.body.body.length - 1];
6636
- if (finalStatement?.type === 'EmptyStatement') finalStatement = decl.body.body[decl.body.body.length - 2];
6637
-
6638
- const lastInst = getLastInst(wasm) ?? [ Opcodes.end ];
6639
- if (lastInst[0] === Opcodes.drop || lastInst[0] === Opcodes.f64_const) {
6640
- if (finalStatement.type.endsWith('Declaration')) {
6641
- // final statement is decl, force undefined
6642
- disposeLeftover(wasm);
6643
- wasm.push(
6644
- number(UNDEFINED),
6645
- number(TYPES.undefined, Valtype.i32)
6646
- );
6647
- } else {
6648
- wasm.splice(wasm.length - 1, 1);
6649
- wasm.push(...getNodeType(func, finalStatement));
6650
- }
6651
- }
6652
-
6653
- if (lastInst[0] === Opcodes.end || lastInst[0] === Opcodes.local_set || lastInst[0] === Opcodes.global_set) {
6654
- if (lastInst[0] === Opcodes.local_set && lastInst[1] === func.locals['#last_type'].idx) {
6655
- wasm.splice(wasm.length - 1, 1);
6656
- } else {
6657
- func.returns = [];
6658
- }
6659
- }
6660
-
6661
- if (lastInst[0] === Opcodes.call) {
6662
- const callee = funcByIndex(lastInst[1]);
6663
- if (callee) func.returns = callee.returns.slice();
6664
- else func.returns = [];
6665
- }
6666
-
6667
- // inject promise job runner func at the end of main if promises are made
6668
- if (Object.hasOwn(funcIndex, 'Promise') || Object.hasOwn(funcIndex, '__Promise_resolve') || Object.hasOwn(funcIndex, '__Promise_reject')) {
6669
- wasm.push(
6670
- [ Opcodes.call, includeBuiltin(func, '__Porffor_promise_runJobs').index ],
6671
- [ Opcodes.drop ],
6672
- [ Opcodes.drop ]
6673
- );
6674
- }
6675
- } else {
6676
- // add end return if not found
6677
- if (wasm[wasm.length - 1]?.[0] !== Opcodes.return && countLeftover(wasm) < func.returns.length) {
6678
- wasm.push(...generateReturn(func, {}));
6679
- }
6680
- }
6681
-
6682
6690
  return func.wasm = wasm;
6683
6691
  }
6684
6692
  };
@@ -6747,24 +6755,31 @@ const generateFunc = (scope, decl, forceNoExpr = false) => {
6747
6755
  if (globalThis.precompile) func.generate();
6748
6756
 
6749
6757
  if (decl._doNotMarkFuncRef) doNotMarkFuncRef = true;
6750
- const out = decl.type.endsWith('Expression') && !forceNoExpr ? funcRef(func) : [];
6758
+ const out = decl.type.endsWith('Expression') && !forceNoExpr ? funcRef(func) : [ number(UNDEFINED) ];
6751
6759
  doNotMarkFuncRef = false;
6752
6760
 
6753
6761
  astCache.set(decl, out);
6754
6762
  return [ func, out ];
6755
6763
  };
6756
6764
 
6757
- const generateCode = (scope, decl) => {
6765
+ const generateBlock = (scope, decl) => {
6758
6766
  let out = [];
6759
6767
 
6760
6768
  scope.inferTree ??= [];
6761
6769
  scope.inferTree.push(decl);
6762
6770
 
6763
- for (const x of decl.body) {
6771
+ let j = 0;
6772
+ for (let i = 0; i < decl.body.length; i++) {
6773
+ const x = decl.body[i];
6774
+ if (isEmptyNode(x)) continue;
6775
+
6776
+ if (j++ > 0) out.push([ Opcodes.drop ]);
6764
6777
  out = out.concat(generate(scope, x));
6765
6778
  }
6766
6779
 
6767
6780
  scope.inferTree.pop();
6781
+
6782
+ if (out.length === 0) out.push(number(UNDEFINED));
6768
6783
  return out;
6769
6784
  };
6770
6785
 
@@ -6867,7 +6882,9 @@ const internalConstrs = {
6867
6882
  scope.usesImports = true;
6868
6883
 
6869
6884
  const str = decl.arguments[0].value;
6870
- return printStaticStr(str);
6885
+ const out = printStaticStr(str);
6886
+ out.push(number(UNDEFINED));
6887
+ return out;
6871
6888
  },
6872
6889
  type: TYPES.undefined,
6873
6890
  notConstr: true,
@@ -7046,4 +7063,4 @@ export default program => {
7046
7063
  delete globals['#ind'];
7047
7064
 
7048
7065
  return { funcs, globals, tags, exceptions, pages, data };
7049
- };
7066
+ };