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 52b94fd5d8..e3691feee7 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 9969d511e9..78a85c4cdb 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 b1e3596f8f..ef9dcf0bc2 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 8a6198f4c5..8a8dc5f170 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 9c023394bd..4d64c5c71a 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 9bee25e1c7..f413391594 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 fc655f244a..3acd2fd69f 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 5e61136a2a..842c57fbe7 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 ab2460fe9f..d34efd4e99 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 5bb1c4bbff..7ba597713c 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 4e5ba38b5c..2375f37830 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 3a7d1085b9..9cebc32d5c 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 b23a8b1720..d0fc23ac63 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 2222ff6ae9..22bb96595f 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 01f4d188e4..8cab210c20 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(),