WrapperGenerator.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494
  1. /*
  2. * Copyright (c) 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. #include <AK/ByteBuffer.h>
  27. #include <AK/StringBuilder.h>
  28. #include <LibCore/ArgsParser.h>
  29. #include <LibCore/File.h>
  30. #include <ctype.h>
  31. static String snake_name(const StringView& title_name)
  32. {
  33. StringBuilder builder;
  34. bool first = true;
  35. for (auto ch : title_name) {
  36. if (isupper(ch)) {
  37. if (!first)
  38. builder.append('_');
  39. builder.append(tolower(ch));
  40. } else {
  41. builder.append(ch);
  42. }
  43. first = false;
  44. }
  45. return builder.to_string();
  46. }
  47. namespace IDL {
  48. struct Type {
  49. String name;
  50. bool nullable { false };
  51. };
  52. struct Parameter {
  53. Type type;
  54. String name;
  55. };
  56. struct Function {
  57. Type return_type;
  58. String name;
  59. Vector<Parameter> parameters;
  60. size_t length() const
  61. {
  62. // FIXME: Take optional arguments into account
  63. return parameters.size();
  64. }
  65. };
  66. struct Attribute {
  67. bool readonly { false };
  68. Type type;
  69. String name;
  70. // Added for convenience after parsing
  71. String getter_callback_name;
  72. String setter_callback_name;
  73. };
  74. struct Interface {
  75. String name;
  76. String parent_name;
  77. Vector<Attribute> attributes;
  78. Vector<Function> functions;
  79. // Added for convenience after parsing
  80. String wrapper_class;
  81. String wrapper_base_class;
  82. };
  83. static String snake_name(const StringView& camel_name)
  84. {
  85. StringBuilder builder;
  86. for (auto ch : camel_name) {
  87. if (isupper(ch)) {
  88. builder.append('_');
  89. builder.append(tolower(ch));
  90. } else {
  91. builder.append(ch);
  92. }
  93. }
  94. return builder.to_string();
  95. }
  96. OwnPtr<Interface> parse_interface(const StringView& input)
  97. {
  98. auto interface = make<Interface>();
  99. size_t index = 0;
  100. auto peek = [&](size_t offset = 0) -> char {
  101. if (index + offset > input.length())
  102. return 0;
  103. return input[index + offset];
  104. };
  105. auto consume = [&] {
  106. return input[index++];
  107. };
  108. auto consume_if = [&](auto ch) {
  109. if (peek() == ch) {
  110. consume();
  111. return true;
  112. }
  113. return false;
  114. };
  115. auto consume_specific = [&](char ch) {
  116. auto consumed = consume();
  117. if (consumed != ch) {
  118. dbg() << "Expected '" << ch << "' at offset " << index << " but got '" << consumed << "'";
  119. ASSERT_NOT_REACHED();
  120. }
  121. };
  122. auto consume_whitespace = [&] {
  123. while (isspace(peek()))
  124. consume();
  125. };
  126. auto consume_string = [&](const StringView& string) {
  127. for (size_t i = 0; i < string.length(); ++i) {
  128. ASSERT(consume() == string[i]);
  129. }
  130. };
  131. auto next_is = [&](const StringView& string) {
  132. for (size_t i = 0; i < string.length(); ++i) {
  133. if (peek(i) != string[i])
  134. return false;
  135. }
  136. return true;
  137. };
  138. auto consume_while = [&](auto condition) {
  139. StringBuilder builder;
  140. while (index < input.length() && condition(peek())) {
  141. builder.append(consume());
  142. }
  143. return builder.to_string();
  144. };
  145. consume_string("interface");
  146. consume_whitespace();
  147. interface->name = consume_while([](auto ch) { return !isspace(ch); });
  148. consume_whitespace();
  149. if (consume_if(':')) {
  150. consume_whitespace();
  151. interface->parent_name = consume_while([](auto ch) { return !isspace(ch); });
  152. consume_whitespace();
  153. }
  154. consume_specific('{');
  155. auto parse_type = [&] {
  156. auto name = consume_while([](auto ch) { return !isspace(ch) && ch != '?'; });
  157. auto nullable = peek() == '?';
  158. if (nullable)
  159. consume_specific('?');
  160. return Type { name, nullable };
  161. };
  162. auto parse_attribute = [&] {
  163. bool readonly = false;
  164. if (next_is("readonly")) {
  165. consume_string("readonly");
  166. readonly = true;
  167. consume_whitespace();
  168. }
  169. if (next_is("attribute")) {
  170. consume_string("attribute");
  171. consume_whitespace();
  172. }
  173. auto type = parse_type();
  174. consume_whitespace();
  175. auto name = consume_while([](auto ch) { return !isspace(ch) && ch != ';'; });
  176. consume_specific(';');
  177. Attribute attribute;
  178. attribute.readonly = readonly;
  179. attribute.type = type;
  180. attribute.name = name;
  181. attribute.getter_callback_name = String::format("%s_getter", snake_name(attribute.name).characters());
  182. attribute.setter_callback_name = String::format("%s_setter", snake_name(attribute.name).characters());
  183. interface->attributes.append(move(attribute));
  184. };
  185. auto parse_function = [&] {
  186. auto return_type = parse_type();
  187. consume_whitespace();
  188. auto name = consume_while([](auto ch) { return !isspace(ch) && ch != '('; });
  189. consume_specific('(');
  190. Vector<Parameter> parameters;
  191. for (;;) {
  192. auto type = parse_type();
  193. consume_whitespace();
  194. auto name = consume_while([](auto ch) { return !isspace(ch) && ch != ',' && ch != ')'; });
  195. parameters.append({ move(type), move(name) });
  196. if (consume_if(')'))
  197. break;
  198. consume_specific(',');
  199. consume_whitespace();
  200. }
  201. consume_specific(';');
  202. interface->functions.append(Function { return_type, name, move(parameters) });
  203. };
  204. for (;;) {
  205. consume_whitespace();
  206. if (consume_if('}'))
  207. break;
  208. if (next_is("readonly") || next_is("attribute")) {
  209. parse_attribute();
  210. continue;
  211. }
  212. parse_function();
  213. }
  214. interface->wrapper_class = String::format("%sWrapper", interface->name.characters());
  215. interface->wrapper_base_class = String::format("%sWrapper", interface->parent_name.characters());
  216. return interface;
  217. }
  218. }
  219. static void generate_header(const IDL::Interface&);
  220. static void generate_implementation(const IDL::Interface&);
  221. int main(int argc, char** argv)
  222. {
  223. Core::ArgsParser args_parser;
  224. const char* path = nullptr;
  225. bool header_mode = false;
  226. bool implementation_mode = false;
  227. args_parser.add_option(header_mode, "Generate the wrapper .h file", "header", 'H');
  228. args_parser.add_option(implementation_mode, "Generate the wrapper .cpp file", "implementation", 'I');
  229. args_parser.add_positional_argument(path, "IDL file", "idl-file");
  230. args_parser.parse(argc, argv);
  231. auto file_or_error = Core::File::open(path, Core::IODevice::ReadOnly);
  232. if (file_or_error.is_error()) {
  233. fprintf(stderr, "Cannot open %s\n", path);
  234. return 1;
  235. }
  236. auto data = file_or_error.value()->read_all();
  237. auto interface = IDL::parse_interface(data);
  238. if (!interface) {
  239. fprintf(stderr, "Cannot parse %s\n", path);
  240. return 1;
  241. }
  242. #if 0
  243. dbg() << "Attributes:";
  244. for (auto& attribute : interface->attributes) {
  245. dbg() << " " << (attribute.readonly ? "Readonly " : "")
  246. << attribute.type.name << (attribute.type.nullable ? "?" : "")
  247. << " " << attribute.name;
  248. }
  249. dbg() << "Functions:";
  250. for (auto& function : interface->functions) {
  251. dbg() << " " << function.return_type.name << (function.return_type.nullable ? "?" : "")
  252. << " " << function.name;
  253. for (auto& parameter : function.parameters) {
  254. dbg() << " " << parameter.type.name << (parameter.type.nullable ? "?" : "") << " " << parameter.name;
  255. }
  256. }
  257. #endif
  258. if (header_mode)
  259. generate_header(*interface);
  260. if (implementation_mode)
  261. generate_implementation(*interface);
  262. return 0;
  263. }
  264. static void generate_header(const IDL::Interface& interface)
  265. {
  266. auto& wrapper_class = interface.wrapper_class;
  267. auto& wrapper_base_class = interface.wrapper_base_class;
  268. out() << "#pragma once";
  269. out() << "#include <LibWeb/Bindings/Wrapper.h>";
  270. out() << "#include <LibWeb/DOM/" << interface.name << ".h>";
  271. if (!wrapper_base_class.is_empty()) {
  272. out() << "#include <LibWeb/Bindings/" << wrapper_base_class << ".h>";
  273. }
  274. out() << "namespace Web {";
  275. out() << "namespace Bindings {";
  276. out() << "class " << wrapper_class << " : public " << wrapper_base_class << " {";
  277. out() << "public:";
  278. out() << " " << wrapper_class << "(JS::GlobalObject&, " << interface.name << "&);";
  279. out() << " virtual void initialize(JS::Interpreter&, JS::GlobalObject&) override;";
  280. out() << " virtual ~" << wrapper_class << "() override;";
  281. if (wrapper_base_class.is_empty()) {
  282. out() << " " << interface.name << "& impl() { return *m_impl; }";
  283. out() << " const " << interface.name << "& impl() const { return *m_impl; }";
  284. } else {
  285. out() << " " << interface.name << "& impl() { return static_cast<" << interface.name << "&>(" << wrapper_base_class << "::impl()); }";
  286. out() << " const " << interface.name << "& impl() const { return static_cast<const " << interface.name << "&>(" << wrapper_base_class << "::impl()); }";
  287. }
  288. auto is_foo_wrapper_name = snake_name(String::format("Is%s", wrapper_class.characters()));
  289. out() << " virtual bool " << is_foo_wrapper_name << "() const final { return true; }";
  290. out() << "private:";
  291. out() << " virtual const char* class_name() const override { return \"" << interface.name << "\"; }";
  292. for (auto& function : interface.functions) {
  293. out() << " JS_DECLARE_NATIVE_FUNCTION(" << snake_name(function.name) << ");";
  294. }
  295. for (auto& attribute : interface.attributes) {
  296. out() << " JS_DECLARE_NATIVE_GETTER(" << snake_name(attribute.name) << "_getter);";
  297. if (!attribute.readonly)
  298. out() << " JS_DECLARE_NATIVE_SETTER(" << snake_name(attribute.name) << "_setter);";
  299. }
  300. out() << "};";
  301. out() << "}";
  302. out() << "}";
  303. }
  304. void generate_implementation(const IDL::Interface& interface)
  305. {
  306. auto& wrapper_class = interface.wrapper_class;
  307. auto& wrapper_base_class = interface.wrapper_base_class;
  308. out() << "#include <AK/FlyString.h>";
  309. out() << "#include <LibJS/Interpreter.h>";
  310. out() << "#include <LibJS/Runtime/Array.h>";
  311. out() << "#include <LibJS/Runtime/Value.h>";
  312. out() << "#include <LibJS/Runtime/GlobalObject.h>";
  313. out() << "#include <LibJS/Runtime/Error.h>";
  314. out() << "#include <LibWeb/Bindings/NodeWrapperFactory.h>";
  315. out() << "#include <LibWeb/Bindings/" << wrapper_class << ".h>";
  316. out() << "#include <LibWeb/DOM/Element.h>";
  317. out() << "namespace Web {";
  318. out() << "namespace Bindings {";
  319. // Implementation: Wrapper constructor
  320. out() << wrapper_class << "::" << wrapper_class << "(JS::GlobalObject& global_object, " << interface.name << "& impl)";
  321. out() << " : " << wrapper_base_class << "(global_object, impl)";
  322. out() << "{";
  323. out() << "}";
  324. // Implementation: Wrapper initialize()
  325. out() << "void " << wrapper_class << "::initialize(JS::Interpreter& interpreter, JS::GlobalObject& global_object)";
  326. out() << "{";
  327. out() << " [[maybe_unused]] u8 default_attributes = JS::Attribute::Enumerable | JS::Attribute::Configurable;";
  328. out() << " " << wrapper_base_class << "::initialize(interpreter, global_object);";
  329. for (auto& attribute : interface.attributes) {
  330. out() << " define_native_property(\"" << attribute.name << "\", " << attribute.getter_callback_name << ", " << (attribute.readonly ? "nullptr" : attribute.setter_callback_name) << ", default_attributes);";
  331. }
  332. for (auto& function : interface.functions) {
  333. out() << " define_native_function(\"" << function.name << "\", " << snake_name(function.name) << ", " << function.length() << ", default_attributes);";
  334. }
  335. out() << "}";
  336. // Implementation: Wrapper destructor
  337. out() << wrapper_class << "::~" << wrapper_class << "()";
  338. out() << "{";
  339. out() << "}";
  340. // Implementation: impl_from()
  341. out() << "static " << interface.name << "* impl_from(JS::Interpreter& interpreter, JS::GlobalObject& global_object)";
  342. out() << "{";
  343. out() << " auto* this_object = interpreter.this_value(global_object).to_object(interpreter, global_object);";
  344. out() << " if (!this_object)";
  345. out() << " return {};";
  346. auto is_foo_wrapper_name = snake_name(String::format("Is%s", wrapper_class.characters()));
  347. out() << " if (!this_object->is_web_wrapper() || !static_cast<Wrapper*>(this_object)->" << is_foo_wrapper_name << "()) {";
  348. out() << " interpreter.throw_exception<JS::TypeError>(JS::ErrorType::NotA, \"" << interface.name << "\");";
  349. out() << " return nullptr;";
  350. out() << " }";
  351. out() << " return &static_cast<" << wrapper_class << "*>(this_object)->impl();";
  352. out() << "}";
  353. auto generate_return_statement = [&](auto& return_type) {
  354. if (return_type.nullable) {
  355. out() << " if (!retval)";
  356. out() << " return JS::js_null();";
  357. }
  358. if (return_type.name == "DOMString") {
  359. out() << " return JS::js_string(interpreter, retval);";
  360. } else if (return_type.name == "ArrayFromVector") {
  361. // FIXME: Remove this fake type hack once it's no longer needed.
  362. // Basically once we have NodeList we can throw this out.
  363. out() << " auto* new_array = JS::Array::create(global_object);";
  364. out() << " for (auto& element : retval) {";
  365. out() << " new_array->indexed_properties().append(wrap(interpreter.heap(), element));";
  366. out() << " }";
  367. out() << " return new_array;";
  368. } else {
  369. out() << " return wrap(interpreter.heap(), const_cast<" << return_type.name << "&>(*retval));";
  370. }
  371. };
  372. // Implementation: Attributes
  373. for (auto& attribute : interface.attributes) {
  374. out() << "JS_DEFINE_NATIVE_GETTER(" << wrapper_class << "::" << snake_name(attribute.name) << "_getter)";
  375. out() << "{";
  376. out() << " auto* impl = impl_from(interpreter, global_object);";
  377. out() << " if (!impl)";
  378. out() << " return {};";
  379. out() << " auto retval = impl->" << snake_name(attribute.name) << "();";
  380. generate_return_statement(attribute.type);
  381. out() << "}";
  382. }
  383. // Implementation: Functions
  384. for (auto& function : interface.functions) {
  385. out() << "JS_DEFINE_NATIVE_FUNCTION(" << wrapper_class << "::" << snake_name(function.name) << ")";
  386. out() << "{";
  387. out() << " auto* impl = impl_from(interpreter, global_object);";
  388. out() << " if (!impl)";
  389. out() << " return {};";
  390. out() << " if (interpreter.argument_count() < " << function.length() << ")";
  391. out() << " return interpreter.throw_exception<JS::TypeError>(JS::ErrorType::BadArgCountMany, \"" << function.name << "\", \"" << function.length() << "\");";
  392. Vector<String> parameter_names;
  393. size_t argument_index = 0;
  394. for (auto& parameter : function.parameters) {
  395. parameter_names.append(snake_name(parameter.name));
  396. if (parameter.type.name == "DOMString") {
  397. out() << " auto " << snake_name(parameter.name) << " = interpreter.argument(" << argument_index << ").to_string(interpreter);";
  398. out() << " if (interpreter.exception())";
  399. out() << " return {};";
  400. }
  401. ++argument_index;
  402. }
  403. StringBuilder arguments_builder;
  404. arguments_builder.join(", ", parameter_names);
  405. if (function.return_type.name != "void") {
  406. out() << " auto retval = impl->" << snake_name(function.name) << "(" << arguments_builder.to_string() << ");";
  407. } else {
  408. out() << " impl->" << snake_name(function.name) << "(" << arguments_builder.to_string() << ");";
  409. }
  410. generate_return_statement(function.return_type);
  411. out() << "}";
  412. }
  413. out() << "}";
  414. out() << "}";
  415. }