microsoft/vscode-react-native

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
1.11.0

Branches

Tags

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

Clone

HTTPS

Download ZIP

src/debugger/direct/directDebugSession.ts

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