repository.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  1. #include "repository.hpp"
  2. #include "genutils.hpp"
  3. #include <regex>
  4. #include <set>
  5. #include <unordered_map>
  6. static std::set<std::string> basic_types{"gchar", "guchar", "gshort", "gushort",
  7. "gint", "guint", "glong", "gulong", "gssize", "gsize", "gintptr",
  8. "guintptr", "gpointer", "gconstpointer", "gboolean", "gint8", "gint16",
  9. "guint8", "guint16", "gint32", "guint32", "gint64", "guint64", "gfloat",
  10. "gdouble", "GType", "utf8", "filename", "gi::cstring", "gunichar",
  11. "dev_t", "gid_t", "pid_t", "socklen_t", "uid_t"};
  12. class RepositoryPriv : public Repository
  13. {
  14. public:
  15. // holds info collected from GIRs indexed by entry's (qualified) name
  16. // (which should be unique within a namespace)
  17. std::unordered_map<key_type, mapped_type> index;
  18. // active ns
  19. std::string ns;
  20. // ODR check
  21. mutable std::unordered_map<std::string, std::string> type_index;
  22. RepositoryPriv()
  23. {
  24. // make basic types known
  25. for (auto &&girname : basic_types) {
  26. auto cpptype = girname;
  27. auto ctype = girname;
  28. int flags = 0;
  29. flags |= TYPE_BASIC;
  30. if (girname == "utf8" || girname == "filename" ||
  31. girname == "gi::cstring") {
  32. flags |= TYPE_CLASS;
  33. ctype = "char*";
  34. cpptype = "gi::cstring";
  35. } else {
  36. flags |= TYPE_VALUE;
  37. if (girname == "gboolean")
  38. cpptype = "bool";
  39. }
  40. auto argtype =
  41. (girname.find("pointer") != girname.npos) ? "void*" : ctype;
  42. index.emplace(
  43. girname, mapped_type{nullptr, std::make_unique<TypeInfo>(girname,
  44. cpptype, ctype, argtype, flags)});
  45. }
  46. // void case
  47. index.emplace(GIR_VOID,
  48. mapped_type{nullptr, std::make_unique<TypeInfo>(GIR_VOID, CPP_VOID,
  49. EMPTY, CPP_VOID, TYPE_BASIC)});
  50. // other special and pre-defined cases
  51. std::vector<std::tuple<std::string, std::string, std::string, int>> pre{
  52. {std::make_tuple("GLib.List", "GList", "GList", TYPE_LIST)},
  53. {std::make_tuple("GLib.SList", "GSList", "GSList", TYPE_LIST)},
  54. {std::make_tuple(
  55. "GLib.HashTable", "GHashTable", "GHashTable", TYPE_MAP)},
  56. {std::make_tuple("GObject.Value", "GObject::Value", "GValue",
  57. TYPE_CLASS | TYPE_BOXED)},
  58. {std::make_tuple(
  59. "GLib.Error", "GLib::Error", "GError", TYPE_CLASS | TYPE_BOXED)},
  60. // avoid namespace mishap
  61. {std::make_tuple("GObject.Object", "GObject::Object", "GObject",
  62. TYPE_CLASS | TYPE_OBJECT)},
  63. // pretend is like object
  64. {std::make_tuple("GObject.ParamSpec", "GObject::ParamSpec",
  65. "GParamSpec", TYPE_CLASS)}};
  66. for (auto &&e : pre) {
  67. // qualify argtype to avoid name conflicts
  68. auto ti = std::make_unique<TypeInfo>(std::get<0>(e), std::get<1>(e),
  69. std::get<2>(e), GI_SCOPE + std::get<2>(e) + "*",
  70. std::get<3>(e) | TYPE_PREDEFINED);
  71. auto girname = ti->girname;
  72. index.emplace(girname, mapped_type{nullptr, std::move(ti)});
  73. }
  74. auto ti =
  75. std::make_unique<TypeInfo>("GLib.DestroyNotify", "GLib::DestroyNotify",
  76. GDESTROYNOTIFY, GDESTROYNOTIFY, TYPE_CALLBACK | TYPE_PREDEFINED);
  77. auto girname = ti->girname;
  78. index.emplace(girname, mapped_type{nullptr, std::move(ti)});
  79. }
  80. };
  81. // C++ on the outside, C trick on the inside
  82. static const RepositoryPriv &
  83. get_self(const Repository *t)
  84. {
  85. return *static_cast<const RepositoryPriv *>(t);
  86. }
  87. static RepositoryPriv &
  88. get_self(Repository *t)
  89. {
  90. return *static_cast<RepositoryPriv *>(t);
  91. }
  92. // set namespace used for unqualified girname
  93. void
  94. Repository::set_ns(const std::string _ns)
  95. {
  96. auto &self = get_self(this);
  97. self.ns = _ns;
  98. }
  99. // qualify girname, optionally wrt relative base
  100. std::string
  101. Repository::qualify(const std::string &girname, const std::string &base) const
  102. {
  103. auto &&self = get_self(this);
  104. if (girname.find('.') == girname.npos) {
  105. auto bns = self.ns;
  106. if (base.size()) {
  107. auto pos = base.find('.');
  108. if (pos == base.npos)
  109. return girname;
  110. bns = base.substr(0, pos);
  111. }
  112. return bns + '.' + girname;
  113. }
  114. return girname;
  115. }
  116. void
  117. Repository::add(const key_type &girname, const mapped_type::tree_type &n)
  118. {
  119. auto &&self = get_self(this);
  120. auto qualified = qualify(girname);
  121. auto it = self.index.find(qualified);
  122. bool registered = false;
  123. if (girname.empty()) {
  124. // should not make it here
  125. assert(false);
  126. } else if (it != self.index.end()) {
  127. auto &e = it->second;
  128. // merge in tree data for predefined
  129. if (e.info && (e.info->flags & TYPE_PREDEFINED)) {
  130. e.tree = std::make_unique<mapped_type::tree_type>(n);
  131. } else {
  132. throw std::runtime_error(
  133. fmt::format("duplicate name {} [{}]", girname, qualified));
  134. }
  135. } else {
  136. // examine node/type
  137. // NOTE not every entry/node represents a type (and therefore has !=
  138. // flags) consider e.g. a constant or function
  139. int flags = 0;
  140. auto &el = n.first;
  141. auto &node = n.second;
  142. auto ctype = get_attribute(node, AT_CTYPE, "");
  143. // base identification
  144. if (el == EL_RECORD) {
  145. flags |= TYPE_CLASS | TYPE_BOXED;
  146. } else if (el == EL_OBJECT || el == EL_INTERFACE) {
  147. flags |= TYPE_CLASS | TYPE_OBJECT;
  148. } else if (el == EL_ENUM || el == EL_FLAGS) {
  149. flags |= TYPE_ENUM | TYPE_VALUE;
  150. } else if (el == EL_ALIAS) {
  151. // adopt flags of typedef'ed one
  152. // need not be a primitive one (e.g. GtkAllocation = GdkRectangle)
  153. auto btype = node.get(EL_TYPE + '.' + PT_ATTR + '.' + AT_NAME, "");
  154. auto ti = lookup(btype);
  155. flags = ti && ti->info ? ti->info->flags : 0;
  156. // also consider as sort-of predefined (at least if we know about
  157. // it)
  158. if (flags)
  159. flags |= TYPE_TYPEDEF;
  160. } else if (el == EL_CALLBACK) {
  161. flags |= TYPE_CALLBACK;
  162. }
  163. if (flags) {
  164. // check if registered
  165. auto gettype = get_attribute(n.second, AT_GLIB_GET_TYPE, "");
  166. if (gettype.size())
  167. registered = true;
  168. }
  169. bool keep_node = false;
  170. bool keep_info = flags != 0;
  171. if (flags & TYPE_CLASS) {
  172. keep_node = true;
  173. // additional checks
  174. auto class_struct = get_attribute(node, AT_GLIB_IS_TYPE_STRUCT_FOR, "");
  175. if (class_struct.size()) {
  176. // is not a valid type, but keep some info around for later
  177. // lookup
  178. flags = 0;
  179. keep_info = true;
  180. } else if (flags & TYPE_BOXED) {
  181. keep_node = false;
  182. }
  183. // e.g. GParamSpec (several), variant
  184. auto fundamental = get_attribute<int>(node, AT_GLIB_FUNDAMENTAL, 0);
  185. // GVariant is marked this way
  186. auto gtype = get_attribute(node, AT_GLIB_GET_TYPE, "");
  187. if (fundamental || gtype == "intern") {
  188. flags = -1;
  189. }
  190. }
  191. {
  192. // filter some more
  193. auto disguised = get_attribute<int>(node, AT_DISGUISED, 0);
  194. // also never mind if it is designed opaque/disguised
  195. // lots of private stuff, but also possibly type structs
  196. // (some at least, in case of opaque struct)
  197. if (disguised)
  198. flags = -1;
  199. // cairo is notable foreign example
  200. // structs are typically opaque, with custom _create, _copy,
  201. // _destroy
  202. // TODO for now filter all, perhaps not so later
  203. // (and then only filter by ignore and allow custom declaration of
  204. // above functions in the boxed system)
  205. auto foreign = get_attribute<int>(node, AT_FOREIGN, 0);
  206. if (foreign && !registered)
  207. flags = -1;
  208. }
  209. // special fundamental case
  210. // partly prepared, mostly generated
  211. if (qualified == GIR_GVARIANT) {
  212. keep_node = false;
  213. keep_info = true;
  214. flags = TYPE_CLASS;
  215. }
  216. // always check
  217. auto movedto = get_attribute(node, AT_MOVED_TO, "");
  218. if (!movedto.empty())
  219. flags = -1;
  220. if (flags == -1) {
  221. logger(Log::DEBUG, "ignoring GIR {} {}", el, qualified);
  222. } else {
  223. logger(Log::LOG, "registering GIR {} {} {}", el, qualified, flags);
  224. // convert GIR qualification to namespace qualification
  225. static const std::regex re_qualify("\\.", std::regex::optimize);
  226. // enum cpptype is unreserve'd in type definition
  227. auto cpptype = std::regex_replace(
  228. (flags & TYPE_ENUM) ? qualify(unreserve(girname)) : qualified,
  229. re_qualify, "::");
  230. assert(!(flags & TYPE_CLASS) || is_qualified(cpptype));
  231. // always top-level qualify ctype to avoid ns ambiguity
  232. if (ctype.size())
  233. ctype = GI_SCOPE + ctype;
  234. auto argtype = ctype;
  235. if ((flags & TYPE_CLASS) && argtype.size())
  236. argtype += GI_PTR;
  237. std::unique_ptr<mapped_type::tree_type> xmlinfo;
  238. // node only needed for class type
  239. // (only a small part of node is needed later on,
  240. // but let's simply keep all of it)
  241. if (keep_node)
  242. xmlinfo = std::make_unique<mapped_type::tree_type>(n);
  243. mapped_type entry = {
  244. std::move(xmlinfo), keep_info ? std::make_unique<TypeInfo>(qualified,
  245. cpptype, ctype, argtype, flags)
  246. : nullptr};
  247. it = std::get<0>(
  248. self.index.emplace(std::move(qualified), std::move(entry)));
  249. }
  250. }
  251. }
  252. void
  253. Repository::discard(const key_type &girname)
  254. {
  255. auto &&self = get_self(this);
  256. auto qualified = qualify(girname);
  257. logger(Log::LOG, "discarding girname {}", qualified);
  258. if (!self.index.erase(qualified))
  259. logger(Log::WARNING, "discarded unknown girname {}", qualified);
  260. }
  261. const Repository::mapped_type::tree_type &
  262. Repository::tree(const std::string &girname) const
  263. {
  264. auto &&self = get_self(this);
  265. auto &index = self.index;
  266. // only used by class types
  267. auto it = index.find(qualify(girname));
  268. if (it == index.end() || !it->second.tree)
  269. throw std::runtime_error("no node info for " + girname);
  270. return *it->second.tree;
  271. }
  272. const Repository::mapped_type *
  273. Repository::lookup(const std::string &girname) const
  274. {
  275. auto &&self = get_self(this);
  276. auto &index = self.index;
  277. // also consider non-qualified for basic types
  278. auto it = index.find(girname);
  279. if (it == index.end())
  280. it = index.find(qualify(girname));
  281. if (it != index.end())
  282. return &it->second;
  283. return nullptr;
  284. }
  285. std::string
  286. Repository::check_odr(const std::string &cpptype, const std::string &ctype)
  287. {
  288. if (!ctype.empty()) {
  289. auto &&self = get_self(this);
  290. auto ret = self.type_index.insert({ctype, cpptype});
  291. if (!ret.second && ret.first->second != cpptype) {
  292. return ret.first->second;
  293. }
  294. }
  295. return {};
  296. }
  297. std::shared_ptr<Repository>
  298. Repository::new_()
  299. {
  300. return std::make_shared<RepositoryPriv>();
  301. }