From 6fb336e6587d550bf4a8355e65923efb0bf14cab Mon Sep 17 00:00:00 2001 From: Vincent Sanders Date: Sat, 1 Aug 2015 22:42:11 +0100 Subject: generate binding header and source These allow a closed set of functions used by the automatic bindings and gives an external interface allowing all the generated prototypes to be created in the correct order --- src/duk-libdom.c | 426 +++++++++++++++++++++++++++++++++++++++++++--------- src/interface-map.c | 14 ++ src/interface-map.h | 3 + 3 files changed, 371 insertions(+), 72 deletions(-) (limited to 'src') diff --git a/src/duk-libdom.c b/src/duk-libdom.c index 590afa2..4dbc8e2 100644 --- a/src/duk-libdom.c +++ b/src/duk-libdom.c @@ -25,6 +25,8 @@ /** prefix for all generated functions */ #define DLPFX "duckky" +#define MAGICPFX "\\xFF\\xFFNETSURF_DUKTAPE_" + #define NSGENBIND_PREAMBLE \ "/* Generated by nsgenbind\n" \ " *\n" \ @@ -62,24 +64,42 @@ static int output_safe_get_private(FILE* outf, char *class_name, int idx) return 0; } + /** - * generate code that gets a prototype by name + * generate a duktape prototype name */ -static int output_get_prototype(FILE* outf, const char *interface_name) +static char *get_prototype_name(const char *interface_name) { char *proto_name; int pnamelen; + int pfxlen; /* 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]); + 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, PROTO_MAGIC);\n"); - fprintf(outf, "\tduk_get_prop_string(ctx, -1, PROTO_NAME(%s));\n", - proto_name); + 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); @@ -234,6 +254,16 @@ output_get_method_private(FILE* outf, char *class_name) return 0; } +/** + * generate preface block for nsgenbind + */ +static int output_tool_preface(FILE* outf) +{ + fprintf(outf, "\n%s\n", NSGENBIND_PREAMBLE); + + return 0; +} + /** * Generate a C class name for the interface. * @@ -319,6 +349,53 @@ output_cdata(FILE* outf, return 0; } +static FILE *open_header(struct interface_map *interface_map, const char *name) +{ + FILE *hdrf; + char *fname; + int fnamel; + + fnamel = strlen(name) + 4; + fname = malloc(fnamel); + snprintf(fname, fnamel, "%s.h", name); + + /* open output file */ + hdrf = genb_fopen(fname, "w"); + 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) +{ + fprintf(hdrf, "\n#endif\n"); + + /* binding postface */ + output_cdata(hdrf, + interface_map->binding_node, + GENBIND_NODE_TYPE_POSTFACE); + + fclose(hdrf); + + return 0; +} + + /** * generate the interface constructor */ @@ -963,15 +1040,6 @@ output_interface_attributes(FILE* outf, return 0; } -/** - * generate preface block for nsgenbind - */ -static int output_tool_preface(FILE* outf) -{ - fprintf(outf, "\n%s\n", NSGENBIND_PREAMBLE); - - return 0; -} /** * generate preface block for nsgenbind @@ -980,10 +1048,14 @@ static int output_tool_prologue(FILE* outf) { char *fpath; - fpath = genb_fpath("private.h"); + 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); @@ -1105,23 +1177,8 @@ output_private_header(struct interface_map *interface_map) int idx; FILE *privf; - /* open output file */ - privf = genb_fopen("private.h", "w"); - if (privf == NULL) { - return -1; - } - - /* binding preface */ - output_cdata(privf, - interface_map->binding_node, - GENBIND_NODE_TYPE_PREFACE); - - /* tool preface */ - output_tool_preface(privf); - - /* header guard */ - fprintf(privf, "\n#ifndef duk_libdom_private_h\n"); - fprintf(privf, "#define duk_libdom_private_h\n\n"); + /* open header */ + privf = open_header(interface_map, "private"); for (idx = 0; idx < interface_map->entryc; idx++) { struct interface_map_entry *interfacee; @@ -1168,20 +1225,13 @@ output_private_header(struct interface_map *interface_map) } - /* binding postface */ - output_cdata(privf, - interface_map->binding_node, - GENBIND_NODE_TYPE_POSTFACE); - - fprintf(privf, "\n#endif\n"); - - fclose(privf); + close_header(interface_map, privf); return 0; } /** - * generate protottype header + * generate prototype header */ static int output_prototype_header(struct interface_map *interface_map) @@ -1189,23 +1239,8 @@ output_prototype_header(struct interface_map *interface_map) int idx; FILE *protof; - /* open output file */ - protof = genb_fopen("prototype.h", "w"); - if (protof == NULL) { - return -1; - } - - /* binding preface */ - output_cdata(protof, - interface_map->binding_node, - GENBIND_NODE_TYPE_PREFACE); - - /* tool preface */ - output_tool_preface(protof); - - /* header guard */ - fprintf(protof, "\n#ifndef duk_libdom_prototype_h\n"); - fprintf(protof, "#define duk_libdom_prototype_h\n\n"); + /* open header */ + protof = open_header(interface_map, "prototype"); for (idx = 0; idx < interface_map->entryc; idx++) { struct interface_map_entry *interfacee; @@ -1224,6 +1259,13 @@ output_prototype_header(struct interface_map *interface_map) 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", @@ -1239,20 +1281,13 @@ output_prototype_header(struct interface_map *interface_map) fprintf(protof, ";\n\n"); } - /* binding postface */ - output_cdata(protof, - interface_map->binding_node, - GENBIND_NODE_TYPE_POSTFACE); - - fprintf(protof, "\n#endif\n"); - - fclose(protof); + close_header(interface_map, protof); return 0; } /** - * generate protottype header + * generate makefile fragment */ static int output_makefile(struct interface_map *interface_map) @@ -1268,7 +1303,7 @@ output_makefile(struct interface_map *interface_map) fprintf(makef, "# duk libdom makefile fragment\n\n"); - fprintf(makef, "NSGENBIND_SOURCES:="); + fprintf(makef, "NSGENBIND_SOURCES:=binding.c "); for (idx = 0; idx < interface_map->entryc; idx++) { struct interface_map_entry *interfacee; @@ -1288,6 +1323,241 @@ output_makefile(struct interface_map *interface_map) 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); + + 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("binding.c", "w"); + if (bindf == NULL) { + return -1; + } + + /* binding preface */ + output_cdata(bindf, + interface_map->binding_node, + GENBIND_NODE_TYPE_PREFACE); + + /* tool preface */ + output_tool_preface(bindf); + + /* binding prologue */ + output_cdata(bindf, + interface_map->binding_node, + GENBIND_NODE_TYPE_PROLOGUE); + + output_tool_prologue(bindf); + + 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); + + fclose(bindf); + return 0; +} + int duk_libdom_output(struct interface_map *interface_map) { int idx; @@ -1314,6 +1584,18 @@ int duk_libdom_output(struct interface_map *interface_map) 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); diff --git a/src/interface-map.c b/src/interface-map.c index 467c0ed..555156a 100644 --- a/src/interface-map.c +++ b/src/interface-map.c @@ -111,6 +111,7 @@ interface_topoligical_sort(struct interface_map_entry *srcinf, int infc) 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; @@ -422,6 +423,7 @@ int interface_map_new(struct genbind_node *genbind, 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, @@ -433,6 +435,18 @@ int interface_map_new(struct genbind_node *genbind, 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); diff --git a/src/interface-map.h b/src/interface-map.h index e07aa19..c34eb4b 100644 --- a/src/interface-map.h +++ b/src/interface-map.h @@ -49,6 +49,9 @@ struct interface_map_entry { * 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; -- cgit v1.2.3