blob: 41dd2c135a427189073d224cc1cda86ce1286ffe [file] [log] [blame]
// 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.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.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 {
public interface FieldSetter {
void setField(int index, DexEncodedField field);
}
public final Origin origin;
public 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;
/** 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,
DexEncodedMethod[] directMethods,
DexEncodedMethod[] virtualMethods,
NestHostClassAttribute nestHost,
List<NestMemberClassAttribute> nestMembers,
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 = new MethodCollection(this, directMethods, virtualMethods);
this.nestHost = nestHost;
this.nestMembers = nestMembers;
assert nestMembers != 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.");
}
}
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<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<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(List<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 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<DexEncodedField> predicate, Consumer<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 TraversalContinuation traverseFields(Function<DexEncodedField, TraversalContinuation> fn) {
for (DexEncodedField field : fields()) {
if (fn.apply(field).shouldBreak()) {
return TraversalContinuation.BREAK;
}
}
return TraversalContinuation.CONTINUE;
}
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 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 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 void clearInstanceFields() {
instanceFields = DexEncodedField.EMPTY_ARRAY;
}
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 DexField lookupUniqueInstanceFieldWithName(DexString name) {
DexField field = null;
for (DexEncodedField encodedField : instanceFields()) {
if (encodedField.getReference().name == name) {
if (field != null) {
return null;
}
field = encodedField.getReference();
}
}
return field;
}
/** 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);
}
/** 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;
}
private 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 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 Origin getOrigin() {
return this.origin;
}
@Override
public DexType getType() {
return type;
}
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 (!isProgramClass()) {
resolvable = appView.dexItemFactory().libraryTypesAssumedToBePresent.contains(type);
} 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 boolean validInterfaceSignatures() {
return getClassSignature().superInterfaceSignatures().isEmpty()
|| interfaces.values.length == getClassSignature().superInterfaceSignatures.size();
}
public void forEachImmediateInterface(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 forEachImmediateSupertype(BiConsumer<DexType, ClassTypeSignature> consumer) {
if (superType != null) {
consumer.accept(superType, classSignature.superClassSignature);
}
forEachImmediateInterface(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 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 != null && !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);
}
/** Returns kotlin class info if the class is synthesized by kotlin compiler. */
public abstract KotlinClassLevelInfo getKotlinInfo();
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;
}
}