-
Notifications
You must be signed in to change notification settings - Fork 1
/
index.ts
148 lines (135 loc) · 4.23 KB
/
index.ts
1
2
3
4
5
6
7
8
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
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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
import ivm from 'isolated-vm';
import binding from './binding';
type Binding = typeof binding;
const nativeModule = new ivm.NativeModule(require.resolve(binding.path));
const kInspect = 'internal/util/inspect';
const kPrimordials = 'internal/per_context/primordials';
const kFiles = [
kPrimordials,
kInspect,
'internal/errors',
'internal/util',
'internal/util/types',
];
export async function create(isolate: ivm.Isolate, context: ivm.Context) {
const [ modules, binding, primordialsHolder ] = await Promise.all([
// Compile modules
async function() {
return new Map(await Promise.all(kFiles.map(async module => {
const src: string = require(`../nodejs/lib/${module}`);
const ivmSrc = `(function(global,primordials,internalBinding,process,module,require){${src}})`;
const script = await isolate.compileScript(ivmSrc, { filename: `${module}.js` });
const reference = await script.run(context, { release: true, reference: true });
const entry: [ string, ivm.Reference ] = [ module, reference ];
return entry;
})));
}(),
// Binding
async function() {
const moduleInstance: ivm.Reference<Binding> = await nativeModule.create(context);
const makeBinding = await moduleInstance.get('makeBinding', { reference: true });
const binding = await makeBinding.apply(null, [], { result: { reference: true } });
moduleInstance.release();
return binding;
}(),
// Make primordials holder
context.eval('Object.create(null)', { reference: true }),
]);
// Run primordials.js
const { global } = context;
const primordials = primordialsHolder;
await modules.get(kPrimordials)!.apply(undefined, [ global.derefInto(), primordials.derefInto() ]);
// Continue with bootstrap
const result = await context.evalClosure(`
const process = {
versions: {},
};
const internalBinding = name => $3[name];
const cache = new Map;
function require(name) {
const cached = cache.get(name);
if (cached !== undefined) {
return cached.exports;
}
const module = { exports: {} };
cache.set(name, module);
const fn = $0.get(name);
if (fn) {
fn.deref()($1, $2, internalBinding, process, module, require);
}
return module.exports;
}
return require("${kInspect}");
`, [
new ivm.ExternalCopy(modules).copyInto(),
global.derefInto(),
primordials.derefInto({ release: true }),
binding.derefInto({ release: true }),
], { result: { reference: true } });
const util: ivm.Reference<typeof import('util')> = result;
// Export functions
const [ formatWithOptions, inspect ] = await Promise.all([
util.get('formatWithOptions', { reference: true }),
util.get('inspect', { reference: true }),
]);
// Release remaining handles
modules.forEach(module => module.release());
util.release();
return { formatWithOptions, inspect };
}
export async function forwardConsole(
context: ivm.Context,
util: ReturnType<typeof create> extends Promise<infer T> ? T : never
) {
const print = (fd: number, payload: string) => {
const stream = fd === 2 ? process.stderr : process.stdout;
stream.write(payload + '\n');
};
context.evalClosureSync(`
const [ formatWithOptions, inspect ] = [ $0, $1 ];
const kTraceBegin = 'b'.charCodeAt(0);
const kTraceEnd = 'e'.charCodeAt(0);
const kTraceInstant = 'n'.charCodeAt(0);
const times = new Map;
const write = (fd, payload) => $2.applySync(undefined, [ fd, payload ]);
const format = args => formatWithOptions({ colors: true }, ...args);
Object.assign(console, {
log(...args) {
write(1, format(args));
},
warn(...args) {
write(2, format(args));
},
error(...args) {
this.warn(...args);
},
dir(object, options) {
write(1, inspect(object, {
customInspect: false,
color: true,
...options,
}));
},
trace: function trace(...args) {
const err = {
name: 'Trace',
message: format(args),
};
Error.captureStackTrace(err, trace);
this.error(err.stack);
},
assert(expression, ...args) {
if (!expression) {
args[0] = \`Assertion failed\${args.length === 0 ? '' : \`: \${args[0]}\`}\`;
this.warn(...args); // The arguments will be formatted in warn() again
}
},
});
`,
[
util.formatWithOptions.derefInto(),
util.inspect.derefInto(),
new ivm.Reference(print),
],
);
}