Serenity Operating System
1/*
2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice, this
9 * list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
22 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include <AK/BufferStream.h>
28#include <AK/Function.h>
29#include <AK/HashMap.h>
30#include <AK/StringBuilder.h>
31#include <LibCore/File.h>
32#include <ctype.h>
33#include <stdio.h>
34
35//#define GENERATE_DEBUG_CODE
36
37struct Parameter {
38 String type;
39 String name;
40};
41
42struct Message {
43 String name;
44 bool is_synchronous { false };
45 Vector<Parameter> inputs;
46 Vector<Parameter> outputs;
47
48 String response_name() const
49 {
50 StringBuilder builder;
51 builder.append(name);
52 builder.append("Response");
53 return builder.to_string();
54 }
55};
56
57struct Endpoint {
58 String name;
59 int magic;
60 Vector<Message> messages;
61};
62
63int main(int argc, char** argv)
64{
65 if (argc != 2) {
66 printf("usage: %s <IPC endpoint definition file>\n", argv[0]);
67 return 0;
68 }
69
70 auto file = Core::File::construct(argv[1]);
71 if (!file->open(Core::IODevice::ReadOnly)) {
72 fprintf(stderr, "Error: Cannot open %s: %s\n", argv[1], file->error_string());
73 return 1;
74 }
75
76 auto file_contents = file->read_all();
77
78 Vector<Endpoint> endpoints;
79
80 Vector<char> buffer;
81
82 size_t index = 0;
83
84 auto peek = [&](size_t offset = 0) -> char {
85 if ((index + offset) < file_contents.size())
86 return file_contents[index + offset];
87 return 0;
88 };
89
90 auto consume_one = [&]() -> char {
91 return file_contents[index++];
92 };
93
94 auto extract_while = [&](Function<bool(char)> condition) -> String {
95 StringBuilder builder;
96 while (condition(peek()))
97 builder.append(consume_one());
98 return builder.to_string();
99 };
100
101 auto consume_specific = [&](char ch) {
102 if (peek() != ch) {
103 dbg() << "consume_specific: wanted '" << ch << "', but got '" << peek() << "' at index " << index;
104 }
105 ASSERT(peek() == ch);
106 ++index;
107 return ch;
108 };
109
110 auto consume_string = [&](const char* str) {
111 for (size_t i = 0, length = strlen(str); i < length; ++i)
112 consume_specific(str[i]);
113 };
114
115 auto consume_whitespace = [&] {
116 while (isspace(peek()))
117 ++index;
118 if (peek() == '/' && peek(1) == '/') {
119 while (peek() != '\n')
120 ++index;
121 }
122 };
123
124 auto parse_parameter = [&](Vector<Parameter>& storage) {
125 for (;;) {
126 Parameter parameter;
127 consume_whitespace();
128 if (peek() == ')')
129 break;
130 parameter.type = extract_while([](char ch) { return !isspace(ch); });
131 consume_whitespace();
132 parameter.name = extract_while([](char ch) { return !isspace(ch) && ch != ',' && ch != ')'; });
133 consume_whitespace();
134 storage.append(move(parameter));
135 if (peek() == ',') {
136 consume_one();
137 continue;
138 }
139 if (peek() == ')')
140 break;
141 }
142 };
143
144 auto parse_parameters = [&](Vector<Parameter>& storage) {
145 for (;;) {
146 consume_whitespace();
147 parse_parameter(storage);
148 consume_whitespace();
149 if (peek() == ',') {
150 consume_one();
151 continue;
152 }
153 if (peek() == ')')
154 break;
155 }
156 };
157
158 auto parse_message = [&] {
159 Message message;
160 consume_whitespace();
161 Vector<char> buffer;
162 while (!isspace(peek()) && peek() != '(')
163 buffer.append(consume_one());
164 message.name = String::copy(buffer);
165 consume_whitespace();
166 consume_specific('(');
167 parse_parameters(message.inputs);
168 consume_specific(')');
169 consume_whitespace();
170 consume_specific('=');
171
172 auto type = consume_one();
173 if (type == '>')
174 message.is_synchronous = true;
175 else if (type == '|')
176 message.is_synchronous = false;
177 else
178 ASSERT_NOT_REACHED();
179
180 consume_whitespace();
181
182 if (message.is_synchronous) {
183 consume_specific('(');
184 parse_parameters(message.outputs);
185 consume_specific(')');
186 }
187
188 consume_whitespace();
189
190 endpoints.last().messages.append(move(message));
191 };
192
193 auto parse_messages = [&] {
194 for (;;) {
195 consume_whitespace();
196 parse_message();
197 consume_whitespace();
198 if (peek() == '}')
199 break;
200 }
201 };
202
203 auto parse_endpoint = [&] {
204 endpoints.empend();
205 consume_whitespace();
206 consume_string("endpoint");
207 consume_whitespace();
208 endpoints.last().name = extract_while([](char ch) { return !isspace(ch); });
209 consume_whitespace();
210 consume_specific('=');
211 consume_whitespace();
212 auto magic_string = extract_while([](char ch) { return !isspace(ch) && ch != '{'; });
213 bool ok;
214 endpoints.last().magic = magic_string.to_int(ok);
215 ASSERT(ok);
216 consume_whitespace();
217 consume_specific('{');
218 parse_messages();
219 consume_specific('}');
220 consume_whitespace();
221 };
222
223 while (index < file_contents.size())
224 parse_endpoint();
225
226 dbg() << "#pragma once";
227 dbg() << "#include <AK/BufferStream.h>";
228 dbg() << "#include <AK/OwnPtr.h>";
229 dbg() << "#include <LibGfx/Color.h>";
230 dbg() << "#include <LibGfx/Rect.h>";
231 dbg() << "#include <LibIPC/Decoder.h>";
232 dbg() << "#include <LibIPC/Encoder.h>";
233 dbg() << "#include <LibIPC/Endpoint.h>";
234 dbg() << "#include <LibIPC/Message.h>";
235 dbg();
236
237 for (auto& endpoint : endpoints) {
238 dbg() << "namespace Messages {";
239 dbg() << "namespace " << endpoint.name << " {";
240 dbg();
241
242 HashMap<String, int> message_ids;
243
244 dbg() << "enum class MessageID : i32 {";
245 for (auto& message : endpoint.messages) {
246 message_ids.set(message.name, message_ids.size() + 1);
247 dbg() << " " << message.name << " = " << message_ids.size() << ",";
248 if (message.is_synchronous) {
249 message_ids.set(message.response_name(), message_ids.size() + 1);
250 dbg() << " " << message.response_name() << " = " << message_ids.size() << ",";
251 }
252 }
253 dbg() << "};";
254 dbg();
255
256 auto constructor_for_message = [&](const String& name, const Vector<Parameter>& parameters) {
257 StringBuilder builder;
258 builder.append(name);
259
260 if (parameters.is_empty()) {
261 builder.append("() {}");
262 return builder.to_string();
263 }
264
265 builder.append('(');
266 for (size_t i = 0; i < parameters.size(); ++i) {
267 auto& parameter = parameters[i];
268 builder.append("const ");
269 builder.append(parameter.type);
270 builder.append("& ");
271 builder.append(parameter.name);
272 if (i != parameters.size() - 1)
273 builder.append(", ");
274 }
275 builder.append(") : ");
276 for (size_t i = 0; i < parameters.size(); ++i) {
277 auto& parameter = parameters[i];
278 builder.append("m_");
279 builder.append(parameter.name);
280 builder.append("(");
281 builder.append(parameter.name);
282 builder.append(")");
283 if (i != parameters.size() - 1)
284 builder.append(", ");
285 }
286 builder.append(" {}");
287 return builder.to_string();
288 };
289
290 auto do_message = [&](const String& name, const Vector<Parameter>& parameters, const String& response_type = {}) {
291 dbg() << "class " << name << " final : public IPC::Message {";
292 dbg() << "public:";
293 if (!response_type.is_null())
294 dbg() << " typedef class " << response_type << " ResponseType;";
295 dbg() << " " << constructor_for_message(name, parameters);
296 dbg() << " virtual ~" << name << "() override {}";
297 dbg() << " virtual i32 endpoint_magic() const override { return " << endpoint.magic << "; }";
298 dbg() << " virtual i32 message_id() const override { return (int)MessageID::" << name << "; }";
299 dbg() << " static i32 static_message_id() { return (int)MessageID::" << name << "; }";
300 dbg() << " virtual const char* message_name() const override { return \"" << endpoint.name << "::" << name << "\"; }";
301 dbg() << " static OwnPtr<" << name << "> decode(BufferStream& stream, size_t& size_in_bytes)";
302 dbg() << " {";
303
304 dbg() << " IPC::Decoder decoder(stream);";
305
306 for (auto& parameter : parameters) {
307 String initial_value = "{}";
308 if (parameter.type == "bool")
309 initial_value = "false";
310 dbg() << " " << parameter.type << " " << parameter.name << " = " << initial_value << ";";
311
312 if (parameter.type == "Vector<Gfx::Rect>") {
313 dbg() << " u64 " << parameter.name << "_size = 0;";
314 dbg() << " stream >> " << parameter.name << "_size;";
315 dbg() << " for (size_t i = 0; i < " << parameter.name << "_size; ++i) {";
316 dbg() << " Gfx::Rect rect;";
317 dbg() << " if (!decoder.decode(rect))";
318 dbg() << " return nullptr;";
319 dbg() << " " << parameter.name << ".append(move(rect));";
320 dbg() << " }";
321 } else {
322 dbg() << " if (!decoder.decode(" << parameter.name << "))";
323 dbg() << " return nullptr;";
324 }
325 }
326
327 StringBuilder builder;
328 for (size_t i = 0; i < parameters.size(); ++i) {
329 auto& parameter = parameters[i];
330 builder.append(parameter.name);
331 if (i != parameters.size() - 1)
332 builder.append(", ");
333 }
334 dbg() << " size_in_bytes = stream.offset();";
335 dbg() << " return make<" << name << ">(" << builder.to_string() << ");";
336 dbg() << " }";
337 dbg() << " virtual IPC::MessageBuffer encode() const override";
338 dbg() << " {";
339 dbg() << " IPC::MessageBuffer buffer;";
340 dbg() << " IPC::Encoder stream(buffer);";
341 dbg() << " stream << endpoint_magic();";
342 dbg() << " stream << (int)MessageID::" << name << ";";
343 for (auto& parameter : parameters) {
344 if (parameter.type == "Gfx::Color") {
345 dbg() << " stream << m_" << parameter.name << ".value();";
346 } else if (parameter.type == "Gfx::Size") {
347 dbg() << " stream << m_" << parameter.name << ".width();";
348 dbg() << " stream << m_" << parameter.name << ".height();";
349 } else if (parameter.type == "Gfx::Point") {
350 dbg() << " stream << m_" << parameter.name << ".x();";
351 dbg() << " stream << m_" << parameter.name << ".y();";
352 } else if (parameter.type == "Gfx::Rect") {
353 dbg() << " stream << m_" << parameter.name << ".x();";
354 dbg() << " stream << m_" << parameter.name << ".y();";
355 dbg() << " stream << m_" << parameter.name << ".width();";
356 dbg() << " stream << m_" << parameter.name << ".height();";
357 } else if (parameter.type == "Vector<Gfx::Rect>") {
358 dbg() << " stream << (u64)m_" << parameter.name << ".size();";
359 dbg() << " for (auto& rect : m_" << parameter.name << ") {";
360 dbg() << " stream << rect.x();";
361 dbg() << " stream << rect.y();";
362 dbg() << " stream << rect.width();";
363 dbg() << " stream << rect.height();";
364 dbg() << " }";
365 } else {
366 dbg() << " stream << m_" << parameter.name << ";";
367 }
368 }
369 dbg() << " return buffer;";
370 dbg() << " }";
371 for (auto& parameter : parameters) {
372 dbg() << " const " << parameter.type << "& " << parameter.name << "() const { return m_" << parameter.name << "; }";
373 }
374 dbg() << "private:";
375 for (auto& parameter : parameters) {
376 dbg() << " " << parameter.type << " m_" << parameter.name << ";";
377 }
378 dbg() << "};";
379 dbg();
380 };
381 for (auto& message : endpoint.messages) {
382 String response_name;
383 if (message.is_synchronous) {
384 response_name = message.response_name();
385 do_message(response_name, message.outputs);
386 }
387 do_message(message.name, message.inputs, response_name);
388 }
389 dbg() << "} // namespace " << endpoint.name;
390 dbg() << "} // namespace Messages";
391 dbg();
392
393 dbg() << "class " << endpoint.name << "Endpoint : public IPC::Endpoint {";
394 dbg() << "public:";
395 dbg() << " " << endpoint.name << "Endpoint() {}";
396 dbg() << " virtual ~" << endpoint.name << "Endpoint() override {}";
397 dbg() << " static int static_magic() { return " << endpoint.magic << "; }";
398 dbg() << " virtual int magic() const override { return " << endpoint.magic << "; }";
399 dbg() << " static String static_name() { return \"" << endpoint.name << "\"; };";
400 dbg() << " virtual String name() const override { return \"" << endpoint.name << "\"; };";
401 dbg() << " static OwnPtr<IPC::Message> decode_message(const ByteBuffer& buffer, size_t& size_in_bytes)";
402 dbg() << " {";
403 dbg() << " BufferStream stream(const_cast<ByteBuffer&>(buffer));";
404 dbg() << " i32 message_endpoint_magic = 0;";
405 dbg() << " stream >> message_endpoint_magic;";
406 dbg() << " if (message_endpoint_magic != " << endpoint.magic << ") {";
407#ifdef GENERATE_DEBUG_CODE
408 dbg() << " dbg() << \"endpoint magic \" << message_endpoint_magic << \" != " << endpoint.magic << "\";";
409#endif
410 dbg() << " return nullptr;";
411 dbg() << " }";
412 dbg() << " i32 message_id = 0;";
413 dbg() << " stream >> message_id;";
414 dbg() << " switch (message_id) {";
415 for (auto& message : endpoint.messages) {
416 auto do_decode_message = [&](const String& name) {
417 dbg() << " case (int)Messages::" << endpoint.name << "::MessageID::" << name << ":";
418 dbg() << " return Messages::" << endpoint.name << "::" << name << "::decode(stream, size_in_bytes);";
419 };
420 do_decode_message(message.name);
421 if (message.is_synchronous)
422 do_decode_message(message.response_name());
423 }
424 dbg() << " default:";
425#ifdef GENERATE_DEBUG_CODE
426 dbg() << " dbg() << \"Failed to decode " << endpoint.name << ".(\" << message_id << \")\";";
427#endif
428 dbg() << " return nullptr;";
429
430 dbg() << " }";
431 dbg() << " }";
432 dbg();
433 dbg() << " virtual OwnPtr<IPC::Message> handle(const IPC::Message& message) override";
434 dbg() << " {";
435 dbg() << " switch (message.message_id()) {";
436 for (auto& message : endpoint.messages) {
437 auto do_decode_message = [&](const String& name, bool returns_something) {
438 dbg() << " case (int)Messages::" << endpoint.name << "::MessageID::" << name << ":";
439 if (returns_something) {
440 dbg() << " return handle(static_cast<const Messages::" << endpoint.name << "::" << name << "&>(message));";
441 } else {
442 dbg() << " handle(static_cast<const Messages::" << endpoint.name << "::" << name << "&>(message));";
443 dbg() << " return nullptr;";
444 }
445 };
446 do_decode_message(message.name, message.is_synchronous);
447 if (message.is_synchronous)
448 do_decode_message(message.response_name(), false);
449 }
450 dbg() << " default:";
451 dbg() << " return nullptr;";
452
453 dbg() << " }";
454 dbg() << " }";
455
456 for (auto& message : endpoint.messages) {
457 String return_type = "void";
458 if (message.is_synchronous) {
459 StringBuilder builder;
460 builder.append("OwnPtr<Messages::");
461 builder.append(endpoint.name);
462 builder.append("::");
463 builder.append(message.name);
464 builder.append("Response");
465 builder.append(">");
466 return_type = builder.to_string();
467 }
468 dbg() << " virtual " << return_type << " handle(const Messages::" << endpoint.name << "::" << message.name << "&) = 0;";
469 }
470
471 dbg() << "private:";
472 dbg() << "};";
473 }
474
475#ifdef DEBUG
476 for (auto& endpoint : endpoints) {
477 dbg() << "Endpoint: '" << endpoint.name << "' (magic: " << endpoint.magic << ")";
478 for (auto& message : endpoint.messages) {
479 dbg() << " Message: '" << message.name << "'";
480 dbg() << " Sync: " << message.is_synchronous;
481 dbg() << " Inputs:";
482 for (auto& parameter : message.inputs)
483 dbg() << " Parameter: " << parameter.name << " (" << parameter.type << ")";
484 if (message.inputs.is_empty())
485 dbg() << " (none)";
486 if (message.is_synchronous) {
487 dbg() << " Outputs:";
488 for (auto& parameter : message.outputs)
489 dbg() << " Parameter: " << parameter.name << " (" << parameter.type << ")";
490 if (message.outputs.is_empty())
491 dbg() << " (none)";
492 }
493 }
494 }
495#endif
496
497 return 0;
498}