diff --git a/dexlib2/src/main/java/org/jf/dexlib2/util/EncodedValueUtils.java b/dexlib2/src/main/java/org/jf/dexlib2/util/EncodedValueUtils.java index e8c66258..a09bbca2 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/util/EncodedValueUtils.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/util/EncodedValueUtils.java @@ -32,7 +32,13 @@ package org.jf.dexlib2.util; import org.jf.dexlib2.ValueType; +import org.jf.dexlib2.iface.AnnotationElement; import org.jf.dexlib2.iface.value.*; +import org.jf.util.StringUtils; + +import java.io.IOException; +import java.io.Writer; +import java.util.Set; public final class EncodedValueUtils { public static boolean isDefaultValue(EncodedValue encodedValue) { @@ -59,5 +65,99 @@ public final class EncodedValueUtils { return false; } + public static void writeEncodedValue(Writer writer, EncodedValue encodedValue) throws IOException { + switch (encodedValue.getValueType()) { + case ValueType.BOOLEAN: + writer.write(Boolean.toString(((BooleanEncodedValue) encodedValue).getValue())); + break; + case ValueType.BYTE: + writer.write(Byte.toString(((ByteEncodedValue)encodedValue).getValue())); + break; + case ValueType.CHAR: + writer.write(Integer.toString(((CharEncodedValue)encodedValue).getValue())); + break; + case ValueType.SHORT: + writer.write(Short.toString(((ShortEncodedValue)encodedValue).getValue())); + break; + case ValueType.INT: + writer.write(Integer.toString(((IntEncodedValue)encodedValue).getValue())); + break; + case ValueType.LONG: + writer.write(Long.toString(((LongEncodedValue)encodedValue).getValue())); + break; + case ValueType.FLOAT: + writer.write(Float.toString(((FloatEncodedValue)encodedValue).getValue())); + break; + case ValueType.DOUBLE: + writer.write(Double.toString(((DoubleEncodedValue)encodedValue).getValue())); + break; + case ValueType.ANNOTATION: + writeAnnotation(writer, (AnnotationEncodedValue)encodedValue); + break; + case ValueType.ARRAY: + writeArray(writer, (ArrayEncodedValue)encodedValue); + break; + case ValueType.STRING: + writer.write('"'); + StringUtils.writeEscapedString(writer, ((StringEncodedValue)encodedValue).getValue()); + writer.write('"'); + break; + case ValueType.FIELD: + ReferenceUtil.writeFieldDescriptor(writer, ((FieldEncodedValue)encodedValue).getValue()); + break; + case ValueType.ENUM: + ReferenceUtil.writeFieldDescriptor(writer, ((EnumEncodedValue)encodedValue).getValue()); + break; + case ValueType.METHOD: + ReferenceUtil.writeMethodDescriptor(writer, ((MethodEncodedValue)encodedValue).getValue()); + break; + case ValueType.TYPE: + writer.write(((TypeEncodedValue)encodedValue).getValue()); + break; + case ValueType.METHOD_TYPE: + ReferenceUtil.writeMethodProtoDescriptor(writer, ((MethodTypeEncodedValue)encodedValue).getValue()); + break; + case ValueType.METHOD_HANDLE: + ReferenceUtil.writeMethodHandle(writer, ((MethodHandleEncodedValue)encodedValue).getValue()); + break; + case ValueType.NULL: + writer.write("null"); + break; + default: + throw new IllegalArgumentException("Unknown encoded value type"); + } + } + + private static void writeAnnotation(Writer writer, AnnotationEncodedValue annotation) throws IOException { + writer.write("Annotation["); + writer.write(annotation.getType()); + + Set elements = annotation.getElements(); + for (AnnotationElement element: elements) { + writer.write(", "); + writer.write(element.getName()); + writer.write('='); + writeEncodedValue(writer, element.getValue()); + } + + writer.write(']'); + } + + private static void writeArray(Writer writer, ArrayEncodedValue array) throws IOException { + writer.write("Array["); + + boolean first = true; + for (EncodedValue element: array.getValue()) { + if (first) { + first = false; + } else { + writer.write(", "); + } + writeEncodedValue(writer, element); + } + + writer.write(']'); + } + private EncodedValueUtils() {} } diff --git a/dexlib2/src/main/java/org/jf/dexlib2/util/ReferenceUtil.java b/dexlib2/src/main/java/org/jf/dexlib2/util/ReferenceUtil.java index 4e46a0e9..9e594d9d 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/util/ReferenceUtil.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/util/ReferenceUtil.java @@ -31,12 +31,15 @@ package org.jf.dexlib2.util; +import org.jf.dexlib2.MethodHandleType; import org.jf.dexlib2.iface.reference.*; +import org.jf.dexlib2.iface.value.EncodedValue; import org.jf.util.StringUtils; import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.io.IOException; +import java.io.StringWriter; import java.io.Writer; public final class ReferenceUtil { @@ -61,14 +64,24 @@ public final class ReferenceUtil { } public static String getMethodProtoDescriptor(MethodProtoReference methodProtoReference) { - StringBuilder sb = new StringBuilder(); - sb.append('('); - for (CharSequence paramType : methodProtoReference.getParameterTypes()) { - sb.append(paramType); + StringWriter stringWriter = new StringWriter(); + try { + writeMethodProtoDescriptor(stringWriter, methodProtoReference); + } catch (IOException ex) { + // IOException shouldn't happen for a StringWriter... + throw new RuntimeException(ex); } - sb.append(')'); - sb.append(methodProtoReference.getReturnType()); - return sb.toString(); + return stringWriter.toString(); + } + + public static void writeMethodProtoDescriptor(Writer writer, MethodProtoReference methodProtoReference) + throws IOException { + writer.write('('); + for (CharSequence paramType : methodProtoReference.getParameterTypes()) { + writer.write(paramType.toString()); + } + writer.write(')'); + writer.write(methodProtoReference.getReturnType()); } public static void writeMethodDescriptor(Writer writer, MethodReference methodReference) throws IOException { @@ -129,6 +142,62 @@ public final class ReferenceUtil { writer.write(fieldReference.getType()); } + public static String getMethodHandleString(MethodHandleReference methodHandleReference) { + StringWriter stringWriter = new StringWriter(); + try { + writeMethodHandle(stringWriter, methodHandleReference); + } catch (IOException ex) { + // IOException shouldn't happen for a StringWriter... + throw new RuntimeException(ex); + } + return stringWriter.toString(); + } + + public static void writeMethodHandle(Writer writer, MethodHandleReference methodHandleReference) + throws IOException { + writer.write(MethodHandleType.toString(methodHandleReference.getMethodHandleType())); + writer.write('@'); + + Reference memberReference = methodHandleReference.getMemberReference(); + if (memberReference instanceof MethodReference) { + writeMethodDescriptor(writer, (MethodReference)memberReference); + } else { + writeFieldDescriptor(writer, (FieldReference)memberReference); + } + } + + public static String getCallSiteString(CallSiteReference callSiteReference) { + StringWriter stringWriter = new StringWriter(); + try { + writeCallSite(stringWriter, callSiteReference); + } catch (IOException ex) { + // IOException shouldn't happen for a StringWriter... + throw new RuntimeException(ex); + } + return stringWriter.toString(); + } + + public static void writeCallSite(Writer writer, CallSiteReference callSiteReference) throws IOException { + writer.write(callSiteReference.getName()); + writer.write('('); + writer.write('"'); + StringUtils.writeEscapedString(writer, callSiteReference.getMethodName()); + writer.write('"'); + writer.write(", "); + writeMethodProtoDescriptor(writer, callSiteReference.getMethodProto()); + + for (EncodedValue encodedValue : callSiteReference.getExtraArguments()) { + writer.write(", "); + EncodedValueUtils.writeEncodedValue(writer, encodedValue); + } + writer.write(")@"); + MethodHandleReference methodHandle = callSiteReference.getMethodHandle(); + if (methodHandle.getMethodHandleType() != MethodHandleType.STATIC_INVOKE) { + throw new IllegalArgumentException("The linker method handle for a call site must be of type static-invoke"); + } + writeMethodDescriptor(writer, (MethodReference)callSiteReference.getMethodHandle().getMemberReference()); + } + @Nullable public static String getReferenceString(@Nonnull Reference reference) { return getReferenceString(reference, null); @@ -156,6 +225,14 @@ public final class ReferenceUtil { MethodProtoReference methodProtoReference = (MethodProtoReference)reference; return getMethodProtoDescriptor(methodProtoReference); } + if (reference instanceof MethodHandleReference) { + MethodHandleReference methodHandleReference = (MethodHandleReference)reference; + return getMethodHandleString(methodHandleReference); + } + if (reference instanceof CallSiteReference) { + CallSiteReference callSiteReference = (CallSiteReference)reference; + return getCallSiteString(callSiteReference); + } return null; }