From 8105324f1508ad0ec434f477d1235caea26f29a8 Mon Sep 17 00:00:00 2001 From: Botond Baranyi <botond.baranyi@ericsson.com> Date: Wed, 30 Mar 2022 18:33:48 +0200 Subject: [PATCH] OOP: updated presence checking operators and object methods (issue #596) Signed-off-by: Botond Baranyi <botond.baranyi@ericsson.com> --- compiler2/Type.cc | 19 +++---- compiler2/Type_codegen.cc | 16 +++--- compiler2/main.cc | 3 ++ compiler2/ttcn3/AST_ttcn3.cc | 14 +++-- compiler2/ttcn3/Ttcnstuff.cc | 44 +++++++++++++++ compiler2/ttcn3/Ttcnstuff.hh | 6 +++ core/OOP.hh | 102 +++++++++++++++++++---------------- regression_test/oop/oop.ttcn | 64 ++++++++++++++++++---- 8 files changed, 189 insertions(+), 79 deletions(-) diff --git a/compiler2/Type.cc b/compiler2/Type.cc index 1641dcb5d..3f59a33bc 100644 --- a/compiler2/Type.cc +++ b/compiler2/Type.cc @@ -1804,17 +1804,18 @@ namespace Common { return 0; } Ttcn::ClassTypeBody* class_ = t->get_class_type_body(); - bool base_toString = false; + bool is_object_method = false; if (!class_->has_local_ass_withId(id)) { - 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; + Ttcn::FormalParList* object_method_fplist = + Ttcn::ClassTypeBody::get_object_method_fplist(id.get_name()); + if (object_method_fplist != NULL) { + // the methods of the 'object' class are not in the AST, + // but they are inherited by every class + is_object_method = 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); + bool is_erroneous = object_method_fplist->fold_named_and_chk(parsed_pars, ap_list); if (is_erroneous) { delete ap_list; return 0; @@ -1823,7 +1824,7 @@ namespace Common { ap_list->set_my_scope(parsed_pars->get_my_scope()); ref->set_parameter_list(ap_list, NULL); } - t = get_pooltype(T_USTR); + t = Ttcn::ClassTypeBody::get_object_method_return_type(id.get_name()); // todo: set *last_method } else { @@ -1834,7 +1835,7 @@ namespace Common { return 0; } } - if (!base_toString) { + if (!is_object_method) { 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) diff --git a/compiler2/Type_codegen.cc b/compiler2/Type_codegen.cc index 5eece2f0d..649ae034a 100644 --- a/compiler2/Type_codegen.cc +++ b/compiler2/Type_codegen.cc @@ -3172,9 +3172,11 @@ void Type::generate_code_ispresentboundchosen(expression_struct *expr, const string& tmp_id2 = module->get_temporary_id(); const char *tmp_id_str = tmp_id.c_str(); const char *tmp_id2_str = tmp_id2.c_str(); + bool next_is_class = next_t->get_type_refd_last()->typetype == T_CLASS; if (t->typetype == T_CLASS) { - expr->expr = mputprintf(expr->expr, "const %s%s %s = %s->%s;\n", + expr->expr = mputprintf(expr->expr, "%s%s%s %s = %s->%s;\n", + next_is_class ? "" : "const ", next_t->get_genname_value(module).c_str(), is_template ? "_template" : "", tmp_id2_str, tmp_generalid_str, id.get_name().c_str()); @@ -3198,8 +3200,8 @@ void Type::generate_code_ispresentboundchosen(expression_struct *expr, } if (i != nof_refs - 1 || optype == ISCHOSEN) { - expr->expr = mputprintf(expr->expr, "%s = %s.is_bound();\n", - global_id.c_str(), tmp_id2_str); + expr->expr = mputprintf(expr->expr, "%s = %s.is_%s();\n", + global_id.c_str(), tmp_id2_str, next_is_class ? "present" : "bound"); } if (i == nof_refs - 1) { switch (optype) { @@ -3253,7 +3255,9 @@ void Type::generate_code_ispresentboundchosen(expression_struct *expr, const string& tmp_id = module->get_temporary_id(); const char *tmp_id_str = tmp_id.c_str(); - expr->expr = mputprintf(expr->expr, "const %s%s %s = %s->%s(", + bool next_is_class = next_t->get_type_refd_last()->typetype == T_CLASS; + expr->expr = mputprintf(expr->expr, "%s%s%s %s = %s->%s(", + next_is_class ? "" : "const ", next_t->get_genname_value(module).c_str(), is_template ? "_template" : "", tmp_id_str, tmp_generalid_str, id.get_name().c_str()); @@ -3261,8 +3265,8 @@ void Type::generate_code_ispresentboundchosen(expression_struct *expr, expr->expr = mputstr(expr->expr, ");\n"); if (i != nof_refs - 1 || optype == ISCHOSEN) { - expr->expr = mputprintf(expr->expr, "%s = %s.is_bound();\n", - global_id.c_str(), tmp_id_str); + expr->expr = mputprintf(expr->expr, "%s = %s.is_%s();\n", + global_id.c_str(), tmp_id_str, next_is_class ? "present" : "bound"); } if (i == nof_refs - 1) { switch (optype) { diff --git a/compiler2/main.cc b/compiler2/main.cc index c191b41fd..794d5087c 100644 --- a/compiler2/main.cc +++ b/compiler2/main.cc @@ -61,6 +61,7 @@ #include "AST.hh" #include "asn1/AST_asn1.hh" #include "ttcn3/AST_ttcn3.hh" +#include "ttcn3/Ttcnstuff.hh" #include "CodeGenHelper.hh" #include "Stopwatch.hh" @@ -1272,6 +1273,8 @@ int main(int argc, char *argv[]) } } + Ttcn::ClassTypeBody::object_method_cleanup(); + if (Kflag) { while (tcov_files != NULL) { tcov_file_list *next_file = tcov_files->next; diff --git a/compiler2/ttcn3/AST_ttcn3.cc b/compiler2/ttcn3/AST_ttcn3.cc index ba979174e..db942e97c 100644 --- a/compiler2/ttcn3/AST_ttcn3.cc +++ b/compiler2/ttcn3/AST_ttcn3.cc @@ -496,16 +496,13 @@ namespace Ttcn { Free(prev_expr); } const Identifier& id = *ref->get_id(); - // 'ass' is null if the 'toString' method from the 'object' class is called + // 'ass' is null if one of the methods 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()); FormalParList* fp_list = ass != NULL ? ass->get_FormalParList() : - new FormalParList; // the formal parameter list of 'toString' is empty + ClassTypeBody::get_object_method_fplist(id.get_name()); ref->get_actual_par_list()->generate_code_noalias(expr, fp_list); - if (ass == NULL) { - delete fp_list; - } expr->expr = mputc(expr->expr, ')'); type = ass != NULL ? ass->get_Type() : Common::Type::get_pooltype(Common::Type::T_USTR); @@ -1505,9 +1502,10 @@ namespace Ttcn { expression_struct isbound_expr; Code::init_expr(&isbound_expr); + Common::Type* ass_type = ass->get_Type(); isbound_expr.preamble = mputprintf(isbound_expr.preamble, - "boolean %s = %s.is_bound();\n", tmp_generalid_str, - ass_id_str); + "boolean %s = %s.is_%s();\n", tmp_generalid_str, ass_id_str, + ass_type->get_type_refd_last()->get_typetype() == Common::Type::T_CLASS ? "present" : "bound"); namedbool p_optype; if (optype == Value::OPTYPE_ISBOUND) { p_optype = ISBOUND; @@ -1520,7 +1518,7 @@ namespace Ttcn { } else { FATAL_ERROR("AST_ttcn3.cc::generate_code_ispresentboundchosen()"); } - ass->get_Type()->generate_code_ispresentboundchosen(&isbound_expr, &subrefs, my_scope->get_scope_mod_gen(), + ass_type->generate_code_ispresentboundchosen(&isbound_expr, &subrefs, my_scope->get_scope_mod_gen(), tmp_generalid, ass_id2, is_template, p_optype, field); expr->preamble = mputstr(expr->preamble, isbound_expr.preamble); diff --git a/compiler2/ttcn3/Ttcnstuff.cc b/compiler2/ttcn3/Ttcnstuff.cc index eb62de86e..cc737ae0e 100644 --- a/compiler2/ttcn3/Ttcnstuff.cc +++ b/compiler2/ttcn3/Ttcnstuff.cc @@ -3066,6 +3066,9 @@ namespace Ttcn { // ===== ClassTypeBody // ================================= + FormalParList* ClassTypeBody::object_toString_fplist = NULL; + FormalParList* ClassTypeBody::object_equals_fplist = NULL; + ClassTypeBody::ClassTypeBody(Common::Identifier* p_class_id, boolean p_external, boolean p_final, boolean p_abstract, boolean p_trait, Types* p_base_types, Reference* p_runs_on_ref, Reference* p_mtc_ref, Reference* p_system_ref, @@ -4511,4 +4514,45 @@ namespace Ttcn { Free(user_info); } + FormalParList* ClassTypeBody::get_object_method_fplist(const string& p_id) + { + if (p_id == string("toString")) { + if (object_toString_fplist == NULL) { + object_toString_fplist = new FormalParList; + } + return object_toString_fplist; + } + else if (p_id == string("equals")) { + if (object_equals_fplist == NULL) { + object_equals_fplist = new FormalParList; + FormalPar* fp = new FormalPar(FormalPar::A_PAR_VAL_IN, new Common::Type(Common::Type::T_CLASS), + new Common::Identifier(Common::Identifier::ID_TTCN, string("obj")), NULL); + object_equals_fplist->add_fp(fp); + } + return object_equals_fplist; + } + else { + return NULL; + } + } + + Common::Type* ClassTypeBody::get_object_method_return_type(const string& p_id) + { + if (p_id == string("toString")) { + return Common::Type::get_pooltype(Common::Type::T_USTR); + } + else if (p_id == string("equals")) { + return Common::Type::get_pooltype(Common::Type::T_BOOL); + } + else { + return NULL; + } + } + + void ClassTypeBody::object_method_cleanup() + { + delete object_toString_fplist; + delete object_equals_fplist; + } + } // namespace Ttcn diff --git a/compiler2/ttcn3/Ttcnstuff.hh b/compiler2/ttcn3/Ttcnstuff.hh index 9b5c9c51b..9f29c9fce 100644 --- a/compiler2/ttcn3/Ttcnstuff.hh +++ b/compiler2/ttcn3/Ttcnstuff.hh @@ -783,6 +783,8 @@ class ClassTypeBody : public Common::Scope, public Common::Location { bool checked; bool default_constructor; /// true if the class uses a default constructor map<FormalPar*, char> defpar_list; + static FormalParList* object_toString_fplist; + static FormalParList* object_equals_fplist; public: ClassTypeBody(Common::Identifier* p_class_id, boolean p_external, boolean p_final, @@ -832,6 +834,10 @@ public: void generate_code(output_struct* target); void generate_class_skeleton(output_struct* target); + + static FormalParList* get_object_method_fplist(const string& p_id); + static Common::Type* get_object_method_return_type(const string& p_id); + static void object_method_cleanup(); }; } diff --git a/core/OOP.hh b/core/OOP.hh index ecf8ffa5b..239ea48b3 100644 --- a/core/OOP.hh +++ b/core/OOP.hh @@ -17,51 +17,6 @@ #include "Logger.hh" #include <functional> -// OBJECT -// ------ - -class CLASS_BASE { - size_t ref_count; - boolean destructor; // true, if the destructor is currently running; - // also makes sure the object is not deleted again when inside the destructor - - CLASS_BASE(const CLASS_BASE&); // copy disabled - CLASS_BASE operator=(const CLASS_BASE&); // assignment disabled - boolean operator==(const CLASS_BASE&); // equality operator disabled -public: - CLASS_BASE(): ref_count(0), destructor(FALSE) {} - virtual ~CLASS_BASE() { - if (ref_count != 0) { - TTCN_error("Internal error: deleting an object with %lu reference(s) left.", ref_count); - } - } - void add_ref() { ++ref_count; } - boolean remove_ref() { - --ref_count; - if (destructor) { - return FALSE; - } - destructor = ref_count == 0; - return destructor; - } -}; - -class OBJECT : public CLASS_BASE { -private: - OBJECT(const OBJECT&); // copy disabled - OBJECT operator=(const OBJECT&); // assignment disabled - boolean operator==(const OBJECT&); // equality operator disabled -public: - OBJECT(): CLASS_BASE() { } - virtual ~OBJECT() { } - virtual void log() const { - TTCN_Logger::log_event_str("object: { }"); - } - virtual UNIVERSAL_CHARSTRING toString() { - return UNIVERSAL_CHARSTRING("Object"); - } - static const char* class_name() { return "object"; } -}; // OBJECT_REF // ---------- @@ -171,6 +126,10 @@ public: TTCN_error("Accessing a null reference."); } + const T* operator*() const { // de-referencing operator (for OBJECT::equals + return ptr; + } + T* operator->() { // de-referencing operator (for methods) if (ptr != NULL) { return ptr; @@ -195,11 +154,11 @@ public: } boolean is_bound() const { - return ptr != NULL; + return TRUE; } boolean is_value() const { - return ptr != NULL; + return TRUE; } boolean is_present() const { @@ -241,6 +200,55 @@ boolean operator!=(null_type, const OBJECT_REF<T>& right_val) { // inequality op return right_val.ptr != NULL; } +// OBJECT +// ------ + +class CLASS_BASE { + size_t ref_count; + boolean destructor; // true, if the destructor is currently running; + // also makes sure the object is not deleted again when inside the destructor + + CLASS_BASE(const CLASS_BASE&); // copy disabled + CLASS_BASE operator=(const CLASS_BASE&); // assignment disabled + boolean operator==(const CLASS_BASE&); // equality operator disabled +public: + CLASS_BASE(): ref_count(0), destructor(FALSE) {} + virtual ~CLASS_BASE() { + if (ref_count != 0) { + TTCN_error("Internal error: deleting an object with %lu reference(s) left.", ref_count); + } + } + void add_ref() { ++ref_count; } + boolean remove_ref() { + --ref_count; + if (destructor) { + return FALSE; + } + destructor = ref_count == 0; + return destructor; + } +}; + +class OBJECT : public CLASS_BASE { +private: + OBJECT(const OBJECT&); // copy disabled + OBJECT operator=(const OBJECT&); // assignment disabled + boolean operator==(const OBJECT&); // equality operator disabled +public: + OBJECT(): CLASS_BASE() { } + virtual ~OBJECT() { } + virtual void log() const { + TTCN_Logger::log_event_str("object: { }"); + } + virtual UNIVERSAL_CHARSTRING toString() { + return UNIVERSAL_CHARSTRING("Object"); + } + virtual BOOLEAN equals(const OBJECT_REF<OBJECT>& obj) { + return *obj == this; + } + static const char* class_name() { return "object"; } +}; + // EXCEPTION // --------- diff --git a/regression_test/oop/oop.ttcn b/regression_test/oop/oop.ttcn index bca21d5a4..cf9812e7b 100644 --- a/regression_test/oop/oop.ttcn +++ b/regression_test/oop/oop.ttcn @@ -351,11 +351,21 @@ testcase tc_this() runs on CT { setverdict(pass); } + +type class Node { + var integer data; + var Node next; + public function get_next() return Node { return next; } + public function f() return integer { return 1; } +} + testcase tc_references() runs on CT { var BaseClass v_base := BaseClass.create(4, { 1, 2, 4 }, "a", 'FF'O, 1.0); var SubClass v_sub := SubClass.create(4, { 1, 2, 4 }, "a", 'FF'O, 1.0); var FinalClass v_final := FinalClass.create(4, { 1, 2, 4 }, "a", 'FF'O, 1.0, 8, "x", -1.5, *); var BaseClass v_null := null; + var Node v_node := Node.create(2, null); + var FinalClass v_null2 := null; var BaseClass v_ref1; if (v_ref1 != null) { @@ -397,13 +407,13 @@ testcase tc_references() runs on CT { if (not isbound(v_sub)) { setverdict(fail, "#12"); } - if (isbound(v_null)) { + if (not isbound(v_null)) { setverdict(fail, "#13"); } if (not isvalue(v_final)) { setverdict(fail, "#14"); } - if (isvalue(v_null)) { + if (not isvalue(v_null)) { setverdict(fail, "#15"); } if (not ispresent(v_base)) { @@ -416,17 +426,40 @@ testcase tc_references() runs on CT { if (v_base2 != null) { setverdict(fail, "#18"); } + if (ispresent(v_null.get_var_temp())) { + setverdict(fail, "#19"); + } + if (ispresent(v_node.get_next())) { + setverdict(fail, "#20"); + } + if (ispresent(v_node.get_next().f())) { + setverdict(fail, "#21"); + } + if (isbound(v_null.get_var_temp())) { + setverdict(fail, "#22"); + } + if (not isbound(v_node.get_next())) { + setverdict(fail, "#23"); + } + if (isbound(v_node.get_next().f())) { + setverdict(fail, "#24"); + } + if (isvalue(v_null.get_var_temp())) { + setverdict(fail, "#25"); + } + if (not isvalue(v_node.get_next())) { + setverdict(fail, "#26"); + } + if (isvalue(v_node.get_next().f())) { + setverdict(fail, "#27"); + } + if (ischosen(v_null2.get_uni().cs)) { + setverdict(fail, "#28"); + } setverdict(pass); } -type class Node { - var integer data; - var Node next; - public function get_next() return Node { return next; } - public function f() return integer { return 1; } -} - 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) { @@ -508,6 +541,9 @@ type class Something { public function toString() return universal charstring { return "Something"; } + public function equals(object obj) return boolean { + return true; + } } testcase tc_object() runs on CT { @@ -537,6 +573,16 @@ testcase tc_object() runs on CT { if (log2str(v_node2.get_data()) != "Something" or log2str(v_node2.get_next().get_data()) != "Node") { setverdict(fail, "v_node2: ", v_node2); } + var object v_obj3 := v_obj; + if (not v_obj3.equals(v_obj)) { + setverdict(fail, "v_obj3 is not equal to v_obj"); + } + if (v_obj.equals(v_node)) { + setverdict(fail, "v_obj is equal to v_node"); + } + if (not v_smthn.equals(v_node)) { + setverdict(fail, "overriden 'equals' failed"); + } setverdict(pass); } -- GitLab