%%% -*- erlang-indent-level: 2 -*-
%%%-------------------------------------------------------------------
%%% Author: Kostis Sagonas
%%%
%%% Contains code examples that exhibited bugs in the BEAM compiler.
%%%-------------------------------------------------------------------
-module(basic_bugs_beam).
-export([test/0]).
%% the following is needed for the test_weird_message
-export([loop/1]).
%% the following are needed for the test_catch_bug
-behaviour(gen_server).
-export([start_link/1]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
test() ->
ok = test_fp_basic_blocks(),
ok = test_weird_message(),
ok = test_catch_bug(),
ok.
%%--------------------------------------------------------------------
%% Test which shows that BEAM's splitting of basic blocks should take
%% into account that arithmetic operations implemented as BIFs can
%% also cause exceptions and thus calls to BIFs should end basic blocks.
%%
%% Investigated and fixed in the beginning of April 2004.
%%--------------------------------------------------------------------
test_fp_basic_blocks() ->
ok = t1(),
ok = t2().
t1() ->
X = (catch bad_arith1(2.0, 1.7)),
case X of
{'EXIT', {badarith, _}} ->
ok;
_ ->
error
end.
bad_arith1(X, Y) when is_float(X) ->
X1 = X * 1.7e+308,
X2 = X1 + 1.0,
Y1 = Y * 2,
{X2, Y1}.
%% Similarly, it is not kosher to have anything that can fail inside
%% the fp block since it will throw the exception before the fp
%% exception and we will get the same problems.
t2() ->
case catch bad_arith2(2.0, []) of
{'EXIT', {badarith, _}} ->
ok;
_ ->
error
end.
bad_arith2(X, Y) when is_float(X) ->
X1 = X * 1.7e+308,
Y1 = element(1, Y),
{X1 + 1.0, Y1}.
%%--------------------------------------------------------------------
%% Sending 'test' to this process should return 'ok'. But:
%%
%% 1> MOD:test().
%% Weird: received true
%% timeout
%%
%% Surprisingly, the message has been bound to the value of 'ena'
%% in the record! The problem was visible in the .S file.
%%--------------------------------------------------------------------
-record(state, {ena = true}).
test_weird_message() ->
P = spawn_link(?MODULE, loop, [#state{}]),
P ! {msg, self()},
receive
What -> What
after 42 -> timeout
end.
loop(S) ->
receive
_ when S#state.ena == false ->
io:format("Weird: ena is false\n");
% loop(S);
{msg, Pid} ->
Pid ! ok;
% loop(S);
Other ->
io:format("Weird: received ~p\n", [Other])
% loop(S)
end.
%%--------------------------------------------------------------------
%% This was posted on the Erlang mailing list as a question:
%%
%% Given the module below and the function call
%% "catch_bug:start_link(foo)."
%% from the Erlang shell, why does Erlang crash with "Catch not found"?
%%
%% The BEAM compiler was generating wrong code for this case;
%% this was fixed in R9C-0. Native code generation was OK.
%%--------------------------------------------------------------------
test_catch_bug() ->
ignore = start_link(foo),
ok.
start_link(Param) ->
gen_server:start_link(?MODULE, Param, []).
init(Param) ->
process_flag(trap_exit, true),
(catch begin
dummy(Param),
(catch exit(bar))
end
),
ignore.
dummy(_) -> ok.
%% gen_server callbacks below
handle_call(_Call, _From, State) -> {noreply, State}.
handle_cast(_Msg, State) -> {noreply, State}.
handle_info(_Msg, State) -> {noreply, State}.
terminate(_Reason, _State) -> ok.
code_change(_OldVsn, State, _Extra) -> {ok, State}.