Skip to content
Draft
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, 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
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;

////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -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);
}
}
}
}
////////////////////////////////////////////////////////////////////////////
//
Expand Down Expand Up @@ -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<TypeCache.SimpleKey> PROXY_CACHE = new TypeCache.WithInlineExpunction<>(
TypeCache.Sort.WEAK);

private static ASTVisitor getInterceptingVisitor(final VisitingContext context,
final ExecutionFlowDescription flowDescription, final ExecutionFlowFrameVisitor visitor) {

DynamicType.Builder<VisitorStub> 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;
}
}

Expand Down Expand Up @@ -1188,18 +1217,21 @@ public static List<ASTNode> 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);
}
Expand Down Expand Up @@ -1230,99 +1262,24 @@ private void addInvocation(ASTNode invocation, IMethodBinding binding) {
* </p>
*
* @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;
this.flowDescription = flowDescription;
this.visitor = visitor;
}
}

/**
* <p>
* Custom invocation handler used by {@link ByteBuddy} on an enhanced
* {@link ASTVisitor}.
* </p>
* <p>
* 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}.
* </p>
* <p>
* This handler may <b>only</b> be used for objects of type {@link VisitorStub}.
* </p>
*
* @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));
}
}
}
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, 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
Expand Down Expand Up @@ -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"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
Loading