Bitcoin Core 30.99.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
38constexpr uint64_t NAMESPACE_ANNOTATION_ID = 0xb9c6f99ebf805f2cull; // From c++.capnp
39constexpr uint64_t INCLUDE_ANNOTATION_ID = 0xb899f3c154fdb458ull; // From proxy.capnp
40constexpr uint64_t INCLUDE_TYPES_ANNOTATION_ID = 0xbcec15648e8a0cf1ull; // From proxy.capnp
41constexpr uint64_t WRAP_ANNOTATION_ID = 0xe6f46079b7b1405eull; // From proxy.capnp
42constexpr uint64_t COUNT_ANNOTATION_ID = 0xd02682b319f69b38ull; // From proxy.capnp
43constexpr uint64_t EXCEPTION_ANNOTATION_ID = 0x996a183200992f88ull; // From proxy.capnp
44constexpr uint64_t NAME_ANNOTATION_ID = 0xb594888f63f4dbb9ull; // From proxy.capnp
45constexpr uint64_t SKIP_ANNOTATION_ID = 0x824c08b82695d8ddull; // From proxy.capnp
46
47template <typename Reader>
48static 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
58template <typename Reader>
59static 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
70template <typename Reader>
71static 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
82static 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
92using CharSlice = kj::ArrayPtr<const char>;
93
94// Overload for any type with a string .begin(), like kj::StringPtr and kj::ArrayPtr<char>.
95template <class OutputStream, class Array, const char* Enable = decltype(std::declval<Array>().begin())()>
96static OutputStream& operator<<(OutputStream& os, const Array& array)
97{
98 os.write(array.begin(), array.size());
99 return os;
100}
101
102struct 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
114static 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
121static 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.
141static 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 <kj/common.h>\n";
232 cpp_client << "#include <mp/proxy.h>\n";
233 cpp_client << "#include <mp/util.h>\n";
234 cpp_client << "#include <" << PROXY_TYPES << ">\n";
235 cpp_client << "// IWYU pragma: end_keep\n\n";
236 cpp_client << "namespace mp {\n";
237
238 std::ofstream cpp_types(output_path + ".proxy-types.c++");
239 cpp_types << "// Generated by " PROXY_BIN " from " << src_file << "\n\n";
240 cpp_types << "// IWYU pragma: no_include \"mp/proxy.h\"\n";
241 cpp_types << "// IWYU pragma: no_include \"mp/proxy-io.h\"\n";
242 cpp_types << "#include <" << include_path << ".proxy.h>\n";
243 cpp_types << "#include <" << include_path << ".proxy-types.h> // IWYU pragma: keep\n";
244 cpp_types << "#include <" << PROXY_TYPES << ">\n\n";
245 cpp_types << "namespace mp {\n";
246
247 std::string guard = output_path;
248 std::ranges::transform(guard, guard.begin(), [](unsigned char c) -> unsigned char {
249 if ('0' <= c && c <= '9') return c;
250 if ('A' <= c && c <= 'Z') return c;
251 if ('a' <= c && c <= 'z') return c - 'a' + 'A';
252 return '_';
253 });
254
255 std::ofstream inl(output_path + ".proxy-types.h");
256 inl << "// Generated by " PROXY_BIN " from " << src_file << "\n\n";
257 inl << "#ifndef " << guard << "_PROXY_TYPES_H\n";
258 inl << "#define " << guard << "_PROXY_TYPES_H\n\n";
259 inl << "// IWYU pragma: no_include \"mp/proxy.h\"\n";
260 inl << "#include <mp/proxy.h> // IWYU pragma: keep\n";
261 inl << "#include <" << include_path << ".proxy.h> // IWYU pragma: keep\n";
262 for (const auto annotation : file_schema.getProto().getAnnotations()) {
263 if (annotation.getId() == INCLUDE_TYPES_ANNOTATION_ID) {
264 inl << "#include \"" << annotation.getValue().getText() << "\" // IWYU pragma: export\n";
265 }
266 }
267 inl << "namespace mp {\n";
268
269 std::ofstream h(output_path + ".proxy.h");
270 h << "// Generated by " PROXY_BIN " from " << src_file << "\n\n";
271 h << "#ifndef " << guard << "_PROXY_H\n";
272 h << "#define " << guard << "_PROXY_H\n\n";
273 h << "#include <" << include_path << ".h> // IWYU pragma: keep\n";
274 for (const auto annotation : file_schema.getProto().getAnnotations()) {
275 if (annotation.getId() == INCLUDE_ANNOTATION_ID) {
276 h << "#include \"" << annotation.getValue().getText() << "\" // IWYU pragma: export\n";
277 }
278 }
279 h << "#include <" << PROXY_DECL << ">\n\n";
280 h << "#if defined(__GNUC__)\n";
281 h << "#pragma GCC diagnostic push\n";
282 h << "#if !defined(__has_warning)\n";
283 h << "#pragma GCC diagnostic ignored \"-Wsuggest-override\"\n";
284 h << "#elif __has_warning(\"-Wsuggest-override\")\n";
285 h << "#pragma GCC diagnostic ignored \"-Wsuggest-override\"\n";
286 h << "#endif\n";
287 h << "#endif\n";
288 h << "namespace mp {\n";
289
290 kj::StringPtr message_namespace;
291 GetAnnotationText(file_schema.getProto(), NAMESPACE_ANNOTATION_ID, &message_namespace);
292
293 std::string base_name = include_base;
294 const size_t output_slash = base_name.rfind('/');
295 if (output_slash != std::string::npos) {
296 base_name.erase(0, output_slash + 1);
297 }
298
299 std::ostringstream methods;
300 std::set<kj::StringPtr> accessors_done;
301 std::ostringstream accessors;
302 std::ostringstream dec;
303 std::ostringstream def_server;
304 std::ostringstream def_client;
305 std::ostringstream int_client;
306 std::ostringstream def_types;
307
308 auto add_accessor = [&](kj::StringPtr name) {
309 if (!accessors_done.insert(name).second) return;
310 const std::string cap = Cap(name);
311 accessors << "struct " << cap << "\n";
312 accessors << "{\n";
313 accessors << " template<typename S> static auto get(S&& s) -> decltype(s.get" << cap << "()) { return s.get" << cap << "(); }\n";
314 accessors << " template<typename S> static bool has(S&& s) { return s.has" << cap << "(); }\n";
315 accessors << " template<typename S, typename A> static void set(S&& s, A&& a) { s.set" << cap
316 << "(std::forward<A>(a)); }\n";
317 accessors << " template<typename S, typename... A> static decltype(auto) init(S&& s, A&&... a) { return s.init"
318 << cap << "(std::forward<A>(a)...); }\n";
319 accessors << " template<typename S> static bool getWant(S&& s) { return s.getWant" << cap << "(); }\n";
320 accessors << " template<typename S> static void setWant(S&& s) { s.setWant" << cap << "(true); }\n";
321 accessors << " template<typename S> static bool getHas(S&& s) { return s.getHas" << cap << "(); }\n";
322 accessors << " template<typename S> static void setHas(S&& s) { s.setHas" << cap << "(true); }\n";
323 accessors << "};\n";
324 };
325
326 for (const auto node_nested : file_schema.getProto().getNestedNodes()) {
327 kj::StringPtr node_name = node_nested.getName();
328 const auto& node = file_schema.getNested(node_name);
329 kj::StringPtr proxied_class_type;
330 GetAnnotationText(node.getProto(), WRAP_ANNOTATION_ID, &proxied_class_type);
331
332 if (node.getProto().isStruct()) {
333 const auto& struc = node.asStruct();
334 std::ostringstream generic_name;
335 generic_name << node_name;
336 dec << "template<";
337 bool first_param = true;
338 for (const auto param : node.getProto().getParameters()) {
339 if (first_param) {
340 first_param = false;
341 generic_name << "<";
342 } else {
343 dec << ", ";
344 generic_name << ", ";
345 }
346 dec << "typename " << param.getName();
347 generic_name << "" << param.getName();
348 }
349 if (!first_param) generic_name << ">";
350 dec << ">\n";
351 dec << "struct ProxyStruct<" << message_namespace << "::" << generic_name.str() << ">\n";
352 dec << "{\n";
353 dec << " using Struct = " << message_namespace << "::" << generic_name.str() << ";\n";
354 for (const auto field : struc.getFields()) {
355 auto field_name = field.getProto().getName();
356 add_accessor(field_name);
357 dec << " using " << Cap(field_name) << "Accessor = Accessor<" << base_name
358 << "_fields::" << Cap(field_name) << ", FIELD_IN | FIELD_OUT";
359 if (BoxedType(field.getType())) dec << " | FIELD_BOXED";
360 dec << ">;\n";
361 }
362 dec << " using Accessors = std::tuple<";
363 size_t i = 0;
364 for (const auto field : struc.getFields()) {
365 if (AnnotationExists(field.getProto(), SKIP_ANNOTATION_ID)) {
366 continue;
367 }
368 if (i) dec << ", ";
369 dec << Cap(field.getProto().getName()) << "Accessor";
370 ++i;
371 }
372 dec << ">;\n";
373 dec << " static constexpr size_t fields = " << i << ";\n";
374 dec << "};\n";
375
376 if (proxied_class_type.size()) {
377 inl << "template<>\n";
378 inl << "struct ProxyType<" << proxied_class_type << ">\n";
379 inl << "{\n";
380 inl << "public:\n";
381 inl << " using Struct = " << message_namespace << "::" << node_name << ";\n";
382 size_t i = 0;
383 for (const auto field : struc.getFields()) {
384 if (AnnotationExists(field.getProto(), SKIP_ANNOTATION_ID)) {
385 continue;
386 }
387 auto field_name = field.getProto().getName();
388 auto member_name = field_name;
389 GetAnnotationText(field.getProto(), NAME_ANNOTATION_ID, &member_name);
390 inl << " static decltype(auto) get(std::integral_constant<size_t, " << i << ">) { return "
391 << "&" << proxied_class_type << "::" << member_name << "; }\n";
392 ++i;
393 }
394 inl << " static constexpr size_t fields = " << i << ";\n";
395 inl << "};\n";
396 }
397 }
398
399 if (proxied_class_type.size() && node.getProto().isInterface()) {
400 const auto& interface = node.asInterface();
401
402 std::ostringstream client;
403 client << "template<>\nstruct ProxyClient<" << message_namespace << "::" << node_name << "> final : ";
404 client << "public ProxyClientCustom<" << message_namespace << "::" << node_name << ", "
405 << proxied_class_type << ">\n{\n";
406 client << "public:\n";
407 client << " using ProxyClientCustom::ProxyClientCustom;\n";
408 client << " ~ProxyClient();\n";
409
410 std::ostringstream server;
411 server << "template<>\nstruct ProxyServer<" << message_namespace << "::" << node_name << "> : public "
412 << "ProxyServerCustom<" << message_namespace << "::" << node_name << ", " << proxied_class_type
413 << ">\n{\n";
414 server << "public:\n";
415 server << " using ProxyServerCustom::ProxyServerCustom;\n";
416 server << " ~ProxyServer();\n";
417
418 const std::ostringstream client_construct;
419 const std::ostringstream client_destroy;
420
421 int method_ordinal = 0;
422 ForEachMethod(interface, [&] (const capnp::InterfaceSchema& method_interface, const capnp::InterfaceSchema::Method& method) {
423 const kj::StringPtr method_name = method.getProto().getName();
424 kj::StringPtr proxied_method_name = method_name;
425 GetAnnotationText(method.getProto(), NAME_ANNOTATION_ID, &proxied_method_name);
426
427 const std::string method_prefix = Format() << message_namespace << "::" << method_interface.getShortDisplayName()
428 << "::" << Cap(method_name);
429 const bool is_construct = method_name == kj::StringPtr{"construct"};
430 const bool is_destroy = method_name == kj::StringPtr{"destroy"};
431
432 struct Field
433 {
434 ::capnp::StructSchema::Field param;
435 bool param_is_set = false;
436 ::capnp::StructSchema::Field result;
437 bool result_is_set = false;
438 int args = 0;
439 bool retval = false;
440 bool optional = false;
441 bool requested = false;
442 bool skip = false;
443 kj::StringPtr exception;
444 };
445
446 std::vector<Field> fields;
447 std::map<kj::StringPtr, int> field_idx; // name -> args index
448 bool has_result = false;
449
450 auto add_field = [&](const ::capnp::StructSchema::Field& schema_field, bool param) {
451 if (AnnotationExists(schema_field.getProto(), SKIP_ANNOTATION_ID)) {
452 return;
453 }
454
455 auto field_name = schema_field.getProto().getName();
456 auto inserted = field_idx.emplace(field_name, fields.size());
457 if (inserted.second) {
458 fields.emplace_back();
459 }
460 auto& field = fields[inserted.first->second];
461 if (param) {
462 field.param = schema_field;
463 field.param_is_set = true;
464 } else {
465 field.result = schema_field;
466 field.result_is_set = true;
467 }
468
469 if (!param && field_name == kj::StringPtr{"result"}) {
470 field.retval = true;
471 has_result = true;
472 }
473
474 GetAnnotationText(schema_field.getProto(), EXCEPTION_ANNOTATION_ID, &field.exception);
475
476 int32_t count = 1;
477 if (!GetAnnotationInt32(schema_field.getProto(), COUNT_ANNOTATION_ID, &count)) {
478 if (schema_field.getType().isStruct()) {
479 GetAnnotationInt32(schema_field.getType().asStruct().getProto(),
481 } else if (schema_field.getType().isInterface()) {
482 GetAnnotationInt32(schema_field.getType().asInterface().getProto(),
484 }
485 }
486
487
488 if (inserted.second && !field.retval && !field.exception.size()) {
489 field.args = count;
490 }
491 };
492
493 for (const auto schema_field : method.getParamType().getFields()) {
494 add_field(schema_field, true);
495 }
496 for (const auto schema_field : method.getResultType().getFields()) {
497 add_field(schema_field, false);
498 }
499 for (auto& field : field_idx) {
500 auto has_field = field_idx.find("has" + Cap(field.first));
501 if (has_field != field_idx.end()) {
502 fields[has_field->second].skip = true;
503 fields[field.second].optional = true;
504 }
505 auto want_field = field_idx.find("want" + Cap(field.first));
506 if (want_field != field_idx.end() && fields[want_field->second].param_is_set) {
507 fields[want_field->second].skip = true;
508 fields[field.second].requested = true;
509 }
510 }
511
512 if (!is_construct && !is_destroy && (&method_interface == &interface)) {
513 methods << "template<>\n";
514 methods << "struct ProxyMethod<" << method_prefix << "Params>\n";
515 methods << "{\n";
516 methods << " static constexpr auto impl = &" << proxied_class_type
517 << "::" << proxied_method_name << ";\n";
518 methods << "};\n\n";
519 }
520
521 std::ostringstream client_args;
522 std::ostringstream client_invoke;
523 std::ostringstream server_invoke_start;
524 std::ostringstream server_invoke_end;
525 int argc = 0;
526 for (const auto& field : fields) {
527 if (field.skip) continue;
528
529 const auto& f = field.param_is_set ? field.param : field.result;
530 auto field_name = f.getProto().getName();
531 auto field_type = f.getType();
532
533 std::ostringstream field_flags;
534 if (!field.param_is_set) {
535 field_flags << "FIELD_OUT";
536 } else if (field.result_is_set) {
537 field_flags << "FIELD_IN | FIELD_OUT";
538 } else {
539 field_flags << "FIELD_IN";
540 }
541 if (field.optional) field_flags << " | FIELD_OPTIONAL";
542 if (field.requested) field_flags << " | FIELD_REQUESTED";
543 if (BoxedType(field_type)) field_flags << " | FIELD_BOXED";
544
545 add_accessor(field_name);
546
547 std::ostringstream fwd_args;
548 for (int i = 0; i < field.args; ++i) {
549 if (argc > 0) client_args << ",";
550
551 // Add to client method parameter list.
552 client_args << "M" << method_ordinal << "::Param<" << argc << "> " << field_name;
553 if (field.args > 1) client_args << i;
554
555 // Add to MakeClientParam argument list using Fwd helper for perfect forwarding.
556 if (i > 0) fwd_args << ", ";
557 fwd_args << "M" << method_ordinal << "::Fwd<" << argc << ">(" << field_name;
558 if (field.args > 1) fwd_args << i;
559 fwd_args << ")";
560
561 ++argc;
562 }
563 client_invoke << ", ";
564
565 if (field.exception.size()) {
566 client_invoke << "ClientException<" << field.exception << ", ";
567 } else {
568 client_invoke << "MakeClientParam<";
569 }
570
571 client_invoke << "Accessor<" << base_name << "_fields::" << Cap(field_name) << ", "
572 << field_flags.str() << ">>(";
573
574 if (field.retval) {
575 client_invoke << field_name;
576 } else {
577 client_invoke << fwd_args.str();
578 }
579 client_invoke << ")";
580
581 if (field.exception.size()) {
582 server_invoke_start << "Make<ServerExcept, " << field.exception;
583 } else if (field.retval) {
584 server_invoke_start << "Make<ServerRet";
585 } else {
586 server_invoke_start << "MakeServerField<" << field.args;
587 }
588 server_invoke_start << ", Accessor<" << base_name << "_fields::" << Cap(field_name) << ", "
589 << field_flags.str() << ">>(";
590 server_invoke_end << ")";
591 }
592
593 const std::string static_str{is_construct || is_destroy ? "static " : ""};
594 const std::string super_str{is_construct || is_destroy ? "Super& super" : ""};
595 const std::string self_str{is_construct || is_destroy ? "super" : "*this"};
596
597 client << " using M" << method_ordinal << " = ProxyClientMethodTraits<" << method_prefix
598 << "Params>;\n";
599 client << " " << static_str << "typename M" << method_ordinal << "::Result " << method_name << "("
600 << super_str << client_args.str() << ")";
601 client << ";\n";
602 def_client << "ProxyClient<" << message_namespace << "::" << node_name << ">::M" << method_ordinal
603 << "::Result ProxyClient<" << message_namespace << "::" << node_name << ">::" << method_name
604 << "(" << super_str << client_args.str() << ") {\n";
605 if (has_result) {
606 def_client << " typename M" << method_ordinal << "::Result result;\n";
607 }
608 def_client << " clientInvoke(" << self_str << ", &" << message_namespace << "::" << node_name
609 << "::Client::" << method_name << "Request" << client_invoke.str() << ");\n";
610 if (has_result) def_client << " return result;\n";
611 def_client << "}\n";
612
613 server << " kj::Promise<void> " << method_name << "(" << Cap(method_name)
614 << "Context call_context) override;\n";
615
616 def_server << "kj::Promise<void> ProxyServer<" << message_namespace << "::" << node_name
617 << ">::" << method_name << "(" << Cap(method_name)
618 << "Context call_context) {\n"
619 " return serverInvoke(*this, call_context, "
620 << server_invoke_start.str();
621 if (is_destroy) {
622 def_server << "ServerDestroy()";
623 } else {
624 def_server << "ServerCall()";
625 }
626 def_server << server_invoke_end.str() << ");\n}\n";
627 ++method_ordinal;
628 });
629
630 client << "};\n";
631 server << "};\n";
632 dec << "\n" << client.str() << "\n" << server.str() << "\n";
633 KJ_IF_MAYBE(bracket, proxied_class_type.findFirst('<')) {
634 // Skip ProxyType definition for complex type expressions which
635 // could lead to duplicate definitions. They can be defined
636 // manually if actually needed.
637 } else {
638 dec << "template<>\nstruct ProxyType<" << proxied_class_type << ">\n{\n";
639 dec << " using Type = " << proxied_class_type << ";\n";
640 dec << " using Message = " << message_namespace << "::" << node_name << ";\n";
641 dec << " using Client = ProxyClient<Message>;\n";
642 dec << " using Server = ProxyServer<Message>;\n";
643 dec << "};\n";
644 int_client << "ProxyTypeRegister t" << node_nested.getId() << "{TypeList<" << proxied_class_type << ">{}};\n";
645 }
646 def_types << "ProxyClient<" << message_namespace << "::" << node_name
647 << ">::~ProxyClient() { clientDestroy(*this); " << client_destroy.str() << " }\n";
648 def_types << "ProxyServer<" << message_namespace << "::" << node_name
649 << ">::~ProxyServer() { serverDestroy(*this); }\n";
650 }
651 }
652
653 h << methods.str() << "namespace " << base_name << "_fields {\n"
654 << accessors.str() << "} // namespace " << base_name << "_fields\n"
655 << dec.str();
656
657 cpp_server << def_server.str();
658 cpp_server << "} // namespace mp\n";
659
660 cpp_client << def_client.str();
661 cpp_client << "namespace {\n" << int_client.str() << "} // namespace\n";
662 cpp_client << "} // namespace mp\n";
663
664 cpp_types << def_types.str();
665 cpp_types << "} // namespace mp\n";
666
667 inl << "} // namespace mp\n";
668 inl << "#endif\n";
669
670 h << "} // namespace mp\n";
671 h << "#if defined(__GNUC__)\n";
672 h << "#pragma GCC diagnostic pop\n";
673 h << "#endif\n";
674 h << "#endif\n";
675}
676
677int main(int argc, char** argv)
678{
679 if (argc < 3) {
680 std::cerr << "Usage: " << PROXY_BIN << " SRC_PREFIX INCLUDE_PREFIX SRC_FILE [IMPORT_PATH...]\n";
681 exit(1);
682 }
683 std::vector<kj::StringPtr> import_paths;
684 std::vector<kj::Own<const kj::ReadableDirectory>> import_dirs;
685 auto fs = kj::newDiskFilesystem();
686 auto cwd = fs->getCurrentPath();
687 kj::Own<const kj::ReadableDirectory> src_dir;
688 KJ_IF_MAYBE(dir, fs->getRoot().tryOpenSubdir(cwd.evalNative(argv[1]))) {
689 src_dir = kj::mv(*dir);
690 } else {
691 throw std::runtime_error(std::string("Failed to open src_prefix prefix directory: ") + argv[1]);
692 }
693 for (int i = 4; i < argc; ++i) {
694 KJ_IF_MAYBE(dir, fs->getRoot().tryOpenSubdir(cwd.evalNative(argv[i]))) {
695 import_paths.emplace_back(argv[i]);
696 import_dirs.emplace_back(kj::mv(*dir));
697 } else {
698 throw std::runtime_error(std::string("Failed to open import directory: ") + argv[i]);
699 }
700 }
701 for (const char* path : {CMAKE_INSTALL_PREFIX "/include", capnp_PREFIX "/include"}) {
702 KJ_IF_MAYBE(dir, fs->getRoot().tryOpenSubdir(cwd.evalNative(path))) {
703 import_paths.emplace_back(path);
704 import_dirs.emplace_back(kj::mv(*dir));
705 }
706 // No exception thrown if _PREFIX directories do not exist
707 }
708 Generate(argv[1], argv[2], argv[3], import_paths, *src_dir, import_dirs);
709 return 0;
710}
ArgsManager & args
Definition: bitcoind.cpp:277
kj::ArrayPtr< const char > CharSlice
Definition: gen.cpp:92
constexpr uint64_t EXCEPTION_ANNOTATION_ID
Definition: gen.cpp:43
constexpr uint64_t NAME_ANNOTATION_ID
Definition: gen.cpp:44
static OutputStream & operator<<(OutputStream &os, const Array &array)
Definition: gen.cpp:96
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 SKIP_ANNOTATION_ID
Definition: gen.cpp:45
int main(int argc, char **argv)
Definition: gen.cpp:677
constexpr uint64_t WRAP_ANNOTATION_ID
Definition: gen.cpp:41
constexpr uint64_t INCLUDE_ANNOTATION_ID
Definition: gen.cpp:39
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
static bool GetAnnotationInt32(const Reader &reader, uint64_t id, int32_t *result)
Definition: gen.cpp:71
static bool BoxedType(const ::capnp::Type &type)
Definition: gen.cpp:121
#define PROXY_TYPES
Definition: gen.cpp:36
#define PROXY_BIN
Definition: gen.cpp:34
constexpr uint64_t INCLUDE_TYPES_ANNOTATION_ID
Definition: gen.cpp:40
constexpr uint64_t NAMESPACE_ANNOTATION_ID
Definition: gen.cpp:38
static bool AnnotationExists(const Reader &reader, uint64_t id)
Definition: gen.cpp:48
static std::string Cap(kj::StringPtr str)
Definition: gen.cpp:114
static bool GetAnnotationText(const Reader &reader, uint64_t id, kj::StringPtr *result)
Definition: gen.cpp:59
#define PROXY_DECL
Definition: gen.cpp:35
constexpr uint64_t COUNT_ANNOTATION_ID
Definition: gen.cpp:42
int WaitProcess(int pid)
Wait for a process to exit and return its exit code.
Definition: util.cpp:186
void ExecProcess(const std::vector< std::string > &args)
Call execvp with vector args.
Definition: util.cpp:174
Definition: messages.h:21
const char * name
Definition: rest.cpp:48
Definition: gen.cpp:103
Format & operator<<(Value &&value)
Definition: gen.cpp:105
std::ostringstream m_os
Definition: gen.cpp:111
static int count