15
15
import java .util .ArrayList ;
16
16
import java .util .Collections ;
17
17
import java .util .List ;
18
+ import javax .annotation .Nonnull ;
18
19
import javax .annotation .Nullable ;
19
20
import net .bytebuddy .agent .builder .AgentBuilder ;
20
21
import net .bytebuddy .description .annotation .AnnotationList ;
22
+ import net .bytebuddy .description .annotation .AnnotationValue ;
21
23
import net .bytebuddy .description .method .MethodDescription ;
22
24
import net .bytebuddy .description .method .MethodList ;
25
+ import net .bytebuddy .description .method .ParameterDescription ;
26
+ import net .bytebuddy .description .method .ParameterList ;
23
27
import net .bytebuddy .description .type .TypeDescription ;
24
28
import net .bytebuddy .description .type .TypeList ;
25
29
import net .bytebuddy .dynamic .ClassFileLocator ;
@@ -294,6 +298,8 @@ public void clear() {
294
298
295
299
/** Based on TypePool.Default.WithLazyResolution */
296
300
private class AgentTypePool extends TypePool .Default {
301
+ // ThreadLocal used for detecting loading of annotation types
302
+ private final ThreadLocal <Boolean > loadingAnnotations = new ThreadLocal <>();
297
303
private final WeakReference <ClassLoader > classLoaderRef ;
298
304
299
305
public AgentTypePool (
@@ -331,6 +337,18 @@ protected TypePool.Resolution doResolve(String name) {
331
337
return resolution ;
332
338
}
333
339
340
+ void enterLoadAnnotations () {
341
+ loadingAnnotations .set (Boolean .TRUE );
342
+ }
343
+
344
+ void exitLoadAnnotations () {
345
+ loadingAnnotations .set (null );
346
+ }
347
+
348
+ boolean isLoadingAnnotations () {
349
+ return loadingAnnotations .get () != null ;
350
+ }
351
+
334
352
/** Based on TypePool.Default.WithLazyResolution.LazyResolution */
335
353
private class LazyResolution implements TypePool .Resolution {
336
354
private final WeakReference <ClassLoader > classLoaderRef ;
@@ -343,6 +361,19 @@ private class LazyResolution implements TypePool.Resolution {
343
361
344
362
@ Override
345
363
public boolean isResolved () {
364
+ // Like jdk, byte-buddy getDeclaredAnnotations does not report annotations whose class is
365
+ // missing. To do this it needs to locate the bytes for annotation types used in class.
366
+ // Which means that if we have a matcher that matches methods annotated with @Foo byte-buddy
367
+ // will end up location bytes for all annotations used on any method in the classes that
368
+ // this matcher is applied to. From our perspective this is unreasonable, we just want to
369
+ // match based on annotation name with as little overhead as possible. As we match only
370
+ // based on annotation name we never need to locate the bytes for the annotation type.
371
+ // See TypePool.Default.LazyTypeDescription.LazyAnnotationDescription.asList()
372
+ // When isResolved() is called during loading of annotations delay resolving to avoid
373
+ // looking up the class bytes.
374
+ if (isLoadingAnnotations ()) {
375
+ return true ;
376
+ }
346
377
return doResolve (name ).isResolved ();
347
378
}
348
379
@@ -354,6 +385,11 @@ public TypeDescription resolve() {
354
385
// super class and interfaces multiple times
355
386
if (cached == null ) {
356
387
cached = new AgentTypePool .LazyTypeDescription (classLoaderRef , name );
388
+ // if we know that an annotation is being loaded wrap the result so that we wouldn't
389
+ // need to resolve the class bytes to tell whether it is an annotation
390
+ if (isLoadingAnnotations ()) {
391
+ cached = new AnnotationTypeDescription (cached );
392
+ }
357
393
}
358
394
return cached ;
359
395
}
@@ -376,7 +412,15 @@ protected TypeDescription delegate() {
376
412
@ Override
377
413
public AnnotationList getDeclaredAnnotations () {
378
414
if (annotations == null ) {
379
- annotations = delegate ().getDeclaredAnnotations ();
415
+ TypeDescription delegate = delegate ();
416
+ // Run getDeclaredAnnotations with ThreadLocal. ThreadLocal helps us detect types looked
417
+ // up by getDeclaredAnnotations and treat them specially.
418
+ enterLoadAnnotations ();
419
+ try {
420
+ annotations = delegate .getDeclaredAnnotations ();
421
+ } finally {
422
+ exitLoadAnnotations ();
423
+ }
380
424
}
381
425
return annotations ;
382
426
}
@@ -413,12 +457,12 @@ public String getName() {
413
457
return name ;
414
458
}
415
459
416
- private volatile Generic cachedSuperClass ;
460
+ private volatile TypeDescription . Generic cachedSuperClass ;
417
461
418
462
@ Override
419
- public Generic getSuperClass () {
463
+ public TypeDescription . Generic getSuperClass () {
420
464
if (cachedSuperClass == null ) {
421
- Generic superClassDescription = delegate ().getSuperClass ();
465
+ TypeDescription . Generic superClassDescription = delegate ().getSuperClass ();
422
466
ClassLoader classLoader = classLoaderRef .get ();
423
467
if (canUseFindLoadedClass () && classLoader != null && superClassDescription != null ) {
424
468
String superName = superClassDescription .getTypeName ();
@@ -445,7 +489,7 @@ public TypeList.Generic getInterfaces() {
445
489
// here we use raw types and loose generic info
446
490
// we don't expect to have matchers that would use the generic info
447
491
List <TypeDescription > result = new ArrayList <>();
448
- for (Generic interfaceDescription : interfaces ) {
492
+ for (TypeDescription . Generic interfaceDescription : interfaces ) {
449
493
String interfaceName = interfaceDescription .getTypeName ();
450
494
Class <?> interfaceClass = findLoadedClass (classLoader , interfaceName );
451
495
if (interfaceClass != null ) {
@@ -461,6 +505,45 @@ public TypeList.Generic getInterfaces() {
461
505
}
462
506
return cachedInterfaces ;
463
507
}
508
+
509
+ private class LazyAnnotationMethodDescription extends DelegatingMethodDescription {
510
+
511
+ LazyAnnotationMethodDescription (MethodDescription .InDefinedShape method ) {
512
+ super (method );
513
+ }
514
+
515
+ @ Override
516
+ public AnnotationList getDeclaredAnnotations () {
517
+ // Run getDeclaredAnnotations with ThreadLocal. ThreadLocal helps us detect types looked
518
+ // up by getDeclaredAnnotations and treat them specially.
519
+ enterLoadAnnotations ();
520
+ try {
521
+ return method .getDeclaredAnnotations ();
522
+ } finally {
523
+ exitLoadAnnotations ();
524
+ }
525
+ }
526
+ }
527
+
528
+ @ Override
529
+ public MethodList <MethodDescription .InDefinedShape > getDeclaredMethods () {
530
+ MethodList <MethodDescription .InDefinedShape > methods = super .getDeclaredMethods ();
531
+
532
+ class MethodListWrapper extends MethodList .AbstractBase <MethodDescription .InDefinedShape > {
533
+
534
+ @ Override
535
+ public MethodDescription .InDefinedShape get (int index ) {
536
+ return new LazyAnnotationMethodDescription (methods .get (index ));
537
+ }
538
+
539
+ @ Override
540
+ public int size () {
541
+ return methods .size ();
542
+ }
543
+ }
544
+
545
+ return new MethodListWrapper ();
546
+ }
464
547
}
465
548
466
549
private AgentTypePool .LazyTypeDescriptionWithClass newTypeDescription (Class <?> clazz ) {
@@ -493,10 +576,10 @@ public String getName() {
493
576
return name ;
494
577
}
495
578
496
- private volatile Generic cachedSuperClass ;
579
+ private volatile TypeDescription . Generic cachedSuperClass ;
497
580
498
581
@ Override
499
- public Generic getSuperClass () {
582
+ public TypeDescription . Generic getSuperClass () {
500
583
if (cachedSuperClass == null ) {
501
584
Class <?> clazz = classRef .get ();
502
585
if (clazz == null ) {
@@ -556,4 +639,91 @@ private static AgentTypePool.LazyTypeDescriptionWithClass newLazyTypeDescription
556
639
}
557
640
return pool .new LazyTypeDescriptionWithClass (clazz );
558
641
}
642
+
643
+ /**
644
+ * Class descriptor that claims to represent an annotation without checking whether the underlying
645
+ * type really is an annotation.
646
+ */
647
+ private static class AnnotationTypeDescription
648
+ extends TypeDescription .AbstractBase .OfSimpleType .WithDelegation {
649
+ private final TypeDescription delegate ;
650
+
651
+ AnnotationTypeDescription (TypeDescription delegate ) {
652
+ this .delegate = delegate ;
653
+ }
654
+
655
+ @ Override
656
+ protected TypeDescription delegate () {
657
+ return delegate ;
658
+ }
659
+
660
+ @ Override
661
+ public String getName () {
662
+ return delegate .getName ();
663
+ }
664
+
665
+ @ Override
666
+ public boolean isAnnotation () {
667
+ // by default byte-buddy checks whether class modifiers have annotation bit set
668
+ // as we wish to avoid looking up the class bytes we assume that every that was expected
669
+ // to be an annotation really is an annotation and return true here
670
+ // See TypePool.Default.LazyTypeDescription.LazyAnnotationDescription.asList()
671
+ return true ;
672
+ }
673
+ }
674
+
675
+ private static class DelegatingMethodDescription
676
+ extends MethodDescription .InDefinedShape .AbstractBase {
677
+ protected final MethodDescription .InDefinedShape method ;
678
+
679
+ DelegatingMethodDescription (MethodDescription .InDefinedShape method ) {
680
+ this .method = method ;
681
+ }
682
+
683
+ @ Nonnull
684
+ @ Override
685
+ public TypeDescription getDeclaringType () {
686
+ return method .getDeclaringType ();
687
+ }
688
+
689
+ @ Override
690
+ public TypeDescription .Generic getReturnType () {
691
+ return method .getReturnType ();
692
+ }
693
+
694
+ @ Override
695
+ public ParameterList <ParameterDescription .InDefinedShape > getParameters () {
696
+ return method .getParameters ();
697
+ }
698
+
699
+ @ Override
700
+ public TypeList .Generic getExceptionTypes () {
701
+ return method .getExceptionTypes ();
702
+ }
703
+
704
+ @ Override
705
+ public AnnotationValue <?, ?> getDefaultValue () {
706
+ return method .getDefaultValue ();
707
+ }
708
+
709
+ @ Override
710
+ public String getInternalName () {
711
+ return method .getInternalName ();
712
+ }
713
+
714
+ @ Override
715
+ public TypeList .Generic getTypeVariables () {
716
+ return method .getTypeVariables ();
717
+ }
718
+
719
+ @ Override
720
+ public int getModifiers () {
721
+ return method .getModifiers ();
722
+ }
723
+
724
+ @ Override
725
+ public AnnotationList getDeclaredAnnotations () {
726
+ return method .getDeclaredAnnotations ();
727
+ }
728
+ }
559
729
}
0 commit comments