Chromium Code Reviews| Index: reflectable/lib/src/transformer_implementation.dart |
| diff --git a/reflectable/lib/src/transformer_implementation.dart b/reflectable/lib/src/transformer_implementation.dart |
| index 90c86356a5d92110c6e132d08ac4201f80dd8810..d7709061f90ca8f8b9a6f98ec3f5362c2ccea25b 100644 |
| --- a/reflectable/lib/src/transformer_implementation.dart |
| +++ b/reflectable/lib/src/transformer_implementation.dart |
| @@ -50,39 +50,98 @@ class ReflectionWorld { |
| class ReflectorDomain { |
| final ClassElement reflector; |
| final List<ClassDomain> annotatedClasses; |
| + final Map<ClassElement, ClassDomain> classMap = |
| + new Map<ClassElement, ClassDomain>(); |
| final Capabilities capabilities; |
| /// Libraries that must be imported to `reflector.library`. |
| final Set<LibraryElement> missingImports = new Set<LibraryElement>(); |
| - ReflectorDomain(this.reflector, this.annotatedClasses, this.capabilities); |
| + ReflectorDomain(this.reflector, this.annotatedClasses, this.capabilities) { |
| + for (ClassDomain classDomain in annotatedClasses) { |
| + classMap[classDomain.classElement] = classDomain; |
| + } |
| + } |
| void computeNames(Namer namer) { |
| annotatedClasses |
| .forEach((ClassDomain classDomain) => classDomain.computeNames(namer)); |
| } |
| + |
| + Map<ClassElement, Map<String, ExecutableElement>> _instanceMemberCache = |
| + new Map<ClassElement, Map<String, ExecutableElement>>(); |
|
eernst
2015/06/18 12:21:12
Do we need an more global mechanism to guide the e
sigurdm
2015/06/18 14:22:27
I am not sure - will leave a todo.
|
| } |
| /// Information about reflectability for a given class. |
| class ClassDomain { |
| final ClassElement classElement; |
| - final Iterable<MethodElement> invokableMethods; |
| final Iterable<MethodElement> declaredMethods; |
| + final Iterable<PropertyAccessorElement> declaredAccessors; |
| + final Iterable<ConstructorElement> constructors; |
| ReflectorDomain reflectorDomain; |
| + |
| + Iterable<MethodElement> get invokableMethods => instanceMembers |
| + .where((ExecutableElement element) => element is MethodElement); |
| + |
| String staticClassMirrorName; |
| String staticInstanceMirrorName; |
| String get baseName => classElement.name; |
| - ClassDomain(this.classElement, this.invokableMethods, this.declaredMethods, |
| - this.reflectorDomain); |
| + ClassDomain(this.classElement, this.declaredMethods, |
| + this.declaredAccessors, this.constructors, this.reflectorDomain); |
| Iterable<ExecutableElement> get declarations { |
| - // TODO(sigurdm): Include constructors. |
| // TODO(sigurdm): Include fields. |
| - // TODO(sigurdm): Include getters and setters. |
| // TODO(sigurdm): Include type variables (if we decide to keep them). |
| - return [declaredMethods].expand((x) => x); |
| + return [declaredMethods, declaredAccessors, constructors].expand((x) => x); |
| + } |
| + |
| + /// Finds all instance members by going through the class hierarchy. |
| + Iterable<ExecutableElement> get instanceMembers { |
| + |
| + Map<String, ExecutableElement> helper(ClassElement classElement) { |
| + if (reflectorDomain._instanceMemberCache[classElement] != null) { |
| + return reflectorDomain._instanceMemberCache[classElement]; |
| + } |
| + Map<String, ExecutableElement> result = |
| + new Map<String, ExecutableElement>(); |
| + |
| + void addIfCapable(ExecutableElement member) { |
| + if (reflectorDomain.capabilities.supportsInstanceInvoke(member.name)) { |
| + result[member.name] = member; |
| + } |
| + } |
| + if (classElement.supertype != null) { |
| + helper(classElement.supertype.element).forEach( |
| + (String name, ExecutableElement member) { |
| + addIfCapable(member); |
| + }); |
| + } |
| + for (InterfaceType mixin in classElement.mixins) { |
| + helper(mixin.element).forEach((String name, ExecutableElement member) { |
| + addIfCapable(member); |
| + }); |
| + } |
| + for (MethodElement member in classElement.methods) { |
| + if (member.isAbstract || member.isStatic) continue; |
| + addIfCapable(member); |
| + } |
| + for (PropertyAccessorElement member in classElement.accessors) { |
| + if (member.isAbstract || member.isStatic) continue; |
| + addIfCapable(member); |
| + } |
| + for (FieldElement field in classElement.fields) { |
| + if (field.isStatic) continue; |
| + if (field.isSynthetic) continue; |
| + addIfCapable(field.getter); |
| + if (!field.isFinal) { |
| + addIfCapable(field.setter); |
| + } |
| + } |
| + return result; |
| + } |
| + return helper(classElement).values; |
| } |
| /// Returns an integer encoding the kind and attributes of the given |
| @@ -94,40 +153,64 @@ class ClassDomain { |
| } else if (element is ConstructorElement) { |
| if (element.isFactory) { |
| result = constants.factoryConstructor; |
| - } else if (element.redirectedConstructor != null) { |
| - result = constants.redirectingConstructor; |
| } else { |
| result = constants.generativeConstructor; |
| } |
| if (element.isConst) { |
| result += constants.constAttribute; |
| } |
| + if (element.redirectedConstructor != null) { |
| + result += constants.redirectingConstructor; |
| + } |
| } else { |
| result = constants.method; |
| } |
| if (element.isPrivate) { |
| result += constants.privateAttribute; |
| } |
| - if (element.isAbstract) { |
| - result += constants.abstractAttribute; |
| - } |
| + assert(!element.isAbstract); |
| if (element.isStatic) { |
| result += constants.staticAttribute; |
| } |
| + if (element.isSynthetic) { |
| + result += constants.syntheticAttribute; |
| + } |
| return result; |
| } |
| + String nameOfDeclaration(ExecutableElement element) { |
| + if (element is ConstructorElement) { |
| + return element.name == "" |
| + ? classElement.name |
| + : "${classElement.name}.${element.name}"; |
| + } |
| + return element.name; |
| + } |
| + |
| /// Returns a String with the textual representation of the declarations-map. |
| String get declarationsString { |
| Iterable<String> declarationParts = declarations.map( |
| - (ExecutableElement instanceMember) { |
| - return '"${instanceMember.name}": ' |
| - 'new MethodMirrorImpl("${instanceMember.name}", ' |
| - '${_declarationDescriptor(instanceMember)}, this)'; |
| + (ExecutableElement declaration) { |
| + return '"${nameOfDeclaration(declaration)}": ' |
| + 'new MethodMirrorImpl("${declaration.name}", ' |
| + '${_declarationDescriptor(declaration)}, this)'; |
| }); |
| return "{${declarationParts.join(", ")}}"; |
| } |
| + /// Returns a String with the textual representation of the |
| + /// instanceMembers-map. |
| + String get instanceMembersString { |
| + // TODO(sigurdm): Find out how to set the right owner. |
| + Iterable<String> instanceMemberParts = instanceMembers.map( |
| + (ExecutableElement declaration) { |
| + return '"${nameOfDeclaration(declaration)}": ' |
| + 'new MethodMirrorImpl("${declaration.name}", ' |
| + '${_declarationDescriptor(declaration)}, null)'; |
| + }); |
| + return "{${instanceMemberParts.join(", ")}}"; |
| + } |
| + |
| void computeNames(Namer namer) { |
| staticClassMirrorName = namer.freshName("Static_${baseName}_ClassMirror"); |
| staticInstanceMirrorName = |
| @@ -383,21 +466,12 @@ class TransformerImplementation { |
| return null; |
| } |
| - /// Finds all the methods in the class and all super-classes. |
| - Iterable<MethodElement> allMethods(ClassElement classElement) { |
| - List<MethodElement> result = new List<MethodElement>(); |
| - result.addAll(classElement.methods); |
| - classElement.allSupertypes.forEach((InterfaceType superType) { |
| - result.addAll(superType.methods); |
| - }); |
| - return result; |
| - } |
| - |
| Iterable<MethodElement> declaredMethods( |
| ClassElement classElement, Capabilities capabilities) { |
| return classElement.methods.where((MethodElement method) { |
| + if (method.isAbstract) return false; |
| if (method.isStatic) { |
| - // TODO(sigurdm): Ask capability about support. |
| + // TODO(sigurdm): Ask capabilities about support. |
| return true; |
| } else { |
| return capabilities.supportsInstanceInvoke(method.name); |
| @@ -405,16 +479,24 @@ class TransformerImplementation { |
| }); |
| } |
| - Iterable<MethodElement> invocableInstanceMethods( |
| + Iterable<PropertyAccessorElement> declaredAccessors( |
| + ClassElement classElement, Capabilities capabilities) { |
| + return classElement.accessors.where((PropertyAccessorElement accessor) { |
| + if (accessor.isAbstract) return false; |
| + if (accessor.isStatic) { |
| + // TODO(sigurdm): Ask capabilities about support. |
| + return true; |
| + } else { |
| + return capabilities.supportsInstanceInvoke(accessor.name); |
| + } |
| + }); |
| + } |
| + |
| + Iterable<ConstructorElement> declaredConstructors( |
| ClassElement classElement, Capabilities capabilities) { |
| - return allMethods(classElement).where((MethodElement method) { |
| - MethodDeclaration methodDeclaration = method.node; |
| - // TODO(eernst): We currently ignore method declarations when |
| - // they are operators. One issue is generation of code (which |
| - // does not work if we go ahead naively). |
| - if (methodDeclaration.isOperator) return false; |
| - String methodName = methodDeclaration.name.name; |
| - return capabilities.supportsInstanceInvoke(methodName); |
| + return classElement.constructors.where((ConstructorElement constructor) { |
| + // TODO(sigurdm): Ask capabilities about support. |
| + return true; |
| }); |
| } |
| @@ -467,12 +549,15 @@ class TransformerImplementation { |
| return new ReflectorDomain( |
| reflector, new List<ClassDomain>(), capabilities); |
| }); |
| - List<MethodElement> instanceMethods = |
| - invocableInstanceMethods(type, domain.capabilities).toList(); |
| List<MethodElement> declaredMethodsOfClass = |
| declaredMethods(type, domain.capabilities).toList(); |
| - domain.annotatedClasses.add(new ClassDomain( |
| - type, instanceMethods, declaredMethodsOfClass, domain)); |
| + List<PropertyAccessorElement> declaredAccessorsOfClass = |
| + declaredAccessors(type, domain.capabilities).toList(); |
| + List<ConstructorElement> declaredConstructorsOfClass = |
| + declaredConstructors(type, domain.capabilities).toList(); |
| + domain.annotatedClasses.add(new ClassDomain(type, |
| + declaredMethodsOfClass, declaredAccessorsOfClass, |
| + declaredConstructorsOfClass, domain)); |
| } |
| } |
| } |
| @@ -771,6 +856,7 @@ class TransformerImplementation { |
| /// is the class modeled by [classElement]. |
| String _staticClassMirrorCode(ClassDomain classDomain) { |
| String declarationsString = classDomain.declarationsString; |
| + String instanceMembersString = classDomain.instanceMembersString; |
| return """ |
| class ${classDomain.staticClassMirrorName} extends ClassMirrorUnimpl { |
| final String simpleName = "${classDomain.classElement.name}"; |
| @@ -783,6 +869,16 @@ class ${classDomain.staticClassMirrorName} extends ClassMirrorUnimpl { |
| } |
| return _declarationsCache; |
| } |
| + |
| + Map<String, MethodMirror> _instanceMembersCache; |
| + |
| + Map<String, MethodMirror> get instanceMembers { |
| + if (_instanceMembersCache == null) { |
| + _instanceMembersCache = new UnmodifiableMapView($instanceMembersString); |
| + } |
| + return _instanceMembersCache; |
| + } |
| + |
| } |
| """; |
| } |
| @@ -945,11 +1041,19 @@ class ${classDomain.staticClassMirrorName} extends ClassMirrorUnimpl { |
| /// the given [classElement], bounded by the permissions given |
| /// in [capabilities]. |
| String _staticInstanceMirrorInvokeCode(ClassDomain classDomain) { |
| + |
| + String tearOff(MethodElement methodElement) { |
| + if (!methodElement.isOperator) return "reflectee.${methodElement.name}"; |
| + if (methodElement.name == "[]=") return "(x, v) => reflectee[x] = v"; |
| + if (methodElement.name == "[]") return "(x) => reflectee[x]"; |
| + return "(x) => reflectee ${methodElement.name} x"; |
| + } |
| + |
| + |
| List<String> methodCases = new List<String>(); |
| for (MethodElement methodElement in classDomain.invokableMethods) { |
| - String methodName = methodElement.name; |
| - methodCases.add("if (memberName == '$methodName') {" |
| - "method = reflectee.$methodName; }"); |
| + methodCases.add("if (memberName == '${methodElement.name}') {" |
| + "method = ${tearOff(methodElement)}; }"); |
| } |
| // TODO(eernst, sigurdm): Create an instance of [Invocation] in user code. |
| methodCases.add("if (instanceMethodFilter.hasMatch(memberName)) {" |