diff --git a/compiler2/Type.cc b/compiler2/Type.cc index 6c9e9c57d99985cb08325830e8a3b36361697db1..1641dcb5d4aa6b36ddba535e82f643d2becfeae5 100644 --- a/compiler2/Type.cc +++ b/compiler2/Type.cc @@ -62,6 +62,10 @@ extern Ttcn::ExtensionAttributes * parse_extattributes( Ttcn::WithAttribPath *w_attrib_path); +// implemented in compiler.y +extern Common::Value* ttcn3_parse_json_default( + const char* p_str, const Common::Location& str_loc); + namespace Common { map<Type*, void> Type::RecursionTracker::types; @@ -3168,9 +3172,12 @@ namespace Common { } } - if (NULL != jsonattrib->default_value) { + if (jsonattrib->default_value.type == JsonAST::JD_STANDARD) { chk_json_default(); } + else if (jsonattrib->default_value.type == JsonAST::JD_LEGACY) { + chk_json_default_legacy(); + } const size_t nof_extensions = jsonattrib->schema_extensions.size(); if (0 != nof_extensions) { @@ -3389,9 +3396,33 @@ namespace Common { } } - void Type::chk_json_default() + void Type::chk_json_default() + { + if (jsonattrib->default_value.type != JsonAST::JD_STANDARD || jsonattrib->default_value.str == NULL || + jsonattrib->default_value.loc == NULL) { + FATAL_ERROR("Type::chk_json_default"); + } + Error_Context cntxt(this, "In JSON default value"); + Value* val = ttcn3_parse_json_default(jsonattrib->default_value.str, *jsonattrib->default_value.loc); + if (val != NULL) { + val->set_my_governor(this); + val->set_my_scope(my_scope); + val->set_fullname(get_fullname() + ".<JSON_default_value>"); + val->set_genname(get_genname_own() + "_json_defval"); + chk_this_value_ref(val); + chk_this_value(val, NULL, EXPECTED_CONSTANT, INCOMPLETE_NOT_ALLOWED, + OMIT_NOT_ALLOWED, SUB_CHK, NOT_IMPLICIT_OMIT); + jsonattrib->default_value.val = val; + } + } + + void Type::chk_json_default_legacy() { - const char* dval = jsonattrib->default_value; + if (jsonattrib->default_value.type != JsonAST::JD_LEGACY || jsonattrib->default_value.str == NULL) { + FATAL_ERROR("Type::chk_json_default"); + } + Error_Context cntxt(this, "In JSON default value (legacy)"); + const char* dval = jsonattrib->default_value.str; const size_t dval_len = strlen(dval); Type *last = get_type_refd_last(); bool err = false; diff --git a/compiler2/Type.hh b/compiler2/Type.hh index 28c22a85b9777c05df48dc88dcc4937e99233f64..37f2e3cac3a54b780e1a1a8fad082b69a0a3edaf 100644 --- a/compiler2/Type.hh +++ b/compiler2/Type.hh @@ -913,6 +913,7 @@ namespace Common { void chk_json(); void chk_json_default(); + void chk_json_default_legacy(); void chk_json_tag_list(); /** If the type does not have a jsonattrib, create one. */ void force_json(); diff --git a/compiler2/Type_codegen.cc b/compiler2/Type_codegen.cc index ea79ebc3eebd5e15c504b37cd49bfce1623f8090..9e3db1f1087d85d3458e77cb94475867f7afbbb1 100644 --- a/compiler2/Type_codegen.cc +++ b/compiler2/Type_codegen.cc @@ -1113,13 +1113,35 @@ void Type::generate_code_jsondescriptor(output_struct *target) if (NULL == jsonattrib) { target->source.global_vars = mputprintf(target->source.global_vars, - "const TTCN_JSONdescriptor_t %s_json_ = { FALSE, NULL, FALSE, NULL, " + "const TTCN_JSONdescriptor_t %s_json_ = { FALSE, NULL, FALSE, { JD_UNSET, NULL }, " "FALSE, FALSE, %s, 0, NULL, FALSE, ESCAPE_AS_SHORT };\n" , get_genname_own().c_str(), as_map ? "TRUE" : "FALSE"); } else { char* alias = jsonattrib->alias ? mputprintf(NULL, "\"%s\"", jsonattrib->alias) : NULL; - char* def_val = jsonattrib->default_value ? - mputprintf(NULL, "\"%s\"", jsonattrib->default_value) : NULL; + const char* def_type; + char* def_val = NULL; + switch (jsonattrib->default_value.type) { + case JsonAST::JD_UNSET: + def_type = "JD_UNSET"; + break; + case JsonAST::JD_LEGACY: + def_type = "JD_LEGACY"; + def_val = mprintf(".str = \"%s\"", jsonattrib->default_value.str); + break; + case JsonAST::JD_STANDARD: + def_type = "JD_STANDARD"; + Value* v = jsonattrib->default_value.val; + const_def cdef; + Code::init_cdef(&cdef); + generate_code_object(&cdef, v, false); + // Generate the initialization of the default values in the post init function + // because the module parameters are not initialized in the pre init function + target->functions.post_init = v->generate_code_init(target->functions.post_init, v->get_lhs_name().c_str()); + Code::merge_cdef(target, &cdef); + Code::free_cdef(&cdef); + def_val = mprintf(".val = &%s", v->get_lhs_name().c_str()); + break; + } char* enum_texts_name; if (0 != jsonattrib->enum_texts.size()) { @@ -1139,13 +1161,13 @@ void Type::generate_code_jsondescriptor(output_struct *target) } target->source.global_vars = mputprintf(target->source.global_vars, - "const TTCN_JSONdescriptor_t %s_json_ = { %s, %s, %s, %s, %s, %s, %s, " + "const TTCN_JSONdescriptor_t %s_json_ = { %s, %s, %s, { %s, %s }, %s, %s, %s, " "%d, %s, %s, %s };\n" , get_genname_own().c_str() , jsonattrib->omit_as_null ? "TRUE" : "FALSE" , alias ? alias : "NULL" , (jsonattrib->as_value || jsonattrib->tag_list != NULL) ? "TRUE" : "FALSE" - , def_val ? def_val : "NULL" + , def_type, def_val ? def_val : "NULL" , jsonattrib->metainfo_unbound ? "TRUE" : "FALSE" , jsonattrib->as_number ? "TRUE" : "FALSE" , as_map ? "TRUE" : "FALSE" @@ -1895,7 +1917,7 @@ void Type::generate_code_Se(output_struct *target) if (type->jsonattrib) { cur.jsonOmitAsNull = type->jsonattrib->omit_as_null; cur.jsonAlias = type->jsonattrib->alias; - cur.jsonDefaultValue = type->jsonattrib->default_value; + cur.jsonDefaultValue = type->jsonattrib->default_value.str; cur.jsonMetainfoUnbound = type->jsonattrib->metainfo_unbound; if (type->jsonattrib->tag_list != NULL) { rawAST_tag_list* tag_list = type->jsonattrib->tag_list; @@ -3811,18 +3833,18 @@ void Type::generate_json_schema(JSON_Tokenizer& json, bool embedded, bool as_val } // insert default value (if any) - if (jsonattrib != NULL && jsonattrib->default_value != NULL) { + if (jsonattrib != NULL && jsonattrib->default_value.str != NULL) { json.put_next_token(JSON_TOKEN_NAME, "default"); switch (last->typetype) { case T_BOOL: - json.put_next_token((jsonattrib->default_value[0] == 't') ? + json.put_next_token((jsonattrib->default_value.str[0] == 't') ? JSON_TOKEN_LITERAL_TRUE : JSON_TOKEN_LITERAL_FALSE); break; case T_INT: case T_REAL: - if (jsonattrib->default_value[0] != 'n' && jsonattrib->default_value[0] != 'i' - && jsonattrib->default_value[1] != 'i') { - json.put_next_token(JSON_TOKEN_NUMBER, jsonattrib->default_value); + if (jsonattrib->default_value.str[0] != 'n' && jsonattrib->default_value.str[0] != 'i' + && jsonattrib->default_value.str[1] != 'i') { + json.put_next_token(JSON_TOKEN_NUMBER, jsonattrib->default_value.str); break; } // no break, insert the special float values as strings @@ -3833,7 +3855,7 @@ void Type::generate_json_schema(JSON_Tokenizer& json, bool embedded, bool as_val case T_USTR: case T_VERDICT: case T_ENUM_T: { - char* default_str = mprintf("\"%s\"", jsonattrib->default_value); + char* default_str = mprintf("\"%s\"", jsonattrib->default_value.str); json.put_next_token(JSON_TOKEN_STRING, default_str); Free(default_str); break; } diff --git a/compiler2/enum.c b/compiler2/enum.c index 9a7de6d043b6ec55e1e88bf6f03cc137f123ca71..bfdbfc46c3d0e69c1f7febb42e1b45f085530d41 100644 --- a/compiler2/enum.c +++ b/compiler2/enum.c @@ -838,11 +838,16 @@ void defEnumClass(const enum_def *edef, output_struct *output) " size_t value_len = 0;\n" " boolean error = FALSE;\n" " size_t dec_len = 0;\n" - " boolean use_default = p_td.json->default_value && 0 == p_tok.get_buffer_length();\n" - " if (use_default) {\n" + " boolean use_default = FALSE;\n" + " if (p_td.json->default_value.type == JD_STANDARD && 0 == p_tok.get_buffer_length()) {\n" + " *this = *static_cast<const %s*>(p_td.json->default_value.val);\n" + " return dec_len;\n" + " }\n" + " if (p_td.json->default_value.type == JD_LEGACY && 0 == p_tok.get_buffer_length()) {\n" // No JSON data in the buffer -> use default value - " value = const_cast<char*>(p_td.json->default_value);\n" + " value = const_cast<char*>(p_td.json->default_value.str);\n" " value_len = strlen(value);\n" + " use_default = TRUE;\n" " } else {\n" " dec_len = p_tok.get_next_token(&token, &value, &value_len);\n" " }\n" @@ -902,7 +907,7 @@ void defEnumClass(const enum_def *edef, output_struct *output) " }\n" " return (int)dec_len;\n" "}\n\n" - , name, edef->elements[0].name, enum_type, unknown_value, enum_type + , name, name, edef->elements[0].name, enum_type, unknown_value, enum_type , unbound_value, unbound_value); } diff --git a/compiler2/record.c b/compiler2/record.c index 50c13528860b413253d74302b7e7cc48bd92e5bd..e544b845e01baa5352f60a4e14e752da6318af39 100644 --- a/compiler2/record.c +++ b/compiler2/record.c @@ -3266,10 +3266,13 @@ void gen_xer(const struct_def *sdef, char **pdef, char **psrc) char* generate_json_decoder(char* src, const struct_def* sdef) { src = mputprintf(src, - "int %s::JSON_decode(const TTCN_Typedescriptor_t&%s, JSON_Tokenizer& p_tok, " + "int %s::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, " "boolean p_silent, boolean p_parent_is_map, int)\n" - "{\n", sdef->name, - ((sdef->nElements == 1 && !sdef->jsonAsValue) || sdef->jsonAsMapPossible) ? " p_td" : ""); + "{\n" + " if (p_td.json->default_value.type == JD_STANDARD && 0 == p_tok.get_buffer_length()) {\n" + " *this = *static_cast<const %s*>(p_td.json->default_value.val);\n" + " return 0;\n" + " }\n", sdef->name, sdef->name); if (sdef->nElements == 1) { if (!sdef->jsonAsValue) { @@ -6897,10 +6900,14 @@ static void defEmptyRecordClass(const struct_def *sdef, src = mputprintf(src, "int %s::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, boolean p_silent, boolean, int)\n" "{\n" - " if (NULL != p_td.json->default_value && 0 == p_tok.get_buffer_length()) {\n" + " if (p_td.json->default_value.type == JD_STANDARD && 0 == p_tok.get_buffer_length()) {\n" + " *this = *static_cast<const %s*>(p_td.json->default_value.val);\n" + " return 0;\n" + " }\n" + " if (p_td.json->default_value.type != JD_UNSET && 0 == p_tok.get_buffer_length()) {\n" // use the default value " bound_flag = TRUE;\n" - " return strlen(p_td.json->default_value);\n" + " return strlen(p_td.json->default_value.str);\n" " }\n" " json_token_t token = JSON_TOKEN_NONE;\n" " size_t dec_len = p_tok.get_next_token(&token, NULL, NULL);\n" @@ -6919,7 +6926,7 @@ static void defEmptyRecordClass(const struct_def *sdef, " bound_flag = TRUE;\n\n" " return (int)dec_len;\n" "}\n\n" - , name); + , name, name); } if (oer_needed) { diff --git a/compiler2/record_of.c b/compiler2/record_of.c index d762b3eee7587fe5e5f3bf623fadec181fd25ee9..0acad137b6ccb8043f6328186791e7dc5efdfbe0 100644 --- a/compiler2/record_of.c +++ b/compiler2/record_of.c @@ -1579,11 +1579,15 @@ void defRecordOfClass1(const struct_of_def *sdef, output_struct *output) src = mputprintf(src, "int %s::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, boolean p_silent, boolean, int)\n" "{\n" - " if (NULL != p_td.json->default_value && 0 == p_tok.get_buffer_length()) {\n" + " if (p_td.json->default_value.type == JD_STANDARD && 0 == p_tok.get_buffer_length()) {\n" + " *this = *static_cast<const %s*>(p_td.json->default_value.val);\n" + " return 0;\n" + " }\n" + " if (p_td.json->default_value.type == JD_LEGACY && 0 == p_tok.get_buffer_length()) {\n" // use the default value (currently only the empty array can be set as // default value for this type) " set_size(0);\n" - " return strlen(p_td.json->default_value);\n" + " return strlen(p_td.json->default_value.str);\n" " }\n" " json_token_t token = JSON_TOKEN_NONE;\n" " size_t dec_len = p_tok.get_next_token(&token, NULL, NULL);\n" @@ -1653,7 +1657,7 @@ void defRecordOfClass1(const struct_of_def *sdef, output_struct *output) " }\n\n" " return (int)dec_len;\n" "}\n\n" - , name, type, type, type); + , name, name, type, type, type); } if (oer_needed) { // OER encode, RT1 @@ -3105,6 +3109,16 @@ void defRecordOfClassMemAllocOptimized(const struct_of_def *sdef, output_struct src = mputprintf(src, "int %s::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, boolean p_silent, boolean, int)\n" "{\n" + " if (p_td.json->default_value.type == JD_STANDARD && 0 == p_tok.get_buffer_length()) {\n" + " *this = *static_cast<const %s*>(p_td.json->default_value.val);\n" + " return 0;\n" + " }\n" + " if (p_td.json->default_value.type == JD_LEGACY && 0 == p_tok.get_buffer_length()) {\n" + // use the default value (currently only the empty array can be set as + // default value for this type) + " set_size(0);\n" + " return strlen(p_td.json->default_value.str);\n" + " }\n" " json_token_t token = JSON_TOKEN_NONE;\n" " size_t dec_len = p_tok.get_next_token(&token, NULL, NULL);\n" " if (JSON_TOKEN_ERROR == token) {\n" @@ -3167,7 +3181,7 @@ void defRecordOfClassMemAllocOptimized(const struct_of_def *sdef, output_struct " }\n\n" " return (int)dec_len;\n" "}\n\n" - , name, type); + , name, name, type); } if (oer_needed) { // OER encode, RT1, mem. alloc. optimised diff --git a/compiler2/ttcn3/JsonAST.cc b/compiler2/ttcn3/JsonAST.cc index af11c51746852eec8a78818c461946e440be3327..2cc49e4770865f2d4492b702fc20cc907dd86006 100644 --- a/compiler2/ttcn3/JsonAST.cc +++ b/compiler2/ttcn3/JsonAST.cc @@ -14,6 +14,7 @@ #include "../../common/memory.h" #include <cstddef> #include <cstdio> +#include "../Value.hh" void JsonSchemaExtension::init(char* p_key, char* p_value) { @@ -38,7 +39,10 @@ void JsonAST::init_JsonAST() omit_as_null = false; alias = NULL; as_value = false; - default_value = NULL; + default_value.type = JD_UNSET; + default_value.str = NULL; + default_value.val = NULL; + default_value.loc = NULL; metainfo_unbound = false; as_number = false; tag_list = NULL; @@ -55,7 +59,9 @@ JsonAST::JsonAST(const JsonAST *other_val) omit_as_null = other_val->omit_as_null; alias = (NULL != other_val->alias) ? mcopystr(other_val->alias) : NULL; as_value = other_val->as_value; - default_value = (NULL != other_val->default_value) ? mcopystr(other_val->default_value) : NULL; + default_value.type = other_val->default_value.type; + default_value.str = (NULL != other_val->default_value.str) ? mcopystr(other_val->default_value.str) : NULL; + default_value.val = (NULL != other_val->default_value.val) ? other_val->default_value.val->clone() : NULL; as_number = other_val->as_number; for (size_t i = 0; i < other_val->schema_extensions.size(); ++i) { schema_extensions.add(new JsonSchemaExtension(*other_val->schema_extensions[i])); @@ -75,7 +81,9 @@ JsonAST::JsonAST(const JsonAST *other_val) JsonAST::~JsonAST() { Free(alias); - Free(default_value); + Free(default_value.str); + delete default_value.val; + delete default_value.loc; for (size_t i = 0; i < schema_extensions.size(); ++i) { delete schema_extensions[i]; } @@ -93,7 +101,7 @@ JsonAST::~JsonAST() boolean JsonAST::empty() const { return omit_as_null == false && alias == NULL && as_value == false && - default_value == NULL && metainfo_unbound == false && as_number == false && + default_value.type == JD_UNSET && metainfo_unbound == false && as_number == false && tag_list == NULL && as_map == false && enum_texts.size() == 0 && use_null == false && type_indicator != JSON_OBJECT && type_indicator != JSON_LITERAL && @@ -167,8 +175,9 @@ void JsonAST::print_JsonAST() const if (as_value) { printf("Encoding unions as JSON value\n\r"); } - if (default_value) { - printf("Default value: %s\n\r", default_value); + if (default_value.type != JD_UNSET) { + printf("Default value%s: %s\n\r", default_value.type == JD_LEGACY ? " (legacy)" : "", + default_value.type == JD_LEGACY ? default_value.str : default_value.val->create_stringRepr().c_str()); } if (as_number) { printf("Encoding enumerated values as numbers\n\r"); diff --git a/compiler2/ttcn3/JsonAST.hh b/compiler2/ttcn3/JsonAST.hh index 800dcdf03828ed185535131892acb5736edcbec2..bfa4f40b68d8add432fdcaa9cdbb57eb6975593d 100644 --- a/compiler2/ttcn3/JsonAST.hh +++ b/compiler2/ttcn3/JsonAST.hh @@ -55,6 +55,11 @@ public: ESCAPE_AS_USI, ESCAPE_AS_TRANSPARENT }; + enum json_default_type { + JD_UNSET, // no default value set + JD_LEGACY, // legacy default value set through the 'JSON: default' variant attribute + JD_STANDARD // standard-compliant default value set through the 'default' variant attribute + }; private: void init_JsonAST(); JsonAST(const JsonAST&); @@ -63,7 +68,12 @@ public: boolean omit_as_null; char* alias; boolean as_value; - char* default_value; + struct { + json_default_type type; + char* str; + Common::Value* val; + Common::Location* loc; + } default_value; vector<JsonSchemaExtension> schema_extensions; boolean metainfo_unbound; boolean as_number; diff --git a/compiler2/ttcn3/compiler.l b/compiler2/ttcn3/compiler.l index 2f5a31e99cbc54759254fe305aa6a01d6ebcf8ca..baaf0108ecf14c82025a6e804ecf56bbe65101e6 100644 --- a/compiler2/ttcn3/compiler.l +++ b/compiler2/ttcn3/compiler.l @@ -197,6 +197,7 @@ LINEMARKER {NUMBER}{WHITESPACE}+\"([^\\\"\r\n]|\\[^\r\n])*\" UID [uU][+]?[0-9A-Fa-f]{1,8} TITAN "$#&&&(#TITANERRONEOUS$#&&^#% " +TITAN2 "$#&&&(#TITANJSONDEFAULT$#&&^#% " %x SC_blockcomment SC_cstring %x SC_binstring SC_binstring_bad @@ -246,6 +247,10 @@ TITAN "$#&&&(#TITANERRONEOUS$#&&^#% " } } +{TITAN2} { + RETURN(TitanJsonDefaultHackKeyword); +} + /* Eat up comments and whitespaces */ "/*" { diff --git a/compiler2/ttcn3/compiler.y b/compiler2/ttcn3/compiler.y index 42954a696f6cc14ff9b8733e55d55dbd4a302d73..3b5f2dba79a6e2fa450f688de0c74b012bc42d9f 100644 --- a/compiler2/ttcn3/compiler.y +++ b/compiler2/ttcn3/compiler.y @@ -76,11 +76,14 @@ const char *infile = NULL; static Ttcn::Module *act_ttcn3_module = NULL; static Ttcn::ErroneousAttributeSpec *act_ttcn3_erroneous_attr_spec = NULL; +static Common::Value* act_json_default = NULL; bool is_erroneous_parsed = false; +bool is_json_default_parsed = false; static void ttcn3_error(const char *str); static Group* act_group = NULL; extern string anytype_field(const string& type_name); static bool anytype_access = false; +static bool parsing_error = false; #ifndef NDEBUG @@ -649,6 +652,7 @@ static const string anyname("anytype"); * This magic requires the presence of the unused keywords. * (It can return an ApplyKeyword if not preceded by a dot) */ %token TitanErroneousHackKeyword +%token TitanJsonDefaultHackKeyword %token ActionKeyword %token ActivateKeyword %token AddressKeyword @@ -2082,11 +2086,12 @@ reduce in case of conflicts. GrammarRoot: TTCN3Module { - if (is_erroneous_parsed) { + if (is_erroneous_parsed || is_json_default_parsed) { delete act_ttcn3_module; act_ttcn3_module = NULL; Location loc(infile, @1); - loc.error("The erroneous attribute cannot be a TTCN-3 module."); + loc.error("The %s cannot be a TTCN-3 module.", + is_json_default_parsed ? "JSON default value" : "erroneous attribute"); } } | TitanErroneousHackKeyword ErroneousAttributeSpec @@ -2095,7 +2100,29 @@ GrammarRoot: delete act_ttcn3_erroneous_attr_spec; act_ttcn3_erroneous_attr_spec = NULL; Location loc(infile, @$); - loc.error("File `%s' does not contain a TTCN-3 module.", infile); + if (is_json_default_parsed) { + loc.error("JSON default value expected instead of erroneous attribute."); + } + else { + loc.error("File `%s' does not contain a TTCN-3 module.", infile); + } + } + } +| TitanJsonDefaultHackKeyword Expression + { + act_json_default = $2; + if (!is_json_default_parsed || parsing_error) { + delete act_json_default; + act_json_default = NULL; + if (!is_json_default_parsed) { + Location loc(infile, @2); + if (is_erroneous_parsed) { + loc.error("Erroneous attribute expected instead of JSON default value."); + } + else { + loc.error("File `%s' does not contain a TTCN-3 module.", infile); + } + } } } | error @@ -11431,6 +11458,7 @@ static void ttcn3_error(const char *str) // the most recently parsed token is unknown loc.error("%s", str); } + parsing_error = true; } int ttcn3_parse_file(const char* filename, boolean generate_code) @@ -11448,6 +11476,7 @@ int ttcn3_parse_file(const char* filename, boolean generate_code) init_ttcn3_lex(); is_erroneous_parsed = false; + is_json_default_parsed = false; NOTIFY("Parsing TTCN-3 module `%s'...", filename); int retval = ttcn3_parse(); @@ -11471,6 +11500,7 @@ Ttcn::ErroneousAttributeSpec* ttcn3_parse_erroneous_attr_spec_string( const char* p_str, const Common::Location& str_loc) { is_erroneous_parsed = true; + is_json_default_parsed = false; act_ttcn3_erroneous_attr_spec = NULL; string titan_err_str("$#&&&(#TITANERRONEOUS$#&&^#% "); size_t hack_str_len = titan_err_str.size(); @@ -11490,6 +11520,30 @@ Ttcn::ErroneousAttributeSpec* ttcn3_parse_erroneous_attr_spec_string( return act_ttcn3_erroneous_attr_spec; } +Common::Value* ttcn3_parse_json_default( + const char* p_str, const Common::Location& str_loc) +{ + is_erroneous_parsed = false; + is_json_default_parsed = true; + act_json_default = NULL; + string titan_err_str("$#&&&(#TITANJSONDEFAULT$#&&^#% "); + size_t hack_str_len = titan_err_str.size(); + string *parsed_string = parse_charstring_value(p_str, str_loc); + titan_err_str += *parsed_string; + delete parsed_string; + init_erroneous_lex(str_loc.get_filename(), str_loc.get_first_line(), str_loc.get_first_column()-hack_str_len+1); + yy_buffer_state *flex_buffer = ttcn3__scan_string(titan_err_str.c_str()); + if (flex_buffer == NULL) { + ERROR("Flex buffer creation failed."); + return NULL; + } + yyparse(); + ttcn3_lex_destroy(); + free_dot_flag_stuff(); + + return act_json_default; +} + #ifndef NDEBUG static void yyprint(FILE *file, int type, const YYSTYPE& value) { diff --git a/compiler2/ttcn3/rawAST.l b/compiler2/ttcn3/rawAST.l index 53aa0a9c70e909e8bdea41ab2b7238f8e95fff61..9626290b73a198e497d17f7983fb0e8fc9586494 100644 --- a/compiler2/ttcn3/rawAST.l +++ b/compiler2/ttcn3/rawAST.l @@ -491,7 +491,7 @@ literal RETURN(XKWliteral); else yylval.str = mcopystr(yytext); RETURN(XJsonValueSegment); } -[)] { BEGIN(jsoncodec); RETURN(XJsonValueEnd); } +[)] { BEGIN(INITIAL); RETURN(XJsonValueEnd); } [\"][\"] { yylval.str = mcopystr("\\\""); RETURN(XJsonValueSegment); } [^\"\\)]+ { yylval.str = mcopystr(yytext); RETURN(XJsonValueSegment); } } diff --git a/compiler2/ttcn3/rawAST.y b/compiler2/ttcn3/rawAST.y index 9a6f94f67391142a9944efb1a03eaa69fc0e4cac..be97c03aac40a706d21abf72cda4e67ac83affb7 100644 --- a/compiler2/ttcn3/rawAST.y +++ b/compiler2/ttcn3/rawAST.y @@ -1860,7 +1860,8 @@ XAsValue: ; XDefault: - XKWdefault XOptSpaces XJsonValue { jsonstruct->default_value = $3; } + XKWdefault XOptSpaces XJsonValue + { jsonstruct->default_value.str = $3; jsonstruct->default_value.type = JsonAST::JD_LEGACY; } ; XExtend: @@ -1938,7 +1939,12 @@ JAsValue: ; JDefault: - XKWdefault XOptSpaces XJsonValue XOptSpaces { jsonstruct->default_value = $3; } + XKWdefault XOptSpaces XJsonValue XOptSpaces + { + jsonstruct->default_value.str = $3; + jsonstruct->default_value.type = JsonAST::JD_STANDARD; + jsonstruct->default_value.loc = new Common::Location(infile, @3); + } ; JExtend: diff --git a/compiler2/union.c b/compiler2/union.c index dfcc4230ad2ef3983806ffcf32b14d8c6b39b0c6..253a99ff524fe2774e44360a44789d58e32dbf98 100644 --- a/compiler2/union.c +++ b/compiler2/union.c @@ -2195,10 +2195,10 @@ void defUnionClass(struct_def const *sdef, output_struct *output) // JSON decode src = mputprintf(src, - "int %s::JSON_decode(const TTCN_Typedescriptor_t&%s, JSON_Tokenizer& p_tok, " + "int %s::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, " "boolean p_silent, boolean, int p_chosen_field)\n" "{\n" - , name, sdef->nElements > 0 && !sdef->jsonAsValue ? " p_td" : ""); + , name); if (sdef->nElements > 0) { src = mputprintf(src, " if (0 <= p_chosen_field && %d > p_chosen_field) {\n" @@ -2211,10 +2211,14 @@ void defUnionClass(struct_def const *sdef, output_struct *output) , (int)i, at_field, sdef->elements[i].name , sdef->elements[i].typedescrname); } - src = mputstr(src, + src = mputprintf(src, " }\n" " }\n" - " json_token_t j_token = JSON_TOKEN_NONE;\n"); + " if (p_td.json->default_value.type == JD_STANDARD && 0 == p_tok.get_buffer_length()) {\n" + " *this = *static_cast<const %s*>(p_td.json->default_value.val);\n" + " return 0;\n" + " }\n" + " json_token_t j_token = JSON_TOKEN_NONE;\n", name); if (!sdef->jsonAsValue) { src = mputstr(src, " if (p_td.json->as_value) {\n"); diff --git a/core/Bitstring.cc b/core/Bitstring.cc index 28e6d0b84e1b8eafea2e7ec8cd33e26f85d8b3ab..1742382f8265da4e49ad46d59c512cf5f4d86ee0 100644 --- a/core/Bitstring.cc +++ b/core/Bitstring.cc @@ -1204,11 +1204,16 @@ int BITSTRING::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_ size_t value_len = 0; boolean error = FALSE; size_t dec_len = 0; - boolean use_default = p_td.json->default_value && 0 == p_tok.get_buffer_length(); - if (use_default) { + boolean use_default = FALSE; + if (p_td.json->default_value.type == JD_STANDARD && 0 == p_tok.get_buffer_length()) { + *this = *static_cast<const BITSTRING*>(p_td.json->default_value.val); + return dec_len; + } + if (p_td.json->default_value.type == JD_LEGACY && 0 == p_tok.get_buffer_length()) { // No JSON data in the buffer -> use default value - value = const_cast<char*>(p_td.json->default_value); + value = const_cast<char*>(p_td.json->default_value.str); value_len = strlen(value); + use_default = TRUE; } else { dec_len = p_tok.get_next_token(&token, &value, &value_len); } diff --git a/core/Boolean.cc b/core/Boolean.cc index 5000c616d556f686310cd63fb02349680833631b..cdae579c4e7aeee8c1096738f8f7ee02f8a07ff5 100644 --- a/core/Boolean.cc +++ b/core/Boolean.cc @@ -770,9 +770,13 @@ int BOOLEAN::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_to { json_token_t token = JSON_TOKEN_NONE; size_t dec_len = 0; - if (p_td.json->default_value && 0 == p_tok.get_buffer_length()) { + if (p_td.json->default_value.type == JD_STANDARD && 0 == p_tok.get_buffer_length()) { + *this = *static_cast<const BOOLEAN*>(p_td.json->default_value.val); + return dec_len; + } + if (p_td.json->default_value.type == JD_LEGACY && 0 == p_tok.get_buffer_length()) { // No JSON data in the buffer -> use default value - if (strcmp(p_td.json->default_value, "true") == 0) { + if (strcmp(p_td.json->default_value.str, "true") == 0) { token = JSON_TOKEN_LITERAL_TRUE; } else { diff --git a/core/Charstring.cc b/core/Charstring.cc index 6efe3f955f6c9b313b0df4ddca3c2e7b77cba632..a59e253b826bdc767bb7e5263c0a3589b9443d64 100644 --- a/core/Charstring.cc +++ b/core/Charstring.cc @@ -1836,11 +1836,16 @@ int CHARSTRING::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p char* value = 0; size_t value_len = 0; size_t dec_len = 0; - boolean use_default = p_td.json->default_value && 0 == p_tok.get_buffer_length(); - if (use_default) { + boolean use_default = FALSE; + if (p_td.json->default_value.type == JD_STANDARD && 0 == p_tok.get_buffer_length()) { + *this = *static_cast<const CHARSTRING*>(p_td.json->default_value.val); + return dec_len; + } + if (p_td.json->default_value.type == JD_LEGACY && 0 == p_tok.get_buffer_length()) { // No JSON data in the buffer -> use default value - value = const_cast<char*>(p_td.json->default_value); + value = const_cast<char*>(p_td.json->default_value.str); value_len = strlen(value); + use_default = TRUE; } else { dec_len = p_tok.get_next_token(&token, &value, &value_len); } diff --git a/core/Float.cc b/core/Float.cc index 8b48d90f851508c0c4fddc8ada01582a6568d324..5546d1fc07746d7cf717eb58157314470abcb4d6 100644 --- a/core/Float.cc +++ b/core/Float.cc @@ -1135,11 +1135,16 @@ int FLOAT::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, char* value = 0; size_t value_len = 0; size_t dec_len = 0; - boolean use_default = p_td.json->default_value && 0 == p_tok.get_buffer_length(); - if (use_default) { + boolean use_default = FALSE; + if (p_td.json->default_value.type == JD_STANDARD && 0 == p_tok.get_buffer_length()) { + *this = *static_cast<const FLOAT*>(p_td.json->default_value.val); + return dec_len; + } + if (p_td.json->default_value.type == JD_LEGACY && 0 == p_tok.get_buffer_length()) { // No JSON data in the buffer -> use default value - value = const_cast<char*>(p_td.json->default_value); + value = const_cast<char*>(p_td.json->default_value.str); value_len = strlen(value); + use_default = TRUE; } else { dec_len = p_tok.get_next_token(&token, &value, &value_len); } diff --git a/core/Hexstring.cc b/core/Hexstring.cc index 35799bc1447f3ca1fff2a42ce743223393c6a0af..a4a25d9614db19cff2ea0dc36591284c490df796 100644 --- a/core/Hexstring.cc +++ b/core/Hexstring.cc @@ -1082,11 +1082,16 @@ int HEXSTRING::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_ size_t value_len = 0; boolean error = FALSE; size_t dec_len = 0; - boolean use_default = p_td.json->default_value && 0 == p_tok.get_buffer_length(); - if (use_default) { + boolean use_default = FALSE; + if (p_td.json->default_value.type == JD_STANDARD && 0 == p_tok.get_buffer_length()) { + *this = *static_cast<const HEXSTRING*>(p_td.json->default_value.val); + return dec_len; + } + if (p_td.json->default_value.type == JD_LEGACY && 0 == p_tok.get_buffer_length()) { // No JSON data in the buffer -> use default value - value = const_cast<char*>(p_td.json->default_value); + value = const_cast<char*>(p_td.json->default_value.str); value_len = strlen(value); + use_default = TRUE; } else { dec_len = p_tok.get_next_token(&token, &value, &value_len); } diff --git a/core/Integer.cc b/core/Integer.cc index 1f8d774179042f099750a2363cee7b0dc8f83210..18064b080894313b762279f45f881a01347ea7ad 100644 --- a/core/Integer.cc +++ b/core/Integer.cc @@ -1759,11 +1759,16 @@ int INTEGER::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_to char* value = 0; size_t value_len = 0; size_t dec_len = 0; - boolean use_default = p_td.json->default_value && 0 == p_tok.get_buffer_length(); - if (use_default) { + boolean use_default = FALSE; + if (p_td.json->default_value.type == JD_STANDARD && 0 == p_tok.get_buffer_length()) { + *this = *static_cast<const INTEGER*>(p_td.json->default_value.val); + return dec_len; + } + if (p_td.json->default_value.type == JD_LEGACY && 0 == p_tok.get_buffer_length()) { // No JSON data in the buffer -> use default value - value = const_cast<char*>(p_td.json->default_value); + value = const_cast<char*>(p_td.json->default_value.str); value_len = strlen(value); + use_default = TRUE; } else { dec_len = p_tok.get_next_token(&token, &value, &value_len); } diff --git a/core/JSON.cc b/core/JSON.cc index 835877a3d7a71a2cb002cb4e4faeddbaf2b1a702..e1f285259835a195218dfb36fbfe3b6b192eea12 100644 --- a/core/JSON.cc +++ b/core/JSON.cc @@ -32,55 +32,55 @@ #include <sys/types.h> // JSON descriptors for base types -const TTCN_JSONdescriptor_t INTEGER_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE, 0, NULL, FALSE, ESCAPE_AS_SHORT }; +const TTCN_JSONdescriptor_t INTEGER_json_ = { FALSE, NULL, FALSE, { JD_UNSET, NULL }, FALSE, FALSE, FALSE, 0, NULL, FALSE, ESCAPE_AS_SHORT }; -const TTCN_JSONdescriptor_t FLOAT_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE, 0, NULL, FALSE, ESCAPE_AS_SHORT }; +const TTCN_JSONdescriptor_t FLOAT_json_ = { FALSE, NULL, FALSE, { JD_UNSET, NULL }, FALSE, FALSE, FALSE, 0, NULL, FALSE, ESCAPE_AS_SHORT }; -const TTCN_JSONdescriptor_t BOOLEAN_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE, 0, NULL, FALSE, ESCAPE_AS_SHORT }; +const TTCN_JSONdescriptor_t BOOLEAN_json_ = { FALSE, NULL, FALSE, { JD_UNSET, NULL }, FALSE, FALSE, FALSE, 0, NULL, FALSE, ESCAPE_AS_SHORT }; -const TTCN_JSONdescriptor_t BITSTRING_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE, 0, NULL, FALSE, ESCAPE_AS_SHORT }; +const TTCN_JSONdescriptor_t BITSTRING_json_ = { FALSE, NULL, FALSE, { JD_UNSET, NULL }, FALSE, FALSE, FALSE, 0, NULL, FALSE, ESCAPE_AS_SHORT }; -const TTCN_JSONdescriptor_t HEXSTRING_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE, 0, NULL, FALSE, ESCAPE_AS_SHORT }; +const TTCN_JSONdescriptor_t HEXSTRING_json_ = { FALSE, NULL, FALSE, { JD_UNSET, NULL }, FALSE, FALSE, FALSE, 0, NULL, FALSE, ESCAPE_AS_SHORT }; -const TTCN_JSONdescriptor_t OCTETSTRING_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE, 0, NULL, FALSE, ESCAPE_AS_SHORT }; +const TTCN_JSONdescriptor_t OCTETSTRING_json_ = { FALSE, NULL, FALSE, { JD_UNSET, NULL }, FALSE, FALSE, FALSE, 0, NULL, FALSE, ESCAPE_AS_SHORT }; -const TTCN_JSONdescriptor_t CHARSTRING_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE, 0, NULL, FALSE, ESCAPE_AS_SHORT }; +const TTCN_JSONdescriptor_t CHARSTRING_json_ = { FALSE, NULL, FALSE, { JD_UNSET, NULL }, FALSE, FALSE, FALSE, 0, NULL, FALSE, ESCAPE_AS_SHORT }; -const TTCN_JSONdescriptor_t UNIVERSAL_CHARSTRING_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE, 0, NULL, FALSE, ESCAPE_AS_SHORT }; +const TTCN_JSONdescriptor_t UNIVERSAL_CHARSTRING_json_ = { FALSE, NULL, FALSE, { JD_UNSET, NULL }, FALSE, FALSE, FALSE, 0, NULL, FALSE, ESCAPE_AS_SHORT }; -const TTCN_JSONdescriptor_t VERDICTTYPE_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE, 0, NULL, FALSE, ESCAPE_AS_SHORT }; +const TTCN_JSONdescriptor_t VERDICTTYPE_json_ = { FALSE, NULL, FALSE, { JD_UNSET, NULL }, FALSE, FALSE, FALSE, 0, NULL, FALSE, ESCAPE_AS_SHORT }; -const TTCN_JSONdescriptor_t GeneralString_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE, 0, NULL, FALSE, ESCAPE_AS_SHORT }; +const TTCN_JSONdescriptor_t GeneralString_json_ = { FALSE, NULL, FALSE, { JD_UNSET, NULL }, FALSE, FALSE, FALSE, 0, NULL, FALSE, ESCAPE_AS_SHORT }; -const TTCN_JSONdescriptor_t NumericString_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE, 0, NULL, FALSE, ESCAPE_AS_SHORT }; +const TTCN_JSONdescriptor_t NumericString_json_ = { FALSE, NULL, FALSE, { JD_UNSET, NULL }, FALSE, FALSE, FALSE, 0, NULL, FALSE, ESCAPE_AS_SHORT }; -const TTCN_JSONdescriptor_t UTF8String_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE, 0, NULL, FALSE, ESCAPE_AS_SHORT }; +const TTCN_JSONdescriptor_t UTF8String_json_ = { FALSE, NULL, FALSE, { JD_UNSET, NULL }, FALSE, FALSE, FALSE, 0, NULL, FALSE, ESCAPE_AS_SHORT }; -const TTCN_JSONdescriptor_t PrintableString_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE, 0, NULL, FALSE, ESCAPE_AS_SHORT }; +const TTCN_JSONdescriptor_t PrintableString_json_ = { FALSE, NULL, FALSE, { JD_UNSET, NULL }, FALSE, FALSE, FALSE, 0, NULL, FALSE, ESCAPE_AS_SHORT }; -const TTCN_JSONdescriptor_t UniversalString_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE, 0, NULL, FALSE, ESCAPE_AS_SHORT }; +const TTCN_JSONdescriptor_t UniversalString_json_ = { FALSE, NULL, FALSE, { JD_UNSET, NULL }, FALSE, FALSE, FALSE, 0, NULL, FALSE, ESCAPE_AS_SHORT }; -const TTCN_JSONdescriptor_t BMPString_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE, 0, NULL, FALSE, ESCAPE_AS_SHORT }; +const TTCN_JSONdescriptor_t BMPString_json_ = { FALSE, NULL, FALSE, { JD_UNSET, NULL }, FALSE, FALSE, FALSE, 0, NULL, FALSE, ESCAPE_AS_SHORT }; -const TTCN_JSONdescriptor_t GraphicString_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE, 0, NULL, FALSE, ESCAPE_AS_SHORT }; +const TTCN_JSONdescriptor_t GraphicString_json_ = { FALSE, NULL, FALSE, { JD_UNSET, NULL }, FALSE, FALSE, FALSE, 0, NULL, FALSE, ESCAPE_AS_SHORT }; -const TTCN_JSONdescriptor_t IA5String_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE, 0, NULL, FALSE, ESCAPE_AS_SHORT }; +const TTCN_JSONdescriptor_t IA5String_json_ = { FALSE, NULL, FALSE, { JD_UNSET, NULL }, FALSE, FALSE, FALSE, 0, NULL, FALSE, ESCAPE_AS_SHORT }; -const TTCN_JSONdescriptor_t TeletexString_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE, 0, NULL, FALSE, ESCAPE_AS_SHORT }; +const TTCN_JSONdescriptor_t TeletexString_json_ = { FALSE, NULL, FALSE, { JD_UNSET, NULL }, FALSE, FALSE, FALSE, 0, NULL, FALSE, ESCAPE_AS_SHORT }; -const TTCN_JSONdescriptor_t VideotexString_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE, 0, NULL, FALSE, ESCAPE_AS_SHORT }; +const TTCN_JSONdescriptor_t VideotexString_json_ = { FALSE, NULL, FALSE, { JD_UNSET, NULL }, FALSE, FALSE, FALSE, 0, NULL, FALSE, ESCAPE_AS_SHORT }; -const TTCN_JSONdescriptor_t VisibleString_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE, 0, NULL, FALSE, ESCAPE_AS_SHORT }; +const TTCN_JSONdescriptor_t VisibleString_json_ = { FALSE, NULL, FALSE, { JD_UNSET, NULL }, FALSE, FALSE, FALSE, 0, NULL, FALSE, ESCAPE_AS_SHORT }; -const TTCN_JSONdescriptor_t ASN_NULL_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE, 0, NULL, FALSE, ESCAPE_AS_SHORT }; +const TTCN_JSONdescriptor_t ASN_NULL_json_ = { FALSE, NULL, FALSE, { JD_UNSET, NULL }, FALSE, FALSE, FALSE, 0, NULL, FALSE, ESCAPE_AS_SHORT }; -const TTCN_JSONdescriptor_t OBJID_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE, 0, NULL, FALSE, ESCAPE_AS_SHORT }; +const TTCN_JSONdescriptor_t OBJID_json_ = { FALSE, NULL, FALSE, { JD_UNSET, NULL }, FALSE, FALSE, FALSE, 0, NULL, FALSE, ESCAPE_AS_SHORT }; -const TTCN_JSONdescriptor_t ASN_ROID_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE, 0, NULL, FALSE, ESCAPE_AS_SHORT }; +const TTCN_JSONdescriptor_t ASN_ROID_json_ = { FALSE, NULL, FALSE, { JD_UNSET, NULL }, FALSE, FALSE, FALSE, 0, NULL, FALSE, ESCAPE_AS_SHORT }; -const TTCN_JSONdescriptor_t ASN_ANY_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE, 0, NULL, FALSE, ESCAPE_AS_SHORT }; +const TTCN_JSONdescriptor_t ASN_ANY_json_ = { FALSE, NULL, FALSE, { JD_UNSET, NULL }, FALSE, FALSE, FALSE, 0, NULL, FALSE, ESCAPE_AS_SHORT }; -const TTCN_JSONdescriptor_t ENUMERATED_json_ = { FALSE, NULL, FALSE, NULL, FALSE, FALSE, FALSE, 0, NULL, FALSE, ESCAPE_AS_SHORT }; +const TTCN_JSONdescriptor_t ENUMERATED_json_ = { FALSE, NULL, FALSE, { JD_UNSET, NULL }, FALSE, FALSE, FALSE, 0, NULL, FALSE, ESCAPE_AS_SHORT }; @@ -1515,4 +1515,4 @@ void bson2json_coding(TTCN_Buffer& buff, JSON_Tokenizer& tok, bool in_object, bo TTCN_error("Unexpected type %i while decoding using bson2json().", *type); } } -} \ No newline at end of file +} diff --git a/core/JSON.hh b/core/JSON.hh index 0bc1baba53a2fd5b1f369c58638659c422c7363c..4e26c178ab176d9617a76ba0603d5a01e8e76fa6 100644 --- a/core/JSON.hh +++ b/core/JSON.hh @@ -20,6 +20,7 @@ class TTCN_Buffer; class JSON_Tokenizer; class CHARSTRING; class INTEGER; +class Base_Type; /** Enumerated text change structure */ struct JsonEnumText { @@ -34,6 +35,12 @@ enum json_string_escaping { ESCAPE_AS_TRANSPARENT /* do not escape anything, except control characters */ }; +enum json_default_type { + JD_UNSET, // no default value set + JD_LEGACY, // legacy default value set through the 'JSON: default' variant attribute + JD_STANDARD // standard-compliant default value set through the 'default' variant attribute +}; + /** Descriptor for JSON encoding/decoding during runtime */ struct TTCN_JSONdescriptor_t { @@ -58,8 +65,14 @@ struct TTCN_JSONdescriptor_t boolean as_value; /** Decoding only. - * Fields that don't appear in the JSON code will decode this value instead. */ - const char* default_value; + * Fields may have a default value set in case they don't appear in the JSON code. */ + struct { + json_default_type type; /// indicates whether this field has a default value, and which type + union { + const char* str; /// legacy default value - contains a JSON value, it is decoded and assigned to this field + const Base_Type* val; /// standard-compliant default value - it is assigned to the field, no decoding needed + }; + } default_value; /** If set, encodes unbound fields of records and sets as null and inserts a * meta info field into the JSON object specifying that the field is unbound. diff --git a/core/Objid.cc b/core/Objid.cc index dff4ad30f7714ffdf7bb6c12a35fef593a9b63a8..83947933a273093980f5d200ce9f702f1ab69488 100644 --- a/core/Objid.cc +++ b/core/Objid.cc @@ -638,11 +638,16 @@ int OBJID::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, size_t value_len = 0; boolean error = FALSE; size_t dec_len = 0; - boolean use_default = p_td.json->default_value && 0 == p_tok.get_buffer_length(); - if (use_default) { + boolean use_default = FALSE; + if (p_td.json->default_value.type == JD_STANDARD && 0 == p_tok.get_buffer_length()) { + *this = *static_cast<const OBJID*>(p_td.json->default_value.val); + return dec_len; + } + if (p_td.json->default_value.type == JD_LEGACY && 0 == p_tok.get_buffer_length()) { // No JSON data in the buffer -> use default value - value = const_cast<char*>(p_td.json->default_value); + value = const_cast<char*>(p_td.json->default_value.str); value_len = strlen(value); + use_default = TRUE; } else { dec_len = p_tok.get_next_token(&token, &value, &value_len); } diff --git a/core/Octetstring.cc b/core/Octetstring.cc index f4ef7e14296cb18a63de998896e8af99dc338874..59f1e54a268e83f5aa1b3768a84193c9451225bf 100644 --- a/core/Octetstring.cc +++ b/core/Octetstring.cc @@ -1336,11 +1336,16 @@ int OCTETSTRING::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& size_t value_len = 0; boolean error = FALSE; size_t dec_len = 0; - boolean use_default = p_td.json->default_value && 0 == p_tok.get_buffer_length(); - if (use_default) { + boolean use_default = FALSE; + if (p_td.json->default_value.type == JD_STANDARD && 0 == p_tok.get_buffer_length()) { + *this = *static_cast<const OCTETSTRING*>(p_td.json->default_value.val); + return dec_len; + } + if (p_td.json->default_value.type == JD_LEGACY && 0 == p_tok.get_buffer_length()) { // No JSON data in the buffer -> use default value - value = const_cast<char*>(p_td.json->default_value); + value = const_cast<char*>(p_td.json->default_value.str); value_len = strlen(value); + use_default = TRUE; } else { dec_len = p_tok.get_next_token(&token, &value, &value_len); } diff --git a/core/Universal_charstring.cc b/core/Universal_charstring.cc index 2d64f115008351c82192e9039431b241ee415a08..cff22da750ba4b38e08be62b7f076887107b094b 100644 --- a/core/Universal_charstring.cc +++ b/core/Universal_charstring.cc @@ -2596,11 +2596,16 @@ int UNIVERSAL_CHARSTRING::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_To char* value = 0; size_t value_len = 0; size_t dec_len = 0; - boolean use_default = p_td.json->default_value && 0 == p_tok.get_buffer_length(); - if (use_default) { + boolean use_default = FALSE; + if (p_td.json->default_value.type == JD_STANDARD && 0 == p_tok.get_buffer_length()) { + *this = *static_cast<const UNIVERSAL_CHARSTRING*>(p_td.json->default_value.val); + return dec_len; + } + if (p_td.json->default_value.type == JD_LEGACY && 0 == p_tok.get_buffer_length()) { // No JSON data in the buffer -> use default value - value = const_cast<char*>(p_td.json->default_value); + value = const_cast<char*>(p_td.json->default_value.str); value_len = strlen(value); + use_default = TRUE; } else { dec_len = p_tok.get_next_token(&token, &value, &value_len); } diff --git a/core/Verdicttype.cc b/core/Verdicttype.cc index 6524f39752279a9f0ceaaed83f822e00e690533f..b7f5623ce2608edb4cd031f2f3b4eb55ca9b6b5c 100644 --- a/core/Verdicttype.cc +++ b/core/Verdicttype.cc @@ -396,11 +396,16 @@ int VERDICTTYPE::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& char* value = 0; size_t value_len = 0; size_t dec_len = 0; - boolean use_default = p_td.json->default_value && 0 == p_tok.get_buffer_length(); - if (use_default) { + boolean use_default = FALSE; + if (p_td.json->default_value.type == JD_STANDARD && 0 == p_tok.get_buffer_length()) { + *this = *static_cast<const VERDICTTYPE*>(p_td.json->default_value.val); + return dec_len; + } + if (p_td.json->default_value.type == JD_LEGACY && 0 == p_tok.get_buffer_length()) { // No JSON data in the buffer -> use default value - value = const_cast<char*>(p_td.json->default_value); + value = const_cast<char*>(p_td.json->default_value.str); value_len = strlen(value); + use_default = TRUE; } else { dec_len = p_tok.get_next_token(&token, &value, &value_len); } diff --git a/core2/Basetype2.cc b/core2/Basetype2.cc index 48c5ff62150614a9a21c0a91099707c5343da9ed..c80ad6051c76b813ca8eff09dcb9cc030377aeb5 100644 --- a/core2/Basetype2.cc +++ b/core2/Basetype2.cc @@ -1603,11 +1603,15 @@ int Record_Of_Type::JSON_encode_negtest(const Erroneous_descriptor_t* p_err_desc int Record_Of_Type::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, boolean p_silent, boolean, int) { - if (NULL != p_td.json->default_value && 0 == p_tok.get_buffer_length()) { + if (p_td.json->default_value.type == JD_STANDARD && 0 == p_tok.get_buffer_length()) { + set_value(p_td.json->default_value.val); + return 0; + } + if (p_td.json->default_value.type == JD_LEGACY && 0 == p_tok.get_buffer_length()) { // use the default value (currently only the empty array can be set as // default value for this type) set_size(0); - return strlen(p_td.json->default_value); + return strlen(p_td.json->default_value.str); } json_token_t token = JSON_TOKEN_NONE; size_t dec_len = p_tok.get_next_token(&token, NULL, NULL); @@ -6191,6 +6195,10 @@ int Record_Type::JSON_encode_negtest(const Erroneous_descriptor_t* p_err_descr, int Record_Type::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, boolean p_silent, boolean p_parent_is_map, int) { + if (p_td.json->default_value.type == JD_STANDARD && 0 == p_tok.get_buffer_length()) { + set_value(p_td.json->default_value.val); + return 0; + } if (p_td.json->as_value) { if (get_at(0)->is_optional()) { // can only happen if the record has the 'JSON:object' attribute; @@ -6391,7 +6399,7 @@ int Record_Type::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& (int)strlen(fld_name(field_idx)), fld_name(field_idx)); } else if (!field_found[field_idx]) { - if (NULL != fld_descr(field_idx)->json && NULL != fld_descr(field_idx)->json->default_value) { + if (NULL != fld_descr(field_idx)->json && fld_descr(field_idx)->json->default_value.type != JD_UNSET) { get_at(field_idx)->JSON_decode(*fld_descr(field_idx), DUMMY_BUFFER, p_silent, FALSE); } else if (field->is_optional()) { @@ -7580,10 +7588,14 @@ int Empty_Record_Type::JSON_encode(const TTCN_Typedescriptor_t&, JSON_Tokenizer& int Empty_Record_Type::JSON_decode(const TTCN_Typedescriptor_t& p_td, JSON_Tokenizer& p_tok, boolean p_silent, boolean, int) { - if (NULL != p_td.json->default_value && 0 == p_tok.get_buffer_length()) { + if (p_td.json->default_value.type == JD_STANDARD && 0 == p_tok.get_buffer_length()) { + set_value(p_td.json->default_value.val); + return 0; + } + if (p_td.json->default_value.type == JD_LEGACY && 0 == p_tok.get_buffer_length()) { // use the default value bound_flag = TRUE; - return strlen(p_td.json->default_value); + return strlen(p_td.json->default_value.str); } json_token_t token = JSON_TOKEN_NONE; size_t dec_len = p_tok.get_next_token(&token, NULL, NULL); diff --git a/regression_test/EncodeDecode/JSON/JsonComplexTest/AttributeTestcases.ttcn b/regression_test/EncodeDecode/JSON/JsonComplexTest/AttributeTestcases.ttcn index 26f3718e21c197a12785b43e40b4a5ee960187b7..f7f263a0ca2cdd899b4163894e6623f896f199dc 100644 --- a/regression_test/EncodeDecode/JSON/JsonComplexTest/AttributeTestcases.ttcn +++ b/regression_test/EncodeDecode/JSON/JsonComplexTest/AttributeTestcases.ttcn @@ -185,8 +185,8 @@ testcase tc_attribute_as_value() runs on CT { setverdict(pass); } -// Testing default values for record fields (decoding only) -testcase tc_attribute_default() runs on CT { +// Testing legacy default values for record fields (decoding only) +testcase tc_attribute_default_legacy() runs on CT { var octetstring os := char2oct("{}"); var RecDef d := { i := -19, f := 1000000.000000, b := false, bs := '101'B, hs := 'DEAD'H, os := '1DE7'O, cs := "empty", ucs := "üres", size := Tiny, vt := fail }; f_bool2verdict( match(f_dec_def(os), d) ); @@ -196,6 +196,43 @@ testcase tc_attribute_default() runs on CT { f_bool2verdict( match(f_dec_def(os), d) ); } +// Testing standard-compliant default values for record fields of basic types (decoding only) +testcase tc_attribute_default_basic() runs on CT { + var octetstring os := char2oct("{}"); + var RecDef2 d := { i := -19, f := 1000000.000000, b := false, bs := '101'B, hs := 'DEAD'H, os := '1DE7'O, cs := "empty", ucs := "üres", size := Tiny, vt := fail }; + f_bool2verdict( match(f_dec_def2(os), d) ); + + os := char2oct("{ \"b\" : null }"); + d.b := omit; + f_bool2verdict( match(f_dec_def2(os), d) ); +} + +// Tests for standard-compliant default values taken from the TTCN-3 standard change request CR 7968 v5 (decoding only) +testcase tc_attribute_default_standard() runs on CT { + var octetstring os := char2oct("{ \"name\" : \"Shoe\", \"price\" : 29.50, \"text\" : \"available\" }"); + var Product2 p := { name := "Shoe", price := 29.500000, id := 'FFFF'O, origin := "Hungary", text := "available" }; + f_bool2verdict( match(f_dec_prod2(os), p) ); + + os := char2oct("{ \"name\" : \"Shirt\", \"price\" : 12.99, \"id\" : null }"); + p := { name := "Shirt", price := 12.990000, id := omit, origin := "Hungary", text := char(1, 2, 3, 4) & char(5, 6, 7, 8) & "?" }; + f_bool2verdict( match(f_dec_prod2(os), p) ); + + os := char2oct("{ \"name\" : \"test shopper\" }"); + var Shopping_cart sc := { name := "test shopper", product := { name := "Shirt", price := 12.990000, id := omit, origin := "Hungary", text := "available" } }; + f_bool2verdict( match(f_dec_cart(os), sc) ); + + os := char2oct("{ \"name\" : \"test shopper\" }"); + var Shopping_cart_2 sc2 := { name := "test shopper", product := { name := "Size \"M\" Shirt", price := 12.990000, id := omit, origin := "Hungary", text := "available" } }; + f_bool2verdict( match(f_dec_cart2(os), sc2) ); +} + +// Testing standard-compliant default values for record fields of structured types (decoding only) +testcase tc_attribute_default_structured() runs on CT { + var octetstring os := char2oct("{ }"); + var RecDef3 d := { er := { }, u := { i := -6 }, roi := { 1, 2, 3 } }; + f_bool2verdict( match(f_dec_def3(os), d) ); +} + // Encoding a few values of ASN.1 types with the coding instruction "as value" // Results are the same as with the TTCN-3 types testcase tc_attribute_as_value_asn() runs on CT { @@ -746,7 +783,10 @@ control { execute(tc_attribute_prettyprint2()); execute(tc_attribute_union()); execute(tc_attribute_as_value()); - execute(tc_attribute_default()); + execute(tc_attribute_default_legacy()); + execute(tc_attribute_default_basic()); + execute(tc_attribute_default_standard()); + execute(tc_attribute_default_structured()); execute(tc_attribute_as_value_asn()); execute(tc_attribute_optional_as_value()); execute(tc_attribute_metainfo_for_unbound()); diff --git a/regression_test/EncodeDecode/JSON/JsonComplexTest/JsonFunctions.ttcn b/regression_test/EncodeDecode/JSON/JsonComplexTest/JsonFunctions.ttcn index d9bdb7c454887a0adc1f6b4d53293db3ac0405d3..2dd3df6c2a33de4836e53eaa147c10988eeb345b 100644 --- a/regression_test/EncodeDecode/JSON/JsonComplexTest/JsonFunctions.ttcn +++ b/regression_test/EncodeDecode/JSON/JsonComplexTest/JsonFunctions.ttcn @@ -322,6 +322,21 @@ external function f_dec_stuff(in octetstring x) return Stuff external function f_dec_def(in octetstring x) return RecDef with { extension "prototype(convert) decode(JSON)" } +external function f_dec_def2(in octetstring x) return RecDef2 + with { extension "prototype(convert) decode(JSON)" } + +external function f_dec_prod2(in octetstring x) return Product2 + with { extension "prototype(convert) decode(JSON)" } + +external function f_dec_cart(in octetstring x) return Shopping_cart + with { extension "prototype(convert) decode(JSON)" } + +external function f_dec_cart2(in octetstring x) return Shopping_cart_2 + with { extension "prototype(convert) decode(JSON)" } + +external function f_dec_def3(in octetstring x) return RecDef3 + with { extension "prototype(convert) decode(JSON)" } + external function f_dec_hpt(in octetstring x) return HasPardType with { extension "prototype(convert) decode(JSON)" } diff --git a/regression_test/EncodeDecode/JSON/JsonComplexTest/JsonTypes1.ttcn b/regression_test/EncodeDecode/JSON/JsonComplexTest/JsonTypes1.ttcn index 518a2c821ffe68210a8148a264db7ff2a5ae995c..10297fc4d2d6503214bb386462ce294e8417f83b 100644 --- a/regression_test/EncodeDecode/JSON/JsonComplexTest/JsonTypes1.ttcn +++ b/regression_test/EncodeDecode/JSON/JsonComplexTest/JsonTypes1.ttcn @@ -188,6 +188,81 @@ type record RecDef { variant(b) "JSON:default(false)"; } +type record RecDef2 { + integer i, + float f, + boolean b optional, + bitstring bs, + hexstring hs, + octetstring os, + charstring cs, + universal charstring ucs, + enumerated { Tiny, Small, Medium, Large, Huge } size, + verdicttype vt +} with { + variant(cs) "default(\"empty\")"; + variant(i) "default(-19)"; + variant(f) "default(1.0e6)"; + variant(size) "default(Tiny)"; + variant(os) "default('1DE7'O)"; + variant(ucs) "default(""üres"")"; + variant(bs) "default ('101'B)"; + variant(hs) "default('DEAD'H)"; + variant(vt) "default(fail)"; + variant(b) "default(false)"; +} + +type record Product2 { + charstring name, + float price, + octetstring id optional, + charstring origin, + universal charstring text +} +with { + variant(id) "default ('FFFF'O)" + variant(origin) "default(""Hungary"")" + variant(text) "default (char(1,2,3,4\) & char(5,6,7,8\) & ""?"")" +} + +type record Shopping_cart { + charstring name, + Product2 product +} with { + variant(product) "default ({""Shirt"", 12.99, omit, ""Hungary"", ""available"" })" +} + +const Product2 c_defaultProduct := { + name := "Size ""M"" Shirt", + price := 12.99, + id := omit, + origin := "Hungary", + text := "available" +} + +type record Shopping_cart_2 { + charstring name, + Product2 product +} with { + variant(product) "default (c_defaultProduct)" +} + +type union Uni { + integer i, + charstring cs +} + +type record RecDef3 { + EmptyRec er, + Uni u, + RoI roi +} +with { + variant (er) "default ( { } )"; + variant (u) "default ({ i := -6 })"; + variant (roi) "default ({1,2,3})"; +} + type record HasPardType { ProtocolElem_Field1 pard, boolean buul,