| // 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(); |
| } |
| } |