Home | History | Annotate | Download | only in debugging
      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 }