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