1212import java .lang .reflect .Member ;
1313import java .lang .reflect .Method ;
1414import java .lang .reflect .Modifier ;
15+ import java .nio .charset .StandardCharsets ;
1516import java .util .ArrayList ;
1617import java .util .Arrays ;
1718import java .util .Collections ;
6869import net .bytebuddy .jar .asm .Type ;
6970import net .bytebuddy .matcher .ElementMatcher ;
7071import net .bytebuddy .matcher .ElementMatchers ;
71- import net .bytebuddy .pool .TypePool ;
7272import org .checkerframework .checker .nullness .qual .Nullable ;
7373
7474public class BytecodeProviderImpl implements BytecodeProvider {
@@ -149,11 +149,9 @@ public ReflectionOptimizer getReflectionOptimizer(
149149 fastClass = null ;
150150 }
151151 else {
152- fastClass = byteBuddyState .load ( clazz , byteBuddy -> byteBuddy
153- .with ( new NamingStrategy .SuffixingRandom (
154- INSTANTIATOR_PROXY_NAMING_SUFFIX ,
155- new NamingStrategy .SuffixingRandom .BaseNameResolver .ForFixedValue ( clazz .getName () )
156- ) )
152+ final String className = clazz .getName () + "$" + INSTANTIATOR_PROXY_NAMING_SUFFIX ;
153+ fastClass = byteBuddyState .load ( clazz , className , (byteBuddy , namingStrategy ) -> byteBuddy
154+ .with ( namingStrategy )
157155 .subclass ( ReflectionOptimizer .InstantiationOptimizer .class )
158156 .method ( newInstanceMethodName )
159157 .intercept ( MethodCall .construct ( constructor ) )
@@ -213,11 +211,9 @@ public ReflectionOptimizer getReflectionOptimizer(
213211 fastClass = null ;
214212 }
215213 else {
216- fastClass = byteBuddyState .load ( clazz , byteBuddy -> byteBuddy
217- .with ( new NamingStrategy .SuffixingRandom (
218- INSTANTIATOR_PROXY_NAMING_SUFFIX ,
219- new NamingStrategy .SuffixingRandom .BaseNameResolver .ForFixedValue ( clazz .getName () )
220- ) )
214+ final String className = clazz .getName () + "$" + INSTANTIATOR_PROXY_NAMING_SUFFIX ;
215+ fastClass = byteBuddyState .load ( clazz , className , (byteBuddy , namingStrategy ) -> byteBuddy
216+ .with ( namingStrategy )
221217 .subclass ( ReflectionOptimizer .InstantiationOptimizer .class )
222218 .method ( newInstanceMethodName )
223219 .intercept ( MethodCall .construct ( constructor ) )
@@ -238,23 +234,41 @@ public ReflectionOptimizer getReflectionOptimizer(
238234 return null ;
239235 }
240236
241- Class <?> superClass = determineAccessOptimizerSuperClass ( clazz , getters , setters );
242-
243237 final String [] propertyNames = propertyAccessMap .keySet ().toArray ( new String [0 ] );
244- final Class <?> bulkAccessor = byteBuddyState .load ( clazz , byteBuddy -> byteBuddy
245- .with ( new NamingStrategy .SuffixingRandom (
246- OPTIMIZER_PROXY_NAMING_SUFFIX ,
247- new NamingStrategy .SuffixingRandom .BaseNameResolver .ForFixedValue ( clazz .getName () )
248- ) )
249- .subclass ( superClass )
250- .implement ( ReflectionOptimizer .AccessOptimizer .class )
251- .method ( getPropertyValuesMethodName )
252- .intercept ( new Implementation .Simple ( new GetPropertyValues ( clazz , propertyNames , getters ) ) )
253- .method ( setPropertyValuesMethodName )
254- .intercept ( new Implementation .Simple ( new SetPropertyValues ( clazz , propertyNames , setters ) ) )
255- .method ( getPropertyNamesMethodName )
256- .intercept ( MethodCall .call ( new CloningPropertyCall ( propertyNames ) ) )
257- );
238+ final Class <?> superClass = determineAccessOptimizerSuperClass ( clazz , propertyNames , getters , setters );
239+
240+ final String className = clazz .getName () + "$" + OPTIMIZER_PROXY_NAMING_SUFFIX + encodeName ( propertyNames , getters , setters );
241+ final Class <?> bulkAccessor ;
242+ if ( className .getBytes ( StandardCharsets .UTF_8 ).length >= 0x10000 ) {
243+ // The JVM has a 64K byte limit on class name length, so fallback to random name if encoding exceeds that
244+ bulkAccessor = byteBuddyState .load ( clazz , byteBuddy -> byteBuddy
245+ .with ( new NamingStrategy .SuffixingRandom (
246+ OPTIMIZER_PROXY_NAMING_SUFFIX ,
247+ new NamingStrategy .SuffixingRandom .BaseNameResolver .ForFixedValue ( clazz .getName () )
248+ ) )
249+ .subclass ( superClass )
250+ .implement ( ReflectionOptimizer .AccessOptimizer .class )
251+ .method ( getPropertyValuesMethodName )
252+ .intercept ( new Implementation .Simple ( new GetPropertyValues ( clazz , propertyNames , getters ) ) )
253+ .method ( setPropertyValuesMethodName )
254+ .intercept ( new Implementation .Simple ( new SetPropertyValues ( clazz , propertyNames , setters ) ) )
255+ .method ( getPropertyNamesMethodName )
256+ .intercept ( MethodCall .call ( new CloningPropertyCall ( propertyNames ) ) )
257+ );
258+ }
259+ else {
260+ bulkAccessor = byteBuddyState .load ( clazz , className , (byteBuddy , namingStrategy ) -> byteBuddy
261+ .with ( namingStrategy )
262+ .subclass ( superClass )
263+ .implement ( ReflectionOptimizer .AccessOptimizer .class )
264+ .method ( getPropertyValuesMethodName )
265+ .intercept ( new Implementation .Simple ( new GetPropertyValues ( clazz , propertyNames , getters ) ) )
266+ .method ( setPropertyValuesMethodName )
267+ .intercept ( new Implementation .Simple ( new SetPropertyValues ( clazz , propertyNames , setters ) ) )
268+ .method ( getPropertyNamesMethodName )
269+ .intercept ( MethodCall .call ( new CloningPropertyCall ( propertyNames ) ) )
270+ );
271+ }
258272
259273 try {
260274 return new ReflectionOptimizerImpl (
@@ -269,6 +283,7 @@ public ReflectionOptimizer getReflectionOptimizer(
269283
270284 private static class ForeignPackageClassInfo {
271285 final Class <?> clazz ;
286+ final List <String > propertyNames = new ArrayList <>();
272287 final List <Member > getters = new ArrayList <>();
273288 final List <Member > setters = new ArrayList <>();
274289
@@ -277,7 +292,7 @@ public ForeignPackageClassInfo(Class<?> clazz) {
277292 }
278293 }
279294
280- private Class <?> determineAccessOptimizerSuperClass (Class <?> clazz , Member [] getters , Member [] setters ) {
295+ private Class <?> determineAccessOptimizerSuperClass (Class <?> clazz , String [] propertyNames , Member [] getters , Member [] setters ) {
281296 if ( clazz .isInterface () ) {
282297 return Object .class ;
283298 }
@@ -291,11 +306,17 @@ private Class<?> determineAccessOptimizerSuperClass(Class<?> clazz, Member[] get
291306 for ( int i = 0 ; i < getters .length ; i ++ ) {
292307 final Member getter = getters [i ];
293308 final Member setter = setters [i ];
309+ boolean found = false ;
294310 if ( getter .getDeclaringClass () == foreignPackageClassInfo .clazz && !Modifier .isPublic ( getter .getModifiers () ) ) {
295311 foreignPackageClassInfo .getters .add ( getter );
312+ found = true ;
296313 }
297314 if ( setter .getDeclaringClass () == foreignPackageClassInfo .clazz && !Modifier .isPublic ( setter .getModifiers () ) ) {
298315 foreignPackageClassInfo .setters .add ( setter );
316+ found = true ;
317+ }
318+ if ( found ) {
319+ foreignPackageClassInfo .propertyNames .add ( propertyNames [i ] );
299320 }
300321 }
301322 if ( foreignPackageClassInfo .getters .isEmpty () && foreignPackageClassInfo .setters .isEmpty () ) {
@@ -307,16 +328,13 @@ private Class<?> determineAccessOptimizerSuperClass(Class<?> clazz, Member[] get
307328 for ( int i = foreignPackageClassInfos .size () - 1 ; i >= 0 ; i -- ) {
308329 final ForeignPackageClassInfo foreignPackageClassInfo = foreignPackageClassInfos .get ( i );
309330 final Class <?> newSuperClass = superClass ;
331+
332+ final String className = foreignPackageClassInfo .clazz .getName () + "$" + OPTIMIZER_PROXY_NAMING_SUFFIX + encodeName ( foreignPackageClassInfo .propertyNames , foreignPackageClassInfo .getters , foreignPackageClassInfo .setters );
310333 superClass = byteBuddyState .load (
311334 foreignPackageClassInfo .clazz ,
312- byteBuddy -> {
313- DynamicType .Builder <?> builder = byteBuddy .with (
314- new NamingStrategy .SuffixingRandom (
315- OPTIMIZER_PROXY_NAMING_SUFFIX ,
316- new NamingStrategy .SuffixingRandom .BaseNameResolver .ForFixedValue (
317- foreignPackageClassInfo .clazz .getName () )
318- )
319- ).subclass ( newSuperClass );
335+ className ,
336+ (byteBuddy , namingStrategy ) -> {
337+ DynamicType .Builder <?> builder = byteBuddy .with ( namingStrategy ).subclass ( newSuperClass );
320338 for ( Member getter : foreignPackageClassInfo .getters ) {
321339 final Class <?> getterType ;
322340 if ( getter instanceof Field ) {
@@ -386,6 +404,42 @@ private Class<?> determineAccessOptimizerSuperClass(Class<?> clazz, Member[] get
386404 return superClass ;
387405 }
388406
407+ private static String encodeName (String [] propertyNames , Member [] getters , Member [] setters ) {
408+ return encodeName ( Arrays .asList ( propertyNames ), Arrays .asList ( getters ), Arrays .asList ( setters ) );
409+ }
410+
411+ private static String encodeName (List <String > propertyNames , List <Member > getters , List <Member > setters ) {
412+ final StringBuilder sb = new StringBuilder ();
413+ for ( int i = 0 ; i < propertyNames .size (); i ++ ) {
414+ final String propertyName = propertyNames .get ( i );
415+ final Member getter = getters .get ( i );
416+ final Member setter = setters .get ( i );
417+ // Encode the two member types as 4 bit integer encoded as hex character
418+ sb .append ( Integer .toHexString ( getKind ( getter ) << 2 | getKind ( setter ) ) );
419+ sb .append ( propertyName );
420+ }
421+ return sb .toString ();
422+ }
423+
424+ private static int getKind (Member member ) {
425+ // Encode the member type as 2 bit integer
426+ if ( member == EMBEDDED_MEMBER ) {
427+ return 0 ;
428+ }
429+ else if ( member instanceof Field ) {
430+ return 1 ;
431+ }
432+ else if ( member instanceof Method ) {
433+ return 2 ;
434+ }
435+ else if ( member instanceof ForeignPackageMember ) {
436+ return 3 ;
437+ }
438+ else {
439+ throw new IllegalArgumentException ( "Unknown member type: " + member );
440+ }
441+ }
442+
389443 private static class ForeignPackageMember implements Member {
390444
391445 private final Class <?> foreignPackageAccessor ;
0 commit comments