porffor 0.2.0-181627c → 0.2.0-2265539

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,73 +1,106 @@
1
- // @porf -funsafe-no-unlikely-proto-checks
2
-
3
- import type { i32, bytestring } from './porffor.d.ts';
4
-
5
- // const appendKeyChar = (target: bytestring, ind: i32): bytestring => {
6
- // const keyStr: bytestring = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
7
- // return target + keyStr.charAt(ind);
8
- // };
9
-
10
- // const appendKeyChar = (target: bytestring, ind: i32): void => {
11
- // const keyStr: bytestring = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
12
-
13
- // Porffor.bytestring.appendCharAt(target, keyStr, ind);
14
-
15
- // Porffor.wasm`local.get ${ind}
16
- // i32.to_u
17
- // i32.const 2
18
- // i32.mul
19
- // local.get ${keyStr}
20
- // i32.to_u
21
- // i32.add
22
- // i32.load8_u 0 4
23
-
24
- // local targetI32 i32
25
- // local.get ${target}
26
- // i32.to_u
27
- // local.tee targetI32
28
- // local.get targetI32
29
- // i32.load 1 0
30
- // i32.const 2
31
- // i32.mul
32
- // i32.add
33
-
34
- // i32.store8 0 4`;
35
- // };
1
+ // @porf -funsafe-no-unlikely-proto-checks -valtype=i32
2
+
3
+ import type { i32, i64, bytestring } from './porffor.d.ts';
4
+
5
+ // while (len >= 8) {
6
+ // // Porffor.wasm`local tmp i64`;
7
+ // // const tmp: i64 = Porffor.wasm.i64.load(i, 0, 4);
8
+
9
+ // Porffor.wasm`
10
+ // local tmp i64
11
+ // local k i32
12
+
13
+ // local.get ${i}
14
+ // i64.load 0 4
15
+ // local.set tmp
16
+
17
+ // loop 64
18
+ // local.get ${j}
19
+ // local.get ${j}
20
+ // i32.const 1
21
+ // i32.add
22
+ // local.set ${j}
23
+
24
+ // local.get ${keyStrPtr}
25
+ // local.get tmp
26
+
27
+ // i32.const 58
28
+
29
+ // local.get k
30
+ // i32.const 6
31
+ // i32.mul
32
+
33
+ // i32.sub
34
+
35
+ // i64.extend_i32_u
36
+
37
+ // i64.shr_u
38
+
39
+ // i64.const 63
40
+ // i64.and
41
+
42
+ // i32.wrap_i64
43
+ // i32.add
44
+
45
+ // i32.load8_u 0 4
46
+ // i32.store8 0 4
47
+
48
+ // local.get k
49
+ // i32.const 1
50
+ // i32.add
51
+ // local.tee k
52
+
53
+ // i32.const 8
54
+ // i32.lt_s
55
+ // br_if 1
56
+ // end
57
+
58
+ // `;
59
+ // // while (k < 8) {
60
+ // // Porffor.wasm.i32.store8(j++, Porffor.wasm.i32.load8_u(keyStrPtr + Porffor.wasm.i32.wrap_i64(Porffor.wasm.i64.and(
61
+ // // Porffor.wasm.i64.shr_u(tmp, Porffor.wasm.i64.extend_i32_u(58 - k * 6)),
62
+ // // Porffor.wasm.i64.const(0x3f)
63
+ // // )), 0, 4), 0, 4);
64
+ // // k += 1;
65
+ // // }
66
+
67
+ // i += 6;
68
+ // len -= 6;
69
+ // }
36
70
 
37
71
  export const btoa = (input: bytestring): bytestring => {
38
72
  const keyStr: bytestring = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
73
+ const keyStrPtr: i32 = Porffor.i32.ptrUnsafe(keyStr);
39
74
 
40
- // todo: throw invalid character for unicode
41
-
75
+ let len: i32 = input.length;
42
76
  let output: bytestring = '';
43
- let i: i32 = 0;
77
+ output.length = 4 * (len / 3 + !!(len % 3));
44
78
 
45
- while (i < input.length) {
46
- const chr1: i32 = input.charCodeAt(i++);
47
- const chr2: i32 = input.charCodeAt(i++);
48
- const chr3: i32 = input.charCodeAt(i++);
79
+ let i: i32 = Porffor.i32.ptrUnsafe(input),
80
+ j: i32 = Porffor.i32.ptrUnsafe(output);
81
+
82
+ const endPtr = i + len;
83
+ while (i < endPtr) {
84
+ const chr1: i32 = Porffor.wasm.i32.load8_u(i++, 0, 4);
85
+ const chr2: i32 = i < endPtr ? Porffor.wasm.i32.load8_u(i++, 0, 4) : -1;
86
+ const chr3: i32 = i < endPtr ? Porffor.wasm.i32.load8_u(i++, 0, 4) : -1;
49
87
 
50
88
  const enc1: i32 = chr1 >> 2;
51
- const enc2: i32 = ((chr1 & 3) << 4) | (chr2 >> 4);
52
- let enc3: i32 = ((chr2 & 15) << 2) | (chr3 >> 6);
89
+ const enc2: i32 = ((chr1 & 3) << 4) | (chr2 == -1 ? 0 : (chr2 >> 4));
90
+ let enc3: i32 = ((chr2 & 15) << 2) | (chr3 == -1 ? 0 : (chr3 >> 6));
53
91
  let enc4: i32 = chr3 & 63;
54
92
 
55
- if (Number.isNaN(chr2)) {
93
+ if (chr2 == -1) {
56
94
  enc3 = 64;
57
95
  enc4 = 64;
58
- } else if (Number.isNaN(chr3)) {
96
+ } else if (chr3 == -1) {
59
97
  enc4 = 64;
60
98
  }
61
99
 
62
- Porffor.bytestring.appendCharAt(output, keyStr, enc1);
63
- Porffor.bytestring.appendCharAt(output, keyStr, enc2);
64
- Porffor.bytestring.appendCharAt(output, keyStr, enc3);
65
- Porffor.bytestring.appendCharAt(output, keyStr, enc4);
66
-
67
- // output += keyStr.charAt(enc1);
68
- // output += keyStr.charAt(enc2);
69
- // output += keyStr.charAt(enc3);
70
- // output += keyStr.charAt(enc4);
100
+ Porffor.wasm.i32.store8(j++, Porffor.wasm.i32.load8_u(keyStrPtr + enc1, 0, 4), 0, 4);
101
+ Porffor.wasm.i32.store8(j++, Porffor.wasm.i32.load8_u(keyStrPtr + enc2, 0, 4), 0, 4);
102
+ Porffor.wasm.i32.store8(j++, Porffor.wasm.i32.load8_u(keyStrPtr + enc3, 0, 4), 0, 4);
103
+ Porffor.wasm.i32.store8(j++, Porffor.wasm.i32.load8_u(keyStrPtr + enc4, 0, 4), 0, 4);
71
104
  }
72
105
 
73
106
  return output;
@@ -1,12 +1,32 @@
1
1
  export type i32 = number;
2
+ export type i64 = number;
2
3
  export type bytestring = string;
3
4
 
4
5
  type PorfforGlobal = {
5
- wasm: (...args: any[]) => void;
6
+ wasm: {
7
+ (...args: any[]): void;
8
+ i32: {
9
+ load8_u: (pointer: i32, align: i32, offset: i32) => i32;
10
+ store8: (pointer: i32, value: i32, align: i32, offset: i32) => i32;
11
+ wrap_i64: (x: i64) => i32;
12
+ }
13
+ i64: {
14
+ load: (pointer: i32, align: i32, offset: i32) => i64;
15
+ shr_u: (a: i64, b: i64) => i64;
16
+ extend_i32_u: (x: i32) => i64;
17
+ and: (a: i64, b: i64) => i64;
18
+ const: (val: number) => i64;
19
+ }
20
+ }
21
+ ptr: (obj: any) => i32;
22
+
23
+ i32: {
24
+ ptr: (obj: any) => i32;
25
+ ptrUnsafe: (obj: any) => i32;
6
26
 
7
- bytestring: {
8
- appendCharAt: (target: bytestring, lookup: bytestring, ind: i32) => void;
9
- appendCharCode: (target: bytestring, code: i32) => void;
27
+ bytestring: {
28
+ setCharAt: (target: bytestring, targetIndex: i32, lookup: bytestring, lookupIndex: i32) => void;
29
+ }
10
30
  }
11
31
  };
12
32
 
@@ -1,6 +1,7 @@
1
1
  import { Blocktype, Opcodes, Valtype, ValtypeSize } from "./wasmSpec.js";
2
2
  import { number, i32x4 } from "./embedding.js";
3
3
  import Prefs from './prefs.js';
4
+ import * as GeneratedBuiltins from './generated_builtins.js';
4
5
 
5
6
  export const importedFuncs = [
6
7
  {
@@ -240,6 +241,40 @@ export const BuiltinFuncs = function() {
240
241
 
241
242
  [ Opcodes.end ]
242
243
  ],
244
+ [TYPES._bytestring]: [
245
+ // simply print a (byte)string :))
246
+ // cache input pointer as i32
247
+ [ Opcodes.local_get, 0 ],
248
+ Opcodes.i32_to_u,
249
+ [ Opcodes.local_tee, 2 ],
250
+
251
+ // make end pointer
252
+ [ Opcodes.i32_load, Math.log2(ValtypeSize.i32) - 1, 0 ],
253
+ [ Opcodes.local_get, 2 ],
254
+ [ Opcodes.i32_add ],
255
+ [ Opcodes.local_set, 3 ],
256
+
257
+ [ Opcodes.loop, Blocktype.void ],
258
+
259
+ // print current char
260
+ [ Opcodes.local_get, 2 ],
261
+ [ Opcodes.i32_load8_u, Math.log2(ValtypeSize.i16) - 1, ValtypeSize.i32 ],
262
+ Opcodes.i32_from_u,
263
+ [ Opcodes.call, importedFuncs.printChar ],
264
+
265
+ // increment pointer
266
+ [ Opcodes.local_get, 2 ],
267
+ [ Opcodes.i32_const, 1 ],
268
+ [ Opcodes.i32_add ],
269
+ [ Opcodes.local_tee, 2 ],
270
+
271
+ // if pointer != end pointer, loop
272
+ [ Opcodes.local_get, 3 ],
273
+ [ Opcodes.i32_ne ],
274
+ [ Opcodes.br_if, 0 ],
275
+
276
+ [ Opcodes.end ]
277
+ ],
243
278
  [TYPES._array]: [
244
279
  ...printStaticStr('[ '),
245
280
 
@@ -708,7 +743,7 @@ export const BuiltinFuncs = function() {
708
743
  return out;
709
744
  };
710
745
 
711
- this.__Porffor_pointer = {
746
+ this.__Porffor_ptr = {
712
747
  params: [ valtypeBinary, Valtype.i32 ],
713
748
  typedParams: true,
714
749
  locals: [ Valtype.i32, Valtype.i32 ],
@@ -723,248 +758,34 @@ export const BuiltinFuncs = function() {
723
758
  ]
724
759
  };
725
760
 
726
- this.__Porffor_bytestring_appendCharAt = {
727
- params: [ valtypeBinary, valtypeBinary, valtypeBinary ],
728
- locals: [ Valtype.i32 ],
729
- localNames: ['target', 'lookup', 'index', 'length'],
730
- returns: [],
731
- wasm: [
732
- // get ptr for target[target.length]
733
- [ Opcodes.local_get, 0 ],
734
- Opcodes.i32_to_u,
735
- [ Opcodes.local_get, 0 ],
736
- Opcodes.i32_to_u,
737
- [ Opcodes.i32_load, 1, 0 ],
738
- [ Opcodes.local_tee, 3 ],
739
- [ Opcodes.i32_const, 2 ],
740
- [ Opcodes.i32_mul ],
741
- [ Opcodes.i32_add ],
742
-
743
- // get lookup[index]
744
- [ Opcodes.local_get, 2 ],
745
- Opcodes.i32_to_u,
746
- [ Opcodes.i32_const, 2 ],
747
- [ Opcodes.i32_mul ],
748
- [ Opcodes.local_get, 1 ],
749
- Opcodes.i32_to_u,
750
- [ Opcodes.i32_add ],
751
- [ Opcodes.i32_load8_u, 0, ValtypeSize.i32 ],
752
-
753
- // store target[target.length] = lookup[index]
754
- [ Opcodes.i32_store8, 0, 4 ],
755
-
756
- // store target.length = target.length + 1
757
- [ Opcodes.local_get, 0 ],
758
- Opcodes.i32_to_u,
759
- [ Opcodes.local_get, 3 ],
760
- [ Opcodes.i32_const, 1 ],
761
- [ Opcodes.i32_add ],
762
- [ Opcodes.i32_store, 0, 0 ],
763
- ]
764
- };
765
-
766
- this.__Porffor_bytestring_appendCharCode = {
767
- params: [ valtypeBinary, valtypeBinary ],
768
- locals: [ Valtype.i32 ],
769
- localnames: ['target', 'code', 'length'],
770
- returns: [],
771
- wasm: [
772
- // get ptr for target[target.length]
773
- [ Opcodes.local_get, 0 ],
774
- Opcodes.i32_to_u,
775
- [ Opcodes.local_get, 0 ],
776
- Opcodes.i32_to_u,
777
- [ Opcodes.i32_load, 1, 0 ],
778
- [ Opcodes.local_tee, 3 ],
779
- [ Opcodes.i32_const, 2 ],
780
- [ Opcodes.i32_mul ],
781
- [ Opcodes.i32_add ],
782
-
783
- // get code
784
- [ Opcodes.local_get, 1 ],
785
- [ Opcodes.i32_to_u ],
786
-
787
- // set target[target.length] = code
788
- [ Opcodes.i32_store8, 0, 4 ],
789
-
790
- // store target.length = target.length + 1
791
- [ Opcodes.local_get, 0 ],
792
- Opcodes.i32_to_u,
793
- [ Opcodes.local_get, 3 ],
794
- [ Opcodes.i32_const, 1 ],
795
- [ Opcodes.i32_add ],
796
- [ Opcodes.i32_store, 0, 0 ],
797
- ]
798
- };
799
-
800
- // todo: indexOfCharAt
801
-
802
-
803
- this.__SIMD_i32x4_load = {
804
- params: [ Valtype.i32 ],
805
- locals: [],
806
- returns: [ Valtype.v128 ],
807
- wasm: [
761
+ this.__Porffor_i32_ptr = {
762
+ params: [ Valtype.i32, Valtype.i32 ],
763
+ typedParams: true,
764
+ locals: [ Valtype.i32, Valtype.i32 ],
765
+ returns: [ Valtype.i32 ],
766
+ wasm: (scope, { TYPES }) => [
767
+ ...localIsOneOf([ [ Opcodes.local_get, 1 ] ], [ TYPES.string, TYPES._array, TYPES._bytestring ], Valtype.i32),
768
+ [ Opcodes.if, Valtype.i32 ],
808
769
  [ Opcodes.local_get, 0 ],
809
- [ ...Opcodes.v128_load, 0, 0 ]
770
+ [ Opcodes.else ],
771
+ ...number(-1, Valtype.i32),
772
+ [ Opcodes.end ]
810
773
  ]
811
774
  };
812
775
 
813
- this.__SIMD_i32x4_splat = {
776
+ // unsafe: does not check type just ~casts to number
777
+ this.__Porffor_i32_ptrUnsafe = {
814
778
  params: [ Valtype.i32 ],
815
779
  locals: [],
816
- returns: [ Valtype.v128 ],
817
- wasm: [
818
- [ Opcodes.local_get, 0 ],
819
- [ ...Opcodes.i32x4_splat ],
820
- ]
821
- };
822
-
823
- this.__SIMD_i16x8_create = {
824
- params: [ Valtype.i32, Valtype.i32, Valtype.i32, Valtype.i32, Valtype.i32, Valtype.i32, Valtype.i32, Valtype.i32 ],
825
- locals: [],
826
- returns: [ Valtype.v128 ],
827
- wasm: [
828
- ...i32x4(0, 0, 0, 0),
829
- [ Opcodes.local_get, 0 ],
830
- [ ...Opcodes.i16x8_replace_lane, 0 ],
831
- [ Opcodes.local_get, 1 ],
832
- [ ...Opcodes.i16x8_replace_lane, 1 ],
833
- [ Opcodes.local_get, 2 ],
834
- [ ...Opcodes.i16x8_replace_lane, 2 ],
835
- [ Opcodes.local_get, 3 ],
836
- [ ...Opcodes.i16x8_replace_lane, 3 ],
837
- [ Opcodes.local_get, 4 ],
838
- [ ...Opcodes.i16x8_replace_lane, 4 ],
839
- [ Opcodes.local_get, 5 ],
840
- [ ...Opcodes.i16x8_replace_lane, 5 ],
841
- [ Opcodes.local_get, 6 ],
842
- [ ...Opcodes.i16x8_replace_lane, 6 ],
843
- [ Opcodes.local_get, 7 ],
844
- [ ...Opcodes.i16x8_replace_lane, 7 ],
845
- ]
846
- };
847
-
848
- this.__SIMD_i32x4_dot_i16x8 = {
849
- params: [ Valtype.v128, Valtype.v128 ],
850
- locals: [],
851
- returns: [ Valtype.v128 ],
852
- wasm: [
853
- [ Opcodes.local_get, 0 ],
854
- [ Opcodes.local_get, 1 ],
855
- [ ...Opcodes.i32x4_dot_i16x8_s ]
856
- ]
857
- };
858
-
859
- this.__SIMD_i32x4_create = {
860
- params: [ Valtype.i32, Valtype.i32, Valtype.i32, Valtype.i32 ],
861
- locals: [],
862
- returns: [ Valtype.v128 ],
863
- wasm: [
864
- ...i32x4(0, 0, 0, 0),
865
- [ Opcodes.local_get, 0 ],
866
- [ ...Opcodes.i32x4_replace_lane, 0 ],
867
- [ Opcodes.local_get, 1 ],
868
- [ ...Opcodes.i32x4_replace_lane, 1 ],
869
- [ Opcodes.local_get, 2 ],
870
- [ ...Opcodes.i32x4_replace_lane, 2 ],
871
- [ Opcodes.local_get, 3 ],
872
- [ ...Opcodes.i32x4_replace_lane, 3 ],
873
- ]
874
- };
875
-
876
- this.__SIMD_i32x4_add = {
877
- params: [ Valtype.v128, Valtype.v128 ],
878
- locals: [],
879
- returns: [ Valtype.v128 ],
880
- wasm: [
881
- [ Opcodes.local_get, 0 ],
882
- [ Opcodes.local_get, 1 ],
883
- [ ...Opcodes.i32x4_add ]
884
- ]
885
- };
886
-
887
- this.__SIMD_i32x4_sub = {
888
- params: [ Valtype.v128, Valtype.v128 ],
889
- locals: [],
890
- returns: [ Valtype.v128 ],
891
- wasm: [
892
- [ Opcodes.local_get, 0 ],
893
- [ Opcodes.local_get, 1 ],
894
- [ ...Opcodes.i32x4_sub ]
895
- ]
896
- };
897
-
898
- this.__SIMD_i32x4_mul = {
899
- params: [ Valtype.v128, Valtype.v128 ],
900
- locals: [],
901
- returns: [ Valtype.v128 ],
902
- wasm: [
903
- [ Opcodes.local_get, 0 ],
904
- [ Opcodes.local_get, 1 ],
905
- [ ...Opcodes.i32x4_mul ]
906
- ]
907
- };
908
-
909
- this.__SIMD_i32x4_get0 = {
910
- params: [ Valtype.v128 ],
911
- locals: [],
912
- returns: [ Valtype.i32 ],
913
- wasm: [
914
- [ Opcodes.local_get, 0 ],
915
- [ ...Opcodes.i32x4_extract_lane, 0 ],
916
- ],
917
- },
918
-
919
- this.__SIMD_i32x4_get1 = {
920
- params: [ Valtype.v128 ],
921
- locals: [],
922
780
  returns: [ Valtype.i32 ],
923
781
  wasm: [
924
782
  [ Opcodes.local_get, 0 ],
925
- [ ...Opcodes.i32x4_extract_lane, 1 ],
926
- ],
927
- };
928
-
929
- this.__SIMD_i32x4_get2 = {
930
- params: [ Valtype.v128 ],
931
- locals: [],
932
- returns: [ Valtype.i32 ],
933
- wasm: [
934
- [ Opcodes.local_get, 0 ],
935
- [ ...Opcodes.i32x4_extract_lane, 2 ],
936
- ],
937
- };
938
-
939
- this.__SIMD_i32x4_get3 = {
940
- params: [ Valtype.v128 ],
941
- locals: [],
942
- returns: [ Valtype.i32 ],
943
- wasm: [
944
- [ Opcodes.local_get, 0 ],
945
- [ ...Opcodes.i32x4_extract_lane, 3 ],
946
- ],
947
- };
948
-
949
- this.__SIMD_i32x4_shuffle_000c = {
950
- params: [ Valtype.v128 ],
951
- locals: [],
952
- returns: [ Valtype.v128 ],
953
- wasm: [
954
- [ Opcodes.local_get, 0 ],
955
- ...i32x4(0, 0, 0, 0),
956
- [ ...Opcodes.i8x16_shuffle, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 8, 9, 10, 11 ], // i32x4 (a, b, c, d) -> i32x4 (0, 0, 0, c)
957
783
  ]
958
784
  };
959
785
 
960
- this.__SIMD_i32x4_shuffle_00ab = {
961
- params: [ Valtype.v128 ],
962
- locals: [],
963
- returns: [ Valtype.v128 ],
964
- wasm: [
965
- [ Opcodes.local_get, 0 ],
966
- ...i32x4(0, 0, 0, 0),
967
- [ ...Opcodes.i8x16_shuffle, 16, 16, 16, 16, 16, 16, 16, 16, 0, 1, 2, 3, 4, 5, 6, 7 ], // i32x4 (a, b, c, d) -> i32x4 (0, 0, a, b)
968
- ]
969
- };
786
+
787
+ const generated = new GeneratedBuiltins.BuiltinFuncs();
788
+ for (const x in generated) {
789
+ this[x] = generated[x];
790
+ }
970
791
  };
@@ -189,7 +189,11 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
189
189
  if (!inst) throw new Error(`inline asm: inst ${asm[0]} not found`);
190
190
 
191
191
  if (!Array.isArray(inst)) inst = [ inst ];
192
- const immediates = asm.slice(1).map(x => parseInt(x) || scope.locals[x]?.idx);
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
+ });
193
197
 
194
198
  out.push([ ...inst, ...immediates ]);
195
199
  }
@@ -220,11 +224,12 @@ const generate = (scope, decl, global = false, name = undefined, valueUnused = f
220
224
 
221
225
  for (let i = 0; i < expressions.length; i++) {
222
226
  const e = expressions[i];
223
- str += lookupName(scope, e.name)[0];
224
-
227
+ str += lookupName(scope, e.name)[0].idx;
225
228
  str += quasis[i + 1].value.raw;
226
229
  }
227
230
 
231
+ console.log(str);
232
+
228
233
  return funcs[name](str);
229
234
  }
230
235
 
@@ -657,11 +662,12 @@ const truthy = (scope, wasm, type, intIn = false, intOut = false) => {
657
662
  ...(!intIn && intOut ? [ Opcodes.i32_to_u ] : [])
658
663
  ];
659
664
 
660
- const tmp = localTmp(scope, `$logicinner_tmp${intIn ? '_int' : ''}`, intIn ? Valtype.i32 : valtypeBinary);
665
+ const useTmp = knownType(scope, type) == null;
666
+ const tmp = !useTmp && localTmp(scope, `$logicinner_tmp${intIn ? '_int' : ''}`, intIn ? Valtype.i32 : valtypeBinary);
661
667
 
662
668
  const def = [
663
669
  // if value != 0
664
- [ Opcodes.local_get, tmp ],
670
+ ...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
665
671
 
666
672
  // ...(intIn ? [ [ Opcodes.i32_eqz ] ] : [ ...Opcodes.eqz ]),
667
673
  ...(!intOut || (intIn && intOut) ? [] : [ Opcodes.i32_to_u ]),
@@ -673,7 +679,7 @@ const truthy = (scope, wasm, type, intIn = false, intOut = false) => {
673
679
 
674
680
  return [
675
681
  ...wasm,
676
- [ Opcodes.local_set, tmp ],
682
+ ...(!useTmp ? [] : [ [ Opcodes.local_set, tmp ] ]),
677
683
 
678
684
  ...typeSwitch(scope, type, {
679
685
  // [TYPES.number]: def,
@@ -682,7 +688,7 @@ const truthy = (scope, wasm, type, intIn = false, intOut = false) => {
682
688
  ...number(1, intOut ? Valtype.i32 : valtypeBinary)
683
689
  ],
684
690
  [TYPES.string]: [
685
- [ Opcodes.local_get, tmp ],
691
+ ...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
686
692
  ...(intIn ? [] : [ Opcodes.i32_to_u ]),
687
693
 
688
694
  // get length
@@ -694,7 +700,7 @@ const truthy = (scope, wasm, type, intIn = false, intOut = false) => {
694
700
  ...(intOut ? [] : [ Opcodes.i32_from_u ])
695
701
  ],
696
702
  [TYPES._bytestring]: [ // duplicate of string
697
- [ Opcodes.local_get, tmp ],
703
+ ...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
698
704
  ...(intIn ? [] : [ Opcodes.i32_to_u ]),
699
705
 
700
706
  // get length
@@ -708,10 +714,12 @@ const truthy = (scope, wasm, type, intIn = false, intOut = false) => {
708
714
  };
709
715
 
710
716
  const falsy = (scope, wasm, type, intIn = false, intOut = false) => {
711
- const tmp = localTmp(scope, `$logicinner_tmp${intIn ? '_int' : ''}`, intIn ? Valtype.i32 : valtypeBinary);
717
+ const useTmp = knownType(scope, type) == null;
718
+ const tmp = !useTmp && localTmp(scope, `$logicinner_tmp${intIn ? '_int' : ''}`, intIn ? Valtype.i32 : valtypeBinary);
719
+
712
720
  return [
713
721
  ...wasm,
714
- [ Opcodes.local_set, tmp ],
722
+ ...(!useTmp ? [] : [ [ Opcodes.local_set, tmp ] ]),
715
723
 
716
724
  ...typeSwitch(scope, type, {
717
725
  [TYPES._array]: [
@@ -719,7 +727,7 @@ const falsy = (scope, wasm, type, intIn = false, intOut = false) => {
719
727
  ...number(0, intOut ? Valtype.i32 : valtypeBinary)
720
728
  ],
721
729
  [TYPES.string]: [
722
- [ Opcodes.local_get, tmp ],
730
+ ...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
723
731
  ...(intIn ? [] : [ Opcodes.i32_to_u ]),
724
732
 
725
733
  // get length
@@ -730,7 +738,7 @@ const falsy = (scope, wasm, type, intIn = false, intOut = false) => {
730
738
  ...(intOut ? [] : [ Opcodes.i32_from_u ])
731
739
  ],
732
740
  [TYPES._bytestring]: [ // duplicate of string
733
- [ Opcodes.local_get, tmp ],
741
+ ...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
734
742
  ...(intIn ? [] : [ Opcodes.i32_to_u ]),
735
743
 
736
744
  // get length
@@ -742,7 +750,7 @@ const falsy = (scope, wasm, type, intIn = false, intOut = false) => {
742
750
  ],
743
751
  default: [
744
752
  // if value == 0
745
- [ Opcodes.local_get, tmp ],
753
+ ...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
746
754
 
747
755
  ...(intIn ? [ [ Opcodes.i32_eqz ] ] : [ ...Opcodes.eqz ]),
748
756
  ...(intOut ? [] : [ Opcodes.i32_from_u ])
@@ -752,10 +760,12 @@ const falsy = (scope, wasm, type, intIn = false, intOut = false) => {
752
760
  };
753
761
 
754
762
  const nullish = (scope, wasm, type, intIn = false, intOut = false) => {
755
- const tmp = localTmp(scope, `$logicinner_tmp${intIn ? '_int' : ''}`, intIn ? Valtype.i32 : valtypeBinary);
763
+ const useTmp = knownType(scope, type) == null;
764
+ const tmp = !useTmp && localTmp(scope, `$logicinner_tmp${intIn ? '_int' : ''}`, intIn ? Valtype.i32 : valtypeBinary);
765
+
756
766
  return [
757
767
  ...wasm,
758
- [ Opcodes.local_set, tmp ],
768
+ ...(!useTmp ? [] : [ [ Opcodes.local_set, tmp ] ]),
759
769
 
760
770
  ...typeSwitch(scope, type, {
761
771
  [TYPES.undefined]: [
@@ -764,7 +774,7 @@ const nullish = (scope, wasm, type, intIn = false, intOut = false) => {
764
774
  ],
765
775
  [TYPES.object]: [
766
776
  // object, null if == 0
767
- [ Opcodes.local_get, tmp ],
777
+ ...(!useTmp ? [] : [ [ Opcodes.local_get, tmp ] ]),
768
778
 
769
779
  ...(intIn ? [ [ Opcodes.i32_eqz ] ] : [ ...Opcodes.eqz ]),
770
780
  ...(intOut ? [] : [ Opcodes.i32_from_u ])
@@ -969,7 +979,7 @@ const generateBinaryExp = (scope, decl, _global, _name) => {
969
979
  return out;
970
980
  };
971
981
 
972
- const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes = [], globalInits, returns, returnType, localNames = [], globalNames = [] }) => {
982
+ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes = [], globalInits, returns, returnType, localNames = [], globalNames = [], data: _data = [] }) => {
973
983
  const existing = funcs.find(x => x.name === name);
974
984
  if (existing) return existing;
975
985
 
@@ -981,6 +991,12 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
981
991
  locals[nameParam(i)] = { idx: i, type: allLocals[i] };
982
992
  }
983
993
 
994
+ for (const x of _data) {
995
+ const copy = { ...x };
996
+ copy.offset += pages.size * pageSize;
997
+ data.push(copy);
998
+ }
999
+
984
1000
  if (typeof wasm === 'function') {
985
1001
  const scope = {
986
1002
  name,
@@ -990,7 +1006,18 @@ const asmFunc = (name, { wasm, params, locals: localTypes, globals: globalTypes
990
1006
  localInd: allLocals.length,
991
1007
  };
992
1008
 
993
- wasm = wasm(scope, { TYPES, TYPE_NAMES, typeSwitch, makeArray, makeString });
1009
+ wasm = wasm(scope, {
1010
+ TYPES, TYPE_NAMES, typeSwitch, makeArray, makeString, allocPage,
1011
+ builtin: name => {
1012
+ let idx = funcIndex[name] ?? importedFuncs[name];
1013
+ if (idx === undefined && builtinFuncs[name]) {
1014
+ includeBuiltin(scope, name);
1015
+ idx = funcIndex[name];
1016
+ }
1017
+
1018
+ return idx;
1019
+ }
1020
+ });
994
1021
  }
995
1022
 
996
1023
  let baseGlobalIdx, i = 0;
@@ -1176,7 +1203,7 @@ const getNodeType = (scope, node) => {
1176
1203
  if (func.returnType) return func.returnType;
1177
1204
  }
1178
1205
 
1179
- if (builtinFuncs[name]) return TYPES[builtinFuncs[name].returnType ?? 'number'];
1206
+ if (builtinFuncs[name] && !builtinFuncs[name].typedReturns) return TYPES[builtinFuncs[name].returnType ?? 'number'];
1180
1207
  if (internalConstrs[name]) return internalConstrs[name].type;
1181
1208
 
1182
1209
  // check if this is a prototype function
@@ -1191,6 +1218,11 @@ const getNodeType = (scope, node) => {
1191
1218
  if (protoFuncs.length === 1) return protoFuncs[0].returnType ?? TYPES.number;
1192
1219
  }
1193
1220
 
1221
+ if (name.startsWith('__Porffor_wasm_')) {
1222
+ // todo: return undefined for non-returning ops
1223
+ return TYPES.number;
1224
+ }
1225
+
1194
1226
  if (scope.locals['#last_type']) return [ getLastType(scope) ];
1195
1227
 
1196
1228
  // presume
@@ -1665,6 +1697,44 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1665
1697
  idx = -1;
1666
1698
  }
1667
1699
 
1700
+ if (idx === undefined && name.startsWith('__Porffor_wasm_')) {
1701
+ const wasmOps = {
1702
+ // pointer, align, offset
1703
+ i32_load8_u: { imms: 2, args: 1 },
1704
+ // pointer, value, align, offset
1705
+ i32_store8: { imms: 2, args: 2 },
1706
+ // pointer, align, offset
1707
+ i64_load: { imms: 2, args: 1 },
1708
+ // a, b
1709
+ i64_shr_u: { imms: 0, args: 2 },
1710
+ // x
1711
+ i32_wrap_i64: { imms: 0, args: 1, },
1712
+ // x
1713
+ i64_extend_i32_u: { imms: 0, args: 1 },
1714
+ // a, b
1715
+ i64_and: { imms: 0, args: 2 },
1716
+ // val (todo: support >255)
1717
+ i64_const: { imms: 1, args: 0 },
1718
+ };
1719
+
1720
+ const opName = name.slice('__Porffor_wasm_'.length);
1721
+
1722
+ if (wasmOps[opName]) {
1723
+ const op = wasmOps[opName];
1724
+
1725
+ const argOut = [];
1726
+ for (let i = 0; i < op.args; i++) argOut.push(...generate(scope, decl.arguments[i]));
1727
+
1728
+ // literals only
1729
+ const imms = decl.arguments.slice(op.args).map(x => x.value);
1730
+
1731
+ return [
1732
+ ...argOut,
1733
+ [ Opcodes[opName], ...imms ]
1734
+ ];
1735
+ }
1736
+ }
1737
+
1668
1738
  if (idx === undefined) {
1669
1739
  if (scope.locals[name] !== undefined || globals[name] !== undefined || builtinVars[name] !== undefined) return internalThrow(scope, 'TypeError', `${unhackName(name)} is not a function`);
1670
1740
  return internalThrow(scope, 'ReferenceError', `${unhackName(name)} is not defined`);
@@ -1691,8 +1761,14 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1691
1761
  if (func && func.throws) scope.throws = true;
1692
1762
 
1693
1763
  let out = [];
1694
- for (const arg of args) {
1764
+ for (let i = 0; i < args.length; i++) {
1765
+ const arg = args[i];
1695
1766
  out = out.concat(generate(scope, arg));
1767
+
1768
+ if (builtinFuncs[name] && builtinFuncs[name].params[i] === Valtype.i32 && valtypeBinary !== Valtype.i32) {
1769
+ out.push(Opcodes.i32_to);
1770
+ }
1771
+
1696
1772
  if (typedParams) out = out.concat(getNodeType(scope, arg));
1697
1773
  }
1698
1774
 
@@ -1710,6 +1786,10 @@ const generateCall = (scope, decl, _global, _name, unusedValue = false) => {
1710
1786
  // );
1711
1787
  } else out.push(setLastType(scope));
1712
1788
 
1789
+ if (builtinFuncs[name] && builtinFuncs[name].returns[0] === Valtype.i32 && valtypeBinary !== Valtype.i32) {
1790
+ out.push(Opcodes.i32_from);
1791
+ }
1792
+
1713
1793
  return out;
1714
1794
  };
1715
1795
 
@@ -1913,6 +1993,7 @@ const typeAnnoToPorfType = x => {
1913
1993
 
1914
1994
  switch (x) {
1915
1995
  case 'i32':
1996
+ case 'i64':
1916
1997
  return TYPES.number;
1917
1998
  }
1918
1999
 
@@ -2707,7 +2788,7 @@ const makeArray = (scope, decl, global = false, name = '$undeclared', initEmpty
2707
2788
  const valtype = itemTypeToValtype[itemType];
2708
2789
  const length = elements.length;
2709
2790
 
2710
- if (firstAssign && useRawElements) {
2791
+ if (firstAssign && useRawElements && !Prefs.noData) {
2711
2792
  // if length is 0 memory/data will just be 0000... anyway
2712
2793
  if (length !== 0) {
2713
2794
  let bytes = compileBytes(length, 'i32');
@@ -2993,6 +3074,8 @@ const generateFunc = (scope, decl) => {
2993
3074
  };
2994
3075
  funcIndex[name] = func.index;
2995
3076
 
3077
+ if (name === 'main') func.gotLastType = true;
3078
+
2996
3079
  // quick hack fixes
2997
3080
  for (const inst of wasm) {
2998
3081
  if (inst[0] === Opcodes.call && inst[1] === -1) {
@@ -1,3 +1,15 @@
1
+ // autogenerated by compiler/precompile.js
2
+ import { number } from './embedding.js';
1
3
 
2
4
  export const BuiltinFuncs = function() {
5
+ this.btoa = {
6
+ wasm: (scope, { allocPage, builtin }) => [...number(allocPage(scope, 'bytestring: keyStr', 'i8') * pageSize, 127),[34,2],[16, builtin('__Porffor_i32_ptrUnsafe')],[33,3],[32,0],[null],[40,1,0],[null],[33,4],...number(allocPage(scope, 'bytestring: output', 'i8') * pageSize, 127),[33,5],[65,0],[65,4],[32,4],[65,3],[109],[32,4],[65,3],[111],[69],[null],[69],[null],[106],[108],[34,6],[null],[54,1,128,128,4],[32,0],[16, builtin('__Porffor_i32_ptrUnsafe')],[33,8],[32,5],[16, builtin('__Porffor_i32_ptrUnsafe')],[33,9],[32,8],[32,4],[106],[33,10],[65,0],[33,11],[3,64],[32,8],[32,10],[72],[null],[4,64],[32,8],[32,8],[65,1],[106],[33,8],[45,0,4],[33,12],[32,8],[32,10],[72],[null],[4,127],[32,8],[32,8],[65,1],[106],[33,8],[45,0,4],[65,0],[33,14],[5],[65,127],[65,0],[33,14],[11],[33,13],[32,8],[32,10],[72],[null],[4,127],[32,8],[32,8],[65,1],[106],[33,8],[45,0,4],[65,0],[33,14],[5],[65,127],[65,0],[33,14],[11],[33,15],[32,12],[65,2],[117],[33,16],[32,12],[65,3],[113],[65,4],[116],[32,13],[65,127],[70],[null],[4,127],[65,0],[65,0],[33,14],[5],[32,13],[65,4],[117],[65,0],[33,14],[11],[114],[33,17],[32,13],[65,15],[113],[65,2],[116],[32,15],[65,127],[70],[null],[4,127],[65,0],[65,0],[33,14],[5],[32,15],[65,6],[117],[65,0],[33,14],[11],[114],[33,18],[32,15],[65,63],[113],[33,19],[32,13],[65,127],[70],[null],[4,64],[65,192,0],[33,18],[65,192,0],[33,19],[5],[32,15],[65,127],[70],[null],[4,64],[65,192,0],[33,19],[11],[11],[32,9],[32,9],[65,1],[106],[33,9],[32,3],[32,16],[106],[45,0,4],[58,0,4],[32,9],[32,9],[65,1],[106],[33,9],[32,3],[32,17],[106],[45,0,4],[58,0,4],[32,9],[32,9],[65,1],[106],[33,9],[32,3],[32,18],[106],[45,0,4],[58,0,4],[32,9],[32,9],[65,1],[106],[33,9],[32,3],[32,19],[106],[45,0,4],[58,0,4],[12,1],[11],[11],[32,5],[65,18],[15]],
7
+ params: [127,127],
8
+ typedParams: true,
9
+ returns: [127,127],
10
+ typedReturns: true,
11
+ locals: [127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127],
12
+ localNames: ["input","input#type","keyStr","keyStrPtr","len","output","__length_setter_tmp","$logicinner_tmp","i","j","endPtr","endPtr#type","chr1","chr2","#last_type","chr3","enc1","enc2","enc3","enc4"],
13
+ data: [{"offset":0,"bytes":[65,0,0,0,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,48,49,50,51,52,53,54,55,56,57,43,47,61]}],
14
+ };
3
15
  };
@@ -6,6 +6,22 @@ import { join } from 'node:path';
6
6
  import { fileURLToPath } from 'node:url';
7
7
  const __dirname = fileURLToPath(new URL('.', import.meta.url));
8
8
 
9
+ const TYPES = {
10
+ number: 0x00,
11
+ boolean: 0x01,
12
+ string: 0x02,
13
+ undefined: 0x03,
14
+ object: 0x04,
15
+ function: 0x05,
16
+ symbol: 0x06,
17
+ bigint: 0x07,
18
+
19
+ // these are not "typeof" types but tracked internally
20
+ _array: 0x10,
21
+ _regexp: 0x11,
22
+ _bytestring: 0x12
23
+ };
24
+
9
25
  // import porfParse from './parse.js';
10
26
  // import porfCodegen from './codeGen.js';
11
27
 
@@ -31,6 +47,8 @@ const compile = async (file, [ _funcs, _globals ]) => {
31
47
 
32
48
  let { funcs, globals, data, exceptions } = porfCompile(source, ['module']);
33
49
 
50
+ const allocated = new Set();
51
+
34
52
  const exports = funcs.filter(x => x.export);
35
53
  for (const x of exports) {
36
54
  if (x.data) x.data = x.data.map(x => data[x]);
@@ -40,15 +58,33 @@ const compile = async (file, [ _funcs, _globals ]) => {
40
58
  return obj;
41
59
  }).filter(x => x);
42
60
 
43
- for (const y of x.wasm) {
61
+ const locals = Object.keys(x.locals).reduce((acc, y) => {
62
+ acc[x.locals[y].idx] = { ...x.locals[y], name: y };
63
+ return acc;
64
+ }, {});
65
+
66
+ for (let i = 0; i < x.wasm.length; i++) {
67
+ const y = x.wasm[i];
68
+ const n = x.wasm[i + 1];
44
69
  if (y[0] === Opcodes.call) {
45
70
  const f = funcs.find(x => x.index === y[1]);
46
71
  if (!f) continue;
47
72
 
48
- if (!f.internal) throw 'todo';
73
+ y[1] = f.name;
74
+ }
75
+
76
+ if (y[0] === Opcodes.const && (n[0] === Opcodes.local_set || n[0] === Opcodes.local_tee)) {
77
+ const l = locals[n[1]];
78
+ if (!l) continue;
79
+ if (![TYPES.string, TYPES._array, TYPES._bytestring].includes(l.metadata?.type)) continue;
80
+ if (!x.pages) continue;
49
81
 
50
- x.used ??= [];
51
- x.used.push(f.name);
82
+ const pageName = [...x.pages.keys()].find(z => z.endsWith(l.name));
83
+ if (!pageName || allocated.has(pageName)) continue;
84
+ allocated.add(pageName);
85
+
86
+ y.splice(0, 10, 'alloc', pageName, x.pages.get(pageName).type);
87
+ // y.push(x.pages.get(pageName));
52
88
  }
53
89
  }
54
90
  }
@@ -66,24 +102,28 @@ const precompile = async () => {
66
102
  await compile(join(dir, file), [ funcs, globals ]);
67
103
  }
68
104
 
69
- // todo: globals, exceptions, pages per func
105
+ // ${x.pages && x.pages.size > 0 ? ` pages: ${JSON.stringify(Object.fromEntries(x.pages.entries()))},` : ''}
106
+ // ${x.used && x.used.length > 0 ? ` used: ${JSON.stringify(x.used)},` : ''}
70
107
 
71
108
  return `// autogenerated by compiler/precompile.js
109
+ import { number } from './embedding.js';
72
110
 
73
111
  export const BuiltinFuncs = function() {
74
112
  ${funcs.map(x => ` this.${x.name} = {
75
- wasm: ${JSON.stringify(x.wasm)},
113
+ wasm: (scope, { allocPage, builtin }) => ${JSON.stringify(x.wasm.filter(x => x.length)).replace(/\["alloc","(.*?)","(.*?)"\]/g, (_, reason, type) => `...number(allocPage(scope, '${reason}', '${type}') * pageSize, ${valtypeBinary})`).replace(/\[16,"(.*?)"]/g, (_, name) => `[16, builtin('${name}')]`)},
76
114
  params: ${JSON.stringify(x.params)},
77
115
  typedParams: true,
78
116
  returns: ${JSON.stringify(x.returns)},
79
117
  typedReturns: true,
80
- locals: ${JSON.stringify(Object.values(x.locals).slice(x.params.length * 2).map(x => x.type))},
81
- ${x.pages && x.pages.size > 0 ? ` pages: ${JSON.stringify(Object.fromEntries(x.pages.entries()))},` : ''}
118
+ locals: ${JSON.stringify(Object.values(x.locals).slice(x.params.length).map(x => x.type))},
119
+ localNames: ${JSON.stringify(Object.keys(x.locals))},
82
120
  ${x.data && x.data.length > 0 ? ` data: ${JSON.stringify(x.data)},` : ''}
83
121
  ${x.exceptions && x.exceptions.length > 0 ? ` exceptions: ${JSON.stringify(x.exceptions)},` : ''}
84
- ${x.used && x.used.length > 0 ? ` used: ${JSON.stringify(x.used)},` : ''}
85
122
  };`.replaceAll('\n\n', '\n')).join('\n')}
86
- }`;
123
+ };`;
87
124
  };
88
125
 
89
- console.log(await precompile());
126
+ const code = await precompile();
127
+ // console.log(code);
128
+
129
+ fs.writeFileSync(join(__dirname, 'generated_builtins.js'), code);
package/compiler/prefs.js CHANGED
@@ -1,3 +1,5 @@
1
+ const onByDefault = [ 'bytestring' ];
2
+
1
3
  const cache = {};
2
4
  const obj = new Proxy({}, {
3
5
  get(_, p) {
@@ -8,10 +10,12 @@ const obj = new Proxy({}, {
8
10
  // fooBar -> foo-bar
9
11
  const name = p[0] === '_' ? p : p.replace(/[A-Z]/g, c => `-${c.toLowerCase()}`);
10
12
  if (process.argv.includes('-' + name)) return true;
13
+ if (process.argv.includes('-no-' + name)) return false;
11
14
 
12
15
  const valArg = process.argv.find(x => x.startsWith(`-${name}=`));
13
16
  if (valArg) return valArg.slice(name.length + 2);
14
17
 
18
+ if (onByDefault.includes(p)) return true;
15
19
  return undefined;
16
20
  })();
17
21
  }
@@ -385,7 +385,7 @@ export const PrototypeFuncs = function() {
385
385
 
386
386
  ...(noUnlikelyChecks ? [] : [ [ Opcodes.i32_or ] ]),
387
387
  [ Opcodes.if, Blocktype.void ],
388
- ...number(NaN),
388
+ ...number(valtype === 'i32' ? -1 : NaN),
389
389
  [ Opcodes.br, 1 ],
390
390
  [ Opcodes.end ],
391
391
 
@@ -598,7 +598,7 @@ export const PrototypeFuncs = function() {
598
598
 
599
599
  ...(noUnlikelyChecks ? [] : [ [ Opcodes.i32_or ] ]),
600
600
  [ Opcodes.if, Blocktype.void ],
601
- ...number(NaN),
601
+ ...number(valtype === 'i32' ? -1 : NaN),
602
602
  [ Opcodes.br, 1 ],
603
603
  [ Opcodes.end ],
604
604
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "porffor",
3
3
  "description": "a basic experimental wip aot optimizing js -> wasm engine/compiler/runtime in js",
4
- "version": "0.2.0-181627c",
4
+ "version": "0.2.0-2265539",
5
5
  "author": "CanadaHonk",
6
6
  "license": "MIT",
7
7
  "dependencies": {