diff --git a/compiler2/ttcn3/Statement.cc b/compiler2/ttcn3/Statement.cc index 9d99ad0c76872c89b618cb4bc50bcc59b82c45b0..7f28142f93c4af69d62ddc72b270f6a9af725dbc 100644 --- a/compiler2/ttcn3/Statement.cc +++ b/compiler2/ttcn3/Statement.cc @@ -8540,7 +8540,16 @@ error: // then the parameter redirect class should use the decoding result // from the template instead of decoding the parameter again needs_decode = false; - if (par->get_type()->get_type_refd_last()->get_typetype_ttcn3() == Type::T_USTR) { + Type* decmatch_type = matched_temp->get_decode_target()->get_expr_governor( + Type::EXPECTED_TEMPLATE)->get_type_refd_last(); + if (ve->get_dec_type() != decmatch_type) { + // the decmatch template and this parameter redirect decode two + // different types, so just decode the parameter + needs_decode = true; + use_decmatch_result = false; + } + else if (par->get_type()->get_type_refd_last()->get_typetype_ttcn3() == + Type::T_USTR) { // for universal charstrings the situation could be trickier // compare the string encodings bool different_ustr_encodings = false; @@ -8618,7 +8627,12 @@ error: set_params_str = mputstr(set_params_str, redir_coding_expr.preamble); } set_params_str = mputprintf(set_params_str, - "if (ptr_matched_temp->%s().get_selection() == DECODE_MATCH", + "if (ptr_matched_temp->%s().get_selection() == DECODE_MATCH && " + // the type the parameter was decoded to in the template must be the same + // as the type this parameter redirect would decode the redirected parameter to + // (this is checked by comparing the addresses of the type descriptors) + "&%s_descr_ == ptr_matched_temp->%s().get_decmatch_type_descr()", + par_name, ve->get_dec_type()->get_genname_typedescriptor(scope).c_str(), par_name); if (redir_coding_expr.expr != NULL) { set_params_str = mputprintf(set_params_str, @@ -9171,7 +9185,16 @@ error: // then the value redirect class should use the decoding result // from the template instead of decoding the value again needs_decode = false; - if (redir_type->get_type_refd_last()->get_typetype_ttcn3() == Type::T_USTR) { + Type* decmatch_type = matched_temp->get_decode_target()->get_expr_governor( + Type::EXPECTED_TEMPLATE)->get_type_refd_last(); + if (v[i]->get_dec_type() != decmatch_type) { + // the decmatch template and this value redirect decode two + // different types, so just decode the value + needs_decode = true; + use_decmatch_result = false; + } + else if (redir_type->get_type_refd_last()->get_typetype_ttcn3() == + Type::T_USTR) { // for universal charstrings the situation could be trickier // compare the string encodings bool different_ustr_encodings = false; @@ -9270,7 +9293,14 @@ error: } Free(current_ref); set_values_str = mputprintf(set_values_str, - "(*ptr_matched_temp)%s.get_selection() == DECODE_MATCH", subrefs_str); + "(*ptr_matched_temp)%s.get_selection() == DECODE_MATCH && " + // the type the value was decoded to in the template must be the same + // as the type this value redirect would decode the redirected value to + // (this is checked by comparing the addresses of the type descriptors) + "&%s_descr_ == (*ptr_matched_temp)%s.get_decmatch_type_descr()", + subrefs_str, + v[i]->get_dec_type()->get_genname_typedescriptor(scope).c_str(), + subrefs_str); if (redir_coding_expr.expr != NULL) { set_values_str = mputprintf(set_values_str, " && %s == (*ptr_matched_temp)%s.get_decmatch_str_enc()", diff --git a/compiler2/ttcn3/TtcnTemplate.cc b/compiler2/ttcn3/TtcnTemplate.cc index ec66171a119ede7f4ac2b9ce6157422aa1f8b7fe..c2ed63a683728d6873b5021a61dc291ac130d02e 100644 --- a/compiler2/ttcn3/TtcnTemplate.cc +++ b/compiler2/ttcn3/TtcnTemplate.cc @@ -4582,6 +4582,9 @@ compile_time: // retrieves the decoding result from the last successful matching // (used for optimizing decoded value and parameter redirects) "void* get_dec_res() const { return dec_val; }\n" + // returns a pointer to the type descriptor used in the decoding + // (used for the runtime type check for decoded value and parameter redirects) + "const TTCN_Typedescriptor_t* get_type_descr() const { return &%s_descr_; }\n" "};\n" "%s.set_type(DECODE_MATCH);\n" "{\n", class_tmp_id.c_str(), @@ -4591,7 +4594,8 @@ compile_time: target_type->get_genname_value(my_scope).c_str(), target_type->get_genname_typedescriptor(my_scope).c_str(), target_type->get_coding(false).c_str(), - omit_in_value_list ? ", TRUE" : "", type_name.c_str(), name); + omit_in_value_list ? ", TRUE" : "", type_name.c_str(), + target_type->get_genname_typedescriptor(my_scope).c_str(), name); // generate the decoding target into a temporary string target_tmp_id = my_scope->get_scope_mod_gen()->get_temporary_id(); diff --git a/core/Basetype.hh b/core/Basetype.hh index 06794c0c0062ee756110b864ebe5f359836902c4..1809b33b13a4761a7ad89c7759a4088a1b73625a 100644 --- a/core/Basetype.hh +++ b/core/Basetype.hh @@ -1103,6 +1103,10 @@ public: * the function returns a void pointer (since the decoding could result in a * value of any type), which is converted to the required type when used */ virtual void* get_dec_res() const = 0; + /** this returns the decoded type's descriptor, which may be used by value and + * parameter redirect classes to determine whether the redirected value would + * be decoded into the same type as the type used in this decmatch template */ + virtual const TTCN_Typedescriptor_t* get_type_descr() const = 0; virtual ~Dec_Match_Interface() {} }; diff --git a/core/Bitstring.cc b/core/Bitstring.cc index 94d8382bf103df479259ab489746750de814eba1..3491fa74f4d12ca973dce266fd4bfcc9928014aa 100644 --- a/core/Bitstring.cc +++ b/core/Bitstring.cc @@ -1836,6 +1836,15 @@ void* BITSTRING_template::get_decmatch_dec_res() const return dec_match->instance->get_dec_res(); } +const TTCN_Typedescriptor_t* BITSTRING_template::get_decmatch_type_descr() const +{ + if (template_selection != DECODE_MATCH) { + TTCN_error("Retrieving the decoded type's descriptor in a non-decmatch " + "bitstring template."); + } + return dec_match->instance->get_type_descr(); +} + static const char patterns[] = { '0', '1', '?', '*' }; void BITSTRING_template::log() const diff --git a/core/Bitstring.hh b/core/Bitstring.hh index a8a69dcfe19e023e35a7a83ded5b651828f65edd..95db2ab6a5aa01d46e72d613243347de7d5c7792 100644 --- a/core/Bitstring.hh +++ b/core/Bitstring.hh @@ -301,6 +301,7 @@ public: void set_decmatch(Dec_Match_Interface* new_instance); void* get_decmatch_dec_res() const; + const TTCN_Typedescriptor_t* get_decmatch_type_descr() const; void log() const; void log_match(const BITSTRING& match_value, boolean legacy = FALSE) const; diff --git a/core/Charstring.cc b/core/Charstring.cc index 6123430c3118a81c0b8583ee500519f9dc7bdf2b..286b4f246ece70cf2c92d03ab9eaa0b107d95ec4 100644 --- a/core/Charstring.cc +++ b/core/Charstring.cc @@ -2454,6 +2454,15 @@ void* CHARSTRING_template::get_decmatch_dec_res() const return dec_match->instance->get_dec_res(); } +const TTCN_Typedescriptor_t* CHARSTRING_template::get_decmatch_type_descr() const +{ + if (template_selection != DECODE_MATCH) { + TTCN_error("Retrieving the decoded type's descriptor in a non-decmatch " + "charstring template."); + } + return dec_match->instance->get_type_descr(); +} + void CHARSTRING_template::log_pattern(int n_chars, const char *chars_ptr) { TTCN_Logger::log_event_str("pattern \""); diff --git a/core/Charstring.hh b/core/Charstring.hh index a6bf883f6607c8a454686a427edd5ea2ca3c64b8..a74ae9ea75484d583a788dc1a380b7cec31f7237 100644 --- a/core/Charstring.hh +++ b/core/Charstring.hh @@ -432,6 +432,7 @@ public: void set_decmatch(Dec_Match_Interface* new_instance); void* get_decmatch_dec_res() const; + const TTCN_Typedescriptor_t* get_decmatch_type_descr() const; void log() const; void log_match(const CHARSTRING& match_value, boolean legacy = FALSE) const; diff --git a/core/Hexstring.cc b/core/Hexstring.cc index 07826fe1c27a262747775e0472454eed1102bf9f..6bfa8b05f492b436e4eb66813e909aef62de628d 100644 --- a/core/Hexstring.cc +++ b/core/Hexstring.cc @@ -1722,6 +1722,15 @@ void* HEXSTRING_template::get_decmatch_dec_res() const return dec_match->instance->get_dec_res(); } +const TTCN_Typedescriptor_t* HEXSTRING_template::get_decmatch_type_descr() const +{ + if (template_selection != DECODE_MATCH) { + TTCN_error("Retrieving the decoded type's descriptor in a non-decmatch " + "hexstring template."); + } + return dec_match->instance->get_type_descr(); +} + void HEXSTRING_template::log() const { switch (template_selection) { diff --git a/core/Hexstring.hh b/core/Hexstring.hh index ed620bfa41a34c1283b8f6d25eb168022413f8ba..c8b8816b8e6080829bdc2a574589231861108a71 100644 --- a/core/Hexstring.hh +++ b/core/Hexstring.hh @@ -255,6 +255,7 @@ public: void set_decmatch(Dec_Match_Interface* new_instance); void* get_decmatch_dec_res() const; + const TTCN_Typedescriptor_t* get_decmatch_type_descr() const; void log() const; void log_match(const HEXSTRING& match_value, boolean legacy = FALSE) const; diff --git a/core/Octetstring.cc b/core/Octetstring.cc index 0f63ec381bad8e2f31132ff4a0ff00c944b6f161..fab2f843ce6c51ba3e0318e31127489fd057754a 100644 --- a/core/Octetstring.cc +++ b/core/Octetstring.cc @@ -1958,6 +1958,15 @@ void* OCTETSTRING_template::get_decmatch_dec_res() const return dec_match->instance->get_dec_res(); } +const TTCN_Typedescriptor_t* OCTETSTRING_template::get_decmatch_type_descr() const +{ + if (template_selection != DECODE_MATCH) { + TTCN_error("Retrieving the decoded type's descriptor in a non-decmatch " + "octetstring template."); + } + return dec_match->instance->get_type_descr(); +} + void OCTETSTRING_template::log() const { switch (template_selection) { diff --git a/core/Octetstring.hh b/core/Octetstring.hh index 631993e34329b64ca816edc928fa91957b59f7dd..2988df311ed11665b6cb1149033299be19fcce97 100644 --- a/core/Octetstring.hh +++ b/core/Octetstring.hh @@ -286,6 +286,7 @@ public: void set_decmatch(Dec_Match_Interface* new_instance); void* get_decmatch_dec_res() const; + const TTCN_Typedescriptor_t* get_decmatch_type_descr() const; void log() const; void log_match(const OCTETSTRING& match_value, boolean legacy = FALSE) const; diff --git a/core/Universal_charstring.cc b/core/Universal_charstring.cc index 747b0c3bdbcac4a72ca5f19e3d6a1d59bc9c3c17..c8668206e341921a740566a037d63d247b0e18e8 100644 --- a/core/Universal_charstring.cc +++ b/core/Universal_charstring.cc @@ -4223,6 +4223,15 @@ CharCoding::CharCodingType UNIVERSAL_CHARSTRING_template::get_decmatch_str_enc() return dec_match->coding; } +const TTCN_Typedescriptor_t* UNIVERSAL_CHARSTRING_template::get_decmatch_type_descr() const +{ + if (template_selection != DECODE_MATCH) { + TTCN_error("Retrieving the decoded type's descriptor in a non-decmatch " + "universal charstring template."); + } + return dec_match->instance->get_type_descr(); +} + void UNIVERSAL_CHARSTRING_template::log() const { switch (template_selection) { diff --git a/core/Universal_charstring.hh b/core/Universal_charstring.hh index f771fdeb92185ffc516577e402225ea62085df68..8a48df6b45a64c64cadf4cce4d97ac17e766f46c 100644 --- a/core/Universal_charstring.hh +++ b/core/Universal_charstring.hh @@ -613,6 +613,7 @@ public: void* get_decmatch_dec_res() const; CharCoding::CharCodingType get_decmatch_str_enc() const; + const TTCN_Typedescriptor_t* get_decmatch_type_descr() const; void log() const; void log_match(const UNIVERSAL_CHARSTRING& match_value, boolean legacy = FALSE) const; diff --git a/regression_test/commProcedure/ProcPort.ttcn b/regression_test/commProcedure/ProcPort.ttcn index fb9870309c9e8d7551814673192d45366d3de45d..c15fb44d6d2300a1e03d838bf8967955cfb45520 100644 --- a/regression_test/commProcedure/ProcPort.ttcn +++ b/regression_test/commProcedure/ProcPort.ttcn @@ -33,6 +33,18 @@ type record MyRecord3 { record of MyRecord2 elems } +type record MyRecord4 { + charstring attr, + octetstring val +} +with { + encode "XML"; + variant "name as 'MyRecord2'"; + variant (attr) "attribute"; + variant (attr) "name as 'num'"; + variant (val) "untagged"; +} + type octetstring MyOctetstring with { encode "RAW"; } signature MyProc(in integer Par1,inout charstring Par2,out float Par3) @@ -722,10 +734,99 @@ testcase tc_MultiValueRedirect() runs on ProcComponent4 { } // tests for decoded parameter and value redirects +// (in these tests the received parameters and values are not matched with a 'decmatch' template) +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 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_enc8 := "UTF-8", str_enc16 := "UTF-16", str_enc32 := "UTF-32LE"; + + // testing parameter and (return) value redirect in getreply: + Port4.call(MyProc7: { val_enc }, 1.0) { + [] Port4.getreply(MyProc7: { reply_val } + value MyRecord2: { num := ?, str := reply_val }) + -> 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 (MyRecord2) caught in getreply test."); + } + [] Port4.catch(MyProc7, MyRecord3: ?) { + setverdict(fail, "Exception (MyRecord3) caught in getreply test."); + } + [] Port4.catch(timeout) { setverdict(fail, "Getreply test timed out."); } + } + + // testing (exception) value redirect in catch: + timer tmr := 1.0; + tmr.start; + alt { + [] Port4.catch(MyProc7, MyRecord2: { num := ?, str := reply_val }) + -> 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 value caught."); + } + [] Port4.catch(MyProc7, MyRecord3: ?) { + setverdict(fail, "Invalid type of exception caught."); + } + [] Port4.getreply(MyProc7: { ? } value MyRecord2: ?) { + setverdict(fail, "Reply received in exception test."); + } + [] tmr.timeout { setverdict(fail, "Exception test timed out."); } + } + setverdict(pass); +} + +// additional tests for decoded parameter and value redirects // (in these tests the value and parameter redirects with the '@decoded' modifier // are optimized to reuse the decoding result in the 'decmatch' template instead of // decoding the same string twice) -function DecodedRedirect_behav(in MyRecord2 val, in universal charstring reply_val) runs on ProcComponent4 { +function DecodedRedirect2_behav(in MyRecord2 val, in universal charstring reply_val) runs on ProcComponent4 { timer tmr := 1.0; var MyRecord2 redir; @@ -745,7 +846,7 @@ function DecodedRedirect_behav(in MyRecord2 val, in universal charstring reply_v } } -testcase tc_DecodedRedirect() runs on ProcComponent4 { +testcase tc_DecodedRedirect2() runs on ProcComponent4 { var ProcComponent4 ct := ProcComponent4.create; connect(ct:Port4, mtc:Port4); var MyRecord2 val := { num := 4, str := "stuff" }; @@ -753,7 +854,7 @@ testcase tc_DecodedRedirect() runs on ProcComponent4 { // 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 matched) var universal charstring reply_val := "payload"; - ct.start(DecodedRedirect_behav(val, reply_val)); + ct.start(DecodedRedirect2_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) @@ -811,11 +912,11 @@ testcase tc_DecodedRedirect() runs on ProcComponent4 { setverdict(pass); } -// additional tests for decoded parameter and value redirects +// further tests for decoded parameter and value redirects // (in these tests the 'decmatch' templates cannot be identified at compile-time, // so the decision of whether to decode the redirected value again or use the // decoding result in the matched template is made at runtime) -function DecodedRedirect2_behav(in MyRecord2 val) runs on ProcComponent4 { +function DecodedRedirect3_behav(in MyRecord2 val) runs on ProcComponent4 { timer tmr := 1.0; var MyRecord2 redir; var template MyProc7 vt_proc7 := { x := decmatch MyRecord2: val }; @@ -838,11 +939,11 @@ function DecodedRedirect2_behav(in MyRecord2 val) runs on ProcComponent4 { } } -testcase tc_DecodedRedirect2() runs on ProcComponent4 { +testcase tc_DecodedRedirect3() runs on ProcComponent4 { var ProcComponent4 ct := ProcComponent4.create; connect(ct:Port4, mtc:Port4); var MyRecord2 val := { num := 4, str := "stuff" }; - ct.start(DecodedRedirect2_behav(val)); + ct.start(DecodedRedirect3_behav(val)); var universal charstring val_enc := encvalue_unichar(val); var MyRecord2 redir[3]; var template MyRecord2 vt_rec2 := { num := ?, str := decmatch MyRecord2: val }; @@ -900,6 +1001,95 @@ testcase tc_DecodedRedirect2() runs on ProcComponent4 { setverdict(pass); } +// even more tests for decoded parameter and value redirects +// (in these tests the sent/received strings are decoded into 2 different types: +// the matching 'decmatch' templates decode the strings into MyRecord2, while the +// value and parameter redirects decode the string into MyRecord4) +function DecodedRedirect4_behav(in MyRecord2 val2, in MyRecord4 val4) runs on ProcComponent4 { + timer tmr := 1.0; + var MyRecord4 redir; + + // testing parameter redirect in getcall: + tmr.start; + alt { + [] Port4.getcall(MyProc7: { decmatch MyRecord2: val2 }) -> param (redir := @decoded x) { + if (redir != val4) { + setverdict(fail, "Getcall parameter redirect failed: ", redir); + } + else { + var universal charstring val_enc := encvalue_unichar(val2); + var MyRecord2 reply_val := { num := val2.num, str := val_enc }; + var MyRecord3 raise_val := { elems := { reply_val } }; + Port4.reply(MyProc7: { val_enc } value reply_val); + Port4.raise(MyProc7, raise_val); + } + } + [] Port4.getcall(MyProc7: { ? }) { setverdict(fail, "Invalid getcall parameter."); } + [] tmr.timeout { setverdict(fail, "Getcall timed out."); } + } +} + +testcase tc_DecodedRedirect4() runs on ProcComponent4 { + var ProcComponent4 ct := ProcComponent4.create; + connect(ct:Port4, mtc:Port4); + var MyRecord2 val2 := { num := 4, str := "ADDC" }; + var MyRecord4 val4 := { attr := "4", val := 'ADDC'O }; + ct.start(DecodedRedirect4_behav(val2, val4)); + var universal charstring val_enc := encvalue_unichar(val2); + var MyRecord4 redir[3]; + + // testing parameter and (return) value redirect in getreply: + Port4.call(MyProc7: { val_enc }, 1.0) { + [] Port4.getreply(MyProc7TemplatePard(decmatch MyRecord2: val2) + value MyRecord2: { num := ?, str := decmatch MyRecord2: val2 }) + -> value (redir[0] := @decoded str) + param (redir[1] := @decoded x) { + if (redir[0] != val4) { + setverdict(fail, "Getreply parameter redirect failed: ", redir[0]); + } + if (redir[1] != val4) { + setverdict(fail, "Getreply value redirect failed: ", redir[1]); + } + } + [] Port4.getreply(MyProc7TemplatePard(decmatch MyRecord2: val2) value MyRecord2: ?) { + setverdict(fail, "Invalid getreply return value."); + } + [] Port4.getreply(MyProc7: { ? } value MyRecord2: ?) { + setverdict(fail, "Invalid getreply parameter."); + } + [] Port4.catch(MyProc7, MyRecord2: ?) { + setverdict(fail, "Exception (MyRecord2) caught in getreply test."); + } + [] Port4.catch(MyProc7, MyRecord3: ?) { + setverdict(fail, "Exception (MyRecord3) caught in getreply test."); + } + [] Port4.catch(timeout) { setverdict(fail, "Getreply test timed out."); } + } + + // testing (exception) value redirect in catch: + var template MyRecord3 vt_rec3 := { elems := { { num := ?, str := decmatch MyRecord2: val2 } } }; + timer tmr := 1.0; + tmr.start; + alt { + [] Port4.catch(MyProc7, vt_rec3) -> value (redir[2] := @decoded elems[0].str) { + if (redir[2] != val4) { + setverdict(fail, "Exception value redirect failed: ", redir[2]); + } + } + [] Port4.catch(MyProc7, MyRecord3: ?) { + setverdict(fail, "Invalid exception value caught."); + } + [] Port4.catch(MyProc7, MyRecord2: ?) { + setverdict(fail, "Invalid type of exception caught."); + } + [] Port4.getreply(MyProc7: { ? } value MyRecord2: ?) { + setverdict(fail, "Reply received in exception test."); + } + [] tmr.timeout { setverdict(fail, "Exception test timed out."); } + } + setverdict(pass); +} + control { execute(tc1_Call()); execute(tc2_Call()); @@ -917,5 +1107,7 @@ control { execute(tc_MultiValueRedirect()); execute(tc_DecodedRedirect()); execute(tc_DecodedRedirect2()); + execute(tc_DecodedRedirect3()); + execute(tc_DecodedRedirect4()); } }