diff --git a/compiler2/ttcn3/AST_ttcn3.cc b/compiler2/ttcn3/AST_ttcn3.cc index 8d140be73f16725285563e03bab9960ec5aa3dd3..647b8cb2af1d8f021924486bca0e780e4d7b53d9 100644 --- a/compiler2/ttcn3/AST_ttcn3.cc +++ b/compiler2/ttcn3/AST_ttcn3.cc @@ -910,6 +910,27 @@ namespace Ttcn { } } } + if (ass != NULL) { + ref_usage_found(ass); + StatementBlock* sb = my_scope->get_statementblock_scope(); + if (sb != NULL && sb->is_in_finally_block()) { + switch (ass->get_asstype()) { + default: + if (!ass->is_local()) { + break; + } + // else fall through + case Common::Assignment::A_PAR_VAL: + case Common::Assignment::A_PAR_VAL_IN: + case Common::Assignment::A_PAR_VAL_OUT: + case Common::Assignment::A_PAR_VAL_INOUT: + case Common::Assignment::A_PAR_TEMPL_IN: + case Common::Assignment::A_PAR_TEMPL_OUT: + case Common::Assignment::A_PAR_TEMPL_INOUT: + sb->get_finally_block()->add_refd_local_def(ass); + } + } + } return ass; } @@ -1115,9 +1136,8 @@ namespace Ttcn { return true; } - void Reference::ref_usage_found() + void Reference::ref_usage_found(Common::Assignment *ass) { - Common::Assignment *ass = get_refd_assignment(); if (!ass) FATAL_ERROR("Reference::ref_usage_found()"); switch (ass->get_asstype()) { case Common::Assignment::A_PAR_VAL_OUT: @@ -1142,6 +1162,13 @@ namespace Ttcn { } def->set_usage_found(); break; } + case Common::Assignment::A_EXCEPTION: { + Def_Exception* def = dynamic_cast<Def_Exception*>(ass); + if (def == NULL) { + FATAL_ERROR("Reference::ref_usage_found()"); + } + def->set_usage_found(); + break; } default: break; } @@ -1170,7 +1197,6 @@ namespace Ttcn { void Reference::generate_code(expression_struct_t *expr) { - ref_usage_found(); Common::Assignment *ass = get_refd_assignment(); if (!ass) FATAL_ERROR("Reference::generate_code()"); if (reftype == REF_THIS) { @@ -1228,7 +1254,6 @@ namespace Ttcn { return; } - ref_usage_found(); Common::Assignment *ass = get_refd_assignment(); if (!ass) FATAL_ERROR("Reference::generate_code_const_ref()"); @@ -1341,7 +1366,6 @@ namespace Ttcn { void Reference::generate_code_portref(expression_struct_t *expr, Scope *p_scope) { - ref_usage_found(); Common::Assignment *ass = get_refd_assignment(); if (!ass) FATAL_ERROR("Reference::generate_code_portref()"); expr->expr = mputstr(expr->expr, @@ -1353,7 +1377,6 @@ namespace Ttcn { void Reference::generate_code_ispresentboundchosen(expression_struct_t *expr, bool is_template, const Value::operationtype_t optype, const char* field) { - ref_usage_found(); Common::Assignment *ass = get_refd_assignment(); const string& ass_id = ass->get_genname_from_scope(my_scope); const char *ass_id_str = ass_id.c_str(); @@ -5548,15 +5571,16 @@ namespace Ttcn { // ===== Def_Exception // ================================= - Def_Exception::Def_Exception(Identifier* p_id, Type* p_type): Def_Var(p_id, p_type, NULL) + Def_Exception::Def_Exception(Identifier* p_id, Type* p_type): Def_Var(p_id, p_type, NULL), usage_found(false) { asstype = Common::Assignment::A_EXCEPTION; } char* Def_Exception::generate_code_str(char *str) { - return mputprintf(str, "EXCEPTION< %s >& %s = static_cast<EXCEPTION< %s >&>(exc_base);\n", - type->get_genname_value(my_scope).c_str(), id->get_name().c_str(), type->get_genname_value(my_scope).c_str()); + return usage_found ? mputprintf(str, "EXCEPTION< %s >& %s = static_cast<EXCEPTION< %s >&>(exc_base);\n", + type->get_genname_value(my_scope).c_str(), id->get_name().c_str(), type->get_genname_value(my_scope).c_str()) : + str; } // ================================= @@ -11235,13 +11259,6 @@ namespace Ttcn { LazyFuzzyParamData::init(used_as_lvalue); LazyFuzzyParamData::generate_code(expr, val, my_scope, param_eval == LAZY_EVAL); LazyFuzzyParamData::clean(); - if (val->get_valuetype() == Value::V_REFD) { - // check if the reference is a parameter, mark it as used if it is - Reference* r = dynamic_cast<Reference*>(val->get_reference()); - if (r != NULL) { - r->ref_usage_found(); - } - } } else { char* expr_expr = NULL; if (use_runtime_2 && TypeConv::needs_conv_refd(val)) { @@ -11292,15 +11309,6 @@ namespace Ttcn { LazyFuzzyParamData::generate_code(expr, temp, gen_restriction_check, my_scope, param_eval == LAZY_EVAL); LazyFuzzyParamData::clean(); - if (temp->get_DerivedRef() != NULL || - temp->get_Template()->get_templatetype() == Template::TEMPLATE_REFD) { - // check if the reference is a parameter, mark it as used if it is - Reference* r = dynamic_cast<Reference*>(temp->get_DerivedRef() != NULL ? - temp->get_DerivedRef() : temp->get_Template()->get_reference()); - if (r != NULL) { - r->ref_usage_found(); - } - } } else { char* expr_expr = NULL; if (use_runtime_2 && TypeConv::needs_conv_refd(temp->get_Template())) { diff --git a/compiler2/ttcn3/AST_ttcn3.hh b/compiler2/ttcn3/AST_ttcn3.hh index 5d6dd9aa371753a40471fd74749815aa2ac2b28b..f74b68f6ee91d734d0f4968211b8a2f0f74c0a0a 100644 --- a/compiler2/ttcn3/AST_ttcn3.hh +++ b/compiler2/ttcn3/AST_ttcn3.hh @@ -416,8 +416,8 @@ namespace Ttcn { * On further runs the cached expression is returned.*/ virtual void generate_code_cached (expression_struct_t *expr); /** Lets the referenced assignment object know, that the reference is used - * at least once (only relevant for formal parameters and external constants). */ - void ref_usage_found(); + * at least once (only relevant for formal parameters, external constants and exceptions). */ + static void ref_usage_found(Common::Assignment *ass); /** Appends a new field subref for the union type's @default alternative at the end of the reference. * Used when the reference points to a union value or template in a context where a union is not allowed. * This attempts to use the union's default alternative instead. */ @@ -1196,6 +1196,9 @@ namespace Ttcn { class Def_Exception : public Def_Var { private: + /** Indicates whether the exception is used in the catch block. */ + bool usage_found; + /// Copy constructor disabled Def_Exception(const Def_Exception& p); /// %Assignment disabled @@ -1203,6 +1206,7 @@ namespace Ttcn { public: Def_Exception(Identifier* p_id, Type* p_type); + void set_usage_found() { usage_found = true; } virtual char* generate_code_str(char *str); }; diff --git a/compiler2/ttcn3/Statement.cc b/compiler2/ttcn3/Statement.cc index 19661463caa5e9fa8aad53591ec1bec28aa57089..fb922fd6a808ca533844bb25569ae03342b8e155 100644 --- a/compiler2/ttcn3/Statement.cc +++ b/compiler2/ttcn3/Statement.cc @@ -60,6 +60,7 @@ namespace Ttcn { } catch_blocks.clear(); delete finally_block; + refd_local_defs.clear(); } StatementBlock *StatementBlock::clone() const @@ -284,6 +285,16 @@ namespace Ttcn { finally_block = p_finally; } + void StatementBlock::add_refd_local_def(Common::Assignment* p_def) + { + if (exception_handling != EH_OOP_FINALLY || p_def == NULL) { + FATAL_ERROR("StatementBlock::add_refd_local_defs"); + } + if (!refd_local_defs.has_key(p_def)) { + refd_local_defs.add(p_def, NULL); + } + } + boolean StatementBlock::is_in_finally_block() const { if (exception_handling == EH_OOP_FINALLY) { @@ -295,6 +306,17 @@ namespace Ttcn { return FALSE; } + StatementBlock* StatementBlock::get_finally_block() + { + if (exception_handling == EH_OOP_FINALLY) { + return this; + } + if (my_sb != NULL) { + return my_sb->get_finally_block(); + } + FATAL_ERROR("StatementBlock::get_finally_block()"); + } + boolean StatementBlock::is_empty() const { return stmts.size() == 0 && exception_handling == StatementBlock::EH_NONE && @@ -568,16 +590,85 @@ namespace Ttcn { if (finally_block != NULL) { string tmp_id = get_scope_mod_gen()->get_temporary_id(); #if __cplusplus < 201103L + // the finally block's code is generated into the destructor of a newly created class; + // all local definitions, parameters and the TTCN_Location object are added to the class as members, + // so the destructor can reach them str = mputprintf(str, "class %s_finally {\n" "public:\n", tmp_id.c_str()); - // TODO: all local declarations (or those referenced in the 'finally' block) need to - // be added as members of this class, so the destructor can reach them + if (include_location_info) { - str = mputprintf(str, - "TTCN_Location& current_location;\n" - "%s_finally(TTCN_Location& p_loc):current_location(p_loc) { }\n", - tmp_id.c_str()); + str = mputstr(str, "TTCN_Location& current_location;\n"); + } + for (size_t i = 0; i < finally_block->refd_local_defs.size(); ++i) { + bool is_const = false; + bool is_template = false; + Common::Assignment* def = finally_block->refd_local_defs.get_nth_key(i); + switch (def->get_asstype()) { + case Common::Assignment::A_PAR_TEMPL_IN: + case Common::Assignment::A_TEMPLATE: + is_template = true; + // fall through + case Common::Assignment::A_PAR_VAL: + case Common::Assignment::A_PAR_VAL_IN: + case Common::Assignment::A_CONST: + is_const = true; + break; + case Common::Assignment::A_VAR_TEMPLATE: + case Common::Assignment::A_PAR_TEMPL_INOUT: + case Common::Assignment::A_PAR_TEMPL_OUT: + is_template = true; + break; + default: + break; + } + str = mputprintf(str, "%s%s& %s;\n", + is_const ? "const " : "", + is_template ? def->get_Type()->get_genname_template(my_sb).c_str() : def->get_Type()->get_genname_value(my_sb).c_str(), + def->get_id().get_name().c_str()); + } + if (include_location_info || finally_block->refd_local_defs.size() > 0) { + str = mputprintf(str, "%s_finally(", tmp_id.c_str()); + if (include_location_info) { + str = mputstr(str, "TTCN_Location& p_loc"); + } + for (size_t i = 0; i < finally_block->refd_local_defs.size(); ++i) { + bool is_const = false; + bool is_template = false; + Common::Assignment* def = finally_block->refd_local_defs.get_nth_key(i); + switch (def->get_asstype()) { + case Common::Assignment::A_PAR_TEMPL_IN: + case Common::Assignment::A_TEMPLATE: + is_template = true; + // fall through + case Common::Assignment::A_PAR_VAL: + case Common::Assignment::A_PAR_VAL_IN: + case Common::Assignment::A_CONST: + is_const = true; + break; + case Common::Assignment::A_VAR_TEMPLATE: + case Common::Assignment::A_PAR_TEMPL_INOUT: + case Common::Assignment::A_PAR_TEMPL_OUT: + is_template = true; + break; + default: + break; + } + str = mputprintf(str, "%s%s%s& p_%s", + include_location_info || i > 0 ? ", " : "", is_const ? "const " : "", + is_template ? def->get_Type()->get_genname_template(my_sb).c_str() : def->get_Type()->get_genname_value(my_sb).c_str(), + def->get_id().get_name().c_str()); + } + str = mputstr(str, "): "); + if (include_location_info) { + str = mputstr(str, "current_location(p_loc)"); + } + for (size_t i = 0; i < finally_block->refd_local_defs.size(); ++i) { + Common::Assignment* def = finally_block->refd_local_defs.get_nth_key(i); + str = mputprintf(str, "%s%s(p_%s)", include_location_info || i > 0 ? ", " : "", + def->get_id().get_name().c_str(), def->get_id().get_name().c_str()); + } + str = mputstr(str, " { }\n"); } str = mputprintf(str, "~%s_finally() {\n" @@ -590,9 +681,20 @@ namespace Ttcn { "}\n" "}\n" "};\n" - "%s_finally %s%s;\n", - tmp_id.c_str(), tmp_id.c_str(), - include_location_info ? "(current_location)" : ""); + "%s_finally %s", + tmp_id.c_str(), tmp_id.c_str()); + if (include_location_info || finally_block->refd_local_defs.size() > 0) { + str = mputc(str, '('); + if (include_location_info) { + str = mputstr(str, "current_location"); + } + for (size_t i = 0; i < finally_block->refd_local_defs.size(); ++i) { + str = mputprintf(str, "%s%s", include_location_info || i > 0 ? ", " : "", + finally_block->refd_local_defs.get_nth_key(i)->get_id().get_name().c_str()); + } + str = mputc(str, ')'); + } + str = mputstr(str, ";\n"); #else // C++11 version: str = mputprintf(str, @@ -9701,8 +9803,6 @@ error: char *Assignment::generate_code(char *str) { - // check if the LHS reference is a parameter, mark it as used if it is - ref->ref_usage_found(); FieldOrArrayRefs *t_subrefs = ref->get_subrefs(); const bool rhs_copied = self_ref; switch (asstype) { diff --git a/compiler2/ttcn3/Statement.hh b/compiler2/ttcn3/Statement.hh index b90976b09758477909d620a3253e57f81c697d36..5571323964e0e0ae19454a025bad6b5e5c210c36 100644 --- a/compiler2/ttcn3/Statement.hh +++ b/compiler2/ttcn3/Statement.hh @@ -100,8 +100,13 @@ namespace Ttcn { Definition *my_def; /** */ exception_handling_t exception_handling; + /** List of catch blocks at the end of this statement block (OOP feature) */ vector<StatementBlock> catch_blocks; + /** Optional finally block at the end of this statement block (OOP feature) */ StatementBlock* finally_block; + /** List of local definitions referenced inside this block + * (only for the finally block of a normal statement block) */ + map<Common::Assignment*, void> refd_local_defs; // elements not owned StatementBlock(const StatementBlock& p); StatementBlock& operator=(const StatementBlock& p); @@ -132,7 +137,9 @@ 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); + void add_refd_local_def(Common::Assignment* p_def); boolean is_in_finally_block() const; + StatementBlock* get_finally_block(); boolean is_empty() const; returnstatus_t has_return() const; /** Used when generating code for interleaved statement. If has diff --git a/regression_test/oop/exceptions.ttcn b/regression_test/oop/exceptions.ttcn index 2556909639cc9ca7fb7833c05e8176406c776311..a47b3ef6b7ce0b878f38f80138becc970b8d0a78 100644 --- a/regression_test/oop/exceptions.ttcn +++ b/regression_test/oop/exceptions.ttcn @@ -46,7 +46,7 @@ type integer SmallNumber (-100..100); function test_block(in integer p_exception_type) runs on CT { - //var integer v_finally_increment := 1; + var integer v_finally_increment := 1; { select (p_exception_type) { case (1) { @@ -118,7 +118,10 @@ function test_block(in integer p_exception_type) runs on CT { } } finally { - cv_finally_counter := cv_finally_counter + 1; + cv_finally_counter := cv_finally_counter + v_finally_increment; // test referencing a local variable (in the main block) + if (cv_finally_counter > 0) { + log(p_exception_type); // test referencing a function parameter (in a sub-block) + } setverdict(pass); } }