Chromium Code Reviews| 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 renderAsClass = const JsonMLConfig("renderAsClass"); | |
|
Jacob
2016/08/02 01:45:11
no need for the word render. These are all options
bmilligan
2016/08/02 16:28:39
Done.
| |
| 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.renderAsClass)); | |
| 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 |
| 83 Object _getType(object) => | |
| 84 object is Type ? object : dart.getReifiedType(object); | |
| 85 | |
| 76 String safePreview(object) { | 86 String safePreview(object) { |
| 77 try { | 87 try { |
| 78 var preview = _devtoolsFormatter._simpleFormatter.preview(object); | 88 var preview = _devtoolsFormatter._simpleFormatter.preview(object); |
| 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 |
| (...skipping 90 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: 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) => |
| 319 _simpleFormatter.hasChildren(object, config: config); | |
| 321 | 320 |
| 322 body(object) { | 321 body(object, config) { |
| 323 var body = new JsonMLElement('ol') | 322 var body = new JsonMLElement('ol') |
| 324 ..setStyle('list-style-type: none;' | 323 ..setStyle('list-style-type: none;' |
| 325 'padding-left: 0px;' | 324 'padding-left: 0px;' |
| 326 'margin-top: 0px;' | 325 'margin-top: 0px;' |
| 327 'margin-bottom: 0px;' | 326 'margin-bottom: 0px;' |
| 328 'margin-left: 12px;'); | 327 'margin-left: 12px;'); |
| 329 if (object is StackTrace) { | 328 if (object is StackTrace) { |
| 330 body.addStyle('color: rgb(196, 26, 22);'); | 329 body.addStyle('color: rgb(196, 26, 22);'); |
| 331 } | 330 } |
| 332 var children = _simpleFormatter.children(object); | 331 var children = _simpleFormatter.children(object, config: config); |
| 333 for (NameValuePair child in children) { | 332 for (NameValuePair child in children) { |
| 334 var li = body.createChild('li'); | 333 var li = body.createChild('li'); |
| 335 var nameSpan = new JsonMLElement('span') | 334 var nameSpan = new JsonMLElement('span') |
| 336 ..createTextChild( | 335 ..createTextChild( |
| 337 child.displayName.isNotEmpty ? '${child.displayName}: ' : '') | 336 child.displayName.isNotEmpty ? '${child.displayName}: ' : '') |
| 338 ..setStyle('color: rgb(136, 19, 145);'); | 337 ..setStyle('color: rgb(136, 19, 145);'); |
| 339 if (_typeof(child.value) == 'object' || | 338 if (_typeof(child.value) == 'object' || |
| 340 _typeof(child.value) == 'function') { | 339 _typeof(child.value) == 'function') { |
| 341 nameSpan.addStyle("padding-left: 13px;"); | 340 nameSpan.addStyle("padding-left: 13px;"); |
| 342 | 341 |
| 343 li.appendChild(nameSpan); | 342 li.appendChild(nameSpan); |
| 344 var objectTag = li.createObjectTag(child.value); | 343 var objectTag = li.createObjectTag(child.value); |
| 345 objectTag.addAttribute('config', child.config); | 344 objectTag.addAttribute('config', child.config); |
| 346 if (!_simpleFormatter.hasChildren(child.value)) { | 345 if (!_simpleFormatter.hasChildren(child.value, config: child.config)) { |
| 347 li.setStyle("padding-left: 13px;"); | 346 li.setStyle("padding-left: 13px;"); |
| 348 } | 347 } |
| 349 } else { | 348 } else { |
| 350 li.setStyle("padding-left: 13px;"); | 349 li.setStyle("padding-left: 13px;"); |
| 351 li.createChild('span') | 350 li.createChild('span') |
| 352 ..appendChild(nameSpan) | 351 ..appendChild(nameSpan) |
| 353 ..createTextChild(safePreview(child.value)); | 352 ..createTextChild(safePreview(child.value)); |
| 354 } | 353 } |
| 355 } | 354 } |
| 356 return body.toJsonML(); | 355 return body.toJsonML(); |
| 357 } | 356 } |
| 358 } | 357 } |
| 359 | 358 |
| 360 abstract class Formatter { | 359 abstract class Formatter { |
| 361 bool accept(object); | 360 bool accept(object); |
| 362 String preview(object); | 361 String preview(object); |
| 363 bool hasChildren(object); | 362 bool hasChildren(object); |
| 364 List<NameValuePair> children(object); | 363 List<NameValuePair> children(object); |
| 365 } | 364 } |
| 366 | 365 |
| 367 class DartFormatter { | 366 class DartFormatter { |
| 368 List<Formatter> _formatters; | 367 List<Formatter> _formatters; |
| 369 | 368 |
| 370 DartFormatter() { | 369 DartFormatter() { |
| 371 // The order of formatters matters as formatters earlier in the list take | 370 // The order of formatters matters as formatters earlier in the list take |
| 372 // precedence. | 371 // precedence. |
| 373 _formatters = [ | 372 _formatters = [ |
| 373 new ClassFormatter(), | |
| 374 new NamedConstructorFormatter(), | 374 new NamedConstructorFormatter(), |
| 375 new FunctionFormatter(), | |
| 376 new MapFormatter(), | 375 new MapFormatter(), |
| 377 new IterableFormatter(), | 376 new IterableFormatter(), |
| 377 new IterableSpanFormatter(), | |
| 378 new MapEntryFormatter(), | 378 new MapEntryFormatter(), |
| 379 new IterableSpanFormatter(), | |
| 380 new StackTraceFormatter(), | 379 new StackTraceFormatter(), |
| 381 new ClassMetadataFormatter(), | 380 new FunctionFormatter(), |
| 382 new HeritageClauseFormatter(), | 381 new HeritageClauseFormatter(), |
| 383 new LibraryModuleFormatter(), | 382 new LibraryModuleFormatter(), |
| 384 new LibraryFormatter(), | 383 new LibraryFormatter(), |
| 385 new ObjectFormatter(), | 384 new ObjectFormatter(), |
| 386 ]; | 385 ]; |
| 387 } | 386 } |
| 388 | 387 |
| 389 String preview(object) { | 388 String preview(object, {config: JsonMLConfig.none}) { |
| 390 try { | 389 try { |
| 391 if (object == null || | 390 if (object == null || |
| 392 object is num || | 391 object is num || |
| 393 object is String || | 392 object is String || |
| 394 isNativeJavaScriptObject(object)) { | 393 isNativeJavaScriptObject(object)) { |
| 395 return object.toString(); | 394 return object.toString(); |
| 396 } | 395 } |
| 397 | 396 if (config == JsonMLConfig.renderAsClass) |
| 397 return new ClassFormatter().preview(object); | |
| 398 for (var formatter in _formatters) { | 398 for (var formatter in _formatters) { |
| 399 if (formatter.accept(object)) return formatter.preview(object); | 399 if (formatter.accept(object)) return formatter.preview(object); |
| 400 } | 400 } |
| 401 } catch (e, trace) { | 401 } catch (e, trace) { |
| 402 // Log formatter internal errors as unfortunately the devtools cannot | 402 // Log formatter internal errors as unfortunately the devtools cannot |
| 403 // be used to debug formatter errors. | 403 // be used to debug formatter errors. |
| 404 html.window.console.error("Caught exception $e\n trace:\n$trace"); | 404 html.window.console.error("Caught exception $e\n trace:\n$trace"); |
| 405 } | 405 } |
| 406 | 406 |
| 407 return null; | 407 return null; |
| 408 } | 408 } |
| 409 | 409 |
| 410 bool hasChildren(object) { | 410 bool hasChildren(object, {config: JsonMLConfig.none}) { |
| 411 if (object == null) return false; | 411 if (object == null) return false; |
| 412 try { | 412 try { |
| 413 if (config == JsonMLConfig.renderAsClass) | |
|
Jacob
2016/08/02 01:45:12
instead of special casing ClassFormatter here, pas
bmilligan
2016/08/02 16:28:39
Done.
| |
| 414 return new ClassFormatter().hasChildren(object); | |
| 413 for (var formatter in _formatters) { | 415 for (var formatter in _formatters) { |
| 414 if (formatter.accept(object)) return formatter.hasChildren(object); | 416 if (formatter.accept(object)) return formatter.hasChildren(object); |
| 415 } | 417 } |
| 416 } catch (e, trace) { | 418 } catch (e, trace) { |
| 417 // See comment for preview. | 419 // See comment for preview. |
| 418 html.window.console | 420 html.window.console |
| 419 .error("[hasChildren] Caught exception $e\n trace:\n$trace"); | 421 .error("[hasChildren] Caught exception $e\n trace:\n$trace"); |
| 420 } | 422 } |
| 421 return false; | 423 return false; |
| 422 } | 424 } |
| 423 | 425 |
| 424 List<NameValuePair> children(object) { | 426 List<NameValuePair> children(object, {config: JsonMLConfig.none}) { |
| 425 try { | 427 try { |
| 426 if (object != null) { | 428 if (object != null) { |
| 429 if (config == JsonMLConfig.renderAsClass) | |
| 430 return new ClassFormatter().children(object); | |
| 427 for (var formatter in _formatters) { | 431 for (var formatter in _formatters) { |
| 428 if (formatter.accept(object)) return formatter.children(object); | 432 if (formatter.accept(object)) return formatter.children(object); |
| 429 } | 433 } |
| 430 } | 434 } |
| 431 } catch (e, trace) { | 435 } catch (e, trace) { |
| 432 // See comment for preview. | 436 // See comment for preview. |
| 433 html.window.console.error("Caught exception $e\n trace:\n$trace"); | 437 html.window.console.error("Caught exception $e\n trace:\n$trace"); |
| 434 } | 438 } |
| 435 return <NameValuePair>[]; | 439 return <NameValuePair>[]; |
| 436 } | 440 } |
| (...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 497 if (hasMethod(object, name)) { | 501 if (hasMethod(object, name)) { |
| 498 continue; | 502 continue; |
| 499 } | 503 } |
| 500 var value = safeGetProperty(object, name); | 504 var value = safeGetProperty(object, name); |
| 501 properties.add(new NameValuePair(name: name, value: value)); | 505 properties.add(new NameValuePair(name: name, value: value)); |
| 502 } | 506 } |
| 503 } | 507 } |
| 504 | 508 |
| 505 return properties.toList(); | 509 return properties.toList(); |
| 506 } | 510 } |
| 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 } | 511 } |
| 513 | 512 |
| 514 /// Formatter for module Dart Library objects. | 513 /// Formatter for module Dart Library objects. |
| 515 class LibraryModuleFormatter extends ObjectFormatter { | 514 class LibraryModuleFormatter implements Formatter { |
| 516 String libraryName; | 515 String libraryName; |
| 517 | 516 |
| 518 accept(object) { | 517 accept(object) { |
| 519 libraryName = dart.getDartLibraryName(object); | 518 libraryName = dart.getDartLibraryName(object); |
| 520 return libraryName != null; | 519 return libraryName != null; |
| 521 } | 520 } |
| 522 | 521 |
| 523 bool hasChildren(object) => true; | 522 bool hasChildren(object) => true; |
| 524 | 523 |
| 525 String preview(object) { | 524 String preview(object) { |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 541 // Replace __ with / to make file paths more readable. Then | 540 // Replace __ with / to make file paths more readable. Then |
| 542 // 'src__result__error' becomes 'src/result/error'. | 541 // 'src__result__error' becomes 'src/result/error'. |
| 543 name = '${name.replaceAll("__", "/")}.dart'; | 542 name = '${name.replaceAll("__", "/")}.dart'; |
| 544 children.add(new NameValuePair( | 543 children.add(new NameValuePair( |
| 545 name: name, value: new Library(name, value), hideName: true)); | 544 name: name, value: new Library(name, value), hideName: true)); |
| 546 } | 545 } |
| 547 return children.toList(); | 546 return children.toList(); |
| 548 } | 547 } |
| 549 } | 548 } |
| 550 | 549 |
| 551 /// Formatter for Dart Library objects. | 550 class LibraryFormatter implements Formatter { |
| 552 class LibraryFormatter extends ObjectFormatter { | |
| 553 var genericParameters = new HashMap<String, String>(); | 551 var genericParameters = new HashMap<String, String>(); |
| 554 | 552 |
| 555 accept(object) => object is Library; | 553 accept(object) => object is Library; |
| 556 | 554 |
| 557 bool hasChildren(object) => true; | 555 bool hasChildren(object) => true; |
| 558 | 556 |
| 559 String preview(object) => object.name; | 557 String preview(object) => object.name; |
| 560 | 558 |
| 561 List<NameValuePair> children(object) { | 559 List<NameValuePair> children(object) { |
| 562 var children = new LinkedHashSet<NameValuePair>(); | 560 var children = new LinkedHashSet<NameValuePair>(); |
| (...skipping 29 matching lines...) Expand all Loading... | |
| 592 .replaceAll(new RegExp(r'[(|)]'), ''); | 590 .replaceAll(new RegExp(r'[(|)]'), ''); |
| 593 } | 591 } |
| 594 | 592 |
| 595 classChild(String name, Object child) { | 593 classChild(String name, Object child) { |
| 596 var typeName = getTypeName(child); | 594 var typeName = getTypeName(child); |
| 597 // Generic class names are generated with a $ at the end, so the | 595 // Generic class names are generated with a $ at the end, so the |
| 598 // corresponding non-generic class can be identified by adding $. | 596 // corresponding non-generic class can be identified by adding $. |
| 599 var parameterName = '$name\$'; | 597 var parameterName = '$name\$'; |
| 600 if (genericParameters.keys.contains(parameterName)) { | 598 if (genericParameters.keys.contains(parameterName)) { |
| 601 typeName = '$typeName<${genericParameters[parameterName]}>'; | 599 typeName = '$typeName<${genericParameters[parameterName]}>'; |
| 600 JSNative.setProperty(child, 'genericTypeName', typeName); | |
| 602 } | 601 } |
| 603 return new NameValuePair( | 602 return new NameValuePair(name: typeName, value: child); |
| 604 name: typeName, value: new ClassMetadata(child, name: typeName)); | |
| 605 } | 603 } |
| 606 } | 604 } |
| 607 | 605 |
| 608 /// Formatter for Dart Function objects. | 606 /// Formatter for Dart Function objects. |
| 609 /// Dart functions happen to be regular JavaScript Function objects but | 607 /// Dart functions happen to be regular JavaScript Function objects but |
| 610 /// we can distinguish them based on whether they have been tagged with | 608 /// we can distinguish them based on whether they have been tagged with |
| 611 /// runtime type information. | 609 /// runtime type information. |
| 612 class FunctionFormatter extends Formatter { | 610 class FunctionFormatter implements Formatter { |
| 613 accept(object) { | 611 accept(object) { |
| 614 if (_typeof(object) != 'function') return false; | 612 if (_typeof(object) != 'function') return false; |
| 615 return dart.getReifiedType(object) != null; | 613 return dart.getReifiedType(object) != null; |
| 616 } | 614 } |
| 617 | 615 |
| 618 bool hasChildren(object) => true; | 616 bool hasChildren(object) => true; |
| 619 | 617 |
| 620 String preview(object) { | 618 String preview(object) { |
| 621 return dart.typeName(dart.getReifiedType(object)); | 619 return dart.typeName(dart.getReifiedType(object)); |
| 622 } | 620 } |
| 623 | 621 |
| 624 List<NameValuePair> children(object) => <NameValuePair>[ | 622 List<NameValuePair> children(object) => <NameValuePair>[ |
| 625 new NameValuePair(name: 'signature', value: preview(object)), | 623 new NameValuePair(name: 'signature', value: preview(object)), |
| 626 new NameValuePair( | 624 new NameValuePair( |
| 627 name: 'JavaScript Function', | 625 name: 'JavaScript Function', |
| 628 value: object, | 626 value: object, |
| 629 config: JsonMLConfig.skipDart) | 627 config: JsonMLConfig.skipDart) |
| 630 ]; | 628 ]; |
| 631 } | 629 } |
| 632 | 630 |
| 633 /// Formatter for Dart Map objects. | 631 /// Formatter for Dart Map objects. |
| 634 class MapFormatter extends ObjectFormatter { | 632 class MapFormatter implements Formatter { |
| 635 accept(object) => object is Map; | 633 accept(object) => object is Map; |
| 636 | 634 |
| 637 bool hasChildren(object) => true; | 635 bool hasChildren(object) => true; |
| 638 | 636 |
| 639 String preview(object) { | 637 String preview(object) { |
| 640 Map map = object; | 638 Map map = object; |
| 641 return '${getObjectTypeName(map)} length ${map.length}'; | 639 return '${getObjectTypeName(map)} length ${map.length}'; |
| 642 } | 640 } |
| 643 | 641 |
| 644 List<NameValuePair> children(object) { | 642 List<NameValuePair> children(object) { |
| 645 // TODO(jacobr): be lazier about enumerating contents of Maps that are not | 643 // TODO(jacobr): be lazier about enumerating contents of Maps that are not |
| 646 // the build in LinkedHashMap class. | 644 // the build in LinkedHashMap class. |
| 647 // TODO(jacobr): handle large Maps better. | 645 // TODO(jacobr): handle large Maps better. |
| 648 Map map = object; | 646 Map map = object; |
| 649 var entries = new LinkedHashSet<NameValuePair>(); | 647 var entries = new LinkedHashSet<NameValuePair>(); |
| 650 map.forEach((key, value) { | 648 map.forEach((key, value) { |
| 651 var entryWrapper = new MapEntry(key: key, value: value); | 649 var entryWrapper = new MapEntry(key: key, value: value); |
| 652 entries.add(new NameValuePair( | 650 entries.add(new NameValuePair( |
| 653 name: entries.length.toString(), value: entryWrapper)); | 651 name: entries.length.toString(), value: entryWrapper)); |
| 654 }); | 652 }); |
| 655 addMetadataChildren(object, entries); | 653 addMetadataChildren(object, entries); |
| 656 return entries.toList(); | 654 return entries.toList(); |
| 657 } | 655 } |
| 658 } | 656 } |
| 659 | 657 |
| 660 /// Formatter for Dart Iterable objects including List and Set. | 658 /// Formatter for Dart Iterable objects including List and Set. |
| 661 class IterableFormatter extends ObjectFormatter { | 659 class IterableFormatter implements Formatter { |
| 662 bool accept(object) => object is Iterable; | 660 bool accept(object) => object is Iterable; |
| 663 | 661 |
| 664 String preview(object) { | 662 String preview(object) { |
| 665 Iterable iterable = object; | 663 Iterable iterable = object; |
| 666 try { | 664 try { |
| 667 var length = iterable.length; | 665 var length = iterable.length; |
| 668 return '${getObjectTypeName(iterable)} length $length'; | 666 return '${getObjectTypeName(iterable)} length $length'; |
| 669 } catch (_) { | 667 } catch (_) { |
| 670 return '${getObjectTypeName(iterable)}'; | 668 return '${getObjectTypeName(iterable)}'; |
| 671 } | 669 } |
| 672 } | 670 } |
| 673 | 671 |
| 674 bool hasChildren(object) => true; | 672 bool hasChildren(object) => true; |
| 675 | 673 |
| 676 List<NameValuePair> children(object) { | 674 List<NameValuePair> children(object) { |
| 677 // TODO(jacobr): be lazier about enumerating contents of Iterables that | 675 // TODO(jacobr): be lazier about enumerating contents of Iterables that |
| 678 // are not the built in Set or List types. | 676 // are not the built in Set or List types. |
| 679 // TODO(jacobr): handle large Iterables better. | 677 // TODO(jacobr): handle large Iterables better. |
| 680 // TODO(jacobr): consider only using numeric indices | 678 // TODO(jacobr): consider only using numeric indices |
| 681 var children = new LinkedHashSet<NameValuePair>(); | 679 var children = new LinkedHashSet<NameValuePair>(); |
| 682 children.addAll(new IterableSpan(0, object.length, object).children()); | 680 children.addAll(new IterableSpan(0, object.length, object).children()); |
| 683 // TODO(jacobr): provide a link to show regular class properties here. | 681 // TODO(jacobr): provide a link to show regular class properties here. |
| 684 // required for subclasses of iterable, etc. | 682 // required for subclasses of iterable, etc. |
| 685 addMetadataChildren(object, children); | 683 addMetadataChildren(object, children); |
| 686 return children.toList(); | 684 return children.toList(); |
| 687 } | 685 } |
| 688 } | 686 } |
| 689 | 687 |
| 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 { | 688 class NamedConstructorFormatter implements Formatter { |
| 771 accept(object) => object is NamedConstructor; | 689 accept(object) => object is NamedConstructor; |
| 772 | 690 |
| 773 // TODO(bmilligan): Display the signature of the named constructor as the | 691 // TODO(bmilligan): Display the signature of the named constructor as the |
| 774 // preview. | 692 // preview. |
| 775 String preview(object) => 'Named Constructor'; | 693 String preview(object) => 'Named Constructor'; |
| 776 | 694 |
| 777 bool hasChildren(object) => true; | 695 bool hasChildren(object) => true; |
| 778 | 696 |
| 779 List<NameValuePair> children(object) => <NameValuePair>[ | 697 List<NameValuePair> children(object) => <NameValuePair>[ |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 812 var typeNames = clause.types.map(getTypeName); | 730 var typeNames = clause.types.map(getTypeName); |
| 813 return '${clause.name} ${typeNames.join(", ")}'; | 731 return '${clause.name} ${typeNames.join(", ")}'; |
| 814 } | 732 } |
| 815 | 733 |
| 816 bool hasChildren(object) => true; | 734 bool hasChildren(object) => true; |
| 817 | 735 |
| 818 List<NameValuePair> children(object) { | 736 List<NameValuePair> children(object) { |
| 819 HeritageClause clause = object; | 737 HeritageClause clause = object; |
| 820 var children = <NameValuePair>[]; | 738 var children = <NameValuePair>[]; |
| 821 for (var type in clause.types) { | 739 for (var type in clause.types) { |
| 822 children.add(new NameValuePair(value: new ClassMetadata(type))); | 740 children.add( |
| 741 new NameValuePair(value: type, config: JsonMLConfig.renderAsClass)); | |
| 823 } | 742 } |
| 824 return children; | 743 return children; |
| 825 } | 744 } |
| 826 } | 745 } |
| 827 | 746 |
| 828 /// Formatter for synthetic IterableSpan objects used to display contents of | 747 /// Formatter for synthetic IterableSpan objects used to display contents of |
| 829 /// an Iterable cleanly. | 748 /// an Iterable cleanly. |
| 830 class IterableSpanFormatter implements Formatter { | 749 class IterableSpanFormatter implements Formatter { |
| 831 accept(object) => object is IterableSpan; | 750 accept(object) => object is IterableSpan; |
| 832 | 751 |
| (...skipping 18 matching lines...) Expand all Loading... | |
| 851 // StackTrace will be added as its own child. | 770 // StackTrace will be added as its own child. |
| 852 List<NameValuePair> children(object) => object | 771 List<NameValuePair> children(object) => object |
| 853 .toString() | 772 .toString() |
| 854 .split('\n') | 773 .split('\n') |
| 855 .map((line) => new NameValuePair( | 774 .map((line) => new NameValuePair( |
| 856 value: line.replaceFirst(new RegExp(r'^\s+at\s'), ''), | 775 value: line.replaceFirst(new RegExp(r'^\s+at\s'), ''), |
| 857 hideName: true)) | 776 hideName: true)) |
| 858 .toList(); | 777 .toList(); |
| 859 } | 778 } |
| 860 | 779 |
| 780 class ClassFormatter implements Formatter { | |
| 781 accept(object) => object is Type; | |
|
Jacob
2016/08/02 01:45:11
change this accept method to take the JSONMLConfig
bmilligan
2016/08/02 16:28:39
Done.
| |
| 782 | |
| 783 String preview(object) { | |
| 784 // TODO(bmilligan): Tag classes with generic types with a Symbol at their | |
|
Jacob
2016/08/02 01:45:12
this comment is obsolete
bmilligan
2016/08/02 16:28:39
The emphasis on this comment is on the "generic ty
Jacob
2016/08/02 16:39:44
Acknowledged.
| |
| 785 // creation so they can be recognized by the ClassFormatter. | |
| 786 var typeName = safeGetProperty(object, 'genericTypeName'); | |
| 787 if (typeName != null) return typeName; | |
| 788 var type = _getType(object); | |
| 789 var implements = dart.getImplements(type); | |
| 790 typeName = getTypeName(type); | |
| 791 if (implements != null) { | |
| 792 var typeNames = implements().map(getTypeName); | |
| 793 return '${typeName} implements ${typeNames.join(", ")}'; | |
| 794 } else { | |
| 795 return typeName; | |
| 796 } | |
| 797 } | |
| 798 | |
| 799 bool hasChildren(object) => true; | |
| 800 | |
| 801 List<NameValuePair> children(object) { | |
| 802 // TODO(jacobr): add other entries describing the class such as | |
| 803 // links to the superclass, mixins, implemented interfaces, and methods. | |
| 804 var type = _getType(object); | |
| 805 var children = <NameValuePair>[]; | |
| 806 var typeName = getTypeName(_getType(object)); | |
| 807 var mixins = dart.getMixins(type); | |
| 808 if (mixins != null && mixins.isNotEmpty) { | |
| 809 children.add(new NameValuePair( | |
| 810 name: '[[Mixins]]', value: new HeritageClause('mixins', mixins))); | |
| 811 } | |
| 812 | |
| 813 var hiddenProperties = ['length', 'name', 'prototype', 'genericTypeName']; | |
| 814 // Addition of NameValuePairs for static variables and named constructors. | |
| 815 for (var name in getOwnPropertyNames(object)) { | |
| 816 // TODO(bmilligan): Perform more principled checks to filter out spurious | |
| 817 // members. | |
| 818 if (hiddenProperties.contains(name)) continue; | |
| 819 var value = safeGetProperty(object, name); | |
| 820 if (value != null && dart.getIsNamedConstructor(value) != null) { | |
| 821 value = new NamedConstructor(value); | |
| 822 name = '${typeName}.$name'; | |
| 823 } | |
| 824 children.add(new NameValuePair(name: name, value: value)); | |
| 825 } | |
| 826 | |
| 827 // TODO(bmilligan): Replace the hard coding of $identityHash. | |
| 828 var hiddenPrototypeProperties = ['constructor', 'new', r'$identityHash']; | |
| 829 // Addition of class methods. | |
| 830 var prototype = JS('var', '#["prototype"]', object); | |
| 831 if (prototype != null) { | |
| 832 for (var name in getOwnPropertyNames(prototype)) { | |
| 833 if (hiddenPrototypeProperties.contains(name)) continue; | |
| 834 // Simulate dart.bind by using dart.tag and tear off the function | |
| 835 // so it will be recognized by the FunctionFormatter. | |
| 836 var function = safeGetProperty(prototype, name); | |
| 837 var constructor = safeGetProperty(prototype, 'constructor'); | |
| 838 var sigObj = dart.getMethodSig(constructor); | |
| 839 if (sigObj != null) { | |
| 840 var value = safeGetProperty(sigObj, name); | |
| 841 if (getTypeName(dart.getReifiedType(value)) != 'Null') { | |
| 842 dart.tag(function, value); | |
| 843 children.add(new NameValuePair(name: name, value: function)); | |
| 844 } | |
| 845 } | |
| 846 } | |
| 847 } | |
| 848 return children; | |
| 849 } | |
| 850 } | |
| 851 | |
| 861 /// This entry point is automatically invoked by the code generated by | 852 /// This entry point is automatically invoked by the code generated by |
| 862 /// Dart Dev Compiler | 853 /// Dart Dev Compiler |
| 863 registerDevtoolsFormatter() { | 854 registerDevtoolsFormatter() { |
| 864 var formatters = [_devtoolsFormatter]; | 855 var formatters = [_devtoolsFormatter]; |
| 865 JS('', 'dart.global.devtoolsFormatters = #', formatters); | 856 JS('', 'dart.global.devtoolsFormatters = #', formatters); |
| 866 } | 857 } |
| OLD | NEW |