<commit_before>/* 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 <NAME> <<EMAIL>>
 */

#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 "duckky"

#define NSGENBIND_PREAMBLE \
"/* Generated by nsgenbind\n"	\
" *\n"								\
" * nsgenbind is published under the MIT Licence.\n"		\
" * nsgenbind is similar to a compiler is a purely transformative tool which\n"\
" * explicitly makes no copyright claim on this generated output\n"\
" */"

/**
 * Generate a C class name for the interface.
 *
 * The IDL interface names are camelcase and not similar to libdom naming so it
 *  is necessary to convert them to a libdom compatible class name. This
 *  implementation is simple ASCII capable only and cannot cope with multibyte
 *  codepoints.
 *
 * The algorithm is:
 *  - copy characters to output lowering their case
 *  - if the previous character in the input name was uppercase and the current
 *    one is lowercase insert an underscore before the *previous* character.
 */
static char *gen_class_name(struct interface_map_entry *interfacee)
{
        const char *inc;
        char *outc;
        char *name;
        int wasupper;

        /* enpty strings are a bad idea */
        if ((interfacee->name == NULL) || (interfacee->name[0] == 0)) {
                return NULL;
        }

        /* allocate result buffer as twice the input length as thats the
         * absolute worst case.
         */
        name = calloc(2, strlen(interfacee->name));

        outc = name;
        inc = interfacee->name;
        wasupper = 0;

        /* first character handled separately as inserting a leading underscore
         * is undesirable
         */
        *outc++ = tolower(*inc++);
        /* copy input to output */
        while (*inc != 0) {
                /* ugly hack as html IDL is always prefixed uppercase and needs
                 * an underscore there
                 */
                if ((inc == (interfacee->name + 4)) &&
                    (interfacee->name[0] == 'H') &&
                    (interfacee->name[1] == 'T') &&
                    (interfacee->name[2] == 'M') &&
                    (interfacee->name[3] == 'L') &&
                    (islower(inc[1]) == 0)) {
                        *outc++ = '_';
                }
                if ((islower(*inc) != 0) && (wasupper != 0)) {
                        *outc = *(outc - 1);
                        *(outc - 1) = '_';
                        outc++;
                        wasupper = 0;
                } else {
                        wasupper = isupper(*inc);
                }
                *outc++ = tolower(*inc++);
        }
        return name;
}

/**
 * output character data of node of given type.
 *
 * used for pre/pro/epi/post sections
 */
static int
output_cdata(FILE* outf,
             struct genbind_node *node,
             enum genbind_node_type nodetype)
{
        char *cdata;
        cdata = genbind_node_gettext(
                genbind_node_find_type(
                        genbind_node_getnode(node),
                        NULL, nodetype));
        if (cdata != NULL) {
                fprintf(outf, "%s", cdata);
        }
        return 0;
}

/**
 * Output code to create a private structure
 *
 */
static int output_duckky_create_private(FILE* outf, char *class_name)
{
        fprintf(outf, "\t/* 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, PRIVATE_MAGIC)\n\n");

        return 0;
}

/**
 * generate code that gets a private pointer
 */
static int output_ducky_safe_get_private(FILE* outf, char *class_name, int idx)
{
        fprintf(outf,
                "\t%s_private_t *priv = dukky_get_private(ctx, %d);\n",
                class_name, idx);
        fprintf(outf, "\tif (priv == NULL) return 0;\n\n");
        return 0;
}


/**
 * generate the interface constructor
 */
static int
output_interface_constructor(FILE* outf, struct interface_map_entry *interfacee)
{
        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_duckky_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_ducky_safe_get_private(outf, interfacee->class_name, 0);

        /* generate call to finaliser */
        fprintf(outf,
                "\t%s_%s___fini(ctx, priv);\n",
                DLPFX, interfacee->class_name);

        fprintf(outf,"\tfree(priv);\n");
        fprintf(outf,"\treturn 0;\n");

        fprintf(outf, "}\n\n");

        return 0;
}

/**
 * generate an initialisor call to parent interface
 */
static int
output_interface_inherit_init(FILE* outf,
                              struct interface_map_entry *interfacee,
                              struct interface_map_entry *inherite)
{
        struct genbind_node *init_node;
        struct genbind_node *inh_init_node;
        struct genbind_node *param_node;
        struct genbind_node *inh_param_node;

        /* only need to call parent initialisor if there is one */
        if (inherite == NULL) {
                return 0;
        }

        /* find the initialisor method on the class (if any) */
        init_node = 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_METHOD);
        }

        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 code that gets a prototype by name
 */
static int output_get_prototype(FILE* outf, const char *interface_name)
{
        char *proto_name;
        int pnamelen;

        /* duplicate the interface name in upper case */
        pnamelen = strlen(interface_name) + 1; /* allow for null byte */
        proto_name = malloc(pnamelen);
        for ( ; pnamelen >= 0; pnamelen--) {
                proto_name[pnamelen] = toupper(interface_name[pnamelen]);
        }
        fprintf(outf, "\t/* get prototype */\n");
        fprintf(outf, "\tduk_get_global_string(ctx, PROTO_MAGIC);\n");
        fprintf(outf, "\tduk_get_prop_string(ctx, -1, PROTO_NAME(%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, INIT_MAGIC);\n");
        fprintf(outf, "\tduk_pop(ctx);\n\n");

        return 0;
}

/**
 * generate code that adds a method in a prototype
 */
static int
output_add_method(FILE* outf, char *class_name, char *method, int argc)
{
        fprintf(outf, "\t/* Add a method */\n");
        fprintf(outf, "\tduk_dup(ctx, 0);\n");
        fprintf(outf, "\tduk_push_string(ctx, %s);\n", method);
        fprintf(outf, "\tduk_push_c_function(ctx, %s_%s_%s, ",
                DLPFX, class_name, method);
        if (argc == -1) {
                fprintf(outf, "DUK_VARARGS);\n");

        } else {
                fprintf(outf, "%d);\n", argc);
        }
        fprintf(outf, "\tDUKKY_DUMP_STACK(ctx);\n");
        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;
}

/**
 * count the number of arguments to an operation
 *
 * \todo this needs to consider multiple lists (overloaded calls?), varadic
 *       parameters.
 *
 * \retuen number of arguments or -1 if variable
 */
static int count_operation_arguments(struct webidl_node *node)
{
        int argc;
        struct webidl_node *list_node;
        list_node = webidl_node_find_type(
                webidl_node_getnode(node),
                NULL,
                WEBIDL_NODE_TYPE_LIST);
        if (list_node == NULL) {
                /** \todo is having no argument list an error or a warning? */
                return 0;
        }
        argc = webidl_node_enumerate_type(webidl_node_getnode(list_node),
                                          WEBIDL_NODE_TYPE_OPTIONAL_ARGUMENT);
        if (argc != 0) {
                return -1;
        }
        return webidl_node_enumerate_type(webidl_node_getnode(list_node),
                                          WEBIDL_NODE_TYPE_ARGUMENT);
}

/**
 * generate a prototype add for a single class method
 */
static int
output_prototype_method(FILE* outf,
                        struct interface_map_entry *interfacee,
                        struct webidl_node *op_node)
{
        char *op_name;
        int op_argc;

        op_name = webidl_node_gettext(
                webidl_node_find_type(
                        webidl_node_getnode(op_node),
                        NULL,
                        WEBIDL_NODE_TYPE_IDENT));

        op_argc = count_operation_arguments(op_node);

        output_add_method(outf, interfacee->class_name, op_name, op_argc);

        return 0;

}

/**
 * generate prototype method definitions
 */
static int
output_prototype_methods(FILE *outf, struct interface_map_entry *interfacee)
{
        int res;
        struct webidl_node *list_node;
        struct webidl_node *op_node; /* operation on list node */

        /* iterate each list node within the interface */
        list_node = webidl_node_find_type(
                webidl_node_getnode(interfacee->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 = output_prototype_method(outf, interfacee, op_node);
                        if (res != 0) {
                                return res;
                        }

                        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(interfacee->node),
                        list_node,
                        WEBIDL_NODE_TYPE_LIST);
        }

        return 0;

}

static int
output_populate_rw_property(FILE* outf, const char *class_name, const char *property)
{
        fprintf(outf, "\t/* Add read/write property */\n");
        fprintf(outf, "\tduk_dup(ctx, 0);\n");
        fprintf(outf, "\tduk_push_string(ctx, \"%s\");\n", property);
        fprintf(outf,
                "\tduk_push_c_function(ctx, %s_%s_%s_getter, 0);\n",
                DLPFX, class_name, property);
        fprintf(outf,
                "\tduk_push_c_function(ctx, %s_%s_%s_setter, 1);\n",
                DLPFX, class_name, property);
        fprintf(outf, "\tduk_def_prop(ctx, -4, DUK_DEFPROP_HAVE_GETTER |\n");
        fprintf(outf, "\t             DUK_DEFPROP_HAVE_SETTER |\n");
        fprintf(outf, "\t             DUK_DEFPROP_HAVE_ENUMERABLE | DUK_DEFPROP_ENUMERABLE |\n");
        fprintf(outf, "\t             DUK_DEFPROP_HAVE_CONFIGURABLE);\n");
        fprintf(outf, "\tduk_pop(ctx)\n\n");

        return 0;
}

static int
output_populate_ro_property(FILE* outf, const char *class_name, const char *property)
{
        fprintf(outf, "\t/* Add readonly property */\n");
        fprintf(outf, "\tduk_dup(ctx, 0);\n");
        fprintf(outf, "\tduk_push_string(ctx, \"%s\");\n", property);
        fprintf(outf,
                "\tduk_push_c_function(ctx, %s_%s_%s_getter, 0);\n",
                DLPFX, class_name, property);
        fprintf(outf,
                "\tduk_def_prop(ctx, -4, DUK_DEFPROP_HAVE_GETTER |\n");
        fprintf(outf, "\t             DUK_DEFPROP_HAVE_ENUMERABLE | DUK_DEFPROP_ENUMERABLE |\n");
        fprintf(outf, "\t             DUK_DEFPROP_HAVE_CONFIGURABLE);\n");
        fprintf(outf, "\tduk_pop(ctx)\n\n");

        return 0;
}

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);
        } else {
                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;

        for (attrc = 0; attrc < interfacee->attributec; attrc++) {
                output_prototype_attribute(outf,
                                        interfacee,
                                        interfacee->attributev + attrc);
        }

        return 0;
}

/**
 * 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));


        fprintf(outf, "\tduk_dup(ctx, 0);\n");
        fprintf(outf, "\tduk_push_string(ctx, \"%s\");\n", constante->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 prototype constant definitions
 */
static int
output_prototype_constants(FILE *outf, struct interface_map_entry *interfacee)
{
        int attrc;

        for (attrc = 0; attrc < interfacee->constantc; attrc++) {
                output_prototype_constant(outf, interfacee->constantv + attrc);
        }

        return 0;
}

/**
 * generate the interface prototype creator
 */
static int
output_interface_prototype(FILE* outf,
                           struct interface_map_entry *interfacee,
                           struct interface_map_entry *inherite)
{
        /* prototype definition */
        fprintf(outf,
                "duk_ret_t %s_%s___proto(duk_context *ctx)\n",
                DLPFX, interfacee->class_name);
        fprintf(outf,"{\n");

        /* generate prototype chaining if interface has a parent */
        if (inherite != NULL) {
                fprintf(outf,
                      "\t/* Set this prototype's prototype (left-parent) */\n");
                output_get_prototype(outf, inherite->name);
                fprintf(outf, "\tduk_set_prototype(ctx, 0);\n\n");
        }

        /* generate setting of methods */
        output_prototype_methods(outf, interfacee);

        /* generate setting of attributes */
        output_prototype_attributes(outf, interfacee);

        /* generate setting of constants */
        output_prototype_constants(outf, interfacee);

        /* generate setting of destructor */
        output_set_destructor(outf, interfacee->class_name, 0);

        /* generate setting of constructor */
        output_set_constructor(outf,
                               interfacee->class_name,
                               0,
                               interfacee->class_init_argc);

        fprintf(outf,"\treturn 1; /* The prototype object */\n");

        fprintf(outf, "}\n\n");

        return 0;
}

/**
 * generate 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, PRIVATE_MAGIC);\n");
        fprintf(outf, "\tpriv = duk_get_pointer(ctx, -1);\n");
        fprintf(outf, "\tduk_pop_2(ctx);\n");
        fprintf(outf, "\tif (priv == NULL) return 0; /* can do? No can do. */\n\n");
        return 0;
}

/**
 * generate a single class method for an interface operation
 */
static int
output_interface_operation(FILE* outf,
                           struct interface_map_entry *interfacee,
                           struct webidl_node *op_node)
{
        char *op_name;
        struct genbind_node *method_node;

        op_name = webidl_node_gettext(
                webidl_node_find_type(
                        webidl_node_getnode(op_node),
                        NULL,
                        WEBIDL_NODE_TYPE_IDENT));

        method_node = genbind_node_find_method_ident(interfacee->class,
                                                     NULL,
                                                     GENBIND_METHOD_TYPE_METHOD,
                                                     op_name);

        /* method definition */
        fprintf(outf,
                "static duk_ret_t %s_%s_%s(duk_context *ctx)\n",
                DLPFX, interfacee->class_name, op_name);
        fprintf(outf,"{\n");
<commit_msg>Clean up code generation functions<commit_after>/* 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 <NAME> <<EMAIL>>
 */

#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 "duckky"

#define NSGENBIND_PREAMBLE \
"/* Generated by nsgenbind\n"	\
" *\n"								\
" * nsgenbind is published under the MIT Licence.\n"		\
" * nsgenbind is similar to a compiler is a purely transformative tool which\n"\
" * explicitly makes no copyright claim on this generated output\n"\
" */"

/**
 * 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, PRIVATE_MAGIC)\n\n");

        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, PRIVATE_MAGIC);\n",idx);
        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 code that gets a prototype by name
 */
static int output_get_prototype(FILE* outf, const char *interface_name)
{
        char *proto_name;
        int pnamelen;

        /* duplicate the interface name in upper case */
        pnamelen = strlen(interface_name) + 1; /* allow for null byte */
        proto_name = malloc(pnamelen);
        for ( ; pnamelen >= 0; pnamelen--) {
                proto_name[pnamelen] = toupper(interface_name[pnamelen]);
        }
        fprintf(outf, "\t/* get prototype */\n");
        fprintf(outf, "\tduk_get_global_string(ctx, PROTO_MAGIC);\n");
        fprintf(outf, "\tduk_get_prop_string(ctx, -1, PROTO_NAME(%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, INIT_MAGIC);\n");
        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, char *class_name, char *method, int argc)
{
        fprintf(outf, "\t/* Add a method */\n");
        fprintf(outf, "\tduk_dup(ctx, 0);\n");
        fprintf(outf, "\tduk_push_string(ctx, %s);\n", method);
        fprintf(outf, "\tduk_push_c_function(ctx, %s_%s_%s, ",
                DLPFX, class_name, method);
        if (argc == -1) {
                fprintf(outf, "DUK_VARARGS);\n");
        } else {
                fprintf(outf, "%d);\n", argc);
        }
        output_dump_stack(outf);
        fprintf(outf, "\tduk_def_prop(ctx, -3,\n");
        fprintf(outf, "\t             DUK_DEFPROP_HAVE_VALUE |\n");
        fprintf(outf, "\t             DUK_DEFPROP_HAVE_WRITABLE |\n");
        fprintf(outf, "\t             DUK_DEFPROP_HAVE_ENUMERABLE | DUK_DEFPROP_ENUMERABLE |\n");
        fprintf(outf, "\t             DUK_DEFPROP_HAVE_CONFIGURABLE);\n");
        fprintf(outf, "\tduk_pop(ctx)\n\n");

        return 0;
}

/**
 * Generate source to populate a read/write property on a prototype
 */
static int
output_populate_rw_property(FILE* outf, const char *class_name, const char *property)
{
        fprintf(outf, "\t/* Add read/write property */\n");
        fprintf(outf, "\tduk_dup(ctx, 0);\n");
        fprintf(outf, "\tduk_push_string(ctx, \"%s\");\n", property);
        fprintf(outf,
                "\tduk_push_c_function(ctx, %s_%s_%s_getter, 0);\n",
                DLPFX, class_name, property);
        fprintf(outf,
                "\tduk_push_c_function(ctx, %s_%s_%s_setter, 1);\n",
                DLPFX, class_name, property);
        fprintf(outf, "\tduk_def_prop(ctx, -4, DUK_DEFPROP_HAVE_GETTER |\n");
        fprintf(outf, "\t             DUK_DEFPROP_HAVE_SETTER |\n");
        fprintf(outf, "\t             DUK_DEFPROP_HAVE_ENUMERABLE | DUK_DEFPROP_ENUMERABLE |\n");
        fprintf(outf, "\t             DUK_DEFPROP_HAVE_CONFIGURABLE);\n");
        fprintf(outf, "\tduk_pop(ctx)\n\n");

        return 0;
}

/**
 * Generate source to populate a readonly property on a prototype
 */
static int
output_populate_ro_property(FILE* outf, const char *class_name, const char *property)
{
        fprintf(outf, "\t/* Add readonly property */\n");
        fprintf(outf, "\tduk_dup(ctx, 0);\n");
        fprintf(outf, "\tduk_push_string(ctx, \"%s\");\n", property);
        fprintf(outf,
                "\tduk_push_c_function(ctx, %s_%s_%s_getter, 0);\n",
                DLPFX, class_name, property);
        fprintf(outf,
                "\tduk_def_prop(ctx, -4, DUK_DEFPROP_HAVE_GETTER |\n");
        fprintf(outf, "\t             DUK_DEFPROP_HAVE_ENUMERABLE | DUK_DEFPROP_ENUMERABLE |\n");
        fprintf(outf, "\t             DUK_DEFPROP_HAVE_CONFIGURABLE);\n");
        fprintf(outf, "\tduk_pop(ctx)\n\n");

        return 0;
}

/**
 * Generate source to add a constant int value on a prototype
 */
static int
output_prototype_constant_int(FILE *outf, const char *constant_name, int value)
{
        fprintf(outf, "\tduk_dup(ctx, 0);\n");
        fprintf(outf, "\tduk_push_string(ctx, \"%s\");\n", constant_name);
        fprintf(outf, "\tduk_push_int(ctx, %d);\n", value);
        fprintf(outf, "\tduk_def_prop(ctx, -3, DUK_DEFPROP_HAVE_VALUE |\n");
        fprintf(outf, "\t             DUK_DEFPROP_HAVE_WRITABLE | DUK_DEFPROP_HAVE_ENUMERABLE |\n");
        fprintf(outf, "\t             DUK_DEFPROP_ENUMERABLE | DUK_DEFPROP_HAVE_CONFIGURABLE);\n");
        fprintf(outf, "\tduk_pop(ctx)\n\n");
        return 0;
}

/**
 * generate code that gets a private pointer for a method
 */
static int
output_get_method_private(FILE* outf, char *class_name)
{
        fprintf(outf, "\t/* Get private data for method */\n");
        fprintf(outf, "\t%s_private_t *priv = NULL;\n", class_name);
        fprintf(outf, "\tduk_push_this(ctx);\n");
        fprintf(outf, "\tduk_get_prop_string(ctx, -1, PRIVATE_MAGIC);\n");
        fprintf(outf, "\tpriv = duk_get_pointer(ctx, -1);\n");
        fprintf(outf, "\tduk_pop_2(ctx);\n");
        fprintf(outf, "\tif (priv == NULL) return 0; /* can do? No can do. */\n\n");
        return 0;
}

/**
 * Generate a C class name for the interface.
 *
 * The IDL interface names are camelcase and not similar to libdom naming so it
 *  is necessary to convert them to a libdom compatible class name. This
 *  implementation is simple ASCII capable only and cannot cope with multibyte
 *  codepoints.
 *
 * The algorithm is:
 *  - copy characters to output lowering their case
 *  - if the previous character in the input name was uppercase and the current
 *    one is lowercase insert an underscore before the *previous* character.
 */
static char *gen_class_name(struct interface_map_entry *interfacee)
{
        const char *inc;
        char *outc;
        char *name;
        int wasupper;

        /* enpty strings are a bad idea */
        if ((interfacee->name == NULL) || (interfacee->name[0] == 0)) {
                return NULL;
        }

        /* allocate result buffer as twice the input length as thats the
         * absolute worst case.
         */
        name = calloc(2, strlen(interfacee->name));

        outc = name;
        inc = interfacee->name;
        wasupper = 0;

        /* first character handled separately as inserting a leading underscore
         * is undesirable
         */
        *outc++ = tolower(*inc++);
        /* copy input to output */
        while (*inc != 0) {
                /* ugly hack as html IDL is always prefixed uppercase and needs
                 * an underscore there
                 */
                if ((inc == (interfacee->name + 4)) &&
                    (interfacee->name[0] == 'H') &&
                    (interfacee->name[1] == 'T') &&
                    (interfacee->name[2] == 'M') &&
                    (interfacee->name[3] == 'L') &&
                    (islower(inc[1]) == 0)) {
                        *outc++ = '_';
                }
                if ((islower(*inc) != 0) && (wasupper != 0)) {
                        *outc = *(outc - 1);
                        *(outc - 1) = '_';
                        outc++;
                        wasupper = 0;
                } else {
                        wasupper = isupper(*inc);
                }
                *outc++ = tolower(*inc++);
        }
        return name;
}

/**
 * output character data of node of given type.
 *
 * used for pre/pro/epi/post sections
 */
static int
output_cdata(FILE* outf,
             struct genbind_node *node,
             enum genbind_node_type nodetype)
{
        char *cdata;
        cdata = genbind_node_gettext(
                genbind_node_find_type(
                        genbind_node_getnode(node),
                        NULL, nodetype));
        if (cdata != NULL) {
                fprintf(outf, "%s", cdata);
        }
        return 0;
}

/**
 * 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_METHOD);
        }

        fprintf(outf,")");

        return 0;
}

static int
output_interface_init(FILE* outf,
                      struct interface_map_entry *interfacee,
                      struct interface_map_entry *inherite)
{
        struct genbind_node *init_node;
        int res;

        /* find the initialisor method on the class (if any) */
        init_node = genbind_node_find_method(interfacee->class,
                                             NULL,
                                             GENBIND_METHOD_TYPE_INIT);

        /* initialisor definition */
        output_interface_init_declaration(outf, interfacee, init_node);

        fprintf(outf,"\n{\n");

        /* if this interface inherits ensure we call its initialisor */
        res = output_interface_inherit_init(outf, interfacee, inherite);
        if (res != 0) {
                return res;
        }

        /* generate log statement */
        if (options->dbglog) {
                fprintf(outf,
                        "\tLOG(\"Initialise %%p (priv=%%p)\", duk_get_heapptr(ctx, 0), priv);\n" );
        }

        /* output the initaliser code from the binding */
        output_cdata(outf, init_node, GENBIND_NODE_TYPE_CDATA);

        fprintf(outf, "}\n\n");

        return 0;

}

static int
output_interface_fini(FILE* outf,
                      struct interface_map_entry *interfacee,
                      struct interface_map_entry *inherite)
{
        struct genbind_node *fini_node;

        /* find the finaliser method on the class (if any) */
        fini_node = genbind_node_find_method(interfacee->class,
                                             NULL,
                                             GENBIND_METHOD_TYPE_FINI);

        /* finaliser definition */
        fprintf(outf,
                "void %s_%s___fini(duk_context *ctx, %s_private_t *priv)\n",
                DLPFX, interfacee->class_name, interfacee->class_name);
        fprintf(outf,"{\n");

        /* generate log statement */
        if (options->dbglog) {
                fprintf(outf,
                        "\tLOG(\"Finalise %%p\", duk_get_heapptr(ctx, 0));\n" );
        }

        /* output the finialisor code from the binding */
        output_cdata(outf, fini_node, GENBIND_NODE_TYPE_CDATA);

        /* if this interface inherits ensure we call its finaliser */
        if (inherite != NULL) {
                fprintf(outf,
                        "\t%s_%s___fini(ctx, &priv->parent);\n",
                        DLPFX, inherite->class_name);
        }
        fprintf(outf, "}\n\n");

        return 0;
}


/**
 * count the number of arguments to an operation
 *
 * \todo this needs to consider multiple lists (overloaded calls?), varadic
 *       parameters.
 *
 * \retuen number of arguments or -1 if variable
 */
static int count_operation_arguments(struct webidl_node *node)
{
        int argc;
        struct webidl_node *list_node;
        list_node = webidl_node_find_type(
                webidl_node_getnode(node),
                NULL,
                WEBIDL_NODE_TYPE_LIST);
        if (list_node == NULL) {
                /** \todo is having no argument list an error or a warning? */
                return 0;
        }
        argc = webidl_node_enumerate_type(webidl_node_getnode(list_node),
                                          WEBIDL_NODE_TYPE_OPTIONAL_ARGUMENT);
        if (argc != 0) {
                return -1;
        }
        return webidl_node_enumerate_type(webidl_node_getnode(list_node),
                                          WEBIDL_NODE_TYPE_ARGUMENT);
}

/**
 * generate a prototype add for a single class method
 */
static int
output_prototype_method(FILE* outf,
                        struct interface_map_entry *interfacee,
                        struct webidl_node *op_node)
{
        char *op_name;
        int op_argc;

        op_name = webidl_node_gettext(
                webidl_node_find_type(
                        webidl_node_getnode(op_node),
                        NULL,
                        WEBIDL_NODE_TYPE_IDENT));

        op_argc = count_operation_arguments(op_node);

        output_add_method(outf, interfacee->class_name, op_name, op_argc);

        return 0;
}

/**
 * generate prototype method definitions
 */
static int
output_prototype_methods(FILE *outf, struct interface_map_entry *interfacee)
{
        int res;
        struct webidl_node *list_node;
        struct webidl_node *op_node; /* operation on list node */

        /* iterate each list node within the interface */
        list_node = webidl_node_find_type(
                webidl_node_getnode(interfacee->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 = output_prototype_method(outf, interfacee, op_node);
                        if (res != 0) {
                                return res;
                        }

                        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(interfacee->node),
                        list_node,
                        WEBIDL_NODE_TYPE_LIST);
        }

        return 0;
}


static int
output_prototype_attribute(FILE *outf,
                        struct interface_map_entry *interfacee,
                        struct interface_map_attribute_entry *attributee)
{
        if (attributee->modifier == WEBIDL_TYPE_MODIFIER_READONLY) {
                return output_populate_ro_property(outf,
                                                   interfacee->class_name,
                                                   attributee->name);
        }
        return output_populate_rw_property(outf,
                                           interfacee->class_name,
                                           attributee->name);
}

/**
 * generate prototype attribute definitions
 */
static int
output_prototype_attributes(FILE *outf, struct interface_map_entry *interfacee)
{
        int attrc;
        int res = 0;

        for (attrc = 0; attrc < interfacee->attributec; attrc++) {
                res = output_prototype_attribute(outf,interfacee,
                              interfacee->attributev + attrc);
                if (res != 0) {
                        break;
                }
        }

        return res;
}

/**
 * output constants on the prototype
 *
 * \todo This implementation assumes the constant is a literal int and should
 * check the type node base value.
 */
static int
output_prototype_constant(FILE *outf,
                          struct interface_map_constant_entry *constante)
{
        int *value;

        value = webidl_node_getint(
                webidl_node_find_type(
                        webidl_node_getnode(constante->node),
                        NULL,
                        WEBIDL_NODE_TYPE_LITERAL_INT));

        output_prototype_constant_int(outf, constante->name, *value);

        return 0;
}

/**
 * generate prototype constant definitions
 */
static int
output_prototype_constants(FILE *outf, struct interface_map_entry *interfacee)
{
        int attrc;
        int res = 0;

        for (attrc = 0; attrc < interfacee->constantc; attrc++) {
                res = output_prototype_constant(outf,
                                                interfacee->constantv + attrc);
                if (res != 0) {
                        break;
                }
        }

        return res;
}

/**
 * generate the interface prototype creator
 */
static int
output_interface_prototype(FILE* outf,
                           struct interface_map_entry *interfacee,
                           struct interface_map_entry *inherite)
{
        /* prototype definition */
        fprintf(outf,
                "duk_ret_t %s_%s___proto(duk_context *ctx)\n",
                DLPFX, interfacee->class_name);
        fprintf(outf,"{\n");

        /* generate prototype chaining if interface has a parent */
        if (inherite != NULL) {
                fprintf(outf,
                      "\t/* Set this prototype's prototype (left-parent) */\n");
                output_get_prototype(outf, inherite->name);
                fprintf(outf, "\tduk_set_prototype(ctx, 0);\n\n");
        }

        /* generate setting of methods */
        output_prototype_methods(outf, interfacee);

        /* generate setting of attributes */
        output_prototype_attributes(outf, interfacee);

        /* generate setting of constants */
        output_prototype_constants(outf, interfacee);

        /* generate setting of destructor */
        output_set_destructor(outf, interfacee->class_name, 0);

        /* generate setting of constructor */
        output_set_constructor(outf,
                               interfacee->class_name,
                               0,
                               interfacee->class_init_argc);

        fprintf(outf,"\treturn 1; /* The prototype object */\n");

        fprintf(outf, "}\n\n");

        return 0;
}


/**
 * generate a single class method for an interface operation
 */
static int
output_interface_operation(FILE* outf,
                           struct interface_map_entry *interfacee,
                           struct webidl_node *op_node)
{
        char *op_name;
        struct genbind_node *method_node;

        op_name = webidl_node_gettext(
                webidl_node_find_type(
                        webidl_node_getnode(op_node),
                        NULL,
                        WEBIDL_NODE_TYPE_IDENT));

        method_node = genbind_node_find_method_ident(interfacee->class,
                                                     NULL,
                                                     GENBIND_METHOD_TYPE_METHOD,
                                                     op_name);

        /* method definition */
        fprintf(outf,
                "static duk_ret_t %s_%s_%s(duk_context *ctx)\n",
                DLPFX, interfacee->class_name, op_name);
        fprintf(outf,"{\n");

{'fraction_non_alphanumeric': 0.10620012277470842, 'fraction_numerical': 0.003502690210522515, 'mean_word_length': 1.6875151632781795, 'pattern_counts': {'":': 0, '<': 31, '<?xml version=': 0, '>': 142, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 88, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1407994', 'n_tokens_mistral': 16729, 'n_tokens_neox': 16116, 'n_words': 3546}