WrapperGenerator.cpp 42 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260
  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/Debug.h>
  28. #include <AK/GenericLexer.h>
  29. #include <AK/HashMap.h>
  30. #include <AK/LexicalPath.h>
  31. #include <AK/SourceGenerator.h>
  32. #include <AK/StringBuilder.h>
  33. #include <LibCore/ArgsParser.h>
  34. #include <LibCore/File.h>
  35. #include <ctype.h>
  36. static String snake_name(const StringView& title_name)
  37. {
  38. StringBuilder builder;
  39. bool first = true;
  40. bool last_was_uppercase = false;
  41. for (auto ch : title_name) {
  42. if (isupper(ch)) {
  43. if (!first && !last_was_uppercase)
  44. builder.append('_');
  45. builder.append(tolower(ch));
  46. } else {
  47. builder.append(ch);
  48. }
  49. first = false;
  50. last_was_uppercase = isupper(ch);
  51. }
  52. return builder.to_string();
  53. }
  54. static String make_input_acceptable_cpp(const String& input)
  55. {
  56. if (input.is_one_of("class", "template", "for", "default", "char")) {
  57. StringBuilder builder;
  58. builder.append(input);
  59. builder.append('_');
  60. return builder.to_string();
  61. }
  62. String input_without_dashes = input;
  63. input_without_dashes.replace("-", "_");
  64. return input_without_dashes;
  65. }
  66. static void report_parsing_error(StringView message, StringView filename, StringView input, size_t offset)
  67. {
  68. // FIXME: Spaghetti code ahead.
  69. size_t lineno = 1;
  70. size_t colno = 1;
  71. size_t start_line = 0;
  72. size_t line_length = 0;
  73. for (size_t index = 0; index < input.length(); ++index) {
  74. if (offset == index)
  75. colno = index - start_line + 1;
  76. if (input[index] == '\n') {
  77. if (index >= offset)
  78. break;
  79. start_line = index + 1;
  80. line_length = 0;
  81. ++lineno;
  82. } else {
  83. ++line_length;
  84. }
  85. }
  86. StringBuilder error_message;
  87. error_message.appendff("{}\n", input.substring_view(start_line, line_length));
  88. for (size_t i = 0; i < colno - 1; ++i)
  89. error_message.append(' ');
  90. error_message.append("\033[1;31m^\n");
  91. error_message.appendff("{}:{}: error: {}\033[0m\n", filename, lineno, message);
  92. warnln("{}", error_message.string_view());
  93. exit(EXIT_FAILURE);
  94. }
  95. namespace IDL {
  96. struct Type {
  97. String name;
  98. bool nullable { false };
  99. };
  100. struct Parameter {
  101. Type type;
  102. String name;
  103. bool optional { false };
  104. };
  105. struct Function {
  106. Type return_type;
  107. String name;
  108. Vector<Parameter> parameters;
  109. HashMap<String, String> extended_attributes;
  110. size_t length() const
  111. {
  112. // FIXME: This seems to produce a length that is way over what it's supposed to be.
  113. // For example, getElementsByTagName has its length set to 20 when it should be 1.
  114. size_t length = 0;
  115. for (auto& parameter : parameters) {
  116. if (!parameter.optional)
  117. length++;
  118. }
  119. return length;
  120. }
  121. };
  122. struct Attribute {
  123. bool readonly { false };
  124. bool unsigned_ { false };
  125. Type type;
  126. String name;
  127. HashMap<String, String> extended_attributes;
  128. // Added for convenience after parsing
  129. String getter_callback_name;
  130. String setter_callback_name;
  131. };
  132. struct Interface {
  133. String name;
  134. String parent_name;
  135. Vector<Attribute> attributes;
  136. Vector<Function> functions;
  137. // Added for convenience after parsing
  138. String wrapper_class;
  139. String wrapper_base_class;
  140. String fully_qualified_name;
  141. String constructor_class;
  142. String prototype_class;
  143. String prototype_base_class;
  144. };
  145. static OwnPtr<Interface> parse_interface(StringView filename, const StringView& input)
  146. {
  147. auto interface = make<Interface>();
  148. GenericLexer lexer(input);
  149. auto assert_specific = [&](char ch) {
  150. if (!lexer.consume_specific(ch))
  151. report_parsing_error(String::formatted("expected '{}'", ch), filename, input, lexer.tell());
  152. };
  153. auto consume_whitespace = [&] {
  154. bool consumed = true;
  155. while (consumed) {
  156. consumed = lexer.consume_while([](char ch) { return isspace(ch); }).length() > 0;
  157. if (lexer.consume_specific("//")) {
  158. lexer.consume_until('\n');
  159. consumed = true;
  160. }
  161. }
  162. };
  163. auto assert_string = [&](const StringView& expected) {
  164. if (!lexer.consume_specific(expected))
  165. report_parsing_error(String::formatted("expected '{}'", expected), filename, input, lexer.tell());
  166. };
  167. assert_string("interface");
  168. consume_whitespace();
  169. interface->name = lexer.consume_until([](auto ch) { return isspace(ch); });
  170. consume_whitespace();
  171. if (lexer.consume_specific(':')) {
  172. consume_whitespace();
  173. interface->parent_name = lexer.consume_until([](auto ch) { return isspace(ch); });
  174. consume_whitespace();
  175. }
  176. assert_specific('{');
  177. auto parse_type = [&] {
  178. auto name = lexer.consume_until([](auto ch) { return isspace(ch) || ch == '?'; });
  179. auto nullable = lexer.consume_specific('?');
  180. return Type { name, nullable };
  181. };
  182. auto parse_attribute = [&](HashMap<String, String>& extended_attributes) {
  183. bool readonly = lexer.consume_specific("readonly");
  184. if (readonly)
  185. consume_whitespace();
  186. if (lexer.consume_specific("attribute"))
  187. consume_whitespace();
  188. bool unsigned_ = lexer.consume_specific("unsigned");
  189. if (unsigned_)
  190. consume_whitespace();
  191. auto type = parse_type();
  192. consume_whitespace();
  193. auto name = lexer.consume_until([](auto ch) { return isspace(ch) || ch == ';'; });
  194. consume_whitespace();
  195. assert_specific(';');
  196. Attribute attribute;
  197. attribute.readonly = readonly;
  198. attribute.unsigned_ = unsigned_;
  199. attribute.type = type;
  200. attribute.name = name;
  201. attribute.getter_callback_name = String::formatted("{}_getter", snake_name(attribute.name));
  202. attribute.setter_callback_name = String::formatted("{}_setter", snake_name(attribute.name));
  203. attribute.extended_attributes = move(extended_attributes);
  204. interface->attributes.append(move(attribute));
  205. };
  206. auto parse_function = [&](HashMap<String, String>& extended_attributes) {
  207. auto return_type = parse_type();
  208. consume_whitespace();
  209. auto name = lexer.consume_until([](auto ch) { return isspace(ch) || ch == '('; });
  210. consume_whitespace();
  211. assert_specific('(');
  212. Vector<Parameter> parameters;
  213. for (;;) {
  214. if (lexer.consume_specific(')'))
  215. break;
  216. bool optional = lexer.consume_specific("optional");
  217. if (optional)
  218. consume_whitespace();
  219. auto type = parse_type();
  220. consume_whitespace();
  221. auto name = lexer.consume_until([](auto ch) { return isspace(ch) || ch == ',' || ch == ')'; });
  222. parameters.append({ move(type), move(name), optional });
  223. if (lexer.consume_specific(')'))
  224. break;
  225. assert_specific(',');
  226. consume_whitespace();
  227. }
  228. consume_whitespace();
  229. assert_specific(';');
  230. interface->functions.append(Function { return_type, name, move(parameters), move(extended_attributes) });
  231. };
  232. auto parse_extended_attributes = [&] {
  233. HashMap<String, String> extended_attributes;
  234. for (;;) {
  235. consume_whitespace();
  236. if (lexer.consume_specific(']'))
  237. break;
  238. auto name = lexer.consume_until([](auto ch) { return ch == ']' || ch == '=' || ch == ','; });
  239. if (lexer.consume_specific('=')) {
  240. auto value = lexer.consume_until([](auto ch) { return ch == ']' || ch == ','; });
  241. extended_attributes.set(name, value);
  242. } else {
  243. extended_attributes.set(name, {});
  244. }
  245. lexer.consume_specific(',');
  246. }
  247. consume_whitespace();
  248. return extended_attributes;
  249. };
  250. for (;;) {
  251. HashMap<String, String> extended_attributes;
  252. consume_whitespace();
  253. if (lexer.consume_specific('}')) {
  254. consume_whitespace();
  255. assert_specific(';');
  256. break;
  257. }
  258. if (lexer.consume_specific('[')) {
  259. extended_attributes = parse_extended_attributes();
  260. }
  261. if (lexer.next_is("readonly") || lexer.next_is("attribute")) {
  262. parse_attribute(extended_attributes);
  263. continue;
  264. }
  265. parse_function(extended_attributes);
  266. }
  267. interface->wrapper_class = String::formatted("{}Wrapper", interface->name);
  268. interface->wrapper_base_class = String::formatted("{}Wrapper", interface->parent_name.is_empty() ? String::empty() : interface->parent_name);
  269. interface->constructor_class = String::formatted("{}Constructor", interface->name);
  270. interface->prototype_class = String::formatted("{}Prototype", interface->name);
  271. interface->prototype_base_class = String::formatted("{}Prototype", interface->parent_name.is_empty() ? "Object" : interface->parent_name);
  272. return interface;
  273. }
  274. }
  275. static void generate_constructor_header(const IDL::Interface&);
  276. static void generate_constructor_implementation(const IDL::Interface&);
  277. static void generate_prototype_header(const IDL::Interface&);
  278. static void generate_prototype_implementation(const IDL::Interface&);
  279. static void generate_header(const IDL::Interface&);
  280. static void generate_implementation(const IDL::Interface&);
  281. int main(int argc, char** argv)
  282. {
  283. Core::ArgsParser args_parser;
  284. const char* path = nullptr;
  285. bool header_mode = false;
  286. bool implementation_mode = false;
  287. bool constructor_header_mode = false;
  288. bool constructor_implementation_mode = false;
  289. bool prototype_header_mode = false;
  290. bool prototype_implementation_mode = false;
  291. args_parser.add_option(header_mode, "Generate the wrapper .h file", "header", 'H');
  292. args_parser.add_option(implementation_mode, "Generate the wrapper .cpp file", "implementation", 'I');
  293. args_parser.add_option(constructor_header_mode, "Generate the constructor .h file", "constructor-header", 'C');
  294. args_parser.add_option(constructor_implementation_mode, "Generate the constructor .cpp file", "constructor-implementation", 'O');
  295. args_parser.add_option(prototype_header_mode, "Generate the prototype .h file", "prototype-header", 'P');
  296. args_parser.add_option(prototype_implementation_mode, "Generate the prototype .cpp file", "prototype-implementation", 'R');
  297. args_parser.add_positional_argument(path, "IDL file", "idl-file");
  298. args_parser.parse(argc, argv);
  299. auto file_or_error = Core::File::open(path, Core::IODevice::ReadOnly);
  300. if (file_or_error.is_error()) {
  301. fprintf(stderr, "Cannot open %s\n", path);
  302. return 1;
  303. }
  304. LexicalPath lexical_path(path);
  305. auto namespace_ = lexical_path.parts().at(lexical_path.parts().size() - 2);
  306. auto data = file_or_error.value()->read_all();
  307. auto interface = IDL::parse_interface(path, data);
  308. if (!interface) {
  309. warnln("Cannot parse {}", path);
  310. return 1;
  311. }
  312. if (namespace_.is_one_of("DOM", "HTML", "UIEvents", "HighResolutionTime", "NavigationTiming", "SVG")) {
  313. StringBuilder builder;
  314. builder.append(namespace_);
  315. builder.append("::");
  316. builder.append(interface->name);
  317. interface->fully_qualified_name = builder.to_string();
  318. } else {
  319. interface->fully_qualified_name = interface->name;
  320. }
  321. if constexpr (debug_wrapper_generator) {
  322. dbgln("Attributes:");
  323. for (auto& attribute : interface->attributes) {
  324. dbgln(" {}{}{} {}",
  325. attribute.readonly ? "readonly " : "",
  326. attribute.type.name,
  327. attribute.type.nullable ? "?" : "",
  328. attribute.name);
  329. }
  330. dbgln("Functions:");
  331. for (auto& function : interface->functions) {
  332. dbgln(" {}{} {}",
  333. function.return_type.name,
  334. function.return_type.nullable ? "?" : "",
  335. function.name);
  336. for (auto& parameter : function.parameters) {
  337. dbgln(" {}{} {}",
  338. parameter.type.name,
  339. parameter.type.nullable ? "?" : "",
  340. parameter.name);
  341. }
  342. }
  343. }
  344. if (header_mode)
  345. generate_header(*interface);
  346. if (implementation_mode)
  347. generate_implementation(*interface);
  348. if (constructor_header_mode)
  349. generate_constructor_header(*interface);
  350. if (constructor_implementation_mode)
  351. generate_constructor_implementation(*interface);
  352. if (prototype_header_mode)
  353. generate_prototype_header(*interface);
  354. if (prototype_implementation_mode)
  355. generate_prototype_implementation(*interface);
  356. return 0;
  357. }
  358. static bool should_emit_wrapper_factory(const IDL::Interface& interface)
  359. {
  360. // FIXME: This is very hackish.
  361. if (interface.name == "Event")
  362. return false;
  363. if (interface.name == "EventTarget")
  364. return false;
  365. if (interface.name == "Node")
  366. return false;
  367. if (interface.name == "Text")
  368. return false;
  369. if (interface.name == "Document")
  370. return false;
  371. if (interface.name == "DocumentType")
  372. return false;
  373. if (interface.name.ends_with("Element"))
  374. return false;
  375. return true;
  376. }
  377. static bool is_wrappable_type(const IDL::Type& type)
  378. {
  379. if (type.name == "Node")
  380. return true;
  381. if (type.name == "Document")
  382. return true;
  383. if (type.name == "Text")
  384. return true;
  385. if (type.name == "DocumentType")
  386. return true;
  387. if (type.name.ends_with("Element"))
  388. return true;
  389. if (type.name == "ImageData")
  390. return true;
  391. return false;
  392. }
  393. static void generate_header(const IDL::Interface& interface)
  394. {
  395. StringBuilder builder;
  396. SourceGenerator generator { builder };
  397. generator.set("name", interface.name);
  398. generator.set("fully_qualified_name", interface.fully_qualified_name);
  399. generator.set("wrapper_base_class", interface.wrapper_base_class);
  400. generator.set("wrapper_class", interface.wrapper_class);
  401. generator.set("wrapper_class:snakecase", snake_name(interface.wrapper_class));
  402. generator.append(R"~~~(
  403. #pragma once
  404. #include <LibWeb/Bindings/Wrapper.h>
  405. // FIXME: This is very strange.
  406. #if __has_include(<LibWeb/DOM/@name@.h>)
  407. # include <LibWeb/DOM/@name@.h>
  408. #elif __has_include(<LibWeb/HTML/@name@.h>)
  409. # include <LibWeb/HTML/@name@.h>
  410. #elif __has_include(<LibWeb/UIEvents/@name@.h>)
  411. # include <LibWeb/UIEvents/@name@.h>
  412. #elif __has_include(<LibWeb/HighResolutionTime/@name@.h>)
  413. # include <LibWeb/HighResolutionTime/@name@.h>
  414. #elif __has_include(<LibWeb/NavigationTiming/@name@.h>)
  415. # include <LibWeb/NavigationTiming/@name@.h>
  416. #elif __has_include(<LibWeb/SVG/@name@.h>)
  417. # include <LibWeb/SVG/@name@.h>
  418. #endif
  419. )~~~");
  420. if (interface.wrapper_base_class != "Wrapper") {
  421. generator.append(R"~~~(
  422. #include <LibWeb/Bindings/@wrapper_base_class@.h>
  423. )~~~");
  424. }
  425. generator.append(R"~~~(
  426. namespace Web::Bindings {
  427. class @wrapper_class@ : public @wrapper_base_class@ {
  428. JS_OBJECT(@wrapper_class@, @wrapper_base_class@);
  429. public:
  430. @wrapper_class@(JS::GlobalObject&, @fully_qualified_name@&);
  431. virtual void initialize(JS::GlobalObject&) override;
  432. virtual ~@wrapper_class@() override;
  433. )~~~");
  434. if (interface.wrapper_base_class == "Wrapper") {
  435. generator.append(R"~~~(
  436. @fully_qualified_name@& impl() { return *m_impl; }
  437. const @fully_qualified_name@& impl() const { return *m_impl; }
  438. )~~~");
  439. } else {
  440. generator.append(R"~~~(
  441. @fully_qualified_name@& impl() { return static_cast<@fully_qualified_name@&>(@wrapper_base_class@::impl()); }
  442. const @fully_qualified_name@& impl() const { return static_cast<const @fully_qualified_name@&>(@wrapper_base_class@::impl()); }
  443. )~~~");
  444. }
  445. generator.append(R"~~~(
  446. private:
  447. )~~~");
  448. if (interface.wrapper_base_class == "Wrapper") {
  449. generator.append(R"~~~(
  450. NonnullRefPtr<@fully_qualified_name@> m_impl;
  451. )~~~");
  452. }
  453. generator.append(R"~~~(
  454. };
  455. )~~~");
  456. if (should_emit_wrapper_factory(interface)) {
  457. generator.append(R"~~~(
  458. @wrapper_class@* wrap(JS::GlobalObject&, @fully_qualified_name@&);
  459. )~~~");
  460. }
  461. generator.append(R"~~~(
  462. } // namespace Web::Bindings
  463. )~~~");
  464. outln("{}", generator.as_string_view());
  465. }
  466. void generate_implementation(const IDL::Interface& interface)
  467. {
  468. StringBuilder builder;
  469. SourceGenerator generator { builder };
  470. generator.set("name", interface.name);
  471. generator.set("wrapper_class", interface.wrapper_class);
  472. generator.set("wrapper_base_class", interface.wrapper_base_class);
  473. generator.set("prototype_class", interface.prototype_class);
  474. generator.set("fully_qualified_name", interface.fully_qualified_name);
  475. generator.append(R"~~~(
  476. #include <AK/FlyString.h>
  477. #include <LibJS/Runtime/Array.h>
  478. #include <LibJS/Runtime/Error.h>
  479. #include <LibJS/Runtime/Function.h>
  480. #include <LibJS/Runtime/GlobalObject.h>
  481. #include <LibJS/Runtime/Uint8ClampedArray.h>
  482. #include <LibJS/Runtime/Value.h>
  483. #include <LibWeb/Bindings/@prototype_class@.h>
  484. #include <LibWeb/Bindings/@wrapper_class@.h>
  485. #include <LibWeb/Bindings/CanvasRenderingContext2DWrapper.h>
  486. #include <LibWeb/Bindings/CommentWrapper.h>
  487. #include <LibWeb/Bindings/DOMImplementationWrapper.h>
  488. #include <LibWeb/Bindings/DocumentFragmentWrapper.h>
  489. #include <LibWeb/Bindings/DocumentTypeWrapper.h>
  490. #include <LibWeb/Bindings/DocumentWrapper.h>
  491. #include <LibWeb/Bindings/EventTargetWrapperFactory.h>
  492. #include <LibWeb/Bindings/HTMLCanvasElementWrapper.h>
  493. #include <LibWeb/Bindings/HTMLHeadElementWrapper.h>
  494. #include <LibWeb/Bindings/HTMLImageElementWrapper.h>
  495. #include <LibWeb/Bindings/ImageDataWrapper.h>
  496. #include <LibWeb/Bindings/NodeWrapperFactory.h>
  497. #include <LibWeb/Bindings/TextWrapper.h>
  498. #include <LibWeb/Bindings/WindowObject.h>
  499. #include <LibWeb/DOM/Element.h>
  500. #include <LibWeb/DOM/EventListener.h>
  501. #include <LibWeb/HTML/HTMLElement.h>
  502. #include <LibWeb/Origin.h>
  503. // FIXME: This is a total hack until we can figure out the namespace for a given type somehow.
  504. using namespace Web::DOM;
  505. using namespace Web::HTML;
  506. namespace Web::Bindings {
  507. )~~~");
  508. if (interface.wrapper_base_class == "Wrapper") {
  509. generator.append(R"~~~(
  510. @wrapper_class@::@wrapper_class@(JS::GlobalObject& global_object, @fully_qualified_name@& impl)
  511. : Wrapper(static_cast<WindowObject&>(global_object).ensure_web_prototype<@prototype_class@>("@name@"))
  512. , m_impl(impl)
  513. {
  514. }
  515. )~~~");
  516. } else {
  517. generator.append(R"~~~(
  518. @wrapper_class@::@wrapper_class@(JS::GlobalObject& global_object, @fully_qualified_name@& impl)
  519. : @wrapper_base_class@(global_object, impl)
  520. {
  521. set_prototype(&static_cast<WindowObject&>(global_object).ensure_web_prototype<@prototype_class@>("@name@"));
  522. }
  523. )~~~");
  524. }
  525. generator.append(R"~~~(
  526. void @wrapper_class@::initialize(JS::GlobalObject& global_object)
  527. {
  528. @wrapper_base_class@::initialize(global_object);
  529. }
  530. @wrapper_class@::~@wrapper_class@()
  531. {
  532. }
  533. )~~~");
  534. if (should_emit_wrapper_factory(interface)) {
  535. generator.append(R"~~~(
  536. @wrapper_class@* wrap(JS::GlobalObject& global_object, @fully_qualified_name@& impl)
  537. {
  538. return static_cast<@wrapper_class@*>(wrap_impl(global_object, impl));
  539. }
  540. )~~~");
  541. }
  542. generator.append(R"~~~(
  543. } // namespace Web::Bindings
  544. )~~~");
  545. outln("{}", generator.as_string_view());
  546. }
  547. static void generate_constructor_header(const IDL::Interface& interface)
  548. {
  549. StringBuilder builder;
  550. SourceGenerator generator { builder };
  551. generator.set("name", interface.name);
  552. generator.set("fully_qualified_name", interface.fully_qualified_name);
  553. generator.set("constructor_class", interface.constructor_class);
  554. generator.set("constructor_class:snakecase", snake_name(interface.constructor_class));
  555. generator.append(R"~~~(
  556. #pragma once
  557. #include <LibJS/Runtime/NativeFunction.h>
  558. namespace Web::Bindings {
  559. class @constructor_class@ : public JS::NativeFunction {
  560. JS_OBJECT(@constructor_class@, JS::NativeFunction);
  561. public:
  562. explicit @constructor_class@(JS::GlobalObject&);
  563. virtual void initialize(JS::GlobalObject&) override;
  564. virtual ~@constructor_class@() override;
  565. virtual JS::Value call() override;
  566. virtual JS::Value construct(JS::Function& new_target) override;
  567. private:
  568. virtual bool has_constructor() const override { return true; }
  569. };
  570. } // namespace Web::Bindings
  571. )~~~");
  572. outln("{}", generator.as_string_view());
  573. }
  574. void generate_constructor_implementation(const IDL::Interface& interface)
  575. {
  576. StringBuilder builder;
  577. SourceGenerator generator { builder };
  578. generator.set("name", interface.name);
  579. generator.set("prototype_class", interface.prototype_class);
  580. generator.set("wrapper_class", interface.wrapper_class);
  581. generator.set("constructor_class", interface.constructor_class);
  582. generator.set("prototype_class:snakecase", snake_name(interface.prototype_class));
  583. generator.set("fully_qualified_name", interface.fully_qualified_name);
  584. generator.append(R"~~~(
  585. #include <LibJS/Heap/Heap.h>
  586. #include <LibJS/Runtime/GlobalObject.h>
  587. #include <LibWeb/Bindings/@constructor_class@.h>
  588. #include <LibWeb/Bindings/@prototype_class@.h>
  589. #include <LibWeb/Bindings/@wrapper_class@.h>
  590. #include <LibWeb/Bindings/WindowObject.h>
  591. #if __has_include(<LibWeb/DOM/@name@.h>)
  592. # include <LibWeb/DOM/@name@.h>
  593. #elif __has_include(<LibWeb/HTML/@name@.h>)
  594. # include <LibWeb/HTML/@name@.h>
  595. #elif __has_include(<LibWeb/UIEvents/@name@.h>)
  596. # include <LibWeb/UIEvents/@name@.h>
  597. #elif __has_include(<LibWeb/HighResolutionTime/@name@.h>)
  598. # include <LibWeb/HighResolutionTime/@name@.h>
  599. #elif __has_include(<LibWeb/NavigationTiming/@name@.h>)
  600. # include <LibWeb/NavigationTiming/@name@.h>
  601. #elif __has_include(<LibWeb/SVG/@name@.h>)
  602. # include <LibWeb/SVG/@name@.h>
  603. #endif
  604. // FIXME: This is a total hack until we can figure out the namespace for a given type somehow.
  605. using namespace Web::DOM;
  606. using namespace Web::HTML;
  607. namespace Web::Bindings {
  608. @constructor_class@::@constructor_class@(JS::GlobalObject& global_object)
  609. : NativeFunction(*global_object.function_prototype())
  610. {
  611. }
  612. @constructor_class@::~@constructor_class@()
  613. {
  614. }
  615. JS::Value @constructor_class@::call()
  616. {
  617. vm().throw_exception<JS::TypeError>(global_object(), JS::ErrorType::ConstructorWithoutNew, "@name@");
  618. return {};
  619. }
  620. JS::Value @constructor_class@::construct(Function&)
  621. {
  622. return {};
  623. #if 0
  624. // FIXME: It would be cool to construct stuff!
  625. auto& window = static_cast<WindowObject&>(global_object());
  626. return heap().allocate<@wrapper_class@>(window, window, @fully_qualified_name@::create(window.impl()));
  627. #endif
  628. }
  629. void @constructor_class@::initialize(JS::GlobalObject& global_object)
  630. {
  631. auto& vm = this->vm();
  632. auto& window = static_cast<WindowObject&>(global_object);
  633. [[maybe_unused]] u8 default_attributes = JS::Attribute::Enumerable;
  634. NativeFunction::initialize(global_object);
  635. define_property(vm.names.prototype, &window.ensure_web_prototype<@prototype_class@>("@name@"), 0);
  636. define_property(vm.names.length, JS::Value(1), JS::Attribute::Configurable);
  637. }
  638. } // namespace Web::Bindings
  639. )~~~");
  640. outln("{}", generator.as_string_view());
  641. }
  642. static void generate_prototype_header(const IDL::Interface& interface)
  643. {
  644. StringBuilder builder;
  645. SourceGenerator generator { builder };
  646. generator.set("name", interface.name);
  647. generator.set("fully_qualified_name", interface.fully_qualified_name);
  648. generator.set("prototype_class", interface.prototype_class);
  649. generator.set("prototype_class:snakecase", snake_name(interface.prototype_class));
  650. generator.append(R"~~~(
  651. #pragma once
  652. #include <LibJS/Runtime/Object.h>
  653. namespace Web::Bindings {
  654. class @prototype_class@ : public JS::Object {
  655. JS_OBJECT(@prototype_class@, JS::Object);
  656. public:
  657. explicit @prototype_class@(JS::GlobalObject&);
  658. virtual void initialize(JS::GlobalObject&) override;
  659. virtual ~@prototype_class@() override;
  660. private:
  661. )~~~");
  662. for (auto& function : interface.functions) {
  663. auto function_generator = generator.fork();
  664. function_generator.set("function.name:snakecase", snake_name(function.name));
  665. function_generator.append(R"~~~(
  666. JS_DECLARE_NATIVE_FUNCTION(@function.name:snakecase@);
  667. )~~~");
  668. }
  669. for (auto& attribute : interface.attributes) {
  670. auto attribute_generator = generator.fork();
  671. attribute_generator.set("attribute.name:snakecase", snake_name(attribute.name));
  672. attribute_generator.append(R"~~~(
  673. JS_DECLARE_NATIVE_GETTER(@attribute.name:snakecase@_getter);
  674. )~~~");
  675. if (!attribute.readonly) {
  676. attribute_generator.append(R"~~~(
  677. JS_DECLARE_NATIVE_SETTER(@attribute.name:snakecase@_setter);
  678. )~~~");
  679. }
  680. }
  681. generator.append(R"~~~(
  682. };
  683. } // namespace Web::Bindings
  684. )~~~");
  685. outln("{}", generator.as_string_view());
  686. }
  687. void generate_prototype_implementation(const IDL::Interface& interface)
  688. {
  689. StringBuilder builder;
  690. SourceGenerator generator { builder };
  691. generator.set("name", interface.name);
  692. generator.set("parent_name", interface.parent_name);
  693. generator.set("prototype_class", interface.prototype_class);
  694. generator.set("prototype_base_class", interface.prototype_base_class);
  695. generator.set("wrapper_class", interface.wrapper_class);
  696. generator.set("constructor_class", interface.constructor_class);
  697. generator.set("prototype_class:snakecase", snake_name(interface.prototype_class));
  698. generator.set("fully_qualified_name", interface.fully_qualified_name);
  699. generator.append(R"~~~(
  700. #include <AK/Function.h>
  701. #include <LibJS/Runtime/Array.h>
  702. #include <LibJS/Runtime/Error.h>
  703. #include <LibJS/Runtime/Function.h>
  704. #include <LibJS/Runtime/GlobalObject.h>
  705. #include <LibJS/Runtime/Uint8ClampedArray.h>
  706. #include <LibWeb/Bindings/@prototype_class@.h>
  707. #include <LibWeb/Bindings/@wrapper_class@.h>
  708. #include <LibWeb/Bindings/CanvasRenderingContext2DWrapper.h>
  709. #include <LibWeb/Bindings/CommentWrapper.h>
  710. #include <LibWeb/Bindings/DOMImplementationWrapper.h>
  711. #include <LibWeb/Bindings/DocumentFragmentWrapper.h>
  712. #include <LibWeb/Bindings/DocumentTypeWrapper.h>
  713. #include <LibWeb/Bindings/DocumentWrapper.h>
  714. #include <LibWeb/Bindings/EventTargetWrapperFactory.h>
  715. #include <LibWeb/Bindings/HTMLCanvasElementWrapper.h>
  716. #include <LibWeb/Bindings/HTMLHeadElementWrapper.h>
  717. #include <LibWeb/Bindings/HTMLImageElementWrapper.h>
  718. #include <LibWeb/Bindings/ImageDataWrapper.h>
  719. #include <LibWeb/Bindings/NodeWrapperFactory.h>
  720. #include <LibWeb/Bindings/PerformanceTimingWrapper.h>
  721. #include <LibWeb/Bindings/TextWrapper.h>
  722. #include <LibWeb/Bindings/WindowObject.h>
  723. #include <LibWeb/DOM/Element.h>
  724. #include <LibWeb/DOM/EventListener.h>
  725. #include <LibWeb/DOM/Window.h>
  726. #include <LibWeb/HTML/HTMLElement.h>
  727. #include <LibWeb/NavigationTiming/PerformanceTiming.h>
  728. #include <LibWeb/Origin.h>
  729. #if __has_include(<LibWeb/Bindings/@prototype_base_class@.h>)
  730. # include <LibWeb/Bindings/@prototype_base_class@.h>
  731. #endif
  732. #if __has_include(<LibWeb/DOM/@name@.h>)
  733. # include <LibWeb/DOM/@name@.h>
  734. #elif __has_include(<LibWeb/HTML/@name@.h>)
  735. # include <LibWeb/HTML/@name@.h>
  736. #elif __has_include(<LibWeb/UIEvents/@name@.h>)
  737. # include <LibWeb/UIEvents/@name@.h>
  738. #elif __has_include(<LibWeb/HighResolutionTime/@name@.h>)
  739. # include <LibWeb/HighResolutionTime/@name@.h>
  740. #elif __has_include(<LibWeb/NavigationTiming/@name@.h>)
  741. # include <LibWeb/NavigationTiming/@name@.h>
  742. #elif __has_include(<LibWeb/SVG/@name@.h>)
  743. # include <LibWeb/SVG/@name@.h>
  744. #endif
  745. // FIXME: This is a total hack until we can figure out the namespace for a given type somehow.
  746. using namespace Web::DOM;
  747. using namespace Web::HTML;
  748. using namespace Web::NavigationTiming;
  749. namespace Web::Bindings {
  750. @prototype_class@::@prototype_class@(JS::GlobalObject& global_object)
  751. : Object(*global_object.object_prototype())
  752. {
  753. )~~~");
  754. if (!interface.parent_name.is_empty()) {
  755. generator.append(R"~~~(
  756. set_prototype(&static_cast<WindowObject&>(global_object).ensure_web_prototype<@prototype_base_class@>("@parent_name@"));
  757. )~~~");
  758. }
  759. generator.append(R"~~~(
  760. }
  761. @prototype_class@::~@prototype_class@()
  762. {
  763. }
  764. void @prototype_class@::initialize(JS::GlobalObject& global_object)
  765. {
  766. [[maybe_unused]] auto& vm = this->vm();
  767. [[maybe_unused]] u8 default_attributes = JS::Attribute::Enumerable | JS::Attribute::Configurable;
  768. )~~~");
  769. for (auto& attribute : interface.attributes) {
  770. auto attribute_generator = generator.fork();
  771. attribute_generator.set("attribute.name", attribute.name);
  772. attribute_generator.set("attribute.getter_callback", attribute.getter_callback_name);
  773. if (attribute.readonly)
  774. attribute_generator.set("attribute.setter_callback", "nullptr");
  775. else
  776. attribute_generator.set("attribute.setter_callback", attribute.setter_callback_name);
  777. attribute_generator.append(R"~~~(
  778. define_native_property("@attribute.name@", @attribute.getter_callback@, @attribute.setter_callback@, default_attributes);
  779. )~~~");
  780. }
  781. for (auto& function : interface.functions) {
  782. auto function_generator = generator.fork();
  783. function_generator.set("function.name", function.name);
  784. function_generator.set("function.name:snakecase", snake_name(function.name));
  785. function_generator.set("function.name:length", String::number(function.name.length()));
  786. function_generator.append(R"~~~(
  787. define_native_function("@function.name@", @function.name:snakecase@, @function.name:length@, default_attributes);
  788. )~~~");
  789. }
  790. generator.append(R"~~~(
  791. Object::initialize(global_object);
  792. }
  793. )~~~");
  794. if (!interface.attributes.is_empty() || !interface.functions.is_empty()) {
  795. generator.append(R"~~~(
  796. static @fully_qualified_name@* impl_from(JS::VM& vm, JS::GlobalObject& global_object)
  797. {
  798. auto* this_object = vm.this_value(global_object).to_object(global_object);
  799. if (!this_object)
  800. return {};
  801. )~~~");
  802. if (interface.name == "EventTarget") {
  803. generator.append(R"~~~(
  804. if (is<WindowObject>(this_object)) {
  805. return &static_cast<WindowObject*>(this_object)->impl();
  806. }
  807. )~~~");
  808. }
  809. generator.append(R"~~~(
  810. if (!is<@wrapper_class@>(this_object)) {
  811. vm.throw_exception<JS::TypeError>(global_object, JS::ErrorType::NotA, "@fully_qualified_name@");
  812. return nullptr;
  813. }
  814. return &static_cast<@wrapper_class@*>(this_object)->impl();
  815. }
  816. )~~~");
  817. }
  818. auto generate_to_cpp = [&](auto& parameter, auto& js_name, const auto& js_suffix, auto cpp_name, bool return_void = false, bool legacy_null_to_empty_string = false, bool optional = false) {
  819. auto scoped_generator = generator.fork();
  820. scoped_generator.set("cpp_name", cpp_name);
  821. scoped_generator.set("js_name", js_name);
  822. scoped_generator.set("js_suffix", js_suffix);
  823. scoped_generator.set("legacy_null_to_empty_string", legacy_null_to_empty_string ? "true" : "false");
  824. scoped_generator.set("parameter.type.name", parameter.type.name);
  825. if (return_void)
  826. scoped_generator.set("return_statement", "return;");
  827. else
  828. scoped_generator.set("return_statement", "return {};");
  829. // FIXME: Add support for optional to all types
  830. if (parameter.type.name == "DOMString") {
  831. if (!optional) {
  832. scoped_generator.append(R"~~~(
  833. auto @cpp_name@ = @js_name@@js_suffix@.to_string(global_object, @legacy_null_to_empty_string@);
  834. if (vm.exception())
  835. @return_statement@
  836. )~~~");
  837. } else {
  838. scoped_generator.append(R"~~~(
  839. String @cpp_name@;
  840. if (!@js_name@@js_suffix@.is_undefined()) {
  841. @cpp_name@ = @js_name@@js_suffix@.to_string(global_object, @legacy_null_to_empty_string@);
  842. if (vm.exception())
  843. @return_statement@
  844. }
  845. )~~~");
  846. }
  847. } else if (parameter.type.name == "EventListener") {
  848. scoped_generator.append(R"~~~(
  849. if (!@js_name@@js_suffix@.is_function()) {
  850. vm.throw_exception<JS::TypeError>(global_object, JS::ErrorType::NotA, "Function");
  851. @return_statement@
  852. }
  853. auto @cpp_name@ = adopt(*new EventListener(JS::make_handle(&@js_name@@js_suffix@.as_function())));
  854. )~~~");
  855. } else if (is_wrappable_type(parameter.type)) {
  856. scoped_generator.append(R"~~~(
  857. auto @cpp_name@_object = @js_name@@js_suffix@.to_object(global_object);
  858. if (vm.exception())
  859. @return_statement@
  860. if (!is<@parameter.type.name@Wrapper>(@cpp_name@_object)) {
  861. vm.throw_exception<JS::TypeError>(global_object, JS::ErrorType::NotA, "@parameter.type.name@");
  862. @return_statement@
  863. }
  864. auto& @cpp_name@ = static_cast<@parameter.type.name@Wrapper*>(@cpp_name@_object)->impl();
  865. )~~~");
  866. } else if (parameter.type.name == "double") {
  867. scoped_generator.append(R"~~~(
  868. auto @cpp_name@ = @js_name@@js_suffix@.to_double(global_object);
  869. if (vm.exception())
  870. @return_statement@
  871. )~~~");
  872. } else if (parameter.type.name == "boolean") {
  873. scoped_generator.append(R"~~~(
  874. auto @cpp_name@ = @js_name@@js_suffix@.to_boolean();
  875. )~~~");
  876. } else {
  877. dbgln("Unimplemented JS-to-C++ conversion: {}", parameter.type.name);
  878. ASSERT_NOT_REACHED();
  879. }
  880. };
  881. auto generate_arguments = [&](auto& parameters, auto& arguments_builder, bool return_void = false) {
  882. auto arguments_generator = generator.fork();
  883. Vector<String> parameter_names;
  884. size_t argument_index = 0;
  885. for (auto& parameter : parameters) {
  886. parameter_names.append(snake_name(parameter.name));
  887. arguments_generator.set("argument.index", String::number(argument_index));
  888. arguments_generator.append(R"~~~(
  889. auto arg@argument.index@ = vm.argument(@argument.index@);
  890. )~~~");
  891. // FIXME: Parameters can have [LegacyNullToEmptyString] attached.
  892. generate_to_cpp(parameter, "arg", String::number(argument_index), snake_name(parameter.name), return_void, false, parameter.optional);
  893. ++argument_index;
  894. }
  895. arguments_builder.join(", ", parameter_names);
  896. };
  897. auto generate_return_statement = [&](auto& return_type) {
  898. auto scoped_generator = generator.fork();
  899. scoped_generator.set("return_type", return_type.name);
  900. if (return_type.name == "undefined") {
  901. scoped_generator.append(R"~~~(
  902. return JS::js_undefined();
  903. )~~~");
  904. return;
  905. }
  906. if (return_type.nullable) {
  907. if (return_type.name == "DOMString") {
  908. scoped_generator.append(R"~~~(
  909. if (retval.is_null())
  910. return JS::js_null();
  911. )~~~");
  912. } else {
  913. scoped_generator.append(R"~~~(
  914. if (!retval)
  915. return JS::js_null();
  916. )~~~");
  917. }
  918. }
  919. if (return_type.name == "DOMString") {
  920. scoped_generator.append(R"~~~(
  921. return JS::js_string(vm, retval);
  922. )~~~");
  923. } else if (return_type.name == "ArrayFromVector") {
  924. // FIXME: Remove this fake type hack once it's no longer needed.
  925. // Basically once we have NodeList we can throw this out.
  926. scoped_generator.append(R"~~~(
  927. auto* new_array = JS::Array::create(global_object);
  928. for (auto& element : retval)
  929. new_array->indexed_properties().append(wrap(global_object, element));
  930. return new_array;
  931. )~~~");
  932. } else if (return_type.name == "long" || return_type.name == "double" || return_type.name == "boolean" || return_type.name == "short") {
  933. scoped_generator.append(R"~~~(
  934. return JS::Value(retval);
  935. )~~~");
  936. } else if (return_type.name == "Uint8ClampedArray") {
  937. scoped_generator.append(R"~~~(
  938. return retval;
  939. )~~~");
  940. } else {
  941. scoped_generator.append(R"~~~(
  942. return wrap(global_object, const_cast<@return_type@&>(*retval));
  943. )~~~");
  944. }
  945. };
  946. for (auto& attribute : interface.attributes) {
  947. auto attribute_generator = generator.fork();
  948. attribute_generator.set("attribute.getter_callback", attribute.getter_callback_name);
  949. attribute_generator.set("attribute.setter_callback", attribute.setter_callback_name);
  950. attribute_generator.set("attribute.name:snakecase", snake_name(attribute.name));
  951. if (attribute.extended_attributes.contains("Reflect")) {
  952. auto attribute_name = attribute.extended_attributes.get("Reflect").value();
  953. if (attribute_name.is_null())
  954. attribute_name = attribute.name;
  955. attribute_name = make_input_acceptable_cpp(attribute_name);
  956. attribute_generator.set("attribute.reflect_name", attribute_name);
  957. } else {
  958. attribute_generator.set("attribute.reflect_name", snake_name(attribute.name));
  959. }
  960. attribute_generator.append(R"~~~(
  961. JS_DEFINE_NATIVE_GETTER(@prototype_class@::@attribute.getter_callback@)
  962. {
  963. auto* impl = impl_from(vm, global_object);
  964. if (!impl)
  965. return {};
  966. )~~~");
  967. if (attribute.extended_attributes.contains("ReturnNullIfCrossOrigin")) {
  968. attribute_generator.append(R"~~~(
  969. if (!impl->may_access_from_origin(static_cast<WindowObject&>(global_object).origin()))
  970. return JS::js_null();
  971. )~~~");
  972. }
  973. if (attribute.extended_attributes.contains("Reflect")) {
  974. if (attribute.type.name != "boolean") {
  975. attribute_generator.append(R"~~~(
  976. auto retval = impl->attribute(HTML::AttributeNames::@attribute.reflect_name@);
  977. )~~~");
  978. } else {
  979. attribute_generator.append(R"~~~(
  980. auto retval = impl->has_attribute(HTML::AttributeNames::@attribute.reflect_name@);
  981. )~~~");
  982. }
  983. } else {
  984. attribute_generator.append(R"~~~(
  985. auto retval = impl->@attribute.name:snakecase@();
  986. )~~~");
  987. }
  988. generate_return_statement(attribute.type);
  989. attribute_generator.append(R"~~~(
  990. }
  991. )~~~");
  992. if (!attribute.readonly) {
  993. attribute_generator.append(R"~~~(
  994. JS_DEFINE_NATIVE_SETTER(@prototype_class@::@attribute.setter_callback@)
  995. {
  996. auto* impl = impl_from(vm, global_object);
  997. if (!impl)
  998. return;
  999. )~~~");
  1000. generate_to_cpp(attribute, "value", "", "cpp_value", true, attribute.extended_attributes.contains("LegacyNullToEmptyString"));
  1001. if (attribute.extended_attributes.contains("Reflect")) {
  1002. if (attribute.type.name != "boolean") {
  1003. attribute_generator.append(R"~~~(
  1004. impl->set_attribute(HTML::AttributeNames::@attribute.reflect_name@, cpp_value);
  1005. )~~~");
  1006. } else {
  1007. attribute_generator.append(R"~~~(
  1008. if (!cpp_value)
  1009. impl->remove_attribute(HTML::AttributeNames::@attribute.reflect_name@);
  1010. else
  1011. impl->set_attribute(HTML::AttributeNames::@attribute.reflect_name@, String::empty());
  1012. )~~~");
  1013. }
  1014. } else {
  1015. attribute_generator.append(R"~~~(
  1016. impl->set_@attribute.name:snakecase@(cpp_value);
  1017. )~~~");
  1018. }
  1019. attribute_generator.append(R"~~~(
  1020. }
  1021. )~~~");
  1022. }
  1023. }
  1024. // Implementation: Functions
  1025. for (auto& function : interface.functions) {
  1026. auto function_generator = generator.fork();
  1027. function_generator.set("function.name", function.name);
  1028. function_generator.set("function.name:snakecase", snake_name(function.name));
  1029. function_generator.set("function.nargs", String::number(function.length()));
  1030. function_generator.append(R"~~~(
  1031. JS_DEFINE_NATIVE_FUNCTION(@prototype_class@::@function.name:snakecase@)
  1032. {
  1033. auto* impl = impl_from(vm, global_object);
  1034. if (!impl)
  1035. return {};
  1036. )~~~");
  1037. if (function.length() > 0) {
  1038. if (function.length() == 1) {
  1039. function_generator.set(".bad_arg_count", "JS::ErrorType::BadArgCountOne");
  1040. function_generator.set(".arg_count_suffix", "");
  1041. } else {
  1042. function_generator.set(".bad_arg_count", "JS::ErrorType::BadArgCountMany");
  1043. function_generator.set(".arg_count_suffix", String::formatted(", \"{}\"", function.length()));
  1044. }
  1045. function_generator.append(R"~~~(
  1046. if (vm.argument_count() < @function.nargs@) {
  1047. vm.throw_exception<JS::TypeError>(global_object, @.bad_arg_count@, "@function.name@"@.arg_count_suffix@);
  1048. return {};
  1049. }
  1050. )~~~");
  1051. }
  1052. StringBuilder arguments_builder;
  1053. generate_arguments(function.parameters, arguments_builder);
  1054. function_generator.set(".arguments", arguments_builder.string_view());
  1055. if (function.return_type.name != "undefined") {
  1056. function_generator.append(R"~~~(
  1057. auto retval = impl->@function.name:snakecase@(@.arguments@);
  1058. )~~~");
  1059. } else {
  1060. function_generator.append(R"~~~(
  1061. impl->@function.name:snakecase@(@.arguments@);
  1062. )~~~");
  1063. }
  1064. generate_return_statement(function.return_type);
  1065. function_generator.append(R"~~~(
  1066. }
  1067. )~~~");
  1068. }
  1069. generator.append(R"~~~(
  1070. } // namespace Web::Bindings
  1071. )~~~");
  1072. outln("{}", generator.as_string_view());
  1073. }