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