1010import java .lang .reflect .Member ;
1111import java .lang .reflect .Method ;
1212import java .lang .reflect .Modifier ;
13+ import java .nio .charset .StandardCharsets ;
1314import java .util .ArrayList ;
1415import java .util .Arrays ;
1516import java .util .Collections ;
@@ -146,11 +147,9 @@ public ReflectionOptimizer getReflectionOptimizer(
146147 fastClass = null ;
147148 }
148149 else {
149- fastClass = byteBuddyState .load ( clazz , byteBuddy -> byteBuddy
150- .with ( new NamingStrategy .SuffixingRandom (
151- INSTANTIATOR_PROXY_NAMING_SUFFIX ,
152- new NamingStrategy .SuffixingRandom .BaseNameResolver .ForFixedValue ( clazz .getName () )
153- ) )
150+ final String className = clazz .getName () + "$" + INSTANTIATOR_PROXY_NAMING_SUFFIX ;
151+ fastClass = byteBuddyState .load ( clazz , className , (byteBuddy , namingStrategy ) -> byteBuddy
152+ .with ( namingStrategy )
154153 .subclass ( ReflectionOptimizer .InstantiationOptimizer .class )
155154 .method ( newInstanceMethodName )
156155 .intercept ( MethodCall .construct ( constructor ) )
@@ -210,11 +209,9 @@ public ReflectionOptimizer getReflectionOptimizer(
210209 fastClass = null ;
211210 }
212211 else {
213- fastClass = byteBuddyState .load ( clazz , byteBuddy -> byteBuddy
214- .with ( new NamingStrategy .SuffixingRandom (
215- INSTANTIATOR_PROXY_NAMING_SUFFIX ,
216- new NamingStrategy .SuffixingRandom .BaseNameResolver .ForFixedValue ( clazz .getName () )
217- ) )
212+ final String className = clazz .getName () + "$" + INSTANTIATOR_PROXY_NAMING_SUFFIX ;
213+ fastClass = byteBuddyState .load ( clazz , className , (byteBuddy , namingStrategy ) -> byteBuddy
214+ .with ( namingStrategy )
218215 .subclass ( ReflectionOptimizer .InstantiationOptimizer .class )
219216 .method ( newInstanceMethodName )
220217 .intercept ( MethodCall .construct ( constructor ) )
@@ -235,23 +232,41 @@ public ReflectionOptimizer getReflectionOptimizer(
235232 return null ;
236233 }
237234
238- Class <?> superClass = determineAccessOptimizerSuperClass ( clazz , getters , setters );
239-
240235 final String [] propertyNames = propertyAccessMap .keySet ().toArray ( new String [0 ] );
241- final Class <?> bulkAccessor = byteBuddyState .load ( clazz , byteBuddy -> byteBuddy
242- .with ( new NamingStrategy .SuffixingRandom (
243- OPTIMIZER_PROXY_NAMING_SUFFIX ,
244- new NamingStrategy .SuffixingRandom .BaseNameResolver .ForFixedValue ( clazz .getName () )
245- ) )
246- .subclass ( superClass )
247- .implement ( ReflectionOptimizer .AccessOptimizer .class )
248- .method ( getPropertyValuesMethodName )
249- .intercept ( new Implementation .Simple ( new GetPropertyValues ( clazz , propertyNames , getters ) ) )
250- .method ( setPropertyValuesMethodName )
251- .intercept ( new Implementation .Simple ( new SetPropertyValues ( clazz , propertyNames , setters ) ) )
252- .method ( getPropertyNamesMethodName )
253- .intercept ( MethodCall .call ( new CloningPropertyCall ( propertyNames ) ) )
254- );
236+ final Class <?> superClass = determineAccessOptimizerSuperClass ( clazz , propertyNames , getters , setters );
237+
238+ final String className = clazz .getName () + "$" + OPTIMIZER_PROXY_NAMING_SUFFIX + encodeName ( propertyNames , getters , setters );
239+ final Class <?> bulkAccessor ;
240+ if ( className .getBytes ( StandardCharsets .UTF_8 ).length >= 0x10000 ) {
241+ // The JVM has a 64K byte limit on class name length, so fallback to random name if encoding exceeds that
242+ bulkAccessor = byteBuddyState .load ( clazz , byteBuddy -> byteBuddy
243+ .with ( new NamingStrategy .SuffixingRandom (
244+ OPTIMIZER_PROXY_NAMING_SUFFIX ,
245+ new NamingStrategy .SuffixingRandom .BaseNameResolver .ForFixedValue ( clazz .getName () )
246+ ) )
247+ .subclass ( superClass )
248+ .implement ( ReflectionOptimizer .AccessOptimizer .class )
249+ .method ( getPropertyValuesMethodName )
250+ .intercept ( new Implementation .Simple ( new GetPropertyValues ( clazz , propertyNames , getters ) ) )
251+ .method ( setPropertyValuesMethodName )
252+ .intercept ( new Implementation .Simple ( new SetPropertyValues ( clazz , propertyNames , setters ) ) )
253+ .method ( getPropertyNamesMethodName )
254+ .intercept ( MethodCall .call ( new CloningPropertyCall ( propertyNames ) ) )
255+ );
256+ }
257+ else {
258+ bulkAccessor = byteBuddyState .load ( clazz , className , (byteBuddy , namingStrategy ) -> byteBuddy
259+ .with ( namingStrategy )
260+ .subclass ( superClass )
261+ .implement ( ReflectionOptimizer .AccessOptimizer .class )
262+ .method ( getPropertyValuesMethodName )
263+ .intercept ( new Implementation .Simple ( new GetPropertyValues ( clazz , propertyNames , getters ) ) )
264+ .method ( setPropertyValuesMethodName )
265+ .intercept ( new Implementation .Simple ( new SetPropertyValues ( clazz , propertyNames , setters ) ) )
266+ .method ( getPropertyNamesMethodName )
267+ .intercept ( MethodCall .call ( new CloningPropertyCall ( propertyNames ) ) )
268+ );
269+ }
255270
256271 try {
257272 return new ReflectionOptimizerImpl (
@@ -266,6 +281,7 @@ public ReflectionOptimizer getReflectionOptimizer(
266281
267282 private static class ForeignPackageClassInfo {
268283 final Class <?> clazz ;
284+ final List <String > propertyNames = new ArrayList <>();
269285 final List <Member > getters = new ArrayList <>();
270286 final List <Member > setters = new ArrayList <>();
271287
@@ -274,7 +290,7 @@ public ForeignPackageClassInfo(Class<?> clazz) {
274290 }
275291 }
276292
277- private Class <?> determineAccessOptimizerSuperClass (Class <?> clazz , Member [] getters , Member [] setters ) {
293+ private Class <?> determineAccessOptimizerSuperClass (Class <?> clazz , String [] propertyNames , Member [] getters , Member [] setters ) {
278294 if ( clazz .isInterface () ) {
279295 return Object .class ;
280296 }
@@ -288,11 +304,17 @@ private Class<?> determineAccessOptimizerSuperClass(Class<?> clazz, Member[] get
288304 for ( int i = 0 ; i < getters .length ; i ++ ) {
289305 final Member getter = getters [i ];
290306 final Member setter = setters [i ];
307+ boolean found = false ;
291308 if ( getter .getDeclaringClass () == foreignPackageClassInfo .clazz && !Modifier .isPublic ( getter .getModifiers () ) ) {
292309 foreignPackageClassInfo .getters .add ( getter );
310+ found = true ;
293311 }
294312 if ( setter .getDeclaringClass () == foreignPackageClassInfo .clazz && !Modifier .isPublic ( setter .getModifiers () ) ) {
295313 foreignPackageClassInfo .setters .add ( setter );
314+ found = true ;
315+ }
316+ if ( found ) {
317+ foreignPackageClassInfo .propertyNames .add ( propertyNames [i ] );
296318 }
297319 }
298320 if ( foreignPackageClassInfo .getters .isEmpty () && foreignPackageClassInfo .setters .isEmpty () ) {
@@ -304,16 +326,13 @@ private Class<?> determineAccessOptimizerSuperClass(Class<?> clazz, Member[] get
304326 for ( int i = foreignPackageClassInfos .size () - 1 ; i >= 0 ; i -- ) {
305327 final ForeignPackageClassInfo foreignPackageClassInfo = foreignPackageClassInfos .get ( i );
306328 final Class <?> newSuperClass = superClass ;
329+
330+ final String className = foreignPackageClassInfo .clazz .getName () + "$" + OPTIMIZER_PROXY_NAMING_SUFFIX + encodeName ( foreignPackageClassInfo .propertyNames , foreignPackageClassInfo .getters , foreignPackageClassInfo .setters );
307331 superClass = byteBuddyState .load (
308332 foreignPackageClassInfo .clazz ,
309- byteBuddy -> {
310- DynamicType .Builder <?> builder = byteBuddy .with (
311- new NamingStrategy .SuffixingRandom (
312- OPTIMIZER_PROXY_NAMING_SUFFIX ,
313- new NamingStrategy .SuffixingRandom .BaseNameResolver .ForFixedValue (
314- foreignPackageClassInfo .clazz .getName () )
315- )
316- ).subclass ( newSuperClass );
333+ className ,
334+ (byteBuddy , namingStrategy ) -> {
335+ DynamicType .Builder <?> builder = byteBuddy .with ( namingStrategy ).subclass ( newSuperClass );
317336 for ( Member getter : foreignPackageClassInfo .getters ) {
318337 final Class <?> getterType ;
319338 if ( getter instanceof Field ) {
@@ -383,6 +402,42 @@ private Class<?> determineAccessOptimizerSuperClass(Class<?> clazz, Member[] get
383402 return superClass ;
384403 }
385404
405+ private static String encodeName (String [] propertyNames , Member [] getters , Member [] setters ) {
406+ return encodeName ( Arrays .asList ( propertyNames ), Arrays .asList ( getters ), Arrays .asList ( setters ) );
407+ }
408+
409+ private static String encodeName (List <String > propertyNames , List <Member > getters , List <Member > setters ) {
410+ final StringBuilder sb = new StringBuilder ();
411+ for ( int i = 0 ; i < propertyNames .size (); i ++ ) {
412+ final String propertyName = propertyNames .get ( i );
413+ final Member getter = getters .get ( i );
414+ final Member setter = setters .get ( i );
415+ // Encode the two member types as 4 bit integer encoded as hex character
416+ sb .append ( Integer .toHexString ( getKind ( getter ) << 2 | getKind ( setter ) ) );
417+ sb .append ( propertyName );
418+ }
419+ return sb .toString ();
420+ }
421+
422+ private static int getKind (Member member ) {
423+ // Encode the member type as 2 bit integer
424+ if ( member == EMBEDDED_MEMBER ) {
425+ return 0 ;
426+ }
427+ else if ( member instanceof Field ) {
428+ return 1 ;
429+ }
430+ else if ( member instanceof Method ) {
431+ return 2 ;
432+ }
433+ else if ( member instanceof ForeignPackageMember ) {
434+ return 3 ;
435+ }
436+ else {
437+ throw new IllegalArgumentException ( "Unknown member type: " + member );
438+ }
439+ }
440+
386441 private static class ForeignPackageMember implements Member {
387442
388443 private final Class <?> foreignPackageAccessor ;
0 commit comments