blob: 08c8a1cee4eae44f3ff2af83692a6a42145e8f7b [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 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.code.ConstNumber;
import com.android.tools.r8.ir.code.ConstType;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.utils.EncodedValueUtils;
import java.util.Arrays;
public abstract class DexValue extends DexItem {
public static final byte VALUE_BYTE = 0x00;
public static final byte VALUE_SHORT = 0x02;
public static final byte VALUE_CHAR = 0x03;
public static final byte VALUE_INT = 0x04;
public static final byte VALUE_LONG = 0x06;
public static final byte VALUE_FLOAT = 0x10;
public static final byte VALUE_DOUBLE = 0x11;
public static final byte VALUE_METHOD_TYPE = 0x15;
public static final byte VALUE_METHOD_HANDLE = 0x16;
public static final byte VALUE_STRING = 0x17;
public static final byte VALUE_TYPE = 0x18;
public static final byte VALUE_FIELD = 0x19;
public static final byte VALUE_METHOD = 0x1a;
public static final byte VALUE_ENUM = 0x1b;
public static final byte VALUE_ARRAY = 0x1c;
public static final byte VALUE_ANNOTATION = 0x1d;
public static final byte VALUE_NULL = 0x1e;
public static final byte VALUE_BOOLEAN = 0x1f;
private static void writeHeader(byte type, int arg, DexOutputBuffer dest) {
dest.putByte((byte) ((arg << 5) | type));
}
@Override
void collectMixedSectionItems(MixedSectionCollection mixedItems) {
// Should never be visited.
assert false;
}
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, DexItemFactory factory) {
if (type == factory.booleanType) {
return DexValueBoolean.DEFAULT;
}
if (type == factory.byteType) {
return DexValueByte.DEFAULT;
}
if (type == factory.charType) {
return DexValueChar.DEFAULT;
}
if (type == factory.shortType) {
return DexValueShort.DEFAULT;
}
if (type == factory.intType) {
return DexValueInt.DEFAULT;
}
if (type == factory.longType) {
return DexValueLong.DEFAULT;
}
if (type == factory.floatType) {
return DexValueFloat.DEFAULT;
}
if (type == factory.doubleType) {
return DexValueDouble.DEFAULT;
}
if (type.isArrayType() || type.isClassType()) {
return DexValueNull.NULL;
}
throw new Unreachable("No default value for unexpected type " + type);
}
// Returns a const instruction for the non default value.
public Instruction asConstInstruction(boolean hasClassInitializer, Value dest) {
return null;
}
public boolean isDefault(DexType type, DexItemFactory factory) {
return this == defaultForType(type, factory);
}
/**
* Whether creating this value as a default value for a field might trigger an allocation.
* <p>
* This is conservative.
*/
public boolean mayTriggerAllocation() {
return true;
}
static private abstract class SimpleDexValue extends DexValue {
@Override
public void collectIndexedItems(IndexedItemCollection indexedItems) {
// Intentionally left empty
}
@Override
public void sort() {
// Intentionally empty
}
@Override
public boolean mayTriggerAllocation() {
return false;
}
protected static void writeIntegerTo(byte type, long value, int expected,
DexOutputBuffer dest) {
// Leave space for header.
dest.forward(1);
int length = dest.putSignedEncodedValue(value, expected);
dest.rewind(length + 1);
writeHeader(type, length - 1, dest);
dest.forward(length);
}
}
static public class DexValueByte extends SimpleDexValue {
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
public void writeTo(DexOutputBuffer dest, ObjectToOffsetMapping mapping) {
writeHeader(VALUE_BYTE, 0, dest);
dest.putSignedEncodedValue(value, 1);
}
@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 Instruction asConstInstruction(boolean hasClassInitializer, Value dest) {
return (this == DEFAULT && hasClassInitializer)
? null
: new ConstNumber(ConstType.INT, dest, value);
}
}
static public class DexValueShort extends SimpleDexValue {
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
public void writeTo(DexOutputBuffer dest, ObjectToOffsetMapping mapping) {
writeIntegerTo(VALUE_SHORT, value, Short.BYTES, dest);
}
@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 Instruction asConstInstruction(boolean hasClassInitializer, Value dest) {
return (this == DEFAULT && hasClassInitializer)
? null
: new ConstNumber(ConstType.INT, dest, value);
}
}
static public class DexValueChar extends SimpleDexValue {
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
public void writeTo(DexOutputBuffer dest, ObjectToOffsetMapping mapping) {
dest.forward(1);
int length = dest.putUnsignedEncodedValue(value, 2);
dest.rewind(length + 1);
writeHeader(VALUE_CHAR, length - 1, dest);
dest.forward(length);
}
@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 Instruction asConstInstruction(boolean hasClassInitializer, Value dest) {
return (this == DEFAULT && hasClassInitializer)
? null
: new ConstNumber(ConstType.INT, dest, value);
}
}
static public class DexValueInt extends SimpleDexValue {
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
public void writeTo(DexOutputBuffer dest, ObjectToOffsetMapping mapping) {
writeIntegerTo(VALUE_INT, value, Integer.BYTES, dest);
}
@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 Instruction asConstInstruction(boolean hasClassInitializer, Value dest) {
return (this == DEFAULT && hasClassInitializer)
? null
: new ConstNumber(ConstType.INT, dest, value);
}
}
static public class DexValueLong extends SimpleDexValue {
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
public void writeTo(DexOutputBuffer dest, ObjectToOffsetMapping mapping) {
writeIntegerTo(VALUE_LONG, value, Long.BYTES, dest);
}
@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 Instruction asConstInstruction(boolean hasClassInitializer, Value dest) {
return (this == DEFAULT && hasClassInitializer)
? null
: new ConstNumber(ConstType.LONG, dest, value);
}
}
static public class DexValueFloat extends SimpleDexValue {
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
public void writeTo(DexOutputBuffer dest, ObjectToOffsetMapping mapping) {
dest.forward(1);
int length = EncodedValueUtils.putFloat(dest, value);
dest.rewind(length + 1);
writeHeader(VALUE_FLOAT, length - 1, dest);
dest.forward(length);
}
@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;
}
}
static public class DexValueDouble extends SimpleDexValue {
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
public void writeTo(DexOutputBuffer dest, ObjectToOffsetMapping mapping) {
dest.forward(1);
int length = EncodedValueUtils.putDouble(dest, value);
dest.rewind(length + 1);
writeHeader(VALUE_DOUBLE, length - 1, dest);
dest.forward(length);
}
@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;
}
protected abstract byte getValueKind();
@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 void collectIndexedItems(IndexedItemCollection indexedItems) {
value.collectIndexedItems(indexedItems);
}
@Override
public void sort() {
// Intentionally empty.
}
@Override
public int hashCode() {
return value.hashCode() * 7 + getValueKind();
}
@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
protected byte getValueKind() {
return VALUE_STRING;
}
}
static public class DexValueType extends NestedDexValue<DexType> {
public DexValueType(DexType value) {
super(value);
}
@Override
protected byte getValueKind() {
return VALUE_TYPE;
}
@Override
public void collectIndexedItems(IndexedItemCollection indexedItems) {
value.collectIndexedItems(indexedItems);
}
}
static public class DexValueField extends NestedDexValue<DexField> {
public DexValueField(DexField value) {
super(value);
}
@Override
protected byte getValueKind() {
return VALUE_FIELD;
}
@Override
public void collectIndexedItems(IndexedItemCollection indexedItems) {
value.collectIndexedItems(indexedItems);
}
}
static public class DexValueMethod extends NestedDexValue<DexMethod> {
public DexValueMethod(DexMethod value) {
super(value);
}
@Override
protected byte getValueKind() {
return VALUE_METHOD;
}
@Override
public void collectIndexedItems(IndexedItemCollection indexedItems) {
value.collectIndexedItems(indexedItems);
}
}
static public class DexValueEnum extends NestedDexValue<DexField> {
public DexValueEnum(DexField value) {
super(value);
}
@Override
protected byte getValueKind() {
return VALUE_ENUM;
}
@Override
public void collectIndexedItems(IndexedItemCollection indexedItems) {
value.collectIndexedItems(indexedItems);
}
}
static public class DexValueMethodType extends NestedDexValue<DexProto> {
public DexValueMethodType(DexProto value) {
super(value);
}
@Override
protected byte getValueKind() {
return VALUE_METHOD_TYPE;
}
@Override
public void collectIndexedItems(IndexedItemCollection indexedItems) {
value.collectIndexedItems(indexedItems);
}
}
static public class DexValueArray extends DexValue {
final DexValue[] values;
public DexValueArray(DexValue[] values) {
this.values = values;
}
public DexValue[] getValues() {
return values;
}
@Override
public void collectIndexedItems(IndexedItemCollection indexedItems) {
collectAll(indexedItems, values);
}
@Override
public void writeTo(DexOutputBuffer dest, ObjectToOffsetMapping mapping) {
writeHeader(VALUE_ARRAY, 0, dest);
dest.putUleb128(values.length);
for (DexValue value : values) {
value.writeTo(dest, mapping);
}
}
@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);
}
}
static public class DexValueAnnotation extends DexValue {
public final DexEncodedAnnotation value;
public DexValueAnnotation(DexEncodedAnnotation value) {
this.value = value;
}
@Override
public void collectIndexedItems(IndexedItemCollection indexedItems) {
value.collectIndexedItems(indexedItems);
}
@Override
public void writeTo(DexOutputBuffer dest, ObjectToOffsetMapping mapping) {
writeHeader(VALUE_ANNOTATION, 0, dest);
FileWriter.writeEncodedAnnotation(value, dest, mapping);
}
@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;
}
}
static public class DexValueNull extends SimpleDexValue {
public static final DexValue NULL = new DexValueNull();
// See DexValueNull.NULL
private DexValueNull() {
}
@Override
public void writeTo(DexOutputBuffer dest, ObjectToOffsetMapping mapping) {
writeHeader(VALUE_NULL, 0, dest);
}
@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";
}
}
static public class DexValueBoolean extends SimpleDexValue {
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
public void writeTo(DexOutputBuffer dest, ObjectToOffsetMapping mapping) {
writeHeader(VALUE_BOOLEAN, value ? 1 : 0, dest);
}
@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 Instruction asConstInstruction(boolean hasClassInitializer, Value dest) {
return (this == DEFAULT && hasClassInitializer)
? null
: new ConstNumber(ConstType.INT, dest, value ? 1 : 0);
}
}
static public class DexValueMethodHandle extends NestedDexValue<DexMethodHandle> {
public DexValueMethodHandle(DexMethodHandle value) {
super(value);
}
@Override
protected byte getValueKind() {
return VALUE_METHOD_HANDLE;
}
@Override
public void collectIndexedItems(IndexedItemCollection indexedItems) {
value.collectIndexedItems(indexedItems);
}
}
}