| // Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file | |
| // for details. All rights reserved. Use of this source code is governed by a | |
| // BSD-style license that can be found in the LICENSE file. | |
| /// Representation of stack traces and logic to parse d8 stack traces. | |
| library; | |
| // TODO(sigmund): we should delete this implementation and instead: | |
| // - switch to use the stack_trace package | |
| // - add support non-d8 frames | |
| // - add support for secondary regexps to detect stranger frames (like eval frames) | |
| import 'package:path/path.dart' as p; | |
| import 'util.dart'; | |
| /// Represents a stack trace line. | |
| class StackTraceLine { | |
| final String? methodName; | |
| final String fileName; | |
| final int? lineNo; | |
| final int columnNo; | |
| StackTraceLine(this.methodName, this.fileName, this.lineNo, this.columnNo); | |
| /// Creates a [StackTraceLine] by parsing a d8 stack trace line [text]. The | |
| /// expected formats are | |
| /// | |
| /// at <methodName>(<fileName>:<lineNo>:<columnNo>) | |
| /// at <methodName>(<fileName>:<lineNo>) | |
| /// at <methodName>(<fileName>) | |
| /// at <fileName>:<lineNo>:<columnNo> | |
| /// at <fileName>:<lineNo> | |
| /// at <fileName> | |
| /// | |
| factory StackTraceLine.fromText(String text, {Logger? logger}) { | |
| text = text.trim(); | |
| assert(text.startsWith('at ')); | |
| text = text.substring('at '.length); | |
| String? methodName; | |
| int endParen = text.indexOf(')'); | |
| if (endParen > 0) { | |
| int nameEnd = text.indexOf('('); | |
| if (nameEnd != -1) { | |
| methodName = text.substring(0, nameEnd).trim(); | |
| text = text.substring(nameEnd + 1, endParen).trim(); | |
| } else { | |
| logger?.log('Missing left-paren in: $text'); | |
| } | |
| } | |
| int? lineNo; | |
| int? columnNo; | |
| String fileName; | |
| int lastColon = text.lastIndexOf(':'); | |
| if (lastColon != -1) { | |
| int? lastValue = int.tryParse(text.substring(lastColon + 1)); | |
| if (lastValue != null) { | |
| int secondToLastColon = text.lastIndexOf(':', lastColon - 1); | |
| if (secondToLastColon != -1) { | |
| int? secondToLastValue = int.tryParse( | |
| text.substring(secondToLastColon + 1, lastColon), | |
| ); | |
| if (secondToLastValue != null) { | |
| lineNo = secondToLastValue; | |
| columnNo = lastValue; | |
| fileName = text.substring(0, secondToLastColon); | |
| } else { | |
| lineNo = lastValue; | |
| fileName = text.substring(0, lastColon); | |
| } | |
| } else { | |
| lineNo = lastValue; | |
| fileName = text.substring(0, lastColon); | |
| } | |
| } else { | |
| fileName = text; | |
| } | |
| } else { | |
| fileName = text; | |
| } | |
| return StackTraceLine(methodName, fileName, lineNo, columnNo ?? 1); | |
| } | |
| @override | |
| String toString() { | |
| StringBuffer sb = StringBuffer(); | |
| sb.write(' at '); | |
| if (methodName != null) { | |
| sb.write(methodName); | |
| sb.write(' ('); | |
| sb.write(fileName); | |
| sb.write(':'); | |
| sb.write(lineNo); | |
| sb.write(':'); | |
| sb.write(columnNo); | |
| sb.write(')'); | |
| } else { | |
| sb.write(fileName); | |
| sb.write(':'); | |
| sb.write(lineNo); | |
| sb.write(':'); | |
| sb.write(columnNo); | |
| } | |
| return sb.toString(); | |
| } | |
| String get inlineString { | |
| StringBuffer sb = StringBuffer(); | |
| var padding = 20; | |
| var lineMethodName = methodName; | |
| if (lineMethodName != null) { | |
| sb.write(lineMethodName); | |
| padding -= (lineMethodName.length); | |
| if (padding <= 0) { | |
| sb.write('\n'); | |
| padding = 20; | |
| } | |
| } | |
| sb.write(' ' * padding); | |
| sb.write(p.url.basename(fileName)); | |
| sb.write(' '); | |
| sb.write(lineNo); | |
| sb.write(':'); | |
| sb.write(columnNo); | |
| return sb.toString(); | |
| } | |
| } | |
| List<StackTraceLine> parseStackTrace(String trace, {Logger? logger}) { | |
| List<String> lines = trace.split(RegExp(r'(\r|\n|\r\n)')); | |
| List<StackTraceLine> jsStackTrace = <StackTraceLine>[]; | |
| for (String line in lines) { | |
| line = line.trim(); | |
| if (line.startsWith('at ')) { | |
| jsStackTrace.add(StackTraceLine.fromText(line, logger: logger)); | |
| } | |
| } | |
| return jsStackTrace; | |
| } | |
| /// Returns the portion of the output that corresponds to the error message. | |
| /// | |
| /// Note: some errors can span multiple lines. | |
| String? extractErrorMessage(String trace) { | |
| var firstStackFrame = trace.indexOf(RegExp('\n +at')); | |
| if (firstStackFrame == -1) return null; | |
| var errorMarker = trace.indexOf('^') + 1; | |
| return trace.substring(errorMarker, firstStackFrame).trim(); | |
| } |