66package io .opentelemetry .javaagent .instrumentation .internal .classloader ;
77
88import static io .opentelemetry .javaagent .extension .matcher .AgentElementMatchers .extendsClass ;
9+ import static io .opentelemetry .javaagent .instrumentation .internal .classloader .AdviceUtil .applyInlineAdvice ;
10+ import static net .bytebuddy .matcher .ElementMatchers .isMethod ;
11+ import static net .bytebuddy .matcher .ElementMatchers .isProtected ;
12+ import static net .bytebuddy .matcher .ElementMatchers .isPublic ;
13+ import static net .bytebuddy .matcher .ElementMatchers .isStatic ;
914import static net .bytebuddy .matcher .ElementMatchers .named ;
15+ import static net .bytebuddy .matcher .ElementMatchers .not ;
16+ import static net .bytebuddy .matcher .ElementMatchers .takesArgument ;
17+ import static net .bytebuddy .matcher .ElementMatchers .takesArguments ;
1018
1119import io .opentelemetry .javaagent .bootstrap .InjectedClassHelper ;
12- import io .opentelemetry .javaagent .bootstrap .InjectedClassHelper .HelperClassInfo ;
1320import io .opentelemetry .javaagent .extension .instrumentation .TypeInstrumentation ;
1421import io .opentelemetry .javaagent .extension .instrumentation .TypeTransformer ;
15- import io .opentelemetry .javaagent .extension .instrumentation .internal .AsmApi ;
16- import net .bytebuddy .ClassFileVersion ;
17- import net .bytebuddy .asm .AsmVisitorWrapper ;
18- import net .bytebuddy .description .field .FieldDescription ;
19- import net .bytebuddy .description .field .FieldList ;
20- import net .bytebuddy .description .method .MethodList ;
22+ import net .bytebuddy .asm .Advice ;
23+ import net .bytebuddy .asm .Advice .AssignReturned ;
24+ import net .bytebuddy .description .method .MethodDescription ;
2125import net .bytebuddy .description .type .TypeDescription ;
22- import net .bytebuddy .implementation .Implementation ;
2326import net .bytebuddy .matcher .ElementMatcher ;
24- import net .bytebuddy .pool .TypePool ;
25- import org .objectweb .asm .ClassVisitor ;
26- import org .objectweb .asm .ClassWriter ;
27- import org .objectweb .asm .Label ;
28- import org .objectweb .asm .MethodVisitor ;
29- import org .objectweb .asm .Opcodes ;
30- import org .objectweb .asm .Type ;
3127
3228/**
3329 * This instrumentation inserts loading of our injected helper classes at the start of {@code
@@ -42,220 +38,63 @@ public ElementMatcher<TypeDescription> typeMatcher() {
4238
4339 @ Override
4440 public void transform (TypeTransformer transformer ) {
45- transformer .applyTransformer (
46- (builder , typeDescription , classLoader , module , protectionDomain ) ->
47- builder .visit (
48- new AsmVisitorWrapper () {
49- @ Override
50- public int mergeWriter (int flags ) {
51- return flags | ClassWriter .COMPUTE_MAXS ;
52- }
53-
54- @ Override
55- public int mergeReader (int flags ) {
56- return flags ;
57- }
58-
59- @ Override
60- public ClassVisitor wrap (
61- TypeDescription instrumentedType ,
62- ClassVisitor classVisitor ,
63- Implementation .Context implementationContext ,
64- TypePool typePool ,
65- FieldList <FieldDescription .InDefinedShape > fields ,
66- MethodList <?> methods ,
67- int writerFlags ,
68- int readerFlags ) {
69- return new ClassLoaderClassVisitor (classVisitor );
70- }
71- }));
41+ ElementMatcher .Junction <MethodDescription > methodMatcher =
42+ isMethod ()
43+ .and (named ("loadClass" ))
44+ .and (
45+ takesArguments (1 )
46+ .and (takesArgument (0 , String .class ))
47+ .or (
48+ takesArguments (2 )
49+ .and (takesArgument (0 , String .class ))
50+ .and (takesArgument (1 , boolean .class ))))
51+ .and (isPublic ().or (isProtected ()))
52+ .and (not (isStatic ()));
53+ // Inline instrumentation to prevent problems with invokedynamic-recursion
54+ applyInlineAdvice (transformer , methodMatcher , this .getClass ().getName () + "$LoadClassAdvice" );
7255 }
7356
74- private static class ClassLoaderClassVisitor extends ClassVisitor implements Opcodes {
75- private String internalClassName ;
76- private boolean frames ;
77-
78- ClassLoaderClassVisitor (ClassVisitor classVisitor ) {
79- super (AsmApi .VERSION , classVisitor );
80- }
81-
82- @ Override
83- public void visit (
84- int version ,
85- int access ,
86- String name ,
87- String signature ,
88- String superName ,
89- String [] interfaces ) {
90- super .visit (version , access , name , signature , superName , interfaces );
91- internalClassName = name ;
92- frames = ClassFileVersion .ofMinorMajor (version ).isAtLeast (ClassFileVersion .JAVA_V6 );
93- }
94-
95- @ Override
96- public MethodVisitor visitMethod (
97- int access , String name , String descriptor , String signature , String [] exceptions ) {
98- MethodVisitor mv = super .visitMethod (access , name , descriptor , signature , exceptions );
99- boolean loadClassMethod =
100- "loadClass" .equals (name )
101- && ("(Ljava/lang/String;)Ljava/lang/Class;" .equals (descriptor )
102- || "(Ljava/lang/String;Z)Ljava/lang/Class;" .equals (descriptor ));
103- if (!loadClassMethod ) {
104- return mv ;
105- }
106-
107- int argumentCount = Type .getArgumentTypes (descriptor ).length ;
108- return new MethodVisitor (api , mv ) {
109- @ Override
110- public void visitCode () {
111- super .visitCode ();
112-
113- // inserts the following at the start of the loadClass method:
114- /*
115- InjectedClassHelper.HelperClassInfo helperClassInfo = InjectedClassHelper.getHelperClassInfo(this, name);
116- if (helperClassInfo != null) {
117- Class<?> clazz = findLoadedClass(name);
118- if (clazz != null) {
119- return clazz;
120- }
121- try {
122- byte[] bytes = helperClassInfo.getClassBytes();
123- return defineClass(name, bytes, 0, bytes.length, helperClassInfo.getProtectionDomain());
124- } catch (LinkageError error) {
125- clazz = findLoadedClass(name);
126- if (clazz != null) {
127- return clazz;
128- }
129- throw error;
130- }
131- }
132- */
133-
134- Label startTry = new Label ();
135- Label endTry = new Label ();
136- Label handler = new Label ();
137- mv .visitTryCatchBlock (startTry , endTry , handler , "java/lang/LinkageError" );
138- // InjectedClassHelper.HelperClassInfo helperClassInfo = InjectedClassHelper
139- // .getHelperClassInfo(this, name);
140- mv .visitVarInsn (ALOAD , 0 );
141- mv .visitVarInsn (ALOAD , 1 );
142- mv .visitMethodInsn (
143- INVOKESTATIC ,
144- Type .getInternalName (InjectedClassHelper .class ),
145- "getHelperClassInfo" ,
146- "(Ljava/lang/ClassLoader;Ljava/lang/String;)"
147- + Type .getDescriptor (HelperClassInfo .class ),
148- false );
149- mv .visitVarInsn (ASTORE , argumentCount + 1 ); // store helperClassInfo
150- mv .visitVarInsn (ALOAD , argumentCount + 1 );
151- Label notHelperClass = new Label ();
152- mv .visitJumpInsn (IFNULL , notHelperClass );
153-
154- // getHelperClassInfo returned non-null
155- // Class<?> clazz = findLoadedClass(name);
156- mv .visitVarInsn (ALOAD , 0 );
157- mv .visitVarInsn (ALOAD , 1 );
158- mv .visitMethodInsn (
159- INVOKEVIRTUAL ,
160- internalClassName ,
161- "findLoadedClass" ,
162- "(Ljava/lang/String;)Ljava/lang/Class;" ,
163- false );
164- mv .visitVarInsn (ASTORE , argumentCount + 2 ); // store clazz
165- mv .visitVarInsn (ALOAD , argumentCount + 2 );
166- mv .visitJumpInsn (IFNULL , startTry );
167-
168- // findLoadedClass returned non-null
169- // return clazz
170- mv .visitVarInsn (ALOAD , argumentCount + 2 );
171- mv .visitInsn (ARETURN );
172-
173- mv .visitLabel (startTry );
174- if (frames ) {
175- mv .visitFrame (
176- Opcodes .F_APPEND ,
177- 2 ,
178- new Object [] {Type .getInternalName (HelperClassInfo .class ), "java/lang/Class" },
179- 0 ,
180- null );
181- }
182- // byte[] bytes = helperClassInfo.getClassBytes();
183- mv .visitVarInsn (ALOAD , argumentCount + 1 );
184- mv .visitMethodInsn (
185- INVOKEINTERFACE ,
186- Type .getInternalName (HelperClassInfo .class ),
187- "getClassBytes" ,
188- "()[B" ,
189- true );
190- mv .visitVarInsn (ASTORE , argumentCount + 3 ); // store bytes
191-
192- // return defineClass(name, bytes, 0, bytes.length,
193- // helperClassInfo.getProtectionDomain());
194- mv .visitVarInsn (ALOAD , 0 );
195- mv .visitVarInsn (ALOAD , 1 );
196- mv .visitVarInsn (ALOAD , argumentCount + 3 );
197- mv .visitInsn (ICONST_0 );
198- mv .visitVarInsn (ALOAD , argumentCount + 3 );
199- mv .visitInsn (ARRAYLENGTH );
200- mv .visitVarInsn (ALOAD , argumentCount + 1 );
201- mv .visitMethodInsn (
202- INVOKEINTERFACE ,
203- Type .getInternalName (HelperClassInfo .class ),
204- "getProtectionDomain" ,
205- "()Ljava/security/ProtectionDomain;" ,
206- true );
207- mv .visitMethodInsn (
208- INVOKEVIRTUAL ,
209- internalClassName ,
210- "defineClass" ,
211- "(Ljava/lang/String;[BIILjava/security/ProtectionDomain;)Ljava/lang/Class;" ,
212- false );
213- mv .visitLabel (endTry );
214- mv .visitInsn (ARETURN );
215-
216- mv .visitLabel (handler );
217- if (frames ) {
218- mv .visitFrame (Opcodes .F_SAME1 , 0 , null , 1 , new Object [] {"java/lang/LinkageError" });
219- }
220- mv .visitVarInsn (ASTORE , argumentCount + 3 ); // store LinkageError
221- // clazz = findLoadedClass(name);
222- mv .visitVarInsn (ALOAD , 0 );
223- mv .visitVarInsn (ALOAD , 1 );
224- mv .visitMethodInsn (
225- INVOKEVIRTUAL ,
226- internalClassName ,
227- "findLoadedClass" ,
228- "(Ljava/lang/String;)Ljava/lang/Class;" ,
229- false );
230- mv .visitVarInsn (ASTORE , argumentCount + 2 ); // score clazz
231- mv .visitVarInsn (ALOAD , argumentCount + 2 );
232- Label throwError = new Label ();
233- mv .visitJumpInsn (IFNULL , throwError );
234- // return clazz
235- mv .visitVarInsn (ALOAD , argumentCount + 2 );
236- mv .visitInsn (ARETURN );
237- mv .visitLabel (throwError );
238- if (frames ) {
239- mv .visitFrame (Opcodes .F_APPEND , 1 , new Object [] {"java/lang/LinkageError" }, 0 , null );
240- }
241- // throw error
242- mv .visitVarInsn (ALOAD , argumentCount + 3 );
243- mv .visitInsn (ATHROW );
244-
245- mv .visitLabel (notHelperClass );
246- if (frames ) {
247- mv .visitFrame (Opcodes .F_CHOP , 3 , null , 0 , null );
248- // ensure there aren't two frames at the same location
249- mv .visitInsn (NOP );
57+ @ SuppressWarnings ("unused" )
58+ public static class LoadClassAdvice {
59+
60+ // Class loader stub is shaded back to the real class loader class. It is used to call protected
61+ // method from the advice that the complier won't let us call directly. During runtime it is
62+ // fine since this code is inlined into subclasses of ClassLoader that can call protected
63+ // methods.
64+ @ Advice .OnMethodEnter (skipOn = Advice .OnNonDefaultValue .class )
65+ public static Class <?> onEnter (
66+ @ Advice .This java .lang .ClassLoader classLoader ,
67+ @ Advice .This
68+ io .opentelemetry .javaagent .instrumentation .internal .classloader .stub .ClassLoader
69+ classLoaderStub ,
70+ @ Advice .Argument (0 ) String name ) {
71+ InjectedClassHelper .HelperClassInfo helperClassInfo =
72+ InjectedClassHelper .getHelperClassInfo (classLoader , name );
73+ if (helperClassInfo != null ) {
74+ Class <?> clazz = classLoaderStub .findLoadedClass (name );
75+ if (clazz != null ) {
76+ return clazz ;
77+ }
78+ try {
79+ byte [] bytes = helperClassInfo .getClassBytes ();
80+ return classLoaderStub .defineClass (
81+ name , bytes , 0 , bytes .length , helperClassInfo .getProtectionDomain ());
82+ } catch (LinkageError error ) {
83+ clazz = classLoaderStub .findLoadedClass (name );
84+ if (clazz != null ) {
85+ return clazz ;
25086 }
87+ throw error ;
25188 }
89+ }
90+ return null ;
91+ }
25292
253- @ Override
254- public void visitMaxs (int maxStack , int maxLocals ) {
255- // minimally we have argumentCount parameters + this + 3 locals added by us
256- super .visitMaxs (maxStack , Math .max (maxLocals , argumentCount + 1 + 3 ));
257- }
258- };
93+ @ AssignReturned .ToReturned
94+ @ Advice .OnMethodExit (onThrowable = Throwable .class )
95+ public static Class <?> onExit (
96+ @ Advice .Return Class <?> originalResult , @ Advice .Enter Class <?> loadedClass ) {
97+ return loadedClass != null ? loadedClass : originalResult ;
25998 }
26099 }
261100}
0 commit comments