porffor 0.2.0-1afe9b8 → 0.2.0-240f1c8

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.
@@ -7,6 +7,7 @@ import { number, i32x4, enforceOneByte, enforceTwoBytes, enforceFourBytes, enfor
7
7
  import { log } from "./log.js";
8
8
  import parse from "./parse.js";
9
9
  import * as Rhemyn from "../rhemyn/compile.js";
10
+ import Prefs from './prefs.js';
10
11
 
11
12
  let globals = {};
12
13
  let globalInd = 0;
@@ -160,7 +161,7 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
160
161
 
161
162
  case 'TaggedTemplateExpression': {
162
163
  const funcs = {
163
- asm: str => {
164
+ __Porffor_wasm: str => {
164
165
  let out = [];
165
166
 
166
167
  for (const line of str.split('\n')) {
@@ -168,8 +169,8 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
168
169
  if (asm[0] === '') continue; // blank
169
170
 
170
171
  if (asm[0] === 'local') {
171
- const [ name, idx, type ] = asm.slice(1);
172
- scope.locals[name] = { idx: parseInt(idx), type: Valtype[type] };
172
+ const [ name, type ] = asm.slice(1);
173
+ scope.locals[name] = { idx: scope.localInd++, type: Valtype[type] };
173
174
  continue;
174
175
  }
175
176
 
@@ -179,7 +180,7 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
179
180
  }
180
181
 
181
182
  if (asm[0] === 'memory') {
182
- allocPage('asm instrinsic');
183
+ allocPage(scope, 'asm instrinsic');
183
184
  // todo: add to store/load offset insts
184
185
  continue;
185
186
  }
@@ -188,26 +189,52 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
188
189
  if (!inst) throw new Error(`inline asm: inst ${asm[0]} not found`);
189
190
 
190
191
  if (!Array.isArray(inst)) inst = [ inst ];
191
- const immediates = asm.slice(1).map(x => parseInt(x));
192
+ const immediates = asm.slice(1).map(x => {
193
+ const int = parseInt(x);
194
+ if (Number.isNaN(int)) return scope.locals[x]?.idx;
195
+ return int;
196
+ });
192
197
 
193
198
  out.push([ ...inst, ...immediates ]);
194
199
  }
195
200
 
196
201
  return out;
197
- }
198
- }
202
+ },
203
+
204
+ __Porffor_bs: str => [
205
+ ...makeString(scope, str, undefined, undefined, true),
206
+
207
+ ...number(TYPES._bytestring, Valtype.i32),
208
+ setLastType(scope)
209
+ ],
210
+ __Porffor_s: str => [
211
+ ...makeString(scope, str, undefined, undefined, false),
212
+
213
+ ...number(TYPES.string, Valtype.i32),
214
+ setLastType(scope)
215
+ ],
216
+ };
199
217
 
200
218
  const name = decl.tag.name;
201
219
  // hack for inline asm
202
220
  if (!funcs[name]) return todo('tagged template expressions not implemented');
203
221
 
204
- const str = decl.quasi.quasis[0].value.raw;
222
+ const { quasis, expressions } = decl.quasi;
223
+ let str = quasis[0].value.raw;
224
+
225
+ for (let i = 0; i < expressions.length; i++) {
226
+ const e = expressions[i];
227
+ str += lookupName(scope, e.name)[0].idx;
228
+ str += quasis[i + 1].value.raw;
229
+ }
230
+
205
231
  return funcs[name](str);
206
232
  }
207
233
 
208
234
  default:
209
- if (decl.type.startsWith('TS')) {
210
- // ignore typescript nodes
235
+ // ignore typescript nodes
236
+ if (decl.type.startsWith('TS') ||
237
+ decl.type === 'ImportDeclaration' && decl.importKind === 'type') {
211
238
  return [];
212
239
  }
213
240
 
@@ -396,9 +423,6 @@ const concatStrings = (scope, left, right, global, name, assign) => {
396
423
  const rightLength = localTmp(scope, 'concat_right_length', Valtype.i32);
397
424
  const leftLength = localTmp(scope, 'concat_left_length', Valtype.i32);
398
425
 
399
- const aotWFA = process.argv.includes('-aot-well-formed-string-approximation');
400
- if (aotWFA) addVarMeta(name, { wellFormed: undefined });
401
-
402
426
  if (assign) {
403
427
  const pointer = arrays.get(name ?? '$undeclared');
404
428
 
@@ -636,11 +660,12 @@ const truthy = (scope, wasm, type, intIn = false, intOut = false) => {
636
660
  ...(!intIn && intOut ? [ Opcodes.i32_to_u ] : [])
637
661
  ];
638
662
 
639
- const tmp = localTmp(scope, `$logicinner_tmp${intIn ? '_int' : ''}`, intIn ? Valtype.i32 : valtypeBinary);
663
+ const useTmp = knownType(scope, type) == null;
664
+ const tmp = !useTmp && localTmp(scope, `$logicinner_tmp${intIn ? '_int' : ''}`, intIn ? Valtype.i32 : valtypeBinary);
640
665
 
641
666
  const def = [
642
667
  // if value != 0
643
- [ Opcodes.local_get, tmp ],
668
+ ...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
644
669
 
645
670
  // ...(intIn ? [ [ Opcodes.i32_eqz ] ] : [ ...Opcodes.eqz ]),
646
671
  ...(!intOut || (intIn && intOut) ? [] : [ Opcodes.i32_to_u ]),
@@ -652,7 +677,7 @@ const truthy = (scope, wasm, type, intIn = false, intOut = false) => {
652
677
 
653
678
  return [
654
679
  ...wasm,
655
- [ Opcodes.local_set, tmp ],
680
+ ...(!useTmp ? [] : [ [ Opcodes.local_set, tmp ] ]),
656
681
 
657
682
  ...typeSwitch(scope, type, {
658
683
  // [TYPES.number]: def,
@@ -661,7 +686,7 @@ const truthy = (scope, wasm, type, intIn = false, intOut = false) => {
661
686
  ...number(1, intOut ? Valtype.i32 : valtypeBinary)
662
687
  ],
663
688
  [TYPES.string]: [
664
- [ Opcodes.local_get, tmp ],
689
+ ...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
665
690
  ...(intIn ? [] : [ Opcodes.i32_to_u ]),
666
691
 
667
692
  // get length
@@ -673,7 +698,7 @@ const truthy = (scope, wasm, type, intIn = false, intOut = false) => {
673
698
  ...(intOut ? [] : [ Opcodes.i32_from_u ])
674
699
  ],
675
700
  [TYPES._bytestring]: [ // duplicate of string
676
- [ Opcodes.local_get, tmp ],
701
+ ...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
677
702
  ...(intIn ? [] : [ Opcodes.i32_to_u ]),
678
703
 
679
704
  // get length
@@ -687,10 +712,12 @@ const truthy = (scope, wasm, type, intIn = false, intOut = false) => {
687
712
  };
688
713
 
689
714
  const falsy = (scope, wasm, type, intIn = false, intOut = false) => {
690
- const tmp = localTmp(scope, `$logicinner_tmp${intIn ? '_int' : ''}`, intIn ? Valtype.i32 : valtypeBinary);
715
+ const useTmp = knownType(scope, type) == null;
716
+ const tmp = !useTmp && localTmp(scope, `$logicinner_tmp${intIn ? '_int' : ''}`, intIn ? Valtype.i32 : valtypeBinary);
717
+
691
718
  return [
692
719
  ...wasm,
693
- [ Opcodes.local_set, tmp ],
720
+ ...(!useTmp ? [] : [ [ Opcodes.local_set, tmp ] ]),
694
721
 
695
722
  ...typeSwitch(scope, type, {
696
723
  [TYPES._array]: [
@@ -698,7 +725,7 @@ const falsy = (scope, wasm, type, intIn = false, intOut = false) => {
698
725
  ...number(0, intOut ? Valtype.i32 : valtypeBinary)
699
726
  ],
700
727
  [TYPES.string]: [
701
- [ Opcodes.local_get, tmp ],
728
+ ...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
702
729
  ...(intIn ? [] : [ Opcodes.i32_to_u ]),
703
730
 
704
731
  // get length
@@ -709,7 +736,7 @@ const falsy = (scope, wasm, type, intIn = false, intOut = false) => {
709
736
  ...(intOut ? [] : [ Opcodes.i32_from_u ])
710
737
  ],
711
738
  [TYPES._bytestring]: [ // duplicate of string
712
- [ Opcodes.local_get, tmp ],
739
+ ...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
713
740
  ...(intIn ? [] : [ Opcodes.i32_to_u ]),
714
741
 
715
742
  // get length
@@ -721,7 +748,7 @@ const falsy = (scope, wasm, type, intIn = false, intOut = false) => {
721
748
  ],
722
749
  default: [
723
750
  // if value == 0
724
- [ Opcodes.local_get, tmp ],
751
+ ...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
725
752
 
726
753
  ...(intIn ? [ [ Opcodes.i32_eqz ] ] : [ ...Opcodes.eqz ]),
727
754
  ...(intOut ? [] : [ Opcodes.i32_from_u ])
@@ -731,10 +758,12 @@ const falsy = (scope, wasm, type, intIn = false, intOut = false) => {
731
758
  };
732
759
 
733
760
  const nullish = (scope, wasm, type, intIn = false, intOut = false) => {
734
- const tmp = localTmp(scope, `$logicinner_tmp${intIn ? '_int' : ''}`, intIn ? Valtype.i32 : valtypeBinary);
761
+ const useTmp = knownType(scope, type) == null;
762
+ const tmp = !useTmp && localTmp(scope, `$logicinner_tmp${intIn ? '_int' : ''}`, intIn ? Valtype.i32 : valtypeBinary);
763
+
735
764
  return [
736
765
  ...wasm,
737
- [ Opcodes.local_set, tmp ],
766
+ ...(!useTmp ? [] : [ [ Opcodes.local_set, tmp ] ]),
738
767
 
739
768
  ...typeSwitch(scope, type, {
740
769
  [TYPES.undefined]: [
@@ -743,7 +772,7 @@ const nullish = (scope, wasm, type, intIn = false, intOut = false) => {
743
772
  ],
744
773
  [TYPES.object]: [
745
774
  // object, null if == 0
746
- [ Opcodes.local_get, tmp ],
775
+ ...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
747
776
 
748
777
  ...(intIn ? [ [ Opcodes.i32_eqz ] ] : [ ...Opcodes.eqz ]),
749
778
  ...(intOut ? [] : [ Opcodes.i32_from_u ])
@@ -772,11 +801,14 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
772
801
  return performLogicOp(scope, op, left, right, leftType, rightType);
773
802
  }
774
803
 
804
+ const knownLeft = knownType(scope, leftType);
805
+ const knownRight = knownType(scope, rightType);
806
+
775
807
  const eqOp = ['==', '===', '!=', '!==', '>', '>=', '<', '<='].includes(op);
776
808
  const strictOp = op === '===' || op === '!==';
777
809
 
778
810
  const startOut = [], endOut = [];
779
- const finalise = out => startOut.concat(out, endOut);
811
+ const finalize = out => startOut.concat(out, endOut);
780
812
 
781
813
  // if strict (in)equal check types match
782
814
  if (strictOp) {
@@ -821,31 +853,32 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
821
853
  // todo: if equality op and an operand is undefined, return false
822
854
  // todo: niche null hell with 0
823
855
 
824
- // if (leftType === TYPES.string || rightType === TYPES.string) {
825
- // if (op === '+') {
826
- // // string concat (a + b)
827
- // return finalise(concatStrings(scope, left, right, _global, _name, assign));
828
- // }
829
-
830
- // // not an equality op, NaN
831
- // if (!eqOp) return finalise(number(NaN));
832
-
833
- // // else leave bool ops
834
- // // todo: convert string to number if string and number/bool
835
- // // todo: string (>|>=|<|<=) string
836
-
837
- // // string comparison
838
- // if (op === '===' || op === '==') {
839
- // return finalise(compareStrings(scope, left, right));
840
- // }
841
-
842
- // if (op === '!==' || op === '!=') {
843
- // return finalise([
844
- // ...compareStrings(scope, left, right),
845
- // [ Opcodes.i32_eqz ]
846
- // ]);
847
- // }
848
- // }
856
+ // todo: this should be dynamic but for now only static
857
+ if (knownLeft === TYPES.string || knownRight === TYPES.string) {
858
+ if (op === '+') {
859
+ // string concat (a + b)
860
+ return concatStrings(scope, left, right, _global, _name, assign);
861
+ }
862
+
863
+ // not an equality op, NaN
864
+ if (!eqOp) return number(NaN);
865
+
866
+ // else leave bool ops
867
+ // todo: convert string to number if string and number/bool
868
+ // todo: string (>|>=|<|<=) string
869
+
870
+ // string comparison
871
+ if (op === '===' || op === '==') {
872
+ return compareStrings(scope, left, right);
873
+ }
874
+
875
+ if (op === '!==' || op === '!=') {
876
+ return [
877
+ ...compareStrings(scope, left, right),
878
+ [ Opcodes.i32_eqz ]
879
+ ];
880
+ }
881
+ }
849
882
 
850
883
  let ops = operatorOpcode[valtype][op];
851
884
 
@@ -855,7 +888,7 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
855
888
  includeBuiltin(scope, builtinName);
856
889
  const idx = funcIndex[builtinName];
857
890
 
858
- return finalise([
891
+ return finalize([
859
892
  ...left,
860
893
  ...right,
861
894
  [ Opcodes.call, idx ]
@@ -870,9 +903,6 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
870
903
  let tmpLeft, tmpRight;
871
904
  // if equal op, check if strings for compareStrings
872
905
  if (op === '===' || op === '==' || op === '!==' || op === '!=') (() => {
873
- const knownLeft = knownType(scope, leftType);
874
- const knownRight = knownType(scope, rightType);
875
-
876
906
  // todo: intelligent partial skip later
877
907
  // if neither known are string, stop this madness
878
908
  if ((knownLeft != null && knownLeft !== TYPES.string) && (knownRight != null && knownRight !== TYPES.string)) {
@@ -912,7 +942,7 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
912
942
  [ Opcodes.i32_or ],
913
943
  [ Opcodes.if, Blocktype.void ],
914
944
  ...number(0, Valtype.i32),
915
- [ Opcodes.br, 1 ],
945
+ [ Opcodes.br, 2 ],
916
946
  [ Opcodes.end ],
917
947
 
918
948
  ...compareStrings(scope, [ [ Opcodes.local_get, tmpLeft ] ], [ [ Opcodes.local_get, tmpRight ] ]),
@@ -930,7 +960,7 @@ const performOp = (scope, op, left, right, leftType, rightType, _global = false,
930
960
  // }
931
961
  })();
932
962
 
933
- return finalise([
963
+ return finalize([
934
964
  ...left,
935
965
  ...(tmpLeft != null ? stringOnly([ [ Opcodes.local_tee, tmpLeft ] ]) : []),
936
966
  ...right,
@@ -947,7 +977,7 @@ const generateBinaryExp = (scope, decl, _global, _name) => {
947
977
  return out;
948
978
  };
949
979
 
950
- const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes = [], globalInits, returns, returnType, localNames = [], globalNames = [] }) => {
980
+ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes = [], globalInits, returns, returnType, localNames = [], globalNames = [], data: _data = [] }) => {
951
981
  const existing = funcs.find(x => x.name === name);
952
982
  if (existing) return existing;
953
983
 
@@ -959,6 +989,12 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
959
989
  locals[nameParam(i)] = { idx: i, type: allLocals[i] };
960
990
  }
961
991
 
992
+ for (const x of _data) {
993
+ const copy = { ...x };
994
+ copy.offset += pages.size * pageSize;
995
+ data.push(copy);
996
+ }
997
+
962
998
  if (typeof wasm === 'function') {
963
999
  const scope = {
964
1000
  name,
@@ -968,7 +1004,18 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
968
1004
  localInd: allLocals.length,
969
1005
  };
970
1006
 
971
- wasm = wasm(scope, { TYPES, TYPE_NAMES, typeSwitch, makeArray, makeString });
1007
+ wasm = wasm(scope, {
1008
+ TYPES, TYPE_NAMES, typeSwitch, makeArray, makeString, allocPage,
1009
+ builtin: name => {
1010
+ let idx = funcIndex[name] ?? importedFuncs[name];
1011
+ if (idx === undefined && builtinFuncs[name]) {
1012
+ includeBuiltin(scope, name);
1013
+ idx = funcIndex[name];
1014
+ }
1015
+
1016
+ return idx;
1017
+ }
1018
+ });
972
1019
  }
973
1020
 
974
1021
  let baseGlobalIdx, i = 0;
@@ -1073,7 +1120,10 @@ const TYPE_NAMES = {
1073
1120
  const getType = (scope, _name) => {
1074
1121
  const name = mapName(_name);
1075
1122
 
1123
+ if (typedInput && scope.locals[name]?.metadata?.type != null) return number(scope.locals[name].metadata.type, Valtype.i32);
1076
1124
  if (scope.locals[name]) return [ [ Opcodes.local_get, scope.locals[name + '#type'].idx ] ];
1125
+
1126
+ if (typedInput && globals[name]?.metadata?.type != null) return number(globals[name].metadata.type, Valtype.i32);
1077
1127
  if (globals[name]) return [ [ Opcodes.global_get, globals[name + '#type'].idx ] ];
1078
1128
 
1079
1129
  let type = TYPES.undefined;
@@ -1151,7 +1201,7 @@ const getNodeType = (scope, node) => {
1151
1201
  if (func.returnType) return func.returnType;
1152
1202
  }
1153
1203
 
1154
- if (builtinFuncs[name]) return TYPES[builtinFuncs[name].returnType ?? 'number'];
1204
+ if (builtinFuncs[name] && !builtinFuncs[name].typedReturns) return TYPES[builtinFuncs[name].returnType ?? 'number'];
1155
1205
  if (internalConstrs[name]) return internalConstrs[name].type;
1156
1206
 
1157
1207
  // check if this is a prototype function
@@ -1166,6 +1216,11 @@ const getNodeType = (scope, node) => {
1166
1216
  if (protoFuncs.length === 1) return protoFuncs[0].returnType ?? TYPES.number;
1167
1217
  }
1168
1218
 
1219
+ if (name.startsWith('__Porffor_wasm_')) {
1220
+ // todo: return undefined for non-returning ops
1221
+ return TYPES.number;
1222
+ }
1223
+
1169
1224
  if (scope.locals['#last_type']) return [ getLastType(scope) ];
1170
1225
 
1171
1226
  // presume
@@ -1214,6 +1269,14 @@ const getNodeType = (scope, node) => {
1214
1269
 
1215
1270
  if (node.type === 'BinaryExpression') {
1216
1271
  if (['==', '===', '!=', '!==', '>', '>=', '<', '<='].includes(node.operator)) return TYPES.boolean;
1272
+ if (node.operator !== '+') return TYPES.number;
1273
+
1274
+ const knownLeft = knownType(scope, getNodeType(scope, node.left));
1275
+ const knownRight = knownType(scope, getNodeType(scope, node.right));
1276
+
1277
+ // todo: this should be dynamic but for now only static
1278
+ if (knownLeft === TYPES.string || knownRight === TYPES.string) return TYPES.string;
1279
+
1217
1280
  return TYPES.number;
1218
1281
 
1219
1282
  // todo: string concat types
@@ -1238,7 +1301,7 @@ const getNodeType = (scope, node) => {
1238
1301
  if (node.operator === '!') return TYPES.boolean;
1239
1302
  if (node.operator === 'void') return TYPES.undefined;
1240
1303
  if (node.operator === 'delete') return TYPES.boolean;
1241
- if (node.operator === 'typeof') return process.argv.includes('-bytestring') ? TYPES._bytestring : TYPES.string;
1304
+ if (node.operator === 'typeof') return Prefs.bytestring ? TYPES._bytestring : TYPES.string;
1242
1305
 
1243
1306
  return TYPES.number;
1244
1307
  }
@@ -1632,6 +1695,44 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1632
1695
  idx = -1;
1633
1696
  }
1634
1697
 
1698
+ if (idx === undefined && name.startsWith('__Porffor_wasm_')) {
1699
+ const wasmOps = {
1700
+ // pointer, align, offset
1701
+ i32_load8_u: { imms: 2, args: 1 },
1702
+ // pointer, value, align, offset
1703
+ i32_store8: { imms: 2, args: 2 },
1704
+ // pointer, align, offset
1705
+ i64_load: { imms: 2, args: 1 },
1706
+ // a, b
1707
+ i64_shr_u: { imms: 0, args: 2 },
1708
+ // x
1709
+ i32_wrap_i64: { imms: 0, args: 1, },
1710
+ // x
1711
+ i64_extend_i32_u: { imms: 0, args: 1 },
1712
+ // a, b
1713
+ i64_and: { imms: 0, args: 2 },
1714
+ // val (todo: support >255)
1715
+ i64_const: { imms: 1, args: 0 },
1716
+ };
1717
+
1718
+ const opName = name.slice('__Porffor_wasm_'.length);
1719
+
1720
+ if (wasmOps[opName]) {
1721
+ const op = wasmOps[opName];
1722
+
1723
+ const argOut = [];
1724
+ for (let i = 0; i < op.args; i++) argOut.push(...generate(scope, decl.arguments[i]));
1725
+
1726
+ // literals only
1727
+ const imms = decl.arguments.slice(op.args).map(x => x.value);
1728
+
1729
+ return [
1730
+ ...argOut,
1731
+ [ Opcodes[opName], ...imms ]
1732
+ ];
1733
+ }
1734
+ }
1735
+
1635
1736
  if (idx === undefined) {
1636
1737
  if (scope.locals[name] !== undefined || globals[name] !== undefined || builtinVars[name] !== undefined) return internalThrow(scope, 'TypeError', `${unhackName(name)} is not a function`);
1637
1738
  return internalThrow(scope, 'ReferenceError', `${unhackName(name)} is not defined`);
@@ -1641,7 +1742,7 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1641
1742
 
1642
1743
  const userFunc = (funcIndex[name] && !importedFuncs[name] && !builtinFuncs[name] && !internalConstrs[name]) || idx === -1;
1643
1744
  const typedParams = userFunc || builtinFuncs[name]?.typedParams;
1644
- const typedReturn = userFunc || builtinFuncs[name]?.typedReturn;
1745
+ const typedReturns = userFunc || builtinFuncs[name]?.typedReturns;
1645
1746
  const paramCount = func && (typedParams ? func.params.length / 2 : func.params.length);
1646
1747
 
1647
1748
  let args = decl.arguments;
@@ -1658,14 +1759,20 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1658
1759
  if (func && func.throws) scope.throws = true;
1659
1760
 
1660
1761
  let out = [];
1661
- for (const arg of args) {
1762
+ for (let i = 0; i < args.length; i++) {
1763
+ const arg = args[i];
1662
1764
  out = out.concat(generate(scope, arg));
1765
+
1766
+ if (builtinFuncs[name] && builtinFuncs[name].params[i] === Valtype.i32 && valtypeBinary !== Valtype.i32) {
1767
+ out.push(Opcodes.i32_to);
1768
+ }
1769
+
1663
1770
  if (typedParams) out = out.concat(getNodeType(scope, arg));
1664
1771
  }
1665
1772
 
1666
1773
  out.push([ Opcodes.call, idx ]);
1667
1774
 
1668
- if (!typedReturn) {
1775
+ if (!typedReturns) {
1669
1776
  // let type;
1670
1777
  // if (builtinFuncs[name]) type = TYPES[builtinFuncs[name].returnType ?? 'number'];
1671
1778
  // if (internalConstrs[name]) type = internalConstrs[name].type;
@@ -1677,6 +1784,10 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1677
1784
  // );
1678
1785
  } else out.push(setLastType(scope));
1679
1786
 
1787
+ if (builtinFuncs[name] && builtinFuncs[name].returns[0] === Valtype.i32 && valtypeBinary !== Valtype.i32) {
1788
+ out.push(Opcodes.i32_from);
1789
+ }
1790
+
1680
1791
  return out;
1681
1792
  };
1682
1793
 
@@ -1801,14 +1912,14 @@ const brTable = (input, bc, returns) => {
1801
1912
  };
1802
1913
 
1803
1914
  const typeSwitch = (scope, type, bc, returns = valtypeBinary) => {
1804
- if (!process.argv.includes('-bytestring')) delete bc[TYPES._bytestring];
1915
+ if (!Prefs.bytestring) delete bc[TYPES._bytestring];
1805
1916
 
1806
1917
  const known = knownType(scope, type);
1807
1918
  if (known != null) {
1808
1919
  return bc[known] ?? bc.default;
1809
1920
  }
1810
1921
 
1811
- if (process.argv.includes('-typeswitch-use-brtable'))
1922
+ if (Prefs.typeswitchUseBrtable)
1812
1923
  return brTable(type, bc, returns);
1813
1924
 
1814
1925
  const tmp = localTmp(scope, '#typeswitch_tmp', Valtype.i32);
@@ -1843,7 +1954,7 @@ const typeSwitch = (scope, type, bc, returns = valtypeBinary) => {
1843
1954
  return out;
1844
1955
  };
1845
1956
 
1846
- const allocVar = (scope, name, global = false) => {
1957
+ const allocVar = (scope, name, global = false, type = true) => {
1847
1958
  const target = global ? globals : scope.locals;
1848
1959
 
1849
1960
  // already declared
@@ -1857,8 +1968,10 @@ const allocVar = (scope, name, global = false) => {
1857
1968
  let idx = global ? globalInd++ : scope.localInd++;
1858
1969
  target[name] = { idx, type: valtypeBinary };
1859
1970
 
1860
- let typeIdx = global ? globalInd++ : scope.localInd++;
1861
- target[name + '#type'] = { idx: typeIdx, type: Valtype.i32 };
1971
+ if (type) {
1972
+ let typeIdx = global ? globalInd++ : scope.localInd++;
1973
+ target[name + '#type'] = { idx: typeIdx, type: Valtype.i32 };
1974
+ }
1862
1975
 
1863
1976
  return idx;
1864
1977
  };
@@ -1878,6 +1991,7 @@ const typeAnnoToPorfType = x => {
1878
1991
 
1879
1992
  switch (x) {
1880
1993
  case 'i32':
1994
+ case 'i64':
1881
1995
  return TYPES.number;
1882
1996
  }
1883
1997
 
@@ -1901,7 +2015,7 @@ const extractTypeAnnotation = decl => {
1901
2015
  const typeName = type;
1902
2016
  type = typeAnnoToPorfType(type);
1903
2017
 
1904
- if (type === TYPES._bytestring && !process.argv.includes('-bytestring')) type = TYPES.string;
2018
+ if (type === TYPES._bytestring && !Prefs.bytestring) type = TYPES.string;
1905
2019
 
1906
2020
  // if (decl.name) console.log(decl.name, { type, elementType });
1907
2021
 
@@ -1936,7 +2050,7 @@ const generateVar = (scope, decl) => {
1936
2050
  continue; // always ignore
1937
2051
  }
1938
2052
 
1939
- let idx = allocVar(scope, name, global);
2053
+ let idx = allocVar(scope, name, global, !x.id.typeAnnotation);
1940
2054
 
1941
2055
  if (typedInput && x.id.typeAnnotation) {
1942
2056
  addVarMetadata(scope, name, global, extractTypeAnnotation(x.id));
@@ -2533,6 +2647,9 @@ const generateThrow = (scope, decl) => {
2533
2647
  let exceptId = exceptions.push({ constructor, message }) - 1;
2534
2648
  let tagIdx = tags[0].idx;
2535
2649
 
2650
+ scope.exceptions ??= [];
2651
+ scope.exceptions.push(exceptId);
2652
+
2536
2653
  // todo: write a description of how this works lol
2537
2654
 
2538
2655
  return [
@@ -2577,7 +2694,7 @@ const generateAssignPat = (scope, decl) => {
2577
2694
  };
2578
2695
 
2579
2696
  let pages = new Map();
2580
- const allocPage = (reason, type) => {
2697
+ const allocPage = (scope, reason, type) => {
2581
2698
  if (pages.has(reason)) return pages.get(reason).ind;
2582
2699
 
2583
2700
  if (reason.startsWith('array:')) pages.hasArray = true;
@@ -2588,16 +2705,20 @@ const allocPage = (reason, type) => {
2588
2705
  const ind = pages.size;
2589
2706
  pages.set(reason, { ind, type });
2590
2707
 
2591
- if (allocLog) log('alloc', `allocated new page of memory (${ind}) | ${reason} (type: ${type})`);
2708
+ scope.pages ??= new Map();
2709
+ scope.pages.set(reason, { ind, type });
2710
+
2711
+ if (Prefs.allocLog) log('alloc', `allocated new page of memory (${ind}) | ${reason} (type: ${type})`);
2592
2712
 
2593
2713
  return ind;
2594
2714
  };
2595
2715
 
2716
+ // todo: add scope.pages
2596
2717
  const freePage = reason => {
2597
2718
  const { ind } = pages.get(reason);
2598
2719
  pages.delete(reason);
2599
2720
 
2600
- if (allocLog) log('alloc', `freed page of memory (${ind}) | ${reason}`);
2721
+ if (Prefs.allocLog) log('alloc', `freed page of memory (${ind}) | ${reason}`);
2601
2722
 
2602
2723
  return ind;
2603
2724
  };
@@ -2623,15 +2744,14 @@ const StoreOps = {
2623
2744
 
2624
2745
  let data = [];
2625
2746
 
2626
- const compileBytes = (val, itemType, signed = true) => {
2747
+ const compileBytes = (val, itemType) => {
2627
2748
  // todo: this is a mess and needs confirming / ????
2628
2749
  switch (itemType) {
2629
2750
  case 'i8': return [ val % 256 ];
2630
- case 'i16': return [ val % 256, Math.floor(val / 256) ];
2631
-
2632
- case 'i32':
2633
- case 'i64':
2634
- return enforceFourBytes(signedLEB128(val));
2751
+ case 'i16': return [ val % 256, (val / 256 | 0) % 256 ];
2752
+ case 'i16': return [ val % 256, (val / 256 | 0) % 256 ];
2753
+ case 'i32': return [...new Uint8Array(new Int32Array([ val ]).buffer)];
2754
+ // todo: i64
2635
2755
 
2636
2756
  case 'f64': return ieee754_binary64(val);
2637
2757
  }
@@ -2655,7 +2775,7 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
2655
2775
 
2656
2776
  // todo: can we just have 1 undeclared array? probably not? but this is not really memory efficient
2657
2777
  const uniqueName = name === '$undeclared' ? name + Math.random().toString().slice(2) : name;
2658
- arrays.set(name, allocPage(`${getAllocType(itemType)}: ${uniqueName}`, itemType) * pageSize);
2778
+ arrays.set(name, allocPage(scope, `${getAllocType(itemType)}: ${uniqueName}`, itemType) * pageSize);
2659
2779
  }
2660
2780
 
2661
2781
  const pointer = arrays.get(name);
@@ -2666,19 +2786,25 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
2666
2786
  const valtype = itemTypeToValtype[itemType];
2667
2787
  const length = elements.length;
2668
2788
 
2669
- if (firstAssign && useRawElements) {
2670
- let bytes = compileBytes(length, 'i32');
2789
+ if (firstAssign && useRawElements && !Prefs.noData) {
2790
+ // if length is 0 memory/data will just be 0000... anyway
2791
+ if (length !== 0) {
2792
+ let bytes = compileBytes(length, 'i32');
2671
2793
 
2672
- if (!initEmpty) for (let i = 0; i < length; i++) {
2673
- if (elements[i] == null) continue;
2794
+ if (!initEmpty) for (let i = 0; i < length; i++) {
2795
+ if (elements[i] == null) continue;
2674
2796
 
2675
- bytes.push(...compileBytes(elements[i], itemType));
2676
- }
2797
+ bytes.push(...compileBytes(elements[i], itemType));
2798
+ }
2677
2799
 
2678
- data.push({
2679
- offset: pointer,
2680
- bytes
2681
- });
2800
+ const ind = data.push({
2801
+ offset: pointer,
2802
+ bytes
2803
+ }) - 1;
2804
+
2805
+ scope.data ??= [];
2806
+ scope.data.push(ind);
2807
+ }
2682
2808
 
2683
2809
  // local value as pointer
2684
2810
  out.push(...number(pointer));
@@ -2712,7 +2838,7 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
2712
2838
  };
2713
2839
 
2714
2840
  const byteStringable = str => {
2715
- if (!process.argv.includes('-bytestring')) return false;
2841
+ if (!Prefs.bytestring) return false;
2716
2842
 
2717
2843
  for (let i = 0; i < str.length; i++) {
2718
2844
  if (str.charCodeAt(i) > 0xFF) return false;
@@ -2721,9 +2847,9 @@ const byteStringable = str => {
2721
2847
  return true;
2722
2848
  };
2723
2849
 
2724
- const makeString = (scope, str, global = false, name = '$undeclared') => {
2850
+ const makeString = (scope, str, global = false, name = '$undeclared', forceBytestring = undefined) => {
2725
2851
  const rawElements = new Array(str.length);
2726
- let byteStringable = process.argv.includes('-bytestring');
2852
+ let byteStringable = Prefs.bytestring;
2727
2853
  for (let i = 0; i < str.length; i++) {
2728
2854
  const c = str.charCodeAt(i);
2729
2855
  rawElements[i] = c;
@@ -2731,6 +2857,8 @@ const makeString = (scope, str, global = false, name = '$undeclared') => {
2731
2857
  if (byteStringable && c > 0xFF) byteStringable = false;
2732
2858
  }
2733
2859
 
2860
+ if (byteStringable && forceBytestring === false) byteStringable = false;
2861
+
2734
2862
  return makeArray(scope, {
2735
2863
  rawElements
2736
2864
  }, global, name, false, byteStringable ? 'i8' : 'i16')[0];
@@ -2882,7 +3010,7 @@ const objectHack = node => {
2882
3010
  if (!objectName) return node;
2883
3011
 
2884
3012
  const name = '__' + objectName + '_' + node.property.name;
2885
- if (codeLog) log('codegen', `object hack! ${node.object.name}.${node.property.name} -> ${name}`);
3013
+ if (Prefs.codeLog) log('codegen', `object hack! ${node.object.name}.${node.property.name} -> ${name}`);
2886
3014
 
2887
3015
  return {
2888
3016
  type: 'Identifier',
@@ -2944,6 +3072,8 @@ const generateFunc = (scope, decl) => {
2944
3072
  };
2945
3073
  funcIndex[name] = func.index;
2946
3074
 
3075
+ if (name === 'main') func.gotLastType = true;
3076
+
2947
3077
  // quick hack fixes
2948
3078
  for (const inst of wasm) {
2949
3079
  if (inst[0] === Opcodes.call && inst[1] === -1) {
@@ -3109,7 +3239,7 @@ export default program => {
3109
3239
  body: program.body
3110
3240
  };
3111
3241
 
3112
- if (process.argv.includes('-ast-log')) console.log(program.body.body);
3242
+ if (Prefs.astLog) console.log(program.body.body);
3113
3243
 
3114
3244
  generateFunc(scope, program);
3115
3245