All Downloads are FREE. Search and download functionalities are using the official Maven repository.

main.java.com.debughelper.tools.r8.graph.DexValue Maven / Gradle / Ivy

The newest version!
// 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.debughelper.tools.r8.graph;

import com.debughelper.tools.r8.dex.DexOutputBuffer;
import com.debughelper.tools.r8.dex.FileWriter;
import com.debughelper.tools.r8.dex.IndexedItemCollection;
import com.debughelper.tools.r8.dex.MixedSectionCollection;
import com.debughelper.tools.r8.graph.DexProto;
import com.debughelper.tools.r8.errors.Unreachable;
import com.debughelper.tools.r8.ir.code.ConstNumber;
import com.debughelper.tools.r8.ir.code.Instruction;
import com.debughelper.tools.r8.ir.code.Value;
import com.debughelper.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 static final UnknownDexValue UNKNOWN = UnknownDexValue.UNKNOWN;

  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;

  public 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 com.debughelper.tools.r8.errors.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 com.debughelper.tools.r8.errors.Unreachable(
          "Unsupported bootstrap static argument of type " + value.getClass().getSimpleName());
    }
  }

  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.
    throw new com.debughelper.tools.r8.errors.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 com.debughelper.tools.r8.errors.Unreachable("No default value for unexpected type " + type);
    }
  }

  public abstract Object getBoxedValue();

  // Returns a const instruction for the non default value.
  public com.debughelper.tools.r8.ir.code.Instruction asConstInstruction(boolean hasClassInitializer, com.debughelper.tools.r8.ir.code.Value dest) {
    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.
   * 

* This is conservative. It also considers allocations due to class loading when referencing a * field or method. */ public boolean mayTriggerAllocation() { return true; } public abstract Object asAsmEncodedObject(); static public class UnknownDexValue extends DexValue { // Singleton instance. public static final UnknownDexValue UNKNOWN = new UnknownDexValue(); private UnknownDexValue() { } @Override public void collectIndexedItems(IndexedItemCollection indexedItems, DexMethod method, int instructionOffset) { throw new com.debughelper.tools.r8.errors.Unreachable(); } @Override public void sort() { throw new com.debughelper.tools.r8.errors.Unreachable(); } @Override public boolean mayTriggerAllocation() { return true; } @Override public void writeTo(DexOutputBuffer dest, ObjectToOffsetMapping mapping) { throw new com.debughelper.tools.r8.errors.Unreachable(); } @Override public Object getBoxedValue() { throw new com.debughelper.tools.r8.errors.Unreachable(); } @Override public Object asAsmEncodedObject() { throw new com.debughelper.tools.r8.errors.Unreachable(); } @Override public int hashCode() { return System.identityHashCode(this); } @Override public boolean equals(Object other) { return other == this; } @Override public String toString() { return "UNKNOWN"; } @Override public com.debughelper.tools.r8.ir.code.Instruction asConstInstruction(boolean hasClassInitializer, com.debughelper.tools.r8.ir.code.Value dest) { return null; } } static private abstract 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 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); } public byte getValue() { return value; } @Override public Object getBoxedValue() { return getValue(); } @Override public void writeTo(DexOutputBuffer dest, ObjectToOffsetMapping mapping) { writeHeader(VALUE_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 com.debughelper.tools.r8.ir.code.Instruction asConstInstruction(boolean hasClassInitializer, com.debughelper.tools.r8.ir.code.Value dest) { return (this == DEFAULT && hasClassInitializer) ? null : new com.debughelper.tools.r8.ir.code.ConstNumber(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); } public short getValue() { return value; } @Override public Object getBoxedValue() { return getValue(); } @Override public void writeTo(DexOutputBuffer dest, ObjectToOffsetMapping mapping) { writeIntegerTo(VALUE_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 com.debughelper.tools.r8.ir.code.Instruction asConstInstruction(boolean hasClassInitializer, com.debughelper.tools.r8.ir.code.Value dest) { return (this == DEFAULT && hasClassInitializer) ? null : new com.debughelper.tools.r8.ir.code.ConstNumber(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); } public char getValue() { return value; } @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(VALUE_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 com.debughelper.tools.r8.ir.code.Instruction asConstInstruction(boolean hasClassInitializer, com.debughelper.tools.r8.ir.code.Value dest) { return (this == DEFAULT && hasClassInitializer) ? null : new com.debughelper.tools.r8.ir.code.ConstNumber(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); } public int getValue() { return value; } @Override public Object getBoxedValue() { return getValue(); } @Override public void writeTo(DexOutputBuffer dest, ObjectToOffsetMapping mapping) { writeIntegerTo(VALUE_INT, value, Integer.BYTES, dest); } @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 com.debughelper.tools.r8.ir.code.Instruction asConstInstruction(boolean hasClassInitializer, com.debughelper.tools.r8.ir.code.Value dest) { return (this == DEFAULT && hasClassInitializer) ? null : new com.debughelper.tools.r8.ir.code.ConstNumber(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); } public long getValue() { return value; } @Override public Object getBoxedValue() { return getValue(); } @Override public void writeTo(DexOutputBuffer dest, ObjectToOffsetMapping mapping) { writeIntegerTo(VALUE_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 com.debughelper.tools.r8.ir.code.Instruction asConstInstruction(boolean hasClassInitializer, com.debughelper.tools.r8.ir.code.Value dest) { return (this == DEFAULT && hasClassInitializer) ? null : new com.debughelper.tools.r8.ir.code.ConstNumber(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); } public float getValue() { return value; } @Override public Object getBoxedValue() { return getValue(); } @Override public void writeTo(DexOutputBuffer dest, ObjectToOffsetMapping mapping) { dest.forward(1); int length = com.debughelper.tools.r8.utils.EncodedValueUtils.putFloat(dest, value); dest.rewind(length + 1); writeHeader(VALUE_FLOAT, length - 1, dest); dest.forward(length); } @Override public Object asAsmEncodedObject() { return Float.valueOf(value); } @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); } public double getValue() { return value; } @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(VALUE_DOUBLE, length - 1, dest); dest.forward(length); } @Override public Object asAsmEncodedObject() { return Double.valueOf(value); } @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 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 Object getBoxedValue() { throw new com.debughelper.tools.r8.errors.Unreachable("No boxed value for DexValue " + this.getClass().getSimpleName()); } @Override public Object asAsmEncodedObject() { throw new com.debughelper.tools.r8.errors.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(); } @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 { public DexValueString(DexString value) { super(value); } public DexString getValue() { return value; } @Override public Object asAsmEncodedObject() { return value.toString(); } @Override protected byte getValueKind() { return VALUE_STRING; } } static public class DexValueType extends NestedDexValue { public DexValueType(DexType value) { super(value); } @Override protected byte getValueKind() { return VALUE_TYPE; } @Override public void collectIndexedItems(IndexedItemCollection indexedItems, DexMethod method, int instructionOffset) { value.collectIndexedItems(indexedItems, method, instructionOffset); } } static public class DexValueField extends NestedDexValue { public DexValueField(DexField value) { super(value); } @Override protected byte getValueKind() { return VALUE_FIELD; } @Override public void collectIndexedItems(IndexedItemCollection indexedItems, DexMethod method, int instructionOffset) { value.collectIndexedItems(indexedItems, method, instructionOffset); } } static public class DexValueMethod extends NestedDexValue { public DexValueMethod(DexMethod value) { super(value); } @Override protected byte getValueKind() { return VALUE_METHOD; } @Override public void collectIndexedItems(IndexedItemCollection indexedItems, DexMethod method, int instructionOffset) { value.collectIndexedItems(indexedItems, method, instructionOffset); } } static public class DexValueEnum extends NestedDexValue { public DexValueEnum(DexField value) { super(value); } @Override protected byte getValueKind() { return VALUE_ENUM; } @Override public void collectIndexedItems(IndexedItemCollection indexedItems, DexMethod method, int instructionOffset) { value.collectIndexedItems(indexedItems, method, instructionOffset); } } static public class DexValueMethodType extends NestedDexValue { public DexValueMethodType(DexProto value) { super(value); } @Override protected byte getValueKind() { return VALUE_METHOD_TYPE; } @Override public void collectIndexedItems(IndexedItemCollection indexedItems, DexMethod method, int instructionOffset) { value.collectIndexedItems(indexedItems, method, instructionOffset); } } 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, DexMethod method, int instructionOffset) { 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 Object getBoxedValue() { throw new com.debughelper.tools.r8.errors.Unreachable("No boxed value for DexValueArray"); } @Override public Object asAsmEncodedObject() { throw new com.debughelper.tools.r8.errors.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); } } static public class DexValueAnnotation extends DexValue { public final DexEncodedAnnotation value; public DexValueAnnotation(DexEncodedAnnotation value) { this.value = value; } @Override public void collectIndexedItems(IndexedItemCollection indexedItems, DexMethod method, int instructionOffset) { value.collectIndexedItems(indexedItems, method, instructionOffset); } @Override public void writeTo(DexOutputBuffer dest, ObjectToOffsetMapping mapping) { writeHeader(VALUE_ANNOTATION, 0, dest); FileWriter.writeEncodedAnnotation(value, dest, mapping); } @Override public Object getBoxedValue() { throw new com.debughelper.tools.r8.errors.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; } } static public class DexValueNull extends SimpleDexValue { public static final DexValue NULL = new DexValueNull(); // See DexValueNull.NULL private DexValueNull() { } public Object getValue() { return null; } @Override public void writeTo(DexOutputBuffer dest, ObjectToOffsetMapping mapping) { writeHeader(VALUE_NULL, 0, dest); } @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"; } } 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; } public boolean getValue() { return value; } @Override public Object getBoxedValue() { return getValue(); } @Override public void writeTo(DexOutputBuffer dest, ObjectToOffsetMapping mapping) { writeHeader(VALUE_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 Instruction asConstInstruction(boolean hasClassInitializer, Value dest) { return (this == DEFAULT && hasClassInitializer) ? null : new ConstNumber(dest, value ? 1 : 0); } } static public class DexValueMethodHandle extends NestedDexValue { public DexValueMethodHandle(DexMethodHandle value) { super(value); } @Override protected byte getValueKind() { return VALUE_METHOD_HANDLE; } @Override public void collectIndexedItems(IndexedItemCollection indexedItems, DexMethod method, int instructionOffset) { value.collectIndexedItems(indexedItems, method, instructionOffset); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy