Serenity Operating System
at portability 498 lines 20 kB view raw
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}