Skip to content

Commit 70050a5

Browse files
author
Dimitar Tachev
authored
Merge pull request #4455 from NativeScript/tachev/ios-crashes-livesync
fix: do not wait 60 seconds for debugger port when the app is crashed
2 parents 871b343 + 41ae672 commit 70050a5

10 files changed

+146
-59
lines changed

lib/common/constants.ts

+1
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ export class EmulatorDiscoveryNames {
5454

5555
export const DEVICE_LOG_EVENT_NAME = "deviceLogData";
5656
export const IOS_LOG_PREDICATE = 'senderImagePath contains "NativeScript" || eventMessage contains[c] "NativeScript"';
57+
export const IOS_APP_CRASH_LOG_REG_EXP = /Fatal JavaScript exception \- application has been terminated/;
5758

5859
export const TARGET_FRAMEWORK_IDENTIFIERS = {
5960
Cordova: "Cordova",

lib/common/mobile/ios/device/ios-device.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,7 @@ export class IOSDevice extends IOSDeviceBase {
6060
}
6161
}
6262

63-
protected async getDebugSocketCore(appId: string, projectName: string): Promise<net.Socket> {
64-
await super.attachToDebuggerFoundEvent(projectName);
63+
protected async getDebugSocketCore(appId: string): Promise<net.Socket> {
6564
await this.$iOSSocketRequestExecutor.executeAttachRequest(this, constants.AWAIT_NOTIFICATION_TIMEOUT_SECONDS, appId);
6665
const port = await super.getDebuggerPort(appId);
6766
const deviceId = this.deviceInfo.identifier;

lib/common/mobile/ios/ios-device-base.ts

+6-4
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@ export abstract class IOSDeviceBase implements Mobile.IiOSDevice {
2222
return this.cachedSockets[appId];
2323
}
2424

25-
this.cachedSockets[appId] = await this.getDebugSocketCore(appId, projectName);
25+
await this.attachToDebuggerFoundEvent(appId, projectName);
26+
await this.applicationManager.startApplication({ appId, projectName });
27+
this.cachedSockets[appId] = await this.getDebugSocketCore(appId);
2628

2729
if (this.cachedSockets[appId]) {
2830
this.cachedSockets[appId].on("close", async () => {
@@ -38,11 +40,11 @@ export abstract class IOSDeviceBase implements Mobile.IiOSDevice {
3840
);
3941
}
4042

41-
protected abstract async getDebugSocketCore(appId: string, projectName: string): Promise<net.Socket>;
43+
protected abstract async getDebugSocketCore(appId: string): Promise<net.Socket>;
4244

43-
protected async attachToDebuggerFoundEvent(projectName: string): Promise<void> {
45+
protected async attachToDebuggerFoundEvent(appId: string, projectName: string): Promise<void> {
4446
await this.startDeviceLogProcess(projectName);
45-
await this.$iOSDebuggerPortService.attachToDebuggerPortFoundEvent();
47+
await this.$iOSDebuggerPortService.attachToDebuggerPortFoundEvent(appId);
4648
}
4749

4850
protected async getDebuggerPort(appId: string): Promise<number> {

lib/common/mobile/ios/simulator/ios-simulator-device.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,8 @@ export class IOSSimulator extends IOSDeviceBase implements Mobile.IiOSDevice {
5353
return this.$iOSSimulatorLogProvider.startLogProcess(this.simulator.id, options);
5454
}
5555

56-
protected async getDebugSocketCore(appId: string, projectName: string): Promise<net.Socket> {
56+
protected async getDebugSocketCore(appId: string): Promise<net.Socket> {
5757
let socket: net.Socket;
58-
await super.attachToDebuggerFoundEvent(projectName);
5958
const attachRequestMessage = this.$iOSNotification.getAttachRequest(appId, this.deviceInfo.identifier);
6059
await this.$iOSEmulatorServices.postDarwinNotification(attachRequestMessage, this.deviceInfo.identifier);
6160
const port = await super.getDebuggerPort(appId);
+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
interface IHmrStatusService {
2+
watchHmrStatus(deviceId: string, operationHash: string): void;
23
getHmrStatus(deviceId: string, operationHash: string): Promise<number>;
34
attachToHmrStatusEvent(): void;
45
}

lib/definitions/ios-debugger-port-service.d.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ interface IIOSDebuggerPortData {
1212
interface IIOSDebuggerPortStoredData {
1313
port: number;
1414
timer?: NodeJS.Timer;
15+
error?: Error;
1516
}
1617

1718
interface IIOSDebuggerPortService {
@@ -24,5 +25,5 @@ interface IIOSDebuggerPortService {
2425
* Attaches on DEBUGGER_PORT_FOUND event and stores the port
2526
* @returns {Promise<void>}
2627
*/
27-
attachToDebuggerPortFoundEvent(): Promise<void>;
28+
attachToDebuggerPortFoundEvent(appId: string): Promise<void>;
2829
}

lib/services/hmr-status-service.ts

+25-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { cache } from "../common/decorators";
2-
import { HmrConstants } from "../common/constants";
2+
import { HmrConstants, IOS_APP_CRASH_LOG_REG_EXP } from "../common/constants";
33

44
export class HmrStatusService implements IHmrStatusService {
55
public static HMR_STATUS_LOG_REGEX = /([a-z A-Z]*) hmr hash ([a-z0-9]*)\./;
@@ -11,9 +11,10 @@ export class HmrStatusService implements IHmrStatusService {
1111

1212
constructor(private $logParserService: ILogParserService,
1313
private $processService: IProcessService,
14+
private $devicePlatformsConstants: Mobile.IDevicePlatformsConstants,
1415
private $logger: ILogger) {
15-
this.$processService.attachToProcessExitSignals(this, this.dispose);
16-
}
16+
this.$processService.attachToProcessExitSignals(this, this.dispose);
17+
}
1718

1819
public getHmrStatus(deviceId: string, operationHash: string): Promise<number> {
1920
return new Promise((resolve, reject) => {
@@ -33,13 +34,32 @@ export class HmrStatusService implements IHmrStatusService {
3334
});
3435
}
3536

37+
public watchHmrStatus(deviceId: string, operationHash: string): void {
38+
this.setData(deviceId, operationHash);
39+
}
40+
3641
@cache()
3742
public attachToHmrStatusEvent(): void {
3843
this.$logParserService.addParseRule({
3944
regex: HmrStatusService.HMR_STATUS_LOG_REGEX,
4045
handler: this.handleHmrStatusFound.bind(this),
4146
name: "hmrStatus"
4247
});
48+
this.$logParserService.addParseRule({
49+
regex: IOS_APP_CRASH_LOG_REG_EXP,
50+
handler: this.handleAppCrash.bind(this),
51+
name: "appCrashHmr",
52+
platform: this.$devicePlatformsConstants.iOS.toLowerCase()
53+
});
54+
}
55+
56+
private handleAppCrash(matches: RegExpMatchArray, deviceId: string): void {
57+
for (const operationId in this.hashOperationStatuses) {
58+
const operation = this.hashOperationStatuses[operationId];
59+
if (operationId.startsWith(deviceId) && !operation.status) {
60+
operation.status = HmrConstants.HMR_ERROR_STATUS;
61+
}
62+
}
4363
}
4464

4565
private handleHmrStatusFound(matches: RegExpMatchArray, deviceId: string): void {
@@ -65,7 +85,7 @@ export class HmrStatusService implements IHmrStatusService {
6585
this.$logger.trace("Found hmr status.", { status, hash });
6686

6787
if (status) {
68-
this.setData(status, hash, deviceId);
88+
this.setData(deviceId, hash, status);
6989
}
7090
}
7191

@@ -77,7 +97,7 @@ export class HmrStatusService implements IHmrStatusService {
7797
return null;
7898
}
7999

80-
private setData(status: Number, operationHash: string, deviceId: string): void {
100+
private setData(deviceId: string, operationHash: string, status?: Number): void {
81101
const key = `${deviceId}${operationHash}`;
82102

83103
if (!this.hashOperationStatuses[key]) {

lib/services/ios-debugger-port-service.ts

+33-8
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,42 @@
1-
import { DEBUGGER_PORT_FOUND_EVENT_NAME, ATTACH_REQUEST_EVENT_NAME } from "../common/constants";
1+
import { DEBUGGER_PORT_FOUND_EVENT_NAME, ATTACH_REQUEST_EVENT_NAME, IOS_APP_CRASH_LOG_REG_EXP } from "../common/constants";
22
import { cache } from "../common/decorators";
33
import { APPLICATION_RESPONSE_TIMEOUT_SECONDS } from "../constants";
44

55
export class IOSDebuggerPortService implements IIOSDebuggerPortService {
66
public static DEBUG_PORT_LOG_REGEX = /NativeScript debugger has opened inspector socket on port (\d+?) for (.*)[.]/;
77
private mapDebuggerPortData: IDictionary<IIOSDebuggerPortStoredData> = {};
8+
private currentAppId: string;
89

910
constructor(private $logParserService: ILogParserService,
1011
private $iOSNotification: IiOSNotification,
1112
private $devicePlatformsConstants: Mobile.IDevicePlatformsConstants,
1213
private $logger: ILogger) { }
1314

14-
public getPort(data: IIOSDebuggerPortInputData): Promise<number> {
15-
return new Promise((resolve, reject) => {
15+
public async getPort(data: IIOSDebuggerPortInputData): Promise<number> {
16+
return new Promise<number>((resolve, reject) => {
1617
const key = `${data.deviceId}${data.appId}`;
1718
const retryInterval = 500;
1819
let retryCount = Math.max(APPLICATION_RESPONSE_TIMEOUT_SECONDS * 1000 / retryInterval, 10);
1920

2021
const interval = setInterval(() => {
21-
let port = this.getPortByKey(key);
22+
const port = this.getPortByKey(key);
2223
if (port || retryCount === 0) {
2324
clearInterval(interval);
2425
resolve(port);
2526
} else {
26-
port = this.getPortByKey(key);
27-
retryCount--;
27+
if (this.mapDebuggerPortData[key] && this.mapDebuggerPortData[key].error) {
28+
clearInterval(interval);
29+
reject(this.mapDebuggerPortData[key].error);
30+
} else {
31+
retryCount--;
32+
}
2833
}
2934
}, retryInterval);
3035
});
3136
}
3237

33-
public async attachToDebuggerPortFoundEvent(): Promise<void> {
38+
public async attachToDebuggerPortFoundEvent(appId: string): Promise<void> {
39+
this.currentAppId = appId;
3440
this.attachToDebuggerPortFoundEventCore();
3541
this.attachToAttachRequestEvent();
3642
}
@@ -43,6 +49,24 @@ export class IOSDebuggerPortService implements IIOSDebuggerPortService {
4349
name: "debugPort",
4450
platform: this.$devicePlatformsConstants.iOS.toLowerCase()
4551
});
52+
this.$logParserService.addParseRule({
53+
regex: IOS_APP_CRASH_LOG_REG_EXP,
54+
handler: this.handleAppCrash.bind(this),
55+
name: "appCrash",
56+
platform: this.$devicePlatformsConstants.iOS.toLowerCase()
57+
});
58+
}
59+
60+
private handleAppCrash(matches: RegExpMatchArray, deviceId: string): void {
61+
const data = {
62+
port: 0,
63+
appId: this.currentAppId,
64+
deviceId,
65+
error: new Error("The application has been terminated.")
66+
};
67+
68+
this.clearTimeout(data);
69+
this.setData(data, { port: data.port, error: data.error });
4670
}
4771

4872
private handlePortFound(matches: RegExpMatchArray, deviceId: string): void {
@@ -73,7 +97,7 @@ export class IOSDebuggerPortService implements IIOSDebuggerPortService {
7397
}
7498

7599
private getPortByKey(key: string): number {
76-
if (this.mapDebuggerPortData[key]) {
100+
if (this.mapDebuggerPortData[key] && this.mapDebuggerPortData[key].port) {
77101
return this.mapDebuggerPortData[key].port;
78102
}
79103

@@ -89,6 +113,7 @@ export class IOSDebuggerPortService implements IIOSDebuggerPortService {
89113

90114
this.mapDebuggerPortData[key].port = storedData.port;
91115
this.mapDebuggerPortData[key].timer = storedData.timer;
116+
this.mapDebuggerPortData[key].error = storedData.error;
92117
}
93118

94119
private clearTimeout(data: IIOSDebuggerPortData): void {

lib/services/livesync/livesync-service.ts

+7-2
Original file line numberDiff line numberDiff line change
@@ -664,12 +664,17 @@ export class LiveSyncService extends EventEmitter implements IDebugLiveSyncServi
664664
const service = this.getLiveSyncService(device.deviceInfo.platform);
665665

666666
const watchAction = async (watchInfo: ILiveSyncWatchInfo): Promise<void> => {
667+
const isInHMRMode = liveSyncData.useHotModuleReload && platformHmrData.hash;
668+
if (isInHMRMode) {
669+
this.$hmrStatusService.watchHmrStatus(device.deviceInfo.identifier, platformHmrData.hash);
670+
}
671+
667672
let liveSyncResultInfo = await service.liveSyncWatchAction(device, watchInfo);
668673

669674
await this.refreshApplication(projectData, liveSyncResultInfo, deviceBuildInfoDescriptor.debugOptions, deviceBuildInfoDescriptor.outputPath);
670675

671-
// If didRecover is true, this means we were in ErrorActivity and fallback files were already transfered and app will be restarted.
672-
if (!liveSyncResultInfo.didRecover && liveSyncData.useHotModuleReload && platformHmrData.hash) {
676+
// If didRecover is true, this means we were in ErrorActivity and fallback files were already transferred and app will be restarted.
677+
if (!liveSyncResultInfo.didRecover && isInHMRMode) {
673678
const status = await this.$hmrStatusService.getHmrStatus(device.deviceInfo.identifier, platformHmrData.hash);
674679
if (status === HmrConstants.HMR_ERROR_STATUS) {
675680
watchInfo.filesToSync = platformHmrData.fallbackFiles;

0 commit comments

Comments
 (0)