Commit cbe61f0a authored by Botond Baranyi's avatar Botond Baranyi

OOP: implemented exception lists, added semantic checks for exception handling...

OOP: implemented exception lists, added semantic checks for exception handling and added function tests (bug 568899)
Signed-off-by: Botond Baranyi's avatarBotond Baranyi <botond.baranyi@ericsson.com>
Change-Id: I56f7ab4757d5f9a2f4f6f5f288396b51b2fe2f31
Signed-off-by: Botond Baranyi's avatarBotond Baranyi <botond.baranyi@ericsson.com>
parent 9b02dc49
......@@ -14,6 +14,7 @@
#include "Type.hh"
#include "CompilerError.hh"
#include "AST.hh"
namespace Common {
......@@ -251,6 +252,47 @@ void SignatureExceptions::chk(Type *p_signature)
}
}
void SignatureExceptions::chk(Assignment* p_def)
{
Error_Context cntxt(this, "In exception list");
for (size_t i = 0; i < exc_v.size(); i++) {
Type* type = exc_v[i];
type->set_genname(p_def->get_id().get_name(), string("_exception_") + Int2string(i + 1));
type->chk();
if (type->get_typetype() == Type::T_ERROR) continue;
Type* type_last = type->get_type_refd_last();
bool type_error = false;
char* error_msg_start = NULL;
switch (type_last->get_typetype()) {
case Type::T_PORT:
type_error = true;
error_msg_start = mprintf("Port type `%s'", type_last->get_typename().c_str());
break;
case Type::T_SIGNATURE:
type_error = true;
error_msg_start = mprintf("Signature `%s'", type_last->get_typename().c_str());
break;
case Type::T_DEFAULT:
type_error = true;
error_msg_start = mcopystr("Default type");
break;
default:
break;
}
if (type_error) {
type->error("%s cannot be on the exception list of a%s %s",
error_msg_start, p_def->get_asstype() == Assignment::A_FUNCTION ? "" : "n", p_def->get_assname());
}
Free(error_msg_start);
const string& type_name = type->get_exception_name();
if (exc_m.has_key(type_name)) {
type->error("Duplicate type in exception list");
exc_m[type_name]->note("Type `%s' is already given here",
type_name.c_str());
} else exc_m.add(type_name, type);
}
}
void SignatureExceptions::set_fullname(const string& p_fullname)
{
Node::set_fullname(p_fullname);
......
......@@ -83,7 +83,7 @@ public:
};
/**
* Signature exception-list
* Signature or OOP exception-list
*/
class SignatureExceptions : public Node, public Location {
vector<Type> exc_v;
......@@ -106,7 +106,10 @@ public:
Type *get_type_byName(const string& p_typename) const
{ return exc_m[p_typename]; }
/** Semantic check for a signature exception list */
void chk(Type *p_signature);
/** Semantic check for a function, external function or altstep exception list (as part of the OOP features) */
void chk(Assignment* p_def);
virtual void set_fullname(const string& p_fullname);
virtual void set_my_scope(Scope *p_scope);
virtual void dump(unsigned level) const;
......
......@@ -47,6 +47,7 @@
#include "../CodeGenHelper.hh"
#include "../../common/JSON_Tokenizer.hh"
#include "../DebuggerStuff.hh"
#include "../SigParam.hh"
#include <limits.h>
// implemented in coding_attrib_p.y
......@@ -5367,21 +5368,27 @@ namespace Ttcn {
void Def_Var::chk()
{
if(checked) return;
Error_Context cntxt(this, "In variable definition `%s'",
id->get_dispname().c_str());
Error_Context cntxt(this, "In %s definition `%s'",
asstype == A_EXCEPTION ? "exception" : "variable", id->get_dispname().c_str());
type->set_genname(_T_, get_genname());
type->chk();
checked = true;
Type *t = type->get_type_refd_last();
switch (t->get_typetype()) {
case Type::T_PORT:
error("Variable cannot be defined for port type `%s'",
t->get_fullname().c_str());
error("%s cannot be defined for port type `%s'",
asstype == A_EXCEPTION ? "Exception" : "Variable", t->get_fullname().c_str());
break;
case Type::T_SIGNATURE:
error("Variable cannot be defined for signature `%s'",
t->get_fullname().c_str());
error("%s cannot be defined for signature `%s'",
asstype == A_EXCEPTION ? "Exception" : "Variable", t->get_fullname().c_str());
break;
case Type::T_DEFAULT:
if (asstype == A_EXCEPTION) {
error("Exception cannot be defined for the default type");
break;
}
// else fall through
default:
if (initial_value) {
initial_value->set_my_governor(type);
......@@ -6609,7 +6616,7 @@ namespace Ttcn {
Def_Function_Base::Def_Function_Base(const Def_Function_Base& p)
: Definition(p), prototype(PROTOTYPE_NONE), input_type(0), output_type(0),
final(p.final)
final(p.final), exceptions(p.exceptions)
{
fp_list = p.fp_list->clone();
fp_list->set_my_def(this);
......@@ -6619,11 +6626,13 @@ namespace Ttcn {
Def_Function_Base::Def_Function_Base(bool is_external, Identifier *p_id,
FormalParList *p_fpl, Type *p_return_type, bool returns_template,
template_restriction_t p_template_restriction, bool p_final)
template_restriction_t p_template_restriction, bool p_final,
Common::SignatureExceptions* p_exceptions)
: Definition(determine_asstype(is_external, p_return_type != 0,
returns_template), p_id), fp_list(p_fpl), return_type(p_return_type),
prototype(PROTOTYPE_NONE), input_type(0), output_type(0),
template_restriction(p_template_restriction), final(p_final)
template_restriction(p_template_restriction), final(p_final),
exceptions(p_exceptions)
{
if (!p_fpl) FATAL_ERROR("Def_Function_Base::Def_Function_Base()");
fp_list->set_my_def(this);
......@@ -6634,6 +6643,7 @@ namespace Ttcn {
{
delete fp_list;
delete return_type;
delete exceptions;
}
void Def_Function_Base::set_fullname(const string& p_fullname)
......@@ -6641,6 +6651,9 @@ namespace Ttcn {
Definition::set_fullname(p_fullname);
fp_list->set_fullname(p_fullname + ".<formal_par_list>");
if (return_type) return_type->set_fullname(p_fullname + ".<return_type>");
if (exceptions != NULL) {
exceptions->set_fullname(p_fullname + ".<exceptions>");
}
}
void Def_Function_Base::set_my_scope(Scope *p_scope)
......@@ -6648,6 +6661,9 @@ namespace Ttcn {
Definition::set_my_scope(p_scope);
fp_list->set_my_scope(p_scope);
if (return_type) return_type->set_my_scope(p_scope);
if (exceptions != NULL) {
exceptions->set_my_scope(p_scope);
}
}
Type *Def_Function_Base::get_Type()
......@@ -6837,6 +6853,20 @@ namespace Ttcn {
return false;
}
}
if (exceptions != NULL || p_other->exceptions != NULL) {
if (exceptions == NULL || p_other->exceptions == NULL) {
// one of them has exceptions, the other doesn't
return false;
}
if (exceptions->get_nof_types() != p_other->exceptions->get_nof_types()) {
return false;
}
for (size_t i = 0; i < exceptions->get_nof_types(); ++i) {
if (!p_other->exceptions->has_type(exceptions->get_type_byIndex(i))) {
return false;
}
}
}
return true;
}
......@@ -6851,9 +6881,9 @@ namespace Ttcn {
Type *p_return_type,
bool returns_template,
template_restriction_t p_template_restriction,
bool p_final, StatementBlock *p_block)
bool p_final, Common::SignatureExceptions* p_exceptions, StatementBlock *p_block)
: Def_Function_Base(false, p_id, p_fpl, p_return_type, returns_template,
p_template_restriction, p_final),
p_template_restriction, p_final, p_exceptions),
runs_on_ref(p_runs_on_ref), runs_on_type(0),
mtc_ref(p_mtc_ref), mtc_type(0),
system_ref(p_system_ref), system_type(0),
......@@ -7101,6 +7131,9 @@ namespace Ttcn {
break;
}
}
if (exceptions != NULL) {
exceptions->chk(this);
}
if (!semantic_check_only) {
fp_list->set_genname(get_genname());
block->set_code_section(GovernedSimple::CS_INLINE);
......@@ -7345,6 +7378,10 @@ namespace Ttcn {
DEBUG(level + 1, "Deterministic: %s", deterministic ? "true" : "false");
if (prototype != PROTOTYPE_NONE)
DEBUG(level + 1, "Prototype: %s", get_prototype_name());
if (exceptions != NULL) {
DEBUG(level + 1, "Exceptions:");
exceptions->dump(level + 2);
}
//DEBUG(level + 1, "Statement block:");
block->dump(level + 1);
}
......@@ -7797,6 +7834,13 @@ namespace Ttcn {
(Type::CT_JSON != encoding_type && Type::CT_XER != encoding_type))) {
error("Attribute 'printing' is only allowed for JSON and XER encoding functions.");
}
if (exceptions != NULL) {
exceptions->chk(this);
if (function_type != EXTFUNC_MANUAL) {
error("Exception list is only allowed for manually written external functions");
}
}
}
char *Def_ExtFunction::generate_code_encode(char *str)
......@@ -8157,6 +8201,10 @@ namespace Ttcn {
DEBUG(level + 2, "Encoding options: %s", encoding_options->c_str());
}
if (eb_list) eb_list->dump(level + 1);
if (exceptions != NULL) {
DEBUG(level + 1, "Exceptions:");
exceptions->dump(level + 2);
}
}
void Def_ExtFunction::generate_json_schema_ref(map<Type*, JSON_Tokenizer>& json_refs)
......@@ -8312,6 +8360,9 @@ namespace Ttcn {
return_type->chk_as_return_type(asstype == A_FUNCTION_RVAL,
"n abstract function");
}
if (exceptions != NULL) {
exceptions->chk(this);
}
if (!semantic_check_only) {
fp_list->set_genname(get_genname());
}
......@@ -8355,10 +8406,10 @@ namespace Ttcn {
Def_Altstep::Def_Altstep(Identifier *p_id, FormalParList *p_fpl,
Reference *p_runs_on_ref, Reference *p_mtc_ref,
Reference *p_system_ref, StatementBlock *p_sb,
AltGuards *p_ags)
AltGuards *p_ags, Common::SignatureExceptions* p_exceptions)
: Definition(A_ALTSTEP, p_id), fp_list(p_fpl), runs_on_ref(p_runs_on_ref),
runs_on_type(0), mtc_ref(p_mtc_ref), mtc_type(0),
system_ref(p_system_ref), system_type(0), sb(p_sb), ags(p_ags)
system_ref(p_system_ref), system_type(0), sb(p_sb), ags(p_ags), exceptions(p_exceptions)
{
if (!p_fpl || !p_sb || !p_ags)
FATAL_ERROR("Def_Altstep::Def_Altstep()");
......@@ -8376,6 +8427,7 @@ namespace Ttcn {
delete system_ref;
delete sb;
delete ags;
delete exceptions;
}
Def_Altstep *Def_Altstep::clone() const
......@@ -8392,6 +8444,9 @@ namespace Ttcn {
if (system_ref) system_ref->set_fullname(p_fullname + ".<system_type>");
sb->set_fullname(p_fullname+".<block>");
ags->set_fullname(p_fullname + ".<guards>");
if (exceptions != NULL) {
exceptions->set_fullname(p_fullname + ".<exceptions>");
}
}
void Def_Altstep::set_my_scope(Scope *p_scope)
......@@ -8406,6 +8461,9 @@ namespace Ttcn {
if (system_ref) system_ref->set_my_scope(&bridgeScope);
sb->set_my_scope(fp_list);
ags->set_my_scope(sb);
if (exceptions != NULL) {
exceptions->set_my_scope(&bridgeScope);
}
}
Type *Def_Altstep::get_RunsOnType()
......@@ -8479,6 +8537,9 @@ namespace Ttcn {
w_attrib_path->chk_global_attrib();
w_attrib_path->chk_no_qualif();
}
if (exceptions != NULL) {
exceptions->chk(this);
}
}
void Def_Altstep::generate_code(output_struct *target, bool)
......@@ -8635,6 +8696,10 @@ namespace Ttcn {
DEBUG(level + 1, "System clause:");
system_ref->dump(level + 2);
}
if (exceptions != NULL) {
DEBUG(level + 1, "Exceptions:");
exceptions->dump(level + 2);
}
/*
DEBUG(level + 1, "Local definitions:");
sb->dump(level + 2);
......
......@@ -1384,6 +1384,7 @@ namespace Ttcn {
/** optional template restriction on return template value */
template_restriction_t template_restriction;
bool final;
Common::SignatureExceptions* exceptions;
static asstype_t determine_asstype(bool is_external, bool has_return_type,
bool returns_template);
......@@ -1404,7 +1405,8 @@ namespace Ttcn {
*/
Def_Function_Base(bool is_external, Identifier *p_id,
FormalParList *p_fpl, Type *p_return_type, bool returns_template,
template_restriction_t p_template_restriction, bool p_final);
template_restriction_t p_template_restriction, bool p_final,
Common::SignatureExceptions* p_exceptions);
virtual ~Def_Function_Base();
virtual void set_fullname(const string& p_fullname);
virtual void set_my_scope(Scope *p_scope);
......@@ -1502,7 +1504,7 @@ namespace Ttcn {
Reference *p_system_ref, Reference *p_port_ref,
Type *p_return_type, bool returns_template,
template_restriction_t p_template_restriction,
bool p_final, StatementBlock *p_block);
bool p_final, Common::SignatureExceptions* p_exceptions, StatementBlock *p_block);
virtual ~Def_Function();
virtual Def_Function *clone() const;
virtual void set_fullname(const string& p_fullname);
......@@ -1598,9 +1600,10 @@ namespace Ttcn {
*/
Def_ExtFunction(bool p_deterministic, Identifier *p_id, FormalParList *p_fpl,
Type *p_return_type, bool returns_template,
template_restriction_t p_template_restriction, bool p_final, bool p_ext_keyword)
template_restriction_t p_template_restriction, bool p_final,
Common::SignatureExceptions* p_exceptions, bool p_ext_keyword)
: Def_Function_Base(true, p_id, p_fpl, p_return_type, returns_template,
p_template_restriction, p_final),
p_template_restriction, p_final, p_exceptions),
function_type(EXTFUNC_MANUAL), encoding_type(Type::CT_UNDEF),
encoding_options(0), eb_list(0), printing(0),
deterministic(p_deterministic), ext_keyword(p_ext_keyword) { }
......@@ -1643,9 +1646,9 @@ namespace Ttcn {
public:
Def_AbsFunction(bool p_deterministic, Identifier* p_id, FormalParList* p_fpl,
Type* p_return_type, bool returns_template,
template_restriction_t p_template_restriction)
template_restriction_t p_template_restriction, Common::SignatureExceptions* p_exceptions)
: Def_Function_Base(false, p_id, p_fpl, p_return_type, returns_template,
p_template_restriction, false) { }
p_template_restriction, false, p_exceptions) { }
virtual ~Def_AbsFunction();
virtual Definition* clone() const;
virtual void chk();
......@@ -1686,6 +1689,7 @@ namespace Ttcn {
Type *system_type;
StatementBlock *sb; /**< contains the local definitions */
AltGuards *ags;
Common::SignatureExceptions* exceptions;
NameBridgingScope bridgeScope;
......@@ -1697,7 +1701,7 @@ namespace Ttcn {
Def_Altstep(Identifier *p_id, FormalParList *p_fpl,
Reference *p_runs_on_ref, Reference *p_mtc_ref,
Reference *p_system_ref, StatementBlock *p_sb,
AltGuards *p_ags);
AltGuards *p_ags, Common::SignatureExceptions* p_exceptions);
virtual ~Def_Altstep();
virtual Def_Altstep *clone() const;
virtual void set_fullname(const string& p_fullname);
......
......@@ -283,12 +283,29 @@ namespace Ttcn {
p_finally->exception_handling = EH_OOP_FINALLY;
finally_block = p_finally;
}
boolean StatementBlock::is_in_finally_block() const
{
if (exception_handling == EH_OOP_FINALLY) {
return TRUE;
}
if (my_sb != NULL) {
return my_sb->is_in_finally_block();
}
return FALSE;
}
boolean StatementBlock::is_empty() const
{
return stmts.size() == 0 && exception_handling == StatementBlock::EH_NONE &&
catch_blocks.size() == 0 && finally_block == NULL;
}
StatementBlock::returnstatus_t StatementBlock::has_return() const
{
returnstatus_t ret_val = RS_NO;
size_t nof_stmts = stmts.size();
for (size_t i = 0; i < nof_stmts; i++) {
for (size_t i = 0; i < nof_stmts && ret_val != RS_YES; i++) {
Statement *stmt = stmts[i];
if (stmt->get_statementtype() == Statement::S_GOTO) {
if (stmt->goto_jumps_forward()) {
......@@ -300,7 +317,7 @@ namespace Ttcn {
if (stmt->get_statementtype() == Statement::S_LABEL &&
stmt->label_is_used()) break;
}
} else return RS_YES;
} else ret_val = RS_YES;
} else if (stmt->get_statementtype()==Statement::S_BLOCK && stmt->get_block()->exception_handling!=EH_NONE) {
switch (stmt->get_block()->exception_handling) {
case EH_TRY: // the i-th statement is a try{} statement block, the (i+1)-th must be a catch{} block
......@@ -311,9 +328,11 @@ namespace Ttcn {
if (try_block_rs==catch_block_rs) {
switch (try_block_rs) {
case RS_YES:
return RS_YES;
ret_val = RS_YES;
break;
case RS_MAYBE:
ret_val = RS_MAYBE;
break;
default:
break;
}
......@@ -333,14 +352,28 @@ namespace Ttcn {
} else {
switch (stmt->has_return()) {
case RS_YES:
return RS_YES;
ret_val = RS_YES;
break;
case RS_MAYBE:
ret_val = RS_MAYBE;
break;
default:
break;
}
}
}
for (size_t i = 0; i < catch_blocks.size(); ++i) {
// all 'catch' blocks must also have return for the statement block to have return
switch(catch_blocks[i]->has_return()) {
case RS_YES:
break; // OK, keep checking
case RS_MAYBE:
return RS_MAYBE;
case RS_NO:
ret_val = (ret_val == RS_NO) ? RS_NO : RS_MAYBE;
break;
}
}
return ret_val;
}
......@@ -418,9 +451,25 @@ namespace Ttcn {
}
chk_trycatch_blocks(prev_stmt, 0);
chk_unused_labels();
map<string, Definition> catch_types;
for (size_t i = 0; i < catch_blocks.size(); ++i) {
catch_blocks[i]->chk();
if (oop_features) {
catch_blocks[i]->chk();
Definition* def_exc = catch_blocks[i]->get_stmt_byIndex(0)->get_def();
string catch_type_str = def_exc->get_Type()->get_exception_name();
if (catch_types.has_key(catch_type_str)) {
def_exc->error("Duplicate catch block definition for exception type `%s'", catch_type_str.c_str());
catch_types[catch_type_str]->note("Catch block for the same exception type already defined here");
}
else {
catch_types.add(catch_type_str, def_exc);
}
}
else {
catch_blocks[i]->error("The compiler option `-k' must be activated to use object-oriented features such as catch clauses.");
}
}
catch_types.clear();
if (finally_block != NULL) {
finally_block->chk();
}
......@@ -531,9 +580,14 @@ namespace Ttcn {
tmp_id.c_str());
}
str = mputprintf(str,
"~%s_finally() {\n", tmp_id.c_str());
"~%s_finally() {\n"
"try {\n", tmp_id.c_str());
str = finally_block->generate_code(str, def_glob_vars, src_glob_vars);
str = mputprintf(str,
"} catch (...) {\n"
"fprintf(stderr, \"Unhandled exception or dynamic test case error in a finally block. Terminating application.\\n\");\n"
"exit(EXIT_FAILURE);\n"
"}\n"
"}\n"
"};\n"
"%s_finally %s%s;\n",
......@@ -3819,6 +3873,10 @@ error:
"It is allowed only in functions and altsteps");
goto error;
}
if (my_sb->is_in_finally_block()) {
error("Return statement cannot be used inside a finally block or the destructor of a class.");
goto error;
}
switch (my_def->get_asstype()) {
case Definition::A_FUNCTION:
if (returnexpr.t) {
......@@ -6353,38 +6411,60 @@ error:
void Statement::chk_raise_oop()
{
Error_Context cntxt(this, "In raise statement");
if (!oop_features) {
error("The compiler option `-k' must be activated to use object-oriented features such as raise statements.");
}
Definition *my_def = my_sb->get_my_def();
if (!my_def) {
error("Raise statement cannot be used in the control part. "
"It is allowed only in functions, altsteps and testcases.");
}
if (my_sb->is_in_finally_block()) {
error("Raise statement cannot be used inside a finally block or the destructor of a class.");
}
Common::Type* exc_type = raise_op.ti->get_expr_governor(Common::Type::EXPECTED_DYNAMIC_VALUE);
if (exc_type == NULL) {
raise_op.ti->get_Template()->set_lowerid_to_ref();
exc_type = raise_op.ti->get_expr_governor(Common::Type::EXPECTED_DYNAMIC_VALUE);
}
if (exc_type == NULL) {
// TODO
}
else {
if (raise_op.ti->get_Type() == exc_type) {
// don't use the type indicator of the TemplateInstance
// (i.e. the 'type:' part of the in-line template)
// as the exception's governor, because it will be deleted
if (exc_type->is_ref()) {
exc_type = exc_type->get_type_refd();
}
else {
exc_type = Type::get_pooltype(exc_type->get_typetype());
}
if (raise_op.ti->get_Template()->get_templatetype() != Template::TEMPLATE_ERROR &&
(raise_op.ti->get_Template()->get_templatetype() != Template::SPECIFIC_VALUE ||
raise_op.ti->get_Template()->get_specific_value()->get_valuetype() != Common::Value::V_ERROR)) {
error("Cannot determine the type of the raised exception.");
}
raise_op.ti->chk(exc_type);
if (!raise_op.ti->get_Template()->is_Value()) {
// TODO: error
delete raise_op.ti;
raise_op.v = new Common::Value(Common::Value::V_ERROR);
exc_type = Type::get_pooltype(Type::T_ERROR);
}
switch (exc_type->get_type_refd_last()->get_typetype()) {
case Common::Type::T_DEFAULT:
case Common::Type::T_SIGNATURE:
//case Common::Type::T_PORT: // this case is handled by Template::get_expr_governor
error("Cannot raise an exception of type %s", exc_type->get_typename().c_str());
break;
default:
break;
}
if (raise_op.ti->get_Type() == exc_type) {
// don't use the type indicator of the TemplateInstance
// (i.e. the 'type:' part of the in-line template)
// as the exception's governor, because it will be deleted
if (exc_type->is_ref()) {
exc_type = exc_type->get_type_refd();
}
else {
Common::Value* val = raise_op.ti->get_Template()->get_Value();
delete raise_op.ti;
raise_op.v = val;
exc_type = Type::get_pooltype(exc_type->get_typetype());
}
// TODO
}
raise_op.ti->chk(exc_type);
if (!raise_op.ti->get_Template()->is_Value()) {
error("A specific value without matching symbols or a reference to a value was expected for a raised exception.");
delete raise_op.ti;
raise_op.v = new Common::Value(Common::Value::V_ERROR);
}
else {
Common::Value* val = raise_op.ti->get_Template()->get_Value();
delete raise_op.ti;
raise_op.v = val;
}
raise_op.checked = true;
}
......@@ -7179,7 +7259,7 @@ error:
default:
FATAL_ERROR("Statement::generate_code_block()");
}
if (block->get_nof_stmts() > 0 || block->get_exception_handling()!=StatementBlock::EH_NONE) {
if (!block->is_empty()) {
str = mputstr(str, "{\n");
if (debugger_active) {
str = mputstr(str, "TTCN3_Debug_Scope debug_scope;\n");
......
......@@ -132,6 +132,8 @@ namespace Ttcn {
void set_my_laic_stmt(AltGuards *p_ags, Statement *p_loop_stmt);
void add_catch_block(StatementBlock* p_catch);
void set_finally_block(StatementBlock* p_finally);
boolean is_in_finally_block() const;
boolean is_empty() const;
returnstatus_t has_return() const;
/** Used when generating code for interleaved statement. If has
* not recv stmt, then the general code generation can be used
......
......@@ -3081,6 +3081,9 @@ namespace Ttcn {
if (members == NULL) {
FATAL_ERROR("ClassTypeBody::ClassTypeBody");
}
if (finally_block != NULL) {
finally_block->set_exception_handling(StatementBlock::EH_OOP_FINALLY);
}
}
ClassTypeBody::ClassTypeBody()
......@@ -3663,6 +3666,7 @@ namespace Ttcn {
}
if (finally_block != NULL) {
Error_Context cntxt(finally_block, "In class destructor");
finally_block->chk();
}
......@@ -3886,7 +3890,7 @@ namespace Ttcn {
local_struct->source.methods = mputprintf(local_struct->source.methods,
"} catch (...) {\n"
"fprintf(stderr, \"Unhandled exception or dynamic test case error in "
"destructor of class `%s'\");\n"
"the destructor of class `%s'. Terminating application.\\n\");\n"
"exit(EXIT_FAILURE);\n"
"}\n"
"}\n\n", class_id->get_name().c_str());
......