porffor 0.14.0-f67c123a1 β 0.16.0-a2e115b05
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 +15 -9
- package/README.md +9 -13
- package/asur/index.js +1 -1
- package/compiler/2c.js +104 -51
- package/compiler/assemble.js +26 -1
- package/compiler/builtins/annexb_string.ts +1 -0
- package/compiler/builtins/array.ts +84 -4
- package/compiler/builtins/base64.ts +1 -0
- package/compiler/builtins/boolean.ts +3 -1
- package/compiler/builtins/console.ts +6 -0
- package/compiler/builtins/crypto.ts +1 -0
- package/compiler/builtins/date.ts +2 -0
- package/compiler/builtins/error.js +22 -0
- package/compiler/builtins/escape.ts +1 -2
- package/compiler/builtins/function.ts +2 -0
- package/compiler/builtins/int.ts +2 -0
- package/compiler/builtins/math.ts +410 -0
- package/compiler/builtins/number.ts +2 -0
- package/compiler/builtins/object.ts +2 -0
- package/compiler/builtins/porffor.d.ts +11 -0
- package/compiler/builtins/set.ts +20 -7
- package/compiler/builtins/string.ts +1 -0
- package/compiler/builtins/symbol.ts +62 -0
- package/compiler/builtins.js +48 -10
- package/compiler/codegen.js +569 -258
- package/compiler/decompile.js +5 -1
- package/compiler/generated_builtins.js +555 -60
- package/compiler/index.js +5 -9
- package/compiler/opt.js +2 -2
- package/compiler/parse.js +2 -2
- package/compiler/precompile.js +5 -4
- package/compiler/prefs.js +6 -2
- package/compiler/prototype.js +180 -157
- package/compiler/wrap.js +82 -40
- package/package.json +1 -1
- package/runner/index.js +5 -4
- package/runner/repl.js +18 -2
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,15 @@ 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.
|
209
|
+
- Generally prefer/use non-strict equality ops (`==`/`!=`).
|
206
210
|
|
207
211
|
<br>
|
208
212
|
|
@@ -229,9 +233,13 @@ builtins/tostring_number: impl radix
|
|
229
233
|
|
230
234
|
## Test262
|
231
235
|
|
232
|
-
|
236
|
+
For the first time, ensure you run `./test262/setup.sh`.
|
233
237
|
|
234
|
-
Run `node test262` to run all the tests and get an output of total overall test results.
|
238
|
+
Run `node test262` to run all the tests and get an output of total overall test results.
|
239
|
+
|
240
|
+
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.
|
241
|
+
|
242
|
+
The main thing you want to pay attention to is the emoji summary (lol):
|
235
243
|
```
|
236
244
|
π§ͺ 50005 | π€ 7007 (-89) | β 1914 (-32) | π 13904 (-61) | π 23477 (-120) | β° 2 | π 2073 (+302) | π₯ 1628
|
237
245
|
```
|
@@ -239,7 +247,7 @@ Run `node test262` to run all the tests and get an output of total overall test
|
|
239
247
|
To break this down:
|
240
248
|
π§ͺ total π€ pass β fail π runtime error π todo (error) β° timeout ποΈ wasm compile error π₯ compile error
|
241
249
|
|
242
|
-
The diff compared to the last commit (with test262 data) is shown in brackets. Basically, you
|
250
|
+
The diff compared to the last commit (with test262 data) is shown in brackets. Basically, you want passes π€ up, and errors ππππ₯ down. It is fine if some errors change balance/etc, as long as they are not new failures.
|
243
251
|
|
244
252
|
It will also log new passes/fails. Be careful as sometimes the overall passes can increase, but other files have also regressed into failures which you might miss. Also keep in mind some tests may have been false positives before, but we can investigate the diff together :)
|
245
253
|
|
@@ -251,6 +259,4 @@ It will also log new passes/fails. Be careful as sometimes the overall passes ca
|
|
251
259
|
|
252
260
|
<br>
|
253
261
|
|
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).
|
262
|
+
[^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
@@ -14,7 +14,7 @@ Porffor is primarily built from scratch, the only thing that is not is the parse
|
|
14
14
|
Expect nothing to work! Only very limited JS is currently supported. See files in `bench` for examples.
|
15
15
|
|
16
16
|
### Install
|
17
|
-
**`npm install -g porffor`**. It's that easy (hopefully) :)
|
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.
|
@@ -266,8 +266,6 @@ Basically none right now (other than giving people headaches). Potential ideas:
|
|
266
266
|
No particular order and no guarentees, just what could happen soonβ’
|
267
267
|
|
268
268
|
- Arrays
|
269
|
-
- More of `Array` prototype
|
270
|
-
- Arrays/strings inside arrays
|
271
269
|
- Destructuring
|
272
270
|
- Objects
|
273
271
|
- Basic object expressions (eg `{}`, `{ a: 0 }`)
|
@@ -315,16 +313,10 @@ Porffor intentionally does not use Wasm proposals which are not commonly impleme
|
|
315
313
|
|
316
314
|
- Multi-value **(required)**
|
317
315
|
- Non-trapping float-to-int conversions **(required)**
|
318
|
-
- Bulk memory operations (
|
319
|
-
- Exception handling (optional, for errors)
|
316
|
+
- Bulk memory operations (optional, can get away without sometimes)
|
317
|
+
- Exception handling (optional, only for errors)
|
320
318
|
- Tail calls (opt-in, off by default)
|
321
319
|
|
322
|
-
## Isn't this the same as AssemblyScript/other Wasm langs?
|
323
|
-
No. they are not alike at all internally and have very different goals/ideals:
|
324
|
-
- Porffor is made as a generic JS engine, not for Wasm stuff specifically
|
325
|
-
- Porffor primarily consumes JS
|
326
|
-
- Porffor is written in pure JS and compiles itself, not using Binaryen/etc
|
327
|
-
- (Also I didn't know it existed when I started this, lol)
|
328
320
|
|
329
321
|
## FAQ
|
330
322
|
|
@@ -338,5 +330,9 @@ No. they are not alike at all internally and have very different goals/ideals:
|
|
338
330
|
### 2. Why at all?
|
339
331
|
Yes!
|
340
332
|
|
341
|
-
|
342
|
-
|
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/compiler/2c.js
CHANGED
@@ -29,8 +29,8 @@ typedef double f64;
|
|
29
29
|
f64 NAN = 0e+0/0e+0;
|
30
30
|
|
31
31
|
struct ReturnValue {
|
32
|
-
|
33
|
-
|
32
|
+
f64 value;
|
33
|
+
i32 type;
|
34
34
|
};\n\n`;
|
35
35
|
|
36
36
|
// todo: is memcpy/etc safe with host endianness?
|
@@ -41,60 +41,60 @@ const CMemFuncs = {
|
|
41
41
|
[Opcodes.i32_store]: {
|
42
42
|
c: `memcpy(_memory + offset + pointer, &value, sizeof(value));`,
|
43
43
|
args: ['pointer', 'value'],
|
44
|
-
argTypes: [
|
44
|
+
argTypes: ['i32', 'i32'],
|
45
45
|
returns: false
|
46
46
|
},
|
47
47
|
[Opcodes.i32_store16]: {
|
48
48
|
c: `memcpy(_memory + offset + pointer, &value, sizeof(value));`,
|
49
49
|
args: ['pointer', 'value'],
|
50
|
-
argTypes: [
|
50
|
+
argTypes: ['i32', 'i16'],
|
51
51
|
returns: false
|
52
52
|
},
|
53
53
|
[Opcodes.i32_store8]: {
|
54
54
|
c: `memcpy(_memory + offset + pointer, &value, sizeof(value));`,
|
55
55
|
args: ['pointer', 'value'],
|
56
|
-
argTypes: [
|
56
|
+
argTypes: ['i32', 'i8'],
|
57
57
|
returns: false
|
58
58
|
},
|
59
59
|
|
60
60
|
[Opcodes.i32_load]: {
|
61
|
-
c:
|
61
|
+
c: `i32 out;
|
62
62
|
memcpy(&out, _memory + offset + pointer, sizeof(out));
|
63
63
|
return out;`,
|
64
64
|
args: ['pointer'],
|
65
|
-
argTypes: [
|
66
|
-
returns:
|
65
|
+
argTypes: ['i32'],
|
66
|
+
returns: 'i32'
|
67
67
|
},
|
68
68
|
[Opcodes.i32_load16_u]: {
|
69
|
-
c:
|
69
|
+
c: `i16 out;
|
70
70
|
memcpy(&out, _memory + offset + pointer, sizeof(out));
|
71
71
|
return out;`,
|
72
72
|
args: ['pointer'],
|
73
|
-
argTypes: [
|
74
|
-
returns:
|
73
|
+
argTypes: ['i32'],
|
74
|
+
returns: 'i32'
|
75
75
|
},
|
76
76
|
[Opcodes.i32_load8_u]: {
|
77
|
-
c:
|
77
|
+
c: `i8 out;
|
78
78
|
memcpy(&out, _memory + offset + pointer, sizeof(out));
|
79
79
|
return out;`,
|
80
80
|
args: ['pointer'],
|
81
|
-
argTypes: [
|
82
|
-
returns:
|
81
|
+
argTypes: ['i32'],
|
82
|
+
returns: 'i32'
|
83
83
|
},
|
84
84
|
|
85
85
|
[Opcodes.f64_store]: {
|
86
86
|
c: `memcpy(_memory + offset + pointer, &value, sizeof(value));`,
|
87
87
|
args: ['pointer', 'value'],
|
88
|
-
argTypes: [
|
88
|
+
argTypes: ['i32', 'f64'],
|
89
89
|
returns: false
|
90
90
|
},
|
91
91
|
[Opcodes.f64_load]: {
|
92
|
-
c:
|
92
|
+
c: `f64 out;
|
93
93
|
memcpy(&out, _memory + offset + pointer, sizeof(out));
|
94
94
|
return out;`,
|
95
95
|
args: ['pointer'],
|
96
|
-
argTypes: [
|
97
|
-
returns:
|
96
|
+
argTypes: ['i32'],
|
97
|
+
returns: 'f64'
|
98
98
|
},
|
99
99
|
};
|
100
100
|
|
@@ -108,7 +108,7 @@ for (const x in CValtype) {
|
|
108
108
|
|
109
109
|
const removeBrackets = str => {
|
110
110
|
// return str;
|
111
|
-
// if (str.startsWith(
|
111
|
+
// if (str.startsWith('(i32)(u32)')) return '(i32)(u32)(' + removeBrackets(str.slice(22, -1)) + ')';
|
112
112
|
|
113
113
|
for (const x in CValtype) {
|
114
114
|
const p = `(${x})`;
|
@@ -119,6 +119,9 @@ const removeBrackets = str => {
|
|
119
119
|
};
|
120
120
|
|
121
121
|
export default ({ funcs, globals, tags, data, exceptions, pages }) => {
|
122
|
+
// fix declaring order for c
|
123
|
+
funcs.reverse();
|
124
|
+
|
122
125
|
const invOperatorOpcode = Object.values(operatorOpcode).reduce((acc, x) => {
|
123
126
|
for (const k in x) {
|
124
127
|
acc[x[k]] = k;
|
@@ -153,17 +156,22 @@ export default ({ funcs, globals, tags, data, exceptions, pages }) => {
|
|
153
156
|
}
|
154
157
|
|
155
158
|
if (data.length > 0) {
|
156
|
-
prependMain.set('_data', data.map(x => `memcpy(_memory + ${x.offset}, (unsigned char[]){${x.bytes.join(',')}}, ${x.bytes.length});`).join('\n'));
|
159
|
+
prependMain.set('_data', data.map(x => `memcpy(_memory + ${x.offset}, (unsigned char[]){${x.bytes.join(',')}}, ${x.bytes.length});`).join('\n '));
|
160
|
+
}
|
161
|
+
|
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;`);
|
157
165
|
}
|
158
166
|
|
159
167
|
if (out) out += '\n';
|
160
168
|
|
161
169
|
let depth = 1;
|
162
170
|
let brDepth = 0;
|
163
|
-
const line = (str, semi = true) => out += `${' '.repeat(depth
|
171
|
+
const line = (str, semi = true) => out += `${' '.repeat((depth + brDepth) * 2)}${str}${semi ? ';' : ''}\n`;
|
164
172
|
const lines = lines => {
|
165
173
|
for (const x of lines) {
|
166
|
-
out += `${' '.repeat(depth * 2)}${x}\n`;
|
174
|
+
out += `${' '.repeat((depth + brDepth) * 2)}${x}\n`;
|
167
175
|
}
|
168
176
|
};
|
169
177
|
|
@@ -199,6 +207,8 @@ export default ({ funcs, globals, tags, data, exceptions, pages }) => {
|
|
199
207
|
depth = 1;
|
200
208
|
brDepth = 0;
|
201
209
|
|
210
|
+
let retTmpId = 0;
|
211
|
+
|
202
212
|
const invLocals = inv(f.locals, x => x.idx);
|
203
213
|
|
204
214
|
for (const x in invLocals) {
|
@@ -207,11 +217,12 @@ export default ({ funcs, globals, tags, data, exceptions, pages }) => {
|
|
207
217
|
|
208
218
|
const returns = f.returns.length > 0;
|
209
219
|
|
210
|
-
const shouldInline = f.internal;
|
211
|
-
|
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 ? 'f64' : 'void') : 'struct ReturnValue'} ${shouldInline ? 'inline ' : ''}${sanitize(f.name)}(${f.params.map((x, i) => `${CValtype[x]} ${invLocals[i]}`).join(', ')}) {\n`;
|
212
223
|
|
213
224
|
if (f.name === 'main') {
|
214
|
-
out += [...prependMain.values()].join('\n');
|
225
|
+
out += ' ' + [...prependMain.values()].join('\n ');
|
215
226
|
if (prependMain.size > 0) out += '\n\n';
|
216
227
|
}
|
217
228
|
|
@@ -272,12 +283,12 @@ export default ({ funcs, globals, tags, data, exceptions, pages }) => {
|
|
272
283
|
switch (i[1]) {
|
273
284
|
// i32_trunc_sat_f64_s
|
274
285
|
case 0x02:
|
275
|
-
vals.push(`(
|
286
|
+
vals.push(`(i32)(${vals.pop()})`);
|
276
287
|
break;
|
277
288
|
|
278
289
|
// i32_trunc_sat_f64_u
|
279
290
|
case 0x03:
|
280
|
-
vals.push(`(
|
291
|
+
vals.push(`(u32)(${vals.pop()})`);
|
281
292
|
break;
|
282
293
|
}
|
283
294
|
|
@@ -326,7 +337,7 @@ export default ({ funcs, globals, tags, data, exceptions, pages }) => {
|
|
326
337
|
|
327
338
|
case Opcodes.f64_trunc:
|
328
339
|
// vals.push(`trunc(${vals.pop()})`);
|
329
|
-
vals.push(`(
|
340
|
+
vals.push(`(i32)(${removeBrackets(vals.pop())})`); // this is ~10x faster with clang??
|
330
341
|
break;
|
331
342
|
|
332
343
|
case Opcodes.f64_convert_i32_u:
|
@@ -334,7 +345,7 @@ export default ({ funcs, globals, tags, data, exceptions, pages }) => {
|
|
334
345
|
case Opcodes.f64_convert_i64_u:
|
335
346
|
case Opcodes.f64_convert_i64_s:
|
336
347
|
// int to f64
|
337
|
-
vals.push(`(
|
348
|
+
vals.push(`(f64)(${removeBrackets(vals.pop())})`);
|
338
349
|
break;
|
339
350
|
|
340
351
|
case Opcodes.i32_eqz:
|
@@ -342,7 +353,7 @@ export default ({ funcs, globals, tags, data, exceptions, pages }) => {
|
|
342
353
|
vals.push(`!(${removeBrackets(vals.pop())})`);
|
343
354
|
} else {
|
344
355
|
let cond = '(' + removeBrackets(vals.pop());
|
345
|
-
if (cond.startsWith(`(
|
356
|
+
if (cond.startsWith(`(i32)`)) cond = `${cond.slice(5)} == 0e+0`;
|
346
357
|
else cond += ') == 0';
|
347
358
|
vals.push(cond);
|
348
359
|
}
|
@@ -357,7 +368,7 @@ export default ({ funcs, globals, tags, data, exceptions, pages }) => {
|
|
357
368
|
case Opcodes.if: {
|
358
369
|
let cond = removeBrackets(vals.pop());
|
359
370
|
if (!lastCond) {
|
360
|
-
if (cond.startsWith(`(
|
371
|
+
if (cond.startsWith(`(i32)`)) cond = `${cond.slice(5)} != 0e+0`;
|
361
372
|
else cond = `(${cond}) != 0`;
|
362
373
|
}
|
363
374
|
|
@@ -423,28 +434,16 @@ export default ({ funcs, globals, tags, data, exceptions, pages }) => {
|
|
423
434
|
const importFunc = importFuncs[i[1]];
|
424
435
|
switch (importFunc.name) {
|
425
436
|
case 'print':
|
426
|
-
// line(`printf("%f\\n", ${vals.pop()})`);
|
427
437
|
line(`printf("${valtype === 'f64' ? '%g' : '%i'}\\n", ${vals.pop()})`);
|
428
438
|
includes.set('stdio.h', true);
|
429
439
|
break;
|
430
440
|
case 'printChar':
|
431
|
-
line(`
|
441
|
+
line(`putchar((int)(${vals.pop()}))`);
|
432
442
|
includes.set('stdio.h', true);
|
433
443
|
break;
|
434
444
|
|
435
445
|
case 'time':
|
436
446
|
line(`double _time_out`);
|
437
|
-
/* platformSpecific(
|
438
|
-
`FILETIME _time_filetime;
|
439
|
-
GetSystemTimeAsFileTime(&_time_filetime);
|
440
|
-
|
441
|
-
ULARGE_INTEGER _time_ularge;
|
442
|
-
_time_ularge.LowPart = _time_filetime.dwLowDateTime;
|
443
|
-
_time_ularge.HighPart = _time_filetime.dwHighDateTime;
|
444
|
-
_time_out = (_time_ularge.QuadPart - 116444736000000000i64) / 10000.;`,
|
445
|
-
`struct timespec _time;
|
446
|
-
clock_gettime(CLOCK_MONOTONIC, &_time);
|
447
|
-
_time_out = _time.tv_nsec / 1000000.;`); */
|
448
447
|
platformSpecific(
|
449
448
|
`LARGE_INTEGER _time_freq, _time_t;
|
450
449
|
QueryPerformanceFrequency(&_time_freq);
|
@@ -459,6 +458,61 @@ _time_out = _time.tv_nsec / 1000000. + _time.tv_sec * 1000.;`);
|
|
459
458
|
winIncludes.set('windows.h', true);
|
460
459
|
break;
|
461
460
|
|
461
|
+
case '__Porffor_readArgv': {
|
462
|
+
prepend.set('__Porffor_readArgv',
|
463
|
+
`i32 __Porffor_readArgv(u32 index, u32 outPtr) {
|
464
|
+
if (index >= _argc) {
|
465
|
+
return -1;
|
466
|
+
}
|
467
|
+
|
468
|
+
char* arg = _argv[index];
|
469
|
+
|
470
|
+
u32 read = 0;
|
471
|
+
char* out = _memory + outPtr + 4;
|
472
|
+
char ch;
|
473
|
+
while ((ch = *(arg++)) != 0) {
|
474
|
+
out[read++] = ch;
|
475
|
+
}
|
476
|
+
|
477
|
+
memcpy(_memory + outPtr, &read, sizeof(read));
|
478
|
+
return read;
|
479
|
+
}`);
|
480
|
+
|
481
|
+
const outPtr = vals.pop();
|
482
|
+
const index = vals.pop();
|
483
|
+
vals.push(`(f64)__Porffor_readArgv((u32)(${index}), (u32)(${outPtr}))`);
|
484
|
+
break;
|
485
|
+
}
|
486
|
+
|
487
|
+
case '__Porffor_readFile': {
|
488
|
+
includes.set('stdio.h', true);
|
489
|
+
|
490
|
+
prepend.set('__Porffor_readFile',
|
491
|
+
`i32 __Porffor_readFile(u32 pathPtr, u32 outPtr) {
|
492
|
+
char* path = _memory + pathPtr + 4;
|
493
|
+
FILE* fp = fopen(path, "r");
|
494
|
+
if (fp == NULL) {
|
495
|
+
return -1;
|
496
|
+
}
|
497
|
+
|
498
|
+
u32 read = 0;
|
499
|
+
char* out = _memory + outPtr + 4;
|
500
|
+
char ch;
|
501
|
+
while ((ch = fgetc(fp)) != EOF) {
|
502
|
+
out[read++] = ch;
|
503
|
+
}
|
504
|
+
|
505
|
+
fclose(fp);
|
506
|
+
|
507
|
+
memcpy(_memory + outPtr, &read, sizeof(read));
|
508
|
+
return read;
|
509
|
+
}`);
|
510
|
+
const outPtr = vals.pop();
|
511
|
+
const pathPtr = vals.pop();
|
512
|
+
vals.push(`(f64)__Porffor_readFile((u32)(${pathPtr}), (u32)(${outPtr}))`);
|
513
|
+
break;
|
514
|
+
}
|
515
|
+
|
462
516
|
default:
|
463
517
|
log.warning('2c', `unimplemented import: ${importFunc.name}`);
|
464
518
|
break;
|
@@ -474,9 +528,10 @@ _time_out = _time.tv_nsec / 1000000. + _time.tv_sec * 1000.;`);
|
|
474
528
|
if (func.internal) {
|
475
529
|
vals.push(`${sanitize(func.name)}(${args.join(', ')})`);
|
476
530
|
} else {
|
477
|
-
|
478
|
-
|
479
|
-
vals.push(`_.
|
531
|
+
const id = retTmpId++;
|
532
|
+
line(`const struct ReturnValue _${id} = ${sanitize(func.name)}(${args.join(', ')})`);
|
533
|
+
vals.push(`_${id}.value`);
|
534
|
+
vals.push(`_${id}.type`);
|
480
535
|
}
|
481
536
|
} else line(`${sanitize(func.name)}(${args.join(', ')})`);
|
482
537
|
|
@@ -506,7 +561,7 @@ _time_out = _time.tv_nsec / 1000000. + _time.tv_sec * 1000.;`);
|
|
506
561
|
|
507
562
|
let cond = removeBrackets(vals.pop());
|
508
563
|
if (!lastCond) {
|
509
|
-
if (cond.startsWith(`(
|
564
|
+
if (cond.startsWith(`(i32)`)) cond = `${cond.slice(5)} != 0e+0`;
|
510
565
|
else cond = `(${cond}) != 0`;
|
511
566
|
}
|
512
567
|
|
@@ -536,8 +591,7 @@ _time_out = _time.tv_nsec / 1000000. + _time.tv_sec * 1000.;`);
|
|
536
591
|
const name = invOpcodes[i[0]];
|
537
592
|
const func = CMemFuncs[i[0]];
|
538
593
|
if (!prepend.has(name)) {
|
539
|
-
prepend.set(name, `${func.returns || 'void'} ${name}(
|
540
|
-
// generate func c and prepend
|
594
|
+
prepend.set(name, `${func.returns || 'void'} ${name}(i32 align, i32 offset, ${func.args.map((x, i) => `${func.argTypes[i]} ${x}`).join(', ')}) {\n ${func.c.replaceAll('\n', '\n ')}\n}\n`);
|
541
595
|
}
|
542
596
|
|
543
597
|
const immediates = [ i[1], read_unsignedLEB128(i.slice(2)) ];
|
@@ -572,7 +626,6 @@ _time_out = _time.tv_nsec / 1000000. + _time.tv_sec * 1000.;`);
|
|
572
626
|
depth = 0;
|
573
627
|
|
574
628
|
const makeIncludes = includes => [...includes.keys()].map(x => `#include <${x}>\n`).join('');
|
575
|
-
|
576
629
|
out = platformSpecific(makeIncludes(winIncludes), makeIncludes(unixIncludes), false) + '\n' + makeIncludes(includes) + '\n' + alwaysPreface + [...prepend.values()].join('\n') + '\n\n' + out;
|
577
630
|
|
578
631
|
return out.trim();
|
package/compiler/assemble.js
CHANGED
@@ -65,6 +65,7 @@ export default (funcs, globals, tags, pages, data, flags) => {
|
|
65
65
|
importFuncs = [...imports.values()];
|
66
66
|
|
67
67
|
// fix call indexes for non-imports
|
68
|
+
// also fix call_indirect types
|
68
69
|
const delta = importedFuncs.length - importFuncs.length;
|
69
70
|
for (const f of funcs) {
|
70
71
|
f.originalIndex = f.index;
|
@@ -74,6 +75,16 @@ export default (funcs, globals, tags, pages, data, flags) => {
|
|
74
75
|
if ((inst[0] === Opcodes.call || inst[0] === Opcodes.return_call) && inst[1] >= importedFuncs.length) {
|
75
76
|
inst[1] -= delta;
|
76
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
|
+
}
|
77
88
|
}
|
78
89
|
}
|
79
90
|
}
|
@@ -101,10 +112,24 @@ export default (funcs, globals, tags, pages, data, flags) => {
|
|
101
112
|
encodeVector([ [
|
102
113
|
0x00,
|
103
114
|
Opcodes.i32_const, 0, Opcodes.end,
|
104
|
-
encodeVector(funcs.map(x => x.index))
|
115
|
+
...encodeVector(funcs.map(x => x.index))
|
105
116
|
] ])
|
106
117
|
);
|
107
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
|
+
|
108
133
|
// const t0 = performance.now();
|
109
134
|
|
110
135
|
// specially optimized assembly for globals as this version is much (>5x) faster than traditional createSection()
|
@@ -1,3 +1,5 @@
|
|
1
|
+
import type {} from './porffor.d.ts';
|
2
|
+
|
1
3
|
export const __Array_isArray = (x: unknown): boolean =>
|
2
4
|
// Porffor.wasm`local.get ${x+1}` == Porffor.TYPES.array;
|
3
5
|
Porffor.rawType(x) == Porffor.TYPES.array;
|
@@ -27,14 +29,16 @@ export const __Array_prototype_slice = (_this: any[], start: number, end: number
|
|
27
29
|
let outPtr: i32 = Porffor.wasm`local.get ${out}`;
|
28
30
|
let thisPtr: i32 = Porffor.wasm`local.get ${_this}`;
|
29
31
|
|
30
|
-
const thisPtrEnd: i32 = thisPtr + end *
|
32
|
+
const thisPtrEnd: i32 = thisPtr + end * 9;
|
31
33
|
|
32
|
-
thisPtr += start *
|
34
|
+
thisPtr += start * 9;
|
33
35
|
|
34
36
|
while (thisPtr < thisPtrEnd) {
|
35
37
|
Porffor.wasm.f64.store(outPtr, Porffor.wasm.f64.load(thisPtr, 0, 4), 0, 4);
|
36
|
-
thisPtr
|
37
|
-
|
38
|
+
Porffor.wasm.i32.store8(outPtr + 8, Porffor.wasm.i32.load8_u(thisPtr + 8, 0, 4), 0, 4);
|
39
|
+
|
40
|
+
thisPtr += 9;
|
41
|
+
outPtr += 9;
|
38
42
|
}
|
39
43
|
|
40
44
|
out.length = end - start;
|
@@ -142,4 +146,80 @@ export const __Array_prototype_toReversed = (_this: any[]) => {
|
|
142
146
|
|
143
147
|
export const __Array_prototype_valueOf = (_this: any[]) => {
|
144
148
|
return _this;
|
149
|
+
};
|
150
|
+
|
151
|
+
|
152
|
+
export const __Array_prototype_forEach = (_this: any[], callbackFn: any) => {
|
153
|
+
const len: i32 = _this.length;
|
154
|
+
let i: i32 = 0;
|
155
|
+
while (i < len) {
|
156
|
+
callbackFn(_this[i], i++, _this);
|
157
|
+
}
|
158
|
+
};
|
159
|
+
|
160
|
+
export const __Array_prototype_filter = (_this: any[], callbackFn: any) => {
|
161
|
+
const out: any[] = [];
|
162
|
+
|
163
|
+
const len: i32 = _this.length;
|
164
|
+
let i: i32 = 0;
|
165
|
+
while (i < len) {
|
166
|
+
const el: any = _this[i];
|
167
|
+
if (Boolean(callbackFn(el, i++, _this))) out.push(el);
|
168
|
+
}
|
169
|
+
|
170
|
+
return out;
|
171
|
+
};
|
172
|
+
|
173
|
+
export const __Array_prototype_map = (_this: any[], callbackFn: any) => {
|
174
|
+
const out: any[] = [];
|
175
|
+
|
176
|
+
const len: i32 = _this.length;
|
177
|
+
let i: i32 = 0;
|
178
|
+
while (i < len) {
|
179
|
+
out.push(callbackFn(_this[i], i++, _this));
|
180
|
+
}
|
181
|
+
|
182
|
+
return out;
|
183
|
+
};
|
184
|
+
|
185
|
+
export const __Array_prototype_find = (_this: any[], callbackFn: any) => {
|
186
|
+
const len: i32 = _this.length;
|
187
|
+
let i: i32 = 0;
|
188
|
+
while (i < len) {
|
189
|
+
const el: any = _this[i];
|
190
|
+
if (Boolean(callbackFn(el, i++, _this))) return el;
|
191
|
+
}
|
192
|
+
};
|
193
|
+
|
194
|
+
export const __Array_prototype_findLast = (_this: any[], callbackFn: any) => {
|
195
|
+
let i: i32 = _this.length;
|
196
|
+
while (i > 0) {
|
197
|
+
const el: any = _this[--i];
|
198
|
+
if (Boolean(callbackFn(el, i, _this))) return el;
|
199
|
+
}
|
200
|
+
};
|
201
|
+
|
202
|
+
export const __Array_prototype_findIndex = (_this: any[], callbackFn: any) => {
|
203
|
+
const len: i32 = _this.length;
|
204
|
+
let i: i32 = 0;
|
205
|
+
while (i < len) {
|
206
|
+
if (Boolean(callbackFn(_this[i], i++, _this))) return i;
|
207
|
+
}
|
208
|
+
};
|
209
|
+
|
210
|
+
export const __Array_prototype_findLastIndex = (_this: any[], callbackFn: any) => {
|
211
|
+
let i: i32 = _this.length;
|
212
|
+
while (i > 0) {
|
213
|
+
if (Boolean(callbackFn(_this[--i], i, _this))) return i;
|
214
|
+
}
|
215
|
+
};
|
216
|
+
|
217
|
+
export const __Array_prototype_every = (_this: any[], callbackFn: any) => {
|
218
|
+
const len: i32 = _this.length;
|
219
|
+
let i: i32 = 0;
|
220
|
+
while (i < len) {
|
221
|
+
if (!Boolean(callbackFn(_this[i], i++, _this))) return false;
|
222
|
+
}
|
223
|
+
|
224
|
+
return true;
|
145
225
|
};
|
@@ -1,3 +1,5 @@
|
|
1
|
+
import type {} from './porffor.d.ts';
|
2
|
+
|
1
3
|
// 20.3.3.2 Boolean.prototype.toString ()
|
2
4
|
// https://tc39.es/ecma262/#sec-boolean.prototype.tostring
|
3
5
|
export const __Boolean_prototype_toString = (_this: boolean) => {
|
@@ -15,4 +17,4 @@ export const __Boolean_prototype_toString = (_this: boolean) => {
|
|
15
17
|
export const __Boolean_prototype_valueOf = (_this: boolean) => {
|
16
18
|
// 1. Return ? ThisBooleanValue(this value).
|
17
19
|
return _this;
|
18
|
-
};
|
20
|
+
};
|