microsoft/vscode-react-native

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
1.11.2

Branches

Tags

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

Clone

HTTPS

Download ZIP

src/debugger/direct/directDebugSession.ts

338lines · 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 vscode from "vscode";
5import { logger } from "vscode-debugadapter";
6import { DebugProtocol } from "vscode-debugprotocol";
7import * as nls from "vscode-nls";
8import { ProjectVersionHelper } from "../../common/projectVersionHelper";
9import { TelemetryHelper } from "../../common/telemetryHelper";
10import { HermesCDPMessageHandler } from "../../cdp-proxy/CDPMessageHandlers/hermesCDPMessageHandler";
11import {
12 DebugSessionBase,
13 DebugSessionStatus,
14 IAttachRequestArgs,
15 ILaunchRequestArgs,
16} from "../debugSessionBase";
17import { JsDebugConfigAdapter } from "../jsDebugConfigAdapter";
18import { DebuggerEndpointHelper } from "../../cdp-proxy/debuggerEndpointHelper";
19import { ErrorHelper } from "../../common/error/errorHelper";
20import { InternalErrorCode } from "../../common/error/internalErrorCode";
21import { IOSDirectCDPMessageHandler } from "../../cdp-proxy/CDPMessageHandlers/iOSDirectCDPMessageHandler";
22import { PlatformType } from "../../extension/launchArgs";
23import { BaseCDPMessageHandler } from "../../cdp-proxy/CDPMessageHandlers/baseCDPMessageHandler";
24import { TipNotificationService } from "../../extension/services/tipsNotificationsService/tipsNotificationService";
25import { RNSession } from "../debugSessionWrapper";
26import { IWDPHelper } from "./IWDPHelper";
27import { SettingsHelper } from "../../extension/settingsHelper";
28
29nls.config({
30 messageFormat: nls.MessageFormat.bundle,
31 bundleFormat: nls.BundleFormat.standalone,
32})();
33const localize = nls.loadMessageBundle();
34
35export class DirectDebugSession extends DebugSessionBase {
36 private debuggerEndpointHelper: DebuggerEndpointHelper;
37 private onDidTerminateDebugSessionHandler: vscode.Disposable;
38 private onDidStartDebugSessionHandler: vscode.Disposable;
39 private appTargetConnectionClosedHandlerDescriptor?: vscode.Disposable;
40 private attachSession: vscode.DebugSession | null;
41 private iOSWKDebugProxyHelper: IWDPHelper;
42
43 constructor(rnSession: RNSession) {
44 super(rnSession);
45 this.debuggerEndpointHelper = new DebuggerEndpointHelper();
46 this.iOSWKDebugProxyHelper = new IWDPHelper();
47 this.attachSession = null;
48
49 this.onDidTerminateDebugSessionHandler = vscode.debug.onDidTerminateDebugSession(
50 this.handleTerminateDebugSession.bind(this),
51 );
52
53 this.onDidStartDebugSessionHandler = vscode.debug.onDidStartDebugSession(
54 this.handleStartDebugSession.bind(this),
55 );
56 }
57
58 protected async launchRequest(
59 response: DebugProtocol.LaunchResponse,
60 launchArgs: ILaunchRequestArgs,
61 // eslint-disable-next-line @typescript-eslint/no-unused-vars
62 request?: DebugProtocol.Request,
63 ): Promise<void> {
64 let extProps = {
65 platform: {
66 value: launchArgs.platform,
67 isPii: false,
68 },
69 isDirect: {
70 value: true,
71 isPii: false,
72 },
73 };
74
75 void TipNotificationService.getInstance().setKnownDateForFeatureById(
76 "directDebuggingWithHermes",
77 );
78
79 try {
80 try {
81 await this.initializeSettings(launchArgs);
82 logger.log("Launching the application");
83 logger.verbose(`Launching the application: ${JSON.stringify(launchArgs, null, 2)}`);
84
85 const versions = await ProjectVersionHelper.getReactNativeVersions(
86 this.projectRootPath,
87 ProjectVersionHelper.generateAdditionalPackagesToCheckByPlatform(launchArgs),
88 );
89 extProps = TelemetryHelper.addPlatformPropertiesToTelemetryProperties(
90 launchArgs,
91 versions,
92 extProps,
93 );
94
95 // eslint-disable-next-line @typescript-eslint/no-unused-vars
96 await TelemetryHelper.generate("launch", extProps, generator =>
97 this.appLauncher.launch(launchArgs),
98 );
99
100 if (!launchArgs.enableDebug) {
101 this.sendResponse(response);
102 // if debugging is not enabled skip attach request
103 return;
104 }
105 } catch (error) {
106 throw ErrorHelper.getInternalError(
107 InternalErrorCode.ApplicationLaunchFailed,
108 error.message || error,
109 );
110 }
111 // if debugging is enabled start attach request
112 await this.vsCodeDebugSession.customRequest("attach", launchArgs);
113 this.sendResponse(response);
114 } catch (error) {
115 this.terminateWithErrorResponse(error, response);
116 }
117 }
118
119 protected async attachRequest(
120 response: DebugProtocol.AttachResponse,
121 attachArgs: IAttachRequestArgs,
122 // eslint-disable-next-line @typescript-eslint/no-unused-vars
123 request?: DebugProtocol.Request,
124 ): Promise<void> {
125 let extProps = {
126 platform: {
127 value: attachArgs.platform,
128 isPii: false,
129 },
130 isDirect: {
131 value: true,
132 isPii: false,
133 },
134 };
135
136 attachArgs.webkitRangeMin = attachArgs.webkitRangeMin || 9223;
137 attachArgs.webkitRangeMax = attachArgs.webkitRangeMax || 9322;
138
139 this.previousAttachArgs = attachArgs;
140
141 try {
142 await this.initializeSettings(attachArgs);
143
144 const packager = this.appLauncher.getPackager();
145 const args: Parameters<typeof packager.forMessage> = [
146 // message indicates that another debugger has connected
147 "Already connected:",
148 {
149 type: "client_log",
150 level: "warn",
151 mode: "BRIDGE",
152 },
153 ];
154
155 void packager.forMessage(...args).then(
156 () => {
157 this.showError(
158 ErrorHelper.getInternalError(
159 InternalErrorCode.AnotherDebuggerConnectedToPackager,
160 ),
161 );
162 void this.terminate();
163 },
164 () => {},
165 );
166
167 logger.log("Attaching to the application");
168 logger.verbose(`Attaching to the application: ${JSON.stringify(attachArgs, null, 2)}`);
169
170 const versions = await ProjectVersionHelper.getReactNativeVersions(
171 this.projectRootPath,
172 ProjectVersionHelper.generateAdditionalPackagesToCheckByPlatform(attachArgs),
173 );
174 extProps = TelemetryHelper.addPlatformPropertiesToTelemetryProperties(
175 attachArgs,
176 versions,
177 extProps,
178 );
179
180 // eslint-disable-next-line @typescript-eslint/no-unused-vars
181 await TelemetryHelper.generate("attach", extProps, async generator => {
182 const port = attachArgs.useHermesEngine
183 ? attachArgs.port || this.appLauncher.getPackagerPort(attachArgs.cwd)
184 : attachArgs.platform === PlatformType.iOS
185 ? attachArgs.port || IWDPHelper.iOS_WEBKIT_DEBUG_PROXY_DEFAULT_PORT
186 : null;
187 if (port === null) {
188 throw ErrorHelper.getInternalError(
189 InternalErrorCode.CouldNotDirectDebugWithoutHermesEngine,
190 attachArgs.platform,
191 );
192 }
193 attachArgs.port = port;
194 logger.log(`Connecting to ${attachArgs.port} port`);
195 await this.appLauncher.getRnCdpProxy().stopServer();
196
197 const cdpMessageHandler: BaseCDPMessageHandler | null = attachArgs.useHermesEngine
198 ? new HermesCDPMessageHandler()
199 : attachArgs.platform === PlatformType.iOS
200 ? new IOSDirectCDPMessageHandler()
201 : null;
202
203 if (!cdpMessageHandler) {
204 throw ErrorHelper.getInternalError(
205 InternalErrorCode.CouldNotDirectDebugWithoutHermesEngine,
206 attachArgs.platform,
207 );
208 }
209 await this.appLauncher
210 .getRnCdpProxy()
211 .initializeServer(
212 cdpMessageHandler,
213 this.cdpProxyLogLevel,
214 this.cancellationTokenSource.token,
215 );
216
217 if (!attachArgs.useHermesEngine && attachArgs.platform === PlatformType.iOS) {
218 await this.iOSWKDebugProxyHelper.startiOSWebkitDebugProxy(
219 attachArgs.port,
220 attachArgs.webkitRangeMin,
221 attachArgs.webkitRangeMax,
222 );
223 const results = await this.iOSWKDebugProxyHelper.getSimulatorProxyPort(
224 attachArgs,
225 );
226 attachArgs.port = results.targetPort;
227 }
228
229 if (attachArgs.request === "attach") {
230 await this.preparePackagerBeforeAttach(attachArgs, versions);
231 }
232
233 this.appTargetConnectionClosedHandlerDescriptor = this.appLauncher
234 .getRnCdpProxy()
235 .onApplicationTargetConnectionClosed(() => {
236 if (this.attachSession) {
237 if (
238 this.debugSessionStatus !== DebugSessionStatus.Stopping &&
239 this.debugSessionStatus !== DebugSessionStatus.Stopped
240 ) {
241 void this.terminate();
242 }
243 this.appTargetConnectionClosedHandlerDescriptor?.dispose();
244 }
245 });
246
247 const settingsPorts = SettingsHelper.getPackagerPort(attachArgs.cwd);
248 const browserInspectUri = await this.debuggerEndpointHelper.retryGetWSEndpoint(
249 `http://localhost:${attachArgs.port}`,
250 90,
251 this.cancellationTokenSource.token,
252 attachArgs.useHermesEngine,
253 settingsPorts,
254 );
255 this.appLauncher.getRnCdpProxy().setBrowserInspectUri(browserInspectUri);
256 await this.establishDebugSession(attachArgs);
257 });
258 this.sendResponse(response);
259 } catch (error) {
260 this.terminateWithErrorResponse(
261 ErrorHelper.getInternalError(
262 InternalErrorCode.CouldNotAttachToDebugger,
263 error.message || error,
264 ),
265 response,
266 );
267 }
268 }
269
270 protected async disconnectRequest(
271 response: DebugProtocol.DisconnectResponse,
272 args: DebugProtocol.DisconnectArguments,
273 request?: DebugProtocol.Request,
274 ): Promise<void> {
275 this.debugSessionStatus = DebugSessionStatus.Stopping;
276
277 this.iOSWKDebugProxyHelper.cleanUp();
278 this.onDidTerminateDebugSessionHandler.dispose();
279 this.onDidStartDebugSessionHandler.dispose();
280 this.appLauncher.getPackager().closeWsConnection();
281 this.appTargetConnectionClosedHandlerDescriptor?.dispose();
282 return super.disconnectRequest(response, args, request);
283 }
284
285 protected async establishDebugSession(attachArgs: IAttachRequestArgs): Promise<void> {
286 const attachConfiguration = JsDebugConfigAdapter.createDebuggingConfigForRNHermes(
287 attachArgs,
288 this.appLauncher.getCdpProxyPort(),
289 this.rnSession.sessionId,
290 );
291
292 const childDebugSessionStarted = await vscode.debug.startDebugging(
293 this.appLauncher.getWorkspaceFolder(),
294 attachConfiguration,
295 {
296 parentSession: this.vsCodeDebugSession,
297 consoleMode: vscode.DebugConsoleMode.MergeWithParent,
298 },
299 );
300 if (!childDebugSessionStarted) {
301 throw new Error(
302 localize("CouldNotStartChildDebugSession", "Couldn't start child debug session"),
303 );
304 }
305 }
306
307 private handleTerminateDebugSession(debugSession: vscode.DebugSession): void {
308 if (
309 debugSession.configuration.rnDebugSessionId === this.rnSession.sessionId &&
310 debugSession.type === this.pwaNodeSessionName
311 ) {
312 void this.terminate();
313 }
314 }
315
316 private handleStartDebugSession(debugSession: vscode.DebugSession): void {
317 if (
318 this.nodeSession &&
319 (debugSession as any).parentSession &&
320 this.nodeSession.id === (debugSession as any).parentSession.id
321 ) {
322 this.attachSession = debugSession;
323 }
324 if (
325 debugSession.configuration.rnDebugSessionId === this.rnSession.sessionId &&
326 debugSession.type === this.pwaNodeSessionName
327 ) {
328 this.nodeSession = debugSession;
329 }
330 }
331
332 protected async initializeSettings(args: any): Promise<any> {
333 await super.initializeSettings(args);
334 if (args.useHermesEngine === undefined) {
335 args.useHermesEngine = true;
336 }
337 }
338}
339