diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile | 4 | ||||
-rw-r--r-- | src/duk-libdom.c | 1586 | ||||
-rw-r--r-- | src/duk-libdom.h | 14 | ||||
-rw-r--r-- | src/interface-map.c | 874 | ||||
-rw-r--r-- | src/interface-map.h | 141 | ||||
-rw-r--r-- | src/jsapi-libdom-const.c | 195 | ||||
-rw-r--r-- | src/jsapi-libdom-function.c (renamed from src/jsapi-libdom-operator.c) | 357 | ||||
-rw-r--r-- | src/jsapi-libdom-init.c | 286 | ||||
-rw-r--r-- | src/jsapi-libdom-jsclass.c | 180 | ||||
-rw-r--r-- | src/jsapi-libdom-new.c | 395 | ||||
-rw-r--r-- | src/jsapi-libdom-property.c | 476 | ||||
-rw-r--r-- | src/jsapi-libdom.c | 597 | ||||
-rw-r--r-- | src/jsapi-libdom.h | 75 | ||||
-rw-r--r-- | src/nsgenbind-ast.c | 860 | ||||
-rw-r--r-- | src/nsgenbind-ast.h | 228 | ||||
-rw-r--r-- | src/nsgenbind-lexer.l | 41 | ||||
-rw-r--r-- | src/nsgenbind-parser.y | 448 | ||||
-rw-r--r-- | src/nsgenbind.c | 332 | ||||
-rw-r--r-- | src/options.h | 12 | ||||
-rw-r--r-- | src/utils.c | 160 | ||||
-rw-r--r-- | src/utils.h | 46 | ||||
-rw-r--r-- | src/webidl-ast.c | 278 | ||||
-rw-r--r-- | src/webidl-ast.h | 60 | ||||
-rw-r--r-- | src/webidl-lexer.l | 9 | ||||
-rw-r--r-- | src/webidl-parser.y | 202 |
25 files changed, 5984 insertions, 1872 deletions
diff --git a/src/Makefile b/src/Makefile index 972beb9..8b034fe 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,7 +1,9 @@ CFLAGS := $(CFLAGS) -I$(BUILDDIR) -Isrc/ -g -DYYENABLE_NLS=0 # Sources in this directory -DIR_SOURCES := nsgenbind.c webidl-ast.c nsgenbind-ast.c jsapi-libdom.c jsapi-libdom-operator.c jsapi-libdom-property.c jsapi-libdom-const.c +DIR_SOURCES := nsgenbind.c utils.c webidl-ast.c nsgenbind-ast.c \ + interface-map.c duk-libdom.c +# jsapi-libdom.c jsapi-libdom-function.c jsapi-libdom-property.c jsapi-libdom-init.c jsapi-libdom-new.c jsapi-libdom-infmap.c jsapi-libdom-jsclass.c SOURCES := $(SOURCES) $(BUILDDIR)/nsgenbind-parser.c $(BUILDDIR)/nsgenbind-lexer.c $(BUILDDIR)/webidl-parser.c $(BUILDDIR)/webidl-lexer.c 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; +} diff --git a/src/duk-libdom.h b/src/duk-libdom.h new file mode 100644 index 0000000..e1dd2c4 --- /dev/null +++ b/src/duk-libdom.h @@ -0,0 +1,14 @@ +/* DukTape binding generation + * + * This file is part of nsgenbind. + * Licensed under the MIT License, + * http://www.opensource.org/licenses/mit-license.php + * Copyright 2015 Vincent Sanders <vince@netsurf-browser.org> + */ + +#ifndef nsgenbind_duk_libdom_h +#define nsgenbind_duk_libdom_h + +int duk_libdom_output(struct interface_map *interface_map); + +#endif diff --git a/src/interface-map.c b/src/interface-map.c new file mode 100644 index 0000000..a5a672a --- /dev/null +++ b/src/interface-map.c @@ -0,0 +1,874 @@ +/* interface mapping + * + * This file is part of nsgenbind. + * Published under the MIT License, + * http://www.opensource.org/licenses/mit-license.php + * Copyright 2015 Vincent Sanders <vince@netsurf-browser.org> + */ + +#include <stdbool.h> +#include <stdio.h> +#include <errno.h> +#include <string.h> +#include <stdlib.h> +#include <ctype.h> + +#include "options.h" +#include "utils.h" +#include "nsgenbind-ast.h" +#include "webidl-ast.h" +#include "interface-map.h" + +/** count the number of nodes of a given type on an interface */ +static int +enumerate_interface_type(struct webidl_node *interface_node, + enum webidl_node_type node_type) +{ + int count = 0; + struct webidl_node *members_node; + + members_node = webidl_node_find_type( + webidl_node_getnode(interface_node), + NULL, + WEBIDL_NODE_TYPE_LIST); + while (members_node != NULL) { + count += webidl_node_enumerate_type( + webidl_node_getnode(members_node), + node_type); + + members_node = webidl_node_find_type( + webidl_node_getnode(interface_node), + members_node, + WEBIDL_NODE_TYPE_LIST); + } + + return count; +} + +/* find index of inherited node if it is one of those listed in the + * binding also maintain refcounts + */ +static void +compute_inherit_refcount(struct interface_map_entry *entries, int entryc) +{ + int idx; + int inf; + + for (idx = 0; idx < entryc; idx++ ) { + entries[idx].inherit_idx = -1; + for (inf = 0; inf < entryc; inf++ ) { + /* cannot inherit from self and name must match */ + if ((inf != idx) && + (entries[idx].inherit_name != NULL ) && + (strcmp(entries[idx].inherit_name, + entries[inf].name) == 0)) { + entries[idx].inherit_idx = inf; + entries[inf].refcount++; + break; + } + } + } +} + +/** Topoligical sort based on the refcount + * + * do not need to consider loops as constructed graph is a acyclic + * + * alloc a second copy of the map + * repeat until all entries copied: + * walk source mapping until first entry with zero refcount + * put the entry at the end of the output map + * reduce refcount on inherit index if !=-1 + * remove entry from source map + */ +static struct interface_map_entry * +interface_topoligical_sort(struct interface_map_entry *srcinf, int infc) +{ + struct interface_map_entry *dstinf; + int idx; + int inf; + + dstinf = calloc(infc, sizeof(struct interface_map_entry)); + if (dstinf == NULL) { + return NULL; + } + + for (idx = infc - 1; idx >= 0; idx--) { + /* walk source map until first valid entry with zero refcount */ + inf = 0; + while ((inf < infc) && + ((srcinf[inf].name == NULL) || + (srcinf[inf].refcount > 0))) { + inf++; + } + if (inf == infc) { + free(dstinf); + return NULL; + } + + /* copy entry to the end of the output map */ + dstinf[idx].name = srcinf[inf].name; + dstinf[idx].node = srcinf[inf].node; + dstinf[idx].inherit_name = srcinf[inf].inherit_name; + dstinf[idx].noobject = srcinf[inf].noobject; + dstinf[idx].primary_global = srcinf[inf].primary_global; + dstinf[idx].operationc = srcinf[inf].operationc; + dstinf[idx].operationv = srcinf[inf].operationv; + dstinf[idx].attributec = srcinf[inf].attributec; + dstinf[idx].attributev = srcinf[inf].attributev; + dstinf[idx].constantc = srcinf[inf].constantc; + dstinf[idx].constantv = srcinf[inf].constantv; + dstinf[idx].class = srcinf[inf].class; + + /* reduce refcount on inherit index if !=-1 */ + if (srcinf[inf].inherit_idx != -1) { + srcinf[srcinf[inf].inherit_idx].refcount--; + } + + /* remove entry from source map */ + srcinf[inf].name = NULL; + } + + return dstinf; +} + +static struct interface_map_operation_entry * +find_operation_name(struct interface_map_operation_entry *operationv, + int operationc, + const char *name) +{ + struct interface_map_operation_entry *cure; + int opc; + + for (opc = 0; opc < operationc; opc++) { + cure = operationv + opc; + + if (cure->name == name) { + /* check pointers for equivalence */ + return cure; + } else { + if ((cure->name != NULL) && + (name != NULL) && + (strcmp(cure->name, name) == 0)) { + return cure; + } + } + } + + return NULL; +} + +static int +argument_map_new(struct webidl_node *arg_list_node, + int *argumentc_out, + struct interface_map_operation_argument_entry **argumentv_out) +{ + int argumentc; + struct webidl_node *argument; + struct interface_map_operation_argument_entry *argumentv; + struct interface_map_operation_argument_entry *cure; + + argumentc = webidl_node_enumerate_type( + webidl_node_getnode(arg_list_node), + WEBIDL_NODE_TYPE_ARGUMENT); + if (argumentc == 0) { + *argumentc_out = 0; + *argumentv_out = NULL; + return 0; + } + + argumentv = calloc(argumentc, sizeof(*argumentv)); + cure = argumentv; + + /* iterate each argument node within the list */ + argument = webidl_node_find_type(webidl_node_getnode(arg_list_node), + NULL, + WEBIDL_NODE_TYPE_ARGUMENT); + + while (argument != NULL) { + + cure->name = webidl_node_gettext( + webidl_node_find_type( + webidl_node_getnode(argument), + NULL, + WEBIDL_NODE_TYPE_IDENT)); + + cure->node = argument; + + + cure->optionalc = webidl_node_enumerate_type( + webidl_node_getnode(argument), + WEBIDL_NODE_TYPE_OPTIONAL); + + cure->elipsisc = webidl_node_enumerate_type( + webidl_node_getnode(argument), + WEBIDL_NODE_TYPE_ELLIPSIS); + + cure++; + + argument = webidl_node_find_type( + webidl_node_getnode(arg_list_node), + argument, + WEBIDL_NODE_TYPE_ARGUMENT); + } + + *argumentc_out = argumentc; + *argumentv_out = argumentv; + + return 0; +} + +/** + * create a new overloaded parameter set on an operation + * + * each operation can be overloaded with multiple function signatures. By + * adding them to the operation as overloads duplicate operation enrtries is + * avoided. + */ +static int +overload_map_new(struct webidl_node *op_node, + int *overloadc_out, + struct interface_map_operation_overload_entry **overloadv_out) +{ + int overloadc = *overloadc_out; + struct interface_map_operation_overload_entry *overloadv; + struct interface_map_operation_overload_entry *cure; + struct webidl_node *arg_list_node; + int argc; + + /* update allocation */ + overloadc++; + overloadv = realloc(*overloadv_out, overloadc * sizeof(*overloadv)); + if (overloadv == NULL) { + return -1; + } + + /* get added entry */ + cure = overloadv + (overloadc - 1); + + /* clear entry */ + cure = memset(cure, 0, sizeof(*cure)); + + /* return type */ + cure->type = webidl_node_find_type(webidl_node_getnode(op_node), + NULL, + WEBIDL_NODE_TYPE_TYPE); + + arg_list_node = webidl_node_find_type(webidl_node_getnode(op_node), + NULL, + WEBIDL_NODE_TYPE_LIST); + if (arg_list_node != NULL) { + argument_map_new(arg_list_node, + &cure->argumentc, + &cure->argumentv); + } + + for (argc = 0; argc < cure->argumentc; argc++) { + struct interface_map_operation_argument_entry *arge; + arge = cure->argumentv + argc; + cure->optionalc += arge->optionalc; + cure->elipsisc += arge->elipsisc; + } + + /* return entry list */ + *overloadc_out = overloadc; + *overloadv_out = overloadv; + + return 0; +} + +static int +operation_map_new(struct webidl_node *interface, + struct genbind_node *class, + int *operationc_out, + struct interface_map_operation_entry **operationv_out) +{ + struct webidl_node *list_node; + struct webidl_node *op_node; /* attribute node */ + struct interface_map_operation_entry *cure; /* current entry */ + struct interface_map_operation_entry *operationv; + int operationc; + + /* enumerate operationss including overloaded members */ + operationc = enumerate_interface_type(interface, + WEBIDL_NODE_TYPE_OPERATION); + + if (operationc < 1) { + /* no operations so empty map */ + *operationc_out = 0; + *operationv_out = NULL; + return 0; + } + + operationv = calloc(operationc, + sizeof(struct interface_map_operation_entry)); + if (operationv == NULL) { + return -1; + }; + cure = operationv; + + /* iterate each list node within the interface */ + list_node = webidl_node_find_type( + webidl_node_getnode(interface), + NULL, + WEBIDL_NODE_TYPE_LIST); + + while (list_node != NULL) { + /* iterate through operations on list */ + op_node = webidl_node_find_type( + webidl_node_getnode(list_node), + NULL, + WEBIDL_NODE_TYPE_OPERATION); + + while (op_node != NULL) { + const char *operation_name; + struct interface_map_operation_entry *finde; + + /* get operation name */ + operation_name = webidl_node_gettext( + webidl_node_find_type( + webidl_node_getnode(op_node), + NULL, + WEBIDL_NODE_TYPE_IDENT)); + /* if this operation is already an entry in the list + * augment that entry else create a new one + */ + finde = find_operation_name(operationv, + operationc, + operation_name); + if (finde == NULL) { + /* operation does not already exist in list */ + + cure->name = operation_name; + + cure->node = op_node; + + cure->method = genbind_node_find_method_ident( + class, + NULL, + GENBIND_METHOD_TYPE_METHOD, + cure->name); + + overload_map_new(op_node, + &cure->overloadc, + &cure->overloadv); + + cure++; /* advance to next entry */ + } else { + overload_map_new(op_node, + &finde->overloadc, + &finde->overloadv); + /* Overloaded entry does not advance the + * current entry but does reduce list + * length. Do not bother shortening allocation. + */ + operationc--; + } + + /* move to next operation */ + op_node = webidl_node_find_type( + webidl_node_getnode(list_node), + op_node, + WEBIDL_NODE_TYPE_OPERATION); + } + + list_node = webidl_node_find_type( + webidl_node_getnode(interface), + list_node, + WEBIDL_NODE_TYPE_LIST); + } + + *operationc_out = operationc; + *operationv_out = operationv; /* resulting operations map */ + + return 0; +} + +static int +attribute_map_new(struct webidl_node *interface, + struct genbind_node *class, + int *attributec_out, + struct interface_map_attribute_entry **attributev_out) +{ + struct webidl_node *list_node; + struct webidl_node *at_node; /* attribute node */ + struct interface_map_attribute_entry *cure; /* current entry */ + struct interface_map_attribute_entry *attributev; + int attributec; + + /* enumerate attributes */ + attributec = enumerate_interface_type(interface, + WEBIDL_NODE_TYPE_ATTRIBUTE); + *attributec_out = attributec; + + if (attributec < 1) { + *attributev_out = NULL; /* no attributes so empty map */ + return 0; + } + + attributev = calloc(attributec, + sizeof(struct interface_map_attribute_entry)); + if (attributev == NULL) { + return -1; + }; + cure = attributev; + + /* iterate each list node within the interface */ + list_node = webidl_node_find_type( + webidl_node_getnode(interface), + NULL, + WEBIDL_NODE_TYPE_LIST); + + while (list_node != NULL) { + /* iterate through attributes on list */ + at_node = webidl_node_find_type( + webidl_node_getnode(list_node), + NULL, + WEBIDL_NODE_TYPE_ATTRIBUTE); + + while (at_node != NULL) { + enum webidl_type_modifier *modifier; + + cure->node = at_node; + + cure->name = webidl_node_gettext( + webidl_node_find_type( + webidl_node_getnode(at_node), + NULL, + WEBIDL_NODE_TYPE_IDENT)); + + cure->getter = genbind_node_find_method_ident( + class, + NULL, + GENBIND_METHOD_TYPE_GETTER, + cure->name); + + /* check fo readonly attributes */ + modifier = (enum webidl_type_modifier *)webidl_node_getint( + webidl_node_find_type( + webidl_node_getnode(at_node), + NULL, + WEBIDL_NODE_TYPE_MODIFIER)); + if ((modifier != NULL) && + (*modifier == WEBIDL_TYPE_MODIFIER_READONLY)) { + cure->modifier = WEBIDL_TYPE_MODIFIER_READONLY; + } else { + cure->modifier = WEBIDL_TYPE_MODIFIER_NONE; + cure->setter = genbind_node_find_method_ident( + class, + NULL, + GENBIND_METHOD_TYPE_SETTER, + cure->name); + } + + cure++; + + /* move to next attribute */ + at_node = webidl_node_find_type( + webidl_node_getnode(list_node), + at_node, + WEBIDL_NODE_TYPE_ATTRIBUTE); + } + + list_node = webidl_node_find_type( + webidl_node_getnode(interface), + list_node, + WEBIDL_NODE_TYPE_LIST); + } + + *attributev_out = attributev; /* resulting attributes map */ + + return 0; +} + +static int +constant_map_new(struct webidl_node *interface, + int *constantc_out, + struct interface_map_constant_entry **constantv_out) +{ + struct webidl_node *list_node; + struct webidl_node *constant_node; /* constant node */ + struct interface_map_constant_entry *cure; /* current entry */ + struct interface_map_constant_entry *constantv; + int constantc; + + /* enumerate constants */ + constantc = enumerate_interface_type(interface, + WEBIDL_NODE_TYPE_CONST); + + if (constantc < 1) { + *constantc_out = 0; + *constantv_out = NULL; /* no constants so empty map */ + return 0; + } + + *constantc_out = constantc; + + constantv = calloc(constantc, + sizeof(struct interface_map_constant_entry)); + if (constantv == NULL) { + return -1; + }; + cure = constantv; + + /* iterate each list node within the interface */ + list_node = webidl_node_find_type( + webidl_node_getnode(interface), + NULL, + WEBIDL_NODE_TYPE_LIST); + + while (list_node != NULL) { + /* iterate through constants on list */ + constant_node = webidl_node_find_type( + webidl_node_getnode(list_node), + NULL, + WEBIDL_NODE_TYPE_CONST); + + while (constant_node != NULL) { + cure->node = constant_node; + + cure->name = webidl_node_gettext( + webidl_node_find_type( + webidl_node_getnode(constant_node), + NULL, + WEBIDL_NODE_TYPE_IDENT)); + + cure++; + + /* move to next constant */ + constant_node = webidl_node_find_type( + webidl_node_getnode(list_node), + constant_node, + WEBIDL_NODE_TYPE_CONST); + } + + list_node = webidl_node_find_type( + webidl_node_getnode(interface), + list_node, + WEBIDL_NODE_TYPE_LIST); + } + + *constantv_out = constantv; /* resulting constants map */ + + return 0; +} + +int interface_map_new(struct genbind_node *genbind, + struct webidl_node *webidl, + struct interface_map **map_out) +{ + int interfacec; + struct interface_map_entry *entries; + struct interface_map_entry *sorted_entries; + struct interface_map_entry *ecur; + struct webidl_node *node; + struct interface_map *map; + + interfacec = webidl_node_enumerate_type(webidl, + WEBIDL_NODE_TYPE_INTERFACE); + + if (options->verbose) { + printf("Mapping %d interfaces\n", interfacec); + } + + entries = calloc(interfacec, sizeof(struct interface_map_entry)); + if (entries == NULL) { + return -1; + } + + /* for each interface populate an entry in the map */ + ecur = entries; + node = webidl_node_find_type(webidl, NULL, WEBIDL_NODE_TYPE_INTERFACE); + while (node != NULL) { + + /* fill map entry */ + ecur->node = node; + + /* name of interface */ + ecur->name = webidl_node_gettext( + webidl_node_find_type( + webidl_node_getnode(node), + NULL, + WEBIDL_NODE_TYPE_IDENT)); + + /* name of the inherited interface (if any) */ + ecur->inherit_name = webidl_node_gettext( + webidl_node_find_type( + webidl_node_getnode(node), + NULL, + WEBIDL_NODE_TYPE_INTERFACE_INHERITANCE)); + + /* is the interface marked as not generating an object */ + if (webidl_node_find_type_ident( + webidl_node_getnode(node), + WEBIDL_NODE_TYPE_EXTENDED_ATTRIBUTE, + "NoInterfaceObject") != NULL) { + /** \todo we should ensure inherit is unset as this + * cannot form part of an inheritance chain if it is + * not generating an output class + */ + ecur->noobject = true; + } + + /* is the interface marked as the primary global */ + if (webidl_node_find_type_ident( + webidl_node_getnode(node), + WEBIDL_NODE_TYPE_EXTENDED_ATTRIBUTE, + "PrimaryGlobal") != NULL) { + /** \todo we should ensure nothing inherits *from* this + * class or all hell will break loose having two + * primary globals. + */ + ecur->primary_global = true; + } + + /* matching class from binding */ + ecur->class = genbind_node_find_type_ident(genbind, + NULL, GENBIND_NODE_TYPE_CLASS, ecur->name); + + /* enumerate and map the interface operations */ + operation_map_new(node, + ecur->class, + &ecur->operationc, + &ecur->operationv); + + /* enumerate and map the interface attributes */ + attribute_map_new(node, + ecur->class, + &ecur->attributec, + &ecur->attributev); + + /* enumerate and map the interface constants */ + constant_map_new(node, + &ecur->constantc, + &ecur->constantv); + + /* move to next interface */ + node = webidl_node_find_type(webidl, node, + WEBIDL_NODE_TYPE_INTERFACE); + ecur++; + } + + /* compute inheritance and refcounts on map */ + compute_inherit_refcount(entries, interfacec); + + /* sort interfaces to ensure correct ordering */ + sorted_entries = interface_topoligical_sort(entries, interfacec); + free(entries); + if (sorted_entries == NULL) { + return -1; + } + + /* compute inheritance and refcounts on sorted map */ + compute_inherit_refcount(sorted_entries, interfacec); + + map = malloc(sizeof(struct interface_map)); + map->entryc = interfacec; + map->entries = sorted_entries; + map->webidl = webidl; + map->binding_node = genbind_node_find_type(genbind, NULL, + GENBIND_NODE_TYPE_BINDING); + + *map_out = map; + + return 0; +} + +int interface_map_dump(struct interface_map *index) +{ + FILE *dumpf; + int eidx; + struct interface_map_entry *ecur; + + /* only dump AST to file if required */ + if (!options->debug) { + return 0; + } + + dumpf = genb_fopen("interface-map", "w"); + if (dumpf == NULL) { + return 2; + } + + ecur = index->entries; + for (eidx = 0; eidx < index->entryc; eidx++) { + fprintf(dumpf, "%d %s\n", eidx, ecur->name); + if (ecur->inherit_name != NULL) { + fprintf(dumpf, "\tinherit:%s\n", ecur->inherit_name); + } + if (ecur->class != NULL) { + fprintf(dumpf, "\tclass:%p\n", ecur->class); + } + + if (ecur->operationc > 0) { + int opc; + + fprintf(dumpf, "\t%d operations\n", + ecur->operationc); + + for (opc = 0; opc < ecur->operationc; opc++) { + int ovlc; + struct interface_map_operation_entry *ope; + + ope = ecur->operationv + opc; + + fprintf(dumpf, + "\t\t%s\n", + ope->name); + fprintf(dumpf, + "\t\t\tmethod:%p\n", + ope->method); + for(ovlc = 0; ovlc < ope->overloadc;ovlc++) { + int argc; + struct interface_map_operation_overload_entry *ovle; + ovle = ope->overloadv + ovlc; + + fprintf(dumpf, + "\t\t\toverload:%d\n", ovlc); + + fprintf(dumpf, + "\t\t\t\treturn type:%p\n", + ovle->type); + + fprintf(dumpf, + "\t\t\t\targuments:%d\n", + ovle->argumentc); + + fprintf(dumpf, + "\t\t\t\toptionals:%d\n", + ovle->optionalc); + + fprintf(dumpf, + "\t\t\t\telipsis:%d\n", + ovle->elipsisc); + + for (argc = 0; argc < ovle->argumentc; argc++) { + struct interface_map_operation_argument_entry *arge; + arge = ovle->argumentv + argc; + + fprintf(dumpf, + "\t\t\t\t\t%s\n", + arge->name); + + if (arge->optionalc != 0) { + fprintf(dumpf, + "\t\t\t\t\t\toptional:%d\n", + arge->optionalc); + } + + if (arge->elipsisc != 0) { + fprintf(dumpf, + "\t\t\t\t\t\telipsis:%d\n", + arge->elipsisc); + } + + } + } + } + } + + if (ecur->attributec > 0) { + int attrc = ecur->attributec; + struct interface_map_attribute_entry *attre; + + fprintf(dumpf, "\t%d attributes\n", attrc); + + attre = ecur->attributev; + while (attre != NULL) { + fprintf(dumpf, "\t\t%s %p", + attre->name, + attre->getter); + if (attre->modifier == WEBIDL_TYPE_MODIFIER_NONE) { + fprintf(dumpf, " %p\n", attre->setter); + } else { + fprintf(dumpf, "\n"); + } + attre++; + attrc--; + if (attrc == 0) { + break; + } + } + } + if (ecur->constantc > 0) { + int idx; + + fprintf(dumpf, "\t%d constants\n", + ecur->constantc); + + for (idx = 0; idx < ecur->constantc; idx++) { + struct interface_map_constant_entry *cone; + cone = ecur->constantv + idx; + fprintf(dumpf, "\t\t%s\n", + cone->name); + } + } + ecur++; + } + + fclose(dumpf); + + return 0; +} + +int interface_map_dumpdot(struct interface_map *index) +{ + FILE *dumpf; + int eidx; + struct interface_map_entry *ecur; + + /* only dump AST to file if required */ + if (!options->debug) { + return 0; + } + + dumpf = genb_fopen("interface.dot", "w"); + if (dumpf == NULL) { + return 2; + } + + fprintf(dumpf, "digraph interfaces {\n"); + + fprintf(dumpf, "node [shape=box]\n"); + + ecur = index->entries; + for (eidx = 0; eidx < index->entryc; eidx++) { + fprintf(dumpf, "%04d [label=\"%s\"", eidx, ecur->name); + if (ecur->noobject == true) { + /* noobject interfaces in red */ + fprintf(dumpf, "fontcolor=\"red\""); + } else if (ecur->class != NULL) { + /* interfaces bound to a class are shown in blue */ + fprintf(dumpf, "fontcolor=\"blue\""); + } + fprintf(dumpf, "];\n"); + ecur++; + } + + ecur = index->entries; + for (eidx = 0; eidx < index->entryc; eidx++) { + if (index->entries[eidx].inherit_idx != -1) { + fprintf(dumpf, "%04d -> %04d;\n", + eidx, index->entries[eidx].inherit_idx); + } + } + + fprintf(dumpf, "}\n"); + + fclose(dumpf); + + return 0; +} + +struct interface_map_entry * +interface_map_inherit_entry(struct interface_map *map, + struct interface_map_entry *entry) +{ + struct interface_map_entry *res = NULL; + + if ((entry != NULL) && + (entry->inherit_idx != -1)) { + res = &map->entries[entry->inherit_idx]; + } + return res; +} diff --git a/src/interface-map.h b/src/interface-map.h new file mode 100644 index 0000000..5f1926e --- /dev/null +++ b/src/interface-map.h @@ -0,0 +1,141 @@ +/* Interface mapping + * + * 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> + */ + +#ifndef nsgenbind_interface_map_h +#define nsgenbind_interface_map_h + +struct genbind_node; +struct webidl_node; + +/** + *map entry for each argument of an overload on an operation + */ +struct interface_map_operation_argument_entry { + const char *name; + + int optionalc; /**< Number of parameters that are optional */ + int elipsisc; /**< Number of elipsis parameters */ + + struct webidl_node *node; +}; + +/** map entry for each overload of an operation */ +struct interface_map_operation_overload_entry { + struct webidl_node *type; /**< The return type of this overload */ + + int optionalc; /**< Number of parameters that are optional */ + int elipsisc; /**< Number of elipsis parameters */ + + int argumentc; /**< the number of parameters */ + struct interface_map_operation_argument_entry *argumentv; +}; + +/** map entry for operations on an interface */ +struct interface_map_operation_entry { + const char *name; /** operation name */ + struct webidl_node *node; /**< AST operation node */ + struct genbind_node *method; /**< method from binding */ + + int overloadc; /**< Number of overloads of this operation */ + struct interface_map_operation_overload_entry *overloadv; +}; + +/** map entry for attributes on an interface */ +struct interface_map_attribute_entry { + const char *name; /** attribute name */ + struct webidl_node *node; /**< AST attribute node */ + enum webidl_type_modifier modifier; + struct genbind_node *getter; /**< getter from binding */ + struct genbind_node *setter; /**< getter from binding */ +}; + +/** map entry for constants on an interface */ +struct interface_map_constant_entry { + const char *name; /** attribute name */ + struct webidl_node *node; /**< AST constant node */ +}; + +/** map entry for an interface */ +struct interface_map_entry { + const char *name; /** interface name */ + struct webidl_node *node; /**< AST interface node */ + const char *inherit_name; /**< Name of interface inhertited from */ + int inherit_idx; /**< index into map of inherited interface or -1 for + * not in map + */ + int refcount; /**< number of interfacess in map that refer to this + * interface + */ + bool noobject; /**< flag indicating if no interface object should eb + * generated. This allows for interfaces which do not + * generate code. For implements (mixin) interfaces + */ + bool primary_global; /**< flag indicating the interface is the primary + * global javascript object. + */ + + int operationc; /**< number of operations on interface */ + struct interface_map_operation_entry *operationv; + + int attributec; /**< number of attributes on interface */ + struct interface_map_attribute_entry *attributev; + + int constantc; /**< number of constants on interface */ + struct interface_map_constant_entry *constantv; + + + struct genbind_node *class; /**< class from binding (if any) */ + + /* The variables are created and used by the output generation but + * rtaher than have another allocation and pointer the data they are + * just inline here. + */ + + char *filename; /**< filename used for output */ + + char *class_name; /**< the interface name converted to output + * appropriate value. e.g. generators targetting c + * might lowercase the name or add underscores + * instead of caps + */ + int class_init_argc; /**< The number of parameters on the class + * initializer. + */ +}; + +/** WebIDL interface map */ +struct interface_map { + int entryc; /**< count of interfaces */ + struct interface_map_entry *entries; /**< interface entries */ + + /** The AST node of the binding information */ + struct genbind_node *binding_node; + + /** Root AST node of the webIDL */ + struct webidl_node *webidl; +}; + +/** + * Create a new interface map + */ +int interface_map_new(struct genbind_node *genbind, + struct webidl_node *webidl, + struct interface_map **map_out); + +int interface_map_dump(struct interface_map *map); + +int interface_map_dumpdot(struct interface_map *map); + +/** + * interface map parent entry + * + * \return inherit entry or NULL if there is not one + */ +struct interface_map_entry *interface_map_inherit_entry(struct interface_map *map, struct interface_map_entry *entry); + +#endif diff --git a/src/jsapi-libdom-const.c b/src/jsapi-libdom-const.c deleted file mode 100644 index ac728c7..0000000 --- a/src/jsapi-libdom-const.c +++ /dev/null @@ -1,195 +0,0 @@ -/* const property generation - * - * 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 <stdbool.h> -#include <stdio.h> -#include <errno.h> -#include <string.h> -#include <stdlib.h> - -#include "options.h" -#include "nsgenbind-ast.h" -#include "webidl-ast.h" -#include "jsapi-libdom.h" - -static int output_cast_literal(struct binding *binding, - struct webidl_node *node) -{ - struct webidl_node *type_node = NULL; - struct webidl_node *literal_node = NULL; - struct webidl_node *type_base = NULL; - enum webidl_type webidl_arg_type; - - type_node = webidl_node_find_type(webidl_node_getnode(node), - NULL, - WEBIDL_NODE_TYPE_TYPE); - - type_base = webidl_node_find_type(webidl_node_getnode(type_node), - NULL, - WEBIDL_NODE_TYPE_TYPE_BASE); - - webidl_arg_type = webidl_node_getint(type_base); - - switch (webidl_arg_type) { - - case WEBIDL_TYPE_BOOL: - /* JSBool */ - literal_node = webidl_node_find_type(webidl_node_getnode(node), - NULL, - WEBIDL_NODE_TYPE_LITERAL_BOOL); - fprintf(binding->outfile, "BOOLEAN_TO_JSVAL(JS_FALSE)"); - break; - - case WEBIDL_TYPE_FLOAT: - case WEBIDL_TYPE_DOUBLE: - /* double */ - literal_node = webidl_node_find_type(webidl_node_getnode(node), - NULL, - WEBIDL_NODE_TYPE_LITERAL_FLOAT); - fprintf(binding->outfile, "DOUBLE_TO_JSVAL(0.0)"); - break; - - case WEBIDL_TYPE_LONG: - /* int32_t */ - literal_node = webidl_node_find_type(webidl_node_getnode(node), - NULL, - WEBIDL_NODE_TYPE_LITERAL_INT); - fprintf(binding->outfile, - "INT_TO_JSVAL(%d)", - webidl_node_getint(literal_node)); - break; - - case WEBIDL_TYPE_SHORT: - /* int16_t */ - literal_node = webidl_node_find_type(webidl_node_getnode(node), - NULL, - WEBIDL_NODE_TYPE_LITERAL_INT); - fprintf(binding->outfile, - "INT_TO_JSVAL(%d)", - webidl_node_getint(literal_node)); - break; - - - case WEBIDL_TYPE_STRING: - case WEBIDL_TYPE_BYTE: - case WEBIDL_TYPE_OCTET: - case WEBIDL_TYPE_LONGLONG: - case WEBIDL_TYPE_SEQUENCE: - case WEBIDL_TYPE_OBJECT: - case WEBIDL_TYPE_DATE: - case WEBIDL_TYPE_VOID: - case WEBIDL_TYPE_USER: - default: - WARN(WARNING_UNIMPLEMENTED, "types not allowed as literal"); - break; /* @todo these types are not allowed here */ - } - - return 0; -} - -static int webidl_const_define_cb(struct webidl_node *node, void *ctx) -{ - struct binding *binding = ctx; - struct webidl_node *ident_node; - - ident_node = webidl_node_find_type(webidl_node_getnode(node), - NULL, - WEBIDL_NODE_TYPE_IDENT); - if (ident_node == NULL) { - /* Broken AST - must have ident */ - return 1; - } - - fprintf(binding->outfile, - "\tJS_DefineProperty(cx,\n" - "\t\tprototype,\n" - "\t\t\"%s\",\n" - "\t\t", - webidl_node_gettext(ident_node)); - - output_cast_literal(binding, node); - - fprintf(binding->outfile, - ",\n" - "\t\tJS_PropertyStub,\n" - "\t\tJS_StrictPropertyStub,\n" - "\t\tJSPROP_READONLY | JSPROP_ENUMERATE | JSPROP_PERMANENT);\n\n"); - - return 0; - -} - - -/* callback to emit implements property spec */ -static int webidl_const_spec_implements_cb(struct webidl_node *node, void *ctx) -{ - struct binding *binding = ctx; - - return output_const_defines(binding, webidl_node_gettext(node)); -} - -int -output_const_defines(struct binding *binding, const char *interface) -{ - struct webidl_node *interface_node; - struct webidl_node *members_node; - struct webidl_node *inherit_node; - int res = 0; - - /* find interface in webidl with correct ident attached */ - interface_node = webidl_node_find_type_ident(binding->wi_ast, - WEBIDL_NODE_TYPE_INTERFACE, - interface); - - if (interface_node == NULL) { - fprintf(stderr, - "Unable to find interface %s in loaded WebIDL\n", - interface); - return -1; - } - - /* generate property entries for each list (partial interfaces) */ - members_node = webidl_node_find_type(webidl_node_getnode(interface_node), - NULL, - WEBIDL_NODE_TYPE_LIST); - - while (members_node != NULL) { - fprintf(binding->outfile,"\t/**** %s ****/\n", interface); - - /* for each const emit a property define */ - webidl_node_for_each_type(webidl_node_getnode(members_node), - WEBIDL_NODE_TYPE_CONST, - webidl_const_define_cb, - binding); - - - members_node = webidl_node_find_type(webidl_node_getnode(interface_node), - members_node, - WEBIDL_NODE_TYPE_LIST); - } - - /* check for inherited nodes and insert them too */ - inherit_node = webidl_node_find(webidl_node_getnode(interface_node), - NULL, - webidl_cmp_node_type, - (void *)WEBIDL_NODE_TYPE_INTERFACE_INHERITANCE); - - if (inherit_node != NULL) { - res = output_const_defines(binding, - webidl_node_gettext(inherit_node)); - } - - if (res == 0) { - res = webidl_node_for_each_type(webidl_node_getnode(interface_node), - WEBIDL_NODE_TYPE_INTERFACE_IMPLEMENTS, - webidl_const_spec_implements_cb, - binding); - } - - return res; -} diff --git a/src/jsapi-libdom-operator.c b/src/jsapi-libdom-function.c index 1d16afe..4f62bcd 100644 --- a/src/jsapi-libdom-operator.c +++ b/src/jsapi-libdom-function.c @@ -1,4 +1,4 @@ -/* function/operator generation +/* jsapi function generation for webidl bodies * * This file is part of nsgenbind. * Licensed under the MIT License, @@ -17,113 +17,85 @@ #include "webidl-ast.h" #include "jsapi-libdom.h" -static int webidl_func_spec_cb(struct webidl_node *node, void *ctx) +static int webidl_operator_spec(struct binding *binding, + struct binding_interface *inf, + struct webidl_node *node) { - struct binding *binding = ctx; struct webidl_node *ident_node; - ident_node = webidl_node_find(webidl_node_getnode(node), - NULL, - webidl_cmp_node_type, - (void *)WEBIDL_NODE_TYPE_IDENT); - + ident_node = webidl_node_find_type(webidl_node_getnode(node), + NULL, + WEBIDL_NODE_TYPE_IDENT); if (ident_node == NULL) { /* operation without identifier - must have special keyword * http://www.w3.org/TR/WebIDL/#idl-operations */ } else { fprintf(binding->outfile, - "\tJSAPI_FS(%s, 0, JSPROP_ENUMERATE ),\n", + "\tJSAPI_FS(%s, %s, 0, JSPROP_ENUMERATE ),\n", + inf->name, webidl_node_gettext(ident_node)); /* @todo number of args to that FN_FS() call should be correct */ } return 0; } - -static int generate_function_spec(struct binding *binding, const char *interface); - -/* callback to emit implements operator spec */ -static int webidl_function_spec_implements_cb(struct webidl_node *node, void *ctx) +int output_function_spec(struct binding *binding) { - struct binding *binding = ctx; + int inf; + int res; + struct webidl_node *list_node; + struct webidl_node *op_node; /* operation on list node */ - return generate_function_spec(binding, webidl_node_gettext(node)); -} + /* generate functions for each interface in the map */ + for (inf = 0; inf < binding->interfacec; inf++) { + if (binding->interfaces[inf].own_functions == 0) { + continue; + } -static int -generate_function_spec(struct binding *binding, const char *interface) -{ - struct webidl_node *interface_node; - struct webidl_node *members_node; - struct webidl_node *inherit_node; - int res = 0; - - /* find interface in webidl with correct ident attached */ - interface_node = webidl_node_find_type_ident(binding->wi_ast, - WEBIDL_NODE_TYPE_INTERFACE, - interface); - - if (interface_node == NULL) { - fprintf(stderr, - "Unable to find interface %s in loaded WebIDL\n", - interface); - return -1; - } + fprintf(binding->outfile, + "static JSFunctionSpec JSClass_%s_functions[] = {\n", + binding->interfaces[inf].name); + + /* iterate each list within an interface */ + list_node = webidl_node_find_type( + webidl_node_getnode(binding->interfaces[inf].widl_node), + NULL, + WEBIDL_NODE_TYPE_LIST); + + while (list_node != NULL) { + /* iterate through operations in a list */ + op_node = webidl_node_find_type( + webidl_node_getnode(list_node), + NULL, + WEBIDL_NODE_TYPE_OPERATION); + + while (op_node != NULL) { + res = webidl_operator_spec( + binding, + &binding->interfaces[inf], + op_node); + if (res != 0) { + return res; + } + + op_node = webidl_node_find_type( + webidl_node_getnode(list_node), + op_node, + WEBIDL_NODE_TYPE_OPERATION); + } - members_node = webidl_node_find(webidl_node_getnode(interface_node), - NULL, - webidl_cmp_node_type, - (void *)WEBIDL_NODE_TYPE_LIST); - while (members_node != NULL) { - - fprintf(binding->outfile,"\t/**** %s ****/\n", interface); - - /* for each function emit a JSAPI_FS()*/ - webidl_node_for_each_type(webidl_node_getnode(members_node), - WEBIDL_NODE_TYPE_OPERATION, - webidl_func_spec_cb, - binding); - - members_node = webidl_node_find(webidl_node_getnode(interface_node), - members_node, - webidl_cmp_node_type, - (void *)WEBIDL_NODE_TYPE_LIST); - } - /* check for inherited nodes and insert them too */ - inherit_node = webidl_node_find(webidl_node_getnode(interface_node), - NULL, - webidl_cmp_node_type, - (void *)WEBIDL_NODE_TYPE_INTERFACE_INHERITANCE); + list_node = webidl_node_find_type( + webidl_node_getnode(binding->interfaces[inf].widl_node), + list_node, + WEBIDL_NODE_TYPE_LIST); + } - if (inherit_node != NULL) { - res = generate_function_spec(binding, - webidl_node_gettext(inherit_node)); - } + fprintf(binding->outfile, "\tJSAPI_FS_END\n};\n\n"); - if (res == 0) { - res = webidl_node_for_each_type(webidl_node_getnode(interface_node), - WEBIDL_NODE_TYPE_INTERFACE_IMPLEMENTS, - webidl_function_spec_implements_cb, - binding); } - - return res; -} - -int output_function_spec(struct binding *binding) -{ - int res; - - fprintf(binding->outfile, - "static JSFunctionSpec jsclass_functions[] = {\n"); - - res = generate_function_spec(binding, binding->interface); - - fprintf(binding->outfile, "\tJSAPI_FS_END\n};\n\n"); - - return res; + return 0; } static int output_return(struct binding *binding, @@ -307,7 +279,7 @@ static int output_return_declaration(struct binding *binding, break; case WEBIDL_TYPE_LONGLONG: - WARN(WARNING_UNIMPLEMENTED, + WARN(WARNING_UNIMPLEMENTED, "Unhandled type WEBIDL_TYPE_LONGLONG"); break; @@ -316,7 +288,7 @@ static int output_return_declaration(struct binding *binding, type_mod = webidl_node_find_type(webidl_node_getnode(type_node), NULL, WEBIDL_NODE_TYPE_MODIFIER); - if ((type_mod != NULL) && + if ((type_mod != NULL) && (webidl_node_getint(type_mod) == WEBIDL_TYPE_MODIFIER_UNSIGNED)) { fprintf(binding->outfile, "\tuint32_t %s = 0;\n", ident); } else { @@ -332,7 +304,7 @@ static int output_return_declaration(struct binding *binding, break; case WEBIDL_TYPE_SEQUENCE: - WARN(WARNING_UNIMPLEMENTED, + WARN(WARNING_UNIMPLEMENTED, "Unhandled type WEBIDL_TYPE_SEQUENCE"); break; @@ -474,14 +446,14 @@ output_variable_definitions(struct binding *binding, type_mod = webidl_node_find_type(webidl_node_getnode(arg_type), NULL, WEBIDL_NODE_TYPE_MODIFIER); - if ((type_mod != NULL) && + if ((type_mod != NULL) && (webidl_node_getint(type_mod) == WEBIDL_TYPE_MODIFIER_UNSIGNED)) { - fprintf(binding->outfile, - "\tuint32_t %s = 0;\n", + fprintf(binding->outfile, + "\tuint32_t %s = 0;\n", webidl_node_gettext(arg_ident)); } else { - fprintf(binding->outfile, - "\tint32_t %s = 0;\n", + fprintf(binding->outfile, + "\tint32_t %s = 0;\n", webidl_node_gettext(arg_ident)); } @@ -624,15 +596,15 @@ output_operation_input(struct binding *binding, type_mod = webidl_node_find_type(webidl_node_getnode(arg_type), NULL, WEBIDL_NODE_TYPE_MODIFIER); - if ((type_mod != NULL) && + if ((type_mod != NULL) && (webidl_node_getint(type_mod) == WEBIDL_TYPE_MODIFIER_UNSIGNED)) { - fprintf(binding->outfile, - "\tJS_ValueToECMAUint32(cx, argv[%d], &%s);\n", + fprintf(binding->outfile, + "\tJS_ValueToECMAUint32(cx, argv[%d], &%s);\n", arg_cur, webidl_node_gettext(arg_ident)); } else { - fprintf(binding->outfile, - "\tJS_ValueToECMAInt32(cx, argv[%d], &%s);\n", + fprintf(binding->outfile, + "\tJS_ValueToECMAInt32(cx, argv[%d], &%s);\n", arg_cur, webidl_node_gettext(arg_ident)); } @@ -690,14 +662,14 @@ output_operation_input(struct binding *binding, } -static int -output_operator_placeholder(struct binding *binding, - struct webidl_node *oplist, +static int +output_operator_placeholder(struct binding *binding, + struct webidl_node *oplist, struct webidl_node *ident_node) { oplist = oplist; - WARN(WARNING_UNIMPLEMENTED, + WARN(WARNING_UNIMPLEMENTED, "operation %s.%s has no implementation\n", binding->interface, webidl_node_gettext(ident_node)); @@ -748,131 +720,118 @@ output_private_get(struct binding *binding, const char *argname) return ret; } -static int webidl_operator_body_cb(struct webidl_node *node, void *ctx) +/** + * Generate operator (function) body + * + * + */ +static int webidl_operator_body(struct binding *binding, + struct binding_interface *inf, + struct webidl_node *node) { - struct binding *binding = ctx; struct webidl_node *ident_node; struct genbind_node *operation_node; - ident_node = webidl_node_find(webidl_node_getnode(node), - NULL, - webidl_cmp_node_type, - (void *)WEBIDL_NODE_TYPE_IDENT); + ident_node = webidl_node_find_type(webidl_node_getnode(node), + NULL, + WEBIDL_NODE_TYPE_IDENT); if (ident_node == NULL) { /* operation without identifier - must have special keyword * http://www.w3.org/TR/WebIDL/#idl-operations */ WARN(WARNING_UNIMPLEMENTED, - "Unhandled operation with no name on %s\n", - binding->interface); - - } else { - /* normal operation with identifier */ + "Unhandled operation with no name on %s\n", + binding->interface); - fprintf(binding->outfile, - "static JSBool JSAPI_FUNC(%s, JSContext *cx, uintN argc, jsval *vp)\n", - webidl_node_gettext(ident_node)); - fprintf(binding->outfile, - "{\n"); + return 0; + } - /* return value declaration */ - output_return_declaration(binding, "jsret", webidl_node_getnode(node)); + /* normal operation with identifier */ - output_variable_definitions(binding, webidl_node_getnode(node)); + fprintf(binding->outfile, + "static JSBool JSAPI_FUNC(%s, %s, JSContext *cx, uintN argc, jsval *vp)\n" + "{\n", + inf->name, + webidl_node_gettext(ident_node)); - output_private_get(binding, "private"); + /* return value declaration */ + output_return_declaration(binding, "jsret", webidl_node_getnode(node)); - output_operation_input(binding, webidl_node_getnode(node)); + output_variable_definitions(binding, webidl_node_getnode(node)); - operation_node = genbind_node_find_type_ident(binding->gb_ast, - NULL, - GENBIND_NODE_TYPE_OPERATION, - webidl_node_gettext(ident_node)); + output_private_get(binding, "private"); - if (operation_node != NULL) { - output_code_block(binding, - genbind_node_getnode(operation_node)); + output_operation_input(binding, webidl_node_getnode(node)); - } else { - output_operator_placeholder(binding, webidl_node_getnode(node), ident_node); - } + operation_node = genbind_node_find_type_ident(binding->gb_ast, + NULL, + GENBIND_NODE_TYPE_OPERATION, + webidl_node_gettext(ident_node)); - output_return(binding, "jsret", webidl_node_getnode(node)); + if (operation_node != NULL) { + output_code_block(binding, + genbind_node_getnode(operation_node)); - /* set return value an return true */ - fprintf(binding->outfile, - "\treturn JS_TRUE;\n" - "}\n\n"); + } else { + output_operator_placeholder(binding, webidl_node_getnode(node), ident_node); } - return 0; -} -/* callback to emit implements operator bodys */ -static int webidl_implements_cb(struct webidl_node *node, void *ctx) -{ - struct binding *binding = ctx; + output_return(binding, "jsret", webidl_node_getnode(node)); - return output_operator_body(binding, webidl_node_gettext(node)); + /* set return value an return true */ + fprintf(binding->outfile, + "\treturn JS_TRUE;\n" + "}\n\n"); + + return 0; } + /* exported interface documented in jsapi-libdom.h */ -int -output_operator_body(struct binding *binding, const char *interface) +int output_function_bodies(struct binding *binding) { - struct webidl_node *interface_node; - struct webidl_node *members_node; - struct webidl_node *inherit_node; - int res = 0; - - /* find interface in webidl with correct ident attached */ - interface_node = webidl_node_find_type_ident(binding->wi_ast, - WEBIDL_NODE_TYPE_INTERFACE, - interface); - - if (interface_node == NULL) { - fprintf(stderr, - "Unable to find interface %s in loaded WebIDL\n", - interface); - return -1; - } - - members_node = webidl_node_find(webidl_node_getnode(interface_node), - NULL, - webidl_cmp_node_type, - (void *)WEBIDL_NODE_TYPE_LIST); - while (members_node != NULL) { - - fprintf(binding->outfile,"/**** %s ****/\n", interface); - - /* for each function emit a JSAPI_FS()*/ - webidl_node_for_each_type(webidl_node_getnode(members_node), - WEBIDL_NODE_TYPE_OPERATION, - webidl_operator_body_cb, - binding); - - members_node = webidl_node_find(webidl_node_getnode(interface_node), - members_node, - webidl_cmp_node_type, - (void *)WEBIDL_NODE_TYPE_LIST); - } - - /* check for inherited nodes and insert them too */ - inherit_node = webidl_node_find_type(webidl_node_getnode(interface_node), - NULL, - WEBIDL_NODE_TYPE_INTERFACE_INHERITANCE); + int inf; + int res; + struct webidl_node *list_node; + struct webidl_node *op_node; /* operation on list node */ + + /* generate functions for each interface in the map */ + for (inf = 0; inf < binding->interfacec; inf++) { + /* iterate each list within an interface */ + list_node = webidl_node_find_type( + webidl_node_getnode(binding->interfaces[inf].widl_node), + NULL, + WEBIDL_NODE_TYPE_LIST); + + while (list_node != NULL) { + /* iterate through operations in a list */ + op_node = webidl_node_find_type( + webidl_node_getnode(list_node), + NULL, + WEBIDL_NODE_TYPE_OPERATION); + + while (op_node != NULL) { + res = webidl_operator_body( + binding, + &binding->interfaces[inf], + op_node); + if (res != 0) { + return res; + } + + op_node = webidl_node_find_type( + webidl_node_getnode(list_node), + op_node, + WEBIDL_NODE_TYPE_OPERATION); + } - if (inherit_node != NULL) { - res = output_operator_body(binding, - webidl_node_gettext(inherit_node)); - } - if (res == 0) { - res = webidl_node_for_each_type(webidl_node_getnode(interface_node), - WEBIDL_NODE_TYPE_INTERFACE_IMPLEMENTS, - webidl_implements_cb, - binding); + list_node = webidl_node_find_type( + webidl_node_getnode(binding->interfaces[inf].widl_node), + list_node, + WEBIDL_NODE_TYPE_LIST); + } } - - return res; + return 0; } diff --git a/src/jsapi-libdom-init.c b/src/jsapi-libdom-init.c new file mode 100644 index 0000000..1077c2d --- /dev/null +++ b/src/jsapi-libdom-init.c @@ -0,0 +1,286 @@ +/* Javascript spidemonkey API to libdom binding generation for class + * initilisation + * + * 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 <stdbool.h> +#include <stdio.h> +#include <errno.h> +#include <string.h> +#include <stdlib.h> +#include <assert.h> + +#include "options.h" +#include "nsgenbind-ast.h" +#include "webidl-ast.h" +#include "jsapi-libdom.h" + +static int output_cast_literal(struct binding *binding, + struct webidl_node *node) +{ + struct webidl_node *type_node = NULL; + struct webidl_node *literal_node = NULL; + struct webidl_node *type_base = NULL; + enum webidl_type webidl_arg_type; + + type_node = webidl_node_find_type(webidl_node_getnode(node), + NULL, + WEBIDL_NODE_TYPE_TYPE); + + type_base = webidl_node_find_type(webidl_node_getnode(type_node), + NULL, + WEBIDL_NODE_TYPE_TYPE_BASE); + + webidl_arg_type = webidl_node_getint(type_base); + + switch (webidl_arg_type) { + + case WEBIDL_TYPE_BOOL: + /* JSBool */ + literal_node = webidl_node_find_type(webidl_node_getnode(node), + NULL, + WEBIDL_NODE_TYPE_LITERAL_BOOL); + fprintf(binding->outfile, "BOOLEAN_TO_JSVAL(JS_FALSE)"); + break; + + case WEBIDL_TYPE_FLOAT: + case WEBIDL_TYPE_DOUBLE: + /* double */ + literal_node = webidl_node_find_type(webidl_node_getnode(node), + NULL, + WEBIDL_NODE_TYPE_LITERAL_FLOAT); + fprintf(binding->outfile, "DOUBLE_TO_JSVAL(0.0)"); + break; + + case WEBIDL_TYPE_LONG: + /* int32_t */ + literal_node = webidl_node_find_type(webidl_node_getnode(node), + NULL, + WEBIDL_NODE_TYPE_LITERAL_INT); + fprintf(binding->outfile, + "INT_TO_JSVAL(%d)", + webidl_node_getint(literal_node)); + break; + + case WEBIDL_TYPE_SHORT: + /* int16_t */ + literal_node = webidl_node_find_type(webidl_node_getnode(node), + NULL, + WEBIDL_NODE_TYPE_LITERAL_INT); + fprintf(binding->outfile, + "INT_TO_JSVAL(%d)", + webidl_node_getint(literal_node)); + break; + + + case WEBIDL_TYPE_STRING: + case WEBIDL_TYPE_BYTE: + case WEBIDL_TYPE_OCTET: + case WEBIDL_TYPE_LONGLONG: + case WEBIDL_TYPE_SEQUENCE: + case WEBIDL_TYPE_OBJECT: + case WEBIDL_TYPE_DATE: + case WEBIDL_TYPE_VOID: + case WEBIDL_TYPE_USER: + default: + WARN(WARNING_UNIMPLEMENTED, "types not allowed as literal"); + break; /* @todo these types are not allowed here */ + } + + return 0; +} + +static int webidl_const_define_cb(struct webidl_node *node, void *ctx) +{ + struct binding *binding = ctx; + struct webidl_node *ident_node; + + ident_node = webidl_node_find_type(webidl_node_getnode(node), + NULL, + WEBIDL_NODE_TYPE_IDENT); + if (ident_node == NULL) { + /* Broken AST - must have ident */ + return 1; + } + + fprintf(binding->outfile, + "\tJS_DefineProperty(cx, " + "prototype, " + "\"%s\", ", + webidl_node_gettext(ident_node)); + + output_cast_literal(binding, node); + + fprintf(binding->outfile, + ", " + "JS_PropertyStub, " + "JS_StrictPropertyStub, " + "JSPROP_READONLY | JSPROP_ENUMERATE | JSPROP_PERMANENT);\n\n"); + + return 0; + +} + + +/** output all the constant property defines for an interface */ +static int +output_interface_consts(struct binding *binding, + struct webidl_node *interface_node) +{ + struct webidl_node *members_node; + + /* generate property entries for each list (partial interfaces) */ + members_node = webidl_node_find_type( + webidl_node_getnode(interface_node), + NULL, + WEBIDL_NODE_TYPE_LIST); + + while (members_node != NULL) { + + /* for each const emit a property define */ + webidl_node_for_each_type(webidl_node_getnode(members_node), + WEBIDL_NODE_TYPE_CONST, + webidl_const_define_cb, + binding); + + + members_node = webidl_node_find_type( + webidl_node_getnode(interface_node), + members_node, + WEBIDL_NODE_TYPE_LIST); + } + + return 0; +} + + +static int generate_prototype_init(struct binding *binding, int inf) +{ + int inherit_inf; + + /* find this interfaces parent interface to inherit prototype from */ + inherit_inf = binding->interfaces[inf].inherit_idx; + while ((inherit_inf != -1) && + (binding->interfaces[inherit_inf].node == NULL)) { + inherit_inf = binding->interfaces[inherit_inf].inherit_idx; + } + + fprintf(binding->outfile, + "\n" + "\tprototype = JS_InitClass(cx, " + "parent, "); + + /* either this init is being constructed without a chain or is + * using a prototype of a previously initialised class + */ + if (inherit_inf == -1) { + fprintf(binding->outfile, + "NULL, "); + } else { + fprintf(binding->outfile, + "prototypes[%d], ", + binding->interfaces[inherit_inf].output_idx); + } + + fprintf(binding->outfile, + "&JSClass_%s, " + "NULL, " + "0, " + "NULL, " + "NULL, " + "NULL, " + "NULL);\n", + binding->interfaces[inf].name); + + /* check prototype construction */ + fprintf(binding->outfile, + "\tif (prototype == NULL) {\n" + "\t\treturn %d;\n" + "\t}\n\n", + binding->interfaces[inf].output_idx); + + /* store result */ + fprintf(binding->outfile, + "\tprototypes[%d] = prototype;\n", + binding->interfaces[inf].output_idx); + + /* output the consts for the interface and ancestors if necessary */ + do { + output_interface_consts(binding, binding->interfaces[inf].widl_node); + inf = binding->interfaces[inf].inherit_idx; + } while (inf != inherit_inf); + + return 0; +} + +/** generate class initialisers + * + * Generates function to create the javascript class prototypes for + * each interface in the binding. + * + */ +int output_class_init(struct binding *binding) +{ + int res = 0; + struct genbind_node *api_node; + int inf; + + /* class Initialisor declaration */ + if (binding->hdrfile) { + + if (binding->interfacec > 1) { + fprintf(binding->hdrfile, + "\n#define %s_INTERFACE_COUNT %d", + binding->name, + binding->interfacec); + } + + fprintf(binding->hdrfile, + "\nint jsapi_InitClass_%s(JSContext *cx, JSObject *parent, JSObject **prototypes);\n\n", + binding->name); + + + } + + /* class Initialisor definition */ + fprintf(binding->outfile, + "int\n" + "jsapi_InitClass_%s(JSContext *cx, " + "JSObject *parent, " + "JSObject **prototypes)\n" + "{\n" + "\tJSObject *prototype;\n", + binding->name); + + /* check for the binding having an init override */ + api_node = genbind_node_find_type_ident(binding->gb_ast, + NULL, + GENBIND_NODE_TYPE_API, + "init"); + + if (api_node != NULL) { + output_code_block(binding, genbind_node_getnode(api_node)); + } else { + /* generate interface init for each class in binding */ + for (inf = 0; inf < binding->interfacec; inf++) { + /* skip generating javascript class + * initialisation for interfaces not in binding + */ + if (binding->interfaces[inf].node != NULL) { + generate_prototype_init(binding, inf); + } + } + + fprintf(binding->outfile, + "\n\treturn %d;\n", + inf); + } + + fprintf(binding->outfile, "}\n\n"); + + return res; +} diff --git a/src/jsapi-libdom-jsclass.c b/src/jsapi-libdom-jsclass.c new file mode 100644 index 0000000..3a5a84c --- /dev/null +++ b/src/jsapi-libdom-jsclass.c @@ -0,0 +1,180 @@ +/* Javascript spidemonkey API to libdom binding generation for class + * initilisation + * + * 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 <stdbool.h> +#include <stdio.h> +#include <errno.h> +#include <string.h> +#include <stdlib.h> +#include <assert.h> + +#include "options.h" +#include "nsgenbind-ast.h" +#include "webidl-ast.h" +#include "jsapi-libdom.h" + +#define HDROUTF(bndg, fmt, args...) do { \ + if (bndg->hdrfile != NULL) { \ + fprintf(bndg->hdrfile, fmt, ##args); \ + } \ + } while(0) + +static bool interface_is_global(struct genbind_node *interface_node) +{ + if (genbind_node_find_type_ident( + genbind_node_getnode(interface_node), + NULL, + GENBIND_NODE_TYPE_BINDING_INTERFACE_FLAGS, + "global") != NULL) { + return true; + } + + return false; +} + +static int output_jsclass(struct binding *binding, + const char *interface_name, + struct genbind_node *interface_node) +{ + /* output the class declaration */ + HDROUTF(binding, "JSClass JSClass_%s;\n", interface_name); + + /* output the class definition */ + fprintf(binding->outfile, + "JSClass JSClass_%s = {\n" + "\t\"%s\",\n", + interface_name, + interface_name); + + /* generate class flags */ + if (interface_is_global(interface_node)) { + fprintf(binding->outfile, "\tJSCLASS_GLOBAL_FLAGS"); + } else { + fprintf(binding->outfile, "\t0"); + } + + if (binding->resolve != NULL) { + fprintf(binding->outfile, " | JSCLASS_NEW_RESOLVE"); + } + + if (binding->mark != NULL) { + fprintf(binding->outfile, " | JSAPI_JSCLASS_MARK_IS_TRACE"); + } + + if (binding->has_private) { + fprintf(binding->outfile, " | JSCLASS_HAS_PRIVATE"); + } + + fprintf(binding->outfile, ",\n"); + + /* add property */ + if (binding->addproperty != NULL) { + fprintf(binding->outfile, + "\tjsapi_property_add,\t/* addProperty */\n"); + } else { + fprintf(binding->outfile, + "\tJS_PropertyStub,\t/* addProperty */\n"); + } + + /* del property */ + if (binding->delproperty != NULL) { + fprintf(binding->outfile, + "\tjsapi_property_del,\t/* delProperty */\n"); + } else { + fprintf(binding->outfile, + "\tJS_PropertyStub,\t/* delProperty */\n"); + } + + /* get property */ + if (binding->getproperty != NULL) { + fprintf(binding->outfile, + "\tjsapi_property_get,\t/* getProperty */\n"); + } else { + fprintf(binding->outfile, + "\tJS_PropertyStub,\t/* getProperty */\n"); + } + + /* set property */ + if (binding->setproperty != NULL) { + fprintf(binding->outfile, + "\tjsapi_property_set,\t/* setProperty */\n"); + } else { + fprintf(binding->outfile, + "\tJS_StrictPropertyStub,\t/* setProperty */\n"); + } + + /* enumerate */ + if (binding->enumerate != NULL) { + fprintf(binding->outfile, + "\tjsclass_enumerate,\t/* enumerate */\n"); + } else { + fprintf(binding->outfile, + "\tJS_EnumerateStub,\t/* enumerate */\n"); + } + + /* resolver */ + if (binding->resolve != NULL) { + fprintf(binding->outfile, + "\t(JSResolveOp)jsclass_resolve,\t/* resolve */\n"); + } else { + fprintf(binding->outfile, + "\tJS_ResolveStub,\t\t/* resolve */\n"); + } + + fprintf(binding->outfile, "\tJS_ConvertStub,\t\t/* convert */\n"); + + if (binding->has_private || (binding->finalise != NULL)) { + fprintf(binding->outfile, + "\tjsclass_finalize,\t/* finalizer */\n"); + } else { + fprintf(binding->outfile, + "\tJS_FinalizeStub,\t/* finalizer */\n"); + } + fprintf(binding->outfile, + "\t0,\t\t\t/* reserved */\n" + "\tNULL,\t\t\t/* checkAccess */\n" + "\tNULL,\t\t\t/* call */\n" + "\tNULL,\t\t\t/* construct */\n" + "\tNULL,\t\t\t/* xdr Object */\n" + "\tNULL,\t\t\t/* hasInstance */\n"); + + /* trace/mark */ + if (binding->mark != NULL) { + fprintf(binding->outfile, + "\tJSAPI_JSCLASS_MARKOP(jsclass_mark),\n"); + } else { + fprintf(binding->outfile, "\tNULL,\t\t\t/* trace/mark */\n"); + } + + fprintf(binding->outfile, + "\tJSAPI_CLASS_NO_INTERNAL_MEMBERS\n" + "};\n\n"); + + return 0; +} + +int +output_jsclasses(struct binding *binding) +{ + int inf; + + for (inf = 0; inf < binding->interfacec; inf++) { + /* skip generating javascript classes for interfaces + * not in binding + */ + if (binding->interfaces[inf].node == NULL) { + continue; + } + + output_jsclass(binding, + binding->interfaces[inf].name, + binding->interfaces[inf].node); + } + return 0; +} diff --git a/src/jsapi-libdom-new.c b/src/jsapi-libdom-new.c new file mode 100644 index 0000000..b78c715 --- /dev/null +++ b/src/jsapi-libdom-new.c @@ -0,0 +1,395 @@ +/* Spidemonkey Javascript API to libdom binding generation for class + * construction. + * + * This file is part of nsgenbind. + * Licensed under the MIT License, + * http://www.opensource.org/licenses/mit-license.php + * Copyright 2013 Vincent Sanders <vince@netsurf-browser.org> + */ + +#include <stdbool.h> +#include <stdio.h> +#include <errno.h> +#include <string.h> +#include <stdlib.h> +#include <assert.h> + +#include "options.h" +#include "nsgenbind-ast.h" +#include "webidl-ast.h" +#include "jsapi-libdom.h" + +static int webidl_private_param_cb(struct genbind_node *node, void *ctx) +{ + struct binding *binding = ctx; + struct genbind_node *ident_node; + struct genbind_node *type_node; + + + ident_node = genbind_node_find_type(genbind_node_getnode(node), + NULL, + GENBIND_NODE_TYPE_IDENT); + if (ident_node == NULL) + return -1; /* bad AST */ + + type_node = genbind_node_find_type(genbind_node_getnode(node), + NULL, + GENBIND_NODE_TYPE_STRING); + if (type_node == NULL) + return -1; /* bad AST */ + + fprintf(binding->outfile, + ",\n\t\t%s%s", + genbind_node_gettext(type_node), + genbind_node_gettext(ident_node)); + + return 0; +} + +static int webidl_private_assign_cb(struct genbind_node *node, void *ctx) +{ + struct binding *binding = ctx; + struct genbind_node *ident_node; + const char *ident; + + ident_node = genbind_node_find_type(genbind_node_getnode(node), + NULL, + GENBIND_NODE_TYPE_IDENT); + if (ident_node == NULL) + return -1; /* bad AST */ + + ident = genbind_node_gettext(ident_node); + + fprintf(binding->outfile, "\tprivate->%s = %s;\n", ident, ident); + + return 0; +} + + + +static int +output_binding_constructor(struct binding *binding) +{ + fprintf(binding->outfile, + "JSObject *jsapi_new_%s(JSContext *cx, \n", + binding->name); + + fprintf(binding->outfile, "\t\tJSObject *prototype, \n"); + + if (binding->interfacec != 1) { + fprintf(binding->outfile, "\t\tconst char *interface_name, \n"); + } + + fprintf(binding->outfile, "\t\tJSObject *parent"); + + genbind_node_foreach_type(binding->binding_list, + GENBIND_NODE_TYPE_BINDING_PRIVATE, + webidl_private_param_cb, + binding); + + fprintf(binding->outfile, ")"); + + return 0; +} + +static int +output_class_wprivate_multi(struct binding *binding) +{ + int inf; + + /* create and initialise private data */ + fprintf(binding->outfile, + "\tstruct jsclass_private *private;\n" + "\n" + "\tprivate = malloc(sizeof(struct jsclass_private));\n" + "\tif (private == NULL) {\n" + "\t\treturn NULL;\n" + "\t}\n"); + + genbind_node_foreach_type(binding->binding_list, + GENBIND_NODE_TYPE_BINDING_PRIVATE, + webidl_private_assign_cb, + binding); + + + fprintf(binding->outfile, "\n\n\t"); + + /* for each interface in the map generate initialisor */ + for (inf = 0; inf < binding->interfacec; inf++) { + + fprintf(binding->outfile, + "if (strcmp(interface_name, JSClass_%s.name) == 0) {\n", + binding->interfaces[inf].name); + + fprintf(binding->outfile, + "\n" + "\t\tnewobject = JS_NewObject(cx, &JSClass_%s, prototype, parent);\n", + binding->interface); + + + fprintf(binding->outfile, + "\t\tif (newobject == NULL) {\n" + "\t\t\tfree(private);\n" + "\t\t\treturn NULL;\n" + "\t\t}\n\n"); + + /* root object to stop it being garbage collected */ + fprintf(binding->outfile, + "\t\tif (JSAPI_ADD_OBJECT_ROOT(cx, &newobject) != JS_TRUE) {\n" + "\t\t\tfree(private);\n" + "\t\t\treturn NULL;\n" + "\t\t}\n\n"); + + fprintf(binding->outfile, + "\n" + "\t\t/* attach private pointer */\n" + "\t\tif (JS_SetPrivate(cx, newobject, private) != JS_TRUE) {\n" + "\t\t\tfree(private);\n" + "\t\t\treturn NULL;\n" + "\t\t}\n\n"); + + + /* attach operations and attributes (functions and properties) */ + fprintf(binding->outfile, + "\t\tif (JS_DefineFunctions(cx, newobject, jsclass_functions) != JS_TRUE) {\n" + "\t\t\tfree(private);\n" + "\t\t\treturn NULL;\n" + "\t\t}\n\n"); + + fprintf(binding->outfile, + "\t\tif (JS_DefineProperties(cx, newobject, jsclass_properties) != JS_TRUE) {\n" + "\t\t\tfree(private);\n" + "\t\t\treturn NULL;\n" + "\t\t}\n\n"); + + /* unroot object */ + fprintf(binding->outfile, + "\t\tJSAPI_REMOVE_OBJECT_ROOT(cx, &newobject);\n\n" + "\t} else "); + } + fprintf(binding->outfile, + "{\n" + "\t\tfree(private);\n" + "\t\treturn NULL;\n" + "\t}\n"); + + return 0; +} + +static int +output_class_wprivate(struct binding *binding, struct genbind_node *api_node) +{ + /* create and initialise private data */ + fprintf(binding->outfile, + "\tstruct jsclass_private *private;\n" + "\n" + "\tprivate = malloc(sizeof(struct jsclass_private));\n" + "\tif (private == NULL) {\n" + "\t\treturn NULL;\n" + "\t}\n"); + + genbind_node_foreach_type(binding->binding_list, + GENBIND_NODE_TYPE_BINDING_PRIVATE, + webidl_private_assign_cb, + binding); + + if (api_node != NULL) { + output_code_block(binding, genbind_node_getnode(api_node)); + } else { + fprintf(binding->outfile, + "\n" + "\tnewobject = JS_NewObject(cx, &JSClass_%s, prototype, parent);\n", + binding->interface); + } + + fprintf(binding->outfile, + "\tif (newobject == NULL) {\n" + "\t\tfree(private);\n" + "\t\treturn NULL;\n" + "\t}\n\n"); + + /* root object to stop it being garbage collected */ + fprintf(binding->outfile, + "\tif (JSAPI_ADD_OBJECT_ROOT(cx, &newobject) != JS_TRUE) {\n" + "\t\tfree(private);\n" + "\t\treturn NULL;\n" + "\t}\n\n"); + + fprintf(binding->outfile, + "\n" + "\t/* attach private pointer */\n" + "\tif (JS_SetPrivate(cx, newobject, private) != JS_TRUE) {\n" + "\t\tfree(private);\n" + "\t\treturn NULL;\n" + "\t}\n\n"); + + + /* attach operations and attributes (functions and properties) */ + fprintf(binding->outfile, + "\tif (JS_DefineFunctions(cx, newobject, jsclass_functions) != JS_TRUE) {\n" + "\t\tfree(private);\n" + "\t\treturn NULL;\n" + "\t}\n\n"); + + fprintf(binding->outfile, + "\tif (JS_DefineProperties(cx, newobject, jsclass_properties) != JS_TRUE) {\n" + "\t\tfree(private);\n" + "\t\treturn NULL;\n" + "\t}\n\n"); + + /* unroot object */ + fprintf(binding->outfile, + "\tJSAPI_REMOVE_OBJECT_ROOT(cx, &newobject);\n\n"); + + return 0; +} + +static int +output_class_woprivate_multi(struct binding *binding) +{ + int inf; + + fprintf(binding->outfile, "\n\t"); + + /* for each interface in the map generate initialisor */ + for (inf = 0; inf < binding->interfacec; inf++) { + fprintf(binding->outfile, + "if (strcmp(interface_name, JSClass_%s.name) == 0) {\n", + binding->interfaces[inf].name); + + fprintf(binding->outfile, + "\t\tnewobject = JS_NewObject(cx, &JSClass_%s, prototype, parent);\n", + binding->interface); + + + fprintf(binding->outfile, + "\tif (newobject == NULL) {\n" + "\t\treturn NULL;\n" + "\t}\n"); + + /* root object to stop it being garbage collected */ + fprintf(binding->outfile, + "\tif (JSAPI_ADD_OBJECT_ROOT(cx, &newobject) != JS_TRUE) {\n" + "\t\treturn NULL;\n" + "\t}\n\n"); + + /* attach operations and attributes (functions and properties) */ + fprintf(binding->outfile, + "\tif (JS_DefineFunctions(cx, newobject, jsclass_functions) != JS_TRUE) {\n" + "\t\treturn NULL;\n" + "\t}\n\n"); + + fprintf(binding->outfile, + "\tif (JS_DefineProperties(cx, newobject, jsclass_properties) != JS_TRUE) {\n" + "\t\treturn NULL;\n" + "\t}\n\n"); + + /* unroot object */ + fprintf(binding->outfile, + "\tJSAPI_REMOVE_OBJECT_ROOT(cx, &newobject);\n\n"); + + } + fprintf(binding->outfile, + "{\n" + "\t\tfree(private);\n" + "\t\treturn NULL;\n" + "\t}\n"); + + + return 0; +} + +static int +output_class_woprivate(struct binding *binding, struct genbind_node *api_node) +{ + + if (api_node != NULL) { + output_code_block(binding, genbind_node_getnode(api_node)); + } else { + fprintf(binding->outfile, + "\n" + "\tnewobject = JS_NewObject(cx, &JSClass_%s, prototype, parent);\n", + binding->interface); + + } + + fprintf(binding->outfile, + "\tif (newobject == NULL) {\n" + "\t\treturn NULL;\n" + "\t}\n"); + + /* root object to stop it being garbage collected */ + fprintf(binding->outfile, + "\tif (JSAPI_ADD_OBJECT_ROOT(cx, &newobject) != JS_TRUE) {\n" + "\t\treturn NULL;\n" + "\t}\n\n"); + + /* attach operations and attributes (functions and properties) */ + fprintf(binding->outfile, + "\tif (JS_DefineFunctions(cx, newobject, jsclass_functions) != JS_TRUE) {\n" + "\t\treturn NULL;\n" + "\t}\n\n"); + + fprintf(binding->outfile, + "\tif (JS_DefineProperties(cx, newobject, jsclass_properties) != JS_TRUE) {\n" + "\t\treturn NULL;\n" + "\t}\n\n"); + + /* unroot object */ + fprintf(binding->outfile, + "\tJSAPI_REMOVE_OBJECT_ROOT(cx, &newobject);\n\n"); + + return 0; +} + +int +output_class_new(struct binding *binding) +{ + int res = 0; + struct genbind_node *api_node; + + /* constructor declaration */ + if (binding->hdrfile) { + binding->outfile = binding->hdrfile; + + output_binding_constructor(binding); + + fprintf(binding->outfile, ";\n"); + + binding->outfile = binding->srcfile; + } + + /* constructor definition */ + output_binding_constructor(binding); + + fprintf(binding->outfile, + "\n{\n" + "\tJSObject *newobject;\n"); + + api_node = genbind_node_find_type_ident(binding->gb_ast, + NULL, + GENBIND_NODE_TYPE_API, + "new"); + + /* generate correct constructor body */ + if (binding->has_private) { + if ((binding->interfacec == 1) || (api_node != NULL)) { + res = output_class_wprivate(binding, api_node); + } else { + res = output_class_wprivate_multi(binding); + } + } else { + if ((binding->interfacec == 1) || (api_node != NULL)) { + res = output_class_woprivate(binding, api_node); + } else { + res = output_class_woprivate_multi(binding); + } + } + + /* return newly created object */ + fprintf(binding->outfile, + "\treturn newobject;\n" + "}\n"); + + return res; +} diff --git a/src/jsapi-libdom-property.c b/src/jsapi-libdom-property.c index 2bd3068..1a6cc33 100644 --- a/src/jsapi-libdom-property.c +++ b/src/jsapi-libdom-property.c @@ -19,25 +19,38 @@ static int generate_property_tinyid(struct binding *binding, const char *interface); static int generate_property_spec(struct binding *binding, const char *interface); -static int generate_property_body(struct binding *binding, const char *interface); /* generate context data fetcher if the binding has private data */ static inline int -output_private_get(struct binding *binding, const char *argname) +output_private_get(struct binding *binding, + struct binding_interface *inf, + const char *argname) { int ret = 0; if (binding->has_private) { ret = fprintf(binding->outfile, - "\tstruct jsclass_private *%s;\n" - "\n" - "\t%s = JS_GetInstancePrivate(cx, obj, &JSClass_%s, NULL);\n" - "\tif (%s == NULL) {\n" - "\t\treturn JS_FALSE;\n" - "\t}\n\n", - argname, argname, binding->interface, argname); + "\tstruct jsclass_private *%s;\n\n", argname); + + if (inf->refcount == 0) { + /* leaf class so use safer getinstance private */ + ret += fprintf(binding->outfile, + "\t%s = JS_GetInstancePrivate(cx, obj, &JSClass_%s, NULL);\n", + argname, + inf->name); + } else { + ret += fprintf(binding->outfile, + "\t%s = JS_GetPrivate(obj);\n", + argname); + } + + ret += fprintf(binding->outfile, + "\tif (%s == NULL) {\n" + "\t\treturn JS_FALSE;\n" + "\t}\n\n", + argname); if (options->dbglog) { ret += fprintf(binding->outfile, @@ -108,7 +121,7 @@ output_property_tinyid_get(struct binding *binding, const char *argname) /******************************** tinyid ********************************/ -static int +static int webidl_property_tinyid_cb(struct webidl_node *node, void *ctx) { struct binding *binding = ctx; @@ -131,14 +144,6 @@ webidl_property_tinyid_cb(struct webidl_node *node, void *ctx) return 0; } -/* callback to emit implements property spec */ -static int -webidl_property_tinyid_implements_cb(struct webidl_node *node, void *ctx) -{ - struct binding *binding = ctx; - - return generate_property_tinyid(binding, webidl_node_gettext(node)); -} static int generate_property_tinyid(struct binding *binding, const char *interface) @@ -190,13 +195,6 @@ generate_property_tinyid(struct binding *binding, const char *interface) res = generate_property_tinyid(binding, webidl_node_gettext(inherit_node)); } - if (res == 0) { - res = webidl_node_for_each_type(webidl_node_getnode(interface_node), - WEBIDL_NODE_TYPE_INTERFACE_IMPLEMENTS, - webidl_property_tinyid_implements_cb, - binding); - } - return res; } @@ -211,7 +209,7 @@ output_property_tinyid(struct binding *binding) res = generate_property_tinyid(binding, binding->interface); - fprintf(binding->outfile, + fprintf(binding->outfile, "\tJSAPI_PROP_TINYID_END,\n" "};\n\n"); @@ -229,6 +227,7 @@ get_binding_shared_modifier(struct binding *binding, const char *type, const cha { struct genbind_node *shared_node; struct genbind_node *shared_mod_node; + enum genbind_type_modifier *shared_modifier; /* look for node matching the ident first */ shared_node = genbind_node_find_type_ident(binding->binding_list, @@ -251,9 +250,11 @@ get_binding_shared_modifier(struct binding *binding, const char *type, const cha shared_mod_node = genbind_node_find_type(genbind_node_getnode(shared_node), NULL, GENBIND_NODE_TYPE_MODIFIER); - if (shared_mod_node != NULL) { - return genbind_node_getint(shared_mod_node); + shared_modifier = (enum genbind_type_modifier *)genbind_node_getint(shared_mod_node); + if (shared_modifier != NULL) { + return *shared_modifier; } + } return GENBIND_TYPE_NONE; } @@ -333,14 +334,15 @@ static bool property_is_ro(struct webidl_node *node) return false; } -static int webidl_property_spec_cb(struct webidl_node *node, void *ctx) +static int webidl_property_spec(struct binding *binding, + struct binding_interface *inf, + struct webidl_node *node) { - struct binding *binding = ctx; - struct webidl_node *type_node; const char *type = NULL; struct webidl_node *ident_node; const char *ident; + bool ro = false; ident_node = webidl_node_find_type(webidl_node_getnode(node), NULL, @@ -368,11 +370,10 @@ static int webidl_property_spec_cb(struct webidl_node *node, void *ctx) /* if there is a putforwards the property requires a setter */ if ((property_is_ro(node)) && (get_keyval_extended_attribute(node, "PutForwards") == NULL)) { - fprintf(binding->outfile, "\tJSAPI_PS_RO(\"%s\",\n", ident); - } else { - fprintf(binding->outfile, "\tJSAPI_PS(\"%s\",\n", ident); + ro = true; } + /* generate property shared status */ switch (get_binding_shared_modifier(binding, type, ident)) { @@ -384,133 +385,109 @@ static int webidl_property_spec_cb(struct webidl_node *node, void *ctx) * perform all GC management. */ fprintf(binding->outfile, - "\t\t%s,\n" - "\t\tJSAPI_PROP_TINYID_%s,\n" - "\t\tJSPROP_SHARED | ", - ident, + "\tJSAPI_PS_%s(%s, %s, JSPROP_SHARED),\n", + ro?"RO":"RW", + inf->name, ident); break; case GENBIND_TYPE_TYPE: /* shared property with a type handler */ fprintf(binding->outfile, - "\t\t%s,\n" - "\t\tJSAPI_PROP_TINYID_%s,\n" - "\t\tJSPROP_SHARED | ", - type, - ident); + "\tJSAPI_PS_ID_%s(%s, %s, JSPROP_SHARED, %s),\n", + ro?"RO":"RW", + inf->name, + ident, + type); break; case GENBIND_TYPE_UNSHARED: /* unshared property without type handler */ - fprintf(binding->outfile, - "\t\t%s,\n" - "\t\tJSAPI_PROP_TINYID_%s,\n" - "\t\t", - ident, ident); + fprintf(binding->outfile, + "\tJSAPI_PS_%s(%s, %s, 0),\n", + ro?"RO":"RW", + inf->name, + ident); break; case GENBIND_TYPE_TYPE_UNSHARED: /* unshared property with a type handler */ - fprintf(binding->outfile, - "\t\t%s,\n" - "\t\tJSAPI_PROP_TINYID_%s,\n" - "\t\t", - type, ident); + fprintf(binding->outfile, + "\tJSAPI_PS_ID_%s(%s, %s, 0, %s),\n", + ro?"RO":"RW", + inf->name, + ident, + type); break; } - fprintf(binding->outfile, "JSPROP_ENUMERATE),\n"); return 0; } -/* callback to emit implements property spec */ -static int webidl_property_spec_implements_cb(struct webidl_node *node, void *ctx) -{ - struct binding *binding = ctx; - - return generate_property_spec(binding, webidl_node_gettext(node)); -} - -static int -generate_property_spec(struct binding *binding, const char *interface) -{ - struct webidl_node *interface_node; - struct webidl_node *members_node; - struct webidl_node *inherit_node; - int res = 0; - - /* find interface in webidl with correct ident attached */ - interface_node = webidl_node_find_type_ident(binding->wi_ast, - WEBIDL_NODE_TYPE_INTERFACE, - interface); - - if (interface_node == NULL) { - fprintf(stderr, - "Unable to find interface %s in loaded WebIDL\n", - interface); - return -1; - } - - /* generate property entries for each list (partial interfaces) */ - members_node = webidl_node_find_type(webidl_node_getnode(interface_node), - NULL, - WEBIDL_NODE_TYPE_LIST); - - while (members_node != NULL) { - - fprintf(binding->outfile,"\t/**** %s ****/\n", interface); - - /* for each property emit a JSAPI_PS() */ - webidl_node_for_each_type(webidl_node_getnode(members_node), - WEBIDL_NODE_TYPE_ATTRIBUTE, - webidl_property_spec_cb, - binding); - - members_node = webidl_node_find_type(webidl_node_getnode(interface_node), - members_node, - WEBIDL_NODE_TYPE_LIST); - } - - /* check for inherited nodes and insert them too */ - inherit_node = webidl_node_find(webidl_node_getnode(interface_node), - NULL, - webidl_cmp_node_type, - (void *)WEBIDL_NODE_TYPE_INTERFACE_INHERITANCE); - - if (inherit_node != NULL) { - res = generate_property_spec(binding, - webidl_node_gettext(inherit_node)); - } - - if (res == 0) { - res = webidl_node_for_each_type(webidl_node_getnode(interface_node), - WEBIDL_NODE_TYPE_INTERFACE_IMPLEMENTS, - webidl_property_spec_implements_cb, - binding); - } - - return res; -} - - - /* exported interface documented in jsapi-libdom.h */ int output_property_spec(struct binding *binding) { + int inf; int res; + struct webidl_node *list_node; + struct webidl_node *op_node; /* operation on list node */ + + /* for each interface in the map */ + for (inf = 0; inf < binding->interfacec; inf++) { + if (binding->interfaces[inf].own_properties == 0) { + /* if the interface has no properties then + * there is nothing to generate. + */ + continue; + } + + /* generate property specifier */ + fprintf(binding->outfile, + "static JSPropertySpec JSClass_%s_properties[] = {\n", + binding->interfaces[inf].name); + + /* iterate each list within an interface */ + list_node = webidl_node_find_type( + webidl_node_getnode(binding->interfaces[inf].widl_node), + NULL, + WEBIDL_NODE_TYPE_LIST); + + while (list_node != NULL) { + /* iterate through operations in a list */ + op_node = webidl_node_find_type( + webidl_node_getnode(list_node), + NULL, + WEBIDL_NODE_TYPE_ATTRIBUTE); + + while (op_node != NULL) { + res = webidl_property_spec( + binding, + &binding->interfaces[inf], + op_node); + if (res != 0) { + return res; + } + + op_node = webidl_node_find_type( + webidl_node_getnode(list_node), + op_node, + WEBIDL_NODE_TYPE_ATTRIBUTE); + } - fprintf(binding->outfile, - "static JSPropertySpec jsclass_properties[] = {\n"); - res = generate_property_spec(binding, binding->interface); + list_node = webidl_node_find_type( + webidl_node_getnode(binding->interfaces[inf].widl_node), + list_node, + WEBIDL_NODE_TYPE_LIST); + } - fprintf(binding->outfile, "\tJSAPI_PS_END\n};\n\n"); + fprintf(binding->outfile, "\tJSAPI_PS_END\n};\n\n"); - return res; + } + return 0; } @@ -760,6 +737,7 @@ static int output_return_declaration(struct binding *binding, static int output_property_placeholder(struct binding *binding, + struct binding_interface *inf, struct webidl_node* oplist, const char *ident) { @@ -767,34 +745,36 @@ output_property_placeholder(struct binding *binding, WARN(WARNING_UNIMPLEMENTED, "property %s.%s has no implementation\n", - binding->interface, + inf->name, ident); if (options->dbglog) { fprintf(binding->outfile, "\tJSLOG(\"property %s.%s has no implementation\");\n", - binding->interface, + inf->name, ident); } return 0; } static int output_property_getter(struct binding *binding, + struct binding_interface *inf, struct webidl_node *node, const char *ident) { struct genbind_node *property_node; fprintf(binding->outfile, - "static JSBool JSAPI_PROP(%s_get, JSContext *cx, JSObject *obj, jsval *vp)\n" + "static JSBool JSAPI_PROP_GET(%s, %s, JSContext *cx, JSObject *obj, jsval *vp)\n" "{\n", + inf->name, ident); /* return value declaration */ output_return_declaration(binding, "jsret", node); - output_private_get(binding, "private"); + output_private_get(binding, inf, "private"); property_node = genbind_node_find_type_ident(binding->gb_ast, NULL, @@ -827,7 +807,7 @@ static int output_property_getter(struct binding *binding, "\tjsret = private->%s;\n", ident); } else { - output_property_placeholder(binding, node, ident); + output_property_placeholder(binding, inf, node, ident); } } @@ -842,8 +822,9 @@ static int output_property_getter(struct binding *binding, } static int output_property_setter(struct binding *binding, + struct binding_interface *inf, struct webidl_node *node, - const char *ident) + const char *property_ident) { struct genbind_node *property_node; char *putforwards; @@ -853,22 +834,23 @@ static int output_property_setter(struct binding *binding, /* generate a putforwards setter */ fprintf(binding->outfile, "/* PutForwards setter */\n" - "static JSBool JSAPI_STRICTPROP(%s_set, JSContext *cx, JSObject *obj, jsval *vp)\n" + "static JSBool JSAPI_PROP_SET(%s, %s, JSContext *cx, JSObject *obj, jsval *vp)\n" "{\n", - ident); + inf->name, + property_ident); fprintf(binding->outfile, "\tjsval propval;\n" "\tif (JS_GetProperty(cx, obj, \"%s\", &propval) == JS_TRUE) {\n" "\t\tJS_SetProperty(cx, JSVAL_TO_OBJECT(propval), \"%s\", vp);\n" "\t}\n", - ident, putforwards); + property_ident, + putforwards); fprintf(binding->outfile, "\treturn JS_FALSE; /* disallow the asignment */\n" "}\n\n"); - return 0; } @@ -880,20 +862,21 @@ static int output_property_setter(struct binding *binding, property_node = genbind_node_find_type_ident(binding->gb_ast, NULL, GENBIND_NODE_TYPE_SETTER, - ident); + property_ident); fprintf(binding->outfile, - "static JSBool JSAPI_STRICTPROP(%s_set, JSContext *cx, JSObject *obj, jsval *vp)\n" + "static JSBool JSAPI_PROP_SET(%s, %s, JSContext *cx, JSObject *obj, jsval *vp)\n" "{\n", - ident); + inf->name, + property_ident); - output_private_get(binding, "private"); + output_private_get(binding, inf, "private"); if (property_node != NULL) { /* binding source block */ output_code_block(binding, genbind_node_getnode(property_node)); } else { - output_property_placeholder(binding, node, ident); + output_property_placeholder(binding, inf, node, property_ident); } fprintf(binding->outfile, @@ -904,9 +887,13 @@ static int output_property_setter(struct binding *binding, return 0; } -static int webidl_property_body_cb(struct webidl_node *node, void *ctx) +/** + * generate atribute (property) body. + */ +static int webidl_property_body(struct binding *binding, + struct binding_interface *inf, + struct webidl_node *node) { - struct binding *binding = ctx; struct webidl_node *ident_node; const char *ident; struct webidl_node *type_node; @@ -941,93 +928,23 @@ static int webidl_property_body_cb(struct webidl_node *node, void *ctx) * type handler */ if ((shared_mod & GENBIND_TYPE_TYPE) == 0) { - ret = output_property_setter(binding, node, ident); + ret = output_property_setter(binding, inf, node, ident); if (ret == 0) { /* property getter */ - ret = output_property_getter(binding, node, ident); + ret = output_property_getter(binding, inf, node, ident); } } return ret; -} - - -/* callback to emit implements property bodys */ -static int webidl_implements_cb(struct webidl_node *node, void *ctx) -{ - struct binding *binding = ctx; - - return generate_property_body(binding, webidl_node_gettext(node)); } -static int -generate_property_body(struct binding *binding, const char *interface) -{ - struct webidl_node *interface_node; - struct webidl_node *members_node; - struct webidl_node *inherit_node; - int res = 0; - - /* find interface in webidl with correct ident attached */ - interface_node = webidl_node_find_type_ident(binding->wi_ast, - WEBIDL_NODE_TYPE_INTERFACE, - interface); - - if (interface_node == NULL) { - fprintf(stderr, - "Unable to find interface %s in loaded WebIDL\n", - interface); - return -1; - } - - /* generate property bodies */ - members_node = webidl_node_find_type(webidl_node_getnode(interface_node), - NULL, - WEBIDL_NODE_TYPE_LIST); - while (members_node != NULL) { - - fprintf(binding->outfile,"/**** %s ****/\n", interface); - - /* emit property body */ - webidl_node_for_each_type(webidl_node_getnode(members_node), - WEBIDL_NODE_TYPE_ATTRIBUTE, - webidl_property_body_cb, - binding); - - - members_node = webidl_node_find_type(webidl_node_getnode(interface_node), - members_node, - WEBIDL_NODE_TYPE_LIST); - - } - - /* check for inherited nodes and insert them too */ - inherit_node = webidl_node_find_type(webidl_node_getnode(interface_node), - NULL, - WEBIDL_NODE_TYPE_INTERFACE_INHERITANCE); - - if (inherit_node != NULL) { - res = generate_property_body(binding, - webidl_node_gettext(inherit_node)); - } - - if (res == 0) { - res = webidl_node_for_each_type(webidl_node_getnode(interface_node), - WEBIDL_NODE_TYPE_INTERFACE_IMPLEMENTS, - webidl_implements_cb, - binding); - } - - return res; -} - - /* setter for type handler */ -static int -output_property_type_setter(struct binding *binding, - struct genbind_node *node, +static int +output_property_type_setter(struct binding *binding, + struct binding_interface *inf, + struct genbind_node *node, const char *type) { struct genbind_node *property_node; @@ -1042,7 +959,7 @@ output_property_type_setter(struct binding *binding, /* property name vars */ output_property_tinyid_get_vars(binding, "tinyid"); /* context data */ - output_private_get(binding, "private"); + output_private_get(binding, inf, "private"); /* property name */ output_property_tinyid_get(binding, "tinyid"); @@ -1066,7 +983,10 @@ output_property_type_setter(struct binding *binding, /* getter for type handlers */ -static int output_property_type_getter(struct binding *binding, struct genbind_node *node, const char *type) +static int output_property_type_getter(struct binding *binding, + struct binding_interface *inf, + struct genbind_node *node, + const char *type) { struct genbind_node *property_node; node = node;/* currently unused */ @@ -1079,7 +999,7 @@ static int output_property_type_getter(struct binding *binding, struct genbind_n /* property tinyid vars */ output_property_tinyid_get_vars(binding, "tinyid"); /* context data */ - output_private_get(binding, "private"); + output_private_get(binding, inf, "private"); /* property tinyid */ output_property_tinyid_get(binding, "tinyid"); @@ -1108,14 +1028,17 @@ static int typehandler_property_cb(struct genbind_node *node, void *ctx) struct genbind_node *ident_node; const char *type; struct genbind_node *mod_node; - enum genbind_type_modifier share_mod; + enum genbind_type_modifier *share_mod; int ret = 0; + struct binding_interface *inf; mod_node = genbind_node_find_type(genbind_node_getnode(node), NULL, GENBIND_NODE_TYPE_MODIFIER); - share_mod = genbind_node_getint(mod_node); - if ((share_mod & GENBIND_TYPE_TYPE) == GENBIND_TYPE_TYPE) { + share_mod = (enum genbind_type_modifier *)genbind_node_getint(mod_node); + + if ((share_mod != NULL) && + ((*share_mod & GENBIND_TYPE_TYPE) == GENBIND_TYPE_TYPE)) { /* type handler */ ident_node = genbind_node_find_type(genbind_node_getnode(node), @@ -1123,10 +1046,14 @@ static int typehandler_property_cb(struct genbind_node *node, void *ctx) GENBIND_NODE_TYPE_IDENT); type = genbind_node_gettext(ident_node); if (type != NULL) { - ret = output_property_type_setter(binding, node, type); + ret = output_property_type_setter(binding, + inf, + node, + type); if (ret == 0) { /* property getter */ ret = output_property_type_getter(binding, + inf, node, type); } @@ -1135,20 +1062,95 @@ static int typehandler_property_cb(struct genbind_node *node, void *ctx) return ret; } -/* exported interface documented in jsapi-libdom.h */ -int -output_property_body(struct binding *binding) + +/** + * generate atribute (property) type body. + * + * Output property handler for an entire type of property + */ +static int webidl_attribute_typehandler_body(struct binding *binding, + struct binding_interface *inf) { int res; - res = generate_property_body(binding, binding->interface); + /* check if the interface has any properties with type handlers */ + if (!inf->has_type_properties) { + return 0; + } - if (res == 0) { - res = genbind_node_for_each_type(binding->binding_list, + res = genbind_node_foreach_type(binding->binding_list, GENBIND_NODE_TYPE_BINDING_PROPERTY, typehandler_property_cb, binding); - } + return res; + +} + +/* exported interface documented in jsapi-libdom.h */ +int +output_property_body(struct binding *binding) +{ + int inf; + int res; + struct webidl_node *list_node; + struct webidl_node *op_node; /* operation on list node */ + + /* for each interface in the map */ + for (inf = 0; inf < binding->interfacec; inf++) { + if (binding->interfaces[inf].own_properties == 0) { + /* if the interface has no properties then + * there is nothing to generate. + */ + continue; + } + + /* generate property bodies */ + + /* iterate each list within an interface */ + list_node = webidl_node_find_type( + webidl_node_getnode(binding->interfaces[inf].widl_node), + NULL, + WEBIDL_NODE_TYPE_LIST); + + while (list_node != NULL) { + /* iterate through operations in a list */ + op_node = webidl_node_find_type( + webidl_node_getnode(list_node), + NULL, + WEBIDL_NODE_TYPE_ATTRIBUTE); + + while (op_node != NULL) { + res = webidl_property_body( + binding, + &binding->interfaces[inf], + op_node); + if (res != 0) { + return res; + } + + op_node = webidl_node_find_type( + webidl_node_getnode(list_node), + op_node, + WEBIDL_NODE_TYPE_ATTRIBUTE); + } + + + list_node = webidl_node_find_type( + webidl_node_getnode(binding->interfaces[inf].widl_node), + list_node, + WEBIDL_NODE_TYPE_LIST); + } + + /* generate type handler bodies */ + res = webidl_attribute_typehandler_body(binding, + &binding->interfaces[inf]); + + if (res != 0) { + return res; + } + + } + return 0; } diff --git a/src/jsapi-libdom.c b/src/jsapi-libdom.c index 7104a83..5e994b9 100644 --- a/src/jsapi-libdom.c +++ b/src/jsapi-libdom.c @@ -25,11 +25,6 @@ " * nsgenbind is similar to a compiler is a purely transformative tool which\n" \ " * explicitly makes no copyright claim on this generated output" -#define HDROUTF(bndg, fmt, args...) do { \ - if (bndg->hdrfile != NULL) { \ - fprintf(bndg->hdrfile, fmt, ##args); \ - } \ - } while(0) static int webidl_file_cb(struct genbind_node *node, void *ctx) @@ -47,11 +42,15 @@ read_webidl(struct genbind_node *genbind_ast, struct webidl_node **webidl_ast) { int res; - res = genbind_node_for_each_type(genbind_ast, + res = genbind_node_foreach_type(genbind_ast, GENBIND_NODE_TYPE_WEBIDLFILE, webidl_file_cb, webidl_ast); + if (res == 0) { + res = webidl_intercalate_implements(*webidl_ast); + } + /* debug dump of web idl AST */ if (options->verbose) { webidl_ast_dump(*webidl_ast, 0); @@ -101,7 +100,7 @@ static int webidl_hdrcomments_cb(struct genbind_node *node, void *ctx) static int webidl_hdrcomment_cb(struct genbind_node *node, void *ctx) { - genbind_node_for_each_type(genbind_node_getnode(node), + genbind_node_foreach_type(genbind_node_getnode(node), GENBIND_NODE_TYPE_STRING, webidl_hdrcomments_cb, ctx); @@ -135,51 +134,7 @@ static int webidl_private_cb(struct genbind_node *node, void *ctx) return 0; } -static int webidl_private_param_cb(struct genbind_node *node, void *ctx) -{ - struct binding *binding = ctx; - struct genbind_node *ident_node; - struct genbind_node *type_node; - - - ident_node = genbind_node_find_type(genbind_node_getnode(node), - NULL, - GENBIND_NODE_TYPE_IDENT); - if (ident_node == NULL) - return -1; /* bad AST */ - - type_node = genbind_node_find_type(genbind_node_getnode(node), - NULL, - GENBIND_NODE_TYPE_STRING); - if (type_node == NULL) - return -1; /* bad AST */ - - fprintf(binding->outfile, - ",\n\t\t%s%s", - genbind_node_gettext(type_node), - genbind_node_gettext(ident_node)); - - return 0; -} - -static int webidl_private_assign_cb(struct genbind_node *node, void *ctx) -{ - struct binding *binding = ctx; - struct genbind_node *ident_node; - const char *ident; - - ident_node = genbind_node_find_type(genbind_node_getnode(node), - NULL, - GENBIND_NODE_TYPE_IDENT); - if (ident_node == NULL) - return -1; /* bad AST */ - - ident = genbind_node_gettext(ident_node); - fprintf(binding->outfile, "\tprivate->%s = %s;\n", ident, ident); - - return 0; -} /* section output generators */ @@ -187,7 +142,7 @@ static int webidl_private_assign_cb(struct genbind_node *node, void *ctx) static int output_epilogue(struct binding *binding) { - genbind_node_for_each_type(binding->gb_ast, + genbind_node_foreach_type(binding->gb_ast, GENBIND_NODE_TYPE_EPILOGUE, webidl_epilogue_cb, binding); @@ -211,7 +166,11 @@ output_epilogue(struct binding *binding) static int output_prologue(struct binding *binding) { - genbind_node_for_each_type(binding->gb_ast, + /* forward declare property list */ + fprintf(binding->outfile, + "static JSPropertySpec jsclass_properties[];\n\n"); + + genbind_node_foreach_type(binding->gb_ast, GENBIND_NODE_TYPE_PROLOGUE, webidl_prologue_cb, binding); @@ -285,7 +244,7 @@ output_api_operations(struct binding *binding) "\n" "\tprivate = JS_GetInstancePrivate(cx, obj, &JSClass_%s, NULL);\n", binding->interface); - + if (options->dbglog) { fprintf(binding->outfile, "\tJSLOG(\"jscontext:%%p jsobject:%%p private:%%p\", cx, obj, private);\n"); @@ -320,7 +279,7 @@ output_api_operations(struct binding *binding) "\n" "\tprivate = JS_GetInstancePrivate(cx, obj, &JSClass_%s, NULL);\n", binding->interface); - + if (options->dbglog) { fprintf(binding->outfile, "\tJSLOG(\"jscontext:%%p jsobject:%%p private:%%p\", cx, obj, private);\n"); @@ -355,7 +314,7 @@ output_api_operations(struct binding *binding) "\n" "\tprivate = JS_GetInstancePrivate(cx, obj, &JSClass_%s, NULL);\n", binding->interface); - + if (options->dbglog) { fprintf(binding->outfile, "\tJSLOG(\"jscontext:%%p jsobject:%%p private:%%p\", cx, obj, private);\n"); @@ -390,7 +349,7 @@ output_api_operations(struct binding *binding) "\n" "\tprivate = JS_GetInstancePrivate(cx, obj, &JSClass_%s, NULL);\n", binding->interface); - + if (options->dbglog) { fprintf(binding->outfile, "\tJSLOG(\"jscontext:%%p jsobject:%%p private:%%p\", cx, obj, private);\n"); @@ -527,208 +486,10 @@ output_code_block(struct binding *binding, struct genbind_node *codelist) } } -/** generate class initialiser which create the javascript class prototype */ -static int -output_class_init(struct binding *binding) -{ - int res = 0; - struct genbind_node *api_node; - - /* class Initialisor declaration */ - if (binding->hdrfile) { - binding->outfile = binding->hdrfile; - - fprintf(binding->outfile, - "JSObject *jsapi_InitClass_%s(JSContext *cx, JSObject *parent);\n", - binding->interface); - - binding->outfile = binding->srcfile; - } - - /* class Initialisor definition */ - fprintf(binding->outfile, - "JSObject *jsapi_InitClass_%s(JSContext *cx, JSObject *parent)\n" - "{\n" - "\tJSObject *prototype;\n", - binding->interface); - - api_node = genbind_node_find_type_ident(binding->gb_ast, - NULL, - GENBIND_NODE_TYPE_API, - "init"); - - if (api_node != NULL) { - output_code_block(binding, genbind_node_getnode(api_node)); - } else { - fprintf(binding->outfile, - "\n" - "\tprototype = JS_InitClass(cx,\n" - "\t\tparent,\n" - "\t\tNULL,\n" - "\t\t&JSClass_%s,\n" - "\t\tNULL,\n" - "\t\t0,\n" - "\t\tNULL,\n" - "\t\tNULL, \n" - "\t\tNULL, \n" - "\t\tNULL);\n", - binding->interface); - } - - output_const_defines(binding, binding->interface); - - fprintf(binding->outfile, - "\treturn prototype;\n" - "}\n\n"); - - return res; -} - -static int -output_class_new(struct binding *binding) -{ - int res = 0; - struct genbind_node *api_node; - - /* constructor declaration */ - if (binding->hdrfile) { - binding->outfile = binding->hdrfile; - - fprintf(binding->outfile, - "JSObject *jsapi_new_%s(JSContext *cx,\n" - "\t\tJSObject *prototype,\n" - "\t\tJSObject *parent", - binding->interface); - - genbind_node_for_each_type(binding->binding_list, - GENBIND_NODE_TYPE_BINDING_PRIVATE, - webidl_private_param_cb, - binding); - - fprintf(binding->outfile, ");"); - - binding->outfile = binding->srcfile; - } - - /* constructor definition */ - fprintf(binding->outfile, - "JSObject *jsapi_new_%s(JSContext *cx,\n" - "\t\tJSObject *prototype,\n" - "\t\tJSObject *parent", - binding->interface); - - genbind_node_for_each_type(binding->binding_list, - GENBIND_NODE_TYPE_BINDING_PRIVATE, - webidl_private_param_cb, - binding); - - fprintf(binding->outfile, - ")\n" - "{\n" - "\tJSObject *newobject;\n"); - - /* create private data */ - if (binding->has_private) { - fprintf(binding->outfile, - "\tstruct jsclass_private *private;\n" - "\n" - "\tprivate = malloc(sizeof(struct jsclass_private));\n" - "\tif (private == NULL) {\n" - "\t\treturn NULL;\n" - "\t}\n"); - - genbind_node_for_each_type(binding->binding_list, - GENBIND_NODE_TYPE_BINDING_PRIVATE, - webidl_private_assign_cb, - binding); - } - - api_node = genbind_node_find_type_ident(binding->gb_ast, - NULL, - GENBIND_NODE_TYPE_API, - "new"); - - if (api_node != NULL) { - output_code_block(binding, genbind_node_getnode(api_node)); - } else { - fprintf(binding->outfile, - "\n" - "\tnewobject = JS_NewObject(cx, &JSClass_%s, prototype, parent);\n", - binding->interface); - } - - if (binding->has_private) { - fprintf(binding->outfile, - "\tif (newobject == NULL) {\n" - "\t\tfree(private);\n" - "\t\treturn NULL;\n" - "\t}\n\n"); - - /* root object to stop it being garbage collected */ - fprintf(binding->outfile, - "\tif (JSAPI_ADD_OBJECT_ROOT(cx, &newobject) != JS_TRUE) {\n" - "\t\tfree(private);\n" - "\t\treturn NULL;\n" - "\t}\n\n"); - - fprintf(binding->outfile, - "\n" - "\t/* attach private pointer */\n" - "\tif (JS_SetPrivate(cx, newobject, private) != JS_TRUE) {\n" - "\t\tfree(private);\n" - "\t\treturn NULL;\n" - "\t}\n\n"); - - - /* attach operations and attributes (functions and properties) */ - fprintf(binding->outfile, - "\tif (JS_DefineFunctions(cx, newobject, jsclass_functions) != JS_TRUE) {\n" - "\t\tfree(private);\n" - "\t\treturn NULL;\n" - "\t}\n\n"); - - fprintf(binding->outfile, - "\tif (JS_DefineProperties(cx, newobject, jsclass_properties) != JS_TRUE) {\n" - "\t\tfree(private);\n" - "\t\treturn NULL;\n" - "\t}\n\n"); - } else { - fprintf(binding->outfile, - "\tif (newobject == NULL) {\n" - "\t\treturn NULL;\n" - "\t}\n"); - - /* root object to stop it being garbage collected */ - fprintf(binding->outfile, - "\tif (JSAPI_ADD_OBJECT_ROOT(cx, &newobject) != JS_TRUE) {\n" - "\t\treturn NULL;\n" - "\t}\n\n"); - - /* attach operations and attributes (functions and properties) */ - fprintf(binding->outfile, - "\tif (JS_DefineFunctions(cx, newobject, jsclass_functions) != JS_TRUE) {\n" - "\t\treturn NULL;\n" - "\t}\n\n"); - fprintf(binding->outfile, - "\tif (JS_DefineProperties(cx, newobject, jsclass_properties) != JS_TRUE) {\n" - "\t\treturn NULL;\n" - "\t}\n\n"); - } - - /* unroot object and return it */ - fprintf(binding->outfile, - "\tJSAPI_REMOVE_OBJECT_ROOT(cx, &newobject);\n" - "\n" - "\treturn newobject;\n" - "}\n"); - - - return res; -} static int -output_jsclass(struct binding *binding) +output_forward_declarations(struct binding *binding) { /* forward declare add property */ if (binding->addproperty != NULL) { @@ -778,147 +539,40 @@ output_jsclass(struct binding *binding) "static void jsclass_finalize(JSContext *cx, JSObject *obj);\n\n"); } - /* forward declare property list */ - fprintf(binding->outfile, - "static JSPropertySpec jsclass_properties[];\n\n"); - - /* output the class declaration */ - HDROUTF(binding, "JSClass JSClass_%s;\n", binding->interface); - - /* output the class definition */ - fprintf(binding->outfile, - "JSClass JSClass_%s = {\n" - "\t\"%s\",\n", - binding->interface, - binding->interface); - - /* generate class flags */ - if (binding->has_global) { - fprintf(binding->outfile, "\tJSCLASS_GLOBAL_FLAGS"); - } else { - fprintf(binding->outfile, "\t0"); - } - - if (binding->resolve != NULL) { - fprintf(binding->outfile, " | JSCLASS_NEW_RESOLVE"); - } - - if (binding->mark != NULL) { - fprintf(binding->outfile, " | JSAPI_JSCLASS_MARK_IS_TRACE"); - } - - if (binding->has_private) { - fprintf(binding->outfile, " | JSCLASS_HAS_PRIVATE"); - } - - fprintf(binding->outfile, ",\n"); - - /* add property */ - if (binding->addproperty != NULL) { - fprintf(binding->outfile, - "\tjsapi_property_add,\t/* addProperty */\n"); - } else { - fprintf(binding->outfile, - "\tJS_PropertyStub,\t/* addProperty */\n"); - } - - /* del property */ - if (binding->delproperty != NULL) { - fprintf(binding->outfile, - "\tjsapi_property_del,\t/* delProperty */\n"); - } else { - fprintf(binding->outfile, - "\tJS_PropertyStub,\t/* delProperty */\n"); - } - - /* get property */ - if (binding->getproperty != NULL) { - fprintf(binding->outfile, - "\tjsapi_property_get,\t/* getProperty */\n"); - } else { - fprintf(binding->outfile, - "\tJS_PropertyStub,\t/* getProperty */\n"); - } - - /* set property */ - if (binding->setproperty != NULL) { - fprintf(binding->outfile, - "\tjsapi_property_set,\t/* setProperty */\n"); - } else { - fprintf(binding->outfile, - "\tJS_StrictPropertyStub,\t/* setProperty */\n"); - } - - /* enumerate */ - if (binding->enumerate != NULL) { - fprintf(binding->outfile, - "\tjsclass_enumerate,\t/* enumerate */\n"); - } else { - fprintf(binding->outfile, - "\tJS_EnumerateStub,\t/* enumerate */\n"); - } - - /* resolver */ - if (binding->resolve != NULL) { - fprintf(binding->outfile, "\t(JSResolveOp)jsclass_resolve,\n"); - } else { - fprintf(binding->outfile, "\tJS_ResolveStub,\n"); - } - - fprintf(binding->outfile, "\tJS_ConvertStub,\t/* convert */\n"); - - if (binding->has_private || (binding->finalise != NULL)) { - fprintf(binding->outfile, "\tjsclass_finalize,\n"); - } else { - fprintf(binding->outfile, "\tJS_FinalizeStub,\n"); - } - fprintf(binding->outfile, - "\t0,\t/* reserved */\n" - "\tNULL,\t/* checkAccess */\n" - "\tNULL,\t/* call */\n" - "\tNULL,\t/* construct */\n" - "\tNULL,\t/* xdr Object */\n" - "\tNULL,\t/* hasInstance */\n"); - - /* trace/mark */ - if (binding->mark != NULL) { - fprintf(binding->outfile, "\tJSAPI_JSCLASS_MARKOP(jsclass_mark),\n"); - } else { - fprintf(binding->outfile, "\tNULL, /* trace/mark */\n"); - } - - fprintf(binding->outfile, - "\tJSAPI_CLASS_NO_INTERNAL_MEMBERS\n" - "};\n\n"); return 0; } + + +/** generate structure definition for internal class data + * + * Every javascript object instance has an internal context to keep + * track of its state in object terms this would be considered the + * "this" pointer giving access to the classes members. + * + * Member access can be considered protected as all interfaces + * (classes) and subclasses generated within this binding have access + * to members. + * + * @param binding the binding to generate the structure for. + * @return 0 on success with output written and -1 on error. + */ static int output_private_declaration(struct binding *binding) { - struct genbind_node *type_node; if (!binding->has_private) { return 0; } - type_node = genbind_node_find(binding->binding_list, - NULL, - genbind_cmp_node_type, - (void *)GENBIND_NODE_TYPE_TYPE); - - if (type_node == NULL) { - return -1; - } - fprintf(binding->outfile, "struct jsclass_private {\n"); - genbind_node_for_each_type(binding->binding_list, + genbind_node_foreach_type(binding->binding_list, GENBIND_NODE_TYPE_BINDING_PRIVATE, webidl_private_cb, binding); - genbind_node_for_each_type(binding->binding_list, + genbind_node_foreach_type(binding->binding_list, GENBIND_NODE_TYPE_BINDING_INTERNAL, webidl_private_cb, binding); @@ -932,7 +586,7 @@ output_private_declaration(struct binding *binding) static int output_preamble(struct binding *binding) { - genbind_node_for_each_type(binding->gb_ast, + genbind_node_foreach_type(binding->gb_ast, GENBIND_NODE_TYPE_PREAMBLE, webidl_preamble_cb, binding); @@ -962,7 +616,7 @@ output_header_comments(struct binding *binding) const char *preamble = HDR_COMMENT_PREAMBLE; fprintf(binding->outfile, preamble, options->infilename); - genbind_node_for_each_type(binding->gb_ast, + genbind_node_foreach_type(binding->gb_ast, GENBIND_NODE_TYPE_HDRCOMMENT, webidl_hdrcomment_cb, binding); @@ -974,7 +628,7 @@ output_header_comments(struct binding *binding) fprintf(binding->outfile, preamble, options->infilename); - genbind_node_for_each_type(binding->gb_ast, + genbind_node_foreach_type(binding->gb_ast, GENBIND_NODE_TYPE_HDRCOMMENT, webidl_hdrcomment_cb, binding); @@ -1008,120 +662,100 @@ binding_has_private(struct genbind_node *binding_list) return false; } -static bool -binding_has_global(struct binding *binding) -{ - struct genbind_node *api_node; - api_node = genbind_node_find_type_ident(binding->gb_ast, - NULL, - GENBIND_NODE_TYPE_API, - "global"); - if (api_node != NULL) { - return true; - } - return false; + +/** obtain the name to use for the binding. + * + * @todo it should be possible to specify the binding name instead of + * just using the name of the first interface. + */ +static const char *get_binding_name(struct genbind_node *binding_node) +{ + return genbind_node_gettext( + genbind_node_find_type( + genbind_node_getnode( + genbind_node_find_type( + genbind_node_getnode(binding_node), + NULL, + GENBIND_NODE_TYPE_BINDING_INTERFACE)), + NULL, + GENBIND_NODE_TYPE_IDENT)); } static struct binding * -binding_new(char *outfilename, - char *hdrfilename, - struct genbind_node *genbind_ast) +binding_new(struct options *options, + struct genbind_node *genbind_ast, + struct genbind_node *binding_node) { struct binding *nb; - struct genbind_node *binding_node; + + int interfacec; /* numer of interfaces in the interface map */ + struct binding_interface *interfaces; /* binding interface map */ + struct genbind_node *binding_list; - struct genbind_node *ident_node; - struct genbind_node *interface_node; - FILE *outfile = NULL; /* output source file */ - FILE *hdrfile = NULL; /* output header file */ char *hdrguard = NULL; struct webidl_node *webidl_ast = NULL; int res; - binding_node = genbind_node_find_type(genbind_ast, - NULL, - GENBIND_NODE_TYPE_BINDING); - if (binding_node == NULL) { - return NULL; - } - - binding_list = genbind_node_getnode(binding_node); - if (binding_list == NULL) { - return NULL; - } - - ident_node = genbind_node_find_type(binding_list, - NULL, - GENBIND_NODE_TYPE_IDENT); - if (ident_node == NULL) { - return NULL; - } - - interface_node = genbind_node_find_type(binding_list, - NULL, - GENBIND_NODE_TYPE_BINDING_INTERFACE); - if (interface_node == NULL) { - return NULL; - } - - /* walk ast and load any web IDL files required */ + /* walk AST and load any web IDL files required */ res = read_webidl(genbind_ast, &webidl_ast); if (res != 0) { - fprintf(stderr, "Error reading Web IDL files\n"); + fprintf(stderr, "Error: failed reading Web IDL\n"); return NULL; } - /* open output file */ - if (outfilename == NULL) { - outfile = stdout; - } else { - outfile = fopen(outfilename, "w"); - } - if (outfile == NULL) { - fprintf(stderr, "Error opening source output %s: %s\n", - outfilename, - strerror(errno)); + /* build the bindings interface (class) name map */ + if (build_interface_map(binding_node, + webidl_ast, + &interfacec, + &interfaces) != 0) { + /* the binding must have at least one interface */ + fprintf(stderr, "Error: Binding must have a valid interface\n"); return NULL; } - /* output header file if required */ - if (hdrfilename != NULL) { + /* header guard */ + if (options->hdrfilename != NULL) { int guardlen; int pos; - hdrfile = fopen(hdrfilename, "w"); - if (hdrfile == NULL) { - fprintf(stderr, "Error opening header output %s: %s\n", - hdrfilename, - strerror(errno)); - fclose(outfile); - return NULL; - } - guardlen = strlen(hdrfilename); + guardlen = strlen(options->hdrfilename); hdrguard = calloc(1, guardlen + 1); for (pos = 0; pos < guardlen; pos++) { - if (isalpha(hdrfilename[pos])) { - hdrguard[pos] = toupper(hdrfilename[pos]); + if (isalpha(options->hdrfilename[pos])) { + hdrguard[pos] = toupper(options->hdrfilename[pos]); } else { hdrguard[pos] = '_'; } } } + binding_list = genbind_node_getnode(binding_node); + if (binding_list == NULL) { + return NULL; + } + nb = calloc(1, sizeof(struct binding)); nb->gb_ast = genbind_ast; nb->wi_ast = webidl_ast; - nb->name = genbind_node_gettext(ident_node); - nb->interface = genbind_node_gettext(interface_node); - nb->outfile = outfile; - nb->srcfile = outfile; - nb->hdrfile = hdrfile; + + /* keep the binding list node */ + nb->binding_list = binding_list; + + /* store the interface mapping */ + nb->interfaces = interfaces; + nb->interfacec = interfacec; + + /* @todo get rid of the interface element out of the binding + * struct and use the interface map instead. + */ + nb->name = nb->interface = get_binding_name(binding_node); + nb->outfile = options->outfilehandle; + nb->srcfile = options->outfilehandle; + nb->hdrfile = options->hdrfilehandle; nb->hdrguard = hdrguard; nb->has_private = binding_has_private(binding_list); - nb->has_global = binding_has_global(nb); - nb->binding_list = binding_list; /* class API */ nb->addproperty = genbind_node_find_type_ident(genbind_ast, @@ -1145,14 +779,14 @@ binding_new(char *outfilename, "setproperty"); nb->enumerate = genbind_node_find_type_ident(genbind_ast, - NULL, - GENBIND_NODE_TYPE_API, - "enumerate"); + NULL, + GENBIND_NODE_TYPE_API, + "enumerate"); nb->resolve = genbind_node_find_type_ident(genbind_ast, - NULL, - GENBIND_NODE_TYPE_API, - "resolve"); + NULL, + GENBIND_NODE_TYPE_API, + "resolve"); nb->finalise = genbind_node_find_type_ident(genbind_ast, NULL, @@ -1160,23 +794,23 @@ binding_new(char *outfilename, "finalise"); nb->mark = genbind_node_find_type_ident(genbind_ast, - NULL, - GENBIND_NODE_TYPE_API, - "mark"); + NULL, + GENBIND_NODE_TYPE_API, + "mark"); return nb; } int -jsapi_libdom_output(char *outfilename, - char *hdrfilename, - struct genbind_node *genbind_ast) +jsapi_libdom_output(struct options *options, + struct genbind_node *genbind_ast, + struct genbind_node *binding_node) { int res; struct binding *binding; /* get general binding information used in output */ - binding = binding_new(outfilename, hdrfilename, genbind_ast); + binding = binding_new(options, genbind_ast, binding_node); if (binding == NULL) { return 40; } @@ -1197,7 +831,12 @@ jsapi_libdom_output(char *outfilename, return 70; } - res = output_jsclass(binding); + res = output_forward_declarations(binding); + if (res) { + return 75; + } + + res = output_jsclasses(binding); if (res) { return 80; } @@ -1207,15 +846,15 @@ jsapi_libdom_output(char *outfilename, return 85; } - /* user code outout just before function bodies emitted */ + /* user code output just before interface code bodies emitted */ res = output_prologue(binding); if (res) { return 89; } - /* operator and atrtribute body generation */ + /* method (function) and property body generation */ - res = output_operator_body(binding, binding->interface); + res = output_function_bodies(binding); if (res) { return 90; } @@ -1225,7 +864,7 @@ jsapi_libdom_output(char *outfilename, return 100; } - /* operator and atrtribute specifier generation */ + /* method (function) and property specifier generation */ res = output_function_spec(binding); if (res) { diff --git a/src/jsapi-libdom.h b/src/jsapi-libdom.h index 7db664d..e8d57c4 100644 --- a/src/jsapi-libdom.h +++ b/src/jsapi-libdom.h @@ -9,16 +9,42 @@ #ifndef nsgenbind_jsapi_libdom_h #define nsgenbind_jsapi_libdom_h +struct options; + +struct binding_interface { + const char *name; /**< name of interface */ + struct genbind_node *node; /**< node of interface in binding */ + struct webidl_node *widl_node; /**< node of interface in webidl */ + const char *inherit_name; /**< name of interface this inherits from */ + int own_properties; /**< the number of properties the interface has */ + int own_functions; /**< the number of functions the interface has */ + + bool has_type_properties; /**< some of the properties on the + * interface have a type handler + */ + + int inherit_idx; /**< index into binding map of inherited + * interface or -1 for not in map + */ + int refcount; /**< number of entries in map that refer to this + * interface + */ + int output_idx; /**< for interfaces that will be output (node + * is valid) this is the output array index + */ +}; + struct binding { struct genbind_node *gb_ast; /* root node of binding AST */ struct webidl_node *wi_ast; /* root node of webidl AST */ + const char *name; /* Name of binding (first interface name by default) */ + int interfacec; /* numer of interfaces in the interface map */ + struct binding_interface *interfaces; /* binding interface map */ - const char *name; /* name of the binding */ const char *interface; /* webidl interface binding is for */ bool has_private; /* true if the binding requires a private structure */ - bool has_global; /* true if the binding is for a global */ struct genbind_node *binding_list; /* node list of the binding */ struct genbind_node *addproperty; /* binding api add property node or NULL */ @@ -34,38 +60,60 @@ struct binding { FILE *outfile ; /* file handle output should be written to, * allows reuse of callback routines to output - * to headers and source files + * to headers and source files */ FILE *srcfile ; /* output source file */ FILE *hdrfile ; /* output header file */ }; /** Generate binding between jsapi and netsurf libdom */ -int jsapi_libdom_output(char *outfile, char *hdrfile, struct genbind_node *genbind_root); +int jsapi_libdom_output(struct options *options, struct genbind_node *genbind_ast, struct genbind_node *binding_node); + +/** Build interface map. + * + * Generate a map of all interfaces referenced from a binding and + * their relationships to each other. + * + * The map will contain all the interfaces both directly referenced by + * the binding and all those inherited through the WebIDL. + * + * The map is topoligicaly sorted to ensure no forward inheritance + * references. + * + * The map contains an monotinicaly incrementing index for all + * interfaces referenced in the binding (i.e. those to be exported). + */ +int build_interface_map(struct genbind_node *binding_node, + struct webidl_node *webidl_ast, + int *interfacec_out, + struct binding_interface **interfaces_out); + /** output code block from a node */ void output_code_block(struct binding *binding, struct genbind_node *codelist); +/** generate classes */ +int output_jsclasses(struct binding *binding); + /* Generate jsapi native function specifiers */ int output_function_spec(struct binding *binding); -/* Generate jsapi native function bodys +/** + * Generate jsapi native function bodies. * * web IDL describes methods as operators * http://www.w3.org/TR/WebIDL/#idl-operations * * This walks the web IDL AST to find all operator interface members * and construct appropriate jsapi native function body to implement - * them. + * them. * * Function body contents can be overriden with an operator code * block in the binding definition. * - * @param binding The binding information - * @param interface The interface to generate operator bodys for + * @param binding The binding information */ -int output_operator_body(struct binding *binding, const char *interface); - +int output_function_bodies(struct binding *binding); /** generate property tinyid enum */ int output_property_tinyid(struct binding *binding); @@ -76,10 +124,11 @@ int output_property_spec(struct binding *binding); /** generate property function bodies */ int output_property_body(struct binding *binding); -/** generate property definitions for constants */ -int output_const_defines(struct binding *binding, const char *interface); - +/** generate binding initialisation */ +int output_class_init(struct binding *binding); +/** generate binding class constructors */ +int output_class_new(struct binding *binding); #endif diff --git a/src/nsgenbind-ast.c b/src/nsgenbind-ast.c index 851cbeb..4f0654a 100644 --- a/src/nsgenbind-ast.c +++ b/src/nsgenbind-ast.c @@ -14,10 +14,17 @@ #include <stdlib.h> #include <errno.h> #include <string.h> +#include <stdarg.h> +#include "utils.h" #include "nsgenbind-ast.h" #include "options.h" +/** + * standard IO handle for parse trace logging. + */ +static FILE *genbind_parsetracef; + /* parser and lexer interface */ extern int nsgenbind_debug; extern int nsgenbind__flex_debug; @@ -26,444 +33,639 @@ extern int nsgenbind_parse(struct genbind_node **genbind_ast); /* terminal nodes have a value only */ struct genbind_node { - enum genbind_node_type type; - struct genbind_node *l; - union { - void *value; - struct genbind_node *node; - char *text; - int number; /* node data is an integer */ - } r; + enum genbind_node_type type; + struct genbind_node *l; + union { + void *value; + struct genbind_node *node; + char *text; + int number; /* node data is an integer */ + } r; }; +/* insert node(s) at beginning of a list */ +struct genbind_node * +genbind_node_prepend(struct genbind_node *list, struct genbind_node *inst) +{ + struct genbind_node *end = inst; + + if (inst == NULL) { + return list; /* no node to prepend - return existing list */ + } + + /* find end of inserted node list */ + while (end->l != NULL) { + end = end->l; + } + + end->l = list; + + return inst; +} + +/* prepend list to a nodes list + * + * inserts a list into the beginning of a nodes r list + * + * CAUTION: if the \a node element is not a node type the node will not be added + */ +struct genbind_node * +genbind_node_add(struct genbind_node *node, struct genbind_node *list) +{ + if (node == NULL) { + return list; + } + + /* this does not use genbind_node_getnode() as it cannot + * determine between an empty node and a node which is not a + * list type + */ + switch (node->type) { + case GENBIND_NODE_TYPE_BINDING: + case GENBIND_NODE_TYPE_CLASS: + case GENBIND_NODE_TYPE_PRIVATE: + case GENBIND_NODE_TYPE_INTERNAL: + case GENBIND_NODE_TYPE_PROPERTY: + case GENBIND_NODE_TYPE_FLAGS: + case GENBIND_NODE_TYPE_METHOD: + case GENBIND_NODE_TYPE_PARAMETER: + break; + + default: + /* not a node type */ + return list; + } + + node->r.node = genbind_node_prepend(node->r.node, list); + + return node; +} char *genbind_strapp(char *a, char *b) { - char *fullstr; - int fulllen; - fulllen = strlen(a) + strlen(b) + 1; - fullstr = malloc(fulllen); - snprintf(fullstr, fulllen, "%s%s", a, b); - free(a); - free(b); - return fullstr; + char *fullstr; + int fulllen; + fulllen = strlen(a) + strlen(b) + 1; + fullstr = malloc(fulllen); + snprintf(fullstr, fulllen, "%s%s", a, b); + free(a); + free(b); + return fullstr; } -struct genbind_node *genbind_node_link(struct genbind_node *tgt, struct genbind_node *src) +struct genbind_node * +genbind_node_link(struct genbind_node *tgt, struct genbind_node *src) { - tgt->l = src; - return tgt; + tgt->l = src; + return tgt; } struct genbind_node * genbind_new_node(enum genbind_node_type type, struct genbind_node *l, void *r) { - struct genbind_node *nn; - nn = calloc(1, sizeof(struct genbind_node)); - nn->type = type; - nn->l = l; - nn->r.value = r; - return nn; + struct genbind_node *nn; + nn = calloc(1, sizeof(struct genbind_node)); + nn->type = type; + nn->l = l; + nn->r.value = r; + return nn; } + +/* exported interface defined in nsgenbind-ast.h */ int -genbind_node_for_each_type(struct genbind_node *node, - enum genbind_node_type type, - genbind_callback_t *cb, - void *ctx) +genbind_node_foreach_type(struct genbind_node *node, + enum genbind_node_type type, + genbind_callback_t *cb, + void *ctx) { - int ret; - - if (node == NULL) { - return -1; - } - if (node->l != NULL) { - ret = genbind_node_for_each_type(node->l, type, cb, ctx); - if (ret != 0) { - return ret; - } - } - if (node->type == type) { - return cb(node, ctx); - } + int ret; + + if (node == NULL) { + return -1; + } + if (node->l != NULL) { + ret = genbind_node_foreach_type(node->l, type, cb, ctx); + if (ret != 0) { + return ret; + } + } + if (node->type == type) { + return cb(node, ctx); + } + + return 0; +} - return 0; +static int genbind_enumerate_node(struct genbind_node *node, void *ctx) +{ + node = node; + (*((int *)ctx))++; + return 0; } +/* exported interface defined in nsgenbind-ast.h */ +int +genbind_node_enumerate_type(struct genbind_node *node, + enum genbind_node_type type) +{ + int count = 0; + genbind_node_foreach_type(node, + type, + genbind_enumerate_node, + &count); + return count; +} /* exported interface defined in nsgenbind-ast.h */ struct genbind_node * genbind_node_find(struct genbind_node *node, - struct genbind_node *prev, - genbind_callback_t *cb, - void *ctx) + struct genbind_node *prev, + genbind_callback_t *cb, + void *ctx) { - struct genbind_node *ret; + struct genbind_node *ret; - if ((node == NULL) || (node == prev)) { - return NULL; - } + if ((node == NULL) || (node == prev)) { + return NULL; + } - if (node->l != prev) { - ret = genbind_node_find(node->l, prev, cb, ctx); - if (ret != NULL) { - return ret; - } - } + if (node->l != prev) { + ret = genbind_node_find(node->l, prev, cb, ctx); + if (ret != NULL) { + return ret; + } + } - if (cb(node, ctx) != 0) { - return node; - } + if (cb(node, ctx) != 0) { + return node; + } - return NULL; + return NULL; } /* exported interface documented in nsgenbind-ast.h */ struct genbind_node * genbind_node_find_type(struct genbind_node *node, - struct genbind_node *prev, - enum genbind_node_type type) + struct genbind_node *prev, + enum genbind_node_type type) { - return genbind_node_find(node, - prev, - genbind_cmp_node_type, - (void *)type); + return genbind_node_find(node, + prev, + genbind_cmp_node_type, + (void *)type); } /* exported interface documented in nsgenbind-ast.h */ struct genbind_node * genbind_node_find_type_ident(struct genbind_node *node, - struct genbind_node *prev, - enum genbind_node_type type, - const char *ident) + struct genbind_node *prev, + enum genbind_node_type type, + const char *ident) { - struct genbind_node *found_node; - struct genbind_node *ident_node; - - if (ident == NULL) { - return NULL; - } - - found_node = genbind_node_find_type(node, prev, type); - - - while (found_node != NULL) { - /* look for an ident node */ - ident_node = genbind_node_find_type(genbind_node_getnode(found_node), - NULL, - GENBIND_NODE_TYPE_IDENT); - if (ident_node != NULL) { - if (strcmp(ident_node->r.text, ident) == 0) - break; - } - - /* look for next matching node */ - found_node = genbind_node_find_type(node, found_node, type); - } - return found_node; + struct genbind_node *found_node; + struct genbind_node *ident_node; + + if (ident == NULL) { + return NULL; + } + + found_node = genbind_node_find_type(node, prev, type); + + while (found_node != NULL) { + /* look for an ident node */ + ident_node = genbind_node_find_type( + genbind_node_getnode(found_node), + NULL, + GENBIND_NODE_TYPE_IDENT); + + while (ident_node != NULL) { + /* check for matching text */ + if (strcmp(ident_node->r.text, ident) == 0) { + return found_node; + } + + ident_node = genbind_node_find_type( + genbind_node_getnode(found_node), + ident_node, + GENBIND_NODE_TYPE_IDENT); + } + + + /* look for next matching node */ + found_node = genbind_node_find_type(node, found_node, type); + } + return found_node; } /* exported interface documented in nsgenbind-ast.h */ struct genbind_node * genbind_node_find_type_type(struct genbind_node *node, - struct genbind_node *prev, - enum genbind_node_type type, - const char *ident) + struct genbind_node *prev, + enum genbind_node_type type, + const char *ident) { - struct genbind_node *found_node; - struct genbind_node *ident_node; + struct genbind_node *found_node; + struct genbind_node *ident_node; + + found_node = genbind_node_find_type(node, prev, type); + + + while (found_node != NULL) { + /* look for a type node */ + ident_node = genbind_node_find_type(genbind_node_getnode(found_node), + NULL, + GENBIND_NODE_TYPE_TYPE); + if (ident_node != NULL) { + if (strcmp(ident_node->r.text, ident) == 0) + break; + } + + /* look for next matching node */ + found_node = genbind_node_find_type(node, found_node, type); + } + return found_node; +} - found_node = genbind_node_find_type(node, prev, type); +/* exported interface documented in nsgenbind-ast.h */ +struct genbind_node * +genbind_node_find_method(struct genbind_node *node, + struct genbind_node *prev, + enum genbind_method_type methodtype) +{ + 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_method_type *type; + + type_node = genbind_node_find_type( + genbind_node_getnode(res_node), + NULL, GENBIND_NODE_TYPE_METHOD_TYPE); + + type = (enum genbind_method_type *)genbind_node_getint(type_node); + if (*type == methodtype) { + break; + } + + res_node = genbind_node_find_type( + genbind_node_getnode(node), + res_node, GENBIND_NODE_TYPE_METHOD); + } + + return res_node; +} - while (found_node != NULL) { - /* look for a type node */ - ident_node = genbind_node_find_type(genbind_node_getnode(found_node), - NULL, - GENBIND_NODE_TYPE_TYPE); - if (ident_node != NULL) { - if (strcmp(ident_node->r.text, ident) == 0) - break; - } - /* look for next matching node */ - found_node = genbind_node_find_type(node, found_node, type); - } - return found_node; +/* exported interface documented in nsgenbind-ast.h */ +struct genbind_node * +genbind_node_find_method_ident(struct genbind_node *node, + struct genbind_node *prev, + enum genbind_method_type nodetype, + const char *ident) +{ + struct genbind_node *res_node; + char *method_ident; + + res_node = genbind_node_find_method(node, prev, nodetype); + while (res_node != NULL) { + method_ident = genbind_node_gettext( + genbind_node_find_type( + genbind_node_getnode(res_node), + NULL, + GENBIND_NODE_TYPE_IDENT)); + + if ((ident != NULL) && + (method_ident != NULL) && + strcmp(ident, method_ident) == 0) { + break; + } + + res_node = genbind_node_find_method(node, res_node, nodetype); + } + return res_node; } + +/* exported interface documented in nsgenbind-ast.h */ int genbind_cmp_node_type(struct genbind_node *node, void *ctx) { - if (node->type == (enum genbind_node_type)ctx) - return 1; - return 0; + if (node->type == (enum genbind_node_type)ctx) + return 1; + return 0; } char *genbind_node_gettext(struct genbind_node *node) { - if (node != NULL) { - switch(node->type) { - case GENBIND_NODE_TYPE_WEBIDLFILE: - case GENBIND_NODE_TYPE_STRING: - case GENBIND_NODE_TYPE_PREAMBLE: - case GENBIND_NODE_TYPE_PROLOGUE: - case GENBIND_NODE_TYPE_EPILOGUE: - case GENBIND_NODE_TYPE_IDENT: - case GENBIND_NODE_TYPE_TYPE: - case GENBIND_NODE_TYPE_BINDING_INTERFACE: - case GENBIND_NODE_TYPE_CBLOCK: - return node->r.text; - - default: - break; - } - } - return NULL; + if (node != NULL) { + switch(node->type) { + case GENBIND_NODE_TYPE_WEBIDL: + case GENBIND_NODE_TYPE_STRING: + case GENBIND_NODE_TYPE_PREFACE: + case GENBIND_NODE_TYPE_PROLOGUE: + case GENBIND_NODE_TYPE_EPILOGUE: + case GENBIND_NODE_TYPE_POSTFACE: + case GENBIND_NODE_TYPE_IDENT: + case GENBIND_NODE_TYPE_TYPE: + case GENBIND_NODE_TYPE_CDATA: + return node->r.text; + + default: + break; + } + } + return NULL; } struct genbind_node *genbind_node_getnode(struct genbind_node *node) { - if (node != NULL) { - switch(node->type) { - case GENBIND_NODE_TYPE_HDRCOMMENT: - case GENBIND_NODE_TYPE_BINDING: - case GENBIND_NODE_TYPE_BINDING_PRIVATE: - case GENBIND_NODE_TYPE_BINDING_INTERNAL: - case GENBIND_NODE_TYPE_BINDING_PROPERTY: - case GENBIND_NODE_TYPE_OPERATION: - case GENBIND_NODE_TYPE_API: - case GENBIND_NODE_TYPE_GETTER: - case GENBIND_NODE_TYPE_SETTER: - return node->r.node; - - default: - break; - } - } - return NULL; + if (node != NULL) { + switch(node->type) { + case GENBIND_NODE_TYPE_BINDING: + case GENBIND_NODE_TYPE_CLASS: + case GENBIND_NODE_TYPE_PRIVATE: + case GENBIND_NODE_TYPE_INTERNAL: + case GENBIND_NODE_TYPE_PROPERTY: + case GENBIND_NODE_TYPE_FLAGS: + case GENBIND_NODE_TYPE_METHOD: + case GENBIND_NODE_TYPE_PARAMETER: + return node->r.node; + + default: + break; + } + } + return NULL; } -int genbind_node_getint(struct genbind_node *node) +int *genbind_node_getint(struct genbind_node *node) { - if (node != NULL) { - switch(node->type) { - case GENBIND_NODE_TYPE_MODIFIER: - return node->r.number; - - default: - break; - } - } - return -1; - + if (node != NULL) { + switch(node->type) { + case GENBIND_NODE_TYPE_METHOD_TYPE: + case GENBIND_NODE_TYPE_MODIFIER: + return &node->r.number; + + default: + break; + } + } + return NULL; } static const char *genbind_node_type_to_str(enum genbind_node_type type) { - switch(type) { - case GENBIND_NODE_TYPE_IDENT: - return "Ident"; + switch(type) { + case GENBIND_NODE_TYPE_IDENT: + return "Ident"; - case GENBIND_NODE_TYPE_ROOT: - return "Root"; + case GENBIND_NODE_TYPE_ROOT: + return "Root"; - case GENBIND_NODE_TYPE_WEBIDLFILE: - return "webidlfile"; + case GENBIND_NODE_TYPE_WEBIDL: + return "webidl"; - case GENBIND_NODE_TYPE_HDRCOMMENT: - return "HdrComment"; + case GENBIND_NODE_TYPE_STRING: + return "String"; - case GENBIND_NODE_TYPE_STRING: - return "String"; + case GENBIND_NODE_TYPE_PREFACE: + return "Preface"; - case GENBIND_NODE_TYPE_PREAMBLE: - return "Preamble"; + case GENBIND_NODE_TYPE_POSTFACE: + return "Postface"; - case GENBIND_NODE_TYPE_BINDING: - return "Binding"; + case GENBIND_NODE_TYPE_PROLOGUE: + return "Prologue"; - case GENBIND_NODE_TYPE_TYPE: - return "Type"; + case GENBIND_NODE_TYPE_EPILOGUE: + return "Epilogue"; - case GENBIND_NODE_TYPE_BINDING_PRIVATE: - return "Private"; + case GENBIND_NODE_TYPE_BINDING: + return "Binding"; - case GENBIND_NODE_TYPE_BINDING_INTERNAL: - return "Internal"; + case GENBIND_NODE_TYPE_TYPE: + return "Type"; - case GENBIND_NODE_TYPE_BINDING_INTERFACE: - return "Interface"; + case GENBIND_NODE_TYPE_PRIVATE: + return "Private"; - case GENBIND_NODE_TYPE_BINDING_PROPERTY: - return "Property"; + case GENBIND_NODE_TYPE_INTERNAL: + return "Internal"; - case GENBIND_NODE_TYPE_OPERATION: - return "Operation"; + case GENBIND_NODE_TYPE_CLASS: + return "Class"; - case GENBIND_NODE_TYPE_API: - return "API"; + case GENBIND_NODE_TYPE_FLAGS: + return "Flags"; - case GENBIND_NODE_TYPE_GETTER: - return "Getter"; + case GENBIND_NODE_TYPE_PROPERTY: + return "Property"; - case GENBIND_NODE_TYPE_SETTER: - return "Setter"; + case GENBIND_NODE_TYPE_METHOD: + return "Method"; - case GENBIND_NODE_TYPE_CBLOCK: - return "CBlock"; + case GENBIND_NODE_TYPE_METHOD_TYPE: + return "Type"; - default: - return "Unknown"; - } + case GENBIND_NODE_TYPE_PARAMETER: + return "Parameter"; + + case GENBIND_NODE_TYPE_CDATA: + return "CBlock"; + + default: + return "Unknown"; + } } -int genbind_ast_dump(struct genbind_node *node, int indent) +/** dump ast node to file at indent level */ +static int genbind_ast_dump(FILE *dfile, struct genbind_node *node, int indent) { - const char *SPACES=" "; - char *txt; - - while (node != NULL) { - printf("%.*s%s", indent, SPACES, genbind_node_type_to_str(node->type)); - - txt = genbind_node_gettext(node); - if (txt == NULL) { - printf("\n"); - genbind_ast_dump(genbind_node_getnode(node), indent + 2); - } else { - printf(": \"%.*s\"\n", 75 - indent, txt); - } - node = node->l; - } - return 0; + const char *SPACES=" "; + char *txt; + int *val; + + while (node != NULL) { + fprintf(dfile, "%.*s%s", indent, SPACES, + genbind_node_type_to_str(node->type)); + + txt = genbind_node_gettext(node); + if (txt == NULL) { + val = genbind_node_getint(node); + if (val == NULL) { + fprintf(dfile, "\n"); + genbind_ast_dump(dfile, + genbind_node_getnode(node), + indent + 2); + } else { + fprintf(dfile, ": %d\n", *val); + } + } else { + fprintf(dfile, ": \"%.*s\"\n", 75 - indent, txt); + } + node = node->l; + } + return 0; } -FILE *genbindopen(const char *filename) + +/* exported interface documented in nsgenbind-ast.h */ +int genbind_dump_ast(struct genbind_node *node) { - FILE *genfile; - char *fullname; - int fulllen; - static char *prevfilepath = NULL; - - /* try filename raw */ - genfile = fopen(filename, "r"); - if (genfile != NULL) { - if (options->verbose) { - printf("Opened Genbind file %s\n", filename); - } - if (prevfilepath == NULL) { - fullname = strrchr(filename, '/'); - if (fullname == NULL) { - fulllen = strlen(filename); - } else { - fulllen = fullname - filename; - } - prevfilepath = strndup(filename,fulllen); - } - if (options->depfilehandle != NULL) { - fprintf(options->depfilehandle, " \\\n\t%s", - filename); - } - return genfile; - } + FILE *dumpf; - /* try based on previous filename */ - if (prevfilepath != NULL) { - fulllen = strlen(prevfilepath) + strlen(filename) + 2; - fullname = malloc(fulllen); - snprintf(fullname, fulllen, "%s/%s", prevfilepath, filename); - if (options->debug) { - printf("Attempting to open Genbind file %s\n", fullname); - } - genfile = fopen(fullname, "r"); - if (genfile != NULL) { - if (options->verbose) { - printf("Opened Genbind file %s\n", fullname); - } - if (options->depfilehandle != NULL) { - fprintf(options->depfilehandle, " \\\n\t%s", - fullname); - } - free(fullname); - return genfile; - } - free(fullname); - } + /* only dump AST to file if required */ + if (!options->debug) { + return 0; + } - /* try on idl path */ - if (options->idlpath != NULL) { - fulllen = strlen(options->idlpath) + strlen(filename) + 2; - fullname = malloc(fulllen); - snprintf(fullname, fulllen, "%s/%s", options->idlpath, filename); - genfile = fopen(fullname, "r"); - if ((genfile != NULL) && options->verbose) { - printf("Opend Genbind file %s\n", fullname); - if (options->depfilehandle != NULL) { - fprintf(options->depfilehandle, " \\\n\t%s", - fullname); - } - } - - free(fullname); - } + dumpf = genb_fopen("binding-ast", "w"); + if (dumpf == NULL) { + return 2; + } + + genbind_ast_dump(dumpf, node, 0); - return genfile; + fclose(dumpf); + + return 0; } -int genbind_parsefile(char *infilename, struct genbind_node **ast) +FILE *genbindopen(const char *filename) { - FILE *infile; - - /* open input file */ - if ((infilename[0] == '-') && - (infilename[1] == 0)) { - if (options->verbose) { - printf("Using stdin for input\n"); - } - infile = stdin; - } else { - infile = genbindopen(infilename); - } - - if (!infile) { - fprintf(stderr, "Error opening %s: %s\n", - infilename, - strerror(errno)); - return 3; - } + FILE *genfile; + char *fullname; + int fulllen; + static char *prevfilepath = NULL; + + /* try filename raw */ + genfile = fopen(filename, "r"); + if (genfile != NULL) { + if (options->verbose) { + printf("Opened Genbind file %s\n", filename); + } + if (prevfilepath == NULL) { + fullname = strrchr(filename, '/'); + if (fullname == NULL) { + fulllen = strlen(filename); + } else { + fulllen = fullname - filename; + } + prevfilepath = strndup(filename,fulllen); + } +#if 0 + if (options->depfilehandle != NULL) { + fprintf(options->depfilehandle, " \\\n\t%s", + filename); + } +#endif + return genfile; + } + + /* try based on previous filename */ + if (prevfilepath != NULL) { + fulllen = strlen(prevfilepath) + strlen(filename) + 2; + fullname = malloc(fulllen); + snprintf(fullname, fulllen, "%s/%s", prevfilepath, filename); + if (options->debug) { + printf("Attempting to open Genbind file %s\n", fullname); + } + genfile = fopen(fullname, "r"); + if (genfile != NULL) { + if (options->verbose) { + printf("Opened Genbind file %s\n", fullname); + } +#if 0 + if (options->depfilehandle != NULL) { + fprintf(options->depfilehandle, " \\\n\t%s", + fullname); + } +#endif + free(fullname); + return genfile; + } + free(fullname); + } + + /* try on idl path */ + if (options->idlpath != NULL) { + fulllen = strlen(options->idlpath) + strlen(filename) + 2; + fullname = malloc(fulllen); + snprintf(fullname, fulllen, "%s/%s", options->idlpath, filename); + genfile = fopen(fullname, "r"); + if ((genfile != NULL) && options->verbose) { + printf("Opend Genbind file %s\n", fullname); +#if 0 + if (options->depfilehandle != NULL) { + fprintf(options->depfilehandle, " \\\n\t%s", + fullname); + } +#endif + } - if (options->debug) { - nsgenbind_debug = 1; - nsgenbind__flex_debug = 1; - } + free(fullname); + } - /* set flex to read from file */ - nsgenbind_restart(infile); + return genfile; +} - /* process binding */ - return nsgenbind_parse(ast); +int genbind_parsefile(char *infilename, struct genbind_node **ast) +{ + FILE *infile; + int ret; + + /* open input file */ + infile = genbindopen(infilename); + if (!infile) { + fprintf(stderr, "Error opening %s: %s\n", + infilename, + strerror(errno)); + return 3; + } + + /* if debugging enabled enable parser tracing and send to file */ + if (options->debug) { + nsgenbind_debug = 1; + nsgenbind__flex_debug = 1; + genbind_parsetracef = genb_fopen("binding-trace", "w"); + } else { + genbind_parsetracef = NULL; + } + + /* set flex to read from file */ + nsgenbind_restart(infile); + + /* process binding */ + ret = nsgenbind_parse(ast); + + /* close tracefile if open */ + if (genbind_parsetracef != NULL) { + fclose(genbind_parsetracef); + } + + return ret; } -#ifdef NEED_STRNDUP - -char *strndup(const char *s, size_t n) +int genbind_fprintf(FILE *stream, const char *format, ...) { - size_t len; - char *s2; + va_list ap; + int ret; - for (len = 0; len != n && s[len]; len++) - continue; + va_start(ap, format); - s2 = malloc(len + 1); - if (!s2) - return 0; + if (genbind_parsetracef == NULL) { + ret = vfprintf(stream, format, ap); + } else { + ret = vfprintf(genbind_parsetracef, format, ap); + } + va_end(ap); - memcpy(s2, s, len); - s2[len] = 0; - return s2; + return ret; } - -#endif - diff --git a/src/nsgenbind-ast.h b/src/nsgenbind-ast.h index 6513f7f..2a384b2 100644 --- a/src/nsgenbind-ast.h +++ b/src/nsgenbind-ast.h @@ -1,4 +1,4 @@ -/* binding file AST interface +/* binding file AST interface * * This file is part of nsnsgenbind. * Licensed under the MIT License, @@ -10,43 +10,56 @@ #define nsgenbind_nsgenbind_ast_h enum genbind_node_type { - GENBIND_NODE_TYPE_ROOT = 0, - GENBIND_NODE_TYPE_IDENT, /**< generic identifier string */ - GENBIND_NODE_TYPE_TYPE, /**< generic type string */ - GENBIND_NODE_TYPE_MODIFIER, /**< node modifier */ - - GENBIND_NODE_TYPE_CBLOCK, - GENBIND_NODE_TYPE_WEBIDLFILE, - GENBIND_NODE_TYPE_HDRCOMMENT, - GENBIND_NODE_TYPE_STRING, - GENBIND_NODE_TYPE_PREAMBLE, - GENBIND_NODE_TYPE_PROLOGUE, - GENBIND_NODE_TYPE_EPILOGUE, - GENBIND_NODE_TYPE_BINDING, - GENBIND_NODE_TYPE_BINDING_PRIVATE, - GENBIND_NODE_TYPE_BINDING_INTERNAL, - GENBIND_NODE_TYPE_BINDING_INTERFACE, - GENBIND_NODE_TYPE_BINDING_PROPERTY, - GENBIND_NODE_TYPE_API, - GENBIND_NODE_TYPE_OPERATION, - GENBIND_NODE_TYPE_GETTER, - GENBIND_NODE_TYPE_SETTER, + GENBIND_NODE_TYPE_ROOT = 0, + GENBIND_NODE_TYPE_IDENT, /**< generic identifier string */ + GENBIND_NODE_TYPE_TYPE, /**< generic type string */ + GENBIND_NODE_TYPE_MODIFIER, /**< node modifier */ + GENBIND_NODE_TYPE_CDATA, /**< verbatim block of character data */ + GENBIND_NODE_TYPE_STRING, /**< text string */ + + GENBIND_NODE_TYPE_BINDING, + GENBIND_NODE_TYPE_WEBIDL, + GENBIND_NODE_TYPE_PREFACE, + GENBIND_NODE_TYPE_PROLOGUE, + GENBIND_NODE_TYPE_EPILOGUE, + GENBIND_NODE_TYPE_POSTFACE, + + GENBIND_NODE_TYPE_CLASS, /**< class definition */ + GENBIND_NODE_TYPE_PRIVATE, + GENBIND_NODE_TYPE_INTERNAL, + GENBIND_NODE_TYPE_PROPERTY, + GENBIND_NODE_TYPE_FLAGS, + + GENBIND_NODE_TYPE_METHOD, /**< binding method */ + GENBIND_NODE_TYPE_METHOD_TYPE, /**< binding method type */ + GENBIND_NODE_TYPE_PARAMETER, /**< method parameter */ }; /* modifier flags */ enum genbind_type_modifier { - GENBIND_TYPE_NONE = 0, - GENBIND_TYPE_TYPE = 1, /**< identifies a type handler */ - GENBIND_TYPE_UNSHARED = 2, /**< unshared item */ - GENBIND_TYPE_TYPE_UNSHARED = 3, /**< identifies a unshared type handler */ + GENBIND_TYPE_NONE = 0, + GENBIND_TYPE_TYPE = 1, /**< identifies a type handler */ + GENBIND_TYPE_UNSHARED = 2, /**< unshared item */ + GENBIND_TYPE_TYPE_UNSHARED = 3, /**< identifies a unshared type handler */ }; +/* binding method types */ +enum genbind_method_type { + GENBIND_METHOD_TYPE_INIT = 0, /**< binding method is initialiser */ + GENBIND_METHOD_TYPE_FINI, /**< binding method is finalizer */ + GENBIND_METHOD_TYPE_METHOD, /**< binding method is a method */ + GENBIND_METHOD_TYPE_GETTER, /**< binding method is a getter */ + GENBIND_METHOD_TYPE_SETTER, /**< binding method is a setter */ + GENBIND_METHOD_TYPE_PROTOTYPE, /**< binding method is a prototype */ +}; struct genbind_node; /** callback for search and iteration routines */ typedef int (genbind_callback_t)(struct genbind_node *node, void *ctx); +int genbind_fprintf(FILE *stream, const char *format, ...); + int genbind_cmp_node_type(struct genbind_node *node, void *ctx); FILE *genbindopen(const char *filename); @@ -58,77 +71,178 @@ char *genbind_strapp(char *a, char *b); struct genbind_node *genbind_new_node(enum genbind_node_type type, struct genbind_node *l, void *r); struct genbind_node *genbind_node_link(struct genbind_node *tgt, struct genbind_node *src); -int genbind_ast_dump(struct genbind_node *ast, int indent); +struct genbind_node *genbind_node_prepend(struct genbind_node *list, struct genbind_node *inst); + +struct genbind_node *genbind_node_add(struct genbind_node *node, struct genbind_node *list); -/** Depth first left hand search using user provided comparison +/** + * Dump the binding AST to file + * + * If the debug flag has been set this causes the binding AST to be written to + * a binding-ast output file + * + * \param node Node of the tree to start dumping from (usually tree root) + * \return 0 on sucess or non zero on faliure and error message printed. + */ +int genbind_dump_ast(struct genbind_node *node); + +/** + *Depth first left hand search using user provided comparison * * @param node The node to start the search from * @param prev The node at which to stop the search, either NULL to - * search the full tree depth (initial search) or the result + * search the full tree depth (initial search) or the result * of a previous search to continue. * @param cb Comparison callback * @param ctx Context for callback */ struct genbind_node * genbind_node_find(struct genbind_node *node, - struct genbind_node *prev, - genbind_callback_t *cb, - void *ctx); + struct genbind_node *prev, + genbind_callback_t *cb, + void *ctx); -/** Depth first left hand search returning nodes of the specified type +/** + * Depth first left hand search returning nodes of the specified type * * @param node The node to start the search from * @param prev The node at which to stop the search, either NULL to - * search the full tree depth (initial search) or the result + * search the full tree depth (initial search) or the result * of a previous search to continue. * @param nodetype The type of node to seach for + * @return The found node or NULL for no nodes. */ struct genbind_node * genbind_node_find_type(struct genbind_node *node, - struct genbind_node *prev, - enum genbind_node_type nodetype); + struct genbind_node *prev, + enum genbind_node_type nodetype); + +/** + * count how many nodes of a specified type. + * + * Enumerate how many nodes of the specified type there are by + * performing a depth first search for nodes of the given type and + * counting the number of results. + * + * @param node The node to start the search from + * @param nodetype The type of node to count + * @return The number of nodes found. + */ +int +genbind_node_enumerate_type(struct genbind_node *node, + enum genbind_node_type type); + -/** Depth first left hand search returning nodes of the specified type - * and a ident child node with matching text +/** + * Depth first left hand search returning nodes of the specified type + * with an ident child node with matching text * * @param node The node to start the search from * @param prev The node at which to stop the search, either NULL to - * search the full tree depth (initial search) or the result + * search the full tree depth (initial search) or the result * of a previous search to continue. * @param nodetype The type of node to seach for * @param ident The text to match the ident child node to */ struct genbind_node * genbind_node_find_type_ident(struct genbind_node *node, - struct genbind_node *prev, - enum genbind_node_type nodetype, - const char *ident); + struct genbind_node *prev, + enum genbind_node_type nodetype, + const char *ident); + -/** Depth first left hand search returning nodes of the specified type - * and a type child node with matching text +/** + * Returning node of the specified type with a GENBIND_NODE_TYPE_TYPE + * subnode with matching text. + * + * This is a conveniance wrapper around nested calls to + * genbind_node_find_type() which performs a depth first left hand + * search returning nodes of the specified type and a child node of + * GENBIND_NODE_TYPE_TYPE with matching text. + * * * @param node The node to start the search from * @param prev The node at which to stop the search, either NULL to - * search the full tree depth (initial search) or the result + * search the full tree depth (initial search) or the result * of a previous search to continue. - * @param nodetype The type of node to seach for - * @param type The text to match the type child node to + * @param nodetype The type of node to seach for. + * @param type The text to match the type child node to. */ struct genbind_node * genbind_node_find_type_type(struct genbind_node *node, - struct genbind_node *prev, - enum genbind_node_type nodetype, - const char *type); + struct genbind_node *prev, + enum genbind_node_type nodetype, + const char *type_text); -int genbind_node_for_each_type(struct genbind_node *node, enum genbind_node_type type, genbind_callback_t *cb, void *ctx); -char *genbind_node_gettext(struct genbind_node *node); +/** + * Find a method node of a given method type + * + * \param node A node of type GENBIND_NODE_TYPE_CLASS to search for methods. + * \param prev The node at which to stop the search, either NULL to + * search the full tree depth (initial search) or the result + * of a previous search to continue. + * \param methodtype The type of method to find. + * \return A node of type GENBIND_NODE_TYPE_METHOD on success or NULL on faliure + */ +struct genbind_node * +genbind_node_find_method(struct genbind_node *node, + struct genbind_node *prev, + enum genbind_method_type methodtype); + + +/** + * Find a method node of a given method type and identifier + * + * \param node A node of type GENBIND_NODE_TYPE_CLASS to search for methods. + * \param prev The node at which to stop the search, either NULL to + * search the full tree depth (initial search) or the result + * of a previous search to continue. + * \param methodtype The type of method to find. + * \param ident The identifier to search for + * \return A node of type GENBIND_NODE_TYPE_METHOD on success or NULL on faliure + */ +struct genbind_node * +genbind_node_find_method_ident(struct genbind_node *node, + struct genbind_node *prev, + enum genbind_method_type methodtype, + const char *ident); + + +/** + * Iterate all nodes of a certian type from a node with a callback. + * + * Depth first search for nodes of the given type calling the callback + * with context. + * + * @param node The node to start the search from. + */ +int genbind_node_foreach_type(struct genbind_node *node, + enum genbind_node_type type, + genbind_callback_t *cb, + void *ctx); + +/** get a nodes node list content + * + * @param node The nodes to get node list from + * @return pointer to the node list or NULL if the node does not contain a list + */ struct genbind_node *genbind_node_getnode(struct genbind_node *node); -int genbind_node_getint(struct genbind_node *node); -#ifdef _WIN32 -#define NEED_STRNDUP 1 -char *strndup(const char *s, size_t n); -#endif +/** get a nodes text content + * + * @param node The nodes to get text from + * @return pointer to the node text or NULL if the node is not of type + * text or is empty. + */ +char *genbind_node_gettext(struct genbind_node *node); + +/** get a nodes integer value + * + * @param node The nodes to get integer from + * @return pointer to the node value or NULL if the node is not of type + * int or is empty. + */ +int *genbind_node_getint(struct genbind_node *node); #endif diff --git a/src/nsgenbind-lexer.l b/src/nsgenbind-lexer.l index 8189a72..d092195 100644 --- a/src/nsgenbind-lexer.l +++ b/src/nsgenbind-lexer.l @@ -72,7 +72,9 @@ cblockopen \%\{ cblockclose \%\} /* used for #include directive */ -poundsign ^{whitespace}*# +poundsign ^{whitespace}*# + +dblcolon \:\: %x cblock @@ -88,42 +90,57 @@ poundsign ^{whitespace}*# yylloc->last_column = 0; } - /* terminals */ + /* binding terminals */ -webidlfile return TOK_IDLFILE; +binding return TOK_BINDING; -hdrcomment return TOK_HDR_COMMENT; +webidl return TOK_WEBIDL; -preamble return TOK_PREAMBLE; +preface return TOK_PREFACE; prologue return TOK_PROLOGUE; epilogue return TOK_EPILOGUE; -binding return TOK_BINDING; +postface return TOK_POSTFACE; -interface return TOK_INTERFACE; -type return TOK_TYPE; + /* class member terminals */ + +class return TOK_CLASS; private return TOK_PRIVATE; internal return TOK_INTERNAL; +flags return TOK_FLAGS; + +type return TOK_TYPE; + unshared return TOK_UNSHARED; shared return TOK_SHARED; property return TOK_PROPERTY; -operation return TOK_OPERATION; + /* implementation terminals */ + +init return TOK_INIT; + +fini return TOK_FINI; -api return TOK_API; +method return TOK_METHOD; getter return TOK_GETTER; setter return TOK_SETTER; +prototype return TOK_PROTOTYPE; + + /* other terminals */ + +{dblcolon} return TOK_DBLCOLON; + {cblockopen} BEGIN(cblock); {identifier} { @@ -138,7 +155,7 @@ setter return TOK_SETTER; {multicomment} /* nothing */ -{poundsign}include BEGIN(incl); +{poundsign}include BEGIN(incl); {other} return (int) yytext[0]; @@ -149,7 +166,7 @@ setter return TOK_SETTER; <cblock>\% yylval->text = strdup(yytext); return TOK_CCODE_LITERAL; -<incl>[ \t]*\" /* eat the whitespace and open quotes */ +<incl>[ \t]*\" /* eat the whitespace and open quotes */ <incl>[^\t\n\"]+ { /* got the include file name */ diff --git a/src/nsgenbind-parser.y b/src/nsgenbind-parser.y index 46bc4f3..1462b39 100644 --- a/src/nsgenbind-parser.y +++ b/src/nsgenbind-parser.y @@ -10,6 +10,12 @@ #include <stdio.h> #include <string.h> +#define YYFPRINTF genbind_fprintf +#define YY_LOCATION_PRINT(File, Loc) \ + genbind_fprintf(File, "%d.%d-%d.%d", \ + (Loc).first_line, (Loc).first_column, \ + (Loc).last_line, (Loc).last_column) + #include "nsgenbind-parser.h" #include "nsgenbind-lexer.h" #include "webidl-ast.h" @@ -17,13 +23,74 @@ char *errtxt; - static void nsgenbind_error(YYLTYPE *locp, struct genbind_node **genbind_ast, const char *str) +static void nsgenbind_error(YYLTYPE *locp, + struct genbind_node **genbind_ast, + const char *str) { locp = locp; genbind_ast = genbind_ast; errtxt = strdup(str); } +static struct genbind_node * +add_method(struct genbind_node **genbind_ast, + long methodtype, + struct genbind_node *declarator, + char *cdata) +{ + struct genbind_node *res_node; + struct genbind_node *method_node; + struct genbind_node *class_node; + struct genbind_node *cdata_node; + char *class_name; + + /* extract the class name from the declarator */ + class_name = genbind_node_gettext( + genbind_node_find_type( + genbind_node_getnode( + genbind_node_find_type( + declarator, + NULL, + GENBIND_NODE_TYPE_CLASS)), + NULL, + GENBIND_NODE_TYPE_IDENT)); + + if (cdata == NULL) { + cdata_node = declarator; + } else { + cdata_node = genbind_new_node(GENBIND_NODE_TYPE_CDATA, + declarator, + cdata); + } + + /* generate method node */ + method_node = genbind_new_node(GENBIND_NODE_TYPE_METHOD, + NULL, + genbind_new_node(GENBIND_NODE_TYPE_METHOD_TYPE, + cdata_node, + (void *)methodtype)); + + class_node = genbind_node_find_type_ident(*genbind_ast, + NULL, + GENBIND_NODE_TYPE_CLASS, + class_name); + if (class_node == NULL) { + /* no existing class so manufacture one and attach method */ + res_node = genbind_new_node(GENBIND_NODE_TYPE_CLASS, NULL, + genbind_new_node(GENBIND_NODE_TYPE_IDENT, + method_node, + class_name)); + + } else { + /* update the existing class */ + + /* link member node into class_node */ + genbind_node_add(class_node, method_node); + + res_node = NULL; /* updating so no need to add a new node */ + } + return res_node; +} %} @@ -37,30 +104,37 @@ char *errtxt; %union { - char* text; + char *text; struct genbind_node *node; long value; } -%token TOK_IDLFILE -%token TOK_HDR_COMMENT -%token TOK_PREAMBLE -%token TOK_PROLOGUE; -%token TOK_EPILOGUE; - -%token TOK_API %token TOK_BINDING -%token TOK_OPERATION -%token TOK_GETTER -%token TOK_SETTER -%token TOK_INTERFACE -%token TOK_TYPE +%token TOK_WEBIDL +%token TOK_PREFACE +%token TOK_PROLOGUE +%token TOK_EPILOGUE +%token TOK_POSTFACE + +%token TOK_CLASS %token TOK_PRIVATE %token TOK_INTERNAL +%token TOK_FLAGS +%token TOK_TYPE %token TOK_UNSHARED %token TOK_SHARED %token TOK_PROPERTY + /* method types */ +%token TOK_INIT +%token TOK_FINI +%token TOK_METHOD +%token TOK_GETTER +%token TOK_SETTER +%token TOK_PROTOTYPE + +%token TOK_DBLCOLON + %token <text> TOK_IDENTIFIER %token <text> TOK_STRING_LITERAL %token <text> TOK_CCODE_LITERAL @@ -72,25 +146,30 @@ char *errtxt; %type <node> Statement %type <node> Statements -%type <node> IdlFile -%type <node> Preamble -%type <node> Prologue -%type <node> Epilogue -%type <node> HdrComment -%type <node> Strings %type <node> Binding %type <node> BindingArgs %type <node> BindingArg -%type <node> Type +%type <node> Class +%type <node> ClassArgs +%type <node> ClassArg +%type <node> ClassFlag +%type <node> ClassFlags + +%type <node> Method +%type <node> MethodDeclarator +%type <value> MethodType + +%type <node> WebIDL +%type <node> Preface +%type <node> Prologue +%type <node> Epilogue +%type <node> Postface %type <node> Private %type <node> Internal -%type <node> Interface %type <node> Property -%type <node> Operation -%type <node> Api -%type <node> Getter -%type <node> Setter +%type <node> ParameterList +%type <node> TypeIdent %% @@ -109,7 +188,7 @@ Statements | Statements Statement { - $$ = genbind_node_link($2, $1); + $$ = *genbind_ast = genbind_node_prepend($2, $1); } | error ';' @@ -122,68 +201,88 @@ Statements Statement : - IdlFile - | - HdrComment - | - Preamble - | - Prologue - | - Epilogue - | Binding | - Operation - | - Api - | - Getter + Class | - Setter + Method ; - /* [3] load a web IDL file */ -IdlFile - : - TOK_IDLFILE TOK_STRING_LITERAL ';' +Binding + : + TOK_BINDING TOK_IDENTIFIER '{' BindingArgs '}' { - $$ = genbind_new_node(GENBIND_NODE_TYPE_WEBIDLFILE, NULL, $2); + $$ = genbind_new_node(GENBIND_NODE_TYPE_BINDING, + NULL, + genbind_new_node(GENBIND_NODE_TYPE_TYPE, $4, $2)); } ; -HdrComment - : - TOK_HDR_COMMENT Strings ';' +BindingArgs + : + BindingArg + | + BindingArgs BindingArg { - $$ = genbind_new_node(GENBIND_NODE_TYPE_HDRCOMMENT, NULL, $2); + $$ = genbind_node_link($2, $1); } ; -Strings +BindingArg : - TOK_STRING_LITERAL + WebIDL + | + Preface + | + Prologue + | + Epilogue + | + Postface + ; + + /* [3] a web IDL file specifier */ +WebIDL + : + TOK_WEBIDL TOK_STRING_LITERAL ';' { - $$ = genbind_new_node(GENBIND_NODE_TYPE_STRING, NULL, $1); + $$ = genbind_new_node(GENBIND_NODE_TYPE_WEBIDL, NULL, $2); } - | - Strings TOK_STRING_LITERAL + ; + + + /* type and identifier of a variable */ +TypeIdent + : + TOK_STRING_LITERAL TOK_IDENTIFIER + { + $$ = genbind_new_node(GENBIND_NODE_TYPE_IDENT, + genbind_new_node(GENBIND_NODE_TYPE_TYPE, NULL, $1), $2); + } + | + TOK_STRING_LITERAL TOK_IDENTIFIER TOK_DBLCOLON TOK_IDENTIFIER { - $$ = genbind_new_node(GENBIND_NODE_TYPE_STRING, $1, $2); + $$ = genbind_new_node(GENBIND_NODE_TYPE_IDENT, + genbind_new_node(GENBIND_NODE_TYPE_IDENT, + genbind_new_node(GENBIND_NODE_TYPE_TYPE, + NULL, + $1), + $2), + $4); } ; -Preamble +Preface : - TOK_PREAMBLE CBlock + TOK_PREFACE CBlock ';' { - $$ = genbind_new_node(GENBIND_NODE_TYPE_PREAMBLE, NULL, $2); + $$ = genbind_new_node(GENBIND_NODE_TYPE_PREFACE, NULL, $2); } ; Prologue : - TOK_PROLOGUE CBlock + TOK_PROLOGUE CBlock ';' { $$ = genbind_new_node(GENBIND_NODE_TYPE_PROLOGUE, NULL, $2); } @@ -191,140 +290,208 @@ Prologue Epilogue : - TOK_EPILOGUE CBlock + TOK_EPILOGUE CBlock ';' { $$ = genbind_new_node(GENBIND_NODE_TYPE_EPILOGUE, NULL, $2); } ; +Postface + : + TOK_POSTFACE CBlock ';' + { + $$ = genbind_new_node(GENBIND_NODE_TYPE_POSTFACE, NULL, $2); + } + ; + CBlock - : + : TOK_CCODE_LITERAL - | - CBlock TOK_CCODE_LITERAL + | + CBlock TOK_CCODE_LITERAL { $$ = genbind_strapp($1, $2); } ; -Operation +MethodType : - TOK_OPERATION TOK_IDENTIFIER CBlock + TOK_INIT + { + $$ = GENBIND_METHOD_TYPE_INIT; + } + | + TOK_FINI + { + $$ = GENBIND_METHOD_TYPE_FINI; + } + | + TOK_METHOD + { + $$ = GENBIND_METHOD_TYPE_METHOD; + } + | + TOK_GETTER + { + $$ = GENBIND_METHOD_TYPE_GETTER; + } + | + TOK_SETTER + { + $$ = GENBIND_METHOD_TYPE_SETTER; + } + | + TOK_PROTOTYPE { - $$ = genbind_new_node(GENBIND_NODE_TYPE_OPERATION, - NULL, - genbind_new_node(GENBIND_NODE_TYPE_IDENT, - genbind_new_node(GENBIND_NODE_TYPE_CBLOCK, - NULL, - $3), - $2)); + $$ = GENBIND_METHOD_TYPE_PROTOTYPE; } + ; -Api +ParameterList : - TOK_API TOK_IDENTIFIER CBlock + TypeIdent + { + $$ = genbind_new_node(GENBIND_NODE_TYPE_PARAMETER, NULL, $1); + } + | + ParameterList ',' TypeIdent { - $$ = genbind_new_node(GENBIND_NODE_TYPE_API, - NULL, - genbind_new_node(GENBIND_NODE_TYPE_IDENT, - genbind_new_node(GENBIND_NODE_TYPE_CBLOCK, - NULL, - $3), - $2)); + $$ = genbind_node_prepend($1, + genbind_new_node( + GENBIND_NODE_TYPE_PARAMETER, + NULL, + $3)); } + ; -Getter +MethodDeclarator : - TOK_GETTER TOK_IDENTIFIER CBlock + TOK_IDENTIFIER TOK_DBLCOLON TOK_IDENTIFIER '(' ParameterList ')' + { + $$ = genbind_new_node(GENBIND_NODE_TYPE_CLASS, + genbind_new_node(GENBIND_NODE_TYPE_IDENT, + $5, + $3), + genbind_new_node(GENBIND_NODE_TYPE_IDENT, + NULL, + $1)); + } + | + TOK_IDENTIFIER TOK_DBLCOLON TOK_IDENTIFIER '(' ')' { - $$ = genbind_new_node(GENBIND_NODE_TYPE_GETTER, - NULL, - genbind_new_node(GENBIND_NODE_TYPE_IDENT, - genbind_new_node(GENBIND_NODE_TYPE_CBLOCK, - NULL, - $3), - $2)); + $$ = genbind_new_node(GENBIND_NODE_TYPE_CLASS, + genbind_new_node(GENBIND_NODE_TYPE_IDENT, + NULL, + $3), + genbind_new_node(GENBIND_NODE_TYPE_IDENT, + NULL, + $1)); } + | + TOK_IDENTIFIER '(' ParameterList ')' + { + $$ = genbind_new_node(GENBIND_NODE_TYPE_CLASS, + $3, + genbind_new_node(GENBIND_NODE_TYPE_IDENT, + NULL, + $1)); + } + | + TOK_IDENTIFIER '(' ')' + { + $$ = genbind_new_node(GENBIND_NODE_TYPE_CLASS, NULL, + genbind_new_node(GENBIND_NODE_TYPE_IDENT, + NULL, + $1)); + } + ; -Setter +Method : - TOK_SETTER TOK_IDENTIFIER CBlock + MethodType MethodDeclarator CBlock + { + $$ = add_method(genbind_ast, $1, $2, $3); + } + | + MethodType MethodDeclarator ';' { - $$ = genbind_new_node(GENBIND_NODE_TYPE_SETTER, - NULL, - genbind_new_node(GENBIND_NODE_TYPE_IDENT, - genbind_new_node(GENBIND_NODE_TYPE_CBLOCK, - NULL, - $3), - $2)); + $$ = add_method(genbind_ast, $1, $2, NULL); } + ; -Binding +Class : - TOK_BINDING TOK_IDENTIFIER '{' BindingArgs '}' + TOK_CLASS TOK_IDENTIFIER '{' ClassArgs '}' { - $$ = genbind_new_node(GENBIND_NODE_TYPE_BINDING, - NULL, - genbind_new_node(GENBIND_NODE_TYPE_IDENT, $4, $2)); + $$ = genbind_new_node(GENBIND_NODE_TYPE_CLASS, NULL, + genbind_new_node(GENBIND_NODE_TYPE_IDENT, $4, $2)); } ; -BindingArgs +ClassArgs : - BindingArg + ClassArg | - BindingArgs BindingArg + ClassArgs ClassArg { - $$ = genbind_node_link($2, $1); + $$ = genbind_node_link($2, $1); } ; -BindingArg - : - Type - | +ClassArg + : Private | Internal | - Interface - | Property + | + ClassFlag + | + Preface + | + Prologue + | + Epilogue + | + Postface ; -Type + +Private : - TOK_TYPE TOK_IDENTIFIER ';' + TOK_PRIVATE TypeIdent ';' { - $$ = genbind_new_node(GENBIND_NODE_TYPE_TYPE, NULL, $2); + $$ = genbind_new_node(GENBIND_NODE_TYPE_PRIVATE, NULL, $2); } ; -Private +Internal : - TOK_PRIVATE TOK_STRING_LITERAL TOK_IDENTIFIER ';' + TOK_INTERNAL TypeIdent ';' { - $$ = genbind_new_node(GENBIND_NODE_TYPE_BINDING_PRIVATE, NULL, - genbind_new_node(GENBIND_NODE_TYPE_IDENT, - genbind_new_node(GENBIND_NODE_TYPE_STRING, NULL, $2), $3)); + $$ = genbind_new_node(GENBIND_NODE_TYPE_INTERNAL, NULL, $2); } ; -Internal +ClassFlag : - TOK_INTERNAL TOK_STRING_LITERAL TOK_IDENTIFIER ';' + TOK_FLAGS ClassFlags ';' { - $$ = genbind_new_node(GENBIND_NODE_TYPE_BINDING_INTERNAL, NULL, - genbind_new_node(GENBIND_NODE_TYPE_IDENT, - genbind_new_node(GENBIND_NODE_TYPE_STRING, NULL, $2), $3)); + $$ = genbind_new_node(GENBIND_NODE_TYPE_FLAGS, NULL, $2); } ; -Interface - : - TOK_INTERFACE TOK_IDENTIFIER ';' +ClassFlags + : + TOK_IDENTIFIER { - $$ = genbind_new_node(GENBIND_NODE_TYPE_BINDING_INTERFACE, NULL, $2); + $$ = genbind_new_node(GENBIND_NODE_TYPE_IDENT, NULL, $1); + } + | + ClassFlags ',' TOK_IDENTIFIER + { + $$ = genbind_new_node(GENBIND_NODE_TYPE_IDENT, $1, $3); } ; @@ -332,13 +499,12 @@ Property : TOK_PROPERTY Modifiers TOK_IDENTIFIER ';' { - $$ = genbind_new_node(GENBIND_NODE_TYPE_BINDING_PROPERTY, - NULL, - genbind_new_node(GENBIND_NODE_TYPE_MODIFIER, - genbind_new_node(GENBIND_NODE_TYPE_IDENT, - NULL, - $3), - (void *)$2)); + $$ = genbind_new_node(GENBIND_NODE_TYPE_PROPERTY, NULL, + genbind_new_node(GENBIND_NODE_TYPE_MODIFIER, + genbind_new_node(GENBIND_NODE_TYPE_IDENT, + NULL, + $3), + (void *)$2)); } ; diff --git a/src/nsgenbind.c b/src/nsgenbind.c index d993646..173f23d 100644 --- a/src/nsgenbind.c +++ b/src/nsgenbind.c @@ -14,149 +14,233 @@ #include <getopt.h> #include <errno.h> +#include "options.h" #include "nsgenbind-ast.h" +#include "webidl-ast.h" +#include "interface-map.h" #include "jsapi-libdom.h" -#include "options.h" +#include "duk-libdom.h" struct options *options; +enum bindingtype_e { + BINDINGTYPE_UNKNOWN, + BINDINGTYPE_JSAPI_LIBDOM, + BINDINGTYPE_DUK_LIBDOM, +}; + static struct options* process_cmdline(int argc, char **argv) { - int opt; - - options = calloc(1,sizeof(struct options)); - if (options == NULL) { - fprintf(stderr, "Allocation error\n"); - return NULL; - } - - while ((opt = getopt(argc, argv, "vgDW::d:I:o:h:")) != -1) { - switch (opt) { - case 'I': - options->idlpath = strdup(optarg); - break; - - case 'o': - options->outfilename = strdup(optarg); - break; - - case 'h': - options->hdrfilename = strdup(optarg); - break; - - case 'd': - options->depfilename = strdup(optarg); - break; - - case 'v': - options->verbose = true; - break; - - case 'D': - options->debug = true; - break; - - case 'g': - options->dbglog = true; - break; - - case 'W': - options->warnings = 1; /* warning flags */ - break; - - default: /* '?' */ - fprintf(stderr, - "Usage: %s [-v] [-g] [-D] [-W] [-d depfilename] [-I idlpath] [-o filename] [-h headerfile] inputfile\n", - argv[0]); - free(options); - return NULL; - - } - } - - if (optind >= argc) { - fprintf(stderr, "Error: expected input filename\n"); - free(options); - return NULL; - } - - options->infilename = strdup(argv[optind]); - - return options; + int opt; + + options = calloc(1,sizeof(struct options)); + if (options == NULL) { + fprintf(stderr, "Allocation error\n"); + return NULL; + } + + while ((opt = getopt(argc, argv, "vgDW::I:")) != -1) { + switch (opt) { + case 'I': + options->idlpath = strdup(optarg); + break; + + case 'v': + options->verbose = true; + break; + + case 'D': + options->debug = true; + break; + + case 'g': + options->dbglog = true; + break; + + case 'W': + if ((optarg == NULL) || + (strcmp(optarg, "all") == 0)) { + /* all warnings */ + options->warnings |= WARNING_ALL; + } else if (strcmp(optarg, "unimplemented") == 0) { + options->warnings |= WARNING_UNIMPLEMENTED; + } else if (strcmp(optarg, "duplicated") == 0) { + options->warnings |= WARNING_DUPLICATED; + } else { + fprintf(stderr, + "Unknown warning option \"%s\" valid options are: all, unimplemented\n", + optarg); + free(options); + return NULL; + + } + break; + + default: /* '?' */ + fprintf(stderr, + "Usage: %s [-v] [-g] [-D] [-W] [-I idlpath] inputfile outputdir\n", + argv[0]); + free(options); + return NULL; + } + } + + if (optind > (argc - 2)) { + fprintf(stderr, + "Error: expected input filename and output directory\n"); + free(options); + return NULL; + } + + options->infilename = strdup(argv[optind]); + + options->outdirname = strdup(argv[optind + 1]); + + return options; } -int main(int argc, char **argv) +static int webidl_file_cb(struct genbind_node *node, void *ctx) { - int res; - struct genbind_node *genbind_root; - - options = process_cmdline(argc, argv); - if (options == NULL) { - return 1; /* bad commandline */ - } - - if (options->verbose && - (options->outfilename == NULL)) { - fprintf(stderr, - "Error: output to stdout with verbose logging would fail\n"); - return 2; - } + struct webidl_node **webidl_ast = ctx; + char *filename; - if (options->depfilename != NULL && - options->outfilename == NULL) { - fprintf(stderr, - "Error: output to stdout with dep generation would fail\n"); - return 3; - } + filename = genbind_node_gettext(node); - if (options->depfilename != NULL && - options->infilename == NULL) { - fprintf(stderr, - "Error: input from stdin with dep generation would fail\n"); - return 3; - } + if (options->verbose) { + printf("Opening IDL file \"%s\"\n", filename); + } - if (options->depfilename != NULL) { - options->depfilehandle = fopen(options->depfilename, "w"); - if (options->depfilehandle == NULL) { - fprintf(stderr, - "Error: unable to open dep file\n"); - return 4; - } - fprintf(options->depfilehandle, - "%s %s :", options->depfilename, - options->outfilename); - } + return webidl_parsefile(filename, webidl_ast); +} - res = genbind_parsefile(options->infilename, &genbind_root); +static int genbind_load_idl(struct genbind_node *genbind, + struct webidl_node **webidl_out) +{ + int res; + struct genbind_node *binding_node; + + binding_node = genbind_node_find_type(genbind, NULL, + GENBIND_NODE_TYPE_BINDING); + + /* walk AST and load any web IDL files required */ + res = genbind_node_foreach_type( + genbind_node_getnode(binding_node), + GENBIND_NODE_TYPE_WEBIDL, + webidl_file_cb, + webidl_out); if (res != 0) { - fprintf(stderr, "Error: parse failed with code %d\n", res); - return res; - } - - if (options->verbose) { - genbind_ast_dump(genbind_root, 0); + fprintf(stderr, "Error: failed reading Web IDL\n"); + return -1; } - res = jsapi_libdom_output(options->outfilename, - options->hdrfilename, - genbind_root); + /* implements are implemented as mixins so intercalate them */ + res = webidl_intercalate_implements(*webidl_out); if (res != 0) { - fprintf(stderr, "Error: output failed with code %d\n", res); - if (options->outfilename != NULL) { - unlink(options->outfilename); - } - if (options->hdrfilename != NULL) { - unlink(options->hdrfilename); - } - return res; + fprintf(stderr, "Error: Failed to intercalate implements\n"); + return -1; } - if (options->depfilehandle != NULL) { - fputc('\n', options->depfilehandle); - fclose(options->depfilehandle); - } + return 0; +} + +/** + * get the type of binding + */ +static enum bindingtype_e genbind_get_type(struct genbind_node *node) +{ + struct genbind_node *binding_node; + const char *binding_type; + + binding_node = genbind_node_find_type(node, + NULL, + GENBIND_NODE_TYPE_BINDING); + if (binding_node == NULL) { + /* binding entry is missing which is invalid */ + return BINDINGTYPE_UNKNOWN; + } + + binding_type = genbind_node_gettext( + genbind_node_find_type( + genbind_node_getnode(binding_node), + NULL, + GENBIND_NODE_TYPE_TYPE)); + if (binding_type == NULL) { + fprintf(stderr, "Error: missing binding type\n"); + return BINDINGTYPE_UNKNOWN; + } + + if (strcmp(binding_type, "jsapi_libdom") == 0) { + return BINDINGTYPE_JSAPI_LIBDOM; + } + + if (strcmp(binding_type, "duk_libdom") == 0) { + return BINDINGTYPE_DUK_LIBDOM; + } + + fprintf(stderr, "Error: unsupported binding type \"%s\"\n", binding_type); + + return BINDINGTYPE_UNKNOWN; +} - return 0; -} +int main(int argc, char **argv) +{ + int res; + struct genbind_node *genbind_root = NULL; + struct webidl_node *webidl_root = NULL; + struct interface_map *interface_map = NULL; + enum bindingtype_e bindingtype; + + options = process_cmdline(argc, argv); + if (options == NULL) { + return 1; /* bad commandline */ + } + + /* parse binding */ + res = genbind_parsefile(options->infilename, &genbind_root); + if (res != 0) { + fprintf(stderr, "Error: parse failed with code %d\n", res); + return res; + } + + /* dump the binding AST */ + genbind_dump_ast(genbind_root); + + /* get type of binding */ + bindingtype = genbind_get_type(genbind_root); + if (bindingtype == BINDINGTYPE_UNKNOWN) { + return 3; + } + + /* load the IDL files specified in the binding */ + res = genbind_load_idl(genbind_root, &webidl_root); + if (res != 0) { + return 4; + } + + /* debug dump of web idl AST */ + webidl_dump_ast(webidl_root); + + /* generate map of WebIDL interfaces sorted by inheritance */ + res = interface_map_new(genbind_root, webidl_root, &interface_map); + if (res != 0) { + return 5; + } + + /* dump the interface mapping */ + interface_map_dump(interface_map); + interface_map_dumpdot(interface_map); + + /* generate binding */ + switch (bindingtype) { + case BINDINGTYPE_DUK_LIBDOM: + res = duk_libdom_output(interface_map); + break; + + default: + fprintf(stderr, "Unable to generate binding of this type\n"); + res = 7; + } + + return res; +} diff --git a/src/options.h b/src/options.h index 13c02be..02674b7 100644 --- a/src/options.h +++ b/src/options.h @@ -12,12 +12,7 @@ /** global options */ struct options { char *infilename; /**< binding source */ - - char *outfilename; /**< output source file */ - char *hdrfilename; /**< output header file */ - - char *depfilename; /**< dependancy output*/ - FILE *depfilehandle; /**< dependancy file handle */ + char *outdirname; /**< output directory */ char *idlpath; /**< path to IDL files */ bool verbose; /**< verbose processing */ @@ -30,13 +25,14 @@ extern struct options *options; enum opt_warnings { WARNING_UNIMPLEMENTED = 1, + WARNING_DUPLICATED = 2, }; -#define WARNING_ALL (WARNING_UNIMPLEMENTED) +#define WARNING_ALL (WARNING_UNIMPLEMENTED | WARNING_DUPLICATED) #define WARN(flags, msg, args...) do { \ if ((options->warnings & flags) != 0) { \ - fprintf(stderr, "%s: warning:"msg"\n", __func__, ## args); \ + fprintf(stderr, "%s: warning: "msg"\n", __func__, ## args); \ } \ } while(0) diff --git a/src/utils.c b/src/utils.c new file mode 100644 index 0000000..0952744 --- /dev/null +++ b/src/utils.c @@ -0,0 +1,160 @@ +/* utility functions + * + * This file is part of nsgenbind. + * Published under the MIT License, + * http://www.opensource.org/licenses/mit-license.php + * Copyright 2015 Vincent Sanders <vince@netsurf-browser.org> + */ + +#include <stdio.h> +#include <string.h> +#include <stdbool.h> +#include <errno.h> +#include <stdlib.h> +#include <sys/types.h> +#include <unistd.h> + +#include "options.h" +#include "utils.h" + +/* exported function documented in utils.h */ +char *genb_fpath(const char *fname) +{ + char *fpath; + int fpathl; + + fpathl = strlen(options->outdirname) + strlen(fname) + 2; + fpath = malloc(fpathl); + snprintf(fpath, fpathl, "%s/%s", options->outdirname, fname); + + return fpath; +} + +static char *genb_fpath_tmp(const char *fname) +{ + char *fpath; + int fpathl; + + fpathl = strlen(options->outdirname) + strlen(fname) + 3; + fpath = malloc(fpathl); + snprintf(fpath, fpathl, "%s/%s.%d", options->outdirname, fname, getpid()); + + return fpath; +} + +/* exported function documented in utils.h */ +FILE *genb_fopen(const char *fname, const char *mode) +{ + char *fpath; + FILE *filef; + + fpath = genb_fpath(fname); + + filef = fopen(fpath, mode); + if (filef == NULL) { + fprintf(stderr, "Error: unable to open file %s (%s)\n", + fpath, strerror(errno)); + free(fpath); + return NULL; + } + free(fpath); + + return filef; +} + +/* exported function documented in utils.h */ +FILE *genb_fopen_tmp(const char *fname) +{ + char *fpath; + FILE *filef; + + fpath = genb_fpath_tmp(fname); + + filef = fopen(fpath, "w+"); + if (filef == NULL) { + fprintf(stderr, "Error: unable to open file %s (%s)\n", + fpath, strerror(errno)); + free(fpath); + return NULL; + } + free(fpath); + + return filef; +} + +int genb_fclose_tmp(FILE *filef_tmp, const char *fname) +{ + char *fpath; + char *tpath; + FILE *filef; + char tbuf[1024]; + char fbuf[1024]; + size_t trd; + size_t frd; + + fpath = genb_fpath(fname); + tpath = genb_fpath_tmp(fname); + + filef = fopen(fpath, "r"); + if (filef == NULL) { + /* unable to open target file for comparison */ + + fclose(filef_tmp); /* close tmpfile */ + + remove(fpath); + rename(tpath, fpath); + } else { + rewind(filef_tmp); + + frd = fread(fbuf, 1, 1024, filef); + while (frd != 0) { + trd = fread(tbuf, 1, frd, filef_tmp); + if ((trd != frd) || + (memcmp(tbuf, fbuf, trd) != 0)) { + /* file doesnt match */ + fclose(filef_tmp); + fclose(filef); + + remove(fpath); + rename(tpath, fpath); + + goto close_done; + } + + frd = fread(fbuf, 1, 1024, filef); + } + + /* was the same kill temporary file */ + fclose(filef_tmp); + fclose(filef); + remove(tpath); + } + +close_done: + free(fpath); + free(tpath); + + return 0; +} + + +#ifdef NEED_STRNDUP + +char *strndup(const char *s, size_t n) +{ + size_t len; + char *s2; + + for (len = 0; len != n && s[len]; len++) + continue; + + s2 = malloc(len + 1); + if (!s2) + return 0; + + memcpy(s2, s, len); + s2[len] = 0; + return s2; +} + +#endif diff --git a/src/utils.h b/src/utils.h new file mode 100644 index 0000000..508d1c1 --- /dev/null +++ b/src/utils.h @@ -0,0 +1,46 @@ +/* utility helpers + * + * This file is part of nsnsgenbind. + * Licensed under the MIT License, + * http://www.opensource.org/licenses/mit-license.php + * Copyright 2015 Vincent Sanders <vince@netsurf-browser.org> + */ + +#ifndef nsgenbind_utils_h +#define nsgenbind_utils_h + +/** + * get a pathname with the output prefix prepended + * + * \param fname leaf filename. + * \return full prefixed path to file caller must free + */ +char *genb_fpath(const char *fname); + +/** + * Open file allowing for output path prefix + */ +FILE *genb_fopen(const char *fname, const char *mode); + +/** + * Open file allowing for output path prefix + * + * file is opened for reading/writing with a temporary suffix allowing for the + * matching close call to check the output is different before touching the + * target file. + */ +FILE *genb_fopen_tmp(const char *fname); + +/** + * Close file opened with genb_fopen + */ +int genb_fclose_tmp(FILE *filef, const char *fname); + +#ifdef _WIN32 +#define NEED_STRNDUP 1 +char *strndup(const char *s, size_t n); +#endif + +#define SLEN(x) (sizeof((x)) - 1) + +#endif diff --git a/src/webidl-ast.c b/src/webidl-ast.c index d75a186..87e3485 100644 --- a/src/webidl-ast.c +++ b/src/webidl-ast.c @@ -11,18 +11,25 @@ #include <stdio.h> #include <string.h> #include <errno.h> +#include <stdarg.h> +#include "utils.h" #include "webidl-ast.h" #include "options.h" +/** + * standard IO handle for parse trace logging. + */ +static FILE *webidl_parsetracef; + extern int webidl_debug; extern int webidl__flex_debug; extern void webidl_restart(FILE*); extern int webidl_parse(struct webidl_node **webidl_ast); struct webidl_node { - enum webidl_node_type type; - struct webidl_node *l; + enum webidl_node_type type; /* the type of the node */ + struct webidl_node *l; /* link to the next sibling node */ union { void *value; struct webidl_node *node; /* node has a list of nodes */ @@ -93,7 +100,7 @@ webidl_node_add(struct webidl_node *node, struct webidl_node *list) case WEBIDL_NODE_TYPE_EXTENDED_ATTRIBUTE: case WEBIDL_NODE_TYPE_ATTRIBUTE: case WEBIDL_NODE_TYPE_OPERATION: - case WEBIDL_NODE_TYPE_OPTIONAL_ARGUMENT: + case WEBIDL_NODE_TYPE_OPTIONAL: case WEBIDL_NODE_TYPE_ARGUMENT: case WEBIDL_NODE_TYPE_TYPE: case WEBIDL_NODE_TYPE_CONST: @@ -162,6 +169,26 @@ int webidl_cmp_node_type(struct webidl_node *node, void *ctx) return 0; } +static int webidl_enumerate_node(struct webidl_node *node, void *ctx) +{ + node = node; + (*((int *)ctx))++; + return 0; +} + +/* exported interface defined in nsgenbind-ast.h */ +int +webidl_node_enumerate_type(struct webidl_node *node, + enum webidl_node_type type) +{ + int count = 0; + webidl_node_for_each_type(node, + type, + webidl_enumerate_node, + &count); + return count; +} + /* exported interface defined in webidl-ast.h */ struct webidl_node * webidl_node_find(struct webidl_node *node, @@ -203,6 +230,7 @@ webidl_node_find_type(struct webidl_node *node, } +/* exported interface defined in webidl-ast.h */ struct webidl_node * webidl_node_find_type_ident(struct webidl_node *root_node, enum webidl_node_type type, @@ -230,6 +258,7 @@ webidl_node_find_type_ident(struct webidl_node *root_node, } +/* exported interface defined in webidl-ast.h */ char *webidl_node_gettext(struct webidl_node *node) { if (node != NULL) { @@ -238,16 +267,18 @@ char *webidl_node_gettext(struct webidl_node *node) case WEBIDL_NODE_TYPE_IDENT: case WEBIDL_NODE_TYPE_INTERFACE_INHERITANCE: case WEBIDL_NODE_TYPE_INTERFACE_IMPLEMENTS: + case WEBIDL_NODE_TYPE_LITERAL_STRING: return node->r.text; default: - break; + break; } } return NULL; } -int +/* exported interface defined in webidl-ast.h */ +int * webidl_node_getint(struct webidl_node *node) { if (node != NULL) { @@ -255,22 +286,25 @@ webidl_node_getint(struct webidl_node *node) case WEBIDL_NODE_TYPE_MODIFIER: case WEBIDL_NODE_TYPE_TYPE_BASE: case WEBIDL_NODE_TYPE_LITERAL_INT: - return node->r.number; + case WEBIDL_NODE_TYPE_SPECIAL: + case WEBIDL_NODE_TYPE_LITERAL_BOOL: + return &node->r.number; default: break; } } - return -1; - + return NULL; } +/* exported interface defined in webidl-ast.h */ enum webidl_node_type webidl_node_gettype(struct webidl_node *node) { return node->type; } +/* exported interface defined in webidl-ast.h */ struct webidl_node *webidl_node_getnode(struct webidl_node *node) { if (node != NULL) { @@ -281,7 +315,7 @@ struct webidl_node *webidl_node_getnode(struct webidl_node *node) case WEBIDL_NODE_TYPE_EXTENDED_ATTRIBUTE: case WEBIDL_NODE_TYPE_ATTRIBUTE: case WEBIDL_NODE_TYPE_OPERATION: - case WEBIDL_NODE_TYPE_OPTIONAL_ARGUMENT: + case WEBIDL_NODE_TYPE_OPTIONAL: case WEBIDL_NODE_TYPE_ARGUMENT: case WEBIDL_NODE_TYPE_TYPE: case WEBIDL_NODE_TYPE_CONST: @@ -294,6 +328,7 @@ struct webidl_node *webidl_node_getnode(struct webidl_node *node) } +/* exported interface defined in webidl-ast.h */ static const char *webidl_node_type_to_str(enum webidl_node_type type) { switch(type) { @@ -321,8 +356,8 @@ static const char *webidl_node_type_to_str(enum webidl_node_type type) case WEBIDL_NODE_TYPE_OPERATION: return "Operation"; - case WEBIDL_NODE_TYPE_OPTIONAL_ARGUMENT: - return "Argument(opt)"; + case WEBIDL_NODE_TYPE_OPTIONAL: + return "Optional"; case WEBIDL_NODE_TYPE_ARGUMENT: return "Argument"; @@ -348,24 +383,44 @@ static const char *webidl_node_type_to_str(enum webidl_node_type type) case WEBIDL_NODE_TYPE_CONST: return "Const"; + case WEBIDL_NODE_TYPE_LITERAL_NULL: + return "Literal (null)"; + case WEBIDL_NODE_TYPE_LITERAL_INT: return "Literal (int)"; + case WEBIDL_NODE_TYPE_LITERAL_BOOL: + return "Literal (bool)"; + + case WEBIDL_NODE_TYPE_LITERAL_FLOAT: + return "Literal (string)"; + + case WEBIDL_NODE_TYPE_LITERAL_STRING: + return "Literal (string)"; + case WEBIDL_NODE_TYPE_EXTENDED_ATTRIBUTE: return "Extended Attribute"; + case WEBIDL_NODE_TYPE_SPECIAL: + return "Special"; + default: return "Unknown"; } } - -int webidl_ast_dump(struct webidl_node *node, int indent) +/** + * Recursively dump the AST nodes increasing indent as appropriate + */ +static int webidl_ast_dump(FILE *dumpf, struct webidl_node *node, int indent) { - const char *SPACES=" "; char *txt; + const char *SPACES=" "; + char *txt; + int *value; while (node != NULL) { - printf("%.*s%s", indent, SPACES, webidl_node_type_to_str(node->type)); + fprintf(dumpf, "%.*s%s", indent, SPACES, + webidl_node_type_to_str(node->type)); txt = webidl_node_gettext(node); if (txt == NULL) { @@ -374,32 +429,61 @@ int webidl_ast_dump(struct webidl_node *node, int indent) next = webidl_node_getnode(node); if (next != NULL) { - printf("\n"); - webidl_ast_dump(next, indent + 2); + fprintf(dumpf, "\n"); + webidl_ast_dump(dumpf, next, indent + 2); } else { - /* not txt or node has to be an int */ - printf(": %d\n", webidl_node_getint(node)); + /* not txt or node try an int */ + value = webidl_node_getint(node); + if (value != NULL) { + fprintf(dumpf, ": %d\n", *value); + } else { + /* no value */ + fprintf(dumpf, "\n"); + } } } else { - printf(": \"%s\"\n", txt); + fprintf(dumpf, ": \"%s\"\n", txt); } node = node->l; } return 0; } +/* exported interface documented in webidl-ast.h */ +int webidl_dump_ast(struct webidl_node *node) +{ + FILE *dumpf; + + /* only dump AST to file if required */ + if (!options->debug) { + return 0; + } + + dumpf = genb_fopen("webidl-ast", "w"); + if (dumpf == NULL) { + return 2; + } + + webidl_ast_dump(dumpf, node, 0); + + fclose(dumpf); + + return 0; +} + +/* exported interface defined in webidl-ast.h */ static FILE *idlopen(const char *filename) { FILE *idlfile; char *fullname; - int fulllen; + int fulllen; if (options->idlpath == NULL) { if (options->verbose) { printf("Opening IDL file %s\n", filename); } return fopen(filename, "r"); - } + } fulllen = strlen(options->idlpath) + strlen(filename) + 2; fullname = malloc(fulllen); @@ -409,14 +493,15 @@ static FILE *idlopen(const char *filename) } idlfile = fopen(fullname, "r"); free(fullname); - + return idlfile; } +/* exported interface defined in webidl-ast.h */ int webidl_parsefile(char *filename, struct webidl_node **webidl_ast) { - FILE *idlfile; + int ret; idlfile = idlopen(filename); if (!idlfile) { @@ -426,14 +511,153 @@ int webidl_parsefile(char *filename, struct webidl_node **webidl_ast) return 2; } - if (options->debug) { + /* if debugging enabled enable parser tracing and send to file */ + if (options->debug) { + char *tracename; + int tracenamelen; webidl_debug = 1; webidl__flex_debug = 1; - } + + tracenamelen = SLEN("webidl--trace") + strlen(filename) + 1; + tracename = malloc(tracenamelen); + snprintf(tracename, tracenamelen,"webidl-%s-trace", filename); + webidl_parsetracef = genb_fopen(tracename, "w"); + free(tracename); + } else { + webidl_parsetracef = NULL; + } /* set flex to read from file */ webidl_restart(idlfile); /* parse the file */ - return webidl_parse(webidl_ast); + ret = webidl_parse(webidl_ast); + + /* close tracefile if open */ + if (webidl_parsetracef != NULL) { + fclose(webidl_parsetracef); + } + return ret; +} + +/* exported interface defined in webidl-ast.h */ +int webidl_fprintf(FILE *stream, const char *format, ...) +{ + va_list ap; + int ret; + + va_start(ap, format); + + if (webidl_parsetracef == NULL) { + ret = vfprintf(stream, format, ap); + } else { + ret = vfprintf(webidl_parsetracef, format, ap); + } + va_end(ap); + + return ret; +} + +/* unlink a child node from a parent */ +static int +webidl_unlink(struct webidl_node *parent, struct webidl_node *node) +{ + struct webidl_node *child; + + child = webidl_node_getnode(parent); + if (child == NULL) { + /* parent does not have children to remove node from */ + return -1; + } + + if (child == node) { + /* parent is pointing at the node we want to remove */ + parent->r.node = node->l; /* point parent at next sibing */ + node->l = NULL; + return 0; + } + + while (child->l != NULL) { + if (child->l == node) { + /* found node, unlink from list */ + child->l = node->l; + node->l = NULL; + return 0; + } + child = child->l; + } + return -1; /* failed to remove node */ +} + +static int implements_copy_nodes(struct webidl_node *src_node, + struct webidl_node *dst_node) +{ + struct webidl_node *src; + struct webidl_node *dst; + + src = webidl_node_getnode(src_node); + dst = webidl_node_getnode(dst_node); + + while (src != NULL) { + if (src->type == WEBIDL_NODE_TYPE_LIST) { + /** @todo technicaly this should copy WEBIDL_NODE_TYPE_INTERFACE_INHERITANCE */ + dst = webidl_node_new(src->type, dst, src->r.text); + } + src = src->l; + } + + dst_node->r.node = dst; + + return 0; +} + +static int +intercalate_implements(struct webidl_node *interface_node, void *ctx) +{ + struct webidl_node *implements_node; + struct webidl_node *implements_interface_node; + struct webidl_node *webidl_ast = ctx; + + implements_node = webidl_node_find_type( + webidl_node_getnode(interface_node), + NULL, + WEBIDL_NODE_TYPE_INTERFACE_IMPLEMENTS); + while (implements_node != NULL) { + + implements_interface_node = webidl_node_find_type_ident( + webidl_ast, + WEBIDL_NODE_TYPE_INTERFACE, + webidl_node_gettext(implements_node)); + + /* recurse, ensuring all subordinate interfaces have + * their implements intercalated first + */ + intercalate_implements(implements_interface_node, webidl_ast); + + implements_copy_nodes(implements_interface_node, interface_node); + + /* once we have copied the implemntation remove entry */ + webidl_unlink(interface_node, implements_node); + + implements_node = webidl_node_find_type( + webidl_node_getnode(interface_node), + implements_node, + WEBIDL_NODE_TYPE_INTERFACE_IMPLEMENTS); + } + return 0; +} + +/* exported interface defined in webidl-ast.h */ +int webidl_intercalate_implements(struct webidl_node *webidl_ast) +{ + /* for each interface: + * for each implements entry: + * find interface from implemets + * recusrse into that interface + * copy the interface into this one + */ + return webidl_node_for_each_type(webidl_ast, + WEBIDL_NODE_TYPE_INTERFACE, + intercalate_implements, + webidl_ast); } diff --git a/src/webidl-ast.h b/src/webidl-ast.h index 70518fd..25ef9a0 100644 --- a/src/webidl-ast.h +++ b/src/webidl-ast.h @@ -27,8 +27,9 @@ enum webidl_node_type { WEBIDL_NODE_TYPE_OPERATION, WEBIDL_NODE_TYPE_CONST, - WEBIDL_NODE_TYPE_OPTIONAL_ARGUMENT, + WEBIDL_NODE_TYPE_SPECIAL, WEBIDL_NODE_TYPE_ARGUMENT, + WEBIDL_NODE_TYPE_OPTIONAL, WEBIDL_NODE_TYPE_ELLIPSIS, WEBIDL_NODE_TYPE_TYPE, WEBIDL_NODE_TYPE_TYPE_BASE, @@ -39,6 +40,7 @@ enum webidl_node_type { WEBIDL_NODE_TYPE_LITERAL_INT, WEBIDL_NODE_TYPE_LITERAL_BOOL, WEBIDL_NODE_TYPE_LITERAL_FLOAT, + WEBIDL_NODE_TYPE_LITERAL_STRING, WEBIDL_NODE_TYPE_EXTENDED_ATTRIBUTE, @@ -62,9 +64,19 @@ enum webidl_type { }; enum webidl_type_modifier { + WEBIDL_TYPE_MODIFIER_NONE, WEBIDL_TYPE_MODIFIER_UNSIGNED, WEBIDL_TYPE_MODIFIER_UNRESTRICTED, - WEBIDL_TYPE_READONLY, + WEBIDL_TYPE_MODIFIER_READONLY, +}; + +/* the type of special node */ +enum webidl_type_special { + WEBIDL_TYPE_SPECIAL_GETTER, + WEBIDL_TYPE_SPECIAL_SETTER, + WEBIDL_TYPE_SPECIAL_CREATOR, + WEBIDL_TYPE_SPECIAL_DELETER, + WEBIDL_TYPE_SPECIAL_LEGACYCALLER, }; struct webidl_node; @@ -86,24 +98,34 @@ struct webidl_node *webidl_node_add(struct webidl_node *node, struct webidl_node /* node contents acessors */ char *webidl_node_gettext(struct webidl_node *node); struct webidl_node *webidl_node_getnode(struct webidl_node *node); -int webidl_node_getint(struct webidl_node *node); +int *webidl_node_getint(struct webidl_node *node); enum webidl_node_type webidl_node_gettype(struct webidl_node *node); /* node searches */ + +/** + * Iterate nodes children matching their type. + * + * For each child node where the type is matched the callback function is + * called with a context value. + */ int webidl_node_for_each_type(struct webidl_node *node, - enum webidl_node_type type, - webidl_callback_t *cb, + enum webidl_node_type type, + webidl_callback_t *cb, void *ctx); +int webidl_node_enumerate_type(struct webidl_node *node, + enum webidl_node_type type); + struct webidl_node * webidl_node_find(struct webidl_node *node, - struct webidl_node *prev, - webidl_callback_t *cb, + struct webidl_node *prev, + webidl_callback_t *cb, void *ctx); struct webidl_node * webidl_node_find_type(struct webidl_node *node, - struct webidl_node *prev, + struct webidl_node *prev, enum webidl_node_type type); struct webidl_node * @@ -112,10 +134,26 @@ webidl_node_find_type_ident(struct webidl_node *root_node, const char *ident); -/* debug dump */ -int webidl_ast_dump(struct webidl_node *node, int indent); -/** parse web idl file */ +/** + * parse web idl file into Abstract Syntax Tree + */ int webidl_parsefile(char *filename, struct webidl_node **webidl_ast); +/** + * dump AST to file + */ +int webidl_dump_ast(struct webidl_node *node); + +/** + * perform replacement of implements elements with copies of ast data + */ +int webidl_intercalate_implements(struct webidl_node *node); + +/** + * formatted printf to allow webidl trace data to be written to file. + */ +int webidl_fprintf(FILE *stream, const char *format, ...); + + #endif diff --git a/src/webidl-lexer.l b/src/webidl-lexer.l index 74b9bb8..8c68fdf 100644 --- a/src/webidl-lexer.l +++ b/src/webidl-lexer.l @@ -86,7 +86,7 @@ lineend ([\n\r]|{LS}|{PS}) hexdigit [0-9A-Fa-f] hexint 0(x|X){hexdigit}+ -decimalint 0|([1-9][0-9]*) +decimalint 0|([\+\-]?[1-9][0-9]*) octalint (0[0-8]+) @@ -207,6 +207,11 @@ void return TOK_VOID; readonly return TOK_READONLY; +Promise return TOK_PROMISE; + +iterable return TOK_ITERABLE; + +legacyiterable return TOK_LEGACYITERABLE; {identifier} { /* A leading "_" is used to escape an identifier from @@ -223,7 +228,7 @@ readonly return TOK_READONLY; {decimalfloat} yylval->text = strdup(yytext); return TOK_FLOAT_LITERAL; -\"{quotedstring}*\" yylval->text = strdup(yytext); return TOK_STRING_LITERAL; +\"{quotedstring}*\" yylval->text = strdup(yytext + 1); *(yylval->text + yyleng - 2) = 0; return TOK_STRING_LITERAL; {multicomment} { /* multicomment */ diff --git a/src/webidl-parser.y b/src/webidl-parser.y index 9324212..9cfd84e 100644 --- a/src/webidl-parser.y +++ b/src/webidl-parser.y @@ -9,6 +9,9 @@ * * Derived from the the grammar in apendix A of W3C WEB IDL * http://www.w3.org/TR/WebIDL/ + * + * WebIDL now has a second edition draft (mid 2015) that the dom and + * html specs are using. https://heycam.github.io/webidl */ #include <stdio.h> @@ -17,11 +20,17 @@ #include <stdint.h> #include <math.h> -#include "webidl-ast.h" +#define YYFPRINTF webidl_fprintf +#define YY_LOCATION_PRINT(File, Loc) \ + webidl_fprintf(File, "%d.%d-%d.%d", \ + (Loc).first_line, (Loc).first_column, \ + (Loc).last_line, (Loc).last_column) #include "webidl-parser.h" #include "webidl-lexer.h" +#include "webidl-ast.h" + char *errtxt; static void @@ -77,6 +86,8 @@ webidl_error(YYLTYPE *locp, struct webidl_node **winbind_ast, const char *str) %token TOK_INFINITY %token TOK_INHERIT %token TOK_INTERFACE +%token TOK_ITERABLE +%token TOK_LEGACYITERABLE %token TOK_LONG %token TOK_MODULE %token TOK_NAN @@ -88,6 +99,7 @@ webidl_error(YYLTYPE *locp, struct webidl_node **winbind_ast, const char *str) %token TOK_OPTIONAL %token TOK_OR %token TOK_PARTIAL +%token TOK_PROMISE %token TOK_RAISES %token TOK_READONLY %token TOK_SETRAISES @@ -105,12 +117,12 @@ webidl_error(YYLTYPE *locp, struct webidl_node **winbind_ast, const char *str) %token TOK_POUND_SIGN -%token <text> TOK_IDENTIFIER -%token <value> TOK_INT_LITERAL -%token <text> TOK_FLOAT_LITERAL -%token <text> TOK_STRING_LITERAL -%token <text> TOK_OTHER_LITERAL -%token <text> TOK_JAVADOC +%token <text> TOK_IDENTIFIER +%token <value> TOK_INT_LITERAL +%token <text> TOK_FLOAT_LITERAL +%token <text> TOK_STRING_LITERAL +%token <text> TOK_OTHER_LITERAL +%token <text> TOK_JAVADOC %type <text> Inheritance @@ -143,6 +155,9 @@ webidl_error(YYLTYPE *locp, struct webidl_node **winbind_ast, const char *str) %type <node> Const %type <node> Operation +%type <node> SpecialOperation +%type <node> Specials +%type <node> Special %type <node> OperationRest %type <node> OptionalIdentifier @@ -153,6 +168,10 @@ webidl_error(YYLTYPE *locp, struct webidl_node **winbind_ast, const char *str) %type <text> ArgumentName %type <text> ArgumentNameKeyword %type <node> Ellipsis +%type <node> Iterable +%type <node> OptionalType +%type <node> Default +%type <node> DefaultValue %type <node> Type %type <node> ReturnType @@ -165,6 +184,7 @@ webidl_error(YYLTYPE *locp, struct webidl_node **winbind_ast, const char *str) %type <node> FloatType %type <node> UnsignedIntegerType %type <node> IntegerType +%type <node> PromiseType %type <node> TypeSuffix %type <node> TypeSuffixStartingWithArray @@ -356,7 +376,7 @@ InterfaceMembers: if (ident_node == NULL) { /* something with no ident - possibly constructors? */ - /* @todo understand this abtter */ + /* @todo understand this better */ $$ = webidl_node_prepend($1, $3); @@ -389,11 +409,17 @@ InterfaceMembers: } ; - /* [10] */ + /* [10] + * SE[10] + * Second edition actually splits up AttributeOrOperation completely + * here we "just" add Iterable as thats what the specs use + */ InterfaceMember: Const | AttributeOrOperation + | + Iterable ; /* [11] */ @@ -426,8 +452,14 @@ PartialDictionary: /* [15] */ Default: /* empty */ + { + $$ = NULL; + } | '=' DefaultValue + { + $$ = $2; + } ; @@ -436,6 +468,9 @@ DefaultValue: ConstValue | TOK_STRING_LITERAL + { + $$ = webidl_node_new(WEBIDL_NODE_TYPE_LITERAL_STRING, NULL, $1); + } ; /* [17] */ @@ -474,18 +509,28 @@ Enum: } ; -/* [21] */ + /* Second edition changes enumeration rules to allow trailing comma */ + + /* SE[20] */ EnumValueList: - TOK_STRING_LITERAL EnumValues + TOK_STRING_LITERAL EnumValueListComma ; -/* [22] */ -EnumValues: + /* SE[21] */ +EnumValueListComma: + ',' EnumValueListString + | /* empty */ + ; + + /* SE[22] */ +EnumValueListString: + TOK_STRING_LITERAL EnumValueListComma | - ',' TOK_STRING_LITERAL EnumValues + /* empty */ ; + /* [23] - bug in w3c grammar? it doesnt list the equals as a terminal */ CallbackRest: TOK_IDENTIFIER '=' ReturnType '(' ArgumentList ')' ';' @@ -658,7 +703,7 @@ Attribute: /* deal with readonly modifier */ if ($2) { - attribute = webidl_node_new(WEBIDL_NODE_TYPE_MODIFIER, attribute, (void *)WEBIDL_TYPE_READONLY); + attribute = webidl_node_new(WEBIDL_NODE_TYPE_MODIFIER, attribute, (void *)WEBIDL_TYPE_MODIFIER_READONLY); } $$ = webidl_node_new(WEBIDL_NODE_TYPE_ATTRIBUTE, NULL, attribute); @@ -692,55 +737,94 @@ ReadOnly: } ; - /* [35] */ + /* SE[47] */ Operation: - Qualifiers OperationRest + ReturnType OperationRest + { + /* put return type on the operation */ + $2 = webidl_node_prepend($1, $2); + + $$ = webidl_node_new(WEBIDL_NODE_TYPE_OPERATION, NULL, $2); + } + | + SpecialOperation { - /* @todo fix qualifiers */ - $$ = webidl_node_new(WEBIDL_NODE_TYPE_OPERATION, NULL, $2); + $$ = webidl_node_new(WEBIDL_NODE_TYPE_OPERATION, NULL, $1); } ; - /* [36] */ -Qualifiers: - TOK_STATIC - | - Specials + /* SE[48] */ +SpecialOperation: + Special Specials ReturnType OperationRest + { + /* put return type on the operation */ + $$ = webidl_node_prepend($4, $3); + + /* specials */ + $$ = webidl_node_prepend($$, $2); + + /* special */ + $$ = webidl_node_prepend($$, $1); + } ; - /* [37] */ + /* SE[49] */ Specials: /* empty */ + { + $$ = NULL; + } | Special Specials + { + $$ = webidl_node_prepend($2, $1); + } ; - /* [38] */ + /* SE[50] */ Special: TOK_GETTER + { + $$ = webidl_node_new(WEBIDL_NODE_TYPE_SPECIAL, + NULL, (void *)WEBIDL_TYPE_SPECIAL_GETTER); + } | TOK_SETTER + { + $$ = webidl_node_new(WEBIDL_NODE_TYPE_SPECIAL, + NULL, (void *)WEBIDL_TYPE_SPECIAL_SETTER); + } | TOK_CREATOR + { + /* second edition removed this special but teh + specifications still use it! + */ + $$ = webidl_node_new(WEBIDL_NODE_TYPE_SPECIAL, + NULL, (void *)WEBIDL_TYPE_SPECIAL_CREATOR); + } | TOK_DELETER + { + $$ = webidl_node_new(WEBIDL_NODE_TYPE_SPECIAL, + NULL, (void *)WEBIDL_TYPE_SPECIAL_DELETER); + } | TOK_LEGACYCALLER + { + $$ = webidl_node_new(WEBIDL_NODE_TYPE_SPECIAL, + NULL, (void *)WEBIDL_TYPE_SPECIAL_LEGACYCALLER); + } ; - /* [39] */ + /* SE[51] */ OperationRest: - ReturnType OptionalIdentifier '(' ArgumentList ')' ';' + OptionalIdentifier '(' ArgumentList ')' ';' { - struct webidl_node *arglist; - - /* put return type in argument list */ - arglist = webidl_node_prepend($4, $1); - /* argument list */ - $$ = webidl_node_new(WEBIDL_NODE_TYPE_LIST, NULL, arglist); + $$ = webidl_node_new(WEBIDL_NODE_TYPE_LIST, NULL, $3); - $$ = webidl_node_prepend($$, $2); /* identifier */ + $$ = webidl_node_prepend($1, $$); /* identifier */ } ; @@ -801,8 +885,9 @@ OptionalOrRequiredArgument: { struct webidl_node *argument; argument = webidl_node_new(WEBIDL_NODE_TYPE_IDENT, NULL, $3); + argument = webidl_node_new(WEBIDL_NODE_TYPE_OPTIONAL, argument, $4); argument = webidl_node_prepend(argument, $2); /* add type node */ - $$ = webidl_node_new(WEBIDL_NODE_TYPE_OPTIONAL_ARGUMENT, NULL, argument); + $$ = webidl_node_new(WEBIDL_NODE_TYPE_ARGUMENT, NULL, argument); } | Type Ellipsis ArgumentName @@ -835,6 +920,32 @@ Ellipsis: } ; + /* SE[59] */ +Iterable: + TOK_ITERABLE '<' Type OptionalType '>' ';' + { + $$ = NULL; + } + | + TOK_LEGACYITERABLE '<' Type '>' ';' + { + $$ = NULL; + } + ; + + /* SE[60] */ +OptionalType: + /* empty */ + { + $$ = NULL; + } + | + ',' Type + { + $$ = NULL; + } + ; + /* [47] */ ExceptionMember: Const @@ -1301,13 +1412,22 @@ UnionMemberTypes: TOK_OR UnionMemberType UnionMemberTypes ; - /* [62] */ + /* [62] + * SE[78] + * Second edition adds several types + */ NonAnyType: PrimitiveType TypeSuffix { $$ = webidl_node_prepend($1, $2); } | + PromiseType TypeSuffix + { + /* second edition adds promise types */ + $$ = webidl_node_prepend($1, $2); + } + | TOK_STRING TypeSuffix { $$ = webidl_node_new(WEBIDL_NODE_TYPE_TYPE_BASE, $2, (void *)WEBIDL_TYPE_STRING); @@ -1442,6 +1562,14 @@ OptionalLong: } ; +/* SE[87] */ +PromiseType: + TOK_PROMISE '<' ReturnType '>' + { + $$ = NULL; + } + ; + /* [70] */ TypeSuffix: /* empty */ |