From 77b0691c9ccb6deec41584cac587a89f15f12c67 Mon Sep 17 00:00:00 2001 From: Botond Baranyi <botond.baranyi@ericsson.com> Date: Fri, 12 Aug 2016 11:34:32 +0200 Subject: [PATCH] Implemented the @decoded modifier and value redirects with field assignments (artf724069) Change-Id: I55482164cb881a1549e00bdaae3502e5f633e933 Signed-off-by: Botond Baranyi <botond.baranyi@ericsson.com> --- compiler2/Type.hh | 3 + compiler2/Type_chk.cc | 13 +- compiler2/Type_codegen.cc | 29 +- compiler2/Value.cc | 18 + compiler2/Value.hh | 4 + compiler2/ttcn3/AST_ttcn3.cc | 7 + compiler2/ttcn3/AST_ttcn3.hh | 5 + compiler2/ttcn3/Statement.cc | 908 ++++++++++++++++-- compiler2/ttcn3/Statement.hh | 148 ++- compiler2/ttcn3/Ttcnstuff.cc | 2 + compiler2/ttcn3/compiler.y | 117 ++- compiler2/ttcn3/port.c | 27 +- compiler2/ttcn3/port.h | 2 + compiler2/ttcn3/signature.c | 99 +- compiler2/ttcn3/signature.h | 3 + core/Universal_charstring.cc | 56 +- core/Universal_charstring.hh | 8 + regression_test/commMessage/TcommMessage.ttcn | 138 +++ regression_test/commProcedure/ProcPort.ttcn | 142 +++ 19 files changed, 1527 insertions(+), 202 deletions(-) diff --git a/compiler2/Type.hh b/compiler2/Type.hh index 4ea6af34d..a51c6f1be 100644 --- a/compiler2/Type.hh +++ b/compiler2/Type.hh @@ -1171,6 +1171,9 @@ namespace Common { map<typetype_t, void>& not_allowed); /** Set the owner and its type type */ void set_ownertype(TypeOwner_t ot, Node *o) { ownertype = ot; owner = o; } + + TypeOwner_t get_ownertype() const { return ownertype; } + Node* get_owner() const { return owner; } bool is_untagged() const; diff --git a/compiler2/Type_chk.cc b/compiler2/Type_chk.cc index 208a6353c..0c8b228e5 100644 --- a/compiler2/Type_chk.cc +++ b/compiler2/Type_chk.cc @@ -5770,18 +5770,7 @@ bool Type::chk_this_template_Str(Template *t, namedbool implicit_omit, "universal charstring templates"); break; } - Error_Context cntxt(t, "In encoding format"); - str_enc->set_lowerid_to_ref(); - get_pooltype(T_CSTR)->chk_this_value(str_enc, lhs, EXPECTED_DYNAMIC_VALUE, - INCOMPLETE_NOT_ALLOWED, OMIT_NOT_ALLOWED, NO_SUB_CHK); - if (!str_enc->is_unfoldable()) { - string enc_name = str_enc->get_val_str(); - if (enc_name != "UTF-8" && enc_name != "UTF-16" && enc_name != "UTF-32" - && enc_name != "UTF-16LE" && enc_name != "UTF-16BE" - && enc_name != "UTF-32LE" && enc_name != "UTF-32BE") { - str_enc->error("'%s' is not a valid encoding format", enc_name.c_str()); - } - } + self_ref |= str_enc->chk_string_encoding(lhs); } } break; diff --git a/compiler2/Type_codegen.cc b/compiler2/Type_codegen.cc index 3acf33af6..8edd57190 100644 --- a/compiler2/Type_codegen.cc +++ b/compiler2/Type_codegen.cc @@ -2150,8 +2150,12 @@ void Type::generate_code_Signature(output_struct *target) memset(&sdef, 0, sizeof(sdef)); sdef.name = get_genname_own().c_str(); sdef.dispname = get_fullname().c_str(); - if (u.signature.return_type) sdef.return_type = - pool.add(u.signature.return_type->get_genname_value(my_scope)); + if (u.signature.return_type) { + sdef.return_type = + pool.add(u.signature.return_type->get_genname_value(my_scope)); + sdef.return_type_w_no_prefix = pool.add(u.signature.return_type->get_genname_value( + u.signature.return_type->get_type_refd_last()->get_my_scope())); + } else sdef.return_type = NULL; if (u.signature.parameters) { sdef.parameters.nElements = u.signature.parameters->get_nof_params(); @@ -2193,6 +2197,8 @@ void Type::generate_code_Signature(output_struct *target) pool.add(type->get_genname_value(my_scope)); sdef.exceptions.elements[i].dispname = pool.add(type->get_typename()); sdef.exceptions.elements[i].altname = pool.add(type->get_genname_altname()); + sdef.exceptions.elements[i].name_w_no_prefix = + pool.add(type->get_genname_value(type->get_type_refd_last()->get_my_scope())); } } else { sdef.exceptions.nElements = 0; @@ -2225,15 +2231,25 @@ void Type::generate_code_done(output_struct *target) const char *genname_str = t_genname.c_str(); const string& dispname = get_typename(); const char *dispname_str = dispname.c_str(); + // value redirect base class (interface) + target->header.class_decls = mputprintf(target->header.class_decls, + "class %s_Redirect_Interface;\n", genname_str); + target->header.class_defs = mputprintf(target->header.class_defs, + "class %s_Redirect_Interface {\n" + "public:\n" + "virtual void set_values(const %s&) = 0;\n" + "virtual ~%s_Redirect_Interface() { }\n" + "};\n\n", genname_str, genname_str, genname_str); + // the done function target->header.function_prototypes = mputprintf (target->header.function_prototypes, "extern alt_status done(const COMPONENT& component_reference, " - "const %s_template& value_template, %s *value_ptr);\n", + "const %s_template& value_template, %s_Redirect_Interface *value_redirect);\n", genname_str, genname_str); target->source.function_bodies = mputprintf (target->source.function_bodies, "alt_status done(const COMPONENT& component_reference, " - "const %s_template& value_template, %s *value_ptr)\n" + "const %s_template& value_template, %s_Redirect_Interface *value_redirect)\n" "{\n" "if (!component_reference.is_bound()) " "TTCN_error(\"Performing a done operation on an unbound component " @@ -2245,7 +2261,10 @@ void Type::generate_code_done(output_struct *target) "%s return_value;\n" "return_value.decode_text(*text_buf);\n" "if (value_template.match(return_value)) {\n" - "if (value_ptr != NULL) *value_ptr = return_value;\n" + "if (value_redirect != NULL) {\n" + "value_redirect->set_values(return_value);\n" + "delete value_redirect;\n" + "}\n" "TTCN_Logger::begin_event(TTCN_Logger::PARALLEL_PTC);\n" "TTCN_Logger::log_event_str(\"PTC with component reference \");\n" "component_reference.log();\n" diff --git a/compiler2/Value.cc b/compiler2/Value.cc index 5fd055078..e1aefe66e 100644 --- a/compiler2/Value.cc +++ b/compiler2/Value.cc @@ -9193,6 +9193,24 @@ error: if (error_flag) set_valuetype(V_ERROR); else if (!my_governor) set_my_governor(Type::get_pooltype(p_tt)); } + + bool Value::chk_string_encoding(Common::Assignment* lhs) + { + Error_Context cntxt(this, "In encoding format"); + set_lowerid_to_ref(); + bool self_ref = Type::get_pooltype(Type::T_CSTR)->chk_this_value(this, lhs, + Type::EXPECTED_DYNAMIC_VALUE, INCOMPLETE_NOT_ALLOWED, OMIT_NOT_ALLOWED, + NO_SUB_CHK); + if (!is_unfoldable()) { + string enc_name = get_val_str(); + if (enc_name != "UTF-8" && enc_name != "UTF-16" && enc_name != "UTF-32" + && enc_name != "UTF-16LE" && enc_name != "UTF-16BE" + && enc_name != "UTF-32LE" && enc_name != "UTF-32BE") { + error("'%s' is not a valid encoding format", enc_name.c_str()); + } + } + return self_ref; + } int Value::is_parsed_infinity() { diff --git a/compiler2/Value.hh b/compiler2/Value.hh index fe4389631..59feef3ee 100644 --- a/compiler2/Value.hh +++ b/compiler2/Value.hh @@ -700,6 +700,10 @@ namespace Common { /** Checks that the value (expression) evals to a default value */ inline void chk_expr_default(Type::expected_value_t exp_val) { chk_expr_type(Type::T_DEFAULT, "default", exp_val); } + + /** Checks that the value is (or evaluates to) a valid universal charstring + * encoding format. */ + bool chk_string_encoding(Common::Assignment* lhs); /* if "infinity" or "-infinity" was parsed then this is a real value or a unary - expression containing a real value, where the real value is diff --git a/compiler2/ttcn3/AST_ttcn3.cc b/compiler2/ttcn3/AST_ttcn3.cc index 6a4a9a0fc..209262e79 100644 --- a/compiler2/ttcn3/AST_ttcn3.cc +++ b/compiler2/ttcn3/AST_ttcn3.cc @@ -333,6 +333,13 @@ namespace Ttcn { FATAL_ERROR("FieldOrArrayRefs::generate_code()"); type = 0; } + generate_code(expr, type, is_template, nof_subrefs); + } + + void FieldOrArrayRefs::generate_code(expression_struct* expr, Type* type, + bool is_template, /* = false */ + size_t nof_subrefs /* = UINT_MAX */) + { size_t n_refs = (nof_subrefs != UINT_MAX) ? nof_subrefs : refs.size(); for (size_t i = 0; i < n_refs; i++) { if (type) type = type->get_type_refd_last(); diff --git a/compiler2/ttcn3/AST_ttcn3.hh b/compiler2/ttcn3/AST_ttcn3.hh index 894ec970f..03e87938b 100644 --- a/compiler2/ttcn3/AST_ttcn3.hh +++ b/compiler2/ttcn3/AST_ttcn3.hh @@ -255,6 +255,11 @@ namespace Ttcn { * @param nof_subrefs indicates the number of sub-references * to generate code from (UINT_MAX means all of them) */ void generate_code(expression_struct *expr, Common::Assignment *ass, size_t nof_subrefs = UINT_MAX); + /** Generates the C++ sub-expression that could access the + * sub-references of a reference of type \a type + * @param nof_subrefs indicates the number of sub-references + * to generate code from (UINT_MAX means all of them) */ + void generate_code(expression_struct *expr, Type *type, bool is_template = false, size_t nof_subrefs = UINT_MAX); /** Appends the string representation of sub-references to \a str. */ void append_stringRepr(string &str) const; bool refers_to_string_element() const { return refs_str_element; } diff --git a/compiler2/ttcn3/Statement.cc b/compiler2/ttcn3/Statement.cc index 447a1ba6a..a18979887 100644 --- a/compiler2/ttcn3/Statement.cc +++ b/compiler2/ttcn3/Statement.cc @@ -1092,7 +1092,7 @@ namespace Ttcn { Statement::Statement(statementtype_t p_st, Reference *p_ref, TemplateInstance *p_templinst, TemplateInstance *p_fromclause, - Reference *p_redirectval, Reference *p_redirectsender) + ValueRedirect *p_redirectval, Reference *p_redirectsender) : statementtype(p_st), my_sb(0) { switch(statementtype) { @@ -1137,7 +1137,7 @@ namespace Ttcn { TemplateInstance *p_templinst, TemplateInstance *p_valuematch, TemplateInstance *p_fromclause, - Reference *p_redirectval, ParamRedirect *p_redirectparam, + ValueRedirect *p_redirectval, ParamRedirect *p_redirectparam, Reference *p_redirectsender) : statementtype(p_st), my_sb(0) { @@ -1161,7 +1161,7 @@ namespace Ttcn { Statement::Statement(statementtype_t p_st, Reference *p_ref, Reference *p_sig, TemplateInstance *p_templinst, bool p_timeout, TemplateInstance *p_fromclause, - Reference *p_redirectval, Reference *p_redirectsender) + ValueRedirect *p_redirectval, Reference *p_redirectsender) : statementtype(p_st), my_sb(0) { switch(statementtype) { @@ -1258,7 +1258,7 @@ namespace Ttcn { } Statement::Statement(statementtype_t p_st, Value *p_compref, - TemplateInstance *p_donematch, Reference *p_redirect) + TemplateInstance *p_donematch, ValueRedirect *p_redirect) : statementtype(p_st), my_sb(0) { switch (statementtype) { @@ -3604,7 +3604,7 @@ error: // the receive parameter (template instance) is present // trying to determine type of the incoming message Type *msg_type = 0; - bool msg_type_determined = false, value_redirect_checked = false; + bool msg_type_determined = false; if (port_type) { // the port reference is correct and the port type is known PortTypeBody *port_type_body = port_type->get_PortBody(); @@ -3616,8 +3616,7 @@ error: msg_type = in_msgs->get_type_byIndex(0); } else { // there are more than one incoming message types - msg_type = get_incoming_type(port_op.r.rcvpar, - port_op.r.redirect.value, value_redirect_checked); + msg_type = get_incoming_type(port_op.r.rcvpar, port_op.r.redirect.value); if (msg_type) { size_t nof_comp_types = in_msgs->get_nof_compatible_types(msg_type); @@ -3657,15 +3656,15 @@ error: } } if (!msg_type_determined) { - msg_type = get_incoming_type(port_op.r.rcvpar, port_op.r.redirect.value, - value_redirect_checked); + msg_type = get_incoming_type(port_op.r.rcvpar, port_op.r.redirect.value); } if (!msg_type) msg_type = Type::get_pooltype(Type::T_ERROR); // check the template instance using the message type port_op.r.rcvpar->chk(msg_type); - // check the value redirect if it is not done so far - if (!value_redirect_checked) - chk_value_redirect(port_op.r.redirect.value, msg_type); + // check the value redirect if it exists + if (port_op.r.redirect.value != NULL) { + port_op.r.redirect.value->chk(msg_type); + } } else { // the statement does not have parameter if (port_type) { @@ -3686,7 +3685,7 @@ error: if (port_op.r.redirect.value) { port_op.r.redirect.value->error("Value redirect cannot be used without " "receive parameter"); - chk_value_redirect(port_op.r.redirect.value, 0); + port_op.r.redirect.value->chk_erroneous(); } } // checking from clause and sender redirect @@ -3907,7 +3906,9 @@ error: port_op.r.getreply_valuematch->chk(return_type); } // checking the value redirect if present - chk_value_redirect(port_op.r.redirect.value, return_type); + if (port_op.r.redirect.value != NULL) { + port_op.r.redirect.value->chk(return_type); + } } else { // the statement does not have parameter (value match is also omitted) if (port_type) { @@ -3929,7 +3930,7 @@ error: if (port_op.r.redirect.value) { port_op.r.redirect.value->error("Value redirect cannot be used " "without signature template"); - chk_value_redirect(port_op.r.redirect.value, 0); + port_op.r.redirect.value->chk_erroneous(); } if (port_op.r.redirect.param) { port_op.r.redirect.param->error("Parameter redirect cannot be used " @@ -3994,7 +3995,7 @@ error: // the receive parameter (template instance) must be also present // trying to determine type of the exception Type *exc_type = 0; - bool exc_type_determined = false, value_redirect_checked = false; + bool exc_type_determined = false; if (port_op.r.ctch.signature) { // the signature is known SignatureExceptions *exceptions = @@ -4006,8 +4007,7 @@ error: exc_type = exceptions->get_type_byIndex(0); } else { // the signature has more than one exception types - exc_type = get_incoming_type(port_op.r.rcvpar, - port_op.r.redirect.value, value_redirect_checked); + exc_type = get_incoming_type(port_op.r.rcvpar, port_op.r.redirect.value); if (exc_type) { size_t nof_comp_types = exceptions->get_nof_compatible_types(exc_type); @@ -4034,15 +4034,15 @@ error: } } if (!exc_type_determined) { - exc_type = get_incoming_type(port_op.r.rcvpar, port_op.r.redirect.value, - value_redirect_checked); + exc_type = get_incoming_type(port_op.r.rcvpar, port_op.r.redirect.value); } if (!exc_type) exc_type = Type::get_pooltype(Type::T_ERROR); // check the template instance using the exception type port_op.r.rcvpar->chk(exc_type); - // check the value redirect if it is not done so far - if (!value_redirect_checked) - chk_value_redirect(port_op.r.redirect.value, exc_type); + // check the value redirect if it exists + if (port_op.r.redirect.value != NULL) { + port_op.r.redirect.value->chk(exc_type); + } // checking for invalid exception types exc_type = exc_type->get_type_refd_last(); switch (exc_type->get_typetype()) { @@ -4121,7 +4121,7 @@ error: // but the value redirect is present port_op.r.redirect.value->error("Value redirect cannot be used without " "signature and parameter"); - chk_value_redirect(port_op.r.redirect.value, 0); + port_op.r.redirect.value->chk_erroneous(); } } // checking from clause and sender redirect @@ -4313,10 +4313,9 @@ error: // value returning done can be used only when the statement contains a // specific component reference if (comp_op.donereturn.donematch) { - bool value_redirect_checked = false; - // try to determine the type of return value + // try to determine the type of the return value Type *return_type = get_incoming_type(comp_op.donereturn.donematch, - comp_op.donereturn.redirect, value_redirect_checked); + comp_op.donereturn.redirect); if (return_type) { bool return_type_correct = false; for (Type *t = return_type; ; t = t->get_type_refd()) { @@ -4336,12 +4335,13 @@ error: return_type = Type::get_pooltype(Type::T_ERROR); } comp_op.donereturn.donematch->chk(return_type); - if (!value_redirect_checked) - chk_value_redirect(comp_op.donereturn.redirect, return_type); + if (comp_op.donereturn.redirect != NULL) { + comp_op.donereturn.redirect->chk(return_type); + } } else if (comp_op.donereturn.redirect) { comp_op.donereturn.redirect->error("Redirect cannot be used for the " "return value without a matching template"); - chk_value_redirect(comp_op.donereturn.redirect, 0); + comp_op.donereturn.redirect->chk_erroneous(); } } @@ -4900,15 +4900,16 @@ error: } Type *Statement::get_incoming_type(TemplateInstance *p_ti, - Reference *p_val_redir, bool& p_val_redir_checked) + ValueRedirect *p_val_redir) { // first analyze the template instance Type *ret_val = p_ti->get_expr_governor(Type::EXPECTED_TEMPLATE); // return if this step was successful if (ret_val) return ret_val; - // use the variable in value redirect in the next step - ret_val = chk_value_redirect(p_val_redir, 0); - p_val_redir_checked = true; + // try to determine the type from the value redirect in the next step + if (p_val_redir != NULL) { + ret_val = p_val_redir->get_type(); + } // return if this step was successful if (ret_val) return ret_val; // finally try to convert the undef identifier in the template instance to @@ -4918,33 +4919,6 @@ error: return t_templ->get_expr_governor(Type::EXPECTED_TEMPLATE); } - Type *Statement::chk_value_redirect(Reference *p_ref, Type *p_type) - { - if (!p_ref) return NULL; - Error_Context cntxt(p_ref, "In `value' redirect"); - Type *t_var_type = p_ref->chk_variable_ref(); - if (p_type && t_var_type) { - TypeCompatInfo info(my_sb->get_scope_mod(), p_type, t_var_type, true, false); - if (p_ref->get_subrefs()) info.set_str2_elem(p_ref->get_subrefs()->refers_to_string_element()); - TypeChain l_chain; - TypeChain r_chain; - if (!p_type->is_compatible(t_var_type, &info, &l_chain, &r_chain)) { - if (info.is_subtype_error()) { - p_ref->error("%s", info.get_subtype_error().c_str()); - } else - if (!info.is_erroneous()) { - p_ref->error("Type mismatch in value redirect: " - "A variable of type `%s' was expected instead of `%s'", - p_type->get_typename().c_str(), - t_var_type->get_typename().c_str()); - } else { - p_ref->error("%s", info.get_error_str_str().c_str()); - } - } - } - return t_var_type; - } - Type *Statement::chk_sender_redirect(Type *address_type) { if (!port_op.r.redirect.sender) return 0; @@ -6872,23 +6846,13 @@ error: expr->expr = mputprintf(expr->expr, ".%s(", opname); if (port_op.r.rcvpar) { // The receive parameter is present. - if (use_runtime_2 && TypeConv::needs_conv_redir(port_op.r.rcvpar, - port_op.r.redirect.value)) { - // Don't change the first parameter. Otherwise it won't receive - // anything. The only thing we need is a temporary to save the - // result and a conversion at the end. - TypeConv::gen_conv_code_redir(expr, port_op.r.rcvpar, - port_op.r.redirect.value); - } else { - port_op.r.rcvpar->generate_code(expr); - expr->expr = mputstr(expr->expr, ", "); - if (port_op.r.redirect.value) { - // Value redirect is also present. - expr->expr = mputstr(expr->expr, "&("); - port_op.r.redirect.value->generate_code(expr); - expr->expr = mputc(expr->expr, ')'); - } else expr->expr = mputstr(expr->expr, "NULL"); - } + port_op.r.rcvpar->generate_code(expr); + expr->expr = mputstr(expr->expr, ", "); + if (port_op.r.redirect.value) { + // Value redirect is also present. + port_op.r.redirect.value->generate_code(expr, + port_op.portref->get_refd_assignment()->get_Type()->get_genname_value(my_sb)); + } else expr->expr = mputstr(expr->expr, "NULL"); expr->expr = mputstr(expr->expr, ", "); } } else { @@ -6915,8 +6879,21 @@ error: generate_code_expr_fromclause(expr); // a temporary object is needed for parameter redirect Type *signature = port_op.r.rcvpar->get_Template()->get_my_governor(); - expr->expr = mputprintf(expr->expr, ", %s_call_redirect(", - signature->get_genname_value(my_sb).c_str()); + if (port_op.r.redirect.param != NULL && + port_op.r.redirect.param->has_decoded_modifier()) { + // a new redirect class (inheriting the old one) is needed if any of the + // redirects have the '@decoded' modifier + string tmp_id = my_sb->get_scope_mod_gen()->get_temporary_id(); + expr->preamble = port_op.r.redirect.param->generate_code_decoded( + expr->preamble, signature, tmp_id.c_str(), false); + expr->expr = mputprintf(expr->expr, ", %s_call_redirect_%s(", + signature->get_genname_value(signature->get_type_refd_last()->get_my_scope() + ).c_str(), tmp_id.c_str()); + } + else { + expr->expr = mputprintf(expr->expr, ", %s_call_redirect(", + signature->get_genname_value(my_sb).c_str()); + } if (port_op.r.redirect.param) port_op.r.redirect.param->generate_code(expr); expr->expr = mputstr(expr->expr, "), "); @@ -6966,15 +6943,27 @@ error: expr->expr = mputstr(expr->expr, ", "); generate_code_expr_fromclause(expr); // a temporary object is needed for value and parameter redirect - expr->expr = mputprintf(expr->expr, ", %s_reply_redirect(", - signature->get_genname_value(my_sb).c_str()); + if (port_op.r.redirect.param != NULL && + port_op.r.redirect.param->has_decoded_modifier()) { + // a new redirect class (inheriting the old one) is needed if any of the + // redirects have the '@decoded' modifier + string tmp_id = my_sb->get_scope_mod_gen()->get_temporary_id(); + expr->preamble = port_op.r.redirect.param->generate_code_decoded( + expr->preamble, signature, tmp_id.c_str(), true); + expr->expr = mputprintf(expr->expr, ", %s_reply_redirect_%s(", + signature->get_genname_value(signature->get_type_refd_last()->get_my_scope() + ).c_str(), tmp_id.c_str()); + } + else { + expr->expr = mputprintf(expr->expr, ", %s_reply_redirect(", + signature->get_genname_value(my_sb).c_str()); + } if (return_type) { // the first argument of the constructor must contain // the value redirect if (port_op.r.redirect.value) { - expr->expr = mputstr(expr->expr, "&("); - port_op.r.redirect.value->generate_code(expr); - expr->expr = mputc(expr->expr, ')'); + port_op.r.redirect.value->generate_code(expr, + signature->get_genname_value(my_sb) + "_reply_redirect"); } else expr->expr = mputstr(expr->expr, "NULL"); if (port_op.r.redirect.param) expr->expr = mputstr(expr->expr, ", "); } @@ -7018,9 +7007,8 @@ error: expr->expr = mputstr(expr->expr, ", "); if (port_op.r.redirect.value) { // value redirect is also present - expr->expr = mputstr(expr->expr, "&("); - port_op.r.redirect.value->generate_code(expr); - expr->expr = mputc(expr->expr, ')'); + port_op.r.redirect.value->generate_code(expr, + port_op.r.ctch.signature->get_genname_value(my_sb) + "_exception_template"); } else expr->expr = mputstr(expr->expr, "NULL"); expr->expr = mputstr(expr->expr, "), "); } @@ -7078,9 +7066,9 @@ error: expr->expr = mputstr(expr->expr, ", "); if (comp_op.donereturn.redirect) { // value redirect is present - expr->expr = mputstr(expr->expr, "&("); - comp_op.donereturn.redirect->generate_code(expr); - expr->expr = mputc(expr->expr, ')'); + comp_op.donereturn.redirect->generate_code(expr, + t_mod != my_sb->get_scope_mod_gen() ? t_mod->get_modid().get_name() : + string("")); } else { // value redirect is omitted expr->expr = mputstr(expr->expr, "NULL"); @@ -7924,16 +7912,23 @@ error: // ===== ParamAssignment // ================================= - ParamAssignment::ParamAssignment(Identifier *p_id, Reference *p_ref) - : Node(), Location(), id(p_id), ref(p_ref) + ParamAssignment::ParamAssignment(Identifier *p_id, Reference *p_ref, + bool p_decoded, Value* p_str_enc) + : Node(), Location(), id(p_id), ref(p_ref), decoded(p_decoded) + , str_enc(p_str_enc), dec_type(NULL) { - if(!id || !ref) FATAL_ERROR("Ttcn::ParamAssignment::ParamAssignment()"); + if (!id || !ref || (!decoded && str_enc)) { + FATAL_ERROR("Ttcn::ParamAssignment::ParamAssignment()"); + } } ParamAssignment::~ParamAssignment() { delete id; delete ref; + if (str_enc != NULL) { + delete str_enc; + } } ParamAssignment *ParamAssignment::clone() const @@ -7945,12 +7940,18 @@ error: { if (!ref) FATAL_ERROR("Ttcn::ParamAssignment::set_my_scope()"); ref->set_my_scope(p_scope); + if (str_enc != NULL) { + str_enc->set_my_scope(p_scope); + } } void ParamAssignment::set_fullname(const string& p_fullname) { if (!ref) FATAL_ERROR("Ttcn::ParamAssignment::set_fullname()"); ref->set_fullname(p_fullname); + if (str_enc != NULL) { + str_enc->set_fullname(p_fullname + ".<string_encoding>"); + } } Reference *ParamAssignment::get_ref() const @@ -7966,6 +7967,18 @@ error: ref = 0; return ret_val; } + + Value* ParamAssignment::get_str_enc() const + { + return str_enc; + } + + Value* ParamAssignment::steal_str_enc() + { + Value *ret_val = str_enc; + str_enc = NULL; + return ret_val; + } // ================================= // ===== ParamAssignments @@ -8008,7 +8021,15 @@ error: // ================================= VariableEntry::VariableEntry(Reference *p_ref) - : Node(), Location(), ref(p_ref) + : Node(), Location(), ref(p_ref), decoded(false), str_enc(NULL), dec_type(NULL) + { + if(!ref) FATAL_ERROR("VariableEntry::VariableEntry()"); + } + + VariableEntry::VariableEntry(Reference* p_ref, bool p_decoded, + Value* p_str_enc, Type* p_dec_type) + : Node(), Location(), ref(p_ref), decoded(p_decoded), str_enc(p_str_enc) + , dec_type(p_dec_type) { if(!ref) FATAL_ERROR("VariableEntry::VariableEntry()"); } @@ -8016,6 +8037,9 @@ error: VariableEntry::~VariableEntry() { delete ref; + if (str_enc != NULL) { + delete str_enc; + } } VariableEntry *VariableEntry::clone() const @@ -8026,12 +8050,18 @@ error: void VariableEntry::set_my_scope(Scope *p_scope) { if(ref) ref->set_my_scope(p_scope); + if (str_enc != NULL) { + str_enc->set_my_scope(p_scope); + } } void VariableEntry::set_fullname(const string& p_fullname) { Node::set_fullname(p_fullname); if(ref) ref->set_fullname(p_fullname+".ref"); + if (str_enc != NULL) { + str_enc->set_fullname(p_fullname + ".<string_encoding>"); + } } // ================================= @@ -8154,6 +8184,10 @@ error: Error_Context cntxt2(t_parass, "In redirect for parameter `%s'", dispname_str); chk_variable_ref(t_parass->get_ref(), 0); + Value* str_enc = t_parass->get_str_enc(); + if (str_enc != NULL) { + str_enc->chk_string_encoding(NULL); + } } parass_m.clear(); break; } @@ -8227,12 +8261,50 @@ error: error_flag = true; } } - chk_variable_ref(t_parass->get_ref(), t_par->get_type()); + if (t_parass->is_decoded()) { + // the redirected parameter could be decoded into any type + Type *t_var_type = t_parass->get_ref()->chk_variable_ref(); + if (t_var_type != NULL) { + // make sure the variable's type has a decoding type set, and store it + t_parass->set_dec_type(t_var_type->get_type_refd()); + t_parass->get_dec_type()->chk_coding(false); + } + Value* str_enc = t_parass->get_str_enc(); + switch (t_par->get_type()->get_type_refd_last()->get_typetype_ttcn3()) { + case Type::T_BSTR: + case Type::T_HSTR: + case Type::T_OSTR: + case Type::T_CSTR: + if (str_enc != NULL) { + str_enc->error("The encoding format parameter for the '@decoded' modifier " + "is only available to parameter redirects of universal charstrings"); + error_flag = true; + } + break; + case Type::T_USTR: + if (str_enc != NULL) { + str_enc->chk_string_encoding(NULL); + } + break; + default: + t_parass->error("The '@decoded' modifier is only available to parameter " + "redirects of string types."); + error_flag = true; + break; + } + } + else { + chk_variable_ref(t_parass->get_ref(), t_par->get_type()); + } } else { t_parass->error("Signature `%s' does not have parameter named `%s'", p_sig->get_typename().c_str(), dispname_str); error_flag = true; chk_variable_ref(t_parass->get_ref(), 0); + Value* str_enc = t_parass->get_str_enc(); + if (str_enc != NULL) { + str_enc->chk_string_encoding(NULL); + } } } if (!error_flag) { @@ -8245,7 +8317,9 @@ error: : p_parlist->get_in_param_byIndex(i); const string& name = t_par->get_id().get_name(); if (parass_m.has_key(name)) - t_ves->add_ve(new VariableEntry(parass_m[name]->steal_ref())); + t_ves->add_ve(new VariableEntry(parass_m[name]->steal_ref(), + parass_m[name]->is_decoded(), parass_m[name]->steal_str_enc(), + parass_m[name]->get_dec_type())); else t_ves->add_ve(new VariableEntry); } delete parasss; @@ -8320,15 +8394,643 @@ error: if (parredirtype != P_VAR) FATAL_ERROR("ParamRedirect::generate_code()"); for (size_t i = 0; i < ves->get_nof_ves(); i++) { if (i > 0) expr->expr = mputstr(expr->expr, ", "); - Reference *ref = ves->get_ve_byIndex(i)->get_ref(); + VariableEntry* ve = ves->get_ve_byIndex(i); + // there's an extra charstring parameter for every decoded universal + // charstring parameter redirect with an unfoldable encoding format + Value* str_enc = (ve->is_decoded() && ve->get_str_enc() != NULL && + ve->get_str_enc()->is_unfoldable()) ? ve->get_str_enc() : NULL; + Reference *ref = ve->get_ref(); if (ref) { - // the variable reference is present - expr->expr = mputstr(expr->expr, "&("); - ref->generate_code(expr); - expr->expr = mputc(expr->expr, ')'); - } else expr->expr = mputstr(expr->expr, "NULL"); + // the variable reference is present + expr->expr = mputstr(expr->expr, "&("); + ref->generate_code(expr); + expr->expr = mputc(expr->expr, ')'); + if (str_enc != NULL) { + expr->expr = mputstr(expr->expr, ", "); + str_enc->generate_code_expr(expr); + } + } + else { + expr->expr = mputstr(expr->expr, "NULL"); + if (str_enc != NULL) { + expr->expr = mputstr(expr->expr, ", CHARSTRING()"); + } + } } } + + char* ParamRedirect::generate_code_decoded(char* str, Type* sig_type, + const char* tmp_id, bool is_out) + { + // AssignmentList is converted to VariableList during checking + if (parredirtype != P_VAR) { + FATAL_ERROR("ParamRedirect::generate_code_decoded()"); + } + Scope* scope = NULL; + for (size_t i = 0; i < ves->get_nof_ves(); i++) { + Reference* ref = ves->get_ve_byIndex(i)->get_ref(); + if (ref != NULL) { + // there must be at least one reference in the redirect, otherwise this + // function would not be called + scope = ref->get_my_scope(); + break; + } + } + // cycle through the variable list and gather all the data needed for the + // new class + char* members_str = NULL; + char* constr_params_str = NULL; + char* base_constr_params_str = NULL; + char* constr_init_list_str = NULL; + char* set_params_str = NULL; + Type* return_type = sig_type->get_signature_return_type(); + if (return_type != NULL && is_out) { + // the return type's value redirect object must be passed through this + // class + constr_params_str = mprintf("%s_Redirect_Interface* return_redirect", + return_type->get_genname_value(return_type->get_my_scope()).c_str()); + base_constr_params_str = mcopystr("return_redirect"); + } + SignatureParamList* parlist = sig_type->get_signature_parameters(); + for (size_t i = 0; i < ves->get_nof_ves(); i++) { + VariableEntry* ve = ves->get_ve_byIndex(i); + SignatureParam* par = is_out ? parlist->get_out_param_byIndex(i) : + parlist->get_in_param_byIndex(i); + const char* par_name = par->get_id().get_name().c_str(); + if (constr_params_str != NULL) { + constr_params_str = mputstr(constr_params_str, ", "); + base_constr_params_str = mputstr(base_constr_params_str, ", "); + } + if (ve->is_decoded()) { + members_str = mputprintf(members_str, "%s* ptr_%s_dec;\n", + ve->get_dec_type()->get_genname_value(scope).c_str(), par_name); + constr_params_str = mputprintf(constr_params_str, "%s* par_%s_dec = NULL", + ve->get_dec_type()->get_genname_value(scope).c_str(), par_name); + base_constr_params_str = mputstr(base_constr_params_str, "NULL"); + constr_init_list_str = mputprintf(constr_init_list_str, + ", ptr_%s_dec(par_%s_dec)", par_name, par_name); + set_params_str = mputprintf(set_params_str, + "if (ptr_%s_dec != NULL) {\n", par_name); + switch (par->get_type()->get_type_refd_last()->get_typetype_ttcn3()) { + case Type::T_OSTR: + case Type::T_CSTR: + set_params_str = mputprintf(set_params_str, + "TTCN_Buffer buff(par.%s());\n", par_name); + break; + case Type::T_BSTR: + set_params_str = mputprintf(set_params_str, + "OCTETSTRING os(bit2oct(par.%s()));\n" + "TTCN_Buffer buff(os);\n", par_name); + break; + case Type::T_HSTR: + set_params_str = mputprintf(set_params_str, + "OCTETSTRING os(hex2oct(par.%s()));\n" + "TTCN_Buffer buff(os);\n", par_name); + break; + case Type::T_USTR: + set_params_str = mputstr(set_params_str, "TTCN_Buffer buff;\n"); + if (ve->get_str_enc() == NULL || !ve->get_str_enc()->is_unfoldable()) { + // if the encoding format is missing or is known at compile-time, then + // use the appropriate string encoding function + string str_enc = (ve->get_str_enc() != NULL) ? + ve->get_str_enc()->get_val_str() : string("UTF-8"); + if (str_enc == "UTF-8") { + set_params_str = mputprintf(set_params_str, + "par.%s().encode_utf8(buff, false);\n", par_name); + } + else if (str_enc == "UTF-16" || str_enc == "UTF-16LE" || + str_enc == "UTF-16BE") { + set_params_str = mputprintf(set_params_str, + "par.%s().encode_utf16(buff, CharCoding::UTF16%s);\n", par_name, + (str_enc == "UTF-16LE") ? "LE" : "BE"); + } + else if (str_enc == "UTF-32" || str_enc == "UTF-32LE" || + str_enc == "UTF-32BE") { + set_params_str = mputprintf(set_params_str, + "par.%s().encode_utf32(buff, CharCoding::UTF32%s);\n", par_name, + (str_enc == "UTF-32LE") ? "LE" : "BE"); + } + } + else { + // the encoding format is not known at compile-time, so an extra + // member and constructor parameter is needed to store it + members_str = mputprintf(members_str, "CHARSTRING enc_fmt_%s;\n", + par_name); + constr_params_str = mputprintf(constr_params_str, + ", CHARSTRING par_fmt_%s = CHARSTRING()", par_name); + constr_init_list_str = mputprintf(constr_init_list_str, + ", enc_fmt_%s(par_fmt_%s)", par_name, par_name); + set_params_str = mputprintf(set_params_str, + "CharCoding::CharCodingType coding = UNIVERSAL_CHARSTRING::" + "get_character_coding(enc_fmt_%s, \"decoded parameter redirect\");\n" + "switch (coding) {\n" + "case CharCoding::UTF_8:\n" + "par.%s().encode_utf8(buff, false);\n" + "break;\n" + "case CharCoding::UTF16:\n" + "case CharCoding::UTF16LE:\n" + "case CharCoding::UTF16BE:\n" + "par.%s().encode_utf16(buff, coding);\n" + "break;\n" + "case CharCoding::UTF32:\n" + "case CharCoding::UTF32LE:\n" + "case CharCoding::UTF32BE:\n" + "par.%s().encode_utf32(buff, coding);\n" + "break;\n" + "default:\n" + "break;\n" + "}\n", par_name, par_name, par_name, par_name); + } + break; + default: + FATAL_ERROR("ParamRedirect::generate_code_decoded"); + } + set_params_str = mputprintf(set_params_str, + "ptr_%s_dec->decode(%s_descr_, buff, TTCN_EncDec::CT_%s);\n" + "if (buff.get_read_len() != 0) {\n" + "TTCN_error(\"Parameter redirect failed, because the buffer was not " + "empty after decoding. Remaining octets: %%d.\", (int)buff.get_read_len());\n" + "}\n" + "}\n", par_name, + ve->get_dec_type()->get_genname_typedescriptor(scope).c_str(), + ve->get_dec_type()->get_coding(false).c_str()); + } + else { + constr_params_str = mputprintf(constr_params_str, "%s* par_%s = NULL", + par->get_type()->get_genname_value(scope).c_str(), par_name); + base_constr_params_str = mputprintf(base_constr_params_str, "par_%s", + par_name); + } + } + // generate the new class with the gathered data + string qualified_sig_name_ = sig_type->get_genname_value(scope); + const char* qualified_sig_name = qualified_sig_name_.c_str(); + string unqualified_sig_name_ = sig_type->get_genname_value( + sig_type->get_type_refd_last()->get_my_scope()); + const char* unqualified_sig_name = unqualified_sig_name_.c_str(); + const char* op_name = is_out ? "reply" : "call"; + str = mputprintf(str, + "class %s_%s_redirect_%s : public %s_%s_redirect {\n" + // member declarations for the decoded parameters and their encoding formats + // (if they have one) + "%s" + "public:\n" + // constructor: + // same as the inherited constructor, except the types of the parameter + // redirects with the '@decoded' modifier are replaced with the decoded + // types (plus an extra encoding format parameter is added when necessary) + "%s_%s_redirect_%s(%s)\n" + // the base constructor is called with the parameters that don't need to + // be decoded; the ones that need to be decoded are set to NULL and are + // initialized in the member initializer list (together with their + // eventual encoding formats) + ": %s_%s_redirect(%s)%s { }\n" + // set_parameters function: decodes the parameters that need decoding, + // the ones that don't are sent to the base class' function + "virtual void set_parameters(const %s_%s& par) const\n" + "{\n" + "%s" + "%s_%s_redirect::set_parameters(par);\n" + "}\n" + "};\n", unqualified_sig_name, op_name, tmp_id, qualified_sig_name, op_name, + members_str, unqualified_sig_name, op_name, tmp_id, constr_params_str, + unqualified_sig_name, op_name, base_constr_params_str, constr_init_list_str, + qualified_sig_name, op_name, set_params_str, unqualified_sig_name, op_name); + Free(members_str); + Free(constr_params_str); + Free(base_constr_params_str); + Free(constr_init_list_str); + Free(set_params_str); + return str; + } + + bool ParamRedirect::has_decoded_modifier() const + { + // this is called during code generation + // AssignmentList is converted to VariableList during checking + if (parredirtype != P_VAR) { + FATAL_ERROR("ParamRedirect::generate_code()"); + } + for (size_t i = 0; i < ves->get_nof_ves(); i++) { + if (ves->get_ve_byIndex(i)->is_decoded()) { + return true; + } + } + return false; + } + + // ================================= + // ===== SingleValueRedirect + // ================================= + + SingleValueRedirect::SingleValueRedirect(Reference* p_var_ref) + : Node(), Location(), var_ref(p_var_ref), subrefs(NULL), decoded(false) + , str_enc(NULL), dec_type(NULL) + { + if (var_ref == NULL) { + FATAL_ERROR("SingleValueRedirect::SingleValueRedirect"); + } + } + + SingleValueRedirect::SingleValueRedirect(Reference* p_var_ref, + FieldOrArrayRefs* p_subrefs, + bool p_decoded, Value* p_str_enc) + : Node(), Location(), var_ref(p_var_ref), subrefs(p_subrefs) + , decoded(p_decoded), str_enc(p_str_enc), dec_type(NULL) + { + if (var_ref == NULL || subrefs == NULL || (!decoded && str_enc != NULL)) { + FATAL_ERROR("SingleValueRedirect::SingleValueRedirect"); + } + } + + SingleValueRedirect::~SingleValueRedirect() + { + delete var_ref; + if (subrefs != NULL) { + delete subrefs; + } + if (str_enc != NULL) { + delete str_enc; + } + } + + SingleValueRedirect* SingleValueRedirect::clone() const + { + FATAL_ERROR("SingleValueRedirect::clone"); + } + + void SingleValueRedirect::set_my_scope(Scope* p_scope) + { + var_ref->set_my_scope(p_scope); + if (subrefs != NULL) { + subrefs->set_my_scope(p_scope); + } + if (str_enc != NULL) { + str_enc->set_my_scope(p_scope); + } + } + + void SingleValueRedirect::set_fullname(const string& p_fullname) + { + Node::set_fullname(p_fullname); + var_ref->set_fullname(p_fullname + ".varref"); + if (subrefs != NULL) { + subrefs->set_fullname(p_fullname + ".fieldrefs"); + } + if (str_enc != NULL) { + str_enc->set_fullname(p_fullname + ".<string_encoding>"); + } + } + + // ================================= + // ==== ValueRedirect + // ================================= + + ValueRedirect::~ValueRedirect() + { + for (size_t i = 0; i < v.size(); ++i) { + delete v[i]; + } + v.clear(); + } + + ValueRedirect* ValueRedirect::clone() const + { + FATAL_ERROR("ValueRedirect::clone"); + } + + void ValueRedirect::set_my_scope(Scope* p_scope) + { + for (size_t i = 0; i < v.size(); ++i) { + v[i]->set_my_scope(p_scope); + } + } + + void ValueRedirect::set_fullname(const string& p_fullname) + { + Node::set_fullname(p_fullname); + for (size_t i = 0; i < v.size(); ++i) { + v[i]->set_fullname(p_fullname + ".redirect_" + Int2string(i + 1)); + } + } + + void ValueRedirect::set_code_section(GovernedSimple::code_section_t p_code_section) + { + for (size_t i = 0; i < v.size(); ++i) { + v[i]->get_var_ref()->set_code_section(p_code_section); + } + } + + void ValueRedirect::add(SingleValueRedirect* ptr) + { + if (ptr == NULL) { + FATAL_ERROR("ValueRedirect::add"); + } + v.add(ptr); + } + + Type* ValueRedirect::get_type() const + { + Error_Context cntxt(this, "In value redirect"); + Type* ret_val = NULL; + for (size_t i = 0; i < v.size(); ++i) { + if (v[i]->get_subrefs() == NULL) { + Type* var_type = v[i]->get_var_ref()->chk_variable_ref(); + if (var_type != NULL) { + if (ret_val == NULL) { + ret_val = var_type; + } + else { + if (!ret_val->is_identical(var_type)) { + error("The variable references the whole value is redirected to " + "should be of the same type"); + return NULL; + } + } + } + } + } + return ret_val; + } + + void ValueRedirect::chk_erroneous() + { + Error_Context cntxt(this, "In value redirect"); + for (size_t i = 0; i < v.size(); ++i) { + Error_Context cntxt2(v[i], "In redirect #%d", (int)(i + 1)); + v[i]->get_var_ref()->chk_variable_ref(); + Value* str_enc = v[i]->get_str_enc(); + if (str_enc != NULL) { + str_enc->chk_string_encoding(NULL); + } + } + } + + void ValueRedirect::chk(Type* p_type) + { + bool invalid_type = p_type->get_typetype() == Type::T_ERROR; + if (!invalid_type) { + // initial check: redirects of fields require the value type to be a record + // or set + Type::typetype_t tt = p_type->get_type_refd_last()->get_typetype_ttcn3(); + Error_Context cntxt(this, "In value redirect"); + if (tt != Type::T_SEQ_T && tt != Type::T_SET_T) { + for (size_t i = 0; i < v.size(); ++i) { + if (v[i]->get_subrefs() != NULL) { + invalid_type = true; + Error_Context cntxt2(v[i], "In redirect #%d", (int)(i + 1)); + v[i]->error("Cannot redirect fields of type '%s', because it is not a " + "record or set", p_type->get_typename().c_str()); + } + } + } + } + if (invalid_type) { + chk_erroneous(); + return; + } + value_type = p_type->get_type_refd_last(); + Error_Context cntxt(this, "In value redirect"); + for (size_t i = 0; i < v.size(); ++i) { + Type* var_type = v[i]->get_var_ref()->chk_variable_ref(); + FieldOrArrayRefs* subrefs = v[i]->get_subrefs(); + Error_Context cntxt2(v[i], "In redirect #%d", (int)(i + 1)); + if (subrefs != NULL) { + // a field of the value is redirected to the referenced variable + Type* field_type = p_type->get_field_type(subrefs, Type::EXPECTED_DYNAMIC_VALUE); + if (field_type != NULL) { + if (v[i]->is_decoded()) { + Value* str_enc = v[i]->get_str_enc(); + switch (field_type->get_type_refd_last()->get_typetype_ttcn3()) { + case Type::T_BSTR: + case Type::T_HSTR: + case Type::T_OSTR: + case Type::T_CSTR: + if (str_enc != NULL) { + str_enc->error("The encoding format parameter for the '@decoded' modifier " + "is only available to value redirects of universal charstrings"); + } + break; + case Type::T_USTR: + if (str_enc != NULL) { + str_enc->chk_string_encoding(NULL); + } + break; + default: + v[i]->error("The '@decoded' modifier is only available to value " + "redirects of string types."); + break; + } + if (var_type != NULL) { + // store the variable type in case it's decoded (since this cannot + // be extracted from the value type with the sub-references) + v[i]->set_dec_type(var_type->get_type_refd()); + v[i]->get_dec_type()->chk_coding(false); + } + } + else if (var_type != NULL && !var_type->is_identical(field_type)) { + v[i]->error("Type mismatch in value redirect: A variable of type " + "`%s' was expected instead of `%s'", + field_type->get_typename().c_str(), var_type->get_typename().c_str()); + } + } + } + else { + // the whole value is redirected to the referenced variable + if (var_type != NULL && !var_type->is_identical(p_type)) { + v[i]->error("Type mismatch in value redirect: A variable of type " + "`%s' was expected instead of `%s'", p_type->get_typename().c_str(), + var_type->get_typename().c_str()); + } + } + } + } + + void ValueRedirect::generate_code(expression_struct* expr, + string base_class_prefix) + { + // a value redirect class is generated for this redirect in the expression's + // preamble and instantiated in the expression + + // the base class (interface) is type-specific, and is defined in the entity + // the new class instance is passed to (the port type for receive operations, + // the signature's reply redirect class for the return value, the signature + // exception template class for catch operations, and in the value type's + // module for done operations) + Scope* scope = v[0]->get_var_ref()->get_my_scope(); + string tmp_id = scope->get_scope_mod_gen()->get_temporary_id(); + expr->expr = mputprintf(expr->expr, "new Value_Redirect_%s(", tmp_id.c_str()); + + // go through the value redirect and gather the necessary data for the class + char* members_str = NULL; + char* constr_params_str = NULL; + char* constr_init_list_str = NULL; + char* set_values_str = NULL; + for (size_t i = 0; i < v.size(); ++i) { + if (i > 0) { + expr->expr = mputstr(expr->expr, ", "); + constr_params_str = mputstr(constr_params_str, ", "); + constr_init_list_str = mputstr(constr_init_list_str, ", "); + } + // pass the variable references to the new instance's constructor + expr->expr = mputstr(expr->expr, "&("); + v[i]->get_var_ref()->generate_code(expr); + expr->expr = mputc(expr->expr, ')'); + Type* redir_type = v[i]->get_subrefs() == NULL ? value_type : + value_type->get_field_type(v[i]->get_subrefs(), Type::EXPECTED_DYNAMIC_VALUE); + Type* member_type = v[i]->is_decoded() ? v[i]->get_dec_type() : redir_type; + string type_str = member_type->get_genname_value(scope); + members_str = mputprintf(members_str, "%s* ptr_%d;\n", type_str.c_str(), (int)i); + constr_params_str = mputprintf(constr_params_str, "%s* par_%d", + type_str.c_str(), (int)i); + constr_init_list_str = mputprintf(constr_init_list_str, + "ptr_%d(par_%d)", (int)i, (int)i); + // generate the sub-references' code in a separate expression structure and + // insert it into the set_values function + expression_struct subrefs_expr; + Code::init_expr(&subrefs_expr); + if (v[i]->get_subrefs() != NULL) { + v[i]->get_subrefs()->generate_code(&subrefs_expr, value_type); + if (redir_type->get_ownertype() == Type::OT_COMP_FIELD) { + CompField* cf = (CompField*)redir_type->get_owner(); + if (cf->get_is_optional()) { + subrefs_expr.expr = mputstr(subrefs_expr.expr, "()"); + } + } + } + if (subrefs_expr.preamble != NULL) { + set_values_str = mputstr(set_values_str, subrefs_expr.preamble); + } + const char* subrefs_str = (subrefs_expr.expr != NULL) ? subrefs_expr.expr : ""; + if (v[i]->is_decoded()) { + switch (redir_type->get_type_refd_last()->get_typetype_ttcn3()) { + case Type::T_OSTR: + case Type::T_CSTR: + set_values_str = mputprintf(set_values_str, + "TTCN_Buffer buff_%d(par%s);\n", (int)i, subrefs_str); + break; + case Type::T_BSTR: + set_values_str = mputprintf(set_values_str, + "OCTETSTRING os(bit2oct(par%s));\n" + "TTCN_Buffer buff_%d(os);\n", subrefs_str, (int)i); + break; + case Type::T_HSTR: + set_values_str = mputprintf(set_values_str, + "OCTETSTRING os(hex2oct(par%s));\n" + "TTCN_Buffer buff_%d(os);\n", subrefs_str, (int)i); + break; + case Type::T_USTR: + set_values_str = mputprintf(set_values_str, "TTCN_Buffer buff_%d;\n", + (int)i); + if (v[i]->get_str_enc() == NULL || !v[i]->get_str_enc()->is_unfoldable()) { + // if the encoding format is missing or is known at compile-time, then + // use the appropriate string encoding function + string str_enc = (v[i]->get_str_enc() != NULL) ? + v[i]->get_str_enc()->get_val_str() : string("UTF-8"); + if (str_enc == "UTF-8") { + set_values_str = mputprintf(set_values_str, + "par%s.encode_utf8(buff_%d, false);\n", subrefs_str, (int)i); + } + else if (str_enc == "UTF-16" || str_enc == "UTF-16LE" || + str_enc == "UTF-16BE") { + set_values_str = mputprintf(set_values_str, + "par%s.encode_utf16(buff_%d, CharCoding::UTF16%s);\n", subrefs_str, + (int)i, (str_enc == "UTF-16LE") ? "LE" : "BE"); + } + else if (str_enc == "UTF-32" || str_enc == "UTF-32LE" || + str_enc == "UTF-32BE") { + set_values_str = mputprintf(set_values_str, + "par%s.encode_utf32(buff_%d, CharCoding::UTF32%s);\n", subrefs_str, + (int)i, (str_enc == "UTF-32LE") ? "LE" : "BE"); + } + } + else { + // the encoding format is not known at compile-time, so an extra + // member and constructor parameter is needed to store it + expr->expr = mputstr(expr->expr, ", "); + v[i]->get_str_enc()->generate_code_expr(expr); + members_str = mputprintf(members_str, "CHARSTRING enc_fmt_%d;\n", + (int)i); + constr_params_str = mputprintf(constr_params_str, + ", CHARSTRING par_fmt_%d", (int)i); + constr_init_list_str = mputprintf(constr_init_list_str, + ", enc_fmt_%d(par_fmt_%d)", (int)i, (int)i); + set_values_str = mputprintf(set_values_str, + "CharCoding::CharCodingType coding = UNIVERSAL_CHARSTRING::" + "get_character_coding(enc_fmt_%d, \"decoded value redirect\");\n" + "switch (coding) {\n" + "case CharCoding::UTF_8:\n" + "par%s.encode_utf8(buff_%d, false);\n" + "break;\n" + "case CharCoding::UTF16:\n" + "case CharCoding::UTF16LE:\n" + "case CharCoding::UTF16BE:\n" + "par%s.encode_utf16(buff_%d, coding);\n" + "break;\n" + "case CharCoding::UTF32:\n" + "case CharCoding::UTF32LE:\n" + "case CharCoding::UTF32BE:\n" + "par%s.encode_utf32(buff_%d, coding);\n" + "break;\n" + "default:\n" + "break;\n" + "}\n", (int)i, subrefs_str, (int)i, subrefs_str, (int)i, + subrefs_str, (int)i); + } + break; + default: + FATAL_ERROR("ValueRedirect::generate_code"); + } + set_values_str = mputprintf(set_values_str, + "ptr_%d->decode(%s_descr_, buff_%d, TTCN_EncDec::CT_%s);\n" + "if (buff_%d.get_read_len() != 0) {\n" + "TTCN_error(\"Parameter redirect #%d failed, because the buffer was " + "not empty after decoding. Remaining octets: %%d.\", " + "(int)buff_%d.get_read_len());\n" + "}\n", + (int)i, member_type->get_genname_typedescriptor(scope).c_str(), (int)i, + member_type->get_coding(false).c_str(), (int)i, (int)(i + 1), (int)i); + } + else { + set_values_str = mputprintf(set_values_str, "*ptr_%d = par%s;\n", + (int)i, subrefs_str); + } + if (subrefs_expr.postamble != NULL) { + set_values_str = mputstr(set_values_str, subrefs_expr.postamble); + } + Code::free_expr(&subrefs_expr); + } + expr->expr = mputc(expr->expr, ')'); + if (!base_class_prefix.empty()) { + base_class_prefix += "::"; + } + // generate the new class with the gathered data + expr->preamble = mputprintf(expr->preamble, + "class Value_Redirect_%s : public %s%s_Redirect_Interface {\n" + // member declarations; one for each variable reference + "%s" + "public:\n" + // constructor: + // stores the pointers to the target variables in the class' members + "Value_Redirect_%s(%s)\n" + ": %s { }\n" + // set_values function: assigns the whole value or a part of it to each + // variable; the redirects marked with the '@decoded' modifier are decoded + // here before they are assigned + "void set_values(const %s& par)\n" + "{\n" + "%s" + "}\n" + "};\n", tmp_id.c_str(), + base_class_prefix.empty() ? "" : base_class_prefix.c_str(), + value_type->get_genname_value(value_type->get_my_scope()).c_str(), + members_str, tmp_id.c_str(), constr_params_str, constr_init_list_str, + value_type->get_genname_value(scope).c_str(), set_values_str); + Free(members_str); + Free(constr_params_str); + Free(constr_init_list_str); + Free(set_values_str); + } // ================================= // ===== LogArgument diff --git a/compiler2/ttcn3/Statement.hh b/compiler2/ttcn3/Statement.hh index 7da17d1f5..30d243b45 100644 --- a/compiler2/ttcn3/Statement.hh +++ b/compiler2/ttcn3/Statement.hh @@ -42,6 +42,7 @@ namespace Ttcn { class VariableEntry; class VariableEntries; class ParamRedirect; + class ValueRedirect; class LogArgument; class LogArguments; class IfClause; @@ -293,7 +294,7 @@ namespace Ttcn { TemplateInstance *rcvpar; TemplateInstance *fromclause; struct { - Reference *value; + ValueRedirect *value; ParamRedirect *param; Reference *sender; } redirect; @@ -334,7 +335,7 @@ namespace Ttcn { /**< used if S_DONE, S_KILLED and compref==0 */ struct { TemplateInstance *donematch; - Reference *redirect; + ValueRedirect *redirect; } donereturn; /**< used if S_DONE and compref!=0 */ Ref_base *funcinstref; /**< used if S_START_COMP */ struct {/** used if S_START_COMP_REFD */ @@ -513,7 +514,7 @@ namespace Ttcn { * S_TRIGGER. p_ref==0 means any port. */ Statement(statementtype_t p_st, Reference *p_ref, TemplateInstance *p_templinst, TemplateInstance *p_fromclause, - Reference *p_redirectval, Reference *p_redirectsender); + ValueRedirect *p_redirectval, Reference *p_redirectsender); /** Constructor used by S_GETCALL and S_CHECK_GETCALL. p_ref==0 * means any port. */ Statement(statementtype_t p_st, Reference *p_ref, @@ -524,14 +525,14 @@ namespace Ttcn { Statement(statementtype_t p_st, Reference *p_ref, TemplateInstance *p_templinst, TemplateInstance *p_valuematch, TemplateInstance *p_fromclause, - Reference *p_redirectval, ParamRedirect *p_redirectparam, + ValueRedirect *p_redirectval, ParamRedirect *p_redirectparam, Reference *p_redirectsender); /** Constructor used by S_CATCH and S_CHECK_CATCH. p_ref==0 means * any port. */ Statement(statementtype_t p_st, Reference *p_ref, Reference *p_sig, TemplateInstance *p_templinst, bool p_timeout, TemplateInstance *p_fromclause, - Reference *p_redirectval, Reference *p_redirectsender); + ValueRedirect *p_redirectval, Reference *p_redirectsender); /** Constructor used by S_CHECK. p_ref==0 means any port. */ Statement(statementtype_t p_st, Reference *p_ref, TemplateInstance *p_fromclause, Reference *p_redirectsender); @@ -546,7 +547,7 @@ namespace Ttcn { ParsedActualParameters *p_ap_list); /** Constructor used by S_DONE */ Statement(statementtype_t p_st, Value *p_compref, - TemplateInstance *p_donematch, Reference *p_redirect); + TemplateInstance *p_donematch, ValueRedirect *p_redirect); /** Constructor used by S_DONE, S_KILLED */ Statement(statementtype_t p_st, component_t p_anyall); /** Constructor used by S_CONNECT, S_DISCONNECT, S_MAP, S_UNMAP */ @@ -692,16 +693,8 @@ namespace Ttcn { static Type *get_outgoing_type(TemplateInstance *p_ti); /** Determines and returns the type of the incoming message or * signature based on a template instance \a p_ti and an optional - * value redirect \a p_val_redir. Flag \a p_val_redir_checked is - * set to true if the value redirect was analyzed during this - * operation. */ - Type *get_incoming_type(TemplateInstance *p_ti, Reference *p_val_redir, - bool& p_val_redir_checked); - /** Checks the variable reference of a value redirect. If - * parameter \a p_type is not NULL it points to the type that the - * variable reference should match. The type of variable is - * returned. */ - Type *chk_value_redirect(Reference *p_ref, Type *p_type); + * value redirect \a p_val_redir. */ + Type *get_incoming_type(TemplateInstance *p_ti, ValueRedirect *p_val_redir); /** Checks the variable reference of a sender redirect. The type * of the variable (or NULL in case of non-existent sender clause * or error) is returned. The type of the variable is also @@ -880,11 +873,19 @@ namespace Ttcn { private: Identifier *id; Reference *ref; + /** indicates whether the redirected parameter should be decoded */ + bool decoded; + /** encoding format for decoded universal charstring parameter redirects */ + Value* str_enc; + /** pointer to the type the redirected parameter is decoded into, if the + * '@decoded' modifier is used (not owned) */ + Type* dec_type; ParamAssignment(const ParamAssignment& p); ParamAssignment& operator=(const ParamAssignment& p); public: - ParamAssignment(Identifier *p_id, Reference *p_ref); + ParamAssignment(Identifier *p_id, Reference *p_ref, bool p_decoded, + Value* p_str_enc); virtual ~ParamAssignment(); virtual ParamAssignment* clone() const; virtual void set_my_scope(Scope *p_scope); @@ -892,6 +893,11 @@ namespace Ttcn { const Identifier& get_id() const { return *id; } Reference *get_ref() const; Reference *steal_ref(); + bool is_decoded() const { return decoded; } + Value* get_str_enc() const; + Value* steal_str_enc(); + void set_dec_type(Type* p_dec_type) { dec_type = p_dec_type; } + Type* get_dec_type() const { return dec_type; } }; /** @@ -922,18 +928,32 @@ namespace Ttcn { class VariableEntry : public Node, public Location { private: Reference *ref; /**< varref or notused if NULL. */ + /** indicates whether the redirected parameter should be decoded */ + bool decoded; + /** encoding format for decoded universal charstring parameter redirects + * (is only set when the AssignmentList is converted to a VariableList) */ + Value* str_enc; + /** pointer to the type the redirected parameter is decoded into, if the + * '@decoded' modifier is used + * (is only set when the AssignmentList is converted to a VariableList, + * not owned) */ + Type* dec_type; VariableEntry(const VariableEntry& p); VariableEntry& operator=(const VariableEntry& p); public: /** Creates a notused entry */ - VariableEntry() : Node(), Location(), ref(0) { } + VariableEntry() : Node(), Location(), ref(0), decoded(false), str_enc(NULL), dec_type(NULL) { } VariableEntry(Reference *p_ref); + VariableEntry(Reference* p_ref, bool p_decoded, Value* p_str_enc, Type* p_dec_type); virtual ~VariableEntry(); virtual VariableEntry* clone() const; virtual void set_my_scope(Scope *p_scope); virtual void set_fullname(const string& p_fullname); Reference *get_ref() const { return ref; } + bool is_decoded() const { return decoded; } + Value* get_str_enc() const { return str_enc; } + Type* get_dec_type() const { return dec_type; } }; /** @@ -1006,6 +1026,98 @@ namespace Ttcn { * to \a p_code_section. */ void set_code_section(GovernedSimple::code_section_t p_code_section); void generate_code(expression_struct_t *expr); + /** generates a new redirect class, inherited from the signature type's + * original redirect class, which extends its functionality to also + * decode the redirected parameters that have the '@decoded' modifier */ + char* generate_code_decoded(char* str, Type* sig_type, const char* tmp_id, + bool is_out); + /** returns true if at least one of the parameter redirects has the + * '@decoded' modifier */ + bool has_decoded_modifier() const; + }; + + /** + * Class for storing a single element of a value redirect + * Each of these elements can be: + * - a lone variable reference (in this case the whole value is redirected to + * the referenced variable), or + * - the assignment of a field or a field's sub-reference to a variable + * (in this case one of the value's fields, or a sub-reference of one of the + * fields is redirected to the referenced variable; this can only happen if + * the value is a record or set). + */ + class SingleValueRedirect : public Node, public Location { + private: + /** reference to the variable the value is redirected to */ + Reference* var_ref; + /** indicates which part (record field or array element) of the value is + * redirected (optional) */ + FieldOrArrayRefs* subrefs; + /** indicates whether the redirected field or element should be decoded + * (only used if subrefs is not null) */ + bool decoded; + /** encoding format for decoded universal charstring value redirects + * (only used if subrefs is not null and decoded is true) */ + Value* str_enc; + /** pointer to the type the redirected field or element is decoded into + * (only used if subrefs is not null and decoded is true), not owned*/ + Type* dec_type; + + SingleValueRedirect(const SingleValueRedirect&); + SingleValueRedirect& operator=(const SingleValueRedirect&); + public: + SingleValueRedirect(Reference* p_var_ref); + SingleValueRedirect(Reference* p_var_ref, FieldOrArrayRefs* p_subrefs, + bool p_decoded, Value* p_str_enc); + virtual ~SingleValueRedirect(); + virtual SingleValueRedirect* clone() const; + virtual void set_my_scope(Scope *p_scope); + virtual void set_fullname(const string& p_fullname); + Reference *get_var_ref() const { return var_ref; } + FieldOrArrayRefs *get_subrefs() const { return subrefs; } + bool is_decoded() const { return decoded; } + Value* get_str_enc() const { return str_enc; } + void set_dec_type(Type* p_dec_type) { dec_type = p_dec_type; } + Type* get_dec_type() const { return dec_type; } + }; + + /** + * Class for storing a value redirect + */ + class ValueRedirect : public Node, public Location { + /** list of single value redirect elements */ + vector<SingleValueRedirect> v; + /** pointer to the type of the redirected value, not owned */ + Type* value_type; + + ValueRedirect(const ValueRedirect&); + ValueRedirect& operator=(const ValueRedirect&); + public: + ValueRedirect(): v(), value_type(NULL) { } + virtual ~ValueRedirect(); + virtual ValueRedirect* clone() const; + virtual void set_my_scope(Scope* p_scope); + virtual void set_fullname(const string& p_fullname); + void set_code_section(GovernedSimple::code_section_t p_code_section); + void add(SingleValueRedirect* ptr); + /** Attempts to identify the type of the redirected value. Only those single + * redirects are checked, which redirect the whole value, not just a field. + * If multiple whole-value-redirects of separate types are found, then an + * error is displayed. */ + Type* get_type() const; + /** Performs semantic analysis on the value redirect without knowing the + * type of the redirected value. Called when the value's type cannot be + * determined or is erroneous. */ + void chk_erroneous(); + /** Performs the full semantic analysis on the value redirect. + * @param p_type the type of the redirected value */ + void chk(Type* p_type); + /** Generates code for the value redirect in the specified expression + * structure. A new class is generated for every value redirect, which + * handles the redirecting. + * @param base_class_prefix the namespace and/or class prefix of the + * base value redirect class of the appropriate type */ + void generate_code(expression_struct* expr, string base_class_prefix); }; /** diff --git a/compiler2/ttcn3/Ttcnstuff.cc b/compiler2/ttcn3/Ttcnstuff.cc index 3f68c29b0..685cd4132 100644 --- a/compiler2/ttcn3/Ttcnstuff.cc +++ b/compiler2/ttcn3/Ttcnstuff.cc @@ -1816,6 +1816,8 @@ namespace Ttcn { pdef.msg_in.elements[i].name = pool.add(type->get_genname_value(my_scope)); pdef.msg_in.elements[i].dispname = pool.add(type->get_typename()); + pdef.msg_in.elements[i].name_w_no_prefix = pool.add(type->get_genname_value( + type->get_type_refd_last()->get_my_scope())); } } else { pdef.msg_in.nElements = 0; diff --git a/compiler2/ttcn3/compiler.y b/compiler2/ttcn3/compiler.y index 11e2ad80f..7c69951e4 100644 --- a/compiler2/ttcn3/compiler.y +++ b/compiler2/ttcn3/compiler.y @@ -211,6 +211,8 @@ static const string anyname("anytype"); CompTypeRefList *comprefs; ComponentTypeBody *compbody; template_restriction_t template_restriction; + ValueRedirect* value_redirect; + SingleValueRedirect* single_value_redirect; struct { bool is_raw; @@ -344,7 +346,7 @@ static const string anyname("anytype"); } portraiseop; struct { - Ttcn::Reference *redirectval; + ValueRedirect *redirectval; Ttcn::Reference *redirectsender; } portredirect; @@ -354,7 +356,7 @@ static const string anyname("anytype"); } portredirectwithparam; struct { - Ttcn::Reference *redirectval; + ValueRedirect *redirectval; ParamRedirect *redirectparam; Ttcn::Reference *redirectsender; } portredirectwithvalueandparam; @@ -367,7 +369,7 @@ static const string anyname("anytype"); struct { TemplateInstance *templ_inst; TemplateInstance *fromclause; - Ttcn::Reference *redirectval; + ValueRedirect *redirectval; Ttcn::Reference *redirectsender; } portreceiveop; @@ -382,7 +384,7 @@ static const string anyname("anytype"); TemplateInstance *templ_inst; TemplateInstance *valuematch; TemplateInstance *fromclause; - Ttcn::Reference *redirectval; + ValueRedirect *redirectval; ParamRedirect *redirectparam; Ttcn::Reference *redirectsender; } portgetreplyop; @@ -398,7 +400,7 @@ static const string anyname("anytype"); TemplateInstance *templ_inst; bool timeout; TemplateInstance *fromclause; - Ttcn::Reference *redirectval; + ValueRedirect *redirectval; Ttcn::Reference *redirectsender; } portcatchop; @@ -409,7 +411,7 @@ static const string anyname("anytype"); TemplateInstance *valuematch; bool timeout; TemplateInstance *fromclause; - Ttcn::Reference *redirectval; + ValueRedirect *redirectval; ParamRedirect *redirectparam; Ttcn::Reference *redirectsender; } portcheckop; @@ -428,7 +430,7 @@ static const string anyname("anytype"); struct { TemplateInstance *donematch; - Ttcn::Reference *redirect; + ValueRedirect *redirect; } donepar; struct { @@ -540,6 +542,16 @@ static const string anyname("anytype"); Value* string_encoding; TemplateInstance* target_template; } decode_match; + + struct { + bool is_decoded; + Value* string_encoding; + } decoded_modifier; + + struct { + size_t nElements; + SingleValueRedirect** elements; + } single_value_redirect_list; } /* Tokens of TTCN-3 */ @@ -972,7 +984,7 @@ static const string anyname("anytype"); %type <refbase> DerivedRefWithParList TemplateRefWithParList DecValueArg %type <refpard> FunctionInstance AltstepInstance %type <reference> PortType optDerivedDef DerivedDef Signature VariableRef - TimerRef TimerRefOrAny Port PortOrAny PortOrAll ValueSpec + TimerRef TimerRefOrAny Port PortOrAny PortOrAll ValueStoreSpec SenderSpec ComponentType optRunsOnSpec RunsOnSpec optSystemSpec %type <valuerange> Range %type <type> NestedEnumDef NestedRecordDef NestedRecordOfDef NestedSetDef @@ -1059,6 +1071,10 @@ static const string anyname("anytype"); %type <erroneous_indicator> ErroneousIndicator %type <imptype> ImportSpec ImportElement %type <decode_match> DecodedContentMatch +%type <decoded_modifier> optDecodedModifier +%type <value_redirect> ValueSpec +%type <single_value_redirect> SingleValueSpec +%type <single_value_redirect_list> SingleValueSpecList /********************************************************************* * Destructors @@ -1291,6 +1307,7 @@ SingleExpression SingleLowerBound SingleTimerInstance SingleValueOrAttrib +SingleValueSpec SingleWithAttrib DecValueArg StartStatement @@ -1350,6 +1367,7 @@ ValueMatchSpec ValueOrAttribList ValueOrRange ValueSpec +ValueStoreSpec ValueofOp VariableAssignment VariableEntry @@ -1423,6 +1441,7 @@ ModulePar ModuleParDef MultiTypedModuleParList PortInstance +SingleValueSpecList TimerInstance TimerList VarInstance @@ -1757,6 +1776,13 @@ optRunsOnComprefOrSelf } DecodedContentMatch +%destructor { + if ($$.string_encoding != NULL) { + delete $$.string_encoding; + } +} +optDecodedModifier + /********************************************************************* * Operator precedences (lowest first) *********************************************************************/ @@ -6203,17 +6229,75 @@ optPortRedirect: // [387] ; ValueSpec: // 389 + ValueStoreSpec + { + $$ = new ValueRedirect(); + SingleValueRedirect* p = new SingleValueRedirect($1); + p->set_location(infile, @$); + $$->add(p); + $$->set_location(infile, @$); + } +| ValueKeyword '(' SingleValueSpecList ')' + { + $$ = new ValueRedirect(); + for (size_t i = 0; i < $3.nElements; ++i) { + $$->add($3.elements[i]); + } + Free($3.elements); + $$->set_location(infile, @$); + } +; + +ValueStoreSpec: ValueKeyword VariableRef { $$ = $2; } | ValueKeyword error { $$ = 0; } ; +SingleValueSpecList: + SingleValueSpec + { + $$.nElements = 1; + $$.elements = (SingleValueRedirect**)Malloc(sizeof(SingleValueRedirect*)); + $$.elements[0] = $1; + } +| SingleValueSpecList ',' SingleValueSpec + { + $$.nElements = $1.nElements + 1; + $$.elements = (SingleValueRedirect**)Realloc($1.elements, + $$.nElements * sizeof(SingleValueRedirect*)); + $$.elements[$$.nElements - 1] = $3; + } +; + +SingleValueSpec: + VariableRef + { + $$ = new SingleValueRedirect($1); + $$->set_location(infile, @$); + } +| VariableRef AssignmentChar optDecodedModifier PredefOrIdentifier + optExtendedFieldReference + { + FieldOrArrayRef* field_ref = new FieldOrArrayRef($4); + field_ref->set_location(infile, @4); + FieldOrArrayRefs* subrefs = new FieldOrArrayRefs; + subrefs->add(field_ref); + for (size_t i = 0; i < $5.nElements; ++i) { + subrefs->add($5.elements[i]); + } + Free($5.elements); + $$ = new SingleValueRedirect($1, subrefs, $3.is_decoded, $3.string_encoding); + $$->set_location(infile, @$); + } +; + SenderSpec: // 391 SenderKeyword VariableRef { $$ = $2; } | SenderKeyword error { $$ = 0; } ; IndexSpec: - IndexKeyword ValueSpec + IndexKeyword ValueStoreSpec { Location loc(infile, @1); loc.error("Modifier '@index' is not currently supported."); @@ -6352,23 +6436,26 @@ AssignmentList: // 404 VariableAssignment: // 405 VariableRef AssignmentChar optDecodedModifier IDentifier { - $$ = new ParamAssignment($4, $1); + $$ = new ParamAssignment($4, $1, $3.is_decoded, $3.string_encoding); $$->set_location(infile, @$); } ; optDecodedModifier: /* empty */ + { + $$.is_decoded = false; + $$.string_encoding = NULL; + } | DecodedKeyword { - Location loc(infile, @1); - loc.error("Modifier '@decoded' is not currently supported."); + $$.is_decoded = true; + $$.string_encoding = NULL; } | DecodedKeyword '(' SingleExpression ')' { - Location loc(infile, @1); - loc.error("Modifier '@decoded' is not currently supported."); - delete $3; + $$.is_decoded = true; + $$.string_encoding = $3; } ; diff --git a/compiler2/ttcn3/port.c b/compiler2/ttcn3/port.c index 88a8cf5cb..fa0276e91 100644 --- a/compiler2/ttcn3/port.c +++ b/compiler2/ttcn3/port.c @@ -565,12 +565,12 @@ static void generate_receive(char **def_ptr, char **src_ptr, } def = mputprintf(def, "alt_status %s(const %s_template& value_template, " - "%s *value_ptr, const %s_template& sender_template, %s " + "%s_Redirect_Interface *value_redirect, const %s_template& sender_template, %s " "*sender_ptr);\n", function_name, message_type->name, - message_type->name, sender_type, sender_type); + message_type->name_w_no_prefix, sender_type, sender_type); src = mputprintf(src, "alt_status %s::%s(const %s_template& " - "value_template, %s *value_ptr, const %s_template& " + "value_template, %s_Redirect_Interface *value_redirect, const %s_template& " "sender_template, %s *sender_ptr)\n" "{\n" "msg_queue_item *my_head = (msg_queue_item*)msg_queue_head;\n" @@ -583,7 +583,7 @@ static void generate_receive(char **def_ptr, char **src_ptr, "return ALT_NO;\n" "}\n" "} else ", class_name, function_name, message_type->name, - message_type->name, sender_type, sender_type); + message_type->name_w_no_prefix, sender_type, sender_type); if (is_address) { src = mputprintf(src, "if (my_head->sender_component != " "SYSTEM_COMPREF) {\n" @@ -658,7 +658,10 @@ static void generate_receive(char **def_ptr, char **src_ptr, if (is_trigger) src = mputstr(src, "remove_msg_queue_head();\n"); src = mputprintf(src, "return %s;\n" "} else {\n" - "if (value_ptr != NULL) *value_ptr = *my_head->message_%lu;\n" + "if (value_redirect != NULL) {\n" + "value_redirect->set_values(*my_head->message_%lu);\n" + "delete value_redirect;\n" + "}\n" "if (sender_ptr != NULL) *sender_ptr = %smy_head->%s;\n", failed_status, (unsigned long) message_index, is_address ? "*" : "", is_address ? "sender_address" : "sender_component"); @@ -1561,6 +1564,20 @@ void defPortClass(const port_def* pdef, output_struct* output) def = mputstr(def, "public:\n"); + + /* value redirect base classes (interfaces) */ + if (has_msg_queue) { + for (i = 0; i < pdef->msg_in.nElements; ++i) { + def = mputprintf(def, + "class %s_Redirect_Interface {\n" + "public:\n" + "virtual void set_values(const %s&) = 0;\n" + "virtual ~%s_Redirect_Interface() { }\n" + "};\n", pdef->msg_in.elements[i].name_w_no_prefix, + pdef->msg_in.elements[i].name, + pdef->msg_in.elements[i].name_w_no_prefix); + } + } /* constructor */ def = mputprintf(def, "%s(const char *par_port_name", class_name); diff --git a/compiler2/ttcn3/port.h b/compiler2/ttcn3/port.h index 206257875..e0673c0dc 100644 --- a/compiler2/ttcn3/port.h +++ b/compiler2/ttcn3/port.h @@ -7,6 +7,7 @@ * * Contributors: * Balasko, Jeno + * Baranyi, Botond * Cserveni, Akos * Delic, Adam * Kremer, Peter @@ -25,6 +26,7 @@ typedef struct port_msg_type_tag { const char *name; const char *dispname; + const char* name_w_no_prefix; } port_msg_type; typedef struct port_msg_type_list_tag { diff --git a/compiler2/ttcn3/signature.c b/compiler2/ttcn3/signature.c index 3e55f7bce..aac84fdcf 100644 --- a/compiler2/ttcn3/signature.c +++ b/compiler2/ttcn3/signature.c @@ -178,7 +178,7 @@ void defSignatureClasses(const signature_def *sdef, output_struct *output) /* set_parameters function (used for param redirect in getcall) */ if (num_in > 0) { - def = mputprintf(def, "void set_parameters(const %s_call& call_par) " + def = mputprintf(def, "virtual void set_parameters(const %s_call& call_par) " "const;\n", name); src = mputprintf(src, "void %s_call_redirect::set_parameters(const " "%s_call& call_par) const\n" @@ -329,10 +329,24 @@ void defSignatureClasses(const signature_def *sdef, output_struct *output) /* class definition */ def = mputprintf(def, "class %s_reply_redirect {\n", name); + + if (sdef->return_type != NULL) { + /* value redirect base class (interface) for the return value */ + def = mputprintf(def, + "public:\n" + "class %s_Redirect_Interface {\n" + "public:\n" + "virtual void set_values(const %s&) = 0;\n" + "virtual ~%s_Redirect_Interface() { }\n" + "};\n" + "private:\n", sdef->return_type_w_no_prefix, sdef->return_type, + sdef->return_type_w_no_prefix); + } /* parameter pointers */ if (sdef->return_type != NULL) { - def = mputprintf(def, "%s *ret_val_ptr;\n", sdef->return_type); + def = mputprintf(def, "%s_Redirect_Interface *ret_val_redir;\n", + sdef->return_type_w_no_prefix); } for (i = 0; i < sdef->parameters.nElements; i++) { if (sdef->parameters.elements[i].direction != PAR_IN) { @@ -343,13 +357,14 @@ void defSignatureClasses(const signature_def *sdef, output_struct *output) } def = mputstr(def, "public:\n"); - + if (num_out > 0 || sdef->return_type != NULL) { boolean first_param = TRUE; /* constructor */ def = mputprintf(def, "%s_reply_redirect(", name); if (sdef->return_type != NULL) { - def = mputprintf(def, "%s *return_ptr", sdef->return_type); + def = mputprintf(def, "%s_Redirect_Interface *return_redirect", + sdef->return_type_w_no_prefix); first_param = FALSE; } for (i = 0; i < sdef->parameters.nElements; i++) { @@ -365,7 +380,7 @@ void defSignatureClasses(const signature_def *sdef, output_struct *output) " : "); first_param = TRUE; if (sdef->return_type != NULL) { - def = mputstr(def, "ret_val_ptr(return_ptr)"); + def = mputstr(def, "ret_val_redir(return_redirect)"); first_param = FALSE; } for (i = 0; i < sdef->parameters.nElements; i++) { @@ -380,11 +395,17 @@ void defSignatureClasses(const signature_def *sdef, output_struct *output) def = mputstr(def, " { }\n"); } /* otherwise constructor is not needed */ + + if (sdef->return_type != NULL) { + /* destructor (only the return redirect object needs to be deleted) */ + def = mputprintf(def, "~%s_reply_redirect() { " + "if (ret_val_redir != NULL) delete ret_val_redir; }\n", name); + } /* set_parameters function (used for param redirect in getreply) */ if (num_out > 0 || sdef->return_type != NULL) { /* if there are "out" or "inout" parameters or a "return" ... */ - def = mputprintf(def, "void set_parameters(const %s_reply& reply_par) " + def = mputprintf(def, "virtual void set_parameters(const %s_reply& reply_par) " "const;\n", name); src = mputprintf(src, "void %s_reply_redirect::set_parameters(const " "%s_reply& reply_par) const\n" @@ -399,8 +420,8 @@ void defSignatureClasses(const signature_def *sdef, output_struct *output) } } if (sdef->return_type!=NULL) { - src = mputstr(src, "if (ret_val_ptr != NULL) " - "*ret_val_ptr = reply_par.return_value();\n"); + src = mputstr(src, "if (ret_val_redir != NULL) " + "ret_val_redir->set_values(reply_par.return_value());\n"); } src = mputstr(src, "}\n\n"); } @@ -661,11 +682,28 @@ void defSignatureClasses(const signature_def *sdef, output_struct *output) */ decl = mputprintf(decl, "class %s_exception_template;\n", name); - def = mputprintf(def, "class %s_exception_template {\n", name); + def = mputprintf(def, + "class %s_exception_template {\n" + "public:\n", name); + + /* value redirect base classes (interfaces) */ + for (i = 0; i < sdef->exceptions.nElements; i++) { + def = mputprintf(def, + "public:\n" + "class %s_Redirect_Interface {\n" + "public:\n" + "virtual void set_values(const %s&) = 0;\n" + "virtual ~%s_Redirect_Interface() { }\n" + "};\n", sdef->exceptions.elements[i].name_w_no_prefix, + sdef->exceptions.elements[i].name, + sdef->exceptions.elements[i].name_w_no_prefix); + } /* data members */ /* exception-selection enum */ - def = mputprintf(def, "%s exception_selection;\n", selection_type); + def = mputprintf(def, + "private:\n" + "%s exception_selection;\n", selection_type); /* union of all possible exceptions (templates) */ def = mputstr(def, "union {\n"); for (i = 0; i < sdef->exceptions.nElements; i++) { @@ -674,33 +712,54 @@ void defSignatureClasses(const signature_def *sdef, output_struct *output) sdef->exceptions.elements[i].altname); } def = mputstr(def, "};\n"); - /* union of all possible value redirect pointers */ + /* union of all possible value redirect objects */ def = mputstr(def, "union {\n"); for (i = 0; i < sdef->exceptions.nElements; i++) { - def = mputprintf(def, "%s *ptr_%s;\n", - sdef->exceptions.elements[i].name, + def = mputprintf(def, "%s_Redirect_Interface *%s_redir;\n", + sdef->exceptions.elements[i].name_w_no_prefix, sdef->exceptions.elements[i].altname); } def = mputstr(def, "};\n" "public:\n"); - /* constructors (for all possible template + redirect pointer pairs) */ + /* constructors (for all possible template + redirect object pairs) */ for (i = 0; i < sdef->exceptions.nElements; i++) { def = mputprintf(def, "%s_exception_template(const %s_template& " - "init_template, %s *value_ptr = NULL);\n", name, + "init_template, %s_Redirect_Interface *value_redirect = NULL);\n", name, sdef->exceptions.elements[i].name, - sdef->exceptions.elements[i].name); + sdef->exceptions.elements[i].name_w_no_prefix); src = mputprintf(src, "%s_exception_template::%s_exception_template" - "(const %s_template& init_template, %s *value_ptr)\n" + "(const %s_template& init_template, %s_Redirect_Interface *value_redirect)\n" "{\n" "exception_selection = %s_%s;\n" "field_%s = &init_template;\n" - "ptr_%s = value_ptr;\n" + "%s_redir = value_redirect;\n" "}\n\n", name, name, sdef->exceptions.elements[i].name, - sdef->exceptions.elements[i].name, selection_prefix, + sdef->exceptions.elements[i].name_w_no_prefix, selection_prefix, sdef->exceptions.elements[i].altname, sdef->exceptions.elements[i].altname, sdef->exceptions.elements[i].altname); } + + /* destructor */ + def = mputprintf(def, "~%s_exception_template();\n", name); + src = mputprintf(src, + "%s_exception_template::~%s_exception_template()\n" + "{\n" + "switch (exception_selection) {\n", name, name); + for (i = 0; i < sdef->exceptions.nElements; i++) { + src = mputprintf(src, + "case %s_%s:\n" + "if (%s_redir != NULL) delete %s_redir;\n" + "break;", selection_prefix, sdef->exceptions.elements[i].altname, + sdef->exceptions.elements[i].altname, + sdef->exceptions.elements[i].altname); + } + src = mputprintf(src, + "default:\n" + "TTCN_error(\"Internal error: Invalid selector when deleting exception " + "object for signature %s.\");\n" + "}\n" + "}\n\n", dispname); /* match function */ def = mputprintf(def, "boolean match(const %s_exception& other_value," @@ -779,7 +838,7 @@ void defSignatureClasses(const signature_def *sdef, output_struct *output) "switch (exception_selection) {\n", name, name); for (i = 0; i < sdef->exceptions.nElements; i++) { src = mputprintf(src, "case %s_%s:\n" - "if (ptr_%s != NULL) *ptr_%s = source_value.%s_field();\n" + "if (%s_redir != NULL) %s_redir->set_values(source_value.%s_field());\n" "return;\n", selection_prefix, sdef->exceptions.elements[i].altname, sdef->exceptions.elements[i].altname, diff --git a/compiler2/ttcn3/signature.h b/compiler2/ttcn3/signature.h index 9208e0a18..ac38da99f 100644 --- a/compiler2/ttcn3/signature.h +++ b/compiler2/ttcn3/signature.h @@ -7,6 +7,7 @@ * * Contributors: * Balasko, Jeno + * Baranyi, Botond * Szabo, Janos Zoltan – initial implementation * Tatarka, Gabor * @@ -37,6 +38,7 @@ typedef struct { const char *name; const char *dispname; const char *altname; + const char* name_w_no_prefix; } signature_exception; typedef struct { @@ -49,6 +51,7 @@ typedef struct { const char *dispname; signature_par_list parameters; const char *return_type; + const char* return_type_w_no_prefix; boolean is_noblock; signature_exception_list exceptions; } signature_def; diff --git a/core/Universal_charstring.cc b/core/Universal_charstring.cc index 079a1335e..1078084c8 100644 --- a/core/Universal_charstring.cc +++ b/core/Universal_charstring.cc @@ -2911,6 +2911,36 @@ int UNIVERSAL_CHARSTRING::check_BOM(CharCoding::CharCodingType expected_coding, return 0; } +CharCoding::CharCodingType UNIVERSAL_CHARSTRING::get_character_coding + (const char* coding_str, const char* context_str) +{ + CharCoding::CharCodingType new_coding = CharCoding::UTF_8; + if (coding_str != NULL && strcmp(coding_str, "UTF-8") != 0) { + if (strcmp(coding_str, "UTF-16") == 0) { + new_coding = CharCoding::UTF16; + } + else if (strcmp(coding_str, "UTF-16LE") == 0) { + new_coding = CharCoding::UTF16LE; + } + else if (strcmp(coding_str, "UTF-16BE") == 0) { + new_coding = CharCoding::UTF16BE; + } + else if (strcmp(coding_str, "UTF-32") == 0) { + new_coding = CharCoding::UTF32; + } + else if (strcmp(coding_str, "UTF-32LE") == 0) { + new_coding = CharCoding::UTF32LE; + } + else if (strcmp(coding_str, "UTF-32BE") == 0) { + new_coding = CharCoding::UTF32BE; + } + else { + TTCN_error("Invalid string serialization for %s.", context_str); + } + } + return new_coding; +} + // member functions of class UNIVERSAL_CHARSTRING_ELEMENTS UNIVERSAL_CHARSTRING_ELEMENT::UNIVERSAL_CHARSTRING_ELEMENT @@ -4167,30 +4197,8 @@ void UNIVERSAL_CHARSTRING_template::set_decmatch(Dec_Match_Interface* new_instan TTCN_error("Setting the decoded content matching mechanism of a non-decmatch " "universal charstring template."); } - CharCoding::CharCodingType new_coding = CharCoding::UTF_8; - if (coding_str != NULL && strcmp(coding_str, "UTF-8") != 0) { - if (strcmp(coding_str, "UTF-16") == 0) { - new_coding = CharCoding::UTF16; - } - else if (strcmp(coding_str, "UTF-16LE") == 0) { - new_coding = CharCoding::UTF16LE; - } - else if (strcmp(coding_str, "UTF-16BE") == 0) { - new_coding = CharCoding::UTF16BE; - } - else if (strcmp(coding_str, "UTF-32") == 0) { - new_coding = CharCoding::UTF32; - } - else if (strcmp(coding_str, "UTF-32LE") == 0) { - new_coding = CharCoding::UTF32LE; - } - else if (strcmp(coding_str, "UTF-32BE") == 0) { - new_coding = CharCoding::UTF32BE; - } - else { - TTCN_error("Invalid string serialization for decoded content matching."); - } - } + CharCoding::CharCodingType new_coding = UNIVERSAL_CHARSTRING::get_character_coding( + coding_str, "decoded content match"); dec_match = new unichar_decmatch_struct; dec_match->ref_count = 1; dec_match->instance = new_instance; diff --git a/core/Universal_charstring.hh b/core/Universal_charstring.hh index e76cb63aa..eb5f8a9db 100644 --- a/core/Universal_charstring.hh +++ b/core/Universal_charstring.hh @@ -391,6 +391,14 @@ public: /** Decodes accordingly to the JSON encoding rules. * Returns the length of the decoded data. */ int JSON_decode(const TTCN_Typedescriptor_t&, JSON_Tokenizer&, boolean); + + /** Converts a string containing a character coding format to its corresponding + * enum value. + * @param coding_str string containing the coding format + * @param context_str string containing the context this function was called + * from (printed as part of an error message if the coding string is invalid) */ + static CharCoding::CharCodingType get_character_coding(const char* coding_str, + const char* context_str); private: #ifdef TITAN_RUNTIME_2 diff --git a/regression_test/commMessage/TcommMessage.ttcn b/regression_test/commMessage/TcommMessage.ttcn index dbe56a7eb..96435e76a 100644 --- a/regression_test/commMessage/TcommMessage.ttcn +++ b/regression_test/commMessage/TcommMessage.ttcn @@ -7,6 +7,7 @@ * * Contributors: * Balasko, Jeno + * Baranyi, Botond * Bartha, Norbert * Delic, Adam * Dimitrov, Peter @@ -1473,6 +1474,141 @@ testcase commMessageDualFacedPorts2() runs on ImplicitMessageEncodingComponent { rc.stop; } +// test for value redirect with multiple variable references and field assignments +testcase commMessageMultiValueRedirect() runs on commMessage_comp3 { + connect(mtc:Port3, mtc:Port3); + var commMessage_trecord rec_val := { x1 := 3, x2 := -1.0 }; + Port3.send(rec_val); + var commMessage_trecord redir_rec; + var integer redir_int; + var float redir_float; + timer tmr := 1.0; + tmr.start; + alt { + [] Port3.receive(rec_val) -> value (redir_rec, redir_int := x1, redir_float := x2) { + if (redir_rec != rec_val) { setverdict(fail, "Invalid record value: ", redir_rec); } + if (redir_int != rec_val.x1) { setverdict(fail, "Invalid integer value: ", redir_int); } + if (redir_float != rec_val.x2) { setverdict(fail, "Invalid float value: ", redir_float); } + } + [] Port3.receive(commMessage_trecord:?) { setverdict(fail, "Incorrect value received."); } + [] tmr.timeout { setverdict(fail, "Timeout."); } + } + setverdict(pass); +} + +type record commMessage_tPayloadRecord { + bitstring bs optional, + hexstring hs optional, + octetstring os optional, + charstring cs optional, + universal charstring ucs optional +} + +type record commMessage_tDecoded { + integer num, + charstring str +} +with { + encode "JSON"; +} + +type port commMessage_port_dec message { + inout commMessage_tPayloadRecord +} +with { extension "internal" }; + +type component commMessage_comp_dec { + port commMessage_port_dec pt +} + +// test for decoded value redirect +testcase commMessageDecodedValueRedirect() runs on commMessage_comp_dec { + connect(mtc:pt, mtc:pt); + var commMessage_tDecoded val := { num := 10, str := "abc" }; + var bitstring bs_val := encvalue(val); + var hexstring hs_val := bit2hex(encvalue(val)); + var octetstring os_val := bit2oct(encvalue(val)); + var charstring cs_val := oct2char(bit2oct(encvalue(val))); + var universal charstring ucs_val := encvalue_unichar(val, "UTF-8"); + pt.send(commMessage_tPayloadRecord: { bs_val, omit, omit, omit, omit }); + pt.send(commMessage_tPayloadRecord: { omit, hs_val, omit, omit, omit }); + pt.send(commMessage_tPayloadRecord: { omit, omit, os_val, omit, omit }); + pt.send(commMessage_tPayloadRecord: { omit, omit, omit, cs_val, omit }); + pt.send(commMessage_tPayloadRecord: { omit, omit, omit, omit, ucs_val }); + timer tmr; + var commMessage_tDecoded redir[5]; + tmr.start(1.0); + alt { + [] pt.receive(commMessage_tPayloadRecord: { decmatch val, *, *, *, * }) -> value (redir[0] := @decoded bs) { + if (redir[0] != val) { setverdict(fail, "Bitstring test - redirect failed: ", redir[0]); } + } + [] pt.receive(commMessage_tPayloadRecord: { bs_val, *, *, *, * }) { + setverdict(fail, "Bitstring test - decmatch failed."); + } + [] pt.receive(commMessage_tPayloadRecord:?) { + setverdict(fail, "Bitstring test - incorrect record received."); + } + [] tmr.timeout { setverdict(fail, "Bitstring test - timeout."); } + } + tmr.stop; + tmr.start(1.0); + alt { + [] pt.receive(commMessage_tPayloadRecord: { *, decmatch val, *, *, * }) -> value (redir[1] := @decoded hs) { + if (redir[1] != val) { setverdict(fail, "Hexstring test - redirect failed: ", redir[1]); } + } + [] pt.receive(commMessage_tPayloadRecord: { *, hs_val, *, *, * }) { + setverdict(fail, "Hexstring test - decmatch failed."); + } + [] pt.receive(commMessage_tPayloadRecord:?) { + setverdict(fail, "Hexstring test - incorrect record received."); + } + [] tmr.timeout { setverdict(fail, "Hexstring test - timeout."); } + } + tmr.stop; + tmr.start(1.0); + alt { + [] pt.receive(commMessage_tPayloadRecord: { *, *, decmatch val, *, * }) -> value (redir[2] := @decoded os) { + if (redir[2] != val) { setverdict(fail, "Octetstring test - redirect failed: ", redir[2]); } + } + [] pt.receive(commMessage_tPayloadRecord: { *, *, os_val, *, * }) { + setverdict(fail, "Octetstring test - decmatch failed."); + } + [] pt.receive(commMessage_tPayloadRecord:?) { + setverdict(fail, "Octetstring test - incorrect record received."); + } + [] tmr.timeout { setverdict(fail, "Octetstring test - timeout."); } + } + tmr.stop; + tmr.start(1.0); + alt { + [] pt.receive(commMessage_tPayloadRecord: { *, *, *, decmatch val, * }) -> value (redir[3] := @decoded cs) { + if (redir[3] != val) { setverdict(fail, "Charstring test - redirect failed: ", redir[3]); } + } + [] pt.receive(commMessage_tPayloadRecord: { *, *, *, cs_val, * }) { + setverdict(fail, "Charstring test - decmatch failed."); + } + [] pt.receive(commMessage_tPayloadRecord:?) { + setverdict(fail, "Charstring test - incorrect record received."); + } + [] tmr.timeout { setverdict(fail, "Charstring test - timeout."); } + } + tmr.stop; + tmr.start(1.0); + alt { + [] pt.receive(commMessage_tPayloadRecord: { *, *, *, *, decmatch("UTF-8") val }) -> value (redir[4] := @decoded("UTF-8") ucs) { + if (redir[4] != val) { setverdict(fail, "Universal charstring test - redirect failed: ", redir[4]); } + } + [] pt.receive(commMessage_tPayloadRecord: { *, *, *, *, ucs_val }) { + setverdict(fail, "Universal charstring test - decmatch failed."); + } + [] pt.receive(commMessage_tPayloadRecord:?) { + setverdict(fail, "Universal charstring test - incorrect record received."); + } + [] tmr.timeout { setverdict(fail, "Universal charstring test - timeout."); } + } + setverdict(pass); +} + control { execute(commMessageIntegerEncode()); execute(commMessageValue()); @@ -1512,5 +1648,7 @@ control { execute(commMessageInterPTCLocalConnection2()); execute(commMessageDualFacedPorts1()); execute(commMessageDualFacedPorts2()); + execute(commMessageMultiValueRedirect()); + execute(commMessageDecodedValueRedirect()); } } diff --git a/regression_test/commProcedure/ProcPort.ttcn b/regression_test/commProcedure/ProcPort.ttcn index 0b11940a8..463975210 100644 --- a/regression_test/commProcedure/ProcPort.ttcn +++ b/regression_test/commProcedure/ProcPort.ttcn @@ -19,6 +19,18 @@ module ProcPort { type record MyRecord { } +type record MyRecord2 { + integer num, + universal charstring str +} +with { + encode "XML"; + variant (num) "attribute"; + variant (str) "untagged"; +} + +type octetstring MyOctetstring with { encode "RAW"; } + signature MyProc(in integer Par1,inout charstring Par2,out float Par3) return boolean exception(integer); @@ -30,6 +42,7 @@ signature MyProc5(in integer A, in integer B) return float exception(charstring, MyRecord); signature s_StopPTC(); signature MyProc6(inout integer I); +signature MyProc7(inout universal charstring x) return MyRecord2 exception(MyRecord2); template s_StopPTC StopPTC := { } @@ -134,6 +147,11 @@ type port ProcPort3 procedure { inout MyProc6; } with { extension "internal" } +type port ProcPort4 procedure { + inout MyProc7; +} +with { extension "internal" } + type component ProcComponent { port ProcPort1 Port0; @@ -167,6 +185,10 @@ type component ProcComponent3 { port ProcPort3 pt; } +type component ProcComponent4 { + port ProcPort4 Port4; +} + function GetCall_behav1() runs on ProcComponent2 { while(true) { alt { @@ -655,6 +677,124 @@ testcase tc_GetReplyParameters() runs on ProcComponent3 { c.done; } +// test for return value and exception redirects with multiple variable references and field assignments +testcase tc_MultiValueRedirect() runs on ProcComponent4 { + connect(mtc:Port4, mtc:Port4); + var MyRecord2 rec_val := { num := 12, str := "xyz" }; + Port4.reply(MyProc7: { "" } value rec_val); + Port4.raise(MyProc7, rec_val); + timer tmr; + var MyRecord2 redir_rec[2]; + var integer redir_int[2]; + var universal charstring redir_str[2]; + tmr.start(1.0); + alt { + [] Port4.getreply(MyProc7: { ? } value rec_val) -> value (redir_rec[0], redir_int[0] := num, redir_str[0] := str) { + if (redir_rec[0] != rec_val) { setverdict(fail, "Invalid record return value: ", redir_rec[0]); } + if (redir_int[0] != rec_val.num) { setverdict(fail, "Invalid integer return value: ", redir_int[0]); } + if (redir_str[0] != rec_val.str) { setverdict(fail, "Invalid charstring return value: ", redir_str[0]); } + } + [] Port4.getreply(MyProc7: { ? } value MyRecord2: ?) { setverdict(fail, "Return value test - invalid return value."); } + [] Port4.catch(MyProc7, MyRecord2: ?) { setverdict(fail, "Return value test - exception caught."); } + [] tmr.timeout { setverdict(fail, "Return value test - timeout"); } + } + tmr.stop; + tmr.start(1.0); + alt { + [] Port4.catch(MyProc7, rec_val) -> value (redir_rec[1], redir_int[1] := num, redir_str[1] := str) { + if (redir_rec[1] != rec_val) { setverdict(fail, "Invalid record exception: ", redir_rec[1]); } + if (redir_int[1] != rec_val.num) { setverdict(fail, "Invalid integer exception: ", redir_int[1]); } + if (redir_str[1] != rec_val.str) { setverdict(fail, "Invalid charstring exception: ", redir_str[1]); } + } + [] Port4.catch(MyProc7, MyRecord2: ?) { setverdict(fail, "Exception test - invalid exception."); } + [] Port4.getreply(MyProc7: { ? } value MyRecord2: ?) { setverdict(fail, "Exception test - reply received."); } + [] tmr.timeout { setverdict(fail, "Exception test - timeout"); } + } + setverdict(pass); +} + +// test for decoded parameter and value redirects +function DecodedRedirect_behav(in MyRecord2 val, in universal charstring reply_val) runs on ProcComponent4 { + timer tmr := 1.0; + var universal charstring val_enc := encvalue_unichar(val); + var MyRecord2 redir; + + // testing parameter redirect in getcall: + tmr.start; + alt { + [] Port4.getcall(MyProc7: { val_enc }) -> param (redir := @decoded x) { + if (redir != val) { setverdict(fail, "Getcall parameter redirect failed: ", redir); } + else { + var MyRecord2 reply_rec := { num := -1, str := reply_val }; + Port4.reply(MyProc7: { reply_val } value reply_rec); + Port4.raise(MyProc7, reply_rec); + } + } + [] Port4.getcall(MyProc7: { ? }) { setverdict(fail, "Invalid getcall parameter."); } + [] tmr.timeout { setverdict(fail, "Getcall timed out."); } + } +} + +testcase tc_DecodedRedirect() runs on ProcComponent4 { + var ProcComponent4 ct := ProcComponent4.create; + connect(ct:Port4, mtc:Port4); + var MyRecord2 val := { num := 4, str := "stuff" }; + // use this string instead of an actual encoded value for reply/getreply and raise/catch, + // because finding a value that can be encoded with UTF-16 or UTF-32 is difficult + // (after this string is decoded into an octetstring, only the octetstring's length is be matched) + var universal charstring reply_val := "payload"; + ct.start(DecodedRedirect_behav(val, reply_val)); + var universal charstring val_enc := encvalue_unichar(val); + var MyOctetstring redir[3]; + // these encoding strings are not known at compile-time (the code generated for them is different) + var charstring str_enc16 := "UTF-16", str_enc32 := "UTF-32LE"; + + // testing parameter and (return) value redirect in getreply: + Port4.call(MyProc7: { val_enc }, 1.0) { + [] Port4.getreply(MyProc7: { decmatch(str_enc16) MyOctetstring: ? length (16) } + value MyRecord2: { num := ?, str := decmatch("UTF-8") MyOctetstring: ? length (7) }) + -> value (redir[0] := @decoded("UTF-8") str) + param (redir[1] := @decoded(str_enc16) x) { + if (redir[0] != unichar2oct(reply_val, "UTF-8")) { + setverdict(fail, "Getreply parameter redirect failed: ", redir[0]); + } + if (redir[1] != unichar2oct(reply_val, str_enc16)) { + setverdict(fail, "Getreply value redirect failed: ", redir[1]); + } + } + [] Port4.getreply(MyProc7: { decmatch(str_enc16) MyOctetstring: ? length (16) } value MyRecord2: ?) { + setverdict(fail, "Invalid getreply return value."); + } + [] Port4.getreply(MyProc7: { ? } value MyRecord2: ?) { + setverdict(fail, "Invalid getreply parameter."); + } + [] Port4.catch(MyProc7, MyRecord2: ?) { + setverdict(fail, "Exception caught in getreply test."); + } + [] Port4.catch(timeout) { setverdict(fail, "Getreply test timed out."); } + } + + // testing value (exception) redirect in catch: + timer tmr := 1.0; + tmr.start; + alt { + [] Port4.catch(MyProc7, MyRecord2: { num := ?, str := decmatch(str_enc32) MyOctetstring: ? length(32) }) + -> value (redir[2] := @decoded(str_enc32) str) { + if (redir[2] != unichar2oct(reply_val, str_enc32)) { + setverdict(fail, "Exception value redirect failed: ", redir[2]); + } + } + [] Port4.catch(MyProc7, MyRecord2: ?) { + setverdict(fail, "Invalid exception caught."); + } + [] Port4.getreply(MyProc7: { ? } value MyRecord2: ?) { + setverdict(fail, "Reply received in exception test."); + } + [] tmr.timeout { setverdict(fail, "Exception test timed out."); } + } + setverdict(pass); +} + control { execute(tc1_Call()); execute(tc2_Call()); @@ -669,5 +809,7 @@ control { execute(tc_PortAddress_external_usage1()); execute(tc_PortAddress_external_usage2()); execute(tc_GetReplyParameters()); + execute(tc_MultiValueRedirect()); + execute(tc_DecodedRedirect()); } } -- GitLab