10
10
import java .lang .reflect .Member ;
11
11
import java .lang .reflect .Method ;
12
12
import java .lang .reflect .Modifier ;
13
+ import java .nio .charset .StandardCharsets ;
13
14
import java .util .ArrayList ;
14
15
import java .util .Arrays ;
15
16
import java .util .Collections ;
@@ -146,11 +147,9 @@ public ReflectionOptimizer getReflectionOptimizer(
146
147
fastClass = null ;
147
148
}
148
149
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 )
154
153
.subclass ( ReflectionOptimizer .InstantiationOptimizer .class )
155
154
.method ( newInstanceMethodName )
156
155
.intercept ( MethodCall .construct ( constructor ) )
@@ -210,11 +209,9 @@ public ReflectionOptimizer getReflectionOptimizer(
210
209
fastClass = null ;
211
210
}
212
211
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 )
218
215
.subclass ( ReflectionOptimizer .InstantiationOptimizer .class )
219
216
.method ( newInstanceMethodName )
220
217
.intercept ( MethodCall .construct ( constructor ) )
@@ -235,23 +232,41 @@ public ReflectionOptimizer getReflectionOptimizer(
235
232
return null ;
236
233
}
237
234
238
- Class <?> superClass = determineAccessOptimizerSuperClass ( clazz , getters , setters );
239
-
240
235
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
+ }
255
270
256
271
try {
257
272
return new ReflectionOptimizerImpl (
@@ -266,6 +281,7 @@ public ReflectionOptimizer getReflectionOptimizer(
266
281
267
282
private static class ForeignPackageClassInfo {
268
283
final Class <?> clazz ;
284
+ final List <String > propertyNames = new ArrayList <>();
269
285
final List <Member > getters = new ArrayList <>();
270
286
final List <Member > setters = new ArrayList <>();
271
287
@@ -274,7 +290,7 @@ public ForeignPackageClassInfo(Class<?> clazz) {
274
290
}
275
291
}
276
292
277
- private Class <?> determineAccessOptimizerSuperClass (Class <?> clazz , Member [] getters , Member [] setters ) {
293
+ private Class <?> determineAccessOptimizerSuperClass (Class <?> clazz , String [] propertyNames , Member [] getters , Member [] setters ) {
278
294
if ( clazz .isInterface () ) {
279
295
return Object .class ;
280
296
}
@@ -288,11 +304,17 @@ private Class<?> determineAccessOptimizerSuperClass(Class<?> clazz, Member[] get
288
304
for ( int i = 0 ; i < getters .length ; i ++ ) {
289
305
final Member getter = getters [i ];
290
306
final Member setter = setters [i ];
307
+ boolean found = false ;
291
308
if ( getter .getDeclaringClass () == foreignPackageClassInfo .clazz && !Modifier .isPublic ( getter .getModifiers () ) ) {
292
309
foreignPackageClassInfo .getters .add ( getter );
310
+ found = true ;
293
311
}
294
312
if ( setter .getDeclaringClass () == foreignPackageClassInfo .clazz && !Modifier .isPublic ( setter .getModifiers () ) ) {
295
313
foreignPackageClassInfo .setters .add ( setter );
314
+ found = true ;
315
+ }
316
+ if ( found ) {
317
+ foreignPackageClassInfo .propertyNames .add ( propertyNames [i ] );
296
318
}
297
319
}
298
320
if ( foreignPackageClassInfo .getters .isEmpty () && foreignPackageClassInfo .setters .isEmpty () ) {
@@ -304,16 +326,13 @@ private Class<?> determineAccessOptimizerSuperClass(Class<?> clazz, Member[] get
304
326
for ( int i = foreignPackageClassInfos .size () - 1 ; i >= 0 ; i -- ) {
305
327
final ForeignPackageClassInfo foreignPackageClassInfo = foreignPackageClassInfos .get ( i );
306
328
final Class <?> newSuperClass = superClass ;
329
+
330
+ final String className = foreignPackageClassInfo .clazz .getName () + "$" + OPTIMIZER_PROXY_NAMING_SUFFIX + encodeName ( foreignPackageClassInfo .propertyNames , foreignPackageClassInfo .getters , foreignPackageClassInfo .setters );
307
331
superClass = byteBuddyState .load (
308
332
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 );
317
336
for ( Member getter : foreignPackageClassInfo .getters ) {
318
337
final Class <?> getterType ;
319
338
if ( getter instanceof Field ) {
@@ -383,6 +402,42 @@ private Class<?> determineAccessOptimizerSuperClass(Class<?> clazz, Member[] get
383
402
return superClass ;
384
403
}
385
404
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
+
386
441
private static class ForeignPackageMember implements Member {
387
442
388
443
private final Class <?> foreignPackageAccessor ;
0 commit comments