diff --git a/compiler2/AST.cc b/compiler2/AST.cc index d448f0ce336307f094599e4c9c636d5bdb4689e1..88ffb738d6cae82f220fcb5e0d99a5e13420a856 100644 --- a/compiler2/AST.cc +++ b/compiler2/AST.cc @@ -1927,6 +1927,11 @@ namespace Common { { return 0; } + + Type *Assignment::get_PortType() + { + return 0; + } void Assignment::chk_ttcn_id() { diff --git a/compiler2/AST.hh b/compiler2/AST.hh index 0be4137b53bae36aea0aa6fe77b275c71d189716..9a5b303ff1ff39a53a654807524a2a91f65ac5a5 100644 --- a/compiler2/AST.hh +++ b/compiler2/AST.hh @@ -18,6 +18,7 @@ * Kovacs, Ferenc * Raduly, Csaba * Szabados, Kristof + * Szabo, Bence Janos * Szabo, Janos Zoltan – initial implementation * Szalai, Gabor * Zalanyi, Balazs Andor @@ -588,6 +589,9 @@ namespace Common { /** Returns the component type referred by the 'runs on' clause of a * TTCN-3 definition */ virtual Type *get_RunsOnType(); + /** Returns the port type referred by the 'port' clause of a + * TTCN-3 function definition */ + virtual Type *get_PortType(); /** Semantic check */ virtual void chk() = 0; /** Checks whether the assignment has a valid TTCN-3 identifier, diff --git a/compiler2/Setting.cc b/compiler2/Setting.cc index 5dc5c31a3332213dd5f4b006b3c3bc1b94a05a7f..3f4a4158ec9afb4bd28582a72971b86891093d82 100644 --- a/compiler2/Setting.cc +++ b/compiler2/Setting.cc @@ -18,6 +18,7 @@ * Kovacs, Ferenc * Raduly, Csaba * Szabados, Kristof + * Szabo, Bence Janos * Szabo, Janos Zoltan – initial implementation * Tatarka, Gabor * @@ -571,6 +572,12 @@ namespace Common { if (parent_scope) return parent_scope->get_scope_runs_on(); else return 0; } + + Ttcn::PortScope *Scope::get_scope_port() + { + if (parent_scope) return parent_scope->get_scope_port(); + else return 0; + } Assignments *Scope::get_scope_asss() { diff --git a/compiler2/Setting.hh b/compiler2/Setting.hh index 1c84988b88f90b522cdd7b5ec6e17316a02acca8..3a1a11c6132a4b44a5f81d5cc885747b371efb35 100644 --- a/compiler2/Setting.hh +++ b/compiler2/Setting.hh @@ -18,6 +18,7 @@ * Kovacs, Ferenc * Raduly, Csaba * Szabados, Kristof + * Szabo, Bence Janos * Szabo, Janos Zoltan – initial implementation * Tatarka, Gabor * Zalanyi, Balazs Andor @@ -55,6 +56,7 @@ namespace Ttcn { class FieldOrArrayRefs; class ActualParList; class RunsOnScope; + class PortScope; class StatementBlock; struct ErroneousDescriptor; class ErroneousDescriptors; @@ -597,6 +599,9 @@ public: /** Returns the scope unit of the hierarchy that belongs to a * 'runs on' clause. */ virtual Ttcn::RunsOnScope *get_scope_runs_on(); + /** Returns the scope unit of the hierarchy that belongs to a + * 'port' clause. */ + virtual Ttcn::PortScope *get_scope_port(); /** Returns the assignments/module definitions scope. */ virtual Assignments *get_scope_asss(); /** Gets the module scope. This function returns this scope or a diff --git a/compiler2/Type.cc b/compiler2/Type.cc index 1885e5d2f99e7d05257bb9c3fba12bd0a1264f3e..cdb774e41af243d50eaaf74ce3bf1a68e6e55c5c 100644 --- a/compiler2/Type.cc +++ b/compiler2/Type.cc @@ -4556,7 +4556,7 @@ namespace Common { maps = encode ? maps : inmaps; maps->set_my_scope(this->get_my_scope()); // The first param should be true, the other is not important if the first is true - maps->chk(true, false); + maps->chk(this, true, false); // look for coding settings t = encode ? this : Type::get_pooltype(T_BSTR); mapping = maps->get_mapping_byType(t); diff --git a/compiler2/Type.hh b/compiler2/Type.hh index 1715ce1ff14d1dda8b69a292e63fe73deea1cb4a..cb886ab93925ab2f4d56d8de0a24684278a81779 100644 --- a/compiler2/Type.hh +++ b/compiler2/Type.hh @@ -259,6 +259,7 @@ namespace Common { OT_OCFT, ///< another Type (T_OCFT), ASN.1 obj.class field type OT_TEMPLATE_INST, ///< a TemplateInstance (TTCN-3) OT_RUNSON_SCOPE, ///< a RunsOnScope (TTCN-3) + OT_PORT_SCOPE, ///< a port scope OT_EXC_SPEC, ///< exception Specification (ExcSpec) OT_SIG_PAR, ///< signature parameter (SignatureParam) OT_POOL ///< It's a pool type, owned by the type pool diff --git a/compiler2/ttcn3/AST_ttcn3.cc b/compiler2/ttcn3/AST_ttcn3.cc index d15eab0d55bf2209f894f652fb448ccb14bc53ca..d568e92c78e9af2c07e1f7d7e5011ee5b1906f75 100644 --- a/compiler2/ttcn3/AST_ttcn3.cc +++ b/compiler2/ttcn3/AST_ttcn3.cc @@ -1212,6 +1212,77 @@ namespace Ttcn { || parent_scope->has_ass_withId(p_id); } + // ================================= + // ===== PortScope + // ================================= + + PortScope::PortScope(Type *p_porttype) + : Scope(), port_type(p_porttype) + { + if (!p_porttype || p_porttype->get_typetype() != Type::T_PORT) + FATAL_ERROR("PortScope::PortScope()"); + port_type->set_ownertype(Type::OT_PORT_SCOPE, this); + PortTypeBody* ptb = p_porttype->get_PortBody(); + if (!ptb) + FATAL_ERROR("PortScope::PortScope()"); + vardefs = ptb->get_vardefs(); + set_scope_name("port `" + p_porttype->get_fullname() + "'"); + } + + PortScope *PortScope::clone() const + { + FATAL_ERROR("PortScope::clone()"); + } + + void PortScope::chk_uniq() + { + if (!vardefs) return; + if (vardefs->get_nof_asss() == 0) return; + // do not perform this check if the port type is defined in the same + // module as the 'port' clause + if (parent_scope->get_scope_mod() == vardefs->get_scope_mod()) + return; + + size_t nof_defs = vardefs->get_nof_asss(); + for (size_t i = 0; i < nof_defs; i++) { + Common::Assignment *vardef = vardefs->get_ass_byIndex(i); + const Identifier& id = vardef->get_id(); + if (parent_scope->has_ass_withId(id)) { + vardef->warning("Imported port element definition `%s' hides a " + "definition at module scope", vardef->get_fullname().c_str()); + Reference ref(0, id.clone()); + Common::Assignment *hidden_ass = parent_scope->get_ass_bySRef(&ref); + hidden_ass->warning("Hidden definition `%s' is here", + hidden_ass->get_fullname().c_str()); + } + } + } + + PortScope *PortScope::get_scope_port() + { + return this; + } + + Common::Assignment *PortScope::get_ass_bySRef(Ref_simple *p_ref) + { + if (!p_ref) FATAL_ERROR("Ttcn::PortScope::get_ass_bySRef()"); + if (p_ref->get_modid()) return parent_scope->get_ass_bySRef(p_ref); + else { + const Identifier& id = *p_ref->get_id(); + if (vardefs->has_local_ass_withId(id)) { + Common::Assignment* ass = vardefs->get_local_ass_byId(id); + if (!ass) FATAL_ERROR("Ttcn::PortScope::get_ass_bySRef()"); + return ass; + } else return parent_scope->get_ass_bySRef(p_ref); + } + } + + bool PortScope::has_ass_withId(const Identifier& p_id) + { + return vardefs->has_ass_withId(p_id) + || parent_scope->has_ass_withId(p_id); + } + // ================================= // ===== FriendMod // ================================= @@ -2238,6 +2309,9 @@ namespace Ttcn { for (size_t i = 0; i < runs_on_scopes.size(); i++) delete runs_on_scopes[i]; runs_on_scopes.clear(); + for (size_t i = 0; i < port_scopes.size(); i++) + delete port_scopes[i]; + port_scopes.clear(); delete w_attrib_path; } @@ -2766,7 +2840,15 @@ namespace Ttcn { ret_val->chk_uniq(); return ret_val; } - + + PortScope *Module::get_port_scope(Type *porttype) + { + PortScope *ret_val = new PortScope(porttype); + port_scopes.add(ret_val); + ret_val->set_parent_scope(asss); + ret_val->chk_uniq(); + return ret_val; + } void Module::dump(unsigned level) const { @@ -6178,13 +6260,15 @@ namespace Ttcn { // ================================= Def_Function::Def_Function(Identifier *p_id, FormalParList *p_fpl, - Reference *p_runs_on_ref, Type *p_return_type, + Reference *p_runs_on_ref, Reference *p_port_ref, + Type *p_return_type, bool returns_template, template_restriction_t p_template_restriction, StatementBlock *p_block) : Def_Function_Base(false, p_id, p_fpl, p_return_type, returns_template, p_template_restriction), - runs_on_ref(p_runs_on_ref), runs_on_type(0), block(p_block), + runs_on_ref(p_runs_on_ref), runs_on_type(0), + port_ref(p_port_ref), port_type(0), block(p_block), is_startable(false), transparent(false) { if (!p_block) FATAL_ERROR("Def_Function::Def_Function()"); @@ -6194,6 +6278,7 @@ namespace Ttcn { Def_Function::~Def_Function() { delete runs_on_ref; + delete port_ref; delete block; } @@ -6206,6 +6291,7 @@ namespace Ttcn { { Def_Function_Base::set_fullname(p_fullname); if (runs_on_ref) runs_on_ref->set_fullname(p_fullname + ".<runs_on_type>"); + if (port_ref) port_ref->set_fullname(p_fullname + ".<port_type>"); block->set_fullname(p_fullname + ".<statement_block>"); } @@ -6216,6 +6302,7 @@ namespace Ttcn { Def_Function_Base::set_my_scope(&bridgeScope); if (runs_on_ref) runs_on_ref->set_my_scope(&bridgeScope); + if (port_ref) port_ref->set_my_scope(&bridgeScope); block->set_my_scope(fp_list); } @@ -6224,6 +6311,12 @@ namespace Ttcn { if (!checked) chk(); return runs_on_type; } + + Type *Def_Function::get_PortType() + { + if (!checked) chk(); + return port_type; + } RunsOnScope *Def_Function::get_runs_on_scope(Type *comptype) { @@ -6232,12 +6325,23 @@ namespace Ttcn { return my_module->get_runs_on_scope(comptype); } + PortScope *Def_Function::get_port_scope(Type *porttype) + { + Module *my_module = dynamic_cast<Module*>(my_scope->get_scope_mod()); + if (!my_module) FATAL_ERROR("Def_Function::get_port_scope()"); + return my_module->get_port_scope(porttype); + } + void Def_Function::chk() { if (checked) return; checked = true; Error_Context cntxt(this, "In function definition `%s'", id->get_dispname().c_str()); + // `runs on' clause and `port' clause are mutually exclusive + if (runs_on_ref && port_ref) { + runs_on_ref->error("A `runs on' and a `port' clause cannot be present at the same time."); + } // checking the `runs on' clause if (runs_on_ref) { Error_Context cntxt2(runs_on_ref, "In `runs on' clause"); @@ -6249,39 +6353,7 @@ namespace Ttcn { fp_list->set_my_scope(runs_on_scope); } } - // checking the formal parameter list - fp_list->chk(asstype); - // checking of return type - if (return_type) { - Error_Context cntxt2(return_type, "In return type"); - return_type->chk(); - return_type->chk_as_return_type(asstype == A_FUNCTION_RVAL,"function"); - } - // decision of startability - is_startable = runs_on_ref != 0; - if (is_startable && !fp_list->get_startability()) is_startable = false; - if (is_startable && return_type && return_type->is_component_internal()) - is_startable = false; - // checking of statement block - block->chk(); - if (return_type) { - // checking the presence of return statements - switch (block->has_return()) { - case StatementBlock::RS_NO: - error("The function has return type, but it does not have any return " - "statement"); - break; - case StatementBlock::RS_MAYBE: - error("The function has return type, but control might leave it " - "without reaching a return statement"); - default: - break; - } - } - if (!semantic_check_only) { - fp_list->set_genname(get_genname()); - block->set_code_section(GovernedSimple::CS_INLINE); - } + if (w_attrib_path) { w_attrib_path->chk_global_attrib(); w_attrib_path->chk_no_qualif(); @@ -6325,8 +6397,65 @@ namespace Ttcn { } } chk_prototype(); + + // checking the `port' clause + if (port_ref) { + Error_Context cntxt2(port_ref, "In `port' clause"); + Assignment *ass = port_ref->get_refd_assignment(); + if (ass) { + port_type = ass->get_Type(); + if (port_type) { + switch (port_type->get_typetype()) { + case Type::T_PORT: { + Scope *port_scope = get_port_scope(port_type); + port_scope->set_parent_scope(my_scope); + fp_list->set_my_scope(port_scope); + break; } + default: + port_ref->error( + "Reference `%s' does not refer to a port type.", + port_ref->get_dispname().c_str()); + } + } else { + FATAL_ERROR("Def_Function::chk()"); + } + } + } + // checking the formal parameter list + fp_list->chk(asstype); + // checking of return type + if (return_type) { + Error_Context cntxt2(return_type, "In return type"); + return_type->chk(); + return_type->chk_as_return_type(asstype == A_FUNCTION_RVAL,"function"); + } + // decision of startability + is_startable = runs_on_ref != 0; + if (is_startable && !fp_list->get_startability()) is_startable = false; + if (is_startable && return_type && return_type->is_component_internal()) + is_startable = false; + // checking of statement block + block->chk(); + if (return_type) { + // checking the presence of return statements + switch (block->has_return()) { + case StatementBlock::RS_NO: + error("The function has return type, but it does not have any return " + "statement"); + break; + case StatementBlock::RS_MAYBE: + error("The function has return type, but control might leave it " + "without reaching a return statement"); + default: + break; + } + } + if (!semantic_check_only) { + fp_list->set_genname(get_genname()); + block->set_code_section(GovernedSimple::CS_INLINE); + } } - + bool Def_Function::chk_startable() { if (!checked) chk(); @@ -6346,8 +6475,14 @@ namespace Ttcn { return false; } - void Def_Function::generate_code(output_struct *target, bool) + void Def_Function::generate_code(output_struct *target, bool clean_up) { + // Functions with 'port' clause are generated into the port type's class def + // Reuse of clean_up variable to allow or disallow the generation. + // clean_up is true when it is called from PortTypeBody::generate_code()) + if (port_type && !clean_up) { + return; + } transparency_holder glass(*this); const string& t_genname = get_genname(); const char *genname_str = t_genname.c_str(); @@ -6384,15 +6519,21 @@ namespace Ttcn { fp_list->generate_code_defval(target); // function prototype target->header.function_prototypes = - mputprintf(target->header.function_prototypes, "extern %s %s(%s);\n", + mputprintf(target->header.function_prototypes, "%s%s %s(%s);\n", + get_PortType() && clean_up ? "" : "extern ", return_type_str, genname_str, formal_par_list); // function body target->source.function_bodies = mputprintf(target->source.function_bodies, - "%s %s(%s)\n" + "%s %s%s%s%s(%s)\n" "{\n" "%s" - "}\n\n", return_type_str, genname_str, formal_par_list, body); + "}\n\n", + return_type_str, + port_type && clean_up ? port_type->get_genname_own().c_str() : "", + port_type && clean_up && port_type->get_PortBody()->get_testport_type() != PortTypeBody::TP_INTERNAL ? "_BASE" : "", + port_type && clean_up ? "::" : "", + genname_str, formal_par_list, body); Free(formal_par_list); Free(body); diff --git a/compiler2/ttcn3/AST_ttcn3.hh b/compiler2/ttcn3/AST_ttcn3.hh index de7ac9ffa53477fc2b0e5dd69e83e924cb139391..85d246bfd0b1945d4076c7835f9031023ea06cc9 100644 --- a/compiler2/ttcn3/AST_ttcn3.hh +++ b/compiler2/ttcn3/AST_ttcn3.hh @@ -457,6 +457,37 @@ namespace Ttcn { virtual Common::Assignment *get_ass_bySRef(Ref_simple *p_ref); virtual bool has_ass_withId(const Identifier& p_id); }; + + /** + * Class Ttcn::PortScope. + * Implements the scoping rules for functions that + * have a 'port' clause. First looks for the variable definitions in the given + * port type first then it searches in its parent scope. + * Note: This scope unit cannot access the parent scope of the port type. + */ + class PortScope : public Scope { + /** Points to the port type. */ + Type *port_type; + /** Shortcut to the definitions within \a port_type. */ + Definitions *vardefs; + + /** Not implemented. Causes \a FATAL_ERROR. */ + PortScope(const PortScope& p); + /** %Assignment not implemented */ + PortScope& operator=(const PortScope& p); + public: + PortScope(Type *p_porttype); + virtual PortScope *clone() const; + + Type *get_port_type() const { return port_type; } + /** Checks the uniqueness of definitions within \a portdefs and + * reports warnings in case of hiding. */ + void chk_uniq(); + + virtual PortScope *get_scope_port(); + virtual Common::Assignment *get_ass_bySRef(Ref_simple *p_ref); + virtual bool has_ass_withId(const Identifier& p_id); + }; /** * Class Ttcn::Definitions. @@ -599,8 +630,9 @@ namespace Ttcn { Imports *imp; ControlPart* controlpart; /** For caching the scope objects that are created in - * \a get_runs_on_scope(). */ + * \a get_runs_on_scope() and get_port_scope(). */ vector<RunsOnScope> runs_on_scopes; + vector<PortScope> port_scopes; private: /** Copy constructor not implemented */ Module(const Module& p); @@ -638,6 +670,10 @@ namespace Ttcn { * object has been created for a component type it will be returned later * instead of creating a new one. */ RunsOnScope *get_runs_on_scope(Type *comptype); + /* The same as get_runs_on_scope except that the returned scope can access + * the port types definitions. + */ + PortScope *get_port_scope(Type *porttype); virtual void dump(unsigned level) const; void set_language_spec(const char *p_language_spec); void add_ass(Definition* p_ass); @@ -858,7 +894,6 @@ namespace Ttcn { : Common::Assignment(p), genname(), parentgroup(0), w_attrib_path(0), erroneous_attrs(0), local_scope(false) { } - virtual string get_genname() const; namedbool has_implicit_omit_attr() const; private: @@ -879,13 +914,14 @@ namespace Ttcn { /** Marks the (template) definition as local to a func/altstep/default */ inline void set_local() { local_scope = true; } + virtual string get_genname() const; void set_genname(const string& p_genname) { genname = p_genname; } /** Check if two definitions are (almost) identical, the type and dimensions * must always be identical, the initial values can be different depending * on the definition type. If error was reported the return value is false. * The initial values (if applicable) may be present/absent, different or * unfoldable. The function must be overridden to be used. - */ + */ virtual bool chk_identical(Definition *p_def); /** Parse and check the erroneous attribute data, * returns erroneous attributes or NULL */ @@ -1366,6 +1402,14 @@ namespace Ttcn { * It is NULL if the function has no 'runs on' clause or \a runs_on_ref is * erroneous. */ Type *runs_on_type; + /** The 'port' clause (i.e. a reference to a TTCN-3 port type) + * It is NULL if the function has no 'port' clause. */ + Reference *port_ref; + /** Points to the object describing the port type referred by + * 'port' clause. + * It is NULL if the function has no 'port' clause or \a port_ref is + * erroneous. */ + Type *port_type; /** The body of the function */ StatementBlock *block; /** Indicates whether the function is startable. That is, it can be @@ -1389,13 +1433,15 @@ namespace Ttcn { * @param p_id function name * @param p_fpl formal parameter list * @param p_runs_on_ref "runs on", else NULL + * @param p_port_ref "port", else NULL * @param p_return_type return type, may be NULL * @param returns_template true if the return value is a template * @param p_template_restriction restriction type * @param p_block the body of the function */ Def_Function(Identifier *p_id, FormalParList *p_fpl, - Reference *p_runs_on_ref, Type *p_return_type, + Reference *p_runs_on_ref, Reference *p_port_ref, + Type *p_return_type, bool returns_template, template_restriction_t p_template_restriction, StatementBlock *p_block); @@ -1404,9 +1450,13 @@ namespace Ttcn { virtual void set_fullname(const string& p_fullname); virtual void set_my_scope(Scope *p_scope); virtual Type *get_RunsOnType(); + virtual Type *get_PortType(); /** Returns a scope that can access the definitions within component type * \a comptype and its parent is \a parent_scope.*/ RunsOnScope *get_runs_on_scope(Type *comptype); + /** Returns a scope that can access the definitions within port type + * \a porttype and its parent is \a parent_scope.*/ + PortScope *get_port_scope(Type *porttype); virtual void chk(); /** Checks and returns whether the function is startable. * Reports the appropriate error message(s) if not. */ @@ -1414,6 +1464,8 @@ namespace Ttcn { bool is_transparent() const { return transparent; } + // Reuse of clean_up variable to allow or disallow the generation. + // clean_up is true when it is called from PortTypeBody::generate_code() virtual void generate_code(output_struct *target, bool clean_up = false); virtual void generate_code(CodeGenHelper& cgh); virtual void dump_internal(unsigned level) const; diff --git a/compiler2/ttcn3/Statement.cc b/compiler2/ttcn3/Statement.cc index b498f732c6ef89aab491fe51078e95e931c5314d..e831aba13d33f340272f2459f1e6226852a4d796 100644 --- a/compiler2/ttcn3/Statement.cc +++ b/compiler2/ttcn3/Statement.cc @@ -3121,10 +3121,13 @@ error: { Error_Context cntxt(this, "In function instance"); Common::Assignment *t_ass = ref_pard->get_refd_assignment(); + if (t_ass->get_PortType()) { + ref_pard->error("Function with `port' clause cannot be called directly."); + } my_sb->chk_runs_on_clause(t_ass, *ref_pard, "call"); if (t_ass->get_Type()) ref_pard->warning("The value returned by %s is not used", - t_ass->get_description().c_str()); + t_ass->get_description().c_str()); } void Statement::chk_block() diff --git a/compiler2/ttcn3/Ttcnstuff.cc b/compiler2/ttcn3/Ttcnstuff.cc index 2d6676ae41e0f57f798545a75d670fcf9cd2315c..18c882d9c8555b784a97a89ccb4640c5dadf967b 100644 --- a/compiler2/ttcn3/Ttcnstuff.cc +++ b/compiler2/ttcn3/Ttcnstuff.cc @@ -375,7 +375,7 @@ namespace Ttcn { } } - void TypeMappingTarget::chk_function(Type *source_type, bool legacy, bool incoming) + void TypeMappingTarget::chk_function(Type *source_type, Type *port_type, bool legacy, bool incoming) { Error_Context cntxt(this, "In `function' mapping"); Assignment *t_ass = u.func.function_ref->get_refd_assignment(false); @@ -478,6 +478,17 @@ namespace Ttcn { output_type->get_typename().c_str()); } } + + // Check that if the function has a port clause it matches the port type + // that it is defined in. + Type *port_clause = u.func.function_ptr->get_PortType(); + if (!legacy && port_clause && port_clause != port_type) { + u.func.function_ref->error("The function %s has a port clause of `%s'" + " but referenced in another port `%s'", + u.func.function_ptr->get_description().c_str(), + port_type->get_dispname().c_str(), + port_clause->get_dispname().c_str()); + } } } @@ -517,7 +528,7 @@ namespace Ttcn { if (u.encdec.eb_list) u.encdec.eb_list->chk(); } - void TypeMappingTarget::chk(Type *source_type, bool legacy, bool incoming) + void TypeMappingTarget::chk(Type *source_type, Type *port_type, bool legacy, bool incoming) { if (checked) return; checked = true; @@ -532,7 +543,7 @@ namespace Ttcn { case TM_DISCARD: break; case TM_FUNCTION: - chk_function(source_type, legacy, incoming); + chk_function(source_type, port_type, legacy, incoming); break; case TM_ENCODE: chk_encode(source_type); @@ -726,7 +737,7 @@ namespace Ttcn { targets->set_my_scope(p_scope); } - void TypeMapping::chk(bool legacy, bool incoming) + void TypeMapping::chk(Type *port_type, bool legacy, bool incoming) { Error_Context cntxt(this, "In type mapping"); { @@ -737,7 +748,7 @@ namespace Ttcn { bool has_sliding = false, has_non_sliding = false; for (size_t i = 0; i < nof_targets; i++) { TypeMappingTarget *target = targets->get_target_byIndex(i); - target->chk(source_type, legacy, incoming); + target->chk(source_type, port_type, legacy, incoming); if (nof_targets > 1) { switch (target->get_mapping_type()) { case TypeMappingTarget::TM_DISCARD: @@ -862,14 +873,14 @@ namespace Ttcn { return mappings_m[p_type->get_typename()]; } - void TypeMappings::chk(bool legacy, bool incoming) + void TypeMappings::chk(Type *port_type, bool legacy, bool incoming) { if (checked) return; checked = true; size_t nof_mappings = mappings_v.size(); for (size_t i = 0; i < nof_mappings; i++) { TypeMapping *mapping = mappings_v[i]; - mapping->chk(legacy, incoming); + mapping->chk(port_type, legacy, incoming); Type *source_type = mapping->get_source_type(); if (source_type->get_type_refd_last()->get_typetype() != Type::T_ERROR) { const string& source_type_name = source_type->get_typename(); @@ -1023,7 +1034,7 @@ namespace Ttcn { PortTypeBody::PortTypeBody(PortOperationMode_t p_operation_mode, Types *p_in_list, Types *p_out_list, Types *p_inout_list, - bool p_in_all, bool p_out_all, bool p_inout_all) + bool p_in_all, bool p_out_all, bool p_inout_all, Definitions *defs) : Node(), Location(), my_type(0), operation_mode(p_operation_mode), in_list(p_in_list), out_list(p_out_list), inout_list(p_inout_list), in_all(p_in_all), out_all(p_out_all), inout_all(p_inout_all), @@ -1031,7 +1042,7 @@ namespace Ttcn { in_msgs(0), out_msgs(0), in_sigs(0), out_sigs(0), testport_type(TP_REGULAR), port_type(PT_REGULAR), provider_refs(), provider_types(), mapper_types(), - in_mappings(0), out_mappings(0) + in_mappings(0), out_mappings(0), vardefs(defs) { } @@ -1052,6 +1063,7 @@ namespace Ttcn { mapper_types.clear(); delete in_mappings; delete out_mappings; + delete vardefs; } PortTypeBody *PortTypeBody::clone() const @@ -1073,8 +1085,8 @@ namespace Ttcn { provider_refs[i]->set_fullname(p_fullname + ".<provider_ref>"); } if (in_mappings) in_mappings->set_fullname(p_fullname + ".<in_mappings>"); - if (out_mappings) - out_mappings->set_fullname(p_fullname + ".<out_mappings>"); + if (out_mappings) out_mappings->set_fullname(p_fullname + ".<out_mappings>"); + vardefs->set_fullname(p_fullname + ".<port_var>"); } void PortTypeBody::set_my_scope(Scope *p_scope) @@ -1087,6 +1099,7 @@ namespace Ttcn { } if (in_mappings) in_mappings->set_my_scope(p_scope); if (out_mappings) out_mappings->set_my_scope(p_scope); + vardefs->set_parent_scope(p_scope); } void PortTypeBody::set_my_type(Type *p_type) @@ -1119,6 +1132,12 @@ namespace Ttcn { if (!checked) FATAL_ERROR("PortTypeBody::get_out_sigs()"); return out_sigs; } + + Definitions *PortTypeBody::get_vardefs() const + { + if (!checked) FATAL_ERROR("PortTypeBody::get_vardefs()"); + return vardefs; + } bool PortTypeBody::has_queue() const { @@ -1528,7 +1547,7 @@ namespace Ttcn { // checking the incoming mappings if (legacy && in_mappings) { Error_Context cntxt2(in_mappings, "In `in' mappings"); - in_mappings->chk(legacy, true); + in_mappings->chk(my_type, legacy, true); // checking source types if (provider_body) { if (provider_body->in_msgs) { @@ -1587,7 +1606,7 @@ namespace Ttcn { // checking the outgoing mappings if (legacy && out_mappings) { Error_Context cntxt2(out_mappings, "In `out' mappings"); - out_mappings->chk(legacy, false); + out_mappings->chk(my_type, legacy, false); // checking source types if (out_msgs) { // check if all source types are present on the `out' list @@ -1710,12 +1729,14 @@ namespace Ttcn { } if (!legacy) { if (out_mappings) { - out_mappings->chk(legacy, false); + out_mappings->chk(my_type, legacy, false); } if (in_mappings) { - in_mappings->chk(legacy, true); + in_mappings->chk(my_type, legacy, true); } chk_map_translation(); + vardefs->chk_uniq(); + vardefs->chk(); } } @@ -1753,6 +1774,10 @@ namespace Ttcn { Error_Context cntxt(inout_list, "In `inout' list"); chk_list(inout_list, true, true); } + + if (provider_refs.size() == 0 && vardefs->get_nof_asss() > 0) { + error("Port variables can only be used when the port is a translation port."); + } } void PortTypeBody::chk_attributes(Ttcn::WithAttribPath *w_attrib_path) @@ -1810,8 +1835,13 @@ namespace Ttcn { ea.warning("Duplicate attribute `provider'"); break; } case PortTypeBody::PT_USER: { + if (legacy) { ea.error("Attributes `user' and `provider' " "cannot be used at the same time"); + } else { + ea.error("The `provider' attribute " + "cannot be used on translation ports"); + } break; } default: FATAL_ERROR("coding_attrib_parse(): invalid testport type"); @@ -1828,7 +1858,11 @@ namespace Ttcn { "cannot be used at the same time"); break; } case PortTypeBody::PT_USER: { - ea.error("Duplicate attribute `user'"); + if (legacy) { + ea.error("Duplicate attribute `user'"); + } else { + ea.error("Attribute `user' cannot be used on translation ports."); + } break; } default: FATAL_ERROR("coding_attrib_parse(): invalid testport type"); @@ -2318,6 +2352,84 @@ namespace Ttcn { } // for mapping->get_nof_targets() } // for in_mappings->get_nof_mappings() } // if in_mappings // todo inout? + + // Variable declarations and definitions + for (size_t i = 0; i < vardefs->get_nof_asss(); i++) { + Definition* def = static_cast<Definition*>(vardefs->get_ass_byIndex(i)); + string type; + switch (def->get_asstype()) { + case Assignment::A_VAR: + case Assignment::A_CONST: + type = def->get_Type()->get_genname_value(my_scope); + break; + case Assignment::A_VAR_TEMPLATE: + type = def->get_Type()->get_genname_template(my_scope); + break; + default: + FATAL_ERROR("PortTypeBody::generate_code()"); + } + + pdef.var_decls = + mputprintf(pdef.var_decls, + "%s %s;\n", + type.c_str(), + def->get_genname().c_str()); + + size_t len = pdef.var_defs ? strlen(pdef.var_defs) : 0; + pdef.var_defs = def->generate_code_init_comp(pdef.var_defs, def); + + // If the def does not have default value then clean it up to + // restore to unbound. (for constants the generate_code_init_comp does nothing) + if ((pdef.var_defs == NULL || len == strlen(pdef.var_defs)) && def->get_asstype() != Assignment::A_CONST) { + pdef.var_defs = mputprintf(pdef.var_defs, "%s.clean_up();\n", + def->get_genname().c_str()); + } + } + + + // Collect the generated code of functions with 'port' clause + // which belongs to this port type + output_struct os; + Code::init_output(&os); + map<Def_Function_Base*, int> funs; + if (out_mappings) { + for (size_t i = 0; i < out_mappings->get_nof_mappings(); i++) { + TypeMapping* tm = out_mappings->get_mapping_byIndex(i); + for (size_t j = 0; j < tm->get_nof_targets(); j++) { + TypeMappingTarget* tmt = tm->get_target_byIndex(j); + if (tmt->get_mapping_type() == TypeMappingTarget::TM_FUNCTION) { + Def_Function_Base* fun = tmt->get_function(); + Type * fun_port_type = fun->get_PortType(); + if (fun_port_type && fun_port_type == my_type && !funs.has_key(fun)) { + // Reuse of clean_up parameter here. + fun->generate_code(&os, true); + funs.add(fun, 0); + } + } + } + } + } + if (in_mappings) { + for (size_t i = 0; i < in_mappings->get_nof_mappings(); i++) { + TypeMapping* tm = in_mappings->get_mapping_byIndex(i); + for (size_t j = 0; j < tm->get_nof_targets(); j++) { + TypeMappingTarget* tmt = tm->get_target_byIndex(j); + if (tmt->get_mapping_type() == TypeMappingTarget::TM_FUNCTION) { + Def_Function_Base* fun = tmt->get_function(); + Type * fun_port_type = fun->get_PortType(); + if (fun_port_type && fun_port_type == my_type && !funs.has_key(fun)) { + // Reuse of clean_up parameter here. + fun->generate_code(&os, true); + funs.add(fun, 0); + } + } + } + } + } + pdef.mapping_func_decls = mcopystr(os.header.function_prototypes); + pdef.mapping_func_defs = mcopystr(os.source.function_bodies); + funs.clear(); + Code::free_output(&os); } // if legacy } else { // "internal provider" is the same as "internal" @@ -2337,7 +2449,7 @@ namespace Ttcn { pdef.mapper_name[i] = pool.add(mapper_types[i]->get_genname_value(my_scope)); } } - + defPortClass(&pdef, target); if (generate_skeleton && testport_type != TP_INTERNAL && (port_type != PT_USER || !legacy)) generateTestPortSkeleton(&pdef); @@ -2355,6 +2467,10 @@ namespace Ttcn { Free(pdef.provider_msg_outlist.elements[i].out_msg_type_names); Free(pdef.provider_msg_outlist.elements); Free(pdef.mapper_name); + Free(pdef.var_decls); + Free(pdef.var_defs); + Free(pdef.mapping_func_decls); + Free(pdef.mapping_func_defs); } void PortTypeBody::dump(unsigned level) const diff --git a/compiler2/ttcn3/Ttcnstuff.hh b/compiler2/ttcn3/Ttcnstuff.hh index 3b02f2e5f9519f83b48dc3b7d2fb21c4c5ae7420..88ecdaa39fe7eb2f744333ef7406962f52e5e686 100644 --- a/compiler2/ttcn3/Ttcnstuff.hh +++ b/compiler2/ttcn3/Ttcnstuff.hh @@ -180,7 +180,7 @@ private: * syntax in the standard was used. Param incoming: true if the mapping is in the in list of port, false if the * mapping is in the out list of the port.*/ - void chk_function(Common::Type *source_type, bool legacy, bool incoming); + void chk_function(Common::Type *source_type, Common::Type *port_type, bool legacy, bool incoming); /** Check "encode()" mapping. */ void chk_encode(Common::Type *source_type); /** Check "decode()" mapping. */ @@ -190,7 +190,7 @@ public: * syntax in the standard was used. Param incoming: true if the mapping is in the in list of port, false if the * mapping is in the out list of the port.*/ - void chk(Common::Type *source_type, bool legacy, bool incoming); + void chk(Common::Type *source_type, Common::Type *port_type, bool legacy, bool incoming); /** Fills the appropriate data structure \a target for code generation. */ bool fill_type_mapping_target(port_msg_type_mapping_target *target, Common::Type *source_type, Common::Scope *p_scope, stringpool& pool); @@ -249,7 +249,7 @@ public: * syntax in the standard was used. Param incoming: true if the mapping is in the in list of port, false if the * mapping is in the out list of the port.*/ - void chk(bool legacy, bool incoming); + void chk(Common::Type *port_type, bool legacy, bool incoming); virtual void dump(unsigned level) const; }; @@ -278,7 +278,7 @@ public: * syntax in the standard was used. Param incoming: true if the mapping is in the in list of port, false if the * mapping is in the out list of the port.*/ - void chk(bool legacy, bool incoming); + void chk(Common::Type *port_type, bool legacy, bool incoming); virtual void dump(unsigned level) const; }; @@ -404,6 +404,7 @@ private: vector<Common::Type> mapper_types; ///< the types that map this port. ///< only for PT_USER && !legacy TypeMappings *in_mappings, *out_mappings; ///< mappings for PT_USER + Definitions *vardefs; ///< variable definitions inside the port /** Copy constructor not implemented */ PortTypeBody(const PortTypeBody& p); /** Assignment disabled */ @@ -411,7 +412,7 @@ private: public: PortTypeBody(PortOperationMode_t p_operation_mode, Types *p_in_list, Types *p_out_list, Types *p_inout_list, - bool p_in_all, bool p_out_all, bool p_inout_all); + bool p_in_all, bool p_out_all, bool p_inout_all, Definitions *defs); ~PortTypeBody(); virtual PortTypeBody *clone() const; virtual void set_fullname(const string& p_fullname); @@ -422,6 +423,7 @@ public: TypeSet *get_out_msgs() const; TypeSet *get_in_sigs() const; TypeSet *get_out_sigs() const; + Definitions *get_vardefs() const; bool has_queue() const; bool getreply_allowed() const; bool catch_allowed() const; diff --git a/compiler2/ttcn3/compiler.y b/compiler2/ttcn3/compiler.y index 5728f63f66a625a6290c72f7b440921d9d75eab7..a19e4d53aaa22d6bcc2ce579c4cef8077b795852 100644 --- a/compiler2/ttcn3/compiler.y +++ b/compiler2/ttcn3/compiler.y @@ -284,6 +284,8 @@ static const string anyname("anytype"); Ttcn::Types *in_list, *out_list, *inout_list; bool in_all, out_all, inout_all; TypeMappings *in_mappings, *out_mappings; + size_t varnElements; + Ttcn::Definition **varElements; } portdefbody; struct { @@ -1018,7 +1020,7 @@ static const string anyname("anytype"); %type <refpard> FunctionInstance AltstepInstance %type <reference> PortType optDerivedDef DerivedDef IndexSpec Signature VariableRef TimerRef Port PortOrAll ValueStoreSpec - SenderSpec ComponentType optRunsOnSpec RunsOnSpec optSystemSpec + SenderSpec ComponentType optRunsOnSpec RunsOnSpec optSystemSpec optPortSpec %type <reference_or_any> PortOrAny TimerRefOrAny %type <valuerange> Range %type <type> NestedEnumDef NestedRecordDef NestedRecordOfDef NestedSetDef @@ -1049,7 +1051,7 @@ AllOrTypeListWithTo TypeListWithFrom TypeListWithTo %type <def_list> AltstepLocalDef AltstepLocalDefList ComponentElementDef ConstDef ExtConstDef FunctionLocalDef FunctionLocalInst ModuleDef ModulePar ModuleParDef MultiTypedModuleParList PortInstance TimerInstance TimerList - VarInstance + VarInstance PortElementVarDef %type <stmt_list> FunctionStatementOrDef ControlStatementOrDef %type <rangedef> RangeDef @@ -1500,6 +1502,7 @@ FriendModuleDef USI UIDlike PortTypeList +PortElementVarDef %destructor { @@ -1533,6 +1536,10 @@ StructOfDefBody delete $$.inout_list; delete $$.in_mappings; delete $$.out_mappings; + for (size_t i = 0; i < $$.varnElements; i++) { + delete $$.varElements[i]; + } + delete $$.varElements; } PortDefList PortDefLists @@ -1866,12 +1873,12 @@ optDecodedModifier %left '*' '/' ModKeyword RemKeyword %left UnarySign -%expect 65 +%expect 66 %start GrammarRoot /* -XXX Source of conflicts (65 S/R): +XXX Source of conflicts (66 S/R): 1.) 9 conflicts in one state The Expression after 'return' keyword is optional in ReturnStatement. @@ -1942,6 +1949,9 @@ ex.: template octetstring t := 'AB'O & ? length (3); Because the parser shifts, the length restriction here is applied to the '?' instead of the concatenation result, which is the expected behavior. +12.) 1 conflict in PortDefLists +PortElementVarDef contains optSemicolon which causes 1 conflict + Note that the parser implemented by bison always chooses to shift instead of reduce in case of conflicts. */ @@ -2803,9 +2813,14 @@ PortDefBody: // 57 PortDefAttribs: // 60 PortOperationMode PortDefLists { + Definitions * defs = new Definitions(); + for (size_t i = 0; i < $2.varnElements; i++) { + defs->add_ass($2.varElements[i]); + } + Free($2.varElements); PortTypeBody *body = new PortTypeBody($1, $2.in_list, $2.out_list, $2.inout_list, - $2.in_all, $2.out_all, $2.inout_all); + $2.in_all, $2.out_all, $2.inout_all, defs); body->set_location(infile, @$); $$ = new Type(Type::T_PORT, body); $$->set_location(infile, @$); @@ -2815,9 +2830,14 @@ PortDefAttribs: // 60 | PortOperationMode MapKeyword ToKeyword PortTypeList PortDefLists { + Definitions * defs = new Definitions(); + for (size_t i = 0; i < $5.varnElements; i++) { + defs->add_ass($5.varElements[i]); + } + Free($5.varElements); PortTypeBody *body = new PortTypeBody($1, $5.in_list, $5.out_list, $5.inout_list, - $5.in_all, $5.out_all, $5.inout_all); + $5.in_all, $5.out_all, $5.inout_all, defs); body->set_location(infile, @$); $$ = new Type(Type::T_PORT, body); body->add_user_attribute($4.elements, $4.nElements, $5.in_mappings, $5.out_mappings, false); @@ -2845,6 +2865,8 @@ PortDefLists: $$.inout_all = false; $$.in_mappings = 0; $$.out_mappings = 0; + $$.varnElements = 0; + $$.varElements = 0; } ; @@ -2905,6 +2927,16 @@ seqPortDefList: loc.warning("Duplicate directive `inout all' in port type definition"); } else $$.inout_all = true; } + if ($2.varnElements > 0) { + size_t i = $$.varnElements; + $$.varnElements += $2.varnElements; + $$.varElements = static_cast<Ttcn::Definition**>(Realloc($$.varElements, $$.varnElements * sizeof(Ttcn::Definition*))); + // Intentional j start + for (size_t j = 0; i < $$.varnElements; i++, j++) { + $$.varElements[i] = $2.varElements[j]; + } + Free($2.varElements); + } } ; @@ -2930,6 +2962,8 @@ PortDefList: $$.inout_list = 0; $$.out_mappings = 0; $$.inout_all = false; + $$.varnElements = 0; + $$.varElements = 0; } | OutParKeyword AllOrTypeListWithTo { @@ -2952,6 +2986,8 @@ PortDefList: $$.in_mappings = 0; $$.inout_list = 0; $$.inout_all = false; + $$.varnElements = 0; + $$.varElements = 0; } | InOutParKeyword AllOrTypeList { @@ -2970,6 +3006,8 @@ PortDefList: delete $2.mappings; $$.in_mappings = 0; $$.out_mappings = 0; + $$.varnElements = 0; + $$.varElements = 0; } | InParKeyword error { @@ -2981,6 +3019,8 @@ PortDefList: $$.inout_all = false; $$.in_mappings = 0; $$.out_mappings = 0; + $$.varnElements = 0; + $$.varElements = 0; } | OutParKeyword error { @@ -2992,6 +3032,8 @@ PortDefList: $$.inout_all = false; $$.in_mappings = 0; $$.out_mappings = 0; + $$.varnElements = 0; + $$.varElements = 0; } | InOutParKeyword error { @@ -3003,7 +3045,25 @@ PortDefList: $$.inout_all = false; $$.in_mappings = 0; $$.out_mappings = 0; + $$.varnElements = 0; + $$.varElements = 0; } +| PortElementVarDef optSemiColon { + $$.in_list = 0; + $$.out_list = 0; + $$.inout_list = 0; + $$.in_all = false; + $$.out_all = false; + $$.inout_all = false; + $$.in_mappings = 0; + $$.out_mappings = 0; + $$.varnElements = $1.nElements; + $$.varElements = static_cast<Ttcn::Definition**>(Malloc($$.varnElements * sizeof(Ttcn::Definition*))); + for (size_t i = 0; i < $$.varnElements; i++) { + $$.varElements[i] = $1.elements[i]; + } + delete $1.elements; +} ; WithList: @@ -3109,6 +3169,10 @@ TypeListWithTo: | TypeListWithTo optError ',' error { $$ = $1; } ; +PortElementVarDef: + VarInstance { $$ = $1; } +| ConstDef { $$ = $1; } + ComponentDef: // 78 ComponentKeyword IDentifier optExtendsDef @@ -4099,11 +4163,11 @@ ValueofOp: // 162 FunctionDef: // 164 FunctionKeyword optDeterministicModifier IDentifier '(' optFunctionFormalParList ')' - optRunsOnSpec optReturnType optError StatementBlock + optRunsOnSpec optPortSpec optReturnType optError StatementBlock { $5->set_location(infile, @4, @6); - $$ = new Def_Function($3, $5, $7, $8.type, $8.returns_template, - $8.template_restriction, $10); + $$ = new Def_Function($3, $5, $7, $8, $9.type, $9.returns_template, + $9.template_restriction, $11); $$->set_location(infile, @$); } ; @@ -4190,6 +4254,12 @@ RunsOnSpec: // 171 RunsKeyword OnKeyword ComponentType { $$ = $3; } ; +optPortSpec: + /* empty */ { $$ = 0; } +| PortKeyword Port { $$ = $2; } +; + + /* StatementBlock changed in 4.1.2 to explicitly prevent statements * followed by definitions. TITAN still allows them to be mixed. */ StatementBlock: /* StatementBlock *statementblock */ // 175 diff --git a/compiler2/ttcn3/port.c b/compiler2/ttcn3/port.c index c4a876e2956cd049151d1dd25cc85caf10ddfe81..6f9eea304af6191d2aa791df0cd7562ff7dd4c5c 100644 --- a/compiler2/ttcn3/port.c +++ b/compiler2/ttcn3/port.c @@ -1654,6 +1654,26 @@ void defPortClass(const port_def* pdef, output_struct* output) def = mputprintf(def, "%s* p_%i;\n", pdef->provider_msg_outlist.elements[i].name, (int)i); } def = mputstr(def, "translation_port_state port_state;\n"); + + // Declarations of port variables + if (pdef->var_decls != NULL) { + def = mputstr(def, pdef->var_decls); + } + + if (pdef->var_defs != NULL) { + def = mputstr(def, "void init_port_variables();\n"); + src = mputprintf(src, + "void %s::init_port_variables() {\n%s}\n\n", class_name, pdef->var_defs); + } + + // Declarations of mapping function which have this port in the 'port' clause + if (pdef->mapping_func_decls != NULL) { + def = mputstr(def, pdef->mapping_func_decls); + } + + if (pdef->mapping_func_defs != NULL) { + src = mputstr(src, pdef->mapping_func_defs); + } } // Port type variables in the provider types. @@ -2073,19 +2093,19 @@ void defPortClass(const port_def* pdef, output_struct* output) // add_port and remove_port is called after the map and unmap statements. for (i = 0; i < pdef->provider_msg_outlist.nElements; i++) { def = mputprintf(def, "void add_port(%s* p);\n", pdef->provider_msg_outlist.elements[i].name); - src = mputprintf(src, "void %s::add_port(%s*p) {\n p_%i = p;\n}\n\n", class_name, pdef->provider_msg_outlist.elements[i].name, (int)i); + src = mputprintf(src, "void %s::add_port(%s*p) {\np_%i = p;\n}\n\n", class_name, pdef->provider_msg_outlist.elements[i].name, (int)i); def = mputprintf(def, "void remove_port(%s*);\n", pdef->provider_msg_outlist.elements[i].name); - src = mputprintf(src, "void %s::remove_port(%s*) {\n p_%i = NULL;\n}\n\n", class_name, pdef->provider_msg_outlist.elements[i].name, (int)i); + src = mputprintf(src, "void %s::remove_port(%s*) {\np_%i = NULL;\n}\n\n", class_name, pdef->provider_msg_outlist.elements[i].name, (int)i); } // in_translation_mode returns true if one of the port type variables are not null def = mputstr(def, "boolean in_translation_mode() const;\n"); src = mputprintf(src, "boolean %s::in_translation_mode() const {\nreturn ", class_name); for (i = 0; i < pdef->provider_msg_outlist.nElements; i++) { - src = mputprintf(src, "p_%i != NULL %s", + src = mputprintf(src, "p_%i != NULL%s", (int)i, - i != pdef->provider_msg_outlist.nElements - 1 ? "|| " : ""); + i != pdef->provider_msg_outlist.nElements - 1 ? " || " : ""); } src = mputstr(src, ";\n}\n\n"); @@ -2103,6 +2123,7 @@ void defPortClass(const port_def* pdef, output_struct* output) src = mputprintf(src, "p_%i = NULL;\n", (int)i); } src = mputstr(src, "}\n\n"); + } // Port type variables in the provider types. if (pdef->n_mapper_name > 0) { diff --git a/compiler2/ttcn3/port.h b/compiler2/ttcn3/port.h index 9258329904dec42440252edb8e6ecaadaabeacd1..795f783e187d5db647d6363618967a008976c707 100644 --- a/compiler2/ttcn3/port.h +++ b/compiler2/ttcn3/port.h @@ -120,6 +120,10 @@ typedef struct port_def_tag { port_msg_mapped_type_list provider_msg_in; boolean has_sliding; boolean legacy; + char *var_decls; + char *var_defs; + char *mapping_func_decls; + char *mapping_func_defs; } port_def; #ifdef __cplusplus diff --git a/core/Port.cc b/core/Port.cc index 7fdfc1de480e6d90698ad08fd59b47a0413c234e..3ec64918f80e644af9e7754cef8ccc876a4b74ba 100644 --- a/core/Port.cc +++ b/core/Port.cc @@ -2149,6 +2149,12 @@ void PORT::map(const char *system_port) TTCN_Logger::log_port_misc( TitanLoggerApi::Port__Misc_reason::port__was__mapped__to__system, port_name, SYSTEM_COMPREF, system_port); + + // Only has effect when the translation port has port variables with + // default values. Only call when it is mapped first. + if (n_system_mappings == 0) { + init_port_variables(); + } // the mapping shall be registered in the table only if user_map() was // successful @@ -2202,7 +2208,9 @@ void PORT::unmap(const char *system_port) // Currently the requirement is that the port needs to map only when mapped // to one port. If it would be mapped to more ports then this call would // remove all translation capability. - reset_port_variables(); + if (n_system_mappings == 0) { + reset_port_variables(); + } TTCN_Logger::log_port_misc( TitanLoggerApi::Port__Misc_reason::port__was__unmapped__from__system, @@ -2215,6 +2223,10 @@ void PORT::reset_port_variables() { } +void PORT::init_port_variables() { + +} + void PORT::change_port_state(translation_port_state /*state*/) { } diff --git a/core/Port.hh b/core/Port.hh index f75927a288babf0373018a30bf4fe279735471b8..e89b72fcec916d07520fd0e5314ef160bcd2f7bf 100644 --- a/core/Port.hh +++ b/core/Port.hh @@ -302,8 +302,12 @@ protected: virtual boolean process_exception(const char *signature_name, Text_Buf& incoming_buf, component sender_component); + // Resets the port type variables to NULL after unmap virtual void reset_port_variables(); + // Initializes the port variables after map + virtual void init_port_variables(); + private: port_connection *add_connection(component remote_component, const char *remote_port, transport_type_enum transport_type); diff --git a/function_test/Semantic_Analyser/port_translation/PortClause_SE.ttcn b/function_test/Semantic_Analyser/port_translation/PortClause_SE.ttcn new file mode 100644 index 0000000000000000000000000000000000000000..b505ead3aec4f62a166bf4c37264218b155cf7ba --- /dev/null +++ b/function_test/Semantic_Analyser/port_translation/PortClause_SE.ttcn @@ -0,0 +1,62 @@ +/****************************************************************************** + * Copyright (c) 2000-2017 Ericsson Telecom AB + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Szabo, Bence Janos + * + ******************************************************************************/ + +// Tests of standard ES 202 781 Ports with translation capability + +module PortClause_SE { //^In TTCN-3 module// + + type component MyComp { + + } + + type port P1 message { //^In type definition \`P1\'\:// //^error\: Port variables can only be used when the port is a translation port\.// + in integer + var integer i := 6; + } with { + extension "provider" + } + + type port P2 message map to P1 { //^In type definition \`P2\'\:// //^In type mapping\:// //^In \`function\' mapping\:// //^In translation capability\:// //^error\: The function function \`\@PortClause_SE\.int_to_int\' has a port clause of \`P2\' but referenced in another port \`P3\'// + in integer from integer with int_to_int(); + var integer i := 6; //^Previous definition with identifier \`i\' in higher scope unit is here// + } + + type port P3 message { + in integer + } + + function f() port MyPort { //^In function definition \`f\'\:// //^In \`port\' clause\:// //^error\: There is no local or imported definition with name \`MyPort\'// + + } + + function g() port MyComp { //^In function definition \`g\'\:// //^In \`port\' clause\:// //^error\: Reference \`MyComp\' does not refer to a port type\.// + + } + + function h() runs on MyComp port P1 { //^In function definition \`h\'\:// //^error\: A \`runs on\' and a \`port\' clause cannot be present at the same time\.// + + } + + function j() port P2 { //^In function definition \`j\'\:// + var integer i := 4; //^error\: Definition with identifier \`i\' is not unique in the scope hierarchy// + } + + function int_to_int(in integer input, out integer output) port P3 { + + } with { + extension "prototype(fast)" + } + + control { //^In control part\:// + j(); //^In function instance\:// //^error\: Function with \`port\' clause cannot be called directly\.// + } +} \ No newline at end of file diff --git a/function_test/Semantic_Analyser/port_translation/PortTranslate_SE.ttcn b/function_test/Semantic_Analyser/port_translation/PortTranslate_SE.ttcn index 949d45a4f1343595ab41d138c8f8f28576c6e223..bdfe868160ab24cc54f566ed5a859a881acc7047 100644 --- a/function_test/Semantic_Analyser/port_translation/PortTranslate_SE.ttcn +++ b/function_test/Semantic_Analyser/port_translation/PortTranslate_SE.ttcn @@ -9,6 +9,9 @@ * Szabo, Bence Janos * ******************************************************************************/ + +// Tests of standard ES 202 781 Ports with translation capability + module PortTranslate_SE { //^In TTCN-3 module// type integer MyInt; @@ -194,4 +197,16 @@ module PortTranslate_SE { //^In TTCN-3 module// with { extension "prototype(fast)" } + + type port PT16 message map to P9 { //^In type definition \`PT16\'\:// + out hexstring + } with { + extension "provider" //^error\: The \`provider\' attribute cannot be used on translation ports// + } + + type port PT17 message map to P9 { //^In type definition \`PT17\'\:// + out hexstring + } with { + extension "user P9 out(hexstring -> hexstring: simple)"; //^error\: Attribute \`user\' cannot be used on translation ports\.// + } } \ No newline at end of file diff --git a/function_test/Semantic_Analyser/port_translation/Setstate_SE.ttcn b/function_test/Semantic_Analyser/port_translation/Setstate_SE.ttcn index ca283746ee34b13468da0c1cf481614aa563f280..7c1d38cd3d99f17151e71db94c927e1da60b838f 100644 --- a/function_test/Semantic_Analyser/port_translation/Setstate_SE.ttcn +++ b/function_test/Semantic_Analyser/port_translation/Setstate_SE.ttcn @@ -9,6 +9,9 @@ * Szabo, Bence Janos * ******************************************************************************/ + +// Tests of standard ES 202 781 Ports with translation capability + module Setstate_SE { //^In TTCN-3 module// const integer i1 := 4; diff --git a/function_test/Semantic_Analyser/xer/untagged_charenc_optional_SE.ttcn b/function_test/Semantic_Analyser/xer/untagged_charenc_optional_SE.ttcn index a28a1a98a06c34ed16020cc1f3e68ab8f513f44a..372dace8af7d59a3f905202425db21d6b6baa5ed 100644 --- a/function_test/Semantic_Analyser/xer/untagged_charenc_optional_SE.ttcn +++ b/function_test/Semantic_Analyser/xer/untagged_charenc_optional_SE.ttcn @@ -39,7 +39,6 @@ type union uni { variant "untagged"; } -// No top level untagged warning for unions and anytype external function enc_uni(in uni u) return octetstring with { extension "prototype(convert) encode(XER:XER_EXTENDED)" } diff --git a/regression_test/portTranslation/Makefile b/regression_test/portTranslation/Makefile index 108ffb482c566d1bcc76ef3097a497c3b0160563..315962998592ab1b80bff062c8b2500b0d64567a 100644 --- a/regression_test/portTranslation/Makefile +++ b/regression_test/portTranslation/Makefile @@ -47,14 +47,14 @@ TTCN3_LIB = ttcn3$(RT2_SUFFIX)-parallel$(DYNAMIC_SUFFIX) # TTCN-3 modules of this project: -TTCN3_MODULES = PortTranslation.ttcn Setstate_neg.ttcn +TTCN3_MODULES = PortTranslation.ttcn Setstate_neg.ttcn PortVariables.ttcn # ASN.1 modules of this project: ASN1_MODULES = # C++ source & header files generated from the TTCN-3 & ASN.1 modules of # this project: -GENERATED_SOURCES = $(TTCN3_MODULES:.ttcn=.cc) $(ASN1_MODULES:.asn=.cc) NP1.cc NPT2.cc +GENERATED_SOURCES = $(TTCN3_MODULES:.ttcn=.cc) $(ASN1_MODULES:.asn=.cc) NP1.cc NPT2.cc NVP1.cc GENERATED_HEADERS = $(GENERATED_SOURCES:.cc=.hh) ifdef SPLIT_TO_SLICES POSTFIXES := $(foreach file, $(SPLIT_TO_SLICES), $(addsuffix $(file), _part_)) @@ -64,7 +64,7 @@ GENERATED_SOURCES += $(GENERATED_SOURCES2) endif # C/C++ Source & header files of Test Ports, external functions and # other modules: -USER_SOURCES = PT2.cc P1.cc P2.cc P3.cc +USER_SOURCES = PT2.cc P1.cc P2.cc P3.cc VP1.cc NVP1.cc USER_HEADERS = $(USER_SOURCES:.cc=.hh) # Object files of this project that are needed for the executable test suite: diff --git a/regression_test/portTranslation/PortTranslation.ttcn b/regression_test/portTranslation/PortTranslation.ttcn index a628adc6bbe20752e1485aa0e73676438617ac74..d57dde601f8674d6e1c2e489e3e26e5996e75a7a 100644 --- a/regression_test/portTranslation/PortTranslation.ttcn +++ b/regression_test/portTranslation/PortTranslation.ttcn @@ -16,9 +16,9 @@ module PortTranslation { function MyRec_to_oct(in MyRec i, out octetstring j) { if (i.types == Oct) { j := i.val; - port.setstate(0, "Some message"); // translated + port.setstate(0, "Translated"); // translated } else { - port.setstate(1); // not translated + port.setstate(1, "Not translated"); // not translated } } with { extension "prototype(fast)"; diff --git a/regression_test/portTranslation/PortVariables.ttcn b/regression_test/portTranslation/PortVariables.ttcn new file mode 100644 index 0000000000000000000000000000000000000000..780d7231021a48259fc970921a356aa0ed05dadf --- /dev/null +++ b/regression_test/portTranslation/PortVariables.ttcn @@ -0,0 +1,303 @@ +/****************************************************************************** + * Copyright (c) 2000-2017 Ericsson Telecom AB + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Szabo, Bence Janos + * + ******************************************************************************/ +module PortVariables { + + type record of integer RoI; + + type port VP1 message { + out integer, charstring, RoI; + in integer, charstring, RoI; + } with { + extension "provider" + } + + type port NVP1 message map to VP1 { + out charstring to integer with char_to_default_int_template() : charstring with char_to_default_char() : charstring with char_to_default_const_char() : RoI with char_to_default_roi() : integer with char_to_int(); + in integer, charstring, RoI; + + const charstring c_cs := "DefaultConst" + var charstring cs := "Default"; + var template integer i := 3; + var template RoI roi := {1,2,3}; + var integer num; + } + + function char_to_default_int_template(in charstring input, out integer output) port NVP1 { + if (input == "int template") { + output := valueof(i); + port.setstate(0); + i := 666; + } else { + port.setstate(1); + } + } with { + extension "prototype(fast)"; + } + + function char_to_default_char(in charstring input, out charstring output) port NVP1 { + if (input == "charstring") { + output := cs; + port.setstate(0); + cs := "Modified"; + } else { + port.setstate(1); + } + } with { + extension "prototype(fast)"; + } + + function char_to_default_const_char(in charstring input, out charstring output) port NVP1 { + if (input == "const charstring") { + output := c_cs; + port.setstate(0); + } else { + port.setstate(1); + } + } with { + extension "prototype(fast)"; + } + + function char_to_default_roi(in charstring input, out RoI output) port NVP1 { + if (input == "RoI") { + output := valueof(roi); + port.setstate(0); + roi := {6,6,6}; + } else { + port.setstate(1); + } + } with { + extension "prototype(fast)"; + } + + function char_to_int(in charstring input, out integer output) port NVP1 { + if (input == "Unbound") { + if (isbound(num)) { + output := num; + } else { + output := 0; + } + port.setstate(0); + num := 666; + } else { + port.setstate(1); + } + } with { + extension "prototype(fast)"; + } + + + + + + type component MyComp { + port NVP1 p[2]; + } + + type component System { + port VP1 p1; + port VP1 p2; + } + + + testcase tc_variable_change_test(boolean test_changed) runs on MyComp system System { + map(self:p[0], system:p1); + + // Test that the default values of port variables are set + // and change them inside the translation functions + p[0].send("int template"); + timer t := 0.5; + t.start; + alt { + [] p[0].receive(3) { setverdict(pass); } + [] t.timeout { setverdict(fail); } + } + t.stop; + + p[0].send("charstring"); + t.start; + alt { + [] p[0].receive("Default") { setverdict(pass); } + [] t.timeout { setverdict(fail); } + } + t.stop; + + p[0].send("const charstring"); + t.start; + alt { + [] p[0].receive("DefaultConst") { setverdict(pass); } + [] t.timeout { setverdict(fail); } + } + t.stop; + + var RoI roi := {1,2,3}; + p[0].send("RoI"); + t.start; + alt { + [] p[0].receive(roi) { setverdict(pass); } + [] t.timeout { setverdict(fail); } + } + t.stop; + + p[0].send("Unbound"); + t.start; + alt { + // 0 means that num is unbound + [] p[0].receive(0) { setverdict(pass); } + [] t.timeout { setverdict(fail); } + } + t.stop; + + // Test that the variables are changed + if (test_changed) { + p[0].send("int template"); + t.start; + alt { + [] p[0].receive(666) { setverdict(pass); } + [] t.timeout { setverdict(fail); } + } + t.stop; + + p[0].send("charstring"); + t.start; + alt { + [] p[0].receive("Modified") { setverdict(pass); } + [] t.timeout { setverdict(fail); } + } + t.stop; + + // The const did not change + p[0].send("const charstring"); + t.start; + alt { + [] p[0].receive("DefaultConst") { setverdict(pass); } + [] t.timeout { setverdict(fail); } + } + t.stop; + + roi := {6,6,6}; + p[0].send("RoI"); + t.start; + alt { + [] p[0].receive(roi) { setverdict(pass); } + [] t.timeout { setverdict(fail); } + } + t.stop; + + p[0].send("Unbound"); + t.start; + alt { + [] p[0].receive(666) { setverdict(pass); } + [] t.timeout { setverdict(fail); } + } + t.stop; + } + + // p[0] and p[1] should have different port variable instances + // Theese tests would fail if they would share the variables + map(self:p[1], system:p2); + + // Test that the default values of port variables are set + // and change them inside the translation functions + p[1].send("int template"); + t.start; + alt { + [] p[1].receive(3) { setverdict(pass); } + [] t.timeout { setverdict(fail); } + } + t.stop; + + p[1].send("charstring"); + t.start; + alt { + [] p[1].receive("Default") { setverdict(pass); } + [] t.timeout { setverdict(fail); } + } + t.stop; + + p[1].send("const charstring"); + t.start; + alt { + [] p[1].receive("DefaultConst") { setverdict(pass); } + [] t.timeout { setverdict(fail); } + } + t.stop; + + roi := {1,2,3}; + p[1].send("RoI"); + t.start; + alt { + [] p[1].receive(roi) { setverdict(pass); } + [] t.timeout { setverdict(fail); } + } + t.stop; + + p[1].send("Unbound"); + t.start; + alt { + [] p[1].receive(0) { setverdict(pass); } + [] t.timeout { setverdict(fail); } + } + t.stop; + + // Test that the variables are changed + if (test_changed) { + p[1].send("int template"); + t.start; + alt { + [] p[1].receive(666) { setverdict(pass); } + [] t.timeout { setverdict(fail); } + } + t.stop; + + p[1].send("charstring"); + t.start; + alt { + [] p[1].receive("Modified") { setverdict(pass); } + [] t.timeout { setverdict(fail); } + } + t.stop; + + // The const did not change + p[1].send("const charstring"); + t.start; + alt { + [] p[1].receive("DefaultConst") { setverdict(pass); } + [] t.timeout { setverdict(fail); } + } + t.stop; + + roi := {6,6,6}; + p[1].send("RoI"); + t.start; + alt { + [] p[1].receive(roi) { setverdict(pass); } + [] t.timeout { setverdict(fail); } + } + t.stop; + + p[1].send("Unbound"); + t.start; + alt { + [] p[1].receive(666) { setverdict(pass); } + [] t.timeout { setverdict(fail); } + } + t.stop; + } + + } + + control { + execute(tc_variable_change_test(true)); + // Test that the variables are restored to the default values + execute(tc_variable_change_test(false)); + } +} \ No newline at end of file diff --git a/regression_test/portTranslation/VP1.cc b/regression_test/portTranslation/VP1.cc new file mode 100644 index 0000000000000000000000000000000000000000..7c6050dd698530d48956dd1af4b5ea9bafd8330f --- /dev/null +++ b/regression_test/portTranslation/VP1.cc @@ -0,0 +1,91 @@ +/****************************************************************************** + * Copyright (c) 2000-2017 Ericsson Telecom AB + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Szabo, Bence Janos + * + ******************************************************************************/ + +#include "VP1.hh" +#include "PortVariables.hh" + +namespace PortVariables { + +VP1_PROVIDER::VP1_PROVIDER(const char *par_port_name) + : PORT(par_port_name) +{ + +} + +VP1_PROVIDER::~VP1_PROVIDER() +{ + +} + +void VP1_PROVIDER::set_parameter(const char * /*parameter_name*/, + const char * /*parameter_value*/) +{ + +} + +/*void VP1_PROVIDER::Handle_Fd_Event(int fd, boolean is_readable, + boolean is_writable, boolean is_error) {}*/ + +void VP1_PROVIDER::Handle_Fd_Event_Error(int /*fd*/) +{ + +} + +void VP1_PROVIDER::Handle_Fd_Event_Writable(int /*fd*/) +{ + +} + +void VP1_PROVIDER::Handle_Fd_Event_Readable(int /*fd*/) +{ + +} + +/*void VP1_PROVIDER::Handle_Timeout(double time_since_last_call) {}*/ + +void VP1_PROVIDER::user_map(const char * /*system_port*/) +{ + +} + +void VP1_PROVIDER::user_unmap(const char * /*system_port*/) +{ + +} + +void VP1_PROVIDER::user_start() +{ + +} + +void VP1_PROVIDER::user_stop() +{ + +} + +void VP1_PROVIDER::outgoing_send(const INTEGER& send_par) +{ + incoming_message(send_par); +} + +void VP1_PROVIDER::outgoing_send(const CHARSTRING& send_par) +{ + incoming_message(send_par); +} + +void VP1_PROVIDER::outgoing_send(const RoI& send_par) +{ + incoming_message(send_par); +} + +} /* end of namespace */ + diff --git a/regression_test/portTranslation/VP1.hh b/regression_test/portTranslation/VP1.hh new file mode 100644 index 0000000000000000000000000000000000000000..fbafa34ad9e9c4da77b853d7c1e30173ae60bdc3 --- /dev/null +++ b/regression_test/portTranslation/VP1.hh @@ -0,0 +1,59 @@ +/****************************************************************************** + * Copyright (c) 2000-2017 Ericsson Telecom AB + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Szabo, Bence Janos + * + ******************************************************************************/ + +#ifndef VP1_HH +#define VP1_HH + +#include <TTCN3.hh> + +// Note: Header file PortVariables.hh must not be included into this file! +// (because it includes this file) +// Please add the declarations of message types manually. + +namespace PortVariables { + +typedef PreGenRecordOf::PREGEN__RECORD__OF__INTEGER RoI; + +class VP1_PROVIDER : public PORT { +public: + VP1_PROVIDER(const char *par_port_name); + ~VP1_PROVIDER(); + + void set_parameter(const char *parameter_name, + const char *parameter_value); + +private: + /* void Handle_Fd_Event(int fd, boolean is_readable, + boolean is_writable, boolean is_error); */ + void Handle_Fd_Event_Error(int fd); + void Handle_Fd_Event_Writable(int fd); + void Handle_Fd_Event_Readable(int fd); + /* void Handle_Timeout(double time_since_last_call); */ +protected: + void user_map(const char *system_port); + void user_unmap(const char *system_port); + + void user_start(); + void user_stop(); + +public: + void outgoing_send(const INTEGER& send_par); + void outgoing_send(const CHARSTRING& send_par); + void outgoing_send(const RoI& send_par); + virtual void incoming_message(const INTEGER& incoming_par) = 0; + virtual void incoming_message(const CHARSTRING& incoming_par) = 0; + virtual void incoming_message(const RoI& incoming_par) = 0; +}; + +} /* end of namespace */ + +#endif diff --git a/regression_test/portTranslation/config.cfg b/regression_test/portTranslation/config.cfg index c4e964a2b01d626b6235f99583c6e01a3ec14475..367d96b9f8eb68dc1587d091b8d7e4c0d8a0f638 100644 --- a/regression_test/portTranslation/config.cfg +++ b/regression_test/portTranslation/config.cfg @@ -12,3 +12,4 @@ [EXECUTE] PortTranslation.control Setstate_neg.control +PortVariables.control \ No newline at end of file