Commit b8a7cdf8 authored by Botond Baranyi's avatar Botond Baranyi
Browse files

JSON 'encode' attributes on subtypes and field/element types are now properly handled (bug 515584)



Change-Id: I6926000b765c983dc2a4f9f69c93290d8ab1efeb
Signed-off-by: Botond Baranyi's avatarBotond Baranyi <botond.baranyi@ericsson.com>
parent a23e629a
......@@ -607,6 +607,7 @@ namespace Common {
chk_finished = false;
pard_type_instance = false;
needs_any_from_done = false;
gen_json_coder_functions = false;
default_encoding.type = CODING_UNSET;
default_decoding.type = CODING_UNSET;
}
......@@ -1759,8 +1760,7 @@ namespace Common {
t->u.array.dimension->chk_index(index_value, expected_index);
} else {
// perform a generic index check for other types
if (refch == 0 // variable assignment
|| index_value->get_valuetype() != Value::V_NOTUSED) {
if (index_value->get_valuetype() != Value::V_NOTUSED) {
Error_Context cntxt(index_value, "In index value");
index_value->chk_expr_int(expected_index);
}
......@@ -3120,6 +3120,7 @@ namespace Common {
case T_SEQ_A:
case T_SET_T:
case T_SET_A:
case T_ANYTYPE:
jsonattrib = new JsonAST;
break;
default:
......@@ -5631,20 +5632,14 @@ namespace Common {
bool Type::hasEncodeAttr(const char* encoding_name)
{
if (0 == strcmp(encoding_name, "JSON") && (implicit_json_encoding
|| is_asn1() || (is_ref() && get_type_refd()->is_asn1()))) {
if (0 == strcmp(encoding_name, "JSON") &&
(implicit_json_encoding || is_asn1())) {
// ASN.1 types automatically support JSON encoding
return true;
}
// Check the type itself first, then the root type
WithAttribPath *aps[2] = { 0, 0 };
size_t num_aps = ((aps[0] = get_attrib_path()) != 0);
// assign, compare, then add 0 or 1
if (is_ref()) {
num_aps += ((aps[num_aps] = get_type_refd()->get_attrib_path()) != 0);
}
for (size_t a = 0; a < num_aps; ++a) {
const vector<SingleWithAttrib>& real = aps[a]->get_real_attrib();
WithAttribPath* ap = get_attrib_path();
if (ap != NULL) {
const vector<SingleWithAttrib>& real = ap->get_real_attrib();
const size_t num_atr = real.size();
for (size_t i = 0; i < num_atr; ++i) {
const SingleWithAttrib& s = *real[i];
......@@ -5655,13 +5650,64 @@ namespace Common {
}
} // if ENCODE
} // for
} // next a
} // if ap
if ((ownertype == OT_COMP_FIELD || ownertype == OT_RECORD_OF ||
ownertype == OT_ARRAY) && parent_type != NULL &&
parent_type->get_attrib_path() != NULL &&
parent_type->hasEncodeAttrForType(this, encoding_name)) {
// the encode attribute might be in the parent type
return true;
}
return false;
}
bool Type::hasEncodeAttrForType(Type* target_type, const char* encoding_name)
{
// if this type has an encode attribute, that also extends to its
// fields/elements
if (hasEncodeAttr(encoding_name)) {
return true;
}
// otherwise search this type's qualified attributes
MultiWithAttrib* mwa = get_attrib_path()->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_ENCODE &&
swa->get_attribSpec().get_spec() == encoding_name) {
// search the attribute's qualifiers for one that refers to the
// target type
Ttcn::Qualifiers* quals = swa->get_attribQualifiers();
for (size_t j = 0; j < quals->get_nof_qualifiers(); ++j) {
Ttcn::Qualifier* qual = const_cast<Ttcn::Qualifier*>(
quals->get_qualifier(j));
if (get_field_type(qual, EXPECTED_CONSTANT) == target_type) {
return true;
}
}
}
}
}
if ((ownertype == OT_COMP_FIELD || ownertype == OT_RECORD_OF ||
ownertype == OT_ARRAY) && parent_type != NULL &&
parent_type->get_attrib_path() != NULL) {
return parent_type->hasEncodeAttrForType(target_type, encoding_name);
}
return false;
}
namespace { // unnamed
enum state { PROCESSING = -1, ANSWER_NO, ANSWER_YES };
enum state {
PROCESSING = -1, // the type is now being checked
ANSWER_NO, // the type (and any types referencing this type) can never have
// the desired encoding type
ANSWER_YES, // the type (and any types referencing this type) has the
// desired encoding type
MISSING_ATTRIBUTE // the type does not have the desired encoding type, but a
// type referencing this type still could, with the right 'encode' attribute
};
struct memoizer : private map<Type*, state> {
memoizer() : map<Type*, state>() {}
......@@ -5732,6 +5778,8 @@ namespace Common {
return false;
case ANSWER_YES:
return true;
case MISSING_ATTRIBUTE:
FATAL_ERROR("Type::has_encoding");
}
}
......@@ -5800,6 +5848,8 @@ namespace Common {
case ANSWER_YES:
subresult = true;
break;
case MISSING_ATTRIBUTE:
FATAL_ERROR("Type::has_encoding");
}
}
else {
......@@ -5837,6 +5887,8 @@ namespace Common {
case ANSWER_YES:
subresult = true;
break;
case MISSING_ATTRIBUTE:
FATAL_ERROR("Type::has_encoding");
}
}
else {
......@@ -5960,7 +6012,8 @@ namespace Common {
}
}
case CT_JSON:
case CT_JSON: {
Type* type_w_enc_attr = NULL;
while (true) {
if (json_mem.has_key(t)) {
switch (*json_mem.get(t)) {
......@@ -5968,13 +6021,23 @@ namespace Common {
break;
case ANSWER_NO:
return false;
case MISSING_ATTRIBUTE:
case ANSWER_YES:
return true;
if (type_w_enc_attr != NULL) {
return json_mem.remember(type_w_enc_attr, ANSWER_YES);
}
return *json_mem.get(t) == ANSWER_YES;
}
}
if (t->jsonattrib) {
t->get_type_refd_last()->set_gen_json_coder_functions();
return json_mem.remember(t, ANSWER_YES);
}
// an 'encode' attribute is not enough, structured types must still be
// checked; this keeps track of which type had the 'encode' attribute
if (type_w_enc_attr == NULL && t->hasEncodeAttr(get_encoding_name(CT_JSON))) {
type_w_enc_attr = t;
}
if (t->is_ref()) {
t = t->get_type_refd();
}
......@@ -6033,6 +6096,7 @@ namespace Common {
// Avoids infinite recursion for self-referencing types.
continue;
case ANSWER_NO:
case MISSING_ATTRIBUTE:
// One field is not OK => the structure is not OK
return json_mem.remember(t, ANSWER_NO);
}
......@@ -6062,6 +6126,7 @@ namespace Common {
// can always be broken with an empty record-of.
break;
case ANSWER_NO:
case MISSING_ATTRIBUTE:
return json_mem.remember(t, ANSWER_NO);
break;
}
......@@ -6083,14 +6148,27 @@ namespace Common {
default:
return json_mem.remember(t, ANSWER_NO);
} // switch
return json_mem.remember(t, hasEncodeAttr(get_encoding_name(CT_JSON)) ? ANSWER_YES : ANSWER_NO);
if (type_w_enc_attr != NULL) {
t->set_gen_json_coder_functions();
return json_mem.remember(type_w_enc_attr, ANSWER_YES);
}
return json_mem.remember(t, MISSING_ATTRIBUTE);
} // else
} // while
} // case
case CT_CUSTOM:
// the encoding name parameter has to be the same as the encoding name
// specified for the type
return custom_encoding ? hasEncodeAttr(custom_encoding->c_str()) : false;
if (custom_encoding == NULL) {
return false;
}
if (hasEncodeAttr(custom_encoding->c_str())) {
return true;
}
if (is_ref()) {
return get_type_refd()->has_encoding(encoding_type, custom_encoding);
}
return false;
default:
FATAL_ERROR("Type::has_encoding()");
......@@ -6530,6 +6608,7 @@ namespace Common {
* descriptor.
*/
if (t->is_tagged() || t->rawattrib || t->textattrib || t->jsonattrib ||
(!is_asn1() && t->hasEncodeAttr(get_encoding_name(CT_JSON))) ||
(t->xerattrib && !t->xerattrib->empty() ))
{
return t->get_genname_own(p_scope);
......@@ -6651,7 +6730,7 @@ namespace Common {
{
Type *t = this;
while (true) {
if (t->jsonattrib) return t->get_genname_own(my_scope);
if (t->has_encoding(CT_JSON)) return t->get_genname_own(my_scope);
else if (t->is_ref()) t = t->get_type_refd();
else break;
}
......
......@@ -440,6 +440,12 @@ namespace Common {
* 'any from' clause) of the 'done' function needs to be generated for
* this type. */
bool needs_any_from_done;
/** True if JSON coder functions need to be generated for this type.
* This is not the same as enabling JSON encoding, since other types that
* refer to this type may have JSON encoding, in which case this type needs
* to have JSON coder functions to code them. */
bool gen_json_coder_functions;
/** Copy constructor, for the use of Type::clone() only. */
Type(const Type& p);
......@@ -1065,8 +1071,16 @@ namespace Common {
bool hasNeedofXerAttrs();
bool hasVariantAttrs();
/** Returns whether the type has the encoding attribute specified by
* the parameter (either in its own 'with' statement or in the module's) */
* the parameter. The function also checks the qualified attributes of
* parent types. Always returns true for ASN.1 types, when checking for a
* JSON encoding attribute. */
bool hasEncodeAttr(const char* encoding_name);
/** Helper function for hasEncodeAttr. Checks this type's qualified encoding
* attributes that refer to the specified type (target_type) and returns
* true if any of them match the specified encoding (encoding_name).
* Recursive function (calls the parent type's hasEncodeAttrForType function
* if no matching attributes are found). */
bool hasEncodeAttrForType(Type* target_type, const char* encoding_name);
/** Returns whether \a this can be encoded according to rules
* \a p_encoding.
* @note Should be called only during code generation, after the entire
......@@ -1252,6 +1266,8 @@ namespace Common {
inline void set_needs_any_from_done() { needs_any_from_done = true; }
inline void set_gen_json_coder_functions() { gen_json_coder_functions = true; }
/** Calculates the type's display name from the genname (replaces double
* underscore characters with single ones) */
string get_dispname() const;
......
......@@ -121,7 +121,7 @@ void Type::chk()
textattrib = new TextAST;
if(!xerattrib && hasVariantAttrs() && hasNeedofXerAttrs())
xerattrib = new XerAttributes;
if (!jsonattrib && (hasVariantAttrs() || hasEncodeAttr(get_encoding_name(CT_JSON)) || hasNeedofJsonAttrs())) {
if (!jsonattrib && hasVariantAttrs() && hasNeedofJsonAttrs()) {
jsonattrib = new JsonAST;
}
break;
......@@ -258,7 +258,7 @@ void Type::parse_attributes()
}
if ((hasVariantAttrs())
&& (enable_text() || enable_raw() || enable_xer())) {
&& (enable_text() || enable_raw() || enable_xer() || enable_json())) {
#ifndef NDEBUG
if (rawAST_debug) {
const char *fn = get_fullname().c_str();
......
......@@ -276,8 +276,8 @@ void Type::generate_code_typedescriptor(output_struct *target)
// FIXME: force_xer should be elminated. if a type needs a descriptor,
// it should say so via get_genname_typedescriptor()
/* genname{type,ber,raw,text,xer}descriptor == gennameown is true if
* the type needs its own {type,ber,raw,text,xer}descriptor
/* genname{type,ber,raw,text,xer,json}descriptor == gennameown is true if
* the type needs its own {type,ber,raw,text,xer,json}descriptor
* and can't use the descriptor of one of the built-in types.
*/
if (gennametypedescriptor == gennameown
......@@ -1102,7 +1102,7 @@ void Type::generate_code_Enum(output_struct *target)
e_def.hasText = textattrib!=NULL;
e_def.hasRaw = rawattrib!=NULL;
e_def.hasXer = has_encoding(CT_XER);
e_def.hasJson = has_encoding(CT_JSON);
e_def.hasJson = gen_json_coder_functions;
if (xerattrib) {
e_def.xerUseNumber = xerattrib->useNumber_;
}
......@@ -1143,7 +1143,7 @@ void Type::generate_code_Choice(output_struct *target)
sdef.isASN1 = is_asn1();
sdef.hasText = textattrib!=NULL;
sdef.hasXer = has_encoding(CT_XER);
sdef.hasJson = has_encoding(CT_JSON);
sdef.hasJson = gen_json_coder_functions;
sdef.has_opentypes = get_has_opentypes();
sdef.opentype_outermost = get_is_opentype_outermost();
sdef.ot = generate_code_ot(pool);
......@@ -1544,7 +1544,7 @@ void Type::generate_code_Se(output_struct *target)
sdef.opentype_outermost = get_is_opentype_outermost();
sdef.ot = NULL;
sdef.hasXer = has_encoding(CT_XER);
sdef.hasJson = has_encoding(CT_JSON);
sdef.hasJson = gen_json_coder_functions;
if (xerattrib){
Module *my_module = get_my_scope()->get_scope_mod();
sdef.xerHasNamespaces = my_module->get_nof_ns() != 0;
......@@ -2005,7 +2005,7 @@ void Type::generate_code_SeOf(output_struct *target)
sofdef.isASN1 = is_asn1();
sofdef.hasText = textattrib!=NULL;
sofdef.hasXer = has_encoding(CT_XER);
sofdef.hasJson = has_encoding(CT_JSON);
sofdef.hasJson = gen_json_coder_functions;
if (xerattrib) {
//sofdef.xerList = xerattrib->list_;
sofdef.xerAttribute = xerattrib->attribute_;
......
......@@ -1155,6 +1155,12 @@ namespace Ttcn {
{
case SingleWithAttrib::AT_ENCODE:
{
if (temp_attrib->get_attribQualifiers() != NULL &&
temp_attrib->get_attribQualifiers()->get_nof_qualifiers() != 0) {
// TODO: check the attributes grouped per qualifier (for now
// ignoring this check for attributes with qualifiers is enough)
break;
}
if(has_encode)
{
temp_attrib->warning("Only the last encode "
......
......@@ -15,6 +15,7 @@ module Functions
import from Types all;
import from JsonData language "ASN.1" all;
import from OtherTypes all;
//=================== Encoders =====================================
......@@ -135,6 +136,66 @@ external function f_enc_meta_setof(in MetainfoSetOf x) return octetstring
external function f_enc_meta_arr(in MetainfoArray x) return octetstring
with { extension "prototype(convert) encode(JSON)" }
external function f_enc_type_alias_rec(in SupportedRec x) return octetstring
with { extension "prototype(convert) encode(JSON)" }
external function f_enc_type_alias_list(in SupportedList x) return octetstring
with { extension "prototype(convert) encode(JSON)" }
external function f_enc_type_alias_uni(in SupportedUni x) return octetstring
with { extension "prototype(convert) encode(JSON)" }
external function f_enc_type_alias_enum(in SupportedEnum x) return octetstring
with { extension "prototype(convert) encode(JSON)" }
external function f_enc_type_alias_any(in SupportedAny x) return octetstring
with { extension "prototype(convert) encode(JSON)" }
external function f_enc_field_rec(in RecWithEncAttrInFields.rec x) return octetstring
with { extension "prototype(convert) encode(JSON)" }
external function f_enc_field_list(in RecWithEncAttrInFields.list x) return octetstring
with { extension "prototype(convert) encode(JSON)" }
external function f_enc_field_uni(in RecWithEncAttrInFields.uni x) return octetstring
with { extension "prototype(convert) encode(JSON)" }
external function f_enc_field_enum(in RecWithEncAttr.en x) return octetstring
with { extension "prototype(convert) encode(JSON)" }
external function f_enc_field_any(in RecWithEncAttr.at x) return octetstring
with { extension "prototype(convert) encode(JSON)" }
external function f_enc_elem_rec(in RecOfRec[-] x) return octetstring
with { extension "prototype(convert) encode(JSON)" }
external function f_enc_elem_list(in RecOfList[-] x) return octetstring
with { extension "prototype(convert) encode(JSON)" }
external function f_enc_elem_uni(in RecOfUni[-] x) return octetstring
with { extension "prototype(convert) encode(JSON)" }
external function f_enc_elem_enum(in RecOfEnum[-] x) return octetstring
with { extension "prototype(convert) encode(JSON)" }
external function f_enc_elem_any(in RecOfAny[-] x) return octetstring
with { extension "prototype(convert) encode(JSON)" }
external function f_enc_embedded_rec(in UniWithEmbEncAttr.rec.rec x) return octetstring
with { extension "prototype(convert) encode(JSON)" }
external function f_enc_embedded_list(in UniWithEmbEncAttr.rec.list x) return octetstring
with { extension "prototype(convert) encode(JSON)" }
external function f_enc_embedded_uni(in UniWithEmbEncAttr.uni_list[-] x) return octetstring
with { extension "prototype(convert) encode(JSON)" }
external function f_enc_embedded_enum(in UniWithEmbEncAttr.emb_list[-].en x) return octetstring
with { extension "prototype(convert) encode(JSON)" }
external function f_enc_embedded_any(in UniWithEmbEncAttr.emb_list[-].at_list[-] x) return octetstring
with { extension "prototype(convert) encode(JSON)" }
// for ASN.1 types
external function f_enc_seqofint(in SeqOfInt x) return octetstring
......@@ -268,6 +329,66 @@ external function f_dec_meta_setof(in octetstring x) return MetainfoSetOf
external function f_dec_meta_arr(in octetstring x) return MetainfoArray
with { extension "prototype(convert) decode(JSON)" }
external function f_dec_type_alias_rec(in octetstring x) return SupportedRec
with { extension "prototype(convert) decode(JSON)" }
external function f_dec_type_alias_list(in octetstring x) return SupportedList
with { extension "prototype(convert) decode(JSON)" }
external function f_dec_type_alias_uni(in octetstring x) return SupportedUni
with { extension "prototype(convert) decode(JSON)" }
external function f_dec_type_alias_enum(in octetstring x) return SupportedEnum
with { extension "prototype(convert) decode(JSON)" }
external function f_dec_type_alias_any(in octetstring x) return SupportedAny
with { extension "prototype(convert) decode(JSON)" }
external function f_dec_field_rec(in octetstring x) return RecWithEncAttrInFields.rec
with { extension "prototype(convert) decode(JSON)" }
external function f_dec_field_list(in octetstring x) return RecWithEncAttrInFields.list
with { extension "prototype(convert) decode(JSON)" }
external function f_dec_field_uni(in octetstring x) return RecWithEncAttrInFields.uni
with { extension "prototype(convert) decode(JSON)" }
external function f_dec_field_enum(in octetstring x) return RecWithEncAttr.en
with { extension "prototype(convert) decode(JSON)" }
external function f_dec_field_any(in octetstring x) return RecWithEncAttr.at
with { extension "prototype(convert) decode(JSON)" }
external function f_dec_elem_rec(in octetstring x) return RecOfRec[-]
with { extension "prototype(convert) decode(JSON)" }
external function f_dec_elem_list(in octetstring x) return RecOfList[-]
with { extension "prototype(convert) decode(JSON)" }
external function f_dec_elem_uni(in octetstring x) return RecOfUni[-]
with { extension "prototype(convert) decode(JSON)" }
external function f_dec_elem_enum(in octetstring x) return RecOfEnum[-]
with { extension "prototype(convert) decode(JSON)" }
external function f_dec_elem_any(in octetstring x) return RecOfAny[-]
with { extension "prototype(convert) decode(JSON)" }
external function f_dec_embedded_rec(in octetstring x) return UniWithEmbEncAttr.rec.rec
with { extension "prototype(convert) decode(JSON)" }
external function f_dec_embedded_list(in octetstring x) return UniWithEmbEncAttr.rec.list
with { extension "prototype(convert) decode(JSON)" }
external function f_dec_embedded_uni(in octetstring x) return UniWithEmbEncAttr.uni_list[-]
with { extension "prototype(convert) decode(JSON)" }
external function f_dec_embedded_enum(in octetstring x) return UniWithEmbEncAttr.emb_list[-].en
with { extension "prototype(convert) decode(JSON)" }
external function f_dec_embedded_any(in octetstring x) return UniWithEmbEncAttr.emb_list[-].at_list[-]
with { extension "prototype(convert) decode(JSON)" }
// for ASN.1 types
external function f_dec_seqofint(in octetstring x) return SeqOfInt
......
......@@ -19,7 +19,7 @@ include $(TOPDIR)/Makefile.regression
TTCN3_LIB = ttcn3$(RT2_SUFFIX)$(DYNAMIC_SUFFIX)
TTCN3_MODULES = Types.ttcn Functions.ttcn AttributeTestcases.ttcn Testcases.ttcn SemanticCheck.ttcn
TTCN3_MODULES = Types.ttcn Functions.ttcn AttributeTestcases.ttcn Testcases.ttcn SemanticCheck.ttcn OtherTypes.ttcn
ASN1_MODULES = JsonData.asn
......
/******************************************************************************
* Copyright (c) 2000-2017 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
*
******************************************************************************/
// more type definitions, but this time there's not 'encode' attribute at module-level
module OtherTypes {
// 'base' types that do not support JSON encoding
type record NotSuppRec {
integer x
}
type set of charstring NotSuppList;
type union NotSuppUni {
integer x
}
type enumerated NotSuppEnum { val1 };
// type aliases that give JSON encoding support to the 'base' types
type NotSuppRec SupportedRec with { encode "JSON" };
type NotSuppList SupportedList with { encode "JSON" };
type NotSuppUni SupportedUni with { encode "JSON" };
type NotSuppEnum SupportedEnum with { encode "JSON" };
type anytype SupportedAny with { encode "JSON" };
// types that use the 'base' types as fields/elements, and give them JSON encoding support
type record RecWithEncAttrInFields {
NotSuppRec rec,
NotSuppList list,
NotSuppUni uni
}
with {
encode (rec) "JSON";
encode (list) "JSON";
encode (uni) "JSON";
}
type record RecWithEncAttr {
NotSuppEnum en,
anytype at
}
with {
encode "JSON";
}
type record of NotSuppRec RecOfRec with { encode "JSON" };
type record of NotSuppList RecOfList with { encode "JSON" };
type record of NotSuppUni RecOfUni with { encode ([-]) "JSON" };
type record of NotSuppEnum RecOfEnum with { encode ([-]) "JSON" };
type record of anytype RecOfAny with { encode ([-]) "JSON" };
type union UniWithEmbEncAttr {
record {
NotSuppRec rec optional,
NotSuppList list optional
} rec,
record of NotSuppUni uni_list,
record of record {
SupportedEnum en optional,
record of anytype at_list optional
} emb_list
}
with {
encode (rec.rec) "JSON";
encode (rec.list) "JSON";
encode (uni_list[-]) "JSON";
encode (emb_list[-].en) "JSON";
encode (emb_list[-].at_list[-]) "JSON";
}
}
with {
extension "anytype integer, charstring"
}
......@@ -15,6 +15,7 @@ module Testcases {
import from Functions all;
import from Types all;
import from JsonData language "ASN.1" all;
import from OtherTypes all;
modulepar R tsp_r := {
i:= 1,
......@@ -632,6 +633,172 @@ testcase tc_binary_strings_with_invalid_chars() runs on MTC {
}
}
// Coding a type alias with JSON encoding, whose base type is a record with no JSON encoding support.
testcase tc_type_alias_record() runs on MTC {
var SupportedRec x := { x := 1 };
var octetstring os := char2oct("{\"x\":1}");
f_check_encoding(encoded:= f_enc_type_alias_rec(x), expected := os);
f_bool2verdict(match(f_dec_type_alias_rec(os), x));