| /** |
| * Copyright (C) 2006 Google Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.google.inject.util; |
| |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.lang.reflect.*; |
| import java.util.HashMap; |
| import java.util.Map; |
| import org.objectweb.asm.*; |
| import org.objectweb.asm.Type; |
| |
| public class LineNumbers |
| { |
| private final Class cls; |
| private Map<String,Integer> lines = new HashMap<String,Integer>(); |
| private String source; |
| |
| /** |
| * Reads line number information from the given class, if available. |
| * @throws IllegalArgumentException if the bytecode for the class cannot be found |
| */ |
| public LineNumbers(Class cls) throws IOException { |
| this.cls = cls; |
| InputStream in = cls.getResourceAsStream("/" + cls.getName().replace('.', '/') + ".class"); |
| if (in == null) |
| throw new IllegalArgumentException("Cannot find bytecode for " + cls); |
| new ClassReader(in).accept(new LineNumberReader(), 0); |
| } |
| |
| /** |
| * Get the source file name as read from the bytecode. |
| * @return the source file name if available, or null |
| */ |
| public String getSource() { |
| return source; |
| } |
| |
| /** |
| * Get the line number associated with the given member. |
| * @param member a field, constructor, or method belonging to the class used during construction |
| * @return the wrapped line number, or null if not available |
| * @throws IllegalArgumentException if the member does not belong to the class used during construction |
| */ |
| public Integer getLineNumber(Member member) { |
| if (!cls.equals(member.getDeclaringClass())) |
| throw new IllegalArgumentException("Member " + member + " belongs to " + member.getDeclaringClass() + ", not " + cls); |
| return lines.get(getKey(member)); |
| } |
| |
| private static String getKey(Member member) { |
| if (member instanceof Field) { |
| return member.getName(); |
| } else if (member instanceof Method) { |
| return member.getName() + Type.getMethodDescriptor((Method)member); |
| } else { |
| return "<init>" + Type.getConstructorDescriptor((Constructor)member); |
| } |
| } |
| |
| private class LineNumberReader implements ClassVisitor, MethodVisitor |
| { |
| private int line = -1; |
| private String pendingMethod; |
| private String name; |
| private boolean inStatic; |
| |
| public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { |
| this.name = name; |
| } |
| |
| public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { |
| if ((access & Opcodes.ACC_PRIVATE) != 0) |
| return null; |
| inStatic = (access & Opcodes.ACC_STATIC) != 0; |
| pendingMethod = name + desc; |
| line = -1; |
| return this; |
| } |
| |
| public void visitSource(String source, String debug) { |
| LineNumbers.this.source = source; |
| } |
| |
| public void visitLineNumber(int line, Label start) { |
| this.line = line; |
| if (pendingMethod != null) { |
| lines.put(pendingMethod, line); |
| pendingMethod = null; |
| } |
| } |
| |
| public void visitFieldInsn(int opcode, String owner, String name, String desc) { |
| if (opcode == Opcodes.PUTFIELD && this.name.equals(owner) && !lines.containsKey(name) && line != -1) |
| lines.put(name, line); |
| } |
| |
| public void visitEnd() { } |
| public void visitInnerClass(String name, String outerName, String innerName, int access) { } |
| public void visitOuterClass(String owner, String name, String desc) { } |
| public void visitAttribute(Attribute attr) { } |
| |
| public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) { return null; } |
| public AnnotationVisitor visitAnnotation(String desc, boolean visible) { return null; } |
| public AnnotationVisitor visitAnnotationDefault() { return null; } |
| public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) { return null; } |
| |
| public void visitCode() { } |
| public void visitFrame(int type, int nLocal, Object[] local, int nStack, Object[] stack) { } |
| public void visitIincInsn(int var, int increment) { } |
| public void visitInsn(int opcode) { } |
| public void visitIntInsn(int opcode, int operand) { } |
| public void visitJumpInsn(int opcode, Label label) { } |
| public void visitLabel(Label label) { } |
| public void visitLdcInsn(Object cst) { } |
| public void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index) { } |
| public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) { } |
| public void visitMaxs(int maxStack, int maxLocals) { } |
| public void visitMethodInsn(int opcode, String owner, String name, String desc) { } |
| public void visitMultiANewArrayInsn(String desc, int dims) { } |
| public void visitTableSwitchInsn(int min, int max, Label dflt, Label[] labels) { } |
| public void visitTryCatchBlock(Label start, Label end, Label handler, String type) { } |
| public void visitTypeInsn(int opcode, String desc) { } |
| public void visitVarInsn(int opcode, int var) { } |
| } |
| } |