Skip to content

Commit d220fd6

Browse files
authored
chore(kernel): remove unnecessary v8 sandbox (#3657)
Use of the v8 sandbox had already been reduced to the point of being entirely unnecessary. Finish removing what is left of it, as it also turns out to make inline debugging a lot easier to perform. --- By submitting this pull request, I confirm that my contribution is made under the terms of the [Apache 2.0 license]. [Apache 2.0 license]: https://www.apache.org/licenses/LICENSE-2.0
1 parent ed370df commit d220fd6

File tree

2 files changed

+58
-111
lines changed

2 files changed

+58
-111
lines changed

packages/@jsii/kernel/src/kernel.test.ts

+7-11
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import * as fs from 'fs-extra';
33
import * as os from 'os';
44
import { join } from 'path';
55
import * as path from 'path';
6-
import * as vm from 'vm';
76

87
import * as api from './api';
98
import {
@@ -701,13 +700,13 @@ defineTest('async overrides: two overrides', async (sandbox) => {
701700
expect(callbacks1.callbacks.length).toBe(1);
702701

703702
sandbox.complete({ cbid: callbacks1.callbacks[0].cbid, result: 666 });
704-
await processPendingPromises(sandbox); // processing next promise
703+
await processPendingPromises(); // processing next promise
705704

706705
const callbacks2 = sandbox.callbacks();
707706
expect(callbacks2.callbacks.length).toBe(1);
708707

709708
sandbox.complete({ cbid: callbacks2.callbacks[0].cbid, result: 101 });
710-
await processPendingPromises(sandbox);
709+
await processPendingPromises();
711710

712711
const result = await sandbox.end({ promiseid: promise.promiseid });
713712
expect(result.result).toBe(775);
@@ -744,7 +743,7 @@ defineTest(
744743
// this is needed in order to cycle through another event loop so
745744
// that promises that are lazily called will be processed (nothing ensures
746745
// that the promise callback will be invokes synchronously).
747-
await processPendingPromises(sandbox);
746+
await processPendingPromises();
748747

749748
const callbacks1 = sandbox.callbacks();
750749

@@ -781,7 +780,7 @@ defineTest(
781780
sandbox.complete({ cbid: callbacks1.callbacks[0].cbid, result: 8888 });
782781

783782
// required: process pending promises so that we will get the next one in the callbacks list
784-
await processPendingPromises(sandbox);
783+
await processPendingPromises();
785784

786785
// ------ end of execution of "overrideMe"
787786

@@ -802,11 +801,8 @@ defineTest(
802801
},
803802
);
804803

805-
function processPendingPromises(sandbox: Kernel) {
806-
return vm.runInContext(
807-
'new Promise(done => setImmediate(done));',
808-
(sandbox as any).sandbox,
809-
);
804+
async function processPendingPromises() {
805+
return new Promise<void>((done) => setImmediate(done));
810806
}
811807

812808
defineTest('sync overrides', async (sandbox) => {
@@ -1347,7 +1343,7 @@ defineTest(
13471343
defineTest('node.js standard library', async (sandbox) => {
13481344
const objref = sandbox.create({ fqn: 'jsii-calc.NodeStandardLibrary' });
13491345
const promise = sandbox.begin({ objref, method: 'fsReadFile' });
1350-
await processPendingPromises(sandbox);
1346+
await processPendingPromises();
13511347

13521348
const output = await sandbox.end({ promiseid: promise.promiseid });
13531349
expect(output).toEqual({ result: 'Hello, resource!' });

packages/@jsii/kernel/src/kernel.ts

+51-100
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@ import * as spec from '@jsii/spec';
22
import { loadAssemblyFromPath } from '@jsii/spec';
33
import * as cp from 'child_process';
44
import * as fs from 'fs-extra';
5+
import { createRequire } from 'module';
56
import * as os from 'os';
67
import * as path from 'path';
78
import * as tar from 'tar';
8-
import * as vm from 'vm';
99

1010
import * as api from './api';
1111
import { TOKEN_REF } from './api';
@@ -27,8 +27,8 @@ export class Kernel {
2727
private nextid = 20000; // incrementing counter for objid, cbid, promiseid
2828
private syncInProgress?: string; // forbids async calls (begin) while processing sync calls (get/set/invoke)
2929
private installDir?: string;
30-
31-
private readonly sandbox: vm.Context;
30+
/** The internal require function, used instead of the global "require" so that webpack does not transform it... */
31+
private require?: typeof require;
3232

3333
/**
3434
* Creates a jsii kernel object.
@@ -37,27 +37,7 @@ export class Kernel {
3737
* It's responsibility is to execute the callback and return it's
3838
* result (or throw an error).
3939
*/
40-
public constructor(public callbackHandler: (callback: api.Callback) => any) {
41-
// `setImmediate` is required for tests to pass (it is otherwise
42-
// impossible to wait for in-VM promises to complete)
43-
44-
// `Buffer` is required when using simple-resource-bundler.
45-
46-
// HACK: when we webpack @jsii/runtime, all "require" statements get transpiled,
47-
// so modules can be resolved within the pack. However, here we actually want to
48-
// let loaded modules to use the native node "require" method.
49-
// I wonder if webpack has some pragma that allows opting-out at certain points
50-
// in the code.
51-
// eslint-disable-next-line @typescript-eslint/no-require-imports,@typescript-eslint/no-var-requires
52-
const moduleLoad = require('module').Module._load;
53-
const nodeRequire = (p: string) => moduleLoad(p, module, false);
54-
55-
this.sandbox = vm.createContext({
56-
Buffer, // to use simple-resource-bundler
57-
setImmediate, // async tests
58-
require: nodeRequire, // modules need to "require"
59-
});
60-
}
40+
public constructor(public callbackHandler: (callback: api.Callback) => any) {}
6141

6242
public load(req: api.LoadRequest): api.LoadResponse {
6343
this._debug('load', req);
@@ -122,11 +102,8 @@ export class Kernel {
122102
throw new Error(`Error for package tarball ${req.tarball}: ${e.message}`);
123103
}
124104

125-
// load the module and capture it's closure
126-
const closure = this._execute(
127-
`require(String.raw\`${packageDir}\`)`,
128-
packageDir,
129-
);
105+
// load the module and capture its closure
106+
const closure = this.require!(packageDir);
130107
const assm = new Assembly(assmSpec, closure);
131108
this._addAssembly(assm);
132109

@@ -205,8 +182,9 @@ export class Kernel {
205182

206183
const prototype = this._findSymbol(fqn);
207184

208-
const value = this._ensureSync(`property ${property}`, () =>
209-
this._wrapSandboxCode(() => prototype[property]),
185+
const value = this._ensureSync(
186+
`property ${property}`,
187+
() => prototype[property],
210188
);
211189

212190
this._debug('value:', value);
@@ -231,15 +209,14 @@ export class Kernel {
231209

232210
const prototype = this._findSymbol(fqn);
233211

234-
this._ensureSync(`property ${property}`, () =>
235-
this._wrapSandboxCode(
236-
() =>
237-
(prototype[property] = this._toSandbox(
238-
value,
239-
ti,
240-
`assigned to static property ${symbol}`,
241-
)),
242-
),
212+
this._ensureSync(
213+
`property ${property}`,
214+
() =>
215+
(prototype[property] = this._toSandbox(
216+
value,
217+
ti,
218+
`assigned to static property ${symbol}`,
219+
)),
243220
);
244221

245222
return {};
@@ -262,7 +239,7 @@ export class Kernel {
262239
// by jsii overrides.
263240
const value = this._ensureSync(
264241
`property '${objref[TOKEN_REF]}.${propertyToGet}'`,
265-
() => this._wrapSandboxCode(() => instance[propertyToGet]),
242+
() => instance[propertyToGet],
266243
);
267244
this._debug('value:', value);
268245
const ret = this._fromSandbox(value, ti, `of property ${fqn}.${property}`);
@@ -285,15 +262,14 @@ export class Kernel {
285262

286263
const propertyToSet = this._findPropertyTarget(instance, property);
287264

288-
this._ensureSync(`property '${objref[TOKEN_REF]}.${propertyToSet}'`, () =>
289-
this._wrapSandboxCode(
290-
() =>
291-
(instance[propertyToSet] = this._toSandbox(
292-
value,
293-
propInfo,
294-
`assigned to property ${fqn}.${property}`,
295-
)),
296-
),
265+
this._ensureSync(
266+
`property '${objref[TOKEN_REF]}.${propertyToSet}'`,
267+
() =>
268+
(instance[propertyToSet] = this._toSandbox(
269+
value,
270+
propInfo,
271+
`assigned to property ${fqn}.${property}`,
272+
)),
297273
);
298274

299275
return {};
@@ -315,14 +291,12 @@ export class Kernel {
315291
const ret = this._ensureSync(
316292
`method '${objref[TOKEN_REF]}.${method}'`,
317293
() => {
318-
return this._wrapSandboxCode(() =>
319-
fn.apply(
320-
obj,
321-
this._toSandboxValues(
322-
args,
323-
`method ${fqn ? `${fqn}#` : ''}${method}`,
324-
ti.parameters,
325-
),
294+
return fn.apply(
295+
obj,
296+
this._toSandboxValues(
297+
args,
298+
`method ${fqn ? `${fqn}#` : ''}${method}`,
299+
ti.parameters,
326300
),
327301
);
328302
},
@@ -359,14 +333,12 @@ export class Kernel {
359333
const fn = prototype[method] as (...params: any[]) => any;
360334

361335
const ret = this._ensureSync(`method '${fqn}.${method}'`, () => {
362-
return this._wrapSandboxCode(() =>
363-
fn.apply(
364-
prototype,
365-
this._toSandboxValues(
366-
args,
367-
`static method ${fqn}.${method}`,
368-
ti.parameters,
369-
),
336+
return fn.apply(
337+
prototype,
338+
this._toSandboxValues(
339+
args,
340+
`static method ${fqn}.${method}`,
341+
ti.parameters,
370342
),
371343
);
372344
});
@@ -402,14 +374,12 @@ export class Kernel {
402374

403375
const fqn = jsiiTypeFqn(obj);
404376

405-
const promise = this._wrapSandboxCode(() =>
406-
fn.apply(
407-
obj,
408-
this._toSandboxValues(
409-
args,
410-
`async method ${fqn ? `${fqn}#` : ''}${method}`,
411-
ti.parameters,
412-
),
377+
const promise = fn.apply(
378+
obj,
379+
this._toSandboxValues(
380+
args,
381+
`async method ${fqn ? `${fqn}#` : ''}${method}`,
382+
ti.parameters,
413383
),
414384
) as Promise<any>;
415385

@@ -579,6 +549,7 @@ export class Kernel {
579549
private _getPackageDir(pkgname: string): string {
580550
if (!this.installDir) {
581551
this.installDir = fs.mkdtempSync(path.join(os.tmpdir(), 'jsii-kernel-'));
552+
this.require = createRequire(this.installDir);
582553
fs.mkdirpSync(path.join(this.installDir, 'node_modules'));
583554
this._debug('creating jsii-kernel modules workdir:', this.installDir);
584555

@@ -597,15 +568,12 @@ export class Kernel {
597568

598569
const ctorResult = this._findCtor(fqn, requestArgs);
599570
const ctor = ctorResult.ctor;
600-
const obj = this._wrapSandboxCode(
601-
() =>
602-
new ctor(
603-
...this._toSandboxValues(
604-
requestArgs,
605-
`new ${fqn}`,
606-
ctorResult.parameters,
607-
),
608-
),
571+
const obj = new ctor(
572+
...this._toSandboxValues(
573+
requestArgs,
574+
`new ${fqn}`,
575+
ctorResult.parameters,
576+
),
609577
);
610578
const objref = this.objects.registerObject(obj, fqn, req.interfaces ?? []);
611579

@@ -1276,23 +1244,6 @@ export class Kernel {
12761244
private _makeprid() {
12771245
return `jsii::promise::${this.nextid++}`;
12781246
}
1279-
1280-
private _wrapSandboxCode<T>(fn: () => T): T {
1281-
return fn();
1282-
}
1283-
1284-
/**
1285-
* Executes arbitrary code in a VM sandbox.
1286-
*
1287-
* @param code JavaScript code to be executed in the VM
1288-
* @param filename the file name to use for the executed code
1289-
*
1290-
* @returns the result of evaluating the code
1291-
*/
1292-
private _execute(code: string, filename: string) {
1293-
const script = new vm.Script(code, { filename });
1294-
return script.runInContext(this.sandbox, { displayErrors: true });
1295-
}
12961247
}
12971248

12981249
interface Callback {

0 commit comments

Comments
 (0)