| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | |
| 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. | |
| 4 | |
| 5 part of js_backend; | |
| 6 | |
| 7 /** | |
| 8 * Support for Custom Elements. | |
| 9 * | |
| 10 * The support for custom elements the compiler builds a table that maps the | |
| 11 * custom element class's [Type] to the interceptor for the class and the | |
| 12 * constructor(s) for the class. | |
| 13 * | |
| 14 * We want the table to contain only the custom element classes used, and we | |
| 15 * want to avoid resolving and compiling constructors that are not used since | |
| 16 * that may bring in unused code. This class controls the resolution and code | |
| 17 * generation to restrict the impact. | |
| 18 * | |
| 19 * The following line of code requires the generation of the generative | |
| 20 * constructor factory function(s) for FancyButton, and their insertion into the | |
| 21 * table: | |
| 22 * | |
| 23 * document.register(FancyButton, 'x-fancy-button'); | |
| 24 * | |
| 25 * We detect this by 'joining' the classes that are referenced as type literals | |
| 26 * with the classes that are custom elements, enabled by detecting the presence | |
| 27 * of the table access code used by document.register. | |
| 28 * | |
| 29 * We have to be more conservative when the type is unknown, e.g. | |
| 30 * | |
| 31 * document.register(classMirror.reflectedType, tagFromMetadata); | |
| 32 * | |
| 33 * and | |
| 34 * | |
| 35 * class Component<T> { | |
| 36 * final tag; | |
| 37 * Component(this.tag); | |
| 38 * void register() => document.register(T, tag); | |
| 39 * } | |
| 40 * const Component<FancyButton>('x-fancy-button').register(); | |
| 41 * | |
| 42 * In these cases we conservatively generate all viable entries in the table. | |
| 43 */ | |
| 44 class CustomElementsAnalysis { | |
| 45 final JavaScriptBackend backend; | |
| 46 final Compiler compiler; | |
| 47 final CustomElementsAnalysisJoin resolutionJoin; | |
| 48 final CustomElementsAnalysisJoin codegenJoin; | |
| 49 bool fetchedTableAccessorMethod = false; | |
| 50 Element tableAccessorMethod; | |
| 51 | |
| 52 CustomElementsAnalysis(JavaScriptBackend backend) | |
| 53 : this.backend = backend, | |
| 54 this.compiler = backend.compiler, | |
| 55 resolutionJoin = new CustomElementsAnalysisJoin(backend), | |
| 56 codegenJoin = new CustomElementsAnalysisJoin(backend) { | |
| 57 // TODO(sra): Remove this work-around. We should mark allClassesSelected in | |
| 58 // both joins only when we see a construct generating an unknown [Type] but | |
| 59 // we can't currently recognize all cases. In particular, the work-around | |
| 60 // for the unimplemented `ClassMirror.reflectedType` is not recognizable. | |
| 61 // TODO(12607): Match on [ClassMirror.reflectedType] | |
| 62 resolutionJoin.allClassesSelected = true; | |
| 63 codegenJoin.allClassesSelected = true; | |
| 64 } | |
| 65 | |
| 66 CustomElementsAnalysisJoin joinFor(Enqueuer enqueuer) => | |
| 67 enqueuer.isResolutionQueue ? resolutionJoin : codegenJoin; | |
| 68 | |
| 69 void registerInstantiatedClass(ClassElement classElement, Enqueuer enqueuer) { | |
| 70 classElement.ensureResolved(compiler); | |
| 71 if (!Elements.isNativeOrExtendsNative(classElement)) return; | |
| 72 if (classElement.isMixinApplication) return; | |
| 73 if (classElement.isAbstract) return; | |
| 74 joinFor(enqueuer).instantiatedClasses.add(classElement); | |
| 75 } | |
| 76 | |
| 77 void registerTypeLiteral(DartType type, Registry registry) { | |
| 78 assert(registry.isForResolution); | |
| 79 // In codegen we see the TypeConstants instead. | |
| 80 if (!registry.isForResolution) return; | |
| 81 | |
| 82 if (type.isInterfaceType) { | |
| 83 // TODO(sra): If we had a flow query from the type literal expression to | |
| 84 // the Type argument of the metadata lookup, we could tell if this type | |
| 85 // literal is really a demand for the metadata. | |
| 86 resolutionJoin.selectedClasses.add(type.element); | |
| 87 } else if (type.isTypeVariable) { | |
| 88 // This is a type parameter of a parameterized class. | |
| 89 // TODO(sra): Is there a way to determine which types are bound to the | |
| 90 // parameter? | |
| 91 resolutionJoin.allClassesSelected = true; | |
| 92 } | |
| 93 } | |
| 94 | |
| 95 void registerTypeConstant(Element element, Enqueuer enqueuer) { | |
| 96 assert(element.isClass); | |
| 97 assert(!enqueuer.isResolutionQueue); | |
| 98 codegenJoin.selectedClasses.add(element); | |
| 99 } | |
| 100 | |
| 101 void registerStaticUse(Element element, Enqueuer enqueuer) { | |
| 102 assert(element != null); | |
| 103 if (!fetchedTableAccessorMethod) { | |
| 104 fetchedTableAccessorMethod = true; | |
| 105 tableAccessorMethod = backend.findInterceptor( | |
| 106 'findIndexForNativeSubclassType'); | |
| 107 } | |
| 108 if (element == tableAccessorMethod) { | |
| 109 joinFor(enqueuer).demanded = true; | |
| 110 } | |
| 111 } | |
| 112 | |
| 113 void onQueueEmpty(Enqueuer enqueuer) { | |
| 114 joinFor(enqueuer).flush(enqueuer); | |
| 115 } | |
| 116 | |
| 117 bool get needsTable => codegenJoin.demanded; | |
| 118 | |
| 119 bool needsClass(ClassElement classElement) => | |
| 120 codegenJoin.activeClasses.contains(classElement); | |
| 121 | |
| 122 List<Element> constructors(ClassElement classElement) => | |
| 123 codegenJoin.computeEscapingConstructors(classElement); | |
| 124 } | |
| 125 | |
| 126 | |
| 127 class CustomElementsAnalysisJoin { | |
| 128 final JavaScriptBackend backend; | |
| 129 Compiler get compiler => backend.compiler; | |
| 130 | |
| 131 // Classes that are candidates for needing constructors. Classes are moved to | |
| 132 // [activeClasses] when we know they need constructors. | |
| 133 final instantiatedClasses = new Set<ClassElement>(); | |
| 134 | |
| 135 // Classes explicitly named. | |
| 136 final selectedClasses = new Set<ClassElement>(); | |
| 137 | |
| 138 // True if we must conservatively include all extension classes. | |
| 139 bool allClassesSelected = false; | |
| 140 | |
| 141 // Did we see a demand for the data? | |
| 142 bool demanded = false; | |
| 143 | |
| 144 // ClassesOutput: classes requiring metadata. | |
| 145 final activeClasses = new Set<ClassElement>(); | |
| 146 | |
| 147 CustomElementsAnalysisJoin(this.backend); | |
| 148 | |
| 149 void flush(Enqueuer enqueuer) { | |
| 150 if (!demanded) return; | |
| 151 var newActiveClasses = new Set<ClassElement>(); | |
| 152 for (ClassElement classElement in instantiatedClasses) { | |
| 153 bool isNative = classElement.isNative; | |
| 154 bool isExtension = | |
| 155 !isNative && Elements.isNativeOrExtendsNative(classElement); | |
| 156 // Generate table entries for native classes that are explicitly named and | |
| 157 // extensions that fix our criteria. | |
| 158 if ((isNative && selectedClasses.contains(classElement)) || | |
| 159 (isExtension && | |
| 160 (allClassesSelected || selectedClasses.contains(classElement)))) { | |
| 161 newActiveClasses.add(classElement); | |
| 162 Iterable<Element> escapingConstructors = | |
| 163 computeEscapingConstructors(classElement); | |
| 164 escapingConstructors.forEach(enqueuer.registerStaticUse); | |
| 165 escapingConstructors | |
| 166 .forEach(compiler.globalDependencies.registerDependency); | |
| 167 // Force the generaton of the type constant that is the key to an entry | |
| 168 // in the generated table. | |
| 169 ConstantValue constant = makeTypeConstant(classElement); | |
| 170 backend.registerCompileTimeConstant( | |
| 171 constant, compiler.globalDependencies); | |
| 172 backend.constants.addCompileTimeConstantForEmission(constant); | |
| 173 } | |
| 174 } | |
| 175 activeClasses.addAll(newActiveClasses); | |
| 176 instantiatedClasses.removeAll(newActiveClasses); | |
| 177 } | |
| 178 | |
| 179 TypeConstantValue makeTypeConstant(ClassElement element) { | |
| 180 DartType elementType = element.rawType; | |
| 181 DartType constantType = backend.typeImplementation.rawType; | |
| 182 return new TypeConstantValue(elementType, constantType); | |
| 183 } | |
| 184 | |
| 185 List<Element> computeEscapingConstructors(ClassElement classElement) { | |
| 186 List<Element> result = <Element>[]; | |
| 187 // Only classes that extend native classes have constructors in the table. | |
| 188 // We could refine this to classes that extend Element, but that would break | |
| 189 // the tests and there is no sane reason to subclass other native classes. | |
| 190 if (classElement.isNative) return result; | |
| 191 | |
| 192 selectGenerativeConstructors(ClassElement enclosing, Element member) { | |
| 193 if (member.isGenerativeConstructor) { | |
| 194 // Ignore constructors that cannot be called with zero arguments. | |
| 195 FunctionElement constructor = member; | |
| 196 FunctionSignature parameters = constructor.functionSignature; | |
| 197 if (parameters.requiredParameterCount == 0) { | |
| 198 result.add(member); | |
| 199 } | |
| 200 } | |
| 201 } | |
| 202 classElement.forEachMember(selectGenerativeConstructors, | |
| 203 includeBackendMembers: false, | |
| 204 includeSuperAndInjectedMembers: false); | |
| 205 return result; | |
| 206 } | |
| 207 } | |
| OLD | NEW |