microsoft/vscode-react-native

Public

mirrored fromhttps://github.com/microsoft/vscode-react-nativeAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
bb77358c8dc7ea46fae9d6aa601a11fde8eed0fd

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

src/debugger/nodeDebugWrapper.ts

194lines · modecode

1// Copyright (c) Microsoft Corporation. All rights reserved.
2// Licensed under the MIT license. See LICENSE file in the project root for details.
3
4import * as fs from "fs";
5import * as path from "path";
6import * as http from "http";
7
8import {Telemetry} from "../common/telemetry";
9import {TelemetryHelper} from "../common/telemetryHelper";
10import {RemoteExtension} from "../common/remoteExtension";
11import {EntryPointHandler, ProcessType} from "../common/entryPointHandler";
12import {ErrorHelper} from "../common/error/errorHelper";
13import {InternalErrorCode} from "../common/error/internalErrorCode";
14import {IOSPlatform} from "./ios/iOSPlatform";
15import {ExtensionTelemetryReporter, NullTelemetryReporter, ReassignableTelemetryReporter} from "../common/telemetryReporters";
16
17// These typings do not reflect the typings as intended to be used
18// but rather as they exist in truth, so we can reach into the internals
19// and access what we need.
20declare module VSCodeDebugAdapter {
21 class DebugSession {
22 public static run: Function;
23 public sendEvent(event: VSCodeDebugAdapter.InitializedEvent): void;
24 public start(input: any, output: any): void;
25 public launchRequest(response: any, args: any): void;
26 public disconnectRequest(response: any, args: any): void;
27 }
28 class InitializedEvent {
29 constructor();
30 }
31 class OutputEvent {
32 constructor(message: string, destination?: string);
33 }
34 class TerminatedEvent {
35 constructor();
36 }
37}
38
39declare class SourceMaps {
40 public _sourceToGeneratedMaps: {};
41 public _generatedToSourceMaps: {};
42 public _allSourceMaps: {};
43}
44
45declare class NodeDebugSession extends VSCodeDebugAdapter.DebugSession {
46 public _sourceMaps: SourceMaps;
47}
48
49interface ILaunchArgs {
50 platform: string;
51 target?: string;
52 internalDebuggerPort?: any;
53 iosRelativeProjectPath?: string;
54 args: string[];
55 logCatArguments: any;
56 program: string;
57}
58
59let version = JSON.parse(fs.readFileSync(path.join(__dirname, "..", "..", "package.json"), "utf-8")).version;
60
61function bailOut(reason: string): void {
62 // Things have gone wrong in initialization: Report the error to telemetry and exit
63 TelemetryHelper.sendSimpleEvent(reason);
64 process.exit(1);
65}
66
67function parseLogCatArguments(userProvidedLogCatArguments: any) {
68 return Array.isArray(userProvidedLogCatArguments)
69 ? userProvidedLogCatArguments.join(" ") // If it's an array, we join the arguments
70 : userProvidedLogCatArguments; // If not, we leave it as-is
71}
72
73function isNullOrUndefined(value: any): boolean {
74 return typeof value === "undefined" || value === null;
75}
76
77let projectRootPath: string = null;
78
79// Enable telemetry
80const telemetryReporter = new ReassignableTelemetryReporter(new NullTelemetryReporter());
81const appName = "react-native-debug-adapter";
82new EntryPointHandler(ProcessType.Debugger).runApp(appName, () => version,
83 ErrorHelper.getInternalError(InternalErrorCode.DebuggingFailed), telemetryReporter, () => {
84 let nodeDebugFolder: string;
85 let vscodeDebugAdapterPackage: typeof VSCodeDebugAdapter;
86
87 // nodeDebugLocation.json is dynamically generated on extension activation.
88 // If it fails, we must not have been in a react native project
89 try {
90 /* tslint:disable:no-var-requires */
91 nodeDebugFolder = require("./nodeDebugLocation.json").nodeDebugPath;
92 vscodeDebugAdapterPackage = require(path.join(nodeDebugFolder, "node_modules", "vscode-debugadapter"));
93 /* tslint:enable:no-var-requires */
94 } catch (e) {
95 // Nothing we can do here: can't even communicate back because we don't know how to speak debug adapter
96 bailOut("cannotFindDebugAdapter");
97 }
98
99 // Temporarily dummy out the DebugSession.run function so we do not start the debug adapter until we are ready
100 const originalDebugSessionRun = vscodeDebugAdapterPackage.DebugSession.run;
101 vscodeDebugAdapterPackage.DebugSession.run = function() { };
102
103 let nodeDebug: { NodeDebugSession: typeof NodeDebugSession };
104
105 try {
106 /* tslint:disable:no-var-requires */
107 nodeDebug = require(path.join(nodeDebugFolder, "out", "node", "nodeDebug"));
108 /* tslint:enable:no-var-requires */
109 } catch (e) {
110 // Unable to find nodeDebug, but we can make our own communication channel now
111 const debugSession = new vscodeDebugAdapterPackage.DebugSession();
112 // Note: this will not work in the context of debugging the debug adapter and communicating over a socket,
113 // but in that case we have much better ways to investigate errors.
114 debugSession.start(process.stdin, process.stdout);
115 debugSession.sendEvent(new vscodeDebugAdapterPackage.OutputEvent("Unable to start debug adapter: " + e.toString(), "stderr"));
116 debugSession.sendEvent(new vscodeDebugAdapterPackage.TerminatedEvent());
117
118 bailOut("cannotFindNodeDebugAdapter");
119 }
120
121 vscodeDebugAdapterPackage.DebugSession.run = originalDebugSessionRun;
122
123 // Intecept the "launchRequest" instance method of NodeDebugSession to interpret arguments
124 const originalNodeDebugSessionLaunchRequest = nodeDebug.NodeDebugSession.prototype.launchRequest;
125 nodeDebug.NodeDebugSession.prototype.launchRequest = function(request: any, args: ILaunchArgs) {
126 projectRootPath = path.resolve(args.program, "../..");
127 telemetryReporter.reassignTo(new ExtensionTelemetryReporter( // We start to send telemetry
128 appName, version, Telemetry.APPINSIGHTS_INSTRUMENTATIONKEY, projectRootPath));
129
130 // Create a server waiting for messages to re-initialize the debug session;
131 const reinitializeServer = http.createServer((req, res) => {
132 res.statusCode = 404;
133 if (req.url === "/refreshBreakpoints") {
134 res.statusCode = 200;
135 if (this) {
136 const sourceMaps = this._sourceMaps;
137 if (sourceMaps) {
138 // Flush any cached source maps
139 sourceMaps._allSourceMaps = {};
140 sourceMaps._generatedToSourceMaps = {};
141 sourceMaps._sourceToGeneratedMaps = {};
142 }
143 // Send an "initialized" event to trigger breakpoints to be re-sent
144 this.sendEvent(new vscodeDebugAdapterPackage.InitializedEvent());
145 }
146 }
147 res.end();
148 });
149 const debugServerListeningPort = parseInt(args.internalDebuggerPort, 10) || 9090;
150
151
152 reinitializeServer.listen(debugServerListeningPort);
153 reinitializeServer.on("error", (err: Error) => {
154 TelemetryHelper.sendSimpleEvent("reinitializeServerError");
155 this.sendEvent(new vscodeDebugAdapterPackage.OutputEvent("Error in debug adapter server: " + err.toString(), "stderr"));
156 this.sendEvent(new vscodeDebugAdapterPackage.OutputEvent("Breakpoints may not update. Consider restarting and specifying a different 'internalDebuggerPort' in launch.json"));
157 });
158
159 // We do not permit arbitrary args to be passed to our process
160 args.args = [
161 args.platform,
162 debugServerListeningPort.toString(),
163 args.iosRelativeProjectPath || IOSPlatform.DEFAULT_IOS_PROJECT_RELATIVE_PATH,
164 args.target || "simulator",
165 ];
166
167 if (!isNullOrUndefined(args.logCatArguments)) { // We add the parameter if it's defined (adapter crashes otherwise)
168 args.args = args.args.concat([parseLogCatArguments(args.logCatArguments)]);
169 }
170
171 originalNodeDebugSessionLaunchRequest.call(this, request, args);
172 };
173
174 // Intecept the "launchRequest" instance method of NodeDebugSession to interpret arguments
175 const originalNodeDebugSessionDisconnectRequest = nodeDebug.NodeDebugSession.prototype.disconnectRequest;
176 function customDisconnectRequest(response: any, args: any): void {
177 try {
178 // First we tell the extension to stop monitoring the logcat, and then we disconnect the debugging session
179 const remoteExtension = RemoteExtension.atProjectRootPath(projectRootPath);
180 remoteExtension.stopMonitoringLogcat()
181 .finally(() => originalNodeDebugSessionDisconnectRequest.call(this, response, args))
182 .done(() => { }, reason => // We just print a warning if something fails
183 process.stderr.write(`WARNING: Couldn't stop monitoring logcat: ${reason.message || reason}\n`));
184 } catch (exception) {
185 // This is a "nice to have" feature, so we just fire the message and forget. We don't event handle
186 // errors in the response promise
187 process.stderr.write(`WARNING: Couldn't stop monitoring logcat. Sync exception: ${exception.message || exception}\n`);
188 originalNodeDebugSessionDisconnectRequest.call(this, response, args);
189 }
190 }
191 nodeDebug.NodeDebugSession.prototype.disconnectRequest = customDisconnectRequest;
192
193 vscodeDebugAdapterPackage.DebugSession.run(nodeDebug.NodeDebugSession);
194 });
195