porffor 0.16.0-fe07da0f4 → 0.17.0-05070e1f0
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 +2 -2
- package/README.md +5 -17
- package/compiler/2c.js +146 -81
- package/compiler/allocators.js +128 -0
- package/compiler/assemble.js +12 -5
- package/compiler/builtins/array.ts +94 -5
- package/compiler/builtins/base64.ts +28 -24
- package/compiler/builtins/date.ts +3 -30
- package/compiler/builtins/math.ts +6 -2
- package/compiler/builtins/number.ts +10 -21
- package/compiler/builtins/porffor.d.ts +10 -0
- package/compiler/builtins/set.ts +9 -14
- package/compiler/builtins/string_f64.ts +10 -0
- package/compiler/builtins/typedarray.js +42 -0
- package/compiler/builtins/z_ecma262.ts +62 -0
- package/compiler/builtins.js +51 -6
- package/compiler/codegen.js +824 -409
- package/compiler/cyclone.js +535 -0
- package/compiler/decompile.js +3 -1
- package/compiler/generated_builtins.js +420 -162
- package/compiler/havoc.js +93 -0
- package/compiler/index.js +104 -7
- package/compiler/opt.js +10 -44
- package/compiler/parse.js +2 -8
- package/compiler/pgo.js +220 -0
- package/compiler/precompile.js +12 -7
- package/compiler/prefs.js +8 -4
- package/compiler/prototype.js +34 -43
- package/compiler/types.js +31 -5
- package/compiler/wasmSpec.js +4 -2
- package/compiler/wrap.js +120 -21
- package/package.json +3 -5
- package/rhemyn/README.md +7 -4
- package/rhemyn/compile.js +138 -66
- package/runner/debug.js +1 -1
- package/runner/index.js +31 -14
- package/runner/{profiler.js → profile.js} +1 -1
- package/runner/repl.js +16 -11
package/rhemyn/compile.js
CHANGED
@@ -7,18 +7,16 @@ import { TYPES } from '../compiler/types.js';
|
|
7
7
|
// local indexes
|
8
8
|
const BasePointer = 0; // base string pointer
|
9
9
|
const IterPointer = 1; // this iteration base pointer
|
10
|
-
const
|
11
|
-
const
|
12
|
-
const
|
13
|
-
const
|
14
|
-
const
|
10
|
+
const Counter = 2; // what char we are running on
|
11
|
+
const Pointer = 3; // next char pointer
|
12
|
+
const Length = 4;
|
13
|
+
const Tmp = 5;
|
14
|
+
const QuantifierTmp = 6; // the temporary variable used for quanitifers
|
15
15
|
|
16
|
-
let exprLastGet = false;
|
17
16
|
const generate = (node, negated = false, get = true, stringSize = 2, func = 'test') => {
|
18
17
|
let out = [];
|
19
18
|
switch (node.type) {
|
20
19
|
case 'Expression':
|
21
|
-
exprLastGet = false;
|
22
20
|
out = [
|
23
21
|
// set length local
|
24
22
|
[ Opcodes.local_get, BasePointer ],
|
@@ -32,49 +30,43 @@ const generate = (node, negated = false, get = true, stringSize = 2, func = 'tes
|
|
32
30
|
[ Opcodes.local_set, IterPointer ],
|
33
31
|
|
34
32
|
[ Opcodes.loop, Blocktype.void ],
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
[ Opcodes.local_tee, Counter ],
|
70
|
-
|
71
|
-
[ Opcodes.local_get, Length ],
|
72
|
-
[ Opcodes.i32_ne ],
|
73
|
-
|
74
|
-
[ Opcodes.br_if, 0 ],
|
33
|
+
// reset pointer as iter pointer
|
34
|
+
[ Opcodes.local_get, IterPointer ],
|
35
|
+
[ Opcodes.local_set, Pointer ],
|
36
|
+
|
37
|
+
[ Opcodes.block, Blocktype.void ],
|
38
|
+
// generate checks
|
39
|
+
...node.body.flatMap(x => generate(x, negated, true, stringSize, func)),
|
40
|
+
|
41
|
+
// reached end without branching out, successful match
|
42
|
+
...({
|
43
|
+
test: number(1, Valtype.i32),
|
44
|
+
search: [
|
45
|
+
[ Opcodes.local_get, Counter ]
|
46
|
+
]
|
47
|
+
})[func],
|
48
|
+
[ Opcodes.return ],
|
49
|
+
[ Opcodes.end ],
|
50
|
+
|
51
|
+
// increment iter pointer by string size
|
52
|
+
[ Opcodes.local_get, IterPointer ],
|
53
|
+
...number(stringSize, Valtype.i32),
|
54
|
+
[ Opcodes.i32_add ],
|
55
|
+
[ Opcodes.local_set, IterPointer ],
|
56
|
+
|
57
|
+
// increment counter by 1, check if eq length, if not loop
|
58
|
+
[ Opcodes.local_get, Counter ],
|
59
|
+
...number(1, Valtype.i32),
|
60
|
+
[ Opcodes.i32_add ],
|
61
|
+
[ Opcodes.local_tee, Counter ],
|
62
|
+
|
63
|
+
[ Opcodes.local_get, Length ],
|
64
|
+
[ Opcodes.i32_ne ],
|
65
|
+
|
66
|
+
[ Opcodes.br_if, 0 ],
|
75
67
|
[ Opcodes.end ],
|
76
68
|
|
77
|
-
// no match
|
69
|
+
// no match
|
78
70
|
...number(({
|
79
71
|
test: 0,
|
80
72
|
search: -1
|
@@ -110,12 +102,12 @@ const generate = (node, negated = false, get = true, stringSize = 2, func = 'tes
|
|
110
102
|
return out;
|
111
103
|
};
|
112
104
|
|
113
|
-
const getNextChar = (stringSize) => [
|
105
|
+
const getNextChar = (stringSize, peek = false) => [
|
114
106
|
// get char from pointer
|
115
107
|
[ Opcodes.local_get, Pointer ],
|
116
108
|
[ stringSize == 2 ? Opcodes.i32_load16_u : Opcodes.i32_load8_u, 0, 0 ],
|
117
109
|
|
118
|
-
...(
|
110
|
+
...(peek ? [] : [
|
119
111
|
// pointer += string size
|
120
112
|
[ Opcodes.local_get, Pointer ],
|
121
113
|
...number(stringSize, Valtype.i32),
|
@@ -134,11 +126,81 @@ const checkFailure = () => [
|
|
134
126
|
[ Opcodes.br_if, 0 ]
|
135
127
|
];
|
136
128
|
|
137
|
-
const
|
129
|
+
const wrapQuantifier = (node, method, get, stringSize) => {
|
130
|
+
const [ min, max ] = node.quantifier;
|
138
131
|
return [
|
139
|
-
|
132
|
+
// initalize our temp value (number of matched characters)
|
133
|
+
...number(0, Valtype.i32),
|
134
|
+
[Opcodes.local_set, QuantifierTmp],
|
135
|
+
|
136
|
+
// start loop
|
137
|
+
[Opcodes.loop, Blocktype.void],
|
138
|
+
[ Opcodes.block, Blocktype.void ],
|
139
|
+
// if counter + tmp == length, break
|
140
|
+
[ Opcodes.local_get, Counter ],
|
141
|
+
[ Opcodes.local_get, QuantifierTmp ],
|
142
|
+
[ Opcodes.i32_add ],
|
143
|
+
[ Opcodes.local_get, Length ],
|
144
|
+
[ Opcodes.i32_eq ],
|
145
|
+
[ Opcodes.br_if, 0 ],
|
146
|
+
|
147
|
+
// if doesn't match, break
|
148
|
+
...method,
|
149
|
+
[Opcodes.br_if, 0 ],
|
150
|
+
...(get ? [
|
151
|
+
// pointer += stringSize
|
152
|
+
[ Opcodes.local_get, Pointer ],
|
153
|
+
...number(stringSize, Valtype.i32),
|
154
|
+
[ Opcodes.i32_add ],
|
155
|
+
[ Opcodes.local_set, Pointer ]
|
156
|
+
] : []),
|
157
|
+
|
158
|
+
// if maximum was reached, break
|
159
|
+
...(max ? [
|
160
|
+
[ Opcodes.local_get, QuantifierTmp ],
|
161
|
+
...number(max, Valtype.i32),
|
162
|
+
[ Opcodes.i32_eq ],
|
163
|
+
[ Opcodes.br_if, 0 ]
|
164
|
+
] : []),
|
165
|
+
|
166
|
+
[ Opcodes.local_get, QuantifierTmp ],
|
167
|
+
...number(1, Valtype.i32),
|
168
|
+
[ Opcodes.i32_add ],
|
169
|
+
[ Opcodes.local_set, QuantifierTmp ],
|
170
|
+
[ Opcodes.br, 1 ],
|
171
|
+
[ Opcodes.end ],
|
172
|
+
[ Opcodes.end ],
|
173
|
+
|
174
|
+
// if less than minimum, fail
|
175
|
+
[Opcodes.local_get, QuantifierTmp],
|
176
|
+
...number(min, Valtype.i32),
|
177
|
+
[Opcodes.i32_lt_s],
|
178
|
+
...(get ? checkFailure(): []),
|
179
|
+
|
180
|
+
// counter += tmp - 1
|
181
|
+
[ Opcodes.local_get, QuantifierTmp ],
|
182
|
+
...number(1, Valtype.i32),
|
183
|
+
[ Opcodes.i32_sub ],
|
184
|
+
[ Opcodes.local_get, Counter ],
|
185
|
+
[ Opcodes.i32_add ],
|
186
|
+
[ Opcodes.local_set, Counter ]
|
187
|
+
];
|
188
|
+
}
|
189
|
+
|
190
|
+
const generateChar = (node, negated, get, stringSize) => {
|
191
|
+
const hasQuantifier = !!node.quantifier;
|
192
|
+
const out = [
|
193
|
+
...(get ? getNextChar(stringSize, hasQuantifier) : []),
|
140
194
|
...number(node.char.charCodeAt(0), Valtype.i32),
|
141
195
|
negated ? [ Opcodes.i32_eq ] : [ Opcodes.i32_ne ],
|
196
|
+
];
|
197
|
+
|
198
|
+
if (node.quantifier) {
|
199
|
+
return wrapQuantifier(node, out, get, stringSize);
|
200
|
+
}
|
201
|
+
|
202
|
+
return [
|
203
|
+
...out,
|
142
204
|
...(get ? checkFailure(): [])
|
143
205
|
];
|
144
206
|
};
|
@@ -146,21 +208,31 @@ const generateChar = (node, negated, get, stringSize) => {
|
|
146
208
|
const generateSet = (node, negated, get, stringSize) => {
|
147
209
|
// for a single char we do not need a tmp, it is like just
|
148
210
|
const singleChar = node.body.length === 1 && node.body[0].type === 'Character';
|
211
|
+
if (singleChar) return generateChar(node.body[0], negated, get, stringSize)
|
149
212
|
|
150
|
-
|
151
|
-
|
152
|
-
|
213
|
+
const hasQuantifier = !!node.quantifier;
|
214
|
+
|
215
|
+
const out = [
|
216
|
+
...(get ? getNextChar(stringSize, hasQuantifier) : []),
|
217
|
+
[ Opcodes.local_set, Tmp ],
|
153
218
|
];
|
154
219
|
|
155
220
|
for (const x of node.body) {
|
156
|
-
out
|
157
|
-
|
158
|
-
...(singleChar ? [] : [ [ Opcodes.local_get, Tmp ] ]),
|
221
|
+
out.push(
|
222
|
+
[ Opcodes.local_get, Tmp ],
|
159
223
|
...generate(x, negated, false, stringSize)
|
160
|
-
|
224
|
+
);
|
161
225
|
}
|
162
226
|
|
163
|
-
if (node.body.length > 0)
|
227
|
+
if (node.body.length > 0) {
|
228
|
+
for (let i = 0; i < node.body.length - 1; i++) {
|
229
|
+
out.push(negated ? [ Opcodes.i32_or ] : [ Opcodes.i32_and ])
|
230
|
+
}
|
231
|
+
};
|
232
|
+
|
233
|
+
if (hasQuantifier) {
|
234
|
+
return wrapQuantifier(node, out, get, stringSize);
|
235
|
+
}
|
164
236
|
|
165
237
|
return [
|
166
238
|
...out,
|
@@ -196,7 +268,7 @@ const wrapFunc = (regex, func, name, index) => {
|
|
196
268
|
const parsed = parse(regex);
|
197
269
|
|
198
270
|
return outputFunc([
|
199
|
-
[ Opcodes.local_get,
|
271
|
+
[ Opcodes.local_get, IterPointer ],
|
200
272
|
...number(TYPES.string, Valtype.i32),
|
201
273
|
[ Opcodes.i32_eq ],
|
202
274
|
[ Opcodes.if, Valtype.i32 ],
|
@@ -224,10 +296,10 @@ const outputFunc = (wasm, name, index) => ({
|
|
224
296
|
locals: {
|
225
297
|
basePointer: { idx: 0, type: Valtype.i32 },
|
226
298
|
iterPointer: { idx: 1, type: Valtype.i32 },
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
299
|
+
counter: { idx: 2, type: Valtype.i32 },
|
300
|
+
pointer: { idx: 3, type: Valtype.i32 },
|
301
|
+
length: { idx: 4, type: Valtype.i32 },
|
302
|
+
tmp: { idx: 5, type: Valtype.i32 },
|
303
|
+
quantifierTmp: { idx: 6, type: Valtype.i32 },
|
232
304
|
}
|
233
305
|
});
|
package/runner/debug.js
CHANGED
@@ -43,7 +43,7 @@ let lastLine;
|
|
43
43
|
let output = '';
|
44
44
|
|
45
45
|
try {
|
46
|
-
const { exports } =
|
46
|
+
const { exports } = compile(source, process.argv.includes('--module') ? [ 'module' ] : [], {
|
47
47
|
y: n => {
|
48
48
|
if (callStarts[callStarts.length - 1] === n - 1) {
|
49
49
|
// end of call
|
package/runner/index.js
CHANGED
@@ -1,8 +1,11 @@
|
|
1
1
|
#!/usr/bin/env node
|
2
|
-
|
3
|
-
import compile from '../compiler/wrap.js';
|
4
2
|
import fs from 'node:fs';
|
5
3
|
|
4
|
+
// deno compat
|
5
|
+
if (typeof process === 'undefined' && typeof Deno !== 'undefined') {
|
6
|
+
globalThis.process = await import('node:process');
|
7
|
+
}
|
8
|
+
|
6
9
|
const start = performance.now();
|
7
10
|
|
8
11
|
if (process.argv.includes('--compile-hints')) {
|
@@ -59,12 +62,22 @@ if (process.argv.includes('--help')) {
|
|
59
62
|
}
|
60
63
|
|
61
64
|
let file = process.argv.slice(2).find(x => x[0] !== '-');
|
62
|
-
if (['run', 'wasm', 'native', 'c', 'profile', 'debug', 'debug-wasm'].includes(file)) {
|
65
|
+
if (['precompile', 'run', 'wasm', 'native', 'c', 'profile', 'debug', 'debug-wasm'].includes(file)) {
|
63
66
|
// remove this arg
|
64
67
|
process.argv.splice(process.argv.indexOf(file), 1);
|
65
68
|
|
69
|
+
if (file === 'precompile') {
|
70
|
+
await import('../compiler/precompile.js');
|
71
|
+
await new Promise(() => {}); // do nothing for the rest of this file
|
72
|
+
}
|
73
|
+
|
66
74
|
if (file === 'profile') {
|
67
|
-
await import('./
|
75
|
+
await import('./profile.js');
|
76
|
+
await new Promise(() => {}); // do nothing for the rest of this file
|
77
|
+
}
|
78
|
+
|
79
|
+
if (file === 'debug') {
|
80
|
+
await import('./debug.js');
|
68
81
|
await new Promise(() => {}); // do nothing for the rest of this file
|
69
82
|
}
|
70
83
|
|
@@ -76,11 +89,6 @@ if (['run', 'wasm', 'native', 'c', 'profile', 'debug', 'debug-wasm'].includes(fi
|
|
76
89
|
process.argv.push('--asur', '--wasm-debug');
|
77
90
|
}
|
78
91
|
|
79
|
-
if (file === 'debug') {
|
80
|
-
await import('./debug.js');
|
81
|
-
await new Promise(() => {}); // do nothing for the rest of this file
|
82
|
-
}
|
83
|
-
|
84
92
|
file = process.argv.slice(2).find(x => x[0] !== '-');
|
85
93
|
|
86
94
|
const nonOptOutFile = process.argv.slice(process.argv.indexOf(file) + 1).find(x => x[0] !== '-');
|
@@ -89,6 +97,8 @@ if (['run', 'wasm', 'native', 'c', 'profile', 'debug', 'debug-wasm'].includes(fi
|
|
89
97
|
}
|
90
98
|
}
|
91
99
|
|
100
|
+
globalThis.file = file;
|
101
|
+
|
92
102
|
if (!file) {
|
93
103
|
if (process.argv.includes('-v') || process.argv.includes('--version')) {
|
94
104
|
// just print version
|
@@ -103,6 +113,8 @@ if (!file) {
|
|
103
113
|
|
104
114
|
const source = fs.readFileSync(file, 'utf8');
|
105
115
|
|
116
|
+
const compile = (await import('../compiler/wrap.js')).default;
|
117
|
+
|
106
118
|
let cache = '';
|
107
119
|
const print = str => {
|
108
120
|
/* cache += str;
|
@@ -115,22 +127,27 @@ const print = str => {
|
|
115
127
|
process.stdout.write(str);
|
116
128
|
};
|
117
129
|
|
130
|
+
let runStart;
|
118
131
|
try {
|
119
132
|
if (process.argv.includes('-b')) {
|
120
|
-
const { wasm, exports } =
|
133
|
+
const { wasm, exports } = compile(source, process.argv.includes('--module') ? [ 'module' ] : [], {}, print);
|
121
134
|
|
135
|
+
runStart = performance.now();
|
122
136
|
if (!process.argv.includes('--no-run')) exports.main();
|
123
137
|
|
124
138
|
console.log(`\n\nwasm size: ${wasm.byteLength} bytes`);
|
125
139
|
} else {
|
126
|
-
const { exports } =
|
140
|
+
const { exports } = compile(source, process.argv.includes('--module') ? [ 'module' ] : [], {}, print);
|
127
141
|
|
128
|
-
|
142
|
+
runStart = performance.now();
|
143
|
+
if (!process.argv.includes('--no-run')) exports.main();
|
129
144
|
}
|
130
145
|
// if (cache) process.stdout.write(cache);
|
131
146
|
} catch (e) {
|
132
147
|
// if (cache) process.stdout.write(cache);
|
133
|
-
|
148
|
+
let out = e;
|
149
|
+
if (!process.argv.includes('-i') && Object.getPrototypeOf(e).message != null) out = `${e.constructor.name}${e.message != null ? `: ${e.message}` : ''}`;
|
150
|
+
console.error(out);
|
134
151
|
}
|
135
152
|
|
136
|
-
if (process.argv.includes('-t')) console.log(`${process.argv.includes('-b') ? '' : '\n\n'}total time: ${(performance.now() - start).toFixed(2)}ms`);
|
153
|
+
if (process.argv.includes('-t')) console.log(`${process.argv.includes('-b') ? '' : '\n\n'}total time: ${(performance.now() - start).toFixed(2)}ms\nexecution time: ${(performance.now() - runStart).toFixed(2)}ms`);
|
@@ -20,7 +20,7 @@ let spin = 0;
|
|
20
20
|
let last = 0;
|
21
21
|
|
22
22
|
try {
|
23
|
-
const { exports } =
|
23
|
+
const { exports } = compile(source, process.argv.includes('--module') ? [ 'module' ] : [], {
|
24
24
|
y: n => {
|
25
25
|
tmp[n] = performance.now();
|
26
26
|
},
|
package/runner/repl.js
CHANGED
@@ -40,10 +40,9 @@ let lastMemory, lastPages;
|
|
40
40
|
const PageSize = 65536;
|
41
41
|
const memoryToString = mem => {
|
42
42
|
let out = '';
|
43
|
-
const pages = lastPages.length;
|
44
43
|
const wasmPages = mem.buffer.byteLength / PageSize;
|
45
44
|
|
46
|
-
out += `\x1B[1mallocated ${mem.buffer.byteLength / 1024}
|
45
|
+
out += `\x1B[1mallocated ${mem.buffer.byteLength / 1024}KiB\x1B[0m (using ${wasmPages} Wasm page${wasmPages === 1 ? '' : 's'})\n\n`;
|
47
46
|
|
48
47
|
const buf = new Uint8Array(mem.buffer);
|
49
48
|
|
@@ -55,10 +54,16 @@ const memoryToString = mem => {
|
|
55
54
|
}
|
56
55
|
|
57
56
|
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`; // ─
|
58
|
-
for (let i = 0; i <
|
59
|
-
|
60
|
-
|
61
|
-
|
57
|
+
for (let i = 0; i < wasmPages; i++) {
|
58
|
+
if (lastPages[i]) {
|
59
|
+
const [ type, name ] = lastPages[i].split(': ');
|
60
|
+
// out += `\x1B[36m${lastPages[i].replace(':', '\x1B[90m:\x1B[34m')}\x1B[90m${' '.repeat(longestName - lastPages[i].length)} | \x1B[0m`;
|
61
|
+
out += ` \x1B[34m${name}${' '.repeat(longestName - name.length)} \x1B[90m│\x1B[0m \x1B[36m${type}${' '.repeat(longestType - type.length)} \x1B[90m│\x1B[0m `;
|
62
|
+
} else {
|
63
|
+
const type = '???';
|
64
|
+
const name = '???';
|
65
|
+
out += ` \x1B[34m${name}${' '.repeat(longestName - name.length)} \x1B[90m│\x1B[0m \x1B[36m${type}${' '.repeat(longestType - type.length)} \x1B[90m│\x1B[0m `;
|
66
|
+
}
|
62
67
|
|
63
68
|
for (let j = 0; j < 40; j++) {
|
64
69
|
const val = buf[i * pageSize + j];
|
@@ -75,13 +80,13 @@ const memoryToString = mem => {
|
|
75
80
|
};
|
76
81
|
|
77
82
|
let prev = '';
|
78
|
-
const run =
|
83
|
+
const run = (source, _context, _filename, callback, run = true) => {
|
79
84
|
// hack: print "secret" before latest code ran to only enable printing for new code
|
80
85
|
|
81
86
|
let toRun = (prev ? (prev + `;\nprint(-0x1337);\n`) : '') + source.trim();
|
82
87
|
|
83
88
|
let shouldPrint = !prev;
|
84
|
-
const { exports, pages } =
|
89
|
+
const { exports, pages } = compile(toRun, [], {}, str => {
|
85
90
|
if (shouldPrint) process.stdout.write(str);
|
86
91
|
if (str === '-4919') shouldPrint = true;
|
87
92
|
});
|
@@ -122,12 +127,12 @@ replServer.defineCommand('memory', {
|
|
122
127
|
});
|
123
128
|
replServer.defineCommand('asm', {
|
124
129
|
help: 'Log Wasm decompiled bytecode',
|
125
|
-
|
130
|
+
action() {
|
126
131
|
this.clearBufferedCommand();
|
127
132
|
|
128
133
|
try {
|
129
134
|
process.argv.push('--opt-funcs');
|
130
|
-
|
135
|
+
run('', null, null, () => {}, false);
|
131
136
|
process.argv.pop();
|
132
137
|
} catch { }
|
133
138
|
|
@@ -136,7 +141,7 @@ replServer.defineCommand('asm', {
|
|
136
141
|
});
|
137
142
|
replServer.defineCommand('js', {
|
138
143
|
help: 'Log JS being actually ran',
|
139
|
-
|
144
|
+
action() {
|
140
145
|
this.clearBufferedCommand();
|
141
146
|
console.log(prev);
|
142
147
|
this.displayPrompt();
|