react-native/React/Base/RCTJSStackFrame.m
Alex Kotliarskyi e5650560c0 Class for JS stack frames instead of dictionaries
Summary:
Currently React Native codebase treats JS stack traces as array of dictionaries.

This diff switches the Red Box to use new `RCTJSStackFrame` for internal data representation, while keeping the exposed API unchanged. The next step would be to replace the rest of manual parsing and usage of dictionaries.

The new class has ability to parse the stack from raw strings or dictionaries.

Depends on D3429031

Reviewed By: javache

Differential Revision: D3473199

fbshipit-source-id: 90d2a4f5e8e054b75c99905f35c2ee54927bb311
2016-07-11 06:13:32 -07:00

102 lines
3.0 KiB
Objective-C

/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import "RCTJSStackFrame.h"
#import "RCTLog.h"
static NSRegularExpression *RCTJSStackFrameRegex()
{
static dispatch_once_t onceToken;
static NSRegularExpression *_regex;
dispatch_once(&onceToken, ^{
NSError *regexError;
_regex = [NSRegularExpression regularExpressionWithPattern:@"^([^@]+)@(.*):(\\d+):(\\d+)$" options:0 error:&regexError];
if (regexError) {
RCTLogError(@"Failed to build regex: %@", [regexError localizedDescription]);
}
});
return _regex;
}
@implementation RCTJSStackFrame
- (instancetype)initWithMethodName:(NSString *)methodName file:(NSString *)file lineNumber:(NSInteger)lineNumber column:(NSInteger)column
{
if (self = [super init]) {
_methodName = methodName;
_file = file;
_lineNumber = lineNumber;
_column = column;
}
return self;
}
- (NSDictionary *)toDictionary
{
return @{
@"methodName": self.methodName,
@"file": self.file,
@"lineNumber": @(self.lineNumber),
@"column": @(self.column)
};
}
+ (instancetype)stackFrameWithLine:(NSString *)line
{
NSTextCheckingResult *match = [RCTJSStackFrameRegex() firstMatchInString:line options:0 range:NSMakeRange(0, line.length)];
if (!match) {
return nil;
}
NSString *methodName = [line substringWithRange:[match rangeAtIndex:1]];
NSString *file = [line substringWithRange:[match rangeAtIndex:2]];
NSString *lineNumber = [line substringWithRange:[match rangeAtIndex:3]];
NSString *column = [line substringWithRange:[match rangeAtIndex:4]];
return [[self alloc] initWithMethodName:methodName
file:file
lineNumber:[lineNumber integerValue]
column:[column integerValue]];
}
+ (instancetype)stackFrameWithDictionary:(NSDictionary *)dict
{
return [[self alloc] initWithMethodName:dict[@"methodName"]
file:dict[@"file"]
lineNumber:[dict[@"lineNumber"] integerValue]
column:[dict[@"column"] integerValue]];
}
+ (NSArray<RCTJSStackFrame *> *)stackFramesWithLines:(NSString *)lines
{
NSMutableArray *stack = [NSMutableArray new];
for (NSString *line in [lines componentsSeparatedByString:@"\n"]) {
RCTJSStackFrame *frame = [self stackFrameWithLine:line];
if (frame) {
[stack addObject:frame];
}
}
return stack;
}
+ (NSArray<RCTJSStackFrame *> *)stackFramesWithDictionaries:(NSArray<NSDictionary *> *)dicts
{
NSMutableArray *stack = [NSMutableArray new];
for (NSDictionary *dict in dicts) {
RCTJSStackFrame *frame = [self stackFrameWithDictionary:dict];
if (frame) {
[stack addObject:frame];
}
}
return stack;
}
@end