porffor 0.2.0-fdf0fc5 โ†’ 0.14.0-0ad2c8a7c

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 (48) hide show
  1. package/CONTRIBUTING.md +12 -7
  2. package/README.md +18 -15
  3. package/asur/index.js +1 -1
  4. package/byg/index.js +3 -24
  5. package/compiler/2c.js +69 -55
  6. package/compiler/assemble.js +51 -11
  7. package/compiler/builtins/annexb_string.js +10 -10
  8. package/compiler/builtins/annexb_string.ts +4 -3
  9. package/compiler/builtins/array.ts +85 -9
  10. package/compiler/builtins/base64.ts +2 -1
  11. package/compiler/builtins/boolean.ts +2 -2
  12. package/compiler/builtins/console.ts +4 -0
  13. package/compiler/builtins/crypto.ts +2 -1
  14. package/compiler/builtins/date.ts +2 -3
  15. package/compiler/builtins/error.js +22 -0
  16. package/compiler/builtins/escape.ts +2 -3
  17. package/compiler/builtins/function.ts +1 -1
  18. package/compiler/builtins/int.ts +1 -1
  19. package/compiler/builtins/math.ts +410 -0
  20. package/compiler/builtins/number.ts +4 -7
  21. package/compiler/builtins/object.ts +1 -1
  22. package/compiler/builtins/porffor.d.ts +9 -8
  23. package/compiler/builtins/set.ts +197 -3
  24. package/compiler/builtins/string.ts +2 -1
  25. package/compiler/builtins/symbol.ts +62 -0
  26. package/compiler/builtins.js +30 -15
  27. package/compiler/codegen.js +641 -365
  28. package/compiler/decompile.js +7 -3
  29. package/compiler/embedding.js +2 -2
  30. package/compiler/encoding.js +0 -14
  31. package/compiler/expression.js +1 -1
  32. package/compiler/generated_builtins.js +781 -187
  33. package/compiler/index.js +5 -11
  34. package/compiler/opt.js +7 -7
  35. package/compiler/parse.js +2 -4
  36. package/compiler/precompile.js +18 -25
  37. package/compiler/prefs.js +6 -2
  38. package/compiler/prototype.js +185 -162
  39. package/compiler/wasmSpec.js +5 -0
  40. package/compiler/wrap.js +150 -90
  41. package/package.json +1 -1
  42. package/runner/compare.js +0 -1
  43. package/runner/debug.js +1 -6
  44. package/runner/index.js +5 -4
  45. package/runner/profiler.js +15 -42
  46. package/runner/repl.js +20 -10
  47. package/runner/sizes.js +2 -2
  48. package/runner/version.js +10 -8
package/CONTRIBUTING.md CHANGED
@@ -98,7 +98,7 @@ Loads the character code at the pointer `pointer` **for a String**.[^1]
98
98
  Porffor.wasm.i32.store(pointer, length, 0, 0)
99
99
  ```
100
100
 
101
- Stores the length `length` at pointer `pointer`, setting the length of an object. This is mostly unneeded today as you can just do `obj.length = length`. [^2]
101
+ Stores the length `length` at pointer `pointer`, setting the length of an object. This is mostly unneeded today as you can just do `obj.length = length`. (The `0, 4` args are necessary for the Wasm instruction, but you don't need to worry about them (`0` alignment, `0` byte offset).
102
102
 
103
103
  <br>
104
104
 
@@ -198,11 +198,14 @@ Store the character code into the `out` pointer variable, and increment it.
198
198
 
199
199
  ## Porffor-specific TS notes
200
200
 
201
- - For declaring variables, you must use explicit type annotations currently (eg `let a: number = 1`, not `let a = 1`)
201
+ - For declaring variables, you must use explicit type annotations currently (eg `let a: number = 1`, not `let a = 1`).
202
202
  - You might spot `Porffor.fastOr`/`Porffor.fastAnd`, these are non-short circuiting versions of `||`/`&&`, taking any number of conditions as arguments. You shouldn't don't need to use or worry about these.
203
- - **There are ~no objects, you cannot use them/literals.**
203
+ - **There are ~no objects, you cannot use them.**
204
204
  - Attempt to avoid string/array-heavy code and use more variables instead if possible, easier on memory and CPU/perf.
205
205
  - Do not set a return type for prototype methods, it can cause errors/unexpected results.
206
+ - You cannot use other functions in the file not exported, or variables not inside the current function.
207
+ - `if (...)` uses a fast truthy implementation which is not spec-compliant as most conditions should be strictly checked. To use spec-compliant behavior, use `if (Boolean(...))`.
208
+ - For object (string/array/etc) literals, you must use a variable eg `const out: bytestring = 'foobar'; console.log(out);` instead of `console.log('foobar')` due to precompile's allocator constraints.
206
209
 
207
210
  <br>
208
211
 
@@ -231,7 +234,11 @@ builtins/tostring_number: impl radix
231
234
 
232
235
  Make sure you have Test262 cloned already **inside of `test262/`** (`git clone https://github.com/tc39/test262.git test262/test262`) and run `npm install` inside `test262/` too.
233
236
 
234
- Run `node test262` to run all the tests and get an output of total overall test results. The main thing you want to pay attention to is the emoji summary (lol):
237
+ Run `node test262` to run all the tests and get an output of total overall test results.
238
+
239
+ Warning: this will consume 1-6GB of memory and ~90% of all CPU cores while running (depending on thread count), it should take 15-120s depending on machine. You can specify how many threads with `--threads=N`, it will use the number of CPU threads by default.
240
+
241
+ The main thing you want to pay attention to is the emoji summary (lol):
235
242
  ```
236
243
  ๐Ÿงช 50005 | ๐Ÿค  7007 (-89) | โŒ 1914 (-32) | ๐Ÿ’€ 13904 (-61) | ๐Ÿ“ 23477 (-120) | โฐ 2 | ๐Ÿ— 2073 (+302) | ๐Ÿ’ฅ 1628
237
244
  ```
@@ -251,6 +258,4 @@ It will also log new passes/fails. Be careful as sometimes the overall passes ca
251
258
 
252
259
  <br>
253
260
 
254
- [^1]: The `0, 4` args are necessary for the Wasm instruction, but you don't need to worry about them (`0` alignment, `4` byte offset for length).
255
-
256
- [^2]: The `0, 4` args are necessary for the Wasm instruction, but you don't need to worry about them (`0` alignment, `0` byte offset).
261
+ [^1]: The `0, 4` args are necessary for the Wasm instruction, but you don't need to worry about them (`0` alignment, `4` byte offset for length).
package/README.md CHANGED
@@ -13,8 +13,8 @@ Porffor is primarily built from scratch, the only thing that is not is the parse
13
13
  ## Usage
14
14
  Expect nothing to work! Only very limited JS is currently supported. See files in `bench` for examples.
15
15
 
16
- ### Setup
17
- **`npm install -g porffor`**. It's that easy (hopefully) :)
16
+ ### Install
17
+ **`npm install -g porffor@latest`**. It's that easy (hopefully) :)
18
18
 
19
19
  ### Trying a REPL
20
20
  **`porf`**. Just run it with no script file argument.
@@ -189,6 +189,13 @@ These include some early (stage 1/0) and/or dead (last commit years ago) proposa
189
189
  - Intrinsic functions (see below)
190
190
  - Inlining wasm via ``asm`...``\` "macro"
191
191
 
192
+ ## Versioning
193
+ Porffor uses a unique versioning system, here's an example: `0.14.0-15cb49f07`. Let's break it down:
194
+ 1. `0` - major, always `0` as Porffor is not ready yet
195
+ 2. `14` - minor, total Test262 pass percentage (floored to nearest int)
196
+ 3. `0` - micro, always `0` as unused
197
+ 4. `15cb49f07` - commit hash
198
+
192
199
  ## Performance
193
200
  *For the features it supports most of the time*, Porffor is *blazingly fast* compared to most interpreters and common engines running without JIT. For those with JIT, it is usually slower by default, but can catch up with compiler arguments and typed input, even more so when compiling to native binaries.
194
201
 
@@ -220,7 +227,7 @@ Mostly for reducing size. I do not really care about compiler perf/time as long
220
227
  - No tags if unused/optimized out
221
228
 
222
229
  ## Test262
223
- Porffor can run Test262 via some hacks/transforms which remove unsupported features whilst still doing the same asserts (eg simpler error messages using literals only). It currently passes >10% (see latest commit desc for latest and details). Use `node test262` to test, it will also show a difference of overall results between the last commit and current results.
230
+ Porffor can run Test262 via some hacks/transforms which remove unsupported features whilst still doing the same asserts (eg simpler error messages using literals only). It currently passes >14% (see latest commit desc for latest and details). Use `node test262` to test, it will also show a difference of overall results between the last commit and current results.
224
231
 
225
232
  ## Codebase
226
233
  - `compiler`: contains the compiler itself
@@ -259,8 +266,6 @@ Basically none right now (other than giving people headaches). Potential ideas:
259
266
  No particular order and no guarentees, just what could happen soonโ„ข
260
267
 
261
268
  - Arrays
262
- - More of `Array` prototype
263
- - Arrays/strings inside arrays
264
269
  - Destructuring
265
270
  - Objects
266
271
  - Basic object expressions (eg `{}`, `{ a: 0 }`)
@@ -308,16 +313,10 @@ Porffor intentionally does not use Wasm proposals which are not commonly impleme
308
313
 
309
314
  - Multi-value **(required)**
310
315
  - Non-trapping float-to-int conversions **(required)**
311
- - Bulk memory operations (required, but uncommonly used)
312
- - Exception handling (optional, for errors)
316
+ - Bulk memory operations (optional, can get away without sometimes)
317
+ - Exception handling (optional, only for errors)
313
318
  - Tail calls (opt-in, off by default)
314
319
 
315
- ## Isn't this the same as AssemblyScript/other Wasm langs?
316
- No. they are not alike at all internally and have very different goals/ideals:
317
- - Porffor is made as a generic JS engine, not for Wasm stuff specifically
318
- - Porffor primarily consumes JS
319
- - Porffor is written in pure JS and compiles itself, not using Binaryen/etc
320
- - (Also I didn't know it existed when I started this, lol)
321
320
 
322
321
  ## FAQ
323
322
 
@@ -331,5 +330,9 @@ No. they are not alike at all internally and have very different goals/ideals:
331
330
  ### 2. Why at all?
332
331
  Yes!
333
332
 
334
- ### 3. But what about spec compliance?
335
- Lol, no. (sorry.)
333
+ ## 3. Isn't this the same as AssemblyScript/other Wasm langs?
334
+ No. they are not alike at all internally and have very different goals/ideals:
335
+ - Porffor is made as a generic JS engine, not for Wasm stuff specifically
336
+ - Porffor primarily consumes JS
337
+ - Porffor is written in pure JS and compiles itself, not using Binaryen/etc
338
+ - (Also I didn't know it existed when I started this, lol)
package/asur/index.js CHANGED
@@ -1155,7 +1155,7 @@ if (bc.porfFunc && paused && op) {
1155
1155
  switch (byg(
1156
1156
  paused,
1157
1157
  funcLines[currentFunc] + currentLine,
1158
- '\x1b[1masur\x1b[22m: ' + callStack.join(' -> ') + (parents.length > 1 ? \` | \${parents.slice(1).map(x => invOpcodes[x.opcode]).join(' -> ')}\` : ''),
1158
+ '\x1b[1masur debugger\x1b[22m: ' + callStack.join(' -> ') + (parents.length > 1 ? \` | \${parents.slice(1).map(x => invOpcodes[x.opcode]).join(' -> ')}\` : ''),
1159
1159
  [
1160
1160
  {
1161
1161
  x: termWidth - 1 - width - 6,
package/byg/index.js CHANGED
@@ -2,7 +2,6 @@ import fs from 'node:fs';
2
2
 
3
3
  const noAnsi = s => s.replace(/\u001b\[[0-9]+m/g, '');
4
4
  const printLine = (line, number, breakpoint = false, current = false, selected = false) => {
5
- // console.log(`\x1b[${breakpoint ? (selected ? '106' : '46') : (selected ? '47' : '100')}m\x1b[${selected ? '30' : '97'}m${number.toFixed(0).padStart(4, ' ')}\x1b[${breakpoint ? (selected ? '96' : '36') : (selected ? '37' : '90')}m\x1b[${current ? '47' : '40'}mโ–Œ \x1b[${current ? '47' : '40'}m\x1b[${current ? '30' : '37'}m${current ? noAnsi(line) : line}\x1b[0K`);
6
5
  console.log(`\x1b[${breakpoint ? (selected ? '43' : '103') : (selected ? '47' : '100')}m\x1b[${selected || breakpoint ? '30' : '97'}m${number.toFixed(0).padStart(4, ' ')}\x1b[${breakpoint ? (selected ? '33' : '93') : (selected ? '37' : '90')}m\x1b[${current ? '47' : '40'}mโ–Œ \x1b[${current ? '47' : '40'}m\x1b[${current ? '30' : '37'}m${current ? noAnsi(line) : line}\x1b[0K`);
7
6
  };
8
7
 
@@ -13,13 +12,10 @@ const box = (x, y, width, height, title = '', content = [], color = ['90', '100'
13
12
 
14
13
  // top
15
14
  process.stdout.write(`\x1b[48m\x1b[${y + 1};${x + 1}H\x1b[${color[0]}m` + 'โ–„'.repeat(width));
16
-
17
15
  // bottom
18
16
  process.stdout.write(`\x1b[${y + height + 1};${x + 1}Hโ–` + 'โ–€'.repeat(width - 1) + 'โ–˜');
19
-
20
17
  // left
21
18
  process.stdout.write(`\x1b[${y + 1};${x + 1}Hโ–—` + '\x1b[1B\x1b[1Dโ–'.repeat(height - 1));
22
-
23
19
  // right
24
20
  process.stdout.write(`\x1b[${y + 1};${x + width + 1}Hโ––` + '\x1b[1B\x1b[1Dโ–Œ'.repeat(height - 1));
25
21
 
@@ -33,8 +29,6 @@ const box = (x, y, width, height, title = '', content = [], color = ['90', '100'
33
29
  process.stdout.write(`\x1b[${y + 1};${x + 1}H\x1b[${color[1]}m` + ' '.repeat(width) + (`\x1b[1B\x1b[${width}D` + ' '.repeat(width)).repeat(Math.max(0, height - 1)));
34
30
 
35
31
  // title
36
- // if (title) process.stdout.write(`\x1b[${y + 1};${x + ((width - title.length) / 2 | 0) + 1}H\x1b[${color[1]}m\x1b[${color[2]}m\x1b[1m${title}\x1b[22m`);
37
- // if (title) process.stdout.write(`\x1b[${y};${x}H\x1b[${color[3]}โ–—\x1b[${color[4]}m\x1b[${color[2]}m\x1b[1m${' '.repeat((width - title.length) / 2 | 0)}${title}${' '.repeat(width - (((width - title.length) / 2 | 0)) - title.length)}\x1b[0m\x1b[${color[4]}mโ––`);
38
32
  if (title) process.stdout.write(`\x1b[${y};${x}H\x1b[0m\x1b[${color[3]}mโ–\x1b[${color[4]}m\x1b[${color[2]}m\x1b[1m${' '.repeat((width - title.length) / 2 | 0)}${title}${' '.repeat(width - (((width - title.length) / 2 | 0)) - title.length)}\x1b[0m\x1b[${color[3]}mโ–Œ`);
39
33
 
40
34
  // content
@@ -66,8 +60,6 @@ export default ({ lines, pause, breakpoint }) => {
66
60
  process.exit();
67
61
  }
68
62
 
69
- // process.stdout.write(s);
70
-
71
63
  if (!paused) pause();
72
64
  });
73
65
 
@@ -83,7 +75,6 @@ export default ({ lines, pause, breakpoint }) => {
83
75
 
84
76
  process.on('exit', () => {
85
77
  process.stdout.write('\x1b[0m');
86
- // console.clear();
87
78
  });
88
79
 
89
80
  console.clear();
@@ -130,18 +121,9 @@ export default ({ lines, pause, breakpoint }) => {
130
121
 
131
122
  for (const x of boxes) {
132
123
  const y = x.y({ currentLinePos });
133
- const height = x.height;
134
124
  if (y < 0 || y >= termHeight) continue;
135
125
 
136
- // crop box if > termHeight
137
- // if (y + height >= termHeight) {
138
- // const excess = y + height - termHeight;
139
- // height -= excess;
140
-
141
- // content = content.slice(0, height - 2);
142
- // }
143
-
144
- box(x.x, y, x.width, height, x.title, x.content);
126
+ box(x.x, y, x.width, x.height, x.title, x.content);
145
127
  }
146
128
 
147
129
  // text += ` | rss: ${(process.memoryUsage.rss() / 1024 / 1024).toFixed(2)}mb`;
@@ -179,7 +161,8 @@ export default ({ lines, pause, breakpoint }) => {
179
161
  }
180
162
 
181
163
  case 'b': {
182
- if (!lastSpecial) { // b pressed normally
164
+ if (!lastSpecial) {
165
+ // b pressed normally
183
166
  breakpoints[currentLine + scrollOffset] = !breakpoints[currentLine + scrollOffset];
184
167
  draw();
185
168
 
@@ -188,7 +171,6 @@ export default ({ lines, pause, breakpoint }) => {
188
171
  }
189
172
 
190
173
  // arrow down
191
- // if (screenOffset + termHeight <= lines.length) scrollOffset++;
192
174
  if (scrollOffset < lines.length - currentLine - 1) scrollOffset++;
193
175
  draw();
194
176
  break;
@@ -198,7 +180,6 @@ export default ({ lines, pause, breakpoint }) => {
198
180
  if (!lastSpecial) break;
199
181
 
200
182
  // arrow up
201
- // if (screenOffset > 0) scrollOffset--;
202
183
  if (scrollOffset > -currentLine) scrollOffset--;
203
184
  draw();
204
185
 
@@ -209,7 +190,6 @@ export default ({ lines, pause, breakpoint }) => {
209
190
  if (!lastSpecial) break;
210
191
 
211
192
  // page up
212
- // scrollOffset -= Math.min(screenOffset, termHeight - 1);
213
193
  scrollOffset -= Math.min(scrollOffset + currentLine, termHeight - 1);
214
194
  draw();
215
195
  break;
@@ -219,7 +199,6 @@ export default ({ lines, pause, breakpoint }) => {
219
199
  if (!lastSpecial) break;
220
200
 
221
201
  // page down
222
- // scrollOffset += Math.min((lines.length + 1) - (screenOffset + termHeight), termHeight - 1);
223
202
  scrollOffset += Math.min(lines.length - (scrollOffset + currentLine) - 1, termHeight - 1);
224
203
  draw();
225
204
  break;
package/compiler/2c.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { read_ieee754_binary64, read_signedLEB128, read_unsignedLEB128 } from './encoding.js';
2
2
  import { Blocktype, Opcodes, Valtype } from './wasmSpec.js';
3
3
  import { operatorOpcode } from './expression.js';
4
- import { log } from "./log.js";
4
+ import { log } from './log.js';
5
5
 
6
6
  const CValtype = {
7
7
  i8: 'i8',
@@ -106,17 +106,6 @@ for (const x in CValtype) {
106
106
  if (Valtype[x]) CValtype[Valtype[x]] = CValtype[x];
107
107
  }
108
108
 
109
- const todo = msg => {
110
- class TodoError extends Error {
111
- constructor(message) {
112
- super(message);
113
- this.name = 'TodoError';
114
- }
115
- }
116
-
117
- throw new TodoError(msg);
118
- };
119
-
120
109
  const removeBrackets = str => {
121
110
  // return str;
122
111
  // if (str.startsWith(`(${CValtype.i32})(${CValtype.u32})`)) return `(${CValtype.i32})(${CValtype.u32})(` + removeBrackets(str.slice(22, -1)) + ')';
@@ -130,6 +119,9 @@ const removeBrackets = str => {
130
119
  };
131
120
 
132
121
  export default ({ funcs, globals, tags, data, exceptions, pages }) => {
122
+ // fix declaring order for c
123
+ funcs.reverse();
124
+
133
125
  const invOperatorOpcode = Object.values(operatorOpcode).reduce((acc, x) => {
134
126
  for (const k in x) {
135
127
  acc[x[k]] = k;
@@ -167,10 +159,10 @@ export default ({ funcs, globals, tags, data, exceptions, pages }) => {
167
159
  prependMain.set('_data', data.map(x => `memcpy(_memory + ${x.offset}, (unsigned char[]){${x.bytes.join(',')}}, ${x.bytes.length});`).join('\n'));
168
160
  }
169
161
 
170
- // for (const [ x, p ] of pages) {
171
- // out += `${CValtype[p.type]} ${x.replace(': ', '_').replace(/[^0-9a-zA-Z_]/g, '')}[100]`;
172
- // out += ';\n';
173
- // }
162
+ if (importFuncs.find(x => x.name === '__Porffor_readArgv')) {
163
+ prepend.set('argv', `int _argc; char** _argv;`);
164
+ prependMain.set('argv', `_argc = argc; _argv = argv;`);
165
+ }
174
166
 
175
167
  if (out) out += '\n';
176
168
 
@@ -213,9 +205,11 @@ export default ({ funcs, globals, tags, data, exceptions, pages }) => {
213
205
 
214
206
  for (const f of funcs) {
215
207
  depth = 1;
208
+ brDepth = 0;
209
+
210
+ let retTmpId = 0;
216
211
 
217
212
  const invLocals = inv(f.locals, x => x.idx);
218
- // if (f.returns.length > 1) todo('funcs returning >1 value unsupported');
219
213
 
220
214
  for (const x in invLocals) {
221
215
  invLocals[x] = sanitize(invLocals[x]);
@@ -223,8 +217,9 @@ export default ({ funcs, globals, tags, data, exceptions, pages }) => {
223
217
 
224
218
  const returns = f.returns.length > 0;
225
219
 
226
- const shouldInline = f.internal;
227
- out += `${f.name === 'main' ? 'int' : (f.internal ? (returns ? CValtype.f64 : 'void') : 'struct ReturnValue')} ${shouldInline ? 'inline ' : ''}${sanitize(f.name)}(${f.params.map((x, i) => `${CValtype[x]} ${invLocals[i]}`).join(', ')}) {\n`;
220
+ const shouldInline = false; // f.internal;
221
+ if (f.name === 'main') out += `int main(${prependMain.has('argv') ? 'int argc, char* argv[]' : ''}) {\n`;
222
+ else out += `${f.internal ? (returns ? CValtype.f64 : 'void') : 'struct ReturnValue'} ${shouldInline ? 'inline ' : ''}${sanitize(f.name)}(${f.params.map((x, i) => `${CValtype[x]} ${invLocals[i]}`).join(', ')}) {\n`;
228
223
 
229
224
  if (f.name === 'main') {
230
225
  out += [...prependMain.values()].join('\n');
@@ -247,12 +242,7 @@ export default ({ funcs, globals, tags, data, exceptions, pages }) => {
247
242
  const brs = [];
248
243
  let lastCond = false;
249
244
 
250
- // let brDepth = 0;
251
-
252
245
  const blockStart = (i, loop) => {
253
- // reset "stack"
254
- // vals = [];
255
-
256
246
  rets.push(i[1]);
257
247
 
258
248
  const br = brId++;
@@ -269,25 +259,6 @@ export default ({ funcs, globals, tags, data, exceptions, pages }) => {
269
259
  brDepth++;
270
260
  };
271
261
 
272
- const highlight = i => {
273
- const surrounding = 6;
274
-
275
- const decomp = decompile(f.wasm.slice(i - surrounding, i + surrounding + 1), '', 0, f.locals, f.params, f.returns, funcs, globals, exceptions).slice(0, -1).split('\n');
276
-
277
- const noAnsi = s => s.replace(/\u001b\[[0-9]+m/g, '');
278
- let longest = 0;
279
- for (let j = 0; j < decomp.length; j++) {
280
- longest = Math.max(longest, noAnsi(decomp[j]).length);
281
- }
282
-
283
- const middle = Math.floor(decomp.length / 2);
284
- decomp[middle] = `\x1B[47m\x1B[30m${noAnsi(decomp[middle])}${'\u00a0'.repeat(longest - noAnsi(decomp[middle]).length)}\x1B[0m`;
285
-
286
- console.log('\x1B[90m...\x1B[0m');
287
- console.log(decomp.join('\n'));
288
- console.log('\x1B[90m...\x1B[0m\n');
289
- };
290
-
291
262
  for (let _ = 0; _ < f.wasm.length; _++) {
292
263
  const i = f.wasm[_];
293
264
  if (!i || !i[0]) continue;
@@ -415,8 +386,6 @@ export default ({ funcs, globals, tags, data, exceptions, pages }) => {
415
386
  const br = brs.at(-1);
416
387
  const ret = rets.at(-1);
417
388
  if (ret && ret !== Blocktype.void) {
418
- // console.log(vals, ret);
419
- // console.log(decompile(f.wasm.slice(_ - 5, _ + 1)));
420
389
  if (vals.length > 0) line(`_r${br} = ${removeBrackets(vals.pop())}`);
421
390
  // vals.push(`_r${br}`);
422
391
  }
@@ -425,8 +394,6 @@ export default ({ funcs, globals, tags, data, exceptions, pages }) => {
425
394
  line(`} else {`, false);
426
395
  depth++;
427
396
 
428
- // reset "stack"
429
- // vals = [];
430
397
  break;
431
398
  }
432
399
 
@@ -441,8 +408,6 @@ export default ({ funcs, globals, tags, data, exceptions, pages }) => {
441
408
  const br = brs.pop();
442
409
  const ret = rets.pop();
443
410
  if (ret && ret !== Blocktype.void) {
444
- // console.log(vals, ret);
445
- // console.log(decompile(f.wasm.slice(_ - 5, _ + 1)));
446
411
  if (vals.length > 0) line(`_r${br} = ${removeBrackets(vals.pop())}`);
447
412
  vals.push(`_r${br}`);
448
413
  }
@@ -505,6 +470,60 @@ _time_out = _time.tv_nsec / 1000000. + _time.tv_sec * 1000.;`);
505
470
  winIncludes.set('windows.h', true);
506
471
  break;
507
472
 
473
+ case '__Porffor_readArgv':
474
+ includes.set('stdlib.h', true);
475
+
476
+ prepend.set('__Porffor_readArgv',
477
+ `void __Porffor_readArgv(u32 index, u32 outPtr) {
478
+ if (index >= _argc) {
479
+ printf("expected %d arguments\\n", index);
480
+ exit(1);
481
+ }
482
+
483
+ char* arg = _argv[index];
484
+
485
+ u32 read = 0;
486
+ char* out = _memory + outPtr + 4;
487
+ char ch;
488
+ while ((ch = *(arg++)) != 0) {
489
+ out[read++] = ch;
490
+ }
491
+
492
+ memcpy(_memory + outPtr, &read, sizeof(read));
493
+ }`);
494
+
495
+ line(`__Porffor_readArgv((u32)(${vals.at(-2)}), (u32)(${vals.pop()}))`);
496
+ vals.pop();
497
+ break;
498
+
499
+ case '__Porffor_readFile':
500
+ includes.set('stdio.h', true);
501
+ includes.set('stdlib.h', true);
502
+
503
+ prepend.set('__Porffor_readFile',
504
+ `void __Porffor_readFile(u32 pathPtr, u32 outPtr) {
505
+ char* path = _memory + pathPtr + 4;
506
+ FILE* fp = fopen(path, "r");
507
+ if (fp == NULL) {
508
+ printf("failed to open file: %s\\n", path);
509
+ exit(1);
510
+ }
511
+
512
+ u32 read = 0;
513
+ char* out = _memory + outPtr + 4;
514
+ char ch;
515
+ while ((ch = fgetc(fp)) != EOF) {
516
+ out[read++] = ch;
517
+ }
518
+
519
+ fclose(fp);
520
+
521
+ memcpy(_memory + outPtr, &read, sizeof(read));
522
+ }`);
523
+ line(`__Porffor_readFile((u32)(${vals.at(-2)}), (u32)(${vals.pop()}))`);
524
+ vals.pop();
525
+ break;
526
+
508
527
  default:
509
528
  log.warning('2c', `unimplemented import: ${importFunc.name}`);
510
529
  break;
@@ -520,7 +539,7 @@ _time_out = _time.tv_nsec / 1000000. + _time.tv_sec * 1000.;`);
520
539
  if (func.internal) {
521
540
  vals.push(`${sanitize(func.name)}(${args.join(', ')})`);
522
541
  } else {
523
- line(`const struct ReturnValue _ = ${sanitize(func.name)}(${args.join(', ')})`);
542
+ line(`const struct ReturnValue _${retTmpId++} = ${sanitize(func.name)}(${args.join(', ')})`);
524
543
  vals.push(`_.value`);
525
544
  vals.push(`_.type`);
526
545
  }
@@ -541,18 +560,14 @@ _time_out = _time.tv_nsec / 1000000. + _time.tv_sec * 1000.;`);
541
560
 
542
561
  case Opcodes.br: {
543
562
  const ret = rets[brDepth - i[1] - 1];
544
- // console.log(rets, brDepth, i[1], brDepth - i[1] - 1, ret, vals);
545
563
  if (ret !== Blocktype.void) line(`_r${brs[brDepth - i[1] - 1]} = ${removeBrackets(vals.pop())}`);
546
564
  line(`goto j${brs[brDepth - i[1] - 1]}`);
547
565
 
548
- // // reset "stack"
549
- // vals = [];
550
566
  break;
551
567
  }
552
568
 
553
569
  case Opcodes.br_if: {
554
570
  const ret = rets[brDepth - i[1] - 1];
555
- // console.log(rets, brDepth, i[1], brDepth - i[1] - 1, ret, vals);
556
571
 
557
572
  let cond = removeBrackets(vals.pop());
558
573
  if (!lastCond) {
@@ -602,7 +617,6 @@ _time_out = _time.tv_nsec / 1000000. + _time.tv_sec * 1000.;`);
602
617
  }
603
618
 
604
619
  log.warning('2c', `unimplemented op: ${invOpcodes[i[0]]}`);
605
- // todo(`unimplemented op: ${invOpcodes[i[0]]}`);
606
620
  }
607
621
 
608
622
  lastCond = false;
@@ -1,8 +1,7 @@
1
- import { Valtype, FuncType, Empty, ExportDesc, Section, Magic, ModuleVersion, Opcodes, PageSize } from './wasmSpec.js';
2
- import { encodeVector, encodeString, encodeLocal, unsignedLEB128, signedLEB128, ieee754_binary64, unsignedLEB128_into, signedLEB128_into, ieee754_binary64_into } from './encoding.js';
3
- // import { number } from './embedding.js';
1
+ import { Valtype, FuncType, ExportDesc, Section, Magic, ModuleVersion, Opcodes, PageSize, Reftype } from './wasmSpec.js';
2
+ import { encodeVector, encodeString, encodeLocal, unsignedLEB128, signedLEB128, unsignedLEB128_into, signedLEB128_into, ieee754_binary64_into } from './encoding.js';
4
3
  import { importedFuncs } from './builtins.js';
5
- import { log } from "./log.js";
4
+ import { log } from './log.js';
6
5
  import Prefs from './prefs.js';
7
6
 
8
7
  const createSection = (type, data) => [
@@ -66,6 +65,7 @@ export default (funcs, globals, tags, pages, data, flags) => {
66
65
  importFuncs = [...imports.values()];
67
66
 
68
67
  // fix call indexes for non-imports
68
+ // also fix call_indirect types
69
69
  const delta = importedFuncs.length - importFuncs.length;
70
70
  for (const f of funcs) {
71
71
  f.originalIndex = f.index;
@@ -75,6 +75,16 @@ export default (funcs, globals, tags, pages, data, flags) => {
75
75
  if ((inst[0] === Opcodes.call || inst[0] === Opcodes.return_call) && inst[1] >= importedFuncs.length) {
76
76
  inst[1] -= delta;
77
77
  }
78
+
79
+ if (inst[0] === Opcodes.call_indirect) {
80
+ const params = [];
81
+ for (let i = 0; i < inst[1]; i++) {
82
+ params.push(valtypeBinary, Valtype.i32);
83
+ }
84
+
85
+ const returns = [ valtypeBinary, Valtype.i32 ];
86
+ inst[1] = getType(params, returns);
87
+ }
78
88
  }
79
89
  }
80
90
  }
@@ -92,17 +102,37 @@ export default (funcs, globals, tags, pages, data, flags) => {
92
102
  encodeVector(funcs.map(x => getType(x.params, x.returns))) // type indexes
93
103
  );
94
104
 
95
- // compilation hints section - unspecd, v8 only
96
- // https://github.com/WebAssembly/design/issues/1473#issuecomment-1431274746
97
- const chSection = !compileHints ? [] : customSection(
98
- 'compilationHints',
99
- // for now just do everything as optimize eager
100
- encodeVector(funcs.map(_ => chHint(0x02, 0x02, 0x02)))
105
+ const tableSection = !funcs.table ? [] : createSection(
106
+ Section.table,
107
+ encodeVector([ [ Reftype.funcref, 0x00, funcs.length ] ])
101
108
  );
102
109
 
110
+ const elementSection = !funcs.table ? [] : createSection(
111
+ Section.element,
112
+ encodeVector([ [
113
+ 0x00,
114
+ Opcodes.i32_const, 0, Opcodes.end,
115
+ ...encodeVector(funcs.map(x => x.index))
116
+ ] ])
117
+ );
118
+
119
+ if (pages.has('func argc lut')) {
120
+ // generate func argc lut data
121
+ const bytes = [];
122
+ for (let i = 0; i < funcs.length; i++) {
123
+ const argc = Math.floor(funcs[i].params.length / 2);
124
+ bytes.push(argc % 256, (argc / 256 | 0) % 256);
125
+ }
126
+
127
+ data.push({
128
+ offset: pages.get('func argc lut').ind * pageSize,
129
+ bytes
130
+ });
131
+ }
132
+
103
133
  // const t0 = performance.now();
104
134
 
105
- // specially optimized assembly for globals as this version is much (>5x) faster than traditional createSection(...)
135
+ // specially optimized assembly for globals as this version is much (>5x) faster than traditional createSection()
106
136
  const globalsValues = Object.values(globals);
107
137
 
108
138
  let globalSection = [];
@@ -227,6 +257,14 @@ export default (funcs, globals, tags, pages, data, flags) => {
227
257
  dataSection: dataSection.map(x => x.toString(16)),
228
258
  });
229
259
 
260
+ // compilation hints section - unspecd, v8 only
261
+ // https://github.com/WebAssembly/design/issues/1473#issuecomment-1431274746
262
+ const chSection = !compileHints ? [] : customSection(
263
+ 'compilationHints',
264
+ // for now just do everything as optimize eager
265
+ encodeVector(funcs.map(_ => chHint(0x02, 0x02, 0x02)))
266
+ );
267
+
230
268
  return Uint8Array.from([
231
269
  ...Magic,
232
270
  ...ModuleVersion,
@@ -234,10 +272,12 @@ export default (funcs, globals, tags, pages, data, flags) => {
234
272
  ...importSection,
235
273
  ...funcSection,
236
274
  ...chSection,
275
+ ...tableSection,
237
276
  ...memorySection,
238
277
  ...tagSection,
239
278
  ...globalSection,
240
279
  ...exportSection,
280
+ ...elementSection,
241
281
  ...dataCountSection,
242
282
  ...codeSection,
243
283
  ...dataSection
@@ -2,7 +2,7 @@ export default () => {
2
2
  let out = `// @porf --funsafe-no-unlikely-proto-checks --valtype=i32
3
3
  `;
4
4
 
5
- const annexB_noArgs = (a0, a1) => out += `
5
+ const noArgs = (a0, a1) => out += `
6
6
  export const __String_prototype_${a0} = (_this: string) => {
7
7
  let out: string = Porffor.s\`<${a1}>\`;
8
8
 
@@ -58,15 +58,15 @@ ${[...a1].map((x, i) => ` Porffor.wasm.i32.store8(outPtr, ${x.charCodeAt(0)}, 0
58
58
  };
59
59
  `;
60
60
 
61
- annexB_noArgs('big', 'big');
62
- annexB_noArgs('blink', 'blink');
63
- annexB_noArgs('bold', 'b');
64
- annexB_noArgs('fixed', 'tt');
65
- annexB_noArgs('italics', 'i');
66
- annexB_noArgs('small', 'small');
67
- annexB_noArgs('strike', 'strike');
68
- annexB_noArgs('sub', 'sub');
69
- annexB_noArgs('sup', 'sup');
61
+ noArgs('big', 'big');
62
+ noArgs('blink', 'blink');
63
+ noArgs('bold', 'b');
64
+ noArgs('fixed', 'tt');
65
+ noArgs('italics', 'i');
66
+ noArgs('small', 'small');
67
+ noArgs('strike', 'strike');
68
+ noArgs('sub', 'sub');
69
+ noArgs('sup', 'sup');
70
70
 
71
71
  return out;
72
72
  };
@@ -1,4 +1,5 @@
1
- // @porf --funsafe-no-unlikely-proto-checks --valtype=i32
1
+ // @porf --valtype=i32
2
+ import type {} from './porffor.d.ts';
2
3
 
3
4
  export const __String_prototype_trimLeft = (_this: string) => {
4
5
  return __String_prototype_trimStart(_this);
@@ -13,6 +14,6 @@ export const __String_prototype_trimRight = (_this: string) => {
13
14
  return __String_prototype_trimEnd(_this);
14
15
  };
15
16
 
16
- export const __ByteString_prototype_trimEnd = (_this: string) => {
17
- return __ByteString_prototype_trimRight(_this);
17
+ export const __ByteString_prototype_trimRight = (_this: string) => {
18
+ return __ByteString_prototype_trimEnd(_this);
18
19
  };