Chromium Code Reviews| Index: tool/input_sdk/private/debugger.dart |
| diff --git a/tool/input_sdk/private/debugger.dart b/tool/input_sdk/private/debugger.dart |
| index ed6450550e965282b8e7d18041a041ba0c8738ae..a8dd1647e92ee2b88e33bbddab319e1776ec4c96 100644 |
| --- a/tool/input_sdk/private/debugger.dart |
| +++ b/tool/input_sdk/private/debugger.dart |
| @@ -146,34 +146,54 @@ class IterableSpan { |
| iterable.skip(start).take(length).toList().asMap(); |
| List<NameValuePair> children() { |
| - var ret = <NameValuePair>[]; |
| + var children = <NameValuePair>[]; |
| if (length <= _maxSpanLength) { |
| asMap().forEach((i, element) { |
| - ret.add( |
| + children.add( |
| new NameValuePair(name: (i + start).toString(), value: element)); |
| }); |
| } else { |
| for (var i = start; i < end; i += subsetSize) { |
| var subSpan = new IterableSpan(i, min(end, subsetSize + i), iterable); |
| if (subSpan.length == 1) { |
| - ret.add(new NameValuePair( |
| + children.add(new NameValuePair( |
| name: i.toString(), value: iterable.elementAt(i))); |
| } else { |
| - ret.add(new NameValuePair( |
| + children.add(new NameValuePair( |
| name: '[${i}...${subSpan.end - 1}]', |
| value: subSpan, |
| hideName: true)); |
| } |
| } |
| } |
| - return ret; |
| + return children; |
| } |
| } |
| +class Library { |
| + Library(this.name, this.object); |
| + |
| + final String name; |
| + final Object object; |
| +} |
| + |
| +class NamedConstructor { |
| + NamedConstructor(this.object); |
| + |
| + final Object object; |
| +} |
| + |
| class ClassMetadata { |
| - ClassMetadata(this.object); |
| + ClassMetadata(this.object, {this.name}); |
| final Object object; |
| + final String name; |
| + |
| + String get typeName { |
| + return name == null |
|
Jacob
2016/07/22 19:17:50
nit: dart now has a ?? operator that can simplify
bmilligan
2016/07/22 20:10:24
So cool!
|
| + ? getTypeName(object is Type ? object : dart.getReifiedType(object)) |
| + : name; |
| + } |
| } |
| class HeritageClause { |
| @@ -183,6 +203,16 @@ class HeritageClause { |
| final List types; |
| } |
| +Object safeGetProperty(Object protoChain, String name) { |
| + var property; |
| + try { |
| + property = JSNative.getProperty(protoChain, name); |
|
Jacob
2016/07/22 19:17:50
nit: cleaner to just return within the try and wit
bmilligan
2016/07/22 20:10:24
Done.
|
| + } catch (e) { |
| + property = '<Exception thrown> $e'; |
| + } |
| + return property; |
| +} |
| + |
| /// Class to simplify building the JsonML objects expected by the |
| /// Devtools Formatter API. |
| class JsonMLElement { |
| @@ -333,9 +363,10 @@ class DartFormatter { |
| List<Formatter> _formatters; |
| DartFormatter() { |
| - // The order of formatters matters as formatters later in the list take |
| - // precidence. |
| + // The order of formatters matters as formatters earlier in the list take |
| + // precedence. |
| _formatters = [ |
| + new NamedConstructorFormatter(), |
| new FunctionFormatter(), |
| new MapFormatter(), |
| new IterableFormatter(), |
| @@ -343,6 +374,8 @@ class DartFormatter { |
| new IterableSpanFormatter(), |
| new ClassMetadataFormatter(), |
| new HeritageClauseFormatter(), |
| + new LibraryModuleFormatter(), |
| + new LibraryFormatter(), |
| new ObjectFormatter(), |
| ]; |
| } |
| @@ -414,15 +447,16 @@ class ObjectFormatter extends Formatter { |
| // Set of property names used to avoid duplicates. |
| addMetadataChildren(object, properties); |
| + var current = object; |
| + |
| /// Helper to add members walking up the prototype chain being careful |
| /// to avoid properties that are Dart methods. |
| var protoChain = <Object>[]; |
| - var current = object; |
| while (current != null && |
| !isNativeJavaScriptObject(current) && |
| JS("bool", "# !== Object.prototype", current)) { |
| protoChain.add(current); |
| - current = JSNative.getProperty(current, '__proto__'); |
| + current = safeGetProperty(current, '__proto__'); |
| } |
| // We walk the prototype chain for symbol properties because they take |
| @@ -445,12 +479,7 @@ class ObjectFormatter extends Formatter { |
| // start with an _ |
| continue; |
| } |
| - var value; |
| - try { |
| - value = JSNative.getProperty(object, symbol); |
| - } catch (e) { |
| - value = '<Exception thrown> $e'; |
| - } |
| + var value = safeGetProperty(object, symbol); |
| properties.add(new NameValuePair(name: dartName, value: value)); |
| } |
| } |
| @@ -464,12 +493,7 @@ class ObjectFormatter extends Formatter { |
| if (hasMethod(object, name)) { |
| continue; |
| } |
| - var value; |
| - try { |
| - value = JSNative.getProperty(object, name); |
| - } catch (e) { |
| - value = '<Exception thrown> $e'; |
| - } |
| + var value = safeGetProperty(object, name); |
| properties.add(new NameValuePair(name: name, value: value)); |
| } |
| } |
| @@ -478,8 +502,90 @@ class ObjectFormatter extends Formatter { |
| } |
| addMetadataChildren(object, Set<NameValuePair> ret) { |
| - ret.add( |
| - new NameValuePair(name: '[[class]]', value: new ClassMetadata(object))); |
| + var child = new ClassMetadata(object); |
| + ret.add(new NameValuePair(name: child.typeName, value: child)); |
| + } |
| +} |
| + |
| +/// Formatter for module Dart Library objects. |
| +class LibraryModuleFormatter extends ObjectFormatter { |
| + String libraryName; |
| + |
| + accept(object) { |
| + libraryName = dart.getDartLibraryName(object); |
| + return libraryName != null; |
| + } |
| + |
| + bool hasChildren(object) => true; |
| + |
| + String preview(object) { |
| + var libraryNames = libraryName.split('/'); |
| + // Library names are received with a repeat directory name, so strip the |
| + // last directory entry here to make the path cleaner. For example, the |
| + // library "third_party/dart/utf/utf" shoud display as |
| + // "third_party/dart/utf/". |
| + if (libraryNames.length > 1) { |
| + libraryNames[libraryNames.length - 1] = ''; |
| + } |
| + return 'Library Module: ${libraryNames.join('/')}'; |
| + } |
| + |
| + List<NameValuePair> children(object) { |
| + var children = new LinkedHashSet<NameValuePair>(); |
| + for (var name in getOwnPropertyNames(object)) { |
| + var value = safeGetProperty(object, name); |
| + // Replace __ with / to make file paths more readable. Then |
| + // 'src__result__error' becomes 'src/result/error'. |
| + name = '${name.replaceAll("__", "/")}.dart'; |
| + children.add(new NameValuePair( |
| + name: name, value: new Library(name, value), hideName: true)); |
| + } |
| + return children.toList(); |
| + } |
| +} |
| + |
| +/// Formatter for Dart Library objects. |
| +class LibraryFormatter extends ObjectFormatter { |
| + String genericName; |
| + String genericTypes; |
| + |
| + accept(object) => object is Library; |
| + |
| + bool hasChildren(object) => true; |
| + |
| + String preview(object) => object.name; |
| + |
| + List<NameValuePair> children(object) { |
| + var children = new LinkedHashSet<NameValuePair>(); |
| + var entry = object.object; |
| + for (var name in getOwnPropertyNames(entry)) { |
| + var value = safeGetProperty(entry, name); |
| + var genericTypeConstructor = dart.getGenericTypeCtor(value); |
| + if (genericTypeConstructor != null) { |
| + genericName = name; |
| + // Using JS toString() eliminates the leading metadata that is generated |
| + // with the toString function provided in operations.dart. |
| + // Splitting by => and taking the first element gives the list of |
| + // arguments in the constructor. |
| + genericTypes = JS('String', '#.toString()', genericTypeConstructor) |
| + .split(' =>')[0] |
|
Jacob
2016/07/22 19:17:50
[0] --> .first
bmilligan
2016/07/22 20:10:24
Done.
|
| + .replaceAll('(', '') |
|
Jacob
2016/07/22 19:17:50
can combine these two call to replaceAll
bmilligan
2016/07/22 20:10:24
Done.
|
| + .replaceAll(')', ''); |
| + } else if (value is Type) { |
| + var typeName = getTypeName(value); |
| + // Generic class names are generated with a $ at the end, so the |
| + // corresponding non-generic class can be identified by adding $. |
| + if ('$name\$' == genericName) { |
| + typeName = '$typeName<$genericTypes>'; |
| + } |
| + children.add(new NameValuePair( |
| + name: typeName, value: new ClassMetadata(value, name: typeName))); |
| + } else { |
| + children.add( |
| + new NameValuePair(name: name, value: new ClassMetadata(value))); |
| + } |
| + } |
| + return children.toList(); |
| } |
| } |
| @@ -556,12 +662,12 @@ class IterableFormatter extends ObjectFormatter { |
| // are not the built in Set or List types. |
| // TODO(jacobr): handle large Iterables better. |
| // TODO(jacobr): consider only using numeric indices |
| - var ret = new LinkedHashSet<NameValuePair>(); |
| - ret.addAll((new IterableSpan(0, object.length, object)).children()); |
| + var children = new LinkedHashSet<NameValuePair>(); |
| + children.addAll(new IterableSpan(0, object.length, object).children()); |
| // TODO(jacobr): provide a link to show regular class properties here. |
| // required for subclasses of iterable, etc. |
| - addMetadataChildren(object, ret); |
| - return ret.toList(); |
| + addMetadataChildren(object, children); |
| + return children.toList(); |
| } |
| } |
| @@ -569,54 +675,99 @@ class IterableFormatter extends ObjectFormatter { |
| class ClassMetadataFormatter implements Formatter { |
| accept(object) => object is ClassMetadata; |
| - _getType(object) { |
| + Object _getType(object) { |
| if (object is Type) return object; |
| return dart.getReifiedType(object); |
| } |
| String preview(object) { |
| ClassMetadata entry = object; |
| - return getTypeName(_getType(entry.object)); |
| + var type = |
| + entry.object is Type ? entry.object : dart.getReifiedType(entry.object); |
| + var implements = dart.getImplements(type); |
| + if (implements != null) { |
| + var typeNames = implements().map(getTypeName); |
| + return '${entry.typeName} implements ${typeNames.join(", ")}'; |
| + } else { |
| + return entry.typeName; |
| + } |
| } |
| bool hasChildren(object) => true; |
| List<NameValuePair> children(object) { |
| ClassMetadata entry = object; |
| + var classObject = entry.object; |
| // TODO(jacobr): add other entries describing the class such as |
| // links to the superclass, mixins, implemented interfaces, and methods. |
| - var type = _getType(entry.object); |
| - var ret = <NameValuePair>[]; |
| - var implements = dart.getImplements(type); |
| - if (implements != null) { |
| - ret.add(new NameValuePair( |
| - name: '[[Implements]]', |
| - value: new HeritageClause('implements', implements()))); |
| - } |
| + var type = _getType(classObject); |
| + var children = <NameValuePair>[]; |
| + |
| var mixins = dart.getMixins(type); |
| if (mixins != null && mixins.isNotEmpty) { |
| - ret.add(new NameValuePair( |
| + children.add(new NameValuePair( |
| name: '[[Mixins]]', value: new HeritageClause('mixins', mixins))); |
| } |
| - ret.add(new NameValuePair( |
| - name: '[[JavaScript View]]', |
| - value: entry.object, |
| - config: JsonMLConfig.skipDart)); |
| - // TODO(jacobr): provide a link to the base class or perhaps the entire |
| - // base class hierarchy as a flat list. |
| + var hiddenProperties = ['length', 'name', 'prototype']; |
| + // Addition of NameValuePairs for static variables and named constructors. |
| + for (var name in getOwnPropertyNames(classObject)) { |
| + // TODO(bmilligan): Perform more principled checks to filter out spurious |
| + // members. |
| + if (hiddenProperties.contains(name)) continue; |
| + var value = safeGetProperty(classObject, name); |
| + if (value != null && dart.getIsNamedConstructor(value) != null) { |
| + value = new NamedConstructor(value); |
| + name = '${entry.typeName}.$name'; |
| + } |
| + children.add(new NameValuePair(name: name, value: value)); |
| + } |
| - if (entry.object is! Type) { |
| - ret.add(new NameValuePair( |
| - name: '[[JavaScript Constructor]]', |
| - value: JSNative.getProperty(entry.object, 'constructor'), |
| - config: JsonMLConfig.skipDart)); |
| - // TODO(jacobr): add constructors, methods, extended class, and static |
| + // TODO(bmilligan): Replace the hard coding of $identityHash. |
| + var hiddenPrototypeProperties = ['constructor', 'new', r'$identityHash']; |
| + // Addition of class methods. |
| + var prototype = JS('var', '#["prototype"]', classObject); |
| + if (prototype != null) { |
| + for (var name in getOwnPropertyNames(prototype)) { |
| + if (hiddenPrototypeProperties.contains(name)) continue; |
| + // Simulate dart.bind by using dart.tag and tear off the function |
| + // so it will be recognized by the FunctionFormatter. |
| + var function = safeGetProperty(prototype, name); |
| + var constructor = safeGetProperty(prototype, 'constructor'); |
| + var sigObj = dart.getMethodSig(constructor); |
| + if (sigObj != null) { |
| + var value = safeGetProperty(sigObj, name); |
| + if (getTypeName(dart.getReifiedType(value)) != 'Null') { |
| + dart.tag(function, value); |
| + children.add(new NameValuePair(name: name, value: function)); |
| + } |
| + } |
| + } |
| } |
| - return ret; |
| + // TODO(jacobr): provide a link to the base class or perhaps the entire |
| + // base class hierarchy as a flat list. |
| + // TODO(jacobr): add constructors, methods, extended class, and static |
| + return children; |
| } |
| } |
| +class NamedConstructorFormatter implements Formatter { |
| + accept(object) => object is NamedConstructor; |
| + |
| + // TODO(bmilligan): Display the signature of the named constructor as the |
| + // preview. |
| + String preview(object) => 'Named Constructor'; |
| + |
| + bool hasChildren(object) => true; |
| + |
| + List<NameValuePair> children(object) => <NameValuePair>[ |
| + new NameValuePair( |
| + name: 'JavaScript Function', |
| + value: object, |
| + config: JsonMLConfig.skipDart) |
| + ]; |
| +} |
| + |
| /// Formatter for synthetic MapEntry objects used to display contents of a Map |
| /// cleanly. |
| class MapEntryFormatter implements Formatter { |
| @@ -642,7 +793,7 @@ class HeritageClauseFormatter implements Formatter { |
| String preview(object) { |
| HeritageClause clause = object; |
| - var typeNames = clause.types.map((type) => getTypeName(type)); |
| + var typeNames = clause.types.map(getTypeName); |
| return '${clause.name} ${typeNames.join(", ")}'; |
| } |
| @@ -650,11 +801,11 @@ class HeritageClauseFormatter implements Formatter { |
| List<NameValuePair> children(object) { |
| HeritageClause clause = object; |
| - var ret = <NameValuePair>[]; |
| + var children = <NameValuePair>[]; |
| for (var type in clause.types) { |
| - ret.add(new NameValuePair(value: new ClassMetadata(type))); |
| + children.add(new NameValuePair(value: new ClassMetadata(type))); |
| } |
| - return ret; |
| + return children; |
| } |
| } |