Bug 579100 - Add support for lambda breakpoints with outer variables When generating lambda methods for expressions with outer variables, those variable becomes part of lambda method parameters. Now those parameters are considered when matching the method entry debug points. Change-Id: Idb46332d8e9fe3df2a9fb4a8ad4103f92e26b04a Signed-off-by: Gayan Perera <gayanper@gmail.com> Reviewed-on: https://git.eclipse.org/r/c/jdt/eclipse.jdt.debug/+/191517 Tested-by: JDT Bot <jdt-bot@eclipse.org> Reviewed-by: Sarika Sinha <sarika.sinha@in.ibm.com>
diff --git a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/actions/ToggleBreakpointAdapter.java b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/actions/ToggleBreakpointAdapter.java index 2aab5b4..036b040 100644 --- a/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/actions/ToggleBreakpointAdapter.java +++ b/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/actions/ToggleBreakpointAdapter.java
@@ -269,7 +269,7 @@ job.schedule(); } - public void toggleLambdaEntryMethodBreakpoints(final IWorkbenchPart part, final ISelection finalSelection, final String lambdaMethodName) { + public void toggleLambdaEntryMethodBreakpoints(final IWorkbenchPart part, final ISelection finalSelection, final String lambdaMethodName, final String lambdaMethodSignature) { Job job = new Job("Toggle Lambda Entry Method Breakpoints") { //$NON-NLS-1$ @Override protected IStatus run(IProgressMonitor monitor) { @@ -277,7 +277,7 @@ return Status.CANCEL_STATUS; } try { - return doToggleLambdaEntryMethodBreakpoints(part, finalSelection, lambdaMethodName, monitor); + return doToggleLambdaEntryMethodBreakpoints(part, finalSelection, lambdaMethodName, lambdaMethodSignature, monitor); } catch (CoreException e) { return e.getStatus(); } finally { @@ -290,7 +290,7 @@ job.schedule(); } - static IStatus doToggleLambdaEntryMethodBreakpoints(IWorkbenchPart part, ISelection selection, String lambdaMethodName, IProgressMonitor monitor) throws CoreException { + static IStatus doToggleLambdaEntryMethodBreakpoints(IWorkbenchPart part, ISelection selection, String lambdaMethodName, String lambdaMethodSignature, IProgressMonitor monitor) throws CoreException { ITextEditor textEditor = getTextEditor(part); if (textEditor == null || !(selection instanceof ITextSelection)) { return Status.OK_STATUS; @@ -316,7 +316,7 @@ } if (method != null) { - doToggleMethodBreakpoint(method, lambdaMethodName, part, selection, monitor); + doToggleMethodBreakpoint(method, lambdaMethodName, lambdaMethodSignature, part, selection, monitor); } else { BreakpointToggleUtils.report(ActionMessages.LambdaEntryBreakpointToggleAction_Unavailable, part); } @@ -354,7 +354,7 @@ } if (method != null) { - doToggleMethodBreakpoint(method, loc.getLambdaMethodName(), part, selection, monitor); + doToggleMethodBreakpoint(method, loc.getLambdaMethodName(), loc.getfLambdaMethodSignature(), part, selection, monitor); } else { ValidBreakpointLocationLocator locNew = new ValidBreakpointLocationLocator(loc.getCompilationUnit(), textSelection.getStartLine() + 1, true, true); @@ -392,10 +392,10 @@ } private static void doToggleMethodBreakpoint(IMethod member, IWorkbenchPart part, ISelection finalSelection, IProgressMonitor monitor) throws CoreException { - doToggleMethodBreakpoint(member, null, part, finalSelection, monitor); + doToggleMethodBreakpoint(member, null, null, part, finalSelection, monitor); } - private static void doToggleMethodBreakpoint(IMethod member, String lambdaMethodName, IWorkbenchPart part, ISelection finalSelection, IProgressMonitor monitor) throws CoreException { + private static void doToggleMethodBreakpoint(IMethod member, String lambdaMethodName, String lambdaMethodSignature, IWorkbenchPart part, ISelection finalSelection, IProgressMonitor monitor) throws CoreException { IJavaBreakpoint breakpoint = getMethodBreakpoint(member); if (breakpoint != null) { if (BreakpointToggleUtils.isToggleTracepoints()) { @@ -416,15 +416,15 @@ Map<String, Object> attributes = new HashMap<>(10); BreakpointUtils.addJavaBreakpointAttributes(attributes, member); IType type = member.getDeclaringType(); - String signature = member.getSignature(); String mname = Optional.ofNullable(lambdaMethodName).orElse(member.getElementName()); + String signature = Optional.ofNullable(lambdaMethodSignature).orElse(member.getSignature()); if (member.isConstructor()) { mname = "<init>"; //$NON-NLS-1$ if (type.isEnum()) { signature = "(Ljava.lang.String;I" + signature.substring(1); //$NON-NLS-1$ } } - if (!type.isBinary()) { + if (!type.isBinary() && lambdaMethodName == null) { signature = resolveMethodSignature(member); if (signature == null) { BreakpointToggleUtils.report(ActionMessages.ManageMethodBreakpointActionDelegate_methodNonAvailable, part); @@ -1601,7 +1601,7 @@ return; } ITextSelection textSelection = new TextSelection(document, firstLambda.getNodeOffset(), firstLambda.getNodeLength()); - toggleLambdaEntryMethodBreakpoints(part, textSelection, firstLambda.getLambdaMethodName()); + toggleLambdaEntryMethodBreakpoints(part, textSelection, firstLambda.getLambdaMethodName(), firstLambda.getfLambdaMethodSignature()); } catch (BadLocationException e) { BreakpointToggleUtils.report(ActionMessages.LambdaEntryBreakpointToggleAction_Unavailable, part); }
diff --git a/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/breakpoints/FirstLambdaLocationLocator.java b/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/breakpoints/FirstLambdaLocationLocator.java index 9f743a2..0013374 100644 --- a/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/breakpoints/FirstLambdaLocationLocator.java +++ b/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/breakpoints/FirstLambdaLocationLocator.java
@@ -23,6 +23,7 @@ private int fLineOffset = -1; private int fLineEndPosition = -1; private String fLambdaMethodName; + private String fLambdaMethodSignature; private boolean fLocationFound = false; public FirstLambdaLocationLocator(int lineOffset, int lineEndPosition) { @@ -37,6 +38,15 @@ return fLambdaMethodName; } + /** + * Return of the signature of the lambda method where the valid location is. + * The signature is computed to be compatible with the final lambda method with + * method arguments and outer local variables. + */ + public String getfLambdaMethodSignature() { + return fLambdaMethodSignature; + } + public int getNodeLength() { return fNodeLength; } @@ -57,15 +67,10 @@ fNodeOffset = node.getStartPosition(); IMethodBinding methodBinding = node.resolveMethodBinding(); if (methodBinding != null) { - fLambdaMethodName = toMethodName(methodBinding); + fLambdaMethodName = LambdaLocationLocatorHelper.toMethodName(methodBinding); + fLambdaMethodSignature = LambdaLocationLocatorHelper.toMethodSignature(methodBinding); fLocationFound = true; } return false; } - - private String toMethodName(IMethodBinding methodBinding) { - String key = methodBinding.getKey(); - return key.substring(key.indexOf('.') + 1, key.indexOf('(')); - } - }
diff --git a/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/breakpoints/LambdaLocationLocatorHelper.java b/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/breakpoints/LambdaLocationLocatorHelper.java new file mode 100644 index 0000000..4d8b542 --- /dev/null +++ b/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/breakpoints/LambdaLocationLocatorHelper.java
@@ -0,0 +1,61 @@ +/******************************************************************************* + * Copyright (c) 2022 Gayan Perera and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Gayan Perera - initial API and implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.debug.core.breakpoints; + +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.eclipse.jdt.core.Signature; +import org.eclipse.jdt.core.dom.IMethodBinding; +import org.eclipse.jdt.core.dom.ITypeBinding; + +public final class LambdaLocationLocatorHelper { + + private LambdaLocationLocatorHelper() { + } + + /** + * Return of the signature of the lambda method. The signature is computed to + * be compatible with the final lambda method with method arguments and outer + * local variables in debugger. + */ + public static String toMethodSignature(IMethodBinding methodBinding) { + StringBuilder builder = new StringBuilder(); + builder.append('('); + if (methodBinding.getParameterTypes().length > 0 || methodBinding.getSyntheticOuterLocals().length > 0) { + builder.append(Stream.of(methodBinding.getSyntheticOuterLocals()) + .map(b -> Signature.createTypeSignature(qualifiedName(b.getType()), true)) + .collect(Collectors.joining())); + + builder.append(Stream.of(methodBinding.getParameterTypes()) + .map(b -> Signature.createTypeSignature(qualifiedName(b), true)) + .collect(Collectors.joining())); + } + builder.append(')'); + builder.append(Signature.createTypeSignature(qualifiedName(methodBinding.getReturnType()), true)); + return builder.toString(); + } + + /** + * Return the lambda method name from the given method binding. + */ + public static String toMethodName(IMethodBinding methodBinding) { + String key = methodBinding.getKey(); + return key.substring(key.indexOf('.') + 1, key.indexOf('(')); + } + + private static String qualifiedName(ITypeBinding binding) { + return binding.getQualifiedName().replace('.', '/'); + } +}
diff --git a/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/breakpoints/ValidBreakpointLocationLocator.java b/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/breakpoints/ValidBreakpointLocationLocator.java index 7691d94..e758b1e 100644 --- a/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/breakpoints/ValidBreakpointLocationLocator.java +++ b/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/breakpoints/ValidBreakpointLocationLocator.java
@@ -140,6 +140,7 @@ private boolean fLocationFound; private boolean fLambdaVisited; private String fLambdaMethodName; + private String fLambdaMethodSignature; private String fTypeName; private int fLineLocation; private int fMemberOffset; @@ -227,6 +228,16 @@ public String getLambdaMethodName() { return fLambdaMethodName; } + + /** + * Return of the signature of the lambda method where the valid location is. + * The signature is computed to be compatible with the final lambda method with + * method arguments and outer local variables. + */ + public String getfLambdaMethodSignature() { + return fLambdaMethodSignature; + } + /** * Return the line number of the computed valid location */ @@ -1024,7 +1035,8 @@ if (methodBinding != null) { fLambdaVisited = true; fLocationType = LOCATION_LAMBDA_METHOD; - fLambdaMethodName = toMethodName(methodBinding); + fLambdaMethodName = LambdaLocationLocatorHelper.toMethodName(methodBinding); + fLambdaMethodSignature = LambdaLocationLocatorHelper.toMethodSignature(methodBinding); fLocationFound = true; return false; } @@ -1062,11 +1074,6 @@ return visit(node, true); } - private String toMethodName(IMethodBinding methodBinding) { - String key = methodBinding.getKey(); - return key.substring(key.indexOf('.') + 1, key.indexOf('(')); - } - /* * (non-Javadoc) *