21

Is it possible to get the line number of an method using reflection or other magic?
It is possible if the method is inside the current Stacktrace. Using Thread.currentThread().getStackTrace(), one can get the line number of an StackTraceElement. But what can I do if I only got the java.lang.reflect.Method Object?

I found this, for classes-> How to get source file-name/line-number from a java.lang.Class object but it's not useful for methods.

Community
  • 1
  • 1
Chriss
  • 5,157
  • 7
  • 41
  • 75

3 Answers3

20

I wanted to do the same thing and after some research settled on javassist. You will need to add javassist (I used version 3.15.0-GA).

Given the following class determine the location of the "x" method. The method name "x" is hard coded however if you are in the same boat as me, the reflection isn't hard so I'm confident you can get a list of method names, then the following will let you get the line numbers of the methods:

public class Widget {
    void x(){System.out.println("I'm x\n");}
    //comment added to create space
    void y(){System.out.println("I'm y\n");} 
}

import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.NotFoundException;

public class App {
    public static void main(String[] args) throws NotFoundException {
        System.out.println("Get method line number with javassist\n");
        ClassPool pool = ClassPool.getDefault();
        CtClass cc = pool.get("com.quaternion.demo.Widget");
        CtMethod methodX = cc.getDeclaredMethod("x");
        int xlineNumber = methodX.getMethodInfo().getLineNumber(0);
        System.out.println("method x is on line " + xlineNumber + "\n");
    }
}

Output: method x is on line 12 which in my case is accurate I cut out some comments...

Note: As mentioned by Pete83 in the comments, this method actually returns the first line of code in the method and not the line which declares the method. This usually won't be an issue as most will probably want to establish relative position (the order in which they were declared) and use this information for your own conventions. This would come up any time you felt the need to include an ordinal value within an annotation which could be readily determined by the position within the code itself.


For quick reference Maven coordinates for javassist:
<dependency>
   <groupId>org.javassist</groupId> <!-- if a version prior to 3.13.0-GA is needed use "javassist" and not "org.javassist" -->
   <artifactId>javassist</artifactId>
   <version>3.15.0-GA</version>
</dependency>
Quaternion
  • 10,380
  • 6
  • 51
  • 102
  • Sound like a good idea! I hope it still works if multipe class loaders involved. I will tried it when i find my use case again... – Chriss Feb 27 '13 at 16:14
  • 1
    In my case I wanted to output ORM entity information in the order presented in the Entity objects. Seems reasonable that the order they appear in the Entity would be a good order to use for a table showing that entity. For that purpose I found all the getters and loaded them in to a TreeMap with the integer being the line number and the String being the getter (actually because of how I was going to fetch the values with EL I processed the getters into the property names, which is remove the "get" and lowercase the first char). Because I wanted this process to be fast... – Quaternion Feb 27 '13 at 18:21
  • A Map of entity classes is created at startup, which maps against an arraylist of these now ordered properties, so I can use this entityFormat object, without much overhead. I'd read about someone else wanting to do this and they were advised to use annotations, but I think annotations are best used to override conventions. With this done, it works pretty well I think annotations on every entity class that specified an ordinal would be really annoying. – Quaternion Feb 27 '13 at 18:25
  • @Quaternion Is line number information always available or does the class file need to be compiled in a special way? If the line number information is not available, I was hoping it would still return some number that correctly reflects the declaration order of the fields/methods. – geert3 Jun 25 '15 at 09:21
  • It has always been available during my standard builds (I don't do anything special), I think I read that this information could be stripped from a file but never found reason to look into this. Also I'm not sure when this information became standard, I'm sure it has not always been this way. I currently work with Java 8. – Quaternion Jun 25 '15 at 23:36
  • Getting a -1 as the output from `getLineNumber(0)`. JavaDocs says "-1 if this information is not available.". Can I do anything about that? – okrunner May 27 '16 at 23:47
  • 2
    It's probably worth mentioning that I ran into a `javassist.NotFoundException` which was solved by adding : `ClassClassPath ccpath = new ClassClassPath(aJavaPipeline); pool.insertClassPath(ccpath);` – okrunner May 28 '16 at 00:03
  • I think this does not really print the line number of the method though, but the first line within the method? Which also makes sense, since the line number of a method declaration is not really contained within the bytecode, so a best guess can only be, to take the first line number within the method and subtract 1, which of course is wrong, if the first line is a comment... Try `void foo()`\n `{`\n `// funny comment `\n`System.out.println("foo");`\n`}` In my case the above solution does not print out the line number of the declaration (even without the comment). – pete83 Dec 30 '17 at 13:51
  • @peate83 you are correct, I've tested and it actually points at the first line in the method, however for all the use-cases I can think of (using method order at run time for the use of conventions, perhaps determining execution order or display order) it is the relative order of the method that is needed and not their absolute value. The only reason I could see for needing the true value would be for some sort of tooling purpose... in that case I suppose you would be looking at some sort of build plugin using a lexical analyser of some sort to store the data you need in a file. – Quaternion Jan 23 '18 at 23:17
  • @Quaternion is it possible to get end line number of a method with javaassit, I have used it to get start line number. Still confused with `getLineNumber(pos)` what is this position represent ? – Yachitha Sandaruwan Feb 11 '19 at 07:32
  • 1
    @YachithaSandaruwan I think it's the offset into the method, 0 being the first line of the method... if you specify an out of range index then it returns -1. See: http://www.javassist.org/html/javassist/bytecode/MethodInfo.html#getLineNumber(int) – Quaternion Feb 19 '19 at 22:43
  • It worked for Java 8, but not 11. So be aware to use JDK/bytecode comatible with given javaassist version. – radzimir Feb 24 '23 at 15:40
10

For me the answer of Quaternion pointing to Javassist is the perfect solution to this question. It works for me and you can use it also when you only get the java.lang.reflect.Method object.

This is how I did it:

Method m; // the method object
ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.get(m.getDeclaringClass().getCanonicalName());
CtMethod javassistMethod = cc.getDeclaredMethod(m.getName());
int linenumber = javassistMethod.getMethodInfo().getLineNumber(0);
Thorben
  • 953
  • 13
  • 28
  • 1
    Good to hear! I'm surprised more people don't want this kind of feature. BTW, my use-case was creating an intelligent order on web pages for automatically generated tables. Could you share what your case is? – Quaternion May 02 '13 at 16:18
  • Yes, of course. My use case was getting all advices from an aspect with the AspectJ Reflection API. Inside every advice there's a method object. I used the line numbers of the method objects to sort the advices as they appear in the source code of the aspect. The order of the advices is very important for invocation. – Thorben May 02 '13 at 16:58
0

You can't get a line number from a Method because you don't know what method this applies to. A method can call any sub-class of the the one you chose.

Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
  • But what if I got the instance too? I can get the class object from it. Somewere in the class-file there is some debug info / linenumber. So technically it should be possible somehow. – Chriss Oct 11 '12 at 08:27
  • 1
    In that case it is. You can read the byte code with a library like ASM, BCEL, Javassist and extract the first line in the byte code. – Peter Lawrey Oct 11 '12 at 09:45