File: genbase.cpp

package info (click to toggle)
cppgir 2.0%2Bgit20250629.2a7d9ce-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 1,220 kB
  • sloc: cpp: 16,451; ansic: 355; python: 86; makefile: 13; sh: 9
file content (222 lines) | stat: -rw-r--r-- 6,982 bytes parent folder | download
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
#include "genbase.hpp"

#include <map>

static std::string
qualify(const std::string &ns, const std::string &cpptype, int flags)
{
  if (cpptype.find(':') == cpptype.npos && !cpptype.empty() &&
      !(flags & TYPE_BASIC))
    return ns + "::" + cpptype;
  return cpptype;
}

GeneratorBase::GeneratorBase(GeneratorContext &_ctx, const std::string _ns)
    : ctx(_ctx), ns(_ns)
{}

std::string
GeneratorBase::qualify(const std::string &cpptype, int flags) const
{
  return ::qualify(ns, cpptype, flags);
}

void
GeneratorBase::parse_typeinfo(
    const std::string &girname, TypeInfo &result) const
{
  auto ti = ctx.repo.lookup(girname);
  if (ti && ti->info)
    result = *ti->info;
}

GeneratorBase::ArgInfo
GeneratorBase::parse_arginfo(const pt::ptree &node, ArgInfo *routput) const
{
  ArgInfo lresult;
  ArgInfo &result = routput ? *routput : lresult;

  const pt::ptree *pntype{};
  std::string kind;
  // try most likely in turn
  for (auto &&v : {EL_TYPE, EL_ARRAY, EL_VARARGS}) {
    const auto &ntypeo = node.get_child_optional(v);
    if (ntypeo.is_initialized()) {
      kind = v;
      pntype = &ntypeo.get();
    }
  }
  if (kind.empty() || !pntype)
    throw skip("type info not found");
  if (kind == EL_VARARGS)
    throw skip("varargs not supported", skip::OK);
  auto &ntype = *pntype;
  bool discard_element = false;
  // no name for array
  if (kind == EL_ARRAY) {
    result.flags = TYPE_ARRAY;
    result.length = get_attribute<int>(ntype, AT_LENGTH, -10);
    result.zeroterminated = get_attribute<int>(ntype, AT_ZERO_TERMINATED, 1);
    result.fixedsize = get_attribute<int>(ntype, AT_FIXED_SIZE, 0);
    if (result.length < 0 && !result.zeroterminated && !result.fixedsize)
      throw skip("inconsistent array info");
    auto name = get_name(ntype, std::nothrow);
    // could be GBytes, etc
    if (name.size()) {
      // transform to plain argument if not ignored
      // FIXME maybe custom wrappers might be more convenient ??
      if (ctx.match_ignore.matches(ns, EL_RECORD, {name}))
        throw skip(name + " array not supported", skip::OK);
      // fresh state and discard element info processing
      result = ArgInfo{};
      discard_element = true;
      parse_typeinfo(name, result);
    }
  } else {
    parse_typeinfo(get_name(ntype), result);
  }
  // should be present (but not so for signal)
  // though const info seems to be missing in array case
  result.ctype = get_attribute(ntype, AT_CTYPE, "");

  // special case where const is preserved for the cpp type
  if (result.girname == "gpointer" && is_const(result.ctype))
    parse_typeinfo("gconstpointer", result);

  // handle element type info
  int i = 0;
  for (const auto &n : ntype) {
    if (n.first == "type" && !discard_element) {
      ArgTypeInfo &ti = i == 0 ? result.first : result.second;
      auto elname = get_name(n.second);
      parse_typeinfo(elname, ti);
      if (!ti.flags)
        throw skip("container element not supported", skip::OK);
      // probably not, but give it a shot
      ti.ctype = get_attribute(n.second, AT_CTYPE, "");
      // avoid specialized bool vector
      if (ti.cpptype == "bool")
        ti.cpptype = "gboolean";
      // some char arrays (e.g. g_regex* parameters) specify utf8 element type
      // along with ctype char rather than char*
      // so discard utf8 in that case and go for a plain array
      if (kind == EL_ARRAY && elname == "utf8" && ti.ctype == "gchar")
        parse_typeinfo(ti.ctype, ti);
      ++i;
    }
  }

  // sanity checks
  if ((result.flags & (TYPE_ARRAY | TYPE_LIST))) {
    if (i != 1)
      throw skip("inconsistent list element info");
    if (result.flags & TYPE_ARRAY) {
      // NOTE apparently both are possible, see e.g. g_shell_parse_arg*
      // in that case it is handled zeroterminated
      // and the size param becomes a regular one
      if (result.zeroterminated) {
        result.length = -1;
        result.fixedsize = 0;
      }
      // 1 pointer level more than the basic type
      result.pdepth = ((result.first.flags & TYPE_CLASS) ? 1 : 0) + 1;
    }
  } else if (result.flags & TYPE_MAP) {
    if (i != 2)
      throw skip("inconsistent map element info");
  } else {
    if (i != 0)
      throw skip("inconsistent type info");
  }

  return result;
}

std::string
GeneratorBase::make_ctype(
    const ArgInfo &info, const std::string &direction, bool callerallocates)
{
  std::string result;
  bool out = !(direction == DIR_IN || direction == DIR_RETURN);
  if (info.flags & TYPE_ARRAY) {
    auto ret = info.first.argtype + GI_PTR;
    if (out && !callerallocates)
      ret += GI_PTR;
    result = ret;
  } else if (info.flags & TYPE_CALLBACK) {
    result = info.cpptype + "::cfunction_type";
  } else {
    if (!out || callerallocates) {
      result = info.argtype;
    } else {
      result = info.argtype + GI_PTR;
    }
  }
  return (is_const(info.ctype) ? std::string("const ") : EMPTY) + result;
}

void
GeneratorBase::track_dependency(DepsSet &deps, const ArgInfo &info) const
{
  auto track = [&](const std::string &cpptype, int flags) {
    // other items (e.g. enums) are included before anyway
    if (flags & TYPE_CLASS && !(flags & (TYPE_BASIC | TYPE_TYPEDEF))) {
      // always track scoped
      assert(is_qualified(cpptype));
      deps.insert({"", cpptype});
      if (flags & TYPE_BOXED)
        deps.insert({"", cpptype + GI_SUFFIX_REF});
    }
  };
  if (!(info.flags & TYPE_CONTAINER)) {
    track(info.cpptype, info.flags);
  } else {
    track(info.first.cpptype, info.first.flags);
    if (info.flags & TYPE_MAP)
      track(info.second.cpptype, info.second.flags);
  }
}

bool
GeneratorBase::check_suppression(const std::string &ns, const std::string &kind,
    const std::string &name) const
{
  // always generate
  ctx.suppressions.insert(ctx.match_sup.format(ns, kind, name));
  return ctx.match_sup.matches(ns, kind, {name});
}

std::string
GeneratorBase::make_wrap_format(const ArgInfo &info,
    const std::string &transfer, const std::string &outtype)
{
  std::string fmts;
  auto format = "{}";
  if (info.flags & TYPE_CLASS) {
    fmts = fmt::format(GI_NS_SCOPED + "wrap ({}, {})", format,
        get_transfer_parameter(transfer));
  } else if (info.flags & TYPE_ENUM) {
    fmts = GI_NS_SCOPED + "wrap ({})";
  } else if (info.flags & (TYPE_LIST | TYPE_ARRAY | TYPE_MAP)) {
    assert(!outtype.empty());
    fmts = fmt::format(GI_NS_SCOPED + "wrap_to<{}>({}, {})", outtype, format,
        get_transfer_parameter(transfer));
  } else {
    fmts = format;
  }
  return fmts;
}

std::string
GeneratorBase::get_transfer_parameter(const std::string &transfer, bool _type)
{
  std::map<std::string, std::string> m{
      {TRANSFER_NOTHING, "transfer_none"},
      {TRANSFER_FULL, "transfer_full"},
      {TRANSFER_CONTAINER, "transfer_container"},
  };
  auto it = m.find(transfer);
  if (it == m.end())
    throw std::runtime_error("invalid transfer " + transfer);
  return GI_NS_SCOPED + it->second + (_type ? "_t" : "");
}