6868import net .bytebuddy .jar .asm .Type ;
6969import net .bytebuddy .matcher .ElementMatcher ;
7070import net .bytebuddy .matcher .ElementMatchers ;
71- import net .bytebuddy .pool .TypePool ;
7271import org .checkerframework .checker .nullness .qual .Nullable ;
7372
7473public class BytecodeProviderImpl implements BytecodeProvider {
@@ -149,11 +148,9 @@ public ReflectionOptimizer getReflectionOptimizer(
149148 fastClass = null ;
150149 }
151150 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- ) )
151+ final String className = clazz .getName () + "$" + INSTANTIATOR_PROXY_NAMING_SUFFIX ;
152+ fastClass = byteBuddyState .load ( clazz , className , (byteBuddy , namingStrategy ) -> byteBuddy
153+ .with ( namingStrategy )
157154 .subclass ( ReflectionOptimizer .InstantiationOptimizer .class )
158155 .method ( newInstanceMethodName )
159156 .intercept ( MethodCall .construct ( constructor ) )
@@ -213,11 +210,9 @@ public ReflectionOptimizer getReflectionOptimizer(
213210 fastClass = null ;
214211 }
215212 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- ) )
213+ final String className = clazz .getName () + "$" + INSTANTIATOR_PROXY_NAMING_SUFFIX ;
214+ fastClass = byteBuddyState .load ( clazz , className , (byteBuddy , namingStrategy ) -> byteBuddy
215+ .with ( namingStrategy )
221216 .subclass ( ReflectionOptimizer .InstantiationOptimizer .class )
222217 .method ( newInstanceMethodName )
223218 .intercept ( MethodCall .construct ( constructor ) )
@@ -238,23 +233,41 @@ public ReflectionOptimizer getReflectionOptimizer(
238233 return null ;
239234 }
240235
241- Class <?> superClass = determineAccessOptimizerSuperClass ( clazz , getters , setters );
242-
243236 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- );
237+ final Class <?> superClass = determineAccessOptimizerSuperClass ( clazz , propertyNames , getters , setters );
238+
239+ final String className = clazz .getName () + "$" + OPTIMIZER_PROXY_NAMING_SUFFIX + encodeName ( propertyNames , getters , setters );
240+ final Class <?> bulkAccessor ;
241+ if ( className .length () >= 0x10000 ) {
242+ // The JVM has a 16K limit on the class name length, so fallback to random name if the encoding exceeds that
243+ bulkAccessor = byteBuddyState .load ( clazz , byteBuddy -> byteBuddy
244+ .with ( new NamingStrategy .SuffixingRandom (
245+ OPTIMIZER_PROXY_NAMING_SUFFIX ,
246+ new NamingStrategy .SuffixingRandom .BaseNameResolver .ForFixedValue ( clazz .getName () )
247+ ) )
248+ .subclass ( superClass )
249+ .implement ( ReflectionOptimizer .AccessOptimizer .class )
250+ .method ( getPropertyValuesMethodName )
251+ .intercept ( new Implementation .Simple ( new GetPropertyValues ( clazz , propertyNames , getters ) ) )
252+ .method ( setPropertyValuesMethodName )
253+ .intercept ( new Implementation .Simple ( new SetPropertyValues ( clazz , propertyNames , setters ) ) )
254+ .method ( getPropertyNamesMethodName )
255+ .intercept ( MethodCall .call ( new CloningPropertyCall ( propertyNames ) ) )
256+ );
257+ }
258+ else {
259+ bulkAccessor = byteBuddyState .load ( clazz , className , (byteBuddy , namingStrategy ) -> byteBuddy
260+ .with ( namingStrategy )
261+ .subclass ( superClass )
262+ .implement ( ReflectionOptimizer .AccessOptimizer .class )
263+ .method ( getPropertyValuesMethodName )
264+ .intercept ( new Implementation .Simple ( new GetPropertyValues ( clazz , propertyNames , getters ) ) )
265+ .method ( setPropertyValuesMethodName )
266+ .intercept ( new Implementation .Simple ( new SetPropertyValues ( clazz , propertyNames , setters ) ) )
267+ .method ( getPropertyNamesMethodName )
268+ .intercept ( MethodCall .call ( new CloningPropertyCall ( propertyNames ) ) )
269+ );
270+ }
258271
259272 try {
260273 return new ReflectionOptimizerImpl (
@@ -269,6 +282,7 @@ public ReflectionOptimizer getReflectionOptimizer(
269282
270283 private static class ForeignPackageClassInfo {
271284 final Class <?> clazz ;
285+ final List <String > propertyNames = new ArrayList <>();
272286 final List <Member > getters = new ArrayList <>();
273287 final List <Member > setters = new ArrayList <>();
274288
@@ -277,7 +291,7 @@ public ForeignPackageClassInfo(Class<?> clazz) {
277291 }
278292 }
279293
280- private Class <?> determineAccessOptimizerSuperClass (Class <?> clazz , Member [] getters , Member [] setters ) {
294+ private Class <?> determineAccessOptimizerSuperClass (Class <?> clazz , String [] propertyNames , Member [] getters , Member [] setters ) {
281295 if ( clazz .isInterface () ) {
282296 return Object .class ;
283297 }
@@ -291,11 +305,17 @@ private Class<?> determineAccessOptimizerSuperClass(Class<?> clazz, Member[] get
291305 for ( int i = 0 ; i < getters .length ; i ++ ) {
292306 final Member getter = getters [i ];
293307 final Member setter = setters [i ];
308+ boolean found = false ;
294309 if ( getter .getDeclaringClass () == foreignPackageClassInfo .clazz && !Modifier .isPublic ( getter .getModifiers () ) ) {
295310 foreignPackageClassInfo .getters .add ( getter );
311+ found = true ;
296312 }
297313 if ( setter .getDeclaringClass () == foreignPackageClassInfo .clazz && !Modifier .isPublic ( setter .getModifiers () ) ) {
298314 foreignPackageClassInfo .setters .add ( setter );
315+ found = true ;
316+ }
317+ if ( found ) {
318+ foreignPackageClassInfo .propertyNames .add ( propertyNames [i ] );
299319 }
300320 }
301321 if ( foreignPackageClassInfo .getters .isEmpty () && foreignPackageClassInfo .setters .isEmpty () ) {
@@ -307,16 +327,13 @@ private Class<?> determineAccessOptimizerSuperClass(Class<?> clazz, Member[] get
307327 for ( int i = foreignPackageClassInfos .size () - 1 ; i >= 0 ; i -- ) {
308328 final ForeignPackageClassInfo foreignPackageClassInfo = foreignPackageClassInfos .get ( i );
309329 final Class <?> newSuperClass = superClass ;
330+
331+ final String className = foreignPackageClassInfo .clazz .getName () + "$" + OPTIMIZER_PROXY_NAMING_SUFFIX + encodeName ( foreignPackageClassInfo .propertyNames , foreignPackageClassInfo .getters , foreignPackageClassInfo .setters );
310332 superClass = byteBuddyState .load (
311333 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 );
334+ className ,
335+ (byteBuddy , namingStrategy ) -> {
336+ DynamicType .Builder <?> builder = byteBuddy .with ( namingStrategy ).subclass ( newSuperClass );
320337 for ( Member getter : foreignPackageClassInfo .getters ) {
321338 final Class <?> getterType ;
322339 if ( getter instanceof Field ) {
@@ -386,6 +403,42 @@ private Class<?> determineAccessOptimizerSuperClass(Class<?> clazz, Member[] get
386403 return superClass ;
387404 }
388405
406+ private static String encodeName (String [] propertyNames , Member [] getters , Member [] setters ) {
407+ return encodeName ( Arrays .asList ( propertyNames ), Arrays .asList ( getters ), Arrays .asList ( setters ) );
408+ }
409+
410+ private static String encodeName (List <String > propertyNames , List <Member > getters , List <Member > setters ) {
411+ final StringBuilder sb = new StringBuilder ();
412+ for ( int i = 0 ; i < propertyNames .size (); i ++ ) {
413+ final String propertyName = propertyNames .get ( i );
414+ final Member getter = getters .get ( i );
415+ final Member setter = setters .get ( i );
416+ // Encode the two member types as 4 bit integer encoded as hex character
417+ sb .append ( Integer .toHexString ( getKind ( getter ) << 2 | getKind ( setter ) ) );
418+ sb .append ( propertyName );
419+ }
420+ return sb .toString ();
421+ }
422+
423+ private static int getKind (Member member ) {
424+ // Encode the member type as 2 bit integer
425+ if ( member == EMBEDDED_MEMBER ) {
426+ return 0 ;
427+ }
428+ else if ( member instanceof Field ) {
429+ return 1 ;
430+ }
431+ else if ( member instanceof Method ) {
432+ return 2 ;
433+ }
434+ else if ( member instanceof ForeignPackageMember ) {
435+ return 3 ;
436+ }
437+ else {
438+ throw new IllegalArgumentException ( "Unknown member type: " + member );
439+ }
440+ }
441+
389442 private static class ForeignPackageMember implements Member {
390443
391444 private final Class <?> foreignPackageAccessor ;
0 commit comments