Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import java.util.concurrent.CompletableFuture;

import com.sun.jdi.Location;
import com.sun.jdi.Method;
import com.sun.jdi.ReferenceType;
import com.sun.jdi.VMDisconnectedException;
import com.sun.jdi.VirtualMachine;
Expand All @@ -38,6 +39,7 @@ public class Breakpoint implements IBreakpoint {
private String condition = null;
private String logMessage = null;
private HashMap<Object, Object> propertyMap = new HashMap<>();
private String methodSignature = null;

Breakpoint(VirtualMachine vm, IEventHub eventHub, String className, int lineNumber) {
this(vm, eventHub, className, lineNumber, 0, null);
Expand All @@ -50,7 +52,12 @@ public class Breakpoint implements IBreakpoint {
Breakpoint(VirtualMachine vm, IEventHub eventHub, String className, int lineNumber, int hitCount, String condition) {
this.vm = vm;
this.eventHub = eventHub;
this.className = className;
if (className != null && className.contains("#")) {
this.className = className.substring(0, className.indexOf("#"));
this.methodSignature = className.substring(className.indexOf("#") + 1);
} else {
this.className = className;
}
this.lineNumber = lineNumber;
this.hitCount = hitCount;
this.condition = condition;
Expand Down Expand Up @@ -234,6 +241,24 @@ private static List<Location> collectLocations(List<ReferenceType> refTypes, int
return locations;
}

private static List<Location> collectLocations(List<ReferenceType> refTypes, String nameAndSignature) {
List<Location> locations = new ArrayList<>();
String[] segments = nameAndSignature.split("#");

for (ReferenceType refType : refTypes) {
List<Method> methods = refType.methods();
for (Method method : methods) {
if (!method.isAbstract() && !method.isNative()
&& segments[0].equals(method.name())
&& (segments[1].equals(method.genericSignature()) || segments[1].equals(method.signature()))) {
locations.add(method.location());
break;
}
}
}
return locations;
}

private List<BreakpointRequest> createBreakpointRequests(ReferenceType refType, int lineNumber, int hitCount,
boolean includeNestedTypes) {
List<ReferenceType> refTypes = new ArrayList<>();
Expand All @@ -243,7 +268,12 @@ private List<BreakpointRequest> createBreakpointRequests(ReferenceType refType,

private List<BreakpointRequest> createBreakpointRequests(List<ReferenceType> refTypes, int lineNumber,
int hitCount, boolean includeNestedTypes) {
List<Location> locations = collectLocations(refTypes, lineNumber, includeNestedTypes);
List<Location> locations;
if (this.methodSignature != null) {
locations = collectLocations(refTypes, this.methodSignature);
} else {
locations = collectLocations(refTypes, lineNumber, includeNestedTypes);
}

// find out the existing breakpoint locations
List<Location> existingLocations = new ArrayList<>(requests.size());
Expand All @@ -268,6 +298,7 @@ private List<BreakpointRequest> createBreakpointRequests(List<ReferenceType> ref
request.addCountFilter(hitCount);
}
request.enable();
request.putProperty(IBreakpoint.REQUEST_TYPE_FUNCTIONAL, Boolean.valueOf(this.methodSignature != null));
newRequests.add(request);
} catch (VMDisconnectedException ex) {
// enable breakpoint operation may be executing while JVM is terminating, thus the VMDisconnectedException may be
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@
import java.util.concurrent.CompletableFuture;

public interface IBreakpoint extends IDebugResource {

String REQUEST_TYPE_FUNCTIONAL = "functional";

String className();

int getLineNumber();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,11 @@
import com.sun.jdi.BooleanValue;
import com.sun.jdi.Field;
import com.sun.jdi.ObjectReference;
import com.sun.jdi.StringReference;
import com.sun.jdi.ReferenceType;
import com.sun.jdi.StringReference;
import com.sun.jdi.ThreadReference;
import com.sun.jdi.Value;
import com.sun.jdi.VMDisconnectedException;
import com.sun.jdi.Value;
import com.sun.jdi.event.BreakpointEvent;
import com.sun.jdi.event.Event;
import com.sun.jdi.event.StepEvent;
Expand Down Expand Up @@ -171,6 +171,11 @@ private IBreakpoint getAssociatedEvaluatableBreakpoint(IDebugAdapterContext cont
).findFirst().orElse(null);
}

private IBreakpoint getAssociatedBreakpoint(IDebugAdapterContext context, BreakpointEvent event) {
return Arrays.asList(context.getBreakpointManager().getBreakpoints()).stream()
.filter(bp -> bp.requests().contains(event.request())).findFirst().orElse(null);
}

private void registerBreakpointHandler(IDebugAdapterContext context) {
IDebugSession debugSession = context.getDebugSession();
if (debugSession != null) {
Expand All @@ -188,7 +193,7 @@ private void registerBreakpointHandler(IDebugAdapterContext context) {

// find the breakpoint related to this breakpoint event
IBreakpoint expressionBP = getAssociatedEvaluatableBreakpoint(context, (BreakpointEvent) event);

boolean functional = (boolean) event.request().getProperty(IBreakpoint.REQUEST_TYPE_FUNCTIONAL);
if (expressionBP != null) {
CompletableFuture.runAsync(() -> {
engine.evaluateForBreakpoint((IEvaluatableBreakpoint) expressionBP, bpThread).whenComplete((value, ex) -> {
Expand All @@ -199,12 +204,14 @@ private void registerBreakpointHandler(IDebugAdapterContext context) {
if (resume) {
debugEvent.eventSet.resume();
} else {
context.getProtocolServer().sendEvent(new Events.StoppedEvent("breakpoint", bpThread.uniqueID()));
context.getProtocolServer().sendEvent(new Events.StoppedEvent(
functional ? "function breakpoint" : "breakpoint", bpThread.uniqueID()));
}
});
});
} else {
context.getProtocolServer().sendEvent(new Events.StoppedEvent("breakpoint", bpThread.uniqueID()));
context.getProtocolServer().sendEvent(new Events.StoppedEvent(
functional ? "function breakpoint" : "breakpoint", bpThread.uniqueID()));
}
debugEvent.shouldResume = false;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/*******************************************************************************
* Copyright (c) 2022 Microsoft Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Gayan Perera (gayanper@gmail.com) - initial API and implementation
*******************************************************************************/
package com.microsoft.java.debug;

import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.IMethodBinding;
import org.eclipse.jdt.core.dom.MethodDeclaration;

@SuppressWarnings("restriction")
public class BreakpointLocationLocator
extends org.eclipse.jdt.internal.debug.core.breakpoints.ValidBreakpointLocationLocator {

private IMethodBinding methodBinding;

public BreakpointLocationLocator(CompilationUnit compilationUnit, int lineNumber, boolean bindingsResolved,
boolean bestMatch) {
super(compilationUnit, lineNumber, bindingsResolved, bestMatch);
}

@Override
public boolean visit(MethodDeclaration node) {
boolean result = super.visit(node);
if (methodBinding == null && getLocationType() == LOCATION_METHOD) {
this.methodBinding = node.resolveBinding();
}
return result;
}

/**
* Returns the signature of method found if the
* {@link org.eclipse.jdt.internal.debug.core.breakpoints.ValidBreakpointLocationLocator#getLocationType()}
* is
* {@link org.eclipse.jdt.internal.debug.core.breakpoints.ValidBreakpointLocationLocator#LOCATION_METHOD}.
* Otherwise return null.
*/
public String getMethodSignature() {
if (this.methodBinding == null) {
return null;
}
return toSignature(this.methodBinding);
}

/**
* Returns the name of method found if the
* {@link org.eclipse.jdt.internal.debug.core.breakpoints.ValidBreakpointLocationLocator#getLocationType()}
* is
* {@link org.eclipse.jdt.internal.debug.core.breakpoints.ValidBreakpointLocationLocator#LOCATION_METHOD}.
* Otherwise return null.
*/
public String getMethodName() {
if (this.methodBinding == null) {
return null;
}
return this.methodBinding.getName();
}

@Override
public String getFullyQualifiedTypeName() {
if (this.methodBinding != null) {
return this.methodBinding.getDeclaringClass().getQualifiedName();
}
return super.getFullyQualifiedTypeName();
}

private String toSignature(IMethodBinding binding) {
// use key for now until JDT core provides a public API for this.
// "Ljava/util/Arrays;.asList<T:Ljava/lang/Object;>([TT;)Ljava/util/List<TT;>;"
// "([Ljava/lang/String;)V|Ljava/lang/InterruptedException;"
String signatureString = binding.getKey();
if (signatureString != null) {
String name = binding.getName();
int index = signatureString.indexOf(name);
if (index > -1) {
int exceptionIndex = signatureString.indexOf("|", signatureString.lastIndexOf(")"));
if (exceptionIndex > -1) {
return signatureString.substring(index + name.length(), exceptionIndex);
}
return signatureString.substring(index + name.length());
}
}
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,6 @@
import java.util.logging.Level;
import java.util.logging.Logger;

import com.microsoft.java.debug.core.Configuration;
import com.microsoft.java.debug.core.DebugException;
import com.microsoft.java.debug.core.adapter.AdapterUtils;
import com.microsoft.java.debug.core.adapter.Constants;
import com.microsoft.java.debug.core.adapter.IDebugAdapterContext;
import com.microsoft.java.debug.core.adapter.ISourceLookUpProvider;

import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.debug.core.sourcelookup.ISourceContainer;
Expand All @@ -48,11 +41,18 @@
import org.eclipse.jdt.core.dom.ASTParser;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.internal.core.JarPackageFragmentRoot;
import org.eclipse.jdt.internal.debug.core.breakpoints.ValidBreakpointLocationLocator;
import org.eclipse.jdt.launching.IVMInstall;
import org.eclipse.jdt.launching.JavaRuntime;
import org.eclipse.jdt.launching.LibraryLocation;

import com.microsoft.java.debug.BreakpointLocationLocator;
import com.microsoft.java.debug.core.Configuration;
import com.microsoft.java.debug.core.DebugException;
import com.microsoft.java.debug.core.adapter.AdapterUtils;
import com.microsoft.java.debug.core.adapter.Constants;
import com.microsoft.java.debug.core.adapter.IDebugAdapterContext;
import com.microsoft.java.debug.core.adapter.ISourceLookUpProvider;

public class JdtSourceLookUpProvider implements ISourceLookUpProvider {
private static final Logger logger = Logger.getLogger(Configuration.LOGGER_NAME);
private static final String JDT_SCHEME = "jdt";
Expand Down Expand Up @@ -162,12 +162,16 @@ public String[] getFullyQualifiedName(String uri, int[] lines, int[] columns) th
// In current stage, we don't support to move the invalid breakpoint down to the next valid location, and just
// mark it as "unverified".
// In future, we could consider supporting to update the breakpoint to a valid location.
ValidBreakpointLocationLocator locator = new ValidBreakpointLocationLocator(astUnit, lines[i], true, true);
BreakpointLocationLocator locator = new BreakpointLocationLocator(astUnit, lines[i], true, true);
astUnit.accept(locator);
// When the final valid line location is same as the original line, that represents it's a valid breakpoint.
// Add location type check to avoid breakpoint on method/field which will never be hit in current implementation.
if (lines[i] == locator.getLineLocation() && locator.getLocationType() == ValidBreakpointLocationLocator.LOCATION_LINE) {
if (lines[i] == locator.getLineLocation()
&& locator.getLocationType() == BreakpointLocationLocator.LOCATION_LINE) {
fqns[i] = locator.getFullyQualifiedTypeName();
} else if (locator.getLocationType() == BreakpointLocationLocator.LOCATION_METHOD) {
fqns[i] = locator.getFullyQualifiedTypeName().concat("#").concat(locator.getMethodName())
.concat("#").concat(locator.getMethodSignature());
}
}
}
Expand Down