// file : odb/processor.cxx // copyright : Copyright (c) 2009-2015 Code Synthesis Tools CC // license : GNU GPL v3; see accompanying LICENSE file #include #include #include // std::find #include #include #include #include #include #include #include using namespace std; namespace { // Indirect (dynamic) context values. // static semantics::type* id_tree_type () { context& c (context::current ()); semantics::data_member& id (*context::id_member (*c.top_object)); return &id.type (); } struct data_member: traversal::data_member, context { virtual void traverse (semantics::data_member& m) { if (transient (m)) return; semantics::names* hint; semantics::type& t (utype (m, hint)); // See if this member is a section. // if (t.fq_name () == "::odb::section") { using semantics::class_; class_& c (dynamic_cast (m.scope ())); class_* poly_root (polymorphic (c)); semantics::data_member* opt (optimistic (c)); // If we have sections in a polymorphic optimistic hierarchy, // then the version member should be in the root. // if (poly_root == &c && opt != 0 && &opt->scope () != &c) { error (m.location ()) << "version must be a direct data member " << "of a class that contains sections" << endl; info (opt->location ()) << "version member is declared here" << endl; throw operation_failed (); } process_user_section (m, c); // We don't need a modifier but the accessor should be by-reference. // process_access (m, "get"); member_access& ma (m.get ("get")); if (ma.by_value) { error (ma.loc) << "accessor returning a value cannot be used " << "for a section" << endl; info (ma.loc) << "accessor returning a const reference is required" << endl; info (m.location ()) << "data member is defined here" << endl; throw operation_failed (); } // Mark this member as transient since we don't store it in the // database. // m.set ("transient", true); features.section = true; return; } else { process_access (m, "get"); process_access (m, "set"); } // See if this member belongs to a section. // if (m.count ("section-member") != 0) process_section_member (m); // We don't need to do any further processing for common if we // are generating static multi-database code. // if (multi_static && options.database ()[0] == database::common) return; // Handle wrappers. // semantics::type* wt (0), *qwt (0); semantics::names* whint (0); if (process_wrapper (t)) { qwt = t.get ("wrapper-type"); whint = t.get ("wrapper-hint"); wt = &utype (*qwt, whint); } // If the type is const and the member is not id, version, or // inverse, then mark it as readonly. In case of a wrapper, // both the wrapper type and the wrapped type must be const. // To see why, consider these possibilities: // // auto_ptr - can modify by setting a new pointer // const auto_ptr - can modify by changing the pointed-to value // if (const_type (m.type ()) && !(id (m) || version (m) || m.count ("inverse"))) { if (qwt == 0 || const_type (*qwt)) m.set ("readonly", true); } process_points_to (m); if (composite_wrapper (t)) return; // Process object pointer. The resulting column will be a simple // or composite value. // if (process_object_pointer (m, t)) return; // Before checking if this is a container, check if this member // or its type were deduced to be a simple value based on the // pragmas. This is necessary because a container member (e.g., // vector) can be "overridden" into a simple value (e.g., // BLOB) with a pragma. // if (m.count ("simple") || t.count ("simple") || (wt != 0 && wt->count ("simple"))) return; process_container (m, (wt != 0 ? *wt : t)); } // // Process member access expressions. // enum found_type { found_none, found_some, // Found something but keep looking for a better one. found_best }; // Check if a function is a suitable accessor for this member. // found_type check_accessor (semantics::data_member& m, tree f, string const& n, member_access& ma, bool strict) { // Must be const. // if (!DECL_CONST_MEMFUNC_P (f)) return found_none; // Accessor is a function with no arguments (other than 'this'). // if (FUNCTION_FIRST_USER_PARMTYPE (f) != void_list_node) return found_none; // Note that to get the return type we have to use // TREE_TYPE(TREE_TYPE()) and not DECL_RESULT, as // suggested in the documentation. // tree r (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (f)))); gcc_tree_code_type tc (TREE_CODE (r)); // In the strict mode make sure the function returns for non-array // types a value or a (const) reference to the member type and for // array types a (const) pointer to element type. In the lax mode // we just check that the return value is not void. // if (strict) { semantics::type& t (utype (m)); semantics::array* ar (dynamic_cast (&t)); if (ar != 0 && tc != POINTER_TYPE) return found_none; tree bt (ar != 0 || tc == REFERENCE_TYPE ? TREE_TYPE (r) : r); tree bt_mv (TYPE_MAIN_VARIANT (bt)); if ((ar != 0 ? ar->base_type () : t).tree_node () != bt_mv) return found_none; } else if (r == void_type_node) return found_none; cxx_tokens& e (ma.expr); e.push_back (cxx_token (0, CPP_KEYWORD, "this")); e.push_back (cxx_token (0, CPP_DOT)); e.push_back (cxx_token (0, CPP_NAME, n)); e.push_back (cxx_token (0, CPP_OPEN_PAREN, n)); e.push_back (cxx_token (0, CPP_CLOSE_PAREN, n)); // See if it returns by value. // ma.by_value = (tc != REFERENCE_TYPE && tc != POINTER_TYPE); return found_best; } // Check if a function is a suitable modifier for this member. // found_type check_modifier (semantics::data_member& m, tree f, string const& n, member_access& ma, bool strict) { tree a (FUNCTION_FIRST_USER_PARMTYPE (f)); // For a modifier, it can either be a function that returns a non- // const reference (or non-const pointer, in case the member is an // array) or a by-value modifier that sets a new value. If both are // available, we prefer the former for efficiency. // cxx_tokens& e (ma.expr); semantics::type& t (utype (m)); semantics::array* ar (dynamic_cast (&t)); if (a == void_list_node) { // Note that to get the return type we have to use // TREE_TYPE(TREE_TYPE()) and not DECL_RESULT, as // suggested in the documentation. // tree r (TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (f)))); gcc_tree_code_type tc (TREE_CODE (r)); // By-reference modifier. Should return a reference or a pointer. // if (tc != (ar != 0 ? POINTER_TYPE : REFERENCE_TYPE)) return found_none; // The base type should not be const and, in strict mode, should // match the member type. // tree bt (TREE_TYPE (r)); if (CP_TYPE_CONST_P (bt)) return found_none; tree bt_mv (TYPE_MAIN_VARIANT (bt)); if (strict && (ar != 0 ? ar->base_type () : t).tree_node () != bt_mv) return found_none; e.clear (); // Could contain by value modifier. e.push_back (cxx_token (0, CPP_KEYWORD, "this")); e.push_back (cxx_token (0, CPP_DOT)); e.push_back (cxx_token (0, CPP_NAME, n)); e.push_back (cxx_token (0, CPP_OPEN_PAREN, n)); e.push_back (cxx_token (0, CPP_CLOSE_PAREN, n)); return found_best; } // Otherwise look for a by value modifier, which is a function // with a single argument. // else if (TREE_CHAIN (a) == void_list_node) { // In the lax mode any function with a single argument works // for us. And we don't care what it returns. // if (strict) { // In the strict mode make sure the argument matches the // member. This is exactly the same logic as in accessor // with regards to arrays, references, etc. // tree at (TREE_VALUE (a)); gcc_tree_code_type tc (TREE_CODE (at)); if (ar != 0 && tc != POINTER_TYPE) return found_none; tree bt (ar != 0 || tc == REFERENCE_TYPE ? TREE_TYPE (at) : at); tree bt_mv (TYPE_MAIN_VARIANT (bt)); if ((ar != 0 ? ar->base_type () : t).tree_node () != bt_mv) return found_none; } if (e.empty ()) { e.push_back (cxx_token (0, CPP_KEYWORD, "this")); e.push_back (cxx_token (0, CPP_DOT)); e.push_back (cxx_token (0, CPP_NAME, n)); e.push_back (cxx_token (0, CPP_OPEN_PAREN, n)); e.push_back (cxx_token (0, CPP_QUERY)); e.push_back (cxx_token (0, CPP_CLOSE_PAREN, n)); // Continue searching in case there is version that returns a // non-const reference which we prefer for efficiency. // return found_some; } else return found_none; // We didn't find anything better. } return found_none; } void process_access (semantics::data_member& m, std::string const& k) { bool virt (m.count ("virtual")); // Ignore certain special virtual members. // if (virt && (m.count ("polymorphic-ref") || m.count ("discriminator"))) return; char const* kind (k == "get" ? "accessor" : "modifier"); semantics::class_& c (dynamic_cast (m.scope ())); // If we don't have an access expression, try to come up with // one. // if (!m.count (k)) { found_type found (found_none); semantics::access const& a (m.named ().access ()); member_access& ma ( m.set ( k, member_access (m.location (), kind, true))); // If this member is not virtual and is either public or if we // are a friend of this class, then go for the member directly. // if (!virt && (a == semantics::access::public_ || c.get ("friend"))) { ma.expr.push_back (cxx_token (0, CPP_KEYWORD, "this")); ma.expr.push_back (cxx_token (0, CPP_DOT)); ma.expr.push_back (cxx_token (0, CPP_NAME, m.name ())); found = found_best; } // Otherwise, try to find a suitable accessor/modifier. // // First try the original name. If that doesn't produce anything, // then try the public name. // bool t (k == "get" ? options.accessor_regex_trace () : options.modifier_regex_trace ()); regex_mapping const& re ( k == "get" ? accessor_regex : modifier_regex); for (unsigned short j (0); found != found_best && j != 2; ++j) { string b (j == 0 ? m.name () : public_name (m, false)); // Skip the second pass if original and public names are the same. // if (j == 1 && b == m.name ()) continue; if (t) cerr << kind << (j == 0 ? " original" : " public") << " name '" << b << "'" << endl; for (regex_mapping::const_iterator i (re.begin ()); found != found_best && i != re.end (); ++i) { if (t) cerr << "try: '" << i->regex () << "' : "; if (!i->match (b)) { if (t) cerr << '-' << endl; continue; } string n (i->replace (b)); if (t) cerr << "'" << n << "' : "; tree decl ( lookup_qualified_name ( c.tree_node (), get_identifier (n.c_str ()), false, false)); if (decl == error_mark_node || TREE_CODE (decl) != BASELINK) { if (t) cerr << '-' << endl; continue; } // OVL_* macros work for both FUNCTION_DECL and OVERLOAD. // for (tree o (BASELINK_FUNCTIONS (decl)); o != 0; o = OVL_NEXT (o)) { tree f (OVL_CURRENT (o)); // We are only interested in public non-static member // functions. Note that TREE_PUBLIC() returns something // other than what we need. // if (!DECL_NONSTATIC_MEMBER_FUNCTION_P (f) || TREE_PRIVATE (f) || TREE_PROTECTED (f)) continue; found_type r (k == "get" ? check_accessor (m, f, n, ma, true) : check_modifier (m, f, n, ma, true)); if (r != found_none) { // Update the location of the access expression to point // to this function. // ma.loc = location (real_source_location (f)); found = r; } } if (t) cerr << (found != found_none ? '+' : '-') << endl; } } // If that didn't work then the generated code won't be able // to access this member. // if (found == found_none) { location const& l (m.location ()); if (virt) { error (l) << "no suitable " << kind << " function could be " << "automatically found for virtual data member '" << m.name () << "'" << endl; info (l) << "use '#pragma db " << k << "' to explicitly " << "specify the " << kind << " function or " << "expression" << endl; } else { error (l) << "data member '" << m.name () << "' is " << a.string () << " and no suitable " << kind << " function could be automatically found" << endl; info (l) << "consider making class 'odb::access' a friend of " << "class '" << class_name (c) << "'" << endl; info (l) << "or use '#pragma db " << k << "' to explicitly " << "specify the " << kind << " function or " << "expression" << endl; } throw operation_failed (); } } member_access& ma (m.get (k)); if (ma.empty ()) return; cxx_tokens& e (ma.expr); // If it is just a name, resolve it and convert to an appropriate // expression. // if (e.size () == 1 && e.back ().type == CPP_NAME) { string n (e.back ().literal); e.clear (); tree decl ( lookup_qualified_name ( c.tree_node (), get_identifier (n.c_str ()), false, false)); if (decl == error_mark_node) { error (ma.loc) << "unable to resolve data member or function " << "name '" << n << "'" << endl; throw operation_failed (); } switch (TREE_CODE (decl)) { case FIELD_DECL: { e.push_back (cxx_token (0, CPP_KEYWORD, "this")); e.push_back (cxx_token (0, CPP_DOT)); e.push_back (cxx_token (0, CPP_NAME, n)); break; } case BASELINK: { // OVL_* macros work for both FUNCTION_DECL and OVERLOAD. // for (tree o (BASELINK_FUNCTIONS (decl)); o != 0; o = OVL_NEXT (o)) { tree f (OVL_CURRENT (o)); // We are only interested in non-static member functions. // if (!DECL_NONSTATIC_MEMBER_FUNCTION_P (f)) continue; if ((k == "get" ? check_accessor (m, f, n, ma, false) : check_modifier (m, f, n, ma, false)) == found_best) break; } if (e.empty ()) { error (ma.loc) << "unable to find suitable " << kind << " function '" << n << "'" << endl; throw operation_failed (); } break; } default: { error (ma.loc) << "name '" << n << "' does not refer to a data " << "member or function" << endl; throw operation_failed (); } } } // If there is no 'this' keyword, then add it as a prefix. // { bool t (false); for (cxx_tokens::iterator i (e.begin ()); i != e.end (); ++i) { if (i->type == CPP_KEYWORD && i->literal == "this") { t = true; break; } } if (!t) { e.insert (e.begin (), cxx_token (0, CPP_DOT)); e.insert (e.begin (), cxx_token (0, CPP_KEYWORD, "this")); } } // Check that there is no placeholder in the accessor expression. // if (k == "get" && ma.placeholder ()) { error (ma.loc) << "(?) placeholder in the accessor expression" << endl; throw operation_failed (); } // Check that the member type is default-constructible if we // have a by value modifier. // if (k == "set" && ma.placeholder ()) { semantics::class_* c (dynamic_cast (&utype (m))); // Assume all other types are default-constructible. // if (c != 0) { // If this type is a class template instantiation, then make // sure it is instantiated. While types used in real members // will be instantiated, this is not necessarily the case for // virtual members. Without the instantiation we won't be able // to detect whether the type has the default ctor. // // It would have been cleaner to do it in post_process_pragmas() // but there we don't yet know whether we need the default ctor. // And it is a good idea not to require instantiability unless // we really need it. // tree type (c->tree_node ()); if (!COMPLETE_TYPE_P (type) && CLASSTYPE_TEMPLATE_INSTANTIATION (type)) { // Reset input location so that we get nice diagnostics in // case of an error. Use the location of the virtual pragma. // location_t loc (m.get ("virtual-location")); input_location = loc; if (instantiate_class_template (type) == error_mark_node || errorcount != 0 || !COMPLETE_TYPE_P (type)) { error (loc) << "unable to instantiate virtual data member " << "type" << endl; throw operation_failed (); } } if (!c->default_ctor ()) { error (ma.loc) << "modifier expression requires member type " << "to be default-constructible" << endl; throw operation_failed (); } } } } // // Process section. // user_section& process_user_section (semantics::data_member& m, semantics::class_& c) { user_sections& uss (c.get ("user-sections")); user_section::load_type l ( m.get ("section-load", user_section::load_eager)); user_section::update_type u ( m.get ("section-update", user_section::update_always)); if (l == user_section::load_eager && u == user_section::update_always) { location const& l (m.location ()); error (l) << "eager-loaded, always-updated section is pointless" << endl; info (l) << "use '#pragma db load' and/or '#pragma db update' to " "specify an alternative loading and/or updating strategy" << endl; info (l) << "or remove the section altogether" << endl; throw operation_failed (); } size_t n (uss.count (user_sections::count_total | user_sections::count_all | user_sections::count_special_version)); user_section us (m, c, n, l, u); // We may already have seen this section (e.g., forward reference // from a member of this section). // user_sections::iterator i (find (uss.begin (), uss.end (), us)); if (i != uss.end ()) return *i; // If we are adding a new section to an optimistic class with // version in a base, make sure the base is sectionable. // semantics::data_member* opt (optimistic (c)); if (opt != 0 && &opt->scope () != &c) { semantics::class_* poly_root (polymorphic (c)); semantics::node* base (poly_root ? poly_root : &opt->scope ()); if (!base->count ("sectionable")) { error (m.location ()) << "adding new section to a derived class " << "in an optimistic hierarchy requires sectionable base class" << endl; info (base->location ()) << "use '#pragma db object sectionable' " << "to make the base class of this hierarchy sectionable" << endl; throw operation_failed (); } } uss.push_back (us); return uss.back (); } void process_section_member (semantics::data_member& m) { using semantics::class_; using semantics::data_member; string name (m.get ("section-member")); location_t loc (m.get ("section-member-location")); class_& c (dynamic_cast (m.scope ())); class_* poly_root (polymorphic (c)); bool poly_derived (poly_root != 0 && poly_root != &c); try { data_member& us (c.lookup (name, class_::include_hidden)); // Make sure we are referencing a section. // if (utype (us).fq_name () != "::odb::section") { error (loc) << "data member '" << name << "' in '#pragma db " << "section' is not of the odb::section type" << endl; throw operation_failed (); } // If the section is in the base, handle polymorphic inheritance. // class_& b (dynamic_cast (us.scope ())); user_section* s (0); if (&c != &b && poly_derived) { user_sections& uss (c.get ("user-sections")); // This is a section override. See if we have already handled // this section. // for (user_sections::iterator i (uss.begin ()); s == 0 && i != uss.end (); ++i) { if (i->member == &us) s = &*i; } // Otherwise, find and copy the nearest override in the base. // The result should be a chain of overrides leading all the // way to the original section. // if (s == 0) { for (class_* b (&polymorphic_base (c));; b = &polymorphic_base (*b)) { user_sections& buss (b->get ("user-sections")); for (user_sections::iterator i (buss.begin ()); s == 0 && i != buss.end (); ++i) { if (i->member == &us) { uss.push_back (*i); uss.back ().object = &c; uss.back ().base = &*i; s = &uss.back (); } } if (s != 0) break; assert (b != poly_root); // We should have found it by now. } } } else s = &process_user_section (us, c); // Mark the member as added/deleted if the section is added/deleted. // Also check that the version ordering is correct. // if (unsigned long long sav = added (*s->member)) { location_t sl (s->member->get ("added-location")); if (unsigned long long mav = added (m)) { location_t ml (m.get ("added-location")); if (mav < sav) { error (ml) << "member addition version is less than the " << "section addition version" << endl; info (sl) << "section addition version is specified here" << endl; throw operation_failed (); } if (mav == sav) { error (ml) << "member addition version is the same as " << "section addition version" << endl; info (sl) << "section addition version is specified here" << endl; info (ml) << "delete this pragma" << endl; throw operation_failed (); } } else { m.set ("added", sav); m.set ("added-location", sl); } } if (unsigned long long sdv = deleted (*s->member)) { location_t sl (s->member->get ("deleted-location")); if (unsigned long long mdv = deleted (m)) { location_t ml (m.get ("deleted-location")); if (mdv > sdv) { error (ml) << "member deletion version is greater than the " << "section deletion version" << endl; info (sl) << "section deletion version is specified here" << endl; throw operation_failed (); } if (mdv == sdv) { error (ml) << "member deletion version is the same as " << "section deletion version" << endl; info (sl) << "section deletion version is specified here" << endl; info (ml) << "delete this pragma" << endl; throw operation_failed (); } } else { m.set ("deleted", sdv); m.set ("deleted-location", sl); } } // Insert as object_section. // m.set ("section", static_cast (s)); } catch (semantics::unresolved const& e) { if (e.type_mismatch) error (loc) << "name '" << name << "' in '#pragma db section' " << "does not refer to a data member" << endl; else error (loc) << "unable to resolve data member '" << name << "' " << "specified with '#pragma db section'" << endl; throw operation_failed (); } catch (semantics::ambiguous const& e) { error (loc) << "data member name '" << name << "' specified " << "with '#pragma db section' is ambiguous" << endl; info (e.first.named ().location ()) << "could resolve to this " << "data member" << endl; info (e.second.named ().location ()) << "or could resolve to " << "this data member" << endl; throw operation_failed (); } } // // Process wrapper. // bool process_wrapper (semantics::type& t) { if (t.count ("wrapper")) return t.get ("wrapper"); // Check this type with wrapper_traits. // tree inst (instantiate_template (wrapper_traits_, t.tree_node ())); if (inst == 0) { t.set ("wrapper", false); return false; } // @@ This points to the primary template, not the specialization. // tree decl (TYPE_NAME (inst)); string f (DECL_SOURCE_FILE (decl)); size_t l (DECL_SOURCE_LINE (decl)); size_t c (DECL_SOURCE_COLUMN (decl)); // Get the wrapped type. // try { tree decl ( lookup_qualified_name ( inst, get_identifier ("wrapped_type"), true, false)); if (decl == error_mark_node || TREE_CODE (decl) != TYPE_DECL) throw operation_failed (); // The wrapped_type alias is a typedef in an instantiation // that we just instantiated dynamically. As a result there // is no semantic graph edges corresponding to this typedef // since we haven't parsed it yet. So to get the tree node // that can actually be resolved to the graph node, we use // the source type of this typedef. // tree type (DECL_ORIGINAL_TYPE (decl)); bool qc (CP_TYPE_CONST_P (type)); bool qv (CP_TYPE_VOLATILE_P (type)); bool qr (CP_TYPE_RESTRICT_P (type)); type = TYPE_MAIN_VARIANT (type); semantics::type* wt ( dynamic_cast (unit.find (type))); // Object pointers and wrappers often use the same smart // pointers so check if the wrapped type is an object. // if (object (*wt)) { t.set ("wrapper", false); return false; } if (qc || qv || qr) { for (semantics::type::qualified_iterator i (wt->qualified_begin ()); i != wt->qualified_end (); ++i) { semantics::qualifier& q (i->qualifier ()); if (q.const_ () == qc && q.volatile_ () == qv && q.restrict_ () == qr) { wt = &q; break; } } } // Find the hint. // semantics::names* wh (0); for (tree ot (DECL_ORIGINAL_TYPE (decl)); ot != 0; ot = decl ? DECL_ORIGINAL_TYPE (decl) : 0) { if ((wh = unit.find_hint (ot))) break; decl = TYPE_NAME (ot); } t.set ("wrapper-type", wt); t.set ("wrapper-hint", wh); } catch (operation_failed const&) { os << f << ":" << l << ":" << c << ": error: " << "wrapper_traits specialization does not define the " << "wrapped_type type" << endl; throw; } // Get the null_handler flag. // bool null_handler (false); try { tree nh ( lookup_qualified_name ( inst, get_identifier ("null_handler"), false, false)); if (nh == error_mark_node || TREE_CODE (nh) != VAR_DECL) throw operation_failed (); // Instantiate this decalaration so that we can get its value. // if (DECL_TEMPLATE_INSTANTIATION (nh) && !DECL_TEMPLATE_INSTANTIATED (nh) && !DECL_EXPLICIT_INSTANTIATION (nh)) instantiate_decl (nh, false, false); tree init (DECL_INITIAL (nh)); if (init == error_mark_node || TREE_CODE (init) != INTEGER_CST) throw operation_failed (); null_handler = static_cast (integer_value (init)); t.set ("wrapper-null-handler", null_handler); } catch (operation_failed const&) { os << f << ":" << l << ":" << c << ": error: " << "wrapper_traits specialization does not define the " << "null_handler constant" << endl; throw; } // Get the null_default flag. // if (null_handler) { try { tree nh ( lookup_qualified_name ( inst, get_identifier ("null_default"), false, false)); if (nh == error_mark_node || TREE_CODE (nh) != VAR_DECL) throw operation_failed (); // Instantiate this decalaration so that we can get its value. // if (DECL_TEMPLATE_INSTANTIATION (nh) && !DECL_TEMPLATE_INSTANTIATED (nh) && !DECL_EXPLICIT_INSTANTIATION (nh)) instantiate_decl (nh, false, false); tree init (DECL_INITIAL (nh)); if (init == error_mark_node || TREE_CODE (init) != INTEGER_CST) throw operation_failed (); t.set ("wrapper-null-default", static_cast (integer_value (init))); } catch (operation_failed const&) { os << f << ":" << l << ":" << c << ": error: " << "wrapper_traits specialization does not define the " << "null_default constant" << endl; throw; } } // Check if the wrapper is a TR1 template instantiation. // if (tree ti = TYPE_TEMPLATE_INFO (t.tree_node ())) { tree decl (TI_TEMPLATE (ti)); // DECL_TEMPLATE // Get to the most general template declaration. // while (DECL_TEMPLATE_INFO (decl)) decl = DECL_TI_TEMPLATE (decl); bool& tr1 (features.tr1_pointer); bool& boost (features.boost_pointer); string n (decl_as_string (decl, TFF_PLAIN_IDENTIFIER)); // In case of a boost TR1 implementation, we cannot distinguish // between the boost:: and std::tr1:: usage since the latter is // just a using-declaration for the former. // tr1 = tr1 || n.compare (0, 8, "std::tr1") == 0 || n.compare (0, 10, "::std::tr1") == 0; boost = boost || n.compare (0, 17, "boost::shared_ptr") == 0 || n.compare (0, 19, "::boost::shared_ptr") == 0; } t.set ("wrapper", true); return true; } // // Process object pointer. // semantics::class_* process_object_pointer (semantics::data_member& m, semantics::type& t, string const& kp = string ()) { using semantics::class_; using semantics::data_member; class_* c (0); // The overall idea is as follows: try to instantiate the pointer // traits class template. If we are successeful, then get the // element type and see if it is an object. // if (t.count ("element-type")) c = t.get ("element-type"); else { tree inst (instantiate_template (pointer_traits_, t.tree_node ())); if (inst == 0) return 0; // @@ This points to the primary template, not the specialization. // tree decl (TYPE_NAME (inst)); string fl (DECL_SOURCE_FILE (decl)); size_t ln (DECL_SOURCE_LINE (decl)); size_t cl (DECL_SOURCE_COLUMN (decl)); // Get the element type. // tree tn (0); try { tree decl ( lookup_qualified_name ( inst, get_identifier ("element_type"), true, false)); if (decl == error_mark_node || TREE_CODE (decl) != TYPE_DECL) throw operation_failed (); tn = TYPE_MAIN_VARIANT (TREE_TYPE (decl)); // Check if the pointer is a TR1 template instantiation. // if (tree ti = TYPE_TEMPLATE_INFO (t.tree_node ())) { decl = TI_TEMPLATE (ti); // DECL_TEMPLATE // Get to the most general template declaration. // while (DECL_TEMPLATE_INFO (decl)) decl = DECL_TI_TEMPLATE (decl); bool& tr1 (features.tr1_pointer); bool& boost (features.boost_pointer); string n (decl_as_string (decl, TFF_PLAIN_IDENTIFIER)); // In case of a boost TR1 implementation, we cannot distinguish // between the boost:: and std::tr1:: usage since the latter is // just a using-declaration for the former. // tr1 = tr1 || n.compare (0, 8, "std::tr1") == 0 || n.compare (0, 10, "::std::tr1") == 0; boost = boost || n.compare (0, 17, "boost::shared_ptr") == 0 || n.compare (0, 19, "::boost::shared_ptr") == 0; } } catch (operation_failed const&) { os << fl << ":" << ln << ":" << cl << ": error: pointer_traits " << "specialization does not define the 'element_type' type" << endl; throw; } c = dynamic_cast (unit.find (tn)); if (c == 0 || !object (*c)) return 0; t.set ("element-type", c); // Determine the pointer kind. // try { tree kind ( lookup_qualified_name ( inst, get_identifier ("kind"), false, false)); if (kind == error_mark_node || TREE_CODE (kind) != VAR_DECL) throw operation_failed (); // Instantiate this decalaration so that we can get its value. // if (DECL_TEMPLATE_INSTANTIATION (kind) && !DECL_TEMPLATE_INSTANTIATED (kind) && !DECL_EXPLICIT_INSTANTIATION (kind)) instantiate_decl (kind, false, false); tree init (DECL_INITIAL (kind)); if (init == error_mark_node || TREE_CODE (init) != INTEGER_CST) throw operation_failed (); pointer_kind_type pk = static_cast ( integer_value (init)); t.set ("pointer-kind", pk); } catch (operation_failed const&) { os << fl << ":" << ln << ":" << cl << ": error: pointer_traits " << "specialization does not define the 'kind' constant" << endl; throw; } // Get the lazy flag. // try { tree lazy ( lookup_qualified_name ( inst, get_identifier ("lazy"), false, false)); if (lazy == error_mark_node || TREE_CODE (lazy) != VAR_DECL) throw operation_failed (); // Instantiate this decalaration so that we can get its value. // if (DECL_TEMPLATE_INSTANTIATION (lazy) && !DECL_TEMPLATE_INSTANTIATED (lazy) && !DECL_EXPLICIT_INSTANTIATION (lazy)) instantiate_decl (lazy, false, false); tree init (DECL_INITIAL (lazy)); if (init == error_mark_node || TREE_CODE (init) != INTEGER_CST) throw operation_failed (); t.set ("pointer-lazy", static_cast (integer_value (init))); } catch (operation_failed const&) { os << fl << ":" << ln << ":" << cl << ": error: pointer_traits " << "specialization does not define the 'kind' constant" << endl; throw; } } // See if this is the inverse side of a bidirectional relationship. // If so, then resolve the member path and cache it in the context. // if (m.count ("inverse")) { string name (m.get ("inverse")); location_t l (m.get ("inverse-location")); data_member_path mp (resolve_data_members (*c, name, l, lex_)); { string tl; data_member& m (*mp.back ()); if (container (m) && lex_.next (tl) != CPP_EOF) { error (l) << "unexpect name after container member " << m.name () << " in '#pragma db inverse'" << endl; throw operation_failed (); } } // Validate each member. // for (data_member_path::iterator i (mp.begin ()); i != mp.end (); ++i) { data_member& im (**i); const string& n (im.name ()); if (im.count ("transient")) { error (l) << "data member '" << n << "' specified with " << "'#pragma db inverse' is transient" << endl; info (im.location ()) << "data member '" << n << "' is " << "defined here" << endl; throw operation_failed (); } if (im.count ("inverse") || im.count ("value-inverse")) { error (l) << "data member '" << n << "' specified with " << "'#pragma db inverse' is itself inverse" << endl; info (im.location ()) << "data member '" << n << "' is " << "defined here" << endl; throw operation_failed (); } } // @@ Would be good to check that the other end is actually // an object pointer/points_to and points to the correct // object. But the other class may not have been processed // yet. Need to do in validator, pass 2. // m.remove ("inverse"); m.set (kp + (kp.empty () ? "": "-") + "inverse", mp); } return c; } // // Process points-to pragma. // void process_points_to (semantics::data_member& m, string const& /*kp*/ = string ()) { if (!m.count ("points-to")) return; using semantics::class_; tree t (m.get ("points-to")); location_t l (m.get ("points-to-location")); class_* c (dynamic_cast (unit.find (t))); if (c == 0 || !object (*c)) { error (l) << "name specified with '#pragma db points_to' does " << "not refer to an object" << endl; throw operation_failed (); } m.remove ("points-to"); m.set (/*kp + (kp.empty () ? "": "-") + */"points-to", c); } // // Process container. // void process_container_value (semantics::type& t, semantics::data_member& m, string const& prefix, bool obj_ptr) { process_wrapper (t); if (composite_wrapper (t)) return; if (obj_ptr) process_object_pointer (m, t, prefix); } bool process_container (semantics::data_member& m, semantics::type& t) { // The overall idea is as follows: try to instantiate the container // traits class template. If we are successeful, then this is a // container type and we can extract the various information from // the instantiation. Otherwise, this is not a container. // container_kind_type ck; bool smart; semantics::type* vt (0); semantics::type* it (0); semantics::type* kt (0); semantics::names* vh (0); semantics::names* ih (0); semantics::names* kh (0); if (t.count ("container-kind")) { ck = t.get ("container-kind"); smart = t.get ("container-smart"); vt = t.get ("value-tree-type"); vh = t.get ("value-tree-hint"); if (ck == ck_ordered) { it = t.get ("index-tree-type"); ih = t.get ("index-tree-hint"); } if (ck == ck_map || ck == ck_multimap) { kt = t.get ("key-tree-type"); kh = t.get ("key-tree-hint"); } } else { tree inst (instantiate_template (container_traits_, t.tree_node ())); if (inst == 0) return false; // @@ This points to the primary template, not the specialization. // tree decl (TYPE_NAME (inst)); string f (DECL_SOURCE_FILE (decl)); size_t l (DECL_SOURCE_LINE (decl)); size_t c (DECL_SOURCE_COLUMN (decl)); // Determine the container kind. // try { tree decl ( lookup_qualified_name ( inst, get_identifier ("kind"), false, false)); if (decl == error_mark_node || TREE_CODE (decl) != VAR_DECL) throw operation_failed (); // Instantiate this decalaration so that we can get its value. // if (DECL_TEMPLATE_INSTANTIATION (decl) && !DECL_TEMPLATE_INSTANTIATED (decl) && !DECL_EXPLICIT_INSTANTIATION (decl)) instantiate_decl (decl, false, false); tree init (DECL_INITIAL (decl)); if (init == error_mark_node || TREE_CODE (init) != INTEGER_CST) throw operation_failed (); ck = static_cast (integer_value (init)); } catch (operation_failed const&) { os << f << ":" << l << ":" << c << ": error: " << "container_traits specialization does not define the " << "container kind constant" << endl; throw; } t.set ("container-kind", ck); // See if it is a smart container. // try { tree decl ( lookup_qualified_name ( inst, get_identifier ("smart"), false, false)); if (decl == error_mark_node || TREE_CODE (decl) != VAR_DECL) throw operation_failed (); // Instantiate this decalaration so that we can get its value. // if (DECL_TEMPLATE_INSTANTIATION (decl) && !DECL_TEMPLATE_INSTANTIATED (decl) && !DECL_EXPLICIT_INSTANTIATION (decl)) instantiate_decl (decl, false, false); tree init (DECL_INITIAL (decl)); if (init == error_mark_node || TREE_CODE (init) != INTEGER_CST) throw operation_failed (); smart = static_cast (integer_value (init)); } catch (operation_failed const&) { os << f << ":" << l << ":" << c << ": error: " << "container_traits specialization does not define the " << "'smart' constant" << endl; throw; } // For now we only support ordered smart containers. // if (smart && ck != ck_ordered) { os << f << ":" << l << ":" << c << ": error: only ordered smart " << "containers are currently supported" << endl; throw operation_failed (); } t.set ("container-smart", smart); // Mark id column as not null. // t.set ("id-not-null", true); // Get the value type. // try { tree decl ( lookup_qualified_name ( inst, get_identifier ("value_type"), true, false)); if (decl == error_mark_node || TREE_CODE (decl) != TYPE_DECL) throw operation_failed (); tree type (TYPE_MAIN_VARIANT (TREE_TYPE (decl))); vt = &dynamic_cast (*unit.find (type)); // Find the hint. // for (tree ot (DECL_ORIGINAL_TYPE (decl)); ot != 0; ot = decl ? DECL_ORIGINAL_TYPE (decl) : 0) { if ((vh = unit.find_hint (ot))) break; decl = TYPE_NAME (ot); } } catch (operation_failed const&) { os << f << ":" << l << ":" << c << ": error: " << "container_traits specialization does not define the " << "value_type type" << endl; throw; } t.set ("value-tree-type", vt); t.set ("value-tree-hint", vh); // If we have a set container, automatically mark the value // column as not null. If we already have an explicit null for // this column, issue an error. // if (ck == ck_set) { if (t.count ("value-null")) { os << t.file () << ":" << t.line () << ":" << t.column () << ":" << " error: set container cannot contain null values" << endl; throw operation_failed (); } else t.set ("value-not-null", true); } // Issue a warning if we are relaxing null-ness in the // container type. // if (t.count ("value-null") && vt->count ("not-null")) { os << t.file () << ":" << t.line () << ":" << t.column () << ":" << " warning: container value declared null while its type " << "is declared not null" << endl; } // Get the index type for ordered containers. // if (ck == ck_ordered) { try { tree decl ( lookup_qualified_name ( inst, get_identifier ("index_type"), true, false)); if (decl == error_mark_node || TREE_CODE (decl) != TYPE_DECL) throw operation_failed (); tree type (TYPE_MAIN_VARIANT (TREE_TYPE (decl))); it = &dynamic_cast (*unit.find (type)); // Find the hint. // for (tree ot (DECL_ORIGINAL_TYPE (decl)); ot != 0; ot = decl ? DECL_ORIGINAL_TYPE (decl) : 0) { if ((ih = unit.find_hint (ot))) break; decl = TYPE_NAME (ot); } } catch (operation_failed const&) { os << f << ":" << l << ":" << c << ": error: " << "container_traits specialization does not define the " << "index_type type" << endl; throw; } t.set ("index-tree-type", it); t.set ("index-tree-hint", ih); t.set ("index-not-null", true); } // Get the key type for maps. // if (ck == ck_map || ck == ck_multimap) { try { tree decl ( lookup_qualified_name ( inst, get_identifier ("key_type"), true, false)); if (decl == error_mark_node || TREE_CODE (decl) != TYPE_DECL) throw operation_failed (); tree type (TYPE_MAIN_VARIANT (TREE_TYPE (decl))); kt = &dynamic_cast (*unit.find (type)); // Find the hint. // for (tree ot (DECL_ORIGINAL_TYPE (decl)); ot != 0; ot = decl ? DECL_ORIGINAL_TYPE (decl) : 0) { if ((kh = unit.find_hint (ot))) break; decl = TYPE_NAME (ot); } } catch (operation_failed const&) { os << f << ":" << l << ":" << c << ": error: " << "container_traits specialization does not define the " << "key_type type" << endl; throw; } t.set ("key-tree-type", kt); t.set ("key-tree-hint", kh); t.set ("key-not-null", true); } // Check if we are versioned. For now we are not allowing for // soft-add/delete in container keys (might be used in WHERE, // primary key). // { semantics::class_* comp (0); switch (ck) { case ck_ordered: { comp = composite (*vt); break; } case ck_map: case ck_multimap: { comp = composite (*kt); if (comp == 0 || column_count (*comp).soft == 0) { comp = composite (*vt); break; } error (m.location ()) << "map key type cannot have soft-" << "added/deleted data members" << endl; info (kt->location ()) << "key type is defined here" << endl; throw operation_failed (); } case ck_set: case ck_multiset: { comp = composite (*vt); if (comp == 0 || column_count (*comp).soft == 0) { comp = 0; break; } error (m.location ()) << "set value type cannot have soft-" << "added/deleted data members" << endl; info (vt->location ()) << "value type is defined here" << endl; throw operation_failed (); } } if (force_versioned || (comp != 0 && column_count (*comp).soft != 0)) t.set ("versioned", true); } } // Process member data. // m.set ("id-tree-type", &id_tree_type); // Has to be first to handle inverse. // process_container_value (*vt, m, "value", true); if (it != 0) process_container_value (*it, m, "index", false); if (kt != 0) process_container_value (*kt, m, "key", false); // A map cannot be an inverse container. // if (m.count ("value-inverse") && (ck == ck_map || ck == ck_multimap)) { os << m.file () << ":" << m.line () << ":" << m.column () << ":" << " error: inverse container cannot be a map" << endl; throw operation_failed (); } // If this is an inverse side of a bidirectional object relationship // and it is an ordered container, mark it as unordred since there is // no concept of order in this construct. // if (ck == ck_ordered && m.count ("value-inverse")) m.set ("unordered", true); // Issue an error if we have a non-inverse smart unordered container. // if (smart && ck == ck_ordered && unordered (m) && !m.count ("value-inverse")) { os << m.file () << ":" << m.line () << ":" << m.column () << ":" << " error: smart ordered container cannot be unordered" << endl; throw operation_failed (); } // Issue an error if we have a null column in a set container. // This can only happen if the value is declared as null in // the member. // if (ck == ck_set && m.count ("value-null")) { os << m.file () << ":" << m.line () << ":" << m.column () << ":" << " error: set container cannot contain null values" << endl; throw operation_failed (); } // Issue a warning if we are relaxing null-ness in the member. // if (m.count ("value-null") && (t.count ("value-not-null") || vt->count ("not-null"))) { os << m.file () << ":" << m.line () << ":" << m.column () << ":" << " warning: container value declared null while the container " << "type or value type declares it as not null" << endl; } return true; } // // Implementation details (c-tor, helpers). // data_member () { // Find the odb namespace. // tree odb = lookup_qualified_name ( global_namespace, get_identifier ("odb"), false, false); if (odb == error_mark_node) { os << unit.file () << ": error: unable to resolve odb namespace" << endl; throw operation_failed (); } // Find wrapper traits. // wrapper_traits_ = lookup_qualified_name ( odb, get_identifier ("wrapper_traits"), true, false); if (wrapper_traits_ == error_mark_node || !DECL_CLASS_TEMPLATE_P (wrapper_traits_)) { os << unit.file () << ": error: unable to resolve wrapper_traits " << "in the odb namespace" << endl; throw operation_failed (); } // Find pointer traits. // pointer_traits_ = lookup_qualified_name ( odb, get_identifier ("pointer_traits"), true, false); if (pointer_traits_ == error_mark_node || !DECL_CLASS_TEMPLATE_P (pointer_traits_)) { os << unit.file () << ": error: unable to resolve pointer_traits " << "in the odb namespace" << endl; throw operation_failed (); } // Find the access class. // tree access = lookup_qualified_name ( odb, get_identifier ("access"), true, false); if (access == error_mark_node) { os << unit.file () << ": error: unable to resolve access class" << "in the odb namespace" << endl; throw operation_failed (); } access = TREE_TYPE (access); // Find container_traits. // container_traits_ = lookup_qualified_name ( access, get_identifier ("container_traits"), true, false); if (container_traits_ == error_mark_node || !DECL_CLASS_TEMPLATE_P (container_traits_)) { os << unit.file () << ": error: unable to resolve container_traits " << "in the odb namespace" << endl; throw operation_failed (); } } static tree instantiate_template (tree t, tree arg) { tree args (make_tree_vec (1)); TREE_VEC_ELT (args, 0) = arg; // This step should succeed regardles of whether there is a // specialization for this type. // tree inst ( lookup_template_class (t, args, 0, 0, 0, tf_warning_or_error)); if (inst == error_mark_node) { // Diagnostics has already been issued by lookup_template_class. // throw operation_failed (); } inst = TYPE_MAIN_VARIANT (inst); // The instantiation may already be complete if it matches a // (complete) specialization or was used before. // if (!COMPLETE_TYPE_P (inst)) inst = instantiate_class_template (inst); // If we cannot instantiate this type, assume there is no suitable // specialization for it. // if (inst == error_mark_node || !COMPLETE_TYPE_P (inst)) return 0; return inst; } private: tree wrapper_traits_; tree pointer_traits_; tree container_traits_; cxx_string_lexer lex_; }; struct view_data_member: traversal::data_member, context { view_data_member (semantics::class_& c) : view_ (c), amap_ (c.get ("alias-map")), omap_ (c.get ("object-map")) {} virtual void traverse (semantics::data_member& m) { using semantics::data_member; if (transient (m)) return; semantics::type& t (utype (m)); if (semantics::class_* c = object_pointer (t)) { location const& l (m.location ()); if (lazy_pointer (t)) { error (l) << "lazy object pointer in view" << endl; throw operation_failed (); } // Find the corresponding associated object. First see if this // data member name matches any aliases. // view_alias_map::iterator i (amap_.find (m.name ())); if (i == amap_.end ()) i = amap_.find (public_name (m, false)); view_object* vo (0); if (i != amap_.end ()) { vo = i->second; if (vo->obj != c) // @@ Poly base/derived difference? { error (l) << "different pointed-to and associated objects" << endl; info (vo->loc) << "associated object is defined here" << endl; throw operation_failed (); } } else { // If there is no alias match, try the object type. // view_object_map::iterator i (omap_.find (c)); if (i == omap_.end ()) { error (l) << "unable to find associated object for object " << "pointer" << endl; info (l) << "use associated object alias as this data member " << "name" << endl; throw operation_failed (); } vo = i->second; } if (vo->ptr != 0) { location const& l2 (vo->ptr->location ()); error (l) << "associated object is already loaded via another " << "object pointer" << endl; info (l2) << "the other data member is defined here" << endl; info (l2) << "use associated object alias as this data member " << "name to load a different object" << endl; throw operation_failed (); } vo->ptr = &m; m.set ("view-object", vo); } } private: semantics::class_& view_; view_alias_map& amap_; view_object_map& omap_; }; // Figure out the "summary" added/deleted version for a composite // value type. // struct summary_version: object_members_base { summary_version (): av (0), dv (0), a_ (true), d_ (true) {} virtual void traverse_simple (semantics::data_member&) { if (a_) { if (unsigned long long v = added (member_path_)) { if (av == 0 || av < v) av = v; } else { av = 0; a_ = false; } } if (d_) { if (unsigned long long v = deleted (member_path_)) { if (dv == 0 || dv > v) dv = v; } else { dv = 0; d_ = false; } } } public: unsigned long long av; unsigned long long dv; bool a_; bool d_; }; struct class_: traversal::class_, context { class_ () : typedefs_ (true), std_string_ (0), std_string_hint_ (0), access_ (0) { *this >> defines_ >> *this; *this >> typedefs_ >> *this; member_names_ >> member_; // Resolve the std::string type node. // using semantics::scope; for (scope::names_iterator_pair ip (unit.find ("std")); ip.first != ip.second; ++ip.first) { if (scope* ns = dynamic_cast (&ip.first->named ())) { scope::names_iterator_pair jp (ns->find ("string")); if (jp.first != jp.second) { std_string_ = dynamic_cast ( &jp.first->named ()); std_string_hint_ = &*jp.first; break; } } } assert (std_string_ != 0); // No std::string? // Resolve odb::access, if any. // tree odb = lookup_qualified_name ( global_namespace, get_identifier ("odb"), false, false); if (odb != error_mark_node) { access_ = lookup_qualified_name ( odb, get_identifier ("access"), true, false); access_ = (access_ != error_mark_node ? TREE_TYPE (access_) : 0); } } virtual void traverse (type& c) { class_kind_type k (class_kind (c)); if (k == class_other) return; names (c); // Process nested classes. // Check if odb::access is a friend of this class. // c.set ("friend", access_ != 0 && is_friend (c.tree_node (), access_)); // Assign pointer. // if (k == class_object || k == class_view) assign_pointer (c); if (k == class_object) traverse_object_pre (c); else if (k == class_view) traverse_view_pre (c); names (c, member_names_); if (k == class_object) traverse_object_post (c); else if (k == class_view) traverse_view_post (c); else if (k == class_composite) traverse_composite_post (c); } // // Object. // virtual void traverse_object_pre (type& c) { semantics::class_* poly_root (polymorphic (c)); // Sections. // user_sections& uss (c.set ("user-sections", user_sections (c))); // Copy sections from reuse bases. For polymorphic classes, sections // are overridden. // if (poly_root == 0 || poly_root == &c) { for (type::inherits_iterator i (c.inherits_begin ()); i != c.inherits_end (); ++i) { type& b (i->base ()); if (object (b)) { user_sections& buss (b.get ("user-sections")); for (user_sections::iterator j (buss.begin ()); j != buss.end (); ++j) { // Don't copy the special version update section. // if (j->special == user_section::special_version) continue; uss.push_back (*j); uss.back ().object = &c; uss.back ().base = &*j; } } } } // Determine whether it is a session object. // if (!c.count ("session")) { // If this is a derived class in a polymorphic hierarchy, // then it should have the same session value as the root. // if (poly_root != 0 && poly_root != &c) c.set ("session", session (*poly_root)); else { // See if any of the namespaces containing this class specify // the session value. // bool found (false); for (semantics::scope* s (&class_scope (c));; s = &s->scope_ ()) { using semantics::namespace_; namespace_* ns (dynamic_cast (s)); if (ns == 0) continue; // Some other scope. if (ns->extension ()) ns = &ns->original (); if (ns->count ("session")) { c.set ("session", ns->get ("session")); found = true; break; } if (ns->global_scope ()) break; } // If still not found, then use the default value. // if (!found) c.set ("session", options.generate_session ()); } } if (session (c)) features.session_object = true; if (poly_root != 0) { using namespace semantics; using semantics::data_member; data_member* idm (id_member (*poly_root)); if (poly_root != &c) { // If we are a derived class in the polymorphic persistent // class hierarchy, then add a synthesized virtual pointer // member that points back to the root. // semantics::class_& base (polymorphic_base (c)); if (&base != poly_root) idm = &dynamic_cast (base.names_begin ()->named ()); path const& f (idm->file ()); size_t l (idm->line ()), col (idm->column ()); semantics::data_member& m ( unit.new_node (f, l, col, tree (0))); m.set ("virtual", true); // Make it the first member in the class. This is important: // we rely on the corrensponding foreign key to be first. // node_position np (c, c.names_end ()); unit.new_edge ( np, m, idm->name (), access::public_); // Use the raw pointer as this member's type. // if (!base.pointed_p ()) { // Create the pointer type in the graph. The pointer node // in GCC seems to always be present, even if not explicitly // used in the translation unit. // tree t (base.tree_node ()); tree ptr (TYPE_POINTER_TO (t)); assert (ptr != 0); ptr = TYPE_MAIN_VARIANT (ptr); pointer& p (unit.new_node (f, l, col, ptr)); unit.insert (ptr, p); unit.new_edge (p, base); assert (base.pointed_p ()); } unit.new_edge (m, base.pointed ().pointer ()); // Mark it as a special kind of id. // m.set ("id", true); m.set ("polymorphic-ref", true); } else { // If we are a root of the polymorphic persistent class hierarchy, // then add a synthesized virtual member for the discriminator. // Use the location of the polymorphic pragma as the location of // this member. // location_t loc (c.get ("polymorphic-location")); semantics::data_member& m ( unit.new_node ( path (LOCATION_FILE (loc)), LOCATION_LINE (loc), LOCATION_COLUMN (loc), tree (0))); m.set ("virtual", true); // Insert it after the id member (or first if this id comes // from reuse-base). // node_position np ( c, c.find (idm->named ())); unit.new_edge ( np, m, "typeid_", access::public_); belongs& edge (unit.new_edge (m, *std_string_)); edge.hint (*std_string_hint_); m.set ("readonly", true); m.set ("discriminator", true); c.set ("discriminator", &m); } } } virtual void traverse_object_post (type& c) { semantics::class_* poly_root (polymorphic (c)); bool poly_derived (poly_root != 0 && poly_root != &c); semantics::data_member* opt (optimistic (c)); // Figure out if we are versioned. We are versioned if we have // soft-added/deleted columns ourselves or our poly-base is // versioned. // if (force_versioned || column_count (c).soft != 0 || (poly_derived && polymorphic_base (c).count ("versioned"))) c.set ("versioned", true); // Sections. // user_sections& uss (c.get ("user-sections")); // See if we need to add a special fake section for version update. // if (c.count ("sectionable")) { uss.push_back ( user_section (*opt, c, uss.count (user_sections::count_total | user_sections::count_all | user_sections::count_special_version), user_section::load_lazy, user_section::update_manual, user_section::special_version)); // If we are a root of a polymorphic hierarchy and the version is in // a reuse-base, then we need to make sure that base is sectionable // and derive from its special version update section. // semantics::node& opt_base (opt->scope ()); if (poly_root == &c && &opt_base != &c) { if (!opt_base.count ("sectionable")) { location_t l (c.get ("sectionable-location")); error (l) << "reuse base class of a sectionable polymorphic " << "root class must be sectionable" << endl; info (opt_base.location ()) << "use '#pragma db object " << "sectionable' to make the base class of this hierarchy " << "sectionable" << endl; throw operation_failed (); } uss.back ().base = &opt_base.get ("user-sections").back (); } } // Calculate column counts for sections. // for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i) { column_count_type cc (column_count (c, &*i)); i->total = cc.total; i->inverse = cc.inverse; i->readonly = cc.readonly; // Figure out if we are versioned. We are versioned if we have // soft-added/deleted columns ourselves or our poly-base is // versioned. // if (force_versioned || cc.soft != 0 || (poly_derived && i->base != 0 && i->base->versioned)) i->versioned = true; if (size_t n = has_a (c, test_container, &*i)) { i->containers = true; i->versioned_containers = n != has_a (c, test_container | exclude_deleted | exclude_added | exclude_versioned, &*i); if ((n = has_a (c, test_readwrite_container, &*i))) { i->readwrite_containers = true; i->readwrite_versioned_containers = n != has_a (c, test_readwrite_container | exclude_deleted | exclude_added | exclude_versioned, &*i); } } } } // // View. // virtual void traverse_view_pre (type& c) { // Resolve referenced objects from tree nodes to semantic graph // nodes. Also populate maps and compute counts. // view_alias_map& amap (c.set ("alias-map", view_alias_map ())); view_object_map& omap (c.set ("object-map", view_object_map ())); size_t& obj_count (c.set ("object-count", size_t (0))); size_t& tbl_count (c.set ("table-count", size_t (0))); if (c.count ("objects")) { using semantics::class_; view_objects& objs (c.get ("objects")); for (view_objects::iterator i (objs.begin ()); i != objs.end (); ++i) { if (i->kind != view_object::object) { tbl_count++; continue; } else obj_count++; tree n (TYPE_MAIN_VARIANT (i->obj_node)); if (TREE_CODE (n) != RECORD_TYPE) { error (i->loc) << "name '" << i->obj_name << "' in db pragma " << "object does not name a class" << endl; throw operation_failed (); } class_& o (dynamic_cast (*unit.find (n))); if (!object (o)) { error (i->loc) << "name '" << i->obj_name << "' in db pragma " << "object does not name a persistent class" << endl; info (o.location ()) << "class '" << i->obj_name << "' is " << "defined here" << endl; throw operation_failed (); } i->obj = &o; i->ptr = 0; // Nothing yet. if (i->alias.empty ()) { if (!omap.insert (view_object_map::value_type (&o, &*i)).second) { error (i->loc) << "persistent class '" << i->obj_name << "' is used in the view more than once" << endl; error (omap[&o]->loc) << "previously used here" << endl; info (i->loc) << "use the alias clause to assign it a " << "different name" << endl; throw operation_failed (); } // Also add the bases of a polymorphic object. // class_* poly_root (polymorphic (o)); if (poly_root != 0 && poly_root != &o) { for (class_* b (&polymorphic_base (o));; b = &polymorphic_base (*b)) { if (!omap.insert (view_object_map::value_type (b, &*i)).second) { error (i->loc) << "base class '" << class_name (*b) << "' is used in the view more than once" << endl; error (omap[b]->loc) << "previously used here" << endl; info (i->loc) << "use the alias clause to assign it a " << "different name" << endl; throw operation_failed (); } if (b == poly_root) break; } } } else { if (!amap.insert ( view_alias_map::value_type (i->alias, &*i)).second) { error (i->loc) << "alias '" << i->alias << "' is used in " << "the view more than once" << endl; throw operation_failed (); } } } } } virtual void traverse_view_post (type& c) { // Handle data members. // { view_data_member t (c); traversal::names n (t); names (c, n); } // Figure out if we are versioned. Forced versioning is handled // in relational/processing. // if (column_count (c).soft != 0) c.set ("versioned", true); } // // Composite. // virtual void traverse_composite_post (type& c) { // Figure out if we are versioned. // if (force_versioned || column_count (c).soft != 0) { c.set ("versioned", true); // See if we are "summarily" added/deleted, that is, all the // columns are added/deleted. Note: this does not include // containers. // summary_version sv; sv.traverse (c); // Note: there are no locations. // if (sv.av != 0) c.set ("added", sv.av); if (sv.dv != 0) c.set ("deleted", sv.dv); } } // // Assign object/view pointer. // void assign_pointer (type& c) { location_t loc (0); // Pragma location, or 0 if not used. try { bool raw; string ptr; string const& type (class_fq_name (c)); tree decl (0); // Resolved template node. string decl_name; // User-provided template name. tree resolve_scope (0); // Scope in which we resolve names. class_pointer const* cp (0); bool cp_template (false); if (c.count ("pointer")) { cp = &c.get ("pointer"); } // If we are a derived type in polymorphic hierarchy, then use // our root's pointer type by default. // else if (semantics::class_* r = polymorphic (c)) { if (&c != r && r->count ("pointer-template")) cp = r->get ("pointer-template"); } if (cp != 0) { string const& p (cp->name); if (p == "*") { raw = true; ptr = type + "*"; cp_template = true; } else if (p[p.size () - 1] == '*') { raw = true; ptr = p; } else if (p.find ('<') != string::npos) { // Template-id. // raw = false; // Fair to assume not raw, though technically can be. ptr = p; decl_name.assign (p, 0, p.find ('<')); } else { // This is not a template-id. Resolve it and see if it is a // template or a type. // decl = resolve_name (p, cp->scope, true); gcc_tree_code_type tc (TREE_CODE (decl)); if (tc == TYPE_DECL) { raw = (TREE_CODE (TREE_TYPE (decl)) == POINTER_TYPE); ptr = p; // This can be a typedef'ed alias for a TR1 template-id. // if (tree ti = TYPE_TEMPLATE_INFO (TREE_TYPE (decl))) { decl = TI_TEMPLATE (ti); // DECL_TEMPLATE // Get to the most general template declaration. // while (DECL_TEMPLATE_INFO (decl)) decl = DECL_TI_TEMPLATE (decl); } else decl = 0; // Not a template. } else if (tc == TEMPLATE_DECL && DECL_CLASS_TEMPLATE_P (decl)) { raw = false; ptr = p + "< " + type + " >"; decl_name = p; cp_template = true; } else { error (cp->loc) << "name '" << p << "' specified with db pragma pointer " << "does not name a type or a template" << endl; throw operation_failed (); } } // Resolve scope is the scope of the pragma. // resolve_scope = cp->scope; loc = cp->loc; } else { // See if any of the namespaces containing this class specify // a pointer. // for (semantics::scope* s (&class_scope (c));; s = &s->scope_ ()) { using semantics::namespace_; namespace_* ns (dynamic_cast (s)); if (ns == 0) continue; // Some other scope. if (ns->extension ()) ns = &ns->original (); if (!ns->count ("pointer")) { if (ns->global_scope ()) break; else continue; } cp = &ns->get ("pointer"); string const& p (cp->name); // Namespace-specified pointer can only be '*' or are template. // if (p == "*") { raw = true; ptr = type + "*"; } else if (p[p.size () - 1] == '*') { error (cp->loc) << "name '" << p << "' specified with db pragma pointer " << "at namespace level cannot be a raw pointer" << endl; } else if (p.find ('<') != string::npos) { error (cp->loc) << "name '" << p << "' specified with db pragma pointer " << "at namespace level cannot be a template-id" << endl; } else { // Resolve this name and make sure it is a template. // decl = resolve_name (p, cp->scope, true); gcc_tree_code_type tc (TREE_CODE (decl)); if (tc == TEMPLATE_DECL && DECL_CLASS_TEMPLATE_P (decl)) { raw = false; ptr = p + "< " + type + " >"; decl_name = p; } else { error (cp->loc) << "name '" << p << "' specified with db pragma pointer " << "does not name a template" << endl; } } if (ptr.empty ()) throw operation_failed (); cp_template = true; // Resolve scope is the scope of the pragma. // resolve_scope = cp->scope; loc = cp->loc; break; } // Use the default pointer. // if (ptr.empty ()) { string const& p (options.default_pointer ()); if (p == "*") { raw = true; ptr = type + "*"; } else { raw = false; ptr = p + "< " + type + " >"; decl_name = p; } // Resolve scope is the scope of the class. // resolve_scope = class_scope (c).tree_node (); } } // If this class is a root of a polymorphic hierarchy, then cache // the pointer template so that we can use it for derived classes. // if (cp != 0 && cp_template && polymorphic (c) == &c) c.set ("pointer-template", cp); // Check if we are using TR1. // if (decl != 0 || !decl_name.empty ()) { bool& tr1 (features.tr1_pointer); bool& boost (features.boost_pointer); // First check the user-supplied name. // tr1 = tr1 || decl_name.compare (0, 8, "std::tr1") == 0 || decl_name.compare (0, 10, "::std::tr1") == 0; // If there was no match, also resolve the name since it can be // a using-declaration for a TR1 template. // if (!tr1) { if (decl == 0) decl = resolve_name (decl_name, resolve_scope, false); if (TREE_CODE (decl) != TEMPLATE_DECL || ! DECL_CLASS_TEMPLATE_P (decl)) { // This is only checked for the --default-pointer option. // error (c.file (), c.line (), c.column ()) << "name '" << decl_name << "' specified with the " << "--default-pointer option does not name a class " << "template" << endl; throw operation_failed (); } string n (decl_as_string (decl, TFF_PLAIN_IDENTIFIER)); // In case of a boost TR1 implementation, we cannot distinguish // between the boost:: and std::tr1:: usage since the latter is // just a using-declaration for the former. // tr1 = tr1 || n.compare (0, 8, "std::tr1") == 0 || n.compare (0, 10, "::std::tr1") == 0; boost = boost || n.compare (0, 17, "boost::shared_ptr") == 0 || n.compare (0, 19, "::boost::shared_ptr") == 0; } } // Fully-qualify all the unqualified components of the name. // try { lex_.start (ptr); ptr.clear (); string t; bool punc (false); bool scoped (false); for (cpp_ttype tt (lex_.next (t)); tt != CPP_EOF; tt = lex_.next (t)) { if (punc && tt > CPP_LAST_PUNCTUATOR) ptr += ' '; punc = false; switch (static_cast (tt)) { case CPP_LESS: { ptr += "< "; break; } case CPP_GREATER: { ptr += " >"; break; } case CPP_COMMA: { ptr += ", "; break; } case CPP_NAME: { // If the name was not preceeded with '::', look it // up in the pragmas's scope and add the qualifer. // if (!scoped) { tree decl (resolve_name (t, resolve_scope, false)); tree scope (CP_DECL_CONTEXT (decl)); // If this is an inline namespace, skip it until we get // to the non-inline one. // while (scope != global_namespace) { tree prev (CP_DECL_CONTEXT (scope)); if (!is_associated_namespace (prev, scope)) break; scope = prev; } if (scope != global_namespace) { ptr += "::"; ptr += decl_as_string (scope, TFF_PLAIN_IDENTIFIER); } ptr += "::"; } ptr += t; punc = true; break; } case CPP_KEYWORD: case CPP_NUMBER: { ptr += t; punc = true; break; } default: { ptr += t; break; } } scoped = (tt == CPP_SCOPE); } } catch (cxx_lexer::invalid_input const&) { throw operation_failed (); } c.set ("object-pointer", ptr); c.set ("object-pointer-raw", raw); } catch (invalid_name const& ex) { if (loc != 0) error (loc) << "name '" << ex.name () << "' specified with db pragma " << "pointer is invalid" << endl; else error (c.file (), c.line (), c.column ()) << "name '" << ex.name () << "' specified with the " << "--default-pointer option is invalid" << endl; throw operation_failed (); } catch (unable_to_resolve const& ex) { if (loc != 0) error (loc) << "unable to resolve name '" << ex.name () << "' specified " << "with db pragma pointer" << endl; else error (c.file (), c.line (), c.column ()) << "unable to resolve name '" << ex.name () << "' specified " << "with the --default-pointer option" << endl; throw operation_failed (); } } private: struct invalid_name { invalid_name (string const& n): name_ (n) {} string const& name () const {return name_;} private: string name_; }; typedef lookup::unable_to_resolve unable_to_resolve; tree resolve_name (string const& qn, tree scope, bool is_type) { try { string tl; tree tn; cpp_ttype tt, ptt; nlex_.start (qn); tt = nlex_.next (tl, &tn); string name; return lookup::resolve_scoped_name ( nlex_, tt, tl, tn, ptt, scope, name, is_type); } catch (cxx_lexer::invalid_input const&) { throw invalid_name (qn); } catch (lookup::invalid_name const&) { throw invalid_name (qn); } } private: traversal::defines defines_; typedefs typedefs_; data_member member_; traversal::names member_names_; cxx_string_lexer lex_; cxx_string_lexer nlex_; // Nested lexer. semantics::type* std_string_; semantics::names* std_string_hint_; tree access_; // odb::access node. }; } void process (options const& ops, features& f, semantics::unit& unit, semantics::path const&) { try { auto_ptr ctx (create_context (cerr, unit, ops, f, 0)); // Common processing. // { traversal::unit unit; traversal::defines unit_defines; typedefs unit_typedefs (true); traversal::namespace_ ns; class_ c; unit >> unit_defines >> ns; unit_defines >> c; unit >> unit_typedefs >> c; traversal::defines ns_defines; typedefs ns_typedefs (true); ns >> ns_defines >> ns; ns_defines >> c; ns >> ns_typedefs >> c; unit.dispatch (ctx->unit); } // Database-specific processing. // switch (ops.database ()[0]) { case database::common: { break; } case database::mssql: case database::mysql: case database::oracle: case database::pgsql: case database::sqlite: { relational::process (); break; } } } catch (operation_failed const&) { // Processing failed. Diagnostics has already been issued. // throw processor_failed (); } }