Skip to content

Commit

Permalink
fix(ios): convert native stack to array and print native error reason
Browse files Browse the repository at this point in the history
  • Loading branch information
janvennemann authored and sgtcoolguy committed May 27, 2021
1 parent 5759752 commit b5d97f2
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 15 deletions.
19 changes: 12 additions & 7 deletions iphone/TitaniumKit/TitaniumKit/Sources/API/TiBindingTiValue.m
Original file line number Diff line number Diff line change
Expand Up @@ -279,21 +279,23 @@ JSValueRef TiBindingTiValueFromNSObject(JSContextRef jsContext, NSObject *obj)
JSValueRef result = JSValueMakeString(jsContext, jsString);
JSStringRelease(jsString);
JSObjectRef excObject = JSObjectMakeError(jsContext, 1, &result, NULL);
JSContext *objcContext = [JSContext contextWithJSGlobalContextRef:JSContextGetGlobalContext(jsContext)];
JSValue *excValue = [JSValue valueWithJSValueRef:excObject inContext:objcContext];
NSDictionary *details = [(NSException *)obj userInfo];

// Add "nativeReason" key
NSString *subreason = [details objectForKey:kTiExceptionSubreason];
if (subreason != nil) {
JSStringRef propertyName = JSStringCreateWithUTF8CString("nativeReason");
JSStringRef valueString = JSStringCreateWithCFString((CFStringRef)subreason);
JSObjectSetProperty(jsContext, excObject, propertyName, JSValueMakeString(jsContext, valueString), kJSPropertyAttributeReadOnly, NULL);
JSStringRelease(propertyName);
JSStringRelease(valueString);
NSString *message = [excValue[@"message"] toString];
NSString *format = [message hasSuffix:@"."] ? @"%@ %@" : @"%@. %@";
message = [NSString stringWithFormat:format, message, subreason];
excValue[@"message"] = [JSValue valueWithObject:message inContext:objcContext];
excValue[@"nativeReason"] = [JSValue valueWithObject:subreason inContext:objcContext];
}

// Add "nativeStack" key
NSArray<NSString *> *nativeStack = [(NSException *)obj callStackSymbols];
NSInteger startIndex = 3; // Starting at index = 4 to not include the script-error API's
NSInteger startIndex = 0;
if (nativeStack == nil) { // the exception was created, but never thrown, so grab the current stack frames
nativeStack = [NSThread callStackSymbols]; // this happens when we construct an exception manually in our ENSURE macros for obj-c proxies
startIndex = 2; // drop ObjcProxy.throwException and this method from frames
Expand All @@ -306,7 +308,10 @@ JSValueRef TiBindingTiValueFromNSObject(JSContextRef jsContext, NSObject *obj)

// re-size stack trace and format results
for (NSUInteger i = startIndex; i < endIndex; i++) {
NSString *line = [[nativeStack objectAtIndex:i] stringByReplacingOccurrencesOfString:@" " withString:@""];
NSString *line = [nativeStack objectAtIndex:i];
while ([line rangeOfString:@" "].location != NSNotFound) {
line = [line stringByReplacingOccurrencesOfString:@" " withString:@" "];
}
[formattedStackTrace addObject:line];
}

Expand Down
12 changes: 6 additions & 6 deletions iphone/TitaniumKit/TitaniumKit/Sources/API/TiExceptionHandler.m
Original file line number Diff line number Diff line change
Expand Up @@ -143,9 +143,6 @@ - (id)initWithMessage:(NSString *)message sourceURL:(NSString *)sourceURL lineNo
- (id)initWithDictionary:(NSDictionary *)dictionary
{
NSString *message = [[dictionary objectForKey:@"message"] description];
if (message == nil) {
message = [[dictionary objectForKey:@"nativeReason"] description];
}
NSString *sourceURL = [[dictionary objectForKey:@"sourceURL"] description];
NSInteger lineNo = [[dictionary objectForKey:@"line"] integerValue];

Expand Down Expand Up @@ -225,13 +222,16 @@ - (NSString *)detailedDescription
}

NSArray<NSString *> *stackTrace = self.nativeStack;
NSInteger startIndex = 0;
if (stackTrace == nil) {
stackTrace = [NSThread callStackSymbols];
// starting at index = 2 to not include this method and callee
startIndex = 2;
}
NSMutableArray<NSString *> *formattedStackTrace = [[[NSMutableArray alloc] init] autorelease];
NSUInteger stackTraceLength = [stackTrace count];
// re-size stack trace and format results. starting at index = 2 to not include this method and callee
for (NSInteger i = 2; i < (stackTraceLength >= 20 ? 20 : stackTraceLength); i++) {
NSUInteger stackTraceLength = MIN([stackTrace count], 20 + startIndex);
// re-size stack trace and format results.
for (NSInteger i = startIndex; i < stackTraceLength; i++) {
NSString *line = [self removeWhitespace:stackTrace[i]];
// remove stack index
line = [line substringFromIndex:[line rangeOfString:@" "].location + 1];
Expand Down
6 changes: 4 additions & 2 deletions iphone/TitaniumKit/TitaniumKit/Sources/API/TiUtils.m
Original file line number Diff line number Diff line change
Expand Up @@ -1340,8 +1340,10 @@ + (TiScriptError *)scriptErrorFromValueRef:(JSValueRef)valueRef inContext:(JSGlo
if ([error hasProperty:@"stack"]) {
[errorDict setObject:[error[@"stack"] toString] forKey:@"stack"];
}
if ([error hasProperty:@"nativeStack"]) {
[errorDict setObject:[error[@"nativeStack"] toString] forKey:@"nativeStack"];
if ([error hasProperty:@"nativeStack"] && error[@"nativeStack"].isString) {
NSString *callStack = error[@"nativeStack"].toString;
NSArray<NSString *> *nativeStack = [callStack componentsSeparatedByCharactersInSet:NSCharacterSet.newlineCharacterSet];
[errorDict setObject:nativeStack forKey:@"nativeStack"];
}

return [[[TiScriptError alloc] initWithDictionary:errorDict] autorelease];
Expand Down
27 changes: 27 additions & 0 deletions tests/Resources/error.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -116,4 +116,31 @@ describe('Error', function () {
should(jsonTable.error.message).be.eql(errorTop.message);
should(jsonTable.error.nestedError.message).be.eql(errorTop.nestedError.message);
});

it.ios('should include native reason in message', () => {
try {
Ti.UI.createView({
top: 20,
backgroundGradient: {
type: 'invalid'
}
});
} catch (e) {
e.message.should.equal('Invalid type passed to function. Must be either \'linear\' or \'radial\'');
e.nativeReason.should.equal('Must be either \'linear\' or \'radial\'');
}
});

it.ios('should include full native call stack', () => {
try {
Ti.UI.createView({
top: 20,
backgroundGradient: {
type: 'invalid'
}
});
} catch (e) {
e.nativeStack.should.match(/TiGradient setType:/);
}
});
});

0 comments on commit b5d97f2

Please sign in to comment.