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 HashSet; | 5 import 'dart:collection' show HashMap, HashSet; |
6 | 6 |
| 7 import 'package:analyzer/analyzer.dart' as analyzer; |
7 import 'package:analyzer/dart/ast/ast.dart' show Identifier; | 8 import 'package:analyzer/dart/ast/ast.dart' show Identifier; |
8 import 'package:analyzer/dart/element/element.dart'; | 9 import 'package:analyzer/dart/element/element.dart'; |
9 import 'package:analyzer/src/dart/element/element.dart' show FieldElementImpl; | 10 import 'package:analyzer/src/dart/element/element.dart' show FieldElementImpl; |
10 | 11 |
11 import '../js_ast/js_ast.dart' as JS; | 12 import '../js_ast/js_ast.dart' as JS; |
12 import 'element_helpers.dart'; | 13 import 'element_helpers.dart'; |
13 import 'js_names.dart' as JS; | 14 import 'js_names.dart' as JS; |
14 | 15 |
15 /// Tracks how fields, getters and setters are represented when emitting JS. | 16 /// Tracks how fields, getters and setters are represented when emitting JS. |
16 /// | 17 /// |
17 /// Dart classes have implicit features that must be made explicit: | 18 /// Dart classes have implicit features that must be made explicit: |
18 /// | 19 /// |
19 /// - virtual fields induce a getter and setter pair. | 20 /// - virtual fields induce a getter and setter pair. |
20 /// - getters and setters are independent. | 21 /// - getters and setters are independent. |
21 /// - getters and setters can be overridden. | 22 /// - getters and setters can be overridden. |
22 /// | 23 /// |
23 class ClassPropertyModel { | 24 class ClassPropertyModel { |
24 /// Fields that are virtual, that is, they must be generated as a property | 25 /// Fields that are virtual, that is, they must be generated as a property |
25 /// pair in JavaScript. | 26 /// pair in JavaScript. |
26 /// | 27 /// |
27 /// The value property stores the symbol used for the field's storage slot. | 28 /// The value property stores the symbol used for the field's storage slot. |
28 final virtualFields = <FieldElement, JS.TemporaryId>{}; | 29 final virtualFields = <FieldElement, JS.TemporaryId>{}; |
29 | 30 |
| 31 /// Fields that have been overridden by the current class and must be |
| 32 /// virtualized. |
| 33 /// |
| 34 /// These fields are the elements of the super class, and not the |
| 35 /// overriding elements. |
| 36 /// |
| 37 /// These elements must point to real fields (not getters). |
| 38 final virtualizedFields = <FieldElement, JS.TemporaryId>{}; |
| 39 |
30 /// Static fields that are overridden, this does not matter for Dart but in | 40 /// Static fields that are overridden, this does not matter for Dart but in |
31 /// JS we need to take care initializing these because JS classes inherit | 41 /// JS we need to take care initializing these because JS classes inherit |
32 /// statics. | 42 /// statics. |
33 final staticFieldOverrides = new HashSet<FieldElement>(); | 43 final staticFieldOverrides = new HashSet<FieldElement>(); |
34 | 44 |
35 /// The set of inherited getters, used because JS getters/setters are paired, | 45 /// The set of inherited getters, used because JS getters/setters are paired, |
36 /// so if we're generating a setter we may need to emit a getter that calls | 46 /// so if we're generating a setter we may need to emit a getter that calls |
37 /// super. | 47 /// super. |
38 final inheritedGetters = new HashSet<String>(); | 48 final inheritedGetters = new HashSet<String>(); |
39 | 49 |
40 /// The set of inherited setters, used because JS getters/setters are paired, | 50 /// The set of inherited setters, used because JS getters/setters are paired, |
41 /// so if we're generating a getter we may need to emit a setter that calls | 51 /// so if we're generating a getter we may need to emit a setter that calls |
42 /// super. | 52 /// super. |
43 final inheritedSetters = new HashSet<String>(); | 53 final inheritedSetters = new HashSet<String>(); |
44 | 54 |
45 ClassPropertyModel.build( | 55 ClassPropertyModel.build( |
46 ClassElement classElem, Iterable<ExecutableElement> extensionMembers) { | 56 ClassElement classElem, Iterable<ExecutableElement> extensionMembers) { |
| 57 LibraryElement classLibrary = classElem.library; |
| 58 HashMap<String, FieldElementImpl> overriddenFields = new HashMap(); |
| 59 HashSet<String> alreadyVirtual = new HashSet(); |
| 60 |
47 // Visit superclasses to collect information about their fields/accessors. | 61 // Visit superclasses to collect information about their fields/accessors. |
48 // This is expensive so we try to collect everything in one pass. | 62 // This is expensive so we try to collect everything in one pass. |
49 for (var base in getSuperclasses(classElem)) { | 63 for (var base in getSuperclasses(classElem)) { |
50 for (var accessor in base.accessors) { | 64 for (var accessor in base.accessors) { |
51 // For getter/setter pairs only process them once. | 65 // For getter/setter pairs only process them once. |
52 if (accessor.correspondingGetter != null) continue; | 66 if (accessor.correspondingGetter != null) continue; |
53 | 67 |
54 var field = accessor.variable; | 68 var field = accessor.variable; |
55 var name = field.name; | 69 var name = field.name; |
| 70 |
56 // Ignore private names from other libraries. | 71 // Ignore private names from other libraries. |
57 if (Identifier.isPrivateName(name) && | 72 if (Identifier.isPrivateName(name) && |
58 accessor.library != classElem.library) { | 73 accessor.library != classLibrary) { |
59 continue; | 74 continue; |
60 } | 75 } |
61 | 76 |
62 if (field.getter?.isAbstract == false) inheritedGetters.add(name); | 77 // Ignore abstract getters and setters. |
63 if (field.setter?.isAbstract == false) inheritedSetters.add(name); | 78 if (!(field.getter?.isAbstract == false || |
| 79 field.setter?.isAbstract == false)) { |
| 80 continue; |
| 81 } |
| 82 |
| 83 if (!field.isStatic && !alreadyVirtual.contains(name)) { |
| 84 // Synthetic field means there is a getter or setter and the member |
| 85 // is therefore already virtual. |
| 86 if (field.isSynthetic) { |
| 87 alreadyVirtual.add(name); |
| 88 } else if (field is FieldElementImpl) { |
| 89 if (overriddenFields.containsKey(name)) { |
| 90 // The field is already overridden by a super class. |
| 91 // class A { int x; } // Second time the field is added. |
| 92 // class B extends A { int x; } // First time the field is added. |
| 93 // class C extends B { ... }; |
| 94 alreadyVirtual.add(name); |
| 95 } else { |
| 96 overriddenFields[name] = field; |
| 97 } |
| 98 } |
| 99 } |
| 100 |
| 101 if (field.getter?.isAbstract == false) { |
| 102 inheritedGetters.add(name); |
| 103 } |
| 104 if (field.setter?.isAbstract == false) { |
| 105 inheritedSetters.add(name); |
| 106 } |
64 } | 107 } |
65 } | 108 } |
66 | 109 |
67 var extensionNames = | 110 var extensionNames = |
68 new HashSet<String>.from(extensionMembers.map((e) => e.name)); | 111 new HashSet<String>.from(extensionMembers.map((e) => e.name)); |
69 | 112 |
70 // Visit accessors in the current class, and see if they need to be | 113 // Visit accessors in the current class, and see if they need to be |
71 // generated differently based on the inherited fields/accessors. | 114 // generated differently based on the inherited fields/accessors. |
72 for (var accessor in classElem.accessors) { | 115 for (var accessor in classElem.accessors) { |
73 // For getter/setter pairs only process them once. | 116 // For getter/setter pairs only process them once. |
74 if (accessor.correspondingGetter != null) continue; | 117 if (accessor.correspondingGetter != null) continue; |
75 // Also ignore abstract fields. | 118 // Also ignore abstract fields. |
76 if (accessor.isAbstract) continue; | 119 if (accessor.isAbstract) continue; |
77 | 120 |
78 var field = accessor.variable; | 121 var field = accessor.variable; |
79 var name = field.name; | 122 var name = field.name; |
| 123 |
| 124 if (!field.isStatic && |
| 125 !alreadyVirtual.contains(name) && |
| 126 overriddenFields.containsKey(name)) { |
| 127 JS.TemporaryId id = new JS.TemporaryId(name); |
| 128 var superField = overriddenFields[name]; |
| 129 virtualizedFields[superField] = id; |
| 130 alreadyVirtual.add(name); |
| 131 } |
| 132 |
80 // Is it a field? | 133 // Is it a field? |
81 if (!field.isSynthetic && field is FieldElementImpl) { | 134 if (!field.isSynthetic && field is FieldElementImpl) { |
82 if (inheritedGetters.contains(name) || | 135 if (inheritedGetters.contains(name) || |
83 inheritedSetters.contains(name) || | 136 inheritedSetters.contains(name) || |
84 extensionNames.contains(name) || | 137 extensionNames.contains(name)) { |
85 field.isVirtual) { | |
86 if (field.isStatic) { | 138 if (field.isStatic) { |
87 staticFieldOverrides.add(field); | 139 staticFieldOverrides.add(field); |
88 } else { | 140 } else { |
89 virtualFields[field] = new JS.TemporaryId(name); | 141 virtualFields[field] = new JS.TemporaryId(name); |
90 } | 142 } |
91 } | 143 } |
92 } | 144 } |
93 } | 145 } |
| 146 |
| 147 // Run through all bodies and find all `super.x` accesses. Then take their |
| 148 // names (`x`) and see if they need to be virtualized. |
| 149 for (var name in SuperFinder.accessedSuperProperties(classElem)) { |
| 150 if (!alreadyVirtual.contains(name) && |
| 151 overriddenFields.containsKey(name)) { |
| 152 JS.TemporaryId id = new JS.TemporaryId(name); |
| 153 var superField = overriddenFields[name]; |
| 154 virtualizedFields[superField] = id; |
| 155 } |
| 156 } |
94 } | 157 } |
95 } | 158 } |
| 159 |
| 160 class SuperFinder extends analyzer.GeneralizingAstVisitor<Object> { |
| 161 final accessed = new HashSet<String>(); |
| 162 |
| 163 static Iterable<String> accessedSuperProperties(ClassElement element) { |
| 164 // When compiling the SDK, only one out of ~50 classes has any references to |
| 165 // super at all, so we avoid searching the method bodies in the common case. |
| 166 if (!element.hasReferenceToSuper) return const <String>[]; |
| 167 SuperFinder finder = new SuperFinder(); |
| 168 element.computeNode().accept(finder); |
| 169 return finder.accessed; |
| 170 } |
| 171 |
| 172 @override |
| 173 analyzer.PropertyAccess visitPropertyAccess(analyzer.PropertyAccess node) { |
| 174 if (node.realTarget is analyzer.SuperExpression) { |
| 175 var name = node.propertyName.name; |
| 176 accessed.add(name); |
| 177 } |
| 178 return node; |
| 179 } |
| 180 } |
OLD | NEW |