Bug 448473: Support functional expressions in Modular projects
Also: Provide access to non-system classes, so you can use static
fields, static method references, expression method references (like
obj::someMethod) as long as they don't reference "this", creation
references (MyClass::new) and type method references (MyType::myMethod
or MyType::myStaticMethod).
Access to public fields and methods of enclosing instances
Improved error handling for certain edge cases.
Change-Id: Ibbbfa6c81b1a9763be6b76f4dac7860a55d7c1b6
diff --git a/org.eclipse.jdt.debug.tests/java8/FunctionalCaptureTest18.java b/org.eclipse.jdt.debug.tests/java8/FunctionalCaptureTest18.java
index 32ef5ca..1a35ce4 100644
--- a/org.eclipse.jdt.debug.tests/java8/FunctionalCaptureTest18.java
+++ b/org.eclipse.jdt.debug.tests/java8/FunctionalCaptureTest18.java
@@ -84,43 +84,47 @@
assertFunctionalExpression(n -> n - parameter, parameter, 0);
/* Capture instance fields */
- /* But not yet on project's types */
- assertFunctionalExpression(n -> n - publicField, 2, 1);/* SKIP */
+ /* But not yet on enclosing instance's instance fields */
+ assertFunctionalExpression(n -> n - publicField, 2, 1);
+ assertFunctionalExpression(n -> n - this.publicField, 2, 1);
assertFunctionalExpression(n -> n - privateField, 4, 2);/* SKIP */
+ assertFunctionalExpression(n -> n - this.privateField, 4, 2);/* SKIP */
/* Capture static fields */
- /* But not yet on project's types */
- assertFunctionalExpression(n -> n - publicStaticField, 6, 3);/* SKIP */
+ /* But not yet on non-public fields */
+ assertFunctionalExpression(n -> n - publicStaticField, 6, 3);
assertFunctionalExpression(n -> n - privateStaticField, 8, 4);/* SKIP */
/* Evaluate unbound method references */
- assertFunctionalExpression(FunctionalCaptureTest18::publicMethod, this, 5);/* SKIP */
+ /* But not yet on project's non-public methods */
+ assertFunctionalExpression(FunctionalCaptureTest18::publicMethod, this, 5);
assertFunctionalExpression(FunctionalCaptureTest18::privateMethod, this, 6);/* SKIP */
/* Evaluate instance method references */
assertFunctionalExpression("Hello, "::concat, "World", "Hello, World");
- /* But not yet on project's types */
- assertFunctionalExpression(this::publicArgMethod, 1, 7);/* SKIP */
+ /* But not yet this-references */
+ assertFunctionalExpression(this::publicArgMethod, 1, 7);
assertFunctionalExpression(this::privateArgMethod, 1, 8);/* SKIP */
/* Evaluate static method references */
assertFunctionalExpression(Integer::valueOf, "16", 16);
- /* But not yet on project's types */
- assertFunctionalExpression(FunctionalCaptureTest18::publicStaticMethod, 17, 9);/* SKIP */
+ assertFunctionalExpression(FunctionalCaptureTest18::publicStaticMethod, 17, 9);
+ /* But not yet on project's non-public methods */
assertFunctionalExpression(FunctionalCaptureTest18::privateStaticMethod, 19, 10);/* SKIP */
/* Capture methods */
assertFunctionalExpression(s -> Integer.valueOf(s, 16), "0B", 11);
- /* But not yet on project's types */
- assertFunctionalExpression(obj -> obj.publicMethod() + 7, this, 12);/* SKIP */
- assertFunctionalExpression(obj -> this.privateMethod() + 7, this, 13);/* SKIP */
- assertFunctionalExpression(obj -> obj.publicMethod() + 9, this, 14);/* SKIP */
+ /* But not yet directlt on the instance */
+ assertFunctionalExpression(obj -> obj.publicMethod() + 7, this, 12);
+ assertFunctionalExpression(obj -> this.publicMethod() + 8, this, 13);
+ assertFunctionalExpression(obj -> publicMethod() + 8, this, 13);
+ assertFunctionalExpression(obj -> obj.privateMethod() + 8, this, 14);/* SKIP */
assertFunctionalExpression(obj -> this.privateMethod() + 9, this, 15);/* SKIP */
+ assertFunctionalExpression(obj -> privateMethod() + 9, this, 15);/* SKIP */
/* Constructor references */
assertFunctionalExpression(String::new, new char[] { 'a','b','c' }, "abc");
- /* But not yet on project's types */
- assertFunctionalExpression(FunctionalCaptureTest18::new, 42, new FunctionalCaptureTest18(42));/* SKIP */
+ assertFunctionalExpression(FunctionalCaptureTest18::new, 42, new FunctionalCaptureTest18(42));
/* END OF TESTS */
System.out.println("OK");
diff --git a/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/EvaluationMessages.java b/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/EvaluationMessages.java
index b7b034b..bee3ae2 100644
--- a/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/EvaluationMessages.java
+++ b/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/EvaluationMessages.java
@@ -28,8 +28,8 @@
public static String LocalEvaluationEngine_Evaluation_failed___unable_to_initialize_local_variables__6;
public static String LocalEvaluationEngine_Evaluation_failed___unable_to_initialize___this___context__5;
public static String LocalEvaluationEngine_Evaluation_failed___unable_to_initialize_local_variables__4;
- public static String RemoteEvaluationEngine_Evaluation_failed___unable_to_load_in_modular_project;
-
+ public static String RemoteEvaluationEngine_Evaluation_failed___unable_to_instantiate_snippet_class;
+ public static String RemoteEvaluationEngine_Evaluation_failed___unable_to_find_injected_class;
static {
// load message values from bundle file
NLS.initializeMessages(BUNDLE_NAME, EvaluationMessages.class);
diff --git a/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/EvaluationMessages.properties b/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/EvaluationMessages.properties
index d9cac7c..b970e07 100644
--- a/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/EvaluationMessages.properties
+++ b/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/EvaluationMessages.properties
@@ -22,4 +22,5 @@
LocalEvaluationEngine_Evaluation_failed___unable_to_initialize_local_variables__6=Evaluation failed - unable to initialize local variables.
LocalEvaluationEngine_Evaluation_failed___unable_to_initialize___this___context__5=Evaluation failed - unable to initialize \'this\' context.
LocalEvaluationEngine_Evaluation_failed___unable_to_initialize_local_variables__4=Evaluation failed - unable to initialize local variables.
-RemoteEvaluationEngine_Evaluation_failed___unable_to_load_in_modular_project=Evaluation failed - lambda expression evaluation is not yet supported in modular projects.
\ No newline at end of file
+RemoteEvaluationEngine_Evaluation_failed___unable_to_instantiate_snippet_class=Evaluation failed - generated class could not be instantiated.
+RemoteEvaluationEngine_Evaluation_failed___unable_to_find_injected_class=Evaluation failed - unable to find injected class
diff --git a/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ExpressionBinder.java b/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ExpressionBinder.java
index af86d6d..2fa84cf 100644
--- a/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ExpressionBinder.java
+++ b/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ExpressionBinder.java
@@ -1,8 +1,10 @@
package org.eclipse.jdt.internal.debug.eval;
+import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.IVariableBinding;
public interface ExpressionBinder {
// Record and register the binding specified
- void bind(IVariableBinding bind, String asVariableName);
+ void bind(IVariableBinding variableToBind, String asVariableName);
+ void bindThis(ITypeBinding thisForType, String asVariableName);
}
diff --git a/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/RemoteEvaluator.java b/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/RemoteEvaluator.java
index 9d6de6e..c19fab8 100644
--- a/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/RemoteEvaluator.java
+++ b/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/RemoteEvaluator.java
@@ -15,9 +15,8 @@
*******************************************************************************/
package org.eclipse.jdt.internal.debug.eval;
-import static org.eclipse.jdt.internal.eval.EvaluationConstants.LOCAL_VAR_PREFIX;
+import static org.eclipse.jdt.core.eval.ICodeSnippetRequestor.LOCAL_VAR_PREFIX;
-import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -32,7 +31,6 @@
import org.eclipse.jdt.debug.core.IJavaClassObject;
import org.eclipse.jdt.debug.core.IJavaClassType;
import org.eclipse.jdt.debug.core.IJavaDebugTarget;
-import org.eclipse.jdt.debug.core.IJavaFieldVariable;
import org.eclipse.jdt.debug.core.IJavaObject;
import org.eclipse.jdt.debug.core.IJavaReferenceType;
import org.eclipse.jdt.debug.core.IJavaThread;
@@ -61,16 +59,20 @@
private IJavaClassObject loadedClass = null;
+ private String enclosingTypeName;
+
/**
* Constructs a new evaluation engine for the given VM in the context of the specified project. Class files required for the evaluation will be
* deployed to the specified directory (which must be on the class path of the VM in order for evaluation to work).
*
+ * @param classFiles
* @param codeSnippetClassName
- * @param classFiles2
* @param variableNames
+ * @param enclosingTypeName
*/
- public RemoteEvaluator(LinkedHashMap<String, byte[]> classFiles, String codeSnippetClassName, List<String> variableNames) {
+ public RemoteEvaluator(LinkedHashMap<String, byte[]> classFiles, String codeSnippetClassName, List<String> variableNames, String enclosingTypeName) {
this.classFiles = classFiles;
+ this.enclosingTypeName = enclosingTypeName;
this.codeSnippetClassName = codeSnippetClassName.replace('.', '/');
this.variableNames = variableNames;
}
@@ -80,32 +82,12 @@
if (loadedClass != null) {
return loadedClass;
}
-
JDIDebugTarget debugTarget = ((JDIDebugTarget) theThread.getDebugTarget());
- IJavaClassType unsafeClass;
- try {
- unsafeClass = (IJavaClassType) findType("sun.misc.Unsafe", debugTarget); //$NON-NLS-1$
- } catch (DebugException ex) {
- throw new DebugException(new Status(IStatus.ERROR, JDIDebugModel.getPluginIdentifier(), DebugException.REQUEST_FAILED, EvaluationMessages.RemoteEvaluationEngine_Evaluation_failed___unable_to_load_in_modular_project,
- null));
- }
-
- // IJavaValue[] getDeclaredFieldArgs = new IJavaValue[] { getDebugTarget().newValue("theUnsafe") }; //$NON-NLS-1$
- IJavaFieldVariable theField = unsafeClass.getField("theUnsafe"); //$NON-NLS-1$
- IJavaObject theUnsafe = (IJavaObject) theField.getValue();
-
- // IJavaValue[] setAccessibleArgs = new IJavaValue[] { getDebugTarget().newValue(true) };
- // theField.sendMessage(
- // "setAccessible", "(Z)V", setAccessibleArgs, getThread(), false); //$NON-NLS-2$ //$NON-NLS-1$
-
- // IJavaValue[] getArgs = new IJavaValue[] { getDebugTarget().newValue(null) };
- // IJavaObject theUnsafe = (IJavaObject) theField
- // .sendMessage(
- // "get", "()Ljava/lang/Object;", getArgs, getThread(), false); //$NON-NLS-2$ //$NON-NLS-1$
-
IJavaClassObject theMainClass = null;
+ IJavaObject classloader = null;
- IJavaReferenceType byteArrayType = findType("byte[]", debugTarget);//$NON-NLS-1$
+ IJavaReferenceType surroundingClass = findType(this.enclosingTypeName, debugTarget);
+ classloader = surroundingClass.getClassLoaderObject();
for (Map.Entry<String, byte[]> entry : classFiles.entrySet()) {
String className = entry.getKey();
@@ -116,21 +98,16 @@
theMainClass = existingClass.getClassObject();
}
} else {
- byte[] classBytes = entry.getValue();
- IJavaArray byteArray = ((IJavaArrayType) byteArrayType).newInstance(classBytes.length);
-
- IJavaValue[] debugClassBytes = new IJavaValue[classBytes.length];
- for (int ix = 0; ix < classBytes.length; ++ix) {
- debugClassBytes[ix] = ((JDIDebugTarget) theThread.getDebugTarget()).newValue(classBytes[ix]);
- }
- byteArray.setValues(debugClassBytes);
- IJavaValue[] defineClassArgs = new IJavaValue[] {
- debugTarget.newValue(className),
+ IJavaArray byteArray = createClassBytes(theThread, debugTarget, entry);
+ IJavaValue[] defineClassArgs = new IJavaValue[] { // args for defineClass
+ debugTarget.newValue(className.replaceAll("/", ".")), // class name //$NON-NLS-1$ //$NON-NLS-2$
byteArray, // classBytes,
- debugTarget.newValue(0), debugTarget.newValue(classBytes.length), debugTarget.nullValue(), // classloader
+ debugTarget.newValue(0), // offset
+ debugTarget.newValue(entry.getValue().length), // length
debugTarget.nullValue() // protection domain
};
- IJavaClassObject theClass = (IJavaClassObject) theUnsafe.sendMessage("defineClass", "(Ljava/lang/String;[BIILjava/lang/ClassLoader;Ljava/security/ProtectionDomain;)Ljava/lang/Class;", defineClassArgs, theThread, false); //$NON-NLS-1$//$NON-NLS-2$
+
+ IJavaClassObject theClass = (IJavaClassObject) classloader.sendMessage("defineClass", "(Ljava/lang/String;[BIILjava/security/ProtectionDomain;)Ljava/lang/Class;", defineClassArgs, theThread, false); //$NON-NLS-1$//$NON-NLS-2$
if (codeSnippetClassName.equals(className)) {
theMainClass = theClass;
}
@@ -139,6 +116,19 @@
return theMainClass;
}
+ private IJavaArray createClassBytes(IJavaThread theThread, JDIDebugTarget debugTarget, Map.Entry<String, byte[]> entry) throws DebugException {
+ IJavaReferenceType byteArrayType = findType("byte[]", debugTarget);//$NON-NLS-1$
+ byte[] classBytes = entry.getValue();
+ IJavaArray byteArray = ((IJavaArrayType) byteArrayType).newInstance(classBytes.length);
+
+ IJavaValue[] debugClassBytes = new IJavaValue[classBytes.length];
+ for (int ix = 0; ix < classBytes.length; ++ix) {
+ debugClassBytes[ix] = ((JDIDebugTarget) theThread.getDebugTarget()).newValue(classBytes[ix]);
+ }
+ byteArray.setValues(debugClassBytes);
+ return byteArray;
+ }
+
private IJavaReferenceType findType(String typeName, IJavaDebugTarget debugTarget) throws DebugException {
IJavaReferenceType theClass = tryLoadType(typeName, debugTarget);
if (theClass == null) {
@@ -148,7 +138,7 @@
IStatus.ERROR,
JDIDebugModel.getPluginIdentifier(),
DebugException.REQUEST_FAILED,
- EvaluationMessages.LocalEvaluationEngine_Evaluation_failed___unable_to_instantiate_code_snippet_class__11,
+ EvaluationMessages.RemoteEvaluationEngine_Evaluation_failed___unable_to_find_injected_class,
null));
}
return theClass;
@@ -312,61 +302,6 @@
}
/**
- * Returns a copy of the type name with '$' replaced by '.', or returns
- * <code>null</code> if the given type name refers to an anonymous inner
- * class.
- *
- * @param typeName
- * a fully qualified type name
- * @return a copy of the type name with '$' replaced by '.', or returns
- * <code>null</code> if the given type name refers to an anonymous
- * inner class.
- */
- protected String getTranslatedTypeName(String typeName) {
- int index = typeName.lastIndexOf('$');
- if (index == -1) {
- return typeName;
- }
- if (index + 1 > typeName.length()) {
- // invalid name
- return typeName;
- }
- String last = typeName.substring(index + 1);
- try {
- Integer.parseInt(last);
- return null;
- } catch (NumberFormatException e) {
- return typeName.replace('$', '.');
- }
- }
-
- /**
- * Returns an array of simple type names that are part of the given type's
- * qualified name. For example, if the given name is <code>x.y.A$B</code>,
- * an array with <code>["A", "B"]</code> is returned.
- *
- * @param typeName
- * fully qualified type name
- * @return array of nested type names
- */
- protected String[] getNestedTypeNames(String typeName) {
- int index = typeName.lastIndexOf('.');
- if (index >= 0) {
- typeName = typeName.substring(index + 1);
- }
- index = typeName.indexOf('$');
- ArrayList<String> list = new ArrayList<>(1);
- while (index >= 0) {
- list.add(typeName.substring(0, index));
- typeName = typeName.substring(index + 1);
- index = typeName.indexOf('$');
- }
- list.add(typeName);
- return list.toArray(new String[list.size()]);
- }
-
- /**
- /**
* Returns the name of the code snippet to instantiate to run the current
* evaluation.
*
diff --git a/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/RemoteEvaluatorBuilder.java b/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/RemoteEvaluatorBuilder.java
index acc9761..03a05fa 100644
--- a/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/RemoteEvaluatorBuilder.java
+++ b/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/RemoteEvaluatorBuilder.java
@@ -13,7 +13,7 @@
*******************************************************************************/
package org.eclipse.jdt.internal.debug.eval;
-import static org.eclipse.jdt.internal.eval.EvaluationConstants.LOCAL_VAR_PREFIX;
+import static org.eclipse.jdt.core.eval.ICodeSnippetRequestor.LOCAL_VAR_PREFIX;
import java.util.ArrayList;
import java.util.Collections;
@@ -24,11 +24,17 @@
import java.util.Map;
import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.debug.core.DebugException;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.dom.*;
import org.eclipse.jdt.core.eval.ICodeSnippetRequestor;
+import org.eclipse.jdt.core.eval.IEvaluationContext;
+import org.eclipse.jdt.internal.debug.core.JDIDebugPlugin;
+import org.eclipse.jdt.internal.debug.eval.ast.engine.EvaluationEngineMessages;
/**
* A builder for a reuseable expression evaluator against a runnng VM.
@@ -39,11 +45,12 @@
private IJavaProject javaProject;
private ExpressionBinder binder;
- private String enclosingTypeName;
- private boolean isStatic;
- private boolean isConstructor;
- private List<String> argumentNames = new ArrayList<>();
- private List<String> argumentTypeNames = new ArrayList<>();
+ private final String enclosingTypeName;
+ private final String packageName;
+ private final boolean isStatic;
+ private final boolean isConstructor;
+ private final List<String> argumentNames = new ArrayList<>();
+ private final List<String> argumentTypeNames = new ArrayList<>();
/**
* The names and bytecodes of the code snippet class to instantiate
@@ -55,11 +62,14 @@
*/
private String codeSnippetClassName = null;
private String snippet = null;
+ private final ITypeBinding enclosingClass;
- public RemoteEvaluatorBuilder(IJavaProject javaProject, ExpressionBinder binder, String enclosingTypeName, boolean isStatic, boolean isConstructor) {
+ public RemoteEvaluatorBuilder(IJavaProject javaProject, ExpressionBinder binder, ITypeBinding enclosingClass, boolean isStatic, boolean isConstructor) {
this.javaProject = javaProject;
this.binder = binder;
- this.enclosingTypeName = enclosingTypeName;
+ this.enclosingClass = enclosingClass;
+ this.enclosingTypeName = enclosingClass.getQualifiedName();
+ this.packageName = enclosingClass.getPackage().getName();
this.isStatic = isStatic;
this.isConstructor = isConstructor;
}
@@ -85,7 +95,7 @@
private static Object EVALUATE_CODE_SNIPPET_LOCK = new Object();
- public RemoteEvaluator build() throws JavaModelException {
+ public RemoteEvaluator build() throws JavaModelException, DebugException {
List<String> boundVariableNames = getVariableNames();
List<String> boundVariableTypeNames = getVariableTypeNames();
@@ -93,12 +103,23 @@
List<String> errors = new ArrayList<>();
IType enclosingType = this.javaProject.findType(enclosingTypeName);
+ if (enclosingType == null) {
+ throw new DebugException(new Status(IStatus.ERROR, JDIDebugPlugin.getUniqueIdentifier(), EvaluationEngineMessages.ASTInstructionCompiler_Functional_expressions_cannot_be_evaluated_inside_local_and_or_anonymous_classes));
+ }
+
synchronized (EVALUATE_CODE_SNIPPET_LOCK) {
- this.javaProject.newEvaluationContext().evaluateCodeSnippet(this.snippet, boundVariableTypeNames.toArray(new String[boundVariableNames.size()]), boundVariableNames.toArray(new String[boundVariableNames.size()]), new int[boundVariableNames.size()], enclosingType, isStatic, isConstructor, new ICodeSnippetRequestor() {
+ IEvaluationContext context = this.javaProject.newEvaluationContext();
+ if (!packageName.startsWith("java.")) { //$NON-NLS-1$
+ context.setPackageName(this.packageName);
+ }
+ // System.out.println(this.snippet);
+ context.evaluateCodeSnippet(this.snippet, boundVariableTypeNames.toArray(new String[boundVariableNames.size()]), boundVariableNames.toArray(new String[boundVariableNames.size()]), new int[boundVariableNames.size()], enclosingType, isStatic, isConstructor, new ICodeSnippetRequestor() {
@Override
public void acceptProblem(IMarker problemMarker, String fragmentSource, int fragmentKind) {
- errors.add(problemMarker.toString());
+ if (problemMarker.getAttribute(IMarker.SEVERITY, IMarker.SEVERITY_INFO) >= IMarker.SEVERITY_ERROR) {
+ errors.add(problemMarker.toString());
+ }
}
@Override
@@ -116,12 +137,10 @@
}
if (!errors.isEmpty()) {
- //LE//$NON-NLS-1$
- //LE//$NON-NLS-1$
- throw new RuntimeException(errors.toString());
+ throw new DebugException(new Status(IStatus.ERROR, JDIDebugPlugin.getUniqueIdentifier(), errors.toString()));
}
- return new RemoteEvaluator(classFiles, codeSnippetClassName, getVariableNames());
+ return new RemoteEvaluator(classFiles, codeSnippetClassName, getVariableNames(), enclosingType.getFullyQualifiedName('$'));
}
private void setCodeSnippetClassName(String codeSnippetClassName) {
@@ -147,10 +166,10 @@
return Collections.unmodifiableList(argumentNames);
}
- public String allocateNewVariable(IVariableBinding binding) {
- String varName = "bound$" + argumentNames.size(); //$NON-NLS-1$
+ public String allocateNewVariable(ITypeBinding binding, String hint) {
+ String varName = hint + "$" + argumentNames.size(); //$NON-NLS-1$
argumentNames.add(varName);
- argumentTypeNames.add(binding.getType().getQualifiedName());
+ argumentTypeNames.add(binding.getQualifiedName());
return varName;
}
@@ -217,19 +236,6 @@
*/
protected StringBuilder buffer = new StringBuilder();
- /**
- * Returns the string accumulated in the visit.
- *
- * @return the serialized
- */
- // public String getResult() {
- // return buffer.toString();
- // }
-
- // public Map<String, ITypeBinding> getCaptures() {
- // return Collections.emptyMap();
- // }
-
private int indent = 2;
private Map<IBinding, String> localBindings = new HashMap<>();
@@ -597,9 +603,9 @@
@Override
public boolean visit(CastExpression node) {
- buffer.append("(");//$NON-NLS-1$
+ //buffer.append("(");//$NON-NLS-1$
node.getType().accept(this);
- buffer.append(")");//$NON-NLS-1$
+ //buffer.append(")");//$NON-NLS-1$
node.getExpression().accept(this);
return false;
}
@@ -869,6 +875,14 @@
@Override
public boolean visit(FieldAccess node) {
+ /* TODO: Make tricks here when we access fields where we have non-public access */
+ ITypeBinding instanceType = node.getExpression().resolveTypeBinding();
+ if (instanceType.isAssignmentCompatible(RemoteEvaluatorBuilder.this.enclosingClass)) {
+ node.getExpression().accept(this);
+ buffer.append(".");//$NON-NLS-1$ */
+ buffer.append(node.getName().getIdentifier());
+ return false;
+ }
node.getExpression().accept(this);
buffer.append(".");//$NON-NLS-1$
node.getName().accept(this);
@@ -1197,6 +1211,12 @@
if (node.getExpression() != null) {
node.getExpression().accept(this);
buffer.append(".");//$NON-NLS-1$
+ } else {
+ String newVarName = new String(LOCAL_VAR_PREFIX) + allocateNewVariable(node.resolveMethodBinding().getDeclaringClass(), "this"); //$NON-NLS-1$
+ binder.bindThis(RemoteEvaluatorBuilder.this.enclosingClass, newVarName);
+ // buffer.append("this."); //$NON-NLS-1$
+ buffer.append(newVarName);
+ buffer.append(".");//$NON-NLS-1$
}
if (node.getAST().apiLevel() >= JLS3) {
if (!node.typeArguments().isEmpty()) {
@@ -1469,18 +1489,31 @@
if (!isLocalBinding(binding)) {
if (binding instanceof IVariableBinding) {
IVariableBinding vb = ((IVariableBinding) binding);
- Object constant = vb.getConstantValue();
- if (constant != null) {
- buffer.append(constant);
- return false;
- }
- if (!vb.isField()) {
- String newVarName = new String(LOCAL_VAR_PREFIX) + allocateNewVariable((IVariableBinding) binding);
+ // For future optimization: Check for duplicates, so same value is only bound once
+ if (vb.isField()) {
+ if (Modifier.isStatic(vb.getModifiers())) {
+ ITypeBinding declaringClass = vb.getDeclaringClass();
+ buffer.append(declaringClass.getQualifiedName());
+ buffer.append("."); //$NON-NLS-1$
+ buffer.append(node.getIdentifier());
+
+ } else {
+ // TODO: Fix this to use same method as visit(FieldAccess)
+ ITypeBinding declaringClass = vb.getDeclaringClass();
+ String newVarName = new String(LOCAL_VAR_PREFIX) + allocateNewVariable(declaringClass, "this"); //$NON-NLS-1$
+ binder.bindThis(declaringClass, newVarName);
+ // buffer.append("this."); //$NON-NLS-1$
+ buffer.append(newVarName);
+ buffer.append("."); //$NON-NLS-1$
+ buffer.append(node.getIdentifier());
+ }
+ } else {
+ String newVarName = new String(LOCAL_VAR_PREFIX) + allocateNewVariable(vb.getType(), node.getIdentifier());
binder.bind((IVariableBinding) binding, newVarName);
- buffer.append("this."); //$NON-NLS-1$
+ // buffer.append("this."); //$NON-NLS-1$
buffer.append(newVarName);
- return false;
}
+ return false;
}
}
@@ -1751,11 +1784,12 @@
@Override
public boolean visit(ThisExpression node) {
- if (node.getQualifier() != null) {
- node.getQualifier().accept(this);
- buffer.append(".");//$NON-NLS-1$
- }
- buffer.append("this");//$NON-NLS-1$
+ ITypeBinding thisType = node.resolveTypeBinding();
+
+ String newVarName = new String(LOCAL_VAR_PREFIX) + allocateNewVariable(thisType, "this"); //$NON-NLS-1$
+ binder.bindThis(thisType, newVarName);
+ // buffer.append("this."); //$NON-NLS-1$
+ buffer.append(newVarName);
return false;
}
diff --git a/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/engine/ASTInstructionCompiler.java b/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/engine/ASTInstructionCompiler.java
index e332112..d41c6b2 100644
--- a/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/engine/ASTInstructionCompiler.java
+++ b/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/engine/ASTInstructionCompiler.java
@@ -23,6 +23,7 @@
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
+import org.eclipse.debug.core.DebugException;
import org.eclipse.jdt.core.Flags;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaModelException;
@@ -2161,14 +2162,15 @@
if (!isActive()) {
return true;
}
- RemoteEvaluatorBuilder builder = makeBuilder(node);
- builder.acceptMethodReference(node, node.resolveTypeBinding());
try {
+ RemoteEvaluatorBuilder builder = makeBuilder(node);
+ builder.acceptMethodReference(node, node.resolveTypeBinding());
RemoteEvaluator remoteEvaluator = builder.build();
push(new RemoteOperator(builder.getSnippet(), node.getStartPosition(), remoteEvaluator));
storeInstruction();
- } catch (JavaModelException e) {
+ } catch (JavaModelException | DebugException e) {
addErrorMessage(e.getMessage());
+ setHasError(true);
}
return false;
}
@@ -2369,39 +2371,36 @@
return true;
}
- RemoteEvaluatorBuilder builder = makeBuilder(node);
- builder.acceptMethodReference(node, node.resolveTypeBinding());
try {
+ RemoteEvaluatorBuilder builder = makeBuilder(node);
+ builder.acceptMethodReference(node, node.resolveTypeBinding());
RemoteEvaluator remoteEvaluator = builder.build();
push(new RemoteOperator(builder.getSnippet(), node.getStartPosition(), remoteEvaluator));
storeInstruction();
- } catch (JavaModelException e) {
+ } catch (JavaModelException | DebugException e) {
addErrorMessage(e.getMessage());
+ setHasError(true);
}
return false;
}
- private RemoteEvaluatorBuilder makeBuilder(ASTNode node) {
+ private RemoteEvaluatorBuilder makeBuilder(ASTNode node) throws DebugException {
RemoteEvaluatorBuilder builder = new RemoteEvaluatorBuilder(fJavaProject, new ExpressionBinder() {
@Override
public void bind(IVariableBinding variableBinding, String asVariableName) {
String variableId = variableBinding.getName();
- ITypeBinding declaringTypeBinding = variableBinding.getDeclaringClass();
- if (variableBinding.isField()) {
- if (Modifier.isStatic(variableBinding.getModifiers())) {
- push(new PushStaticFieldVariable(variableId, getTypeName(declaringTypeBinding), fCounter));
- } else {
- push(new PushFieldVariable(variableId, getTypeSignature(declaringTypeBinding), fCounter));
- push(new PushThis(getEnclosingLevel(node, declaringTypeBinding)));
- storeInstruction();
- }
- } else {
- push(new PushLocalVariable(variableId));
- }
+ push(new PushLocalVariable(variableId));
storeInstruction();
}
- }, getEnclosingClass(node).getQualifiedName(), false, false);
+
+ @Override
+ public void bindThis(ITypeBinding typeBinding, String asVariableName) {
+ push(new PushThis(getEnclosingLevel(node, typeBinding)));
+ storeInstruction();
+ }
+
+ }, getEnclosingClass(node), isStaticContext(node), false);
return builder;
}
@@ -2418,6 +2417,18 @@
return null;
}
+ private boolean isStaticContext(ASTNode node) {
+ while (node != null) {
+ if (node instanceof MethodDeclaration) {
+ return Modifier.isStatic(((MethodDeclaration) node).getModifiers());
+ } else if (node instanceof Initializer) {
+ return Modifier.isStatic(((Initializer) node).getModifiers());
+ }
+ node = node.getParent();
+ }
+ return false;
+ }
+
/**
* @see ASTVisitor#visit(ExpressionStatement)
*/
@@ -2925,14 +2936,15 @@
return true;
}
- RemoteEvaluatorBuilder builder = makeBuilder(node);
- builder.acceptLambda(node, node.resolveTypeBinding());
try {
+ RemoteEvaluatorBuilder builder = makeBuilder(node);
+ builder.acceptLambda(node, node.resolveTypeBinding());
RemoteEvaluator remoteEvaluator = builder.build();
push(new RemoteOperator(builder.getSnippet(), node.getStartPosition(), remoteEvaluator));
storeInstruction();
- } catch (JavaModelException e) {
+ } catch (JavaModelException | DebugException e) {
addErrorMessage(e.getMessage());
+ setHasError(true);
}
return false;
}
@@ -4318,14 +4330,15 @@
return true;
}
- RemoteEvaluatorBuilder builder = makeBuilder(node);
- builder.acceptMethodReference(node, node.resolveTypeBinding());
try {
+ RemoteEvaluatorBuilder builder = makeBuilder(node);
+ builder.acceptMethodReference(node, node.resolveTypeBinding());
RemoteEvaluator remoteEvaluator = builder.build();
push(new RemoteOperator(builder.getSnippet(), node.getStartPosition(), remoteEvaluator));
storeInstruction();
- } catch (JavaModelException e) {
+ } catch (JavaModelException | DebugException e) {
addErrorMessage(e.getMessage());
+ setHasError(true);
}
return false;
diff --git a/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/engine/EvaluationEngineMessages.java b/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/engine/EvaluationEngineMessages.java
index 42d9044..138371d 100644
--- a/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/engine/EvaluationEngineMessages.java
+++ b/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/engine/EvaluationEngineMessages.java
@@ -62,6 +62,7 @@
public static String ASTInstructionCompiler_Lambda_expressions_cannot_be_used_in_an_evaluation_expression;
public static String ASTInstructionCompiler_Reference_expressions_cannot_be_used_in_an_evaluation_expression;
public static String ASTInstructionCompiler_Switch_expressions_cannot_be_used_in_an_evaluation_expression;
+ public static String ASTInstructionCompiler_Functional_expressions_cannot_be_evaluated_inside_local_and_or_anonymous_classes;
static {
// load message values from bundle file
diff --git a/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/engine/EvaluationEngineMessages.properties b/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/engine/EvaluationEngineMessages.properties
index 5f19282..125c9ce 100644
--- a/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/engine/EvaluationEngineMessages.properties
+++ b/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/engine/EvaluationEngineMessages.properties
@@ -57,4 +57,5 @@
ASTInstructionCompiler_Lambda_expressions_cannot_be_used_in_an_evaluation_expression=Lambda expressions cannot be used in an evaluation expression
ASTInstructionCompiler_Reference_expressions_cannot_be_used_in_an_evaluation_expression=Reference expressions cannot be used in an evaluation expression
-ASTInstructionCompiler_Switch_expressions_cannot_be_used_in_an_evaluation_expression=Switch expressions cannot be used in an evaluation expression
\ No newline at end of file
+ASTInstructionCompiler_Switch_expressions_cannot_be_used_in_an_evaluation_expression=Switch expressions cannot be used in an evaluation expression
+ASTInstructionCompiler_Functional_expressions_cannot_be_evaluated_inside_local_and_or_anonymous_classes=Functional expressions cannot be evaluated inside local and or anonymous classes
\ No newline at end of file