summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVincent Sanders <vince@kyllikki.org>2015-08-01 22:42:11 +0100
committerVincent Sanders <vince@kyllikki.org>2015-08-01 22:54:19 +0100
commit6fb336e6587d550bf4a8355e65923efb0bf14cab (patch)
treef49dee7c51ed9ffcc620bd0d3d424c201e317b4e
parentcb2089531d4165786772c5cef17d28dc1a8e28d6 (diff)
downloadnsgenbind-6fb336e6587d550bf4a8355e65923efb0bf14cab.tar.gz
nsgenbind-6fb336e6587d550bf4a8355e65923efb0bf14cab.tar.bz2
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
-rw-r--r--src/duk-libdom.c426
-rw-r--r--src/interface-map.c14
-rw-r--r--src/interface-map.h3
3 files changed, 371 insertions, 72 deletions
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);
@@ -235,6 +255,16 @@ output_get_method_private(FILE* outf, char *class_name)
}
/**
+ * 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.
*
* The IDL interface names are camelcase and not similar to libdom naming so it
@@ -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;