From 77b0691c9ccb6deec41584cac587a89f15f12c67 Mon Sep 17 00:00:00 2001
From: Botond Baranyi <botond.baranyi@ericsson.com>
Date: Fri, 12 Aug 2016 11:34:32 +0200
Subject: [PATCH] Implemented the @decoded modifier and value redirects with
 field assignments (artf724069)

Change-Id: I55482164cb881a1549e00bdaae3502e5f633e933
Signed-off-by: Botond Baranyi <botond.baranyi@ericsson.com>
---
 compiler2/Type.hh                             |   3 +
 compiler2/Type_chk.cc                         |  13 +-
 compiler2/Type_codegen.cc                     |  29 +-
 compiler2/Value.cc                            |  18 +
 compiler2/Value.hh                            |   4 +
 compiler2/ttcn3/AST_ttcn3.cc                  |   7 +
 compiler2/ttcn3/AST_ttcn3.hh                  |   5 +
 compiler2/ttcn3/Statement.cc                  | 908 ++++++++++++++++--
 compiler2/ttcn3/Statement.hh                  | 148 ++-
 compiler2/ttcn3/Ttcnstuff.cc                  |   2 +
 compiler2/ttcn3/compiler.y                    | 117 ++-
 compiler2/ttcn3/port.c                        |  27 +-
 compiler2/ttcn3/port.h                        |   2 +
 compiler2/ttcn3/signature.c                   |  99 +-
 compiler2/ttcn3/signature.h                   |   3 +
 core/Universal_charstring.cc                  |  56 +-
 core/Universal_charstring.hh                  |   8 +
 regression_test/commMessage/TcommMessage.ttcn | 138 +++
 regression_test/commProcedure/ProcPort.ttcn   | 142 +++
 19 files changed, 1527 insertions(+), 202 deletions(-)

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