#include "fe_utils.h" #include "fe_private.h" #include "fe_extern.h" #include "ast_uses.h" #include "ast_component.h" #include "ast_generator.h" #include "ast_root.h" #include "ast_structure.h" #include "ast_valuetype.h" #include "ast_sequence.h" #include "ast_template_module.h" #include "global_extern.h" #include "utl_scoped_name.h" #include "utl_strlist.h" #include "utl_namelist.h" #include "utl_identifier.h" #include "utl_string.h" #include "ace/OS_NS_strings.h" #include "ace/OS_NS_ctype.h" #include "ace/OS_NS_stdio.h" #include "ace/OS_NS_unistd.h" FE_Utils::T_Param_Info::T_Param_Info (void) : type_ (AST_Decl::NT_fixed), const_type_ (AST_Expression::EV_none), enum_const_type_decl_ (0) { } bool FE_Utils::duplicate_param_id (T_PARAMLIST_INFO *params) { size_t cur_pos = 0UL; size_t size = params->size (); for (FE_Utils::T_PARAMLIST_INFO::CONST_ITERATOR i (*params); !i.done (); i.advance (), ++cur_pos) { FE_Utils::T_Param_Info *this_one = 0; FE_Utils::T_Param_Info *that_one = 0; i.next (this_one); for (size_t j = cur_pos + 1; j < size; ++j) { params->get (that_one, j); if (this_one->name_ == that_one->name_) { return true; } } } return false; } void FE_Utils::T_ARGLIST::destroy (void) { AST_Decl **d = 0; for (T_ARGLIST::ITERATOR i (this->begin ()); !i.done (); i.advance ()) { i.next (d); /// This is a problem - nodes that aren't NT_const have /// already been destroyed. Must find some other way of /// ferreting out the NT_const arglist nodes. /* // These were created on the fly and not part of any scope // so we manage their lifetime here. AST_Decl *tmp = *d; if (tmp->node_type () == AST_Decl::NT_const) { tmp->destroy (); delete tmp; tmp = 0; } */ } } /* * Convert a PredefinedType to an ExprType */ AST_Expression::ExprType FE_Utils::PredefinedTypeToExprType ( AST_PredefinedType::PredefinedType pt) { switch (pt) { case AST_PredefinedType::PT_long: return AST_Expression::EV_long; case AST_PredefinedType::PT_ulong: return AST_Expression::EV_ulong; case AST_PredefinedType::PT_short: return AST_Expression::EV_short; case AST_PredefinedType::PT_ushort: return AST_Expression::EV_ushort; case AST_PredefinedType::PT_longlong: return AST_Expression::EV_longlong; case AST_PredefinedType::PT_ulonglong: return AST_Expression::EV_ulonglong; case AST_PredefinedType::PT_float: return AST_Expression::EV_float; case AST_PredefinedType::PT_double: return AST_Expression::EV_double; case AST_PredefinedType::PT_longdouble: return AST_Expression::EV_longdouble; case AST_PredefinedType::PT_char: return AST_Expression::EV_char; case AST_PredefinedType::PT_wchar: return AST_Expression::EV_wchar; case AST_PredefinedType::PT_octet: return AST_Expression::EV_octet; case AST_PredefinedType::PT_boolean: return AST_Expression::EV_bool; case AST_PredefinedType::PT_void: return AST_Expression::EV_void; default: return AST_Expression::EV_enum; } } AST_PredefinedType::PredefinedType FE_Utils::ExprTypeToPredefinedType (AST_Expression::ExprType et) { switch (et) { case AST_Expression::EV_short: return AST_PredefinedType::PT_short; case AST_Expression::EV_ushort: return AST_PredefinedType::PT_ushort; case AST_Expression::EV_long: return AST_PredefinedType::PT_long; case AST_Expression::EV_ulong: return AST_PredefinedType::PT_ulong; case AST_Expression::EV_longlong: return AST_PredefinedType::PT_longlong; case AST_Expression::EV_ulonglong: return AST_PredefinedType::PT_ulonglong; case AST_Expression::EV_float: return AST_PredefinedType::PT_float; case AST_Expression::EV_double: return AST_PredefinedType::PT_double; case AST_Expression::EV_longdouble: return AST_PredefinedType::PT_longdouble; case AST_Expression::EV_char: return AST_PredefinedType::PT_char; case AST_Expression::EV_wchar: return AST_PredefinedType::PT_wchar; case AST_Expression::EV_octet: return AST_PredefinedType::PT_octet; case AST_Expression::EV_bool: return AST_PredefinedType::PT_boolean; case AST_Expression::EV_any: return AST_PredefinedType::PT_any; case AST_Expression::EV_object: return AST_PredefinedType::PT_object; case AST_Expression::EV_void: return AST_PredefinedType::PT_void; case AST_Expression::EV_enum: case AST_Expression::EV_string: case AST_Expression::EV_wstring: case AST_Expression::EV_none: default: ACE_ERROR ((LM_ERROR, ACE_TEXT ("FE_Utils::ExprTypeToPredefinedType - ") ACE_TEXT ("no pathing PredefinedType enum value\n"))); /// A deliberately odd return value, so the caller can check /// it and take further action if necessary. return AST_PredefinedType::PT_pseudo; } } UTL_ScopedName * FE_Utils::string_to_scoped_name (const char *s) { UTL_ScopedName *retval = 0; ACE_CString str (s); Identifier *id = 0; UTL_ScopedName *sn = 0; while (! str.empty ()) { // Skip a leading double colon. if (str.find (':') == 0) { str = str.substr (2); } // Find the next double colon (if any) and get the next // name segment. ACE_CString::size_type pos = str.find (':'); ACE_CString lname (str.substr (0, pos)); // Construct a UTL_ScopedName segment. ACE_NEW_RETURN (id, Identifier (lname.c_str ()), 0); ACE_NEW_RETURN (sn, UTL_ScopedName (id, 0), 0); // Either make it the head of a new list or the tail of // an existing one. if (retval == 0) { retval = sn; } else { retval->nconc (sn); } // Update the working string. str = str.substr (pos); } return retval; } const char * FE_Utils::stripped_preproc_include (const char *name) { // Some preprocessors prepend "./" to filenames in the // working directory, some others prepend ".\". If either // of these are here, we want to strip them. if (name[0] == '.') { if (name[1] == '\\' || name[1] == '/') { return name + 2; } } return name; } void FE_Utils::create_uses_multiple_stuff (AST_Component *c, AST_Uses *u, const char *prefix) { ACE_CString struct_name (prefix); if (!struct_name.empty ()) { struct_name += '_'; } struct_name += u->local_name ()->get_string (); struct_name += "Connection"; Identifier struct_id (struct_name.c_str ()); UTL_ScopedName sn (&struct_id, 0); // In case this call comes from the backend. We // will pop the scope before returning. idl_global->scopes ().push (c); AST_Structure *connection = idl_global->gen ()->create_structure (&sn, 0, 0); struct_id.destroy (); /// If the field type is a param holder, we want /// to use the lookup to create a fresh one, /// since the field will own it and destroy it. UTL_ScopedName *fn = u->uses_type ()->name (); AST_Decl *d = idl_global->root ()->lookup_by_name (fn, true, false); AST_Type *ft = dynamic_cast (d); Identifier object_id ("objref"); UTL_ScopedName object_name (&object_id, 0); AST_Field *object_field = idl_global->gen ()->create_field (ft, &object_name, AST_Field::vis_NA); (void) DeclAsScope (connection)->fe_add_field (object_field); object_id.destroy (); Identifier local_id ("Cookie"); UTL_ScopedName local_name (&local_id, 0); Identifier module_id ("Components"); UTL_ScopedName scoped_name (&module_id, &local_name); d = c->lookup_by_name (&scoped_name, true); local_id.destroy (); module_id.destroy (); if (d == 0) { // This would happen if we haven't included Components.idl. idl_global->err ()->lookup_error (&scoped_name); return; } AST_ValueType *cookie = dynamic_cast (d); Identifier cookie_id ("ck"); UTL_ScopedName cookie_name (&cookie_id, 0); AST_Field *cookie_field = idl_global->gen ()->create_field (cookie, &cookie_name, AST_Field::vis_NA); (void) DeclAsScope (connection)->fe_add_field (cookie_field); cookie_id.destroy (); (void) c->fe_add_structure (connection); ACE_CDR::ULong bound = 0; AST_Expression *bound_expr = idl_global->gen ()->create_expr (bound, AST_Expression::EV_ulong); AST_Sequence *sequence = idl_global->gen ()->create_sequence (bound_expr, connection, 0, 0, 0); ACE_CString seq_string (struct_name); seq_string += 's'; Identifier seq_id (seq_string.c_str ()); UTL_ScopedName seq_name (&seq_id, 0); AST_Typedef *connections = idl_global->gen ()->create_typedef (sequence, &seq_name, 0, 0); seq_id.destroy (); (void) c->fe_add_typedef (connections); // In case this call comes from the backend. idl_global->scopes ().pop (); } void FE_Utils::create_implied_ami_uses_stuff (void) { if (idl_global->included_ami_receps_done ()) { return; } for (ACE_Unbounded_Queue::CONST_ITERATOR i ( idl_global->included_ami_recep_names ()); ! i.done (); i.advance ()) { char **item = 0; i.next (item); UTL_ScopedName *sn = FE_Utils::string_to_scoped_name (*item); AST_Decl *d = idl_global->root ()->lookup_by_name (sn, true); if (d == 0) { idl_global->err ()->lookup_error (sn); sn->destroy (); delete sn; sn = 0; continue; } sn->destroy (); delete sn; sn = 0; AST_Uses *u = dynamic_cast (d); if (u == 0) { ACE_ERROR ((LM_ERROR, ACE_TEXT ("idl_global::create_") ACE_TEXT ("implied_ami_uses_stuff - ") ACE_TEXT ("narrow to receptacle ") ACE_TEXT ("failed\n"))); continue; } if (!u->is_multiple ()) { continue; } AST_Component *c = dynamic_cast (u->defined_in ()); if (c == 0) { ACE_ERROR ((LM_ERROR, ACE_TEXT ("idl_global::create_") ACE_TEXT ("implied_ami_uses_stuff - ") ACE_TEXT ("receptacle not defined") ACE_TEXT ("in a component\n"))); continue; } FE_Utils::create_uses_multiple_stuff (c, u, "sendc"); } idl_global->included_ami_receps_done (true); } int FE_Utils::path_cmp (const char *s, const char *t) { #if defined (WIN32) || defined (ACE_OPENVMS) // Since Windows has case-insensitive filenames, the preprocessor, // when searching using a provided relative path, will sometimes // capitalize the first letter of the last segment of a path name // and make the rest lowercase, regardless of how it was actually // spelled when created. This 'feature' was preventing the // validation of included IDL files, necessary before generating // the corresponding C++ includes. return ACE_OS::strcasecmp (s, t); #else return ACE_OS::strcmp (s, t); #endif /* defined (WIN32) */ } bool FE_Utils::hasspace (const char *s) { if (s) { const size_t length = ACE_OS::strlen (s); // Windows can't have a space as the first or last character // but a unix filename can. Need to check all characters. for (size_t i = 0u; i < length; ++i) { if (ACE_OS::ace_isspace (s [i])) { return true; } } } return false; } ACE_CString FE_Utils::check_for_seq_of_param (FE_Utils::T_PARAMLIST_INFO *list) { ACE_CString id, retval; const char *pattern = "sequence<"; size_t len = ACE_OS::strlen (pattern); size_t index = 0; for (FE_Utils::T_PARAMLIST_INFO::CONST_ITERATOR i (*list); !i.done (); i.advance (), ++index) { FE_Utils::T_Param_Info *param = 0; i.next (param); if (param->name_.find (pattern) == 0) { // Get the substring of what's between the brackets. // It will have to match a previous param in the list. id = param->name_.substr (len, param->name_.length () - (len + 1)); if (!FE_Utils::check_one_seq_of_param (list, id, index)) { retval = id; break; } } } return retval; } FILE * FE_Utils::open_included_file (char const * filename, char const *& directory) { FILE * f = 0; ACE_CString const the_file (ACE_CString ('/') + ACE_CString (filename)); for (IDL_GlobalData::Unbounded_Paths_Queue_Iterator i ( idl_global->include_paths ()); !i.done () && f == 0; i.advance ()) { IDL_GlobalData::Include_Path_Info *path_info = 0; (void) i.next (path_info); if (path_info->path_ != 0) { ACE_CString const complete_filename (ACE_CString (path_info->path_) + the_file); f = ACE_OS::fopen (complete_filename.c_str (), "r"); if (f != 0) directory = path_info->path_; } } return f; } bool FE_Utils::is_include_file_found (ACE_CString & inc_file, UTL_String * idl_file_name) { char abspath[MAXPATHLEN] = ""; char *full_path = 0; // If the include path has literal "s (because of an include // of a Windows path with spaces), we must remove them here. const char *tmp_inc_file = inc_file.c_str (); if (tmp_inc_file && FE_Utils::hasspace (tmp_inc_file) && tmp_inc_file[0] == '\"') { inc_file = inc_file.substr (1, inc_file.length () - 2); } inc_file += ACE_DIRECTORY_SEPARATOR_STR_A; inc_file += idl_file_name->get_string (); full_path = ACE_OS::realpath (inc_file.c_str (), abspath); if (full_path != 0) { FILE *test = ACE_OS::fopen (abspath, "r"); if (test == 0) { return false; } else { // Overwrite inc_file with abspath since the later // is normalized to the native OS representation. inc_file = abspath; ACE_OS::fclose (test); return true; } } return false; } /// Validate the included idl files, some files might have been /// ignored by the preprocessor. void FE_Utils::validate_included_idl_files (void) { // Flag to make sure we don't repeat things. static bool already_done = false; if (already_done) { return; } already_done = true; // New number of included_idl_files. size_t newj = 0; size_t n_pre_preproc_includes = idl_global->n_included_idl_files (); char **pre_preproc_includes = idl_global->included_idl_files (); size_t n_post_preproc_includes = idl_global->n_include_file_names (); UTL_String **post_preproc_includes = idl_global->include_file_names (); char pre_abspath [MAXPATHLEN] = ""; char post_abspath [MAXPATHLEN] = ""; IDL_GlobalData::Include_Path_Info *path_info = 0; char *post_tmp = 0; char *full_path = 0; // We are going to assemble a list of the include files found in // the top level file in the order that they are found in the pre- // processor output here // @see bug TAO#711 / Bugzilla #3513 for more char** ordered_include_files = new char* [n_pre_preproc_includes]; for (size_t i = 0u; i < n_post_preproc_includes; ++i) { post_tmp = post_preproc_includes [i]->get_string (); full_path = ACE_OS::realpath (post_tmp, post_abspath); if (full_path) { for (size_t j = 0u; j < n_pre_preproc_includes; ++j) { // Check this name with the name that we got from the // preprocessor. bool valid_file = false; full_path = ACE_OS::realpath (pre_preproc_includes [j], pre_abspath); if (full_path && FE_Utils::path_cmp (pre_abspath, post_abspath) == 0 && ACE_OS::access (post_abspath, R_OK) == 0) { // This file name is valid. valid_file = true; } else for (IDL_GlobalData::Unbounded_Paths_Queue_Iterator iter ( idl_global->include_paths ()); !iter.done (); iter.advance ()) { iter.next (path_info); ACE_CString pre_partial (path_info->path_); // If the include path has literal "s (because of an include // of a Windows path with spaces), we must remove them here. if (pre_partial.c_str () && 2u < pre_partial.length () && '"' == pre_partial [0] && '"' == pre_partial [pre_partial.length () - 1u]) { pre_partial = pre_partial.substr (1, pre_partial.length () - 2u); } pre_partial += ACE_DIRECTORY_SEPARATOR_STR_A; pre_partial += pre_preproc_includes [j]; full_path = ACE_OS::realpath (pre_partial.c_str (), pre_abspath); if (full_path && FE_Utils::path_cmp (pre_abspath, post_abspath) == 0 && ACE_OS::access (post_abspath, R_OK) == 0) { // This file name is valid. valid_file = true; break; } } if (valid_file) { // File is valid. // Move to the new index position. // ... in the ordered list ordered_include_files [newj] = pre_preproc_includes [j]; // Increment the new index. ++newj; for (size_t k = j + 1; k < n_pre_preproc_includes; ++k) { // Shift remaining entries down pre_preproc_includes [k-1] = pre_preproc_includes [k]; } // Reduce length and zero the discarded element pre_preproc_includes [--n_pre_preproc_includes] = 0; // Break out to next entry in pre-processor // output break; } } if (n_pre_preproc_includes == 0) { break; } } } // Tidy up not required includes for (size_t l = 0u; l < n_pre_preproc_includes; ++l) { delete [] pre_preproc_includes [l]; pre_preproc_includes [l] = 0; } // Copy list back for (size_t m = 0u; m < newj; ++m) { pre_preproc_includes [m] = ordered_include_files [m]; } delete [] ordered_include_files; // Now adjust the count on the included_idl_files. idl_global->n_included_idl_files (newj); } bool FE_Utils::validate_orb_include (UTL_String * idl_file_name) { char foundpath[MAXPATHLEN] = ""; { // Check in the current folder. char abspath[MAXPATHLEN] = ""; ACE_CString cwd_path = ACE_OS::getcwd (abspath, sizeof (abspath) / sizeof (char)); if (FE_Utils::is_include_file_found (cwd_path, idl_file_name)) { ACE_OS::strcpy (foundpath, cwd_path.c_str ()); } } for (IDL_GlobalData::Unbounded_Paths_Queue_Iterator iter ( idl_global->include_paths ()); !iter.done (); iter.advance ()) { IDL_GlobalData::Include_Path_Info *path_info = 0; iter.next (path_info); ACE_CString partial = path_info->path_; // We don't need to check anything if the file is already // found and the folder where are currently checking is // provided by user. if (foundpath[0] != 0 && !path_info->is_system_) { continue; } if (FE_Utils::is_include_file_found (partial, idl_file_name)) { if (path_info->is_system_) { if (foundpath[0] == 0 || ACE_OS::strcmp (foundpath, partial.c_str ()) == 0) { return true; } } else { // We can fill in foundpath here since we are sure // that it was not set before. Check above ensures that. ACE_OS::strcpy (foundpath, partial.c_str ()); continue; } } } return false; } void FE_Utils::original_local_name (Identifier *local_name) { const char *lname = local_name->get_string (); // Remove _cxx_ if: // 1. it occurs and // 2. it occurs at the beginning of the string and // 3. the rest of the string is a C++ keyword if (ACE_OS::strstr (lname, "_cxx_") == lname) { TAO_IDL_CPP_Keyword_Table cpp_key_tbl; unsigned int len = static_cast (ACE_OS::strlen (lname + 5)); const TAO_IDL_CPP_Keyword_Entry *entry = cpp_key_tbl.lookup (lname + 5, len); if (entry != 0) { ACE_CString tmp (lname + 5); local_name->replace_string (tmp.c_str ()); } } } bool FE_Utils::can_be_redefined (AST_Decl *prev_decl, AST_Decl *curr_decl) { AST_Decl::NodeType pnt = prev_decl->node_type (); AST_Decl::NodeType cnt = curr_decl->node_type (); switch (cnt) { /// For these, any non-zero previous decl /// is an error. case AST_Decl::NT_attr: case AST_Decl::NT_op: case AST_Decl::NT_ext_port: case AST_Decl::NT_mirror_port: case AST_Decl::NT_provides: case AST_Decl::NT_publishes: case AST_Decl::NT_consumes: case AST_Decl::NT_uses: return false; default: break; } UTL_Scope *prev_scope = prev_decl->defined_in (); UTL_Scope *curr_scope = curr_decl->defined_in (); AST_Structure *s = 0; AST_StructureFwd *s_fwd = 0; AST_Template_Module *ptm = 0; AST_Template_Module *ctm = 0; bool nt_eq = (pnt == cnt); bool s_eq = (prev_scope == curr_scope); switch (pnt) { case AST_Decl::NT_module: /// Need to check that both are modules. if (cnt != AST_Decl::NT_module) { return false; } /// Neither can be a template module. ptm = dynamic_cast (prev_decl); ctm = dynamic_cast (curr_decl); return (ptm == 0 && ctm == 0); /// For the *_fwd types, if scopes aren't related, it's ok. /// If they are related, then we need another fwd or a full decl. case AST_Decl::NT_component_fwd: return (!s_eq || (nt_eq || cnt == AST_Decl::NT_component)); case AST_Decl::NT_eventtype_fwd: return (!s_eq || (nt_eq || cnt == AST_Decl::NT_eventtype)); case AST_Decl::NT_interface_fwd: return (!s_eq || (nt_eq || cnt == AST_Decl::NT_interface)); case AST_Decl::NT_struct_fwd: return (!s_eq || (nt_eq || cnt == AST_Decl::NT_struct)); case AST_Decl::NT_union_fwd: return (!s_eq || (nt_eq || cnt == AST_Decl::NT_union)); case AST_Decl::NT_valuetype_fwd: return (!s_eq || (nt_eq || cnt == AST_Decl::NT_valuetype)); /// If scopes aren't related, it's ok. If they are, check /// if the previous is a dummy for a fwd decl. Even a redef /// in a derived interface type is ok. case AST_Decl::NT_struct: case AST_Decl::NT_union: s = dynamic_cast (prev_decl); s_fwd = (s == 0 ? 0 : s->fwd_decl ()); return (!s_eq || s_fwd != 0); /// Only 2 or more full definitions in the same scope are illegal, /// and that is caught elsewhere. case AST_Decl::NT_interface: case AST_Decl::NT_component: case AST_Decl::NT_eventtype: case AST_Decl::NT_valuetype: return true; /// For connectors, factories, exceptions, constants and types, even a /// redef in a derived interface type is ok. Checking for /// unequal scopes covers boxed valuetypes as well. case AST_Decl::NT_connector: case AST_Decl::NT_valuebox: case AST_Decl::NT_except: case AST_Decl::NT_typedef: case AST_Decl::NT_const: case AST_Decl::NT_factory: case AST_Decl::NT_type: case AST_Decl::NT_enum: case AST_Decl::NT_enum_val: { return !s_eq; } /// What's left are the case from the previous switch statement. /// Same rule applies - no overrides or redefs, ever. default: return false; } } void FE_Utils::tmpl_mod_ref_check (AST_Decl *context, AST_Decl *ref) { if (ref == 0 || ref->node_type () == AST_Decl::NT_param_holder || idl_global->in_tmpl_mod_alias ()) { return; } bool ok = true; if (ref->in_tmpl_mod_not_aliased ()) { if (! context->in_tmpl_mod_not_aliased ()) { ok = false; } else { AST_Template_Module *context_tm = FE_Utils::get_tm_container (context); AST_Template_Module *ref_tm = FE_Utils::get_tm_container (ref); if (context_tm != ref_tm) { ok = false; } } } if (! ok) { idl_global->err ()->template_scope_ref_not_aliased (ref); throw Bailout (); } } bool FE_Utils::check_one_seq_of_param (FE_Utils::T_PARAMLIST_INFO *list, ACE_CString ¶m_id, size_t index) { size_t local_index = 0; for (FE_Utils::T_PARAMLIST_INFO::CONST_ITERATOR i (*list); !i.done (); i.advance (), ++local_index) { if (local_index == index) { break; } FE_Utils::T_Param_Info *info = 0; i.next (info); if (info->name_ == param_id) { return true; } } return false; } AST_Template_Module * FE_Utils::get_tm_container (AST_Decl *contained) { AST_Decl *d = contained; while (d != 0) { AST_Template_Module *tm = dynamic_cast (d); if (tm != 0) { return tm; } d = ScopeAsDecl (d->defined_in ()); } return 0; }