Add support for method handle and call site references in ReferenceUtil

This commit is contained in:
Ben Gruver 2018-05-21 15:17:19 -07:00
parent d82252e4d6
commit e8760aa46b
2 changed files with 184 additions and 7 deletions

View File

@ -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<? extends AnnotationElement> 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() {}
}

View File

@ -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;
}