porffor 0.2.0-fdf0fc5 โ†’ 0.14.0-032e4ad08

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 +9 -3
  2. package/README.md +17 -14
  3. package/asur/index.js +1 -1
  4. package/byg/index.js +3 -24
  5. package/compiler/2c.js +66 -54
  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/crypto.ts +2 -1
  13. package/compiler/builtins/date.ts +2 -3
  14. package/compiler/builtins/error.js +22 -0
  15. package/compiler/builtins/escape.ts +2 -3
  16. package/compiler/builtins/function.ts +1 -1
  17. package/compiler/builtins/int.ts +1 -1
  18. package/compiler/builtins/math.ts +410 -0
  19. package/compiler/builtins/number.ts +4 -7
  20. package/compiler/builtins/object.ts +1 -1
  21. package/compiler/builtins/porffor.d.ts +9 -8
  22. package/compiler/builtins/set.ts +197 -3
  23. package/compiler/builtins/string.ts +2 -1
  24. package/compiler/builtins/symbol.ts +62 -0
  25. package/compiler/builtins.js +41 -15
  26. package/compiler/codegen.js +653 -366
  27. package/compiler/decompile.js +3 -3
  28. package/compiler/embedding.js +2 -2
  29. package/compiler/encoding.js +0 -14
  30. package/compiler/expression.js +1 -1
  31. package/compiler/generated_builtins.js +771 -187
  32. package/compiler/index.js +5 -11
  33. package/compiler/opt.js +7 -7
  34. package/compiler/parse.js +2 -4
  35. package/compiler/precompile.js +18 -25
  36. package/compiler/prefs.js +6 -2
  37. package/compiler/prototype.js +185 -162
  38. package/compiler/wasmSpec.js +5 -0
  39. package/compiler/wrap.js +150 -90
  40. package/package.json +1 -1
  41. package/porffor_tmp.c +202 -0
  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
@@ -198,11 +198,13 @@ 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(...))`.
206
208
 
207
209
  <br>
208
210
 
@@ -231,7 +233,11 @@ builtins/tostring_number: impl radix
231
233
 
232
234
  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
235
 
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):
236
+ Run `node test262` to run all the tests and get an output of total overall test results.
237
+
238
+ 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.
239
+
240
+ The main thing you want to pay attention to is the emoji summary (lol):
235
241
  ```
236
242
  ๐Ÿงช 50005 | ๐Ÿค  7007 (-89) | โŒ 1914 (-32) | ๐Ÿ’€ 13904 (-61) | ๐Ÿ“ 23477 (-120) | โฐ 2 | ๐Ÿ— 2073 (+302) | ๐Ÿ’ฅ 1628
237
243
  ```
package/README.md CHANGED
@@ -13,7 +13,7 @@ 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
16
+ ### Install
17
17
  **`npm install -g porffor`**. It's that easy (hopefully) :)
18
18
 
19
19
  ### Trying a REPL
@@ -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,9 @@ export default ({ funcs, globals, tags, data, exceptions, pages }) => {
213
205
 
214
206
  for (const f of funcs) {
215
207
  depth = 1;
208
+ brDepth = 0;
216
209
 
217
210
  const invLocals = inv(f.locals, x => x.idx);
218
- // if (f.returns.length > 1) todo('funcs returning >1 value unsupported');
219
211
 
220
212
  for (const x in invLocals) {
221
213
  invLocals[x] = sanitize(invLocals[x]);
@@ -223,8 +215,9 @@ export default ({ funcs, globals, tags, data, exceptions, pages }) => {
223
215
 
224
216
  const returns = f.returns.length > 0;
225
217
 
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`;
218
+ const shouldInline = false; // f.internal;
219
+ if (f.name === 'main') out += `int main(${prependMain.has('argv') ? 'int argc, char* argv[]' : ''}) {\n`;
220
+ 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
221
 
229
222
  if (f.name === 'main') {
230
223
  out += [...prependMain.values()].join('\n');
@@ -247,12 +240,7 @@ export default ({ funcs, globals, tags, data, exceptions, pages }) => {
247
240
  const brs = [];
248
241
  let lastCond = false;
249
242
 
250
- // let brDepth = 0;
251
-
252
243
  const blockStart = (i, loop) => {
253
- // reset "stack"
254
- // vals = [];
255
-
256
244
  rets.push(i[1]);
257
245
 
258
246
  const br = brId++;
@@ -269,25 +257,6 @@ export default ({ funcs, globals, tags, data, exceptions, pages }) => {
269
257
  brDepth++;
270
258
  };
271
259
 
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
260
  for (let _ = 0; _ < f.wasm.length; _++) {
292
261
  const i = f.wasm[_];
293
262
  if (!i || !i[0]) continue;
@@ -415,8 +384,6 @@ export default ({ funcs, globals, tags, data, exceptions, pages }) => {
415
384
  const br = brs.at(-1);
416
385
  const ret = rets.at(-1);
417
386
  if (ret && ret !== Blocktype.void) {
418
- // console.log(vals, ret);
419
- // console.log(decompile(f.wasm.slice(_ - 5, _ + 1)));
420
387
  if (vals.length > 0) line(`_r${br} = ${removeBrackets(vals.pop())}`);
421
388
  // vals.push(`_r${br}`);
422
389
  }
@@ -425,8 +392,6 @@ export default ({ funcs, globals, tags, data, exceptions, pages }) => {
425
392
  line(`} else {`, false);
426
393
  depth++;
427
394
 
428
- // reset "stack"
429
- // vals = [];
430
395
  break;
431
396
  }
432
397
 
@@ -441,8 +406,6 @@ export default ({ funcs, globals, tags, data, exceptions, pages }) => {
441
406
  const br = brs.pop();
442
407
  const ret = rets.pop();
443
408
  if (ret && ret !== Blocktype.void) {
444
- // console.log(vals, ret);
445
- // console.log(decompile(f.wasm.slice(_ - 5, _ + 1)));
446
409
  if (vals.length > 0) line(`_r${br} = ${removeBrackets(vals.pop())}`);
447
410
  vals.push(`_r${br}`);
448
411
  }
@@ -505,6 +468,60 @@ _time_out = _time.tv_nsec / 1000000. + _time.tv_sec * 1000.;`);
505
468
  winIncludes.set('windows.h', true);
506
469
  break;
507
470
 
471
+ case '__Porffor_readArgv':
472
+ includes.set('stdlib.h', true);
473
+
474
+ prepend.set('__Porffor_readArgv',
475
+ `void __Porffor_readArgv(u32 index, u32 outPtr) {
476
+ if (index >= _argc) {
477
+ printf("expected %d arguments\\n", index);
478
+ exit(1);
479
+ }
480
+
481
+ char* arg = _argv[index];
482
+
483
+ u32 read = 0;
484
+ char* out = _memory + outPtr + 4;
485
+ char ch;
486
+ while ((ch = *(arg++)) != 0) {
487
+ out[read++] = ch;
488
+ }
489
+
490
+ memcpy(_memory + outPtr, &read, sizeof(read));
491
+ }`);
492
+
493
+ line(`__Porffor_readArgv((u32)(${vals.at(-2)}), (u32)(${vals.pop()}))`);
494
+ vals.pop();
495
+ break;
496
+
497
+ case '__Porffor_readFile':
498
+ includes.set('stdio.h', true);
499
+ includes.set('stdlib.h', true);
500
+
501
+ prepend.set('__Porffor_readFile',
502
+ `void __Porffor_readFile(u32 pathPtr, u32 outPtr) {
503
+ char* path = _memory + pathPtr + 4;
504
+ FILE* fp = fopen(path, "r");
505
+ if (fp == NULL) {
506
+ printf("failed to open file: %s\\n", path);
507
+ exit(1);
508
+ }
509
+
510
+ u32 read = 0;
511
+ char* out = _memory + outPtr + 4;
512
+ char ch;
513
+ while ((ch = fgetc(fp)) != EOF) {
514
+ out[read++] = ch;
515
+ }
516
+
517
+ fclose(fp);
518
+
519
+ memcpy(_memory + outPtr, &read, sizeof(read));
520
+ }`);
521
+ line(`__Porffor_readFile((u32)(${vals.at(-2)}), (u32)(${vals.pop()}))`);
522
+ vals.pop();
523
+ break;
524
+
508
525
  default:
509
526
  log.warning('2c', `unimplemented import: ${importFunc.name}`);
510
527
  break;
@@ -541,18 +558,14 @@ _time_out = _time.tv_nsec / 1000000. + _time.tv_sec * 1000.;`);
541
558
 
542
559
  case Opcodes.br: {
543
560
  const ret = rets[brDepth - i[1] - 1];
544
- // console.log(rets, brDepth, i[1], brDepth - i[1] - 1, ret, vals);
545
561
  if (ret !== Blocktype.void) line(`_r${brs[brDepth - i[1] - 1]} = ${removeBrackets(vals.pop())}`);
546
562
  line(`goto j${brs[brDepth - i[1] - 1]}`);
547
563
 
548
- // // reset "stack"
549
- // vals = [];
550
564
  break;
551
565
  }
552
566
 
553
567
  case Opcodes.br_if: {
554
568
  const ret = rets[brDepth - i[1] - 1];
555
- // console.log(rets, brDepth, i[1], brDepth - i[1] - 1, ret, vals);
556
569
 
557
570
  let cond = removeBrackets(vals.pop());
558
571
  if (!lastCond) {
@@ -602,7 +615,6 @@ _time_out = _time.tv_nsec / 1000000. + _time.tv_sec * 1000.;`);
602
615
  }
603
616
 
604
617
  log.warning('2c', `unimplemented op: ${invOpcodes[i[0]]}`);
605
- // todo(`unimplemented op: ${invOpcodes[i[0]]}`);
606
618
  }
607
619
 
608
620
  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
  };