// REQUIRED_ARGS: -g
// REQUIRED_ARGS(linux freebsd openbsd dragonflybsd): -L-export-dynamic
// LDC (required for Win32 and -O): REQUIRED_ARGS(windows32): -link-defaultlib-debug
// LDC (FreeBSD's libexecinfo apparently doesn't like elided frame pointers): REQUIRED_ARGS(freebsd): -link-defaultlib-debug -frame-pointer=all
// PERMUTE_ARGS:
// DISABLED: osx

import core.stdc.stdio;
import ldc.attributes;

@optStrategy("none") // otherwise LDC fails on Windows with -O
void main()
{
    fun(1);
    fun(2);
    fun(3);
#line 30
    fun(4);

    foo(1, 10);
    foo(2, 10);
    foo(3, 10);
#line 40
    foo(4, 10);
}

void fun(int n, int defParam = 10)
{
    try
    {
        if (n == 4)
            throw new Exception("fun");
    }
    catch(Exception e)
    {
        string s = e.toString();
        printf("%.*s\n", cast(int)s.length, s.ptr);
        int line = lineInMain(e.toString());
        assert(line >= 30 && line <= 32); // return address might be next statement
    }
}

void foo(int n, int m)
{
    try
    {
        if (n == 4)
            throw new Exception("foo");
    }
    catch(Exception e)
    {
        string s = e.toString();
        printf("%.*s\n", cast(int)s.length, s.ptr);
        int line = lineInMain(e.toString());
        assert(line >= 40 && line <= 41); // return address might be next statement
    }
}

int lineInMain(string msg)
{
    // find line number of _Dmain in stack trace
    // on linux:   file.d:line _Dmain [addr]
    // on windows: addr in _Dmain at file.d(line)
    int line = 0;
    bool mainFound = false;
    for (size_t pos = 0; pos + 6 < msg.length; pos++)
    {
        if (msg[pos] == '\n')
        {
            line = 0;
            mainFound = false;
        }
        else if ((msg[pos] == ':' || msg[pos] == '(') && line == 0)
        {
            for (pos++; pos < msg.length && msg[pos] >= '0' && msg[pos] <= '9'; pos++)
                line = line * 10 + msg[pos] - '0';
            if (line > 0 && mainFound)
                return line;
        }
        else if (msg[pos .. pos + 6] == "_Dmain" || msg[pos .. pos + 6] == "D main")
        {
            mainFound = true;
            if (line > 0 && mainFound)
                return line;
        }
    }
    return 0;
}
