Commit 2793df03 authored by Botond Baranyi's avatar Botond Baranyi
Browse files

Support for multiple encodings - stage two (bug 517843)



Change-Id: I7ebd47e4f108ee05021a6130ec42e1d038fa308e
Signed-off-by: Botond Baranyi's avatarBotond Baranyi <botond.baranyi@ericsson.com>
parent e84bc2a4
......@@ -773,6 +773,16 @@ namespace Common {
for (size_t i = 0; i < coding_table.size(); ++i) {
if (!coding_table[i]->built_in) {
Free(coding_table[i]->custom_coding.name);
for (size_t j = 0; j < coding_table[i]->custom_coding.encoders->size(); ++j) {
delete coding_table[i]->custom_coding.encoders->get_nth_elem(j);
}
coding_table[i]->custom_coding.encoders->clear();
delete coding_table[i]->custom_coding.encoders;
for (size_t j = 0; j < coding_table[i]->custom_coding.decoders->size(); ++j) {
delete coding_table[i]->custom_coding.decoders->get_nth_elem(j);
}
coding_table[i]->custom_coding.decoders->clear();
delete coding_table[i]->custom_coding.decoders;
}
delete coding_table[i];
}
......@@ -3128,7 +3138,7 @@ namespace Common {
if (err) {
if (last->typetype == T_ENUM_T) {
error("Invalid JSON default value for enumerated type `%s'",
last->get_stringRepr().c_str());
last->get_typename().c_str());
} else {
error("Invalid %s JSON default value", get_typename_builtin(last->typetype));
}
......@@ -3293,11 +3303,9 @@ namespace Common {
else {
coding_t* new_coding = new coding_t;
new_coding->built_in = FALSE;
new_coding->custom_coding.enc_func = NULL;
new_coding->custom_coding.enc_conflict = FALSE;
new_coding->custom_coding.dec_func = NULL;
new_coding->custom_coding.dec_conflict = FALSE;
new_coding->custom_coding.name = mcopystr(name.c_str());
new_coding->custom_coding.encoders = new map<Type*, coder_function_t>;
new_coding->custom_coding.decoders = new map<Type*, coder_function_t>;
coding_table.add(new_coding);
}
}
......@@ -3308,19 +3316,24 @@ namespace Common {
if (legacy_codec_handling) {
FATAL_ERROR("Type::set_coding_function");
}
// TODO: the coding table might be in a referenced type
for (size_t i = 0; i < coding_table.size(); ++i) {
if (!coding_table[i]->built_in &&
strcmp(coding_table[i]->custom_coding.name, coding_name) == 0) {
Assignment*& func_ptr = encode ? coding_table[i]->custom_coding.enc_func :
coding_table[i]->custom_coding.dec_func;
boolean& conflict = encode ? coding_table[i]->custom_coding.enc_conflict :
coding_table[i]->custom_coding.dec_conflict;
if (func_ptr != NULL) {
conflict = TRUE;
Type* t = get_type_w_coding_table();
if (t == NULL) {
return;
}
for (size_t i = 0; i < t->coding_table.size(); ++i) {
if (!t->coding_table[i]->built_in &&
strcmp(t->coding_table[i]->custom_coding.name, coding_name) == 0) {
map<Type*, coder_function_t>* coders = encode ?
t->coding_table[i]->custom_coding.encoders :
t->coding_table[i]->custom_coding.decoders;
if (coders->has_key(this)) {
(*coders)[this]->conflict = TRUE;
}
else if (!conflict) {
func_ptr = function_def;
else {
coder_function_t* new_coder = new coder_function_t;
new_coder->func_def = function_def;
new_coder->conflict = FALSE;
coders->add(this, new_coder);
Common::Module* func_mod = function_def->get_my_scope()->get_scope_mod();
Common::Module* type_mod = my_scope->get_scope_mod();
if (func_mod != type_mod) {
......@@ -3347,6 +3360,32 @@ namespace Common {
}
}
Type::coder_function_t* Type::get_coding_function(size_t index, boolean encode)
{
if (legacy_codec_handling) {
FATAL_ERROR("Type::get_coding_function");
}
Type* t_ct = get_type_w_coding_table();
if (t_ct == NULL || t_ct->coding_table.size() <= index ||
t_ct->coding_table[index]->built_in) {
FATAL_ERROR("Type::get_coding_function");
}
map<Type*, coder_function_t>* coders = encode ?
t_ct->coding_table[index]->custom_coding.encoders :
t_ct->coding_table[index]->custom_coding.decoders;
if (coders->has_key(this)) {
return (*coders)[this];
}
Type* t = this;
while (t->is_ref()) {
t = t->get_type_refd();
if (coders->has_key(t)) {
return (*coders)[t];
}
}
return NULL; // not found
}
Type* Type::get_type_w_coding_table()
{
// only return the type if it has a non-empty coding table
......@@ -4957,25 +4996,22 @@ namespace Common {
if (!t->coding_table[i]->built_in) {
// all user-defined encodings must have exactly one encoder/decoder
// function set
boolean& conflict = encode ? t->coding_table[i]->custom_coding.enc_conflict :
t->coding_table[i]->custom_coding.dec_conflict;
if (conflict) {
error("Multiple `%s' %scoder functions defined for type `%s'",
t->coding_table[i]->custom_coding.name, encode ? "en" : "de",
get_typename().c_str());
// TODO: don't display this twice (once in the normal call and once in the delayed call)
continue;
}
if (!delayed) {
// there might still be unchecked external functions, so the check
// must be delayed until everything else has been checked
Modules::delay_type_encode_check(this, usage_mod, encode);
return;
}
Assignment* func_def = encode ? t->coding_table[i]->custom_coding.enc_func :
t->coding_table[i]->custom_coding.dec_func;
if (func_def == NULL && !is_asn1()) {
warning("No `%s' %scoder function defined for type `%s'",
coder_function_t* coding_func = get_coding_function(i, encode);
if (coding_func == NULL) {
if (!t->is_asn1()) {
warning("No `%s' %scoder function defined for type `%s'",
t->coding_table[i]->custom_coding.name, encode ? "en" : "de",
get_typename().c_str());
}
}
else if (coding_func->conflict) {
warning("Multiple `%s' %scoder functions defined for type `%s'",
t->coding_table[i]->custom_coding.name, encode ? "en" : "de",
get_typename().c_str());
}
......@@ -5187,7 +5223,7 @@ namespace Common {
}
}
Assignment* Type::get_coding_function(bool encode) const
Assignment* Type::get_legacy_coding_function(bool encode) const
{
if (!legacy_codec_handling) {
FATAL_ERROR("Type::get_coding_function");
......@@ -6220,8 +6256,10 @@ namespace Common {
}
}
Type* last = get_type_refd_last();
if (last->is_structured_type() || last->get_typetype_ttcn3() == T_ENUM_T) {
// these types need an 'encode' attribute
if (encoding_type == CT_CUSTOM ||
last->is_structured_type() || last->get_typetype_ttcn3() == T_ENUM_T) {
// these types need an 'encode' attribute for built-in codecs, and all
// types need an 'encode' attribute for user-defined codecs
return false;
}
return last->can_have_coding(encoding_type);
......@@ -7147,6 +7185,10 @@ namespace Common {
FATAL_ERROR("Type::get_genname_coder");
}
Type* t = this;
Type* t_ct = get_type_w_coding_table();
if (t_ct == NULL) {
return string();
}
for ( ; ; ) {
// if the type has an 'encode' or 'variant' attribute, then it needs its
// own coder functions
......@@ -7155,6 +7197,15 @@ namespace Common {
(t->xerattrib && !t->xerattrib->empty())) {
return t->get_genname_own(p_scope);
}
// if it has its own custom encoder or decoder functions set, then it needs
// its own coder functions
for (size_t i = 0; i < t_ct->coding_table.size(); ++i) {
if (!t_ct->coding_table[i]->built_in &&
(t_ct->coding_table[i]->custom_coding.encoders->has_key(t) ||
t_ct->coding_table[i]->custom_coding.decoders->has_key(t))) {
return t->get_genname_own(p_scope);
}
}
if (t->is_ref()) {
t = t->get_type_refd();
}
......@@ -7648,12 +7699,24 @@ namespace Common {
get_encoding_name(coding_table[i]->built_in_coding));
}
else {
Assignment* enc_func = coding_table[i]->custom_coding.enc_func;
Assignment* def_func = coding_table[i]->custom_coding.dec_func;
DEBUG(level + 1, "%s (encoder: %s, decoder: %s)",
coding_table[i]->custom_coding.name,
(enc_func != NULL) ? enc_func->get_fullname().c_str() : "<unset>",
(def_func != NULL) ? def_func->get_fullname().c_str() : "<unset>");
DEBUG(level + 1, "%s (user-defined)",
coding_table[i]->custom_coding.name);
DEBUG(level + 1, "Encoders:");
map<Type*, coder_function_t>* encoders = coding_table[i]->custom_coding.encoders;
for (size_t j = 0; j < encoders->size(); ++j) {
DEBUG(level + 2, "Type: %s, function: %s",
encoders->get_nth_key(j)->get_fullname().c_str(),
encoders->get_nth_elem(j)->conflict ? "<multiple>" :
encoders->get_nth_elem(j)->func_def->get_fullname().c_str());
}
DEBUG(level + 1, "Decoders:");
map<Type*, coder_function_t>* decoders = coding_table[i]->custom_coding.decoders;
for (size_t j = 0; j < decoders->size(); ++j) {
DEBUG(level + 2, "Type: %s, function: %s",
decoders->get_nth_key(j)->get_fullname().c_str(),
decoders->get_nth_elem(j)->conflict ? "<multiple>" :
decoders->get_nth_elem(j)->func_def->get_fullname().c_str());
}
}
}
}
......
......@@ -292,6 +292,12 @@ namespace Common {
};
};
/** Stores information about the custom encoder or decoder function of a type */
struct coder_function_t {
Assignment* func_def; ///< definition of the encoder or decoder function
boolean conflict; ///< indicates whether there are multiple encoder/decoder functions for this type and codec
};
/** Stores information related to an encoding type (codec), when using new
* codec handling. */
struct coding_t {
......@@ -300,10 +306,9 @@ namespace Common {
MessageEncodingType_t built_in_coding; ///< built-in codec
struct {
char* name; ///< name of the user defined codec (the string in the 'encode' attribute)
Assignment* enc_func; ///< definition of the encoder function
boolean enc_conflict; ///< indicates whether there are multiple encoder functions for this type and codec
Assignment* dec_func; ///< definition of the decoder function
boolean dec_conflict; ///< indicates whether there are multiple decoder functions for this type and codec
// note: other types that use this type's coding table may have their own encoder/decoder functions
map<Type*, coder_function_t>* encoders; ///< the map of encoder functions per type
map<Type*, coder_function_t>* decoders; ///< the map of decoder functions per type
} custom_coding;
};
};
......@@ -628,6 +633,19 @@ namespace Common {
* encoding, if set by the compiler option. */
void chk_encodings();
/** Checks the type's variant attributes (when using the new codec handling). */
void chk_variants();
/** Parses the specified variant attribute and checks its validity (when
* using the new codec handling). */
void chk_this_variant(const Ttcn::SingleWithAttrib* swa, bool global);
/** Checks the coding instructions set by the parsed variant attributes.
* The check is performed recursively on the type's fields and elements
* when using the new codec handling.
* The check is only performed on this type, when using legacy codec handling. */
void chk_coding_attribs();
/** Adds support for an encoding by the type (when using new codec handling),
* if the type can have that encoding.
* @param name name of the encoding as it appears in the 'encode' attribute;
......@@ -639,6 +657,10 @@ namespace Common {
void set_coding_function(const char* coding_name, boolean encode,
Assignment* function_def);
/** Returns the custom encoder or decoder function at the specified index in
* the coding table related to this type (when using the new codec handling). */
coder_function_t* get_coding_function(size_t index, boolean encode);
/** Returns the type that contains this type's coding table (since types
* with no 'encode' attributes of their own inherit the 'encode' attributes
* of a referenced type or a parent type).
......@@ -784,8 +806,9 @@ namespace Common {
string get_coding(bool encode) const;
/** Returns the function definition of the type's encoder/decoder function.
* Used during code generation for types encoded/decoded by functions. */
Assignment* get_coding_function(bool encode) const;
* Used during code generation for types encoded/decoded by functions.
* Only used with legacy codec handling. */
Assignment* get_legacy_coding_function(bool encode) const;
static MessageEncodingType_t get_enc_type(const string& enc);
......
......@@ -48,7 +48,9 @@ using Ttcn::Qualifiers;
void Type::chk()
{
if(w_attrib_path) w_attrib_path->chk_global_attrib();
parse_attributes();
if (legacy_codec_handling) {
parse_attributes();
}
if(!tags_checked) {
tags_checked = true;
if(tags) tags->chk();
......@@ -112,28 +114,14 @@ void Type::chk()
case T_SEQ_T:
case T_SET_T:
case T_CHOICE_T:
chk_SeCho_T();
// If this sequence type has no attributes but one of its fields does,
// create an empty attribute structure.
if(!rawattrib && hasVariantAttrs() && hasNeedofRawAttrs())
rawattrib = new RawAST(get_default_raw_fieldlength());
if(!textattrib && hasVariantAttrs() && hasNeedofTextAttrs())
textattrib = new TextAST;
if(!xerattrib && hasVariantAttrs() && hasNeedofXerAttrs())
xerattrib = new XerAttributes;
if (!jsonattrib && hasVariantAttrs() && hasNeedofJsonAttrs()) {
jsonattrib = new JsonAST;
}
chk_SeCho_T();
break;
case T_CHOICE_A:
chk_Choice_A();
// TODO create an empty XerAttrib as above, when ASN.1 gets XER ?
// The code was originally for TTCN-only encodings.
break;
case T_SEQ_A:
case T_SET_A:
chk_Se_A();
// TODO create an empty XerAttrib as above, when ASN.1 gets XER ?
break;
case T_SEQOF:
case T_SETOF:
......@@ -187,9 +175,6 @@ void Type::chk()
break;
}
}
if (!legacy_codec_handling && my_scope != NULL) {
chk_encodings();
}
checked = true;
if(tags) tags->set_plicit(this);
......@@ -208,14 +193,46 @@ void Type::chk()
*/
if (!parent_type) chk_table_constraints();
if(rawattrib || is_root_basic()){
if (legacy_codec_handling) {
chk_coding_attribs();
}
else if (my_scope != NULL) {
chk_encodings();
chk_variants();
}
chk_finished = true;
}
void Type::chk_coding_attribs()
{
if (typetype == T_SEQ_T || typetype == T_SET_T || typetype == T_CHOICE_T) {
// If this record/set/union type has no attributes but one of its fields does,
// create an empty attribute structure.
// TODO: do the same for ASN.1 sequence/set/choice types, when ASN.1 gets XER ?
// The code was originally for TTCN-only encodings.
if (!rawattrib && hasVariantAttrs() && hasNeedofRawAttrs()) {
rawattrib = new RawAST(get_default_raw_fieldlength());
}
if (!textattrib && hasVariantAttrs() && hasNeedofTextAttrs()) {
textattrib = new TextAST;
}
if (!xerattrib && hasVariantAttrs() && hasNeedofXerAttrs()) {
xerattrib = new XerAttributes;
}
if (!jsonattrib && hasVariantAttrs() && hasNeedofJsonAttrs()) {
jsonattrib = new JsonAST;
}
}
if (rawattrib != NULL || is_root_basic()) {
chk_raw();
}
if(textattrib || is_root_basic()) {
if (textattrib != NULL || is_root_basic()) {
chk_text();
}
if (jsonattrib || is_root_basic()) {
if (jsonattrib != NULL || is_root_basic()) {
chk_json();
}
......@@ -223,7 +240,31 @@ void Type::chk()
// XER attributes from parent types.
chk_xer();
chk_finished = true;
if (!legacy_codec_handling) {
switch (typetype) {
case T_SEQ_T:
case T_SEQ_A:
case T_SET_T:
case T_SET_A:
case T_CHOICE_T:
case T_CHOICE_A:
case T_ANYTYPE:
case T_OPENTYPE:
for (size_t i = 0; i < get_nof_comps(); ++i) {
get_comp_byIndex(i)->get_type()->chk_coding_attribs();
}
break;
case T_ARRAY:
case T_SEQOF:
case T_SETOF:
get_ofType()->chk_coding_attribs();
break;
default:
break;
}
}
}
void Type::parse_attributes()
......@@ -558,9 +599,10 @@ void Type::parse_attributes()
// Special case when trying to reference the inner type
// of a record-of when it wasn't a record-of.
if (tmp_id == underscore_zero) temp_qualifier->error(
"Invalid field qualifier [-]");
else temp_qualifier->error("Invalid field qualifier %s",
tmp_id.get_dispname().c_str());
"Type `%s' cannot be indexed", get_typename().c_str());
else temp_qualifier->error(
"Reference to non-existent field `%s' in type `%s'",
tmp_id.get_dispname().c_str(), get_typename().c_str());
}
self_attribs->delete_element(i);
}else{
......@@ -666,8 +708,16 @@ void Type::chk_encodings()
for (size_t j = 0; j < quals->get_nof_qualifiers(); ++j) {
Ttcn::Qualifier* qual = const_cast<Ttcn::Qualifier*>(
quals->get_qualifier(j));
get_field_type(qual, EXPECTED_CONSTANT)->
add_coding(swa->get_attribSpec().get_spec());
Type* t = get_field_type(qual, EXPECTED_CONSTANT);
if (t != NULL) {
if (t->my_scope != my_scope) {
qual->warning("Encode attribute is ignored, because it "
"refers to a type from a different type definition");
}
else {
t->add_coding(swa->get_attribSpec().get_spec());
}
}
}
}
else {
......@@ -676,7 +726,9 @@ void Type::chk_encodings()
}
}
}
if (coding_table.size() == 0) {
if (get_type_w_coding_table() == NULL) {
// if there are no 'encode' attributes in this type, the referenced
// types, or the parent type, then try the nearest group or the module
const vector<SingleWithAttrib>& real = ap->get_real_attrib();
for (size_t i = 0; i < real.size(); ++i) {
const SingleWithAttrib* swa = real[i];
......@@ -713,6 +765,249 @@ void Type::chk_encodings()
}
}
void Type::chk_variants()
{
if (legacy_codec_handling) {
FATAL_ERROR("Type::chk_encodings");
}
if (is_asn1() || ownertype != OT_TYPE_DEF) {
return;
}
// check global (group/module) variant attributes first
WithAttribPath* global_ap = NULL;
Ttcn::Def_Type* def = static_cast<Ttcn::Def_Type*>(owner);
Ttcn::Group* nearest_group = def->get_parent_group();
if (nearest_group != NULL) { // there is a group
global_ap = nearest_group->get_attrib_path();
}
else { // no group, use the module
Common::Module* mymod = my_scope->get_scope_mod();
// OT_TYPE_DEF is always from a TTCN-3 module
Ttcn::Module* my_ttcn_module = static_cast<Ttcn::Module *>(mymod);
global_ap = my_ttcn_module->get_attrib_path();
}
if (global_ap != NULL) {
MultiWithAttrib* mwa = global_ap->get_with_attr();
if (mwa != NULL) {
for (size_t i = 0; i < mwa->get_nof_elements(); ++i) {
const SingleWithAttrib* swa = mwa->get_element(i);
if (swa->get_attribKeyword() == SingleWithAttrib::AT_VARIANT) {
chk_this_variant(swa, true);
}
}
}
}
// check local variant attributes second, so they overwrite global ones if they
// conflict with each other
WithAttribPath* ap = get_attrib_path();
if (ap != NULL) {
MultiWithAttrib* mwa = ap->get_with_attr();
if (mwa != NULL) {
for (size_t i = 0; i < mwa->get_nof_elements(); ++i) {
const SingleWithAttrib* swa = mwa->get_element(i);
if (swa->get_attribKeyword() == SingleWithAttrib::AT_VARIANT) {
Ttcn::Qualifiers* quals = swa->get_attribQualifiers();
if (quals != NULL && quals->get_nof_qualifiers() != 0) {
for (size_t j = 0; j < quals->get_nof_qualifiers(); ++j) {
Ttcn::Qualifier* qual = const_cast<Ttcn::Qualifier*>(
quals->get_qualifier(j));
Type* t = get_field_type(qual, EXPECTED_CONSTANT);
if (t != NULL) {
if (t->my_scope != my_scope) {
qual->warning("Variant attribute is ignored, because it refers "
"to a type from a different type definition");
}
else {
t->chk_this_variant(swa, false);
}
}
}
}
else {
chk_this_variant(swa, false);
}
}
}
}
}
// check the coding attributes set by the variants
chk_coding_attribs();
}
void Type::chk_this_variant(const Ttcn::SingleWithAttrib* swa, bool global)
{
// TODO: use something other than get_typename...
Type* t = get_type_w_coding_table();
if (t == NULL) {
if (!global) {
swa->error("No encoding rules defined for type `%s'",
get_typename().c_str());
}
}
else {
const string& enc_str = swa->get_attribSpec().get_encoding();
MessageEncodingType_t coding = get_enc_type(enc_str);
bool erroneous = false;
if (enc_str.empty()) {
if (t->coding_table.size() > 1) {
if (!global) {
swa->error("The encoding reference is mandatory for variant attributes "
"of type `%s', which has multiple encodings", get_typename().c_str());
}
erroneous = true;
}
else if (t->coding_table[0]->built_in) {
coding = t->coding_table[0]->built_in_coding;
}
else if (strcmp(t->coding_table[0]->custom_coding.name, "PER") == 0) {
coding = CT_PER;
}
// else leave it as CT_CUSTOM
}
else {
if (!has_encoding(coding, &enc_str)) {
erroneous = true;
if (!global) {
if (coding != CT_CUSTOM) {
swa->error("Type `%s' does not support %s encoding",
get_typename().c_str(), get_encoding_name(coding));
}
else {
swa->error("Type `%s' does not support custom encoding `%s'",
get_typename().c_str(), enc_str.c_str());
}
}
}
}
if (!erroneous && coding != CT_PER && coding != CT_CUSTOM) {
bool new_ber = false; // a BerAST object was allocated here
bool new_raw = false; // a RawAST object was allocated here
bool new_text = false; // a TextAST object was allocated here
bool new_xer = false; // a XerAttribute object was allocated here
bool new_json = false; // a JsonAST object was allocated here
bool ber_found = false; // a BER attribute was found by the parser
bool raw_found = false; // a RAW attribute was found by the parser
bool text_found = false; // a TEXT attribute was found by the parser
bool xer_found = false; // a XER attribute was found by the parser
bool json_found = false; // a JSON attribute was found by the parser
if (berattrib == NULL) {
berattrib = new BerAST;
new_ber = true;
}
if (rawattrib == NULL) {
Type* t_refd = this;
while (t_refd->rawattrib == NULL && t_refd->is_ref()) {
t_refd = t_refd->get_type_refd();
}
rawattrib = new RawAST(t_refd->rawattrib, get_default_raw_fieldlength());
new_raw = true;
}
if (textattrib == NULL) {
Type* t_refd = this;
while (t_refd->textattrib == NULL && t_refd->is_ref()) {