11package datadog .trace .agent .tooling .muzzle ;
22
3- import datadog .trace .agent .tooling .HelperInjector ;
43import datadog .trace .agent .tooling .Instrumenter ;
5- import java .lang .reflect .Method ;
6- import java .util .List ;
7- import java .util .ServiceLoader ;
4+ import net .bytebuddy .asm .AsmVisitorWrapper ;
85import net .bytebuddy .build .Plugin ;
6+ import net .bytebuddy .description .field .FieldDescription ;
7+ import net .bytebuddy .description .field .FieldList ;
8+ import net .bytebuddy .description .method .MethodList ;
99import net .bytebuddy .description .type .TypeDefinition ;
1010import net .bytebuddy .description .type .TypeDescription ;
1111import net .bytebuddy .dynamic .DynamicType .Builder ;
12+ import net .bytebuddy .implementation .Implementation ;
13+ import net .bytebuddy .jar .asm .ClassVisitor ;
14+ import net .bytebuddy .jar .asm .MethodVisitor ;
15+ import net .bytebuddy .jar .asm .Opcodes ;
16+ import net .bytebuddy .pool .TypePool ;
1217
18+ /** Bytebuddy gradle plugin which creates muzzle-references at compile time. */
1319public class MuzzleGradlePlugin implements Plugin {
14- // TODO:
15- // - Additional references to check
16- // - Fields
17- // - methods
18- // - visit annotations
19- // - visit parameter types
20- // - visit method instructions
21- // - method invoke type
22- // - access flags (including implicit package-private)
23- // - supertypes
24- // - Misc
25- // - Also match interfaces which extend Instrumenter
26- // - Expose config instead of hardcoding datadog namespace (or reconfigure classpath)
27- // - Run muzzle in matching phase (may require a rewrite of the instrumentation api)
28- // - Documentation
29- // - Fix TraceConfigInstrumentation
30- // - assert no muzzle field defined in instrumentation
31- // - make getMuzzle final in default and remove in gradle plugin
32- // - pull muzzle field + method names into static constants
33-
3420 private static final TypeDescription DefaultInstrumenterTypeDesc =
3521 new TypeDescription .ForLoadedType (Instrumenter .Default .class );
3622
3723 @ Override
3824 public boolean matches (final TypeDescription target ) {
3925 // AutoService annotation is not retained at runtime. Check for Instrumenter.Default supertype
4026 boolean isInstrumenter = false ;
41- TypeDefinition instrumenter = target ;
27+ TypeDefinition instrumenter = null == target ? null : target . getSuperClass () ;
4228 while (instrumenter != null ) {
4329 if (instrumenter .equals (DefaultInstrumenterTypeDesc )) {
4430 isInstrumenter = true ;
@@ -51,15 +37,10 @@ public boolean matches(final TypeDescription target) {
5137
5238 @ Override
5339 public Builder <?> apply (Builder <?> builder , TypeDescription typeDescription ) {
54- if (typeDescription .equals (DefaultInstrumenterTypeDesc )) {
55- // FIXME
56- System .out .println ("~~~~FIXME: remove final modifier on Default: " + typeDescription );
57- return builder ;
58- } else {
59- return builder .visit (new MuzzleVisitor ());
60- }
40+ return builder .visit (new MuzzleVisitor ());
6141 }
6242
43+ /** Compile-time Optimization used by gradle buildscripts. */
6344 public static class NoOp implements Plugin {
6445 @ Override
6546 public boolean matches (final TypeDescription target ) {
@@ -72,53 +53,56 @@ public Builder<?> apply(Builder<?> builder, TypeDescription typeDescription) {
7253 }
7354 }
7455
75- public static void assertAllInstrumentationIsMuzzled (ClassLoader cl ) throws Exception {
76- for (Instrumenter instrumenter :
77- ServiceLoader .load (Instrumenter .class , MuzzleGradlePlugin .class .getClassLoader ())) {
78- if (instrumenter .getClass ().getName ().endsWith ("TraceConfigInstrumentation" )) {
79- // TraceConfigInstrumentation doesn't do muzzle checks
80- // check on TracerClassInstrumentation instead
81- instrumenter =
82- (Instrumenter )
83- MuzzleGradlePlugin .class
84- .getClassLoader ()
85- .loadClass (instrumenter .getClass ().getName () + "$TracerClassInstrumentation" )
86- .getDeclaredConstructor ()
87- .newInstance ();
88- }
89- Method m = null ;
90- try {
91- m = instrumenter .getClass ().getDeclaredMethod ("getInstrumentationMuzzle" );
92- m .setAccessible (true );
93- ReferenceMatcher muzzle = (ReferenceMatcher ) m .invoke (instrumenter );
94- List <Reference .Mismatch > mismatches = muzzle .getMismatchedReferenceSources (cl );
95- if (mismatches .size () > 0 ) {
96- System .err .println (
97- "FAILED MUZZLE VALIDATION: " + instrumenter .getClass ().getName () + " mismatches:" );
98- for (Reference .Mismatch mismatch : mismatches ) {
99- System .err .println ("-- " + mismatch );
100- }
101- throw new RuntimeException ("Instrumentation failed Muzzle validation" );
102- }
103- } finally {
104- if (null != m ) {
105- m .setAccessible (false );
106- }
56+ private static class RemoveFinalFlagVisitor implements AsmVisitorWrapper {
57+ final String methodName ;
58+
59+ public RemoveFinalFlagVisitor (String methodName ) {
60+ this .methodName = methodName ;
61+ }
62+
63+ @ Override
64+ public int mergeWriter (int flags ) {
65+ return flags ;
66+ }
67+
68+ @ Override
69+ public int mergeReader (int flags ) {
70+ return flags ;
71+ }
72+
73+ @ Override
74+ public ClassVisitor wrap (
75+ TypeDescription instrumentedType ,
76+ ClassVisitor classVisitor ,
77+ Implementation .Context implementationContext ,
78+ TypePool typePool ,
79+ FieldList <FieldDescription .InDefinedShape > fields ,
80+ MethodList <?> methods ,
81+ int writerFlags ,
82+ int readerFlags ) {
83+ return new Visitor (classVisitor );
84+ }
85+
86+ private class Visitor extends ClassVisitor {
87+ public Visitor (ClassVisitor cv ) {
88+ super (Opcodes .ASM6 , cv );
10789 }
108- try {
109- // Ratpack injects the scope manager as a helper.
110- // This is likely a bug, but we'll grandfather it out of the helper checks for now.
111- if (!instrumenter .getClass ().getName ().contains ("Ratpack" )) {
112- // verify helper injector works
113- final String [] helperClassNames = instrumenter .helperClassNames ();
114- if (helperClassNames .length > 0 ) {
115- new HelperInjector (helperClassNames ).transform (null , null , cl , null );
116- }
90+
91+ @ Override
92+ public MethodVisitor visitMethod (
93+ final int access ,
94+ final String name ,
95+ final String descriptor ,
96+ final String signature ,
97+ final String [] exceptions ) {
98+ MethodVisitor methodVisitor =
99+ super .visitMethod (access , name , descriptor , signature , exceptions );
100+ if (name .equals (methodName ) && (access & Opcodes .ACC_FINAL ) != 0 ) {
101+ return super .visitMethod (
102+ access ^ Opcodes .ACC_FINAL , name , descriptor , signature , exceptions );
103+ } else {
104+ return super .visitMethod (access , name , descriptor , signature , exceptions );
117105 }
118- } catch (Exception e ) {
119- System .err .println (
120- "FAILED HELPER INJECTION. Are Helpers being injected in the correct order?" );
121- throw e ;
122106 }
123107 }
124108 }
0 commit comments