porffor 0.2.0-a759814 → 0.2.0-a88bbe6

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.
Files changed (56) hide show
  1. package/CONTRIBUTING.md +256 -0
  2. package/LICENSE +20 -20
  3. package/README.md +156 -87
  4. package/asur/README.md +2 -0
  5. package/asur/index.js +1262 -0
  6. package/byg/index.js +237 -0
  7. package/compiler/2c.js +317 -72
  8. package/compiler/{sections.js → assemble.js} +64 -16
  9. package/compiler/builtins/annexb_string.js +72 -0
  10. package/compiler/builtins/annexb_string.ts +18 -0
  11. package/compiler/builtins/array.ts +145 -0
  12. package/compiler/builtins/base64.ts +76 -0
  13. package/compiler/builtins/boolean.ts +18 -0
  14. package/compiler/builtins/crypto.ts +120 -0
  15. package/compiler/builtins/date.ts +2067 -0
  16. package/compiler/builtins/escape.ts +141 -0
  17. package/compiler/builtins/function.ts +5 -0
  18. package/compiler/builtins/int.ts +145 -0
  19. package/compiler/builtins/number.ts +529 -0
  20. package/compiler/builtins/object.ts +4 -0
  21. package/compiler/builtins/porffor.d.ts +60 -0
  22. package/compiler/builtins/set.ts +188 -0
  23. package/compiler/builtins/string.ts +1080 -0
  24. package/compiler/builtins.js +580 -272
  25. package/compiler/{codeGen.js → codegen.js} +1311 -467
  26. package/compiler/decompile.js +3 -4
  27. package/compiler/embedding.js +22 -22
  28. package/compiler/encoding.js +108 -10
  29. package/compiler/generated_builtins.js +1625 -0
  30. package/compiler/index.js +36 -34
  31. package/compiler/log.js +6 -3
  32. package/compiler/opt.js +57 -31
  33. package/compiler/parse.js +33 -23
  34. package/compiler/precompile.js +120 -0
  35. package/compiler/prefs.js +27 -0
  36. package/compiler/prototype.js +182 -42
  37. package/compiler/types.js +38 -0
  38. package/compiler/wasmSpec.js +31 -7
  39. package/compiler/wrap.js +176 -65
  40. package/package.json +9 -5
  41. package/porf +4 -0
  42. package/rhemyn/compile.js +46 -27
  43. package/rhemyn/parse.js +322 -320
  44. package/rhemyn/test/parse.js +58 -58
  45. package/runner/compare.js +34 -34
  46. package/runner/debug.js +122 -0
  47. package/runner/index.js +91 -11
  48. package/runner/profiler.js +102 -0
  49. package/runner/repl.js +42 -9
  50. package/runner/sizes.js +37 -37
  51. package/compiler/builtins/base64.js +0 -92
  52. package/runner/info.js +0 -89
  53. package/runner/profile.js +0 -46
  54. package/runner/results.json +0 -1
  55. package/runner/transform.js +0 -15
  56. package/util/enum.js +0 -20
package/compiler/wrap.js CHANGED
@@ -1,24 +1,74 @@
1
1
  import compile from './index.js';
2
2
  import decompile from './decompile.js';
3
- // import fs from 'node:fs';
3
+ import { encodeVector, encodeLocal } from './encoding.js';
4
+ import Prefs from './prefs.js';
5
+ import { log } from './log.js';
6
+ import { TYPES } from './types.js';
4
7
 
5
8
  const bold = x => `\u001b[1m${x}\u001b[0m`;
6
9
 
7
- const typeBase = 0x00;
8
- const internalTypeBase = 0x10;
9
- const TYPES = {
10
- [typeBase]: 'number',
11
- [typeBase + 1]: 'boolean',
12
- [typeBase + 2]: 'string',
13
- [typeBase + 3]: 'undefined',
14
- [typeBase + 4]: 'object',
15
- [typeBase + 5]: 'function',
16
- [typeBase + 6]: 'symbol',
17
- [typeBase + 7]: 'bigint',
18
-
19
- // internal
20
- [internalTypeBase]: '_array',
21
- [internalTypeBase + 1]: '_regexp'
10
+ const porfToJSValue = (memory, funcs, value, type) => {
11
+ switch (type) {
12
+ case TYPES.boolean: return Boolean(value);
13
+ case TYPES.undefined: return undefined;
14
+ case TYPES.object: return value === 0 ? null : {};
15
+
16
+ case TYPES.function: {
17
+ // wasm func index, including all imports
18
+ const func = funcs.find(x => (x.originalIndex ?? x.index) === value);
19
+ // if (!func) return value;
20
+ if (!func) return function () {};
21
+
22
+ // make fake empty func for repl/etc
23
+ return {[func.name]() {}}[func.name];
24
+ }
25
+
26
+ case TYPES.string: {
27
+ const length = (new Int32Array(memory.buffer, value, 1))[0];
28
+ return Array.from(new Uint16Array(memory.buffer, value + 4, length)).map(x => String.fromCharCode(x)).join('');
29
+ }
30
+
31
+ case TYPES.bytestring: {
32
+ const length = (new Int32Array(memory.buffer, value, 1))[0];
33
+ return Array.from(new Uint8Array(memory.buffer, value + 4, length)).map(x => String.fromCharCode(x)).join('');
34
+ }
35
+
36
+ case TYPES.array: {
37
+ const length = (new Int32Array(memory.buffer, value, 1))[0];
38
+
39
+ // have to slice because of memory alignment (?)
40
+ const buf = memory.buffer.slice(value + 4, value + 4 + 8 * length);
41
+ return Array.from(new Float64Array(buf, 0, length));
42
+ }
43
+
44
+ case TYPES.date: {
45
+ const t = (new Float64Array(memory.buffer, value, 1))[0];
46
+ return new Date(t);
47
+ }
48
+
49
+ case TYPES.set: {
50
+ const size = (new Int32Array(memory.buffer, value, 1))[0];
51
+
52
+ const out = new Set();
53
+ for (let i = 0; i < size; i++) {
54
+ const offset = value + 4 + (i * 9);
55
+
56
+ // have to slice because of memory alignment (?)
57
+ const v = (new Float64Array(memory.buffer.slice(offset, offset + 8), 0, 1))[0];
58
+ const t = (new Uint8Array(memory.buffer, offset + 8, 1))[0];
59
+
60
+ // console.log(`reading value at index ${i}...`)
61
+ // console.log(' memory:', Array.from(new Uint8Array(memory.buffer, offset, 9)).map(x => x.toString(16).padStart(2, '0')).join(' '));
62
+ // console.log(' read:', { value: v, type: t }, '\n');
63
+
64
+ out.add(porfToJSValue(memory, funcs, v, t));
65
+ }
66
+
67
+ return out;
68
+ }
69
+
70
+ default: return value;
71
+ }
22
72
  };
23
73
 
24
74
  export default async (source, flags = [ 'module' ], customImports = {}, print = str => process.stdout.write(str)) => {
@@ -27,25 +77,122 @@ export default async (source, flags = [ 'module' ], customImports = {}, print =
27
77
  const t1 = performance.now();
28
78
  const { wasm, funcs, globals, tags, exceptions, pages, c } = compile(source, flags);
29
79
 
80
+ globalThis.porfDebugInfo = { funcs, globals };
81
+
30
82
  if (source.includes('export function')) flags.push('module');
31
83
 
32
- // fs.writeFileSync('out.wasm', Buffer.from(wasm));
84
+ // (await import('node:fs')).writeFileSync('out.wasm', Buffer.from(wasm));
33
85
 
34
86
  times.push(performance.now() - t1);
35
- if (flags.includes('info')) console.log(bold(`compiled in ${times[0].toFixed(2)}ms`));
87
+ if (Prefs.profileCompiler) console.log(bold(`compiled in ${times[0].toFixed(2)}ms`));
36
88
 
37
89
  const t2 = performance.now();
38
- const { instance } = await WebAssembly.instantiate(wasm, {
39
- '': {
40
- p: valtype === 'i64' ? i => print(Number(i).toString()) : i => print(i.toString()),
41
- c: valtype === 'i64' ? i => print(String.fromCharCode(Number(i))) : i => print(String.fromCharCode(i)),
42
- t: _ => performance.now(),
43
- ...customImports
90
+
91
+ let instance;
92
+ try {
93
+ let wasmEngine = WebAssembly;
94
+ if (Prefs.asur) {
95
+ log.warning('wrap', 'using our !experimental! asur wasm engine instead of host to run');
96
+ wasmEngine = await import('../asur/index.js');
97
+ }
98
+
99
+ 0, { instance } = await wasmEngine.instantiate(wasm, {
100
+ '': {
101
+ p: valtype === 'i64' ? i => print(Number(i).toString()) : i => print(i.toString()),
102
+ c: valtype === 'i64' ? i => print(String.fromCharCode(Number(i))) : i => print(String.fromCharCode(i)),
103
+ t: () => performance.now(),
104
+ u: () => performance.timeOrigin,
105
+ y: () => {},
106
+ z: () => {},
107
+ ...customImports
108
+ }
109
+ });
110
+ } catch (e) {
111
+ // only backtrace for runner, not test262/etc
112
+ if (!process.argv[1].includes('/runner')) throw e;
113
+
114
+ const funcInd = parseInt(e.message.match(/function #([0-9]+) /)?.[1]);
115
+ const blobOffset = parseInt(e.message.split('@')?.[1]);
116
+
117
+ if (!funcInd) throw e;
118
+
119
+ // convert blob offset -> function wasm offset.
120
+ // this is not good code and is somewhat duplicated
121
+ // I just want it to work for debugging, I don't care about perf/yes
122
+
123
+ const func = funcs.find(x => x.index === funcInd);
124
+ const locals = Object.values(func.locals).sort((a, b) => a.idx - b.idx).slice(func.params.length).sort((a, b) => a.idx - b.idx);
125
+
126
+ let localDecl = [], typeCount = 0, lastType;
127
+ for (let i = 0; i < locals.length; i++) {
128
+ const local = locals[i];
129
+ if (i !== 0 && local.type !== lastType) {
130
+ localDecl.push(encodeLocal(typeCount, lastType));
131
+ typeCount = 0;
132
+ }
133
+
134
+ typeCount++;
135
+ lastType = local.type;
44
136
  }
45
- });
137
+
138
+ if (typeCount !== 0) localDecl.push(encodeLocal(typeCount, lastType));
139
+
140
+ const toFind = encodeVector(localDecl).concat(func.wasm.flat().filter(x => x != null && x <= 0xff).slice(0, 40));
141
+
142
+ let i = 0;
143
+ for (; i < wasm.length; i++) {
144
+ let mismatch = false;
145
+ for (let j = 0; j < toFind.length; j++) {
146
+ if (wasm[i + j] !== toFind[j]) {
147
+ mismatch = true;
148
+ break;
149
+ }
150
+ }
151
+
152
+ if (!mismatch) break;
153
+ }
154
+
155
+ if (i === wasm.length) throw e;
156
+
157
+ const offset = (blobOffset - i) + encodeVector(localDecl).length;
158
+
159
+ let cumLen = 0;
160
+ i = 0;
161
+ for (; i < func.wasm.length; i++) {
162
+ cumLen += func.wasm[i].filter(x => x != null && x <= 0xff).length;
163
+ if (cumLen === offset) break;
164
+ }
165
+
166
+ if (cumLen !== offset) throw e;
167
+
168
+ i -= 1;
169
+
170
+ console.log(`\x1B[35m\x1B[1mporffor backtrace\u001b[0m`);
171
+
172
+ console.log('\x1B[4m' + func.name + '\x1B[0m');
173
+
174
+ const surrounding = 6;
175
+
176
+ const decomp = decompile(func.wasm.slice(i - surrounding, i + surrounding + 1), '', 0, func.locals, func.params, func.returns, funcs, globals, exceptions).slice(0, -1).split('\n');
177
+
178
+ const noAnsi = s => s.replace(/\u001b\[[0-9]+m/g, '');
179
+ let longest = 0;
180
+ for (let j = 0; j < decomp.length; j++) {
181
+ longest = Math.max(longest, noAnsi(decomp[j]).length);
182
+ }
183
+
184
+ const middle = Math.floor(decomp.length / 2);
185
+ decomp[middle] = `\x1B[47m\x1B[30m${noAnsi(decomp[middle])}${'\u00a0'.repeat(longest - noAnsi(decomp[middle]).length)}\x1B[0m`;
186
+
187
+ console.log('\x1B[90m...\x1B[0m');
188
+ console.log(decomp.join('\n'));
189
+ console.log('\x1B[90m...\x1B[0m\n');
190
+
191
+ throw e;
192
+ }
46
193
 
47
194
  times.push(performance.now() - t2);
48
- if (flags.includes('info')) console.log(`instantiated in ${times[1].toFixed(2)}ms`);
195
+ if (Prefs.profileCompiler) console.log(`instantiated in ${times[1].toFixed(2)}ms`);
49
196
 
50
197
  const exports = {};
51
198
 
@@ -65,47 +212,11 @@ export default async (source, flags = [ 'module' ], customImports = {}, print =
65
212
 
66
213
  exports[func.name] = function() {
67
214
  try {
68
- const _ret = exp.apply(this, arguments);
69
-
70
- if (_ret == null) return undefined;
71
-
72
- const [ ret, type ] = _ret;
73
-
74
- // if (ret >= typeBase && ret <= typeBase + 8) return ret > (typeBase + 7) ? 'object' : TYPES[ret];
215
+ const ret = exp.apply(this, arguments);
75
216
 
76
- switch (TYPES[type]) {
77
- case 'boolean': return Boolean(ret);
78
- case 'undefined': return undefined;
79
- case 'object': return ret === 0 ? null : {};
217
+ if (ret == null) return undefined;
80
218
 
81
- case '_array': {
82
- const pointer = ret;
83
- const length = new Int32Array(memory.buffer, pointer, 1);
84
-
85
- // have to slice because of memory alignment
86
- const buf = memory.buffer.slice(pointer + 4, pointer + 4 + 8 * length);
87
-
88
- return Array.from(new Float64Array(buf));
89
- }
90
-
91
- case 'string': {
92
- const pointer = ret;
93
- const length = new Int32Array(memory.buffer, pointer, 1);
94
-
95
- return Array.from(new Uint16Array(memory.buffer, pointer + 4, length)).map(x => String.fromCharCode(x)).join('');
96
- }
97
-
98
- case 'function': {
99
- // wasm func index, including all imports
100
- const func = funcs.find(x => (x.originalIndex ?? x.index) === ret);
101
- if (!func) return ret;
102
-
103
- // make fake empty func for repl/etc
104
- return {[func.name]() {}}[func.name];
105
- }
106
-
107
- default: return ret;
108
- }
219
+ return porfToJSValue(memory, funcs, ret[0], ret[1])
109
220
  } catch (e) {
110
221
  if (e.is && e.is(exceptTag)) {
111
222
  const exceptId = e.getArg(exceptTag, 0);
package/package.json CHANGED
@@ -1,21 +1,25 @@
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-a759814",
4
+ "version": "0.2.0-a88bbe6",
5
5
  "author": "CanadaHonk",
6
6
  "license": "MIT",
7
+ "scripts": {
8
+ "precompile": "node ./compiler/precompile.js"
9
+ },
7
10
  "dependencies": {
8
- "acorn": "^8.9.0"
11
+ "acorn": "^8.11.3",
12
+ "node-repl-polyfill": "^0.1.1"
9
13
  },
10
14
  "optionalDependencies": {
11
- "@babel/parser": "^7.23.6",
15
+ "@babel/parser": "^7.24.4",
12
16
  "hermes-parser": "^0.18.2",
13
17
  "meriyah": "^4.3.9"
14
18
  },
15
19
  "bin": {
16
20
  "porf": "./runner/index.js"
17
21
  },
18
- "main": "./runner/index.js",
22
+ "main": "./compiler/wrap.js",
19
23
  "type": "module",
20
24
  "repository": {
21
25
  "type": "git",
@@ -25,4 +29,4 @@
25
29
  "url": "https://github.com/CanadaHonk/porffor/issues"
26
30
  },
27
31
  "homepage": "https://porffor.goose.icu"
28
- }
32
+ }
package/porf ADDED
@@ -0,0 +1,4 @@
1
+ #!/bin/sh
2
+ node runner/index.js "$@"
3
+ # deno run -A runner/index.js "$@"
4
+ # bun runner/index.js "$@"
package/rhemyn/compile.js CHANGED
@@ -1,7 +1,8 @@
1
- import { Blocktype, Opcodes, Valtype, PageSize, ValtypeSize } from '../compiler/wasmSpec.js';
1
+ import { Blocktype, Opcodes, Valtype, ValtypeSize } from '../compiler/wasmSpec.js';
2
2
  import { number } from '../compiler/embedding.js';
3
- import { signedLEB128, unsignedLEB128 } from '../compiler/encoding.js';
4
3
  import parse from './parse.js';
4
+ import Prefs from '../compiler/prefs.js';
5
+ import { TYPES } from '../compiler/types.js';
5
6
 
6
7
  // local indexes
7
8
  const BasePointer = 0; // base string pointer
@@ -13,7 +14,7 @@ const Length = 5;
13
14
  const Tmp = 6;
14
15
 
15
16
  let exprLastGet = false;
16
- const generate = (node, negated = false, get = true, func = 'test') => {
17
+ const generate = (node, negated = false, get = true, stringSize = 2, func = 'test') => {
17
18
  let out = [];
18
19
  switch (node.type) {
19
20
  case 'Expression':
@@ -41,7 +42,7 @@ const generate = (node, negated = false, get = true, func = 'test') => {
41
42
  // generate checks
42
43
  ...node.body.flatMap((x, i) => {
43
44
  exprLastGet = x.type !== 'Group' && i === (node.body.length - 1);
44
- return generate(x, negated);
45
+ return generate(x, negated, true, stringSize, func);
45
46
  }),
46
47
 
47
48
  // reached end without branching out, successful match
@@ -55,9 +56,9 @@ const generate = (node, negated = false, get = true, func = 'test') => {
55
56
 
56
57
  [ Opcodes.end ],
57
58
 
58
- // increment iter pointer by sizeof i16
59
+ // increment iter pointer by string size
59
60
  [ Opcodes.local_get, IterPointer ],
60
- ...number(ValtypeSize.i16, Valtype.i32),
61
+ ...number(stringSize, Valtype.i32),
61
62
  [ Opcodes.i32_add ],
62
63
  [ Opcodes.local_set, IterPointer ],
63
64
 
@@ -80,7 +81,7 @@ const generate = (node, negated = false, get = true, func = 'test') => {
80
81
  })[func], Valtype.i32)
81
82
  ];
82
83
 
83
- if (globalThis.regexLog) {
84
+ if (Prefs.regexLog) {
84
85
  const underline = x => `\u001b[4m\u001b[1m${x}\u001b[0m`;
85
86
  console.log(`\n${underline('ast')}`);
86
87
  console.log(node);
@@ -90,34 +91,34 @@ const generate = (node, negated = false, get = true, func = 'test') => {
90
91
  break;
91
92
 
92
93
  case 'Character':
93
- out = generateChar(node, node.negated ^ negated, get);
94
+ out = generateChar(node, node.negated ^ negated, get, stringSize);
94
95
  break;
95
96
 
96
97
  case 'Set':
97
- out = generateSet(node, node.negated, get);
98
+ out = generateSet(node, node.negated, get, stringSize);
98
99
  break;
99
100
 
100
101
  case 'Group':
101
- out = generateGroup(node, negated, get);
102
+ out = generateGroup(node, negated, get, stringSize);
102
103
  break;
103
104
 
104
105
  case 'Range':
105
- out = generateRange(node, negated, get);
106
+ out = generateRange(node, negated, get, stringSize);
106
107
  break;
107
108
  }
108
109
 
109
110
  return out;
110
111
  };
111
112
 
112
- const getNextChar = () => [
113
+ const getNextChar = (stringSize) => [
113
114
  // get char from pointer
114
115
  [ Opcodes.local_get, Pointer ],
115
- [ Opcodes.i32_load16_u, Math.log2(ValtypeSize.i16) - 1, ...unsignedLEB128(0) ],
116
+ [ stringSize == 2 ? Opcodes.i32_load16_u : Opcodes.i32_load8_u, 0, 0 ],
116
117
 
117
118
  ...(exprLastGet ? [] : [
118
- // pointer += sizeof i16
119
+ // pointer += string size
119
120
  [ Opcodes.local_get, Pointer ],
120
- ...number(ValtypeSize.i16, Valtype.i32),
121
+ ...number(stringSize, Valtype.i32),
121
122
  [ Opcodes.i32_add ],
122
123
  [ Opcodes.local_set, Pointer ]
123
124
  ])
@@ -133,21 +134,21 @@ const checkFailure = () => [
133
134
  [ Opcodes.br_if, 0 ]
134
135
  ];
135
136
 
136
- const generateChar = (node, negated, get) => {
137
+ const generateChar = (node, negated, get, stringSize) => {
137
138
  return [
138
- ...(get ? getNextChar() : []),
139
+ ...(get ? getNextChar(stringSize) : []),
139
140
  ...number(node.char.charCodeAt(0), Valtype.i32),
140
141
  negated ? [ Opcodes.i32_eq ] : [ Opcodes.i32_ne ],
141
142
  ...(get ? checkFailure(): [])
142
143
  ];
143
144
  };
144
145
 
145
- const generateSet = (node, negated, get) => {
146
+ const generateSet = (node, negated, get, stringSize) => {
146
147
  // for a single char we do not need a tmp, it is like just
147
148
  const singleChar = node.body.length === 1 && node.body[0].type === 'Character';
148
149
 
149
150
  let out = [
150
- ...(get ? getNextChar() : []),
151
+ ...(get ? getNextChar(stringSize) : []),
151
152
  ...(singleChar ? [] : [ [ Opcodes.local_set, Tmp ] ]),
152
153
  ];
153
154
 
@@ -155,11 +156,11 @@ const generateSet = (node, negated, get) => {
155
156
  out = [
156
157
  ...out,
157
158
  ...(singleChar ? [] : [ [ Opcodes.local_get, Tmp ] ]),
158
- ...generate(x, negated, false)
159
+ ...generate(x, negated, false, stringSize)
159
160
  ];
160
161
  }
161
162
 
162
- out = out.concat(new Array(node.body.length - 1).fill(negated ? [ Opcodes.i32_or ] : [ Opcodes.i32_and ]));
163
+ if (node.body.length > 0) out = out.concat(new Array(node.body.length - 1).fill(negated ? [ Opcodes.i32_or ] : [ Opcodes.i32_and ]));
163
164
 
164
165
  return [
165
166
  ...out,
@@ -167,9 +168,9 @@ const generateSet = (node, negated, get) => {
167
168
  ];
168
169
  };
169
170
 
170
- const generateRange = (node, negated, get) => {
171
+ const generateRange = (node, negated, get, stringSize) => {
171
172
  return [
172
- ...(get ? getNextChar() : []),
173
+ ...(get ? getNextChar(stringSize) : []),
173
174
  ...(get ? [ [ Opcodes.local_tee, Tmp ] ] : []),
174
175
 
175
176
  ...number(node.from.charCodeAt(0), Valtype.i32),
@@ -187,11 +188,29 @@ const generateRange = (node, negated, get) => {
187
188
  };
188
189
 
189
190
  const generateGroup = (node, negated, get) => {
191
+ // todo
192
+ return [];
193
+ };
190
194
 
195
+ const wrapFunc = (regex, func, name, index) => {
196
+ const parsed = parse(regex);
197
+
198
+ return outputFunc([
199
+ [ Opcodes.local_get, 1 ],
200
+ ...number(TYPES.string, Valtype.i32),
201
+ [ Opcodes.i32_eq ],
202
+ [ Opcodes.if, Valtype.i32 ],
203
+ // string
204
+ ...generate(parsed, false, true, 2, func),
205
+ [ Opcodes.else ],
206
+ // bytestring
207
+ ...generate(parsed, false, true, 1, func),
208
+ [ Opcodes.end ]
209
+ ], name, index);
191
210
  };
192
211
 
193
- export const test = (regex, index = 0, name = 'regex_test_' + regex) => outputFunc(generate(parse(regex), false, true, 'test'), name, index);
194
- export const search = (regex, index = 0, name = 'regex_search_' + regex) => outputFunc(generate(parse(regex), false, true, 'search'), name, index);
212
+ export const test = (regex, index = 0, name = 'regex_test_' + regex) => wrapFunc(regex, 'test', name, index);
213
+ export const search = (regex, index = 0, name = 'regex_search_' + regex) => wrapFunc(regex, 'search', name, index);
195
214
 
196
215
  const outputFunc = (wasm, name, index) => ({
197
216
  name,
@@ -199,9 +218,9 @@ const outputFunc = (wasm, name, index) => ({
199
218
  wasm,
200
219
 
201
220
  export: true,
202
- params: [ Valtype.i32 ],
221
+ params: [ Valtype.i32, Valtype.i32 ],
203
222
  returns: [ Valtype.i32 ],
204
- returnType: 0xffffffffffff1, // boolean - todo: do not hardcode this
223
+ returnType: TYPES.boolean,
205
224
  locals: {
206
225
  basePointer: { idx: 0, type: Valtype.i32 },
207
226
  iterPointer: { idx: 1, type: Valtype.i32 },