/* 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 "dukky" #define MAGICPFX "\\xFF\\xFFNETSURF_DUKTAPE_" #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"\ " */" /** * Output code to create a private structure * */ static int output_create_private(FILE* outf, char *class_name) { fprintf(outf, "\t/* create private data and attach to instance */\n"); 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, \"%sPRIVATE\");\n\n", MAGICPFX); return 0; } /** * generate code that gets a private pointer */ static int output_safe_get_private(FILE* outf, char *class_name, int idx) { fprintf(outf, "\t%s_private_t *priv;\n", class_name); fprintf(outf, "\tduk_get_prop_string(ctx, %d, \"%sPRIVATE\");\n", idx, MAGICPFX); fprintf(outf, "\tpriv = duk_get_pointer(ctx, -1);\n"); fprintf(outf, "\tduk_pop(ctx);\n"); fprintf(outf, "\tif (priv == NULL) return 0;\n\n"); return 0; } /** * generate a duktape prototype name */ static char *get_prototype_name(const char *interface_name) { char *proto_name; int pnamelen; int pfxlen; /* duplicate the interface name in upper case */ pfxlen = SLEN(MAGICPFX) + SLEN("PROTOTYPE_"); pnamelen = strlen(interface_name) + 1; proto_name = malloc(pnamelen + pfxlen); snprintf(proto_name, pnamelen + pfxlen, "%sPROTOTYPE_%s", MAGICPFX, interface_name); for (pnamelen-- ; pnamelen >= 0; pnamelen--) { proto_name[pnamelen + pfxlen] = toupper(interface_name[pnamelen]); } return proto_name; } /** * generate code that gets a prototype by name */ static int output_get_prototype(FILE* outf, const char *interface_name) { char *proto_name; proto_name = get_prototype_name(interface_name); fprintf(outf, "\t/* get prototype */\n"); fprintf(outf, "\tduk_get_global_string(ctx, \"%sPROTOTYPES\");\n", MAGICPFX); fprintf(outf, "\tduk_get_prop_string(ctx, -1, \"%s\");\n", proto_name); fprintf(outf, "\tduk_replace(ctx, -2);\n"); free(proto_name); return 0; } /** * generate code that sets a destructor in a prototype */ static int output_set_destructor(FILE* outf, char *class_name, int idx) { fprintf(outf, "\t/* Set the destructor */\n"); fprintf(outf, "\tduk_dup(ctx, %d);\n", idx); fprintf(outf, "\tduk_push_c_function(ctx, %s_%s___destructor, 1);\n", DLPFX, class_name); fprintf(outf, "\tduk_set_finalizer(ctx, -2);\n"); fprintf(outf, "\tduk_pop(ctx);\n\n"); return 0; } /** * generate code that sets a constructor in a prototype */ static int output_set_constructor(FILE* outf, char *class_name, int idx, int argc) { fprintf(outf, "\t/* Set the constructor */\n"); fprintf(outf, "\tduk_dup(ctx, %d);\n", idx); fprintf(outf, "\tduk_push_c_function(ctx, %s_%s___constructor, %d);\n", DLPFX, class_name, 1 + argc); fprintf(outf, "\tduk_put_prop_string(ctx, -2, \"%sINIT\");\n", MAGICPFX); fprintf(outf, "\tduk_pop(ctx);\n\n"); return 0; } static int output_dump_stack(FILE* outf) { if (options->dbglog) { /* dump stack */ fprintf(outf, "\tduk_push_context_dump(ctx);\n"); fprintf(outf, "\tLOG(\"Stack: %%s\", duk_to_string(ctx, -1));\n"); fprintf(outf, "\tduk_pop(ctx);\n"); } return 0; } /** * generate code that adds a method in a prototype */ static int output_add_method(FILE* outf, const char *class_name, const char *method, int argc) { fprintf(outf, "\t/* Add a method */\n"); fprintf(outf, "\tduk_dup(ctx, 0);\n"); fprintf(outf, "\tduk_push_string(ctx, \"%s\");\n", method); fprintf(outf, "\tduk_push_c_function(ctx, %s_%s_%s, ", DLPFX, class_name, method); if (argc == -1) { fprintf(outf, "DUK_VARARGS);\n"); } else { fprintf(outf, "%d);\n", argc); } output_dump_stack(outf); fprintf(outf, "\tduk_def_prop(ctx, -3,\n"); fprintf(outf, "\t DUK_DEFPROP_HAVE_VALUE |\n"); fprintf(outf, "\t DUK_DEFPROP_HAVE_WRITABLE |\n"); fprintf(outf, "\t DUK_DEFPROP_HAVE_ENUMERABLE | DUK_DEFPROP_ENUMERABLE |\n"); fprintf(outf, "\t DUK_DEFPROP_HAVE_CONFIGURABLE);\n"); fprintf(outf, "\tduk_pop(ctx);\n\n"); return 0; } /** * Generate source to populate a read/write property on a prototype */ static int output_populate_rw_property(FILE* outf, const char *class_name, const char *property) { fprintf(outf, "\t/* Add read/write property */\n"); fprintf(outf, "\tduk_dup(ctx, 0);\n"); fprintf(outf, "\tduk_push_string(ctx, \"%s\");\n", property); fprintf(outf, "\tduk_push_c_function(ctx, %s_%s_%s_getter, 0);\n", DLPFX, class_name, property); fprintf(outf, "\tduk_push_c_function(ctx, %s_%s_%s_setter, 1);\n", DLPFX, class_name, property); fprintf(outf, "\tduk_def_prop(ctx, -4, DUK_DEFPROP_HAVE_GETTER |\n"); fprintf(outf, "\t DUK_DEFPROP_HAVE_SETTER |\n"); fprintf(outf, "\t DUK_DEFPROP_HAVE_ENUMERABLE | DUK_DEFPROP_ENUMERABLE |\n"); fprintf(outf, "\t DUK_DEFPROP_HAVE_CONFIGURABLE);\n"); fprintf(outf, "\tduk_pop(ctx);\n\n"); return 0; } /** * Generate source to populate a readonly property on a prototype */ static int output_populate_ro_property(FILE* outf, const char *class_name, const char *property) { fprintf(outf, "\t/* Add readonly property */\n"); fprintf(outf, "\tduk_dup(ctx, 0);\n"); fprintf(outf, "\tduk_push_string(ctx, \"%s\");\n", property); fprintf(outf, "\tduk_push_c_function(ctx, %s_%s_%s_getter, 0);\n", DLPFX, class_name, property); fprintf(outf, "\tduk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_GETTER |\n"); fprintf(outf, "\t DUK_DEFPROP_HAVE_ENUMERABLE | DUK_DEFPROP_ENUMERABLE |\n"); fprintf(outf, "\t DUK_DEFPROP_HAVE_CONFIGURABLE);\n"); fprintf(outf, "\tduk_pop(ctx);\n\n"); return 0; } /** * Generate source to add a constant int value on a prototype */ static int output_prototype_constant_int(FILE *outf, const char *constant_name, int value) { fprintf(outf, "\tduk_dup(ctx, 0);\n"); fprintf(outf, "\tduk_push_string(ctx, \"%s\");\n", constant_name); fprintf(outf, "\tduk_push_int(ctx, %d);\n", value); fprintf(outf, "\tduk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_VALUE |\n"); fprintf(outf, "\t DUK_DEFPROP_HAVE_WRITABLE | DUK_DEFPROP_HAVE_ENUMERABLE |\n"); fprintf(outf, "\t DUK_DEFPROP_ENUMERABLE | DUK_DEFPROP_HAVE_CONFIGURABLE);\n"); fprintf(outf, "\tduk_pop(ctx);\n\n"); return 0; } /** * generate code that gets a private pointer for a method */ static int output_get_method_private(FILE* outf, char *class_name) { fprintf(outf, "\t/* Get private data for method */\n"); fprintf(outf, "\t%s_private_t *priv = NULL;\n", class_name); fprintf(outf, "\tduk_push_this(ctx);\n"); fprintf(outf, "\tduk_get_prop_string(ctx, -1, \"%sPRIVATE\");\n", MAGICPFX); fprintf(outf, "\tpriv = duk_get_pointer(ctx, -1);\n"); fprintf(outf, "\tduk_pop_2(ctx);\n"); fprintf(outf, "\tif (priv == NULL) return 0; /* can do? No can do. */\n\n"); return 0; } /** * generate preface block for nsgenbind */ static int output_tool_preface(FILE* outf) { fprintf(outf, "%s\n", NSGENBIND_PREAMBLE); return 0; } /** * 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; } /** * 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; } static FILE *open_header(struct interface_map *interface_map, const char *name) { FILE *hdrf; char *fname; int fnamel; fnamel = strlen(name) + 4; fname = malloc(fnamel); snprintf(fname, fnamel, "%s.h", name); /* open output file */ hdrf = genb_fopen_tmp(fname); free(fname); if (hdrf == NULL) { return NULL; } /* binding preface */ output_cdata(hdrf, interface_map->binding_node, GENBIND_NODE_TYPE_PREFACE); /* tool preface */ output_tool_preface(hdrf); /* header guard */ fprintf(hdrf, "\n#ifndef %s_%s_h\n", DLPFX, name); fprintf(hdrf, "#define %s_%s_h\n\n", DLPFX, name); return hdrf; } static int close_header(struct interface_map *interface_map, FILE *hdrf, const char *name) { char *fname; int fnamel; fnamel = strlen(name) + 4; fname = malloc(fnamel); snprintf(fname, fnamel, "%s.h", name); fprintf(hdrf, "\n#endif\n"); /* binding postface */ output_cdata(hdrf, interface_map->binding_node, GENBIND_NODE_TYPE_POSTFACE); genb_fclose_tmp(hdrf, fname); free(fname); return 0; } /** * generate the interface constructor */ static int output_interface_constructor(FILE* outf, struct interface_map_entry *interfacee) { int init_argc; /* constructor definition */ fprintf(outf, "static duk_ret_t %s_%s___constructor(duk_context *ctx)\n", DLPFX, interfacee->class_name); fprintf(outf,"{\n"); output_create_private(outf, interfacee->class_name); /* generate call to initialisor */ fprintf(outf, "\t%s_%s___init(ctx, priv", DLPFX, interfacee->class_name); for (init_argc = 1; init_argc <= interfacee->class_init_argc; init_argc++) { fprintf(outf, ", duk_get_pointer(ctx, %d)", init_argc); } fprintf(outf, ");\n"); 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, "static duk_ret_t %s_%s___destructor(duk_context *ctx)\n", DLPFX, interfacee->class_name); fprintf(outf,"{\n"); output_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 = genbind_node_find_method(interfacee->class, NULL, GENBIND_METHOD_TYPE_INIT); inh_init_node = genbind_node_find_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_declaration(FILE* outf, struct interface_map_entry *interfacee, struct genbind_node *init_node) { struct genbind_node *param_node; fprintf(outf, "void %s_%s___init(duk_context *ctx, %s_private_t *priv", DLPFX, interfacee->class_name, interfacee->class_name); /* count the number of arguments on the initializer */ interfacee->class_init_argc = 0; /* 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) { interfacee->class_init_argc++; 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_PARAMETER); } fprintf(outf,")"); return 0; } static int output_interface_init(FILE* outf, struct interface_map_entry *interfacee, struct interface_map_entry *inherite) { struct genbind_node *init_node; int res; /* find the initialisor method on the class (if any) */ init_node = genbind_node_find_method(interfacee->class, NULL, GENBIND_METHOD_TYPE_INIT); /* initialisor definition */ output_interface_init_declaration(outf, interfacee, init_node); 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 = genbind_node_find_method(interfacee->class, NULL, GENBIND_METHOD_TYPE_FINI); /* finaliser definition */ fprintf(outf, "void %s_%s___fini(duk_context *ctx, %s_private_t *priv)\n", DLPFX, 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, "\t%s_%s___fini(ctx, &priv->parent);\n", DLPFX, inherite->class_name); } fprintf(outf, "}\n\n"); return 0; } /** * count the number of arguments to an operation * * \todo this needs to consider multiple lists (overloaded calls?), varadic * parameters. * * \retuen number of arguments or -1 if variable */ static int count_operation_arguments(struct webidl_node *node) { int argc; struct webidl_node *list_node; list_node = webidl_node_find_type( webidl_node_getnode(node), NULL, WEBIDL_NODE_TYPE_LIST); if (list_node == NULL) { /** \todo is having no argument list an error or a warning? */ return 0; } argc = webidl_node_enumerate_type(webidl_node_getnode(list_node), WEBIDL_NODE_TYPE_OPTIONAL_ARGUMENT); if (argc != 0) { return -1; } return webidl_node_enumerate_type(webidl_node_getnode(list_node), WEBIDL_NODE_TYPE_ARGUMENT); } /** * generate a prototype add for a single class method */ static int output_prototype_method(FILE* outf, struct interface_map_entry *interfacee, struct interface_map_operation_entry *operatione) { int op_argc; if (operatione->overloadc > 1) { /* only generate a method for the first occourance of an * overloaded method */ return 0; } if (operatione->name != NULL) { /* normal method of prototype */ op_argc = count_operation_arguments(operatione->node); output_add_method(outf, interfacee->class_name, operatione->name, op_argc); } else { /* special method on prototype */ fprintf(outf, "\t/* Special method on prototype - UNIMPLEMENTED */\n\n"); } return 0; } /** * generate prototype method definitions */ static int output_prototype_methods(FILE *outf, struct interface_map_entry *interfacee) { int opc; int res = 0; for (opc = 0; opc < interfacee->operationc; opc++) { res = output_prototype_method(outf, interfacee, interfacee->operationv + opc); if (res != 0) { break; } } return res; } static int output_prototype_attribute(FILE *outf, struct interface_map_entry *interfacee, struct interface_map_attribute_entry *attributee) { if (attributee->modifier == WEBIDL_TYPE_MODIFIER_READONLY) { return output_populate_ro_property(outf, interfacee->class_name, attributee->name); } return output_populate_rw_property(outf, interfacee->class_name, attributee->name); } /** * generate prototype attribute definitions */ static int output_prototype_attributes(FILE *outf, struct interface_map_entry *interfacee) { int attrc; int res = 0; for (attrc = 0; attrc < interfacee->attributec; attrc++) { res = output_prototype_attribute(outf,interfacee, interfacee->attributev + attrc); if (res != 0) { break; } } return res; } /** * output constants on the prototype * * \todo This implementation assumes the constant is a literal int and should * check the type node base value. */ static int output_prototype_constant(FILE *outf, struct interface_map_constant_entry *constante) { int *value; value = webidl_node_getint( webidl_node_find_type( webidl_node_getnode(constante->node), NULL, WEBIDL_NODE_TYPE_LITERAL_INT)); output_prototype_constant_int(outf, constante->name, *value); return 0; } /** * generate prototype constant definitions */ static int output_prototype_constants(FILE *outf, struct interface_map_entry *interfacee) { int attrc; int res = 0; for (attrc = 0; attrc < interfacee->constantc; attrc++) { res = output_prototype_constant(outf, interfacee->constantv + attrc); if (res != 0) { break; } } return res; } /** * generate the interface prototype creator */ static int output_interface_prototype(FILE* outf, struct interface_map_entry *interfacee, struct interface_map_entry *inherite) { /* prototype definition */ fprintf(outf, "duk_ret_t %s_%s___proto(duk_context *ctx)\n", DLPFX, interfacee->class_name); fprintf(outf,"{\n"); /* generate prototype chaining if interface has a parent */ if (inherite != NULL) { fprintf(outf, "\t/* Set this prototype's prototype (left-parent) */\n"); output_get_prototype(outf, inherite->name); fprintf(outf, "\tduk_set_prototype(ctx, 0);\n\n"); } /* generate setting of methods */ output_prototype_methods(outf, interfacee); /* generate setting of attributes */ output_prototype_attributes(outf, interfacee); /* generate setting of constants */ output_prototype_constants(outf, interfacee); /* generate setting of destructor */ output_set_destructor(outf, interfacee->class_name, 0); /* generate setting of constructor */ output_set_constructor(outf, interfacee->class_name, 0, interfacee->class_init_argc); fprintf(outf,"\treturn 1; /* The prototype object */\n"); fprintf(outf, "}\n\n"); return 0; } /** * generate a single class method for an interface operation */ static int output_interface_operation(FILE* outf, struct interface_map_entry *interfacee, struct interface_map_operation_entry *operatione) { if (operatione->overloadc > 1) { /* only generate a method for the first occourance of an * overloaded method */ return 0; } if (operatione->name != NULL) { /* normal method definition */ fprintf(outf, "static duk_ret_t %s_%s_%s(duk_context *ctx)\n", DLPFX, interfacee->class_name, operatione->name); fprintf(outf,"{\n"); output_get_method_private(outf, interfacee->class_name); output_cdata(outf, operatione->method, GENBIND_NODE_TYPE_CDATA); fprintf(outf,"\treturn 0;\n"); fprintf(outf, "}\n\n"); } else { /* special method definition */ fprintf(outf, "/* Special method definition - UNIMPLEMENTED */\n\n"); } return 0; } /** * generate class methods for each interface operation */ static int output_interface_operations(FILE* outf, struct interface_map_entry *interfacee) { int opc; int res = 0; for (opc = 0; opc < interfacee->operationc; opc++) { res = output_interface_operation(outf, interfacee, interfacee->operationv + opc); if (res != 0) { break; } } return res; } /** * Generate class property getter/setter for a single attribute */ static int output_interface_attribute(FILE* outf, struct interface_map_entry *interfacee, struct interface_map_attribute_entry *atributee) { /* getter definition */ fprintf(outf, "static duk_ret_t %s_%s_%s_getter(duk_context *ctx)\n", DLPFX, interfacee->class_name, atributee->name); fprintf(outf,"{\n"); output_get_method_private(outf, interfacee->class_name); output_cdata(outf, atributee->getter, GENBIND_NODE_TYPE_CDATA); fprintf(outf,"\treturn 0;\n"); fprintf(outf, "}\n\n"); /* readonly attributes have no setter */ if (atributee->modifier == WEBIDL_TYPE_MODIFIER_READONLY) { return 0; } /* setter definition */ fprintf(outf, "static duk_ret_t %s_%s_%s_setter(duk_context *ctx)\n", DLPFX, interfacee->class_name, atributee->name); fprintf(outf,"{\n"); output_get_method_private(outf, interfacee->class_name); output_cdata(outf, atributee->setter, GENBIND_NODE_TYPE_CDATA); fprintf(outf,"\treturn 0;\n"); fprintf(outf, "}\n\n"); return 0; } /** * generate class property getters and setters for each interface attribute */ static int output_interface_attributes(FILE* outf, struct interface_map_entry *interfacee) { int attrc; for (attrc = 0; attrc < interfacee->attributec; attrc++) { output_interface_attribute(outf, interfacee, interfacee->attributev + attrc); } return 0; } /** * generate preface block for nsgenbind */ static int output_tool_prologue(FILE* outf) { char *fpath; fpath = genb_fpath("binding.h"); fprintf(outf, "\n#include \"%s\"\n", fpath); free(fpath); fpath = genb_fpath("private.h"); fprintf(outf, "#include \"%s\"\n", fpath); free(fpath); fpath = genb_fpath("prototype.h"); fprintf(outf, "#include \"%s\"\n", fpath); free(fpath); return 0; } /** * generate a source file to implement an interface using duk and libdom. */ static int output_interface(struct interface_map *interface_map, struct interface_map_entry *interfacee) { FILE *ifacef; int ifacenamelen; struct interface_map_entry *inherite; int res = 0; /* do not generate class for interfaces marked no output */ if (interfacee->noobject) { return 0; } /* compute class 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_tmp(interfacee->filename); if (ifacef == NULL) { return -1; } /* find parent interface entry */ inherite = interface_map_inherit_entry(interface_map, interfacee); /* tool preface */ output_tool_preface(ifacef); /* binding preface */ output_cdata(ifacef, interface_map->binding_node, GENBIND_NODE_TYPE_PREFACE); /* class preface */ output_cdata(ifacef, interfacee->class, GENBIND_NODE_TYPE_PREFACE); /* tool prologue */ output_tool_prologue(ifacef); /* binding prologue */ output_cdata(ifacef, interface_map->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); /* operations */ output_interface_operations(ifacef, interfacee); /* attributes */ output_interface_attributes(ifacef, interfacee); /* prototype */ output_interface_prototype(ifacef, interfacee, inherite); fprintf(ifacef, "\n"); /* class epilogue */ output_cdata(ifacef, interfacee->class, GENBIND_NODE_TYPE_EPILOGUE); /* binding epilogue */ output_cdata(ifacef, interface_map->binding_node, GENBIND_NODE_TYPE_EPILOGUE); /* class postface */ output_cdata(ifacef, interfacee->class, GENBIND_NODE_TYPE_POSTFACE); /* binding postface */ output_cdata(ifacef, interface_map->binding_node, GENBIND_NODE_TYPE_POSTFACE); op_error: genb_fclose_tmp(ifacef, interfacee->filename); return res; } /** * generate private header */ static int output_private_header(struct interface_map *interface_map) { int idx; FILE *privf; /* open header */ privf = open_header(interface_map, "private"); for (idx = 0; idx < interface_map->entryc; idx++) { struct interface_map_entry *interfacee; struct interface_map_entry *inherite; struct genbind_node *priv_node; interfacee = interface_map->entries + idx; /* do not generate private structs for interfaces marked no * output */ if (interfacee->noobject) { continue; } /* find parent interface entry */ inherite = interface_map_inherit_entry(interface_map, interfacee); fprintf(privf, "typedef struct {\n"); if (inherite != NULL) { fprintf(privf, "\t%s_private_t parent;\n", inherite->class_name); } /* for each private variable on the class output it here. */ priv_node = genbind_node_find_type( genbind_node_getnode(interfacee->class), NULL, GENBIND_NODE_TYPE_PRIVATE); while (priv_node != NULL) { fprintf(privf, "\t"); output_cdata(privf, priv_node, GENBIND_NODE_TYPE_TYPE); output_cdata(privf, priv_node, GENBIND_NODE_TYPE_IDENT); fprintf(privf, ";\n"); priv_node = genbind_node_find_type( genbind_node_getnode(interfacee->class), priv_node, GENBIND_NODE_TYPE_PRIVATE); } fprintf(privf, "} %s_private_t;\n\n", interfacee->class_name); } close_header(interface_map, privf, "private"); return 0; } /** * generate prototype header */ static int output_prototype_header(struct interface_map *interface_map) { int idx; FILE *protof; /* open header */ protof = open_header(interface_map, "prototype"); for (idx = 0; idx < interface_map->entryc; idx++) { struct interface_map_entry *interfacee; struct genbind_node *init_node; interfacee = interface_map->entries + idx; /* do not generate prototype declarations for interfaces marked * no output */ if (interfacee->noobject) { continue; } /* prototype declaration */ fprintf(protof, "duk_ret_t %s_%s___proto(duk_context *ctx);\n", DLPFX, interfacee->class_name); /** \todo if the interface has no references (no other * interface inherits from it) there is no reason to export * the initalisor/finaliser as no other class * constructor/destructor should call them. Additionally the * init/fini definition should be made static. */ /* finaliser declaration */ fprintf(protof, "void %s_%s___fini(duk_context *ctx, %s_private_t *priv);\n", DLPFX, interfacee->class_name, interfacee->class_name); /* find the initialisor method on the class (if any) */ init_node = genbind_node_find_method(interfacee->class, NULL, GENBIND_METHOD_TYPE_INIT); /* initialisor definition */ output_interface_init_declaration(protof, interfacee, init_node); fprintf(protof, ";\n\n"); } close_header(interface_map, protof, "prototype"); return 0; } /** * generate makefile fragment */ static int output_makefile(struct interface_map *interface_map) { int idx; FILE *makef; /* open output file */ makef = genb_fopen_tmp("Makefile"); if (makef == NULL) { return -1; } fprintf(makef, "# duk libdom makefile fragment\n\n"); fprintf(makef, "NSGENBIND_SOURCES:=binding.c "); for (idx = 0; idx < interface_map->entryc; idx++) { struct interface_map_entry *interfacee; interfacee = interface_map->entries + idx; /* no source for interfaces marked no output */ if (interfacee->noobject) { continue; } fprintf(makef, "%s ", interfacee->filename); } fprintf(makef, "\nNSGENBIND_PREFIX:=%s\n", options->outdirname); genb_fclose_tmp(makef, "Makefile"); return 0; } /** * generate binding header * * The binding header contains all the duk-libdom specific binding interface * macros and definitions. * * the create prototypes interface is used to cause all the prototype creation * functions for all generated classes to be called in the correct order with * the primary global (if any) generated last. */ static int output_binding_header(struct interface_map *interface_map) { FILE *bindf; /* open header */ bindf = open_header(interface_map, "binding"); fprintf(bindf, "#define _MAGIC(S) (\"%s\" S)\n" "#define MAGIC(S) _MAGIC(#S)\n" "#define PROTO_MAGIC MAGIC(PROTOTYPES)\n" "#define PRIVATE_MAGIC MAGIC(PRIVATE)\n" "#define INIT_MAGIC MAGIC(INIT)\n" "#define NODE_MAGIC MAGIC(NODE_MAP)\n" "#define _PROTO_NAME(K) _MAGIC(\"PROTOTYPE_\" K)\n" "#define PROTO_NAME(K) _PROTO_NAME(#K)\n" "#define _PROP_NAME(K,V) _MAGIC(K \"_PROPERTY_\" V)\n" "#define PROP_NAME(K,V) _PROP_NAME(#K,#V)\n" "\n", MAGICPFX); fprintf(bindf, "duk_bool_t %s_instanceof(duk_context *ctx, const char *klass);\n", DLPFX); fprintf(bindf, "duk_ret_t %s_create_prototypes(duk_context *ctx);\n", DLPFX); close_header(interface_map, bindf, "binding"); return 0; } /** * generate binding source * * The binding header contains all the duk-libdom specific binding * implementations. */ static int output_binding_src(struct interface_map *interface_map) { int idx; FILE *bindf; struct interface_map_entry *pglobale = NULL; char *proto_name; /* open output file */ bindf = genb_fopen_tmp("binding.c"); if (bindf == NULL) { return -1; } /* tool preface */ output_tool_preface(bindf); /* binding preface */ output_cdata(bindf, interface_map->binding_node, GENBIND_NODE_TYPE_PREFACE); output_tool_prologue(bindf); /* binding prologue */ output_cdata(bindf, interface_map->binding_node, GENBIND_NODE_TYPE_PROLOGUE); fprintf(bindf, "\n"); /* instance of helper */ fprintf(bindf, "duk_bool_t\n" "%s_instanceof(duk_context *ctx, const char *klass)\n", DLPFX); fprintf(bindf, "{\n" "\t/* ... ??? */\n" "\tif (!duk_check_type(ctx, -1, DUK_TYPE_OBJECT)) {\n" "\t\treturn false;\n" "\t}\n" "\t/* ... obj */\n" "\tduk_get_global_string(ctx, \"%sPROTOTYPES\");\n" "\t/* ... obj protos */\n" "\tduk_get_prop_string(ctx, -1, klass);\n" "\t/* ... obj protos goalproto */\n" "\tduk_get_prototype(ctx, -3);\n" "\t/* ... obj protos goalproto proto? */\n" "\twhile (!duk_is_undefined(ctx, -1)) {\n" "\t\tif (duk_strict_equals(ctx, -1, -2)) {\n" "\t\t\tduk_pop_3(ctx);\n" "\t\t\treturn true;\n" "\t\t}\n" "\t\tduk_get_prototype(ctx, -1);\n" "\t\t/* ... obj protos goalproto proto proto? */\n" "\t\tduk_replace(ctx, -2);\n" "\t\t/* ... obj protos goalproto proto? */\n" "\t}\n" "\tduk_pop_3(ctx);\n" "\t/* ... obj */\n" "\treturn false;\n" "}\n" "\n", MAGICPFX); /* prototype creation helper function */ fprintf(bindf, "static duk_ret_t\n" "%s_to_string(duk_context *ctx)\n" "{\n" "\t/* */\n" "\tduk_push_this(ctx);\n" "\t/* this */\n" "\tduk_get_prototype(ctx, -1);\n" "\t/* this proto */\n" "\tduk_get_prop_string(ctx, -1, \"%sklass_name\");\n" "\t/* this proto classname */\n" "\tduk_push_string(ctx, \"[object \");\n" "\t/* this proto classname str */\n" "\tduk_insert(ctx, -2);\n" "\t/* this proto str classname */\n" "\tduk_push_string(ctx, \"]\");\n" "\t/* this proto str classname str */\n" "\tduk_concat(ctx, 3);\n" "\t/* this proto str */\n" "\treturn 1;\n" "}\n" "\n", DLPFX, MAGICPFX); fprintf(bindf, "static duk_ret_t %s_create_prototype(duk_context *ctx,\n", DLPFX); fprintf(bindf, "\t\t\t\t\tduk_safe_call_function genproto,\n" "\t\t\t\t\tconst char *proto_name,\n" "\t\t\t\t\tconst char *klass_name)\n" "{\n" "\tduk_int_t ret;\n" "\tduk_push_object(ctx);\n" "\tif ((ret = duk_safe_call(ctx, genproto, 1, 1)) != DUK_EXEC_SUCCESS) {\n" "\t\tduk_pop(ctx);\n" "\t\tLOG(\"Failed to register prototype for %%s\", proto_name + 2);\n" "\t\treturn ret;\n" "\t}\n" "\t/* top of stack is the ready prototype, inject it */\n" "\tduk_push_string(ctx, klass_name);\n" "\tduk_put_prop_string(ctx, -2, \"%sklass_name\");\n" "\tduk_push_c_function(ctx, %s_to_string, 0);\n" "\tduk_put_prop_string(ctx, -2, \"toString\");\n" "\tduk_push_string(ctx, \"toString\");\n" "\tduk_def_prop(ctx, -2, DUK_DEFPROP_HAVE_ENUMERABLE);\n" "\tduk_put_global_string(ctx, proto_name);\n" "\treturn DUK_ERR_NONE;\n" "}\n\n", MAGICPFX, DLPFX); /* generate prototype creation */ fprintf(bindf, "duk_ret_t %s_create_prototypes(duk_context *ctx)\n", DLPFX); fprintf(bindf, "{\n"); for (idx = 0; idx < interface_map->entryc; idx++) { struct interface_map_entry *interfacee; interfacee = interface_map->entries + idx; /* do not generate prototype calls for interfaces marked * no output */ if (interfacee->noobject) { continue; } if (interfacee->primary_global) { pglobale = interfacee; continue; } proto_name = get_prototype_name(interfacee->name); fprintf(bindf, "\t%s_create_prototype(ctx, %s_%s___proto, \"%s\", \"%s\");\n", DLPFX, DLPFX, interfacee->class_name, proto_name, interfacee->name); free(proto_name); } if (pglobale != NULL) { fprintf(bindf, "\n\t/* Global object prototype is last */\n"); proto_name = get_prototype_name(pglobale->name); fprintf(bindf, "\t%s_create_prototype(ctx, %s_%s___proto, \"%s\", \"%s\");\n", DLPFX, DLPFX, pglobale->class_name, proto_name, pglobale->name); free(proto_name); } fprintf(bindf, "\n\treturn DUK_ERR_NONE;\n"); fprintf(bindf, "}\n"); /* binding postface */ output_cdata(bindf, interface_map->binding_node, GENBIND_NODE_TYPE_POSTFACE); genb_fclose_tmp(bindf, "binding.c"); return 0; } int duk_libdom_output(struct interface_map *interface_map) { int idx; int res = 0; /* generate interfaces */ for (idx = 0; idx < interface_map->entryc; idx++) { res = output_interface(interface_map, interface_map->entries + idx); if (res != 0) { goto output_err; } } /* generate private header */ res = output_private_header(interface_map); if (res != 0) { goto output_err; } /* generate prototype header */ res = output_prototype_header(interface_map); if (res != 0) { goto output_err; } /* generate binding header */ res = output_binding_header(interface_map); if (res != 0) { goto output_err; } /* generate binding source */ res = output_binding_src(interface_map); if (res != 0) { goto output_err; } /* generate makefile fragment */ res = output_makefile(interface_map); output_err: return res; }