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.debugging; 33 34 import com.intellij.debugger.NoDataException; 35 import com.intellij.debugger.PositionManager; 36 import com.intellij.debugger.SourcePosition; 37 import com.intellij.debugger.engine.DebugProcess; 38 import com.intellij.debugger.requests.ClassPrepareRequestor; 39 import com.intellij.openapi.application.ApplicationManager; 40 import com.intellij.openapi.util.Computable; 41 import com.intellij.psi.search.GlobalSearchScope; 42 import com.sun.jdi.Location; 43 import com.sun.jdi.ReferenceType; 44 import com.sun.jdi.request.ClassPrepareRequest; 45 import org.jetbrains.annotations.NotNull; 46 import org.jetbrains.annotations.Nullable; 47 import org.jf.smalidea.psi.impl.SmaliClass; 48 import org.jf.smalidea.psi.impl.SmaliFile; 49 import org.jf.smalidea.psi.impl.SmaliMethod; 50 import org.jf.smalidea.psi.index.SmaliClassNameIndex; 51 52 import java.util.ArrayList; 53 import java.util.Collection; 54 import java.util.List; 55 56 public class SmaliPositionManager implements PositionManager { 57 private final DebugProcess debugProcess; 58 59 public SmaliPositionManager(DebugProcess debugProcess) { 60 this.debugProcess = debugProcess; 61 } 62 63 public SourcePosition getSourcePosition(String declaringType, String methodName, String methodSignature, 64 int codeIndex) throws NoDataException { 65 Collection<SmaliClass> classes = SmaliClassNameIndex.INSTANCE.get(declaringType, 66 debugProcess.getProject(), GlobalSearchScope.projectScope(debugProcess.getProject())); 67 68 if (classes.size() > 0) { 69 SmaliClass smaliClass = classes.iterator().next(); 70 71 // TODO: make an index for this? 72 for (SmaliMethod smaliMethod: smaliClass.getMethods()) { 73 if (smaliMethod.getName().equals(methodName) && 74 smaliMethod.getMethodPrototype().getText().equals(methodSignature)) { 75 return smaliMethod.getSourcePositionForCodeOffset(codeIndex * 2); 76 } 77 } 78 } 79 80 throw NoDataException.INSTANCE; 81 } 82 83 @Override 84 public SourcePosition getSourcePosition(@Nullable Location location) throws NoDataException { 85 if (location == null) { 86 throw NoDataException.INSTANCE; 87 } 88 89 return getSourcePosition(location.declaringType().name(), location.method().name(), 90 location.method().signature(), (int)location.codeIndex()); 91 } 92 93 @Override @NotNull 94 public List<ReferenceType> getAllClasses(@NotNull SourcePosition classPosition) throws NoDataException { 95 if (!(classPosition.getElementAt().getContainingFile() instanceof SmaliFile)) { 96 throw NoDataException.INSTANCE; 97 } 98 99 String className = getClassFromPosition(classPosition); 100 return debugProcess.getVirtualMachineProxy().classesByName(className); 101 } 102 103 @NotNull 104 private String getClassFromPosition(@NotNull final SourcePosition position) { 105 return ApplicationManager.getApplication().runReadAction(new Computable<String>() { 106 @Override public String compute() { 107 SmaliClass smaliClass = ((SmaliFile)position.getElementAt().getContainingFile()).getPsiClass(); 108 if (smaliClass == null) { 109 return ""; 110 } 111 return smaliClass.getQualifiedName(); 112 } 113 }); 114 } 115 116 @Override @NotNull 117 public List<Location> locationsOfLine(@NotNull final ReferenceType type, 118 @NotNull final SourcePosition position) throws NoDataException { 119 if (!(position.getElementAt().getContainingFile() instanceof SmaliFile)) { 120 throw NoDataException.INSTANCE; 121 } 122 123 final ArrayList<Location> locations = new ArrayList<Location>(1); 124 125 ApplicationManager.getApplication().runReadAction(new Runnable() { 126 @Override 127 public void run() { 128 String typeName = type.name(); 129 Collection<SmaliClass> classes = SmaliClassNameIndex.INSTANCE.get(typeName, debugProcess.getProject(), 130 GlobalSearchScope.projectScope(debugProcess.getProject())); 131 132 if (classes.size() > 0) { 133 final SmaliClass smaliClass = classes.iterator().next(); 134 135 Location location = smaliClass.getLocationForSourcePosition(type, position); 136 137 if (location != null) { 138 locations.add(location); 139 } 140 } 141 } 142 }); 143 return locations; 144 } 145 146 @Override 147 public ClassPrepareRequest createPrepareRequest(@NotNull final ClassPrepareRequestor requestor, 148 @NotNull final SourcePosition position) throws NoDataException { 149 Computable<Boolean> isSmaliFile = new Computable<Boolean>() { 150 @Override 151 public Boolean compute() { 152 return position.getFile() instanceof SmaliFile; 153 } 154 }; 155 156 ApplicationManager.getApplication().runReadAction(isSmaliFile); 157 158 if (!isSmaliFile.compute()) { 159 throw NoDataException.INSTANCE; 160 } 161 162 String className = getClassFromPosition(position); 163 return debugProcess.getRequestsManager().createClassPrepareRequest(new ClassPrepareRequestor() { 164 @Override 165 public void processClassPrepare(DebugProcess debuggerProcess, ReferenceType referenceType) { 166 requestor.processClassPrepare(debuggerProcess, referenceType); 167 } 168 }, className); 169 } 170 }