/* duktape binding generation implementation * * This file is part of nsgenbind. * Licensed under the MIT License, * http://www.opensource.org/licenses/mit-license.php * Copyright 2012 Vincent Sanders */ #include #include #include #include #include #include #include #include #include "options.h" #include "utils.h" #include "nsgenbind-ast.h" #include "webidl-ast.h" #include "interface-map.h" #include "duk-libdom.h" /** prefix for all generated functions */ #define DLPFX "duckky" #define NSGENBIND_PREAMBLE \ "/* Generated by nsgenbind\n" \ " *\n" \ " * nsgenbind is published under the MIT Licence.\n" \ " * nsgenbind is similar to a compiler is a purely transformative tool which\n"\ " * explicitly makes no copyright claim on this generated output\n"\ " */" /** * Generate a C class name for the interface. * * The IDL interface names are camelcase and not similar to libdom naming so it * is necessary to convert them to a libdom compatible class name. This * implementation is simple ASCII capable only and cannot cope with multibyte * codepoints. * * The algorithm is: * - copy characters to output lowering their case * - if the previous character in the input name was uppercase and the current * one is lowercase insert an underscore before the *previous* character. */ static char *gen_class_name(struct interface_map_entry *interfacee) { const char *inc; char *outc; char *name; int wasupper; /* enpty strings are a bad idea */ if ((interfacee->name == NULL) || (interfacee->name[0] == 0)) { return NULL; } /* allocate result buffer as twice the input length as thats the * absolute worst case. */ name = calloc(2, strlen(interfacee->name)); outc = name; inc = interfacee->name; wasupper = 0; /* first character handled separately as inserting a leading underscore * is undesirable */ *outc++ = tolower(*inc++); /* copy input to output */ while (*inc != 0) { /* ugly hack as html IDL is always prefixed uppercase and needs * an underscore there */ if ((inc == (interfacee->name + 4)) && (interfacee->name[0] == 'H') && (interfacee->name[1] == 'T') && (interfacee->name[2] == 'M') && (interfacee->name[3] == 'L') && (islower(inc[1]) == 0)) { *outc++ = '_'; } if ((islower(*inc) != 0) && (wasupper != 0)) { *outc = *(outc - 1); *(outc - 1) = '_'; outc++; wasupper = 0; } else { wasupper = isupper(*inc); } *outc++ = tolower(*inc++); } return name; } /** * find method by type on class */ static struct genbind_node * find_class_method(struct genbind_node *node, struct genbind_node *prev, enum genbind_node_type nodetype) { struct genbind_node *res_node; res_node = genbind_node_find_type( genbind_node_getnode(node), prev, GENBIND_NODE_TYPE_METHOD); while (res_node != NULL) { struct genbind_node *type_node; enum genbind_node_type *type; type_node = genbind_node_find_type( genbind_node_getnode(res_node), NULL, GENBIND_NODE_TYPE_METHOD_TYPE); type = (enum genbind_node_type *)genbind_node_getint(type_node); if (*type == nodetype) { break; } res_node = genbind_node_find_type( genbind_node_getnode(node), res_node, GENBIND_NODE_TYPE_METHOD); } return res_node; } /** * output character data of node of given type. * * used for pre/pro/epi/post sections */ static int output_cdata(FILE* outf, struct genbind_node *node, enum genbind_node_type nodetype) { char *cdata; cdata = genbind_node_gettext( genbind_node_find_type( genbind_node_getnode(node), NULL, nodetype)); if (cdata != NULL) { fprintf(outf, "%s", cdata); } return 0; } /** * Output code to create a private structure * */ static int output_duckky_create_private(FILE* outf, char *class_name) { fprintf(outf, "\t%s_private_t *priv = calloc(1, sizeof(*priv));\n", class_name); fprintf(outf, "\tif (priv == NULL) return 0;\n"); fprintf(outf, "\tduk_push_pointer(ctx, priv);\n"); fprintf(outf, "\tduk_put_prop_string(ctx, 0, PRIVATE_MAGIC)\n\n"); return 0; } /** * generate code that gets a private pointer */ static int output_ducky_safe_get_private(FILE* outf, char *class_name, int idx) { fprintf(outf, "\t%s_private_t *priv = dukky_get_private(ctx, %d);\n", class_name, idx); fprintf(outf, "\tif (priv == NULL) return 0;\n\n"); return 0; } /** * generate the interface constructor */ static int output_interface_constructor(FILE* outf, struct interface_map_entry *interfacee) { /* constructor definition */ fprintf(outf, "duk_ret_t %s_%s___constructor(duk_context *ctx)\n", DLPFX, interfacee->class_name); fprintf(outf,"{\n"); output_duckky_create_private(outf, interfacee->class_name); /* generate call to initialisor */ fprintf(outf, "\t%s_%s___init(ctx, priv, duk_get_pointer(ctx, 1));\n", DLPFX, interfacee->class_name); fprintf(outf, "\tduk_set_top(ctx, 1);\n"); fprintf(outf, "\treturn 1;\n"); fprintf(outf, "}\n\n"); return 0; } /** * generate the interface destructor */ static int output_interface_destructor(FILE* outf, struct interface_map_entry *interfacee) { /* destructor definition */ fprintf(outf, "duk_ret_t %s_%s___destructor(duk_context *ctx)\n", DLPFX, interfacee->class_name); fprintf(outf,"{\n"); output_ducky_safe_get_private(outf, interfacee->class_name, 0); /* generate call to finaliser */ fprintf(outf, "\t%s_%s___fini(ctx, priv);\n", DLPFX, interfacee->class_name); fprintf(outf,"\tfree(priv);\n"); fprintf(outf,"\treturn 0;\n"); fprintf(outf, "}\n\n"); return 0; } /** * generate an initialisor call to parent interface */ static int output_interface_inherit_init(FILE* outf, struct interface_map_entry *interfacee, struct interface_map_entry *inherite) { struct genbind_node *init_node; struct genbind_node *inh_init_node; struct genbind_node *param_node; struct genbind_node *inh_param_node; /* only need to call parent initialisor if there is one */ if (inherite == NULL) { return 0; } /* find the initialisor method on the class (if any) */ init_node = find_class_method(interfacee->class, NULL, GENBIND_METHOD_TYPE_INIT); inh_init_node = find_class_method(inherite->class, NULL, GENBIND_METHOD_TYPE_INIT); fprintf(outf, "\t%s_%s___init(ctx, &priv->parent", DLPFX, inherite->class_name); /* for each parameter in the parent find a matching named * parameter to pass and cast if necessary */ inh_param_node = genbind_node_find_type( genbind_node_getnode(inh_init_node), NULL, GENBIND_NODE_TYPE_PARAMETER); while (inh_param_node != NULL) { char *param_name; param_name = genbind_node_gettext( genbind_node_find_type( genbind_node_getnode(inh_param_node), NULL, GENBIND_NODE_TYPE_IDENT)); param_node = genbind_node_find_type_ident( genbind_node_getnode(init_node), NULL, GENBIND_NODE_TYPE_PARAMETER, param_name); if (param_node == NULL) { fprintf(stderr, "class \"%s\" (interface %s) parent class \"%s\" (interface %s) initialisor requires a parameter \"%s\" with compatible identifier\n", interfacee->class_name, interfacee->name, inherite->class_name, inherite->name, param_name); return -1; } else { char *param_type; char *inh_param_type; fprintf(outf, ", "); /* cast the parameter if required */ param_type = genbind_node_gettext( genbind_node_find_type( genbind_node_getnode(param_node), NULL, GENBIND_NODE_TYPE_TYPE)); inh_param_type = genbind_node_gettext( genbind_node_find_type( genbind_node_getnode(inh_param_node), NULL, GENBIND_NODE_TYPE_TYPE)); if (strcmp(param_type, inh_param_type) != 0) { fprintf(outf, "(%s)", inh_param_type); } /* output the parameter identifier */ output_cdata(outf, param_node, GENBIND_NODE_TYPE_IDENT); } inh_param_node = genbind_node_find_type( genbind_node_getnode(inh_init_node), inh_param_node, GENBIND_NODE_TYPE_METHOD); } fprintf(outf, ");\n"); return 0; } static int output_interface_init(FILE* outf, struct interface_map_entry *interfacee, struct interface_map_entry *inherite) { struct genbind_node *init_node; struct genbind_node *param_node; int res; /* find the initialisor method on the class (if any) */ init_node = find_class_method(interfacee->class, NULL, GENBIND_METHOD_TYPE_INIT); /* initialisor definition */ fprintf(outf, "void %s_%s___init(duk_context *ctx, %s_private_t *priv", DLPFX, interfacee->class_name, interfacee->class_name); /* output the paramters on the method (if any) */ param_node = genbind_node_find_type( genbind_node_getnode(init_node), NULL, GENBIND_NODE_TYPE_PARAMETER); while (param_node != NULL) { fprintf(outf, ", "); output_cdata(outf, param_node, GENBIND_NODE_TYPE_TYPE); output_cdata(outf, param_node, GENBIND_NODE_TYPE_IDENT); param_node = genbind_node_find_type( genbind_node_getnode(init_node), param_node, GENBIND_NODE_TYPE_METHOD); } fprintf(outf,")\n{\n"); /* if this interface inherits ensure we call its initialisor */ res = output_interface_inherit_init(outf, interfacee, inherite); if (res != 0) { return res; } /* generate log statement */ if (options->dbglog) { fprintf(outf, "\tLOG(\"Initialise %%p (priv=%%p)\", duk_get_heapptr(ctx, 0), priv);\n" ); } /* output the initaliser code from the binding */ output_cdata(outf, init_node, GENBIND_NODE_TYPE_CDATA); fprintf(outf, "}\n\n"); return 0; } static int output_interface_fini(FILE* outf, struct interface_map_entry *interfacee, struct interface_map_entry *inherite) { struct genbind_node *fini_node; /* find the finaliser method on the class (if any) */ fini_node = find_class_method(interfacee->class, NULL, GENBIND_METHOD_TYPE_FINI); /* finaliser definition */ fprintf(outf, "void dukky_%s___fini(duk_context *ctx, %s_private_t *priv)\n", interfacee->class_name, interfacee->class_name); fprintf(outf,"{\n"); /* generate log statement */ if (options->dbglog) { fprintf(outf, "\tLOG(\"Finalise %%p\", duk_get_heapptr(ctx, 0));\n" ); } /* output the finialisor code from the binding */ output_cdata(outf, fini_node, GENBIND_NODE_TYPE_CDATA); /* if this interface inherits ensure we call its finaliser */ if (inherite != NULL) { fprintf(outf, "\tdukky_%s___fini(ctx, &priv->parent);\n", inherite->class_name); } fprintf(outf, "}\n\n"); return 0; } /** * generate a source file to implement an interface using duk and libdom. */ static int output_interface(struct genbind_node *genbind, struct webidl_node *webidl, struct interface_map *interface_map, struct interface_map_entry *interfacee) { FILE *ifacef; int ifacenamelen; struct genbind_node *binding_node; struct interface_map_entry *inherite; int res = 0; /* compute clas name */ interfacee->class_name = gen_class_name(interfacee); /* generate source filename */ ifacenamelen = strlen(interfacee->class_name) + 4; interfacee->filename = malloc(ifacenamelen); snprintf(interfacee->filename, ifacenamelen, "%s.c", interfacee->class_name); /* open output file */ ifacef = genb_fopen(interfacee->filename, "w"); if (ifacef == NULL) { return -1; } /* find parent interface entry */ inherite = interface_map_inherit_entry(interface_map, interfacee); binding_node = genbind_node_find_type(genbind, NULL, GENBIND_NODE_TYPE_BINDING); /* binding preface */ output_cdata(ifacef, binding_node, GENBIND_NODE_TYPE_PREFACE); /* nsgenbind preamble */ fprintf(ifacef, "\n%s\n", NSGENBIND_PREAMBLE); /* class preface */ output_cdata(ifacef, interfacee->class, GENBIND_NODE_TYPE_PREFACE); /* binding prologue */ output_cdata(ifacef, binding_node, GENBIND_NODE_TYPE_PROLOGUE); /* class prologue */ output_cdata(ifacef, interfacee->class, GENBIND_NODE_TYPE_PROLOGUE); fprintf(ifacef, "\n"); /* initialisor */ res = output_interface_init(ifacef, interfacee, inherite); if (res != 0) { goto op_error; } /* finaliser */ output_interface_fini(ifacef, interfacee, inherite); /* constructor */ output_interface_constructor(ifacef, interfacee); /* destructor */ output_interface_destructor(ifacef, interfacee); fprintf(ifacef, "\n"); /* class epilogue */ output_cdata(ifacef, interfacee->class, GENBIND_NODE_TYPE_EPILOGUE); /* binding epilogue */ output_cdata(ifacef, binding_node, GENBIND_NODE_TYPE_EPILOGUE); /* class postface */ output_cdata(ifacef, interfacee->class, GENBIND_NODE_TYPE_POSTFACE); /* binding postface */ output_cdata(ifacef, binding_node, GENBIND_NODE_TYPE_POSTFACE); op_error: fclose(ifacef); return res; } int duk_libdom_output(struct genbind_node *genbind, struct webidl_node *webidl, struct interface_map *interface_map) { int idx; int res = 0; /* generate interfaces */ for (idx = 0; idx < interface_map->entryc; idx++) { res = output_interface(genbind, webidl, interface_map, &interface_map->entries[idx]); if (res != 0) { break; } } /* generate header */ /** \todo implement header */ /* generate makefile fragment */ /** \todo implement makefile generation */ return res; }