| // Copyright (c) 2016, the R8 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. |
| package com.android.tools.r8.graph; |
| |
| import static com.android.tools.r8.kotlin.KotlinMetadataUtils.getNoKotlinInfo; |
| import static com.google.common.base.Predicates.alwaysTrue; |
| |
| import com.android.tools.r8.ProgramResource; |
| import com.android.tools.r8.ProgramResource.Kind; |
| import com.android.tools.r8.cf.CfVersion; |
| import com.android.tools.r8.dex.IndexedItemCollection; |
| import com.android.tools.r8.dex.MixedSectionCollection; |
| import com.android.tools.r8.errors.CompilationError; |
| import com.android.tools.r8.graph.GenericSignature.ClassSignature; |
| import com.android.tools.r8.graph.GenericSignature.ClassTypeSignature; |
| import com.android.tools.r8.graph.MethodCollection.MethodCollectionFactory; |
| import com.android.tools.r8.ir.conversion.LensCodeRewriterUtils; |
| import com.android.tools.r8.kotlin.KotlinClassLevelInfo; |
| import com.android.tools.r8.naming.NamingLens; |
| import com.android.tools.r8.origin.Origin; |
| import com.android.tools.r8.synthesis.SyntheticMarker; |
| import com.android.tools.r8.utils.InternalOptions; |
| import com.android.tools.r8.utils.OptionalBool; |
| import com.android.tools.r8.utils.TraversalContinuation; |
| import com.android.tools.r8.utils.structural.Ordered; |
| import com.android.tools.r8.utils.structural.StructuralItem; |
| import com.android.tools.r8.utils.structural.StructuralMapping; |
| import com.android.tools.r8.utils.structural.StructuralSpecification; |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.Iterables; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collections; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Set; |
| import java.util.function.Consumer; |
| import java.util.function.Function; |
| import java.util.function.Predicate; |
| import java.util.function.Supplier; |
| |
| public class DexProgramClass extends DexClass |
| implements ProgramClass, Supplier<DexProgramClass>, StructuralItem<DexProgramClass> { |
| |
| @FunctionalInterface |
| public interface ChecksumSupplier { |
| long getChecksum(DexProgramClass programClass); |
| } |
| |
| public static final DexProgramClass[] EMPTY_ARRAY = {}; |
| |
| private final ProgramResource.Kind originKind; |
| private CfVersion initialClassFileVersion = null; |
| private boolean deprecated = false; |
| private KotlinClassLevelInfo kotlinInfo = getNoKotlinInfo(); |
| private OptionalBool reachabilitySensitive = OptionalBool.unknown(); |
| |
| private final ChecksumSupplier checksumSupplier; |
| |
| private SyntheticMarker syntheticMarker; |
| |
| public DexProgramClass( |
| DexType type, |
| Kind originKind, |
| Origin origin, |
| ClassAccessFlags accessFlags, |
| DexType superType, |
| DexTypeList interfaces, |
| DexString sourceFile, |
| NestHostClassAttribute nestHost, |
| List<NestMemberClassAttribute> nestMembers, |
| List<PermittedSubclassAttribute> permittedSubclasses, |
| EnclosingMethodAttribute enclosingMember, |
| List<InnerClassAttribute> innerClasses, |
| ClassSignature classSignature, |
| DexAnnotationSet classAnnotations, |
| DexEncodedField[] staticFields, |
| DexEncodedField[] instanceFields, |
| MethodCollectionFactory methodCollectionFactory, |
| boolean skipNameValidationForTesting, |
| ChecksumSupplier checksumSupplier, |
| SyntheticMarker syntheticMarker) { |
| super( |
| sourceFile, |
| interfaces, |
| accessFlags, |
| superType, |
| type, |
| staticFields, |
| instanceFields, |
| methodCollectionFactory, |
| nestHost, |
| nestMembers, |
| permittedSubclasses, |
| enclosingMember, |
| innerClasses, |
| classSignature, |
| classAnnotations, |
| origin, |
| skipNameValidationForTesting); |
| assert checksumSupplier != null; |
| assert classAnnotations != null; |
| this.originKind = originKind; |
| this.checksumSupplier = checksumSupplier; |
| this.syntheticMarker = syntheticMarker; |
| } |
| |
| public DexProgramClass( |
| DexType type, |
| Kind originKind, |
| Origin origin, |
| ClassAccessFlags accessFlags, |
| DexType superType, |
| DexTypeList interfaces, |
| DexString sourceFile, |
| NestHostClassAttribute nestHost, |
| List<NestMemberClassAttribute> nestMembers, |
| List<PermittedSubclassAttribute> permittedSubclasses, |
| EnclosingMethodAttribute enclosingMember, |
| List<InnerClassAttribute> innerClasses, |
| ClassSignature classSignature, |
| DexAnnotationSet classAnnotations, |
| DexEncodedField[] staticFields, |
| DexEncodedField[] instanceFields, |
| MethodCollectionFactory methodCollectionFactory, |
| boolean skipNameValidationForTesting, |
| ChecksumSupplier checksumSupplier) { |
| this( |
| type, |
| originKind, |
| origin, |
| accessFlags, |
| superType, |
| interfaces, |
| sourceFile, |
| nestHost, |
| nestMembers, |
| permittedSubclasses, |
| enclosingMember, |
| innerClasses, |
| classSignature, |
| classAnnotations, |
| staticFields, |
| instanceFields, |
| methodCollectionFactory, |
| skipNameValidationForTesting, |
| checksumSupplier, |
| null); |
| } |
| |
| public static DexProgramClass createMockClassForTesting(DexItemFactory dexItemFactory) { |
| return new DexProgramClass( |
| dexItemFactory.createType("LMock;"), |
| null, |
| Origin.unknown(), |
| ClassAccessFlags.fromSharedAccessFlags(0), |
| dexItemFactory.objectType, |
| DexTypeList.empty(), |
| null, |
| null, |
| Collections.emptyList(), |
| Collections.emptyList(), |
| null, |
| Collections.emptyList(), |
| ClassSignature.noSignature(), |
| DexAnnotationSet.empty(), |
| DexEncodedField.EMPTY_ARRAY, |
| DexEncodedField.EMPTY_ARRAY, |
| MethodCollectionFactory.empty(), |
| false, |
| DexProgramClass::invalidChecksumRequest); |
| } |
| |
| @Override |
| public void accept( |
| Consumer<DexProgramClass> programClassConsumer, |
| Consumer<DexClasspathClass> classpathClassConsumer, |
| Consumer<DexLibraryClass> libraryClassConsumer) { |
| programClassConsumer.accept(this); |
| } |
| |
| @Override |
| public DexProgramClass self() { |
| return this; |
| } |
| |
| @Override |
| public DexProgramClass getContext() { |
| return this; |
| } |
| |
| /** |
| * Is the class reachability sensitive. |
| * |
| * <p>A class is reachability sensitive if the |
| * dalvik.annotation.optimization.ReachabilitySensitive annotation is on any field or method. When |
| * that is the case, dead reference elimination is disabled and locals are kept alive for their |
| * entire scope. |
| */ |
| public boolean getOrComputeReachabilitySensitive(AppView<?> appView) { |
| if (reachabilitySensitive.isUnknown()) { |
| reachabilitySensitive = OptionalBool.of(internalComputeReachabilitySensitive(appView)); |
| } |
| return reachabilitySensitive.isTrue(); |
| } |
| |
| private boolean internalComputeReachabilitySensitive(AppView<?> appView) { |
| DexItemFactory dexItemFactory = appView.dexItemFactory(); |
| for (DexEncodedMember<?, ?> member : members()) { |
| for (DexAnnotation annotation : member.annotations().annotations) { |
| if (annotation.annotation.type == dexItemFactory.annotationReachabilitySensitive) { |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| |
| @Override |
| public StructuralMapping<DexProgramClass> getStructuralMapping() { |
| return DexProgramClass::specify; |
| } |
| |
| public SyntheticMarker stripSyntheticInputMarker() { |
| SyntheticMarker marker = syntheticMarker; |
| // The synthetic input marker is "read once". It is stored only for identifying the input as |
| // synthetic and amending it to the SyntheticItems collection. After identification this field |
| // should not be used. |
| syntheticMarker = null; |
| return marker; |
| } |
| |
| private static void specify(StructuralSpecification<DexProgramClass, ?> spec) { |
| spec.withItem(c -> c.type) |
| .withItem(c -> c.superType) |
| .withItem(c -> c.interfaces) |
| .withItem(c -> c.accessFlags) |
| .withNullableItem(c -> c.sourceFile) |
| .withNullableItem(c -> c.initialClassFileVersion) |
| .withBool(c -> c.deprecated) |
| .withNullableItem(DexClass::getNestHostClassAttribute) |
| .withItemCollection(DexClass::getNestMembersClassAttributes) |
| .withItem(DexDefinition::annotations) |
| // TODO(b/158159959): Make signatures structural. |
| .withAssert(c -> c.classSignature == ClassSignature.noSignature()) |
| .withItemCollection(DexClass::allFieldsSorted) |
| .withItemCollection(DexClass::allMethodsSorted); |
| } |
| |
| public void forEachProgramField(Consumer<? super ProgramField> consumer) { |
| forEachProgramFieldMatching(alwaysTrue(), consumer); |
| } |
| |
| public void forEachProgramFieldMatching( |
| Predicate<? super DexEncodedField> predicate, Consumer<? super ProgramField> consumer) { |
| forEachFieldMatching(predicate, field -> consumer.accept(new ProgramField(this, field))); |
| } |
| |
| public void forEachProgramInstanceField(Consumer<? super ProgramField> consumer) { |
| forEachInstanceField(field -> consumer.accept(new ProgramField(this, field))); |
| } |
| |
| public void forEachProgramStaticField(Consumer<? super ProgramField> consumer) { |
| forEachStaticField(field -> consumer.accept(new ProgramField(this, field))); |
| } |
| |
| public void forEachProgramStaticMethod(Consumer<? super ProgramMethod> consumer) { |
| forEachProgramDirectMethodMatching(DexEncodedMethod::isStatic, consumer); |
| } |
| |
| public void forEachProgramMember(Consumer<? super ProgramMember<?, ?>> consumer) { |
| forEachProgramField(consumer); |
| forEachProgramMethod(consumer); |
| } |
| |
| public void forEachProgramMethod(Consumer<? super ProgramMethod> consumer) { |
| forEachProgramMethodMatching(alwaysTrue(), consumer); |
| } |
| |
| public void forEachProgramMethodMatching( |
| Predicate<DexEncodedMethod> predicate, Consumer<? super ProgramMethod> consumer) { |
| methodCollection.forEachMethodMatching( |
| predicate, method -> consumer.accept(new ProgramMethod(this, method))); |
| } |
| |
| public Iterable<ProgramMethod> programMethods() { |
| return Iterables.concat(directProgramMethods(), virtualProgramMethods()); |
| } |
| |
| public Iterable<ProgramMethod> directProgramMethods() { |
| return Iterables.transform(directMethods(), method -> new ProgramMethod(this, method)); |
| } |
| |
| public Iterable<ProgramMethod> directProgramMethods(Predicate<DexEncodedMethod> predicate) { |
| return Iterables.transform(directMethods(predicate), method -> new ProgramMethod(this, method)); |
| } |
| |
| public Iterable<ProgramMethod> virtualProgramMethods() { |
| return Iterables.transform(virtualMethods(), method -> new ProgramMethod(this, method)); |
| } |
| |
| public Iterable<ProgramMethod> virtualProgramMethods(Predicate<DexEncodedMethod> predicate) { |
| return Iterables.transform( |
| virtualMethods(predicate), method -> new ProgramMethod(this, method)); |
| } |
| |
| public Iterable<ProgramMethod> programInstanceInitializers() { |
| return directProgramMethods(DexEncodedMethod::isInstanceInitializer); |
| } |
| |
| public void forEachProgramDirectMethod(Consumer<ProgramMethod> consumer) { |
| forEachProgramDirectMethodMatching(alwaysTrue(), consumer); |
| } |
| |
| public void forEachProgramDirectMethodMatching( |
| Predicate<DexEncodedMethod> predicate, Consumer<? super ProgramMethod> consumer) { |
| methodCollection.forEachDirectMethodMatching( |
| predicate, method -> consumer.accept(new ProgramMethod(this, method))); |
| } |
| |
| public void forEachProgramInstanceInitializer(Consumer<ProgramMethod> consumer) { |
| forEachProgramInstanceInitializerMatching(alwaysTrue(), consumer); |
| } |
| |
| public void forEachProgramInstanceInitializerMatching( |
| Predicate<DexEncodedMethod> predicate, Consumer<ProgramMethod> consumer) { |
| forEachProgramDirectMethodMatching( |
| method -> method.isInstanceInitializer() && predicate.test(method), consumer); |
| } |
| |
| public void forEachProgramVirtualMethod(Consumer<ProgramMethod> consumer) { |
| forEachProgramVirtualMethodMatching(alwaysTrue(), consumer); |
| } |
| |
| public void forEachProgramVirtualMethodMatching( |
| Predicate<DexEncodedMethod> predicate, Consumer<ProgramMethod> consumer) { |
| methodCollection.forEachVirtualMethodMatching( |
| predicate, method -> consumer.accept(new ProgramMethod(this, method))); |
| } |
| |
| public ProgramMethod getProgramClassInitializer() { |
| return toProgramMethodOrNull(getClassInitializer()); |
| } |
| |
| public void acceptProgramClassInitializer(Consumer<ProgramMethod> consumer) { |
| if (hasClassInitializer()) { |
| consumer.accept(getProgramClassInitializer()); |
| } |
| } |
| |
| public ProgramMethod getProgramDefaultInitializer() { |
| return getProgramInitializer(DexType.EMPTY_ARRAY); |
| } |
| |
| public ProgramMethod getProgramInitializer(DexType[] types) { |
| return toProgramMethodOrNull(getInitializer(types)); |
| } |
| |
| /** Find member in this class matching {@param member}. */ |
| @SuppressWarnings("unchecked") |
| public <D extends DexEncodedMember<D, R>, R extends DexMember<D, R>> |
| ProgramMember<D, R> lookupProgramMember(DexMember<D, R> member) { |
| ProgramMember<?, ?> definition = |
| member.isDexField() |
| ? lookupProgramField(member.asDexField()) |
| : lookupProgramMethod(member.asDexMethod()); |
| return (ProgramMember<D, R>) definition; |
| } |
| |
| public ProgramField lookupProgramField(DexField reference) { |
| return toProgramFieldOrNull(lookupField(reference)); |
| } |
| |
| public ProgramMethod lookupProgramMethod(DexMethod reference) { |
| return toProgramMethodOrNull(getMethodCollection().getMethod(reference)); |
| } |
| |
| private ProgramField toProgramFieldOrNull(DexEncodedField field) { |
| if (field != null) { |
| return new ProgramField(this, field); |
| } |
| return null; |
| } |
| |
| private ProgramMethod toProgramMethodOrNull(DexEncodedMethod method) { |
| if (method != null) { |
| return new ProgramMethod(this, method); |
| } |
| return null; |
| } |
| |
| public TraversalContinuation<?, ?> traverseProgramMembers( |
| Function<ProgramMember<?, ?>, TraversalContinuation<?, ?>> fn) { |
| TraversalContinuation<?, ?> continuation = traverseProgramFields(fn); |
| if (continuation.shouldContinue()) { |
| return traverseProgramMethods(fn); |
| } |
| return TraversalContinuation.doBreak(); |
| } |
| |
| public TraversalContinuation<?, ?> traverseProgramFields( |
| Function<? super ProgramField, TraversalContinuation<?, ?>> fn) { |
| return traverseFields(field -> fn.apply(new ProgramField(this, field))); |
| } |
| |
| public TraversalContinuation<?, ?> traverseProgramMethods( |
| Function<? super ProgramMethod, TraversalContinuation<?, ?>> fn) { |
| return getMethodCollection().traverse(method -> fn.apply(new ProgramMethod(this, method))); |
| } |
| |
| public TraversalContinuation<?, ?> traverseProgramInstanceInitializers( |
| Function<ProgramMethod, TraversalContinuation<?, ?>> fn) { |
| return traverseProgramMethods(fn, DexEncodedMethod::isInstanceInitializer); |
| } |
| |
| public TraversalContinuation<?, ?> traverseProgramMethods( |
| Function<ProgramMethod, TraversalContinuation<?, ?>> fn, |
| Predicate<DexEncodedMethod> predicate) { |
| return getMethodCollection() |
| .traverse( |
| method -> |
| predicate.test(method) |
| ? fn.apply(new ProgramMethod(this, method)) |
| : TraversalContinuation.doContinue()); |
| } |
| |
| public Kind getOriginKind() { |
| return originKind; |
| } |
| |
| public boolean originatesFromDexResource() { |
| return originKind == Kind.DEX; |
| } |
| |
| public boolean originatesFromClassResource() { |
| return originKind == Kind.CF; |
| } |
| |
| public void collectIndexedItems( |
| AppView<?> appView, IndexedItemCollection indexedItems, LensCodeRewriterUtils rewriter) { |
| if (indexedItems.addClass(this)) { |
| type.collectIndexedItems(appView, indexedItems); |
| if (superType != null) { |
| superType.collectIndexedItems(appView, indexedItems); |
| } else { |
| assert type.toDescriptorString().equals("Ljava/lang/Object;"); |
| } |
| if (sourceFile != null) { |
| sourceFile.collectIndexedItems(indexedItems); |
| } |
| annotations().collectIndexedItems(appView, indexedItems); |
| if (interfaces != null) { |
| interfaces.collectIndexedItems(appView, indexedItems); |
| } |
| if (getEnclosingMethodAttribute() != null) { |
| getEnclosingMethodAttribute().collectIndexedItems(appView, indexedItems); |
| } |
| for (InnerClassAttribute attribute : getInnerClasses()) { |
| attribute.collectIndexedItems(appView, indexedItems); |
| } |
| // We are explicitly not adding items referenced in signatures. |
| forEachProgramField(field -> field.collectIndexedItems(appView, indexedItems)); |
| forEachProgramMethod(method -> method.collectIndexedItems(appView, indexedItems, rewriter)); |
| } |
| } |
| |
| @Override |
| void collectMixedSectionItems(MixedSectionCollection mixedItems) { |
| assert getEnclosingMethodAttribute() == null; |
| assert getInnerClasses().isEmpty(); |
| assert !classSignature.hasSignature(); |
| if (hasClassOrMemberAnnotations()) { |
| mixedItems.setAnnotationsDirectoryForClass(this, new DexAnnotationDirectory(this)); |
| } |
| } |
| |
| @Override |
| public void addDependencies(MixedSectionCollection collector) { |
| assert getEnclosingMethodAttribute() == null; |
| assert getInnerClasses().isEmpty(); |
| assert !classSignature.hasSignature(); |
| // We only have a class data item if there are methods or fields. |
| if (hasMethodsOrFields()) { |
| collector.add(this); |
| methodCollection.forEachMethod(m -> m.collectMixedSectionItems(collector)); |
| fieldCollection.forEachField(f -> f.collectMixedSectionItems(collector)); |
| } |
| annotations().collectMixedSectionItems(collector); |
| if (interfaces != null) { |
| interfaces.collectMixedSectionItems(collector); |
| } |
| } |
| |
| @Override |
| public String toString() { |
| return type.toString(); |
| } |
| |
| @Override |
| public String toSourceString() { |
| return type.toSourceString(); |
| } |
| |
| /** |
| * Returns true if this class is final, or it is a non-pinned program class with no instantiated |
| * subtypes. |
| */ |
| @Override |
| public boolean isEffectivelyFinal(AppView<?> appView) { |
| if (isFinal()) { |
| return true; |
| } |
| if (appView.hasLiveness()) { |
| assert appView.enableWholeProgramOptimizations(); |
| InternalOptions options = appView.options(); |
| return !appView.getKeepInfo(this).isPinned(options) |
| && !appView.appInfoWithLiveness().isInstantiatedIndirectly(this); |
| } |
| return false; |
| } |
| |
| @Override |
| public boolean isProgramClass() { |
| return true; |
| } |
| |
| @Override |
| public DexProgramClass asProgramClass() { |
| return this; |
| } |
| |
| public static DexProgramClass asProgramClassOrNull(DexClass clazz) { |
| return clazz != null ? clazz.asProgramClass() : null; |
| } |
| |
| @Override |
| public boolean isNotProgramClass() { |
| return false; |
| } |
| |
| @Override |
| public KotlinClassLevelInfo getKotlinInfo() { |
| return kotlinInfo; |
| } |
| |
| public void setKotlinInfo(KotlinClassLevelInfo kotlinInfo) { |
| assert kotlinInfo != null; |
| assert this.kotlinInfo == getNoKotlinInfo(); |
| this.kotlinInfo = kotlinInfo; |
| } |
| |
| public void clearKotlinInfo() { |
| this.kotlinInfo = getNoKotlinInfo(); |
| } |
| |
| @Override |
| boolean internalClassOrInterfaceMayHaveInitializationSideEffects( |
| AppView<?> appView, |
| DexClass initialAccessHolder, |
| Predicate<DexType> ignore, |
| Set<DexType> seen) { |
| if (!seen.add(getType()) || ignore.test(getType())) { |
| return false; |
| } |
| return isInterface() |
| ? internalInterfaceMayHaveInitializationSideEffects( |
| appView, initialAccessHolder, ignore, seen) |
| : internalClassMayHaveInitializationSideEffects(appView, initialAccessHolder, ignore, seen); |
| } |
| |
| private boolean internalClassMayHaveInitializationSideEffects( |
| AppView<?> appView, |
| DexClass initialAccessHolder, |
| Predicate<DexType> ignore, |
| Set<DexType> seen) { |
| assert !isInterface(); |
| assert seen.contains(getType()); |
| assert !ignore.test(getType()); |
| if (hasClassInitializer() |
| && !getClassInitializer().getOptimizationInfo().classInitializerMayBePostponed()) { |
| return true; |
| } |
| return defaultValuesForStaticFieldsMayTriggerAllocation() |
| || initializationOfParentTypesMayHaveSideEffects( |
| appView, initialAccessHolder, ignore, seen); |
| } |
| |
| /** |
| * Interface initialization is described the JVM Specification, section 5.5 Initialization (Java |
| * SE 11 Edition). |
| * |
| * <p>A class or interface C may be initialized only as a result of: |
| * |
| * <ul> |
| * <li>The execution of any one of the Java Virtual Machine instructions new, getstatic, |
| * putstatic, or invokestatic that references C. |
| * <li>... |
| * <li>If C is an interface that declares a non-abstract, non-static method, the initialization |
| * of a class that implements C directly or indirectly. |
| * </ul> |
| */ |
| private boolean internalInterfaceMayHaveInitializationSideEffects( |
| AppView<?> appView, |
| DexClass initialAccessHolder, |
| Predicate<DexType> ignore, |
| Set<DexType> seen) { |
| assert isInterface(); |
| assert seen.contains(getType()); |
| assert !ignore.test(getType()); |
| |
| // If there is a direct access to the interface, then this has side effects if its clinit has |
| // side effects. Parent types are not initialized and thus don't need to be considered. |
| if (this == initialAccessHolder) { |
| if (hasClassInitializer() |
| && !getClassInitializer().getOptimizationInfo().classInitializerMayBePostponed()) { |
| return true; |
| } |
| return defaultValuesForStaticFieldsMayTriggerAllocation(); |
| } |
| |
| // Otherwise, this interface has side effects if its clinit has side effects and it has at least |
| // one default interface method, or if one of its parent types have observable side effects. |
| if (hasClassInitializer() |
| && !getClassInitializer().getOptimizationInfo().classInitializerMayBePostponed() |
| && getMethodCollection().hasVirtualMethods(DexEncodedMethod::isDefaultMethod)) { |
| return true; |
| } |
| |
| return initializationOfParentTypesMayHaveSideEffects( |
| appView, initialAccessHolder, ignore, seen); |
| } |
| |
| private boolean initializationOfParentTypesMayHaveSideEffects( |
| AppView<?> appView, |
| DexClass initialAccessHolder, |
| Predicate<DexType> ignore, |
| Set<DexType> seen) { |
| if (superType != null |
| && superType.internalClassOrInterfaceMayHaveInitializationSideEffects( |
| appView, initialAccessHolder, ignore, seen)) { |
| return true; |
| } |
| for (DexType iface : interfaces) { |
| if (iface.internalClassOrInterfaceMayHaveInitializationSideEffects( |
| appView, initialAccessHolder, ignore, seen)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| public boolean hasFields() { |
| return fieldCollection.size() > 0; |
| } |
| |
| public boolean hasMethods() { |
| return methodCollection.size() > 0; |
| } |
| |
| public boolean hasMethodsOrFields() { |
| return hasMethods() || hasFields(); |
| } |
| |
| /** Determine if the class or any of its methods/fields has any attributes. */ |
| public boolean hasClassOrMemberAnnotations() { |
| return !annotations().isEmpty() |
| || hasAnnotations(methodCollection) |
| || hasAnnotations(fieldCollection); |
| } |
| |
| boolean hasOnlyInternalizableAnnotations() { |
| return !hasAnnotations(methodCollection) && !hasAnnotations(fieldCollection); |
| } |
| |
| private boolean hasAnnotations(FieldCollection fields) { |
| synchronized (fields) { |
| return fields.hasAnnotations(); |
| } |
| } |
| |
| private boolean hasAnnotations(MethodCollection methods) { |
| synchronized (methods) { |
| return methods.hasAnnotations(); |
| } |
| } |
| |
| public DexEncodedArray computeStaticValuesArray(NamingLens namingLens) { |
| // Fast path to avoid sorting and collection allocation when no non-default values exist. |
| if (!hasNonDefaultStaticFieldValues()) { |
| return null; |
| } |
| List<DexEncodedField> fields = new ArrayList<>(staticFields()); |
| fields.sort((a, b) -> a.getReference().compareToWithNamingLens(b.getReference(), namingLens)); |
| int length = 0; |
| List<DexValue> values = new ArrayList<>(fields.size()); |
| for (int i = 0; i < fields.size(); i++) { |
| DexEncodedField field = fields.get(i); |
| DexValue staticValue = field.getStaticValue(); |
| assert staticValue != null; |
| values.add(staticValue); |
| if (!staticValue.isDefault(field.getReference().type)) { |
| length = i + 1; |
| } |
| } |
| return length > 0 |
| ? new DexEncodedArray(values.subList(0, length).toArray(DexValue.EMPTY_ARRAY)) |
| : null; |
| } |
| |
| private boolean hasNonDefaultStaticFieldValues() { |
| for (DexEncodedField field : staticFields()) { |
| DexValue value = field.getStaticValue(); |
| if (value != null && !value.isDefault(field.getReference().type)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| public void addMethod(DexEncodedMethod method) { |
| methodCollection.addMethod(method); |
| } |
| |
| public void replaceVirtualMethod( |
| DexMethod virtualMethod, Function<DexEncodedMethod, DexEncodedMethod> replacement) { |
| methodCollection.replaceVirtualMethod(virtualMethod, replacement); |
| } |
| |
| public void addExtraInterfaces(List<ClassTypeSignature> extraInterfaces) { |
| if (extraInterfaces.isEmpty()) { |
| return; |
| } |
| addExtraInterfacesToInterfacesArray(extraInterfaces); |
| addExtraInterfacesToSignatureIfPresent(extraInterfaces); |
| } |
| |
| private void addExtraInterfacesToInterfacesArray(List<ClassTypeSignature> extraInterfaces) { |
| DexType[] newInterfaces = |
| Arrays.copyOf(interfaces.values, interfaces.size() + extraInterfaces.size()); |
| for (int i = interfaces.size(); i < newInterfaces.length; i++) { |
| newInterfaces[i] = extraInterfaces.get(i - interfaces.size()).type(); |
| } |
| interfaces = new DexTypeList(newInterfaces); |
| } |
| |
| private void addExtraInterfacesToSignatureIfPresent(List<ClassTypeSignature> extraInterfaces) { |
| // We introduce the extra interfaces to the generic signature. |
| if (classSignature.hasNoSignature() || extraInterfaces.isEmpty()) { |
| return; |
| } |
| ImmutableList.Builder<ClassTypeSignature> interfacesBuilder = |
| ImmutableList.<ClassTypeSignature>builder().addAll(classSignature.superInterfaceSignatures); |
| for (ClassTypeSignature extraInterface : extraInterfaces) { |
| interfacesBuilder.add(extraInterface); |
| } |
| classSignature = |
| new ClassSignature( |
| classSignature.formalTypeParameters, |
| classSignature.superClassSignature, |
| interfacesBuilder.build()); |
| } |
| |
| @Override |
| public DexProgramClass get() { |
| return this; |
| } |
| |
| @Override |
| public DexProgramClass getContextClass() { |
| return this; |
| } |
| |
| @Override |
| public DexType getContextType() { |
| return getType(); |
| } |
| |
| @Override |
| public DexProgramClass getDefinition() { |
| return this; |
| } |
| |
| public void setInitialClassFileVersion(CfVersion initialClassFileVersion) { |
| assert this.initialClassFileVersion == null; |
| assert initialClassFileVersion != null; |
| this.initialClassFileVersion = initialClassFileVersion; |
| } |
| |
| public void downgradeInitialClassFileVersion(CfVersion version) { |
| assert version != null; |
| this.initialClassFileVersion = Ordered.minIgnoreNull(this.initialClassFileVersion, version); |
| } |
| |
| public boolean hasClassFileVersion() { |
| return initialClassFileVersion != null; |
| } |
| |
| public CfVersion getInitialClassFileVersion() { |
| return initialClassFileVersion; |
| } |
| |
| public void setDeprecated() { |
| deprecated = true; |
| } |
| |
| public boolean isDeprecated() { |
| return deprecated; |
| } |
| |
| public static Iterable<DexProgramClass> asProgramClasses( |
| Iterable<DexType> types, DexDefinitionSupplier definitions) { |
| return () -> |
| new Iterator<DexProgramClass>() { |
| |
| private final Iterator<DexType> iterator = types.iterator(); |
| |
| private DexProgramClass next = findNext(); |
| |
| @Override |
| public boolean hasNext() { |
| return next != null; |
| } |
| |
| @Override |
| public DexProgramClass next() { |
| DexProgramClass current = next; |
| next = findNext(); |
| return current; |
| } |
| |
| private DexProgramClass findNext() { |
| while (iterator.hasNext()) { |
| DexType next = iterator.next(); |
| DexClass clazz = definitions.contextIndependentDefinitionFor(next); |
| if (clazz != null && clazz.isProgramClass()) { |
| return clazz.asProgramClass(); |
| } |
| } |
| return null; |
| } |
| }; |
| } |
| |
| public static long invalidChecksumRequest(DexProgramClass clazz) { |
| throw new CompilationError( |
| clazz + " has no checksum information while checksum encoding is requested", clazz.origin); |
| } |
| |
| public static long checksumFromType(DexProgramClass clazz) { |
| return clazz.type.hashCode(); |
| } |
| |
| public long getChecksum() { |
| return checksumSupplier.getChecksum(this); |
| } |
| |
| public ChecksumSupplier getChecksumSupplier() { |
| return checksumSupplier; |
| } |
| |
| } |