Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[TIMOB-26007] iOS: Refactor crash dialog #10022

Merged
merged 17 commits into from May 24, 2018
Merged

Conversation

garymathews
Copy link
Contributor

@garymathews garymathews commented May 3, 2018

  • Refactor crash dialog to display stack trace
TEST CASE #1
var win = Ti.UI.createWindow({ backgroundColor: 'gray' });

win.addEventListener('open', function (e) {
   Ti.API.info(e.test.crash);
});

win.open();

JIRA Ticket

@build
Copy link
Contributor

build commented May 3, 2018

Messages
📖

💾 Here's the generated SDK zipfile.

Generated by 🚫 dangerJS

Copy link
Collaborator

@hansemannn hansemannn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a few changes for now. Really really great work @garymathews!

{
TiObjectRef exceptionObj = TiValueToObject(context, exception, NULL);
TiStringRef stackProperty = TiStringCreateWithUTF8CString("stack");
TiValueRef stackValue = JSObjectGetProperty(context, exceptionObj, stackProperty, NULL);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use TiObjectGetProperty to make it usable with TiCore.

@@ -730,4 +730,6 @@ typedef enum {
*/
+ (UIImage *)imageWithColor:(UIColor *)color;

+ (NSString*)stackFromException:(TiContextRef)context exception:(TiValueRef)exception;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please format the source with clang-format -style=file -i Classes/TiUtils.* and for other related files. Only necessary at the end :-).

/**
* Returns line column where error happened.
*/
@property (nonatomic, readonly) NSInteger column;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the column cannot be < 0, you can use NSUInteger to be more precise.


[dismissButton addTarget:self action:@selector(dismiss:) forControlEvents:UIControlEventTouchUpInside];

UIScrollView* view = [[[NSBundle mainBundle] loadNibNamed:@"ErrorScreen" owner:self options:nil] objectAtIndex:0];
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we using an xib for convenience? Usually we try to add the AutoLayout code programmatically to have more control over it, but I understand that this may be critical for more complex layouts.

@sgtcoolguy
Copy link
Contributor

I assume this is sort of a parity ticket for iOS like we did in #10003 for Android?

I'd love to be able to also add nativeStack like we did there.

[[TiApp app] showModalError:[error description]];
} else {
[[TiApp app] showModalError:[NSString stringWithFormat:@"%@\n\n%@", [error description], [nativeStackTrace componentsJoinedByString:@"\n"]]];
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@garymathews I have pushed one more commit that includes an alternative way of receiving the stack-symbols without throwing a "computed" error by using [NSThread callStackSymbols]. We may also need to change the start-index to 3, since the order is:

  • showScriptError
  • handleScriptError
  • reportScriptError
  • (ANY CLASS)

@hansemannn
Copy link
Collaborator

@garymathews I think we are good to merge here. Thanks again for your work on the iOS side!

@sgtcoolguy
Copy link
Contributor

Forgive me if I'm missing something here, but it looks like we show the native stack in the modal error dialog, but don't set it on Error objects in JS-land now?

I think we should be generating the native stack lower-level where ever it is we surface the Error objects to JS-world so that the property's value can be read from JS too.

Specifically, I'd like to see this unit test suite tweaked slightly to support accessing nativeStack on both iOS and Android: https://github.com/appcelerator/titanium_mobile/pull/10003/files#diff-3dc400bf7f83b2691685b65f5a5066d1R13

There we added tests to verify 3 typical scenarios:

  • A JS error occurred, should be a typical Error object with message and stack properties
  • A native error occurred, should be an Error object with message and stack properties, but also a nativeStack property. This particular trigger case for Android doesn't cause an issue on iOS, so we'd likely need to some up with some other example where type coercion doesn't get handled properly and causes a native exception in Obj-C.
  • throwing a primitive like a string literal in JS, the caught object is the string, not an Error

I think e should be able to enable this test on both platforms and have them each pass. Clearly the nativeStack will differ on Android versus iOS, and maybe the triggering code for causing a native error. But the general type/property checks should apply.

@hansemannn
Copy link
Collaborator

hansemannn commented May 22, 2018

@sgtcoolguy @garymathews

JS Errors

For JS errors, message and stack properties are already included:

2018-05-22 23:15:05.481292+0200 Titanium[2312:16120] [ERROR] {
    column = 17;
    line = 20;
    message = "undefined is not an object (evaluating 'obj.test.crash')";
    sourceURL = "file:///Users/hknoechel/Library/Developer/CoreSimulator/Devices/C02D33D2-92F2-490F-A2A8-D978B36B0626/data/Containers/Bundle/Application/C3F8BB88-5231-4F2B-9603-284BF11BE457/Titanium.app/app.js";
    stack = "file:///Users/hknoechel/Library/Developer/CoreSimulator/Devices/C02D33D2-92F2-490F-A2A8-D978B36B0626/data/Containers/Bundle/Application/C3F8BB88-5231-4F2B-9603-284BF11BE457/Titanium.app/app.js:20:17";
}

but they are named differently.

Native Errors

For native errors, we include nativeLocation and nativeReason as well.

{
    column = 23;
    line = 25;
    message = "Invalid type passed to function";
    nativeLocation = "-[GeolocationModule setAccuracy:] (GeolocationModule.m:585)";
    nativeReason = "expected: Number, was: String";
    sourceURL = "file:///Users/hknoechel/Library/Developer/CoreSimulator/Devices/C02D33D2-92F2-490F-A2A8-D978B36B0626/data/Containers/Bundle/Application/84C5664F-B508-4F4F-A557-168E14FD52DC/Titanium.app/app.js";
    stack = "file:///Users/hknoechel/Library/Developer/CoreSimulator/Devices/C02D33D2-92F2-490F-A2A8-D978B36B0626/data/Containers/Bundle/Application/84C5664F-B508-4F4F-A557-168E14FD52DC/Titanium.app/app.js:25:23";
}

Should we remove those for iOS?

EDIT: We could add it pretty easily in the TiBindingTiValueFromNSObject selector:

    NSArray<NSString *> *nativeStack = [(NSException *)obj callStackSymbols];
    if (nativeStack != nil) {
      TiStringRef propertyName = TiStringCreateWithUTF8CString("nativeStack");
      TiStringRef valueString = TiStringCreateWithCFString((CFStringRef)[nativeStack componentsJoinedByString:@"\n"]);
      TiObjectSetProperty(jsContext, excObject, propertyName, TiValueMakeString(jsContext, valueString), kTiPropertyAttributeReadOnly, NULL);
      TiStringRelease(propertyName);
      TiStringRelease(valueString);
    }

it would show as the following:

2018-05-22 23:25:27.004166+0200 Titanium[2970:29155] [ERROR] {
    column = 23;
    line = 25;
    message = "Invalid type passed to function";
    nativeLocation = "-[GeolocationModule setAccuracy:] (GeolocationModule.m:585)";
    nativeReason = "expected: Number, was: String";
    nativeStack = "0   CoreFoundation                      0x0000000116c431ab __exceptionPreprocess + 171\n1   libobjc.A.dylib                     0x0000000115bf4f41 objc_exception_throw + 48\n2   Titanium                            0x000000010ea10c47 TiExceptionThrowWithNameAndReason + 167\n3   Titanium                            0x000000010e9d3149 -[TiProxy throwException:subreason:location:] + 105\n4   Titanium                            0x000000010eb511a2 -[GeolocationModule setAccuracy:] + 514\n5   Titanium                            0x000000010e917175 -[KrollObject setValue:forKey:] + 757\n6   Titanium                            0x000000010e91412e __KrollSetProperty_block_invoke + 46\n7   Titanium                            0x000000010ea10efd TiThreadPerformOnMainThread + 77\n8   Titanium                            0x000000010e91402d KrollSetProperty + 653\n9   Titanium                            0x000000010ec9eaee _ZN2TI16JSCallbackObjectINS_20JSDestructibleObjectEE3putEPNS_6JSCellEPNS_9ExecStateENS_12PropertyNameENS_7TiValueERNS_15PutPropertySlotE + 302\n10  Titanium                            0x000000010ed44e70 llint_slow_path_put_by_id + 496\n11  Titanium                            0x000000010ed4f4ad llint_op_put_by_id + 133";
    sourceURL = "file:///Users/hknoechel/Library/Developer/CoreSimulator/Devices/C02D33D2-92F2-490F-A2A8-D978B36B0626/data/Containers/Bundle/Application/9C839F85-66A9-49E0-961E-F64BAEE3E714/Titanium.app/app.js";
    stack = "file:///Users/hknoechel/Library/Developer/CoreSimulator/Devices/C02D33D2-92F2-490F-A2A8-D978B36B0626/data/Containers/Bundle/Application/9C839F85-66A9-49E0-961E-F64BAEE3E714/Titanium.app/app.js:25:23";
}

We should trim the last 2 stacks again to not include the exception handling like we do for our error-controller, but yes, that could work out.

String Literal Errors

Quick test that passes already, using the current state:

try {
  throw ('this is my error string');
} catch (ex) {
  Ti.API.error(ex);
  Ti.API.error(ex === 'this is my error string');
}

I have added a PR for the above in garymathews#2

@sgtcoolguy
Copy link
Contributor

I'm satisfied with @hansemannn 's additional commits on his fork here, so if they get included - and we update the test to actually run on iOS, we should be good.

@hansemannn
Copy link
Collaborator

@sgtcoolguy @garymathews Unit-test added as well. I have removed the "contains string" checks to be less error prone, especially because Windows (cc @infosia) will come as well.

@sgtcoolguy sgtcoolguy merged commit e4cfa45 into tidev:master May 24, 2018
NSUInteger exceptionStackTraceLength = [exceptionStackTrace count];

// re-size stack trace and format results. Starting at index = 4 to not include the script-error API's
for (NSInteger i = 4; i <= (exceptionStackTraceLength >= 20 ? 20 : exceptionStackTraceLength); i++) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@garymathews This is causing an array out of bound exception:

var win = Ti.UI.createWindow({
    backgroundColor: '#fff',
    homeIndicatorAutoHidden : true
});
 
var scrollable = Ti.UI.createScrollableView({
});

win.add(scrollable);
win.open();

setTimeout(function () {
    scrollable.setViews([-1]);
}, 4000);

We should probably do i < xxxxx?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants