OLD | NEW |
---|---|
1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 import 'dart:collection' show HashMap, HashSet, Queue; | 5 import 'dart:collection' show HashMap, HashSet, Queue; |
6 | 6 |
7 import 'package:analyzer/dart/ast/ast.dart' show Identifier; | |
8 import 'package:analyzer/dart/element/element.dart'; | 7 import 'package:analyzer/dart/element/element.dart'; |
8 import 'package:analyzer/dart/element/type.dart' show InterfaceType; | |
9 import 'package:analyzer/src/dart/element/element.dart' show FieldElementImpl; | 9 import 'package:analyzer/src/dart/element/element.dart' show FieldElementImpl; |
10 | 10 |
11 import '../js_ast/js_ast.dart' as JS; | 11 import '../js_ast/js_ast.dart' as JS; |
12 import 'element_helpers.dart'; | 12 import 'element_helpers.dart'; |
13 import 'extension_types.dart'; | |
13 import 'js_names.dart' as JS; | 14 import 'js_names.dart' as JS; |
14 | 15 |
15 /// Dart allows all fields to be overridden. | 16 /// Dart allows all fields to be overridden. |
16 /// | 17 /// |
17 /// To prevent a performance/code size penalty for allowing this, we analyze | 18 /// To prevent a performance/code size penalty for allowing this, we analyze |
18 /// private classes within each library that is being compiled to determine | 19 /// private classes within each library that is being compiled to determine |
19 /// if those fields should be virtual or not. In effect, we devirtualize fields | 20 /// if those fields should be virtual or not. In effect, we devirtualize fields |
20 /// when possible by analyzing the class hierarchy and using knowledge of | 21 /// when possible by analyzing the class hierarchy and using knowledge of |
21 /// which members are private and thus, could not be overridden outside of the | 22 /// which members are private and thus, could not be overridden outside of the |
22 /// current library. | 23 /// current library. |
(...skipping 154 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
177 /// The set of inherited getters, used because JS getters/setters are paired, | 178 /// The set of inherited getters, used because JS getters/setters are paired, |
178 /// so if we're generating a setter we may need to emit a getter that calls | 179 /// so if we're generating a setter we may need to emit a getter that calls |
179 /// super. | 180 /// super. |
180 final inheritedGetters = new HashSet<String>(); | 181 final inheritedGetters = new HashSet<String>(); |
181 | 182 |
182 /// The set of inherited setters, used because JS getters/setters are paired, | 183 /// The set of inherited setters, used because JS getters/setters are paired, |
183 /// so if we're generating a getter we may need to emit a setter that calls | 184 /// so if we're generating a getter we may need to emit a setter that calls |
184 /// super. | 185 /// super. |
185 final inheritedSetters = new HashSet<String>(); | 186 final inheritedSetters = new HashSet<String>(); |
186 | 187 |
187 ClassPropertyModel.build(VirtualFieldModel fieldModel, ClassElement classElem, | 188 final mockMembers = <String, ExecutableElement>{}; |
188 Iterable<ExecutableElement> extensionMembers) { | 189 |
190 final extensionMembers = new Set<ExecutableElement>(); | |
191 final mixinExtensionMembers = new Set<ExecutableElement>(); | |
192 | |
193 ClassPropertyModel.build(ExtensionTypeSet extensionTypes, | |
194 VirtualFieldModel fieldModel, ClassElement classElem) { | |
189 // Visit superclasses to collect information about their fields/accessors. | 195 // Visit superclasses to collect information about their fields/accessors. |
190 // This is expensive so we try to collect everything in one pass. | 196 // This is expensive so we try to collect everything in one pass. |
191 for (var base in getSuperclasses(classElem)) { | 197 for (var base in getSuperclasses(classElem)) { |
192 for (var accessor in base.accessors) { | 198 for (var accessor in base.accessors) { |
193 // For getter/setter pairs only process them once. | 199 // For getter/setter pairs only process them once. |
194 if (accessor.correspondingGetter != null) continue; | 200 if (accessor.correspondingGetter != null) continue; |
195 | 201 |
196 var field = accessor.variable; | 202 var field = accessor.variable; |
197 var name = field.name; | |
198 // Ignore private names from other libraries. | 203 // Ignore private names from other libraries. |
199 if (Identifier.isPrivateName(name) && | 204 if (field.isPrivate && accessor.library != classElem.library) { |
200 accessor.library != classElem.library) { | |
201 continue; | 205 continue; |
202 } | 206 } |
203 | 207 |
204 if (field.getter?.isAbstract == false) inheritedGetters.add(name); | 208 if (field.getter?.isAbstract == false) inheritedGetters.add(field.name); |
205 if (field.setter?.isAbstract == false) inheritedSetters.add(name); | 209 if (field.setter?.isAbstract == false) inheritedSetters.add(field.name); |
206 } | 210 } |
207 } | 211 } |
208 | 212 |
209 var extensionNames = | 213 _collectMockMembers(classElem.type); |
210 new HashSet<String>.from(extensionMembers.map((e) => e.name)); | 214 _collectExtensionMembers(extensionTypes, classElem); |
215 | |
216 var virtualAccessorNames = new HashSet<String>() | |
217 ..addAll(inheritedGetters) | |
218 ..addAll(inheritedSetters) | |
219 ..addAll(extensionMembers | |
220 .map((m) => m is PropertyAccessorElement ? m.variable.name : m.name)) | |
221 ..addAll(mockMembers.values | |
222 .map((m) => m is PropertyAccessorElement ? m.variable.name : m.name)); | |
211 | 223 |
212 // Visit accessors in the current class, and see if they need to be | 224 // Visit accessors in the current class, and see if they need to be |
213 // generated differently based on the inherited fields/accessors. | 225 // generated differently based on the inherited fields/accessors. |
214 for (var accessor in classElem.accessors) { | 226 for (var accessor in classElem.accessors) { |
215 // For getter/setter pairs only process them once. | 227 // For getter/setter pairs only process them once. |
216 if (accessor.correspondingGetter != null) continue; | 228 if (accessor.correspondingGetter != null) continue; |
217 // Also ignore abstract fields. | 229 // Also ignore abstract fields. |
218 if (accessor.isAbstract) continue; | 230 if (accessor.isAbstract) continue; |
219 | 231 |
220 var field = accessor.variable; | 232 var field = accessor.variable; |
221 var name = field.name; | 233 var name = field.name; |
222 // Is it a field? | 234 // Is it a field? |
223 if (!field.isSynthetic && field is FieldElementImpl) { | 235 if (!field.isSynthetic && field is FieldElementImpl) { |
224 if (inheritedGetters.contains(name) || | 236 if (virtualAccessorNames.contains(name) || |
225 inheritedSetters.contains(name) || | |
226 extensionNames.contains(name) || | |
227 fieldModel.isVirtual(field)) { | 237 fieldModel.isVirtual(field)) { |
228 if (field.isStatic) { | 238 if (field.isStatic) { |
229 staticFieldOverrides.add(field); | 239 staticFieldOverrides.add(field); |
230 } else { | 240 } else { |
231 virtualFields[field] = new JS.TemporaryId(name); | 241 virtualFields[field] = new JS.TemporaryId(name); |
232 } | 242 } |
233 } | 243 } |
234 } | 244 } |
235 } | 245 } |
236 } | 246 } |
247 | |
248 void _collectMockMembers(InterfaceType type) { | |
249 // TODO(jmesserly): every type with nSM will generate new stubs for all | |
250 // abstract members. For example: | |
251 // | |
252 // class C { m(); noSuchMethod(...) { ... } } | |
253 // class D extends C { m(); noSuchMethod(...) { ... } } | |
254 // | |
255 // We'll generate D.m even though it is not necessary. | |
256 // | |
257 // Doing better is a bit tricky, as our current codegen strategy for the | |
258 // mock methods encodes information about the number of arguments (and type | |
259 // arguments) that D expects. | |
260 var element = type.element; | |
261 if (!hasNoSuchMethod(element)) return; | |
262 | |
263 // Collect all unimplemented members. | |
264 // | |
265 // Initially, we track abstract and concrete members separately, then | |
266 // remove concrete from the abstract set. This is done because abstract | |
267 // members are allowed to "override" concrete ones in Dart. | |
vsm
2017/04/07 20:32:02
Unrelated to this CL since the code just moved, bu
Jennifer Messerly
2017/04/07 20:50:22
Hmmm quite possibly! I'll see if I can reproduce t
| |
268 // (In that case, it will still be treated as a concrete member and can be | |
269 // called at runtime.) | |
270 var concreteMembers = new HashSet<String>(); | |
271 | |
272 void visit(InterfaceType type, bool isAbstract) { | |
273 if (type == null) return; | |
274 visit(type.superclass, isAbstract); | |
275 for (var m in type.mixins) visit(m, isAbstract); | |
276 for (var i in type.interfaces) visit(i, true); | |
277 | |
278 for (var m in [type.methods, type.accessors].expand((m) => m)) { | |
279 if (isAbstract || m.isAbstract) { | |
280 mockMembers[m.name] = m; | |
281 } else if (!m.isStatic) { | |
282 concreteMembers.add(m.name); | |
283 } | |
284 } | |
285 } | |
286 | |
287 visit(type, false); | |
288 | |
289 concreteMembers.forEach(mockMembers.remove); | |
290 } | |
291 | |
292 void _collectExtensionMembers( | |
293 ExtensionTypeSet extensionTypes, ClassElement element) { | |
294 if (extensionTypes.isNativeClass(element)) return; | |
295 | |
296 // Collect all extension types we implement. | |
297 var type = element.type; | |
298 var types = extensionTypes.collectNativeInterfaces(element); | |
299 if (types.isEmpty) return; | |
300 | |
301 // Collect all possible extension method names. | |
302 var possibleExtensions = new HashSet<String>(); | |
303 for (var t in types) { | |
304 for (var m in [t.methods, t.accessors].expand((m) => m)) { | |
305 if (!m.isStatic && m.isPublic) possibleExtensions.add(m.name); | |
306 } | |
307 } | |
308 | |
309 // Collect all of extension methods this type and its mixins implement. | |
310 | |
311 void visitType(InterfaceType type, bool isMixin) { | |
312 for (var mixin in type.mixins) { | |
313 // If the mixin isn't native, make sure to visit it too, because those | |
314 // methods haven't been accounted for yet. | |
315 if (!extensionTypes.hasNativeSubtype(mixin)) visitType(mixin, true); | |
316 } | |
317 for (var m in [type.methods, type.accessors].expand((m) => m)) { | |
318 if (!m.isAbstract && possibleExtensions.contains(m.name)) { | |
319 (isMixin ? mixinExtensionMembers : extensionMembers).add(m); | |
320 } | |
321 } | |
322 } | |
323 | |
324 visitType(type, false); | |
325 | |
326 for (var m in mockMembers.values) { | |
327 if (possibleExtensions.contains(m.name)) extensionMembers.add(m); | |
328 } | |
329 } | |
237 } | 330 } |
OLD | NEW |