porffor 0.14.0-c6edfd328 → 0.14.0-d6c141d91

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.
@@ -1,4 +1,5 @@
1
1
  // @porf --valtype=i32
2
+ import type {} from './porffor.d.ts';
2
3
 
3
4
  export const __String_fromCharCode = (code: i32) => {
4
5
  // todo: support >1 arg
@@ -1,3 +1,5 @@
1
+ import type {} from './porffor.d.ts';
2
+
1
3
  export const __Porffor_symbol_descStore = (op: boolean, value: any): any => {
2
4
  const ptr: bytestring = '';
3
5
 
@@ -40,6 +40,18 @@ export const importedFuncs = [
40
40
  import: 'z',
41
41
  params: 1,
42
42
  returns: 0
43
+ },
44
+ {
45
+ name: '__Porffor_readArgv',
46
+ import: 'w',
47
+ params: 2,
48
+ returns: 0
49
+ },
50
+ {
51
+ name: '__Porffor_readFile',
52
+ import: 'q',
53
+ params: 2,
54
+ returns: 0
43
55
  }
44
56
  ];
45
57
 
@@ -221,8 +233,7 @@ export const BuiltinFuncs = function() {
221
233
  typedParams: true,
222
234
  locals: [ Valtype.i32, Valtype.i32 ],
223
235
  returns: [],
224
- callsSelf: true,
225
- wasm: (scope, { typeSwitch }) => [
236
+ wasm: (scope, { typeSwitch, builtin }) => [
226
237
  ...typeSwitch(scope, [ [ Opcodes.local_get, 1 ] ], {
227
238
  [TYPES.number]: [
228
239
  [ Opcodes.local_get, 0 ],
@@ -327,14 +338,14 @@ export const BuiltinFuncs = function() {
327
338
 
328
339
  [ Opcodes.loop, Blocktype.void ],
329
340
 
330
- // print current char
341
+ // print current array element
331
342
  [ Opcodes.local_get, 2 ],
332
343
  [ Opcodes.load, 0, ValtypeSize.i32 ],
333
344
 
334
345
  [ Opcodes.local_get, 2 ],
335
346
  [ Opcodes.i32_load8_u, 0, ValtypeSize.i32 + ValtypeSize[valtype] ],
336
347
 
337
- [ Opcodes.call, -1 ],
348
+ [ Opcodes.call, builtin('__Porffor_print') ],
338
349
 
339
350
  // increment pointer by sizeof valtype
340
351
  [ Opcodes.local_get, 2 ],
@@ -1084,5 +1095,16 @@ export const BuiltinFuncs = function() {
1084
1095
  ]
1085
1096
  };
1086
1097
 
1098
+
1099
+ this.__fs_readFileSync = {
1100
+ params: [ valtypeBinary, valtypeBinary ],
1101
+ locals: [],
1102
+ returns: [ valtypeBinary ],
1103
+ returnType: TYPES.bytestring,
1104
+ wasm: [
1105
+ [ Opcodes.call, importedFuncs.__Porffor_readFile ],
1106
+ ]
1107
+ };
1108
+
1087
1109
  GeneratedBuiltins.BuiltinFuncs.call(this);
1088
1110
  };
@@ -40,11 +40,11 @@ const todo = (scope, msg, expectsValue = undefined) => {
40
40
  }
41
41
  };
42
42
 
43
- const isFuncType = type => type === 'FunctionDeclaration' || type === 'FunctionExpression' || type === 'ArrowFunctionExpression';
44
- const hasFuncWithName = name => {
45
- const func = funcs.find(x => x.name === name);
46
- return !!(func || builtinFuncs[name] || importedFuncs[name] || internalConstrs[name]);
47
- };
43
+ const isFuncType = type =>
44
+ type === 'FunctionDeclaration' || type === 'FunctionExpression' || type === 'ArrowFunctionExpression';
45
+ const hasFuncWithName = name =>
46
+ funcIndex[name] != null || builtinFuncs[name] != null || importedFuncs[name] != null || internalConstrs[name] != null;
47
+
48
48
  const generate = (scope, decl, global = false, name = undefined, valueUnused = false) => {
49
49
  switch (decl.type) {
50
50
  case 'BinaryExpression':
@@ -147,14 +147,16 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
147
147
  return generateMember(scope, decl, global, name);
148
148
 
149
149
  case 'ExportNamedDeclaration':
150
- // hack to flag new func for export
151
- const funcsBefore = funcs.length;
150
+ const funcsBefore = funcs.map(x => x.name);
152
151
  generate(scope, decl.declaration);
153
152
 
154
- if (funcsBefore !== funcs.length) {
155
- // new func added
156
- const newFunc = funcs[funcs.length - 1];
157
- newFunc.export = true;
153
+ // set new funcs as exported
154
+ if (funcsBefore.length !== funcs.length) {
155
+ const newFuncs = funcs.filter(x => !funcsBefore.includes(x.name)).filter(x => !x.internal);
156
+
157
+ for (const x of newFuncs) {
158
+ x.export = true;
159
+ }
158
160
  }
159
161
 
160
162
  return [];
@@ -361,7 +363,7 @@ const localTmp = (scope, name, type = valtypeBinary) => {
361
363
  };
362
364
 
363
365
  const isIntOp = op => op && ((op[0] >= 0x45 && op[0] <= 0x4f) || (op[0] >= 0x67 && op[0] <= 0x78) || op[0] === 0x41);
364
- const isFloatToIntOp = op => op && (op[0] >= 0xb7 && op[0] <= 0xba);
366
+ const isIntToFloatOp = op => op && (op[0] >= 0xb7 && op[0] <= 0xba);
365
367
 
366
368
  const performLogicOp = (scope, op, left, right, leftType, rightType) => {
367
369
  const checks = {
@@ -378,10 +380,10 @@ const performLogicOp = (scope, op, left, right, leftType, rightType) => {
378
380
 
379
381
  // if we can, use int tmp and convert at the end to help prevent unneeded conversions
380
382
  // (like if we are in an if condition - very common)
381
- const leftIsInt = isFloatToIntOp(left[left.length - 1]);
382
- const rightIsInt = isFloatToIntOp(right[right.length - 1]);
383
+ const leftWasInt = isIntToFloatOp(left[left.length - 1]);
384
+ const rightWasInt = isIntToFloatOp(right[right.length - 1]);
383
385
 
384
- const canInt = leftIsInt && rightIsInt;
386
+ const canInt = leftWasInt && rightWasInt;
385
387
 
386
388
  if (canInt) {
387
389
  // remove int -> float conversions from left and right
@@ -646,9 +648,9 @@ const compareStrings = (scope, left, right, bytestrings = false) => {
646
648
  [ Opcodes.i32_add ],
647
649
  [ Opcodes.local_tee, index ],
648
650
 
649
- // if index != index end (length * sizeof valtype), loop
651
+ // if index < index end (length * sizeof valtype), loop
650
652
  [ Opcodes.local_get, indexEnd ],
651
- [ Opcodes.i32_ne ],
653
+ [ Opcodes.i32_lt_s ],
652
654
  [ Opcodes.br_if, 0 ],
653
655
  [ Opcodes.end ],
654
656
 
@@ -666,8 +668,8 @@ const compareStrings = (scope, left, right, bytestrings = false) => {
666
668
  ];
667
669
  };
668
670
 
669
- const truthy = (scope, wasm, type, intIn = false, intOut = false) => {
670
- if (isFloatToIntOp(wasm[wasm.length - 1])) return [
671
+ const truthy = (scope, wasm, type, intIn = false, intOut = false, forceTruthyMode = undefined) => {
672
+ if (isIntToFloatOp(wasm[wasm.length - 1])) return [
671
673
  ...wasm,
672
674
  ...(!intIn && intOut ? [ Opcodes.i32_to_u ] : [])
673
675
  ];
@@ -676,47 +678,38 @@ const truthy = (scope, wasm, type, intIn = false, intOut = false) => {
676
678
  const useTmp = knownType(scope, type) == null;
677
679
  const tmp = useTmp && localTmp(scope, `#logicinner_tmp${intIn ? '_int' : ''}`, intIn ? Valtype.i32 : valtypeBinary);
678
680
 
679
- const def = [
680
- // if value != 0
681
- ...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
681
+ const def = (truthyMode => {
682
+ if (truthyMode === 'full') return [
683
+ // if value != 0 or NaN
684
+ ...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
685
+ ...(intIn ? [ ] : [ Opcodes.i32_to ]),
682
686
 
683
- // ...(intIn ? [ [ Opcodes.i32_eqz ] ] : [ ...Opcodes.eqz ]),
684
- // [ Opcodes.i32_eqz ],
687
+ [ Opcodes.i32_eqz ],
688
+ [ Opcodes.i32_eqz ],
685
689
 
686
- // ...(intIn ? [
687
- // ...number(0, Valtype.i32),
688
- // [ Opcodes.i32_gt_s ]
689
- // ] : [
690
- // ...number(0),
691
- // [ Opcodes.f64_gt ]
692
- // ]),
693
-
694
- // ...(intOut ? [] : [ Opcodes.i32_from ]),
695
-
696
- // ...(intIn ? [] : [ Opcodes.i32_to ]),
697
- // [ Opcodes.if, intOut ? Valtype.i32 : valtypeBinary ],
698
- // ...number(1, intOut ? Valtype.i32 : valtypeBinary),
699
- // [ Opcodes.else ],
700
- // ...number(0, intOut ? Valtype.i32 : valtypeBinary),
701
- // [ Opcodes.end ],
702
-
703
- ...(!intOut || (intIn && intOut) ? [] : [ Opcodes.i32_to_u ]),
704
-
705
- /* Opcodes.eqz,
706
- [ Opcodes.i32_eqz ],
707
- Opcodes.i32_from */
708
- ];
690
+ ...(intOut ? [] : [ Opcodes.i32_from ]),
691
+ ];
692
+
693
+ if (truthyMode === 'no_negative') return [
694
+ // if value != 0 or NaN, non-binary output. negative numbers not truthy :/
695
+ ...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
696
+ ...(intIn ? [] : [ Opcodes.i32_to ]),
697
+ ...(intOut ? [] : [ Opcodes.i32_from ])
698
+ ];
699
+
700
+ if (truthyMode === 'no_nan_negative') return [
701
+ // simpler and faster but makes NaN truthy and negative numbers not truthy,
702
+ // plus non-binary output
703
+ ...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
704
+ ...(!intOut || (intIn && intOut) ? [] : [ Opcodes.i32_to_u ])
705
+ ];
706
+ })(forceTruthyMode ?? Prefs.truthy ?? 'full');
709
707
 
710
708
  return [
711
709
  ...wasm,
712
710
  ...(!useTmp ? [] : [ [ Opcodes.local_set, tmp ] ]),
713
711
 
714
712
  ...typeSwitch(scope, type, {
715
- // [TYPES.number]: def,
716
- [TYPES.array]: [
717
- // arrays are always truthy
718
- ...number(1, intOut ? Valtype.i32 : valtypeBinary)
719
- ],
720
713
  [TYPES.string]: [
721
714
  ...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
722
715
  ...(intIn ? [] : [ Opcodes.i32_to_u ]),
@@ -752,10 +745,6 @@ const falsy = (scope, wasm, type, intIn = false, intOut = false) => {
752
745
  ...(!useTmp ? [] : [ [ Opcodes.local_set, tmp ] ]),
753
746
 
754
747
  ...typeSwitch(scope, type, {
755
- [TYPES.array]: [
756
- // arrays are always truthy
757
- ...number(0, intOut ? Valtype.i32 : valtypeBinary)
758
- ],
759
748
  [TYPES.string]: [
760
749
  ...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
761
750
  ...(intIn ? [] : [ Opcodes.i32_to_u ]),
@@ -999,7 +988,7 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
999
988
  // if both are true
1000
989
  [ Opcodes.i32_and ],
1001
990
  [ Opcodes.if, Blocktype.void ],
1002
- ...compareStrings(scope, [ [ Opcodes.local_get, tmpLeft ] ], [ [ Opcodes.local_get, tmpRight ] ]),
991
+ ...compareStrings(scope, [ [ Opcodes.local_get, tmpLeft ] ], [ [ Opcodes.local_get, tmpRight ] ], false),
1003
992
  ...(op === '!==' || op === '!=' ? [ [ Opcodes.i32_eqz ] ] : []),
1004
993
  [ Opcodes.br, 1 ],
1005
994
  [ Opcodes.end ],
@@ -1048,14 +1037,14 @@ const generateBinaryExp = (scope, decl, _global, _name) => {
1048
1037
  return out;
1049
1038
  };
1050
1039
 
1051
- const asmFuncToAsm = (func, { name = '#unknown_asm_func', params = [], locals = [], returns = [], localInd = 0 }) => {
1052
- return func({ name, params, locals, returns, localInd }, {
1040
+ const asmFuncToAsm = (func, scope) => {
1041
+ return func(scope, {
1053
1042
  TYPES, TYPE_NAMES, typeSwitch, makeArray, makeString, allocPage, internalThrow,
1054
- builtin: name => {
1055
- let idx = funcIndex[name] ?? importedFuncs[name];
1056
- if (idx === undefined && builtinFuncs[name]) {
1057
- includeBuiltin(null, name);
1058
- idx = funcIndex[name];
1043
+ builtin: n => {
1044
+ let idx = funcIndex[n] ?? importedFuncs[n];
1045
+ if (idx == null && builtinFuncs[n]) {
1046
+ includeBuiltin(null, n);
1047
+ idx = funcIndex[n];
1059
1048
  }
1060
1049
 
1061
1050
  return idx;
@@ -1063,7 +1052,7 @@ const asmFuncToAsm = (func, { name = '#unknown_asm_func', params = [], locals =
1063
1052
  });
1064
1053
  };
1065
1054
 
1066
- const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes = [], globalInits, returns, returnType, localNames = [], globalNames = [], data: _data = [], table = false, callsSelf = false }) => {
1055
+ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes = [], globalInits, returns, returnType, localNames = [], globalNames = [], data: _data = [], table = false }) => {
1067
1056
  const existing = funcs.find(x => x.name === name);
1068
1057
  if (existing) return existing;
1069
1058
 
@@ -1081,7 +1070,22 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
1081
1070
  data.push(copy);
1082
1071
  }
1083
1072
 
1084
- if (typeof wasm === 'function') wasm = asmFuncToAsm(wasm, { name, params, locals, returns, localInd: allLocals.length });
1073
+ const func = {
1074
+ name,
1075
+ params,
1076
+ locals,
1077
+ localInd: allLocals.length,
1078
+ returns,
1079
+ returnType: returnType ?? TYPES.number,
1080
+ internal: true,
1081
+ index: currentFuncIndex++,
1082
+ table
1083
+ };
1084
+
1085
+ funcs.push(func);
1086
+ funcIndex[name] = func.index;
1087
+
1088
+ if (typeof wasm === 'function') wasm = asmFuncToAsm(wasm, func);
1085
1089
 
1086
1090
  let baseGlobalIdx, i = 0;
1087
1091
  for (const type of globalTypes) {
@@ -1100,23 +1104,6 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
1100
1104
  }
1101
1105
  }
1102
1106
 
1103
- const func = {
1104
- name,
1105
- params,
1106
- locals,
1107
- returns,
1108
- returnType: returnType ?? TYPES.number,
1109
- wasm,
1110
- internal: true,
1111
- index: currentFuncIndex++
1112
- };
1113
-
1114
- if (callsSelf) for (const inst of wasm) {
1115
- if (inst[0] === Opcodes.call && inst[1] === -1) {
1116
- inst[1] = func.index;
1117
- }
1118
- }
1119
-
1120
1107
  if (table) for (const inst of wasm) {
1121
1108
  if (inst[0] === Opcodes.i32_load16_u && inst.at(-1) === 'read_argc') {
1122
1109
  inst.splice(2, 99);
@@ -1124,10 +1111,7 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
1124
1111
  }
1125
1112
  }
1126
1113
 
1127
- funcs.push(func);
1128
- funcIndex[name] = func.index;
1129
-
1130
- if (table) funcs.table = true;
1114
+ func.wasm = wasm;
1131
1115
 
1132
1116
  return func;
1133
1117
  };
@@ -1479,16 +1463,11 @@ const countLeftover = wasm => {
1479
1463
  else if (inst[0] === Opcodes.return) count = 0;
1480
1464
  else if (inst[0] === Opcodes.call) {
1481
1465
  let func = funcs.find(x => x.index === inst[1]);
1482
- if (inst[1] === -1) {
1483
- // todo: count for calling self
1484
- } else if (!func && inst[1] < importedFuncs.length) {
1485
- count -= importedFuncs[inst[1]].params;
1486
- count += importedFuncs[inst[1]].returns;
1466
+ if (inst[1] < importedFuncs.length) {
1467
+ func = importedFuncs[inst[1]];
1468
+ count = count - func.params + func.returns;
1487
1469
  } else {
1488
- if (func) {
1489
- count -= func.params.length;
1490
- } else count--;
1491
- if (func) count += func.returns.length;
1470
+ count = count - func.params.length + func.returns.length;
1492
1471
  }
1493
1472
  } else if (inst[0] === Opcodes.call_indirect) {
1494
1473
  count--; // funcidx
@@ -1641,6 +1620,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1641
1620
 
1642
1621
  if (!funcIndex[rhemynName]) {
1643
1622
  const func = Rhemyn[funcName](regex, currentFuncIndex++, rhemynName);
1623
+ func.internal = true;
1644
1624
 
1645
1625
  funcIndex[func.name] = func.index;
1646
1626
  funcs.push(func);
@@ -1812,11 +1792,6 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1812
1792
 
1813
1793
  if (idx === undefined && internalConstrs[name]) return internalConstrs[name].generate(scope, decl, _global, _name);
1814
1794
 
1815
- if (idx === undefined && name === scope.name) {
1816
- // hack: calling self, func generator will fix later
1817
- idx = -1;
1818
- }
1819
-
1820
1795
  if (idx === undefined && name.startsWith('__Porffor_wasm_')) {
1821
1796
  const wasmOps = {
1822
1797
  // pointer, align, offset
@@ -1998,11 +1973,10 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1998
1973
  return internalThrow(scope, 'ReferenceError', `${unhackName(name)} is not defined`, true);
1999
1974
  }
2000
1975
 
2001
- const func = funcs.find(x => x.index === idx);
2002
-
2003
- const userFunc = (funcIndex[name] && !importedFuncs[name] && !builtinFuncs[name] && !internalConstrs[name]) || idx === -1;
1976
+ const func = funcs[idx - importedFuncs.length]; // idx === scope.index ? scope : funcs.find(x => x.index === idx);
1977
+ const userFunc = func && !func.internal;
2004
1978
  const typedParams = userFunc || builtinFuncs[name]?.typedParams;
2005
- const typedReturns = (func ? func.returnType == null : userFunc) || builtinFuncs[name]?.typedReturns;
1979
+ const typedReturns = (func && func.returnType == null) || builtinFuncs[name]?.typedReturns;
2006
1980
  const paramCount = func && (typedParams ? func.params.length / 2 : func.params.length);
2007
1981
 
2008
1982
  let args = decl.arguments;
@@ -2605,6 +2579,7 @@ const generateUnary = (scope, decl) => {
2605
2579
  ];
2606
2580
 
2607
2581
  case '!':
2582
+ // todo/perf: optimize !!
2608
2583
  // !=
2609
2584
  return falsy(scope, generate(scope, decl.argument), getNodeType(scope, decl.argument), false, false);
2610
2585
 
@@ -3144,14 +3119,18 @@ const generateThrow = (scope, decl) => {
3144
3119
  };
3145
3120
 
3146
3121
  const generateTry = (scope, decl) => {
3147
- if (decl.finalizer) return todo(scope, 'try finally not implemented yet');
3122
+ // todo: handle control-flow pre-exit for finally
3123
+ // "Immediately before a control-flow statement (return, throw, break, continue) is executed in the try block or catch block."
3148
3124
 
3149
3125
  const out = [];
3150
3126
 
3127
+ const finalizer = decl.finalizer ? generate(scope, decl.finalizer) : [];
3128
+
3151
3129
  out.push([ Opcodes.try, Blocktype.void ]);
3152
3130
  depth.push('try');
3153
3131
 
3154
3132
  out.push(...generate(scope, decl.block));
3133
+ out.push(...finalizer);
3155
3134
 
3156
3135
  if (decl.handler) {
3157
3136
  depth.pop();
@@ -3159,6 +3138,7 @@ const generateTry = (scope, decl) => {
3159
3138
 
3160
3139
  out.push([ Opcodes.catch_all ]);
3161
3140
  out.push(...generate(scope, decl.handler.body));
3141
+ out.push(...finalizer);
3162
3142
  }
3163
3143
 
3164
3144
  out.push([ Opcodes.end ]);
@@ -3256,8 +3236,13 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
3256
3236
  // todo: can we just have 1 undeclared array? probably not? but this is not really memory efficient
3257
3237
  const uniqueName = name === '$undeclared' ? name + Math.random().toString().slice(2) : name;
3258
3238
 
3259
- if (Prefs.scopedPageNames) scope.arrays.set(name, allocPage(scope, `${getAllocType(itemType)}: ${scope.name}/${uniqueName}`, itemType) * pageSize);
3260
- else scope.arrays.set(name, allocPage(scope, `${getAllocType(itemType)}: ${uniqueName}`, itemType) * pageSize);
3239
+ let page;
3240
+ if (Prefs.scopedPageNames) page = allocPage(scope, `${getAllocType(itemType)}: ${scope.name}/${uniqueName}`, itemType);
3241
+ else page = allocPage(scope, `${getAllocType(itemType)}: ${uniqueName}`, itemType);
3242
+
3243
+ // hack: use 1 for page 0 pointer for fast truthiness
3244
+ const ptr = page === 0 ? 1 : (page * pageSize);
3245
+ scope.arrays.set(name, ptr);
3261
3246
  }
3262
3247
 
3263
3248
  const pointer = scope.arrays.get(name);
@@ -3444,8 +3429,19 @@ const withType = (scope, wasm, type) => [
3444
3429
 
3445
3430
  const generateMember = (scope, decl, _global, _name) => {
3446
3431
  const name = decl.object.name;
3447
- const pointer = scope.arrays?.get(name);
3448
3432
 
3433
+ // hack: process.argv[n]
3434
+ if (name === '__process_argv') {
3435
+ const setPointer = scope.arrays?.get(_name);
3436
+
3437
+ return [
3438
+ ...number(decl.property.value - 1),
3439
+ ...(setPointer ? number(setPointer) : allocPage(scope, `__process_argv out (${randId()})`)),
3440
+ [ Opcodes.call, importedFuncs.__Porffor_readArgv ]
3441
+ ];
3442
+ }
3443
+
3444
+ const pointer = scope.arrays?.get(name);
3449
3445
  const aotPointer = Prefs.aotPointerOpt && pointer;
3450
3446
 
3451
3447
  // hack: .name
@@ -3466,8 +3462,7 @@ const generateMember = (scope, decl, _global, _name) => {
3466
3462
  if (decl.property.name === 'length') {
3467
3463
  const func = funcs.find(x => x.name === name);
3468
3464
  if (func) {
3469
- const userFunc = funcIndex[name] && !importedFuncs[name] && !builtinFuncs[name] && !internalConstrs[name];
3470
- const typedParams = userFunc || builtinFuncs[name]?.typedParams;
3465
+ const typedParams = !func.internal || builtinFuncs[name]?.typedParams;
3471
3466
  return withType(scope, number(typedParams ? func.params.length / 2 : func.params.length), TYPES.number);
3472
3467
  }
3473
3468
 
@@ -3693,34 +3688,39 @@ const generateFunc = (scope, decl) => {
3693
3688
  const name = decl.id ? decl.id.name : `anonymous_${randId()}`;
3694
3689
  const params = decl.params ?? [];
3695
3690
 
3696
- // const innerScope = { ...scope };
3697
3691
  // TODO: share scope/locals between !!!
3698
- const innerScope = {
3692
+ const func = {
3699
3693
  locals: {},
3700
3694
  localInd: 0,
3701
3695
  // value, type
3702
3696
  returns: [ valtypeBinary, Valtype.i32 ],
3703
3697
  throws: false,
3704
- name
3698
+ name,
3699
+ index: currentFuncIndex++
3705
3700
  };
3706
3701
 
3707
3702
  if (typedInput && decl.returnType) {
3708
3703
  const { type } = extractTypeAnnotation(decl.returnType);
3709
3704
  // if (type != null && !Prefs.indirectCalls) {
3710
3705
  if (type != null) {
3711
- innerScope.returnType = type;
3712
- innerScope.returns = [ valtypeBinary ];
3706
+ func.returnType = type;
3707
+ func.returns = [ valtypeBinary ];
3713
3708
  }
3714
3709
  }
3715
3710
 
3716
3711
  for (let i = 0; i < params.length; i++) {
3717
- allocVar(innerScope, params[i].name, false);
3712
+ const name = params[i].name;
3713
+ // if (name == null) return todo('non-identifier args are not supported');
3714
+
3715
+ allocVar(func, name, false);
3718
3716
 
3719
3717
  if (typedInput && params[i].typeAnnotation) {
3720
- addVarMetadata(innerScope, params[i].name, false, extractTypeAnnotation(params[i]));
3718
+ addVarMetadata(func, name, false, extractTypeAnnotation(params[i]));
3721
3719
  }
3722
3720
  }
3723
3721
 
3722
+ func.params = Object.values(func.locals).map(x => x.type);
3723
+
3724
3724
  let body = objectHack(decl.body);
3725
3725
  if (decl.type === 'ArrowFunctionExpression' && decl.expression) {
3726
3726
  // hack: () => 0 -> () => return 0
@@ -3730,37 +3730,23 @@ const generateFunc = (scope, decl) => {
3730
3730
  };
3731
3731
  }
3732
3732
 
3733
- const wasm = generate(innerScope, body);
3734
- const func = {
3735
- name,
3736
- params: Object.values(innerScope.locals).slice(0, params.length * 2).map(x => x.type),
3737
- index: currentFuncIndex++,
3738
- ...innerScope
3739
- };
3740
3733
  funcIndex[name] = func.index;
3734
+ funcs.push(func);
3741
3735
 
3742
- if (name === 'main') func.gotLastType = true;
3736
+ const wasm = generate(func, body);
3737
+ func.wasm = wasm;
3743
3738
 
3744
- // quick hack fixes
3745
- for (const inst of wasm) {
3746
- if (inst[0] === Opcodes.call && inst[1] === -1) {
3747
- inst[1] = func.index;
3748
- }
3749
- }
3739
+ if (name === 'main') func.gotLastType = true;
3750
3740
 
3751
3741
  // add end return if not found
3752
3742
  if (name !== 'main' && wasm[wasm.length - 1]?.[0] !== Opcodes.return && countLeftover(wasm) === 0) {
3753
3743
  wasm.push(
3754
3744
  ...number(0),
3755
- ...(innerScope.returnType != null ? [] : number(TYPES.undefined, Valtype.i32)),
3745
+ ...(func.returnType != null ? [] : number(TYPES.undefined, Valtype.i32)),
3756
3746
  [ Opcodes.return ]
3757
3747
  );
3758
3748
  }
3759
3749
 
3760
- func.wasm = wasm;
3761
-
3762
- funcs.push(func);
3763
-
3764
3750
  return func;
3765
3751
  };
3766
3752
 
@@ -3864,7 +3850,7 @@ const internalConstrs = {
3864
3850
  generate: (scope, decl) => {
3865
3851
  // todo: boolean object when used as constructor
3866
3852
  const arg = decl.arguments[0] ?? DEFAULT_VALUE;
3867
- return truthy(scope, generate(scope, arg), getNodeType(scope, arg));
3853
+ return truthy(scope, generate(scope, arg), getNodeType(scope, arg), false, false, 'full');
3868
3854
  },
3869
3855
  type: TYPES.boolean,
3870
3856
  length: 1
@@ -4009,9 +3995,8 @@ export default program => {
4009
3995
 
4010
3996
  if (Prefs.astLog) console.log(JSON.stringify(program.body.body, null, 2));
4011
3997
 
4012
- generateFunc(scope, program);
3998
+ const main = generateFunc(scope, program);
4013
3999
 
4014
- const main = funcs[funcs.length - 1];
4015
4000
  main.export = true;
4016
4001
  main.returns = [ valtypeBinary, Valtype.i32 ];
4017
4002
 
@@ -4038,7 +4023,7 @@ export default program => {
4038
4023
  }
4039
4024
 
4040
4025
  // if blank main func and other exports, remove it
4041
- if (main.wasm.length === 0 && funcs.reduce((acc, x) => acc + (x.export ? 1 : 0), 0) > 1) funcs.splice(funcs.length - 1, 1);
4026
+ if (main.wasm.length === 0 && funcs.reduce((acc, x) => acc + (x.export ? 1 : 0), 0) > 1) funcs.splice(main.index - importedFuncs.length, 1);
4042
4027
 
4043
4028
  return { funcs, globals, tags, exceptions, pages, data };
4044
4029
  };