diff --git a/dexlib2/src/main/java/org/jf/dexlib2/formatter/DexFormattedWriter.java b/dexlib2/src/main/java/org/jf/dexlib2/formatter/DexFormattedWriter.java new file mode 100644 index 00000000..951a0da8 --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/formatter/DexFormattedWriter.java @@ -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}. + * + *
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}. + * + *
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 simple name + * to write. + */ + protected void writeSimpleName(CharSequence simpleName) throws IOException { + writer.append(simpleName); + } + + /** + * Write the given quoted string. + * + *
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(); + } +} diff --git a/dexlib2/src/main/java/org/jf/dexlib2/formatter/DexFormatter.java b/dexlib2/src/main/java/org/jf/dexlib2/formatter/DexFormatter.java new file mode 100644 index 00000000..47233b55 --- /dev/null +++ b/dexlib2/src/main/java/org/jf/dexlib2/formatter/DexFormatter.java @@ -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(); + } +} 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 a09bbca2..26062584 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/util/EncodedValueUtils.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/util/EncodedValueUtils.java @@ -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: 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 86d35368..d33fb263 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/util/ReferenceUtil.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/util/ReferenceUtil.java @@ -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); diff --git a/dexlib2/src/main/java/org/jf/util/StringUtils.java b/dexlib2/src/main/java/org/jf/util/StringUtils.java index 4de6d9de..01fcb8f9 100644 --- a/dexlib2/src/main/java/org/jf/util/StringUtils.java +++ b/dexlib2/src/main/java/org/jf/util/StringUtils.java @@ -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); diff --git a/dexlib2/src/test/java/org/jf/dexlib2/formatter/DexFormattedWriterStringTest.java b/dexlib2/src/test/java/org/jf/dexlib2/formatter/DexFormattedWriterStringTest.java new file mode 100644 index 00000000..73b55a5c --- /dev/null +++ b/dexlib2/src/test/java/org/jf/dexlib2/formatter/DexFormattedWriterStringTest.java @@ -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(); + } +} diff --git a/dexlib2/src/test/java/org/jf/dexlib2/formatter/DexFormattedWriterTest.java b/dexlib2/src/test/java/org/jf/dexlib2/formatter/DexFormattedWriterTest.java new file mode 100644 index 00000000..4ac2e96a --- /dev/null +++ b/dexlib2/src/test/java/org/jf/dexlib2/formatter/DexFormattedWriterTest.java @@ -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()))); + } +} diff --git a/dexlib2/src/test/java/org/jf/dexlib2/formatter/DexFormattedWriterTypeTest.java b/dexlib2/src/test/java/org/jf/dexlib2/formatter/DexFormattedWriterTypeTest.java new file mode 100644 index 00000000..2f55e717 --- /dev/null +++ b/dexlib2/src/test/java/org/jf/dexlib2/formatter/DexFormattedWriterTypeTest.java @@ -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(); + } +} diff --git a/dexlib2/src/test/java/org/jf/dexlib2/formatter/DexFormatterTest.java b/dexlib2/src/test/java/org/jf/dexlib2/formatter/DexFormatterTest.java new file mode 100644 index 00000000..cad6c779 --- /dev/null +++ b/dexlib2/src/test/java/org/jf/dexlib2/formatter/DexFormatterTest.java @@ -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"); + } + }; + } + } +}