From 560469d31d0372b4f6aa516ffb5adbfd6eae44e6 Mon Sep 17 00:00:00 2001
From: Patrick Ziegler
Date: Mon, 10 Aug 2026 22:32:20 +0200
Subject: [PATCH] Remove ByteBuddy indirection in ExecutionFlowFrameVisitor
We use ByteBuddy to "augment" the visitor when traversing generic
statements. This approach is time/memory intensive, as we need to create
and load a new class, every time the Java file is parsed, but also
difficult to debug, as the generated byte code lacks any meaningful
debug information.
Instead the logic is embedded directly into the base visitor and
dynamically enabled when this complex behavior is required.
---
.../wb/core/eval/ExecutionFlowUtils.java | 199 +++++++-----------
.../wb/core/eval/ExecutionFlowUtils2.java | 3 +-
.../internal/core/parser/JavaInfoParser.java | 2 +
3 files changed, 82 insertions(+), 122 deletions(-)
diff --git a/org.eclipse.wb.core.java/src/org/eclipse/wb/core/eval/ExecutionFlowUtils.java b/org.eclipse.wb.core.java/src/org/eclipse/wb/core/eval/ExecutionFlowUtils.java
index 697340f895..8fec7147fb 100644
--- a/org.eclipse.wb.core.java/src/org/eclipse/wb/core/eval/ExecutionFlowUtils.java
+++ b/org.eclipse.wb.core.java/src/org/eclipse/wb/core/eval/ExecutionFlowUtils.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2011, 2025 Google, Inc. and others.
+ * Copyright (c) 2011, 2026 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
@@ -32,8 +32,6 @@
import org.eclipse.wb.internal.core.utils.ast.AstNodeUtils;
import org.eclipse.wb.internal.core.utils.ast.DomGenerics;
import org.eclipse.wb.internal.core.utils.check.Assert;
-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.external.ExternalFactoriesHelper;
@@ -72,15 +70,8 @@
import org.eclipse.jdt.core.dom.VariableDeclarationStatement;
import net.bytebuddy.ByteBuddy;
-import net.bytebuddy.TypeCache;
-import net.bytebuddy.dynamic.DynamicType;
-import net.bytebuddy.implementation.InvocationHandlerAdapter;
-import net.bytebuddy.matcher.ElementMatchers;
import java.lang.reflect.Field;
-import java.lang.reflect.InvocationHandler;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
@@ -120,6 +111,8 @@ private ExecutionFlowUtils() {
* @author scheglov_ke
*/
public static class ExecutionFlowFrameVisitor extends ASTVisitor {
+ private ExecutionFlowDescription flowDescription;
+ private VisitingContext context;
public Statement m_currentStatement;
////////////////////////////////////////////////////////////////////////////
@@ -157,6 +150,61 @@ public boolean enterFrame(ASTNode node) {
*/
public void leaveFrame(ASTNode node) {
}
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Execution Flow
+ //
+ ////////////////////////////////////////////////////////////////////////////
+
+ @Override
+ public void endVisit(ClassInstanceCreation node) {
+ if (flowDescription == null || context == null) {
+ return;
+ }
+ // quick check
+ {
+ String identifier = flowDescription.geTypeDeclaration().getName().getIdentifier();
+ if (!node.toString().contains(identifier)) {
+ return;
+ }
+ }
+ // check for local constructor
+ MethodDeclaration methodDeclaration = getLocalConstructorDeclaration(node);
+ if (methodDeclaration != null) {
+ // redirect execution flow to constructor
+ ExecutionFlowUtils.visit(context, flowDescription, this, List.of(methodDeclaration));
+ }
+ }
+
+ @Override
+ public void endVisit(ConstructorInvocation node) {
+ if (flowDescription == null || context == null) {
+ return;
+ }
+ MethodDeclaration constructor = getConstructor(node);
+ constructor.setProperty(KEY_FRAME_INVOCATION, node);
+ ExecutionFlowUtils.visit(context, flowDescription, this, List.of(constructor));
+ }
+
+ @Override
+ public void endVisit(MethodInvocation node) {
+ if (flowDescription == null || context == null) {
+ return;
+ }
+ // check for local method invocation
+ MethodDeclaration methodDeclaration = getLocalMethodDeclaration(node);
+ if (methodDeclaration != null) {
+ methodDeclaration.setProperty(KEY_FRAME_INVOCATION, node);
+ // check for qualified local invocation, for example "appl.open()", so visit it
+ // as type
+ if (node.getExpression() != null && !(node.getExpression() instanceof ThisExpression)) {
+ ExecutionFlowUtils.visit(context, flowDescription, this, List.of(methodDeclaration));
+ } else {
+ ExecutionFlowUtils.visit(context, flowDescription, this, methodDeclaration);
+ }
+ }
+ }
}
////////////////////////////////////////////////////////////////////////////
//
@@ -356,41 +404,22 @@ && shouldVisit_IfStatement_Else(ifStatement)) {
visitStatement(context, flowDescription, ifStatement.getElseStatement(), visitor);
}
} else if (shouldVisitStatement(statement)) {
- ASTVisitor complexVisitor = getInterceptingVisitor(context, flowDescription, visitor);
- statement.accept(complexVisitor);
+ visitStatementComplex(context, flowDescription, statement, visitor);
}
}
/**
- * Local storage for the enhanced {@link ASTVisitor} class. Classes are loaded
- * with the {@link ExecutionFlowUtils} class-loader, meaning there should only
- * exists a single instance of this class at a time. All instances are should be
- * created from this class. We have to avoid creating a separate class for each
- * enhanced visitor, as the {@link ClassLoader} is never discarded and thus, the
- * classes stay in memory indefinitely. If left unchecked, this may lead to an
- * {@link OutOfMemoryError}.
+ * Visit {@link ASTNode} with additional handling of some nodes.
*/
- private static final TypeCache PROXY_CACHE = new TypeCache.WithInlineExpunction<>(
- TypeCache.Sort.WEAK);
-
- private static ASTVisitor getInterceptingVisitor(final VisitingContext context,
- final ExecutionFlowDescription flowDescription, final ExecutionFlowFrameVisitor visitor) {
-
- DynamicType.Builder builder = new ByteBuddy() //
- .subclass(VisitorStub.class) //
- .method(ElementMatchers.any()) //
- .intercept(InvocationHandlerAdapter.of(new ExecutionFlowHandler()));
-
- ClassLoader proxyClassLoader = ExecutionFlowUtils.class.getClassLoader();
- TypeCache.SimpleKey proxyKey = new TypeCache.SimpleKey(VisitorStub.class);
-
+ private static void visitStatementComplex(VisitingContext context, ExecutionFlowDescription flowDescription,
+ Statement statement, ExecutionFlowFrameVisitor visitor) {
try {
- return (ASTVisitor) PROXY_CACHE
- .findOrInsert(proxyClassLoader, proxyKey, () -> builder.make().load(proxyClassLoader).getLoaded())
- .getConstructor(VisitingContext.class, ExecutionFlowDescription.class, ExecutionFlowFrameVisitor.class)
- .newInstance(context, flowDescription, visitor);
- } catch (ReflectiveOperationException e) {
- throw new DesignerException(ICoreExceptionConstants.EVAL_BYTEBUDDY, e);
+ visitor.context = context;
+ visitor.flowDescription = flowDescription;
+ statement.accept(visitor);
+ } finally {
+ visitor.flowDescription = null;
+ visitor.context = null;
}
}
@@ -1188,18 +1217,21 @@ public static List getInvocations(ExecutionFlowDescription flowDescript
new ExecutionFlowFrameVisitor() {
@Override
public void endVisit(MethodInvocation node) {
+ super.endVisit(node);
IMethodBinding binding = getMethodBinding(node);
addInvocation(node, binding);
}
@Override
public void endVisit(ClassInstanceCreation node) {
+ super.endVisit(node);
IMethodBinding binding = getCreationBinding(node);
addInvocation(node, binding);
}
@Override
public void endVisit(ConstructorInvocation node) {
+ super.endVisit(node);
IMethodBinding binding = getBinding(node);
addInvocation(node, binding);
}
@@ -1230,12 +1262,19 @@ private void addInvocation(ASTNode invocation, IMethodBinding binding) {
*
*
* @see ExecutionFlowHandler
+ * @deprecated No longer used. This class will be removed after the 2028-09
+ * release.
*/
+ @Deprecated(forRemoval = true, since = "2026-09")
public static class VisitorStub extends ASTVisitor {
+ @SuppressWarnings("unused")
private final VisitingContext context;
+ @SuppressWarnings("unused")
private final ExecutionFlowDescription flowDescription;
+ @SuppressWarnings("unused")
private final ExecutionFlowFrameVisitor visitor;
+ @Deprecated(forRemoval = true, since = "2026-09")
public VisitorStub(VisitingContext context, ExecutionFlowDescription flowDescription,
ExecutionFlowFrameVisitor visitor) {
this.context = context;
@@ -1243,86 +1282,4 @@ public VisitorStub(VisitingContext context, ExecutionFlowDescription flowDescrip
this.visitor = visitor;
}
}
-
- /**
- *
- * Custom invocation handler used by {@link ByteBuddy} on an enhanced
- * {@link ASTVisitor}.
- *
- *
- * All method invocations of the enhanced base class are delegated to the main
- * visitor. This visitor is stored in an internal field of the enhanced object.
- * Special code is executed for AST nodes of type
- * {@link AnonymousClassDeclaration}, {@link ClassInstanceCreation},
- * {@link MethodInvocation} and {@link ConstructorInvocation}.
- *
- *
- * This handler may only be used for objects of type {@link VisitorStub}.
- *
- *
- * @see VisitorStub
- */
- private static class ExecutionFlowHandler implements InvocationHandler {
- @Override
- public Object invoke(Object obj, Method method, Object[] args) throws Throwable {
- VisitorStub stub = (VisitorStub) obj;
- // routing
- Class>[] parameterTypes = method.getParameterTypes();
- if (parameterTypes.length == 1) {
- Class> parameterType = parameterTypes[0];
- if (method.getName().equals("endVisit")) {
- if (parameterType == ClassInstanceCreation.class) {
- endVisit(stub, (ClassInstanceCreation) args[0]);
- } else if (parameterType == MethodInvocation.class) {
- endVisit(stub, (MethodInvocation) args[0]);
- } else if (parameterType == ConstructorInvocation.class) {
- endVisit(stub, (ConstructorInvocation) args[0]);
- }
- }
- }
- // use main visitor
- try {
- return method.invoke(stub.visitor, args);
- } catch (InvocationTargetException e) {
- throw e.getCause();
- }
- }
-
- private void endVisit(VisitorStub stub, ClassInstanceCreation node) {
- // quick check
- {
- String identifier = stub.flowDescription.geTypeDeclaration().getName().getIdentifier();
- if (!node.toString().contains(identifier)) {
- return;
- }
- }
- // check for local constructor
- MethodDeclaration methodDeclaration = getLocalConstructorDeclaration(node);
- if (methodDeclaration != null) {
- // redirect execution flow to constructor
- ExecutionFlowUtils.visit(stub.context, stub.flowDescription, stub.visitor, List.of(methodDeclaration));
- }
- }
-
- private void endVisit(VisitorStub stub, MethodInvocation node) {
- // check for local method invocation
- MethodDeclaration methodDeclaration = getLocalMethodDeclaration(node);
- if (methodDeclaration != null) {
- methodDeclaration.setProperty(KEY_FRAME_INVOCATION, node);
- // check for qualified local invocation, for example "appl.open()", so visit it
- // as type
- if (node.getExpression() != null && !(node.getExpression() instanceof ThisExpression)) {
- ExecutionFlowUtils.visit(stub.context, stub.flowDescription, stub.visitor, List.of(methodDeclaration));
- } else {
- ExecutionFlowUtils.visit(stub.context, stub.flowDescription, stub.visitor, methodDeclaration);
- }
- }
- }
-
- private void endVisit(VisitorStub stub, ConstructorInvocation node) {
- MethodDeclaration constructor = getConstructor(node);
- constructor.setProperty(KEY_FRAME_INVOCATION, node);
- ExecutionFlowUtils.visit(stub.context, stub.flowDescription, stub.visitor, List.of(constructor));
- }
- }
}
diff --git a/org.eclipse.wb.core.java/src/org/eclipse/wb/core/eval/ExecutionFlowUtils2.java b/org.eclipse.wb.core.java/src/org/eclipse/wb/core/eval/ExecutionFlowUtils2.java
index 622f94cecd..6bd30555c8 100644
--- a/org.eclipse.wb.core.java/src/org/eclipse/wb/core/eval/ExecutionFlowUtils2.java
+++ b/org.eclipse.wb.core.java/src/org/eclipse/wb/core/eval/ExecutionFlowUtils2.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2011, 2024 Google, Inc. and others.
+ * Copyright (c) 2011, 2026 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
@@ -338,6 +338,7 @@ public void endVisit(CastExpression node) {
@Override
public void endVisit(MethodInvocation node) {
+ super.endVisit(node);
MethodDeclaration method = AstNodeUtils.getLocalMethodDeclaration(node);
if (method != null) {
// may be "lazy"
diff --git a/org.eclipse.wb.core.java/src/org/eclipse/wb/internal/core/parser/JavaInfoParser.java b/org.eclipse.wb.core.java/src/org/eclipse/wb/internal/core/parser/JavaInfoParser.java
index a16a6ff46f..ac262b6ed5 100644
--- a/org.eclipse.wb.core.java/src/org/eclipse/wb/internal/core/parser/JavaInfoParser.java
+++ b/org.eclipse.wb.core.java/src/org/eclipse/wb/internal/core/parser/JavaInfoParser.java
@@ -660,6 +660,7 @@ private boolean createJavaInfo_noModel(ASTNode node) {
@Override
public void endVisit(ClassInstanceCreation creation) {
+ super.endVisit(creation);
try {
if (createJavaInfo_noModel(creation)) {
return;
@@ -743,6 +744,7 @@ private boolean createJavaInfo_noModel(ClassInstanceCreation creation,
@Override
public void endVisit(MethodInvocation invocation) {
+ super.endVisit(invocation);
try {
if (createJavaInfo_noModel(invocation)) {
return;