diff --git a/compiler2/Setting.hh b/compiler2/Setting.hh index 74d0029ac70bcaa9f7fc6b87950fb1c81d06cbfd..dc5d47af2568ea60cce8bf7edd0124cd0d4b310c 100644 --- a/compiler2/Setting.hh +++ b/compiler2/Setting.hh @@ -728,6 +728,12 @@ public: * scope-hierarchy) than a Module. */ class Ref_simple : public Reference { + public: + enum reftype_t { + REF_BASIC, // basic reference (not class related to any class scope) + REF_SUPER, // reference to the superclass + REF_THIS // reference to the current class object + }; protected: // Derived classes need access /** Points to the referred assignment. Used for caching. */ Assignment *refd_ass; @@ -743,6 +749,8 @@ public: /** Returns the \a id. */ virtual const Identifier* get_id() =0; /** Creates a display-name for the reference. */ + virtual reftype_t get_reftype() const { return REF_BASIC; } + virtual void set_reftype(reftype_t) { FATAL_ERROR("Ref_simple::set_reftype"); } virtual string get_dispname(); virtual Setting* get_refd_setting(); /** \param check_parlist is ignored */ diff --git a/compiler2/Type.cc b/compiler2/Type.cc index 3281174edf41758ff4813604c0e61200a4099fb1..19f5f90d64ec58683c9823cdff13271c3bcf71ea 100644 --- a/compiler2/Type.cc +++ b/compiler2/Type.cc @@ -1677,7 +1677,7 @@ namespace Common { Type *Type::get_field_type(Ttcn::FieldOrArrayRefs *subrefs, expected_value_t expected_index, ReferenceChain *refch, - bool interrupt_if_optional) + bool interrupt_if_optional, Assignment** last_method) { if (!subrefs) return this; Type *t = this; @@ -1789,11 +1789,15 @@ namespace Common { return 0; case Assignment::A_FUNCTION: case Assignment::A_EXT_FUNCTION: - // TODO: are these handled elsewhere? in some kind of statement? - ref->error("Invalid reference to method `%s' with no return type in " - "class type `%s'", - id.get_dispname().c_str(), t->get_typename().c_str()); - return 0; + if (i != nof_refs - 1 || last_method == NULL) { + ref->error("Invalid reference to method `%s' with no return type in " + "class type `%s'", + id.get_dispname().c_str(), t->get_typename().c_str()); + return 0; + } + // a method with no return value can still be valid (e.g. if it's + // in a statement) + // proceed as normal and return null at the end case Assignment::A_FUNCTION_RVAL: case Assignment::A_FUNCTION_RTEMP: case Assignment::A_EXT_FUNCTION_RVAL: @@ -1817,6 +1821,9 @@ namespace Common { ap_list->set_my_scope(parsed_pars->get_my_scope()); ref->set_actual_par_list(ap_list); } + if (last_method != NULL) { + *last_method = ass; + } break; } default: FATAL_ERROR("Type::get_field_type - %s shouldn't be in a class", diff --git a/compiler2/Type.hh b/compiler2/Type.hh index 547c03cfb200b1cc133fe1b96ecac129b48c716c..f3423ab861f2455ae7d3358058cb90dc966de83d 100644 --- a/compiler2/Type.hh +++ b/compiler2/Type.hh @@ -634,10 +634,15 @@ namespace Common { * Special case: if \a interrupt_if_optional is true then return NULL if an * optional field has been reached. Using this bool parameter it can be * checked if a referenced field is on an optional path (used by template - * restriction checking code) */ + * restriction checking code) + * @param last_method if not null, indicates that a method with no return + * value is valid at the end of the subreferences, and should not produce + * an error; the function assignment is also stored in the pointer pointed + * to by this parameter if the last subreference is a method (even if it has + * a return value) */ Type *get_field_type(Ttcn::FieldOrArrayRefs *subrefs, expected_value_t expected_index, ReferenceChain *refch = 0, - bool interrupt_if_optional = false); + bool interrupt_if_optional = false, Assignment** last_method = NULL); /** subrefs must point to an existing field, get_field_type() should be used * to check. subrefs_array will be filled with the indexes of the fields, * type_array will be filled with types whose field indexes were collected, @@ -1003,6 +1008,7 @@ namespace Common { expected_value_t expected_value, namedbool incomplete_allowed); void chk_this_value_Component(Value *value); void chk_this_value_FAT(Value *value); + void chk_this_value_class(Value* value); public: /** Checks whether template \a t is a specific value and the embedded value * is a referenced one. If the reference in the value points to a @@ -1300,7 +1306,7 @@ namespace Common { void generate_code_ispresentboundchosen(expression_struct *expr, Ttcn::FieldOrArrayRefs *subrefs, Common::Module* module, const string& global_id, const string& external_id, - const bool is_template, const namedbool optype, const char* field); + bool is_template, const namedbool optype, const char* field); /** Extension attribute for optimized code generation of structured types: * with { extension "optimize:xxx" } diff --git a/compiler2/Type_chk.cc b/compiler2/Type_chk.cc index ab219c6101e5a151a7f26e5699a2b93077a1660e..80ccc86a30a3be317188c322840eae2610da5e3b 100644 --- a/compiler2/Type_chk.cc +++ b/compiler2/Type_chk.cc @@ -4131,6 +4131,9 @@ bool Type::chk_this_value(Value *value, Common::Assignment *lhs, expected_value_ case T_TESTCASE: chk_this_value_FAT(value); break; + case T_CLASS: + chk_this_value_class(value); + break; default: FATAL_ERROR("Type::chk_this_value()"); } // switch @@ -4194,6 +4197,15 @@ bool Type::chk_this_refd_value(Value *value, Common::Assignment *lhs, expected_v error_flag = true; } break; + case Assignment::A_TYPE: + if (ass->get_Type()->get_type_refd_last()->typetype != T_CLASS) { + value->error("Reference to a %s was expected instead of %s", + expected_value == EXPECTED_TEMPLATE ? "value or template" : "value", + ass->get_description().c_str()); + value->set_valuetype(Value::V_ERROR); + return self_ref; + } + // else fall through case Assignment::A_VAR: case Assignment::A_PAR_VAL: case Assignment::A_PAR_VAL_IN: @@ -5975,6 +5987,31 @@ void Type::chk_this_value_FAT(Value *value) } } +void Type::chk_this_value_class(Value* value) +{ + Value* v = value->get_value_refd_last(); + switch(v->get_valuetype()) { + case Value::V_TTCN3_NULL: + break; // OK + case Value::V_REFD: + // TODO + break; + case Value::V_EXPR: + switch (v->get_optype()) { + case Value::OPTYPE_CLASS_CREATE: + // TODO + break; + default: + // error + break; + } + break; + default: + // error + break; + } +} + void Type::chk_this_template_length_restriction(Template *t) { Ttcn::LengthRestriction *lr = t->get_length_restriction(); @@ -6108,6 +6145,40 @@ void Type::chk_this_template_ref(Template *t) // endless recursion in case of embedded circular references. // The parameter lists will be verified later. Assignment *ass = v->get_reference()->get_refd_assignment(false); + if (ass != NULL && ass->get_asstype() == Assignment::A_VAR) { + // there could be class objects in the subreferences, which would change + // the type of the assignment (e.g. to a var template); + // use the assignment after the last class object in the subreference chain + Ttcn::FieldOrArrayRefs* subrefs = v->get_reference()->get_subrefs(); + if (subrefs != NULL) { + Type* type = ass->get_Type(); + if (type->get_field_type(subrefs, EXPECTED_DYNAMIC_VALUE) != NULL) { + // subrefs are valid + for (size_t i = 0; i < subrefs->get_nof_refs(); ++i) { + type = type->get_type_refd_last(); + Ttcn::FieldOrArrayRef* subref = subrefs->get_ref(i); + switch (subref->get_type()) { + case Ttcn::FieldOrArrayRef::FIELD_REF: + case Ttcn::FieldOrArrayRef::FUNCTION_REF: + if (type->typetype == T_CLASS) { + ass = type->get_class_type_body()-> + get_local_ass_byId(*subref->get_id()); + type = ass->get_Type(); + } + else { + type = type->get_comp_byName(*subref->get_id())->get_type(); + } + break; + case Ttcn::FieldOrArrayRef::ARRAY_REF: + if (type->is_structured_type()) { + type = type->get_ofType(); + } + break; + } + } + } + } + } if (ass) { switch (ass->get_asstype()) { case Assignment::A_VAR_TEMPLATE: { diff --git a/compiler2/Type_codegen.cc b/compiler2/Type_codegen.cc index 04bc4ae680f62933272415027f4ebee1a7509252..7ef873ec3c3e91d37c90e0b2ab111dc9b6e75528 100644 --- a/compiler2/Type_codegen.cc +++ b/compiler2/Type_codegen.cc @@ -2893,7 +2893,6 @@ bool Type::ispresent_anyvalue_embedded_field(Type* t, Ttcn::FieldOrArrayRef *ref = subrefs->get_ref(i); switch (ref->get_type()) { case Ttcn::FieldOrArrayRef::FIELD_REF: { - CompField* cf = t->get_comp_byName(*ref->get_id()); switch (t->typetype) { case T_CHOICE_T: case T_CHOICE_A: @@ -2903,13 +2902,17 @@ bool Type::ispresent_anyvalue_embedded_field(Type* t, case T_SEQ_T: case T_SET_T: case T_SEQ_A: - case T_SET_A: + case T_SET_A: { + CompField* cf = t->get_comp_byName(*ref->get_id()); if (cf->get_is_optional()) return FALSE; + t = cf->get_type(); + break; } + case T_CLASS: + t = t->get_class_type_body()->get_local_ass_byId(*ref->get_id())->get_Type(); break; default: FATAL_ERROR("Type::ispresent_anyvalue_embedded_field()"); } - t = cf->get_type(); } break; case Ttcn::FieldOrArrayRef::ARRAY_REF: switch (t->typetype) { @@ -2932,7 +2935,7 @@ bool Type::ispresent_anyvalue_embedded_field(Type* t, void Type::generate_code_ispresentboundchosen(expression_struct *expr, Ttcn::FieldOrArrayRefs *subrefs, Common::Module* module, - const string& global_id, const string& external_id, const bool is_template, + const string& global_id, const string& external_id, bool is_template, const namedbool optype, const char* field) { if (!subrefs) return; @@ -2950,7 +2953,9 @@ void Type::generate_code_ispresentboundchosen(expression_struct *expr, // (e.g. because of circular reference) if (t->typetype == T_ERROR) return; - if (is_template) { + if (is_template && t->typetype != T_CLASS) { + // TODO: the initial value of 'is_template' is not always set properly + // (it seems to always be false in case of functions) bool anyval_ret_val = TRUE; if (optype == ISPRESENT) { anyval_ret_val = ispresent_anyvalue_embedded_field(t, subrefs, i); @@ -2987,9 +2992,23 @@ void Type::generate_code_ispresentboundchosen(expression_struct *expr, switch (ref->get_type()) { case Ttcn::FieldOrArrayRef::FIELD_REF: { const Identifier& id = *ref->get_id(); - CompField* cf = t->get_comp_byName(id); - next_t = cf->get_type(); - next_o = !is_template && cf->get_is_optional(); + if (t->typetype == T_CLASS) { + Assignment* ass = t->get_class_type_body()->get_local_ass_byId(id); + if (ass->get_asstype() == Assignment::A_TEMPLATE || + ass->get_asstype() == Assignment::A_VAR_TEMPLATE) { + is_template = true; + } + else { + is_template = false; + } + next_t = ass->get_Type(); + next_o = false; + } + else { + CompField* cf = t->get_comp_byName(id); + next_t = cf->get_type(); + next_o = !is_template && cf->get_is_optional(); + } switch (t->typetype) { case T_CHOICE_A: @@ -3008,6 +3027,7 @@ void Type::generate_code_ispresentboundchosen(expression_struct *expr, case T_SET_A: case T_SET_T: case T_ANYTYPE: + case T_CLASS: break; default: FATAL_ERROR("Type::generate_code_ispresentboundchosen()"); @@ -3125,21 +3145,29 @@ void Type::generate_code_ispresentboundchosen(expression_struct *expr, const char *tmp_id_str = tmp_id.c_str(); const char *tmp_id2_str = tmp_id2.c_str(); - // Own const ref to the temp value - expr->expr = mputprintf(expr->expr, - "const %s%s& %s = %s;\n", - t->get_genname_value(module).c_str(), - is_template ? "_template" : "", - tmp_id_str, tmp_generalid_str); - // Get the const ref of the field from the previous const ref - // If we would get the const ref of the field immediately then the - // value in the const ref would be free-d instantly. - expr->expr = mputprintf(expr->expr, - "const %s%s& %s = %s.%s%s();\n", - next_t->get_genname_value(module).c_str(), - is_template ? "_template" : "", - tmp_id2_str, tmp_id_str, - t->typetype == T_ANYTYPE ? "AT_" : "", id.get_name().c_str()); + if (t->typetype == T_CLASS) { + expr->expr = mputprintf(expr->expr, "const %s%s %s = %s->%s;\n", + next_t->get_genname_value(module).c_str(), + is_template ? "_template" : "", tmp_id2_str, + tmp_generalid_str, id.get_name().c_str()); + } + else { + // Own const ref to the temp value + expr->expr = mputprintf(expr->expr, + "const %s%s& %s = %s;\n", + t->get_genname_value(module).c_str(), + is_template ? "_template" : "", + tmp_id_str, tmp_generalid_str); + // Get the const ref of the field from the previous const ref + // If we would get the const ref of the field immediately then the + // value in the const ref would be free-d instantly. + expr->expr = mputprintf(expr->expr, + "const %s%s& %s = %s.%s%s();\n", + next_t->get_genname_value(module).c_str(), + is_template ? "_template" : "", + tmp_id2_str, tmp_id_str, + t->typetype == T_ANYTYPE ? "AT_" : "", id.get_name().c_str()); + } if (i != nof_refs - 1 || optype == ISCHOSEN) { expr->expr = mputprintf(expr->expr, "%s = %s.is_bound();\n", @@ -3174,6 +3202,68 @@ void Type::generate_code_ispresentboundchosen(expression_struct *expr, tmp_generalid_str = mcopystr(tmp_id2_str); } + t = next_t; + break; } + case Ttcn::FieldOrArrayRef::FUNCTION_REF: { + const Identifier& id = *ref->get_id(); + Assignment* ass = t->get_class_type_body()->get_local_ass_byId(id); + if (ass->get_asstype() == Assignment::A_FUNCTION_RTEMP || + ass->get_asstype() == Assignment::A_EXT_FUNCTION_RTEMP) { + is_template = true; + } + else { + is_template = false; + } + Ttcn::Def_Function_Base* def_func = dynamic_cast<Ttcn::Def_Function_Base*>(ass); + next_t = def_func->get_return_type(); + + expr->expr = mputprintf(expr->expr, "if(%s) {\n", global_id.c_str()); + expstring_t closing_brackets2 = mprintf("}\n%s", closing_brackets); + Free(closing_brackets); + closing_brackets = closing_brackets2; + + const string& tmp_id = module->get_temporary_id(); + const char *tmp_id_str = tmp_id.c_str(); + + expr->expr = mputprintf(expr->expr, "const %s%s %s = %s->%s(", + next_t->get_genname_value(module).c_str(), + is_template ? "_template" : "", tmp_id_str, + tmp_generalid_str, id.get_name().c_str()); + ref->get_actual_par_list()->generate_code_noalias(expr, ass->get_FormalParList()); + expr->expr = mputstr(expr->expr, ");\n"); + + if (i != nof_refs - 1 || optype == ISCHOSEN) { + expr->expr = mputprintf(expr->expr, "%s = %s.is_bound();\n", + global_id.c_str(), tmp_id_str); + } + if (i == nof_refs - 1) { + switch (optype) { + case ISBOUND: + expr->expr = mputprintf(expr->expr, "%s = %s.is_bound();\n", + global_id.c_str(), tmp_id_str); + break; + case ISPRESENT: + expr->expr = mputprintf(expr->expr, "%s = %s.is_present(%s);\n", + global_id.c_str(), tmp_id_str, + (is_template && omit_in_value_list) ? "TRUE" : ""); + break; + case ISCHOSEN: + expr->expr = mputprintf(expr->expr, + "if (%s) {\n" + "%s = %s.ischosen(%s);\n" + "}\n", global_id.c_str(), global_id.c_str(), tmp_id_str, field); + break; + case ISVALUE: + expr->expr = mputprintf(expr->expr, "%s = %s.is_value();\n", + global_id.c_str(), tmp_id_str); + break; + default: + FATAL_ERROR("Type::generate_code_ispresentboundchosen"); + } + } + Free(tmp_generalid_str); + tmp_generalid_str = mcopystr(tmp_id_str); + t = next_t; break; } case Ttcn::FieldOrArrayRef::ARRAY_REF: { diff --git a/compiler2/Value.cc b/compiler2/Value.cc index 6817d7d62aaa618fa6e07807280dae23116dddea..2cee16d5b2e0cf423697c20b02911477b76b5c0d 100644 --- a/compiler2/Value.cc +++ b/compiler2/Value.cc @@ -4060,6 +4060,12 @@ namespace Common { exp_val == Type::EXPECTED_TEMPLATE ? "value or template" : "value", ass->get_description().c_str()); goto error; + case Assignment::A_TYPE: + if (ass->get_Type()->get_type_refd_last()->get_typetype() == Type::T_CLASS) { + tmp_type = ass->get_Type(); + break; + } + // else fall through default: error("Reference to a %s was expected instead of %s", exp_val == Type::EXPECTED_TEMPLATE ? "value or template" : "value", @@ -9536,22 +9542,27 @@ void Value::chk_expr_operand_execute_refd(Value *v1, destroy_refch = true; } if (refch->add(get_fullname())) { - Ttcn::FieldOrArrayRefs *subrefs = u.ref.ref->get_subrefs(); - Value *v_refd = ass->get_Value() - ->get_refd_sub_value(subrefs, 0, - u.ref.ref->getUsedInIsbound(), refch); - if (v_refd) { - Value *v_last = v_refd->get_value_refd_last(refch); - // in case of circular recursion the valuetype is already set - // to V_ERROR, so don't set the cache - if (valuetype == V_REFD) u.ref.refd_last = v_last; - } else if (subrefs && subrefs->has_unfoldable_index()) { + if (ass->get_my_scope()->get_parent_scope()->is_class_scope()) { u.ref.refd_last = this; - } else if (u.ref.ref->getUsedInIsbound()) { - u.ref.refd_last = this; - } else { - // the sub-reference points to a non-existent field - set_valuetype(V_ERROR); + } + else { + Ttcn::FieldOrArrayRefs *subrefs = u.ref.ref->get_subrefs(); + Value *v_refd = ass->get_Value() + ->get_refd_sub_value(subrefs, 0, + u.ref.ref->getUsedInIsbound(), refch); + if (v_refd) { + Value *v_last = v_refd->get_value_refd_last(refch); + // in case of circular recursion the valuetype is already set + // to V_ERROR, so don't set the cache + if (valuetype == V_REFD) u.ref.refd_last = v_last; + } else if (subrefs && subrefs->has_unfoldable_index()) { + u.ref.refd_last = this; + } else if (u.ref.ref->getUsedInIsbound()) { + u.ref.refd_last = this; + } else { + // the sub-reference points to a non-existent field + set_valuetype(V_ERROR); + } } } else { // a circular recursion was detected @@ -9585,6 +9596,12 @@ void Value::chk_expr_operand_execute_refd(Value *v1, ass->get_description().c_str()); set_valuetype(V_ERROR); break; + case Assignment::A_TYPE: + if (ass->get_Type()->get_type_refd_last()->get_typetype() == Type::T_CLASS) { + u.ref.refd_last = this; + break; + } + // else fall through default: u.ref.ref->error("Reference to a value was expected instead of %s", ass->get_description().c_str()); @@ -9641,12 +9658,12 @@ void Value::chk_expr_operand_execute_refd(Value *v1, case V_OPENTYPE: case V_UNDEF_LOWERID: case V_UNDEF_BLOCK: - case V_TTCN3_NULL: case V_REFER: // these value types are eliminated during semantic analysis FATAL_ERROR("Value::is_unfoldable()"); case V_ERROR: case V_INVOKE: + case V_TTCN3_NULL: return true; case V_CHOICE: return u.choice.alt_value->is_unfoldable(refch, exp_val); @@ -12911,7 +12928,8 @@ void Value::chk_expr_operand_execute_refd(Value *v1, void Value::generate_code_expr_mandatory(expression_struct *expr) { generate_code_expr(expr); - if (valuetype == V_REFD && get_value_refd_last()->valuetype == V_REFD) + if (valuetype == V_REFD && get_value_refd_last()->valuetype == V_REFD && + u.ref.ref->get_refd_assignment()->get_Type()->get_type_refd_last()->get_typetype() != Type::T_CLASS) generate_code_expr_optional_field_ref(expr, u.ref.ref); } @@ -13641,6 +13659,7 @@ void Value::chk_expr_operand_execute_refd(Value *v1, case OPTYPE_ISBOUND: { Template::templatetype_t temp = u.expr.ti1->get_Template() ->get_templatetype(); + // TODO: use get_refd_assignment instead to determine whether it's a template? if (temp == Template::SPECIFIC_VALUE) { Value* specific_value = u.expr.ti1->get_Template() ->get_specific_value(); @@ -15542,6 +15561,8 @@ void Value::chk_expr_operand_execute_refd(Value *v1, case V_ALTSTEP: case V_TESTCASE: return get_single_expr_fat(); + case V_TTCN3_NULL: + return string("NULL_VALUE"); default: FATAL_ERROR("Value::get_single_expr()"); return string(); diff --git a/compiler2/Value.hh b/compiler2/Value.hh index 94f11c021aa0f056d118d6fbb013915e2d9b4ff0..0581f6f69751538909d4cede5b29853772796212 100644 --- a/compiler2/Value.hh +++ b/compiler2/Value.hh @@ -116,7 +116,7 @@ namespace Common { V_UNDEF_BLOCK, /**< undefined {block} */ V_OMIT, /**< special value for optional values */ V_VERDICT, /**< verdict */ - V_TTCN3_NULL, /**< TTCN-3 null (for component or default references) */ + V_TTCN3_NULL, /**< TTCN-3 null (for component, default or class references) */ V_DEFAULT_NULL, /**< null default reference */ V_FAT_NULL, /**< null for function, altstep and testcase */ V_EXPR, /**< expressions */ diff --git a/compiler2/ttcn3/AST_ttcn3.cc b/compiler2/ttcn3/AST_ttcn3.cc index 6949d1c8af514a198aec3f3d571debed96591fee..544787f2383e91bde1cdb7a915ecc75c00f021ff 100644 --- a/compiler2/ttcn3/AST_ttcn3.cc +++ b/compiler2/ttcn3/AST_ttcn3.cc @@ -430,11 +430,7 @@ namespace Ttcn { bool const_ref, /* = false */ bool is_template, /* = false */ size_t nof_subrefs /* = UINT_MAX */) - { - // ensure everything is checked - // TODO: incorporate this into the semantic analysis somehow... - if (type) type->get_field_type(this, Common::Type::EXPECTED_DYNAMIC_VALUE); - + { size_t n_refs = (nof_subrefs != UINT_MAX) ? nof_subrefs : refs.size(); for (size_t i = 0; i < n_refs; i++) { if (type) type = type->get_type_refd_last(); @@ -691,21 +687,31 @@ namespace Ttcn { // ================================= Reference::Reference(const Reference& p) - : Ref_base(p), parlist(NULL), gen_const_prefix(false), expr_cache(NULL) + : Ref_base(p), reftype(p.reftype), parlist(NULL), gen_const_prefix(false), + expr_cache(NULL) { params = p.params != NULL ? p.params->clone() : NULL; } Reference::Reference(Identifier *p_id) - : Ref_base(), parlist(NULL), params(NULL), gen_const_prefix(false), - expr_cache(NULL) + : Ref_base(), reftype(REF_BASIC), parlist(NULL), params(NULL), + gen_const_prefix(false), expr_cache(NULL) { subrefs.add(new FieldOrArrayRef(p_id)); } + Reference::Reference(reftype_t p_reftype) + : Ref_base(), reftype(p_reftype), parlist(NULL), params(NULL), + gen_const_prefix(false), expr_cache(NULL) + { + if (reftype != REF_THIS) { + FATAL_ERROR("Ttcn::Reference(): basic or 'super' reference with no ID"); + } + } + Reference::Reference(Identifier *p_modid, Identifier *p_id, - ParsedActualParameters *p_params) - : Ref_base(p_modid, p_id), parlist(NULL), params(p_params), + ParsedActualParameters *p_params, reftype_t p_reftype) + : Ref_base(p_modid, p_id), reftype(p_reftype), parlist(NULL), params(p_params), gen_const_prefix(false), expr_cache(NULL) { if (p_params == NULL) { @@ -1043,6 +1049,21 @@ namespace Ttcn { ref_usage_found(); Common::Assignment *ass = get_refd_assignment(); if (!ass) FATAL_ERROR("Reference::generate_code()"); + if (reftype == REF_THIS) { + if (id != NULL) { + expr->expr = mputstr(expr->expr, "this->"); + } + else { // no 'id' means it's just a 'this' reference + expr->expr = mputprintf(expr->expr, "OBJECT_REF<%s>(this)", + ass->get_Type()->get_genname_value(my_scope).c_str()); + return; + } + } + else if (reftype == REF_SUPER) { + expr->expr = mputprintf(expr->expr, "%s::", + my_scope->get_scope_class()->get_base_type()-> + get_genname_value(my_scope).c_str()); + } string const_prefix; // empty by default if (gen_const_prefix) { if (ass->get_asstype() == Common::Assignment::A_CONST) { @@ -1053,8 +1074,6 @@ namespace Ttcn { } } if (parlist != NULL) { - // reference without parameters to a template that has only default formal parameters. - // if @lazy: nothing to do, it's a C++ function call just like in case of Ref_pard::generate_code() expr->expr = mputprintf(expr->expr, "%s(", ass->get_genname_from_scope(my_scope).c_str()); parlist->generate_code_alias(expr, ass->get_FormalParList(), @@ -1208,9 +1227,16 @@ namespace Ttcn { expression_struct isbound_expr; Code::init_expr(&isbound_expr); - isbound_expr.preamble = mputprintf(isbound_expr.preamble, + if (ass->get_Type()->get_type_refd_last()->get_typetype() == Common::Type::T_CLASS) { + isbound_expr.preamble = mputprintf(isbound_expr.preamble, + "boolean %s = %s != NULL_VALUE;\n", tmp_generalid_str, + ass_id_str); + } + else { + isbound_expr.preamble = mputprintf(isbound_expr.preamble, "boolean %s = %s.is_bound();\n", tmp_generalid_str, ass_id_str); + } namedbool p_optype; if (optype == Value::OPTYPE_ISBOUND) { p_optype = ISBOUND; @@ -1268,7 +1294,7 @@ namespace Ttcn { void Reference::detect_modid() { // do nothing if detection is already performed - if (id) return; + if (id || reftype == REF_THIS) return; // the first element of subrefs must be an <id> const Identifier *first_id = subrefs.get_ref(0)->get_id(), *second_id = 0; const ParsedActualParameters* second_params = 0; @@ -2177,14 +2203,16 @@ namespace Ttcn { ass_m.add(name, def); if (parent_scope) { if (parent_scope->has_ass_withId(id)) { - const char *dispname_str = id.get_dispname().c_str(); - def->error("Definition with identifier `%s' is not unique in the " - "scope hierarchy", dispname_str); - Reference ref(0, id.clone()); - Common::Assignment *ass = parent_scope->get_ass_bySRef(&ref); - if (!ass) FATAL_ERROR("OtherDefinitions::chk_for()"); - ass->note("Previous definition with identifier `%s' in higher " - "scope unit is here", dispname_str); + if (parent_scope->get_scope_class() == NULL) { + const char *dispname_str = id.get_dispname().c_str(); + def->error("Definition with identifier `%s' is not unique in the " + "scope hierarchy", dispname_str); + Reference ref(0, id.clone()); + Common::Assignment *ass = parent_scope->get_ass_bySRef(&ref); + if (!ass) FATAL_ERROR("OtherDefinitions::chk_for()"); + ass->note("Previous definition with identifier `%s' in higher " + "scope unit is here", dispname_str); + } } else if (parent_scope->is_valid_moduleid(id)) { def->warning("Definition with name `%s' hides a module identifier", id.get_dispname().c_str()); @@ -2608,6 +2636,11 @@ namespace Ttcn { Common::Assignment* Module::get_ass_bySRef(Ref_simple *p_ref) { + if (p_ref->get_reftype() != Ref_simple::REF_BASIC) { + p_ref->error("Reference to `%s' can only be used inside a class definition", + p_ref->get_reftype() == Ref_simple::REF_THIS ? "this" : "super"); + return NULL; + } const Identifier *r_modid = p_ref->get_modid(); const Identifier *r_id = p_ref->get_id(); if (r_modid) { @@ -9834,7 +9867,9 @@ namespace Ttcn { Common::Assignment *FormalParList::get_ass_bySRef(Common::Ref_simple *p_ref) { if (!p_ref || !checked) FATAL_ERROR("FormalParList::get_ass_bySRef()"); - if (p_ref->get_modid()) return parent_scope->get_ass_bySRef(p_ref); + if (p_ref->get_modid() || p_ref->get_reftype() != Ref_simple::REF_BASIC) { + return parent_scope->get_ass_bySRef(p_ref); + } else { const string& name = p_ref->get_id()->get_name(); if (pars_m.has_key(name)) return pars_m[name]; @@ -9900,7 +9935,8 @@ namespace Ttcn { pars_m[name]->note("Previous definition of `%s' is here", dispname); } else { pars_m.add(name, par); - if (parent_scope && parent_scope->has_ass_withId(id)) { + if (parent_scope && parent_scope->get_scope_class() == NULL && + parent_scope->has_ass_withId(id)) { par->error("Parameter name `%s' is not unique in the scope " "hierarchy", dispname); Reference ref(0, id.clone()); diff --git a/compiler2/ttcn3/AST_ttcn3.hh b/compiler2/ttcn3/AST_ttcn3.hh index 24435f66f154a67c1fc710a2d155c16893e473f9..695ddf5e37883f8b365e40a8c0ee6d578679dcf6 100644 --- a/compiler2/ttcn3/AST_ttcn3.hh +++ b/compiler2/ttcn3/AST_ttcn3.hh @@ -334,11 +334,12 @@ namespace Ttcn { }; /** - * TTCN-3 reference without parameters. + * TTCN-3 reference. * Implements the automatic detection whether the first identifier is a * module name or not. */ class Reference : public Ref_base { + reftype_t reftype; /** "Processed" parameter list, after the semantic check. */ ActualParList* parlist; /** "Raw" parameter list, before the semantic check. */ @@ -352,10 +353,11 @@ namespace Ttcn { Reference(const Reference& p); public: Reference(Identifier *p_id); - Reference(Identifier *p_modid, Identifier *p_id) - : Ref_base(p_modid, p_id), parlist(NULL), params(NULL), gen_const_prefix(false), expr_cache(NULL) { } + Reference(reftype_t p_reftype); + Reference(Identifier *p_modid, Identifier *p_id, reftype_t p_reftype = REF_BASIC) + : Ref_base(p_modid, p_id), reftype(p_reftype), parlist(NULL), params(NULL), gen_const_prefix(false), expr_cache(NULL) { } Reference(Identifier *p_modid, Identifier *p_id, - ParsedActualParameters *p_params); + ParsedActualParameters *p_params, reftype_t p_reftype = REF_BASIC); ~Reference(); virtual bool has_parameters() const; virtual Reference *clone() const; @@ -363,6 +365,8 @@ namespace Ttcn { virtual void set_my_scope(Scope* p_scope); virtual string get_dispname(); virtual Common::Assignment *get_refd_assignment(bool check_parlist = true); + virtual reftype_t get_reftype() const { return reftype; } + virtual void set_reftype(reftype_t p_reftype) { reftype = p_reftype; } virtual const Identifier* get_modid(); virtual const Identifier* get_id(); virtual ActualParList *get_parlist(); diff --git a/compiler2/ttcn3/Statement.cc b/compiler2/ttcn3/Statement.cc index 241040471a606a0b13b1b0d12d8377d59fbedfae..47ddccab1d0ce580ee0d141d2b978035f6258aa0 100644 --- a/compiler2/ttcn3/Statement.cc +++ b/compiler2/ttcn3/Statement.cc @@ -140,14 +140,16 @@ namespace Ttcn { defs.add(id, p_def); if (parent_scope) { if (parent_scope->has_ass_withId(id)) { - const char *dispname = id.get_dispname().c_str(); - p_def->error("Definition with identifier `%s' is not unique" - " in the scope hierarchy", dispname); - Reference ref(0, id.clone()); - Common::Assignment *ass = parent_scope->get_ass_bySRef(&ref); - if (!ass) FATAL_ERROR("StatementBlock::register_def()"); - ass->note("Previous definition with identifier `%s' in higher " - "scope unit is here", dispname); + if (parent_scope->get_scope_class() == NULL) { + const char *dispname = id.get_dispname().c_str(); + p_def->error("Definition with identifier `%s' is not unique" + " in the scope hierarchy", dispname); + Reference ref(0, id.clone()); + Common::Assignment *ass = parent_scope->get_ass_bySRef(&ref); + if (!ass) FATAL_ERROR("StatementBlock::register_def()"); + ass->note("Previous definition with identifier `%s' in higher " + "scope unit is here", dispname); + } } else if (parent_scope->is_valid_moduleid(id)) { p_def->warning("Definition with name `%s' hides a module identifier", id.get_dispname().c_str()); @@ -163,7 +165,9 @@ namespace Ttcn { Common::Assignment* StatementBlock::get_ass_bySRef(Ref_simple *p_ref) { - if(p_ref->get_modid()) return get_parent_scope()->get_ass_bySRef(p_ref); + if(p_ref->get_modid() || p_ref->get_reftype() != Ref_simple::REF_BASIC) { + return get_parent_scope()->get_ass_bySRef(p_ref); + } const Identifier& id=*p_ref->get_id(); if(defs.has_key(id)) return defs[id]; else return get_parent_scope()->get_ass_bySRef(p_ref); @@ -3150,6 +3154,14 @@ namespace Ttcn { } if (!t_ass) goto error; switch (t_ass->get_asstype()) { + case Common::Assignment::A_VAR: + if (t_ass->get_Type()->get_type_refd_last()->get_typetype() != Common::Type::T_CLASS) { + ref_pard->error("Reference to a function or altstep was expected " + "instead of %s, which cannot be invoked", + t_ass->get_description().c_str()); + goto error; + } + // else fall through case Common::Assignment::A_FUNCTION: case Common::Assignment::A_FUNCTION_RVAL: case Common::Assignment::A_FUNCTION_RTEMP: @@ -3232,6 +3244,19 @@ error: { Error_Context cntxt(this, "In function instance"); Common::Assignment *t_ass = ref_pard->get_refd_assignment(); + if (t_ass->get_asstype() == Common::Assignment::A_VAR) { + // it could be a class object method + Common::Assignment* last_method = NULL; + t_ass->get_Type()->get_field_type(ref_pard->get_subrefs(), + Type::EXPECTED_DYNAMIC_VALUE, 0, false, &last_method); + if (last_method == NULL) { + ref_pard->error("Reference to a function or altstep was expected"); + } + else { + // do the checks on the method at the end of the subreferences instead + t_ass = last_method; + } + } if (t_ass->get_PortType()) { ref_pard->error("Function with `port' clause cannot be called directly."); } @@ -9240,6 +9265,9 @@ error: refd_ass->get_asstype() == Common::Assignment::A_CONST) { rhs_name = string("const_") + rhs_name; } + else if (ref->get_reftype() == Ref_simple::REF_THIS) { + rhs_name = string("this->") + rhs_name; + } if (val->can_use_increment(ref)) { switch (val->get_optype()) { case Value::OPTYPE_ADD: @@ -9353,6 +9381,9 @@ error: refd_ass->get_asstype() == Common::Assignment::A_TEMPLATE) { rhs_name = string("template_") + rhs_name; } + else if (ref->get_reftype() == Ref_simple::REF_THIS) { + rhs_name = string("this->") + rhs_name; + } if (Common::Type::T_SEQOF == templ->get_my_governor()->get_typetype() || Common::Type::T_ARRAY == templ->get_my_governor()->get_typetype()) { str = mputprintf(str, "%s.remove_all_permutations();\n", (rhs_copied ? rhs_copy : rhs_name).c_str()); @@ -11476,11 +11507,20 @@ error: if (!t_ass) return; Common::Assignment::asstype_t asstype = t_ass->get_asstype(); switch (asstype) { + case Common::Assignment::A_TYPE: + if (t_ass->get_Type()->get_typetype() != Common::Type::T_CLASS) { + ref->error("Reference to a value, template, timer, port or class object " + "was expected instead of %s", t_ass->get_description().c_str()); + return; + } + // else fall through case Common::Assignment::A_FUNCTION_RVAL: case Common::Assignment::A_FUNCTION_RTEMP: case Common::Assignment::A_EXT_FUNCTION_RVAL: case Common::Assignment::A_EXT_FUNCTION_RTEMP: - ref->get_my_scope()->chk_runs_on_clause(t_ass, *this, "call"); + if (asstype != Common::Assignment::A_TYPE) { + ref->get_my_scope()->chk_runs_on_clause(t_ass, *this, "call"); + } case Common::Assignment::A_CONST: case Common::Assignment::A_EXT_CONST: case Common::Assignment::A_MODULEPAR: @@ -11527,13 +11567,13 @@ error: break; case Common::Assignment::A_FUNCTION: case Common::Assignment::A_EXT_FUNCTION: - ref->error("Reference to a value, template, timer or port was expected " - "instead of a call of %s, which does not have return type", + ref->error("Reference to a value, template, timer, port or class object " + "was expected instead of a call of %s, which does not have return type", t_ass->get_description().c_str()); break; default: - ref->error("Reference to a value, template, timer or port was expected " - "instead of %s", t_ass->get_description().c_str()); + ref->error("Reference to a value, template, timer, port or class object " + "was expected instead of %s", t_ass->get_description().c_str()); } } diff --git a/compiler2/ttcn3/TtcnTemplate.cc b/compiler2/ttcn3/TtcnTemplate.cc index 15815fb0cd4211d8e630655d43cc8169ee81f830..ffd71a721505d61d167968e9357616fa1ab8f92b 100644 --- a/compiler2/ttcn3/TtcnTemplate.cc +++ b/compiler2/ttcn3/TtcnTemplate.cc @@ -31,6 +31,7 @@ #include "../main.hh" #include "../../common/dbgnew.hh" #include "Attributes.hh" +#include "Ttcnstuff.cc" namespace Ttcn { @@ -2164,6 +2165,40 @@ namespace Ttcn { } case TEMPLATE_REFD: { Common::Assignment *ass = u.ref.ref->get_refd_assignment(); + if (ass->get_asstype() == Common::Assignment::A_VAR) { + // there could be class objects in the subreferences, which would change + // the type of the assignment (e.g. to a var template); + // use the assignment after the last class object in the subreference chain + FieldOrArrayRefs* subrefs = u.ref.ref->get_subrefs(); + if (subrefs != NULL) { + Type* type = ass->get_Type(); + if (type->get_field_type(subrefs, Common::Type::EXPECTED_DYNAMIC_VALUE) != NULL) { + // subrefs are valid + for (size_t i = 0; i < subrefs->get_nof_refs(); ++i) { + type = type->get_type_refd_last(); + FieldOrArrayRef* subref = subrefs->get_ref(i); + switch (subref->get_type()) { + case FieldOrArrayRef::FIELD_REF: + case FieldOrArrayRef::FUNCTION_REF: + if (type->get_typetype() == Common::Type::T_CLASS) { + ass = type->get_class_type_body()-> + get_local_ass_byId(*subref->get_id()); + type = ass->get_Type(); + } + else { + type = type->get_comp_byName(*subref->get_id())->get_type(); + } + break; + case FieldOrArrayRef::ARRAY_REF: + if (type->is_structured_type()) { + type = type->get_ofType(); + } + break; + } + } + } + } + } switch (ass->get_asstype()) { case Common::Assignment::A_EXT_CONST: case Common::Assignment::A_PAR_VAL: @@ -2503,9 +2538,9 @@ end: if (get_template_refd_last()->templatetype == TEMPLATE_REFD) { - Assignment *formal_param_ass = get_template_refd_last()->get_reference()->get_refd_assignment(); - if (formal_param_ass->get_asstype() >= Assignment::A_PAR_VAL) - if (formal_param_ass->get_asstype() <= Assignment::A_PAR_TEMPL_INOUT) + Common::Assignment *formal_param_ass = get_template_refd_last()->get_reference()->get_refd_assignment(); + if (formal_param_ass->get_asstype() >= Common::Assignment::A_PAR_VAL) + if (formal_param_ass->get_asstype() <= Common::Assignment::A_PAR_TEMPL_INOUT) if (formal_param_ass->get_eval_type() != NORMAL_EVAL) warning("Fuzzy parameter '%s' may change (during) the actual snapshot.", get_reference()->get_dispname().c_str()); @@ -5786,43 +5821,43 @@ compile_time: if (derived_reference) { int asstype = ((Reference*)derived_reference)->get_refd_assignment()->get_asstype(); switch (asstype) { - case Assignment::A_TYPE: /**< type */ - case Assignment::A_CONST: /**< value (const) */ - case Assignment::A_UNDEF: /**< undefined/undecided (ASN.1) */ - case Assignment::A_ERROR: /**< erroneous; the kind cannot be deduced (ASN.1) */ - case Assignment::A_OC: /**< information object class (ASN.1) */ - case Assignment::A_OBJECT: /**< information object (ASN.1) */ - case Assignment::A_OS: /**< information object set (ASN.1) */ - case Assignment::A_VS: /**< value set (ASN.1) */ - case Assignment::A_EXT_CONST: /**< external constant (TTCN-3) */ - case Assignment::A_MODULEPAR: /**< module parameter (TTCN-3) */ - case Assignment::A_MODULEPAR_TEMP: /**< template module parameter */ - case Assignment::A_VAR: /**< variable (TTCN-3) */ - case Assignment::A_VAR_TEMPLATE: /**< template variable: dynamic template (TTCN-3) */ - case Assignment::A_TIMER: /**< timer (TTCN-3) */ - case Assignment::A_PORT: /**< port (TTCN-3) */ - case Assignment::A_ALTSTEP: /**< altstep (TTCN-3) */ - case Assignment::A_TESTCASE: /**< testcase Assignment::(TTCN-3) */ - case Assignment::A_PAR_TIMER: /**< formal parameter (timer) (TTCN-3) */ - case Assignment::A_PAR_PORT: /**< formal parameter (port) (TTCN-3) */ - case Assignment::A_FUNCTION: /**< function without return type (TTCN-3) */ - case Assignment::A_FUNCTION_RVAL: /**< function that returns a value (TTCN-3) */ - case Assignment::A_FUNCTION_RTEMP: /**< function that returns a template (TTCN-3) */ - case Assignment::A_EXT_FUNCTION: /**< external function without return type (TTCN-3) */ - case Assignment::A_EXT_FUNCTION_RVAL: /**< ext. func that returns a value (TTCN-3) */ - case Assignment::A_EXT_FUNCTION_RTEMP: /**< ext. func that returns a template (TTCN-3) */ + case Common::Assignment::A_TYPE: /**< type */ + case Common::Assignment::A_CONST: /**< value (const) */ + case Common::Assignment::A_UNDEF: /**< undefined/undecided (ASN.1) */ + case Common::Assignment::A_ERROR: /**< erroneous; the kind cannot be deduced (ASN.1) */ + case Common::Assignment::A_OC: /**< information object class (ASN.1) */ + case Common::Assignment::A_OBJECT: /**< information object (ASN.1) */ + case Common::Assignment::A_OS: /**< information object set (ASN.1) */ + case Common::Assignment::A_VS: /**< value set (ASN.1) */ + case Common::Assignment::A_EXT_CONST: /**< external constant (TTCN-3) */ + case Common::Assignment::A_MODULEPAR: /**< module parameter (TTCN-3) */ + case Common::Assignment::A_MODULEPAR_TEMP: /**< template module parameter */ + case Common::Assignment::A_VAR: /**< variable (TTCN-3) */ + case Common::Assignment::A_VAR_TEMPLATE: /**< template variable: dynamic template (TTCN-3) */ + case Common::Assignment::A_TIMER: /**< timer (TTCN-3) */ + case Common::Assignment::A_PORT: /**< port (TTCN-3) */ + case Common::Assignment::A_ALTSTEP: /**< altstep (TTCN-3) */ + case Common::Assignment::A_TESTCASE: /**< testcase Common::Assignment::(TTCN-3) */ + case Common::Assignment::A_PAR_TIMER: /**< formal parameter (timer) (TTCN-3) */ + case Common::Assignment::A_PAR_PORT: /**< formal parameter (port) (TTCN-3) */ + case Common::Assignment::A_FUNCTION: /**< function without return type (TTCN-3) */ + case Common::Assignment::A_FUNCTION_RVAL: /**< function that returns a value (TTCN-3) */ + case Common::Assignment::A_FUNCTION_RTEMP: /**< function that returns a template (TTCN-3) */ + case Common::Assignment::A_EXT_FUNCTION: /**< external function without return type (TTCN-3) */ + case Common::Assignment::A_EXT_FUNCTION_RVAL: /**< ext. func that returns a value (TTCN-3) */ + case Common::Assignment::A_EXT_FUNCTION_RTEMP: /**< ext. func that returns a template (TTCN-3) */ break; - case Assignment::A_TEMPLATE: /**< template (TTCN-3) */ + case Common::Assignment::A_TEMPLATE: /**< template (TTCN-3) */ if(((Reference*)derived_reference)->get_parlist()) ((Reference*)derived_reference)->get_parlist()->chk_immutability(); break; - case Assignment::A_PAR_VAL: /**< formal parameter (value) (TTCN-3) */ - case Assignment::A_PAR_VAL_IN: /**< formal parameter (in value) (TTCN-3) */ - case Assignment::A_PAR_VAL_OUT: /**< formal parameter (out value) (TTCN-3) */ - case Assignment::A_PAR_VAL_INOUT: /**< formal parameter (inout value) (TTCN-3) */ - case Assignment::A_PAR_TEMPL_IN: /**< formal parameter ([in] template) (TTCN-3) */ - case Assignment::A_PAR_TEMPL_OUT: /**< formal parameter (out template) (TTCN-3) */ - case Assignment::A_PAR_TEMPL_INOUT:/**< formal parameter (inout template) (TTCN-3) */ + case Common::Assignment::A_PAR_VAL: /**< formal parameter (value) (TTCN-3) */ + case Common::Assignment::A_PAR_VAL_IN: /**< formal parameter (in value) (TTCN-3) */ + case Common::Assignment::A_PAR_VAL_OUT: /**< formal parameter (out value) (TTCN-3) */ + case Common::Assignment::A_PAR_VAL_INOUT: /**< formal parameter (inout value) (TTCN-3) */ + case Common::Assignment::A_PAR_TEMPL_IN: /**< formal parameter ([in] template) (TTCN-3) */ + case Common::Assignment::A_PAR_TEMPL_OUT: /**< formal parameter (out template) (TTCN-3) */ + case Common::Assignment::A_PAR_TEMPL_INOUT:/**< formal parameter (inout template) (TTCN-3) */ if (((Reference*)derived_reference)->get_refd_assignment()->get_eval_type() == FUZZY_EVAL) warning("Fuzzy parameter '%s' may change (during) the actual snapshot.", ((Reference*)derived_reference)->get_dispname().c_str()); diff --git a/compiler2/ttcn3/Ttcnstuff.cc b/compiler2/ttcn3/Ttcnstuff.cc index e5b222ae71851d74b6988f326ab5bdd32a78181b..d8954480e87fb8e41d4395a69a2ed5ac6849a3d3 100644 --- a/compiler2/ttcn3/Ttcnstuff.cc +++ b/compiler2/ttcn3/Ttcnstuff.cc @@ -2985,7 +2985,7 @@ namespace Ttcn { 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), + : Scope(), Location(), class_id(p_class_id), my_def(NULL), external(p_external), final(p_final), abstract(p_abstract), base_type(p_base_type), runs_on_ref(p_runs_on_ref), mtc_ref(p_mtc_ref), system_ref(p_system_ref), members(p_members), finally_block(p_finally_block), constructor(NULL), checked(false), default_constructor(false) @@ -2998,6 +2998,7 @@ namespace Ttcn { ClassTypeBody::ClassTypeBody(const ClassTypeBody& p) { class_id = p.class_id->clone(); + my_def = p.my_def; external = p.external; final = p.final; abstract = p.abstract; @@ -3148,8 +3149,31 @@ namespace Ttcn { if (p_ref == NULL || parent_scope == NULL) { FATAL_ERROR("ClassTypeBody::get_ass_bySRef()"); } + chk(); if (p_ref->get_modid() == NULL) { const Common::Identifier* id = p_ref->get_id(); + if (p_ref->get_reftype() == Ref_simple::REF_SUPER) { + if (base_type == NULL) { + p_ref->error("Reference to `super' in class type `%s', which has " + "no base class", class_id->get_dispname().c_str()); + return NULL; + } + else { + // send the reference to the base type, with the reftype changed to 'this' + p_ref->set_reftype(Ref_simple::REF_THIS); + Common::Assignment* ass = base_type->get_type_refd_last()-> + get_class_type_body()->get_ass_bySRef(p_ref); + p_ref->set_reftype(Ref_simple::REF_SUPER); + return ass; + } + } + else if (id == NULL && p_ref->get_reftype() == Ref_simple::REF_THIS) { + // reference is just 'this' + return my_def; + // nothing special is needed for 'this.field' or 'this.method' + // (it's already been handled at the lower scopes) + } + if (id != NULL && has_local_ass_withId(*id)) { Common::Assignment* ass = get_local_ass_byId(*id); if (ass == NULL) { @@ -3261,14 +3285,13 @@ namespace Ttcn { case Common::Assignment::A_CONST: case Common::Assignment::A_VAR: { // add a formal parameter for this member - Common::Identifier* id = new Common::Identifier( - Common::Identifier::ID_TTCN, string("p_") + member->get_id().get_ttcnname()); + Common::Identifier* id = member->get_id().clone(); FormalPar* fp = new FormalPar(is_template ? Common::Assignment::A_PAR_TEMPL_IN : Common::Assignment::A_PAR_VAL_IN, member->get_Type()->clone(), id, NULL); fp_list->add_fp(fp); // add a statement, that assigns the parameter's value to the member - Reference* ref_lhs = new Reference(NULL, member->get_id().clone()); + Reference* ref_lhs = new Reference(NULL, id->clone(), Ref_simple::REF_THIS); Reference* ref_rhs = new Reference(NULL, id->clone()); Common::Value* val_rhs = new Value(Common::Value::V_REFD, ref_rhs); Template* temp_rhs = new Template(val_rhs); @@ -3389,7 +3412,50 @@ namespace Ttcn { "}\n" "}\n\n", class_id->get_name().c_str()); } - + + // logging function (similar to logging a record value, but with the + // class name and the base class' log at the beginning) + target->header.class_defs = mputstr(target->header.class_defs, + "public:\n" + "virtual void log() const;\n"); + target->source.methods = mputprintf(target->source.methods, + "void %s::log() const\n" + "{\n" + "TTCN_Logger::log_event_str(\"%s: { \");\n", + class_id->get_name().c_str(), class_id->get_dispname().c_str()); + bool first_logged = false; + if (base_type != NULL) { + target->source.methods = mputprintf(target->source.methods, + "%s::log();\n", + base_type->get_genname_value(this).c_str()); + first_logged = true; + } + for (size_t i = 0; i < members->get_nof_asss(); ++i) { + Common::Assignment* member = members->get_ass_byIndex(i); + switch (member->get_asstype()) { + case Common::Assignment::A_CONST: + case Common::Assignment::A_VAR: + case Common::Assignment::A_TEMPLATE: + case Common::Assignment::A_VAR_TEMPLATE: + case Common::Assignment::A_TIMER: // ? + if (first_logged) { + target->source.methods = mputstr(target->source.methods, + "TTCN_Logger::log_event_str(\", \");\n"); + } + target->source.methods = mputprintf(target->source.methods, + "TTCN_Logger::log_event_str(\"%s := \");\n" + "%s.log();\n", + member->get_id().get_dispname().c_str(), member->get_id().get_name().c_str()); + first_logged = true; + break; + default: + break; // don't log anything for methods + } + } + target->source.methods = mputstr(target->source.methods, + "TTCN_Logger::log_event_str(\" }\");\n" + "}\n\n"); + target->header.class_defs = mputstr(target->header.class_defs, "};\n\n"); } else { // external class diff --git a/compiler2/ttcn3/Ttcnstuff.hh b/compiler2/ttcn3/Ttcnstuff.hh index 8578acaad1318e33a4c4af0e8cc02ff46e39bfe0..7bad71098f37da2d7fa84769353b5852de872fab 100644 --- a/compiler2/ttcn3/Ttcnstuff.hh +++ b/compiler2/ttcn3/Ttcnstuff.hh @@ -756,6 +756,7 @@ public: class ClassTypeBody : public Common::Scope, public Common::Location { Common::Identifier* class_id; // not owned + Definition* my_def; // pointer to the class type definition (not owned) boolean external; boolean final; boolean abstract; @@ -780,6 +781,8 @@ public: ClassTypeBody* clone() const; virtual ~ClassTypeBody(); + void set_my_def(Definition* p_def) { my_def = p_def; } + void set_fullname(const string& p_fullname); void set_my_scope(Scope* p_scope); void dump(unsigned level) const; @@ -788,6 +791,7 @@ public: virtual const ClassTypeBody* get_scope_class() const { return this; } Common::Identifier* get_id() const { return class_id; } Def_Constructor* get_constructor(); + Common::Type* get_base_type() const { return base_type; } bool is_parent_class(const ClassTypeBody* p_class) const; bool has_local_ass_withId(const Identifier& p_id); diff --git a/compiler2/ttcn3/compiler.l b/compiler2/ttcn3/compiler.l index 7540f485d022372f0de9e441aeae648c05d126cb..f18436ce906d3d9027fff53634b24bc859712b44 100644 --- a/compiler2/ttcn3/compiler.l +++ b/compiler2/ttcn3/compiler.l @@ -582,6 +582,30 @@ finally { RETURN_LVAL(IDentifier); } } +this { + if (oop_features) { + RETURN(ThisKeyword); + } + else { + Location loc(infile, yylloc); + loc.warning("Keyword 'this' 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); + } +} +super { + if (oop_features) { + RETURN(SuperKeyword); + } + else { + Location loc(infile, yylloc); + loc.warning("Keyword 'super' is treated as an identifier. Activate " + "compiler option '-k' to use object-oriented features."); + yylval.id = new Identifier(Identifier::ID_TTCN, string(yyleng, yytext)); + RETURN_LVAL(IDentifier); + } +} /* modifier keywords */ diff --git a/compiler2/ttcn3/compiler.y b/compiler2/ttcn3/compiler.y index 97b6ad29d28f45282f94cbbc7a1ed9f4880ee8b5..e4e0c01a411d63b1d9c2f7dae7b1514cd966093f 100644 --- a/compiler2/ttcn3/compiler.y +++ b/compiler2/ttcn3/compiler.y @@ -781,10 +781,12 @@ static const string anyname("anytype"); %token StartKeyword %token StopKeyword %token SubsetKeyword +%token SuperKeyword %token SupersetKeyword %token SystemKeyword %token TemplateKeyword %token TestcaseKeyword +%token ThisKeyword %token TimeoutKeyword %token TimestampKeyword %token TimerKeyword @@ -1946,20 +1948,20 @@ optDecodedModifier %left '*' '/' ModKeyword RemKeyword %left UnarySign -%expect 68 +%expect 75 %start GrammarRoot /* -XXX Source of conflicts (68 S/R): +XXX Source of conflicts (75 S/R): -1.) 10 conflicts in one state +1.) 12 conflicts in one state The Expression after 'return' keyword is optional in ReturnStatement. -For 10 tokens the parser cannot decide whether the token is a part of +For 12 tokens the parser cannot decide whether the token is a part of the return expression (shift) or it is the beginning of the next statement (reduce). -2.) 10 distinct states, each with one conflict caused by token '[' +2.) 13 distinct states, each with one conflict caused by token '[' The local definitions in altsteps can be followed immediately by the guard expression. When the parser sees the '[' token it cannot decide whether it belongs to the local definition as array dimension or array subreference @@ -1975,6 +1977,9 @@ The situations are the following: - var t v := ref.function(...)<subrefs> <here> [ - var template t v := decmatch (...) ref <here> [ - var t v := valueof(...)<subrefs> <here> [ +- var t v := this.field <here> [ +- var t v := this.function(...) <here> [ +- var t v := super.function(...) <here> [ 3.) 1 conflict The sequence identifier.objid can be either the beginning of a module name @@ -1991,9 +1996,9 @@ non-standard language extension. 6.) 1 Conflict due to pattern concatenation -7.) 27 conflicts in one state +7.) 29 conflicts in one state In the DecodedContentMatch rule a SingleExpression encased in round brackets is -followed by an in-line template. For 27 tokens (after the ')' ) the parser cannot +followed by an in-line template. For 29 tokens (after the ')' ) the parser cannot decide whether the token is the beginning of the in-line template (shift) or the brackets are only part of the SingleExpression itself and the conflicting token is the next segment in the expression (reduce). @@ -3550,6 +3555,7 @@ ClassDef: type->set_location(infile, @$); $$ = new Def_Type($5, type); $$->set_location(infile, @$); + class_->set_my_def($$); } ; @@ -8710,6 +8716,42 @@ Reference: // 490 ValueReference Free($2.elements); $$.ref->set_location(infile, @$); } +| ThisKeyword '.' IDentifier optExtendedFieldReference + { + $$.is_ref = true; + $$.ref = new Ttcn::Reference(NULL, $3, Ref_simple::REF_THIS); + for (size_t i = 0; i < $4.nElements; i++) { + $$.ref->add($4.elements[i]); + } + Free($4.elements); + $$.ref->set_location(infile, @$); + } +| ThisKeyword '.' IDentifier '(' optFunctionActualParList ')' optExtendedFieldReference + { + $$.is_ref = true; + $$.ref = new Ttcn::Reference(NULL, $3, $5, Ref_simple::REF_THIS); + for (size_t i = 0; i < $7.nElements; i++) { + $$.ref->add($7.elements[i]); + } + Free($7.elements); + $$.ref->set_location(infile, @$); + } +| ThisKeyword + { + $$.is_ref = true; + $$.ref = new Ttcn::Reference(Ref_simple::REF_THIS); + $$.ref->set_location(infile, @$); + } +| SuperKeyword '.' IDentifier '(' optFunctionActualParList ')' optExtendedFieldReference + { + $$.is_ref = true; + $$.ref = new Ttcn::Reference(NULL, $3, $5, Ref_simple::REF_SUPER); + for (size_t i = 0; i < $7.nElements; i++) { + $$.ref->add($7.elements[i]); + } + Free($7.elements); + $$.ref->set_location(infile, @$); + } ; /* A.1.6.5 Parameterization */ @@ -10620,6 +10662,18 @@ IschosenArg: /* see also Reference... */ $$.ref->set_location(infile, @1, @4); $$.id = $6; } +| IDentifier '.' IDentifier '(' optFunctionActualParList ')' + optExtendedFieldReference '.' PredefOrIdentifier + { + $$.ref = new Ttcn::Reference($1); + FieldOrArrayRef* funcref = new FieldOrArrayRef($3, $5); + funcref->set_location(infile, @3, @5); + $$.ref->add(funcref); + for (size_t i = 0; i < $7.nElements; i++) $$.ref->add($7.elements[i]); + Free($7.elements); + $$.ref->set_location(infile, @1, @7); + $$.id = $9; + } | IDentifier ArrayOrBitRef optExtendedFieldReference '.' PredefOrIdentifier { $$.ref = new Ttcn::Reference($1); diff --git a/conformance_test/core_language_tests/negative_tests/15_templates.script b/conformance_test/core_language_tests/negative_tests/15_templates.script index 7c874d025287fc8a8d59764eebb4daecb0357eb9..0c815bb18fc21711d3954c8e53790653ba391ea2 100644 --- a/conformance_test/core_language_tests/negative_tests/15_templates.script +++ b/conformance_test/core_language_tests/negative_tests/15_templates.script @@ -4795,7 +4795,7 @@ module NegSem_1511_ConcatenatingTemplatesOfStringAndListTypes_005 { <END_MODULE> <RESULT COUNT 1> -error: at or before token `\*': syntax error, unexpected '\*', expecting Identifier or Cstring or CharKeyword +error: at or before token `\*': syntax error, unexpected '\*' <END_RESULT> <END_TC> diff --git a/conformance_test/core_language_tests/negative_tests/16-20_folders.script b/conformance_test/core_language_tests/negative_tests/16-20_folders.script index c66c3ba686938fd6112a953ca12623890568e836..437cef07caefb6e8dd6f30a2a86aee0d2522b109 100644 --- a/conformance_test/core_language_tests/negative_tests/16-20_folders.script +++ b/conformance_test/core_language_tests/negative_tests/16-20_folders.script @@ -3563,7 +3563,7 @@ module NegSem_1911_log_statement_001 { <END_MODULE> <RESULT COUNT 1> -error: Reference to a value, template, timer or port was expected instead of a call of function `@NegSem_1911_log_statement_001.f_no_return', which does not have return type +error: Reference to a value, template, timer, port or class object was expected instead of a call of function `@NegSem_1911_log_statement_001.f_no_return', which does not have return type <END_RESULT> <END_TC> diff --git a/core/OOP.hh b/core/OOP.hh index 0b0a59fd08f47fb2cd318b162cf9063b83c3f20e..a3ddd55e000a1a584c074f8750a0fff5acde055c 100644 --- a/core/OOP.hh +++ b/core/OOP.hh @@ -14,15 +14,32 @@ #define OOP_HH #include "Universal_charstring.hh" +#include "Logger.hh" // OBJECT // ------ class OBJECT { +private: + size_t ref_count; + + OBJECT(const OBJECT&); // copy disabled + OBJECT operator=(const OBJECT&); // assignment disabled + boolean operator==(const OBJECT&); // equality operator disabled public: - boolean operator==(const OBJECT&) const { return TRUE; } - UNIVERSAL_CHARSTRING toString() const { - return UNIVERSAL_CHARSTRING("Object"); + OBJECT(): ref_count(0) {} + virtual ~OBJECT() { + if (ref_count != 0) { + TTCN_error("Internal error: deleting an object with %lu reference(s) left.", ref_count); + } + } + void add_ref() { ++ref_count; } + boolean remove_ref() { + --ref_count; + return ref_count == 0; + } + void log() const { + TTCN_Logger::log_event_str("object: { }"); } }; @@ -31,37 +48,34 @@ public: template <typename T> class OBJECT_REF { -public: - struct object_ref_struct { - T* obj_ptr; - size_t ref_count; - }; - + template <typename T2> + friend boolean operator==(null_type, const OBJECT_REF<T2>& right_val); + template <typename T2> + friend boolean operator!=(null_type, const OBJECT_REF<T2>& right_val); private: - object_ref_struct* val_ptr; // NULL if it's a null reference + T* 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; + if (ptr != NULL) { + if (ptr->remove_ref()) { + delete ptr; } - val_ptr = NULL; + ptr = NULL; } } public: - OBJECT_REF(): val_ptr(NULL) {} // constructor for null reference + OBJECT_REF(): ptr(NULL) {} // constructor with no parameters + + OBJECT_REF(null_type): 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(T* p_ptr): ptr(p_ptr) { // constructor for new value (.create) + ptr->add_ref(); } - 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(const OBJECT_REF<T>& p_other): ptr(p_other.ptr) { // copy constructor + if (ptr != NULL) { + ptr->add_ref(); } } @@ -75,45 +89,70 @@ public: 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; + if (p_other.ptr != NULL) { + ptr = p_other.ptr; + ptr->add_ref(); } 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==(null_type) const { // equality operator (with null reference) + return ptr == NULL; } - boolean operator!=(const OBJECT_REF<T>& p_other) const { // inequality operator - return !(*this == p_other); + boolean operator!=(null_type) const { // inequality operator (with null reference) + return ptr != NULL; + } + + boolean operator==(const OBJECT_REF<T>& p_other) const { // equality operator (with actual reference) + return ptr == p_other.ptr; + } + + boolean operator!=(const OBJECT_REF<T>& p_other) const { // inequality operator (with actual reference) + return ptr != p_other.ptr; } T* operator*() { // de-referencing operator - if (val_ptr != NULL) { - return val_ptr->obj_ptr; + if (ptr != NULL) { + return ptr; } TTCN_error("Accessing a null reference."); } T* operator->() { // de-referencing operator (for methods) - if (val_ptr != NULL) { - return val_ptr->obj_ptr; + if (ptr != NULL) { + return 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; + if (ptr != NULL) { + return ptr; } TTCN_error("Accessing a null reference."); } + + void log() const { + if (ptr == NULL) { + TTCN_Logger::log_event_str("null"); + } + else { + ptr->log(); + } + } }; +template<typename T> +boolean operator==(null_type, const OBJECT_REF<T>& right_val) { // equality operator (with null reference, inverted) + return right_val.ptr == NULL; +} + +template<typename T> +boolean operator!=(null_type, const OBJECT_REF<T>& right_val) { // inequality operator (with null reference, inverted) + return right_val.ptr != NULL; +} + // EXCEPTION // --------- diff --git a/function_test/Semantic_Analyser/TTCN3_SA_ttcn3adhoc_TD.script b/function_test/Semantic_Analyser/TTCN3_SA_ttcn3adhoc_TD.script index fa64dff85a34de2aa01b3d3c7d503eba7e3587a0..da7f73d712b523225bb0f4b3d6718169c3ad118f 100644 --- a/function_test/Semantic_Analyser/TTCN3_SA_ttcn3adhoc_TD.script +++ b/function_test/Semantic_Analyser/TTCN3_SA_ttcn3adhoc_TD.script @@ -4380,10 +4380,10 @@ control { } <END_MODULE> <RESULT COUNT 2> -(?is)\berror: at or before token `12': syntax error, unexpected Number, expecting Identifier or Cstring +(?is)\berror: at or before token `12': syntax error, unexpected Number <END_RESULT> <RESULT COUNT 2> -(?is)\berror: at or before token `refers': syntax error, unexpected RefersKeyword, expecting Identifier or Cstring +(?is)\berror: at or before token `refers': syntax error, unexpected RefersKeyword <END_RESULT> <RESULT COUNT 6> (?is)\berror: Invalid reference expression diff --git a/regression_test/oop/oop.ttcn b/regression_test/oop/oop.ttcn index e1b9550f0f63ed50a1e9006e51edee492154c746..f8213cc6eb999c775024bf0da315a1ca658cd82c 100644 --- a/regression_test/oop/oop.ttcn +++ b/regression_test/oop/oop.ttcn @@ -28,6 +28,11 @@ type record Rec { IntList list } +type union Uni { + integer i, + charstring cs +} + type class BaseClass runs on CT mtc CT system CT { public const integer m_const := 1; private const IntList m_const2 := { 1, 2, 3 }; @@ -70,6 +75,28 @@ type class BaseClass runs on CT mtc CT system CT { public function get_var() return charstring { return m_var; } + + public function this_test(in integer get_var, in charstring p_log_str) { + var integer m_const := 9; + if (this.m_const != 2) { + setverdict(fail, "this.m_const = ", this.m_const); + } + if (this.get_var() != m_var) { + setverdict(fail, "this.get_var() = ", this.get_var(), ", m_var = ", m_var); + } + if (log2str(this) != p_log_str) { + setverdict(fail, "this = ", this, ", expected: ", p_log_str); + } + var BaseClass v_ref := this; + if (not v_ref == this) { + setverdict(fail, "equality failed"); + } + var BaseClass v_ref2; + v_ref2 := this; + if (this != v_ref2) { + setverdict(fail, "inequality failed"); + } + } } finally { //pt.send(-1); @@ -87,9 +114,7 @@ type class SubClass extends BaseClass { const octetstring m_const3 := 'AB'O; // the parser currently doesn't accept constants without initial value 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; + return super.f(x) - 1; } } @@ -100,9 +125,15 @@ type class @final FinalClass extends SubClass { private var template octetstring m_final_var_temp; public function @final f(in integer x) return integer { - //return super.super.f(x) + m_final_const; // not supported yet - m_var := m_var & int2str(x); - return x + m_final_const; + return super.f(x) + m_final_const + 1; + } + + public function get_uni() return Uni { + return { i := m_final_const }; + } + + public function get_uni_temp() return template Uni { + return { cs := m_final_temp }; } } @@ -144,10 +175,6 @@ testcase tc_members_and_methods() runs on CT { var BaseClass v_sub := SubClass.create(4, il, "a", 'FF'O, tf); var BaseClass v_final := FinalClass.create(4, il, "a", 'FF'O, tf, 8, "x", -1.5, *); - //log(v_base); - //log(v_sub); - //log(v_final); - log(v_base.f(6)); log(v_sub.f(6)); log(v_final.f(6)); @@ -163,7 +190,7 @@ testcase tc_members_and_methods() runs on CT { if (v_sub.f(6) != 5) { setverdict(fail, "v_sub.f(6) = ", v_sub.f(6)); } - if (v_final.f(6) != 7) { + if (v_final.f(6) != 14) { setverdict(fail, "v_final.f(6) = ", v_final.f(6)); } @@ -186,9 +213,157 @@ testcase tc_members_and_methods() runs on CT { setverdict(pass); } +testcase tc_logging() runs on CT { + var BaseClass v_base := BaseClass.create(4, { 1, 2, 4 }, "a", 'FF'O, (0.0 .. 10.0)); + var IntList il := { 1, 2, 4 }; + var template float tf := (0.0 .. 10.0); + var BaseClass v_sub := SubClass.create(4, il, "a", 'FF'O, tf); + var BaseClass v_final := FinalClass.create(4, il, "a", 'FF'O, tf, 8, "x", -1.5, *); + + log(v_base); + log(v_sub); + log(v_final); + + var charstring v_base_str := "BaseClass: { m_const := 2, m_const2 := { 1, 2, 1 }, m_temp := *, m_timer := timer: { name: m_timer, default duration: none, state: inactive }, m_timer_array := { timer: { name: m_timer_array[0], default duration: none, state: inactive }, timer: { name: m_timer_array[1], default duration: none, state: inactive }, timer: { name: m_timer_array[2], default duration: none, state: inactive } }, m_var := \"a\", m_var_temp := (0.000000 .. 10.000000) }"; + var charstring v_sub_str := "SubClass: { BaseClass: { m_const := 2, m_const2 := { 1, 2, 1 }, m_temp := *, m_timer := timer: { name: m_timer, default duration: none, state: inactive }, m_timer_array := { timer: { name: m_timer_array[0], default duration: none, state: inactive }, timer: { name: m_timer_array[1], default duration: none, state: inactive }, timer: { name: m_timer_array[2], default duration: none, state: inactive } }, m_var := \"ax\", m_var_temp := (0.000000 .. 10.000000) }, m_const3 := 'FFFF'O }"; + var charstring v_final_str := "FinalClass: { SubClass: { BaseClass: { m_const := 2, m_const2 := { 1, 2, 1 }, m_temp := *, m_timer := timer: { name: m_timer, default duration: none, state: inactive }, m_timer_array := { timer: { name: m_timer_array[0], default duration: none, state: inactive }, timer: { name: m_timer_array[1], default duration: none, state: inactive }, timer: { name: m_timer_array[2], default duration: none, state: inactive } }, m_var := \"ax\", m_var_temp := (0.000000 .. 10.000000) }, m_const3 := 'FFFF'O }, m_final_const := 8, m_final_temp := \"x\", m_final_var := -1.500000, m_final_var_temp := * }"; + + if (log2str(v_base) != v_base_str) { + setverdict(fail, "v_base: ", v_base); + } + if (log2str(v_sub) != v_sub_str) { + setverdict(fail, "v_sub: ", v_sub); + } + if (log2str(v_final) != v_final_str) { + setverdict(fail, "v_final: ", v_final); + } + setverdict(pass); +} + +testcase tc_equality() runs on CT { + var BaseClass v_base := BaseClass.create(4, { 1, 2, 4 }, "a", 'FF'O, (0.0 .. 10.0)); + var BaseClass v_base2 := BaseClass.create(4, { 1, 2, 4 }, "a", 'FF'O, (0.0 .. 10.0)); + if (v_base == v_base2) { + setverdict(fail, "Equality of different objects failed"); + } + if (not v_base != v_base2) { + setverdict(fail, "Inquality of different objects failed"); + } + + var BaseClass v_ref := v_base; + if (not v_ref == v_base) { + setverdict(fail, "Equality of object and reference to object failed"); + } + if (v_ref != v_base) { + setverdict(fail, "Inequality of object and reference to object failed"); + } + + var BaseClass v_ref2 := v_ref; + if (not v_ref2 == v_base) { + setverdict(fail, "Equality of object and indirect reference to object failed"); + } + if (v_ref2 != v_base) { + setverdict(fail, "Inequality of object and indirect reference to object failed"); + } + setverdict(pass); +} + +testcase tc_null() runs on CT { + var BaseClass v_base := BaseClass.create(4, { 1, 2, 4 }, "a", 'FF'O, (0.0 .. 10.0)); + var IntList il := { 1, 2, 4 }; + var template float tf := (0.0 .. 10.0); + var SubClass v_sub := SubClass.create(4, il, "a", 'FF'O, tf); + var FinalClass v_final := FinalClass.create(4, il, "a", 'FF'O, tf, 8, "x", -1.5, *); + + var BaseClass v_null := null; + var BaseClass v_empty; + + log(v_null); + log(v_empty); + + if (v_base == v_null) { + setverdict(fail, "BaseClass equality with null reference failed"); + } + if (v_base == v_empty) { + setverdict(fail, "BaseClass equality with empty reference failed"); + } + if (v_null != v_empty) { + setverdict(fail, "null reference equality with empty reference failed"); + } + if (v_base == null) { + setverdict(fail, "BaseClass equality with null value failed"); + } + if (null == v_final) { + setverdict(fail, "FinalClass equality with null value failed"); + } + if (v_null != null) { + setverdict(fail, "null reference equality with null value failed"); + } + if (null != v_empty) { + setverdict(fail, "null value equality with empty reference failed"); + } + setverdict(pass); +} + +testcase tc_this() runs on CT { + var BaseClass v_base := BaseClass.create(4, { 1, 2, 4 }, "a", 'FF'O, (0.0 .. 10.0)); + v_base.this_test(10, log2str(v_base)); + setverdict(pass); +} + +testcase tc_references() runs on CT { + var BaseClass v_base := BaseClass.create(4, { 1, 2, 4 }, "a", 'FF'O, 1.0); + var SubClass v_sub := SubClass.create(4, { 1, 2, 4 }, "a", 'FF'O, 1.0); + var FinalClass v_final := FinalClass.create(4, { 1, 2, 4 }, "a", 'FF'O, 1.0, 8, "x", -1.5, *); + + var BaseClass v_ref1; + if (v_ref1 != null) { + setverdict(fail, "#1"); + } + v_ref1 := v_base; + if (v_ref1 != v_base) { + setverdict(fail, "#2"); + } + var integer v_int := v_final.f(1); + if (v_int != 9) { + setverdict(fail, "#3, ", v_final.f(1)); + } + v_int := v_final.f(2); + if (v_int != 10) { + setverdict(fail, "#4, ", v_final.f(2)); + } + if (not isbound(v_final.f2(3).list[1])) { + setverdict(fail, "#5"); + } + if (not isvalue(v_base.m_var_temp)) { + setverdict(fail, "#6"); + } + if (not ispresent(v_base.m_var_temp)) { + setverdict(fail, "#7"); + } + if (ischosen(v_final.get_uni().cs)) { + setverdict(fail, "#8"); + } + if (not ischosen(v_final.get_uni_temp().cs)) { + setverdict(fail, "#9"); + } + if (v_final.f2(3).list[1] + v_final.f(-6) != v_base.f(3) * float2int(valueof(v_sub.m_var_temp))) { + setverdict(fail, "#10, ", v_final.f2(3).list[1] + v_final.f(-6), " != ", v_base.f(3) * float2int(valueof(v_sub.m_var_temp))); + } + if (match(v_final.get_uni(), v_final.get_uni_temp())) { + setverdict(fail, "#11, ", match(v_final.get_uni(), v_final.get_uni_temp())); + } + setverdict(pass); +} + control { execute(tc_members_and_methods()); + execute(tc_logging()); + execute(tc_equality()); + execute(tc_null()); + execute(tc_this()); + execute(tc_references()); } }