LCOV - code coverage report
Current view: top level - stack_trace-1.10.0/lib/src - frame.dart (source / functions) Hit Total Coverage
Test: lcov.info Lines: 27 119 22.7 %
Date: 2021-11-28 14:37:50 Functions: 0 0 -

          Line data    Source code
       1             : // Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
       2             : // for details. All rights reserved. Use of this source code is governed by a
       3             : // BSD-style license that can be found in the LICENSE file.
       4             : 
       5             : import 'package:path/path.dart' as path;
       6             : 
       7             : import 'trace.dart';
       8             : import 'unparsed_frame.dart';
       9             : 
      10             : // #1      Foo._bar (file:///home/nweiz/code/stuff.dart:42:21)
      11             : // #1      Foo._bar (file:///home/nweiz/code/stuff.dart:42)
      12             : // #1      Foo._bar (file:///home/nweiz/code/stuff.dart)
      13          33 : final _vmFrame = RegExp(r'^#\d+\s+(\S.*) \((.+?)((?::\d+){0,2})\)$');
      14             : 
      15             : //     at Object.stringify (native)
      16             : //     at VW.call$0 (https://example.com/stuff.dart.js:560:28)
      17             : //     at VW.call$0 (eval as fn
      18             : //         (https://example.com/stuff.dart.js:560:28), efn:3:28)
      19             : //     at https://example.com/stuff.dart.js:560:28
      20           0 : final _v8Frame =
      21             :     RegExp(r'^\s*at (?:(\S.*?)(?: \[as [^\]]+\])? \((.*)\)|(.*))$');
      22             : 
      23             : // https://example.com/stuff.dart.js:560:28
      24             : // https://example.com/stuff.dart.js:560
      25           0 : final _v8UrlLocation = RegExp(r'^(.*?):(\d+)(?::(\d+))?$|native$');
      26             : 
      27             : // eval as function (https://example.com/stuff.dart.js:560:28), efn:3:28
      28             : // eval as function (https://example.com/stuff.dart.js:560:28)
      29             : // eval as function (eval as otherFunction
      30             : //     (https://example.com/stuff.dart.js:560:28))
      31           0 : final _v8EvalLocation =
      32             :     RegExp(r'^eval at (?:\S.*?) \((.*)\)(?:, .*?:\d+:\d+)?$');
      33             : 
      34             : // anonymous/<@https://example.com/stuff.js line 693 > Function:3:40
      35             : // anonymous/<@https://example.com/stuff.js line 693 > eval:3:40
      36           0 : final _firefoxEvalLocation =
      37             :     RegExp(r'(\S+)@(\S+) line (\d+) >.* (Function|eval):\d+:\d+');
      38             : 
      39             : // .VW.call$0@https://example.com/stuff.dart.js:560
      40             : // .VW.call$0("arg")@https://example.com/stuff.dart.js:560
      41             : // .VW.call$0/name<@https://example.com/stuff.dart.js:560
      42             : // .VW.call$0@https://example.com/stuff.dart.js:560:36
      43             : // https://example.com/stuff.dart.js:560
      44           0 : final _firefoxSafariFrame = RegExp(r'^'
      45             :     r'(?:' // Member description. Not present in some Safari frames.
      46             :     r'([^@(/]*)' // The actual name of the member.
      47             :     r'(?:\(.*\))?' // Arguments to the member, sometimes captured by Firefox.
      48             :     r'((?:/[^/]*)*)' // Extra characters indicating a nested closure.
      49             :     r'(?:\(.*\))?' // Arguments to the closure.
      50             :     r'@'
      51             :     r')?'
      52             :     r'(.*?)' // The frame's URL.
      53             :     r':'
      54             :     r'(\d*)' // The line number. Empty in Safari if it's unknown.
      55             :     r'(?::(\d*))?' // The column number. Not present in older browsers and
      56             :     // empty in Safari if it's unknown.
      57             :     r'$');
      58             : 
      59             : // foo/bar.dart 10:11 Foo._bar
      60             : // foo/bar.dart 10:11 (anonymous function).dart.fn
      61             : // https://dart.dev/foo/bar.dart Foo._bar
      62             : // data:... 10:11 Foo._bar
      63           0 : final _friendlyFrame = RegExp(r'^(\S+)(?: (\d+)(?::(\d+))?)?\s+([^\d].*)$');
      64             : 
      65             : /// A regular expression that matches asynchronous member names generated by the
      66             : /// VM.
      67          33 : final _asyncBody = RegExp(r'<(<anonymous closure>|[^>]+)_async_body>');
      68             : 
      69           0 : final _initialDot = RegExp(r'^\.');
      70             : 
      71             : /// A single stack frame. Each frame points to a precise location in Dart code.
      72             : class Frame {
      73             :   /// The URI of the file in which the code is located.
      74             :   ///
      75             :   /// This URI will usually have the scheme `dart`, `file`, `http`, or `https`.
      76             :   final Uri uri;
      77             : 
      78             :   /// The line number on which the code location is located.
      79             :   ///
      80             :   /// This can be null, indicating that the line number is unknown or
      81             :   /// unimportant.
      82             :   final int? line;
      83             : 
      84             :   /// The column number of the code location.
      85             :   ///
      86             :   /// This can be null, indicating that the column number is unknown or
      87             :   /// unimportant.
      88             :   final int? column;
      89             : 
      90             :   /// The name of the member in which the code location occurs.
      91             :   ///
      92             :   /// Anonymous closures are represented as `<fn>` in this member string.
      93             :   final String? member;
      94             : 
      95             :   /// Whether this stack frame comes from the Dart core libraries.
      96          44 :   bool get isCore => uri.scheme == 'dart';
      97             : 
      98             :   /// Returns a human-friendly description of the library that this stack frame
      99             :   /// comes from.
     100             :   ///
     101             :   /// This will usually be the string form of [uri], but a relative URI will be
     102             :   /// used if possible. Data URIs will be truncated.
     103          11 :   String get library {
     104          33 :     if (uri.scheme == 'data') return 'data:...';
     105          22 :     return path.prettyUri(uri);
     106             :   }
     107             : 
     108             :   /// Returns the name of the package this stack frame comes from, or `null` if
     109             :   /// this stack frame doesn't come from a `package:` URL.
     110          11 :   String? get package {
     111          33 :     if (uri.scheme != 'package') return null;
     112          44 :     return uri.path.split('/').first;
     113             :   }
     114             : 
     115             :   /// A human-friendly description of the code location.
     116          11 :   String get location {
     117          13 :     if (line == null) return library;
     118          11 :     if (column == null) return '$library $line';
     119          44 :     return '$library $line:$column';
     120             :   }
     121             : 
     122             :   /// Returns a single frame of the current stack.
     123             :   ///
     124             :   /// By default, this will return the frame above the current method. If
     125             :   /// [level] is `0`, it will return the current method's frame; if [level] is
     126             :   /// higher than `1`, it will return higher frames.
     127           0 :   factory Frame.caller([int level = 1]) {
     128           0 :     if (level < 0) {
     129           0 :       throw ArgumentError('Argument [level] must be greater than or equal '
     130             :           'to 0.');
     131             :     }
     132             : 
     133           0 :     return Trace.current(level + 1).frames.first;
     134             :   }
     135             : 
     136             :   /// Parses a string representation of a Dart VM stack frame.
     137          33 :   factory Frame.parseVM(String frame) => _catchFormatException(frame, () {
     138             :         // The VM sometimes folds multiple stack frames together and replaces
     139             :         // them with "...".
     140          11 :         if (frame == '...') {
     141           0 :           return Frame(Uri(), null, null, '...');
     142             :         }
     143             : 
     144          22 :         var match = _vmFrame.firstMatch(frame);
     145           0 :         if (match == null) return UnparsedFrame(frame);
     146             : 
     147             :         // Get the pieces out of the regexp match. Function, URI and line should
     148             :         // always be found. The column is optional.
     149          11 :         var member = match[1]!
     150          22 :             .replaceAll(_asyncBody, '<async>')
     151          11 :             .replaceAll('<anonymous closure>', '<fn>');
     152          22 :         var uri = match[2]!.startsWith('<data:')
     153           0 :             ? Uri.dataFromString('')
     154          22 :             : Uri.parse(match[2]!);
     155             : 
     156          22 :         var lineAndColumn = match[3]!.split(':');
     157             :         var line =
     158          44 :             lineAndColumn.length > 1 ? int.parse(lineAndColumn[1]) : null;
     159             :         var column =
     160          44 :             lineAndColumn.length > 2 ? int.parse(lineAndColumn[2]) : null;
     161          11 :         return Frame(uri, line, column, member);
     162             :       });
     163             : 
     164             :   /// Parses a string representation of a Chrome/V8 stack frame.
     165           0 :   factory Frame.parseV8(String frame) => _catchFormatException(frame, () {
     166           0 :         var match = _v8Frame.firstMatch(frame);
     167           0 :         if (match == null) return UnparsedFrame(frame);
     168             : 
     169             :         // v8 location strings can be arbitrarily-nested, since it adds a layer
     170             :         // of nesting for each eval performed on that line.
     171           0 :         Frame parseLocation(String location, String member) {
     172           0 :           var evalMatch = _v8EvalLocation.firstMatch(location);
     173             :           while (evalMatch != null) {
     174           0 :             location = evalMatch[1]!;
     175           0 :             evalMatch = _v8EvalLocation.firstMatch(location);
     176             :           }
     177             : 
     178           0 :           if (location == 'native') {
     179           0 :             return Frame(Uri.parse('native'), null, null, member);
     180             :           }
     181             : 
     182           0 :           var urlMatch = _v8UrlLocation.firstMatch(location);
     183           0 :           if (urlMatch == null) return UnparsedFrame(frame);
     184             : 
     185           0 :           final uri = _uriOrPathToUri(urlMatch[1]!);
     186           0 :           final line = int.parse(urlMatch[2]!);
     187           0 :           final columnMatch = urlMatch[3];
     188           0 :           final column = columnMatch != null ? int.parse(columnMatch) : null;
     189           0 :           return Frame(uri, line, column, member);
     190             :         }
     191             : 
     192             :         // V8 stack frames can be in two forms.
     193           0 :         if (match[2] != null) {
     194             :           // The first form looks like " at FUNCTION (LOCATION)". V8 proper
     195             :           // lists anonymous functions within eval as "<anonymous>", while IE10
     196             :           // lists them as "Anonymous function".
     197             :           return parseLocation(
     198           0 :               match[2]!,
     199           0 :               match[1]!
     200           0 :                   .replaceAll('<anonymous>', '<fn>')
     201           0 :                   .replaceAll('Anonymous function', '<fn>')
     202           0 :                   .replaceAll('(anonymous function)', '<fn>'));
     203             :         } else {
     204             :           // The second form looks like " at LOCATION", and is used for
     205             :           // anonymous functions.
     206           0 :           return parseLocation(match[3]!, '<fn>');
     207             :         }
     208             :       });
     209             : 
     210             :   /// Parses a string representation of a JavaScriptCore stack trace.
     211           0 :   factory Frame.parseJSCore(String frame) => Frame.parseV8(frame);
     212             : 
     213             :   /// Parses a string representation of an IE stack frame.
     214             :   ///
     215             :   /// IE10+ frames look just like V8 frames. Prior to IE10, stack traces can't
     216             :   /// be retrieved.
     217           0 :   factory Frame.parseIE(String frame) => Frame.parseV8(frame);
     218             : 
     219             :   /// Parses a Firefox 'eval' or 'function' stack frame.
     220             :   ///
     221             :   /// for example:
     222             :   /// anonymous/<@https://example.com/stuff.js line 693 > Function:3:40
     223             :   /// anonymous/<@https://example.com/stuff.js line 693 > eval:3:40
     224           0 :   factory Frame._parseFirefoxEval(String frame) =>
     225           0 :       _catchFormatException(frame, () {
     226           0 :         final match = _firefoxEvalLocation.firstMatch(frame);
     227           0 :         if (match == null) return UnparsedFrame(frame);
     228           0 :         var member = match[1]!.replaceAll('/<', '');
     229           0 :         final uri = _uriOrPathToUri(match[2]!);
     230           0 :         final line = int.parse(match[3]!);
     231           0 :         if (member.isEmpty || member == 'anonymous') {
     232             :           member = '<fn>';
     233             :         }
     234           0 :         return Frame(uri, line, null, member);
     235             :       });
     236             : 
     237             :   /// Parses a string representation of a Firefox stack frame.
     238           0 :   factory Frame.parseFirefox(String frame) => _catchFormatException(frame, () {
     239           0 :         var match = _firefoxSafariFrame.firstMatch(frame);
     240           0 :         if (match == null) return UnparsedFrame(frame);
     241             : 
     242           0 :         if (match[3]!.contains(' line ')) {
     243           0 :           return Frame._parseFirefoxEval(frame);
     244             :         }
     245             : 
     246             :         // Normally this is a URI, but in a jsshell trace it can be a path.
     247           0 :         var uri = _uriOrPathToUri(match[3]!);
     248             : 
     249           0 :         var member = match[1];
     250             :         if (member != null) {
     251           0 :           member +=
     252           0 :               List.filled('/'.allMatches(match[2]!).length, '.<fn>').join();
     253           0 :           if (member == '') member = '<fn>';
     254             : 
     255             :           // Some Firefox members have initial dots. We remove them for
     256             :           // consistency with other platforms.
     257           0 :           member = member.replaceFirst(_initialDot, '');
     258             :         } else {
     259             :           member = '<fn>';
     260             :         }
     261             : 
     262           0 :         var line = match[4] == '' ? null : int.parse(match[4]!);
     263             :         var column =
     264           0 :             match[5] == null || match[5] == '' ? null : int.parse(match[5]!);
     265           0 :         return Frame(uri, line, column, member);
     266             :       });
     267             : 
     268             :   /// Parses a string representation of a Safari 6.0 stack frame.
     269             :   @Deprecated('Use Frame.parseSafari instead.')
     270           0 :   factory Frame.parseSafari6_0(String frame) => Frame.parseFirefox(frame);
     271             : 
     272             :   /// Parses a string representation of a Safari 6.1+ stack frame.
     273             :   @Deprecated('Use Frame.parseSafari instead.')
     274           0 :   factory Frame.parseSafari6_1(String frame) => Frame.parseFirefox(frame);
     275             : 
     276             :   /// Parses a string representation of a Safari stack frame.
     277           0 :   factory Frame.parseSafari(String frame) => Frame.parseFirefox(frame);
     278             : 
     279             :   /// Parses this package's string representation of a stack frame.
     280           0 :   factory Frame.parseFriendly(String frame) => _catchFormatException(frame, () {
     281           0 :         var match = _friendlyFrame.firstMatch(frame);
     282             :         if (match == null) {
     283           0 :           throw FormatException(
     284           0 :               "Couldn't parse package:stack_trace stack trace line '$frame'.");
     285             :         }
     286             :         // Fake truncated data urls generated by the friendly stack trace format
     287             :         // cause Uri.parse to throw an exception so we have to special case
     288             :         // them.
     289           0 :         var uri = match[1] == 'data:...'
     290           0 :             ? Uri.dataFromString('')
     291           0 :             : Uri.parse(match[1]!);
     292             :         // If there's no scheme, this is a relative URI. We should interpret it as
     293             :         // relative to the current working directory.
     294           0 :         if (uri.scheme == '') {
     295           0 :           uri = path.toUri(path.absolute(path.fromUri(uri)));
     296             :         }
     297             : 
     298           0 :         var line = match[2] == null ? null : int.parse(match[2]!);
     299           0 :         var column = match[3] == null ? null : int.parse(match[3]!);
     300           0 :         return Frame(uri, line, column, match[4]);
     301             :       });
     302             : 
     303             :   /// A regular expression matching an absolute URI.
     304           0 :   static final _uriRegExp = RegExp(r'^[a-zA-Z][-+.a-zA-Z\d]*://');
     305             : 
     306             :   /// A regular expression matching a Windows path.
     307           0 :   static final _windowsRegExp = RegExp(r'^([a-zA-Z]:[\\/]|\\\\)');
     308             : 
     309             :   /// Converts [uriOrPath], which can be a URI, a Windows path, or a Posix path,
     310             :   /// to a URI (absolute if possible).
     311           0 :   static Uri _uriOrPathToUri(String uriOrPath) {
     312           0 :     if (uriOrPath.contains(_uriRegExp)) {
     313           0 :       return Uri.parse(uriOrPath);
     314           0 :     } else if (uriOrPath.contains(_windowsRegExp)) {
     315           0 :       return Uri.file(uriOrPath, windows: true);
     316           0 :     } else if (uriOrPath.startsWith('/')) {
     317           0 :       return Uri.file(uriOrPath, windows: false);
     318             :     }
     319             : 
     320             :     // As far as I've seen, Firefox and V8 both always report absolute paths in
     321             :     // their stack frames. However, if we do get a relative path, we should
     322             :     // handle it gracefully.
     323           0 :     if (uriOrPath.contains('\\')) return path.windows.toUri(uriOrPath);
     324           0 :     return Uri.parse(uriOrPath);
     325             :   }
     326             : 
     327             :   /// Runs [body] and returns its result.
     328             :   ///
     329             :   /// If [body] throws a [FormatException], returns an [UnparsedFrame] with
     330             :   /// [text] instead.
     331          11 :   static Frame _catchFormatException(String text, Frame Function() body) {
     332             :     try {
     333             :       return body();
     334           0 :     } on FormatException catch (_) {
     335           0 :       return UnparsedFrame(text);
     336             :     }
     337             :   }
     338             : 
     339          11 :   Frame(this.uri, this.line, this.column, this.member);
     340             : 
     341           0 :   @override
     342           0 :   String toString() => '$location in $member';
     343             : }

Generated by: LCOV version 1.14