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