From 173f32cd6359a250708aedc01716c805a1489b0e Mon Sep 17 00:00:00 2001 From: Botond Baranyi <botond.baranyi@ericsson.com> Date: Thu, 11 Jun 2020 18:39:11 +0200 Subject: [PATCH] Implemented object-oriented features - stage 7 (bug 563718) Change-Id: I8f4a57c7ff3f2d23d7b4226a3eba702eb2a150bf Signed-off-by: Botond Baranyi <botond.baranyi@ericsson.com> --- compiler2/Value.cc | 237 +++++++++ compiler2/Value.hh | 12 +- compiler2/ttcn3/AST_ttcn3.cc | 11 +- compiler2/ttcn3/AST_ttcn3.hh | 15 +- compiler2/ttcn3/Statement.cc | 471 +++++++++++++++++- compiler2/ttcn3/Statement.hh | 96 ++++ compiler2/ttcn3/Ttcnstuff.cc | 10 + compiler2/ttcn3/compiler.l | 2 + compiler2/ttcn3/compiler.y | 106 +++- core/OOP.hh | 13 + .../Semantic_Analyser/oop/oop_SE.ttcn | 37 ++ regression_test/oop/oop.ttcn | 154 ++++++ 12 files changed, 1140 insertions(+), 24 deletions(-) diff --git a/compiler2/Value.cc b/compiler2/Value.cc index 2d8a79be3..c04aebbf4 100644 --- a/compiler2/Value.cc +++ b/compiler2/Value.cc @@ -398,6 +398,15 @@ namespace Common { u.expr.ti1 = p.u.expr.ti1->clone(); u.expr.v2 = p.u.expr.v2->clone(); break; + case OPTYPE_OF_CLASS: + case OPTYPE_CLASS_CASTING: + u.expr.type = p.u.expr.type->clone(); + u.expr.r2 = p.u.expr.r2->clone(); + break; + case OPTYPE_CLASS_CASTING_REF: + u.expr.r1 = p.u.expr.r1->clone(); + u.expr.r2 = p.u.expr.r2->clone(); + break; default: FATAL_ERROR("Value::Value()"); } // switch @@ -614,6 +623,14 @@ namespace Common { u.expr.v2->chk_expr_immutability(); u.expr.ti1->chk_immutability(); break; + case OPTYPE_OF_CLASS: + case OPTYPE_CLASS_CASTING: + u.expr.r2->chk_immutability(); + break; + case OPTYPE_CLASS_CASTING_REF: + u.expr.r1->chk_immutability(); + u.expr.r2->chk_immutability(); + break; default: FATAL_ERROR("Value::chk_expr_immutability()"); } @@ -869,6 +886,7 @@ namespace Common { break; case OPTYPE_UNDEF_RUNNING: // r1 [r2] b4 case OPTYPE_TMR_RUNNING: // r1 [r2] b4 + case OPTYPE_CLASS_CASTING_REF: delete u.expr.r1; delete u.expr.r2; break; @@ -991,6 +1009,11 @@ namespace Common { case OPTYPE_ANY2UNISTR: delete u.expr.logargs; break; + case OPTYPE_OF_CLASS: + case OPTYPE_CLASS_CASTING: + delete u.expr.type; + delete u.expr.r2; + break; default: FATAL_ERROR("Value::clean_up_expr()"); } // switch @@ -1784,6 +1807,13 @@ namespace Common { u.expr.v3=NULL; u.expr.v4 = NULL; break; + case OPTYPE_CLASS_CASTING_REF: + if (p_r1 == NULL || p_r2 == NULL) { + FATAL_ERROR("Value::Value()"); + } + u.expr.r1 = p_r1; + u.expr.r2 = p_r2; + break; default: FATAL_ERROR("Value::Value()"); } // switch @@ -1873,6 +1903,26 @@ namespace Common { FATAL_ERROR("Value::Value()"); } // switch } + + // type r2 + Value::Value(operationtype_t p_optype, Type* p_type, Ttcn::Reference* p_r2) + : GovernedSimple(S_V), valuetype(V_EXPR), my_governor(0), in_brackets(false) + { + u.expr.v_optype = p_optype; + u.expr.state = EXPR_NOT_CHECKED; + switch (p_optype) { + case OPTYPE_OF_CLASS: + case OPTYPE_CLASS_CASTING: + if (p_type == NULL || p_r2 == NULL) { + FATAL_ERROR("Value::Value()"); + } + u.expr.type = p_type; + u.expr.r2 = p_r2; + break; + default: + FATAL_ERROR("Value::Value()"); + } // switch + } Value::~Value() { @@ -2257,6 +2307,15 @@ namespace Common { u.expr.r2->set_fullname(p_fullname+".redirindex"); } break; + case OPTYPE_OF_CLASS: + case OPTYPE_CLASS_CASTING: + u.expr.type->set_fullname(p_fullname + ".<classtype>"); + u.expr.r2->set_fullname(p_fullname + ".<operand>"); + break; + case OPTYPE_CLASS_CASTING_REF: + u.expr.r1->set_fullname(p_fullname + ".<operand2>"); + u.expr.r2->set_fullname(p_fullname + ".<operand1>"); + break; default: FATAL_ERROR("Value::set_fullname_expr()"); } // switch @@ -2515,6 +2574,15 @@ namespace Common { case OPTYPE_ANY2UNISTR: u.expr.logargs->set_my_scope(p_scope); break; + case OPTYPE_OF_CLASS: + case OPTYPE_CLASS_CASTING: + u.expr.type->set_my_scope(p_scope); + u.expr.r2->set_my_scope(p_scope); + break; + case OPTYPE_CLASS_CASTING_REF: + u.expr.r1->set_my_scope(p_scope); + u.expr.r2->set_my_scope(p_scope); + break; default: FATAL_ERROR("Value::set_my_scope_expr()"); } // switch @@ -2906,6 +2974,13 @@ namespace Common { case OPTYPE_ANY2UNISTR: u.expr.logargs->set_code_section(p_code_section); break; + case OPTYPE_CLASS_CASTING_REF: + u.expr.r1->set_code_section(p_code_section); + // no break + case OPTYPE_CLASS_CASTING: + case OPTYPE_OF_CLASS: + u.expr.r2->set_code_section(p_code_section); + break; default: FATAL_ERROR("Value::set_code_section()"); } // switch @@ -3653,6 +3728,7 @@ namespace Common { case OPTYPE_CHECKSTATE_ANY: case OPTYPE_CHECKSTATE_ALL: case OPTYPE_ISTEMPLATEKIND: + case OPTYPE_OF_CLASS: return Type::T_BOOL; case OPTYPE_GETVERDICT: return Type::T_VERDICT; @@ -3935,6 +4011,9 @@ namespace Common { return Type::T_OSTR; case OPTYPE_DECOMP: return Type::T_OID; + case OPTYPE_CLASS_CASTING: + case OPTYPE_CLASS_CASTING_REF: + return Type::T_CLASS; default: FATAL_ERROR("Value::get_expr_returntype(): invalid optype"); // to avoid warning @@ -4116,6 +4195,11 @@ namespace Common { case OPTYPE_GET_PORT_REF: chk_expr_operands(NULL, exp_val); // calculate the port type return u.expr.type; + case OPTYPE_CLASS_CASTING_REF: + chk_expr_operands(NULL, exp_val); + // no break + case OPTYPE_CLASS_CASTING: + return u.expr.type; default: break; } @@ -4411,6 +4495,11 @@ namespace Common { return "@profiler.running"; case OPTYPE_GET_PORT_REF: return "port.getref()"; + case OPTYPE_OF_CLASS: + return "of"; + case OPTYPE_CLASS_CASTING: + case OPTYPE_CLASS_CASTING_REF: + return "=>"; default: FATAL_ERROR("Value::get_opname()"); } // switch @@ -8347,6 +8436,69 @@ void Value::chk_expr_operand_execute_refd(Value *v1, if (!governor) return; chk_expr_eval_ti(u.expr.ti1, governor, refch, ti_exp_val); } break; + case OPTYPE_CLASS_CASTING_REF: + { + Error_Context cntxt(this, "In the second operand of operation `%s'", opname); + Type* type = u.expr.r1->chk_variable_ref(); + type = (type == NULL) ? new Type(Type::T_ERROR) : type->clone(); + type->set_my_scope(my_scope); + type->set_fullname(get_fullname() + ".<classtype>"); + type->set_location(*u.expr.r1); + delete u.expr.r1; + u.expr.type = type; + u.expr.v_optype = OPTYPE_CLASS_CASTING; + } + // no break + case OPTYPE_CLASS_CASTING: + case OPTYPE_OF_CLASS: { + bool erroneous = false; + Type* ref_type_last = NULL; + { + Error_Context cntxt(this, "In the first operand of operation `%s'", opname); + Type* ref_type = u.expr.r2->chk_variable_ref(); + if (ref_type != NULL) { + ref_type_last = ref_type->get_type_refd_last(); + if (ref_type_last->get_typetype() != Type::T_CLASS) { + u.expr.r2->error("Reference to a class object was expected"); + erroneous = true; + } + } + else { + erroneous = true; + } + } + { + Error_Context cntxt(this, "In the second operand of operation `%s'", opname); + u.expr.type->chk(); + Type* type_last = u.expr.type->get_type_refd_last(); + if (type_last->get_typetype() != Type::T_CLASS) { + if (type_last->get_typetype() != Type::T_ERROR) { + u.expr.type->error("Class type was expected"); + } + erroneous = true; + } + else if (u.expr.v_optype == OPTYPE_CLASS_CASTING) { + Ttcn::ClassTypeBody* new_class = type_last->get_class_type_body(); + if (new_class->is_abstract()) { + u.expr.type->error("Cannot cast to abstract class type `%s'", + u.expr.type->get_typename().c_str()); + } + if (!erroneous) { + Ttcn::ClassTypeBody* old_class = ref_type_last->get_class_type_body(); + if (!new_class->is_parent_class(old_class) && + !old_class->is_parent_class(new_class)) { + u.expr.type->error("Cannot cast an object of class type `%s' " + "to class type `%s'", + ref_type_last->get_typename().c_str(), + u.expr.type->get_typename().c_str()); + } + } + } + } + if (erroneous) { + set_valuetype(V_ERROR); + } + break; } default: FATAL_ERROR("chk_expr_operands()"); } // switch optype @@ -9293,6 +9445,25 @@ void Value::chk_expr_operand_execute_refd(Value *v1, delete v1; } break; + case OPTYPE_OF_CLASS: + if (!is_unfoldable()) { + clean_up(); + valuetype = V_BOOL; + u.val_bool = false; + } + break; + case OPTYPE_CLASS_CASTING: + if (!is_unfoldable()) { + Type* type = u.expr.type; + Reference* ref = u.expr.r2; + valuetype = V_REFD; + u.ref.ref = ref; + u.ref.refd_last = this; + set_my_governor(type->get_type_refd_last()); + delete type; + } + break; + case OPTYPE_CLASS_CASTING_REF: case OPTYPE_UNDEF_RUNNING: default: FATAL_ERROR("Value::evaluate_value()"); @@ -9904,6 +10075,19 @@ void Value::chk_expr_operand_execute_refd(Value *v1, case OPTYPE_ANY2UNISTR: case OPTYPE_TTCN2STRING: return true; + case OPTYPE_OF_CLASS: { + // class of object reference + Ttcn::ClassTypeBody* ref_class = u.expr.r2->chk_variable_ref()-> + get_type_refd_last()->get_class_type_body(); + // expected class + Ttcn::ClassTypeBody* exp_class = u.expr.type->get_type_refd_last()-> + get_class_type_body(); + // the result is always false if the classes are not related to each other, + // otherwise a runtime check is required + return ref_class->is_parent_class(exp_class) || + exp_class->is_parent_class(ref_class); } + case OPTYPE_CLASS_CASTING: + return !u.expr.type->is_identical(u.expr.r2->chk_variable_ref()); default: FATAL_ERROR("Value::is_unfoldable()"); } // switch @@ -11661,9 +11845,15 @@ void Value::chk_expr_operand_execute_refd(Value *v1, self_ref |= chk_expr_self_ref_val(u.expr.v1, lhs); // TODO t_list2 break; + case OPTYPE_OF_CLASS: + case OPTYPE_CLASS_CASTING: { + Common::Assignment *ass = u.expr.r2->get_refd_assignment(); + self_ref |= (ass == lhs); + break; } case NUMBER_OF_OPTYPES: // can never happen case OPTYPE_ISCHOSEN: // r1 i2, should have been classified as _T or _V + case OPTYPE_CLASS_CASTING_REF: FATAL_ERROR("Value::chk_expr_self_ref(%d)", u.expr.v_optype); break; } // switch u.expr.v_optype @@ -12301,6 +12491,15 @@ void Value::chk_expr_operand_execute_refd(Value *v1, return string("@profiler.running"); case OPTYPE_GET_PORT_REF: return string("port.getref()"); + case OPTYPE_OF_CLASS: + return u.expr.r2->get_dispname() + string(" of ") + + u.expr.type->get_typename(); + case OPTYPE_CLASS_CASTING: + return u.expr.r2->get_dispname() + string(" => ") + + u.expr.type->get_typename(); + case OPTYPE_CLASS_CASTING_REF: + return u.expr.r2->get_dispname() + string(" => (") + + u.expr.r1->get_dispname() + string(")"); default: return string("<unsupported optype>"); } // switch u.expr.v_optype @@ -13919,6 +14118,41 @@ void Value::chk_expr_operand_execute_refd(Value *v1, u.expr.type->get_typename().c_str()); expr->expr = mputprintf(expr->expr, "(*%s)", tmp_id.c_str()); break; } + case OPTYPE_OF_CLASS: { + Ttcn::ClassTypeBody* exp_class = u.expr.type->get_type_refd_last()-> + get_class_type_body(); + expression_struct_t ref_expr; + Code::init_expr(&ref_expr); + u.expr.r2->generate_code(&ref_expr); + if (ref_expr.preamble != NULL) { + expr->preamble = mputstr(expr->preamble, ref_expr.preamble); + } + expr->expr = mputprintf(expr->expr, + "%s != NULL_VALUE && dynamic_cast<%s*>(*%s) != NULL", + ref_expr.expr, exp_class->is_built_in() ? "OBJECT" : + u.expr.type->get_type_refd_last()->get_genname_own(my_scope).c_str(), + ref_expr.expr); + if (ref_expr.postamble != NULL) { + expr->postamble = mputstr(expr->postamble, ref_expr.postamble); + } + Code::free_expr(&ref_expr); + break; } + case OPTYPE_CLASS_CASTING: { + expression_struct_t ref_expr; + Code::init_expr(&ref_expr); + u.expr.r2->generate_code(&ref_expr); + if (ref_expr.preamble != NULL) { + expr->preamble = mputstr(expr->preamble, ref_expr.preamble); + } + expr->expr = mputprintf(expr->expr, + "%s.cast_to<%s>()", + ref_expr.expr, + u.expr.type->get_type_refd_last()->get_genname_own(my_scope).c_str()); + if (ref_expr.postamble != NULL) { + expr->postamble = mputstr(expr->postamble, ref_expr.postamble); + } + Code::free_expr(&ref_expr); + break; } default: FATAL_ERROR("Value::generate_code_expr_expr()"); } @@ -15770,6 +16004,9 @@ void Value::chk_expr_operand_execute_refd(Value *v1, case OPTYPE_EXECUTE_REFD: // v1 ap_list2 [v3] return has_single_expr_invoke(u.expr.v1, u.expr.ap_list2) && (!u.expr.v3 || u.expr.v3->has_single_expr()); + case OPTYPE_OF_CLASS: + case OPTYPE_CLASS_CASTING: + return u.expr.r2->has_single_expr(); default: FATAL_ERROR("Value::has_single_expr_expr()"); } // switch diff --git a/compiler2/Value.hh b/compiler2/Value.hh index 0581f6f69..5e24451ee 100644 --- a/compiler2/Value.hh +++ b/compiler2/Value.hh @@ -279,11 +279,15 @@ namespace Common { OPTYPE_CBOR2JSON, // v1 OPTYPE_JSON2CBOR, // v1 OPTYPE_BSON2JSON, // v1 - OPTYPE_JSON2BSON, // v1 + OPTYPE_JSON2BSON, // v1 120 OPTYPE_GET_PORT_REF, // - + + OPTYPE_OF_CLASS, // type r2 + OPTYPE_CLASS_CASTING, // type r2 + OPTYPE_CLASS_CASTING_REF, // r1 r2 - NUMBER_OF_OPTYPES // must be last 122 + NUMBER_OF_OPTYPES // must be last 125 }; enum macrotype_t { @@ -474,7 +478,7 @@ namespace Common { Value(valuetype_t p_vt, Reference *p_ref); Value(valuetype_t p_vt, Block *p_block); Value(valuetype_t p_vt, verdict_t p_verdict); - /** Constructor used by decode */ + /** Constructor used by V_EXPR "r1 r2": DECODE, OPTYPE_CLASS_CASTING_REF */ Value(operationtype_t p_optype, Ttcn::Reference *p_r1, Ttcn::Reference *p_r2); /** Constructor used by decvalue_unichar*/ Value(operationtype_t p_optype, Ttcn::Reference *p_r1, Ttcn::Reference *p_r2, Value *p_v3); @@ -486,6 +490,8 @@ namespace Common { Value* p_v3, Value* p_v4, Value* p_v5); /** Constructor used by V_ANY_VALUE and V_ANY_OR_OMIT */ Value(valuetype_t p_vt, Ttcn::LengthRestriction* p_len_res); + /** Constructor used by V_EXPR "type r2": OF_CLASS, OPTYPE_CLASS_CASTING */ + Value(operationtype_t p_optype, Type* p_type, Ttcn::Reference* p_r2); virtual ~Value(); virtual Value* clone() const; valuetype_t get_valuetype() const {return valuetype;} diff --git a/compiler2/ttcn3/AST_ttcn3.cc b/compiler2/ttcn3/AST_ttcn3.cc index 1ea4a48d0..abf2ea2f3 100644 --- a/compiler2/ttcn3/AST_ttcn3.cc +++ b/compiler2/ttcn3/AST_ttcn3.cc @@ -6428,7 +6428,8 @@ namespace Ttcn { } Def_Function_Base::Def_Function_Base(const Def_Function_Base& p) - : Definition(p), prototype(PROTOTYPE_NONE), input_type(0), output_type(0) + : Definition(p), prototype(PROTOTYPE_NONE), input_type(0), output_type(0), + final(p.final) { fp_list = p.fp_list->clone(); fp_list->set_my_def(this); @@ -6438,11 +6439,11 @@ namespace Ttcn { Def_Function_Base::Def_Function_Base(bool is_external, Identifier *p_id, FormalParList *p_fpl, Type *p_return_type, bool returns_template, - template_restriction_t p_template_restriction) + template_restriction_t p_template_restriction, bool p_final) : Definition(determine_asstype(is_external, p_return_type != 0, returns_template), p_id), fp_list(p_fpl), return_type(p_return_type), prototype(PROTOTYPE_NONE), input_type(0), output_type(0), - template_restriction(p_template_restriction) + template_restriction(p_template_restriction), final(p_final) { if (!p_fpl) FATAL_ERROR("Def_Function_Base::Def_Function_Base()"); fp_list->set_my_def(this); @@ -6662,9 +6663,9 @@ namespace Ttcn { Type *p_return_type, bool returns_template, template_restriction_t p_template_restriction, - StatementBlock *p_block) + bool p_final, StatementBlock *p_block) : Def_Function_Base(false, p_id, p_fpl, p_return_type, returns_template, - p_template_restriction), + p_template_restriction, p_final), runs_on_ref(p_runs_on_ref), runs_on_type(0), mtc_ref(p_mtc_ref), mtc_type(0), system_ref(p_system_ref), system_type(0), diff --git a/compiler2/ttcn3/AST_ttcn3.hh b/compiler2/ttcn3/AST_ttcn3.hh index 9341701f3..2d7039ac1 100644 --- a/compiler2/ttcn3/AST_ttcn3.hh +++ b/compiler2/ttcn3/AST_ttcn3.hh @@ -1356,6 +1356,7 @@ namespace Ttcn { Type *output_type; /** optional template restriction on return template value */ template_restriction_t template_restriction; + bool final; static asstype_t determine_asstype(bool is_external, bool has_return_type, bool returns_template); @@ -1376,7 +1377,7 @@ namespace Ttcn { */ Def_Function_Base(bool is_external, Identifier *p_id, FormalParList *p_fpl, Type *p_return_type, bool returns_template, - template_restriction_t p_template_restriction); + template_restriction_t p_template_restriction, bool p_final); virtual ~Def_Function_Base(); virtual void set_fullname(const string& p_fullname); virtual void set_my_scope(Scope *p_scope); @@ -1393,6 +1394,7 @@ namespace Ttcn { bool is_identical(Def_Function_Base* p_other); template_restriction_t get_template_restriction() { return template_restriction; } + virtual bool is_final() const { return final; } /** Checks and returns whether the function is startable. * Reports the appropriate error message(s) if not. */ //bool chk_startable(Location* caller_location); @@ -1471,10 +1473,9 @@ namespace Ttcn { Def_Function(bool p_deterministic, Identifier *p_id, FormalParList *p_fpl, Reference *p_runs_on_ref, Reference *p_mtc_ref, Reference *p_system_ref, Reference *p_port_ref, - Type *p_return_type, - bool returns_template, + Type *p_return_type, bool returns_template, template_restriction_t p_template_restriction, - StatementBlock *p_block); + bool p_final, StatementBlock *p_block); virtual ~Def_Function(); virtual Def_Function *clone() const; virtual void set_fullname(const string& p_fullname); @@ -1570,9 +1571,9 @@ namespace Ttcn { */ Def_ExtFunction(bool p_deterministic, Identifier *p_id, FormalParList *p_fpl, Type *p_return_type, bool returns_template, - template_restriction_t p_template_restriction, bool p_ext_keyword) + template_restriction_t p_template_restriction, bool p_final, bool p_ext_keyword) : Def_Function_Base(true, p_id, p_fpl, p_return_type, returns_template, - p_template_restriction), + p_template_restriction, p_final), function_type(EXTFUNC_MANUAL), encoding_type(Type::CT_UNDEF), encoding_options(0), eb_list(0), printing(0), deterministic(p_deterministic), ext_keyword(p_ext_keyword) { } @@ -1617,7 +1618,7 @@ namespace Ttcn { 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) { } + p_template_restriction, false) { } virtual ~Def_AbsFunction(); virtual Definition* clone() const; virtual void chk(); diff --git a/compiler2/ttcn3/Statement.cc b/compiler2/ttcn3/Statement.cc index a7ffea6a4..503eea6bc 100644 --- a/compiler2/ttcn3/Statement.cc +++ b/compiler2/ttcn3/Statement.cc @@ -550,6 +550,10 @@ namespace Ttcn { delete select_union.expr; delete select_union.sus; break; + case S_SELECT_CLASS: + delete select_class.ref; + delete select_class.sccs; + break; case S_FOR: if(loop.for_stmt.varinst) delete loop.for_stmt.init_varinst; @@ -964,7 +968,7 @@ namespace Ttcn { } // switch statementtype } - Statement::Statement(statementtype_t p_st, Value *p_expr, SelectUnions *p_sus) + Statement::Statement(statementtype_t p_st, Value *p_expr, SelectUnions *p_sus) : statementtype(p_st), my_sb(0) { switch(statementtype) { @@ -978,6 +982,22 @@ namespace Ttcn { FATAL_ERROR("Statement::Statement()"); } // switch statementtype } + + Statement::Statement(statementtype_t p_st, Reference *p_ref, SelectClassCases *p_sccs) + : statementtype(p_st), my_sb(0) + { + switch(statementtype) { + case S_SELECT_CLASS: + if (p_ref == NULL || p_sccs == NULL) { + FATAL_ERROR("Statement::Statement()"); + } + select_class.ref = p_ref; + select_class.sccs = p_sccs; + break; + default: + FATAL_ERROR("Statement::Statement()"); + } // switch statementtype + } Statement::Statement(statementtype_t p_st, Definitions *p_defs, Assignment *p_ass, Value *p_final, @@ -1598,6 +1618,7 @@ namespace Ttcn { case S_IF: return "if"; case S_SELECT: return "select-case"; case S_SELECTUNION: return "select-union"; + case S_SELECT_CLASS: return "select-class"; case S_FOR: return "for"; case S_WHILE: return "while"; case S_DOWHILE: return "do-while"; @@ -1777,6 +1798,10 @@ namespace Ttcn { select_union.expr->set_my_scope(p_scope); select_union.sus->set_my_scope(p_scope); break; + case S_SELECT_CLASS: + select_class.ref->set_my_scope(p_scope); + select_class.sccs->set_my_scope(p_scope); + break; case S_FOR: if (loop.for_stmt.varinst) { loop.for_stmt.init_varinst->set_parent_scope(p_scope); @@ -2086,6 +2111,10 @@ namespace Ttcn { select_union.expr->set_fullname(p_fullname+".expr"); select_union.sus->set_fullname(p_fullname+".sus"); break; + case S_SELECT_CLASS: + select_class.ref->set_fullname(p_fullname+".ref"); + select_class.sccs->set_fullname(p_fullname+".sccs"); + break; case S_FOR: if(loop.for_stmt.varinst) loop.for_stmt.init_varinst->set_fullname(p_fullname+".init"); @@ -2380,6 +2409,9 @@ namespace Ttcn { case S_SELECTUNION: select_union.sus->set_my_sb(p_sb, p_index); break; + case S_SELECT_CLASS: + select_class.sccs->set_my_sb(p_sb, p_index); + break; case S_FOR: case S_WHILE: case S_DOWHILE: @@ -2413,6 +2445,9 @@ namespace Ttcn { case S_SELECTUNION: select_union.sus->set_my_def(p_def); break; + case S_SELECT_CLASS: + select_class.sccs->set_my_def(p_def); + break; case S_FOR: case S_WHILE: case S_DOWHILE: @@ -2446,6 +2481,9 @@ namespace Ttcn { case S_SELECTUNION: select_union.sus->set_my_ags(p_ags); break; + case S_SELECT_CLASS: + select_class.sccs->set_my_ags(p_ags); + break; case S_FOR: case S_WHILE: case S_DOWHILE: @@ -2476,6 +2514,9 @@ namespace Ttcn { case S_SELECTUNION: select_union.sus->set_my_laic_stmt(p_ags, p_loop_stmt); break; + case S_SELECT_CLASS: + select_class.sccs->set_my_laic_stmt(p_ags, p_loop_stmt); + break; case S_ALT: case S_INTERLEAVE: if (p_loop_stmt) @@ -2548,6 +2589,8 @@ namespace Ttcn { return select.scs->has_return(); case S_SELECTUNION: return select_union.sus->has_return(); + case S_SELECT_CLASS: + return select_class.sccs->has_return(); case S_FOR: case S_WHILE: if (loop.block->has_return() == StatementBlock::RS_NO) @@ -2672,6 +2715,8 @@ namespace Ttcn { return select.scs->has_receiving_stmt(); case S_SELECTUNION: return select_union.sus->has_receiving_stmt(); + case S_SELECT_CLASS: + return select_class.sccs->has_receiving_stmt(); case S_FOR: case S_WHILE: case S_DOWHILE: @@ -2763,6 +2808,9 @@ namespace Ttcn { case S_SELECTUNION: chk_select_union(); break; + case S_SELECT_CLASS: + chk_select_class(); + break; case S_FOR: chk_for(); break; @@ -2989,6 +3037,9 @@ namespace Ttcn { case S_SELECTUNION: select_union.sus->chk_allowed_interleave(); break; + case S_SELECT_CLASS: + select_class.sccs->chk_allowed_interleave(); + break; case S_FOR: case S_WHILE: case S_DOWHILE: @@ -3435,6 +3486,24 @@ error: select_union.sus->chk(); } + + void Statement::chk_select_class() + { + Error_Context cntxt(this, "In select class statement"); + Type* ref_type = select_class.ref->chk_variable_ref(); + ClassTypeBody* ref_class = NULL; + if (ref_type != NULL) { + Type* ref_type_last = ref_type->get_type_refd_last(); + if (ref_type_last->get_typetype() != Type::T_CLASS) { + select_class.ref->error("Reference to a class object was expected"); + } + else { + ref_class = ref_type_last->get_class_type_body(); + } + } + select_class.sccs->chk(ref_class); + } + void Statement::chk_for() { Error_Context cntxt(this, "In for statement"); @@ -6111,6 +6180,10 @@ error: select_union.expr->set_code_section(p_code_section); select_union.sus->set_code_section(p_code_section); break; + case S_SELECT_CLASS: + select_class.ref->set_code_section(p_code_section); + select_class.sccs->set_code_section(p_code_section); + break; case S_ALT: case S_INTERLEAVE: ags->set_code_section(p_code_section); @@ -6360,6 +6433,7 @@ error: case S_IF: case S_SELECT: case S_SELECTUNION: + case S_SELECT_CLASS: case S_FOR: case S_WHILE: case S_DOWHILE: @@ -6406,6 +6480,9 @@ error: case S_SELECTUNION: str=generate_code_select_union(str, def_glob_vars, src_glob_vars); break; + case S_SELECT_CLASS: + str = generate_code_select_class(str, def_glob_vars, src_glob_vars); + break; case S_FOR: str=generate_code_for(str, def_glob_vars, src_glob_vars); break; @@ -6707,6 +6784,9 @@ error: case S_SELECTUNION: ilt_generate_code_select_union(ilt); break; + case S_SELECT_CLASS: + ilt_generate_code_select_class(ilt); + break; case S_CALL: ilt_generate_code_call(ilt); break; @@ -6983,6 +7063,26 @@ error: return str; } + char* Statement::generate_code_select_class(char* str, char*& def_glob_vars, char*& src_glob_vars) + { + const string& tmp_prefix = my_sb->get_scope_mod_gen()->get_temporary_id(); + str = mputstr(str, "{\n"); + expression_struct_t expr; + Code::init_expr(&expr); + select_class.ref->generate_code(&expr); + if (expr.preamble != NULL) { + str = mputstr(str, expr.preamble); + } + str = select_class.sccs->generate_code(str, def_glob_vars, src_glob_vars, + tmp_prefix.c_str(), expr.expr); + if (expr.postamble != NULL) { + str = mputstr(str, expr.postamble); + } + Code::free_expr(&expr); + str = mputstr(str, "}\n"); + return str; + } + char *Statement::generate_code_for(char *str, char*& def_glob_vars, char*& src_glob_vars) { /** \todo initial does not have its own location */ @@ -7362,6 +7462,30 @@ error: str = mputstr(str, "}\n"); } } + + void Statement::ilt_generate_code_select_class(ILT* ilt) + { + const string& tmp_prefix = my_sb->get_scope_mod_gen()->get_temporary_id(); + char*& str = ilt->get_out_branches(); + expression_struct expr; + Code::init_expr(&expr); + select_class.ref->generate_code(&expr); + bool has_recv = select_class.sccs->has_receiving_stmt(); + if (has_recv) { + str = mputstr(str, "{\n"); + } + if (expr.preamble != NULL) { + str = mputstr(str, expr.preamble); + } + select_class.sccs->ilt_generate_code(ilt, tmp_prefix.c_str(), expr.expr); + if (expr.postamble != NULL) { + str = mputstr(str, expr.postamble); + } + Code::free_expr(&expr); + if (has_recv) { + str = mputstr(str, "}\n"); + } + } void Statement::ilt_generate_code_call(ILT *ilt) { @@ -8675,6 +8799,9 @@ error: case S_SELECTUNION: select_union.sus->set_parent_path(p_path); break; + case S_SELECT_CLASS: + select_class.sccs->set_parent_path(p_path); + break; case S_FOR: loop.block->set_parent_path(p_path); break; @@ -12870,6 +12997,348 @@ error: sus[i]->set_parent_path(p_path); } } + + // ================================= + // ===== SelectClassCase + // ================================= + + SelectClassCase::SelectClassCase(Common::Type* p_type, StatementBlock* p_block) + : type(p_type), block(p_block), my_scope(NULL), always_false(false) + { + if (block == NULL) { + FATAL_ERROR("SelectClassCase::SelectClassCase()"); + } + } + + SelectClassCase::~SelectClassCase() + { + delete type; + delete block; + } + + SelectClassCase* SelectClassCase::clone() const + { + FATAL_ERROR("SelectClassCase::clone"); + } + + void SelectClassCase::set_my_scope(Scope* p_scope) + { + my_scope = p_scope; + if (type != NULL) { + type->set_my_scope(p_scope); + } + block->set_my_scope(p_scope); + } + + void SelectClassCase::set_fullname(const string& p_fullname) + { + Node::set_fullname(p_fullname); + if (type != NULL) { + type->set_fullname(p_fullname + ".type"); + } + block->set_fullname(p_fullname + ".block"); + } + + void SelectClassCase::chk(ClassTypeBody* p_ref_class, bool& unreach) + { + Error_Context cntxt(this, "In select class case statement"); + if (unreach) { + warning("Control never reaches this code because of previous " + "effective case(s)"); + } + if (type != NULL) { + type->chk(); + Common::Type* type_last = type->get_type_refd_last(); + if (type_last->get_typetype() != Type::T_CLASS) { + type->error("Class type was expected"); + } + else if (!unreach) { + ClassTypeBody* class_ = type_last->get_class_type_body(); + if (p_ref_class != NULL && !class_->is_parent_class(p_ref_class) && + !p_ref_class->is_parent_class(class_)) { + block->warning("Control never reaches this code because the case will " + "never be chosen"); + always_false = true; + } + } + } + else { + unreach = true; // else statement + } + block->chk(); + } + + void SelectClassCase::set_code_section(GovernedSimple::code_section_t p_code_section) + { + block->set_code_section(p_code_section); + } + + char* SelectClassCase::generate_code_if(char* str, const char* tmp_prefix, + const char* ref_str, size_t idx, + bool& unreach) + { + if (unreach || always_false) { + return str; + } + if (type != NULL) { + ClassTypeBody* class_ = type->get_type_refd_last()->get_class_type_body(); + str = mputprintf(str, + "if (%s != NULL_VALUE && dynamic_cast<%s*>(*%s) != NULL) goto %s_%lu;\n", + ref_str, class_->is_built_in() ? "OBJECT" : + type->get_type_refd_last()->get_genname_own(my_scope).c_str(), + ref_str, tmp_prefix, static_cast<unsigned long>(idx)); + } + else { + unreach = true; // else statement + str = mputprintf(str, "goto %s_%lu;\n", + tmp_prefix, static_cast<unsigned long>(idx)); + } + return str; + } + + char* SelectClassCase::generate_code_stmt(char* str, char*& def_glob_vars, char*& src_glob_vars, + const char* tmp_prefix, size_t idx, bool& unreach) + { + if (unreach || always_false) { + return str; + } + if (type == NULL) { + unreach = true; + } + str = mputprintf(str, "%s_%lu:\n{\n", + tmp_prefix, static_cast<unsigned long>(idx)); + if (debugger_active) { + str = mputstr(str, "TTCN3_Debug_Scope debug_scope;\n"); + } + str = block->generate_code(str, def_glob_vars, src_glob_vars); + str = mputprintf(str, "goto %s_end;\n}\n", tmp_prefix); + return str; + } + + void SelectClassCase::ilt_generate_code_stmt(ILT* ilt, const char* tmp_prefix, + size_t idx, bool& unreach) + { + if (unreach || always_false) { + return; + } + if (type == NULL) { + unreach = true; + } + char*& str = ilt->get_out_branches(); + str = mputprintf(str, "%s_%lu:\n", + tmp_prefix, static_cast<unsigned long>(idx)); + bool has_recv = block->has_receiving_stmt(); + if (!has_recv) { + str = mputstr(str, "{\n"); + str = block->generate_code(str, ilt->get_out_def_glob_vars(), + ilt->get_out_src_glob_vars()); + } + else { + block->ilt_generate_code(ilt); + } + str = mputprintf(str, "goto %s_end;\n", tmp_prefix); + if (!has_recv) { + str = mputstr(str, "}\n"); + } + } + + void SelectClassCase::set_parent_path(WithAttribPath* p_path) { + block->set_parent_path(p_path); + } + + // ================================= + // ===== SelectClassCases + // ================================= + + SelectClassCases::~SelectClassCases() + { + for (size_t i = 0; i < sccs.size(); i++) { + delete sccs[i]; + } + sccs.clear(); + } + + SelectClassCases *SelectClassCases::clone() const + { + FATAL_ERROR("SelectClassCases::clone"); + } + + void SelectClassCases::add_scc(SelectClassCase *p_scc) + { + if (p_scc == NULL) { + FATAL_ERROR("SelectClassCases::add_scc()"); + } + sccs.add(p_scc); + } + + void SelectClassCases::set_my_scope(Scope* p_scope) + { + for (size_t i = 0; i < sccs.size(); i++) { + sccs[i]->set_my_scope(p_scope); + } + } + + void SelectClassCases::set_fullname(const string& p_fullname) + { + Node::set_fullname(p_fullname); + for (size_t i = 0; i < sccs.size(); i++) { + sccs[i]->set_fullname(p_fullname + ".scc_" + Int2string(i+1)); + } + } + + void SelectClassCases::set_my_sb(StatementBlock* p_sb, size_t p_index) + { + for (size_t i = 0; i < sccs.size(); i++) { + sccs[i]->get_block()->set_my_sb(p_sb, p_index); + } + } + + void SelectClassCases::set_my_def(Definition* p_def) + { + for (size_t i = 0; i < sccs.size(); i++) { + sccs[i]->get_block()->set_my_def(p_def); + } + } + + void SelectClassCases::set_my_ags(AltGuards *p_ags) + { + for (size_t i = 0; i < sccs.size(); i++) { + sccs[i]->get_block()->set_my_ags(p_ags); + } + } + + void SelectClassCases::set_my_laic_stmt(AltGuards* p_ags, Statement* p_loop_stmt) + { + for (size_t i = 0; i < sccs.size(); i++) { + sccs[i]->get_block()->set_my_laic_stmt(p_ags, p_loop_stmt); + } + } + + StatementBlock::returnstatus_t SelectClassCases::has_return() const + { + StatementBlock::returnstatus_t ret_val = StatementBlock::RS_MAYBE; + bool has_else = false; + for (size_t i = 0; i < sccs.size(); i++) { + SelectClassCase* scc = sccs[i]; + switch (scc->get_block()->has_return()) { + case StatementBlock::RS_NO: + if (ret_val == StatementBlock::RS_YES) { + return StatementBlock::RS_MAYBE; + } + else { + ret_val = StatementBlock::RS_NO; + } + break; + case StatementBlock::RS_YES: + if (ret_val == StatementBlock::RS_NO) { + return StatementBlock::RS_MAYBE; + } + else { + ret_val = StatementBlock::RS_YES; + } + break; + default: + return StatementBlock::RS_MAYBE; + } + if (scc->get_type() == NULL) { + has_else = true; + break; + } + } + if (!has_else && ret_val == StatementBlock::RS_YES) { + return StatementBlock::RS_MAYBE; + } + else { + return ret_val; + } + } + + bool SelectClassCases::has_receiving_stmt() const + { + for (size_t i = 0; i < sccs.size(); i++) { + if (sccs[i]->get_block()->has_receiving_stmt()) { + return true; + } + } + return false; + } + + void SelectClassCases::chk(ClassTypeBody* p_ref_class) + { + bool unreach = false; + for (size_t i = 0; i < sccs.size(); i++) { + sccs[i]->chk(p_ref_class, unreach); + } + } + + void SelectClassCases::chk_allowed_interleave() + { + for (size_t i = 0; i < sccs.size(); i++) { + sccs[i]->get_block()->chk_allowed_interleave(); + } + } + + void SelectClassCases::set_code_section(GovernedSimple::code_section_t p_code_section) + { + for (size_t i = 0; i < sccs.size(); i++) { + sccs[i]->set_code_section(p_code_section); + } + } + + char* SelectClassCases::generate_code(char* str, char*& def_glob_vars, char*& src_glob_vars, + const char* tmp_prefix, const char* ref_str) + { + bool unreach = false; + for (size_t i = 0; i < sccs.size(); i++) { + str = sccs[i]->generate_code_if(str, tmp_prefix, ref_str, i, unreach); + if (unreach) { + break; + } + } + if (!unreach) { + str = mputprintf(str, "goto %s_end;\n", tmp_prefix); + } + unreach = false; + for (size_t i = 0; i<sccs.size(); i++) { + str = sccs[i]->generate_code_stmt(str, def_glob_vars, src_glob_vars, + tmp_prefix, i, unreach); + if (unreach) { + break; + } + } + str = mputprintf(str, "%s_end: /* empty */;\n", tmp_prefix); + return str; + } + + void SelectClassCases::ilt_generate_code(ILT* ilt, const char* tmp_prefix, + const char* ref_str) + { + char*& str = ilt->get_out_branches(); + bool unreach = false; + for (size_t i = 0; i < sccs.size(); i++) { + if (unreach) { + break; + } + str = sccs[i]->generate_code_if(str, tmp_prefix, ref_str, i, unreach); + } + if (!unreach) { + str = mputprintf(str, "goto %s_end;\n", tmp_prefix); + } + unreach = false; + for (size_t i = 0; i < sccs.size(); i++) { + if (unreach) { + break; + } + sccs[i]->ilt_generate_code_stmt(ilt, tmp_prefix, i, unreach); + } + str = mputprintf(str, "%s_end:\n", tmp_prefix); + } + + void SelectClassCases::set_parent_path(WithAttribPath* p_path) { + for (size_t i = 0; i < sccs.size(); i++) { + sccs[i]->set_parent_path(p_path); + } + } // ================================= // ===== AltGuard diff --git a/compiler2/ttcn3/Statement.hh b/compiler2/ttcn3/Statement.hh index e15f0abc2..659246596 100644 --- a/compiler2/ttcn3/Statement.hh +++ b/compiler2/ttcn3/Statement.hh @@ -52,6 +52,8 @@ namespace Ttcn { class SelectCases; class SelectUnion; class SelectUnions; + class SelectClassCase; + class SelectClassCases; class AltGuard; class AltGuards; class WithAttribPath; @@ -197,6 +199,7 @@ namespace Ttcn { S_IF, // if_stmt S_SELECT, // select S_SELECTUNION, // select union + S_SELECT_CLASS, // select class S_FOR, // loop S_WHILE, // loop S_DOWHILE, // loop @@ -453,6 +456,11 @@ namespace Ttcn { Value *expr; SelectUnions *sus; } select_union; + + struct { + Reference* ref; + SelectClassCases* sccs; + } select_class; struct { Value *v; @@ -539,6 +547,8 @@ namespace Ttcn { Statement(statementtype_t p_st, Value *p_expr, SelectCases *p_scs); /** Constructor used by S_SELECTUNION */ Statement(statementtype_t p_st, Value *p_expr, SelectUnions *p_sus); + /** Constructor used by S_SELECT_CLASS */ + Statement(statementtype_t p_st, Reference *p_ref, SelectClassCases *p_sccs); /** Constructor used by S_FOR */ Statement(statementtype_t p_st, Definitions *p_defs, Assignment *p_ass, Value *p_final, Assignment *p_step, StatementBlock *p_block); @@ -708,6 +718,7 @@ namespace Ttcn { void chk_if(); void chk_select(); void chk_select_union(); + void chk_select_class(); void chk_for(); void chk_while(); void chk_do_while(); @@ -848,6 +859,7 @@ namespace Ttcn { char *generate_code_if(char *str, char*& def_glob_vars, char*& src_glob_vars); char *generate_code_select(char *str, char*& def_glob_vars, char*& src_glob_vars); char *generate_code_select_union(char *str, char*& def_glob_vars, char*& src_glob_vars); + char *generate_code_select_class(char *str, char*& def_glob_vars, char*& src_glob_vars); char *generate_code_for(char *str, char*& def_glob_vars, char*& src_glob_vars); char *generate_code_while(char *str, char*& def_glob_vars, char*& src_glob_vars); char *generate_code_dowhile(char *str, char*& def_glob_vars, char*& src_glob_vars); @@ -862,6 +874,7 @@ namespace Ttcn { void ilt_generate_code_if(ILT *ilt); void ilt_generate_code_select(ILT *ilt); void ilt_generate_code_select_union(ILT *ilt); + void ilt_generate_code_select_class(ILT *ilt); void ilt_generate_code_call(ILT *ilt); void ilt_generate_code_for(ILT *ilt); void ilt_generate_code_while(ILT *ilt); @@ -1567,6 +1580,89 @@ namespace Ttcn { */ virtual void set_parent_path(WithAttribPath* p_path); }; + + + /** + * Class to represent a select-class-case: the class type reference and + * the statement block. + */ + class SelectClassCase : public Node, public Location { + private: + Common::Type* type; + StatementBlock* block; + Common::Scope* my_scope; + bool always_false; + + SelectClassCase(const SelectCase& p); + SelectClassCase& operator=(const SelectCase& p); + public: + /** type == NULL means "else" case */ + SelectClassCase(Common::Type* p_type, StatementBlock* p_block); + virtual ~SelectClassCase(); + virtual SelectClassCase* clone() const; + virtual void set_my_scope(Scope* p_scope); + virtual void set_fullname(const string& p_fullname); + Common::Type* get_type() const { return type; } + StatementBlock* get_block() const { return block; } + /* checking functions */ + void chk(ClassTypeBody* p_ref_class, bool& unreach); + /** Sets the code section selector of all embedded values and + * templates to \a p_code_section. */ + void set_code_section(GovernedSimple::code_section_t p_code_section); + char* generate_code_if(char* str, const char* tmp_prefix, + const char* ref_str, size_t idx, bool& unreach); + char* generate_code_stmt(char* str, char*& def_glob_vars, char*& src_glob_vars, + const char* tmp_prefix, size_t idx, bool& unreach); + void ilt_generate_code_stmt(ILT* ilt, const char* tmp_prefix, + size_t idx, bool& unreach); + + /** Needed by implicit omit. Pushes attrib path down to definitions + */ + virtual void set_parent_path(WithAttribPath* p_path); + }; + + /** + * Class to represent a select class construct. + */ + class SelectClassCases : public Node { + private: + vector<SelectClassCase> sccs; + + SelectClassCases(const SelectCases& p); + SelectClassCases& operator=(const SelectCases& p); + public: + SelectClassCases() : Node() { } + virtual ~SelectClassCases(); + virtual SelectClassCases* clone() const; + void add_scc(SelectClassCase* p_scc); + size_t get_nof_sccs() const { return sccs.size(); } + SelectClassCase *get_scc_byIndex(size_t p_i) const { return sccs[p_i]; } + virtual void set_my_scope(Scope* p_scope); + virtual void set_fullname(const string& p_fullname); + void set_my_sb(StatementBlock* p_sb, size_t p_index); + void set_my_def(Definition* p_def); + void set_my_ags(AltGuards* p_ags); + void set_my_laic_stmt(AltGuards* p_ags, Statement* p_loop_stmt); + StatementBlock::returnstatus_t has_return() const; + bool has_receiving_stmt() const; + /* checking functions */ + /** p_ref_class is the class of the select reference */ + void chk(ClassTypeBody* p_ref_class); + /** checks whether all embedded statements are allowed in an interleaved + * construct */ + void chk_allowed_interleave(); + /** Sets the code section selector of all embedded values and + * templates to \a p_code_section. */ + void set_code_section(GovernedSimple::code_section_t p_code_section); + char* generate_code(char* str, char*& def_glob_vars, char*& src_glob_vars, + const char* tmp_prefix, const char* ref_str); + void ilt_generate_code(ILT* ilt, const char* tmp_prefix, const char* ref_str); + + + /** Needed by implicit omit. Pushes attrib path down to definitions + */ + virtual void set_parent_path(WithAttribPath* p_path); + }; /** diff --git a/compiler2/ttcn3/Ttcnstuff.cc b/compiler2/ttcn3/Ttcnstuff.cc index bc17b0353..6ed30376b 100644 --- a/compiler2/ttcn3/Ttcnstuff.cc +++ b/compiler2/ttcn3/Ttcnstuff.cc @@ -3444,6 +3444,10 @@ namespace Ttcn { "to that of inherited method `%s'", local_id.get_dispname().c_str(), base_def->get_fullname().c_str()); } + else if (base_func->is_final()) { + local_def->error("Cannot override final method `%s'", + base_def->get_fullname().c_str()); + } break; } default: local_def->error("%s shadows inherited member `%s'", @@ -3649,6 +3653,12 @@ namespace Ttcn { "class %s : public %s {\n", class_id->get_name().c_str(), base_type_name.c_str()); + // class name + target->header.class_defs = mputprintf(target->header.class_defs, + "public:\n" + "static const char* class_name() { return \"%s\"; }\n\n", + class_id->get_dispname().c_str()); + // members members->generate_code(target); if (default_constructor) { diff --git a/compiler2/ttcn3/compiler.l b/compiler2/ttcn3/compiler.l index cc5de153a..66a42b80e 100644 --- a/compiler2/ttcn3/compiler.l +++ b/compiler2/ttcn3/compiler.l @@ -1075,6 +1075,8 @@ NULL RETURN(NullValue); RETURN_NOLOCUPD(NE); } +"=>" RETURN(ClassCastingSymbol); + /* Identifiers */ {IDENTIFIER} { diff --git a/compiler2/ttcn3/compiler.y b/compiler2/ttcn3/compiler.y index c100f7e74..578b5577a 100644 --- a/compiler2/ttcn3/compiler.y +++ b/compiler2/ttcn3/compiler.y @@ -186,6 +186,8 @@ static const string anyname("anytype"); SelectCases *selectcases; SelectUnion *selectunion; SelectUnions *selectunions; + SelectClassCase* selectclasscase; + SelectClassCases* selectclasscases; SignatureExceptions *signexc; SignatureParam *signparam; SignatureParamList *signparamlist; @@ -947,6 +949,7 @@ static const string anyname("anytype"); %token SR ">>" %token RL "<@" %token _RR "@>" /* Name clash with bn.h:292 */ +%token ClassCastingSymbol "=>" /********************************************************************* * Semantic types of nonterminals @@ -1024,6 +1027,8 @@ static const string anyname("anytype"); %type <selectcases> seqSelectCase SelectCaseBody %type <selectunion> SelectUnion SelectunionElse %type <selectunions> seqSelectUnion SelectUnionBody +%type <selectclasscase> SelectClassCase +%type <selectclasscases> seqSelectClassCase SelectClassBody %type <signexc> ExceptionTypeList optExceptionSpec %type <signparam> SignatureFormalPar %type <signparamlist> SignatureFormalParList optSignatureFormalParList @@ -1041,8 +1046,9 @@ static const string anyname("anytype"); StartTimerStatement StopExecutionStatement StopStatement StopTCStatement StopTimerStatement TimeoutStatement TimerStatements TriggerStatement UnmapStatement VerdictStatements WhileStatement SelectCaseConstruct - SelectUnionConstruct UpdateStatement SetstateStatement SetencodeStatement - StopTestcaseStatement String2TtcnStatement ProfilerStatement int2enumStatement + SelectUnionConstruct SelectClassConstruct UpdateStatement SetstateStatement + SetencodeStatement StopTestcaseStatement String2TtcnStatement ProfilerStatement + int2enumStatement %type <statementblock> StatementBlock optElseClause FunctionStatementOrDefList ControlStatementOrDefList ModuleControlBody optFinallyDef %type <subtypeparse> ValueOrRange @@ -1085,7 +1091,7 @@ AllOrTypeListWithTo TypeListWithFrom TypeListWithTo PredefinedValue ReadTimerOp ReferOp ReferencedValue RunningOp RunningTimerOp SelfOp SingleExpression SingleLowerBound SystemOp TemplateOps TimerOps TimerValue UpperBound Value ValueofOp VerdictOps VerdictValue optReplyValue - optTestcaseTimerValue optToClause ProfilerRunningOp + optTestcaseTimerValue optToClause ProfilerRunningOp OfClassOp ClassCastingOp %type <values> ArrayElementExpressionList seqArrayExpressionSpec %type <variableentries> VariableList %type <variableentry> VariableEntry @@ -1239,6 +1245,7 @@ CatchStatement CharStringMatch CharStringValue CheckStatement +ClassCastingOp ClassDef ClassMemberList ClearStatement @@ -1357,6 +1364,7 @@ ObjIdComponentList ObjectIdentifierValue OctetStringMatch OctetStringValue +OfClassOp OmitValue OpCall Ostring @@ -1391,10 +1399,14 @@ RunningOp RunningTimerOp RunsOnSpec SUTStatements +SelectClassBody +SelectClassCase +SelectClassConstruct SelfOp SendParameter SendStatement SenderSpec +seqSelectClassCase SetDef SetencodeStatement SetLocalVerdict @@ -3644,7 +3656,7 @@ ClassFunctionDef: optReturnType optError StatementBlock { $$ = new Def_Function($3, $4, $6, NULL, NULL, NULL, NULL, $8.type, - $8.returns_template, $8.template_restriction, $10); + $8.returns_template, $8.template_restriction, $2, $10); $$->set_location(infile, @$); } | FunctionKeyword optFinalModifier AbstractKeyword @@ -3660,7 +3672,7 @@ ClassFunctionDef: optReturnType { $$ = new Def_ExtFunction($4, $5, $7, $9.type, $9.returns_template, - $9.template_restriction, true); + $9.template_restriction, $3, true); $$->set_location(infile, @$); } | FunctionKeyword optFinalModifier @@ -3668,7 +3680,7 @@ ClassFunctionDef: optReturnType { $$ = new Def_ExtFunction($3, $4, $6, $8.type, $8.returns_template, - $8.template_restriction, false); + $8.template_restriction, $2, false); $$->set_location(infile, @$); } ; @@ -4537,7 +4549,7 @@ FunctionDef: // 164 { $5->set_location(infile, @4, @6); $$ = new Def_Function($2, $3, $5, $7, $8.mtcref, $8.systemref, $9, $10.type, $10.returns_template, - $10.template_restriction, $12); + $10.template_restriction, false, $12); $$->set_location(infile, @$); } ; @@ -5643,7 +5655,7 @@ ExtFunctionDef: // 276 { $6->set_location(infile, @5, @7); $$ = new Def_ExtFunction($3, $4, $6, $8.type, $8.returns_template, - $8.template_restriction, true); + $8.template_restriction, false, true); $$->set_location(infile, @$); } ; @@ -9645,6 +9657,7 @@ BasicStatements: // 578 | ConditionalConstruct { $$ = $1; } | SelectCaseConstruct { $$ = $1; } | SelectUnionConstruct { $$ = $1; } +| SelectClassConstruct { $$ = $1; } ; Expression: // 579 @@ -10219,6 +10232,36 @@ OpCall: // 611 $$ = new Value(Value::OPTYPE_NOW); $$->set_location(infile, @$); } +| OfClassOp { $$ = $1; } +| ClassCastingOp { $$ = $1; } +; + +OfClassOp: + VariableRef OfKeyword ReferencedType + { + $$ = new Value(Value::OPTYPE_OF_CLASS, $3, $1); + $$->set_location(infile, @$); + } +| VariableRef OfKeyword ObjectKeyword + { + Type* type = new Type(Type::T_CLASS); + type->set_location(infile, @3); + $$ = new Value(Value::OPTYPE_OF_CLASS, type, $1); + $$->set_location(infile, @$); + } +; + +ClassCastingOp: + VariableRef ClassCastingSymbol ReferencedType + { + $$ = new Value(Value::OPTYPE_CLASS_CASTING, $3, $1); + $$->set_location(infile, @$); + } +| VariableRef ClassCastingSymbol '(' VariableRef ')' + { + $$ = new Value(Value::OPTYPE_CLASS_CASTING_REF, $4, $1); + $$->set_location(infile, @$); + } ; PredefinedOps: @@ -11091,6 +11134,53 @@ SelectunionElse: $$=new SelectUnion($3); // The else branch, ids is empty $$->set_location(infile, @2); } +; + +SelectClassConstruct: + SelectKeyword ClassKeyword '(' VariableRef ')' SelectClassBody + { + $$ = new Statement(Statement::S_SELECT_CLASS, $4, $6); + $$->set_location(infile, @$); + } +; + +SelectClassBody: + '{' seqSelectClassCase optError '}' { $$ = $2; } +| '{' error '}' { $$ = new SelectClassCases; } +; + +seqSelectClassCase: + optError SelectClassCase + { + $$ = new SelectClassCases; + $$->add_scc($2); + } +| seqSelectClassCase optError SelectClassCase + { + $$ = $1; + $$->add_scc($3); + } +; + +SelectClassCase: + CaseKeyword '(' ReferencedType optError ')' StatementBlock optSemiColon + { + $$ = new SelectClassCase($3, $6); + $$->set_location(infile, @$); + } +| CaseKeyword '(' ObjectKeyword optError ')' StatementBlock optSemiColon + { + Type* type = new Type(Type::T_CLASS); + type->set_location(infile, @3); + $$ = new SelectClassCase(type, $6); + $$->set_location(infile, @$); + } +| CaseKeyword ElseKeyword StatementBlock optSemiColon + { + $$ = new SelectClassCase(NULL, $3); + $$->set_location(infile, @$); + } +; diff --git a/core/OOP.hh b/core/OOP.hh index c72d9ef0a..5c2157652 100644 --- a/core/OOP.hh +++ b/core/OOP.hh @@ -50,6 +50,7 @@ public: virtual UNIVERSAL_CHARSTRING toString() { return UNIVERSAL_CHARSTRING("Object"); } + static const char* class_name() { return "object"; } }; // OBJECT_REF @@ -191,6 +192,18 @@ public: boolean is_present() const { return ptr != NULL; } + + template<typename T2> + OBJECT_REF<T2> cast_to() const { + if (ptr == NULL) { + TTCN_error("Casting a null reference"); + } + T2* new_ptr = dynamic_cast<T2*>(ptr); + if (new_ptr == NULL) { + TTCN_error("Invalid casting to class type `%s'", T2::class_name()); + } + return OBJECT_REF<T2>(new_ptr); + } }; template<typename T> diff --git a/function_test/Semantic_Analyser/oop/oop_SE.ttcn b/function_test/Semantic_Analyser/oop/oop_SE.ttcn index 063482daa..4eade8c7e 100644 --- a/function_test/Semantic_Analyser/oop/oop_SE.ttcn +++ b/function_test/Semantic_Analyser/oop/oop_SE.ttcn @@ -467,12 +467,49 @@ type class C47 { const integer m1 := 3; function m2() return boolean { return true; } function m3(in integer p) return charstring { return int2str(p); } + function @final m4() { } } type class C48 extends C47 { //^In type definition// var template octetstring m1; //template variable `@oop_SE.C48.m1' shadows inherited member `@oop_SE.C47.m1'// var integer m2; //variable `@oop_SE.C48.m2' shadows inherited method `@oop_SE.C47.m2'// function m3() return charstring { return "1"; } //The prototype of method `m3' is not identical to that of inherited method `@oop_SE.C47.m3'// + function m4() { } //Cannot override final method `@oop_SE.C47.m4'// +} + + +function f_of_operator() { //^In function definition// + var C0 x := C0.create; + if (x of object) { log(1); } + if (x of C0) { log(2); } + if (x of C5) { log(3); } //^In if statement// //Control never reaches this code because the conditional expression evaluates to false// + if (x of Comp) { log(4); } //^In if statement// //^In the second operand of operation `of'// //Class type was expected// + if (C0 of C0) { log(5); } //^In if statement// //^In the first operand of operation `of'// //Reference to a variable or value parameter was expected instead of type `@oop_SE.C0'// +} + +function f_select_class() { //^In function definition// + var C0 x := C0.create; + select class (x) { //^In select class statement// + case (C0) { log(1); } + case (C5) { log(2); } //^In select class case statement// //Control never reaches this code because the case will never be chosen// + case else { log(3); } + case (Comp) { log(4); } //^In select class case statement// //Control never reaches this code because of previous effective case\(s\)// //Class type was expected// + } + var integer y; + select class (y) { //^In select class statement// //Reference to a class object was expected// + case (object) { log(5); } + case else { log(6); } + } +} + +function f_casting() { //^In function definition// + var C35 x1 := C36.create; + var C36 x2 := x1 => C36; + var C0 x3 := x1 => C0; //^In variable definition// //^In the second operand of operation `=>'// //Cannot cast an object of class type `@oop_SE.C35' to class type `@oop_SE.C0'// + var C35 x4 := x2 => (x1); //^In variable definition// //^In the second operand of operation `=>'// //Cannot cast to abstract class type `@oop_SE.C35'// + var C36 x5 := c => C36; //^In variable definition// //^In the first operand of operation `=>'// //Reference to a variable or value parameter was expected instead of constant `@oop_SE.c'// + var Rec x6 := x1 => Rec; //^In variable definition// //^In the second operand of operation `=>'// //Class type was expected// + var integer x7 := x1 => (x6); //^In variable definition// //^In the second operand of operation `=>'// //Class type was expected// } diff --git a/regression_test/oop/oop.ttcn b/regression_test/oop/oop.ttcn index 6e0d2d17c..0c088dcba 100644 --- a/regression_test/oop/oop.ttcn +++ b/regression_test/oop/oop.ttcn @@ -512,6 +512,157 @@ testcase tc_destructor() runs on CT { var Destructor x := Destructor.create; } +testcase tc_of_operator() runs on CT { + var SubClass v_sub := SubClass.create(4, { 1, 2, 4 }, "a", 'FF'O, 1.0); + var SubClass v_final := FinalClass.create(4, { 1, 2, 4 }, "a", 'FF'O, 1.0, 8, "x", -1.5, *); + var object v_obj := Node.create(3, null); + var BaseClass v_null := null; + + if (not v_sub of BaseClass) { + setverdict(fail, "#1"); + } + if (not v_sub of SubClass) { + setverdict(fail, "#2"); + } + if (v_sub of FinalClass) { + setverdict(fail, "#3"); + } + if (not v_final of BaseClass) { + setverdict(fail, "#4"); + } + if (not v_final of SubClass) { + setverdict(fail, "#5"); + } + if (not v_final of FinalClass) { + setverdict(fail, "#6"); + } + if (v_sub of Node) { + setverdict(fail, "#7"); + } + if (not v_sub of object) { + setverdict(fail, "#8"); + } + if (not v_obj of object) { + setverdict(fail, "#9"); + } + if (not v_obj of Node) { + setverdict(fail, "#10"); + } + if (v_obj of AbstractClass) { + setverdict(fail, "#11"); + } + if (v_null of BaseClass) { + setverdict(fail, "#12"); + } + if (v_null of object) { + setverdict(fail, "#13"); + } + setverdict(pass); +} + +testcase tc_select_class() runs on CT { + var SubClass v_sub := SubClass.create(4, { 1, 2, 4 }, "a", 'FF'O, 1.0); + var SubClass v_final := FinalClass.create(4, { 1, 2, 4 }, "a", 'FF'O, 1.0, 8, "x", -1.5, *); + var object v_obj := Node.create(3, null); + var BaseClass v_null := null; + + select class (v_sub) { + case (FinalClass) { + setverdict(fail, "#1 FinalClass"); + } + case (SubClass) { + setverdict(pass); + } + case (BaseClass) { + setverdict(fail, "#1 BaseClass"); + } + } + + select class (v_final) { + case (FinalClass) { + setverdict(pass); + } + case (SubClass) { + setverdict(fail, "#2 SubClass"); + } + case (BaseClass) { + setverdict(fail, "#2 BaseClass"); + } + } + + select class (v_obj) { + case (object) { + setverdict(pass); + } + case (Node) { + setverdict(fail, "#3 Node"); + } + case else { + setverdict(fail, "#3 else"); + } + } + + select class (v_null) { + case (BaseClass) { + setverdict(fail, "#4 BaseClass"); + } + case (Node) { + setverdict(fail, "#4 Node"); + } + case (object) { + setverdict(fail, "#4 object"); + } + case else { + setverdict(pass); + } + } +} + +testcase tc_casting() runs on CT { + var SubClass v_sub := SubClass.create(4, { 1, 2, 4 }, "a", 'FF'O, 1.0); + var SubClass v_final := FinalClass.create(4, { 1, 2, 4 }, "a", 'FF'O, 1.0, 8, "x", -1.5, *); + var object v_obj := Node.create(3, null); + var BaseClass v_null := null; + var Node v_null2 := null; + + var BaseClass v_base := v_sub => BaseClass; + var BaseClass v_base2 := v_sub; + if (log2str(v_base) != log2str(v_base2)) { + setverdict(fail, "#1: ", v_base); + } + var FinalClass v_final2 := v_final => FinalClass; + if (log2str(v_final2) != log2str(v_final)) { + setverdict(fail, "#2: ", v_final2); + } + var Node v_node := v_obj => (v_null2); + if (log2str(v_node) != log2str(v_obj)) { + setverdict(fail, "#3: ", v_node); + } + var SubClass v_sub2 := v_sub => SubClass; + if (log2str(v_sub2) != log2str(v_sub)) { + setverdict(fail, "#4: ", v_sub2); + } + @try { + var SubClass v_bad := v_null => SubClass; + setverdict(fail, "#5: Error expected"); + } + @catch(msg) { + if (not match(msg, pattern "*Casting a null reference")) { + setverdict(fail, "#5: Invalid error: ", msg); + } + } + @try { + var FinalClass v_bad2 := v_sub => FinalClass; + setverdict(fail, "#6: Error expected"); + } + @catch(msg) { + if (not match(msg, pattern "*Invalid casting to class type `FinalClass'")) { + setverdict(fail, "#6: Invalid error: ", msg); + } + } + setverdict(pass); +} + control { execute(tc_members_and_methods()); @@ -525,6 +676,9 @@ control { execute(tc_order()); execute(tc_abstract()); execute(tc_destructor()); + execute(tc_of_operator()); + execute(tc_select_class()); + execute(tc_casting()); } } -- GitLab