Serenity Operating System
1/*
2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6
7#include <AK/Debug.h>
8#include <AK/Function.h>
9#include <AK/GenericLexer.h>
10#include <AK/HashMap.h>
11#include <AK/SourceGenerator.h>
12#include <AK/StringBuilder.h>
13#include <LibCore/File.h>
14#include <LibMain/Main.h>
15#include <ctype.h>
16#include <stdio.h>
17
18struct Parameter {
19 Vector<DeprecatedString> attributes;
20 DeprecatedString type;
21 DeprecatedString name;
22};
23
24static DeprecatedString pascal_case(DeprecatedString const& identifier)
25{
26 StringBuilder builder;
27 bool was_new_word = true;
28 for (auto ch : identifier) {
29 if (ch == '_') {
30 was_new_word = true;
31 continue;
32 }
33 if (was_new_word) {
34 builder.append(toupper(ch));
35 was_new_word = false;
36 } else
37 builder.append(ch);
38 }
39 return builder.to_deprecated_string();
40}
41
42struct Message {
43 DeprecatedString name;
44 bool is_synchronous { false };
45 Vector<Parameter> inputs;
46 Vector<Parameter> outputs;
47
48 DeprecatedString response_name() const
49 {
50 StringBuilder builder;
51 builder.append(pascal_case(name));
52 builder.append("Response"sv);
53 return builder.to_deprecated_string();
54 }
55};
56
57struct Endpoint {
58 Vector<DeprecatedString> includes;
59 DeprecatedString name;
60 u32 magic;
61 Vector<Message> messages;
62};
63
64static bool is_primitive_type(DeprecatedString const& type)
65{
66 return type.is_one_of("u8", "i8", "u16", "i16", "u32", "i32", "u64", "i64", "size_t", "bool", "double", "float", "int", "unsigned", "unsigned int");
67}
68
69static bool is_simple_type(DeprecatedString const& type)
70{
71 // Small types that it makes sense just to pass by value.
72 return type.is_one_of("Gfx::Color", "Gfx::IntPoint", "Gfx::FloatPoint", "Gfx::IntSize", "Gfx::FloatSize", "Core::File::OpenMode");
73}
74
75static bool is_primitive_or_simple_type(DeprecatedString const& type)
76{
77 return is_primitive_type(type) || is_simple_type(type);
78}
79
80static DeprecatedString message_name(DeprecatedString const& endpoint, DeprecatedString const& message, bool is_response)
81{
82 StringBuilder builder;
83 builder.append("Messages::"sv);
84 builder.append(endpoint);
85 builder.append("::"sv);
86 builder.append(pascal_case(message));
87 if (is_response)
88 builder.append("Response"sv);
89 return builder.to_deprecated_string();
90}
91
92Vector<Endpoint> parse(ByteBuffer const& file_contents)
93{
94 GenericLexer lexer(file_contents);
95
96 Vector<Endpoint> endpoints;
97
98 auto assert_specific = [&lexer](char ch) {
99 if (lexer.peek() != ch)
100 warnln("assert_specific: wanted '{}', but got '{}' at index {}", ch, lexer.peek(), lexer.tell());
101 bool saw_expected = lexer.consume_specific(ch);
102 VERIFY(saw_expected);
103 };
104
105 auto consume_whitespace = [&lexer] {
106 lexer.ignore_while([](char ch) { return isspace(ch); });
107 if (lexer.peek() == '/' && lexer.peek(1) == '/')
108 lexer.ignore_until('\n');
109 };
110
111 auto parse_parameter = [&](Vector<Parameter>& storage) {
112 for (;;) {
113 Parameter parameter;
114 if (lexer.is_eof()) {
115 warnln("EOF when parsing parameter");
116 VERIFY_NOT_REACHED();
117 }
118 consume_whitespace();
119 if (lexer.peek() == ')')
120 break;
121 if (lexer.consume_specific('[')) {
122 for (;;) {
123 if (lexer.consume_specific(']')) {
124 consume_whitespace();
125 break;
126 }
127 if (lexer.consume_specific(',')) {
128 consume_whitespace();
129 }
130 auto attribute = lexer.consume_until([](char ch) { return ch == ']' || ch == ','; });
131 parameter.attributes.append(attribute);
132 consume_whitespace();
133 }
134 }
135 // FIXME: This is not entirely correct. Types can have spaces, for example `HashMap<int, DeprecatedString>`.
136 // Maybe we should use LibCpp::Parser for parsing types.
137 parameter.type = lexer.consume_until([](char ch) { return isspace(ch); });
138 VERIFY(!lexer.is_eof());
139 consume_whitespace();
140 parameter.name = lexer.consume_until([](char ch) { return isspace(ch) || ch == ',' || ch == ')'; });
141 consume_whitespace();
142 storage.append(move(parameter));
143 if (lexer.consume_specific(','))
144 continue;
145 if (lexer.peek() == ')')
146 break;
147 }
148 };
149
150 auto parse_parameters = [&](Vector<Parameter>& storage) {
151 for (;;) {
152 consume_whitespace();
153 parse_parameter(storage);
154 consume_whitespace();
155 if (lexer.consume_specific(','))
156 continue;
157 if (lexer.peek() == ')')
158 break;
159 }
160 };
161
162 auto parse_message = [&] {
163 Message message;
164 consume_whitespace();
165 message.name = lexer.consume_until([](char ch) { return isspace(ch) || ch == '('; });
166 consume_whitespace();
167 assert_specific('(');
168 parse_parameters(message.inputs);
169 assert_specific(')');
170 consume_whitespace();
171 assert_specific('=');
172
173 auto type = lexer.consume();
174 if (type == '>')
175 message.is_synchronous = true;
176 else if (type == '|')
177 message.is_synchronous = false;
178 else
179 VERIFY_NOT_REACHED();
180
181 consume_whitespace();
182
183 if (message.is_synchronous) {
184 assert_specific('(');
185 parse_parameters(message.outputs);
186 assert_specific(')');
187 }
188
189 consume_whitespace();
190
191 endpoints.last().messages.append(move(message));
192 };
193
194 auto parse_messages = [&] {
195 for (;;) {
196 consume_whitespace();
197 if (lexer.peek() == '}')
198 break;
199 parse_message();
200 consume_whitespace();
201 }
202 };
203
204 auto parse_include = [&] {
205 DeprecatedString include;
206 consume_whitespace();
207 include = lexer.consume_while([](char ch) { return ch != '\n'; });
208 consume_whitespace();
209
210 endpoints.last().includes.append(move(include));
211 };
212
213 auto parse_includes = [&] {
214 for (;;) {
215 consume_whitespace();
216 if (lexer.peek() != '#')
217 break;
218 parse_include();
219 consume_whitespace();
220 }
221 };
222
223 auto parse_endpoint = [&] {
224 endpoints.empend();
225 consume_whitespace();
226 parse_includes();
227 consume_whitespace();
228 lexer.consume_specific("endpoint");
229 consume_whitespace();
230 endpoints.last().name = lexer.consume_while([](char ch) { return !isspace(ch); });
231 endpoints.last().magic = Traits<DeprecatedString>::hash(endpoints.last().name);
232 consume_whitespace();
233 assert_specific('{');
234 parse_messages();
235 assert_specific('}');
236 consume_whitespace();
237 };
238
239 while (lexer.tell() < file_contents.size())
240 parse_endpoint();
241
242 return endpoints;
243}
244
245HashMap<DeprecatedString, int> build_message_ids_for_endpoint(SourceGenerator generator, Endpoint const& endpoint)
246{
247 HashMap<DeprecatedString, int> message_ids;
248
249 generator.appendln("\nenum class MessageID : i32 {");
250 for (auto const& message : endpoint.messages) {
251
252 message_ids.set(message.name, message_ids.size() + 1);
253 generator.set("message.pascal_name", pascal_case(message.name));
254 generator.set("message.id", DeprecatedString::number(message_ids.size()));
255
256 generator.appendln(" @message.pascal_name@ = @message.id@,");
257 if (message.is_synchronous) {
258 message_ids.set(message.response_name(), message_ids.size() + 1);
259 generator.set("message.pascal_name", pascal_case(message.response_name()));
260 generator.set("message.id", DeprecatedString::number(message_ids.size()));
261
262 generator.appendln(" @message.pascal_name@ = @message.id@,");
263 }
264 }
265 generator.appendln("};");
266 return message_ids;
267}
268
269DeprecatedString constructor_for_message(DeprecatedString const& name, Vector<Parameter> const& parameters)
270{
271 StringBuilder builder;
272 builder.append(name);
273
274 if (parameters.is_empty()) {
275 builder.append("() {}"sv);
276 return builder.to_deprecated_string();
277 }
278 builder.append('(');
279 for (size_t i = 0; i < parameters.size(); ++i) {
280 auto const& parameter = parameters[i];
281 builder.appendff("{} {}", parameter.type, parameter.name);
282 if (i != parameters.size() - 1)
283 builder.append(", "sv);
284 }
285 builder.append(") : "sv);
286 for (size_t i = 0; i < parameters.size(); ++i) {
287 auto const& parameter = parameters[i];
288 builder.appendff("m_{}(move({}))", parameter.name, parameter.name);
289 if (i != parameters.size() - 1)
290 builder.append(", "sv);
291 }
292 builder.append(" {}"sv);
293 return builder.to_deprecated_string();
294}
295
296void do_message(SourceGenerator message_generator, DeprecatedString const& name, Vector<Parameter> const& parameters, DeprecatedString const& response_type = {})
297{
298 auto pascal_name = pascal_case(name);
299 message_generator.set("message.name", name);
300 message_generator.set("message.pascal_name", pascal_name);
301 message_generator.set("message.response_type", response_type);
302 message_generator.set("message.constructor", constructor_for_message(pascal_name, parameters));
303
304 message_generator.appendln(R"~~~(
305class @message.pascal_name@ final : public IPC::Message {
306public:)~~~");
307
308 if (!response_type.is_null())
309 message_generator.appendln(R"~~~(
310 typedef class @message.response_type@ ResponseType;)~~~");
311
312 message_generator.appendln(R"~~~(
313 @message.pascal_name@(decltype(nullptr)) : m_ipc_message_valid(false) { }
314 @message.pascal_name@(@message.pascal_name@ const&) = default;
315 @message.pascal_name@(@message.pascal_name@&&) = default;
316 @message.pascal_name@& operator=(@message.pascal_name@ const&) = default;
317 @message.constructor@)~~~");
318
319 if (parameters.size() == 1) {
320 auto const& parameter = parameters[0];
321 message_generator.set("parameter.type"sv, parameter.type);
322 message_generator.set("parameter.name"sv, parameter.name);
323
324 message_generator.appendln(R"~~~(
325 template <typename WrappedReturnType>
326 requires(!SameAs<WrappedReturnType, @parameter.type@>)
327 @message.pascal_name@(WrappedReturnType&& value)
328 : m_@parameter.name@(forward<WrappedReturnType>(value))
329 {
330 })~~~");
331 }
332
333 message_generator.appendln(R"~~~(
334 virtual ~@message.pascal_name@() override {}
335
336 virtual u32 endpoint_magic() const override { return @endpoint.magic@; }
337 virtual i32 message_id() const override { return (int)MessageID::@message.pascal_name@; }
338 static i32 static_message_id() { return (int)MessageID::@message.pascal_name@; }
339 virtual const char* message_name() const override { return "@endpoint.name@::@message.pascal_name@"; }
340
341 static ErrorOr<NonnullOwnPtr<@message.pascal_name@>> decode(Stream& stream, Core::LocalSocket& socket)
342 {
343 IPC::Decoder decoder { stream, socket };)~~~");
344
345 for (auto const& parameter : parameters) {
346 auto parameter_generator = message_generator.fork();
347
348 parameter_generator.set("parameter.type", parameter.type);
349 parameter_generator.set("parameter.name", parameter.name);
350
351 if (parameter.type == "bool")
352 parameter_generator.set("parameter.initial_value", "false");
353 else
354 parameter_generator.set("parameter.initial_value", "{}");
355
356 parameter_generator.appendln(R"~~~(
357 auto @parameter.name@ = TRY((decoder.decode<@parameter.type@>()));)~~~");
358
359 if (parameter.attributes.contains_slow("UTF8")) {
360 parameter_generator.appendln(R"~~~(
361 if (!Utf8View(@parameter.name@).validate())
362 return Error::from_string_literal("Decoded @parameter.name@ is invalid UTF-8");)~~~");
363 }
364 }
365
366 StringBuilder builder;
367 for (size_t i = 0; i < parameters.size(); ++i) {
368 auto const& parameter = parameters[i];
369 builder.appendff("move({})", parameter.name);
370 if (i != parameters.size() - 1)
371 builder.append(", "sv);
372 }
373
374 message_generator.set("message.constructor_call_parameters", builder.to_deprecated_string());
375 message_generator.appendln(R"~~~(
376 return make<@message.pascal_name@>(@message.constructor_call_parameters@);
377 })~~~");
378
379 message_generator.appendln(R"~~~(
380 virtual bool valid() const override { return m_ipc_message_valid; }
381
382 virtual ErrorOr<IPC::MessageBuffer> encode() const override
383 {
384 VERIFY(valid());
385
386 IPC::MessageBuffer buffer;
387 IPC::Encoder stream(buffer);
388 TRY(stream.encode(endpoint_magic()));
389 TRY(stream.encode((int)MessageID::@message.pascal_name@));)~~~");
390
391 for (auto const& parameter : parameters) {
392 auto parameter_generator = message_generator.fork();
393
394 parameter_generator.set("parameter.name", parameter.name);
395 parameter_generator.appendln(R"~~~(
396 TRY(stream.encode(m_@parameter.name@));)~~~");
397 }
398
399 message_generator.appendln(R"~~~(
400 return buffer;
401 })~~~");
402
403 for (auto const& parameter : parameters) {
404 auto parameter_generator = message_generator.fork();
405 parameter_generator.set("parameter.type", parameter.type);
406 parameter_generator.set("parameter.name", parameter.name);
407 parameter_generator.appendln(R"~~~(
408 const @parameter.type@& @parameter.name@() const { return m_@parameter.name@; }
409 @parameter.type@ take_@parameter.name@() { return move(m_@parameter.name@); })~~~");
410 }
411
412 message_generator.appendln(R"~~~(
413private:
414 bool m_ipc_message_valid { true };)~~~");
415
416 for (auto const& parameter : parameters) {
417 auto parameter_generator = message_generator.fork();
418 parameter_generator.set("parameter.type", parameter.type);
419 parameter_generator.set("parameter.name", parameter.name);
420 parameter_generator.appendln(R"~~~(
421 @parameter.type@ m_@parameter.name@ {};)~~~");
422 }
423
424 message_generator.appendln("\n};");
425}
426
427void do_message_for_proxy(SourceGenerator message_generator, Endpoint const& endpoint, Message const& message)
428{
429 auto do_implement_proxy = [&](DeprecatedString const& name, Vector<Parameter> const& parameters, bool is_synchronous, bool is_try) {
430 DeprecatedString return_type = "void";
431 if (is_synchronous) {
432 if (message.outputs.size() == 1)
433 return_type = message.outputs[0].type;
434 else if (!message.outputs.is_empty())
435 return_type = message_name(endpoint.name, message.name, true);
436 }
437 DeprecatedString inner_return_type = return_type;
438 if (is_try)
439 return_type = DeprecatedString::formatted("IPC::IPCErrorOr<{}>", return_type);
440
441 message_generator.set("message.name", message.name);
442 message_generator.set("message.pascal_name", pascal_case(message.name));
443 message_generator.set("message.complex_return_type", return_type);
444 message_generator.set("async_prefix_maybe", is_synchronous ? "" : "async_");
445 message_generator.set("try_prefix_maybe", is_try ? "try_" : "");
446
447 message_generator.set("handler_name", name);
448 message_generator.appendln(R"~~~(
449 @message.complex_return_type@ @try_prefix_maybe@@async_prefix_maybe@@handler_name@()~~~");
450
451 for (size_t i = 0; i < parameters.size(); ++i) {
452 auto const& parameter = parameters[i];
453 auto argument_generator = message_generator.fork();
454 argument_generator.set("argument.type", parameter.type);
455 argument_generator.set("argument.name", parameter.name);
456 argument_generator.append("@argument.type@ @argument.name@");
457 if (i != parameters.size() - 1)
458 argument_generator.append(", ");
459 }
460
461 message_generator.append(") {");
462
463 if (is_synchronous && !is_try) {
464 if (return_type != "void") {
465 message_generator.append(R"~~~(
466 return )~~~");
467 if (message.outputs.size() != 1)
468 message_generator.append("move(*");
469 } else {
470 message_generator.append(R"~~~(
471 (void) )~~~");
472 }
473
474 message_generator.append("m_connection.template send_sync<Messages::@endpoint.name@::@message.pascal_name@>(");
475 } else if (is_try) {
476 message_generator.append(R"~~~(
477 auto result = m_connection.template send_sync_but_allow_failure<Messages::@endpoint.name@::@message.pascal_name@>()~~~");
478 } else {
479 message_generator.append(R"~~~(
480 // FIXME: Handle post_message failures.
481 (void) m_connection.post_message(Messages::@endpoint.name@::@message.pascal_name@ { )~~~");
482 }
483
484 for (size_t i = 0; i < parameters.size(); ++i) {
485 auto const& parameter = parameters[i];
486 auto argument_generator = message_generator.fork();
487 argument_generator.set("argument.name", parameter.name);
488 if (is_primitive_or_simple_type(parameters[i].type))
489 argument_generator.append("@argument.name@");
490 else
491 argument_generator.append("move(@argument.name@)");
492 if (i != parameters.size() - 1)
493 argument_generator.append(", ");
494 }
495
496 if (is_synchronous && !is_try) {
497 if (return_type != "void") {
498 message_generator.append(")");
499 }
500
501 if (message.outputs.size() == 1) {
502 message_generator.append("->take_");
503 message_generator.append(message.outputs[0].name);
504 message_generator.append("()");
505 } else
506 message_generator.append(")");
507
508 message_generator.append(";");
509 } else if (is_try) {
510 message_generator.append(R"~~~();
511 if (!result)
512 return IPC::ErrorCode::PeerDisconnected;)~~~");
513 if (inner_return_type != "void") {
514 message_generator.appendln(R"~~~(
515 return move(*result);)~~~");
516 } else {
517 message_generator.appendln(R"~~~(
518 return { };)~~~");
519 }
520 } else {
521 message_generator.appendln(" });");
522 }
523
524 message_generator.appendln(R"~~~(
525 })~~~");
526 };
527
528 do_implement_proxy(message.name, message.inputs, message.is_synchronous, false);
529 if (message.is_synchronous) {
530 do_implement_proxy(message.name, message.inputs, false, false);
531 do_implement_proxy(message.name, message.inputs, true, true);
532 }
533}
534
535void build_endpoint(SourceGenerator generator, Endpoint const& endpoint)
536{
537 generator.set("endpoint.name", endpoint.name);
538 generator.set("endpoint.magic", DeprecatedString::number(endpoint.magic));
539
540 generator.appendln("\nnamespace Messages::@endpoint.name@ {");
541
542 HashMap<DeprecatedString, int> message_ids = build_message_ids_for_endpoint(generator.fork(), endpoint);
543
544 for (auto const& message : endpoint.messages) {
545 DeprecatedString response_name;
546 if (message.is_synchronous) {
547 response_name = message.response_name();
548 do_message(generator.fork(), response_name, message.outputs);
549 }
550 do_message(generator.fork(), message.name, message.inputs, response_name);
551 }
552
553 generator.appendln(R"~~~(
554} // namespace Messages::@endpoint.name@
555
556template<typename LocalEndpoint, typename PeerEndpoint>
557class @endpoint.name@Proxy {
558public:
559 // Used to disambiguate the constructor call.
560 struct Tag { };
561
562 @endpoint.name@Proxy(IPC::Connection<LocalEndpoint, PeerEndpoint>& connection, Tag)
563 : m_connection(connection)
564 { })~~~");
565
566 for (auto const& message : endpoint.messages)
567 do_message_for_proxy(generator.fork(), endpoint, message);
568
569 generator.appendln(R"~~~(
570private:
571 IPC::Connection<LocalEndpoint, PeerEndpoint>& m_connection;
572};)~~~");
573
574 generator.append(R"~~~(
575template<typename LocalEndpoint, typename PeerEndpoint>
576class @endpoint.name@Proxy;
577class @endpoint.name@Stub;
578
579class @endpoint.name@Endpoint {
580public:
581 template<typename LocalEndpoint>
582 using Proxy = @endpoint.name@Proxy<LocalEndpoint, @endpoint.name@Endpoint>;
583 using Stub = @endpoint.name@Stub;
584
585 static u32 static_magic() { return @endpoint.magic@; }
586
587 static ErrorOr<NonnullOwnPtr<IPC::Message>> decode_message(ReadonlyBytes buffer, [[maybe_unused]] Core::LocalSocket& socket)
588 {
589 FixedMemoryStream stream { buffer };
590 auto message_endpoint_magic = TRY(stream.read_value<u32>());)~~~");
591 generator.append(R"~~~(
592
593 if (message_endpoint_magic != @endpoint.magic@) {)~~~");
594 if constexpr (GENERATE_DEBUG) {
595 generator.append(R"~~~(
596 dbgln("@endpoint.name@: Endpoint magic number message_endpoint_magic != @endpoint.magic@, not my message! (the other endpoint may have handled it)");)~~~");
597 }
598 generator.appendln(R"~~~(
599 return Error::from_string_literal("Endpoint magic number mismatch, not my message!");
600 }
601
602 auto message_id = TRY(stream.read_value<i32>());)~~~");
603 generator.appendln(R"~~~(
604
605 switch (message_id) {)~~~");
606
607 for (auto const& message : endpoint.messages) {
608 auto do_decode_message = [&](DeprecatedString const& name) {
609 auto message_generator = generator.fork();
610
611 message_generator.set("message.name", name);
612 message_generator.set("message.pascal_name", pascal_case(name));
613
614 message_generator.append(R"~~~(
615 case (int)Messages::@endpoint.name@::MessageID::@message.pascal_name@:
616 return TRY(Messages::@endpoint.name@::@message.pascal_name@::decode(stream, socket));)~~~");
617 };
618
619 do_decode_message(message.name);
620 if (message.is_synchronous)
621 do_decode_message(message.response_name());
622 }
623
624 generator.append(R"~~~(
625 default:)~~~");
626 if constexpr (GENERATE_DEBUG) {
627 generator.append(R"~~~(
628 dbgln("Failed to decode @endpoint.name@.({})", message_id);)~~~");
629 }
630 generator.appendln(R"~~~(
631 return Error::from_string_literal("Failed to decode @endpoint.name@ message");
632 })~~~");
633
634 generator.appendln(R"~~~(
635 VERIFY_NOT_REACHED();
636 }
637
638};
639
640class @endpoint.name@Stub : public IPC::Stub {
641public:
642 @endpoint.name@Stub() { }
643 virtual ~@endpoint.name@Stub() override { }
644
645 virtual u32 magic() const override { return @endpoint.magic@; }
646 virtual DeprecatedString name() const override { return "@endpoint.name@"; }
647
648 virtual ErrorOr<OwnPtr<IPC::MessageBuffer>> handle(const IPC::Message& message) override
649 {
650 switch (message.message_id()) {)~~~");
651 for (auto const& message : endpoint.messages) {
652 auto do_handle_message = [&](DeprecatedString const& name, Vector<Parameter> const& parameters, bool returns_something) {
653 auto message_generator = generator.fork();
654
655 StringBuilder argument_generator;
656 for (size_t i = 0; i < parameters.size(); ++i) {
657 auto const& parameter = parameters[i];
658 argument_generator.append("request."sv);
659 argument_generator.append(parameter.name);
660 argument_generator.append("()"sv);
661 if (i != parameters.size() - 1)
662 argument_generator.append(", "sv);
663 }
664
665 message_generator.set("message.pascal_name", pascal_case(name));
666 message_generator.set("message.response_type", pascal_case(message.response_name()));
667 message_generator.set("handler_name", name);
668 message_generator.set("arguments", argument_generator.to_deprecated_string());
669 message_generator.appendln(R"~~~(
670 case (int)Messages::@endpoint.name@::MessageID::@message.pascal_name@: {)~~~");
671 if (returns_something) {
672 if (message.outputs.is_empty()) {
673 message_generator.appendln(R"~~~(
674 [[maybe_unused]] auto& request = static_cast<const Messages::@endpoint.name@::@message.pascal_name@&>(message);
675 @handler_name@(@arguments@);
676 auto response = Messages::@endpoint.name@::@message.response_type@ { };
677 return make<IPC::MessageBuffer>(TRY(response.encode()));)~~~");
678 } else {
679 message_generator.appendln(R"~~~(
680 [[maybe_unused]] auto& request = static_cast<const Messages::@endpoint.name@::@message.pascal_name@&>(message);
681 auto response = @handler_name@(@arguments@);
682 if (!response.valid())
683 return Error::from_string_literal("Failed to handle @endpoint.name@::@message.pascal_name@ message");
684 return make<IPC::MessageBuffer>(TRY(response.encode()));)~~~");
685 }
686 } else {
687 message_generator.appendln(R"~~~(
688 [[maybe_unused]] auto& request = static_cast<const Messages::@endpoint.name@::@message.pascal_name@&>(message);
689 @handler_name@(@arguments@);
690 return nullptr;)~~~");
691 }
692 message_generator.appendln(R"~~~(
693 })~~~");
694 };
695 do_handle_message(message.name, message.inputs, message.is_synchronous);
696 }
697 generator.appendln(R"~~~(
698 default:
699 return Error::from_string_literal("Unknown message ID for @endpoint.name@ endpoint");
700 }
701 })~~~");
702
703 for (auto const& message : endpoint.messages) {
704 auto message_generator = generator.fork();
705
706 auto do_handle_message_decl = [&](DeprecatedString const& name, Vector<Parameter> const& parameters, bool is_response) {
707 DeprecatedString return_type = "void";
708 if (message.is_synchronous && !message.outputs.is_empty() && !is_response)
709 return_type = message_name(endpoint.name, message.name, true);
710 message_generator.set("message.complex_return_type", return_type);
711
712 message_generator.set("handler_name", name);
713 message_generator.appendln(R"~~~(
714 virtual @message.complex_return_type@ @handler_name@()~~~");
715
716 auto make_argument_type = [](DeprecatedString const& type) {
717 StringBuilder builder;
718
719 bool const_ref = !is_primitive_or_simple_type(type);
720
721 builder.append(type);
722 if (const_ref)
723 builder.append(" const&"sv);
724
725 return builder.to_deprecated_string();
726 };
727
728 for (size_t i = 0; i < parameters.size(); ++i) {
729 auto const& parameter = parameters[i];
730 auto argument_generator = message_generator.fork();
731 argument_generator.set("argument.type", make_argument_type(parameter.type));
732 argument_generator.set("argument.name", parameter.name);
733 argument_generator.append("[[maybe_unused]] @argument.type@ @argument.name@");
734 if (i != parameters.size() - 1)
735 argument_generator.append(", ");
736 }
737
738 if (is_response) {
739 message_generator.append(") { };");
740 } else {
741 message_generator.appendln(") = 0;");
742 }
743 };
744
745 do_handle_message_decl(message.name, message.inputs, false);
746 }
747
748 generator.appendln(R"~~~(
749private:
750};
751
752#if defined(AK_COMPILER_CLANG)
753#pragma clang diagnostic pop
754#endif)~~~");
755}
756
757void build(StringBuilder& builder, Vector<Endpoint> const& endpoints)
758{
759 SourceGenerator generator { builder };
760
761 generator.appendln("#pragma once");
762
763 // This must occur before LibIPC/Decoder.h
764 for (auto const& endpoint : endpoints) {
765 for (auto const& include : endpoint.includes) {
766 generator.appendln(include);
767 }
768 }
769
770 generator.appendln(R"~~~(#include <AK/Error.h>
771#include <AK/MemoryStream.h>
772#include <AK/OwnPtr.h>
773#include <AK/Result.h>
774#include <AK/Utf8View.h>
775#include <LibIPC/Connection.h>
776#include <LibIPC/Decoder.h>
777#include <LibIPC/Dictionary.h>
778#include <LibIPC/Encoder.h>
779#include <LibIPC/File.h>
780#include <LibIPC/Message.h>
781#include <LibIPC/Stub.h>
782
783#if defined(AK_COMPILER_CLANG)
784#pragma clang diagnostic push
785#pragma clang diagnostic ignored "-Wdefaulted-function-deleted"
786#endif)~~~");
787
788 for (auto const& endpoint : endpoints)
789 build_endpoint(generator.fork(), endpoint);
790}
791
792ErrorOr<int> serenity_main(Main::Arguments arguments)
793{
794 if (arguments.argc != 2) {
795 outln("usage: {} <IPC endpoint definition file>", arguments.strings[0]);
796 return 1;
797 }
798
799 auto file = TRY(Core::File::open(arguments.strings[1], Core::File::OpenMode::Read));
800
801 auto file_contents = TRY(file->read_until_eof());
802
803 auto endpoints = parse(file_contents);
804
805 StringBuilder builder;
806 build(builder, endpoints);
807
808 outln("{}", builder.string_view());
809
810 if constexpr (GENERATE_DEBUG) {
811 for (auto& endpoint : endpoints) {
812 warnln("Endpoint '{}' (magic: {})", endpoint.name, endpoint.magic);
813 for (auto& message : endpoint.messages) {
814 warnln(" Message: '{}'", message.name);
815 warnln(" Sync: {}", message.is_synchronous);
816 warnln(" Inputs:");
817 for (auto& parameter : message.inputs)
818 warnln(" Parameter: {} ({})", parameter.name, parameter.type);
819 if (message.inputs.is_empty())
820 warnln(" (none)");
821 if (message.is_synchronous) {
822 warnln(" Outputs:");
823 for (auto& parameter : message.outputs)
824 warnln(" Parameter: {} ({})", parameter.name, parameter.type);
825 if (message.outputs.is_empty())
826 warnln(" (none)");
827 }
828 }
829 }
830 }
831 return 0;
832}