porffor 0.37.6 → 0.37.7

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.
@@ -390,6 +390,8 @@ const generateReturn = (scope, decl) => {
390
390
  const arg = decl.argument ?? DEFAULT_VALUE();
391
391
 
392
392
  if (scope.async) {
393
+ typeUsed(scope, TYPES.promise);
394
+
393
395
  return [
394
396
  // resolve promise with return value
395
397
  [ Opcodes.local_get, scope.locals['#async_out_promise'].idx ],
@@ -589,7 +591,7 @@ const truthy = (scope, wasm, type, intIn = false, intOut = false, forceTruthyMod
589
591
  ...(!useTmp ? [] : [ [ Opcodes.local_set, tmp ] ]),
590
592
 
591
593
  ...typeSwitch(scope, type, [
592
- [ [ TYPES.string, TYPES.bytestring ], [
594
+ [ [ TYPES.string, TYPES.bytestring ], () => [
593
595
  ...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
594
596
  ...(intIn ? [] : [ Opcodes.i32_to_u ]),
595
597
 
@@ -643,7 +645,7 @@ const falsy = (scope, wasm, type, intIn = false, intOut = false, forceTruthyMode
643
645
  ...(!useTmp ? [] : [ [ Opcodes.local_set, tmp ] ]),
644
646
 
645
647
  ...typeSwitch(scope, type, [
646
- [ [ TYPES.string, TYPES.bytestring ], [
648
+ [ [ TYPES.string, TYPES.bytestring ], () => [
647
649
  ...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
648
650
  ...(intIn ? [] : [ Opcodes.i32_to_u ]),
649
651
 
@@ -689,17 +691,6 @@ const nullish = (scope, wasm, type, intIn = false, intOut = false) => {
689
691
  ];
690
692
  };
691
693
 
692
- const stringOnly = wasm => {
693
- if (!Array.isArray(wasm[0])) return [ ...wasm, 'string_only' ];
694
- if (wasm.length === 1) return [ [ ...wasm[0], 'string_only' ] ];
695
-
696
- return [
697
- [ ...wasm[0], 'string_only|start' ],
698
- ...wasm.slice(1, -1),
699
- [ ...wasm[wasm.length - 1], 'string_only|end' ]
700
- ];
701
- }
702
-
703
694
  const performOp = (scope, op, left, right, leftType, rightType, _global = false, _name = '$undeclared', assign = false) => {
704
695
  if (op === '||' || op === '&&' || op === '??') {
705
696
  return performLogicOp(scope, op, left, right, leftType, rightType);
@@ -787,7 +778,7 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
787
778
  tmpLeft = localTmp(scope, '__tmpop_left');
788
779
  tmpRight = localTmp(scope, '__tmpop_right');
789
780
 
790
- ops.unshift(...stringOnly([
781
+ ops.unshift(
791
782
  // if left or right are string or bytestring
792
783
  ...leftType,
793
784
  ...number(TYPE_FLAGS.parity, Valtype.i32),
@@ -808,18 +799,18 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
808
799
  [ Opcodes.end ],
809
800
 
810
801
  ...setLastType(scope, TYPES.number)
811
- ]));
802
+ );
812
803
 
813
804
  // add a surrounding block
814
- startOut.push(stringOnly([ Opcodes.block, Valtype.f64 ]));
815
- endOut.unshift(stringOnly([ Opcodes.end ]));
805
+ startOut.push([ Opcodes.block, Valtype.f64 ]);
806
+ endOut.unshift([ Opcodes.end ]);
816
807
  }
817
808
 
818
809
  if ((op === '===' || op === '==' || op === '!==' || op === '!=') && (knownLeft == null && knownRight == null)) {
819
810
  tmpLeft = localTmp(scope, '__tmpop_left');
820
811
  tmpRight = localTmp(scope, '__tmpop_right');
821
812
 
822
- ops.unshift(...stringOnly([
813
+ ops.unshift(
823
814
  // if left or right are string or bytestring
824
815
  ...leftType,
825
816
  ...number(TYPE_FLAGS.parity, Valtype.i32),
@@ -839,18 +830,18 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
839
830
  ...(op === '!==' || op === '!=' ? [ [ Opcodes.i32_eqz ] ] : []),
840
831
  [ Opcodes.br, 1 ],
841
832
  [ Opcodes.end ]
842
- ]));
833
+ );
843
834
 
844
835
  // add a surrounding block
845
- startOut.push(stringOnly([ Opcodes.block, Valtype.i32 ]));
846
- endOut.unshift(stringOnly([ Opcodes.end ]));
836
+ startOut.push([ Opcodes.block, Valtype.i32 ]);
837
+ endOut.unshift([ Opcodes.end ]);
847
838
  }
848
839
 
849
840
  return finalize([
850
841
  ...left,
851
- ...(tmpLeft != null ? stringOnly([ [ Opcodes.local_tee, tmpLeft ] ]) : []),
842
+ ...(tmpLeft != null ? [ [ Opcodes.local_tee, tmpLeft ] ] : []),
852
843
  ...right,
853
- ...(tmpRight != null ? stringOnly([ [ Opcodes.local_tee, tmpRight ] ]) : []),
844
+ ...(tmpRight != null ? [ [ Opcodes.local_tee, tmpRight ] ] : []),
854
845
  ...ops
855
846
  ]);
856
847
  };
@@ -944,7 +935,6 @@ const asmFuncToAsm = (scope, func) => {
944
935
  builtin: (n, offset = false) => {
945
936
  let idx = funcIndex[n] ?? importedFuncs[n];
946
937
  if (idx == null && builtinFuncs[n]) {
947
- // console.log(scope.name, '->', n);
948
938
  includeBuiltin(scope, n);
949
939
  idx = funcIndex[n];
950
940
  }
@@ -1002,11 +992,21 @@ const asmFuncToAsm = (scope, func) => {
1002
992
  }
1003
993
 
1004
994
  return scope.locals[name].idx;
995
+ },
996
+ t: (types, wasm) => {
997
+ if (types.some(x => usedTypes.has(x))) {
998
+ return wasm();
999
+ } else {
1000
+ return [ [ null, () => {
1001
+ if (types.some(x => usedTypes.has(x))) return wasm();
1002
+ return [];
1003
+ } ] ];
1004
+ }
1005
1005
  }
1006
1006
  });
1007
1007
  };
1008
1008
 
1009
- const asmFunc = (name, { wasm, params = [], typedParams = false, locals: localTypes = [], globals: globalTypes = [], globalInits = [], returns = [], returnType, localNames = [], globalNames = [], data: _data = [], table = false, constr = false, hasRestArgument = false } = {}) => {
1009
+ const asmFunc = (name, { wasm, params = [], typedParams = false, locals: localTypes = [], globals: globalTypes = [], globalInits = [], returns = [], returnType, localNames = [], globalNames = [], data: _data = [], table = false, constr = false, hasRestArgument = false, usedTypes = [] } = {}) => {
1010
1010
  if (wasm == null) { // called with no builtin
1011
1011
  log.warning('codegen', `${name} has no built-in!`);
1012
1012
  wasm = [];
@@ -1080,6 +1080,8 @@ const asmFunc = (name, { wasm, params = [], typedParams = false, locals: localTy
1080
1080
 
1081
1081
  if (hasRestArgument) func.hasRestArgument = true;
1082
1082
 
1083
+ for (const x of usedTypes) typeUsed(func, x);
1084
+
1083
1085
  func.wasm = wasm;
1084
1086
 
1085
1087
  return func;
@@ -1161,6 +1163,8 @@ const getType = (scope, _name) => {
1161
1163
  };
1162
1164
 
1163
1165
  const setType = (scope, _name, type) => {
1166
+ typeUsed(scope, knownType(scope, type));
1167
+
1164
1168
  const name = mapName(_name);
1165
1169
 
1166
1170
  const out = typeof type === 'number' ? number(type, Valtype.i32) : type;
@@ -1200,10 +1204,13 @@ const getLastType = scope => {
1200
1204
  return [ [ Opcodes.local_get, localTmp(scope, '#last_type', Valtype.i32) ] ];
1201
1205
  };
1202
1206
 
1203
- const setLastType = (scope, type = []) => [
1204
- ...(typeof type === 'number' ? number(type, Valtype.i32) : type),
1205
- [ Opcodes.local_set, localTmp(scope, '#last_type', Valtype.i32) ]
1206
- ];
1207
+ const setLastType = (scope, type = []) => {
1208
+ typeUsed(scope, knownType(scope, type));
1209
+ return [
1210
+ ...(typeof type === 'number' ? number(type, Valtype.i32) : type),
1211
+ [ Opcodes.local_set, localTmp(scope, '#last_type', Valtype.i32) ]
1212
+ ];
1213
+ };
1207
1214
 
1208
1215
  const getNodeType = (scope, node) => {
1209
1216
  let guess = null;
@@ -1426,6 +1433,8 @@ const getNodeType = (scope, node) => {
1426
1433
  const out = typeof ret === 'number' ? number(ret, Valtype.i32) : ret;
1427
1434
  if (guess != null) out.guess = typeof guess === 'number' ? number(guess, Valtype.i32) : guess;
1428
1435
 
1436
+ typeUsed(scope, knownType(scope, out));
1437
+
1429
1438
  return out;
1430
1439
  };
1431
1440
 
@@ -1468,7 +1477,7 @@ const countLeftover = wasm => {
1468
1477
 
1469
1478
  if (depth === 0)
1470
1479
  if ([Opcodes.throw, Opcodes.drop, Opcodes.local_set, Opcodes.global_set].includes(inst[0])) count--;
1471
- else if ([null, Opcodes.i32_eqz, Opcodes.i64_eqz, 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)) {}
1480
+ else if ([Opcodes.i32_eqz, Opcodes.i64_eqz, 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)) {}
1472
1481
  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++;
1473
1482
  else if ([Opcodes.i32_store, Opcodes.i64_store, Opcodes.f64_store, Opcodes.f32_store, Opcodes.i32_store16, Opcodes.i32_store8].includes(inst[0])) count -= 2;
1474
1483
  else if (inst[0] === Opcodes.memory_copy[0] && (inst[1] === Opcodes.memory_copy[1] || inst[1] === Opcodes.memory_init[1])) count -= 3;
@@ -1949,7 +1958,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1949
1958
  const type = TYPES[x.split('_prototype_')[0].slice(2).toLowerCase()];
1950
1959
  if (type == null) continue;
1951
1960
 
1952
- protoBC[type] = generate(scope, {
1961
+ protoBC[type] = () => generate(scope, {
1953
1962
  type: 'CallExpression',
1954
1963
  callee: {
1955
1964
  type: 'Identifier',
@@ -1988,8 +1997,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1988
1997
  const usePointerCache = !Object.values(protoCands).every(x => x.noPointerCache === true);
1989
1998
  const getPointer = usePointerCache ? [ [ Opcodes.local_get, pointerLocal ] ] : rawPointer;
1990
1999
 
1991
- let allOptUnused = true;
1992
- let lengthI32CacheUsed = false;
2000
+ const useLengthCache = true; // basically every prototype uses it
1993
2001
  for (const x in protoCands) {
1994
2002
  const protoFunc = protoCands[x];
1995
2003
  if (protoFunc.noArgRetLength && decl.arguments.length === 0) {
@@ -2000,63 +2008,64 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
2000
2008
  continue;
2001
2009
  }
2002
2010
 
2003
- const protoLocal = protoFunc.local ? localTmp(scope, `__${protoName}_tmp`, protoFunc.local) : -1;
2004
- const protoLocal2 = protoFunc.local2 ? localTmp(scope, `__${protoName}_tmp2`, protoFunc.local2) : -1;
2005
-
2006
- let optUnused = false;
2007
- const protoOut = protoFunc(getPointer, {
2008
- getCachedI32: () => {
2009
- lengthI32CacheUsed = true;
2010
- return [ [ Opcodes.local_get, lengthLocal ] ];
2011
+ protoBC[x] = () => {
2012
+ const protoLocal = protoFunc.local ? localTmp(scope, `__${protoName}_tmp`, protoFunc.local) : -1;
2013
+ const protoLocal2 = protoFunc.local2 ? localTmp(scope, `__${protoName}_tmp2`, protoFunc.local2) : -1;
2014
+
2015
+ let optUnused = false;
2016
+ const protoOut = protoFunc(getPointer, {
2017
+ getCachedI32: () => [ [ Opcodes.local_get, lengthLocal ] ],
2018
+ setCachedI32: () => [ [ Opcodes.local_set, lengthLocal ] ],
2019
+ get: () => RTArrayUtil.getLength(getPointer),
2020
+ getI32: () => RTArrayUtil.getLengthI32(getPointer),
2021
+ set: value => RTArrayUtil.setLength(getPointer, value),
2022
+ setI32: value => RTArrayUtil.setLengthI32(getPointer, value)
2011
2023
  },
2012
- setCachedI32: () => [ [ Opcodes.local_set, lengthLocal ] ],
2013
- get: () => RTArrayUtil.getLength(getPointer),
2014
- getI32: () => RTArrayUtil.getLengthI32(getPointer),
2015
- set: value => RTArrayUtil.setLength(getPointer, value),
2016
- setI32: value => RTArrayUtil.setLengthI32(getPointer, value)
2017
- }, generate(scope, decl.arguments[0] ?? DEFAULT_VALUE()), getNodeType(scope, decl.arguments[0] ?? DEFAULT_VALUE()), protoLocal, protoLocal2, (length, itemType) => {
2018
- return makeArray(scope, {
2019
- rawElements: new Array(length)
2020
- }, _global, _name, true, itemType, true);
2021
- }, () => {
2022
- optUnused = true;
2023
- return unusedValue;
2024
- });
2025
-
2026
- if (!optUnused) allOptUnused = false;
2027
-
2028
- protoBC[x] = [
2029
- [ Opcodes.block, unusedValue && optUnused ? Blocktype.void : valtypeBinary ],
2030
- ...protoOut,
2031
- ...(unusedValue && optUnused ? [] : (protoFunc.returnType != null ? setLastType(scope, protoFunc.returnType) : setLastType(scope))),
2032
- [ Opcodes.end ]
2033
- ];
2024
+ generate(scope, decl.arguments[0] ?? DEFAULT_VALUE()),
2025
+ getNodeType(scope, decl.arguments[0] ?? DEFAULT_VALUE()),
2026
+ protoLocal, protoLocal2,
2027
+ (length, itemType) => {
2028
+ return makeArray(scope, {
2029
+ rawElements: new Array(length)
2030
+ }, _global, _name, true, itemType, true);
2031
+ },
2032
+ () => {
2033
+ optUnused = true;
2034
+ return unusedValue;
2035
+ });
2036
+
2037
+ return [
2038
+ [ Opcodes.block, unusedValue ? Blocktype.void : valtypeBinary ],
2039
+ ...protoOut,
2040
+ ...(unusedValue && optUnused ? [] : (protoFunc.returnType != null ? setLastType(scope, protoFunc.returnType) : setLastType(scope))),
2041
+ ...(unusedValue && !optUnused ? [ [ Opcodes.drop ] ] : []),
2042
+ [ Opcodes.end ]
2043
+ ];
2044
+ };
2034
2045
  }
2035
2046
 
2036
- // todo: if some cands use optUnused and some don't, we will probably crash
2037
-
2038
2047
  return [
2039
2048
  ...(usePointerCache ? [
2040
2049
  ...rawPointer,
2041
2050
  [ Opcodes.local_set, pointerLocal ],
2042
2051
  ] : []),
2043
2052
 
2044
- ...(!lengthI32CacheUsed ? [] : [
2053
+ ...(useLengthCache ? [
2045
2054
  ...RTArrayUtil.getLengthI32(getPointer),
2046
2055
  [ Opcodes.local_set, lengthLocal ],
2047
- ]),
2056
+ ] : []),
2048
2057
 
2049
2058
  ...typeSwitch(scope, getNodeType(scope, target), {
2050
2059
  ...protoBC,
2051
2060
 
2052
2061
  // TODO: error better
2053
- default: internalThrow(scope, 'TypeError', `'${protoName}' proto func tried to be called on a type without an impl`)
2054
- }, allOptUnused && unusedValue ? Blocktype.void : valtypeBinary),
2062
+ default: internalThrow(scope, 'TypeError', `'${protoName}' proto func tried to be called on a type without an impl`, !unusedValue)
2063
+ }, unusedValue ? Blocktype.void : valtypeBinary),
2055
2064
  ];
2056
2065
  }
2057
2066
 
2058
2067
  if (Object.keys(protoBC).length > 0) {
2059
- let def = internalThrow(scope, 'TypeError', `'${protoName}' proto func tried to be called on a type without an impl`);
2068
+ let def = internalThrow(scope, 'TypeError', `'${protoName}' proto func tried to be called on a type without an impl`, true);
2060
2069
 
2061
2070
  // fallback to object prototype impl as a basic prototype chain hack
2062
2071
  if (protoBC[TYPES.object]) def = protoBC[TYPES.object];
@@ -2193,7 +2202,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
2193
2202
  [ Opcodes.local_set, localTmp(scope, '#indirect_callee') ],
2194
2203
 
2195
2204
  ...typeSwitch(scope, getNodeType(scope, decl.callee), {
2196
- [TYPES.function]: [
2205
+ [TYPES.function]: () => [
2197
2206
  ...out,
2198
2207
 
2199
2208
  [ Opcodes.local_get, localTmp(scope, '#indirect_callee') ],
@@ -2332,7 +2341,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
2332
2341
  [ Opcodes.local_set, localTmp(scope, '#indirect_callee') ],
2333
2342
 
2334
2343
  ...typeSwitch(scope, getNodeType(scope, callee), {
2335
- [TYPES.function]: [
2344
+ [TYPES.function]: () => [
2336
2345
  ...out,
2337
2346
 
2338
2347
  [ Opcodes.local_get, localTmp(scope, '#indirect_callee') ],
@@ -2584,6 +2593,8 @@ const unhackName = name => {
2584
2593
  };
2585
2594
 
2586
2595
  const knownType = (scope, type) => {
2596
+ if (typeof type === 'number') return type;
2597
+
2587
2598
  if (type.length === 1 && type[0][0] === Opcodes.i32_const) {
2588
2599
  return read_signedLEB128(type[0].slice(1));
2589
2600
  }
@@ -2692,6 +2703,16 @@ const brTable = (input, bc, returns) => {
2692
2703
 
2693
2704
  let typeswitchDepth = 0;
2694
2705
 
2706
+ let usedTypes = new Set();
2707
+ const typeUsed = (scope, x) => {
2708
+ if (x == null) return;
2709
+ usedTypes.add(x);
2710
+
2711
+ // console.log(scope.name, TYPE_NAMES[x]);
2712
+
2713
+ scope.usedTypes ??= new Set();
2714
+ scope.usedTypes.add(x);
2715
+ };
2695
2716
  const typeSwitch = (scope, type, bc, returns = valtypeBinary, fallthrough = false) => {
2696
2717
  const known = knownType(scope, type);
2697
2718
  if (known != null) {
@@ -2704,13 +2725,14 @@ const typeSwitch = (scope, type, bc, returns = valtypeBinary, fallthrough = fals
2704
2725
  }
2705
2726
 
2706
2727
  if (Array.isArray(type)) {
2707
- if (type.includes(known)) return wasm;
2708
- } else if (type === known) return wasm;
2728
+ if (type.includes(known)) return typeof wasm === 'function' ? wasm() : wasm;
2729
+ } else if (type === known) return typeof wasm === 'function' ? wasm() : wasm;
2709
2730
  }
2710
2731
 
2711
- return def;
2732
+ return typeof def === 'function' ? def() : def;
2712
2733
  } else {
2713
- return bc[known] ?? bc.default;
2734
+ const wasm = bc[known] ?? bc.default;
2735
+ return typeof wasm === 'function' ? wasm() : wasm;
2714
2736
  }
2715
2737
  }
2716
2738
 
@@ -2720,7 +2742,7 @@ const typeSwitch = (scope, type, bc, returns = valtypeBinary, fallthrough = fals
2720
2742
  }
2721
2743
 
2722
2744
  const tmp = localTmp(scope, `#typeswitch_tmp${++typeswitchDepth}${Prefs.typeswitchUniqueTmp ? uniqId() : ''}`, Valtype.i32);
2723
- const out = [
2745
+ let out = [
2724
2746
  ...type,
2725
2747
  [ Opcodes.local_set, tmp ],
2726
2748
  [ Opcodes.block, returns ]
@@ -2732,46 +2754,67 @@ const typeSwitch = (scope, type, bc, returns = valtypeBinary, fallthrough = fals
2732
2754
  if (!Array.isArray(bc)) {
2733
2755
  def = bc.default;
2734
2756
  bc = Object.entries(bc);
2757
+
2758
+ // turn keys back into numbers from keys
2759
+ for (const x of bc) {
2760
+ if (x[0] !== 'default') x[0] = +x[0];
2761
+ }
2735
2762
  }
2736
2763
 
2737
2764
  for (let i = 0; i < bc.length; i++) {
2738
- let [ type, wasm ] = bc[i];
2739
- if (type === 'default') {
2740
- def = wasm;
2765
+ let [ types, wasm ] = bc[i];
2766
+ if (types === 'default') {
2767
+ def = typeof wasm === 'function' ? wasm() : wasm;
2741
2768
  continue;
2742
2769
  }
2770
+ if (!Array.isArray(types)) types = [ types ];
2771
+
2772
+ const add = () => {
2773
+ if (typeof wasm === 'function') wasm = wasm();
2743
2774
 
2744
- if (Array.isArray(type)) {
2745
- for (let j = 0; j < type.length; j++) {
2775
+ for (let j = 0; j < types.length; j++) {
2746
2776
  out.push(
2747
2777
  [ Opcodes.local_get, tmp ],
2748
- ...number(type[j], Valtype.i32),
2778
+ ...number(types[j], Valtype.i32),
2749
2779
  [ Opcodes.i32_eq ]
2750
2780
  );
2751
2781
 
2752
2782
  if (j > 0) out.push([ Opcodes.i32_or ]);
2753
2783
  }
2754
- } else {
2784
+
2755
2785
  out.push(
2756
- [ Opcodes.local_get, tmp ],
2757
- ...number(type, Valtype.i32),
2758
- [ Opcodes.i32_eq ]
2786
+ [ Opcodes.if, Blocktype.void ],
2787
+ ...wasm,
2788
+ ...(fallthrough ? [] : [ [ Opcodes.br, 1 ] ]),
2789
+ [ Opcodes.end ]
2759
2790
  );
2760
- }
2791
+ };
2761
2792
 
2762
- out.push(
2763
- [ Opcodes.if, Blocktype.void, `TYPESWITCH|${Array.isArray(type) ? type.map(t => TYPE_NAMES[t]).join(',') : TYPE_NAMES[type]}` ],
2764
- ...wasm,
2765
- ...(fallthrough ? [] : [ [ Opcodes.br, 1 ] ]),
2766
- [ Opcodes.end ]
2767
- );
2793
+ if (globalThis.precompile) {
2794
+ // just magic precompile things™
2795
+ out.push([ null, 'typeswitch case start', types ]);
2796
+ add();
2797
+ out.push([ null, 'typeswitch case end' ]);
2798
+ } else {
2799
+ if (types.some(x => usedTypes.has(x))) {
2800
+ // type already used, just add it now
2801
+ add();
2802
+ } else {
2803
+ // type not used, add callback
2804
+ out.push([ null, () => {
2805
+ out = [];
2806
+ if (types.some(x => usedTypes.has(x))) add();
2807
+ return out;
2808
+ }]);
2809
+ }
2810
+ }
2768
2811
  }
2769
2812
 
2770
2813
  // default
2771
2814
  if (def) out.push(...def);
2772
2815
  else if (returns !== Blocktype.void) out.push(...number(0, returns));
2773
2816
 
2774
- out.push([ Opcodes.end, 'TYPESWITCH_end' ]);
2817
+ out.push([ Opcodes.end ]);
2775
2818
 
2776
2819
  typeswitchDepth--;
2777
2820
 
@@ -3268,7 +3311,7 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
3268
3311
 
3269
3312
  // todo: review last type usage here
3270
3313
  ...typeSwitch(scope, getNodeType(scope, object), {
3271
- [TYPES.array]: [
3314
+ [TYPES.array]: () => [
3272
3315
  ...objectWasm,
3273
3316
  Opcodes.i32_to_u,
3274
3317
 
@@ -3300,7 +3343,7 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
3300
3343
  ],
3301
3344
 
3302
3345
  ...wrapBC({
3303
- [TYPES.uint8array]: [
3346
+ [TYPES.uint8array]: () => [
3304
3347
  [ Opcodes.i32_add ],
3305
3348
  ...(op === '=' ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
3306
3349
 
@@ -3314,7 +3357,7 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
3314
3357
  Opcodes.i32_to_u,
3315
3358
  [ Opcodes.i32_store8, 0, 4 ]
3316
3359
  ],
3317
- [TYPES.uint8clampedarray]: [
3360
+ [TYPES.uint8clampedarray]: () => [
3318
3361
  [ Opcodes.i32_add ],
3319
3362
  ...(op === '=' ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
3320
3363
 
@@ -3332,7 +3375,7 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
3332
3375
  Opcodes.i32_to_u,
3333
3376
  [ Opcodes.i32_store8, 0, 4 ]
3334
3377
  ],
3335
- [TYPES.int8array]: [
3378
+ [TYPES.int8array]: () => [
3336
3379
  [ Opcodes.i32_add ],
3337
3380
  ...(op === '=' ? [] : [ [ Opcodes.local_tee, pointerTmp ] ]),
3338
3381
 
@@ -3346,7 +3389,7 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
3346
3389
  Opcodes.i32_to,
3347
3390
  [ Opcodes.i32_store8, 0, 4 ]
3348
3391
  ],
3349
- [TYPES.uint16array]: [
3392
+ [TYPES.uint16array]: () => [
3350
3393
  ...number(2, Valtype.i32),
3351
3394
  [ Opcodes.i32_mul ],
3352
3395
  [ Opcodes.i32_add ],
@@ -3362,7 +3405,7 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
3362
3405
  Opcodes.i32_to_u,
3363
3406
  [ Opcodes.i32_store16, 0, 4 ]
3364
3407
  ],
3365
- [TYPES.int16array]: [
3408
+ [TYPES.int16array]: () => [
3366
3409
  ...number(2, Valtype.i32),
3367
3410
  [ Opcodes.i32_mul ],
3368
3411
  [ Opcodes.i32_add ],
@@ -3378,7 +3421,7 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
3378
3421
  Opcodes.i32_to,
3379
3422
  [ Opcodes.i32_store16, 0, 4 ]
3380
3423
  ],
3381
- [TYPES.uint32array]: [
3424
+ [TYPES.uint32array]: () => [
3382
3425
  ...number(4, Valtype.i32),
3383
3426
  [ Opcodes.i32_mul ],
3384
3427
  [ Opcodes.i32_add ],
@@ -3394,7 +3437,7 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
3394
3437
  Opcodes.i32_to_u,
3395
3438
  [ Opcodes.i32_store, 0, 4 ]
3396
3439
  ],
3397
- [TYPES.int32array]: [
3440
+ [TYPES.int32array]: () => [
3398
3441
  ...number(4, Valtype.i32),
3399
3442
  [ Opcodes.i32_mul ],
3400
3443
  [ Opcodes.i32_add ],
@@ -3410,7 +3453,7 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
3410
3453
  Opcodes.i32_to,
3411
3454
  [ Opcodes.i32_store, 0, 4 ]
3412
3455
  ],
3413
- [TYPES.float32array]: [
3456
+ [TYPES.float32array]: () => [
3414
3457
  ...number(4, Valtype.i32),
3415
3458
  [ Opcodes.i32_mul ],
3416
3459
  [ Opcodes.i32_add ],
@@ -3426,7 +3469,7 @@ const generateAssign = (scope, decl, _global, _name, valueUnused = false) => {
3426
3469
  [ Opcodes.f32_demote_f64 ],
3427
3470
  [ Opcodes.f32_store, 0, 4 ]
3428
3471
  ],
3429
- [TYPES.float64array]: [
3472
+ [TYPES.float64array]: () => [
3430
3473
  ...number(8, Valtype.i32),
3431
3474
  [ Opcodes.i32_mul ],
3432
3475
  [ Opcodes.i32_add ],
@@ -3662,16 +3705,15 @@ const generateUnary = (scope, decl) => {
3662
3705
  disposeLeftover(out);
3663
3706
 
3664
3707
  out.push(...typeSwitch(scope, overrideType ?? getNodeType(scope, decl.argument), [
3665
- [ TYPES.number, makeString(scope, 'number', false, '#typeof_result') ],
3666
- [ TYPES.boolean, makeString(scope, 'boolean', false, '#typeof_result') ],
3667
- [ TYPES.string, makeString(scope, 'string', false, '#typeof_result') ],
3668
- [ [ TYPES.undefined, TYPES.empty ], makeString(scope, 'undefined', false, '#typeof_result') ],
3669
- [ TYPES.function, makeString(scope, 'function', false, '#typeof_result') ],
3670
- [ TYPES.symbol, makeString(scope, 'symbol', false, '#typeof_result') ],
3671
- [ TYPES.bytestring, makeString(scope, 'string', false, '#typeof_result') ],
3708
+ [ TYPES.number, () => makeString(scope, 'number', false, '#typeof_result') ],
3709
+ [ TYPES.boolean, () => makeString(scope, 'boolean', false, '#typeof_result') ],
3710
+ [ [ TYPES.string, TYPES.bytestring ], () => makeString(scope, 'string', false, '#typeof_result') ],
3711
+ [ [ TYPES.undefined, TYPES.empty ], () => makeString(scope, 'undefined', false, '#typeof_result') ],
3712
+ [ TYPES.function, () => makeString(scope, 'function', false, '#typeof_result') ],
3713
+ [ TYPES.symbol, () => makeString(scope, 'symbol', false, '#typeof_result') ],
3672
3714
 
3673
3715
  // object and internal types
3674
- [ 'default', makeString(scope, 'object', false, '#typeof_result') ],
3716
+ [ 'default', () => makeString(scope, 'object', false, '#typeof_result') ],
3675
3717
  ]));
3676
3718
 
3677
3719
  return out;
@@ -3921,7 +3963,7 @@ const generateForOf = (scope, decl) => {
3921
3963
  // set type for local
3922
3964
  // todo: optimize away counter and use end pointer
3923
3965
  out.push(...typeSwitch(scope, iterType, {
3924
- [TYPES.array]: [
3966
+ [TYPES.array]: () => [
3925
3967
  [ Opcodes.loop, Blocktype.void ],
3926
3968
 
3927
3969
  [ Opcodes.local_get, pointer ],
@@ -3962,7 +4004,7 @@ const generateForOf = (scope, decl) => {
3962
4004
  [ Opcodes.end ]
3963
4005
  ],
3964
4006
 
3965
- [TYPES.string]: [
4007
+ [TYPES.string]: () => [
3966
4008
  ...setType(scope, tmpName, TYPES.string),
3967
4009
 
3968
4010
  // allocate out string
@@ -4017,7 +4059,7 @@ const generateForOf = (scope, decl) => {
4017
4059
  [ Opcodes.end ],
4018
4060
  [ Opcodes.end ]
4019
4061
  ],
4020
- [TYPES.bytestring]: [
4062
+ [TYPES.bytestring]: () => [
4021
4063
  ...setType(scope, tmpName, TYPES.bytestring),
4022
4064
 
4023
4065
  // allocate out string
@@ -4069,7 +4111,7 @@ const generateForOf = (scope, decl) => {
4069
4111
  [ Opcodes.end ]
4070
4112
  ],
4071
4113
 
4072
- [TYPES.set]: [
4114
+ [TYPES.set]: () => [
4073
4115
  [ Opcodes.loop, Blocktype.void ],
4074
4116
 
4075
4117
  [ Opcodes.local_get, pointer ],
@@ -4111,25 +4153,25 @@ const generateForOf = (scope, decl) => {
4111
4153
  ],
4112
4154
 
4113
4155
  ...wrapBC({
4114
- [TYPES.uint8array]: [
4156
+ [TYPES.uint8array]: () => [
4115
4157
  [ Opcodes.i32_add ],
4116
4158
 
4117
4159
  [ Opcodes.i32_load8_u, 0, 4 ],
4118
4160
  Opcodes.i32_from_u
4119
4161
  ],
4120
- [TYPES.uint8clampedarray]: [
4162
+ [TYPES.uint8clampedarray]: () => [
4121
4163
  [ Opcodes.i32_add ],
4122
4164
 
4123
4165
  [ Opcodes.i32_load8_u, 0, 4 ],
4124
4166
  Opcodes.i32_from_u
4125
4167
  ],
4126
- [TYPES.int8array]: [
4168
+ [TYPES.int8array]: () => [
4127
4169
  [ Opcodes.i32_add ],
4128
4170
 
4129
4171
  [ Opcodes.i32_load8_s, 0, 4 ],
4130
4172
  Opcodes.i32_from
4131
4173
  ],
4132
- [TYPES.uint16array]: [
4174
+ [TYPES.uint16array]: () => [
4133
4175
  ...number(2, Valtype.i32),
4134
4176
  [ Opcodes.i32_mul ],
4135
4177
  [ Opcodes.i32_add ],
@@ -4137,7 +4179,7 @@ const generateForOf = (scope, decl) => {
4137
4179
  [ Opcodes.i32_load16_u, 0, 4 ],
4138
4180
  Opcodes.i32_from_u
4139
4181
  ],
4140
- [TYPES.int16array]: [
4182
+ [TYPES.int16array]: () => [
4141
4183
  ...number(2, Valtype.i32),
4142
4184
  [ Opcodes.i32_mul ],
4143
4185
  [ Opcodes.i32_add ],
@@ -4145,7 +4187,7 @@ const generateForOf = (scope, decl) => {
4145
4187
  [ Opcodes.i32_load16_s, 0, 4 ],
4146
4188
  Opcodes.i32_from
4147
4189
  ],
4148
- [TYPES.uint32array]: [
4190
+ [TYPES.uint32array]: () => [
4149
4191
  ...number(4, Valtype.i32),
4150
4192
  [ Opcodes.i32_mul ],
4151
4193
  [ Opcodes.i32_add ],
@@ -4153,7 +4195,7 @@ const generateForOf = (scope, decl) => {
4153
4195
  [ Opcodes.i32_load, 0, 4 ],
4154
4196
  Opcodes.i32_from_u
4155
4197
  ],
4156
- [TYPES.int32array]: [
4198
+ [TYPES.int32array]: () => [
4157
4199
  ...number(4, Valtype.i32),
4158
4200
  [ Opcodes.i32_mul ],
4159
4201
  [ Opcodes.i32_add ],
@@ -4161,7 +4203,7 @@ const generateForOf = (scope, decl) => {
4161
4203
  [ Opcodes.i32_load, 0, 4 ],
4162
4204
  Opcodes.i32_from
4163
4205
  ],
4164
- [TYPES.float32array]: [
4206
+ [TYPES.float32array]: () => [
4165
4207
  ...number(4, Valtype.i32),
4166
4208
  [ Opcodes.i32_mul ],
4167
4209
  [ Opcodes.i32_add ],
@@ -4169,7 +4211,7 @@ const generateForOf = (scope, decl) => {
4169
4211
  [ Opcodes.f32_load, 0, 4 ],
4170
4212
  [ Opcodes.f64_promote_f32 ]
4171
4213
  ],
4172
- [TYPES.float64array]: [
4214
+ [TYPES.float64array]: () => [
4173
4215
  ...number(8, Valtype.i32),
4174
4216
  [ Opcodes.i32_mul ],
4175
4217
  [ Opcodes.i32_add ],
@@ -4348,7 +4390,7 @@ const generateForIn = (scope, decl) => {
4348
4390
  [TYPES.object]: out,
4349
4391
 
4350
4392
  // wrap for of object.keys
4351
- default: generate(scope, {
4393
+ default: () => generate(scope, {
4352
4394
  type: 'ForOfStatement',
4353
4395
  left: decl.left,
4354
4396
  body: decl.body,
@@ -4414,8 +4456,7 @@ const generateSwitch = (scope, decl) => {
4414
4456
  types.push(type);
4415
4457
 
4416
4458
  if (consequent.length !== 0) {
4417
- const o = generate(scope, { type: 'BlockStatement', body: consequent });
4418
- bc.push([ types, o ]);
4459
+ bc.push([ types, () => generate(scope, { type: 'BlockStatement', body: consequent }) ]);
4419
4460
  types = [];
4420
4461
  }
4421
4462
  }
@@ -4685,11 +4726,6 @@ const allocPage = (scope, reason, type) => {
4685
4726
  const ptr = i => i === 0 ? 16 : (i * pageSize);
4686
4727
  if (pages.has(reason)) return ptr(pages.get(reason).ind);
4687
4728
 
4688
- if (reason.startsWith('array:')) pages.hasArray = true;
4689
- if (reason.startsWith('string:')) pages.hasString = true;
4690
- if (reason.startsWith('bytestring:')) pages.hasByteString = true;
4691
- if (reason.includes('string:')) pages.hasAnyString = true;
4692
-
4693
4729
  const ind = pages.size;
4694
4730
  pages.set(reason, { ind, type });
4695
4731
 
@@ -4771,14 +4807,6 @@ const printStaticStr = str => {
4771
4807
  };
4772
4808
 
4773
4809
  const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty = false, itemType = valtype, intOut = false, typed = false) => {
4774
- if (itemType !== 'i16' && itemType !== 'i8') {
4775
- pages.hasArray = true;
4776
- } else {
4777
- pages.hasAnyString = true;
4778
- if (itemType === 'i8') pages.hasByteString = true;
4779
- else pages.hasString = true;
4780
- }
4781
-
4782
4810
  const out = [];
4783
4811
 
4784
4812
  const uniqueName = name === '$undeclared' ? name + uniqId() : name;
@@ -5121,11 +5149,19 @@ const withType = (scope, wasm, type) => [
5121
5149
  const wrapBC = (bc, { prelude = [], postlude = [] } = {}) => {
5122
5150
  const out = {};
5123
5151
  for (const x in bc) {
5124
- out[x] = [
5125
- ...prelude,
5126
- ...bc[x],
5127
- ...postlude
5128
- ];
5152
+ if (typeof bc[x] === 'function') {
5153
+ out[x] = () => [
5154
+ ...prelude,
5155
+ ...bc[x](),
5156
+ ...postlude
5157
+ ];
5158
+ } else {
5159
+ out[x] = [
5160
+ ...prelude,
5161
+ ...bc[x],
5162
+ ...postlude
5163
+ ];
5164
+ }
5129
5165
  }
5130
5166
 
5131
5167
  return out;
@@ -5316,12 +5352,12 @@ const generateMember = (scope, decl, _global, _name, _objectWasm = undefined) =>
5316
5352
  const propertyWasm = [ [ Opcodes.local_get, localTmp(scope, '#member_prop') ] ];
5317
5353
 
5318
5354
  const out = typeSwitch(scope, getNodeType(scope, object), {
5319
- [TYPES.array]: [
5355
+ [TYPES.array]: () => [
5320
5356
  ...loadArray(scope, objectWasm, propertyWasm),
5321
5357
  ...setLastType(scope)
5322
5358
  ],
5323
5359
 
5324
- [TYPES.string]: [
5360
+ [TYPES.string]: () => [
5325
5361
  // allocate out string
5326
5362
  [ Opcodes.call, includeBuiltin(scope, '__Porffor_allocate').index ],
5327
5363
  [ Opcodes.local_tee, localTmp(scope, '#member_allocd', Valtype.i32) ],
@@ -5355,7 +5391,7 @@ const generateMember = (scope, decl, _global, _name, _objectWasm = undefined) =>
5355
5391
  ...setLastType(scope, TYPES.string)
5356
5392
  ],
5357
5393
 
5358
- [TYPES.bytestring]: [
5394
+ [TYPES.bytestring]: () => [
5359
5395
  // allocate out string
5360
5396
  [ Opcodes.call, includeBuiltin(scope, '__Porffor_allocate').index ],
5361
5397
  [ Opcodes.local_tee, localTmp(scope, '#member_allocd', Valtype.i32) ],
@@ -5467,7 +5503,7 @@ const generateMember = (scope, decl, _global, _name, _objectWasm = undefined) =>
5467
5503
  [TYPES.undefined]: internalThrow(scope, 'TypeError', 'Cannot read property of undefined', true),
5468
5504
 
5469
5505
  // default: internalThrow(scope, 'TypeError', 'Unsupported member expression object', true)
5470
- default: [
5506
+ default: () => [
5471
5507
  ...objectWasm,
5472
5508
  Opcodes.i32_to_u,
5473
5509
  ...getNodeType(scope, object),
@@ -6186,6 +6222,7 @@ export default program => {
6186
6222
  data = [];
6187
6223
  currentFuncIndex = importedFuncs.length;
6188
6224
  typeswitchDepth = 0;
6225
+ usedTypes = new Set([ TYPES.empty, TYPES.undefined, TYPES.number, TYPES.boolean, TYPES.function ]);
6189
6226
 
6190
6227
  const valtypeInd = ['i32', 'i64', 'f64'].indexOf(valtype);
6191
6228
 
@@ -6226,8 +6263,6 @@ export default program => {
6226
6263
 
6227
6264
  const [ main ] = generateFunc({}, program);
6228
6265
 
6229
- delete globals['#ind'];
6230
-
6231
6266
  // if wanted and blank main func and other exports, remove it
6232
6267
  if (Prefs.rmBlankMain && main.wasm.length === 0 && funcs.some(x => x.export)) funcs.splice(main.index - importedFuncs.length, 1);
6233
6268
 
@@ -6235,7 +6270,16 @@ export default program => {
6235
6270
  // todo: these should just be deleted once able
6236
6271
  for (let i = 0; i < funcs.length; i++) {
6237
6272
  const f = funcs[i];
6238
- if (f.internal || f.wasm) {
6273
+ if (f.wasm) {
6274
+ // run callbacks
6275
+ const wasm = f.wasm;
6276
+ for (let j = 0; j < wasm.length; j++) {
6277
+ const i = wasm[j];
6278
+ if (i[0] === null && typeof i[1] === 'function') {
6279
+ wasm.splice(j, 1, ...i[1]());
6280
+ }
6281
+ }
6282
+
6239
6283
  continue;
6240
6284
  }
6241
6285
 
@@ -6282,5 +6326,9 @@ export default program => {
6282
6326
  // }
6283
6327
  // }
6284
6328
 
6329
+ delete globals['#ind'];
6330
+
6331
+ // console.log([...usedTypes].map(x => TYPE_NAMES[x]));
6332
+
6285
6333
  return { funcs, globals, tags, exceptions, pages, data };
6286
6334
  };