|  | // 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.definitelyNotNull; | 
|  |  | 
|  | import com.android.tools.r8.dex.DexOutputBuffer; | 
|  | import com.android.tools.r8.dex.FileWriter; | 
|  | import com.android.tools.r8.dex.IndexedItemCollection; | 
|  | import com.android.tools.r8.dex.MixedSectionCollection; | 
|  | import com.android.tools.r8.errors.Unreachable; | 
|  | import com.android.tools.r8.ir.analysis.type.TypeElement; | 
|  | import com.android.tools.r8.ir.analysis.value.AbstractValue; | 
|  | import com.android.tools.r8.ir.analysis.value.AbstractValueFactory; | 
|  | import com.android.tools.r8.ir.analysis.value.UnknownValue; | 
|  | import com.android.tools.r8.ir.code.ConstInstruction; | 
|  | import com.android.tools.r8.ir.code.ConstString; | 
|  | import com.android.tools.r8.ir.code.DexItemBasedConstString; | 
|  | import com.android.tools.r8.ir.code.IRCode; | 
|  | import com.android.tools.r8.ir.code.Value; | 
|  | import com.android.tools.r8.naming.dexitembasedstring.NameComputationInfo; | 
|  | import com.android.tools.r8.utils.BooleanUtils; | 
|  | import com.android.tools.r8.utils.EncodedValueUtils; | 
|  | import com.android.tools.r8.utils.structural.CompareToVisitor; | 
|  | import com.android.tools.r8.utils.structural.HashingVisitor; | 
|  | import com.android.tools.r8.utils.structural.StructuralItem; | 
|  | import com.android.tools.r8.utils.structural.StructuralMapping; | 
|  | import java.util.Arrays; | 
|  | import java.util.function.Consumer; | 
|  | import org.objectweb.asm.Handle; | 
|  | import org.objectweb.asm.Type; | 
|  |  | 
|  | public abstract class DexValue extends DexItem implements StructuralItem<DexValue> { | 
|  |  | 
|  | public enum DexValueKind { | 
|  | BYTE(0x00), | 
|  | SHORT(0x02), | 
|  | CHAR(0x03), | 
|  | INT(0x04), | 
|  | LONG(0x06), | 
|  | FLOAT(0x10), | 
|  | DOUBLE(0x11), | 
|  | METHOD_TYPE(0x15), | 
|  | METHOD_HANDLE(0x16), | 
|  | STRING(0x17), | 
|  | TYPE(0x18), | 
|  | FIELD(0x19), | 
|  | METHOD(0x1a), | 
|  | ENUM(0x1b), | 
|  | ARRAY(0x1c), | 
|  | ANNOTATION(0x1d), | 
|  | NULL(0x1e), | 
|  | BOOLEAN(0x1f); | 
|  |  | 
|  | public static DexValueKind fromId(int id) { | 
|  | switch (id) { | 
|  | case 0x00: | 
|  | return BYTE; | 
|  | case 0x02: | 
|  | return SHORT; | 
|  | case 0x03: | 
|  | return CHAR; | 
|  | case 0x04: | 
|  | return INT; | 
|  | case 0x06: | 
|  | return LONG; | 
|  | case 0x10: | 
|  | return FLOAT; | 
|  | case 0x11: | 
|  | return DOUBLE; | 
|  | case 0x15: | 
|  | return METHOD_TYPE; | 
|  | case 0x16: | 
|  | return METHOD_HANDLE; | 
|  | case 0x17: | 
|  | return STRING; | 
|  | case 0x18: | 
|  | return TYPE; | 
|  | case 0x19: | 
|  | return FIELD; | 
|  | case 0x1a: | 
|  | return METHOD; | 
|  | case 0x1b: | 
|  | return ENUM; | 
|  | case 0x1c: | 
|  | return ARRAY; | 
|  | case 0x1d: | 
|  | return ANNOTATION; | 
|  | case 0x1e: | 
|  | return NULL; | 
|  | case 0x1f: | 
|  | return BOOLEAN; | 
|  | default: | 
|  | throw new Unreachable(); | 
|  | } | 
|  | } | 
|  |  | 
|  | private final byte b; | 
|  |  | 
|  | DexValueKind(int b) { | 
|  | this.b = (byte) b; | 
|  | } | 
|  |  | 
|  | byte toByte() { | 
|  | return b; | 
|  | } | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public DexValue self() { | 
|  | return this; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public final StructuralMapping<DexValue> getStructuralMapping() { | 
|  | // DexValue is not generic at its base type (and can't as we use it as a polymorphic value), | 
|  | // so each concrete value must implement polymorphic accept functions. This base class | 
|  | // implements (most of) the polymorphic checks and concrete types implement the internal | 
|  | // variants for that type. | 
|  | throw new Unreachable(); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public final int acceptCompareTo(DexValue other, CompareToVisitor visitor) { | 
|  | // Order first on 'kind', only equal kinds then forward to the 'kind' specific internal compare. | 
|  | if (getValueKind() != other.getValueKind()) { | 
|  | return visitor.visitInt(getValueKind().toByte(), other.getValueKind().toByte()); | 
|  | } else { | 
|  | return internalAcceptCompareTo(other, visitor); | 
|  | } | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public final void acceptHashing(HashingVisitor visitor) { | 
|  | // Always hash the 'kind' which ensures that raw values of different type are distinct. | 
|  | visitor.visitInt(getValueKind().toByte()); | 
|  | internalAcceptHashing(visitor); | 
|  | } | 
|  |  | 
|  | abstract int internalAcceptCompareTo(DexValue other, CompareToVisitor visitor); | 
|  |  | 
|  | abstract void internalAcceptHashing(HashingVisitor visitor); | 
|  |  | 
|  | public static final DexValue[] EMPTY_ARRAY = {}; | 
|  |  | 
|  | public abstract DexValueKind getValueKind(); | 
|  |  | 
|  | public boolean isDexItemBasedValueString() { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | public DexItemBasedValueString asDexItemBasedValueString() { | 
|  | return null; | 
|  | } | 
|  |  | 
|  | public boolean isDexValueMethodHandle() { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | public DexValueMethodHandle asDexValueMethodHandle() { | 
|  | return null; | 
|  | } | 
|  |  | 
|  | public boolean isDexValueMethodType() { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | public DexValueMethodType asDexValueMethodType() { | 
|  | return null; | 
|  | } | 
|  |  | 
|  | public boolean isDexValueAnnotation() { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | public DexValueAnnotation asDexValueAnnotation() { | 
|  | return null; | 
|  | } | 
|  |  | 
|  | public boolean isDexValueArray() { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | public DexValueArray asDexValueArray() { | 
|  | return null; | 
|  | } | 
|  |  | 
|  | public boolean isDexValueBoolean() { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | public DexValueBoolean asDexValueBoolean() { | 
|  | return null; | 
|  | } | 
|  |  | 
|  | public boolean isDexValueByte() { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | public DexValueByte asDexValueByte() { | 
|  | return null; | 
|  | } | 
|  |  | 
|  | public boolean isDexValueDouble() { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | public DexValueDouble asDexValueDouble() { | 
|  | return null; | 
|  | } | 
|  |  | 
|  | public boolean isDexValueChar() { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | public DexValueChar asDexValueChar() { | 
|  | return null; | 
|  | } | 
|  |  | 
|  | public boolean isDexValueEnum() { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | public DexValueEnum asDexValueEnum() { | 
|  | return null; | 
|  | } | 
|  |  | 
|  | public boolean isDexValueField() { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | public DexValueField asDexValueField() { | 
|  | return null; | 
|  | } | 
|  |  | 
|  | public boolean isDexValueFloat() { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | public DexValueFloat asDexValueFloat() { | 
|  | return null; | 
|  | } | 
|  |  | 
|  | public boolean isDexValueInt() { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | public DexValueInt asDexValueInt() { | 
|  | return null; | 
|  | } | 
|  |  | 
|  | public boolean isDexValueLong() { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | public DexValueLong asDexValueLong() { | 
|  | return null; | 
|  | } | 
|  |  | 
|  | public boolean isDexValueMethod() { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | public DexValueMethod asDexValueMethod() { | 
|  | return null; | 
|  | } | 
|  |  | 
|  | public boolean isDexValueNull() { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | public DexValueNull asDexValueNull() { | 
|  | return null; | 
|  | } | 
|  |  | 
|  | public boolean isDexValueNumber() { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | public DexValueNumber asDexValueNumber() { | 
|  | return null; | 
|  | } | 
|  |  | 
|  | public boolean isDexValueShort() { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | public DexValueShort asDexValueShort() { | 
|  | return null; | 
|  | } | 
|  |  | 
|  | public boolean isDexValueString() { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | public DexValueString asDexValueString() { | 
|  | return null; | 
|  | } | 
|  |  | 
|  | public boolean isDexValueType() { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | public DexValueType asDexValueType() { | 
|  | return null; | 
|  | } | 
|  |  | 
|  | public boolean isNestedDexValue() { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | public abstract AbstractValue toAbstractValue(AbstractValueFactory factory); | 
|  |  | 
|  | static DexValue fromAsmBootstrapArgument( | 
|  | Object value, JarApplicationReader application, DexType clazz) { | 
|  | if (value instanceof Integer) { | 
|  | return DexValue.DexValueInt.create((Integer) value); | 
|  | } else if (value instanceof Long) { | 
|  | return DexValue.DexValueLong.create((Long) value); | 
|  | } else if (value instanceof Float) { | 
|  | return DexValue.DexValueFloat.create((Float) value); | 
|  | } else if (value instanceof Double) { | 
|  | return DexValue.DexValueDouble.create((Double) value); | 
|  | } else if (value instanceof String) { | 
|  | return new DexValue.DexValueString(application.getString((String) value)); | 
|  |  | 
|  | } else if (value instanceof Type) { | 
|  | Type type = (Type) value; | 
|  | switch (type.getSort()) { | 
|  | case Type.OBJECT: | 
|  | return new DexValue.DexValueType( | 
|  | application.getTypeFromDescriptor(((Type) value).getDescriptor())); | 
|  | case Type.METHOD: | 
|  | return new DexValue.DexValueMethodType( | 
|  | application.getProto(((Type) value).getDescriptor())); | 
|  | default: | 
|  | throw new Unreachable("Type sort is not supported: " + type.getSort()); | 
|  | } | 
|  | } else if (value instanceof Handle) { | 
|  | return new DexValue.DexValueMethodHandle( | 
|  | DexMethodHandle.fromAsmHandle((Handle) value, application, clazz)); | 
|  | } else { | 
|  | throw new Unreachable( | 
|  | "Unsupported bootstrap static argument of type " + value.getClass().getSimpleName()); | 
|  | } | 
|  | } | 
|  |  | 
|  | private static void writeHeader(DexValueKind kind, int arg, DexOutputBuffer dest) { | 
|  | dest.putByte((byte) ((arg << 5) | kind.toByte())); | 
|  | } | 
|  |  | 
|  | public void collectIndexedItems(IndexedItemCollection indexedItems) { | 
|  | // Intentionally left empty | 
|  | } | 
|  |  | 
|  | @Override | 
|  | void collectMixedSectionItems(MixedSectionCollection mixedItems) { | 
|  | // Should never be visited. | 
|  | throw new Unreachable(); | 
|  | } | 
|  |  | 
|  | public abstract void sort(); | 
|  |  | 
|  | public abstract void writeTo(DexOutputBuffer dest, ObjectToOffsetMapping mapping); | 
|  |  | 
|  | @Override | 
|  | public abstract int hashCode(); | 
|  |  | 
|  | @Override | 
|  | public abstract boolean equals(Object other); | 
|  |  | 
|  | @Override | 
|  | public abstract String toString(); | 
|  |  | 
|  | public static DexValue defaultForType(DexType type) { | 
|  | switch (type.toShorty()) { | 
|  | case 'Z': | 
|  | return DexValueBoolean.DEFAULT; | 
|  | case 'B': | 
|  | return DexValueByte.DEFAULT; | 
|  | case 'C': | 
|  | return DexValueChar.DEFAULT; | 
|  | case 'S': | 
|  | return DexValueShort.DEFAULT; | 
|  | case 'I': | 
|  | return DexValueInt.DEFAULT; | 
|  | case 'J': | 
|  | return DexValueLong.DEFAULT; | 
|  | case 'F': | 
|  | return DexValueFloat.DEFAULT; | 
|  | case 'D': | 
|  | return DexValueDouble.DEFAULT; | 
|  | case 'L': | 
|  | return DexValueNull.NULL; | 
|  | default: | 
|  | throw new Unreachable("No default value for unexpected type " + type); | 
|  | } | 
|  | } | 
|  |  | 
|  | public abstract DexType getType(DexItemFactory factory); | 
|  |  | 
|  | public abstract Object getBoxedValue(); | 
|  |  | 
|  | /** Returns an instruction that can be used to materialize this {@link DexValue} (or null). */ | 
|  | public ConstInstruction asConstInstruction( | 
|  | AppView<? extends AppInfoWithClassHierarchy> appView, IRCode code, DebugLocalInfo local) { | 
|  | return null; | 
|  | } | 
|  |  | 
|  | public boolean isDefault(DexType type) { | 
|  | return this == defaultForType(type); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Whether creating this value as a default value for a field might trigger an allocation. | 
|  | * | 
|  | * <p>This is conservative. It also considers allocations due to class loading when referencing a | 
|  | * field or method. | 
|  | */ | 
|  | public boolean mayHaveSideEffects() { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | public abstract Object asAsmEncodedObject(); | 
|  |  | 
|  | private abstract static class SimpleDexValue extends DexValue { | 
|  |  | 
|  | @Override | 
|  | public void sort() { | 
|  | // Intentionally empty | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean mayHaveSideEffects() { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | static void writeIntegerTo(DexValueKind kind, long value, int expected, DexOutputBuffer dest) { | 
|  | // Leave space for header. | 
|  | dest.forward(1); | 
|  | int length = dest.putSignedEncodedValue(value, expected); | 
|  | dest.rewind(length + 1); | 
|  | writeHeader(kind, length - 1, dest); | 
|  | dest.forward(length); | 
|  | } | 
|  | } | 
|  |  | 
|  | public abstract static class DexValueNumber extends SimpleDexValue { | 
|  |  | 
|  | public abstract long getRawValue(); | 
|  |  | 
|  | @Override | 
|  | public boolean isDexValueNumber() { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public DexValueNumber asDexValueNumber() { | 
|  | return this; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public AbstractValue toAbstractValue(AbstractValueFactory factory) { | 
|  | return factory.createSingleNumberValue(getRawValue()); | 
|  | } | 
|  | } | 
|  |  | 
|  | public static class DexValueByte extends DexValueNumber { | 
|  |  | 
|  | public static final DexValueByte DEFAULT = new DexValueByte((byte) 0); | 
|  |  | 
|  | final byte value; | 
|  |  | 
|  | private DexValueByte(byte value) { | 
|  | this.value = value; | 
|  | } | 
|  |  | 
|  | public static DexValueByte create(byte value) { | 
|  | return value == DEFAULT.value ? DEFAULT : new DexValueByte(value); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | int internalAcceptCompareTo(DexValue other, CompareToVisitor visitor) { | 
|  | return visitor.visitInt(value, other.asDexValueByte().value); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | void internalAcceptHashing(HashingVisitor visitor) { | 
|  | visitor.visitInt(value); | 
|  | } | 
|  |  | 
|  | public byte getValue() { | 
|  | return value; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public DexValueKind getValueKind() { | 
|  | return DexValueKind.BYTE; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public DexType getType(DexItemFactory factory) { | 
|  | return factory.byteType; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public long getRawValue() { | 
|  | return value; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean isDexValueByte() { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public DexValueByte asDexValueByte() { | 
|  | return this; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public Object getBoxedValue() { | 
|  | return getValue(); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void writeTo(DexOutputBuffer dest, ObjectToOffsetMapping mapping) { | 
|  | writeHeader(DexValueKind.BYTE, 0, dest); | 
|  | dest.putSignedEncodedValue(value, 1); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public Object asAsmEncodedObject() { | 
|  | return Integer.valueOf(value); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public int hashCode() { | 
|  | return value * 3; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean equals(Object other) { | 
|  | if (other == this) { | 
|  | return true; | 
|  | } | 
|  | return other instanceof DexValueByte && value == ((DexValueByte) other).value; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public String toString() { | 
|  | return "Byte " + value; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public ConstInstruction asConstInstruction( | 
|  | AppView<? extends AppInfoWithClassHierarchy> appView, IRCode code, DebugLocalInfo local) { | 
|  | return code.createIntConstant(value, local); | 
|  | } | 
|  | } | 
|  |  | 
|  | public static class DexValueShort extends DexValueNumber { | 
|  |  | 
|  | public static final DexValueShort DEFAULT = new DexValueShort((short) 0); | 
|  | final short value; | 
|  |  | 
|  | private DexValueShort(short value) { | 
|  | this.value = value; | 
|  | } | 
|  |  | 
|  | public static DexValueShort create(short value) { | 
|  | return value == DEFAULT.value ? DEFAULT : new DexValueShort(value); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | void internalAcceptHashing(HashingVisitor visitor) { | 
|  | visitor.visitInt(value); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | int internalAcceptCompareTo(DexValue other, CompareToVisitor visitor) { | 
|  | return visitor.visitInt(value, other.asDexValueShort().getValue()); | 
|  | } | 
|  |  | 
|  | public short getValue() { | 
|  | return value; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public DexValueKind getValueKind() { | 
|  | return DexValueKind.SHORT; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public DexType getType(DexItemFactory factory) { | 
|  | return factory.shortType; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public long getRawValue() { | 
|  | return value; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean isDexValueShort() { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public DexValueShort asDexValueShort() { | 
|  | return this; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public Object getBoxedValue() { | 
|  | return getValue(); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void writeTo(DexOutputBuffer dest, ObjectToOffsetMapping mapping) { | 
|  | writeIntegerTo(DexValueKind.SHORT, value, Short.BYTES, dest); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public Object asAsmEncodedObject() { | 
|  | return Integer.valueOf(value); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public int hashCode() { | 
|  | return value * 7; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean equals(Object other) { | 
|  | if (other == this) { | 
|  | return true; | 
|  | } | 
|  | return other instanceof DexValueShort && value == ((DexValueShort) other).value; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public String toString() { | 
|  | return "Short " + value; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public ConstInstruction asConstInstruction( | 
|  | AppView<? extends AppInfoWithClassHierarchy> appView, IRCode code, DebugLocalInfo local) { | 
|  | return code.createIntConstant(value, local); | 
|  | } | 
|  | } | 
|  |  | 
|  | public static class DexValueChar extends DexValueNumber { | 
|  |  | 
|  | public static final DexValueChar DEFAULT = new DexValueChar((char) 0); | 
|  | final char value; | 
|  |  | 
|  | private DexValueChar(char value) { | 
|  | this.value = value; | 
|  | } | 
|  |  | 
|  | public static DexValueChar create(char value) { | 
|  | return value == DEFAULT.value ? DEFAULT : new DexValueChar(value); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | int internalAcceptCompareTo(DexValue other, CompareToVisitor visitor) { | 
|  | return visitor.visitInt(value, other.asDexValueChar().value); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | void internalAcceptHashing(HashingVisitor visitor) { | 
|  | visitor.visitInt(value); | 
|  | } | 
|  |  | 
|  | public char getValue() { | 
|  | return value; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public DexValueKind getValueKind() { | 
|  | return DexValueKind.CHAR; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public DexType getType(DexItemFactory factory) { | 
|  | return factory.charType; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public long getRawValue() { | 
|  | return value; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean isDexValueChar() { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public DexValueChar asDexValueChar() { | 
|  | return this; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public Object getBoxedValue() { | 
|  | return getValue(); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void writeTo(DexOutputBuffer dest, ObjectToOffsetMapping mapping) { | 
|  | dest.forward(1); | 
|  | int length = dest.putUnsignedEncodedValue(value, 2); | 
|  | dest.rewind(length + 1); | 
|  | writeHeader(DexValueKind.CHAR, length - 1, dest); | 
|  | dest.forward(length); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public Object asAsmEncodedObject() { | 
|  | return Integer.valueOf(value); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public int hashCode() { | 
|  | return value * 5; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean equals(Object other) { | 
|  | if (other == this) { | 
|  | return true; | 
|  | } | 
|  | return other instanceof DexValueChar && value == ((DexValueChar) other).value; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public String toString() { | 
|  | return "Char " + value; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public ConstInstruction asConstInstruction( | 
|  | AppView<? extends AppInfoWithClassHierarchy> appView, IRCode code, DebugLocalInfo local) { | 
|  | return code.createIntConstant(value, local); | 
|  | } | 
|  | } | 
|  |  | 
|  | public static class DexValueInt extends DexValueNumber { | 
|  |  | 
|  | public static final DexValueInt DEFAULT = new DexValueInt(0); | 
|  | public final int value; | 
|  |  | 
|  | private DexValueInt(int value) { | 
|  | this.value = value; | 
|  | } | 
|  |  | 
|  | public static DexValueInt create(int value) { | 
|  | return value == DEFAULT.value ? DEFAULT : new DexValueInt(value); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | void internalAcceptHashing(HashingVisitor visitor) { | 
|  | visitor.visitInt(value); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | int internalAcceptCompareTo(DexValue other, CompareToVisitor visitor) { | 
|  | return visitor.visitInt(value, other.asDexValueInt().value); | 
|  | } | 
|  |  | 
|  | public int getValue() { | 
|  | return value; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public DexValueKind getValueKind() { | 
|  | return DexValueKind.INT; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public DexType getType(DexItemFactory factory) { | 
|  | return factory.intType; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public long getRawValue() { | 
|  | return value; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public Object getBoxedValue() { | 
|  | return getValue(); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void writeTo(DexOutputBuffer dest, ObjectToOffsetMapping mapping) { | 
|  | writeIntegerTo(DexValueKind.INT, value, Integer.BYTES, dest); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean isDexValueInt() { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public DexValueInt asDexValueInt() { | 
|  | return this; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public Object asAsmEncodedObject() { | 
|  | return Integer.valueOf(value); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public int hashCode() { | 
|  | return value * 11; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean equals(Object other) { | 
|  | if (other == this) { | 
|  | return true; | 
|  | } | 
|  | return other instanceof DexValueInt && value == ((DexValueInt) other).value; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public String toString() { | 
|  | return "Int " + value; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public ConstInstruction asConstInstruction( | 
|  | AppView<? extends AppInfoWithClassHierarchy> appView, IRCode code, DebugLocalInfo local) { | 
|  | return code.createIntConstant(value, local); | 
|  | } | 
|  | } | 
|  |  | 
|  | public static class DexValueLong extends DexValueNumber { | 
|  |  | 
|  | public static final DexValueLong DEFAULT = new DexValueLong(0); | 
|  | final long value; | 
|  |  | 
|  | private DexValueLong(long value) { | 
|  | this.value = value; | 
|  | } | 
|  |  | 
|  | public static DexValueLong create(long value) { | 
|  | return value == DEFAULT.value ? DEFAULT : new DexValueLong(value); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | int internalAcceptCompareTo(DexValue other, CompareToVisitor visitor) { | 
|  | return visitor.visitLong(value, other.asDexValueLong().value); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | void internalAcceptHashing(HashingVisitor visitor) { | 
|  | visitor.visitLong(value); | 
|  | } | 
|  |  | 
|  | public long getValue() { | 
|  | return value; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public DexValueKind getValueKind() { | 
|  | return DexValueKind.LONG; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public DexType getType(DexItemFactory factory) { | 
|  | return factory.longType; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public long getRawValue() { | 
|  | return value; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean isDexValueLong() { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public DexValueLong asDexValueLong() { | 
|  | return this; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public Object getBoxedValue() { | 
|  | return getValue(); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void writeTo(DexOutputBuffer dest, ObjectToOffsetMapping mapping) { | 
|  | writeIntegerTo(DexValueKind.LONG, value, Long.BYTES, dest); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public Object asAsmEncodedObject() { | 
|  | return Long.valueOf(value); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public int hashCode() { | 
|  | return (int) value * 13; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean equals(Object other) { | 
|  | if (other == this) { | 
|  | return true; | 
|  | } | 
|  | return other instanceof DexValueLong && value == ((DexValueLong) other).value; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public String toString() { | 
|  | return "Long " + value; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public ConstInstruction asConstInstruction( | 
|  | AppView<? extends AppInfoWithClassHierarchy> appView, IRCode code, DebugLocalInfo local) { | 
|  | return code.createLongConstant(value, local); | 
|  | } | 
|  | } | 
|  |  | 
|  | public static class DexValueFloat extends DexValueNumber { | 
|  |  | 
|  | public static final DexValueFloat DEFAULT = new DexValueFloat(0); | 
|  | final float value; | 
|  |  | 
|  | private DexValueFloat(float value) { | 
|  | this.value = value; | 
|  | } | 
|  |  | 
|  | public static DexValueFloat create(float value) { | 
|  | return Float.compare(value, DEFAULT.value) == 0 ? DEFAULT : new DexValueFloat(value); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | void internalAcceptHashing(HashingVisitor visitor) { | 
|  | visitor.visitFloat(value); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | int internalAcceptCompareTo(DexValue other, CompareToVisitor visitor) { | 
|  | return visitor.visitFloat(value, other.asDexValueFloat().value); | 
|  | } | 
|  |  | 
|  | public float getValue() { | 
|  | return value; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public DexValueKind getValueKind() { | 
|  | return DexValueKind.FLOAT; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public DexType getType(DexItemFactory factory) { | 
|  | return factory.floatType; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public long getRawValue() { | 
|  | return Float.floatToIntBits(value); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean isDexValueFloat() { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public DexValueFloat asDexValueFloat() { | 
|  | return this; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public Object getBoxedValue() { | 
|  | return getValue(); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void writeTo(DexOutputBuffer dest, ObjectToOffsetMapping mapping) { | 
|  | dest.forward(1); | 
|  | int length = EncodedValueUtils.putFloat(dest, value); | 
|  | dest.rewind(length + 1); | 
|  | writeHeader(DexValueKind.FLOAT, length - 1, dest); | 
|  | dest.forward(length); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public Object asAsmEncodedObject() { | 
|  | return Float.valueOf(value); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public ConstInstruction asConstInstruction( | 
|  | AppView<? extends AppInfoWithClassHierarchy> appView, IRCode code, DebugLocalInfo local) { | 
|  | return code.createFloatConstant(value, local); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public int hashCode() { | 
|  | return (int) (value * 19); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean equals(Object other) { | 
|  | if (other == this) { | 
|  | return true; | 
|  | } | 
|  | return other instanceof DexValueFloat | 
|  | && Float.compare(value, ((DexValueFloat) other).value) == 0; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public String toString() { | 
|  | return "Float " + value; | 
|  | } | 
|  | } | 
|  |  | 
|  | public static class DexValueDouble extends DexValueNumber { | 
|  |  | 
|  | public static final DexValueDouble DEFAULT = new DexValueDouble(0); | 
|  |  | 
|  | final double value; | 
|  |  | 
|  | private DexValueDouble(double value) { | 
|  | this.value = value; | 
|  | } | 
|  |  | 
|  | public static DexValueDouble create(double value) { | 
|  | return Double.compare(value, DEFAULT.value) == 0 ? DEFAULT : new DexValueDouble(value); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | int internalAcceptCompareTo(DexValue other, CompareToVisitor visitor) { | 
|  | return visitor.visitDouble(value, other.asDexValueDouble().value); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | void internalAcceptHashing(HashingVisitor visitor) { | 
|  | visitor.visitDouble(value); | 
|  | } | 
|  |  | 
|  | public double getValue() { | 
|  | return value; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public DexValueKind getValueKind() { | 
|  | return DexValueKind.DOUBLE; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public DexType getType(DexItemFactory factory) { | 
|  | return factory.doubleType; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public long getRawValue() { | 
|  | return Double.doubleToRawLongBits(value); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean isDexValueDouble() { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public DexValueDouble asDexValueDouble() { | 
|  | return this; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public Object getBoxedValue() { | 
|  | return getValue(); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void writeTo(DexOutputBuffer dest, ObjectToOffsetMapping mapping) { | 
|  | dest.forward(1); | 
|  | int length = EncodedValueUtils.putDouble(dest, value); | 
|  | dest.rewind(length + 1); | 
|  | writeHeader(DexValueKind.DOUBLE, length - 1, dest); | 
|  | dest.forward(length); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public Object asAsmEncodedObject() { | 
|  | return Double.valueOf(value); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public ConstInstruction asConstInstruction( | 
|  | AppView<? extends AppInfoWithClassHierarchy> appView, IRCode code, DebugLocalInfo local) { | 
|  | return code.createDoubleConstant(value, local); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public int hashCode() { | 
|  | return (int) (value * 29); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean equals(Object other) { | 
|  | if (other == this) { | 
|  | return true; | 
|  | } | 
|  | return (other instanceof DexValueDouble) && | 
|  | (Double.compare(value, ((DexValueDouble) other).value) == 0); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public String toString() { | 
|  | return "Double " + value; | 
|  | } | 
|  | } | 
|  |  | 
|  | static private abstract class NestedDexValue<T extends IndexedDexItem> extends DexValue { | 
|  |  | 
|  | public final T value; | 
|  |  | 
|  | private NestedDexValue(T value) { | 
|  | this.value = value; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean isNestedDexValue() { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public DexType getType(DexItemFactory factory) { | 
|  | throw new Unreachable(); | 
|  | } | 
|  |  | 
|  | public T getValue() { | 
|  | return value; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void writeTo(DexOutputBuffer dest, ObjectToOffsetMapping mapping) { | 
|  | int offset = value.getOffset(mapping); | 
|  | dest.forward(1); | 
|  | int length = dest.putUnsignedEncodedValue(offset, 4); | 
|  | dest.rewind(length + 1); | 
|  | writeHeader(getValueKind(), length - 1, dest); | 
|  | dest.forward(length); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public Object getBoxedValue() { | 
|  | throw new Unreachable("No boxed value for DexValue " + this.getClass().getSimpleName()); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public Object asAsmEncodedObject() { | 
|  | throw new Unreachable("No ASM conversion for DexValue " + this.getClass().getSimpleName()); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void sort() { | 
|  | // Intentionally empty. | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public int hashCode() { | 
|  | return value.hashCode() * 7 + getValueKind().toByte(); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean equals(Object other) { | 
|  | if (other == this) { | 
|  | return true; | 
|  | } | 
|  | if (other instanceof NestedDexValue) { | 
|  | NestedDexValue<?> that = (NestedDexValue<?>) other; | 
|  | return that.getValueKind() == getValueKind() && that.value.equals(value); | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public String toString() { | 
|  | return "Item " + getValueKind() + " " + value; | 
|  | } | 
|  | } | 
|  |  | 
|  | static public class DexValueString extends NestedDexValue<DexString> { | 
|  |  | 
|  | public DexValueString(DexString value) { | 
|  | super(value); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | void internalAcceptHashing(HashingVisitor visitor) { | 
|  | value.acceptHashing(visitor); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | int internalAcceptCompareTo(DexValue other, CompareToVisitor visitor) { | 
|  | int order = DexItemBasedValueString.compareAndCheckValueStrings(this, other, visitor); | 
|  | if (order != 0) { | 
|  | return order; | 
|  | } | 
|  | return value.acceptCompareTo(other.asDexValueString().value, visitor); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void collectIndexedItems(IndexedItemCollection indexedItems) { | 
|  | value.collectIndexedItems(indexedItems); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public DexValueString asDexValueString() { | 
|  | return this; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean isDexValueString() { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public Object asAsmEncodedObject() { | 
|  | return value.toString(); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public DexValueKind getValueKind() { | 
|  | return DexValueKind.STRING; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public DexType getType(DexItemFactory factory) { | 
|  | return factory.stringType; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public ConstInstruction asConstInstruction( | 
|  | AppView<? extends AppInfoWithClassHierarchy> appView, IRCode code, DebugLocalInfo local) { | 
|  | TypeElement type = TypeElement.stringClassType(appView, definitelyNotNull()); | 
|  | Value outValue = code.createValue(type, local); | 
|  | ConstString instruction = new ConstString(outValue, value); | 
|  | if (!instruction.instructionInstanceCanThrow()) { | 
|  | return instruction; | 
|  | } | 
|  | return null; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean mayHaveSideEffects() { | 
|  | // Assuming that strings do not have side-effects. | 
|  | return false; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public AbstractValue toAbstractValue(AbstractValueFactory factory) { | 
|  | return factory.createSingleStringValue(value); | 
|  | } | 
|  | } | 
|  |  | 
|  | public static class DexItemBasedValueString extends NestedDexValue<DexReference> { | 
|  |  | 
|  | // Helper to ensure a consistent order on DexValueString and DexItemBasedValueString which are | 
|  | // both defined to have kind 'string'. | 
|  | static int compareAndCheckValueStrings(DexValue v1, DexValue v2, CompareToVisitor visitor) { | 
|  | assert v1.getValueKind() == DexValueKind.STRING; | 
|  | assert v2.getValueKind() == DexValueKind.STRING; | 
|  | int order1 = v1.isDexItemBasedValueString() ? 1 : 0; | 
|  | int order2 = v2.isDexItemBasedValueString() ? 1 : 0; | 
|  | return visitor.visitInt(order1, order2); | 
|  | } | 
|  |  | 
|  | private final NameComputationInfo<?> nameComputationInfo; | 
|  |  | 
|  | public DexItemBasedValueString(DexReference value, NameComputationInfo<?> nameComputationInfo) { | 
|  | super(value); | 
|  | this.nameComputationInfo = nameComputationInfo; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | void internalAcceptHashing(HashingVisitor visitor) { | 
|  | visitor.visitDexReference(value); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | int internalAcceptCompareTo(DexValue other, CompareToVisitor visitor) { | 
|  | int order = compareAndCheckValueStrings(this, other, visitor); | 
|  | if (order != 0) { | 
|  | return order; | 
|  | } | 
|  | return visitor.visitDexReference(value, other.asDexItemBasedValueString().value); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void collectIndexedItems(IndexedItemCollection indexedItems) { | 
|  | value.collectIndexedItems(indexedItems); | 
|  | } | 
|  |  | 
|  | public NameComputationInfo<?> getNameComputationInfo() { | 
|  | return nameComputationInfo; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean isDexItemBasedValueString() { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public DexItemBasedValueString asDexItemBasedValueString() { | 
|  | return this; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public Object asAsmEncodedObject() { | 
|  | return value.toString(); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public DexValueKind getValueKind() { | 
|  | return DexValueKind.STRING; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public DexType getType(DexItemFactory factory) { | 
|  | return factory.stringType; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public ConstInstruction asConstInstruction( | 
|  | AppView<? extends AppInfoWithClassHierarchy> appView, IRCode code, DebugLocalInfo local) { | 
|  | TypeElement type = TypeElement.stringClassType(appView, definitelyNotNull()); | 
|  | Value outValue = code.createValue(type, local); | 
|  | DexItemBasedConstString instruction = | 
|  | new DexItemBasedConstString(outValue, value, nameComputationInfo); | 
|  | // DexItemBasedConstString cannot throw. | 
|  | assert !instruction.instructionInstanceCanThrow(); | 
|  | return instruction; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public AbstractValue toAbstractValue(AbstractValueFactory factory) { | 
|  | return factory.createSingleDexItemBasedStringValue(value, nameComputationInfo); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void writeTo(DexOutputBuffer dest, ObjectToOffsetMapping mapping) { | 
|  | throw new Unreachable( | 
|  | "DexItemBasedValueString values should always be rewritten into DexValueString"); | 
|  | } | 
|  | } | 
|  |  | 
|  | static public class DexValueType extends NestedDexValue<DexType> { | 
|  |  | 
|  | public DexValueType(DexType value) { | 
|  | super(value); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | int internalAcceptCompareTo(DexValue other, CompareToVisitor visitor) { | 
|  | return value.acceptCompareTo(other.asDexValueType().value, visitor); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | void internalAcceptHashing(HashingVisitor visitor) { | 
|  | value.acceptHashing(visitor); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public DexValueKind getValueKind() { | 
|  | return DexValueKind.TYPE; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void collectIndexedItems(IndexedItemCollection indexedItems) { | 
|  | value.collectIndexedItems(indexedItems); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public DexValueType asDexValueType() { | 
|  | return this; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean isDexValueType() { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public AbstractValue toAbstractValue(AbstractValueFactory factory) { | 
|  | return UnknownValue.getInstance(); | 
|  | } | 
|  | } | 
|  |  | 
|  | static public class DexValueField extends NestedDexValue<DexField> { | 
|  |  | 
|  | public DexValueField(DexField value) { | 
|  | super(value); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | int internalAcceptCompareTo(DexValue other, CompareToVisitor visitor) { | 
|  | return value.acceptCompareTo(other.asDexValueField().value, visitor); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | void internalAcceptHashing(HashingVisitor visitor) { | 
|  | value.acceptHashing(visitor); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public DexValueKind getValueKind() { | 
|  | return DexValueKind.FIELD; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void collectIndexedItems(IndexedItemCollection indexedItems) { | 
|  | value.collectIndexedItems(indexedItems); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean isDexValueField() { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public DexValueField asDexValueField() { | 
|  | return this; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public AbstractValue toAbstractValue(AbstractValueFactory factory) { | 
|  | return UnknownValue.getInstance(); | 
|  | } | 
|  | } | 
|  |  | 
|  | static public class DexValueMethod extends NestedDexValue<DexMethod> { | 
|  |  | 
|  | public DexValueMethod(DexMethod value) { | 
|  | super(value); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | int internalAcceptCompareTo(DexValue other, CompareToVisitor visitor) { | 
|  | return value.acceptCompareTo(other.asDexValueMethod().value, visitor); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | void internalAcceptHashing(HashingVisitor visitor) { | 
|  | value.acceptHashing(visitor); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public DexValueKind getValueKind() { | 
|  | return DexValueKind.METHOD; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void collectIndexedItems(IndexedItemCollection indexedItems) { | 
|  | value.collectIndexedItems(indexedItems); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean isDexValueMethod() { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public DexValueMethod asDexValueMethod() { | 
|  | return this; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public AbstractValue toAbstractValue(AbstractValueFactory factory) { | 
|  | return UnknownValue.getInstance(); | 
|  | } | 
|  | } | 
|  |  | 
|  | static public class DexValueEnum extends NestedDexValue<DexField> { | 
|  |  | 
|  | public DexValueEnum(DexField value) { | 
|  | super(value); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | int internalAcceptCompareTo(DexValue other, CompareToVisitor visitor) { | 
|  | return value.acceptCompareTo(other.asDexValueEnum().value, visitor); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | void internalAcceptHashing(HashingVisitor visitor) { | 
|  | value.acceptHashing(visitor); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public DexValueKind getValueKind() { | 
|  | return DexValueKind.ENUM; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void collectIndexedItems(IndexedItemCollection indexedItems) { | 
|  | value.collectIndexedItems(indexedItems); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean isDexValueEnum() { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public DexValueEnum asDexValueEnum() { | 
|  | return this; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public AbstractValue toAbstractValue(AbstractValueFactory factory) { | 
|  | return UnknownValue.getInstance(); | 
|  | } | 
|  | } | 
|  |  | 
|  | static public class DexValueMethodType extends NestedDexValue<DexProto> { | 
|  |  | 
|  | public DexValueMethodType(DexProto value) { | 
|  | super(value); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | int internalAcceptCompareTo(DexValue other, CompareToVisitor visitor) { | 
|  | return value.acceptCompareTo(other.asDexValueMethodType().value, visitor); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | void internalAcceptHashing(HashingVisitor visitor) { | 
|  | value.acceptHashing(visitor); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean isDexValueMethodType() { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public DexValueMethodType asDexValueMethodType() { | 
|  | return this; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public DexValueKind getValueKind() { | 
|  | return DexValueKind.METHOD_TYPE; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void collectIndexedItems(IndexedItemCollection indexedItems) { | 
|  | value.collectIndexedItems(indexedItems); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public AbstractValue toAbstractValue(AbstractValueFactory factory) { | 
|  | return UnknownValue.getInstance(); | 
|  | } | 
|  | } | 
|  |  | 
|  | static public class DexValueArray extends DexValue { | 
|  |  | 
|  | final DexValue[] values; | 
|  |  | 
|  | public DexValueArray(DexValue[] values) { | 
|  | this.values = values; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | int internalAcceptCompareTo(DexValue other, CompareToVisitor visitor) { | 
|  | return visitor.visitItemArray(values, other.asDexValueArray().values); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | void internalAcceptHashing(HashingVisitor visitor) { | 
|  | visitor.visitItemArray(values); | 
|  | } | 
|  |  | 
|  | public void forEachElement(Consumer<DexValue> consumer) { | 
|  | for (DexValue value : values) { | 
|  | consumer.accept(value); | 
|  | } | 
|  | } | 
|  |  | 
|  | public DexValue[] getValues() { | 
|  | return values; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public DexValueKind getValueKind() { | 
|  | return DexValueKind.ARRAY; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void collectIndexedItems(IndexedItemCollection indexedItems) { | 
|  | for (DexValue value : values) { | 
|  | value.collectIndexedItems(indexedItems); | 
|  | } | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void writeTo(DexOutputBuffer dest, ObjectToOffsetMapping mapping) { | 
|  | writeHeader(DexValueKind.ARRAY, 0, dest); | 
|  | dest.putUleb128(values.length); | 
|  | for (DexValue value : values) { | 
|  | value.writeTo(dest, mapping); | 
|  | } | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public DexType getType(DexItemFactory factory) { | 
|  | throw new Unreachable(); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public Object getBoxedValue() { | 
|  | throw new Unreachable("No boxed value for DexValueArray"); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public AbstractValue toAbstractValue(AbstractValueFactory factory) { | 
|  | return UnknownValue.getInstance(); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public Object asAsmEncodedObject() { | 
|  | throw new Unreachable("No ASM conversion for DexValueArray"); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void sort() { | 
|  | for (DexValue value : values) { | 
|  | value.sort(); | 
|  | } | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public int hashCode() { | 
|  | return Arrays.hashCode(values); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean equals(Object other) { | 
|  | if (other == this) { | 
|  | return true; | 
|  | } | 
|  | if (other instanceof DexValueArray) { | 
|  | DexValueArray that = (DexValueArray) other; | 
|  | return Arrays.equals(that.values, values); | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public String toString() { | 
|  | return "Array " + Arrays.toString(values); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean isDexValueArray() { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public DexValueArray asDexValueArray() { | 
|  | return this; | 
|  | } | 
|  | } | 
|  |  | 
|  | static public class DexValueAnnotation extends DexValue { | 
|  |  | 
|  | public final DexEncodedAnnotation value; | 
|  |  | 
|  | public DexValueAnnotation(DexEncodedAnnotation value) { | 
|  | this.value = value; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | int internalAcceptCompareTo(DexValue other, CompareToVisitor visitor) { | 
|  | return value.acceptCompareTo(other.asDexValueAnnotation().value, visitor); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | void internalAcceptHashing(HashingVisitor visitor) { | 
|  | value.acceptHashing(visitor); | 
|  | } | 
|  |  | 
|  | public DexEncodedAnnotation getValue() { | 
|  | return value; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public DexValueKind getValueKind() { | 
|  | return DexValueKind.ANNOTATION; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean isDexValueAnnotation() { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public DexValueAnnotation asDexValueAnnotation() { | 
|  | return this; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void collectIndexedItems(IndexedItemCollection indexedItems) { | 
|  | value.collectIndexedItems(indexedItems); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public AbstractValue toAbstractValue(AbstractValueFactory factory) { | 
|  | return UnknownValue.getInstance(); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void writeTo(DexOutputBuffer dest, ObjectToOffsetMapping mapping) { | 
|  | writeHeader(DexValueKind.ANNOTATION, 0, dest); | 
|  | FileWriter.writeEncodedAnnotation(value, dest, mapping); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public DexType getType(DexItemFactory factory) { | 
|  | throw new Unreachable(); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public Object getBoxedValue() { | 
|  | throw new Unreachable("No boxed value for DexValueAnnotation"); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public Object asAsmEncodedObject() { | 
|  | throw new Unreachable("No ASM conversion for DexValueAnnotation"); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void sort() { | 
|  | value.sort(); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public int hashCode() { | 
|  | return value.hashCode() * 7; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean equals(Object other) { | 
|  | if (other == this) { | 
|  | return true; | 
|  | } | 
|  | if (other instanceof DexValueAnnotation) { | 
|  | DexValueAnnotation that = (DexValueAnnotation) other; | 
|  | return that.value.equals(value); | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public String toString() { | 
|  | return "Annotation " + value; | 
|  | } | 
|  | } | 
|  |  | 
|  | public static class DexValueNull extends DexValueNumber { | 
|  |  | 
|  | public static final DexValue NULL = new DexValueNull(); | 
|  |  | 
|  | // See DexValueNull.NULL | 
|  | private DexValueNull() { | 
|  | } | 
|  |  | 
|  | @Override | 
|  | void internalAcceptHashing(HashingVisitor visitor) { | 
|  | assert this == NULL; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | int internalAcceptCompareTo(DexValue other, CompareToVisitor visitor) { | 
|  | assert this == NULL; | 
|  | assert other == NULL; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | public Object getValue() { | 
|  | return null; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public DexValueKind getValueKind() { | 
|  | return DexValueKind.NULL; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public DexType getType(DexItemFactory factory) { | 
|  | throw new Unreachable(); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public long getRawValue() { | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void writeTo(DexOutputBuffer dest, ObjectToOffsetMapping mapping) { | 
|  | writeHeader(DexValueKind.NULL, 0, dest); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean isDexValueNull() { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public DexValueNull asDexValueNull() { | 
|  | return this; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public Object getBoxedValue() { | 
|  | return null; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public Object asAsmEncodedObject() { | 
|  | return null; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public int hashCode() { | 
|  | return 42; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean equals(Object other) { | 
|  | if (other == this) { | 
|  | return true; | 
|  | } | 
|  | return other instanceof DexValueNull; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public String toString() { | 
|  | return "Null"; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public ConstInstruction asConstInstruction( | 
|  | AppView<? extends AppInfoWithClassHierarchy> appView, IRCode code, DebugLocalInfo local) { | 
|  | return code.createConstNull(local); | 
|  | } | 
|  | } | 
|  |  | 
|  | public static class DexValueBoolean extends DexValueNumber { | 
|  |  | 
|  | private static final DexValueBoolean TRUE = new DexValueBoolean(true); | 
|  | private static final DexValueBoolean FALSE = new DexValueBoolean(false); | 
|  | // Use a separate instance for the default value to distinguish it from an explicit false value. | 
|  | private static final DexValueBoolean DEFAULT = new DexValueBoolean(false); | 
|  |  | 
|  | final boolean value; | 
|  |  | 
|  | private DexValueBoolean(boolean value) { | 
|  | this.value = value; | 
|  | } | 
|  |  | 
|  | public static DexValueBoolean create(boolean value) { | 
|  | return value ? TRUE : FALSE; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | int internalAcceptCompareTo(DexValue other, CompareToVisitor visitor) { | 
|  | return visitor.visitBool(value, other.asDexValueBoolean().value); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | void internalAcceptHashing(HashingVisitor visitor) { | 
|  | visitor.visitBool(value); | 
|  | } | 
|  |  | 
|  | public boolean getValue() { | 
|  | return value; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public DexValueKind getValueKind() { | 
|  | return DexValueKind.BOOLEAN; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public DexType getType(DexItemFactory factory) { | 
|  | return factory.booleanType; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public long getRawValue() { | 
|  | return BooleanUtils.longValue(value); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean isDexValueBoolean() { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public DexValueBoolean asDexValueBoolean() { | 
|  | return this; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public Object getBoxedValue() { | 
|  | return getValue(); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void writeTo(DexOutputBuffer dest, ObjectToOffsetMapping mapping) { | 
|  | writeHeader(DexValueKind.BOOLEAN, value ? 1 : 0, dest); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public Object asAsmEncodedObject() { | 
|  | return Integer.valueOf(value ? 1 : 0); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public int hashCode() { | 
|  | return value ? 1234 : 4321; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean equals(Object other) { | 
|  | if (other == this) { | 
|  | return true; | 
|  | } | 
|  | return other instanceof DexValueBoolean && ((DexValueBoolean) other).value == value; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public String toString() { | 
|  | return value ? "True" : "False"; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public ConstInstruction asConstInstruction( | 
|  | AppView<? extends AppInfoWithClassHierarchy> appView, IRCode code, DebugLocalInfo local) { | 
|  | return code.createIntConstant(BooleanUtils.intValue(value), local); | 
|  | } | 
|  | } | 
|  |  | 
|  | static public class DexValueMethodHandle extends NestedDexValue<DexMethodHandle> { | 
|  |  | 
|  | public DexValueMethodHandle(DexMethodHandle value) { | 
|  | super(value); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | int internalAcceptCompareTo(DexValue other, CompareToVisitor visitor) { | 
|  | return value.acceptCompareTo(other.asDexValueMethodHandle().value, visitor); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | void internalAcceptHashing(HashingVisitor visitor) { | 
|  | value.acceptHashing(visitor); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean isDexValueMethodHandle() { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public DexValueMethodHandle asDexValueMethodHandle() { | 
|  | return this; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public DexValueKind getValueKind() { | 
|  | return DexValueKind.METHOD_HANDLE; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public void collectIndexedItems(IndexedItemCollection indexedItems) { | 
|  | value.collectIndexedItems(indexedItems); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public AbstractValue toAbstractValue(AbstractValueFactory factory) { | 
|  | return UnknownValue.getInstance(); | 
|  | } | 
|  | } | 
|  | } |