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 |