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 697340f89..8fec7147f 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 622f94cec..6bd30555c 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 a16a6ff46..ac262b6ed 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;