diff --git a/compiler2/Type.cc b/compiler2/Type.cc index 9cd01254a7ca94021de2df24b40c42b4c253a84e..0bdc941d399c2403f42fdcb429dac7800e6961f4 100644 --- a/compiler2/Type.cc +++ b/compiler2/Type.cc @@ -604,6 +604,8 @@ namespace Common { owner = 0; chk_finished = false; pard_type_instance = false; + asn_encoding = CT_UNDEF; + asn_decoding = CT_UNDEF; } void Type::clean_up() @@ -4402,98 +4404,134 @@ namespace Common { coding_str = function_name; coding_by_function = true; } + + void Type::set_asn_coding(bool encode, Type::MessageEncodingType_t new_coding) + { + MessageEncodingType_t& coding = encode ? asn_encoding : asn_decoding; + if (coding == CT_UNDEF) { + // this is the first encoding/decoding function for this type, store it + coding = new_coding; + } + else if (coding != new_coding) { + // there are several encoding/decoding functions declared for this type + // with different codings (encvalue/decvalue cannot be used in this case) + coding = CT_MULTIPLE; + } + } void Type::chk_coding(bool encode, bool delayed /* = false */) { string& coding_str = encode ? encoding_str : decoding_str; if (!coding_str.empty()) return; coding_by_function = false; - - if (!w_attrib_path) { - error("No coding rule specified for type '%s'", get_typename().c_str()); - return; - } Type::MessageEncodingType_t coding = CT_UNDEF; - // Checking extension attributes - Ttcn::ExtensionAttributes * extatrs = parse_extattributes(w_attrib_path); - if (extatrs != 0) { // NULL means parsing error - for (size_t k = 0; k < extatrs->size(); ++k) { - Ttcn::ExtensionAttribute &ea = extatrs->get(k); - Ttcn::TypeMappings *inmaps = 0, *maps = 0; - Ttcn::TypeMapping* mapping = 0; - Ttcn::TypeMappingTarget* target = 0; - Type* t = 0; - switch (ea.get_type()) { - case Ttcn::ExtensionAttribute::ENCDECVALUE: - ea.get_encdecvalue_mappings(inmaps, maps); - maps = encode ? maps : inmaps; - maps->set_my_scope(this->get_my_scope()); - maps->chk(); - // look for coding settings - t = encode ? this : Type::get_pooltype(T_BSTR); - mapping = maps->get_mapping_byType(t); - if (mapping->get_nof_targets() == 0) - goto end_ext; - else { - for (size_t ind = 0; ind < mapping->get_nof_targets(); ind++) { - target = mapping->get_target_byIndex(ind); - t = target->get_target_type(); - if ((encode && (t->get_typetype() == T_BSTR)) || - (!encode && (t->get_typename() == this->get_typename()))) - { - if (target->get_mapping_type() == - Ttcn::TypeMappingTarget::TM_FUNCTION) { - if (!coding_str.empty()) - target->error("Multiple definition of this target"); - coding_str = target->get_function()-> - get_genname_from_scope(my_scope); - coding_by_function = true; - } else { - target->error("Only function is supported to do this mapping"); + if (!is_asn1()) { + if (!w_attrib_path) { + error("No coding rule specified for type '%s'", get_typename().c_str()); + return; + } + + // Checking extension attributes + Ttcn::ExtensionAttributes * extatrs = parse_extattributes(w_attrib_path); + if (extatrs != 0) { // NULL means parsing error + for (size_t k = 0; k < extatrs->size(); ++k) { + Ttcn::ExtensionAttribute &ea = extatrs->get(k); + Ttcn::TypeMappings *inmaps = 0, *maps = 0; + Ttcn::TypeMapping* mapping = 0; + Ttcn::TypeMappingTarget* target = 0; + Type* t = 0; + switch (ea.get_type()) { + case Ttcn::ExtensionAttribute::ENCDECVALUE: + ea.get_encdecvalue_mappings(inmaps, maps); + maps = encode ? maps : inmaps; + maps->set_my_scope(this->get_my_scope()); + maps->chk(); + // look for coding settings + t = encode ? this : Type::get_pooltype(T_BSTR); + mapping = maps->get_mapping_byType(t); + if (mapping->get_nof_targets() == 0) + goto end_ext; + else { + for (size_t ind = 0; ind < mapping->get_nof_targets(); ind++) { + target = mapping->get_target_byIndex(ind); + t = target->get_target_type(); + if ((encode && (t->get_typetype() == T_BSTR)) || + (!encode && (t->get_typename() == this->get_typename()))) + { + if (target->get_mapping_type() == + Ttcn::TypeMappingTarget::TM_FUNCTION) { + if (!coding_str.empty()) + target->error("Multiple definition of this target"); + coding_str = target->get_function()-> + get_genname_from_scope(my_scope); + coding_by_function = true; + } else { + target->error("Only function is supported to do this mapping"); + } } } + if (coding_str.empty()) { + ea.warning("Extension attribute is found for %s but without " + "typemappings", encode ? "encvalue" : "decvalue"); + } } - if (coding_str.empty()) { - ea.warning("Extension attribute is found for %s but without " - "typemappings", encode ? "encvalue" : "decvalue"); - } - } - break; + break; - case Ttcn::ExtensionAttribute::ANYTYPELIST: - break; // ignore (may be inherited from the module) + case Ttcn::ExtensionAttribute::ANYTYPELIST: + break; // ignore (may be inherited from the module) - case Ttcn::ExtensionAttribute::NONE: - break; // ignore erroneous attribute + case Ttcn::ExtensionAttribute::NONE: + break; // ignore erroneous attribute - default: - ea.error("A type can only have type mapping extension attribute: " - "in(...) or out(...)"); - break; + default: + ea.error("A type can only have type mapping extension attribute: " + "in(...) or out(...)"); + break; + } } + delete extatrs; } - delete extatrs; - } - if (!coding_str.empty()) - return; -end_ext: - - const vector<SingleWithAttrib>& real_attribs - = w_attrib_path->get_real_attrib(); - bool found = false; - for (size_t i = real_attribs.size(); i > 0 && !found; i--) { - if (real_attribs[i-1]->get_attribKeyword() - == SingleWithAttrib::AT_ENCODE) { - found = true; - coding = get_enc_type(*real_attribs[i-1]); + if (!coding_str.empty()) + return; + end_ext: + + const vector<SingleWithAttrib>& real_attribs + = w_attrib_path->get_real_attrib(); + bool found = false; + for (size_t i = real_attribs.size(); i > 0 && !found; i--) { + if (real_attribs[i-1]->get_attribKeyword() + == SingleWithAttrib::AT_ENCODE) { + found = true; + coding = get_enc_type(*real_attribs[i-1]); + } + } + if (coding == CT_UNDEF) { + // no "encode" attribute found + error("No coding rule specified for type '%s'", get_typename().c_str()); + return; } } - if (coding == CT_UNDEF) { - // no "encode" attribute found - error("No coding rule specified for type '%s'", get_typename().c_str()); - return; + else { // ASN.1 type + coding = encode ? asn_encoding : asn_decoding; + if ((coding == CT_UNDEF && delayed) || coding == CT_MULTIPLE) { + // either this is the delayed call and no external function has been + // found, or there was already more than one function + error("Cannot determine the %s rules for ASN.1 type `%s'. " + "%s %s external function%s found%s", encode ? "encoding" : "decoding", + get_typename().c_str(), coding == CT_UNDEF ? "No" : "Multiple", + encode ? "encoding" : "decoding", coding == CT_UNDEF ? "" : "s", + coding == CT_UNDEF ? "" : " with different rules"); + return; + } + if (coding == CT_UNDEF && !delayed) { + // the coding type is set by the external function's checker in this case; + // it's possible, that the function exists, but has not been reached yet; + // delay this function until everything else has been checked + Modules::delay_type_encode_check(this, encode); + return; + } } if (coding != CT_CUSTOM && !has_encoding(coding)) { error("Type '%s' cannot be coded with the selected method '%s'", diff --git a/compiler2/Type.hh b/compiler2/Type.hh index a23a222092e3e4e2bf3c06ef6f6e246b01bab31e..a210100a704fdc1729846322d9b722a0086209c6 100644 --- a/compiler2/Type.hh +++ b/compiler2/Type.hh @@ -201,7 +201,8 @@ namespace Common { CT_TEXT, /**< TTCN-3 TEXT */ CT_XER, /**< ASN.1 XER */ CT_JSON, /**< TTCN-3 JSON */ - CT_CUSTOM /**< user defined encoding */ + CT_CUSTOM, /**< user defined encoding */ + CT_MULTIPLE /**< multiple codings defined for an ASN.1 type */ }; /** selector for value checking algorithms */ @@ -307,6 +308,8 @@ namespace Common { string encoding_str; // needed by codegen for encvalue() and decvalue() string decoding_str; bool coding_by_function; // false - coding attribute is set, true - coding via coding function + MessageEncodingType_t asn_encoding; // set by the semantic analysis of encoding + MessageEncodingType_t asn_decoding; // and decoding external functions for ASN.1 types /** What kind of AST element owns the type. * It may not be known at creation type, so it's initially OT_UNKNOWN. * We want this information so we don't have to bother with XER @@ -649,6 +652,7 @@ namespace Common { /** Sets the encoding or decoding function for the type (in case of custom * encoding). */ void set_coding_function(bool encode, const string& function_name); + void set_asn_coding(bool encode, MessageEncodingType_t new_coding); void chk_coding(bool encode, bool delayed = false); bool is_coding_by_function() const; const string& get_coding(bool encode) const; diff --git a/compiler2/record_of.c b/compiler2/record_of.c index 828cd66de89fb62683f737322434ca40eeaed3a4..c98be28c2134ec4d9fc3deff9660f5666f62c6f7 100644 --- a/compiler2/record_of.c +++ b/compiler2/record_of.c @@ -1240,9 +1240,9 @@ void defRecordOfClass1(const struct_of_def *sdef, output_struct *output) " after.XER_encode(UNIVERSAL_CHARSTRING_xer_, p_buf, p_flavor | ANY_ATTRIBUTES, p_indent, 0);\n" // Put this attribute in a dummy element and walk through it to check its validity " TTCN_Buffer check_buf;\n" - " check_buf.put_s(2, (unsigned char*)\"<a\");\n" + " check_buf.put_s(2, (const unsigned char*)\"<a\");\n" " check_buf.put_s(p_buf.get_len() - buf_start, p_buf.get_data() + buf_start);\n" - " check_buf.put_s(2, (unsigned char*)\"/>\");" + " check_buf.put_s(2, (const unsigned char*)\"/>\");" " XmlReaderWrap checker(check_buf);\n" " while (1 == checker.Read()) ;\n" " }\n" diff --git a/compiler2/ttcn3/AST_ttcn3.cc b/compiler2/ttcn3/AST_ttcn3.cc index dec0bdcf1172bf404327ffd4ea4e6b696d98732d..3d9482329cad862d802c3cd37205da2f2e15ea4a 100644 --- a/compiler2/ttcn3/AST_ttcn3.cc +++ b/compiler2/ttcn3/AST_ttcn3.cc @@ -6573,6 +6573,10 @@ namespace Ttcn { function_type = EXTFUNC_MANUAL; } } + else if (input_type->is_ref() && input_type->get_type_refd()->is_asn1()) { + // let the input ASN.1 type know that this is its encoding type + input_type->get_type_refd()->set_asn_coding(true, encoding_type); + } } } if (output_type) { @@ -6649,6 +6653,11 @@ namespace Ttcn { function_type = EXTFUNC_MANUAL; } } + else if (output_type != NULL && output_type->is_ref() && + output_type->get_type_refd()->is_asn1()) { + // let the output ASN.1 type know that this is its decoding type + output_type->get_type_refd()->set_asn_coding(false, encoding_type); + } } if (eb_list) eb_list->chk(); chk_allowed_encode(); diff --git a/core2/Basetype2.cc b/core2/Basetype2.cc index 695fdff6de0a1aae65ec6cc459dc8e7e6e2eff44..86195fc04d8cd7a5ac692a959e4108af3fac4e1c 100644 --- a/core2/Basetype2.cc +++ b/core2/Basetype2.cc @@ -1928,9 +1928,9 @@ int Record_Of_Type::XER_encode(const XERdescriptor_t& p_td, TTCN_Buffer& p_buf, // Put this attribute in a dummy element and walk through it to check its validity TTCN_Buffer check_buf; - check_buf.put_s(2, (unsigned char*)"<a"); + check_buf.put_s(2, (const unsigned char*)"<a"); check_buf.put_s(p_buf.get_len() - buf_start, p_buf.get_data() + buf_start); - check_buf.put_s(2, (unsigned char*)"/>"); + check_buf.put_s(2, (const unsigned char*)"/>"); XmlReaderWrap checker(check_buf); while (1 == checker.Read()); } diff --git a/regression_test/customEncoding/Custom1.ttcn b/regression_test/customEncoding/Custom1.ttcn index 29b7c8ce7dee303552174dfddfa78bbdebfda602..8f253de2f4afe96fbaa3c192009fbc66da86f4b0 100644 --- a/regression_test/customEncoding/Custom1.ttcn +++ b/regression_test/customEncoding/Custom1.ttcn @@ -11,14 +11,17 @@ * ******************************************************************************/ -// This module tests custom encoding -// (encvalue and decvalue encode and decode values using manually written -// external functions, as long as they have the same encoding name as the -// value's type) +// This module tests custom encoding for TTCN-3 types and encoding for ASN.1 types +// (Encvalue and decvalue encode and decode values of TTCN-3 types using manually written +// external functions, as long as they have the same encoding name as the value's type. +// A similar technique is used when using encvalue or decvalue on a value of +// and ASN.1 type: an encoding/decoding external function, of a built-in encoding type, +// must be declared for the ASN.1 type). module Custom1 { import from Custom2 all; import from Custom3 all; +import from Types all; type record Msg { octetstring data optional, @@ -237,6 +240,92 @@ testcase tc_custom_decmatch() runs on CT } } +// these let encvalue and decvalue know which encoding to use for the ASN.1 type +external function f_enc_seq(in Seq x) return octetstring + with { extension "prototype(convert) encode(JSON)" }; + +external function f_dec_seq(in octetstring x) return Seq + with { extension "prototype(convert) decode(JSON)" }; + +// Test 8. +// Using encvalue and decvalue on the ASN.1 type Seq (with JSON encoding). +testcase tc_asn() runs on CT +{ + var Seq x := { num := 10, str := "abc" }; + var bitstring enc_exp := oct2bit(char2oct("{\n\t\"num\" : 10,\n\t\"str\" : \"abc\"\n}")); + var Seq dec_exp := x; + + var bitstring enc := encvalue(x); + if (enc != enc_exp) { + setverdict(fail, "Expected: ", enc_exp, ", got: ", enc); + } + var Seq dec; + var integer res := decvalue(enc_exp, dec); + if (res != 0) { + setverdict(fail, "Failed to decode ", enc_exp); + } + if (dec != dec_exp) { + setverdict(fail, "Expected: ", dec_exp, ", got: ", dec); + } + setverdict(pass); +} + +// Test 9. +// The redirected parameter is decoded into a value of ASN.1 type Seq. +// Same input value as in test 8. +testcase tc_asn_param_redirect() runs on CT +{ + connect(self:pt_proc, self:pt_proc); + var Seq val := { num := 10, str := "abc" }; + var charstring str_fmt := "UTF-8"; + var universal charstring val_enc := encvalue_unichar(val, str_fmt); + var Seq res; + pt_proc.reply(Sig: { p := val_enc }); + timer tmr := 1.0; + tmr.start; + alt { + [] pt_proc.getreply(Sig: { p := val_enc }) -> param (res := @decoded(str_fmt) p) { + if (res != val) { + setverdict(fail, "Invalid decoded parameter. Expected: ", val, ", got: ", res); + } + else { + setverdict(pass); + } + } + [] pt_proc.getreply(Sig: { p := ?}) { + setverdict(fail, "Invalid reply received."); + } + [] tmr.timeout { + setverdict(fail, "Timed out."); + } + } +} + +// Test 10. +// Decoded content matching against a value of ASN.1 type Seq. +// Same input value as in test 8. +testcase tc_asn_decmatch() runs on CT +{ + connect(self:pt_msg, self:pt_msg); + var Seq val := { num := 10, str := "abc" }; + var Msg msg := { data := omit, list := { encvalue(val) } }; + var Seq res; + pt_msg.send(msg); + timer tmr := 1.0; + tmr.start; + alt { + [] pt_msg.receive(Msg: { data := omit, list := { decmatch val } }) { + setverdict(pass); + } + [] pt_msg.receive(?) { + setverdict(fail, "Invalid message received or decoded content matching failed."); + } + [] tmr.timeout { + setverdict(fail, "Timed out."); + } + } +} + control { execute(tc_custom1()); execute(tc_custom2()); @@ -245,6 +334,9 @@ control { execute(tc_custom_unichar()); execute(tc_custom_param_redirect()); execute(tc_custom_decmatch()); + execute(tc_asn()); + execute(tc_asn_param_redirect()); + execute(tc_asn_decmatch()); } } diff --git a/regression_test/customEncoding/Custom4.ttcn b/regression_test/customEncoding/Custom4.ttcn index c3e68713a56ac1f96597e60bb4628030d29c36eb..9326226fe0675ac0d2457e35ffe57e075b3ae6d1 100644 --- a/regression_test/customEncoding/Custom4.ttcn +++ b/regression_test/customEncoding/Custom4.ttcn @@ -10,15 +10,16 @@ * ******************************************************************************/ -// This module contains further tests for custom encodings +// This module contains further tests for custom encodings and encodings for ASN.1 types // (in features only available in the Function Test Runtime). module Custom4 { import from Custom1 all; import from Custom2 all; import from Custom3 all; +import from Types all; -// Test 8 (RT2 only). +// Test 11 (RT2 only). // Using custom encoding on a decoded value redirect. // Same input value as in test 1. testcase tc_custom_value_redirect() runs on CT @@ -49,8 +50,39 @@ testcase tc_custom_value_redirect() runs on CT } } +// Test 12 (RT2 only). +// The redirected value is decoded into a value of ASN.1 type Seq. +// Same input value as in test 8. +testcase tc_asn_value_redirect() runs on CT +{ + connect(self:pt_msg, self:pt_msg); + var Seq val := { num := 10, str := "abc" }; + var Msg msg := { data := bit2oct(encvalue(val)), list := { } }; + var Seq res; + pt_msg.send(msg); + timer tmr := 1.0; + tmr.start; + alt { + [] pt_msg.receive(msg) -> value (res := @decoded data) { + if (res != val) { + setverdict(fail, "Invalid decoded value. Expected: ", val, ", got: ", res); + } + else { + setverdict(pass); + } + } + [] pt_msg.receive(?) { + setverdict(fail, "Invalid message received."); + } + [] tmr.timeout { + setverdict(fail, "Timed out."); + } + } +} + control { execute(tc_custom_value_redirect()); + execute(tc_asn_value_redirect()); } } diff --git a/regression_test/customEncoding/Makefile b/regression_test/customEncoding/Makefile index 21f701f43ac93fea3b7c4b0f103403fff6105a41..fee2198833b40b7655a1230a50cd0bb7655a999a 100644 --- a/regression_test/customEncoding/Makefile +++ b/regression_test/customEncoding/Makefile @@ -20,6 +20,7 @@ include $(TOPDIR)/Makefile.regression TTCN3_LIB = ttcn3$(RT2_SUFFIX)$(DYNAMIC_SUFFIX) TTCN3_MODULES = Custom1.ttcn Custom2.ttcn Custom3.ttcn +ASN1_MODULES = Types.asn ifdef RT2 TTCN3_MODULES += Custom4.ttcn @@ -27,7 +28,7 @@ endif USER_SOURCES = Coders.cc -GENERATED_SOURCES = $(TTCN3_MODULES:.ttcn=.cc) +GENERATED_SOURCES = $(TTCN3_MODULES:.ttcn=.cc) $(ASN1_MODULES:.asn=.cc) GENERATED_HEADERS = $(GENERATED_SOURCES:.cc=.hh) ifdef CODE_SPLIT GENERATED_SOURCES := $(foreach file, $(GENERATED_SOURCES:.cc=), $(addprefix $(file), .cc _seq.cc _set.cc _seqof.cc _setof.cc _union.cc)) diff --git a/regression_test/customEncoding/Types.asn b/regression_test/customEncoding/Types.asn new file mode 100644 index 0000000000000000000000000000000000000000..b3ce172b1ce327e92a7272335bfa32f57070a054 --- /dev/null +++ b/regression_test/customEncoding/Types.asn @@ -0,0 +1,26 @@ +--///////////////////////////////////////////////////////////////////////////// +-- Copyright (c) 2000-2016 Ericsson Telecom AB +-- All rights reserved. This program and the accompanying materials +-- are made available under the terms of the Eclipse Public License v1.0 +-- which accompanies this distribution, and is available at +-- http://www.eclipse.org/legal/epl-v10.html +-- +-- Contributors: +-- Baranyi, Botond +-- +--///////////////////////////////////////////////////////////////////////////// + +Types +DEFINITIONS + +AUTOMATIC TAGS ::= + +BEGIN +IMPORTS; + +Seq ::= SEQUENCE { + num INTEGER, + str VisibleString +} + +END diff --git a/usrguide/referenceguide.doc b/usrguide/referenceguide.doc index 034462f3ca0ea6aa904a5b2c53084a98ad7ab030..8756b417fd8bd5d0684cc4b4e2149ea7e0c65089 100644 Binary files a/usrguide/referenceguide.doc and b/usrguide/referenceguide.doc differ