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 AK::dbg_raw(true);
66
67 if (argc != 2) {
68 printf("usage: %s <IPC endpoint definition file>\n", argv[0]);
69 return 0;
70 }
71
72 auto file = Core::File::construct(argv[1]);
73 if (!file->open(Core::IODevice::ReadOnly)) {
74 fprintf(stderr, "Error: Cannot open %s: %s\n", argv[1], file->error_string());
75 return 1;
76 }
77
78 auto file_contents = file->read_all();
79
80 Vector<Endpoint> endpoints;
81
82 Vector<char> buffer;
83
84 size_t index = 0;
85
86 auto peek = [&](size_t offset = 0) -> char {
87 if ((index + offset) < file_contents.size())
88 return file_contents[index + offset];
89 return 0;
90 };
91
92 auto consume_one = [&]() -> char {
93 return file_contents[index++];
94 };
95
96 auto extract_while = [&](Function<bool(char)> condition) -> String {
97 StringBuilder builder;
98 while (condition(peek()))
99 builder.append(consume_one());
100 return builder.to_string();
101 };
102
103 auto consume_specific = [&](char ch) {
104 if (peek() != ch) {
105 warn() << "consume_specific: wanted '" << ch << "', but got '" << peek() << "' at index " << index;
106 }
107 ASSERT(peek() == ch);
108 ++index;
109 return ch;
110 };
111
112 auto consume_string = [&](const char* str) {
113 for (size_t i = 0, length = strlen(str); i < length; ++i)
114 consume_specific(str[i]);
115 };
116
117 auto consume_whitespace = [&] {
118 while (isspace(peek()))
119 ++index;
120 if (peek() == '/' && peek(1) == '/') {
121 while (peek() != '\n')
122 ++index;
123 }
124 };
125
126 auto parse_parameter = [&](Vector<Parameter>& storage) {
127 for (;;) {
128 Parameter parameter;
129 consume_whitespace();
130 if (peek() == ')')
131 break;
132 parameter.type = extract_while([](char ch) { return !isspace(ch); });
133 consume_whitespace();
134 parameter.name = extract_while([](char ch) { return !isspace(ch) && ch != ',' && ch != ')'; });
135 consume_whitespace();
136 storage.append(move(parameter));
137 if (peek() == ',') {
138 consume_one();
139 continue;
140 }
141 if (peek() == ')')
142 break;
143 }
144 };
145
146 auto parse_parameters = [&](Vector<Parameter>& storage) {
147 for (;;) {
148 consume_whitespace();
149 parse_parameter(storage);
150 consume_whitespace();
151 if (peek() == ',') {
152 consume_one();
153 continue;
154 }
155 if (peek() == ')')
156 break;
157 }
158 };
159
160 auto parse_message = [&] {
161 Message message;
162 consume_whitespace();
163 Vector<char> buffer;
164 while (!isspace(peek()) && peek() != '(')
165 buffer.append(consume_one());
166 message.name = String::copy(buffer);
167 consume_whitespace();
168 consume_specific('(');
169 parse_parameters(message.inputs);
170 consume_specific(')');
171 consume_whitespace();
172 consume_specific('=');
173
174 auto type = consume_one();
175 if (type == '>')
176 message.is_synchronous = true;
177 else if (type == '|')
178 message.is_synchronous = false;
179 else
180 ASSERT_NOT_REACHED();
181
182 consume_whitespace();
183
184 if (message.is_synchronous) {
185 consume_specific('(');
186 parse_parameters(message.outputs);
187 consume_specific(')');
188 }
189
190 consume_whitespace();
191
192 endpoints.last().messages.append(move(message));
193 };
194
195 auto parse_messages = [&] {
196 for (;;) {
197 consume_whitespace();
198 parse_message();
199 consume_whitespace();
200 if (peek() == '}')
201 break;
202 }
203 };
204
205 auto parse_endpoint = [&] {
206 endpoints.empend();
207 consume_whitespace();
208 consume_string("endpoint");
209 consume_whitespace();
210 endpoints.last().name = extract_while([](char ch) { return !isspace(ch); });
211 consume_whitespace();
212 consume_specific('=');
213 consume_whitespace();
214 auto magic_string = extract_while([](char ch) { return !isspace(ch) && ch != '{'; });
215 bool ok;
216 endpoints.last().magic = magic_string.to_int(ok);
217 ASSERT(ok);
218 consume_whitespace();
219 consume_specific('{');
220 parse_messages();
221 consume_specific('}');
222 consume_whitespace();
223 };
224
225 while (index < file_contents.size())
226 parse_endpoint();
227
228 out() << "#pragma once";
229 out() << "#include <AK/BufferStream.h>";
230 out() << "#include <AK/OwnPtr.h>";
231 out() << "#include <LibGfx/Color.h>";
232 out() << "#include <LibGfx/Rect.h>";
233 out() << "#include <LibGfx/ShareableBitmap.h>";
234 out() << "#include <LibIPC/Decoder.h>";
235 out() << "#include <LibIPC/Encoder.h>";
236 out() << "#include <LibIPC/Endpoint.h>";
237 out() << "#include <LibIPC/Message.h>";
238 out();
239
240 for (auto& endpoint : endpoints) {
241 out() << "namespace Messages {";
242 out() << "namespace " << endpoint.name << " {";
243 out();
244
245 HashMap<String, int> message_ids;
246
247 out() << "enum class MessageID : i32 {";
248 for (auto& message : endpoint.messages) {
249 message_ids.set(message.name, message_ids.size() + 1);
250 out() << " " << message.name << " = " << message_ids.size() << ",";
251 if (message.is_synchronous) {
252 message_ids.set(message.response_name(), message_ids.size() + 1);
253 out() << " " << message.response_name() << " = " << message_ids.size() << ",";
254 }
255 }
256 out() << "};";
257 out();
258
259 auto constructor_for_message = [&](const String& name, const Vector<Parameter>& parameters) {
260 StringBuilder builder;
261 builder.append(name);
262
263 if (parameters.is_empty()) {
264 builder.append("() {}");
265 return builder.to_string();
266 }
267
268 builder.append('(');
269 for (size_t i = 0; i < parameters.size(); ++i) {
270 auto& parameter = parameters[i];
271 builder.append("const ");
272 builder.append(parameter.type);
273 builder.append("& ");
274 builder.append(parameter.name);
275 if (i != parameters.size() - 1)
276 builder.append(", ");
277 }
278 builder.append(") : ");
279 for (size_t i = 0; i < parameters.size(); ++i) {
280 auto& parameter = parameters[i];
281 builder.append("m_");
282 builder.append(parameter.name);
283 builder.append("(");
284 builder.append(parameter.name);
285 builder.append(")");
286 if (i != parameters.size() - 1)
287 builder.append(", ");
288 }
289 builder.append(" {}");
290 return builder.to_string();
291 };
292
293 auto do_message = [&](const String& name, const Vector<Parameter>& parameters, const String& response_type = {}) {
294 out() << "class " << name << " final : public IPC::Message {";
295 out() << "public:";
296 if (!response_type.is_null())
297 out() << " typedef class " << response_type << " ResponseType;";
298 out() << " " << constructor_for_message(name, parameters);
299 out() << " virtual ~" << name << "() override {}";
300 out() << " virtual i32 endpoint_magic() const override { return " << endpoint.magic << "; }";
301 out() << " virtual i32 message_id() const override { return (int)MessageID::" << name << "; }";
302 out() << " static i32 static_message_id() { return (int)MessageID::" << name << "; }";
303 out() << " virtual const char* message_name() const override { return \"" << endpoint.name << "::" << name << "\"; }";
304 out() << " static OwnPtr<" << name << "> decode(BufferStream& stream, size_t& size_in_bytes)";
305 out() << " {";
306
307 out() << " IPC::Decoder decoder(stream);";
308
309 for (auto& parameter : parameters) {
310 String initial_value = "{}";
311 if (parameter.type == "bool")
312 initial_value = "false";
313 out() << " " << parameter.type << " " << parameter.name << " = " << initial_value << ";";
314
315 if (parameter.type == "Vector<Gfx::Rect>") {
316 out() << " u64 " << parameter.name << "_size = 0;";
317 out() << " stream >> " << parameter.name << "_size;";
318 out() << " for (size_t i = 0; i < " << parameter.name << "_size; ++i) {";
319 out() << " Gfx::Rect rect;";
320 out() << " if (!decoder.decode(rect))";
321 out() << " return nullptr;";
322 out() << " " << parameter.name << ".append(move(rect));";
323 out() << " }";
324 } else {
325 out() << " if (!decoder.decode(" << parameter.name << "))";
326 out() << " return nullptr;";
327 }
328 }
329
330 StringBuilder builder;
331 for (size_t i = 0; i < parameters.size(); ++i) {
332 auto& parameter = parameters[i];
333 builder.append(parameter.name);
334 if (i != parameters.size() - 1)
335 builder.append(", ");
336 }
337 out() << " size_in_bytes = stream.offset();";
338 out() << " return make<" << name << ">(" << builder.to_string() << ");";
339 out() << " }";
340 out() << " virtual IPC::MessageBuffer encode() const override";
341 out() << " {";
342 out() << " IPC::MessageBuffer buffer;";
343 out() << " IPC::Encoder stream(buffer);";
344 out() << " stream << endpoint_magic();";
345 out() << " stream << (int)MessageID::" << name << ";";
346 for (auto& parameter : parameters) {
347 if (parameter.type == "Gfx::Color") {
348 out() << " stream << m_" << parameter.name << ".value();";
349 } else if (parameter.type == "Gfx::Size") {
350 out() << " stream << m_" << parameter.name << ".width();";
351 out() << " stream << m_" << parameter.name << ".height();";
352 } else if (parameter.type == "Gfx::Point") {
353 out() << " stream << m_" << parameter.name << ".x();";
354 out() << " stream << m_" << parameter.name << ".y();";
355 } else if (parameter.type == "Gfx::Rect") {
356 out() << " stream << m_" << parameter.name << ".x();";
357 out() << " stream << m_" << parameter.name << ".y();";
358 out() << " stream << m_" << parameter.name << ".width();";
359 out() << " stream << m_" << parameter.name << ".height();";
360 } else if (parameter.type == "Vector<Gfx::Rect>") {
361 out() << " stream << (u64)m_" << parameter.name << ".size();";
362 out() << " for (auto& rect : m_" << parameter.name << ") {";
363 out() << " stream << rect.x();";
364 out() << " stream << rect.y();";
365 out() << " stream << rect.width();";
366 out() << " stream << rect.height();";
367 out() << " }";
368 } else if (parameter.type == "Gfx::ShareableBitmap") {
369 out() << " stream << m_" << parameter.name << ".shbuf_id();";
370 out() << " stream << m_" << parameter.name << ".width();";
371 out() << " stream << m_" << parameter.name << ".height();";
372 } else {
373 out() << " stream << m_" << parameter.name << ";";
374 }
375 }
376 out() << " return buffer;";
377 out() << " }";
378 for (auto& parameter : parameters) {
379 out() << " const " << parameter.type << "& " << parameter.name << "() const { return m_" << parameter.name << "; }";
380 }
381 out() << "private:";
382 for (auto& parameter : parameters) {
383 out() << " " << parameter.type << " m_" << parameter.name << ";";
384 }
385 out() << "};";
386 out();
387 };
388 for (auto& message : endpoint.messages) {
389 String response_name;
390 if (message.is_synchronous) {
391 response_name = message.response_name();
392 do_message(response_name, message.outputs);
393 }
394 do_message(message.name, message.inputs, response_name);
395 }
396 out() << "} // namespace " << endpoint.name;
397 out() << "} // namespace Messages";
398 out();
399
400 out() << "class " << endpoint.name << "Endpoint : public IPC::Endpoint {";
401 out() << "public:";
402 out() << " " << endpoint.name << "Endpoint() {}";
403 out() << " virtual ~" << endpoint.name << "Endpoint() override {}";
404 out() << " static int static_magic() { return " << endpoint.magic << "; }";
405 out() << " virtual int magic() const override { return " << endpoint.magic << "; }";
406 out() << " static String static_name() { return \"" << endpoint.name << "\"; };";
407 out() << " virtual String name() const override { return \"" << endpoint.name << "\"; };";
408 out() << " static OwnPtr<IPC::Message> decode_message(const ByteBuffer& buffer, size_t& size_in_bytes)";
409 out() << " {";
410 out() << " BufferStream stream(const_cast<ByteBuffer&>(buffer));";
411 out() << " i32 message_endpoint_magic = 0;";
412 out() << " stream >> message_endpoint_magic;";
413 out() << " if (message_endpoint_magic != " << endpoint.magic << ") {";
414#ifdef GENERATE_DEBUG_CODE
415 sout() << " sout() << \"endpoint magic \" << message_endpoint_magic << \" != " << endpoint.magic << "\";";
416#endif
417 out() << " return nullptr;";
418 out() << " }";
419 out() << " i32 message_id = 0;";
420 out() << " stream >> message_id;";
421 out() << " switch (message_id) {";
422 for (auto& message : endpoint.messages) {
423 auto do_decode_message = [&](const String& name) {
424 out() << " case (int)Messages::" << endpoint.name << "::MessageID::" << name << ":";
425 out() << " return Messages::" << endpoint.name << "::" << name << "::decode(stream, size_in_bytes);";
426 };
427 do_decode_message(message.name);
428 if (message.is_synchronous)
429 do_decode_message(message.response_name());
430 }
431 out() << " default:";
432#ifdef GENERATE_DEBUG_CODE
433 sout() << " sout() << \"Failed to decode " << endpoint.name << ".(\" << message_id << \")\";";
434#endif
435 out() << " return nullptr;";
436
437 out() << " }";
438 out() << " }";
439 out();
440 out() << " virtual OwnPtr<IPC::Message> handle(const IPC::Message& message) override";
441 out() << " {";
442 out() << " switch (message.message_id()) {";
443 for (auto& message : endpoint.messages) {
444 auto do_decode_message = [&](const String& name, bool returns_something) {
445 out() << " case (int)Messages::" << endpoint.name << "::MessageID::" << name << ":";
446 if (returns_something) {
447 out() << " return handle(static_cast<const Messages::" << endpoint.name << "::" << name << "&>(message));";
448 } else {
449 out() << " handle(static_cast<const Messages::" << endpoint.name << "::" << name << "&>(message));";
450 out() << " return nullptr;";
451 }
452 };
453 do_decode_message(message.name, message.is_synchronous);
454 if (message.is_synchronous)
455 do_decode_message(message.response_name(), false);
456 }
457 out() << " default:";
458 out() << " return nullptr;";
459
460 out() << " }";
461 out() << " }";
462
463 for (auto& message : endpoint.messages) {
464 String return_type = "void";
465 if (message.is_synchronous) {
466 StringBuilder builder;
467 builder.append("OwnPtr<Messages::");
468 builder.append(endpoint.name);
469 builder.append("::");
470 builder.append(message.name);
471 builder.append("Response");
472 builder.append(">");
473 return_type = builder.to_string();
474 }
475 out() << " virtual " << return_type << " handle(const Messages::" << endpoint.name << "::" << message.name << "&) = 0;";
476 }
477
478 out() << "private:";
479 out() << "};";
480 }
481
482#ifdef IPC_DEBUG
483 for (auto& endpoint : endpoints) {
484 warn() << "Endpoint: '" << endpoint.name << "' (magic: " << endpoint.magic << ")";
485 for (auto& message : endpoint.messages) {
486 warn() << " Message: '" << message.name << "'";
487 warn() << " Sync: " << message.is_synchronous;
488 warn() << " Inputs:";
489 for (auto& parameter : message.inputs)
490 warn() << " Parameter: " << parameter.name << " (" << parameter.type << ")";
491 if (message.inputs.is_empty())
492 warn() << " (none)";
493 if (message.is_synchronous) {
494 warn() << " Outputs:";
495 for (auto& parameter : message.outputs)
496 warn() << " Parameter: " << parameter.name << " (" << parameter.type << ")";
497 if (message.outputs.is_empty())
498 warn() << " (none)";
499 }
500 }
501 }
502#endif
503
504 return 0;
505}