12
12
import java .lang .reflect .Member ;
13
13
import java .lang .reflect .Method ;
14
14
import java .lang .reflect .Modifier ;
15
+ import java .nio .charset .StandardCharsets ;
15
16
import java .util .ArrayList ;
16
17
import java .util .Arrays ;
17
18
import java .util .Collections ;
68
69
import net .bytebuddy .jar .asm .Type ;
69
70
import net .bytebuddy .matcher .ElementMatcher ;
70
71
import net .bytebuddy .matcher .ElementMatchers ;
71
- import net .bytebuddy .pool .TypePool ;
72
72
import org .checkerframework .checker .nullness .qual .Nullable ;
73
73
74
74
public class BytecodeProviderImpl implements BytecodeProvider {
@@ -149,11 +149,9 @@ public ReflectionOptimizer getReflectionOptimizer(
149
149
fastClass = null ;
150
150
}
151
151
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 )
157
155
.subclass ( ReflectionOptimizer .InstantiationOptimizer .class )
158
156
.method ( newInstanceMethodName )
159
157
.intercept ( MethodCall .construct ( constructor ) )
@@ -213,11 +211,9 @@ public ReflectionOptimizer getReflectionOptimizer(
213
211
fastClass = null ;
214
212
}
215
213
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 )
221
217
.subclass ( ReflectionOptimizer .InstantiationOptimizer .class )
222
218
.method ( newInstanceMethodName )
223
219
.intercept ( MethodCall .construct ( constructor ) )
@@ -238,23 +234,41 @@ public ReflectionOptimizer getReflectionOptimizer(
238
234
return null ;
239
235
}
240
236
241
- Class <?> superClass = determineAccessOptimizerSuperClass ( clazz , getters , setters );
242
-
243
237
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
+ }
258
272
259
273
try {
260
274
return new ReflectionOptimizerImpl (
@@ -269,6 +283,7 @@ public ReflectionOptimizer getReflectionOptimizer(
269
283
270
284
private static class ForeignPackageClassInfo {
271
285
final Class <?> clazz ;
286
+ final List <String > propertyNames = new ArrayList <>();
272
287
final List <Member > getters = new ArrayList <>();
273
288
final List <Member > setters = new ArrayList <>();
274
289
@@ -277,7 +292,7 @@ public ForeignPackageClassInfo(Class<?> clazz) {
277
292
}
278
293
}
279
294
280
- private Class <?> determineAccessOptimizerSuperClass (Class <?> clazz , Member [] getters , Member [] setters ) {
295
+ private Class <?> determineAccessOptimizerSuperClass (Class <?> clazz , String [] propertyNames , Member [] getters , Member [] setters ) {
281
296
if ( clazz .isInterface () ) {
282
297
return Object .class ;
283
298
}
@@ -291,11 +306,17 @@ private Class<?> determineAccessOptimizerSuperClass(Class<?> clazz, Member[] get
291
306
for ( int i = 0 ; i < getters .length ; i ++ ) {
292
307
final Member getter = getters [i ];
293
308
final Member setter = setters [i ];
309
+ boolean found = false ;
294
310
if ( getter .getDeclaringClass () == foreignPackageClassInfo .clazz && !Modifier .isPublic ( getter .getModifiers () ) ) {
295
311
foreignPackageClassInfo .getters .add ( getter );
312
+ found = true ;
296
313
}
297
314
if ( setter .getDeclaringClass () == foreignPackageClassInfo .clazz && !Modifier .isPublic ( setter .getModifiers () ) ) {
298
315
foreignPackageClassInfo .setters .add ( setter );
316
+ found = true ;
317
+ }
318
+ if ( found ) {
319
+ foreignPackageClassInfo .propertyNames .add ( propertyNames [i ] );
299
320
}
300
321
}
301
322
if ( foreignPackageClassInfo .getters .isEmpty () && foreignPackageClassInfo .setters .isEmpty () ) {
@@ -307,16 +328,13 @@ private Class<?> determineAccessOptimizerSuperClass(Class<?> clazz, Member[] get
307
328
for ( int i = foreignPackageClassInfos .size () - 1 ; i >= 0 ; i -- ) {
308
329
final ForeignPackageClassInfo foreignPackageClassInfo = foreignPackageClassInfos .get ( i );
309
330
final Class <?> newSuperClass = superClass ;
331
+
332
+ final String className = foreignPackageClassInfo .clazz .getName () + "$" + OPTIMIZER_PROXY_NAMING_SUFFIX + encodeName ( foreignPackageClassInfo .propertyNames , foreignPackageClassInfo .getters , foreignPackageClassInfo .setters );
310
333
superClass = byteBuddyState .load (
311
334
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 );
320
338
for ( Member getter : foreignPackageClassInfo .getters ) {
321
339
final Class <?> getterType ;
322
340
if ( getter instanceof Field ) {
@@ -386,6 +404,42 @@ private Class<?> determineAccessOptimizerSuperClass(Class<?> clazz, Member[] get
386
404
return superClass ;
387
405
}
388
406
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
+
389
443
private static class ForeignPackageMember implements Member {
390
444
391
445
private final Class <?> foreignPackageAccessor ;
0 commit comments