|  | // 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.google.common.base.Predicates.alwaysFalse; | 
|  | import static com.google.common.base.Predicates.alwaysTrue; | 
|  |  | 
|  | import com.android.tools.r8.dex.MixedSectionCollection; | 
|  | import com.android.tools.r8.errors.CompilationError; | 
|  | import com.android.tools.r8.errors.Unreachable; | 
|  | import com.android.tools.r8.graph.GenericSignature.ClassSignature; | 
|  | import com.android.tools.r8.graph.GenericSignature.ClassTypeSignature; | 
|  | import com.android.tools.r8.graph.GenericSignature.FieldTypeSignature; | 
|  | import com.android.tools.r8.graph.GenericSignature.FormalTypeParameter; | 
|  | import com.android.tools.r8.graph.MethodCollection.MethodCollectionFactory; | 
|  | import com.android.tools.r8.kotlin.KotlinClassLevelInfo; | 
|  | import com.android.tools.r8.origin.Origin; | 
|  | import com.android.tools.r8.references.ClassReference; | 
|  | import com.android.tools.r8.references.Reference; | 
|  | import com.android.tools.r8.utils.AndroidApiLevelUtils; | 
|  | import com.android.tools.r8.utils.InternalOptions; | 
|  | import com.android.tools.r8.utils.IterableUtils; | 
|  | import com.android.tools.r8.utils.OptionalBool; | 
|  | import com.android.tools.r8.utils.TraversalContinuation; | 
|  | import com.google.common.base.MoreObjects; | 
|  | import com.google.common.base.Predicates; | 
|  | import com.google.common.collect.ImmutableList; | 
|  | import com.google.common.collect.Iterables; | 
|  | import com.google.common.collect.Iterators; | 
|  | import com.google.common.collect.Sets; | 
|  | import java.util.ArrayList; | 
|  | import java.util.Arrays; | 
|  | import java.util.Collection; | 
|  | import java.util.Collections; | 
|  | import java.util.Iterator; | 
|  | import java.util.List; | 
|  | import java.util.ListIterator; | 
|  | import java.util.Set; | 
|  | import java.util.function.BiConsumer; | 
|  | import java.util.function.Consumer; | 
|  | import java.util.function.Function; | 
|  | import java.util.function.Predicate; | 
|  |  | 
|  | public abstract class DexClass extends DexDefinition | 
|  | implements ClassDefinition, ClassResolutionResult { | 
|  |  | 
|  | public interface FieldSetter { | 
|  | void setField(int index, DexEncodedField field); | 
|  | } | 
|  |  | 
|  | public final Origin origin; | 
|  | public final DexType type; | 
|  | public final ClassAccessFlags accessFlags; | 
|  | public DexType superType; | 
|  | public DexTypeList interfaces; | 
|  | public DexString sourceFile; | 
|  |  | 
|  | private OptionalBool isResolvable = OptionalBool.unknown(); | 
|  |  | 
|  | /** Access has to be synchronized during concurrent collection/writing phase. */ | 
|  | protected DexEncodedField[] staticFields = DexEncodedField.EMPTY_ARRAY; | 
|  |  | 
|  | /** Access has to be synchronized during concurrent collection/writing phase. */ | 
|  | protected DexEncodedField[] instanceFields = DexEncodedField.EMPTY_ARRAY; | 
|  |  | 
|  | /** Access has to be synchronized during concurrent collection/writing phase. */ | 
|  | protected final MethodCollection methodCollection; | 
|  |  | 
|  | /** Enclosing context of this class if it is an inner class, null otherwise. */ | 
|  | private EnclosingMethodAttribute enclosingMethod; | 
|  |  | 
|  | /** InnerClasses table. If this class is an inner class, it will have an entry here. */ | 
|  | private List<InnerClassAttribute> innerClasses; | 
|  |  | 
|  | /** | 
|  | * Nest attributes. If this class was compiled in JDK 11 and higher, and is in a nest, one of the | 
|  | * two attributes will be set. | 
|  | */ | 
|  | private NestHostClassAttribute nestHost; | 
|  |  | 
|  | private List<NestMemberClassAttribute> nestMembers; | 
|  |  | 
|  | private List<PermittedSubclassAttribute> permittedSubclasses; | 
|  |  | 
|  | /** Generic signature information if the attribute is present in the input */ | 
|  | protected ClassSignature classSignature; | 
|  |  | 
|  | public DexClass( | 
|  | DexString sourceFile, | 
|  | DexTypeList interfaces, | 
|  | ClassAccessFlags accessFlags, | 
|  | DexType superType, | 
|  | DexType type, | 
|  | DexEncodedField[] staticFields, | 
|  | DexEncodedField[] instanceFields, | 
|  | MethodCollectionFactory methodCollectionFactory, | 
|  | NestHostClassAttribute nestHost, | 
|  | List<NestMemberClassAttribute> nestMembers, | 
|  | List<PermittedSubclassAttribute> permittedSubclasses, | 
|  | EnclosingMethodAttribute enclosingMethod, | 
|  | List<InnerClassAttribute> innerClasses, | 
|  | ClassSignature classSignature, | 
|  | DexAnnotationSet annotations, | 
|  | Origin origin, | 
|  | boolean skipNameValidationForTesting) { | 
|  | super(annotations); | 
|  | assert origin != null; | 
|  | this.origin = origin; | 
|  | this.sourceFile = sourceFile; | 
|  | this.interfaces = interfaces; | 
|  | this.accessFlags = accessFlags; | 
|  | this.superType = superType; | 
|  | this.type = type; | 
|  | setStaticFields(staticFields); | 
|  | setInstanceFields(instanceFields); | 
|  | this.methodCollection = methodCollectionFactory.create(this); | 
|  | this.nestHost = nestHost; | 
|  | this.nestMembers = nestMembers; | 
|  | assert nestMembers != null; | 
|  | this.permittedSubclasses = permittedSubclasses; | 
|  | assert permittedSubclasses != null; | 
|  | this.enclosingMethod = enclosingMethod; | 
|  | this.innerClasses = innerClasses; | 
|  | assert classSignature != null; | 
|  | this.classSignature = classSignature; | 
|  | assert GenericSignatureUtils.verifyNoDuplicateGenericDefinitions(classSignature, annotations); | 
|  | if (type == superType) { | 
|  | throw new CompilationError("Class " + type.toString() + " cannot extend itself"); | 
|  | } | 
|  | for (DexType interfaceType : interfaces.values) { | 
|  | if (type == interfaceType) { | 
|  | throw new CompilationError("Interface " + type.toString() + " cannot implement itself"); | 
|  | } | 
|  | } | 
|  | if (!skipNameValidationForTesting && !type.descriptor.isValidClassDescriptor()) { | 
|  | throw new CompilationError( | 
|  | "Class descriptor '" | 
|  | + type.descriptor.toString() | 
|  | + "' cannot be represented in dex format."); | 
|  | } | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean hasClassResolutionResult() { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void forEachClassResolutionResult(Consumer<DexClass> consumer) { | 
|  | consumer.accept(this); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public DexClass toSingleClassWithProgramOverLibrary() { | 
|  | return this; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public DexClass toSingleClassWithLibraryOverProgram() { | 
|  | return this; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public DexClass toAlternativeClass() { | 
|  | return null; | 
|  | } | 
|  |  | 
|  | public abstract void accept( | 
|  | Consumer<DexProgramClass> programClassConsumer, | 
|  | Consumer<DexClasspathClass> classpathClassConsumer, | 
|  | Consumer<DexLibraryClass> libraryClassConsumer); | 
|  |  | 
|  | @Override | 
|  | public void forEachClassField(Consumer<? super DexClassAndField> consumer) { | 
|  | forEachClassFieldMatching(alwaysTrue(), consumer); | 
|  | } | 
|  |  | 
|  | public void forEachClassFieldMatching( | 
|  | Predicate<DexEncodedField> predicate, Consumer<? super DexClassAndField> consumer) { | 
|  | forEachFieldMatching(predicate, field -> consumer.accept(DexClassAndField.create(this, field))); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void forEachClassMethod(Consumer<? super DexClassAndMethod> consumer) { | 
|  | forEachClassMethodMatching(alwaysTrue(), consumer); | 
|  | } | 
|  |  | 
|  | public void forEachClassMethodMatching( | 
|  | Predicate<DexEncodedMethod> predicate, Consumer<? super DexClassAndMethod> consumer) { | 
|  | methodCollection.forEachMethodMatching( | 
|  | predicate, method -> consumer.accept(DexClassAndMethod.create(this, method))); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public ClassAccessFlags getAccessFlags() { | 
|  | return accessFlags; | 
|  | } | 
|  |  | 
|  | public DexTypeList getInterfaces() { | 
|  | return interfaces; | 
|  | } | 
|  |  | 
|  | public void setInterfaces(DexTypeList interfaces) { | 
|  | this.interfaces = interfaces; | 
|  | } | 
|  |  | 
|  | public DexString getSourceFile() { | 
|  | return sourceFile; | 
|  | } | 
|  |  | 
|  | public void setSourceFile(DexString sourceFile) { | 
|  | this.sourceFile = sourceFile; | 
|  | } | 
|  |  | 
|  | public Iterable<DexClassAndField> classFields() { | 
|  | return Iterables.transform(fields(), field -> DexClassAndField.create(this, field)); | 
|  | } | 
|  |  | 
|  | public Iterable<DexEncodedField> fields() { | 
|  | return fields(Predicates.alwaysTrue()); | 
|  | } | 
|  |  | 
|  | public Iterable<DexEncodedField> fields(final Predicate<? super DexEncodedField> predicate) { | 
|  | return Iterables.concat( | 
|  | Iterables.filter(Arrays.asList(instanceFields), predicate::test), | 
|  | Iterables.filter(Arrays.asList(staticFields), predicate::test)); | 
|  | } | 
|  |  | 
|  | public Iterable<DexEncodedMember<?, ?>> members() { | 
|  | return Iterables.concat(fields(), methods()); | 
|  | } | 
|  |  | 
|  | public Iterable<DexEncodedMember<?, ?>> members(Predicate<DexEncodedMember<?, ?>> predicate) { | 
|  | return Iterables.concat(fields(predicate), methods(predicate)); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public MethodCollection getMethodCollection() { | 
|  | return methodCollection; | 
|  | } | 
|  |  | 
|  | public Iterable<DexClassAndMethod> classMethods() { | 
|  | return Iterables.transform(methods(), method -> DexClassAndMethod.create(this, method)); | 
|  | } | 
|  |  | 
|  | public Iterable<DexEncodedMethod> methods() { | 
|  | return methodCollection.methods(); | 
|  | } | 
|  |  | 
|  | public Iterable<DexEncodedMethod> methods(Predicate<? super DexEncodedMethod> predicate) { | 
|  | return methodCollection.methods(predicate); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | void collectMixedSectionItems(MixedSectionCollection mixedItems) { | 
|  | throw new Unreachable(); | 
|  | } | 
|  |  | 
|  | public Iterable<DexEncodedMethod> directMethods() { | 
|  | return methodCollection.directMethods(); | 
|  | } | 
|  |  | 
|  | public Iterable<DexEncodedMethod> directMethods(Predicate<? super DexEncodedMethod> predicate) { | 
|  | return Iterables.filter(directMethods(), predicate::test); | 
|  | } | 
|  |  | 
|  | public void addDirectMethod(DexEncodedMethod method) { | 
|  | methodCollection.addDirectMethod(method); | 
|  | } | 
|  |  | 
|  | public void addDirectMethods(Collection<DexEncodedMethod> methods) { | 
|  | methodCollection.addDirectMethods(methods); | 
|  | } | 
|  |  | 
|  | public DexEncodedMethod removeMethod(DexMethod method) { | 
|  | return methodCollection.removeMethod(method); | 
|  | } | 
|  |  | 
|  | public void setDirectMethods(Collection<DexEncodedMethod> methods) { | 
|  | setDirectMethods(methods.toArray(DexEncodedMethod.EMPTY_ARRAY)); | 
|  | } | 
|  |  | 
|  | public void setDirectMethods(DexEncodedMethod[] methods) { | 
|  | methodCollection.setDirectMethods(methods); | 
|  | } | 
|  |  | 
|  | public Iterable<DexEncodedMethod> virtualMethods() { | 
|  | return methodCollection.virtualMethods(); | 
|  | } | 
|  |  | 
|  | public Iterable<DexEncodedMethod> virtualMethods(Predicate<? super DexEncodedMethod> predicate) { | 
|  | return Iterables.filter(virtualMethods(), predicate::test); | 
|  | } | 
|  |  | 
|  | public void addVirtualMethod(DexEncodedMethod method) { | 
|  | methodCollection.addVirtualMethod(method); | 
|  | } | 
|  |  | 
|  | public void addVirtualMethods(Collection<DexEncodedMethod> methods) { | 
|  | methodCollection.addVirtualMethods(methods); | 
|  | } | 
|  |  | 
|  | public void setVirtualMethods(List<DexEncodedMethod> methods) { | 
|  | setVirtualMethods(methods.toArray(DexEncodedMethod.EMPTY_ARRAY)); | 
|  | } | 
|  |  | 
|  | public void setVirtualMethods(DexEncodedMethod[] methods) { | 
|  | methodCollection.setVirtualMethods(methods); | 
|  | } | 
|  |  | 
|  | private boolean verifyNoAbstractMethodsOnNonAbstractClasses( | 
|  | Iterable<DexEncodedMethod> methods, InternalOptions options) { | 
|  | if (options.canHaveDalvikAbstractMethodOnNonAbstractClassVerificationBug() && !isAbstract()) { | 
|  | for (DexEncodedMethod method : methods) { | 
|  | assert !method.isAbstract() | 
|  | : "Non-abstract method on abstract class: `" | 
|  | + method.getReference().toSourceString() | 
|  | + "`"; | 
|  | } | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | public void forEachMethod(Consumer<DexEncodedMethod> consumer) { | 
|  | methodCollection.forEachMethod(consumer); | 
|  | } | 
|  |  | 
|  | public List<DexEncodedMethod> allMethodsSorted() { | 
|  | return methodCollection.allMethodsSorted(); | 
|  | } | 
|  |  | 
|  | public void virtualizeMethods(Set<DexEncodedMethod> privateInstanceMethods) { | 
|  | methodCollection.virtualizeMethods(privateInstanceMethods); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * For all annotations on the class and all annotations on its methods and fields apply the | 
|  | * specified consumer. | 
|  | */ | 
|  | public void forEachAnnotation(Consumer<DexAnnotation> consumer) { | 
|  | annotations().forEach(consumer); | 
|  | for (DexEncodedMethod method : methods()) { | 
|  | method.annotations().forEach(consumer); | 
|  | method.parameterAnnotationsList.forEachAnnotation(consumer); | 
|  | } | 
|  | for (DexEncodedField field : fields()) { | 
|  | field.annotations().forEach(consumer); | 
|  | } | 
|  | } | 
|  |  | 
|  | public void forEachField(Consumer<DexEncodedField> consumer) { | 
|  | forEachFieldMatching(alwaysTrue(), consumer); | 
|  | } | 
|  |  | 
|  | public void forEachFieldMatching( | 
|  | Predicate<? super DexEncodedField> predicate, Consumer<? super DexEncodedField> consumer) { | 
|  | fields(predicate).forEach(consumer); | 
|  | } | 
|  |  | 
|  | public void forEachInstanceField(Consumer<DexEncodedField> consumer) { | 
|  | forEachInstanceFieldMatching(alwaysTrue(), consumer); | 
|  | } | 
|  |  | 
|  | public void forEachInstanceFieldMatching( | 
|  | Predicate<DexEncodedField> predicate, Consumer<DexEncodedField> consumer) { | 
|  | instanceFields(predicate).forEach(consumer); | 
|  | } | 
|  |  | 
|  | public void forEachStaticField(Consumer<DexEncodedField> consumer) { | 
|  | forEachStaticFieldMatching(alwaysTrue(), consumer); | 
|  | } | 
|  |  | 
|  | public void forEachStaticFieldMatching( | 
|  | Predicate<DexEncodedField> predicate, Consumer<DexEncodedField> consumer) { | 
|  | staticFields(predicate).forEach(consumer); | 
|  | } | 
|  |  | 
|  | public TraversalContinuation<?, ?> traverseFields( | 
|  | Function<DexEncodedField, TraversalContinuation<?, ?>> fn) { | 
|  | for (DexEncodedField field : fields()) { | 
|  | if (fn.apply(field).shouldBreak()) { | 
|  | return TraversalContinuation.doBreak(); | 
|  | } | 
|  | } | 
|  | return TraversalContinuation.doContinue(); | 
|  | } | 
|  |  | 
|  | public List<DexEncodedField> staticFields() { | 
|  | assert staticFields != null; | 
|  | if (InternalOptions.assertionsEnabled()) { | 
|  | return Collections.unmodifiableList(Arrays.asList(staticFields)); | 
|  | } | 
|  | return Arrays.asList(staticFields); | 
|  | } | 
|  |  | 
|  | public Iterable<DexEncodedField> staticFields(Predicate<DexEncodedField> predicate) { | 
|  | return IterableUtils.filter(staticFields(), predicate); | 
|  | } | 
|  |  | 
|  | public void appendStaticField(DexEncodedField field) { | 
|  | DexEncodedField[] newFields = new DexEncodedField[staticFields.length + 1]; | 
|  | System.arraycopy(staticFields, 0, newFields, 0, staticFields.length); | 
|  | newFields[staticFields.length] = field; | 
|  | staticFields = newFields; | 
|  | assert verifyCorrectnessOfFieldHolder(field); | 
|  | assert verifyNoDuplicateFields(); | 
|  | } | 
|  |  | 
|  | public void appendStaticFields(Collection<DexEncodedField> fields) { | 
|  | DexEncodedField[] newFields = new DexEncodedField[staticFields.length + fields.size()]; | 
|  | System.arraycopy(staticFields, 0, newFields, 0, staticFields.length); | 
|  | int i = staticFields.length; | 
|  | for (DexEncodedField field : fields) { | 
|  | newFields[i] = field; | 
|  | i++; | 
|  | } | 
|  | staticFields = newFields; | 
|  | assert verifyCorrectnessOfFieldHolders(fields); | 
|  | assert verifyNoDuplicateFields(); | 
|  | } | 
|  |  | 
|  | public DexEncodedField[] clearStaticFields() { | 
|  | DexEncodedField[] previousFields = staticFields; | 
|  | setStaticFields(DexEncodedField.EMPTY_ARRAY); | 
|  | return previousFields; | 
|  | } | 
|  |  | 
|  | public void removeStaticField(int index) { | 
|  | DexEncodedField[] newFields = new DexEncodedField[staticFields.length - 1]; | 
|  | System.arraycopy(staticFields, 0, newFields, 0, index); | 
|  | System.arraycopy(staticFields, index + 1, newFields, index, staticFields.length - index - 1); | 
|  | staticFields = newFields; | 
|  | } | 
|  |  | 
|  | public void setStaticField(int index, DexEncodedField field) { | 
|  | staticFields[index] = field; | 
|  | assert verifyCorrectnessOfFieldHolder(field); | 
|  | assert verifyNoDuplicateFields(); | 
|  | } | 
|  |  | 
|  | public void setStaticFields(DexEncodedField[] fields) { | 
|  | staticFields = MoreObjects.firstNonNull(fields, DexEncodedField.EMPTY_ARRAY); | 
|  | assert verifyCorrectnessOfFieldHolders(staticFields()); | 
|  | assert verifyNoDuplicateFields(); | 
|  | } | 
|  |  | 
|  | public void setStaticFields(Collection<DexEncodedField> fields) { | 
|  | setStaticFields(fields.toArray(DexEncodedField.EMPTY_ARRAY)); | 
|  | } | 
|  |  | 
|  | public boolean definesStaticField(DexField field) { | 
|  | for (DexEncodedField encodedField : staticFields()) { | 
|  | if (encodedField.getReference() == field) { | 
|  | return true; | 
|  | } | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | public List<DexEncodedField> instanceFields() { | 
|  | assert instanceFields != null; | 
|  | if (InternalOptions.assertionsEnabled()) { | 
|  | return Collections.unmodifiableList(Arrays.asList(instanceFields)); | 
|  | } | 
|  | return Arrays.asList(instanceFields); | 
|  | } | 
|  |  | 
|  | public Iterable<DexEncodedField> instanceFields(Predicate<? super DexEncodedField> predicate) { | 
|  | return Iterables.filter(Arrays.asList(instanceFields), predicate::test); | 
|  | } | 
|  |  | 
|  | public void appendInstanceField(DexEncodedField field) { | 
|  | DexEncodedField[] newFields = new DexEncodedField[instanceFields.length + 1]; | 
|  | System.arraycopy(instanceFields, 0, newFields, 0, instanceFields.length); | 
|  | newFields[instanceFields.length] = field; | 
|  | instanceFields = newFields; | 
|  | assert verifyCorrectnessOfFieldHolder(field); | 
|  | assert verifyNoDuplicateFields(); | 
|  | } | 
|  |  | 
|  | public void appendInstanceFields(Collection<DexEncodedField> fields) { | 
|  | DexEncodedField[] newFields = new DexEncodedField[instanceFields.length + fields.size()]; | 
|  | System.arraycopy(instanceFields, 0, newFields, 0, instanceFields.length); | 
|  | int i = instanceFields.length; | 
|  | for (DexEncodedField field : fields) { | 
|  | newFields[i] = field; | 
|  | i++; | 
|  | } | 
|  | instanceFields = newFields; | 
|  | assert verifyCorrectnessOfFieldHolders(fields); | 
|  | assert verifyNoDuplicateFields(); | 
|  | } | 
|  |  | 
|  | public void removeInstanceField(int index) { | 
|  | DexEncodedField[] newFields = new DexEncodedField[instanceFields.length - 1]; | 
|  | System.arraycopy(instanceFields, 0, newFields, 0, index); | 
|  | System.arraycopy( | 
|  | instanceFields, index + 1, newFields, index, instanceFields.length - index - 1); | 
|  | instanceFields = newFields; | 
|  | } | 
|  |  | 
|  | public void setInstanceField(int index, DexEncodedField field) { | 
|  | instanceFields[index] = field; | 
|  | assert verifyCorrectnessOfFieldHolder(field); | 
|  | assert verifyNoDuplicateFields(); | 
|  | } | 
|  |  | 
|  | public void setInstanceFields(DexEncodedField[] fields) { | 
|  | instanceFields = MoreObjects.firstNonNull(fields, DexEncodedField.EMPTY_ARRAY); | 
|  | assert verifyCorrectnessOfFieldHolders(instanceFields()); | 
|  | assert verifyNoDuplicateFields(); | 
|  | } | 
|  |  | 
|  | public DexEncodedField[] clearInstanceFields() { | 
|  | DexEncodedField[] previousFields = instanceFields; | 
|  | instanceFields = DexEncodedField.EMPTY_ARRAY; | 
|  | return previousFields; | 
|  | } | 
|  |  | 
|  | private boolean verifyCorrectnessOfFieldHolder(DexEncodedField field) { | 
|  | assert field.getHolderType() == type | 
|  | : "Expected field `" | 
|  | + field.getReference().toSourceString() | 
|  | + "` to have holder `" | 
|  | + type.toSourceString() | 
|  | + "`"; | 
|  | return true; | 
|  | } | 
|  |  | 
|  | private boolean verifyCorrectnessOfFieldHolders(Iterable<DexEncodedField> fields) { | 
|  | for (DexEncodedField field : fields) { | 
|  | assert verifyCorrectnessOfFieldHolder(field); | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | private boolean verifyNoDuplicateFields() { | 
|  | Set<DexField> unique = Sets.newIdentityHashSet(); | 
|  | for (DexEncodedField field : fields()) { | 
|  | boolean changed = unique.add(field.getReference()); | 
|  | assert changed : "Duplicate field `" + field.getReference().toSourceString() + "`"; | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | /** Find static field in this class matching {@param field}. */ | 
|  | public DexEncodedField lookupStaticField(DexField field) { | 
|  | return lookupTarget(staticFields, field); | 
|  | } | 
|  |  | 
|  | /** Find instance field in this class matching {@param field}. */ | 
|  | public DexEncodedField lookupInstanceField(DexField field) { | 
|  | return lookupTarget(instanceFields, field); | 
|  | } | 
|  |  | 
|  | public DexEncodedField lookupUniqueInstanceFieldWithName(DexString name) { | 
|  | return internalLookupUniqueFieldThatMatches(field -> field.getName() == name, instanceFields()); | 
|  | } | 
|  |  | 
|  | public DexEncodedField lookupUniqueStaticFieldWithName(DexString name) { | 
|  | return internalLookupUniqueFieldThatMatches(field -> field.getName() == name, staticFields()); | 
|  | } | 
|  |  | 
|  | private static DexEncodedField internalLookupUniqueFieldThatMatches( | 
|  | Predicate<DexEncodedField> predicate, List<DexEncodedField> fields) { | 
|  | DexEncodedField result = null; | 
|  | for (DexEncodedField field : fields) { | 
|  | if (predicate.test(field)) { | 
|  | if (result != null) { | 
|  | return null; | 
|  | } | 
|  | result = field; | 
|  | } | 
|  | } | 
|  | return result; | 
|  | } | 
|  |  | 
|  | /** Find method in this class matching {@param method}. */ | 
|  | public DexClassAndField lookupClassField(DexField field) { | 
|  | return toClassFieldOrNull(lookupField(field)); | 
|  | } | 
|  |  | 
|  | private DexClassAndField toClassFieldOrNull(DexEncodedField field) { | 
|  | return field != null ? DexClassAndField.create(this, field) : null; | 
|  | } | 
|  |  | 
|  | /** Find field in this class matching {@param field}. */ | 
|  | public DexEncodedField lookupField(DexField field) { | 
|  | DexEncodedField result = lookupInstanceField(field); | 
|  | return result == null ? lookupStaticField(field) : result; | 
|  | } | 
|  |  | 
|  | /** Find direct method in this class matching {@param method}. */ | 
|  | public DexEncodedMethod lookupDirectMethod(DexMethod method) { | 
|  | return methodCollection.getDirectMethod(method); | 
|  | } | 
|  |  | 
|  | /** Find direct method in this class matching {@param predicate}. */ | 
|  | public DexEncodedMethod lookupDirectMethod(Predicate<DexEncodedMethod> predicate) { | 
|  | return methodCollection.getDirectMethod(predicate); | 
|  | } | 
|  |  | 
|  | /** Find virtual method in this class matching {@param method}. */ | 
|  | public DexEncodedMethod lookupVirtualMethod(DexMethod method) { | 
|  | return methodCollection.getVirtualMethod(method); | 
|  | } | 
|  |  | 
|  | /** Find virtual method in this class matching {@param predicate}. */ | 
|  | public DexEncodedMethod lookupVirtualMethod(Predicate<DexEncodedMethod> predicate) { | 
|  | return methodCollection.getVirtualMethod(predicate); | 
|  | } | 
|  |  | 
|  | /** Find member in this class matching {@param member}. */ | 
|  | @SuppressWarnings("unchecked") | 
|  | public <D extends DexEncodedMember<D, R>, R extends DexMember<D, R>> D lookupMember( | 
|  | DexMember<D, R> member) { | 
|  | DexEncodedMember<?, ?> definition = | 
|  | member.isDexField() ? lookupField(member.asDexField()) : lookupMethod(member.asDexMethod()); | 
|  | return (D) definition; | 
|  | } | 
|  |  | 
|  | /** Find method in this class matching {@param method}. */ | 
|  | public DexClassAndMethod lookupClassMethod(DexMethod method) { | 
|  | return toClassMethodOrNull(methodCollection.getMethod(method)); | 
|  | } | 
|  |  | 
|  | private DexClassAndMethod toClassMethodOrNull(DexEncodedMethod method) { | 
|  | return method != null ? DexClassAndMethod.create(this, method) : null; | 
|  | } | 
|  |  | 
|  | /** Find method in this class matching {@param method}. */ | 
|  | public DexEncodedMethod lookupMethod(DexMethod method) { | 
|  | return methodCollection.getMethod(method); | 
|  | } | 
|  |  | 
|  | public DexEncodedMethod lookupMethod(DexProto methodProto, DexString methodName) { | 
|  | return methodCollection.getMethod(methodProto, methodName); | 
|  | } | 
|  |  | 
|  | /** Find method in this class matching {@param method}. */ | 
|  | public DexEncodedMethod lookupMethod(Predicate<DexEncodedMethod> predicate) { | 
|  | return methodCollection.getMethod(predicate); | 
|  | } | 
|  |  | 
|  | public DexEncodedMethod lookupSignaturePolymorphicMethod( | 
|  | DexString methodName, DexItemFactory factory) { | 
|  | if (type != factory.methodHandleType && type != factory.varHandleType) { | 
|  | return null; | 
|  | } | 
|  | DexEncodedMethod matchingName = null; | 
|  | DexEncodedMethod signaturePolymorphicMethod = null; | 
|  | for (DexEncodedMethod method : virtualMethods()) { | 
|  | if (method.getReference().name == methodName) { | 
|  | if (matchingName != null) { | 
|  | // The jvm spec, section 5.4.3.3 details that there must be exactly one method with the | 
|  | // given name only. | 
|  | return null; | 
|  | } | 
|  | matchingName = method; | 
|  | if (isSignaturePolymorphicMethod(method, factory)) { | 
|  | signaturePolymorphicMethod = method; | 
|  | } | 
|  | } | 
|  | } | 
|  | return signaturePolymorphicMethod; | 
|  | } | 
|  |  | 
|  | public static boolean isSignaturePolymorphicMethod( | 
|  | DexEncodedMethod method, DexItemFactory factory) { | 
|  | assert method.getHolderType() == factory.methodHandleType | 
|  | || method.getHolderType() == factory.varHandleType; | 
|  | return method.accessFlags.isVarargs() | 
|  | && method.accessFlags.isNative() | 
|  | && method.getReference().proto.parameters.size() == 1 | 
|  | && method.getReference().proto.parameters.values[0] == factory.objectArrayType; | 
|  | } | 
|  |  | 
|  | private <D extends DexEncodedMember<D, R>, R extends DexMember<D, R>> D lookupTarget( | 
|  | D[] items, R descriptor) { | 
|  | for (D entry : items) { | 
|  | if (descriptor.match(entry)) { | 
|  | return entry; | 
|  | } | 
|  | } | 
|  | return null; | 
|  | } | 
|  |  | 
|  | public boolean canBeInstantiatedByNewInstance() { | 
|  | return !isAbstract() && !isAnnotation() && !isInterface(); | 
|  | } | 
|  |  | 
|  | public boolean isAbstract() { | 
|  | return accessFlags.isAbstract(); | 
|  | } | 
|  |  | 
|  | public boolean isAnnotation() { | 
|  | return accessFlags.isAnnotation(); | 
|  | } | 
|  |  | 
|  | public boolean isFinal() { | 
|  | return accessFlags.isFinal(); | 
|  | } | 
|  |  | 
|  | public boolean isEffectivelyFinal(AppView<?> appView) { | 
|  | return isFinal(); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean isInterface() { | 
|  | return accessFlags.isInterface(); | 
|  | } | 
|  |  | 
|  | public boolean isEnum() { | 
|  | return accessFlags.isEnum(); | 
|  | } | 
|  |  | 
|  | public boolean isRecord() { | 
|  | return accessFlags.isRecord(); | 
|  | } | 
|  |  | 
|  | public abstract void addDependencies(MixedSectionCollection collector); | 
|  |  | 
|  | @Override | 
|  | public DexReference getReference() { | 
|  | return getType(); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public DexClass asClass() { | 
|  | return this; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean isDexClass() { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public DexClass asDexClass() { | 
|  | return this; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean isClasspathClass() { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public DexClasspathClass asClasspathClass() { | 
|  | return null; | 
|  | } | 
|  |  | 
|  | public abstract boolean isNotProgramClass(); | 
|  |  | 
|  | @Override | 
|  | public boolean isLibraryClass() { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public DexLibraryClass asLibraryClass() { | 
|  | return null; | 
|  | } | 
|  |  | 
|  | public boolean isPrivate() { | 
|  | return accessFlags.isPrivate(); | 
|  | } | 
|  |  | 
|  | public boolean isPublic() { | 
|  | return accessFlags.isPublic(); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean isStatic() { | 
|  | return accessFlags.isStatic(); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean isStaticMember() { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | public DexEncodedMethod getClassInitializer() { | 
|  | DexEncodedMethod classInitializer = methodCollection.getClassInitializer(); | 
|  | assert classInitializer != DexEncodedMethod.SENTINEL; | 
|  | return classInitializer; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public ClassReference getClassReference() { | 
|  | return Reference.classFromDescriptor(getType().toDescriptorString()); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public DexClass getContextClass() { | 
|  | return this; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public DexClass getDefinition() { | 
|  | return this; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public Origin getOrigin() { | 
|  | return this.origin; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public DexType getType() { | 
|  | return type; | 
|  | } | 
|  |  | 
|  | public boolean hasSuperType() { | 
|  | return superType != null; | 
|  | } | 
|  |  | 
|  | public DexType getSuperType() { | 
|  | return superType; | 
|  | } | 
|  |  | 
|  | public boolean hasClassInitializer() { | 
|  | return getClassInitializer() != null; | 
|  | } | 
|  |  | 
|  | public boolean hasDefaultInitializer() { | 
|  | return getDefaultInitializer() != null; | 
|  | } | 
|  |  | 
|  | public DexEncodedMethod getInitializer(DexType[] parameters) { | 
|  | for (DexEncodedMethod method : directMethods()) { | 
|  | if (method.isInstanceInitializer() | 
|  | && Arrays.equals(method.getReference().proto.parameters.values, parameters)) { | 
|  | return method; | 
|  | } | 
|  | } | 
|  | return null; | 
|  | } | 
|  |  | 
|  | public DexEncodedMethod getDefaultInitializer() { | 
|  | return getInitializer(DexType.EMPTY_ARRAY); | 
|  | } | 
|  |  | 
|  | public boolean hasMissingSuperType(AppInfoWithClassHierarchy appInfo) { | 
|  | if (superType != null && appInfo.isMissingOrHasMissingSuperType(superType)) { | 
|  | return true; | 
|  | } | 
|  | for (DexType interfaceType : interfaces.values) { | 
|  | if (appInfo.isMissingOrHasMissingSuperType(interfaceType)) { | 
|  | return true; | 
|  | } | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | public boolean isResolvable(AppView<?> appView) { | 
|  | if (isResolvable.isUnknown()) { | 
|  | boolean resolvable; | 
|  | if (isLibraryClass()) { | 
|  | resolvable = AndroidApiLevelUtils.isApiSafeForReference(asLibraryClass(), appView); | 
|  | } else { | 
|  | resolvable = true; | 
|  | for (DexType supertype : allImmediateSupertypes()) { | 
|  | resolvable &= supertype.isResolvable(appView); | 
|  | if (!resolvable) { | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  | isResolvable = OptionalBool.of(resolvable); | 
|  | } | 
|  | assert !isResolvable.isUnknown(); | 
|  | return isResolvable.isTrue(); | 
|  | } | 
|  |  | 
|  | public boolean isSerializable(AppView<? extends AppInfoWithClassHierarchy> appView) { | 
|  | return appView.appInfo().isSerializable(type); | 
|  | } | 
|  |  | 
|  | public boolean isExternalizable(AppView<? extends AppInfoWithClassHierarchy> appView) { | 
|  | return appView.appInfo().isExternalizable(type); | 
|  | } | 
|  |  | 
|  | public boolean classInitializationMayHaveSideEffects(AppView<?> appView) { | 
|  | return classInitializationMayHaveSideEffects(appView, alwaysFalse()); | 
|  | } | 
|  |  | 
|  | public boolean classInitializationMayHaveSideEffects( | 
|  | AppView<?> appView, Predicate<DexType> ignore) { | 
|  | return internalClassOrInterfaceMayHaveInitializationSideEffects( | 
|  | appView, this, ignore, Sets.newIdentityHashSet()); | 
|  | } | 
|  |  | 
|  | public final boolean classInitializationMayHaveSideEffectsInContext( | 
|  | AppView<?> appView, ProgramDefinition context) { | 
|  | // Types that are a super type of the current context are guaranteed to be initialized already. | 
|  | return classInitializationMayHaveSideEffects( | 
|  | appView, type -> appView.isSubtype(context.getContextType(), type).isTrue()); | 
|  | } | 
|  |  | 
|  | abstract boolean internalClassOrInterfaceMayHaveInitializationSideEffects( | 
|  | AppView<?> appView, | 
|  | DexClass initialAccessHolder, | 
|  | Predicate<DexType> ignore, | 
|  | Set<DexType> seen); | 
|  |  | 
|  | public void forEachImmediateInterface(Consumer<DexType> fn) { | 
|  | for (DexType iface : interfaces.values) { | 
|  | fn.accept(iface); | 
|  | } | 
|  | } | 
|  |  | 
|  | public void forEachImmediateSupertype(Consumer<DexType> fn) { | 
|  | if (superType != null) { | 
|  | fn.accept(superType); | 
|  | } | 
|  | forEachImmediateInterface(fn); | 
|  | } | 
|  |  | 
|  | public void forEachImmediateSupertype(BiConsumer<DexType, Boolean> fn) { | 
|  | if (superType != null) { | 
|  | fn.accept(superType, false); | 
|  | } | 
|  | forEachImmediateInterface(iface -> fn.accept(iface, true)); | 
|  | } | 
|  |  | 
|  | public boolean validInterfaceSignatures() { | 
|  | return getClassSignature().superInterfaceSignatures().isEmpty() | 
|  | || interfaces.values.length == getClassSignature().superInterfaceSignatures.size(); | 
|  | } | 
|  |  | 
|  | public void forEachImmediateInterfaceWithSignature( | 
|  | BiConsumer<DexType, ClassTypeSignature> consumer) { | 
|  | assert validInterfaceSignatures(); | 
|  |  | 
|  | // If there is no generic signature information don't pass any type arguments. | 
|  | if (getClassSignature().superInterfaceSignatures().isEmpty()) { | 
|  | forEachImmediateInterface( | 
|  | superInterface -> | 
|  | consumer.accept(superInterface, new ClassTypeSignature(superInterface))); | 
|  | return; | 
|  | } | 
|  |  | 
|  | Iterator<DexType> interfaceIterator = Arrays.asList(interfaces.values).iterator(); | 
|  | Iterator<ClassTypeSignature> interfaceSignatureIterator = | 
|  | getClassSignature().superInterfaceSignatures().iterator(); | 
|  |  | 
|  | while (interfaceIterator.hasNext()) { | 
|  | assert interfaceSignatureIterator.hasNext(); | 
|  | DexType superInterface = interfaceIterator.next(); | 
|  | ClassTypeSignature superInterfaceSignatures = interfaceSignatureIterator.next(); | 
|  | consumer.accept(superInterface, superInterfaceSignatures); | 
|  | } | 
|  | } | 
|  |  | 
|  | public void forEachImmediateSupertypeWithSignature( | 
|  | BiConsumer<DexType, ClassTypeSignature> consumer) { | 
|  | if (superType != null) { | 
|  | consumer.accept(superType, classSignature.superClassSignature); | 
|  | } | 
|  | forEachImmediateInterfaceWithSignature(consumer); | 
|  | } | 
|  |  | 
|  | public void forEachImmediateInterfaceWithAppliedTypeArguments( | 
|  | List<FieldTypeSignature> typeArguments, | 
|  | BiConsumer<DexType, List<FieldTypeSignature>> consumer) { | 
|  | assert validInterfaceSignatures(); | 
|  |  | 
|  | // If there is no generic signature information don't pass any type arguments. | 
|  | if (getClassSignature().superInterfaceSignatures().size() == 0) { | 
|  | forEachImmediateInterface( | 
|  | superInterface -> consumer.accept(superInterface, ImmutableList.of())); | 
|  | return; | 
|  | } | 
|  |  | 
|  | Iterator<DexType> interfaceIterator = Arrays.asList(interfaces.values).iterator(); | 
|  | Iterator<ClassTypeSignature> interfaceSignatureIterator = | 
|  | getClassSignature().superInterfaceSignatures().iterator(); | 
|  |  | 
|  | while (interfaceIterator.hasNext()) { | 
|  | assert interfaceSignatureIterator.hasNext(); | 
|  | DexType superInterface = interfaceIterator.next(); | 
|  | ClassTypeSignature superInterfaceSignatures = interfaceSignatureIterator.next(); | 
|  |  | 
|  | // With no type arguments erase the signatures. | 
|  | if (typeArguments.isEmpty() && superInterfaceSignatures.hasTypeVariableArguments()) { | 
|  | consumer.accept(superInterface, ImmutableList.of()); | 
|  | continue; | 
|  | } | 
|  |  | 
|  | consumer.accept(superInterface, applyTypeArguments(superInterfaceSignatures, typeArguments)); | 
|  | } | 
|  | assert !interfaceSignatureIterator.hasNext(); | 
|  | } | 
|  |  | 
|  | public void forEachImmediateSupertypeWithAppliedTypeArguments( | 
|  | List<FieldTypeSignature> typeArguments, | 
|  | BiConsumer<DexType, List<FieldTypeSignature>> consumer) { | 
|  | if (superType != null) { | 
|  | consumer.accept( | 
|  | superType, applyTypeArguments(getClassSignature().superClassSignature, typeArguments)); | 
|  | } | 
|  | forEachImmediateInterfaceWithAppliedTypeArguments(typeArguments, consumer); | 
|  | } | 
|  |  | 
|  | private List<FieldTypeSignature> applyTypeArguments( | 
|  | ClassTypeSignature superInterfaceSignatures, List<FieldTypeSignature> appliedTypeArguments) { | 
|  | ImmutableList.Builder<FieldTypeSignature> superTypeArgumentsBuilder = ImmutableList.builder(); | 
|  | if (superInterfaceSignatures.type.toSourceString().equals("java.util.Map")) { | 
|  | System.currentTimeMillis(); | 
|  | } | 
|  | superInterfaceSignatures | 
|  | .typeArguments() | 
|  | .forEach( | 
|  | typeArgument -> { | 
|  | if (typeArgument.isTypeVariableSignature()) { | 
|  | for (int i = 0; i < getClassSignature().getFormalTypeParameters().size(); i++) { | 
|  | FormalTypeParameter formalTypeParameter = | 
|  | getClassSignature().getFormalTypeParameters().get(i); | 
|  | if (formalTypeParameter | 
|  | .getName() | 
|  | .equals(typeArgument.asTypeVariableSignature().typeVariable())) { | 
|  | if (i >= appliedTypeArguments.size()) { | 
|  | assert false; | 
|  | } else { | 
|  | superTypeArgumentsBuilder.add(appliedTypeArguments.get(i)); | 
|  | } | 
|  | } | 
|  | } | 
|  | } else { | 
|  | superTypeArgumentsBuilder.add(typeArgument); | 
|  | } | 
|  | }); | 
|  | return superTypeArgumentsBuilder.build(); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public Iterable<DexType> allImmediateSupertypes() { | 
|  | Iterator<DexType> iterator = | 
|  | superType != null | 
|  | ? Iterators.concat( | 
|  | Iterators.singletonIterator(superType), Iterators.forArray(interfaces.values)) | 
|  | : Iterators.forArray(interfaces.values); | 
|  | return () -> iterator; | 
|  | } | 
|  |  | 
|  | public boolean definesFinalizer(DexItemFactory factory) { | 
|  | return lookupVirtualMethod(factory.objectMembers.finalize) != null; | 
|  | } | 
|  |  | 
|  | public boolean defaultValuesForStaticFieldsMayTriggerAllocation() { | 
|  | return staticFields().stream() | 
|  | .anyMatch( | 
|  | field -> field.hasExplicitStaticValue() && field.getStaticValue().mayHaveSideEffects()); | 
|  | } | 
|  |  | 
|  | public List<InnerClassAttribute> getInnerClasses() { | 
|  | return innerClasses; | 
|  | } | 
|  |  | 
|  | public void setInnerClasses(List<InnerClassAttribute> innerClasses) { | 
|  | this.innerClasses = innerClasses; | 
|  | } | 
|  |  | 
|  | public boolean hasEnclosingMethodAttribute() { | 
|  | return enclosingMethod != null; | 
|  | } | 
|  |  | 
|  | public EnclosingMethodAttribute getEnclosingMethodAttribute() { | 
|  | return enclosingMethod; | 
|  | } | 
|  |  | 
|  | public void setEnclosingMethodAttribute(EnclosingMethodAttribute enclosingMethod) { | 
|  | this.enclosingMethod = enclosingMethod; | 
|  | } | 
|  |  | 
|  | public void clearEnclosingMethodAttribute() { | 
|  | enclosingMethod = null; | 
|  | } | 
|  |  | 
|  | public void removeEnclosingMethodAttribute(Predicate<EnclosingMethodAttribute> predicate) { | 
|  | if (enclosingMethod != null && predicate.test(enclosingMethod)) { | 
|  | enclosingMethod = null; | 
|  | } | 
|  | } | 
|  |  | 
|  | public void clearInnerClasses() { | 
|  | innerClasses.clear(); | 
|  | } | 
|  |  | 
|  | public void clearClassSignature() { | 
|  | classSignature = ClassSignature.noSignature(); | 
|  | } | 
|  |  | 
|  | public void removeInnerClasses(Predicate<InnerClassAttribute> predicate) { | 
|  | innerClasses.removeIf(predicate); | 
|  | } | 
|  |  | 
|  | public InnerClassAttribute getInnerClassAttributeForThisClass() { | 
|  | for (InnerClassAttribute innerClassAttribute : getInnerClasses()) { | 
|  | if (type == innerClassAttribute.getInner()) { | 
|  | return innerClassAttribute; | 
|  | } | 
|  | } | 
|  | return null; | 
|  | } | 
|  |  | 
|  | public void replaceInnerClassAttributeForThisClass(InnerClassAttribute newInnerClassAttribute) { | 
|  | ListIterator<InnerClassAttribute> iterator = getInnerClasses().listIterator(); | 
|  | while (iterator.hasNext()) { | 
|  | InnerClassAttribute innerClassAttribute = iterator.next(); | 
|  | if (type == innerClassAttribute.getInner()) { | 
|  | iterator.set(newInnerClassAttribute); | 
|  | return; | 
|  | } | 
|  | } | 
|  | throw new Unreachable(); | 
|  | } | 
|  |  | 
|  | public ClassSignature getClassSignature() { | 
|  | return classSignature; | 
|  | } | 
|  |  | 
|  | public void setClassSignature(ClassSignature classSignature) { | 
|  | this.classSignature = classSignature; | 
|  | } | 
|  |  | 
|  | public void clearPermittedSubclasses() { | 
|  | permittedSubclasses.clear(); | 
|  | } | 
|  |  | 
|  | public boolean isLocalClass() { | 
|  | InnerClassAttribute innerClass = getInnerClassAttributeForThisClass(); | 
|  | // The corresponding enclosing-method attribute might be not available, e.g., CF version 50. | 
|  | return innerClass != null && innerClass.getOuter() == null && innerClass.isNamed(); | 
|  | } | 
|  |  | 
|  | public boolean isMemberClass() { | 
|  | InnerClassAttribute innerClass = getInnerClassAttributeForThisClass(); | 
|  | boolean isMember = innerClass != null && innerClass.getOuter() != null && innerClass.isNamed(); | 
|  | assert !isMember || getEnclosingMethodAttribute() == null; | 
|  | return isMember; | 
|  | } | 
|  |  | 
|  | public boolean isAnonymousClass() { | 
|  | InnerClassAttribute innerClass = getInnerClassAttributeForThisClass(); | 
|  | // The corresponding enclosing-method attribute might be not available, e.g., CF version 50. | 
|  | // We can't rely on outer type either because it's not null prior to 51 and null since 51. | 
|  | return innerClass != null && innerClass.isAnonymous(); | 
|  | } | 
|  |  | 
|  | public boolean isInANest() { | 
|  | return isNestHost() || isNestMember(); | 
|  | } | 
|  |  | 
|  | public void clearNestHost() { | 
|  | nestHost = null; | 
|  | } | 
|  |  | 
|  | public void clearNestMembers() { | 
|  | nestMembers.clear(); | 
|  | } | 
|  |  | 
|  | public void setNestHost(DexType type) { | 
|  | assert type != null; | 
|  | this.nestHost = new NestHostClassAttribute(type); | 
|  | } | 
|  |  | 
|  | public void setNestHostAttribute(NestHostClassAttribute nestHostAttribute) { | 
|  | this.nestHost = nestHostAttribute; | 
|  | } | 
|  |  | 
|  | public boolean isNestHost() { | 
|  | return !nestMembers.isEmpty(); | 
|  | } | 
|  |  | 
|  | public boolean isNestMember() { | 
|  | return nestHost != null; | 
|  | } | 
|  |  | 
|  | public DexType getNestHost() { | 
|  | if (isNestMember()) { | 
|  | return nestHost.getNestHost(); | 
|  | } | 
|  | if (isNestHost()) { | 
|  | return type; | 
|  | } | 
|  | return null; | 
|  | } | 
|  |  | 
|  | public void forEachNestMember(Consumer<DexType> consumer) { | 
|  | assert isNestHost(); | 
|  | getNestMembersClassAttributes().forEach(member -> consumer.accept(member.getNestMember())); | 
|  | } | 
|  |  | 
|  | public NestHostClassAttribute getNestHostClassAttribute() { | 
|  | return nestHost; | 
|  | } | 
|  |  | 
|  | public boolean hasNestMemberAttributes() { | 
|  | return !nestMembers.isEmpty(); | 
|  | } | 
|  |  | 
|  | public List<NestMemberClassAttribute> getNestMembersClassAttributes() { | 
|  | return nestMembers; | 
|  | } | 
|  |  | 
|  | public void setNestMemberAttributes(List<NestMemberClassAttribute> nestMemberAttributes) { | 
|  | this.nestMembers = nestMemberAttributes; | 
|  | } | 
|  |  | 
|  | public void removeNestMemberAttributes(Predicate<NestMemberClassAttribute> predicate) { | 
|  | nestMembers.removeIf(predicate); | 
|  | } | 
|  |  | 
|  | public boolean hasPermittedSubclassAttributes() { | 
|  | return !permittedSubclasses.isEmpty(); | 
|  | } | 
|  |  | 
|  | public List<PermittedSubclassAttribute> getPermittedSubclassAttributes() { | 
|  | return permittedSubclasses; | 
|  | } | 
|  |  | 
|  | /** Returns kotlin class info if the class is synthesized by kotlin compiler. */ | 
|  | public abstract KotlinClassLevelInfo getKotlinInfo(); | 
|  |  | 
|  | public final String getSimpleName() { | 
|  | return getType().getSimpleName(); | 
|  | } | 
|  |  | 
|  | public final String getTypeName() { | 
|  | return getType().getTypeName(); | 
|  | } | 
|  |  | 
|  | public boolean hasStaticFields() { | 
|  | return staticFields.length > 0; | 
|  | } | 
|  |  | 
|  | public boolean hasInstanceFields() { | 
|  | return instanceFields.length > 0; | 
|  | } | 
|  |  | 
|  | public boolean hasInstanceFieldsDirectlyOrIndirectly(AppView<?> appView) { | 
|  | if (superType == null || type == appView.dexItemFactory().objectType) { | 
|  | return false; | 
|  | } | 
|  | if (hasInstanceFields()) { | 
|  | return true; | 
|  | } | 
|  | DexClass superClass = appView.definitionFor(superType); | 
|  | return superClass == null || superClass.hasInstanceFieldsDirectlyOrIndirectly(appView); | 
|  | } | 
|  |  | 
|  | public List<DexEncodedField> getDirectAndIndirectInstanceFields(AppView<?> appView) { | 
|  | List<DexEncodedField> result = new ArrayList<>(); | 
|  | DexClass current = this; | 
|  | while (current != null && current.type != appView.dexItemFactory().objectType) { | 
|  | result.addAll(current.instanceFields()); | 
|  | current = appView.definitionFor(current.superType); | 
|  | } | 
|  | return result; | 
|  | } | 
|  |  | 
|  | public boolean isValid(InternalOptions options) { | 
|  | assert verifyNoAbstractMethodsOnNonAbstractClasses(virtualMethods(), options); | 
|  | assert !isInterface() || !getMethodCollection().hasVirtualMethods(DexEncodedMethod::isFinal); | 
|  | assert verifyCorrectnessOfFieldHolders(fields()); | 
|  | assert verifyNoDuplicateFields(); | 
|  | assert methodCollection.verify(); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | public boolean hasStaticSynchronizedMethods() { | 
|  | for (DexEncodedMethod encodedMethod : directMethods()) { | 
|  | if (encodedMethod.isStatic() && encodedMethod.isSynchronized()) { | 
|  | return true; | 
|  | } | 
|  | } | 
|  | return false; | 
|  | } | 
|  | } |