diff --git a/dexlib2/src/main/java/org/jf/dexlib2/analysis/MethodAnalyzer.java b/dexlib2/src/main/java/org/jf/dexlib2/analysis/MethodAnalyzer.java index d5ac9771..4b7bdfc7 100644 --- a/dexlib2/src/main/java/org/jf/dexlib2/analysis/MethodAnalyzer.java +++ b/dexlib2/src/main/java/org/jf/dexlib2/analysis/MethodAnalyzer.java @@ -1208,8 +1208,27 @@ public class MethodAnalyzer { AnalyzedInstruction prevAnalyzedInstruction = analyzedInstructions.valueAt(instructionIndex - 1); if (prevAnalyzedInstruction.instruction.getOpcode() == Opcode.INSTANCE_OF) { if (canNarrowAfterInstanceOf(prevAnalyzedInstruction, analyzedInstruction, classPath)) { + List narrowingRegisters = Lists.newArrayList(); + + if (instructionIndex > 1) { + // If we have something like: + // move-object/from16 v0, p1 + // instance-of v2, v0, Lblah; + // if-eqz v2, :target + // Then we need to narrow both v0 AND p1 + AnalyzedInstruction prevPrevAnalyzedInstruction = + analyzedInstructions.valueAt(instructionIndex - 2); + Opcode opcode = prevPrevAnalyzedInstruction.instruction.getOpcode(); + if (opcode == Opcode.MOVE_OBJECT || opcode == Opcode.MOVE_OBJECT_16 || + opcode == Opcode.MOVE_OBJECT_FROM16) { + narrowingRegisters.add( + ((TwoRegisterInstruction)prevPrevAnalyzedInstruction.instruction).getRegisterB()); + } + } + // Propagate the original type to the failing branch, and the new type to the successful branch int narrowingRegister = ((Instruction22c)prevAnalyzedInstruction.instruction).getRegisterB(); + narrowingRegisters.add(narrowingRegister); RegisterType originalType = analyzedInstruction.getPreInstructionRegisterType(narrowingRegister); RegisterType newType = RegisterType.getRegisterType(classPath, (TypeReference)((Instruction22c)prevAnalyzedInstruction.instruction).getReference()); @@ -1221,16 +1240,18 @@ public class MethodAnalyzer { ((Instruction21t)analyzedInstruction.instruction).getCodeOffset(); AnalyzedInstruction branchInstruction = analyzedInstructions.get(nextAddress); - if (analyzedInstruction.instruction.getOpcode() == Opcode.IF_EQZ) { - overridePredecessorRegisterTypeAndPropagateChanges(fallthroughInstruction, analyzedInstruction, - narrowingRegister, newType); - overridePredecessorRegisterTypeAndPropagateChanges(branchInstruction, analyzedInstruction, - narrowingRegister, originalType); - } else { - overridePredecessorRegisterTypeAndPropagateChanges(fallthroughInstruction, analyzedInstruction, - narrowingRegister, originalType); - overridePredecessorRegisterTypeAndPropagateChanges(branchInstruction, analyzedInstruction, - narrowingRegister, newType); + for (int register: narrowingRegisters) { + if (analyzedInstruction.instruction.getOpcode() == Opcode.IF_EQZ) { + overridePredecessorRegisterTypeAndPropagateChanges(fallthroughInstruction, analyzedInstruction, + register, newType); + overridePredecessorRegisterTypeAndPropagateChanges(branchInstruction, analyzedInstruction, + register, originalType); + } else { + overridePredecessorRegisterTypeAndPropagateChanges(fallthroughInstruction, analyzedInstruction, + register, originalType); + overridePredecessorRegisterTypeAndPropagateChanges(branchInstruction, analyzedInstruction, + register, newType); + } } } } diff --git a/dexlib2/src/test/java/org/jf/dexlib2/analysis/MethodAnalyzerTest.java b/dexlib2/src/test/java/org/jf/dexlib2/analysis/MethodAnalyzerTest.java new file mode 100644 index 00000000..8d108ac9 --- /dev/null +++ b/dexlib2/src/test/java/org/jf/dexlib2/analysis/MethodAnalyzerTest.java @@ -0,0 +1,156 @@ +/* + * Copyright 2016, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.jf.dexlib2.analysis; + +import org.jf.dexlib2.AccessFlags; +import org.jf.dexlib2.Opcode; +import org.jf.dexlib2.Opcodes; +import org.jf.dexlib2.builder.MethodImplementationBuilder; +import org.jf.dexlib2.builder.instruction.BuilderInstruction10x; +import org.jf.dexlib2.builder.instruction.BuilderInstruction12x; +import org.jf.dexlib2.builder.instruction.BuilderInstruction21t; +import org.jf.dexlib2.builder.instruction.BuilderInstruction22c; +import org.jf.dexlib2.iface.ClassDef; +import org.jf.dexlib2.iface.DexFile; +import org.jf.dexlib2.iface.Method; +import org.jf.dexlib2.iface.MethodImplementation; +import org.jf.dexlib2.immutable.ImmutableClassDef; +import org.jf.dexlib2.immutable.ImmutableDexFile; +import org.jf.dexlib2.immutable.ImmutableMethod; +import org.jf.dexlib2.immutable.ImmutableMethodParameter; +import org.jf.dexlib2.immutable.reference.ImmutableTypeReference; +import org.junit.Assert; +import org.junit.Test; + +import java.io.IOException; +import java.util.Collections; +import java.util.List; + +public class MethodAnalyzerTest { + + @Test + public void testInstanceOfNarrowingEqz() throws IOException { + MethodImplementationBuilder builder = new MethodImplementationBuilder(2); + + builder.addInstruction(new BuilderInstruction22c(Opcode.INSTANCE_OF, 0, 1, + new ImmutableTypeReference("Lmain;"))); + builder.addInstruction(new BuilderInstruction21t(Opcode.IF_EQZ, 0, builder.getLabel("not_instance_of"))); + builder.addInstruction(new BuilderInstruction10x(Opcode.RETURN_VOID)); + + builder.addLabel("not_instance_of"); + builder.addInstruction(new BuilderInstruction10x(Opcode.RETURN_VOID)); + + MethodImplementation methodImplementation = builder.getMethodImplementation(); + + Method method = new ImmutableMethod("Lmain;", "narrowing", + Collections.singletonList(new ImmutableMethodParameter("Ljava/lang/Object;", null, null)), "V", + AccessFlags.PUBLIC.getValue(), null, methodImplementation); + ClassDef classDef = new ImmutableClassDef("Lmain;", AccessFlags.PUBLIC.getValue(), "Ljava/lang/Object;", null, + null, null, null, Collections.singletonList(method)); + DexFile dexFile = new ImmutableDexFile(Opcodes.forApi(15), Collections.singletonList(classDef)); + + ClassPath classPath = new ClassPath(new DexClassProvider(dexFile)); + MethodAnalyzer methodAnalyzer = new MethodAnalyzer(classPath, method, null, false); + + List analyzedInstructions = methodAnalyzer.getAnalyzedInstructions(); + Assert.assertEquals("Lmain;", analyzedInstructions.get(2).getPreInstructionRegisterType(1).type.getType()); + + Assert.assertEquals("Ljava/lang/Object;", + analyzedInstructions.get(3).getPreInstructionRegisterType(1).type.getType()); + } + + @Test + public void testInstanceOfNarrowingNez() throws IOException { + MethodImplementationBuilder builder = new MethodImplementationBuilder(2); + + builder.addInstruction(new BuilderInstruction22c(Opcode.INSTANCE_OF, 0, 1, + new ImmutableTypeReference("Lmain;"))); + builder.addInstruction(new BuilderInstruction21t(Opcode.IF_NEZ, 0, builder.getLabel("instance_of"))); + builder.addInstruction(new BuilderInstruction10x(Opcode.RETURN_VOID)); + + builder.addLabel("instance_of"); + builder.addInstruction(new BuilderInstruction10x(Opcode.RETURN_VOID)); + + MethodImplementation methodImplementation = builder.getMethodImplementation(); + + Method method = new ImmutableMethod("Lmain;", "narrowing", + Collections.singletonList(new ImmutableMethodParameter("Ljava/lang/Object;", null, null)), "V", + AccessFlags.PUBLIC.getValue(), null, methodImplementation); + ClassDef classDef = new ImmutableClassDef("Lmain;", AccessFlags.PUBLIC.getValue(), "Ljava/lang/Object;", null, + null, null, null, Collections.singletonList(method)); + DexFile dexFile = new ImmutableDexFile(Opcodes.forApi(15), Collections.singletonList(classDef)); + + ClassPath classPath = new ClassPath(new DexClassProvider(dexFile)); + MethodAnalyzer methodAnalyzer = new MethodAnalyzer(classPath, method, null, false); + + List analyzedInstructions = methodAnalyzer.getAnalyzedInstructions(); + Assert.assertEquals("Ljava/lang/Object;", + analyzedInstructions.get(2).getPreInstructionRegisterType(1).type.getType()); + + Assert.assertEquals("Lmain;", analyzedInstructions.get(3).getPreInstructionRegisterType(1).type.getType()); + } + + @Test + public void testInstanceOfNarrowingAfterMove() throws IOException { + MethodImplementationBuilder builder = new MethodImplementationBuilder(3); + + builder.addInstruction(new BuilderInstruction12x(Opcode.MOVE_OBJECT, 1, 2)); + builder.addInstruction(new BuilderInstruction22c(Opcode.INSTANCE_OF, 0, 1, + new ImmutableTypeReference("Lmain;"))); + builder.addInstruction(new BuilderInstruction21t(Opcode.IF_EQZ, 0, builder.getLabel("not_instance_of"))); + builder.addInstruction(new BuilderInstruction10x(Opcode.RETURN_VOID)); + + builder.addLabel("not_instance_of"); + builder.addInstruction(new BuilderInstruction10x(Opcode.RETURN_VOID)); + + MethodImplementation methodImplementation = builder.getMethodImplementation(); + + Method method = new ImmutableMethod("Lmain;", "narrowing", + Collections.singletonList(new ImmutableMethodParameter("Ljava/lang/Object;", null, null)), "V", + AccessFlags.PUBLIC.getValue(), null, methodImplementation); + ClassDef classDef = new ImmutableClassDef("Lmain;", AccessFlags.PUBLIC.getValue(), "Ljava/lang/Object;", null, + null, null, null, Collections.singletonList(method)); + DexFile dexFile = new ImmutableDexFile(Opcodes.forApi(15), Collections.singletonList(classDef)); + + ClassPath classPath = new ClassPath(new DexClassProvider(dexFile)); + MethodAnalyzer methodAnalyzer = new MethodAnalyzer(classPath, method, null, false); + + List analyzedInstructions = methodAnalyzer.getAnalyzedInstructions(); + Assert.assertEquals("Lmain;", analyzedInstructions.get(3).getPreInstructionRegisterType(1).type.getType()); + Assert.assertEquals("Lmain;", analyzedInstructions.get(3).getPreInstructionRegisterType(2).type.getType()); + + Assert.assertEquals("Ljava/lang/Object;", + analyzedInstructions.get(4).getPreInstructionRegisterType(1).type.getType()); + Assert.assertEquals("Ljava/lang/Object;", + analyzedInstructions.get(4).getPreInstructionRegisterType(2).type.getType()); + } +}