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.
- package/CONTRIBUTING.md +12 -7
- package/README.md +18 -15
- package/asur/index.js +1 -1
- package/byg/index.js +3 -24
- package/compiler/2c.js +69 -55
- package/compiler/assemble.js +51 -11
- package/compiler/builtins/annexb_string.js +10 -10
- package/compiler/builtins/annexb_string.ts +4 -3
- package/compiler/builtins/array.ts +85 -9
- package/compiler/builtins/base64.ts +2 -1
- package/compiler/builtins/boolean.ts +2 -2
- package/compiler/builtins/console.ts +4 -0
- package/compiler/builtins/crypto.ts +2 -1
- package/compiler/builtins/date.ts +2 -3
- package/compiler/builtins/error.js +22 -0
- package/compiler/builtins/escape.ts +2 -3
- package/compiler/builtins/function.ts +1 -1
- package/compiler/builtins/int.ts +1 -1
- package/compiler/builtins/math.ts +410 -0
- package/compiler/builtins/number.ts +4 -7
- package/compiler/builtins/object.ts +1 -1
- package/compiler/builtins/porffor.d.ts +9 -8
- package/compiler/builtins/set.ts +197 -3
- package/compiler/builtins/string.ts +2 -1
- package/compiler/builtins/symbol.ts +62 -0
- package/compiler/builtins.js +30 -15
- package/compiler/codegen.js +641 -365
- package/compiler/decompile.js +7 -3
- package/compiler/embedding.js +2 -2
- package/compiler/encoding.js +0 -14
- package/compiler/expression.js +1 -1
- package/compiler/generated_builtins.js +781 -187
- package/compiler/index.js +5 -11
- package/compiler/opt.js +7 -7
- package/compiler/parse.js +2 -4
- package/compiler/precompile.js +18 -25
- package/compiler/prefs.js +6 -2
- package/compiler/prototype.js +185 -162
- package/compiler/wasmSpec.js +5 -0
- package/compiler/wrap.js +150 -90
- package/package.json +1 -1
- package/runner/compare.js +0 -1
- package/runner/debug.js +1 -6
- package/runner/index.js +5 -4
- package/runner/profiler.js +15 -42
- package/runner/repl.js +20 -10
- package/runner/sizes.js +2 -2
- 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`.
|
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
|
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.
|
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
|
-
###
|
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 >
|
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 (
|
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
|
-
|
335
|
-
|
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
|
-
|
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) {
|
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
|
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
|
-
|
171
|
-
|
172
|
-
|
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
|
-
|
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;
|
package/compiler/assemble.js
CHANGED
@@ -1,8 +1,7 @@
|
|
1
|
-
import { Valtype, FuncType,
|
2
|
-
import { encodeVector, encodeString, encodeLocal, unsignedLEB128, signedLEB128,
|
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
|
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
|
-
|
96
|
-
|
97
|
-
|
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
|
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
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
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 --
|
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
|
17
|
-
return
|
17
|
+
export const __ByteString_prototype_trimRight = (_this: string) => {
|
18
|
+
return __ByteString_prototype_trimEnd(_this);
|
18
19
|
};
|