| OLD | NEW |
| (Empty) | |
| 1 // Copyright (c) 2017, 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 '../closure.dart'; |
| 6 import '../common.dart'; |
| 7 import '../compiler.dart'; |
| 8 import '../constants/values.dart'; |
| 9 import '../elements/elements.dart'; |
| 10 import '../elements/resolution_types.dart'; |
| 11 import '../enqueue.dart'; |
| 12 import '../world.dart'; |
| 13 import '../util/emptyset.dart'; |
| 14 |
| 15 class MirrorsData { |
| 16 /// True if a call to preserveMetadataMarker has been seen. This means that |
| 17 /// metadata must be retained for dart:mirrors to work correctly. |
| 18 bool mustRetainMetadata = false; |
| 19 |
| 20 /// True if any metadata has been retained. This is slightly different from |
| 21 /// [mustRetainMetadata] and tells us if any metadata was retained. For |
| 22 /// example, if [mustRetainMetadata] is true but there is no metadata in the |
| 23 /// program, this variable will stil be false. |
| 24 bool hasRetainedMetadata = false; |
| 25 |
| 26 /// True if a call to preserveLibraryNames has been seen. |
| 27 bool mustRetainLibraryNames = false; |
| 28 |
| 29 /// True if a call to preserveNames has been seen. |
| 30 bool mustPreserveNames = false; |
| 31 |
| 32 /// True if a call to disableTreeShaking has been seen. |
| 33 bool isTreeShakingDisabled = false; |
| 34 |
| 35 /// True if there isn't sufficient @MirrorsUsed data. |
| 36 bool hasInsufficientMirrorsUsed = false; |
| 37 |
| 38 /// List of symbols that the user has requested for reflection. |
| 39 final Set<String> symbolsUsed = new Set<String>(); |
| 40 |
| 41 /// List of elements that the user has requested for reflection. |
| 42 final Set<Element> targetsUsed = new Set<Element>(); |
| 43 |
| 44 /// List of annotations provided by user that indicate that the annotated |
| 45 /// element must be retained. |
| 46 final Set<Element> metaTargetsUsed = new Set<Element>(); |
| 47 |
| 48 final Compiler compiler; |
| 49 |
| 50 MirrorsData(this.compiler); |
| 51 |
| 52 /// Should [element] (a getter) that would normally not be generated due to |
| 53 /// treeshaking be retained for reflection? |
| 54 bool shouldRetainGetter(Element element) { |
| 55 return isTreeShakingDisabled && isAccessibleByReflection(element); |
| 56 } |
| 57 |
| 58 /// Should [element] (a setter) hat would normally not be generated due to |
| 59 /// treeshaking be retained for reflection? |
| 60 bool shouldRetainSetter(Element element) { |
| 61 return isTreeShakingDisabled && isAccessibleByReflection(element); |
| 62 } |
| 63 |
| 64 /// Should [name] be retained for reflection? |
| 65 bool shouldRetainName(String name) { |
| 66 if (hasInsufficientMirrorsUsed) return mustPreserveNames; |
| 67 if (name == '') return false; |
| 68 return symbolsUsed.contains(name); |
| 69 } |
| 70 |
| 71 bool retainMetadataOf(Element element) { |
| 72 if (mustRetainMetadata) hasRetainedMetadata = true; |
| 73 if (mustRetainMetadata && referencedFromMirrorSystem(element)) { |
| 74 for (MetadataAnnotation metadata in element.metadata) { |
| 75 metadata.ensureResolved(compiler.resolution); |
| 76 ConstantValue constant = |
| 77 compiler.backend.constants.getConstantValueForMetadata(metadata); |
| 78 compiler.backend.constants.addCompileTimeConstantForEmission(constant); |
| 79 } |
| 80 return true; |
| 81 } |
| 82 return false; |
| 83 } |
| 84 |
| 85 bool invokedReflectively(Element element) { |
| 86 if (element.isRegularParameter || element.isInitializingFormal) { |
| 87 ParameterElement parameter = element; |
| 88 if (invokedReflectively(parameter.functionDeclaration)) return true; |
| 89 } |
| 90 |
| 91 if (element.isField) { |
| 92 if (Elements.isStaticOrTopLevel(element) && |
| 93 (element.isFinal || element.isConst)) { |
| 94 return false; |
| 95 } |
| 96 } |
| 97 |
| 98 return isAccessibleByReflection(element.declaration); |
| 99 } |
| 100 |
| 101 /// Set of methods that are needed by reflection. Computed using |
| 102 /// [computeMembersNeededForReflection] on first use. |
| 103 Set<Element> _membersNeededForReflection = null; |
| 104 Iterable<Element> get membersNeededForReflection { |
| 105 assert(_membersNeededForReflection != null); |
| 106 return _membersNeededForReflection; |
| 107 } |
| 108 |
| 109 /// Called by [MirrorUsageAnalyzerTask] after it has merged all @MirrorsUsed |
| 110 /// annotations. The arguments corresponds to the unions of the corresponding |
| 111 /// fields of the annotations. |
| 112 void registerMirrorUsage( |
| 113 Set<String> symbols, Set<Element> targets, Set<Element> metaTargets) { |
| 114 if (symbols == null && targets == null && metaTargets == null) { |
| 115 // The user didn't specify anything, or there are imports of |
| 116 // 'dart:mirrors' without @MirrorsUsed. |
| 117 hasInsufficientMirrorsUsed = true; |
| 118 return; |
| 119 } |
| 120 if (symbols != null) symbolsUsed.addAll(symbols); |
| 121 if (targets != null) { |
| 122 for (Element target in targets) { |
| 123 if (target.isAbstractField) { |
| 124 AbstractFieldElement field = target; |
| 125 targetsUsed.add(field.getter); |
| 126 targetsUsed.add(field.setter); |
| 127 } else { |
| 128 targetsUsed.add(target); |
| 129 } |
| 130 } |
| 131 } |
| 132 if (metaTargets != null) metaTargetsUsed.addAll(metaTargets); |
| 133 } |
| 134 |
| 135 /** |
| 136 * Returns `true` if [element] can be accessed through reflection, that is, |
| 137 * is in the set of elements covered by a `MirrorsUsed` annotation. |
| 138 * |
| 139 * This property is used to tag emitted elements with a marker which is |
| 140 * checked by the runtime system to throw an exception if an element is |
| 141 * accessed (invoked, get, set) that is not accessible for the reflective |
| 142 * system. |
| 143 */ |
| 144 bool isAccessibleByReflection(Element element) { |
| 145 if (element.isClass) { |
| 146 element = compiler.backend.getDartClass(element); |
| 147 } |
| 148 return membersNeededForReflection.contains(element); |
| 149 } |
| 150 |
| 151 /// Returns `true` if this member element needs reflection information at |
| 152 /// runtime. |
| 153 bool isMemberAccessibleByReflection(MemberElement element) { |
| 154 return membersNeededForReflection.contains(element); |
| 155 } |
| 156 |
| 157 /// Returns true if this element has to be enqueued due to |
| 158 /// mirror usage. Might be a subset of [referencedFromMirrorSystem] if |
| 159 /// normal tree shaking is still active ([isTreeShakingDisabled] is false). |
| 160 bool requiredByMirrorSystem(Element element) { |
| 161 return hasInsufficientMirrorsUsed && isTreeShakingDisabled || |
| 162 matchesMirrorsMetaTarget(element) || |
| 163 targetsUsed.contains(element); |
| 164 } |
| 165 |
| 166 /// Returns true if this element is covered by a mirrorsUsed annotation. |
| 167 /// |
| 168 /// Note that it might still be ok to tree shake the element away if no |
| 169 /// reflection is used in the program (and thus [isTreeShakingDisabled] is |
| 170 /// still false). Therefore _do not_ use this predicate to decide inclusion |
| 171 /// in the tree, use [requiredByMirrorSystem] instead. |
| 172 bool referencedFromMirrorSystem(Element element, [recursive = true]) { |
| 173 Element enclosing = recursive ? element.enclosingElement : null; |
| 174 |
| 175 return hasInsufficientMirrorsUsed || |
| 176 matchesMirrorsMetaTarget(element) || |
| 177 targetsUsed.contains(element) || |
| 178 (enclosing != null && referencedFromMirrorSystem(enclosing)); |
| 179 } |
| 180 |
| 181 /** |
| 182 * Returns `true` if the element is needed because it has an annotation |
| 183 * of a type that is used as a meta target for reflection. |
| 184 */ |
| 185 bool matchesMirrorsMetaTarget(Element element) { |
| 186 if (metaTargetsUsed.isEmpty) return false; |
| 187 for (MetadataAnnotation metadata in element.metadata) { |
| 188 // TODO(kasperl): It would be nice if we didn't have to resolve |
| 189 // all metadata but only stuff that potentially would match one |
| 190 // of the used meta targets. |
| 191 metadata.ensureResolved(compiler.resolution); |
| 192 ConstantValue value = |
| 193 compiler.constants.getConstantValue(metadata.constant); |
| 194 if (value == null) continue; |
| 195 ResolutionDartType type = value.getType(compiler.commonElements); |
| 196 if (metaTargetsUsed.contains(type.element)) return true; |
| 197 } |
| 198 return false; |
| 199 } |
| 200 |
| 201 /** |
| 202 * Visits all classes and computes whether its members are needed for |
| 203 * reflection. |
| 204 * |
| 205 * We have to precompute this set as we cannot easily answer the need for |
| 206 * reflection locally when looking at the member: We lack the information by |
| 207 * which classes a member is inherited. Called after resolution is complete. |
| 208 * |
| 209 * We filter out private libraries here, as their elements should not |
| 210 * be visible by reflection unless some other interfaces makes them |
| 211 * accessible. |
| 212 */ |
| 213 void computeMembersNeededForReflection(ClosedWorld closedWorld) { |
| 214 if (_membersNeededForReflection != null) return; |
| 215 if (closedWorld.commonElements.mirrorsLibrary == null) { |
| 216 _membersNeededForReflection = const ImmutableEmptySet<Element>(); |
| 217 return; |
| 218 } |
| 219 // Compute a mapping from class to the closures it contains, so we |
| 220 // can include the correct ones when including the class. |
| 221 Map<ClassElement, List<LocalFunctionElement>> closureMap = |
| 222 new Map<ClassElement, List<LocalFunctionElement>>(); |
| 223 for (LocalFunctionElement closure |
| 224 in compiler.resolutionWorldBuilder.allClosures) { |
| 225 closureMap.putIfAbsent(closure.enclosingClass, () => []).add(closure); |
| 226 } |
| 227 bool foundClosure = false; |
| 228 Set<Element> reflectableMembers = new Set<Element>(); |
| 229 ResolutionEnqueuer resolution = compiler.enqueuer.resolution; |
| 230 for (ClassElement cls |
| 231 in resolution.worldBuilder.directlyInstantiatedClasses) { |
| 232 // Do not process internal classes. |
| 233 if (cls.library.isInternalLibrary || cls.isInjected) continue; |
| 234 if (referencedFromMirrorSystem(cls)) { |
| 235 Set<Name> memberNames = new Set<Name>(); |
| 236 // 1) the class (should be resolved) |
| 237 assert(invariant(cls, cls.isResolved)); |
| 238 reflectableMembers.add(cls); |
| 239 // 2) its constructors (if resolved) |
| 240 cls.constructors.forEach((Element constructor) { |
| 241 if (resolution.hasBeenProcessed(constructor)) { |
| 242 reflectableMembers.add(constructor); |
| 243 } |
| 244 }); |
| 245 // 3) all members, including fields via getter/setters (if resolved) |
| 246 cls.forEachClassMember((Member member) { |
| 247 MemberElement element = member.element; |
| 248 if (resolution.hasBeenProcessed(element)) { |
| 249 memberNames.add(member.name); |
| 250 reflectableMembers.add(element); |
| 251 element.nestedClosures |
| 252 .forEach((SynthesizedCallMethodElementX callFunction) { |
| 253 reflectableMembers.add(callFunction); |
| 254 reflectableMembers.add(callFunction.closureClass); |
| 255 }); |
| 256 } |
| 257 }); |
| 258 // 4) all overriding members of subclasses/subtypes (should be resolved) |
| 259 if (closedWorld.hasAnyStrictSubtype(cls)) { |
| 260 closedWorld.forEachStrictSubtypeOf(cls, (ClassElement subcls) { |
| 261 subcls.forEachClassMember((Member member) { |
| 262 if (memberNames.contains(member.name)) { |
| 263 // TODO(20993): find out why this assertion fails. |
| 264 // assert(invariant(member.element, |
| 265 // resolution.hasBeenProcessed(member.element))); |
| 266 if (resolution.hasBeenProcessed(member.element)) { |
| 267 reflectableMembers.add(member.element); |
| 268 } |
| 269 } |
| 270 }); |
| 271 }); |
| 272 } |
| 273 // 5) all its closures |
| 274 List<LocalFunctionElement> closures = closureMap[cls]; |
| 275 if (closures != null) { |
| 276 reflectableMembers.addAll(closures); |
| 277 foundClosure = true; |
| 278 } |
| 279 } else { |
| 280 // check members themselves |
| 281 cls.constructors.forEach((ConstructorElement element) { |
| 282 if (!resolution.hasBeenProcessed(element)) return; |
| 283 if (referencedFromMirrorSystem(element, false)) { |
| 284 reflectableMembers.add(element); |
| 285 } |
| 286 }); |
| 287 cls.forEachClassMember((Member member) { |
| 288 if (!resolution.hasBeenProcessed(member.element)) return; |
| 289 if (referencedFromMirrorSystem(member.element, false)) { |
| 290 reflectableMembers.add(member.element); |
| 291 } |
| 292 }); |
| 293 // Also add in closures. Those might be reflectable is their enclosing |
| 294 // member is. |
| 295 List<LocalFunctionElement> closures = closureMap[cls]; |
| 296 if (closures != null) { |
| 297 for (LocalFunctionElement closure in closures) { |
| 298 MemberElement member = closure.memberContext; |
| 299 if (referencedFromMirrorSystem(member, false)) { |
| 300 reflectableMembers.add(closure); |
| 301 foundClosure = true; |
| 302 } |
| 303 } |
| 304 } |
| 305 } |
| 306 } |
| 307 // We also need top-level non-class elements like static functions and |
| 308 // global fields. We use the resolution queue to decide which elements are |
| 309 // part of the live world. |
| 310 for (LibraryElement lib in compiler.libraryLoader.libraries) { |
| 311 if (lib.isInternalLibrary) continue; |
| 312 lib.forEachLocalMember((Element member) { |
| 313 if (!(member.isClass || member.isTypedef) && |
| 314 resolution.hasBeenProcessed(member) && |
| 315 referencedFromMirrorSystem(member)) { |
| 316 reflectableMembers.add(member); |
| 317 } |
| 318 }); |
| 319 } |
| 320 // And closures inside top-level elements that do not have a surrounding |
| 321 // class. These will be in the [:null:] bucket of the [closureMap]. |
| 322 if (closureMap.containsKey(null)) { |
| 323 for (Element closure in closureMap[null]) { |
| 324 if (referencedFromMirrorSystem(closure)) { |
| 325 reflectableMembers.add(closure); |
| 326 foundClosure = true; |
| 327 } |
| 328 } |
| 329 } |
| 330 // As we do not think about closures as classes, yet, we have to make sure |
| 331 // their superclasses are available for reflection manually. |
| 332 if (foundClosure) { |
| 333 ClassElement cls = compiler.backend.helpers.closureClass; |
| 334 reflectableMembers.add(cls); |
| 335 } |
| 336 Set<Element> closurizedMembers = |
| 337 compiler.resolutionWorldBuilder.closurizedMembers; |
| 338 if (closurizedMembers.any(reflectableMembers.contains)) { |
| 339 ClassElement cls = compiler.backend.helpers.boundClosureClass; |
| 340 reflectableMembers.add(cls); |
| 341 } |
| 342 // Add typedefs. |
| 343 reflectableMembers |
| 344 .addAll(closedWorld.allTypedefs.where(referencedFromMirrorSystem)); |
| 345 // Register all symbols of reflectable elements |
| 346 for (Element element in reflectableMembers) { |
| 347 symbolsUsed.add(element.name); |
| 348 } |
| 349 _membersNeededForReflection = reflectableMembers; |
| 350 } |
| 351 |
| 352 // TODO(20791): compute closure classes after resolution and move this code to |
| 353 // [computeMembersNeededForReflection]. |
| 354 void maybeMarkClosureAsNeededForReflection( |
| 355 ClosureClassElement globalizedElement, |
| 356 FunctionElement callFunction, |
| 357 FunctionElement function) { |
| 358 if (!_membersNeededForReflection.contains(function)) return; |
| 359 _membersNeededForReflection.add(callFunction); |
| 360 _membersNeededForReflection.add(globalizedElement); |
| 361 } |
| 362 |
| 363 /// Called when [:const Symbol(name):] is seen. |
| 364 void registerConstSymbol(String name) { |
| 365 symbolsUsed.add(name); |
| 366 if (name.endsWith('=')) { |
| 367 symbolsUsed.add(name.substring(0, name.length - 1)); |
| 368 } |
| 369 } |
| 370 } |
| OLD | NEW |