From 94e477a229b083805d5fd84a46feba0e69727382 Mon Sep 17 00:00:00 2001 From: Botond Baranyi <botond.baranyi@ericsson.com> Date: Wed, 14 Sep 2016 16:21:17 +0200 Subject: [PATCH] optimized redirects with '@decoded' to use decmatch result (artf768988) Change-Id: If2d455116506ff5d625259e618852f22adc72004 Signed-off-by: Botond Baranyi <botond.baranyi@ericsson.com> --- compiler2/Type_chk.cc | 2 +- compiler2/ttcn3/Statement.cc | 740 +++++++++++++++----- compiler2/ttcn3/Statement.hh | 15 +- compiler2/ttcn3/TtcnTemplate.cc | 88 ++- compiler2/ttcn3/TtcnTemplate.hh | 7 +- core/Basetype.hh | 8 +- core/Bitstring.cc | 9 + core/Bitstring.hh | 2 + core/Charstring.cc | 9 + core/Charstring.hh | 2 + core/Hexstring.cc | 9 + core/Hexstring.hh | 2 + core/Octetstring.cc | 9 + core/Octetstring.hh | 2 + core/Universal_charstring.cc | 18 + core/Universal_charstring.hh | 3 + regression_test/commProcedure/ProcPort.ttcn | 128 +++- 17 files changed, 834 insertions(+), 219 deletions(-) diff --git a/compiler2/Type_chk.cc b/compiler2/Type_chk.cc index 41d492225..1e2745345 100644 --- a/compiler2/Type_chk.cc +++ b/compiler2/Type_chk.cc @@ -5756,7 +5756,7 @@ bool Type::chk_this_template_Str(Template *t, namedbool implicit_omit, target->error("Type of template instance cannot be determined"); break; } - if (target->get_Type() != NULL) { + if (target->get_Type() != NULL && target_type->is_ref()) { target_type = target_type->get_type_refd(); } self_ref = target_type->chk_this_template_generic( diff --git a/compiler2/ttcn3/Statement.cc b/compiler2/ttcn3/Statement.cc index 7fe0167de..9d99ad0c7 100644 --- a/compiler2/ttcn3/Statement.cc +++ b/compiler2/ttcn3/Statement.cc @@ -6851,7 +6851,7 @@ error: 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.r.redirect.value->generate_code(expr, port_op.r.rcvpar, 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, ", "); @@ -6874,19 +6874,20 @@ error: port_op.portref->generate_code(expr); expr->expr = mputprintf(expr->expr, ".%s(", opname); if (port_op.r.rcvpar) { + bool has_decoded_redirect = port_op.r.redirect.param != NULL && + port_op.r.redirect.param->has_decoded_modifier(); // the signature template is present - port_op.r.rcvpar->generate_code(expr); + port_op.r.rcvpar->generate_code(expr, TR_NONE, has_decoded_redirect); expr->expr = mputstr(expr->expr, ", "); generate_code_expr_fromclause(expr); // a temporary object is needed for parameter redirect Type *signature = port_op.r.rcvpar->get_Template()->get_my_governor(); - if (port_op.r.redirect.param != NULL && - port_op.r.redirect.param->has_decoded_modifier()) { + if (has_decoded_redirect) { // 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->preamble, port_op.r.rcvpar, 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()); @@ -6896,7 +6897,7 @@ error: signature->get_genname_value(my_sb).c_str()); } if (port_op.r.redirect.param) - port_op.r.redirect.param->generate_code(expr); + port_op.r.redirect.param->generate_code(expr, port_op.r.rcvpar, false); expr->expr = mputstr(expr->expr, "), "); generate_code_expr_senderredirect(expr); } else { @@ -6923,8 +6924,13 @@ error: port_op.portref->generate_code(expr); expr->expr = mputprintf(expr->expr, ".%s(", opname); if (port_op.r.rcvpar) { + bool has_decoded_param_redirect = port_op.r.redirect.param != NULL && + port_op.r.redirect.param->has_decoded_modifier(); + bool has_decoded_value_redirect = port_op.r.redirect.value != NULL && + port_op.r.redirect.value->has_decoded_modifier(); // the signature template is present - port_op.r.rcvpar->generate_code(expr); + port_op.r.rcvpar->generate_code(expr, TR_NONE, + has_decoded_param_redirect || has_decoded_value_redirect); Type *signature = port_op.r.rcvpar->get_Template()->get_my_governor(); Type *return_type = signature->get_type_refd_last()->get_signature_return_type(); @@ -6944,13 +6950,12 @@ error: expr->expr = mputstr(expr->expr, ", "); generate_code_expr_fromclause(expr); // a temporary object is needed for value and parameter redirect - if (port_op.r.redirect.param != NULL && - port_op.r.redirect.param->has_decoded_modifier()) { + if (has_decoded_param_redirect) { // 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->preamble, port_op.r.rcvpar, 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()); @@ -6963,13 +6968,13 @@ error: // the first argument of the constructor must contain // the value redirect if (port_op.r.redirect.value) { - port_op.r.redirect.value->generate_code(expr, + port_op.r.redirect.value->generate_code(expr, port_op.r.getreply_valuematch, 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, ", "); } if (port_op.r.redirect.param) - port_op.r.redirect.param->generate_code(expr); + port_op.r.redirect.param->generate_code(expr, port_op.r.rcvpar, true); expr->expr = mputstr(expr->expr, "), "); generate_code_expr_senderredirect(expr); } else { @@ -7004,11 +7009,13 @@ error: // the signature reference and the exception template is present expr->expr = mputprintf(expr->expr, "%s_exception_template(", port_op.r.ctch.signature->get_genname_value(my_sb).c_str()); - port_op.r.rcvpar->generate_code(expr); + bool has_decoded_redirect = port_op.r.redirect.value != NULL && + port_op.r.redirect.value->has_decoded_modifier(); + port_op.r.rcvpar->generate_code(expr, TR_NONE, has_decoded_redirect); 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.r.redirect.value->generate_code(expr, port_op.r.rcvpar, port_op.r.ctch.signature->get_genname_value(my_sb) + "_exception_template"); } else expr->expr = mputstr(expr->expr, "NULL"); expr->expr = mputstr(expr->expr, "), "); @@ -7063,11 +7070,13 @@ error: expr->expr = mputstr(expr->expr, "done("); comp_op.compref->generate_code_expr(expr); expr->expr = mputstr(expr->expr, ", "); - comp_op.donereturn.donematch->generate_code(expr); + bool has_decoded_redirect = comp_op.donereturn.redirect != NULL && + comp_op.donereturn.redirect->has_decoded_modifier(); + comp_op.donereturn.donematch->generate_code(expr, TR_NONE, has_decoded_redirect); expr->expr = mputstr(expr->expr, ", "); if (comp_op.donereturn.redirect) { // value redirect is present - comp_op.donereturn.redirect->generate_code(expr, + comp_op.donereturn.redirect->generate_code(expr, comp_op.donereturn.donematch, t_mod != my_sb->get_scope_mod_gen() ? t_mod->get_modid().get_name() : string("")); } else { @@ -8263,13 +8272,6 @@ error: } } 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: @@ -8293,6 +8295,13 @@ error: error_flag = true; break; } + // the redirected parameter could be decoded into any type + Type *t_var_type = t_parass->get_ref()->chk_variable_ref(); + if (!error_flag && 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); + } } else { chk_variable_ref(t_parass->get_ref(), t_par->get_type()); @@ -8389,10 +8398,14 @@ error: } } - void ParamRedirect::generate_code(expression_struct_t *expr) + void ParamRedirect::generate_code(expression_struct_t *expr, + TemplateInstance* matched_ti, bool is_out) { // AssignmentList is converted to VariableList during checking if (parredirtype != P_VAR) FATAL_ERROR("ParamRedirect::generate_code()"); + if (has_decoded_modifier()) { + expr->expr = mputprintf(expr->expr, "&(%s), ", matched_ti->get_last_gen_expr()); + } for (size_t i = 0; i < ves->get_nof_ves(); i++) { if (i > 0) expr->expr = mputstr(expr->expr, ", "); VariableEntry* ve = ves->get_ve_byIndex(i); @@ -8420,7 +8433,7 @@ error: } } - char* ParamRedirect::generate_code_decoded(char* str, Type* sig_type, + char* ParamRedirect::generate_code_decoded(char* str, TemplateInstance* matched_ti, const char* tmp_id, bool is_out) { // AssignmentList is converted to VariableList during checking @@ -8444,14 +8457,22 @@ error: char* base_constr_params_str = NULL; char* constr_init_list_str = NULL; char* set_params_str = NULL; + Type* sig_type = matched_ti->get_Template()->get_my_governor()->get_type_refd_last(); 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", + 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"); } + // store a pointer to the matched template, the decoding results from + // decmatch templates might be reused to optimize decoded parameter redirects + members_str = mprintf("%s* ptr_matched_temp;\n", + sig_type->get_genname_template(scope).c_str()); + constr_params_str = mputprintf(constr_params_str, "%s* par_matched_temp", + sig_type->get_genname_template(scope).c_str()); + constr_init_list_str = mcopystr(", ptr_matched_temp(par_matched_temp)"); 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); @@ -8460,6 +8481,8 @@ error: const char* par_name = par->get_id().get_name().c_str(); if (constr_params_str != NULL) { constr_params_str = mputstr(constr_params_str, ", "); + } + if (base_constr_params_str != NULL) { base_constr_params_str = mputstr(base_constr_params_str, ", "); } if (ve->is_decoded()) { @@ -8472,89 +8495,242 @@ error: ", 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"); + NamedTemplate* matched_named_temp = + matched_ti->get_Template()->get_templatetype() == Template::NAMED_TEMPLATE_LIST ? + matched_ti->get_Template()->get_namedtemp_byName(par->get_id()) : NULL; + Template* matched_temp = matched_named_temp != NULL ? + matched_named_temp->get_template()->get_template_refd_last() : NULL; + bool use_decmatch_result = matched_temp != NULL && + matched_temp->get_templatetype() == Template::DECODE_MATCH; + bool needs_decode = true; + expression_struct redir_coding_expr; + Code::init_expr(&redir_coding_expr); + if (par->get_type()->get_type_refd_last()->get_typetype_ttcn3() == Type::T_USTR) { 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); + const char* redir_coding_str; + if (ve->get_str_enc() == NULL || + ve->get_str_enc()->get_val_str() == "UTF-8") { + redir_coding_str = "UTF_8"; } - 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 (ve->get_str_enc()->get_val_str() == "UTF-16" || + ve->get_str_enc()->get_val_str() == "UTF-16BE") { + redir_coding_str = "UTF16BE"; } - 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 if (ve->get_str_enc()->get_val_str() == "UTF-16LE") { + redir_coding_str = "UTF16LE"; } + else if (ve->get_str_enc()->get_val_str() == "UTF-32LE") { + redir_coding_str = "UTF32LE"; + } + else { + redir_coding_str = "UTF32BE"; + } + redir_coding_expr.expr = mprintf("CharCoding::%s", redir_coding_str); } 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", + redir_coding_expr.preamble = mprintf( + "CharCoding::CharCodingType coding = UNIVERSAL_CHARSTRING::" + "get_character_coding(enc_fmt_%s, \"decoded parameter redirect\");\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); + redir_coding_expr.expr = mcopystr("coding"); + } + } + if (use_decmatch_result) { + // if the redirected parameter was matched using a decmatch template, + // then the parameter redirect class should use the decoding result + // from the template instead of decoding the parameter again + needs_decode = false; + if (par->get_type()->get_type_refd_last()->get_typetype_ttcn3() == Type::T_USTR) { + // for universal charstrings the situation could be trickier + // compare the string encodings + bool different_ustr_encodings = false; + bool unknown_ustr_encodings = false; + if (ve->get_str_enc() == NULL) { + if (matched_temp->get_string_encoding() != NULL) { + if (matched_temp->get_string_encoding()->is_unfoldable()) { + unknown_ustr_encodings = true; + } + else if (matched_temp->get_string_encoding()->get_val_str() != "UTF-8") { + different_ustr_encodings = true; + } + } + } + else if (ve->get_str_enc()->is_unfoldable()) { + unknown_ustr_encodings = true; + } + else if (matched_temp->get_string_encoding() == NULL) { + if (ve->get_str_enc()->get_val_str() != "UTF-8") { + different_ustr_encodings = true; + } + } + else if (matched_temp->get_string_encoding()->is_unfoldable()) { + unknown_ustr_encodings = true; + } + else if (ve->get_str_enc()->get_val_str() != + matched_temp->get_string_encoding()->get_val_str()) { + different_ustr_encodings = true; + } + if (unknown_ustr_encodings) { + // the decision of whether to use the decmatch result or to decode + // the value is made at runtime + needs_decode = true; + set_params_str = mputprintf(set_params_str, + "%sif (%s == ptr_matched_temp->%s().get_decmatch_str_enc()) {\n", + redir_coding_expr.preamble != NULL ? redir_coding_expr.preamble : "", + redir_coding_expr.expr, par_name); + } + else if (different_ustr_encodings) { + // if the encodings are different, then ignore the decmatch result + // and just generate the decoding code as usual + needs_decode = true; + use_decmatch_result = false; + } + } + } + else { + // it might still be a decmatch template if it's not known at compile-time + bool unfoldable = matched_temp == NULL; + if (!unfoldable) { + switch (matched_temp->get_templatetype()) { + case Template::ANY_VALUE: + case Template::ANY_OR_OMIT: + case Template::BSTR_PATTERN: + case Template::CSTR_PATTERN: + case Template::HSTR_PATTERN: + case Template::OSTR_PATTERN: + case Template::USTR_PATTERN: + case Template::COMPLEMENTED_LIST: + case Template::VALUE_LIST: + case Template::VALUE_RANGE: + case Template::OMIT_VALUE: + // it's known at compile-time, and not a decmatch template + break; + default: + // needs runtime check + unfoldable = true; + break; + } + } + if (unfoldable) { + // the decmatch-check must be done at runtime + use_decmatch_result = true; + if (redir_coding_expr.preamble != NULL) { + set_params_str = mputstr(set_params_str, redir_coding_expr.preamble); + } 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); + "if (ptr_matched_temp->%s().get_selection() == DECODE_MATCH", + par_name); + if (redir_coding_expr.expr != NULL) { + set_params_str = mputprintf(set_params_str, + " && %s == ptr_matched_temp->%s().get_decmatch_str_enc()", + redir_coding_expr.expr, par_name); + } + set_params_str = mputstr(set_params_str, ") {\n"); } - 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()); + Code::free_expr(&redir_coding_expr); + if (use_decmatch_result) { + set_params_str = mputprintf(set_params_str, + "*ptr_%s_dec = *((%s*)ptr_matched_temp->%s().get_decmatch_dec_res());\n", + par_name, ve->get_dec_type()->get_genname_value(scope).c_str(), par_name); + } + if (needs_decode) { + if (use_decmatch_result) { + set_params_str = mputstr(set_params_str, "}\nelse {\n"); + } + 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); + if (!use_decmatch_result) { + // if the decmatch result code is generated too, then this variable + // was already generated before the main 'if' + set_params_str = mputprintf(set_params_str, + "CharCoding::CharCodingType coding = UNIVERSAL_CHARSTRING::" + "get_character_coding(enc_fmt_%s, \"decoded parameter redirect\");\n", + par_name); + } + set_params_str = mputprintf(set_params_str, + "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); + } + 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", par_name, + ve->get_dec_type()->get_genname_typedescriptor(scope).c_str(), + ve->get_dec_type()->get_coding(false).c_str()); + if (use_decmatch_result) { + set_params_str = mputstr(set_params_str, "}\n"); + } + } + set_params_str = mputstr(set_params_str, "}\n"); } else { constr_params_str = mputprintf(constr_params_str, "%s* par_%s = NULL", @@ -8803,6 +8979,7 @@ error: if (field_type != NULL) { if (v[i]->is_decoded()) { Value* str_enc = v[i]->get_str_enc(); + bool erroneous = false; switch (field_type->get_type_refd_last()->get_typetype_ttcn3()) { case Type::T_BSTR: case Type::T_HSTR: @@ -8811,6 +8988,7 @@ error: if (str_enc != NULL) { str_enc->error("The encoding format parameter for the '@decoded' modifier " "is only available to value redirects of universal charstrings"); + erroneous = true; } break; case Type::T_USTR: @@ -8821,9 +8999,10 @@ error: default: v[i]->error("The '@decoded' modifier is only available to value " "redirects of string types."); + erroneous = true; break; } - if (var_type != NULL) { + if (!erroneous && 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()); @@ -8875,6 +9054,7 @@ error: } void ValueRedirect::generate_code(expression_struct* expr, + TemplateInstance* matched_ti, string base_class_prefix) { // a value redirect class is generated for this redirect in the expression's @@ -8894,6 +9074,16 @@ error: char* constr_params_str = NULL; char* constr_init_list_str = NULL; char* set_values_str = NULL; + if (has_decoded_modifier()) { + // store a pointer to the matched template, the decoding results from + // decmatch templates might be reused to optimize decoded value redirects + expr->expr = mputprintf(expr->expr, "&(%s), ", matched_ti->get_last_gen_expr()); + members_str = mprintf("%s* ptr_matched_temp;\n", + value_type->get_genname_template(scope).c_str()); + constr_params_str = mputprintf(constr_params_str, "%s* par_matched_temp, ", + value_type->get_genname_template(scope).c_str()); + constr_init_list_str = mcopystr("ptr_matched_temp(par_matched_temp), "); + } for (size_t i = 0; i < v.size(); ++i) { if (i > 0) { expr->expr = mputstr(expr->expr, ", "); @@ -8919,12 +9109,13 @@ error: // insert it into the set_values function expression_struct subrefs_expr; Code::init_expr(&subrefs_expr); + const char* opt_suffix = ""; 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, "()"); + opt_suffix = "()"; } } } @@ -8933,93 +9124,268 @@ error: } 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); + // set the usedInIsbound parameter to true so get_refd_sub_template does not + // report errors for missing fields/elements + Template* matched_temp = matched_ti->get_Template()->get_refd_sub_template( + v[i]->get_subrefs(), true, NULL); + if (matched_temp != NULL) { + matched_temp = matched_temp->get_template_refd_last(); + } + bool use_decmatch_result = matched_temp != NULL && + matched_temp->get_templatetype() == Template::DECODE_MATCH; + bool needs_decode = true; + expression_struct redir_coding_expr; + Code::init_expr(&redir_coding_expr); + if (redir_type->get_type_refd_last()->get_typetype_ttcn3() == Type::T_USTR) { 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); + const char* redir_coding_str; + if (v[i]->get_str_enc() == NULL || + v[i]->get_str_enc()->get_val_str() == "UTF-8") { + redir_coding_str = "UTF_8"; } - 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 (v[i]->get_str_enc()->get_val_str() == "UTF-16" || + v[i]->get_str_enc()->get_val_str() == "UTF-16BE") { + redir_coding_str = "UTF16BE"; } - 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 if (v[i]->get_str_enc()->get_val_str() == "UTF-16LE") { + redir_coding_str = "UTF16LE"; + } + else if (v[i]->get_str_enc()->get_val_str() == "UTF-32LE") { + redir_coding_str = "UTF32LE"; } + else { + redir_coding_str = "UTF32BE"; + } + redir_coding_expr.expr = mprintf("CharCoding::%s", redir_coding_str); } 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", + redir_coding_expr.preamble = mprintf( + "CharCoding::CharCodingType coding = UNIVERSAL_CHARSTRING::" + "get_character_coding(enc_fmt_%d, \"decoded parameter redirect\");\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); + redir_coding_expr.expr = mcopystr("coding"); + } + } + if (use_decmatch_result) { + // if the redirected value was matched using a decmatch template, + // then the value redirect class should use the decoding result + // from the template instead of decoding the value again + needs_decode = false; + if (redir_type->get_type_refd_last()->get_typetype_ttcn3() == Type::T_USTR) { + // for universal charstrings the situation could be trickier + // compare the string encodings + bool different_ustr_encodings = false; + bool unknown_ustr_encodings = false; + if (v[i]->get_str_enc() == NULL) { + if (matched_temp->get_string_encoding() != NULL) { + if (matched_temp->get_string_encoding()->is_unfoldable()) { + unknown_ustr_encodings = true; + } + else if (matched_temp->get_string_encoding()->get_val_str() != "UTF-8") { + different_ustr_encodings = true; + } + } + } + else if (v[i]->get_str_enc()->is_unfoldable()) { + unknown_ustr_encodings = true; + } + else if (matched_temp->get_string_encoding() == NULL) { + if (v[i]->get_str_enc()->get_val_str() != "UTF-8") { + different_ustr_encodings = true; + } + } + else if (matched_temp->get_string_encoding()->is_unfoldable()) { + unknown_ustr_encodings = true; + } + else if (v[i]->get_str_enc()->get_val_str() != + matched_temp->get_string_encoding()->get_val_str()) { + different_ustr_encodings = true; + } + if (unknown_ustr_encodings) { + // the decision of whether to use the decmatch result or to decode + // the value is made at runtime + needs_decode = true; + set_values_str = mputprintf(set_values_str, + "%sif (%s == (*ptr_matched_temp)%s.get_decmatch_str_enc()) {\n", + redir_coding_expr.preamble != NULL ? redir_coding_expr.preamble : "", + redir_coding_expr.expr, subrefs_str); + } + else if (different_ustr_encodings) { + // if the encodings are different, then ignore the decmatch result + // and just generate the decoding code as usual + needs_decode = true; + use_decmatch_result = false; + } + } + } + else { + // it might still be a decmatch template if it's not known at compile-time + bool unfoldable = matched_temp == NULL; + if (!unfoldable) { + switch (matched_temp->get_templatetype()) { + case Template::ANY_VALUE: + case Template::ANY_OR_OMIT: + case Template::BSTR_PATTERN: + case Template::CSTR_PATTERN: + case Template::HSTR_PATTERN: + case Template::OSTR_PATTERN: + case Template::USTR_PATTERN: + case Template::COMPLEMENTED_LIST: + case Template::VALUE_LIST: + case Template::VALUE_RANGE: + case Template::OMIT_VALUE: + // it's known at compile-time, and not a decmatch template + break; + default: + // needs runtime check + unfoldable = true; + break; + } + } + if (unfoldable) { + // the decmatch-check must be done at runtime + use_decmatch_result = true; + if (redir_coding_expr.preamble != NULL) { + set_values_str = mputstr(set_values_str, redir_coding_expr.preamble); + } + // before we can check whether the template at the end of the + // subreferences is a decmatch template, we must make sure that + // every prior subreference points to a specific value template + // (otherwise accessing the template at the end will result in a DTE) + set_values_str = mputstr(set_values_str, + "if (ptr_matched_temp->get_selection() == SPECIFIC_VALUE && "); + char* current_ref = mcopystr("(*ptr_matched_temp)"); + size_t len = strlen(subrefs_str); + size_t start = 0; + // go through the already generated subreference string, append + // one reference at a time, and check if the referenced template + // is a specific value + for (size_t j = 1; j < len; ++j) { + if (subrefs_str[j] == '.' || subrefs_str[j] == '[') { + current_ref = mputstrn(current_ref, subrefs_str + start, j - start); + set_values_str = mputprintf(set_values_str, + "%s.get_selection() == SPECIFIC_VALUE && ", current_ref); + start = j; + } + } + Free(current_ref); 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); + "(*ptr_matched_temp)%s.get_selection() == DECODE_MATCH", subrefs_str); + if (redir_coding_expr.expr != NULL) { + set_values_str = mputprintf(set_values_str, + " && %s == (*ptr_matched_temp)%s.get_decmatch_str_enc()", + redir_coding_expr.expr, subrefs_str); + } + set_values_str = mputstr(set_values_str, ") {\n"); + } + } + Code::free_expr(&redir_coding_expr); + if (use_decmatch_result) { + set_values_str = mputprintf(set_values_str, + "*ptr_%d = *((%s*)((*ptr_matched_temp)%s.get_decmatch_dec_res()));\n", + (int)i, type_str.c_str(), subrefs_str); + } + if (needs_decode) { + if (use_decmatch_result) { + set_values_str = mputstr(set_values_str, "}\nelse {\n"); + } + 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%s);\n", (int)i, subrefs_str, opt_suffix); + break; + case Type::T_BSTR: + set_values_str = mputprintf(set_values_str, + "OCTETSTRING os(bit2oct(par%s%s));\n" + "TTCN_Buffer buff_%d(os);\n", subrefs_str, opt_suffix, (int)i); + break; + case Type::T_HSTR: + set_values_str = mputprintf(set_values_str, + "OCTETSTRING os(hex2oct(par%s%s));\n" + "TTCN_Buffer buff_%d(os);\n", subrefs_str, opt_suffix, (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%s.encode_utf8(buff_%d, false);\n", subrefs_str, opt_suffix, (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%s.encode_utf16(buff_%d, CharCoding::UTF16%s);\n", subrefs_str, + opt_suffix, (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%s.encode_utf32(buff_%d, CharCoding::UTF32%s);\n", subrefs_str, + opt_suffix, (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); + if (!use_decmatch_result) { + // if the decmatch result code is generated too, then this variable + // was already generated before the main 'if' + set_values_str = mputprintf(set_values_str, + "CharCoding::CharCodingType coding = UNIVERSAL_CHARSTRING::" + "get_character_coding(enc_fmt_%d, \"decoded value redirect\");\n", + (int)i); + } + set_values_str = mputprintf(set_values_str, + "switch (coding) {\n" + "case CharCoding::UTF_8:\n" + "par%s%s.encode_utf8(buff_%d, false);\n" + "break;\n" + "case CharCoding::UTF16:\n" + "case CharCoding::UTF16LE:\n" + "case CharCoding::UTF16BE:\n" + "par%s%s.encode_utf16(buff_%d, coding);\n" + "break;\n" + "case CharCoding::UTF32:\n" + "case CharCoding::UTF32LE:\n" + "case CharCoding::UTF32BE:\n" + "par%s%s.encode_utf32(buff_%d, coding);\n" + "break;\n" + "default:\n" + "break;\n" + "}\n", subrefs_str, opt_suffix, (int)i, subrefs_str, opt_suffix, + (int)i, subrefs_str, opt_suffix, (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(\"Value 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); + if (use_decmatch_result) { + set_values_str = mputstr(set_values_str, "}\n"); } - 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(\"Value 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 { // if the variable reference and the received value (or its specified field) @@ -9028,17 +9394,17 @@ error: Common::Module* mod = scope->get_scope_mod(); mod->add_type_conv(new TypeConv(redir_type, ref_type, false)); set_values_str = mputprintf(set_values_str, - "if (!%s(*ptr_%d, par%s)) {\n" + "if (!%s(*ptr_%d, par%s%s)) {\n" "TTCN_error(\"Failed to convert redirected value #%d from type `%s' " "to type `%s'.\");\n" "}\n", TypeConv::get_conv_func(redir_type, ref_type, mod).c_str(), (int)i, - subrefs_str, (int)(i + 1), redir_type->get_typename().c_str(), + subrefs_str, opt_suffix, (int)(i + 1), redir_type->get_typename().c_str(), ref_type->get_typename().c_str()); } else { - set_values_str = mputprintf(set_values_str, "*ptr_%d = par%s;\n", - (int)i, subrefs_str); + set_values_str = mputprintf(set_values_str, "*ptr_%d = par%s%s;\n", + (int)i, subrefs_str, opt_suffix); } } if (subrefs_expr.postamble != NULL) { @@ -9077,6 +9443,16 @@ error: Free(constr_init_list_str); Free(set_values_str); } + + bool ValueRedirect::has_decoded_modifier() const + { + for (size_t i = 0; i < v.size(); ++i) { + if (v[i]->is_decoded()) { + return true; + } + } + return false; + } // ================================= // ===== LogArgument diff --git a/compiler2/ttcn3/Statement.hh b/compiler2/ttcn3/Statement.hh index 30d243b45..022050b3a 100644 --- a/compiler2/ttcn3/Statement.hh +++ b/compiler2/ttcn3/Statement.hh @@ -1025,12 +1025,13 @@ namespace Ttcn { /** Sets the code section selector of all embedded values and templates * to \a p_code_section. */ void set_code_section(GovernedSimple::code_section_t p_code_section); - void generate_code(expression_struct_t *expr); + void generate_code(expression_struct_t *expr, TemplateInstance* matched_ti, + bool is_out); /** 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); + char* generate_code_decoded(char* str, TemplateInstance* matched_ti, + 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; @@ -1115,9 +1116,15 @@ namespace Ttcn { /** 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 matched_ti the template instance used for matching the redirected + * value * @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); + void generate_code(expression_struct* expr, TemplateInstance* matched_ti, + string base_class_prefix); + /** returns true if at least one of the value redirects has the + * '@decoded' modifier*/ + bool has_decoded_modifier() const; }; /** diff --git a/compiler2/ttcn3/TtcnTemplate.cc b/compiler2/ttcn3/TtcnTemplate.cc index 49fcf9d14..ec66171a1 100644 --- a/compiler2/ttcn3/TtcnTemplate.cc +++ b/compiler2/ttcn3/TtcnTemplate.cc @@ -1225,6 +1225,15 @@ namespace Ttcn { FATAL_ERROR("Template::get_namedtemp_byIndex()"); return u.named_templates->get_nt_byIndex(n); } + + NamedTemplate* Template::get_namedtemp_byName(const Identifier& name) const + { + if (templatetype != NAMED_TEMPLATE_LIST) { + FATAL_ERROR("Template::get_namedtemp_byName()"); + } + return u.named_templates->has_nt_withName(name) ? + u.named_templates->get_nt_byName(name) : NULL; + } Template *Template::get_all_from() const { @@ -4519,38 +4528,50 @@ compile_time: Type* target_type = u.dec_match.target->get_expr_governor( Type::EXPECTED_TEMPLATE)->get_type_refd_last(); // use the name of the type at the end of the reference chain for logging + string type_name; const char* type_name_ptr = target_type->get_typename_builtin( target_type->get_typetype_ttcn3()); if (type_name_ptr == NULL) { - type_name_ptr = target_type->get_type_refd_last()->get_dispname().c_str(); + type_name = target_type->get_type_refd_last()->get_dispname(); + } + else { + type_name = type_name_ptr; } - // copy the character pointer returned by Type::get_dispname() as it might - // change before its use - char* type_name = mcopystr(type_name_ptr); str = mputprintf(str, "class Dec_Match_%s : public Dec_Match_Interface {\n" - // store the decoding target as a member, since both functions use it + // store the decoding target as a member, since 2 functions use it "%s target;\n" + // store the decoding result from the last successful match() call + "%s* dec_val;\n" "public:\n" - "Dec_Match_%s(%s p_target): target(p_target) { }\n" + "Dec_Match_%s(%s p_target): target(p_target), dec_val(NULL) { }\n" + "~Dec_Match_%s() { if (dec_val != NULL) delete dec_val; }\n" // called when matching, the buffer parameter contains the string to be matched - "virtual boolean match(TTCN_Buffer& buff) const\n" + "virtual boolean match(TTCN_Buffer& buff)\n" "{\n" - "%s val;\n" + "if (dec_val != NULL) delete dec_val;\n" + "dec_val = new %s;\n" // decode the value - "val.decode(%s_descr_, buff, TTCN_EncDec::CT_%s);\n" + "dec_val->decode(%s_descr_, buff, TTCN_EncDec::CT_%s);\n" + "boolean ret_val;\n" // make sure no errors occurred (these already displayed warnings during // decoding) "if (TTCN_EncDec::get_last_error_type() != TTCN_EncDec::ET_NONE) " - "return FALSE;\n" + "ret_val = FALSE;\n" // make sure the buffer is empty after decoding, display a warning otherwise - "if (buff.get_read_len() != 0) {\n" + "else if (buff.get_read_len() != 0) {\n" "TTCN_warning(\"Decoded content matching failed, because the buffer was not " "empty after decoding. Remaining octets: %%d.\", (int)buff.get_read_len());\n" - "return FALSE;\n" + "ret_val = FALSE;\n" "}\n" // finally, match the decoded value against the target template - "return target.match(val%s);\n" + "else ret_val = target.match(*dec_val%s);\n" + // delete the decoded value if matching was not successful + "if (!ret_val) {\n" + "delete dec_val;\n" + "dec_val = NULL;\n" + "}\n" + "return ret_val;\n" "}\n" "virtual void log() const\n" "{\n" @@ -4558,16 +4579,19 @@ compile_time: "TTCN_Logger::log_event_str(\"%s: \");\n" "target.log();\n" "}\n" + // retrieves the decoding result from the last successful matching + // (used for optimizing decoded value and parameter redirects) + "void* get_dec_res() const { return dec_val; }\n" "};\n" "%s.set_type(DECODE_MATCH);\n" "{\n", class_tmp_id.c_str(), - target_type->get_genname_template(my_scope).c_str(), class_tmp_id.c_str(), target_type->get_genname_template(my_scope).c_str(), + target_type->get_genname_value(my_scope).c_str(), class_tmp_id.c_str(), + target_type->get_genname_template(my_scope).c_str(), class_tmp_id.c_str(), target_type->get_genname_value(my_scope).c_str(), target_type->get_genname_typedescriptor(my_scope).c_str(), target_type->get_coding(false).c_str(), - omit_in_value_list ? ", TRUE" : "", type_name, name); - Free(type_name); + omit_in_value_list ? ", TRUE" : "", type_name.c_str(), name); // generate the decoding target into a temporary string target_tmp_id = my_scope->get_scope_mod_gen()->get_temporary_id(); @@ -4988,7 +5012,7 @@ compile_time: TemplateInstance::TemplateInstance(Type *p_type, Ref_base *p_ref, Template *p_body) : Node(), Location(), type(p_type), - derived_reference(p_ref), template_body(p_body) + derived_reference(p_ref), template_body(p_body), last_gen_expr(NULL) { if (!p_body) FATAL_ERROR("TemplateInstance::TemplateInstance()"); if (type) type->set_ownertype(Type::OT_TEMPLATE_INST, this); @@ -4999,6 +5023,7 @@ compile_time: delete type; delete derived_reference; delete template_body; + Free(last_gen_expr); } void TemplateInstance::release() @@ -5252,8 +5277,9 @@ compile_time: } void TemplateInstance::generate_code(expression_struct *expr, - template_restriction_t template_restriction) + template_restriction_t template_restriction, bool has_decoded_redirect) { + size_t start_pos = mstrlen(expr->expr); if (derived_reference) { // preserve the target expression char *expr_backup = expr->expr; @@ -5280,7 +5306,31 @@ compile_time: // restore the target expression append the name of the temporary // variable to it expr->expr = mputstr(expr_backup, tmp_id_str); - } else template_body->generate_code_expr(expr, template_restriction); + } else { + char *expr_backup; + if (has_decoded_redirect) { + // preserve the target expression + expr_backup = expr->expr; + // reset the space for the target expression + expr->expr = NULL; + } + template_body->generate_code_expr(expr, template_restriction); + if (has_decoded_redirect) { + // create a temporary variable and move the template's initialization code + // after it + const string& tmp_id = template_body->get_temporary_id(); + const char *tmp_id_str = tmp_id.c_str(); + expr->preamble = mputprintf(expr->preamble, "%s %s(%s);\n", + template_body->get_my_governor()->get_genname_template( + template_body->get_my_scope()).c_str(), tmp_id_str, expr->expr); + Free(expr->expr); + // restore the target expression and append the name of the temporary + // variable to it + expr->expr = mputstr(expr_backup, tmp_id_str); + } + } + size_t end_pos = mstrlen(expr->expr); + last_gen_expr = mcopystrn(expr->expr + start_pos, end_pos - start_pos); } char *TemplateInstance::rearrange_init_code(char *str, Common::Module* usage_mod) diff --git a/compiler2/ttcn3/TtcnTemplate.hh b/compiler2/ttcn3/TtcnTemplate.hh index 69b24efde..7a6864591 100644 --- a/compiler2/ttcn3/TtcnTemplate.hh +++ b/compiler2/ttcn3/TtcnTemplate.hh @@ -285,6 +285,7 @@ namespace Ttcn { size_t get_nof_comps() const; Template *get_temp_byIndex(size_t n) const; NamedTemplate *get_namedtemp_byIndex(size_t n) const; + NamedTemplate* get_namedtemp_byName(const Identifier& name) const; IndexedTemplate *get_indexedtemp_byIndex(size_t n) const; Template *get_all_from() const; /** Returns the number of elements in a VALUE_LIST. The elements of @@ -517,6 +518,8 @@ namespace Ttcn { Type *type; // type before the colon, may be null Ref_base *derived_reference; // base template, may be null Template *template_body; // must not be null + char* last_gen_expr; // last expression generated from this template instance + // (used if this template needs to be used multiple times) /** Copy constructor disabled. */ TemplateInstance(const TemplateInstance& p); @@ -539,6 +542,7 @@ namespace Ttcn { Type* get_Type() const { return type; } Ref_base* get_DerivedRef() const { return derived_reference; } Template* get_Template() const { return template_body; } + char* get_last_gen_expr() const { return last_gen_expr; } // it can return null pointer Def_Template* get_Referenced_Base_Template(); @@ -577,7 +581,8 @@ namespace Ttcn { void set_code_section(GovernedSimple::code_section_t p_code_section); bool needs_temp_ref(); void generate_code(expression_struct *expr, - template_restriction_t template_restriction = TR_NONE); + template_restriction_t template_restriction = TR_NONE, + bool has_decoded_redirect = false); /** Appends the initialization sequence of the referred templates * and their default values to \a str. Only templates from module * \a usage_mod are considered. */ diff --git a/core/Basetype.hh b/core/Basetype.hh index a0e7eb3bb..06794c0c0 100644 --- a/core/Basetype.hh +++ b/core/Basetype.hh @@ -1095,8 +1095,14 @@ public: * functions when the template object's match() or log() functions are called. */ class Dec_Match_Interface { public: - virtual boolean match(TTCN_Buffer&) const = 0; + virtual boolean match(TTCN_Buffer&) = 0; virtual void log() const = 0; + /** this returns the decoding result of the last successfully matched value, + * which may be used by value and parameter redirect classes for optimization + * (so they don't have to decode the same value again) + * the function returns a void pointer (since the decoding could result in a + * value of any type), which is converted to the required type when used */ + virtual void* get_dec_res() const = 0; virtual ~Dec_Match_Interface() {} }; diff --git a/core/Bitstring.cc b/core/Bitstring.cc index 56d4905c8..94d8382bf 100644 --- a/core/Bitstring.cc +++ b/core/Bitstring.cc @@ -1827,6 +1827,15 @@ void BITSTRING_template::set_decmatch(Dec_Match_Interface* new_instance) dec_match->instance = new_instance; } +void* BITSTRING_template::get_decmatch_dec_res() const +{ + if (template_selection != DECODE_MATCH) { + TTCN_error("Retrieving the decoding result of a non-decmatch bitstring " + "template."); + } + return dec_match->instance->get_dec_res(); +} + static const char patterns[] = { '0', '1', '?', '*' }; void BITSTRING_template::log() const diff --git a/core/Bitstring.hh b/core/Bitstring.hh index 877a56495..a8a69dcfe 100644 --- a/core/Bitstring.hh +++ b/core/Bitstring.hh @@ -299,6 +299,8 @@ public: BITSTRING_template& list_item(unsigned int list_index); void set_decmatch(Dec_Match_Interface* new_instance); + + void* get_decmatch_dec_res() const; void log() const; void log_match(const BITSTRING& match_value, boolean legacy = FALSE) const; diff --git a/core/Charstring.cc b/core/Charstring.cc index c3123705a..6123430c3 100644 --- a/core/Charstring.cc +++ b/core/Charstring.cc @@ -2445,6 +2445,15 @@ void CHARSTRING_template::set_decmatch(Dec_Match_Interface* new_instance) dec_match->coding = CharCoding::UTF_8; } +void* CHARSTRING_template::get_decmatch_dec_res() const +{ + if (template_selection != DECODE_MATCH) { + TTCN_error("Retrieving the decoding result of a non-decmatch charstring " + "template."); + } + return dec_match->instance->get_dec_res(); +} + void CHARSTRING_template::log_pattern(int n_chars, const char *chars_ptr) { TTCN_Logger::log_event_str("pattern \""); diff --git a/core/Charstring.hh b/core/Charstring.hh index 8b41a9702..a6bf883f6 100644 --- a/core/Charstring.hh +++ b/core/Charstring.hh @@ -430,6 +430,8 @@ public: void set_max(const CHARSTRING& max_value); void set_decmatch(Dec_Match_Interface* new_instance); + + void* get_decmatch_dec_res() const; void log() const; void log_match(const CHARSTRING& match_value, boolean legacy = FALSE) const; diff --git a/core/Hexstring.cc b/core/Hexstring.cc index 968a6e140..07826fe1c 100644 --- a/core/Hexstring.cc +++ b/core/Hexstring.cc @@ -1713,6 +1713,15 @@ void HEXSTRING_template::set_decmatch(Dec_Match_Interface* new_instance) dec_match->instance = new_instance; } +void* HEXSTRING_template::get_decmatch_dec_res() const +{ + if (template_selection != DECODE_MATCH) { + TTCN_error("Retrieving the decoding result of a non-decmatch hexstring " + "template."); + } + return dec_match->instance->get_dec_res(); +} + void HEXSTRING_template::log() const { switch (template_selection) { diff --git a/core/Hexstring.hh b/core/Hexstring.hh index 0792afa42..ed620bfa4 100644 --- a/core/Hexstring.hh +++ b/core/Hexstring.hh @@ -253,6 +253,8 @@ public: HEXSTRING_template& list_item(unsigned int list_index); void set_decmatch(Dec_Match_Interface* new_instance); + + void* get_decmatch_dec_res() const; void log() const; void log_match(const HEXSTRING& match_value, boolean legacy = FALSE) const; diff --git a/core/Octetstring.cc b/core/Octetstring.cc index 7d9dd6eac..0f63ec381 100644 --- a/core/Octetstring.cc +++ b/core/Octetstring.cc @@ -1949,6 +1949,15 @@ void OCTETSTRING_template::set_decmatch(Dec_Match_Interface* new_instance) dec_match->instance = new_instance; } +void* OCTETSTRING_template::get_decmatch_dec_res() const +{ + if (template_selection != DECODE_MATCH) { + TTCN_error("Retrieving the decoding result of a non-decmatch octetstring " + "template."); + } + return dec_match->instance->get_dec_res(); +} + void OCTETSTRING_template::log() const { switch (template_selection) { diff --git a/core/Octetstring.hh b/core/Octetstring.hh index 7e33563ff..631993e34 100644 --- a/core/Octetstring.hh +++ b/core/Octetstring.hh @@ -284,6 +284,8 @@ public: OCTETSTRING_template& list_item(unsigned int list_index); void set_decmatch(Dec_Match_Interface* new_instance); + + void* get_decmatch_dec_res() const; void log() const; void log_match(const OCTETSTRING& match_value, boolean legacy = FALSE) const; diff --git a/core/Universal_charstring.cc b/core/Universal_charstring.cc index 1078084c8..747b0c3bd 100644 --- a/core/Universal_charstring.cc +++ b/core/Universal_charstring.cc @@ -4205,6 +4205,24 @@ void UNIVERSAL_CHARSTRING_template::set_decmatch(Dec_Match_Interface* new_instan dec_match->coding = new_coding; } +void* UNIVERSAL_CHARSTRING_template::get_decmatch_dec_res() const +{ + if (template_selection != DECODE_MATCH) { + TTCN_error("Retrieving the decoding result of a non-decmatch universal " + "charstring template."); + } + return dec_match->instance->get_dec_res(); +} + +CharCoding::CharCodingType UNIVERSAL_CHARSTRING_template::get_decmatch_str_enc() const +{ + if (template_selection != DECODE_MATCH) { + TTCN_error("Retrieving the encoding format of a non-decmatch universal " + "charstring template."); + } + return dec_match->coding; +} + void UNIVERSAL_CHARSTRING_template::log() const { switch (template_selection) { diff --git a/core/Universal_charstring.hh b/core/Universal_charstring.hh index eb5f8a9db..f771fdeb9 100644 --- a/core/Universal_charstring.hh +++ b/core/Universal_charstring.hh @@ -610,6 +610,9 @@ public: void set_max(const UNIVERSAL_CHARSTRING& max_value); void set_decmatch(Dec_Match_Interface* new_instance, const char* coding_str = NULL); + + void* get_decmatch_dec_res() const; + CharCoding::CharCodingType get_decmatch_str_enc() const; void log() const; void log_match(const UNIVERSAL_CHARSTRING& match_value, boolean legacy = FALSE) const; diff --git a/regression_test/commProcedure/ProcPort.ttcn b/regression_test/commProcedure/ProcPort.ttcn index 463975210..fb9870309 100644 --- a/regression_test/commProcedure/ProcPort.ttcn +++ b/regression_test/commProcedure/ProcPort.ttcn @@ -29,6 +29,10 @@ with { variant (str) "untagged"; } +type record MyRecord3 { + record of MyRecord2 elems +} + type octetstring MyOctetstring with { encode "RAW"; } signature MyProc(in integer Par1,inout charstring Par2,out float Par3) @@ -42,7 +46,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); +signature MyProc7(inout universal charstring x) return MyRecord2 exception(MyRecord2, MyRecord3); template s_StopPTC StopPTC := { } @@ -102,6 +106,10 @@ template address MyAddress(integer p1,integer p2) := { a2:=p2 } +template MyProc7 MyProc7TemplatePard(template universal charstring p) := { + x := p +} + type port PortAddress procedure { inout MyProc,s_StopPTC; in MyProc2; @@ -713,16 +721,18 @@ testcase tc_MultiValueRedirect() runs on ProcComponent4 { setverdict(pass); } -// test for decoded parameter and value redirects +// tests for decoded parameter and value redirects +// (in these tests the value and parameter redirects with the '@decoded' modifier +// are optimized to reuse the decoding result in the 'decmatch' template instead of +// decoding the same string twice) 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) { + [] Port4.getcall(MyProc7: { decmatch("UTF-8") MyRecord2: val }) -> param (redir := @decoded x) { if (redir != val) { setverdict(fail, "Getcall parameter redirect failed: ", redir); } else { var MyRecord2 reply_rec := { num := -1, str := reply_val }; @@ -741,18 +751,18 @@ testcase tc_DecodedRedirect() runs on ProcComponent4 { 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) + // (after this string is decoded into an octetstring, only the octetstring's length is 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"; + var charstring str_enc8 := "UTF-8", 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 MyRecord2: { num := ?, str := decmatch(str_enc8) 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")) { @@ -769,23 +779,118 @@ testcase tc_DecodedRedirect() runs on ProcComponent4 { setverdict(fail, "Invalid getreply parameter."); } [] Port4.catch(MyProc7, MyRecord2: ?) { - setverdict(fail, "Exception caught in getreply test."); + setverdict(fail, "Exception (MyRecord2) caught in getreply test."); + } + [] Port4.catch(MyProc7, MyRecord3: ?) { + setverdict(fail, "Exception (MyRecord3) caught in getreply test."); } [] Port4.catch(timeout) { setverdict(fail, "Getreply test timed out."); } } - // testing value (exception) redirect in catch: + // testing (exception) value redirect in catch: timer tmr := 1.0; tmr.start; alt { - [] Port4.catch(MyProc7, MyRecord2: { num := ?, str := decmatch(str_enc32) MyOctetstring: ? length(32) }) + [] Port4.catch(MyProc7, MyRecord2: { num := ?, str := decmatch("UTF-32LE") 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."); + setverdict(fail, "Invalid exception value caught."); + } + [] Port4.catch(MyProc7, MyRecord3: ?) { + setverdict(fail, "Invalid type of exception caught."); + } + [] Port4.getreply(MyProc7: { ? } value MyRecord2: ?) { + setverdict(fail, "Reply received in exception test."); + } + [] tmr.timeout { setverdict(fail, "Exception test timed out."); } + } + setverdict(pass); +} + +// additional tests for decoded parameter and value redirects +// (in these tests the 'decmatch' templates cannot be identified at compile-time, +// so the decision of whether to decode the redirected value again or use the +// decoding result in the matched template is made at runtime) +function DecodedRedirect2_behav(in MyRecord2 val) runs on ProcComponent4 { + timer tmr := 1.0; + var MyRecord2 redir; + var template MyProc7 vt_proc7 := { x := decmatch MyRecord2: val }; + + // testing parameter redirect in getcall: + tmr.start; + alt { + [] Port4.getcall(vt_proc7) -> param (redir := @decoded x) { + if (redir != val) { setverdict(fail, "Getcall parameter redirect failed: ", redir); } + else { + var universal charstring val_enc := encvalue_unichar(val); + var MyRecord2 reply_val := { num := val.num, str := val_enc }; + var MyRecord3 raise_val := { elems := { reply_val } }; + Port4.reply(MyProc7: { val_enc } value reply_val); + Port4.raise(MyProc7, raise_val); + } + } + [] Port4.getcall(MyProc7: { ? }) { setverdict(fail, "Invalid getcall parameter."); } + [] tmr.timeout { setverdict(fail, "Getcall timed out."); } + } +} + +testcase tc_DecodedRedirect2() runs on ProcComponent4 { + var ProcComponent4 ct := ProcComponent4.create; + connect(ct:Port4, mtc:Port4); + var MyRecord2 val := { num := 4, str := "stuff" }; + ct.start(DecodedRedirect2_behav(val)); + var universal charstring val_enc := encvalue_unichar(val); + var MyRecord2 redir[3]; + var template MyRecord2 vt_rec2 := { num := ?, str := decmatch MyRecord2: val }; + var charstring str_enc8 := "UTF-8"; + + // testing parameter and (return) value redirect in getreply: + Port4.call(MyProc7: { val_enc }, 1.0) { + [] Port4.getreply(MyProc7TemplatePard(decmatch MyRecord2: val) + value modifies vt_rec2 := { num := val.num }) + -> value (redir[0] := @decoded str) + param (redir[1] := @decoded(str_enc8) x) { + if (redir[0] != val) { + setverdict(fail, "Getreply parameter redirect failed: ", redir[0]); + } + if (redir[1] != val) { + setverdict(fail, "Getreply value redirect failed: ", redir[1]); + } + } + [] Port4.getreply(MyProc7TemplatePard(decmatch MyRecord2: val) 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 (MyRecord2) caught in getreply test."); + } + [] Port4.catch(MyProc7, MyRecord3: ?) { + setverdict(fail, "Exception (MyRecord3) caught in getreply test."); + } + [] Port4.catch(timeout) { setverdict(fail, "Getreply test timed out."); } + } + + // testing (exception) value redirect in catch: + timer tmr := 1.0; + tmr.start; + alt { + [] Port4.catch(MyProc7, MyRecord3: { elems := { vt_rec2 } }) + -> value (redir[2] := @decoded(str_enc8) elems[0].str) { + if (redir[2] != val) { + setverdict(fail, "Exception value redirect failed: ", redir[2]); + } + } + [] Port4.catch(MyProc7, MyRecord3: ?) { + setverdict(fail, "Invalid exception value caught."); + } + [] Port4.catch(MyProc7, MyRecord2: ?) { + setverdict(fail, "Invalid type of exception caught."); } [] Port4.getreply(MyProc7: { ? } value MyRecord2: ?) { setverdict(fail, "Reply received in exception test."); @@ -811,5 +916,6 @@ control { execute(tc_GetReplyParameters()); execute(tc_MultiValueRedirect()); execute(tc_DecodedRedirect()); + execute(tc_DecodedRedirect2()); } } -- GitLab