Index: sdk/lib/_internal/lib/js_mirrors.dart |
diff --git a/sdk/lib/_internal/lib/js_mirrors.dart b/sdk/lib/_internal/lib/js_mirrors.dart |
index ceb177f872fe6584ba45af99180f097958e13d18..1036bfdb25540a67d5e265feb3ca4b8c3aefdfed 100644 |
--- a/sdk/lib/_internal/lib/js_mirrors.dart |
+++ b/sdk/lib/_internal/lib/js_mirrors.dart |
@@ -341,7 +341,13 @@ class JsLibraryMirror extends JsDeclarationMirror with JsObjectMirror |
// TODO(ahe): What receiver to use? |
throw new NoSuchMethodError(this, fieldName, [], null); |
} |
- return reflect(mirror._getField(this)); |
+ if (mirror is! MethodMirror) return reflect(mirror._getField(this)); |
+ JsMethodMirror methodMirror = mirror; |
+ if (methodMirror.isGetter) return reflect(mirror._getField(this)); |
+ assert(methodMirror.isRegularMethod); |
+ var getter = JS("", "#['\$getter']", methodMirror._jsFunction); |
+ if (getter == null) throw new UnimplementedError(); |
+ return reflect(JS("", "#()", getter)); |
} |
InstanceMirror invoke(Symbol memberName, |
@@ -351,15 +357,20 @@ class JsLibraryMirror extends JsDeclarationMirror with JsObjectMirror |
throw new UnsupportedError('Named arguments are not implemented.'); |
} |
JsDeclarationMirror mirror = __members[memberName]; |
- if (mirror == null) { |
+ |
+ if (mirror is JsMethodMirror && !mirror.canInvokeReflectively()) { |
+ throwInvalidReflectionError(n(memberName)); |
+ } |
+ if (mirror == null || mirror is JsMethodMirror && mirror.isSetter) { |
// TODO(ahe): What receiver to use? |
throw new NoSuchMethodError( |
this, memberName, positionalArguments, namedArguments); |
} |
- if (mirror is JsMethodMirror && !mirror.canInvokeReflectively()) { |
- throwInvalidReflectionError(n(memberName)); |
+ if (mirror is JsMethodMirror && !mirror.isGetter) { |
+ return reflect(mirror._invoke(positionalArguments, namedArguments)); |
} |
- return reflect(mirror._invoke(positionalArguments, namedArguments)); |
+ return getField(memberName) |
+ .invoke(#call, positionalArguments, namedArguments); |
} |
_loadField(String name) { |
@@ -387,7 +398,7 @@ class JsLibraryMirror extends JsDeclarationMirror with JsObjectMirror |
var jsFunction = JS('', '#[#]', _globalObject, name); |
String unmangledName = mangledGlobalNames[name]; |
if (unmangledName == null || |
- JS('bool', "!!#['getterStub']", jsFunction)) { |
+ JS('bool', "!!#['\$getterStub']", jsFunction)) { |
// If there is no unmangledName, [jsFunction] is either a synthetic |
// implementation detail, or something that is excluded |
// by @MirrorsUsed. |
@@ -816,61 +827,61 @@ class JsInstanceMirror extends JsObjectMirror implements InstanceMirror { |
InstanceMirror invoke(Symbol memberName, |
List positionalArguments, |
[Map<Symbol,dynamic> namedArguments]) { |
- String name = n(memberName); |
- String reflectiveName; |
- if (namedArguments != null && !namedArguments.isEmpty) { |
- var interceptor = getInterceptor(reflectee); |
- |
- var jsFunction = JS('', '#[# + "*"]', interceptor, name); |
- if (jsFunction == null) { |
- // TODO(ahe): Invoke noSuchMethod. |
- throw new UnimplementedNoSuchMethodError( |
- 'Invoking noSuchMethod with named arguments not implemented'); |
- } |
- ReflectionInfo info = new ReflectionInfo(jsFunction); |
- if (jsFunction == null) { |
- // TODO(ahe): Invoke noSuchMethod. |
- throw new UnimplementedNoSuchMethodError( |
- 'Invoking noSuchMethod with named arguments not implemented'); |
- } |
- |
- positionalArguments = new List.from(positionalArguments); |
- // Check the number of positional arguments is valid. |
- if (info.requiredParameterCount != positionalArguments.length) { |
+ if (namedArguments == null) namedArguments = const {}; |
+ // We can safely pass positionalArguments to _invoke as it will wrap it in |
+ // a JSArray if needed. |
+ return _invoke(memberName, JSInvocationMirror.METHOD, |
+ positionalArguments, namedArguments); |
+ } |
+ |
+ InstanceMirror _invokeMethodWithNamedArguments( |
+ String reflectiveName, |
+ List positionalArguments, Map<Symbol,dynamic> namedArguments) { |
+ assert(namedArguments.isNotEmpty); |
+ var interceptor = getInterceptor(reflectee); |
+ |
+ var jsFunction = JS('', '#[#]', interceptor, reflectiveName); |
+ if (jsFunction == null) { |
+ // TODO(ahe): Invoke noSuchMethod. |
+ throw new UnimplementedNoSuchMethodError( |
+ 'Invoking noSuchMethod with named arguments not implemented'); |
+ } |
+ ReflectionInfo info = new ReflectionInfo(jsFunction); |
+ if (jsFunction == null) { |
+ // TODO(ahe): Invoke noSuchMethod. |
+ throw new UnimplementedNoSuchMethodError( |
+ 'Invoking noSuchMethod with named arguments not implemented'); |
+ } |
+ |
+ positionalArguments = new List.from(positionalArguments); |
+ // Check the number of positional arguments is valid. |
+ if (info.requiredParameterCount != positionalArguments.length) { |
+ // TODO(ahe): Invoke noSuchMethod. |
+ throw new UnimplementedNoSuchMethodError( |
+ 'Invoking noSuchMethod with named arguments not implemented'); |
+ } |
+ var defaultArguments = new Map(); |
+ for (int i = 0; i < info.optionalParameterCount; i++) { |
+ var parameterName = info.parameterName(i + info.requiredParameterCount); |
+ var defaultValue = |
+ getMetadata(info.defaultValue(i + info.requiredParameterCount)); |
+ defaultArguments[parameterName] = defaultValue; |
+ } |
+ namedArguments.forEach((Symbol symbol, value) { |
+ String parameter = n(symbol); |
+ if (defaultArguments.containsKey(parameter)) { |
+ defaultArguments[parameter] = value; |
+ } else { |
+ // Extraneous named argument. |
// TODO(ahe): Invoke noSuchMethod. |
throw new UnimplementedNoSuchMethodError( |
'Invoking noSuchMethod with named arguments not implemented'); |
} |
- var defaultArguments = new Map(); |
- for (int i = 0; i < info.optionalParameterCount; i++) { |
- var parameterName = info.parameterName(i + info.requiredParameterCount); |
- var defaultValue = |
- getMetadata(info.defaultValue(i + info.requiredParameterCount)); |
- defaultArguments[parameterName] = defaultValue; |
- } |
- namedArguments.forEach((Symbol symbol, value) { |
- String parameter = n(symbol); |
- if (defaultArguments.containsKey(parameter)) { |
- defaultArguments[parameter] = value; |
- } else { |
- // Extraneous named argument. |
- // TODO(ahe): Invoke noSuchMethod. |
- throw new UnimplementedNoSuchMethodError( |
- 'Invoking noSuchMethod with named arguments not implemented'); |
- } |
- }); |
- positionalArguments.addAll(defaultArguments.values); |
- // TODO(ahe): Handle intercepted methods. |
- return reflect( |
- JS('', '#.apply(#, #)', jsFunction, reflectee, positionalArguments)); |
- } else { |
- reflectiveName = |
- JS('String', '# + ":" + # + ":0"', name, positionalArguments.length); |
- } |
- // We can safely pass positionalArguments to _invoke as it will wrap it in |
- // a JSArray if needed. |
- return _invoke(memberName, JSInvocationMirror.METHOD, reflectiveName, |
- positionalArguments); |
+ }); |
+ positionalArguments.addAll(defaultArguments.values); |
+ // TODO(ahe): Handle intercepted methods. |
+ return reflect( |
+ JS('', '#.apply(#, #)', jsFunction, reflectee, positionalArguments)); |
} |
/// Grabs hold of the class-specific invocation cache for the reflectee. |
@@ -888,55 +899,94 @@ class JsInstanceMirror extends JsObjectMirror implements InstanceMirror { |
return cache; |
} |
- /// Invoke the member specified through name and type on the reflectee. |
- /// As a side-effect, this populates the class-specific invocation cache |
- /// for the reflectee. |
- InstanceMirror _invoke(Symbol name, |
- int type, |
- String reflectiveName, |
- List arguments) { |
+ String _computeReflectiveName(Symbol symbolName, int type, |
+ List positionalArguments, |
+ Map<Symbol, dynamic> namedArguments) { |
+ String name = n(symbolName); |
+ switch (type) { |
+ case JSInvocationMirror.GETTER: return name; |
+ case JSInvocationMirror.SETTER: return '$name='; |
+ case JSInvocationMirror.METHOD: |
+ if (namedArguments.isNotEmpty) return '$name*'; |
+ int nbArgs = positionalArguments.length as int; |
+ return "$name:$nbArgs:0"; |
+ } |
+ throw new RuntimeError("Could not compute reflective name for $name"); |
+ } |
+ |
+ /** |
+ * Returns a `CachedInvocation` or `CachedNoSuchMethodInvocation` for the |
+ * given member. |
+ * |
+ * Caches the result. |
+ */ |
+ _getCachedInvocation(Symbol name, int type, String reflectiveName, |
+ List positionalArguments, Map<Symbol,dynamic> namedArguments) { |
+ |
var cache = _classInvocationCache; |
var cacheEntry = JsCache.fetch(cache, reflectiveName); |
var result; |
- Invocation invocation; |
if (cacheEntry == null) { |
disableTreeShaking(); |
String mangledName = reflectiveNames[reflectiveName]; |
List<String> argumentNames = const []; |
- if (type == JSInvocationMirror.METHOD) { |
- // Note: [argumentNames] are not what the user actually provided, it is |
- // always all the named parameters. |
- argumentNames = reflectiveName.split(':').skip(3).toList(); |
- } |
// TODO(ahe): We don't need to create an invocation mirror here. The |
// logic from JSInvocationMirror.getCachedInvocation could easily be |
// inlined here. |
- invocation = createUnmangledInvocationMirror( |
- name, mangledName, type, arguments, argumentNames); |
+ Invocation invocation = createUnmangledInvocationMirror( |
+ name, mangledName, type, positionalArguments, argumentNames); |
cacheEntry = |
JSInvocationMirror.getCachedInvocation(invocation, reflectee); |
JsCache.update(cache, reflectiveName, cacheEntry); |
} |
+ return cacheEntry; |
+ } |
+ |
+ /// Invoke the member specified through name and type on the reflectee. |
+ /// As a side-effect, this populates the class-specific invocation cache |
+ /// for the reflectee. |
+ InstanceMirror _invoke(Symbol name, |
+ int type, |
+ List positionalArguments, |
+ Map<Symbol,dynamic> namedArguments) { |
+ String reflectiveName = |
+ _computeReflectiveName(name, type, positionalArguments, namedArguments); |
+ |
+ if (namedArguments.isNotEmpty) { |
+ // TODO(floitsch): first, make sure it's not a getter. |
+ return _invokeMethodWithNamedArguments( |
+ reflectiveName, positionalArguments, namedArguments); |
+ } |
+ var cacheEntry = _getCachedInvocation( |
+ name, type, reflectiveName, positionalArguments, namedArguments); |
+ |
if (cacheEntry.isNoSuchMethod) { |
- if (invocation == null) { |
- String mangledName = reflectiveNames[reflectiveName]; |
- // TODO(ahe): Get the argument names. |
- List<String> argumentNames = []; |
- invocation = createUnmangledInvocationMirror( |
- name, mangledName, type, arguments, argumentNames); |
+ // Could be that we want to invoke a getter, or get a method. |
+ if (type == JSInvocationMirror.METHOD && _instanceFieldExists(name)) { |
+ return getField(name).invoke( |
+ #call, positionalArguments, namedArguments); |
} |
+ |
+ if (type == JSInvocationMirror.SETTER) { |
+ // For setters we report the setter name "field=". |
+ name = s("${n(name)}="); |
+ } |
+ |
+ String mangledName = reflectiveNames[reflectiveName]; |
+ // TODO(ahe): Get the argument names. |
+ List<String> argumentNames = []; |
+ Invocation invocation = createUnmangledInvocationMirror( |
+ name, mangledName, type, positionalArguments, argumentNames); |
return reflect(cacheEntry.invokeOn(reflectee, invocation)); |
} else { |
- return reflect(cacheEntry.invokeOn(reflectee, arguments)); |
+ return reflect(cacheEntry.invokeOn(reflectee, positionalArguments)); |
} |
} |
InstanceMirror setField(Symbol fieldName, Object arg) { |
- String reflectiveName = '${n(fieldName)}='; |
- _invoke( |
- s(reflectiveName), JSInvocationMirror.SETTER, reflectiveName, [arg]); |
+ _invoke(fieldName, JSInvocationMirror.SETTER, [arg], const {}); |
return reflect(arg); |
} |
@@ -964,6 +1014,15 @@ class JsInstanceMirror extends JsObjectMirror implements InstanceMirror { |
/// engine to speed up cache probing. |
var _getterCache = 4; |
+ bool _instanceFieldExists(Symbol name) { |
+ int getterType = JSInvocationMirror.GETTER; |
+ String getterName = |
+ _computeReflectiveName(name, getterType, const [], const {}); |
+ var getterCacheEntry = _getCachedInvocation( |
+ name, getterType, getterName, const [], const {}); |
+ return !getterCacheEntry.isNoSuchMethod && !getterCacheEntry.isGetterStub; |
+ } |
+ |
InstanceMirror getField(Symbol fieldName) { |
// BUG(16400): This should be a labelled block, but that makes |
// dart2js crash when merging locals information in the type |
@@ -996,8 +1055,9 @@ class JsInstanceMirror extends JsObjectMirror implements InstanceMirror { |
InstanceMirror _getFieldSlow(Symbol fieldName) { |
// First do the slow-case getter invocation. As a side-effect of this, |
// the invocation cache is filled in so we can query it afterwards. |
+ var result = |
+ _invoke(fieldName, JSInvocationMirror.GETTER, const [], const {}); |
String name = n(fieldName); |
- var result = _invoke(fieldName, JSInvocationMirror.GETTER, name, const []); |
var cacheEntry = JsCache.fetch(_classInvocationCache, name); |
if (cacheEntry.isNoSuchMethod) { |
return result; |
@@ -1723,6 +1783,13 @@ class JsClassMirror extends JsTypeMirror with JsObjectMirror |
throw new NoSuchMethodError(this, setterSymbol(fieldName), [arg], null); |
} |
+ bool _staticFieldExists(Symbol fieldName) { |
+ JsVariableMirror mirror = __variables[fieldName]; |
+ if (mirror != null) return mirror.isStatic; |
+ JsMethodMirror getter = __getters[fieldName]; |
+ return getter != null && getter.isStatic; |
+ } |
+ |
InstanceMirror getField(Symbol fieldName) { |
JsVariableMirror mirror = __variables[fieldName]; |
if (mirror != null && mirror.isStatic) { |
@@ -1739,6 +1806,21 @@ class JsClassMirror extends JsTypeMirror with JsObjectMirror |
return reflect(JS('', '#[#]', JS_CURRENT_ISOLATE(), jsName)); |
} |
} |
+ JsMethodMirror getter = __getters[fieldName]; |
+ if (getter != null && getter.isStatic) { |
+ return reflect(getter._invoke(const [], const {})); |
+ } |
+ // If the fieldName designates a static function we have to return |
+ // its closure. |
+ JsMethodMirror method = __methods[fieldName]; |
+ if (method != null && method.isStatic) { |
+ // We invoke the same getter that Dart code would execute. During |
+ // initialization we have stored that getter on the function (so that |
+ // we can find it more easily here). |
+ var getter = JS("", "#['\$getter']", method._jsFunction); |
+ if (getter == null) throw new UnimplementedError(); |
+ return reflect(JS("", "#()", getter)); |
+ } |
// TODO(ahe): What receiver to use? |
throw new NoSuchMethodError(this, fieldName, null, null); |
} |
@@ -1839,6 +1921,11 @@ class JsClassMirror extends JsTypeMirror with JsObjectMirror |
throw new UnsupportedError('Named arguments are not implemented.'); |
} |
JsMethodMirror mirror = __methods[memberName]; |
+ |
+ if (mirror == null && _staticFieldExists(memberName)) { |
+ return getField(memberName) |
+ .invoke(#call, positionalArguments, namedArguments); |
+ } |
if (mirror == null || !mirror.isStatic) { |
// TODO(ahe): What receiver to use? |
throw new NoSuchMethodError( |