blob: 96b6b5aba11a24fc486db6315b51f076fe57fa4e [file] [log] [blame]
// 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.BasicBlock.ThrowingInfo;
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 java.util.Arrays;
import org.objectweb.asm.Handle;
import org.objectweb.asm.Type;
public abstract class DexValue extends DexItem {
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;
}
}
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 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()));
}
@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 collectIndexedItems(
IndexedItemCollection indexedItems, DexMethod method, int instructionOffset) {
// Intentionally left empty
}
@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);
}
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);
}
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);
}
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);
}
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);
}
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);
}
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);
}
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 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 collectIndexedItems(IndexedItemCollection indexedItems,
DexMethod method, int instructionOffset) {
value.collectIndexedItems(indexedItems, method, instructionOffset);
}
@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
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, ThrowingInfo.defaultForConstString(appView.options()));
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> {
private final NameComputationInfo<?> nameComputationInfo;
public DexItemBasedValueString(DexReference value, NameComputationInfo<?> nameComputationInfo) {
super(value);
this.nameComputationInfo = nameComputationInfo;
}
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,
ThrowingInfo.defaultForConstString(appView.options()));
// DexItemBasedConstString cannot throw.
assert !instruction.instructionInstanceCanThrow();
return instruction;
}
@Override
public AbstractValue toAbstractValue(AbstractValueFactory factory) {
// TODO(b/150835624): Update once there is an abstract value to represent dex item based
// strings.
return UnknownValue.getInstance();
}
@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
public DexValueKind getValueKind() {
return DexValueKind.TYPE;
}
@Override
public void collectIndexedItems(
IndexedItemCollection indexedItems, DexMethod method, int instructionOffset) {
value.collectIndexedItems(indexedItems, method, instructionOffset);
}
@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
public DexValueKind getValueKind() {
return DexValueKind.FIELD;
}
@Override
public void collectIndexedItems(
IndexedItemCollection indexedItems, DexMethod method, int instructionOffset) {
value.collectIndexedItems(indexedItems, method, instructionOffset);
}
@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
public DexValueKind getValueKind() {
return DexValueKind.METHOD;
}
@Override
public void collectIndexedItems(
IndexedItemCollection indexedItems, DexMethod method, int instructionOffset) {
value.collectIndexedItems(indexedItems, method, instructionOffset);
}
@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
public DexValueKind getValueKind() {
return DexValueKind.ENUM;
}
@Override
public void collectIndexedItems(
IndexedItemCollection indexedItems, DexMethod method, int instructionOffset) {
value.collectIndexedItems(indexedItems, method, instructionOffset);
}
@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
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, DexMethod method, int instructionOffset) {
value.collectIndexedItems(indexedItems, method, instructionOffset);
}
@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;
}
public DexValue[] getValues() {
return values;
}
@Override
public DexValueKind getValueKind() {
return DexValueKind.ARRAY;
}
@Override
public void collectIndexedItems(IndexedItemCollection indexedItems,
DexMethod method, int instructionOffset) {
collectAll(indexedItems, values);
}
@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
public DexValueKind getValueKind() {
return DexValueKind.ANNOTATION;
}
@Override
public boolean isDexValueAnnotation() {
return true;
}
@Override
public DexValueAnnotation asDexValueAnnotation() {
return this;
}
@Override
public void collectIndexedItems(IndexedItemCollection indexedItems,
DexMethod method, int instructionOffset) {
value.collectIndexedItems(indexedItems, method, instructionOffset);
}
@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() {
}
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;
}
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
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, DexMethod method, int instructionOffset) {
value.collectIndexedItems(indexedItems, method, instructionOffset);
}
@Override
public AbstractValue toAbstractValue(AbstractValueFactory factory) {
return UnknownValue.getInstance();
}
}
}