From e6c2fb0287355c74d38789b8ebc51bbc5927fd22 Mon Sep 17 00:00:00 2001 From: Botond Baranyi <botond.baranyi@ericsson.com> Date: Tue, 12 May 2020 16:57:06 +0200 Subject: [PATCH] Implemented object-oriented features - stage 6.1 (bug 552011) Change-Id: Ief2350ee8c8a5d979e8c1efc7e564d691a91fba7 Signed-off-by: Botond Baranyi <botond.baranyi@ericsson.com> --- compiler2/Setting.cc | 58 +++++++ compiler2/Setting.hh | 17 +- compiler2/Type.cc | 145 +++++++++++------- compiler2/Type_codegen.cc | 1 + compiler2/Value.cc | 7 + compiler2/ttcn3/AST_ttcn3.cc | 49 +++--- compiler2/ttcn3/Ttcnstuff.cc | 129 +++++++++++++--- compiler2/ttcn3/Ttcnstuff.hh | 7 +- compiler2/ttcn3/compiler.l | 12 ++ compiler2/ttcn3/compiler.y | 12 +- core/OOP.hh | 24 +-- .../Semantic_Analyser/oop/oop_SE.ttcn | 90 +++++++++-- regression_test/oop/oop.ttcn | 89 +++++++++++ 13 files changed, 517 insertions(+), 123 deletions(-) diff --git a/compiler2/Setting.cc b/compiler2/Setting.cc index 3e57713fe..4ecad25e2 100644 --- a/compiler2/Setting.cc +++ b/compiler2/Setting.cc @@ -39,6 +39,7 @@ #include "ttcn3/profiler.h" #include "ttcn3/Attributes.hh" #include "ttcn3/Ttcnstuff.hh" +#include "ttcn3/Statement.hh" namespace Common { @@ -676,6 +677,9 @@ namespace Common { case Type::T_ALTSTEP: typetype_name = "altstep"; break; + case Type::T_CLASS: + typetype_name = "class"; + break; default: FATAL_ERROR("Scope::chk_runs_on_clause()"); typetype_name = 0; @@ -727,6 +731,33 @@ namespace Common { } } + void Scope::chk_mtc_clause(Type* p_type, const Location& p_loc) + { + // component type of the referred definition + Type* refd_comptype = p_type->get_class_type_body()->get_MtcType(); + // definitions without 'mtc' can be called from anywhere + if (refd_comptype == NULL) { + return; + } + if (get_statementblock_scope()->get_my_def() == NULL) { // in control part + p_loc.error("Cannot create value of class type `%s', which has an `mtc' " + "clause, in the control part.", p_type->get_typename().c_str()); + return; + } + Type* t_comptype = get_mtc_system_comptype(false); + if (t_comptype != NULL) { + if (!refd_comptype->is_compatible_component_by_port(t_comptype)) { + // the 'mtc' clause of the referred definition is not compatible + // with that of the current scope (i.e. the referring definition) + p_loc.error("Mtc clause mismatch: A definition that runs on component " + "type `%s' cannot create a value of class type `%s', which has `mtc' " + "component type `%s'", + t_comptype->get_typename().c_str(), p_type->get_typename().c_str(), + refd_comptype->get_typename().c_str()); + } + } + } + void Scope::chk_system_clause(Assignment *p_ass, const Location& p_loc, const char *p_what, bool in_control_part) { @@ -751,6 +782,33 @@ namespace Common { } } } + + void Scope::chk_system_clause(Type* p_type, const Location& p_loc) + { + // component type of the referred definition + Type* refd_comptype = p_type->get_class_type_body()->get_SystemType(); + // definitions without 'system' can be called from anywhere + if (refd_comptype == NULL) { + return; + } + if (get_statementblock_scope()->get_my_def() == NULL) { // in control part + p_loc.error("Cannot create value of class type `%s', which has a `system' " + "clause, in the control part.", p_type->get_typename().c_str()); + return; + } + Type* t_comptype = get_mtc_system_comptype(true); + if (t_comptype != NULL) { + if (!refd_comptype->is_compatible_component_by_port(t_comptype)) { + // the 'system' clause of the referred definition is not compatible + // with that of the current scope (i.e. the referring definition) + p_loc.error("System clause mismatch: A definition that runs on component " + "type `%s' cannot create a value of class type `%s', which has `system' " + "component type `%s'", + t_comptype->get_typename().c_str(), p_type->get_typename().c_str(), + refd_comptype->get_typename().c_str()); + } + } + } // ================================= // ===== Reference diff --git a/compiler2/Setting.hh b/compiler2/Setting.hh index 0f5ab426d..ca3103316 100644 --- a/compiler2/Setting.hh +++ b/compiler2/Setting.hh @@ -634,10 +634,11 @@ public: * "activate". */ void chk_runs_on_clause(Assignment *p_ass, const Location& p_loc, const char *p_what); - /** Checks the 'runs on' clause of type \a p_fat that the values of it can - * be called from this scope unit. Type \a p_fat shall be of type function - * or altstep. Parameters \a p_loc and \a p_what are used in error messages. - * \a p_what contains "call" or "activate". */ + /** Checks the 'runs on' clause of type \a p_fat that values of it can + * be called/created from this scope unit. + * Type \a p_fat shall be of function, altstep or class type. + * Parameters \a p_loc and \a p_what are used in error messages. + * \a p_what contains "call", "activate" or "create". */ void chk_runs_on_clause(Type *p_fat, const Location& p_loc, const char *p_what); /** Checks the 'mtc' clause of definition \a p_ass that it can @@ -646,12 +647,20 @@ public: * "activate". */ void chk_mtc_clause(Assignment *p_ass, const Location& p_loc, const char *p_what, bool in_control_part); + /** Checks the 'mtc' clause of type \a p_type that values of it can + * be created from this scope unit. Type \a p_type shall be of class type. + * Parameters \a p_loc is used in error messages. */ + void chk_mtc_clause(Type* p_type, const Location& p_loc); /** Checks the 'system' clause of definition \a p_ass that it can * be called from this scope unit. Parameters \a p_loc and \a * p_what are used in error messages. \a p_what contains "call" or * "activate". */ void chk_system_clause(Assignment *p_ass, const Location& p_loc, const char *p_what, bool in_control_part); + /** Checks the 'system' clause of type \a p_type that values of it can + * be created from this scope unit. Type \a p_type shall be of class type. + * Parameters \a p_loc is used in error messages. */ + void chk_system_clause(Type* p_type, const Location& p_loc); }; /** diff --git a/compiler2/Type.cc b/compiler2/Type.cc index 928aea26a..ffeee7778 100644 --- a/compiler2/Type.cc +++ b/compiler2/Type.cc @@ -53,6 +53,7 @@ #include "ttcn3/Templatestuff.hh" #include "ttcn3/RawAST.hh" #include "ttcn3/JsonAST.hh" +#include "ttcn3/Statement.hh" #include "../common/static_check.h" #include "PredefFunc.hh" @@ -227,6 +228,7 @@ namespace Common { case T_VERDICT: case T_COMPONENT: case T_DEFAULT: + case T_CLASS: // for the built-in class 'object' break; // we have a pool type default: return 0; // no pool type for you! @@ -867,6 +869,9 @@ namespace Common { case T_ADDRESS: u.address = 0; break; + case T_CLASS: + u.class_ = new Ttcn::ClassTypeBody(); + break; default: FATAL_ERROR("Type::Type()"); } // switch @@ -1776,66 +1781,91 @@ namespace Common { return 0; } Ttcn::ClassTypeBody* class_ = t->get_class_type_body(); + bool base_toString = false; if (!class_->has_local_ass_withId(id)) { - ref->error("Reference to non-existent method `%s' in class type `%s'", - id.get_dispname().c_str(), t->get_typename().c_str()); - return 0; - } - Assignment* ass = class_->get_local_ass_byId(id); - if (!class_->chk_visibility(ass, ref, subrefs->get_my_scope())) { - // the method is not visible (the error has already been reported) - return 0; - } - switch (ass->get_asstype()) { - case Assignment::A_VAR: - case Assignment::A_VAR_TEMPLATE: - case Assignment::A_CONST: - case Assignment::A_TEMPLATE: - ref->error("Invalid reference to member `%s' in class type `%s', " - "reference to a method was expected instead", - id.get_dispname().c_str(), t->get_typename().c_str()); - return 0; - case Assignment::A_FUNCTION: - case Assignment::A_EXT_FUNCTION: - if (i != nof_refs - 1 || last_method == NULL) { - ref->error("Invalid reference to method `%s' with no return type in " - "class type `%s'", + if (id.get_name() == string("toString")) { + // the 'toString' method is not in the AST, but it is inherited by + // every class from the 'object' class + base_toString = true; + if (!ref->parameters_checked()) { + Ttcn::FormalParList fp_list; // empty formal parameter list + Ttcn::ParsedActualParameters* parsed_pars = ref->get_parsed_pars(); + Ttcn::ActualParList* ap_list = new Ttcn::ActualParList; + bool is_erroneous = fp_list.fold_named_and_chk(parsed_pars, ap_list); + if (is_erroneous) { + delete ap_list; + return 0; + } + ap_list->set_fullname(parsed_pars->get_fullname()); + ap_list->set_my_scope(parsed_pars->get_my_scope()); + ref->set_actual_par_list(ap_list); + } + t = get_pooltype(T_USTR); + // todo: set *last_method + } + else { + ref->error("Reference to non-existent method `%s' in class type `%s'", id.get_dispname().c_str(), t->get_typename().c_str()); return 0; } - // a method with no return value can still be valid (e.g. if it's - // in a statement) - // proceed as normal and return null at the end - case Assignment::A_FUNCTION_RVAL: - case Assignment::A_FUNCTION_RTEMP: - case Assignment::A_EXT_FUNCTION_RVAL: - case Assignment::A_EXT_FUNCTION_RTEMP: { - Ttcn::Def_Function_Base* def_func = - dynamic_cast<Ttcn::Def_Function_Base*>(ass); - if (def_func == NULL) { - FATAL_ERROR("Type::get_field_type"); + } + if (!base_toString) { + Assignment* ass = class_->get_local_ass_byId(id); + if (!class_->chk_visibility(ass, ref, subrefs->get_my_scope())) { + // the method is not visible (the error has already been reported) + return 0; } - t = def_func->get_return_type(); - if (!ref->parameters_checked()) { - Ttcn::FormalParList* fp_list = ass->get_FormalParList(); - Ttcn::ParsedActualParameters* parsed_pars = ref->get_parsed_pars(); - Ttcn::ActualParList* ap_list = new Ttcn::ActualParList; - bool is_erroneous = fp_list->fold_named_and_chk(parsed_pars, ap_list); - if (is_erroneous) { - delete ap_list; + switch (ass->get_asstype()) { + case Assignment::A_VAR: + case Assignment::A_VAR_TEMPLATE: + case Assignment::A_CONST: + case Assignment::A_TEMPLATE: + ref->error("Invalid reference to member `%s' in class type `%s', " + "reference to a method was expected instead", + id.get_dispname().c_str(), t->get_typename().c_str()); + return 0; + case Assignment::A_FUNCTION: + case Assignment::A_EXT_FUNCTION: + if (i != nof_refs - 1 || last_method == NULL) { + ref->error("Invalid reference to method `%s' with no return type in " + "class type `%s'", + id.get_dispname().c_str(), t->get_typename().c_str()); return 0; } - ap_list->set_fullname(parsed_pars->get_fullname()); - ap_list->set_my_scope(parsed_pars->get_my_scope()); - ref->set_actual_par_list(ap_list); - } - if (last_method != NULL) { - *last_method = ass; + // a method with no return value can still be valid (e.g. if it's + // in a statement) + // proceed as normal and return null at the end + case Assignment::A_FUNCTION_RVAL: + case Assignment::A_FUNCTION_RTEMP: + case Assignment::A_EXT_FUNCTION_RVAL: + case Assignment::A_EXT_FUNCTION_RTEMP: { + Ttcn::Def_Function_Base* def_func = + dynamic_cast<Ttcn::Def_Function_Base*>(ass); + if (def_func == NULL) { + FATAL_ERROR("Type::get_field_type"); + } + t = def_func->get_return_type(); + if (!ref->parameters_checked()) { + Ttcn::FormalParList* fp_list = ass->get_FormalParList(); + Ttcn::ParsedActualParameters* parsed_pars = ref->get_parsed_pars(); + Ttcn::ActualParList* ap_list = new Ttcn::ActualParList; + bool is_erroneous = fp_list->fold_named_and_chk(parsed_pars, ap_list); + if (is_erroneous) { + delete ap_list; + return 0; + } + ap_list->set_fullname(parsed_pars->get_fullname()); + ap_list->set_my_scope(parsed_pars->get_my_scope()); + ref->set_actual_par_list(ap_list); + } + if (last_method != NULL) { + *last_method = ass; + } + break; } + default: + FATAL_ERROR("Type::get_field_type - %s shouldn't be in a class", + ass->get_assname()); } - break; } - default: - FATAL_ERROR("Type::get_field_type - %s shouldn't be in a class", - ass->get_assname()); } break; } case Ttcn::FieldOrArrayRef::ARRAY_REF: { @@ -6224,6 +6254,8 @@ namespace Common { case T_ALTSTEP: case T_TESTCASE: return u.fatref.runs_on.type; + case T_CLASS: + return u.class_->get_RunsOnType(); default: FATAL_ERROR("Type::get_fat_runs_on_type()"); return 0; @@ -7542,6 +7574,9 @@ namespace Common { return string("COMPONENT"); case T_DEFAULT: return string("DEFAULT"); + case T_CLASS: + return t->u.class_->is_built_in() ? string("OBJECT_REF<OBJECT>") : + string("OBJECT_REF<") + t->get_genname_own(p_scope) + string(">"); case T_ARRAY: if (!t->u.array.in_typedef) return t->u.array.dimension->get_value_type(t->u.array.element_type, @@ -7634,6 +7669,11 @@ namespace Common { const char* tn = get_typename_builtin(t->typetype); if (tn != 0) return string(tn); switch (t->typetype) { + case T_CLASS: + if (t->u.class_->is_built_in()) { + return string("object"); + } + // else fall through case T_COMPONENT: case T_SIGNATURE: case T_CHOICE_A: @@ -7651,7 +7691,6 @@ namespace Common { case T_FUNCTION: case T_ALTSTEP: case T_TESTCASE: - case T_CLASS: return t->get_fullname(); case T_ARRAY: { string dimensions(t->u.array.dimension->get_stringRepr()); diff --git a/compiler2/Type_codegen.cc b/compiler2/Type_codegen.cc index 7ef873ec3..9f4e9659f 100644 --- a/compiler2/Type_codegen.cc +++ b/compiler2/Type_codegen.cc @@ -257,6 +257,7 @@ void Type::generate_code_typedescriptor(output_struct *target) switch (get_type_refd_last()->typetype) { case T_PORT: case T_SIGNATURE: + case T_CLASS: // do not generate any type descriptor for these non-data types return; case T_ARRAY: diff --git a/compiler2/Value.cc b/compiler2/Value.cc index 2cee16d5b..6204546b2 100644 --- a/compiler2/Value.cc +++ b/compiler2/Value.cc @@ -2200,6 +2200,7 @@ namespace Common { if(u.expr.v3) u.expr.v3->set_fullname(p_fullname+".<operand3>"); break; case OPTYPE_UNDEF_CREATE: // r1 t_list2 b4 + case OPTYPE_CLASS_CREATE: // r1 t_list2 b4 u.expr.r1->set_fullname(p_fullname+".<operand1>"); u.expr.t_list2->set_fullname(p_fullname+".<parameterlist>"); break; @@ -8216,6 +8217,9 @@ void Value::chk_expr_operand_execute_refd(Value *v1, u.expr.ap_list2 = parlist; } chk_expr_dynamic_part(exp_val, true); + my_scope->chk_runs_on_clause(t, *this, "create"); + my_scope->chk_mtc_clause(t, *this); + my_scope->chk_system_clause(t, *this); } if (u.expr.v_optype != OPTYPE_COMP_CREATE) { break; @@ -13029,6 +13033,9 @@ void Value::chk_expr_operand_execute_refd(Value *v1, FATAL_ERROR("Value::generate_code_init()"); } break; + case V_TTCN3_NULL: + str = mputprintf(str, "%s = NULL_VALUE;\n", name); + break; case V_NOTUSED: // unbound value, don't generate anything break; diff --git a/compiler2/ttcn3/AST_ttcn3.cc b/compiler2/ttcn3/AST_ttcn3.cc index 1a4481507..3a464bf8a 100644 --- a/compiler2/ttcn3/AST_ttcn3.cc +++ b/compiler2/ttcn3/AST_ttcn3.cc @@ -482,12 +482,19 @@ namespace Ttcn { Free(prev_expr); } const Identifier& id = *ref->get_id(); - Common::Assignment* ass = class_->get_local_ass_byId(id); + // 'ass' is null if the 'toString' method from the 'object' class is called + Common::Assignment* ass = class_->has_local_ass_withId(id) ? + class_->get_local_ass_byId(id) : NULL; expr->expr = mputprintf(expr->expr, "->%s(", id.get_name().c_str()); - ref->get_actual_par_list()->generate_code_noalias(expr, ass->get_FormalParList()); + FormalParList* fp_list = ass != NULL ? ass->get_FormalParList() : + new FormalParList; // the formal parameter list of 'toString' is empty + ref->get_actual_par_list()->generate_code_noalias(expr, fp_list); + if (ass == NULL) { + delete fp_list; + } expr->expr = mputc(expr->expr, ')'); - Def_Function_Base* def_func = dynamic_cast<Def_Function_Base*>(ass); - type = def_func->get_return_type(); + type = ass != NULL ? ass->get_Type() : + Common::Type::get_pooltype(Common::Type::T_USTR); if (const_ref && i < n_refs - 1 && refs[i + 1]->get_type() != FieldOrArrayRef::FUNCTION_REF) { // the next subreference is a field name or array index, have to @@ -1055,15 +1062,17 @@ namespace Ttcn { expr->expr = mputstr(expr->expr, "this->"); } else { // no 'id' means it's just a 'this' reference - expr->expr = mputprintf(expr->expr, "OBJECT_REF<%s>(this)", + expr->expr = mputprintf(expr->expr, "%s(this)", ass->get_Type()->get_genname_value(my_scope).c_str()); return; } } else if (reftype == REF_SUPER) { + Common::Type* base_type = my_scope->get_scope_class()->get_base_type()-> + get_type_refd_last(); expr->expr = mputprintf(expr->expr, "%s::", - my_scope->get_scope_class()->get_base_type()-> - get_genname_value(my_scope).c_str()); + base_type->get_class_type_body()->is_built_in() ? "OBJECT" : + base_type->get_genname_own(my_scope).c_str()); } string const_prefix; // empty by default if (gen_const_prefix) { @@ -1146,12 +1155,8 @@ namespace Ttcn { } else { // don't convert to const object if the first subreference is a method call if (t_subrefs->get_ref(0)->get_type() != FieldOrArrayRef::FUNCTION_REF) { - string type_str = refd_gov->get_genname_value(get_my_scope()); - if (refd_gov->get_type_refd_last()->get_typetype() == Common::Type::T_CLASS) { - type_str = string("OBJECT_REF<") + type_str + string(">"); - } this_expr.expr = mputprintf(this_expr.expr, "const_cast< const %s&>(", - type_str.c_str()); + refd_gov->get_genname_value(get_my_scope()).c_str()); } } if (parlist != NULL) { @@ -3688,6 +3693,11 @@ namespace Ttcn { continue; } field_name = tref->get_id()->get_ttcnname(); + if (oop_features && field_name == string("object")) { + ea.error("Class type `object' cannot be added to the anytype"); + delete t; + continue; + } } else { // Can't happen here @@ -3888,7 +3898,7 @@ namespace Ttcn { break; case Type::T_CLASS: error("Constant cannot be defined for class type `%s'", - t->get_fullname().c_str()); + t->get_typename().c_str()); break; default: value_under_check = true; @@ -4078,7 +4088,7 @@ namespace Ttcn { break; case Type::T_CLASS: error("External constant cannot be defined for class type `%s'", - t->get_fullname().c_str()); + t->get_typename().c_str()); break; default: break; @@ -4204,7 +4214,7 @@ namespace Ttcn { break; case Type::T_CLASS: error("Type of module parameter cannot be or embed class type `%s'", - t->get_fullname().c_str()); + t->get_typename().c_str()); break; case Type::T_FUNCTION: case Type::T_ALTSTEP: @@ -4384,7 +4394,7 @@ namespace Ttcn { break; case Type::T_CLASS: error("Type of template module parameter cannot be class type `%s'", - t->get_fullname().c_str()); + t->get_typename().c_str()); break; default: if (IMPLICIT_OMIT == has_implicit_omit_attr()) { @@ -4605,7 +4615,7 @@ namespace Ttcn { } else if (t->get_typetype() == Type::T_CLASS) { error("Template cannot be defined for class type `%s'", - t->get_fullname().c_str()); + t->get_typename().c_str()); } chk_modified(); chk_recursive_derivation(); @@ -5285,9 +5295,6 @@ namespace Ttcn { const string& t_genname = get_genname(); const char *genname_str = t_genname.c_str(); string type_name = type->get_genname_value(my_scope); - if (type->get_type_refd_last()->get_typetype() == Common::Type::T_CLASS) { - type_name = string("OBJECT_REF<") + type_name + string(">"); - } if (initial_value && initial_value->has_single_expr()) { // the initial value can be represented by a single C++ expression // the object is initialized by the constructor @@ -5396,7 +5403,7 @@ namespace Ttcn { } else if (t->get_typetype() == Type::T_CLASS) { error("Template variable cannot be defined for class type `%s'", - t->get_fullname().c_str()); + t->get_typename().c_str()); } if (initial_value) { diff --git a/compiler2/ttcn3/Ttcnstuff.cc b/compiler2/ttcn3/Ttcnstuff.cc index 9a2e25891..f588d5bc0 100644 --- a/compiler2/ttcn3/Ttcnstuff.cc +++ b/compiler2/ttcn3/Ttcnstuff.cc @@ -2982,23 +2982,37 @@ namespace Ttcn { // ================================= ClassTypeBody::ClassTypeBody(Common::Identifier* p_class_id, boolean p_external, boolean p_final, - boolean p_abstract, Common::Type* p_base_type, Reference* p_runs_on_ref, - Reference* p_mtc_ref, Reference* p_system_ref, + boolean p_abstract, Common::Type* p_base_type, + Reference* p_runs_on_ref, Reference* p_mtc_ref, Reference* p_system_ref, Definitions* p_members, StatementBlock* p_finally_block) - : Scope(), Location(), class_id(p_class_id), my_def(NULL), external(p_external), final(p_final), abstract(p_abstract), - base_type(p_base_type), runs_on_ref(p_runs_on_ref), runs_on_type(NULL), - mtc_ref(p_mtc_ref), mtc_type(NULL), system_ref(p_system_ref), system_type(NULL), + : Scope(), Location(), class_id(p_class_id), my_def(NULL), external(p_external), final(p_final), + abstract(p_abstract), built_in(FALSE), base_type(p_base_type), + runs_on_ref(p_runs_on_ref), runs_on_type(NULL), mtc_ref(p_mtc_ref), + mtc_type(NULL), system_ref(p_system_ref), system_type(NULL), members(p_members), finally_block(p_finally_block), constructor(NULL), checked(false), default_constructor(false) { + // constructor for user-defined classes if (members == NULL) { FATAL_ERROR("ClassTypeBody::ClassTypeBody"); } } + ClassTypeBody::ClassTypeBody() + : Scope(), Location(), class_id(NULL), my_def(NULL), external(FALSE), final(FALSE), + abstract(TRUE), built_in(TRUE), base_type(NULL), + runs_on_ref(NULL), runs_on_type(NULL), mtc_ref(NULL), + mtc_type(NULL), system_ref(NULL), system_type(NULL), + members(NULL), finally_block(NULL), constructor(NULL), checked(false), + default_constructor(false) + { + // constructor for the built-in class 'object' + } + ClassTypeBody::ClassTypeBody(const ClassTypeBody& p) { - class_id = p.class_id->clone(); + built_in = p.built_in; + class_id = p.class_id != NULL ? p.class_id->clone() : NULL; my_def = p.my_def; external = p.external; final = p.final; @@ -3007,7 +3021,7 @@ namespace Ttcn { runs_on_ref = p.runs_on_ref != NULL ? p.runs_on_ref->clone() : NULL; mtc_ref = p.mtc_ref != NULL ? p.mtc_ref->clone() : NULL; system_ref = p.system_ref != NULL ? p.system_ref->clone() : NULL; - members = p.members->clone(); + members = p.members != NULL ? p.members->clone() : NULL; finally_block = p.finally_block != NULL ? p.finally_block->clone() : NULL; default_constructor = p.default_constructor; constructor = default_constructor ? p.constructor->clone() : p.constructor; @@ -3021,6 +3035,9 @@ namespace Ttcn { ClassTypeBody::~ClassTypeBody() { + if (built_in) { + return; + } delete base_type; delete finally_block; delete members; @@ -3034,6 +3051,9 @@ namespace Ttcn { void ClassTypeBody::set_fullname(const string& p_fullname) { + if (built_in) { + return; + } Common::Scope::set_fullname(p_fullname); if (base_type != NULL) { base_type->set_fullname(p_fullname + ".<superclass>"); @@ -3055,6 +3075,9 @@ namespace Ttcn { void ClassTypeBody::set_my_scope(Scope* p_scope) { + if (built_in) { + return; + } set_parent_scope(p_scope); if (base_type != NULL) { base_type->set_my_scope(p_scope); @@ -3076,6 +3099,10 @@ namespace Ttcn { void ClassTypeBody::dump(unsigned level) const { + if (built_in) { + DEBUG(level, "Built-in class 'object'"); + return; + } DEBUG(level, "Modifiers:%s%s%s", external ? "external " : "", final ? " @final " : "", abstract ? " @abstract" : ""); DEBUG(level, "Base class: %s", base_type != NULL ? @@ -3132,7 +3159,7 @@ namespace Ttcn { bool ClassTypeBody::is_parent_class(const ClassTypeBody* p_class) const { - if (this == p_class) { + if (this == p_class || p_class->built_in) { return true; } if (base_type == NULL) { @@ -3144,6 +3171,9 @@ namespace Ttcn { bool ClassTypeBody::has_local_ass_withId(const Identifier& p_id) { + if (built_in) { + return false; + } chk(); if (members->has_local_ass_withId(p_id)) { return true; @@ -3159,6 +3189,9 @@ namespace Ttcn { Common::Assignment* ClassTypeBody::get_local_ass_byId(const Identifier& p_id) { + if (built_in) { + FATAL_ERROR("ClassTypeBody::get_local_ass_byId"); + } chk(); Common::Assignment* ass = NULL; if (members->has_local_ass_withId(p_id)) { @@ -3175,6 +3208,9 @@ namespace Ttcn { Common::Location* usage_loc, Common::Scope* usage_scope) { + if (built_in) { + FATAL_ERROR("ClassTypeBody::chk_visibility"); + } if (ass->get_visibility() == PUBLIC) { // it's public, so it doesn't matter where it's accessed from return true; @@ -3204,6 +3240,9 @@ namespace Ttcn { Common::Assignment* ClassTypeBody::get_ass_bySRef(Common::Ref_simple* p_ref) { + if (built_in) { + return NULL; + } if (p_ref == NULL || parent_scope == NULL) { FATAL_ERROR("ClassTypeBody::get_ass_bySRef()"); } @@ -3251,7 +3290,7 @@ namespace Ttcn { void ClassTypeBody::chk() { - if (checked) { + if (checked || built_in) { return; } checked = true; @@ -3270,25 +3309,69 @@ namespace Ttcn { // TODO: additional checks for the base type } + ClassTypeBody* base_class = base_type != NULL ? + base_type->get_type_refd_last()->get_class_type_body() : NULL; if (runs_on_ref != NULL) { Error_Context cntxt(runs_on_ref, "In `runs on' clause"); runs_on_type = runs_on_ref->chk_comptype_ref(); - // TODO + if (base_class != NULL) { + Type* base_runs_on_type = base_class->get_RunsOnType(); + if (base_runs_on_type != NULL && + !base_runs_on_type->is_compatible(runs_on_type, NULL, NULL)) { + runs_on_ref->error("The `runs on' component type of the subclass, " + "`%s', is not compatible with the `runs on' component type of the " + "superclass, `%s'", + runs_on_type->get_typename().c_str(), + base_runs_on_type->get_typename().c_str()); + } + } + } + else if (base_class != NULL) { + // inherit `runs on' component from the superclass + runs_on_type = base_class->get_RunsOnType(); } + if (mtc_ref != NULL) { Error_Context cntxt(mtc_ref, "In `mtc' clause"); mtc_type = mtc_ref->chk_comptype_ref(); - // TODO + if (base_class != NULL) { + Type* base_mtc_type = base_class->get_MtcType(); + if (base_mtc_type != NULL && + !base_mtc_type->is_compatible(mtc_type, NULL, NULL)) { + mtc_ref->error("The `mtc' component type of the subclass, `%s', is not " + "compatible with the `mtc' component type of the superclass, `%s'", + mtc_type->get_typename().c_str(), + base_mtc_type->get_typename().c_str()); + } + } } + else if (base_class != NULL) { + // inherit `mtc' component from the superclass + mtc_type = base_class->get_MtcType(); + } + if (system_ref != NULL) { Error_Context cntxt(system_ref, "In `system' clause"); system_type = system_ref->chk_comptype_ref(); - // TODO + if (base_class != NULL) { + Type* base_system_type = base_class->get_SystemType(); + if (base_system_type != NULL && + !base_system_type->is_compatible(system_type, NULL, NULL)) { + system_ref->error("The `system' component type of the subclass, `%s', is " + "not compatible with the `system' component type of the superclass, `%s'", + system_type->get_typename().c_str(), + base_system_type->get_typename().c_str()); + } + } + } + else if (base_class != NULL) { + // inherit `system' component from the superclass + system_type = base_class->get_SystemType(); } members->chk_uniq(); members->chk(); - + for (size_t i = 0; i < members->get_nof_asss(); ++i) { Common::Assignment* ass = members->get_ass_byIndex(i); if (ass->get_asstype() == Common::Assignment::A_CONSTRUCTOR) { @@ -3305,9 +3388,7 @@ namespace Ttcn { // create a default constructor Reference* base_call = NULL; FormalParList* fp_list = NULL; - if (base_type != NULL) { - ClassTypeBody* base_class = base_type->get_type_refd_last()-> - get_class_type_body(); + if (base_class != NULL) { Def_Constructor* base_constructor = base_class->get_constructor(); if (base_constructor != NULL) { FormalParList* base_fp_list = base_constructor->get_FormalParList(); @@ -3374,6 +3455,9 @@ namespace Ttcn { void ClassTypeBody::chk_recursions(ReferenceChain& refch) { + if (built_in) { + return; + } if (base_type != NULL) { base_type->chk_recursions(refch); } @@ -3381,8 +3465,12 @@ namespace Ttcn { for (size_t i = 0; i < members->get_nof_asss(); ++i) { Common::Assignment* def = members->get_ass_byIndex(i); switch (def->get_asstype()) { - case Common::Assignment::A_CONST: case Common::Assignment::A_VAR: + if (def->get_Type()->get_type_refd_last()->get_typetype() == Common::Type::T_CLASS) { + break; + } + // else fall through + case Common::Assignment::A_CONST: case Common::Assignment::A_TEMPLATE: case Common::Assignment::A_VAR_TEMPLATE: def->get_Type()->chk_recursions(refch); @@ -3395,11 +3483,14 @@ namespace Ttcn { void ClassTypeBody::generate_code(output_struct* target) { + if (built_in) { + return; + } target->header.class_decls = mputprintf(target->header.class_decls, "class %s;\n", class_id->get_name().c_str()); if (!external) { string base_type_name = base_type != NULL ? - base_type->get_genname_value(this) : string("OBJECT"); + base_type->get_type_refd_last()->get_genname_own(this) : string("OBJECT"); target->header.class_defs = mputprintf(target->header.class_defs, "class %s : public %s {\n", @@ -3505,7 +3596,7 @@ namespace Ttcn { if (base_type != NULL) { target->source.methods = mputprintf(target->source.methods, "%s::log();\n", - base_type->get_genname_value(this).c_str()); + base_type_name.c_str()); first_logged = true; } for (size_t i = 0; i < members->get_nof_asss(); ++i) { diff --git a/compiler2/ttcn3/Ttcnstuff.hh b/compiler2/ttcn3/Ttcnstuff.hh index 9ad4794da..d6a7669c0 100644 --- a/compiler2/ttcn3/Ttcnstuff.hh +++ b/compiler2/ttcn3/Ttcnstuff.hh @@ -760,6 +760,7 @@ class ClassTypeBody : public Common::Scope, public Common::Location { boolean external; boolean final; boolean abstract; + boolean built_in; Common::Type* base_type; Reference* runs_on_ref; Type* runs_on_type; @@ -777,9 +778,10 @@ class ClassTypeBody : public Common::Scope, public Common::Location { public: ClassTypeBody(Common::Identifier* p_class_id, boolean p_external, boolean p_final, - boolean p_abstract, Common::Type* p_base_type, Ttcn::Reference* p_runs_on_ref, - Reference* p_mtc_ref, Reference* p_system_ref, + boolean p_abstract, Common::Type* p_base_type, + Ttcn::Reference* p_runs_on_ref, Reference* p_mtc_ref, Reference* p_system_ref, Definitions* p_members, StatementBlock* p_finally_block); + ClassTypeBody(); ClassTypeBody(const ClassTypeBody& p); ClassTypeBody* clone() const; virtual ~ClassTypeBody(); @@ -795,6 +797,7 @@ public: Common::Identifier* get_id() const { return class_id; } Def_Constructor* get_constructor(); Common::Type* get_base_type() const { return base_type; } + boolean is_built_in() const { return built_in; } Type* get_RunsOnType(); Type* get_MtcType(); diff --git a/compiler2/ttcn3/compiler.l b/compiler2/ttcn3/compiler.l index f18436ce9..cc5de153a 100644 --- a/compiler2/ttcn3/compiler.l +++ b/compiler2/ttcn3/compiler.l @@ -606,6 +606,18 @@ super { RETURN_LVAL(IDentifier); } } +object { + if (oop_features) { + RETURN(ObjectKeyword); + } + else { + Location loc(infile, yylloc); + loc.warning("Keyword 'object' is treated as an identifier. Activate " + "compiler option '-k' to use object-oriented features."); + yylval.id = new Identifier(Identifier::ID_TTCN, string(yyleng, yytext)); + RETURN_LVAL(IDentifier); + } +} /* modifier keywords */ diff --git a/compiler2/ttcn3/compiler.y b/compiler2/ttcn3/compiler.y index e4e0c01a4..bc7557559 100644 --- a/compiler2/ttcn3/compiler.y +++ b/compiler2/ttcn3/compiler.y @@ -738,6 +738,7 @@ static const string anyname("anytype"); %token NowKeyword %token NowaitKeyword %token NullKeyword +%token ObjectKeyword %token ObjectIdentifierKeyword %token OctetStringKeyword %token OfKeyword @@ -1948,7 +1949,7 @@ optDecodedModifier %left '*' '/' ModKeyword RemKeyword %left UnarySign -%expect 75 +%expect 76 %start GrammarRoot @@ -1996,9 +1997,9 @@ non-standard language extension. 6.) 1 Conflict due to pattern concatenation -7.) 29 conflicts in one state +7.) 30 conflicts in one state In the DecodedContentMatch rule a SingleExpression encased in round brackets is -followed by an in-line template. For 29 tokens (after the ')' ) the parser cannot +followed by an in-line template. For 30 tokens (after the ')' ) the parser cannot decide whether the token is the beginning of the in-line template (shift) or the brackets are only part of the SingleExpression itself and the conflicting token is the next segment in the expression (reduce). @@ -8229,6 +8230,11 @@ Type: // 450 $$ = new Type($1); $$->set_location(infile, @$); } +| ObjectKeyword + { + $$ = new Type(Type::T_CLASS); + $$->set_location(infile, @$); + } | AnyTypeKeyword /* a predefined type with special treatment */ { anytype_access = true; diff --git a/core/OOP.hh b/core/OOP.hh index a3ddd55e0..f5a03e74c 100644 --- a/core/OOP.hh +++ b/core/OOP.hh @@ -38,9 +38,12 @@ public: --ref_count; return ref_count == 0; } - void log() const { + virtual void log() const { TTCN_Logger::log_event_str("object: { }"); } + virtual UNIVERSAL_CHARSTRING toString() { + return UNIVERSAL_CHARSTRING("Object"); + } }; // OBJECT_REF @@ -55,15 +58,6 @@ class OBJECT_REF { private: T* ptr; // NULL if it's a null reference - void clean_up() { - if (ptr != NULL) { - if (ptr->remove_ref()) { - delete ptr; - } - ptr = NULL; - } - } - public: OBJECT_REF(): ptr(NULL) {} // constructor with no parameters @@ -79,12 +73,22 @@ public: } } + void clean_up() { + if (ptr != NULL) { + if (ptr->remove_ref()) { + delete ptr; + } + ptr = NULL; + } + } + ~OBJECT_REF() { clean_up(); } OBJECT_REF& operator=(null_type) { // assignment operator for null reference clean_up(); + return *this; } OBJECT_REF& operator=(const OBJECT_REF<T>& p_other) { // assignment operator for actual reference diff --git a/function_test/Semantic_Analyser/oop/oop_SE.ttcn b/function_test/Semantic_Analyser/oop/oop_SE.ttcn index b7d6129fe..b2213a0ff 100644 --- a/function_test/Semantic_Analyser/oop/oop_SE.ttcn +++ b/function_test/Semantic_Analyser/oop/oop_SE.ttcn @@ -124,32 +124,44 @@ type class C8 extends C7 { //^In type definition// //While checking embedded rec type class C9 extends C8 {} //^In type definition// //^In superclass definition// -external const C0 ec_obj; //^In external constant definition// //External constant cannot be defined for class type `@oop_SE.C0'// -modulepar C0 mp_obj; //^In module parameter definition// //Type of module parameter cannot be or embed class type `@oop_SE.C0'// -modulepar template C0 mpt_obj; //^In template module parameter definition// //Type of template module parameter cannot be class type `@oop_SE.C0'// +external const C0 ec_c0; //^In external constant definition// //External constant cannot be defined for class type `@oop_SE.C0'// +external const object ec_obj; //^In external constant definition// //External constant cannot be defined for class type `object'// +modulepar C0 mp_c0; //^In module parameter definition// //Type of module parameter cannot be or embed class type `@oop_SE.C0'// +modulepar object mp_obj; //^In module parameter definition// //Type of module parameter cannot be or embed class type `object'// +modulepar template C0 mpt_c0; //^In template module parameter definition// //Type of template module parameter cannot be class type `@oop_SE.C0'// +modulepar template object mpt_obj; //^In template module parameter definition// //Type of template module parameter cannot be class type `object'// function f_defs() { //^In function definition// - const C0 c_obj := null; //^In constant definition// //Constant cannot be defined for class type `@oop_SE.C0'// - var C0 v_obj; - template C0 t_obj := *; //^In template definition// //Template cannot be defined for class type `@oop_SE.C0'// - var template C0 vt_obj; //^In template variable definition// //Template variable cannot be defined for class type `@oop_SE.C0'// + const C0 c_c0 := null; //^In constant definition// //Constant cannot be defined for class type `@oop_SE.C0'// + const object c_obj := null; //^In constant definition// //Constant cannot be defined for class type `object'// + var C0 v_c0; + var object v_obj; + template C0 t_c0 := *; //^In template definition// //Template cannot be defined for class type `@oop_SE.C0'// + template object t_obj := *; //^In template definition// //Template cannot be defined for class type `object'// + var template C0 vt_c0; //^In template variable definition// //Template variable cannot be defined for class type `@oop_SE.C0'// + var template object vt_obj; //^In template variable definition// //Template variable cannot be defined for class type `object'// } type record RecClass { //^In type definition// - C0 x //^In record field// //Class type `@oop_SE.C0' cannot be embedded into another type// + C0 x, //^In record field// //Class type `@oop_SE.C0' cannot be embedded into another type// + object y //^In record field// //Class type `object' cannot be embedded into another type// } type set SetClass { //^In type definition// - C0 x //^In set field// //Class type `@oop_SE.C0' cannot be embedded into another type// + C0 x, //^In set field// //Class type `@oop_SE.C0' cannot be embedded into another type// + object y //^In set field// //Class type `object' cannot be embedded into another type// } type record of C0 RecOfClass; //^In type definition// //^In embedded type of record of// //Class type `@oop_SE.C0' cannot be embedded into another type// +type record of object RecOfObject; //^In type definition// //^In embedded type of record of// //Class type `object' cannot be embedded into another type// type set of C0 SetOfClass; //^In type definition// //^In embedded type of set of// //Class type `@oop_SE.C0' cannot be embedded into another type// +type set of object SetOfObject; //^In type definition// //^In embedded type of set of// //Class type `object' cannot be embedded into another type// type union UniClass { //^In type definition// - C0 x //^In union field// //Class type `@oop_SE.C0' cannot be embedded into another type// + C0 x, //^In union field// //Class type `@oop_SE.C0' cannot be embedded into another type// + object y //^In union field// //Class type `object' cannot be embedded into another type// } function f_embedded_types() { //^In function definition// @@ -191,11 +203,25 @@ type component CT_RunsOn { type component CT_Mtc { var integer cv_mtc; const integer cc_mtc := 1; + port Port pt; } type component CT_System { var integer cv_system; const integer cc_system := 2; + port Port pt; +} + +type component CT_RunsOn2 extends CT_RunsOn { + var template integer cvt_runs_on := *; +} + +type component CT_Mtc2 extends CT_Mtc { + var template integer cvt_mtc := ?; +} + +type component CT_System2 extends CT_System { + var template integer cvt_system := omit; } type class C10 runs on CT_RunsOn mtc CT_Mtc system CT_System { //^In type definition// @@ -210,8 +236,50 @@ type class C10 runs on CT_RunsOn mtc CT_Mtc system CT_System { //^In type defini } } +type class C11 extends C10 { //^In type definition// + public function f_inherited_comp_visibility(in integer p1 := cc_runs_on, //^In function definition// //^In formal parameter list// + in integer p2 := cc_mtc, + in integer p3 := cc_system, + in integer p4 := cc_comp) { //^In parameter// //^In default value// //There is no local or imported definition with name `cc_comp'// + log(cv_runs_on); + log(cv_mtc); + log(cv_system); + log(cv_comp); //^In log statement// //There is no local or imported definition with name `cv_comp'// + } +} + +type class C12 extends C10 runs on CT_RunsOn mtc CT_Mtc system CT_System { } + +type class C13 extends C11 //^In type definition// + runs on Comp //^In `runs on' clause// //The `runs on' component type of the subclass, `@oop_SE.Comp', is not compatible with the `runs on' component type of the superclass, `@oop_SE.CT_RunsOn'// + mtc Comp //^In `mtc' clause// //The `mtc' component type of the subclass, `@oop_SE.Comp', is not compatible with the `mtc' component type of the superclass, `@oop_SE.CT_Mtc'// + system Comp //^In `system' clause// //The `system' component type of the subclass, `@oop_SE.Comp', is not compatible with the `system' component type of the superclass, `@oop_SE.CT_System'// + { } + +type class C14 extends C11 + runs on CT_RunsOn2 + mtc CT_Mtc2 + system CT_System2 + { } + +function f_good_comp_usage() runs on CT_RunsOn mtc CT_Mtc system CT_System { + var C11 x := C11.create; +} + +function f_bad_comp_usage() runs on Comp mtc Comp system Comp { //^In function definition// + var C11 x := C11.create; //^In variable definition// //Runs on clause mismatch: A definition that runs on component type `@oop_SE.Comp' cannot create a value of class type `@oop_SE.C11', which runs on `@oop_SE.CT_RunsOn'// //Mtc clause mismatch: A definition that runs on component type `@oop_SE.Comp' cannot create a value of class type `@oop_SE.C11', which has `mtc' component type `@oop_SE.CT_Mtc'// //System clause mismatch: A definition that runs on component type `@oop_SE.Comp' cannot create a value of class type `@oop_SE.C11', which has `system' component type `@oop_SE.CT_System'// +} + +function f_no_comp_usage() { //^In function definition// + var C11 x := C11.create; //^In variable definition// //A definition without `runs on' clause cannot create a value of class type `@oop_SE.C11', which runs on component type `@oop_SE.CT_RunsOn'// +} + + +control { //^In control part// + var C11 x := C11.create; //^In variable definition// //A definition without `runs on' clause cannot create a value of class type `@oop_SE.C11', which runs on component type `@oop_SE.CT_RunsOn'// //Cannot create value of class type `@oop_SE.C11', which has an `mtc' clause, in the control part.// //Cannot create value of class type `@oop_SE.C11', which has a `system' clause, in the control part.// +} } with { - extension "anytype C0" //^In anytype field// //Class type `@oop_SE.C0' cannot be embedded into another type// + extension "anytype C0, object" //^In anytype field// //Class type `@oop_SE.C0' cannot be embedded into another type// //Class type `object' cannot be added to the anytype// } diff --git a/regression_test/oop/oop.ttcn b/regression_test/oop/oop.ttcn index f8213cc6e..dbef29742 100644 --- a/regression_test/oop/oop.ttcn +++ b/regression_test/oop/oop.ttcn @@ -357,6 +357,93 @@ testcase tc_references() runs on CT { } +type class Node { + var integer data; + var Node next; +} + +function f_test(in Node p1, inout Node p2, out Node p3, + in charstring p1_str, in charstring p2_str) return Node { + if (log2str(p1) != p1_str) { + setverdict(fail, "Invalid 'in' parameter value at start: ", p1, ", expected: ", p1_str); + } + if (log2str(p2) != p2_str) { + setverdict(fail, "Invalid 'inout' parameter value at start: ", p2, ", expected: ", p2_str); + } + if (p3 != null) { + setverdict(fail, "Invalid 'out' parameter value at start: ", p3, ", expected: null"); + } + var Node tmp := p2; + p3 := p1; + p1 := null; + p2 := null; + return tmp; +} + +testcase tc_function_pars_and_retval() runs on CT { + var Node x := Node.create(1, Node.create(2, Node.create(3, null))); + var Node y := Node.create(0, null); + var Node z := Node.create(-1, null); + var charstring x_str := log2str(x); + var charstring y_str := log2str(y); + var Node res := f_test(x, y, z, x_str, y_str); + if (log2str(x) != x_str) { + setverdict(fail, "Invalid 'in' parameter value at end: ", x, ", expected: ", x_str); + } + if (y != null) { + setverdict(fail, "Invalid 'inout' parameter value at end: ", y, ", expected: null"); + } + if (log2str(z) != x_str) { + setverdict(fail, "Invalid 'out' parameter value at end: ", z, ", expected: ", x_str); + } + if (log2str(res) != y_str) { + setverdict(fail, "Invalid return value: ", res, ", expected: ", y_str); + } + setverdict(pass); +} + +type class Node2 { + var object data; + var Node2 next; +} + +type class Something { + public function toString() return universal charstring { + return "Something"; + } +} + +testcase tc_object() runs on CT { + var object v_obj := Node.create(3, null); + var Node v_node := Node.create(3, null); + if (log2str(v_obj) != log2str(v_node)) { + setverdict(fail, "v_obj: ", v_obj, ", v_node: ", v_node); + } + if (v_obj.toString() != "Object") { + setverdict(fail, "v_obj.toString(): ", v_obj.toString()); + } + if (v_obj.toString() != v_node.toString()) { + setverdict(fail, "v_obj.toString(): ", v_obj.toString(), ", v_node.toString(): ", v_node.toString()); + } + var object v_obj2 := Something.create; + var Something v_smthn := Something.create; + if (v_obj2.toString() != "Something") { + setverdict(fail, "v_obj2.toString(): ", v_obj2.toString()); + } + if (v_obj2.toString() != v_smthn.toString()) { + setverdict(fail, "v_obj2.toString(): ", v_obj2.toString(), ", v_smthn.toString(): ", v_smthn.toString()); + } + if (v_obj2.toString()[3] != "e") { + setverdict(fail, "v_obj2.toString()[3]: ", v_obj2.toString()[3]); + } + var Node2 v_node2 := Node2.create(Something.create, Node2.create(Node.create(1, null), null)); + if (log2str(v_node2) != "Node2: { data := Something: { }, next := Node2: { data := Node: { data := 1, next := null }, next := null } }") { + setverdict(fail, "v_node2: ", v_node2); + } + setverdict(pass); +} + + control { execute(tc_members_and_methods()); execute(tc_logging()); @@ -364,6 +451,8 @@ control { execute(tc_null()); execute(tc_this()); execute(tc_references()); + execute(tc_function_pars_and_retval()); + execute(tc_object()); } } -- GitLab