/* * 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); } }