1 /* duktape binding generation implementation
2 *
3 * This file is part of nsgenbind.
4 * Licensed under the MIT License,
5 * http://www.opensource.org/licenses/mit-license.php
6 * Copyright 2015 Vincent Sanders <vince@netsurf-browser.org>
7 */
8
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <stdbool.h>
12 #include <string.h>
13 #include <unistd.h>
14 #include <getopt.h>
15 #include <errno.h>
16 #include <ctype.h>
17
18 #include "options.h"
19 #include "utils.h"
20 #include "nsgenbind-ast.h"
21 #include "webidl-ast.h"
22 #include "ir.h"
23 #include "output.h"
24 #include "duk-libdom.h"
25
26 /** prefix for all generated functions */
27 #define DLPFX "dukky"
28
29 #define MAGICPFX "\\xFF\\xFFNETSURF_DUKTAPE_"
30
31 /**
32 * \todo Constructors
33 * \todo dukky_inject_not_ctr as binding.c function
34 * \todo instanceof needs cleaning/generalising
35 * \todo large text sections
36 * \todo operations with elipsis need parameter checking love
37 * \todo parameter type checking probably should do instanceof on user objects
38 * \todo tool should generate warning for interfaces with empty private structs for the entire inheritance chain
39 */
40
41 /**
42 * generate a duktape prototype name
43 */
get_prototype_name(const char * interface_name)44 static char *get_prototype_name(const char *interface_name)
45 {
46 char *proto_name;
47 int pnamelen;
48 int pfxlen;
49
50 /* duplicate the interface name in upper case */
51 pfxlen = SLEN(MAGICPFX) + SLEN("PROTOTYPE_");
52 pnamelen = strlen(interface_name) + 1;
53
54 proto_name = malloc(pnamelen + pfxlen);
55 snprintf(proto_name, pnamelen + pfxlen, "%sPROTOTYPE_%s", MAGICPFX, interface_name);
56 for (pnamelen-- ; pnamelen >= 0; pnamelen--) {
57 proto_name[pnamelen + pfxlen] = toupper(interface_name[pnamelen]);
58 }
59 return proto_name;
60 }
61
62
63
64
65
open_header(struct ir * ir,const char * name)66 static struct opctx *open_header(struct ir *ir, const char *name)
67 {
68 char *fname;
69 int fnamel;
70 struct opctx *hdrc;
71 int res;
72
73 fnamel = strlen(name) + 4;
74 fname = malloc(fnamel);
75 snprintf(fname, fnamel, "%s.h", name);
76
77 /* open the output header file */
78 res = output_open(fname, &hdrc);
79 free(fname);
80 if (res != 0 ) {
81 return NULL;
82 }
83
84 /* tool preface */
85 output_tool_preface(hdrc);
86
87 /* binding preface */
88 output_method_cdata(hdrc,
89 ir->binding_node,
90 GENBIND_METHOD_TYPE_PREFACE);
91
92 /* header guard */
93 outputf(hdrc,
94 "\n#ifndef %s_%s_h\n", DLPFX, name);
95 outputf(hdrc,
96 "#define %s_%s_h\n\n", DLPFX, name);
97
98 return hdrc;
99 }
100
close_header(struct ir * ir,struct opctx * hdrc)101 static int close_header(struct ir *ir, struct opctx *hdrc)
102 {
103 /* close header guard */
104 outputf(hdrc,
105 "\n#endif\n");
106
107 /* binding postface */
108 output_method_cdata(hdrc,
109 ir->binding_node,
110 GENBIND_METHOD_TYPE_POSTFACE);
111
112 output_close(hdrc);
113
114 return 0;
115 }
116
117
118 /**
119 * generate private header
120 */
121 static int
output_private_header(struct ir * ir)122 output_private_header(struct ir *ir)
123 {
124 int idx;
125 struct opctx *privc;
126
127 /* open header */
128 privc = open_header(ir, "private");
129
130 for (idx = 0; idx < ir->entryc; idx++) {
131 struct ir_entry *interfacee;
132 struct ir_entry *inherite;
133 struct genbind_node *priv_node;
134
135 interfacee = ir->entries + idx;
136
137 /* do not generate private structs for interfaces marked no
138 * output
139 */
140 if ((interfacee->type == IR_ENTRY_TYPE_INTERFACE) &&
141 (interfacee->u.interface.noobject)) {
142 continue;
143 }
144
145 switch (interfacee->type) {
146 case IR_ENTRY_TYPE_INTERFACE:
147 outputf(privc,
148 "/* Private data for %s interface */\n",
149 interfacee->name);
150 break;
151
152 case IR_ENTRY_TYPE_DICTIONARY:
153 outputf(privc,
154 "/* Private data for %s dictionary */\n",
155 interfacee->name);
156 break;
157 }
158
159 outputf(privc,
160 "typedef struct {\n");
161
162 /* find parent entry and include in private */
163 inherite = ir_inherit_entry(ir, interfacee);
164 if (inherite != NULL) {
165 outputf(privc,
166 "\t%s_private_t parent;\n",
167 inherite->class_name);
168 }
169
170 /* for each private variable on the class output it here. */
171 priv_node = genbind_node_find_type(
172 genbind_node_getnode(interfacee->class),
173 NULL,
174 GENBIND_NODE_TYPE_PRIVATE);
175 while (priv_node != NULL) {
176 outputc(privc, '\t');
177
178 output_ctype(privc, priv_node, true);
179
180 outputf(privc, ";\n");
181
182 priv_node = genbind_node_find_type(
183 genbind_node_getnode(interfacee->class),
184 priv_node,
185 GENBIND_NODE_TYPE_PRIVATE);
186 }
187
188 outputf(privc,
189 "} __attribute__((aligned)) %s_private_t;\n\n",
190 interfacee->class_name);
191 }
192
193 close_header(ir, privc);
194
195 return 0;
196 }
197
198 /**
199 * generate prototype header
200 */
201 static int
output_prototype_header(struct ir * ir)202 output_prototype_header(struct ir *ir)
203 {
204 int idx;
205 struct opctx *protoc;
206
207 /* open header */
208 protoc = open_header(ir, "prototype");
209
210 for (idx = 0; idx < ir->entryc; idx++) {
211 struct ir_entry *entry;
212
213 entry = ir->entries + idx;
214
215 switch (entry->type) {
216 case IR_ENTRY_TYPE_INTERFACE:
217 output_interface_declaration(protoc, entry);
218 break;
219
220 case IR_ENTRY_TYPE_DICTIONARY:
221 output_dictionary_declaration(protoc, entry);
222 break;
223 }
224 }
225
226 close_header(ir, protoc);
227
228 return 0;
229 }
230
231 /**
232 * generate makefile fragment
233 */
234 static int
output_makefile(struct ir * ir)235 output_makefile(struct ir *ir)
236 {
237 int idx;
238 FILE *makef;
239
240 /* open output file */
241 makef = genb_fopen_tmp("Makefile");
242 if (makef == NULL) {
243 return -1;
244 }
245
246 fprintf(makef, "# duk libdom makefile fragment\n\n");
247
248 fprintf(makef, "NSGENBIND_SOURCES:=binding.c ");
249 for (idx = 0; idx < ir->entryc; idx++) {
250 struct ir_entry *interfacee;
251
252 interfacee = ir->entries + idx;
253
254 /* no source for interfaces marked no output */
255 if ((interfacee->type == IR_ENTRY_TYPE_INTERFACE) &&
256 (interfacee->u.interface.noobject)) {
257 continue;
258 }
259
260 fprintf(makef, "%s ", interfacee->filename);
261 }
262 fprintf(makef, "\nNSGENBIND_PREFIX:=%s\n", options->outdirname);
263
264 genb_fclose_tmp(makef, "Makefile");
265
266 return 0;
267 }
268
269
270 /**
271 * generate binding header
272 *
273 * The binding header contains all the duk-libdom specific binding interface
274 * macros and definitions.
275 *
276 * the create prototypes interface is used to cause all the prototype creation
277 * functions for all generated classes to be called in the correct order with
278 * the primary global (if any) generated last.
279 */
280 static int
output_binding_header(struct ir * ir)281 output_binding_header(struct ir *ir)
282 {
283 struct opctx *bindc;
284
285 /* open header */
286 bindc = open_header(ir, "binding");
287
288 outputf(bindc,
289 "#define _MAGIC(S) (\"%s\" S)\n"
290 "#define MAGIC(S) _MAGIC(#S)\n"
291 "#define PROTO_MAGIC MAGIC(PROTOTYPES)\n"
292 "#define PRIVATE_MAGIC MAGIC(PRIVATE)\n"
293 "#define INIT_MAGIC MAGIC(INIT)\n"
294 "#define NODE_MAGIC MAGIC(NODE_MAP)\n"
295 "#define _PROTO_NAME(K) _MAGIC(\"PROTOTYPE_\" K)\n"
296 "#define PROTO_NAME(K) _PROTO_NAME(#K)\n"
297 "#define _PROP_NAME(K,V) _MAGIC(K \"_PROPERTY_\" V)\n"
298 "#define PROP_NAME(K,V) _PROP_NAME(#K,#V)\n"
299 "\n",
300 MAGICPFX);
301
302 /* declaration of constant string values */
303 outputf(bindc,
304 "/* Constant strings */\n"
305 "extern const char *%s_error_fmt_argument;\n"
306 "extern const char *%s_error_fmt_bool_type;\n"
307 "extern const char *%s_error_fmt_number_type;\n"
308 "extern const char *%s_magic_string_private;\n"
309 "extern const char *%s_magic_string_prototypes;\n"
310 "\n",
311 DLPFX, DLPFX, DLPFX, DLPFX, DLPFX);
312
313 outputf(bindc,
314 "duk_bool_t %s_instanceof(duk_context *ctx, duk_idx_t index, const char *klass);\n",
315 DLPFX);
316
317 outputf(bindc,
318 "duk_ret_t %s_create_prototypes(duk_context *ctx);\n", DLPFX);
319
320 close_header(ir, bindc);
321
322 return 0;
323 }
324
325
326 /**
327 * generate binding source
328 *
329 * The binding header contains all the duk-libdom specific binding
330 * implementations.
331 */
332 static int
output_binding_src(struct ir * ir)333 output_binding_src(struct ir *ir)
334 {
335 int idx;
336 struct ir_entry *pglobale = NULL;
337 char *proto_name;
338 struct opctx *bindc;
339 int res;
340
341 /* open the output binding file */
342 res = output_open("binding.c", &bindc);
343 if (res != 0 ) {
344 return -1;
345 }
346
347 /* tool preface */
348 output_tool_preface(bindc);
349
350 /* binding preface */
351 output_method_cdata(bindc,
352 ir->binding_node,
353 GENBIND_METHOD_TYPE_PREFACE);
354
355 /* tool prologue */
356 output_tool_prologue(bindc);
357
358 /* binding prologue */
359 output_method_cdata(bindc,
360 ir->binding_node,
361 GENBIND_METHOD_TYPE_PROLOGUE);
362
363 outputc(bindc, '\n');
364
365 outputf(bindc,
366 "/* Error format strings */\n"
367 "const char *%s_error_fmt_argument =\"%%d argument required, but ony %%d present.\";\n"
368 "const char *%s_error_fmt_bool_type =\"argument %%d (%%s) requires a bool\";\n"
369 "const char *%s_error_fmt_number_type =\"argument %%d (%%s) requires a number\";\n",
370 DLPFX, DLPFX, DLPFX);
371
372 outputf(bindc, "\n");
373
374 outputf(bindc,
375 "/* Magic identifiers */\n"
376 "const char *%s_magic_string_private =\"%sPRIVATE\";\n"
377 "const char *%s_magic_string_prototypes =\"%sPROTOTYPES\";\n",
378 DLPFX, MAGICPFX, DLPFX, MAGICPFX);
379
380 outputf(bindc, "\n");
381
382
383 /* instanceof helper */
384 outputf(bindc,
385 "duk_bool_t\n"
386 "%s_instanceof(duk_context *ctx, duk_idx_t _idx, const char *klass)\n"
387 "{\n"
388 "\tduk_idx_t idx = duk_normalize_index(ctx, _idx);\n"
389 "\t/* ... ??? ... */\n"
390 "\tif (!duk_check_type(ctx, idx, DUK_TYPE_OBJECT)) {\n"
391 "\t\treturn false;\n"
392 "\t}\n"
393 "\t/* ... obj ... */\n"
394 "\tduk_get_global_string(ctx, %s_magic_string_prototypes);\n"
395 "\t/* ... obj ... protos */\n"
396 "\tduk_get_prop_string(ctx, -1, klass);\n"
397 "\t/* ... obj ... protos goalproto */\n"
398 "\tduk_get_prototype(ctx, idx);\n"
399 "\t/* ... obj ... protos goalproto proto? */\n"
400 "\twhile (!duk_is_undefined(ctx, -1)) {\n"
401 "\t\tif (duk_strict_equals(ctx, -1, -2)) {\n"
402 "\t\t\tduk_pop_3(ctx);\n"
403 "\t\t\t/* ... obj ... */\n"
404 "\t\t\treturn true;\n"
405 "\t\t}\n"
406 "\t\tduk_get_prototype(ctx, -1);\n"
407 "\t\t/* ... obj ... protos goalproto proto proto? */\n"
408 "\t\tduk_replace(ctx, -2);\n"
409 "\t\t/* ... obj ... protos goalproto proto? */\n"
410 "\t}\n"
411 "\tduk_pop_3(ctx);\n"
412 "\t/* ... obj ... */\n"
413 "\treturn false;\n"
414 "}\n"
415 "\n",
416 DLPFX, DLPFX);
417
418 /* prototype creation helper function */
419 outputf(bindc,
420 "static duk_ret_t\n"
421 "%s_to_string(duk_context *ctx)\n"
422 "{\n"
423 "\t/* */\n"
424 "\tduk_push_this(ctx);\n"
425 "\t/* this */\n"
426 "\tduk_get_prototype(ctx, -1);\n"
427 "\t/* this proto */\n"
428 "\tduk_get_prop_string(ctx, -1, \"%sklass_name\");\n"
429 "\t/* this proto classname */\n"
430 "\tduk_push_string(ctx, \"[object \");\n"
431 "\t/* this proto classname str */\n"
432 "\tduk_insert(ctx, -2);\n"
433 "\t/* this proto str classname */\n"
434 "\tduk_push_string(ctx, \"]\");\n"
435 "\t/* this proto str classname str */\n"
436 "\tduk_concat(ctx, 3);\n"
437 "\t/* this proto str */\n"
438 "\treturn 1;\n"
439 "}\n"
440 "\n",
441 DLPFX,
442 MAGICPFX);
443
444 outputf(bindc,
445 "static duk_ret_t %s_create_prototype(duk_context *ctx,\n",
446 DLPFX);
447 outputf(bindc,
448 "\t\t\t\t\tduk_safe_call_function genproto,\n"
449 "\t\t\t\t\tconst char *proto_name,\n"
450 "\t\t\t\t\tconst char *klass_name)\n"
451 "{\n"
452 "\tduk_int_t ret;\n"
453 "\tduk_push_object(ctx);\n"
454 "\tif ((ret = duk_safe_call(ctx, genproto, NULL, 1, 1)) != DUK_EXEC_SUCCESS) {\n"
455 "\t\tduk_pop(ctx);\n"
456 "\t\tNSLOG(dukky, WARNING, \"Failed to register prototype for %%s\", proto_name + 2);\n"
457 "\t\treturn ret;\n"
458 "\t}\n"
459 "\t/* top of stack is the ready prototype, inject it */\n"
460 "\tduk_push_string(ctx, klass_name);\n"
461 "\tduk_put_prop_string(ctx, -2, \"%sklass_name\");\n"
462 "\tduk_push_c_function(ctx, %s_to_string, 0);\n"
463 "\tduk_put_prop_string(ctx, -2, \"toString\");\n"
464 "\tduk_push_string(ctx, \"toString\");\n"
465 "\tduk_def_prop(ctx, -2, DUK_DEFPROP_HAVE_ENUMERABLE);\n"
466 "\tduk_put_global_string(ctx, proto_name);\n"
467 "\treturn DUK_ERR_NONE;\n"
468 "}\n\n",
469 MAGICPFX,
470 DLPFX);
471
472 /* generate prototype creation */
473 outputf(bindc,
474 "duk_ret_t %s_create_prototypes(duk_context *ctx)\n", DLPFX);
475
476 outputf(bindc, "{\n");
477
478 for (idx = 0; idx < ir->entryc; idx++) {
479 struct ir_entry *interfacee;
480
481 interfacee = ir->entries + idx;
482
483 if (interfacee->type == IR_ENTRY_TYPE_DICTIONARY) {
484 continue;
485 }
486
487 /* do not generate prototype calls for interfaces marked
488 * no output
489 */
490 if (interfacee->type == IR_ENTRY_TYPE_INTERFACE) {
491 if (interfacee->u.interface.noobject) {
492 continue;
493 }
494
495 if (interfacee->u.interface.primary_global) {
496 pglobale = interfacee;
497 continue;
498 }
499 }
500 proto_name = get_prototype_name(interfacee->name);
501
502 outputf(bindc,
503 "\t%s_create_prototype(ctx, %s_%s___proto, \"%s\", \"%s\");\n",
504 DLPFX,
505 DLPFX,
506 interfacee->class_name,
507 proto_name,
508 interfacee->name);
509
510 free(proto_name);
511 }
512
513 if (pglobale != NULL) {
514 outputf(bindc, "\n\t/* Global object prototype is last */\n");
515
516 proto_name = get_prototype_name(pglobale->name);
517 outputf(bindc,
518 "\t%s_create_prototype(ctx, %s_%s___proto, \"%s\", \"%s\");\n",
519 DLPFX,
520 DLPFX,
521 pglobale->class_name,
522 proto_name,
523 pglobale->name);
524 free(proto_name);
525 }
526
527 outputf(bindc, "\n\treturn DUK_ERR_NONE;\n");
528
529 outputf(bindc, "}\n");
530
531 /* binding postface */
532 output_method_cdata(bindc,
533 ir->binding_node,
534 GENBIND_METHOD_TYPE_POSTFACE);
535
536 output_close(bindc);
537
538 return 0;
539 }
540
output_interfaces_dictionaries(struct ir * ir)541 static int output_interfaces_dictionaries(struct ir *ir)
542 {
543 int res;
544 int idx;
545
546 /* generate interfaces */
547 for (idx = 0; idx < ir->entryc; idx++) {
548 struct ir_entry *irentry;
549
550 irentry = ir->entries + idx;
551
552 switch (irentry->type) {
553 case IR_ENTRY_TYPE_INTERFACE:
554 /* do not generate class for interfaces marked no
555 * output
556 */
557 if (!irentry->u.interface.noobject) {
558 res = output_interface(ir, irentry);
559 if (res != 0) {
560 return res;
561 }
562 }
563 break;
564
565 case IR_ENTRY_TYPE_DICTIONARY:
566 res = output_dictionary(ir, irentry);
567 if (res != 0) {
568 return res;
569 }
570
571 default:
572 break;
573 }
574 }
575
576 return 0;
577 }
578
duk_libdom_output(struct ir * ir)579 int duk_libdom_output(struct ir *ir)
580 {
581 int idx;
582 int res = 0;
583
584 /* process ir entries for output */
585 for (idx = 0; idx < ir->entryc; idx++) {
586 struct ir_entry *irentry;
587
588 irentry = ir->entries + idx;
589
590 /* compute class name */
591 irentry->class_name = gen_idl2c_name(irentry->name);
592
593 if (irentry->class_name != NULL) {
594 int ifacenamelen;
595
596 /* generate source filename */
597 ifacenamelen = strlen(irentry->class_name) + 4;
598 irentry->filename = malloc(ifacenamelen);
599 snprintf(irentry->filename,
600 ifacenamelen,
601 "%s.c",
602 irentry->class_name);
603 }
604 }
605
606 res = output_interfaces_dictionaries(ir);
607 if (res != 0) {
608 goto output_err;
609 }
610
611 /* generate private header */
612 res = output_private_header(ir);
613 if (res != 0) {
614 goto output_err;
615 }
616
617 /* generate prototype header */
618 res = output_prototype_header(ir);
619 if (res != 0) {
620 goto output_err;
621 }
622
623 /* generate binding header */
624 res = output_binding_header(ir);
625 if (res != 0) {
626 goto output_err;
627 }
628
629 /* generate binding source */
630 res = output_binding_src(ir);
631 if (res != 0) {
632 goto output_err;
633 }
634
635 /* generate makefile fragment */
636 res = output_makefile(ir);
637
638 output_err:
639
640 return res;
641 }
642