1 /**
2  * Copyright (C) 2008 Google Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.google.inject.internal.util;
18 
19 import static com.google.inject.Asserts.assertContains;
20 import static com.google.inject.Asserts.getDeclaringSourcePart;
21 
22 import com.google.inject.AbstractModule;
23 import com.google.inject.CreationException;
24 import com.google.inject.Guice;
25 import com.google.inject.Injector;
26 import com.google.inject.matcher.Matchers;
27 
28 import junit.framework.TestCase;
29 
30 import java.lang.reflect.Modifier;
31 
32 import javax.inject.Inject;
33 
34 /**
35  * @author jessewilson@google.com (Jesse Wilson)
36  */
37 public class LineNumbersTest extends TestCase {
38 
testLineNumbers()39   public void testLineNumbers() {
40     try {
41       Guice.createInjector(new AbstractModule() {
42         protected void configure() {
43           bind(A.class);
44         }
45       });
46       fail();
47     } catch (CreationException expected) {
48       assertContains(expected.getMessage(),
49           "1) No implementation for " + B.class.getName() + " was bound.",
50           "for parameter 0 at " + A.class.getName() + ".<init>(LineNumbersTest.java:",
51           "at " + LineNumbersTest.class.getName(), getDeclaringSourcePart(getClass()));
52     }
53   }
54 
55   static class A {
A(B b)56     @Inject A(B b) {}
57   }
58   public interface B {}
59 
60   /*if[AOP]*/
testCanHandleLineNumbersForGuiceGeneratedClasses()61   public void testCanHandleLineNumbersForGuiceGeneratedClasses() {
62     try {
63       Guice.createInjector(new AbstractModule() {
64         protected void configure() {
65           bindInterceptor(Matchers.only(A.class), Matchers.any(),
66               new org.aopalliance.intercept.MethodInterceptor() {
67                 public Object invoke(org.aopalliance.intercept.MethodInvocation methodInvocation) {
68                   return null;
69                 }
70               });
71 
72           bind(A.class);
73         }
74       });
75       fail();
76     } catch (CreationException expected) {
77       assertContains(expected.getMessage(),
78           "1) No implementation for " + B.class.getName() + " was bound.",
79           "for parameter 0 at " + A.class.getName() + ".<init>(LineNumbersTest.java:",
80           "at " + LineNumbersTest.class.getName(), getDeclaringSourcePart(getClass()));
81     }
82   }
83 
84   static class GeneratingClassLoader extends ClassLoader {
85     static String name = "__generated";
86 
GeneratingClassLoader()87     GeneratingClassLoader() {
88       super(B.class.getClassLoader());
89     }
90 
generate()91     Class<?> generate() {
92       org.objectweb.asm.ClassWriter cw =
93           new org.objectweb.asm.ClassWriter(org.objectweb.asm.ClassWriter.COMPUTE_MAXS);
94       cw.visit(org.objectweb.asm.Opcodes.V1_5,
95           Modifier.PUBLIC, name, null,
96           org.objectweb.asm.Type.getInternalName(Object.class), null);
97 
98       String sig = "("+org.objectweb.asm.Type.getDescriptor(B.class)+")V";
99 
100       org.objectweb.asm.MethodVisitor mv = cw.visitMethod(Modifier.PUBLIC, "<init>", sig, null, null);
101 
102       mv.visitAnnotation(org.objectweb.asm.Type.getDescriptor(Inject.class), true);
103       mv.visitCode();
104       mv.visitVarInsn(org.objectweb.asm.Opcodes.ALOAD, 0);
105       mv.visitMethodInsn(org.objectweb.asm.Opcodes.INVOKESPECIAL,
106           org.objectweb.asm.Type.getInternalName(Object.class), "<init>", "()V" );
107       mv.visitInsn(org.objectweb.asm.Opcodes.RETURN);
108       mv.visitMaxs(0, 0);
109       mv.visitEnd();
110       cw.visitEnd();
111 
112       byte[] buf = cw.toByteArray();
113 
114       return defineClass(name.replace('/', '.'), buf, 0, buf.length);
115     }
116   }
117 
testUnavailableByteCodeShowsUnknownSource()118   public void testUnavailableByteCodeShowsUnknownSource() {
119     try {
120       Guice.createInjector(new AbstractModule() {
121         protected void configure() {
122           bind(new GeneratingClassLoader().generate());
123         }
124       });
125       fail();
126     } catch (CreationException expected) {
127       assertContains(expected.getMessage(),
128           "1) No implementation for " + B.class.getName() + " was bound.",
129           "for parameter 0 at " + GeneratingClassLoader.name + ".<init>(Unknown Source)",
130           "at " + LineNumbersTest.class.getName(), getDeclaringSourcePart(getClass()));
131     }
132   }
133 
testGeneratedClassesCanSucceed()134   public void testGeneratedClassesCanSucceed() {
135     final Class<?> generated = new GeneratingClassLoader().generate();
136     Injector injector = Guice.createInjector(new AbstractModule() {
137       protected void configure() {
138         bind(generated);
139         bind(B.class).toInstance(new B() {});
140       }
141     });
142     Object instance = injector.getInstance(generated);
143     assertEquals(instance.getClass(), generated);
144   }
145   /*end[AOP]*/
146 }
147