diff options
Diffstat (limited to 'src/duk-libdom.c')
-rw-r--r-- | src/duk-libdom.c | 1586 |
1 files changed, 1586 insertions, 0 deletions
diff --git a/src/duk-libdom.c b/src/duk-libdom.c new file mode 100644 index 0000000..195f4a4 --- /dev/null +++ b/src/duk-libdom.c @@ -0,0 +1,1586 @@ +/* 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 <vince@netsurf-browser.org> + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdbool.h> +#include <string.h> +#include <unistd.h> +#include <getopt.h> +#include <errno.h> +#include <ctype.h> + +#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) +{ + 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, DUK_VARARGS);\n", + DLPFX, class_name, method); + 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); + output_dump_stack(outf); + fprintf(outf, "\tduk_def_prop(ctx, -4, DUK_DEFPROP_HAVE_GETTER |\n"); + fprintf(outf, "\t\tDUK_DEFPROP_HAVE_SETTER |\n"); + fprintf(outf, "\t\tDUK_DEFPROP_HAVE_ENUMERABLE | DUK_DEFPROP_ENUMERABLE |\n"); + fprintf(outf, "\t\tDUK_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); + output_dump_stack(outf); + fprintf(outf, "\tduk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_GETTER |\n"); + fprintf(outf, "\t\tDUK_DEFPROP_HAVE_ENUMERABLE | DUK_DEFPROP_ENUMERABLE |\n"); + fprintf(outf, "\t\tDUK_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) {\n"); + if (options->dbglog) { + fprintf(outf, "\t\tLOG(\"priv failed\");\n"); + } + fprintf(outf, "\t\treturn 0; /* can do? No can do. */\n"); + fprintf(outf, "\t}\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 any cdata including pre/pro/epi/post sections + * + * \param outf The file handle to write output. + * \param node The node to search. + * \param nodetype the type of child node to search for. + * \return The number of nodes written or 0 for none. + */ +static int +output_cdata(FILE* outf, + struct genbind_node *node, + enum genbind_node_type nodetype) +{ + char *cdata; + int res = 0; + + cdata = genbind_node_gettext( + genbind_node_find_type( + genbind_node_getnode(node), + NULL, nodetype)); + if (cdata != NULL) { + fprintf(outf, "%s", cdata); + res = 1; + } + return res; +} + +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; +} + + +/** + * 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) +{ + + if (operatione->name != NULL) { + /* normal method on prototype */ + output_add_method(outf, + interfacee->class_name, + operatione->name); + } 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) +{ + struct genbind_node *proto_node; + + /* find the prototype method on the class */ + proto_node = genbind_node_find_method(interfacee->class, + NULL, + GENBIND_METHOD_TYPE_PROTOTYPE); + + /* prototype definition */ + fprintf(outf, "duk_ret_t %s_%s___proto(duk_context *ctx)\n", + DLPFX, interfacee->class_name); + fprintf(outf,"{\n"); + + /* Output any binding data first */ + if (output_cdata(outf, proto_node, GENBIND_NODE_TYPE_CDATA) != 0) { + 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) +{ + int cdatac; /* cdata blocks output */ + + if (operatione->name == NULL) { + /* special method definition */ + fprintf(outf, + "/* Special method definition - UNIMPLEMENTED */\n\n"); + return 0; + } + + /* 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); + + cdatac = output_cdata(outf, + operatione->method, + GENBIND_NODE_TYPE_CDATA); + + if (cdatac == 0) { + /* no implementation so generate default */ + WARN(WARNING_UNIMPLEMENTED, + "Unimplemented: method %s::%s();", + interfacee->name, operatione->name); + fprintf(outf,"\treturn 0;\n"); + } + + fprintf(outf, "}\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) +{ + int cdatac; + + /* 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); + + cdatac = output_cdata(outf, atributee->getter, GENBIND_NODE_TYPE_CDATA); + + if (cdatac == 0) { + WARN(WARNING_UNIMPLEMENTED, + "Unimplemented: getter %s::%s();", + interfacee->name, atributee->name); + + /* no implementation so generate default */ + 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); + + cdatac = output_cdata(outf, atributee->setter, GENBIND_NODE_TYPE_CDATA); + + if (cdatac == 0) { + WARN(WARNING_UNIMPLEMENTED, + "Unimplemented: setter %s::%s();", + interfacee->name, atributee->name); + + /* no implementation so generate default */ + 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; +} |