porffor 0.2.0-6aff0fa → 0.2.0-767de65
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/.vscode/launch.json +18 -0
- package/LICENSE +20 -20
- package/README.md +54 -41
- package/asur/index.js +624 -340
- package/byg/index.js +237 -0
- package/compiler/2c.js +1 -1
- package/compiler/{sections.js → assemble.js} +57 -10
- package/compiler/builtins/annexb_string.js +72 -0
- package/compiler/builtins/annexb_string.ts +19 -0
- package/compiler/builtins/array.ts +145 -0
- package/compiler/builtins/base64.ts +3 -5
- package/compiler/builtins/crypto.ts +120 -0
- package/compiler/builtins/escape.ts +141 -0
- package/compiler/builtins/int.ts +147 -0
- package/compiler/builtins/number.ts +527 -0
- package/compiler/builtins/porffor.d.ts +27 -8
- package/compiler/builtins/string.ts +1055 -0
- package/compiler/builtins/tostring.ts +45 -0
- package/compiler/builtins.js +405 -108
- package/compiler/{codeGen.js → codegen.js} +654 -246
- package/compiler/embedding.js +22 -22
- package/compiler/encoding.js +108 -10
- package/compiler/generated_builtins.js +682 -2
- package/compiler/index.js +16 -14
- package/compiler/log.js +2 -2
- package/compiler/opt.js +23 -22
- package/compiler/parse.js +31 -25
- package/compiler/precompile.js +19 -25
- package/compiler/prefs.js +2 -2
- package/compiler/prototype.js +2 -18
- package/compiler/types.js +37 -0
- package/compiler/wasmSpec.js +13 -1
- package/compiler/wrap.js +36 -42
- package/empty.js +0 -0
- package/hello.js +2 -0
- package/package.json +9 -5
- package/porf +2 -0
- package/rhemyn/compile.js +3 -2
- package/rhemyn/parse.js +323 -320
- package/rhemyn/test/parse.js +58 -58
- package/runner/compare.js +34 -34
- package/runner/debug.js +122 -0
- package/runner/index.js +26 -10
- package/runner/profiler.js +45 -26
- package/runner/repl.js +40 -7
- package/runner/sizes.js +37 -37
- package/test262_changes_from_1afe9b87d2_to_04-09.md +270 -0
- package/runner/info.js +0 -89
- package/runner/transform.js +0 -15
- package/util/enum.js +0 -20
package/compiler/index.js
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
import { underline, bold, log } from './log.js';
|
2
2
|
import parse from './parse.js';
|
3
|
-
import
|
3
|
+
import codegen from './codegen.js';
|
4
4
|
import opt from './opt.js';
|
5
|
-
import
|
5
|
+
import assemble from './assemble.js';
|
6
6
|
import decompile from './decompile.js';
|
7
7
|
import toc from './2c.js';
|
8
8
|
import Prefs from './prefs.js';
|
@@ -26,29 +26,29 @@ const logFuncs = (funcs, globals, exceptions) => {
|
|
26
26
|
console.log();
|
27
27
|
};
|
28
28
|
|
29
|
-
const
|
29
|
+
const fs = (typeof process?.version !== 'undefined' ? (await import('node:fs')) : undefined);
|
30
30
|
const execSync = (typeof process?.version !== 'undefined' ? (await import('node:child_process')).execSync : undefined);
|
31
31
|
|
32
32
|
export default (code, flags) => {
|
33
33
|
const t0 = performance.now();
|
34
34
|
const program = parse(code, flags);
|
35
|
-
if (
|
35
|
+
if (Prefs.profileCompiler) console.log(`1. parsed in ${(performance.now() - t0).toFixed(2)}ms`);
|
36
36
|
|
37
37
|
const t1 = performance.now();
|
38
|
-
const { funcs, globals, tags, exceptions, pages, data } =
|
39
|
-
if (
|
38
|
+
const { funcs, globals, tags, exceptions, pages, data } = codegen(program);
|
39
|
+
if (Prefs.profileCompiler) console.log(`2. generated code in ${(performance.now() - t1).toFixed(2)}ms`);
|
40
40
|
|
41
41
|
if (Prefs.funcs) logFuncs(funcs, globals, exceptions);
|
42
42
|
|
43
43
|
const t2 = performance.now();
|
44
44
|
opt(funcs, globals, pages, tags, exceptions);
|
45
|
-
if (
|
45
|
+
if (Prefs.profileCompiler) console.log(`3. optimized in ${(performance.now() - t2).toFixed(2)}ms`);
|
46
46
|
|
47
47
|
if (Prefs.optFuncs) logFuncs(funcs, globals, exceptions);
|
48
48
|
|
49
49
|
const t3 = performance.now();
|
50
|
-
const
|
51
|
-
if (
|
50
|
+
const wasm = assemble(funcs, globals, tags, pages, data, flags);
|
51
|
+
if (Prefs.profileCompiler) console.log(`4. assembled in ${(performance.now() - t3).toFixed(2)}ms`);
|
52
52
|
|
53
53
|
if (Prefs.allocLog) {
|
54
54
|
const wasmPages = Math.ceil((pages.size * pageSize) / 65536);
|
@@ -57,13 +57,13 @@ export default (code, flags) => {
|
|
57
57
|
console.log([...pages.keys()].map(x => `\x1B[36m - ${x}\x1B[0m`).join('\n') + '\n');
|
58
58
|
}
|
59
59
|
|
60
|
-
const out = { wasm
|
60
|
+
const out = { wasm, funcs, globals, tags, exceptions, pages, data };
|
61
61
|
|
62
62
|
const target = Prefs.target ?? 'wasm';
|
63
63
|
const outFile = Prefs.o;
|
64
64
|
|
65
65
|
if (target === 'wasm' && outFile) {
|
66
|
-
writeFileSync(outFile, Buffer.from(
|
66
|
+
fs.writeFileSync(outFile, Buffer.from(wasm));
|
67
67
|
|
68
68
|
if (process.version) process.exit();
|
69
69
|
}
|
@@ -73,7 +73,7 @@ export default (code, flags) => {
|
|
73
73
|
out.c = c;
|
74
74
|
|
75
75
|
if (outFile) {
|
76
|
-
writeFileSync(outFile, c);
|
76
|
+
fs.writeFileSync(outFile, c);
|
77
77
|
} else {
|
78
78
|
console.log(c);
|
79
79
|
}
|
@@ -88,17 +88,19 @@ export default (code, flags) => {
|
|
88
88
|
if (compiler === 'zig') compiler = [ 'zig', 'cc' ];
|
89
89
|
else compiler = [ compiler ];
|
90
90
|
|
91
|
-
const tmpfile = '
|
91
|
+
const tmpfile = 'porffor_tmp.c';
|
92
92
|
// const args = [ compiler, tmpfile, '-o', outFile ?? (process.platform === 'win32' ? 'out.exe' : 'out'), '-' + cO, '-march=native', '-s', '-fno-unwind-tables', '-fno-asynchronous-unwind-tables', '-ffunction-sections', '-fdata-sections', '-Wl', '-fno-ident', '-fno-exceptions', '-ffast-math' ];
|
93
93
|
// const args = [ ...compiler, tmpfile, '-o', outFile ?? (process.platform === 'win32' ? 'out.exe' : 'out'), '-' + cO, '-march=native', '-s', '-ffast-math', '-fno-exceptions', '-target', 'x86_64-linux' ];
|
94
94
|
const args = [ ...compiler, tmpfile, '-o', outFile ?? (process.platform === 'win32' ? 'out.exe' : 'out'), '-' + cO, '-march=native', '-s', '-ffast-math', '-fno-exceptions' ];
|
95
95
|
|
96
96
|
const c = toc(out);
|
97
|
-
writeFileSync(tmpfile, c);
|
97
|
+
fs.writeFileSync(tmpfile, c);
|
98
98
|
|
99
99
|
// obvious command escape is obvious
|
100
100
|
execSync(args.join(' '), { stdio: 'inherit' });
|
101
101
|
|
102
|
+
fs.unlinkSync(tmpfile);
|
103
|
+
|
102
104
|
if (process.version) process.exit();
|
103
105
|
}
|
104
106
|
|
package/compiler/log.js
CHANGED
@@ -5,9 +5,9 @@ export const bold = x => `\u001b[1m${x}\u001b[0m`;
|
|
5
5
|
const areaColors = {
|
6
6
|
codegen: [ 20, 80, 250 ],
|
7
7
|
opt: [ 250, 20, 80 ],
|
8
|
-
|
8
|
+
assemble: [ 20, 250, 80 ],
|
9
9
|
alloc: [ 250, 250, 20 ],
|
10
|
-
|
10
|
+
parse: [ 240, 240, 240 ],
|
11
11
|
'2c': [ 20, 250, 250 ],
|
12
12
|
wrap: [ 250, 100, 20 ]
|
13
13
|
};
|
package/compiler/opt.js
CHANGED
@@ -100,8 +100,9 @@ export default (funcs, globals, pages, tags, exceptions) => {
|
|
100
100
|
|
101
101
|
if (Prefs.optInlineOnly) return;
|
102
102
|
|
103
|
-
|
104
|
-
const
|
103
|
+
// todo: this breaks exceptions after due to indexes not being adjusted
|
104
|
+
// const tagUse = tags.reduce((acc, x) => { acc[x.idx] = 0; return acc; }, {});
|
105
|
+
// const exceptionUse = exceptions.reduce((acc, _, i) => { acc[i] = 0; return acc; }, {});
|
105
106
|
|
106
107
|
// wasm transform pass
|
107
108
|
for (const f of funcs) {
|
@@ -109,7 +110,7 @@ export default (funcs, globals, pages, tags, exceptions) => {
|
|
109
110
|
|
110
111
|
const lastType = f.locals['#last_type'];
|
111
112
|
|
112
|
-
let runs = 2; // how many by default?
|
113
|
+
let runs = (+Prefs.optWasmRuns) || 2; // todo: how many by default?
|
113
114
|
while (runs > 0) {
|
114
115
|
runs--;
|
115
116
|
|
@@ -131,12 +132,12 @@ export default (funcs, globals, pages, tags, exceptions) => {
|
|
131
132
|
if (inst[0] === Opcodes.local_get) getCount[inst[1]]++;
|
132
133
|
if (inst[0] === Opcodes.local_set || inst[0] === Opcodes.local_tee) setCount[inst[1]]++;
|
133
134
|
|
134
|
-
if (inst[0] === Opcodes.throw) {
|
135
|
-
|
135
|
+
// if (inst[0] === Opcodes.throw) {
|
136
|
+
// tagUse[inst[1]]++;
|
136
137
|
|
137
|
-
|
138
|
-
|
139
|
-
}
|
138
|
+
// const exceptId = read_signedLEB128(wasm[i - 1].slice(1));
|
139
|
+
// exceptionUse[exceptId]++;
|
140
|
+
// }
|
140
141
|
|
141
142
|
if (inst[0] === Opcodes.block) {
|
142
143
|
// remove unneeded blocks (no brs inside)
|
@@ -171,14 +172,14 @@ export default (funcs, globals, pages, tags, exceptions) => {
|
|
171
172
|
}
|
172
173
|
}
|
173
174
|
|
174
|
-
if (inst[inst.length - 1] === 'string_only' && !pages.
|
175
|
+
if (inst[inst.length - 1] === 'string_only' && !pages.hasAnyString) {
|
175
176
|
// remove this inst
|
176
177
|
wasm.splice(i, 1);
|
177
178
|
if (i > 0) i--;
|
178
179
|
inst = wasm[i];
|
179
180
|
}
|
180
181
|
|
181
|
-
if (inst[inst.length - 1] === 'string_only|start' && !pages.
|
182
|
+
if (inst[inst.length - 1] === 'string_only|start' && !pages.hasAnyString) {
|
182
183
|
let j = i;
|
183
184
|
for (; j < wasm.length; j++) {
|
184
185
|
const op = wasm[j];
|
@@ -553,18 +554,18 @@ export default (funcs, globals, pages, tags, exceptions) => {
|
|
553
554
|
}
|
554
555
|
}
|
555
556
|
|
556
|
-
for (const x in tagUse) {
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
}
|
562
|
-
|
563
|
-
for (const x of Object.keys(exceptionUse).sort((a, b) => b - a)) {
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
}
|
557
|
+
// for (const x in tagUse) {
|
558
|
+
// if (tagUse[x] === 0) {
|
559
|
+
// const el = tags.find(y => y.idx === x);
|
560
|
+
// tags.splice(tags.indexOf(el), 1);
|
561
|
+
// }
|
562
|
+
// }
|
563
|
+
|
564
|
+
// for (const x of Object.keys(exceptionUse).sort((a, b) => b - a)) {
|
565
|
+
// if (exceptionUse[x] === 0) {
|
566
|
+
// exceptions.splice(+x, 1);
|
567
|
+
// }
|
568
|
+
// }
|
568
569
|
|
569
570
|
// return funcs;
|
570
571
|
};
|
package/compiler/parse.js
CHANGED
@@ -20,37 +20,43 @@ globalThis.typedInput = types && Prefs.optTypes;
|
|
20
20
|
// - hermes-parser
|
21
21
|
// - @babel/parser
|
22
22
|
|
23
|
-
|
23
|
+
globalThis.parser = '';
|
24
|
+
let parse;
|
24
25
|
const loadParser = async (fallbackParser = 'acorn', forceParser) => {
|
25
26
|
parser = forceParser ?? process.argv.find(x => x.startsWith('-parser='))?.split('=')?.[1] ?? fallbackParser;
|
26
|
-
0, { parse } = (await import((globalThis.document ? 'https://esm.sh/' : '') + parser));
|
27
|
+
0, { parse } = (await import((globalThis.document || globalThis.Deno ? 'https://esm.sh/' : '') + parser));
|
27
28
|
};
|
28
29
|
globalThis._porf_loadParser = loadParser;
|
29
30
|
await loadParser(types ? '@babel/parser' : undefined);
|
30
31
|
|
31
|
-
if (types && !['@babel/parser', 'hermes-parser'].includes(parser)) log.warning('
|
32
|
+
if (types && !['@babel/parser', 'hermes-parser'].includes(parser)) log.warning('parse', `passed -parse-types with a parser (${parser}) which does not support`);
|
32
33
|
|
33
34
|
export default (input, flags) => {
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
35
|
+
try {
|
36
|
+
const ast = parse(input, {
|
37
|
+
// acorn
|
38
|
+
ecmaVersion: 'latest',
|
39
|
+
|
40
|
+
// meriyah
|
41
|
+
next: true,
|
42
|
+
module: flags.includes('module'),
|
43
|
+
webcompat: true,
|
44
|
+
|
45
|
+
// babel
|
46
|
+
plugins: types ? ['estree', 'typescript'] : ['estree'],
|
47
|
+
|
48
|
+
// multiple
|
49
|
+
sourceType: flags.includes('module') ? 'module' : 'script',
|
50
|
+
ranges: false,
|
51
|
+
tokens: false,
|
52
|
+
comments: false,
|
53
|
+
});
|
54
|
+
|
55
|
+
if (ast.type === 'File') return ast.program;
|
56
|
+
|
57
|
+
return ast;
|
58
|
+
} catch (e) {
|
59
|
+
// normalize error class thrown by 3rd party parsers
|
60
|
+
throw new SyntaxError(e.message, { cause: e });
|
61
|
+
}
|
56
62
|
};
|
package/compiler/precompile.js
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
import { Opcodes } from './wasmSpec.js';
|
2
|
+
import { TYPES } from './types.js';
|
2
3
|
|
3
4
|
import fs from 'node:fs';
|
4
5
|
import { join } from 'node:path';
|
@@ -6,33 +7,21 @@ import { join } from 'node:path';
|
|
6
7
|
import { fileURLToPath } from 'node:url';
|
7
8
|
const __dirname = fileURLToPath(new URL('.', import.meta.url));
|
8
9
|
|
9
|
-
const TYPES = {
|
10
|
-
number: 0x00,
|
11
|
-
boolean: 0x01,
|
12
|
-
string: 0x02,
|
13
|
-
undefined: 0x03,
|
14
|
-
object: 0x04,
|
15
|
-
function: 0x05,
|
16
|
-
symbol: 0x06,
|
17
|
-
bigint: 0x07,
|
18
|
-
|
19
|
-
// these are not "typeof" types but tracked internally
|
20
|
-
_array: 0x10,
|
21
|
-
_regexp: 0x11,
|
22
|
-
_bytestring: 0x12
|
23
|
-
};
|
24
|
-
|
25
10
|
// import porfParse from './parse.js';
|
26
11
|
// import porfCodegen from './codeGen.js';
|
27
12
|
|
28
13
|
const argv = process.argv.slice();
|
29
14
|
|
30
15
|
const compile = async (file, [ _funcs, _globals ]) => {
|
31
|
-
|
32
|
-
|
16
|
+
let source = fs.readFileSync(file, 'utf8');
|
17
|
+
let first = source.slice(0, source.indexOf('\n'));
|
18
|
+
|
19
|
+
if (first.startsWith('export default')) {
|
20
|
+
source = (await import(file)).default();
|
21
|
+
first = source.slice(0, source.indexOf('\n'));
|
22
|
+
}
|
33
23
|
|
34
|
-
let args = ['-bytestring'];
|
35
|
-
if (file.endsWith('.ts')) args.push('-parse-types', '-opt-types');
|
24
|
+
let args = ['-bytestring', '-todo-time=compile', '-no-aot-pointer-opt', '-scoped-page-names', '-parse-types', '-opt-types'];
|
36
25
|
if (first.startsWith('// @porf')) {
|
37
26
|
args = args.concat(first.slice('// @porf '.length).split(' '));
|
38
27
|
}
|
@@ -51,7 +40,12 @@ const compile = async (file, [ _funcs, _globals ]) => {
|
|
51
40
|
|
52
41
|
const exports = funcs.filter(x => x.export);
|
53
42
|
for (const x of exports) {
|
54
|
-
if (x.data)
|
43
|
+
if (x.data) {
|
44
|
+
x.data = x.data.map(x => data[x]);
|
45
|
+
for (const y in x.data) {
|
46
|
+
x.data[y].offset -= x.data[0].offset;
|
47
|
+
}
|
48
|
+
}
|
55
49
|
if (x.exceptions) x.exceptions = x.exceptions.map(x => {
|
56
50
|
const obj = exceptions[x];
|
57
51
|
if (obj) obj.exceptId = x;
|
@@ -83,7 +77,7 @@ const compile = async (file, [ _funcs, _globals ]) => {
|
|
83
77
|
if (!pageName || allocated.has(pageName)) continue;
|
84
78
|
allocated.add(pageName);
|
85
79
|
|
86
|
-
y.splice(0, 10, 'alloc', pageName, x.pages.get(pageName).type);
|
80
|
+
y.splice(0, 10, 'alloc', pageName, x.pages.get(pageName).type, valtypeBinary);
|
87
81
|
// y.push(x.pages.get(pageName));
|
88
82
|
}
|
89
83
|
}
|
@@ -110,16 +104,16 @@ import { number } from './embedding.js';
|
|
110
104
|
|
111
105
|
export const BuiltinFuncs = function() {
|
112
106
|
${funcs.map(x => ` this.${x.name} = {
|
113
|
-
wasm: (scope, { allocPage, builtin }) => ${JSON.stringify(x.wasm.filter(x => x.length && x[0] != null)).replace(/\["alloc","(.*?)","(.*?)"\]/g, (_, reason, type) => `...number(allocPage(scope, '${reason}', '${type}') * pageSize, ${
|
107
|
+
wasm: (scope, { allocPage, builtin }) => ${JSON.stringify(x.wasm.filter(x => x.length && x[0] != null)).replace(/\["alloc","(.*?)","(.*?)",(.*?)\]/g, (_, reason, type, valtype) => `...number(allocPage(scope, '${reason}', '${type}') * pageSize, ${valtype})`).replace(/\[16,"(.*?)"]/g, (_, name) => `[16, builtin('${name}')]`)},
|
114
108
|
params: ${JSON.stringify(x.params)},
|
115
109
|
typedParams: true,
|
116
110
|
returns: ${JSON.stringify(x.returns)},
|
117
|
-
typedReturns: true,
|
111
|
+
${x.returnType != null ? `returnType: ${JSON.stringify(x.returnType)}` : 'typedReturns: true'},
|
118
112
|
locals: ${JSON.stringify(Object.values(x.locals).slice(x.params.length).map(x => x.type))},
|
119
113
|
localNames: ${JSON.stringify(Object.keys(x.locals))},
|
120
114
|
${x.data && x.data.length > 0 ? ` data: ${JSON.stringify(x.data)},` : ''}
|
121
115
|
${x.exceptions && x.exceptions.length > 0 ? ` exceptions: ${JSON.stringify(x.exceptions)},` : ''}
|
122
|
-
};`.replaceAll('\n\n', '\n')).join('\n')}
|
116
|
+
};`.replaceAll('\n\n', '\n').replaceAll('\n\n', '\n')).join('\n')}
|
123
117
|
};`;
|
124
118
|
};
|
125
119
|
|
package/compiler/prefs.js
CHANGED
package/compiler/prototype.js
CHANGED
@@ -3,23 +3,7 @@ import { number } from "./embedding.js";
|
|
3
3
|
import { unsignedLEB128 } from "./encoding.js";
|
4
4
|
import { UNDEFINED } from "./builtins.js";
|
5
5
|
import Prefs from './prefs.js';
|
6
|
-
|
7
|
-
// todo: do not duplicate this
|
8
|
-
const TYPES = {
|
9
|
-
number: 0x00,
|
10
|
-
boolean: 0x01,
|
11
|
-
string: 0x02,
|
12
|
-
undefined: 0x03,
|
13
|
-
object: 0x04,
|
14
|
-
function: 0x05,
|
15
|
-
symbol: 0x06,
|
16
|
-
bigint: 0x07,
|
17
|
-
|
18
|
-
// these are not "typeof" types but tracked internally
|
19
|
-
_array: 0x10,
|
20
|
-
_regexp: 0x11,
|
21
|
-
_bytestring: 0x12
|
22
|
-
};
|
6
|
+
import { TYPES } from './types.js';
|
23
7
|
|
24
8
|
// todo: turn these into built-ins once arrays and these become less hacky
|
25
9
|
|
@@ -150,7 +134,7 @@ export const PrototypeFuncs = function() {
|
|
150
134
|
shift: (pointer, length) => [
|
151
135
|
// if length == 0, noop
|
152
136
|
...length.getCachedI32(),
|
153
|
-
Opcodes.i32_eqz,
|
137
|
+
[ Opcodes.i32_eqz ],
|
154
138
|
[ Opcodes.if, Blocktype.void ],
|
155
139
|
...number(UNDEFINED),
|
156
140
|
[ Opcodes.br, 1 ],
|
@@ -0,0 +1,37 @@
|
|
1
|
+
export const TYPES = {
|
2
|
+
number: 0x00,
|
3
|
+
boolean: 0x01,
|
4
|
+
string: 0x02,
|
5
|
+
undefined: 0x03,
|
6
|
+
object: 0x04,
|
7
|
+
function: 0x05,
|
8
|
+
symbol: 0x06,
|
9
|
+
bigint: 0x07
|
10
|
+
};
|
11
|
+
|
12
|
+
export const TYPE_NAMES = {
|
13
|
+
[TYPES.number]: 'Number',
|
14
|
+
[TYPES.boolean]: 'Boolean',
|
15
|
+
[TYPES.string]: 'String',
|
16
|
+
[TYPES.undefined]: 'undefined',
|
17
|
+
[TYPES.object]: 'Object',
|
18
|
+
[TYPES.function]: 'Function',
|
19
|
+
[TYPES.symbol]: 'Symbol',
|
20
|
+
[TYPES.bigint]: 'BigInt'
|
21
|
+
};
|
22
|
+
|
23
|
+
export const INTERNAL_TYPE_BASE = 0x10;
|
24
|
+
let internalTypeIndex = INTERNAL_TYPE_BASE;
|
25
|
+
const registerInternalType = name => {
|
26
|
+
const n = internalTypeIndex++;
|
27
|
+
TYPES['_' + name.toLowerCase()] = n;
|
28
|
+
TYPE_NAMES[n] = name;
|
29
|
+
};
|
30
|
+
|
31
|
+
// note: when adding a new internal type, please also add a deserializer to wrap.js
|
32
|
+
// (it is okay to add a throw todo deserializer for wips)
|
33
|
+
|
34
|
+
registerInternalType('Array');
|
35
|
+
registerInternalType('RegExp');
|
36
|
+
registerInternalType('ByteString');
|
37
|
+
registerInternalType('Date');
|
package/compiler/wasmSpec.js
CHANGED
@@ -1,4 +1,13 @@
|
|
1
|
-
|
1
|
+
const enumify = (...args) => {
|
2
|
+
const obj = {};
|
3
|
+
|
4
|
+
for (let i = 0; i < args.length; i++) {
|
5
|
+
obj[i] = args[i];
|
6
|
+
obj[args[i]] = i;
|
7
|
+
}
|
8
|
+
|
9
|
+
return obj;
|
10
|
+
};
|
2
11
|
|
3
12
|
export const Section = enumify('custom', 'type', 'import', 'func', 'table', 'memory', 'global', 'export', 'start', 'element', 'code', 'data', 'data_count', 'tag');
|
4
13
|
export const ExportDesc = enumify('func', 'table', 'mem', 'global', 'tag');
|
@@ -107,6 +116,8 @@ export const Opcodes = {
|
|
107
116
|
i32_shl: 0x74,
|
108
117
|
i32_shr_s: 0x75,
|
109
118
|
i32_shr_u: 0x76,
|
119
|
+
i32_rotl: 0x77,
|
120
|
+
i32_rotr: 0x78,
|
110
121
|
|
111
122
|
i64_eqz: 0x50,
|
112
123
|
i64_eq: 0x51,
|
@@ -130,6 +141,7 @@ export const Opcodes = {
|
|
130
141
|
i64_shr_s: 0x87,
|
131
142
|
i64_shr_u: 0x88,
|
132
143
|
i64_rotl: 0x89,
|
144
|
+
i64_rotr: 0x8a,
|
133
145
|
|
134
146
|
f64_eq: 0x61,
|
135
147
|
f64_ne: 0x62,
|
package/compiler/wrap.js
CHANGED
@@ -3,39 +3,24 @@ import decompile from './decompile.js';
|
|
3
3
|
import { encodeVector, encodeLocal } from './encoding.js';
|
4
4
|
import Prefs from './prefs.js';
|
5
5
|
import { log } from './log.js';
|
6
|
+
import { TYPES } from './types.js';
|
6
7
|
|
7
8
|
const bold = x => `\u001b[1m${x}\u001b[0m`;
|
8
9
|
|
9
|
-
const typeBase = 0x00;
|
10
|
-
const internalTypeBase = 0x10;
|
11
|
-
const TYPES = {
|
12
|
-
[typeBase]: 'number',
|
13
|
-
[typeBase + 1]: 'boolean',
|
14
|
-
[typeBase + 2]: 'string',
|
15
|
-
[typeBase + 3]: 'undefined',
|
16
|
-
[typeBase + 4]: 'object',
|
17
|
-
[typeBase + 5]: 'function',
|
18
|
-
[typeBase + 6]: 'symbol',
|
19
|
-
[typeBase + 7]: 'bigint',
|
20
|
-
|
21
|
-
// internal
|
22
|
-
[internalTypeBase]: '_array',
|
23
|
-
[internalTypeBase + 1]: '_regexp',
|
24
|
-
[internalTypeBase + 2]: '_bytestring'
|
25
|
-
};
|
26
|
-
|
27
10
|
export default async (source, flags = [ 'module' ], customImports = {}, print = str => process.stdout.write(str)) => {
|
28
11
|
const times = [];
|
29
12
|
|
30
13
|
const t1 = performance.now();
|
31
14
|
const { wasm, funcs, globals, tags, exceptions, pages, c } = compile(source, flags);
|
32
15
|
|
16
|
+
globalThis.porfDebugInfo = { funcs, globals };
|
17
|
+
|
33
18
|
if (source.includes('export function')) flags.push('module');
|
34
19
|
|
35
20
|
// (await import('node:fs')).writeFileSync('out.wasm', Buffer.from(wasm));
|
36
21
|
|
37
22
|
times.push(performance.now() - t1);
|
38
|
-
if (
|
23
|
+
if (Prefs.profileCompiler) console.log(bold(`compiled in ${times[0].toFixed(2)}ms`));
|
39
24
|
|
40
25
|
const t2 = performance.now();
|
41
26
|
|
@@ -51,7 +36,9 @@ export default async (source, flags = [ 'module' ], customImports = {}, print =
|
|
51
36
|
'': {
|
52
37
|
p: valtype === 'i64' ? i => print(Number(i).toString()) : i => print(i.toString()),
|
53
38
|
c: valtype === 'i64' ? i => print(String.fromCharCode(Number(i))) : i => print(String.fromCharCode(i)),
|
54
|
-
t:
|
39
|
+
t: () => performance.now(),
|
40
|
+
y: () => {},
|
41
|
+
z: () => {},
|
55
42
|
...customImports
|
56
43
|
}
|
57
44
|
});
|
@@ -59,8 +46,10 @@ export default async (source, flags = [ 'module' ], customImports = {}, print =
|
|
59
46
|
// only backtrace for runner, not test262/etc
|
60
47
|
if (!process.argv[1].includes('/runner')) throw e;
|
61
48
|
|
62
|
-
const funcInd = parseInt(e.message.match(/function #([0-9]+) /)[1]);
|
63
|
-
const blobOffset = parseInt(e.message.split('@')[1]);
|
49
|
+
const funcInd = parseInt(e.message.match(/function #([0-9]+) /)?.[1]);
|
50
|
+
const blobOffset = parseInt(e.message.split('@')?.[1]);
|
51
|
+
|
52
|
+
if (!funcInd) throw e;
|
64
53
|
|
65
54
|
// convert blob offset -> function wasm offset.
|
66
55
|
// this is not good code and is somewhat duplicated
|
@@ -138,7 +127,7 @@ export default async (source, flags = [ 'module' ], customImports = {}, print =
|
|
138
127
|
}
|
139
128
|
|
140
129
|
times.push(performance.now() - t2);
|
141
|
-
if (
|
130
|
+
if (Prefs.profileCompiler) console.log(`instantiated in ${times[1].toFixed(2)}ms`);
|
142
131
|
|
143
132
|
const exports = {};
|
144
133
|
|
@@ -166,43 +155,48 @@ export default async (source, flags = [ 'module' ], customImports = {}, print =
|
|
166
155
|
|
167
156
|
// if (ret >= typeBase && ret <= typeBase + 8) return ret > (typeBase + 7) ? 'object' : TYPES[ret];
|
168
157
|
|
169
|
-
switch (
|
170
|
-
case
|
171
|
-
case
|
172
|
-
case
|
158
|
+
switch (type) {
|
159
|
+
case TYPES.boolean: return Boolean(ret);
|
160
|
+
case TYPES.undefined: return undefined;
|
161
|
+
case TYPES.object: return ret === 0 ? null : {};
|
173
162
|
|
174
|
-
case
|
163
|
+
case TYPES.string: {
|
175
164
|
const pointer = ret;
|
176
165
|
const length = new Int32Array(memory.buffer, pointer, 1);
|
177
166
|
|
178
|
-
|
179
|
-
|
167
|
+
return Array.from(new Uint16Array(memory.buffer, pointer + 4, length)).map(x => String.fromCharCode(x)).join('');
|
168
|
+
}
|
180
169
|
|
181
|
-
|
170
|
+
case TYPES.function: {
|
171
|
+
// wasm func index, including all imports
|
172
|
+
const func = funcs.find(x => (x.originalIndex ?? x.index) === ret);
|
173
|
+
// if (!func) return ret;
|
174
|
+
if (!func) return function () {};
|
175
|
+
|
176
|
+
// make fake empty func for repl/etc
|
177
|
+
return {[func.name]() {}}[func.name];
|
182
178
|
}
|
183
179
|
|
184
|
-
case
|
180
|
+
case TYPES._array: {
|
185
181
|
const pointer = ret;
|
186
182
|
const length = new Int32Array(memory.buffer, pointer, 1);
|
187
183
|
|
188
|
-
|
184
|
+
// have to slice because of memory alignment
|
185
|
+
const buf = memory.buffer.slice(pointer + 4, pointer + 4 + 8 * length);
|
186
|
+
|
187
|
+
return Array.from(new Float64Array(buf));
|
189
188
|
}
|
190
189
|
|
191
|
-
case
|
190
|
+
case TYPES._bytestring: {
|
192
191
|
const pointer = ret;
|
193
192
|
const length = new Int32Array(memory.buffer, pointer, 1);
|
194
193
|
|
195
194
|
return Array.from(new Uint8Array(memory.buffer, pointer + 4, length)).map(x => String.fromCharCode(x)).join('');
|
196
195
|
}
|
197
196
|
|
198
|
-
case
|
199
|
-
//
|
200
|
-
|
201
|
-
// if (!func) return ret;
|
202
|
-
if (!func) return function () {};
|
203
|
-
|
204
|
-
// make fake empty func for repl/etc
|
205
|
-
return {[func.name]() {}}[func.name];
|
197
|
+
case TYPES._date: {
|
198
|
+
// todo
|
199
|
+
throw new Error('todo! deserialize date');
|
206
200
|
}
|
207
201
|
|
208
202
|
default: return ret;
|
package/empty.js
ADDED
File without changes
|
package/hello.js
ADDED
package/package.json
CHANGED
@@ -1,21 +1,25 @@
|
|
1
1
|
{
|
2
2
|
"name": "porffor",
|
3
3
|
"description": "a basic experimental wip aot optimizing js -> wasm engine/compiler/runtime in js",
|
4
|
-
"version": "0.2.0-
|
4
|
+
"version": "0.2.0-767de65",
|
5
5
|
"author": "CanadaHonk",
|
6
6
|
"license": "MIT",
|
7
|
+
"scripts": {
|
8
|
+
"precompile": "node ./compiler/precompile.js"
|
9
|
+
},
|
7
10
|
"dependencies": {
|
8
|
-
"acorn": "^8.
|
11
|
+
"acorn": "^8.11.3",
|
12
|
+
"node-repl-polyfill": "^0.1.1"
|
9
13
|
},
|
10
14
|
"optionalDependencies": {
|
11
|
-
"@babel/parser": "^7.
|
15
|
+
"@babel/parser": "^7.24.4",
|
12
16
|
"hermes-parser": "^0.18.2",
|
13
17
|
"meriyah": "^4.3.9"
|
14
18
|
},
|
15
19
|
"bin": {
|
16
20
|
"porf": "./runner/index.js"
|
17
21
|
},
|
18
|
-
"main": "./
|
22
|
+
"main": "./compiler/wrap.js",
|
19
23
|
"type": "module",
|
20
24
|
"repository": {
|
21
25
|
"type": "git",
|
@@ -25,4 +29,4 @@
|
|
25
29
|
"url": "https://github.com/CanadaHonk/porffor/issues"
|
26
30
|
},
|
27
31
|
"homepage": "https://porffor.goose.icu"
|
28
|
-
}
|
32
|
+
}
|
package/porf
CHANGED