| // 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.utils.structural; |
| |
| import com.android.tools.r8.graph.DexReference; |
| import com.android.tools.r8.utils.structural.StructuralItem.CompareToAccept; |
| import com.android.tools.r8.utils.structural.StructuralItem.HashingAccept; |
| import java.util.Arrays; |
| import java.util.Iterator; |
| import java.util.function.Function; |
| import java.util.function.Predicate; |
| import java.util.function.ToDoubleFunction; |
| import java.util.function.ToIntFunction; |
| import java.util.function.ToLongFunction; |
| |
| /** |
| * Simple hash code implementation. |
| * |
| * <p>This visitor relies on the specification of hashCode on all object types. Thus it does not |
| * have a call-back structure that requires the spec implementation as well as a visitor for the |
| * recursive decent. There is also no support for overriding the visitation apart from the usual |
| * override of hashCode(). |
| */ |
| public class HashCodeVisitor<T> extends StructuralSpecification<T, HashCodeVisitor<T>> { |
| |
| public static <T extends StructuralItem<T>> int run(T item) { |
| return run(item, item.getStructuralMapping()); |
| } |
| |
| public static <T> int run(T item, StructuralMapping<T> visit) { |
| HashCodeVisitor<T> visitor = new HashCodeVisitor<>(item); |
| visit.apply(visitor); |
| return visitor.hashCode; |
| } |
| |
| private final T item; |
| |
| private int hashCode = 0; |
| |
| private HashCodeVisitor(T item) { |
| this.item = item; |
| } |
| |
| @Override |
| HashCodeVisitor<T> self() { |
| return this; |
| } |
| |
| private HashCodeVisitor<T> amend(int value) { |
| // This mirrors the behavior of Objects.hash(values...) / Arrays.hashCode(array). |
| hashCode = 31 * hashCode + value; |
| return this; |
| } |
| |
| @Override |
| public HashCodeVisitor<T> withAssert(Predicate<T> predicate) { |
| assert predicate.test(item); |
| return this; |
| } |
| |
| @Override |
| public HashCodeVisitor<T> withBool(Predicate<T> getter) { |
| return amend(Boolean.hashCode(getter.test(item))); |
| } |
| |
| @Override |
| public HashCodeVisitor<T> withInt(ToIntFunction<T> getter) { |
| return amend(Integer.hashCode(getter.applyAsInt(item))); |
| } |
| |
| @Override |
| public HashCodeVisitor<T> withLong(ToLongFunction<T> getter) { |
| return amend(Long.hashCode(getter.applyAsLong(item))); |
| } |
| |
| @Override |
| public HashCodeVisitor<T> withDouble(ToDoubleFunction<T> getter) { |
| return amend(Double.hashCode(getter.applyAsDouble(item))); |
| } |
| |
| @Override |
| public HashCodeVisitor<T> withIntArray(Function<T, int[]> getter) { |
| return amend(Arrays.hashCode(getter.apply(item))); |
| } |
| |
| @Override |
| public HashCodeVisitor<T> withShortArray(Function<T, short[]> getter) { |
| return amend(Arrays.hashCode(getter.apply(item))); |
| } |
| |
| @Override |
| protected <S> HashCodeVisitor<T> withConditionalCustomItem( |
| Predicate<T> predicate, |
| Function<T, S> getter, |
| CompareToAccept<S> compare, |
| HashingAccept<S> hasher) { |
| if (predicate.test(item)) { |
| return amend(getter.apply(item).hashCode()); |
| } else { |
| // Use the value 1 for the failing-predicate case such that a different hash is obtained for, |
| // eg, {null, null} and {null}. |
| return amend(1); |
| } |
| } |
| |
| @Override |
| protected <S> HashCodeVisitor<T> withCustomItemIterator( |
| Function<T, Iterator<S>> getter, CompareToAccept<S> compare, HashingAccept<S> hasher) { |
| Iterator<S> it = getter.apply(item); |
| while (it.hasNext()) { |
| amend(it.next().hashCode()); |
| } |
| return this; |
| } |
| |
| @Override |
| public HashCodeVisitor<T> withDexReference(Function<T, DexReference> getter) { |
| return amend(getter.apply(item).hashCode()); |
| } |
| } |