Skip to content

Commit

Permalink
Merge pull request #722 from pguyot/w31/fix-exception-classes
Browse files Browse the repository at this point in the history
Fix handling of exception class

* Fix lost class issue when exception is rethrown (fix #713)
* Fix semantic of first parameter of `erlang:raise/3`

These changes are made under both the "Apache 2.0" and the "GNU Lesser General
Public License 2.1 or later" license terms (dual license).

SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later
  • Loading branch information
bettio committed Aug 3, 2023
2 parents 1041318 + aae3bce commit 7d9a418
Show file tree
Hide file tree
Showing 7 changed files with 158 additions and 14 deletions.
6 changes: 5 additions & 1 deletion src/libAtomVM/nifs.c
Original file line number Diff line number Diff line change
Expand Up @@ -2927,7 +2927,11 @@ static term nif_erlang_raise(Context *ctx, int argc, term argv[])
{
UNUSED(argc);

ctx->x[0] = argv[0];
term ex_class = argv[0];
if (UNLIKELY(ex_class != ERROR_ATOM && ex_class != LOWERCASE_EXIT_ATOM && ex_class != THROW_ATOM)) {
return BADARG_ATOM;
}
ctx->x[0] = ex_class;
ctx->x[1] = argv[1];
ctx->x[2] = term_nil();
return term_invalid_term();
Expand Down
16 changes: 9 additions & 7 deletions src/libAtomVM/opcodesswitch.h
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ typedef union
#define SET_ERROR(error_type_atom) \
ctx->x[0] = ERROR_ATOM; \
ctx->x[1] = error_type_atom; \
ctx->x[2] = stacktrace_create_raw(ctx, mod, i); \
ctx->x[2] = stacktrace_create_raw(ctx, mod, i, ERROR_ATOM); \

// Override nifs.h RAISE_ERROR macro
#ifdef RAISE_ERROR
Expand Down Expand Up @@ -812,8 +812,8 @@ typedef union
#define POINTER_TO_II(instruction_pointer) \
(((uint8_t *) (instruction_pointer)) - code)

#define HANDLE_ERROR() \
ctx->x[2] = stacktrace_create_raw(ctx, mod, i); \
#define HANDLE_ERROR() \
ctx->x[2] = stacktrace_create_raw(ctx, mod, i, ctx->x[0]); \
goto handle_error;

#define VERIFY_IS_INTEGER(t, opcode_name) \
Expand Down Expand Up @@ -3645,22 +3645,24 @@ HOT_FUNC int scheduler_entry_point(GlobalContext *glb)
int next_off = 1;
term stacktrace;
DECODE_COMPACT_TERM(stacktrace, code, i, next_off);
UNUSED(stacktrace);
term exc_value;
DECODE_COMPACT_TERM(exc_value, code, i, next_off);

#ifdef IMPL_CODE_LOADER
TRACE("raise/2\n");
UNUSED(stacktrace);
UNUSED(exc_value);
NEXT_INSTRUCTION(next_off);
#endif

#ifdef IMPL_EXECUTE_LOOP
TRACE("raise/2 stacktrace=0x%lx exc_value=0x%lx\n", stacktrace, exc_value);

RAISE_ERROR(exc_value);
ctx->x[0] = stacktrace_exception_class(stacktrace);
ctx->x[1] = exc_value;
ctx->x[2] = stacktrace_create_raw(ctx, mod, i, ctx->x[0]);
goto handle_error;
#endif

NEXT_INSTRUCTION(next_off);
break;
}

Expand Down
21 changes: 16 additions & 5 deletions src/libAtomVM/stacktrace.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,12 @@

#ifndef AVM_CREATE_STACKTRACES

term stacktrace_create_raw(Context *ctx, Module *mod, int current_offset)
term stacktrace_create_raw(Context *ctx, Module *mod, int current_offset, term exception_class)
{
UNUSED(ctx);
UNUSED(mod);
UNUSED(current_offset);
return UNDEFINED_ATOM;
return exception_class;
}

term stacktrace_build(Context *ctx, term *stack_info)
Expand All @@ -38,6 +38,11 @@ term stacktrace_build(Context *ctx, term *stack_info)
return UNDEFINED_ATOM;
}

term stacktrace_exception_class(term stack_info)
{
return stack_info;
}

#else

static void cp_to_mod_lbl_off(term cp, Context *ctx, Module **cp_mod, int *label, int *l_off, long *mod_offset)
Expand Down Expand Up @@ -77,7 +82,7 @@ static bool is_module_member(Module *mod, Module **mods, unsigned long len)
return false;
}

term stacktrace_create_raw(Context *ctx, Module *mod, int current_offset)
term stacktrace_create_raw(Context *ctx, Module *mod, int current_offset, term exception_class)
{
unsigned int num_frames = 0;
unsigned int num_aux_terms = 0;
Expand Down Expand Up @@ -155,7 +160,7 @@ term stacktrace_create_raw(Context *ctx, Module *mod, int current_offset)
free(modules);

// {num_frames, num_aux_terms, filename_lens, num_mods, [{module, offset}, ...]}
size_t requested_size = TUPLE_SIZE(5) + num_frames * (2 + TUPLE_SIZE(2));
size_t requested_size = TUPLE_SIZE(6) + num_frames * (2 + TUPLE_SIZE(2));
if (UNLIKELY(memory_ensure_free(ctx, requested_size) != MEMORY_GC_OK)) {
fprintf(stderr, "WARNING: Unable to allocate heap space for raw stacktrace\n");
return OUT_OF_MEMORY_ATOM;
Expand Down Expand Up @@ -215,16 +220,22 @@ term stacktrace_create_raw(Context *ctx, Module *mod, int current_offset)
ct++;
}

term stack_info = term_alloc_tuple(5, &ctx->heap);
term stack_info = term_alloc_tuple(6, &ctx->heap);
term_put_tuple_element(stack_info, 0, term_from_int(num_frames));
term_put_tuple_element(stack_info, 1, term_from_int(num_aux_terms));
term_put_tuple_element(stack_info, 2, term_from_int(filename_lens));
term_put_tuple_element(stack_info, 3, term_from_int(num_mods));
term_put_tuple_element(stack_info, 4, raw_stacktrace);
term_put_tuple_element(stack_info, 5, exception_class);

return stack_info;
}

term stacktrace_exception_class(term stack_info)
{
return term_get_tuple_element(stack_info, 5);
}

struct ModulePathPair
{
term module;
Expand Down
3 changes: 2 additions & 1 deletion src/libAtomVM/stacktrace.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,9 @@ extern "C" {
#include "module.h"
#include "term.h"

term stacktrace_create_raw(Context *ctx, Module *mod, int current_offset);
term stacktrace_create_raw(Context *ctx, Module *mod, int current_offset, term exception_class);
term stacktrace_build(Context *ctx, term *stack_info);
term stacktrace_exception_class(term stack_info);

#ifdef __cplusplus
}
Expand Down
2 changes: 2 additions & 0 deletions tests/erlang_tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ compile_erlang(pingpong)
compile_erlang(prime_ext)
compile_erlang(prime_smp)
compile_erlang(test_try_case_end)
compile_erlang(test_exception_classes)
compile_erlang(test_recursion_and_try_catch)
compile_erlang(test_func_info)
compile_erlang(test_func_info2)
Expand Down Expand Up @@ -596,6 +597,7 @@ add_custom_target(erlang_test_modules DEPENDS
prime_ext.beam
prime_smp.beam
test_try_case_end.beam
test_exception_classes.beam
test_recursion_and_try_catch.beam
test_func_info.beam
test_func_info2.beam
Expand Down
123 changes: 123 additions & 0 deletions tests/erlang_tests/test_exception_classes.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
%
% This file is part of AtomVM.
%
% Copyright 2023 Paul Guyot <pguyot@kallisys.net>
%
% Licensed under the Apache License, Version 2.0 (the "License");
% you may not use this file except in compliance with the License.
% You may obtain a copy of the License at
%
% http://www.apache.org/licenses/LICENSE-2.0
%
% Unless required by applicable law or agreed to in writing, software
% distributed under the License is distributed on an "AS IS" BASIS,
% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
% See the License for the specific language governing permissions and
% limitations under the License.
%
% SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later
%

-module(test_exception_classes).

-export([start/0]).

start() ->
ok = test_exit(),
ok = test_throw(),
ok = test_error(),
ok = test_inner_exit(),
ok = test_after_catch_throw(),
ok = test_after_throw(),
ok = test_raise_error(),
ok = test_raise_exit(),
ok = test_raise_badarg(),
0.

test_exit() ->
try
exit(foo)
catch
exit:foo -> ok;
C:V -> {unexpected, ?LINE, C, V}
end.

test_throw() ->
try
throw(foo)
catch
throw:foo -> ok;
C:V -> {unexpected, ?LINE, C, V}
end.

test_error() ->
try
error(foo)
catch
error:foo -> ok;
C:V -> {unexpected, ?LINE, C, V}
end.

test_inner_exit() ->
try
try
exit(foo)
catch
error:foo -> {unexpected, ?LINE}
end
catch
exit:foo -> ok;
C:V -> {unexpected, ?LINE, C, V}
end.

test_after_catch_throw() ->
try
try
exit(foo)
after
try
throw(foo)
catch
throw:foo -> ok
end
end
catch
exit:foo -> ok;
C:V -> {unexpected, ?LINE, C, V}
end.

test_after_throw() ->
try
try
exit(foo)
after
throw(foo)
end
catch
throw:foo -> ok;
C:V -> {unexpected, ?LINE, C, V}
end.

test_raise_error() ->
try
erlang:raise(error, foo, [])
catch
error:foo -> ok;
C:V -> {unexpected, ?LINE, C, V}
end.

test_raise_exit() ->
try
erlang:raise(exit, foo, [])
catch
exit:foo -> ok;
C:V -> {unexpected, ?LINE, C, V}
end.

test_raise_badarg() ->
try
badarg = erlang:raise(bar, foo, []),
ok
catch
C:V -> {unexpected, ?LINE, C, V}
end.
1 change: 1 addition & 0 deletions tests/test.c
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ struct Test tests[] = {
TEST_CASE_EXPECTED(test_open_port_badargs, -21),
TEST_CASE_EXPECTED(prime_ext, 1999),
TEST_CASE_EXPECTED(test_try_case_end, 256),
TEST_CASE(test_exception_classes),
TEST_CASE_EXPECTED(test_recursion_and_try_catch, 3628800),
TEST_CASE_EXPECTED(test_func_info, 89),
TEST_CASE_EXPECTED(test_func_info2, 1),
Expand Down

0 comments on commit 7d9a418

Please sign in to comment.