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 <clinit> (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 <clinit>
    at org.example.MyBean$$SpringCGLIB$$0.CGLIB$STATICHOOK1(<generated>)
    at org.example.MyBean$$SpringCGLIB$$0.<clinit>(<generated>)

This commit therefore introduces inline comments pointing out why we
stay with Java 1.8 byte code level with CGLIB.

See gh-34602
This commit is contained in:
Sam Brannen 2025-03-18 12:40:28 +01:00
parent a0763d13c5
commit d6a7aefd1d
15 changed files with 36 additions and 4 deletions

View File

@ -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 <clinit>.
ce.begin_class(Constants.V1_8,
Constants.ACC_PUBLIC,
getClassName(),

View File

@ -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 <clinit>.
ce.begin_class(Constants.V1_8,
Constants.ACC_PUBLIC,
getClassName(),

View File

@ -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 <clinit>.
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);

View File

@ -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 <clinit>.
begin_class(Constants.V1_8, Constants.ACC_PUBLIC, className, BULK_BEAN, null, Constants.SOURCE_FILE);
EmitUtils.null_constructor(this);
generateGet(target, getters);

View File

@ -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 <clinit>.
ce.begin_class(Constants.V1_8,
Constants.ACC_PUBLIC,
getClassName(),

View File

@ -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 <clinit>.
ce.begin_class(Constants.V1_8,
Constants.ACC_PUBLIC,
getClassName(),

View File

@ -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 <clinit>.
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 <clinit>.
e.begin_class(Constants.V1_8,
Constants.ACC_PUBLIC,
getClassName(),

View File

@ -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 <clinit>.
ce.begin_class(Constants.V1_8,
Constants.ACC_PUBLIC | Constants.ACC_INTERFACE | Constants.ACC_ABSTRACT,
getClassName(),

View File

@ -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 <clinit>.
begin_class(Constants.V1_8,
Constants.ACC_PUBLIC,
className,

View File

@ -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 <clinit>.
ce.begin_class(Constants.V1_8,
Constants.ACC_PUBLIC,
getClassName(),

View File

@ -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 <clinit>.
begin_class(Constants.V1_8, Constants.ACC_PUBLIC, className, FAST_CLASS, null, Constants.SOURCE_FILE);
// constructor

View File

@ -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 <clinit>.
ce.begin_class(Constants.V1_8,
Constants.ACC_PUBLIC,
getClassName(),

View File

@ -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 <clinit>.
ce.begin_class(Constants.V1_8,
Constants.ACC_PUBLIC,
getClassName(),

View File

@ -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 <clinit>.
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);

View File

@ -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 <clinit>.
ce.begin_class(Constants.V1_8,
Constants.ACC_PUBLIC,
getClassName(),