Index: sdk/lib/_internal/js_runtime/lib/js_helper.dart |
diff --git a/sdk/lib/_internal/js_runtime/lib/js_helper.dart b/sdk/lib/_internal/js_runtime/lib/js_helper.dart |
index 663121a605f99407676a7468c2bd6fe0ce0044ee..000753264a5a14a5d0146b9800c9770b9ec1c2c5 100644 |
--- a/sdk/lib/_internal/js_runtime/lib/js_helper.dart |
+++ b/sdk/lib/_internal/js_runtime/lib/js_helper.dart |
@@ -49,6 +49,7 @@ import 'dart:_foreign_helper' show |
JS_EMBEDDED_GLOBAL, |
JS_GET_FLAG, |
JS_GET_NAME, |
+ JS_INTERCEPTOR_CONSTANT, |
JS_STRING_CONCAT, |
RAW_DART_FUNCTION_REF; |
@@ -845,24 +846,71 @@ class Primitives { |
/// |
/// In minified mode, uses the unminified names if available. |
static String objectTypeName(Object object) { |
- String name = constructorNameFallback(getInterceptor(object)); |
- if (name == 'Object') { |
- // Try to decompile the constructor by turning it into a string and get |
- // the name out of that. If the decompiled name is a string containing an |
- // identifier, we use that instead of the very generic 'Object'. |
- var decompiled = |
- JS('var', r'#.match(/^\s*function\s*([\w$]*)\s*\(/)[1]', |
- JS('var', r'String(#.constructor)', object)); |
- if (decompiled is String) |
- if (JS('bool', r'/^\w+$/.test(#)', decompiled)) |
- name = decompiled; |
+ return formatType(_objectRawTypeName(object), getRuntimeTypeInfo(object)); |
+ } |
+ |
+ static String _objectRawTypeName(Object object) { |
+ var interceptor = getInterceptor(object); |
+ // The interceptor is either an object (self-intercepting plain Dart class), |
+ // the prototype of the constructor for an Interceptor class (like |
+ // `JSString.prototype`, `JSNull.prototype`), or an Interceptor object |
+ // instance (`const JSString()`, should use `JSString.prototype`). |
+ // |
+ // These all should have a `constructor` property with a `name` property. |
+ String name; |
+ var interceptorConstructor = JS('', '#.constructor', interceptor); |
+ if (JS('bool', 'typeof # == "function"', interceptorConstructor)) { |
+ var interceptorConstructorName = JS('', '#.name', interceptorConstructor); |
+ if (interceptorConstructorName is String) { |
+ name = interceptorConstructorName; |
+ } |
+ } |
+ |
+ if (name == null || |
+ identical(interceptor, |
+ JS_INTERCEPTOR_CONSTANT(UnknownJavaScriptObject)) || |
+ identical(interceptor, JS_INTERCEPTOR_CONSTANT(Interceptor))) { |
+ // Try to do better. If we do not find something better, leave the name |
+ // as 'UnknownJavaScriptObject' or 'Interceptor' (or the minified name). |
+ // |
+ // When we get here via the UnknownJavaScriptObject test (for JavaScript |
+ // objects from outside the program), the object's constructor has a |
+ // better name that 'UnknownJavaScriptObject'. |
+ // |
+ // When we get here the Interceptor test (for Native classes that are |
+ // declared in the Dart program but have been 'folded' into Interceptor), |
+ // the native class's constructor name is better than the generic |
+ // 'Interceptor' (an abstract class). |
+ |
+ // Try the [constructorNameFallback]. This gets the constructor name for |
+ // any browser (used by [getNativeInterceptor]). |
+ String dispatchName = constructorNameFallback(object); |
+ if (name == null) name = dispatchName; |
+ if (dispatchName == 'Object') { |
+ // Try to decompile the constructor by turning it into a string and get |
+ // the name out of that. If the decompiled name is a string containing |
+ // an identifier, we use that instead of the very generic 'Object'. |
+ var objectConstructor = JS('', '#.constructor', object); |
+ if (JS('bool', 'typeof # == "function"', objectConstructor)) { |
+ var decompiledName = |
+ JS('var', r'#.match(/^\s*function\s*([\w$]*)\s*\(/)[1]', |
+ JS('var', r'String(#)', objectConstructor)); |
+ if (decompiledName is String && |
+ JS('bool', r'/^\w+$/.test(#)', decompiledName)) { |
+ name = decompiledName; |
+ } |
+ } |
+ } else { |
+ name = dispatchName; |
+ } |
} |
+ |
// TODO(kasperl): If the namer gave us a fresh global name, we may |
// want to remove the numeric suffix that makes it unique too. |
if (name.length > 1 && identical(name.codeUnitAt(0), DOLLAR_CHAR_VALUE)) { |
name = name.substring(1); |
} |
- return formatType(name, getRuntimeTypeInfo(object)); |
+ return name; |
} |
/// In minified mode, uses the unminified names if available. |