Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
57d185f
adding an example for testing
alan-knight Jun 14, 2019
e87e291
Merge branch 'master' into variables
alan-knight Jun 18, 2019
6675035
Setting up an example for scopes
alan-knight Jun 20, 2019
1cceebf
Merge branch 'master' into variables
alan-knight Jun 21, 2019
28ce43f
spike works
alan-knight Jun 25, 2019
0b02c7e
making things work better
alan-knight Jun 25, 2019
64a6121
Merge branch 'master' into variables
alan-knight Jun 25, 2019
d5a13c8
Merge branch 'master' into variables
alan-knight Jun 25, 2019
a24b113
part-way through trying to use fields
alan-knight Jun 26, 2019
246afe1
Cleaning up
alan-knight Jun 26, 2019
dda2222
Mostly clean
alan-knight Jun 26, 2019
129a644
really cleaned up
alan-knight Jun 26, 2019
9dd7547
Adding tests
alan-knight Jun 26, 2019
2943bd4
Writing tests - broke things
alan-knight Jun 27, 2019
d18c9e7
Test works
alan-knight Jul 8, 2019
9ba4f55
Cleaned up
alan-knight Jul 9, 2019
a9abbfd
dartfmt and remove unused import
alan-knight Jul 9, 2019
fcaaba6
Merge branch 'master' into variables
alan-knight Jul 9, 2019
d77dae9
Fix lints
alan-knight Jul 9, 2019
094889f
Adding end to end tests
alan-knight Jul 9, 2019
e2ee451
Generalize tests
alan-knight Jul 9, 2019
3a556eb
Changes from review comments
alan-knight Jul 9, 2019
db672cd
dartfmt
alan-knight Jul 10, 2019
c8efe59
Fix initialization issue
alan-knight Jul 10, 2019
9d17811
Merge branch 'master' into variables
alan-knight Jul 10, 2019
b22df55
Apparently some generated files changed
alan-knight Jul 10, 2019
c2c751c
Merge branch 'master' into variables
alan-knight Jul 10, 2019
4833ddf
dartfmt
alan-knight Jul 10, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions dwds/lib/data/connect_request.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 6 additions & 4 deletions dwds/lib/data/devtools_request.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions dwds/lib/data/isolate_events.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions dwds/lib/data/run_request.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions dwds/lib/src/chrome_proxy_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -567,6 +567,7 @@ const _stdoutTypes = ['log', 'info', 'warning'];
/// result.
void handleErrorIfPresent(WipResponse response,
{String evalContents, Object additionalDetails}) {
if (response == null) return;
if (response.result.containsKey('exceptionDetails')) {
throw ChromeDebugException(
response.result['exceptionDetails'] as Map<String, dynamic>,
Expand Down
60 changes: 52 additions & 8 deletions dwds/lib/src/debugger.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import 'chrome_proxy_service.dart';
import 'dart_uri.dart';
import 'domain.dart';
import 'location.dart';
import 'objects.dart';
import 'sources.dart';

/// Converts from ExceptionPauseMode strings to [PauseState] enums.
Expand Down Expand Up @@ -146,12 +147,13 @@ class Debugger extends Domain {
sources = Sources(_assetHandler);
// We must add a listener before enabling the debugger otherwise we will
// miss events.
_chromeDebugger.onScriptParsed.listen(sources.scriptParsed);
_chromeDebugger.onPaused.listen(_pauseHandler);
_chromeDebugger.onResumed.listen(_resumeHandler);
// Allow a null debugger/connection for unit tests.
_chromeDebugger?.onScriptParsed?.listen(sources.scriptParsed);
_chromeDebugger?.onPaused?.listen(_pauseHandler);
_chromeDebugger?.onResumed?.listen(_resumeHandler);

handleErrorIfPresent(await _tabConnection.page.enable() as WipResponse);
handleErrorIfPresent(await _chromeDebugger.enable() as WipResponse);
handleErrorIfPresent(await _tabConnection?.page?.enable() as WipResponse);
handleErrorIfPresent(await _chromeDebugger?.enable() as WipResponse);
}

/// Add a breakpoint at the given position.
Expand Down Expand Up @@ -268,10 +270,10 @@ class Debugger extends Domain {

/// Translates Chrome callFrames contained in [DebuggerPausedEvent] into Dart
/// [Frame]s.
List<Frame> _dartFramesFor(DebuggerPausedEvent e) {
Future<List<Frame>> dartFramesFor(List<Map<String, dynamic>> frames) async {
var dartFrames = <Frame>[];
var index = 0;
for (var frame in e.params['callFrames']) {
for (var frame in frames) {
var location = frame['location'];
var functionName = frame['functionName'] as String ?? '';
functionName = functionName.split('.').last;
Expand All @@ -282,12 +284,52 @@ class Debugger extends Domain {
if (dartFrame != null) {
dartFrame.code.name = functionName.isEmpty ? '<closure>' : functionName;
dartFrame.index = index++;
dartFrame.vars =
await _variablesFor(frame['scopeChain'] as List<dynamic>);
dartFrames.add(dartFrame);
}
}
return dartFrames;
}

/// The variables visible in a frame in Dart protocol [BoundVariable] form.
Future<List<BoundVariable>> _variablesFor(List<dynamic> scopeChain) async {
// TODO: Much better logic for which frames to use. This is probably just
// the dynamically visible variables, so we should omit library scope.
return [
for (var scope in scopeChain.take(2)) ...await _boundVariables(scope)
];
}

/// The [BoundVariable]s visible in a v8 'scope' object as found in the
/// 'scopeChain' field of the 'callFrames' in a DebuggerPausedEvent.
Future<Iterable<BoundVariable>> _boundVariables(dynamic scope) async {
var properties =
await _getProperties(scope['object']['objectId'] as String);
// We return one level of properties from this object. Sub-properties are
// another round trip.
var refs = properties
.map<Future<BoundVariable>>((property) async => BoundVariable()
..name = property.name
..value = await inspector.instanceRefFor(property.value));
return Future.wait(refs);
}

/// Calls the Chrome Runtime.getProperties API for the object
/// with [id].
Future<List<Property>> _getProperties(String id) async {
var response = await _tabConnection.runtime
.sendCommand('Runtime.getProperties', params: {
'objectId': id,
'ownProperties': true,
});
var jsProperties = response.result['result'];
var properties = (jsProperties as List)
.map<Property>((each) => Property(each as Map<String, dynamic>))
.toList();
return properties;
}

/// Returns a Dart [Frame] for a [JsLocation].
Frame _frameFor(JsLocation jsLocation) {
// TODO(sdk/issues/37240) - ideally we look for an exact location instead
Expand Down Expand Up @@ -332,7 +374,9 @@ class Debugger extends Domain {
}
event.kind = EventKind.kPauseInterrupted;
}
var frames = _dartFramesFor(e);
var jsFrames =
(e.params['callFrames'] as List).cast<Map<String, dynamic>>();
var frames = await dartFramesFor(jsFrames);
_pausedStack = Stack()
..frames = frames
..messages = [];
Expand Down
4 changes: 4 additions & 0 deletions dwds/lib/src/domain.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ abstract class Domain {

Domain(this._appInspectorProvider);

/// A constructor for the AppInspector to call which doesn't set
/// [_appInspectorProvider] as it's not used by the AppInspector.
Domain.forInspector() : _appInspectorProvider = null;

AppInspector get inspector => _appInspectorProvider();

/// Validate that isolateId matches the current isolate we're connected to and
Expand Down
45 changes: 34 additions & 11 deletions dwds/lib/src/inspector.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,24 @@
// 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 'dart:async';

import 'dart:math' as math show min;

import 'package:path/path.dart' as p;
import 'package:vm_service_lib/vm_service_lib.dart';
import 'package:webkit_inspection_protocol/webkit_inspection_protocol.dart';

import 'chrome_proxy_service.dart';
import 'dart_uri.dart';
import 'debugger.dart';
import 'domain.dart';
import 'helpers.dart';

/// An inspector for a running Dart application contained in the
/// [WipConnection].
///
/// Provides information about currently loaded scripts and objects and support
/// for eval.
class AppInspector {
class AppInspector extends Domain {
/// Map of class ID to [Class].
final _classes = <String, Class>{};

Expand Down Expand Up @@ -50,7 +53,13 @@ class AppInspector {
this._assetHandler,
this._debugger,
this._root,
) : isolateRef = _toIsolateRef(isolate);
) : isolateRef = _toIsolateRef(isolate),
super.forInspector();

@override

/// We are the inspector, so this getter is trivial.
AppInspector get inspector => this;

Future<void> _initialize() async {
isolate.libraries.addAll(await _getLibraryRefs());
Expand Down Expand Up @@ -146,9 +155,16 @@ function($argsString) {
'scope': scope,
});
}
var remoteObject =
RemoteObject(result.result['result'] as Map<String, dynamic>);
return await instanceRefFor(
RemoteObject(result.result['result'] as Map<String, dynamic>));
}

/// Create an [InstanceRef] for the given Chrome [remoteObject].
Future<InstanceRef> instanceRefFor(RemoteObject remoteObject) async {
// If we have a null result, treat it as a reference to null.
if (remoteObject == null) {
return _primitiveInstance(InstanceKind.kNull, remoteObject);
}
switch (remoteObject.type) {
case 'string':
return _primitiveInstance(InstanceKind.kString, remoteObject);
Expand All @@ -157,16 +173,27 @@ function($argsString) {
case 'boolean':
return _primitiveInstance(InstanceKind.kBool, remoteObject);
case 'object':
// TODO: Actual toString()
var toString = 'Placeholder for toString() result';
// TODO: Make the truncation consistent with the VM.
var truncated = toString.substring(0, math.min(100, toString.length));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Curious where this 100 value is coming from.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's completely arbitrary. Added a TODO to make that consistent with what the VM does.

return InstanceRef()
..kind = InstanceKind.kPlainInstance
..id = remoteObject.objectId
..valueAsString = toString
..valueAsStringIsTruncated = truncated.length != toString.length
// TODO(jakemac): Create a real ClassRef, we need a way of looking
// up the library for a given instance to create it though.
// https://github.com/dart-lang/sdk/issues/36771.
..classRef = ClassRef();
default:
throw UnsupportedError(
'Unsupported response type ${remoteObject.type}');
// Return unsupported types as a String placeholder for now.
var unsupported = RemoteObject({
'type': 'String',
'value':
'Unsupported type:${remoteObject.type} (${remoteObject.description})'
});
return _primitiveInstance(InstanceKind.kString, unsupported);
}
}

Expand All @@ -189,7 +216,6 @@ function($argsString) {
if (clazz != null) return clazz;
var scriptRef = _scriptRefs[objectId];
if (scriptRef != null) return await _getScript(isolateId, scriptRef);

throw UnsupportedError(
'Only libraries and classes are supported for getObject');
}
Expand Down Expand Up @@ -350,10 +376,7 @@ function($argsString) {

/// Returns all scripts in the isolate.
Future<List<ScriptRef>> scriptRefs(String isolateId) async {
if (isolateId != isolate.id) {
throw ArgumentError.value(
isolateId, 'isolateId', 'Unrecognized isolate id');
}
checkIsolate(isolateId);
var scripts = <ScriptRef>[];
for (var lib in isolate.libraries) {
// We can't provide the source for `dart:` imports so ignore for now.
Expand Down
31 changes: 31 additions & 0 deletions dwds/lib/src/objects.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright (c) 2019, 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 'dart:async';

/// A library for WebKit mirror objects and support code. These probably should
/// get migrated into webkit_inspection_protocol over time.
import 'package:webkit_inspection_protocol/webkit_inspection_protocol.dart';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we not depend on this package for this abstraction? This will become problematic as we make use of the Chrome Debug Extension.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It needs that now because it can return RemoteObject from that package for its values. My thought was more to fold this into that package than to re-implement what it does.

Why is it problematic if we use the extension? Is the extension going to use an incompatible protocol?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Discussed offline. We'll likely reuse the abstractions provided by webkit_inspection_protocol but change out the actual communication layer.


/// Represents a property of an object.
class Property {
final Map<String, dynamic> _map;

Property(this._map);

/// The remote object value in unwrapped form.
///
/// Useful for getting access to properties of particular types of
/// RemoteObject.
Map<String, dynamic> get rawValue => _map['value'] as Map<String, dynamic>;

/// Remote object value in case of primitive values or JSON values (if it was
/// requested). (optional)
RemoteObject get value => RemoteObject(rawValue);

/// The name of the property
String get name => _map['name'] as String;

@override
String toString() => '$name $value';
}
14 changes: 8 additions & 6 deletions dwds/lib/src/sources.dart
Original file line number Diff line number Diff line change
Expand Up @@ -69,17 +69,19 @@ class Sources {
dartUri,
);
serverPaths.add(dartUri.serverPath);
_sourceToLocation
.putIfAbsent(dartUri.serverPath, () => Set())
.add(location);
_scriptIdToLocation
.putIfAbsent(script.scriptId, () => Set())
.add(location);
noteLocation(dartUri.serverPath, location, script.scriptId);
}
}
}
}

/// Add [location] to our lookups for both the Dart and JS scripts.
void noteLocation(
String dartServerPath, Location location, String wipScriptId) {
_sourceToLocation.putIfAbsent(dartServerPath, () => Set()).add(location);
_scriptIdToLocation.putIfAbsent(wipScriptId, () => Set()).add(location);
}

/// Returns the tokenPosTable for the provided Dart script path as defined
/// in:
/// https://github.com/dart-lang/sdk/blob/master/runtime/vm/service/service.md#script
Expand Down
Loading