diff --git a/smali-integration-tests/src/test/smali/junit-tests/InstructionTests/Format31t/Format31t.smali b/smali-integration-tests/src/test/smali/junit-tests/InstructionTests/Format31t/Format31t.smali index 1e518f45..fef7bcfb 100644 --- a/smali-integration-tests/src/test/smali/junit-tests/InstructionTests/Format31t/Format31t.smali +++ b/smali-integration-tests/src/test/smali/junit-tests/InstructionTests/Format31t/Format31t.smali @@ -81,7 +81,7 @@ Label13: return-void PackedSwitch: - .packed-switch switch: 10 + .packed-switch 10 Label10: Label11: Label12: @@ -120,7 +120,7 @@ Label99: return-void SparseSwitch: - .sparse-switch switch: + .sparse-switch 10 -> Label10: 13 -> Label13: 15 -> Label15: diff --git a/smali-integration-tests/src/test/smali/junit-tests/SpecialInstructionPaddingTest/SpecialInstructionPaddingTest.smali b/smali-integration-tests/src/test/smali/junit-tests/SpecialInstructionPaddingTest/SpecialInstructionPaddingTest.smali index b5736c73..1291cb9f 100644 --- a/smali-integration-tests/src/test/smali/junit-tests/SpecialInstructionPaddingTest/SpecialInstructionPaddingTest.smali +++ b/smali-integration-tests/src/test/smali/junit-tests/SpecialInstructionPaddingTest/SpecialInstructionPaddingTest.smali @@ -33,7 +33,6 @@ const v0, 12 -switch: packed-switch v0, PackedSwitch: Label10: @@ -58,7 +57,7 @@ Label13: nop PackedSwitch: - .packed-switch switch: 10 + .packed-switch 10 Label10: Label11: Label12: diff --git a/smali/src/main/antlr3/org/jf/smali/smaliLexer.g b/smali/src/main/antlr3/org/jf/smali/smaliLexer.g index 998c1ca0..c4a3f3f2 100644 --- a/smali/src/main/antlr3/org/jf/smali/smaliLexer.g +++ b/smali/src/main/antlr3/org/jf/smali/smaliLexer.g @@ -452,8 +452,6 @@ ARRAY_DATA_PHRASE PACKED_SWITCH_PHRASE : PACKED_SWITCH_DIRECTIVE_EMIT - WS - (LABEL_EMIT | OFFSET_EMIT) WS FIXED_32BIT_LITERAL_EMITCHILD (WSC (LABEL_EMIT | OFFSET_EMIT))* @@ -462,12 +460,10 @@ PACKED_SWITCH_PHRASE SPARSE_SWITCH_PHRASE : SPARSE_SWITCH_DIRECTIVE_EMIT - WS - (LABEL_EMIT | OFFSET_EMIT) (WSC FIXED_32BIT_LITERAL_EMITCHILD WS? '->' WS? (LABEL_EMIT | OFFSET_EMIT))* - WSC + WSC? END_SPARSE_SWITCH_DIRECTIVE_EMIT; - + REGISTERS_PHRASE : REGISTERS_DIRECTIVE_EMIT WS diff --git a/smali/src/main/antlr3/org/jf/smali/smaliParser.g b/smali/src/main/antlr3/org/jf/smali/smaliParser.g index e18ae4ba..c79b1136 100644 --- a/smali/src/main/antlr3/org/jf/smali/smaliParser.g +++ b/smali/src/main/antlr3/org/jf/smali/smaliParser.g @@ -63,13 +63,15 @@ tokens { I_ARRAY_ELEMENT_SIZE; I_ARRAY_ELEMENTS; I_PACKED_SWITCH_START_KEY; - I_PACKED_SWITCH_BASE_OFFSET; I_PACKED_SWITCH_TARGET_COUNT; I_PACKED_SWITCH_TARGETS; - I_SPARSE_SWITCH_BASE_OFFSET; + I_PACKED_SWITCH_DECLARATION; + I_PACKED_SWITCH_DECLARATIONS; I_SPARSE_SWITCH_KEYS; I_SPARSE_SWITCH_TARGET_COUNT; I_SPARSE_SWITCH_TARGETS; + I_SPARSE_SWITCH_DECLARATION; + I_SPARSE_SWITCH_DECLARATIONS; I_ADDRESS; I_CATCH; I_CATCHES; @@ -153,6 +155,14 @@ import org.jf.dexlib.code.Format.*; public String getErrorHeader(RecognitionException e) { return getSourceName()+"["+ e.line+","+e.charPositionInLine+"]"; } + + private CommonTree buildTree(int type, String text, List children) { + CommonTree root = new CommonTree(new CommonToken(type, text)); + for (CommonTree child: children) { + root.addChild(child); + } + return root; + } } @@ -235,10 +245,17 @@ fully_qualified_field -> reference_type_descriptor MEMBER_NAME nonvoid_type_descriptor; statements_and_directives - scope {boolean hasRegistersDirective;} + scope + { + boolean hasRegistersDirective; + List packedSwitchDeclarations; + List sparseSwitchDeclarations; + } : { $method::currentAddress = 0; $statements_and_directives::hasRegistersDirective = false; + $statements_and_directives::packedSwitchDeclarations = new ArrayList(); + $statements_and_directives::sparseSwitchDeclarations = new ArrayList(); } ( instruction {$method::currentAddress += $instruction.size/2;} | {!$statements_and_directives::hasRegistersDirective}?=> registers_directive {$statements_and_directives::hasRegistersDirective = true;} @@ -250,6 +267,8 @@ statements_and_directives )* -> ^(I_REGISTERS registers_directive?) ^(I_LABELS label*) + {buildTree(I_PACKED_SWITCH_DECLARATIONS, "I_PACKED_SWITCH_DECLARATIONS", $statements_and_directives::packedSwitchDeclarations)} + {buildTree(I_SPARSE_SWITCH_DECLARATIONS, "I_SPARSE_SWITCH_DECLARATIONS", $statements_and_directives::sparseSwitchDeclarations)} ^(I_STATEMENTS instruction*) ^(I_CATCHES catch_directive*) ^(I_PARAMETERS parameter_directive*) @@ -389,8 +408,23 @@ instruction returns [int size] INSTRUCTION_FORMAT31i REGISTER fixed_32bit_literal {$size = Format.Format31i.size;} -> ^(I_STATEMENT_FORMAT31i[$start, "I_STATEMENT_FORMAT31i"] INSTRUCTION_FORMAT31i REGISTER fixed_32bit_literal) | //e.g. fill-array-data v0, ArrayData: - INSTRUCTION_FORMAT31t REGISTER (LABEL | OFFSET) {$size = Format.Format31t.size;} - -> ^(I_STATEMENT_FORMAT31t[$start, "I_STATEMENT_FORMAT31t"] INSTRUCTION_FORMAT31t REGISTER LABEL? OFFSET?) + INSTRUCTION_FORMAT31t REGISTER offset_or_label {$size = Format.Format31t.size;} + { + if ($INSTRUCTION_FORMAT31t.text.equals("packed-switch")) { + CommonTree root = new CommonTree(new CommonToken(I_PACKED_SWITCH_DECLARATION, "I_PACKED_SWITCH_DECLARATION")); + CommonTree address = new CommonTree(new CommonToken(I_ADDRESS, Integer.toString($method::currentAddress))); + root.addChild(address); + root.addChild($offset_or_label.tree.dupNode()); + $statements_and_directives::packedSwitchDeclarations.add(root); + } else if ($INSTRUCTION_FORMAT31t.text.equals("sparse-switch")) { + CommonTree root = new CommonTree(new CommonToken(I_SPARSE_SWITCH_DECLARATION, "I_SPARSE_SWITCH_DECLARATION")); + CommonTree address = new CommonTree(new CommonToken(I_ADDRESS, Integer.toString($method::currentAddress))); + root.addChild(address); + root.addChild($offset_or_label.tree.dupNode()); + $statements_and_directives::sparseSwitchDeclarations.add(root); + } + } + -> ^(I_STATEMENT_FORMAT31t[$start, "I_STATEMENT_FORMAT31t"] INSTRUCTION_FORMAT31t REGISTER offset_or_label) | //e.g. move/16 v4567, v1234 INSTRUCTION_FORMAT32x REGISTER REGISTER {$size = Format.Format32x.size;} -> ^(I_STATEMENT_FORMAT32x[$start, "I_STATEMENT_FORMAT32x"] INSTRUCTION_FORMAT32x REGISTER REGISTER) @@ -439,9 +473,7 @@ instruction returns [int size] $size = 0; } } - - base_offset = offset_or_label - + fixed_32bit_literal (switch_target += offset_or_label {$size+=4; targetCount++;})* @@ -451,13 +483,11 @@ instruction returns [int size] /*add a nop statement before this if needed to force the correct alignment*/ -> {needsNop}? ^(I_STATEMENT_FORMAT10x[$start, "I_STATEMENT_FORMAT10x"] INSTRUCTION_FORMAT10x[$start, "nop"]) ^(I_STATEMENT_PACKED_SWITCH[$start, "I_STATEMENT_PACKED_SWITCH"] - ^(I_PACKED_SWITCH_BASE_OFFSET[$start, "I_PACKED_SWITCH_BASE_OFFSET"] $base_offset) ^(I_PACKED_SWITCH_START_KEY[$start, "I_PACKED_SWITCH_START_KEY"] fixed_32bit_literal) ^(I_PACKED_SWITCH_TARGETS[$start, "I_PACKED_SWITCH_TARGETS"] I_PACKED_SWITCH_TARGET_COUNT[$start, Integer.toString(targetCount)] $switch_target*) ) -> ^(I_STATEMENT_PACKED_SWITCH[$start, "I_STATEMENT_PACKED_SWITCH"] - ^(I_PACKED_SWITCH_BASE_OFFSET[$start, "I_PACKED_SWITCH_BASE_OFFSET"] $base_offset) ^(I_PACKED_SWITCH_START_KEY[$start, "I_PACKED_SWITCH_START_KEY"] fixed_32bit_literal) ^(I_PACKED_SWITCH_TARGETS[$start, "I_PACKED_SWITCH_TARGETS"] I_PACKED_SWITCH_TARGET_COUNT[$start, Integer.toString(targetCount)] $switch_target*) ) @@ -473,9 +503,7 @@ instruction returns [int size] $size = 0; } } - - base_offset = offset_or_label - + (fixed_32bit_literal switch_target += offset_or_label {$size += 8; targetCount++;})* END_SPARSE_SWITCH_DIRECTIVE {$size = $size + 4;} @@ -483,12 +511,10 @@ instruction returns [int size] /*add a nop statement before this if needed to force the correct alignment*/ -> {needsNop}? ^(I_STATEMENT_FORMAT10x[$start, "I_STATEMENT_FORMAT10x"] INSTRUCTION_FORMAT10x[$start, "nop"]) ^(I_STATEMENT_SPARSE_SWITCH[$start, "I_STATEMENT_SPARSE_SWITCH"] - ^(I_SPARSE_SWITCH_BASE_OFFSET[$start, "I_SPARSE_SWITCH_BASE_OFFSET"] $base_offset) I_SPARSE_SWITCH_TARGET_COUNT[$start, Integer.toString(targetCount)] ^(I_SPARSE_SWITCH_KEYS[$start, "I_SPARSE_SWITCH_KEYS"] fixed_32bit_literal*) ^(I_SPARSE_SWITCH_TARGETS $switch_target*)) -> ^(I_STATEMENT_SPARSE_SWITCH[$start, "I_STATEMENT_SPARSE_SWITCH"] - ^(I_SPARSE_SWITCH_BASE_OFFSET[$start, "I_SPARSE_SWITCH_BASE_OFFSET"] $base_offset) I_SPARSE_SWITCH_TARGET_COUNT[$start, Integer.toString(targetCount)] ^(I_SPARSE_SWITCH_KEYS[$start, "I_SPARSE_SWITCH_KEYS"] fixed_32bit_literal*) ^(I_SPARSE_SWITCH_TARGETS $switch_target*)) diff --git a/smali/src/main/antlr3/org/jf/smali/smaliTreeWalker.g b/smali/src/main/antlr3/org/jf/smali/smaliTreeWalker.g index deb7cce7..746dab52 100644 --- a/smali/src/main/antlr3/org/jf/smali/smaliTreeWalker.g +++ b/smali/src/main/antlr3/org/jf/smali/smaliTreeWalker.g @@ -1,4 +1,4 @@ -/* + /* * [The "BSD licence"] * Copyright (c) 2009 Ben Gruver * All rights reserved. @@ -112,7 +112,7 @@ smali_file : ^(I_CLASS_DEF header methods fields annotations) { AnnotationDirectoryItem annotationDirectoryItem = null; - + if ( $methods.methodAnnotationSets != null || $methods.parameterAnnotationSets != null || $fields.fieldAnnotationSets != null || @@ -124,7 +124,7 @@ smali_file $methods.methodAnnotationSets, $methods.parameterAnnotationSets); } - + classDefItem.setAnnotations(annotationDirectoryItem); }; catch [Exception ex] { @@ -132,6 +132,7 @@ smali_file } + header returns[TypeIdItem classType, int accessFlags, TypeIdItem superType, TypeListItem implementsList, StringIdItem sourceSpec] : class_spec super_spec implements_list source_spec { @@ -141,6 +142,7 @@ header returns[TypeIdItem classType, int accessFlags, TypeIdItem superType, Type $super_spec.type, $implements_list.implementsList, $source_spec.source, classDataItem); }; + class_spec returns[TypeIdItem type, int accessFlags] : class_type_descriptor access_list { @@ -153,7 +155,7 @@ super_spec returns[TypeIdItem type] { $type = $class_type_descriptor.type; }; - + implements_spec returns[TypeIdItem type] : ^(I_IMPLEMENTS class_type_descriptor) @@ -313,7 +315,7 @@ array_elements returns[List values] 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] +packed_switch_targets[int baseAddress] returns[int[\] targets] : ^(I_PACKED_SWITCH_TARGETS packed_switch_target_count @@ -325,7 +327,7 @@ packed_switch_targets[int baseOffset] returns[int[\] targets] (offset_or_label { - targets[targetsPosition++] = $offset_or_label.offsetValue - $baseOffset; + $targets[targetsPosition++] = ($method::currentAddress + $offset_or_label.offsetValue) - $baseAddress; })* ); @@ -344,7 +346,8 @@ sparse_switch_keys[int targetCount] returns[int[\] keys] })* ); -sparse_switch_targets[int baseOffset, int targetCount] returns[int[\] targets] + +sparse_switch_targets[int baseAddress, int targetCount] returns[int[\] targets] : { $targets = new int[$targetCount]; int targetsPosition = 0; @@ -352,10 +355,10 @@ sparse_switch_targets[int baseOffset, int targetCount] returns[int[\] targets] ^(I_SPARSE_SWITCH_TARGETS (offset_or_label { - $targets[targetsPosition++] = $offset_or_label.offsetValue - $baseOffset; + $targets[targetsPosition++] = ($method::currentAddress + $offset_or_label.offsetValue) - $baseAddress; })* ); - + method returns[ ClassDataItem.EncodedMethod encodedMethod, AnnotationDirectoryItem.MethodAnnotation methodAnnotationSet, AnnotationDirectoryItem.ParameterAnnotation parameterAnnotationSets] @@ -365,6 +368,8 @@ method returns[ ClassDataItem.EncodedMethod encodedMethod, TryListBuilder tryList; int currentAddress; DebugInfoBuilder debugInfo; + HashMap packedSwitchDeclarations; + HashMap sparseSwitchDeclarations; } @init { @@ -379,6 +384,8 @@ method returns[ ClassDataItem.EncodedMethod encodedMethod, $method::tryList = new TryListBuilder(); $method::currentAddress = 0; $method::debugInfo = new DebugInfoBuilder(); + $method::packedSwitchDeclarations = new HashMap(); + $method::sparseSwitchDeclarations = new HashMap(); } ^( I_METHOD method_name_and_prototype @@ -394,6 +401,8 @@ method returns[ ClassDataItem.EncodedMethod encodedMethod, totalMethodRegisters = $registers_directive.registers; } labels + packed_switch_declarations + sparse_switch_declarations statements[totalMethodRegisters, methodParameterRegisters] catches parameters @@ -524,6 +533,34 @@ label_def $method::labels.put(labelName, $address.address); }; + +packed_switch_declarations + : ^(I_PACKED_SWITCH_DECLARATIONS packed_switch_declaration*); +packed_switch_declaration + : ^(I_PACKED_SWITCH_DECLARATION address offset_or_label_absolute[$address.address]) + { + int switchDataAddress = $offset_or_label_absolute.address; + if ((switchDataAddress \% 2) != 0) { + switchDataAddress++; + } + if (!$method::packedSwitchDeclarations.containsKey(switchDataAddress)) { + $method::packedSwitchDeclarations.put(switchDataAddress, $address.address); + } + }; + +sparse_switch_declarations + : ^(I_SPARSE_SWITCH_DECLARATIONS sparse_switch_declaration*); +sparse_switch_declaration + : ^(I_SPARSE_SWITCH_DECLARATION address offset_or_label_absolute[$address.address]) + { + int switchDataAddress = $offset_or_label_absolute.address; + if ((switchDataAddress \% 2) != 0) { + switchDataAddress++; + } + if (!$method::sparseSwitchDeclarations.containsKey(switchDataAddress)) { + $method::sparseSwitchDeclarations.put(switchDataAddress, $address.address); + } + }; catches : ^(I_CATCHES catch_directive*); @@ -1032,7 +1069,15 @@ instruction[int totalMethodRegisters, int methodParameterRegisters] returns[Ins } | - ^(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]) + ^(I_STATEMENT_PACKED_SWITCH ^(I_PACKED_SWITCH_START_KEY fixed_32bit_literal) + { + int currentAddress = $method::currentAddress; + Integer baseAddress = $method::packedSwitchDeclarations.get(currentAddress); + if (baseAddress == null) { + baseAddress = 0; + } + } + packed_switch_targets[baseAddress]) { int startKey = $fixed_32bit_literal.value; int[] targets = $packed_switch_targets.targets; @@ -1040,7 +1085,16 @@ instruction[int totalMethodRegisters, int methodParameterRegisters] returns[Ins $instruction = new InstructionField(dexFile, new PackedSwitchDataPseudoInstruction(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]) + ^(I_STATEMENT_SPARSE_SWITCH sparse_switch_target_count sparse_switch_keys[$sparse_switch_target_count.targetCount] + { + int currentAddress = $method::currentAddress; + Integer baseAddress = $method::sparseSwitchDeclarations.get(currentAddress); + if (baseAddress == null) { + baseAddress = 0; + } + } + + sparse_switch_targets[baseAddress, $sparse_switch_target_count.targetCount]) { int[] keys = $sparse_switch_keys.keys; int[] targets = $sparse_switch_targets.targets;