Bug 534687 - [1.8] Lambda variable names in variables view is shown
as arg$1 etc
Debugger shows captured lambda variable names as val$outerArg instead of
arg$1 etc.
Change-Id: I84b6e65b3db99fd7ed53ab2c82df437d4dc61391
Signed-off-by: Andrey Loskutov <loskutov@gmx.de>
diff --git a/org.eclipse.jdt.debug.tests/java8/DebugHoverTest18.java b/org.eclipse.jdt.debug.tests/java8/DebugHoverTest18.java
index 9ecd81b..d712774 100644
--- a/org.eclipse.jdt.debug.tests/java8/DebugHoverTest18.java
+++ b/org.eclipse.jdt.debug.tests/java8/DebugHoverTest18.java
@@ -34,9 +34,9 @@
System.out.println(arg);
run(()->{
- String var3 = "v2";
- System.out.println(var2);
+ String var3 = "v3";
System.out.println(var3);
+ System.out.println(var2);
System.out.println(var1);
System.out.println(arg);
});
diff --git a/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/ui/DebugHoverTests.java b/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/ui/DebugHoverTests.java
index d9177b6..ac271dd 100644
--- a/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/ui/DebugHoverTests.java
+++ b/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/ui/DebugHoverTests.java
@@ -31,7 +31,7 @@
import org.eclipse.jdt.debug.core.IJavaThread;
import org.eclipse.jdt.debug.tests.TestUtil;
import org.eclipse.jdt.debug.ui.IJavaDebugUIConstants;
-import org.eclipse.jdt.internal.debug.core.model.JDILocalVariable;
+import org.eclipse.jdt.internal.debug.core.model.JDIModificationVariable;
import org.eclipse.jdt.internal.debug.core.model.JDIObjectValue;
import org.eclipse.jdt.internal.debug.core.model.JDIValue;
import org.eclipse.jdt.internal.debug.ui.JDIDebugUIPlugin;
@@ -50,6 +50,8 @@
*/
public class DebugHoverTests extends AbstractDebugUiTests {
+ private static final String VAL_PREFIX = new String(org.eclipse.jdt.internal.compiler.lookup.TypeConstants.SYNTHETIC_OUTER_LOCAL_PREFIX);
+
public static Test suite() {
return new OrderedTestSuite(DebugHoverTests.class);
}
@@ -109,9 +111,9 @@
hover.setEditor(part);
Map<String, Region> offsets = new LinkedHashMap<>();
- offsets.put("arg", new Region(1059, "arg".length()));
- offsets.put("var1", new Region(1030, "var1".length()));
- offsets.put("var2", new Region(1001, "var2".length()));
+ offsets.put(VAL_PREFIX + "arg", new Region(1059, "arg".length()));
+ offsets.put(VAL_PREFIX + "var1", new Region(1030, "var1".length()));
+ offsets.put(/* local */ "var2", new Region(1001, "var2".length()));
Set<Entry<String, Region>> entrySet = offsets.entrySet();
int startLine = bpLine1;
@@ -125,12 +127,10 @@
part = openEditorAndValidateStack(expectedMethod2, framesNumber2, file, thread);
offsets = new LinkedHashMap<>();
- offsets.put("arg", new Region(1216, "arg".length()));
- offsets.put("var1", new Region(1186, "var1".length()));
- offsets.put("var3", new Region(1156, "var3".length()));
- // This will not work yet, I have no idea how to identify parent
- // lambda element...
- // offsets.put("var2", new Region(1108, "var2".length()));
+ offsets.put(VAL_PREFIX + "arg", new Region(1216, "arg".length()));
+ offsets.put(VAL_PREFIX + "var1", new Region(1186, "var1".length()));
+ offsets.put(VAL_PREFIX + "var2", new Region(1156, "var2".length()));
+ offsets.put(/* local */ "var3", new Region(1126, "var3".length()));
entrySet = offsets.entrySet();
startLine = bpLine2;
@@ -166,15 +166,16 @@
}
private void validateLine(final int line, int valueIndex, CompilationUnitEditor part, JavaDebugHover hover, Entry<String, Region> varData) throws Exception, DebugException {
- String variableName = varData.getKey();
+ String debugVarName = varData.getKey();
+ String variableName = debugVarName.startsWith(VAL_PREFIX) ? debugVarName.substring(VAL_PREFIX.length()) : debugVarName;
IRegion region = varData.getValue();
String text = selectAndReveal(part, line, region);
assertEquals(variableName, text);
Object args = sync(() -> hover.getHoverInfo2(part.getViewer(), region));
assertNotNull(args);
- JDILocalVariable var = (JDILocalVariable) args;
- assertEquals(variableName, var.getName());
+ JDIModificationVariable var = (JDIModificationVariable) args;
+ assertEquals(debugVarName, var.getName());
JDIValue value = (JDIValue) var.getValue();
assertEquals(JDIObjectValue.class, value.getClass());
JDIObjectValue valueObj = (JDIObjectValue) var.getValue();
diff --git a/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/model/JDIStackFrame.java b/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/model/JDIStackFrame.java
index e292189..9a56d7e 100644
--- a/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/model/JDIStackFrame.java
+++ b/org.eclipse.jdt.debug/model/org/eclipse/jdt/internal/debug/core/model/JDIStackFrame.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2018 IBM Corporation and others.
+ * Copyright (c) 2000, 2019 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
@@ -19,7 +19,9 @@
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
+import java.util.ListIterator;
+import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
@@ -32,9 +34,19 @@
import org.eclipse.debug.core.model.ITerminate;
import org.eclipse.debug.core.model.IThread;
import org.eclipse.debug.core.model.IVariable;
+import org.eclipse.jdi.internal.FieldImpl;
+import org.eclipse.jdi.internal.ReferenceTypeImpl;
import org.eclipse.jdi.internal.ValueImpl;
import org.eclipse.jdi.internal.VirtualMachineImpl;
+import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.Signature;
+import org.eclipse.jdt.core.dom.AST;
+import org.eclipse.jdt.core.dom.ASTParser;
+import org.eclipse.jdt.core.dom.ASTVisitor;
+import org.eclipse.jdt.core.dom.CompilationUnit;
+import org.eclipse.jdt.core.dom.IMethodBinding;
+import org.eclipse.jdt.core.dom.IVariableBinding;
+import org.eclipse.jdt.core.dom.LambdaExpression;
import org.eclipse.jdt.debug.core.IJavaClassType;
import org.eclipse.jdt.debug.core.IJavaModifiers;
import org.eclipse.jdt.debug.core.IJavaObject;
@@ -44,6 +56,7 @@
import org.eclipse.jdt.debug.core.IJavaValue;
import org.eclipse.jdt.debug.core.IJavaVariable;
import org.eclipse.jdt.internal.debug.core.JDIDebugPlugin;
+import org.eclipse.jdt.internal.debug.core.JavaDebugUtils;
import org.eclipse.jdt.internal.debug.core.logicalstructures.JDILambdaVariable;
import org.eclipse.jdt.internal.debug.core.logicalstructures.JDIReturnValueVariable;
import org.eclipse.jdt.internal.debug.core.model.MethodResult.ResultType;
@@ -128,6 +141,9 @@
*/
private boolean fIsTop;
+ @SuppressWarnings("restriction")
+ private static final String SYNTHETIC_OUTER_LOCAL_PREFIX = new String(org.eclipse.jdt.internal.compiler.lookup.TypeConstants.SYNTHETIC_OUTER_LOCAL_PREFIX);
+
/**
* Creates a new stack frame in the given thread.
*
@@ -366,7 +382,9 @@
int previousIndex = frames.indexOf(this) + 1;
if (previousIndex > 0 && previousIndex < frames.size()) {
IJavaStackFrame previousFrame = frames.get(previousIndex);
- IJavaValue closureValue = JDIValue.createValue((JDIDebugTarget) getDebugTarget(), ((JDIStackFrame) previousFrame).getUnderlyingThisObject());
+ ObjectReference underlyingThisObject = ((JDIStackFrame) previousFrame).getUnderlyingThisObject();
+ IJavaValue closureValue = JDIValue.createValue((JDIDebugTarget) getDebugTarget(), underlyingThisObject);
+ setLambdaVariableNames(closureValue, underlyingThisObject);
fVariables.add(new JDILambdaVariable(closureValue));
}
}
@@ -386,6 +404,73 @@
}
}
+ private void setLambdaVariableNames(IJavaValue value, ObjectReference underlyingThisObject) {
+ try {
+ IType type = JavaDebugUtils.resolveType(value.getJavaType());
+ ASTParser parser = ASTParser.newParser(AST.JLS11);
+ parser.setResolveBindings(true);
+ parser.setSource(type.getTypeRoot());
+ CompilationUnit cu = (CompilationUnit) parser.createAST(null);
+ cu.accept(new LambdaASTVisitor(false, underlyingThisObject, getUnderlyingMethod()));
+ } catch (CoreException e) {
+ logError(e);
+ }
+ }
+
+ private final static class LambdaASTVisitor extends ASTVisitor {
+ private final ObjectReference underlyingThisObject;
+ private Method underlyingMethod;
+
+ private LambdaASTVisitor(boolean visitDocTags, ObjectReference underlyingThisObject, Method underlyingMethod) {
+ super(visitDocTags);
+ this.underlyingThisObject = underlyingThisObject;
+ this.underlyingMethod = underlyingMethod;
+ }
+
+ @Override
+ public boolean visit(LambdaExpression lambdaExpression) {
+ IMethodBinding binding = lambdaExpression.resolveMethodBinding();
+ IVariableBinding[] synVars = binding.getSyntheticOuterLocals();
+ if (synVars == null || synVars.length == 0) {// name cannot be updated if Synthetic Outer Locals are not available
+ return true;
+ }
+ List<Field> allFields = underlyingThisObject.referenceType().fields();
+ ListIterator<Field> listIterator = allFields.listIterator();
+ int i = 0;
+ if (underlyingMethod.isStatic()) {
+ if (synVars.length == allFields.size()) {
+ while (listIterator.hasNext()) {
+ FieldImpl field = (FieldImpl) listIterator.next();
+ String newName = synVars[i].getName();
+ FieldImpl newField = createRenamedCopy(field, newName);
+ listIterator.set(newField);
+ i++;
+ }
+ }
+ } else {
+ if (synVars.length + 1 == allFields.size()) {
+ while (listIterator.hasNext()) {
+ FieldImpl field = (FieldImpl) listIterator.next();
+ String newName = field.name();
+ if (i == 0) {
+ newName = "this"; //$NON-NLS-1$
+ } else {
+ newName = synVars[i - 1].getName();
+ }
+ FieldImpl newField = createRenamedCopy(field, newName);
+ listIterator.set(newField);
+ i++;
+ }
+ }
+ }
+ return true;
+ }
+
+ private FieldImpl createRenamedCopy(FieldImpl field, String newName) {
+ return new FieldImpl((VirtualMachineImpl) field.virtualMachine(), (ReferenceTypeImpl) field.declaringType(), field.getFieldID(), newName, field.signature(), field.genericSignature(), field.modifiers());
+ }
+ }
+
/**
* If there is a return value from a "step return" that belongs to this frame, insert it as first element
*
@@ -791,6 +876,19 @@
// save for later - check for instance and static variables
thisVariable = var;
}
+ if (var instanceof JDILambdaVariable) {
+ // Check if we have match in synthetic fields generated
+ // by compiler for the captured variables (they start with "val$")
+ JDILambdaVariable lambda = (JDILambdaVariable) var;
+ JDIObjectValue ov = (JDIObjectValue) lambda.getValue();
+ IVariable[] lvars = ov.getVariables();
+ for (IVariable lv : lvars) {
+ String name = lv.getName();
+ if (name.startsWith(SYNTHETIC_OUTER_LOCAL_PREFIX) && (SYNTHETIC_OUTER_LOCAL_PREFIX + varName).equals(name)) {
+ possibleMatches.add((IJavaVariable) lv);
+ }
+ }
+ }
}
for(IJavaVariable variable: possibleMatches){
// Local Variable has more preference than Field Variable