blob: 9c0abbe617790386759c68cf55a0b4fe9c94184b [file] [log] [blame]
/*
* 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.debugging;
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.requests.ClassPrepareRequestor;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.util.Computable;
import com.intellij.psi.PsiFile;
import com.intellij.psi.search.GlobalSearchScope;
import com.sun.jdi.Location;
import com.sun.jdi.ReferenceType;
import com.sun.jdi.request.ClassPrepareRequest;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jf.smalidea.psi.impl.SmaliClass;
import org.jf.smalidea.psi.impl.SmaliFile;
import org.jf.smalidea.psi.impl.SmaliMethod;
import org.jf.smalidea.psi.index.SmaliClassNameIndex;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
public class SmaliPositionManager implements PositionManager {
private final DebugProcess debugProcess;
public SmaliPositionManager(DebugProcess debugProcess) {
this.debugProcess = debugProcess;
}
public SourcePosition getSourcePosition(final String declaringType, String methodName, String methodSignature,
int codeIndex) throws NoDataException {
Collection<SmaliClass> classes = ApplicationManager.getApplication().runReadAction(
new Computable<Collection<SmaliClass>>() {
@Override public Collection<SmaliClass> compute() {
return SmaliClassNameIndex.INSTANCE.get(declaringType, debugProcess.getProject(),
GlobalSearchScope.projectScope(debugProcess.getProject()));
}
});
if (classes.size() > 0) {
SmaliClass smaliClass = classes.iterator().next();
// TODO: make an index for this?
for (SmaliMethod smaliMethod: smaliClass.getMethods()) {
if (smaliMethod.getName().equals(methodName) &&
smaliMethod.getMethodPrototype().getText().equals(methodSignature)) {
return smaliMethod.getSourcePositionForCodeOffset(codeIndex * 2);
}
}
}
throw NoDataException.INSTANCE;
}
@Override
public SourcePosition getSourcePosition(@Nullable Location location) throws NoDataException {
if (location == null) {
throw NoDataException.INSTANCE;
}
return getSourcePosition(location.declaringType().name(), location.method().name(),
location.method().signature(), (int)location.codeIndex());
}
@Override @NotNull
public List<ReferenceType> getAllClasses(@NotNull SourcePosition classPosition) throws NoDataException {
if (!(classPosition.getElementAt().getContainingFile() instanceof SmaliFile)) {
throw NoDataException.INSTANCE;
}
String className = getClassFromPosition(classPosition);
return debugProcess.getVirtualMachineProxy().classesByName(className);
}
@NotNull
private String getClassFromPosition(@NotNull final SourcePosition position) {
return ApplicationManager.getApplication().runReadAction(new Computable<String>() {
@Override public String compute() {
SmaliClass smaliClass = ((SmaliFile)position.getElementAt().getContainingFile()).getPsiClass();
if (smaliClass == null) {
return "";
}
return smaliClass.getQualifiedName();
}
});
}
@Override @NotNull
public List<Location> locationsOfLine(@NotNull final ReferenceType type,
@NotNull final SourcePosition position) throws NoDataException {
PsiFile containingFile = ApplicationManager.getApplication().runReadAction(new Computable<PsiFile>() {
@Override public PsiFile compute() {
return position.getElementAt().getContainingFile();
}
});
if (!(containingFile instanceof SmaliFile)) {
throw NoDataException.INSTANCE;
}
final ArrayList<Location> locations = new ArrayList<Location>(1);
ApplicationManager.getApplication().runReadAction(new Runnable() {
@Override
public void run() {
String typeName = type.name();
Collection<SmaliClass> classes = SmaliClassNameIndex.INSTANCE.get(typeName, debugProcess.getProject(),
GlobalSearchScope.projectScope(debugProcess.getProject()));
if (classes.size() > 0) {
final SmaliClass smaliClass = classes.iterator().next();
Location location = smaliClass.getLocationForSourcePosition(type, position);
if (location != null) {
locations.add(location);
}
}
}
});
return locations;
}
@Override
public ClassPrepareRequest createPrepareRequest(@NotNull final ClassPrepareRequestor requestor,
@NotNull final SourcePosition position) throws NoDataException {
Computable<Boolean> isSmaliFile = new Computable<Boolean>() {
@Override
public Boolean compute() {
return position.getFile() instanceof SmaliFile;
}
};
ApplicationManager.getApplication().runReadAction(isSmaliFile);
if (!isSmaliFile.compute()) {
throw NoDataException.INSTANCE;
}
String className = getClassFromPosition(position);
return debugProcess.getRequestsManager().createClassPrepareRequest(new ClassPrepareRequestor() {
@Override
public void processClassPrepare(DebugProcess debuggerProcess, ReferenceType referenceType) {
requestor.processClassPrepare(debuggerProcess, referenceType);
}
}, className);
}
}