porffor 0.2.0-fde989a → 0.14.0-4057a18e9
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 +256 -0
- package/LICENSE +20 -20
- package/README.md +131 -86
- package/asur/README.md +2 -0
- package/asur/index.js +1262 -0
- package/byg/index.js +216 -0
- package/compiler/2c.js +2 -53
- package/compiler/{sections.js → assemble.js} +84 -21
- package/compiler/builtins/annexb_string.js +72 -0
- package/compiler/builtins/annexb_string.ts +18 -0
- package/compiler/builtins/array.ts +145 -0
- package/compiler/builtins/base64.ts +76 -0
- package/compiler/builtins/boolean.ts +18 -0
- package/compiler/builtins/crypto.ts +120 -0
- package/compiler/builtins/date.ts +2067 -0
- package/compiler/builtins/escape.ts +141 -0
- package/compiler/builtins/function.ts +5 -0
- package/compiler/builtins/int.ts +145 -0
- package/compiler/builtins/number.ts +529 -0
- package/compiler/builtins/object.ts +4 -0
- package/compiler/builtins/porffor.d.ts +60 -0
- package/compiler/builtins/set.ts +187 -0
- package/compiler/builtins/string.ts +1080 -0
- package/compiler/builtins.js +436 -283
- package/compiler/{codeGen.js → codegen.js} +1027 -482
- package/compiler/decompile.js +2 -3
- package/compiler/embedding.js +22 -22
- package/compiler/encoding.js +94 -10
- package/compiler/expression.js +1 -1
- package/compiler/generated_builtins.js +1625 -0
- package/compiler/index.js +25 -36
- package/compiler/log.js +6 -3
- package/compiler/opt.js +55 -41
- package/compiler/parse.js +38 -30
- package/compiler/precompile.js +120 -0
- package/compiler/prefs.js +27 -0
- package/compiler/prototype.js +31 -46
- package/compiler/types.js +38 -0
- package/compiler/wasmSpec.js +33 -8
- package/compiler/wrap.js +88 -70
- package/package.json +9 -5
- package/porf +2 -0
- package/rhemyn/compile.js +46 -27
- package/rhemyn/parse.js +322 -320
- package/rhemyn/test/parse.js +58 -58
- package/runner/compare.js +33 -34
- package/runner/debug.js +117 -0
- package/runner/index.js +78 -11
- package/runner/profiler.js +75 -0
- package/runner/repl.js +40 -13
- package/runner/sizes.js +37 -37
- package/runner/version.js +10 -8
- package/compiler/builtins/base64.js +0 -92
- package/filesize.cmd +0 -2
- package/runner/info.js +0 -89
- package/runner/profile.js +0 -46
- package/runner/results.json +0 -1
- package/runner/transform.js +0 -15
- package/tmp.c +0 -661
- package/util/enum.js +0 -20
package/rhemyn/test/parse.js
CHANGED
@@ -1,59 +1,59 @@
|
|
1
|
-
import util from 'node:util';
|
2
|
-
|
3
|
-
import parse from '../parse.js';
|
4
|
-
|
5
|
-
const tests = {
|
6
|
-
'a': {},
|
7
|
-
'a(b)': {},
|
8
|
-
'a(b(c))': {},
|
9
|
-
'ab': {},
|
10
|
-
'[ab]': {},
|
11
|
-
'[a-z]': {},
|
12
|
-
'a*': {},
|
13
|
-
'a+': {},
|
14
|
-
'a?': {},
|
15
|
-
'a(b)+': {},
|
16
|
-
'[^a]': {},
|
17
|
-
'[a^]': {},
|
18
|
-
'[^ab]': {},
|
19
|
-
'.': {},
|
20
|
-
|
21
|
-
// not range
|
22
|
-
'[-]': {},
|
23
|
-
'[0-]': {},
|
24
|
-
'[-0]': {},
|
25
|
-
'[\\s-\\S]': {},
|
26
|
-
'[\\s-.]': {},
|
27
|
-
|
28
|
-
'[\\S]': {},
|
29
|
-
|
30
|
-
'\\c': {},
|
31
|
-
'\\c0': {},
|
32
|
-
'\\cJ': {},
|
33
|
-
|
34
|
-
'\\x': {},
|
35
|
-
'\\x0': {},
|
36
|
-
'\\x0g': {},
|
37
|
-
'\\x0a': {},
|
38
|
-
|
39
|
-
'\\u': {},
|
40
|
-
'\\u0': {},
|
41
|
-
'\\u000': {},
|
42
|
-
'\\u000g': {},
|
43
|
-
'\\u000a': {},
|
44
|
-
|
45
|
-
/*
|
46
|
-
// email regexes
|
47
|
-
'^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$': {},
|
48
|
-
|
49
|
-
// input type=email from HTML spec
|
50
|
-
// https://html.spec.whatwg.org/multipage/input.html#email-state-(type=email)
|
51
|
-
// simpler form
|
52
|
-
'^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\\.[a-zA-Z0-9-]+)*$': {},
|
53
|
-
// full/complex form
|
54
|
-
'^[a-zA-Z0-9.!#$%&\'*+\\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$': {}*/
|
55
|
-
};
|
56
|
-
|
57
|
-
for (const str in tests) {
|
58
|
-
console.log(str, util.inspect(parse(str), false, null, true));
|
1
|
+
import util from 'node:util';
|
2
|
+
|
3
|
+
import parse from '../parse.js';
|
4
|
+
|
5
|
+
const tests = {
|
6
|
+
'a': {},
|
7
|
+
'a(b)': {},
|
8
|
+
'a(b(c))': {},
|
9
|
+
'ab': {},
|
10
|
+
'[ab]': {},
|
11
|
+
'[a-z]': {},
|
12
|
+
'a*': {},
|
13
|
+
'a+': {},
|
14
|
+
'a?': {},
|
15
|
+
'a(b)+': {},
|
16
|
+
'[^a]': {},
|
17
|
+
'[a^]': {},
|
18
|
+
'[^ab]': {},
|
19
|
+
'.': {},
|
20
|
+
|
21
|
+
// not range
|
22
|
+
'[-]': {},
|
23
|
+
'[0-]': {},
|
24
|
+
'[-0]': {},
|
25
|
+
'[\\s-\\S]': {},
|
26
|
+
'[\\s-.]': {},
|
27
|
+
|
28
|
+
'[\\S]': {},
|
29
|
+
|
30
|
+
'\\c': {},
|
31
|
+
'\\c0': {},
|
32
|
+
'\\cJ': {},
|
33
|
+
|
34
|
+
'\\x': {},
|
35
|
+
'\\x0': {},
|
36
|
+
'\\x0g': {},
|
37
|
+
'\\x0a': {},
|
38
|
+
|
39
|
+
'\\u': {},
|
40
|
+
'\\u0': {},
|
41
|
+
'\\u000': {},
|
42
|
+
'\\u000g': {},
|
43
|
+
'\\u000a': {},
|
44
|
+
|
45
|
+
/*
|
46
|
+
// email regexes
|
47
|
+
'^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$': {},
|
48
|
+
|
49
|
+
// input type=email from HTML spec
|
50
|
+
// https://html.spec.whatwg.org/multipage/input.html#email-state-(type=email)
|
51
|
+
// simpler form
|
52
|
+
'^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\\.[a-zA-Z0-9-]+)*$': {},
|
53
|
+
// full/complex form
|
54
|
+
'^[a-zA-Z0-9.!#$%&\'*+\\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$': {}*/
|
55
|
+
};
|
56
|
+
|
57
|
+
for (const str in tests) {
|
58
|
+
console.log(str, util.inspect(parse(str), false, null, true));
|
59
59
|
}
|
package/runner/compare.js
CHANGED
@@ -1,35 +1,34 @@
|
|
1
|
-
import { execSync } from 'node:child_process';
|
2
|
-
import
|
3
|
-
|
4
|
-
|
5
|
-
const
|
6
|
-
|
7
|
-
|
8
|
-
const
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
console.log(`now: ${wasmNow} bytes`);
|
1
|
+
import { execSync } from 'node:child_process';
|
2
|
+
import fs from 'node:fs';
|
3
|
+
|
4
|
+
const [ commitsAgo, file ] = process.argv.slice(2);
|
5
|
+
const source = fs.readFileSync(file, 'utf8');
|
6
|
+
|
7
|
+
const compileNow = (await import('../compiler/index.js')).default;
|
8
|
+
const wasmNow = compileNow(source).byteLength;
|
9
|
+
|
10
|
+
execSync(`git checkout HEAD~${commitsAgo}`);
|
11
|
+
|
12
|
+
let failed = false;
|
13
|
+
|
14
|
+
fs.writeFileSync('tmp.js', source);
|
15
|
+
try {
|
16
|
+
execSync(`node runner/index.js tmp.js`);
|
17
|
+
} catch {
|
18
|
+
failed = true;
|
19
|
+
} finally {
|
20
|
+
fs.rmSync('tmp.js');
|
21
|
+
execSync(`git checkout main`);
|
22
|
+
}
|
23
|
+
|
24
|
+
console.log();
|
25
|
+
|
26
|
+
if (failed) {
|
27
|
+
console.log(`!! failed to compile then`);
|
28
|
+
process.exit();
|
29
|
+
}
|
30
|
+
|
31
|
+
const wasmThen = Buffer.byteLength(fs.readFileSync('out.wasm'));
|
32
|
+
|
33
|
+
console.log(`now: ${wasmNow} bytes`);
|
35
34
|
console.log(`${commitsAgo} commit${commitsAgo > 1 ? 's' : ''} ago: ${wasmThen} bytes`);
|
package/runner/debug.js
ADDED
@@ -0,0 +1,117 @@
|
|
1
|
+
#!/usr/bin/env node
|
2
|
+
|
3
|
+
import compile from '../compiler/wrap.js';
|
4
|
+
import Byg from '../byg/index.js';
|
5
|
+
import fs from 'node:fs';
|
6
|
+
|
7
|
+
const file = process.argv.slice(2).find(x => x[0] !== '-');
|
8
|
+
let source = fs.readFileSync(file, 'utf8');
|
9
|
+
|
10
|
+
const originalLines = source.split('\n');
|
11
|
+
|
12
|
+
let funcs = {}, funcId = 0;
|
13
|
+
source = source.replace(/^\s*(function|const)\s*([a-zA-Z0-9]+)(\s*=\s*)?\([^)]*\)\s*(=>)?\s*\{$/gm, (x, _, n) => {
|
14
|
+
const id = funcId++;
|
15
|
+
funcs[funcId] = n;
|
16
|
+
return `${x}profile2(Porffor.wasm.i32.const(${id}))`;
|
17
|
+
});
|
18
|
+
|
19
|
+
const lines = source.split('\n');
|
20
|
+
for (let i = 0; i < lines.length; i++) {
|
21
|
+
if (lines[i].trim().replace('}', '') !== '') lines[i] = `profile1(Porffor.wasm.i32.const(${i}));` + lines[i];
|
22
|
+
}
|
23
|
+
source = lines.join('\n');
|
24
|
+
|
25
|
+
const breakpoints = new Array(lines.length);
|
26
|
+
|
27
|
+
let paused = true;
|
28
|
+
const byg = Byg({
|
29
|
+
lines: originalLines,
|
30
|
+
pause: () => { paused = true; },
|
31
|
+
breakpoint: (line, breakpoint) => {
|
32
|
+
breakpoints[line] = breakpoint;
|
33
|
+
}
|
34
|
+
});
|
35
|
+
|
36
|
+
let stepIn = false, stepOut = false;
|
37
|
+
const callStack = [];
|
38
|
+
|
39
|
+
let _paused;
|
40
|
+
let callStarts = [];
|
41
|
+
let lastLine;
|
42
|
+
|
43
|
+
let output = '';
|
44
|
+
|
45
|
+
try {
|
46
|
+
const { exports } = await compile(source, process.argv.includes('--module') ? [ 'module' ] : [], {
|
47
|
+
y: n => {
|
48
|
+
if (callStarts[callStarts.length - 1] === n - 1) {
|
49
|
+
// end of call
|
50
|
+
|
51
|
+
callStarts.pop();
|
52
|
+
callStack.pop();
|
53
|
+
|
54
|
+
paused = _paused;
|
55
|
+
}
|
56
|
+
|
57
|
+
lastLine = n;
|
58
|
+
|
59
|
+
if (breakpoints[n]) paused = true;
|
60
|
+
|
61
|
+
if (paused) {
|
62
|
+
stepIn = false; stepOut = false;
|
63
|
+
|
64
|
+
switch (byg(
|
65
|
+
paused,
|
66
|
+
n,
|
67
|
+
`\x1b[1mporffor debugger\x1b[22m: ${file}@${n + 1} ${callStack.join('->')}`,
|
68
|
+
[
|
69
|
+
{
|
70
|
+
x: termWidth - 1 - 40 - 6,
|
71
|
+
y: () => 4,
|
72
|
+
width: 40,
|
73
|
+
height: 20,
|
74
|
+
title: 'console',
|
75
|
+
content: output.split('\n')
|
76
|
+
}
|
77
|
+
]
|
78
|
+
)) {
|
79
|
+
case 'resume': {
|
80
|
+
paused = false;
|
81
|
+
break;
|
82
|
+
}
|
83
|
+
|
84
|
+
case 'stepOver': {
|
85
|
+
break;
|
86
|
+
}
|
87
|
+
|
88
|
+
case 'stepIn': {
|
89
|
+
stepIn = true;
|
90
|
+
// paused = false;
|
91
|
+
break;
|
92
|
+
}
|
93
|
+
|
94
|
+
case 'stepOut': {
|
95
|
+
stepOut = true;
|
96
|
+
paused = false;
|
97
|
+
break;
|
98
|
+
}
|
99
|
+
}
|
100
|
+
}
|
101
|
+
},
|
102
|
+
z: n => {
|
103
|
+
// start of call
|
104
|
+
callStack.push(funcs[n]);
|
105
|
+
|
106
|
+
callStarts.push(lastLine);
|
107
|
+
|
108
|
+
_paused = paused;
|
109
|
+
if (!stepIn) paused = false;
|
110
|
+
else paused = true;
|
111
|
+
}
|
112
|
+
}, s => output += s);
|
113
|
+
|
114
|
+
exports.main();
|
115
|
+
} catch (e) {
|
116
|
+
console.error(e);
|
117
|
+
}
|
package/runner/index.js
CHANGED
@@ -3,7 +3,9 @@
|
|
3
3
|
import compile from '../compiler/wrap.js';
|
4
4
|
import fs from 'node:fs';
|
5
5
|
|
6
|
-
|
6
|
+
const start = performance.now();
|
7
|
+
|
8
|
+
if (process.argv.includes('--compile-hints')) {
|
7
9
|
const v8 = await import('node:v8');
|
8
10
|
v8.setFlagsFromString(`--experimental-wasm-compilation-hints`);
|
9
11
|
|
@@ -15,10 +17,67 @@ if (process.argv.includes('-compile-hints')) {
|
|
15
17
|
// --experimental-wasm-return-call (on by default)
|
16
18
|
}
|
17
19
|
|
20
|
+
if (process.argv.includes('--help')) {
|
21
|
+
// description + version
|
22
|
+
console.log(`\x1B[1m\x1B[35mPorffor\x1B[0m is a JavaScript engine/runtime/compiler. \x1B[90m(${(await import('./version.js')).default})\x1B[0m`);
|
23
|
+
|
24
|
+
// basic usage
|
25
|
+
console.log(`Usage: \x1B[1mporf [command] path/to/script.js [...prefs] [...args]\x1B[0m`);
|
26
|
+
|
27
|
+
// commands
|
28
|
+
console.log(`\n\u001b[4mCommands\x1B[0m`);
|
29
|
+
for (const [ cmd, [ color, desc ] ] of Object.entries({
|
30
|
+
run: [ 34, 'Run a JS file' ],
|
31
|
+
wasm: [ 34, 'Compile a JS file to a Wasm binary\n' ],
|
32
|
+
c: [ 31, 'Compile a JS file to C source code' ],
|
33
|
+
native: [ 31, 'Compile a JS file to a native binary\n' ],
|
34
|
+
profile: [ 33, 'Profile a JS file' ],
|
35
|
+
debug: [ 33, 'Debug a JS file' ],
|
36
|
+
'debug-wasm': [ 33, 'Debug the compiled Wasm of a JS file' ]
|
37
|
+
})) {
|
38
|
+
console.log(` \x1B[1m\x1B[${color}m${cmd}\x1B[0m${' '.repeat(20 - cmd.length - (desc.startsWith('🧪') ? 3 : 0))}${desc}`);
|
39
|
+
}
|
40
|
+
|
41
|
+
// console.log();
|
42
|
+
|
43
|
+
// // options
|
44
|
+
// console.log(`\n\u001b[4mCommands\x1B[0m`);
|
45
|
+
// for (const [ cmd, [ color, desc ] ] of Object.entries({
|
46
|
+
// run: [ 34, 'Run a JS file' ],
|
47
|
+
// wasm: [ 34, 'Compile a JS file to a Wasm binary\n' ],
|
48
|
+
// c: [ 31, 'Compile a JS file to C source code' ],
|
49
|
+
// native: [ 31, 'Compile a JS file to a native binary\n' ],
|
50
|
+
// profile: [ 33, 'Profile a JS file' ],
|
51
|
+
// debug: [ 33, 'Debug a JS file' ],
|
52
|
+
// 'debug-wasm': [ 33, 'Debug the compiled Wasm of a JS file' ]
|
53
|
+
// })) {
|
54
|
+
// console.log(` \x1B[1m\x1B[${color}m${cmd}\x1B[0m${' '.repeat(20 - cmd.length - (desc.startsWith('🧪') ? 3 : 0))}${desc}`);
|
55
|
+
// }
|
56
|
+
|
57
|
+
console.log();
|
58
|
+
process.exit(0);
|
59
|
+
}
|
60
|
+
|
18
61
|
let file = process.argv.slice(2).find(x => x[0] !== '-');
|
19
|
-
if (['run', 'wasm', 'native', 'c'].includes(file)) {
|
62
|
+
if (['run', 'wasm', 'native', 'c', 'profile', 'debug', 'debug-wasm'].includes(file)) {
|
63
|
+
if (file === 'profile') {
|
64
|
+
process.argv.splice(process.argv.indexOf(file), 1);
|
65
|
+
await import('./profiler.js');
|
66
|
+
await new Promise(() => {}); // do nothing for the rest of this file
|
67
|
+
}
|
68
|
+
|
20
69
|
if (['wasm', 'native', 'c'].includes(file)) {
|
21
|
-
process.argv.push(
|
70
|
+
process.argv.push(`--target=${file}`);
|
71
|
+
}
|
72
|
+
|
73
|
+
if (file === 'debug-wasm') {
|
74
|
+
process.argv.push('--asur', '--wasm-debug');
|
75
|
+
}
|
76
|
+
|
77
|
+
if (file === 'debug') {
|
78
|
+
process.argv.splice(process.argv.indexOf(file), 1);
|
79
|
+
await import('./debug.js');
|
80
|
+
await new Promise(() => {}); // do nothing for the rest of this file
|
22
81
|
}
|
23
82
|
|
24
83
|
file = process.argv.slice(process.argv.indexOf(file) + 1).find(x => x[0] !== '-');
|
@@ -38,9 +97,7 @@ if (!file) {
|
|
38
97
|
|
39
98
|
// run repl if no file given
|
40
99
|
await import('./repl.js');
|
41
|
-
|
42
|
-
// do nothing for the rest of this file
|
43
|
-
await new Promise(() => {});
|
100
|
+
await new Promise(() => {}); // do nothing for the rest of this file
|
44
101
|
}
|
45
102
|
|
46
103
|
const source = fs.readFileSync(file, 'utf8');
|
@@ -58,11 +115,21 @@ const print = str => {
|
|
58
115
|
};
|
59
116
|
|
60
117
|
try {
|
61
|
-
|
118
|
+
if (process.argv.includes('-b')) {
|
119
|
+
const { wasm, exports } = await compile(source, process.argv.includes('--module') ? [ 'module' ] : [], {}, print);
|
62
120
|
|
63
|
-
|
64
|
-
|
121
|
+
if (!process.argv.includes('--no-run')) exports.main();
|
122
|
+
|
123
|
+
console.log(`\n\nwasm size: ${wasm.byteLength} bytes`);
|
124
|
+
} else {
|
125
|
+
const { exports } = await compile(source, process.argv.includes('--module') ? [ 'module' ] : [], {}, print);
|
126
|
+
|
127
|
+
if (!process.argv.includes('-no-run')) exports.main();
|
128
|
+
}
|
129
|
+
// if (cache) process.stdout.write(cache);
|
65
130
|
} catch (e) {
|
66
|
-
if (cache) process.stdout.write(cache);
|
131
|
+
// if (cache) process.stdout.write(cache);
|
67
132
|
console.error(process.argv.includes('-i') ? e : `${e.constructor.name}: ${e.message}`);
|
68
|
-
}
|
133
|
+
}
|
134
|
+
|
135
|
+
if (process.argv.includes('-t')) console.log(`${process.argv.includes('-b') ? '' : '\n\n'}total time: ${(performance.now() - start).toFixed(2)}ms`);
|
@@ -0,0 +1,75 @@
|
|
1
|
+
#!/usr/bin/env node
|
2
|
+
|
3
|
+
import compile from '../compiler/wrap.js';
|
4
|
+
import fs from 'node:fs';
|
5
|
+
|
6
|
+
const file = process.argv.slice(2).find(x => x[0] !== '-');
|
7
|
+
let source = fs.readFileSync(file, 'utf8');
|
8
|
+
|
9
|
+
let profileId = 0;
|
10
|
+
source = source.replace(/^[^\n}]*;$/mg, _ => `profile1(Porffor.wasm.i32.const(${profileId}));${_}profile2(Porffor.wasm.i32.const(${profileId++}));`)
|
11
|
+
|
12
|
+
let tmp = new Array(profileId).fill(0);
|
13
|
+
let times = new Array(profileId).fill(0);
|
14
|
+
let samples = 0;
|
15
|
+
|
16
|
+
const percents = process.argv.includes('-%');
|
17
|
+
|
18
|
+
const spinner = ['-', '\\', '|', '/'];
|
19
|
+
let spin = 0;
|
20
|
+
let last = 0;
|
21
|
+
|
22
|
+
try {
|
23
|
+
const { exports } = await compile(source, process.argv.includes('--module') ? [ 'module' ] : [], {
|
24
|
+
y: n => {
|
25
|
+
tmp[n] = performance.now();
|
26
|
+
},
|
27
|
+
z: n => {
|
28
|
+
const t = performance.now();
|
29
|
+
times[n] += t - tmp[n];
|
30
|
+
|
31
|
+
samples++;
|
32
|
+
if (t > last) {
|
33
|
+
process.stdout.write(`\r${spinner[spin++ % 4]} running: collected ${samples} samples...`);
|
34
|
+
last = t + 100;
|
35
|
+
}
|
36
|
+
}
|
37
|
+
});
|
38
|
+
|
39
|
+
const start = performance.now();
|
40
|
+
|
41
|
+
exports.main();
|
42
|
+
|
43
|
+
const total = performance.now() - start;
|
44
|
+
|
45
|
+
console.log(`\ntotal: ${total}ms\nsamples: ${samples}\n\n\n` + source.split('\n').map(x => {
|
46
|
+
let time = 0;
|
47
|
+
if (x.startsWith('profile')) {
|
48
|
+
const id = parseInt(x.slice(32, x.indexOf(')')));
|
49
|
+
time = times[id]
|
50
|
+
}
|
51
|
+
|
52
|
+
let color = [ 0, 0, 0 ];
|
53
|
+
if (time) {
|
54
|
+
let relativeTime = time / total;
|
55
|
+
if (percents) time = relativeTime;
|
56
|
+
|
57
|
+
relativeTime = Math.sqrt(relativeTime);
|
58
|
+
color = [ (relativeTime * 250) | 0, (Math.sin(relativeTime * Math.PI) * 50) | 0, 0 ];
|
59
|
+
}
|
60
|
+
|
61
|
+
const ansiColor = `2;${color[0]};${color[1]};${color[2]}m`;
|
62
|
+
|
63
|
+
const line = x.replace(/profile[0-9]\(Porffor.wasm.i32.const\([0-9]+\)\);/g, '');
|
64
|
+
|
65
|
+
if (percents) return `\x1b[48;${ansiColor}\x1b[97m${time ? ((time * 100).toFixed(0).padStart(4, ' ') + '%') : ' '}\x1b[0m\x1b[38;${ansiColor}▌\x1b[0m ${line}`;
|
66
|
+
|
67
|
+
let digits = 2;
|
68
|
+
if (time >= 100) digits = 1;
|
69
|
+
if (time >= 1000) digits = 0;
|
70
|
+
|
71
|
+
return `\x1b[48;${ansiColor}\x1b[97m${time ? time.toFixed(digits).padStart(6, ' ') : ' '}\x1b[0m\x1b[38;${ansiColor}▌\x1b[0m ${line}`;
|
72
|
+
}).join('\n'));
|
73
|
+
} catch (e) {
|
74
|
+
console.error(e);
|
75
|
+
}
|
package/runner/repl.js
CHANGED
@@ -1,17 +1,34 @@
|
|
1
1
|
import compile from '../compiler/wrap.js';
|
2
|
-
import
|
2
|
+
import version from './version.js';
|
3
3
|
|
4
|
-
|
4
|
+
let repl;
|
5
|
+
try {
|
6
|
+
// try importing node:repl
|
7
|
+
repl = await import('node:repl');
|
5
8
|
|
6
|
-
//
|
9
|
+
// check it is not just a mock with REPLServer prototype
|
10
|
+
if (repl.REPLServer.prototype.defineCommand == null)
|
11
|
+
throw 'mock node:repl detected';
|
12
|
+
} catch {
|
13
|
+
// it failed, import the polyfill
|
14
|
+
repl = (await import('node-repl-polyfill')).default;
|
15
|
+
}
|
7
16
|
|
8
17
|
globalThis.valtype = 'f64';
|
9
18
|
|
10
|
-
const valtypeOpt = process.argv.find(x => x.startsWith('
|
19
|
+
const valtypeOpt = process.argv.find(x => x.startsWith('--valtype='));
|
11
20
|
if (valtypeOpt) valtype = valtypeOpt.split('=')[1];
|
12
21
|
|
13
|
-
|
14
|
-
|
22
|
+
let host = globalThis?.navigator?.userAgent;
|
23
|
+
if (typeof process !== 'undefined' && process.argv0 === 'node') host = 'Node/' + process.versions.node;
|
24
|
+
host ??= 'Unknown';
|
25
|
+
|
26
|
+
if (host.startsWith('Node')) host = '\x1B[92m' + host;
|
27
|
+
if (host.startsWith('Deno')) host = '\x1B[97m' + host;
|
28
|
+
if (host.startsWith('Bun')) host = '\x1B[93m' + host;
|
29
|
+
|
30
|
+
console.log(`Welcome to \x1B[1m\x1B[35mPorffor\x1B[0m \x1B[90m(${version})\x1B[0m running on \x1B[1m${host.replace('/', ' \x1B[0m\x1B[90m(')})\x1B[0m`);
|
31
|
+
console.log(`\x1B[90musing opt ${process.argv.find(x => x.startsWith('-O')) ?? '-O1'}, parser ${parser}, valtype ${valtype}\x1B[0m`);
|
15
32
|
console.log();
|
16
33
|
|
17
34
|
let lastMemory, lastPages;
|
@@ -21,16 +38,27 @@ const memoryToString = mem => {
|
|
21
38
|
const pages = lastPages.length;
|
22
39
|
const wasmPages = mem.buffer.byteLength / PageSize;
|
23
40
|
|
24
|
-
out += `\x1B[1mallocated ${mem.buffer.byteLength / 1024}KB\x1B[0m for ${pages} thing${pages === 1 ? '' : 's'} using ${wasmPages} Wasm page${wasmPages === 1 ? '' : 's'}\n`;
|
41
|
+
out += `\x1B[1mallocated ${mem.buffer.byteLength / 1024}KB\x1B[0m for ${pages} thing${pages === 1 ? '' : 's'} using ${wasmPages} Wasm page${wasmPages === 1 ? '' : 's'}\n\n`;
|
25
42
|
|
26
43
|
const buf = new Uint8Array(mem.buffer);
|
27
44
|
|
45
|
+
let longestType = 0, longestName = 0;
|
46
|
+
for (const x of lastPages) {
|
47
|
+
const [ type, name ] = x.split(': ');
|
48
|
+
if (type.length > longestType) longestType = type.length;
|
49
|
+
if (name.length > longestName) longestName = name.length;
|
50
|
+
}
|
51
|
+
|
52
|
+
out += `\x1B[0m\x1B[1m name${' '.repeat(longestName - 4)} \x1B[0m\x1B[90m│\x1B[0m\x1B[1m type${' '.repeat(longestType - 4)} \x1B[0m\x1B[90m│\x1B[0m\x1B[1m memory\x1B[0m\n`; // ─
|
28
53
|
for (let i = 0; i < pages; i++) {
|
29
|
-
|
54
|
+
const [ type, name ] = lastPages[i].split(': ');
|
55
|
+
// out += `\x1B[36m${lastPages[i].replace(':', '\x1B[90m:\x1B[34m')}\x1B[90m${' '.repeat(longestName - lastPages[i].length)} | \x1B[0m`;
|
56
|
+
out += ` \x1B[34m${name}${' '.repeat(longestName - name.length)} \x1B[90m│\x1B[0m \x1B[36m${type}${' '.repeat(longestType - type.length)} \x1B[90m│\x1B[0m `;
|
30
57
|
|
31
|
-
for (let j = 0; j <
|
58
|
+
for (let j = 0; j < 40; j++) {
|
32
59
|
const val = buf[i * pageSize + j];
|
33
|
-
if (val === 0) out += '\x1B[2m';
|
60
|
+
// if (val === 0) out += '\x1B[2m';
|
61
|
+
if (val === 0) out += '\x1B[90m';
|
34
62
|
out += val.toString(16).padStart(2, '0');
|
35
63
|
if (val === 0) out += '\x1B[0m';
|
36
64
|
out += ' ';
|
@@ -48,11 +76,10 @@ const run = async (source, _context, _filename, callback, run = true) => {
|
|
48
76
|
let toRun = (prev ? (prev + `;\nprint(-0x1337);\n`) : '') + source.trim();
|
49
77
|
|
50
78
|
let shouldPrint = !prev;
|
51
|
-
const { exports,
|
79
|
+
const { exports, pages } = await compile(toRun, [], {}, str => {
|
52
80
|
if (shouldPrint) process.stdout.write(str);
|
53
81
|
if (str === '-4919') shouldPrint = true;
|
54
82
|
});
|
55
|
-
// fs.writeFileSync('out.wasm', Buffer.from(wasm));
|
56
83
|
|
57
84
|
if (run && exports.$) {
|
58
85
|
lastMemory = exports.$;
|
@@ -83,7 +110,7 @@ replServer.defineCommand('asm', {
|
|
83
110
|
this.clearBufferedCommand();
|
84
111
|
|
85
112
|
try {
|
86
|
-
process.argv.push('
|
113
|
+
process.argv.push('--opt-funcs');
|
87
114
|
await run('', null, null, () => {}, false);
|
88
115
|
process.argv.pop();
|
89
116
|
} catch { }
|
package/runner/sizes.js
CHANGED
@@ -1,38 +1,38 @@
|
|
1
|
-
import
|
2
|
-
import
|
3
|
-
|
4
|
-
// deno compat
|
5
|
-
const textEncoder = new TextEncoder();
|
6
|
-
if (typeof process === 'undefined') globalThis.process = { argv: ['', '', ...Deno.args], stdout: { write: str => Deno.writeAllSync(Deno.stdout, textEncoder.encode(str)) } };
|
7
|
-
|
8
|
-
let csv = `file,porffor i32 -O0,porffor i32 -O1,porffor i32 -O2,porffor i32 -O3,porffor i64 -O0,porffor i64 -O1,porffor i64 -O2,porffor i64 -O3,porffor f64 -O0, porffor f64 -O1, porffor f64 -O2, porffor f64 -O3\n`;
|
9
|
-
const perform = async (file, args) => {
|
10
|
-
process.argv = process.argv.slice(0, 2).concat(args);
|
11
|
-
const source = fs.readFileSync(file, 'utf8');
|
12
|
-
|
13
|
-
const { wasm } = compile(source, []);
|
14
|
-
const size = wasm.byteLength;
|
15
|
-
|
16
|
-
const label = `${file} ${args.join(' ')}`;
|
17
|
-
csv += `${size},`;
|
18
|
-
console.log(label, ' '.repeat(40 - label.length), `${size}b`);
|
19
|
-
};
|
20
|
-
|
21
|
-
const argsValtypes = [ '
|
22
|
-
const argsOptlevels = [ '-O0', '-O1', '-O2', '-O3' ];
|
23
|
-
|
24
|
-
for (const file of [ 'bench/prime_basic.js', 'bench/fib_iter.js', 'test/math_1.js', 'test/math_3.js', 'test/while_1.js', 'test/for_2.js', 'test/unary_3.js', 'test/updateexp_1.js', 'test/eq_3.js', 'test/empty.js' ]) {
|
25
|
-
const niceFile = file.split('/')[1].slice(0, -3);
|
26
|
-
csv += `${niceFile},`;
|
27
|
-
|
28
|
-
for (const x of argsValtypes) {
|
29
|
-
for (const y of argsOptlevels) {
|
30
|
-
await perform(file, [ x, y ]);
|
31
|
-
}
|
32
|
-
}
|
33
|
-
|
34
|
-
csv = csv.slice(0, -1) + '\n';
|
35
|
-
}
|
36
|
-
|
37
|
-
fs.writeFileSync('sizes.csv', csv);
|
1
|
+
import compile from '../compiler/index.js';
|
2
|
+
import fs from 'node:fs';
|
3
|
+
|
4
|
+
// deno compat
|
5
|
+
const textEncoder = new TextEncoder();
|
6
|
+
if (typeof process === 'undefined') globalThis.process = { argv: ['', '', ...Deno.args], stdout: { write: str => Deno.writeAllSync(Deno.stdout, textEncoder.encode(str)) } };
|
7
|
+
|
8
|
+
let csv = `file,porffor i32 -O0,porffor i32 -O1,porffor i32 -O2,porffor i32 -O3,porffor i64 -O0,porffor i64 -O1,porffor i64 -O2,porffor i64 -O3,porffor f64 -O0, porffor f64 -O1, porffor f64 -O2, porffor f64 -O3\n`;
|
9
|
+
const perform = async (file, args) => {
|
10
|
+
process.argv = process.argv.slice(0, 2).concat(args);
|
11
|
+
const source = fs.readFileSync(file, 'utf8');
|
12
|
+
|
13
|
+
const { wasm } = compile(source, []);
|
14
|
+
const size = wasm.byteLength;
|
15
|
+
|
16
|
+
const label = `${file} ${args.join(' ')}`;
|
17
|
+
csv += `${size},`;
|
18
|
+
console.log(label, ' '.repeat(40 - label.length), `${size}b`);
|
19
|
+
};
|
20
|
+
|
21
|
+
const argsValtypes = [ '--valtype=i32', '--valtype=f64' ];
|
22
|
+
const argsOptlevels = [ '-O0', '-O1', '-O2', '-O3' ];
|
23
|
+
|
24
|
+
for (const file of [ 'bench/prime_basic.js', 'bench/fib_iter.js', 'test/math_1.js', 'test/math_3.js', 'test/while_1.js', 'test/for_2.js', 'test/unary_3.js', 'test/updateexp_1.js', 'test/eq_3.js', 'test/empty.js' ]) {
|
25
|
+
const niceFile = file.split('/')[1].slice(0, -3);
|
26
|
+
csv += `${niceFile},`;
|
27
|
+
|
28
|
+
for (const x of argsValtypes) {
|
29
|
+
for (const y of argsOptlevels) {
|
30
|
+
await perform(file, [ x, y ]);
|
31
|
+
}
|
32
|
+
}
|
33
|
+
|
34
|
+
csv = csv.slice(0, -1) + '\n';
|
35
|
+
}
|
36
|
+
|
37
|
+
fs.writeFileSync('sizes.csv', csv);
|
38
38
|
console.log(csv);
|