blob: 8f1931bc216403a4a3e57f92bde71e6c20220aef [file] [log] [blame]
// Copyright (c) 2013, 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.
library frame;
import 'package:pathos/path.dart' as path;
import 'trace.dart';
import 'utils.dart';
final _nativeFrameRegExp = new RegExp(
r'^#\d+\s+([^\s].*) \((.+):(\d+):(\d+)\)$');
/// A single stack frame. Each frame points to a precise location in Dart code.
class Frame {
/// The URI of the file in which the code is located.
///
/// This URI will usually have the scheme `dart`, `file`, `http`, or `https`.
final Uri uri;
/// The line number on which the code location is located.
///
/// This can be null, indicating that the line number is unknown or
/// unimportant.
final int line;
/// The column number of the code location.
///
/// This can be null, indicating that the column number is unknown or
/// unimportant.
final int column;
/// The name of the member in which the code location occurs.
///
/// Anonymous closures are represented as `<fn>` in this member string.
final String member;
/// Whether this stack frame comes from the Dart core libraries.
bool get isCore => uri.scheme == 'dart';
/// Returns a human-friendly description of the library that this stack frame
/// comes from.
///
/// This will usually be the string form of [uri], but a relative path will be
/// used if possible.
String get library {
// TODO(nweiz): handle relative URIs here as well once pathos supports that.
if (uri.scheme != 'file') return uri.toString();
return path.relative(fileUriToPath(uri));
}
/// Returns the name of the package this stack frame comes from, or `null` if
/// this stack frame doesn't come from a `package:` URL.
String get package {
if (uri.scheme != 'package') return null;
return uri.path.split('/').first;
}
/// A human-friendly description of the code location.
String get location {
if (line == null || column == null) return library;
return '$library $line:$column';
}
/// Returns a single frame of the current stack.
///
/// By default, this will return the frame above the current method. If
/// [level] is `0`, it will return the current method's frame; if [level] is
/// higher than `1`, it will return higher frames.
factory Frame.caller([int level=1]) {
if (level < 0) {
throw new ArgumentError("Argument [level] must be greater than or equal "
"to 0.");
}
return new Trace.current(level + 1).frames.first;
}
/// Parses a string representation of a stack frame.
///
/// [frame] should be formatted in the same way as a native stack trace frame.
factory Frame.parse(String frame) {
var match = _nativeFrameRegExp.firstMatch(frame);
if (match == null) {
throw new FormatException("Couldn't parse stack trace line '$frame'.");
}
var uri = Uri.parse(match[2]);
var member = match[1].replaceAll("<anonymous closure>", "<fn>");
return new Frame(uri, int.parse(match[3]), int.parse(match[4]), member);
}
Frame(this.uri, this.line, this.column, this.member);
String toString() => '$location in $member';
}