-module(stacktrace).
-export([?MODULE/0]).

?MODULE() ->
    OldDepth = erlang:system_flag(backtrace_depth, 32),
    try
        do_try()
    catch
        throw:done:Stk0 ->
            Stk = trim(Stk0),
            {done,Stk}
    after
        erlang:system_flag(backtrace_depth, OldDepth)
    end.

trim([{int_eval_SUITE,_,_,_}|_]) ->
    [];
trim([H|T]) ->
    [H|trim(T)];
trim([]) -> [].

do_try() ->
    try
	0 = id(42)
    catch
	error:{badmatch,42} ->
	    do_try2()				%Tail-recursive
    end.

do_try2() ->
    try
	0 = id(42)
    catch
	error:{badmatch,42} ->
	    do_try3()				%Not tail-recursive
    end,
    ?LINE.

do_try3() ->
    try id(42) of
	42 -> do_try4()				%Tail-recursive
    catch
	error:ignore ->				%Should never catch
	    ?LINE
    end.

do_try4() ->
    try
	do_recv()				%Not tail-recursive
    catch
	error:ignore ->				%Should never catch
	    ?LINE
    end.

do_recv() ->
    self() ! x,
    receive
	x -> do_recv2()				%Not tail-recursive
    end,
    ?LINE.

do_recv2() ->
    self() ! y,
    receive
	y -> do_recv3()				%Tail-recursive
    end.

do_recv3() ->
    receive
	after 0 -> do_recv4()			%Tail-recursive
    end.

do_recv4() ->
    receive
	after 0 -> do_if(true)			%Not tail-recursive
    end,
    ?LINE.

do_if(Bool) ->
    if
	Bool -> do_if2(Bool)			%Tail-recursive
    end.

do_if2(Bool) ->
    if
	Bool -> do_case(Bool)			%Not tail-recursive
    end,
    ?LINE.


do_case(Bool) ->
    case Bool of
	true -> do_case2(Bool)			%Tail-recursive
    end.

do_case2(Bool) ->
    case Bool of
	true -> do_fun(Bool)			%Not tail-recursive
    end,
    ?LINE.

do_fun(Bool) ->
    F = fun(true) ->
		do_fun2(Bool)			%Tail-recursive
	end,
    F(Bool).					%Tail-recursive

do_fun2(Bool) ->
    F = fun(true, _) ->
		cons(Bool)			%Tail-recursive
	end,
    F(Bool, F),                                 %Not tail-recursive. (The fun is not inlined)
    ?LINE.

cons(Bool) ->
    [Bool|tuple()].

tuple() ->
    {ok,op()}.

op() ->
    1 + lc().

lc() ->
    [done() || true].

done() ->
    tail(100),
    throw(done).

tail(0) -> ok;
tail(N) -> tail(N-1).

id(I) ->
    I.
