1 /* 2 * Copyright 2014, Google Inc. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are 7 * met: 8 * 9 * * Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * * Redistributions in binary form must reproduce the above 12 * copyright notice, this list of conditions and the following disclaimer 13 * in the documentation and/or other materials provided with the 14 * distribution. 15 * * Neither the name of Google Inc. nor the names of its 16 * contributors may be used to endorse or promote products derived from 17 * this software without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 package org.jf.smalidea; 33 34 import com.google.common.collect.Lists; 35 import com.intellij.debugger.NoDataException; 36 import com.intellij.debugger.PositionManager; 37 import com.intellij.debugger.SourcePosition; 38 import com.intellij.debugger.engine.DebugProcess; 39 import com.intellij.debugger.engine.DebugProcessListener; 40 import com.intellij.debugger.engine.evaluation.EvaluateException; 41 import com.intellij.debugger.engine.evaluation.EvaluationContext; 42 import com.intellij.debugger.engine.jdi.VirtualMachineProxy; 43 import com.intellij.debugger.engine.managerThread.DebuggerManagerThread; 44 import com.intellij.debugger.requests.RequestManager; 45 import com.intellij.execution.ExecutionResult; 46 import com.intellij.execution.process.ProcessHandler; 47 import com.intellij.openapi.project.Project; 48 import com.intellij.openapi.util.Key; 49 import com.intellij.psi.search.GlobalSearchScope; 50 import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase; 51 import com.sun.jdi.*; 52 import org.jetbrains.annotations.NotNull; 53 import org.jf.dexlib2.Opcode; 54 import org.jf.smalidea.debugging.SmaliPositionManager; 55 import org.jf.smalidea.psi.impl.SmaliInstruction; 56 import org.junit.Assert; 57 58 import java.util.List; 59 import java.util.Map; 60 61 public class SmaliPositionManagerTest extends LightCodeInsightFixtureTestCase { 62 private static final String testClass = 63 "\n\n.class public Lmy/pkg/blah; .super Ljava/lang/Object;\n" + 64 ".method public getRandomParentType(I)I\n" + 65 " .registers 4\n" + 66 " .param p1, \"edge\" # I\n" + 67 "\n" + 68 " .prologue\n" + 69 " const/4 v1, 0x2\n" + 70 "\n" + 71 " .line 179\n" + 72 " if-nez p1, :cond_5\n" + 73 "\n" + 74 " move v0, v1\n" + 75 "\n" + 76 " .line 185\n" + 77 " :goto_4\n" + 78 " return v0\n" + 79 "\n" + 80 " .line 182\n" + 81 " :cond_5\n" + 82 " if-ne p1, v1, :cond_f\n" + 83 "\n" + 84 " .line 183\n" + 85 " sget-object v0, Lorg/jf/Penroser/PenroserApp;->random:Ljava/util/Random;\n" + 86 "\n" + 87 " const/4 v1, 0x3\n" + 88 "\n" + 89 " invoke-virtual {v0, v1}, Ljava/util/Random;->nextInt(I)I\n" + 90 "\n" + 91 " move-result v0\n" + 92 "\n" + 93 " goto :goto_4\n" + 94 "\n" + 95 " .line 185\n" + 96 " :cond_f\n" + 97 " sget-object v0, Lorg/jf/Penroser/PenroserApp;->random:Ljava/util/Random;\n" + 98 "\n" + 99 " invoke-virtual {v0, v1}, Ljava/util/Random;->nextInt(I)I\n" + 100 "\n" + 101 " move-result v0\n" + 102 "\n" + 103 " goto :goto_4\n" + 104 ".end method"; 105 testGetSourcePosition()106 public void testGetSourcePosition() throws NoDataException { 107 myFixture.addFileToProject("my/pkg/blah.smali", testClass); 108 109 SmaliPositionManager positionManager = new SmaliPositionManager(new MockDebugProcess()); 110 111 SourcePosition sourcePosition = positionManager.getSourcePosition( 112 "my.pkg.blah", "getRandomParentType", "(I)I", 0); 113 Assert.assertEquals(Opcode.CONST_4, ((SmaliInstruction)sourcePosition.getElementAt()).getOpcode()); 114 Assert.assertEquals(0, ((SmaliInstruction)sourcePosition.getElementAt()).getOffset()); 115 116 sourcePosition = positionManager.getSourcePosition("my.pkg.blah", "getRandomParentType", "(I)I", 10); 117 Assert.assertEquals(Opcode.INVOKE_VIRTUAL, ((SmaliInstruction)sourcePosition.getElementAt()).getOpcode()); 118 Assert.assertEquals(20, ((SmaliInstruction)sourcePosition.getElementAt()).getOffset()); 119 } 120 testGetAllClasses()121 public void testGetAllClasses() throws NoDataException { 122 myFixture.addFileToProject("my/pkg/blah.smali", testClass); 123 124 SmaliPositionManager positionManager = new SmaliPositionManager(new MockDebugProcess()); 125 126 List<ReferenceType> classes = positionManager.getAllClasses(positionManager.getSourcePosition( 127 "my.pkg.blah", "getRandomParentType", "(I)I", 0)); 128 Assert.assertEquals(1, classes.size()); 129 Assert.assertEquals("my.pkg.blah", classes.get(0).name()); 130 } 131 132 private class MockDebugProcess implements DebugProcess { getProject()133 @Override public Project getProject() { 134 return SmaliPositionManagerTest.this.getProject(); 135 } 136 getVirtualMachineProxy()137 @Override public VirtualMachineProxy getVirtualMachineProxy() { 138 return new VirtualMachineProxy() { 139 @Override public List<ReferenceType> classesByName(final String s) { 140 return Lists.<ReferenceType>newArrayList(new MockReferenceType(s)); 141 } 142 143 @Override public List<ReferenceType> allClasses() { return null; } 144 @Override public boolean canGetBytecodes() { return false; } 145 @Override public boolean versionHigher(String version) { return false; } 146 @Override public boolean canWatchFieldModification() { return false; } 147 @Override public boolean canWatchFieldAccess() { return false; } 148 @Override public boolean canInvokeMethods() { return false; } 149 @Override public DebugProcess getDebugProcess() { return null; } 150 @Override public List<ReferenceType> nestedTypes(ReferenceType refType) { return null; } 151 }; 152 } 153 addDebugProcessListener(DebugProcessListener listener)154 @Override public void addDebugProcessListener(DebugProcessListener listener) {} getUserData(Key<T> key)155 @Override public <T> T getUserData(Key<T> key) { return null; } putUserData(Key<T> key, T value)156 @Override public <T> void putUserData(Key<T> key, T value) {} getRequestsManager()157 @Override public RequestManager getRequestsManager() { return null; } getPositionManager()158 @Override public PositionManager getPositionManager() { return null; } removeDebugProcessListener(DebugProcessListener listener)159 @Override public void removeDebugProcessListener(DebugProcessListener listener) {} appendPositionManager(PositionManager positionManager)160 @Override public void appendPositionManager(PositionManager positionManager) {} waitFor()161 @Override public void waitFor() {} waitFor(long timeout)162 @Override public void waitFor(long timeout) {} stop(boolean forceTerminate)163 @Override public void stop(boolean forceTerminate) {} getExecutionResult()164 @Override public ExecutionResult getExecutionResult() { return null; } getManagerThread()165 @Override public DebuggerManagerThread getManagerThread() { return null; } invokeMethod(EvaluationContext evaluationContext, ObjectReference objRef, Method method, List args)166 @Override public Value invokeMethod(EvaluationContext evaluationContext, ObjectReference objRef, Method method, List args) throws EvaluateException { return null; } invokeMethod(EvaluationContext evaluationContext, ClassType classType, Method method, List args)167 @Override public Value invokeMethod(EvaluationContext evaluationContext, ClassType classType, Method method, List args) throws EvaluateException { return null; } invokeInstanceMethod(EvaluationContext evaluationContext, ObjectReference objRef, Method method, List args, int invocationOptions)168 @Override public Value invokeInstanceMethod(EvaluationContext evaluationContext, ObjectReference objRef, Method method, List args, int invocationOptions) throws EvaluateException { return null; } findClass(EvaluationContext evaluationContext, String name, ClassLoaderReference classLoader)169 @Override public ReferenceType findClass(EvaluationContext evaluationContext, String name, ClassLoaderReference classLoader) throws EvaluateException { return null; } newInstance(ArrayType arrayType, int dimension)170 @Override public ArrayReference newInstance(ArrayType arrayType, int dimension) throws EvaluateException { return null; } newInstance(EvaluationContext evaluationContext, ClassType classType, Method constructor, List paramList)171 @Override public ObjectReference newInstance(EvaluationContext evaluationContext, ClassType classType, Method constructor, List paramList) throws EvaluateException { return null; } isAttached()172 @Override public boolean isAttached() { return false; } isDetached()173 @Override public boolean isDetached() { return false; } isDetaching()174 @Override public boolean isDetaching() { return false; } getSearchScope()175 @NotNull @Override public GlobalSearchScope getSearchScope() { return null; } printToConsole(String text)176 @Override public void printToConsole(String text) {} getProcessHandler()177 @Override public ProcessHandler getProcessHandler() { return null; } 178 } 179 180 private static class MockReferenceType implements ReferenceType { 181 private final String name; 182 183 public MockReferenceType(String name) { 184 this.name = name; 185 } 186 187 @Override public String name() { 188 return name; 189 } 190 191 @Override public List<Field> allFields() { return null; } 192 @Override public String genericSignature() { return null; } 193 @Override public ClassLoaderReference classLoader() { return null; } 194 @Override public String sourceName() throws AbsentInformationException { return null; } 195 @Override public List<String> sourceNames(String s) throws AbsentInformationException { return null; } 196 @Override public List<String> sourcePaths(String s) throws AbsentInformationException { return null; } 197 @Override public String sourceDebugExtension() throws AbsentInformationException { return null; } 198 @Override public boolean isStatic() { return false; } 199 @Override public boolean isAbstract() { return false; } 200 @Override public boolean isFinal() { return false; } 201 @Override public boolean isPrepared() { return false; } 202 @Override public boolean isVerified() { return false; } 203 @Override public boolean isInitialized() { return false; } 204 @Override public boolean failedToInitialize() { return false; } 205 @Override public List<Field> fields() { return null; } 206 @Override public List<Field> visibleFields() { return null; } 207 @Override public Field fieldByName(String s) { return null; } 208 @Override public List<Method> methods() { return null; } 209 @Override public List<Method> visibleMethods() { return null; } 210 @Override public List<Method> allMethods() { return null; } 211 @Override public List<Method> methodsByName(String s) { return null; } 212 @Override public List<Method> methodsByName(String s, String s1) { return null; } 213 @Override public List<ReferenceType> nestedTypes() { return null; } 214 @Override public Value getValue(Field field) { return null; } 215 @Override public Map<Field, Value> getValues(List<? extends Field> list) { return null; } 216 @Override public ClassObjectReference classObject() { return null; } 217 @Override public List<Location> allLineLocations() throws AbsentInformationException { return null; } 218 @Override public List<Location> allLineLocations(String s, String s1) throws AbsentInformationException { return null; } 219 @Override public List<Location> locationsOfLine(int i) throws AbsentInformationException { return null; } 220 @Override public List<Location> locationsOfLine(String s, String s1, int i) throws AbsentInformationException { return null; } 221 @Override public List<String> availableStrata() { return null; } 222 @Override public String defaultStratum() { return null; } 223 @Override public List<ObjectReference> instances(long l) { return null; } 224 @Override public int majorVersion() { return 0; } 225 @Override public int minorVersion() { return 0; } 226 @Override public int constantPoolCount() { return 0; } 227 @Override public byte[] constantPool() { return new byte[0]; } 228 @Override public int modifiers() { return 0; } 229 @Override public boolean isPrivate() { return false; } 230 @Override public boolean isPackagePrivate() { return false; } 231 @Override public boolean isProtected() { return false; } 232 @Override public boolean isPublic() { return false; } 233 @Override public int compareTo(ReferenceType o) { return 0; } 234 @Override public String signature() { return null; } 235 @Override public VirtualMachine virtualMachine() { return null; } 236 } 237 } 238