/* * Copyright 2014, 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.smalidea; import com.google.common.collect.Lists; import com.intellij.debugger.NoDataException; import com.intellij.debugger.PositionManager; import com.intellij.debugger.SourcePosition; import com.intellij.debugger.engine.DebugProcess; import com.intellij.debugger.engine.DebugProcessListener; import com.intellij.debugger.engine.evaluation.EvaluateException; import com.intellij.debugger.engine.evaluation.EvaluationContext; import com.intellij.debugger.engine.jdi.VirtualMachineProxy; import com.intellij.debugger.engine.managerThread.DebuggerManagerThread; import com.intellij.debugger.requests.RequestManager; import com.intellij.execution.ExecutionResult; import com.intellij.execution.process.ProcessHandler; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Key; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase; import com.sun.jdi.*; import org.jetbrains.annotations.NotNull; import org.jf.dexlib2.Opcode; import org.jf.smalidea.debugging.SmaliPositionManager; import org.jf.smalidea.psi.impl.SmaliInstruction; import org.junit.Assert; import java.util.List; import java.util.Map; public class SmaliPositionManagerTest extends LightCodeInsightFixtureTestCase { private static final String testClass = "\n\n.class public Lmy/pkg/blah; .super Ljava/lang/Object;\n" + ".method public getRandomParentType(I)I\n" + " .registers 4\n" + " .param p1, \"edge\" # I\n" + "\n" + " .prologue\n" + " const/4 v1, 0x2\n" + "\n" + " .line 179\n" + " if-nez p1, :cond_5\n" + "\n" + " move v0, v1\n" + "\n" + " .line 185\n" + " :goto_4\n" + " return v0\n" + "\n" + " .line 182\n" + " :cond_5\n" + " if-ne p1, v1, :cond_f\n" + "\n" + " .line 183\n" + " sget-object v0, Lorg/jf/Penroser/PenroserApp;->random:Ljava/util/Random;\n" + "\n" + " const/4 v1, 0x3\n" + "\n" + " invoke-virtual {v0, v1}, Ljava/util/Random;->nextInt(I)I\n" + "\n" + " move-result v0\n" + "\n" + " goto :goto_4\n" + "\n" + " .line 185\n" + " :cond_f\n" + " sget-object v0, Lorg/jf/Penroser/PenroserApp;->random:Ljava/util/Random;\n" + "\n" + " invoke-virtual {v0, v1}, Ljava/util/Random;->nextInt(I)I\n" + "\n" + " move-result v0\n" + "\n" + " goto :goto_4\n" + ".end method"; public void testGetSourcePosition() throws NoDataException { myFixture.addFileToProject("my/pkg/blah.smali", testClass); SmaliPositionManager positionManager = new SmaliPositionManager(new MockDebugProcess()); SourcePosition sourcePosition = positionManager.getSourcePosition( "my.pkg.blah", "getRandomParentType", "(I)I", 0); Assert.assertEquals(Opcode.CONST_4, ((SmaliInstruction)sourcePosition.getElementAt()).getOpcode()); Assert.assertEquals(0, ((SmaliInstruction)sourcePosition.getElementAt()).getOffset()); sourcePosition = positionManager.getSourcePosition("my.pkg.blah", "getRandomParentType", "(I)I", 10); Assert.assertEquals(Opcode.INVOKE_VIRTUAL, ((SmaliInstruction)sourcePosition.getElementAt()).getOpcode()); Assert.assertEquals(20, ((SmaliInstruction)sourcePosition.getElementAt()).getOffset()); } public void testGetAllClasses() throws NoDataException { myFixture.addFileToProject("my/pkg/blah.smali", testClass); SmaliPositionManager positionManager = new SmaliPositionManager(new MockDebugProcess()); List<ReferenceType> classes = positionManager.getAllClasses(positionManager.getSourcePosition( "my.pkg.blah", "getRandomParentType", "(I)I", 0)); Assert.assertEquals(1, classes.size()); Assert.assertEquals("my.pkg.blah", classes.get(0).name()); } private class MockDebugProcess implements DebugProcess { @Override public Project getProject() { return SmaliPositionManagerTest.this.getProject(); } @Override public VirtualMachineProxy getVirtualMachineProxy() { return new VirtualMachineProxy() { @Override public List<ReferenceType> classesByName(final String s) { return Lists.<ReferenceType>newArrayList(new MockReferenceType(s)); } @Override public List<ReferenceType> allClasses() { return null; } @Override public boolean canGetBytecodes() { return false; } @Override public boolean versionHigher(String version) { return false; } @Override public boolean canWatchFieldModification() { return false; } @Override public boolean canWatchFieldAccess() { return false; } @Override public boolean canInvokeMethods() { return false; } @Override public DebugProcess getDebugProcess() { return null; } @Override public List<ReferenceType> nestedTypes(ReferenceType refType) { return null; } }; } @Override public void addDebugProcessListener(DebugProcessListener listener) {} @Override public <T> T getUserData(Key<T> key) { return null; } @Override public <T> void putUserData(Key<T> key, T value) {} @Override public RequestManager getRequestsManager() { return null; } @Override public PositionManager getPositionManager() { return null; } @Override public void removeDebugProcessListener(DebugProcessListener listener) {} @Override public void appendPositionManager(PositionManager positionManager) {} @Override public void waitFor() {} @Override public void waitFor(long timeout) {} @Override public void stop(boolean forceTerminate) {} @Override public ExecutionResult getExecutionResult() { return null; } @Override public DebuggerManagerThread getManagerThread() { return null; } @Override public Value invokeMethod(EvaluationContext evaluationContext, ObjectReference objRef, Method method, List args) throws EvaluateException { return null; } @Override public Value invokeMethod(EvaluationContext evaluationContext, ClassType classType, Method method, List args) throws EvaluateException { return null; } @Override public Value invokeInstanceMethod(EvaluationContext evaluationContext, ObjectReference objRef, Method method, List args, int invocationOptions) throws EvaluateException { return null; } @Override public ReferenceType findClass(EvaluationContext evaluationContext, String name, ClassLoaderReference classLoader) throws EvaluateException { return null; } @Override public ArrayReference newInstance(ArrayType arrayType, int dimension) throws EvaluateException { return null; } @Override public ObjectReference newInstance(EvaluationContext evaluationContext, ClassType classType, Method constructor, List paramList) throws EvaluateException { return null; } @Override public boolean isAttached() { return false; } @Override public boolean isDetached() { return false; } @Override public boolean isDetaching() { return false; } @NotNull @Override public GlobalSearchScope getSearchScope() { return null; } @Override public void printToConsole(String text) {} @Override public ProcessHandler getProcessHandler() { return null; } } private static class MockReferenceType implements ReferenceType { private final String name; public MockReferenceType(String name) { this.name = name; } @Override public String name() { return name; } @Override public List<Field> allFields() { return null; } @Override public String genericSignature() { return null; } @Override public ClassLoaderReference classLoader() { return null; } @Override public String sourceName() throws AbsentInformationException { return null; } @Override public List<String> sourceNames(String s) throws AbsentInformationException { return null; } @Override public List<String> sourcePaths(String s) throws AbsentInformationException { return null; } @Override public String sourceDebugExtension() throws AbsentInformationException { return null; } @Override public boolean isStatic() { return false; } @Override public boolean isAbstract() { return false; } @Override public boolean isFinal() { return false; } @Override public boolean isPrepared() { return false; } @Override public boolean isVerified() { return false; } @Override public boolean isInitialized() { return false; } @Override public boolean failedToInitialize() { return false; } @Override public List<Field> fields() { return null; } @Override public List<Field> visibleFields() { return null; } @Override public Field fieldByName(String s) { return null; } @Override public List<Method> methods() { return null; } @Override public List<Method> visibleMethods() { return null; } @Override public List<Method> allMethods() { return null; } @Override public List<Method> methodsByName(String s) { return null; } @Override public List<Method> methodsByName(String s, String s1) { return null; } @Override public List<ReferenceType> nestedTypes() { return null; } @Override public Value getValue(Field field) { return null; } @Override public Map<Field, Value> getValues(List<? extends Field> list) { return null; } @Override public ClassObjectReference classObject() { return null; } @Override public List<Location> allLineLocations() throws AbsentInformationException { return null; } @Override public List<Location> allLineLocations(String s, String s1) throws AbsentInformationException { return null; } @Override public List<Location> locationsOfLine(int i) throws AbsentInformationException { return null; } @Override public List<Location> locationsOfLine(String s, String s1, int i) throws AbsentInformationException { return null; } @Override public List<String> availableStrata() { return null; } @Override public String defaultStratum() { return null; } @Override public List<ObjectReference> instances(long l) { return null; } @Override public int majorVersion() { return 0; } @Override public int minorVersion() { return 0; } @Override public int constantPoolCount() { return 0; } @Override public byte[] constantPool() { return new byte[0]; } @Override public int modifiers() { return 0; } @Override public boolean isPrivate() { return false; } @Override public boolean isPackagePrivate() { return false; } @Override public boolean isProtected() { return false; } @Override public boolean isPublic() { return false; } @Override public int compareTo(ReferenceType o) { return 0; } @Override public String signature() { return null; } @Override public VirtualMachine virtualMachine() { return null; } } }