File: repository.cpp

package info (click to toggle)
cppgir 2.0%2Bgit20240928.c8bb1c6%2Breally2.0-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 1,228 kB
  • sloc: cpp: 14,307; ansic: 339; makefile: 11; sh: 9
file content (329 lines) | stat: -rw-r--r-- 10,801 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
#include "repository.hpp"

#include "genutils.hpp"

#include <regex>
#include <set>
#include <unordered_map>

static std::set<std::string> basic_types{"gchar", "guchar", "gshort", "gushort",
    "gint", "guint", "glong", "gulong", "gssize", "gsize", "gintptr",
    "guintptr", "gpointer", "gconstpointer", "gboolean", "gint8", "gint16",
    "guint8", "guint16", "gint32", "guint32", "gint64", "guint64", "gfloat",
    "gdouble", "GType", "utf8", "filename", "gi::cstring", "gunichar",
    "dev_t", "gid_t", "pid_t", "socklen_t", "uid_t"};

class RepositoryPriv : public Repository
{
public:
  // holds info collected from GIRs indexed by entry's (qualified) name
  // (which should be unique within a namespace)
  std::unordered_map<key_type, mapped_type> index;
  // active ns
  std::string ns;
  // ODR check
  mutable std::unordered_map<std::string, std::string> type_index;

  RepositoryPriv()
  {
    // make basic types known
    for (auto &&girname : basic_types) {
      auto cpptype = girname;
      auto ctype = girname;
      int flags = 0;

      flags |= TYPE_BASIC;
      if (girname == "utf8" || girname == "filename" ||
          girname == "gi::cstring") {
        flags |= TYPE_CLASS;
        ctype = "char*";
        cpptype = "gi::cstring";
      } else {
        flags |= TYPE_VALUE;
        if (girname == "gboolean")
          cpptype = "bool";
      }
      auto argtype =
          (girname.find("pointer") != girname.npos) ? "void*" : ctype;
      index.emplace(
          girname, mapped_type{nullptr, std::make_unique<TypeInfo>(girname,
                                            cpptype, ctype, argtype, flags)});
    }
    // void case
    index.emplace(GIR_VOID,
        mapped_type{nullptr, std::make_unique<TypeInfo>(GIR_VOID, CPP_VOID,
                                 EMPTY, CPP_VOID, TYPE_BASIC)});

    // other special and pre-defined cases
    std::vector<std::tuple<std::string, std::string, std::string, int>> pre{
        {std::make_tuple("GLib.List", "GList", "GList", TYPE_LIST)},
        {std::make_tuple("GLib.SList", "GSList", "GSList", TYPE_LIST)},
        {std::make_tuple(
            "GLib.HashTable", "GHashTable", "GHashTable", TYPE_MAP)},
        {std::make_tuple("GObject.Value", "GObject::Value", "GValue",
            TYPE_CLASS | TYPE_BOXED)},
        {std::make_tuple(
            "GLib.Error", "GLib::Error", "GError", TYPE_CLASS | TYPE_BOXED)},
        // avoid namespace mishap
        {std::make_tuple("GObject.Object", "GObject::Object", "GObject",
            TYPE_CLASS | TYPE_OBJECT)},
        // pretend is like object
        {std::make_tuple("GObject.ParamSpec", "GObject::ParamSpec",
            "GParamSpec", TYPE_CLASS)}};
    for (auto &&e : pre) {
      // qualify argtype to avoid name conflicts
      auto ti = std::make_unique<TypeInfo>(std::get<0>(e), std::get<1>(e),
          std::get<2>(e), GI_SCOPE + std::get<2>(e) + "*",
          std::get<3>(e) | TYPE_PREDEFINED);
      auto girname = ti->girname;
      index.emplace(girname, mapped_type{nullptr, std::move(ti)});
    }
    auto ti =
        std::make_unique<TypeInfo>("GLib.DestroyNotify", "GLib::DestroyNotify",
            GDESTROYNOTIFY, GDESTROYNOTIFY, TYPE_CALLBACK | TYPE_PREDEFINED);
    auto girname = ti->girname;
    index.emplace(girname, mapped_type{nullptr, std::move(ti)});
  }
};

// C++ on the outside, C trick on the inside
static const RepositoryPriv &
get_self(const Repository *t)
{
  return *static_cast<const RepositoryPriv *>(t);
}
static RepositoryPriv &
get_self(Repository *t)
{
  return *static_cast<RepositoryPriv *>(t);
}

// set namespace used for unqualified girname
void
Repository::set_ns(const std::string _ns)
{
  auto &self = get_self(this);
  self.ns = _ns;
}

// qualify girname, optionally wrt relative base
std::string
Repository::qualify(const std::string &girname, const std::string &base) const
{
  auto &&self = get_self(this);
  if (girname.find('.') == girname.npos) {
    auto bns = self.ns;
    if (base.size()) {
      auto pos = base.find('.');
      if (pos == base.npos)
        return girname;
      bns = base.substr(0, pos);
    }
    return bns + '.' + girname;
  }
  return girname;
}

void
Repository::add(const key_type &girname, const mapped_type::tree_type &n)
{
  auto &&self = get_self(this);
  auto qualified = qualify(girname);
  auto it = self.index.find(qualified);
  bool registered = false;

  if (girname.empty()) {
    // should not make it here
    assert(false);
  } else if (it != self.index.end()) {
    auto &e = it->second;
    // merge in tree data for predefined
    if (e.info && (e.info->flags & TYPE_PREDEFINED)) {
      e.tree = std::make_unique<mapped_type::tree_type>(n);
    } else {
      throw std::runtime_error(
          fmt::format("duplicate name {} [{}]", girname, qualified));
    }
  } else {
    // examine node/type
    // NOTE not every entry/node represents a type (and therefore has !=
    // flags) consider e.g. a constant or function
    int flags = 0;
    auto &el = n.first;
    auto &node = n.second;
    auto ctype = get_attribute(node, AT_CTYPE, "");

    // base identification
    if (el == EL_RECORD) {
      flags |= TYPE_CLASS | TYPE_BOXED;
    } else if (el == EL_OBJECT || el == EL_INTERFACE) {
      flags |= TYPE_CLASS | TYPE_OBJECT;
    } else if (el == EL_ENUM || el == EL_FLAGS) {
      flags |= TYPE_ENUM | TYPE_VALUE;
    } else if (el == EL_ALIAS) {
      // adopt flags of typedef'ed one
      // need not be a primitive one (e.g. GtkAllocation = GdkRectangle)
      auto btype = node.get(EL_TYPE + '.' + PT_ATTR + '.' + AT_NAME, "");
      auto ti = lookup(btype);
      flags = ti && ti->info ? ti->info->flags : 0;
      // also consider as sort-of predefined (at least if we know about
      // it)
      if (flags)
        flags |= TYPE_TYPEDEF;
    } else if (el == EL_CALLBACK) {
      flags |= TYPE_CALLBACK;
    }
    if (flags) {
      // check if registered
      auto gettype = get_attribute(n.second, AT_GLIB_GET_TYPE, "");
      if (gettype.size())
        registered = true;
    }

    bool keep_node = false;
    bool keep_info = flags != 0;
    if (flags & TYPE_CLASS) {
      keep_node = true;
      // additional checks
      auto class_struct = get_attribute(node, AT_GLIB_IS_TYPE_STRUCT_FOR, "");
      if (class_struct.size()) {
        // is not a valid type, but keep some info around for later
        // lookup
        flags = 0;
        keep_info = true;
      } else if (flags & TYPE_BOXED) {
        keep_node = false;
      }
      // e.g. GParamSpec (several), variant
      auto fundamental = get_attribute<int>(node, AT_GLIB_FUNDAMENTAL, 0);
      // GVariant is marked this way
      auto gtype = get_attribute(node, AT_GLIB_GET_TYPE, "");
      if (fundamental || gtype == "intern") {
        flags = -1;
      }
    }

    {
      // filter some more
      auto disguised = get_attribute<int>(node, AT_DISGUISED, 0);
      // also never mind if it is designed opaque/disguised
      // lots of private stuff, but also possibly type structs
      // (some at least, in case of opaque struct)
      if (disguised)
        flags = -1;
      auto deprecated = get_attribute<int>(node, AT_DEPRECATED, 0);
      // cairo is notable foreign example
      // structs are typically opaque, with custom _create, _copy,
      // _destroy
      // TODO for now filter all, perhaps not so later
      // (and then only filter by ignore and allow custom declaration of
      // above functions in the boxed system)
      auto foreign = get_attribute<int>(node, AT_FOREIGN, 0);
      if (deprecated || (foreign && !registered))
        flags = -1;
    }

    // special fundamental case
    // partly prepared, mostly generated
    if (qualified == GIR_GVARIANT) {
      keep_node = false;
      keep_info = true;
      flags = TYPE_CLASS;
    }

    // always check
    auto movedto = get_attribute(node, AT_MOVED_TO, "");
    if (!movedto.empty())
      flags = -1;

    if (flags == -1) {
      logger(Log::DEBUG, "ignoring GIR {} {}", el, qualified);
    } else {
      logger(Log::LOG, "registering GIR {} {} {}", el, qualified, flags);

      // convert GIR qualification to namespace qualification
      static const std::regex re_qualify("\\.", std::regex::optimize);
      // enum cpptype is unreserve'd in type definition
      auto cpptype = std::regex_replace(
          (flags & TYPE_ENUM) ? qualify(unreserve(girname)) : qualified,
          re_qualify, "::");
      assert(!(flags & TYPE_CLASS) || is_qualified(cpptype));

      // always top-level qualify ctype to avoid ns ambiguity
      if (ctype.size())
        ctype = GI_SCOPE + ctype;
      auto argtype = ctype;
      if ((flags & TYPE_CLASS) && argtype.size())
        argtype += GI_PTR;

      std::unique_ptr<mapped_type::tree_type> xmlinfo;
      // node only needed for class type
      // (only a small part of node is needed later on,
      // but let's simply keep all of it)
      if (keep_node)
        xmlinfo = std::make_unique<mapped_type::tree_type>(n);

      mapped_type entry = {
          std::move(xmlinfo), keep_info ? std::make_unique<TypeInfo>(qualified,
                                              cpptype, ctype, argtype, flags)
                                        : nullptr};
      it = std::get<0>(
          self.index.emplace(std::move(qualified), std::move(entry)));
    }
  }
}

void
Repository::discard(const key_type &girname)
{
  auto &&self = get_self(this);
  auto qualified = qualify(girname);
  logger(Log::LOG, "discarding girname {}", qualified);
  if (!self.index.erase(qualified))
    logger(Log::WARNING, "discarded unknown girname {}", qualified);
}

const Repository::mapped_type::tree_type &
Repository::tree(const std::string &girname) const
{
  auto &&self = get_self(this);
  auto &index = self.index;
  // only used by class types
  auto it = index.find(qualify(girname));
  if (it == index.end() || !it->second.tree)
    throw std::runtime_error("no node info for " + girname);
  return *it->second.tree;
}

const Repository::mapped_type *
Repository::lookup(const std::string &girname) const
{
  auto &&self = get_self(this);
  auto &index = self.index;
  // also consider non-qualified for basic types
  auto it = index.find(girname);
  if (it == index.end())
    it = index.find(qualify(girname));
  if (it != index.end())
    return &it->second;
  return nullptr;
}

std::string
Repository::check_odr(const std::string &cpptype, const std::string &ctype)
{
  if (!ctype.empty()) {
    auto &&self = get_self(this);
    auto ret = self.type_index.insert({ctype, cpptype});
    if (!ret.second && ret.first->second != cpptype) {
      return ret.first->second;
    }
  }
  return {};
}

std::shared_ptr<Repository>
Repository::new_()
{
  return std::make_shared<RepositoryPriv>();
}