all) {
}
}
- // m is more generic than a
+ /**
+ * Checks if two methods are equal (same name, params, and compatible return types).
+ * m is more generic than a.
+ */
private static boolean isEqual(Method m, Method a) {
- if (m.getName().equals(a.getName()) && m.getParameterCount() == a.getParameterCount()
- && m.getReturnType().isAssignableFrom(a.getReturnType())) {
+ if (m.getName().equals(a.getName()) && m.getParameterCount() == a.getParameterCount()) {
+ // Check parameters match exactly (or are compatible)
for (int i = 0; i < m.getParameterCount(); i++) {
if (!(m.getParameterTypes()[i].isAssignableFrom(a.getParameterTypes()[i]))) {
return false;
}
}
- return true;
+ // For return types, allow covariant returns in either direction
+ // The decorator method (a) can have a more specific return type than the interface method (m)
+ // OR the interface method (m) can have a more specific return type (WeldEvent vs Event)
+ return m.getReturnType().isAssignableFrom(a.getReturnType())
+ || a.getReturnType().isAssignableFrom(m.getReturnType());
}
return false;
}
- @Override
- protected String getProxyNameSuffix() {
- return PROXY_SUFFIX;
- }
-
- @Override
- protected boolean isUsingProxyInstantiator() {
- return false;
- }
-
- private void createAbstractMethodCode(ClassMethod classMethod, MethodInformation method, ClassMethod staticConstructor) {
- if ((delegateField != null) && (!Modifier.isPrivate(delegateField.getModifiers()))) {
- // Call the corresponding method directly on the delegate
- final CodeAttribute b = classMethod.getCodeAttribute();
- // load the delegate field
- b.aload(0);
- b.getfield(classMethod.getClassFile().getName(), delegateField.getName(),
- DescriptorUtils.makeDescriptor(delegateField.getType()));
- // load the parameters
- b.loadMethodParameters();
- // invoke the delegate method
- b.invokeinterface(delegateField.getType().getName(), method.getName(), method.getDescriptor());
- // return the value if applicable
- b.returnInstruction();
- } else {
- if (!Modifier.isPrivate(method.getMethod().getModifiers())) {
- // if it is a parameter injection point we need to initialize the
- // injection point then handle the method with the method handler
-
- // this is slightly different to a normal method handler call, as we pass
- // in a TargetInstanceBytecodeMethodResolver. This resolver uses the
- // method handler to call getTargetClass to get the correct class type to
- // resolve the method with, and then resolves this method
-
- invokeMethodHandler(classMethod, method, true, targetInstanceBytecodeMethodResolver, staticConstructor);
- } else {
- // if the delegate is private we need to use the method handler
- createInterceptorBody(classMethod, method, staticConstructor);
- }
- }
- }
-
- /**
- * When creates the delegate initializer code when the delegate is injected
- * into a method.
- *
- * super initializer method is called first, and then _initMH is called
- *
- * @param initializerMethodInfo
- * @param delegateParameterPosition
- * @return
- */
- private void createDelegateInitializerCode(ClassMethod classMethod, MethodInformation initializerMethodInfo,
- int delegateParameterPosition) {
- final CodeAttribute b = classMethod.getCodeAttribute();
- // we need to push all the parameters on the stack to call the corresponding
- // superclass arguments
- b.aload(0); // load this
- int localVariables = 1;
- int actualDelegateParameterPosition = 0;
- for (int i = 0; i < initializerMethodInfo.getMethod().getParameterCount(); ++i) {
- if (i == delegateParameterPosition) {
- // figure out the actual position of the delegate in the local
- // variables
- actualDelegateParameterPosition = localVariables;
- }
- Class> type = initializerMethodInfo.getMethod().getParameterTypes()[i];
- BytecodeUtils.addLoadInstruction(b, DescriptorUtils.makeDescriptor(type), localVariables);
- if (type == long.class || type == double.class) {
- localVariables = localVariables + 2;
- } else {
- localVariables++;
- }
- }
- b.invokespecial(classMethod.getClassFile().getSuperclass(), initializerMethodInfo.getName(),
- initializerMethodInfo.getDescriptor());
- // if this method returns a value it is now sitting on top of the stack
- // we will leave it there are return it later
-
- // now we need to call _initMH
- b.aload(0); // load this
- b.aload(actualDelegateParameterPosition); // load the delegate
- b.invokevirtual(classMethod.getClassFile().getName(), INIT_MH_METHOD_NAME,
- "(" + LJAVA_LANG_OBJECT + ")" + BytecodeUtils.VOID_CLASS_DESCRIPTOR);
- // return the object from the top of the stack that we got from calling
- // the superclass method earlier
- b.returnInstruction();
-
- }
-
- protected class TargetInstanceBytecodeMethodResolver implements BytecodeMethodResolver {
- private static final String JAVA_LANG_CLASS_CLASS_NAME = "java.lang.Class";
-
- public void getDeclaredMethod(ClassMethod classMethod, String declaringClass, String methodName,
- String[] parameterTypes, ClassMethod staticConstructor) {
- // get the correct class type to use to resolve the method
- MethodInformation methodInfo = new StaticMethodInformation("weld_getTargetClass", new String[0], LJAVA_LANG_CLASS,
- TargetInstanceProxy.class.getName());
- invokeMethodHandler(classMethod, methodInfo, false, DEFAULT_METHOD_RESOLVER, staticConstructor);
- CodeAttribute code = classMethod.getCodeAttribute();
- code.checkcast("java/lang/Class");
- // now we have the class on the stack
- code.ldc(methodName);
- // now we need to load the parameter types into an array
- code.iconst(parameterTypes.length);
- code.anewarray(JAVA_LANG_CLASS_CLASS_NAME);
- for (int i = 0; i < parameterTypes.length; ++i) {
- code.dup(); // duplicate the array reference
- code.iconst(i);
- // now load the class object
- String type = parameterTypes[i];
- BytecodeUtils.pushClassType(code, type);
- // and store it in the array
- code.aastore();
- }
- code.invokestatic(Reflections.class.getName(), "wrapException",
- "(Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;");
- code.checkcast(Method.class);
- }
-
- }
-
}
diff --git a/impl/src/main/java/org/jboss/weld/bean/proxy/DecoratorProxyMethodHandler.java b/impl/src/main/java/org/jboss/weld/bean/proxy/DecoratorProxyMethodHandler.java
index 978cc4ffe8a..fc17220fe34 100644
--- a/impl/src/main/java/org/jboss/weld/bean/proxy/DecoratorProxyMethodHandler.java
+++ b/impl/src/main/java/org/jboss/weld/bean/proxy/DecoratorProxyMethodHandler.java
@@ -18,6 +18,7 @@
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
+import java.util.Arrays;
import jakarta.enterprise.inject.spi.Decorator;
import jakarta.inject.Inject;
@@ -83,7 +84,46 @@ private Object doInvoke(WeldDecorator> weldDecorator, Object decoratorInstance
}
}
}
- Reflections.ensureAccessible(method, getTargetInstance());
- return Reflections.invokeAndUnwrap(getTargetInstance(), method, args);
+ // If the decorator doesn't implement this method, delegate to the target instance
+ // However, the method signature might have covariant return types (e.g., WeldEvent vs Event)
+ // We need to find the actual method on the target instance that matches
+ Object target = getTargetInstance();
+ Method targetMethod = findMatchingMethod(target.getClass(), method);
+ if (targetMethod != null) {
+ Reflections.ensureAccessible(targetMethod, target);
+ return Reflections.invokeAndUnwrap(target, targetMethod, args);
+ } else {
+ // Fallback to original method if no match found
+ Reflections.ensureAccessible(method, target);
+ return Reflections.invokeAndUnwrap(target, method, args);
+ }
+ }
+
+ /**
+ * Find a method on the target class that matches the given method,
+ * taking into account covariant return types.
+ */
+ private Method findMatchingMethod(Class> targetClass, Method method) {
+ try {
+ // First try exact match
+ return targetClass.getMethod(method.getName(), method.getParameterTypes());
+ } catch (NoSuchMethodException e) {
+ // If exact match fails, look for covariant return types
+ try {
+ for (Method m : targetClass.getMethods()) {
+ if (m.getName().equals(method.getName()) &&
+ Arrays.equals(m.getParameterTypes(), method.getParameterTypes())) {
+ // Found a method with same name and parameters, check return type compatibility
+ if (method.getReturnType().isAssignableFrom(m.getReturnType()) ||
+ m.getReturnType().isAssignableFrom(method.getReturnType())) {
+ return m;
+ }
+ }
+ }
+ } catch (Exception ex) {
+ // Ignore
+ }
+ return null;
+ }
}
}
diff --git a/impl/src/main/java/org/jboss/weld/bean/proxy/DefaultBytecodeMethodResolver.java b/impl/src/main/java/org/jboss/weld/bean/proxy/DefaultBytecodeMethodResolver.java
deleted file mode 100644
index 453da5583e6..00000000000
--- a/impl/src/main/java/org/jboss/weld/bean/proxy/DefaultBytecodeMethodResolver.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * JBoss, Home of Professional Open Source
- * Copyright 2008, Red Hat, Inc. and/or its affiliates, and individual contributors
- * by the @authors tag. See the copyright.txt in the distribution for a
- * full listing of individual contributors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.jboss.weld.bean.proxy;
-
-import java.lang.reflect.Method;
-import java.util.concurrent.atomic.AtomicLong;
-
-import org.jboss.classfilewriter.AccessFlag;
-import org.jboss.classfilewriter.ClassFile;
-import org.jboss.classfilewriter.ClassMethod;
-import org.jboss.classfilewriter.code.CodeAttribute;
-import org.jboss.weld.logging.ReflectionLogger;
-import org.jboss.weld.util.bytecode.BytecodeUtils;
-
-/**
- * A {@link BytecodeMethodResolver} that looks up the method using the reflection API.
- *
- *
- * @author Stuart Douglas
- * @author Martin Kouba
- */
-public class DefaultBytecodeMethodResolver implements BytecodeMethodResolver {
-
- private static final AtomicLong METHOD_COUNT = new AtomicLong();
-
- private static final String WELD_MEMBER_PREFIX = "weld$$$";
-
- private static final String LJAVA_LANG_REFLECT_METHOD = "Ljava/lang/reflect/Method;";
-
- @Override
- public void getDeclaredMethod(final ClassMethod classMethod, final String declaringClass, final String methodName,
- final String[] parameterTypes,
- ClassMethod staticConstructor) {
-
- String weldMemberName = WELD_MEMBER_PREFIX + METHOD_COUNT.incrementAndGet();
- staticConstructor.getClassFile().addField(AccessFlag.PRIVATE | AccessFlag.STATIC, weldMemberName,
- LJAVA_LANG_REFLECT_METHOD);
-
- final CodeAttribute code = staticConstructor.getCodeAttribute();
-
- addInitMethod(declaringClass, methodName, parameterTypes, weldMemberName, staticConstructor.getClassFile());
- code.invokestatic(staticConstructor.getClassFile().getName(), weldMemberName, "()Ljava/lang/reflect/Method;");
- code.putstatic(classMethod.getClassFile().getName(), weldMemberName, LJAVA_LANG_REFLECT_METHOD);
-
- CodeAttribute methodCode = classMethod.getCodeAttribute();
- methodCode.getstatic(classMethod.getClassFile().getName(), weldMemberName, LJAVA_LANG_REFLECT_METHOD);
- }
-
- private void addInitMethod(final String declaringClass, final String methodName, final String[] parameterTypes,
- String weldMethodName, ClassFile classFile) {
- ClassMethod initMethod = classFile.addMethod(AccessFlag.of(AccessFlag.PRIVATE, AccessFlag.STATIC), weldMethodName,
- LJAVA_LANG_REFLECT_METHOD);
- final CodeAttribute code = initMethod.getCodeAttribute();
- BytecodeUtils.pushClassType(code, declaringClass);
- // now we have the class on the stack
- code.ldc(methodName);
- // now we need to load the parameter types into an array
- code.iconst(parameterTypes.length);
- code.anewarray(Class.class.getName());
- for (int i = 0; i < parameterTypes.length; ++i) {
- code.dup(); // duplicate the array reference
- code.iconst(i);
- // now load the class object
- String type = parameterTypes[i];
- BytecodeUtils.pushClassType(code, type);
- // and store it in the array
- code.aastore();
- }
- code.invokestatic(DefaultBytecodeMethodResolver.class.getName(), "getMethod",
- "(Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;");
- code.returnInstruction();
- }
-
- public static Method getMethod(Class> javaClass, String methodName, Class>... parameterTypes) {
- try {
- return javaClass.getDeclaredMethod(methodName, parameterTypes);
- } catch (NoSuchMethodException e) {
- throw ReflectionLogger.LOG.noSuchMethodWrapper(e, e.getMessage());
- }
- }
-
-}
diff --git a/impl/src/main/java/org/jboss/weld/bean/proxy/DummyClassFactoryImpl.java b/impl/src/main/java/org/jboss/weld/bean/proxy/DummyClassFactoryImpl.java
deleted file mode 100644
index 5f4c857f17b..00000000000
--- a/impl/src/main/java/org/jboss/weld/bean/proxy/DummyClassFactoryImpl.java
+++ /dev/null
@@ -1,29 +0,0 @@
-package org.jboss.weld.bean.proxy;
-
-import java.security.ProtectionDomain;
-
-import org.jboss.classfilewriter.ClassFactory;
-import org.jboss.weld.bean.proxy.util.WeldDefaultProxyServices;
-
-/**
- * A dummy implementation which has only one purpose - to avoid instantiating {@code DefaultClassFactory.INSTANCE}.
- * The sole method in this class is never used as we define classes using different means that further vary
- * between in-container (such as WildFly) and SE setups.
- *
- * See {@link WeldDefaultProxyServices#defineClass(Class, String, byte[], int, int)} for details on how we define
- * classes.
- */
-public class DummyClassFactoryImpl implements ClassFactory {
-
- private DummyClassFactoryImpl() {
- }
-
- // final so that there's only one instance that's being referenced from anywhere
- public static final DummyClassFactoryImpl INSTANCE = new DummyClassFactoryImpl();
-
- @Override
- public Class> defineClass(ClassLoader loader, String name, byte[] b, int off, int len, ProtectionDomain protectionDomain)
- throws ClassFormatError {
- throw new UnsupportedOperationException("DummyClasFactoryImpl should not be used to define classes");
- }
-}
diff --git a/impl/src/main/java/org/jboss/weld/bean/proxy/InterceptedProxyFactory.java b/impl/src/main/java/org/jboss/weld/bean/proxy/InterceptedProxyFactory.java
index 2f01ed2d9e7..3beff7264c8 100644
--- a/impl/src/main/java/org/jboss/weld/bean/proxy/InterceptedProxyFactory.java
+++ b/impl/src/main/java/org/jboss/weld/bean/proxy/InterceptedProxyFactory.java
@@ -16,33 +16,31 @@
*/
package org.jboss.weld.bean.proxy;
-import static org.jboss.classfilewriter.util.DescriptorUtils.isPrimitive;
-import static org.jboss.classfilewriter.util.DescriptorUtils.isWide;
-
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.util.HashSet;
-import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
import java.util.Set;
-import org.jboss.classfilewriter.ClassFile;
-import org.jboss.classfilewriter.ClassMethod;
-import org.jboss.classfilewriter.DuplicateMemberException;
-import org.jboss.classfilewriter.code.CodeAttribute;
-import org.jboss.classfilewriter.util.Boxing;
-import org.jboss.classfilewriter.util.DescriptorUtils;
import org.jboss.weld.annotated.enhanced.MethodSignature;
import org.jboss.weld.annotated.enhanced.jlr.MethodSignatureImpl;
-import org.jboss.weld.bean.proxy.InterceptionDecorationContext.Stack;
import org.jboss.weld.exceptions.WeldException;
import org.jboss.weld.injection.InterceptionFactoryImpl;
+import org.jboss.weld.interceptor.util.proxy.TargetInstanceProxy;
import org.jboss.weld.logging.BeanLogger;
-import org.jboss.weld.util.bytecode.BytecodeUtils;
-import org.jboss.weld.util.bytecode.MethodInformation;
-import org.jboss.weld.util.bytecode.RuntimeMethodInformation;
import org.jboss.weld.util.reflection.Reflections;
+import io.quarkus.gizmo2.Const;
+import io.quarkus.gizmo2.Expr;
+import io.quarkus.gizmo2.ParamVar;
+import io.quarkus.gizmo2.creator.BlockCreator;
+import io.quarkus.gizmo2.creator.ClassCreator;
+import io.quarkus.gizmo2.creator.InstanceMethodCreator;
+import io.quarkus.gizmo2.desc.FieldDesc;
+import io.quarkus.gizmo2.desc.MethodDesc;
+
/**
* Generates proxies used to apply interceptors to custom bean instances and return values of producer methods.
*
@@ -56,8 +54,6 @@ public class InterceptedProxyFactory extends ProxyFactory {
public static final String PROXY_SUFFIX = "InterceptedProxy";
- private static final String JAVA_LANG_OBJECT = "java.lang.Object";
-
private final Set enhancedMethodSignatures;
private final Set interceptedMethodSignatures;
@@ -83,203 +79,259 @@ protected String getProxyNameSuffix() {
}
@Override
- protected void addMethodsFromClass(ClassFile proxyClassType, ClassMethod staticConstructor) {
- try {
-
- final Set finalMethods = new HashSet();
- final Set processedBridgeMethods = new HashSet();
+ protected void addMethods(ClassCreator cc) {
+ // For interface-based proxies, ensure all interface methods are collected
+ // This must be done before calling super.addMethods() which creates the static initializer
+ if (builtFromInterface) {
+ // This will be called by super.addMethods(), but we need to augment the list first
+ // Unfortunately we can't easily override collectMethodsToProxy() due to inner class access
+ // So we'll add missing methods in addMethodsFromClass() instead
+ }
+ super.addMethods(cc);
+ }
- // Add all methods from the class hierarchy + proxied type
- Set> classes = new LinkedHashSet<>();
- for (Class> cls = getBeanType(); cls != null; cls = cls.getSuperclass()) {
- classes.add(cls);
- }
- classes.add(getProxiedBeanType());
-
- for (Class> cls : classes) {
- Set declaredBridgeMethods = new HashSet();
- for (Method method : cls.getDeclaredMethods()) {
-
- final MethodSignatureImpl methodSignature = new MethodSignatureImpl(method);
-
- if (isMethodAccepted(method, getProxySuperclass()) && enhancedMethodSignatures.contains(methodSignature)
- && !finalMethods.contains(methodSignature) && !processedBridgeMethods.contains(methodSignature)) {
- try {
- final MethodInformation methodInfo = new RuntimeMethodInformation(method);
- ClassMethod classMethod = proxyClassType.addMethod(method);
-
- if (interceptedMethodSignatures.contains(methodSignature)) {
- // this method is intercepted
- createInterceptedMethod(classMethod, methodInfo, method, staticConstructor);
- BeanLogger.LOG.addingMethodToProxy(method);
- } else {
- createNotInterceptedMethod(classMethod, methodInfo, method, staticConstructor);
- }
-
- } catch (DuplicateMemberException e) {
- // do nothing. This will happen if superclass methods have
- // been overridden
- }
- } else {
- if (Modifier.isFinal(method.getModifiers())) {
- finalMethods.add(methodSignature);
- }
- if (method.isBridge()) {
- declaredBridgeMethods.add(methodSignature);
- }
- }
- }
- processedBridgeMethods.addAll(declaredBridgeMethods);
+ @Override
+ protected List collectMethodsToProxy() {
+ List methods = super.collectMethodsToProxy();
+
+ // For interface-based proxies, ensure ALL interface methods are included
+ // This is needed to avoid AbstractMethodError when the proxy is invoked
+ if (builtFromInterface) {
+ // Collect signatures of methods already in the list
+ Set existingMethods = new HashSet<>();
+ for (MethodInfo methodInfo : methods) {
+ existingMethods.add(new MethodSignatureImpl(methodInfo.method));
}
- if (builtFromInterface) {
- // since we are just on top of an interface, we will need to add all the methods from interface
- // hierarchy so that there are no AbstractMethodError exception popping up
- for (Class> c : interfacesToInspect) {
- for (Method method : c.getMethods()) {
- MethodSignature signature = new MethodSignatureImpl(method);
- try {
- if (isMethodAccepted(method, getProxySuperclass())
- && !processedBridgeMethods.contains(signature)) {
-
- final MethodInformation methodInfo = new RuntimeMethodInformation(method);
- ClassMethod classMethod = proxyClassType.addMethod(method);
- createNotInterceptedMethod(classMethod, methodInfo, method, staticConstructor);
- BeanLogger.LOG.addingMethodToProxy(method);
- }
- } catch (DuplicateMemberException e) {
- }
- if (method.isBridge()) {
- processedBridgeMethods.add(signature);
- }
- }
+
+ // Get all methods from the proxied interface (including inherited)
+ for (Method method : getProxiedBeanType().getMethods()) {
+ MethodSignature signature = new MethodSignatureImpl(method);
+ if (!existingMethods.contains(signature) && isMethodAccepted(method, getProxySuperclass())) {
+ // Add missing method to the list
+ methods.add(new MethodInfo(method,
+ Reflections.isDefault(method)));
+ existingMethods.add(signature);
}
}
- } catch (Exception e) {
- throw new WeldException(e);
}
+
+ return methods;
}
@Override
- public void addInterfacesFromTypeClosure(Set extends Type> typeClosure, Class> proxiedBeanType) {
- // store all interfaces we want to look into later, only usable if we make this proxy on top of an interface
- for (Type type : typeClosure) {
- Class> c = Reflections.getRawType(type);
- if (c.isInterface()) {
- addInterfaceToInspect(c);
+ protected void addMethodsFromClass(ClassCreator cc,
+ List methodsToProxy,
+ Map methodFieldNames) {
+
+ // Process all collected methods (including those added by collectMethodsToProxy override)
+ for (MethodInfo methodInfo : methodsToProxy) {
+ Method method = methodInfo.method;
+ MethodSignature methodSignature = new MethodSignatureImpl(method);
+
+ String methodFieldName = methodFieldNames.get(methodInfo);
+
+ // Check if method is in enhancedMethodSignatures (has interception metadata)
+ boolean isEnhanced = enhancedMethodSignatures.contains(methodSignature);
+
+ if (isEnhanced) {
+ // Enhanced method: check if it has interceptors
+ boolean hasInterceptors = interceptedMethodSignatures.contains(methodSignature);
+
+ if (hasInterceptors) {
+ // Intercepted method: use StackAwareMethodHandler with method as proceed
+ addInterceptedProxyMethod(cc, methodInfo, methodFieldName);
+ } else {
+ // Enhanced but non-intercepted method: use regular MethodHandler
+ super.addProxyMethod(cc, methodInfo, methodFieldName);
+ }
+ } else {
+ // Not enhanced: still add proxy method to avoid AbstractMethodError
+ // This is needed for interface-based proxies where all methods need implementations
+ super.addProxyMethod(cc, methodInfo, methodFieldName);
}
}
}
- @Override
- protected boolean isMethodAccepted(Method method, Class> proxySuperclass) {
- return super.isMethodAccepted(method, proxySuperclass)
- && CommonProxiedMethodFilters.NON_PRIVATE.accept(method, proxySuperclass) && !method.isBridge();
- }
+ /**
+ * Adds an intercepted proxy method that uses StackAwareMethodHandler.
+ * The method is passed as both thisMethod and proceed to the handler.
+ */
+ protected void addInterceptedProxyMethod(ClassCreator cc,
+ MethodInfo methodInfo, String methodFieldName) {
+ Method method = methodInfo.method;
+
+ // Create method descriptor from the reflection Method
+ MethodDesc methodDesc = MethodDesc.of(method);
+
+ cc.method(methodDesc, m -> {
+ // Set method modifiers
+ int modifiers = method.getModifiers();
+ if (Modifier.isPublic(modifiers)) {
+ m.public_();
+ } else if (Modifier.isProtected(modifiers)) {
+ m.protected_();
+ }
- private void createNotInterceptedMethod(ClassMethod classMethod, final MethodInformation methodInfo, Method method,
- ClassMethod staticConstructor) {
- // we only care about default and intercepted methods now
- final CodeAttribute b = classMethod.getCodeAttribute();
-
- b.aload(0);
- getMethodHandlerField(classMethod.getClassFile(), b);
-
- b.aload(0);
- DEFAULT_METHOD_RESOLVER.getDeclaredMethod(classMethod, methodInfo.getDeclaringClass(), method.getName(),
- methodInfo.getParameterTypes(), staticConstructor);
- b.aconstNull();
-
- b.iconst(method.getParameterCount());
- b.anewarray(JAVA_LANG_OBJECT);
-
- int localVariableCount = 1;
-
- for (int i = 0; i < method.getParameterCount(); ++i) {
- String typeString = methodInfo.getParameterTypes()[i];
- b.dup(); // duplicate the array reference
- b.iconst(i);
- // load the parameter value
- BytecodeUtils.addLoadInstruction(b, typeString, localVariableCount);
- // box the parameter if necessary
- Boxing.boxIfNessesary(b, typeString);
- // and store it in the array
- b.aastore();
- if (isWide(typeString)) {
- localVariableCount = localVariableCount + 2;
- } else {
- localVariableCount++;
+ // Set varargs flag if the method is varargs
+ if (method.isVarArgs()) {
+ m.varargs();
}
- }
- b.invokeinterface(MethodHandler.class.getName(), INVOKE_METHOD_NAME, LJAVA_LANG_OBJECT,
- new String[] { LJAVA_LANG_OBJECT, LJAVA_LANG_REFLECT_METHOD, LJAVA_LANG_REFLECT_METHOD,
- "[" + LJAVA_LANG_OBJECT });
+ // Add parameters
+ ParamVar[] params = new ParamVar[method.getParameterCount()];
+ for (int i = 0; i < method.getParameterCount(); i++) {
+ params[i] = m.parameter("arg" + i, method.getParameterTypes()[i]);
+ }
- if (methodInfo.getReturnType().equals(BytecodeUtils.VOID_CLASS_DESCRIPTOR)) {
- b.returnInstruction();
- } else if (isPrimitive(methodInfo.getReturnType())) {
- Boxing.unbox(b, classMethod.getReturnType());
- b.returnInstruction();
- } else {
- b.checkcast(BytecodeUtils.getName(methodInfo.getReturnType()));
- b.returnInstruction();
- }
+ // Add checked exceptions
+ for (Class> exceptionType : method.getExceptionTypes()) {
+ @SuppressWarnings("unchecked")
+ Class extends Throwable> throwableClass = (Class extends Throwable>) exceptionType;
+ m.throws_(throwableClass);
+ }
+
+ m.body(b -> {
+ // InterceptedProxy doesn't use constructed flag - it wraps an existing instance
+ // No constructed guard needed
+
+ // Invoke using StackAwareMethodHandler with method as proceed
+ invokeStackAwareMethodHandler(m, b, method, methodFieldName, params);
+ });
+ });
+
+ BeanLogger.LOG.addingMethodToProxy(method);
}
- private void createInterceptedMethod(ClassMethod classMethod, final MethodInformation methodInfo, Method method,
- ClassMethod staticConstructor) {
- final CodeAttribute b = classMethod.getCodeAttribute();
-
- b.aload(0);
- getMethodHandlerField(classMethod.getClassFile(), b);
-
- // get the Stack
- b.invokestatic(InterceptionDecorationContext.class.getName(), "getStack",
- "()" + DescriptorUtils.makeDescriptor(Stack.class));
-
- b.aload(0);
- DEFAULT_METHOD_RESOLVER.getDeclaredMethod(classMethod, methodInfo.getDeclaringClass(), method.getName(),
- methodInfo.getParameterTypes(), staticConstructor);
- b.dup();
- // Params
- b.iconst(method.getParameterCount());
- b.anewarray(JAVA_LANG_OBJECT);
- int localVariableCount = 1;
- for (int i = 0; i < method.getParameterCount(); ++i) {
- String typeString = methodInfo.getParameterTypes()[i];
- b.dup(); // duplicate the array reference
- b.iconst(i);
- // load the parameter value
- BytecodeUtils.addLoadInstruction(b, typeString, localVariableCount);
- // box the parameter if necessary
- Boxing.boxIfNessesary(b, typeString);
- // and store it in the array
- b.aastore();
- if (isWide(typeString)) {
- localVariableCount = localVariableCount + 2;
- } else {
- localVariableCount++;
+ /**
+ * Invokes the StackAwareMethodHandler: handler.invoke(stack, this, method, method, args)
+ * The method is passed as both thisMethod and proceed.
+ */
+ protected void invokeStackAwareMethodHandler(InstanceMethodCreator m,
+ BlockCreator b, Method method, String methodFieldName,
+ ParamVar[] params) {
+
+ // 1. Load this.methodHandler and cast to StackAwareMethodHandler
+ FieldDesc methodHandlerField = FieldDesc.of(
+ m.owner(),
+ METHOD_HANDLER_FIELD_NAME,
+ getMethodHandlerType());
+ Expr handlerField = b.get(m.this_().field(methodHandlerField));
+ Expr handler = b.cast(handlerField, StackAwareMethodHandler.class);
+
+ // 2. Get the Stack from InterceptionDecorationContext
+ MethodDesc getStackDesc = MethodDesc.of(
+ InterceptionDecorationContext.class,
+ "getStack",
+ InterceptionDecorationContext.Stack.class);
+ Expr stackExpr = b.invokeStatic(getStackDesc);
+ var stack = b.localVar("stack", stackExpr);
+
+ // 3. Load the static Method field
+ FieldDesc methodField = FieldDesc.of(
+ m.owner(),
+ methodFieldName,
+ Method.class);
+ Expr methodObj = Expr.staticField(methodField);
+
+ // 4. Store method in LocalVar so we can use it twice (as thisMethod and proceed)
+ var methodVar = b.localVar("method", methodObj);
+
+ // 5. Create and populate Object[] args array
+ Class>[] paramTypes = method.getParameterTypes();
+
+ Expr argsArray;
+ if (paramTypes.length == 0) {
+ argsArray = b.newEmptyArray(Object.class, 0);
+ } else {
+ Expr arrayExpr = b.newEmptyArray(Object.class, paramTypes.length);
+ var argsVar = b.localVar("args", arrayExpr);
+
+ for (int i = 0; i < paramTypes.length; i++) {
+ Expr paramValue = params[i];
+ // Box primitive types
+ if (paramTypes[i].isPrimitive()) {
+ paramValue = boxPrimitive(b, paramValue, paramTypes[i]);
+ }
+ b.set(argsVar.elem(i), paramValue);
}
+ argsArray = argsVar;
}
- b.invokeinterface(StackAwareMethodHandler.class.getName(), INVOKE_METHOD_NAME, LJAVA_LANG_OBJECT,
- InterceptedSubclassFactory.INVOKE_METHOD_PARAMETERS);
-
- if (methodInfo.getReturnType().equals(BytecodeUtils.VOID_CLASS_DESCRIPTOR)) {
- b.returnInstruction();
- } else if (isPrimitive(methodInfo.getReturnType())) {
- Boxing.unbox(b, classMethod.getReturnType());
- b.returnInstruction();
+ // 6. Call handler.invoke(stack, this, method, method, args)
+ // StackAwareMethodHandler.invoke(Stack, Object, Method, Method, Object[])
+ MethodDesc invokeDesc = MethodDesc.of(
+ StackAwareMethodHandler.class,
+ INVOKE_METHOD_NAME,
+ Object.class,
+ InterceptionDecorationContext.Stack.class, Object.class,
+ Method.class, Method.class, Object[].class);
+
+ // Pass methodVar twice - as both thisMethod and proceed
+ Expr result = b.invokeInterface(invokeDesc, handler,
+ stack, m.this_(), methodVar, methodVar, argsArray);
+
+ // 7. Handle return value
+ Class> returnType = method.getReturnType();
+ if (returnType == void.class) {
+ b.return_();
+ } else if (returnType.isPrimitive()) {
+ Expr unboxed = unboxPrimitive(b, result, returnType);
+ b.return_(unboxed);
} else {
- b.checkcast(BytecodeUtils.getName(methodInfo.getReturnType()));
- b.returnInstruction();
+ Expr casted = b.cast(result, returnType);
+ b.return_(casted);
}
}
- private void addInterfaceToInspect(Class> iface) {
- if (interfacesToInspect == null) {
- interfacesToInspect = new HashSet<>();
+ // DISABLED @Override
+
+ @Override
+ protected void addSpecialMethods(ClassCreator cc) {
+ try {
+ // Call parent to add LifecycleMixin and ProxyObject methods
+ super.addSpecialMethods(cc);
+
+ // Add TargetInstanceProxy methods (not implemented by parent)
+ Method getInstanceMethod = TargetInstanceProxy.class
+ .getMethod("weld_getTargetInstance");
+ generateGetTargetInstanceBody(cc, getInstanceMethod);
+
+ Method getInstanceClassMethod = TargetInstanceProxy.class
+ .getMethod("weld_getTargetClass");
+ generateGetTargetClassBody(cc, getInstanceClassMethod);
+ } catch (Exception e) {
+ throw new WeldException(e);
}
- this.interfacesToInspect.add(iface);
+ }
+
+ /**
+ * Generates weld_getTargetInstance() which returns 'this'.
+ */
+ private void generateGetTargetInstanceBody(ClassCreator cc,
+ Method method) {
+ cc.method(method.getName(), m -> {
+ m.public_();
+ m.returning(method.getReturnType());
+
+ m.body(b -> {
+ b.return_(m.this_());
+ });
+ });
+ }
+
+ /**
+ * Generates weld_getTargetClass() which returns the bean type class.
+ */
+ private void generateGetTargetClassBody(ClassCreator cc,
+ Method method) {
+ cc.method(method.getName(), m -> {
+ m.public_();
+ m.returning(method.getReturnType());
+
+ m.body(b -> {
+ Expr beanTypeClass = Const.of(getBeanType());
+ b.return_(beanTypeClass);
+ });
+ });
}
}
diff --git a/impl/src/main/java/org/jboss/weld/bean/proxy/InterceptedSubclassFactory.java b/impl/src/main/java/org/jboss/weld/bean/proxy/InterceptedSubclassFactory.java
index 970bbb06348..0279ff1a18d 100644
--- a/impl/src/main/java/org/jboss/weld/bean/proxy/InterceptedSubclassFactory.java
+++ b/impl/src/main/java/org/jboss/weld/bean/proxy/InterceptedSubclassFactory.java
@@ -17,30 +17,19 @@
package org.jboss.weld.bean.proxy;
-import static org.jboss.classfilewriter.util.DescriptorUtils.isPrimitive;
-import static org.jboss.classfilewriter.util.DescriptorUtils.isWide;
-import static org.jboss.classfilewriter.util.DescriptorUtils.makeDescriptor;
-
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
-import java.util.Arrays;
-import java.util.Collection;
+import java.util.ArrayList;
import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
import java.util.Set;
import jakarta.enterprise.inject.spi.Bean;
-import org.jboss.classfilewriter.AccessFlag;
-import org.jboss.classfilewriter.ClassFile;
-import org.jboss.classfilewriter.ClassMethod;
-import org.jboss.classfilewriter.DuplicateMemberException;
-import org.jboss.classfilewriter.code.BranchEnd;
-import org.jboss.classfilewriter.code.CodeAttribute;
-import org.jboss.classfilewriter.util.Boxing;
-import org.jboss.classfilewriter.util.DescriptorUtils;
import org.jboss.weld.annotated.enhanced.MethodSignature;
import org.jboss.weld.annotated.enhanced.jlr.MethodSignatureImpl;
import org.jboss.weld.bean.proxy.InterceptionDecorationContext.Stack;
@@ -48,11 +37,16 @@
import org.jboss.weld.interceptor.proxy.LifecycleMixin;
import org.jboss.weld.interceptor.util.proxy.TargetInstanceProxy;
import org.jboss.weld.logging.BeanLogger;
-import org.jboss.weld.util.bytecode.BytecodeUtils;
-import org.jboss.weld.util.bytecode.MethodInformation;
-import org.jboss.weld.util.bytecode.RuntimeMethodInformation;
+import org.jboss.weld.util.bytecode.DeferredBytecode;
import org.jboss.weld.util.reflection.Reflections;
+import io.quarkus.gizmo2.Const;
+import io.quarkus.gizmo2.Expr;
+import io.quarkus.gizmo2.ParamVar;
+import io.quarkus.gizmo2.creator.ClassCreator;
+import io.quarkus.gizmo2.desc.FieldDesc;
+import io.quarkus.gizmo2.desc.MethodDesc;
+
/**
* Factory for producing subclasses that are used by the combined interceptors and decorators stack.
*
@@ -67,8 +61,6 @@ public class InterceptedSubclassFactory extends ProxyFactory {
static final String COMBINED_INTERCEPTOR_AND_DECORATOR_STACK_METHOD_HANDLER_CLASS_NAME = CombinedInterceptorAndDecoratorStackMethodHandler.class
.getName();
- static final String[] INVOKE_METHOD_PARAMETERS = new String[] { makeDescriptor(Stack.class), LJAVA_LANG_OBJECT,
- LJAVA_LANG_REFLECT_METHOD, LJAVA_LANG_REFLECT_METHOD, "[" + LJAVA_LANG_OBJECT };
protected static final String PRIVATE_METHOD_HANDLER_FIELD_NAME = "privateMethodHandler";
@@ -102,6 +94,243 @@ public InterceptedSubclassFactory(String contextId, Class> proxiedBeanType, Se
}
@Override
+ protected boolean isMethodAccepted(Method method, Class> proxySuperclass) {
+ // Use parent filters first
+ if (!super.isMethodAccepted(method, proxySuperclass)) {
+ return false;
+ }
+ // Additionally filter out private methods with package-private parameters
+ // These would cause IllegalAccessError when the proxy subclass (in a different package)
+ // tries to reference the package-private type
+ return CommonProxiedMethodFilters.NON_PRIVATE_WITHOUT_PACK_PRIVATE_PARAMS.accept(method, proxySuperclass);
+ }
+
+ /**
+ * Override to handle bridge methods specially for intercepted subclasses.
+ * Bridge methods need special handling because:
+ * 1. We can't use invokespecial on interface methods (VerifyError)
+ * 2. We should skip bridge methods that have concrete implementations
+ * 3. We must intercept bridge methods that don't have concrete implementations
+ */
+ @Override
+ protected List collectMethodsToProxy() {
+ List methods = new ArrayList<>();
+ Class> cls = getBeanType();
+ Set foundFinalMethods = new HashSet<>();
+ Set addedMethods = new HashSet<>();
+ Set processedBridgeMethods = new HashSet<>();
+
+ // Add methods from the class hierarchy
+ while (cls != null) {
+ Method[] classDeclaredMethods = cls.getDeclaredMethods();
+ Set declaredBridgeMethods = new HashSet<>();
+
+ for (Method method : classDeclaredMethods) {
+ MethodSignature methodSignature = new MethodSignatureImpl(method);
+
+ if (Modifier.isFinal(method.getModifiers())) {
+ foundFinalMethods.add(methodSignature);
+ }
+
+ // Check if this is a bridge method with a concrete implementation
+ boolean skipBridgeMethod = method.isBridge() &&
+ hasConcreteImplementation(method, classDeclaredMethods);
+
+ // Check if method should be proxied (don't restrict to enhancedMethodSignatures - proxy all methods like parent)
+ if (isMethodAccepted(method, getProxySuperclass())
+ && !skipBridgeMethod
+ && !foundFinalMethods.contains(methodSignature)
+ && !addedMethods.contains(methodSignature)
+ && !bridgeMethodsContainsMethod(processedBridgeMethods, methodSignature,
+ method.getGenericReturnType(), Modifier.isAbstract(method.getModifiers()))) {
+ methods.add(new MethodInfo(method, false));
+ addedMethods.add(methodSignature);
+ } else {
+ // Track bridge methods even if we skip them
+ if (method.isBridge()) {
+ BridgeMethod bridgeMethod = new BridgeMethod(methodSignature, method.getGenericReturnType());
+ declaredBridgeMethods.add(bridgeMethod);
+ }
+ }
+ }
+
+ processedBridgeMethods.addAll(declaredBridgeMethods);
+ cls = cls.getSuperclass();
+ }
+
+ // Add methods from interfaces (including bridge methods from interfaces)
+ Set> allInterfaces = new HashSet<>(getAdditionalInterfaces());
+ if (interfacesToInspect != null) {
+ allInterfaces.addAll(interfacesToInspect);
+ }
+
+ for (Class> iface : allInterfaces) {
+ for (Method method : iface.getMethods()) {
+ MethodSignature signature = new MethodSignatureImpl(method);
+ // For interfaces we do not consider return types when checking bridge methods
+ if (enhancedMethodSignatures.contains(signature)
+ && !addedMethods.contains(signature) // Check if already added
+ && !bridgeMethodsContainsMethod(processedBridgeMethods, signature, null,
+ Modifier.isAbstract(method.getModifiers()))) {
+ // Only add default methods from interfaces
+ if (Reflections.isDefault(method)) {
+ methods.add(new MethodInfo(method, true));
+ addedMethods.add(signature);
+ }
+ }
+ if (method.isBridge()) {
+ processedBridgeMethods.add(new BridgeMethod(signature, method.getGenericReturnType()));
+ }
+ }
+ }
+
+ return methods;
+ }
+
+ /**
+ * Checks if a method signature is already covered by a processed bridge method.
+ */
+ private boolean bridgeMethodsContainsMethod(Set processedBridgeMethods,
+ MethodSignature signature, Type returnType, boolean isMethodAbstract) {
+ for (BridgeMethod bridgeMethod : processedBridgeMethods) {
+ if (bridgeMethod.signature.equals(signature)) {
+ // Method signature is equal (name and params) but return type can still differ
+ if (returnType != null) {
+ if (bridgeMethod.returnType.equals(Object.class) || isMethodAbstract) {
+ // Bridge method with matching signature has Object as return type
+ // or the method we compare against is abstract meaning the bridge overrides it
+ return true;
+ } else {
+ if (bridgeMethod.returnType instanceof Class && returnType instanceof TypeVariable) {
+ // Bridge method with specific return type in subclass
+ // and we are observing a TypeVariable return type in superclass
+ return true;
+ } else {
+ // As a last resort, check equality of return type
+ return bridgeMethod.returnType.equals(returnType);
+ }
+ }
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Checks if a bridge method has a concrete (non-bridge) implementation in the same class.
+ * Based on the old skipIfBridgeMethod logic.
+ */
+ @Override
+ protected boolean hasConcreteImplementation(Method bridgeMethod,
+ Method[] classDeclaredMethods) {
+ if (!bridgeMethod.isBridge()) {
+ return false;
+ }
+
+ String bridgeName = bridgeMethod.getName();
+ Class>[] bridgeParams = bridgeMethod.getParameterTypes();
+
+ for (Method declaredMethod : classDeclaredMethods) {
+ // Only check non-bridge declared methods
+ if (declaredMethod.isBridge()) {
+ continue;
+ }
+
+ if (bridgeName.equals(declaredMethod.getName())) {
+ Class>[] methodParams = bridgeMethod.getParameterTypes();
+ Class>[] declaredMethodParams = declaredMethod.getParameterTypes();
+
+ if (methodParams.length == declaredMethodParams.length) {
+ boolean paramsMatch = true;
+ boolean paramsNotMatching = false;
+ for (int i = 0; i < methodParams.length; i++) {
+ String methodParamName = methodParams[i].getName();
+ String declaredMethodParamName = declaredMethodParams[i].getName();
+ if (!methodParamName.equals(declaredMethodParamName)
+ && !methodParamName.equals(Object.class.getName())) {
+ paramsNotMatching = true;
+ break;
+ }
+ }
+
+ if (paramsNotMatching) {
+ continue;
+ }
+
+ // Parameters match, check if this is not an interface method
+ if (!Modifier.isInterface(declaredMethod.getDeclaringClass().getModifiers())) {
+ if (bridgeMethod.getReturnType().getName().equals(Object.class.getName())
+ || Modifier.isAbstract(declaredMethod.getModifiers())) {
+ // Bridge method with matching signature has Object as return type
+ // or the method we compare against is abstract meaning the bridge overrides it
+ return true;
+ } else {
+ // As a last resort, check equality of return type
+ if (bridgeMethod.getReturnType().getName().equals(declaredMethod.getReturnType().getName())) {
+ return true;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Helper class to track bridge methods with their return types.
+ */
+ private static class BridgeMethod {
+ private final Type returnType;
+ private final MethodSignature signature;
+
+ public BridgeMethod(MethodSignature signature, Type returnType) {
+ this.signature = signature;
+ this.returnType = returnType;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((returnType == null) ? 0 : returnType.hashCode());
+ result = prime * result + ((signature == null) ? 0 : signature.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (!(obj instanceof BridgeMethod)) {
+ return false;
+ }
+ BridgeMethod other = (BridgeMethod) obj;
+ if (returnType == null) {
+ if (other.returnType != null) {
+ return false;
+ }
+ } else if (!returnType.equals(other.returnType)) {
+ return false;
+ }
+ if (signature == null) {
+ if (other.signature != null) {
+ return false;
+ }
+ } else if (!signature.equals(other.signature)) {
+ return false;
+ }
+ return true;
+ }
+ }
+
+ // DISABLED @Override
public void addInterfacesFromTypeClosure(Set extends Type> typeClosure, Class> proxiedBeanType) {
// these interfaces we want to scan for method and our proxies will implement them
for (Class> c : proxiedBeanType.getInterfaces()) {
@@ -135,526 +364,770 @@ protected String getProxyNameSuffix() {
}
@Override
- protected void addMethods(ClassFile proxyClassType, ClassMethod staticConstructor) {
- // Add all class methods for interception
- addMethodsFromClass(proxyClassType, staticConstructor);
+ protected Class extends MethodHandler> getMethodHandlerType() {
+ return CombinedInterceptorAndDecoratorStackMethodHandler.class;
+ }
- // Add special proxy methods
- addSpecialMethods(proxyClassType, staticConstructor);
+ @Override
+ protected boolean isUsingProxyInstantiator() {
+ return false;
+ }
+ @Override
+ public Class> getBeanType() {
+ return proxiedBeanType;
}
- private boolean skipIfBridgeMethod(Method method, Collection classDeclaredMethods) {
- if (method.isBridge()) {
- // if it's a bridge method, we need to see if the class also contains an actual "impl" of that method
- // if it does, we can skip this method, if it doesn't we will need to intercept it
- for (Method declaredMethod : classDeclaredMethods) {
- // only check non-bridge declared methods
- if (declaredMethod.isBridge()) {
- continue;
- }
- if (method.getName().equals(declaredMethod.getName())) {
- Class>[] methodParams = method.getParameterTypes();
- Class>[] declaredMethodParams = declaredMethod.getParameterTypes();
- if (methodParams.length != declaredMethodParams.length) {
- continue;
- }
- boolean paramsNotMatching = false;
- for (int i = 0; i < methodParams.length; i++) {
- String methodParamName = methodParams[i].getName();
- String declaredMethodParamName = declaredMethodParams[i].getName();
- if (methodParamName.equals(declaredMethodParamName)
- || methodParamName.equals(Object.class.getName())) {
- continue;
- } else {
- paramsNotMatching = true;
+ @Override
+ protected void addFields(ClassCreator cc,
+ List initialValueBytecode) {
+ super.addFields(cc, initialValueBytecode);
+
+ // Add private method handler field for private method interception
+ cc.field(PRIVATE_METHOD_HANDLER_FIELD_NAME, f -> {
+ f.setType(MethodHandler.class);
+ f.private_();
+ });
+ }
+
+ @Override
+ protected void addMethodsFromClass(ClassCreator cc,
+ List methodsToProxy,
+ Map methodFieldNames) {
+
+ for (MethodInfo methodInfo : methodsToProxy) {
+ Method method = methodInfo.method;
+ String methodFieldName = methodFieldNames.get(methodInfo);
+
+ // Check if this method should be intercepted
+ MethodSignature methodSignature = new MethodSignatureImpl(method);
+ boolean hasInterceptors = interceptedMethodSignatures.contains(methodSignature);
+
+ // IMPORTANT: For bridge methods, check if the corresponding non-bridge method is intercepted
+ // Bridge methods use invokespecial which bypasses virtual dispatch, so we must override them
+ // in the proxy to ensure interception works
+ if (!hasInterceptors && !method.isBridge()) {
+ // This is a non-bridge method - check if there's a bridge with the same name that's intercepted
+ for (MethodInfo other : methodsToProxy) {
+ if (other.method.getName().equals(method.getName()) && other.method.isBridge()) {
+ MethodSignature otherSig = new MethodSignatureImpl(
+ other.method);
+ if (interceptedMethodSignatures.contains(otherSig)) {
+ hasInterceptors = true;
break;
}
}
- if (paramsNotMatching) {
- continue;
- }
- if (!Modifier.isInterface(declaredMethod.getDeclaringClass().getModifiers())) {
- if (method.getReturnType().getName().equals(Object.class.getName())
- || Modifier.isAbstract(declaredMethod.getModifiers())) {
- // bridge method with matching signature has Object as return type
- // or the method we compare against is abstract meaning the bridge overrides it
- // both cases are a match
- return true;
- } else {
- // as a last resort, we simply check equality of return Type
- if (method.getReturnType().getName().equals(declaredMethod.getReturnType().getName())) {
- return true;
+ }
+ }
+
+ if (hasInterceptors) {
+ // For intercepted methods, we need to differentiate between bridge and non-bridge methods
+ if (method.isBridge()) {
+ // For bridge methods, we need to determine if there's a corresponding non-bridge method
+ // that will handle the interception. If so, skip the bridge and let it delegate naturally.
+ // Only create a bridge delegate if the bridge is the ONLY intercepted method with this name.
+ boolean hasNonBridgeIntercepted = false;
+ for (MethodInfo other : methodsToProxy) {
+ if (other.method.getName().equals(method.getName()) && !other.method.isBridge()) {
+ MethodSignature otherSig = new MethodSignatureImpl(
+ other.method);
+ if (interceptedMethodSignatures.contains(otherSig)) {
+ hasNonBridgeIntercepted = true;
+ break;
}
}
}
- }
- }
- return false;
- } else {
- return false;
- }
- }
- @Override
- protected void addMethodsFromClass(ClassFile proxyClassType, ClassMethod staticConstructor) {
- try {
+ if (hasNonBridgeIntercepted) {
+ // Skip this bridge - there's a non-bridge method that's intercepted,
+ // and we'll create a proper bridge delegate after that method is created
+ continue;
+ } else {
+ // This bridge has no corresponding non-bridge intercepted method,
+ // so we need to intercept it directly
- final Set finalMethods = new HashSet();
- final Set processedBridgeMethods = new HashSet();
-
- // Add all methods from the class hierarchy
- Class> cls = getBeanType();
- while (cls != null) {
- Set declaredBridgeMethods = new HashSet();
- Collection classDeclaredMethods = Arrays
- .asList(cls.getDeclaredMethods().clone());
- for (Method method : classDeclaredMethods) {
-
- final MethodSignatureImpl methodSignature = new MethodSignatureImpl(method);
-
- if (!Modifier.isFinal(method.getModifiers()) && !skipIfBridgeMethod(method, classDeclaredMethods)
- && enhancedMethodSignatures.contains(methodSignature)
- && !finalMethods.contains(methodSignature)
- && CommonProxiedMethodFilters.NON_PRIVATE_WITHOUT_PACK_PRIVATE_PARAMS.accept(method,
- getProxySuperclass())
- && !bridgeMethodsContainsMethod(processedBridgeMethods, methodSignature,
- method.getGenericReturnType(), Modifier.isAbstract(method.getModifiers()))) {
- try {
- final MethodInformation methodInfo = new RuntimeMethodInformation(method);
-
- if (interceptedMethodSignatures.contains(methodSignature)) {
- // create delegate-to-super method
- createDelegateMethod(proxyClassType, method, methodInfo);
-
- // this method is intercepted
- // override a subclass method to delegate to method handler
- ClassMethod classMethod = proxyClassType.addMethod(method);
- addConstructedGuardToMethodBody(classMethod);
- createForwardingMethodBody(classMethod, methodInfo, staticConstructor);
- BeanLogger.LOG.addingMethodToProxy(method);
+ // First create the $$super method (skip for private methods only)
+ if (!Modifier.isPrivate(method.getModifiers())) {
+ if (methodInfo.isDefault) {
+ createDefaultMethodSuperDelegate(cc, method);
} else {
- // this method is not intercepted
- // we still need to override and push InterceptionDecorationContext stack to prevent full interception
- ClassMethod classMethod = proxyClassType.addMethod(method);
- new RunWithinInterceptionDecorationContextGenerator(classMethod, this) {
-
- @Override
- void doWork(CodeAttribute b, ClassMethod classMethod) {
- if (Modifier.isPrivate(classMethod.getAccessFlags())) {
- // Weld cannot use invokespecial to invoke a private method from the superclass
- invokePrivateMethodHandler(b, classMethod, methodInfo, staticConstructor);
- } else {
- // build the bytecode that invokes the super class method directly
- b.aload(0);
- // create the method invocation
- b.loadMethodParameters();
- b.invokespecial(methodInfo.getDeclaringClass(), methodInfo.getName(),
- methodInfo.getDescriptor());
- }
- // leave the result on top of the stack
- }
-
- @Override
- void doReturn(CodeAttribute b, ClassMethod method) {
- // assumes doWork() result is on top of the stack
- b.returnInstruction();
- }
- }.runStartIfNotOnTop();
+ createSuperDelegateMethod(cc, method);
}
-
- } catch (DuplicateMemberException e) {
- // do nothing. This will happen if superclass methods have
- // been overridden
}
- } else {
- if (Modifier.isFinal(method.getModifiers())) {
- finalMethods.add(methodSignature);
- }
- if (method.isBridge()) {
- BridgeMethod bridgeMethod = new BridgeMethod(methodSignature, method.getGenericReturnType());
- if (!hasAbstractPackagePrivateSuperClassWithImplementation(cls, bridgeMethod)) {
- declaredBridgeMethods.add(bridgeMethod);
- }
+
+ // Then create the intercepted method
+ createInterceptedMethod(cc, methodInfo, methodFieldName);
+ }
+ } else {
+ // For non-bridge intercepted methods, create TWO methods:
+ // 1. The regular method that delegates to the interceptor chain
+ // 2. The method$$super() that calls super.method() (used as proceed by interceptors)
+
+ // Create the $$super method first (skip for private methods only)
+ // For default interface methods, create a special $$super that uses invokeSpecial on bean class
+ if (!Modifier.isPrivate(method.getModifiers())) {
+ if (methodInfo.isDefault) {
+ createDefaultMethodSuperDelegate(cc, method);
+ } else {
+ createSuperDelegateMethod(cc, method);
}
}
- }
- processedBridgeMethods.addAll(declaredBridgeMethods);
- cls = cls.getSuperclass();
- }
- // We want to iterate over pre-defined interfaces (getAdditionalInterfaces()) and also over those we discovered earlier (interfacesToInspect)
- Set> allInterfaces = new HashSet<>(getAdditionalInterfaces());
- if (interfacesToInspect != null) {
- allInterfaces.addAll(interfacesToInspect);
- }
- for (Class> c : allInterfaces) {
- for (Method method : c.getMethods()) {
- MethodSignature signature = new MethodSignatureImpl(method);
- // For interfaces we do not consider return types when going through processed bridge methods
- if (enhancedMethodSignatures.contains(signature) && !bridgeMethodsContainsMethod(processedBridgeMethods,
- signature, null, Modifier.isAbstract(method.getModifiers()))) {
- try {
- MethodInformation methodInfo = new RuntimeMethodInformation(method);
- if (interceptedMethodSignatures.contains(signature) && Reflections.isDefault(method)) {
- createDelegateMethod(proxyClassType, method, methodInfo);
-
- // this method is intercepted
- // override a subclass method to delegate to method handler
- ClassMethod classMethod = proxyClassType.addMethod(method);
- addConstructedGuardToMethodBody(classMethod);
- createForwardingMethodBody(classMethod, methodInfo, staticConstructor);
- BeanLogger.LOG.addingMethodToProxy(method);
- } else {
- // we only want to add default methods, rest is abstract and cannot be invoked
- if (Reflections.isDefault(method)) {
- createDelegateMethod(proxyClassType, method, methodInfo);
- }
+
+ // Then create the regular method that delegates to interceptor chain
+ createInterceptedMethod(cc, methodInfo, methodFieldName);
+
+ // After creating the intercepted method, check if there are any bridge methods with the same name
+ // and create simple delegates for them (to prevent inherited bridges from using invokespecial)
+ for (MethodInfo bridgeCandidate : methodsToProxy) {
+ if (bridgeCandidate.method.isBridge()
+ && bridgeCandidate.method.getName().equals(method.getName())) {
+ MethodSignature bridgeSig = new MethodSignatureImpl(
+ bridgeCandidate.method);
+ if (interceptedMethodSignatures.contains(bridgeSig)) {
+ // Create a simple bridge that calls this non-bridge method
+ createBridgeDelegateToMethod(cc, bridgeCandidate.method, method);
}
- } catch (DuplicateMemberException e) {
}
}
- if (method.isBridge()) {
- processedBridgeMethods.add(new BridgeMethod(signature, method.getGenericReturnType()));
- }
+ }
+ } else {
+ // For non-intercepted methods in InterceptedSubclass, we still need special handling
+ // They must call super directly but need to manage InterceptionDecorationContext
+ // to prevent full interception when calling other methods
+ //
+ // EXCEPTION: Skip bridge methods and default interface methods - they naturally delegate
+ // to concrete methods and we can't use invokespecial on interface methods anyway
+ if (!method.isBridge() && !methodInfo.isDefault) {
+ createNonInterceptedMethod(cc, methodInfo, methodFieldName);
}
}
- } catch (Exception e) {
- throw new WeldException(e);
}
+
}
/**
- * Returns true if super class of the parameter exists and is abstract and package private. In such case we want to omit
- * such method.
- *
- * See WELD-2507 and Oracle issue - https://bugs.java.com/view_bug.do?bug_id=6342411
- *
- * @return true if the super class exists and is abstract and package private
+ * Creates a bridge method that delegates to a specific target method using virtual dispatch.
+ * This prevents the inherited bridge from using invokespecial which would bypass interception.
*/
- private boolean hasAbstractPackagePrivateSuperClassWithImplementation(Class> clazz, BridgeMethod bridgeMethod) {
- Class> superClass = clazz.getSuperclass();
- while (superClass != null) {
- if (Modifier.isAbstract(superClass.getModifiers()) && Reflections.isPackagePrivate(superClass.getModifiers())) {
- // if superclass is abstract, we need to dig deeper
- for (Method method : superClass.getDeclaredMethods()) {
- if (bridgeMethod.signature.matches(method) && method.getGenericReturnType().equals(bridgeMethod.returnType)
- && !Reflections.isAbstract(method)) {
- // this is the case we are after -> methods have same signature and the one in super class has actual implementation
- return true;
- }
- }
+ private void createBridgeDelegateToMethod(ClassCreator cc, Method bridgeMethod,
+ Method targetMethod) {
+ MethodDesc bridgeDesc = MethodDesc.of(bridgeMethod);
+ MethodDesc targetDesc = MethodDesc.of(targetMethod);
+
+ cc.method(bridgeDesc, m -> {
+ m.public_();
+ m.synthetic();
+ m.returning(bridgeMethod.getReturnType());
+
+ // Add parameters
+ Class>[] paramTypes = bridgeMethod.getParameterTypes();
+ ParamVar[] params = new ParamVar[paramTypes.length];
+ for (int i = 0; i < paramTypes.length; i++) {
+ params[i] = m.parameter("arg" + i, paramTypes[i]);
}
- superClass = superClass.getSuperclass();
- }
- return false;
- }
- private boolean bridgeMethodsContainsMethod(Set processedBridgeMethods, MethodSignature signature,
- Type returnType, boolean isMethodAbstract) {
- for (BridgeMethod bridgeMethod : processedBridgeMethods) {
- if (bridgeMethod.signature.equals(signature)) {
- // method signature is equal (name and params) but return type can still differ
- if (returnType != null) {
- if (bridgeMethod.returnType.equals(Object.class) || isMethodAbstract) {
- // bridge method with matching signature has Object as return type
- // or the method we compare against is abstract meaning the bridge overrides it
- // both cases are a match
- return true;
+ m.body(b -> {
+ // Call this.targetMethod(params) using virtual dispatch
+ // Cast parameters to match target method signature
+ Class>[] targetParamTypes = targetMethod.getParameterTypes();
+ Expr[] args = new Expr[params.length];
+ for (int i = 0; i < params.length; i++) {
+ if (paramTypes[i] != targetParamTypes[i]) {
+ args[i] = b.cast(params[i], targetParamTypes[i]);
} else {
- if (bridgeMethod.returnType instanceof Class && returnType instanceof TypeVariable) {
- // in this case we have encountered a bridge method with specific return type in subclass
- // and we are observing a TypeVariable return type in superclass, this is a match
- return true;
- } else {
- // as a last resort, we simply check equality of return Type
- return bridgeMethod.returnType.equals(returnType);
- }
+ args[i] = params[i];
}
}
- return true;
- }
- }
- return false;
- }
- protected void createForwardingMethodBody(ClassMethod classMethod, MethodInformation method,
- ClassMethod staticConstructor) {
- createInterceptorBody(classMethod, method, true, staticConstructor);
+ Expr result;
+ if (params.length == 0) {
+ result = b.invokeVirtual(targetDesc, m.this_());
+ } else if (params.length == 1) {
+ result = b.invokeVirtual(targetDesc, m.this_(), args[0]);
+ } else if (params.length == 2) {
+ result = b.invokeVirtual(targetDesc, m.this_(), args[0], args[1]);
+ } else {
+ result = b.invokeVirtual(targetDesc, m.this_(), args);
+ }
+
+ if (bridgeMethod.getReturnType() == void.class) {
+ b.return_();
+ } else {
+ b.return_(result);
+ }
+ });
+ });
}
/**
- * Creates the given method on the proxy class where the implementation
- * forwards the call directly to the method handler.
- *
- * the generated bytecode is equivalent to:
- *
- * return (RetType) methodHandler.invoke(this,param1,param2);
- *
- * @param methodInfo any JLR method
- * @param delegateToSuper
- * @return the method byte code
+ * Creates a method$$super() for default interface methods.
+ * Uses invokeSpecial on the bean class (superclass), which naturally delegates to the default interface method.
*/
+ private void createDefaultMethodSuperDelegate(ClassCreator cc, Method method) {
+ String superMethodName = method.getName() + SUPER_DELEGATE_SUFFIX;
+
+ cc.method(superMethodName, m -> {
+ // Make it private and synthetic
+ m.private_();
+ m.synthetic();
+ m.returning(method.getReturnType());
+
+ // Set varargs flag if the method is varargs
+ if (method.isVarArgs()) {
+ m.varargs();
+ }
- protected void createInterceptorBody(ClassMethod method, MethodInformation methodInfo, boolean delegateToSuper,
- ClassMethod staticConstructor) {
+ // Add parameters
+ Class>[] paramTypes = method.getParameterTypes();
+ ParamVar[] params = new ParamVar[paramTypes.length];
+ for (int i = 0; i < paramTypes.length; i++) {
+ params[i] = m.parameter("arg" + i, paramTypes[i]);
+ }
- invokeMethodHandler(method, methodInfo, true, DEFAULT_METHOD_RESOLVER, delegateToSuper, staticConstructor);
- }
+ // Add exception types
+ for (Class> exceptionType : method.getExceptionTypes()) {
+ @SuppressWarnings("unchecked")
+ Class extends Throwable> throwableType = (Class extends Throwable>) exceptionType;
+ m.throws_(throwableType);
+ }
- private void createDelegateToSuper(ClassMethod classMethod, MethodInformation method) {
- createDelegateToSuper(classMethod, method, classMethod.getClassFile().getSuperclass());
- }
+ m.body(b -> {
+ // For default interface methods, call the bean class (superclass) method via invokeSpecial
+ // Since the bean class doesn't override this method, it will naturally delegate to the
+ // default interface implementation
+ MethodDesc superMethodDesc = MethodDesc.of(getBeanType(), method.getName(),
+ method.getReturnType(), method.getParameterTypes());
+
+ Expr result;
+ if (params.length == 0) {
+ result = b.invokeSpecial(superMethodDesc, m.this_());
+ } else if (params.length == 1) {
+ result = b.invokeSpecial(superMethodDesc, m.this_(), params[0]);
+ } else if (params.length == 2) {
+ result = b.invokeSpecial(superMethodDesc, m.this_(), params[0], params[1]);
+ } else {
+ result = b.invokeSpecial(superMethodDesc, m.this_(), (Expr[]) params);
+ }
- private void createDelegateToSuper(ClassMethod classMethod, MethodInformation method, String className) {
- CodeAttribute b = classMethod.getCodeAttribute();
- // first generate the invokespecial call to the super class method
- b.aload(0);
- b.loadMethodParameters();
- b.invokespecial(className, method.getName(), method.getDescriptor());
- b.returnInstruction();
+ if (method.getReturnType() == void.class) {
+ b.return_();
+ } else {
+ b.return_(result);
+ }
+ });
+ });
}
/**
- * calls methodHandler.invoke for a given method
- *
- * @param methodInfo declaring class of the method
- * @param addReturnInstruction set to true you want to return the result of
- * @param bytecodeMethodResolver The method resolver
- * @param addProceed
+ * Creates a method$$super() that simply calls super.method().
+ * This is used by interceptors as the "proceed" method.
*/
- protected void invokeMethodHandler(ClassMethod method, MethodInformation methodInfo, boolean addReturnInstruction,
- BytecodeMethodResolver bytecodeMethodResolver, boolean addProceed, ClassMethod staticConstructor) {
- // now we need to build the bytecode. The order we do this in is as
- // follows:
- // load methodHandler
- // dup the methodhandler
- // invoke isDisabledHandler on the method handler to figure out of this is
- // a self invocation.
-
- // load this
- // load the method object
- // load the proceed method that invokes the superclass version of the
- // current method
- // create a new array the same size as the number of parameters
- // push our parameter values into the array
- // invokeinterface the invoke method
- // add checkcast to cast the result to the return type, or unbox if
- // primitive
- // add an appropriate return instruction
- final CodeAttribute b = method.getCodeAttribute();
- b.aload(0);
- getMethodHandlerField(method.getClassFile(), b);
-
- if (addProceed) {
- b.dup();
-
- // get the Stack
- b.invokestatic(InterceptionDecorationContext.class.getName(), "getStack",
- "()" + DescriptorUtils.makeDescriptor(Stack.class));
-
- // this is a self invocation optimisation
- // test to see if this is a self invocation, and if so invokespecial the
- // superclass method directly
- // Do not optimize in case of private and default methods
- if (!Reflections.isDefault(methodInfo.getMethod()) && !Modifier.isPrivate(method.getAccessFlags())) {
- b.dupX1(); // Handler, Stack -> Stack, Handler, Stack
- b.invokevirtual(COMBINED_INTERCEPTOR_AND_DECORATOR_STACK_METHOD_HANDLER_CLASS_NAME, "isDisabledHandler",
- "(" + DescriptorUtils.makeDescriptor(Stack.class) + ")" + BytecodeUtils.BOOLEAN_CLASS_DESCRIPTOR);
- b.iconst(0);
- BranchEnd invokeSuperDirectly = b.ifIcmpeq();
- // now build the bytecode that invokes the super class method
- b.pop2(); // pop Stack and Handler
- b.aload(0);
- // create the method invocation
- b.loadMethodParameters();
- b.invokespecial(methodInfo.getDeclaringClass(), methodInfo.getName(), methodInfo.getDescriptor());
- b.returnInstruction();
- b.branchEnd(invokeSuperDirectly);
+ private void createSuperDelegateMethod(ClassCreator cc, Method method) {
+ String superMethodName = method.getName() + SUPER_DELEGATE_SUFFIX;
+
+ cc.method(superMethodName, m -> {
+ // Make it private and synthetic
+ m.private_();
+ m.synthetic();
+ m.returning(method.getReturnType());
+
+ // Set varargs flag if the method is varargs
+ if (method.isVarArgs()) {
+ m.varargs();
}
- } else {
- b.aconstNull();
- }
-
- b.aload(0);
- bytecodeMethodResolver.getDeclaredMethod(method, methodInfo.getDeclaringClass(), methodInfo.getName(),
- methodInfo.getParameterTypes(), staticConstructor);
- if (addProceed) {
- if (Modifier.isPrivate(method.getAccessFlags())) {
- // If the original method is private we can't use WeldSubclass.method$$super() as proceed
- bytecodeMethodResolver.getDeclaredMethod(method, methodInfo.getDeclaringClass(), methodInfo.getName(),
- methodInfo.getParameterTypes(),
- staticConstructor);
- } else {
- bytecodeMethodResolver.getDeclaredMethod(method, method.getClassFile().getName(),
- methodInfo.getName() + SUPER_DELEGATE_SUFFIX,
- methodInfo.getParameterTypes(), staticConstructor);
+ // Add parameters
+ Class>[] paramTypes = method.getParameterTypes();
+ ParamVar[] params = new ParamVar[paramTypes.length];
+ for (int i = 0; i < paramTypes.length; i++) {
+ params[i] = m.parameter("arg" + i, paramTypes[i]);
}
- } else {
- b.aconstNull();
- }
- b.iconst(methodInfo.getParameterTypes().length);
- b.anewarray(Object.class.getName());
-
- int localVariableCount = 1;
-
- for (int i = 0; i < methodInfo.getParameterTypes().length; ++i) {
- String typeString = methodInfo.getParameterTypes()[i];
- b.dup(); // duplicate the array reference
- b.iconst(i);
- // load the parameter value
- BytecodeUtils.addLoadInstruction(b, typeString, localVariableCount);
- // box the parameter if necessary
- Boxing.boxIfNessesary(b, typeString);
- // and store it in the array
- b.aastore();
- if (isWide(typeString)) {
- localVariableCount = localVariableCount + 2;
- } else {
- localVariableCount++;
+ // Add exception types
+ for (Class> exceptionType : method.getExceptionTypes()) {
+ @SuppressWarnings("unchecked")
+ Class extends Throwable> throwableType = (Class extends Throwable>) exceptionType;
+ m.throws_(throwableType);
}
- }
- // now we have all our arguments on the stack
- // lets invoke the method
- b.invokeinterface(StackAwareMethodHandler.class.getName(), INVOKE_METHOD_NAME, LJAVA_LANG_OBJECT,
- INVOKE_METHOD_PARAMETERS);
- if (addReturnInstruction) {
- // now we need to return the appropriate type
- if (methodInfo.getReturnType().equals(BytecodeUtils.VOID_CLASS_DESCRIPTOR)) {
- b.returnInstruction();
- } else if (isPrimitive(methodInfo.getReturnType())) {
- Boxing.unbox(b, method.getReturnType());
- b.returnInstruction();
- } else {
- b.checkcast(BytecodeUtils.getName(methodInfo.getReturnType()));
- b.returnInstruction();
- }
- }
+
+ m.body(b -> {
+ // Call super.method(args)
+ MethodDesc superMethodDesc = MethodDesc.of(method);
+
+ Expr result;
+ if (params.length == 0) {
+ result = b.invokeSpecial(superMethodDesc, m.this_());
+ } else if (params.length == 1) {
+ result = b.invokeSpecial(superMethodDesc, m.this_(), params[0]);
+ } else if (params.length == 2) {
+ result = b.invokeSpecial(superMethodDesc, m.this_(), params[0], params[1]);
+ } else {
+ result = b.invokeSpecial(superMethodDesc, m.this_(), (Expr[]) params);
+ }
+
+ if (method.getReturnType() == void.class) {
+ b.return_();
+ } else {
+ b.return_(result);
+ }
+ });
+ });
}
/**
- * Adds methods requiring special implementations rather than just
- * delegation.
- *
- * @param proxyClassType the Javassist class description for the proxy type
+ * Creates the regular method that delegates to the interceptor chain.
+ * The interceptor chain will call method$$super() as the proceed method.
*/
- protected void addSpecialMethods(ClassFile proxyClassType, ClassMethod staticConstructor) {
+ private void createInterceptedMethod(ClassCreator cc, MethodInfo methodInfo,
+ String methodFieldName) {
+ Method method = methodInfo.method;
+ // Use methodInfo.isDefault to check if this is a default interface method
+ boolean isDefaultInterfaceMethod = methodInfo.isDefault;
+
+ // Use MethodDesc to properly handle overloaded methods
+ MethodDesc methodDesc = MethodDesc.of(method);
+
+ cc.method(methodDesc, m -> {
+ m.public_();
+ m.returning(method.getReturnType());
+
+ // Set varargs flag if the method is varargs
+ if (method.isVarArgs()) {
+ m.varargs();
+ }
+
+ // Add parameters
+ Class>[] paramTypes = method.getParameterTypes();
+ ParamVar[] params = new ParamVar[paramTypes.length];
+ for (int i = 0; i < paramTypes.length; i++) {
+ params[i] = m.parameter("arg" + i, paramTypes[i]);
+ }
+
+ // Add exception types
+ for (Class> exceptionType : method.getExceptionTypes()) {
+ @SuppressWarnings("unchecked")
+ Class extends Throwable> throwableType = (Class extends Throwable>) exceptionType;
+ m.throws_(throwableType);
+ }
+
+ m.body(b -> {
+ // Get the method handler field descriptor
+ FieldDesc methodHandlerField = FieldDesc.of(
+ cc.type(),
+ METHOD_HANDLER_FIELD_NAME,
+ getMethodHandlerType());
+
+ // Create a local variable to store the instance reference (to avoid repeated field access)
+ var thisVar = b.localVar("thisInstance", m.this_());
+
+ // For relaxed construction mode, if methodHandler is null (during construction),
+ // just call the super method directly (without interception)
+ // EXCEPT for bridge/default interface methods - they can't use invokespecial
+ if (!method.isBridge() && !isDefaultInterfaceMethod) {
+ Expr handlerCheck = b.get(thisVar.field(methodHandlerField));
+ Expr isNull = b.eq(handlerCheck, Const.ofNull(getMethodHandlerType()));
+ b.if_(isNull, nullBlock -> {
+ // Call super.method(args) directly
+ MethodDesc superMethodDesc = MethodDesc.of(method);
+
+ Expr superResult;
+ if (params.length == 0) {
+ superResult = nullBlock.invokeSpecial(superMethodDesc, thisVar);
+ } else if (params.length == 1) {
+ superResult = nullBlock.invokeSpecial(superMethodDesc, thisVar, params[0]);
+ } else if (params.length == 2) {
+ superResult = nullBlock.invokeSpecial(superMethodDesc, thisVar, params[0], params[1]);
+ } else {
+ superResult = nullBlock.invokeSpecial(superMethodDesc, thisVar, (Expr[]) params);
+ }
+
+ if (method.getReturnType() == void.class) {
+ nullBlock.return_();
+ } else {
+ nullBlock.return_(superResult);
+ }
+ });
+ }
+ // For bridge/default interface methods, skip the null check and always delegate to interceptor chain
+ // These will be invoked via reflection
+
+ // Read the handler field again (after null check, so we know it's not null here)
+ Expr handlerField = b.get(thisVar.field(methodHandlerField));
+
+ // Cast to StackAwareMethodHandler to ensure we call the correct invoke overload
+ Expr handler = b.cast(handlerField, StackAwareMethodHandler.class);
+
+ // Get the InterceptionDecorationContext Stack
+ MethodDesc getStackDesc = MethodDesc.of(
+ InterceptionDecorationContext.class,
+ "getStack",
+ InterceptionDecorationContext.Stack.class);
+ Expr stack = b.invokeStatic(getStackDesc);
+
+ // Get the Method object for this method (from static field)
+ FieldDesc methodField = FieldDesc.of(
+ cc.type(),
+ methodFieldName,
+ Method.class);
+ Expr thisMethodObj = Expr.staticField(methodField);
+
+ // Get the proceed Method object
+ // For private methods ONLY: use the original method (will be invoked via reflection)
+ // For all other methods (including bridge/default interface): use the $$super method
+ Expr proceedMethodObj;
+ if (Modifier.isPrivate(method.getModifiers())) {
+ // For private methods, the proceed method is the same as thisMethod
+ // These will be invoked via reflection by the method handler
+ proceedMethodObj = thisMethodObj;
+ } else {
+ // For all non-private methods (regular, bridge, default interface), look up the $$super method
+ // The $$super method will be created separately and calls super.method() using invokespecial
+ // Call this.getClass().getDeclaredMethod(methodName + "$$super", paramTypes)
+ Expr thisClass = b.invokeVirtual(
+ MethodDesc.of(Object.class, "getClass", Class.class),
+ m.this_());
+
+ Expr superMethodName = Const.of(method.getName() + SUPER_DELEGATE_SUFFIX);
+
+ // Create parameter types array
+ Expr paramTypesArray;
+ if (paramTypes.length == 0) {
+ paramTypesArray = b.newEmptyArray(Class.class, 0);
+ } else {
+ Expr paramTypesExpr = b.newEmptyArray(Class.class, paramTypes.length);
+ var paramTypesVar = b.localVar("paramTypes", paramTypesExpr);
+
+ for (int i = 0; i < paramTypes.length; i++) {
+ b.set(paramTypesVar.elem(i), Const.of(paramTypes[i]));
+ }
+ paramTypesArray = paramTypesVar;
+ }
+
+ proceedMethodObj = b.invokeVirtual(
+ MethodDesc.of(Class.class, "getDeclaredMethod",
+ Method.class, String.class, Class[].class),
+ thisClass, superMethodName, paramTypesArray);
+ }
+
+ // Create args array
+ Expr argsArray;
+ if (paramTypes.length == 0) {
+ argsArray = b.newEmptyArray(Object.class, 0);
+ } else {
+ Expr arrayExpr = b.newEmptyArray(Object.class, paramTypes.length);
+ var argsVar = b.localVar("args", arrayExpr);
+
+ for (int i = 0; i < paramTypes.length; i++) {
+ Expr paramValue = params[i];
+ if (paramTypes[i].isPrimitive()) {
+ paramValue = b.box(paramValue);
+ }
+ b.set(argsVar.elem(i), paramValue);
+ }
+ argsArray = argsVar;
+ }
+
+ // Call methodHandler.invoke(stack, this, thisMethod, proceedMethod, args)
+ // Use StackAwareMethodHandler interface which has a 5-parameter invoke method
+ MethodDesc invokeDesc = MethodDesc.of(
+ StackAwareMethodHandler.class,
+ INVOKE_METHOD_NAME,
+ Object.class,
+ InterceptionDecorationContext.Stack.class, Object.class, Method.class,
+ Method.class, Object[].class);
+
+ Expr result = b.invokeInterface(invokeDesc, handler,
+ stack, m.this_(), thisMethodObj, proceedMethodObj, argsArray);
+
+ // Handle return value
+ if (method.getReturnType() == void.class) {
+ b.return_();
+ } else if (method.getReturnType().isPrimitive()) {
+ Expr casted = b.cast(result, getWrapperType(method.getReturnType()));
+ Expr unboxed = b.unbox(casted);
+ b.return_(unboxed);
+ } else {
+ Expr casted = b.cast(result, method.getReturnType());
+ b.return_(casted);
+ }
+ });
+ });
+ }
+
+ @Override
+ protected void addSpecialMethods(ClassCreator cc) {
try {
- // Add special methods for interceptors
+ // Add LifecycleMixin methods (postConstruct, preDestroy) using Stack-aware invoke
for (Method method : LifecycleMixin.class.getMethods()) {
BeanLogger.LOG.addingMethodToProxy(method);
- MethodInformation methodInfo = new RuntimeMethodInformation(method);
- createInterceptorBody(proxyClassType.addMethod(method), methodInfo, false, staticConstructor);
+ generateStackAwareLifecycleMixinMethod(cc, method);
}
- Method getInstanceMethod = TargetInstanceProxy.class.getMethod("weld_getTargetInstance");
- Method getInstanceClassMethod = TargetInstanceProxy.class.getMethod("weld_getTargetClass");
- generateGetTargetInstanceBody(proxyClassType.addMethod(getInstanceMethod));
- generateGetTargetClassBody(proxyClassType.addMethod(getInstanceClassMethod));
- Method setMethodHandlerMethod = ProxyObject.class.getMethod("weld_setHandler", MethodHandler.class);
- generateSetMethodHandlerBody(proxyClassType.addMethod(setMethodHandlerMethod));
+ // Add TargetInstanceProxy methods
+ Method getInstanceMethod = TargetInstanceProxy.class
+ .getMethod("weld_getTargetInstance");
+ generateGetTargetInstanceBody(cc, getInstanceMethod);
+
+ Method getInstanceClassMethod = TargetInstanceProxy.class
+ .getMethod("weld_getTargetClass");
+ generateGetTargetClassBody(cc, getInstanceClassMethod);
+
+ // Add ProxyObject methods (getMethodHandler, setMethodHandler)
+ Method setMethodHandlerMethod = ProxyObject.class.getMethod("weld_setHandler",
+ MethodHandler.class);
+ generateSetMethodHandlerBody(cc, setMethodHandlerMethod);
Method getMethodHandlerMethod = ProxyObject.class.getMethod("weld_getHandler");
- generateGetMethodHandlerBody(proxyClassType.addMethod(getMethodHandlerMethod));
+ generateGetMethodHandlerBody(cc, getMethodHandlerMethod);
} catch (Exception e) {
throw new WeldException(e);
}
}
- private static void generateGetTargetInstanceBody(ClassMethod method) {
- final CodeAttribute b = method.getCodeAttribute();
- b.aload(0);
- b.returnInstruction();
+ /**
+ * Generates a LifecycleMixin method using Stack-aware invoke (5 parameters instead of 4).
+ * This is needed for InterceptedSubclass to properly handle the interception/decoration context.
+ */
+ private void generateStackAwareLifecycleMixinMethod(ClassCreator cc,
+ Method method) {
+ cc.method(method.getName(), m -> {
+ m.public_();
+ m.returning(void.class);
+
+ m.body(b -> {
+ // Get the method handler field
+ FieldDesc methodHandlerField = FieldDesc.of(
+ cc.type(),
+ METHOD_HANDLER_FIELD_NAME,
+ getMethodHandlerType());
+ Expr handlerField = b.get(m.this_().field(methodHandlerField));
+
+ // Cast to StackAwareMethodHandler to ensure we call the correct invoke overload
+ Expr handler = b.cast(handlerField, StackAwareMethodHandler.class);
+
+ // Get the InterceptionDecorationContext Stack
+ // Pass null to let the handler fetch the stack (lifecycle callbacks use this pattern)
+ Expr stack = Const.ofNull(InterceptionDecorationContext.Stack.class);
+
+ // Get the Method object for this lifecycle method
+ Expr lifecycleMixinClass = Const
+ .of(LifecycleMixin.class);
+ Expr methodName = Const.of(method.getName());
+ Expr emptyClassArray = b.newEmptyArray(Class.class, 0);
+
+ MethodDesc getMethodDesc = MethodDesc.of(
+ Class.class, "getMethod", Method.class, String.class, Class[].class);
+ Expr methodObj = b.invokeVirtual(getMethodDesc, lifecycleMixinClass,
+ methodName, emptyClassArray);
+
+ // Create null proceed Method parameter (lifecycle callbacks don't have proceed)
+ Expr nullMethod = Const.ofNull(Method.class);
+
+ // Create empty args array
+ Expr emptyArgs = b.newEmptyArray(Object.class, 0);
+
+ // Call methodHandler.invoke(stack, this, methodObj, null, emptyArgs)
+ // Use StackAwareMethodHandler's 5-parameter invoke
+ MethodDesc invokeDesc = MethodDesc.of(
+ StackAwareMethodHandler.class,
+ INVOKE_METHOD_NAME,
+ Object.class,
+ InterceptionDecorationContext.Stack.class, Object.class, Method.class,
+ Method.class, Object[].class);
+
+ b.invokeInterface(invokeDesc, handler, stack, m.this_(), methodObj, nullMethod, emptyArgs);
+
+ // Return (void method)
+ b.return_();
+ });
+ });
}
- private static void generateGetTargetClassBody(ClassMethod method) {
- final CodeAttribute b = method.getCodeAttribute();
- BytecodeUtils.pushClassType(b, method.getClassFile().getSuperclass());
- b.returnInstruction();
- }
+ /**
+ * Creates a non-intercepted method that calls super directly.
+ * These methods still need to manage the InterceptionDecorationContext to prevent
+ * full interception when they call other intercepted methods.
+ */
+ private void createNonInterceptedMethod(ClassCreator cc, MethodInfo methodInfo,
+ String methodFieldName) {
+ Method method = methodInfo.method;
- @Override
- public Class> getBeanType() {
- return proxiedBeanType;
- }
+ cc.method(method.getName(), m -> {
+ m.public_();
+ m.returning(method.getReturnType());
- @Override
- protected Class extends MethodHandler> getMethodHandlerType() {
- return CombinedInterceptorAndDecoratorStackMethodHandler.class;
- }
+ // Set varargs flag if the method is varargs
+ if (method.isVarArgs()) {
+ m.varargs();
+ }
- @Override
- protected boolean isUsingProxyInstantiator() {
- return false;
+ // Add parameters
+ Class>[] paramTypes = method.getParameterTypes();
+ ParamVar[] params = new ParamVar[paramTypes.length];
+ for (int i = 0; i < paramTypes.length; i++) {
+ params[i] = m.parameter("arg" + i, paramTypes[i]);
+ }
+
+ // Add exception types
+ for (Class> exceptionType : method.getExceptionTypes()) {
+ @SuppressWarnings("unchecked")
+ Class extends Throwable> throwableType = (Class extends Throwable>) exceptionType;
+ m.throws_(throwableType);
+ }
+
+ m.body(b -> {
+ // For non-intercepted methods, we need to manage the InterceptionDecorationContext
+ // to suppress interception for any calls made from within this method.
+ // This prevents interceptors from firing on direct/self-invocations.
+
+ // Get InterceptionDecorationContext stack
+ MethodDesc getStackDesc = MethodDesc.of(
+ InterceptionDecorationContext.class,
+ "getStack",
+ InterceptionDecorationContext.Stack.class);
+ Expr stackExpr = b.invokeStatic(getStackDesc);
+ var stack = b.localVar("stack", stackExpr);
+
+ // Get method handler to push onto stack
+ FieldDesc methodHandlerField = FieldDesc.of(
+ cc.type(),
+ METHOD_HANDLER_FIELD_NAME,
+ getMethodHandlerType());
+ Expr handler = b.get(m.this_().field(methodHandlerField));
+
+ // Call stack.startIfNotOnTop(handler)
+ // This returns true if we pushed, false if handler was already on top
+ MethodDesc startIfNotOnTopDesc = MethodDesc.of(
+ InterceptionDecorationContext.Stack.class,
+ "startIfNotOnTop",
+ boolean.class,
+ CombinedInterceptorAndDecoratorStackMethodHandler.class);
+ Expr shouldPopExpr = b.invokeVirtual(startIfNotOnTopDesc, stack, handler);
+ var shouldPop = b.localVar("shouldPop", shouldPopExpr);
+
+ // Call the method
+ // - For private/interface methods: use privateMethodHandler to invoke via reflection
+ // - For regular class methods: call super.method() directly with invokeSpecial
+ Expr result;
+ boolean isInterfaceMethod = method.getDeclaringClass().isInterface();
+ if (Modifier.isPrivate(method.getModifiers()) || isInterfaceMethod) {
+ // For private/interface methods, use the private method handler to invoke via reflection
+ // Get the privateMethodHandler field
+ FieldDesc privateMethodHandlerField = FieldDesc.of(
+ cc.type(),
+ PRIVATE_METHOD_HANDLER_FIELD_NAME,
+ MethodHandler.class);
+ Expr privateHandler = b.get(m.this_().field(privateMethodHandlerField));
+
+ // Get the Method object for this private method
+ FieldDesc methodField = FieldDesc.of(
+ cc.type(),
+ methodFieldName,
+ Method.class);
+ Expr thisMethodObj = Expr.staticField(methodField);
+
+ // Create args array
+ Expr argsArray;
+ if (paramTypes.length == 0) {
+ argsArray = b.newEmptyArray(Object.class, 0);
+ } else {
+ Expr arrayExpr = b.newEmptyArray(Object.class, paramTypes.length);
+ var argsVar = b.localVar("args", arrayExpr);
+
+ for (int i = 0; i < paramTypes.length; i++) {
+ Expr paramValue = params[i];
+ if (paramTypes[i].isPrimitive()) {
+ paramValue = b.box(paramValue);
+ }
+ b.set(argsVar.elem(i), paramValue);
+ }
+ argsArray = argsVar;
+ }
+
+ // Call privateMethodHandler.invoke(this, thisMethod, null, args)
+ MethodDesc invokeDesc = MethodDesc.of(
+ MethodHandler.class,
+ INVOKE_METHOD_NAME,
+ Object.class,
+ Object.class, Method.class,
+ Method.class, Object[].class);
+ result = b.invokeInterface(invokeDesc, privateHandler,
+ m.this_(), thisMethodObj, Const.ofNull(Method.class), argsArray);
+ } else {
+ // For regular class methods (non-private, non-interface), call super.method(args) directly
+ MethodDesc superMethodDesc = MethodDesc.of(method);
+ if (params.length == 0) {
+ result = b.invokeSpecial(superMethodDesc, m.this_());
+ } else if (params.length == 1) {
+ result = b.invokeSpecial(superMethodDesc, m.this_(), params[0]);
+ } else if (params.length == 2) {
+ result = b.invokeSpecial(superMethodDesc, m.this_(), params[0], params[1]);
+ } else {
+ result = b.invokeSpecial(superMethodDesc, m.this_(), (Expr[]) params);
+ }
+ }
+
+ // If we pushed onto the stack, pop it now
+ Expr shouldPopCheck = b.eq(shouldPop, Const.of(true));
+ b.if_(shouldPopCheck, popBlock -> {
+ MethodDesc endDesc = MethodDesc.of(
+ InterceptionDecorationContext.Stack.class,
+ "end",
+ void.class);
+ popBlock.invokeVirtual(endDesc, stack);
+ });
+
+ // Return the result
+ if (method.getReturnType() == void.class) {
+ b.return_();
+ } else {
+ b.return_(result);
+ }
+ });
+ });
}
- @SuppressWarnings("unchecked")
- private void createDelegateMethod(ClassFile proxyClassType, Method method, MethodInformation methodInformation) {
- int modifiers = (method.getModifiers() | AccessFlag.SYNTHETIC | AccessFlag.PRIVATE) & ~AccessFlag.PUBLIC
- & ~AccessFlag.PROTECTED;
- ClassMethod delegatingMethod = proxyClassType.addMethod(modifiers, method.getName() + SUPER_DELEGATE_SUFFIX,
- DescriptorUtils.makeDescriptor(method.getReturnType()),
- DescriptorUtils.parameterDescriptors(method.getParameterTypes()));
- delegatingMethod.addCheckedExceptions((Class extends Exception>[]) method.getExceptionTypes());
- createDelegateToSuper(delegatingMethod, methodInformation);
+ /**
+ * Generates weld_getTargetInstance() which returns 'this'.
+ * Note: The method is declared in TargetInstanceProxy to return T, but at runtime
+ * it's erased to Object. We use Object as the return type to match the erased signature.
+ */
+ private void generateGetTargetInstanceBody(ClassCreator cc,
+ Method method) {
+ cc.method(method.getName(), m -> {
+ m.public_();
+ // Use the actual return type from the method (which is erased to Object)
+ m.returning(method.getReturnType());
+
+ m.body(b -> {
+ // Simply return this
+ b.return_(m.this_());
+ });
+ });
}
- private void invokePrivateMethodHandler(CodeAttribute b, ClassMethod classMethod, MethodInformation methodInfo,
- ClassMethod staticConstructor) {
- try {
- classMethod.getClassFile().addField(AccessFlag.PRIVATE, PRIVATE_METHOD_HANDLER_FIELD_NAME, MethodHandler.class);
- } catch (DuplicateMemberException ignored) {
- }
- // 1. Load private method handler
- b.aload(0);
- b.getfield(classMethod.getClassFile().getName(), PRIVATE_METHOD_HANDLER_FIELD_NAME,
- DescriptorUtils.makeDescriptor(MethodHandler.class));
- // 2. Load this
- b.aload(0);
- // 3. Load method
- DEFAULT_METHOD_RESOLVER.getDeclaredMethod(classMethod, methodInfo.getDeclaringClass(), methodInfo.getName(),
- methodInfo.getParameterTypes(),
- staticConstructor);
- // 4. No proceed method
- b.aconstNull();
- // 5. Load method params
- b.iconst(methodInfo.getParameterTypes().length);
- b.anewarray(Object.class.getName());
- int localVariableCount = 1;
- for (int i = 0; i < methodInfo.getParameterTypes().length; ++i) {
- String typeString = methodInfo.getParameterTypes()[i];
- b.dup(); // duplicate the array reference
- b.iconst(i);
- // load the parameter value
- BytecodeUtils.addLoadInstruction(b, typeString, localVariableCount);
- // box the parameter if necessary
- Boxing.boxIfNessesary(b, typeString);
- // and store it in the array
- b.aastore();
- if (isWide(typeString)) {
- localVariableCount = localVariableCount + 2;
- } else {
- localVariableCount++;
- }
- }
- // Invoke PrivateMethodHandler
- b.invokeinterface(MethodHandler.class.getName(), INVOKE_METHOD_NAME, LJAVA_LANG_OBJECT,
- new String[] { LJAVA_LANG_OBJECT, LJAVA_LANG_REFLECT_METHOD, LJAVA_LANG_REFLECT_METHOD,
- "[" + LJAVA_LANG_OBJECT });
- if (methodInfo.getReturnType().equals(BytecodeUtils.VOID_CLASS_DESCRIPTOR)) {
- // No-op
- } else if (isPrimitive(methodInfo.getReturnType())) {
- Boxing.unbox(b, methodInfo.getReturnType());
- } else {
- b.checkcast(BytecodeUtils.getName(methodInfo.getReturnType()));
- }
+ /**
+ * Generates weld_getTargetClass() which returns the bean type class.
+ * Note: The method is declared in TargetInstanceProxy to return Class extends T>,
+ * but at runtime it's erased to Class. We match the erased signature.
+ */
+ private void generateGetTargetClassBody(ClassCreator cc,
+ Method method) {
+ cc.method(method.getName(), m -> {
+ m.public_();
+ // Use the actual return type from the method (which is erased to Class)
+ m.returning(method.getReturnType());
+
+ m.body(b -> {
+ // Return the bean type class (superclass of this proxy)
+ Expr beanTypeClass = Const.of(getBeanType());
+ b.return_(beanTypeClass);
+ });
+ });
}
/**
- * If the given instance represents a proxy and its class is synthetic and its class name ends with {@value #PROXY_SUFFIX},
- * attempt to find the
- * {@value #PRIVATE_METHOD_HANDLER_FIELD_NAME} field and set its value to {@link PrivateMethodHandler#INSTANCE}.
+ * Temporarily stubbed - not yet migrated to Gizmo 2
*
* @param instance
*/
@@ -672,62 +1145,4 @@ public static void setPrivateMethodHandler(T instance) {
}
}
}
-
- private static class BridgeMethod {
-
- private final Type returnType;
-
- private final MethodSignature signature;
-
- public BridgeMethod(MethodSignature signature, Type returnType) {
- this.signature = signature;
- this.returnType = returnType;
- }
-
- @Override
- public int hashCode() {
- final int prime = 31;
- int result = 1;
- result = prime * result + ((returnType == null) ? 0 : returnType.hashCode());
- result = prime * result + ((signature == null) ? 0 : signature.hashCode());
- return result;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj) {
- return true;
- }
- if (obj == null) {
- return false;
- }
- if (!(obj instanceof BridgeMethod)) {
- return false;
- }
- BridgeMethod other = (BridgeMethod) obj;
- if (returnType == null) {
- if (other.returnType != null) {
- return false;
- }
- } else if (!returnType.equals(other.returnType)) {
- return false;
- }
- if (signature == null) {
- if (other.signature != null) {
- return false;
- }
- } else if (!signature.equals(other.signature)) {
- return false;
- }
- return true;
- }
-
- @Override
- public String toString() {
- return new StringBuilder().append("method ").append(returnType).append(" ").append(signature.getMethodName())
- .append(Arrays.toString(signature.getParameterTypes()).replace('[', '(').replace(']', ')')).toString();
- }
-
- }
-
}
diff --git a/impl/src/main/java/org/jboss/weld/bean/proxy/ProtectionDomainCache.java b/impl/src/main/java/org/jboss/weld/bean/proxy/ProtectionDomainCache.java
index d6e55d0b443..812c45be21e 100644
--- a/impl/src/main/java/org/jboss/weld/bean/proxy/ProtectionDomainCache.java
+++ b/impl/src/main/java/org/jboss/weld/bean/proxy/ProtectionDomainCache.java
@@ -35,7 +35,6 @@
* block
* to be able to resolve methods using {@link Class#getDeclaredMethod(String, Class...)}.
*
- * @see BytecodeMethodResolver
* @see WELD-1751
*
* @author Jozef Hartinger
diff --git a/impl/src/main/java/org/jboss/weld/bean/proxy/ProxyFactory.java b/impl/src/main/java/org/jboss/weld/bean/proxy/ProxyFactory.java
index 5d430de7796..ab89c1c0fd7 100644
--- a/impl/src/main/java/org/jboss/weld/bean/proxy/ProxyFactory.java
+++ b/impl/src/main/java/org/jboss/weld/bean/proxy/ProxyFactory.java
@@ -17,13 +17,12 @@
package org.jboss.weld.bean.proxy;
-import static org.jboss.classfilewriter.util.DescriptorUtils.isPrimitive;
-import static org.jboss.classfilewriter.util.DescriptorUtils.isWide;
import static org.jboss.weld.util.reflection.Reflections.cast;
import java.io.File;
import java.io.IOException;
import java.io.Serializable;
+import java.lang.constant.ClassDesc;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
@@ -34,22 +33,16 @@
import java.security.ProtectionDomain;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
+import java.util.Map;
import java.util.Set;
import jakarta.enterprise.inject.spi.Bean;
-import org.jboss.classfilewriter.AccessFlag;
-import org.jboss.classfilewriter.ClassFile;
-import org.jboss.classfilewriter.ClassMethod;
-import org.jboss.classfilewriter.DuplicateMemberException;
-import org.jboss.classfilewriter.code.BranchEnd;
-import org.jboss.classfilewriter.code.CodeAttribute;
-import org.jboss.classfilewriter.util.Boxing;
-import org.jboss.classfilewriter.util.DescriptorUtils;
import org.jboss.weld.Container;
import org.jboss.weld.annotated.enhanced.MethodSignature;
import org.jboss.weld.annotated.enhanced.jlr.MethodSignatureImpl;
@@ -67,15 +60,23 @@
import org.jboss.weld.serialization.spi.ProxyServices;
import org.jboss.weld.util.Proxies;
import org.jboss.weld.util.Proxies.TypeInfo;
-import org.jboss.weld.util.bytecode.BytecodeUtils;
import org.jboss.weld.util.bytecode.ConstructorUtils;
import org.jboss.weld.util.bytecode.DeferredBytecode;
-import org.jboss.weld.util.bytecode.MethodInformation;
-import org.jboss.weld.util.bytecode.RuntimeMethodInformation;
import org.jboss.weld.util.collections.ImmutableSet;
import org.jboss.weld.util.collections.Sets;
import org.jboss.weld.util.reflection.Reflections;
+import io.quarkus.gizmo2.Const;
+import io.quarkus.gizmo2.Expr;
+import io.quarkus.gizmo2.Gizmo;
+import io.quarkus.gizmo2.ParamVar;
+import io.quarkus.gizmo2.creator.BlockCreator;
+import io.quarkus.gizmo2.creator.ClassCreator;
+import io.quarkus.gizmo2.creator.InstanceMethodCreator;
+import io.quarkus.gizmo2.desc.ConstructorDesc;
+import io.quarkus.gizmo2.desc.FieldDesc;
+import io.quarkus.gizmo2.desc.MethodDesc;
+
/**
* Main factory to produce proxy classes and instances for Weld beans. This
* implementation creates proxies which forward non-static method invocations to
@@ -94,15 +95,6 @@ public class ProxyFactory {
public static final String WELD_PROXY_PREFIX = "org.jboss.weld.generated.proxies";
public static final String DEFAULT_PROXY_PACKAGE = WELD_PROXY_PREFIX + ".default";
public static final String CONSTRUCTED_FLAG_NAME = "constructed";
- protected static final BytecodeMethodResolver DEFAULT_METHOD_RESOLVER = new DefaultBytecodeMethodResolver();
- protected static final String LJAVA_LANG_REFLECT_METHOD = "Ljava/lang/reflect/Method;";
- protected static final String LJAVA_LANG_BYTE = "Ljava/lang/Byte;";
- protected static final String LJAVA_LANG_CLASS = "Ljava/lang/Class;";
- protected static final String LJAVA_LANG_OBJECT = "Ljava/lang/Object;";
- protected static final String LBEAN_IDENTIFIER = "Lorg/jboss/weld/serialization/spi/BeanIdentifier;";
- protected static final String LJAVA_LANG_STRING = "Ljava/lang/String;";
- protected static final String LJAVA_LANG_THREAD_LOCAL = "Ljava/lang/ThreadLocal;";
- protected static final String INIT_METHOD_NAME = "";
protected static final String INVOKE_METHOD_NAME = "invoke";
protected static final String METHOD_HANDLER_FIELD_NAME = "methodHandler";
static final String JAVA = "java";
@@ -468,37 +460,58 @@ private Class createProxyClass(Class> originalClass, String proxyClassName)
// Remove special interfaces from main set (deserialization scenario)
additionalInterfaces.removeAll(specialInterfaces);
- ClassFile proxyClassType = null;
- final int accessFlags = AccessFlag.of(AccessFlag.PUBLIC, AccessFlag.SUPER, AccessFlag.SYNTHETIC);
- if (getBeanType().isInterface()) {
- proxyClassType = newClassFile(proxyClassName, accessFlags, Object.class.getName());
- proxyClassType.addInterface(getBeanType().getName());
- } else {
- proxyClassType = newClassFile(proxyClassName, accessFlags, getBeanType().getName());
- }
- // Add interfaces which require method generation
- for (Class> clazz : additionalInterfaces) {
- proxyClassType.addInterface(clazz.getName());
- }
List initialValueBytecode = new ArrayList();
- // Workaround for IBM JVM - the ACC_STATIC flag should only be required for class file with version number 51.0 or above
- ClassMethod staticConstructor = proxyClassType.addMethod(AccessFlag.of(AccessFlag.PUBLIC, AccessFlag.STATIC),
- "", "V");
+ // Create ByteArrayClassOutput to capture generated bytecode
+ ByteArrayClassOutput classOutput = new ByteArrayClassOutput();
+ Gizmo gizmo = Gizmo.create(classOutput);
+
+ // Generate proxy class using Gizmo 2
+ gizmo.class_(proxyClassName, cc -> {
+ // Set modifiers (public, synthetic)
+ cc.public_();
+ cc.synthetic();
+
+ // Set superclass
+ if (getBeanType().isInterface()) {
+ cc.extends_(Object.class);
+ cc.implements_(getBeanType());
+ } else {
+ cc.extends_(getBeanType());
+ }
- addFields(proxyClassType, initialValueBytecode);
- addConstructors(proxyClassType, initialValueBytecode);
- addMethods(proxyClassType, staticConstructor);
+ // Add interfaces which require method generation
+ for (Class> clazz : additionalInterfaces) {
+ cc.implements_(clazz);
+ }
+
+ // Additional interfaces whose methods require special handling
+ for (Class> specialInterface : specialInterfaces) {
+ cc.implements_(specialInterface);
+ }
- staticConstructor.getCodeAttribute().returnInstruction();
+ // Add fields
+ addFields(cc, initialValueBytecode);
- // Additional interfaces whose methods require special handling
- for (Class> specialInterface : specialInterfaces) {
- proxyClassType.addInterface(specialInterface.getName());
+ // Add constructors
+ addConstructors(cc, initialValueBytecode);
+
+ // Add methods (includes static initializer)
+ addMethods(cc);
+
+ // Add serialization support
+ addSerializationSupport(cc);
+ });
+
+ // Get generated bytecode
+ byte[] bytecode = classOutput.getBytes();
+
+ if (bytecode == null) {
+ throw new WeldException("Failed to generate proxy class: " + proxyClassName);
}
// Dump proxy type bytecode if necessary
- dumpToFile(proxyClassName, proxyClassType.toBytecode());
+ dumpToFile(proxyClassName, bytecode);
ProtectionDomain domain = proxiedBeanType.getProtectionDomain();
@@ -506,23 +519,12 @@ private Class createProxyClass(Class> originalClass, String proxyClassName)
|| proxiedBeanType.equals(Object.class)) {
domain = ProxyFactory.class.getProtectionDomain();
}
- Class proxyClass = cast(toClass(proxyClassType, originalClass, proxyServices, domain));
+
+ Class proxyClass = cast(toClass(bytecode, proxyClassName, originalClass, proxyServices, domain));
BeanLogger.LOG.createdProxyClass(proxyClass, Arrays.toString(proxyClass.getInterfaces()));
return proxyClass;
}
- private ClassFile newClassFile(String name, int accessFlags, String superclass, String... interfaces) {
- try {
- // We need to use a (non-deprecated) method that avoids instantiating DefaultClassFactory.INSTANCE
- // If that happens, we will have module accessibility issues and the need to use --add-opens clausules
- // NOTE: the CL and ClassFactory are never really used to define the class, see WeldDefaultProxyServices
- return new ClassFile(name, accessFlags, superclass, ProxyFactory.class.getClassLoader(),
- DummyClassFactoryImpl.INSTANCE, interfaces);
- } catch (Exception e) {
- throw BeanLogger.LOG.unableToCreateClassFile(name, e.getCause());
- }
- }
-
private void dumpToFile(String fileName, byte[] data) {
File proxyDumpFilePath = configuration.getProxyDumpFilePath();
if (proxyDumpFilePath == null) {
@@ -537,34 +539,33 @@ private void dumpToFile(String fileName, byte[] data) {
}
/**
- * Adds a constructor for the proxy for each constructor declared by the base
- * bean type.
+ * Adds constructors to the proxy class using Gizmo 2 API.
*
- * @param proxyClassType the Javassist class for the proxy
- * @param initialValueBytecode
+ * @param cc the class creator
+ * @param initialValueBytecode deferred bytecode for field initialization
*/
- protected void addConstructors(ClassFile proxyClassType, List initialValueBytecode) {
+ protected void addConstructors(ClassCreator cc, List initialValueBytecode) {
try {
if (getBeanType().isInterface()) {
- ConstructorUtils.addDefaultConstructor(proxyClassType, initialValueBytecode, !useConstructedFlag());
+ // Interface-based proxy: add default constructor calling Object()
+ ConstructorUtils.addDefaultConstructor(cc, Object.class, initialValueBytecode, !useConstructedFlag());
} else {
+ // Class-based proxy: mirror all non-private constructors from the bean type
boolean constructorFound = false;
for (Constructor> constructor : getBeanType().getDeclaredConstructors()) {
if ((constructor.getModifiers() & Modifier.PRIVATE) == 0) {
constructorFound = true;
- String[] exceptions = new String[constructor.getExceptionTypes().length];
- for (int i = 0; i < exceptions.length; ++i) {
- exceptions[i] = constructor.getExceptionTypes()[i].getName();
- }
- ConstructorUtils.addConstructor(BytecodeUtils.VOID_CLASS_DESCRIPTOR,
- DescriptorUtils.parameterDescriptors(constructor.getParameterTypes()), exceptions,
- proxyClassType, initialValueBytecode, !useConstructedFlag());
+ Class>[] paramTypes = constructor.getParameterTypes();
+ Class>[] exceptionTypes = constructor.getExceptionTypes();
+
+ ConstructorUtils.addConstructor(cc, getBeanType(), paramTypes, exceptionTypes,
+ initialValueBytecode, !useConstructedFlag());
}
}
if (!constructorFound) {
// the bean only has private constructors, we need to generate
// two fake constructors that call each other
- addConstructorsForBeanWithPrivateConstructors(proxyClassType);
+ addConstructorsForBeanWithPrivateConstructors(cc);
}
}
} catch (Exception e) {
@@ -572,13 +573,25 @@ protected void addConstructors(ClassFile proxyClassType, List
}
}
- protected void addFields(ClassFile proxyClassType, List initialValueBytecode) {
- // The field representing the underlying instance or special method
- // handling
- proxyClassType.addField(AccessFlag.PRIVATE, METHOD_HANDLER_FIELD_NAME, getMethodHandlerType());
+ /**
+ * Adds fields to the proxy class using Gizmo 2 API.
+ *
+ * @param cc the class creator
+ * @param initialValueBytecode deferred bytecode for field initialization
+ */
+ protected void addFields(ClassCreator cc, List initialValueBytecode) {
+ // The field representing the underlying instance or special method handling
+ cc.field(METHOD_HANDLER_FIELD_NAME, f -> {
+ f.setType(getMethodHandlerType());
+ f.private_();
+ });
+
if (useConstructedFlag()) {
// field used to indicate that super() has been called
- proxyClassType.addField(AccessFlag.PRIVATE, CONSTRUCTED_FLAG_NAME, BytecodeUtils.BOOLEAN_CLASS_DESCRIPTOR);
+ cc.field(CONSTRUCTED_FLAG_NAME, f -> {
+ f.setType(boolean.class);
+ f.private_();
+ });
}
}
@@ -586,338 +599,775 @@ protected Class extends MethodHandler> getMethodHandlerType() {
return MethodHandler.class;
}
- protected void addMethods(ClassFile proxyClassType, ClassMethod staticConstructor) {
- // Add all class methods for interception
- addMethodsFromClass(proxyClassType, staticConstructor);
+ /**
+ * Adds special serialization code using Gizmo 2 API. By default this is a nop
+ *
+ * @param cc the class creator
+ */
+ protected void addSerializationSupport(ClassCreator cc) {
+ //noop
+ }
+
+ /**
+ * Adds all methods to the proxy class using Gizmo 2 API.
+ *
+ * @param cc the class creator
+ */
+ protected void addMethods(ClassCreator cc) {
+ try {
+ // Collect all methods that need to be proxied
+ List methodsToProxy = collectMethodsToProxy();
+
+ // Add static fields for Method reflection objects
+ Map methodFieldNames = new HashMap<>();
+ for (MethodInfo methodInfo : methodsToProxy) {
+ String fieldName = "weld$$$method$$$" + methodFieldNames.size();
+ methodFieldNames.put(methodInfo, fieldName);
+
+ cc.staticField(fieldName, f -> {
+ f.setType(Method.class);
+ f.private_();
+ });
+ }
- // Add special proxy methods
- addSpecialMethods(proxyClassType, staticConstructor);
+ // Add static initializer to populate Method fields
+ if (!methodsToProxy.isEmpty()) {
+ addStaticInitializer(cc, methodsToProxy, methodFieldNames);
+ }
- // Add serialization support methods
- addSerializationSupport(proxyClassType);
+ // Add methods from class hierarchy
+ addMethodsFromClass(cc, methodsToProxy, methodFieldNames);
+
+ // Add special proxy methods
+ addSpecialMethods(cc);
+
+ // Note: Serialization support is added separately via addSerializationSupport(cc)
+ } catch (Exception e) {
+ throw new WeldException(e);
+ }
}
/**
- * Adds special serialization code. By default this is a nop
- *
- * @param proxyClassType the Javassist class for the proxy class
+ * Simple holder for method information during proxy generation.
*/
- protected void addSerializationSupport(ClassFile proxyClassType) {
- //noop
+ protected static class MethodInfo {
+ final Method method;
+ final boolean isDefault;
+
+ MethodInfo(Method method, boolean isDefault) {
+ this.method = method;
+ this.isDefault = isDefault;
+ }
}
- protected void addMethodsFromClass(ClassFile proxyClassType, ClassMethod staticConstructor) {
- try {
- // Add all methods from the class hierarchy
- Class> cls = getBeanType();
-
- // First add equals/hashCode methods if required
- generateEqualsMethod(proxyClassType);
- generateHashCodeMethod(proxyClassType);
-
- // In rare cases, the bean class may be abstract - in this case we have to add methods from all interfaces implemented by any abstract class
- // from the hierarchy
- boolean isBeanClassAbstract = Modifier.isAbstract(cls.getModifiers());
- // a final method might have a non-final declaration in abstract superclass
- // hence we need to remember which we saw and skip those in superclasses
- Set foundFinalMethods = new HashSet<>();
-
- while (cls != null) {
- addMethods(cls, proxyClassType, staticConstructor, foundFinalMethods);
- if (isBeanClassAbstract && Modifier.isAbstract(cls.getModifiers())) {
- for (Class> implementedInterface : Reflections.getInterfaceClosure(cls)) {
- if (!additionalInterfaces.contains(implementedInterface)) {
- addMethods(implementedInterface, proxyClassType, staticConstructor, foundFinalMethods);
- }
- }
+ /**
+ * Collects all methods that need to be proxied.
+ */
+ protected List collectMethodsToProxy() {
+ List methods = new ArrayList<>();
+ Class> cls = getBeanType();
+ boolean isBeanClassAbstract = Modifier.isAbstract(cls.getModifiers());
+ Set foundFinalMethods = new HashSet<>();
+ Set addedMethods = new HashSet<>();
+
+ // Add methods from the class hierarchy
+ while (cls != null) {
+ Method[] classDeclaredMethods = cls.getDeclaredMethods();
+ for (Method method : classDeclaredMethods) {
+ MethodSignature methodSignature = new MethodSignatureImpl(method);
+ if (Modifier.isFinal(method.getModifiers())) {
+ foundFinalMethods.add(methodSignature);
}
- cls = cls.getSuperclass();
- }
- for (Class> c : additionalInterfaces) {
- for (Method method : c.getMethods()) {
- if (isMethodAccepted(method, getProxySuperclass())) {
- try {
- MethodInformation methodInfo = new RuntimeMethodInformation(method);
- ClassMethod classMethod = proxyClassType.addMethod(method);
- if (Reflections.isDefault(method)) {
- addConstructedGuardToMethodBody(classMethod);
- createForwardingMethodBody(classMethod, methodInfo, staticConstructor);
- } else {
- createSpecialMethodBody(classMethod, methodInfo, staticConstructor);
+ // Skip bridge methods that have a concrete implementation in the same class
+ if (method.isBridge() && hasConcreteImplementation(method, classDeclaredMethods)) {
+ continue;
+ }
+ if (isMethodAccepted(method, getProxySuperclass())
+ && !foundFinalMethods.contains(methodSignature)
+ && !addedMethods.contains(methodSignature)) {
+ methods.add(new MethodInfo(method, false));
+ addedMethods.add(methodSignature);
+ }
+ }
+ if (isBeanClassAbstract && Modifier.isAbstract(cls.getModifiers())) {
+ for (Class> implementedInterface : Reflections.getInterfaceClosure(cls)) {
+ if (!additionalInterfaces.contains(implementedInterface)) {
+ for (Method method : implementedInterface.getMethods()) {
+ MethodSignature methodSignature = new MethodSignatureImpl(method);
+ if (isMethodAccepted(method, getProxySuperclass()) && !addedMethods.contains(methodSignature)) {
+ methods.add(new MethodInfo(method, Reflections.isDefault(method)));
+ addedMethods.add(methodSignature);
}
- BeanLogger.LOG.addingMethodToProxy(method);
- } catch (DuplicateMemberException e) {
}
}
}
}
- } catch (Exception e) {
- throw new WeldException(e);
+ cls = cls.getSuperclass();
}
- }
- private void addMethods(Class> cls, ClassFile proxyClassType, ClassMethod staticConstructor,
- Set foundFinalmethods) {
- for (Method method : cls.getDeclaredMethods()) {
- MethodSignature methodSignature = new MethodSignatureImpl(method);
- if (Modifier.isFinal(method.getModifiers())) {
- foundFinalmethods.add(methodSignature);
- }
- if (isMethodAccepted(method, getProxySuperclass()) && !foundFinalmethods.contains(methodSignature)) {
- try {
- MethodInformation methodInfo = new RuntimeMethodInformation(method);
- ClassMethod classMethod = proxyClassType.addMethod(method);
- addConstructedGuardToMethodBody(classMethod);
- createForwardingMethodBody(classMethod, methodInfo, staticConstructor);
- BeanLogger.LOG.addingMethodToProxy(method);
- } catch (DuplicateMemberException e) {
- // do nothing. This will happen if superclass methods
- // have been overridden
+ // Add methods from additional interfaces
+ for (Class> iface : additionalInterfaces) {
+ for (Method method : iface.getMethods()) {
+ MethodSignature methodSignature = new MethodSignatureImpl(method);
+ if (isMethodAccepted(method, getProxySuperclass()) && !addedMethods.contains(methodSignature)) {
+ methods.add(new MethodInfo(method, Reflections.isDefault(method)));
+ addedMethods.add(methodSignature);
}
}
}
+
+ return methods;
}
- protected boolean isMethodAccepted(Method method, Class> proxySuperclass) {
- for (ProxiedMethodFilter filter : METHOD_FILTERS) {
- if (!filter.accept(method, proxySuperclass)) {
- return false;
+ /**
+ * Adds a static initializer that populates Method reflection fields.
+ */
+ protected void addStaticInitializer(ClassCreator cc, List methodsToProxy,
+ Map methodFieldNames) {
+ cc.staticMethod("", m -> {
+ m.returning(void.class);
+
+ m.body(b -> {
+ for (MethodInfo methodInfo : methodsToProxy) {
+ String fieldName = methodFieldNames.get(methodInfo);
+ Method method = methodInfo.method;
+
+ // Get the declaring class: Class> declaringClass = .class
+ Expr classExpr = Const.of(method.getDeclaringClass());
+
+ // Get the method name: String methodName = "methodName"
+ Expr methodNameExpr = Const.of(method.getName());
+
+ // Create parameter types array: Class>[] paramTypes = new Class>[] { ... }
+ Class>[] paramTypes = method.getParameterTypes();
+
+ Expr paramTypesArray;
+ if (paramTypes.length == 0) {
+ paramTypesArray = b.newEmptyArray(Class.class, 0);
+ } else {
+ // Create array, store in LocalVar immediately, then populate
+ Expr arrayExpr = b.newEmptyArray(Class.class, paramTypes.length);
+ var paramTypesVar = b.localVar("paramTypes_" + fieldName, arrayExpr);
+
+ for (int i = 0; i < paramTypes.length; i++) {
+ // Use Const.of(Class) to properly handle arrays and primitives
+ Expr paramClassExpr = Const.of(paramTypes[i]);
+ b.set(paramTypesVar.elem(i), paramClassExpr);
+ }
+ paramTypesArray = paramTypesVar;
+ }
+
+ // Call Class.getDeclaredMethod(methodName, paramTypes)
+ MethodDesc getDeclaredMethodDesc = MethodDesc.of(
+ Class.class,
+ "getDeclaredMethod",
+ Method.class,
+ String.class,
+ Class[].class);
+
+ Expr methodExpr = b.invokeVirtual(getDeclaredMethodDesc, classExpr,
+ methodNameExpr, paramTypesArray);
+
+ // Store in static field: .fieldName = method
+ FieldDesc fieldDesc = FieldDesc.of(
+ cc.type(),
+ fieldName,
+ Method.class);
+ b.setStaticField(fieldDesc, methodExpr);
+ }
+
+ b.return_();
+ });
+ });
+ }
+
+ /**
+ * Adds methods from the class hierarchy to the proxy.
+ */
+ protected void addMethodsFromClass(ClassCreator cc, List methodsToProxy,
+ Map methodFieldNames) {
+ // Always generate equals/hashCode methods
+ // We override any bean implementations to ensure proxy identity semantics
+ generateEqualsMethod(cc);
+ generateHashCodeMethod(cc);
+
+ for (MethodInfo methodInfo : methodsToProxy) {
+ // Skip equals/hashCode - we always generate our own versions
+ if (methodInfo.method.getName().equals("equals") && methodInfo.method.getParameterCount() == 1
+ && methodInfo.method.getParameterTypes()[0] == Object.class) {
+ continue;
+ }
+ if (methodInfo.method.getName().equals("hashCode") && methodInfo.method.getParameterCount() == 0) {
+ continue;
}
+ addProxyMethod(cc, methodInfo, methodFieldNames.get(methodInfo));
}
- return true;
}
/**
- * Generate the body of the proxies hashCode method.
- *
- * If this method returns null, the method will not be added, and the
- * hashCode on the superclass will be used as per normal virtual method
- * resolution rules
+ * Generates equals() method that compares proxy classes.
+ * Two proxies are equal if they have the same class.
*/
- protected void generateHashCodeMethod(ClassFile proxyClassType) {
+ protected void generateEqualsMethod(ClassCreator cc) {
+ cc.method("equals", m -> {
+ m.public_();
+ m.returning(boolean.class);
+ var otherParam = m.parameter("other", Object.class);
+
+ m.body(b -> {
+ // if (other == null) return false;
+ Expr nullCheck = b.eq(otherParam, Const.ofNull(Object.class));
+ b.if_(nullCheck, nullBlock -> {
+ nullBlock.return_(Const.of(false));
+ });
+
+ // return this.getClass().equals(other.getClass());
+ Expr thisClass = b.invokeVirtual(
+ MethodDesc.of(Object.class, "getClass", Class.class),
+ m.this_());
+ Expr otherClass = b.invokeVirtual(
+ MethodDesc.of(Object.class, "getClass", Class.class),
+ otherParam);
+ Expr result = b.invokeVirtual(
+ MethodDesc.of(Object.class, "equals", boolean.class, Object.class),
+ thisClass, otherClass);
+ b.return_(result);
+ });
+ });
}
/**
- * Generate the body of the proxies equals method.
- *
- * If this method returns null, the method will not be added, and the
- * hashCode on the superclass will be used as per normal virtual method
- * resolution rules
- *
- * @param proxyClassType The class file
+ * Generates hashCode() method that returns the proxy class hashCode.
+ */
+ protected void generateHashCodeMethod(ClassCreator cc) {
+ cc.method("hashCode", m -> {
+ m.public_();
+ m.returning(int.class);
+
+ m.body(b -> {
+ // return this.getClass().hashCode();
+ Expr thisClass = b.invokeVirtual(
+ MethodDesc.of(Object.class, "getClass", Class.class),
+ m.this_());
+ Expr hashCode = b.invokeVirtual(
+ MethodDesc.of(Object.class, "hashCode", int.class),
+ thisClass);
+ b.return_(hashCode);
+ });
+ });
+ }
+
+ /**
+ * Adds a single proxy method that forwards to the method handler.
*/
- protected void generateEqualsMethod(ClassFile proxyClassType) {
+ protected void addProxyMethod(ClassCreator cc, MethodInfo methodInfo, String methodFieldName) {
+ Method method = methodInfo.method;
+
+ // Create method descriptor from the reflection Method
+ MethodDesc methodDesc = MethodDesc.of(method);
+
+ cc.method(methodDesc, m -> {
+ // Set method modifiers
+ int modifiers = method.getModifiers();
+ if (Modifier.isPublic(modifiers)) {
+ m.public_();
+ } else if (Modifier.isProtected(modifiers)) {
+ m.protected_();
+ }
+
+ // Set varargs flag if the method is varargs
+ if (method.isVarArgs()) {
+ m.varargs();
+ }
+
+ // Add parameters
+ ParamVar[] params = new ParamVar[method.getParameterCount()];
+ for (int i = 0; i < method.getParameterCount(); i++) {
+ params[i] = m.parameter("arg" + i, method.getParameterTypes()[i]);
+ }
+ // Add checked exceptions
+ for (Class> exceptionType : method.getExceptionTypes()) {
+ @SuppressWarnings("unchecked")
+ Class extends Throwable> throwableClass = (Class extends Throwable>) exceptionType;
+ m.throws_(throwableClass);
+ }
+
+ m.body(b -> {
+ // Add constructed guard if needed (prevents delegation before constructor completes)
+ if (useConstructedFlag()) {
+ addConstructedGuard(m, b, method, params);
+ }
+
+ // Forward to method handler: methodHandler.invoke(this, method, null, args)
+ invokeMethodHandler(m, b, method, methodFieldName, params);
+ });
+ });
+
+ BeanLogger.LOG.addingMethodToProxy(method);
}
- protected void createSpecialMethodBody(ClassMethod proxyClassType, MethodInformation method,
- ClassMethod staticConstructor) {
- createInterceptorBody(proxyClassType, method, staticConstructor);
+ /**
+ * Adds a constructed guard that prevents method delegation before constructor completes.
+ * Generates: if (!this.constructed) return super.method(args);
+ */
+ protected void addConstructedGuard(InstanceMethodCreator m,
+ BlockCreator b, Method method, ParamVar[] params) {
+
+ // Load the constructed flag: this.constructed
+ FieldDesc constructedField = FieldDesc.of(
+ m.owner(),
+ CONSTRUCTED_FLAG_NAME,
+ boolean.class);
+ Expr constructedValue = b.get(m.this_().field(constructedField));
+
+ // Create a local variable to store the value (needed for cross-scope usage)
+ var constructedVar = b.localVar("constructed", constructedValue);
+
+ // Create boolean expression: constructed == false
+ Expr condition = b.eq(constructedVar, Const.of(false));
+
+ // if (!constructed) { call super and return }
+ b.if_(condition, falseBlock -> {
+ // Inside the if block: call super.method(args) and return
+
+ // Prepare parameter expressions
+ Expr[] paramExprs = new Expr[params.length];
+ for (int i = 0; i < params.length; i++) {
+ paramExprs[i] = params[i];
+ }
+
+ // Get the method's declaring class
+ Class> declaringClass = method.getDeclaringClass();
+
+ // For interface methods (non-default), we can't use invokeSpecial
+ // Return default values instead
+ if (declaringClass.isInterface() || getBeanType().isInterface()) {
+ // If the method is from an interface, there's no super implementation we can call
+ // Just return default value
+ if (method.getReturnType() == void.class) {
+ falseBlock.return_();
+ } else if (method.getReturnType().isPrimitive()) {
+ falseBlock.return_(getDefaultPrimitiveValue(falseBlock, method.getReturnType()));
+ } else {
+ falseBlock.return_(Const.ofNull(Object.class));
+ }
+ return;
+ }
+
+ // Call super.method(args) - only valid for concrete class methods
+ MethodDesc superMethodDesc = MethodDesc.of(method);
+
+ Expr result;
+ if (paramExprs.length == 0) {
+ result = falseBlock.invokeSpecial(superMethodDesc, m.this_());
+ } else if (paramExprs.length == 1) {
+ result = falseBlock.invokeSpecial(superMethodDesc, m.this_(), paramExprs[0]);
+ } else if (paramExprs.length == 2) {
+ result = falseBlock.invokeSpecial(superMethodDesc, m.this_(), paramExprs[0], paramExprs[1]);
+ } else {
+ result = falseBlock.invokeSpecial(superMethodDesc, m.this_(), paramExprs);
+ }
+
+ if (method.getReturnType() == void.class) {
+ falseBlock.return_();
+ } else {
+ falseBlock.return_(result);
+ }
+ });
+ // If constructed == true, execution continues to method handler invocation
}
- protected void addConstructedGuardToMethodBody(final ClassMethod classMethod) {
- addConstructedGuardToMethodBody(classMethod, classMethod.getClassFile().getSuperclass());
+ /**
+ * Returns the default value for a primitive type.
+ */
+ protected Expr getDefaultPrimitiveValue(BlockCreator b,
+ Class> primitiveType) {
+ if (primitiveType == boolean.class) {
+ return Const.of(false);
+ } else if (primitiveType == byte.class) {
+ return Const.of((byte) 0);
+ } else if (primitiveType == short.class) {
+ return Const.of((short) 0);
+ } else if (primitiveType == int.class) {
+ return Const.of(0);
+ } else if (primitiveType == long.class) {
+ return Const.of(0L);
+ } else if (primitiveType == float.class) {
+ return Const.of(0.0f);
+ } else if (primitiveType == double.class) {
+ return Const.of(0.0);
+ } else if (primitiveType == char.class) {
+ return Const.of((char) 0);
+ } else {
+ throw new IllegalArgumentException("Unknown primitive type: " + primitiveType);
+ }
}
/**
- * Adds the following code to a delegating method:
- *
- *
- * if(!this.constructed) return super.thisMethod()
- *
- *
- * This means that the proxy will not start to delegate to the underlying
- * bean instance until after the constructor has finished.
+ * Invokes the method handler: methodHandler.invoke(this, staticMethodField, null, args)
*/
- protected void addConstructedGuardToMethodBody(final ClassMethod classMethod, String className) {
- if (!useConstructedFlag()) {
- return;
+ protected void invokeMethodHandler(InstanceMethodCreator m,
+ BlockCreator b, Method method, String methodFieldName,
+ ParamVar[] params) {
+
+ // 1. Load this.methodHandler
+ FieldDesc methodHandlerField = FieldDesc.of(
+ m.owner(),
+ METHOD_HANDLER_FIELD_NAME,
+ getMethodHandlerType());
+ Expr handler = b.get(m.this_().field(methodHandlerField));
+
+ // 2. Load the static Method field
+ FieldDesc methodField = FieldDesc.of(
+ m.owner(),
+ methodFieldName,
+ Method.class);
+ Expr methodObj = Expr.staticField(methodField);
+
+ // 3. Create null for the second Method parameter (not used in ProxyFactory)
+ Expr nullMethod = Const.ofNull(Method.class);
+
+ // 4. Create and populate Object[] args array
+ Class>[] paramTypes = method.getParameterTypes();
+
+ // If no parameters, create empty array and store directly
+ Expr argsArray;
+ if (paramTypes.length == 0) {
+ argsArray = b.newEmptyArray(Object.class, 0);
+ } else {
+ // For parameters, create array, store in LocalVar immediately, then populate
+ Expr arrayExpr = b.newEmptyArray(Object.class, paramTypes.length);
+ var argsVar = b.localVar("args", arrayExpr);
+
+ for (int i = 0; i < paramTypes.length; i++) {
+ Expr paramValue = params[i];
+ // Box primitive types
+ if (paramTypes[i].isPrimitive()) {
+ paramValue = boxPrimitive(b, paramValue, paramTypes[i]);
+ }
+ b.set(argsVar.elem(i), paramValue);
+ }
+ argsArray = argsVar;
+ }
+
+ // 5. Call methodHandler.invoke(this, method, null, args)
+ // MethodHandler.invoke(Object self, Method thisMethod, Method proceed, Object[] args)
+ MethodDesc invokeDesc = MethodDesc.of(
+ MethodHandler.class,
+ INVOKE_METHOD_NAME,
+ Object.class,
+ Object.class, Method.class, Method.class, Object[].class);
+
+ Expr result = b.invokeInterface(invokeDesc, handler,
+ m.this_(), methodObj, nullMethod, argsArray);
+
+ // 6. Handle return value
+ Class> returnType = method.getReturnType();
+ if (returnType == void.class) {
+ // Void method - just return
+ b.return_();
+ } else if (returnType.isPrimitive()) {
+ // Primitive return - unbox
+ Expr unboxed = unboxPrimitive(b, result, returnType);
+ b.return_(unboxed);
+ } else {
+ // Object return - cast
+ Expr casted = b.cast(result, returnType);
+ b.return_(casted);
}
- // now create the conditional
- final CodeAttribute cond = classMethod.getCodeAttribute();
- cond.aload(0);
- cond.getfield(classMethod.getClassFile().getName(), CONSTRUCTED_FLAG_NAME, BytecodeUtils.BOOLEAN_CLASS_DESCRIPTOR);
+ }
+
+ /**
+ * Boxes a primitive value into its wrapper type.
+ * Gizmo 2's box() automatically determines the wrapper type from the expression type.
+ */
+ protected Expr boxPrimitive(BlockCreator b,
+ Expr value, Class> primitiveType) {
+ return b.box(value);
+ }
- // jump if the proxy constructor has finished
- BranchEnd jumpMarker = cond.ifne();
- // generate the invokespecial call to the super class method
- // this is run when the proxy is being constructed
- cond.aload(0);
- cond.loadMethodParameters();
- cond.invokespecial(className, classMethod.getName(), classMethod.getDescriptor());
- cond.returnInstruction();
- cond.branchEnd(jumpMarker);
+ /**
+ * Unboxes a wrapper object into its primitive value.
+ * Gizmo 2's unbox() automatically determines the primitive type from the expression type.
+ */
+ protected Expr unboxPrimitive(BlockCreator b,
+ Expr value, Class> primitiveType) {
+ // First cast to the wrapper type, then unbox
+ Class> wrapperType = getWrapperType(primitiveType);
+ Expr casted = b.cast(value, wrapperType);
+ return b.unbox(casted);
}
- protected void createForwardingMethodBody(ClassMethod classMethod, MethodInformation method,
- ClassMethod staticConstructor) {
- createInterceptorBody(classMethod, method, staticConstructor);
+ /**
+ * Gets the wrapper type for a primitive type.
+ */
+ protected Class> getWrapperType(Class> primitiveType) {
+ if (primitiveType == boolean.class) {
+ return Boolean.class;
+ } else if (primitiveType == byte.class) {
+ return Byte.class;
+ } else if (primitiveType == short.class) {
+ return Short.class;
+ } else if (primitiveType == int.class) {
+ return Integer.class;
+ } else if (primitiveType == long.class) {
+ return Long.class;
+ } else if (primitiveType == float.class) {
+ return Float.class;
+ } else if (primitiveType == double.class) {
+ return Double.class;
+ } else if (primitiveType == char.class) {
+ return Character.class;
+ } else {
+ throw new IllegalArgumentException("Unknown primitive type: " + primitiveType);
+ }
}
/**
- * Creates the given method on the proxy class where the implementation
- * forwards the call directly to the method handler.
- *
- * the generated bytecode is equivalent to:
- *
- * return (RetType) methodHandler.invoke(this,param1,param2);
+ * Adds constructors for beans with only private constructors using Gizmo 2 API.
*
- * @param classMethod the class method
- * @param method any JLR method
- * @return the method byte code
+ * @param cc the class creator
*/
- protected void createInterceptorBody(ClassMethod classMethod, MethodInformation method, ClassMethod staticConstructor) {
- invokeMethodHandler(classMethod, method, true, DEFAULT_METHOD_RESOLVER, staticConstructor);
+ /**
+ * Adds two constructors to the class that call each other in order to bypass
+ * the JVM class file verifier.
+ *
+ * This would result in a stack overflow if they were actually called,
+ * however the proxy is directly created without calling the constructor
+ * (using Unsafe.allocateInstance or similar mechanisms).
+ */
+ protected void addConstructorsForBeanWithPrivateConstructors(ClassCreator cc) {
+ // Add first constructor: public (Byte b)
+ // This calls the second constructor: this(null, null)
+ cc.constructor(c -> {
+ c.public_();
+ c.parameter("b", Byte.class);
+
+ c.body(b -> {
+ // Call this(null, null) - invoke the second constructor
+ ConstructorDesc secondCtor = ConstructorDesc.of(
+ cc.type(),
+ ClassDesc.of(Byte.class.getName()),
+ ClassDesc.of(Byte.class.getName()));
+ Expr thisRef = c.this_();
+ Expr nullByte1 = Const.ofNull(Byte.class);
+ Expr nullByte2 = Const.ofNull(Byte.class);
+ b.invokeSpecial(secondCtor, thisRef, nullByte1, nullByte2);
+ b.return_();
+ });
+ });
+
+ // Add second constructor: public (Byte b1, Byte b2)
+ // This calls the first constructor: this(null)
+ cc.constructor(c -> {
+ c.public_();
+ c.parameter("b1", Byte.class);
+ c.parameter("b2", Byte.class);
+
+ c.body(b -> {
+ // Call this(null) - invoke the first constructor
+ ConstructorDesc firstCtor = ConstructorDesc.of(
+ cc.type(),
+ ClassDesc.of(Byte.class.getName()));
+ Expr thisRef = c.this_();
+ Expr nullByte = Const.ofNull(Byte.class);
+ b.invokeSpecial(firstCtor, thisRef, nullByte);
+ b.return_();
+ });
+ });
+ }
+
+ protected boolean isMethodAccepted(Method method, Class> proxySuperclass) {
+ for (ProxiedMethodFilter filter : METHOD_FILTERS) {
+ if (!filter.accept(method, proxySuperclass)) {
+ return false;
+ }
+ }
+ return true;
}
/**
- * calls methodHandler.invoke for a given method
+ * Checks if a bridge method has a concrete (non-bridge) implementation in the same class.
+ * If it does, we skip the bridge method and only proxy the concrete implementation.
*
- * @param method The method information
- * @param addReturnInstruction set to true you want to return the result of
- * the method invocation
- * @param bytecodeMethodResolver The resolver that returns the method to invoke
- */
- protected void invokeMethodHandler(ClassMethod classMethod, MethodInformation method, boolean addReturnInstruction,
- BytecodeMethodResolver bytecodeMethodResolver, ClassMethod staticConstructor) {
- // now we need to build the bytecode. The order we do this in is as
- // follows:
- // load methodHandler
- // load this
- // load the method object
- // load null
- // create a new array the same size as the number of parameters
- // push our parameter values into the array
- // invokeinterface the invoke method
- // add checkcast to cast the result to the return type, or unbox if
- // primitive
- // add an appropriate return instruction
- final CodeAttribute b = classMethod.getCodeAttribute();
- b.aload(0);
- getMethodHandlerField(classMethod.getClassFile(), b);
- b.aload(0);
- bytecodeMethodResolver.getDeclaredMethod(classMethod, method.getDeclaringClass(), method.getName(),
- method.getParameterTypes(), staticConstructor);
- b.aconstNull();
-
- b.iconst(method.getParameterTypes().length);
- b.anewarray("java.lang.Object");
-
- int localVariableCount = 1;
-
- for (int i = 0; i < method.getParameterTypes().length; ++i) {
- String typeString = method.getParameterTypes()[i];
- b.dup(); // duplicate the array reference
- b.iconst(i);
- // load the parameter value
- BytecodeUtils.addLoadInstruction(b, typeString, localVariableCount);
- // box the parameter if necessary
- Boxing.boxIfNessesary(b, typeString);
- // and store it in the array
- b.aastore();
- if (isWide(typeString)) {
- localVariableCount = localVariableCount + 2;
- } else {
- localVariableCount++;
- }
- }
- // now we have all our arguments on the stack
- // lets invoke the method
- b.invokeinterface(MethodHandler.class.getName(), INVOKE_METHOD_NAME, LJAVA_LANG_OBJECT,
- new String[] { LJAVA_LANG_OBJECT,
- LJAVA_LANG_REFLECT_METHOD, LJAVA_LANG_REFLECT_METHOD, "[" + LJAVA_LANG_OBJECT });
- if (addReturnInstruction) {
- // now we need to return the appropriate type
- if (method.getReturnType().equals(BytecodeUtils.VOID_CLASS_DESCRIPTOR)) {
- b.returnInstruction();
- } else if (isPrimitive(method.getReturnType())) {
- Boxing.unbox(b, method.getReturnType());
- b.returnInstruction();
- } else {
- b.checkcast(BytecodeUtils.getName(method.getReturnType()));
- b.returnInstruction();
+ * @param bridgeMethod the bridge method to check
+ * @param classMethods all declared methods in the class
+ * @return true if there's a concrete implementation, false otherwise
+ */
+ protected boolean hasConcreteImplementation(Method bridgeMethod, Method[] classMethods) {
+ if (!bridgeMethod.isBridge()) {
+ return false;
+ }
+
+ String bridgeName = bridgeMethod.getName();
+ Class>[] bridgeParams = bridgeMethod.getParameterTypes();
+
+ for (Method candidate : classMethods) {
+ // Skip if it's also a bridge method or has different name
+ if (candidate.isBridge() || !candidate.getName().equals(bridgeName)) {
+ continue;
+ }
+
+ // Check if parameter count matches
+ Class>[] candidateParams = candidate.getParameterTypes();
+ if (candidateParams.length != bridgeParams.length) {
+ continue;
+ }
+
+ // Check if this is a more specific version of the bridge method
+ // Bridge methods typically have Object or other generic types as parameters
+ // while concrete implementations have specific types
+ boolean isMoreSpecific = false;
+ for (int i = 0; i < bridgeParams.length; i++) {
+ if (bridgeParams[i] != candidateParams[i]) {
+ // Parameters differ - check if candidate is more specific
+ if (bridgeParams[i].isAssignableFrom(candidateParams[i])) {
+ isMoreSpecific = true;
+ } else {
+ // Parameters are incompatible, not a match
+ isMoreSpecific = false;
+ break;
+ }
+ }
+ }
+
+ if (isMoreSpecific || (bridgeParams.length == candidateParams.length &&
+ java.util.Arrays.equals(bridgeParams, candidateParams))) {
+ // Found a concrete implementation (either more specific or exact match that's not a bridge)
+ return true;
}
}
+
+ return false;
}
/**
- * Adds methods requiring special implementations rather than just
- * delegation.
+ * Adds methods requiring special implementations using Gizmo 2 API.
*
- * @param proxyClassType the Javassist class description for the proxy type
+ * @param cc the class creator
*/
- protected void addSpecialMethods(ClassFile proxyClassType, ClassMethod staticConstructor) {
+ protected void addSpecialMethods(ClassCreator cc) {
try {
- // Add special methods for interceptors
+ // Add special methods for interceptors (LifecycleMixin interface)
for (Method method : LifecycleMixin.class.getMethods()) {
BeanLogger.LOG.addingMethodToProxy(method);
- MethodInformation methodInfo = new RuntimeMethodInformation(method);
- final ClassMethod classMethod = proxyClassType.addMethod(method);
- createInterceptorBody(classMethod, methodInfo, staticConstructor);
+ // Implement lifecycle methods - they just delegate to method handler
+ generateLifecycleMixinMethod(cc, method);
}
- Method getInstanceMethod = TargetInstanceProxy.class.getMethod("weld_getTargetInstance");
- Method getInstanceClassMethod = TargetInstanceProxy.class.getMethod("weld_getTargetClass");
- MethodInformation getInstanceMethodInfo = new RuntimeMethodInformation(getInstanceMethod);
- createInterceptorBody(proxyClassType.addMethod(getInstanceMethod), getInstanceMethodInfo, staticConstructor);
-
- MethodInformation getInstanceClassMethodInfo = new RuntimeMethodInformation(getInstanceClassMethod);
- createInterceptorBody(proxyClassType.addMethod(getInstanceClassMethod), getInstanceClassMethodInfo,
- staticConstructor);
+ // Add TargetInstanceProxy methods
+ // TODO: Method getInstanceMethod = TargetInstanceProxy.class.getMethod("weld_getTargetInstance");
+ // TODO: Method getInstanceClassMethod = TargetInstanceProxy.class.getMethod("weld_getTargetClass");
+ // Add ProxyObject methods (getMethodHandler, setMethodHandler)
Method setMethodHandlerMethod = ProxyObject.class.getMethod("weld_setHandler", MethodHandler.class);
- generateSetMethodHandlerBody(proxyClassType.addMethod(setMethodHandlerMethod));
+ generateSetMethodHandlerBody(cc, setMethodHandlerMethod);
Method getMethodHandlerMethod = ProxyObject.class.getMethod("weld_getHandler");
- generateGetMethodHandlerBody(proxyClassType.addMethod(getMethodHandlerMethod));
+ generateGetMethodHandlerBody(cc, getMethodHandlerMethod);
} catch (Exception e) {
throw new WeldException(e);
}
}
- protected void generateSetMethodHandlerBody(ClassMethod method) {
- final CodeAttribute b = method.getCodeAttribute();
- b.aload(0);
- b.aload(1);
- b.checkcast(getMethodHandlerType());
- b.putfield(method.getClassFile().getName(), METHOD_HANDLER_FIELD_NAME,
- DescriptorUtils.makeDescriptor(getMethodHandlerType()));
- b.returnInstruction();
+ /**
+ * Generates a LifecycleMixin method (postConstruct/preDestroy) that delegates to the method handler.
+ */
+ protected void generateLifecycleMixinMethod(ClassCreator cc, Method method) {
+ cc.method(method.getName(), m -> {
+ m.public_();
+ m.returning(void.class);
+
+ m.body(b -> {
+ // Get the method handler field
+ FieldDesc methodHandlerField = FieldDesc.of(
+ cc.type(),
+ METHOD_HANDLER_FIELD_NAME,
+ getMethodHandlerType());
+ Expr handler = b.get(m.this_().field(methodHandlerField));
+
+ // Get the Method object for this lifecycle method
+ // LifecycleMixin.class.getMethod(methodName)
+ Expr lifecycleMixinClass = Const.of(LifecycleMixin.class);
+ Expr methodName = Const.of(method.getName());
+ Expr emptyClassArray = b.newEmptyArray(Class.class, 0);
+
+ MethodDesc getMethodDesc = MethodDesc.of(
+ Class.class, "getMethod", Method.class, String.class, Class[].class);
+ Expr methodObj = b.invokeVirtual(getMethodDesc, lifecycleMixinClass,
+ methodName, emptyClassArray);
+
+ // Create null proceed Method parameter
+ Expr nullMethod = Const.ofNull(Method.class);
+
+ // Create empty args array
+ Expr emptyArgs = b.newEmptyArray(Object.class, 0);
+
+ // Call methodHandler.invoke(this, methodObj, null, emptyArgs)
+ MethodDesc invokeDesc = MethodDesc.of(
+ MethodHandler.class,
+ INVOKE_METHOD_NAME,
+ Object.class,
+ Object.class, Method.class, Method.class, Object[].class);
+
+ b.invokeInterface(invokeDesc, handler, m.this_(), methodObj, nullMethod, emptyArgs);
+
+ // Return (void method)
+ b.return_();
+ });
+ });
}
- protected void generateGetMethodHandlerBody(ClassMethod method) {
- final CodeAttribute b = method.getCodeAttribute();
- b.aload(0);
- getMethodHandlerField(method.getClassFile(), b);
- b.returnInstruction();
+ /**
+ * Generates the setMethodHandler method using Gizmo 2 API.
+ *
+ * @param cc the class creator
+ * @param method the method to implement (weld_setHandler)
+ */
+ protected void generateSetMethodHandlerBody(ClassCreator cc, Method method) {
+ cc.method(method.getName(), m -> {
+ m.public_();
+ m.returning(void.class);
+ var handlerParam = m.parameter("handler", MethodHandler.class);
+
+ m.body(b -> {
+ // this.methodHandler = (MethodHandlerType) handler;
+ FieldDesc methodHandlerField = FieldDesc.of(
+ cc.type(),
+ METHOD_HANDLER_FIELD_NAME,
+ getMethodHandlerType());
+
+ // Cast the parameter to the specific MethodHandler type and set the field
+ var castedHandler = b.cast(handlerParam, getMethodHandlerType());
+ b.set(m.this_().field(methodHandlerField), castedHandler);
+ b.return_();
+ });
+ });
}
/**
- * Adds two constructors to the class that call each other in order to bypass
- * the JVM class file verifier.
- *
- * This would result in a stack overflow if they were actually called,
- * however the proxy is directly created without calling the constructor
+ * Generates the getMethodHandler method using Gizmo 2 API.
+ *
+ * @param cc the class creator
+ * @param method the method to implement (weld_getHandler)
*/
- private void addConstructorsForBeanWithPrivateConstructors(ClassFile proxyClassType) {
- ClassMethod ctor = proxyClassType.addMethod(AccessFlag.PUBLIC, INIT_METHOD_NAME, BytecodeUtils.VOID_CLASS_DESCRIPTOR,
- LJAVA_LANG_BYTE);
- CodeAttribute b = ctor.getCodeAttribute();
- b.aload(0);
- b.aconstNull();
- b.aconstNull();
- b.invokespecial(proxyClassType.getName(), INIT_METHOD_NAME,
- "(" + LJAVA_LANG_BYTE + LJAVA_LANG_BYTE + ")" + BytecodeUtils.VOID_CLASS_DESCRIPTOR);
- b.returnInstruction();
-
- ctor = proxyClassType.addMethod(AccessFlag.PUBLIC, INIT_METHOD_NAME, BytecodeUtils.VOID_CLASS_DESCRIPTOR,
- LJAVA_LANG_BYTE, LJAVA_LANG_BYTE);
- b = ctor.getCodeAttribute();
- b.aload(0);
- b.aconstNull();
- b.invokespecial(proxyClassType.getName(), INIT_METHOD_NAME,
- "(" + LJAVA_LANG_BYTE + ")" + BytecodeUtils.VOID_CLASS_DESCRIPTOR);
- b.returnInstruction();
+ protected void generateGetMethodHandlerBody(ClassCreator cc, Method method) {
+ cc.method(method.getName(), m -> {
+ m.public_();
+ m.returning(MethodHandler.class);
+
+ m.body(b -> {
+ // return this.methodHandler;
+ FieldDesc methodHandlerField = FieldDesc.of(
+ cc.type(),
+ METHOD_HANDLER_FIELD_NAME,
+ getMethodHandlerType());
+
+ var fieldValue = b.get(m.this_().field(methodHandlerField));
+ b.return_(fieldValue);
+ });
+ });
}
public Class> getBeanType() {
@@ -940,10 +1390,6 @@ protected Class> getProxiedBeanType() {
return proxiedBeanType;
}
- protected void getMethodHandlerField(ClassFile file, CodeAttribute b) {
- b.getfield(file.getName(), METHOD_HANDLER_FIELD_NAME, DescriptorUtils.makeDescriptor(getMethodHandlerType()));
- }
-
protected Class> getProxySuperclass() {
return getBeanType().isInterface() ? Object.class : getBeanType();
}
@@ -962,17 +1408,36 @@ private boolean useConstructedFlag() {
return !isUsingProxyInstantiator() || proxyInstantiator.isUsingConstructor();
}
+ /**
+ * Converts a Class to a ClassDesc, properly handling arrays and primitives.
+ * For regular classes: "com.example.Foo" -> ClassDesc
+ * For arrays: "[Ljava.lang.String;" -> ClassDesc
+ * For primitives: "int" -> ClassDesc
+ */
+ private static java.lang.constant.ClassDesc classDescriptorOf(Class> clazz) {
+ if (clazz.isPrimitive() || clazz.isArray()) {
+ // For primitives and arrays, use descriptor format
+ // Primitives: "I", "J", "Z", etc.
+ // Arrays: "[Ljava/lang/Object;", "[[I", etc.
+ return java.lang.constant.ClassDesc.ofDescriptor(clazz.descriptorString());
+ } else {
+ // For regular classes, use binary name
+ return java.lang.constant.ClassDesc.of(clazz.getName());
+ }
+ }
+
/**
* Delegates proxy creation via {@link ProxyServices} to the integrator or to our own implementation.
+ * Uses bytecode and className generated by Gizmo 2.
*/
- protected Class> toClass(ClassFile ct, Class> originalClass, ProxyServices proxyServices, ProtectionDomain domain) {
+ protected Class> toClass(byte[] bytecode, String className, Class> originalClass, ProxyServices proxyServices,
+ ProtectionDomain domain) {
try {
- byte[] bytecode = ct.toBytecode();
Class> result;
if (domain == null) {
- result = proxyServices.defineClass(originalClass, ct.getName(), bytecode, 0, bytecode.length);
+ result = proxyServices.defineClass(originalClass, className, bytecode, 0, bytecode.length);
} else {
- result = proxyServices.defineClass(originalClass, ct.getName(), bytecode, 0, bytecode.length, domain);
+ result = proxyServices.defineClass(originalClass, className, bytecode, 0, bytecode.length, domain);
}
return result;
} catch (RuntimeException e) {
diff --git a/impl/src/main/java/org/jboss/weld/bean/proxy/RunWithinInterceptionDecorationContextGenerator.java b/impl/src/main/java/org/jboss/weld/bean/proxy/RunWithinInterceptionDecorationContextGenerator.java
deleted file mode 100644
index 1d8d8bda8a6..00000000000
--- a/impl/src/main/java/org/jboss/weld/bean/proxy/RunWithinInterceptionDecorationContextGenerator.java
+++ /dev/null
@@ -1,177 +0,0 @@
-/*
- * JBoss, Home of Professional Open Source
- * Copyright 2014, Red Hat, Inc., and individual contributors
- * by the @authors tag. See the copyright.txt in the distribution for a
- * full listing of individual contributors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.jboss.weld.bean.proxy;
-
-import static org.jboss.classfilewriter.util.DescriptorUtils.makeDescriptor;
-import static org.jboss.classfilewriter.util.DescriptorUtils.methodDescriptor;
-import static org.jboss.weld.util.bytecode.BytecodeUtils.DOUBLE_CLASS_DESCRIPTOR;
-import static org.jboss.weld.util.bytecode.BytecodeUtils.LONG_CLASS_DESCRIPTOR;
-import static org.jboss.weld.util.bytecode.BytecodeUtils.VOID_CLASS_DESCRIPTOR;
-
-import org.jboss.classfilewriter.ClassMethod;
-import org.jboss.classfilewriter.code.BranchEnd;
-import org.jboss.classfilewriter.code.CodeAttribute;
-import org.jboss.classfilewriter.code.ExceptionHandler;
-import org.jboss.weld.bean.proxy.InterceptionDecorationContext.Stack;
-
-/**
- * Generates bytecode that wraps {@link #doWork(CodeAttribute, ClassMethod)} within
- * {@link InterceptionDecorationContext#startInterceptorContextIfNotEmpty()}
- * and {@link InterceptionDecorationContext#endInterceptorContext()}
- *
- * @author Stuart Douglas
- * @author Jozef Hartinger
- *
- */
-abstract class RunWithinInterceptionDecorationContextGenerator {
-
- static final String INTERCEPTION_DECORATION_CONTEXT_CLASS_NAME = InterceptionDecorationContext.class.getName();
- static final String START_INTERCEPTOR_CONTEXT_IF_NOT_EMPTY_METHOD_NAME = "startIfNotEmpty";
- static final String START_INTERCEPTOR_CONTEXT_IF_NOT_ON_TOP_METHOD_NAME = "startIfNotOnTop";
- static final String END_INTERCEPTOR_CONTEXT_METHOD_NAME = "end";
- private static final String STACK_DESCRIPTOR = makeDescriptor(Stack.class);
- private static final String EMPTY_PARENTHESES = "()";
- private static final String RETURNS_STACK_DESCRIPTOR = EMPTY_PARENTHESES + STACK_DESCRIPTOR;
- static final String START_INTERCEPTOR_CONTEXT_IF_NOT_ON_TOP_METHOD_SIGNATURE = methodDescriptor(
- new String[] { makeDescriptor(CombinedInterceptorAndDecoratorStackMethodHandler.class) }, STACK_DESCRIPTOR);
-
- private final ClassMethod classMethod;
- private final CodeAttribute b;
- private final ProxyFactory> factory;
-
- RunWithinInterceptionDecorationContextGenerator(ClassMethod classMethod, ProxyFactory> factory) {
- this.classMethod = classMethod;
- this.b = classMethod.getCodeAttribute();
- this.factory = factory;
- }
-
- abstract void doWork(CodeAttribute b, ClassMethod method);
-
- abstract void doReturn(CodeAttribute b, ClassMethod method);
-
- void startIfNotEmpty(CodeAttribute b, ClassMethod method) {
- b.invokestatic(INTERCEPTION_DECORATION_CONTEXT_CLASS_NAME, START_INTERCEPTOR_CONTEXT_IF_NOT_EMPTY_METHOD_NAME,
- RETURNS_STACK_DESCRIPTOR);
- // store the outcome so that we know later whether to end the context or not
- storeToLocalVariable(0);
- }
-
- void startIfNotOnTop(CodeAttribute b, ClassMethod method) {
- b.aload(0);
- factory.getMethodHandlerField(method.getClassFile(), b);
- b.dup();
-
- // if handler != null (may happen inside constructor calls)
- final BranchEnd handlerNull = b.ifnull();
- b.invokestatic(INTERCEPTION_DECORATION_CONTEXT_CLASS_NAME, START_INTERCEPTOR_CONTEXT_IF_NOT_ON_TOP_METHOD_NAME,
- START_INTERCEPTOR_CONTEXT_IF_NOT_ON_TOP_METHOD_SIGNATURE);
- final BranchEnd endOfIfStatement = b.gotoInstruction();
- b.branchEnd(handlerNull);
- // else started = false
- // keeping null handler on top of stack
- b.branchEnd(endOfIfStatement);
-
- storeToLocalVariable(0);
- }
-
- void withinCatchBlock(CodeAttribute b, ClassMethod method) {
- final ExceptionHandler start = b.exceptionBlockStart(Throwable.class.getName());
-
- doWork(b, method);
-
- // end the interceptor context, everything was fine
- endIfStarted(b, method);
-
- // jump over the catch block
- BranchEnd gotoEnd = b.gotoInstruction();
-
- // create catch block
- b.exceptionBlockEnd(start);
- b.exceptionHandlerStart(start);
-
- // end the interceptor context if there was an exception
- endIfStarted(b, method);
- b.athrow();
-
- // update the correct address to jump over the catch block
- b.branchEnd(gotoEnd);
-
- doReturn(b, method);
- }
-
- /**
- * Ends interception context if it was previously stated. This is indicated by a local variable with index 0.
- */
- void endIfStarted(CodeAttribute b, ClassMethod method) {
- b.aload(getLocalVariableIndex(0));
- b.dup();
- final BranchEnd ifnotnull = b.ifnull();
- b.checkcast(Stack.class);
- b.invokevirtual(Stack.class.getName(), END_INTERCEPTOR_CONTEXT_METHOD_NAME, EMPTY_PARENTHESES + VOID_CLASS_DESCRIPTOR);
- BranchEnd ifnull = b.gotoInstruction();
- b.branchEnd(ifnotnull);
- b.pop(); // remove null Stack
- b.branchEnd(ifnull);
- }
-
- /**
- * Generates bytecode that invokes {@link InterceptionDecorationContext#startIfNotEmpty()} and stores the result in a local
- * variable. Then, the bytecode
- * generated by {@link #doWork(CodeAttribute, ClassMethod)} is added. Lastly, bytecode that conditionally calls
- * {@link InterceptionDecorationContext} based
- * on the value of the local variable is added. This is done within a catch block so that the context is ended no matter if
- * the bytecode generated by
- * {@link #doWork(CodeAttribute, ClassMethod)} yields an exception or not.
- */
- void runStartIfNotEmpty() {
- startIfNotEmpty(b, classMethod);
- withinCatchBlock(b, classMethod);
- }
-
- /**
- * Generates bytecode that loads the "methodHandler" field, invokes
- * {@link InterceptionDecorationContext#startIfNotOnTop(CombinedInterceptorAndDecoratorStackMethodHandler)} and stores the
- * result in a local variable. Then,
- * the bytecode generated by {@link #doWork(CodeAttribute, ClassMethod)} is added. Lastly, bytecode that conditionally calls
- * {@link InterceptionDecorationContext} based on the value of the local variable is added. This is done within a catch
- * block so that the context is ended
- * no matter if the bytecode generated by {@link #doWork(CodeAttribute, ClassMethod)} yields an exception or not.
- */
- void runStartIfNotOnTop() {
- startIfNotOnTop(b, classMethod);
- withinCatchBlock(b, classMethod);
- }
-
- void storeToLocalVariable(int i) {
- b.astore(getLocalVariableIndex(0));
- }
-
- /**
- * Gets the index of a local variable (the first index after method parameters). Indexes start with 0.
- */
- private int getLocalVariableIndex(int i) {
- int index = classMethod.isStatic() ? 0 : 1;
- for (String type : classMethod.getParameters()) {
- if (type.equals(DOUBLE_CLASS_DESCRIPTOR) || type.equals(LONG_CLASS_DESCRIPTOR)) {
- index += 2;
- } else {
- index++;
- }
- }
- return index + i;
- }
-}
diff --git a/impl/src/main/java/org/jboss/weld/bean/proxy/util/WeldDefaultProxyServices.java b/impl/src/main/java/org/jboss/weld/bean/proxy/util/WeldDefaultProxyServices.java
index 9737f16b6ed..1e17fc0a85f 100644
--- a/impl/src/main/java/org/jboss/weld/bean/proxy/util/WeldDefaultProxyServices.java
+++ b/impl/src/main/java/org/jboss/weld/bean/proxy/util/WeldDefaultProxyServices.java
@@ -17,14 +17,6 @@
package org.jboss.weld.bean.proxy.util;
-import static org.jboss.classfilewriter.AccessFlag.FINAL;
-import static org.jboss.classfilewriter.AccessFlag.PUBLIC;
-import static org.jboss.classfilewriter.AccessFlag.STATIC;
-import static org.jboss.classfilewriter.AccessFlag.SUPER;
-import static org.jboss.classfilewriter.AccessFlag.SYNTHETIC;
-import static org.jboss.classfilewriter.util.DescriptorUtils.makeDescriptor;
-import static org.jboss.weld.util.bytecode.BytecodeUtils.VOID_CLASS_DESCRIPTOR;
-
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
@@ -32,15 +24,17 @@
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
-import org.jboss.classfilewriter.AccessFlag;
-import org.jboss.classfilewriter.ClassFile;
-import org.jboss.classfilewriter.code.CodeAttribute;
-import org.jboss.weld.bean.proxy.DummyClassFactoryImpl;
+import org.jboss.weld.bean.proxy.ByteArrayClassOutput;
import org.jboss.weld.bean.proxy.ProxyFactory;
+import org.jboss.weld.exceptions.IllegalStateException;
import org.jboss.weld.logging.BeanLogger;
import org.jboss.weld.proxy.WeldClientProxy;
import org.jboss.weld.serialization.spi.ProxyServices;
+import io.quarkus.gizmo2.Gizmo;
+import io.quarkus.gizmo2.ParamVar;
+import io.quarkus.gizmo2.desc.MethodDesc;
+
/**
* This class is a default implementation of ProxyServices that will only be loaded if no other implementation is detected.
* It supports class defining and attempts to use {@link MethodHandles.Lookup} if possible making it JDK 11+ friendly.
@@ -210,33 +204,53 @@ private MethodHandle generateEnsureReadsMethod(Class> lookupBaseClass)
private byte[] generateReadsHelperBytes(Class> lookupBaseClass) {
// Put helper in the same package as the base class so the private Lookup can define it
+ String className = (lookupBaseClass.getPackage() == null ? "" : lookupBaseClass.getPackage().getName() + ".")
+ + "Weld$ReadsHelper";
- // Create class header
- ClassFile ensureReadsClassFile = new ClassFile(
- (lookupBaseClass.getPackage() == null ? "" : lookupBaseClass.getPackage().getName() + ".") + "Weld$ReadsHelper",
- AccessFlag.of(PUBLIC, FINAL, SUPER, SYNTHETIC),
- Object.class.getName(),
- ProxyFactory.class.getClassLoader(),
- DummyClassFactoryImpl.INSTANCE);
-
- // Create method header for "public static void ensureReads(Module other)"
- CodeAttribute code = ensureReadsClassFile.addMethod(
- AccessFlag.of(PUBLIC, STATIC),
- "ensureReads",
- VOID_CLASS_DESCRIPTOR,
- makeDescriptor(Module.class))
- .getCodeAttribute();
-
- // Create code for the method body "MethodHandles.lookup().lookupClass().getModule().addReads(other);"
- code.invokestatic("java/lang/invoke/MethodHandles", "lookup", "()Ljava/lang/invoke/MethodHandles$Lookup;");
- code.invokevirtual("java/lang/invoke/MethodHandles$Lookup", "lookupClass", "()Ljava/lang/Class;");
- code.invokevirtual("java/lang/Class", "getModule", "()Ljava/lang/Module;");
- code.aload(0); // parameter: Module other
- code.invokevirtual("java/lang/Module", "addReads", "(Ljava/lang/Module;)Ljava/lang/Module;");
- code.pop();
- code.returnInstruction();
-
- return ensureReadsClassFile.toBytecode();
+ // Capture bytecode using ByteArrayClassOutput
+ ByteArrayClassOutput classOutput = new ByteArrayClassOutput();
+ Gizmo.create(classOutput).class_(className, cc -> {
+ cc.public_();
+ cc.final_();
+ cc.synthetic();
+
+ // Create method "public static void ensureReads(Module other)"
+ cc.staticMethod("ensureReads", m -> {
+ m.public_();
+ m.returning(void.class);
+ ParamVar moduleParam = m.parameter("other", Module.class);
+
+ m.body(b -> {
+ // MethodHandles.lookup().lookupClass().getModule().addReads(other);
+
+ // MethodHandles.lookup()
+ MethodDesc lookup = MethodDesc.of(MethodHandles.class, "lookup", MethodHandles.Lookup.class);
+ var lookupResult = b.invokeStatic(lookup);
+
+ // .lookupClass()
+ MethodDesc lookupClass = MethodDesc.of(MethodHandles.Lookup.class, "lookupClass", Class.class);
+ var classResult = b.invokeVirtual(lookupClass, lookupResult);
+
+ // .getModule()
+ MethodDesc getModule = MethodDesc.of(Class.class, "getModule", Module.class);
+ var moduleResult = b.invokeVirtual(getModule, classResult);
+
+ // .addReads(other)
+ MethodDesc addReads = MethodDesc.of(Module.class, "addReads", Module.class, Module.class);
+ b.invokeVirtual(addReads, moduleResult, moduleParam);
+
+ // Return
+ b.return_();
+ });
+ });
+ });
+
+ byte[] bytes = classOutput.getBytes();
+ if (bytes == null) {
+ throw new IllegalStateException(
+ "Failed to generate helper class for modular access. Lookup base class: " + lookupBaseClass);
+ }
+ return bytes;
}
/**
diff --git a/impl/src/main/java/org/jboss/weld/util/bytecode/AccessFlags.java b/impl/src/main/java/org/jboss/weld/util/bytecode/AccessFlags.java
new file mode 100644
index 00000000000..0a5f141f803
--- /dev/null
+++ b/impl/src/main/java/org/jboss/weld/util/bytecode/AccessFlags.java
@@ -0,0 +1,49 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2025, Red Hat, Inc., and individual contributors
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jboss.weld.util.bytecode;
+
+/**
+ * JVM access flag constants.
+ * Provides lightweight replacements for org.jboss.classfilewriter.AccessFlag functionality.
+ * Values match the JVM specification (JVMS §4.1, §4.5, §4.6).
+ *
+ * @author Claude (Gizmo 2 migration)
+ */
+public final class AccessFlags {
+
+ private AccessFlags() {
+ }
+
+ public static final int PUBLIC = 0x0001;
+ public static final int PRIVATE = 0x0002;
+ public static final int PROTECTED = 0x0004;
+ public static final int STATIC = 0x0008;
+ public static final int FINAL = 0x0010;
+ public static final int SUPER = 0x0020; // Class access flag
+ public static final int SYNCHRONIZED = 0x0020; // Method access flag (same value as SUPER)
+ public static final int VOLATILE = 0x0040; // Field access flag
+ public static final int BRIDGE = 0x0040; // Method access flag (same value as VOLATILE)
+ public static final int TRANSIENT = 0x0080; // Field access flag
+ public static final int VARARGS = 0x0080; // Method access flag (same value as TRANSIENT)
+ public static final int NATIVE = 0x0100;
+ public static final int INTERFACE = 0x0200;
+ public static final int ABSTRACT = 0x0400;
+ public static final int STRICT = 0x0800;
+ public static final int SYNTHETIC = 0x1000;
+ public static final int ANNOTATION = 0x2000;
+ public static final int ENUM = 0x4000;
+}
diff --git a/impl/src/main/java/org/jboss/weld/util/bytecode/BytecodeUtils.java b/impl/src/main/java/org/jboss/weld/util/bytecode/BytecodeUtils.java
index fde0a975631..d7362639335 100644
--- a/impl/src/main/java/org/jboss/weld/util/bytecode/BytecodeUtils.java
+++ b/impl/src/main/java/org/jboss/weld/util/bytecode/BytecodeUtils.java
@@ -16,7 +16,12 @@
*/
package org.jboss.weld.util.bytecode;
-import org.jboss.classfilewriter.code.CodeAttribute;
+import java.lang.constant.ClassDesc;
+
+import io.quarkus.gizmo2.Const;
+import io.quarkus.gizmo2.Expr;
+import io.quarkus.gizmo2.creator.BlockCreator;
+import io.quarkus.gizmo2.desc.FieldDesc;
/**
* utility class for common bytecode operations
@@ -45,79 +50,71 @@ private BytecodeUtils() {
}
/**
- * Adds the correct load instruction based on the type descriptor
+ * NOTE: This method is not applicable in Gizmo 2 where parameters are declared
+ * with parameter() and accessed directly as ParamVar instances.
*
- * @param code the bytecode to add the instruction to
- * @param type the type of the variable
- * @param variable the variable number
+ * @deprecated In Gizmo 2, use parameter() to declare parameters and reference them directly
*/
- public static void addLoadInstruction(CodeAttribute code, String type, int variable) {
- char tp = type.charAt(0);
- if (tp != 'L' && tp != '[') {
- // we have a primitive type
- switch (tp) {
- case 'J':
- code.lload(variable);
- break;
- case 'D':
- code.dload(variable);
- break;
- case 'F':
- code.fload(variable);
- break;
- default:
- code.iload(variable);
- }
- } else {
- code.aload(variable);
- }
+ @Deprecated
+ public static void addLoadInstruction(Object code, String type, int variable) {
+ throw new UnsupportedOperationException(
+ "addLoadInstruction is not applicable in Gizmo 2 - use parameter() to declare parameters");
}
/**
- * Pushes a class type onto the stack from the string representation This can
- * also handle primitives
+ * Loads a Class object from the string representation.
+ * This can handle both object types and primitives.
*
- * @param b the bytecode
- * @param classType the type descriptor for the class or primitive to push.
+ * @param b the block creator
+ * @param classType the type descriptor for the class or primitive to load.
* This will accept both the java.lang.Object form and the
* Ljava/lang/Object; form
+ * @return the Expr representing the Class object
*/
- public static void pushClassType(CodeAttribute b, String classType) {
+ public static Expr pushClassType(BlockCreator b, String classType) {
if (classType.length() != 1) {
+ // Object or array type
if (classType.startsWith("L") && classType.endsWith(";")) {
classType = classType.substring(1, classType.length() - 1);
}
- b.loadClass(classType);
+ // Convert internal name (slashes) to binary name (dots)
+ String className = classType.replace('/', '.');
+ // In Gizmo 2, we use Const.of() for class literals
+ return Const.of(ClassDesc.of(className));
} else {
+ // Primitive type - load the TYPE field from the wrapper class
char type = classType.charAt(0);
+ Class> wrapperClass;
switch (type) {
case 'I':
- b.getstatic(Integer.class.getName(), TYPE, LJAVA_LANG_CLASS);
+ wrapperClass = Integer.class;
break;
case 'J':
- b.getstatic(Long.class.getName(), TYPE, LJAVA_LANG_CLASS);
+ wrapperClass = Long.class;
break;
case 'S':
- b.getstatic(Short.class.getName(), TYPE, LJAVA_LANG_CLASS);
+ wrapperClass = Short.class;
break;
case 'F':
- b.getstatic(Float.class.getName(), TYPE, LJAVA_LANG_CLASS);
+ wrapperClass = Float.class;
break;
case 'D':
- b.getstatic(Double.class.getName(), TYPE, LJAVA_LANG_CLASS);
+ wrapperClass = Double.class;
break;
case 'B':
- b.getstatic(Byte.class.getName(), TYPE, LJAVA_LANG_CLASS);
+ wrapperClass = Byte.class;
break;
case 'C':
- b.getstatic(Character.class.getName(), TYPE, LJAVA_LANG_CLASS);
+ wrapperClass = Character.class;
break;
case 'Z':
- b.getstatic(Boolean.class.getName(), TYPE, LJAVA_LANG_CLASS);
+ wrapperClass = Boolean.class;
break;
default:
throw new RuntimeException("Cannot handle primitive type: " + type);
}
+ FieldDesc typeField = FieldDesc.of(wrapperClass, TYPE);
+ return b.getStaticField(typeField);
}
}
diff --git a/impl/src/main/java/org/jboss/weld/util/bytecode/ConstructorUtils.java b/impl/src/main/java/org/jboss/weld/util/bytecode/ConstructorUtils.java
index 6dc11ab3f7b..e839fe7ceb4 100644
--- a/impl/src/main/java/org/jboss/weld/util/bytecode/ConstructorUtils.java
+++ b/impl/src/main/java/org/jboss/weld/util/bytecode/ConstructorUtils.java
@@ -16,19 +16,19 @@
*/
package org.jboss.weld.util.bytecode;
-import static org.jboss.classfilewriter.util.DescriptorUtils.methodDescriptor;
-
import java.util.List;
-import org.jboss.classfilewriter.AccessFlag;
-import org.jboss.classfilewriter.ClassFile;
-import org.jboss.classfilewriter.ClassMethod;
-import org.jboss.classfilewriter.DuplicateMemberException;
-import org.jboss.classfilewriter.code.CodeAttribute;
import org.jboss.weld.bean.proxy.ProxyFactory;
+import io.quarkus.gizmo2.Const;
+import io.quarkus.gizmo2.Expr;
+import io.quarkus.gizmo2.ParamVar;
+import io.quarkus.gizmo2.creator.ClassCreator;
+import io.quarkus.gizmo2.desc.ConstructorDesc;
+import io.quarkus.gizmo2.desc.FieldDesc;
+
/**
- * Utility class for working with constructors in the low level javassist API
+ * Utility class for working with constructors in Gizmo bytecode generation.
*
* @author Stuart Douglas
*/
@@ -38,55 +38,88 @@ private ConstructorUtils() {
}
/**
- * adds a constructor that calls super()
+ * Adds a default constructor that calls super().
+ *
+ * @param classCreator the class creator
+ * @param superClass the superclass
+ * @param initialValueBytecode deferred bytecode for field initialization
+ * @param useUnsafeInstantiators whether unsafe instantiators are used
*/
- public static void addDefaultConstructor(ClassFile file, List initialValueBytecode,
+ public static void addDefaultConstructor(ClassCreator classCreator, Class> superClass,
+ List initialValueBytecode,
final boolean useUnsafeInstantiators) {
- addConstructor(BytecodeUtils.VOID_CLASS_DESCRIPTOR, new String[0], new String[0], file, initialValueBytecode,
+ addConstructor(classCreator, superClass, new Class>[0], new Class>[0], initialValueBytecode,
useUnsafeInstantiators);
}
/**
* Adds a constructor that delegates to a super constructor with the same
- * descriptor. The bytecode in initialValueBytecode will be executed at the
+ * parameter types. The bytecode in initialValueBytecode will be executed at the
* start of the constructor and can be used to initialize fields to a default
- * value. As the object is not properly constructed at this point this
- * bytecode may not reference this (i.e. the variable at location 0)
+ * value.
*
- * @param returnType the constructor descriptor
- * @param exceptions any exceptions that are thrown
- * @param file the classfile to add the constructor to
- * @param initialValueBytecode bytecode that can be used to set initial values
+ * @param classCreator the class creator
+ * @param superClass the superclass
+ * @param parameterTypes the constructor parameter types
+ * @param exceptionTypes any exceptions that are thrown
+ * @param initialValueBytecode deferred bytecode for field initialization
+ * @param useUnsafeInstantiators whether unsafe instantiators are used
*/
- public static void addConstructor(String returnType, String[] params, String[] exceptions, ClassFile file,
+ public static void addConstructor(ClassCreator classCreator, Class> superClass, Class>[] parameterTypes,
+ Class>[] exceptionTypes,
List initialValueBytecode, final boolean useUnsafeInstantiators) {
- try {
- final String initMethodName = "";
- final ClassMethod ctor = file.addMethod(AccessFlag.PUBLIC, initMethodName, returnType, params);
- ctor.addCheckedExceptions(exceptions);
- final CodeAttribute b = ctor.getCodeAttribute();
- for (final DeferredBytecode iv : initialValueBytecode) {
- iv.apply(b);
+ classCreator.constructor(ctor -> {
+ ctor.public_();
+
+ // Add parameters
+ ParamVar[] params = new ParamVar[parameterTypes.length];
+ for (int i = 0; i < parameterTypes.length; i++) {
+ params[i] = ctor.parameter("param" + i, parameterTypes[i]);
}
- // we need to generate a constructor with a single invokespecial call
- // to the super constructor
- // to do this we need to push all the arguments on the stack first
- // local variables is the number of parameters +1 for this
- // if some of the parameters are wide this may go up.
- b.aload(0);
- b.loadMethodParameters();
- // now we have the parameters on the stack
- b.invokespecial(file.getSuperclass(), initMethodName, methodDescriptor(params, returnType));
- if (!useUnsafeInstantiators) {
- // now set constructed to true
- b.aload(0);
- b.iconst(1);
- b.putfield(file.getName(), ProxyFactory.CONSTRUCTED_FLAG_NAME, BytecodeUtils.BOOLEAN_CLASS_DESCRIPTOR);
+
+ // Add checked exceptions
+ for (Class> exceptionType : exceptionTypes) {
+ @SuppressWarnings("unchecked")
+ Class extends Throwable> throwableClass = (Class extends Throwable>) exceptionType;
+ ctor.throws_(throwableClass);
}
- b.returnInstruction();
- } catch (DuplicateMemberException e) {
- throw new RuntimeException(e);
- }
+
+ ctor.body(b -> {
+ // Apply deferred bytecode (field initialization)
+ for (final DeferredBytecode iv : initialValueBytecode) {
+ iv.apply(b);
+ }
+
+ // Call super constructor
+ ConstructorDesc superConstructor = ConstructorDesc.of(superClass, parameterTypes);
+ Expr[] paramExprs = new Expr[params.length];
+ for (int i = 0; i < params.length; i++) {
+ paramExprs[i] = params[i];
+ }
+
+ if (paramExprs.length == 0) {
+ b.invokeSpecial(superConstructor, ctor.this_());
+ } else if (paramExprs.length == 1) {
+ b.invokeSpecial(superConstructor, ctor.this_(), paramExprs[0]);
+ } else if (paramExprs.length == 2) {
+ b.invokeSpecial(superConstructor, ctor.this_(), paramExprs[0], paramExprs[1]);
+ } else {
+ // For 3+ parameters, use the varargs version
+ b.invokeSpecial(superConstructor, ctor.this_(), paramExprs);
+ }
+
+ // If not using unsafe instantiators, set the constructed flag to true
+ if (!useUnsafeInstantiators) {
+ FieldDesc constructedField = FieldDesc.of(
+ classCreator.type(),
+ ProxyFactory.CONSTRUCTED_FLAG_NAME,
+ boolean.class);
+ b.set(ctor.this_().field(constructedField), Const.of(true));
+ }
+
+ b.return_();
+ });
+ });
}
}
diff --git a/impl/src/main/java/org/jboss/weld/util/bytecode/DeferredBytecode.java b/impl/src/main/java/org/jboss/weld/util/bytecode/DeferredBytecode.java
index a0a072a93ad..3db84bfc3ae 100644
--- a/impl/src/main/java/org/jboss/weld/util/bytecode/DeferredBytecode.java
+++ b/impl/src/main/java/org/jboss/weld/util/bytecode/DeferredBytecode.java
@@ -1,12 +1,22 @@
package org.jboss.weld.util.bytecode;
-import org.jboss.classfilewriter.code.CodeAttribute;
+import io.quarkus.gizmo2.creator.BlockCreator;
/**
+ * Deferred bytecode that can be applied to a BlockCreator.
+ * This is used to generate bytecode that needs to be inserted at specific points,
+ * such as field initialization in constructors.
+ *
* @author Stuart Douglas
*/
+@FunctionalInterface
public interface DeferredBytecode {
- void apply(CodeAttribute codeAttribute);
+ /**
+ * Applies this deferred bytecode to the given block creator.
+ *
+ * @param blockCreator the block creator to apply bytecode to
+ */
+ void apply(BlockCreator blockCreator);
}
diff --git a/impl/src/main/java/org/jboss/weld/util/bytecode/DescriptorUtil.java b/impl/src/main/java/org/jboss/weld/util/bytecode/DescriptorUtil.java
new file mode 100644
index 00000000000..725da8462a6
--- /dev/null
+++ b/impl/src/main/java/org/jboss/weld/util/bytecode/DescriptorUtil.java
@@ -0,0 +1,133 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2025, Red Hat, Inc., and individual contributors
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jboss.weld.util.bytecode;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+
+/**
+ * Utility class for generating JVM type descriptors.
+ * Provides lightweight replacements for org.jboss.classfilewriter.util.DescriptorUtils functionality.
+ *
+ * @author Claude (Gizmo 2 migration)
+ */
+public class DescriptorUtil {
+
+ private DescriptorUtil() {
+ }
+
+ /**
+ * Creates a JVM type descriptor for a class.
+ *
+ * @param clazz the class
+ * @return the JVM type descriptor (e.g., "Ljava/lang/String;" or "I" for primitives)
+ */
+ public static String makeDescriptor(Class> clazz) {
+ if (clazz == void.class) {
+ return "V";
+ }
+ if (clazz == boolean.class) {
+ return "Z";
+ }
+ if (clazz == byte.class) {
+ return "B";
+ }
+ if (clazz == char.class) {
+ return "C";
+ }
+ if (clazz == short.class) {
+ return "S";
+ }
+ if (clazz == int.class) {
+ return "I";
+ }
+ if (clazz == long.class) {
+ return "J";
+ }
+ if (clazz == float.class) {
+ return "F";
+ }
+ if (clazz == double.class) {
+ return "D";
+ }
+ if (clazz.isArray()) {
+ return clazz.getName().replace('.', '/');
+ }
+ return "L" + clazz.getName().replace('.', '/') + ";";
+ }
+
+ /**
+ * Creates parameter descriptors for an array of parameter types.
+ *
+ * @param parameterTypes the parameter types
+ * @return array of type descriptors
+ */
+ public static String[] parameterDescriptors(Class>[] parameterTypes) {
+ String[] result = new String[parameterTypes.length];
+ for (int i = 0; i < parameterTypes.length; i++) {
+ result[i] = makeDescriptor(parameterTypes[i]);
+ }
+ return result;
+ }
+
+ /**
+ * Creates parameter descriptors for a method.
+ *
+ * @param method the method
+ * @return array of type descriptors
+ */
+ public static String[] parameterDescriptors(Method method) {
+ return parameterDescriptors(method.getParameterTypes());
+ }
+
+ /**
+ * Creates a method descriptor string.
+ *
+ * @param parameterDescriptors the parameter type descriptors
+ * @param returnTypeDescriptor the return type descriptor
+ * @return the method descriptor (e.g., "(Ljava/lang/String;I)V")
+ */
+ public static String methodDescriptor(String[] parameterDescriptors, String returnTypeDescriptor) {
+ StringBuilder sb = new StringBuilder("(");
+ for (String param : parameterDescriptors) {
+ sb.append(param);
+ }
+ sb.append(")");
+ sb.append(returnTypeDescriptor);
+ return sb.toString();
+ }
+
+ /**
+ * Creates a method descriptor for a method.
+ *
+ * @param method the method
+ * @return the method descriptor
+ */
+ public static String methodDescriptor(Method method) {
+ return methodDescriptor(parameterDescriptors(method), makeDescriptor(method.getReturnType()));
+ }
+
+ /**
+ * Creates a constructor descriptor for a constructor.
+ *
+ * @param constructor the constructor
+ * @return the constructor descriptor
+ */
+ public static String makeDescriptor(Constructor> constructor) {
+ return methodDescriptor(parameterDescriptors(constructor.getParameterTypes()), "V");
+ }
+}
diff --git a/impl/src/main/java/org/jboss/weld/util/bytecode/RuntimeMethodInformation.java b/impl/src/main/java/org/jboss/weld/util/bytecode/RuntimeMethodInformation.java
index 608ecb3000d..1234e65e9a6 100644
--- a/impl/src/main/java/org/jboss/weld/util/bytecode/RuntimeMethodInformation.java
+++ b/impl/src/main/java/org/jboss/weld/util/bytecode/RuntimeMethodInformation.java
@@ -16,13 +16,10 @@
*/
package org.jboss.weld.util.bytecode;
-import static org.jboss.classfilewriter.util.DescriptorUtils.methodDescriptor;
+import static org.jboss.weld.util.bytecode.DescriptorUtil.methodDescriptor;
import java.lang.reflect.Method;
-import org.jboss.classfilewriter.AccessFlag;
-import org.jboss.classfilewriter.util.DescriptorUtils;
-
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
/**
@@ -41,18 +38,18 @@ public class RuntimeMethodInformation implements MethodInformation {
public RuntimeMethodInformation(Method method) {
this.method = method;
- this.parameterTypes = DescriptorUtils.parameterDescriptors(method);
- this.returnType = DescriptorUtils.makeDescriptor(method.getReturnType());
+ this.parameterTypes = DescriptorUtil.parameterDescriptors(method);
+ this.returnType = DescriptorUtil.makeDescriptor(method.getReturnType());
this.descriptor = methodDescriptor(parameterTypes, returnType);
this.declaringClass = method.getDeclaringClass().getName();
int modifier;
if (method.isBridge()) {
- modifier = AccessFlag.PUBLIC | AccessFlag.BRIDGE | AccessFlag.SYNTHETIC;
+ modifier = AccessFlags.PUBLIC | AccessFlags.BRIDGE | AccessFlags.SYNTHETIC;
} else {
- modifier = AccessFlag.PUBLIC;
+ modifier = AccessFlags.PUBLIC;
}
if (method.isVarArgs()) {
- modifier |= AccessFlag.VARARGS;
+ modifier |= AccessFlags.VARARGS;
}
this.modifier = modifier;
}
diff --git a/impl/src/main/java/org/jboss/weld/util/bytecode/StaticMethodInformation.java b/impl/src/main/java/org/jboss/weld/util/bytecode/StaticMethodInformation.java
index 7a9b52a3c40..1341ed7d982 100644
--- a/impl/src/main/java/org/jboss/weld/util/bytecode/StaticMethodInformation.java
+++ b/impl/src/main/java/org/jboss/weld/util/bytecode/StaticMethodInformation.java
@@ -18,9 +18,6 @@
import java.lang.reflect.Method;
-import org.jboss.classfilewriter.AccessFlag;
-import org.jboss.classfilewriter.util.DescriptorUtils;
-
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
public class StaticMethodInformation implements MethodInformation {
@@ -32,14 +29,14 @@ public class StaticMethodInformation implements MethodInformation {
private final int modifiers;
public StaticMethodInformation(String name, Class>[] parameterTypes, Class> returnType, String declaringClass) {
- this(name, parameterTypes, returnType, declaringClass, AccessFlag.PUBLIC);
+ this(name, parameterTypes, returnType, declaringClass, AccessFlags.PUBLIC);
}
public StaticMethodInformation(String name, Class>[] parameterTypes, Class> returnType, String declaringClass,
int modifiers) {
this.name = name;
- this.parameterTypes = DescriptorUtils.parameterDescriptors(parameterTypes);
- this.returnType = DescriptorUtils.makeDescriptor(returnType);
+ this.parameterTypes = DescriptorUtil.parameterDescriptors(parameterTypes);
+ this.returnType = DescriptorUtil.makeDescriptor(returnType);
this.declaringClass = declaringClass;
StringBuilder builder = new StringBuilder("(");
for (String p : this.parameterTypes) {
@@ -64,7 +61,7 @@ public StaticMethodInformation(String name, String[] parameterTypes, String retu
builder.append(')');
builder.append(returnType);
descriptor = builder.toString();
- this.modifiers = AccessFlag.PUBLIC;
+ this.modifiers = AccessFlags.PUBLIC;
}
public String getDeclaringClass() {
diff --git a/impl/src/main/java/org/jboss/weld/util/reflection/Formats.java b/impl/src/main/java/org/jboss/weld/util/reflection/Formats.java
index fb07976cd7e..de7630b9a7d 100644
--- a/impl/src/main/java/org/jboss/weld/util/reflection/Formats.java
+++ b/impl/src/main/java/org/jboss/weld/util/reflection/Formats.java
@@ -45,10 +45,10 @@
import org.apache.bcel.classfile.ClassParser;
import org.apache.bcel.classfile.JavaClass;
import org.apache.bcel.classfile.LineNumberTable;
-import org.jboss.classfilewriter.util.DescriptorUtils;
import org.jboss.weld.ejb.spi.BusinessInterfaceDescriptor;
import org.jboss.weld.resources.ClassLoaderResourceLoader;
import org.jboss.weld.resources.WeldClassLoaderResourceLoader;
+import org.jboss.weld.util.bytecode.DescriptorUtil;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
@@ -167,10 +167,10 @@ public static int getLineNumber(Member member) {
String signature;
String name;
if (member instanceof Method) {
- signature = DescriptorUtils.methodDescriptor((Method) member);
+ signature = DescriptorUtil.methodDescriptor((Method) member);
name = member.getName();
} else if (member instanceof Constructor) {
- signature = DescriptorUtils.makeDescriptor((Constructor>) member);
+ signature = DescriptorUtil.makeDescriptor((Constructor>) member);
name = INIT_METHOD_NAME;
} else {
return 0;
diff --git a/modules/ejb/src/main/java/org/jboss/weld/module/ejb/EnterpriseProxyFactory.java b/modules/ejb/src/main/java/org/jboss/weld/module/ejb/EnterpriseProxyFactory.java
index 3c962e16eaa..7933e58901f 100644
--- a/modules/ejb/src/main/java/org/jboss/weld/module/ejb/EnterpriseProxyFactory.java
+++ b/modules/ejb/src/main/java/org/jboss/weld/module/ejb/EnterpriseProxyFactory.java
@@ -19,17 +19,25 @@
import java.lang.reflect.Method;
import java.lang.reflect.Type;
+import java.util.Set;
-import org.jboss.classfilewriter.ClassFile;
-import org.jboss.classfilewriter.ClassMethod;
+import jakarta.enterprise.context.spi.CreationalContext;
+
+import org.jboss.weld.bean.SessionBean;
import org.jboss.weld.bean.proxy.CommonProxiedMethodFilters;
+import org.jboss.weld.bean.proxy.Marker;
+import org.jboss.weld.bean.proxy.MethodHandler;
import org.jboss.weld.bean.proxy.ProxyFactory;
import org.jboss.weld.exceptions.WeldException;
import org.jboss.weld.logging.BeanLogger;
-import org.jboss.weld.util.bytecode.MethodInformation;
-import org.jboss.weld.util.bytecode.RuntimeMethodInformation;
import org.jboss.weld.util.collections.ImmutableSet;
+import io.quarkus.gizmo2.Const;
+import io.quarkus.gizmo2.Expr;
+import io.quarkus.gizmo2.creator.ClassCreator;
+import io.quarkus.gizmo2.desc.FieldDesc;
+import io.quarkus.gizmo2.desc.MethodDesc;
+
/**
* This factory produces client proxies specific for enterprise beans, in
* particular session beans. It adds the interface
@@ -52,17 +60,84 @@ class EnterpriseProxyFactory extends ProxyFactory {
}
@Override
- protected void addSpecialMethods(ClassFile proxyClassType, ClassMethod staticConstructor) {
- super.addSpecialMethods(proxyClassType, staticConstructor);
+ protected void addAdditionalInterfaces(Set> interfaces) {
+ super.addAdditionalInterfaces(interfaces);
+ // Add the EnterpriseBeanInstance interface
+ interfaces.add(EnterpriseBeanInstance.class);
+ }
- // Add methods for the EnterpriseBeanInstance interface
+ @Override
+ protected void addSpecialMethods(ClassCreator cc) {
+ super.addSpecialMethods(cc);
+ // Add the destroy() method from EnterpriseBeanInstance interface
+ generateDestroyMethod(cc);
+ }
+
+ /**
+ * Generates the destroy() method from EnterpriseBeanInstance interface.
+ * This method delegates to the method handler which will handle the actual destruction.
+ */
+ private void generateDestroyMethod(ClassCreator cc) {
try {
- proxyClassType.addInterface(EnterpriseBeanInstance.class.getName());
- for (Method method : EnterpriseBeanInstance.class.getMethods()) {
- BeanLogger.LOG.addingMethodToEnterpriseProxy(method);
- MethodInformation methodInfo = new RuntimeMethodInformation(method);
- createInterceptorBody(proxyClassType.addMethod(method), methodInfo, staticConstructor);
- }
+ Method destroyMethod = EnterpriseBeanInstance.class.getMethod("destroy",
+ Marker.class, SessionBean.class, CreationalContext.class);
+ BeanLogger.LOG.addingMethodToEnterpriseProxy(destroyMethod);
+
+ cc.method(destroyMethod.getName(), m -> {
+ m.public_();
+ m.returning(void.class);
+ var markerParam = m.parameter("marker", Marker.class);
+ var sessionBeanParam = m.parameter("enterpriseBean", SessionBean.class);
+ var contextParam = m.parameter("creationalContext", CreationalContext.class);
+
+ m.body(b -> {
+ // Get the method handler field
+ FieldDesc methodHandlerField = FieldDesc.of(
+ cc.type(),
+ METHOD_HANDLER_FIELD_NAME,
+ getMethodHandlerType());
+ Expr handler = b.get(m.this_().field(methodHandlerField));
+
+ // Get the Method object for destroy()
+ // EnterpriseBeanInstance.class.getMethod("destroy", ...)
+ Expr enterpriseBeanInstanceClass = Const.of(EnterpriseBeanInstance.class);
+ Expr methodName = Const.of("destroy");
+
+ // Create parameter types array
+ Expr paramTypesArray = b.newEmptyArray(Class.class, 3);
+ var paramTypesVar = b.localVar("paramTypes", paramTypesArray);
+ b.set(paramTypesVar.elem(0), Const.of(Marker.class));
+ b.set(paramTypesVar.elem(1), Const.of(SessionBean.class));
+ b.set(paramTypesVar.elem(2), Const.of(CreationalContext.class));
+
+ MethodDesc getMethodDesc = MethodDesc.of(
+ Class.class, "getMethod", Method.class, String.class, Class[].class);
+ Expr methodObj = b.invokeVirtual(getMethodDesc, enterpriseBeanInstanceClass,
+ methodName, paramTypesVar);
+
+ // Create null proceed Method parameter
+ Expr nullMethod = Const.ofNull(Method.class);
+
+ // Create args array with the three parameters
+ Expr argsArray = b.newEmptyArray(Object.class, 3);
+ var argsVar = b.localVar("args", argsArray);
+ b.set(argsVar.elem(0), markerParam);
+ b.set(argsVar.elem(1), sessionBeanParam);
+ b.set(argsVar.elem(2), contextParam);
+
+ // Call methodHandler.invoke(this, methodObj, null, args)
+ MethodDesc invokeDesc = MethodDesc.of(
+ MethodHandler.class,
+ INVOKE_METHOD_NAME,
+ Object.class,
+ Object.class, Method.class, Method.class, Object[].class);
+
+ b.invokeInterface(invokeDesc, handler, m.this_(), methodObj, nullMethod, argsVar);
+
+ // Return (void method)
+ b.return_();
+ });
+ });
} catch (Exception e) {
throw new WeldException(e);
}
diff --git a/pom.xml b/pom.xml
index eac518844c7..6ddbcd2df53 100644
--- a/pom.xml
+++ b/pom.xml
@@ -68,7 +68,7 @@
- 1.3.1.Final
+ 2.1.1
5.0.4
2.70.0
0.8.14
@@ -202,10 +202,11 @@
${atinject.tck.version}