Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(886)

Unified Diff: pkg/compiler/lib/src/js_backend/mirrors_data.dart

Issue 2688413003: Extract BackendUsage, MirrorsData, and CheckedModeHelpers from Backend. (Closed)
Patch Set: Cleanup Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « pkg/compiler/lib/src/js_backend/mirrors_analysis.dart ('k') | pkg/compiler/lib/src/js_backend/namer.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: pkg/compiler/lib/src/js_backend/mirrors_data.dart
diff --git a/pkg/compiler/lib/src/js_backend/mirrors_data.dart b/pkg/compiler/lib/src/js_backend/mirrors_data.dart
new file mode 100644
index 0000000000000000000000000000000000000000..6734fed8687d152767e1b05b3b1be10015dc7bb5
--- /dev/null
+++ b/pkg/compiler/lib/src/js_backend/mirrors_data.dart
@@ -0,0 +1,370 @@
+// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import '../closure.dart';
+import '../common.dart';
+import '../compiler.dart';
+import '../constants/values.dart';
+import '../elements/elements.dart';
+import '../elements/resolution_types.dart';
+import '../enqueue.dart';
+import '../world.dart';
+import '../util/emptyset.dart';
+
+class MirrorsData {
+ /// True if a call to preserveMetadataMarker has been seen. This means that
+ /// metadata must be retained for dart:mirrors to work correctly.
+ bool mustRetainMetadata = false;
+
+ /// True if any metadata has been retained. This is slightly different from
+ /// [mustRetainMetadata] and tells us if any metadata was retained. For
+ /// example, if [mustRetainMetadata] is true but there is no metadata in the
+ /// program, this variable will stil be false.
+ bool hasRetainedMetadata = false;
+
+ /// True if a call to preserveLibraryNames has been seen.
+ bool mustRetainLibraryNames = false;
+
+ /// True if a call to preserveNames has been seen.
+ bool mustPreserveNames = false;
+
+ /// True if a call to disableTreeShaking has been seen.
+ bool isTreeShakingDisabled = false;
+
+ /// True if there isn't sufficient @MirrorsUsed data.
+ bool hasInsufficientMirrorsUsed = false;
+
+ /// List of symbols that the user has requested for reflection.
+ final Set<String> symbolsUsed = new Set<String>();
+
+ /// List of elements that the user has requested for reflection.
+ final Set<Element> targetsUsed = new Set<Element>();
+
+ /// List of annotations provided by user that indicate that the annotated
+ /// element must be retained.
+ final Set<Element> metaTargetsUsed = new Set<Element>();
+
+ final Compiler compiler;
+
+ MirrorsData(this.compiler);
+
+ /// Should [element] (a getter) that would normally not be generated due to
+ /// treeshaking be retained for reflection?
+ bool shouldRetainGetter(Element element) {
+ return isTreeShakingDisabled && isAccessibleByReflection(element);
+ }
+
+ /// Should [element] (a setter) hat would normally not be generated due to
+ /// treeshaking be retained for reflection?
+ bool shouldRetainSetter(Element element) {
+ return isTreeShakingDisabled && isAccessibleByReflection(element);
+ }
+
+ /// Should [name] be retained for reflection?
+ bool shouldRetainName(String name) {
+ if (hasInsufficientMirrorsUsed) return mustPreserveNames;
+ if (name == '') return false;
+ return symbolsUsed.contains(name);
+ }
+
+ bool retainMetadataOf(Element element) {
+ if (mustRetainMetadata) hasRetainedMetadata = true;
+ if (mustRetainMetadata && referencedFromMirrorSystem(element)) {
+ for (MetadataAnnotation metadata in element.metadata) {
+ metadata.ensureResolved(compiler.resolution);
+ ConstantValue constant =
+ compiler.backend.constants.getConstantValueForMetadata(metadata);
+ compiler.backend.constants.addCompileTimeConstantForEmission(constant);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ bool invokedReflectively(Element element) {
+ if (element.isRegularParameter || element.isInitializingFormal) {
+ ParameterElement parameter = element;
+ if (invokedReflectively(parameter.functionDeclaration)) return true;
+ }
+
+ if (element.isField) {
+ if (Elements.isStaticOrTopLevel(element) &&
+ (element.isFinal || element.isConst)) {
+ return false;
+ }
+ }
+
+ return isAccessibleByReflection(element.declaration);
+ }
+
+ /// Set of methods that are needed by reflection. Computed using
+ /// [computeMembersNeededForReflection] on first use.
+ Set<Element> _membersNeededForReflection = null;
+ Iterable<Element> get membersNeededForReflection {
+ assert(_membersNeededForReflection != null);
+ return _membersNeededForReflection;
+ }
+
+ /// Called by [MirrorUsageAnalyzerTask] after it has merged all @MirrorsUsed
+ /// annotations. The arguments corresponds to the unions of the corresponding
+ /// fields of the annotations.
+ void registerMirrorUsage(
+ Set<String> symbols, Set<Element> targets, Set<Element> metaTargets) {
+ if (symbols == null && targets == null && metaTargets == null) {
+ // The user didn't specify anything, or there are imports of
+ // 'dart:mirrors' without @MirrorsUsed.
+ hasInsufficientMirrorsUsed = true;
+ return;
+ }
+ if (symbols != null) symbolsUsed.addAll(symbols);
+ if (targets != null) {
+ for (Element target in targets) {
+ if (target.isAbstractField) {
+ AbstractFieldElement field = target;
+ targetsUsed.add(field.getter);
+ targetsUsed.add(field.setter);
+ } else {
+ targetsUsed.add(target);
+ }
+ }
+ }
+ if (metaTargets != null) metaTargetsUsed.addAll(metaTargets);
+ }
+
+ /**
+ * Returns `true` if [element] can be accessed through reflection, that is,
+ * is in the set of elements covered by a `MirrorsUsed` annotation.
+ *
+ * This property is used to tag emitted elements with a marker which is
+ * checked by the runtime system to throw an exception if an element is
+ * accessed (invoked, get, set) that is not accessible for the reflective
+ * system.
+ */
+ bool isAccessibleByReflection(Element element) {
+ if (element.isClass) {
+ element = compiler.backend.getDartClass(element);
+ }
+ return membersNeededForReflection.contains(element);
+ }
+
+ /// Returns `true` if this member element needs reflection information at
+ /// runtime.
+ bool isMemberAccessibleByReflection(MemberElement element) {
+ return membersNeededForReflection.contains(element);
+ }
+
+ /// Returns true if this element has to be enqueued due to
+ /// mirror usage. Might be a subset of [referencedFromMirrorSystem] if
+ /// normal tree shaking is still active ([isTreeShakingDisabled] is false).
+ bool requiredByMirrorSystem(Element element) {
+ return hasInsufficientMirrorsUsed && isTreeShakingDisabled ||
+ matchesMirrorsMetaTarget(element) ||
+ targetsUsed.contains(element);
+ }
+
+ /// Returns true if this element is covered by a mirrorsUsed annotation.
+ ///
+ /// Note that it might still be ok to tree shake the element away if no
+ /// reflection is used in the program (and thus [isTreeShakingDisabled] is
+ /// still false). Therefore _do not_ use this predicate to decide inclusion
+ /// in the tree, use [requiredByMirrorSystem] instead.
+ bool referencedFromMirrorSystem(Element element, [recursive = true]) {
+ Element enclosing = recursive ? element.enclosingElement : null;
+
+ return hasInsufficientMirrorsUsed ||
+ matchesMirrorsMetaTarget(element) ||
+ targetsUsed.contains(element) ||
+ (enclosing != null && referencedFromMirrorSystem(enclosing));
+ }
+
+ /**
+ * Returns `true` if the element is needed because it has an annotation
+ * of a type that is used as a meta target for reflection.
+ */
+ bool matchesMirrorsMetaTarget(Element element) {
+ if (metaTargetsUsed.isEmpty) return false;
+ for (MetadataAnnotation metadata in element.metadata) {
+ // TODO(kasperl): It would be nice if we didn't have to resolve
+ // all metadata but only stuff that potentially would match one
+ // of the used meta targets.
+ metadata.ensureResolved(compiler.resolution);
+ ConstantValue value =
+ compiler.constants.getConstantValue(metadata.constant);
+ if (value == null) continue;
+ ResolutionDartType type = value.getType(compiler.commonElements);
+ if (metaTargetsUsed.contains(type.element)) return true;
+ }
+ return false;
+ }
+
+ /**
+ * Visits all classes and computes whether its members are needed for
+ * reflection.
+ *
+ * We have to precompute this set as we cannot easily answer the need for
+ * reflection locally when looking at the member: We lack the information by
+ * which classes a member is inherited. Called after resolution is complete.
+ *
+ * We filter out private libraries here, as their elements should not
+ * be visible by reflection unless some other interfaces makes them
+ * accessible.
+ */
+ void computeMembersNeededForReflection(ClosedWorld closedWorld) {
+ if (_membersNeededForReflection != null) return;
+ if (closedWorld.commonElements.mirrorsLibrary == null) {
+ _membersNeededForReflection = const ImmutableEmptySet<Element>();
+ return;
+ }
+ // Compute a mapping from class to the closures it contains, so we
+ // can include the correct ones when including the class.
+ Map<ClassElement, List<LocalFunctionElement>> closureMap =
+ new Map<ClassElement, List<LocalFunctionElement>>();
+ for (LocalFunctionElement closure
+ in compiler.resolutionWorldBuilder.allClosures) {
+ closureMap.putIfAbsent(closure.enclosingClass, () => []).add(closure);
+ }
+ bool foundClosure = false;
+ Set<Element> reflectableMembers = new Set<Element>();
+ ResolutionEnqueuer resolution = compiler.enqueuer.resolution;
+ for (ClassElement cls
+ in resolution.worldBuilder.directlyInstantiatedClasses) {
+ // Do not process internal classes.
+ if (cls.library.isInternalLibrary || cls.isInjected) continue;
+ if (referencedFromMirrorSystem(cls)) {
+ Set<Name> memberNames = new Set<Name>();
+ // 1) the class (should be resolved)
+ assert(invariant(cls, cls.isResolved));
+ reflectableMembers.add(cls);
+ // 2) its constructors (if resolved)
+ cls.constructors.forEach((Element constructor) {
+ if (resolution.hasBeenProcessed(constructor)) {
+ reflectableMembers.add(constructor);
+ }
+ });
+ // 3) all members, including fields via getter/setters (if resolved)
+ cls.forEachClassMember((Member member) {
+ MemberElement element = member.element;
+ if (resolution.hasBeenProcessed(element)) {
+ memberNames.add(member.name);
+ reflectableMembers.add(element);
+ element.nestedClosures
+ .forEach((SynthesizedCallMethodElementX callFunction) {
+ reflectableMembers.add(callFunction);
+ reflectableMembers.add(callFunction.closureClass);
+ });
+ }
+ });
+ // 4) all overriding members of subclasses/subtypes (should be resolved)
+ if (closedWorld.hasAnyStrictSubtype(cls)) {
+ closedWorld.forEachStrictSubtypeOf(cls, (ClassElement subcls) {
+ subcls.forEachClassMember((Member member) {
+ if (memberNames.contains(member.name)) {
+ // TODO(20993): find out why this assertion fails.
+ // assert(invariant(member.element,
+ // resolution.hasBeenProcessed(member.element)));
+ if (resolution.hasBeenProcessed(member.element)) {
+ reflectableMembers.add(member.element);
+ }
+ }
+ });
+ });
+ }
+ // 5) all its closures
+ List<LocalFunctionElement> closures = closureMap[cls];
+ if (closures != null) {
+ reflectableMembers.addAll(closures);
+ foundClosure = true;
+ }
+ } else {
+ // check members themselves
+ cls.constructors.forEach((ConstructorElement element) {
+ if (!resolution.hasBeenProcessed(element)) return;
+ if (referencedFromMirrorSystem(element, false)) {
+ reflectableMembers.add(element);
+ }
+ });
+ cls.forEachClassMember((Member member) {
+ if (!resolution.hasBeenProcessed(member.element)) return;
+ if (referencedFromMirrorSystem(member.element, false)) {
+ reflectableMembers.add(member.element);
+ }
+ });
+ // Also add in closures. Those might be reflectable is their enclosing
+ // member is.
+ List<LocalFunctionElement> closures = closureMap[cls];
+ if (closures != null) {
+ for (LocalFunctionElement closure in closures) {
+ MemberElement member = closure.memberContext;
+ if (referencedFromMirrorSystem(member, false)) {
+ reflectableMembers.add(closure);
+ foundClosure = true;
+ }
+ }
+ }
+ }
+ }
+ // We also need top-level non-class elements like static functions and
+ // global fields. We use the resolution queue to decide which elements are
+ // part of the live world.
+ for (LibraryElement lib in compiler.libraryLoader.libraries) {
+ if (lib.isInternalLibrary) continue;
+ lib.forEachLocalMember((Element member) {
+ if (!(member.isClass || member.isTypedef) &&
+ resolution.hasBeenProcessed(member) &&
+ referencedFromMirrorSystem(member)) {
+ reflectableMembers.add(member);
+ }
+ });
+ }
+ // And closures inside top-level elements that do not have a surrounding
+ // class. These will be in the [:null:] bucket of the [closureMap].
+ if (closureMap.containsKey(null)) {
+ for (Element closure in closureMap[null]) {
+ if (referencedFromMirrorSystem(closure)) {
+ reflectableMembers.add(closure);
+ foundClosure = true;
+ }
+ }
+ }
+ // As we do not think about closures as classes, yet, we have to make sure
+ // their superclasses are available for reflection manually.
+ if (foundClosure) {
+ ClassElement cls = compiler.backend.helpers.closureClass;
+ reflectableMembers.add(cls);
+ }
+ Set<Element> closurizedMembers =
+ compiler.resolutionWorldBuilder.closurizedMembers;
+ if (closurizedMembers.any(reflectableMembers.contains)) {
+ ClassElement cls = compiler.backend.helpers.boundClosureClass;
+ reflectableMembers.add(cls);
+ }
+ // Add typedefs.
+ reflectableMembers
+ .addAll(closedWorld.allTypedefs.where(referencedFromMirrorSystem));
+ // Register all symbols of reflectable elements
+ for (Element element in reflectableMembers) {
+ symbolsUsed.add(element.name);
+ }
+ _membersNeededForReflection = reflectableMembers;
+ }
+
+ // TODO(20791): compute closure classes after resolution and move this code to
+ // [computeMembersNeededForReflection].
+ void maybeMarkClosureAsNeededForReflection(
+ ClosureClassElement globalizedElement,
+ FunctionElement callFunction,
+ FunctionElement function) {
+ if (!_membersNeededForReflection.contains(function)) return;
+ _membersNeededForReflection.add(callFunction);
+ _membersNeededForReflection.add(globalizedElement);
+ }
+
+ /// Called when [:const Symbol(name):] is seen.
+ void registerConstSymbol(String name) {
+ symbolsUsed.add(name);
+ if (name.endsWith('=')) {
+ symbolsUsed.add(name.substring(0, name.length - 1));
+ }
+ }
+}
« no previous file with comments | « pkg/compiler/lib/src/js_backend/mirrors_analysis.dart ('k') | pkg/compiler/lib/src/js_backend/namer.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698