From 95745b3f66c5db0008729435b4ea07cb8b31812b Mon Sep 17 00:00:00 2001 From: Botond Baranyi <botond.baranyi@ericsson.com> Date: Thu, 10 Oct 2019 17:31:34 +0200 Subject: [PATCH] Implemented object-oriented features - stage 1 (bug 552011) Change-Id: Id2222ae91a23b3934e7c175aa163a3eec6f72cad Signed-off-by: Botond Baranyi <botond.baranyi@ericsson.com> --- compiler2/AST.hh | 1 + compiler2/Code.cc | 18 +- compiler2/Code.hh | 2 +- compiler2/Setting.cc | 9 + compiler2/Setting.hh | 3 + compiler2/Type.cc | 52 ++++- compiler2/Type.hh | 10 +- compiler2/Type_chk.cc | 3 + compiler2/Type_codegen.cc | 47 ++-- compiler2/asn1/AST_asn1.cc | 2 +- compiler2/asn1/Object.cc | 4 +- compiler2/main.cc | 12 +- compiler2/main.hh | 2 +- compiler2/ttcn3/AST_ttcn3.cc | 336 +++++++++++++++++++++------ compiler2/ttcn3/AST_ttcn3.hh | 52 +++++ compiler2/ttcn3/Ttcnstuff.cc | 331 ++++++++++++++++++++++++-- compiler2/ttcn3/Ttcnstuff.hh | 41 ++++ compiler2/ttcn3/compiler.h | 4 + compiler2/ttcn3/compiler.l | 26 +++ compiler2/ttcn3/compiler.y | 158 ++++++++++++- core/Makefile | 2 +- core/OOP.hh | 149 ++++++++++++ core/TTCN3.hh | 1 + regression_test/oop/.gitignore | 5 + regression_test/oop/ExternalClass.hh | 29 +++ regression_test/oop/Makefile | 55 +++++ regression_test/oop/oop.cfg | 24 ++ regression_test/oop/oop.ttcn | 121 ++++++++++ 28 files changed, 1375 insertions(+), 124 deletions(-) create mode 100644 core/OOP.hh create mode 100644 regression_test/oop/.gitignore create mode 100644 regression_test/oop/ExternalClass.hh create mode 100644 regression_test/oop/Makefile create mode 100644 regression_test/oop/oop.cfg create mode 100644 regression_test/oop/oop.ttcn diff --git a/compiler2/AST.hh b/compiler2/AST.hh index 6d5a6234b..dd0990488 100644 --- a/compiler2/AST.hh +++ b/compiler2/AST.hh @@ -525,6 +525,7 @@ namespace Common { A_EXT_FUNCTION_RTEMP, /**< ext. func that returns a template (TTCN-3) */ A_ALTSTEP, /**< altstep (TTCN-3) */ A_TESTCASE, /**< testcase (TTCN-3) */ + A_CONSTRUCTOR, /**< constructor (TTCN-3) */ A_PAR_VAL, /**< formal parameter (value) (TTCN-3) */ A_PAR_VAL_IN, /**< formal parameter (in value) (TTCN-3) */ A_PAR_VAL_OUT, /**< formal parameter (out value) (TTCN-3) */ diff --git a/compiler2/Code.cc b/compiler2/Code.cc index 9270ede59..aaeca1094 100644 --- a/compiler2/Code.cc +++ b/compiler2/Code.cc @@ -82,6 +82,8 @@ namespace Common { output->intervals.static_conversion_function_bodies = NULL; output->intervals.static_function_bodies = NULL; } + output->temp.constructor_init = NULL; + output->temp.constructor = NULL; } void Code::merge_output(output_struct *dest, output_struct *src) @@ -177,6 +179,8 @@ namespace Common { Free(output->intervals.function_bodies); Free(output->intervals.static_conversion_function_bodies); Free(output->intervals.static_function_bodies); + Free(output->temp.constructor_init); + Free(output->temp.constructor); init_output(output, TRUE); } @@ -189,12 +193,16 @@ namespace Common { cdef->post = NULL; } - void Code::merge_cdef(output_struct *dest, const_def *cdef) + void Code::merge_cdef(output_struct *dest, const_def *cdef, boolean in_class) { - dest->header.global_vars = mputstr(dest->header.global_vars, cdef->decl); - dest->source.global_vars = mputstr(dest->source.global_vars, cdef->def); - dest->functions.pre_init = mputstr(dest->functions.pre_init, cdef->init); - dest->functions.post_init = mputstr(dest->functions.post_init, cdef->post); + char*& header = in_class ? dest->header.class_defs : dest->header.global_vars; + header = mputstr(header, cdef->decl); + char*& source = in_class ? dest->temp.constructor_init : dest->source.global_vars; + source = mputstr(source, cdef->def); + char*& pre_init = in_class ? dest->temp.constructor : dest->functions.pre_init; + pre_init = mputstr(pre_init, cdef->init); + char*& post_init = in_class ? dest->temp.constructor : dest->functions.post_init; + post_init = mputstr(post_init, cdef->post); } void Code::free_cdef(const_def *cdef) diff --git a/compiler2/Code.hh b/compiler2/Code.hh index 609880805..6466c352f 100644 --- a/compiler2/Code.hh +++ b/compiler2/Code.hh @@ -36,7 +36,7 @@ namespace Common { static void free_output(output_struct *output); static void init_cdef(const_def *cdef); - static void merge_cdef(output_struct *dest, const_def *cdef); + static void merge_cdef(output_struct *dest, const_def *cdef, boolean in_class = FALSE); static void free_cdef(const_def *cdef); static void init_expr(expression_struct *expr); diff --git a/compiler2/Setting.cc b/compiler2/Setting.cc index cdb91279e..cb532aea0 100644 --- a/compiler2/Setting.cc +++ b/compiler2/Setting.cc @@ -38,6 +38,7 @@ #include "main.hh" #include "ttcn3/profiler.h" #include "ttcn3/Attributes.hh" +#include "ttcn3/Ttcnstuff.hh" namespace Common { @@ -604,6 +605,14 @@ namespace Common { get_scope_name().c_str()); return 0; } + + const Ttcn::ClassTypeBody* Scope::get_scope_class() const + { + if (parent_scope != NULL) { + return parent_scope->get_scope_class(); + } + return NULL; + } bool Scope::has_ass_withId(const Identifier& p_id) { diff --git a/compiler2/Setting.hh b/compiler2/Setting.hh index d140ddc6d..3f559255d 100644 --- a/compiler2/Setting.hh +++ b/compiler2/Setting.hh @@ -62,6 +62,7 @@ namespace Ttcn { class ErroneousDescriptors; class transparency_holder; class Statement; + class ClassTypeBody; } // namespace Ttcn namespace Common { @@ -613,6 +614,8 @@ public: * Module. */ virtual Module* get_scope_mod(); virtual Module* get_scope_mod_gen(); + virtual bool is_class_scope() const { return false; } + virtual const Ttcn::ClassTypeBody* get_scope_class() const; /** Returns the assignment referenced by \a p_ref. If no such * node, 0 is returned. */ virtual Assignment* get_ass_bySRef(Ref_simple *p_ref) =0; diff --git a/compiler2/Type.cc b/compiler2/Type.cc index 6e8efcf1f..d41ef23c5 100644 --- a/compiler2/Type.cc +++ b/compiler2/Type.cc @@ -130,6 +130,7 @@ namespace Common { "altstep reference(TTCN-3)", // T_ALTSTEP "testcase reference(TTCN-3)", // T_TESTCASE "anytype(TTCN-3)", // T_ANYTYPE + "class(TTCN-3)" // T_CLASS }; // ================================= @@ -579,6 +580,9 @@ namespace Common { u.fatref.returns_template = false; u.fatref.template_restriction = TR_NONE; break; + case T_CLASS: + u.class_ = p.u.class_->clone(); + break; default: FATAL_ERROR("Type::Type()"); } // switch @@ -749,6 +753,9 @@ namespace Common { delete u.fatref.runs_on.ref; delete u.fatref.system.ref; break; + case T_CLASS: + delete u.class_; + break; default: FATAL_ERROR("Type::clean_up()"); } // switch @@ -1120,7 +1127,23 @@ namespace Common { u.fatref.returns_template = false; u.fatref.template_restriction = TR_NONE; } - + + Type::Type(typetype_t p_tt, Ttcn::ClassTypeBody* p_class) + : Governor(S_T), typetype(p_tt) + { + init(); + switch (typetype) { + case T_CLASS: + if (p_class == NULL) { + FATAL_ERROR("Type::Type"); + } + u.class_ = p_class; + break; + default: + FATAL_ERROR("Type::Type"); + } + } + Type::~Type() { clean_up(); @@ -1446,6 +1469,9 @@ namespace Common { if (u.fatref.system.ref) u.fatref.system.ref->set_fullname(p_fullname + ".<system_type>"); break; + case T_CLASS: + u.class_->set_fullname(p_fullname); + break; default: break; } // switch @@ -1522,6 +1548,9 @@ namespace Common { if (u.fatref.system.ref) u.fatref.system.ref->set_my_scope(p_scope); break; + case T_CLASS: + u.class_->set_my_scope(p_scope); + break; default: break; } // switch @@ -3923,6 +3952,7 @@ namespace Common { case T_FUNCTION: case T_ALTSTEP: case T_TESTCASE: + case T_CLASS: return p_tt1 == p_tt2; case T_OSTR: return p_tt2==T_OSTR || (!p_is_asn11 && p_tt2==T_ANY); @@ -4300,6 +4330,7 @@ namespace Common { case T_FUNCTION: case T_ALTSTEP: case T_TESTCASE: + case T_CLASS: // TODO: Compatibility. is_type_comp = ( t1 == t2 ); break; @@ -5222,6 +5253,7 @@ namespace Common { case T_FUNCTION: case T_ALTSTEP: case T_TESTCASE: + case T_CLASS: // user-defined structured types must be identical return t1 == t2; case T_ARRAY: @@ -6034,6 +6066,14 @@ namespace Common { FATAL_ERROR("Type::Returns_template()"); return u.fatref.returns_template; } + + Ttcn::ClassTypeBody* Type::get_class_type_body() + { + if (typetype != T_CLASS) { + FATAL_ERROR("Type::get_class_type_body()"); + } + return u.class_; + } void Type::add_tag(Tag *p_tag) { @@ -6680,6 +6720,7 @@ namespace Common { case T_FUNCTION: // TTCN-3 case T_ALTSTEP: // TTCN-3 case T_TESTCASE: // TTCN-3 + case T_CLASS: // TTCN-3 return memory.remember(t, ANSWER_NO); case T_UNDEF: @@ -7387,7 +7428,8 @@ namespace Common { case T_SIGNATURE: case T_FUNCTION: case T_ALTSTEP: - case T_TESTCASE: { + case T_TESTCASE: + case T_CLASS: { // user-defined types // always use the qualified name (including module identifier) string ret_val(t_scope->get_scope_mod_gen()->get_modid().get_name()); @@ -7424,6 +7466,7 @@ 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()); @@ -8065,6 +8108,10 @@ namespace Common { u.fatref.system.ref->dump(level+2); } break; + case T_CLASS: + DEBUG(level,"Type: class"); + u.class_->dump(level + 1); + break; default: DEBUG(level, "type (%d - %s)", typetype, const_cast<Type*>(this)->get_stringRepr().c_str()); } // switch @@ -8222,6 +8269,7 @@ namespace Common { switch (typetype) { case T_DEFAULT: case T_PORT: + case T_CLASS: return true; case T_FUNCTION: case T_ALTSTEP: diff --git a/compiler2/Type.hh b/compiler2/Type.hh index b8f524c79..d8a6de9da 100644 --- a/compiler2/Type.hh +++ b/compiler2/Type.hh @@ -77,6 +77,7 @@ namespace Ttcn { class PortTypeBody; class Def_Type; class Ref_pard; + class ClassTypeBody; /** Stores the modifier of an attribute */ enum attribute_modifier_t { @@ -199,6 +200,7 @@ namespace Common { T_ALTSTEP, /**< altstep reference (TTCN-3) */ T_TESTCASE, /**< testcase reference (TTCN-3) */ T_ANYTYPE, /**< anytype (TTCN-3) */ + T_CLASS, /**< class (TTCN-3) */ // WRITE new type before this line T_LAST }; //DO NOT FORGET to update type_as_string[] in Type.cc @@ -466,6 +468,7 @@ namespace Common { template_restriction_t template_restriction; bool is_startable; } fatref; + Ttcn::ClassTypeBody* class_; } u; static const char* type_as_string[]; @@ -587,6 +590,7 @@ namespace Common { /// Create a TTCN3 testcase Type(typetype_t p_tt,Ttcn::FormalParList *p_params, Ttcn::Reference* p_runs_on_ref, Ttcn::Reference *p_system_ref); + Type(typetype_t p_tt, Ttcn::ClassTypeBody* p_class); /** @} */ virtual ~Type(); /** This function must be called to clean up the pool types, @@ -1165,6 +1169,8 @@ namespace Common { bool get_fat_runs_on_self(); /** Applicable only if typetype == T_FUNCTION */ bool get_returns_template(); + /** Applicably only if typetype == T_CLASS */ + Ttcn::ClassTypeBody* get_class_type_body(); /** Retruns true if it is a tagged type.*/ bool is_tagged() const {return tags!=0;} @@ -1320,11 +1326,11 @@ namespace Common { * Value or Template object in the AST (e.g. in case of variables). */ void generate_code_object(const_def *cdef, Scope* p_scope, const string& name, const char *prefix, bool is_template, - bool has_err_descr); + bool has_err_descr, bool in_class); /** Generates the declaration and definition of a C++ value or template * object governed by \a this into \a cdef based on the attributes of * \a p_setting. */ - void generate_code_object(const_def *cdef, GovernedSimple *p_setting); + void generate_code_object(const_def *cdef, GovernedSimple *p_setting, bool in_class); private: virtual string create_stringRepr(); public: diff --git a/compiler2/Type_chk.cc b/compiler2/Type_chk.cc index 5ab6baaa7..5c302706e 100644 --- a/compiler2/Type_chk.cc +++ b/compiler2/Type_chk.cc @@ -186,6 +186,9 @@ void Type::chk() case T_TESTCASE: chk_Fat(); break; + case T_CLASS: + u.class_->chk(); + break; default: FATAL_ERROR("Type::chk()"); } // switch diff --git a/compiler2/Type_codegen.cc b/compiler2/Type_codegen.cc index 574cd68a6..0274341ff 100644 --- a/compiler2/Type_codegen.cc +++ b/compiler2/Type_codegen.cc @@ -101,6 +101,9 @@ void Type::generate_code(output_struct *target) case T_TESTCASE: generate_code_Fat(target); break; + case T_CLASS: + u.class_->generate_code(target); + break; default: generate_code_alias(target); break; @@ -582,7 +585,7 @@ void Type::generate_code_xerdescriptor(output_struct* target) dfe_str = xerattrib->defaultValue_->get_genname_own().c_str(); const_def cdef; Code::init_cdef(&cdef); - t->generate_code_object(&cdef, xerattrib->defaultValue_); + t->generate_code_object(&cdef, xerattrib->defaultValue_, false); // Generate the initialization of the dfe values in the post init function // because the module params are not initialized in the pre init function target->functions.post_init = xerattrib->defaultValue_->generate_code_init @@ -1845,7 +1848,7 @@ void Type::generate_code_Se(output_struct *target) Value *defval = cf->get_defval(); const_def cdef; Code::init_cdef(&cdef); - type->generate_code_object(&cdef, defval); + type->generate_code_object(&cdef, defval, false); cdef.init = defval->generate_code_init (cdef.init, defval->get_lhs_name().c_str()); Code::merge_cdef(target, &cdef); @@ -3395,7 +3398,8 @@ bool Type::has_done_attribute() } void Type::generate_code_object(const_def *cdef, Scope *p_scope, - const string& name, const char *prefix, bool is_template, bool has_err_descr) + const string& name, const char *prefix, bool is_template, bool has_err_descr, + bool in_class) { string type_name; if (is_template) type_name = get_genname_template(p_scope); @@ -3403,23 +3407,32 @@ void Type::generate_code_object(const_def *cdef, Scope *p_scope, const char *name_str = name.c_str(); const char *type_name_str = type_name.c_str(); if (prefix) { - cdef->decl = mputprintf(cdef->decl, "extern const %s& %s;\n", - type_name_str, name_str); - if (split_to_slices || has_err_descr) { - cdef->decl = mputprintf(cdef->decl, "extern %s %s%s;\n", type_name_str, prefix, name_str); - } - cdef->def = mputprintf(cdef->def, "%s%s %s%s;\n" - "const %s& %s = %s%s;\n", split_to_slices || has_err_descr ? "" : "static ", - type_name_str, prefix, name_str, type_name_str, name_str, prefix, name_str); + cdef->decl = mputprintf(cdef->decl, "%sconst %s& %s;\n", + in_class ? "" : "extern ", type_name_str, name_str); + if (split_to_slices || has_err_descr || in_class) { + cdef->decl = mputprintf(cdef->decl, "%s%s %s%s;\n", + in_class ? "" : "extern ", type_name_str, prefix, name_str); + } + if (!in_class) { + cdef->def = mputprintf(cdef->def, "%s%s %s%s;\n" + "const %s& %s = %s%s;\n", split_to_slices || has_err_descr ? "" : "static ", + type_name_str, prefix, name_str, type_name_str, name_str, prefix, name_str); + } + else { + cdef->def = mputprintf(cdef->def, ", %s(%s%s)", name_str, prefix, name_str); + } } else { - cdef->decl = mputprintf(cdef->decl, "extern %s %s;\n", - type_name_str, name_str); - cdef->def = mputprintf(cdef->def, "%s %s;\n", - type_name_str, name_str); + cdef->decl = mputprintf(cdef->decl, "%s%s %s;\n", + in_class ? "" : "extern ", type_name_str, name_str); + if (!in_class) { + cdef->def = mputprintf(cdef->def, "%s %s;\n", + type_name_str, name_str); + } } } -void Type::generate_code_object(const_def *cdef, GovernedSimple *p_setting) +void Type::generate_code_object(const_def *cdef, GovernedSimple *p_setting, + bool in_class) { bool is_template = FALSE; switch (p_setting->get_st()) { @@ -3442,7 +3455,7 @@ void Type::generate_code_object(const_def *cdef, GovernedSimple *p_setting) // regenerated generate_code_object(cdef, p_setting->get_my_scope(), p_setting->get_genname_own(), p_setting->get_genname_prefix(), - is_template, use_runtime_2); + is_template, use_runtime_2, in_class); } void Type::generate_json_schema(JSON_Tokenizer& json, bool embedded, bool as_value) diff --git a/compiler2/asn1/AST_asn1.cc b/compiler2/asn1/AST_asn1.cc index 9c5b1f0a2..cae93d8ae 100644 --- a/compiler2/asn1/AST_asn1.cc +++ b/compiler2/asn1/AST_asn1.cc @@ -1872,7 +1872,7 @@ namespace Asn { left->generate_code(target); const_def cdef; Code::init_cdef(&cdef); - left->generate_code_object(&cdef, right); + left->generate_code_object(&cdef, right, false); cdef.init = right->generate_code_init(cdef.init, right->get_lhs_name().c_str()); Code::merge_cdef(target, &cdef); diff --git a/compiler2/asn1/Object.cc b/compiler2/asn1/Object.cc index 117557cb4..c52a1d481 100644 --- a/compiler2/asn1/Object.cc +++ b/compiler2/asn1/Object.cc @@ -722,7 +722,7 @@ namespace Asn { if(defval) { const_def cdef; Code::init_cdef(&cdef); - fixtype->generate_code_object(&cdef, defval); + fixtype->generate_code_object(&cdef, defval, false); cdef.init = defval->generate_code_init(cdef.init, defval->get_lhs_name().c_str()); Code::merge_cdef(target, &cdef); @@ -1316,7 +1316,7 @@ namespace Asn { const_def cdef; Code::init_cdef(&cdef); Type *type = setting->get_my_governor(); - type->generate_code_object(&cdef, setting); + type->generate_code_object(&cdef, setting, false); cdef.init = setting->generate_code_init(cdef.init, setting->get_lhs_name().c_str()); Code::merge_cdef(target, &cdef); diff --git a/compiler2/main.cc b/compiler2/main.cc index e98ab2892..6e4254a56 100644 --- a/compiler2/main.cc +++ b/compiler2/main.cc @@ -100,7 +100,7 @@ boolean generate_skeleton = FALSE, force_overwrite = FALSE, warnings_for_bad_variants = FALSE, debugger_active = FALSE, legacy_unbound_union_fields = FALSE, split_to_slices = FALSE, legacy_untagged_union, disable_user_info, legacy_codec_handling = FALSE, - realtime_features = FALSE; + realtime_features = FALSE, oop_features = FALSE; // Default code splitting mode is set to 'no splitting'. CodeGenHelper::split_type code_splitting_mode = CodeGenHelper::SPLIT_NONE; @@ -503,7 +503,7 @@ int main(int argc, char *argv[]) Sflag = false, Kflag = false, jflag = false, zflag = false, Fflag = false, Mflag = false, Eflag = false, nflag = false, Bflag = false, errflag = false, print_usage = false, ttcn2json = false, Nflag = false, Dflag = false, - eflag = false, Oflag = false, Iflag = false; + eflag = false, Oflag = false, Iflag = false, kflag = false; CodeGenHelper cgh; @@ -599,7 +599,7 @@ int main(int argc, char *argv[]) if (!ttcn2json) { for ( ; ; ) { - int c = getopt(argc, argv, "aA:bBcC:dDeEfFgiIjJ:K:lLMnNo:OpP:qQ:rRsStT:uU:vV:wxXyYz:0-"); + int c = getopt(argc, argv, "aA:bBcC:dDeEfFgiIjJ:kK:lLMnNo:OpP:qQ:rRsStT:uU:vV:wxXyYz:0-"); if (c == -1) break; switch (c) { case 'a': @@ -680,6 +680,10 @@ int main(int argc, char *argv[]) case 'J': file_list_file_name = optarg; break; + case 'k': + SET_FLAG(k); + oop_features = TRUE; + break; case 'K': SET_FLAG(K); tcov_file_name = optarg; @@ -846,7 +850,7 @@ int main(int argc, char *argv[]) bflag || fflag || iflag || lflag || oflag || pflag || qflag || rflag || sflag || tflag || uflag || wflag || xflag || Xflag || Rflag || Uflag || yflag || Kflag || jflag || zflag || Fflag || Mflag || Eflag || - nflag || Bflag || Dflag || eflag || Oflag || Iflag || s0flag) { + nflag || Bflag || Dflag || eflag || Oflag || Iflag || s0flag || kflag) { errflag = true; print_usage = true; } diff --git a/compiler2/main.hh b/compiler2/main.hh index fdd0c12c7..6f020af52 100644 --- a/compiler2/main.hh +++ b/compiler2/main.hh @@ -52,7 +52,7 @@ extern boolean generate_skeleton, force_overwrite, include_line_info, implicit_json_encoding, json_refs_for_all_types, force_gen_seof, omit_in_value_list, warnings_for_bad_variants, debugger_active, legacy_unbound_union_fields, split_to_slices, legacy_untagged_union, - disable_user_info, legacy_codec_handling, realtime_features; + disable_user_info, legacy_codec_handling, realtime_features, oop_features; extern const char *expected_platform; diff --git a/compiler2/ttcn3/AST_ttcn3.cc b/compiler2/ttcn3/AST_ttcn3.cc index dac7ed53a..97477618e 100644 --- a/compiler2/ttcn3/AST_ttcn3.cc +++ b/compiler2/ttcn3/AST_ttcn3.cc @@ -1233,6 +1233,14 @@ namespace Ttcn { { return get_parent_scope()->get_ass_bySRef(p_ref); } + + bool NameBridgingScope::is_class_scope() const + { + if (parent_scope == NULL) { + FATAL_ERROR("NameBridgingScope::is_class_scope()"); + } + return parent_scope->is_class_scope(); + } // ================================= // ===== RunsOnScope @@ -2013,6 +2021,14 @@ namespace Ttcn { Ttcn::Definition* Definitions::get_raw_ass_byIndex(size_t p_i) { return ass_v[p_i]; } + + bool Definitions::is_class_scope() const + { + if (parent_scope == NULL) { + FATAL_ERROR("Definitions::is_class_scope()"); + } + return parent_scope->is_class_scope(); + } void Definitions::chk_uniq() { @@ -2089,7 +2105,16 @@ namespace Ttcn { void Definitions::generate_code(output_struct* target) { - for(size_t i = 0; i < ass_v.size(); i++) ass_v[i]->generate_code(target); + bool in_class = parent_scope->is_class_scope(); + for (size_t i = 0; i < ass_v.size(); i++) { + if (in_class && ass_v[i]->get_asstype() != Common::Assignment::A_CONSTRUCTOR) { + visibility_t vis = ass_v[i]->get_visibility(); + target->header.class_defs = mputprintf(target->header.class_defs, + "%s:\n", vis == PUBLIC ? "public" : (vis == PRIVATE ? "private" : + "protected")); + } + ass_v[i]->generate_code(target); + } } void Definitions::generate_code(CodeGenHelper& cgh) { @@ -3784,7 +3809,8 @@ namespace Ttcn { type->generate_code(target); const_def cdef; Code::init_cdef(&cdef); - type->generate_code_object(&cdef, value); + bool in_class = my_scope->is_class_scope(); + type->generate_code_object(&cdef, value, in_class); if (value->is_unfoldable()) { cdef.post = update_location_object(cdef.post); cdef.post = value->generate_code_init(cdef.post, @@ -3794,7 +3820,7 @@ namespace Ttcn { cdef.init = value->generate_code_init(cdef.init, value->get_lhs_name().c_str()); } - Code::merge_cdef(target, &cdef); + Code::merge_cdef(target, &cdef, in_class); Code::free_cdef(&cdef); } @@ -4069,7 +4095,7 @@ namespace Ttcn { Code::init_cdef(&cdef); const string& t_genname = get_genname(); const char *name = t_genname.c_str(); - type->generate_code_object(&cdef, my_scope, t_genname, "modulepar_", false, false); + type->generate_code_object(&cdef, my_scope, t_genname, "modulepar_", false, false, false); if (def_value) { cdef.init = update_location_object(cdef.init); cdef.init = def_value->generate_code_init(cdef.init, def_value->get_lhs_name().c_str()); @@ -4248,7 +4274,7 @@ namespace Ttcn { Code::init_cdef(&cdef); const string& t_genname = get_genname(); const char *name = t_genname.c_str(); - type->generate_code_object(&cdef, my_scope, t_genname, "modulepar_", true, false); + type->generate_code_object(&cdef, my_scope, t_genname, "modulepar_", true, false, false); if (def_template) { cdef.init = update_location_object(cdef.init); cdef.init = def_template->generate_code_init(cdef.init, def_template->get_lhs_name().c_str()); @@ -4761,7 +4787,8 @@ namespace Ttcn { // non-parameterized template const_def cdef; Code::init_cdef(&cdef); - type->generate_code_object(&cdef, body); + bool in_class = my_scope->is_class_scope(); + type->generate_code_object(&cdef, body, in_class); cdef.init = update_location_object(cdef.init); if (base_template) { // modified template @@ -4812,12 +4839,12 @@ namespace Ttcn { body->get_lhs_name().c_str(), body->get_lhs_name().c_str(), static_cast<unsigned long>( body->get_err_descr()->get_descr_index(NULL) )); } - target->header.global_vars = mputstr(target->header.global_vars, - cdef.decl); - target->source.global_vars = mputstr(target->source.global_vars, - cdef.def); - target->functions.post_init = mputstr(target->functions.post_init, - cdef.init); + char*& header = in_class ? target->header.class_defs : target->header.global_vars; + header = mputstr(header, cdef.decl); + char*& source = in_class ? target->temp.constructor_init : target->source.global_vars; + source = mputstr(source, cdef.def); + char*& init = in_class ? target->temp.constructor : target->functions.post_init; + init = mputstr(init, cdef.init); Code::free_cdef(&cdef); } } @@ -5076,12 +5103,15 @@ namespace Ttcn { type->generate_code(target); const_def cdef; Code::init_cdef(&cdef); - type->generate_code_object(&cdef, my_scope, get_genname(), 0, false, false); - Code::merge_cdef(target, &cdef); + bool in_class = my_scope->is_class_scope(); + type->generate_code_object(&cdef, my_scope, get_genname(), 0, false, false, + in_class); + Code::merge_cdef(target, &cdef, in_class); Code::free_cdef(&cdef); if (initial_value) { - target->functions.init_comp = - initial_value->generate_code_init(target->functions.init_comp, + char*& init = in_class ? target->temp.constructor : + target->functions.init_comp; + init = initial_value->generate_code_init(init, initial_value->get_lhs_name().c_str()); } else if (clean_up) { // No initial value. target->functions.init_comp = mputprintf(target->functions.init_comp, @@ -5288,22 +5318,24 @@ namespace Ttcn { type->generate_code(target); const_def cdef; Code::init_cdef(&cdef); - type->generate_code_object(&cdef, my_scope, get_genname(), 0, true, false); - Code::merge_cdef(target, &cdef); + bool in_class = my_scope->is_class_scope(); + type->generate_code_object(&cdef, my_scope, get_genname(), 0, true, false, + in_class); + Code::merge_cdef(target, &cdef, in_class); Code::free_cdef(&cdef); if (initial_value) { + char*& init = in_class ? target->temp.constructor : + target->functions.init_comp; if (Common::Type::T_SEQOF == initial_value->get_my_governor()->get_typetype() || Common::Type::T_ARRAY == initial_value->get_my_governor()->get_typetype()) { - target->functions.init_comp = mputprintf(target->functions.init_comp, - "%s.remove_all_permutations();\n", initial_value->get_lhs_name().c_str()); + init = mputprintf(init, "%s.remove_all_permutations();\n", + initial_value->get_lhs_name().c_str()); } - target->functions.init_comp = - initial_value->generate_code_init(target->functions.init_comp, + init = initial_value->generate_code_init(init, initial_value->get_lhs_name().c_str()); if (template_restriction!=TR_NONE && gen_restriction_check) - target->functions.init_comp = Template::generate_restriction_check_code( - target->functions.init_comp, initial_value->get_lhs_name().c_str(), - template_restriction); + init = Template::generate_restriction_check_code(init, + initial_value->get_lhs_name().c_str(), template_restriction); } else if (clean_up) { // No initial value. // Always reset component variables/variable templates on component // reinitialization. Fix for HM79493. @@ -5649,54 +5681,82 @@ namespace Ttcn { const string& t_genname = get_genname(); const char *genname_str = t_genname.c_str(); const string& dispname = id->get_dispname(); + bool in_class = my_scope->is_class_scope(); + char*& header = in_class ? target->header.class_defs : + target->header.global_vars; + char*& source = in_class ? target->temp.constructor : + target->source.global_vars; + char*& pre_init = in_class ? target->temp.constructor : + target->functions.pre_init; + char*& post_init = in_class ? target->temp.constructor : + target->functions.post_init; if (dimensions) { // timer array const string& array_type = dimensions->get_timer_type(); const char *array_type_str = array_type.c_str(); - target->header.global_vars = mputprintf(target->header.global_vars, - "extern %s %s;\n", array_type_str, genname_str); - target->source.global_vars = mputprintf(target->source.global_vars, - "%s %s;\n", array_type_str, genname_str); - target->functions.pre_init = mputstr(target->functions.pre_init, "{\n" + header = mputprintf(header, + "%s%s %s;\n", in_class ? "" : "extern ", array_type_str, genname_str); + if (!in_class) { + source = mputprintf(source, "%s %s;\n", array_type_str, genname_str); + } + pre_init = mputstr(pre_init, "{\n" "static const char * const timer_name = \""); - target->functions.pre_init = mputstr(target->functions.pre_init, + pre_init = mputstr(pre_init, dispname.c_str()); - target->functions.pre_init = mputprintf(target->functions.pre_init, - "\";\n" + pre_init = mputprintf(pre_init, "\";\n" "%s.set_name(timer_name);\n" "}\n", genname_str); - if (default_duration) target->functions.post_init = - generate_code_array_duration(target->functions.post_init, genname_str, + if (default_duration) post_init = + generate_code_array_duration(post_init, genname_str, default_duration); } else { // single timer - target->header.global_vars = mputprintf(target->header.global_vars, - "extern TIMER %s;\n", genname_str); + header = mputprintf(header, "%sTIMER %s;\n", + in_class ? "" : "extern ", genname_str); if (default_duration) { // has default duration Value *v = default_duration->get_value_refd_last(); if (v->get_valuetype() == Value::V_REAL) { // duration is known at compilation time -> set in the constructor - target->source.global_vars = mputprintf(target->source.global_vars, - "TIMER %s(\"%s\", %s);\n", genname_str, dispname.c_str(), - v->get_single_expr().c_str()); + if (in_class) { + source = mputprintf(source, + "%s.set_name(\"%s\");\n" + "%s.set_default_duration(%s);\n", + genname_str, dispname.c_str(), + genname_str, v->get_single_expr().c_str()); + } + else { + source = mputprintf(source, "TIMER %s(\"%s\", %s);\n", + genname_str, dispname.c_str(), v->get_single_expr().c_str()); + } } else { // duration is known only at runtime -> set in post_init - target->source.global_vars = mputprintf(target->source.global_vars, - "TIMER %s(\"%s\");\n", genname_str, dispname.c_str()); + if (in_class) { + source = mputprintf(source, "%s.set_name(\"%s\");\n", + genname_str, dispname.c_str()); + } + else { + source = mputprintf(source, "TIMER %s(\"%s\");\n", + genname_str, dispname.c_str()); + } expression_struct expr; Code::init_expr(&expr); expr.expr = mputprintf(expr.expr, "%s.set_default_duration(", genname_str); default_duration->generate_code_expr(&expr); expr.expr = mputc(expr.expr, ')'); - target->functions.post_init = - Code::merge_free_expr(target->functions.post_init, &expr); + post_init = Code::merge_free_expr(post_init, &expr); } } else { // does not have default duration - target->source.global_vars = mputprintf(target->source.global_vars, - "TIMER %s(\"%s\");\n", genname_str, dispname.c_str()); + if (in_class) { + source = mputprintf(source, "%s.set_name(\"%s\");\n", + genname_str, dispname.c_str()); + } + else { + source = mputprintf(source, "TIMER %s(\"%s\");\n", + genname_str, dispname.c_str()); + } } } } @@ -6669,27 +6729,34 @@ namespace Ttcn { // smart formal parameter list (names of unused parameters are omitted) char *formal_par_list = fp_list->generate_code(memptystr()); fp_list->generate_code_defval(target); + + bool in_class = my_scope->is_class_scope(); + char*& header = in_class ? target->header.class_defs : + target->header.function_prototypes; + char*& source = in_class ? target->source.methods : + target->source.function_bodies; + // function prototype - target->header.function_prototypes = - mputprintf(target->header.function_prototypes, "%s%s %s(%s);\n", - get_PortType() && clean_up ? "" : "extern ", - return_type_str, genname_str, formal_par_list); + header = mputprintf(header, "%s%s %s(%s);\n", + get_PortType() && clean_up ? "" : (in_class ? "virtual " : "extern "), + return_type_str, genname_str, formal_par_list); // function body - target->source.function_bodies = mputprintf(target->source.function_bodies, + source = mputprintf(source, "%s %s%s%s%s(%s)\n" "{\n" "%s" "}\n\n", return_type_str, - port_type && clean_up ? port_type->get_genname_own().c_str() : "", + port_type && clean_up ? port_type->get_genname_own().c_str() : + (in_class ? my_scope->get_scope_class()->get_id()->get_name().c_str() : ""), port_type && clean_up && port_type->get_PortBody()->get_testport_type() != PortTypeBody::TP_INTERNAL ? "_BASE" : "", - port_type && clean_up ? "::" : "", + in_class || (port_type && clean_up) ? "::" : "", genname_str, formal_par_list, body); Free(formal_par_list); Free(body); - if (is_startable) { + if (is_startable && !in_class) { size_t nof_fps = fp_list->get_nof_fps(); // use the full list of formal parameters here (since they are all logged) char *full_formal_par_list = fp_list->generate_code(memptystr(), nof_fps); @@ -6800,15 +6867,17 @@ namespace Ttcn { Free(full_formal_par_list); } - target->functions.pre_init = mputprintf(target->functions.pre_init, - "%s.add_function(\"%s\", (genericfunc_t)&%s, ", get_module_object_name(), - dispname_str, genname_str); - if(is_startable) + if (!in_class) { target->functions.pre_init = mputprintf(target->functions.pre_init, - "(genericfunc_t)&start_%s);\n", genname_str); - else - target->functions.pre_init = mputstr(target->functions.pre_init, - "NULL);\n"); + "%s.add_function(\"%s\", (genericfunc_t)&%s, ", get_module_object_name(), + dispname_str, genname_str); + if(is_startable) + target->functions.pre_init = mputprintf(target->functions.pre_init, + "(genericfunc_t)&start_%s);\n", genname_str); + else + target->functions.pre_init = mputstr(target->functions.pre_init, + "NULL);\n"); + } } void Def_Function::generate_code(CodeGenHelper& cgh) { @@ -7556,10 +7625,13 @@ namespace Ttcn { const char *return_type_str = return_type_name.c_str(); char *formal_par_list = fp_list->generate_code(memptystr(), fp_list->get_nof_fps()); fp_list->generate_code_defval(target); + + bool in_class = my_scope->is_class_scope(); // function prototype - target->header.function_prototypes = - mputprintf(target->header.function_prototypes, "extern %s %s(%s);\n", - return_type_str, genname_str, formal_par_list); + char*& header = in_class ? target->header.class_defs : + target->header.function_prototypes; + header = mputprintf(header, "%s %s %s(%s);\n", + in_class ? "virtual" : "extern", return_type_str, genname_str, formal_par_list); if (function_type != EXTFUNC_MANUAL) { // function body written by the compiler @@ -7593,9 +7665,11 @@ namespace Ttcn { Free(formal_par_list); - target->functions.pre_init = mputprintf(target->functions.pre_init, - "%s.add_function(\"%s\", (genericfunc_t)&%s, NULL);\n", - get_module_object_name(), id->get_dispname().c_str(), genname_str); + if (!in_class) { + target->functions.pre_init = mputprintf(target->functions.pre_init, + "%s.add_function(\"%s\", (genericfunc_t)&%s, NULL);\n", + get_module_object_name(), id->get_dispname().c_str(), genname_str); + } } void Def_ExtFunction::generate_code(CodeGenHelper& cgh) { @@ -7740,6 +7814,56 @@ namespace Ttcn { json->put_next_token(JSON_TOKEN_OBJECT_END); } } + + // ================================= + // ===== Def_AbsFunction + // ================================= + + Def_AbsFunction::~Def_AbsFunction() + { + // TODO + } + + Definition* Def_AbsFunction::clone() const + { + return NULL; + // TODO + } + + void Def_AbsFunction::chk() + { + // TODO + } + + void Def_AbsFunction::generate_code(output_struct* target, bool) + { + const string& t_genname = get_genname(); + const char* genname_str = t_genname.c_str(); + string return_type_name; + switch (asstype) { + case A_FUNCTION: + return_type_name = "void"; + break; + case A_FUNCTION_RVAL: + return_type_name = return_type->get_genname_value(my_scope); + break; + case A_FUNCTION_RTEMP: + return_type_name = return_type->get_genname_template(my_scope); + break; + default: + FATAL_ERROR("Def_AbsFunction::generate_code"); + } + const char* return_type_str = return_type_name.c_str(); + char* formal_par_list = fp_list->generate_code(memptystr(), fp_list->get_nof_fps()); + fp_list->generate_code_defval(target); + + target->header.class_defs = mputprintf(target->header.class_defs, + "virtual %s %s(%s) = 0;\n", return_type_str, genname_str, formal_par_list); + + Free(formal_par_list); + } + + // TODO // ================================= // ===== Def_Altstep @@ -8352,6 +8476,78 @@ namespace Ttcn { if (block) block->set_parent_path(w_attrib_path); } + + // ================================= + // ===== Def_Constructor + // ================================= + + Def_Constructor::Def_Constructor(FormalParList* p_fp_list, + Ref_pard* p_base_call, StatementBlock* p_block) + : Definition(A_CONSTRUCTOR, new Common::Identifier( + Common::Identifier::ID_TTCN, string("create"), true)), + fp_list(p_fp_list), base_call(p_base_call), block(p_block) + { + if (p_fp_list == NULL || block == NULL) { + FATAL_ERROR("Def_Constructor::Def_Constructor"); + } + } + + Def_Constructor::~Def_Constructor() + { + delete fp_list; + delete base_call; + delete block; + } + + Def_Constructor *Def_Constructor::clone() const + { + FATAL_ERROR("Def_Constructor::clone"); + } + + void Def_Constructor::set_fullname(const string& p_fullname) + { + Definition::set_fullname(p_fullname); + fp_list->set_fullname(p_fullname + ".<formal_par_list>"); + if (base_call != NULL) { + base_call->set_fullname(p_fullname + ".<base_call>"); + } + block->set_fullname(p_fullname + ".<statement_block>"); + } + + void Def_Constructor::set_my_scope(Scope* p_scope) + { + //bridgeScope.set_parent_scope(p_scope); + //bridgeScope.set_scopeMacro_name(id->get_dispname()); + + //Definition::set_my_scope(&bridgeScope); + Definition::set_my_scope(p_scope); // TODO + if (base_call != NULL) { + base_call->set_my_scope(p_scope); + } + block->set_my_scope(fp_list); + } + + FormalParList* Def_Constructor::get_FormalParList() + { + if (!checked) chk(); + return fp_list; + } + + void Def_Constructor::chk() + { + // TODO + } + + void Def_Constructor::generate_code(output_struct *target, bool clean_up) + { + // TODO + } + + void Def_Constructor::set_parent_path(WithAttribPath* p_path) + { + Definition::set_parent_path(p_path); + block->set_parent_path(w_attrib_path); + } // ================================= // ===== FormalPar @@ -9210,7 +9406,7 @@ namespace Ttcn { Value *val = defval.ap->get_Value(); const_def cdef; Code::init_cdef(&cdef); - type->generate_code_object(&cdef, val); + type->generate_code_object(&cdef, val, false); Code::merge_cdef(target, &cdef); Code::free_cdef(&cdef); break; } @@ -9222,7 +9418,7 @@ namespace Ttcn { Template *temp = ti->get_Template(); const_def cdef; Code::init_cdef(&cdef); - type->generate_code_object(&cdef, temp); + type->generate_code_object(&cdef, temp, false); Code::merge_cdef(target, &cdef); Code::free_cdef(&cdef); break; } diff --git a/compiler2/ttcn3/AST_ttcn3.hh b/compiler2/ttcn3/AST_ttcn3.hh index 79bc745a1..fa1eb6cd2 100644 --- a/compiler2/ttcn3/AST_ttcn3.hh +++ b/compiler2/ttcn3/AST_ttcn3.hh @@ -428,6 +428,7 @@ namespace Ttcn { virtual string get_scopeMacro_name() const; virtual NameBridgingScope* clone() const; virtual Common::Assignment* get_ass_bySRef(Ref_simple *p_ref); + virtual bool is_class_scope() const; }; /** @@ -524,6 +525,7 @@ namespace Ttcn { virtual Common::Assignment* get_ass_byIndex(size_t p_i); size_t get_nof_raw_asss(); Definition *get_raw_ass_byIndex(size_t p_i); + virtual bool is_class_scope() const; /** Checks the uniqueness of identifiers. */ void chk_uniq(); /** Checks all definitions. */ @@ -1601,6 +1603,24 @@ namespace Ttcn { * inserted in the schema. */ void generate_json_schema_ref(map<Type*, JSON_Tokenizer>& json_refs); }; + + /** + * Represents an abstract function definition (inside a class definition) + */ + class Def_AbsFunction : public Def_Function_Base { + bool deterministic; + public: + Def_AbsFunction(bool p_deterministic, Identifier* p_id, FormalParList* p_fpl, + Type* p_return_type, bool returns_template, + template_restriction_t p_template_restriction) + : Def_Function_Base(false, p_id, p_fpl, p_return_type, returns_template, + p_template_restriction) { } + virtual ~Def_AbsFunction(); + virtual Definition* clone() const; + virtual void chk(); + virtual void generate_code(output_struct* target, bool clean_up = false); + // TODO + }; /** * Represents an altstep definition. @@ -1717,6 +1737,38 @@ namespace Ttcn { virtual void set_parent_path(WithAttribPath* p_path); }; + + /** + * Represents a constructor definition (inside a class definition). + */ + class Def_Constructor : public Definition { + private: + /** The formal parameter list of the constructor. It is never NULL even if + * the constructor has no parameters. */ + FormalParList* fp_list; + + Ref_pard* base_call; + + StatementBlock* block; + + //NameBridgingScope bridgeScope; + + /// Copy constructor disabled + Def_Constructor(const Def_Constructor& p); + /// %Assignment disabled + Def_Constructor& operator=(const Def_Constructor& p); + public: + Def_Constructor(FormalParList* p_fp_list, Ref_pard* p_base_call, + StatementBlock* p_block); + virtual ~Def_Constructor(); + virtual Def_Constructor* clone() const; + virtual void set_fullname(const string& p_fullname); + virtual void set_my_scope(Scope* p_scope); + virtual FormalParList *get_FormalParList(); + virtual void chk(); + virtual void generate_code(output_struct *target, bool clean_up = false); + virtual void set_parent_path(WithAttribPath* p_path); + }; /** General class to represent formal parameters. The inherited * attribute asstype carries the kind and direction of the diff --git a/compiler2/ttcn3/Ttcnstuff.cc b/compiler2/ttcn3/Ttcnstuff.cc index 9eab7fdcb..b46e06a04 100644 --- a/compiler2/ttcn3/Ttcnstuff.cc +++ b/compiler2/ttcn3/Ttcnstuff.cc @@ -23,6 +23,7 @@ #include "../main.hh" #include "Attributes.hh" #include <errno.h> +#include "Statement.hh" // implemented in coding_attrib_p.y extern Ttcn::ExtensionAttributes * parse_extattributes( @@ -379,17 +380,17 @@ namespace Ttcn { void TypeMappingTarget::chk_function(Type *source_type, Type *port_type, bool legacy, bool incoming) { Error_Context cntxt(this, "In `function' mapping"); - Assignment *t_ass = u.func.function_ref->get_refd_assignment(false); + Common::Assignment *t_ass = u.func.function_ref->get_refd_assignment(false); if (!t_ass) return; t_ass->chk(); switch (t_ass->get_asstype()) { - case Assignment::A_FUNCTION: - case Assignment::A_FUNCTION_RVAL: - case Assignment::A_FUNCTION_RTEMP: + case Common::Assignment::A_FUNCTION: + case Common::Assignment::A_FUNCTION_RVAL: + case Common::Assignment::A_FUNCTION_RTEMP: break; - case Assignment::A_EXT_FUNCTION: - case Assignment::A_EXT_FUNCTION_RVAL: - case Assignment::A_EXT_FUNCTION_RTEMP: + case Common::Assignment::A_EXT_FUNCTION: + case Common::Assignment::A_EXT_FUNCTION_RVAL: + case Common::Assignment::A_EXT_FUNCTION_RTEMP: // External functions are not allowed when the standard like behaviour is used if (legacy) { break; @@ -1504,9 +1505,9 @@ namespace Ttcn { size_t n_prov_t = 0; for (size_t p = 0; p < provider_refs.size(); p++) { provider_body = 0; - Assignment *t_ass = provider_refs[p]->get_refd_assignment(); // provider port + Common::Assignment *t_ass = provider_refs[p]->get_refd_assignment(); // provider port if (t_ass) { - if (t_ass->get_asstype() == Assignment::A_TYPE) { + if (t_ass->get_asstype() == Common::Assignment::A_TYPE) { Type *t = t_ass->get_Type()->get_type_refd_last(); if (t->get_typetype() == Type::T_PORT) { bool found = false; @@ -2440,11 +2441,11 @@ namespace Ttcn { Definition* def = static_cast<Definition*>(vardefs->get_ass_byIndex(i)); string type; switch (def->get_asstype()) { - case Assignment::A_VAR: - case Assignment::A_CONST: + case Common::Assignment::A_VAR: + case Common::Assignment::A_CONST: type = def->get_Type()->get_genname_value(my_scope); break; - case Assignment::A_VAR_TEMPLATE: + case Common::Assignment::A_VAR_TEMPLATE: type = def->get_Type()->get_genname_template(my_scope); break; default: @@ -2462,7 +2463,7 @@ namespace Ttcn { // If the def does not have default value then clean it up to // restore to unbound. (for constants the generate_code_init_comp does nothing) - if ((pdef.var_defs == NULL || len == strlen(pdef.var_defs)) && def->get_asstype() != Assignment::A_CONST) { + if ((pdef.var_defs == NULL || len == strlen(pdef.var_defs)) && def->get_asstype() != Common::Assignment::A_CONST) { pdef.var_defs = mputprintf(pdef.var_defs, "%s.clean_up();\n", def->get_genname().c_str()); } @@ -2973,5 +2974,309 @@ namespace Ttcn { } other->clear(); } + + // ================================= + // ===== ClassTypeBody + // ================================= + + 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, + Definitions* p_members, StatementBlock* p_finally_block) + : Scope(), Location(), class_id(p_class_id), external(p_external), final(p_final), abstract(p_abstract), + base_type(p_base_type), runs_on_ref(p_runs_on_ref), mtc_ref(p_mtc_ref), system_ref(p_system_ref), + members(p_members), finally_block(p_finally_block), checked(false) + { + if (members == NULL) { + FATAL_ERROR("ClassTypeBody::ClassTypeBody"); + } + } + + ClassTypeBody::ClassTypeBody(const ClassTypeBody& p) + { + class_id = p.class_id->clone(); + external = p.external; + final = p.final; + abstract = p.abstract; + base_type = p.base_type != NULL ? p.base_type->clone() : NULL; + 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(); + finally_block = p.finally_block != NULL ? p.finally_block->clone() : NULL; + checked = p.checked; + } + + ClassTypeBody* ClassTypeBody::clone() const + { + return new ClassTypeBody(*this); + } + + ClassTypeBody::~ClassTypeBody() + { + delete base_type; + delete finally_block; + delete members; + delete mtc_ref; + delete runs_on_ref; + delete system_ref; + } + + void ClassTypeBody::set_fullname(const string& p_fullname) + { + Common::Scope::set_fullname(p_fullname); + if (base_type != NULL) { + base_type->set_fullname(p_fullname + ".<base_type>"); + } + if (runs_on_ref != NULL) { + runs_on_ref->set_fullname(p_fullname + ".<runs_on_type>"); + } + if (mtc_ref != NULL) { + mtc_ref->set_fullname(p_fullname + ".<mtc_type>"); + } + if (system_ref != NULL) { + system_ref->set_fullname(p_fullname + ".<system_type>"); + } + members->set_fullname(p_fullname + ".<members>"); + if (finally_block != NULL) { + finally_block->set_fullname(p_fullname + ".<finally_block>"); + } + } + + void ClassTypeBody::set_my_scope(Scope* p_scope) + { + set_parent_scope(p_scope); + if (base_type != NULL) { + base_type->set_my_scope(p_scope); + } + if (runs_on_ref != NULL) { + runs_on_ref->set_my_scope(p_scope); + } + if (mtc_ref != NULL) { + mtc_ref->set_my_scope(p_scope); + } + if (system_ref != NULL) { + system_ref->set_my_scope(p_scope); + } + //members->set_my_scope(p_scope); + /*if (finally_block != NULL) { + finally_block->set_my_scope(p_scope); + }*/ + } + + void ClassTypeBody::dump(unsigned level) const + { + DEBUG(level, "Modifiers:%s%s%s", external ? "external " : "", + final ? " @final " : "", abstract ? " @abstract" : ""); + DEBUG(level, "Base class: %s", base_type != NULL ? + base_type->get_typename().c_str() : ""); + if (runs_on_ref != NULL) { + DEBUG(level, "Runs on clause:"); + runs_on_ref->dump(level + 1); + } + if (mtc_ref != NULL) { + DEBUG(level, "MTC clause:"); + mtc_ref->dump(level + 1); + } + if (system_ref != NULL) { + DEBUG(level, "System clause:"); + system_ref->dump(level + 1); + } + DEBUG(level, "Members:"); + members->dump(level + 1); + DEBUG(level, "%s `finally' block", finally_block != NULL ? + "Has" : "Doesn't have"); + } + + bool ClassTypeBody::is_parent_class(const ClassTypeBody* p_class) const + { + if (this == p_class) { + return true; + } + if (base_type == NULL) { + return false; + } + return base_type->get_type_refd_last()->get_class_type_body()-> + is_parent_class(p_class); + } + + bool ClassTypeBody::has_local_ass_withId(const Identifier& p_id) + { + chk(); + if (members->has_local_ass_withId(p_id)) { + return true; + } + if (base_type != NULL) { + return base_type->get_type_refd_last()->get_class_type_body()-> + has_local_ass_withId(p_id); + } + else { + return false; + } + } + + Common::Assignment* ClassTypeBody::get_local_ass_byId(const Identifier& p_id) + { + chk(); + Common::Assignment* ass = NULL; + if (members->has_local_ass_withId(p_id)) { + ass = members->get_local_ass_byId(p_id); + } + if (ass == NULL && base_type != NULL) { + ass = base_type->get_type_refd_last()->get_class_type_body()-> + get_local_ass_byId(p_id); + } + return ass; + } + + Common::Assignment* ClassTypeBody::get_ass_bySRef(Common::Ref_simple* p_ref) + { + if (p_ref == NULL || parent_scope == NULL) { + FATAL_ERROR("ClassTypeBody::get_ass_bySRef()"); + } + if (p_ref->get_modid() == NULL) { + const Common::Identifier* id = p_ref->get_id(); + if (id != NULL && has_local_ass_withId(*id)) { + Common::Assignment* ass = get_local_ass_byId(*id); + if (ass == NULL) { + FATAL_ERROR("ClassTypeBody::get_ass_bySRef()"); + } + + if (ass->get_visibility() == PUBLIC) { + // it's public, so it doesn't matter where it's accessed from + return ass; + } + + const ClassTypeBody* ref_scope_class = p_ref->get_my_scope()->get_scope_class(); + if (ref_scope_class == this) { + // the reference is inside this class => any visibility is fine + return ass; + } + + if (ref_scope_class != NULL && + ass->get_visibility() == NOCHANGE && // i.e. protected + ref_scope_class->is_parent_class(this)) { + return ass; + } + + p_ref->error("The member definition `%s' in class type `%s'" + " is not visible in this scope", id->get_dispname().c_str(), + class_id->get_dispname().c_str()); + return NULL; + } + } + return parent_scope->get_ass_bySRef(p_ref); + } + + void ClassTypeBody::chk() + { + if (checked) { + return; + } + checked = true; + // TODO: external? final? abstract? + if (base_type != NULL) { + base_type->chk(); + // TODO: additional checks for the base type + } + + if (runs_on_ref != NULL) { + Common::Assignment* ass = runs_on_ref->get_refd_assignment(true); + // TODO + } + if (mtc_ref != NULL) { + Common::Assignment* ass = mtc_ref->get_refd_assignment(true); + // TODO + } + if (system_ref != NULL) { + Common::Assignment* ass = system_ref->get_refd_assignment(true); + // TODO + } + + members->set_parent_scope(this); + members->chk_uniq(); + members->chk(); + + if (finally_block != NULL) { + finally_block->set_parent_scope(this); + finally_block->chk(); + } + } + + void ClassTypeBody::generate_code(output_struct* target) + { + 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"); + + target->header.class_defs = mputprintf(target->header.class_defs, + "class %s : public %s {\n", + class_id->get_name().c_str(), base_type_name.c_str()); + // members + members->generate_code(target); + + // constructor + target->header.class_defs = mputprintf(target->header.class_defs, + "public:\n" + "%s();\n", class_id->get_name().c_str()); + target->source.methods = mputprintf(target->source.methods, + "%s::%s()\n" + ": %s()", + class_id->get_name().c_str(), class_id->get_name().c_str(), + base_type_name.c_str()); + if (target->temp.constructor_init != NULL) { + target->source.methods = mputstr(target->source.methods, + target->temp.constructor_init); + Free(target->temp.constructor_init); + target->temp.constructor_init = NULL; + } + target->source.methods = mputstr(target->source.methods, "\n{\n"); + if (target->temp.constructor != NULL) { + target->source.methods = create_location_object( + target->source.methods, "FUNCTION", class_id->get_name().c_str()); + target->source.methods = mputstr(target->source.methods, + target->temp.constructor); + Free(target->temp.constructor); + target->temp.constructor = NULL; + } + target->source.methods = mputstr(target->source.methods, "}\n\n"); + + // destructor + target->header.class_defs = mputprintf(target->header.class_defs, + "public:\n" + "virtual ~%s()%s\n", + class_id->get_name().c_str(), finally_block != NULL ? ";" : " { }"); + if (finally_block != NULL) { + target->source.methods = mputprintf(target->source.methods, + "%s::~%s()\n" + "{\n", class_id->get_name().c_str(), class_id->get_name().c_str()); + char* destructor_str = mprintf("~%s", class_id->get_name().c_str()); + target->source.methods = finally_block->create_location_object( + target->source.methods, "FUNCTION", destructor_str); + Free(destructor_str); + target->source.methods = mputstr(target->source.methods, "try {\n"); + target->source.methods = finally_block->generate_code( + target->source.methods, target->header.global_vars, + target->source.global_vars); + target->source.methods = mputprintf(target->source.methods, + "} catch (...) {\n" + "fprintf(stderr, \"Unhandled exception or dynamic test case error in " + "destructor of class `%s'\");\n" + "exit(EXIT_FAILURE);\n" + "}\n" + "}\n\n", class_id->get_name().c_str()); + } + + target->header.class_defs = mputstr(target->header.class_defs, "};\n\n"); + } + else { // external class + target->header.class_defs = mputprintf(target->header.class_defs, + "#include \"%s.hh\"\n\n", + duplicate_underscores ? class_id->get_name().c_str() : + class_id->get_dispname().c_str()); + } + } } // namespace Ttcn diff --git a/compiler2/ttcn3/Ttcnstuff.hh b/compiler2/ttcn3/Ttcnstuff.hh index 42b347a5a..f036abebf 100644 --- a/compiler2/ttcn3/Ttcnstuff.hh +++ b/compiler2/ttcn3/Ttcnstuff.hh @@ -753,6 +753,47 @@ public: size_t size() const { return vector<ExtensionAttribute>::size(); } }; + +class ClassTypeBody : public Common::Scope, public Common::Location { + Common::Identifier* class_id; // not owned + boolean external; + boolean final; + boolean abstract; + Common::Type* base_type; + Reference* runs_on_ref; + Reference* mtc_ref; + Reference* system_ref; + Definitions* members; + StatementBlock* finally_block; + bool checked; + +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, + Definitions* p_members, StatementBlock* p_finally_block); + ClassTypeBody(const ClassTypeBody& p); + ClassTypeBody* clone() const; + virtual ~ClassTypeBody(); + + void set_fullname(const string& p_fullname); + void set_my_scope(Scope* p_scope); + void dump(unsigned level) const; + + virtual bool is_class_scope() const { return true; } + virtual const ClassTypeBody* get_scope_class() const { return this; } + Common::Identifier* get_id() const { return class_id; } + + bool is_parent_class(const ClassTypeBody* p_class) const; + bool has_local_ass_withId(const Identifier& p_id); + Common::Assignment* get_local_ass_byId(const Identifier& p_id); + Common::Assignment* get_ass_bySRef(Common::Ref_simple* p_ref); + + void chk(); + + void generate_code(output_struct* target); +}; + } #endif /* TTCNSTUFF_H_ */ diff --git a/compiler2/ttcn3/compiler.h b/compiler2/ttcn3/compiler.h index 2b0001605..8564d87cf 100644 --- a/compiler2/ttcn3/compiler.h +++ b/compiler2/ttcn3/compiler.h @@ -67,6 +67,10 @@ extern "C" { char *start; /**< Code for start_ptc_function() */ char *control; /**< Code for module_control_part() */ } functions; + struct { + char* constructor_init; + char* constructor; + } temp; struct { size_t pre_things_size; // Size of string_literals + global_vars size_t *methods; diff --git a/compiler2/ttcn3/compiler.l b/compiler2/ttcn3/compiler.l index 1d0ffef30..a70536cb4 100644 --- a/compiler2/ttcn3/compiler.l +++ b/compiler2/ttcn3/compiler.l @@ -558,6 +558,30 @@ timestamp { RETURN_LVAL(IDentifier); } } +class { + if (oop_features) { + RETURN(ClassKeyword); + } + else { + Location loc(infile, yylloc); + loc.warning("Keyword 'class' 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); + } +} +finally { + if (oop_features) { + RETURN(FinallyKeyword); + } + else { + Location loc(infile, yylloc); + loc.warning("Keyword 'finally' 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 */ @@ -568,6 +592,8 @@ timestamp { "@fuzzy" RETURN(FuzzyKeyword); "@index" RETURN(IndexKeyword); "@local" RETURN(LocalKeyword); +"@final" RETURN(FinalKeyword); +"@abstract" RETURN(AbstractKeyword); /* special TITAN specific keywords */ diff --git a/compiler2/ttcn3/compiler.y b/compiler2/ttcn3/compiler.y index fd0af25e9..79b1002a9 100644 --- a/compiler2/ttcn3/compiler.y +++ b/compiler2/ttcn3/compiler.y @@ -659,6 +659,7 @@ static const string anyname("anytype"); %token CharStringKeyword %token CheckOpKeyword %token CheckStateKeyword +%token ClassKeyword %token ClearOpKeyword %token ComplementKeyword %token ComponentKeyword @@ -687,6 +688,7 @@ static const string anyname("anytype"); %token ExtKeyword %token FailKeyword %token FalseKeyword +%token FinallyKeyword %token FloatKeyword %token ForKeyword %token FriendKeyword @@ -807,6 +809,8 @@ static const string anyname("anytype"); %token FuzzyKeyword %token IndexKeyword %token LocalKeyword +%token FinalKeyword +%token AbstractKeyword /* TITAN specific keywords */ %token TitanSpecificTryKeyword @@ -942,6 +946,7 @@ static const string anyname("anytype"); %type <bool_val> optAliveKeyword optOptionalKeyword optErrValueRaw optAllKeyword optDeterministicModifier optRealtimeClause + optExtKeyword optFinalModifier optAbstractModifier %type <str> FreeText optLanguageSpec PatternChunk PatternChunkList %type <uchar_val> Group Plane Row Cell %type <id> FieldIdentifier FieldReference GlobalModuleId @@ -968,9 +973,11 @@ static const string anyname("anytype"); %type <compfield> StructFieldDef UnionFieldDef %type <compfieldmap> StructFieldDefList optStructFieldDefList UnionFieldDefList %type <definition> AltstepDef ExtFunctionDef FunctionDef TemplateDef TestcaseDef + ClassFunctionDef ConstructorDef +%type <defs> optClassMemberList ClassMemberList %type <deftype> TypeDefBody StructuredTypeDef SubTypeDef RecordDef UnionDef SetDef RecordOfDef SetOfDef EnumDef PortDef PortDefBody ComponentDef - TypeDef SignatureDef FunctionTypeDef AltstepTypeDef TestcaseTypeDef + TypeDef SignatureDef FunctionTypeDef AltstepTypeDef TestcaseTypeDef ClassDef %type <deftimer> SingleTimerInstance %type <enumitem> Enumeration %type <enumitems> EnumerationList @@ -1028,7 +1035,7 @@ static const string anyname("anytype"); SelectUnionConstruct UpdateStatement SetstateStatement SetencodeStatement StopTestcaseStatement String2TtcnStatement ProfilerStatement int2enumStatement %type <statementblock> StatementBlock optElseClause FunctionStatementOrDefList - ControlStatementOrDefList ModuleControlBody + ControlStatementOrDefList ModuleControlBody optFinallyDef %type <subtypeparse> ValueOrRange %type <templ> MatchingSymbol SingleValueOrAttrib SimpleSpec TemplateBody ArrayElementSpec ArrayValueOrAttrib FieldSpecList ArraySpecList @@ -1046,7 +1053,7 @@ static const string anyname("anytype"); ArrayElementSpecList SubsetMatch SupersetMatch PermutationMatch %type <ass> Assignment Step %type <refbase> DerivedRefWithParList TemplateRefWithParList DecValueArg -%type <refpard> FunctionInstance AltstepInstance +%type <refpard> FunctionInstance AltstepInstance optBaseConstructorCall %type <reference> PortType optDerivedDef DerivedDef IndexSpec Signature VariableRef TimerRef Port PortOrAll ValueStoreSpec SenderSpec ComponentType optRunsOnSpec RunsOnSpec optSystemSpec optPortSpec @@ -1056,7 +1063,7 @@ static const string anyname("anytype"); %type <type> NestedEnumDef NestedRecordDef NestedRecordOfDef NestedSetDef NestedSetOfDef NestedTypeDef NestedUnionDef PortDefAttribs ReferencedType Type TypeOrNestedTypeDef NestedFunctionTypeDef NestedAltstepTypeDef - NestedTestcaseTypeDef + NestedTestcaseTypeDef optExtendsClassDef %type <types_with_mapping> TypeList AllOrTypeList AllOrTypeListWithFrom AllOrTypeListWithTo TypeListWithFrom TypeListWithTo %type <value> AddressValue AliveOp AllPortsSpec AltGuardChar ArrayBounds @@ -1081,7 +1088,7 @@ AllOrTypeListWithTo TypeListWithFrom TypeListWithTo %type <def_list> AltstepLocalDef AltstepLocalDefList ComponentElementDef ConstDef ExtConstDef FunctionLocalDef FunctionLocalInst ModuleDef ModulePar ModuleParDef MultiTypedModuleParList PortInstance TimerInstance TimerList - VarInstance PortElementVarDef + VarInstance PortElementVarDef ClassMember %type <stmt_list> FunctionStatementOrDef ControlStatementOrDef %type <rangedef> RangeDef @@ -1223,6 +1230,8 @@ CatchStatement CharStringMatch CharStringValue CheckStatement +ClassDef +ClassMemberList ClearStatement CommunicationStatements Complement @@ -1467,11 +1476,15 @@ WithList WithStatement optAltstepFormalParList optAttribQualifier +optBaseConstructorCall +optClassMemberList optComponentDefList optDerivedDef optElseClause optExceptionSpec +optExtendsClassDef optExtendsDef +optFinallyDef optFromClause optFunctionActualParList optFunctionFormalParList @@ -2345,6 +2358,7 @@ StructuredTypeDef: // 16 | FunctionTypeDef { $$ = $1; } | AltstepTypeDef { $$ = $1; } | TestcaseTypeDef { $$ = $1; } +| ClassDef { $$ = $1; } ; RecordDef: // 17 @@ -3493,6 +3507,140 @@ PortElement: // 86 } ; +ClassDef: + optExtKeyword ClassKeyword optFinalModifier optAbstractModifier IDentifier + optExtendsClassDef optRunsOnSpec optMtcSpec optSystemSpec '{' + optClassMemberList '}' optFinallyDef + { + ClassTypeBody* class_ = new ClassTypeBody($5, $1, $3, $4, $6, $7, $8, $9, + $11, $13); + class_->set_location(infile, @$); + Type* type = new Type(Type::T_CLASS, class_); + type->set_location(infile, @$); + $$ = new Def_Type($5, type); + $$->set_location(infile, @$); + } +; + +optExtKeyword: + /* empty */ { $$ = false; } +| ExtKeyword { $$ = true; } +; + +optFinalModifier: + /* empty */ { $$ = false; } +| FinalKeyword { $$ = true; } +; + +optAbstractModifier: + /* empty */ { $$ = false; } +| AbstractKeyword { $$ = true; } +; + +optExtendsClassDef: + /* empty */ { $$ = NULL; } +| ExtendsKeyword ReferencedType { $$ = $2; } +; + +optFinallyDef: + /* empty */ { $$ = NULL; } +| FinallyKeyword StatementBlock { $$ = $2; } +; + +optClassMemberList: + /* empty */ { $$ = new Definitions; } +| ClassMemberList { $$ = $1; } +; + +ClassMemberList: + optVisibility ClassMember optWithStatementAndSemiColon + { + $$ = new Definitions; + for (size_t i = 0; i < $2.nElements; ++i) { + $2.elements[i]->set_visibility($1.visibility); + $$->add_ass($2.elements[i]); + } + Free($2.elements); + // TODO: 'with' attributes + } +| ClassMemberList optVisibility ClassMember optWithStatementAndSemiColon + { + $$ = $1; + for (size_t i = 0; i < $3.nElements; ++i) { + $3.elements[i]->set_visibility($2.visibility); + $$->add_ass($3.elements[i]); + } + Free($3.elements); + // TODO: 'with' attributes + } +; + +ClassMember: + VarInstance { $$ = $1; } +| TimerInstance { $$ = $1; } +| ConstDef { $$ = $1; } +| TemplateDef + { + $$.nElements = 1; + $$.elements = (Definition**)Malloc(sizeof(Definition*)); + $$.elements[0] = $1; + } +| ClassFunctionDef + { + $$.nElements = 1; + $$.elements = (Definition**)Malloc(sizeof(Definition*)); + $$.elements[0] = $1; + } +| ConstructorDef + { + $$.nElements = 1; + $$.elements = (Definition**)Malloc(sizeof(Definition*)); + $$.elements[0] = $1; + } +; + +ClassFunctionDef: + FunctionKeyword optFinalModifier + optDeterministicModifier IDentifier '(' optFunctionFormalParList ')' + optReturnType optError StatementBlock + { + $$ = new Def_Function($3, $4, $6, NULL, NULL, NULL, NULL, $8.type, + $8.returns_template, $8.template_restriction, $10); + $$->set_location(infile, @$); + } +| FunctionKeyword optFinalModifier AbstractKeyword + optDeterministicModifier IDentifier '(' optFunctionFormalParList ')' + optReturnType optError + { + $$ = new Def_AbsFunction($4, $5, $7, $9.type, $9.returns_template, + $9.template_restriction); + $$->set_location(infile, @$); + } +| ExtKeyword FunctionKeyword optFinalModifier optAbstractModifier + optDeterministicModifier IDentifier '(' optFunctionFormalParList ')' + optReturnType + { + $$ = new Def_ExtFunction($5, $6, $8, $10.type, $10.returns_template, + $10.template_restriction); + $$->set_location(infile, @$); + } +; + +ConstructorDef: + CreateKeyword '(' optFunctionFormalParList ')' optBaseConstructorCall + StatementBlock + { + $$ = new Def_Constructor($3, $5, $6); + $$->set_location(infile, @$); + } +; + +optBaseConstructorCall: + /* empty */ { $$ = NULL; } +| ':' FunctionInstance { $$ = $2; } +; + + /* A.1.6.1.2 Constant definitions */ ConstDef: // 88 diff --git a/core/Makefile b/core/Makefile index 02576d66f..09a5cfb1d 100644 --- a/core/Makefile +++ b/core/Makefile @@ -204,7 +204,7 @@ Module_list.hh Parameters.h Addfunc.hh RAW.hh BER.hh TEXT.hh ASN_Null.hh \ ASN_Any.hh ASN_External.hh ASN_EmbeddedPDV.hh ASN_CharacterString.hh XER.hh \ XmlReader.hh cversion.h TitanLoggerControl.ttcn TitanLoggerApi.xsd Vector.hh \ JSON.hh Profiler.hh RefdIndex.hh ProfilerTools.hh Debugger.hh DebugCommands.hh \ -DebuggerUI.hh OER.hh +DebuggerUI.hh OER.hh OOP.hh # Copied during "make install" ifdef REGEX_DIR diff --git a/core/OOP.hh b/core/OOP.hh new file mode 100644 index 000000000..1aabb52d6 --- /dev/null +++ b/core/OOP.hh @@ -0,0 +1,149 @@ +/****************************************************************************** + * Copyright (c) 2000-2019 Ericsson Telecom AB + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html + * + * Contributors: + * Baranyi, Botond + * + ******************************************************************************/ + +#ifndef OOP_HH +#define OOP_HH + +#include "Universal_charstring.hh" + +// OBJECT +// ------ + +class OBJECT { +public: + boolean operator==(const OBJECT&) const { return TRUE; } + UNIVERSAL_CHARSTRING toString() const { + return UNIVERSAL_CHARSTRING("Object"); + } +}; + +// OBJECT_REF +// ---------- + +template <typename T> +class OBJECT_REF { +public: + struct object_ref_struct { + T* obj_ptr; + size_t ref_count; + }; + +private: + object_ref_struct* val_ptr; // NULL if it's a null reference + + void clean_up() { + if (val_ptr != NULL) { + --val_ptr->ref_count; + if (val_ptr->ref_count == 0) { + delete val_ptr->obj_ptr; + delete val_ptr; + } + val_ptr = NULL; + } + } + +public: + OBJECT_REF(): val_ptr(NULL) {} // constructor for null reference + + OBJECT_REF(T* p_obj_ptr): val_ptr(new object_ref_struct) { // constructor for new value (.create) + val_ptr->obj_ptr = p_obj_ptr; + val_ptr->ref_count = 1; + } + + OBJECT_REF(const OBJECT_REF<T>& p_other): val_ptr(p_other.val_ptr) { // copy constructor + if (val_ptr != NULL) { + ++val_ptr->ref_count; + } + } + + ~OBJECT_REF() { + clean_up(); + } + + OBJECT_REF& operator=(null_type) { // assignment operator for null reference + clean_up(); + } + + OBJECT_REF& operator=(const OBJECT_REF<T>& p_other) { // assignment operator for actual reference + clean_up(); + if (p_other.val_ptr != NULL) { + val_ptr = p_other.val_ptr; + ++val_ptr->ref_count; + } + return *this; + } + + boolean operator==(const OBJECT_REF<T>& p_other) const { // equality operator + if (val_ptr == p_other.val_ptr) return TRUE; + if (val_ptr == NULL || p_other.val_ptr == NULL) return FALSE; + return *val_ptr->obj_ptr == *p_other.val_ptr->obj_ptr; + } + + boolean operator!=(const OBJECT_REF<T>& p_other) const { // inequality operator + return !(*this == p_other); + } + + T* operator*() { // de-referencing operator + if (val_ptr != NULL) { + return val_ptr->obj_ptr; + } + TTCN_error("Accessing a null reference."); + } + + T* operator->() { // de-referencing operator (for methods) + if (val_ptr != NULL) { + return val_ptr->obj_ptr; + } + TTCN_error("Accessing a null reference."); + } + + const T* operator->() const { // de-referencing operator (for constant methods) + if (val_ptr != NULL) { + return val_ptr->obj_ptr; + } + TTCN_error("Accessing a null reference."); + } +}; + +// EXCEPTION +// --------- + +template<typename T, int type_id> +class EXCEPTION { +public: + struct exception_struct { + T* val_ptr; + size_t ref_count; + }; +private: + exception_struct* ex_ptr; + EXCEPTION operator=(const EXCEPTION&); // assignment disabled +public: + EXCEPTION(T* p_value): ex_ptr(new exception_struct) { + ex_ptr->val_ptr = p_value; + ex_ptr->ref_count = 1; + } + EXCEPTION(const EXCEPTION& p): ex_ptr(p.ex_ptr) { + ++ex_ptr->ref_count; + } + ~EXCEPTION() { + --ex_ptr->ref_count; + if (ex_ptr->ref_count == 0) { + delete ex_ptr->val_ptr; + delete ex_ptr; + } + } + T& operator()() { return *ex_ptr->val_ptr; } +}; + +#endif /* OOP_HH */ + diff --git a/core/TTCN3.hh b/core/TTCN3.hh index c52c693f5..cab18c969 100644 --- a/core/TTCN3.hh +++ b/core/TTCN3.hh @@ -93,5 +93,6 @@ #include "XmlReader.hh" #include "Profiler.hh" #include "Debugger.hh" +#include "OOP.hh" #endif diff --git a/regression_test/oop/.gitignore b/regression_test/oop/.gitignore new file mode 100644 index 000000000..326407d63 --- /dev/null +++ b/regression_test/oop/.gitignore @@ -0,0 +1,5 @@ +oop +oop.exe +oop*.cc +oop*.hh +oop*.log diff --git a/regression_test/oop/ExternalClass.hh b/regression_test/oop/ExternalClass.hh new file mode 100644 index 000000000..c3eca41f1 --- /dev/null +++ b/regression_test/oop/ExternalClass.hh @@ -0,0 +1,29 @@ +/****************************************************************************** + * Copyright (c) 2000-2019 Ericsson Telecom AB + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html + * + * Contributors: + * Baranyi, Botond + * + ******************************************************************************/ + +#include <TTCN3.hh> + +#ifndef EXTERNALCLASS_HH +#define EXTERNALCLASS_HH + +namespace oop { + +class ExternalClass : public OBJECT { +public: + CHARSTRING f__ext(const INTEGER& x) { + return int2str(x) + CHARSTRING("?"); + } +}; + +} // namespace + +#endif diff --git a/regression_test/oop/Makefile b/regression_test/oop/Makefile new file mode 100644 index 000000000..466453426 --- /dev/null +++ b/regression_test/oop/Makefile @@ -0,0 +1,55 @@ +############################################################################## +# Copyright (c) 2000-2019 Ericsson Telecom AB +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Eclipse Public License v2.0 +# which accompanies this distribution, and is available at +# https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html +# +# Contributors: +# Baranyi, Botond +# +############################################################################## +TOPDIR := .. +include $(TOPDIR)/Makefile.regression + +.SUFFIXES: .ttcn .asn .hh +.PHONY: all clean dep run + +TTCN3_LIB = ttcn3$(RT2_SUFFIX)$(DYNAMIC_SUFFIX) + +TTCN3_MODULES = oop.ttcn + +GENERATED_SOURCES = $(ASN1_MODULES:.asn=.cc) $(TTCN3_MODULES:.ttcn=.cc) + +GENERATED_HEADERS = $(GENERATED_SOURCES:.cc=.hh) +ifdef CODE_SPLIT +GENERATED_SOURCES := $(foreach file, $(GENERATED_SOURCES:.cc=), $(addprefix $(file), .cc _seq.cc _set.cc _seqof.cc _setof.cc _union.cc)) +else ifdef SPLIT_TO_SLICES +POSTFIXES := $(foreach file, $(SPLIT_TO_SLICES), $(addsuffix $(file), _part_)) +POSTFIXES := $(foreach file, $(POSTFIXES), $(addprefix $(file), .cc)) +GENERATED_SOURCES2 := $(foreach file, $(GENERATED_SOURCES:.cc=), $(addprefix $(file), $(POSTFIXES))) +GENERATED_SOURCES += $(GENERATED_SOURCES2) +endif + +OBJECTS = $(GENERATED_SOURCES:.cc=.o) + +TARGET = oop$(EXESUFFIX) + +all: $(TARGET) + +$(TARGET): $(GENERATED_SOURCES) $(USER_SOURCES) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(LDFLAGS) -o $@ $^ -L$(TTCN3_DIR)/lib -l$(TTCN3_LIB) -L$(OPENSSL_DIR)/lib -lcrypto $($(PLATFORM)_LIBS) + +$(GENERATED_SOURCES) $(GENERATED_HEADERS): compile + +compile: $(TTCN3_MODULES) $(ASN1_MODULES) + $(TTCN3_COMPILER) -k $(COMPILER_FLAGS) $^ + +clean distclean: + $(RM) $(TARGET) $(GENERATED_HEADERS) $(GENERATED_SOURCES) $(OBJECTS) *.log Makefile.bak compile + +dep: $(GENERATED_SOURCES) + makedepend $(CPPFLAGS) $(GENERATED_SOURCES) + +run: $(TARGET) + ./$(TARGET) oop.cfg diff --git a/regression_test/oop/oop.cfg b/regression_test/oop/oop.cfg new file mode 100644 index 000000000..d79be6074 --- /dev/null +++ b/regression_test/oop/oop.cfg @@ -0,0 +1,24 @@ +############################################################################### +# Copyright (c) 2000-2019 Ericsson Telecom AB +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Eclipse Public License v2.0 +# which accompanies this distribution, and is available at +# https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html +# +# Contributors: +# Baranyi, Botond +# +############################################################################### + +[LOGGING] +FileMask := LOG_ALL | DEBUG | MATCHING +ConsoleMask := ERROR | WARNING | TESTCASE | STATISTICS | PORTEVENT +LogSourceInfo := Yes +AppendFile := No +TimeStampFormat := DateTime +LogEventTypes := Yes +SourceInfoFormat := Single +LogEntityName := Yes + +[EXECUTE] +oop.control diff --git a/regression_test/oop/oop.ttcn b/regression_test/oop/oop.ttcn new file mode 100644 index 000000000..81e60c360 --- /dev/null +++ b/regression_test/oop/oop.ttcn @@ -0,0 +1,121 @@ +/****************************************************************************** + * Copyright (c) 2000-2019 Ericsson Telecom AB + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html + * + * Contributors: + * Baranyi, Botond + * + ******************************************************************************/ +module oop { + +type component CT { + port PT pt; +} + +type port PT message { inout integer; } with { extension "internal" } + +/////////////////////////////////////////////////// +///////////////////// CLASSES ///////////////////// +/////////////////////////////////////////////////// + +type class BaseClass runs on CT mtc CT system CT { + public const integer m_const := 1; + /*protected*/ var charstring m_var; + private template octetstring m_temp := ? length (1..4); + public var template float m_var_temp; + //private PT m_port; // port members not supported + /*protected*/ timer m_timer; + private timer m_timer_array[3]; + + //public template integer m_temp_pard(integer p) := p; // parameterized template members not supported + + // this would also be the implicit constructor (constructors are not yet implemented): + create(integer p_const, charstring p_var, + template octetstring p_temp, template float p_var_temp, + PT p_port, timer p_timer) { + m_const := p_const; + m_const := 2; + m_var := p_var; + m_temp := p_temp; + m_temp := *; + m_var_temp := p_var_temp; + m_port := p_port; + m_timer := p_timer; + } + + public function f(in integer x) return integer { + m_var := m_var & int2str(x); + return x; + } + + public function get_var() return charstring { + return m_var; + } +} +finally { + //pt.send(-1); + log(m_var); +} + +type class SubClass extends BaseClass { + const octetstring m_const2 := 'AB'O; // the parser currently doesn't accept constants without initial value + + create(integer p_const := 1, charstring p_var, + template octetstring p_temp, template float p_var_temp, + PT p_port, timer p_timer) + : BaseClass(p_const, p_var, p_temp, p_var_temp, p_port, p_timer) { + m_const2 := 'FFFF'O; + } + + public function f(in integer x) return integer { + //return super.f(x) - 1; // not supported yet + m_var := m_var & int2str(x); + return x - 1; + } +} + +type class @final FinalClass extends SubClass { + public function @final f(in integer x) return integer { + //return super.super.f(x) + 1; // not supported yet + m_var := m_var & int2str(x); + return x + 1; + } +} + + +type class @abstract AbstractClass { + public function @abstract f_abs(inout integer x) return boolean; + public function f_con(in charstring x) { + log(x); + } +} + +type class ConcreteClass extends AbstractClass { + public function f_abs(inout integer x) return boolean { + x := x + 1; + return x > 0; + } +} + +/* external classes don't work yet +type external class ExternalClass { + public external function f_ext(in integer x) return charstring; +} + +type class InternalClass extends ExternalClass { + public function f_int() return integer { + return 0; + } + public external function f_ext2(); +} +*/ + + +control { + log("nothing to run, yet..."); +} + +} -- GitLab