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.google.inject.util;
016:
017: import java.io.IOException;
018: import java.io.InputStream;
019: import java.lang.reflect.*;
020: import java.util.HashMap;
021: import java.util.Map;
022: import org.objectweb.asm.*;
023: import org.objectweb.asm.Type;
024:
025: /**
026: * Looks up line numbers for classes and their members.
027: *
028: * @author Chris Nokleberg
029: */
030: public class LineNumbers {
031:
032: private final Class cls;
033: private Map<String, Integer> lines = new HashMap<String, Integer>();
034: private String source;
035: private int firstLine = Integer.MAX_VALUE;
036:
037: /**
038: * Reads line number information from the given class, if available.
039: *
040: * @param cls the class to read line number information from
041: * @throws IllegalArgumentException if the bytecode for the class cannot be
042: * found
043: * @throws IOException if an error occurs while reading bytecode
044: */
045: public LineNumbers(Class cls) throws IOException {
046: this .cls = cls;
047: InputStream in = cls.getResourceAsStream("/"
048: + cls.getName().replace('.', '/') + ".class");
049: if (in == null) {
050: throw new IllegalArgumentException(
051: "Cannot find bytecode for " + cls);
052: }
053: new ClassReader(in).accept(new LineNumberReader(), 0);
054: }
055:
056: /**
057: * Get the source file name as read from the bytecode.
058: *
059: * @return the source file name if available, or null
060: */
061: public String getSource() {
062: return source;
063: }
064:
065: /**
066: * Get the line number associated with the given member.
067: *
068: * @param member a field, constructor, or method belonging to the class used
069: * during construction
070: * @return the wrapped line number, or null if not available
071: * @throws IllegalArgumentException if the member does not belong to the
072: * class used during construction
073: */
074: public Integer getLineNumber(Member member) {
075: if (!cls.equals(member.getDeclaringClass())) {
076: throw new IllegalArgumentException("Member " + member
077: + " belongs to " + member.getDeclaringClass()
078: + ", not " + cls);
079: }
080: return lines.get(getKey(member));
081: }
082:
083: /**
084: * Gets the first line number.
085: */
086: public int getFirstLine() {
087: return firstLine == Integer.MAX_VALUE ? 1 : firstLine;
088: }
089:
090: private static String getKey(Member member) {
091: if (member instanceof Field) {
092: return member.getName();
093: } else if (member instanceof Method) {
094: return member.getName()
095: + Type.getMethodDescriptor((Method) member);
096: } else {
097: return "<init>"
098: + Type
099: .getConstructorDescriptor((Constructor) member);
100: }
101: }
102:
103: private class LineNumberReader implements ClassVisitor,
104: MethodVisitor {
105:
106: private int line = -1;
107: private String pendingMethod;
108: private String name;
109:
110: public void visit(int version, int access, String name,
111: String signature, String super Name, String[] interfaces) {
112: this .name = name;
113: }
114:
115: public MethodVisitor visitMethod(int access, String name,
116: String desc, String signature, String[] exceptions) {
117: if ((access & Opcodes.ACC_PRIVATE) != 0) {
118: return null;
119: }
120: pendingMethod = name + desc;
121: line = -1;
122: return this ;
123: }
124:
125: public void visitSource(String source, String debug) {
126: LineNumbers.this .source = source;
127: }
128:
129: public void visitLineNumber(int line, Label start) {
130: if (line < firstLine) {
131: firstLine = line;
132: }
133:
134: this .line = line;
135: if (pendingMethod != null) {
136: lines.put(pendingMethod, line);
137: pendingMethod = null;
138: }
139: }
140:
141: public void visitFieldInsn(int opcode, String owner,
142: String name, String desc) {
143: if (opcode == Opcodes.PUTFIELD && this .name.equals(owner)
144: && !lines.containsKey(name) && line != -1) {
145: lines.put(name, line);
146: }
147: }
148:
149: public void visitEnd() {
150: }
151:
152: public void visitInnerClass(String name, String outerName,
153: String innerName, int access) {
154: }
155:
156: public void visitOuterClass(String owner, String name,
157: String desc) {
158: }
159:
160: public void visitAttribute(Attribute attr) {
161: }
162:
163: public FieldVisitor visitField(int access, String name,
164: String desc, String signature, Object value) {
165: return null;
166: }
167:
168: public AnnotationVisitor visitAnnotation(String desc,
169: boolean visible) {
170: return null;
171: }
172:
173: public AnnotationVisitor visitAnnotationDefault() {
174: return null;
175: }
176:
177: public AnnotationVisitor visitParameterAnnotation(
178: int parameter, String desc, boolean visible) {
179: return null;
180: }
181:
182: public void visitCode() {
183: }
184:
185: public void visitFrame(int type, int nLocal, Object[] local,
186: int nStack, Object[] stack) {
187: }
188:
189: public void visitIincInsn(int var, int increment) {
190: }
191:
192: public void visitInsn(int opcode) {
193: }
194:
195: public void visitIntInsn(int opcode, int operand) {
196: }
197:
198: public void visitJumpInsn(int opcode, Label label) {
199: }
200:
201: public void visitLabel(Label label) {
202: }
203:
204: public void visitLdcInsn(Object cst) {
205: }
206:
207: public void visitLocalVariable(String name, String desc,
208: String signature, Label start, Label end, int index) {
209: }
210:
211: public void visitLookupSwitchInsn(Label dflt, int[] keys,
212: Label[] labels) {
213: }
214:
215: public void visitMaxs(int maxStack, int maxLocals) {
216: }
217:
218: public void visitMethodInsn(int opcode, String owner,
219: String name, String desc) {
220: }
221:
222: public void visitMultiANewArrayInsn(String desc, int dims) {
223: }
224:
225: public void visitTableSwitchInsn(int min, int max, Label dflt,
226: Label[] labels) {
227: }
228:
229: public void visitTryCatchBlock(Label start, Label end,
230: Label handler, String type) {
231: }
232:
233: public void visitTypeInsn(int opcode, String desc) {
234: }
235:
236: public void visitVarInsn(int opcode, int var) {
237: }
238: }
239: }
|