Skip to content
This repository has been archived by the owner on Aug 1, 2019. It is now read-only.

Commit

Permalink
add getSourceReport to VMServiceReference
Browse files Browse the repository at this point in the history
  • Loading branch information
kevmoo committed May 20, 2016
1 parent a06a8a3 commit 167eb9c
Show file tree
Hide file tree
Showing 9 changed files with 448 additions and 8 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
## 0.2.2

* Add `getSourceReport` to `VMIsolateRef` and `VMScriptRef`, which return a
`VMSourceReport` for the target isolate or just the target script
respectively.

## 0.2.1

* `VMScriptToken.offset` is deprecated. This never returned the documented value
Expand Down
33 changes: 33 additions & 0 deletions lib/src/isolate.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import 'library.dart';
import 'pause_event.dart';
import 'scope.dart';
import 'sentinel.dart';
import 'source_report.dart';
import 'stack.dart';
import 'stream_manager.dart';
import 'utils.dart';
Expand Down Expand Up @@ -343,6 +344,38 @@ class VMIsolateRef {
return _scope.sendRequest(method, params);
}

/// Generates a report of code coverage information and possible break points
/// for the scripts in this isolate.
///
/// If [includeCoverageReport] is `true`, the report includes code coverage
/// information via [VMSourceReportRange.hits] and
/// [VMSourceReportRange.misses] in [VMSourceReport.ranges]. Otherwise,
/// [VMSourceReportRange.coverage] is `null`.
///
/// If [includePossibleBreakpoints] is `true`, the report includes a list of
/// token positions which correspond to possible breakpoints via
/// [VMSourceReportRange.possibleBreakpoints] in [VMSourceReport.ranges].
/// Otherwise, [VMSourceReportRange.possibleBreakpoints] is `null`.
///
/// If [forceCompile] is `true`, all functions in the range of the report
/// will be compiled. If `false`, functions that are never used may not appear
/// in [VMSourceReportRange.misses]. Forcing compilation can cause a
/// compilation error, which could terminate the running Dart program.
Future<VMSourceReport> getSourceReport(
{bool includeCoverageReport: true,
bool includePossibleBreakpoints: true,
bool forceCompile: false}) async {
var reports = <String>[];
if (includeCoverageReport) reports.add('Coverage');
if (includePossibleBreakpoints) reports.add('PossibleBreakpoints');

var params = {'reports': reports};
if (forceCompile) params['forceCompile'] = true;

var json = await _scope.sendRequest('getSourceReport', params);
return newSourceReport(_scope, json);
}

bool operator ==(other) => other is VMIsolateRef &&
other._scope.isolateId == _scope.isolateId;

Expand Down
1 change: 0 additions & 1 deletion lib/src/pause_event.dart
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,6 @@ class VMPauseInterruptedEvent extends VMPauseEvent {
String toString() => "pause on interrupt";
}

/// An event indicating that an isolate was paused due to an exception.
/// An event indicating that an isolate was paused due to an exception.
class VMPauseExceptionEvent extends VMPauseEvent {
/// The exception that caused the isolate to become paused.
Expand Down
50 changes: 50 additions & 0 deletions lib/src/script.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import 'library.dart';
import 'object.dart';
import 'scope.dart';
import 'source_location.dart';
import 'source_report.dart';

VMScriptRef newVMScriptRef(Scope scope, Map json) {
if (json == null) return null;
Expand All @@ -27,6 +28,11 @@ VMScriptToken newVMScriptToken(String isolateId, String scriptId,
return new VMScriptToken._(isolateId, scriptId, position);
}

VMScriptToken newVMScriptTokenFromPosition(VMScriptRef script, int position) {
if (position == null) return null;
return new VMScriptToken._(script._scope.isolateId, script._id, position);
}

/// A reference to a script in the Dart VM.
///
/// A script contains information about the actual text of a library. Usually
Expand Down Expand Up @@ -70,6 +76,50 @@ class VMScriptRef implements VMObjectRef {
}
}

/// Generates a set of reports tied to this script.
///
/// If [includeCoverageReport] is `true`, the report includes code coverage
/// information via [VMSourceReportRange.coverage] in
/// [VMSourceReport.ranges].
/// Otherwise, [VMSourceReportRange.coverage] is `null`.
///
/// If [includePossibleBreakpoints] is `true`, the report includes a list of
/// token positions which correspond to possible breakpoints via
/// [VMSourceReportRange.possibleBreakpoints] in [VMSourceReport.ranges].
/// Otherwise, [VMSourceReportRange.possibleBreakpoints] is `null`.
///
/// If [forceCompile] is `true`, all functions in the range of the report
/// will be compiled. If `false`, functions that are never used may not appear
/// in [VMSourceReportRange.misses].
/// Forcing compilation can cause a compilation error, which could terminate
/// the running Dart program.
///
/// [location] can be provided to restrict analysis to a subrange of the
/// script. An [ArgumentError] is thrown if `location.end` is `null`.
Future<VMSourceReport> getSourceReport(
{bool includeCoverageReport: true,
bool includePossibleBreakpoints: true,
bool forceCompile: false,
VMSourceLocation location}) async {
var reports = <String>[];
if (includeCoverageReport) reports.add('Coverage');
if (includePossibleBreakpoints) reports.add('PossibleBreakpoints');

var params = <String, dynamic>{'scriptId': _id, 'reports': reports};
if (forceCompile) params['forceCompile'] = true;
if (location != null) {
if (location.end == null) {
throw new ArgumentError.value(
location, 'location', 'location.end cannot be null.');
}
params['tokenPos'] = location.token._position;
params['endTokenPos'] = location.end._position;
}

var json = await _scope.sendRequest('getSourceReport', params);
return newSourceReport(_scope, json);
}

bool operator ==(other) => other is VMScriptRef &&
(_fixedId ? _id == other._id : super == other);

Expand Down
13 changes: 11 additions & 2 deletions lib/src/source_location.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,16 @@ import 'breakpoint.dart';
import 'scope.dart';
import 'script.dart';

VMSourceLocation newVMSourceLocation(Scope scope,
Map json) {
VMSourceLocation newVMSourceLocation(Scope scope, Map json) {
if (json == null) return null;
assert(json["type"] == "SourceLocation");
return new VMSourceLocation._(scope, json);
}

VMSourceLocation newVMSourceLocationFromPosition(
VMScriptRef script, int tokenPos, int endTokenPos) =>
new VMSourceLocation._fromPositions(script, tokenPos, endTokenPos);

/// A location or span of code in a Dart script.
class VMSourceLocation implements VMBreakpointLocation {
/// The script containing the source location.
Expand All @@ -34,6 +37,12 @@ class VMSourceLocation implements VMBreakpointLocation {
end = newVMScriptToken(
scope.isolateId, json["script"]["id"], json["endTokenPos"]);

VMSourceLocation._fromPositions(
VMScriptRef script, int tokenPos, int endTokenPos)
: this.script = script,
token = newVMScriptTokenFromPosition(script, tokenPos),
end = newVMScriptTokenFromPosition(script, endTokenPos);

String toString() =>
end == null ? "$script at $token" : "$script from $token to $end";
}
120 changes: 120 additions & 0 deletions lib/src/source_report.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
// Copyright (c) 2016, 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.

import 'script.dart';
import 'scope.dart';
import 'source_location.dart';

VMSourceReport newSourceReport(Scope scope, Map json) {
if (json == null) return null;
assert(json["type"] == "SourceReport");

var scripts = (json['scripts'] as List)
.map((script) => newVMScriptRef(scope, script))
.toList();

var ranges = new List<VMSourceReportRange>.unmodifiable(
(json['ranges'] as List).map((rangeItem) =>
new VMSourceReportRange._fromJson(rangeItem, scripts)));

return new VMSourceReport._(ranges);
}

/// A report about ranges of source code.
///
/// [ranges] exposes code coverage and information on possible breakpoints.
class VMSourceReport {
/// Ranges in the program source corresponding to ranges of executable code in
/// the user's program.
///
/// Ranges can include members such as functions, methods, and
/// constructors.
///
/// Note: ranges may nest in other ranges, in the case of nested functions.
/// Ranges may be duplicated, in the case of mixins.
final List<VMSourceReportRange> ranges;

VMSourceReport._(this.ranges);
}

/// A range of executable code (function, method, constructor, etc)
/// in the running program.
///
/// This is part of a [VMSourceReport].
class VMSourceReportRange {
VMScriptRef get script => location.script;

/// The location of the range in the target script.
final VMSourceLocation location;

/// `true` if this range been compiled by the Dart VM.
///
/// If `false`, `possibleBreakpoints`, `hits`, and `misses` will be `null`.
///
/// Use `forceCompile` in `getSourceReport` to ensure the target scripts are
/// compiled.
final bool compiled;

/// Token positions at which breakpoints can be set.
///
/// Provided only when `includePossibleBreakpoints` in `getSourceReport`
/// is `true` and the range has been compiled.
final List<VMScriptToken> possibleBreakpoints;

/// Token positions in this range which have been executed.
///
/// Provided only when `includeCoverageReport` in `getSourceReport` is `true`
/// and the range has been compiled.
///
/// Sorted by the position in the source file, starting with the first hit.
final List<VMScriptToken> hits;

/// Token positions in this range which have not yet been executed.
///
/// Provided only when `includeCoverageReport` in `getSourceReport` is `true`
/// and the range has been compiled.
///
/// Sorted by the location in the source file, starting with the first miss.
final List<VMScriptToken> misses;

/// Creates a new [VMSourceReportRange].
///
/// [json] corresponds to data returned by the `getSourceReport` RPC.
///
/// [scripts] is used to find the [VMScriptRef] referenced by index in [json].
factory VMSourceReportRange._fromJson(Map json, List<VMScriptRef> scripts) {
var script = scripts[json['scriptIndex']];

var location = newVMSourceLocationFromPosition(
script, json['startPos'], json['endPos']);

var compiled = json['compiled'];

var hits = json['coverage'] == null
? null
: _getTokens(script, json['coverage']['hits']);

var misses = json['coverage'] == null
? null
: _getTokens(script, json['coverage']['misses']);

var possibleBreakpoints = json['possibleBreakpoints'] == null
? null
: _getTokens(script, json['possibleBreakpoints']);

return new VMSourceReportRange._(
compiled, hits, misses, possibleBreakpoints, location);
}

VMSourceReportRange._(this.compiled, this.hits, this.misses,
this.possibleBreakpoints, this.location);

/// Returns an unmodifiable [List<VMScriptToken>] corresponding to the
/// provided token [locations].
static List<VMScriptToken> _getTokens(
VMScriptRef script, Iterable<int> locations) {
return new List<VMScriptToken>.unmodifiable(locations
.map((position) => newVMScriptTokenFromPosition(script, position)));
}
}
9 changes: 6 additions & 3 deletions lib/vm_service_client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,13 @@ export 'src/library.dart' hide newVMLibraryRef;
export 'src/message.dart' hide newVMMessage;
export 'src/object.dart';
export 'src/pause_event.dart' hide newVMPauseEvent;
export 'src/script.dart' hide newVMScriptRef, newVMScriptToken;
export 'src/script.dart' hide newVMScriptRef, newVMScriptToken,
newVMScriptTokenFromPosition;
export 'src/sentinel.dart' hide newVMSentinel;
export 'src/service_version.dart' hide newVMServiceVersion;
export 'src/source_location.dart' hide newVMSourceLocation;
export 'src/source_location.dart' hide newVMSourceLocation,
newVMSourceLocationFromPosition;
export 'src/source_report.dart' hide newSourceReport;
export 'src/stack.dart' hide newVMStack;
export 'src/type_arguments.dart' hide newVMTypeArgumentsRef;
export 'src/unresolved_source_location.dart' hide newVMUnresolvedSourceLocation;
Expand Down Expand Up @@ -127,7 +130,7 @@ class VMServiceClient {
/// This is useful when using the client over a pre-existing connection. To
/// establish a connection from scratch, use [connect].
factory VMServiceClient(StreamChannel<String> channel) =>
new VMServiceClient.withoutJson(channel.transform(jsonDocument));
new VMServiceClient.withoutJson(jsonDocument.bind(channel));

/// Creates a client that reads incoming decoded messages from [incoming] and
/// writes outgoing decoded messages to [outgoing].
Expand Down
4 changes: 2 additions & 2 deletions pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
name: vm_service_client
version: 0.2.1
version: 0.2.2
description: A client for the Dart VM service.
author: Dart Team <[email protected]>
homepage: https://github.com/dart-lang/vm_service_client

environment:
sdk: '>=1.13.0 <2.0.0'
sdk: '>=1.16.0 <2.0.0'

dependencies:
async: '^1.7.0'
Expand Down
Loading

0 comments on commit 167eb9c

Please sign in to comment.