|  | // 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 static com.android.tools.r8.ir.analysis.type.Nullability.maybeNull; | 
|  |  | 
|  | import com.android.tools.r8.dex.IndexedItemCollection; | 
|  | import com.android.tools.r8.errors.CompilationError; | 
|  | import com.android.tools.r8.ir.analysis.type.TypeElement; | 
|  | import com.android.tools.r8.references.FieldReference; | 
|  | import com.android.tools.r8.references.Reference; | 
|  | import com.android.tools.r8.utils.structural.CompareToVisitor; | 
|  | import com.android.tools.r8.utils.structural.HashingVisitor; | 
|  | import com.android.tools.r8.utils.structural.StructuralMapping; | 
|  | import com.android.tools.r8.utils.structural.StructuralSpecification; | 
|  | import java.util.Collections; | 
|  | import java.util.function.BiConsumer; | 
|  | import java.util.function.Consumer; | 
|  | import java.util.function.Function; | 
|  |  | 
|  | public class DexField extends DexMember<DexEncodedField, DexField> { | 
|  |  | 
|  | public final DexType type; | 
|  |  | 
|  | DexField(DexType holder, DexType type, DexString name, boolean skipNameValidationForTesting) { | 
|  | super(holder, name); | 
|  | this.type = type; | 
|  | if (!skipNameValidationForTesting && !name.isValidFieldName()) { | 
|  | throw new CompilationError( | 
|  | "Field name '" + name.toString() + "' cannot be represented in dex format."); | 
|  | } | 
|  | } | 
|  |  | 
|  | private static void specify(StructuralSpecification<DexField, ?> spec) { | 
|  | spec.withItem(DexField::getHolderType).withItem(DexField::getName).withItem(DexField::getType); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public int compareTo(DexReference other) { | 
|  | if (other.isDexField()) { | 
|  | return compareTo(other.asDexField()); | 
|  | } | 
|  | if (other.isDexMethod()) { | 
|  | int comparisonResult = getHolderType().compareTo(other.getContextType()); | 
|  | return comparisonResult != 0 ? comparisonResult : -1; | 
|  | } | 
|  | int comparisonResult = getHolderType().compareTo(other.asDexType()); | 
|  | return comparisonResult != 0 ? comparisonResult : 1; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void acceptHashing(HashingVisitor visitor) { | 
|  | visitor.visitDexType(holder); | 
|  | visitor.visitDexString(name); | 
|  | getReferencedTypes().forEach(visitor::visitDexType); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public DexField self() { | 
|  | return this; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public StructuralMapping<DexField> getStructuralMapping() { | 
|  | return DexField::specify; | 
|  | } | 
|  |  | 
|  | public DexType getType() { | 
|  | return type; | 
|  | } | 
|  |  | 
|  | public TypeElement getTypeElement(AppView<?> appView) { | 
|  | return TypeElement.fromDexType(getType(), maybeNull(), appView); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public DexEncodedField lookupOnClass(DexClass clazz) { | 
|  | return clazz != null ? clazz.lookupField(this) : null; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public DexClassAndField lookupMemberOnClass(DexClass clazz) { | 
|  | return clazz != null ? clazz.lookupClassField(this) : null; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public ProgramField lookupOnProgramClass(DexProgramClass clazz) { | 
|  | return clazz != null ? clazz.lookupProgramField(this) : null; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public <T> T apply(Function<DexField, T> fieldConsumer, Function<DexMethod, T> methodConsumer) { | 
|  | return fieldConsumer.apply(this); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public <T> T apply( | 
|  | Function<DexType, T> classConsumer, | 
|  | Function<DexField, T> fieldConsumer, | 
|  | Function<DexMethod, T> methodConsumer) { | 
|  | return fieldConsumer.apply(this); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void accept( | 
|  | Consumer<DexType> classConsumer, | 
|  | Consumer<DexField> fieldConsumer, | 
|  | Consumer<DexMethod> methodConsumer) { | 
|  | fieldConsumer.accept(this); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public <T> void accept( | 
|  | BiConsumer<DexType, T> classConsumer, | 
|  | BiConsumer<DexField, T> fieldConsumer, | 
|  | BiConsumer<DexMethod, T> methodConsumer, | 
|  | T arg) { | 
|  | fieldConsumer.accept(this, arg); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public int computeHashCode() { | 
|  | return holder.hashCode() | 
|  | + type.hashCode() * 7 | 
|  | + name.hashCode() * 31; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean computeEquals(Object other) { | 
|  | if (other instanceof DexField) { | 
|  | DexField o = (DexField) other; | 
|  | return holder.equals(o.holder) | 
|  | && type.equals(o.type) | 
|  | && name.equals(o.name); | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public String toString() { | 
|  | return "Field " + type + " " + holder + "." + name; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void collectIndexedItems(AppView<?> appView, IndexedItemCollection indexedItems) { | 
|  | if (indexedItems.addField(this)) { | 
|  | holder.collectIndexedItems(appView, indexedItems); | 
|  | type.collectIndexedItems(appView, indexedItems); | 
|  | appView.getNamingLens().lookupName(this).collectIndexedItems(indexedItems); | 
|  | } | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public int getOffset(ObjectToOffsetMapping mapping) { | 
|  | return mapping.getOffsetFor(this); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean isDexField() { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public DexField asDexField() { | 
|  | return this; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public Iterable<DexType> getReferencedTypes() { | 
|  | return Collections.singleton(type); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public int acceptCompareTo(DexField other, CompareToVisitor visitor) { | 
|  | return visitor.visitDexField(this, other); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean match(DexField field) { | 
|  | return field.name == name && field.type == type; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean match(DexEncodedField encodedField) { | 
|  | return match(encodedField.getReference()); | 
|  | } | 
|  |  | 
|  | public String qualifiedName() { | 
|  | return holder + "." + name; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public String toSmaliString() { | 
|  | return holder.toSmaliString() + "->" + name + ":" + type.toSmaliString(); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public String toSourceString() { | 
|  | return type.toSourceString() + " " + holder.toSourceString() + "." + name.toSourceString(); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public DexField withHolder(DexType holder, DexItemFactory dexItemFactory) { | 
|  | return dexItemFactory.createField(holder, type, name); | 
|  | } | 
|  |  | 
|  | public DexField withName(DexString name, DexItemFactory dexItemFactory) { | 
|  | return dexItemFactory.createField(holder, type, name); | 
|  | } | 
|  |  | 
|  | public DexField withType(DexType type, DexItemFactory dexItemFactory) { | 
|  | return dexItemFactory.createField(holder, type, name); | 
|  | } | 
|  |  | 
|  | public FieldReference asFieldReference() { | 
|  | return Reference.field( | 
|  | Reference.classFromDescriptor(holder.toDescriptorString()), | 
|  | name.toString(), | 
|  | Reference.typeFromDescriptor(type.toDescriptorString())); | 
|  | } | 
|  | } |