From d6a7aefd1de99d54ae421e1cb5a1b04a082c4036 Mon Sep 17 00:00:00 2001 From: Sam Brannen <104798+sbrannen@users.noreply.github.com> Date: Tue, 18 Mar 2025 12:40:28 +0100 Subject: [PATCH] Add comments regarding Java 1.8 byte code level for CGLIB Unfortunately, it is not possible to raise the byte code level beyond Java 1.8 for classes generated using CGLIB due to the fact that CGLIB generates STATICHOOK methods which set static final fields outside the initializer method (i.e., a static initialization block). Attempting to raise the level to Java 17 (or even Java 9) results in exceptions like the following. Caused by: java.lang.IllegalAccessError: Update to static final field org.example.MyBean$$SpringCGLIB$$0.CGLIB$THREAD_CALLBACKS attempted from a different method (CGLIB$STATICHOOK1) than the initializer method at org.example.MyBean$$SpringCGLIB$$0.CGLIB$STATICHOOK1() at org.example.MyBean$$SpringCGLIB$$0.() This commit therefore introduces inline comments pointing out why we stay with Java 1.8 byte code level with CGLIB. See gh-34602 --- .../org/springframework/cglib/beans/BeanCopier.java | 2 ++ .../org/springframework/cglib/beans/BeanGenerator.java | 2 ++ .../springframework/cglib/beans/BeanMapEmitter.java | 2 ++ .../springframework/cglib/beans/BulkBeanEmitter.java | 2 ++ .../org/springframework/cglib/beans/ImmutableBean.java | 2 ++ .../org/springframework/cglib/core/KeyFactory.java | 2 ++ .../java/org/springframework/cglib/proxy/Enhancer.java | 4 ++++ .../springframework/cglib/proxy/InterfaceMaker.java | 10 ++++++---- .../org/springframework/cglib/proxy/MixinEmitter.java | 2 ++ .../cglib/reflect/ConstructorDelegate.java | 2 ++ .../cglib/reflect/FastClassEmitter.java | 2 ++ .../springframework/cglib/reflect/MethodDelegate.java | 2 ++ .../cglib/reflect/MulticastDelegate.java | 2 ++ .../cglib/util/ParallelSorterEmitter.java | 2 ++ .../org/springframework/cglib/util/StringSwitcher.java | 2 ++ 15 files changed, 36 insertions(+), 4 deletions(-) diff --git a/spring-core/src/main/java/org/springframework/cglib/beans/BeanCopier.java b/spring-core/src/main/java/org/springframework/cglib/beans/BeanCopier.java index 52b94fd5d81..e3691feee77 100644 --- a/spring-core/src/main/java/org/springframework/cglib/beans/BeanCopier.java +++ b/spring-core/src/main/java/org/springframework/cglib/beans/BeanCopier.java @@ -116,6 +116,8 @@ abstract public class BeanCopier Type sourceType = Type.getType(source); Type targetType = Type.getType(target); ClassEmitter ce = new ClassEmitter(v); + // Byte code level cannot be higher than 1.8 due to STATICHOOK methods + // which set static final fields outside the initializer method . ce.begin_class(Constants.V1_8, Constants.ACC_PUBLIC, getClassName(), diff --git a/spring-core/src/main/java/org/springframework/cglib/beans/BeanGenerator.java b/spring-core/src/main/java/org/springframework/cglib/beans/BeanGenerator.java index 9969d511e99..78a85c4cdb8 100644 --- a/spring-core/src/main/java/org/springframework/cglib/beans/BeanGenerator.java +++ b/spring-core/src/main/java/org/springframework/cglib/beans/BeanGenerator.java @@ -117,6 +117,8 @@ public class BeanGenerator extends AbstractClassGenerator types[i] = (Type)props.get(names[i]); } ClassEmitter ce = new ClassEmitter(v); + // Byte code level cannot be higher than 1.8 due to STATICHOOK methods + // which set static final fields outside the initializer method . ce.begin_class(Constants.V1_8, Constants.ACC_PUBLIC, getClassName(), diff --git a/spring-core/src/main/java/org/springframework/cglib/beans/BeanMapEmitter.java b/spring-core/src/main/java/org/springframework/cglib/beans/BeanMapEmitter.java index b1e3596f8ff..ef9dcf0bc29 100644 --- a/spring-core/src/main/java/org/springframework/cglib/beans/BeanMapEmitter.java +++ b/spring-core/src/main/java/org/springframework/cglib/beans/BeanMapEmitter.java @@ -57,6 +57,8 @@ class BeanMapEmitter extends ClassEmitter { public BeanMapEmitter(ClassVisitor v, String className, Class type, int require) { super(v); + // Byte code level cannot be higher than 1.8 due to STATICHOOK methods + // which set static final fields outside the initializer method . begin_class(Constants.V1_8, Constants.ACC_PUBLIC, className, BEAN_MAP, null, Constants.SOURCE_FILE); EmitUtils.null_constructor(this); EmitUtils.factory_method(this, NEW_INSTANCE); diff --git a/spring-core/src/main/java/org/springframework/cglib/beans/BulkBeanEmitter.java b/spring-core/src/main/java/org/springframework/cglib/beans/BulkBeanEmitter.java index 8a6198f4c51..8a8dc5f1700 100644 --- a/spring-core/src/main/java/org/springframework/cglib/beans/BulkBeanEmitter.java +++ b/spring-core/src/main/java/org/springframework/cglib/beans/BulkBeanEmitter.java @@ -56,6 +56,8 @@ class BulkBeanEmitter extends ClassEmitter { Method[] setters = new Method[setterNames.length]; validate(target, getterNames, setterNames, types, getters, setters); + // Byte code level cannot be higher than 1.8 due to STATICHOOK methods + // which set static final fields outside the initializer method . begin_class(Constants.V1_8, Constants.ACC_PUBLIC, className, BULK_BEAN, null, Constants.SOURCE_FILE); EmitUtils.null_constructor(this); generateGet(target, getters); diff --git a/spring-core/src/main/java/org/springframework/cglib/beans/ImmutableBean.java b/spring-core/src/main/java/org/springframework/cglib/beans/ImmutableBean.java index 9c023394bd3..4d64c5c71a1 100644 --- a/spring-core/src/main/java/org/springframework/cglib/beans/ImmutableBean.java +++ b/spring-core/src/main/java/org/springframework/cglib/beans/ImmutableBean.java @@ -89,6 +89,8 @@ public class ImmutableBean public void generateClass(ClassVisitor v) { Type targetType = Type.getType(target); ClassEmitter ce = new ClassEmitter(v); + // Byte code level cannot be higher than 1.8 due to STATICHOOK methods + // which set static final fields outside the initializer method . ce.begin_class(Constants.V1_8, Constants.ACC_PUBLIC, getClassName(), diff --git a/spring-core/src/main/java/org/springframework/cglib/core/KeyFactory.java b/spring-core/src/main/java/org/springframework/cglib/core/KeyFactory.java index 9bee25e1c7b..f4133915941 100644 --- a/spring-core/src/main/java/org/springframework/cglib/core/KeyFactory.java +++ b/spring-core/src/main/java/org/springframework/cglib/core/KeyFactory.java @@ -261,6 +261,8 @@ abstract public class KeyFactory { } Type[] parameterTypes = TypeUtils.getTypes(newInstance.getParameterTypes()); + // Byte code level cannot be higher than 1.8 due to STATICHOOK methods + // which set static final fields outside the initializer method . ce.begin_class(Constants.V1_8, Constants.ACC_PUBLIC, getClassName(), diff --git a/spring-core/src/main/java/org/springframework/cglib/proxy/Enhancer.java b/spring-core/src/main/java/org/springframework/cglib/proxy/Enhancer.java index fc655f244ad..3acd2fd69fe 100644 --- a/spring-core/src/main/java/org/springframework/cglib/proxy/Enhancer.java +++ b/spring-core/src/main/java/org/springframework/cglib/proxy/Enhancer.java @@ -678,6 +678,8 @@ public class Enhancer extends AbstractClassGenerator { ClassEmitter e = new ClassEmitter(v); if (currentData == null) { + // Byte code level cannot be higher than 1.8 due to STATICHOOK methods + // which set static final fields outside the initializer method . e.begin_class(Constants.V1_8, Constants.ACC_PUBLIC, getClassName(), @@ -688,6 +690,8 @@ public class Enhancer extends AbstractClassGenerator { Constants.SOURCE_FILE); } else { + // Byte code level cannot be higher than 1.8 due to STATICHOOK methods + // which set static final fields outside the initializer method . e.begin_class(Constants.V1_8, Constants.ACC_PUBLIC, getClassName(), diff --git a/spring-core/src/main/java/org/springframework/cglib/proxy/InterfaceMaker.java b/spring-core/src/main/java/org/springframework/cglib/proxy/InterfaceMaker.java index 5e61136a2a7..842c57fbe7a 100644 --- a/spring-core/src/main/java/org/springframework/cglib/proxy/InterfaceMaker.java +++ b/spring-core/src/main/java/org/springframework/cglib/proxy/InterfaceMaker.java @@ -94,23 +94,25 @@ public class InterfaceMaker extends AbstractClassGenerator } @Override - protected ClassLoader getDefaultClassLoader() { + protected ClassLoader getDefaultClassLoader() { return null; } @Override - protected Object firstInstance(Class type) { + protected Object firstInstance(Class type) { return type; } @Override - protected Object nextInstance(Object instance) { + protected Object nextInstance(Object instance) { throw new IllegalStateException("InterfaceMaker does not cache"); } @Override - public void generateClass(ClassVisitor v) throws Exception { + public void generateClass(ClassVisitor v) throws Exception { ClassEmitter ce = new ClassEmitter(v); + // Byte code level cannot be higher than 1.8 due to STATICHOOK methods + // which set static final fields outside the initializer method . ce.begin_class(Constants.V1_8, Constants.ACC_PUBLIC | Constants.ACC_INTERFACE | Constants.ACC_ABSTRACT, getClassName(), diff --git a/spring-core/src/main/java/org/springframework/cglib/proxy/MixinEmitter.java b/spring-core/src/main/java/org/springframework/cglib/proxy/MixinEmitter.java index ab2460fe9f8..d34efd4e991 100644 --- a/spring-core/src/main/java/org/springframework/cglib/proxy/MixinEmitter.java +++ b/spring-core/src/main/java/org/springframework/cglib/proxy/MixinEmitter.java @@ -48,6 +48,8 @@ class MixinEmitter extends ClassEmitter { public MixinEmitter(ClassVisitor v, String className, Class[] classes, int[] route) { super(v); + // Byte code level cannot be higher than 1.8 due to STATICHOOK methods + // which set static final fields outside the initializer method . begin_class(Constants.V1_8, Constants.ACC_PUBLIC, className, diff --git a/spring-core/src/main/java/org/springframework/cglib/reflect/ConstructorDelegate.java b/spring-core/src/main/java/org/springframework/cglib/reflect/ConstructorDelegate.java index 5bb1c4bbff7..7ba597713c2 100644 --- a/spring-core/src/main/java/org/springframework/cglib/reflect/ConstructorDelegate.java +++ b/spring-core/src/main/java/org/springframework/cglib/reflect/ConstructorDelegate.java @@ -105,6 +105,8 @@ abstract public class ConstructorDelegate { } ClassEmitter ce = new ClassEmitter(v); + // Byte code level cannot be higher than 1.8 due to STATICHOOK methods + // which set static final fields outside the initializer method . ce.begin_class(Constants.V1_8, Constants.ACC_PUBLIC, getClassName(), diff --git a/spring-core/src/main/java/org/springframework/cglib/reflect/FastClassEmitter.java b/spring-core/src/main/java/org/springframework/cglib/reflect/FastClassEmitter.java index 4e5ba38b5c1..2375f378309 100644 --- a/spring-core/src/main/java/org/springframework/cglib/reflect/FastClassEmitter.java +++ b/spring-core/src/main/java/org/springframework/cglib/reflect/FastClassEmitter.java @@ -75,6 +75,8 @@ class FastClassEmitter extends ClassEmitter { super(v); Type base = Type.getType(type); + // Byte code level cannot be higher than 1.8 due to STATICHOOK methods + // which set static final fields outside the initializer method . begin_class(Constants.V1_8, Constants.ACC_PUBLIC, className, FAST_CLASS, null, Constants.SOURCE_FILE); // constructor diff --git a/spring-core/src/main/java/org/springframework/cglib/reflect/MethodDelegate.java b/spring-core/src/main/java/org/springframework/cglib/reflect/MethodDelegate.java index 3a7d1085b9c..9cebc32d5c5 100644 --- a/spring-core/src/main/java/org/springframework/cglib/reflect/MethodDelegate.java +++ b/spring-core/src/main/java/org/springframework/cglib/reflect/MethodDelegate.java @@ -236,6 +236,8 @@ abstract public class MethodDelegate { ClassEmitter ce = new ClassEmitter(v); CodeEmitter e; + // Byte code level cannot be higher than 1.8 due to STATICHOOK methods + // which set static final fields outside the initializer method . ce.begin_class(Constants.V1_8, Constants.ACC_PUBLIC, getClassName(), diff --git a/spring-core/src/main/java/org/springframework/cglib/reflect/MulticastDelegate.java b/spring-core/src/main/java/org/springframework/cglib/reflect/MulticastDelegate.java index b23a8b17207..d0fc23ac639 100644 --- a/spring-core/src/main/java/org/springframework/cglib/reflect/MulticastDelegate.java +++ b/spring-core/src/main/java/org/springframework/cglib/reflect/MulticastDelegate.java @@ -117,6 +117,8 @@ abstract public class MulticastDelegate implements Cloneable { final MethodInfo method = ReflectUtils.getMethodInfo(ReflectUtils.findInterfaceMethod(iface)); ClassEmitter ce = new ClassEmitter(cv); + // Byte code level cannot be higher than 1.8 due to STATICHOOK methods + // which set static final fields outside the initializer method . ce.begin_class(Constants.V1_8, Constants.ACC_PUBLIC, getClassName(), diff --git a/spring-core/src/main/java/org/springframework/cglib/util/ParallelSorterEmitter.java b/spring-core/src/main/java/org/springframework/cglib/util/ParallelSorterEmitter.java index 2222ff6ae91..22bb96595f6 100644 --- a/spring-core/src/main/java/org/springframework/cglib/util/ParallelSorterEmitter.java +++ b/spring-core/src/main/java/org/springframework/cglib/util/ParallelSorterEmitter.java @@ -38,6 +38,8 @@ class ParallelSorterEmitter extends ClassEmitter { public ParallelSorterEmitter(ClassVisitor v, String className, Object[] arrays) { super(v); + // Byte code level cannot be higher than 1.8 due to STATICHOOK methods + // which set static final fields outside the initializer method . begin_class(Constants.V1_8, Constants.ACC_PUBLIC, className, PARALLEL_SORTER, null, Constants.SOURCE_FILE); EmitUtils.null_constructor(this); EmitUtils.factory_method(this, NEW_INSTANCE); diff --git a/spring-core/src/main/java/org/springframework/cglib/util/StringSwitcher.java b/spring-core/src/main/java/org/springframework/cglib/util/StringSwitcher.java index 01f4d188e4f..8cab210c20f 100644 --- a/spring-core/src/main/java/org/springframework/cglib/util/StringSwitcher.java +++ b/spring-core/src/main/java/org/springframework/cglib/util/StringSwitcher.java @@ -133,6 +133,8 @@ abstract public class StringSwitcher { @Override public void generateClass(ClassVisitor v) throws Exception { ClassEmitter ce = new ClassEmitter(v); + // Byte code level cannot be higher than 1.8 due to STATICHOOK methods + // which set static final fields outside the initializer method . ce.begin_class(Constants.V1_8, Constants.ACC_PUBLIC, getClassName(),