From fcbf96e42b4cd89dcbdaac4a7d71a486613282a6 Mon Sep 17 00:00:00 2001 From: Botond Baranyi <botond.baranyi@ericsson.com> Date: Mon, 27 Feb 2017 10:54:26 +0100 Subject: [PATCH] Implemented the '@update' statement for changing erroneous attributes (bug 511903) Change-Id: I2fe9654e659c08cbd8415b4aa2cc5b6b52f67709 Signed-off-by: Botond Baranyi <botond.baranyi@ericsson.com> --- compiler2/Setting.cc | 17 + compiler2/Setting.hh | 19 +- compiler2/Type.hh | 3 +- compiler2/Type_codegen.cc | 21 +- compiler2/Value.cc | 13 +- compiler2/ttcn3/AST_ttcn3.cc | 100 ++++-- compiler2/ttcn3/AST_ttcn3.hh | 5 +- compiler2/ttcn3/Attributes.cc | 139 ++++++-- compiler2/ttcn3/Attributes.hh | 39 ++- compiler2/ttcn3/ILT.cc | 28 +- compiler2/ttcn3/ILT.hh | 10 +- compiler2/ttcn3/Statement.cc | 309 +++++++++++++----- compiler2/ttcn3/Statement.hh | 80 +++-- compiler2/ttcn3/TtcnTemplate.cc | 6 - compiler2/ttcn3/compiler.l | 2 +- compiler2/ttcn3/compiler.y | 22 +- .../Semantic_Analyser/Makefile.semantic | 2 +- .../erroneous_attributes/.gitignore | 2 + .../ErroneousAttributes_SE.ttcn | 178 ++++++++++ .../erroneous_attributes/Makefile | 12 + .../Semantic_Analyser/erroneous_attributes/t | 9 + regression_test/negativeTest/Makefile | 2 +- regression_test/negativeTest/NegTest_JSON.cfg | 1 + .../negativeTest/NegTest_JSON.ttcn | 24 ++ .../negativeTest/NegTest_Update.ttcn | 231 +++++++++++++ regression_test/negativeTest/NegTest_all.cfg | 1 + 26 files changed, 1072 insertions(+), 203 deletions(-) create mode 100644 function_test/Semantic_Analyser/erroneous_attributes/.gitignore create mode 100644 function_test/Semantic_Analyser/erroneous_attributes/ErroneousAttributes_SE.ttcn create mode 100644 function_test/Semantic_Analyser/erroneous_attributes/Makefile create mode 100755 function_test/Semantic_Analyser/erroneous_attributes/t create mode 100644 regression_test/negativeTest/NegTest_Update.ttcn diff --git a/compiler2/Setting.cc b/compiler2/Setting.cc index 83246cc7f..5dc5c31a3 100644 --- a/compiler2/Setting.cc +++ b/compiler2/Setting.cc @@ -36,6 +36,7 @@ #include "Int.hh" #include "main.hh" #include "ttcn3/profiler.h" +#include "ttcn3/Attributes.hh" namespace Common { @@ -481,6 +482,22 @@ namespace Common { // ================================= // ===== GovernedSimple // ================================= + + GovernedSimple::~GovernedSimple() + { + delete err_descrs; + } + + void GovernedSimple::add_err_descr(Ttcn::Statement* p_update_statement, + Ttcn::ErroneousDescriptor* p_err_descr) + { + if (p_err_descr != NULL) { + if (err_descrs == NULL) { + err_descrs = new Ttcn::ErroneousDescriptors; + } + err_descrs->add(p_update_statement, p_err_descr); + } + } string GovernedSimple::get_lhs_name() const { diff --git a/compiler2/Setting.hh b/compiler2/Setting.hh index c0f37a8fb..1c84988b8 100644 --- a/compiler2/Setting.hh +++ b/compiler2/Setting.hh @@ -57,7 +57,9 @@ namespace Ttcn { class RunsOnScope; class StatementBlock; struct ErroneousDescriptor; + class ErroneousDescriptors; class transparency_holder; + class Statement; } // namespace Ttcn namespace Common { @@ -466,18 +468,19 @@ public: */ bool code_generated; protected: // Derived classes need access to the copy c-tor - Ttcn::ErroneousDescriptor* err_descr; // not owned, used by negative testing + Ttcn::ErroneousDescriptors* err_descrs; // owned, used by negative testing GovernedSimple(const GovernedSimple& p) : Governed(p), genname_prefix(p.genname_prefix), code_section(p.code_section), - code_generated(false), err_descr(NULL), needs_conversion(false) { } + code_generated(false), err_descrs(NULL), needs_conversion(false) { } bool needs_conversion; /**< Type conversion needed. */ private: /** Assignment disabled */ GovernedSimple& operator=(const GovernedSimple& p); public: GovernedSimple(settingtype_t p_st) : Governed(p_st), genname_prefix(0), - code_section(CS_UNKNOWN), code_generated(false), err_descr(NULL), + code_section(CS_UNKNOWN), code_generated(false), err_descrs(NULL), needs_conversion(false) { } + ~GovernedSimple(); /** Sets attribute \a genname_prefix to \a p_genname_prefix. For efficiency * reasons the string itself is not copied, thus it must point to a @@ -498,11 +501,11 @@ public: /** Sets the flag \a code_generated to true. */ void set_code_generated() { code_generated = true; } - /** Sets the err_descr if the template or value has negative testing */ - void set_err_descr(Ttcn::ErroneousDescriptor* p_err_descr) - { err_descr = p_err_descr; } - Ttcn::ErroneousDescriptor* get_err_descr() const - { return err_descr; } + /** Adds an error descriptor to the template or value (for negative testing) */ + void add_err_descr(Ttcn::Statement* p_update_statement, + Ttcn::ErroneousDescriptor* p_err_descr); + Ttcn::ErroneousDescriptors* get_err_descr() const + { return err_descrs; } /** has_single_expr() to return false. */ inline void set_needs_conversion() { needs_conversion = true; } diff --git a/compiler2/Type.hh b/compiler2/Type.hh index 09173b9e6..0d7ab4ce4 100644 --- a/compiler2/Type.hh +++ b/compiler2/Type.hh @@ -1157,7 +1157,8 @@ namespace Common { * exported with name \a name. This function shall be used when there is no * Value or Template object in the AST (e.g. in case of variables). */ void generate_code_object(const_def *cdef, Scope* p_scope, - const string& name, const char *prefix, bool is_template); + const string& name, const char *prefix, bool is_template, + bool has_err_descr); /** Generates the declaration and definition of a C++ value or template * object governed by \a this into \a cdef based on the attributes of * \a p_setting. */ diff --git a/compiler2/Type_codegen.cc b/compiler2/Type_codegen.cc index c7246a7ac..e75ad4657 100644 --- a/compiler2/Type_codegen.cc +++ b/compiler2/Type_codegen.cc @@ -2820,7 +2820,7 @@ bool Type::has_done_attribute() } void Type::generate_code_object(const_def *cdef, Scope *p_scope, - const string& name, const char *prefix, bool is_template) + const string& name, const char *prefix, bool is_template, bool has_err_descr) { string type_name; if (is_template) type_name = get_genname_template(p_scope); @@ -2830,12 +2830,12 @@ void Type::generate_code_object(const_def *cdef, Scope *p_scope, if (prefix) { cdef->decl = mputprintf(cdef->decl, "extern const %s& %s;\n", type_name_str, name_str); - if (split_to_slices) { + if (split_to_slices || has_err_descr) { cdef->decl = mputprintf(cdef->decl, "extern %s %s%s;\n", type_name_str, prefix, name_str); } cdef->def = mputprintf(cdef->def, "%s%s %s%s;\n" - "const %s& %s = %s%s;\n", split_to_slices ? "" : "static ", type_name_str, prefix, name_str, - type_name_str, name_str, prefix, name_str); + "const %s& %s = %s%s;\n", split_to_slices || has_err_descr ? "" : "static ", + type_name_str, prefix, name_str, type_name_str, name_str, prefix, name_str); } else { cdef->decl = mputprintf(cdef->decl, "extern %s %s;\n", type_name_str, name_str); @@ -2856,13 +2856,18 @@ void Type::generate_code_object(const_def *cdef, GovernedSimple *p_setting) default: FATAL_ERROR("Type::generate_code_object()"); } - if (p_setting->get_err_descr()) { - cdef->def = p_setting->get_err_descr()->generate_code_str(cdef->def, cdef->decl, - p_setting->get_genname_prefix() + p_setting->get_genname_own(), FALSE); + if (p_setting->get_err_descr() != NULL && + p_setting->get_err_descr()->has_descr(NULL)) { + cdef->def = p_setting->get_err_descr()->generate_code_str(NULL, cdef->def, + cdef->decl, p_setting->get_genname_prefix() + p_setting->get_genname_own()); } + // allways pass 'true' to the 'has_err_descr' parameter while in Runtime2, not + // just if the value/template has erroneous descriptors, so adding an '@update' + // statement in a different module does not require this module's code to be + // regenerated generate_code_object(cdef, p_setting->get_my_scope(), p_setting->get_genname_own(), p_setting->get_genname_prefix(), - is_template); + is_template, use_runtime_2); } void Type::generate_json_schema(JSON_Tokenizer& json, bool embedded, bool as_value) diff --git a/compiler2/Value.cc b/compiler2/Value.cc index 4e56647c1..2784f2a63 100644 --- a/compiler2/Value.cc +++ b/compiler2/Value.cc @@ -10019,8 +10019,8 @@ error: default: break; } - if (v->err_descr) { // FIXME: make this work - v->err_descr->chk_recursions(refch); + if (v->err_descrs) { // FIXME: make this work + v->err_descrs->chk_recursions(refch); } } recurs_checked = true; @@ -11677,8 +11677,8 @@ error: char *Value::generate_code_init(char *str, const char *name) { if (get_code_generated()) return str; - if (err_descr) { - str = err_descr->generate_code_init_str(str, string(name) + "_err_descr"); + if (err_descrs != NULL && err_descrs->has_descr(NULL)) { + str = err_descrs->generate_code_init_str(NULL, str, string(name)); } switch (valuetype) { case V_NULL: @@ -11753,8 +11753,9 @@ error: default: FATAL_ERROR("Value::generate_code_init()"); } - if (err_descr) { - str = mputprintf(str, "%s.set_err_descr(&%s_err_descr);\n", name, name); + if (err_descrs != NULL && err_descrs->has_descr(NULL)) { + str = mputprintf(str, "%s.set_err_descr(&%s_%lu_err_descr);\n", name, + name, (unsigned long) err_descrs->get_descr_index(NULL)); } set_code_generated(); return str; diff --git a/compiler2/ttcn3/AST_ttcn3.cc b/compiler2/ttcn3/AST_ttcn3.cc index f3568746a..db440a8e8 100644 --- a/compiler2/ttcn3/AST_ttcn3.cc +++ b/compiler2/ttcn3/AST_ttcn3.cc @@ -2171,7 +2171,8 @@ namespace Ttcn { "debug_scope.initial_snapshot();\n", module_dispname); } target->functions.control = - block->generate_code(target->functions.control); + block->generate_code(target->functions.control, target->header.global_vars, + target->source.global_vars); target->functions.control = mputstr(target->functions.control, "TTCN_Runtime::end_controlpart();\n"); } @@ -3112,18 +3113,21 @@ namespace Ttcn { return false; } - void Definition::chk_erroneous_attr() + ErroneousAttributes* Definition::chk_erroneous_attr(WithAttribPath* p_attrib_path, Type* p_type, + Scope* p_scope, string p_fullname, + bool in_update_stmt) { - if (!w_attrib_path) return; - const Ttcn::MultiWithAttrib* attribs = w_attrib_path->get_local_attrib(); - if (!attribs) return; + if (!p_attrib_path) return NULL; + const Ttcn::MultiWithAttrib* attribs = p_attrib_path->get_local_attrib(); + if (!attribs) return NULL; + ErroneousAttributes* erroneous_attrs = NULL; for (size_t i = 0; i < attribs->get_nof_elements(); i++) { const Ttcn::SingleWithAttrib* act_attr = attribs->get_element(i); if (act_attr->get_attribKeyword()==Ttcn::SingleWithAttrib::AT_ERRONEOUS) { if (!use_runtime_2) { - error("`erroneous' attributes can be used only with the Function Test Runtime"); - note("If you need negative testing use the -R flag when generating the makefile"); - return; + attribs->error("`erroneous' attributes can be used only with the Function Test Runtime"); + attribs->note("If you need negative testing use the -R flag when generating the makefile"); + return NULL; } size_t nof_qualifiers = act_attr->get_attribQualifiers() ? act_attr->get_attribQualifiers()->get_nof_qualifiers() : 0; dynamic_array<Type*> refd_type_array(nof_qualifiers); // only the qualifiers pointing to existing fields will be added to erroneous_attrs objects @@ -3133,12 +3137,12 @@ namespace Ttcn { // check if qualifiers point to existing fields for (size_t qi=0; qi<nof_qualifiers; qi++) { Qualifier* act_qual = const_cast<Qualifier*>(act_attr->get_attribQualifiers()->get_qualifier(qi)); - act_qual->set_my_scope(get_my_scope()); - Type* field_type = get_Type()->get_field_type(act_qual, Type::EXPECTED_CONSTANT); + act_qual->set_my_scope(p_scope); + Type* field_type = p_type->get_field_type(act_qual, Type::EXPECTED_CONSTANT); if (field_type) { dynamic_array<size_t> subrefs_array; dynamic_array<Type*> type_array; - bool valid_indexes = get_Type()->get_subrefs_as_array(act_qual, subrefs_array, type_array); + bool valid_indexes = p_type->get_subrefs_as_array(act_qual, subrefs_array, type_array); if (!valid_indexes) field_type = NULL; if (act_qual->refers_to_string_element()) { act_qual->error("Reference to a string element cannot be used in this context"); @@ -3152,12 +3156,12 @@ namespace Ttcn { ErroneousAttributeSpec* err_attr_spec = ttcn3_parse_erroneous_attr_spec_string( act_attr->get_attribSpec().get_spec().c_str(), act_attr->get_attribSpec()); if (err_attr_spec) { - if (!erroneous_attrs) erroneous_attrs = new ErroneousAttributes(get_Type()); + if (!erroneous_attrs) erroneous_attrs = new ErroneousAttributes(p_type); // attr.spec will be owned by erroneous_attrs object erroneous_attrs->add_spec(err_attr_spec); - err_attr_spec->set_fullname(get_fullname()); - err_attr_spec->set_my_scope(get_my_scope()); - err_attr_spec->chk(); + err_attr_spec->set_fullname(p_fullname); + err_attr_spec->set_my_scope(p_scope); + err_attr_spec->chk(in_update_stmt); // create qualifier - err.attr.spec. pairs for (size_t qi=0; qi<nof_qualifiers; qi++) { if (refd_type_array[qi] && (err_attr_spec->get_indicator()!=ErroneousAttributeSpec::I_INVALID)) { @@ -3168,6 +3172,7 @@ namespace Ttcn { } } if (erroneous_attrs) erroneous_attrs->chk(); + return erroneous_attrs; } char* Definition::generate_code_str(char *str) @@ -3546,8 +3551,9 @@ namespace Ttcn { type->chk_this_value(value, 0, Type::EXPECTED_CONSTANT, INCOMPLETE_ALLOWED, OMIT_NOT_ALLOWED, SUB_CHK, has_implicit_omit_attr()); value_under_check = false; - chk_erroneous_attr(); - if (erroneous_attrs) value->set_err_descr(erroneous_attrs->get_err_descr()); + erroneous_attrs = chk_erroneous_attr(w_attrib_path, type, get_my_scope(), + get_fullname(), false); + if (erroneous_attrs) value->add_err_descr(NULL, erroneous_attrs->get_err_descr()); { ReferenceChain refch(type, "While checking embedded recursions"); value->chk_recursions(refch); @@ -3878,7 +3884,7 @@ namespace Ttcn { Code::init_cdef(&cdef); const string& t_genname = get_genname(); const char *name = t_genname.c_str(); - type->generate_code_object(&cdef, my_scope, t_genname, "modulepar_", false); + type->generate_code_object(&cdef, my_scope, t_genname, "modulepar_", false, false); if (def_value) { cdef.init = update_location_object(cdef.init); cdef.init = def_value->generate_code_init(cdef.init, def_value->get_lhs_name().c_str()); @@ -4053,7 +4059,7 @@ namespace Ttcn { Code::init_cdef(&cdef); const string& t_genname = get_genname(); const char *name = t_genname.c_str(); - type->generate_code_object(&cdef, my_scope, t_genname, "modulepar_", true); + type->generate_code_object(&cdef, my_scope, t_genname, "modulepar_", true, false); if (def_template) { cdef.init = update_location_object(cdef.init); cdef.init = def_template->generate_code_init(cdef.init, def_template->get_lhs_name().c_str()); @@ -4233,8 +4239,9 @@ namespace Ttcn { ANY_OR_OMIT_ALLOWED, SUB_CHK, has_implicit_omit_attr() ? IMPLICIT_OMIT : NOT_IMPLICIT_OMIT, 0); - chk_erroneous_attr(); - if (erroneous_attrs) body->set_err_descr(erroneous_attrs->get_err_descr()); + erroneous_attrs = chk_erroneous_attr(w_attrib_path, type, get_my_scope(), + get_fullname(), false); + if (erroneous_attrs) body->add_err_descr(NULL, erroneous_attrs->get_err_descr()); { ReferenceChain refch(type, "While checking embedded recursions"); @@ -4465,6 +4472,10 @@ namespace Ttcn { void Def_Template::generate_code(output_struct *target, bool) { type->generate_code(target); + if (body->get_err_descr() != NULL && body->get_err_descr()->has_descr(NULL)) { + target->functions.post_init = body->get_err_descr()->generate_code_init_str( + NULL, target->functions.post_init, body->get_lhs_name()); + } if (fp_list) { // Parameterized template. Generate code for a function which returns // a $(genname)_template and has the appropriate parameters. @@ -4504,10 +4515,6 @@ namespace Ttcn { function_body = mputprintf(function_body, "%s ret_val;\n", type_genname_str); } - if (erroneous_attrs && erroneous_attrs->get_err_descr()) { - function_body = erroneous_attrs->get_err_descr()-> - generate_code_str(function_body, target->header.global_vars, string("ret_val"), true); - } function_body = body->generate_code_init(function_body, "ret_val"); if (template_restriction!=TR_NONE && gen_restriction_check) function_body = Template::generate_restriction_check_code(function_body, @@ -4517,6 +4524,28 @@ namespace Ttcn { "ttcn3_debugger.set_return_value((TTCN_Logger::begin_event_log2str(), " "ret_val.log(), TTCN_Logger::end_event_log2str()));\n"); } + if (ErroneousDescriptors::can_have_err_attribs(type)) { + // these are always generated, not just if the template has erroneous + // descriptors, so adding '@update' statements in other modules does not + // require this module's code to be regenerated + target->source.global_vars = mputprintf(target->source.global_vars, + "Erroneous_descriptor_t* %s_err_descr_ptr = NULL;\n", + body->get_lhs_name().c_str()); + target->header.global_vars = mputprintf(target->header.global_vars, + "extern Erroneous_descriptor_t* %s_err_descr_ptr;\n", + body->get_lhs_name().c_str()); + function_body = mputprintf(function_body, + "ret_val.set_err_descr(%s_err_descr_ptr);\n", + body->get_lhs_name().c_str()); + } + if (body->get_err_descr() != NULL && body->get_err_descr()->has_descr(NULL)) { + target->source.global_vars = body->get_err_descr()->generate_code_str(NULL, + target->source.global_vars, target->header.global_vars, body->get_lhs_name()); + target->functions.post_init = mputprintf(target->functions.post_init, + "%s_err_descr_ptr = &%s_%lu_err_descr;\n", + body->get_lhs_name().c_str(), body->get_lhs_name().c_str(), + (unsigned long) body->get_err_descr()->get_descr_index(NULL)); + } function_body = mputstr(function_body, "return ret_val;\n"); // if the template modifies a parameterized template, then the inherited // formal parameters must always be displayed, otherwise generate a smart @@ -4585,6 +4614,11 @@ namespace Ttcn { if (template_restriction != TR_NONE && gen_restriction_check) cdef.init = Template::generate_restriction_check_code(cdef.init, body->get_lhs_name().c_str(), template_restriction); + if (body->get_err_descr() != NULL && body->get_err_descr()->has_descr(NULL)) { + cdef.init = mputprintf(cdef.init, "%s.set_err_descr(&%s_%lu_err_descr);\n", + body->get_lhs_name().c_str(), body->get_lhs_name().c_str(), + (unsigned long) body->get_err_descr()->get_descr_index(NULL)); + } target->header.global_vars = mputstr(target->header.global_vars, cdef.decl); target->source.global_vars = mputstr(target->source.global_vars, @@ -4849,7 +4883,7 @@ namespace Ttcn { type->generate_code(target); const_def cdef; Code::init_cdef(&cdef); - type->generate_code_object(&cdef, my_scope, get_genname(), 0, false); + type->generate_code_object(&cdef, my_scope, get_genname(), 0, false, false); Code::merge_cdef(target, &cdef); Code::free_cdef(&cdef); if (initial_value) { @@ -5062,7 +5096,7 @@ namespace Ttcn { type->generate_code(target); const_def cdef; Code::init_cdef(&cdef); - type->generate_code_object(&cdef, my_scope, get_genname(), 0, true); + type->generate_code_object(&cdef, my_scope, get_genname(), 0, true, false); Code::merge_cdef(target, &cdef); Code::free_cdef(&cdef); if (initial_value) { @@ -6344,7 +6378,8 @@ namespace Ttcn { if (debugger_active) { body = generate_code_debugger_function_init(body, this); } - body = block->generate_code(body); + body = block->generate_code(body, target->header.global_vars, + target->source.global_vars); // smart formal parameter list (names of unused parameters are omitted) char *formal_par_list = fp_list->generate_code(memptystr()); fp_list->generate_code_defval(target); @@ -7488,8 +7523,10 @@ namespace Ttcn { if (debugger_active) { body = generate_code_debugger_function_init(body, this); } - body = sb->generate_code(body); - body = ags->generate_code_altstep(body); + body = sb->generate_code(body, target->header.global_vars, + target->source.global_vars); + body = ags->generate_code_altstep(body, target->header.global_vars, + target->source.global_vars); // generate a smart formal parameter list (omits unused parameter names) char *formal_par_list = fp_list->generate_code(memptystr()); fp_list->generate_code_defval(target); @@ -7772,7 +7809,8 @@ namespace Ttcn { body = system_type->get_CompBody()->generate_code_comptype_name(body); else body = runs_on_body->generate_code_comptype_name(body); body = mputstr(body, ", has_timer, timer_value);\n"); - body = block->generate_code(body); + body = block->generate_code(body, target->header.global_vars, + target->source.global_vars); body = mputprintf(body, "} catch (const TC_Error& tc_error) {\n" "} catch (const TC_End& tc_end) {\n" diff --git a/compiler2/ttcn3/AST_ttcn3.hh b/compiler2/ttcn3/AST_ttcn3.hh index ff46e62b1..de7ac9ffa 100644 --- a/compiler2/ttcn3/AST_ttcn3.hh +++ b/compiler2/ttcn3/AST_ttcn3.hh @@ -888,8 +888,9 @@ namespace Ttcn { */ virtual bool chk_identical(Definition *p_def); /** Parse and check the erroneous attribute data, - * sets erroneous_attrs member */ - void chk_erroneous_attr(); + * returns erroneous attributes or NULL */ + static ErroneousAttributes* chk_erroneous_attr(WithAttribPath* p_attrib_path, + Type* p_type, Scope* p_scope, string p_fullname, bool in_update_stmt); /** This code generation is used when this definition is embedded * in a statement block. */ virtual char* generate_code_str(char *str); diff --git a/compiler2/ttcn3/Attributes.cc b/compiler2/ttcn3/Attributes.cc index 042096ef2..c291857c4 100644 --- a/compiler2/ttcn3/Attributes.cc +++ b/compiler2/ttcn3/Attributes.cc @@ -23,6 +23,7 @@ #include "../Type.hh" #include "../main.hh" #include "TtcnTemplate.hh" +#include "Statement.hh" namespace Ttcn { @@ -226,7 +227,7 @@ namespace Ttcn { return (tmpl_inst->get_Template()->get_templatetype()==Template::OMIT_VALUE); } - void ErroneousAttributeSpec::chk() + void ErroneousAttributeSpec::chk(bool in_update_stmt) { if (get_is_omit()) { // special case, no type needed if ((indicator==I_BEFORE)||(indicator==I_AFTER)) { @@ -290,7 +291,10 @@ namespace Ttcn { value = templ->get_Value(); value->set_my_governor(type); type->chk_this_value_ref(value); - type->chk_this_value(value, 0, Type::EXPECTED_CONSTANT, + // dynamic values are allowed for attribute specs in '@update' statements, + // but not for the initial attribute specs of constants and templates + type->chk_this_value(value, 0, in_update_stmt ? + Type::EXPECTED_DYNAMIC_VALUE : Type::EXPECTED_CONSTANT, INCOMPLETE_NOT_ALLOWED, OMIT_NOT_ALLOWED, SUB_CHK); //{ FIXME: make this work // ReferenceChain refch(type, "While checking embedded recursions"); @@ -315,21 +319,21 @@ namespace Ttcn { return ""; } - char* ErroneousAttributeSpec::generate_code_str(char *str, char *& def, string genname, const bool embedded) + char* ErroneousAttributeSpec::generate_code_str(char *str, char *& def, string genname) { if (get_is_omit()) return str; if (!type) FATAL_ERROR("ErroneousAttributeSpec::generate_code_str()"); if (!value) FATAL_ERROR("ErroneousAttributeSpec::generate_code_str()"); if (first_genname.empty()) { // this is the first use - str = mputprintf(str, "%s%s %s;\n", split_to_slices && !embedded ? "" : "static ", + str = mputprintf(str, "%s%s %s;\n", split_to_slices ? "" : "static ", type->get_genname_value(value->get_my_scope()).c_str(), genname.c_str()); first_genname = genname; - if (split_to_slices && !embedded) { + if (split_to_slices) { def = mputprintf(def, "extern %s %s;\n", type->get_genname_value(value->get_my_scope()).c_str(), genname.c_str()); } } else { - str = mputprintf(str, "%s%s& %s = %s;\n", split_to_slices && !embedded ? "" : "static ", + str = mputprintf(str, "%s& %s = %s;\n", type->get_genname_value(value->get_my_scope()).c_str(), genname.c_str(), first_genname.c_str()); } @@ -364,22 +368,23 @@ namespace Ttcn { // ==== ErroneousValues ==== - char* ErroneousValues::generate_code_embedded_str(char *str, char *& def, string genname, const bool embedded) + char* ErroneousValues::generate_code_embedded_str(char *str, char *& def, string genname) { - if (before) str = generate_code_embedded_str(str, def, genname+"_before", before, embedded); - if (value) str = generate_code_embedded_str(str, def, genname+"_value", value, embedded); - if (after) str = generate_code_embedded_str(str, def, genname+"_after", after, embedded); + if (before) str = generate_code_embedded_str(str, def, genname+"_before", before); + if (value) str = generate_code_embedded_str(str, def, genname+"_value", value); + if (after) str = generate_code_embedded_str(str, def, genname+"_after", after); return str; } - char* ErroneousValues::generate_code_embedded_str(char *str, char *& def, string genname, ErroneousAttributeSpec* attr_spec, const bool embedded) + char* ErroneousValues::generate_code_embedded_str(char *str, char *& def, string genname, ErroneousAttributeSpec* attr_spec) { - str = attr_spec->generate_code_str(str, def, genname+"_errval", embedded); - str = mputprintf(str, "%sErroneous_value_t %s = { %s, %s, %s };\n", split_to_slices && !embedded ? "" : "static ", genname.c_str(), + str = attr_spec->generate_code_str(str, def, genname+"_errval"); + str = mputprintf(str, "%sErroneous_value_t %s = { %s, %s, %s };\n", + split_to_slices ? "" : "static ", genname.c_str(), attr_spec->get_is_raw() ? "true" : "false", attr_spec->get_is_omit() ? "NULL" : ("&"+genname+"_errval").c_str(), attr_spec->get_typedescriptor_str().c_str()); - if (split_to_slices && !embedded) { + if (split_to_slices) { def = mputprintf(def, "extern Erroneous_value_t %s;\n", genname.c_str()); } return str; @@ -412,39 +417,41 @@ namespace Ttcn { // ==== ErroneousDescriptor ==== - char* ErroneousDescriptor::generate_code_embedded_str(char *str, char *& def, string genname, const bool embedded) + char* ErroneousDescriptor::generate_code_embedded_str(char *str, char *& def, string genname) { // values for (size_t i=0; i<values_m.size(); i++) { - str = values_m.get_nth_elem(i)->generate_code_embedded_str(str, def, genname+"_v"+Int2string((int)values_m.get_nth_key(i)), embedded); + str = values_m.get_nth_elem(i)->generate_code_embedded_str(str, def, genname+"_v"+Int2string((int)values_m.get_nth_key(i))); } // embedded descriptors for (size_t i=0; i<descr_m.size(); i++) { - str = descr_m.get_nth_elem(i)->generate_code_embedded_str(str, def, genname+"_d"+Int2string((int)descr_m.get_nth_key(i)), embedded); + str = descr_m.get_nth_elem(i)->generate_code_embedded_str(str, def, genname+"_d"+Int2string((int)descr_m.get_nth_key(i))); } // values vector if (values_m.size()>0) { - str = mputprintf(str, "%sErroneous_values_t %s_valsvec[%d] = { ", split_to_slices && !embedded ? "" : "static ", genname.c_str(), (int)values_m.size()); + str = mputprintf(str, "%sErroneous_values_t %s_valsvec[%d] = { ", + split_to_slices ? "" : "static ", genname.c_str(), (int)values_m.size()); for (size_t i=0; i<values_m.size(); i++) { if (i>0) str = mputstr(str, ", "); int key_i = (int)values_m.get_nth_key(i); str = values_m.get_nth_elem(i)->generate_code_struct_str(str, genname+"_v"+Int2string(key_i), key_i); } str = mputstr(str, " };\n"); - if (split_to_slices && !embedded) { + if (split_to_slices) { def = mputprintf(def, "extern Erroneous_values_t %s_valsvec[%d];\n", genname.c_str(), (int)values_m.size()); } } // embedded descriptor vector if (descr_m.size()>0) { - str = mputprintf(str, "%sErroneous_descriptor_t %s_embvec[%d] = { ", split_to_slices && !embedded ? "" : "static ", genname.c_str(), (int)descr_m.size()); + str = mputprintf(str, "%sErroneous_descriptor_t %s_embvec[%d] = { ", + split_to_slices ? "" : "static ", genname.c_str(), (int)descr_m.size()); for (size_t i=0; i<descr_m.size(); i++) { if (i>0) str = mputstr(str, ", "); int key_i = (int)descr_m.get_nth_key(i); str = descr_m.get_nth_elem(i)->generate_code_struct_str(str, def, genname+"_d"+Int2string(key_i), key_i); } str = mputstr(str, " };\n"); - if (split_to_slices && !embedded) { + if (split_to_slices) { def = mputprintf(def, "extern Erroneous_descriptor_t %s_embvec[%d];\n", genname.c_str(), (int)descr_m.size()); } } @@ -486,18 +493,83 @@ namespace Ttcn { } } - char* ErroneousDescriptor::generate_code_str(char *str, char *& def, string genname, const bool embedded) + char* ErroneousDescriptor::generate_code_str(char *str, char *& def, string genname) { genname += "_err_descr"; - str = generate_code_embedded_str(str, def, genname, embedded); - str = mputprintf(str, "%sErroneous_descriptor_t %s = ", split_to_slices && !embedded ? "" : "static ", genname.c_str()); + str = generate_code_embedded_str(str, def, genname); + str = mputprintf(str, "%sErroneous_descriptor_t %s = ", + split_to_slices ? "" : "static ", genname.c_str()); str = generate_code_struct_str(str, def, genname, -1); str = mputstr(str, ";\n"); - if (split_to_slices && !embedded) { + if (split_to_slices) { def = mputprintf(def, "extern Erroneous_descriptor_t %s;\n", genname.c_str()); } return str; } + + ErroneousDescriptors::~ErroneousDescriptors() + { + descr_map.clear(); + } + + void ErroneousDescriptors::add(Statement* p_update_statement, ErroneousDescriptor* p_descr) + { + descr_map.add(p_update_statement, p_descr); + } + + bool ErroneousDescriptors::has_descr(Statement* p_update_statement) + { + return descr_map.has_key(p_update_statement); + } + + size_t ErroneousDescriptors::get_descr_index(Statement* p_update_statement) + { + return descr_map.find_key(p_update_statement); + } + + char* ErroneousDescriptors::generate_code_init_str(Statement* p_update_statement, char *str, string genname) + { + size_t i = descr_map.find_key(p_update_statement); + return descr_map.get_nth_elem(i)->generate_code_init_str(str, + genname + string("_") + Int2string((int)i) + string("_err_descr")); + } + + char* ErroneousDescriptors::generate_code_str(Statement* p_update_statement, char *str, char *& def, string genname) + { + size_t i = descr_map.find_key(p_update_statement); + return descr_map.get_nth_elem(i)->generate_code_str(str, def, + genname + string("_") + Int2string((int)i)); + } + + void ErroneousDescriptors::chk_recursions(ReferenceChain& refch) + { + for (size_t i = 0; i < descr_map.size(); ++i) { + descr_map.get_nth_elem(i)->chk_recursions(refch); + } + } + + boolean ErroneousDescriptors::can_have_err_attribs(Type* t) + { + if (t == NULL) { + FATAL_ERROR("ErroneousDescriptors::can_have_err_attribs"); + } + t = t->get_type_refd_last(); + switch (t->get_typetype_ttcn3()) { + case Type::T_SEQ_T: + case Type::T_SET_T: + if (t->get_nof_comps() == 0) { + // empty records/sets can't have erroneous attributes + return FALSE; + } + // else fall through + case Type::T_SEQOF: + case Type::T_SETOF: + case Type::T_CHOICE_T: + return use_runtime_2; + default: + return FALSE; + } + } // ==== ErroneousAttributes ==== @@ -1016,6 +1088,23 @@ namespace Ttcn { attributes_checked = true; } + + void WithAttribPath::chk_only_erroneous() + { + if (attributes_checked || m_w_attrib == NULL) { + return; + } + + for (size_t i = 0; i < m_w_attrib->get_nof_elements(); ++i) { + const SingleWithAttrib* attrib = m_w_attrib->get_element(i); + if (attrib->get_attribKeyword() != SingleWithAttrib::AT_ERRONEOUS) { + attrib->error("Only `erroneous' attributes are allowed in an `@update' " + "statement"); + } + } + + attributes_checked = true; + } void WithAttribPath::dump(unsigned int level) const { diff --git a/compiler2/ttcn3/Attributes.hh b/compiler2/ttcn3/Attributes.hh index 41e4faa9b..7b4048f22 100644 --- a/compiler2/ttcn3/Attributes.hh +++ b/compiler2/ttcn3/Attributes.hh @@ -33,6 +33,7 @@ namespace Ttcn { class Group; class Def_Template; class TemplateInstance; + class Statement; /** Attribute qualifier (DefOrFieldRef). */ class Qualifier: public FieldOrArrayRefs, public Location @@ -113,13 +114,13 @@ namespace Ttcn { void set_my_scope(Scope *p_scope); void dump(unsigned level) const; /** basic check, the qualifier of the field is not known here */ - void chk(); + void chk(bool in_update_stmt); indicator_t get_indicator() const { return indicator; } Type* get_type() const { return type; } bool get_is_raw() const { return is_raw; } bool get_is_omit() const; static const char* get_indicator_str(indicator_t i); - char* generate_code_str(char *str, char *& def, string genname, const bool embedded); + char* generate_code_str(char *str, char *& def, string genname); char* generate_code_init_str(char *str, string genname); string get_typedescriptor_str(); void chk_recursions(ReferenceChain& refch); @@ -132,9 +133,9 @@ namespace Ttcn { ErroneousAttributeSpec *before, *value, *after; // NULL if not specified string field_name; // qualifier string ErroneousValues(const string& p_field_name): before(0), value(0), after(0), field_name(p_field_name) {} - char* generate_code_embedded_str(char *str, char *& def, string genname, const bool embedded); + char* generate_code_embedded_str(char *str, char *& def, string genname); char* generate_code_init_str(char *str, string genname); - char* generate_code_embedded_str(char *str, char *& def, string genname, ErroneousAttributeSpec* attr_spec, const bool embedded); + char* generate_code_embedded_str(char *str, char *& def, string genname, ErroneousAttributeSpec* attr_spec); char* generate_code_struct_str(char *str, string genname, int field_index); void chk_recursions(ReferenceChain& refch); }; @@ -152,11 +153,36 @@ namespace Ttcn { public: ErroneousDescriptor(): omit_before(-1), omit_after(-1) {} ~ErroneousDescriptor(); - char* generate_code_embedded_str(char *str, char *& def, string genname, const bool embedded); + char* generate_code_embedded_str(char *str, char *& def, string genname); char* generate_code_init_str(char *str, string genname); char* generate_code_struct_str(char *str, char *& def, string genname, int field_index); - char* generate_code_str(char *str, char *& def, string genname, const bool embedded); + char* generate_code_str(char *str, char *& def, string genname); + void chk_recursions(ReferenceChain& refch); + }; + + /** + * helper to construct several trees of erroneous attributes + * (contains the set of erroneous attributes set at initialization and the + * sets of erroneous attributes set by '@update' statements) + */ + class ErroneousDescriptors { + /** Map of erroneous descriptors + * Key: pointer to the '@update' statement or NULL (for the erroneous + * attributes specified at initialization) + * Values not owned */ + map<Statement*, ErroneousDescriptor> descr_map; + ErroneousDescriptors(const ErroneousDescriptor& p); // disabled + ErroneousDescriptors& operator=(const ErroneousDescriptors& p); // disabled + public: + ErroneousDescriptors() {} + ~ErroneousDescriptors(); + void add(Statement* p_update_statement, ErroneousDescriptor* p_descr); + bool has_descr(Statement* p_update_statement); + size_t get_descr_index(Statement* p_update_statement); + char* generate_code_init_str(Statement* p_update_statement, char *str, string genname); + char* generate_code_str(Statement* p_update_statement, char *str, char *& def, string genname); void chk_recursions(ReferenceChain& refch); + static boolean can_have_err_attribs(Type* t); }; /** @@ -319,6 +345,7 @@ namespace Ttcn { void set_had_global_variants(bool has) { had_global_variants = has; } bool get_had_global_variants() { return had_global_variants; } void chk_no_qualif(); + void chk_only_erroneous(); void chk_global_attrib(bool erroneous_allowed=false); void set_parent(WithAttribPath* p_parent) { parent = p_parent; } WithAttribPath* get_parent() { return parent; } diff --git a/compiler2/ttcn3/ILT.cc b/compiler2/ttcn3/ILT.cc index a80f004b1..edd9c8186 100644 --- a/compiler2/ttcn3/ILT.cc +++ b/compiler2/ttcn3/ILT.cc @@ -48,8 +48,9 @@ namespace Ttcn { // ===== ILT_root // ================================= - ILT_root::ILT_root(Statement *p_il) - : il(p_il), tmpnum(0), c_l(0), c_b(0) + ILT_root::ILT_root(Statement *p_il, char*& p_def_glob_vars, char*& p_src_glob_vars) + : il(p_il), tmpnum(0), c_l(0), c_b(0), out_def_glob_vars(p_def_glob_vars) + , out_src_glob_vars(p_src_glob_vars) { if(!p_il || p_il->get_statementtype()!=Statement::S_INTERLEAVE) FATAL_ERROR("ILT_root::ILT_root()"); @@ -100,6 +101,16 @@ namespace Ttcn { { return out_branches; } + + char*& ILT_root::get_out_def_glob_vars() + { + return out_def_glob_vars; + } + + char*& ILT_root::get_out_src_glob_vars() + { + return out_src_glob_vars; + } const string& ILT_root::get_my_tmpid() { @@ -293,6 +304,16 @@ namespace Ttcn { { return root->get_out_branches(); } + + char*& ILT_branch::get_out_def_glob_vars() + { + return root->get_out_def_glob_vars(); + } + + char*& ILT_branch::get_out_src_glob_vars() + { + return root->get_out_src_glob_vars(); + } const string& ILT_branch::get_my_tmpid() { @@ -411,7 +432,8 @@ namespace Ttcn { } else { out_stmt=mputstr(out_stmt, "{\n"); // (2) - out_stmt=block->generate_code(out_stmt); + out_stmt=block->generate_code(out_stmt, get_out_def_glob_vars(), + get_out_src_glob_vars()); } if(branchtype==BT_IL) { out_stmt=mputprintf(out_stmt, "%s_state[%lu]=1;\n", diff --git a/compiler2/ttcn3/ILT.hh b/compiler2/ttcn3/ILT.hh index fd9394253..5957cb45f 100644 --- a/compiler2/ttcn3/ILT.hh +++ b/compiler2/ttcn3/ILT.hh @@ -52,6 +52,8 @@ namespace Ttcn { virtual char*& get_out_def() = 0; virtual char*& get_out_code() = 0; virtual char*& get_out_branches() = 0; + virtual char*& get_out_def_glob_vars() = 0; + virtual char*& get_out_src_glob_vars() = 0; virtual const string& get_my_tmpid() = 0; virtual size_t get_new_tmpnum() = 0; virtual size_t get_new_state_var(bool toplevel) = 0; @@ -78,6 +80,8 @@ namespace Ttcn { char *out_statevars; // 0: unused; 1: ready; 2: toplevel (IL) etc char *out_code; char *out_branches; + char*& out_def_glob_vars; + char*& out_src_glob_vars; private: /** Copy constructor not implemented */ ILT_root(const ILT_root& p); @@ -85,7 +89,7 @@ namespace Ttcn { ILT_root& operator=(const ILT_root& p); public: /** Constructor; the parameter should be an InterleavedStatement */ - ILT_root(Statement *p_il); + ILT_root(Statement *p_il, char*& p_def_glob_vars, char*& p_src_glob_vars); virtual ~ILT_root(); virtual ILT_root *clone() const; virtual ILT_root* get_my_root(); @@ -93,6 +97,8 @@ namespace Ttcn { virtual char*& get_out_def(); virtual char*& get_out_code(); virtual char*& get_out_branches(); + virtual char*& get_out_def_glob_vars(); + virtual char*& get_out_src_glob_vars(); virtual const string& get_my_tmpid(); virtual size_t get_new_tmpnum(); virtual size_t get_new_state_var(bool toplevel); @@ -151,6 +157,8 @@ namespace Ttcn { virtual char*& get_out_def(); virtual char*& get_out_code(); virtual char*& get_out_branches(); + virtual char*& get_out_def_glob_vars(); + virtual char*& get_out_src_glob_vars(); virtual const string& get_my_tmpid(); virtual size_t get_new_tmpnum(); virtual size_t get_new_state_var(bool toplevel); diff --git a/compiler2/ttcn3/Statement.cc b/compiler2/ttcn3/Statement.cc index a8dd88b60..f972355ed 100644 --- a/compiler2/ttcn3/Statement.cc +++ b/compiler2/ttcn3/Statement.cc @@ -420,14 +420,14 @@ namespace Ttcn { stmts[i]->set_code_section(p_code_section); } - char* StatementBlock::generate_code(char *str) + char* StatementBlock::generate_code(char *str, char*& def_glob_vars, char*& src_glob_vars) { if (exception_handling==EH_TRY) { str = mputstr(str, "TTCN_TryBlock try_block;\n"); } if (stmts.size()>0) { Statement* first_stmt = stmts[0]; - str = first_stmt->generate_code(str); + str = first_stmt->generate_code(str, def_glob_vars, src_glob_vars); if (exception_handling==EH_CATCH) { if (first_stmt->get_statementtype()!=Statement::S_DEF) FATAL_ERROR("StatementBlock::generate_code()"); Definition* error_msg_def = first_stmt->get_def(); @@ -436,7 +436,7 @@ namespace Ttcn { } } for(size_t i=1; i<stmts.size(); i++) { - str = stmts[i]->generate_code(str); + str = stmts[i]->generate_code(str, def_glob_vars, src_glob_vars); } return str; } @@ -452,7 +452,8 @@ namespace Ttcn { bool has_def=has_def_stmt_i(); if(has_def) str=mputstr(str, "{\n"); for(size_t i=0; i<nof_stmts; i++) - str=stmts[i]->generate_code(str); + str=stmts[i]->generate_code(str, ilt->get_out_def_glob_vars(), + ilt->get_out_src_glob_vars()); if(has_def) str=mputstr(str, "}\n"); return; } @@ -463,7 +464,8 @@ namespace Ttcn { bool has_def=has_def_stmt_i(last_recv_stmt_i+1); if(has_def) str=mputstr(str, "{\n"); for(size_t i=last_recv_stmt_i+1; i<nof_stmts; i++) - str=stmts[i]->generate_code(str); + str=stmts[i]->generate_code(str, ilt->get_out_def_glob_vars(), + ilt->get_out_src_glob_vars()); if(has_def) str=mputstr(str, "}\n"); } @@ -711,6 +713,11 @@ namespace Ttcn { delete convert_op.val; delete convert_op.ref; break; + case S_UPDATE: + delete update_op.ref; + delete update_op.w_attrib_path; + delete update_op.err_attrib; + break; default: FATAL_ERROR("Statement::clean_up()"); } // switch statementtype @@ -1434,6 +1441,29 @@ namespace Ttcn { FATAL_ERROR("Statement::Statement()"); } } + + Statement::Statement(statementtype_t p_st, Reference* p_ref, MultiWithAttrib* p_attrib) + : statementtype(p_st), my_sb(0) + { + switch (statementtype) { + case S_UPDATE: + if (p_ref == NULL) { + FATAL_ERROR("Statement::Statement()"); + } + update_op.ref = p_ref; + if (p_attrib != NULL) { + update_op.w_attrib_path = new WithAttribPath; + update_op.w_attrib_path->set_with_attr(p_attrib); + } + else { + update_op.w_attrib_path = NULL; + } + update_op.err_attrib = NULL; + break; + default: + FATAL_ERROR("Statement::Statement()"); + } + } Statement::~Statement() { @@ -1563,6 +1593,7 @@ namespace Ttcn { case S_INT2ENUM: return "int2enum"; case S_START_PROFILER: return "@profiler.start"; case S_STOP_PROFILER: return "@profiler.stop"; + case S_UPDATE: return "@update"; default: FATAL_ERROR("Statement::get_stmt_name()"); return ""; @@ -1886,6 +1917,12 @@ namespace Ttcn { convert_op.val->set_my_scope(p_scope); convert_op.ref->set_my_scope(p_scope); break; + case S_UPDATE: + update_op.ref->set_my_scope(p_scope); + if (update_op.w_attrib_path != NULL) { + update_op.w_attrib_path->set_my_scope(p_scope); + } + break; default: FATAL_ERROR("Statement::set_my_scope()"); } // switch statementtype @@ -2164,6 +2201,12 @@ namespace Ttcn { convert_op.val->set_fullname(p_fullname+".ti"); convert_op.ref->set_fullname(p_fullname+".ref"); break; + case S_UPDATE: + update_op.ref->set_fullname(p_fullname + ".ref"); + if (update_op.w_attrib_path != NULL) { + update_op.w_attrib_path->set_fullname(p_fullname + ".<attribpath>"); + } + break; default: FATAL_ERROR("Statement::set_fullname()"); } // switch statementtype @@ -2453,6 +2496,7 @@ namespace Ttcn { case S_START_PROFILER: case S_STOP_PROFILER: case S_INT2ENUM: + case S_UPDATE: return false; case S_ALT: case S_INTERLEAVE: @@ -2707,6 +2751,9 @@ namespace Ttcn { case S_STOP_PROFILER: // do nothing break; + case S_UPDATE: + chk_update(); + break; default: FATAL_ERROR("Statement::chk()"); } // switch statementtype @@ -5531,6 +5578,49 @@ error: return 0; } } + + void Statement::chk_update() + { + Error_Context cntxt(this, "In @update statement"); + if (!use_runtime_2) { + error("The @update statement is only available in the Function Test " + "runtime"); + return; + } + Common::Assignment* refd_ass = update_op.ref->get_refd_assignment(false); + Type* ref_type = NULL; + if (refd_ass != NULL) { + switch (refd_ass->get_asstype()) { + case Definition::A_CONST: + case Definition::A_TEMPLATE: + break; // OK + default: + update_op.ref->error("Reference to constant or template definition was " + "expected instead of %s", refd_ass->get_assname()); + return; + } + ref_type = refd_ass->get_Type(); + if (ref_type != NULL && + !ErroneousDescriptors::can_have_err_attribs(ref_type)) { + update_op.ref->error("Type `%s' cannot have erroneous attributes", + ref_type->get_typename().c_str()); + } + if (update_op.w_attrib_path != NULL) { + update_op.w_attrib_path->chk_only_erroneous(); + update_op.err_attrib = Definition::chk_erroneous_attr(update_op.w_attrib_path, + ref_type, my_sb, get_fullname(), true); + if (update_op.err_attrib != NULL) { + GovernedSimple* refd_obj = static_cast<GovernedSimple*>( + refd_ass->get_Setting()); + refd_obj->add_err_descr(this, update_op.err_attrib->get_err_descr()); + } + } + } + if (update_op.ref->get_subrefs() != NULL) { + update_op.ref->error("Field names and array indexes are not allowed in " + "this context"); + } + } void Statement::set_code_section( GovernedSimple::code_section_t p_code_section) @@ -5786,12 +5876,15 @@ error: convert_op.val->set_code_section(p_code_section); convert_op.ref->set_code_section(p_code_section); break; + case S_UPDATE: + update_op.ref->set_code_section(p_code_section); + break; default: FATAL_ERROR("Statement::set_code_section()"); } // switch statementtype } - char *Statement::generate_code(char *str) + char *Statement::generate_code(char *str, char*& def_glob_vars, char*& src_glob_vars) { switch (statementtype) { case S_BLOCK: @@ -5824,7 +5917,7 @@ error: str=generate_code_invoke(str); break; case S_BLOCK: - str=generate_code_block(str); + str=generate_code_block(str, def_glob_vars, src_glob_vars); break; case S_LOG: str=generate_code_log(str); @@ -5836,22 +5929,22 @@ error: str = generate_code_goto(str); break; case S_IF: - str=generate_code_if(str); + str=generate_code_if(str, def_glob_vars, src_glob_vars); break; case S_SELECT: - str=generate_code_select(str); + str=generate_code_select(str, def_glob_vars, src_glob_vars); break; case S_SELECTUNION: - str=generate_code_select_union(str); + str=generate_code_select_union(str, def_glob_vars, src_glob_vars); break; case S_FOR: - str=generate_code_for(str); + str=generate_code_for(str, def_glob_vars, src_glob_vars); break; case S_WHILE: - str=generate_code_while(str); + str=generate_code_while(str, def_glob_vars, src_glob_vars); break; case S_DOWHILE: - str=generate_code_dowhile(str); + str=generate_code_dowhile(str, def_glob_vars, src_glob_vars); break; case S_BREAK: str=generate_code_break(str); @@ -5866,13 +5959,13 @@ error: str=generate_code_testcase_stop(str); break; case S_ALT: - str=ags->generate_code_alt(str, *this); + str=ags->generate_code_alt(str, def_glob_vars, src_glob_vars, *this); break; case S_REPEAT: str=generate_code_repeat(str); break; case S_INTERLEAVE: - str=generate_code_interleave(str); + str=generate_code_interleave(str, def_glob_vars, src_glob_vars); break; case S_RETURN: str=generate_code_return(str); @@ -5890,7 +5983,7 @@ error: str = generate_code_send(str); break; case S_CALL: - str = generate_code_call(str); + str = generate_code_call(str, def_glob_vars, src_glob_vars); break; case S_REPLY: str = generate_code_reply(str); @@ -5979,6 +6072,9 @@ error: case S_STOP_PROFILER: str = mputstr(str, "ttcn3_prof.stop();\n"); break; + case S_UPDATE: + str = generate_code_update(str, def_glob_vars, src_glob_vars); + break; default: FATAL_ERROR("Statement::generate_code()"); } // switch @@ -6120,7 +6216,7 @@ error: } if (!has_receiving_stmt()) { char*& str=ilt->get_out_branches(); - str=generate_code(str); + str=generate_code(str, ilt->get_out_def_glob_vars(), ilt->get_out_src_glob_vars()); return; } switch (statementtype) { @@ -6235,7 +6331,7 @@ error: return str; } - char *Statement::generate_code_block(char *str) + char *Statement::generate_code_block(char *str, char*& def_glob_vars, char*& src_glob_vars) { switch (block->get_exception_handling()) { case StatementBlock::EH_NONE: @@ -6254,7 +6350,7 @@ error: if (debugger_active) { str = mputstr(str, "TTCN3_Debug_Scope debug_scope;\n"); } - str = block->generate_code(str); + str = block->generate_code(str, def_glob_vars, src_glob_vars); str = mputstr(str, "}\n"); } else str = mputstr(str, "/* empty block */;\n"); return str; @@ -6331,11 +6427,12 @@ error: return str; } - char* Statement::generate_code_if(char *str) + char* Statement::generate_code_if(char *str, char*& def_glob_vars, char*& src_glob_vars) { size_t blockcount=0; bool unreach=false, eachfalse=true; - str=if_stmt.ics->generate_code(str, blockcount, unreach, eachfalse); + str=if_stmt.ics->generate_code(str, def_glob_vars, src_glob_vars, + blockcount, unreach, eachfalse); if(if_stmt.elseblock && !unreach) { if(!eachfalse) str=mputstr(str, "else "); eachfalse=false; @@ -6344,14 +6441,14 @@ error: str = mputstr(str, "TTCN3_Debug_Scope debug_scope;\n"); } blockcount++; - str=if_stmt.elseblock->generate_code(str); + str=if_stmt.elseblock->generate_code(str, def_glob_vars, src_glob_vars); } while(blockcount-->0) str=mputstr(str, "}\n"); if(eachfalse) str=mputstr(str, "/* never occurs */;\n"); return str; } - char* Statement::generate_code_select(char *str) + char* Statement::generate_code_select(char *str, char*& def_glob_vars, char*& src_glob_vars) { const string& tmp_prefix = my_sb->get_scope_mod_gen()->get_temporary_id(); char *expr_init=memptystr(); @@ -6377,10 +6474,10 @@ error: gen_switch_code = select.scs->can_generate_switch(); } if (gen_switch_code) { - str=select.scs->generate_code_switch(str, tmp_id.c_str()); + str=select.scs->generate_code_switch(str, def_glob_vars, src_glob_vars, tmp_id.c_str()); } else { - str=select.scs->generate_code(str, tmp_prefix.c_str(), tmp_id.c_str()); + str=select.scs->generate_code(str, def_glob_vars, src_glob_vars, tmp_prefix.c_str(), tmp_id.c_str()); } Free(expr_name); str=mputstr(str, "}\n"); @@ -6388,7 +6485,7 @@ error: return str; } - char* Statement::generate_code_select_union(char *str) + char* Statement::generate_code_select_union(char *str, char*& def_glob_vars, char*& src_glob_vars) { expression_struct expr; Code::init_expr(&expr); @@ -6400,7 +6497,7 @@ error: const char* type_name = select_union.expr->get_expr_governor_last()->get_genname_value(select_union.expr->get_my_scope()).c_str(); char* loc = NULL; loc = select_union.expr->update_location_object(loc); - str = select_union.sus->generate_code(str, type_name, loc); + str = select_union.sus->generate_code(str, def_glob_vars, src_glob_vars, type_name, loc); str = mputstr(str, "}\n"); if (expr.postamble) { str = mputstr(str, expr.postamble); @@ -6410,7 +6507,7 @@ error: return str; } - char *Statement::generate_code_for(char *str) + char *Statement::generate_code_for(char *str, char*& def_glob_vars, char*& src_glob_vars) { /** \todo initial does not have its own location */ // statements in initial may have side effects @@ -6451,7 +6548,7 @@ error: if (debugger_active) { str = mputstr(str, "TTCN3_Debug_Scope debug_scope;\n"); } - str = loop.block->generate_code(str); + str = loop.block->generate_code(str, def_glob_vars, src_glob_vars); if (loop.label_next) str = mputprintf(str, "}\n" "%s:\n", loop.label_next->c_str()); @@ -6463,7 +6560,7 @@ error: return str; } - char *Statement::generate_code_while(char *str) + char *Statement::generate_code_while(char *str, char*& def_glob_vars, char*& src_glob_vars) { // check whether the expression is constant bool condition_always_true = false, condition_always_false = false; @@ -6490,13 +6587,13 @@ error: if (debugger_active) { str = mputstr(str, "TTCN3_Debug_Scope debug_scope;\n"); } - str = loop.block->generate_code(str); + str = loop.block->generate_code(str, def_glob_vars, src_glob_vars); str = mputstr(str, "}\n"); } return str; } - char *Statement::generate_code_dowhile(char *str) + char *Statement::generate_code_dowhile(char *str, char*& def_glob_vars, char*& src_glob_vars) { // check whether the expression is constant bool expr_is_const = !loop.expr->is_unfoldable(); @@ -6510,7 +6607,7 @@ error: if (debugger_active) { str = mputstr(str, "TTCN3_Debug_Scope debug_scope;\n"); } - str = loop.block->generate_code(str); + str = loop.block->generate_code(str, def_glob_vars, src_glob_vars); } else { str = mputstr(str, "for ( ; ; ) {\n"); if (loop.has_cnt_in_ags || (!expr_is_const && loop.has_cnt)) @@ -6522,7 +6619,7 @@ error: if (debugger_active) { str = mputstr(str, "TTCN3_Debug_Scope debug_scope;\n"); } - str = loop.block->generate_code(str); + str = loop.block->generate_code(str, def_glob_vars, src_glob_vars); // do not generate the exit condition for infinite loops if (!is_infinite_loop) { if (loop.label_next) @@ -6592,9 +6689,9 @@ error: return str; } - char* Statement::generate_code_interleave(char *str) + char* Statement::generate_code_interleave(char *str, char*& def_glob_vars, char*& src_glob_vars) { - ILT_root ilt(this); + ILT_root ilt(this, def_glob_vars, src_glob_vars); str=ilt.generate_code(str); return str; } @@ -6776,7 +6873,8 @@ error: const char* type_name = select_union.expr->get_expr_governor_last()->get_genname_value(select_union.expr->get_my_scope()).c_str(); char* loc = NULL; loc = select_union.expr->update_location_object(loc); - str = select_union.sus->generate_code(str, type_name, loc); + str = select_union.sus->generate_code(str, ilt->get_out_def_glob_vars(), + ilt->get_out_src_glob_vars(), type_name, loc); str = mputstr(str, "}\n"); if (expr.postamble) { str = mputstr(str, expr.postamble); @@ -6817,8 +6915,9 @@ error: // the label name is used for prefixing local variables if(!my_sb) FATAL_ERROR("Statement::generate_code_call()"); const string& tmplabel = my_sb->get_scope_mod_gen()->get_temporary_id(); - str = port_op.s.call.body->generate_code_call_body(str, *this, tmplabel, - true); + str = port_op.s.call.body->generate_code_call_body(str, + ilt->get_out_def_glob_vars(), ilt->get_out_src_glob_vars(), *this, + tmplabel, true); const char *label_str = tmplabel.c_str(); str=mputprintf(str, "goto %s_end;\n" "}\n", // (1) @@ -7078,7 +7177,7 @@ error: return Code::merge_free_expr(str, &expr); } - char *Statement::generate_code_call(char *str) + char *Statement::generate_code_call(char *str, char*& def_glob_vars, char*& src_glob_vars) { expression_struct expr; Code::init_expr(&expr); @@ -7104,7 +7203,7 @@ error: } // the label name is used for prefixing local variables if(!my_sb) FATAL_ERROR("Statement::generate_code_call()"); - str = port_op.s.call.body->generate_code_call_body(str, *this, + str = port_op.s.call.body->generate_code_call_body(str, def_glob_vars, src_glob_vars, *this, my_sb->get_scope_mod_gen()->get_temporary_id(), false); str=mputstr(str, "}\n"); } @@ -7385,6 +7484,60 @@ error: } else expr.expr = mputstr(expr.expr, "FALSE, 0.0)"); return Code::merge_free_expr(str,&expr); } + + char *Statement::generate_code_update(char *str, char*& def_glob_vars, char*& src_glob_vars) + { + Common::Assignment* refd_ass = update_op.ref->get_refd_assignment(false); + GovernedSimple* refd_obj = static_cast<GovernedSimple*>( + refd_ass->get_Setting()); + + // namespace prefix, in case the value/template was declared in another module + string prefix; + if (my_sb->get_scope_mod_gen() != refd_obj->get_my_scope()->get_scope_mod_gen()) { + prefix = refd_obj->get_my_scope()->get_scope_mod_gen()->get_modid().get_name() + + string("::"); + } + if (refd_obj->get_err_descr()->has_descr(this)) { + // the statement has erroneous attributes + // generate them to the global scope, so they remain active even after the + // '@update' statement's scope ends + src_glob_vars = refd_obj->get_err_descr()->generate_code_str(this, + src_glob_vars, def_glob_vars, refd_obj->get_lhs_name()); + + // generate the descriptor's initialization code to the local scope, since + // it may depend on local variables + str = refd_obj->get_err_descr()->generate_code_init_str(this, str, + refd_obj->get_lhs_name()); + if (refd_ass->get_FormalParList() != NULL) { + // a global descriptor pointer is used for parameterized templates, since + // there is no global constant or template to store the descriptor's + // address in + str = mputprintf(str, "%s%s_err_descr_ptr = &%s_%lu_err_descr;\n", + prefix.c_str(), refd_obj->get_lhs_name().c_str(), + refd_obj->get_lhs_name().c_str(), + (unsigned long) refd_obj->get_err_descr()->get_descr_index(this)); + } + else { + // store the descriptor's address in the constant/template + str = mputprintf(str, "%s%s.set_err_descr(&%s_%lu_err_descr);\n", + prefix.c_str(), refd_obj->get_lhs_name().c_str(), + refd_obj->get_lhs_name().c_str(), + (unsigned long) refd_obj->get_err_descr()->get_descr_index(this)); + } + } + else { + // remove the previously stored descriptor's address (if any) + if (refd_ass->get_FormalParList() != NULL) { + str = mputprintf(str, "%s%s_err_descr_ptr = NULL;\n", prefix.c_str(), + refd_obj->get_lhs_name().c_str()); + } + else { + str = mputprintf(str, "%s%s.set_err_descr(NULL);\n", prefix.c_str(), + refd_obj->get_lhs_name().c_str()); + } + } + return str; + } void Statement::generate_code_expr_receive(expression_struct *expr, const char *opname) @@ -7963,6 +8116,7 @@ error: case S_START_PROFILER: case S_STOP_PROFILER: case S_INT2ENUM: + case S_UPDATE: break; default: FATAL_ERROR("Statement::set_parent_path()"); @@ -10988,8 +11142,8 @@ error: block->set_code_section(p_code_section); } - char* IfClause::generate_code(char *str, size_t& blockcount, - bool& unreach, bool& eachfalse) + char* IfClause::generate_code(char *str, char*& def_glob_vars, char*& src_glob_vars, + size_t& blockcount, bool& unreach, bool& eachfalse) { if(unreach) return str; if(!expr->is_unfoldable()) { @@ -11011,7 +11165,7 @@ error: if (debugger_active) { str = mputstr(str, "TTCN3_Debug_Scope debug_scope;\n"); } - str=block->generate_code(str); + str=block->generate_code(str, def_glob_vars, src_glob_vars); str=mputstr(str, "}\n"); return str; } @@ -11182,12 +11336,12 @@ error: ics[i]->set_code_section(p_code_section); } - char* IfClauses::generate_code(char *str, size_t& blockcount, - bool& unreach, bool& eachfalse) + char* IfClauses::generate_code(char *str, char*& def_glob_vars, char*& src_glob_vars, + size_t& blockcount, bool& unreach, bool& eachfalse) { for(size_t i=0; i<ics.size(); i++) { if(unreach) return str; - str=ics[i]->generate_code(str, blockcount, unreach, eachfalse); + str=ics[i]->generate_code(str, def_glob_vars, src_glob_vars, blockcount, unreach, eachfalse); } return str; } @@ -11333,8 +11487,9 @@ error: return str; } - char* SelectCase::generate_code_case(char *str, bool &else_branch, - vector<const Int>& used_numbers) { + char* SelectCase::generate_code_case(char *str, char*& def_glob_vars, char*& src_glob_vars, + bool &else_branch, + vector<const Int>& used_numbers) { bool already_present_all = true; // to decide if we need to generate the block if (tis != NULL) { for (size_t i = 0; i < tis->get_nof_tis(); i++) { @@ -11360,14 +11515,15 @@ error: } if (!already_present_all) { str = mputstr(str, "{\n"); - str = block->generate_code(str); + str = block->generate_code(str, def_glob_vars, src_glob_vars); str = mputstr(str, "break;\n}\n"); } return str; } /** \todo review */ - char* SelectCase::generate_code_stmt(char *str, const char *tmp_prefix, + char* SelectCase::generate_code_stmt(char *str, char*& def_glob_vars, char*& src_glob_vars, + const char *tmp_prefix, size_t idx, bool& unreach) { if(unreach) return str; @@ -11376,7 +11532,7 @@ error: if (debugger_active) { str = mputstr(str, "TTCN3_Debug_Scope debug_scope;\n"); } - str=block->generate_code(str); + str=block->generate_code(str, def_glob_vars, src_glob_vars); str=mputprintf(str, "goto %s_end;\n}\n", tmp_prefix); return str; } @@ -11391,7 +11547,8 @@ error: bool has_recv=block->has_receiving_stmt(); if(!has_recv) { str=mputstr(str, "{\n"); - str=block->generate_code(str); + str=block->generate_code(str, ilt->get_out_def_glob_vars(), + ilt->get_out_src_glob_vars()); } else block->ilt_generate_code(ilt); str=mputprintf(str, "goto %s_end;\n", tmp_prefix); @@ -11534,8 +11691,8 @@ error: scs[i]->set_code_section(p_code_section); } - char* SelectCases::generate_code(char *str, const char *tmp_prefix, - const char *expr_name) + char* SelectCases::generate_code(char *str, char*& def_glob_vars, char*& src_glob_vars, + const char *tmp_prefix, const char *expr_name) { bool unreach=false; for(size_t i=0; i<scs.size(); i++) { @@ -11545,20 +11702,21 @@ error: if(!unreach) str=mputprintf(str, "goto %s_end;\n", tmp_prefix); unreach=false; for(size_t i=0; i<scs.size(); i++) { - str=scs[i]->generate_code_stmt(str, tmp_prefix, i, unreach); + str=scs[i]->generate_code_stmt(str, def_glob_vars, src_glob_vars, tmp_prefix, i, unreach); if(unreach) break; } str=mputprintf(str, "%s_end: /* empty */;\n", tmp_prefix); return str; } - char* SelectCases::generate_code_switch(char *str, const char *expr_name) + char* SelectCases::generate_code_switch(char *str, char*& def_glob_vars, char*& src_glob_vars, + const char *expr_name) { bool else_branch=false; vector<const Int> used_numbers; // store the case values to remove duplicates str=mputprintf(str, "switch(%s.get_long_long_val()) {\n", expr_name); for(size_t i=0; i<scs.size(); i++) { - str=scs[i]->generate_code_case(str, else_branch, used_numbers); + str=scs[i]->generate_code_case(str, def_glob_vars, src_glob_vars, else_branch, used_numbers); if(else_branch) break; } str=mputprintf(str, "};"); @@ -11604,7 +11762,8 @@ error: vector<const Int> used_numbers; // store the case values to remove duplicates str=mputprintf(str, "switch(%s.get_long_long_val()) {\n", expr_name); for(size_t i=0; i<scs.size(); i++) { - str=scs[i]->generate_code_case(str, else_branch, used_numbers); + str=scs[i]->generate_code_case(str, ilt->get_out_def_glob_vars(), + ilt->get_out_src_glob_vars(), else_branch, used_numbers); if(else_branch) break; } str=mputprintf(str, "};"); @@ -11671,7 +11830,8 @@ error: block->set_code_section(p_code_section); } - char* SelectUnion::generate_code_case(char *str, const char *type_name, bool &else_branch) + char* SelectUnion::generate_code_case(char *str, char*& def_glob_vars, char*& src_glob_vars, + const char *type_name, bool &else_branch) { if (ids.size() != 0) { for (size_t i = 0; i < ids.size(); i++) { @@ -11684,7 +11844,7 @@ error: } str = mputstr(str, "{\n"); - str = block->generate_code(str); + str = block->generate_code(str, def_glob_vars, src_glob_vars); str = mputstr(str, "break;\n}\n"); return str; } @@ -11820,7 +11980,8 @@ error: } } - char* SelectUnions::generate_code(char *str, const char *type_name, const char *loc) + char* SelectUnions::generate_code(char *str, char*& def_glob_vars, char*& src_glob_vars, + const char *type_name, const char *loc) { str = mputprintf(str, "case(%s::UNBOUND_VALUE):\n", type_name); str = mputprintf(str, "%s", loc); @@ -11828,7 +11989,7 @@ error: str = mputstr(str, "break;\n"); bool else_branch = false; for (size_t i = 0; i < sus.size(); i++) { - str = sus[i]->generate_code_case(str, type_name, else_branch); + str = sus[i]->generate_code_case(str, def_glob_vars, src_glob_vars, type_name, else_branch); } if (!else_branch) { str = mputstr(str, "default:\nbreak;\n"); @@ -12369,7 +12530,8 @@ error: ags[i]->set_code_section(p_code_section); } - char *AltGuards::generate_code_alt(char *str, const Location& loc) + char *AltGuards::generate_code_alt(char *str, char*& def_glob_vars, char*& src_glob_vars, + const Location& loc) { bool label_needed = has_repeat, has_else_branch = false; for (size_t i = 0; i < ags.size(); i++) { @@ -12429,7 +12591,7 @@ error: if (debugger_active) { str = mputstr(str, "TTCN3_Debug_Scope debug_scope;\n"); } - str = block->generate_code(str); + str = block->generate_code(str, def_glob_vars, src_glob_vars); str = mputstr(str, "}\n"); } // jump out of the infinite for() loop @@ -12511,7 +12673,7 @@ error: if (debugger_active) { str = mputstr(str, "TTCN3_Debug_Scope debug_scope;\n"); } - str = block->generate_code(str); + str = block->generate_code(str, def_glob_vars, src_glob_vars); if (block->has_return() != StatementBlock::RS_YES) str = mputstr(str, "break;\n"); str = mputstr(str, "}\n"); @@ -12552,7 +12714,7 @@ error: return str; } - char *AltGuards::generate_code_altstep(char *str) + char *AltGuards::generate_code_altstep(char *str, char*& def_glob_vars, char*& src_glob_vars) { if (!my_scope) FATAL_ERROR("AltGuards::generate_code_altstep()"); Common::Module *my_mod = my_scope->get_scope_mod_gen(); @@ -12572,7 +12734,7 @@ error: if (debugger_active) { str = mputstr(str, "TTCN3_Debug_Scope debug_scope;\n"); } - str = block->generate_code(str); + str = block->generate_code(str, def_glob_vars, src_glob_vars); str = mputstr(str, "}\n"); } if (block->has_return() != StatementBlock::RS_YES) @@ -12647,7 +12809,7 @@ error: if (debugger_active) { str = mputstr(str, "TTCN3_Debug_Scope debug_scope;\n"); } - str = block->generate_code(str); + str = block->generate_code(str, def_glob_vars, src_glob_vars); str = mputstr(str, "}\n"); } if (!block || block->has_return() != StatementBlock::RS_YES) @@ -12673,8 +12835,9 @@ error: return str; } - char* AltGuards::generate_code_call_body(char *str, const Location& loc, - const string& temp_id, bool in_interleave) + char* AltGuards::generate_code_call_body(char *str, char*& def_glob_vars, char*& src_glob_vars, + const Location& loc, + const string& temp_id, bool in_interleave) { if (label) FATAL_ERROR("AltGuards::generate_code_call_body()"); label = new string(temp_id); @@ -12737,7 +12900,7 @@ error: } else { str = mputstr(str, "{\n"); // (3) - str = block->generate_code(str); + str = block->generate_code(str, def_glob_vars, src_glob_vars); str = mputprintf(str, "goto %s_end;\n" "}\n", // (3) label_str); @@ -12748,7 +12911,7 @@ error: else { if (block && block->get_nof_stmts() > 0) { str = mputstr(str, "{\n"); // (3) - str = block->generate_code(str); + str = block->generate_code(str, def_glob_vars, src_glob_vars); if (block->has_return() != StatementBlock::RS_YES) str = mputstr(str, "break;\n"); str = mputstr(str, "}\n"); // (3) diff --git a/compiler2/ttcn3/Statement.hh b/compiler2/ttcn3/Statement.hh index 4dc6555ab..5d27cb5cf 100644 --- a/compiler2/ttcn3/Statement.hh +++ b/compiler2/ttcn3/Statement.hh @@ -58,6 +58,8 @@ namespace Ttcn { /* not defined here: */ class ILT; class ILT_branch; + class WithAttribPath; + class ErroneousAttributes; /** * Represents a %StatementBlock. @@ -156,7 +158,12 @@ namespace Ttcn { /** Sets the code section selector of all embedded values and * templates to \a p_code_section. */ void set_code_section(GovernedSimple::code_section_t p_code_section); - char* generate_code(char *str); + /** Generates code for this statement block. + * '@update' statements may need to generate code into the global variables + * sections of the module's header and source file, so pointer references to + * these strings are passed to every statement block that may contain an + * '@update' statement. */ + char* generate_code(char *str, char*& def_glob_vars, char*& src_glob_vars); void ilt_generate_code(ILT *ilt); virtual void set_parent_path(WithAttribPath* p_path); @@ -258,7 +265,9 @@ namespace Ttcn { S_STOP_PROFILER, /* Conversion statements */ S_STRING2TTCN, // convert_op - S_INT2ENUM // convert_op + S_INT2ENUM, // convert_op + /* update statement */ + S_UPDATE // update_op }; enum component_t { @@ -466,6 +475,12 @@ namespace Ttcn { Value* val; Reference* ref; } convert_op; + + struct { + Reference* ref; + WithAttribPath* w_attrib_path; + ErroneousAttributes* err_attrib; + } update_op; /**< S_UPDATE */ }; Statement(const Statement& p); ///< copy disabled @@ -589,6 +604,8 @@ namespace Ttcn { TemplateInstances *p_ap_list, Value *p_val); /** Constructor used by S_STRING2TTCN, S_INT2ENUM */ Statement(statementtype_t p_st, Value* p_val, Reference* p_ref); + /** Constructor used by S_UPDATE */ + Statement(statementtype_t p_st, Reference* p_ref, MultiWithAttrib* p_attrib); virtual ~Statement(); virtual Statement* clone() const; virtual void dump(unsigned int level) const; @@ -768,11 +785,12 @@ namespace Ttcn { bool allow_system); void chk_string2ttcn(); void chk_int2enum(); + void chk_update(); public: /** Sets the code section selector of all embedded values and * templates to \a p_code_section. */ void set_code_section(GovernedSimple::code_section_t p_code_section); - char* generate_code(char *str); + char* generate_code(char *str, char*& def_glob_vars, char*& src_glob_vars); void generate_code_expr(expression_struct *expr); void ilt_generate_code(ILT *ilt); @@ -792,22 +810,22 @@ namespace Ttcn { /** used for function and altstep instances */ char *generate_code_funcinst(char *str); char *generate_code_invoke(char *str); - char *generate_code_block(char *str); + char *generate_code_block(char *str, char*& def_glob_vars, char*& src_glob_vars); char *generate_code_log(char *str); char* generate_code_string2ttcn(char *str); char *generate_code_testcase_stop(char *str); char *generate_code_label(char *str); char *generate_code_goto(char *str); - char *generate_code_if(char *str); - char *generate_code_select(char *str); - char *generate_code_select_union(char *str); - char *generate_code_for(char *str); - char *generate_code_while(char *str); - char *generate_code_dowhile(char *str); + char *generate_code_if(char *str, char*& def_glob_vars, char*& src_glob_vars); + char *generate_code_select(char *str, char*& def_glob_vars, char*& src_glob_vars); + char *generate_code_select_union(char *str, char*& def_glob_vars, char*& src_glob_vars); + char *generate_code_for(char *str, char*& def_glob_vars, char*& src_glob_vars); + char *generate_code_while(char *str, char*& def_glob_vars, char*& src_glob_vars); + char *generate_code_dowhile(char *str, char*& def_glob_vars, char*& src_glob_vars); char *generate_code_break(char *str); char *generate_code_continue(char *str); char *generate_code_repeat(char *str); - char *generate_code_interleave(char *str); + char *generate_code_interleave(char *str, char*& def_glob_vars, char*& src_glob_vars); void ilt_generate_code_interleave(ILT *ilt); void ilt_generate_code_alt(ILT *ilt); void ilt_generate_code_receiving(ILT *ilt); @@ -824,7 +842,7 @@ namespace Ttcn { char *generate_code_activate_refd(char *str); char *generate_code_deactivate(char *str); char *generate_code_send(char *str); - char *generate_code_call(char *str); + char *generate_code_call(char *str, char*& def_glob_vars, char*& src_glob_vars); char *generate_code_reply(char *str); char *generate_code_raise(char *str); char *generate_code_portop(char *str, const char *opname); @@ -838,6 +856,7 @@ namespace Ttcn { char *generate_code_action(char *str); char *generate_code_testcaseinst(char *str); char *generate_code_execute_refd(char *str); + char* generate_code_update(char *str, char*& def_glob_vars, char*& src_glob_vars); /** used for receive, check-receive, trigger */ void generate_code_expr_receive(expression_struct *expr, const char *opname); @@ -1292,8 +1311,8 @@ namespace Ttcn { /** Sets the code section selector of all embedded values and * templates to \a p_code_section. */ void set_code_section(GovernedSimple::code_section_t p_code_section); - char* generate_code(char *str, size_t& blockcount, - bool& unreach, bool& eachfalse); + char* generate_code(char *str, char*& def_glob_vars, char*& src_glob_vars, + size_t& blockcount, bool& unreach, bool& eachfalse); void ilt_generate_code(ILT *ilt, const char *end_label, bool& unreach); /** Needed by implicit omit. Pushes attrib path down to definitions @@ -1337,8 +1356,8 @@ namespace Ttcn { /** Sets the code section selector of all embedded values and * templates to \a p_code_section. */ void set_code_section(GovernedSimple::code_section_t p_code_section); - char* generate_code(char *str, size_t& blockcount, - bool& unreach, bool& eachfalse); + char* generate_code(char *str, char*& def_glob_vars, char*& src_glob_vars, + size_t& blockcount, bool& unreach, bool& eachfalse); void ilt_generate_code(ILT *ilt, const char *end_label, bool& unreach); /** Needed by implicit omit. Pushes attrib path down to definitions @@ -1374,10 +1393,10 @@ namespace Ttcn { void set_code_section(GovernedSimple::code_section_t p_code_section); char* generate_code_if(char *str, const char *tmp_prefix, const char *expr_name, size_t idx, bool& unreach); - char* generate_code_case(char *str, bool& else_branch, - vector<const Int>& used_numbers); - char* generate_code_stmt(char *str, const char *tmp_prefix, - size_t idx, bool& unreach); + char* generate_code_case(char *str, char*& def_glob_vars, char*& src_glob_vars, + bool& else_branch, vector<const Int>& used_numbers); + char* generate_code_stmt(char *str, char*& def_glob_vars, char*& src_glob_vars, + const char *tmp_prefix, size_t idx, bool& unreach); void ilt_generate_code_stmt(ILT *ilt, const char *tmp_prefix, size_t idx, bool& unreach); @@ -1420,14 +1439,14 @@ namespace Ttcn { /** Sets the code section selector of all embedded values and * templates to \a p_code_section. */ void set_code_section(GovernedSimple::code_section_t p_code_section); - char *generate_code(char *str, const char *tmp_prefix, - const char *expr_name); + char *generate_code(char *str, char*& def_glob_vars, char*& src_glob_vars, + const char *tmp_prefix, const char *expr_name); void ilt_generate_code(ILT *ilt, const char *tmp_prefix, const char *expr_init, const char *head_expr, const char *expr_name); /** generates code with switch c++ statement only for integer * compatible types*/ - char *generate_code_switch(char *str, const char *expr_name); + char *generate_code_switch(char *str, char*& def_glob_vars, char*& src_glob_vars, const char *expr_name); void ilt_generate_code_switch(ILT *ilt, const char *expr_init, const char *head_expr, const char *expr_name); @@ -1461,7 +1480,8 @@ namespace Ttcn { /* checking functions */ void chk(Type *p_gov); void set_code_section(GovernedSimple::code_section_t p_code_section); - char* generate_code_case(char *str, const char *type_name, bool &else_branch); + char* generate_code_case(char *str, char*& def_glob_vars, char*& src_glob_vars, + const char *type_name, bool &else_branch); /** Needed by implicit omit. Pushes attrib path down to definitions */ @@ -1499,7 +1519,8 @@ namespace Ttcn { * construct */ void chk_allowed_interleave(); void set_code_section(GovernedSimple::code_section_t p_code_section); - char *generate_code(char *str, const char *type_name, const char* loc); + char *generate_code(char *str, char*& def_glob_vars, char*& src_glob_vars, + const char *type_name, const char* loc); /** Needed by implicit omit. Pushes attrib path down to definitions */ @@ -1625,10 +1646,11 @@ namespace Ttcn { /** Generates the equivalent C++ code for the branches of an alt construct, * appends it to \a str and returns the resulting string. Parameter \a loc * shall contain the location of the alt construct. */ - char *generate_code_alt(char *str, const Location& loc); + char *generate_code_alt(char *str, char*& def_glob_vars, char*& src_glob_vars, + const Location& loc); /** Generates the equivalent C++ code for the branches of an altstep, * appends it to \a str and returns the resulting string. */ - char *generate_code_altstep(char *str); + char *generate_code_altstep(char *str, char*& def_glob_vars, char*& src_glob_vars); /** Generates the equivalent C++ code for the response and * exception handling part of a call statement, appends it to \a * str and returns the resulting string. Parameter \a loc @@ -1638,8 +1660,8 @@ namespace Ttcn { * those alt branches that contain receiving statement(s) are not * generated but a "goto label_str_branch_n" where 'n' is the * branch number. */ - char* generate_code_call_body(char *str, const Location& loc, - const string& temp_id, bool in_interleave); + char* generate_code_call_body(char *str, char*& def_glob_vars, char*& src_glob_vars, + const Location& loc, const string& temp_id, bool in_interleave); void ilt_generate_code_call_body(ILT *ilt, const char *label_str); }; diff --git a/compiler2/ttcn3/TtcnTemplate.cc b/compiler2/ttcn3/TtcnTemplate.cc index 69662da56..b07ec0203 100644 --- a/compiler2/ttcn3/TtcnTemplate.cc +++ b/compiler2/ttcn3/TtcnTemplate.cc @@ -3099,9 +3099,6 @@ end: { if (get_code_generated()) return str; set_code_generated(); - if (err_descr) { - str = err_descr->generate_code_init_str(str, string(name)+"_err_descr"); - } switch (templatetype) { case OMIT_VALUE: case ANY_VALUE: @@ -3179,9 +3176,6 @@ end: str = length_restriction->generate_code_init(str, name); } if (is_ifpresent) str = mputprintf(str, "%s.set_ifpresent();\n", name); - if (err_descr) { - str = mputprintf(str, "%s.set_err_descr(&%s_err_descr);\n", name, name); - } return str; } diff --git a/compiler2/ttcn3/compiler.l b/compiler2/ttcn3/compiler.l index ca2f23b0c..a251832aa 100644 --- a/compiler2/ttcn3/compiler.l +++ b/compiler2/ttcn3/compiler.l @@ -531,7 +531,7 @@ xor4b RETURN(Xor4bKeyword); "@try" RETURN(TitanSpecificTryKeyword); "@catch" RETURN(TitanSpecificCatchKeyword); "@profiler" RETURN(TitanSpecificProfilerKeyword); - +"@update" RETURN(TitanSpecificUpdateKeyword); /* Predefined function identifiers */ diff --git a/compiler2/ttcn3/compiler.y b/compiler2/ttcn3/compiler.y index f37210107..fae8af54b 100644 --- a/compiler2/ttcn3/compiler.y +++ b/compiler2/ttcn3/compiler.y @@ -777,6 +777,7 @@ static const string anyname("anytype"); %token TitanSpecificTryKeyword %token TitanSpecificCatchKeyword %token TitanSpecificProfilerKeyword +%token TitanSpecificUpdateKeyword /* Keywords combined with a leading dot */ @@ -981,7 +982,7 @@ static const string anyname("anytype"); StartTimerStatement StopExecutionStatement StopStatement StopTCStatement StopTimerStatement TimeoutStatement TimerStatements TriggerStatement UnmapStatement VerdictStatements WhileStatement SelectCaseConstruct - SelectUnionConstruct + SelectUnionConstruct UpdateStatement StopTestcaseStatement String2TtcnStatement ProfilerStatement int2enumStatement %type <statementblock> StatementBlock optElseClause FunctionStatementOrDefList ControlStatementOrDefList ModuleControlBody @@ -1381,6 +1382,7 @@ UnionFieldDef UnionFieldDefList UnmapStatement UnnamedPart +UpdateStatement UpperBound Value ValueList @@ -4077,6 +4079,7 @@ FunctionStatement: // 180 | StopTestcaseStatement { $$ = $1; } | ProfilerStatement { $$ = $1; } | int2enumStatement { $$ = $1; } +| UpdateStatement { $$ = $1; } ; FunctionInstance: /* refpard */ // 181 @@ -5247,6 +5250,7 @@ ControlStatement: /* Statement *stmt */ // 295 | StopExecutionStatement { $$ = $1; } | ProfilerStatement { $$ = $1; } | int2enumStatement { $$ = $1; } +| UpdateStatement { $$ = $1; } ; /* A.1.6.2.1 Variable instantiation */ @@ -8131,6 +8135,22 @@ int2enumStatement: } ; +UpdateStatement: + TitanSpecificUpdateKeyword '(' Reference ')' optWithStatement + { + Ttcn::Reference* ref; + if ($3.is_ref) { + ref = $3.ref; + } + else { + ref = new Ttcn::Reference($3.id); + ref->set_location(infile, @3); + } + $$ = new Statement(Statement::S_UPDATE, ref, $5); + $$->set_location(infile, @$); + } +; + ProfilerRunningOp: TitanSpecificProfilerKeyword DotRunningKeyword { diff --git a/function_test/Semantic_Analyser/Makefile.semantic b/function_test/Semantic_Analyser/Makefile.semantic index 62da9eb27..70d89e865 100644 --- a/function_test/Semantic_Analyser/Makefile.semantic +++ b/function_test/Semantic_Analyser/Makefile.semantic @@ -14,7 +14,7 @@ ############################################################################## SADIRS := ver xer encode param template any_from pattern_ref float recof_index ifeq ($(RT2), yes) -SADIRS += deprecated +SADIRS += deprecated erroneous_attributes endif #$(wildcard TTCN3_[a0-9]* ASN_[a0-9]*) ver xer diff --git a/function_test/Semantic_Analyser/erroneous_attributes/.gitignore b/function_test/Semantic_Analyser/erroneous_attributes/.gitignore new file mode 100644 index 000000000..e2d293255 --- /dev/null +++ b/function_test/Semantic_Analyser/erroneous_attributes/.gitignore @@ -0,0 +1,2 @@ +!Makefile +!*.ttcn diff --git a/function_test/Semantic_Analyser/erroneous_attributes/ErroneousAttributes_SE.ttcn b/function_test/Semantic_Analyser/erroneous_attributes/ErroneousAttributes_SE.ttcn new file mode 100644 index 000000000..37863d77f --- /dev/null +++ b/function_test/Semantic_Analyser/erroneous_attributes/ErroneousAttributes_SE.ttcn @@ -0,0 +1,178 @@ +/****************************************************************************** + * Copyright (c) 2000-2017 Ericsson Telecom AB + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Baranyi, Botond + * + ******************************************************************************/ + +module ErroneousAttributes_SE { //^In TTCN-3 module// + +type record Rec { + integer num, + charstring str +} + +type record of integer IntList; + +type union Uni { + integer i, + charstring cs, + IntList list +} + +type charstring StringArray[3]; + +type enumerated Enum { val1, val2 }; + +function f_int(in integer x) return integer { + return x; +} + + +const Rec c_rec := { num := 1, str := "a" } with { erroneous(num) "value := 6" } + +const IntList c_list := { 1, 2, 3 } with { erroneous([1]) "after := omit all" } + +const Uni c_uni := { i := 10 } with { erroneous(i) "value := charstring:\"abc\"" } + +const StringArray c_arr := { "x", "y", "z" } with { erroneous([1]) "before := omit all" } //^In constant definition// //field qualifiers are only allowed for record, set and union types// + +const integer c_int := -5 with { erroneous "value := 100" } //^In constant definition// //At least one qualifier must be specified for the `erroneous' attribute// + +const float c_float := 12.5 with { erroneous(not_a_field) "value := 100" } //^In constant definition// //field qualifiers are only allowed for record, set and union types// + +const boolean c_bool := true with { erroneous([0]) "value := 100" } //^In constant definition// //field qualifiers are only allowed for record, set and union types// + +const bitstring c_bit := '1010'B with { erroneous "value := 100" } //^In constant definition// //At least one qualifier must be specified for the `erroneous' attribute// + +const hexstring c_hex := '1234ABC'H with { erroneous "value := 100" } //^In constant definition// //At least one qualifier must be specified for the `erroneous' attribute// + +const octetstring c_oct := '1234ABCD'O with { erroneous "value := 100" } //^In constant definition// //At least one qualifier must be specified for the `erroneous' attribute// + +const charstring c_char := "xyz" with { erroneous "value := 100" } //^In constant definition// //At least one qualifier must be specified for the `erroneous' attribute// + +const universal charstring c_unichar := char(0, 0, 1, 117) with { erroneous "value := 100" } //^In constant definition// //At least one qualifier must be specified for the `erroneous' attribute// + +const objid c_objid := objid { 0 1 10 } with { erroneous "value := 100" } //^In constant definition// //At least one qualifier must be specified for the `erroneous' attribute// + +const Enum c_enum := val1 with { erroneous "value := 100" } //^In constant definition// //At least one qualifier must be specified for the `erroneous' attribute// + +template Rec t_rec := { num := 1, str := "a" } with { erroneous(num) "after := omit all" } + +template IntList t_list := { 1, 2, 3 } with { erroneous([0]) "value := c_rec" } + +template Uni t_uni := { list := { 1, 2, 3 } } with { erroneous(list[1]) "value := f_int(5)" } //^In template definition// //Reference to a constant value was expected instead of the return value of function// + +template Rec t_rec_pard(in integer p) := { num := p, str := "a" } with { erroneous(str) "value := charstring: \"a\"" } + +template IntList t_list_pard(in integer p) := { 1, 2, p } with { erroneous([1]) "value := p" } //^In template definition// //There is no local or imported definition with name `p'// + +template Uni t_uni_pard(in charstring p) := { cs := p } with { erroneous(cs) "value := 10" } + + +function f_bad_updates() { //^In function definition// + @update(c_int) with { erroneous "value := -1" } //^In @update statement// //Type `integer' cannot have erroneous attributes// //At least one qualifier must be specified for the `erroneous' attribute// + @update(c_float) with { erroneous "value := -1" } //^In @update statement// //Type `float' cannot have erroneous attributes// //At least one qualifier must be specified for the `erroneous' attribute// + @update(c_bool) with { erroneous "value := -1" } //^In @update statement// //Type `boolean' cannot have erroneous attributes// //At least one qualifier must be specified for the `erroneous' attribute// + @update(c_bit) with { erroneous "value := -1" } //^In @update statement// //Type `bitstring' cannot have erroneous attributes// //At least one qualifier must be specified for the `erroneous' attribute// + @update(c_hex) with { erroneous "value := -1" } //^In @update statement// //Type `hexstring' cannot have erroneous attributes// //At least one qualifier must be specified for the `erroneous' attribute// + @update(c_oct) with { erroneous "value := -1" } //^In @update statement// //Type `octetstring' cannot have erroneous attributes// //At least one qualifier must be specified for the `erroneous' attribute// + @update(c_char) with { erroneous "value := -1" } //^In @update statement// //Type `charstring' cannot have erroneous attributes// //At least one qualifier must be specified for the `erroneous' attribute// + @update(c_unichar) with { erroneous "value := -1" } //^In @update statement// //Type `universal charstring' cannot have erroneous attributes// //At least one qualifier must be specified for the `erroneous' attribute// + @update(c_objid) with { erroneous "value := -1" } //^In @update statement// //Type `objid' cannot have erroneous attributes// //At least one qualifier must be specified for the `erroneous' attribute// + @update(c_enum) with { erroneous "value := -1" } //^In @update statement// //Type `@ErroneousAttributes_SE.Enum' cannot have erroneous attributes// //At least one qualifier must be specified for the `erroneous' attribute// + @update(c_arr) with { erroneous "value := -1" } //^In @update statement// //Type `charstring\[3\]' cannot have erroneous attributes// //At least one qualifier must be specified for the `erroneous' attribute// + + @update(c_rec) with { encode "XML" } //^In @update statement// //Only `erroneous' attributes are allowed in an `@update' statement// + @update(c_rec) with { optional "implicit omit" } //^In @update statement// //Only `erroneous' attributes are allowed in an `@update' statement// + @update(c_rec) with { variant(num) "name as 'int'" } //^In @update statement// //Only `erroneous' attributes are allowed in an `@update' statement// + @update(c_rec) with { extension "transparent" } //^In @update statement// //Only `erroneous' attributes are allowed in an `@update' statement// + @update(c_rec) with { display(str) "red" } //^In @update statement// //Only `erroneous' attributes are allowed in an `@update' statement// + + @update(c_rec) with { erroneous "value := -1" } //^In @update statement// //At least one qualifier must be specified for the `erroneous' attribute// + @update(c_rec) with { erroneous(not_a_field) "value := -1" } //^In @update statement// //Reference to non-existent field `not_a_field' in type `@ErroneousAttributes_SE.Rec'// + @update(c_rec.num) with { erroneous "value := -1" } //^In @update statement// //At least one qualifier must be specified for the `erroneous' attribute// //Field names and array indexes are not allowed in this context// + + @update(f_good_updates); //^In @update statement// //Reference to constant or template definition was expected instead of function// + @update(CT); //^In @update statement// //Reference to constant or template definition was expected instead of type// + @update(Sig); //^In @update statement// //Reference to constant or template definition was expected instead of type// + @update(PT); //^In @update statement// //Reference to constant or template definition was expected instead of type// +} + +signature Sig(inout integer p); + +type port PT procedure { + inout Sig; +} +with { + extension "internal"; +} + +type component CT { + port PT pt; +} + +function f_good_updates() runs on CT { + var integer v := 0; + @update(c_rec) with { erroneous(str) "value := c_rec.num" } + + while (v > 0) { + @update(c_list) with { erroneous([1]) "before := omit all" } + } + + if (v == 10) { + @update(c_uni) with { erroneous(i) "value := f_int(3)" } + } + else { + @update(t_rec) with { erroneous(num) "after := omit all" } + } + + do { + @update(t_list) with { erroneous([2]) "value := f_int(v)" } + } + while (v < 10); + + for (v := 1; v < 2; v := v + 1) { + @update(t_uni) with { erroneous(list[0]) "value := f_int(v + 1) * (v - 10)" } + } + + select (v) { + case (1) { + @update(t_rec_pard) with { erroneous(str) "value := valueof(t_rec_pard(v))" } + } + case else { + @update(t_list_pard) with { erroneous([1]) "value := int2str(v)" } + } + } + + timer tmr; + tmr.start(0.1); + alt { + [] tmr.timeout { + @update(t_uni_pard) with { erroneous(cs) "value := encvalue(c_list)" } + } + } + + interleave { + [] tmr.timeout { + @update(c_rec) with { erroneous(str) "value := omit" } + } + } + + pt.call(Sig : { v }, 0.3) { + [] pt.catch(timeout) { + @update(c_rec) with { erroneous(str) "before := omit all" } + } + } +} + +} +with { + encode "JSON"; + extension "anytype integer,Rec"; +} diff --git a/function_test/Semantic_Analyser/erroneous_attributes/Makefile b/function_test/Semantic_Analyser/erroneous_attributes/Makefile new file mode 100644 index 000000000..3f369041a --- /dev/null +++ b/function_test/Semantic_Analyser/erroneous_attributes/Makefile @@ -0,0 +1,12 @@ +############################################################################## +# Copyright (c) 2000-2017 Ericsson Telecom AB +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Eclipse Public License v1.0 +# which accompanies this distribution, and is available at +# http://www.eclipse.org/legal/epl-v10.html +# +# Contributors: +# Baranyi, Botond +# +############################################################################## +include ../common.mk diff --git a/function_test/Semantic_Analyser/erroneous_attributes/t b/function_test/Semantic_Analyser/erroneous_attributes/t new file mode 100755 index 000000000..3a4b58ec1 --- /dev/null +++ b/function_test/Semantic_Analyser/erroneous_attributes/t @@ -0,0 +1,9 @@ +#!/usr/bin/perl +# note this is called through "perl -w" +use strict; + +my $self = $0; +$self =~ s!/t!!; + +exec('make check --no-print-directory -s -C ' . $self); + diff --git a/regression_test/negativeTest/Makefile b/regression_test/negativeTest/Makefile index a53a77cc0..00307792a 100644 --- a/regression_test/negativeTest/Makefile +++ b/regression_test/negativeTest/Makefile @@ -42,7 +42,7 @@ TTCN3_MODULES = negtest.ttcn NegTestTestcases.ttcn \ NegTest_TEXT_Types.ttcn NegTest_TEXT_Testcases.ttcn \ NegTest_RAW_Types.ttcn NegTest_RAW_Testcases.ttcn \ www_XmlTest_org_negativeTest_XML_Types.ttcn NegTest_XML_Testcases.ttcn XSD.ttcn UsefulTtcn3Types.ttcn \ -NegTest_JSON.ttcn +NegTest_JSON.ttcn NegTest_Update.ttcn ASN1_MODULES = Types.asn NegTestTypes.asn diff --git a/regression_test/negativeTest/NegTest_JSON.cfg b/regression_test/negativeTest/NegTest_JSON.cfg index c0cc0059c..6da448dab 100644 --- a/regression_test/negativeTest/NegTest_JSON.cfg +++ b/regression_test/negativeTest/NegTest_JSON.cfg @@ -19,3 +19,4 @@ LogEventTypes := Detailed [EXECUTE] NegTest_JSON.control +NegTest_Update.control diff --git a/regression_test/negativeTest/NegTest_JSON.ttcn b/regression_test/negativeTest/NegTest_JSON.ttcn index 33217c4ca..7511862f9 100644 --- a/regression_test/negativeTest/NegTest_JSON.ttcn +++ b/regression_test/negativeTest/NegTest_JSON.ttcn @@ -102,6 +102,15 @@ const Uni2 u7 := { i := -6 } with { erroneous(i) "value := omit" }; const Uni2 u8 := { i := -6 } with { erroneous(i) "value(raw) := \"abc\" & char(0,0,1,117)" }; const Uni2 u9 := { list := { 0, 3, 6 } } with { erroneous(list[2]) "value := \"6\"" }; +template Rec r_temp := { num := 10, str := "hello" } with { erroneous(str) "value := r1" }; // the erroneous attributes of r1 will also affect the result + +template Set s_temp_pard(IntList p1, Rec p2) := { list := p1, rec := p2, b := true } with { erroneous(list) "after := omit all" }; // the erroneous attributes of p1 will also affect the result + +/* Erroneous constants and templates imported by other modules */ +const IntList c_imp := { 1, 12, 23, 34 } with { erroneous([3]) "value := 234" }; +template Rec t_imp := { num := 10, str := "hello" }; +template Uni2 t_imp_pard(IntList p) := { list := p } with { erroneous(list[0]) "after := omit all" }; + /******** Test cases for record-ofs ********/ testcase tc_record_of_omit_before() runs on CT { var octetstring res := f_enc_il(il1); @@ -279,6 +288,19 @@ testcase tc_union_as_value_alternative_is_erroneous() runs on CT { else { setverdict(fail, res); } } +/********* Test cases for templates *********/ +testcase tc_record_template_other_value() runs on CT { + var octetstring res := f_enc_r(valueof(r_temp)); + if (char2oct("{\"num\":10,\"str\":{\"BOOLEAN\":true,\"num\":10,\"str\":\"hello\"}}") == res) { setverdict(pass); } + else { setverdict(fail, res); } +} + +testcase tc_set_template_pard_omit_after() runs on CT { + var octetstring res := f_enc_s(valueof(s_temp_pard(il1, r1))); + if (char2oct("{\"list\":[2,3]}") == res) { setverdict(pass); } + else { setverdict(fail, res); } +} + /*************** Control part ***************/ control { execute(tc_record_of_omit_before()); @@ -310,6 +332,8 @@ control { execute(tc_union_as_value_omit_value()); execute(tc_union_as_value_raw_value()); execute(tc_union_as_value_alternative_is_erroneous()); + execute(tc_record_template_other_value()); + execute(tc_set_template_pard_omit_after()); } } diff --git a/regression_test/negativeTest/NegTest_Update.ttcn b/regression_test/negativeTest/NegTest_Update.ttcn new file mode 100644 index 000000000..cacc1ab58 --- /dev/null +++ b/regression_test/negativeTest/NegTest_Update.ttcn @@ -0,0 +1,231 @@ +/****************************************************************************** + * Copyright (c) 2000-2017 Ericsson Telecom AB + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Baranyi, Botond + * + ******************************************************************************/ + +// This module tests the '@update' statement, which changes the erroneous +// attributes of constants and templates. +module NegTest_Update { + +import from NegTest_JSON all; + +/************* Erroneous constants **************/ +const IntList c1 := { 1, 3, 5, 7, 9 } with { erroneous([2]) "value := 99" } +const Rec c2 := { num := 3, str := "abc" } with { erroneous(num) "value := float: 3.0" } +const Uni c3 := { list := { 10, 20 } } // not erroneous initially + +/************* Erroneous templates **************/ +template IntListList t1 := { { 1, 2, 3 }, { 10, 20, 30 }, { 100, 200, 300 } } with { erroneous([1][0]) "after := omit all"; erroneous([2][2]) "before := omit all" }; +template Set t2 := { list := { 0, 3, 6 }, rec := { num := 100, str := "aeiou" }, b := true }; +template Uni2 t3 := { i := 10 } with { erroneous(i) "value := charstring: \"ten\"" }; + +template IntList t_pard1(integer p) := { 0, p } with { erroneous([1]) "value := Rec: { num := 0, str := \"xy\" }" }; +template Rec t_pard2(template integer p) := { num := p, str := "p" } with { erroneous(num) "after := omit all" }; +template Uni t_pard3(template Uni p) := p; + +/******* Test cases for local constants *********/ +testcase tc_update_const_change() runs on CT { + @update(c1) with { erroneous([1]) "before := omit all" } + var octetstring res := f_enc_il(c1); + if (char2oct("[3,5,7,9]") == res) { setverdict(pass); } + else { setverdict(fail, res); } +} + +testcase tc_update_const_remove() runs on CT { + @update(c2); // removes the erroneous attributes + var octetstring res := f_enc_r(c2); + if (char2oct("{\"num\":3,\"str\":\"abc\"}") == res) { setverdict(pass); } + else { setverdict(fail, res); } +} + +testcase tc_update_const_add() runs on CT { + @update(c3) with { erroneous(list[1]) "value := Rec: { num := 0, str := \"xy\" }" } + var octetstring res := f_enc_u(c3); + if (char2oct("{\"list\":[10,{\"num\":0,\"str\":\"xy\"}]}") == res) { setverdict(pass); } + else { setverdict(fail, res); } +} + +testcase tc_update_const_reset() runs on CT { + @update(c3); + var octetstring res := f_enc_u(c3); + if (char2oct("{\"list\":[10,20]}") == res) { setverdict(pass); } + else { setverdict(fail, res); } +} + +/******* Test cases for local templates *********/ +testcase tc_update_temp_change() runs on CT { + @update(t1) with { erroneous([1]) "value := charstring: \"aaaa\""; erroneous([2]) "after := il4" /* from the imported module */ }; + var octetstring res := f_enc_ill(valueof(t1)); + if (char2oct("[[1,2,3],\"aaaa\",[100,200,300],[4,5,false,6]]") == res) { setverdict(pass); } + else { setverdict(fail, res); } +} + +testcase tc_update_temp_add() runs on CT { + @update(t2) with { erroneous(rec.str) "value := omit" }; + var octetstring res := f_enc_s(valueof(t2)); + if (char2oct("{\"list\":[0,3,6],\"rec\":{\"num\":100},\"b\":true}") == res) { setverdict(pass); } + else { setverdict(fail, res); } +} + +testcase tc_update_temp_remove() runs on CT { + @update(t3); + var octetstring res := f_enc_u2(valueof(t3)); + if (char2oct("10") == res) { setverdict(pass); } + else { setverdict(fail, res); } +} + +testcase tc_update_temp_readd() runs on CT { + @update(t3) with { erroneous(i) "value := float: 123.456" }; + var octetstring res := f_enc_u2(valueof(t3)); + if (char2oct("123.456000") == res) { setverdict(pass); } + else { setverdict(fail, res); } +} + +/* Test cases for local parameterized templates */ +testcase tc_update_temp_pard_change() runs on CT { + @update(t_pard1) with { erroneous([0]) "after := IntList: { 1, 2, 4 }" }; + var octetstring res := f_enc_il(valueof(t_pard1(7))); + if (char2oct("[0,[1,2,4],7]") == res) { setverdict(pass); } + else { setverdict(fail, res); } +} + +testcase tc_update_temp_pard_remove() runs on CT { + @update(t_pard2); + var octetstring res := f_enc_r(valueof(t_pard2(-9))); + if (char2oct("{\"num\":-9,\"str\":\"p\"}") == res) { setverdict(pass); } + else { setverdict(fail, res); } +} + +testcase tc_update_temp_pard_add() runs on CT { + @update(t_pard3) with { erroneous(i) "value := float: 0.123456" }; + var octetstring res := f_enc_u(valueof(t_pard3(Uni: { i := 700 }))); + if (char2oct("{\"i\":0.123456}") == res) { setverdict(pass); } + else { setverdict(fail, res); } +} + +testcase tc_update_temp_pard_reset() runs on CT { + @update(t_pard3); + var octetstring res := f_enc_u(valueof(t_pard3(Uni: { i := 700 }))); + if (char2oct("{\"i\":700}") == res) { setverdict(pass); } + else { setverdict(fail, res); } +} + +/* Test cases for imported constants and templates */ +testcase tc_update_imported_const_change() runs on CT { + @update(c_imp) with { erroneous([1]) "before := omit all" }; + var octetstring res := f_enc_il(c_imp); + if (char2oct("[12,23,34]") == res) { setverdict(pass); } + else { setverdict(fail, res); } +} + +testcase tc_update_imported_temp_add() runs on CT { + @update(t_imp) with { erroneous(str) "value := \"bye\"" }; + var octetstring res := f_enc_r(valueof(t_imp)); + if (char2oct("{\"num\":10,\"str\":\"bye\"}") == res) { setverdict(pass); } + else { setverdict(fail, res); } +} + +testcase tc_update_imported_temp_pard_remove() runs on CT { + @update(t_imp_pard); + var octetstring res := f_enc_u2(valueof(t_imp_pard(il2 /* from the imported module */))); + if (char2oct("[0,1,2]") == res) { setverdict(pass); } + else { setverdict(fail, res); } +} + +testcase tc_update_imported_temp_pard_readd() runs on CT { + @update(t_imp_pard) with { erroneous(list[0]) "after := omit all" }; + var octetstring res := f_enc_u2(valueof(t_imp_pard(il2))); + if (char2oct("[0]") == res) { setverdict(pass); } + else { setverdict(fail, res); } +} + +/* Test case for using '@update' in an interleave statement */ +testcase tc_update_in_interleave() runs on CT { + var octetstring res1, res2; + timer tmr1, tmr2; + tmr1.start(0.5); + tmr2.start(0.5); + interleave { + [] tmr1.timeout { + @update(t1) with { erroneous([1][0]) "after := omit all"; erroneous([2][2]) "before := omit all" }; + res1 := f_enc_ill(valueof(t1)); + } + [] tmr2.timeout { + @update(c_imp) with { erroneous([3]) "value := 234" }; + } + } + res2 := f_enc_il(c_imp); + if (char2oct("[[1,2,3],[10],[300]]") == res1 and + char2oct("[1,12,23,234]") == res2) { + setverdict(pass); + } + else { + setverdict(fail, res1, " ", res2); + } +} + +/* Test cases with dynamic values in attribute specs */ +testcase tc_update_const_change_dynamic() runs on CT { + var integer v := 17; + @update(c1) with { erroneous([1]) "value := v"; erroneous([4]) "value := 2 * v - 5" } + var octetstring res := f_enc_il(c1); + if (char2oct("[1,17,5,7,29]") == res) { setverdict(pass); } + else { setverdict(fail, res); } +} + +function f_return_list(in integer p) return IntList { + var IntList ret_val; + for (var integer i := 0; i < p; i := i + 1) { + ret_val[i] := i; + } + return ret_val; +} + +testcase tc_update_temp_change_dynamic() runs on CT { + var Rec v := { num := 6, str := "something" }; + @update(t2) with { erroneous(list) "value := v"; erroneous(rec) "value := f_return_list(6)" } + var octetstring res := f_enc_s(valueof(t2)); + if (char2oct("{\"list\":{\"num\":6,\"str\":\"something\"},\"rec\":[0,1,2,3,4,5],\"b\":true}") == res) { setverdict(pass); } + else { setverdict(fail, res); } +} + +testcase tc_update_imported_temp_pard_change_dynamic() runs on CT { + var IntList v := { 2, 4, 8, 16 }; + @update(t_imp_pard) with { erroneous(list) "value := f_return_list(v[1]) & { v[2] } & v" } + var octetstring res := f_enc_u2(valueof(t_imp_pard(v))); + if (char2oct("[0,1,2,3,8,2,4,8,16]") == res) { setverdict(pass); } + else { setverdict(fail, res); } +} + +/*************** Control part ***************/ +control { + execute(tc_update_const_change()); + execute(tc_update_const_remove()); + execute(tc_update_const_add()); + execute(tc_update_const_reset()); + execute(tc_update_temp_change()); + execute(tc_update_temp_add()); + execute(tc_update_temp_remove()); + execute(tc_update_temp_readd()); + execute(tc_update_temp_pard_change()); + execute(tc_update_temp_pard_remove()); + execute(tc_update_temp_pard_add()); + execute(tc_update_temp_pard_reset()); + execute(tc_update_imported_const_change()); + execute(tc_update_imported_temp_add()); + execute(tc_update_imported_temp_pard_remove()); + execute(tc_update_imported_temp_pard_readd()); + execute(tc_update_in_interleave()); + execute(tc_update_const_change_dynamic()); + execute(tc_update_temp_change_dynamic()); + execute(tc_update_imported_temp_pard_change_dynamic()); +} + +} diff --git a/regression_test/negativeTest/NegTest_all.cfg b/regression_test/negativeTest/NegTest_all.cfg index f37427d77..7fa14c846 100644 --- a/regression_test/negativeTest/NegTest_all.cfg +++ b/regression_test/negativeTest/NegTest_all.cfg @@ -25,5 +25,6 @@ NegTest_TEXT_Testcases.control NegTest_XML_Testcases.control NegTest_RAW_Testcases.control NegTest_JSON.control +NegTest_Update.control //saved by GUI -- GitLab