Skip to content

Commit

Permalink
Enable stepInTarget feature on more use cases
Browse files Browse the repository at this point in the history
  • Loading branch information
testforstephen committed Oct 27, 2022
1 parent 69230b3 commit a6ca87a
Show file tree
Hide file tree
Showing 8 changed files with 459 additions and 228 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@
import com.microsoft.java.debug.core.JavaBreakpointLocation;
import com.microsoft.java.debug.core.protocol.Types.SourceBreakpoint;

import com.sun.jdi.StackFrame;

public interface ISourceLookUpProvider extends IProvider {
boolean supportsRealtimeBreakpointVerification();

Expand Down Expand Up @@ -70,17 +68,19 @@ default String getJavaRuntimeVersion(String projectName) {
* Return method invocation found in the statement as the given line number of
* the source file.
*
* @param stackframe The stack frame where the invocation must be searched.
* @param uri The source file where the invocation must be searched.
* @param line The line number where the invocation must be searched.
*
* @return List of found method invocation or empty if not method invocations
* can be found.
*/
List<MethodInvocation> findMethodInvocations(StackFrame stackframe);
List<MethodInvocation> findMethodInvocations(String uri, int line);

public static class MethodInvocation {
public String expression;
public String methodName;
public String methodSignature;
public String methodGenericSignature;
public String declaringTypeName;
public int lineStart;
public int lineEnd;
Expand All @@ -89,27 +89,24 @@ public static class MethodInvocation {

@Override
public int hashCode() {
return Objects.hash(columnEnd, columnStart, declaringTypeName, expression, lineEnd, lineStart, methodName,
methodSignature);
return Objects.hash(expression, methodName, methodSignature, methodGenericSignature, declaringTypeName,
lineStart, lineEnd, columnStart, columnEnd);
}

@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
if (!(obj instanceof MethodInvocation)) {
return false;
}
MethodInvocation other = (MethodInvocation) obj;
return columnEnd == other.columnEnd && columnStart == other.columnStart
&& Objects.equals(declaringTypeName, other.declaringTypeName)
&& Objects.equals(expression, other.expression) && lineEnd == other.lineEnd
&& lineStart == other.lineStart && Objects.equals(methodName, other.methodName)
&& Objects.equals(methodSignature, other.methodSignature);
return Objects.equals(expression, other.expression) && Objects.equals(methodName, other.methodName)
&& Objects.equals(methodSignature, other.methodSignature)
&& Objects.equals(methodGenericSignature, other.methodGenericSignature)
&& Objects.equals(declaringTypeName, other.declaringTypeName) && lineStart == other.lineStart
&& lineEnd == other.lineEnd && columnStart == other.columnStart && columnEnd == other.columnEnd;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -89,10 +89,12 @@ public CompletableFuture<Response> handle(Command command, Arguments arguments,
StackFrame[] frames = context.getStackFrameManager().reloadStackFrames(thread, stacktraceArgs.startFrame, count);
List<StackFrameInfo> jdiFrames = resolveStackFrameInfos(frames, context.asyncJDWP());
for (int i = 0; i < count; i++) {
StackFrameReference stackframe = new StackFrameReference(thread, stacktraceArgs.startFrame + i);
int frameId = context.getRecyclableIdPool().addObject(stacktraceArgs.threadId, stackframe);
StackFrameReference frameReference = new StackFrameReference(thread, stacktraceArgs.startFrame + i);
int frameId = context.getRecyclableIdPool().addObject(stacktraceArgs.threadId, frameReference);
StackFrameInfo jdiFrame = jdiFrames.get(i);
result.add(convertDebuggerStackFrameToClient(jdiFrame, frameId, i == 0, context));
Types.StackFrame lspFrame = convertDebuggerStackFrameToClient(jdiFrame, frameId, i == 0, context);
result.add(lspFrame);
frameReference.setSource(lspFrame.source);
}
} catch (IncompatibleThreadStateException | IndexOutOfBoundsException | URISyntaxException
| AbsentInformationException | ObjectCollectedException
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,19 @@
*******************************************************************************/
package com.microsoft.java.debug.core.adapter.handler;

import java.io.File;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.microsoft.java.debug.core.Configuration;
import com.microsoft.java.debug.core.adapter.AdapterUtils;
import com.microsoft.java.debug.core.adapter.IDebugAdapterContext;
import com.microsoft.java.debug.core.adapter.IDebugRequestHandler;
import com.microsoft.java.debug.core.adapter.ISourceLookUpProvider;
Expand All @@ -27,10 +33,14 @@
import com.microsoft.java.debug.core.protocol.Requests.Command;
import com.microsoft.java.debug.core.protocol.Requests.StepInTargetsArguments;
import com.microsoft.java.debug.core.protocol.Responses.StepInTargetsResponse;
import com.microsoft.java.debug.core.protocol.Types.Source;
import com.microsoft.java.debug.core.protocol.Types.StepInTarget;
import com.sun.jdi.AbsentInformationException;
import com.sun.jdi.ReferenceType;
import com.sun.jdi.StackFrame;

public class StepInTargetsRequestHandler implements IDebugRequestHandler {
private static final Logger logger = Logger.getLogger(Configuration.LOGGER_NAME);

@Override
public List<Command> getTargetCommands() {
Expand All @@ -45,37 +55,84 @@ public CompletableFuture<Response> handle(Command command, Arguments arguments,
final int frameId = stepInTargetsArguments.frameId;
return CompletableFuture.supplyAsync(() -> {
response.body = new StepInTargetsResponse(
findFrame(frameId, context).map(f -> findTargets(f.thread().uniqueID(), f, context))
findFrame(frameId, context).map(f -> findTargets(f, context))
.orElse(Collections.emptyList()).toArray(StepInTarget[]::new));
return response;
});
}

private Optional<StackFrame> findFrame(int frameId, IDebugAdapterContext context) {
private Optional<StackFrameReference> findFrame(int frameId, IDebugAdapterContext context) {
Object object = context.getRecyclableIdPool().getObjectById(frameId);
if (object instanceof StackFrameReference) {
return Optional.of(context.getStackFrameManager().getStackFrame((StackFrameReference) object));
return Optional.of((StackFrameReference) object);
}
return Optional.empty();
}

private List<StepInTarget> findTargets(long threadId, StackFrame stackframe, IDebugAdapterContext context) {
private List<StepInTarget> findTargets(StackFrameReference frameReference, IDebugAdapterContext context) {
StackFrame stackframe = context.getStackFrameManager().getStackFrame(frameReference);
if (stackframe == null) {
return Collections.emptyList();
}

Source source = frameReference.getSource() == null ? findSource(stackframe, context) : frameReference.getSource();
if (source == null) {
return Collections.emptyList();
}

String sourceUri = AdapterUtils.convertPath(source.path, AdapterUtils.isUri(source.path), true);
if (sourceUri == null) {
return Collections.emptyList();
}

ISourceLookUpProvider sourceLookUpProvider = context.getProvider(ISourceLookUpProvider.class);
List<MethodInvocation> invocations = sourceLookUpProvider.findMethodInvocations(stackframe);
List<MethodInvocation> invocations = sourceLookUpProvider.findMethodInvocations(sourceUri, stackframe.location().lineNumber());
if (invocations.isEmpty()) {
return Collections.emptyList();
}

long threadId = stackframe.thread().uniqueID();
List<StepInTarget> targets = new ArrayList<>(invocations.size());
for (MethodInvocation methodInvocation : invocations) {
int id = context.getRecyclableIdPool().addObject(threadId, methodInvocation);
StepInTarget target = new StepInTarget(id, methodInvocation.expression);
target.column = methodInvocation.columnStart;
target.endColumn = methodInvocation.columnEnd;
target.line = methodInvocation.lineStart;
target.endLine = methodInvocation.lineEnd;
target.column = AdapterUtils.convertColumnNumber(methodInvocation.columnStart,
context.isDebuggerColumnsStartAt1(), context.isClientColumnsStartAt1());
target.endColumn = AdapterUtils.convertColumnNumber(methodInvocation.columnEnd,
context.isDebuggerColumnsStartAt1(), context.isClientColumnsStartAt1());
target.line = AdapterUtils.convertLineNumber(methodInvocation.lineStart,
context.isDebuggerLinesStartAt1(), context.isClientLinesStartAt1());
target.endLine = AdapterUtils.convertLineNumber(methodInvocation.lineEnd,
context.isDebuggerLinesStartAt1(), context.isClientLinesStartAt1());
targets.add(target);
}

// TODO remove the executed method calls.
return targets;
}

private Source findSource(StackFrame frame, IDebugAdapterContext context) {
ReferenceType declaringType = frame.location().declaringType();
String typeName = declaringType.name();
String sourceName = null;
String sourcePath = null;
try {
// When the .class file doesn't contain source information in meta data,
// invoking ReferenceType#sourceName() would throw AbsentInformationException.
sourceName = declaringType.sourceName();
sourcePath = declaringType.sourcePaths(null).get(0);
} catch (AbsentInformationException e) {
String enclosingType = AdapterUtils.parseEnclosingType(typeName);
sourceName = enclosingType.substring(enclosingType.lastIndexOf('.') + 1) + ".java";
sourcePath = enclosingType.replace('.', File.separatorChar) + ".java";
}

try {
return StackTraceRequestHandler.convertDebuggerSourceToClient(typeName, sourceName, sourcePath, context);
} catch (URISyntaxException e) {
logger.log(Level.SEVERE, "Failed to resolve the source info of the stack frame.", e);
}

return null;
}
}
Loading

0 comments on commit a6ca87a

Please sign in to comment.