| OLD | NEW |
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 library dart._debugger; | 5 library dart._debugger; |
| 6 | 6 |
| 7 import 'dart:_foreign_helper' show JS; | 7 import 'dart:_foreign_helper' show JS; |
| 8 import 'dart:_runtime' as dart; | 8 import 'dart:_runtime' as dart; |
| 9 import 'dart:core'; | 9 import 'dart:core'; |
| 10 import 'dart:collection'; | 10 import 'dart:collection'; |
| (...skipping 10 matching lines...) Expand all Loading... |
| 21 /// the Dart formatter. | 21 /// the Dart formatter. |
| 22 /// | 22 /// |
| 23 /// We'd like this to be an enum, but we can't because it's a dev_compiler bug. | 23 /// We'd like this to be an enum, but we can't because it's a dev_compiler bug. |
| 24 class JsonMLConfig { | 24 class JsonMLConfig { |
| 25 const JsonMLConfig(this.name); | 25 const JsonMLConfig(this.name); |
| 26 | 26 |
| 27 final String name; | 27 final String name; |
| 28 static const none = const JsonMLConfig("none"); | 28 static const none = const JsonMLConfig("none"); |
| 29 static const skipDart = const JsonMLConfig("skipDart"); | 29 static const skipDart = const JsonMLConfig("skipDart"); |
| 30 static const keyToString = const JsonMLConfig("keyToString"); | 30 static const keyToString = const JsonMLConfig("keyToString"); |
| 31 static const asClass = const JsonMLConfig("asClass"); |
| 31 } | 32 } |
| 32 | 33 |
| 33 int _maxSpanLength = 100; | 34 int _maxSpanLength = 100; |
| 34 | |
| 35 var _devtoolsFormatter = new JsonMLFormatter(new DartFormatter()); | 35 var _devtoolsFormatter = new JsonMLFormatter(new DartFormatter()); |
| 36 | 36 |
| 37 String _typeof(object) => JS('String', 'typeof #', object); | 37 String _typeof(object) => JS('String', 'typeof #', object); |
| 38 | 38 |
| 39 List<String> getOwnPropertyNames(object) => JS('List<String>', | 39 List<String> getOwnPropertyNames(object) => JS('List<String>', |
| 40 'dart.list(Object.getOwnPropertyNames(#), #)', object, String); | 40 'dart.list(Object.getOwnPropertyNames(#), #)', object, String); |
| 41 | 41 |
| 42 List getOwnPropertySymbols(object) => | 42 List getOwnPropertySymbols(object) => |
| 43 JS('List', 'Object.getOwnPropertySymbols(#)', object); | 43 JS('List', 'Object.getOwnPropertySymbols(#)', object); |
| 44 | 44 |
| 45 // TODO(jacobr): move this to dart:js and fully implement. | 45 // TODO(jacobr): move this to dart:js and fully implement. |
| 46 class JSNative { | 46 class JSNative { |
| 47 // Name may be a String or a Symbol. | 47 // Name may be a String or a Symbol. |
| 48 static getProperty(object, name) => JS('', '#[#]', object, name); | 48 static getProperty(object, name) => JS('', '#[#]', object, name); |
| 49 // Name may be a String or a Symbol. | 49 // Name may be a String or a Symbol. |
| 50 static setProperty(object, name, value) => | 50 static setProperty(object, name, value) => |
| 51 JS('', '#[#]=#', object, name, value); | 51 JS('', '#[#]=#', object, name, value); |
| 52 } | 52 } |
| 53 | 53 |
| 54 void addMetadataChildren(object, Set<NameValuePair> ret) { |
| 55 ret.add(new NameValuePair( |
| 56 name: getTypeName(_getType(object)), |
| 57 value: object, |
| 58 config: JsonMLConfig.asClass)); |
| 59 } |
| 60 |
| 54 String getObjectTypeName(object) { | 61 String getObjectTypeName(object) { |
| 55 var reifiedType = dart.getReifiedType(object); | 62 var reifiedType = dart.getReifiedType(object); |
| 56 if (reifiedType == null) { | 63 if (reifiedType == null) { |
| 57 if (_typeof(object) == 'function') { | 64 if (_typeof(object) == 'function') { |
| 58 return '[[Raw JavaScript Function]]'; | 65 return '[[Raw JavaScript Function]]'; |
| 59 } | 66 } |
| 60 return '<Error getting type name>'; | 67 return '<Error getting type name>'; |
| 61 } | 68 } |
| 62 return getTypeName(reifiedType); | 69 return getTypeName(reifiedType); |
| 63 } | 70 } |
| 64 | 71 |
| 65 String getTypeName(Type type) { | 72 String getTypeName(Type type) { |
| 66 var name = dart.typeName(type); | 73 var name = dart.typeName(type); |
| 67 // Hack to cleanup names for List<dynamic> | 74 // Hack to cleanup names for List<dynamic> |
| 68 // TODO(jacobr): it would be nice if there was a way we could distinguish | 75 // TODO(jacobr): it would be nice if there was a way we could distinguish |
| 69 // between a List<dynamic> created from Dart and an Array passed in from | 76 // between a List<dynamic> created from Dart and an Array passed in from |
| 70 // JavaScript. | 77 // JavaScript. |
| 71 if (name == 'JSArray<dynamic>' || name == 'JSObject<Array>') | 78 if (name == 'JSArray<dynamic>' || name == 'JSObject<Array>') |
| 72 return 'List<dynamic>'; | 79 return 'List<dynamic>'; |
| 73 return name; | 80 return name; |
| 74 } | 81 } |
| 75 | 82 |
| 76 String safePreview(object) { | 83 Object _getType(object) => |
| 84 object is Type ? object : dart.getReifiedType(object); |
| 85 |
| 86 String safePreview(object, config) { |
| 77 try { | 87 try { |
| 78 var preview = _devtoolsFormatter._simpleFormatter.preview(object); | 88 var preview = _devtoolsFormatter._simpleFormatter.preview(object, config); |
| 79 if (preview != null) return preview; | 89 if (preview != null) return preview; |
| 80 return object.toString(); | 90 return object.toString(); |
| 81 } catch (e) { | 91 } catch (e) { |
| 82 return '<Exception thrown> $e'; | 92 return '<Exception thrown> $e'; |
| 83 } | 93 } |
| 84 } | 94 } |
| 85 | 95 |
| 86 String symbolName(symbol) { | 96 String symbolName(symbol) { |
| 87 var name = symbol.toString(); | 97 var name = symbol.toString(); |
| 88 assert(name.startsWith('Symbol(')); | 98 assert(name.startsWith('Symbol(')); |
| (...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 176 final String name; | 186 final String name; |
| 177 final Object object; | 187 final Object object; |
| 178 } | 188 } |
| 179 | 189 |
| 180 class NamedConstructor { | 190 class NamedConstructor { |
| 181 NamedConstructor(this.object); | 191 NamedConstructor(this.object); |
| 182 | 192 |
| 183 final Object object; | 193 final Object object; |
| 184 } | 194 } |
| 185 | 195 |
| 186 class ClassMetadata { | |
| 187 ClassMetadata(this.object, {this.name}); | |
| 188 | |
| 189 final Object object; | |
| 190 final String name; | |
| 191 | |
| 192 String get typeName => | |
| 193 name ?? | |
| 194 getTypeName(object is Type ? object : dart.getReifiedType(object)); | |
| 195 } | |
| 196 | |
| 197 class HeritageClause { | 196 class HeritageClause { |
| 198 HeritageClause(this.name, this.types); | 197 HeritageClause(this.name, this.types); |
| 199 | 198 |
| 200 final String name; | 199 final String name; |
| 201 final List types; | 200 final List types; |
| 202 } | 201 } |
| 203 | 202 |
| 204 Object safeGetProperty(Object protoChain, String name) { | 203 Object safeGetProperty(Object protoChain, Object name) { |
| 205 try { | 204 try { |
| 206 return JSNative.getProperty(protoChain, name); | 205 return JSNative.getProperty(protoChain, name); |
| 207 } catch (e) { | 206 } catch (e) { |
| 208 return '<Exception thrown> $e'; | 207 return '<Exception thrown> $e'; |
| 209 } | 208 } |
| 210 } | 209 } |
| 211 | 210 |
| 212 safeProperties(object) => new Map.fromIterable( | 211 safeProperties(object) => new Map.fromIterable( |
| 213 getOwnPropertyNames(object) | 212 getOwnPropertyNames(object) |
| 214 .where((each) => safeGetProperty(object, each) != null), | 213 .where((each) => safeGetProperty(object, each) != null), |
| (...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 293 | 292 |
| 294 void setMaxSpanLengthForTestingOnly(int spanLength) { | 293 void setMaxSpanLengthForTestingOnly(int spanLength) { |
| 295 _maxSpanLength = spanLength; | 294 _maxSpanLength = spanLength; |
| 296 } | 295 } |
| 297 | 296 |
| 298 header(object, config) { | 297 header(object, config) { |
| 299 customFormattersOn = true; | 298 customFormattersOn = true; |
| 300 if (config == JsonMLConfig.skipDart || isNativeJavaScriptObject(object)) { | 299 if (config == JsonMLConfig.skipDart || isNativeJavaScriptObject(object)) { |
| 301 return null; | 300 return null; |
| 302 } | 301 } |
| 303 | 302 var c = _simpleFormatter.preview(object, config); |
| 304 var c = _simpleFormatter.preview(object); | |
| 305 if (c == null) return null; | 303 if (c == null) return null; |
| 306 | 304 |
| 307 if (config == JsonMLConfig.keyToString) { | 305 if (config == JsonMLConfig.keyToString) { |
| 308 c = object.toString(); | 306 c = object.toString(); |
| 309 } | 307 } |
| 310 | 308 |
| 311 // Indicate this is a Dart Object by using a Dart background color. | 309 // Indicate this is a Dart Object by using a Dart background color. |
| 312 // This is stylistically a bit ugly but it eases distinguishing Dart and | 310 // This is stylistically a bit ugly but it eases distinguishing Dart and |
| 313 // JS objects. | 311 // JS objects. |
| 314 var element = new JsonMLElement('span') | 312 var element = new JsonMLElement('span') |
| 315 ..setStyle('background-color: #d9edf7;') | 313 ..setStyle('background-color: #d9edf7;') |
| 316 ..createTextChild(c); | 314 ..createTextChild(c); |
| 317 return element.toJsonML(); | 315 return element.toJsonML(); |
| 318 } | 316 } |
| 319 | 317 |
| 320 bool hasBody(object) => _simpleFormatter.hasChildren(object); | 318 bool hasBody(object, config) => _simpleFormatter.hasChildren(object, config); |
| 321 | 319 |
| 322 body(object) { | 320 body(object, config) { |
| 323 var body = new JsonMLElement('ol') | 321 var body = new JsonMLElement('ol') |
| 324 ..setStyle('list-style-type: none;' | 322 ..setStyle('list-style-type: none;' |
| 325 'padding-left: 0px;' | 323 'padding-left: 0px;' |
| 326 'margin-top: 0px;' | 324 'margin-top: 0px;' |
| 327 'margin-bottom: 0px;' | 325 'margin-bottom: 0px;' |
| 328 'margin-left: 12px;'); | 326 'margin-left: 12px;'); |
| 329 if (object is StackTrace) { | 327 if (object is StackTrace) { |
| 330 body.addStyle('color: rgb(196, 26, 22);'); | 328 body.addStyle('color: rgb(196, 26, 22);'); |
| 331 } | 329 } |
| 332 var children = _simpleFormatter.children(object); | 330 var children = _simpleFormatter.children(object, config); |
| 333 for (NameValuePair child in children) { | 331 for (NameValuePair child in children) { |
| 334 var li = body.createChild('li'); | 332 var li = body.createChild('li'); |
| 335 var nameSpan = new JsonMLElement('span') | 333 var nameSpan = new JsonMLElement('span') |
| 336 ..createTextChild( | 334 ..createTextChild( |
| 337 child.displayName.isNotEmpty ? '${child.displayName}: ' : '') | 335 child.displayName.isNotEmpty ? '${child.displayName}: ' : '') |
| 338 ..setStyle('color: rgb(136, 19, 145);'); | 336 ..setStyle('color: rgb(136, 19, 145);'); |
| 339 if (_typeof(child.value) == 'object' || | 337 if (_typeof(child.value) == 'object' || |
| 340 _typeof(child.value) == 'function') { | 338 _typeof(child.value) == 'function') { |
| 341 nameSpan.addStyle("padding-left: 13px;"); | 339 nameSpan.addStyle("padding-left: 13px;"); |
| 342 | 340 |
| 343 li.appendChild(nameSpan); | 341 li.appendChild(nameSpan); |
| 344 var objectTag = li.createObjectTag(child.value); | 342 var objectTag = li.createObjectTag(child.value); |
| 345 objectTag.addAttribute('config', child.config); | 343 objectTag.addAttribute('config', child.config); |
| 346 if (!_simpleFormatter.hasChildren(child.value)) { | 344 if (!_simpleFormatter.hasChildren(child.value, child.config)) { |
| 347 li.setStyle("padding-left: 13px;"); | 345 li.setStyle("padding-left: 13px;"); |
| 348 } | 346 } |
| 349 } else { | 347 } else { |
| 350 li.setStyle("padding-left: 13px;"); | 348 li.setStyle("padding-left: 13px;"); |
| 351 li.createChild('span') | 349 li.createChild('span') |
| 352 ..appendChild(nameSpan) | 350 ..appendChild(nameSpan) |
| 353 ..createTextChild(safePreview(child.value)); | 351 ..createTextChild(safePreview(child.value, child.config)); |
| 354 } | 352 } |
| 355 } | 353 } |
| 356 return body.toJsonML(); | 354 return body.toJsonML(); |
| 357 } | 355 } |
| 358 } | 356 } |
| 359 | 357 |
| 360 abstract class Formatter { | 358 abstract class Formatter { |
| 361 bool accept(object); | 359 bool accept(object, config); |
| 362 String preview(object); | 360 String preview(object); |
| 363 bool hasChildren(object); | 361 bool hasChildren(object); |
| 364 List<NameValuePair> children(object); | 362 List<NameValuePair> children(object); |
| 365 } | 363 } |
| 366 | 364 |
| 367 class DartFormatter { | 365 class DartFormatter { |
| 368 List<Formatter> _formatters; | 366 List<Formatter> _formatters; |
| 369 | 367 |
| 370 DartFormatter() { | 368 DartFormatter() { |
| 371 // The order of formatters matters as formatters earlier in the list take | 369 // The order of formatters matters as formatters earlier in the list take |
| 372 // precedence. | 370 // precedence. |
| 373 _formatters = [ | 371 _formatters = [ |
| 372 new ClassFormatter(), |
| 374 new NamedConstructorFormatter(), | 373 new NamedConstructorFormatter(), |
| 375 new FunctionFormatter(), | |
| 376 new MapFormatter(), | 374 new MapFormatter(), |
| 377 new IterableFormatter(), | 375 new IterableFormatter(), |
| 376 new IterableSpanFormatter(), |
| 378 new MapEntryFormatter(), | 377 new MapEntryFormatter(), |
| 379 new IterableSpanFormatter(), | |
| 380 new StackTraceFormatter(), | 378 new StackTraceFormatter(), |
| 381 new ClassMetadataFormatter(), | 379 new FunctionFormatter(), |
| 382 new HeritageClauseFormatter(), | 380 new HeritageClauseFormatter(), |
| 383 new LibraryModuleFormatter(), | 381 new LibraryModuleFormatter(), |
| 384 new LibraryFormatter(), | 382 new LibraryFormatter(), |
| 385 new ObjectFormatter(), | 383 new ObjectFormatter(), |
| 386 ]; | 384 ]; |
| 387 } | 385 } |
| 388 | 386 |
| 389 String preview(object) { | 387 String preview(object, config) { |
| 390 try { | 388 try { |
| 391 if (object == null || | 389 if (object == null || |
| 392 object is num || | 390 object is num || |
| 393 object is String || | 391 object is String || |
| 394 isNativeJavaScriptObject(object)) { | 392 isNativeJavaScriptObject(object)) { |
| 395 return object.toString(); | 393 return object.toString(); |
| 396 } | 394 } |
| 397 | |
| 398 for (var formatter in _formatters) { | 395 for (var formatter in _formatters) { |
| 399 if (formatter.accept(object)) return formatter.preview(object); | 396 if (formatter.accept(object, config)) return formatter.preview(object); |
| 400 } | 397 } |
| 401 } catch (e, trace) { | 398 } catch (e, trace) { |
| 402 // Log formatter internal errors as unfortunately the devtools cannot | 399 // Log formatter internal errors as unfortunately the devtools cannot |
| 403 // be used to debug formatter errors. | 400 // be used to debug formatter errors. |
| 404 html.window.console.error("Caught exception $e\n trace:\n$trace"); | 401 html.window.console.error("Caught exception $e\n trace:\n$trace"); |
| 405 } | 402 } |
| 406 | 403 |
| 407 return null; | 404 return null; |
| 408 } | 405 } |
| 409 | 406 |
| 410 bool hasChildren(object) { | 407 bool hasChildren(object, config) { |
| 411 if (object == null) return false; | 408 if (object == null) return false; |
| 412 try { | 409 try { |
| 413 for (var formatter in _formatters) { | 410 for (var formatter in _formatters) { |
| 414 if (formatter.accept(object)) return formatter.hasChildren(object); | 411 if (formatter.accept(object, config)) |
| 412 return formatter.hasChildren(object); |
| 415 } | 413 } |
| 416 } catch (e, trace) { | 414 } catch (e, trace) { |
| 417 // See comment for preview. | 415 // See comment for preview. |
| 418 html.window.console | 416 html.window.console |
| 419 .error("[hasChildren] Caught exception $e\n trace:\n$trace"); | 417 .error("[hasChildren] Caught exception $e\n trace:\n$trace"); |
| 420 } | 418 } |
| 421 return false; | 419 return false; |
| 422 } | 420 } |
| 423 | 421 |
| 424 List<NameValuePair> children(object) { | 422 List<NameValuePair> children(object, config) { |
| 425 try { | 423 try { |
| 426 if (object != null) { | 424 if (object != null) { |
| 427 for (var formatter in _formatters) { | 425 for (var formatter in _formatters) { |
| 428 if (formatter.accept(object)) return formatter.children(object); | 426 if (formatter.accept(object, config)) |
| 427 return formatter.children(object); |
| 429 } | 428 } |
| 430 } | 429 } |
| 431 } catch (e, trace) { | 430 } catch (e, trace) { |
| 432 // See comment for preview. | 431 // See comment for preview. |
| 433 html.window.console.error("Caught exception $e\n trace:\n$trace"); | 432 html.window.console.error("Caught exception $e\n trace:\n$trace"); |
| 434 } | 433 } |
| 435 return <NameValuePair>[]; | 434 return <NameValuePair>[]; |
| 436 } | 435 } |
| 437 } | 436 } |
| 438 | 437 |
| 439 /// Default formatter for Dart Objects. | 438 /// Default formatter for Dart Objects. |
| 440 class ObjectFormatter extends Formatter { | 439 class ObjectFormatter extends Formatter { |
| 441 static Set<String> _customNames = new Set() | 440 static Set<String> _customNames = new Set() |
| 442 ..add('constructor') | 441 ..add('constructor') |
| 443 ..add('prototype') | 442 ..add('prototype') |
| 444 ..add('__proto__'); | 443 ..add('__proto__'); |
| 445 bool accept(object) => !isNativeJavaScriptObject(object); | 444 bool accept(object, config) => !isNativeJavaScriptObject(object); |
| 446 | 445 |
| 447 String preview(object) => getObjectTypeName(object); | 446 String preview(object) => getObjectTypeName(object); |
| 448 | 447 |
| 449 bool hasChildren(object) => true; | 448 bool hasChildren(object) => true; |
| 450 | 449 |
| 451 List<NameValuePair> children(object) { | 450 List<NameValuePair> children(object) { |
| 452 var properties = new LinkedHashSet<NameValuePair>(); | 451 var properties = new LinkedHashSet<NameValuePair>(); |
| 453 // Set of property names used to avoid duplicates. | 452 // Set of property names used to avoid duplicates. |
| 454 addMetadataChildren(object, properties); | 453 addMetadataChildren(object, properties); |
| 455 | 454 |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 497 if (hasMethod(object, name)) { | 496 if (hasMethod(object, name)) { |
| 498 continue; | 497 continue; |
| 499 } | 498 } |
| 500 var value = safeGetProperty(object, name); | 499 var value = safeGetProperty(object, name); |
| 501 properties.add(new NameValuePair(name: name, value: value)); | 500 properties.add(new NameValuePair(name: name, value: value)); |
| 502 } | 501 } |
| 503 } | 502 } |
| 504 | 503 |
| 505 return properties.toList(); | 504 return properties.toList(); |
| 506 } | 505 } |
| 507 | |
| 508 addMetadataChildren(object, Set<NameValuePair> ret) { | |
| 509 var child = new ClassMetadata(object); | |
| 510 ret.add(new NameValuePair(name: child.typeName, value: child)); | |
| 511 } | |
| 512 } | 506 } |
| 513 | 507 |
| 514 /// Formatter for module Dart Library objects. | 508 /// Formatter for module Dart Library objects. |
| 515 class LibraryModuleFormatter extends ObjectFormatter { | 509 class LibraryModuleFormatter implements Formatter { |
| 516 String libraryName; | 510 String libraryName; |
| 517 | 511 |
| 518 accept(object) { | 512 accept(object, config) { |
| 519 libraryName = dart.getDartLibraryName(object); | 513 libraryName = dart.getDartLibraryName(object); |
| 520 return libraryName != null; | 514 return libraryName != null; |
| 521 } | 515 } |
| 522 | 516 |
| 523 bool hasChildren(object) => true; | 517 bool hasChildren(object) => true; |
| 524 | 518 |
| 525 String preview(object) { | 519 String preview(object) { |
| 526 var libraryNames = libraryName.split('/'); | 520 var libraryNames = libraryName.split('/'); |
| 527 // Library names are received with a repeat directory name, so strip the | 521 // Library names are received with a repeat directory name, so strip the |
| 528 // last directory entry here to make the path cleaner. For example, the | 522 // last directory entry here to make the path cleaner. For example, the |
| (...skipping 12 matching lines...) Expand all Loading... |
| 541 // Replace __ with / to make file paths more readable. Then | 535 // Replace __ with / to make file paths more readable. Then |
| 542 // 'src__result__error' becomes 'src/result/error'. | 536 // 'src__result__error' becomes 'src/result/error'. |
| 543 name = '${name.replaceAll("__", "/")}.dart'; | 537 name = '${name.replaceAll("__", "/")}.dart'; |
| 544 children.add(new NameValuePair( | 538 children.add(new NameValuePair( |
| 545 name: name, value: new Library(name, value), hideName: true)); | 539 name: name, value: new Library(name, value), hideName: true)); |
| 546 } | 540 } |
| 547 return children.toList(); | 541 return children.toList(); |
| 548 } | 542 } |
| 549 } | 543 } |
| 550 | 544 |
| 551 /// Formatter for Dart Library objects. | 545 class LibraryFormatter implements Formatter { |
| 552 class LibraryFormatter extends ObjectFormatter { | |
| 553 var genericParameters = new HashMap<String, String>(); | 546 var genericParameters = new HashMap<String, String>(); |
| 554 | 547 |
| 555 accept(object) => object is Library; | 548 accept(object, config) => object is Library; |
| 556 | 549 |
| 557 bool hasChildren(object) => true; | 550 bool hasChildren(object) => true; |
| 558 | 551 |
| 559 String preview(object) => object.name; | 552 String preview(object) => object.name; |
| 560 | 553 |
| 561 List<NameValuePair> children(object) { | 554 List<NameValuePair> children(object) { |
| 562 var children = new LinkedHashSet<NameValuePair>(); | 555 var children = new LinkedHashSet<NameValuePair>(); |
| 563 var nonGenericProperties = new LinkedHashMap<String, Object>(); | 556 var nonGenericProperties = new LinkedHashMap<String, Object>(); |
| 564 var objectProperties = safeProperties(object.object); | 557 var objectProperties = safeProperties(object.object); |
| 565 objectProperties.forEach((name, value) { | 558 objectProperties.forEach((name, value) { |
| (...skipping 26 matching lines...) Expand all Loading... |
| 592 .replaceAll(new RegExp(r'[(|)]'), ''); | 585 .replaceAll(new RegExp(r'[(|)]'), ''); |
| 593 } | 586 } |
| 594 | 587 |
| 595 classChild(String name, Object child) { | 588 classChild(String name, Object child) { |
| 596 var typeName = getTypeName(child); | 589 var typeName = getTypeName(child); |
| 597 // Generic class names are generated with a $ at the end, so the | 590 // Generic class names are generated with a $ at the end, so the |
| 598 // corresponding non-generic class can be identified by adding $. | 591 // corresponding non-generic class can be identified by adding $. |
| 599 var parameterName = '$name\$'; | 592 var parameterName = '$name\$'; |
| 600 if (genericParameters.keys.contains(parameterName)) { | 593 if (genericParameters.keys.contains(parameterName)) { |
| 601 typeName = '$typeName<${genericParameters[parameterName]}>'; | 594 typeName = '$typeName<${genericParameters[parameterName]}>'; |
| 595 // TODO(bmilligan): Add a symbol to classes with generic types at their |
| 596 // creation so they can be recognized independently by the debugger. |
| 597 JSNative.setProperty(child, 'genericTypeName', typeName); |
| 602 } | 598 } |
| 603 return new NameValuePair( | 599 return new NameValuePair(name: typeName, value: child); |
| 604 name: typeName, value: new ClassMetadata(child, name: typeName)); | |
| 605 } | 600 } |
| 606 } | 601 } |
| 607 | 602 |
| 608 /// Formatter for Dart Function objects. | 603 /// Formatter for Dart Function objects. |
| 609 /// Dart functions happen to be regular JavaScript Function objects but | 604 /// Dart functions happen to be regular JavaScript Function objects but |
| 610 /// we can distinguish them based on whether they have been tagged with | 605 /// we can distinguish them based on whether they have been tagged with |
| 611 /// runtime type information. | 606 /// runtime type information. |
| 612 class FunctionFormatter extends Formatter { | 607 class FunctionFormatter implements Formatter { |
| 613 accept(object) { | 608 accept(object, config) { |
| 614 if (_typeof(object) != 'function') return false; | 609 if (_typeof(object) != 'function') return false; |
| 615 return dart.getReifiedType(object) != null; | 610 return dart.getReifiedType(object) != null; |
| 616 } | 611 } |
| 617 | 612 |
| 618 bool hasChildren(object) => true; | 613 bool hasChildren(object) => true; |
| 619 | 614 |
| 620 String preview(object) { | 615 String preview(object) { |
| 621 return dart.typeName(dart.getReifiedType(object)); | 616 return dart.typeName(dart.getReifiedType(object)); |
| 622 } | 617 } |
| 623 | 618 |
| 624 List<NameValuePair> children(object) => <NameValuePair>[ | 619 List<NameValuePair> children(object) => <NameValuePair>[ |
| 625 new NameValuePair(name: 'signature', value: preview(object)), | 620 new NameValuePair(name: 'signature', value: preview(object)), |
| 626 new NameValuePair( | 621 new NameValuePair( |
| 627 name: 'JavaScript Function', | 622 name: 'JavaScript Function', |
| 628 value: object, | 623 value: object, |
| 629 config: JsonMLConfig.skipDart) | 624 config: JsonMLConfig.skipDart) |
| 630 ]; | 625 ]; |
| 631 } | 626 } |
| 632 | 627 |
| 633 /// Formatter for Dart Map objects. | 628 /// Formatter for Dart Map objects. |
| 634 class MapFormatter extends ObjectFormatter { | 629 class MapFormatter implements Formatter { |
| 635 accept(object) => object is Map; | 630 accept(object, config) => object is Map; |
| 636 | 631 |
| 637 bool hasChildren(object) => true; | 632 bool hasChildren(object) => true; |
| 638 | 633 |
| 639 String preview(object) { | 634 String preview(object) { |
| 640 Map map = object; | 635 Map map = object; |
| 641 return '${getObjectTypeName(map)} length ${map.length}'; | 636 return '${getObjectTypeName(map)} length ${map.length}'; |
| 642 } | 637 } |
| 643 | 638 |
| 644 List<NameValuePair> children(object) { | 639 List<NameValuePair> children(object) { |
| 645 // TODO(jacobr): be lazier about enumerating contents of Maps that are not | 640 // TODO(jacobr): be lazier about enumerating contents of Maps that are not |
| 646 // the build in LinkedHashMap class. | 641 // the build in LinkedHashMap class. |
| 647 // TODO(jacobr): handle large Maps better. | 642 // TODO(jacobr): handle large Maps better. |
| 648 Map map = object; | 643 Map map = object; |
| 649 var entries = new LinkedHashSet<NameValuePair>(); | 644 var entries = new LinkedHashSet<NameValuePair>(); |
| 650 map.forEach((key, value) { | 645 map.forEach((key, value) { |
| 651 var entryWrapper = new MapEntry(key: key, value: value); | 646 var entryWrapper = new MapEntry(key: key, value: value); |
| 652 entries.add(new NameValuePair( | 647 entries.add(new NameValuePair( |
| 653 name: entries.length.toString(), value: entryWrapper)); | 648 name: entries.length.toString(), value: entryWrapper)); |
| 654 }); | 649 }); |
| 655 addMetadataChildren(object, entries); | 650 addMetadataChildren(object, entries); |
| 656 return entries.toList(); | 651 return entries.toList(); |
| 657 } | 652 } |
| 658 } | 653 } |
| 659 | 654 |
| 660 /// Formatter for Dart Iterable objects including List and Set. | 655 /// Formatter for Dart Iterable objects including List and Set. |
| 661 class IterableFormatter extends ObjectFormatter { | 656 class IterableFormatter implements Formatter { |
| 662 bool accept(object) => object is Iterable; | 657 bool accept(object, config) => object is Iterable; |
| 663 | 658 |
| 664 String preview(object) { | 659 String preview(object) { |
| 665 Iterable iterable = object; | 660 Iterable iterable = object; |
| 666 try { | 661 try { |
| 667 var length = iterable.length; | 662 var length = iterable.length; |
| 668 return '${getObjectTypeName(iterable)} length $length'; | 663 return '${getObjectTypeName(iterable)} length $length'; |
| 669 } catch (_) { | 664 } catch (_) { |
| 670 return '${getObjectTypeName(iterable)}'; | 665 return '${getObjectTypeName(iterable)}'; |
| 671 } | 666 } |
| 672 } | 667 } |
| 673 | 668 |
| 674 bool hasChildren(object) => true; | 669 bool hasChildren(object) => true; |
| 675 | 670 |
| 676 List<NameValuePair> children(object) { | 671 List<NameValuePair> children(object) { |
| 677 // TODO(jacobr): be lazier about enumerating contents of Iterables that | 672 // TODO(jacobr): be lazier about enumerating contents of Iterables that |
| 678 // are not the built in Set or List types. | 673 // are not the built in Set or List types. |
| 679 // TODO(jacobr): handle large Iterables better. | 674 // TODO(jacobr): handle large Iterables better. |
| 680 // TODO(jacobr): consider only using numeric indices | 675 // TODO(jacobr): consider only using numeric indices |
| 681 var children = new LinkedHashSet<NameValuePair>(); | 676 var children = new LinkedHashSet<NameValuePair>(); |
| 682 children.addAll(new IterableSpan(0, object.length, object).children()); | 677 children.addAll(new IterableSpan(0, object.length, object).children()); |
| 683 // TODO(jacobr): provide a link to show regular class properties here. | 678 // TODO(jacobr): provide a link to show regular class properties here. |
| 684 // required for subclasses of iterable, etc. | 679 // required for subclasses of iterable, etc. |
| 685 addMetadataChildren(object, children); | 680 addMetadataChildren(object, children); |
| 686 return children.toList(); | 681 return children.toList(); |
| 687 } | 682 } |
| 688 } | 683 } |
| 689 | 684 |
| 690 // This class does double duting displaying metadata for | |
| 691 class ClassMetadataFormatter implements Formatter { | |
| 692 accept(object) => object is ClassMetadata; | |
| 693 | |
| 694 Object _getType(object) { | |
| 695 if (object is Type) return object; | |
| 696 return dart.getReifiedType(object); | |
| 697 } | |
| 698 | |
| 699 String preview(object) { | |
| 700 ClassMetadata entry = object; | |
| 701 var type = | |
| 702 entry.object is Type ? entry.object : dart.getReifiedType(entry.object); | |
| 703 var implements = dart.getImplements(type); | |
| 704 if (implements != null) { | |
| 705 var typeNames = implements().map(getTypeName); | |
| 706 return '${entry.typeName} implements ${typeNames.join(", ")}'; | |
| 707 } else { | |
| 708 return entry.typeName; | |
| 709 } | |
| 710 } | |
| 711 | |
| 712 bool hasChildren(object) => true; | |
| 713 | |
| 714 List<NameValuePair> children(object) { | |
| 715 ClassMetadata entry = object; | |
| 716 var classObject = entry.object; | |
| 717 // TODO(jacobr): add other entries describing the class such as | |
| 718 // links to the superclass, mixins, implemented interfaces, and methods. | |
| 719 var type = _getType(classObject); | |
| 720 var children = <NameValuePair>[]; | |
| 721 | |
| 722 var mixins = dart.getMixins(type); | |
| 723 if (mixins != null && mixins.isNotEmpty) { | |
| 724 children.add(new NameValuePair( | |
| 725 name: '[[Mixins]]', value: new HeritageClause('mixins', mixins))); | |
| 726 } | |
| 727 | |
| 728 var hiddenProperties = ['length', 'name', 'prototype']; | |
| 729 // Addition of NameValuePairs for static variables and named constructors. | |
| 730 for (var name in getOwnPropertyNames(classObject)) { | |
| 731 // TODO(bmilligan): Perform more principled checks to filter out spurious | |
| 732 // members. | |
| 733 if (hiddenProperties.contains(name)) continue; | |
| 734 var value = safeGetProperty(classObject, name); | |
| 735 if (value != null && dart.getIsNamedConstructor(value) != null) { | |
| 736 value = new NamedConstructor(value); | |
| 737 name = '${entry.typeName}.$name'; | |
| 738 } | |
| 739 children.add(new NameValuePair(name: name, value: value)); | |
| 740 } | |
| 741 | |
| 742 // TODO(bmilligan): Replace the hard coding of $identityHash. | |
| 743 var hiddenPrototypeProperties = ['constructor', 'new', r'$identityHash']; | |
| 744 // Addition of class methods. | |
| 745 var prototype = JS('var', '#["prototype"]', classObject); | |
| 746 if (prototype != null) { | |
| 747 for (var name in getOwnPropertyNames(prototype)) { | |
| 748 if (hiddenPrototypeProperties.contains(name)) continue; | |
| 749 // Simulate dart.bind by using dart.tag and tear off the function | |
| 750 // so it will be recognized by the FunctionFormatter. | |
| 751 var function = safeGetProperty(prototype, name); | |
| 752 var constructor = safeGetProperty(prototype, 'constructor'); | |
| 753 var sigObj = dart.getMethodSig(constructor); | |
| 754 if (sigObj != null) { | |
| 755 var value = safeGetProperty(sigObj, name); | |
| 756 if (getTypeName(dart.getReifiedType(value)) != 'Null') { | |
| 757 dart.tag(function, value); | |
| 758 children.add(new NameValuePair(name: name, value: function)); | |
| 759 } | |
| 760 } | |
| 761 } | |
| 762 } | |
| 763 // TODO(jacobr): provide a link to the base class or perhaps the entire | |
| 764 // base class hierarchy as a flat list. | |
| 765 // TODO(jacobr): add constructors, methods, extended class, and static | |
| 766 return children; | |
| 767 } | |
| 768 } | |
| 769 | |
| 770 class NamedConstructorFormatter implements Formatter { | 685 class NamedConstructorFormatter implements Formatter { |
| 771 accept(object) => object is NamedConstructor; | 686 accept(object, config) => object is NamedConstructor; |
| 772 | 687 |
| 773 // TODO(bmilligan): Display the signature of the named constructor as the | 688 // TODO(bmilligan): Display the signature of the named constructor as the |
| 774 // preview. | 689 // preview. |
| 775 String preview(object) => 'Named Constructor'; | 690 String preview(object) => 'Named Constructor'; |
| 776 | 691 |
| 777 bool hasChildren(object) => true; | 692 bool hasChildren(object) => true; |
| 778 | 693 |
| 779 List<NameValuePair> children(object) => <NameValuePair>[ | 694 List<NameValuePair> children(object) => <NameValuePair>[ |
| 780 new NameValuePair( | 695 new NameValuePair( |
| 781 name: 'JavaScript Function', | 696 name: 'JavaScript Function', |
| 782 value: object, | 697 value: object, |
| 783 config: JsonMLConfig.skipDart) | 698 config: JsonMLConfig.skipDart) |
| 784 ]; | 699 ]; |
| 785 } | 700 } |
| 786 | 701 |
| 787 /// Formatter for synthetic MapEntry objects used to display contents of a Map | 702 /// Formatter for synthetic MapEntry objects used to display contents of a Map |
| 788 /// cleanly. | 703 /// cleanly. |
| 789 class MapEntryFormatter implements Formatter { | 704 class MapEntryFormatter implements Formatter { |
| 790 accept(object) => object is MapEntry; | 705 accept(object, config) => object is MapEntry; |
| 791 | 706 |
| 792 String preview(object) { | 707 String preview(object) { |
| 793 MapEntry entry = object; | 708 MapEntry entry = object; |
| 794 return '${safePreview(entry.key)} => ${safePreview(entry.value)}'; | 709 return '${safePreview(entry.key, JsonMLConfig.none)} => ${safePreview(entry.
value, JsonMLConfig.none)}'; |
| 795 } | 710 } |
| 796 | 711 |
| 797 bool hasChildren(object) => true; | 712 bool hasChildren(object) => true; |
| 798 | 713 |
| 799 List<NameValuePair> children(object) => <NameValuePair>[ | 714 List<NameValuePair> children(object) => <NameValuePair>[ |
| 800 new NameValuePair( | 715 new NameValuePair( |
| 801 name: 'key', value: object.key, config: JsonMLConfig.keyToString), | 716 name: 'key', value: object.key, config: JsonMLConfig.keyToString), |
| 802 new NameValuePair(name: 'value', value: object.value) | 717 new NameValuePair(name: 'value', value: object.value) |
| 803 ]; | 718 ]; |
| 804 } | 719 } |
| 805 | 720 |
| 806 /// Formatter for Dart Iterable objects including List and Set. | 721 /// Formatter for Dart Iterable objects including List and Set. |
| 807 class HeritageClauseFormatter implements Formatter { | 722 class HeritageClauseFormatter implements Formatter { |
| 808 bool accept(object) => object is HeritageClause; | 723 bool accept(object, config) => object is HeritageClause; |
| 809 | 724 |
| 810 String preview(object) { | 725 String preview(object) { |
| 811 HeritageClause clause = object; | 726 HeritageClause clause = object; |
| 812 var typeNames = clause.types.map(getTypeName); | 727 var typeNames = clause.types.map(getTypeName); |
| 813 return '${clause.name} ${typeNames.join(", ")}'; | 728 return '${clause.name} ${typeNames.join(", ")}'; |
| 814 } | 729 } |
| 815 | 730 |
| 816 bool hasChildren(object) => true; | 731 bool hasChildren(object) => true; |
| 817 | 732 |
| 818 List<NameValuePair> children(object) { | 733 List<NameValuePair> children(object) { |
| 819 HeritageClause clause = object; | 734 HeritageClause clause = object; |
| 820 var children = <NameValuePair>[]; | 735 var children = <NameValuePair>[]; |
| 821 for (var type in clause.types) { | 736 for (var type in clause.types) { |
| 822 children.add(new NameValuePair(value: new ClassMetadata(type))); | 737 children |
| 738 .add(new NameValuePair(value: type, config: JsonMLConfig.asClass)); |
| 823 } | 739 } |
| 824 return children; | 740 return children; |
| 825 } | 741 } |
| 826 } | 742 } |
| 827 | 743 |
| 828 /// Formatter for synthetic IterableSpan objects used to display contents of | 744 /// Formatter for synthetic IterableSpan objects used to display contents of |
| 829 /// an Iterable cleanly. | 745 /// an Iterable cleanly. |
| 830 class IterableSpanFormatter implements Formatter { | 746 class IterableSpanFormatter implements Formatter { |
| 831 accept(object) => object is IterableSpan; | 747 accept(object, config) => object is IterableSpan; |
| 832 | 748 |
| 833 String preview(object) { | 749 String preview(object) { |
| 834 return '[${object.start}...${object.end-1}]'; | 750 return '[${object.start}...${object.end-1}]'; |
| 835 } | 751 } |
| 836 | 752 |
| 837 bool hasChildren(object) => true; | 753 bool hasChildren(object) => true; |
| 838 | 754 |
| 839 List<NameValuePair> children(object) => object.children(); | 755 List<NameValuePair> children(object) => object.children(); |
| 840 } | 756 } |
| 841 | 757 |
| 842 class StackTraceFormatter implements Formatter { | 758 class StackTraceFormatter implements Formatter { |
| 843 accept(object) => object is StackTrace; | 759 accept(object, config) => object is StackTrace; |
| 844 | 760 |
| 845 String preview(object) => 'StackTrace'; | 761 String preview(object) => 'StackTrace'; |
| 846 | 762 |
| 847 bool hasChildren(object) => true; | 763 bool hasChildren(object) => true; |
| 848 | 764 |
| 849 // Using the stack_trace formatting would be ideal, but adding the | 765 // Using the stack_trace formatting would be ideal, but adding the |
| 850 // dependency or re-writing the code is too messy, so each line of the | 766 // dependency or re-writing the code is too messy, so each line of the |
| 851 // StackTrace will be added as its own child. | 767 // StackTrace will be added as its own child. |
| 852 List<NameValuePair> children(object) => object | 768 List<NameValuePair> children(object) => object |
| 853 .toString() | 769 .toString() |
| 854 .split('\n') | 770 .split('\n') |
| 855 .map((line) => new NameValuePair( | 771 .map((line) => new NameValuePair( |
| 856 value: line.replaceFirst(new RegExp(r'^\s+at\s'), ''), | 772 value: line.replaceFirst(new RegExp(r'^\s+at\s'), ''), |
| 857 hideName: true)) | 773 hideName: true)) |
| 858 .toList(); | 774 .toList(); |
| 859 } | 775 } |
| 860 | 776 |
| 777 class ClassFormatter implements Formatter { |
| 778 accept(object, config) => object is Type || config == JsonMLConfig.asClass; |
| 779 |
| 780 String preview(object) { |
| 781 var typeName = safeGetProperty(object, 'genericTypeName'); |
| 782 if (typeName != null) return typeName; |
| 783 var type = _getType(object); |
| 784 var implements = dart.getImplements(type); |
| 785 typeName = getTypeName(type); |
| 786 if (implements != null) { |
| 787 var typeNames = implements().map(getTypeName); |
| 788 return '${typeName} implements ${typeNames.join(", ")}'; |
| 789 } else { |
| 790 return typeName; |
| 791 } |
| 792 } |
| 793 |
| 794 bool hasChildren(object) => true; |
| 795 |
| 796 List<NameValuePair> children(object) { |
| 797 // TODO(jacobr): add other entries describing the class such as |
| 798 // links to the superclass, mixins, implemented interfaces, and methods. |
| 799 var type = _getType(object); |
| 800 var children = <NameValuePair>[]; |
| 801 var typeName = getTypeName(_getType(object)); |
| 802 var mixins = dart.getMixins(type); |
| 803 if (mixins != null && mixins.isNotEmpty) { |
| 804 children.add(new NameValuePair( |
| 805 name: '[[Mixins]]', value: new HeritageClause('mixins', mixins))); |
| 806 } |
| 807 |
| 808 var hiddenProperties = ['length', 'name', 'prototype', 'genericTypeName']; |
| 809 // Addition of NameValuePairs for static variables and named constructors. |
| 810 for (var name in getOwnPropertyNames(object)) { |
| 811 // TODO(bmilligan): Perform more principled checks to filter out spurious |
| 812 // members. |
| 813 if (hiddenProperties.contains(name)) continue; |
| 814 var value = safeGetProperty(object, name); |
| 815 if (value != null && dart.getIsNamedConstructor(value) != null) { |
| 816 value = new NamedConstructor(value); |
| 817 name = '${typeName}.$name'; |
| 818 } |
| 819 children.add(new NameValuePair(name: name, value: value)); |
| 820 } |
| 821 |
| 822 // TODO(bmilligan): Replace the hard coding of $identityHash. |
| 823 var hiddenPrototypeProperties = ['constructor', 'new', r'$identityHash']; |
| 824 // Addition of class methods. |
| 825 var prototype = JS('var', '#["prototype"]', object); |
| 826 if (prototype != null) { |
| 827 for (var name in getOwnPropertyNames(prototype)) { |
| 828 if (hiddenPrototypeProperties.contains(name)) continue; |
| 829 // Simulate dart.bind by using dart.tag and tear off the function |
| 830 // so it will be recognized by the FunctionFormatter. |
| 831 var function = safeGetProperty(prototype, name); |
| 832 var constructor = safeGetProperty(prototype, 'constructor'); |
| 833 var sigObj = dart.getMethodSig(constructor); |
| 834 if (sigObj != null) { |
| 835 var value = safeGetProperty(sigObj, name); |
| 836 if (getTypeName(dart.getReifiedType(value)) != 'Null') { |
| 837 dart.tag(function, value); |
| 838 children.add(new NameValuePair(name: name, value: function)); |
| 839 } |
| 840 } |
| 841 } |
| 842 } |
| 843 return children; |
| 844 } |
| 845 } |
| 846 |
| 861 /// This entry point is automatically invoked by the code generated by | 847 /// This entry point is automatically invoked by the code generated by |
| 862 /// Dart Dev Compiler | 848 /// Dart Dev Compiler |
| 863 registerDevtoolsFormatter() { | 849 registerDevtoolsFormatter() { |
| 864 var formatters = [_devtoolsFormatter]; | 850 var formatters = [_devtoolsFormatter]; |
| 865 JS('', 'dart.global.devtoolsFormatters = #', formatters); | 851 JS('', 'dart.global.devtoolsFormatters = #', formatters); |
| 866 } | 852 } |
| OLD | NEW |