View Javadoc

1   // Copyright (C) 2009 - 2012 Philip Aston
2   // All rights reserved.
3   //
4   // This file is part of The Grinder software distribution. Refer to
5   // the file LICENSE which is part of The Grinder distribution for
6   // licensing details. The Grinder distribution is available on the
7   // Internet at http://grinder.sourceforge.net/
8   //
9   // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
10  // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
11  // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
12  // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
13  // COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
14  // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
15  // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
16  // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
17  // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
18  // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
19  // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
20  // OF THE POSSIBILITY OF SUCH DAMAGE.
21  
22  package net.grinder.util.weave.j2se6;
23  
24  import static net.grinder.testutility.AssertUtilities.assertArraysEqual;
25  
26  import java.io.ByteArrayOutputStream;
27  import java.io.IOException;
28  import java.io.ObjectOutputStream;
29  import java.io.Serializable;
30  import java.lang.instrument.ClassFileTransformer;
31  import java.lang.instrument.Instrumentation;
32  import java.lang.reflect.Constructor;
33  import java.lang.reflect.Member;
34  import java.lang.reflect.Method;
35  import java.lang.reflect.Modifier;
36  import java.util.ArrayList;
37  import java.util.HashMap;
38  import java.util.List;
39  import java.util.Map;
40  
41  import junit.framework.TestCase;
42  import net.grinder.testutility.CallData;
43  import net.grinder.testutility.CallRecorder;
44  import net.grinder.testutility.RandomStubFactory;
45  import net.grinder.util.weave.Weaver.TargetSource;
46  import net.grinder.util.weave.WeavingException;
47  import net.grinder.util.weave.agent.ExposeInstrumentation;
48  import net.grinder.util.weave.j2se6.DCRWeaver.ClassFileTransformerFactory;
49  
50  
51  /**
52   * Unit tests for {@link ASMTransformerFactory}.
53   *
54   * @author Philip Aston
55   */
56  public class TestASMTransformerFactory extends TestCase {
57  
58    private final PointCutRegistryStubFactory m_pointCutRegistryStubFactory =
59      new PointCutRegistryStubFactory();
60    private final PointCutRegistry m_pointCutRegistry =
61      m_pointCutRegistryStubFactory.getStub();
62  
63    @Override protected void tearDown() throws Exception {
64      super.tearDown();
65      m_pointCutRegistryStubFactory.clear();
66    }
67  
68    private static final CallRecorder s_callRecorder = new CallRecorder();
69  
70    public void testFactory() throws Exception {
71      final ClassFileTransformerFactory transformerFactory =
72        new ASMTransformerFactory(MyAdvice.class);
73  
74      assertNotNull(transformerFactory.create(m_pointCutRegistry));
75    }
76  
77    public void testFactoryWithBadAdvice() throws Exception {
78      try {
79        new ASMTransformerFactory(BadAdvice1.class);
80        fail("Expected WeavingException");
81      }
82      catch (WeavingException e) {
83        assertTrue(e.getCause() instanceof NoSuchMethodException);
84      }
85  
86      try {
87        new ASMTransformerFactory(BadAdvice2.class);
88        fail("Expected WeavingException");
89      }
90      catch (WeavingException e) {
91        assertTrue(e.getCause() instanceof NoSuchMethodException);
92      }
93  
94      try {
95        new ASMTransformerFactory(BadAdvice3.class);
96        fail("Expected WeavingException");
97      }
98      catch (WeavingException e) {
99      }
100 
101     try {
102       new ASMTransformerFactory(BadAdvice4.class);
103       fail("Expected WeavingException");
104     }
105     catch (WeavingException e) {
106     }
107   }
108 
109   private static final Instrumentation getInstrumentation() {
110     final Instrumentation instrumentation =
111       ExposeInstrumentation.getInstrumentation();
112 
113     assertNotNull(
114       "Instrumentation is not available, " +
115       "please add -javaagent:grinder-agent.jar to the command line",
116       instrumentation);
117 
118     return instrumentation;
119   }
120 
121   public void testWithAgent() throws Exception {
122     final Instrumentation instrumentation = getInstrumentation();
123     assertTrue(instrumentation.isRetransformClassesSupported());
124 
125     final ClassFileTransformerFactory transformerFactory =
126       new ASMTransformerFactory(MyAdvice.class);
127 
128     m_pointCutRegistryStubFactory.addMethod(A.class, "m1", "loc1");
129     m_pointCutRegistryStubFactory.addMethod(A.class, "m2", "loc2");
130     m_pointCutRegistryStubFactory.addMethod(A.class, "m4", "loc4");
131 
132     final ClassFileTransformer transformer =
133       transformerFactory.create(m_pointCutRegistry);
134 
135     final A anotherA = new A();
136     anotherA.m1();
137     s_callRecorder.assertNoMoreCalls();
138 
139     instrumentation.addTransformer(transformer, true);
140     instrumentation.retransformClasses(new Class[] { A.class, A2.class });
141 
142     final A a = new A();
143     assertEquals(1, a.m1());
144 
145     s_callRecorder.assertSuccess("enter", a, "loc1");
146     s_callRecorder.assertSuccess("exit", a, "loc1", true);
147     s_callRecorder.assertNoMoreCalls();
148 
149     anotherA.m1();
150     s_callRecorder.assertSuccess("enter", anotherA, "loc1");
151     s_callRecorder.assertSuccess("exit", anotherA, "loc1", true);
152     s_callRecorder.assertNoMoreCalls();
153 
154     try {
155       a.m2();
156       fail("Expected RuntimeException");
157     }
158     catch (RuntimeException e) {
159     }
160 
161     s_callRecorder.assertSuccess("enter", a, "loc2");
162     s_callRecorder.assertSuccess("exit", a, "loc2", false);
163     s_callRecorder.assertNoMoreCalls();
164 
165     try {
166       a.m4();
167       fail("Expected RuntimeException");
168     }
169     catch (RuntimeException e) {
170     }
171 
172     s_callRecorder.assertSuccess("enter", a, "loc4");
173     s_callRecorder.assertSuccess("enter", a, "loc2");
174     s_callRecorder.assertSuccess("exit", a, "loc2", false);
175     s_callRecorder.assertSuccess("exit", a, "loc4", false);
176     s_callRecorder.assertNoMoreCalls();
177 
178     m_pointCutRegistryStubFactory.addMethod(A.class, "m1", "locX");
179 
180     instrumentation.retransformClasses(new Class[] { A.class, A2.class });
181 
182     a.m1();
183     // We only support more than one advice per method.
184     s_callRecorder.assertSuccess("enter", a, "loc1");
185     s_callRecorder.assertSuccess("enter", a, "locX");
186     s_callRecorder.assertSuccess("exit", a, "locX", true);
187     s_callRecorder.assertSuccess("exit", a, "loc1", true);
188     s_callRecorder.assertNoMoreCalls();
189 
190     instrumentation.removeTransformer(transformer);
191   }
192 
193   public void testTwoTransformations() throws Exception {
194     final Instrumentation instrumentation = getInstrumentation();
195 
196     final ClassFileTransformerFactory transformerFactory =
197       new ASMTransformerFactory(MyAdvice.class);
198 
199     m_pointCutRegistryStubFactory.addMethod(A.class, "m1", "loc1");
200     m_pointCutRegistryStubFactory.addMethod(A.class, "m2", "loc2");
201 
202     final ClassFileTransformer transformer1 =
203       transformerFactory.create(m_pointCutRegistry);
204     final ClassFileTransformer transformer2 =
205       transformerFactory.create(m_pointCutRegistry);
206 
207     instrumentation.addTransformer(transformer1, true);
208     instrumentation.addTransformer(transformer2, true);
209 
210     instrumentation.retransformClasses(new Class[] { A.class, });
211 
212 
213     final A a = new A();
214     assertEquals(1, a.m1());
215 
216     s_callRecorder.assertSuccess("enter", a, "loc1");
217     s_callRecorder.assertSuccess("enter", a, "loc1");
218     s_callRecorder.assertSuccess("exit", a, "loc1", true);
219     s_callRecorder.assertSuccess("exit", a, "loc1", true);
220     s_callRecorder.assertNoMoreCalls();
221 
222     try {
223       a.m2();
224       fail("Expected RuntimeException");
225     }
226     catch (RuntimeException e) {
227     }
228 
229     s_callRecorder.assertSuccess("enter", a, "loc2");
230     s_callRecorder.assertSuccess("enter", a, "loc2");
231     s_callRecorder.assertSuccess("exit", a, "loc2", false);
232     s_callRecorder.assertSuccess("exit", a, "loc2", false);
233     s_callRecorder.assertNoMoreCalls();
234 
235     assertTrue(instrumentation.removeTransformer(transformer2));
236     assertTrue(instrumentation.removeTransformer(transformer1));
237     instrumentation.retransformClasses(new Class[] { A.class, });
238     s_callRecorder.assertNoMoreCalls();
239 
240     instrumentation.removeTransformer(transformer1);
241     instrumentation.removeTransformer(transformer2);
242   }
243 
244   public void testSerializationNotBroken() throws Exception {
245     final Instrumentation instrumentation = getInstrumentation();
246 
247     final ClassFileTransformerFactory transformerFactory =
248       new ASMTransformerFactory(MyAdvice.class);
249 
250     m_pointCutRegistryStubFactory.addMethod(SerializableA.class, "m1", "loc1");
251 
252     final ClassFileTransformer transformer =
253       transformerFactory.create(m_pointCutRegistry);
254 
255     final SerializableA a = new SerializableA();
256 
257     assertEquals(1, a.m1());
258     s_callRecorder.assertNoMoreCalls();
259 
260     final byte[] originalBytes = serialize(a);
261 
262     instrumentation.addTransformer(transformer, true);
263     instrumentation.retransformClasses(new Class[] { SerializableA.class, });
264 
265     assertEquals(1, a.m1());
266 
267     s_callRecorder.assertSuccess("enter", a, "loc1");
268     s_callRecorder.assertSuccess("exit", a, "loc1", true);
269     s_callRecorder.assertNoMoreCalls();
270 
271     final byte[] bytes = serialize(a);
272 
273     assertArraysEqual(originalBytes, bytes);
274 
275     instrumentation.removeTransformer(transformer);
276   }
277 
278   public void testConstructors() throws Exception {
279     final Instrumentation instrumentation = getInstrumentation();
280 
281     final ClassFileTransformerFactory transformerFactory =
282       new ASMTransformerFactory(MyAdvice.class);
283 
284     m_pointCutRegistryStubFactory.addConstructor(
285       A2.class, A2.class.getDeclaredConstructor(Integer.TYPE), "loc1");
286 
287     final ClassFileTransformer transformer =
288       transformerFactory.create(m_pointCutRegistry);
289 
290     new A2(1);
291     s_callRecorder.assertNoMoreCalls();
292 
293     instrumentation.addTransformer(transformer, true);
294     instrumentation.retransformClasses(new Class[] { A2.class, });
295 
296     new A2(1);
297 
298     s_callRecorder.assertSuccess("enter", A2.class, "loc1");
299     s_callRecorder.assertSuccess("exit", A2.class, "loc1", true);
300     s_callRecorder.assertNoMoreCalls();
301 
302     new A3();
303     s_callRecorder.assertNoMoreCalls();
304 
305     m_pointCutRegistryStubFactory.addConstructor(
306       A3.class, A3.class.getDeclaredConstructor(), "loc2");
307     instrumentation.retransformClasses(new Class[] { A3.class, A2.class });
308 
309     new A3();
310 
311     s_callRecorder.assertSuccess("enter", A3.class, "loc2");
312     s_callRecorder.assertSuccess("exit", A3.class, "loc2", true);
313     s_callRecorder.assertNoMoreCalls();
314 
315     instrumentation.removeTransformer(transformer);
316   }
317 
318   public void testOverloading() throws Exception {
319     final Instrumentation instrumentation = getInstrumentation();
320 
321     final ClassFileTransformerFactory transformerFactory =
322       new ASMTransformerFactory(MyAdvice.class);
323 
324     m_pointCutRegistryStubFactory.addConstructor(
325       A4.class, A4.class.getDeclaredConstructor(), "loc1");
326     m_pointCutRegistryStubFactory.addConstructor(
327       A4.class, A4.class.getDeclaredConstructor(String.class), "loc2");
328 
329     m_pointCutRegistryStubFactory.addMethod(
330       A4.class, A4.class.getDeclaredMethod("m1", Integer.TYPE), "loc3");
331     m_pointCutRegistryStubFactory.addMethod(
332       A4.class, A4.class.getDeclaredMethod("m1", String.class), "loc4");
333 
334     final ClassFileTransformer transformer =
335       transformerFactory.create(m_pointCutRegistry);
336 
337     instrumentation.addTransformer(transformer, true);
338     instrumentation.retransformClasses(new Class[] { A4.class, });
339 
340     final A4 a = new A4("abc");
341 
342     s_callRecorder.assertSuccess("enter", A4.class, "loc2");
343     s_callRecorder.assertSuccess("enter", A4.class, "loc1");
344     s_callRecorder.assertSuccess("exit", A4.class, "loc1", true);
345     s_callRecorder.assertSuccess("exit", A4.class, "loc2", true);
346     s_callRecorder.assertNoMoreCalls();
347 
348     a.m1(1);
349 
350     s_callRecorder.assertSuccess("enter", a, "loc3");
351     s_callRecorder.assertSuccess("enter", a, "loc4");
352     s_callRecorder.assertSuccess("exit", a, "loc4", true);
353     s_callRecorder.assertSuccess("exit", a, "loc3", true);
354     s_callRecorder.assertNoMoreCalls();
355 
356     instrumentation.removeTransformer(transformer);
357   }
358 
359   public void testStaticMethods() throws Exception {
360     final Instrumentation instrumentation = getInstrumentation();
361 
362     final ClassFileTransformerFactory transformerFactory =
363       new ASMTransformerFactory(MyAdvice.class);
364 
365     m_pointCutRegistryStubFactory.addMethod(
366       A2.class, A2.class.getDeclaredMethod("m3"), "loc1");
367 
368     final ClassFileTransformer transformer =
369       transformerFactory.create(m_pointCutRegistry);
370 
371     instrumentation.addTransformer(transformer, true);
372     instrumentation.retransformClasses(new Class[] { A2.class, });
373 
374     assertEquals(3, A2.m3());
375 
376     s_callRecorder.assertSuccess("enter", A2.class, "loc1");
377     s_callRecorder.assertSuccess("exit", A2.class, "loc1", true);
378     s_callRecorder.assertNoMoreCalls();
379 
380     instrumentation.removeTransformer(transformer);
381   }
382 
383   public void testVariedByteCode() throws Exception {
384     final Instrumentation instrumentation = getInstrumentation();
385 
386     final ClassFileTransformerFactory transformerFactory =
387       new ASMTransformerFactory(MyAdvice.class);
388 
389     m_pointCutRegistryStubFactory.addConstructor(
390       A5.class, A5.class.getDeclaredConstructor(Integer.TYPE), "loc1");
391 
392     m_pointCutRegistryStubFactory.addMethod(
393       A5.class, A5.class.getDeclaredMethod("m1", Integer.TYPE), "loc3");
394     m_pointCutRegistryStubFactory.addMethod(
395       A5.class, A5.class.getDeclaredMethod("m1", String.class), "loc4");
396     m_pointCutRegistryStubFactory.addMethod(
397       A5.class, A5.class.getDeclaredMethod("m2"), "loc5");
398     m_pointCutRegistryStubFactory.addMethod(
399       A5.class, A5.class.getDeclaredMethod("m3"), "loc6");
400 
401     final ClassFileTransformer transformer =
402       transformerFactory.create(m_pointCutRegistry);
403 
404     instrumentation.addTransformer(transformer, true);
405     instrumentation.retransformClasses(new Class[] { A5.class, });
406 
407     final A5 a = new A5(10);
408 
409     s_callRecorder.assertSuccess("enter", A5.class, "loc1");
410     s_callRecorder.assertSuccess("exit", A5.class, "loc1", true);
411     s_callRecorder.assertNoMoreCalls();
412 
413     assertEquals(-11d, a.m1(1), 0.01d);
414 
415     s_callRecorder.assertSuccess("enter", a, "loc3");
416     s_callRecorder.assertSuccess("enter", a, "loc4");
417     s_callRecorder.assertSuccess("exit", a, "loc4", true);
418     s_callRecorder.assertSuccess("exit", a, "loc3", true);
419     s_callRecorder.assertNoMoreCalls();
420 
421     instrumentation.removeTransformer(transformer);
422   }
423 
424   public void testTargetSource() throws Exception {
425     final Instrumentation instrumentation = getInstrumentation();
426 
427     final ClassFileTransformerFactory transformerFactory =
428       new ASMTransformerFactory(MyAdvice.class);
429 
430     m_pointCutRegistryStubFactory.addMethod(
431       A4.class,
432       A4.class.getDeclaredMethod("m1", String.class),
433       "X",
434       TargetSource.SECOND_PARAMETER);
435 
436     final ClassFileTransformer transformer =
437       transformerFactory.create(m_pointCutRegistry);
438 
439     instrumentation.addTransformer(transformer, true);
440     instrumentation.retransformClasses(new Class[] { A4.class, });
441 
442     final A4 a = new A4();
443 
444     a.m1("Hello");
445 
446     s_callRecorder.assertSuccess("enter", "Hello", "X");
447     s_callRecorder.assertSuccess("exit", "Hello", "X", true);
448     s_callRecorder.assertNoMoreCalls();
449 
450 
451     m_pointCutRegistryStubFactory.addMethod(
452       A4.class,
453       A4.class.getDeclaredMethod("m1", String.class),
454       "Y",
455       TargetSource.FIRST_PARAMETER);
456 
457     instrumentation.retransformClasses(new Class[] { A4.class, });
458 
459     a.m1("Goodbye");
460 
461     s_callRecorder.assertSuccess("enter", "Goodbye", "X");
462     s_callRecorder.assertSuccess("enter", a, "Y");
463     s_callRecorder.assertSuccess("exit", a, "Y", true);
464     s_callRecorder.assertSuccess("exit", "Goodbye", "X", true);
465     s_callRecorder.assertNoMoreCalls();
466 
467     instrumentation.removeTransformer(transformer);
468   }
469 
470   private static final byte[] serialize(final Object a) throws IOException {
471     final ByteArrayOutputStream byteOutputStream =
472       new ByteArrayOutputStream();
473 
474     final ObjectOutputStream objectOutputStream =
475       new ObjectOutputStream(byteOutputStream);
476 
477     objectOutputStream.writeObject(a);
478     objectOutputStream.close();
479 
480     return byteOutputStream.toByteArray();
481   }
482 
483   public static final class A {
484     public int m1() {
485       return 1;
486     }
487 
488     private void m2() {
489       throw new RuntimeException("Test");
490     }
491 
492     public static int m3() {
493       return 2;
494     }
495 
496     public void m4() {
497       m2();
498     }
499   }
500 
501   public static final class A2 {
502     public A2(int x) {
503     }
504 
505     protected int m1() {
506       return 1;
507     }
508 
509     public void m2() {
510     }
511 
512     public static int m3() {
513       return 3;
514     }
515   }
516 
517   public static final class A3 {
518   }
519 
520   public static final class A4 {
521     private A4() {
522     }
523 
524     protected A4(String a) {
525       this();
526     }
527 
528     public void m1(int a) {
529       m1(Integer.toString(a));
530     }
531 
532     private void m1(String a) {
533     }
534   }
535 
536   public static final class A5 {
537     private float m_y;
538 
539     private A5(int x) {
540       ++x;
541 
542       switch (x) {
543         case 99:
544           m_y = x;
545           break;
546         default:
547           m_y = -x;
548       }
549     }
550 
551     public double m1(int a) {
552       final int[][] x = new int[3][2];
553 
554       // Hacked at this until the compile generated LOOKUPSWITCH
555       // instead of TABLESWITCH.
556       switch (a) {
557         case 1: return m1(Integer.toString(a));
558         case 2: return 1;
559         case 30: return 1;
560         case 4: return 1;
561         case -1: return 1;
562         default: return x[0][0];
563       }
564     }
565 
566     private float m1(String a) {
567       return m_y;
568     }
569 
570     long m2() {
571       return 2;
572     }
573 
574     Object m3() {
575       return this;
576     }
577   }
578 
579   public static final class SerializableA implements Serializable {
580     public int m1() {
581       return 1;
582     }
583   }
584 
585   public static final class MyAdvice {
586     private static final Method ENTER_METHOD;
587     private static final Method EXIT_METHOD;
588 
589     static {
590       try {
591         ENTER_METHOD = MyAdvice.class.getMethod(
592           "enter", Object.class, String.class);
593         EXIT_METHOD = MyAdvice.class.getMethod(
594           "exit", Object.class, String.class, Boolean.TYPE);
595       }
596       catch (Exception e) {
597         throw new ExceptionInInitializerError(e);
598       }
599     }
600 
601     public static void enter(Object reference, String location) {
602       s_callRecorder.record(new CallData(ENTER_METHOD,
603                                          null,
604                                          reference,
605                                          location));
606     }
607 
608     public static void exit(Object reference,
609                             String location,
610                             boolean success) {
611 
612       s_callRecorder.record(new CallData(EXIT_METHOD,
613                                          null,
614                                          reference,
615                                          location,
616                                          success));
617     }
618   }
619 
620   public static final class BadAdvice1 {
621   }
622 
623   public static final class BadAdvice2 {
624     public static void enter(Object reference, String location) { }
625 
626     public static void exit(Object reference, String location) { }
627   }
628 
629   public static final class BadAdvice3 {
630     public void enter(Object reference, String location) { }
631 
632     public static void exit(Object reference,
633                             String location,
634                             boolean success) { }
635   }
636 
637   public static final class BadAdvice4 {
638     public static void enter(Object reference, String location) { }
639 
640     public void exit(Object reference, String location, boolean success) { }
641   }
642 
643   public static final class PointCutRegistryStubFactory
644     extends RandomStubFactory<PointCutRegistry> {
645 
646     private final Map<String, Map<Constructor<?>, List<WeavingDetails>>>
647       m_constructors =
648         new HashMap<String, Map<Constructor<?>, List<WeavingDetails>>>();
649 
650     private final Map<String, Map<Method, List<WeavingDetails>>> m_methods =
651       new HashMap<String, Map<Method, List<WeavingDetails>>>();
652 
653     protected PointCutRegistryStubFactory() {
654       super(PointCutRegistry.class);
655     }
656 
657     public void clear() {
658       m_constructors.clear();
659       m_methods.clear();
660     }
661 
662     public Map<Constructor<?>, List<WeavingDetails>>
663       override_getConstructorPointCutsForClass(Object stub, String className) {
664       return m_constructors.get(className);
665     }
666 
667     public Map<Method, List<WeavingDetails>>
668       override_getMethodPointCutsForClass(Object stub, String className) {
669       return m_methods.get(className);
670     }
671 
672     public void addConstructor(Class<?> theClass,
673                                Constructor<?> constructor,
674                                String location) {
675       addMember(theClass, constructor, location, m_constructors, null);
676     }
677 
678     /**
679      * Convenience for methods that have no parameters.
680      */
681     public void addMethod(Class<?> theClass,
682                           String methodName,
683                           String location)
684       throws SecurityException, NoSuchMethodException {
685 
686       addMethod(theClass,
687                 theClass.getDeclaredMethod(methodName),
688                 location);
689     }
690 
691     public void addMethod(Class<?> theClass,
692                           Method method,
693                           String location) {
694       addMethod(theClass, method, location, null);
695     }
696 
697     public void addMethod(Class<?> theClass,
698                           Method method,
699                           String location,
700                           TargetSource source) {
701       addMember(theClass, method, location, m_methods, source);
702     }
703 
704     public <T extends Member> void addMember(
705       Class<?> theClass,
706       T member,
707       String location,
708       Map<String, Map<T, List<WeavingDetails>>> members,
709       TargetSource source) {
710 
711       final String internalClassName = theClass.getName().replace('.', '/');
712 
713       final Map<T, List<WeavingDetails>> forClass;
714 
715       final Map<T, List<WeavingDetails>> existing =
716         members.get(internalClassName);
717 
718       if (existing != null) {
719         forClass = existing;
720       }
721       else {
722         forClass = new HashMap<T, List<WeavingDetails>>();
723         members.put(internalClassName, forClass);
724       }
725 
726       if (source == null) {
727 
728         if (Modifier.isStatic(member.getModifiers()) ||
729             member instanceof Constructor<?>) {
730           source = TargetSource.CLASS;
731         }
732         else {
733           source = TargetSource.FIRST_PARAMETER;
734         }
735       }
736 
737       getList(forClass, member).add(new WeavingDetails(location, source));
738     }
739   }
740 
741   private static <K, V> List<V> getList(Map<K, List<V>> map, K key) {
742 
743     final List<V> existing = map.get(key);
744 
745     if (existing == null) {
746       final List<V> list = new ArrayList<V>();
747       map.put(key, list);
748       return list;
749     }
750     else {
751       return existing;
752     }
753   }
754 }