UNPKG

4.53 kBPlain TextView Raw
1/*
2 * Copyright (c) Facebook, Inc. and its affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 */
7
8#import "RCTJSStackFrame.h"
9
10#import "RCTLog.h"
11#import "RCTUtils.h"
12
13/**
14* The RegEx used to parse Error.stack.
15*
16* JavaScriptCore has the following format:
17*
18* Exception: Error: argh
19* func1@/path/to/file.js:2:18
20* func2@/path/to/file.js:6:8
21* eval@[native code]
22* global code@/path/to/file.js:13:5
23*
24* Another supported format:
25*
26* Error: argh
27* at func1 (/path/to/file.js:2:18)
28* at func2 (/path/to/file.js:6:8)
29* at eval (native)
30* at global (/path/to/file.js:13:5)
31*/
32static NSRegularExpression *RCTJSStackFrameRegex()
33{
34 static dispatch_once_t onceToken;
35 static NSRegularExpression *_regex;
36 dispatch_once(&onceToken, ^{
37 NSString *pattern =
38 @"\\s*(?:at)?\\s*" // Skip leading "at" and whitespace, noncapturing
39 @"(.+?)" // Capture the function name (group 1)
40 @"\\s*[@(]" // Skip whitespace, then @ or (
41 @"(.*):" // Capture the file name (group 2), then colon
42 @"(\\d+):(\\d+)" // Line and column number (groups 3 and 4)
43 @"\\)?$" // Optional closing paren and EOL
44 ;
45 NSError *regexError;
46 _regex = [NSRegularExpression regularExpressionWithPattern:pattern options:0 error:&regexError];
47 if (regexError) {
48 RCTLogError(@"Failed to build regex: %@", [regexError localizedDescription]);
49 }
50 });
51 return _regex;
52}
53
54@implementation RCTJSStackFrame
55
56- (instancetype)initWithMethodName:(NSString *)methodName file:(NSString *)file lineNumber:(NSInteger)lineNumber column:(NSInteger)column collapse:(NSInteger)collapse
57{
58 if (self = [super init]) {
59 _methodName = methodName;
60 _file = file;
61 _lineNumber = lineNumber;
62 _column = column;
63 _collapse = collapse;
64 }
65 return self;
66}
67
68- (NSDictionary *)toDictionary
69{
70 return @{
71 @"methodName": RCTNullIfNil(self.methodName),
72 @"file": RCTNullIfNil(self.file),
73 @"lineNumber": @(self.lineNumber),
74 @"column": @(self.column),
75 @"collapse": @(self.collapse)
76 };
77}
78
79+ (instancetype)stackFrameWithLine:(NSString *)line
80{
81 NSTextCheckingResult *match = [RCTJSStackFrameRegex() firstMatchInString:line options:0 range:NSMakeRange(0, line.length)];
82 if (!match) {
83 return nil;
84 }
85
86 // methodName may not be present for e.g. anonymous functions
87 const NSRange methodNameRange = [match rangeAtIndex:1];
88 NSString *methodName = methodNameRange.location == NSNotFound ? nil : [line substringWithRange:methodNameRange];
89 NSString *file = [line substringWithRange:[match rangeAtIndex:2]];
90 NSString *lineNumber = [line substringWithRange:[match rangeAtIndex:3]];
91 NSString *column = [line substringWithRange:[match rangeAtIndex:4]];
92
93 return [[self alloc] initWithMethodName:methodName
94 file:file
95 lineNumber:[lineNumber integerValue]
96 column:[column integerValue]
97 collapse:@NO];
98}
99
100+ (instancetype)stackFrameWithDictionary:(NSDictionary *)dict
101{
102 return [[self alloc] initWithMethodName:RCTNilIfNull(dict[@"methodName"])
103 file:dict[@"file"]
104 lineNumber:[RCTNilIfNull(dict[@"lineNumber"]) integerValue]
105 column:[RCTNilIfNull(dict[@"column"]) integerValue]
106 collapse:[RCTNilIfNull(dict[@"collapse"]) integerValue]];
107}
108
109+ (NSArray<RCTJSStackFrame *> *)stackFramesWithLines:(NSString *)lines
110{
111 NSMutableArray *stack = [NSMutableArray new];
112 for (NSString *line in [lines componentsSeparatedByString:@"\n"]) {
113 RCTJSStackFrame *frame = [self stackFrameWithLine:line];
114 if (frame) {
115 [stack addObject:frame];
116 }
117 }
118 return stack;
119}
120
121+ (NSArray<RCTJSStackFrame *> *)stackFramesWithDictionaries:(NSArray<NSDictionary *> *)dicts
122{
123 NSMutableArray *stack = [NSMutableArray new];
124 for (NSDictionary *dict in dicts) {
125 RCTJSStackFrame *frame = [self stackFrameWithDictionary:dict];
126 if (frame) {
127 [stack addObject:frame];
128 }
129 }
130 return stack;
131}
132
133- (NSString *)description {
134 return [NSString stringWithFormat:@"<%@: %p method name: %@; file name: %@; line: %ld; column: %ld>",
135 self.class,
136 self,
137 self.methodName,
138 self.file,
139 (long)self.lineNumber,
140 (long)self.column];
141}
142
143@end