pulumi/sdk/nodejs/runtime/closure/v8Hooks.ts
CyrusNajmabadi 901a238fd5
Get closure serialiation working in Node11 (#2101)
* Make v8 primitives async as there is no way to avoid async in node11.

* Simplify API.

* Move processing of well-known globals into the v8 layer.
We'll need this so that we can map from RemoteObjectIds back to these well known values.

* Remove unnecesssary check.

* Cleanup comments and extract helper.

* Introduce helper bridge method for the simple case of making an entry for a string.

* Make functions async.  They'll need to be async once we move to the Inspector api.

* Make functions async.  They'll need to be async once we move to the Inspector api.

* Make functions async.  They'll need to be async once we move to the Inspector api.

* Move property access behind helpers so they can move to the Inspector API in the future.

* Only call function when we know we have a Function.  Remove redundant null check.

* Properly serialize certain special JavaScript number values that JSON serialization cannot handle.

* Only marshall across the 'source' and 'flags' for a RegExp when serializing.

* Add a simple test to validate a regex without flags.

* Extract functionality into helper method.

* Add test with complex output scenarios.

* Output serialization needs to avoid recursively trying to serialize a serialized value.

* Introduce indirection for introspecting properties of an object.

* Use our own introspection API for examining an Array.

* Hide direct property access through API indirection.

* Produce values like the v8 Inspector does.

* Compute the module map asynchronously.  Will need that when mapping mirrors instead.

* Cleanup a little code in closure creation.

* Get serialization working on Node11 (except function locations).

* Run tests in the same order on <v11 and >=v11

* Make tests run on multiple versions of node.

* Rename file to make PR simpler to review.

* Cleanup.

* Be more careful with global state.

* Remove commented line.

* Only allow getting a session when on Node11 or above.

* Promisify methods.
2018-11-01 15:46:21 -07:00

82 lines
3.1 KiB
TypeScript

// Copyright 2016-2018, Pulumi Corporation.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Module that hooks into v8 and provides information about it to interested parties. Because this
// hooks into v8 events it is critical that this module is loaded early when the process starts.
// Otherwise, information may not be known when needed. This module is only intended for use on
// Node v11 and higher.
import * as v8 from "v8";
v8.setFlagsFromString("--allow-natives-syntax");
import * as semver from "semver";
// On node11 and above, create an 'inspector session' that can be used to keep track of what is
// happening through a supported API. Pre-11 we can just call into % intrinsics for the same data.
export const isNodeAtLeastV11 = semver.gte(process.version, "11.0.0");
const session = isNodeAtLeastV11
? createInspectorSessionAsync()
: Promise.resolve<import("inspector").Session>(<any>undefined);
const scriptIdToUrlMap = new Map<string, string>();
async function createInspectorSessionAsync(): Promise<import("inspector").Session> {
// Delay loading 'inspector' as it is not available on early versions of node, so we can't
// require it on the outside.
const inspector = await import("inspector");
const inspectorSession = new inspector.Session();
inspectorSession.connect();
// Enable debugging support so we can hear about the Debugger.scriptParsed event. We need that
// event to know how to map from scriptId's to file-urls.
await new Promise<import("inspector").Debugger.EnableReturnType>((resolve, reject) => {
inspectorSession.post("Debugger.enable", (err, res) => err ? reject(err) : resolve(res));
});
inspectorSession.addListener("Debugger.scriptParsed", event => {
const { scriptId, url } = event.params;
scriptIdToUrlMap.set(scriptId, url);
});
return inspectorSession;
}
/**
* Returns the inspector session that can be used to query the state of this running Node instance.
* Must only be called on Node11 and above. On Node10 and below, this will throw.
*/
export async function getSessionAsync() {
if (!isNodeAtLeastV11) {
throw new Error("Should not call getSessionAsync unless on Node11 or above.");
}
return session;
}
/**
* Returns a promise that can be used to determine when the v8hooks have been injected properly and
* code that depends on them can continue executing.
*/
export async function isInitializedAsync() {
await session;
}
/**
* Maps from a script-id to the local file url it corresponds to.
*/
export function getScriptUrl(id: import("inspector").Runtime.ScriptId) {
return scriptIdToUrlMap.get(id);
}