blob: 3e96a434b68729d0fda8d8974229fcf2e4e34ce2 [file] [log] [blame]
// Copyright (c) 2020, 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.utils.DequeUtils;
import com.android.tools.r8.utils.SetUtils;
import com.google.common.collect.Sets;
import java.util.Collection;
import java.util.Deque;
import java.util.IdentityHashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
public class PrunedItems {
private final DexApplication prunedApp;
private final Set<DexReference> additionalPinnedItems;
private final Map<DexMethod, ProgramMethod> fullyInlinedMethods;
private final Set<DexType> noLongerSyntheticItems;
private final Set<DexType> removedClasses;
private final Set<DexField> removedFields;
private final Set<DexMethod> removedMethods;
private PrunedItems(
DexApplication prunedApp,
Set<DexReference> additionalPinnedItems,
Map<DexMethod, ProgramMethod> fullyInlinedMethods,
Set<DexType> noLongerSyntheticItems,
Set<DexType> removedClasses,
Set<DexField> removedFields,
Set<DexMethod> removedMethods) {
this.prunedApp = prunedApp;
this.additionalPinnedItems = additionalPinnedItems;
this.fullyInlinedMethods = fullyInlinedMethods;
this.noLongerSyntheticItems = noLongerSyntheticItems;
this.removedClasses = removedClasses;
this.removedFields = removedFields;
this.removedMethods = removedMethods;
}
public static ConcurrentBuilder concurrentBuilder() {
return new ConcurrentBuilder();
}
public static Builder builder() {
return new Builder();
}
public Builder toBuilder() {
return new Builder(this);
}
public static PrunedItems empty(DexApplication application) {
return new Builder().setPrunedApp(application).build();
}
public boolean isEmpty() {
return additionalPinnedItems.isEmpty()
&& fullyInlinedMethods.isEmpty()
&& noLongerSyntheticItems.isEmpty()
&& removedClasses.isEmpty()
&& removedFields.isEmpty()
&& removedMethods.isEmpty();
}
public boolean isFullyInlined(DexMethod method) {
return fullyInlinedMethods.containsKey(method);
}
public void forEachFullyInlinedMethodCaller(DexMethod method, Consumer<ProgramMethod> consumer) {
assert isFullyInlined(method);
consumer.accept(fullyInlinedMethods.get(method));
}
public boolean isRemoved(DexField field) {
return removedFields.contains(field) || removedClasses.contains(field.getHolderType());
}
public boolean isRemoved(DexMethod method) {
return removedMethods.contains(method) || removedClasses.contains(method.getHolderType());
}
public boolean isRemoved(DexReference reference) {
return reference.apply(this::isRemoved, this::isRemoved, this::isRemoved);
}
public boolean isRemoved(DexType type) {
return removedClasses.contains(type);
}
public DexApplication getPrunedApp() {
return prunedApp;
}
public Set<? extends DexReference> getAdditionalPinnedItems() {
return additionalPinnedItems;
}
public Map<DexMethod, ProgramMethod> getFullyInlinedMethods() {
return fullyInlinedMethods;
}
public Set<DexType> getNoLongerSyntheticItems() {
return noLongerSyntheticItems;
}
public boolean hasRemovedClasses() {
return !removedClasses.isEmpty();
}
public boolean hasRemovedFields() {
return !removedFields.isEmpty();
}
public boolean hasRemovedMembers() {
return hasRemovedFields() || hasRemovedMethods();
}
public boolean hasRemovedMethods() {
return !removedMethods.isEmpty();
}
public Set<DexType> getRemovedClasses() {
return removedClasses;
}
public Set<DexField> getRemovedFields() {
return removedFields;
}
public Set<DexMethod> getRemovedMethods() {
return removedMethods;
}
public static class Builder {
private DexApplication prunedApp;
private final Set<DexReference> additionalPinnedItems;
private Map<DexMethod, ProgramMethod> fullyInlinedMethods;
private final Set<DexType> noLongerSyntheticItems;
private Set<DexType> removedClasses;
private final Set<DexField> removedFields;
private Set<DexMethod> removedMethods;
Builder() {
additionalPinnedItems = newEmptySet();
fullyInlinedMethods = newEmptyMap();
noLongerSyntheticItems = newEmptySet();
removedClasses = newEmptySet();
removedFields = newEmptySet();
removedMethods = newEmptySet();
}
Builder(PrunedItems prunedItems) {
this();
assert prunedItems.getFullyInlinedMethods().isEmpty();
additionalPinnedItems.addAll(prunedItems.getAdditionalPinnedItems());
noLongerSyntheticItems.addAll(prunedItems.getNoLongerSyntheticItems());
prunedApp = prunedItems.getPrunedApp();
removedClasses.addAll(prunedItems.getRemovedClasses());
removedFields.addAll(prunedItems.getRemovedFields());
removedMethods.addAll(prunedItems.getRemovedMethods());
}
<T> Set<T> newEmptySet() {
return Sets.newIdentityHashSet();
}
<S, T> Map<S, T> newEmptyMap() {
return new IdentityHashMap<>();
}
public Builder setPrunedApp(DexApplication prunedApp) {
this.prunedApp = prunedApp;
return this;
}
public Builder addAdditionalPinnedItems(
Collection<? extends DexReference> additionalPinnedItems) {
this.additionalPinnedItems.addAll(additionalPinnedItems);
return this;
}
public boolean hasFullyInlinedMethods() {
return !fullyInlinedMethods.isEmpty();
}
public Builder addFullyInlinedMethod(DexMethod method, ProgramMethod singleCaller) {
assert !fullyInlinedMethods.containsKey(method);
fullyInlinedMethods.put(method, singleCaller);
removedMethods.add(method);
return this;
}
public void clearFullyInlinedMethods() {
fullyInlinedMethods.clear();
}
public Builder addNoLongerSyntheticItems(Set<DexType> noLongerSyntheticItems) {
this.noLongerSyntheticItems.addAll(noLongerSyntheticItems);
return this;
}
public Builder addRemovedClass(DexType removedClass) {
this.noLongerSyntheticItems.add(removedClass);
this.removedClasses.add(removedClass);
return this;
}
public Builder addRemovedClasses(Set<DexType> removedClasses) {
this.noLongerSyntheticItems.addAll(removedClasses);
this.removedClasses.addAll(removedClasses);
return this;
}
public Builder addRemovedField(DexField removedField) {
removedFields.add(removedField);
return this;
}
public Builder addRemovedFields(Collection<DexField> removedFields) {
this.removedFields.addAll(removedFields);
return this;
}
public boolean hasRemovedMethods() {
return !removedMethods.isEmpty();
}
public Builder addRemovedMethod(DexMethod removedMethod) {
removedMethods.add(removedMethod);
return this;
}
public Builder addRemovedMethods(Collection<DexMethod> removedMethods) {
this.removedMethods.addAll(removedMethods);
return this;
}
public void clearRemovedMethods() {
removedMethods.clear();
}
public Builder setRemovedClasses(Set<DexType> removedClasses) {
this.removedClasses = removedClasses;
return this;
}
public Builder setRemovedMethods(Set<DexMethod> removedMethods) {
this.removedMethods = removedMethods;
return this;
}
public PrunedItems build() {
if (hasFullyInlinedMethods()) {
compressInliningPaths();
}
return new PrunedItems(
prunedApp,
additionalPinnedItems,
fullyInlinedMethods,
noLongerSyntheticItems,
removedClasses,
removedFields,
removedMethods);
}
private void compressInliningPaths() {
Map<DexMethod, ProgramMethod> fullyInlinedMethodsUpdate = new IdentityHashMap<>();
for (Entry<DexMethod, ProgramMethod> entry : fullyInlinedMethods.entrySet()) {
DexMethod innermostCallee = entry.getKey();
if (fullyInlinedMethodsUpdate.containsKey(innermostCallee)) {
// Already processed as a result of previously processing a callee of the current callee.
continue;
}
ProgramMethod innermostCaller = entry.getValue();
ProgramMethod outermostCaller = fullyInlinedMethods.get(innermostCaller.getReference());
if (outermostCaller == null) {
continue;
}
Deque<DexMethod> fullyInlinedMethodChain =
DequeUtils.newArrayDeque(innermostCallee, innermostCaller.getReference());
while (true) {
DexMethod currentCallee = outermostCaller.getReference();
ProgramMethod currentCaller = fullyInlinedMethods.get(currentCallee);
if (currentCaller == null) {
break;
}
fullyInlinedMethodChain.addLast(currentCallee);
outermostCaller = currentCaller;
}
assert !removedMethods.contains(outermostCaller.getReference());
for (DexMethod callee : fullyInlinedMethodChain) {
fullyInlinedMethodsUpdate.put(callee, outermostCaller);
}
}
fullyInlinedMethods.putAll(fullyInlinedMethodsUpdate);
}
}
public static class ConcurrentBuilder extends Builder {
@Override
<T> Set<T> newEmptySet() {
return SetUtils.newConcurrentHashSet();
}
@Override
<S, T> Map<S, T> newEmptyMap() {
return new ConcurrentHashMap<>();
}
@Override
public Builder setRemovedClasses(Set<DexType> removedClasses) {
throw new UnsupportedOperationException();
}
@Override
public Builder setRemovedMethods(Set<DexMethod> removedMethods) {
throw new UnsupportedOperationException();
}
}
}