OLD | NEW |
(Empty) | |
| 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 |
| 3 // BSD-style license that can be found in the LICENSE file. |
| 4 |
| 5 import 'dart:collection' show HashMap, HashSet, Queue; |
| 6 |
| 7 import 'package:analyzer/dart/ast/ast.dart' show Identifier; |
| 8 import 'package:analyzer/dart/element/element.dart'; |
| 9 import 'package:analyzer/src/dart/element/element.dart' show FieldElementImpl; |
| 10 |
| 11 import '../js_ast/js_ast.dart' as JS; |
| 12 import 'element_helpers.dart'; |
| 13 import 'js_names.dart' as JS; |
| 14 |
| 15 /// Dart allows all fields to be overridden. |
| 16 /// |
| 17 /// 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 /// 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 /// which members are private and thus, could not be overridden outside of the |
| 22 /// current library. |
| 23 class VirtualFieldModel { |
| 24 final _modelForLibrary = |
| 25 new HashMap<LibraryElement, _LibraryVirtualFieldModel>(); |
| 26 |
| 27 _LibraryVirtualFieldModel _getModel(LibraryElement library) => |
| 28 _modelForLibrary.putIfAbsent( |
| 29 library, () => new _LibraryVirtualFieldModel.build(library)); |
| 30 |
| 31 /// Returns true if a field is virtual. |
| 32 bool isVirtual(FieldElement field) => |
| 33 _getModel(field.library).isVirtual(field); |
| 34 } |
| 35 |
| 36 /// This is a building block of [VirtualFieldModel], used to track information |
| 37 /// about a single library that has been analyzed. |
| 38 class _LibraryVirtualFieldModel { |
| 39 /// Fields that are private (or public fields of a private class) and |
| 40 /// overridden in this library. |
| 41 /// |
| 42 /// This means we must generate them as virtual fields using a property pair |
| 43 /// in JavaScript. |
| 44 final _overriddenPrivateFields = new HashSet<FieldElement>(); |
| 45 |
| 46 /// Private classes that can be extended outside of this library. |
| 47 /// |
| 48 /// Normally private classes cannot be accessed outside this library, however, |
| 49 /// this can happen if they are extended by a public class, for example: |
| 50 /// |
| 51 /// class _A { int x = 42; } |
| 52 /// class _B { int x = 42; } |
| 53 /// |
| 54 /// // _A is now effectively public for the purpose of overrides. |
| 55 /// class C extends _A {} |
| 56 /// |
| 57 /// The class _A must treat is "x" as virtual, however _B does not. |
| 58 final _extensiblePrivateClasses = new HashSet<ClassElement>(); |
| 59 |
| 60 _LibraryVirtualFieldModel.build(LibraryElement library) { |
| 61 var allTypes = library.units.expand((u) => u.types).toList(); |
| 62 |
| 63 // The set of public types is our initial extensible type set. |
| 64 // From there, visit all immediate private types in this library, and so on |
| 65 // from those private types, marking them as extensible. |
| 66 var typesToVisit = |
| 67 new Queue<ClassElement>.from(allTypes.where((t) => t.isPublic)); |
| 68 while (typesToVisit.isNotEmpty) { |
| 69 var extensibleType = typesToVisit.removeFirst(); |
| 70 |
| 71 // For each supertype of a public type in this library, |
| 72 // if we encounter a private class, we mark it as being extended, and |
| 73 // add it to our work set if this is the first time we've visited it. |
| 74 for (var type in getImmediateSuperclasses(extensibleType)) { |
| 75 if (!type.isPublic && type.library == library) { |
| 76 if (_extensiblePrivateClasses.add(type)) typesToVisit.add(type); |
| 77 } |
| 78 } |
| 79 } |
| 80 |
| 81 // ClassElement can only look up inherited members with an O(N) scan through |
| 82 // the class, so we build up a mapping of all fields in the library ahead of |
| 83 // time. |
| 84 var allFields = |
| 85 new HashMap<ClassElement, HashMap<String, FieldElement>>.fromIterable( |
| 86 allTypes, |
| 87 value: (t) => new HashMap.fromIterable( |
| 88 t.fields.where((f) => !f.isStatic), |
| 89 key: (f) => f.name)); |
| 90 |
| 91 for (var type in allTypes) { |
| 92 Set<ClassElement> supertypes = null; |
| 93 |
| 94 // Visit accessors in the current class, and see if they override an |
| 95 // otherwise private field. |
| 96 for (var accessor in type.accessors) { |
| 97 // For getter/setter pairs only process them once. |
| 98 if (accessor.correspondingGetter != null) continue; |
| 99 // Ignore abstract or static accessors. |
| 100 if (accessor.isAbstract || accessor.isStatic) continue; |
| 101 // Ignore public accessors in extensible classes. |
| 102 if (accessor.isPublic && |
| 103 (type.isPublic || _extensiblePrivateClasses.contains(type))) { |
| 104 continue; |
| 105 } |
| 106 |
| 107 if (supertypes == null) { |
| 108 supertypes = new Set(); |
| 109 var library = type.library; |
| 110 void collectSupertypes(ClassElement cls) { |
| 111 if (!supertypes.add(cls)) return; |
| 112 var s = cls.supertype?.element; |
| 113 if (s != null) collectSupertypes(s); |
| 114 cls.mixins.forEach((m) => collectSupertypes(m.element)); |
| 115 } |
| 116 |
| 117 collectSupertypes(type); |
| 118 supertypes.remove(type); |
| 119 supertypes.removeWhere((c) => c.library != type.library); |
| 120 } |
| 121 |
| 122 // Look in all super classes to see if we're overriding a field in our |
| 123 // library, if so mark that field as overridden. |
| 124 var name = accessor.variable.name; |
| 125 _overriddenPrivateFields.addAll( |
| 126 supertypes.map((c) => allFields[c][name]).where((f) => f != null)); |
| 127 } |
| 128 } |
| 129 } |
| 130 |
| 131 /// Returns true if a field inside this library is virtual. |
| 132 bool isVirtual(FieldElement field) { |
| 133 // If the field was marked non-virtual, we know for sure. |
| 134 if (!field.isVirtual) return false; |
| 135 if (field.isStatic) return false; |
| 136 |
| 137 var type = field.enclosingElement; |
| 138 var library = type.library; |
| 139 if (library.isInSdk && library.source.uri.toString().startsWith('dart:_')) { |
| 140 // There should be no extensible fields in private SDK libraries. |
| 141 return false; |
| 142 } |
| 143 |
| 144 if (field.isPublic) { |
| 145 // Public fields in public classes (or extensible private classes) |
| 146 // are always virtual. |
| 147 // They could be overridden by someone using our library. |
| 148 if (type.isPublic) return true; |
| 149 if (_extensiblePrivateClasses.contains(type)) return true; |
| 150 } |
| 151 |
| 152 // Otherwise, the field is effectively private and we only need to make it |
| 153 // virtual if it's overridden. |
| 154 return _overriddenPrivateFields.contains(field); |
| 155 } |
| 156 } |
| 157 |
| 158 /// Tracks how fields, getters and setters are represented when emitting JS. |
| 159 /// |
| 160 /// Dart classes have implicit features that must be made explicit: |
| 161 /// |
| 162 /// - virtual fields induce a getter and setter pair. |
| 163 /// - getters and setters are independent. |
| 164 /// - getters and setters can be overridden. |
| 165 /// |
| 166 class ClassPropertyModel { |
| 167 /// Fields that are virtual, that is, they must be generated as a property |
| 168 /// pair in JavaScript. |
| 169 /// |
| 170 /// The value property stores the symbol used for the field's storage slot. |
| 171 final virtualFields = <FieldElement, JS.TemporaryId>{}; |
| 172 |
| 173 /// Static fields that are overridden, this does not matter for Dart but in |
| 174 /// JS we need to take care initializing these because JS classes inherit |
| 175 /// statics. |
| 176 final staticFieldOverrides = new HashSet<FieldElement>(); |
| 177 |
| 178 /// The set of inherited getters, used because JS getters/setters are paired, |
| 179 /// so if we're generating a setter we may need to emit a getter that calls |
| 180 /// super. |
| 181 final inheritedGetters = new HashSet<String>(); |
| 182 |
| 183 /// The set of inherited setters, used because JS getters/setters are paired, |
| 184 /// so if we're generating a getter we may need to emit a setter that calls |
| 185 /// super. |
| 186 final inheritedSetters = new HashSet<String>(); |
| 187 |
| 188 ClassPropertyModel.build(VirtualFieldModel fieldModel, ClassElement classElem, |
| 189 Iterable<ExecutableElement> extensionMembers) { |
| 190 // Visit superclasses to collect information about their fields/accessors. |
| 191 // This is expensive so we try to collect everything in one pass. |
| 192 for (var base in getSuperclasses(classElem)) { |
| 193 for (var accessor in base.accessors) { |
| 194 // For getter/setter pairs only process them once. |
| 195 if (accessor.correspondingGetter != null) continue; |
| 196 |
| 197 var field = accessor.variable; |
| 198 var name = field.name; |
| 199 // Ignore private names from other libraries. |
| 200 if (Identifier.isPrivateName(name) && |
| 201 accessor.library != classElem.library) { |
| 202 continue; |
| 203 } |
| 204 |
| 205 if (field.getter?.isAbstract == false) inheritedGetters.add(name); |
| 206 if (field.setter?.isAbstract == false) inheritedSetters.add(name); |
| 207 } |
| 208 } |
| 209 |
| 210 var extensionNames = |
| 211 new HashSet<String>.from(extensionMembers.map((e) => e.name)); |
| 212 |
| 213 // Visit accessors in the current class, and see if they need to be |
| 214 // generated differently based on the inherited fields/accessors. |
| 215 for (var accessor in classElem.accessors) { |
| 216 // For getter/setter pairs only process them once. |
| 217 if (accessor.correspondingGetter != null) continue; |
| 218 // Also ignore abstract fields. |
| 219 if (accessor.isAbstract) continue; |
| 220 |
| 221 var field = accessor.variable; |
| 222 var name = field.name; |
| 223 // Is it a field? |
| 224 if (!field.isSynthetic && field is FieldElementImpl) { |
| 225 if (inheritedGetters.contains(name) || |
| 226 inheritedSetters.contains(name) || |
| 227 extensionNames.contains(name) || |
| 228 fieldModel.isVirtual(field)) { |
| 229 if (field.isStatic) { |
| 230 staticFieldOverrides.add(field); |
| 231 } else { |
| 232 virtualFields[field] = new JS.TemporaryId(name); |
| 233 } |
| 234 } |
| 235 } |
| 236 } |
| 237 } |
| 238 } |
OLD | NEW |