blob: 7b80c82269590f96e8659aa697d88c9dfa18017b [file] [log] [blame]
// Copyright (c) 2021, 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.ir.analysis.type;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.OptionalBool;
import com.android.tools.r8.utils.Pair;
import it.unimi.dsi.fastutil.objects.Reference2BooleanMap;
import it.unimi.dsi.fastutil.objects.Reference2BooleanMap.Entry;
import it.unimi.dsi.fastutil.objects.Reference2BooleanMaps;
import it.unimi.dsi.fastutil.objects.Reference2BooleanOpenHashMap;
import java.util.ArrayList;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.BiPredicate;
public class InterfaceCollection {
public static boolean isKnownToImplement(
DexType iface, DexType implementor, InternalOptions options) {
if (options.canHaveZipFileWithMissingCloseableBug()
&& implementor == options.dexItemFactory().zipFileType
&& iface == options.dexItemFactory().closeableType) {
return false;
}
return true;
}
public static class Builder {
private Reference2BooleanMap<DexType> interfaces = new Reference2BooleanOpenHashMap<>();
private Builder() {}
public Builder addInterface(DexType iface, DexClass implementor, InternalOptions options) {
return addInterface(
iface,
!implementor.isLibraryClass()
|| isKnownToImplement(iface, implementor.getType(), options));
}
public Builder addInterface(DexType iface, DexType implementor, InternalOptions options) {
return addInterface(iface, isKnownToImplement(iface, implementor, options));
}
public Builder addInterface(DexType type, boolean isKnown) {
interfaces.compute(
type,
(existingType, existingIsKnown) ->
// If the entry is new 'existingIsKnown == null', so we join with (null or true).
(existingIsKnown == null || existingIsKnown) && isKnown);
return this;
}
public InterfaceCollection build() {
if (interfaces.isEmpty()) {
return InterfaceCollection.empty();
}
return new InterfaceCollection(interfaces);
}
}
private static final InterfaceCollection EMPTY =
new InterfaceCollection(Reference2BooleanMaps.emptyMap());
public static InterfaceCollection empty() {
return EMPTY;
}
public static InterfaceCollection singleton(DexType type) {
return new InterfaceCollection(Reference2BooleanMaps.singleton(type, true));
}
public static Builder builder() {
return new Builder();
}
/**
* Set of interfaces mapping to an optional presence.
*
* <ul>
* <li>An unmapped type is known to not be present.
* <li>A type mapped to true is known to always be present.
* <li>A type mapped to false is not always known to be present.
*/
private final Reference2BooleanMap<DexType> interfaces;
private InterfaceCollection(Reference2BooleanMap<DexType> interfaces) {
assert interfaces != null;
this.interfaces = interfaces;
}
public boolean isEmpty() {
return interfaces.isEmpty();
}
public int size() {
return interfaces.size();
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof InterfaceCollection)) {
return false;
}
InterfaceCollection that = (InterfaceCollection) o;
return interfaces.equals(that.interfaces);
}
@Override
public int hashCode() {
return interfaces.hashCode();
}
public void forEach(BiConsumer<DexType, Boolean> fn) {
interfaces.forEach(fn::accept);
}
public boolean anyMatch(BiPredicate<DexType, Boolean> fn) {
for (Entry<DexType> entry : interfaces.reference2BooleanEntrySet()) {
if (fn.test(entry.getKey(), entry.getBooleanValue())) {
return true;
}
}
return false;
}
public List<Pair<DexType, Boolean>> getInterfaceList() {
List<Pair<DexType, Boolean>> list = new ArrayList<>(interfaces.size());
interfaces.forEach((iface, isKnown) -> list.add(new Pair<>(iface, isKnown)));
return list;
}
public boolean hasSingleKnownInterface() {
DexType singleKnownInterface = getSingleKnownInterface();
return singleKnownInterface != null;
}
public DexType getSingleKnownInterface() {
if (interfaces.size() != 1) {
return null;
}
DexType type = interfaces.keySet().iterator().next();
return interfaces.getBoolean(type) ? type : null;
}
public OptionalBool contains(DexType type) {
Boolean value = interfaces.get(type);
if (value == null) {
return OptionalBool.FALSE;
}
return value ? OptionalBool.TRUE : OptionalBool.unknown();
}
public boolean containsKnownInterface(DexType type) {
return contains(type).isTrue();
}
}