mirror of
https://github.com/revanced/smali.git
synced 2025-05-21 00:17:05 +02:00
Add new DexFormatter/DexFormattedWriter class
This will be a more unified way to get/write out human-readable strings for individual "things" from a dex file. The goal is to replace things like ReferenceUtil, EncodedValueUtils, etc.
This commit is contained in:
parent
812bf35149
commit
e894435e9d
@ -0,0 +1,445 @@
|
||||
/*
|
||||
* Copyright 2021, Google Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following disclaimer
|
||||
* in the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* * Neither the name of Google Inc. nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package org.jf.dexlib2.formatter;
|
||||
|
||||
import org.jf.dexlib2.MethodHandleType;
|
||||
import org.jf.dexlib2.ValueType;
|
||||
import org.jf.dexlib2.iface.AnnotationElement;
|
||||
import org.jf.dexlib2.iface.reference.*;
|
||||
import org.jf.dexlib2.iface.value.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* This class handles formatting and writing various types of items in a dex file to a Writer.
|
||||
*/
|
||||
public class DexFormattedWriter extends Writer {
|
||||
|
||||
protected final Writer writer;
|
||||
|
||||
public DexFormattedWriter(Writer writer) {
|
||||
this.writer = writer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the method descriptor for the given {@link MethodReference}.
|
||||
*/
|
||||
public void writeMethodDescriptor(MethodReference methodReference) throws IOException {
|
||||
writeType(methodReference.getDefiningClass());
|
||||
writer.write("->");
|
||||
writeSimpleName(methodReference.getName());
|
||||
writer.write('(');
|
||||
for (CharSequence paramType: methodReference.getParameterTypes()) {
|
||||
writeType(paramType);
|
||||
}
|
||||
writer.write(')');
|
||||
writeType(methodReference.getReturnType());
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the short method descriptor for the given {@link MethodReference}.
|
||||
*
|
||||
* <p>The short method descriptor elides the class that the field is a member of.
|
||||
*/
|
||||
public void writeShortMethodDescriptor(MethodReference methodReference) throws IOException {
|
||||
writeSimpleName(methodReference.getName());
|
||||
writer.write('(');
|
||||
for (CharSequence paramType: methodReference.getParameterTypes()) {
|
||||
writeType(paramType);
|
||||
}
|
||||
writer.write(')');
|
||||
writeType(methodReference.getReturnType());
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the method proto descriptor for the given {@link MethodProtoReference}.
|
||||
*/
|
||||
public void writeMethodProtoDescriptor(MethodProtoReference protoReference) throws IOException {
|
||||
writer.write('(');
|
||||
for (CharSequence paramType : protoReference.getParameterTypes()) {
|
||||
writeType(paramType);
|
||||
}
|
||||
writer.write(')');
|
||||
writeType(protoReference.getReturnType());
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the field descriptor for the given {@link FieldReference}.
|
||||
*/
|
||||
public void writeFieldDescriptor(FieldReference fieldReference) throws IOException {
|
||||
writeType(fieldReference.getDefiningClass());
|
||||
writer.write("->");
|
||||
writeSimpleName(fieldReference.getName());
|
||||
writer.write(':');
|
||||
writeType(fieldReference.getType());
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the short field descriptor for the given {@link FieldReference}.
|
||||
*
|
||||
* <p>The short field descriptor typically elides the class that the field is a member of.
|
||||
*/
|
||||
public void writeShortFieldDescriptor(FieldReference fieldReference) throws IOException {
|
||||
writeSimpleName(fieldReference.getName());
|
||||
writer.write(':');
|
||||
writeType(fieldReference.getType());
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the given {@link MethodHandleReference}.
|
||||
*/
|
||||
public void writeMethodHandle(MethodHandleReference methodHandleReference) throws IOException {
|
||||
writer.write(MethodHandleType.toString(methodHandleReference.getMethodHandleType()));
|
||||
writer.write('@');
|
||||
|
||||
Reference memberReference = methodHandleReference.getMemberReference();
|
||||
if (memberReference instanceof MethodReference) {
|
||||
writeMethodDescriptor((MethodReference)memberReference);
|
||||
} else {
|
||||
writeFieldDescriptor((FieldReference)memberReference);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the given {@link CallSiteReference}.
|
||||
*/
|
||||
public void writeCallSite(CallSiteReference callSiteReference) throws IOException {
|
||||
writeSimpleName(callSiteReference.getName());
|
||||
writer.write('(');
|
||||
writeQuotedString(callSiteReference.getMethodName());
|
||||
writer.write(", ");
|
||||
writeMethodProtoDescriptor(callSiteReference.getMethodProto());
|
||||
|
||||
for (EncodedValue encodedValue : callSiteReference.getExtraArguments()) {
|
||||
writer.write(", ");
|
||||
writeEncodedValue(encodedValue);
|
||||
}
|
||||
writer.write(")@");
|
||||
MethodHandleReference methodHandle = callSiteReference.getMethodHandle();
|
||||
if (methodHandle.getMethodHandleType() != MethodHandleType.INVOKE_STATIC) {
|
||||
throw new IllegalArgumentException("The linker method handle for a call site must be of type invoke-static");
|
||||
}
|
||||
writeMethodDescriptor((MethodReference)callSiteReference.getMethodHandle().getMemberReference());
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the given type.
|
||||
*/
|
||||
public void writeType(CharSequence type) throws IOException {
|
||||
for (int i = 0; i < type.length(); i++) {
|
||||
char c = type.charAt(i);
|
||||
if (c == 'L') {
|
||||
writeClass(type.subSequence(i, type.length()));
|
||||
return;
|
||||
} else if (c == '[') {
|
||||
writer.write(c);
|
||||
} else if (c == 'Z' ||
|
||||
c == 'B' ||
|
||||
c == 'S' ||
|
||||
c == 'C' ||
|
||||
c == 'I' ||
|
||||
c == 'J' ||
|
||||
c == 'F' ||
|
||||
c == 'D' ||
|
||||
c == 'V') {
|
||||
writer.write(c);
|
||||
|
||||
if (i != type.length() - 1) {
|
||||
throw new IllegalArgumentException(
|
||||
String.format("Invalid type string: %s", type));
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
throw new IllegalArgumentException(
|
||||
String.format("Invalid type string: %s", type));
|
||||
}
|
||||
}
|
||||
|
||||
// Any valid type would have returned from within the loop.
|
||||
throw new IllegalArgumentException(
|
||||
String.format("Invalid type string: %s", type));
|
||||
}
|
||||
|
||||
protected void writeClass(CharSequence type) throws IOException {
|
||||
assert type.charAt(0) == 'L';
|
||||
|
||||
writer.write(type.charAt(0));
|
||||
|
||||
int startIndex = 1;
|
||||
int i;
|
||||
for (i = startIndex; i < type.length(); i++) {
|
||||
char c = type.charAt(i);
|
||||
|
||||
if (c == '/') {
|
||||
if (i == startIndex) {
|
||||
throw new IllegalArgumentException(
|
||||
String.format("Invalid type string: %s", type));
|
||||
}
|
||||
|
||||
writeSimpleName(type.subSequence(startIndex, i));
|
||||
writer.write(type.charAt(i));
|
||||
startIndex = i+1;
|
||||
} else if (c == ';') {
|
||||
if (i == startIndex) {
|
||||
throw new IllegalArgumentException(
|
||||
String.format("Invalid type string: %s", type));
|
||||
}
|
||||
|
||||
writeSimpleName(type.subSequence(startIndex, i));
|
||||
writer.write(type.charAt(i));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i != type.length() - 1 || type.charAt(i) != ';') {
|
||||
throw new IllegalArgumentException(
|
||||
String.format("Invalid type string: %s", type));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the given simple name.
|
||||
*
|
||||
* @param simpleName The <a href="https://source.android.com/devices/tech/dalvik/dex-format#simplename">simple name</a>
|
||||
* to write.
|
||||
*/
|
||||
protected void writeSimpleName(CharSequence simpleName) throws IOException {
|
||||
writer.append(simpleName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the given quoted string.
|
||||
*
|
||||
* <p>This includes the beginning and ending quotation marks, and the string value is be escaped as necessary.
|
||||
*/
|
||||
public void writeQuotedString(CharSequence charSequence) throws IOException {
|
||||
writer.write('"');
|
||||
|
||||
String string = charSequence.toString();
|
||||
for (int i = 0; i < string.length(); i++) {
|
||||
char c = string.charAt(i);
|
||||
|
||||
if ((c >= ' ') && (c < 0x7f)) {
|
||||
if ((c == '\'') || (c == '\"') || (c == '\\')) {
|
||||
writer.write('\\');
|
||||
}
|
||||
writer.write(c);
|
||||
continue;
|
||||
} else if (c <= 0x7f) {
|
||||
switch (c) {
|
||||
case '\n': writer.write("\\n"); continue;
|
||||
case '\r': writer.write("\\r"); continue;
|
||||
case '\t': writer.write("\\t"); continue;
|
||||
}
|
||||
}
|
||||
|
||||
writer.write("\\u");
|
||||
writer.write(Character.forDigit(c >> 12, 16));
|
||||
writer.write(Character.forDigit((c >> 8) & 0x0f, 16));
|
||||
writer.write(Character.forDigit((c >> 4) & 0x0f, 16));
|
||||
writer.write(Character.forDigit(c & 0x0f, 16));
|
||||
}
|
||||
|
||||
writer.write('"');
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the given {@link EncodedValue}.
|
||||
*/
|
||||
public void writeEncodedValue(EncodedValue encodedValue) throws IOException {
|
||||
switch (encodedValue.getValueType()) {
|
||||
case ValueType.BOOLEAN:
|
||||
writer.write(Boolean.toString(((BooleanEncodedValue) encodedValue).getValue()));
|
||||
break;
|
||||
case ValueType.BYTE:
|
||||
writer.write(
|
||||
String.format("0x%x", ((ByteEncodedValue)encodedValue).getValue()));
|
||||
break;
|
||||
case ValueType.CHAR:
|
||||
writer.write(
|
||||
String.format("0x%x", (int)((CharEncodedValue)encodedValue).getValue()));
|
||||
break;
|
||||
case ValueType.SHORT:
|
||||
writer.write(
|
||||
String.format("0x%x", ((ShortEncodedValue)encodedValue).getValue()));
|
||||
break;
|
||||
case ValueType.INT:
|
||||
writer.write(
|
||||
String.format("0x%x", ((IntEncodedValue)encodedValue).getValue()));
|
||||
break;
|
||||
case ValueType.LONG:
|
||||
writer.write(
|
||||
String.format("0x%x", ((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((AnnotationEncodedValue)encodedValue);
|
||||
break;
|
||||
case ValueType.ARRAY:
|
||||
writeArray((ArrayEncodedValue)encodedValue);
|
||||
break;
|
||||
case ValueType.STRING:
|
||||
writeQuotedString(((StringEncodedValue)encodedValue).getValue());
|
||||
break;
|
||||
case ValueType.FIELD:
|
||||
writeFieldDescriptor(((FieldEncodedValue)encodedValue).getValue());
|
||||
break;
|
||||
case ValueType.ENUM:
|
||||
writeFieldDescriptor(((EnumEncodedValue)encodedValue).getValue());
|
||||
break;
|
||||
case ValueType.METHOD:
|
||||
writeMethodDescriptor(((MethodEncodedValue)encodedValue).getValue());
|
||||
break;
|
||||
case ValueType.TYPE:
|
||||
writeType(((TypeEncodedValue)encodedValue).getValue());
|
||||
break;
|
||||
case ValueType.METHOD_TYPE:
|
||||
writeMethodProtoDescriptor(((MethodTypeEncodedValue)encodedValue).getValue());
|
||||
break;
|
||||
case ValueType.METHOD_HANDLE:
|
||||
writeMethodHandle(((MethodHandleEncodedValue)encodedValue).getValue());
|
||||
break;
|
||||
case ValueType.NULL:
|
||||
writer.write("null");
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown encoded value type");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the given {@link AnnotationEncodedValue}.
|
||||
*/
|
||||
protected void writeAnnotation(AnnotationEncodedValue annotation) throws IOException {
|
||||
writer.write("Annotation[");
|
||||
writeType(annotation.getType());
|
||||
|
||||
Set<? extends AnnotationElement> elements = annotation.getElements();
|
||||
for (AnnotationElement element: elements) {
|
||||
writer.write(", ");
|
||||
writeSimpleName(element.getName());
|
||||
writer.write('=');
|
||||
writeEncodedValue(element.getValue());
|
||||
}
|
||||
|
||||
writer.write(']');
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the given {@link ArrayEncodedValue}.
|
||||
*/
|
||||
protected void writeArray(ArrayEncodedValue array) throws IOException {
|
||||
writer.write("Array[");
|
||||
|
||||
boolean first = true;
|
||||
for (EncodedValue element: array.getValue()) {
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
writer.write(", ");
|
||||
}
|
||||
writeEncodedValue(element);
|
||||
}
|
||||
|
||||
writer.write(']');
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the given {@link Reference}.
|
||||
*/
|
||||
public void writeReference(Reference reference) throws IOException {
|
||||
if (reference instanceof StringReference) {
|
||||
writeQuotedString((StringReference) reference);
|
||||
} else if (reference instanceof TypeReference) {
|
||||
writeType((TypeReference) reference);
|
||||
} else if (reference instanceof FieldReference) {
|
||||
writeFieldDescriptor((FieldReference) reference);
|
||||
} else if (reference instanceof MethodReference) {
|
||||
writeMethodDescriptor((MethodReference) reference);
|
||||
} else if (reference instanceof MethodProtoReference) {
|
||||
writeMethodProtoDescriptor((MethodProtoReference) reference);
|
||||
} else if (reference instanceof MethodHandleReference) {
|
||||
writeMethodHandle((MethodHandleReference) reference);
|
||||
} else if (reference instanceof CallSiteReference) {
|
||||
writeCallSite((CallSiteReference) reference);
|
||||
} else {
|
||||
throw new IllegalArgumentException(String.format("Not a known reference type: %s", reference.getClass()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override public void write(int c) throws IOException {
|
||||
writer.write(c);
|
||||
}
|
||||
|
||||
@Override public void write(char[] cbuf) throws IOException {
|
||||
writer.write(cbuf);
|
||||
}
|
||||
|
||||
@Override public void write(char[] cbuf, int off, int len) throws IOException {
|
||||
writer.write(cbuf, off, len);
|
||||
}
|
||||
|
||||
@Override public void write(String str) throws IOException {
|
||||
writer.write(str);
|
||||
}
|
||||
|
||||
@Override public void write(String str, int off, int len) throws IOException {
|
||||
writer.write(str, off, len);
|
||||
}
|
||||
|
||||
@Override public Writer append(CharSequence csq) throws IOException {
|
||||
return writer.append(csq);
|
||||
}
|
||||
|
||||
@Override public Writer append(CharSequence csq, int start, int end) throws IOException {
|
||||
return writer.append(csq, start, end);
|
||||
}
|
||||
|
||||
@Override public Writer append(char c) throws IOException {
|
||||
return writer.append(c);
|
||||
}
|
||||
|
||||
@Override public void flush() throws IOException {
|
||||
writer.flush();
|
||||
}
|
||||
|
||||
@Override public void close() throws IOException {
|
||||
writer.close();
|
||||
}
|
||||
}
|
166
dexlib2/src/main/java/org/jf/dexlib2/formatter/DexFormatter.java
Normal file
166
dexlib2/src/main/java/org/jf/dexlib2/formatter/DexFormatter.java
Normal file
@ -0,0 +1,166 @@
|
||||
/*
|
||||
* Copyright 2021, Google Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following disclaimer
|
||||
* in the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* * Neither the name of Google Inc. nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package org.jf.dexlib2.formatter;
|
||||
|
||||
import org.jf.dexlib2.iface.reference.*;
|
||||
import org.jf.dexlib2.iface.value.EncodedValue;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
import java.io.Writer;
|
||||
|
||||
/**
|
||||
* This class handles formatting and getting strings for various types of items in a dex file.
|
||||
*/
|
||||
public class DexFormatter {
|
||||
|
||||
public static final DexFormatter INSTANCE = new DexFormatter();
|
||||
|
||||
/**
|
||||
* Gets a {@link DexFormattedWriter} for writing formatted strings to a {@link Writer}, with the same settings as this Formatter.
|
||||
*
|
||||
* @param writer The {@link Writer} that the {@link DexFormattedWriter} will write to.
|
||||
*/
|
||||
public DexFormattedWriter getWriter(Writer writer) {
|
||||
return new DexFormattedWriter(writer);
|
||||
}
|
||||
|
||||
public String getMethodDescriptor(MethodReference methodReference) {
|
||||
StringWriter writer = new StringWriter();
|
||||
try {
|
||||
getWriter(writer).writeMethodDescriptor(methodReference);
|
||||
} catch (IOException e) {
|
||||
throw new AssertionError("Unexpected IOException");
|
||||
}
|
||||
return writer.toString();
|
||||
}
|
||||
|
||||
public String getShortMethodDescriptor(MethodReference methodReference) {
|
||||
StringWriter writer = new StringWriter();
|
||||
try {
|
||||
getWriter(writer).writeShortMethodDescriptor(methodReference);
|
||||
} catch (IOException e) {
|
||||
throw new AssertionError("Unexpected IOException");
|
||||
}
|
||||
return writer.toString();
|
||||
}
|
||||
|
||||
public String getMethodProtoDescriptor(MethodProtoReference protoReference) {
|
||||
StringWriter writer = new StringWriter();
|
||||
try {
|
||||
getWriter(writer).writeMethodProtoDescriptor(protoReference);
|
||||
} catch (IOException e) {
|
||||
throw new AssertionError("Unexpected IOException");
|
||||
}
|
||||
return writer.toString();
|
||||
}
|
||||
|
||||
public String getFieldDescriptor(FieldReference fieldReference) {
|
||||
StringWriter writer = new StringWriter();
|
||||
try {
|
||||
getWriter(writer).writeFieldDescriptor(fieldReference);
|
||||
} catch (IOException e) {
|
||||
throw new AssertionError("Unexpected IOException");
|
||||
}
|
||||
return writer.toString();
|
||||
}
|
||||
|
||||
public String getShortFieldDescriptor(FieldReference fieldReference) {
|
||||
StringWriter writer = new StringWriter();
|
||||
try {
|
||||
getWriter(writer).writeShortFieldDescriptor(fieldReference);
|
||||
} catch (IOException e) {
|
||||
throw new AssertionError("Unexpected IOException");
|
||||
}
|
||||
return writer.toString();
|
||||
}
|
||||
|
||||
public String getMethodHandle(MethodHandleReference methodHandleReference) {
|
||||
StringWriter writer = new StringWriter();
|
||||
try {
|
||||
getWriter(writer).writeMethodHandle(methodHandleReference);
|
||||
} catch (IOException e) {
|
||||
throw new AssertionError("Unexpected IOException");
|
||||
}
|
||||
return writer.toString();
|
||||
}
|
||||
|
||||
public String getCallSite(CallSiteReference callSiteReference) {
|
||||
StringWriter writer = new StringWriter();
|
||||
try {
|
||||
getWriter(writer).writeCallSite(callSiteReference);
|
||||
} catch (IOException e) {
|
||||
throw new AssertionError("Unexpected IOException");
|
||||
}
|
||||
return writer.toString();
|
||||
}
|
||||
|
||||
public String getType(CharSequence type) {
|
||||
StringWriter writer = new StringWriter();
|
||||
try {
|
||||
getWriter(writer).writeType(type);
|
||||
} catch (IOException e) {
|
||||
throw new AssertionError("Unexpected IOException");
|
||||
}
|
||||
return writer.toString();
|
||||
}
|
||||
|
||||
public String getQuotedString(CharSequence string) {
|
||||
StringWriter writer = new StringWriter();
|
||||
try {
|
||||
getWriter(writer).writeQuotedString(string);
|
||||
} catch (IOException e) {
|
||||
throw new AssertionError("Unexpected IOException");
|
||||
}
|
||||
return writer.toString();
|
||||
}
|
||||
|
||||
public String getEncodedValue(EncodedValue encodedValue) {
|
||||
StringWriter writer = new StringWriter();
|
||||
try {
|
||||
getWriter(writer).writeEncodedValue(encodedValue);
|
||||
} catch (IOException e) {
|
||||
throw new AssertionError("Unexpected IOException");
|
||||
}
|
||||
return writer.toString();
|
||||
}
|
||||
|
||||
public String getReference(Reference reference) {
|
||||
StringWriter writer = new StringWriter();
|
||||
try {
|
||||
getWriter(writer).writeReference(reference);
|
||||
} catch (IOException e) {
|
||||
throw new AssertionError("Unexpected IOException");
|
||||
}
|
||||
return writer.toString();
|
||||
}
|
||||
}
|
@ -40,6 +40,9 @@ import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Some utilities for generating human-readable strings for encoded values.
|
||||
*/
|
||||
public final class EncodedValueUtils {
|
||||
public static boolean isDefaultValue(EncodedValue encodedValue) {
|
||||
switch (encodedValue.getValueType()) {
|
||||
@ -65,6 +68,10 @@ public final class EncodedValueUtils {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link org.jf.dexlib2.formatter.DefaultDexFormatter} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public static void writeEncodedValue(Writer writer, EncodedValue encodedValue) throws IOException {
|
||||
switch (encodedValue.getValueType()) {
|
||||
case ValueType.BOOLEAN:
|
||||
|
@ -42,6 +42,12 @@ import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
import java.io.Writer;
|
||||
|
||||
/**
|
||||
* Some utilities for generating human-readable strings for the various types of references.
|
||||
*
|
||||
* @deprecated use {@link org.jf.dexlib2.formatter.DefaultDexFormatter} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public final class ReferenceUtil {
|
||||
public static String getMethodDescriptor(MethodReference methodReference) {
|
||||
return getMethodDescriptor(methodReference, false);
|
||||
|
@ -57,6 +57,10 @@ public class StringUtils {
|
||||
writer.write(Character.forDigit(c & 0x0f, 16));
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link org.jf.dexlib2.formatter.DexFormattedWriter#writeQuotedString(CharSequence)}
|
||||
*/
|
||||
@Deprecated
|
||||
public static void writeEscapedString(Writer writer, String value) throws IOException {
|
||||
for (int i = 0; i < value.length(); i++) {
|
||||
char c = value.charAt(i);
|
||||
|
@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright 2021, Google Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following disclaimer
|
||||
* in the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* * Neither the name of Google Inc. nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package org.jf.dexlib2.formatter;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
|
||||
public class DexFormattedWriterStringTest {
|
||||
|
||||
@Test
|
||||
public void testWriteQuotedString() throws IOException {
|
||||
Assert.assertEquals(
|
||||
"\"string value\"",
|
||||
performWriteQuotedString("string value"));
|
||||
|
||||
Assert.assertEquals(
|
||||
"\"string value with double quote \\\"\"",
|
||||
performWriteQuotedString("string value with double quote \""));
|
||||
|
||||
Assert.assertEquals(
|
||||
"\"string value with single quote \\'\"",
|
||||
performWriteQuotedString("string value with single quote '"));
|
||||
|
||||
Assert.assertEquals(
|
||||
"\"string value with backslash \\\\\"",
|
||||
performWriteQuotedString("string value with backslash \\"));
|
||||
|
||||
Assert.assertEquals(
|
||||
"\"string value with newline \\n\"",
|
||||
performWriteQuotedString("string value with newline \n"));
|
||||
|
||||
Assert.assertEquals(
|
||||
"\"string value with return \\r\"",
|
||||
performWriteQuotedString("string value with return \r"));
|
||||
|
||||
Assert.assertEquals(
|
||||
"\"string value with tab \\t\"",
|
||||
performWriteQuotedString("string value with tab \t"));
|
||||
|
||||
Assert.assertEquals(
|
||||
"\"string value with unicode escape \\u1234\"",
|
||||
performWriteQuotedString("string value with unicode escape \u1234"));
|
||||
}
|
||||
|
||||
private String performWriteQuotedString(String value) throws IOException {
|
||||
StringWriter stringWriter = new StringWriter();
|
||||
DexFormattedWriter writer = new DexFormattedWriter(stringWriter);
|
||||
writer.writeQuotedString(value);
|
||||
return stringWriter.toString();
|
||||
}
|
||||
}
|
@ -0,0 +1,466 @@
|
||||
/*
|
||||
* Copyright 2021, Google Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following disclaimer
|
||||
* in the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* * Neither the name of Google Inc. nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package org.jf.dexlib2.formatter;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import org.jf.dexlib2.MethodHandleType;
|
||||
import org.jf.dexlib2.iface.reference.CallSiteReference;
|
||||
import org.jf.dexlib2.iface.reference.MethodHandleReference;
|
||||
import org.jf.dexlib2.immutable.ImmutableAnnotationElement;
|
||||
import org.jf.dexlib2.immutable.reference.*;
|
||||
import org.jf.dexlib2.immutable.value.*;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
|
||||
public class DexFormattedWriterTest {
|
||||
|
||||
private StringWriter output;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
output = new StringWriter();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteMethodDescriptor() throws IOException {
|
||||
DexFormattedWriter writer = new DexFormattedWriter(output);
|
||||
|
||||
writer.writeMethodDescriptor(getMethodReference());
|
||||
|
||||
Assert.assertEquals("Ldefining/class;->methodName(Lparam1;Lparam2;)Lreturn/type;", output.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteShortMethodDescriptor() throws IOException {
|
||||
DexFormattedWriter writer = new DexFormattedWriter(output);
|
||||
|
||||
writer.writeShortMethodDescriptor(getMethodReference());
|
||||
|
||||
Assert.assertEquals("methodName(Lparam1;Lparam2;)Lreturn/type;", output.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteMethodProtoDescriptor() throws IOException {
|
||||
DexFormattedWriter writer = new DexFormattedWriter(output);
|
||||
|
||||
writer.writeMethodProtoDescriptor(getMethodProtoReference());
|
||||
|
||||
Assert.assertEquals("(Lparam1;Lparam2;)Lreturn/type;", output.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteFieldDescriptor() throws IOException {
|
||||
DexFormattedWriter writer = new DexFormattedWriter(output);
|
||||
|
||||
writer.writeFieldDescriptor(getFieldReference());
|
||||
|
||||
Assert.assertEquals("Ldefining/class;->fieldName:Lfield/type;", output.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteShortFieldDescriptor() throws IOException {
|
||||
DexFormattedWriter writer = new DexFormattedWriter(output);
|
||||
|
||||
writer.writeShortFieldDescriptor(getFieldReference());
|
||||
|
||||
Assert.assertEquals("fieldName:Lfield/type;", output.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteMethodHandle_fieldAccess() throws IOException {
|
||||
DexFormattedWriter writer = new DexFormattedWriter(output);
|
||||
|
||||
writer.writeMethodHandle(getMethodHandleReferenceForField());
|
||||
|
||||
Assert.assertEquals("instance-get@Ldefining/class;->fieldName:Lfield/type;", output.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteMethodHandle_methodAccess() throws IOException {
|
||||
DexFormattedWriter writer = new DexFormattedWriter(output);
|
||||
|
||||
writer.writeMethodHandle(getMethodHandleReferenceForMethod());
|
||||
|
||||
Assert.assertEquals("invoke-instance@Ldefining/class;->methodName(Lparam1;Lparam2;)Lreturn/type;",
|
||||
output.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteCallsite() throws IOException {
|
||||
DexFormattedWriter writer = new DexFormattedWriter(output);
|
||||
|
||||
writer.writeCallSite(getCallSiteReference());
|
||||
|
||||
Assert.assertEquals(
|
||||
"callsiteName(\"callSiteMethodName\", " +
|
||||
"(Lparam1;Lparam2;)Lreturn/type;, Ldefining/class;->fieldName:Lfield/type;, " +
|
||||
"Ldefining/class;->methodName(Lparam1;Lparam2;)Lreturn/type;)@" +
|
||||
"Ldefining/class;->methodName(Lparam1;Lparam2;)Lreturn/type;",
|
||||
output.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteEncodedValue_boolean_true() throws IOException {
|
||||
DexFormattedWriter writer = new DexFormattedWriter(output);
|
||||
|
||||
writer.writeEncodedValue(ImmutableBooleanEncodedValue.TRUE_VALUE);
|
||||
|
||||
Assert.assertEquals("true", output.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteEncodedValue_boolean_false() throws IOException {
|
||||
DexFormattedWriter writer = new DexFormattedWriter(output);
|
||||
|
||||
writer.writeEncodedValue(ImmutableBooleanEncodedValue.FALSE_VALUE);
|
||||
|
||||
Assert.assertEquals("false", output.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteEncodedValue_byte() throws IOException {
|
||||
DexFormattedWriter writer = new DexFormattedWriter(output);
|
||||
|
||||
writer.writeEncodedValue(new ImmutableByteEncodedValue((byte)0x12));
|
||||
|
||||
Assert.assertEquals("0x12", output.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteEncodedValue_char() throws IOException {
|
||||
DexFormattedWriter writer = new DexFormattedWriter(output);
|
||||
|
||||
writer.writeEncodedValue(new ImmutableCharEncodedValue('a'));
|
||||
|
||||
Assert.assertEquals("0x61", output.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteEncodedValue_short() throws IOException {
|
||||
DexFormattedWriter writer = new DexFormattedWriter(output);
|
||||
|
||||
writer.writeEncodedValue(new ImmutableShortEncodedValue((short) 0x12));
|
||||
|
||||
Assert.assertEquals("0x12", output.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteEncodedValue_int() throws IOException {
|
||||
DexFormattedWriter writer = new DexFormattedWriter(output);
|
||||
|
||||
writer.writeEncodedValue(new ImmutableIntEncodedValue(0x12));
|
||||
|
||||
Assert.assertEquals("0x12", output.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteEncodedValue_long() throws IOException {
|
||||
DexFormattedWriter writer = new DexFormattedWriter(output);
|
||||
|
||||
writer.writeEncodedValue(new ImmutableLongEncodedValue(0x12));
|
||||
|
||||
Assert.assertEquals("0x12", output.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteEncodedValue_float() throws IOException {
|
||||
DexFormattedWriter writer = new DexFormattedWriter(output);
|
||||
|
||||
writer.writeEncodedValue(new ImmutableFloatEncodedValue(12.34f));
|
||||
|
||||
Assert.assertEquals("12.34", output.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteEncodedValue_double() throws IOException {
|
||||
DexFormattedWriter writer = new DexFormattedWriter(output);
|
||||
|
||||
writer.writeEncodedValue(new ImmutableDoubleEncodedValue(12.34));
|
||||
|
||||
Assert.assertEquals("12.34", output.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteEncodedValue_annotation() throws IOException {
|
||||
DexFormattedWriter writer = new DexFormattedWriter(output);
|
||||
|
||||
writer.writeEncodedValue(new ImmutableAnnotationEncodedValue(
|
||||
"Lannotation/type;",
|
||||
ImmutableSet.of(
|
||||
new ImmutableAnnotationElement("element1", new ImmutableFieldEncodedValue(getFieldReference())),
|
||||
new ImmutableAnnotationElement("element2", new ImmutableMethodEncodedValue(getMethodReference()))
|
||||
)));
|
||||
|
||||
Assert.assertEquals(
|
||||
"Annotation[Lannotation/type;, " +
|
||||
"element1=Ldefining/class;->fieldName:Lfield/type;, " +
|
||||
"element2=Ldefining/class;->methodName(Lparam1;Lparam2;)Lreturn/type;]",
|
||||
output.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteEncodedValue_array() throws IOException {
|
||||
DexFormattedWriter writer = new DexFormattedWriter(output);
|
||||
|
||||
writer.writeEncodedValue(new ImmutableArrayEncodedValue(ImmutableList.of(
|
||||
new ImmutableFieldEncodedValue(getFieldReference()),
|
||||
new ImmutableMethodEncodedValue(getMethodReference()))));
|
||||
|
||||
Assert.assertEquals(
|
||||
"Array[Ldefining/class;->fieldName:Lfield/type;, " +
|
||||
"Ldefining/class;->methodName(Lparam1;Lparam2;)Lreturn/type;]",
|
||||
output.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteEncodedValue_string() throws IOException {
|
||||
DexFormattedWriter writer = new DexFormattedWriter(output);
|
||||
|
||||
writer.writeEncodedValue(new ImmutableStringEncodedValue("string value\n"));
|
||||
|
||||
Assert.assertEquals(
|
||||
"\"string value\\n\"",
|
||||
output.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteEncodedValue_field() throws IOException {
|
||||
DexFormattedWriter writer = new DexFormattedWriter(output);
|
||||
|
||||
writer.writeEncodedValue(new ImmutableFieldEncodedValue(getFieldReference()));
|
||||
|
||||
Assert.assertEquals(
|
||||
"Ldefining/class;->fieldName:Lfield/type;",
|
||||
output.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteEncodedValue_enum() throws IOException {
|
||||
DexFormattedWriter writer = new DexFormattedWriter(output);
|
||||
|
||||
writer.writeEncodedValue(new ImmutableEnumEncodedValue(getFieldReference()));
|
||||
|
||||
Assert.assertEquals(
|
||||
"Ldefining/class;->fieldName:Lfield/type;",
|
||||
output.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteEncodedValue_method() throws IOException {
|
||||
DexFormattedWriter writer = new DexFormattedWriter(output);
|
||||
|
||||
writer.writeEncodedValue(new ImmutableMethodEncodedValue(getMethodReference()));
|
||||
|
||||
Assert.assertEquals(
|
||||
"Ldefining/class;->methodName(Lparam1;Lparam2;)Lreturn/type;",
|
||||
output.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteEncodedValue_type() throws IOException {
|
||||
DexFormattedWriter writer = new DexFormattedWriter(output);
|
||||
|
||||
writer.writeEncodedValue(new ImmutableTypeEncodedValue("Ltest/type;"));
|
||||
|
||||
Assert.assertEquals(
|
||||
"Ltest/type;",
|
||||
output.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteEncodedValue_methodType() throws IOException {
|
||||
DexFormattedWriter writer = new DexFormattedWriter(output);
|
||||
|
||||
writer.writeEncodedValue(new ImmutableMethodTypeEncodedValue(getMethodProtoReference()));
|
||||
|
||||
Assert.assertEquals(
|
||||
"(Lparam1;Lparam2;)Lreturn/type;",
|
||||
output.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteEncodedValue_methodHandle() throws IOException {
|
||||
DexFormattedWriter writer = new DexFormattedWriter(output);
|
||||
|
||||
writer.writeEncodedValue(new ImmutableMethodHandleEncodedValue(getMethodHandleReferenceForField()));
|
||||
|
||||
Assert.assertEquals(
|
||||
"instance-get@Ldefining/class;->fieldName:Lfield/type;",
|
||||
output.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteEncodedValue_null() throws IOException {
|
||||
DexFormattedWriter writer = new DexFormattedWriter(output);
|
||||
|
||||
writer.writeEncodedValue(ImmutableNullEncodedValue.INSTANCE);
|
||||
|
||||
Assert.assertEquals(
|
||||
"null",
|
||||
output.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteReference_string() throws IOException {
|
||||
DexFormattedWriter writer = new DexFormattedWriter(output);
|
||||
|
||||
writer.writeReference(new ImmutableStringReference("string value"));
|
||||
|
||||
Assert.assertEquals(
|
||||
"\"string value\"",
|
||||
output.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteReference_type() throws IOException {
|
||||
DexFormattedWriter writer = new DexFormattedWriter(output);
|
||||
|
||||
writer.writeReference(new ImmutableTypeReference("Ltest/type;"));
|
||||
|
||||
Assert.assertEquals(
|
||||
"Ltest/type;",
|
||||
output.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteReference_field() throws IOException {
|
||||
DexFormattedWriter writer = new DexFormattedWriter(output);
|
||||
|
||||
writer.writeReference(getFieldReference());
|
||||
|
||||
Assert.assertEquals(
|
||||
"Ldefining/class;->fieldName:Lfield/type;",
|
||||
output.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteReference_method() throws IOException {
|
||||
DexFormattedWriter writer = new DexFormattedWriter(output);
|
||||
|
||||
writer.writeReference(getMethodReference());
|
||||
|
||||
Assert.assertEquals(
|
||||
"Ldefining/class;->methodName(Lparam1;Lparam2;)Lreturn/type;",
|
||||
output.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteReference_methodProto() throws IOException {
|
||||
DexFormattedWriter writer = new DexFormattedWriter(output);
|
||||
|
||||
writer.writeReference(getMethodProtoReference());
|
||||
|
||||
Assert.assertEquals(
|
||||
"(Lparam1;Lparam2;)Lreturn/type;",
|
||||
output.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteReference_methodHandle() throws IOException {
|
||||
DexFormattedWriter writer = new DexFormattedWriter(output);
|
||||
|
||||
writer.writeReference(getMethodHandleReferenceForMethod());
|
||||
|
||||
Assert.assertEquals(
|
||||
"invoke-instance@Ldefining/class;->methodName(Lparam1;Lparam2;)Lreturn/type;",
|
||||
output.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteReference_callSite() throws IOException {
|
||||
DexFormattedWriter writer = new DexFormattedWriter(output);
|
||||
|
||||
writer.writeReference(getCallSiteReference());
|
||||
|
||||
Assert.assertEquals(
|
||||
"callsiteName(\"callSiteMethodName\", " +
|
||||
"(Lparam1;Lparam2;)Lreturn/type;, Ldefining/class;->fieldName:Lfield/type;, " +
|
||||
"Ldefining/class;->methodName(Lparam1;Lparam2;)Lreturn/type;)@" +
|
||||
"Ldefining/class;->methodName(Lparam1;Lparam2;)Lreturn/type;",
|
||||
output.toString());
|
||||
}
|
||||
|
||||
private ImmutableMethodReference getMethodReference() {
|
||||
return new ImmutableMethodReference(
|
||||
"Ldefining/class;",
|
||||
"methodName",
|
||||
ImmutableList.of("Lparam1;", "Lparam2;"),
|
||||
"Lreturn/type;");
|
||||
}
|
||||
|
||||
private ImmutableMethodProtoReference getMethodProtoReference() {
|
||||
return new ImmutableMethodProtoReference(
|
||||
ImmutableList.of("Lparam1;", "Lparam2;"),
|
||||
"Lreturn/type;");
|
||||
}
|
||||
|
||||
private ImmutableFieldReference getFieldReference() {
|
||||
return new ImmutableFieldReference(
|
||||
"Ldefining/class;",
|
||||
"fieldName",
|
||||
"Lfield/type;");
|
||||
}
|
||||
|
||||
private ImmutableMethodHandleReference getMethodHandleReferenceForField() {
|
||||
return new ImmutableMethodHandleReference(
|
||||
MethodHandleType.INSTANCE_GET,
|
||||
getFieldReference());
|
||||
}
|
||||
|
||||
private MethodHandleReference getMethodHandleReferenceForMethod() {
|
||||
return new ImmutableMethodHandleReference(
|
||||
MethodHandleType.INVOKE_INSTANCE,
|
||||
getMethodReference());
|
||||
}
|
||||
|
||||
private MethodHandleReference getInvokeStaticMethodHandleReferenceForMethod() {
|
||||
return new ImmutableMethodHandleReference(
|
||||
MethodHandleType.INVOKE_STATIC,
|
||||
getMethodReference());
|
||||
}
|
||||
|
||||
private CallSiteReference getCallSiteReference() {
|
||||
return new ImmutableCallSiteReference(
|
||||
"callsiteName",
|
||||
getInvokeStaticMethodHandleReferenceForMethod(),
|
||||
"callSiteMethodName",
|
||||
getMethodProtoReference(),
|
||||
ImmutableList.of(
|
||||
new ImmutableFieldEncodedValue(getFieldReference()),
|
||||
new ImmutableMethodEncodedValue(getMethodReference())));
|
||||
}
|
||||
}
|
@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright 2021, Google Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following disclaimer
|
||||
* in the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* * Neither the name of Google Inc. nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package org.jf.dexlib2.formatter;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
|
||||
public class DexFormattedWriterTypeTest {
|
||||
|
||||
|
||||
@Test
|
||||
public void testWriteType_unquoted() throws IOException {
|
||||
String[] typeStrings = new String[] {
|
||||
"Ljava/lang/Object;",
|
||||
"Z",
|
||||
"B",
|
||||
"S",
|
||||
"C",
|
||||
"I",
|
||||
"J",
|
||||
"F",
|
||||
"D",
|
||||
"V",
|
||||
"[D",
|
||||
"[[D",
|
||||
"[Ljava/lang/Object;",
|
||||
"[[Ljava/lang/Object;",
|
||||
"LC;"
|
||||
};
|
||||
|
||||
for (String typeString: typeStrings) {
|
||||
Assert.assertEquals(typeString, performWriteType(typeString));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteType_invalid() throws IOException {
|
||||
|
||||
assertWriteTypeFails("L;");
|
||||
assertWriteTypeFails("H");
|
||||
assertWriteTypeFails("L/blah;");
|
||||
assertWriteTypeFails("La//b;");
|
||||
|
||||
assertWriteTypeFails("La//b");
|
||||
assertWriteTypeFails("La//b ");
|
||||
|
||||
assertWriteTypeFails("[");
|
||||
|
||||
assertWriteTypeFails("[L");
|
||||
|
||||
assertWriteTypeFails("[L ");
|
||||
}
|
||||
|
||||
private void assertWriteTypeFails(String input) throws IOException {
|
||||
try {
|
||||
performWriteType(input);
|
||||
Assert.fail("Expected failure did not occur");
|
||||
} catch (IllegalArgumentException ex) {
|
||||
// expected exception
|
||||
}
|
||||
}
|
||||
|
||||
private String performWriteType(String input) throws IOException {
|
||||
StringWriter stringWriter = new StringWriter();
|
||||
DexFormattedWriter writer = new DexFormattedWriter(stringWriter);
|
||||
writer.writeType(input);
|
||||
return stringWriter.toString();
|
||||
}
|
||||
}
|
@ -0,0 +1,186 @@
|
||||
/*
|
||||
* Copyright 2021, Google Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following disclaimer
|
||||
* in the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* * Neither the name of Google Inc. nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package org.jf.dexlib2.formatter;
|
||||
|
||||
import org.jf.dexlib2.iface.reference.*;
|
||||
import org.jf.dexlib2.iface.value.EncodedValue;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
public class DexFormatterTest {
|
||||
|
||||
@Test
|
||||
public void testGetMethodReference() throws IOException {
|
||||
TestDexFormatter formatter = new TestDexFormatter();
|
||||
Assert.assertEquals(
|
||||
"method descriptor",
|
||||
formatter.getMethodDescriptor(mock(MethodReference.class)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetShortMethodReference() throws IOException {
|
||||
TestDexFormatter formatter = new TestDexFormatter();
|
||||
Assert.assertEquals(
|
||||
"short method descriptor",
|
||||
formatter.getShortMethodDescriptor(mock(MethodReference.class)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetMethodProtoReference() throws IOException {
|
||||
TestDexFormatter formatter = new TestDexFormatter();
|
||||
Assert.assertEquals(
|
||||
"method proto descriptor",
|
||||
formatter.getMethodProtoDescriptor(mock(MethodProtoReference.class)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetFieldReference() throws IOException {
|
||||
TestDexFormatter formatter = new TestDexFormatter();
|
||||
Assert.assertEquals(
|
||||
"field descriptor",
|
||||
formatter.getFieldDescriptor(mock(FieldReference.class)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetShortFieldReference() throws IOException {
|
||||
TestDexFormatter formatter = new TestDexFormatter();
|
||||
Assert.assertEquals(
|
||||
"short field descriptor",
|
||||
formatter.getShortFieldDescriptor(mock(FieldReference.class)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetMethodHandle() throws IOException {
|
||||
TestDexFormatter formatter = new TestDexFormatter();
|
||||
Assert.assertEquals(
|
||||
"method handle",
|
||||
formatter.getMethodHandle(mock(MethodHandleReference.class)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetCallSite() throws IOException {
|
||||
TestDexFormatter formatter = new TestDexFormatter();
|
||||
Assert.assertEquals(
|
||||
"call site",
|
||||
formatter.getCallSite(mock(CallSiteReference.class)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetType() throws IOException {
|
||||
TestDexFormatter formatter = new TestDexFormatter();
|
||||
Assert.assertEquals(
|
||||
"type",
|
||||
formatter.getType("mock type"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetQuotedString() throws IOException {
|
||||
TestDexFormatter formatter = new TestDexFormatter();
|
||||
Assert.assertEquals(
|
||||
"quoted string",
|
||||
formatter.getQuotedString("mock string"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetEncodedValue() throws IOException {
|
||||
TestDexFormatter formatter = new TestDexFormatter();
|
||||
Assert.assertEquals(
|
||||
"encoded value",
|
||||
formatter.getEncodedValue(mock(EncodedValue.class)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReference() throws IOException {
|
||||
TestDexFormatter formatter = new TestDexFormatter();
|
||||
Assert.assertEquals(
|
||||
"reference",
|
||||
formatter.getReference(mock(Reference.class)));
|
||||
}
|
||||
|
||||
private static class TestDexFormatter extends DexFormatter {
|
||||
|
||||
@Override public DexFormattedWriter getWriter(Writer writer) {
|
||||
return new DexFormattedWriter(writer) {
|
||||
@Override public void writeMethodDescriptor(MethodReference methodReference) throws IOException {
|
||||
writer.write("method descriptor");
|
||||
}
|
||||
|
||||
@Override public void writeShortMethodDescriptor(MethodReference methodReference) throws IOException {
|
||||
writer.write("short method descriptor");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeMethodProtoDescriptor(MethodProtoReference protoReference) throws IOException {
|
||||
writer.write("method proto descriptor");
|
||||
}
|
||||
|
||||
@Override public void writeFieldDescriptor(FieldReference fieldReference) throws IOException {
|
||||
writer.write("field descriptor");
|
||||
}
|
||||
|
||||
@Override public void writeShortFieldDescriptor(FieldReference fieldReference) throws IOException {
|
||||
writer.write("short field descriptor");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeMethodHandle(MethodHandleReference methodHandleReference) throws IOException {
|
||||
writer.write("method handle");
|
||||
}
|
||||
|
||||
@Override public void writeCallSite(CallSiteReference callSiteReference) throws IOException {
|
||||
writer.write("call site");
|
||||
}
|
||||
|
||||
@Override public void writeType(CharSequence type) throws IOException {
|
||||
writer.write("type");
|
||||
}
|
||||
|
||||
@Override public void writeQuotedString(CharSequence string) throws IOException {
|
||||
writer.write("quoted string");
|
||||
}
|
||||
|
||||
@Override public void writeEncodedValue(EncodedValue encodedValue) throws IOException {
|
||||
writer.write("encoded value");
|
||||
}
|
||||
|
||||
@Override public void writeReference(Reference reference) throws IOException {
|
||||
writer.write("reference");
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user