/* 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 */ #include #include #include #include #include #include #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].operationc = srcinf[inf].operationc; dstinf[idx].operationv = srcinf[inf].operationv; dstinf[idx].attributec = srcinf[inf].attributec; dstinf[idx].attributev = srcinf[inf].attributev; 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 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 */ operationc = enumerate_interface_type(interface, WEBIDL_NODE_TYPE_OPERATION); *operationc_out = operationc; if (operationc < 1) { *operationv_out = NULL; /* no operations so empty map */ 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) { cure->node = op_node; cure->name = webidl_node_gettext( webidl_node_find_type( webidl_node_getnode(op_node), NULL, WEBIDL_NODE_TYPE_IDENT)); cure->method = genbind_node_find_method_ident( class, NULL, GENBIND_METHOD_TYPE_METHOD, cure->name); cure++; /* 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); } *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; } int interface_map_new(struct genbind_node *genbind, struct webidl_node *webidl, struct interface_map **index_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 *index; interfacec = webidl_node_enumerate_type(webidl, WEBIDL_NODE_TYPE_INTERFACE); if (options->verbose) { printf("Indexing %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 index */ ecur = entries; node = webidl_node_find_type(webidl, NULL, WEBIDL_NODE_TYPE_INTERFACE); while (node != NULL) { /* fill index 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)); /* 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); /* 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); index = malloc(sizeof(struct interface_map)); index->entryc = interfacec; index->entries = sorted_entries; *index_out = index; 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, " inherit:%s\n", ecur->inherit_name); } if (ecur->class != NULL) { fprintf(dumpf, " class:%p\n", ecur->class); } if (ecur->operationc > 0) { int opc = ecur->operationc; struct interface_map_operation_entry *ope; fprintf(dumpf, " %d operations\n", ecur->operationc); ope = ecur->operationv; while (ope != NULL) { fprintf(dumpf, " %s %p\n", ope->name, ope->method); ope++; opc--; if (opc == 0) { break; } } } if (ecur->attributec > 0) { int attrc = ecur->attributec; struct interface_map_attribute_entry *attre; fprintf(dumpf, " %d attributes\n", attrc); attre = ecur->attributev; while (attre != NULL) { fprintf(dumpf, " %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; } } } 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"); ecur = index->entries; for (eidx = 0; eidx < index->entryc; eidx++) { if (ecur->class != NULL) { /* interfaces bound to a class are shown in blue */ fprintf(dumpf, "%04d [label=\"%s\" fontcolor=\"blue\"];\n", eidx, ecur->name); } else { fprintf(dumpf, "%04d [label=\"%s\"];\n", eidx, ecur->name); } 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; }