blob: 4312bfd6ddd5f4eb1cf1e9350f7280ae22acf816 [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 com.android.tools.r8.ProgramResource;
import com.android.tools.r8.ProgramResource.Kind;
import com.android.tools.r8.dex.IndexedItemCollection;
import com.android.tools.r8.dex.MixedSectionCollection;
import com.android.tools.r8.kotlin.KotlinInfo;
import com.android.tools.r8.origin.Origin;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.function.Supplier;
public class DexProgramClass extends DexClass implements Supplier<DexProgramClass> {
private static final DexEncodedArray SENTINEL_NOT_YET_COMPUTED =
new DexEncodedArray(new DexValue[0]);
private final ProgramResource.Kind originKind;
private DexEncodedArray staticValues = SENTINEL_NOT_YET_COMPUTED;
private final Collection<DexProgramClass> synthesizedFrom;
private int initialClassFileVersion = -1;
private KotlinInfo kotlinInfo = null;
public DexProgramClass(
DexType type,
ProgramResource.Kind originKind,
Origin origin,
ClassAccessFlags accessFlags,
DexType superType,
DexTypeList interfaces,
DexString sourceFile,
EnclosingMethodAttribute enclosingMember,
List<InnerClassAttribute> innerClasses,
DexAnnotationSet classAnnotations,
DexEncodedField[] staticFields,
DexEncodedField[] instanceFields,
DexEncodedMethod[] directMethods,
DexEncodedMethod[] virtualMethods,
boolean skipNameValidationForTesting) {
this(
type,
originKind,
origin,
accessFlags,
superType,
interfaces,
sourceFile,
enclosingMember,
innerClasses,
classAnnotations,
staticFields,
instanceFields,
directMethods,
virtualMethods,
skipNameValidationForTesting,
Collections.emptyList());
}
public DexProgramClass(
DexType type,
ProgramResource.Kind originKind,
Origin origin,
ClassAccessFlags accessFlags,
DexType superType,
DexTypeList interfaces,
DexString sourceFile,
EnclosingMethodAttribute enclosingMember,
List<InnerClassAttribute> innerClasses,
DexAnnotationSet classAnnotations,
DexEncodedField[] staticFields,
DexEncodedField[] instanceFields,
DexEncodedMethod[] directMethods,
DexEncodedMethod[] virtualMethods,
boolean skipNameValidationForTesting,
Collection<DexProgramClass> synthesizedDirectlyFrom) {
super(
sourceFile,
interfaces,
accessFlags,
superType,
type,
staticFields,
instanceFields,
directMethods,
virtualMethods,
enclosingMember,
innerClasses,
classAnnotations,
origin,
skipNameValidationForTesting);
assert classAnnotations != null;
this.originKind = originKind;
this.synthesizedFrom = new HashSet<>();
synthesizedDirectlyFrom.forEach(this::addSynthesizedFrom);
}
public boolean originatesFromDexResource() {
return originKind == Kind.DEX;
}
public boolean originatesFromClassResource() {
return originKind == Kind.CF;
}
@Override
public void collectIndexedItems(IndexedItemCollection indexedItems,
DexMethod method, int instructionOffset) {
if (indexedItems.addClass(this)) {
type.collectIndexedItems(indexedItems, method, instructionOffset);
if (superType != null) {
superType.collectIndexedItems(indexedItems, method, instructionOffset);
} else {
assert type.toDescriptorString().equals("Ljava/lang/Object;");
}
if (sourceFile != null) {
sourceFile.collectIndexedItems(indexedItems, method, instructionOffset);
}
if (annotations != null) {
annotations.collectIndexedItems(indexedItems, method, instructionOffset);
}
if (interfaces != null) {
interfaces.collectIndexedItems(indexedItems, method, instructionOffset);
}
if (getEnclosingMethod() != null) {
getEnclosingMethod().collectIndexedItems(indexedItems);
}
for (InnerClassAttribute attribute : getInnerClasses()) {
attribute.collectIndexedItems(indexedItems);
}
synchronizedCollectAll(indexedItems, staticFields);
synchronizedCollectAll(indexedItems, instanceFields);
synchronizedCollectAll(indexedItems, directMethods);
synchronizedCollectAll(indexedItems, virtualMethods);
}
}
private static <T extends DexItem> void synchronizedCollectAll(IndexedItemCollection collection,
T[] items) {
synchronized (items) {
collectAll(collection, items);
}
}
public Collection<DexProgramClass> getSynthesizedFrom() {
return synthesizedFrom;
}
@Override
void collectMixedSectionItems(MixedSectionCollection mixedItems) {
assert getEnclosingMethod() == null;
assert getInnerClasses().isEmpty();
if (hasAnnotations()) {
mixedItems.setAnnotationsDirectoryForClass(this, new DexAnnotationDirectory(this));
}
}
@Override
public void addDependencies(MixedSectionCollection collector) {
assert getEnclosingMethod() == null;
assert getInnerClasses().isEmpty();
// We only have a class data item if there are methods or fields.
if (hasMethodsOrFields()) {
collector.add(this);
// The ordering of methods and fields may not be deterministic due to concurrency
// (see b/116027780).
sortMembers();
synchronizedCollectAll(collector, directMethods);
synchronizedCollectAll(collector, virtualMethods);
synchronizedCollectAll(collector, staticFields);
synchronizedCollectAll(collector, instanceFields);
}
if (annotations != null) {
annotations.collectMixedSectionItems(collector);
}
if (interfaces != null) {
interfaces.collectMixedSectionItems(collector);
}
annotations.collectMixedSectionItems(collector);
}
private static <T extends DexItem> void synchronizedCollectAll(MixedSectionCollection collection,
T[] items) {
synchronized (items) {
collectAll(collection, items);
}
}
@Override
public String toString() {
return type.toString();
}
@Override
public String toSourceString() {
return type.toSourceString();
}
@Override
public boolean isProgramClass() {
return true;
}
@Override
public DexProgramClass asProgramClass() {
return this;
}
@Override
public KotlinInfo getKotlinInfo() {
return kotlinInfo;
}
public void setKotlinInfo(KotlinInfo kotlinInfo) {
assert this.kotlinInfo == null || kotlinInfo == null;
this.kotlinInfo = kotlinInfo;
}
public boolean hasMethodsOrFields() {
int numberOfFields = staticFields().size() + instanceFields().size();
int numberOfMethods = directMethods().size() + virtualMethods().size();
return numberOfFields + numberOfMethods > 0;
}
public boolean hasAnnotations() {
return !annotations.isEmpty()
|| hasAnnotations(virtualMethods)
|| hasAnnotations(directMethods)
|| hasAnnotations(staticFields)
|| hasAnnotations(instanceFields);
}
boolean hasOnlyInternalizableAnnotations() {
return !hasAnnotations(virtualMethods)
&& !hasAnnotations(directMethods)
&& !hasAnnotations(staticFields)
&& !hasAnnotations(instanceFields);
}
private boolean hasAnnotations(DexEncodedField[] fields) {
synchronized (fields) {
return Arrays.stream(fields).anyMatch(DexEncodedField::hasAnnotation);
}
}
private boolean hasAnnotations(DexEncodedMethod[] methods) {
synchronized (methods) {
return Arrays.stream(methods).anyMatch(DexEncodedMethod::hasAnnotation);
}
}
public void addSynthesizedFrom(DexProgramClass clazz) {
if (clazz.synthesizedFrom.isEmpty()) {
synthesizedFrom.add(clazz);
} else {
clazz.synthesizedFrom.forEach(this::addSynthesizedFrom);
}
}
public void computeStaticValues() {
// It does not actually hurt to compute this multiple times. So racing on staticValues is OK.
if (staticValues == SENTINEL_NOT_YET_COMPUTED) {
synchronized (staticFields) {
assert PresortedComparable.isSorted(Arrays.asList(staticFields));
DexEncodedField[] fields = staticFields;
int length = 0;
List<DexValue> values = new ArrayList<>(fields.length);
for (int i = 0; i < fields.length; i++) {
DexEncodedField field = fields[i];
DexValue staticValue = field.getStaticValue();
assert staticValue != null;
values.add(staticValue);
if (!staticValue.isDefault(field.field.type)) {
length = i + 1;
}
}
if (length > 0) {
staticValues = new DexEncodedArray(
values.subList(0, length).toArray(new DexValue[length]));
} else {
staticValues = null;
}
}
}
}
public boolean isSorted() {
return isSorted(virtualMethods)
&& isSorted(directMethods)
&& isSorted(staticFields)
&& isSorted(instanceFields);
}
private static <T extends KeyedDexItem<S>, S extends PresortedComparable<S>> boolean isSorted(
T[] items) {
synchronized (items) {
T[] sorted = items.clone();
Arrays.sort(sorted, Comparator.comparing(KeyedDexItem::getKey));
return Arrays.equals(items, sorted);
}
}
public DexEncodedArray getStaticValues() {
// The sentinel value is left over for classes that actually have no fields.
if (staticValues == SENTINEL_NOT_YET_COMPUTED) {
assert !hasMethodsOrFields();
return null;
}
return staticValues;
}
public void addMethod(DexEncodedMethod method) {
if (method.accessFlags.isStatic()
|| method.accessFlags.isPrivate()
|| method.accessFlags.isConstructor()) {
addDirectMethod(method);
} else {
addVirtualMethod(method);
}
}
public void addVirtualMethod(DexEncodedMethod virtualMethod) {
assert !virtualMethod.accessFlags.isStatic();
assert !virtualMethod.accessFlags.isPrivate();
assert !virtualMethod.accessFlags.isConstructor();
synchronized (virtualMethods) {
virtualMethods = Arrays.copyOf(virtualMethods, virtualMethods.length + 1);
virtualMethods[virtualMethods.length - 1] = virtualMethod;
}
}
public void addDirectMethod(DexEncodedMethod staticMethod) {
assert staticMethod.accessFlags.isStatic() || staticMethod.accessFlags.isPrivate()
|| staticMethod.accessFlags.isConstructor();
synchronized (directMethods) {
directMethods = Arrays.copyOf(directMethods, directMethods.length + 1);
directMethods[directMethods.length - 1] = staticMethod;
}
}
public void sortMembers() {
sortEncodedFields(staticFields);
sortEncodedFields(instanceFields);
sortEncodedMethods(directMethods);
sortEncodedMethods(virtualMethods);
}
private void sortEncodedFields(DexEncodedField[] fields) {
synchronized (fields) {
Arrays.sort(fields, Comparator.comparing(a -> a.field));
}
}
private void sortEncodedMethods(DexEncodedMethod[] methods) {
synchronized (methods) {
Arrays.sort(methods, Comparator.comparing(a -> a.method));
}
}
@Override
public DexProgramClass get() {
return this;
}
public void setInitialClassFileVersion(int initialClassFileVersion) {
assert this.initialClassFileVersion == -1 && initialClassFileVersion > 0;
this.initialClassFileVersion = initialClassFileVersion;
}
public boolean hasClassFileVersion() {
return initialClassFileVersion > -1;
}
public int getInitialClassFileVersion() {
assert initialClassFileVersion > -1;
return initialClassFileVersion;
}
/**
* 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 hasReachabilitySensitiveAnnotation(DexItemFactory factory) {
for (DexEncodedMethod directMethod : directMethods) {
for (DexAnnotation annotation : directMethod.annotations.annotations) {
if (annotation.annotation.type == factory.annotationReachabilitySensitive) {
return true;
}
}
}
for (DexEncodedMethod virtualMethod : virtualMethods) {
for (DexAnnotation annotation : virtualMethod.annotations.annotations) {
if (annotation.annotation.type == factory.annotationReachabilitySensitive) {
return true;
}
}
}
for (DexEncodedField staticField : staticFields) {
for (DexAnnotation annotation : staticField.annotations.annotations) {
if (annotation.annotation.type == factory.annotationReachabilitySensitive) {
return true;
}
}
}
for (DexEncodedField instanceField : instanceFields) {
for (DexAnnotation annotation : instanceField.annotations.annotations) {
if (annotation.annotation.type == factory.annotationReachabilitySensitive) {
return true;
}
}
}
return false;
}
}