Why can I "fake" the stack trace of an exception in Java?

R. Martinho Fernandes picture R. Martinho Fernandes · Nov 8, 2009 · Viewed 7.7k times · Source

If I run the following test, it fails:

public class CrazyExceptions {
    private Exception exception;

    @Before
    public void setUp(){
        exception = new Exception();
    }

    @Test
    public void stackTraceMentionsTheLocationWhereTheExceptionWasThrown(){
        String thisMethod = new Exception().getStackTrace()[0].getMethodName();
        try {
            throw exception;
        }
        catch(Exception e) {
            assertEquals(thisMethod, e.getStackTrace()[0].getMethodName());
        }
    }
}

With the following error:

Expected :stackTraceMentionsTheLocationWhereTheExceptionWasThrown
Actual   :setUp

The stack trace is just flat out lying.

Why isn't the stack trace rewritten when the exception is thrown? I am not a Java dev, and maybe I'm missing something here.

Answer

mhaller picture mhaller · Nov 8, 2009

The stack trace is created when the exception is instantiated, not when it is thrown. This is specified behaviour of the Java Language Specification

20.22.1  public Throwable()

This constructor initializes a newly created Throwable object with null as
its error message string. Also, the method fillInStackTrace (§20.22.5) is
called for this object. 

....

20.22.5  public Throwable fillInStackTrace()

This method records within this Throwable object information about the
current state of the stack frames for the current thread. 

I don't know why they did it that way, but if the specification defines it like that, it is at least consistent on all the various Java VMs.

However, you can refresh it by calling exception.fillInStackTrace() manually.

Also note that you should use Thread.currentThread().getStackTrace() instead of using new Exception().getStackTrace() (bad style).