|  | // 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().length + instanceFields().length; | 
|  | 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; | 
|  | } | 
|  | } |