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

Filter by extension

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
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2011, 2024 Google, Inc. and others.
* Copyright (c) 2011, 2025 Google, Inc. and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
Expand Down Expand Up @@ -35,7 +35,6 @@
import org.eclipse.wb.internal.core.utils.exception.DesignerException;
import org.eclipse.wb.internal.core.utils.exception.ICoreExceptionConstants;
import org.eclipse.wb.internal.core.utils.exception.MultipleConstructorsError;
import org.eclipse.wb.internal.core.utils.execution.ExecutionUtils;
import org.eclipse.wb.internal.core.utils.external.ExternalFactoriesHelper;

import org.eclipse.jdt.core.dom.ASTNode;
Expand All @@ -55,6 +54,7 @@
import org.eclipse.jdt.core.dom.IMethodBinding;
import org.eclipse.jdt.core.dom.IfStatement;
import org.eclipse.jdt.core.dom.Initializer;
import org.eclipse.jdt.core.dom.LambdaExpression;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.MethodInvocation;
import org.eclipse.jdt.core.dom.PrefixExpression;
Expand Down Expand Up @@ -132,6 +132,11 @@ public boolean visit(AnonymousClassDeclaration node) {
return shouldVisitAnonymousClassDeclaration(node);
}

@Override
public boolean visit(LambdaExpression node) {
return shouldVisitAnonymousClassDeclaration(node);
}

////////////////////////////////////////////////////////////////////////////
//
// Frames
Expand Down Expand Up @@ -393,15 +398,13 @@ private static ASTVisitor getInterceptingVisitor(final VisitingContext context,
* In general we should not visit anonymous classes, they are usually event handlers. However
* there are special cases, such as <code>EventQueue.invokeLater(Runnable)</code> in Swing.
*/
private static boolean shouldVisitAnonymousClassDeclaration(final AnonymousClassDeclaration anonymous) {
return ExecutionUtils.runObjectLog(() -> {
for (ExecutionFlowProvider provider : getExecutionFlowProviders()) {
if (provider.shouldVisit(anonymous)) {
return true;
}
private static boolean shouldVisitAnonymousClassDeclaration(final ASTNode anonymous) {
for (ExecutionFlowProvider provider : getExecutionFlowProviders()) {
if (provider.shouldVisit(anonymous)) {
return true;
}
return false;
}, false);
}
return false;
}

/**
Expand Down Expand Up @@ -1267,11 +1270,7 @@ public Object invoke(Object obj, Method method, Object[] args) throws Throwable
Class<?>[] parameterTypes = method.getParameterTypes();
if (parameterTypes.length == 1) {
Class<?> parameterType = parameterTypes[0];
if (method.getName().equals("visit")) {
if (parameterType == AnonymousClassDeclaration.class) {
visit(stub, (AnonymousClassDeclaration) args[0]);
}
} else if (method.getName().equals("endVisit")) {
if (method.getName().equals("endVisit")) {
if (parameterType == ClassInstanceCreation.class) {
endVisit(stub, (ClassInstanceCreation) args[0]);
} else if (parameterType == MethodInvocation.class) {
Expand All @@ -1289,10 +1288,6 @@ public Object invoke(Object obj, Method method, Object[] args) throws Throwable
}
}

private boolean visit(VisitorStub stub, AnonymousClassDeclaration node) {
return shouldVisitAnonymousClassDeclaration(node);
}

private void endVisit(VisitorStub stub, ClassInstanceCreation node) {
// quick check
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2011 Google, Inc.
* Copyright (c) 2011, 2025 Google, Inc. and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
Expand All @@ -12,8 +12,15 @@
*******************************************************************************/
package org.eclipse.wb.internal.core.eval;

import org.eclipse.wb.internal.core.utils.ast.AstNodeUtils;

import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.AnonymousClassDeclaration;
import org.eclipse.jdt.core.dom.ClassInstanceCreation;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.LambdaExpression;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.MethodInvocation;
import org.eclipse.jdt.core.dom.TypeDeclaration;

import java.awt.EventQueue;
Expand All @@ -36,7 +43,34 @@ public MethodDeclaration getDefaultConstructor(TypeDeclaration typeDeclaration)
return null;
}

public boolean shouldVisit(AnonymousClassDeclaration anonymous) throws Exception {
public boolean shouldVisit(ASTNode node) {
return false;
}

protected MethodInvocation getMethodInvocation(ASTNode node) {
return switch (node) {
case AnonymousClassDeclaration declaration -> {
ClassInstanceCreation creation = (ClassInstanceCreation) declaration.getParent();
if (creation.getLocationInParent() == MethodInvocation.ARGUMENTS_PROPERTY) {
yield (MethodInvocation) creation.getParent();
}
yield null;
}
case LambdaExpression expression -> {
if (expression.getLocationInParent() == MethodInvocation.ARGUMENTS_PROPERTY) {
yield (MethodInvocation) expression.getParent();
}
yield null;
}
default -> null;
};
}

protected ITypeBinding getTypeBinding(ASTNode node) {
return switch (node) {
case AnonymousClassDeclaration declaration -> AstNodeUtils.getTypeBinding(declaration);
case LambdaExpression expression -> AstNodeUtils.getTypeBinding(expression);
default -> null;
};
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2011 Google, Inc.
* Copyright (c) 2011, 2025 Google, Inc. and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
Expand All @@ -15,8 +15,7 @@
import org.eclipse.wb.internal.core.eval.ExecutionFlowProvider;
import org.eclipse.wb.internal.core.utils.ast.AstNodeUtils;

import org.eclipse.jdt.core.dom.AnonymousClassDeclaration;
import org.eclipse.jdt.core.dom.ClassInstanceCreation;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.MethodInvocation;
Expand Down Expand Up @@ -75,19 +74,17 @@ public MethodDeclaration getDefaultConstructor(TypeDeclaration typeDeclaration)
}

@Override
public boolean shouldVisit(AnonymousClassDeclaration anonymous) throws Exception {
// Realm.runWithDefault()
if (AstNodeUtils.isSuccessorOf(anonymous, "java.lang.Runnable")) {
ClassInstanceCreation creation = (ClassInstanceCreation) anonymous.getParent();
if (creation.getLocationInParent() == MethodInvocation.ARGUMENTS_PROPERTY) {
MethodInvocation invocation = (MethodInvocation) creation.getParent();
public boolean shouldVisit(ASTNode node) {
if (AstNodeUtils.isSuccessorOf(getTypeBinding(node), "java.lang.Runnable")) {
MethodInvocation invocation = getMethodInvocation(node);
if (invocation != null) {
return AstNodeUtils.isMethodInvocation(
invocation,
"org.eclipse.core.databinding.observable.Realm",
"runWithDefault(org.eclipse.core.databinding.observable.Realm,java.lang.Runnable)");
}
}
// unknown pattern
return false;
return super.shouldVisit(node);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2011 Google, Inc.
* Copyright (c) 2011, 2025 Google, Inc. and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
Expand All @@ -15,8 +15,7 @@
import org.eclipse.wb.internal.core.eval.ExecutionFlowProvider;
import org.eclipse.wb.internal.core.utils.ast.AstNodeUtils;

import org.eclipse.jdt.core.dom.AnonymousClassDeclaration;
import org.eclipse.jdt.core.dom.ClassInstanceCreation;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.MethodInvocation;
Expand Down Expand Up @@ -45,11 +44,10 @@ public MethodDeclaration getDefaultConstructor(TypeDeclaration typeDeclaration)
}

@Override
public boolean shouldVisit(AnonymousClassDeclaration anonymous) throws Exception {
if (AstNodeUtils.isSuccessorOf(anonymous.resolveBinding(), "java.lang.Runnable")) {
ClassInstanceCreation creation = (ClassInstanceCreation) anonymous.getParent();
if (creation.getLocationInParent() == MethodInvocation.ARGUMENTS_PROPERTY) {
MethodInvocation invocation = (MethodInvocation) creation.getParent();
public boolean shouldVisit(ASTNode node) {
if (AstNodeUtils.isSuccessorOf(getTypeBinding(node), "java.lang.Runnable")) {
MethodInvocation invocation = getMethodInvocation(node);
if (invocation != null) {
return AstNodeUtils.isMethodInvocation(
invocation,
"java.awt.EventQueue",
Expand All @@ -68,6 +66,6 @@ public boolean shouldVisit(AnonymousClassDeclaration anonymous) throws Exception
"invokeAndWait(java.lang.Runnable)");
}
}
return super.shouldVisit(anonymous);
return super.shouldVisit(node);
}
}
1 change: 1 addition & 0 deletions org.eclipse.wb.tests/META-INF/MANIFEST.MF
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ Require-Bundle: org.eclipse.core.runtime;bundle-version="[3.31.100,4.0.0)",
org.objenesis;bundle-version="[3.4.0,4.0.0)",
net.bytebuddy.byte-buddy;bundle-version="[1.14.16,2.0.0)",
junit-jupiter-api;bundle-version="[5.10.2,6.0.0)",
junit-jupiter-params;bundle-version="[5.10.2,6.0.0)",
junit-platform-suite-api;bundle-version="[1.10.2,2.0.0)",
org.opentest4j;bundle-version="[1.3.0,2.0.0)"
Bundle-ActivationPolicy: lazy
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2011, 2024 Google, Inc. and others.
* Copyright (c) 2011, 2025 Google, Inc. and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
Expand Down Expand Up @@ -43,6 +43,8 @@
import org.eclipse.jdt.core.dom.ExpressionStatement;
import org.eclipse.jdt.core.dom.FieldAccess;
import org.eclipse.jdt.core.dom.FieldDeclaration;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.LambdaExpression;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.MethodInvocation;
import org.eclipse.jdt.core.dom.SimpleName;
Expand All @@ -58,6 +60,8 @@
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
Expand Down Expand Up @@ -830,17 +834,18 @@ public void endVisit(SimpleName node) {

////////////////////////////////////////////////////////////////////////////
//
// Visiting and AnonymousClassDeclaration
// Visiting and AnonymousClassDeclaration/LambdaExpressios
//
////////////////////////////////////////////////////////////////////////////
/**
* Test for ignoring {@link AnonymousClassDeclaration}.
* <p>
* {@link Runnable} in {@link Thread} should not be visited.
*/
@Test
public void test_visit_AnonymousClassDeclaration_noVisit() throws Exception {
String threadCode = "new Thread(new Runnable() {public void run() {int a;}});";
@ParameterizedTest
@ValueSource(strings = { "new Runnable() {public void run() {int a;}}", "() -> {int a;}" })
public void test_visit_AnonymousClassDeclaration_noVisit(String runnableCode) throws Exception {
String threadCode = "new Thread(" + runnableCode + ");";
String code = "void root() {" + threadCode + "}";
String expectedNodes[] = {threadCode};
check_visitNodes(code, expectedNodes, "root()");
Expand All @@ -852,9 +857,10 @@ public void test_visit_AnonymousClassDeclaration_noVisit() throws Exception {
* {@link Runnable} in {@link Thread} should not be visited, and invocations also should not be
* followed.
*/
@Test
public void test_visit_AnonymousClassDeclaration_withInvocation() throws Exception {
String threadCode = "new Thread(new Runnable() {public void run() {foo();}});";
@ParameterizedTest
@ValueSource(strings = { "new Runnable() {public void run() {foo();}}", "() -> {foo();}" })
public void test_visit_AnonymousClassDeclaration_withInvocation(String runnableCode) throws Exception {
String threadCode = "new Thread(" + runnableCode + ");";
String code = "void root() {" + threadCode + "} void foo() {int b;}";
String expectedNodes[] = {threadCode};
check_visitNodes(code, expectedNodes, "root()");
Expand All @@ -865,13 +871,23 @@ public void test_visit_AnonymousClassDeclaration_withInvocation() throws Excepti
*/
public static class ExecutionFlowProvider_RunnableInThread extends ExecutionFlowProvider {
@Override
public boolean shouldVisit(AnonymousClassDeclaration anonymous) throws Exception {
if (AstNodeUtils.isSuccessorOf(anonymous.resolveBinding(), "java.lang.Runnable")) {
ASTNode anonymousCreation = anonymous.getParent();
if (anonymousCreation.getLocationInParent() == ClassInstanceCreation.ARGUMENTS_PROPERTY) {
Expression enclosingCreation = (ClassInstanceCreation) anonymousCreation.getParent();
if (AstNodeUtils.isSuccessorOf(enclosingCreation, "java.lang.Thread")) {
return true;
public boolean shouldVisit(ASTNode node) {
ITypeBinding typeBinding = getTypeBinding(node);
if (AstNodeUtils.isSuccessorOf(typeBinding, "java.lang.Runnable")) {
if (node instanceof AnonymousClassDeclaration anonymous) {
ASTNode anonymousCreation = anonymous.getParent();
if (anonymousCreation.getLocationInParent() == ClassInstanceCreation.ARGUMENTS_PROPERTY) {
Expression enclosingCreation = (ClassInstanceCreation) anonymousCreation.getParent();
if (AstNodeUtils.isSuccessorOf(enclosingCreation, "java.lang.Thread")) {
return true;
}
}
} else if (node instanceof LambdaExpression lambda) {
if (lambda.getLocationInParent() == ClassInstanceCreation.ARGUMENTS_PROPERTY) {
Expression enclosingCreation = (ClassInstanceCreation) lambda.getParent();
if (AstNodeUtils.isSuccessorOf(enclosingCreation, "java.lang.Thread")) {
return true;
}
}
}
}
Expand All @@ -886,8 +902,9 @@ public boolean shouldVisit(AnonymousClassDeclaration anonymous) throws Exception
* should not be followed. However we install special {@link ExecutionFlowProvider} that allows
* such visiting.
*/
@Test
public void test_visit_AnonymousClassDeclaration_withInvocation_doVisit() throws Exception {
@ParameterizedTest
@ValueSource(strings = { "new Runnable() {public void run() {foo();}}", "() -> {foo();}" })
public void test_visit_AnonymousClassDeclaration_withInvocation_doVisit(String runnableCode) throws Exception {
TestBundle testBundle = new TestBundle();
try {
Class<?> providerClass = ExecutionFlowProvider_RunnableInThread.class;
Expand All @@ -897,7 +914,7 @@ public void test_visit_AnonymousClassDeclaration_withInvocation_doVisit() throws
"<provider class='" + providerClass.getName() + "'/>");
testBundle.install();
try {
String threadCode = "new Thread(new Runnable() {public void run() {foo();}});";
String threadCode = "new Thread(" + runnableCode + ");";
String code = "void root() {" + threadCode + "} void foo() {int b;}";
String expectedNodes[] = {"int b;", "foo();", threadCode};
check_visitNodes(code, expectedNodes, "root()");
Expand Down
Loading