porffor 0.14.0-b5a80d8e3 → 0.14.0-b880d42f1

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.
@@ -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
 
@@ -3261,8 +3236,13 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
3261
3236
  // todo: can we just have 1 undeclared array? probably not? but this is not really memory efficient
3262
3237
  const uniqueName = name === '$undeclared' ? name + Math.random().toString().slice(2) : name;
3263
3238
 
3264
- if (Prefs.scopedPageNames) scope.arrays.set(name, allocPage(scope, `${getAllocType(itemType)}: ${scope.name}/${uniqueName}`, itemType) * pageSize);
3265
- 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);
3266
3246
  }
3267
3247
 
3268
3248
  const pointer = scope.arrays.get(name);
@@ -3449,8 +3429,19 @@ const withType = (scope, wasm, type) => [
3449
3429
 
3450
3430
  const generateMember = (scope, decl, _global, _name) => {
3451
3431
  const name = decl.object.name;
3452
- const pointer = scope.arrays?.get(name);
3453
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);
3454
3445
  const aotPointer = Prefs.aotPointerOpt && pointer;
3455
3446
 
3456
3447
  // hack: .name
@@ -3471,8 +3462,7 @@ const generateMember = (scope, decl, _global, _name) => {
3471
3462
  if (decl.property.name === 'length') {
3472
3463
  const func = funcs.find(x => x.name === name);
3473
3464
  if (func) {
3474
- const userFunc = funcIndex[name] && !importedFuncs[name] && !builtinFuncs[name] && !internalConstrs[name];
3475
- const typedParams = userFunc || builtinFuncs[name]?.typedParams;
3465
+ const typedParams = !func.internal || builtinFuncs[name]?.typedParams;
3476
3466
  return withType(scope, number(typedParams ? func.params.length / 2 : func.params.length), TYPES.number);
3477
3467
  }
3478
3468
 
@@ -3698,34 +3688,39 @@ const generateFunc = (scope, decl) => {
3698
3688
  const name = decl.id ? decl.id.name : `anonymous_${randId()}`;
3699
3689
  const params = decl.params ?? [];
3700
3690
 
3701
- // const innerScope = { ...scope };
3702
3691
  // TODO: share scope/locals between !!!
3703
- const innerScope = {
3692
+ const func = {
3704
3693
  locals: {},
3705
3694
  localInd: 0,
3706
3695
  // value, type
3707
3696
  returns: [ valtypeBinary, Valtype.i32 ],
3708
3697
  throws: false,
3709
- name
3698
+ name,
3699
+ index: currentFuncIndex++
3710
3700
  };
3711
3701
 
3712
3702
  if (typedInput && decl.returnType) {
3713
3703
  const { type } = extractTypeAnnotation(decl.returnType);
3714
3704
  // if (type != null && !Prefs.indirectCalls) {
3715
3705
  if (type != null) {
3716
- innerScope.returnType = type;
3717
- innerScope.returns = [ valtypeBinary ];
3706
+ func.returnType = type;
3707
+ func.returns = [ valtypeBinary ];
3718
3708
  }
3719
3709
  }
3720
3710
 
3721
3711
  for (let i = 0; i < params.length; i++) {
3722
- 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);
3723
3716
 
3724
3717
  if (typedInput && params[i].typeAnnotation) {
3725
- addVarMetadata(innerScope, params[i].name, false, extractTypeAnnotation(params[i]));
3718
+ addVarMetadata(func, name, false, extractTypeAnnotation(params[i]));
3726
3719
  }
3727
3720
  }
3728
3721
 
3722
+ func.params = Object.values(func.locals).map(x => x.type);
3723
+
3729
3724
  let body = objectHack(decl.body);
3730
3725
  if (decl.type === 'ArrowFunctionExpression' && decl.expression) {
3731
3726
  // hack: () => 0 -> () => return 0
@@ -3735,37 +3730,23 @@ const generateFunc = (scope, decl) => {
3735
3730
  };
3736
3731
  }
3737
3732
 
3738
- const wasm = generate(innerScope, body);
3739
- const func = {
3740
- name,
3741
- params: Object.values(innerScope.locals).slice(0, params.length * 2).map(x => x.type),
3742
- index: currentFuncIndex++,
3743
- ...innerScope
3744
- };
3745
3733
  funcIndex[name] = func.index;
3734
+ funcs.push(func);
3746
3735
 
3747
- if (name === 'main') func.gotLastType = true;
3736
+ const wasm = generate(func, body);
3737
+ func.wasm = wasm;
3748
3738
 
3749
- // quick hack fixes
3750
- for (const inst of wasm) {
3751
- if (inst[0] === Opcodes.call && inst[1] === -1) {
3752
- inst[1] = func.index;
3753
- }
3754
- }
3739
+ if (name === 'main') func.gotLastType = true;
3755
3740
 
3756
3741
  // add end return if not found
3757
3742
  if (name !== 'main' && wasm[wasm.length - 1]?.[0] !== Opcodes.return && countLeftover(wasm) === 0) {
3758
3743
  wasm.push(
3759
3744
  ...number(0),
3760
- ...(innerScope.returnType != null ? [] : number(TYPES.undefined, Valtype.i32)),
3745
+ ...(func.returnType != null ? [] : number(TYPES.undefined, Valtype.i32)),
3761
3746
  [ Opcodes.return ]
3762
3747
  );
3763
3748
  }
3764
3749
 
3765
- func.wasm = wasm;
3766
-
3767
- funcs.push(func);
3768
-
3769
3750
  return func;
3770
3751
  };
3771
3752
 
@@ -3869,7 +3850,7 @@ const internalConstrs = {
3869
3850
  generate: (scope, decl) => {
3870
3851
  // todo: boolean object when used as constructor
3871
3852
  const arg = decl.arguments[0] ?? DEFAULT_VALUE;
3872
- return truthy(scope, generate(scope, arg), getNodeType(scope, arg));
3853
+ return truthy(scope, generate(scope, arg), getNodeType(scope, arg), false, false, 'full');
3873
3854
  },
3874
3855
  type: TYPES.boolean,
3875
3856
  length: 1
@@ -4014,9 +3995,8 @@ export default program => {
4014
3995
 
4015
3996
  if (Prefs.astLog) console.log(JSON.stringify(program.body.body, null, 2));
4016
3997
 
4017
- generateFunc(scope, program);
3998
+ const main = generateFunc(scope, program);
4018
3999
 
4019
- const main = funcs[funcs.length - 1];
4020
4000
  main.export = true;
4021
4001
  main.returns = [ valtypeBinary, Valtype.i32 ];
4022
4002
 
@@ -4043,7 +4023,7 @@ export default program => {
4043
4023
  }
4044
4024
 
4045
4025
  // if blank main func and other exports, remove it
4046
- 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);
4047
4027
 
4048
4028
  return { funcs, globals, tags, exceptions, pages, data };
4049
4029
  };