From 35329727a4a0e7547074e192eda48e99d03137c2 Mon Sep 17 00:00:00 2001 From: "JesusFreke@JesusFreke.com" Date: Sun, 31 May 2009 07:08:04 +0000 Subject: [PATCH] - Added the ability to dexlib to do an annotated dump of the dex file as it is writing it (similiar to the --dump-to functionality in dx) - Other misc refactoring and cleanup in dexlib git-svn-id: https://smali.googlecode.com/svn/trunk@50 55b6fa8a-2a1e-11de-a435-ffa8d773f76a --- .../org/JesusFreke/smali/smaliTreeWalker.g | 2246 ++++++++--------- .../dexlib/AnnotationDirectoryItem.java | 112 +- .../org/JesusFreke/dexlib/AnnotationItem.java | 43 +- .../JesusFreke/dexlib/AnnotationSetItem.java | 26 +- .../dexlib/AnnotationSetRefList.java | 28 +- .../java/org/JesusFreke/dexlib/ByteField.java | 23 +- .../dexlib/CachedIntegerValueField.java | 55 +- .../org/JesusFreke/dexlib/ClassDataItem.java | 147 +- .../org/JesusFreke/dexlib/ClassDefItem.java | 97 +- .../java/org/JesusFreke/dexlib/CodeItem.java | 190 +- .../org/JesusFreke/dexlib/CompositeField.java | 41 +- .../org/JesusFreke/dexlib/DebugInfoItem.java | 33 +- .../java/org/JesusFreke/dexlib/DexFile.java | 30 +- .../JesusFreke/dexlib/EncodedArrayItem.java | 16 +- .../EncodedValue/AnnotationElement.java | 14 +- .../AnnotationEncodedValueSubField.java | 13 +- .../ArrayEncodedValueSubField.java | 17 +- .../BoolEncodedValueSubField.java | 4 +- .../ByteEncodedValueSubField.java | 5 +- .../CharEncodedValueSubField.java | 8 +- .../DoubleEncodedValueSubField.java | 9 +- .../EncodedIndexedItemReference.java | 43 +- .../dexlib/EncodedValue/EncodedValue.java | 16 +- .../FloatEncodedValueSubField.java | 9 +- .../EncodedValue/IntEncodedValueSubField.java | 8 +- .../LongEncodedValueSubField.java | 8 +- .../NullEncodedValueSubField.java | 4 +- .../ShortEncodedValueSubField.java | 8 +- .../java/org/JesusFreke/dexlib/Field.java | 3 +- .../org/JesusFreke/dexlib/FieldIdItem.java | 50 +- .../org/JesusFreke/dexlib/FieldListField.java | 19 +- .../dexlib/FixedByteArrayField.java | 17 +- .../org/JesusFreke/dexlib/HeaderItem.java | 94 +- .../org/JesusFreke/dexlib/IndexedItem.java | 13 +- .../dexlib/IndexedItemReference.java | 48 +- .../org/JesusFreke/dexlib/IntegerField.java | 32 +- src/main/java/org/JesusFreke/dexlib/Item.java | 58 +- .../org/JesusFreke/dexlib/ItemReference.java | 57 +- .../org/JesusFreke/dexlib/Leb128Field.java | 23 +- .../org/JesusFreke/dexlib/Leb128p1Field.java | 23 +- .../org/JesusFreke/dexlib/ListSizeField.java | 13 +- .../java/org/JesusFreke/dexlib/MapField.java | 57 +- .../java/org/JesusFreke/dexlib/MapItem.java | 17 +- .../org/JesusFreke/dexlib/MethodIdItem.java | 75 +- .../dexlib/NullTerminatedByteArrayField.java | 15 +- .../org/JesusFreke/dexlib/OffsettedItem.java | 3 +- .../dexlib/OffsettedItemReference.java | 51 +- .../org/JesusFreke/dexlib/ProtoIdItem.java | 81 +- .../java/org/JesusFreke/dexlib/Section.java | 16 +- .../JesusFreke/dexlib/SectionHeaderInfo.java | 52 +- .../JesusFreke/dexlib/ShortIntegerField.java | 30 +- .../JesusFreke/dexlib/SignedLeb128Field.java | 23 +- .../org/JesusFreke/dexlib/StringDataItem.java | 28 +- .../org/JesusFreke/dexlib/StringIdItem.java | 28 +- .../org/JesusFreke/dexlib/TypeIdItem.java | 44 +- .../org/JesusFreke/dexlib/TypeListItem.java | 40 +- .../dexlib/code/Format/Format21c.java | 2 +- .../dexlib/code/Format/Format35c.java | 4 +- .../dexlib/code/Format/Format3rc.java | 4 +- .../JesusFreke/dexlib/code/Instruction.java | 5 +- .../JesusFreke/dexlib/debug/AdvanceLine.java | 11 +- .../JesusFreke/dexlib/debug/AdvancePC.java | 11 +- .../org/JesusFreke/dexlib/debug/EndLocal.java | 11 +- .../JesusFreke/dexlib/debug/EndSequence.java | 9 +- .../JesusFreke/dexlib/debug/RestartLocal.java | 11 +- .../dexlib/debug/SetEpilogueBegin.java | 9 +- .../org/JesusFreke/dexlib/debug/SetFile.java | 12 +- .../dexlib/debug/SetPrologueEnd.java | 9 +- .../dexlib/debug/SpecialOpcode.java | 10 +- .../JesusFreke/dexlib/debug/StartLocal.java | 17 +- .../dexlib/debug/StartLocalExtended.java | 20 +- .../dexlib/util/AnnotatedOutput.java | 83 + .../dexlib/util/ByteArrayAnnotatedOutput.java | 669 +++++ .../dexlib/util/IndentingWriter.java | 169 ++ .../dexlib/util/TwoColumnOutput.java | 254 ++ src/main/java/org/JesusFreke/smali/smali.java | 16 +- src/test/java/TryListBuilderTest.java | 2 +- 77 files changed, 3349 insertions(+), 2252 deletions(-) create mode 100644 src/main/java/org/JesusFreke/dexlib/util/AnnotatedOutput.java create mode 100644 src/main/java/org/JesusFreke/dexlib/util/ByteArrayAnnotatedOutput.java create mode 100644 src/main/java/org/JesusFreke/dexlib/util/IndentingWriter.java create mode 100644 src/main/java/org/JesusFreke/dexlib/util/TwoColumnOutput.java diff --git a/src/main/antlr3/org/JesusFreke/smali/smaliTreeWalker.g b/src/main/antlr3/org/JesusFreke/smali/smaliTreeWalker.g index 4884a920..24b8f568 100644 --- a/src/main/antlr3/org/JesusFreke/smali/smaliTreeWalker.g +++ b/src/main/antlr3/org/JesusFreke/smali/smaliTreeWalker.g @@ -1,1123 +1,1123 @@ -/* - * [The "BSD licence"] - * Copyright (c) 2009 Ben Gruver - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. 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. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. - */ - -tree grammar smaliTreeWalker; - -options { - tokenVocab=smaliParser; - ASTLabelType=CommonTree; -} - -@header { -package org.JesusFreke.smali; - -import java.util.HashMap; - -import org.JesusFreke.dexlib.*; -import org.JesusFreke.dexlib.EncodedValue.*; -import org.JesusFreke.dexlib.util.*; -import org.JesusFreke.dexlib.code.*; -import org.JesusFreke.dexlib.code.Format.*; -} - -@members { - public DexFile dexFile; - public ClassDefItem classDefItem; - public ClassDataItem classDataItem; - - private static byte parseRegister_nibble(String register) { - //register should be in the format "v12" - byte val = Byte.parseByte(register.substring(1)); - if (val >= 2<<4) { - //TODO: throw correct exception type - throw new RuntimeException("The maximum allowed register in this context is list of registers is v15"); - } - //the parser wouldn't accept a negative register, i.e. v-1, so we don't have to check for val<0; - return val; - } - - //return a short, because java's byte is signed - private static short parseRegister_byte(String register) { - //register should be in the format "v123" - short val = Short.parseShort(register.substring(1)); - if (val >= 2<<8) { - //TODO: throw correct exception type - throw new RuntimeException("The maximum allowed register in this context is v255"); - } - return val; - } - - //return an int because java's short is signed - private static int parseRegister_short(String register) { - //register should be in the format "v12345" - int val = Integer.parseInt(register.substring(1)); - if (val >= 2<<16) { - //TODO: throw correct exception type - throw new RuntimeException("The maximum allowed register in this context is v65535"); - } - //the parser wouldn't accept a negative register, i.e. v-1, so we don't have to check for val<0; - return val; - } -} - - - -smali_file - : ^(I_CLASS_DEF header methods fields annotations) - { - AnnotationDirectoryItem annotationDirectoryItem = null; - - if ( $methods.methodAnnotationSets != null || - $methods.parameterAnnotationSets != null || - $fields.fieldAnnotationSets != null || - $annotations.annotationSetItem != null) { - annotationDirectoryItem = new AnnotationDirectoryItem( - dexFile, - $annotations.annotationSetItem, - $fields.fieldAnnotationSets, - $methods.methodAnnotationSets, - $methods.parameterAnnotationSets); - } - - classDefItem.setAnnotations(annotationDirectoryItem); - }; - - -header returns[TypeIdItem classType, int accessFlags, TypeIdItem superType, TypeListItem implementsList, StringIdItem sourceSpec] -: class_spec super_spec implements_list source_spec - { - classDataItem = new ClassDataItem(dexFile, 0); - classDefItem = new ClassDefItem(dexFile, $class_spec.type, $class_spec.accessFlags, - $super_spec.type, $implements_list.implementsList, $source_spec.source, classDataItem); - }; - -class_spec returns[TypeIdItem type, int accessFlags] - : class_type_descriptor access_list - { - $type = $class_type_descriptor.type; - $accessFlags = $access_list.value; - }; - -super_spec returns[TypeIdItem type] - : ^(I_SUPER class_type_descriptor) - { - $type = $class_type_descriptor.type; - }; - - -implements_spec returns[TypeIdItem type] - : ^(I_IMPLEMENTS class_type_descriptor) - { - $type = $class_type_descriptor.type; - }; - -implements_list returns[TypeListItem implementsList] -@init { ArrayList typeList; } - : {typeList = new ArrayList();} - (implements_spec {typeList.add($implements_spec.type);} )* - {if (typeList.size() > 0) $implementsList = new TypeListItem(dexFile, typeList); - else $implementsList = null;}; - -source_spec returns[StringIdItem source] - : {$source = null;} - ^(I_SOURCE string_literal {$source = new StringIdItem(dexFile, $string_literal.value);}) - | ; - - - -access_list returns [int value] - @init - { - $value = 0; - } - : ^(I_ACCESS_LIST - ( - ACCESS_SPEC - { - $value |= AccessFlags.getValueForAccessFlag($ACCESS_SPEC.getText()); - } - )+); - -fields returns[List fieldAnnotationSets] - : ^(I_FIELDS - (field - { - classDefItem.addField($field.encodedField, $field.encodedValue); - if ($field.fieldAnnotationSet != null) { - if ($fieldAnnotationSets == null) { - $fieldAnnotationSets = new ArrayList(); - } - fieldAnnotationSets.add($field.fieldAnnotationSet); - } - })*); - -methods returns[List methodAnnotationSets, - List parameterAnnotationSets] - : ^(I_METHODS - (method - { - classDataItem.addMethod($method.encodedMethod); - if ($method.methodAnnotationSet != null) { - if ($methodAnnotationSets == null) { - $methodAnnotationSets = new ArrayList(); - } - $methodAnnotationSets.add($method.methodAnnotationSet); - } - if ($method.parameterAnnotationSets != null) { - if ($parameterAnnotationSets == null) { - $parameterAnnotationSets = new ArrayList(); - } - $parameterAnnotationSets.add($method.parameterAnnotationSets); - } - })*); - -field returns[ClassDataItem.EncodedField encodedField, EncodedValue encodedValue, AnnotationDirectoryItem.FieldAnnotation fieldAnnotationSet] - :^(I_FIELD MEMBER_NAME access_list ^(I_FIELD_TYPE field_type_descriptor) field_initial_value annotations?) - { - TypeIdItem classType = classDefItem.getClassType(); - StringIdItem memberName = new StringIdItem(dexFile, $MEMBER_NAME.text); - TypeIdItem fieldType = $field_type_descriptor.type; - - FieldIdItem fieldIdItem = new FieldIdItem(dexFile, classType, memberName, fieldType); - $encodedField = new ClassDataItem.EncodedField(dexFile, fieldIdItem, $access_list.value); - - if ($field_initial_value.encodedValue != null) { - if (($access_list.value & AccessFlags.STATIC) == 0) { - //TODO: change to an appropriate exception type? - throw new RuntimeException("Initial field values can only be specified for static fields."); - } - - $encodedValue = $field_initial_value.encodedValue; - } else { - $encodedValue = null; - } - - if ($annotations.annotationSetItem != null) { - $fieldAnnotationSet = new AnnotationDirectoryItem.FieldAnnotation(dexFile, fieldIdItem, $annotations.annotationSetItem); - } - }; - - -field_initial_value returns[EncodedValue encodedValue] - : ^(I_FIELD_INITIAL_VALUE literal) {$encodedValue = $literal.encodedValue;} - | ; - -literal returns[EncodedValue encodedValue] - : integer_literal { $encodedValue = new EncodedValue(dexFile, new IntEncodedValueSubField($integer_literal.value)); } - | long_literal { $encodedValue = new EncodedValue(dexFile, new LongEncodedValueSubField($long_literal.value)); } - | short_literal { $encodedValue = new EncodedValue(dexFile, new ShortEncodedValueSubField($short_literal.value)); } - | byte_literal { $encodedValue = new EncodedValue(dexFile, new ByteEncodedValueSubField($byte_literal.value)); } - | float_literal { $encodedValue = new EncodedValue(dexFile, new FloatEncodedValueSubField($float_literal.value)); } - | double_literal { $encodedValue = new EncodedValue(dexFile, new DoubleEncodedValueSubField($double_literal.value)); } - | char_literal { $encodedValue = new EncodedValue(dexFile, new CharEncodedValueSubField($char_literal.value)); } - | string_literal { $encodedValue = new EncodedValue(dexFile, new EncodedIndexedItemReference(dexFile, new StringIdItem(dexFile, $string_literal.value))); } - | bool_literal { $encodedValue = new EncodedValue(dexFile, new BoolEncodedValueSubField($bool_literal.value)); } - | type_descriptor { $encodedValue = new EncodedValue(dexFile, new EncodedIndexedItemReference(dexFile, $type_descriptor.type)); } - | array_literal { $encodedValue = new EncodedValue(dexFile, new ArrayEncodedValueSubField(dexFile, $array_literal.values)); } - | subannotation { $encodedValue = new EncodedValue(dexFile, $subannotation.value); }; - - -//everything but string -fixed_size_literal returns[byte[\] value] - : integer_literal { $value = literalTools.intToBytes($integer_literal.value); } - | long_literal { $value = literalTools.longToBytes($long_literal.value); } - | short_literal { $value = literalTools.shortToBytes($short_literal.value); } - | byte_literal { $value = new byte[] { $byte_literal.value }; } - | float_literal { $value = literalTools.floatToBytes($float_literal.value); } - | double_literal { $value = literalTools.doubleToBytes($double_literal.value); } - | char_literal { $value = literalTools.charToBytes($char_literal.value); } - | bool_literal { $value = literalTools.boolToBytes($bool_literal.value); }; - -//everything but string -fixed_64bit_literal returns[long value] - : integer_literal { $value = $integer_literal.value; } - | long_literal { $value = $long_literal.value; } - | short_literal { $value = $short_literal.value; } - | byte_literal { $value = $byte_literal.value; } - | float_literal { $value = Float.floatToRawIntBits($float_literal.value); } - | double_literal { $value = Double.doubleToRawLongBits($double_literal.value); } - | char_literal { $value = $char_literal.value; } - | bool_literal { $value = $bool_literal.value?1:0; }; - -//everything but string and double -//long is allowed, but it must fit into an int -fixed_32bit_literal returns[int value] - : integer_literal { $value = $integer_literal.value; } - | long_literal { literalTools.checkInt($long_literal.value); $value = (int)$long_literal.value; } - | short_literal { $value = $short_literal.value; } - | byte_literal { $value = $byte_literal.value; } - | float_literal { $value = Float.floatToRawIntBits($float_literal.value); } - | char_literal { $value = $char_literal.value; } - | bool_literal { $value = $bool_literal.value?1:0; }; - -array_elements returns[List values] - : {$values = new ArrayList();} - ^(I_ARRAY_ELEMENTS - (fixed_size_literal - { - $values.add($fixed_size_literal.value); - })*); - -packed_switch_target_count returns[int targetCount] - : I_PACKED_SWITCH_TARGET_COUNT {$targetCount = Integer.parseInt($I_PACKED_SWITCH_TARGET_COUNT.text);}; - -packed_switch_targets[int baseOffset] returns[int[\] targets] - : - ^(I_PACKED_SWITCH_TARGETS - packed_switch_target_count - { - int targetCount = $packed_switch_target_count.targetCount; - $targets = new int[targetCount]; - int targetsPosition = 0; - } - - (offset_or_label - { - targets[targetsPosition++] = $offset_or_label.offsetValue - $baseOffset; - })* - ); - -sparse_switch_target_count returns[int targetCount] - : I_SPARSE_SWITCH_TARGET_COUNT {$targetCount = Integer.parseInt($I_SPARSE_SWITCH_TARGET_COUNT.text);}; - -sparse_switch_keys[int targetCount] returns[int[\] keys] - : { - $keys = new int[$targetCount]; - int keysPosition = 0; - } - ^(I_SPARSE_SWITCH_KEYS - (fixed_32bit_literal - { - $keys[keysPosition++] = $fixed_32bit_literal.value; - })* - ); - -sparse_switch_targets[int baseOffset, int targetCount] returns[int[\] targets] - : { - $targets = new int[$targetCount]; - int targetsPosition = 0; - } - ^(I_SPARSE_SWITCH_TARGETS - (offset_or_label - { - $targets[targetsPosition++] = $offset_or_label.offsetValue - $baseOffset; - })* - ); - -method returns[ ClassDataItem.EncodedMethod encodedMethod, - AnnotationDirectoryItem.MethodAnnotation methodAnnotationSet, - AnnotationDirectoryItem.ParameterAnnotation parameterAnnotationSets] - scope - { - HashMap labels; - TryListBuilder tryList; - int currentAddress; - DebugInfoBuilder debugInfo; - } - : { - $method::labels = new HashMap(); - $method::tryList = new TryListBuilder(); - $method::currentAddress = 0; - $method::debugInfo = new DebugInfoBuilder(); - } - ^( I_METHOD - method_name_and_prototype - access_list - registers_directive - labels - statements - catches - parameters - ordered_debug_directives - annotations - ) - { - MethodIdItem methodIdItem = $method_name_and_prototype.methodIdItem; - int registers = $registers_directive.registers; - int access = $access_list.value; - boolean isStatic = (access & AccessFlags.STATIC) != 0; - ArrayList instructions = $statements.instructions; - - - int minRegisters = methodIdItem.getParameterWordCount((access & AccessFlags.STATIC) != 0); - - if (registers < minRegisters) { - //TODO: throw the correct exception type - throw new RuntimeException( "This method requires at least " + - Integer.toString(minRegisters) + - " registers, for the method parameters"); - } - - Pair, List> temp = $method::tryList.encodeTries(dexFile); - List tries = temp.first; - List handlers = temp.second; - - - int methodParameterCount = methodIdItem.getParameterCount(); - if ($method::debugInfo.getParameterNameCount() > methodParameterCount) { - //TODO: throw the correct exception type - throw new RuntimeException( "Too many parameter names specified. This method only has " + - Integer.toString(methodParameterCount) + - " parameters."); - } - - DebugInfoItem debugInfoItem = $method::debugInfo.encodeDebugInfo(dexFile); - - CodeItem codeItem = new CodeItem(dexFile, - registers, - methodIdItem.getParameterWordCount(isStatic), - instructions, - debugInfoItem, - tries, - handlers); - - $encodedMethod = new ClassDataItem.EncodedMethod(dexFile, methodIdItem, access, codeItem); - - if ($annotations.annotationSetItem != null) { - $methodAnnotationSet = new AnnotationDirectoryItem.MethodAnnotation(dexFile, methodIdItem, $annotations.annotationSetItem); - } - - if ($parameters.parameterAnnotations != null) { - $parameterAnnotationSets = new AnnotationDirectoryItem.ParameterAnnotation(dexFile, methodIdItem, $parameters.parameterAnnotations); - } - }; - -method_prototype returns[ProtoIdItem protoIdItem] - : ^(I_METHOD_PROTOTYPE ^(I_METHOD_RETURN_TYPE type_descriptor) field_type_list) - { - TypeIdItem returnType = $type_descriptor.type; - ArrayList parameterTypes = $field_type_list.types; - - $protoIdItem = new ProtoIdItem(dexFile, returnType, parameterTypes); - }; - -method_name_and_prototype returns[MethodIdItem methodIdItem] - : MEMBER_NAME method_prototype - { - TypeIdItem classType = classDefItem.getClassType(); - String methodNameString = $MEMBER_NAME.text; - StringIdItem methodName = new StringIdItem(dexFile, methodNameString); - ProtoIdItem protoIdItem = $method_prototype.protoIdItem; - - $methodIdItem = new MethodIdItem(dexFile, classType, methodName, protoIdItem); - }; - -field_type_list returns[ArrayList types] - @init - { - $types = new ArrayList(); - } - : ( - field_type_descriptor - { - $types.add($field_type_descriptor.type); - } - )*; - - -fully_qualified_method returns[MethodIdItem methodIdItem] - : CLASS_NAME MEMBER_NAME method_prototype - { - TypeIdItem classType = new TypeIdItem(dexFile, "L" + $CLASS_NAME.text + ";"); - StringIdItem methodName = new StringIdItem(dexFile, $MEMBER_NAME.text); - ProtoIdItem prototype = $method_prototype.protoIdItem; - $methodIdItem = new MethodIdItem(dexFile, classType, methodName, prototype); - }; - -fully_qualified_field returns[FieldIdItem fieldIdItem] - : CLASS_NAME MEMBER_NAME field_type_descriptor - { - TypeIdItem classType = new TypeIdItem(dexFile, "L" + $CLASS_NAME.text + ";"); - StringIdItem fieldName = new StringIdItem(dexFile, $MEMBER_NAME.text); - TypeIdItem fieldType = $field_type_descriptor.type; - $fieldIdItem = new FieldIdItem(dexFile, classType, fieldName, fieldType); - }; - -registers_directive returns[int registers] - : ^(I_REGISTERS short_integral_literal) {$registers = $short_integral_literal.value;}; - -labels - : ^(I_LABELS label_def*); - -label_def - : ^(I_LABEL label address) - { - String labelName = $label.labelName; - if ($method::labels.containsKey(labelName)) { - //TODO: use appropriate exception type - throw new RuntimeException("Label " + labelName + " has multiple defintions."); - } - - - $method::labels.put(labelName, $address.address); - }; - -catches : ^(I_CATCHES catch_directive*); - -catch_directive - : ^(I_CATCH address field_type_descriptor from=offset_or_label_absolute[$address.address] to=offset_or_label_absolute[$address.address] using=offset_or_label_absolute[$address.address]) - { - TypeIdItem type = $field_type_descriptor.type; - int startAddress = $from.address; - int endAddress = $to.address; - int handlerAddress = $using.address; - - $method::tryList.addHandler(type, startAddress, endAddress, handlerAddress); - }; - -address returns[int address] - : I_ADDRESS - { - $address = Integer.parseInt($I_ADDRESS.text); - }; - -parameters returns[AnnotationSetRefList parameterAnnotations] - @init - { - int parameterCount = 0; - List annotationSetItems = new ArrayList(); - } - : ^(I_PARAMETERS (parameter - { - if ($parameter.parameterAnnotationSet != null) { - while (annotationSetItems.size() < parameterCount) { - annotationSetItems.add(new AnnotationSetItem(dexFile, -1)); - } - annotationSetItems.add($parameter.parameterAnnotationSet); - } - - parameterCount++; - })* - ) - { - if (annotationSetItems.size() > 0) { - while (annotationSetItems.size() < parameterCount) { - annotationSetItems.add(new AnnotationSetItem(dexFile, -1)); - } - $parameterAnnotations = new AnnotationSetRefList(dexFile, annotationSetItems); - } - }; - -parameter returns[AnnotationSetItem parameterAnnotationSet] - : ^(I_PARAMETER ( string_literal {$method::debugInfo.addParameterName($string_literal.value);} - | I_PARAMETER_NOT_SPECIFIED {$method::debugInfo.addParameterName(null);} - ) - annotations {$parameterAnnotationSet = $annotations.annotationSetItem;} - ); - -ordered_debug_directives - : ^(I_ORDERED_DEBUG_DIRECTIVES ( line - | local - | end_local - | restart_local - | prologue - | epilogue - | source - )*); - -line - : ^(I_LINE integral_literal address) - { - $method::debugInfo.addLine($address.address, $integral_literal.value); - }; - -local - : ^(I_LOCAL REGISTER SIMPLE_NAME field_type_descriptor string_literal? address) - { - int registerNumber = parseRegister_short($REGISTER.text); - - if ($string_literal.value != null) { - $method::debugInfo.addLocalExtended($address.address, registerNumber, $SIMPLE_NAME.text, $field_type_descriptor.type.toString(), $string_literal.value); - } else { - $method::debugInfo.addLocal($address.address, registerNumber, $SIMPLE_NAME.text, $field_type_descriptor.type.toString()); - } - }; - -end_local - : ^(I_END_LOCAL REGISTER address) - { - int registerNumber = parseRegister_short($REGISTER.text); - - $method::debugInfo.addEndLocal($address.address, registerNumber); - }; - -restart_local - : ^(I_RESTART_LOCAL REGISTER address) - { - int registerNumber = parseRegister_short($REGISTER.text); - - $method::debugInfo.addRestartLocal($address.address, registerNumber); - }; - -prologue - : ^(I_PROLOGUE address) - { - $method::debugInfo.addPrologue($address.address); - }; - -epilogue - : ^(I_EPILOGUE address) - { - $method::debugInfo.addEpilogue($address.address); - }; - -source - : ^(I_SOURCE string_literal address) - { - $method::debugInfo.addSetFile($address.address, $string_literal.value); - }; - -statements returns[ArrayList instructions] - @init - { - $instructions = new ArrayList(); - } - : ^(I_STATEMENTS (instruction - { - $instructions.add($instruction.instruction); - $method::currentAddress += $instruction.instruction.getBytes().length/2; - })*); - -label_ref returns[int labelAddress] - : label - { - String labelName = $label.labelName; - - Integer labelAdd = $method::labels.get(labelName); - - if (labelAdd == null) { - //TODO: throw correct exception type - throw new RuntimeException("Label \"" + labelName + "\" is not defined."); - } - - $labelAddress = labelAdd; - }; - - -label returns[String labelName] - : LABEL - { - String label = $LABEL.text; - return label.substring(0, label.length()-1); - }; - -offset returns[int offsetValue] - : OFFSET - { - String offsetText = $OFFSET.text; - $offsetValue = literalTools.parseInt(offsetText); - }; - -offset_or_label_absolute[int baseAddress] returns[int address] - : offset {$address = $offset.offsetValue + $baseAddress;} - | label_ref {$address = $label_ref.labelAddress;}; - -offset_or_label returns[int offsetValue] - : offset {$offsetValue = $offset.offsetValue;} - | label_ref {$offsetValue = $label_ref.labelAddress-$method::currentAddress;}; - -instruction returns[Instruction instruction] - : //e.g. goto endloop: - ^(I_STATEMENT_FORMAT10t INSTRUCTION_FORMAT10t offset_or_label) - { - Opcode opcode = Opcode.getOpcodeByName($INSTRUCTION_FORMAT10t.text); - - int addressOffset = $offset_or_label.offsetValue; - - if (addressOffset < Byte.MIN_VALUE || addressOffset > Byte.MAX_VALUE) { - //TODO: throw correct exception type - throw new RuntimeException("The offset/label is out of range. The offset is " + Integer.toString(addressOffset) + " and the range for this opcode is [-128, 127]."); - } - - $instruction = Format10t.Format.make(dexFile, opcode.value, (byte)addressOffset); - } - | //e.g. return - ^(I_STATEMENT_FORMAT10x INSTRUCTION_FORMAT10x) - { - Opcode opcode = Opcode.getOpcodeByName($INSTRUCTION_FORMAT10x.text); - $instruction = Format10x.Format.make(dexFile, opcode.value); - } - | //e.g. const/4 v0, 5 - ^(I_STATEMENT_FORMAT11n INSTRUCTION_FORMAT11n REGISTER short_integral_literal) - { - Opcode opcode = Opcode.getOpcodeByName($INSTRUCTION_FORMAT11n.text); - byte regA = parseRegister_nibble($REGISTER.text); - - short litB = $short_integral_literal.value; - literalTools.checkNibble(litB); - - $instruction = Format11n.Format.make(dexFile, opcode.value, regA, (byte)litB); - } - | //e.g. move-result-object v1 - ^(I_STATEMENT_FORMAT11x INSTRUCTION_FORMAT11x REGISTER) - { - Opcode opcode = Opcode.getOpcodeByName($INSTRUCTION_FORMAT11x.text); - short regA = parseRegister_byte($REGISTER.text); - - $instruction = Format11x.Format.make(dexFile, opcode.value, regA); - } - | //e.g. move v1 v2 - ^(I_STATEMENT_FORMAT12x INSTRUCTION_FORMAT12x registerA=REGISTER registerB=REGISTER) - { - Opcode opcode = Opcode.getOpcodeByName($INSTRUCTION_FORMAT12x.text); - byte regA = parseRegister_nibble($registerA.text); - byte regB = parseRegister_nibble($registerB.text); - - $instruction = Format12x.Format.make(dexFile, opcode.value, regA, regB); - } - | //e.g. goto/16 endloop: - ^(I_STATEMENT_FORMAT20t INSTRUCTION_FORMAT20t offset_or_label) - { - Opcode opcode = Opcode.getOpcodeByName($INSTRUCTION_FORMAT20t.text); - - int addressOffset = $offset_or_label.offsetValue; - - if (addressOffset < Short.MIN_VALUE || addressOffset > Short.MAX_VALUE) { - //TODO: throw correct exception type - throw new RuntimeException("The offset/label is out of range. The offset is " + Integer.toString(addressOffset) + " and the range for this opcode is [-32768, 32767]."); - } - - $instruction = Format20t.Format.make(dexFile, opcode.value, (short)addressOffset); - } - | //e.g. sget_object v0 java/lang/System/out LJava/io/PrintStream; - ^(I_STATEMENT_FORMAT21c_FIELD INSTRUCTION_FORMAT21c_FIELD REGISTER fully_qualified_field) - { - Opcode opcode = Opcode.getOpcodeByName($INSTRUCTION_FORMAT21c_FIELD.text); - short regA = parseRegister_byte($REGISTER.text); - - FieldIdItem fieldIdItem = $fully_qualified_field.fieldIdItem; - - $instruction = Format21c.Format.make(dexFile, opcode.value, regA, fieldIdItem); - } - | //e.g. const-string v1 "Hello World!" - ^(I_STATEMENT_FORMAT21c_STRING INSTRUCTION_FORMAT21c_STRING REGISTER string_literal) - { - Opcode opcode = Opcode.getOpcodeByName($INSTRUCTION_FORMAT21c_STRING.text); - short regA = parseRegister_byte($REGISTER.text); - - StringIdItem stringIdItem = new StringIdItem(dexFile, $string_literal.value); - - $instruction = Format21c.Format.make(dexFile, opcode.value, regA, stringIdItem); - } - | //e.g. const-class v2 org/JesusFreke/HelloWorld2/HelloWorld2 - ^(I_STATEMENT_FORMAT21c_TYPE INSTRUCTION_FORMAT21c_TYPE REGISTER class_or_array_type_descriptor) - { - Opcode opcode = Opcode.getOpcodeByName($INSTRUCTION_FORMAT21c_TYPE.text); - short regA = parseRegister_byte($REGISTER.text); - - TypeIdItem typeIdItem = $class_or_array_type_descriptor.type; - - $instruction = Format21c.Format.make(dexFile, opcode.value, regA, typeIdItem); - } - | //e.g. const/high16 v1, 1234 - ^(I_STATEMENT_FORMAT21h INSTRUCTION_FORMAT21h REGISTER short_integral_literal) - { - Opcode opcode = Opcode.getOpcodeByName($INSTRUCTION_FORMAT21h.text); - short regA = parseRegister_byte($REGISTER.text); - - short litB = $short_integral_literal.value; - - $instruction = Format21h.Format.make(dexFile, opcode.value, regA, litB); - } - | //e.g. const/16 v1, 1234 - ^(I_STATEMENT_FORMAT21s INSTRUCTION_FORMAT21s REGISTER short_integral_literal) - { - Opcode opcode = Opcode.getOpcodeByName($INSTRUCTION_FORMAT21s.text); - short regA = parseRegister_byte($REGISTER.text); - - short litB = $short_integral_literal.value; - - $instruction = Format21s.Format.make(dexFile, opcode.value, regA, litB); - } - | //e.g. if-eqz v0, endloop: - ^(I_STATEMENT_FORMAT21t INSTRUCTION_FORMAT21t REGISTER offset_or_label) - { - Opcode opcode = Opcode.getOpcodeByName($INSTRUCTION_FORMAT21t.text); - short regA = parseRegister_byte($REGISTER.text); - - int addressOffset = $offset_or_label.offsetValue; - - if (addressOffset < Short.MIN_VALUE || addressOffset > Short.MAX_VALUE) { - //TODO: throw correct exception type - throw new RuntimeException("The offset/label is out of range. The offset is " + Integer.toString(addressOffset) + " and the range for this opcode is [-32768, 32767]."); - } - - $instruction = Format21t.Format.make(dexFile, opcode.value, regA, (short)addressOffset); - } - | //e.g. add-int v0, v1, 123 - ^(I_STATEMENT_FORMAT22b INSTRUCTION_FORMAT22b registerA=REGISTER registerB=REGISTER short_integral_literal) - { - Opcode opcode = Opcode.getOpcodeByName($INSTRUCTION_FORMAT22b.text); - short regA = parseRegister_byte($registerA.text); - short regB = parseRegister_byte($registerB.text); - - short litC = $short_integral_literal.value; - literalTools.checkByte(litC); - - $instruction = Format22b.Format.make(dexFile, opcode.value, regA, regB, (byte)litC); - } - | //e.g. iput-object v1 v0 org/JesusFreke/HelloWorld2/HelloWorld2.helloWorld Ljava/lang/String; - ^(I_STATEMENT_FORMAT22c_FIELD INSTRUCTION_FORMAT22c_FIELD registerA=REGISTER registerB=REGISTER fully_qualified_field) - { - Opcode opcode = Opcode.getOpcodeByName($INSTRUCTION_FORMAT22c_FIELD.text); - byte regA = parseRegister_nibble($registerA.text); - byte regB = parseRegister_nibble($registerB.text); - - FieldIdItem fieldIdItem = $fully_qualified_field.fieldIdItem; - - $instruction = Format22c.Format.make(dexFile, opcode.value, regA, regB, fieldIdItem); - } - | //e.g. instance-of v0, v1, Ljava/lang/String; - ^(I_STATEMENT_FORMAT22c_TYPE INSTRUCTION_FORMAT22c_TYPE registerA=REGISTER registerB=REGISTER field_type_descriptor) - { - Opcode opcode = Opcode.getOpcodeByName($INSTRUCTION_FORMAT22c_TYPE.text); - byte regA = parseRegister_nibble($registerA.text); - byte regB = parseRegister_nibble($registerB.text); - - TypeIdItem typeIdItem = $field_type_descriptor.type; - - $instruction = Format22c.Format.make(dexFile, opcode.value, regA, regB, typeIdItem); - } - | //e.g. add-int/lit16 v0, v1, 12345 - ^(I_STATEMENT_FORMAT22s INSTRUCTION_FORMAT22s registerA=REGISTER registerB=REGISTER short_integral_literal) - { - Opcode opcode = Opcode.getOpcodeByName($INSTRUCTION_FORMAT22s.text); - byte regA = parseRegister_nibble($registerA.text); - byte regB = parseRegister_nibble($registerB.text); - - short litC = $short_integral_literal.value; - - $instruction = Format22s.Format.make(dexFile, opcode.value, regA, regB, litC); - } - | //e.g. if-eq v0, v1, endloop: - ^(I_STATEMENT_FORMAT22t INSTRUCTION_FORMAT22t registerA=REGISTER registerB=REGISTER offset_or_label) - { - Opcode opcode = Opcode.getOpcodeByName($INSTRUCTION_FORMAT22t.text); - byte regA = parseRegister_nibble($registerA.text); - byte regB = parseRegister_nibble($registerB.text); - - int addressOffset = $offset_or_label.offsetValue; - - if (addressOffset < Short.MIN_VALUE || addressOffset > Short.MAX_VALUE) { - //TODO: throw correct exception type - throw new RuntimeException("The offset/label is out of range. The offset is " + Integer.toString(addressOffset) + " and the range for this opcode is [-32768, 32767]."); - } - - $instruction = Format22t.Format.make(dexFile, opcode.value, regA, regB, (short)addressOffset); - } - | //e.g. move/from16 v1, v1234 - ^(I_STATEMENT_FORMAT22x INSTRUCTION_FORMAT22x registerA=REGISTER registerB=REGISTER) - { - Opcode opcode = Opcode.getOpcodeByName($INSTRUCTION_FORMAT22x.text); - short regA = parseRegister_byte($registerA.text); - int regB = parseRegister_short($registerB.text); - - $instruction = Format22x.Format.make(dexFile, opcode.value, regA, regB); - } - | //e.g. add-int v1, v2, v3 - ^(I_STATEMENT_FORMAT23x INSTRUCTION_FORMAT23x registerA=REGISTER registerB=REGISTER registerC=REGISTER) - { - Opcode opcode = Opcode.getOpcodeByName($INSTRUCTION_FORMAT23x.text); - short regA = parseRegister_byte($registerA.text); - short regB = parseRegister_byte($registerB.text); - short regC = parseRegister_byte($registerC.text); - - $instruction = Format23x.Format.make(dexFile, opcode.value, regA, regB, regC); - } - | //e.g. goto/32 endloop: - ^(I_STATEMENT_FORMAT30t INSTRUCTION_FORMAT30t offset_or_label) - { - Opcode opcode = Opcode.getOpcodeByName($INSTRUCTION_FORMAT30t.text); - - int addressOffset = $offset_or_label.offsetValue; - - $instruction = Format30t.Format.make(dexFile, opcode.value, addressOffset); - } - | //e.g. const-string/jumbo v1 "Hello World!" - ^(I_STATEMENT_FORMAT31c INSTRUCTION_FORMAT31c REGISTER string_literal) - { - Opcode opcode = Opcode.getOpcodeByName($INSTRUCTION_FORMAT31c.text); - short regA = parseRegister_byte($REGISTER.text); - - StringIdItem stringIdItem = new StringIdItem(dexFile, $string_literal.value); - - $instruction = Format31c.Format.make(dexFile, opcode.value, regA, stringIdItem); - } - | //e.g. const v0, 123456 - ^(I_STATEMENT_FORMAT31i INSTRUCTION_FORMAT31i REGISTER fixed_32bit_literal) - { - Opcode opcode = Opcode.getOpcodeByName($INSTRUCTION_FORMAT31i.text); - short regA = parseRegister_byte($REGISTER.text); - - int litB = $fixed_32bit_literal.value; - - $instruction = Format31i.Format.make(dexFile, opcode.value, regA, litB); - } - | //e.g. fill-array-data v0, ArrayData: - ^(I_STATEMENT_FORMAT31t INSTRUCTION_FORMAT31t REGISTER offset_or_label) - { - Opcode opcode = Opcode.getOpcodeByName($INSTRUCTION_FORMAT31t.text); - - short regA = parseRegister_byte($REGISTER.text); - - int addressOffset = $offset_or_label.offsetValue; - if (($method::currentAddress + addressOffset) \% 2 != 0) { - addressOffset++; - } - - $instruction = Format31t.Format.make(dexFile, opcode.value, regA, addressOffset); - } - | //e.g. move/16 v5678, v1234 - ^(I_STATEMENT_FORMAT32x INSTRUCTION_FORMAT32x registerA=REGISTER registerB=REGISTER) - { - Opcode opcode = Opcode.getOpcodeByName($INSTRUCTION_FORMAT32x.text); - int regA = parseRegister_short($registerA.text); - int regB = parseRegister_short($registerB.text); - - $instruction = Format32x.Format.make(dexFile, opcode.value, regA, regB); - } - | //e.g. invoke-virtual {v0,v1} java/io/PrintStream/print(Ljava/lang/Stream;)V - ^(I_STATEMENT_FORMAT35c_METHOD INSTRUCTION_FORMAT35c_METHOD register_list fully_qualified_method) - { - Opcode opcode = Opcode.getOpcodeByName($INSTRUCTION_FORMAT35c_METHOD.text); - - //this depends on the fact that register_list returns a byte[5] - byte[] registers = $register_list.registers; - byte registerCount = $register_list.registerCount; - - MethodIdItem methodIdItem = $fully_qualified_method.methodIdItem; - - $instruction = Format35c.Format.make(dexFile, opcode.value, registerCount, registers[0], registers[1], registers[2], registers[3], registers[4], methodIdItem); - } - | //e.g. invoke-virtual/range {v25..v26} java/lang/StringBuilder/append(Ljava/lang/String;)Ljava/lang/StringBuilder; - ^(I_STATEMENT_FORMAT3rc_METHOD INSTRUCTION_FORMAT3rc_METHOD register_range fully_qualified_method) - { - Opcode opcode = Opcode.getOpcodeByName($INSTRUCTION_FORMAT3rc_METHOD.text); - int startRegister = $register_range.startRegister; - int endRegister = $register_range.endRegister; - - int registerCount = endRegister-startRegister+1; - if (registerCount > 256) { - //TODO: throw appropriate exception type - throw new RuntimeException("A register range can span a maximum of 256 registers"); - } - if (registerCount < 1) { - //TODO: throw appropriate exception type - throw new RuntimeException("A register range must have the lower register listed first"); - } - - MethodIdItem methodIdItem = $fully_qualified_method.methodIdItem; - - //not supported yet - $instruction = Format3rc.Format.make(dexFile, opcode.value, (short)registerCount, startRegister, methodIdItem); - } - | //e.g. const-wide v0, 5000000000L - ^(I_STATEMENT_FORMAT51l INSTRUCTION_FORMAT51l REGISTER fixed_64bit_literal) - { - Opcode opcode = Opcode.getOpcodeByName($INSTRUCTION_FORMAT51l.text); - short regA = parseRegister_byte($REGISTER.text); - - long litB = $fixed_64bit_literal.value; - - $instruction = Format51l.Format.make(dexFile, opcode.value, regA, litB); - } - | //e.g. .array-data 4 1000000 .end array-data - ^(I_STATEMENT_ARRAY_DATA ^(I_ARRAY_ELEMENT_SIZE short_integral_literal) array_elements) - { - int elementWidth = $short_integral_literal.value; - List byteValues = $array_elements.values; - - $instruction = ArrayData.make(dexFile, elementWidth, byteValues); - } - | - - ^(I_STATEMENT_PACKED_SWITCH ^(I_PACKED_SWITCH_BASE_OFFSET base_offset=offset_or_label) ^(I_PACKED_SWITCH_START_KEY fixed_32bit_literal) packed_switch_targets[$base_offset.offsetValue]) - { - int startKey = $fixed_32bit_literal.value; - int[] targets = $packed_switch_targets.targets; - - $instruction = PackedSwitchData.make(dexFile, startKey, targets); - } - | - ^(I_STATEMENT_SPARSE_SWITCH ^(I_SPARSE_SWITCH_BASE_OFFSET base_offset=offset_or_label) sparse_switch_target_count sparse_switch_keys[$sparse_switch_target_count.targetCount] sparse_switch_targets[$base_offset.offsetValue, $sparse_switch_target_count.targetCount]) - { - int[] keys = $sparse_switch_keys.keys; - int[] targets = $sparse_switch_targets.targets; - - $instruction = SparseSwitchData.make(dexFile, keys, targets); - } - ; - - -register_list returns[byte[\] registers, byte registerCount] - @init - { - $registers = new byte[5]; - $registerCount = 0; - } - : ^(I_REGISTER_LIST - (REGISTER - { - if ($registerCount == 5) { - //TODO: throw the correct type of exception - throw new RuntimeException("A list of registers can only have a maximum of 5 registers. Use the /range alternate opcode instead."); - } - $registers[$registerCount++] = parseRegister_nibble($REGISTER.text); - })*); - -register_range returns[int startRegister, int endRegister] - : ^(I_REGISTER_RANGE startReg=REGISTER endReg=REGISTER?) - { - $startRegister = parseRegister_short($startReg.text); - if ($endReg == null) { - $endRegister = $startRegister; - } else { - $endRegister = parseRegister_short($endReg.text); - } - } - ; - -field_type_descriptor returns [TypeIdItem type] - : (PRIMITIVE_TYPE - | CLASS_DESCRIPTOR - | ARRAY_DESCRIPTOR) - { - $type = new TypeIdItem(dexFile, $start.getText()); - }; - -class_or_array_type_descriptor returns [TypeIdItem type] - : (CLASS_DESCRIPTOR - | ARRAY_DESCRIPTOR) - { - $type = new TypeIdItem(dexFile, $start.getText()); - }; - -class_type_descriptor returns [TypeIdItem type] - : CLASS_DESCRIPTOR - { - $type = new TypeIdItem(dexFile, $CLASS_DESCRIPTOR.text); - }; - -type_descriptor returns [TypeIdItem type] - : VOID_TYPE {$type = new TypeIdItem(dexFile, "V");} - | field_type_descriptor {$type = $field_type_descriptor.type;} - ; - -short_integral_literal returns[short value] - : long_literal - { - literalTools.checkShort($long_literal.value); - $value = (short)$long_literal.value; - } - | integer_literal - { - literalTools.checkShort($integer_literal.value); - $value = (short)$integer_literal.value; - } - | short_literal {$value = $short_literal.value;} - | byte_literal {$value = $byte_literal.value;}; - -integral_literal returns[int value] - : long_literal - { - literalTools.checkInt($long_literal.value); - $value = (short)$long_literal.value; - } - | integer_literal {$value = (short)$integer_literal.value;} - | short_literal {$value = $short_literal.value;} - | byte_literal {$value = $byte_literal.value;}; - - -integer_literal returns[int value] - : INTEGER_LITERAL { $value = literalTools.parseInt($INTEGER_LITERAL.text); }; - -long_literal returns[long value] - : LONG_LITERAL { $value = literalTools.parseLong($LONG_LITERAL.text); }; - -short_literal returns[short value] - : SHORT_LITERAL { $value = literalTools.parseShort($SHORT_LITERAL.text); }; - -byte_literal returns[byte value] - : BYTE_LITERAL { $value = literalTools.parseByte($BYTE_LITERAL.text); }; - -float_literal returns[float value] - : FLOAT_LITERAL { $value = Float.parseFloat($FLOAT_LITERAL.text); }; - -double_literal returns[double value] - : DOUBLE_LITERAL { $value = Double.parseDouble($DOUBLE_LITERAL.text); }; - -char_literal returns[char value] - : CHAR_LITERAL { $value = $CHAR_LITERAL.text.charAt(0); }; - -string_literal returns[String value] - : STRING_LITERAL - { - $value = $STRING_LITERAL.text; - $value = $value.substring(1,$value.length()-1); - }; - -bool_literal returns[boolean value] - : BOOL_LITERAL { $value = Boolean.parseBoolean($BOOL_LITERAL.text); }; - -array_literal returns[ArrayList values] - : {$values = new ArrayList();} - ^(I_ENCODED_ARRAY (literal {$values.add($literal.encodedValue);})*); - - -annotations returns[AnnotationSetItem annotationSetItem] - : {ArrayList annotationList = new ArrayList();} - ^(I_ANNOTATIONS (annotation {annotationList.add($annotation.annotationItem);} )*) - { - if (annotationList.size() > 0) { - $annotationSetItem = new AnnotationSetItem(dexFile, annotationList); - } - }; - - -annotation returns[AnnotationItem annotationItem] - : ^(I_ANNOTATION ANNOTATION_VISIBILITY subannotation) - { - AnnotationVisibility visibility = AnnotationVisibility.fromName($ANNOTATION_VISIBILITY.text); - $annotationItem = new AnnotationItem(dexFile, visibility, $subannotation.value); - }; - -annotation_element returns[AnnotationElement element] - : ^(I_ANNOTATION_ELEMENT MEMBER_NAME literal) - { - $element = new AnnotationElement(dexFile, new StringIdItem(dexFile, $MEMBER_NAME.text), $literal.encodedValue); - }; - -subannotation returns[AnnotationEncodedValueSubField value] - : {ArrayList elements = new ArrayList();} - ^( I_SUBANNOTATION - class_type_descriptor - (annotation_element {elements.add($annotation_element.element);} )* ) - { - $value = new AnnotationEncodedValueSubField(dexFile, $class_type_descriptor.type, elements); - }; - +/* + * [The "BSD licence"] + * Copyright (c) 2009 Ben Gruver + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +tree grammar smaliTreeWalker; + +options { + tokenVocab=smaliParser; + ASTLabelType=CommonTree; +} + +@header { +package org.JesusFreke.smali; + +import java.util.HashMap; + +import org.JesusFreke.dexlib.*; +import org.JesusFreke.dexlib.EncodedValue.*; +import org.JesusFreke.dexlib.util.*; +import org.JesusFreke.dexlib.code.*; +import org.JesusFreke.dexlib.code.Format.*; +} + +@members { + public DexFile dexFile; + public ClassDefItem classDefItem; + public ClassDataItem classDataItem; + + private static byte parseRegister_nibble(String register) { + //register should be in the format "v12" + byte val = Byte.parseByte(register.substring(1)); + if (val >= 2<<4) { + //TODO: throw correct exception type + throw new RuntimeException("The maximum allowed register in this context is list of registers is v15"); + } + //the parser wouldn't accept a negative register, i.e. v-1, so we don't have to check for val<0; + return val; + } + + //return a short, because java's byte is signed + private static short parseRegister_byte(String register) { + //register should be in the format "v123" + short val = Short.parseShort(register.substring(1)); + if (val >= 2<<8) { + //TODO: throw correct exception type + throw new RuntimeException("The maximum allowed register in this context is v255"); + } + return val; + } + + //return an int because java's short is signed + private static int parseRegister_short(String register) { + //register should be in the format "v12345" + int val = Integer.parseInt(register.substring(1)); + if (val >= 2<<16) { + //TODO: throw correct exception type + throw new RuntimeException("The maximum allowed register in this context is v65535"); + } + //the parser wouldn't accept a negative register, i.e. v-1, so we don't have to check for val<0; + return val; + } +} + + + +smali_file + : ^(I_CLASS_DEF header methods fields annotations) + { + AnnotationDirectoryItem annotationDirectoryItem = null; + + if ( $methods.methodAnnotationSets != null || + $methods.parameterAnnotationSets != null || + $fields.fieldAnnotationSets != null || + $annotations.annotationSetItem != null) { + annotationDirectoryItem = new AnnotationDirectoryItem( + dexFile, + $annotations.annotationSetItem, + $fields.fieldAnnotationSets, + $methods.methodAnnotationSets, + $methods.parameterAnnotationSets); + } + + classDefItem.setAnnotations(annotationDirectoryItem); + }; + + +header returns[TypeIdItem classType, int accessFlags, TypeIdItem superType, TypeListItem implementsList, StringIdItem sourceSpec] +: class_spec super_spec implements_list source_spec + { + classDataItem = new ClassDataItem(dexFile, 0); + classDefItem = new ClassDefItem(dexFile, $class_spec.type, $class_spec.accessFlags, + $super_spec.type, $implements_list.implementsList, $source_spec.source, classDataItem); + }; + +class_spec returns[TypeIdItem type, int accessFlags] + : class_type_descriptor access_list + { + $type = $class_type_descriptor.type; + $accessFlags = $access_list.value; + }; + +super_spec returns[TypeIdItem type] + : ^(I_SUPER class_type_descriptor) + { + $type = $class_type_descriptor.type; + }; + + +implements_spec returns[TypeIdItem type] + : ^(I_IMPLEMENTS class_type_descriptor) + { + $type = $class_type_descriptor.type; + }; + +implements_list returns[TypeListItem implementsList] +@init { ArrayList typeList; } + : {typeList = new ArrayList();} + (implements_spec {typeList.add($implements_spec.type);} )* + {if (typeList.size() > 0) $implementsList = new TypeListItem(dexFile, typeList); + else $implementsList = null;}; + +source_spec returns[StringIdItem source] + : {$source = null;} + ^(I_SOURCE string_literal {$source = new StringIdItem(dexFile, $string_literal.value);}) + | ; + + + +access_list returns [int value] + @init + { + $value = 0; + } + : ^(I_ACCESS_LIST + ( + ACCESS_SPEC + { + $value |= AccessFlags.getValueForAccessFlag($ACCESS_SPEC.getText()); + } + )+); + +fields returns[List fieldAnnotationSets] + : ^(I_FIELDS + (field + { + classDefItem.addField($field.encodedField, $field.encodedValue); + if ($field.fieldAnnotationSet != null) { + if ($fieldAnnotationSets == null) { + $fieldAnnotationSets = new ArrayList(); + } + fieldAnnotationSets.add($field.fieldAnnotationSet); + } + })*); + +methods returns[List methodAnnotationSets, + List parameterAnnotationSets] + : ^(I_METHODS + (method + { + classDataItem.addMethod($method.encodedMethod); + if ($method.methodAnnotationSet != null) { + if ($methodAnnotationSets == null) { + $methodAnnotationSets = new ArrayList(); + } + $methodAnnotationSets.add($method.methodAnnotationSet); + } + if ($method.parameterAnnotationSets != null) { + if ($parameterAnnotationSets == null) { + $parameterAnnotationSets = new ArrayList(); + } + $parameterAnnotationSets.add($method.parameterAnnotationSets); + } + })*); + +field returns[ClassDataItem.EncodedField encodedField, EncodedValue encodedValue, AnnotationDirectoryItem.FieldAnnotation fieldAnnotationSet] + :^(I_FIELD MEMBER_NAME access_list ^(I_FIELD_TYPE field_type_descriptor) field_initial_value annotations?) + { + TypeIdItem classType = classDefItem.getClassType(); + StringIdItem memberName = new StringIdItem(dexFile, $MEMBER_NAME.text); + TypeIdItem fieldType = $field_type_descriptor.type; + + FieldIdItem fieldIdItem = new FieldIdItem(dexFile, classType, memberName, fieldType); + $encodedField = new ClassDataItem.EncodedField(dexFile, fieldIdItem, $access_list.value); + + if ($field_initial_value.encodedValue != null) { + if (($access_list.value & AccessFlags.STATIC) == 0) { + //TODO: change to an appropriate exception type? + throw new RuntimeException("Initial field values can only be specified for static fields."); + } + + $encodedValue = $field_initial_value.encodedValue; + } else { + $encodedValue = null; + } + + if ($annotations.annotationSetItem != null) { + $fieldAnnotationSet = new AnnotationDirectoryItem.FieldAnnotation(dexFile, fieldIdItem, $annotations.annotationSetItem); + } + }; + + +field_initial_value returns[EncodedValue encodedValue] + : ^(I_FIELD_INITIAL_VALUE literal) {$encodedValue = $literal.encodedValue;} + | ; + +literal returns[EncodedValue encodedValue] + : integer_literal { $encodedValue = new EncodedValue(dexFile, new IntEncodedValueSubField($integer_literal.value)); } + | long_literal { $encodedValue = new EncodedValue(dexFile, new LongEncodedValueSubField($long_literal.value)); } + | short_literal { $encodedValue = new EncodedValue(dexFile, new ShortEncodedValueSubField($short_literal.value)); } + | byte_literal { $encodedValue = new EncodedValue(dexFile, new ByteEncodedValueSubField($byte_literal.value)); } + | float_literal { $encodedValue = new EncodedValue(dexFile, new FloatEncodedValueSubField($float_literal.value)); } + | double_literal { $encodedValue = new EncodedValue(dexFile, new DoubleEncodedValueSubField($double_literal.value)); } + | char_literal { $encodedValue = new EncodedValue(dexFile, new CharEncodedValueSubField($char_literal.value)); } + | string_literal { $encodedValue = new EncodedValue(dexFile, new EncodedIndexedItemReference(dexFile, new StringIdItem(dexFile, $string_literal.value))); } + | bool_literal { $encodedValue = new EncodedValue(dexFile, new BoolEncodedValueSubField($bool_literal.value)); } + | type_descriptor { $encodedValue = new EncodedValue(dexFile, new EncodedIndexedItemReference(dexFile, $type_descriptor.type)); } + | array_literal { $encodedValue = new EncodedValue(dexFile, new ArrayEncodedValueSubField(dexFile, $array_literal.values)); } + | subannotation { $encodedValue = new EncodedValue(dexFile, $subannotation.value); }; + + +//everything but string +fixed_size_literal returns[byte[\] value] + : integer_literal { $value = literalTools.intToBytes($integer_literal.value); } + | long_literal { $value = literalTools.longToBytes($long_literal.value); } + | short_literal { $value = literalTools.shortToBytes($short_literal.value); } + | byte_literal { $value = new byte[] { $byte_literal.value }; } + | float_literal { $value = literalTools.floatToBytes($float_literal.value); } + | double_literal { $value = literalTools.doubleToBytes($double_literal.value); } + | char_literal { $value = literalTools.charToBytes($char_literal.value); } + | bool_literal { $value = literalTools.boolToBytes($bool_literal.value); }; + +//everything but string +fixed_64bit_literal returns[long value] + : integer_literal { $value = $integer_literal.value; } + | long_literal { $value = $long_literal.value; } + | short_literal { $value = $short_literal.value; } + | byte_literal { $value = $byte_literal.value; } + | float_literal { $value = Float.floatToRawIntBits($float_literal.value); } + | double_literal { $value = Double.doubleToRawLongBits($double_literal.value); } + | char_literal { $value = $char_literal.value; } + | bool_literal { $value = $bool_literal.value?1:0; }; + +//everything but string and double +//long is allowed, but it must fit into an int +fixed_32bit_literal returns[int value] + : integer_literal { $value = $integer_literal.value; } + | long_literal { literalTools.checkInt($long_literal.value); $value = (int)$long_literal.value; } + | short_literal { $value = $short_literal.value; } + | byte_literal { $value = $byte_literal.value; } + | float_literal { $value = Float.floatToRawIntBits($float_literal.value); } + | char_literal { $value = $char_literal.value; } + | bool_literal { $value = $bool_literal.value?1:0; }; + +array_elements returns[List values] + : {$values = new ArrayList();} + ^(I_ARRAY_ELEMENTS + (fixed_size_literal + { + $values.add($fixed_size_literal.value); + })*); + +packed_switch_target_count returns[int targetCount] + : I_PACKED_SWITCH_TARGET_COUNT {$targetCount = Integer.parseInt($I_PACKED_SWITCH_TARGET_COUNT.text);}; + +packed_switch_targets[int baseOffset] returns[int[\] targets] + : + ^(I_PACKED_SWITCH_TARGETS + packed_switch_target_count + { + int targetCount = $packed_switch_target_count.targetCount; + $targets = new int[targetCount]; + int targetsPosition = 0; + } + + (offset_or_label + { + targets[targetsPosition++] = $offset_or_label.offsetValue - $baseOffset; + })* + ); + +sparse_switch_target_count returns[int targetCount] + : I_SPARSE_SWITCH_TARGET_COUNT {$targetCount = Integer.parseInt($I_SPARSE_SWITCH_TARGET_COUNT.text);}; + +sparse_switch_keys[int targetCount] returns[int[\] keys] + : { + $keys = new int[$targetCount]; + int keysPosition = 0; + } + ^(I_SPARSE_SWITCH_KEYS + (fixed_32bit_literal + { + $keys[keysPosition++] = $fixed_32bit_literal.value; + })* + ); + +sparse_switch_targets[int baseOffset, int targetCount] returns[int[\] targets] + : { + $targets = new int[$targetCount]; + int targetsPosition = 0; + } + ^(I_SPARSE_SWITCH_TARGETS + (offset_or_label + { + $targets[targetsPosition++] = $offset_or_label.offsetValue - $baseOffset; + })* + ); + +method returns[ ClassDataItem.EncodedMethod encodedMethod, + AnnotationDirectoryItem.MethodAnnotation methodAnnotationSet, + AnnotationDirectoryItem.ParameterAnnotation parameterAnnotationSets] + scope + { + HashMap labels; + TryListBuilder tryList; + int currentAddress; + DebugInfoBuilder debugInfo; + } + : { + $method::labels = new HashMap(); + $method::tryList = new TryListBuilder(); + $method::currentAddress = 0; + $method::debugInfo = new DebugInfoBuilder(); + } + ^( I_METHOD + method_name_and_prototype + access_list + registers_directive + labels + statements + catches + parameters + ordered_debug_directives + annotations + ) + { + MethodIdItem methodIdItem = $method_name_and_prototype.methodIdItem; + int registers = $registers_directive.registers; + int access = $access_list.value; + boolean isStatic = (access & AccessFlags.STATIC) != 0; + ArrayList instructions = $statements.instructions; + + + int minRegisters = methodIdItem.getParameterRegisterCount((access & AccessFlags.STATIC) != 0); + + if (registers < minRegisters) { + //TODO: throw the correct exception type + throw new RuntimeException( "This method requires at least " + + Integer.toString(minRegisters) + + " registers, for the method parameters"); + } + + Pair, List> temp = $method::tryList.encodeTries(dexFile); + List tries = temp.first; + List handlers = temp.second; + + + int methodParameterCount = methodIdItem.getParameterCount(); + if ($method::debugInfo.getParameterNameCount() > methodParameterCount) { + //TODO: throw the correct exception type + throw new RuntimeException( "Too many parameter names specified. This method only has " + + Integer.toString(methodParameterCount) + + " parameters."); + } + + DebugInfoItem debugInfoItem = $method::debugInfo.encodeDebugInfo(dexFile); + + CodeItem codeItem = new CodeItem(dexFile, + registers, + methodIdItem.getParameterRegisterCount(isStatic), + instructions, + debugInfoItem, + tries, + handlers); + + $encodedMethod = new ClassDataItem.EncodedMethod(dexFile, methodIdItem, access, codeItem); + + if ($annotations.annotationSetItem != null) { + $methodAnnotationSet = new AnnotationDirectoryItem.MethodAnnotation(dexFile, methodIdItem, $annotations.annotationSetItem); + } + + if ($parameters.parameterAnnotations != null) { + $parameterAnnotationSets = new AnnotationDirectoryItem.ParameterAnnotation(dexFile, methodIdItem, $parameters.parameterAnnotations); + } + }; + +method_prototype returns[ProtoIdItem protoIdItem] + : ^(I_METHOD_PROTOTYPE ^(I_METHOD_RETURN_TYPE type_descriptor) field_type_list) + { + TypeIdItem returnType = $type_descriptor.type; + ArrayList parameterTypes = $field_type_list.types; + + $protoIdItem = new ProtoIdItem(dexFile, returnType, parameterTypes); + }; + +method_name_and_prototype returns[MethodIdItem methodIdItem] + : MEMBER_NAME method_prototype + { + TypeIdItem classType = classDefItem.getClassType(); + String methodNameString = $MEMBER_NAME.text; + StringIdItem methodName = new StringIdItem(dexFile, methodNameString); + ProtoIdItem protoIdItem = $method_prototype.protoIdItem; + + $methodIdItem = new MethodIdItem(dexFile, classType, methodName, protoIdItem); + }; + +field_type_list returns[ArrayList types] + @init + { + $types = new ArrayList(); + } + : ( + field_type_descriptor + { + $types.add($field_type_descriptor.type); + } + )*; + + +fully_qualified_method returns[MethodIdItem methodIdItem] + : CLASS_NAME MEMBER_NAME method_prototype + { + TypeIdItem classType = new TypeIdItem(dexFile, "L" + $CLASS_NAME.text + ";"); + StringIdItem methodName = new StringIdItem(dexFile, $MEMBER_NAME.text); + ProtoIdItem prototype = $method_prototype.protoIdItem; + $methodIdItem = new MethodIdItem(dexFile, classType, methodName, prototype); + }; + +fully_qualified_field returns[FieldIdItem fieldIdItem] + : CLASS_NAME MEMBER_NAME field_type_descriptor + { + TypeIdItem classType = new TypeIdItem(dexFile, "L" + $CLASS_NAME.text + ";"); + StringIdItem fieldName = new StringIdItem(dexFile, $MEMBER_NAME.text); + TypeIdItem fieldType = $field_type_descriptor.type; + $fieldIdItem = new FieldIdItem(dexFile, classType, fieldName, fieldType); + }; + +registers_directive returns[int registers] + : ^(I_REGISTERS short_integral_literal) {$registers = $short_integral_literal.value;}; + +labels + : ^(I_LABELS label_def*); + +label_def + : ^(I_LABEL label address) + { + String labelName = $label.labelName; + if ($method::labels.containsKey(labelName)) { + //TODO: use appropriate exception type + throw new RuntimeException("Label " + labelName + " has multiple defintions."); + } + + + $method::labels.put(labelName, $address.address); + }; + +catches : ^(I_CATCHES catch_directive*); + +catch_directive + : ^(I_CATCH address field_type_descriptor from=offset_or_label_absolute[$address.address] to=offset_or_label_absolute[$address.address] using=offset_or_label_absolute[$address.address]) + { + TypeIdItem type = $field_type_descriptor.type; + int startAddress = $from.address; + int endAddress = $to.address; + int handlerAddress = $using.address; + + $method::tryList.addHandler(type, startAddress, endAddress, handlerAddress); + }; + +address returns[int address] + : I_ADDRESS + { + $address = Integer.parseInt($I_ADDRESS.text); + }; + +parameters returns[AnnotationSetRefList parameterAnnotations] + @init + { + int parameterCount = 0; + List annotationSetItems = new ArrayList(); + } + : ^(I_PARAMETERS (parameter + { + if ($parameter.parameterAnnotationSet != null) { + while (annotationSetItems.size() < parameterCount) { + annotationSetItems.add(new AnnotationSetItem(dexFile, -1)); + } + annotationSetItems.add($parameter.parameterAnnotationSet); + } + + parameterCount++; + })* + ) + { + if (annotationSetItems.size() > 0) { + while (annotationSetItems.size() < parameterCount) { + annotationSetItems.add(new AnnotationSetItem(dexFile, -1)); + } + $parameterAnnotations = new AnnotationSetRefList(dexFile, annotationSetItems); + } + }; + +parameter returns[AnnotationSetItem parameterAnnotationSet] + : ^(I_PARAMETER ( string_literal {$method::debugInfo.addParameterName($string_literal.value);} + | I_PARAMETER_NOT_SPECIFIED {$method::debugInfo.addParameterName(null);} + ) + annotations {$parameterAnnotationSet = $annotations.annotationSetItem;} + ); + +ordered_debug_directives + : ^(I_ORDERED_DEBUG_DIRECTIVES ( line + | local + | end_local + | restart_local + | prologue + | epilogue + | source + )*); + +line + : ^(I_LINE integral_literal address) + { + $method::debugInfo.addLine($address.address, $integral_literal.value); + }; + +local + : ^(I_LOCAL REGISTER SIMPLE_NAME field_type_descriptor string_literal? address) + { + int registerNumber = parseRegister_short($REGISTER.text); + + if ($string_literal.value != null) { + $method::debugInfo.addLocalExtended($address.address, registerNumber, $SIMPLE_NAME.text, $field_type_descriptor.type.getTypeDescriptor(), $string_literal.value); + } else { + $method::debugInfo.addLocal($address.address, registerNumber, $SIMPLE_NAME.text, $field_type_descriptor.type.getTypeDescriptor()); + } + }; + +end_local + : ^(I_END_LOCAL REGISTER address) + { + int registerNumber = parseRegister_short($REGISTER.text); + + $method::debugInfo.addEndLocal($address.address, registerNumber); + }; + +restart_local + : ^(I_RESTART_LOCAL REGISTER address) + { + int registerNumber = parseRegister_short($REGISTER.text); + + $method::debugInfo.addRestartLocal($address.address, registerNumber); + }; + +prologue + : ^(I_PROLOGUE address) + { + $method::debugInfo.addPrologue($address.address); + }; + +epilogue + : ^(I_EPILOGUE address) + { + $method::debugInfo.addEpilogue($address.address); + }; + +source + : ^(I_SOURCE string_literal address) + { + $method::debugInfo.addSetFile($address.address, $string_literal.value); + }; + +statements returns[ArrayList instructions] + @init + { + $instructions = new ArrayList(); + } + : ^(I_STATEMENTS (instruction + { + $instructions.add($instruction.instruction); + $method::currentAddress += $instruction.instruction.getBytes().length/2; + })*); + +label_ref returns[int labelAddress] + : label + { + String labelName = $label.labelName; + + Integer labelAdd = $method::labels.get(labelName); + + if (labelAdd == null) { + //TODO: throw correct exception type + throw new RuntimeException("Label \"" + labelName + "\" is not defined."); + } + + $labelAddress = labelAdd; + }; + + +label returns[String labelName] + : LABEL + { + String label = $LABEL.text; + return label.substring(0, label.length()-1); + }; + +offset returns[int offsetValue] + : OFFSET + { + String offsetText = $OFFSET.text; + $offsetValue = literalTools.parseInt(offsetText); + }; + +offset_or_label_absolute[int baseAddress] returns[int address] + : offset {$address = $offset.offsetValue + $baseAddress;} + | label_ref {$address = $label_ref.labelAddress;}; + +offset_or_label returns[int offsetValue] + : offset {$offsetValue = $offset.offsetValue;} + | label_ref {$offsetValue = $label_ref.labelAddress-$method::currentAddress;}; + +instruction returns[Instruction instruction] + : //e.g. goto endloop: + ^(I_STATEMENT_FORMAT10t INSTRUCTION_FORMAT10t offset_or_label) + { + Opcode opcode = Opcode.getOpcodeByName($INSTRUCTION_FORMAT10t.text); + + int addressOffset = $offset_or_label.offsetValue; + + if (addressOffset < Byte.MIN_VALUE || addressOffset > Byte.MAX_VALUE) { + //TODO: throw correct exception type + throw new RuntimeException("The offset/label is out of range. The offset is " + Integer.toString(addressOffset) + " and the range for this opcode is [-128, 127]."); + } + + $instruction = Format10t.Format.make(dexFile, opcode.value, (byte)addressOffset); + } + | //e.g. return + ^(I_STATEMENT_FORMAT10x INSTRUCTION_FORMAT10x) + { + Opcode opcode = Opcode.getOpcodeByName($INSTRUCTION_FORMAT10x.text); + $instruction = Format10x.Format.make(dexFile, opcode.value); + } + | //e.g. const/4 v0, 5 + ^(I_STATEMENT_FORMAT11n INSTRUCTION_FORMAT11n REGISTER short_integral_literal) + { + Opcode opcode = Opcode.getOpcodeByName($INSTRUCTION_FORMAT11n.text); + byte regA = parseRegister_nibble($REGISTER.text); + + short litB = $short_integral_literal.value; + literalTools.checkNibble(litB); + + $instruction = Format11n.Format.make(dexFile, opcode.value, regA, (byte)litB); + } + | //e.g. move-result-object v1 + ^(I_STATEMENT_FORMAT11x INSTRUCTION_FORMAT11x REGISTER) + { + Opcode opcode = Opcode.getOpcodeByName($INSTRUCTION_FORMAT11x.text); + short regA = parseRegister_byte($REGISTER.text); + + $instruction = Format11x.Format.make(dexFile, opcode.value, regA); + } + | //e.g. move v1 v2 + ^(I_STATEMENT_FORMAT12x INSTRUCTION_FORMAT12x registerA=REGISTER registerB=REGISTER) + { + Opcode opcode = Opcode.getOpcodeByName($INSTRUCTION_FORMAT12x.text); + byte regA = parseRegister_nibble($registerA.text); + byte regB = parseRegister_nibble($registerB.text); + + $instruction = Format12x.Format.make(dexFile, opcode.value, regA, regB); + } + | //e.g. goto/16 endloop: + ^(I_STATEMENT_FORMAT20t INSTRUCTION_FORMAT20t offset_or_label) + { + Opcode opcode = Opcode.getOpcodeByName($INSTRUCTION_FORMAT20t.text); + + int addressOffset = $offset_or_label.offsetValue; + + if (addressOffset < Short.MIN_VALUE || addressOffset > Short.MAX_VALUE) { + //TODO: throw correct exception type + throw new RuntimeException("The offset/label is out of range. The offset is " + Integer.toString(addressOffset) + " and the range for this opcode is [-32768, 32767]."); + } + + $instruction = Format20t.Format.make(dexFile, opcode.value, (short)addressOffset); + } + | //e.g. sget_object v0 java/lang/System/out LJava/io/PrintStream; + ^(I_STATEMENT_FORMAT21c_FIELD INSTRUCTION_FORMAT21c_FIELD REGISTER fully_qualified_field) + { + Opcode opcode = Opcode.getOpcodeByName($INSTRUCTION_FORMAT21c_FIELD.text); + short regA = parseRegister_byte($REGISTER.text); + + FieldIdItem fieldIdItem = $fully_qualified_field.fieldIdItem; + + $instruction = Format21c.Format.make(dexFile, opcode.value, regA, fieldIdItem); + } + | //e.g. const-string v1 "Hello World!" + ^(I_STATEMENT_FORMAT21c_STRING INSTRUCTION_FORMAT21c_STRING REGISTER string_literal) + { + Opcode opcode = Opcode.getOpcodeByName($INSTRUCTION_FORMAT21c_STRING.text); + short regA = parseRegister_byte($REGISTER.text); + + StringIdItem stringIdItem = new StringIdItem(dexFile, $string_literal.value); + + $instruction = Format21c.Format.make(dexFile, opcode.value, regA, stringIdItem); + } + | //e.g. const-class v2 org/JesusFreke/HelloWorld2/HelloWorld2 + ^(I_STATEMENT_FORMAT21c_TYPE INSTRUCTION_FORMAT21c_TYPE REGISTER class_or_array_type_descriptor) + { + Opcode opcode = Opcode.getOpcodeByName($INSTRUCTION_FORMAT21c_TYPE.text); + short regA = parseRegister_byte($REGISTER.text); + + TypeIdItem typeIdItem = $class_or_array_type_descriptor.type; + + $instruction = Format21c.Format.make(dexFile, opcode.value, regA, typeIdItem); + } + | //e.g. const/high16 v1, 1234 + ^(I_STATEMENT_FORMAT21h INSTRUCTION_FORMAT21h REGISTER short_integral_literal) + { + Opcode opcode = Opcode.getOpcodeByName($INSTRUCTION_FORMAT21h.text); + short regA = parseRegister_byte($REGISTER.text); + + short litB = $short_integral_literal.value; + + $instruction = Format21h.Format.make(dexFile, opcode.value, regA, litB); + } + | //e.g. const/16 v1, 1234 + ^(I_STATEMENT_FORMAT21s INSTRUCTION_FORMAT21s REGISTER short_integral_literal) + { + Opcode opcode = Opcode.getOpcodeByName($INSTRUCTION_FORMAT21s.text); + short regA = parseRegister_byte($REGISTER.text); + + short litB = $short_integral_literal.value; + + $instruction = Format21s.Format.make(dexFile, opcode.value, regA, litB); + } + | //e.g. if-eqz v0, endloop: + ^(I_STATEMENT_FORMAT21t INSTRUCTION_FORMAT21t REGISTER offset_or_label) + { + Opcode opcode = Opcode.getOpcodeByName($INSTRUCTION_FORMAT21t.text); + short regA = parseRegister_byte($REGISTER.text); + + int addressOffset = $offset_or_label.offsetValue; + + if (addressOffset < Short.MIN_VALUE || addressOffset > Short.MAX_VALUE) { + //TODO: throw correct exception type + throw new RuntimeException("The offset/label is out of range. The offset is " + Integer.toString(addressOffset) + " and the range for this opcode is [-32768, 32767]."); + } + + $instruction = Format21t.Format.make(dexFile, opcode.value, regA, (short)addressOffset); + } + | //e.g. add-int v0, v1, 123 + ^(I_STATEMENT_FORMAT22b INSTRUCTION_FORMAT22b registerA=REGISTER registerB=REGISTER short_integral_literal) + { + Opcode opcode = Opcode.getOpcodeByName($INSTRUCTION_FORMAT22b.text); + short regA = parseRegister_byte($registerA.text); + short regB = parseRegister_byte($registerB.text); + + short litC = $short_integral_literal.value; + literalTools.checkByte(litC); + + $instruction = Format22b.Format.make(dexFile, opcode.value, regA, regB, (byte)litC); + } + | //e.g. iput-object v1 v0 org/JesusFreke/HelloWorld2/HelloWorld2.helloWorld Ljava/lang/String; + ^(I_STATEMENT_FORMAT22c_FIELD INSTRUCTION_FORMAT22c_FIELD registerA=REGISTER registerB=REGISTER fully_qualified_field) + { + Opcode opcode = Opcode.getOpcodeByName($INSTRUCTION_FORMAT22c_FIELD.text); + byte regA = parseRegister_nibble($registerA.text); + byte regB = parseRegister_nibble($registerB.text); + + FieldIdItem fieldIdItem = $fully_qualified_field.fieldIdItem; + + $instruction = Format22c.Format.make(dexFile, opcode.value, regA, regB, fieldIdItem); + } + | //e.g. instance-of v0, v1, Ljava/lang/String; + ^(I_STATEMENT_FORMAT22c_TYPE INSTRUCTION_FORMAT22c_TYPE registerA=REGISTER registerB=REGISTER field_type_descriptor) + { + Opcode opcode = Opcode.getOpcodeByName($INSTRUCTION_FORMAT22c_TYPE.text); + byte regA = parseRegister_nibble($registerA.text); + byte regB = parseRegister_nibble($registerB.text); + + TypeIdItem typeIdItem = $field_type_descriptor.type; + + $instruction = Format22c.Format.make(dexFile, opcode.value, regA, regB, typeIdItem); + } + | //e.g. add-int/lit16 v0, v1, 12345 + ^(I_STATEMENT_FORMAT22s INSTRUCTION_FORMAT22s registerA=REGISTER registerB=REGISTER short_integral_literal) + { + Opcode opcode = Opcode.getOpcodeByName($INSTRUCTION_FORMAT22s.text); + byte regA = parseRegister_nibble($registerA.text); + byte regB = parseRegister_nibble($registerB.text); + + short litC = $short_integral_literal.value; + + $instruction = Format22s.Format.make(dexFile, opcode.value, regA, regB, litC); + } + | //e.g. if-eq v0, v1, endloop: + ^(I_STATEMENT_FORMAT22t INSTRUCTION_FORMAT22t registerA=REGISTER registerB=REGISTER offset_or_label) + { + Opcode opcode = Opcode.getOpcodeByName($INSTRUCTION_FORMAT22t.text); + byte regA = parseRegister_nibble($registerA.text); + byte regB = parseRegister_nibble($registerB.text); + + int addressOffset = $offset_or_label.offsetValue; + + if (addressOffset < Short.MIN_VALUE || addressOffset > Short.MAX_VALUE) { + //TODO: throw correct exception type + throw new RuntimeException("The offset/label is out of range. The offset is " + Integer.toString(addressOffset) + " and the range for this opcode is [-32768, 32767]."); + } + + $instruction = Format22t.Format.make(dexFile, opcode.value, regA, regB, (short)addressOffset); + } + | //e.g. move/from16 v1, v1234 + ^(I_STATEMENT_FORMAT22x INSTRUCTION_FORMAT22x registerA=REGISTER registerB=REGISTER) + { + Opcode opcode = Opcode.getOpcodeByName($INSTRUCTION_FORMAT22x.text); + short regA = parseRegister_byte($registerA.text); + int regB = parseRegister_short($registerB.text); + + $instruction = Format22x.Format.make(dexFile, opcode.value, regA, regB); + } + | //e.g. add-int v1, v2, v3 + ^(I_STATEMENT_FORMAT23x INSTRUCTION_FORMAT23x registerA=REGISTER registerB=REGISTER registerC=REGISTER) + { + Opcode opcode = Opcode.getOpcodeByName($INSTRUCTION_FORMAT23x.text); + short regA = parseRegister_byte($registerA.text); + short regB = parseRegister_byte($registerB.text); + short regC = parseRegister_byte($registerC.text); + + $instruction = Format23x.Format.make(dexFile, opcode.value, regA, regB, regC); + } + | //e.g. goto/32 endloop: + ^(I_STATEMENT_FORMAT30t INSTRUCTION_FORMAT30t offset_or_label) + { + Opcode opcode = Opcode.getOpcodeByName($INSTRUCTION_FORMAT30t.text); + + int addressOffset = $offset_or_label.offsetValue; + + $instruction = Format30t.Format.make(dexFile, opcode.value, addressOffset); + } + | //e.g. const-string/jumbo v1 "Hello World!" + ^(I_STATEMENT_FORMAT31c INSTRUCTION_FORMAT31c REGISTER string_literal) + { + Opcode opcode = Opcode.getOpcodeByName($INSTRUCTION_FORMAT31c.text); + short regA = parseRegister_byte($REGISTER.text); + + StringIdItem stringIdItem = new StringIdItem(dexFile, $string_literal.value); + + $instruction = Format31c.Format.make(dexFile, opcode.value, regA, stringIdItem); + } + | //e.g. const v0, 123456 + ^(I_STATEMENT_FORMAT31i INSTRUCTION_FORMAT31i REGISTER fixed_32bit_literal) + { + Opcode opcode = Opcode.getOpcodeByName($INSTRUCTION_FORMAT31i.text); + short regA = parseRegister_byte($REGISTER.text); + + int litB = $fixed_32bit_literal.value; + + $instruction = Format31i.Format.make(dexFile, opcode.value, regA, litB); + } + | //e.g. fill-array-data v0, ArrayData: + ^(I_STATEMENT_FORMAT31t INSTRUCTION_FORMAT31t REGISTER offset_or_label) + { + Opcode opcode = Opcode.getOpcodeByName($INSTRUCTION_FORMAT31t.text); + + short regA = parseRegister_byte($REGISTER.text); + + int addressOffset = $offset_or_label.offsetValue; + if (($method::currentAddress + addressOffset) \% 2 != 0) { + addressOffset++; + } + + $instruction = Format31t.Format.make(dexFile, opcode.value, regA, addressOffset); + } + | //e.g. move/16 v5678, v1234 + ^(I_STATEMENT_FORMAT32x INSTRUCTION_FORMAT32x registerA=REGISTER registerB=REGISTER) + { + Opcode opcode = Opcode.getOpcodeByName($INSTRUCTION_FORMAT32x.text); + int regA = parseRegister_short($registerA.text); + int regB = parseRegister_short($registerB.text); + + $instruction = Format32x.Format.make(dexFile, opcode.value, regA, regB); + } + | //e.g. invoke-virtual {v0,v1} java/io/PrintStream/print(Ljava/lang/Stream;)V + ^(I_STATEMENT_FORMAT35c_METHOD INSTRUCTION_FORMAT35c_METHOD register_list fully_qualified_method) + { + Opcode opcode = Opcode.getOpcodeByName($INSTRUCTION_FORMAT35c_METHOD.text); + + //this depends on the fact that register_list returns a byte[5] + byte[] registers = $register_list.registers; + byte registerCount = $register_list.registerCount; + + MethodIdItem methodIdItem = $fully_qualified_method.methodIdItem; + + $instruction = Format35c.Format.make(dexFile, opcode.value, registerCount, registers[0], registers[1], registers[2], registers[3], registers[4], methodIdItem); + } + | //e.g. invoke-virtual/range {v25..v26} java/lang/StringBuilder/append(Ljava/lang/String;)Ljava/lang/StringBuilder; + ^(I_STATEMENT_FORMAT3rc_METHOD INSTRUCTION_FORMAT3rc_METHOD register_range fully_qualified_method) + { + Opcode opcode = Opcode.getOpcodeByName($INSTRUCTION_FORMAT3rc_METHOD.text); + int startRegister = $register_range.startRegister; + int endRegister = $register_range.endRegister; + + int registerCount = endRegister-startRegister+1; + if (registerCount > 256) { + //TODO: throw appropriate exception type + throw new RuntimeException("A register range can span a maximum of 256 registers"); + } + if (registerCount < 1) { + //TODO: throw appropriate exception type + throw new RuntimeException("A register range must have the lower register listed first"); + } + + MethodIdItem methodIdItem = $fully_qualified_method.methodIdItem; + + //not supported yet + $instruction = Format3rc.Format.make(dexFile, opcode.value, (short)registerCount, startRegister, methodIdItem); + } + | //e.g. const-wide v0, 5000000000L + ^(I_STATEMENT_FORMAT51l INSTRUCTION_FORMAT51l REGISTER fixed_64bit_literal) + { + Opcode opcode = Opcode.getOpcodeByName($INSTRUCTION_FORMAT51l.text); + short regA = parseRegister_byte($REGISTER.text); + + long litB = $fixed_64bit_literal.value; + + $instruction = Format51l.Format.make(dexFile, opcode.value, regA, litB); + } + | //e.g. .array-data 4 1000000 .end array-data + ^(I_STATEMENT_ARRAY_DATA ^(I_ARRAY_ELEMENT_SIZE short_integral_literal) array_elements) + { + int elementWidth = $short_integral_literal.value; + List byteValues = $array_elements.values; + + $instruction = ArrayData.make(dexFile, elementWidth, byteValues); + } + | + + ^(I_STATEMENT_PACKED_SWITCH ^(I_PACKED_SWITCH_BASE_OFFSET base_offset=offset_or_label) ^(I_PACKED_SWITCH_START_KEY fixed_32bit_literal) packed_switch_targets[$base_offset.offsetValue]) + { + int startKey = $fixed_32bit_literal.value; + int[] targets = $packed_switch_targets.targets; + + $instruction = PackedSwitchData.make(dexFile, startKey, targets); + } + | + ^(I_STATEMENT_SPARSE_SWITCH ^(I_SPARSE_SWITCH_BASE_OFFSET base_offset=offset_or_label) sparse_switch_target_count sparse_switch_keys[$sparse_switch_target_count.targetCount] sparse_switch_targets[$base_offset.offsetValue, $sparse_switch_target_count.targetCount]) + { + int[] keys = $sparse_switch_keys.keys; + int[] targets = $sparse_switch_targets.targets; + + $instruction = SparseSwitchData.make(dexFile, keys, targets); + } + ; + + +register_list returns[byte[\] registers, byte registerCount] + @init + { + $registers = new byte[5]; + $registerCount = 0; + } + : ^(I_REGISTER_LIST + (REGISTER + { + if ($registerCount == 5) { + //TODO: throw the correct type of exception + throw new RuntimeException("A list of registers can only have a maximum of 5 registers. Use the /range alternate opcode instead."); + } + $registers[$registerCount++] = parseRegister_nibble($REGISTER.text); + })*); + +register_range returns[int startRegister, int endRegister] + : ^(I_REGISTER_RANGE startReg=REGISTER endReg=REGISTER?) + { + $startRegister = parseRegister_short($startReg.text); + if ($endReg == null) { + $endRegister = $startRegister; + } else { + $endRegister = parseRegister_short($endReg.text); + } + } + ; + +field_type_descriptor returns [TypeIdItem type] + : (PRIMITIVE_TYPE + | CLASS_DESCRIPTOR + | ARRAY_DESCRIPTOR) + { + $type = new TypeIdItem(dexFile, $start.getText()); + }; + +class_or_array_type_descriptor returns [TypeIdItem type] + : (CLASS_DESCRIPTOR + | ARRAY_DESCRIPTOR) + { + $type = new TypeIdItem(dexFile, $start.getText()); + }; + +class_type_descriptor returns [TypeIdItem type] + : CLASS_DESCRIPTOR + { + $type = new TypeIdItem(dexFile, $CLASS_DESCRIPTOR.text); + }; + +type_descriptor returns [TypeIdItem type] + : VOID_TYPE {$type = new TypeIdItem(dexFile, "V");} + | field_type_descriptor {$type = $field_type_descriptor.type;} + ; + +short_integral_literal returns[short value] + : long_literal + { + literalTools.checkShort($long_literal.value); + $value = (short)$long_literal.value; + } + | integer_literal + { + literalTools.checkShort($integer_literal.value); + $value = (short)$integer_literal.value; + } + | short_literal {$value = $short_literal.value;} + | byte_literal {$value = $byte_literal.value;}; + +integral_literal returns[int value] + : long_literal + { + literalTools.checkInt($long_literal.value); + $value = (short)$long_literal.value; + } + | integer_literal {$value = (short)$integer_literal.value;} + | short_literal {$value = $short_literal.value;} + | byte_literal {$value = $byte_literal.value;}; + + +integer_literal returns[int value] + : INTEGER_LITERAL { $value = literalTools.parseInt($INTEGER_LITERAL.text); }; + +long_literal returns[long value] + : LONG_LITERAL { $value = literalTools.parseLong($LONG_LITERAL.text); }; + +short_literal returns[short value] + : SHORT_LITERAL { $value = literalTools.parseShort($SHORT_LITERAL.text); }; + +byte_literal returns[byte value] + : BYTE_LITERAL { $value = literalTools.parseByte($BYTE_LITERAL.text); }; + +float_literal returns[float value] + : FLOAT_LITERAL { $value = Float.parseFloat($FLOAT_LITERAL.text); }; + +double_literal returns[double value] + : DOUBLE_LITERAL { $value = Double.parseDouble($DOUBLE_LITERAL.text); }; + +char_literal returns[char value] + : CHAR_LITERAL { $value = $CHAR_LITERAL.text.charAt(0); }; + +string_literal returns[String value] + : STRING_LITERAL + { + $value = $STRING_LITERAL.text; + $value = $value.substring(1,$value.length()-1); + }; + +bool_literal returns[boolean value] + : BOOL_LITERAL { $value = Boolean.parseBoolean($BOOL_LITERAL.text); }; + +array_literal returns[ArrayList values] + : {$values = new ArrayList();} + ^(I_ENCODED_ARRAY (literal {$values.add($literal.encodedValue);})*); + + +annotations returns[AnnotationSetItem annotationSetItem] + : {ArrayList annotationList = new ArrayList();} + ^(I_ANNOTATIONS (annotation {annotationList.add($annotation.annotationItem);} )*) + { + if (annotationList.size() > 0) { + $annotationSetItem = new AnnotationSetItem(dexFile, annotationList); + } + }; + + +annotation returns[AnnotationItem annotationItem] + : ^(I_ANNOTATION ANNOTATION_VISIBILITY subannotation) + { + AnnotationVisibility visibility = AnnotationVisibility.fromName($ANNOTATION_VISIBILITY.text); + $annotationItem = new AnnotationItem(dexFile, visibility, $subannotation.value); + }; + +annotation_element returns[AnnotationElement element] + : ^(I_ANNOTATION_ELEMENT MEMBER_NAME literal) + { + $element = new AnnotationElement(dexFile, new StringIdItem(dexFile, $MEMBER_NAME.text), $literal.encodedValue); + }; + +subannotation returns[AnnotationEncodedValueSubField value] + : {ArrayList elements = new ArrayList();} + ^( I_SUBANNOTATION + class_type_descriptor + (annotation_element {elements.add($annotation_element.element);} )* ) + { + $value = new AnnotationEncodedValueSubField(dexFile, $class_type_descriptor.type, elements); + }; + diff --git a/src/main/java/org/JesusFreke/dexlib/AnnotationDirectoryItem.java b/src/main/java/org/JesusFreke/dexlib/AnnotationDirectoryItem.java index ece7ab12..1a104b3d 100644 --- a/src/main/java/org/JesusFreke/dexlib/AnnotationDirectoryItem.java +++ b/src/main/java/org/JesusFreke/dexlib/AnnotationDirectoryItem.java @@ -36,48 +36,50 @@ import java.util.Collections; //TODO: fix field names in dex-format.html and submit public class AnnotationDirectoryItem extends OffsettedItem { - private final Field[] fields; - private final ArrayList fieldAnnotationList = new ArrayList(); private final ArrayList methodAnnotationList = new ArrayList(); private final ArrayList parameterAnnotationList = new ArrayList(); - private final OffsettedItemReference classAnnotations; - private final ListSizeField annotatedFieldsCount; - private final ListSizeField annotatedMethodsCount; - private final ListSizeField annotatedParametersCount; - private final FieldListField fieldAnnotations; - private final FieldListField methodAnnotations; - private final FieldListField parameterAnnotations; - + private final OffsettedItemReference classAnnotationsReferenceField; + private final ListSizeField annotatedFieldsCountField; + private final ListSizeField annotatedMethodsCountField; + private final ListSizeField annotatedParametersCountField; + private final FieldListField fieldAnnotationListField; + private final FieldListField methodAnnotationListField; + private final FieldListField parameterAnnotationListField; + public AnnotationDirectoryItem(final DexFile dexFile, int offset) { super(offset); fields = new Field[] { - classAnnotations = new OffsettedItemReference(dexFile.AnnotationSetsSection, new IntegerField()), - annotatedFieldsCount = new ListSizeField(fieldAnnotationList, new IntegerField()), - annotatedMethodsCount = new ListSizeField(methodAnnotationList, new IntegerField()), - annotatedParametersCount = new ListSizeField(parameterAnnotationList, new IntegerField()), - fieldAnnotations = new FieldListField(fieldAnnotationList) { + classAnnotationsReferenceField = new OffsettedItemReference( + dexFile.AnnotationSetsSection, new IntegerField(null), "class_annotations_off"), + annotatedFieldsCountField = new ListSizeField(fieldAnnotationList, new IntegerField("fields_size")), + annotatedMethodsCountField = new ListSizeField(methodAnnotationList, + new IntegerField("annotated_methods_size")), + annotatedParametersCountField = new ListSizeField(parameterAnnotationList, + new IntegerField("annotated_parameters_size")), + fieldAnnotationListField = new FieldListField(fieldAnnotationList, + "field_annotations") { protected FieldAnnotation make() { return new FieldAnnotation(dexFile); } }, - methodAnnotations = new FieldListField(methodAnnotationList) { + methodAnnotationListField = new FieldListField(methodAnnotationList, + "method_annotations") { protected MethodAnnotation make() { return new MethodAnnotation(dexFile); } }, - parameterAnnotations = new FieldListField(parameterAnnotationList) { + parameterAnnotationListField = new FieldListField(parameterAnnotationList, + "parameter_annotations") { protected ParameterAnnotation make() { return new ParameterAnnotation(dexFile); } } }; } - - - + public AnnotationDirectoryItem(final DexFile dexFile, AnnotationSetItem classAnnotations, List fieldAnnotations, @@ -85,22 +87,21 @@ public class AnnotationDirectoryItem extends OffsettedItem parameterAnnotations) { this(dexFile, -1); - this.classAnnotations.setReference(classAnnotations); + classAnnotationsReferenceField.setReference(classAnnotations); - if (fieldAnnotations != null) { - this.fieldAnnotationList.addAll(fieldAnnotations); + if (fieldAnnotationListField != null) { + fieldAnnotationList.addAll(fieldAnnotations); } - if (methodAnnotations != null) { - this.methodAnnotationList.addAll(methodAnnotations); + if (methodAnnotationListField != null) { + methodAnnotationList.addAll(methodAnnotations); } if (parameterAnnotations != null) { - this.parameterAnnotationList.addAll(parameterAnnotations); + parameterAnnotationList.addAll(parameterAnnotations); } } - @Override public int place(int index, int offset) { Collections.sort(fieldAnnotationList); @@ -113,54 +114,53 @@ public class AnnotationDirectoryItem extends OffsettedItem implements Comparable { - private final Field[] fields; - - private final IndexedItemReference field; - private final OffsettedItemReference annotationSet; + private final IndexedItemReference fieldReferenceField; + private final OffsettedItemReference annotationSetReferenceField; public FieldAnnotation(DexFile dexFile) { + super("field_annotation"); fields = new Field[] { - field = new IndexedItemReference(dexFile.FieldIdsSection, new IntegerField()), - annotationSet = new OffsettedItemReference(dexFile.AnnotationSetsSection, new IntegerField()) + fieldReferenceField = new IndexedItemReference(dexFile.FieldIdsSection, + new IntegerField(null), "field_idx"), + annotationSetReferenceField = new OffsettedItemReference( + dexFile.AnnotationSetsSection, new IntegerField(null), "annotations_off") }; } public FieldAnnotation(DexFile dexFile, FieldIdItem field, AnnotationSetItem annotationSet) { this(dexFile); - this.field.setReference(field); - this.annotationSet.setReference(annotationSet); - } - - protected Field[] getFields() { - return fields; + this.fieldReferenceField.setReference(field); + this.annotationSetReferenceField.setReference(annotationSet); } public int compareTo(FieldAnnotation o) { - return ((Integer)field.getReference().getIndex()).compareTo(o.field.getReference().getIndex()); + return ((Integer) fieldReferenceField.getReference().getIndex()).compareTo( + o.fieldReferenceField.getReference().getIndex()); } } public static class MethodAnnotation extends CompositeField implements Comparable { - private final Field[] fields; - private final IndexedItemReference method; private final OffsettedItemReference annotationSet; public MethodAnnotation(DexFile dexFile) { + super("method_annotation"); fields = new Field[] { - method = new IndexedItemReference(dexFile.MethodIdsSection, new IntegerField()), - annotationSet = new OffsettedItemReference(dexFile.AnnotationSetsSection, new IntegerField()) + method = new IndexedItemReference(dexFile.MethodIdsSection, + new IntegerField(null), "method_idx"), + annotationSet = new OffsettedItemReference(dexFile.AnnotationSetsSection, + new IntegerField(null), "annotations_off") }; } @@ -170,10 +170,6 @@ public class AnnotationDirectoryItem extends OffsettedItem implements Comparable { - private final Field[] fields; - private final IndexedItemReference method; private final OffsettedItemReference parameterAnnotations; public ParameterAnnotation(DexFile dexFile) { + super("parameter_annotation"); fields = new Field[] { - method = new IndexedItemReference(dexFile.MethodIdsSection, new IntegerField()), + method = new IndexedItemReference(dexFile.MethodIdsSection, + new IntegerField(null), "method_idx"), parameterAnnotations = new OffsettedItemReference( - dexFile.AnnotationSetRefListsSection, new IntegerField()) + dexFile.AnnotationSetRefListsSection, new IntegerField(null), "annotations_off") }; } @@ -200,10 +196,6 @@ public class AnnotationDirectoryItem extends OffsettedItem { - private final Field[] fields; - - private final ByteField visibility; - private final AnnotationEncodedValueSubField annotation; + private final ByteField visibilityField; + private final AnnotationEncodedValueSubField annotationField; public AnnotationItem(DexFile dexFile, int offset) { super(offset); fields = new Field[] { - visibility = new ByteField(), - annotation = new AnnotationEncodedValueSubField(dexFile) + visibilityField = new ByteField("visibility"), + annotationField = new AnnotationEncodedValueSubField(dexFile) }; } @@ -51,41 +49,16 @@ public class AnnotationItem extends OffsettedItem { super(-1); fields = new Field[] { - this.visibility = new ByteField(visibility.value), - this.annotation = annotation + this.visibilityField = new ByteField(visibility.value, "visibility"), + this.annotationField = annotation }; } - protected int getAlignment() { - return 1; - } - - protected Field[] getFields() { - return fields; - } - public ItemType getItemType() { return ItemType.TYPE_ANNOTATION_ITEM; } - public void copyTo(DexFile dexFile, AnnotationItem copy) { - visibility.copyTo(dexFile, copy.visibility); - annotation.copyTo(dexFile, copy.annotation); - } - - public int hashCode() { - return visibility.hashCode() * 31 + annotation.hashCode(); - } - - public boolean equals(Object o) { - if (!(o instanceof AnnotationItem)) { - return false; - } - - AnnotationItem other = (AnnotationItem)o; - - if (!visibility.equals(other.visibility)) - return false; - return annotation.equals(other.annotation); + public String getConciseIdentity() { + return "annotation_item @0x" + Integer.toHexString(getOffset()); } } diff --git a/src/main/java/org/JesusFreke/dexlib/AnnotationSetItem.java b/src/main/java/org/JesusFreke/dexlib/AnnotationSetItem.java index c5e8b2ed..8157a433 100644 --- a/src/main/java/org/JesusFreke/dexlib/AnnotationSetItem.java +++ b/src/main/java/org/JesusFreke/dexlib/AnnotationSetItem.java @@ -34,23 +34,22 @@ import java.util.ArrayList; import java.util.List; public class AnnotationSetItem extends OffsettedItem { - private final Field[] fields; - private final ArrayList> annotationReferences = new ArrayList>(); - private final ListSizeField annotationCount; - private final FieldListField> annotations; + private final ListSizeField annotationCountField; + private final FieldListField> annotationsListField; public AnnotationSetItem(final DexFile dexFile, int offset) { super(offset); fields = new Field[] { - annotationCount = new ListSizeField(annotationReferences, new IntegerField()), - annotations = new FieldListField>(annotationReferences) { + annotationCountField = new ListSizeField(annotationReferences, new IntegerField("size")), + annotationsListField = new FieldListField>( + annotationReferences, "annotation") { protected OffsettedItemReference make() { return new OffsettedItemReference(dexFile.AnnotationsSection, - new IntegerField()); + new IntegerField(null), "annotation_off"); } } }; @@ -60,8 +59,9 @@ public class AnnotationSetItem extends OffsettedItem { this(dexFile, -1); for (AnnotationItem annotationItem: annotations) { - this.annotationReferences.add(new OffsettedItemReference(dexFile, - annotationItem, new IntegerField())); + OffsettedItemReference annotationReference = annotationsListField.make(); + annotationReference.setReference(annotationItem); + this.annotationReferences.add(annotationReference); } } @@ -69,11 +69,11 @@ public class AnnotationSetItem extends OffsettedItem { return 4; } - protected Field[] getFields() { - return fields; - } - public ItemType getItemType() { return ItemType.TYPE_ANNOTATION_SET_ITEM; } + + public String getConciseIdentity() { + return "annotation_set_item @0x" + Integer.toHexString(getOffset()); + } } diff --git a/src/main/java/org/JesusFreke/dexlib/AnnotationSetRefList.java b/src/main/java/org/JesusFreke/dexlib/AnnotationSetRefList.java index 90188dfd..c50c9ee0 100644 --- a/src/main/java/org/JesusFreke/dexlib/AnnotationSetRefList.java +++ b/src/main/java/org/JesusFreke/dexlib/AnnotationSetRefList.java @@ -34,22 +34,22 @@ import java.util.ArrayList; import java.util.List; public class AnnotationSetRefList extends OffsettedItem { - private final Field[] fields; - private final ArrayList> annotationSetReferences = new ArrayList>(); - private final ListSizeField annotationSetCount; - private final FieldListField> annotationSets; + private final ListSizeField annotationSetCountField; + private final FieldListField> annotationSetsListField; public AnnotationSetRefList(final DexFile dexFile, int offset) { super(offset); fields = new Field[] { - annotationSetCount = new ListSizeField(annotationSetReferences, new IntegerField()), - annotationSets = new FieldListField>(annotationSetReferences) { + annotationSetCountField = new ListSizeField(annotationSetReferences, new IntegerField("size")), + annotationSetsListField = new FieldListField>( + annotationSetReferences, "list") { protected OffsettedItemReference make() { - return new OffsettedItemReference(dexFile.AnnotationSetsSection, new IntegerField()); + return new OffsettedItemReference(dexFile.AnnotationSetsSection, + new IntegerField(null), "annotation_set_ref_item"); } } }; @@ -58,8 +58,10 @@ public class AnnotationSetRefList extends OffsettedItem { public AnnotationSetRefList(final DexFile dexFile, List annotationSets) { this(dexFile, -1); - for (AnnotationSetItem annotation: annotationSets) { - this.annotationSetReferences.add(new OffsettedItemReference(dexFile, annotation, new IntegerField())); + for (AnnotationSetItem annotationSet: annotationSets) { + OffsettedItemReference annotationSetReference = annotationSetsListField.make(); + annotationSetReference.setReference(annotationSet); + this.annotationSetReferences.add(annotationSetReference); } } @@ -67,11 +69,11 @@ public class AnnotationSetRefList extends OffsettedItem { return 4; } - protected Field[] getFields() { - return fields; - } - public ItemType getItemType() { return ItemType.TYPE_ANNOTATION_SET_REF_LIST; } + + public String getConciseIdentity() { + return "annotation_set_item @0x" + Integer.toHexString(getOffset()); + } } diff --git a/src/main/java/org/JesusFreke/dexlib/ByteField.java b/src/main/java/org/JesusFreke/dexlib/ByteField.java index 12e06874..2bea94b7 100644 --- a/src/main/java/org/JesusFreke/dexlib/ByteField.java +++ b/src/main/java/org/JesusFreke/dexlib/ByteField.java @@ -28,21 +28,16 @@ package org.JesusFreke.dexlib; -import org.JesusFreke.dexlib.util.Output; import org.JesusFreke.dexlib.util.Input; +import org.JesusFreke.dexlib.util.Output; public class ByteField extends CachedIntegerValueField { - protected byte value; - - public ByteField() { + public ByteField(String fieldName) { + super(fieldName); } - public ByteField(byte value) { - this.value = value; - } - - public void writeTo(Output out) { - out.writeByte(value); + public ByteField(byte value, String fieldName) { + super(value, fieldName); } public void readFrom(Input in) { @@ -53,11 +48,7 @@ public class ByteField extends CachedIntegerValueField { return offset + 1; } - public int getCachedValue() { - return value; - } - - public void cacheValue(int value) { - this.value = (byte)value; + public void writeValue(Output out) { + out.writeByte(value); } } diff --git a/src/main/java/org/JesusFreke/dexlib/CachedIntegerValueField.java b/src/main/java/org/JesusFreke/dexlib/CachedIntegerValueField.java index bcb75acb..ae8d6047 100644 --- a/src/main/java/org/JesusFreke/dexlib/CachedIntegerValueField.java +++ b/src/main/java/org/JesusFreke/dexlib/CachedIntegerValueField.java @@ -28,25 +28,64 @@ package org.JesusFreke.dexlib; -public abstract class CachedIntegerValueField implements Field { - public abstract int getCachedValue(); - public abstract void cacheValue(int value); +import org.JesusFreke.dexlib.util.AnnotatedOutput; +import org.JesusFreke.dexlib.util.Output; - public void copyTo(DexFile dexFile, CachedIntegerValueField copy) { - copy.cacheValue(getCachedValue()); +public abstract class CachedIntegerValueField + implements Field { + + private final String fieldName; + protected int value; + + protected CachedIntegerValueField(String fieldName) { + this.fieldName = fieldName; + } + + protected CachedIntegerValueField(int value, String fieldName) { + this(fieldName); + this.value = value; + } + + public void copyTo(DexFile dexFile, T copy) { + copy.value = value; } public int hashCode() { - return ((Integer)getCachedValue()).hashCode(); + return value; + } + + protected abstract void writeValue(Output out); + + public void writeTo(AnnotatedOutput out) { + if (fieldName != null) { + out.annotate(fieldName + ": 0x" + Integer.toHexString(getCachedValue())); + } + writeValue(out); } public boolean equals(Object o) { - if (getClass() != o.getClass()) { + //TODO: check if this returns false if o is a different subclass + if (!this.getClass().isInstance(o)) { return false; } - CachedIntegerValueField other = (CachedIntegerValueField)o; + T other = (T)o; return getCachedValue() == other.getCachedValue(); } + + /** + * This method returns the integer value that has been cached. This + * value is either the value that the field was constructed with, the + * value that was read via readFrom, or the value that was + * cached when place was called + * @return the cached value + */ + public int getCachedValue() { + return value; + } + + public void cacheValue(int value) { + this.value = value; + } } diff --git a/src/main/java/org/JesusFreke/dexlib/ClassDataItem.java b/src/main/java/org/JesusFreke/dexlib/ClassDataItem.java index 688fcc35..1801ce93 100644 --- a/src/main/java/org/JesusFreke/dexlib/ClassDataItem.java +++ b/src/main/java/org/JesusFreke/dexlib/ClassDataItem.java @@ -29,55 +29,55 @@ package org.JesusFreke.dexlib; import org.JesusFreke.dexlib.ItemType; -import org.JesusFreke.dexlib.util.Output; -import org.JesusFreke.dexlib.util.Input; -import org.JesusFreke.dexlib.util.AccessFlags; +import org.JesusFreke.dexlib.util.*; import java.util.ArrayList; import java.util.Collections; public class ClassDataItem extends OffsettedItem { - private final Field[] fields; - private final ArrayList staticFieldList = new ArrayList(); private final ArrayList instanceFieldList = new ArrayList(); private final ArrayList directMethodList = new ArrayList(); private final ArrayList virtualMethodList = new ArrayList(); - private final ListSizeField staticFieldsCount; - private final ListSizeField instanceFieldsCount; - private final ListSizeField directMethodsCount; - private final ListSizeField virtualMethodsCount; - private final EncodedMemberList staticFields; - private final EncodedMemberList instanceFields; - private final EncodedMemberList directMethods; - private final EncodedMemberList virtualMethods; + private final ListSizeField staticFieldsCountField; + private final ListSizeField instanceFieldsCountField; + private final ListSizeField directMethodsCountField; + private final ListSizeField virtualMethodsCountField; + private final EncodedMemberList staticFieldsListField; + private final EncodedMemberList instanceFieldsListField; + private final EncodedMemberList directMethodsListField; + private final EncodedMemberList virtualMethodsListField; public ClassDataItem(final DexFile dexFile, int offset) { super(offset); fields = new Field[] { - staticFieldsCount = new ListSizeField(staticFieldList, new Leb128Field()), - instanceFieldsCount = new ListSizeField(instanceFieldList, new Leb128Field()), - directMethodsCount = new ListSizeField(directMethodList, new Leb128Field()), - virtualMethodsCount = new ListSizeField(virtualMethodList, new Leb128Field()), - staticFields = new EncodedMemberList(staticFieldList) { + staticFieldsCountField = new ListSizeField(staticFieldList, + new Leb128Field("static_fields_size")), + instanceFieldsCountField = new ListSizeField(instanceFieldList, + new Leb128Field("instance_fields_size")), + directMethodsCountField = new ListSizeField(directMethodList, + new Leb128Field("direct_methods_size")), + virtualMethodsCountField = new ListSizeField(virtualMethodList, + new Leb128Field("virtual_methods_size")), + staticFieldsListField = new EncodedMemberList(staticFieldList, "static_fields") { protected EncodedField make(EncodedField previousField) { return new EncodedField(dexFile, previousField); } }, - instanceFields = new EncodedMemberList(instanceFieldList) { + instanceFieldsListField = new EncodedMemberList(instanceFieldList, "instance_fields") { protected EncodedField make(EncodedField previousField) { return new EncodedField(dexFile, previousField); } }, - directMethods = new EncodedMemberList(directMethodList) { + directMethodsListField = new EncodedMemberList(directMethodList, "direct_methods") { protected EncodedMethod make(EncodedMethod previousMethod) { return new EncodedMethod(dexFile, previousMethod); } }, - virtualMethods = new EncodedMemberList(virtualMethodList) { + virtualMethodsListField = new EncodedMemberList(virtualMethodList, "virtual_methods") { protected EncodedMethod make(EncodedMethod previousMethod) { return new EncodedMethod(dexFile, previousMethod); } @@ -112,19 +112,29 @@ public class ClassDataItem extends OffsettedItem { private static abstract class EncodedMember> extends CompositeField implements Field, Comparable { + public EncodedMember(String fieldName) { + super(fieldName); + } + protected abstract void setPreviousMember(T previousMember); } private static abstract class EncodedMemberList> implements Field> { private final ArrayList list; + private final String fieldName; - public EncodedMemberList(ArrayList list) { + public EncodedMemberList(ArrayList list, String fieldName) { this.list = list; + this.fieldName = fieldName; } - public void writeTo(Output out) { + public void writeTo(AnnotatedOutput out) { + out.annotate(0, fieldName + ":"); + int i=0; for (T field: list) { + out.annotate(0, "[0x" + Integer.toHexString(i) + "]"); field.writeTo(out); + i++; } } @@ -196,13 +206,12 @@ public class ClassDataItem extends OffsettedItem { } public static class EncodedField extends EncodedMember { - private final Field[] fields; - private final IndexedItemReference field; private final Leb128DeltaField fieldIndexField; private final Leb128Field accessFlags; public EncodedField(DexFile dexFile, final EncodedField previousField) { + super("encoded_field"); Leb128DeltaField previousIndexField = null; if (previousField != null) { previousIndexField = previousField.fieldIndexField; @@ -211,16 +220,17 @@ public class ClassDataItem extends OffsettedItem { fields = new Field[] { field = new IndexedItemReference(dexFile.FieldIdsSection, - fieldIndexField = new Leb128DeltaField(previousIndexField)), - accessFlags = new Leb128Field() + fieldIndexField = new Leb128DeltaField(previousIndexField, null), "field_idx_diff"), + accessFlags = new Leb128Field("access_flags") }; } public EncodedField(DexFile dexFile, FieldIdItem field, int accessFlags) { + super("encoded_field"); fields = new Field[] { this.field = new IndexedItemReference(dexFile, field, - fieldIndexField = new Leb128DeltaField(null)), - this.accessFlags = new Leb128Field(accessFlags) + fieldIndexField = new Leb128DeltaField(null), "field_idx_diff"), + this.accessFlags = new Leb128Field(accessFlags, "access_flags") }; } @@ -232,10 +242,6 @@ public class ClassDataItem extends OffsettedItem { } } - protected Field[] getFields() { - return fields; - } - public int compareTo(EncodedField other) { return field.getReference().compareTo(other.field.getReference()); @@ -251,14 +257,13 @@ public class ClassDataItem extends OffsettedItem { } public static class EncodedMethod extends EncodedMember { - private final Field[] fields; - private final IndexedItemReference method; private final Leb128DeltaField methodIndexField; private final Leb128Field accessFlags; private final OffsettedItemReference codeItem; public EncodedMethod(DexFile dexFile, final EncodedMethod previousMethod) { + super("encedod_method"); Leb128DeltaField previousIndexField = null; if (previousMethod != null) { previousIndexField = previousMethod.methodIndexField; @@ -266,18 +271,21 @@ public class ClassDataItem extends OffsettedItem { fields = new Field[] { method = new IndexedItemReference(dexFile.MethodIdsSection, - methodIndexField = new Leb128DeltaField(previousIndexField)), - accessFlags = new Leb128Field(), - codeItem = new OffsettedItemReference(dexFile.CodeItemsSection, new Leb128Field()) + methodIndexField = new Leb128DeltaField(previousIndexField, null), "method_idx_diff"), + accessFlags = new Leb128Field("access_flags"), + codeItem = new OffsettedItemReference(dexFile.CodeItemsSection, + new Leb128Field(null), "code_off") }; } public EncodedMethod(DexFile dexFile, MethodIdItem methodIdItem, int accessFlags, CodeItem codeItem) { + super("encoded_method"); fields = new Field[] { this.method = new IndexedItemReference(dexFile, methodIdItem, - methodIndexField = new Leb128DeltaField(null)), - this.accessFlags = new Leb128Field(accessFlags), - this.codeItem = new OffsettedItemReference(dexFile, codeItem, new Leb128Field()) + methodIndexField = new Leb128DeltaField(null), "method_idx_diff"), + this.accessFlags = new Leb128Field(accessFlags, "access_flags"), + this.codeItem = new OffsettedItemReference(dexFile, codeItem, + new Leb128Field(null), "code_off") }; } @@ -289,10 +297,6 @@ public class ClassDataItem extends OffsettedItem { } } - protected Field[] getFields() { - return fields; - } - public int compareTo(EncodedMethod other) { return method.getReference().compareTo(other.method.getReference()); @@ -310,42 +314,51 @@ public class ClassDataItem extends OffsettedItem { * item encodes the value as per normal */ protected static class Leb128DeltaField extends Leb128Field { - private Leb128DeltaField previousField; + private Leb128DeltaField previousField = null; - public Leb128DeltaField(Leb128DeltaField previousField) { + public Leb128DeltaField(String fieldName) { + super(fieldName); + } + + public void readFrom(Input in) { + super.readFrom(in); + value += getPreviousValue(); + } + + public int place(int offset) { + return offset + Leb128Utils.unsignedLeb128Size(value - getPreviousValue()); + } + + private int getPreviousValue() { + if (previousField == null) { + return 0; + } + return previousField.value; + } + + public void writeValue(Output out) { + out.writeUnsignedLeb128(value - getPreviousValue()); + } + + public Leb128DeltaField(Leb128DeltaField previousField, String fieldName) { + super(fieldName); this.previousField = previousField; } public void setPreviousField(Leb128DeltaField previousField) { this.previousField = previousField; } - - public int getCachedValue() { - if (previousField != null) { - return previousField.getCachedValue() + super.getCachedValue(); - } else { - return super.getCachedValue(); - } - } - - public void cacheValue(int value) { - if (previousField != null) { - super.cacheValue(value - previousField.getCachedValue()); - } else { - super.cacheValue(value); - } - } } protected int getAlignment() { return 1; } - protected Field[] getFields() { - return fields; - } - public ItemType getItemType() { return ItemType.TYPE_CLASS_DATA_ITEM; } + + public String getConciseIdentity() { + return "class_data_item @0x" + Integer.toHexString(getOffset()); + } } diff --git a/src/main/java/org/JesusFreke/dexlib/ClassDefItem.java b/src/main/java/org/JesusFreke/dexlib/ClassDefItem.java index f24b0f65..c1350360 100644 --- a/src/main/java/org/JesusFreke/dexlib/ClassDefItem.java +++ b/src/main/java/org/JesusFreke/dexlib/ClassDefItem.java @@ -37,16 +37,14 @@ import java.util.HashMap; import java.util.ArrayList; public class ClassDefItem extends IndexedItem { - private final Field[] fields; - - private final IndexedItemReference classType; - private final IntegerField accessFlags; - private final IndexedItemReference superclassType; - private final OffsettedItemReference classInterfacesList; - private final IndexedItemReference sourceFile; - private final OffsettedItemReference classAnnotations; - private final OffsettedItemReference classData; - private final OffsettedItemReference staticFieldInitialValues; + private final IndexedItemReference classTypeReferenceField; + private final IntegerField accessFlagsField; + private final IndexedItemReference superclassTypeReferenceField; + private final OffsettedItemReference classInterfacesListReferenceField; + private final IndexedItemReference sourceFileReferenceField; + private final OffsettedItemReference classAnnotationsReferenceField; + private final OffsettedItemReference classDataReferenceField; + private final OffsettedItemReference staticFieldInitialValuesReferenceField; private ArrayList staticFieldInitialValuesList; @@ -58,14 +56,22 @@ public class ClassDefItem extends IndexedItem { this.dexFile = dexFile; fields = new Field[] { - classType = new IndexedItemReference(dexFile.TypeIdsSection, new IntegerField()), - accessFlags = new IntegerField(), - superclassType = new IndexedItemReference(dexFile.TypeIdsSection, new IntegerField()), - classInterfacesList = new OffsettedItemReference(dexFile.TypeListsSection, new IntegerField()), - sourceFile = new IndexedItemReference(dexFile.StringIdsSection, new IntegerField()), - classAnnotations = new OffsettedItemReference(dexFile.AnnotationDirectoriesSection, new IntegerField()), - classData = new OffsettedItemReference(dexFile.ClassDataSection, new IntegerField()), - staticFieldInitialValues = new OffsettedItemReference(dexFile.EncodedArraysSection, new IntegerField()) + classTypeReferenceField = new IndexedItemReference(dexFile.TypeIdsSection, + new IntegerField(null), "class_idx"), + //TODO: add annotated output showing the flags + accessFlagsField = new IntegerField("access_flags:"), + superclassTypeReferenceField = new IndexedItemReference(dexFile.TypeIdsSection, + new IntegerField(null), "superclass_idx"), + classInterfacesListReferenceField = new OffsettedItemReference(dexFile.TypeListsSection, + new IntegerField(null), "interfaces_off"), + sourceFileReferenceField = new IndexedItemReference(dexFile.StringIdsSection, + new IntegerField(null), "source_file_off"), + classAnnotationsReferenceField = new OffsettedItemReference( + dexFile.AnnotationDirectoriesSection, new IntegerField(null), "annotations_off"), + classDataReferenceField = new OffsettedItemReference(dexFile.ClassDataSection, + new IntegerField(null), "class_data_off"), + staticFieldInitialValuesReferenceField = new OffsettedItemReference( + dexFile.EncodedArraysSection, new IntegerField(null), "static_values_off") }; } @@ -76,36 +82,30 @@ public class ClassDefItem extends IndexedItem { TypeListItem implementsList, StringIdItem source, ClassDataItem classDataItem) { - super(-1); + this(dexFile, -1); - this.dexFile = dexFile; - - fields = new Field[] { - this.classType = new IndexedItemReference(dexFile, classType, new IntegerField()), - this.accessFlags = new IntegerField(accessFlags), - superclassType = new IndexedItemReference(dexFile, superType, new IntegerField()), - classInterfacesList = new OffsettedItemReference(dexFile, implementsList, new IntegerField()), - sourceFile = new IndexedItemReference(dexFile, source, new IntegerField()), - classAnnotations = new OffsettedItemReference(dexFile.AnnotationDirectoriesSection, new IntegerField()), - classData = new OffsettedItemReference(dexFile, classDataItem, new IntegerField()), - staticFieldInitialValues = new OffsettedItemReference(dexFile.EncodedArraysSection, new IntegerField()) - }; + classTypeReferenceField.setReference(classType); + accessFlagsField.cacheValue(accessFlags); + superclassTypeReferenceField.setReference(superType); + classInterfacesListReferenceField.setReference(implementsList); + sourceFileReferenceField.setReference(source); + classDataReferenceField.setReference(classDataItem); } public TypeIdItem getSuperclass() { - return superclassType.getReference(); + return superclassTypeReferenceField.getReference(); } - public TypeIdItem getClassType() { - return classType.getReference(); + public TypeIdItem getClassTypeReferenceField() { + return classTypeReferenceField.getReference(); } protected int getAlignment() { return 4; } - protected Field[] getFields() { - return fields; + public TypeIdItem getClassType() { + return classTypeReferenceField.getReference(); } public ItemType getItemType() { @@ -113,15 +113,15 @@ public class ClassDefItem extends IndexedItem { } public String getClassName() { - return classType.getReference().toString(); + return classTypeReferenceField.getReference().getTypeDescriptor(); } - public String toString() { - return getClassName(); + public String getConciseIdentity() { + return "class_def_item: " + getClassName(); } public int hashCode() { - return classType.getReference().hashCode(); + return classTypeReferenceField.getReference().hashCode(); } public boolean equals(Object o) { @@ -129,7 +129,7 @@ public class ClassDefItem extends IndexedItem { return false; } ClassDefItem other = (ClassDefItem)o; - return classType.equals(other.classType); + return classTypeReferenceField.equals(other.classTypeReferenceField); } public int compareTo(ClassDefItem o) { @@ -145,7 +145,7 @@ public class ClassDefItem extends IndexedItem { throw new RuntimeException("Initial values are only allowed for static fields."); } - ClassDataItem classDataItem = this.classData.getReference(); + ClassDataItem classDataItem = this.classDataReferenceField.getReference(); int fieldIndex = classDataItem.addField(encodedField); if (initialValue != null) { @@ -153,13 +153,14 @@ public class ClassDefItem extends IndexedItem { staticFieldInitialValuesList = new ArrayList(); EncodedArrayItem encodedArrayItem = new EncodedArrayItem(dexFile, staticFieldInitialValuesList); - staticFieldInitialValues.setReference(encodedArrayItem); + staticFieldInitialValuesReferenceField.setReference(encodedArrayItem); } //All static fields before this one must have an initial value. Add any default values as needed for (int i=staticFieldInitialValuesList.size(); i < fieldIndex; i++) { ClassDataItem.EncodedField staticField = classDataItem.getStaticFieldAtIndex(i); - EncodedValueSubField subField = TypeUtils.makeDefaultValueForType(dexFile, staticField.getField().getFieldType().toString()); + EncodedValueSubField subField = TypeUtils.makeDefaultValueForType(dexFile, + staticField.getField().getFieldType().getTypeDescriptor()); EncodedValue encodedValue = new EncodedValue(dexFile, subField); staticFieldInitialValuesList.add(i, encodedValue); } @@ -169,7 +170,7 @@ public class ClassDefItem extends IndexedItem { } public void setAnnotations(AnnotationDirectoryItem annotations) { - this.classAnnotations.setReference(annotations); + this.classAnnotationsReferenceField.setReference(annotations); } public static int placeClassDefItems(IndexedSection section, int offset) { @@ -192,7 +193,7 @@ public class ClassDefItem extends IndexedItem { this.section = section; for (ClassDefItem classDefItem: section.items) { - TypeIdItem typeIdItem = classDefItem.classType.getReference(); + TypeIdItem typeIdItem = classDefItem.classTypeReferenceField.getReference(); classDefsByType.put(typeIdItem, classDefItem); } } @@ -212,14 +213,14 @@ public class ClassDefItem extends IndexedItem { private void placeClass(ClassDefItem classDefItem) { if (!classDefItem.isPlaced()) { - TypeIdItem superType = classDefItem.superclassType.getReference(); + TypeIdItem superType = classDefItem.superclassTypeReferenceField.getReference(); ClassDefItem superClassDefItem = classDefsByType.get(superType); if (superClassDefItem != null) { placeClass(superClassDefItem); } - TypeListItem interfaces = classDefItem.classInterfacesList.getReference(); + TypeListItem interfaces = classDefItem.classInterfacesListReferenceField.getReference(); if (interfaces != null) { for (TypeIdItem interfaceType: interfaces.getTypes()) { diff --git a/src/main/java/org/JesusFreke/dexlib/CodeItem.java b/src/main/java/org/JesusFreke/dexlib/CodeItem.java index f005ea77..3b6287c8 100644 --- a/src/main/java/org/JesusFreke/dexlib/CodeItem.java +++ b/src/main/java/org/JesusFreke/dexlib/CodeItem.java @@ -32,28 +32,27 @@ import org.JesusFreke.dexlib.code.Instruction; import org.JesusFreke.dexlib.code.Opcode; import org.JesusFreke.dexlib.ItemType; import org.JesusFreke.dexlib.util.Input; -import org.JesusFreke.dexlib.util.Output; +import org.JesusFreke.dexlib.util.AnnotatedOutput; import java.util.ArrayList; import java.util.List; import java.util.HashMap; public class CodeItem extends OffsettedItem { - private final Field[] fields; private final ArrayList instructionList; private final ArrayList tryItems = new ArrayList(); private final ArrayList catchHandlerList = new ArrayList(); - private final ShortIntegerField registersCount; - private final ShortIntegerField inArgumentCount; - private final ShortIntegerField outArgumentCount; - private final ListSizeField triesCount; - private final OffsettedItemReference debugInfo; - private final IntegerField instructionsSize; + private final ShortIntegerField registersCountField; + private final ShortIntegerField inArgumentCountField; + private final ShortIntegerField outArgumentCountField; + private final ListSizeField triesCountField; + private final OffsettedItemReference debugInfoReferenceField; + private final IntegerField instructionsSizeField; private final InstructionListField instructionListField; - private final PaddingField padding; - private final FieldListField tries; - private final EncodedCatchHandlerList catchHandlers; + private final PaddingField paddingField; + private final FieldListField triesListField; + private final EncodedCatchHandlerList catchHandlersListField; public CodeItem(final DexFile dexFile, int offset) { super(offset); @@ -61,37 +60,36 @@ public class CodeItem extends OffsettedItem { instructionList = new ArrayList(); fields = new Field[] { - registersCount = new ShortIntegerField(), - inArgumentCount = new ShortIntegerField(), - outArgumentCount = new ShortIntegerField(), - triesCount = new ListSizeField(tryItems, new ShortIntegerField()), - debugInfo = new OffsettedItemReference(dexFile.DebugInfoItemsSection, new IntegerField()), - instructionsSize = new IntegerField(), + registersCountField = new ShortIntegerField("registers_size"), + inArgumentCountField = new ShortIntegerField("ins_size"), + outArgumentCountField = new ShortIntegerField("outs_size"), + triesCountField = new ListSizeField(tryItems, new ShortIntegerField("tries_size")), + debugInfoReferenceField = new OffsettedItemReference(dexFile.DebugInfoItemsSection, + new IntegerField(null), "debug_off"), + instructionsSizeField = new IntegerField("insns_size"), instructionListField = new InstructionListField(dexFile), - padding = new PaddingField(), - tries = new FieldListField(tryItems) { + paddingField = new PaddingField(), + triesListField = new FieldListField(tryItems, "try_item") { protected TryItem make() { - return new TryItem(catchHandlers); + return new TryItem(catchHandlersListField); } }, - catchHandlers = new EncodedCatchHandlerList(dexFile) + catchHandlersListField = new EncodedCatchHandlerList(dexFile) }; } - - public CodeItem(final DexFile dexFile, int registersCount, int inArguments, - ArrayList instructions, + List instructions, DebugInfoItem debugInfo, List tries, List handlers) { - super(-1); + this(dexFile, 0); - this.instructionList = new ArrayList(instructions); - this.instructionListField = new InstructionListField(dexFile); + instructionList.addAll(instructions); + instructionsSizeField.cacheValue(instructionListField.getInstructionWordCount()); if (tries != null) { tryItems.addAll(tries); @@ -103,22 +101,10 @@ public class CodeItem extends OffsettedItem { throw new RuntimeException("The handlers parameter must be null if the tries parameter is null"); } - fields = new Field[] { - this.registersCount = new ShortIntegerField(registersCount), - this.inArgumentCount = new ShortIntegerField(inArguments), - this.outArgumentCount = new ShortIntegerField(instructionListField.getOutArguments()), - this.triesCount = new ListSizeField(tryItems, new ShortIntegerField(0)), - this.debugInfo = new OffsettedItemReference(dexFile, debugInfo, new IntegerField()), - this.instructionsSize = new IntegerField(instructionListField.getInstructionWordCount()), - instructionListField, - this.padding = new PaddingField(), - this.tries = new FieldListField(tryItems) { - protected TryItem make() { - return new TryItem(catchHandlers); - } - }, - this.catchHandlers = new EncodedCatchHandlerList(dexFile) - }; + registersCountField.cacheValue(registersCount); + inArgumentCountField.cacheValue(inArguments); + outArgumentCountField.cacheValue(instructionListField.getOutArguments()); + debugInfoReferenceField.setReference(debugInfo); } protected int getAlignment() { @@ -131,48 +117,43 @@ public class CodeItem extends OffsettedItem { public void copyTo(DexFile dexFile, CodeItem copy) { - Field[] fields = getFields(); - Field[] fieldsCopy = copy.getFields(); for (int i = 0; i < fields.length-2; i++) { - fields[i].copyTo(dexFile, fieldsCopy[i]); + fields[i].copyTo(dexFile, copy.fields[i]); } //we need to do this in reverse order, so when the tries are copied, //the catchHandler copies will already exist - catchHandlers.copyTo(dexFile, copy.catchHandlers); - tries.copyTo(dexFile, copy.tries); + catchHandlersListField.copyTo(dexFile, copy.catchHandlersListField); + triesListField.copyTo(dexFile, copy.triesListField); } - public Field[] getFields() { - return fields; + public String getConciseIdentity() { + //TODO: should mention the method name here + return "code_item @0x" + Integer.toHexString(getOffset()); } public static class TryItem extends CompositeField { - private final Field[] fields; - private final IntegerField startAddr; private final ShortIntegerField insnCount; private final EncodedCatchHandlerReference encodedCatchHandlerReference; public TryItem(EncodedCatchHandlerList encodedCatchHandlerList) { + super("try_item"); fields = new Field[] { - startAddr = new IntegerField(), - insnCount = new ShortIntegerField(), + startAddr = new IntegerField("start_addr"), + insnCount = new ShortIntegerField("insn_count"), encodedCatchHandlerReference = new EncodedCatchHandlerReference(encodedCatchHandlerList) }; } public TryItem(int startAddr, int insnCount, EncodedCatchHandler encodedCatchHandler) { + super("try_item"); fields = new Field[] { - this.startAddr = new IntegerField(startAddr), - this.insnCount = new ShortIntegerField(insnCount), + this.startAddr = new IntegerField(startAddr, "start_addr"), + this.insnCount = new ShortIntegerField(insnCount, "insn_count"), this.encodedCatchHandlerReference = new EncodedCatchHandlerReference(encodedCatchHandler) }; } - protected Field[] getFields() { - return fields; - } - public int getStartAddress() { return startAddr.getCachedValue(); } @@ -191,10 +172,12 @@ public class CodeItem extends OffsettedItem { private EncodedCatchHandler encodedCatchHandler; public EncodedCatchHandlerReference(EncodedCatchHandlerList encodedCatchHandlerList) { + super("encoded_catch_handler"); this.encodedCatchHandlerList = encodedCatchHandlerList; } public EncodedCatchHandlerReference(EncodedCatchHandler encodedCatchHandler) { + super("encoded_catch_handler"); this.encodedCatchHandlerList = null; this.encodedCatchHandler = encodedCatchHandler; } @@ -212,7 +195,6 @@ public class CodeItem extends OffsettedItem { } public void copyTo(DexFile dexFile, CachedIntegerValueField _copy) { - EncodedCatchHandlerReference copy = (EncodedCatchHandlerReference)_copy; EncodedCatchHandler copiedItem = copy.getEncodedCatchHandlerList().getByOffset( encodedCatchHandler.getOffsetInList()); @@ -220,7 +202,7 @@ public class CodeItem extends OffsettedItem { } - public void writeTo(Output out) { + public void writeTo(AnnotatedOutput out) { cacheValue(encodedCatchHandler.getOffsetInList()); super.writeTo(out); @@ -252,18 +234,17 @@ public class CodeItem extends OffsettedItem { itemsByOffset.put(offset, encodedCatchHandler); } return encodedCatchHandler; + + } - public EncodedCatchHandlerList(DexFile dexFile) { + public EncodedCatchHandlerList(final DexFile dexFile) { + super("encoded_catch_handler_list"); this.dexFile = dexFile; - } - private final ListSizeField sizeField; - private final FieldListField listField; - - private final Field[] fields = new Field[] { - sizeField = new ListSizeField(catchHandlerList, new Leb128Field()), - listField = new FieldListField(catchHandlerList) { + fields = new Field[] { + sizeField = new ListSizeField(catchHandlerList, new Leb128Field("size")), + listField = new FieldListField(catchHandlerList, "encoded_catch_handler") { protected EncodedCatchHandler make() { return new EncodedCatchHandler(dexFile, 0); } @@ -287,7 +268,11 @@ public class CodeItem extends OffsettedItem { } } } - }; + }; + } + + private final ListSizeField sizeField; + private final FieldListField listField; public void readFrom(Input in) { if (tryItems.size() > 0) { @@ -296,7 +281,7 @@ public class CodeItem extends OffsettedItem { } } - public void writeTo(Output out) { + public void writeTo(AnnotatedOutput out) { if (fieldPresent) { super.writeTo(out); } @@ -314,10 +299,6 @@ public class CodeItem extends OffsettedItem { } } - protected Field[] getFields() { - return fields; - } - public void copyTo(DexFile dexFile, EncodedCatchHandlerList copy) { super.copyTo(dexFile, copy); copy.fieldPresent = fieldPresent; @@ -329,7 +310,6 @@ public class CodeItem extends OffsettedItem { } public static class EncodedCatchHandler extends CompositeField { - public final Field[] fields; private ArrayList list; boolean hasCatchAll = false; private int baseOffset = 0; @@ -341,11 +321,12 @@ public class CodeItem extends OffsettedItem { private int offset; public EncodedCatchHandler(final DexFile dexFile, int offset) { + super("encoded_catch_handler"); this.offset = offset; list = new ArrayList(); fields = new Field[] { - size = new ListSizeField(list, new SignedLeb128Field() { + size = new ListSizeField(list, new SignedLeb128Field("size") { public void readFrom(Input in) { super.readFrom(in); hasCatchAll = (getCachedValue() <= 0); @@ -355,19 +336,19 @@ public class CodeItem extends OffsettedItem { super.cacheValue(value * (hasCatchAll?-1:1)); }}) , - handlers = new FieldListField(list) { + handlers = new FieldListField(list, "encoded_type_addr_pair") { protected EncodedTypeAddrPair make() { return new EncodedTypeAddrPair(dexFile); } }, - catchAllAddress = new Leb128Field() { + catchAllAddress = new Leb128Field("catch_all_addr") { public void readFrom(Input in) { if (hasCatchAll) { super.readFrom(in); } } - public void writeTo(Output out) { + public void writeTo(AnnotatedOutput out) { if (hasCatchAll) { super.writeTo(out); } @@ -393,10 +374,6 @@ public class CodeItem extends OffsettedItem { } } - protected Field[] getFields() { - return fields; - } - public int getOffsetInList() { return offset-baseOffset; } @@ -434,35 +411,30 @@ public class CodeItem extends OffsettedItem { } public static class EncodedTypeAddrPair extends CompositeField { - public final Field[] fields; - - public final IndexedItemReference type; - public final Leb128Field handlerAddress; + public final IndexedItemReference typeReferenceField; + public final Leb128Field handlerAddressField; public EncodedTypeAddrPair(DexFile dexFile) { + super("encoded_type_addr_pair"); fields = new Field[] { - type = new IndexedItemReference(dexFile.TypeIdsSection, new Leb128Field()), - handlerAddress = new Leb128Field() + typeReferenceField = new IndexedItemReference(dexFile.TypeIdsSection, + new Leb128Field(null), "type_idx"), + handlerAddressField = new Leb128Field("addr") }; } public EncodedTypeAddrPair(DexFile dexFile, TypeIdItem type, int handlerOffset) { - fields = new Field[] { - this.type = new IndexedItemReference(dexFile, type, new Leb128Field()), - this.handlerAddress = new Leb128Field(handlerOffset) - }; + this(dexFile); + typeReferenceField.setReference(type); + handlerAddressField.cacheValue(handlerOffset); } - protected Field[] getFields() { - return fields; - } - - public TypeIdItem getType() { - return type.getReference(); + public TypeIdItem getTypeReferenceField() { + return typeReferenceField.getReference(); } public int getHandlerAddress() { - return handlerAddress.getCachedValue(); + return handlerAddressField.getCachedValue(); } } @@ -473,18 +445,18 @@ public class CodeItem extends OffsettedItem { this.dexFile = dexFile; } - public void writeTo(Output out) { + public void writeTo(AnnotatedOutput out) { int startPosition = out.getCursor(); for (Instruction instruction: instructionList) { instruction.writeTo(out); } - if ((out.getCursor() - startPosition) != (instructionsSize.getCachedValue() * 2)) { + if ((out.getCursor() - startPosition) != (instructionsSizeField.getCachedValue() * 2)) { throw new RuntimeException("Did not write the expected amount of bytes"); } } public void readFrom(Input in) { - int numBytes = instructionsSize.getCachedValue() * 2; + int numBytes = instructionsSizeField.getCachedValue() * 2; int startPosition = in.getCursor(); do { @@ -499,7 +471,7 @@ public class CodeItem extends OffsettedItem { } public int place(int offset) { - return offset + (instructionsSize.getCachedValue() * 2); + return offset + (instructionsSizeField.getCachedValue() * 2); } public void copyTo(DexFile dexFile, InstructionListField copy) { @@ -539,7 +511,7 @@ public class CodeItem extends OffsettedItem { if (opcode == Opcode.INVOKE_STATIC || opcode == Opcode.INVOKE_STATIC_RANGE) { isStatic = true; } - int paramWordCount = methodIdItem.getParameterWordCount(isStatic); + int paramWordCount = methodIdItem.getParameterRegisterCount(isStatic); if (maxParamWordCount < paramWordCount) { maxParamWordCount = paramWordCount; @@ -556,10 +528,10 @@ public class CodeItem extends OffsettedItem { } private boolean needsAlign() { - return (triesCount.getCachedValue() > 0) && (instructionsSize.getCachedValue() % 2 == 1); + return (triesCountField.getCachedValue() > 0) && (instructionsSizeField.getCachedValue() % 2 == 1); } - public void writeTo(Output out) { + public void writeTo(AnnotatedOutput out) { if (needsAlign()) { out.writeShort(0); } diff --git a/src/main/java/org/JesusFreke/dexlib/CompositeField.java b/src/main/java/org/JesusFreke/dexlib/CompositeField.java index e933ec2a..7e77972a 100644 --- a/src/main/java/org/JesusFreke/dexlib/CompositeField.java +++ b/src/main/java/org/JesusFreke/dexlib/CompositeField.java @@ -28,53 +28,58 @@ package org.JesusFreke.dexlib; -import org.JesusFreke.dexlib.util.Output; import org.JesusFreke.dexlib.util.Input; +import org.JesusFreke.dexlib.util.AnnotatedOutput; public abstract class CompositeField> implements Field { - /** - * Every instance of a specific subclass should return an array with the same structure, - * in other words have the same size, and the same type of field at each position. - * @return An array of fields that represents the sub-fields that make up this CompositeField - */ - protected abstract Field[] getFields(); + protected Field[] fields; + private final String fieldName; - public void writeTo(Output out) { - for (Field field: getFields()) { + /** + * Every instance of a specific subclass have a field array with the same structure. + * In other words have the same size, and the same type of field at each position. + */ + public CompositeField(String fieldName){ + this.fieldName = fieldName; + } + + public void writeTo(AnnotatedOutput out) { + out.annotate(0, fieldName + ":"); + out.indent(); + for (Field field: fields) { field.writeTo(out); } + out.deindent(); } public void readFrom(Input in) { - for (Field field: getFields()) { + for (Field field: fields) { field.readFrom(in); } } public int place(int offset) { - for (Field field: getFields()) { + for (Field field: fields) { offset = field.place(offset); } return offset; } public void copyTo(DexFile dexFile, T copy) { - Field[] fields = getFields(); - Field[] copyFields = copy.getFields(); for (int i = 0; i < fields.length; i++) { /** * This assumes that the fields will be the same for every instance * of a specific concrete subclass. By making this assumption, every subclass is * prevented from having to implement copyTo */ - fields[i].copyTo(dexFile, copyFields[i]); + fields[i].copyTo(dexFile, copy.fields[i]); } } public int hashCode() { int h = 1; - for (Field field: getFields()) { + for (Field field: fields) { h = h * 31 + field.hashCode(); } return h; @@ -86,13 +91,11 @@ public abstract class CompositeField> implements Fie } CompositeField other = (CompositeField)o; - Field[] fields = getFields(); - Field[] otherFields = other.getFields(); - if (fields.length != otherFields.length) { + if (fields.length != other.fields.length) { return false; } for (int i = 0; i < fields.length; i++) { - if (!fields[i].equals(otherFields[i])) { + if (!fields[i].equals(other.fields[i])) { return false; } } diff --git a/src/main/java/org/JesusFreke/dexlib/DebugInfoItem.java b/src/main/java/org/JesusFreke/dexlib/DebugInfoItem.java index 1c32d200..4aa51c49 100644 --- a/src/main/java/org/JesusFreke/dexlib/DebugInfoItem.java +++ b/src/main/java/org/JesusFreke/dexlib/DebugInfoItem.java @@ -32,15 +32,13 @@ import org.JesusFreke.dexlib.ItemType; import org.JesusFreke.dexlib.debug.DebugInstructionFactory; import org.JesusFreke.dexlib.debug.EndSequence; import org.JesusFreke.dexlib.debug.DebugInstruction; -import org.JesusFreke.dexlib.util.Output; import org.JesusFreke.dexlib.util.Input; +import org.JesusFreke.dexlib.util.AnnotatedOutput; import java.util.ArrayList; import java.util.List; public class DebugInfoItem extends OffsettedItem { - private final Field[] fields; - private final ArrayList> parameterNames = new ArrayList>(); @@ -55,11 +53,13 @@ public class DebugInfoItem extends OffsettedItem { super(offset); fields = new Field[] { - lineStartField = new Leb128Field(), - parameterNamesSizeField = new ListSizeField(parameterNames, new Leb128Field()), - parameterNamesField = new FieldListField>(parameterNames) { + lineStartField = new Leb128Field("line_start"), + parameterNamesSizeField = new ListSizeField(parameterNames, new Leb128Field("parameters_size")), + parameterNamesField = new FieldListField>( + parameterNames, "parameter_names") { protected IndexedItemReference make() { - return new IndexedItemReference(dexFile.StringIdsSection, new Leb128p1Field()); + return new IndexedItemReference(dexFile.StringIdsSection, + new Leb128p1Field(null), "parameter_name"); } }, debugInstructionListField = new DebugInstructionList(dexFile) @@ -75,25 +75,22 @@ public class DebugInfoItem extends OffsettedItem { this.lineStartField.cacheValue(lineStart); for (StringIdItem parameterName: parameterNames) { - this.parameterNames.add(new IndexedItemReference(dexFile, parameterName, - new Leb128p1Field())); + IndexedItemReference parameterReference = parameterNamesField.make(); + parameterReference.setReference(parameterName); + this.parameterNames.add(parameterReference); } this.instructionFields.addAll(debugInstructions); } - protected int getAlignment() { - return 1; - } - - protected Field[] getFields() { - return fields; - } - public ItemType getItemType() { return ItemType.TYPE_DEBUG_INFO_ITEM; } + public String getConciseIdentity() { + return "debug_info_item @0x" + Integer.toHexString(getOffset()); + } + private class DebugInstructionList implements Field { private final DexFile dexFile; private final ArrayList list; @@ -103,7 +100,7 @@ public class DebugInfoItem extends OffsettedItem { list = instructionFields; } - public void writeTo(Output out) { + public void writeTo(AnnotatedOutput out) { for (DebugInstruction debugInstruction: list) { debugInstruction.writeTo(out); } diff --git a/src/main/java/org/JesusFreke/dexlib/DexFile.java b/src/main/java/org/JesusFreke/dexlib/DexFile.java index 7a77d4ad..a9629096 100644 --- a/src/main/java/org/JesusFreke/dexlib/DexFile.java +++ b/src/main/java/org/JesusFreke/dexlib/DexFile.java @@ -28,10 +28,7 @@ package org.JesusFreke.dexlib; -import org.JesusFreke.dexlib.util.Input; -import org.JesusFreke.dexlib.util.ByteArrayInput; -import org.JesusFreke.dexlib.util.Output; -import org.JesusFreke.dexlib.util.FileUtils; +import org.JesusFreke.dexlib.util.*; import java.util.HashMap; import java.util.ArrayList; @@ -176,7 +173,7 @@ public class DexFile fileSize = offset; } - public void writeTo(Output out) { + public void writeTo(AnnotatedOutput out) { HeaderItemSection.writeTo(out); for (IndexedSection indexedSection: indexedSections) { indexedSection.writeTo(out); @@ -189,29 +186,6 @@ public class DexFile MapSection.writeTo(out); } - public ClassDefItem getClassByName(String className) { - for (ClassDefItem classDefItem: ClassDefsSection.items) { - if (classDefItem.getClassName().equals(className)) { - return classDefItem; - } - } - throw new RuntimeException("class not found"); - } - - public MethodIdItem[] getMethodsByClass(TypeIdItem classType) { - ArrayList methods = new ArrayList(); - - for (MethodIdItem methodIdItem: MethodIdsSection.items) { - if (methodIdItem.getClassType() == classType) { - methods.add(methodIdItem); - } - } - - return methods.toArray(new MethodIdItem[0]); - } - - - public final IndexedSection HeaderItemSection = new IndexedSection() { protected HeaderItem make(int index) { try { diff --git a/src/main/java/org/JesusFreke/dexlib/EncodedArrayItem.java b/src/main/java/org/JesusFreke/dexlib/EncodedArrayItem.java index 032f2b9b..e6b01139 100644 --- a/src/main/java/org/JesusFreke/dexlib/EncodedArrayItem.java +++ b/src/main/java/org/JesusFreke/dexlib/EncodedArrayItem.java @@ -32,13 +32,11 @@ import org.JesusFreke.dexlib.ItemType; import org.JesusFreke.dexlib.EncodedValue.ArrayEncodedValueSubField; import org.JesusFreke.dexlib.EncodedValue.EncodedValue; import org.JesusFreke.dexlib.util.Input; -import org.JesusFreke.dexlib.util.Output; +import org.JesusFreke.dexlib.util.AnnotatedOutput; import java.util.ArrayList; public class EncodedArrayItem extends OffsettedItem { - private final Field[] fields; - private final ArrayEncodedValueSubField encodedArray; public EncodedArrayItem(DexFile dexFile, int offset) { @@ -50,7 +48,7 @@ public class EncodedArrayItem extends OffsettedItem { } public EncodedArrayItem(DexFile dexFile, ArrayList encodedValues) { - super(-1); + super(0); fields = new Field[] { encodedArray = new ArrayEncodedValueSubField(dexFile, encodedValues) @@ -65,7 +63,7 @@ public class EncodedArrayItem extends OffsettedItem { return super.place(index, offset); } - public void writeTo(Output out) { + public void writeTo(AnnotatedOutput out) { super.writeTo(out); } @@ -73,10 +71,6 @@ public class EncodedArrayItem extends OffsettedItem { return 1; } - protected Field[] getFields() { - return fields; - } - public int getOffset() { return super.getOffset(); } @@ -88,4 +82,8 @@ public class EncodedArrayItem extends OffsettedItem { public ItemType getItemType() { return ItemType.TYPE_ENCODED_ARRAY_ITEM; } + + public String getConciseIdentity() { + return "encoded_array @0x" + Integer.toHexString(getOffset()); + } } diff --git a/src/main/java/org/JesusFreke/dexlib/EncodedValue/AnnotationElement.java b/src/main/java/org/JesusFreke/dexlib/EncodedValue/AnnotationElement.java index ecbd1467..c3eb1c1a 100644 --- a/src/main/java/org/JesusFreke/dexlib/EncodedValue/AnnotationElement.java +++ b/src/main/java/org/JesusFreke/dexlib/EncodedValue/AnnotationElement.java @@ -31,26 +31,24 @@ package org.JesusFreke.dexlib.EncodedValue; import org.JesusFreke.dexlib.*; public class AnnotationElement extends CompositeField { - private Field[] fields; - private final IndexedItemReference elementName; private final EncodedValue encodedValue; public AnnotationElement(final DexFile dexFile) { + super("annotation_element"); fields = new Field[] { - elementName = new IndexedItemReference(dexFile.StringIdsSection, new Leb128Field()), + elementName = new IndexedItemReference(dexFile.StringIdsSection, + new Leb128Field(null), "element_name"), encodedValue = new EncodedValue(dexFile) }; } public AnnotationElement(final DexFile dexFile, StringIdItem elementName, EncodedValue encodedValue) { + super("annotation_element"); fields = new Field[] { - this.elementName = new IndexedItemReference(dexFile, elementName, new Leb128Field()), + this.elementName = new IndexedItemReference(dexFile, elementName, + new Leb128Field(null), "element_name"), this.encodedValue = encodedValue }; } - - protected Field[] getFields() { - return fields; - } } diff --git a/src/main/java/org/JesusFreke/dexlib/EncodedValue/AnnotationEncodedValueSubField.java b/src/main/java/org/JesusFreke/dexlib/EncodedValue/AnnotationEncodedValueSubField.java index 93da288d..390ee0be 100644 --- a/src/main/java/org/JesusFreke/dexlib/EncodedValue/AnnotationEncodedValueSubField.java +++ b/src/main/java/org/JesusFreke/dexlib/EncodedValue/AnnotationEncodedValueSubField.java @@ -36,7 +36,6 @@ import java.util.List; public class AnnotationEncodedValueSubField extends CompositeField implements EncodedValueSubField { - private final Field[] fields; private final ArrayList annotationElementList = new ArrayList(); private final IndexedItemReference annotationType; @@ -44,10 +43,12 @@ public class AnnotationEncodedValueSubField extends CompositeField annotationElements; public AnnotationEncodedValueSubField(final DexFile dexFile) { + super("encoded_annotation"); fields = new Field[] { - annotationType = new IndexedItemReference(dexFile.TypeIdsSection, new Leb128Field()), - annotationCount = new ListSizeField(annotationElementList, new Leb128Field()), - annotationElements = new FieldListField(annotationElementList) { + annotationType = new IndexedItemReference(dexFile.TypeIdsSection, + new Leb128Field(null), "type_idx"), + annotationCount = new ListSizeField(annotationElementList, new Leb128Field("size")), + annotationElements = new FieldListField(annotationElementList, "elements") { protected AnnotationElement make() { return new AnnotationElement(dexFile); } @@ -62,10 +63,6 @@ public class AnnotationEncodedValueSubField extends CompositeField { - private final Field[] fields; private final ArrayList encodedValues; public ArrayEncodedValueSubField(final DexFile dexFile) { + super("encoded_array"); + encodedValues = new ArrayList(); fields = new Field[] { - new ListSizeField(encodedValues, new Leb128Field()), - new FieldListField(encodedValues) { + new ListSizeField(encodedValues, new Leb128Field("size")), + new FieldListField(encodedValues, "values") { protected EncodedValue make() { return new EncodedValue(dexFile); } @@ -52,11 +53,13 @@ public class ArrayEncodedValueSubField extends CompositeField encodedValues) { + super("encoded_array"); + this.encodedValues = encodedValues; fields = new Field[] { - new ListSizeField(this.encodedValues, new Leb128Field()), - new FieldListField(encodedValues) { + new ListSizeField(this.encodedValues, new Leb128Field("size")), + new FieldListField(encodedValues, "values") { protected EncodedValue make() { return new EncodedValue(dexFile); } @@ -64,10 +67,6 @@ public class ArrayEncodedValueSubField extends CompositeField @@ -41,7 +41,7 @@ public class BoolEncodedValueSubField this.value = value; } - public void writeTo(Output out) { + public void writeTo(AnnotatedOutput out) { } public void readFrom(Input in) { diff --git a/src/main/java/org/JesusFreke/dexlib/EncodedValue/ByteEncodedValueSubField.java b/src/main/java/org/JesusFreke/dexlib/EncodedValue/ByteEncodedValueSubField.java index a59e9441..50429790 100644 --- a/src/main/java/org/JesusFreke/dexlib/EncodedValue/ByteEncodedValueSubField.java +++ b/src/main/java/org/JesusFreke/dexlib/EncodedValue/ByteEncodedValueSubField.java @@ -28,8 +28,8 @@ package org.JesusFreke.dexlib.EncodedValue; -import org.JesusFreke.dexlib.util.Output; import org.JesusFreke.dexlib.util.Input; +import org.JesusFreke.dexlib.util.AnnotatedOutput; public class ByteEncodedValueSubField extends SimpleEncodedValueSubField @@ -41,7 +41,8 @@ public class ByteEncodedValueSubField this.value = value; } - public void writeTo(Output out) { + public void writeTo(AnnotatedOutput out) { + out.annotate(1, "byte" + Integer.toHexString(value) + " " + Integer.toString(value)); out.writeByte(value); } diff --git a/src/main/java/org/JesusFreke/dexlib/EncodedValue/CharEncodedValueSubField.java b/src/main/java/org/JesusFreke/dexlib/EncodedValue/CharEncodedValueSubField.java index ae70b879..62f03e74 100644 --- a/src/main/java/org/JesusFreke/dexlib/EncodedValue/CharEncodedValueSubField.java +++ b/src/main/java/org/JesusFreke/dexlib/EncodedValue/CharEncodedValueSubField.java @@ -28,9 +28,9 @@ package org.JesusFreke.dexlib.EncodedValue; -import org.JesusFreke.dexlib.util.Output; import org.JesusFreke.dexlib.util.EncodedValueUtils; import org.JesusFreke.dexlib.util.Input; +import org.JesusFreke.dexlib.util.AnnotatedOutput; public class CharEncodedValueSubField extends SimpleEncodedValueSubField @@ -42,8 +42,10 @@ public class CharEncodedValueSubField this.value = value; } - public void writeTo(Output out) { - out.write(EncodedValueUtils.encodeUnsignedIntegralValue(value)); + public void writeTo(AnnotatedOutput out) { + byte[] bytes = EncodedValueUtils.encodeUnsignedIntegralValue(value); + out.annotate(bytes.length, "CharEncodedValueSubField"); + out.write(bytes); } public void readFrom(Input in) { diff --git a/src/main/java/org/JesusFreke/dexlib/EncodedValue/DoubleEncodedValueSubField.java b/src/main/java/org/JesusFreke/dexlib/EncodedValue/DoubleEncodedValueSubField.java index 49e26db3..f6a2c34f 100644 --- a/src/main/java/org/JesusFreke/dexlib/EncodedValue/DoubleEncodedValueSubField.java +++ b/src/main/java/org/JesusFreke/dexlib/EncodedValue/DoubleEncodedValueSubField.java @@ -28,9 +28,9 @@ package org.JesusFreke.dexlib.EncodedValue; -import org.JesusFreke.dexlib.util.Output; import org.JesusFreke.dexlib.util.EncodedValueUtils; import org.JesusFreke.dexlib.util.Input; +import org.JesusFreke.dexlib.util.AnnotatedOutput; public class DoubleEncodedValueSubField extends SimpleEncodedValueSubField @@ -42,9 +42,10 @@ public class DoubleEncodedValueSubField this.value = value; } - public void writeTo(Output out) { - out.write(EncodedValueUtils.encodeRightZeroExtendedValue( - Double.doubleToLongBits(value))); + public void writeTo(AnnotatedOutput out) { + byte[] bytes = EncodedValueUtils.encodeRightZeroExtendedValue(Double.doubleToLongBits(value)); + out.annotate(bytes.length, "DoubleEncodedValueSubField"); + out.write(bytes); } public void readFrom(Input in) { diff --git a/src/main/java/org/JesusFreke/dexlib/EncodedValue/EncodedIndexedItemReference.java b/src/main/java/org/JesusFreke/dexlib/EncodedValue/EncodedIndexedItemReference.java index cec9b984..460a09b7 100644 --- a/src/main/java/org/JesusFreke/dexlib/EncodedValue/EncodedIndexedItemReference.java +++ b/src/main/java/org/JesusFreke/dexlib/EncodedValue/EncodedIndexedItemReference.java @@ -29,25 +29,25 @@ package org.JesusFreke.dexlib.EncodedValue; import org.JesusFreke.dexlib.*; -import org.JesusFreke.dexlib.util.Output; import org.JesusFreke.dexlib.util.Input; import org.JesusFreke.dexlib.util.EncodedValueUtils; +import org.JesusFreke.dexlib.util.AnnotatedOutput; -/** TODO: it should be possible to somehow use the IndexedItemReference class */ public class EncodedIndexedItemReference> - extends ItemReference> implements EncodedValueSubField> { private int initialValueArg; private ValueType valueType; + private T item = null; + private IndexedSection section; + public EncodedIndexedItemReference(IndexedSection section, ValueType valueType) { - super(section); this.valueType = valueType; + this.section = section; } //TODO: implement support for enum values public EncodedIndexedItemReference(DexFile dexFile, T item) { - super(dexFile, item); if (item.getClass() == StringIdItem.class) { valueType = ValueType.VALUE_STRING; } else if (item.getClass() == TypeIdItem.class) { @@ -57,31 +57,48 @@ public class EncodedIndexedItemReference> } else if (item.getClass() == MethodIdItem.class) { valueType = ValueType.VALUE_METHOD; } + this.item = item; } - public void writeTo(Output out) { - T item = getReference(); + public void writeTo(AnnotatedOutput out) { + if (!item.isPlaced()) { throw new RuntimeException("Trying to write a reference to an item that hasn't been placed."); } - out.write(EncodedValueUtils.encodeUnsignedIntegralValue(item.getIndex())); + + byte[] bytes = EncodedValueUtils.encodeUnsignedIntegralValue(item.getIndex()); + + if (item != null) { + out.annotate(bytes.length, item.getItemType().getTypeName() + " reference"); + } else { + out.annotate(bytes.length, "null reference"); + } + + out.write(bytes); } public void readFrom(Input in) { - setReference(((IndexedSection)getSection()).getByIndex( - (int)EncodedValueUtils.decodeUnsignedIntegralValue(in.readBytes(initialValueArg + 1)))); + item = section.getByIndex( + (int)EncodedValueUtils.decodeUnsignedIntegralValue(in.readBytes(initialValueArg + 1))); } public int place(int offset) { - T item = getReference(); if (!item.isPlaced()) { throw new RuntimeException("Trying to place a reference to an item that hasn't been placed."); } return offset + EncodedValueUtils.getRequiredBytesForUnsignedIntegralValue(item.getIndex()); } + public void copyTo(DexFile dexFile, EncodedIndexedItemReference copy) { + if (item == null) { + return; + } + T copiedItem = copy.section.intern(dexFile, item); + copy.item = copiedItem; + } + public T getValue() { - return getReference(); + return item; } public void setInitialValueArg(byte valueArg) @@ -90,7 +107,7 @@ public class EncodedIndexedItemReference> } public byte getValueArg() { - return EncodedValueUtils.getRequiredBytesForUnsignedIntegralValue(getReference().getIndex()); + return EncodedValueUtils.getRequiredBytesForUnsignedIntegralValue(item.getIndex()); } public ValueType getValueType() { diff --git a/src/main/java/org/JesusFreke/dexlib/EncodedValue/EncodedValue.java b/src/main/java/org/JesusFreke/dexlib/EncodedValue/EncodedValue.java index 967de9f0..a78f0dbf 100644 --- a/src/main/java/org/JesusFreke/dexlib/EncodedValue/EncodedValue.java +++ b/src/main/java/org/JesusFreke/dexlib/EncodedValue/EncodedValue.java @@ -28,13 +28,11 @@ package org.JesusFreke.dexlib.EncodedValue; -import org.JesusFreke.dexlib.util.Output; import org.JesusFreke.dexlib.util.Input; +import org.JesusFreke.dexlib.util.AnnotatedOutput; import org.JesusFreke.dexlib.*; public class EncodedValue extends CompositeField { - private final Field[] fields; - private class ValueTypeArgField implements Field { private ValueType valueType; private byte valueArg; @@ -46,8 +44,8 @@ public class EncodedValue extends CompositeField { this.valueType = valueType; } - public void writeTo(Output out) { - + public void writeTo(AnnotatedOutput out) { + out.annotate(1, "valuetype=" + Integer.toString(valueType.getMapValue()) + " valueArg=" + Integer.toString(valueArg)); byte value = (byte)(valueType.getMapValue() | (valueArg << 5)); out.writeByte(value); } @@ -102,7 +100,7 @@ public class EncodedValue extends CompositeField { this.subField = subField; } - public void writeTo(Output out) { + public void writeTo(AnnotatedOutput out) { subField.writeTo(out); } @@ -147,6 +145,7 @@ public class EncodedValue extends CompositeField { private final EncodedValueSubFieldWrapper encodedValue; public EncodedValue(final DexFile dexFile) { + super("encoded_value"); fields = new Field[] { valueTypeArg = new ValueTypeArgField(), encodedValue = new EncodedValueSubFieldWrapper(dexFile) @@ -154,6 +153,7 @@ public class EncodedValue extends CompositeField { } public EncodedValue(final DexFile dexFile, EncodedValueSubField subField) { + super("encoded_value"); fields = new Field[] { valueTypeArg = new ValueTypeArgField(subField.getValueType()), encodedValue = new EncodedValueSubFieldWrapper(dexFile, subField) @@ -182,8 +182,4 @@ public class EncodedValue extends CompositeField { public byte getValueArg() { return valueTypeArg.getValueArg(); } - - protected Field[] getFields() { - return fields; - } } diff --git a/src/main/java/org/JesusFreke/dexlib/EncodedValue/FloatEncodedValueSubField.java b/src/main/java/org/JesusFreke/dexlib/EncodedValue/FloatEncodedValueSubField.java index f58d2624..b494da19 100644 --- a/src/main/java/org/JesusFreke/dexlib/EncodedValue/FloatEncodedValueSubField.java +++ b/src/main/java/org/JesusFreke/dexlib/EncodedValue/FloatEncodedValueSubField.java @@ -28,9 +28,9 @@ package org.JesusFreke.dexlib.EncodedValue; -import org.JesusFreke.dexlib.util.Output; import org.JesusFreke.dexlib.util.EncodedValueUtils; import org.JesusFreke.dexlib.util.Input; +import org.JesusFreke.dexlib.util.AnnotatedOutput; public class FloatEncodedValueSubField extends SimpleEncodedValueSubField @@ -41,9 +41,10 @@ public class FloatEncodedValueSubField public FloatEncodedValueSubField(float value) { this.value = value; } - public void writeTo(Output out) { - out.write(EncodedValueUtils.encodeRightZeroExtendedValue( - ((long)Float.floatToIntBits(value)) << 32)); + public void writeTo(AnnotatedOutput out) { + byte[] bytes = EncodedValueUtils.encodeRightZeroExtendedValue(((long)Float.floatToIntBits(value)) << 32); + out.annotate(bytes.length, "FloatEncodedValueSubField"); + out.write(bytes); } public void readFrom(Input in) { diff --git a/src/main/java/org/JesusFreke/dexlib/EncodedValue/IntEncodedValueSubField.java b/src/main/java/org/JesusFreke/dexlib/EncodedValue/IntEncodedValueSubField.java index 418fa492..f49267e8 100644 --- a/src/main/java/org/JesusFreke/dexlib/EncodedValue/IntEncodedValueSubField.java +++ b/src/main/java/org/JesusFreke/dexlib/EncodedValue/IntEncodedValueSubField.java @@ -28,9 +28,9 @@ package org.JesusFreke.dexlib.EncodedValue; -import org.JesusFreke.dexlib.util.Output; import org.JesusFreke.dexlib.util.EncodedValueUtils; import org.JesusFreke.dexlib.util.Input; +import org.JesusFreke.dexlib.util.AnnotatedOutput; public class IntEncodedValueSubField extends SimpleEncodedValueSubField @@ -42,8 +42,10 @@ public class IntEncodedValueSubField this.value = value; } - public void writeTo(Output out) { - out.write(EncodedValueUtils.encodeSignedIntegralValue(value)); + public void writeTo(AnnotatedOutput out) { + byte[] bytes = EncodedValueUtils.encodeSignedIntegralValue(value); + out.annotate(bytes.length, "IntEncodedValueSubField"); + out.write(bytes); } public void readFrom(Input in) { diff --git a/src/main/java/org/JesusFreke/dexlib/EncodedValue/LongEncodedValueSubField.java b/src/main/java/org/JesusFreke/dexlib/EncodedValue/LongEncodedValueSubField.java index 9f4f95ac..24f08f61 100644 --- a/src/main/java/org/JesusFreke/dexlib/EncodedValue/LongEncodedValueSubField.java +++ b/src/main/java/org/JesusFreke/dexlib/EncodedValue/LongEncodedValueSubField.java @@ -28,9 +28,9 @@ package org.JesusFreke.dexlib.EncodedValue; -import org.JesusFreke.dexlib.util.Output; import org.JesusFreke.dexlib.util.EncodedValueUtils; import org.JesusFreke.dexlib.util.Input; +import org.JesusFreke.dexlib.util.AnnotatedOutput; public class LongEncodedValueSubField extends SimpleEncodedValueSubField @@ -42,8 +42,10 @@ public class LongEncodedValueSubField this.value = value; } - public void writeTo(Output out) { - out.write(EncodedValueUtils.encodeSignedIntegralValue(value)); + public void writeTo(AnnotatedOutput out) { + byte[] bytes = EncodedValueUtils.encodeSignedIntegralValue(value); + out.annotate(bytes.length, "LongEncodedValueSubField"); + out.write(bytes); } public void readFrom(Input in) { diff --git a/src/main/java/org/JesusFreke/dexlib/EncodedValue/NullEncodedValueSubField.java b/src/main/java/org/JesusFreke/dexlib/EncodedValue/NullEncodedValueSubField.java index 0b899835..93f2d6fe 100644 --- a/src/main/java/org/JesusFreke/dexlib/EncodedValue/NullEncodedValueSubField.java +++ b/src/main/java/org/JesusFreke/dexlib/EncodedValue/NullEncodedValueSubField.java @@ -28,8 +28,8 @@ package org.JesusFreke.dexlib.EncodedValue; -import org.JesusFreke.dexlib.util.Output; import org.JesusFreke.dexlib.util.Input; +import org.JesusFreke.dexlib.util.AnnotatedOutput; public class NullEncodedValueSubField extends SimpleEncodedValueSubField @@ -37,7 +37,7 @@ public class NullEncodedValueSubField public NullEncodedValueSubField() { } - public void writeTo(Output out) { + public void writeTo(AnnotatedOutput out) { } public void readFrom(Input in) { diff --git a/src/main/java/org/JesusFreke/dexlib/EncodedValue/ShortEncodedValueSubField.java b/src/main/java/org/JesusFreke/dexlib/EncodedValue/ShortEncodedValueSubField.java index 2f098ac8..5654c5dc 100644 --- a/src/main/java/org/JesusFreke/dexlib/EncodedValue/ShortEncodedValueSubField.java +++ b/src/main/java/org/JesusFreke/dexlib/EncodedValue/ShortEncodedValueSubField.java @@ -28,9 +28,9 @@ package org.JesusFreke.dexlib.EncodedValue; -import org.JesusFreke.dexlib.util.Output; import org.JesusFreke.dexlib.util.EncodedValueUtils; import org.JesusFreke.dexlib.util.Input; +import org.JesusFreke.dexlib.util.AnnotatedOutput; public class ShortEncodedValueSubField extends SimpleEncodedValueSubField @@ -42,8 +42,10 @@ public class ShortEncodedValueSubField this.value = value; } - public void writeTo(Output out) { - out.write(EncodedValueUtils.encodeSignedIntegralValue(value)); + public void writeTo(AnnotatedOutput out) { + byte[] bytes = EncodedValueUtils.encodeSignedIntegralValue(value); + out.annotate(bytes.length, "ShortEncodedValueSubField"); + out.write(bytes); } public void readFrom(Input in) { diff --git a/src/main/java/org/JesusFreke/dexlib/Field.java b/src/main/java/org/JesusFreke/dexlib/Field.java index b53c31a5..4c5b598e 100644 --- a/src/main/java/org/JesusFreke/dexlib/Field.java +++ b/src/main/java/org/JesusFreke/dexlib/Field.java @@ -30,9 +30,10 @@ package org.JesusFreke.dexlib; import org.JesusFreke.dexlib.util.Output; import org.JesusFreke.dexlib.util.Input; +import org.JesusFreke.dexlib.util.AnnotatedOutput; public interface Field { - public void writeTo(Output out); + public void writeTo(AnnotatedOutput out); public void readFrom(Input in); public int place(int offset); public void copyTo(DexFile dexFile, T copy); diff --git a/src/main/java/org/JesusFreke/dexlib/FieldIdItem.java b/src/main/java/org/JesusFreke/dexlib/FieldIdItem.java index f628c95a..c3b2f769 100644 --- a/src/main/java/org/JesusFreke/dexlib/FieldIdItem.java +++ b/src/main/java/org/JesusFreke/dexlib/FieldIdItem.java @@ -28,31 +28,28 @@ package org.JesusFreke.dexlib; -import org.JesusFreke.dexlib.ItemType; - public class FieldIdItem extends IndexedItem { - private final Field[] fields; - - private final IndexedItemReference classType; - private final IndexedItemReference fieldType; - private final IndexedItemReference fieldName; + private final IndexedItemReference classTypeReferenceField; + private final IndexedItemReference fieldTypeReferenceField; + private final IndexedItemReference fieldNameReferenceField; public FieldIdItem(DexFile dexFile, int index) { super(index); fields = new Field[] { - classType = new IndexedItemReference(dexFile.TypeIdsSection, new ShortIntegerField()), - fieldType = new IndexedItemReference(dexFile.TypeIdsSection, new ShortIntegerField()), - fieldName = new IndexedItemReference(dexFile.StringIdsSection, new IntegerField()) + classTypeReferenceField = new IndexedItemReference(dexFile.TypeIdsSection, + new ShortIntegerField(null), "class_idx"), + fieldTypeReferenceField = new IndexedItemReference(dexFile.TypeIdsSection, + new ShortIntegerField(null), "type_idx"), + fieldNameReferenceField = new IndexedItemReference(dexFile.StringIdsSection, + new IntegerField(null), "parameters_off") }; } public FieldIdItem(DexFile dexFile, TypeIdItem classType, StringIdItem fieldName, TypeIdItem fieldType) { - super(-1); - fields = new Field[] { - this.classType = new IndexedItemReference(dexFile, classType, new ShortIntegerField()), - this.fieldType = new IndexedItemReference(dexFile, fieldType, new ShortIntegerField()), - this.fieldName = new IndexedItemReference(dexFile, fieldName, new IntegerField()) - }; + this(dexFile, -1); + classTypeReferenceField.setReference(classType); + fieldTypeReferenceField.setReference(fieldType); + fieldNameReferenceField.setReference(fieldName); } public FieldIdItem(DexFile dexFile, TypeIdItem classType, String fieldName, TypeIdItem fieldType) { @@ -63,34 +60,35 @@ public class FieldIdItem extends IndexedItem { return 4; } - protected Field[] getFields() { - return fields; - } - public ItemType getItemType() { return ItemType.TYPE_FIELD_ID_ITEM; } - public String toString() { - return classType.toString() + " - " + fieldName.toString(); + public String getConciseIdentity() { + String parentClass = classTypeReferenceField.getReference().getTypeDescriptor(); + //strip off the leading L and trailing ; + parentClass = parentClass.substring(1, parentClass.length() - 1); + + return parentClass + "/" + fieldNameReferenceField.getReference().getStringValue() + + ":" + fieldTypeReferenceField.getReference().getTypeDescriptor(); } public int compareTo(FieldIdItem o) { - int result = classType.compareTo(o.classType); + int result = classTypeReferenceField.compareTo(o.classTypeReferenceField); if (result != 0) { return result; } - result = fieldName.compareTo(o.fieldName); + result = fieldNameReferenceField.compareTo(o.fieldNameReferenceField); if (result != 0) { return result; } - return fieldType.compareTo(o.fieldType); + return fieldTypeReferenceField.compareTo(o.fieldTypeReferenceField); } public TypeIdItem getFieldType() { - return fieldType.getReference(); + return fieldTypeReferenceField.getReference(); } } diff --git a/src/main/java/org/JesusFreke/dexlib/FieldListField.java b/src/main/java/org/JesusFreke/dexlib/FieldListField.java index e6cf9489..c3987158 100644 --- a/src/main/java/org/JesusFreke/dexlib/FieldListField.java +++ b/src/main/java/org/JesusFreke/dexlib/FieldListField.java @@ -28,21 +28,30 @@ package org.JesusFreke.dexlib; -import org.JesusFreke.dexlib.util.Output; import org.JesusFreke.dexlib.util.Input; +import org.JesusFreke.dexlib.util.AnnotatedOutput; import java.util.ArrayList; public abstract class FieldListField implements Field> { final ArrayList list; + private final String fieldName; - public FieldListField(ArrayList list) { + public FieldListField(ArrayList list, String fieldName) { this.list = list; + this.fieldName = fieldName; } - public void writeTo(Output out) { - for (Field field: list) { - field.writeTo(out); + public void writeTo(AnnotatedOutput out) { + if (list.size() > 0) { + int i=0; + for (Field field: list) { + out.annotate(0, fieldName + "[" + Integer.toString(i) + "]"); + out.indent(); + field.writeTo(out); + out.deindent(); + i++; + } } } diff --git a/src/main/java/org/JesusFreke/dexlib/FixedByteArrayField.java b/src/main/java/org/JesusFreke/dexlib/FixedByteArrayField.java index 54cb7019..c7e1d675 100644 --- a/src/main/java/org/JesusFreke/dexlib/FixedByteArrayField.java +++ b/src/main/java/org/JesusFreke/dexlib/FixedByteArrayField.java @@ -28,27 +28,34 @@ package org.JesusFreke.dexlib; -import org.JesusFreke.dexlib.util.Output; import org.JesusFreke.dexlib.util.Input; import org.JesusFreke.dexlib.util.ByteArray; +import org.JesusFreke.dexlib.util.AnnotatedOutput; public class FixedByteArrayField implements Field { protected byte[] value; + private final String fieldName; - public FixedByteArrayField(int size) { + public FixedByteArrayField(int size, String fieldName) { value = new byte[size]; + this.fieldName = fieldName; } - public FixedByteArrayField(byte[] bytes) { + public FixedByteArrayField(byte[] bytes, String fieldName) { this.value = bytes.clone(); + this.fieldName = fieldName; } - public FixedByteArrayField(ByteArray byteArray) { + public FixedByteArrayField(ByteArray byteArray, String fieldName) { value = new byte[byteArray.size()]; byteArray.getBytes(value, 0); + this.fieldName = fieldName; } - public void writeTo(Output out) { + public void writeTo(AnnotatedOutput out) { + if (fieldName != null) { + out.annotate(fieldName); + } out.write(value); } diff --git a/src/main/java/org/JesusFreke/dexlib/HeaderItem.java b/src/main/java/org/JesusFreke/dexlib/HeaderItem.java index e23252f0..20f29a63 100644 --- a/src/main/java/org/JesusFreke/dexlib/HeaderItem.java +++ b/src/main/java/org/JesusFreke/dexlib/HeaderItem.java @@ -29,7 +29,7 @@ package org.JesusFreke.dexlib; import org.JesusFreke.dexlib.ItemType; -import org.JesusFreke.dexlib.util.Output; +import org.JesusFreke.dexlib.util.AnnotatedOutput; import java.io.UnsupportedEncodingException; @@ -46,103 +46,97 @@ public class HeaderItem extends IndexedItem { /** the endianness tag */ private static final int ENDIAN_TAG = 0x12345678; - private final Field[] fields; - - protected Field[] getFields() { - return fields; - } - - private final FixedByteArrayField magic; - private final IntegerField checksum; - private final FixedByteArrayField signature; - private final IntegerField fileSize; - private final IntegerField headerSize; - private final IntegerField endianTag; - private final IntegerField linkSize; - private final IntegerField linkOff; - private final IntegerField mapOff; - private final SectionHeaderInfo StringIdsInfo; - private final SectionHeaderInfo TypeIdsInfo; - private final SectionHeaderInfo ProtoIdsInfo; - private final SectionHeaderInfo FieldIdsInfo; - private final SectionHeaderInfo MethodIdsInfo; - private final SectionHeaderInfo ClassDefsInfo; - private final IntegerField dataSize; - private final IntegerField dataOff; + private final FixedByteArrayField magicField; + private final IntegerField checksumField; + private final FixedByteArrayField signatureField; + private final IntegerField fileSizeField; + private final IntegerField headerSizeField; + private final IntegerField endianTagField; + private final IntegerField linkSizeField; + private final IntegerField linkOffField; + private final IntegerField mapOffField; + private final SectionHeaderInfo StringIdsHeaderField; + private final SectionHeaderInfo TypeIdsHeaderField; + private final SectionHeaderInfo ProtoIdsHeaderField; + private final SectionHeaderInfo FieldIdsHeaderField; + private final SectionHeaderInfo MethodIdsHeaderField; + private final SectionHeaderInfo ClassDefsHeaderField; + private final IntegerField dataSizeField; + private final IntegerField dataOffField; public HeaderItem(final DexFile file, int index) throws UnsupportedEncodingException { super(index); fields = new Field[] { - magic = new FixedByteArrayField(MAGIC.getBytes("US-ASCII")), - checksum = new IntegerField() { - public void writeTo(Output out) { + magicField = new FixedByteArrayField(MAGIC.getBytes("US-ASCII"), "magic"), + checksumField = new IntegerField("checksum") { + public void writeTo(AnnotatedOutput out) { cacheValue(0); super.writeTo(out); } }, - signature = new FixedByteArrayField(20) { - public void writeTo(Output out) { + signatureField = new FixedByteArrayField(20, "signature") { + public void writeTo(AnnotatedOutput out) { for (int i = 0; i < value.length; i++) { value[i] = 0; } super.writeTo(out); } }, - fileSize = new IntegerField() { - public void writeTo(Output out) { + fileSizeField = new IntegerField("file_size") { + public void writeTo(AnnotatedOutput out) { cacheValue(file.getFileSize()); super.writeTo(out); } }, - headerSize = new IntegerField(HEADER_SIZE), - endianTag = new IntegerField(ENDIAN_TAG), - linkSize = new IntegerField(0), - linkOff = new IntegerField(0), - mapOff = new IntegerField() { - public void writeTo(Output out) { + headerSizeField = new IntegerField(HEADER_SIZE,"header_size"), + endianTagField = new IntegerField(ENDIAN_TAG,"endian_tag"), + linkSizeField = new IntegerField(0,"link_size"), + linkOffField = new IntegerField(0,"link_off"), + mapOffField = new IntegerField("map_off") { + public void writeTo(AnnotatedOutput out) { cacheValue(file.MapSection.getOffset()); super.writeTo(out); } }, - StringIdsInfo = new SectionHeaderInfo() { + StringIdsHeaderField = new SectionHeaderInfo("string_ids") { protected Section getSection() { return file.StringIdsSection; } }, - TypeIdsInfo = new SectionHeaderInfo() { + TypeIdsHeaderField = new SectionHeaderInfo("type_ids") { protected Section getSection() { return file.TypeIdsSection; } }, - ProtoIdsInfo = new SectionHeaderInfo() { + ProtoIdsHeaderField = new SectionHeaderInfo("proto_ids") { protected Section getSection() { return file.ProtoIdsSection; } }, - FieldIdsInfo = new SectionHeaderInfo() { + FieldIdsHeaderField = new SectionHeaderInfo("field_ids") { protected Section getSection() { return file.FieldIdsSection; } }, - MethodIdsInfo = new SectionHeaderInfo() { + MethodIdsHeaderField = new SectionHeaderInfo("method_ids") { protected Section getSection() { return file.MethodIdsSection; } }, - ClassDefsInfo = new SectionHeaderInfo() { + ClassDefsHeaderField = new SectionHeaderInfo("class_defs") { protected Section getSection() { return file.ClassDefsSection; } }, - dataSize = new IntegerField() { - public void writeTo(Output out) { + dataSizeField = new IntegerField("data_size") { + public void writeTo(AnnotatedOutput out) { cacheValue(file.getDataSize()); super.writeTo(out); } }, - dataOff = new IntegerField() { - public void writeTo(Output out) { + dataOffField = new IntegerField("data_off") { + public void writeTo(AnnotatedOutput out) { cacheValue(file.getDataOffset()); super.writeTo(out); } @@ -151,7 +145,7 @@ public class HeaderItem extends IndexedItem { } public int getMapOffset() { - return mapOff.getCachedValue(); + return mapOffField.getCachedValue(); } protected int getAlignment() { @@ -162,6 +156,10 @@ public class HeaderItem extends IndexedItem { return ItemType.TYPE_HEADER_ITEM; } + public String getConciseIdentity() { + return "header_item"; + } + public int compareTo(HeaderItem o) { //there is only 1 header item return 0; diff --git a/src/main/java/org/JesusFreke/dexlib/IndexedItem.java b/src/main/java/org/JesusFreke/dexlib/IndexedItem.java index 895d948d..d8a2f6f2 100644 --- a/src/main/java/org/JesusFreke/dexlib/IndexedItem.java +++ b/src/main/java/org/JesusFreke/dexlib/IndexedItem.java @@ -29,18 +29,7 @@ package org.JesusFreke.dexlib; public abstract class IndexedItem extends Item implements Comparable { - private int index; - protected IndexedItem(int index) { this.index = index; } - - public int place(int index, int offset) { - this.index = index; - return super.place(index, offset); - } - - public int getIndex() { - return index; - } -} +} \ No newline at end of file diff --git a/src/main/java/org/JesusFreke/dexlib/IndexedItemReference.java b/src/main/java/org/JesusFreke/dexlib/IndexedItemReference.java index 85a2e254..d2796310 100644 --- a/src/main/java/org/JesusFreke/dexlib/IndexedItemReference.java +++ b/src/main/java/org/JesusFreke/dexlib/IndexedItemReference.java @@ -28,49 +28,41 @@ package org.JesusFreke.dexlib; -import org.JesusFreke.dexlib.util.Output; import org.JesusFreke.dexlib.util.Input; +import org.JesusFreke.dexlib.util.AnnotatedOutput; public class IndexedItemReference> extends ItemReference> implements Comparable> { - private final CachedIntegerValueField underlyingField; - public IndexedItemReference(IndexedSection section, CachedIntegerValueField underlyingField) { - super(section); - this.underlyingField = underlyingField; + public IndexedItemReference(IndexedSection section, CachedIntegerValueField underlyingField, + String fieldName) { + super(section, underlyingField, fieldName); } - public IndexedItemReference(DexFile dexFile, T item, CachedIntegerValueField underlyingField) { - super(dexFile, item); - this.underlyingField = underlyingField; - } - - public void writeTo(Output out) { - T item = getReference(); - if (item != null && !item.isPlaced()) { - throw new RuntimeException("Trying to write a reference to an item that hasn't been placed."); - } - underlyingField.writeTo(out); - } - - public void readFrom(Input in) { - underlyingField.readFrom(in); - if (underlyingField.getCachedValue() != -1) { - setReference(getSection().getByIndex(underlyingField.getCachedValue())); - } + public IndexedItemReference(DexFile dexFile, T item, CachedIntegerValueField underlyingField, + String fieldName) { + super(dexFile, item, underlyingField, fieldName); } public IndexedSection getSection() { return (IndexedSection)super.getSection(); } - public int place(int offset) { - if (getReference() != null) { - underlyingField.cacheValue(getReference().getIndex()); + protected int getReferenceValue() { + T item = getReference(); + if (item == null) { + return -1; } else { - underlyingField.cacheValue(-1); + return item.getIndex(); + } + } + + protected T getReferencedItem(int referenceValue) { + if (referenceValue == -1) { + return null; + } else { + return getSection().getByIndex(referenceValue); } - return underlyingField.place(offset); } public int compareTo(IndexedItemReference o) { diff --git a/src/main/java/org/JesusFreke/dexlib/IntegerField.java b/src/main/java/org/JesusFreke/dexlib/IntegerField.java index d04cf694..32ff89a5 100644 --- a/src/main/java/org/JesusFreke/dexlib/IntegerField.java +++ b/src/main/java/org/JesusFreke/dexlib/IntegerField.java @@ -28,21 +28,16 @@ package org.JesusFreke.dexlib; -import org.JesusFreke.dexlib.util.Output; import org.JesusFreke.dexlib.util.Input; +import org.JesusFreke.dexlib.util.Output; -public class IntegerField extends CachedIntegerValueField { - protected int value = 0; - - public IntegerField() { +public class IntegerField extends CachedIntegerValueField { + public IntegerField(String fieldName) { + super(fieldName); } - public IntegerField(int value) { - this.value = value; - } - - public void writeTo(Output out) { - out.writeInt(value); + public IntegerField(int value, String fieldName) { + super(value, fieldName); } public void readFrom(Input in) { @@ -53,18 +48,7 @@ public class IntegerField extends CachedIntegerValueField { return offset + 4; } - /** - * This method returns the integer value that has been cached. This - * value is either the value that the field was constructed with, the - * value that was read via readFrom, or the value that was - * cached when place was called - * @return the cached value - */ - public int getCachedValue() { - return value; - } - - public void cacheValue(int value) { - this.value = value; + protected void writeValue(Output out) { + out.writeInt(value); } } diff --git a/src/main/java/org/JesusFreke/dexlib/Item.java b/src/main/java/org/JesusFreke/dexlib/Item.java index d3a4c4a8..d3591cbc 100644 --- a/src/main/java/org/JesusFreke/dexlib/Item.java +++ b/src/main/java/org/JesusFreke/dexlib/Item.java @@ -30,18 +30,18 @@ package org.JesusFreke.dexlib; import org.JesusFreke.dexlib.util.Input; import org.JesusFreke.dexlib.util.Output; +import org.JesusFreke.dexlib.util.AnnotatedOutput; import org.JesusFreke.dexlib.ItemType; public abstract class Item { - private int offset = -1; + protected int offset = -1; + protected int index = -1; + + protected Field[] fields; protected Item() { } - protected Item(int offset) { - this.offset = offset; - } - public boolean isPlaced() { return offset > -1; } @@ -53,10 +53,9 @@ public abstract class Item { public int place(int index, int offset) { offset = alignOffset(offset); + this.index = index; this.offset = offset; - Field[] fields = getFields(); - for (Field field: fields) { offset = field.place(offset); } @@ -64,23 +63,30 @@ public abstract class Item { } public void readFrom(Input in) { - for (Field field: getFields()) { + for (Field field: fields) { field.readFrom(in); } } - public void writeTo(Output out) { + public void writeTo(AnnotatedOutput out) { out.alignTo(getAlignment()); + out.annotate(0, "[0x" + Integer.toHexString(this.getIndex()) + "] " + this.getItemType().getTypeName() ); + out.indent(); + if (out.getCursor() != offset) { throw new RuntimeException("Item is being written somewhere other than where it was placed"); } - for (Field field: getFields()) { + for (Field field: fields) { field.writeTo(out); } + + out.deindent(); } - protected abstract int getAlignment(); + protected int getAlignment() { + return 1; + } protected int alignOffset(int offset) { int mask = getAlignment() - 1; @@ -88,49 +94,57 @@ public abstract class Item { return (offset + mask) & ~mask; } - protected int getOffset() { + public int getOffset() { return offset; } - protected abstract Field[] getFields(); + public int getIndex() { + return index; + } public abstract ItemType getItemType(); public void copyTo(DexFile dexFile, T copy) { - Field[] fields = getFields(); - Field[] fieldsCopy = copy.getFields(); for (int i = 0; i < fields.length; i++) { - fields[i].copyTo(dexFile, fieldsCopy[i]); + fields[i].copyTo(dexFile, copy.fields[i]); } } public int hashCode() { int h = 1; - for (Field field: getFields()) { + for (Field field: fields) { h = h*31 + field.hashCode(); } return h; } public boolean equals(Object o) { - if (!(o instanceof Item)) { + if (!this.getClass().isInstance(o)) { return false; } Item other = (Item)o; - Field[] fields = getFields(); - Field[] otherFields = other.getFields(); - if (fields.length != otherFields.length) { + if (fields.length != other.fields.length) { return false; } for (int i = 0; i < fields.length; i++) { - if (!fields[i].equals(otherFields[i])) { + if (!fields[i].equals(other.fields[i])) { return false; } } return true; } + + /** + * Returns a concise string value that conveys the identity of this item + * @return A concise string value that conveys the identity of this item + */ + public abstract String getConciseIdentity(); + + public String toString() { + return getConciseIdentity(); + } } diff --git a/src/main/java/org/JesusFreke/dexlib/ItemReference.java b/src/main/java/org/JesusFreke/dexlib/ItemReference.java index 9f6023e3..c07b29dc 100644 --- a/src/main/java/org/JesusFreke/dexlib/ItemReference.java +++ b/src/main/java/org/JesusFreke/dexlib/ItemReference.java @@ -28,19 +28,28 @@ package org.JesusFreke.dexlib; +import org.JesusFreke.dexlib.util.AnnotatedOutput; +import org.JesusFreke.dexlib.util.Input; + public abstract class ItemReference, S extends ItemReference> implements Field { private T item = null; private Section section; + private final CachedIntegerValueField underlyingField; + private final String fieldName; - public ItemReference(Section section) { + public ItemReference(Section section, CachedIntegerValueField underlyingField, String fieldName) { this.section = section; + this.underlyingField = underlyingField; + this.fieldName = fieldName; } - public ItemReference(DexFile dexFile, T item) { + public ItemReference(DexFile dexFile, T item, CachedIntegerValueField underlyingField, String fieldName) { if (item != null) { section = dexFile.getSectionForItem(item); this.item = item; } + this.underlyingField = underlyingField; + this.fieldName = fieldName; } public T getReference() { @@ -65,6 +74,34 @@ public abstract class ItemReference, S extends ItemReference, S extends ItemReference { private final ArrayList list; private final CachedIntegerValueField underlyingField; @@ -42,13 +42,14 @@ public class ListSizeField extends CachedIntegerValueField { this.underlyingField = underlyingField; } - public void writeTo(Output out) { + public void writeTo(AnnotatedOutput out) { underlyingField.writeTo(out); } public void readFrom(Input in) { underlyingField.readFrom(in); - /** the absolute value operation is needed for the case when a list sized is + /** + * the absolute value operation is needed for the case when a list size is * encoded as the absolute value of a signed integer */ int listSize = Math.abs(underlyingField.getCachedValue()); @@ -65,6 +66,10 @@ public class ListSizeField extends CachedIntegerValueField { return underlyingField.place(offset); } + public void copyTo(DexFile dexFile, ListSizeField copy) { + //nothing to do, the value is retrieved from the list + } + public int getCachedValue() { return underlyingField.getCachedValue(); } diff --git a/src/main/java/org/JesusFreke/dexlib/MapField.java b/src/main/java/org/JesusFreke/dexlib/MapField.java index 54fe5d9a..8887eba1 100644 --- a/src/main/java/org/JesusFreke/dexlib/MapField.java +++ b/src/main/java/org/JesusFreke/dexlib/MapField.java @@ -29,32 +29,20 @@ package org.JesusFreke.dexlib; import org.JesusFreke.dexlib.ItemType; -import org.JesusFreke.dexlib.util.Output; +import org.JesusFreke.dexlib.util.AnnotatedOutput; public class MapField extends CompositeField { - private final Field[] fields; - - public ItemType getSectionItemType() { - return ItemType.fromInt(sectionType.getCachedValue()); - } - - public int getSectionSize() { - return sectionInfo.getSectionSize(); - } - - public int getSectionOffset() { - return sectionInfo.getSectionOffset(); - } - - private final ShortIntegerField sectionType; - private final ShortIntegerField unused; - private final SectionHeaderInfo sectionInfo; + private final ShortIntegerField sectionTypeField; + private final ShortIntegerField unusedField; + private final SectionHeaderInfo sectionInfoField; public MapField(final DexFile dexFile) { + super("map_entry"); fields = new Field[] { - sectionType = new ShortIntegerField(), - unused = new ShortIntegerField((short)0), - sectionInfo = new SectionHeaderInfo() { + //TODO: add an annotation for the item type + sectionTypeField = new ShortIntegerField("type"), + unusedField = new ShortIntegerField((short)0, "not used"), + sectionInfoField = new SectionHeaderInfo("section") { protected Section getSection() { return dexFile.getSectionForType(getSectionItemType()); } @@ -63,26 +51,19 @@ public class MapField extends CompositeField { } public MapField(final DexFile dexFile, short sectionType) { - fields = new Field[] { - this.sectionType = new ShortIntegerField(sectionType), - this.unused = new ShortIntegerField((short)0), - this.sectionInfo = new SectionHeaderInfo() { - protected Section getSection() { - return dexFile.getSectionForType(getSectionItemType()); - } - } - }; + this(dexFile); + sectionTypeField.cacheValue(sectionType); + } + + public ItemType getSectionItemType() { + return ItemType.fromInt(sectionTypeField.getCachedValue()); } - public int place(int offset) { - return super.place(offset); + public int getSectionSize() { + return sectionInfoField.getSectionSize(); } - public void writeTo(Output out) { - super.writeTo(out); - } - - protected Field[] getFields() { - return fields; + public int getSectionOffset() { + return sectionInfoField.getSectionOffset(); } } diff --git a/src/main/java/org/JesusFreke/dexlib/MapItem.java b/src/main/java/org/JesusFreke/dexlib/MapItem.java index 53461adb..68b9d5d3 100644 --- a/src/main/java/org/JesusFreke/dexlib/MapItem.java +++ b/src/main/java/org/JesusFreke/dexlib/MapItem.java @@ -29,22 +29,21 @@ package org.JesusFreke.dexlib; import org.JesusFreke.dexlib.ItemType; -import org.JesusFreke.dexlib.util.Output; +import org.JesusFreke.dexlib.util.AnnotatedOutput; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; public class MapItem extends IndexedItem { - private final Field[] fields; private ArrayList mapEntries = new ArrayList(); public MapItem(final DexFile dexFile, int index) { super(index); fields = new Field[] { - new ListSizeField(mapEntries, new IntegerField()), - new FieldListField(mapEntries) { + new ListSizeField(mapEntries, new IntegerField("size")), + new FieldListField(mapEntries, "map_entry") { protected MapField make() { return new MapField(dexFile); } @@ -64,7 +63,7 @@ public class MapItem extends IndexedItem { return super.place(index, offset); } - public void writeTo(Output out) { + public void writeTo(AnnotatedOutput out) { Collections.sort(mapEntries, new Comparator() { public int compare(MapField o1, MapField o2) { @@ -108,10 +107,6 @@ public class MapItem extends IndexedItem { return 4; } - protected Field[] getFields() { - return fields; - } - public ItemType getItemType() { return ItemType.TYPE_MAP_LIST; } @@ -128,6 +123,10 @@ public class MapItem extends IndexedItem { return getClass() == o.getClass(); } + public String getConciseIdentity() { + return "map_item"; + } + public int compareTo(MapItem o) { //there is only 1 map item return 1; diff --git a/src/main/java/org/JesusFreke/dexlib/MethodIdItem.java b/src/main/java/org/JesusFreke/dexlib/MethodIdItem.java index d86c81f1..6efa000c 100644 --- a/src/main/java/org/JesusFreke/dexlib/MethodIdItem.java +++ b/src/main/java/org/JesusFreke/dexlib/MethodIdItem.java @@ -29,65 +29,60 @@ package org.JesusFreke.dexlib; public class MethodIdItem extends IndexedItem { - private final Field[] fields; - - private final IndexedItemReference classType; - private final IndexedItemReference prototype; - private final IndexedItemReference methodName; + private final IndexedItemReference classTypeReferenceField; + private final IndexedItemReference prototypeReferenceField; + private final IndexedItemReference methodNameReferenceField; public MethodIdItem(DexFile dexFile, int index) { super(index); fields = new Field[] { - classType = new IndexedItemReference(dexFile.TypeIdsSection, new ShortIntegerField()), - prototype = new IndexedItemReference(dexFile.ProtoIdsSection, new ShortIntegerField()), - methodName = new IndexedItemReference(dexFile.StringIdsSection, new IntegerField()) + classTypeReferenceField = new IndexedItemReference(dexFile.TypeIdsSection, + new ShortIntegerField(null), "class_idx"), + prototypeReferenceField = new IndexedItemReference(dexFile.ProtoIdsSection, + new ShortIntegerField(null), "proto_idx"), + methodNameReferenceField = new IndexedItemReference(dexFile.StringIdsSection, + new IntegerField(null), "name_idx") }; } public MethodIdItem(DexFile dexFile, TypeIdItem classType, StringIdItem methodName, ProtoIdItem prototype) { - super(-1); - fields = new Field[] { - this.classType = new IndexedItemReference(dexFile, classType, new ShortIntegerField()), - this.prototype = new IndexedItemReference(dexFile, prototype, new ShortIntegerField()), - this.methodName = new IndexedItemReference(dexFile, methodName, new IntegerField()) - }; + this(dexFile, -1); + classTypeReferenceField.setReference(classType); + prototypeReferenceField.setReference(prototype); + methodNameReferenceField.setReference(methodName); } - public MethodIdItem(DexFile dexFile, TypeIdItem classType, String methodName, ProtoIdItem prototype) { - this(dexFile, classType, new StringIdItem(dexFile, methodName), prototype); - } - - protected int getAlignment() { return 4; } - protected Field[] getFields() { - return fields; - } - public ItemType getItemType() { return ItemType.TYPE_METHOD_ID_ITEM; } - public TypeIdItem getClassType() { - return classType.getReference(); + public String getConciseIdentity() { + return "method_id_item: " + getMethodString(); + } + + private String cachedMethodString = null; + public String getMethodString() { + if (cachedMethodString == null) { + String parentClass = classTypeReferenceField.getReference().getTypeDescriptor(); + //strip the leading L and trailing ; + parentClass = parentClass.substring(1, parentClass.length() - 1); + + cachedMethodString = parentClass + methodNameReferenceField.getReference().getStringValue() + + prototypeReferenceField.getReference().getPrototypeString(); + } + return cachedMethodString; } public String getMethodName() { - return methodName.getReference().toString(); + return methodNameReferenceField.getReference().getStringValue(); } - public void setClassType(TypeIdItem newClassType) { - classType.setReference(newClassType); - } - - public String toString() { - return classType.getReference().toString() + " - " + methodName.getReference().toString(); - } - - public int getParameterWordCount(boolean isStatic) { - return prototype.getReference().getParameterWordCount() + (isStatic?0:1); + public int getParameterRegisterCount(boolean isStatic) { + return prototypeReferenceField.getReference().getParameterRegisterCount() + (isStatic?0:1); } /** @@ -95,20 +90,20 @@ public class MethodIdItem extends IndexedItem { * @return The number of parameters, not including the "this" parameter, if any */ public int getParameterCount() { - return prototype.getReference().getParameterCount(); + return prototypeReferenceField.getReference().getParameterCount(); } public int compareTo(MethodIdItem o) { - int result = classType.compareTo(o.classType); + int result = classTypeReferenceField.compareTo(o.classTypeReferenceField); if (result != 0) { return result; } - result = methodName.compareTo(o.methodName); + result = methodNameReferenceField.compareTo(o.methodNameReferenceField); if (result != 0) { return result; } - return prototype.compareTo(o.prototype); + return prototypeReferenceField.compareTo(o.prototypeReferenceField); } } diff --git a/src/main/java/org/JesusFreke/dexlib/NullTerminatedByteArrayField.java b/src/main/java/org/JesusFreke/dexlib/NullTerminatedByteArrayField.java index c70980a8..897950c4 100644 --- a/src/main/java/org/JesusFreke/dexlib/NullTerminatedByteArrayField.java +++ b/src/main/java/org/JesusFreke/dexlib/NullTerminatedByteArrayField.java @@ -28,26 +28,31 @@ package org.JesusFreke.dexlib; -import org.JesusFreke.dexlib.util.Output; import org.JesusFreke.dexlib.util.Input; import org.JesusFreke.dexlib.util.ByteArray; +import org.JesusFreke.dexlib.util.AnnotatedOutput; public class NullTerminatedByteArrayField implements Field { protected byte[] value; + private final String fieldName; - public NullTerminatedByteArrayField() { + public NullTerminatedByteArrayField(String fieldName) { + this.fieldName = fieldName; } - public NullTerminatedByteArrayField(byte[] value) { + public NullTerminatedByteArrayField(byte[] value, String fieldName) { + this(fieldName); this.value = value.clone(); } - public NullTerminatedByteArrayField(ByteArray byteArray) { + public NullTerminatedByteArrayField(ByteArray byteArray, String fieldName) { + this(fieldName); value = new byte[byteArray.size()]; byteArray.getBytes(value, 0); } - public void writeTo(Output out) { + public void writeTo(AnnotatedOutput out) { + out.annotate(fieldName); out.write(value); out.writeByte(0); } diff --git a/src/main/java/org/JesusFreke/dexlib/OffsettedItem.java b/src/main/java/org/JesusFreke/dexlib/OffsettedItem.java index 71485ab5..a6e02bfc 100644 --- a/src/main/java/org/JesusFreke/dexlib/OffsettedItem.java +++ b/src/main/java/org/JesusFreke/dexlib/OffsettedItem.java @@ -29,8 +29,7 @@ package org.JesusFreke.dexlib; public abstract class OffsettedItem> extends Item { - public OffsettedItem(int offset) { - super(offset); + this.offset = offset; } } diff --git a/src/main/java/org/JesusFreke/dexlib/OffsettedItemReference.java b/src/main/java/org/JesusFreke/dexlib/OffsettedItemReference.java index b1047297..5a4ac073 100644 --- a/src/main/java/org/JesusFreke/dexlib/OffsettedItemReference.java +++ b/src/main/java/org/JesusFreke/dexlib/OffsettedItemReference.java @@ -28,56 +28,41 @@ package org.JesusFreke.dexlib; -import org.JesusFreke.dexlib.util.Output; import org.JesusFreke.dexlib.util.Input; +import org.JesusFreke.dexlib.util.AnnotatedOutput; public class OffsettedItemReference> extends ItemReference> { - private final CachedIntegerValueField underlyingField; - - public OffsettedItemReference(OffsettedSection section, CachedIntegerValueField underlyingField) { - super(section); - this.underlyingField = underlyingField; + + public OffsettedItemReference(DexFile dexFile, T item, CachedIntegerValueField underlyingField, + String fieldName) { + super(dexFile, item, underlyingField, fieldName); + } + + public OffsettedItemReference(OffsettedSection section, CachedIntegerValueField underlyingField, + String fieldName) { + super(section, underlyingField, fieldName); } - public OffsettedItemReference(DexFile dexFile, T item, CachedIntegerValueField underlyingField) { - super(dexFile, item); - this.underlyingField = underlyingField; - } public OffsettedSection getSection() { return (OffsettedSection)super.getSection(); } - public void writeTo(Output out) { + protected int getReferenceValue() { T item = getReference(); - if (item != null && !item.isPlaced()) { - throw new RuntimeException("Trying to write reference to an item that hasn't been placed."); - } - - //TODO: this is a hack to force it to reload the correct offset value + if (item == null) { - underlyingField.cacheValue(0); + return 0; } else { - underlyingField.cacheValue(item.getOffset()); - } - - underlyingField.writeTo(out); - } - - public void readFrom(Input in) { - underlyingField.readFrom(in); - if (underlyingField.getCachedValue() != 0) { - setReference(getSection().getByOffset(underlyingField.getCachedValue())); + return item.getOffset(); } } - public int place(int offset) { - if (getReference() != null) { - underlyingField.cacheValue(getReference().getOffset()); - } else { - underlyingField.cacheValue(0); + protected T getReferencedItem(int referenceValue) { + if (referenceValue == 0) { + return null; } - return underlyingField.place(offset); + return getSection().getByOffset(referenceValue); } } diff --git a/src/main/java/org/JesusFreke/dexlib/ProtoIdItem.java b/src/main/java/org/JesusFreke/dexlib/ProtoIdItem.java index 96aac46d..12c43e28 100644 --- a/src/main/java/org/JesusFreke/dexlib/ProtoIdItem.java +++ b/src/main/java/org/JesusFreke/dexlib/ProtoIdItem.java @@ -33,40 +33,41 @@ import org.JesusFreke.dexlib.ItemType; import java.util.ArrayList; public class ProtoIdItem extends IndexedItem { - private final Field[] fields; - - private final IndexedItemReference shortyDescriptor; - private final IndexedItemReference returnType; - private final OffsettedItemReference parameters; + private final IndexedItemReference shortyDescriptorReferenceField; + private final IndexedItemReference returnTypeReferenceField; + private final OffsettedItemReference parametersReferenceField; public ProtoIdItem(DexFile dexFile, int index) { super(index); fields = new Field[] { - shortyDescriptor = new IndexedItemReference(dexFile.StringIdsSection, new IntegerField()), - returnType = new IndexedItemReference(dexFile.TypeIdsSection, new IntegerField()), - parameters = new OffsettedItemReference(dexFile.TypeListsSection, new IntegerField()) + shortyDescriptorReferenceField = new IndexedItemReference(dexFile.StringIdsSection, + new IntegerField(null), "shorty_idx"), + returnTypeReferenceField = new IndexedItemReference(dexFile.TypeIdsSection, + new IntegerField(null), "return_type_idx"), + parametersReferenceField = new OffsettedItemReference(dexFile.TypeListsSection, + new IntegerField(null), "parameters_off") }; } public ProtoIdItem(DexFile dexFile, TypeIdItem returnType, ArrayList parameters) { - super(-1); - StringIdItem stringIdItem = new StringIdItem(dexFile, createShortyDescriptor(returnType, parameters)); - TypeListItem typeListItem = new TypeListItem(dexFile, parameters); - - fields = new Field[] { - this.shortyDescriptor = new IndexedItemReference(dexFile, stringIdItem, new IntegerField()), - this.returnType = new IndexedItemReference(dexFile, returnType, new IntegerField()), - this.parameters = new OffsettedItemReference(dexFile, typeListItem, new IntegerField()) - }; + this(dexFile, -1); + shortyDescriptorReferenceField.setReference( + new StringIdItem(dexFile, createShortyDescriptor(returnType, parameters))); + returnTypeReferenceField.setReference(returnType); + if (parameters != null && parameters.size() > 0) { + parametersReferenceField.setReference(new TypeListItem(dexFile, parameters)); + } } private String createShortyDescriptor(TypeIdItem returnType, ArrayList parameters) { StringBuilder sb = new StringBuilder(); sb.append(returnType.toShorty()); - for (TypeIdItem typeIdItem: parameters) { - sb.append(typeIdItem.toShorty()); + if (parameters != null) { + for (TypeIdItem typeIdItem: parameters) { + sb.append(typeIdItem.toShorty()); + } } return sb.toString(); } @@ -75,18 +76,17 @@ public class ProtoIdItem extends IndexedItem { return 4; } - public int getParameterWordCount() { - TypeListItem typeList = parameters.getReference(); + public int getParameterRegisterCount() { + TypeListItem typeList = parametersReferenceField.getReference(); if (typeList == null) { return 0; } else { - return typeList.getWordCount(); + return typeList.getRegisterCount(); } } - public int getParameterCount() { - TypeListItem typeList = parameters.getReference(); + TypeListItem typeList = parametersReferenceField.getReference(); if (typeList == null) { return 0; } else { @@ -94,29 +94,44 @@ public class ProtoIdItem extends IndexedItem { } } - protected Field[] getFields() { - return fields; - } - public ItemType getItemType() { return ItemType.TYPE_PROTO_ID_ITEM; } public int compareTo(ProtoIdItem o) { - int result = returnType.compareTo(o.returnType); + int result = returnTypeReferenceField.compareTo(o.returnTypeReferenceField); if (result != 0) { return result; } - TypeListItem thisParameters = parameters.getReference(); + TypeListItem thisParameters = parametersReferenceField.getReference(); if (thisParameters == null) { return -1; } - return thisParameters.compareTo(o.parameters.getReference()); + return thisParameters.compareTo(o.parametersReferenceField.getReference()); } - public String toString() { - return shortyDescriptor.toString(); + public String getConciseIdentity() { + return "proto_id_item: " + getPrototypeString(); + } + + private String cachedPrototypeString = null; + public String getPrototypeString() { + if (cachedPrototypeString == null) { + StringBuilder sb = new StringBuilder(); + + TypeListItem parameterList = this.parametersReferenceField.getReference(); + + if (parameterList != null) { + for (TypeIdItem type: parameterList.getTypes()) { + sb.append(type.getTypeDescriptor()); + } + } + + cachedPrototypeString = "(" + sb.toString() + ")" + + this.returnTypeReferenceField.getReference().getTypeDescriptor(); + } + return cachedPrototypeString; } } diff --git a/src/main/java/org/JesusFreke/dexlib/Section.java b/src/main/java/org/JesusFreke/dexlib/Section.java index e59998e7..b4a1e4be 100644 --- a/src/main/java/org/JesusFreke/dexlib/Section.java +++ b/src/main/java/org/JesusFreke/dexlib/Section.java @@ -30,6 +30,7 @@ package org.JesusFreke.dexlib; import org.JesusFreke.dexlib.util.Output; import org.JesusFreke.dexlib.util.Input; +import org.JesusFreke.dexlib.util.AnnotatedOutput; import java.util.ArrayList; import java.util.HashMap; @@ -56,14 +57,14 @@ public abstract class Section { for (int i=0; i < items.size(); i++) { T item = items.get(i); if (item == null) { - items.remove(i--); - continue; + throw new RuntimeException("This section contains a null item"); } offset = item.place(i, offset); if (i == 0) { - /** if this item type has an alignment requirement, + /** + * if this item type has an alignment requirement, * then item.getOffset() may be different than the - * offset before item.place was called, so we have + * offset that was passed in to this method, so we have * to initialize the section offset to the actual * (post-alignment) offset of the first item */ @@ -80,21 +81,24 @@ public abstract class Section { } } - public void writeTo(Output out) { + public void writeTo(AnnotatedOutput out) { for (int i = 0; i < size(); i++) { T item = items.get(i); if (item == null) { throw new RuntimeException("Cannot write section because all items haven't been initialized"); } item.writeTo(out); + out.annotate(0, " "); } + out.annotate(0, " "); } public abstract void readFrom(int size, Input in); protected void setSize(int size) { if (items.size() > size) { - throw new RuntimeException("This section contains references to items beyond the size of the section"); + throw new RuntimeException("There are references elsewhere to items in this section, that are " + + "beyond the end of the section"); } items.ensureCapacity(size); diff --git a/src/main/java/org/JesusFreke/dexlib/SectionHeaderInfo.java b/src/main/java/org/JesusFreke/dexlib/SectionHeaderInfo.java index 48a11d4c..b1c53893 100644 --- a/src/main/java/org/JesusFreke/dexlib/SectionHeaderInfo.java +++ b/src/main/java/org/JesusFreke/dexlib/SectionHeaderInfo.java @@ -28,56 +28,44 @@ package org.JesusFreke.dexlib; -import org.JesusFreke.dexlib.util.Output; import org.JesusFreke.dexlib.util.Input; +import org.JesusFreke.dexlib.util.AnnotatedOutput; -public abstract class SectionHeaderInfo implements Field { - private int sectionSize; - private int sectionOffset; +public abstract class SectionHeaderInfo extends CompositeField { + private final String sectionName; - public SectionHeaderInfo() { + private final IntegerField sectionSizeField; + private final IntegerField sectionOffsetField; + + + public SectionHeaderInfo(String sectionName) { + super(sectionName); + fields = new Field[] { + sectionSizeField = new IntegerField(sectionName + "_size"), + sectionOffsetField = new IntegerField(sectionName + "_off") + }; + this.sectionName = sectionName; } protected abstract Section getSection(); - public void writeTo(Output out) { + public void writeTo(AnnotatedOutput out) { Section section = getSection(); if (!section.isPlaced()) { throw new RuntimeException("Trying to write a reference to a section that hasn't been placed."); } - sectionSize = section.size(); - sectionOffset = section.getOffset(); + sectionSizeField.cacheValue(section.size()); + sectionOffsetField.cacheValue(section.getOffset()); - out.writeInt(sectionSize); - out.writeInt(sectionOffset); - } - - public void readFrom(Input in) { - sectionSize = in.readInt(); - sectionOffset = in.readInt(); + super.writeTo(out); } public int getSectionSize() { - return sectionSize; + return sectionSizeField.getCachedValue(); } public int getSectionOffset() { - return sectionOffset; - } - - public int place(int offset) { - Section section = getSection(); - sectionSize = section.size(); - sectionOffset = section.getOffset(); - - return offset+8; - } - - public void copyTo(DexFile dexFile, SectionHeaderInfo copy) { - /** - * do nothing. the section size and offset are dynamically generated - * when the copy is written - */ + return sectionOffsetField.getCachedValue(); } } diff --git a/src/main/java/org/JesusFreke/dexlib/ShortIntegerField.java b/src/main/java/org/JesusFreke/dexlib/ShortIntegerField.java index 99a607c8..686cb56d 100644 --- a/src/main/java/org/JesusFreke/dexlib/ShortIntegerField.java +++ b/src/main/java/org/JesusFreke/dexlib/ShortIntegerField.java @@ -28,21 +28,16 @@ package org.JesusFreke.dexlib; -import org.JesusFreke.dexlib.util.Output; import org.JesusFreke.dexlib.util.Input; +import org.JesusFreke.dexlib.util.Output; public class ShortIntegerField extends CachedIntegerValueField { - protected int value = 0; - - public ShortIntegerField() { + public ShortIntegerField(String fieldName) { + super(fieldName); } - public ShortIntegerField(int value) { - this.value = value; - } - - public void writeTo(Output out) { - out.writeShort(value); + public ShortIntegerField(int value, String fieldName) { + super(value, fieldName); } public void readFrom(Input in) { @@ -53,18 +48,7 @@ public class ShortIntegerField extends CachedIntegerValueField { return offset + 2; } - /** - * This method returns the short integer value that has been cached. This - * value is either the value that the field was constructed with, the - * value that was read via readFrom, or the value that was - * cached when place was called - * @return the cached value - */ - public int getCachedValue() { - return value; - } - - public void cacheValue(int value) { - this.value = (short)value; + protected void writeValue(Output out) { + out.writeShort(value); } } diff --git a/src/main/java/org/JesusFreke/dexlib/SignedLeb128Field.java b/src/main/java/org/JesusFreke/dexlib/SignedLeb128Field.java index 9047af0f..364311d5 100644 --- a/src/main/java/org/JesusFreke/dexlib/SignedLeb128Field.java +++ b/src/main/java/org/JesusFreke/dexlib/SignedLeb128Field.java @@ -28,22 +28,17 @@ package org.JesusFreke.dexlib; -import org.JesusFreke.dexlib.util.Output; import org.JesusFreke.dexlib.util.Input; import org.JesusFreke.dexlib.util.Leb128Utils; +import org.JesusFreke.dexlib.util.Output; public class SignedLeb128Field extends CachedIntegerValueField { - protected int value; - - public SignedLeb128Field() { + public SignedLeb128Field(String fieldName) { + super(fieldName); } - public SignedLeb128Field(int value) { - this.value = value; - } - - public void writeTo(Output out) { - out.writeSignedLeb128(value); + public SignedLeb128Field(int value, String fieldName) { + super(value, fieldName); } public void readFrom(Input in) { @@ -54,11 +49,7 @@ public class SignedLeb128Field extends CachedIntegerValueField { return offset + Leb128Utils.signedLeb128Size(value); } - public int getCachedValue() { - return value; - } - - public void cacheValue(int value) { - this.value = value; + public void writeValue(Output out) { + out.writeSignedLeb128(value); } } diff --git a/src/main/java/org/JesusFreke/dexlib/StringDataItem.java b/src/main/java/org/JesusFreke/dexlib/StringDataItem.java index 720f6191..da5c5d98 100644 --- a/src/main/java/org/JesusFreke/dexlib/StringDataItem.java +++ b/src/main/java/org/JesusFreke/dexlib/StringDataItem.java @@ -32,8 +32,6 @@ import org.JesusFreke.dexlib.util.ByteArray; import org.JesusFreke.dexlib.util.Utf8Utils; public class StringDataItem extends OffsettedItem implements Comparable { - private final Field[] fields; - private String value = null; private final Leb128Field stringSize; @@ -43,8 +41,8 @@ public class StringDataItem extends OffsettedItem implements Com super(offset); fields = new Field[] { - stringSize = new Leb128Field(), - stringByteArray = new NullTerminatedByteArrayField() + stringSize = new Leb128Field("string_length"), + stringByteArray = new NullTerminatedByteArrayField("data") }; } @@ -54,32 +52,28 @@ public class StringDataItem extends OffsettedItem implements Com this.value = value; fields = new Field[] { - stringSize = new Leb128Field(value.length()), - stringByteArray = new NullTerminatedByteArrayField(Utf8Utils.stringToUtf8Bytes(value)) + stringSize = new Leb128Field(value.length(), "string_length"), + stringByteArray = new NullTerminatedByteArrayField(Utf8Utils.stringToUtf8Bytes(value), "data") }; } - public Field[] getFields() { - return fields; - } - - public int getAlignment() { - return 1; - } - public ItemType getItemType() { return ItemType.TYPE_STRING_DATA_ITEM; } - public String toString() { + public String getStringValue() { if (value == null) { value = Utf8Utils.utf8BytesToString(new ByteArray(((NullTerminatedByteArrayField)fields[1]).value)); } - return value; + return value; + } + + public String getConciseIdentity() { + return "string_data_item: " + getStringValue(); } public int compareTo(StringDataItem o) { - return toString().compareTo(o.toString()); + return getStringValue().compareTo(o.getStringValue()); } } diff --git a/src/main/java/org/JesusFreke/dexlib/StringIdItem.java b/src/main/java/org/JesusFreke/dexlib/StringIdItem.java index 40f01fb9..e292cd0d 100644 --- a/src/main/java/org/JesusFreke/dexlib/StringIdItem.java +++ b/src/main/java/org/JesusFreke/dexlib/StringIdItem.java @@ -29,23 +29,19 @@ package org.JesusFreke.dexlib; public class StringIdItem extends IndexedItem { - private final Field[] fields; - - private final OffsettedItemReference stringData; + private final OffsettedItemReference stringDataReferenceField; public StringIdItem(DexFile dexFile, int index) { super(index); fields = new Field[] { - stringData = new OffsettedItemReference(dexFile.StringDataSection, new IntegerField()) + stringDataReferenceField = new OffsettedItemReference(dexFile.StringDataSection, + new IntegerField(null), "string_data_off") }; } public StringIdItem(DexFile dexFile, StringDataItem stringDataItem) { - super(-1); - - fields = new Field[] { - stringData = new OffsettedItemReference(dexFile, stringDataItem, new IntegerField()) - }; + this(dexFile, -1); + stringDataReferenceField.setReference(stringDataItem); } public StringIdItem(DexFile dexFile, String value) { @@ -56,20 +52,20 @@ public class StringIdItem extends IndexedItem { return 4; } - protected Field[] getFields() { - return fields; - } - public ItemType getItemType() { return ItemType.TYPE_STRING_ID_ITEM; } - public String toString() { - return stringData.getReference().toString(); + public String getConciseIdentity() { + return "string_id_item: " + getStringValue(); + } + + public String getStringValue() { + return stringDataReferenceField.getReference().getStringValue(); } public int compareTo(StringIdItem o) { //sort by the string value - return toString().compareTo(o.toString()); + return getStringValue().compareTo(o.getStringValue()); } } diff --git a/src/main/java/org/JesusFreke/dexlib/TypeIdItem.java b/src/main/java/org/JesusFreke/dexlib/TypeIdItem.java index 794f0698..0a3e015e 100644 --- a/src/main/java/org/JesusFreke/dexlib/TypeIdItem.java +++ b/src/main/java/org/JesusFreke/dexlib/TypeIdItem.java @@ -29,43 +29,35 @@ package org.JesusFreke.dexlib; public class TypeIdItem extends IndexedItem { - private final Field[] fields; - - private final IndexedItemReference type; + private final IndexedItemReference typeDescriptorReferenceField; public TypeIdItem(DexFile dexFile, int index) { super(index); fields = new Field[] { - type = new IndexedItemReference(dexFile.StringIdsSection, new IntegerField()) + typeDescriptorReferenceField = new IndexedItemReference(dexFile.StringIdsSection, + new IntegerField(null), "descriptor_idx") }; } public TypeIdItem(DexFile dexFile, StringIdItem stringIdItem) { - super(-1); - fields = new Field[] { - type = new IndexedItemReference(dexFile, stringIdItem, new IntegerField()) - }; + this(dexFile, -1); + typeDescriptorReferenceField.setReference(stringIdItem); } public TypeIdItem(DexFile dexFile, String value) { - super(-1); - StringDataItem stringDataItem = new StringDataItem(value); - StringIdItem stringIdItem = new StringIdItem(dexFile, stringDataItem); - fields = new Field[] { - type = new IndexedItemReference(dexFile, stringIdItem, new IntegerField()) - }; + this(dexFile, new StringIdItem(dexFile, value)); } protected int getAlignment() { return 4; } - protected Field[] getFields() { - return fields; - } - - public int getWordCount() { - String type = this.toString(); + /** + * Returns the number of 2-byte registers that an instance of this type requires + * @return The number of 2-byte registers that an instance of this type requires + */ + public int getRegisterCount() { + String type = this.getTypeDescriptor(); /** Only the long and double primitive types are 2 words, * everything else is a single word */ @@ -80,17 +72,21 @@ public class TypeIdItem extends IndexedItem { return ItemType.TYPE_TYPE_ID_ITEM; } - public String toString() { - return type.getReference().toString(); + public String getConciseIdentity() { + return "type_id_item: " + getTypeDescriptor(); + } + + public String getTypeDescriptor() { + return typeDescriptorReferenceField.getReference().getStringValue(); } public int compareTo(TypeIdItem o) { //sort by the index of the StringIdItem - return type.compareTo(o.type); + return typeDescriptorReferenceField.compareTo(o.typeDescriptorReferenceField); } public String toShorty() { - String type = toString(); + String type = getTypeDescriptor(); if (type.length() > 1) { return "L"; } else { diff --git a/src/main/java/org/JesusFreke/dexlib/TypeListItem.java b/src/main/java/org/JesusFreke/dexlib/TypeListItem.java index 1a6c0ff6..af951e0e 100644 --- a/src/main/java/org/JesusFreke/dexlib/TypeListItem.java +++ b/src/main/java/org/JesusFreke/dexlib/TypeListItem.java @@ -32,17 +32,20 @@ import java.util.ArrayList; import java.util.List; public class TypeListItem extends OffsettedItem implements Comparable { - private final Field[] fields; private final ArrayList> typeList = new ArrayList>(); + private final ListSizeField sizeField; + private final FieldListField> listField; + public TypeListItem(final DexFile dexFile, int offset) { super(offset); fields = new Field[] { - new ListSizeField(typeList, new IntegerField()), - new FieldListField>(typeList) { + sizeField = new ListSizeField(typeList, new IntegerField("size")), + listField = new FieldListField>(typeList, "type_item") { protected IndexedItemReference make() { - return new IndexedItemReference(dexFile.TypeIdsSection, new ShortIntegerField()); + return new IndexedItemReference(dexFile.TypeIdsSection, + new ShortIntegerField(null), "type_idx"); } } }; @@ -52,7 +55,9 @@ public class TypeListItem extends OffsettedItem implements Compara this(dexFile, 0); for (TypeIdItem typeIdItem: types) { - typeList.add(new IndexedItemReference(dexFile, typeIdItem, new ShortIntegerField())); + IndexedItemReference typeReference = listField.make(); + typeReference.setReference(typeIdItem); + typeList.add(typeReference); } } @@ -66,15 +71,11 @@ public class TypeListItem extends OffsettedItem implements Compara return list; } - public Field[] getFields() { - return fields; - } - - public int getWordCount() { + public int getRegisterCount() { int wordCount = 0; for (IndexedItemReference typeRef: typeList) { TypeIdItem item = typeRef.getReference(); - wordCount += item.getWordCount(); + wordCount += item.getRegisterCount(); } return wordCount; } @@ -91,6 +92,23 @@ public class TypeListItem extends OffsettedItem implements Compara return ItemType.TYPE_TYPE_LIST; } + public String getConciseIdentity() { + return "type_list: " + getTypeListString(); + } + + private String cachedTypeListString = null; + public String getTypeListString() { + if (cachedTypeListString == null) { + StringBuilder sb = new StringBuilder(); + + for (IndexedItemReference typeReference: typeList) { + sb.append(typeReference.getReference().getTypeDescriptor()); + } + cachedTypeListString = sb.toString(); + } + return cachedTypeListString; + } + public int compareTo(TypeListItem o) { if (o == null) { return 1; diff --git a/src/main/java/org/JesusFreke/dexlib/code/Format/Format21c.java b/src/main/java/org/JesusFreke/dexlib/code/Format/Format21c.java index 37925c59..a76e447b 100644 --- a/src/main/java/org/JesusFreke/dexlib/code/Format/Format21c.java +++ b/src/main/java/org/JesusFreke/dexlib/code/Format/Format21c.java @@ -53,7 +53,7 @@ public class Format21c extends Format throw new RuntimeException("The register number must be less than v256"); } - if (opcode == Opcode.NEW_INSTANCE.value && ((TypeIdItem)item).toString().charAt(0) != 'L') { + if (opcode == Opcode.NEW_INSTANCE.value && ((TypeIdItem)item).getTypeDescriptor().charAt(0) != 'L') { throw new RuntimeException("Only class references can be used with the new-instance opcode"); } diff --git a/src/main/java/org/JesusFreke/dexlib/code/Format/Format35c.java b/src/main/java/org/JesusFreke/dexlib/code/Format/Format35c.java index 8e0c47a1..35791ced 100644 --- a/src/main/java/org/JesusFreke/dexlib/code/Format/Format35c.java +++ b/src/main/java/org/JesusFreke/dexlib/code/Format/Format35c.java @@ -69,7 +69,7 @@ public class Format35c extends Format if (opcode == FILLED_NEW_ARRAY.value) { //check data for filled-new-array opcode - String type = ((TypeIdItem)item).toString(); + String type = ((TypeIdItem)item).getTypeDescriptor(); if (type.charAt(0) != '[') { throw new RuntimeException("The type must be an array type"); } @@ -79,7 +79,7 @@ public class Format35c extends Format } else if (opcode >= INVOKE_VIRTUAL.value && opcode <= INVOKE_INTERFACE.value) { //check data for invoke-* opcodes MethodIdItem methodIdItem = (MethodIdItem)item; - if (methodIdItem.getParameterWordCount(opcode == INVOKE_STATIC.value) != regCount) { + if (methodIdItem.getParameterRegisterCount(opcode == INVOKE_STATIC.value) != regCount) { throw new RuntimeException("regCount does not match the number of arguments of the method"); } } else { diff --git a/src/main/java/org/JesusFreke/dexlib/code/Format/Format3rc.java b/src/main/java/org/JesusFreke/dexlib/code/Format/Format3rc.java index 4d92d158..e41875cf 100644 --- a/src/main/java/org/JesusFreke/dexlib/code/Format/Format3rc.java +++ b/src/main/java/org/JesusFreke/dexlib/code/Format/Format3rc.java @@ -72,7 +72,7 @@ public class Format3rc extends Format if (opcode == FILLED_NEW_ARRAY_RANGE.value) { //check data for filled-new-array/range opcode - String type = ((TypeIdItem)item).toString(); + String type = ((TypeIdItem)item).getTypeDescriptor(); if (type.charAt(0) != '[') { throw new RuntimeException("The type must be an array type"); } @@ -82,7 +82,7 @@ public class Format3rc extends Format } else if (opcode >= INVOKE_VIRTUAL_RANGE.value && opcode <= INVOKE_INTERFACE_RANGE.value) { //check data for invoke-*/range opcodes MethodIdItem methodIdItem = (MethodIdItem)item; - if (methodIdItem.getParameterWordCount(opcode == INVOKE_STATIC.value) != regCount) { + if (methodIdItem.getParameterRegisterCount(opcode == INVOKE_STATIC.value) != regCount) { throw new RuntimeException("regCount does not match the number of arguments of the method"); } } else { diff --git a/src/main/java/org/JesusFreke/dexlib/code/Instruction.java b/src/main/java/org/JesusFreke/dexlib/code/Instruction.java index 6a60decd..a6b0e0f8 100644 --- a/src/main/java/org/JesusFreke/dexlib/code/Instruction.java +++ b/src/main/java/org/JesusFreke/dexlib/code/Instruction.java @@ -30,7 +30,7 @@ package org.JesusFreke.dexlib.code; import org.JesusFreke.dexlib.*; import org.JesusFreke.dexlib.util.Input; -import org.JesusFreke.dexlib.util.Output; +import org.JesusFreke.dexlib.util.AnnotatedOutput; public final class Instruction implements Field { private DexFile dexFile; @@ -143,7 +143,8 @@ public final class Instruction implements Field { bytes = in.readBytes(opcode.numBytes); } - public void writeTo(Output out) { + public void writeTo(AnnotatedOutput out) { + out.annotate(bytes.length, "instruction"); if (bytes[0] == 0 && bytes[1] > 0) { //the "special instructions" must be 4 byte aligned out.alignTo(4); diff --git a/src/main/java/org/JesusFreke/dexlib/debug/AdvanceLine.java b/src/main/java/org/JesusFreke/dexlib/debug/AdvanceLine.java index 83b8dc77..bf620340 100644 --- a/src/main/java/org/JesusFreke/dexlib/debug/AdvanceLine.java +++ b/src/main/java/org/JesusFreke/dexlib/debug/AdvanceLine.java @@ -34,15 +34,14 @@ import org.JesusFreke.dexlib.ByteField; import org.JesusFreke.dexlib.SignedLeb128Field; public class AdvanceLine extends CompositeField implements DebugInstruction { - private final Field[] fields; - private final ByteField opcodeField; private final SignedLeb128Field lineDeltaField; public AdvanceLine() { + super("DBG_ADVANCE_LINE"); fields = new Field[] { - opcodeField = new ByteField((byte)0x02), - lineDeltaField = new SignedLeb128Field() + opcodeField = new ByteField((byte)0x02, "opcode"), + lineDeltaField = new SignedLeb128Field("line_diff") }; } @@ -51,10 +50,6 @@ public class AdvanceLine extends CompositeField implements DebugIns lineDeltaField.cacheValue(lineDelta); } - protected Field[] getFields() { - return fields; - } - public byte getOpcode() { return 0x02; } diff --git a/src/main/java/org/JesusFreke/dexlib/debug/AdvancePC.java b/src/main/java/org/JesusFreke/dexlib/debug/AdvancePC.java index 89881346..fd9370cd 100644 --- a/src/main/java/org/JesusFreke/dexlib/debug/AdvancePC.java +++ b/src/main/java/org/JesusFreke/dexlib/debug/AdvancePC.java @@ -31,15 +31,14 @@ package org.JesusFreke.dexlib.debug; import org.JesusFreke.dexlib.*; public class AdvancePC extends CompositeField implements DebugInstruction { - private final Field[] fields; - private final ByteField opcodeField; private final Leb128Field addressDeltaField; public AdvancePC() { + super("DBG_ADVANCE_PC"); fields = new Field[] { - opcodeField = new ByteField((byte)0x01), - addressDeltaField = new Leb128Field() + opcodeField = new ByteField((byte)0x01, "opcode"), + addressDeltaField = new Leb128Field("addr_diff") }; } @@ -48,10 +47,6 @@ public class AdvancePC extends CompositeField implements DebugInstruc addressDeltaField.cacheValue(addressDelta); } - protected Field[] getFields() { - return fields; - } - public byte getOpcode() { return 0x01; } diff --git a/src/main/java/org/JesusFreke/dexlib/debug/EndLocal.java b/src/main/java/org/JesusFreke/dexlib/debug/EndLocal.java index 4fb17a14..11979a6c 100644 --- a/src/main/java/org/JesusFreke/dexlib/debug/EndLocal.java +++ b/src/main/java/org/JesusFreke/dexlib/debug/EndLocal.java @@ -31,15 +31,14 @@ package org.JesusFreke.dexlib.debug; import org.JesusFreke.dexlib.*; public class EndLocal extends CompositeField implements DebugInstruction { - private final Field[] fields; - private final ByteField opcode; private final Leb128Field registerNumber; public EndLocal() { + super("DBG_END_LOCAL"); fields = new Field[] { - opcode = new ByteField((byte)0x05), - registerNumber = new Leb128Field() + opcode = new ByteField((byte)0x05, "opcode"), + registerNumber = new Leb128Field("register_num") }; } @@ -48,10 +47,6 @@ public class EndLocal extends CompositeField implements DebugInstructi this.registerNumber.cacheValue(registerNumber); } - protected Field[] getFields() { - return fields; - } - public byte getOpcode() { return 0x05; } diff --git a/src/main/java/org/JesusFreke/dexlib/debug/EndSequence.java b/src/main/java/org/JesusFreke/dexlib/debug/EndSequence.java index c5cd6c3a..421fec5f 100644 --- a/src/main/java/org/JesusFreke/dexlib/debug/EndSequence.java +++ b/src/main/java/org/JesusFreke/dexlib/debug/EndSequence.java @@ -33,18 +33,13 @@ import org.JesusFreke.dexlib.Field; import org.JesusFreke.dexlib.ByteField; public class EndSequence extends CompositeField implements DebugInstruction { - private final Field[] fields; - public EndSequence() { + super("DBG_END_SEQUENCE"); fields = new Field[] { - new ByteField((byte)0x00) + new ByteField((byte)0x00, "opcode") }; } - protected Field[] getFields() { - return fields; - } - public byte getOpcode() { return 0x00; } diff --git a/src/main/java/org/JesusFreke/dexlib/debug/RestartLocal.java b/src/main/java/org/JesusFreke/dexlib/debug/RestartLocal.java index 8b63f1a3..959981e7 100644 --- a/src/main/java/org/JesusFreke/dexlib/debug/RestartLocal.java +++ b/src/main/java/org/JesusFreke/dexlib/debug/RestartLocal.java @@ -31,15 +31,14 @@ package org.JesusFreke.dexlib.debug; import org.JesusFreke.dexlib.*; public class RestartLocal extends CompositeField implements DebugInstruction { - private final Field[] fields; - private final ByteField opcode; private final Leb128Field registerNumber; public RestartLocal() { + super("DBG_RESTART_LOCAL"); fields = new Field[] { - opcode = new ByteField((byte)0x06), - registerNumber = new Leb128Field() + opcode = new ByteField((byte)0x06, "opcode"), + registerNumber = new Leb128Field("register_num") }; } @@ -48,10 +47,6 @@ public class RestartLocal extends CompositeField implements DebugI this.registerNumber.cacheValue(registerNumber); } - protected Field[] getFields() { - return fields; - } - public byte getOpcode() { return 0x06; } diff --git a/src/main/java/org/JesusFreke/dexlib/debug/SetEpilogueBegin.java b/src/main/java/org/JesusFreke/dexlib/debug/SetEpilogueBegin.java index b7524647..2999bb33 100644 --- a/src/main/java/org/JesusFreke/dexlib/debug/SetEpilogueBegin.java +++ b/src/main/java/org/JesusFreke/dexlib/debug/SetEpilogueBegin.java @@ -33,18 +33,13 @@ import org.JesusFreke.dexlib.Field; import org.JesusFreke.dexlib.ByteField; public class SetEpilogueBegin extends CompositeField implements DebugInstruction { - private final Field[] fields; - public SetEpilogueBegin() { + super("DBG_SET_EPILOGUE_BEGIN"); fields = new Field[] { - new ByteField((byte)0x08) + new ByteField((byte)0x08, "opcode") }; } - protected Field[] getFields() { - return fields; - } - public byte getOpcode() { return 0x08; } diff --git a/src/main/java/org/JesusFreke/dexlib/debug/SetFile.java b/src/main/java/org/JesusFreke/dexlib/debug/SetFile.java index f63fb0ff..f510b62a 100644 --- a/src/main/java/org/JesusFreke/dexlib/debug/SetFile.java +++ b/src/main/java/org/JesusFreke/dexlib/debug/SetFile.java @@ -31,15 +31,15 @@ package org.JesusFreke.dexlib.debug; import org.JesusFreke.dexlib.*; public class SetFile extends CompositeField implements DebugInstruction { - private final Field[] fields; - private final ByteField opcode; private final IndexedItemReference fileName; public SetFile(DexFile dexFile) { + super("DBG_SET_FILE"); fields = new Field[] { - opcode = new ByteField((byte)0x09), - fileName = new IndexedItemReference(dexFile.StringIdsSection, new Leb128p1Field()) + opcode = new ByteField((byte)0x09, "opcode"), + fileName = new IndexedItemReference(dexFile.StringIdsSection, + new Leb128p1Field(null), "name_idx") }; } @@ -48,10 +48,6 @@ public class SetFile extends CompositeField implements DebugInstruction this.fileName.setReference(fileName); } - protected Field[] getFields() { - return fields; - } - public byte getOpcode() { return 0x09; } diff --git a/src/main/java/org/JesusFreke/dexlib/debug/SetPrologueEnd.java b/src/main/java/org/JesusFreke/dexlib/debug/SetPrologueEnd.java index c111029a..43cd02d4 100644 --- a/src/main/java/org/JesusFreke/dexlib/debug/SetPrologueEnd.java +++ b/src/main/java/org/JesusFreke/dexlib/debug/SetPrologueEnd.java @@ -33,18 +33,13 @@ import org.JesusFreke.dexlib.Field; import org.JesusFreke.dexlib.ByteField; public class SetPrologueEnd extends CompositeField implements DebugInstruction { - private final Field[] fields; - public SetPrologueEnd() { + super("DBG_SET_PROLOGUE_END"); fields = new Field[] { - new ByteField((byte)0x07) + new ByteField((byte)0x07, "opcode") }; } - protected Field[] getFields() { - return fields; - } - public byte getOpcode() { return 0x07; } diff --git a/src/main/java/org/JesusFreke/dexlib/debug/SpecialOpcode.java b/src/main/java/org/JesusFreke/dexlib/debug/SpecialOpcode.java index e5dc2079..dda189e9 100644 --- a/src/main/java/org/JesusFreke/dexlib/debug/SpecialOpcode.java +++ b/src/main/java/org/JesusFreke/dexlib/debug/SpecialOpcode.java @@ -33,21 +33,17 @@ import org.JesusFreke.dexlib.Field; import org.JesusFreke.dexlib.ByteField; public class SpecialOpcode extends CompositeField implements DebugInstruction { - private final Field[] fields; - private final byte opcode; public SpecialOpcode(byte opcode) { + super("SPECIAL_OPCODE"); this.opcode = opcode; fields = new Field[] { - new ByteField(opcode) + //TODO: annotate the line and address delta + new ByteField(opcode, "opcode") }; } - protected Field[] getFields() { - return fields; - } - public byte getOpcode() { return opcode; } diff --git a/src/main/java/org/JesusFreke/dexlib/debug/StartLocal.java b/src/main/java/org/JesusFreke/dexlib/debug/StartLocal.java index 779f1bda..32516cbd 100644 --- a/src/main/java/org/JesusFreke/dexlib/debug/StartLocal.java +++ b/src/main/java/org/JesusFreke/dexlib/debug/StartLocal.java @@ -31,19 +31,20 @@ package org.JesusFreke.dexlib.debug; import org.JesusFreke.dexlib.*; public class StartLocal extends CompositeField implements DebugInstruction { - private final Field[] fields; - private final ByteField opcodeField; private final SignedLeb128Field registerNumber; private final IndexedItemReference localName; private final IndexedItemReference localType; public StartLocal(DexFile dexFile) { + super("DBG_START_LOCAL"); fields = new Field[] { - opcodeField = new ByteField((byte)0x03), - registerNumber = new SignedLeb128Field(), - localName = new IndexedItemReference(dexFile.StringIdsSection, new Leb128p1Field()), - localType = new IndexedItemReference(dexFile.TypeIdsSection, new Leb128p1Field()), + opcodeField = new ByteField((byte)0x03, "opcode"), + registerNumber = new SignedLeb128Field("register_num"), + localName = new IndexedItemReference(dexFile.StringIdsSection, + new Leb128p1Field(null), "name_idx"), + localType = new IndexedItemReference(dexFile.TypeIdsSection, + new Leb128p1Field(null), "type_idx"), }; } @@ -54,10 +55,6 @@ public class StartLocal extends CompositeField implements DebugInstr this.localType.setReference(localType); } - protected Field[] getFields() { - return fields; - } - public byte getOpcode() { return 0x03; } diff --git a/src/main/java/org/JesusFreke/dexlib/debug/StartLocalExtended.java b/src/main/java/org/JesusFreke/dexlib/debug/StartLocalExtended.java index 12c4fa05..993e0e92 100644 --- a/src/main/java/org/JesusFreke/dexlib/debug/StartLocalExtended.java +++ b/src/main/java/org/JesusFreke/dexlib/debug/StartLocalExtended.java @@ -31,8 +31,6 @@ package org.JesusFreke.dexlib.debug; import org.JesusFreke.dexlib.*; public class StartLocalExtended extends CompositeField implements DebugInstruction { - private final Field[] fields; - private final ByteField opcodeField; //TODO: signed or unsigned leb? private final SignedLeb128Field registerNumber; @@ -41,12 +39,16 @@ public class StartLocalExtended extends CompositeField imple private final IndexedItemReference signature; public StartLocalExtended(DexFile dexFile) { + super("DBG_START_LOCAL_EXTENDED"); fields = new Field[] { - opcodeField = new ByteField((byte)0x04), - registerNumber = new SignedLeb128Field(), - localName = new IndexedItemReference(dexFile.StringIdsSection, new Leb128p1Field()), - localType = new IndexedItemReference(dexFile.TypeIdsSection, new Leb128p1Field()), - signature = new IndexedItemReference(dexFile.StringIdsSection, new Leb128p1Field()) + opcodeField = new ByteField((byte)0x04, "opcode"), + registerNumber = new SignedLeb128Field("register_num"), + localName = new IndexedItemReference(dexFile.StringIdsSection, + new Leb128p1Field(null), "name_idx"), + localType = new IndexedItemReference(dexFile.TypeIdsSection, + new Leb128p1Field(null), "type_idx"), + signature = new IndexedItemReference(dexFile.StringIdsSection, + new Leb128p1Field(null), "sig_idx") }; } @@ -59,10 +61,6 @@ public class StartLocalExtended extends CompositeField imple this.signature.setReference(signature); } - protected Field[] getFields() { - return fields; - } - public byte getOpcode() { return 0x04; } diff --git a/src/main/java/org/JesusFreke/dexlib/util/AnnotatedOutput.java b/src/main/java/org/JesusFreke/dexlib/util/AnnotatedOutput.java new file mode 100644 index 00000000..d5bb6393 --- /dev/null +++ b/src/main/java/org/JesusFreke/dexlib/util/AnnotatedOutput.java @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.JesusFreke.dexlib.util; + +/** + * Interface for a binary output destination that may be augmented + * with textual annotations. + */ +public interface AnnotatedOutput + extends Output { + /** + * Get whether this instance will actually keep annotations. + * + * @return true iff annotations are being kept + */ + public boolean annotates(); + + /** + * Get whether this instance is intended to keep verbose annotations. + * Annotators may use the result of calling this method to inform their + * annotation activity. + * + * @return true iff annotations are to be verbose + */ + public boolean isVerbose(); + + /** + * Add an annotation for the subsequent output. Any previously + * open annotation will be closed by this call, and the new + * annotation marks all subsequent output until another annotation + * call. + * + * @param msg non-null; the annotation message + */ + public void annotate(String msg); + + /** + * Add an annotation for a specified amount of subsequent + * output. Any previously open annotation will be closed by this + * call. If there is already pending annotation from one or more + * previous calls to this method, the new call "consumes" output + * after all the output covered by the previous calls. + * + * @param amt >= 0; the amount of output for this annotation to + * cover + * @param msg non-null; the annotation message + */ + public void annotate(int amt, String msg); + + /** + * End the most recent annotation. Subsequent output will be unannotated, + * until the next call to {@link #annotate}. + */ + public void endAnnotation(); + + /** + * Get the maximum width of the annotated output. This is advisory: + * Implementations of this interface are encouraged to deal with too-wide + * output, but annotaters are encouraged to attempt to avoid exceeding + * the indicated width. + * + * @return >= 1; the maximum width + */ + public int getAnnotationWidth(); + + public void setIndentAmount(int indentAmount); + public void indent(); + public void deindent(); +} diff --git a/src/main/java/org/JesusFreke/dexlib/util/ByteArrayAnnotatedOutput.java b/src/main/java/org/JesusFreke/dexlib/util/ByteArrayAnnotatedOutput.java new file mode 100644 index 00000000..b4bc3e04 --- /dev/null +++ b/src/main/java/org/JesusFreke/dexlib/util/ByteArrayAnnotatedOutput.java @@ -0,0 +1,669 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.JesusFreke.dexlib.util; + +import java.io.IOException; +import java.io.Writer; +import java.util.ArrayList; + +/** + * Implementation of {@link AnnotatedOutput} which stores the written data + * into a byte[]. + * + *

Note: As per the {@link Output} interface, multi-byte + * writes all use little-endian order.

+ */ +public final class ByteArrayAnnotatedOutput + implements AnnotatedOutput { + /** default size for stretchy instances */ + private static final int DEFAULT_SIZE = 1000; + + /** + * whether the instance is stretchy, that is, whether its array + * may be resized to increase capacity + */ + private final boolean stretchy; + + /** non-null; the data itself */ + private byte[] data; + + /** >= 0; current output cursor */ + private int cursor; + + /** whether annotations are to be verbose */ + private boolean verbose; + + /** + * null-ok; list of annotations, or null if this instance + * isn't keeping them + */ + private ArrayList annotations; + + /** >= 40 (if used); the desired maximum annotation width */ + private int annotationWidth; + + /** + * >= 8 (if used); the number of bytes of hex output to use + * in annotations + */ + private int hexCols; + + private int currentIndent = 0; + private int indentAmount = 2; + + /** + * Constructs an instance with a fixed maximum size. Note that the + * given array is the only one that will be used to store data. In + * particular, no reallocation will occur in order to expand the + * capacity of the resulting instance. Also, the constructed + * instance does not keep annotations by default. + * + * @param data non-null; data array to use for output + */ + public ByteArrayAnnotatedOutput(byte[] data) { + this(data, false); + } + + /** + * Constructs a "stretchy" instance. The underlying array may be + * reallocated. The constructed instance does not keep annotations + * by default. + */ + public ByteArrayAnnotatedOutput() { + this(new byte[DEFAULT_SIZE], true); + } + + /** + * Internal constructor. + * + * @param data non-null; data array to use for output + * @param stretchy whether the instance is to be stretchy + */ + private ByteArrayAnnotatedOutput(byte[] data, boolean stretchy) { + if (data == null) { + throw new NullPointerException("data == null"); + } + + this.stretchy = stretchy; + this.data = data; + this.cursor = 0; + this.verbose = false; + this.annotations = null; + this.annotationWidth = 0; + this.hexCols = 0; + } + + /** + * Gets the underlying byte[] of this instance, which + * may be larger than the number of bytes written + * + * @see #toByteArray + * + * @return non-null; the byte[] + */ + public byte[] getArray() { + return data; + } + + /** + * Constructs and returns a new byte[] that contains + * the written contents exactly (that is, with no extra unwritten + * bytes at the end). + * + * @see #getArray + * + * @return non-null; an appropriately-constructed array + */ + public byte[] toByteArray() { + byte[] result = new byte[cursor]; + System.arraycopy(data, 0, result, 0, cursor); + return result; + } + + /** {@inheritDoc} */ + public int getCursor() { + return cursor; + } + + /** {@inheritDoc} */ + public void assertCursor(int expectedCursor) { + if (cursor != expectedCursor) { + throw new ExceptionWithContext("expected cursor " + + expectedCursor + "; actual value: " + cursor); + } + } + + /** {@inheritDoc} */ + public void writeByte(int value) { + int writeAt = cursor; + int end = writeAt + 1; + + if (stretchy) { + ensureCapacity(end); + } else if (end > data.length) { + throwBounds(); + return; + } + + data[writeAt] = (byte) value; + cursor = end; + } + + /** {@inheritDoc} */ + public void writeShort(int value) { + int writeAt = cursor; + int end = writeAt + 2; + + if (stretchy) { + ensureCapacity(end); + } else if (end > data.length) { + throwBounds(); + return; + } + + data[writeAt] = (byte) value; + data[writeAt + 1] = (byte) (value >> 8); + cursor = end; + } + + /** {@inheritDoc} */ + public void writeInt(int value) { + int writeAt = cursor; + int end = writeAt + 4; + + if (stretchy) { + ensureCapacity(end); + } else if (end > data.length) { + throwBounds(); + return; + } + + data[writeAt] = (byte) value; + data[writeAt + 1] = (byte) (value >> 8); + data[writeAt + 2] = (byte) (value >> 16); + data[writeAt + 3] = (byte) (value >> 24); + cursor = end; + } + + /** {@inheritDoc} */ + public void writeLong(long value) { + int writeAt = cursor; + int end = writeAt + 8; + + if (stretchy) { + ensureCapacity(end); + } else if (end > data.length) { + throwBounds(); + return; + } + + int half = (int) value; + data[writeAt] = (byte) half; + data[writeAt + 1] = (byte) (half >> 8); + data[writeAt + 2] = (byte) (half >> 16); + data[writeAt + 3] = (byte) (half >> 24); + + half = (int) (value >> 32); + data[writeAt + 4] = (byte) half; + data[writeAt + 5] = (byte) (half >> 8); + data[writeAt + 6] = (byte) (half >> 16); + data[writeAt + 7] = (byte) (half >> 24); + + cursor = end; + } + + /** {@inheritDoc} */ + public int writeUnsignedLeb128(int value) { + int remaining = value >> 7; + int count = 0; + + while (remaining != 0) { + writeByte((value & 0x7f) | 0x80); + value = remaining; + remaining >>= 7; + count++; + } + + writeByte(value & 0x7f); + return count + 1; + } + + /** {@inheritDoc} */ + public int writeSignedLeb128(int value) { + int remaining = value >> 7; + int count = 0; + boolean hasMore = true; + int end = ((value & Integer.MIN_VALUE) == 0) ? 0 : -1; + + while (hasMore) { + hasMore = (remaining != end) + || ((remaining & 1) != ((value >> 6) & 1)); + + writeByte((value & 0x7f) | (hasMore ? 0x80 : 0)); + value = remaining; + remaining >>= 7; + count++; + } + + return count; + } + + /** {@inheritDoc} */ + public void write(ByteArray bytes) { + int blen = bytes.size(); + int writeAt = cursor; + int end = writeAt + blen; + + if (stretchy) { + ensureCapacity(end); + } else if (end > data.length) { + throwBounds(); + return; + } + + bytes.getBytes(data, writeAt); + cursor = end; + } + + /** {@inheritDoc} */ + public void write(byte[] bytes, int offset, int length) { + int writeAt = cursor; + int end = writeAt + length; + int bytesEnd = offset + length; + + // twos-complement math trick: ((x < 0) || (y < 0)) <=> ((x|y) < 0) + if (((offset | length | end) < 0) || (bytesEnd > bytes.length)) { + throw new IndexOutOfBoundsException("bytes.length " + + bytes.length + "; " + + offset + "..!" + end); + } + + if (stretchy) { + ensureCapacity(end); + } else if (end > data.length) { + throwBounds(); + return; + } + + System.arraycopy(bytes, offset, data, writeAt, length); + cursor = end; + } + + /** {@inheritDoc} */ + public void write(byte[] bytes) { + write(bytes, 0, bytes.length); + } + + /** {@inheritDoc} */ + public void writeZeroes(int count) { + if (count < 0) { + throw new IllegalArgumentException("count < 0"); + } + + int end = cursor + count; + + if (stretchy) { + ensureCapacity(end); + } else if (end > data.length) { + throwBounds(); + return; + } + + /* + * There is no need to actually write zeroes, since the array is + * already preinitialized with zeroes. + */ + + cursor = end; + } + + /** {@inheritDoc} */ + public void alignTo(int alignment) { + int mask = alignment - 1; + + if ((alignment < 0) || ((mask & alignment) != 0)) { + throw new IllegalArgumentException("bogus alignment"); + } + + int end = (cursor + mask) & ~mask; + + if (stretchy) { + ensureCapacity(end); + } else if (end > data.length) { + throwBounds(); + return; + } + + /* + * There is no need to actually write zeroes, since the array is + * already preinitialized with zeroes. + */ + + cursor = end; + } + + /** {@inheritDoc} */ + public boolean annotates() { + return (annotations != null); + } + + /** {@inheritDoc} */ + public boolean isVerbose() { + return verbose; + } + + /** {@inheritDoc} */ + public void annotate(String msg) { + if (annotations == null) { + return; + } + + endAnnotation(); + annotations.add(new Annotation(cursor, msg, currentIndent)); + } + + public void indent() { + currentIndent++; + } + + public void deindent() { + currentIndent--; + if (currentIndent < 0) { + currentIndent = 0; + } + } + + public void setIndentAmount(int indentAmount) { + this.indentAmount = indentAmount; + } + + /** {@inheritDoc} */ + public void annotate(int amt, String msg) { + if (annotations == null) { + return; + } + + endAnnotation(); + + int asz = annotations.size(); + int lastEnd = (asz == 0) ? 0 : annotations.get(asz - 1).getEnd(); + int startAt; + + if (lastEnd <= cursor) { + startAt = cursor; + } else { + startAt = lastEnd; + } + + annotations.add(new Annotation(startAt, startAt + amt, msg, currentIndent)); + } + + /** {@inheritDoc} */ + public void endAnnotation() { + if (annotations == null) { + return; + } + + int sz = annotations.size(); + + if (sz != 0) { + annotations.get(sz - 1).setEndIfUnset(cursor); + } + } + + /** {@inheritDoc} */ + public int getAnnotationWidth() { + int leftWidth = 8 + (hexCols * 2) + (hexCols / 2); + + return annotationWidth - leftWidth; + } + + /** + * Indicates that this instance should keep annotations. This method may + * be called only once per instance, and only before any data has been + * written to the it. + * + * @param annotationWidth >= 40; the desired maximum annotation width + * @param verbose whether or not to indicate verbose annotations + */ + public void enableAnnotations(int annotationWidth, boolean verbose) { + if ((annotations != null) || (cursor != 0)) { + throw new RuntimeException("cannot enable annotations"); + } + + if (annotationWidth < 40) { + throw new IllegalArgumentException("annotationWidth < 40"); + } + + int hexCols = (((annotationWidth - 7) / 15) + 1) & ~1; + if (hexCols < 6) { + hexCols = 6; + } else if (hexCols > 10) { + hexCols = 10; + } + + this.annotations = new ArrayList(1000); + this.annotationWidth = annotationWidth; + this.hexCols = hexCols; + this.verbose = verbose; + } + + /** + * Finishes up annotation processing. This closes off any open + * annotations and removes annotations that don't refer to written + * data. + */ + public void finishAnnotating() { + // Close off the final annotation, if any. + endAnnotation(); + + // Remove annotations that refer to unwritten data. + if (annotations != null) { + int asz = annotations.size(); + while (asz > 0) { + Annotation last = annotations.get(asz - 1); + if (last.getStart() > cursor) { + annotations.remove(asz - 1); + asz--; + } else if (last.getEnd() > cursor) { + last.setEnd(cursor); + break; + } else { + break; + } + } + } + } + + /** + * Writes the annotated content of this instance to the given writer. + * + * @param out non-null; where to write to + */ + public void writeAnnotationsTo(Writer out) throws IOException { + int width2 = getAnnotationWidth(); + int width1 = annotationWidth - width2 - 1; + + StringBuilder padding = new StringBuilder(); + for (int i=0; i<1000; i++) { + padding.append(' '); + } + + TwoColumnOutput twoc = new TwoColumnOutput(out, width1, width2, "|"); + Writer left = twoc.getLeft(); + Writer right = twoc.getRight(); + int leftAt = 0; // left-hand byte output cursor + int rightAt = 0; // right-hand annotation index + int rightSz = annotations.size(); + + while ((leftAt < cursor) && (rightAt < rightSz)) { + Annotation a = annotations.get(rightAt); + int start = a.getStart(); + int end; + String text; + + if (leftAt < start) { + // This is an area with no annotation. + end = start; + start = leftAt; + text = ""; + } else { + // This is an area with an annotation. + end = a.getEnd(); + text = padding.substring(0, a.getIndent() * this.indentAmount) + a.getText(); + rightAt++; + } + + left.write(Hex.dump(data, start, end - start, start, hexCols, 6)); + right.write(text); + twoc.flush(); + leftAt = end; + } + + if (leftAt < cursor) { + // There is unannotated output at the end. + left.write(Hex.dump(data, leftAt, cursor - leftAt, leftAt, + hexCols, 6)); + } + + while (rightAt < rightSz) { + // There are zero-byte annotations at the end. + right.write(annotations.get(rightAt).getText()); + rightAt++; + } + + twoc.flush(); + } + + /** + * Throws the excpetion for when an attempt is made to write past the + * end of the instance. + */ + private static void throwBounds() { + throw new IndexOutOfBoundsException("attempt to write past the end"); + } + + /** + * Reallocates the underlying array if necessary. Calls to this method + * should be guarded by a test of {@link #stretchy}. + * + * @param desiredSize >= 0; the desired minimum total size of the array + */ + private void ensureCapacity(int desiredSize) { + if (data.length < desiredSize) { + byte[] newData = new byte[desiredSize * 2 + 1000]; + System.arraycopy(data, 0, newData, 0, cursor); + data = newData; + } + } + + /** + * Annotation on output. + */ + private static class Annotation { + /** >= 0; start of annotated range (inclusive) */ + private final int start; + + /** + * >= 0; end of annotated range (exclusive); + * Integer.MAX_VALUE if unclosed + */ + private int end; + + /** non-null; annotation text */ + private final String text; + + private int indent; + + /** + * Constructs an instance. + * + * @param start >= 0; start of annotated range + * @param end >= start; end of annotated range (exclusive) or + * Integer.MAX_VALUE if unclosed + * @param text non-null; annotation text + */ + public Annotation(int start, int end, String text, int indent) { + this.start = start; + this.end = end; + this.text = text; + this.indent = indent; + } + + /** + * Constructs an instance. It is initally unclosed. + * + * @param start >= 0; start of annotated range + * @param text non-null; annotation text + */ + public Annotation(int start, String text, int indent) { + this(start, Integer.MAX_VALUE, text, indent); + } + + /** + * Sets the end as given, but only if the instance is unclosed; + * otherwise, do nothing. + * + * @param end >= start; the end + */ + public void setEndIfUnset(int end) { + if (this.end == Integer.MAX_VALUE) { + this.end = end; + } + } + + /** + * Sets the end as given. + * + * @param end >= start; the end + */ + public void setEnd(int end) { + this.end = end; + } + + /** + * Gets the start. + * + * @return the start + */ + public int getStart() { + return start; + } + + /** + * Gets the end. + * + * @return the end + */ + public int getEnd() { + return end; + } + + /** + * Gets the text. + * + * @return non-null; the text + */ + public String getText() { + return text; + } + + public int getIndent() { + return indent; + } + } +} diff --git a/src/main/java/org/JesusFreke/dexlib/util/IndentingWriter.java b/src/main/java/org/JesusFreke/dexlib/util/IndentingWriter.java new file mode 100644 index 00000000..df808d78 --- /dev/null +++ b/src/main/java/org/JesusFreke/dexlib/util/IndentingWriter.java @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.JesusFreke.dexlib.util; + +import java.io.FilterWriter; +import java.io.IOException; +import java.io.Writer; + +/** + * Writer that wraps another writer and passes width-limited and + * optionally-prefixed output to its subordinate. When lines are + * wrapped they are automatically indented based on the start of the + * line. + */ +public final class IndentingWriter extends FilterWriter { + /** null-ok; optional prefix for every line */ + private final String prefix; + + /** > 0; the maximum output width */ + private final int width; + + /** > 0; the maximum indent */ + private final int maxIndent; + + /** >= 0; current output column (zero-based) */ + private int column; + + /** whether indent spaces are currently being collected */ + private boolean collectingIndent; + + /** >= 0; current indent amount */ + private int indent; + + /** + * Constructs an instance. + * + * @param out non-null; writer to send final output to + * @param width >= 0; the maximum output width (not including + * prefix), or 0 for no maximum + * @param prefix non-null; the prefix for each line + */ + public IndentingWriter(Writer out, int width, String prefix) { + super(out); + + if (out == null) { + throw new NullPointerException("out == null"); + } + + if (width < 0) { + throw new IllegalArgumentException("width < 0"); + } + + if (prefix == null) { + throw new NullPointerException("prefix == null"); + } + + this.width = (width != 0) ? width : Integer.MAX_VALUE; + this.maxIndent = width >> 1; + this.prefix = (prefix.length() == 0) ? null : prefix; + + bol(); + } + + /** + * Constructs a no-prefix instance. + * + * @param out non-null; writer to send final output to + * @param width >= 0; the maximum output width (not including + * prefix), or 0 for no maximum + */ + public IndentingWriter(Writer out, int width) { + this(out, width, ""); + } + + /** {@inheritDoc} */ + @Override + public void write(int c) throws IOException { + synchronized (lock) { + if (collectingIndent) { + if (c == ' ') { + indent++; + if (indent >= maxIndent) { + indent = maxIndent; + collectingIndent = false; + } + } else { + collectingIndent = false; + } + } + + if ((column == width) && (c != '\n')) { + out.write('\n'); + column = 0; + /* + * Note: No else, so this should fall through to the next + * if statement. + */ + } + + if (column == 0) { + if (prefix != null) { + out.write(prefix); + } + + if (!collectingIndent) { + for (int i = 0; i < indent; i++) { + out.write(' '); + } + column = indent; + } + } + + out.write(c); + + if (c == '\n') { + bol(); + } else { + column++; + } + } + } + + /** {@inheritDoc} */ + @Override + public void write(char[] cbuf, int off, int len) throws IOException { + synchronized (lock) { + while (len > 0) { + write(cbuf[off]); + off++; + len--; + } + } + } + + /** {@inheritDoc} */ + @Override + public void write(String str, int off, int len) throws IOException { + synchronized (lock) { + while (len > 0) { + write(str.charAt(off)); + off++; + len--; + } + } + } + + /** + * Indicates that output is at the beginning of a line. + */ + private void bol() { + column = 0; + collectingIndent = (maxIndent != 0); + indent = 0; + } +} diff --git a/src/main/java/org/JesusFreke/dexlib/util/TwoColumnOutput.java b/src/main/java/org/JesusFreke/dexlib/util/TwoColumnOutput.java new file mode 100644 index 00000000..01c209d5 --- /dev/null +++ b/src/main/java/org/JesusFreke/dexlib/util/TwoColumnOutput.java @@ -0,0 +1,254 @@ +/* + * Copyright (C) 2007 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.JesusFreke.dexlib.util; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.StringWriter; +import java.io.Writer; + +/** + * Class that takes a combined output destination and provides two + * output writers, one of which ends up writing to the left column and + * one which goes on the right. + */ +public final class TwoColumnOutput { + /** non-null; underlying writer for final output */ + private final Writer out; + + /** > 0; the left column width */ + private final int leftWidth; + + /** non-null; pending left column output */ + private final StringBuffer leftBuf; + + /** non-null; pending right column output */ + private final StringBuffer rightBuf; + + /** non-null; left column writer */ + private final IndentingWriter leftColumn; + + /** non-null; right column writer */ + private final IndentingWriter rightColumn; + + /** + * Turns the given two strings (with widths) and spacer into a formatted + * two-column string. + * + * @param s1 non-null; first string + * @param width1 > 0; width of the first column + * @param spacer non-null; spacer string + * @param s2 non-null; second string + * @param width2 > 0; width of the second column + * @return non-null; an appropriately-formatted string + */ + public static String toString(String s1, int width1, String spacer, + String s2, int width2) { + int len1 = s1.length(); + int len2 = s2.length(); + + StringWriter sw = new StringWriter((len1 + len2) * 3); + TwoColumnOutput twoOut = + new TwoColumnOutput(sw, width1, width2, spacer); + + try { + twoOut.getLeft().write(s1); + twoOut.getRight().write(s2); + } catch (IOException ex) { + throw new RuntimeException("shouldn't happen", ex); + } + + twoOut.flush(); + return sw.toString(); + } + + /** + * Constructs an instance. + * + * @param out non-null; writer to send final output to + * @param leftWidth > 0; width of the left column, in characters + * @param rightWidth > 0; width of the right column, in characters + * @param spacer non-null; spacer string to sit between the two columns + */ + public TwoColumnOutput(Writer out, int leftWidth, int rightWidth, + String spacer) { + if (out == null) { + throw new NullPointerException("out == null"); + } + + if (leftWidth < 1) { + throw new IllegalArgumentException("leftWidth < 1"); + } + + if (rightWidth < 1) { + throw new IllegalArgumentException("rightWidth < 1"); + } + + if (spacer == null) { + throw new NullPointerException("spacer == null"); + } + + StringWriter leftWriter = new StringWriter(1000); + StringWriter rightWriter = new StringWriter(1000); + + this.out = out; + this.leftWidth = leftWidth; + this.leftBuf = leftWriter.getBuffer(); + this.rightBuf = rightWriter.getBuffer(); + this.leftColumn = new IndentingWriter(leftWriter, leftWidth); + this.rightColumn = + new IndentingWriter(rightWriter, rightWidth, spacer); + } + + /** + * Constructs an instance. + * + * @param out non-null; stream to send final output to + * @param leftWidth >= 1; width of the left column, in characters + * @param rightWidth >= 1; width of the right column, in characters + * @param spacer non-null; spacer string to sit between the two columns + */ + public TwoColumnOutput(OutputStream out, int leftWidth, int rightWidth, + String spacer) { + this(new OutputStreamWriter(out), leftWidth, rightWidth, spacer); + } + + /** + * Gets the writer to use to write to the left column. + * + * @return non-null; the left column writer + */ + public Writer getLeft() { + return leftColumn; + } + + /** + * Gets the writer to use to write to the right column. + * + * @return non-null; the right column writer + */ + public Writer getRight() { + return rightColumn; + } + + /** + * Flushes the output. If there are more lines of pending output in one + * column, then the other column will get filled with blank lines. + */ + public void flush() { + try { + appendNewlineIfNecessary(leftBuf, leftColumn); + appendNewlineIfNecessary(rightBuf, rightColumn); + outputFullLines(); + flushLeft(); + flushRight(); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + } + + /** + * Outputs to the final destination as many full line pairs as + * there are in the pending output, removing those lines from + * their respective buffers. This method terminates when at + * least one of the two column buffers is empty. + */ + private void outputFullLines() throws IOException { + for (;;) { + int leftLen = leftBuf.indexOf("\n"); + if (leftLen < 0) { + return; + } + + int rightLen = rightBuf.indexOf("\n"); + if (rightLen < 0) { + return; + } + + if (leftLen != 0) { + out.write(leftBuf.substring(0, leftLen)); + } + + if (rightLen != 0) { + writeSpaces(out, leftWidth - leftLen); + out.write(rightBuf.substring(0, rightLen)); + } + + out.write('\n'); + + leftBuf.delete(0, leftLen + 1); + rightBuf.delete(0, rightLen + 1); + } + } + + /** + * Flushes the left column buffer, printing it and clearing the buffer. + * If the buffer is already empty, this does nothing. + */ + private void flushLeft() throws IOException { + appendNewlineIfNecessary(leftBuf, leftColumn); + + while (leftBuf.length() != 0) { + rightColumn.write('\n'); + outputFullLines(); + } + } + + /** + * Flushes the right column buffer, printing it and clearing the buffer. + * If the buffer is already empty, this does nothing. + */ + private void flushRight() throws IOException { + appendNewlineIfNecessary(rightBuf, rightColumn); + + while (rightBuf.length() != 0) { + leftColumn.write('\n'); + outputFullLines(); + } + } + + /** + * Appends a newline to the given buffer via the given writer, but + * only if it isn't empty and doesn't already end with one. + * + * @param buf non-null; the buffer in question + * @param out non-null; the writer to use + */ + private static void appendNewlineIfNecessary(StringBuffer buf, + Writer out) + throws IOException { + int len = buf.length(); + + if ((len != 0) && (buf.charAt(len - 1) != '\n')) { + out.write('\n'); + } + } + + /** + * Writes the given number of spaces to the given writer. + * + * @param out non-null; where to write + * @param amt >= 0; the number of spaces to write + */ + private static void writeSpaces(Writer out, int amt) throws IOException { + while (amt > 0) { + out.write(' '); + amt--; + } + } +} diff --git a/src/main/java/org/JesusFreke/smali/smali.java b/src/main/java/org/JesusFreke/smali/smali.java index 215564a0..ae7f758f 100644 --- a/src/main/java/org/JesusFreke/smali/smali.java +++ b/src/main/java/org/JesusFreke/smali/smali.java @@ -30,6 +30,7 @@ package org.JesusFreke.smali; import org.JesusFreke.dexlib.DexFile; import org.JesusFreke.dexlib.util.ByteArrayOutput; +import org.JesusFreke.dexlib.util.ByteArrayAnnotatedOutput; import org.antlr.runtime.ANTLRInputStream; import org.antlr.runtime.CommonTokenStream; import org.antlr.runtime.Token; @@ -38,6 +39,7 @@ import org.antlr.runtime.tree.CommonTreeNodeStream; import java.io.FileInputStream; import java.io.FileOutputStream; +import java.io.FileWriter; public class smali { @@ -70,7 +72,8 @@ public class smali dexFile.place(); try { - ByteArrayOutput out = new ByteArrayOutput(); + ByteArrayAnnotatedOutput out = new ByteArrayAnnotatedOutput(); + out.enableAnnotations(120, true); dexFile.writeTo(out); byte[] bytes = out.toByteArray(); @@ -78,6 +81,17 @@ public class smali DexFile.calcSignature(bytes); DexFile.calcChecksum(bytes); + + out.finishAnnotating(); + + + + FileWriter fileWriter = new FileWriter("classes.dump"); + + out.writeAnnotationsTo(fileWriter); + fileWriter.flush(); + fileWriter.close(); + FileOutputStream fileOutputStream = new FileOutputStream("classes.dex"); fileOutputStream.write(bytes); diff --git a/src/test/java/TryListBuilderTest.java b/src/test/java/TryListBuilderTest.java index 4c2e83e7..e937fb61 100644 --- a/src/test/java/TryListBuilderTest.java +++ b/src/test/java/TryListBuilderTest.java @@ -70,7 +70,7 @@ public class TryListBuilderTest CodeItem.EncodedTypeAddrPair typeAddrPair = encodedCatchHandler.getHandler(i); Handler handler = handlers[i]; - Assert.assertTrue(typeAddrPair.getType().toString().compareTo(handler.type) == 0); + Assert.assertTrue(typeAddrPair.getTypeReferenceField().toString().compareTo(handler.type) == 0); Assert.assertTrue(typeAddrPair.getHandlerAddress() == handler.handlerAddress); } }