1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 | #import "RCTJSStackFrame.h"
|
9 |
|
10 | #import "RCTLog.h"
|
11 | #import "RCTUtils.h"
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 |
|
23 |
|
24 |
|
25 |
|
26 |
|
27 |
|
28 |
|
29 |
|
30 |
|
31 |
|
32 | static 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:®exError];
|
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
|