Bitcoin Core  31.0.0
P2P Digital Currency
gen.cpp
Go to the documentation of this file.
1 // Copyright (c) The Bitcoin Core developers
2 // Distributed under the MIT software license, see the accompanying
3 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4 
5 #include <mp/config.h>
6 #include <mp/util.h>
7 
8 #include <algorithm>
9 #include <capnp/schema.h>
10 #include <capnp/schema-parser.h>
11 #include <cerrno>
12 #include <cstdint>
13 #include <cstdio>
14 #include <cstdlib>
15 #include <fstream>
16 #include <functional>
17 #include <initializer_list>
18 #include <iostream>
19 #include <kj/array.h>
20 #include <kj/common.h>
21 #include <kj/filesystem.h>
22 #include <kj/memory.h>
23 #include <kj/string.h>
24 #include <map>
25 #include <set>
26 #include <sstream>
27 #include <stdexcept>
28 #include <string>
29 #include <system_error>
30 #include <unistd.h>
31 #include <utility>
32 #include <vector>
33 
34 #define PROXY_BIN "mpgen"
35 #define PROXY_DECL "mp/proxy.h"
36 #define PROXY_TYPES "mp/proxy-types.h"
37 
38 constexpr uint64_t NAMESPACE_ANNOTATION_ID = 0xb9c6f99ebf805f2cull; // From c++.capnp
39 constexpr uint64_t INCLUDE_ANNOTATION_ID = 0xb899f3c154fdb458ull; // From proxy.capnp
40 constexpr uint64_t INCLUDE_TYPES_ANNOTATION_ID = 0xbcec15648e8a0cf1ull; // From proxy.capnp
41 constexpr uint64_t WRAP_ANNOTATION_ID = 0xe6f46079b7b1405eull; // From proxy.capnp
42 constexpr uint64_t COUNT_ANNOTATION_ID = 0xd02682b319f69b38ull; // From proxy.capnp
43 constexpr uint64_t EXCEPTION_ANNOTATION_ID = 0x996a183200992f88ull; // From proxy.capnp
44 constexpr uint64_t NAME_ANNOTATION_ID = 0xb594888f63f4dbb9ull; // From proxy.capnp
45 constexpr uint64_t SKIP_ANNOTATION_ID = 0x824c08b82695d8ddull; // From proxy.capnp
46 
47 template <typename Reader>
48 static bool AnnotationExists(const Reader& reader, uint64_t id)
49 {
50  for (const auto annotation : reader.getAnnotations()) {
51  if (annotation.getId() == id) {
52  return true;
53  }
54  }
55  return false;
56 }
57 
58 template <typename Reader>
59 static bool GetAnnotationText(const Reader& reader, uint64_t id, kj::StringPtr* result)
60 {
61  for (const auto annotation : reader.getAnnotations()) {
62  if (annotation.getId() == id) {
63  *result = annotation.getValue().getText();
64  return true;
65  }
66  }
67  return false;
68 }
69 
70 template <typename Reader>
71 static bool GetAnnotationInt32(const Reader& reader, uint64_t id, int32_t* result)
72 {
73  for (const auto annotation : reader.getAnnotations()) {
74  if (annotation.getId() == id) {
75  *result = annotation.getValue().getInt32();
76  return true;
77  }
78  }
79  return false;
80 }
81 
82 static void ForEachMethod(const capnp::InterfaceSchema& interface, const std::function<void(const capnp::InterfaceSchema& interface, const capnp::InterfaceSchema::Method)>& callback) // NOLINT(misc-no-recursion)
83 {
84  for (const auto super : interface.getSuperclasses()) {
85  ForEachMethod(super, callback);
86  }
87  for (const auto method : interface.getMethods()) {
88  callback(interface, method);
89  }
90 }
91 
92 using CharSlice = kj::ArrayPtr<const char>;
93 
94 // Overload for any type with a string .begin(), like kj::StringPtr and kj::ArrayPtr<char>.
95 template <class OutputStream, class Array, const char* Enable = decltype(std::declval<Array>().begin())()>
96 static OutputStream& operator<<(OutputStream& os, const Array& array)
97 {
98  os.write(array.begin(), array.size());
99  return os;
100 }
101 
102 struct Format
103 {
104  template <typename Value>
105  Format& operator<<(Value&& value)
106  {
107  m_os << value;
108  return *this;
109  }
110  operator std::string() const { return m_os.str(); }
111  std::ostringstream m_os;
112 };
113 
114 static std::string Cap(kj::StringPtr str)
115 {
116  std::string result = str;
117  if (!result.empty() && 'a' <= result[0] && result[0] <= 'z') result[0] -= 'a' - 'A';
118  return result;
119 }
120 
121 static bool BoxedType(const ::capnp::Type& type)
122 {
123  return !(type.isVoid() || type.isBool() || type.isInt8() || type.isInt16() || type.isInt32() || type.isInt64() ||
124  type.isUInt8() || type.isUInt16() || type.isUInt32() || type.isUInt64() || type.isFloat32() ||
125  type.isFloat64() || type.isEnum());
126 }
127 
128 // src_file is path to .capnp file to generate stub code from.
129 //
130 // src_prefix can be used to generate outputs in a different directory than the
131 // source directory. For example if src_file is "/a/b/c/d/file.canp", and
132 // src_prefix is "/a/b", then output files will be "c/d/file.capnp.h"
133 // "c/d/file.capnp.cxx" "c/d/file.capnp.proxy.h", etc. This is equivalent to
134 // the capnp "--src-prefix" option (see "capnp help compile").
135 //
136 // include_prefix can be used to control relative include paths used in
137 // generated files. For example if src_file is "/a/b/c/d/file.canp" and
138 // include_prefix is "/a/b/c" include lines like
139 // "#include <d/file.capnp.proxy.h>", "#include <d/file.capnp.proxy-types.h>"
140 // will be generated.
141 static void Generate(kj::StringPtr src_prefix,
142  kj::StringPtr include_prefix,
143  kj::StringPtr src_file,
144  const std::vector<kj::StringPtr>& import_paths,
145  const kj::ReadableDirectory& src_dir,
146  const std::vector<kj::Own<const kj::ReadableDirectory>>& import_dirs)
147 {
148  std::string output_path;
149  if (src_prefix == kj::StringPtr{"."}) {
150  output_path = src_file;
151  } else if (!src_file.startsWith(src_prefix) || src_file.size() <= src_prefix.size() ||
152  src_file[src_prefix.size()] != '/') {
153  throw std::runtime_error("src_prefix is not src_file prefix");
154  } else {
155  output_path = src_file.slice(src_prefix.size() + 1);
156  }
157 
158  std::string include_path;
159  if (include_prefix == kj::StringPtr{"."}) {
160  include_path = src_file;
161  } else if (!src_file.startsWith(include_prefix) || src_file.size() <= include_prefix.size() ||
162  src_file[include_prefix.size()] != '/') {
163  throw std::runtime_error("include_prefix is not src_file prefix");
164  } else {
165  include_path = src_file.slice(include_prefix.size() + 1);
166  }
167 
168  std::string include_base = include_path;
169  const std::string::size_type p = include_base.rfind('.');
170  if (p != std::string::npos) include_base.erase(p);
171 
172  std::vector<std::string> args;
173  args.emplace_back(capnp_PREFIX "/bin/capnp");
174  args.emplace_back("compile");
175  args.emplace_back("--src-prefix=");
176  args.back().append(src_prefix.cStr(), src_prefix.size());
177  for (const auto& import_path : import_paths) {
178  args.emplace_back("--import-path=");
179  args.back().append(import_path.cStr(), import_path.size());
180  }
181  args.emplace_back("--output=" capnp_PREFIX "/bin/capnpc-c++");
182  args.emplace_back(src_file);
183  const int pid = fork();
184  if (pid == -1) {
185  throw std::system_error(errno, std::system_category(), "fork");
186  }
187  if (!pid) {
189  }
190  const int status = mp::WaitProcess(pid);
191  if (status) {
192  throw std::runtime_error("Invoking " capnp_PREFIX "/bin/capnp failed");
193  }
194 
195  const capnp::SchemaParser parser;
196  auto directory_pointers = kj::heapArray<const kj::ReadableDirectory*>(import_dirs.size());
197  for (size_t i = 0; i < import_dirs.size(); ++i) {
198  directory_pointers[i] = import_dirs[i].get();
199  }
200  auto file_schema = parser.parseFromDirectory(src_dir, kj::Path::parse(output_path), directory_pointers);
201 
202  std::ofstream cpp_server(output_path + ".proxy-server.c++");
203  cpp_server << "// Generated by " PROXY_BIN " from " << src_file << "\n\n";
204  cpp_server << "// IWYU pragma: no_include <kj/memory.h>\n";
205  cpp_server << "// IWYU pragma: no_include <memory>\n";
206  cpp_server << "// IWYU pragma: begin_keep\n";
207  cpp_server << "#include <" << include_path << ".proxy.h>\n";
208  cpp_server << "#include <" << include_path << ".proxy-types.h>\n";
209  cpp_server << "#include <capnp/generated-header-support.h>\n";
210  cpp_server << "#include <cstring>\n";
211  cpp_server << "#include <kj/async.h>\n";
212  cpp_server << "#include <kj/common.h>\n";
213  cpp_server << "#include <kj/exception.h>\n";
214  cpp_server << "#include <kj/tuple.h>\n";
215  cpp_server << "#include <mp/proxy.h>\n";
216  cpp_server << "#include <mp/util.h>\n";
217  cpp_server << "#include <" << PROXY_TYPES << ">\n";
218  cpp_server << "// IWYU pragma: end_keep\n\n";
219  cpp_server << "namespace mp {\n";
220 
221  std::ofstream cpp_client(output_path + ".proxy-client.c++");
222  cpp_client << "// Generated by " PROXY_BIN " from " << src_file << "\n\n";
223  cpp_client << "// IWYU pragma: no_include <kj/memory.h>\n";
224  cpp_client << "// IWYU pragma: no_include <memory>\n";
225  cpp_client << "// IWYU pragma: begin_keep\n";
226  cpp_client << "#include <" << include_path << ".h>\n";
227  cpp_client << "#include <" << include_path << ".proxy.h>\n";
228  cpp_client << "#include <" << include_path << ".proxy-types.h>\n";
229  cpp_client << "#include <capnp/generated-header-support.h>\n";
230  cpp_client << "#include <cstring>\n";
231  cpp_client << "#include <vector>\n";
232  cpp_client << "#include <kj/common.h>\n";
233  cpp_client << "#include <mp/proxy.h>\n";
234  cpp_client << "#include <mp/util.h>\n";
235  cpp_client << "#include <" << PROXY_TYPES << ">\n";
236  cpp_client << "// IWYU pragma: end_keep\n\n";
237  cpp_client << "namespace mp {\n";
238 
239  std::ofstream cpp_types(output_path + ".proxy-types.c++");
240  cpp_types << "// Generated by " PROXY_BIN " from " << src_file << "\n\n";
241  cpp_types << "// IWYU pragma: no_include \"mp/proxy.h\"\n";
242  cpp_types << "// IWYU pragma: no_include \"mp/proxy-io.h\"\n";
243  cpp_types << "#include <" << include_path << ".proxy.h>\n";
244  cpp_types << "#include <" << include_path << ".proxy-types.h> // IWYU pragma: keep\n";
245  cpp_types << "#include <" << PROXY_TYPES << ">\n\n";
246  cpp_types << "namespace mp {\n";
247 
248  std::string guard = output_path;
249  std::ranges::transform(guard, guard.begin(), [](unsigned char c) -> unsigned char {
250  if ('0' <= c && c <= '9') return c;
251  if ('A' <= c && c <= 'Z') return c;
252  if ('a' <= c && c <= 'z') return c - 'a' + 'A';
253  return '_';
254  });
255 
256  std::ofstream inl(output_path + ".proxy-types.h");
257  inl << "// Generated by " PROXY_BIN " from " << src_file << "\n\n";
258  inl << "#ifndef " << guard << "_PROXY_TYPES_H\n";
259  inl << "#define " << guard << "_PROXY_TYPES_H\n\n";
260  inl << "// IWYU pragma: no_include \"mp/proxy.h\"\n";
261  inl << "#include <mp/proxy.h> // IWYU pragma: keep\n";
262  inl << "#include <" << include_path << ".proxy.h> // IWYU pragma: keep\n";
263  for (const auto annotation : file_schema.getProto().getAnnotations()) {
264  if (annotation.getId() == INCLUDE_TYPES_ANNOTATION_ID) {
265  inl << "#include \"" << annotation.getValue().getText() << "\" // IWYU pragma: export\n";
266  }
267  }
268  inl << "namespace mp {\n";
269 
270  std::ofstream h(output_path + ".proxy.h");
271  h << "// Generated by " PROXY_BIN " from " << src_file << "\n\n";
272  h << "#ifndef " << guard << "_PROXY_H\n";
273  h << "#define " << guard << "_PROXY_H\n\n";
274  h << "#include <" << include_path << ".h> // IWYU pragma: keep\n";
275  for (const auto annotation : file_schema.getProto().getAnnotations()) {
276  if (annotation.getId() == INCLUDE_ANNOTATION_ID) {
277  h << "#include \"" << annotation.getValue().getText() << "\" // IWYU pragma: export\n";
278  }
279  }
280  h << "#include <" << PROXY_DECL << ">\n\n";
281  h << "#if defined(__GNUC__)\n";
282  h << "#pragma GCC diagnostic push\n";
283  h << "#if !defined(__has_warning)\n";
284  h << "#pragma GCC diagnostic ignored \"-Wsuggest-override\"\n";
285  h << "#elif __has_warning(\"-Wsuggest-override\")\n";
286  h << "#pragma GCC diagnostic ignored \"-Wsuggest-override\"\n";
287  h << "#endif\n";
288  h << "#endif\n";
289  h << "namespace mp {\n";
290 
291  kj::StringPtr message_namespace;
292  GetAnnotationText(file_schema.getProto(), NAMESPACE_ANNOTATION_ID, &message_namespace);
293 
294  std::string base_name = include_base;
295  const size_t output_slash = base_name.rfind('/');
296  if (output_slash != std::string::npos) {
297  base_name.erase(0, output_slash + 1);
298  }
299 
300  std::ostringstream methods;
301  std::set<kj::StringPtr> accessors_done;
302  std::ostringstream accessors;
303  std::ostringstream dec;
304  std::ostringstream def_server;
305  std::ostringstream def_client;
306  std::ostringstream int_client;
307  std::ostringstream def_types;
308 
309  auto add_accessor = [&](kj::StringPtr name) {
310  if (!accessors_done.insert(name).second) return;
311  const std::string cap = Cap(name);
312  accessors << "struct " << cap << "\n";
313  accessors << "{\n";
314  accessors << " template<typename S> static auto get(S&& s) -> decltype(s.get" << cap << "()) { return s.get" << cap << "(); }\n";
315  accessors << " template<typename S> static bool has(S&& s) { return s.has" << cap << "(); }\n";
316  accessors << " template<typename S, typename A> static void set(S&& s, A&& a) { s.set" << cap
317  << "(std::forward<A>(a)); }\n";
318  accessors << " template<typename S, typename... A> static decltype(auto) init(S&& s, A&&... a) { return s.init"
319  << cap << "(std::forward<A>(a)...); }\n";
320  accessors << " template<typename S> static bool getWant(S&& s) { return s.getWant" << cap << "(); }\n";
321  accessors << " template<typename S> static void setWant(S&& s) { s.setWant" << cap << "(true); }\n";
322  accessors << " template<typename S> static bool getHas(S&& s) { return s.getHas" << cap << "(); }\n";
323  accessors << " template<typename S> static void setHas(S&& s) { s.setHas" << cap << "(true); }\n";
324  accessors << "};\n";
325  };
326 
327  for (const auto node_nested : file_schema.getProto().getNestedNodes()) {
328  kj::StringPtr node_name = node_nested.getName();
329  const auto& node = file_schema.getNested(node_name);
330  kj::StringPtr proxied_class_type;
331  GetAnnotationText(node.getProto(), WRAP_ANNOTATION_ID, &proxied_class_type);
332 
333  if (node.getProto().isStruct()) {
334  const auto& struc = node.asStruct();
335  std::ostringstream generic_name;
336  generic_name << node_name;
337  dec << "template<";
338  bool first_param = true;
339  for (const auto param : node.getProto().getParameters()) {
340  if (first_param) {
341  first_param = false;
342  generic_name << "<";
343  } else {
344  dec << ", ";
345  generic_name << ", ";
346  }
347  dec << "typename " << param.getName();
348  generic_name << "" << param.getName();
349  }
350  if (!first_param) generic_name << ">";
351  dec << ">\n";
352  dec << "struct ProxyStruct<" << message_namespace << "::" << generic_name.str() << ">\n";
353  dec << "{\n";
354  dec << " using Struct = " << message_namespace << "::" << generic_name.str() << ";\n";
355  for (const auto field : struc.getFields()) {
356  auto field_name = field.getProto().getName();
357  add_accessor(field_name);
358  dec << " using " << Cap(field_name) << "Accessor = Accessor<" << base_name
359  << "_fields::" << Cap(field_name) << ", FIELD_IN | FIELD_OUT";
360  if (BoxedType(field.getType())) dec << " | FIELD_BOXED";
361  dec << ">;\n";
362  }
363  dec << " using Accessors = std::tuple<";
364  size_t i = 0;
365  for (const auto field : struc.getFields()) {
366  if (AnnotationExists(field.getProto(), SKIP_ANNOTATION_ID)) {
367  continue;
368  }
369  if (i) dec << ", ";
370  dec << Cap(field.getProto().getName()) << "Accessor";
371  ++i;
372  }
373  dec << ">;\n";
374  dec << " static constexpr size_t fields = " << i << ";\n";
375  dec << "};\n";
376 
377  if (proxied_class_type.size()) {
378  inl << "template<>\n";
379  inl << "struct ProxyType<" << proxied_class_type << ">\n";
380  inl << "{\n";
381  inl << "public:\n";
382  inl << " using Struct = " << message_namespace << "::" << node_name << ";\n";
383  size_t i = 0;
384  for (const auto field : struc.getFields()) {
385  if (AnnotationExists(field.getProto(), SKIP_ANNOTATION_ID)) {
386  continue;
387  }
388  auto field_name = field.getProto().getName();
389  auto member_name = field_name;
390  GetAnnotationText(field.getProto(), NAME_ANNOTATION_ID, &member_name);
391  inl << " static decltype(auto) get(std::integral_constant<size_t, " << i << ">) { return "
392  << "&" << proxied_class_type << "::" << member_name << "; }\n";
393  ++i;
394  }
395  inl << " static constexpr size_t fields = " << i << ";\n";
396  inl << "};\n";
397  }
398  }
399 
400  if (proxied_class_type.size() && node.getProto().isInterface()) {
401  const auto& interface = node.asInterface();
402 
403  std::ostringstream client;
404  client << "template<>\nstruct ProxyClient<" << message_namespace << "::" << node_name << "> final : ";
405  client << "public ProxyClientCustom<" << message_namespace << "::" << node_name << ", "
406  << proxied_class_type << ">\n{\n";
407  client << "public:\n";
408  client << " using ProxyClientCustom::ProxyClientCustom;\n";
409  client << " ~ProxyClient();\n";
410 
411  std::ostringstream server;
412  server << "template<>\nstruct ProxyServer<" << message_namespace << "::" << node_name << "> : public "
413  << "ProxyServerCustom<" << message_namespace << "::" << node_name << ", " << proxied_class_type
414  << ">\n{\n";
415  server << "public:\n";
416  server << " using ProxyServerCustom::ProxyServerCustom;\n";
417  server << " ~ProxyServer();\n";
418 
419  const std::ostringstream client_construct;
420  const std::ostringstream client_destroy;
421 
422  int method_ordinal = 0;
423  ForEachMethod(interface, [&] (const capnp::InterfaceSchema& method_interface, const capnp::InterfaceSchema::Method& method) {
424  const kj::StringPtr method_name = method.getProto().getName();
425  kj::StringPtr proxied_method_name = method_name;
426  GetAnnotationText(method.getProto(), NAME_ANNOTATION_ID, &proxied_method_name);
427 
428  const std::string method_prefix = Format() << message_namespace << "::" << method_interface.getShortDisplayName()
429  << "::" << Cap(method_name);
430  const bool is_construct = method_name == kj::StringPtr{"construct"};
431  const bool is_destroy = method_name == kj::StringPtr{"destroy"};
432 
433  struct Field
434  {
435  ::capnp::StructSchema::Field param;
436  bool param_is_set = false;
437  ::capnp::StructSchema::Field result;
438  bool result_is_set = false;
439  int args = 0;
440  bool retval = false;
441  bool optional = false;
442  bool requested = false;
443  bool skip = false;
444  kj::StringPtr exception;
445  };
446 
447  std::vector<Field> fields;
448  std::map<kj::StringPtr, int> field_idx; // name -> args index
449  bool has_result = false;
450 
451  auto add_field = [&](const ::capnp::StructSchema::Field& schema_field, bool param) {
452  if (AnnotationExists(schema_field.getProto(), SKIP_ANNOTATION_ID)) {
453  return;
454  }
455 
456  auto field_name = schema_field.getProto().getName();
457  auto inserted = field_idx.emplace(field_name, fields.size());
458  if (inserted.second) {
459  fields.emplace_back();
460  }
461  auto& field = fields[inserted.first->second];
462  if (param) {
463  field.param = schema_field;
464  field.param_is_set = true;
465  } else {
466  field.result = schema_field;
467  field.result_is_set = true;
468  }
469 
470  if (!param && field_name == kj::StringPtr{"result"}) {
471  field.retval = true;
472  has_result = true;
473  }
474 
475  GetAnnotationText(schema_field.getProto(), EXCEPTION_ANNOTATION_ID, &field.exception);
476 
477  int32_t count = 1;
478  if (!GetAnnotationInt32(schema_field.getProto(), COUNT_ANNOTATION_ID, &count)) {
479  if (schema_field.getType().isStruct()) {
480  GetAnnotationInt32(schema_field.getType().asStruct().getProto(),
482  } else if (schema_field.getType().isInterface()) {
483  GetAnnotationInt32(schema_field.getType().asInterface().getProto(),
485  }
486  }
487 
488 
489  if (inserted.second && !field.retval && !field.exception.size()) {
490  field.args = count;
491  }
492  };
493 
494  for (const auto schema_field : method.getParamType().getFields()) {
495  add_field(schema_field, true);
496  }
497  for (const auto schema_field : method.getResultType().getFields()) {
498  add_field(schema_field, false);
499  }
500  for (auto& field : field_idx) {
501  auto has_field = field_idx.find("has" + Cap(field.first));
502  if (has_field != field_idx.end()) {
503  fields[has_field->second].skip = true;
504  fields[field.second].optional = true;
505  }
506  auto want_field = field_idx.find("want" + Cap(field.first));
507  if (want_field != field_idx.end() && fields[want_field->second].param_is_set) {
508  fields[want_field->second].skip = true;
509  fields[field.second].requested = true;
510  }
511  }
512 
513  if (!is_construct && !is_destroy && (&method_interface == &interface)) {
514  methods << "template<>\n";
515  methods << "struct ProxyMethod<" << method_prefix << "Params>\n";
516  methods << "{\n";
517  methods << " static constexpr auto impl = &" << proxied_class_type
518  << "::" << proxied_method_name << ";\n";
519  methods << "};\n\n";
520  }
521 
522  std::ostringstream client_args;
523  std::ostringstream client_invoke;
524  std::ostringstream server_invoke_start;
525  std::ostringstream server_invoke_end;
526  int argc = 0;
527  for (const auto& field : fields) {
528  if (field.skip) continue;
529 
530  const auto& f = field.param_is_set ? field.param : field.result;
531  auto field_name = f.getProto().getName();
532  auto field_type = f.getType();
533 
534  std::ostringstream field_flags;
535  if (!field.param_is_set) {
536  field_flags << "FIELD_OUT";
537  } else if (field.result_is_set) {
538  field_flags << "FIELD_IN | FIELD_OUT";
539  } else {
540  field_flags << "FIELD_IN";
541  }
542  if (field.optional) field_flags << " | FIELD_OPTIONAL";
543  if (field.requested) field_flags << " | FIELD_REQUESTED";
544  if (BoxedType(field_type)) field_flags << " | FIELD_BOXED";
545 
546  add_accessor(field_name);
547 
548  std::ostringstream fwd_args;
549  for (int i = 0; i < field.args; ++i) {
550  if (argc > 0) client_args << ",";
551 
552  // Add to client method parameter list.
553  client_args << "M" << method_ordinal << "::Param<" << argc << "> " << field_name;
554  if (field.args > 1) client_args << i;
555 
556  // Add to MakeClientParam argument list using Fwd helper for perfect forwarding.
557  if (i > 0) fwd_args << ", ";
558  fwd_args << "M" << method_ordinal << "::Fwd<" << argc << ">(" << field_name;
559  if (field.args > 1) fwd_args << i;
560  fwd_args << ")";
561 
562  ++argc;
563  }
564  client_invoke << ", ";
565 
566  if (field.exception.size()) {
567  client_invoke << "ClientException<" << field.exception << ", ";
568  } else {
569  client_invoke << "MakeClientParam<";
570  }
571 
572  client_invoke << "Accessor<" << base_name << "_fields::" << Cap(field_name) << ", "
573  << field_flags.str() << ">>(";
574 
575  if (field.retval) {
576  client_invoke << field_name;
577  } else {
578  client_invoke << fwd_args.str();
579  }
580  client_invoke << ")";
581 
582  if (field.exception.size()) {
583  server_invoke_start << "Make<ServerExcept, " << field.exception;
584  } else if (field.retval) {
585  server_invoke_start << "Make<ServerRet";
586  } else {
587  server_invoke_start << "MakeServerField<" << field.args;
588  }
589  server_invoke_start << ", Accessor<" << base_name << "_fields::" << Cap(field_name) << ", "
590  << field_flags.str() << ">>(";
591  server_invoke_end << ")";
592  }
593 
594  const std::string static_str{is_construct || is_destroy ? "static " : ""};
595  const std::string super_str{is_construct || is_destroy ? "Super& super" : ""};
596  const std::string self_str{is_construct || is_destroy ? "super" : "*this"};
597 
598  client << " using M" << method_ordinal << " = ProxyClientMethodTraits<" << method_prefix
599  << "Params>;\n";
600  client << " " << static_str << "typename M" << method_ordinal << "::Result " << method_name << "("
601  << super_str << client_args.str() << ")";
602  client << ";\n";
603  def_client << "ProxyClient<" << message_namespace << "::" << node_name << ">::M" << method_ordinal
604  << "::Result ProxyClient<" << message_namespace << "::" << node_name << ">::" << method_name
605  << "(" << super_str << client_args.str() << ") {\n";
606  if (has_result) {
607  def_client << " typename M" << method_ordinal << "::Result result;\n";
608  }
609  def_client << " clientInvoke(" << self_str << ", &" << message_namespace << "::" << node_name
610  << "::Client::" << method_name << "Request" << client_invoke.str() << ");\n";
611  if (has_result) def_client << " return result;\n";
612  def_client << "}\n";
613 
614  server << " kj::Promise<void> " << method_name << "(" << Cap(method_name)
615  << "Context call_context) override;\n";
616 
617  def_server << "kj::Promise<void> ProxyServer<" << message_namespace << "::" << node_name
618  << ">::" << method_name << "(" << Cap(method_name)
619  << "Context call_context) {\n"
620  " return serverInvoke(*this, call_context, "
621  << server_invoke_start.str();
622  if (is_destroy) {
623  def_server << "ServerDestroy()";
624  } else {
625  def_server << "ServerCall()";
626  }
627  def_server << server_invoke_end.str() << ");\n}\n";
628  ++method_ordinal;
629  });
630 
631  client << "};\n";
632  server << "};\n";
633  dec << "\n" << client.str() << "\n" << server.str() << "\n";
634  KJ_IF_MAYBE(bracket, proxied_class_type.findFirst('<')) {
635  // Skip ProxyType definition for complex type expressions which
636  // could lead to duplicate definitions. They can be defined
637  // manually if actually needed.
638  } else {
639  dec << "template<>\nstruct ProxyType<" << proxied_class_type << ">\n{\n";
640  dec << " using Type = " << proxied_class_type << ";\n";
641  dec << " using Message = " << message_namespace << "::" << node_name << ";\n";
642  dec << " using Client = ProxyClient<Message>;\n";
643  dec << " using Server = ProxyServer<Message>;\n";
644  dec << "};\n";
645  int_client << "ProxyTypeRegister t" << node_nested.getId() << "{TypeList<" << proxied_class_type << ">{}};\n";
646  }
647  def_types << "ProxyClient<" << message_namespace << "::" << node_name
648  << ">::~ProxyClient() { clientDestroy(*this); " << client_destroy.str() << " }\n";
649  def_types << "ProxyServer<" << message_namespace << "::" << node_name
650  << ">::~ProxyServer() { serverDestroy(*this); }\n";
651  }
652  }
653 
654  h << methods.str() << "namespace " << base_name << "_fields {\n"
655  << accessors.str() << "} // namespace " << base_name << "_fields\n"
656  << dec.str();
657 
658  cpp_server << def_server.str();
659  cpp_server << "} // namespace mp\n";
660 
661  cpp_client << def_client.str();
662  cpp_client << "namespace {\n" << int_client.str() << "} // namespace\n";
663  cpp_client << "} // namespace mp\n";
664 
665  cpp_types << def_types.str();
666  cpp_types << "} // namespace mp\n";
667 
668  inl << "} // namespace mp\n";
669  inl << "#endif\n";
670 
671  h << "} // namespace mp\n";
672  h << "#if defined(__GNUC__)\n";
673  h << "#pragma GCC diagnostic pop\n";
674  h << "#endif\n";
675  h << "#endif\n";
676 }
677 
678 int main(int argc, char** argv)
679 {
680  if (argc < 3) {
681  std::cerr << "Usage: " << PROXY_BIN << " SRC_PREFIX INCLUDE_PREFIX SRC_FILE [IMPORT_PATH...]\n";
682  exit(1);
683  }
684  std::vector<kj::StringPtr> import_paths;
685  std::vector<kj::Own<const kj::ReadableDirectory>> import_dirs;
686  auto fs = kj::newDiskFilesystem();
687  auto cwd = fs->getCurrentPath();
688  kj::Own<const kj::ReadableDirectory> src_dir;
689  KJ_IF_MAYBE(dir, fs->getRoot().tryOpenSubdir(cwd.evalNative(argv[1]))) {
690  src_dir = kj::mv(*dir);
691  } else {
692  throw std::runtime_error(std::string("Failed to open src_prefix prefix directory: ") + argv[1]);
693  }
694  for (int i = 4; i < argc; ++i) {
695  KJ_IF_MAYBE(dir, fs->getRoot().tryOpenSubdir(cwd.evalNative(argv[i]))) {
696  import_paths.emplace_back(argv[i]);
697  import_dirs.emplace_back(kj::mv(*dir));
698  } else {
699  throw std::runtime_error(std::string("Failed to open import directory: ") + argv[i]);
700  }
701  }
702  for (const char* path : {CMAKE_INSTALL_PREFIX "/include", capnp_PREFIX "/include"}) {
703  KJ_IF_MAYBE(dir, fs->getRoot().tryOpenSubdir(cwd.evalNative(path))) {
704  import_paths.emplace_back(path);
705  import_dirs.emplace_back(kj::mv(*dir));
706  }
707  // No exception thrown if _PREFIX directories do not exist
708  }
709  Generate(argv[1], argv[2], argv[3], import_paths, *src_dir, import_dirs);
710  return 0;
711 }
static std::string Cap(kj::StringPtr str)
Definition: gen.cpp:114
constexpr uint64_t COUNT_ANNOTATION_ID
Definition: gen.cpp:42
kj::ArrayPtr< const char > CharSlice
Definition: gen.cpp:92
#define PROXY_TYPES
Definition: gen.cpp:36
static bool GetAnnotationInt32(const Reader &reader, uint64_t id, int32_t *result)
Definition: gen.cpp:71
constexpr uint64_t SKIP_ANNOTATION_ID
Definition: gen.cpp:45
constexpr uint64_t INCLUDE_ANNOTATION_ID
Definition: gen.cpp:39
static bool GetAnnotationText(const Reader &reader, uint64_t id, kj::StringPtr *result)
Definition: gen.cpp:59
static void ForEachMethod(const capnp::InterfaceSchema &interface, const std::function< void(const capnp::InterfaceSchema &interface, const capnp::InterfaceSchema::Method)> &callback)
Definition: gen.cpp:82
constexpr uint64_t INCLUDE_TYPES_ANNOTATION_ID
Definition: gen.cpp:40
static OutputStream & operator<<(OutputStream &os, const Array &array)
Definition: gen.cpp:96
constexpr uint64_t NAMESPACE_ANNOTATION_ID
Definition: gen.cpp:38
static bool AnnotationExists(const Reader &reader, uint64_t id)
Definition: gen.cpp:48
ArgsManager & args
Definition: bitcoind.cpp:277
const char * name
Definition: rest.cpp:48
void ExecProcess(const std::vector< std::string > &args)
Call execvp with vector args.
Definition: util.cpp:174
std::ostringstream m_os
Definition: gen.cpp:111
int WaitProcess(int pid)
Wait for a process to exit and return its exit code.
Definition: util.cpp:186
static void Generate(kj::StringPtr src_prefix, kj::StringPtr include_prefix, kj::StringPtr src_file, const std::vector< kj::StringPtr > &import_paths, const kj::ReadableDirectory &src_dir, const std::vector< kj::Own< const kj::ReadableDirectory >> &import_dirs)
Definition: gen.cpp:141
Format & operator<<(Value &&value)
Definition: gen.cpp:105
constexpr uint64_t NAME_ANNOTATION_ID
Definition: gen.cpp:44
Definition: messages.h:21
int main(int argc, char **argv)
Definition: gen.cpp:678
auto result
Definition: common-types.h:74
#define PROXY_BIN
Definition: gen.cpp:34
static int count
constexpr uint64_t EXCEPTION_ANNOTATION_ID
Definition: gen.cpp:43
Definition: gen.cpp:102
static bool BoxedType(const ::capnp::Type &type)
Definition: gen.cpp:121
#define PROXY_DECL
Definition: gen.cpp:35
constexpr uint64_t WRAP_ANNOTATION_ID
Definition: gen.cpp:41