microsoft/vscode-react-native

Public

mirrored from https://github.com/microsoft/vscode-react-nativeAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
67d948c46dc355d02fa79b1876f1581e693b4975

Branches

Tags

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

Clone

HTTPS

Download ZIP

src/debugger/nodeDebugWrapper.ts

226lines · modeblame

e45838cbVladimir Kotikov9 years ago1// 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 Q from "q";
5import * as path from "path";
6import * as fs from "fs";
7import stripJsonComments = require("strip-json-comments");
8
b8999098Dmitry Zinovyev9 years ago9import { Telemetry } from "../common/telemetry";
10import { TelemetryHelper } from "../common/telemetryHelper";
11import { RemoteExtension } from "../common/remoteExtension";
12import { ExtensionTelemetryReporter, ReassignableTelemetryReporter } from "../common/telemetryReporters";
0a68f8dbArtem Egorov8 years ago13import { ChromeDebugSession, IChromeDebugSessionOpts, ChromeDebugAdapter, logger } from "vscode-chrome-debug-core";
14import { ContinuedEvent, TerminatedEvent, Logger } from "vscode-debugadapter";
15import { DebugProtocol } from "vscode-debugprotocol";
e45838cbVladimir Kotikov9 years ago16
17import { MultipleLifetimesAppWorker } from "./appWorker";
18
0a68f8dbArtem Egorov8 years ago19
b8999098Dmitry Zinovyev9 years ago20export function makeSession(
0a68f8dbArtem Egorov8 years ago21debugSessionClass: typeof ChromeDebugSession,
22debugSessionOpts: IChromeDebugSessionOpts,
b8999098Dmitry Zinovyev9 years ago23telemetryReporter: ReassignableTelemetryReporter,
0a68f8dbArtem Egorov8 years ago24appName: string, version: string): typeof ChromeDebugSession {
e45838cbVladimir Kotikov9 years ago25
26return class extends debugSessionClass {
27
28private projectRootPath: string;
29private remoteExtension: RemoteExtension;
5c8365a6Artem Egorov8 years ago30private appWorker: MultipleLifetimesAppWorker | null = null;
e45838cbVladimir Kotikov9 years ago31
32constructor(debuggerLinesAndColumnsStartAt1?: boolean, isServer?: boolean) {
33super(debuggerLinesAndColumnsStartAt1, isServer, debugSessionOpts);
34}
35
36// Override ChromeDebugSession's sendEvent to control what we will send to client
0a68f8dbArtem Egorov8 years ago37public sendEvent(event: DebugProtocol.Event): void {
e45838cbVladimir Kotikov9 years ago38// Do not send "terminated" events signaling about session's restart to client as it would cause it
39// to restart adapter's process, while we want to stay alive and don't want to interrupt connection
6c75098eVladimir9 years ago40// to packager.
b8999098Dmitry Zinovyev9 years ago41
720e992dArtem Egorov8 years ago42if (event.event === "terminated" && event.body && event.body.restart) {
b8999098Dmitry Zinovyev9 years ago43
44// Worker has been reloaded and switched to "continue" state
45// So we have to send "continued" event to client instead of "terminated"
46// Otherwise client might mistakenly show "stopped" state
0a68f8dbArtem Egorov8 years ago47let continuedEvent: ContinuedEvent = {
b8999098Dmitry Zinovyev9 years ago48event: "continued",
49type: "event",
50seq: event["seq"], // tslint:disable-line
51body: { threadId: event.body.threadId },
52};
53
54super.sendEvent(continuedEvent);
e45838cbVladimir Kotikov9 years ago55return;
56}
57
58super.sendEvent(event);
59}
60
0a68f8dbArtem Egorov8 years ago61protected dispatchRequest(request: DebugProtocol.Request): void {
e45838cbVladimir Kotikov9 years ago62if (request.command === "disconnect")
63return this.disconnect(request);
64
65if (request.command === "attach")
66return this.attach(request);
67
68if (request.command === "launch")
69return this.launch(request);
70
71return super.dispatchRequest(request);
72}
73
0a68f8dbArtem Egorov8 years ago74private launch(request: DebugProtocol.Request): void {
75this.requestSetup(request.arguments);
76this.remoteExtension.launch(request)
cbb0e869Artem Egorov8 years ago77.then(() => {
2e432a9eArtem Egorov8 years ago78return this.remoteExtension.getPackagerPort(request.arguments.program);
0a68f8dbArtem Egorov8 years ago79})
80.then((packagerPort: number) => {
6eeec3c0Serge Svekolnikov8 years ago81this.attachRequest({
82...request,
83arguments: {
84...request.arguments,
85port: packagerPort,
86},
87});
0a68f8dbArtem Egorov8 years ago88})
89.catch(error => {
748105d9Artem Egorov8 years ago90this.bailOut(error.data || error.message);
cbb0e869Artem Egorov8 years ago91});
e45838cbVladimir Kotikov9 years ago92}
93
0a68f8dbArtem Egorov8 years ago94private attach(request: DebugProtocol.Request): void {
95this.requestSetup(request.arguments);
2e432a9eArtem Egorov8 years ago96this.remoteExtension.getPackagerPort(request.arguments.program)
0a68f8dbArtem Egorov8 years ago97.then((packagerPort: number) => {
6eeec3c0Serge Svekolnikov8 years ago98this.attachRequest({
99...request,
100arguments: {
101...request.arguments,
102port: packagerPort,
103},
104});
cbb0e869Artem Egorov8 years ago105});
e45838cbVladimir Kotikov9 years ago106}
107
0a68f8dbArtem Egorov8 years ago108private disconnect(request: DebugProtocol.Request): void {
e45838cbVladimir Kotikov9 years ago109// The client is about to disconnect so first we need to stop app worker
f920e582Vladimir Kotikov9 years ago110if (this.appWorker) {
111this.appWorker.stop();
112}
e45838cbVladimir Kotikov9 years ago113
114// Then we tell the extension to stop monitoring the logcat, and then we disconnect the debugging session
0a68f8dbArtem Egorov8 years ago115if (request.arguments.platform === "android") {
e45838cbVladimir Kotikov9 years ago116this.remoteExtension.stopMonitoringLogcat()
0a68f8dbArtem Egorov8 years ago117.catch(reason => logger.warn(`Couldn't stop monitoring logcat: ${reason.message || reason}`))
b8999098Dmitry Zinovyev9 years ago118.finally(() => super.dispatchRequest(request));
e45838cbVladimir Kotikov9 years ago119} else {
120super.dispatchRequest(request);
121}
122}
123
0a68f8dbArtem Egorov8 years ago124private requestSetup(args: any): void {
125let logLevel: string = args.trace;
126if (logLevel) {
127logLevel = logLevel.replace(logLevel[0], logLevel[0].toUpperCase());
128logger.setup(Logger.LogLevel[logLevel], false);
129} else {
130logger.setup(Logger.LogLevel.Log, false);
131}
132
e45838cbVladimir Kotikov9 years ago133this.projectRootPath = getProjectRoot(args);
134this.remoteExtension = RemoteExtension.atProjectRootPath(this.projectRootPath);
135
136// Start to send telemetry
137telemetryReporter.reassignTo(new ExtensionTelemetryReporter(
138appName, version, Telemetry.APPINSIGHTS_INSTRUMENTATIONKEY, this.projectRootPath));
139}
140
141/**
142* Runs logic needed to attach.
143* Attach should:
144* - Enable js debugging
145*/
0a68f8dbArtem Egorov8 years ago146// tslint:disable-next-line:member-ordering
147protected attachRequest(
6eeec3c0Serge Svekolnikov8 years ago148request: DebugProtocol.Request): Q.Promise<void> {
e45838cbVladimir Kotikov9 years ago149return TelemetryHelper.generate("attach", (generator) => {
150return Q({})
151.then(() => {
0a68f8dbArtem Egorov8 years ago152logger.log("Starting debugger app worker.");
e45838cbVladimir Kotikov9 years ago153// TODO: remove dependency on args.program - "program" property is technically
154// no more required in launch configuration and could be removed
155const workspaceRootPath = path.resolve(path.dirname(request.arguments.program), "..");
156const sourcesStoragePath = path.join(workspaceRootPath, ".vscode", ".react");
157
158// If launch is invoked first time, appWorker is undefined, so create it here
6eeec3c0Serge Svekolnikov8 years ago159this.appWorker = new MultipleLifetimesAppWorker(
160request.arguments,
161sourcesStoragePath,
162this.projectRootPath,
163undefined);
e45838cbVladimir Kotikov9 years ago164this.appWorker.on("connected", (port: number) => {
0a68f8dbArtem Egorov8 years ago165logger.log("Debugger worker loaded runtime on port " + port);
e45838cbVladimir Kotikov9 years ago166// Don't mutate original request to avoid side effects
6eeec3c0Serge Svekolnikov8 years ago167let attachArguments = Object.assign({}, request.arguments, {
168address: "localhost",
169port,
170restart: true,
171request: "attach",
172remoteRoot: undefined,
173localRoot: undefined,
174});
e45838cbVladimir Kotikov9 years ago175let attachRequest = Object.assign({}, request, { command: "attach", arguments: attachArguments });
176
6c75098eVladimir9 years ago177// Reinstantiate debug adapter, as the current implementation of ChromeDebugAdapter
178// doesn't allow us to reattach to another debug target easily. As of now it's easier
179// to throw previous instance out and create a new one.
0a68f8dbArtem Egorov8 years ago180(this as any)._debugAdapter = new (<any>debugSessionOpts.adapter)(debugSessionOpts, this);
e45838cbVladimir Kotikov9 years ago181super.dispatchRequest(attachRequest);
182});
183
184return this.appWorker.start();
185})
186.catch(error => this.bailOut(error.message));
187});
188}
189
190/**
191* Logs error to user and finishes the debugging process.
192*/
193private bailOut(message: string): void {
0a68f8dbArtem Egorov8 years ago194logger.error(`Could not debug. ${message}`);
195this.sendEvent(new TerminatedEvent());
27710197Vladimir Kotikov8 years ago196}
e45838cbVladimir Kotikov9 years ago197};
198}
199
0a68f8dbArtem Egorov8 years ago200export function makeAdapter(debugAdapterClass: typeof ChromeDebugAdapter): typeof ChromeDebugAdapter {
e45838cbVladimir Kotikov9 years ago201return class extends debugAdapterClass {
202public doAttach(port: number, targetUrl?: string, address?: string, timeout?: number): Promise<void> {
4b86d595Vladimir Kotikov9 years ago203// We need to overwrite ChromeDebug's _attachMode to let Node2 adapter
e45838cbVladimir Kotikov9 years ago204// to set up breakpoints on initial pause event
0a68f8dbArtem Egorov8 years ago205(this as any)._attachMode = false;
e45838cbVladimir Kotikov9 years ago206return super.doAttach(port, targetUrl, address, timeout);
207}
208};
209}
210
211/**
212* Parses settings.json file for workspace root property
213*/
214function getProjectRoot(args: any): string {
215try {
216let vsCodeRoot = path.resolve(args.program, "../..");
217let settingsPath = path.resolve(vsCodeRoot, ".vscode/settings.json");
218let settingsContent = fs.readFileSync(settingsPath, "utf8");
219settingsContent = stripJsonComments(settingsContent);
220let parsedSettings = JSON.parse(settingsContent);
9a364375Serge Svekolnikov8 years ago221let projectRootPath = parsedSettings["react-native-tools.projectRoot"] || parsedSettings["react-native-tools"].projectRoot;
e45838cbVladimir Kotikov9 years ago222return path.resolve(vsCodeRoot, projectRootPath);
223} catch (e) {
224return path.resolve(args.program, "../..");
225}
226}