microsoft/vscode-react-native

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
7ef94dcec53e3855b735c2023711fb53cc5764af

Branches

Tags

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

Clone

HTTPS

Download ZIP

src/debugger/nodeDebugWrapper.ts

204lines · 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) => {
81this.attachRequest(request, packagerPort);
82})
83.catch(error => {
748105d9Artem Egorov8 years ago84this.bailOut(error.data || error.message);
cbb0e869Artem Egorov8 years ago85});
e45838cbVladimir Kotikov9 years ago86}
87
0a68f8dbArtem Egorov8 years ago88private attach(request: DebugProtocol.Request): void {
89this.requestSetup(request.arguments);
2e432a9eArtem Egorov8 years ago90this.remoteExtension.getPackagerPort(request.arguments.program)
0a68f8dbArtem Egorov8 years ago91.then((packagerPort: number) => {
92this.attachRequest(request, packagerPort);
cbb0e869Artem Egorov8 years ago93});
e45838cbVladimir Kotikov9 years ago94}
95
0a68f8dbArtem Egorov8 years ago96private disconnect(request: DebugProtocol.Request): void {
e45838cbVladimir Kotikov9 years ago97// The client is about to disconnect so first we need to stop app worker
f920e582Vladimir Kotikov9 years ago98if (this.appWorker) {
99this.appWorker.stop();
100}
e45838cbVladimir Kotikov9 years ago101
102// Then we tell the extension to stop monitoring the logcat, and then we disconnect the debugging session
0a68f8dbArtem Egorov8 years ago103if (request.arguments.platform === "android") {
e45838cbVladimir Kotikov9 years ago104this.remoteExtension.stopMonitoringLogcat()
0a68f8dbArtem Egorov8 years ago105.catch(reason => logger.warn(`Couldn't stop monitoring logcat: ${reason.message || reason}`))
b8999098Dmitry Zinovyev9 years ago106.finally(() => super.dispatchRequest(request));
e45838cbVladimir Kotikov9 years ago107} else {
108super.dispatchRequest(request);
109}
110}
111
0a68f8dbArtem Egorov8 years ago112private requestSetup(args: any): void {
113let logLevel: string = args.trace;
114if (logLevel) {
115logLevel = logLevel.replace(logLevel[0], logLevel[0].toUpperCase());
116logger.setup(Logger.LogLevel[logLevel], false);
117} else {
118logger.setup(Logger.LogLevel.Log, false);
119}
120
e45838cbVladimir Kotikov9 years ago121this.projectRootPath = getProjectRoot(args);
122this.remoteExtension = RemoteExtension.atProjectRootPath(this.projectRootPath);
123
124// Start to send telemetry
125telemetryReporter.reassignTo(new ExtensionTelemetryReporter(
126appName, version, Telemetry.APPINSIGHTS_INSTRUMENTATIONKEY, this.projectRootPath));
127}
128
129/**
130* Runs logic needed to attach.
131* Attach should:
132* - Enable js debugging
133*/
0a68f8dbArtem Egorov8 years ago134// tslint:disable-next-line:member-ordering
135protected attachRequest(
136request: DebugProtocol.Request,
137packagerPort: number): Q.Promise<void> {
e45838cbVladimir Kotikov9 years ago138return TelemetryHelper.generate("attach", (generator) => {
139return Q({})
140.then(() => {
0a68f8dbArtem Egorov8 years ago141logger.log("Starting debugger app worker.");
e45838cbVladimir Kotikov9 years ago142// TODO: remove dependency on args.program - "program" property is technically
143// no more required in launch configuration and could be removed
144const workspaceRootPath = path.resolve(path.dirname(request.arguments.program), "..");
145const sourcesStoragePath = path.join(workspaceRootPath, ".vscode", ".react");
146
147// If launch is invoked first time, appWorker is undefined, so create it here
7daed3fcArtem Egorov8 years ago148this.appWorker = new MultipleLifetimesAppWorker(packagerPort, sourcesStoragePath, this.projectRootPath);
e45838cbVladimir Kotikov9 years ago149this.appWorker.on("connected", (port: number) => {
0a68f8dbArtem Egorov8 years ago150logger.log("Debugger worker loaded runtime on port " + port);
e45838cbVladimir Kotikov9 years ago151// Don't mutate original request to avoid side effects
152let attachArguments = Object.assign({}, request.arguments, { port, restart: true, request: "attach" });
153let attachRequest = Object.assign({}, request, { command: "attach", arguments: attachArguments });
154
6c75098eVladimir9 years ago155// Reinstantiate debug adapter, as the current implementation of ChromeDebugAdapter
156// doesn't allow us to reattach to another debug target easily. As of now it's easier
157// to throw previous instance out and create a new one.
0a68f8dbArtem Egorov8 years ago158(this as any)._debugAdapter = new (<any>debugSessionOpts.adapter)(debugSessionOpts, this);
e45838cbVladimir Kotikov9 years ago159super.dispatchRequest(attachRequest);
160});
161
162return this.appWorker.start();
163})
164.catch(error => this.bailOut(error.message));
165});
166}
167
168/**
169* Logs error to user and finishes the debugging process.
170*/
171private bailOut(message: string): void {
0a68f8dbArtem Egorov8 years ago172logger.error(`Could not debug. ${message}`);
173this.sendEvent(new TerminatedEvent());
27710197Vladimir Kotikov8 years ago174}
e45838cbVladimir Kotikov9 years ago175};
176}
177
0a68f8dbArtem Egorov8 years ago178export function makeAdapter(debugAdapterClass: typeof ChromeDebugAdapter): typeof ChromeDebugAdapter {
e45838cbVladimir Kotikov9 years ago179return class extends debugAdapterClass {
180public doAttach(port: number, targetUrl?: string, address?: string, timeout?: number): Promise<void> {
4b86d595Vladimir Kotikov9 years ago181// We need to overwrite ChromeDebug's _attachMode to let Node2 adapter
e45838cbVladimir Kotikov9 years ago182// to set up breakpoints on initial pause event
0a68f8dbArtem Egorov8 years ago183(this as any)._attachMode = false;
e45838cbVladimir Kotikov9 years ago184return super.doAttach(port, targetUrl, address, timeout);
185}
186};
187}
188
189/**
190* Parses settings.json file for workspace root property
191*/
192function getProjectRoot(args: any): string {
193try {
194let vsCodeRoot = path.resolve(args.program, "../..");
195let settingsPath = path.resolve(vsCodeRoot, ".vscode/settings.json");
196let settingsContent = fs.readFileSync(settingsPath, "utf8");
197settingsContent = stripJsonComments(settingsContent);
198let parsedSettings = JSON.parse(settingsContent);
199let projectRootPath = parsedSettings["react-native-tools"].projectRoot;
200return path.resolve(vsCodeRoot, projectRootPath);
201} catch (e) {
202return path.resolve(args.program, "../..");
203}
204}