001: /**
002: * Copyright (C) 2006 Google Inc.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */package com.bm.ejb3guice.internal;
016:
017: import java.io.IOException;
018: import java.io.InputStream;
019: import java.lang.reflect.Constructor;
020: import java.lang.reflect.Field;
021: import java.lang.reflect.Member;
022: import java.lang.reflect.Method;
023: import java.util.HashMap;
024: import java.util.Map;
025:
026: import org.ejb3unit.asm.AnnotationVisitor;
027: import org.ejb3unit.asm.FieldVisitor;
028: import org.ejb3unit.asm.MethodVisitor;
029: import org.ejb3unit.asm.Opcodes;
030: import org.ejb3unit.asm.commons.EmptyVisitor;
031: import org.objectweb.asm.Attribute;
032: import org.objectweb.asm.ClassReader;
033: import org.objectweb.asm.ClassVisitor;
034: import org.objectweb.asm.CodeVisitor;
035: import org.objectweb.asm.Label;
036: import org.objectweb.asm.Type;
037:
038: /**
039: * Looks up line numbers for classes and their members.
040: *
041: * @author Chris Nokleberg
042: */
043: public class LineNumbers {
044:
045: private final Class cls;
046:
047: private Map<String, Integer> lines = new HashMap<String, Integer>();
048:
049: private String source;
050:
051: private int firstLine = Integer.MAX_VALUE;
052:
053: /**
054: * Reads line number information from the given class, if available.
055: *
056: * @param cls
057: * the class to read line number information from
058: * @throws IllegalArgumentException
059: * if the bytecode for the class cannot be found
060: * @throws IOException
061: * if an error occurs while reading bytecode
062: */
063: public LineNumbers(Class cls) throws IOException {
064: this .cls = cls;
065: InputStream in = cls.getResourceAsStream("/"
066: + cls.getName().replace('.', '/') + ".class");
067: if (in == null) {
068: throw new IllegalArgumentException(
069: "Cannot find bytecode for " + cls);
070: }
071: new ClassReader(in).accept(new LineNumberReader(), false);
072: }
073:
074: /**
075: * Get the source file name as read from the bytecode.
076: *
077: * @return the source file name if available, or null
078: */
079: public String getSource() {
080: return source;
081: }
082:
083: /**
084: * Get the line number associated with the given member.
085: *
086: * @param member
087: * a field, constructor, or method belonging to the class used
088: * during construction
089: * @return the wrapped line number, or null if not available
090: * @throws IllegalArgumentException
091: * if the member does not belong to the class used during
092: * construction
093: */
094: public Integer getLineNumber(Member member) {
095: if (!cls.equals(member.getDeclaringClass())) {
096: throw new IllegalArgumentException("Member " + member
097: + " belongs to " + member.getDeclaringClass()
098: + ", not " + cls);
099: }
100: return lines.get(getKey(member));
101: }
102:
103: /**
104: * Gets the first line number.
105: */
106: public int getFirstLine() {
107: return firstLine == Integer.MAX_VALUE ? 1 : firstLine;
108: }
109:
110: private static String getKey(Member member) {
111: if (member instanceof Field) {
112: return member.getName();
113: } else if (member instanceof Method) {
114: return member.getName()
115: + Type.getMethodDescriptor((Method) member);
116: } else {
117: return "<init>"
118: + getConstructorDescriptor((Constructor) member);
119: }
120: }
121:
122: public static String getConstructorDescriptor(Constructor c) {
123: StringBuilder sb = new StringBuilder();
124: sb.append('(');
125: for (Class param : c.getParameterTypes())
126: sb.append(Type.getDescriptor(param));
127: return sb.append(")V").toString();
128: }
129:
130: private class LineNumberReader extends EmptyVisitor implements
131: ClassVisitor, MethodVisitor, AnnotationVisitor {
132:
133: private int line = -1;
134:
135: private String pendingMethod;
136:
137: private String name;
138:
139: public void visit(int version, int access, String name,
140: String signature, String super Name, String[] interfaces) {
141: this .name = name;
142: }
143:
144: public MethodVisitor visitMethod(int access, String name,
145: String desc, String signature, String[] exceptions) {
146: if ((access & Opcodes.ACC_PRIVATE) != 0) {
147: return null;
148: }
149: pendingMethod = name + desc;
150: line = -1;
151: return this ;
152: }
153:
154: public void visitSource(String source, String debug) {
155: LineNumbers.this .source = source;
156: }
157:
158: public void visitLineNumber(int line, Label start) {
159: if (line < firstLine) {
160: firstLine = line;
161: }
162:
163: this .line = line;
164: if (pendingMethod != null) {
165: lines.put(pendingMethod, line);
166: pendingMethod = null;
167: }
168: }
169:
170: public void visitFieldInsn(int opcode, String owner,
171: String name, String desc) {
172: if (opcode == Opcodes.PUTFIELD && this .name.equals(owner)
173: && !lines.containsKey(name) && line != -1) {
174: lines.put(name, line);
175: }
176: }
177:
178: public void visitEnd() {
179: }
180:
181: public void visitInnerClass(String name, String outerName,
182: String innerName, int access) {
183: }
184:
185: public void visitOuterClass(String owner, String name,
186: String desc) {
187: }
188:
189: public void visitAttribute(Attribute attr) {
190: }
191:
192: public FieldVisitor visitField(int access, String name,
193: String desc, String signature, Object value) {
194: return null;
195: }
196:
197: public AnnotationVisitor visitAnnotation(String desc,
198: boolean visible) {
199: return this ;
200: }
201:
202: public AnnotationVisitor visitAnnotation(String name,
203: String desc) {
204: return this ;
205: }
206:
207: public AnnotationVisitor visitAnnotationDefault() {
208: return this ;
209: }
210:
211: public AnnotationVisitor visitParameterAnnotation(
212: int parameter, String desc, boolean visible) {
213: return this ;
214: }
215:
216: public AnnotationVisitor visitArray(String name) {
217: return this ;
218: }
219:
220: public void visitEnum(String name, String desc, String value) {
221: }
222:
223: public void visit(String name, Object value) {
224: }
225:
226: public void visitCode() {
227: }
228:
229: public void visitFrame(int type, int nLocal, Object[] local,
230: int nStack, Object[] stack) {
231: }
232:
233: public void visitIincInsn(int var, int increment) {
234: }
235:
236: public void visitInsn(int opcode) {
237: }
238:
239: public void visitIntInsn(int opcode, int operand) {
240: }
241:
242: public void visitJumpInsn(int opcode, Label label) {
243: }
244:
245: public void visitLabel(Label label) {
246: }
247:
248: public void visitLdcInsn(Object cst) {
249: }
250:
251: public void visitLocalVariable(String name, String desc,
252: String signature, Label start, Label end, int index) {
253: }
254:
255: public void visitLookupSwitchInsn(Label dflt, int[] keys,
256: Label[] labels) {
257: }
258:
259: public void visitMaxs(int maxStack, int maxLocals) {
260: }
261:
262: public void visitMethodInsn(int opcode, String owner,
263: String name, String desc) {
264: }
265:
266: public void visitMultiANewArrayInsn(String desc, int dims) {
267: }
268:
269: public void visitTableSwitchInsn(int min, int max, Label dflt,
270: Label[] labels) {
271: }
272:
273: public void visitTryCatchBlock(Label start, Label end,
274: Label handler, String type) {
275: }
276:
277: public void visitTypeInsn(int opcode, String desc) {
278: }
279:
280: public void visitVarInsn(int opcode, int var) {
281: }
282:
283: public void visit(int arg0, int arg1, String arg2, String arg3,
284: String[] arg4, String arg5) {
285:
286: }
287:
288: public void visitField(int arg0, String arg1, String arg2,
289: Object arg3, Attribute arg4) {
290:
291: }
292:
293: public CodeVisitor visitMethod(int arg0, String arg1,
294: String arg2, String[] arg3, Attribute arg4) {
295: return null;
296: }
297: }
298: }
|