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 2012 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 /**
33  * get default value as a string
34  */
35 static int
get_member_default_str(struct ir_entry * dictionarye,struct ir_operation_argument_entry * membere,enum webidl_type member_type,char ** defl_out)36 get_member_default_str(struct ir_entry *dictionarye,
37                        struct ir_operation_argument_entry *membere,
38                        enum webidl_type member_type,
39                        char **defl_out)
40 {
41         struct webidl_node *lit_node;
42         enum webidl_node_type lit_type;
43         int *lit_int;
44         float *lit_flt;
45 
46         lit_node = webidl_node_getnode(
47                 webidl_node_find_type(
48                         webidl_node_getnode(membere->node),
49                         NULL,
50                         WEBIDL_NODE_TYPE_OPTIONAL));
51         if (lit_node == NULL) {
52                 *defl_out = NULL;
53                 return 0;
54         }
55 
56         lit_type = webidl_node_gettype(lit_node);
57 
58         switch (lit_type) {
59 
60         case WEBIDL_NODE_TYPE_LITERAL_BOOL:
61                 if (member_type != WEBIDL_TYPE_BOOL) {
62                         fprintf(stderr,
63                                 "Dictionary %s:%s literal boolean type mismatch\n",
64                                 dictionarye->name,
65                                 membere->name);
66                         return -1;
67                 }
68                 lit_int = webidl_node_getint(lit_node);
69                 if (*lit_int == 0) {
70                         *defl_out = strdup("false");
71                 } else {
72                         *defl_out = strdup("true");
73                 }
74                 break;
75 
76         case WEBIDL_NODE_TYPE_LITERAL_NULL:
77                 *defl_out = strdup("NULL");
78                 break;
79 
80         case WEBIDL_NODE_TYPE_LITERAL_STRING:
81                 *defl_out = strdup(webidl_node_gettext(lit_node));
82                 break;
83 
84         case WEBIDL_NODE_TYPE_LITERAL_INT:
85                 lit_int = webidl_node_getint(lit_node);
86                 *defl_out = malloc(128);
87                 snprintf(*defl_out, 128, "%d", *lit_int);
88                 break;
89 
90         case WEBIDL_NODE_TYPE_LITERAL_FLOAT:
91                 lit_flt = webidl_node_getfloat(lit_node);
92                 *defl_out = malloc(128);
93                 snprintf(*defl_out, 128, "%f", *lit_flt);
94                 break;
95 
96         default:
97                 *defl_out = NULL;
98                 break;
99         }
100 
101         return 0;
102 }
103 
104 
105 /**
106  * generate a single class method for an interface operation
107  */
108 static int
output_member_acessor(struct opctx * outc,struct ir_entry * dictionarye,struct ir_operation_argument_entry * membere)109 output_member_acessor(struct opctx *outc,
110                       struct ir_entry *dictionarye,
111                       struct ir_operation_argument_entry *membere)
112 {
113         struct webidl_node *type_node;
114         enum webidl_type *argument_type;
115         char *defl; /* default for member */
116         int res;
117 
118         type_node = webidl_node_find_type(
119                 webidl_node_getnode(membere->node),
120                 NULL,
121                 WEBIDL_NODE_TYPE_TYPE);
122 
123         if (type_node == NULL) {
124                 fprintf(stderr, "%s:%s has no type\n",
125                         dictionarye->name,
126                         membere->name);
127                 return -1;
128         }
129 
130         argument_type = (enum webidl_type *)webidl_node_getint(
131                 webidl_node_find_type(
132                         webidl_node_getnode(type_node),
133                         NULL,
134                         WEBIDL_NODE_TYPE_TYPE_BASE));
135 
136         if (argument_type == NULL) {
137                 fprintf(stderr,
138                         "%s:%s has no type base\n",
139                         dictionarye->name,
140                         membere->name);
141                 return -1;
142         }
143 
144         /* get default text */
145         res = get_member_default_str(dictionarye, membere, *argument_type, &defl);
146         if (res != 0) {
147                 return res;
148         }
149 
150         switch (*argument_type) {
151 
152         case WEBIDL_TYPE_STRING:
153                 outputf(outc,
154                         "const char *\n"
155                         "%s_%s_get_%s(duk_context *ctx, duk_idx_t idx)\n"
156                         "{\n",
157                         DLPFX, dictionarye->class_name, membere->name);
158 
159                 if (defl == NULL) {
160                         outputf(outc,
161                                 "\tconst char *ret = NULL; /* No default */\n");
162                 } else {
163                         outputf(outc,
164                                 "\tconst char *ret = \"%s\"; /* Default value of %s */\n",
165                                 defl, membere->name);
166                 }
167 
168                 outputf(outc,
169                         "\t/* ... obj@idx ... */\n"
170                         "\tduk_get_prop_string(ctx, idx, \"%s\");\n"
171                         "\t/* ... obj@idx ... value/undefined */\n"
172                         "\tif (!duk_is_undefined(ctx, -1)) {\n"
173                         "\t\t/* Note, this throws a duk_error if it's not a string */\n"
174                         "\t\tret = duk_require_string(ctx, -1);\n"
175                         "\t}\n"
176                         "\tduk_pop(ctx);\n"
177                         "\treturn ret;\n"
178                         "}\n\n",
179                         membere->name);
180 
181                 break;
182 
183         case WEBIDL_TYPE_BOOL:
184                 outputf(outc,
185                         "duk_bool_t\n"
186                         "%s_%s_get_%s(duk_context *ctx, duk_idx_t idx)\n"
187                         "{\n",
188                         DLPFX, dictionarye->class_name, membere->name);
189 
190                 if (defl == NULL) {
191                         outputf(outc,
192                                 "\tduk_bool_t ret = false; /* No default */\n");
193                 } else {
194                 outputf(outc,
195                         "\tduk_bool_t ret = %s; /* Default value of %s */\n",
196                         defl, membere->name);
197                 }
198 
199                 outputf(outc,
200                         "\t/* ... obj@idx ... */\n"
201                         "\tduk_get_prop_string(ctx, idx, \"%s\");\n"
202                         "\t/* ... obj@idx ... value/undefined */\n"
203                         "\tif (!duk_is_undefined(ctx, -1)) {\n"
204                         "\t\t/* Note, this throws a duk_error if it's not a boolean */\n"
205                         "\t\tret = duk_require_boolean(ctx, -1);\n"
206                         "\t}\n"
207                         "\tduk_pop(ctx);\n"
208                         "\treturn ret;\n"
209                         "}\n\n",
210                         membere->name);
211 
212                 break;
213 
214         case WEBIDL_TYPE_SHORT:
215         case WEBIDL_TYPE_LONG:
216         case WEBIDL_TYPE_LONGLONG:
217                 outputf(outc,
218                         "duk_int_t\n"
219                         "%s_%s_get_%s(duk_context *ctx, duk_idx_t idx)\n"
220                         "{\n",
221                         DLPFX, dictionarye->class_name, membere->name);
222 
223                 if (defl == NULL) {
224                         outputf(outc,
225                                 "\tduk_int_t ret = 0; /* No default */\n");
226                 } else {
227                         outputf(outc,
228                                 "\tduk_int_t ret = %s; /* Default value of %s */\n",
229                                 defl, membere->name);
230                 }
231 
232                 outputf(outc,
233                         "\t/* ... obj@idx ... */\n"
234                         "\tduk_get_prop_string(ctx, idx, \"%s\");\n"
235                         "\t/* ... obj@idx ... value/undefined */\n"
236                         "\tif (!duk_is_undefined(ctx, -1)) {\n"
237                         "\t\t/* Note, this throws a duk_error if it's not a int */\n"
238                         "\t\tret = duk_require_int(ctx, -1);\n"
239                         "\t}\n"
240                         "\tduk_pop(ctx);\n"
241                         "\treturn ret;\n"
242                         "}\n\n",
243                         membere->name);
244                 break;
245 
246         case WEBIDL_TYPE_FLOAT:
247         case WEBIDL_TYPE_DOUBLE:
248                 outputf(outc,
249                         "duk_double_t\n"
250                         "%s_%s_get_%s(duk_context *ctx, duk_idx_t idx)\n",
251                         DLPFX, dictionarye->class_name, membere->name);
252 
253                 outputf(outc,
254                         "{\n"
255                         "\tduk_double_t ret = %s; /* Default value of %s */\n"
256                         "\t/* ... obj@idx ... */\n"
257                         "\tduk_get_prop_string(ctx, idx, \"%s\");\n",
258                         defl, membere->name, membere->name);
259 
260                 outputf(outc,
261                         "\t/* ... obj@idx ... value/undefined */\n"
262                         "\tif (!duk_is_undefined(ctx, -1)) {\n"
263                         "\t\t/* Note, this throws a duk_error if it's not a number */\n"
264                         "\t\tret = duk_require_number(ctx, -1);\n"
265                         "\t}\n"
266                         "\tduk_pop(ctx);\n"
267                         "\treturn ret;\n"
268                         "}\n\n");
269                 break;
270 
271         default:
272                 WARN(WARNING_UNIMPLEMENTED,
273                         "Dictionary %s:%s unhandled type (%d)",
274                         dictionarye->name,
275                         membere->name,
276                         *argument_type);
277                 outputf(outc,
278                         "/* Dictionary %s:%s unhandled type (%d) */\n\n",
279                         dictionarye->name,
280                         membere->name,
281                         *argument_type);
282         }
283 
284         if (defl != NULL) {
285                 free(defl);
286         }
287 
288         return 0;
289 }
290 
291 static int
output_member_acessors(struct opctx * outc,struct ir_entry * dictionarye)292 output_member_acessors(struct opctx *outc, struct ir_entry *dictionarye)
293 {
294         int memberc;
295         int res = 0;
296 
297         for (memberc = 0;
298              memberc < dictionarye->u.dictionary.memberc;
299              memberc++) {
300                 res = output_member_acessor(
301                         outc,
302                         dictionarye,
303                         dictionarye->u.dictionary.memberv + memberc);
304                 if (res != 0) {
305                         break;
306                 }
307         }
308 
309         return res;
310 }
311 
312 
313 /* exported function documented in duk-libdom.h */
output_dictionary(struct ir * ir,struct ir_entry * dictionarye)314 int output_dictionary(struct ir *ir, struct ir_entry *dictionarye)
315 {
316         struct opctx *dyop;
317         int res = 0;
318 
319         /* open the output */
320         res = output_open(dictionarye->filename, &dyop);
321         if (res != 0) {
322                 return res;
323         }
324 
325         /* tool preface */
326         output_tool_preface(dyop);
327 
328         /* binding preface */
329         output_method_cdata(dyop,
330                             ir->binding_node,
331                             GENBIND_METHOD_TYPE_PREFACE);
332 
333         /* class preface */
334         output_method_cdata(dyop,
335                             dictionarye->class,
336                             GENBIND_METHOD_TYPE_PREFACE);
337 
338         /* tool prologue */
339         output_tool_prologue(dyop);
340 
341         /* binding prologue */
342         output_method_cdata(dyop,
343                             ir->binding_node,
344                             GENBIND_METHOD_TYPE_PROLOGUE);
345 
346         /* class prologue */
347         output_method_cdata(dyop,
348                             dictionarye->class,
349                             GENBIND_METHOD_TYPE_PROLOGUE);
350 
351         outputf(dyop, "\n");
352 
353         res = output_member_acessors(dyop, dictionarye);
354         if (res != 0) {
355                 goto op_error;
356         }
357 
358         outputf(dyop, "\n");
359 
360         /* class epilogue */
361         output_method_cdata(dyop,
362                             dictionarye->class,
363                             GENBIND_METHOD_TYPE_EPILOGUE);
364 
365         /* binding epilogue */
366         output_method_cdata(dyop,
367                             ir->binding_node,
368                             GENBIND_METHOD_TYPE_EPILOGUE);
369 
370         /* class postface */
371         output_method_cdata(dyop,
372                             dictionarye->class,
373                             GENBIND_METHOD_TYPE_POSTFACE);
374 
375         /* binding postface */
376         output_method_cdata(dyop,
377                             ir->binding_node,
378                             GENBIND_METHOD_TYPE_POSTFACE);
379 
380 op_error:
381         output_close(dyop);
382 
383         return res;
384 }
385 
386 /**
387  * generate a single class method declaration for an interface operation
388  */
389 static int
output_member_declaration(struct opctx * outc,struct ir_entry * dictionarye,struct ir_operation_argument_entry * membere)390 output_member_declaration(struct opctx *outc,
391                           struct ir_entry *dictionarye,
392                           struct ir_operation_argument_entry *membere)
393 {
394         struct webidl_node *type_node;
395         enum webidl_type *argument_type;
396 
397         type_node = webidl_node_find_type(
398                 webidl_node_getnode(membere->node),
399                 NULL,
400                 WEBIDL_NODE_TYPE_TYPE);
401 
402         if (type_node == NULL) {
403                 fprintf(stderr, "%s:%s has no type\n",
404                         dictionarye->name,
405                         membere->name);
406                 return -1;
407         }
408 
409         argument_type = (enum webidl_type *)webidl_node_getint(
410                 webidl_node_find_type(
411                         webidl_node_getnode(type_node),
412                         NULL,
413                         WEBIDL_NODE_TYPE_TYPE_BASE));
414 
415         if (argument_type == NULL) {
416                 fprintf(stderr,
417                         "%s:%s has no type base\n",
418                         dictionarye->name,
419                         membere->name);
420                 return -1;
421         }
422 
423 
424         switch (*argument_type) {
425 
426         case WEBIDL_TYPE_STRING:
427                 outputf(outc,
428                         "const char *%s_%s_get_%s(duk_context *ctx, duk_idx_t idx);\n",
429                         DLPFX, dictionarye->class_name, membere->name);
430                 break;
431 
432         case WEBIDL_TYPE_BOOL:
433                 outputf(outc,
434                         "duk_bool_t %s_%s_get_%s(duk_context *ctx, duk_idx_t idx);\n",
435                         DLPFX, dictionarye->class_name, membere->name);
436                 break;
437 
438         case WEBIDL_TYPE_SHORT:
439         case WEBIDL_TYPE_LONG:
440         case WEBIDL_TYPE_LONGLONG:
441                 outputf(outc,
442                         "duk_int_t %s_%s_get_%s(duk_context *ctx, duk_idx_t idx);\n",
443                         DLPFX, dictionarye->class_name, membere->name);
444                 break;
445 
446         case WEBIDL_TYPE_FLOAT:
447         case WEBIDL_TYPE_DOUBLE:
448                 outputf(outc,
449                         "duk_double_t %s_%s_get_%s(duk_context *ctx, duk_idx_t idx);\n",
450                         DLPFX, dictionarye->class_name, membere->name);
451                 break;
452 
453         default:
454                 outputf(outc,
455                         "/* Dictionary %s:%s unhandled type (%d) */\n",
456                         dictionarye->name,
457                         membere->name,
458                         *argument_type);
459         }
460 
461         return 0;
462 }
463 
464 /* exported function documented in duk-libdom.h */
465 int
output_dictionary_declaration(struct opctx * outc,struct ir_entry * dictionarye)466 output_dictionary_declaration(struct opctx *outc, struct ir_entry *dictionarye)
467 {
468         int memberc;
469         int res = 0;
470 
471         for (memberc = 0;
472              memberc < dictionarye->u.dictionary.memberc;
473              memberc++) {
474                 res = output_member_declaration(
475                         outc,
476                         dictionarye,
477                         dictionarye->u.dictionary.memberv + memberc);
478                 if (res != 0) {
479                         break;
480                 }
481         }
482 
483         return res;
484 }
485