mirror of
https://github.com/facebookarchive/prepack.git
synced 2024-09-11 14:46:37 +03:00
Debugger sourcemaps (#2154)
Summary: Release Notes: - Debugger recognizes sourcemaps and opens the correct file/line when debugging in Nuclide. - Explanation of how sourcemap translation works in [this Quip](https://fb.quip.com/0uQCAtkuPAHF). This is probably useful to read before attempting to read the changes to `Debugger.js` and `PathNormalizer.js`. - Removed unnecessary abstractions in `IsStatement` and `DebuggerOptions`. - Refactored multi-file support to conform to Nuclide initialization protocol - Refactored `DebuggerConfigArguments` so as to pass sourceFiles without increase Flow cycle length. Example of FatalError on FB4A: https://www.dropbox.com/s/ig8vrlpcazbygo3/Prepack%20Fatal%20Diagnostic%20on%20FB4A.mov?dl=0 NOTE: Apologies for the large number of commits -- I incorrectly pulled in some from master when rebasing Closes https://github.com/facebook/prepack/pull/2154 Differential Revision: D8707082 Pulled By: caiismyname fbshipit-source-id: 812610c0345536a1ad9b480449c16f34018040c3
This commit is contained in:
parent
3240eae91b
commit
e79def7cd2
@ -20,22 +20,24 @@ import * as partialEvaluators from "./partial-evaluators/index.js";
|
||||
import { Environment } from "./singletons.js";
|
||||
import { ObjectValue } from "./values/index.js";
|
||||
import { DebugServer } from "./debugger/server/Debugger.js";
|
||||
import type { DebugChannel } from "./debugger/server/channel/DebugChannel.js";
|
||||
import simplifyAndRefineAbstractValue from "./utils/simplifier.js";
|
||||
import invariant from "./invariant.js";
|
||||
import type { DebuggerConfigArguments } from "./types";
|
||||
|
||||
export default function(
|
||||
opts: RealmOptions = {},
|
||||
debugChannel: void | DebugChannel = undefined,
|
||||
debuggerConfigArgs: void | DebuggerConfigArguments,
|
||||
statistics: void | RealmStatistics = undefined
|
||||
): Realm {
|
||||
initializeSingletons();
|
||||
let r = new Realm(opts, statistics || new RealmStatistics());
|
||||
// Presence of debugChannel indicates we wish to use debugger.
|
||||
if (debugChannel) {
|
||||
invariant(debugChannel.debuggerIsAttached(), "Debugger intends to be used but is not attached.");
|
||||
invariant(opts.debuggerConfigArgs !== undefined, "Debugger intends to be used but does not have config arguments.");
|
||||
r.debuggerInstance = new DebugServer(debugChannel, r, opts.debuggerConfigArgs);
|
||||
if (debuggerConfigArgs) {
|
||||
let debugChannel = debuggerConfigArgs.debugChannel;
|
||||
if (debugChannel) {
|
||||
invariant(debugChannel.debuggerIsAttached(), "Debugger intends to be used but is not attached.");
|
||||
r.debuggerInstance = new DebugServer(debugChannel, r, debuggerConfigArgs);
|
||||
}
|
||||
}
|
||||
|
||||
let i = r.intrinsics;
|
||||
|
@ -114,9 +114,10 @@ class PrepackDebugSession extends DebugSession {
|
||||
let adapterChannel = new AdapterChannel(inFilePath, outFilePath);
|
||||
this._adapterChannel = adapterChannel;
|
||||
this._registerMessageCallbacks();
|
||||
let separatedSourceFiles = args.sourceFile.trim().split(/\s+/g); // Split on all whitespace
|
||||
let launchArgs: PrepackLaunchArguments = {
|
||||
kind: "launch",
|
||||
sourceFiles: args.sourceFiles,
|
||||
sourceFiles: separatedSourceFiles,
|
||||
prepackRuntime: args.prepackRuntime,
|
||||
prepackArguments: args.prepackArguments,
|
||||
debugInFilePath: inFilePath,
|
||||
|
@ -99,7 +99,10 @@ export class AdapterChannel {
|
||||
if (args.prepackRuntime.length > 0) {
|
||||
// user specified a Prepack path
|
||||
runtime = "node";
|
||||
prepackCommand = [args.prepackRuntime].concat(prepackCommand);
|
||||
// Increase node's memory allowance so Prepack can handle large inputs
|
||||
prepackCommand = ["--max_old_space_size=8192", "--stack_size=10000"]
|
||||
.concat([args.prepackRuntime])
|
||||
.concat(prepackCommand);
|
||||
}
|
||||
this._prepackProcess = child_process.spawn(runtime, prepackCommand);
|
||||
|
||||
|
@ -10,7 +10,7 @@
|
||||
/* @flow strict */
|
||||
|
||||
// More error types will be added as needed
|
||||
export type DebuggerErrorType = "Invalid command" | "Invalid response" | "Startup Error";
|
||||
export type DebuggerErrorType = "Invalid command" | "Invalid response" | "Startup Error" | "Invalid input";
|
||||
|
||||
export class DebuggerError extends Error {
|
||||
constructor(errorType: DebuggerErrorType, message: string) {
|
||||
|
@ -10,7 +10,6 @@
|
||||
/* @flow strict */
|
||||
|
||||
import * as DebugProtocol from "vscode-debugprotocol";
|
||||
import type { Severity } from "../../errors.js";
|
||||
|
||||
export type DebuggerRequest = {
|
||||
id: number,
|
||||
@ -40,10 +39,6 @@ export type PrepackLaunchArguments = {
|
||||
exitCallback: () => void,
|
||||
};
|
||||
|
||||
export type DebuggerConfigArguments = {
|
||||
diagnosticSeverity?: Severity,
|
||||
};
|
||||
|
||||
export type Breakpoint = {
|
||||
filePath: string,
|
||||
line: number,
|
||||
@ -167,7 +162,7 @@ export type EvaluateResult = {
|
||||
export type LaunchRequestArguments = {
|
||||
...DebugProtocol.LaunchRequestArguments,
|
||||
noDebug?: boolean,
|
||||
sourceFiles: Array<string>,
|
||||
sourceFile: string,
|
||||
prepackRuntime: string,
|
||||
prepackArguments: Array<string>,
|
||||
};
|
||||
|
@ -157,7 +157,7 @@ export class UISession {
|
||||
_processInitializeResponse(response: DebugProtocol.InitializeResponse) {
|
||||
let launchArgs: LaunchRequestArguments = {
|
||||
prepackRuntime: this._prepackRuntime,
|
||||
sourceFiles: this._sourceFiles,
|
||||
sourceFile: this._sourceFiles.join(" "),
|
||||
prepackArguments: this._prepackArguments,
|
||||
};
|
||||
this._sendLaunchRequest(launchArgs);
|
||||
|
@ -13,7 +13,6 @@ import { PerFileBreakpointMap } from "./PerFileBreakpointMap.js";
|
||||
import { Breakpoint } from "./Breakpoint.js";
|
||||
import type { Breakpoint as BreakpointType } from "./../common/types.js";
|
||||
import { BabelNode } from "babel-types";
|
||||
import { IsStatement } from "./../../methods/is.js";
|
||||
|
||||
// Storing BreakpointStores for all source files
|
||||
export class BreakpointManager {
|
||||
@ -23,7 +22,6 @@ export class BreakpointManager {
|
||||
_breakpointMaps: Map<string, PerFileBreakpointMap>;
|
||||
|
||||
getStoppableBreakpoint(ast: BabelNode): void | Breakpoint {
|
||||
if (!IsStatement(ast)) return;
|
||||
if (ast.loc && ast.loc.source) {
|
||||
let location = ast.loc;
|
||||
let filePath = location.source;
|
||||
|
@ -25,7 +25,6 @@ import type {
|
||||
VariablesArguments,
|
||||
EvaluateArguments,
|
||||
SourceData,
|
||||
DebuggerConfigArguments,
|
||||
} from "./../common/types.js";
|
||||
import type { Realm } from "./../../realm.js";
|
||||
import { ExecutionContext } from "./../../realm.js";
|
||||
@ -42,6 +41,8 @@ import {
|
||||
} from "./../../environment.js";
|
||||
import { CompilerDiagnostic } from "../../errors.js";
|
||||
import type { Severity } from "../../errors.js";
|
||||
import { SourceMapManager } from "./SourceMapManager.js";
|
||||
import type { DebuggerConfigArguments } from "../../types";
|
||||
|
||||
export class DebugServer {
|
||||
constructor(channel: DebugChannel, realm: Realm, configArgs: DebuggerConfigArguments) {
|
||||
@ -52,6 +53,7 @@ export class DebugServer {
|
||||
this._stepManager = new SteppingManager(this._realm, /* default discard old steppers */ false);
|
||||
this._stopEventManager = new StopEventManager();
|
||||
this._diagnosticSeverity = configArgs.diagnosticSeverity || "FatalError";
|
||||
this._sourceMapManager = new SourceMapManager(configArgs.buckRoot, configArgs.sourcemaps);
|
||||
this.waitForRun(undefined);
|
||||
}
|
||||
// the collection of breakpoints
|
||||
@ -65,6 +67,7 @@ export class DebugServer {
|
||||
_lastExecuted: SourceData;
|
||||
// Severity at which debugger will break when CompilerDiagnostics are generated. Default is Fatal.
|
||||
_diagnosticSeverity: Severity;
|
||||
_sourceMapManager: SourceMapManager;
|
||||
|
||||
/* Block until adapter says to run
|
||||
/* ast: the current ast node we are stopped on
|
||||
@ -87,10 +90,11 @@ export class DebugServer {
|
||||
if (breakpoint) stoppables.push(breakpoint);
|
||||
let reason = this._stopEventManager.getDebuggeeStopReason(ast, stoppables);
|
||||
if (reason) {
|
||||
invariant(ast.loc && ast.loc.source);
|
||||
this._channel.sendStoppedResponse(reason, ast.loc.source, ast.loc.start.line, ast.loc.start.column);
|
||||
invariant(ast.loc && ast.loc !== null);
|
||||
this.waitForRun(ast.loc);
|
||||
let location = ast.loc;
|
||||
invariant(location && location.source);
|
||||
let absolutePath = this._sourceMapManager.relativeToAbsolute(location.source);
|
||||
this._channel.sendStoppedResponse(reason, absolutePath, location.start.line, location.start.column);
|
||||
this.waitForRun(location);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -101,6 +105,13 @@ export class DebugServer {
|
||||
let requestID = request.id;
|
||||
let command = request.command;
|
||||
let args = request.arguments;
|
||||
// Convert incoming location sources to relative paths in order to match internal representation of filenames.
|
||||
if (args.kind === "breakpoint") {
|
||||
for (let bp of args.breakpoints) {
|
||||
bp.filePath = this._sourceMapManager.absoluteToRelative(bp.filePath);
|
||||
}
|
||||
}
|
||||
|
||||
switch (command) {
|
||||
case DebugMessage.BREAKPOINT_ADD_COMMAND:
|
||||
invariant(args.kind === "breakpoint");
|
||||
@ -182,7 +193,7 @@ export class DebugServer {
|
||||
let frameInfo: Stackframe = {
|
||||
id: this._realm.contextStack.length - 1 - i,
|
||||
functionName: functionName,
|
||||
fileName: fileName,
|
||||
fileName: this._sourceMapManager.relativeToAbsolute(fileName), // Outward facing paths must be absolute.
|
||||
line: line,
|
||||
column: column,
|
||||
};
|
||||
@ -278,12 +289,16 @@ export class DebugServer {
|
||||
let line = ast.loc.start.line;
|
||||
let column = ast.loc.start.column;
|
||||
let stackSize = this._realm.contextStack.length;
|
||||
// check if the current location is same as the last one
|
||||
// Check if the current location is same as the last one.
|
||||
// Does not check columns since column debugging is not supported.
|
||||
// Column support is unnecessary because these nodes will have been sourcemap-translated.
|
||||
// Ignoring columns prevents:
|
||||
// - Lines with multiple AST nodes from triggering the same breakpoint more than once.
|
||||
// - Step-out from completing in the same line that it was set in.
|
||||
if (
|
||||
this._lastExecuted &&
|
||||
filePath === this._lastExecuted.filePath &&
|
||||
line === this._lastExecuted.line &&
|
||||
column === this._lastExecuted.column &&
|
||||
stackSize === this._lastExecuted.stackSize
|
||||
) {
|
||||
return false;
|
||||
@ -299,18 +314,18 @@ export class DebugServer {
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
Displays PP error message, then waits for user to run the program to
|
||||
continue (similar to a breakpoint).
|
||||
*/
|
||||
// Displays Prepack error message, then waits for user to run the program to continue (similar to a breakpoint).
|
||||
handlePrepackError(diagnostic: CompilerDiagnostic) {
|
||||
invariant(diagnostic.location && diagnostic.location.source);
|
||||
// The following constructs the message and stop-instruction that is sent to the UI to actually stop the execution.
|
||||
let location = diagnostic.location;
|
||||
let absoluteSource = "";
|
||||
if (location.source !== null) absoluteSource = this._sourceMapManager.relativeToAbsolute(location.source);
|
||||
let message = `${diagnostic.severity} ${diagnostic.errorCode}: ${diagnostic.message}`;
|
||||
console.log(message);
|
||||
this._channel.sendStoppedResponse(
|
||||
"Diagnostic",
|
||||
location.source || "",
|
||||
absoluteSource,
|
||||
location.start.line,
|
||||
location.start.column,
|
||||
message
|
||||
|
227
src/debugger/server/SourceMapManager.js
Normal file
227
src/debugger/server/SourceMapManager.js
Normal file
@ -0,0 +1,227 @@
|
||||
/**
|
||||
* Copyright (c) 2017-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
/* @flow strict-local */
|
||||
|
||||
import type { SourceFile } from "./../../types.js";
|
||||
import { DebuggerError } from "./../common/DebuggerError.js";
|
||||
import invariant from "../common/invariant.js";
|
||||
|
||||
/**
|
||||
* Sourcemap paths can come in one of two formats:
|
||||
* - Relative: The paths include `../` and can be followed from the sourcemap's location
|
||||
* to arrive at the original source's location. In this format, path conversion
|
||||
* requires two different prefixes (MapDifference and CommonPrefix) that must be
|
||||
* discovered from the input paths.
|
||||
* - Common Directory: The paths take the format of an absolute path (`/foo/bar`) and
|
||||
* assume there is a common prefix to the path that, when added, will make the path an
|
||||
* valid absolute path. This prefix is passed in as the `buckRoot` argument.
|
||||
* Example:
|
||||
* In a directory structure with /A/B/map.js and /A/C/original.js,
|
||||
* the sourcemaps would have the following path structures:
|
||||
* - Relative: ../C/original.js, with `CP` = /A and 'MD' = ../
|
||||
* - Common Directory: /C/original.js, with `buckRoot` = /A
|
||||
*/
|
||||
export class SourceMapManager {
|
||||
constructor(buckRoot?: string, sourceMaps?: Array<SourceFile>) {
|
||||
// Use presence of buck root argument to indicate which path format sourcemap prefixes take on.
|
||||
if (buckRoot !== undefined) {
|
||||
if (sourceMaps === undefined) {
|
||||
throw new DebuggerError(
|
||||
"Invalid input",
|
||||
"Can't provide a sourcemap directory root without having sourcemaps present"
|
||||
);
|
||||
}
|
||||
this._buckRoot = buckRoot;
|
||||
if (this._buckRoot[this._buckRoot.length - 1] === "/") {
|
||||
// Remove trailing slash to prepare for prepending to internal paths.
|
||||
this._buckRoot = this._buckRoot.slice(0, -1);
|
||||
}
|
||||
} else {
|
||||
// If sourcemaps don't exist, set prefixes to undefined and break.
|
||||
if (sourceMaps) {
|
||||
for (let map of sourceMaps) {
|
||||
if (map.sourceMapContents === undefined || map.sourceMapContents === "") {
|
||||
this._sourcemapCommonPrefix = undefined;
|
||||
this._sourcemapMapDifference = undefined;
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this._sourcemapCommonPrefix = undefined;
|
||||
this._sourcemapMapDifference = undefined;
|
||||
return;
|
||||
}
|
||||
|
||||
// Extract common prefix and map difference
|
||||
let originalSourcePaths = [];
|
||||
let mapPaths = [];
|
||||
for (let map of sourceMaps) {
|
||||
invariant(map.sourceMapContents); // Checked above.
|
||||
let parsed = JSON.parse(map.sourceMapContents);
|
||||
// Two formats for sourcemaps exist.
|
||||
if ("sections" in parsed) {
|
||||
for (let section of parsed.sections) {
|
||||
for (let source of section.map.sources) {
|
||||
originalSourcePaths.push(this._getAbsoluteSourcePath(map.filePath, source));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (let source of parsed.sources) {
|
||||
originalSourcePaths.push(this._getAbsoluteSourcePath(map.filePath, source));
|
||||
}
|
||||
}
|
||||
mapPaths.push(this._stripEmptyStringBookends(map.filePath.split("/")));
|
||||
}
|
||||
|
||||
let originalSourceCommonPrefix = this._findCommonPrefix(originalSourcePaths);
|
||||
let originalSourceCPElements = this._stripEmptyStringBookends(originalSourceCommonPrefix.split("/"));
|
||||
let mapCommonPrefix = this._findCommonPrefix(mapPaths);
|
||||
let mapCPElements = this._stripEmptyStringBookends(mapCommonPrefix.split("/"));
|
||||
|
||||
this._sourcemapCommonPrefix = this._findCommonPrefix([originalSourceCPElements, mapCPElements]);
|
||||
this._sourcemapMapDifference = this._findMapDifference(this._sourcemapCommonPrefix, mapCommonPrefix);
|
||||
}
|
||||
}
|
||||
|
||||
// Prefixes used to translate between relative paths stored in AST nodes and absolute paths given to IDE.
|
||||
_sourcemapCommonPrefix: void | string; // For paths relative to map location. (Used in Babel format)
|
||||
_sourcemapMapDifference: void | string; // For paths relative to map location. (Used in Babel format)
|
||||
_buckRoot: void | string; // For paths relative to directory root. (Used in Buck format)
|
||||
|
||||
/**
|
||||
* Assumes that input file and sourcemap are in the same directory.
|
||||
* Assumes pathToInput is an absolute path and pathToSource is relative.
|
||||
* Uses pathToSource to find absolute path of original source file.
|
||||
*/
|
||||
_getAbsoluteSourcePath(pathToInput: string, pathToSource: string): Array<string> {
|
||||
// pathToInput is an absolute path to the file being prepacked.
|
||||
let fullPath = this._stripEmptyStringBookends(pathToInput.split("/"));
|
||||
// Remove last entry because it is the filename, while we want the parent directory of the input file.
|
||||
fullPath.pop();
|
||||
|
||||
// Traverse the path to the source file.
|
||||
let steps = pathToSource.split("/");
|
||||
for (let step of steps) {
|
||||
switch (step) {
|
||||
case ".":
|
||||
break;
|
||||
case "..":
|
||||
fullPath.pop();
|
||||
break;
|
||||
default:
|
||||
fullPath.push(step);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return fullPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the longest possible prefix common to all input paths.
|
||||
* Input paths must be absolute.
|
||||
* Input is nested array because each path must be separated into elements.
|
||||
*/
|
||||
_findCommonPrefix(paths: Array<Array<string>>): string {
|
||||
// Find the point at which the paths diverge.
|
||||
let divergenceIndex = 0;
|
||||
let allPathsMatch = true;
|
||||
let maxDivergenceIndex = Math.max(...paths.map(path => path.length));
|
||||
|
||||
while (allPathsMatch && divergenceIndex < maxDivergenceIndex) {
|
||||
let entry = paths[0][divergenceIndex]; // Arbitrary choice of 0th path, since we're checking if all entires match.
|
||||
for (let path of paths) {
|
||||
if (path[divergenceIndex] !== entry) {
|
||||
allPathsMatch = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (allPathsMatch) divergenceIndex += 1;
|
||||
}
|
||||
// Edge case: if there's only one path, it will match itself, including the filename at the end.
|
||||
// For 2+ paths, even if they all share a prefix, the filenames will not match, so this is not needed.
|
||||
if (paths.length === 1) divergenceIndex -= 1;
|
||||
|
||||
// Concatenate prefix into string that's bookended by slashes for use as an absolute path prefix.
|
||||
return `/${paths[0].slice(0, divergenceIndex).join("/")}/`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the path that must be followed to arrive at the directory of the
|
||||
* common prefix from the sourcemap.
|
||||
*/
|
||||
_findMapDifference(commonPrefix: string, mapPrefix: string): string {
|
||||
// Find difference in path between the map's location and the common prefix.
|
||||
let mapPrefixUniqueElements = this._stripEmptyStringBookends(mapPrefix.replace(commonPrefix, "").split("/"));
|
||||
let mapDifference = "";
|
||||
for (let i = 0; i < mapPrefixUniqueElements.length; i++) {
|
||||
mapDifference = mapDifference.concat("../");
|
||||
}
|
||||
return mapDifference;
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes in ["", "foo", "bar", ""] and returns ["foo", "bar"]
|
||||
*/
|
||||
_stripEmptyStringBookends(path: Array<string>): Array<string> {
|
||||
if (path[0] === "") path.shift();
|
||||
if (path[path.length - 1] === "") path.pop();
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used by DebugAdapter to convert relative paths (used internally in debugging/Prepack engine)
|
||||
* into absolute paths (used by debugging UI/IDE).
|
||||
*/
|
||||
relativeToAbsolute(path: string): string {
|
||||
let absolute;
|
||||
if (this._buckRoot !== undefined) {
|
||||
let dirRoot = this._buckRoot;
|
||||
if (
|
||||
// If the "relative" path is actually absolute, then don't prepend anything.
|
||||
this._stripEmptyStringBookends(path.split("/"))[0] ===
|
||||
this._stripEmptyStringBookends(this._buckRoot.split("/"))[0]
|
||||
) {
|
||||
absolute = path;
|
||||
} else {
|
||||
let separator = path[0] === "/" ? "" : "/";
|
||||
absolute = dirRoot + separator + path;
|
||||
}
|
||||
} else {
|
||||
if (this._sourcemapCommonPrefix !== undefined && this._sourcemapMapDifference !== undefined) {
|
||||
absolute = path.replace(this._sourcemapMapDifference, "");
|
||||
invariant(this._sourcemapCommonPrefix !== undefined);
|
||||
absolute = this._sourcemapCommonPrefix + absolute;
|
||||
} else {
|
||||
absolute = path;
|
||||
}
|
||||
}
|
||||
return absolute;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used by DebugAdapter to convert absolute paths (used by debugging UI/IDE)
|
||||
* into relative paths (used internally in debugging/Prepack engine).
|
||||
*/
|
||||
absoluteToRelative(path: string): string {
|
||||
let relative;
|
||||
if (this._buckRoot !== undefined) {
|
||||
relative = path.replace(this._buckRoot, "");
|
||||
} else {
|
||||
if (this._sourcemapCommonPrefix !== undefined && this._sourcemapMapDifference !== undefined) {
|
||||
relative = path.replace(this._sourcemapCommonPrefix, "");
|
||||
invariant(this._sourcemapMapDifference !== undefined);
|
||||
relative = this._sourcemapMapDifference + relative;
|
||||
} else {
|
||||
relative = path;
|
||||
}
|
||||
}
|
||||
return relative;
|
||||
}
|
||||
}
|
@ -9,7 +9,6 @@
|
||||
|
||||
/* @flow strict-local */
|
||||
import type { SourceData } from "./../common/types.js";
|
||||
import { IsStatement } from "./../../methods/is.js";
|
||||
import { BabelNode } from "babel-types";
|
||||
import invariant from "./../common/invariant.js";
|
||||
|
||||
@ -32,8 +31,6 @@ export class Stepper {
|
||||
// The same node in two different excutions contexts (e.g. recursive call)
|
||||
// will not be detected. Check the stackSize (via realm) in those cases.
|
||||
isAstLocationChanged(ast: BabelNode) {
|
||||
// we should only step to statements
|
||||
if (!IsStatement(ast)) return false;
|
||||
let loc = ast.loc;
|
||||
if (!loc) return false;
|
||||
let filePath = loc.source;
|
||||
|
@ -31,7 +31,6 @@ import { Value } from "../values/index.js";
|
||||
import invariant from "../invariant.js";
|
||||
import { HasName, HasCompatibleType } from "./has.js";
|
||||
import type { BabelNodeExpression, BabelNodeCallExpression, BabelNodeLVal, BabelNodeClassMethod } from "babel-types";
|
||||
import { BabelNode } from "babel-types";
|
||||
|
||||
// ECMA262 22.1.3.1.1
|
||||
export function IsConcatSpreadable(realm: Realm, _O: Value): boolean {
|
||||
@ -358,46 +357,3 @@ export function IsStatic(classElement: BabelNodeClassMethod): boolean {
|
||||
// $FlowFixMe need to backport static property to BabelNodeClassMethod
|
||||
return classElement.static;
|
||||
}
|
||||
|
||||
export function IsStatement(node: BabelNode): boolean {
|
||||
switch (node.type) {
|
||||
case "BlockStatement":
|
||||
case "BreakStatement":
|
||||
case "ContinueStatement":
|
||||
case "DebuggerStatement":
|
||||
case "DoWhileStatement":
|
||||
case "EmptyStatement":
|
||||
case "ExpressionStatement":
|
||||
case "ForInStatement":
|
||||
case "ForStatement":
|
||||
case "FunctionDeclaration":
|
||||
case "IfStatement":
|
||||
case "LabeledStatement":
|
||||
case "ReturnStatement":
|
||||
case "SwitchStatement":
|
||||
case "ThrowStatement":
|
||||
case "TryStatement":
|
||||
case "VariableDeclaration":
|
||||
case "WhileStatement":
|
||||
case "WithStatement":
|
||||
case "ClassDeclaration":
|
||||
case "ExportAllDeclaration":
|
||||
case "ExportDefaultDeclaration":
|
||||
case "ExportNamedDeclaration":
|
||||
case "ForOfStatement":
|
||||
case "ImportDeclaration":
|
||||
case "DeclareClass":
|
||||
case "DeclareFunction":
|
||||
case "DeclareInterface":
|
||||
case "DeclareModule":
|
||||
case "DeclareModuleExports":
|
||||
case "DeclareTypeAlias":
|
||||
case "DeclareVariable":
|
||||
case "InterfaceDeclaration":
|
||||
case "TypeAlias":
|
||||
case "ForAwaitStatement":
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -7,10 +7,9 @@
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
/* @flow strict */
|
||||
/* @flow strict-local */
|
||||
|
||||
import type { ErrorHandler } from "./errors.js";
|
||||
import type { DebuggerConfigArguments } from "./debugger/common/types";
|
||||
|
||||
export type Compatibility =
|
||||
| "browser"
|
||||
@ -75,7 +74,6 @@ export type RealmOptions = {
|
||||
reactOptimizeNestedFunctions?: boolean,
|
||||
stripFlow?: boolean,
|
||||
abstractValueImpliesMax?: number,
|
||||
debuggerConfigArgs?: DebuggerConfigArguments,
|
||||
};
|
||||
|
||||
export type SerializerOptions = {
|
||||
@ -99,9 +97,4 @@ export type PartialEvaluatorOptions = {
|
||||
sourceMaps?: boolean,
|
||||
};
|
||||
|
||||
export type DebuggerOptions = {
|
||||
inFilePath: string,
|
||||
outFilePath: string,
|
||||
};
|
||||
|
||||
export const defaultOptions = {};
|
||||
|
@ -30,7 +30,7 @@ import invariant from "./invariant";
|
||||
import zipFactory from "node-zip";
|
||||
import path from "path";
|
||||
import JSONTokenizer from "./utils/JSONTokenizer.js";
|
||||
import type { DebuggerConfigArguments } from "./debugger/common/types";
|
||||
import type { DebuggerConfigArguments } from "./types";
|
||||
|
||||
// Prepack helper
|
||||
declare var __residual: any;
|
||||
@ -80,6 +80,7 @@ function run(
|
||||
--repro Create a zip file with all information needed to reproduce a Prepack run"
|
||||
--cpuprofile Create a CPU profile file for the run that can be loaded into the Chrome JavaScript CPU Profile viewer",
|
||||
--debugDiagnosticSeverity FatalError | RecoverableError | Warning | Information (default = FatalError). Diagnostic level at which debugger will stop
|
||||
--debugBuckRoot Root directory that buck assumes when creating sourcemap paths.
|
||||
`;
|
||||
let args = Array.from(process.argv);
|
||||
args.splice(0, 2);
|
||||
@ -274,6 +275,9 @@ function run(
|
||||
);
|
||||
debuggerConfigArgs.diagnosticSeverity = arg;
|
||||
break;
|
||||
case "debugBuckRoot":
|
||||
debuggerConfigArgs.buckRoot = args.shift();
|
||||
break;
|
||||
case "help":
|
||||
const options = [
|
||||
"-- | input.js",
|
||||
|
@ -15,7 +15,6 @@
|
||||
import { defaultOptions } from "./options";
|
||||
import { FatalError } from "./errors.js";
|
||||
import { type PrepackOptions } from "./prepack-options";
|
||||
import { getDebuggerOptions } from "./prepack-options";
|
||||
import { prepackNodeCLI, prepackNodeCLISync } from "./prepack-node-environment.js";
|
||||
import { prepackSources } from "./prepack-standalone.js";
|
||||
import { type SourceMap } from "./types.js";
|
||||
@ -142,11 +141,14 @@ export function prepackFileSync(filenames: Array<string>, options: PrepackOption
|
||||
}
|
||||
return { filePath: filename, fileContents: code, sourceMapContents: sourceMap };
|
||||
});
|
||||
let debugChannel;
|
||||
|
||||
// The existence of debug[In/Out]FilePath represents the desire to use the debugger.
|
||||
if (options.debugInFilePath !== undefined && options.debugOutFilePath !== undefined) {
|
||||
let debugOptions = getDebuggerOptions(options);
|
||||
let ioWrapper = new FileIOWrapper(false, debugOptions.inFilePath, debugOptions.outFilePath);
|
||||
debugChannel = new DebugChannel(ioWrapper);
|
||||
if (options.debuggerConfigArgs === undefined) options.debuggerConfigArgs = {};
|
||||
|
||||
let ioWrapper = new FileIOWrapper(false, options.debugInFilePath, options.debugOutFilePath);
|
||||
options.debuggerConfigArgs.debugChannel = new DebugChannel(ioWrapper);
|
||||
options.debuggerConfigArgs.sourcemaps = sourceFiles;
|
||||
}
|
||||
return prepackSources(sourceFiles, options, debugChannel, createStatistics(options));
|
||||
return prepackSources(sourceFiles, options, options.debuggerConfigArgs, createStatistics(options));
|
||||
}
|
||||
|
@ -10,17 +10,9 @@
|
||||
/* @flow strict-local */
|
||||
|
||||
import type { ErrorHandler } from "./errors.js";
|
||||
import type {
|
||||
SerializerOptions,
|
||||
RealmOptions,
|
||||
Compatibility,
|
||||
DebuggerOptions,
|
||||
ReactOutputTypes,
|
||||
InvariantModeTypes,
|
||||
} from "./options";
|
||||
import type { SerializerOptions, RealmOptions, Compatibility, ReactOutputTypes, InvariantModeTypes } from "./options";
|
||||
import { Realm } from "./realm.js";
|
||||
import invariant from "./invariant.js";
|
||||
import type { DebuggerConfigArguments } from "./debugger/common/types";
|
||||
import type { DebuggerConfigArguments } from "./types";
|
||||
|
||||
export type PrepackOptions = {|
|
||||
additionalGlobals?: Realm => void,
|
||||
@ -156,13 +148,3 @@ export function getSerializerOptions({
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export function getDebuggerOptions({ debugInFilePath, debugOutFilePath }: PrepackOptions): DebuggerOptions {
|
||||
invariant(debugInFilePath !== undefined, "Debugger invoked without input file path");
|
||||
invariant(debugOutFilePath !== undefined, "Debugger invoked without output file path");
|
||||
let result: DebuggerOptions = {
|
||||
inFilePath: debugInFilePath,
|
||||
outFilePath: debugOutFilePath,
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
@ -23,7 +23,6 @@ import type { PrepackOptions } from "./prepack-options";
|
||||
import { defaultOptions } from "./options";
|
||||
import invariant from "./invariant.js";
|
||||
import { version } from "../package.json";
|
||||
import type { DebugChannel } from "./debugger/server/channel/DebugChannel.js";
|
||||
import { type SerializedResult } from "./serializer/types.js";
|
||||
import { SerializerStatistics } from "./serializer/statistics.js";
|
||||
import { ResidualHeapVisitor } from "./serializer/ResidualHeapVisitor.js";
|
||||
@ -31,16 +30,17 @@ import { Modules } from "./utils/modules.js";
|
||||
import { Logger } from "./utils/logger.js";
|
||||
import { Generator } from "./utils/generator.js";
|
||||
import { AbstractObjectValue, AbstractValue, ObjectValue } from "./values/index.js";
|
||||
import type { DebuggerConfigArguments } from "./types";
|
||||
|
||||
export function prepackSources(
|
||||
sources: Array<SourceFile>,
|
||||
options: PrepackOptions = defaultOptions,
|
||||
debugChannel: DebugChannel | void = undefined,
|
||||
debuggerConfigArgs: void | DebuggerConfigArguments,
|
||||
statistics: SerializerStatistics | void = undefined
|
||||
): SerializedResult {
|
||||
let realmOptions = getRealmOptions(options);
|
||||
realmOptions.errorHandler = options.errorHandler;
|
||||
let realm = construct_realm(realmOptions, debugChannel, statistics || new SerializerStatistics());
|
||||
let realm = construct_realm(realmOptions, debuggerConfigArgs, statistics || new SerializerStatistics());
|
||||
initializeGlobals(realm);
|
||||
if (typeof options.additionalGlobals === "function") {
|
||||
options.additionalGlobals(realm);
|
||||
@ -82,6 +82,7 @@ export function prepackSources(
|
||||
sourceMapContents: serialized.map && JSON.stringify(serialized.map),
|
||||
},
|
||||
];
|
||||
let debugChannel = debuggerConfigArgs ? debuggerConfigArgs.debugChannel : undefined;
|
||||
realm = construct_realm(realmOptions, debugChannel);
|
||||
initializeGlobals(realm);
|
||||
if (typeof options.additionalGlobals === "function") {
|
||||
|
@ -52,6 +52,7 @@ import type {
|
||||
import type { Bindings, Effects, EvaluationResult, PropertyBindings, CreatedObjects, Realm } from "./realm.js";
|
||||
import { CompilerDiagnostic } from "./errors.js";
|
||||
import type { Severity } from "./errors.js";
|
||||
import type { DebugChannel } from "./debugger/server/channel/DebugChannel.js";
|
||||
|
||||
export const ElementSize = {
|
||||
Float32: 4,
|
||||
@ -1053,3 +1054,10 @@ export type UtilsType = {|
|
||||
getTypeFromName: string => void | typeof Value,
|
||||
describeValue: Value => string,
|
||||
|};
|
||||
|
||||
export type DebuggerConfigArguments = {
|
||||
diagnosticSeverity?: Severity,
|
||||
sourcemaps?: Array<SourceFile>,
|
||||
buckRoot?: string,
|
||||
debugChannel?: DebugChannel,
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user