porffor 0.2.0-6aff0fa → 0.2.0-6bc63ef
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 +115 -82
- package/asur/index.js +624 -340
- package/byg/index.js +216 -0
- package/compiler/2c.js +2 -53
- package/compiler/{sections.js → assemble.js} +60 -14
- 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 +7 -84
- 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 +44 -7
- package/compiler/builtins/set.ts +187 -0
- package/compiler/builtins/string.ts +1080 -0
- package/compiler/builtins.js +400 -120
- package/compiler/{codeGen.js → codegen.js} +850 -402
- 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 +1613 -3
- package/compiler/index.js +16 -16
- package/compiler/log.js +2 -2
- package/compiler/opt.js +28 -27
- package/compiler/parse.js +36 -30
- package/compiler/precompile.js +37 -46
- package/compiler/prefs.js +7 -6
- package/compiler/prototype.js +20 -36
- package/compiler/types.js +38 -0
- package/compiler/wasmSpec.js +14 -1
- package/compiler/wrap.js +79 -69
- package/package.json +9 -5
- package/porf +2 -0
- package/rhemyn/compile.js +44 -26
- 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 +69 -12
- package/runner/profiler.js +22 -30
- package/runner/repl.js +40 -13
- package/runner/sizes.js +37 -37
- package/runner/version.js +3 -3
- package/runner/info.js +0 -89
- package/runner/transform.js +0 -15
- package/util/enum.js +0 -20
package/compiler/wrap.js
CHANGED
@@ -1,27 +1,74 @@
|
|
1
1
|
import compile from './index.js';
|
2
2
|
import decompile from './decompile.js';
|
3
3
|
import { encodeVector, encodeLocal } from './encoding.js';
|
4
|
-
import
|
4
|
+
import { TYPES } from './types.js';
|
5
5
|
import { log } from './log.js';
|
6
|
+
import Prefs from './prefs.js';
|
6
7
|
|
7
8
|
const bold = x => `\u001b[1m${x}\u001b[0m`;
|
8
9
|
|
9
|
-
const
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
10
|
+
const porfToJSValue = (memory, funcs, value, type) => {
|
11
|
+
switch (type) {
|
12
|
+
case TYPES.boolean: return Boolean(value);
|
13
|
+
case TYPES.undefined: return undefined;
|
14
|
+
case TYPES.object: return value === 0 ? null : {};
|
15
|
+
|
16
|
+
case TYPES.function: {
|
17
|
+
// wasm func index, including all imports
|
18
|
+
const func = funcs.find(x => (x.originalIndex ?? x.index) === value);
|
19
|
+
// if (!func) return value;
|
20
|
+
if (!func) return function () {};
|
21
|
+
|
22
|
+
// make fake empty func for repl/etc
|
23
|
+
return {[func.name]() {}}[func.name];
|
24
|
+
}
|
25
|
+
|
26
|
+
case TYPES.string: {
|
27
|
+
const length = (new Int32Array(memory.buffer, value, 1))[0];
|
28
|
+
return Array.from(new Uint16Array(memory.buffer, value + 4, length)).map(x => String.fromCharCode(x)).join('');
|
29
|
+
}
|
30
|
+
|
31
|
+
case TYPES.bytestring: {
|
32
|
+
const length = (new Int32Array(memory.buffer, value, 1))[0];
|
33
|
+
return Array.from(new Uint8Array(memory.buffer, value + 4, length)).map(x => String.fromCharCode(x)).join('');
|
34
|
+
}
|
35
|
+
|
36
|
+
case TYPES.array: {
|
37
|
+
const length = (new Int32Array(memory.buffer, value, 1))[0];
|
38
|
+
|
39
|
+
// have to slice because of memory alignment (?)
|
40
|
+
const buf = memory.buffer.slice(value + 4, value + 4 + 8 * length);
|
41
|
+
return Array.from(new Float64Array(buf, 0, length));
|
42
|
+
}
|
43
|
+
|
44
|
+
case TYPES.date: {
|
45
|
+
const t = (new Float64Array(memory.buffer, value, 1))[0];
|
46
|
+
return new Date(t);
|
47
|
+
}
|
48
|
+
|
49
|
+
case TYPES.set: {
|
50
|
+
const size = (new Int32Array(memory.buffer, value, 1))[0];
|
51
|
+
|
52
|
+
const out = new Set();
|
53
|
+
for (let i = 0; i < size; i++) {
|
54
|
+
const offset = value + 4 + (i * 9);
|
55
|
+
|
56
|
+
// have to slice because of memory alignment (?)
|
57
|
+
const v = (new Float64Array(memory.buffer.slice(offset, offset + 8), 0, 1))[0];
|
58
|
+
const t = (new Uint8Array(memory.buffer, offset + 8, 1))[0];
|
59
|
+
|
60
|
+
// console.log(`reading value at index ${i}...`)
|
61
|
+
// console.log(' memory:', Array.from(new Uint8Array(memory.buffer, offset, 9)).map(x => x.toString(16).padStart(2, '0')).join(' '));
|
62
|
+
// console.log(' read:', { value: v, type: t }, '\n');
|
63
|
+
|
64
|
+
out.add(porfToJSValue(memory, funcs, v, t));
|
65
|
+
}
|
66
|
+
|
67
|
+
return out;
|
68
|
+
}
|
69
|
+
|
70
|
+
default: return value;
|
71
|
+
}
|
25
72
|
};
|
26
73
|
|
27
74
|
export default async (source, flags = [ 'module' ], customImports = {}, print = str => process.stdout.write(str)) => {
|
@@ -30,12 +77,14 @@ export default async (source, flags = [ 'module' ], customImports = {}, print =
|
|
30
77
|
const t1 = performance.now();
|
31
78
|
const { wasm, funcs, globals, tags, exceptions, pages, c } = compile(source, flags);
|
32
79
|
|
80
|
+
globalThis.porfDebugInfo = { funcs, globals };
|
81
|
+
|
33
82
|
if (source.includes('export function')) flags.push('module');
|
34
83
|
|
35
84
|
// (await import('node:fs')).writeFileSync('out.wasm', Buffer.from(wasm));
|
36
85
|
|
37
86
|
times.push(performance.now() - t1);
|
38
|
-
if (
|
87
|
+
if (Prefs.profileCompiler) console.log(bold(`compiled in ${times[0].toFixed(2)}ms`));
|
39
88
|
|
40
89
|
const t2 = performance.now();
|
41
90
|
|
@@ -51,7 +100,10 @@ export default async (source, flags = [ 'module' ], customImports = {}, print =
|
|
51
100
|
'': {
|
52
101
|
p: valtype === 'i64' ? i => print(Number(i).toString()) : i => print(i.toString()),
|
53
102
|
c: valtype === 'i64' ? i => print(String.fromCharCode(Number(i))) : i => print(String.fromCharCode(i)),
|
54
|
-
t:
|
103
|
+
t: () => performance.now(),
|
104
|
+
u: () => performance.timeOrigin,
|
105
|
+
y: () => {},
|
106
|
+
z: () => {},
|
55
107
|
...customImports
|
56
108
|
}
|
57
109
|
});
|
@@ -59,8 +111,10 @@ export default async (source, flags = [ 'module' ], customImports = {}, print =
|
|
59
111
|
// only backtrace for runner, not test262/etc
|
60
112
|
if (!process.argv[1].includes('/runner')) throw e;
|
61
113
|
|
62
|
-
const funcInd = parseInt(e.message.match(/function #([0-9]+) /)[1]);
|
63
|
-
const blobOffset = parseInt(e.message.split('@')[1]);
|
114
|
+
const funcInd = parseInt(e.message.match(/function #([0-9]+) /)?.[1]);
|
115
|
+
const blobOffset = parseInt(e.message.split('@')?.[1]);
|
116
|
+
|
117
|
+
if (!funcInd) throw e;
|
64
118
|
|
65
119
|
// convert blob offset -> function wasm offset.
|
66
120
|
// this is not good code and is somewhat duplicated
|
@@ -138,7 +192,7 @@ export default async (source, flags = [ 'module' ], customImports = {}, print =
|
|
138
192
|
}
|
139
193
|
|
140
194
|
times.push(performance.now() - t2);
|
141
|
-
if (
|
195
|
+
if (Prefs.profileCompiler) console.log(`instantiated in ${times[1].toFixed(2)}ms`);
|
142
196
|
|
143
197
|
const exports = {};
|
144
198
|
|
@@ -158,55 +212,11 @@ export default async (source, flags = [ 'module' ], customImports = {}, print =
|
|
158
212
|
|
159
213
|
exports[func.name] = function() {
|
160
214
|
try {
|
161
|
-
const
|
162
|
-
|
163
|
-
if (_ret == null) return undefined;
|
164
|
-
|
165
|
-
const [ ret, type ] = _ret;
|
166
|
-
|
167
|
-
// if (ret >= typeBase && ret <= typeBase + 8) return ret > (typeBase + 7) ? 'object' : TYPES[ret];
|
168
|
-
|
169
|
-
switch (TYPES[type]) {
|
170
|
-
case 'boolean': return Boolean(ret);
|
171
|
-
case 'undefined': return undefined;
|
172
|
-
case 'object': return ret === 0 ? null : {};
|
215
|
+
const ret = exp.apply(this, arguments);
|
173
216
|
|
174
|
-
|
175
|
-
const pointer = ret;
|
176
|
-
const length = new Int32Array(memory.buffer, pointer, 1);
|
217
|
+
if (ret == null) return undefined;
|
177
218
|
|
178
|
-
|
179
|
-
const buf = memory.buffer.slice(pointer + 4, pointer + 4 + 8 * length);
|
180
|
-
|
181
|
-
return Array.from(new Float64Array(buf));
|
182
|
-
}
|
183
|
-
|
184
|
-
case 'string': {
|
185
|
-
const pointer = ret;
|
186
|
-
const length = new Int32Array(memory.buffer, pointer, 1);
|
187
|
-
|
188
|
-
return Array.from(new Uint16Array(memory.buffer, pointer + 4, length)).map(x => String.fromCharCode(x)).join('');
|
189
|
-
}
|
190
|
-
|
191
|
-
case '_bytestring': {
|
192
|
-
const pointer = ret;
|
193
|
-
const length = new Int32Array(memory.buffer, pointer, 1);
|
194
|
-
|
195
|
-
return Array.from(new Uint8Array(memory.buffer, pointer + 4, length)).map(x => String.fromCharCode(x)).join('');
|
196
|
-
}
|
197
|
-
|
198
|
-
case 'function': {
|
199
|
-
// wasm func index, including all imports
|
200
|
-
const func = funcs.find(x => (x.originalIndex ?? x.index) === ret);
|
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];
|
206
|
-
}
|
207
|
-
|
208
|
-
default: return ret;
|
209
|
-
}
|
219
|
+
return porfToJSValue(memory, funcs, ret[0], ret[1])
|
210
220
|
} catch (e) {
|
211
221
|
if (e.is && e.is(exceptTag)) {
|
212
222
|
const exceptId = e.getArg(exceptTag, 0);
|
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-6bc63ef",
|
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
package/rhemyn/compile.js
CHANGED
@@ -1,8 +1,8 @@
|
|
1
|
-
import { Blocktype, Opcodes, Valtype,
|
1
|
+
import { Blocktype, Opcodes, Valtype, ValtypeSize } from '../compiler/wasmSpec.js';
|
2
2
|
import { number } from '../compiler/embedding.js';
|
3
|
-
import { signedLEB128, unsignedLEB128 } from '../compiler/encoding.js';
|
4
3
|
import parse from './parse.js';
|
5
4
|
import Prefs from '../compiler/prefs.js';
|
5
|
+
import { TYPES } from '../compiler/types.js';
|
6
6
|
|
7
7
|
// local indexes
|
8
8
|
const BasePointer = 0; // base string pointer
|
@@ -14,7 +14,7 @@ const Length = 5;
|
|
14
14
|
const Tmp = 6;
|
15
15
|
|
16
16
|
let exprLastGet = false;
|
17
|
-
const generate = (node, negated = false, get = true, func = 'test') => {
|
17
|
+
const generate = (node, negated = false, get = true, stringSize = 2, func = 'test') => {
|
18
18
|
let out = [];
|
19
19
|
switch (node.type) {
|
20
20
|
case 'Expression':
|
@@ -42,7 +42,7 @@ const generate = (node, negated = false, get = true, func = 'test') => {
|
|
42
42
|
// generate checks
|
43
43
|
...node.body.flatMap((x, i) => {
|
44
44
|
exprLastGet = x.type !== 'Group' && i === (node.body.length - 1);
|
45
|
-
return generate(x, negated);
|
45
|
+
return generate(x, negated, true, stringSize, func);
|
46
46
|
}),
|
47
47
|
|
48
48
|
// reached end without branching out, successful match
|
@@ -56,9 +56,9 @@ const generate = (node, negated = false, get = true, func = 'test') => {
|
|
56
56
|
|
57
57
|
[ Opcodes.end ],
|
58
58
|
|
59
|
-
// increment iter pointer by
|
59
|
+
// increment iter pointer by string size
|
60
60
|
[ Opcodes.local_get, IterPointer ],
|
61
|
-
...number(
|
61
|
+
...number(stringSize, Valtype.i32),
|
62
62
|
[ Opcodes.i32_add ],
|
63
63
|
[ Opcodes.local_set, IterPointer ],
|
64
64
|
|
@@ -91,34 +91,34 @@ const generate = (node, negated = false, get = true, func = 'test') => {
|
|
91
91
|
break;
|
92
92
|
|
93
93
|
case 'Character':
|
94
|
-
out = generateChar(node, node.negated ^ negated, get);
|
94
|
+
out = generateChar(node, node.negated ^ negated, get, stringSize);
|
95
95
|
break;
|
96
96
|
|
97
97
|
case 'Set':
|
98
|
-
out = generateSet(node, node.negated, get);
|
98
|
+
out = generateSet(node, node.negated, get, stringSize);
|
99
99
|
break;
|
100
100
|
|
101
101
|
case 'Group':
|
102
|
-
out = generateGroup(node, negated, get);
|
102
|
+
out = generateGroup(node, negated, get, stringSize);
|
103
103
|
break;
|
104
104
|
|
105
105
|
case 'Range':
|
106
|
-
out = generateRange(node, negated, get);
|
106
|
+
out = generateRange(node, negated, get, stringSize);
|
107
107
|
break;
|
108
108
|
}
|
109
109
|
|
110
110
|
return out;
|
111
111
|
};
|
112
112
|
|
113
|
-
const getNextChar = () => [
|
113
|
+
const getNextChar = (stringSize) => [
|
114
114
|
// get char from pointer
|
115
115
|
[ Opcodes.local_get, Pointer ],
|
116
|
-
[ Opcodes.i32_load16_u
|
116
|
+
[ stringSize == 2 ? Opcodes.i32_load16_u : Opcodes.i32_load8_u, 0, 0 ],
|
117
117
|
|
118
118
|
...(exprLastGet ? [] : [
|
119
|
-
// pointer +=
|
119
|
+
// pointer += string size
|
120
120
|
[ Opcodes.local_get, Pointer ],
|
121
|
-
...number(
|
121
|
+
...number(stringSize, Valtype.i32),
|
122
122
|
[ Opcodes.i32_add ],
|
123
123
|
[ Opcodes.local_set, Pointer ]
|
124
124
|
])
|
@@ -134,21 +134,21 @@ const checkFailure = () => [
|
|
134
134
|
[ Opcodes.br_if, 0 ]
|
135
135
|
];
|
136
136
|
|
137
|
-
const generateChar = (node, negated, get) => {
|
137
|
+
const generateChar = (node, negated, get, stringSize) => {
|
138
138
|
return [
|
139
|
-
...(get ? getNextChar() : []),
|
139
|
+
...(get ? getNextChar(stringSize) : []),
|
140
140
|
...number(node.char.charCodeAt(0), Valtype.i32),
|
141
141
|
negated ? [ Opcodes.i32_eq ] : [ Opcodes.i32_ne ],
|
142
142
|
...(get ? checkFailure(): [])
|
143
143
|
];
|
144
144
|
};
|
145
145
|
|
146
|
-
const generateSet = (node, negated, get) => {
|
146
|
+
const generateSet = (node, negated, get, stringSize) => {
|
147
147
|
// for a single char we do not need a tmp, it is like just
|
148
148
|
const singleChar = node.body.length === 1 && node.body[0].type === 'Character';
|
149
149
|
|
150
150
|
let out = [
|
151
|
-
...(get ? getNextChar() : []),
|
151
|
+
...(get ? getNextChar(stringSize) : []),
|
152
152
|
...(singleChar ? [] : [ [ Opcodes.local_set, Tmp ] ]),
|
153
153
|
];
|
154
154
|
|
@@ -156,11 +156,11 @@ const generateSet = (node, negated, get) => {
|
|
156
156
|
out = [
|
157
157
|
...out,
|
158
158
|
...(singleChar ? [] : [ [ Opcodes.local_get, Tmp ] ]),
|
159
|
-
...generate(x, negated, false)
|
159
|
+
...generate(x, negated, false, stringSize)
|
160
160
|
];
|
161
161
|
}
|
162
162
|
|
163
|
-
out = out.concat(new Array(node.body.length - 1).fill(negated ? [ Opcodes.i32_or ] : [ Opcodes.i32_and ]));
|
163
|
+
if (node.body.length > 0) out = out.concat(new Array(node.body.length - 1).fill(negated ? [ Opcodes.i32_or ] : [ Opcodes.i32_and ]));
|
164
164
|
|
165
165
|
return [
|
166
166
|
...out,
|
@@ -168,9 +168,9 @@ const generateSet = (node, negated, get) => {
|
|
168
168
|
];
|
169
169
|
};
|
170
170
|
|
171
|
-
const generateRange = (node, negated, get) => {
|
171
|
+
const generateRange = (node, negated, get, stringSize) => {
|
172
172
|
return [
|
173
|
-
...(get ? getNextChar() : []),
|
173
|
+
...(get ? getNextChar(stringSize) : []),
|
174
174
|
...(get ? [ [ Opcodes.local_tee, Tmp ] ] : []),
|
175
175
|
|
176
176
|
...number(node.from.charCodeAt(0), Valtype.i32),
|
@@ -188,11 +188,29 @@ const generateRange = (node, negated, get) => {
|
|
188
188
|
};
|
189
189
|
|
190
190
|
const generateGroup = (node, negated, get) => {
|
191
|
+
// todo
|
192
|
+
return [];
|
193
|
+
};
|
191
194
|
|
195
|
+
const wrapFunc = (regex, func, name, index) => {
|
196
|
+
const parsed = parse(regex);
|
197
|
+
|
198
|
+
return outputFunc([
|
199
|
+
[ Opcodes.local_get, 1 ],
|
200
|
+
...number(TYPES.string, Valtype.i32),
|
201
|
+
[ Opcodes.i32_eq ],
|
202
|
+
[ Opcodes.if, Valtype.i32 ],
|
203
|
+
// string
|
204
|
+
...generate(parsed, false, true, 2, func),
|
205
|
+
[ Opcodes.else ],
|
206
|
+
// bytestring
|
207
|
+
...generate(parsed, false, true, 1, func),
|
208
|
+
[ Opcodes.end ]
|
209
|
+
], name, index);
|
192
210
|
};
|
193
211
|
|
194
|
-
export const test = (regex, index = 0, name = 'regex_test_' + regex) =>
|
195
|
-
export const search = (regex, index = 0, name = 'regex_search_' + regex) =>
|
212
|
+
export const test = (regex, index = 0, name = 'regex_test_' + regex) => wrapFunc(regex, 'test', name, index);
|
213
|
+
export const search = (regex, index = 0, name = 'regex_search_' + regex) => wrapFunc(regex, 'search', name, index);
|
196
214
|
|
197
215
|
const outputFunc = (wasm, name, index) => ({
|
198
216
|
name,
|
@@ -200,9 +218,9 @@ const outputFunc = (wasm, name, index) => ({
|
|
200
218
|
wasm,
|
201
219
|
|
202
220
|
export: true,
|
203
|
-
params: [ Valtype.i32 ],
|
221
|
+
params: [ Valtype.i32, Valtype.i32 ],
|
204
222
|
returns: [ Valtype.i32 ],
|
205
|
-
returnType:
|
223
|
+
returnType: TYPES.boolean,
|
206
224
|
locals: {
|
207
225
|
basePointer: { idx: 0, type: Valtype.i32 },
|
208
226
|
iterPointer: { idx: 1, type: Valtype.i32 },
|