/* * The parse tree transformation module for SIP. * * Copyright (c) 2010 Riverbank Computing Limited * * This file is part of SIP. * * This copy of SIP is licensed for use under the terms of the SIP License * Agreement. See the file LICENSE for more details. * * This copy of SIP may also used under the terms of the GNU General Public * License v2 or v3 as published by the Free Software Foundation which can be * found in the files LICENSE-GPL2 and LICENSE-GPL3 included in this package. * * SIP is supplied WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. */ #include #include #include #include "sip.h" static int samePythonSignature(signatureDef *sd1, signatureDef *sd2); static int nextSignificantArg(signatureDef *sd, int a); static int sameArgType(argDef *a1, argDef *a2, int strict); static int supportedType(classDef *,overDef *,argDef *,int); static int sameOverload(overDef *od1, overDef *od2); static int sameVirtualHandler(virtHandlerDef *vhd1,virtHandlerDef *vhd2); static int isSubClass(classDef *cc,classDef *pc); static void setAllImports(moduleDef *mod); static void addUniqueModule(moduleDef *mod, moduleDef *imp); static void ensureInput(classDef *,overDef *,argDef *); static void defaultInput(argDef *); static void defaultOutput(argDef *ad); static void createSortedNumberedTypesTable(sipSpec *pt, moduleDef *mod); static int compareTypes(const void *t1, const void *t2); static void addAutoOverload(sipSpec *,classDef *,overDef *); static void ifaceFileIsUsed(ifaceFileList **used, argDef *ad); static void ifaceFilesAreUsedByOverload(ifaceFileList **used, overDef *od); static void ifaceFilesAreUsedBySignature(ifaceFileList **used, signatureDef *sd); static void scopeDefaultValue(sipSpec *,classDef *,argDef *); static void setHierarchy(sipSpec *,classDef *,classDef *,classList **); static void transformModules(sipSpec *pt, moduleDef *mod); static void transformCtors(sipSpec *,classDef *); static void transformCasts(sipSpec *,classDef *); static void addDefaultCopyCtor(classDef *); static void transformScopeOverloads(sipSpec *pt, classDef *c_scope, mappedTypeDef *mt_scope, overDef *overs); static void transformVariableList(sipSpec *pt, moduleDef *mod); static void transformMappedTypes(sipSpec *pt, moduleDef *mod); static void getVisibleMembers(sipSpec *,classDef *); static void getVirtuals(sipSpec *pt,classDef *cd); static void getClassVirtuals(classDef *,classDef *); static void transformTypedefs(sipSpec *pt, moduleDef *mod); static void resolveMappedTypeTypes(sipSpec *,mappedTypeDef *); static void resolveCtorTypes(sipSpec *,classDef *,ctorDef *); static void resolveFuncTypes(sipSpec *pt, moduleDef *mod, classDef *c_scope, mappedTypeDef *mt_scope, overDef *od); static void resolvePySigTypes(sipSpec *,moduleDef *,classDef *,overDef *,signatureDef *,int); static void resolveVariableType(sipSpec *,varDef *); static void fatalNoDefinedType(scopedNameDef *); static void getBaseType(sipSpec *,moduleDef *,classDef *,argDef *); static void searchClassScope(sipSpec *,classDef *,scopedNameDef *,argDef *); static void searchMappedTypes(sipSpec *,moduleDef *,scopedNameDef *,argDef *); static void searchEnums(sipSpec *,scopedNameDef *,argDef *); static void searchClasses(sipSpec *,moduleDef *mod,scopedNameDef *,argDef *); static void appendToMRO(mroDef *,mroDef ***,classDef *); static void moveMainModuleCastsSlots(sipSpec *pt, moduleDef *mod); static void moveClassCasts(sipSpec *pt, moduleDef *mod, classDef *cd); static void moveGlobalSlot(sipSpec *pt, moduleDef *mod, memberDef *gmd); static classDef *findAltClassImplementation(sipSpec *pt, mappedTypeDef *mtd); static void filterMainModuleVirtualHandlers(moduleDef *mod); static void filterModuleVirtualHandlers(moduleDef *mod); static ifaceFileDef *getIfaceFile(argDef *ad); static mappedTypeDef *instantiateMappedTypeTemplate(sipSpec *pt, moduleDef *mod, mappedTypeTmplDef *mtt, argDef *type); static classDef *getProxy(moduleDef *mod, classDef *cd); static int generatingCodeForModule(sipSpec *pt, moduleDef *mod); static void checkAssignmentHelper(sipSpec *pt, classDef *cd); static void addComplementarySlots(sipSpec *pt, classDef *cd); static void addComplementarySlot(sipSpec *pt, classDef *cd, memberDef *md, slotType cslot, const char *cslot_name); static void resolveInstantiatedClassTemplate(sipSpec *pt, argDef *type); static void setStringPoolOffsets(sipSpec *pt); static const char *templateString(const char *src, scopedNameDef *names, scopedNameDef *values); /* * Transform the parse tree. */ void transform(sipSpec *pt) { moduleDef *mod; classDef *cd, *rev, **tail; classList *newl; overDef *od; /* * The class list has the main module's classes at the front and the ones * from the module at the most nested %Import at the end. This affects * some of the following algorithms. We have to have consistency whenever * a module is used. To achieve this we reverse the order of the classes. */ rev = NULL; cd = pt -> classes; while (cd != NULL) { classDef *next = cd -> next; cd -> next = rev; rev = cd; /* * Mark any QObject class. This flag will ripple through all derived * classes when we set the hierarchy. */ if (strcmp(classBaseName(cd), "QObject") == 0) setIsQObjectSubClass(cd); cd = next; } pt -> classes = rev; /* * Build the list of all imports for each module and check each has been * named. */ for (mod = pt->modules; mod != NULL; mod = mod->next) { if (mod->name == NULL) fatal("A module is missing a %%Module or %%CModule directive\n"); setAllImports(mod); } /* * Set the default meta-type for the main module if it doesn't have one * explicitly set. */ if (pt->module->defmetatype == NULL) { moduleListDef *mld; for (mld = pt->module->allimports; mld != NULL; mld = mld->next) { if (mld->module->defmetatype == NULL) continue; if (pt->module->defmetatype == NULL) pt->module->defmetatype = mld->module->defmetatype; else if (pt->module->defmetatype != mld->module->defmetatype) fatal("The %s module has imported different default meta-types %s and %s\n", pt->module->fullname->text, pt->module->defmetatype->text, mld->module->defmetatype->text); } } /* Check each class has been defined. */ for (cd = pt -> classes; cd != NULL; cd = cd -> next) if (cd -> iff -> module == NULL) { fatalScopedName(classFQCName(cd)); fatal(" has not been defined\n"); } /* * Set the super-class hierarchy for each class and re-order the list of * classes so that no class appears before a super class or an enclosing * scope class. */ newl = NULL; for (cd = pt -> classes; cd != NULL; cd = cd -> next) setHierarchy(pt,cd,cd,&newl); /* Replace the old list with the new one. */ tail = &pt -> classes; while (newl != NULL) { classList *cl = newl; *tail = cl -> cd; tail = &cl -> cd -> next; newl = cl -> next; free(cl); } *tail = NULL; /* Transform the various types in the modules. */ if (isConsolidated(pt->module)) { /* Transform the modules included by the consolidated module. */ for (mod = pt->modules->next; mod != NULL; mod = mod->next) transformModules(pt, mod); } else { transformModules(pt, pt->modules); } /* Handle default ctors now that the argument types are resolved. */ if (!pt->genc) for (cd = pt->classes; cd != NULL; cd = cd->next) if (!noDefaultCtors(cd) && !isOpaque(cd) && cd->iff->type != namespace_iface) addDefaultCopyCtor(cd); /* Create the array of numbered types sorted by type name. */ for (mod = pt->modules; mod != NULL; mod = mod->next) createSortedNumberedTypesTable(pt, mod); /* Add any automatically generated methods. */ for (cd = pt -> classes; cd != NULL; cd = cd -> next) for (od = cd -> overs; od != NULL; od = od -> next) if (isAutoGen(od)) addAutoOverload(pt,cd,od); /* * Move casts and slots around to their correct classes (if in the same * module) or create proxies for them (if cross-module). */ if (!pt->genc) for (mod = pt->modules; mod != NULL; mod = mod->next) if (generatingCodeForModule(pt, mod)) moveMainModuleCastsSlots(pt, mod); /* Automatically generate missing complementary slots. */ if (!pt->genc) { for (cd = pt->classes; cd != NULL; cd = cd->next) addComplementarySlots(pt, cd); for (mod = pt->modules; mod != NULL; mod = mod->next) if (generatingCodeForModule(pt, mod)) for (cd = mod->proxies; cd != NULL; cd = cd->next) addComplementarySlots(pt, cd); } /* Generate the different class views. */ for (cd = pt->classes; cd != NULL; cd = cd->next) if (cd->iff->type == class_iface) { /* Get the list of visible member functions. */ getVisibleMembers(pt, cd); /* Get the virtual members. */ if (hasShadow(cd)) getVirtuals(pt, cd); } else if (cd->iff->type == namespace_iface) for (od = cd->overs; od != NULL; od = od->next) ifaceFilesAreUsedByOverload(&cd->iff->used, od); /* * Filter the virtuals of all component modules (if consolidated) or the * main module (if not). */ for (mod = pt->modules; mod != NULL; mod = mod->next) { if (generatingCodeForModule(pt, mod)) { filterMainModuleVirtualHandlers(mod); for (od = mod->overs; od != NULL; od = od->next) ifaceFilesAreUsedByOverload(&mod->used, od); } /* Update proxies with some information from the real classes. */ for (cd = mod->proxies; cd != NULL; cd = cd->next) cd->iff->ifacenr = cd->real->iff->ifacenr; } /* Mark classes that can have an assignment helper. */ for (cd = pt->classes; cd != NULL; cd = cd->next) checkAssignmentHelper(pt, cd); setStringPoolOffsets(pt); } /* * Transform a module and the modules it imports. */ static void transformModules(sipSpec *pt, moduleDef *mod) { classDef *cd; moduleListDef *mld; /* Handle the trivial case. */ if (isTransformed(mod)) return; /* * The modules on which this one depends must be done first because they * might generate new template-based types and they must be defined in the * right module. */ for (mld = mod->imports; mld != NULL; mld = mld->next) transformModules(pt, mld->module); /* Transform typedefs, variables and global functions. */ transformTypedefs(pt, mod); transformVariableList(pt, mod); transformScopeOverloads(pt, NULL, NULL, mod->overs); /* Transform class ctors, functions and casts. */ for (cd = pt->classes; cd != NULL; cd = cd->next) { if (cd->iff->module == mod) { transformCtors(pt, cd); if (!pt->genc) { transformScopeOverloads(pt, cd, NULL, cd->overs); transformCasts(pt, cd); } } } /* Transform mapped types based on templates. */ transformMappedTypes(pt, mod); setIsTransformed(mod); } /* * Set the offset into the string pool for every used name. */ static void setStringPoolOffsets(sipSpec *pt) { nameDef *nd; size_t offset = 0; for (nd = pt->namecache; nd != NULL; nd = nd->next) { size_t len; nameDef *prev; if (!isUsedName(nd)) continue; /* See if the tail of a previous used name could be used instead. */ len = nd->len; for (prev = pt->namecache; prev->len > len; prev = prev->next) { size_t pos; if (!isUsedName(prev) || isSubstring(prev)) continue; pos = prev->len - len; if (memcmp(&prev->text[pos], nd->text, len) == 0) { setIsSubstring(nd); nd->offset = prev->offset + pos; break; } } if (!isSubstring(nd)) { nd->offset = offset; offset += len + 1; } } } /* * Add any missing complementary slots to a class. This emulates the C++ * behaviour of automatically interpreting (for example) >= as !<. */ static void addComplementarySlots(sipSpec *pt, classDef *cd) { memberDef *md; for (md = cd->members; md != NULL; md = md->next) switch (md->slot) { case lt_slot: addComplementarySlot(pt, cd, md, ge_slot, "__ge__"); break; case le_slot: addComplementarySlot(pt, cd, md, gt_slot, "__gt__"); break; case gt_slot: addComplementarySlot(pt, cd, md, le_slot, "__le__"); break; case ge_slot: addComplementarySlot(pt, cd, md, lt_slot, "__lt__"); break; case eq_slot: addComplementarySlot(pt, cd, md, ne_slot, "__ne__"); break; case ne_slot: addComplementarySlot(pt, cd, md, eq_slot, "__eq__"); break; } } /* * Add a complementary slot if it is missing. */ static void addComplementarySlot(sipSpec *pt, classDef *cd, memberDef *md, slotType cslot, const char *cslot_name) { overDef *od1; memberDef *md2 = NULL; for (od1 = cd->overs; od1 != NULL; od1 = od1->next) { overDef *od2; if (od1->common != md || isComplementary(od1) || od1->methodcode != NULL) continue; /* Try and find an existing complementary slot. */ for (od2 = cd->overs; od2 != NULL; od2 = od2->next) if (od2->common->slot == cslot && sameSignature(&od1->pysig, &od2->pysig, TRUE)) break; /* * If there is an explicit complementary slot then there is nothing to * do. */ if (od2 != NULL) continue; /* Create a new member if needed. */ if (md2 == NULL) { for (md2 = cd->members; md2 != NULL; md2 = md2->next) if (md2->slot == cslot) break; if (md2 == NULL) { md2 = sipMalloc(sizeof (memberDef)); md2->pyname = cacheName(pt, cslot_name); md2->memberflags = md->memberflags; md2->slot = cslot; md2->module = md->module; md2->next = cd->members; cd->members = md2; if (isUsedName(md->pyname)) setIsUsedName(md2->pyname); } } /* Create the complementary slot. */ od2 = sipMalloc(sizeof (overDef)); *od2 = *od1; resetIsVirtual(od2); setIsComplementary(od2); od2->common = md2; od2->next = cd->overs; cd->overs = od2; } } /* * See if a class supports an assignment helper. */ static void checkAssignmentHelper(sipSpec *pt, classDef *cd) { int pub_def_ctor, pub_copy_ctor; ctorDef *ct; /* * We register types with Qt if the class is not abstract, doesn't have a * private assignment operator, has a public default ctor, a public copy * ctor and a public dtor. */ if (isAbstractClass(cd)) return; if (cannotAssign(cd)) return; if (!isPublicDtor(cd)) return; pub_def_ctor = pub_copy_ctor = FALSE; for (ct = cd->ctors; ct != NULL; ct = ct->next) { if (ct->cppsig == NULL || !isPublicCtor(ct)) continue; if (ct->cppsig->nrArgs == 0 || ct->cppsig->args[0].defval != NULL) { /* * The ctor either has no arguments or all arguments have defaults. */ pub_def_ctor = TRUE; } else if (ct->cppsig->nrArgs == 1) { argDef *ad = &ct->cppsig->args[0]; classDef *arg_cd; if (ad->atype == class_type) arg_cd = ad->u.cd; else if (ad->atype == mapped_type) arg_cd = findAltClassImplementation(pt, ad->u.mtd); else arg_cd = NULL; if (arg_cd == cd && isReference(ad) && isConstArg(ad) && ad->nrderefs == 0 && ad->defval == NULL) pub_copy_ctor = TRUE; } } if (pub_def_ctor && pub_copy_ctor) { setAssignmentHelper(cd); addToUsedList(&cd->iff->module->used, cd->iff); } } /* * Set the list of all imports for a module. The list is ordered so that a * module appears before any module that imports it. */ static void setAllImports(moduleDef *mod) { moduleListDef *mld; /* * Handle the trivial case where there are no imports, or the list has * already been done. */ if (mod->imports == NULL || mod->allimports != NULL) return; /* Make sure all the direct imports are done first. */ for (mld = mod->imports; mld != NULL; mld = mld->next) setAllImports(mld->module); /* * Now build the list from our direct imports lists but ignoring * duplicates. */ for (mld = mod->imports; mld != NULL; mld = mld->next) { moduleListDef *amld; for (amld = mld->module->allimports; amld != NULL; amld = amld->next) addUniqueModule(mod, amld->module); addUniqueModule(mod, mld->module); } } /* * Append a module to the list of all imported modules if it isn't already * there. */ static void addUniqueModule(moduleDef *mod, moduleDef *imp) { moduleListDef **tail; for (tail = &mod->allimports; *tail != NULL; tail = &(*tail)->next) if ((*tail)->module == imp) return; *tail = sipMalloc(sizeof (moduleListDef)); (*tail)->module = imp; (*tail)->next = NULL; } /* * Move the casts and slots to the correct place for a main module (ie. one we * are generating code for). */ static void moveMainModuleCastsSlots(sipSpec *pt, moduleDef *mod) { classDef *cd; memberDef *md; for (cd = pt->classes; cd != NULL; cd = cd->next) if (cd->iff->module == mod) moveClassCasts(pt, mod, cd); for (md = mod->othfuncs; md != NULL; md = md->next) if (md->slot != no_slot && md->module == mod) moveGlobalSlot(pt, mod, md); } /* * Move any class casts to its correct class, or publish as a ctor extender. */ static void moveClassCasts(sipSpec *pt, moduleDef *mod, classDef *cd) { argList *al; for (al = cd->casts; al != NULL; al = al->next) { classDef *dcd = al->arg.u.cd; ctorDef *ct, **ctp; argDef *ad; if (al->arg.atype == class_type) dcd = al->arg.u.cd; else /* Previous error checking means this will always work. */ dcd = findAltClassImplementation(pt, al->arg.u.mtd); /* * If the destination class is in a different module then use * a proxy. */ if (dcd->iff->module != mod) dcd = getProxy(mod, dcd); /* Create the new ctor. */ ct = sipMalloc(sizeof (ctorDef)); ct->ctorflags = SECT_IS_PUBLIC | CTOR_CAST; ct->cppsig = &ct->pysig; /* Add the source class as the only argument. */ ct->pysig.result.atype = void_type; ad = &ct->pysig.args[0]; ad->atype = class_type; ad->name = NULL; ad->argflags = ARG_IN | (al->arg.argflags & (ARG_IS_REF | ARG_IS_CONST)); ad->nrderefs = al->arg.nrderefs; ad->defval = NULL; ad->u.cd = cd; ifaceFileIsUsed(&dcd->iff->used, ad); ct->pysig.nrArgs = 1; /* Append it to the list. */ for (ctp = &dcd->ctors; *ctp != NULL; ctp = &(*ctp)->next) if (sameSignature(&(*ctp)->pysig, &ct->pysig, FALSE)) { fatal("operator "); fatalScopedName(classFQCName(dcd)); fatal("::"); fatalScopedName(classFQCName(dcd)); fatal("("); fatalScopedName(classFQCName(cd)); fatal(") already defined\n"); } *ctp = ct; } } /* * If possible, move a global slot to its correct class. */ static void moveGlobalSlot(sipSpec *pt, moduleDef *mod, memberDef *gmd) { overDef **odp = &mod->overs, *od; while ((od = *odp) != NULL) { int second; argDef *arg0, *arg1; memberDef *md, **mdhead; overDef **odhead; moduleDef *mod; nameDef *nd; if (od->common != gmd) { odp = &od->next; continue; } /* * We know that the slot has the right number of arguments, but the * first or second one needs to be a class or enum defined in the same * module. Otherwise we leave it as it is and publish it as a slot * extender. */ arg0 = &od->pysig.args[0]; arg1 = &od->pysig.args[1]; mdhead = NULL; second = FALSE; nd = NULL; if (arg0->atype == class_type) { mdhead = &arg0->u.cd->members; odhead = &arg0->u.cd->overs; mod = arg0->u.cd->iff->module; } else if (arg0->atype == mapped_type) { classDef *cd = findAltClassImplementation(pt, arg0->u.mtd); if (cd != NULL) { mdhead = &cd->members; odhead = &cd->overs; mod = cd->iff->module; } } else if (arg0->atype == enum_type) { mdhead = &arg0->u.ed->slots; odhead = &arg0->u.ed->overs; mod = arg0->u.ed->module; nd = arg0->u.ed->pyname; } else if (arg1->atype == class_type) { mdhead = &arg1->u.cd->members; odhead = &arg1->u.cd->overs; mod = arg1->u.cd->iff->module; second = TRUE; } else if (arg1->atype == mapped_type) { classDef *cd = findAltClassImplementation(pt, arg1->u.mtd); if (cd != NULL) { mdhead = &cd->members; odhead = &cd->overs; mod = cd->iff->module; second = TRUE; } } else if (arg1->atype == enum_type) { mdhead = &arg1->u.ed->slots; odhead = &arg1->u.ed->overs; mod = arg1->u.ed->module; nd = arg1->u.ed->pyname; second = TRUE; } if (mdhead == NULL) { fatal("One of the arguments of "); prOverloadName(stderr, od); fatal(" must be a class or enum\n"); } /* * For rich comparisons the first argument must be a class or an enum. * For cross-module slots then it may only be a class. (This latter * limitation is artificial, but is unlikely to be a problem in * practice.) */ if (isRichCompareSlot(gmd)) { if (second) { fatal("The first argument of "); prOverloadName(stderr, od); fatal(" must be a class or enum\n"); } if (mod != gmd->module && arg0->atype == enum_type) { fatal("The first argument of "); prOverloadName(stderr, od); fatal(" must be a class\n"); } } if (mod != gmd->module) { if (isRichCompareSlot(gmd)) { classDef *pcd = getProxy(mod, arg0->u.cd); memberDef *pmd; overDef *pod; /* Create a new proxy member if needed. */ for (pmd = pcd->members; pmd != NULL; pmd = pmd->next) if (pmd->slot == gmd->slot) break; if (pmd == NULL) { pmd = sipMalloc(sizeof (memberDef)); pmd->pyname = gmd->pyname; pmd->memberflags = 0; pmd->slot = gmd->slot; pmd->module = mod; pmd->next = pcd->members; pcd->members = pmd; } /* Add the proxy overload. */ pod = sipMalloc(sizeof (overDef)); *pod = *od; pod->common = pmd; pod->next = pcd->overs; pcd->overs = pod; /* Remove the first argument. */ pod->pysig.args[0] = pod->pysig.args[1]; pod->pysig.nrArgs = 1; /* Remove from the list. */ *odp = od->next; } else odp = &od->next; continue; } /* Remove from the list. */ *odp = od->next; /* The only time we need the name of an enum is when it has slots. */ if (nd != NULL) setIsUsedName(nd); /* See if there is already a member or create a new one. */ for (md = *mdhead; md != NULL; md = md->next) if (md->slot == gmd->slot) break; if (md == NULL) { md = sipMalloc(sizeof (memberDef)); *md = *gmd; md->module = mod; md->next = *mdhead; *mdhead = md; } /* Move the overload to the end of the destination list. */ setIsPublic(od); setIsGlobal(od); od->common = md; od->next = NULL; while (*odhead != NULL) odhead = &(*odhead)->next; *odhead = od; /* Remove the first argument of comparison operators. */ if (isRichCompareSlot(md)) { /* Remember if the argument was a pointer. */ if (arg0->nrderefs > 0) setDontDerefSelf(od); *arg0 = *arg1; od->pysig.nrArgs = 1; } } } /* * Return an alternative class implementation of a mapped type if there is * one. Note that we cheat as we assume there is one going to be one (as * there will be in PyQt at the moment). */ static classDef *findAltClassImplementation(sipSpec *pt, mappedTypeDef *mtd) { ifaceFileDef *iff = mtd->iff->first_alt; while (iff != NULL) { if (iff->type == class_iface) { classDef *cd; for (cd = pt->classes; cd != NULL; cd = cd->next) if (cd->iff == iff) return cd; } iff = iff->next_alt; } return NULL; } /* * Create a proxy for a class if it doesn't already exist. Proxies are used as * containers for cross-module extenders. */ static classDef *getProxy(moduleDef *mod, classDef *cd) { classDef *pcd; for (pcd = mod->proxies; pcd != NULL; pcd = pcd->next) if (pcd->iff == cd->iff) return pcd; pcd = sipMalloc(sizeof (classDef)); pcd->pyname = cd->pyname; pcd->iff = cd->iff; pcd->ecd = cd->ecd; pcd->real = cd; pcd->supers = cd->supers; pcd->mro = cd->mro; pcd->next = mod->proxies; mod->proxies = pcd; return pcd; } /* * Filter the virtual handlers for a main module (ie. one we are generating * code for. */ static void filterMainModuleVirtualHandlers(moduleDef *mod) { moduleListDef *mld; virtHandlerDef *vhd; /* * Remove redundant virtual handlers. It's important that earlier, ie. * those at the deepest level of %Import, are done first. */ for (mld = mod->allimports; mld != NULL; mld = mld->next) filterModuleVirtualHandlers(mld->module); filterModuleVirtualHandlers(mod); /* * Make sure we have the interface files for all types from other modules * that are used in virtual handlers implemented in this module. */ for (vhd = mod->virthandlers; vhd != NULL; vhd = vhd->next) if (!isDuplicateVH(vhd)) ifaceFilesAreUsedBySignature(&mod->used, vhd->cppsig); } /* * Go through the virtual handlers filtering those that can duplicate earlier * ones. Make sure each virtual is numbered within its module, and according * to their position in the list (ignoring duplicates). */ static void filterModuleVirtualHandlers(moduleDef *mod) { virtHandlerDef *vhd; /* See if it has already been done for this module. */ if (mod->nrvirthandlers >= 0) return; mod->nrvirthandlers = 0; for (vhd = mod->virthandlers; vhd != NULL; vhd = vhd->next) { virtHandlerDef *best, *best_thismod, *hd; best = best_thismod = NULL; /* * If this has handwritten code then we will want to use it. * Otherwise, look for a handler in earlier modules. */ if (vhd->virtcode == NULL) { moduleListDef *mld; for (mld = mod->allimports; mld != NULL && mld->module != mod; mld = mld->next) { for (hd = mld->module->virthandlers; hd != NULL; hd = hd->next) if (sameVirtualHandler(vhd, hd)) { best = hd; break; } /* * No need to check later modules as this will either be the * right one, or a duplicate of the right one. */ if (best != NULL) break; } } /* * Find the best candidate in this module in case we want to give it * our handwritten code. */ for (hd = mod->virthandlers; hd != vhd; hd = hd->next) if (sameVirtualHandler(vhd, hd)) { best_thismod = hd; break; } /* * We don't use this one if it doesn't have virtual code and there is * an alternative, or if it does have virtual code and there is already * an alternative in the same module which doesn't have virtual code. */ if ((vhd->virtcode == NULL && (best != NULL || best_thismod != NULL)) || (vhd->virtcode != NULL && best_thismod != NULL && best_thismod->virtcode == NULL)) { virtHandlerDef *saved; /* * If the alternative is in the same module and we have virtual * code then give it to the alternative. Note that there is a bug * here. If there are three handlers, the first without code and * the second and third with code then which code is transfered to * the first is down to luck. We should really only transfer code * to methods that are known to be re-implementations - just having * the same signature isn't enough. */ if (best_thismod != NULL) { if (best_thismod->virtcode == NULL && vhd->virtcode != NULL) { best_thismod->virtcode = vhd->virtcode; resetIsDuplicateVH(best_thismod); } best = best_thismod; } /* Use the better one in place of this one. */ saved = vhd->next; *vhd = *best; setIsDuplicateVH(vhd); vhd->next = saved; } else vhd->virthandlernr = mod->nrvirthandlers++; } } /* * Add an overload that is automatically generated (typically by Qt's moc). */ static void addAutoOverload(sipSpec *pt,classDef *autocd,overDef *autood) { classDef *cd; /* Find every class that has this one in its hierarchy. */ for (cd = pt -> classes; cd != NULL; cd = cd -> next) { mroDef *mro; if (cd == autocd) continue; for (mro = cd -> mro; mro != NULL; mro = mro -> next) if (mro -> cd == autocd) { memberDef *md; overDef *od; /* Another overload may already exist. */ for (md = cd -> members; md != NULL; md = md -> next) if (md -> pyname == autood -> common -> pyname) break; if (md == NULL) { md = sipMalloc(sizeof (memberDef)); md -> pyname = autood -> common -> pyname; md -> memberflags = autood -> common -> memberflags; md -> slot = autood -> common -> slot; md -> module = cd -> iff -> module; md -> next = cd -> members; cd -> members = md; } od = sipMalloc(sizeof (overDef)); *od = *autood; od -> common = md; od -> next = cd -> overs; cd -> overs = od; resetIsAutoGen(od); if (generatingCodeForModule(pt, cd->iff->module)) setIsUsedName(md -> pyname); break; } } } /* * Set the complete hierarchy for a class. */ static void setHierarchy(sipSpec *pt, classDef *base, classDef *cd, classList **head) { mroDef **tailp = &cd->mro; /* See if it has already been done. */ if (cd->mro != NULL) return; if (cd->ecd != NULL) { setHierarchy(pt, base, cd->ecd, head); if (isDeprecatedClass(cd->ecd)) setIsDeprecatedClass(cd); } if (cd->iff->type == class_iface) { classList *cl; /* The first thing is itself. */ appendToMRO(cd->mro, &tailp, cd); if (cd->convtosubcode != NULL) cd->subbase = cd; /* Now do it's superclasses. */ setHierBeingSet(cd->mro); for (cl = cd->supers; cl != NULL; cl = cl->next) { mroDef *mro; if (cl->cd->mro != NULL && hierBeingSet(cl->cd->mro)) { fatal("Recursive class hierarchy detected: "); fatalScopedName(classFQCName(cd)); fatal(" and "); fatalScopedName(classFQCName(cl->cd)); fatal("\n"); } /* Make sure the super-class's hierarchy has been done. */ setHierarchy(pt, base, cl->cd, head); /* Append the super-classes hierarchy. */ for (mro = cl->cd->mro; mro != NULL; mro = mro->next) { appendToMRO(cd->mro, &tailp, mro->cd); if (isDeprecatedClass(mro->cd)) setIsDeprecatedClass(cd); /* * If the super-class is a QObject sub-class then this one is * as well. */ if (isQObjectSubClass(mro->cd)) setIsQObjectSubClass(cd); /* * If the super-class can't be assigned to then this one * cannot either. */ if (cannotAssign(mro->cd)) setCannotAssign(cd); /* * If the super-class has a shadow then this one should have * one as well. */ if (hasShadow(mro->cd)) setHasShadow(cd); /* * Ensure that the sub-class base class is the furthest up the * hierarchy. */ if (mro->cd->subbase != NULL) cd->subbase = mro->cd->subbase; } } resetHierBeingSet(cd->mro); /* * If the class doesn't have an explicit meta-type then inherit from * the module's default. */ if (cd->metatype == NULL && cd->supers == NULL) cd->metatype = cd->iff->module->defmetatype; if (cd->metatype != NULL && generatingCodeForModule(pt, cd->iff->module)) setIsUsedName(cd->metatype); /* * If the class doesn't have an explicit super-type then inherit from * the module's default. */ if (cd->supertype == NULL && cd->supers == NULL) cd->supertype = cd->iff->module->defsupertype; if (cd->supertype != NULL && strcmp(cd->supertype->text, "sip.wrapper") == 0) cd->supertype = NULL; if (cd->supertype != NULL && generatingCodeForModule(pt, cd->iff->module)) setIsUsedName(cd->supertype); } /* * Make sure that the module in which a sub-class convertor will be created * knows about the base class. */ if (cd->subbase != NULL) addToUsedList(&cd->iff->module->used, cd->subbase->iff); /* * We can't have a shadow if the specification is incomplete, there is * a private dtor, there are no none-private ctors or there are private * abstract methods. */ if (isIncomplete(cd) || isPrivateDtor(cd) || !canCreate(cd)) resetHasShadow(cd); else { overDef *od; /* * Note that we should be able to provide better support for * abstract private methods than we do at the moment. */ for (od = cd->overs; od != NULL; od = od->next) if (isAbstract(od) && isPrivate(od)) { resetHasShadow(cd); /* * It also means we cannot create an instance * from Python. */ resetCanCreate(cd); break; } } /* Add it to the new list. */ appendToClassList(head,cd); } /* * Append a class definition to an mro list */ static void appendToMRO(mroDef *head,mroDef ***tailp,classDef *cd) { mroDef *mro, *new; new = sipMalloc(sizeof (mroDef)); new -> cd = cd; new -> mroflags = 0; new -> next = NULL; /* See if it is a duplicate. */ for (mro = head; mro != NULL; mro = mro -> next) if (mro -> cd == cd) { setIsDuplicateSuper(new); if (!isDuplicateSuper(mro)) setHasDuplicateSuper(mro); break; } /* Append to the list and update the tail pointer. */ **tailp = new; *tailp = &new -> next; } /* * Get the base types for all typedefs of a module. */ static void transformTypedefs(sipSpec *pt, moduleDef *mod) { typedefDef *td; for (td = pt->typedefs; td != NULL; td = td->next) if (td->module == mod) getBaseType(pt, td->module, td->ecd, &td->type); } /* * Transform the data types for mapped types based on a template. */ static void transformMappedTypes(sipSpec *pt, moduleDef *mod) { mappedTypeDef *mt; for (mt = pt->mappedtypes; mt != NULL; mt = mt->next) { if (mt->iff->module == mod) { if (mt->type.atype == template_type) resolveMappedTypeTypes(pt, mt); else transformScopeOverloads(pt, NULL, mt, mt->overs); } } } /* * Transform the data types for a list of ctors. */ static void transformCtors(sipSpec *pt, classDef *cd) { ctorDef *ct; for (ct = cd->ctors; ct != NULL; ct = ct->next) { ctorDef *prev; resolveCtorTypes(pt, cd, ct); /* * Now check that the Python signature doesn't conflict with an * earlier one. */ for (prev = cd->ctors; prev != ct; prev = prev->next) if (samePythonSignature(&prev->pysig, &ct->pysig)) { fatalScopedName(classFQCName(cd)); fatal(" has ctors with the same Python signature\n"); } if (isDeprecatedClass(cd)) setIsDeprecatedCtor(ct); } } /* * Transform the data type for a list of casts. */ static void transformCasts(sipSpec *pt, classDef *cd) { argList *al; for (al = cd->casts; al != NULL; al = al->next) { classDef *dcd; getBaseType(pt, cd->iff->module, cd, &al->arg); if (al->arg.atype == class_type) dcd = al->arg.u.cd; else if (al->arg.atype == mapped_type) dcd = findAltClassImplementation(pt, al->arg.u.mtd); else dcd = NULL; if (dcd == NULL) { fatalScopedName(classFQCName(cd)); fatal(" operator cast must be to a class\n"); } } } /* * Add a default copy ctor if required. */ static void addDefaultCopyCtor(classDef *cd) { ctorDef *copyct, **tailp; mroDef *mro; /* See if there is a private copy ctor in the hierarchy. */ for (mro = cd->mro; mro != NULL; mro = mro->next) { ctorDef *ct; if (isDuplicateSuper(mro)) continue; for (ct = mro->cd->ctors; ct != NULL; ct = ct->next) { argDef *ad = &ct -> pysig.args[0]; /* See if is a copy ctor. */ if (ct->pysig.nrArgs == 1 && ad->nrderefs == 0 && isReference(ad)) { ifaceFileDef *iff; /* To check the type we have to look at all versions. */ if (ad->atype == class_type) iff = ad->u.cd->iff; else if (ad->atype == mapped_type) iff = ad->u.mtd->iff; else continue; for (iff = iff->first_alt; iff != NULL; iff = iff->next_alt) if (mro->cd->iff == iff) break; if (iff != NULL) break; } } if (ct != NULL) { /* If the copy ctor is private then the class can't be copied. */ if (isPrivateCtor(ct)) { setCannotCopy(cd); return; } /* * If the ctor is in the class itself then there is nothing to do. */ if (mro == cd->mro) return; /* Otherwise we need to create a default. */ break; } } /* Create a default public copy ctor. */ copyct = sipMalloc(sizeof (ctorDef)); copyct->ctorflags = SECT_IS_PUBLIC; copyct->pysig.nrArgs = 1; copyct->pysig.result.atype = void_type; copyct->pysig.args[0].atype = class_type; copyct->pysig.args[0].u.cd = cd; copyct->pysig.args[0].argflags = (ARG_IS_REF | ARG_IS_CONST | ARG_IN); copyct->pysig.args[0].nrderefs = 0; copyct->pysig.args[0].defval = NULL; copyct->cppsig = ©ct->pysig; if (isDeprecatedClass(cd)) setIsDeprecatedCtor(copyct); /* Append it to the list. */ for (tailp = &cd->ctors; *tailp != NULL; tailp = &(*tailp)->next) ; *tailp = copyct; } /* * Transform the data types for a list of overloads. */ static void transformScopeOverloads(sipSpec *pt, classDef *c_scope, mappedTypeDef *mt_scope, overDef *overs) { overDef *od; for (od = overs; od != NULL; od = od->next) { overDef *prev; resolveFuncTypes(pt, od->common->module, c_scope, mt_scope, od); /* * Now check that the Python signature doesn't conflict with an earlier * one. */ for (prev = overs; prev != od; prev = prev->next) { if (prev->common != od->common) continue; /* They can only conflict if one is unversioned. */ if (prev->api_range != NULL && od->api_range != NULL) continue; if (samePythonSignature(&prev->pysig, &od->pysig)) { ifaceFileDef *iff; if (mt_scope != NULL) iff = mt_scope->iff; else if (c_scope != NULL) iff = c_scope->iff; else iff = NULL; if (iff != NULL) { fatalScopedName(iff->fqcname); fatal("::"); } fatal("%s() has overloaded functions with the same Python signature\n", od->common->pyname->text); } } if (c_scope != NULL && isDeprecatedClass(c_scope)) setIsDeprecated(od); } } /* * Transform the data types for the variables of a module. */ static void transformVariableList(sipSpec *pt, moduleDef *mod) { varDef *vd; for (vd = pt->vars; vd != NULL; vd = vd->next) if (vd->module == mod) if (vd->ecd == NULL || !isTemplateClass(vd->ecd)) resolveVariableType(pt, vd); } /* * Set the list of visible member functions for a class. */ static void getVisibleMembers(sipSpec *pt, classDef *cd) { mroDef *mro; cd->visible = NULL; for (mro = cd->mro; mro != NULL; mro = mro->next) { memberDef *md; classDef *mrocd; if (isDuplicateSuper(mro)) continue; mrocd = mro->cd; for (md = mrocd->members; md != NULL; md = md->next) { visibleList *vl; /* * See if it is already in the list. This has the desired side * effect of eliminating any functions that have an implementation * closer to this class in the hierarchy. This is the only reason * to define private functions. */ for (vl = cd->visible; vl != NULL; vl = vl->next) if (vl->m->pyname == md->pyname) break; /* See if it is a new member function. */ if (vl == NULL) { overDef *od; vl = sipMalloc(sizeof (visibleList)); vl->m = md; vl->cd = mrocd; vl->next = cd->visible; cd->visible = vl; for (od = mrocd->overs; od != NULL; od = od->next) if (od->common == md) { if (isAbstract(od)) setIsAbstractClass(cd); ifaceFilesAreUsedByOverload(&cd->iff->used, od); /* See if we need the name. */ if (!generatingCodeForModule(pt, cd->iff->module)) continue; if (isProtected(od) || (isSignal(od) && pluginPyQt3(pt))) setIsUsedName(md->pyname); /* Make we have any API name. */ if (od->api_range != NULL) setIsUsedName(od->api_range->api_name); } } } } } /* * Get all the virtuals for a particular class. */ static void getVirtuals(sipSpec *pt, classDef *cd) { mroDef *mro; virtOverDef *vod; for (mro = cd->mro; mro != NULL; mro = mro->next) { if (isDuplicateSuper(mro)) continue; getClassVirtuals(cd, mro->cd); } /* * Identify any re-implementations of virtuals. We have to do this for all * classes, not just those in the module we are generating code for. */ for (vod = cd->vmembers; vod != NULL; vod = vod->next) { overDef *od; for (od = cd->overs; od != NULL; od = od->next) { if (isVirtual(od)) continue; if (strcmp(vod->o.cppname, od->cppname) == 0 && sameOverload(&vod->o, od)) { setIsVirtualReimp(od); break; } } /* * If this class is defined in the main module make sure we get the API * files for all the visible virtuals. */ if (generatingCodeForModule(pt, cd->iff->module)) { /* Make sure we get the name. */ setIsUsedName(vod->o.common->pyname); } } } /* * Get the list of visible virtual functions for a class. */ static void getClassVirtuals(classDef *base, classDef *cd) { overDef *od; for (od = cd->overs; od != NULL; od = od->next) { virtOverDef **tailp, *vod; if (!isVirtual(od) || isPrivate(od)) continue; /* * See if a virtual of this name and signature is already in the list. */ for (tailp = &base->vmembers; (vod = *tailp) != NULL; tailp = &vod->next) if (strcmp(vod->o.cppname, od->cppname) == 0 && sameOverload(&vod->o, od)) break; if (vod == NULL) { /* * See if there is a non-virtual reimplementation nearer in the * class hierarchy. */ mroDef *mro; classDef *scope = NULL; overDef *eod; for (mro = base->mro; mro->cd != cd; mro = mro->next) { if (isDuplicateSuper(mro)) continue; /* * Ignore classes that are on a different branch of the class * hierarchy. */ if (!isSubClass(mro->cd, cd)) continue; for (eod = mro->cd->overs; eod != NULL; eod = eod->next) if (strcmp(eod->cppname, od->cppname) == 0 && sameSignature(eod->cppsig, od->cppsig, TRUE) && isConst(eod) == isConst(od) && !isAbstract(eod)) { scope = mro->cd; break; } if (scope != NULL) break; } vod = sipMalloc(sizeof (virtOverDef)); vod->o = *od; vod->scope = (scope != NULL ? scope : cd); vod->next = NULL; *tailp = vod; /* * If there was a nearer reimplementation then we use its * protection and abstract flags. */ if (scope != NULL) { vod->o.overflags &= ~(SECT_MASK | OVER_IS_ABSTRACT); vod->o.overflags |= (SECT_MASK | OVER_IS_ABSTRACT) & eod->overflags; } } } } /* * Return TRUE is a class is derived from another. */ static int isSubClass(classDef *cc,classDef *pc) { mroDef *mro; /* * In other words, does the parent class appear in the child class's * MRO list. */ for (mro = cc -> mro; mro != NULL; mro = mro -> next) if (mro -> cd == pc) return TRUE; return FALSE; } /* * Resolve the types of a mapped type based on a template. */ static void resolveMappedTypeTypes(sipSpec *pt, mappedTypeDef *mt) { int a; signatureDef *sd = &mt->type.u.td->types; for (a = 0; a < sd->nrArgs; ++a) { argDef *ad = &sd->args[a]; /* Leave templates as they are. */ if (ad->atype != template_type) getBaseType(pt, mt->iff->module, NULL, ad); } /* Make sure that the signature result won't cause problems. */ sd->result.atype = no_type; ifaceFilesAreUsedBySignature(&mt->iff->used, sd); } /* * Resolve the types of a ctor. */ static void resolveCtorTypes(sipSpec *pt,classDef *scope,ctorDef *ct) { int a; /* Handle any C++ signature. */ if (ct->cppsig != NULL && ct->cppsig != &ct->pysig) for (a = 0; a < ct -> cppsig -> nrArgs; ++a) getBaseType(pt, scope->iff->module, scope, &ct->cppsig->args[a]); /* Handle the Python signature. */ for (a = 0; a < ct -> pysig.nrArgs; ++a) { argDef *ad = &ct -> pysig.args[a]; getBaseType(pt, scope->iff->module, scope, ad); if (!supportedType(scope,NULL,ad,FALSE) && (ct -> cppsig == &ct -> pysig || ct -> methodcode == NULL)) { fatalScopedName(classFQCName(scope)); fatal(" unsupported ctor argument type - provide %%MethodCode and a C++ signature\n"); } ifaceFileIsUsed(&scope->iff->used, ad); scopeDefaultValue(pt, scope, ad); } } /* * Resolve the types of a function. */ static void resolveFuncTypes(sipSpec *pt, moduleDef *mod, classDef *c_scope, mappedTypeDef *mt_scope, overDef *od) { argDef *res; /* Handle any C++ signature. */ if (od->cppsig != &od->pysig) { int a; getBaseType(pt,mod, c_scope, &od->cppsig->result); for (a = 0; a < od->cppsig->nrArgs; ++a) getBaseType(pt, mod, c_scope, &od->cppsig->args[a]); } /* Handle the Python signature. */ resolvePySigTypes(pt, mod, c_scope, od, &od->pysig, isSignal(od)); res = &od->pysig.result; /* These slots must return SIP_SSIZE_T (or int - deprecated). */ if (isSSizeReturnSlot(od->common)) if ((res->atype != ssize_type && res->atype != int_type) || res->nrderefs != 0 || isReference(res) || isConstArg(res)) fatal("%s slots must return SIP_SSIZE_T\n", od->common->pyname->text); /* These slots must return int. */ if (isIntReturnSlot(od->common)) if (res->atype != int_type || res->nrderefs != 0 || isReference(res) || isConstArg(res)) fatal("%s slots must return int\n", od->common->pyname->text); /* These slots must return void. */ if (isVoidReturnSlot(od->common)) if (res->atype != void_type || res->nrderefs != 0 || isReference(res) || isConstArg(res)) fatal("%s slots must return void\n", od->common->pyname->text); /* These slots must return long. */ if (isLongReturnSlot(od->common)) if (res->atype != long_type || res->nrderefs != 0 || isReference(res) || isConstArg(res)) fatal("%s slots must return long\n", od->common->pyname->text); } /* * Resolve the types of a Python signature. */ static void resolvePySigTypes(sipSpec *pt, moduleDef *mod, classDef *scope, overDef *od, signatureDef *pysig, int issignal) { int a; argDef *res = &pysig -> result; if (res -> atype != void_type || res -> nrderefs != 0) { if (issignal) { if (scope != NULL) { fatalScopedName(classFQCName(scope)); fatal("::"); } fatal("%s() signals must return void\n",od -> cppname); } getBaseType(pt, mod, scope, res); /* Results must be simple. */ if (!supportedType(scope,od,res,FALSE) && (od -> cppsig == &od -> pysig || od -> methodcode == NULL)) { if (scope != NULL) { fatalScopedName(classFQCName(scope)); fatal("::"); } fatal("%s() unsupported function return type - provide %%MethodCode and a %s signature\n",od -> cppname,(pt -> genc ? "C" : "C++")); } } for (a = 0; a < pysig -> nrArgs; ++a) { argDef *ad = &pysig -> args[a]; getBaseType(pt, mod, scope, ad); if (ad -> atype == slotcon_type) resolvePySigTypes(pt, mod, scope, od, ad->u.sa, TRUE); /* * Note signal arguments are restricted in their types because we don't * (yet) support handwritten code for them. */ if (issignal) { if (!supportedType(scope,od,ad,FALSE)) { if (scope != NULL) { fatalScopedName(classFQCName(scope)); fatal("::"); } fatal("%s() unsupported signal argument type\n", od->cppname); } } else if (!supportedType(scope,od,ad,TRUE) && (od -> cppsig == &od -> pysig || od -> methodcode == NULL || (isVirtual(od) && od -> virthandler -> virtcode == NULL))) { if (scope != NULL) { fatalScopedName(classFQCName(scope)); fatal("::"); } if (isVirtual(od)) fatal("%s() unsupported function argument type - provide %%MethodCode, a valid %%VirtualCatcherCode and a valid C++ signature\n",od -> cppname); fatal("%s() unsupported function argument type - provide %%MethodCode and a valid %s signature\n",od -> cppname,(pt -> genc ? "C" : "C++")); } if (scope != NULL) scopeDefaultValue(pt,scope,ad); } } /* * Resolve the type of a variable. */ static void resolveVariableType(sipSpec *pt, varDef *vd) { int bad = TRUE; argDef *vtype = &vd->type; getBaseType(pt, vd->module, vd->ecd, vtype); switch (vtype->atype) { case mapped_type: case class_type: /* Class, Class & and Class * are supported. */ if (vtype->nrderefs <= 1) bad = FALSE; break; case ascii_string_type: case latin1_string_type: case utf8_string_type: case sstring_type: case ustring_type: case string_type: case wstring_type: /* * (signed/unsigned) char, (signed/unsigned) char *, wchar_t, wchar_t * * are supported. */ if (!isReference(vtype) && vtype->nrderefs <= 1) bad = FALSE; break; case cfloat_type: case float_type: case cdouble_type: case double_type: case enum_type: case bool_type: case cbool_type: case ushort_type: case short_type: case uint_type: case cint_type: case int_type: case ulong_type: case long_type: case ulonglong_type: case longlong_type: case ssize_type: case pyobject_type: case pytuple_type: case pylist_type: case pydict_type: case pycallable_type: case pyslice_type: case pytype_type: /* These are supported without pointers or references. */ if (!isReference(vtype) && vtype->nrderefs == 0) bad = FALSE; break; case struct_type: case void_type: /* A simple pointer is supported. */ if (!isReference(vtype) && vtype->nrderefs == 1) bad = FALSE; break; } if (bad && (vd->getcode == NULL || vd->setcode == NULL)) { fatalScopedName(vd->fqcname); fatal(" has an unsupported type - provide %%GetCode and %%SetCode\n"); } if (vtype->atype != class_type && vd->accessfunc != NULL) { fatalScopedName(vd->fqcname); fatal(" has %%AccessCode but isn't a class instance\n"); } if (vd->ecd != NULL) ifaceFileIsUsed(&vd->ecd->iff->used, vtype); else ifaceFileIsUsed(&vd->module->used, vtype); /* Scoped variables need a handler unless they have %AccessCode. */ if (vd->ecd != NULL && vd->accessfunc == NULL) { setNeedsHandler(vd); setHasVarHandlers(vd->ecd); } } /* * See if a type is supported by the generated code. */ static int supportedType(classDef *cd,overDef *od,argDef *ad,int outputs) { switch (ad -> atype) { case anyslot_type: /* * This must be an input, and must also have handwritten code. */ ensureInput(cd,od,ad); return FALSE; case signal_type: case slot_type: case rxcon_type: case rxdis_type: case slotcon_type: case slotdis_type: case qobject_type: case ellipsis_type: /* These can only appear in argument lists without * or &. */ ensureInput(cd,od,ad); return TRUE; case ascii_string_type: case latin1_string_type: case utf8_string_type: case sstring_type: case ustring_type: case string_type: case wstring_type: if (isReference(ad)) { if (outputs && ad -> nrderefs <= 1) { defaultOutput(ad); return TRUE; } } else if (ad -> nrderefs == 0) { ensureInput(cd,od,ad); return TRUE; } else if (ad -> nrderefs == 1) { if (outputs) defaultInput(ad); else ensureInput(cd,od,ad); return TRUE; } else if (ad -> nrderefs == 2 && outputs) { defaultOutput(ad); return TRUE; } break; case cfloat_type: case float_type: case cdouble_type: case double_type: case enum_type: case bool_type: case cbool_type: case ushort_type: case short_type: case uint_type: case cint_type: case int_type: case ulong_type: case long_type: case ulonglong_type: case longlong_type: case ssize_type: case pyobject_type: case pytuple_type: case pylist_type: case pydict_type: case pycallable_type: case pyslice_type: case pytype_type: if (isReference(ad)) { if (ad -> nrderefs == 0 && outputs) { defaultOutput(ad); return TRUE; } } else if (ad -> nrderefs == 0) { ensureInput(cd,od,ad); return TRUE; } else if (ad -> nrderefs == 1 && outputs) { defaultOutput(ad); return TRUE; } break; case mapped_type: case class_type: if (isReference(ad)) { if (ad -> nrderefs == 0) { defaultInput(ad); return TRUE; } else if (ad -> nrderefs == 1 && outputs) { defaultOutput(ad); return TRUE; } } else if (ad -> nrderefs == 0) { ensureInput(cd,od,ad); return TRUE; } else if (ad -> nrderefs == 1) { if (outputs) defaultInput(ad); else ensureInput(cd,od,ad); return TRUE; } else if (ad -> nrderefs == 2 && outputs) { defaultOutput(ad); return TRUE; } break; case struct_type: case void_type: if (isReference(ad)) { if (ad -> nrderefs == 1 && outputs) { defaultOutput(ad); return TRUE; } } else if (ad -> nrderefs == 1) { ensureInput(cd,od,ad); return TRUE; } else if (ad -> nrderefs == 2 && outputs) { defaultOutput(ad); return TRUE; } break; } /* Unsupported if we got this far. */ return FALSE; } /* * Ensure the direction of an argument is an input. */ static void ensureInput(classDef *cd,overDef *od,argDef *ad) { if (isOutArg(ad)) { if (cd != NULL) { fatalScopedName(classFQCName(cd)); fatal("::"); } if (od != NULL) fatal("%s",od -> cppname); fatal("() invalid argument type for /Out/\n"); } setIsInArg(ad); } /* * Default the direction of an argument to an input. */ static void defaultInput(argDef *ad) { if (!isInArg(ad) && !isOutArg(ad)) setIsInArg(ad); } /* * Default the direction of an argument to an output unless the argument is * const. */ static void defaultOutput(argDef *ad) { if (!isOutArg(ad) && !isInArg(ad)) { if (isConstArg(ad)) setIsInArg(ad); else setIsOutArg(ad); } } /* * Put a scoped name to stderr. */ void fatalScopedName(scopedNameDef *snd) { while (snd != NULL) { fatal("%s",snd -> name); snd = snd -> next; if (snd != NULL) fatal("::"); } } /* * Compare two overloads and return TRUE if they are the same. */ static int sameOverload(overDef *od1, overDef *od2) { /* They must both be enabled for the same API. */ if (od1->api_range != od2->api_range) return FALSE; /* They must both be const, or both not. */ if (isConst(od1) != isConst(od2)) return FALSE; return sameSignature(&od1->pysig, &od2->pysig, TRUE); } /* * Compare two virtual handlers and return TRUE if they are the same. */ static int sameVirtualHandler(virtHandlerDef *vhd1,virtHandlerDef *vhd2) { int a; if (isTransferVH(vhd1) != isTransferVH(vhd2)) return FALSE; if (!sameArgType(&vhd1->pysig->result, &vhd2->pysig->result, TRUE)) return FALSE; if (!sameSignature(vhd1->pysig, vhd2->pysig, TRUE)) return FALSE; /* Take into account the argument directions in the Python signatures. */ for (a = 0; a < vhd1->pysig->nrArgs; ++a) { int dir1 = (vhd1->pysig->args[a].argflags & (ARG_IN | ARG_OUT)); int dir2 = (vhd2->pysig->args[a].argflags & (ARG_IN | ARG_OUT)); if (dir1 != dir2) return FALSE; } if (vhd1->pysig == vhd1->cppsig && vhd2->pysig == vhd2->cppsig) return TRUE; if (!sameArgType(&vhd1->cppsig->result, &vhd2->cppsig->result, TRUE)) return FALSE; return sameSignature(vhd1->cppsig, vhd2->cppsig, TRUE); } /* * Compare two signatures and return TRUE if they are the same. */ int sameSignature(signatureDef *sd1,signatureDef *sd2,int strict) { int a; if (strict) { /* The number of arguments must be the same. */ if (sd1 -> nrArgs != sd2 -> nrArgs) return FALSE; } else { int na1, na2; /* We only count the compulsory arguments. */ na1 = 0; for (a = 0; a < sd1 -> nrArgs; ++a) { if (sd1 -> args[a].defval != NULL) break; ++na1; } na2 = 0; for (a = 0; a < sd2 -> nrArgs; ++a) { if (sd2 -> args[a].defval != NULL) break; ++na2; } if (na1 != na2) return FALSE; } /* The arguments must be the same. */ for (a = 0; a < sd1 -> nrArgs; ++a) { if (!strict && sd1 -> args[a].defval != NULL) break; if (!sameArgType(&sd1 -> args[a],&sd2 -> args[a],strict)) return FALSE; } /* Must be the same if we've got this far. */ return TRUE; } #define pyAsString(t) ((t) == ustring_type || (t) == sstring_type || \ (t) == string_type || (t) == ascii_string_type || \ (t) == latin1_string_type || (t) == utf8_string_type) #define pyAsFloat(t) ((t) == cfloat_type || (t) == float_type || \ (t) == cdouble_type || (t) == double_type) #define pyAsInt(t) ((t) == bool_type || (t) == ssize_type || \ (t) == short_type || (t) == ushort_type || \ (t) == cint_type || (t) == int_type || (t) == uint_type) #define pyAsLong(t) ((t) == long_type || (t) == longlong_type) #define pyAsULong(t) ((t) == ulong_type || (t) == ulonglong_type) #define pyAsAuto(t) ((t) == bool_type || \ (t) == short_type || (t) == ushort_type || \ (t) == int_type || (t) == uint_type || \ (t) == float_type || (t) == double_type) #define pyIsConstrained(t) ((t) == cbool_type || (t) == cint_type || \ (t) == cfloat_type || (t) == cdouble_type) /* * Compare two argument types and return TRUE if they are the same. "strict" * means as C++ would see it, rather than Python. */ static int sameArgType(argDef *a1, argDef *a2, int strict) { /* The references must be the same. */ if (isReference(a1) != isReference(a2) || a1->nrderefs != a2->nrderefs) return FALSE; if (strict) { /* The const should be the same. */ if (isConstArg(a1) != isConstArg(a2)) return FALSE; return sameBaseType(a1,a2); } /* If both are constrained fundamental types then the types must match. */ if (pyIsConstrained(a1->atype) && pyIsConstrained(a2->atype)) return (a1->atype == a2->atype); /* An unconstrained enum also acts as a (very) constrained int. */ if ((pyAsInt(a1->atype) && a2->atype == enum_type && !isConstrained(a2)) || (a1->atype == enum_type && !isConstrained(a1) && pyAsInt(a2->atype))) return TRUE; /* Python will see all these as strings. */ if (pyAsString(a1->atype) && pyAsString(a2->atype)) return TRUE; /* Python will see all these as floats. */ if (pyAsFloat(a1->atype) && pyAsFloat(a2->atype)) return TRUE; /* Python will see all these as ints. */ if (pyAsInt(a1->atype) && pyAsInt(a2->atype)) return TRUE; /* Python will see all these as longs. */ if (pyAsLong(a1->atype) && pyAsLong(a2->atype)) return TRUE; /* Python will see all these as unsigned longs. */ if (pyAsULong(a1->atype) && pyAsULong(a2->atype)) return TRUE; /* Python will automatically convert between these. */ if (pyAsAuto(a1->atype) && pyAsAuto(a2->atype)) return TRUE; /* All the special cases have been handled. */ return sameBaseType(a1, a2); } /* * Compare two basic types and return TRUE if they are the same. */ int sameBaseType(argDef *a1, argDef *a2) { /* The types must be the same. */ if (a1->atype != a2->atype) { /* * If we are comparing a template with those that have already been * used to instantiate a class or mapped type then we need to compare * with the class or mapped type name. */ if (a1->atype == class_type && a2->atype == defined_type) return compareScopedNames(a1->u.cd->iff->fqcname, a2->u.snd) == 0; if (a1->atype == defined_type && a2->atype == class_type) return compareScopedNames(a1->u.snd, a2->u.cd->iff->fqcname) == 0; if (a1->atype == mapped_type && a2->atype == defined_type) return compareScopedNames(a1->u.mtd->iff->fqcname, a2->u.snd) == 0; if (a1->atype == defined_type && a2->atype == mapped_type) return compareScopedNames(a1->u.snd, a2->u.mtd->iff->fqcname) == 0; return FALSE; } switch (a1->atype) { case class_type: if (a1->u.cd != a2->u.cd) return FALSE; break; case enum_type: if (a1->u.ed != a2->u.ed) return FALSE; break; case slotcon_type: case slotdis_type: if (!sameSignature(a1->u.sa, a2->u.sa, TRUE)) return FALSE; break; case template_type: { int a; templateDef *td1, *td2; td1 = a1->u.td; td2 = a2->u.td; if (compareScopedNames(td1->fqname, td2->fqname) != 0 || td1->types.nrArgs != td2->types.nrArgs) return FALSE; for (a = 0; a < td1->types.nrArgs; ++a) if (!sameBaseType(&td1->types.args[a], &td2->types.args[a])) return FALSE; break; } case struct_type: if (compareScopedNames(a1->u.sname, a2->u.sname) != 0) return FALSE; break; case defined_type: if (compareScopedNames(a1->u.snd, a2->u.snd) != 0) return FALSE; break; case mapped_type: if (a1->u.mtd != a2->u.mtd) return FALSE; break; } /* Must be the same if we've got this far. */ return TRUE; } /* * See if two Python signatures are the same as far as Python is concerned. */ static int samePythonSignature(signatureDef *sd1, signatureDef *sd2) { int a1, a2; a1 = a2 = -1; for (;;) { a1 = nextSignificantArg(sd1, a1); a2 = nextSignificantArg(sd2, a2); if (a1 < 0 || a2 < 0) break; if (!sameArgType(&sd1->args[a1], &sd2->args[a2], FALSE)) return FALSE; } return (a1 < 0 && a2 < 0); } /* * Return the next significant argument from a Python signature (ie. one that * is not optional or an output only argument. Return -1 if there isn't one. */ static int nextSignificantArg(signatureDef *sd, int a) { while (++a < sd->nrArgs) { if (sd->args[a].defval != NULL) break; if (isInArg(&sd->args[a])) return a; } return -1; } /* * The equivalent of strcmp() for scoped names. */ int compareScopedNames(scopedNameDef *snd1, scopedNameDef *snd2) { while (snd1 != NULL && snd2 != NULL) { int res = strcmp(snd1->name, snd2->name); if (res != 0) return res; snd1 = snd1->next; snd2 = snd2->next; } if (snd1 == NULL) return (snd2 == NULL ? 0 : -1); return 1; } /* * Add an explicit scope to the default value of an argument if possible. */ static void scopeDefaultValue(sipSpec *pt,classDef *cd,argDef *ad) { valueDef *vd, **tailp, *newvd; /* * We do a quick check to see if we need to do anything. This means * we can limit the times we need to copy the default value. It needs * to be copied because it will be shared by class versions that have * been created on the fly and it may need to be scoped differently for * each of those versions. */ for (vd = ad -> defval; vd != NULL; vd = vd -> next) if (vd -> vtype == scoped_value && vd -> u.vscp -> next == NULL) break; if (vd == NULL) return; /* * It's not certain that we will do anything, but we assume we will and * start copying. */ newvd = NULL; tailp = &newvd; for (vd = ad -> defval; vd != NULL; vd = vd -> next) { mroDef *mro; scopedNameDef *origname; valueDef *new; /* Make the copy. */ new = sipMalloc(sizeof (valueDef)); *new = *vd; *tailp = new; tailp = &new -> next; /* * Skip this part of the expression if it isn't a named value * or it already has a scope. */ if (vd -> vtype != scoped_value || vd -> u.vscp -> next != NULL) continue; /* * Search the class hierarchy for an enum value with the same * name. If we don't find one, leave it as it is (the compiler * will find out if this is a problem). */ origname = vd -> u.vscp; for (mro = cd -> mro; mro != NULL; mro = mro -> next) { enumDef *ed; if (isDuplicateSuper(mro)) continue; for (ed = pt -> enums; ed != NULL; ed = ed -> next) { enumMemberDef *emd; if (ed -> ecd != mro -> cd) continue; for (emd = ed -> members; emd != NULL; emd = emd -> next) if (strcmp(emd -> cname,origname -> name) == 0) { scopedNameDef *snd; /* * Take the scope from the * class that the enum was * defined in. */ snd = copyScopedName(mro -> cd -> iff -> fqcname); appendScopedName(&snd,origname); new -> u.vscp = snd; /* Nothing more to do. */ break; } if (emd != NULL) break; } if (ed != NULL) break; } } ad -> defval = newvd; } /* * Make sure a type is a base type. */ static void getBaseType(sipSpec *pt, moduleDef *mod, classDef *c_scope, argDef *type) { /* Loop until we've got to a base type. */ while (type->atype == defined_type) { scopedNameDef *snd = type->u.snd; type->atype = no_type; if (c_scope != NULL) searchClassScope(pt, c_scope, snd,type); if (type->atype == no_type) searchMappedTypes(pt, mod, snd, type); if (type->atype == no_type) searchTypedefs(pt, snd, type); if (type->atype == no_type) searchEnums(pt, snd, type); if (type->atype == no_type) searchClasses(pt, mod, snd, type); if (type->atype == no_type) fatalNoDefinedType(snd); } /* Get the base type of any slot arguments. */ if (type->atype == slotcon_type || type->atype == slotdis_type) { int sa; for (sa = 0; sa < type->u.sa->nrArgs; ++sa) getBaseType(pt, mod, c_scope, &type->u.sa->args[sa]); } /* See if the type refers to an instantiated template. */ resolveInstantiatedClassTemplate(pt, type); /* Replace the base type if it has been mapped. */ if (type->atype == struct_type || type->atype == template_type) { searchMappedTypes(pt, mod, NULL, type); /* * If we still have a template then see if we need to automatically * instantiate it. */ if (type->atype == template_type) { mappedTypeTmplDef *mtt; for (mtt = pt->mappedtypetemplates; mtt != NULL; mtt = mtt->next) if (compareScopedNames(type->u.td->fqname, mtt->mt->type.u.td->fqname) == 0 && sameTemplateSignature(&mtt->mt->type.u.td->types, &type->u.td->types, TRUE)) { type->u.mtd = instantiateMappedTypeTemplate(pt, mod, mtt, type); type->atype = mapped_type; break; } } } } /* * If the type corresponds to a previously instantiated class template then * replace it with the class that was created. */ static void resolveInstantiatedClassTemplate(sipSpec *pt, argDef *type) { int a; classDef *cd; templateDef *td; signatureDef *sd; if (type->atype != template_type) return; td = type->u.td; sd = &td->types; for (a = 0; a < sd->nrArgs; ++a) resolveInstantiatedClassTemplate(pt, &sd->args[a]); for (cd = pt->classes; cd != NULL; cd = cd->next) if (cd->td != NULL && compareScopedNames(cd->td->fqname, td->fqname) == 0 && sameSignature(&cd->td->types, sd, TRUE)) { type->atype = class_type; type->u.cd = cd; break; } } /* * Instantiate a mapped type template and return it. */ static mappedTypeDef *instantiateMappedTypeTemplate(sipSpec *pt, moduleDef *mod, mappedTypeTmplDef *mtt, argDef *type) { scopedNameDef *type_names, *type_values; mappedTypeDef *mtd; type_names = type_values = NULL; appendTypeStrings(type->u.td->fqname, &mtt->mt->type.u.td->types, &type->u.td->types, &mtt->sig, &type_names, &type_values); mtd = allocMappedType(pt, type); if (generatingCodeForModule(pt, mod)) setIsUsedName(mtd->cname); mtd->iff = findIfaceFile(pt, mod, encodedTemplateName(type->u.td), mappedtype_iface, NULL, type); mtd->iff->module = mod; mtd->doctype = templateString(mtt->mt->doctype, type_names, type_values); appendCodeBlock(&mtd->iff->hdrcode, templateCode(pt, &mtd->iff->used, mtt->mt->iff->hdrcode, type_names, type_values)); mtd->convfromcode = templateCode(pt, &mtd->iff->used, mtt->mt->convfromcode, type_names, type_values); mtd->convtocode = templateCode(pt, &mtd->iff->used, mtt->mt->convtocode, type_names, type_values); mtd->next = pt->mappedtypes; pt->mappedtypes = mtd; if (type_names != NULL) freeScopedName(type_names); if (type_values != NULL) freeScopedName(type_values); return mtd; } /* * Return a string based on an original with names replaced by corresponding * values. */ static const char *templateString(const char *src, scopedNameDef *names, scopedNameDef *values) { char *dst; /* Handle the trivial case. */ if (src == NULL) return NULL; dst = sipStrdup(src); while (names != NULL && values != NULL) { char *cp, *vname = values->name; size_t name_len, value_len; name_len = strlen(names->name); value_len = strlen(vname); /* Translate any C++ scoping to Python. */ while ((cp = strstr(vname, "::")) != NULL) { char *new_vname = sipMalloc(value_len); size_t pos = cp - vname; memcpy(new_vname, vname, pos); new_vname[pos] = '.'; strcpy(new_vname + pos + 1, cp + 2); if (vname != values->name) free(vname); vname = new_vname; --value_len; } while ((cp = strstr(dst, names->name)) != NULL) { char *new_dst = sipMalloc(strlen(dst) - name_len + value_len + 1); memcpy(new_dst, dst, cp - dst); memcpy(new_dst + (cp - dst), vname, value_len); strcpy(new_dst + (cp - dst) + value_len, cp + name_len); free(dst); dst = new_dst; } if (vname != values->name) free(vname); names = names->next; values = values->next; } return dst; } /* * Search for a name in a scope and return the corresponding type. */ static void searchClassScope(sipSpec *pt, classDef *c_scope, scopedNameDef *snd, argDef *ad) { scopedNameDef *tmpsnd = NULL; mroDef *mro; for (mro = c_scope->mro; mro != NULL; mro = mro->next) { if (isDuplicateSuper(mro)) continue; /* Append the name to the scope and see if it exists. */ tmpsnd = copyScopedName(classFQCName(mro->cd)); appendScopedName(&tmpsnd, copyScopedName(snd)); searchMappedTypes(pt, mro->cd->iff->module, tmpsnd, ad); if (ad->atype != no_type) break; searchTypedefs(pt, tmpsnd, ad); if (ad->atype != no_type) break; searchEnums(pt, tmpsnd, ad); if (ad->atype != no_type) break; searchClasses(pt, mro->cd->iff->module, tmpsnd, ad); if (ad->atype != no_type) break; freeScopedName(tmpsnd); tmpsnd = NULL; } if (tmpsnd != NULL) freeScopedName(tmpsnd); } /* * Search the mapped types for a name and return the type. */ static void searchMappedTypes(sipSpec *pt, moduleDef *context, scopedNameDef *snd, argDef *ad) { mappedTypeDef *mtd; scopedNameDef *oname; /* Patch back to defined types so we can use sameBaseType(). */ if (snd != NULL) { oname = ad->u.snd; ad->u.snd = snd; ad->atype = defined_type; } for (mtd = pt->mappedtypes; mtd != NULL; mtd = mtd->next) if (sameBaseType(ad, &mtd->type)) { /* * If we a building a consolidated module and this mapped type is * defined in a different module then see if that other module is * in a different branch of the module hierarchy. */ if (isConsolidated(pt->module) && context != mtd->iff->module) { moduleListDef *mld; for (mld = context->allimports; mld != NULL; mld = mld->next) if (mld->module == mtd->iff->module) break; /* If it's in a different branch then we ignore it. */ if (mld == NULL) continue; } /* Copy the type. */ ad->atype = mapped_type; ad->u.mtd = mtd; return; } /* Restore because we didn't find anything. */ if (snd != NULL) { ad->u.snd = oname; ad->atype = no_type; } } /* * Search the typedefs for a name and return the type. */ void searchTypedefs(sipSpec *pt, scopedNameDef *snd, argDef *ad) { typedefDef *td; for (td = pt->typedefs; td != NULL; td = td->next) { int res = compareScopedNames(td->fqname, snd); if (res == 0) { /* Copy the type. */ ad->atype = td->type.atype; ad->argflags |= td->type.argflags; ad->nrderefs += td->type.nrderefs; ad->doctype = td->type.doctype; ad->u = td->type.u; if (ad->original_type == NULL) ad->original_type = td; break; } /* The list is sorted so stop if we have gone too far. */ if (res > 0) break; } } /* * Search the enums for a name and return the type. */ static void searchEnums(sipSpec *pt, scopedNameDef *snd, argDef *ad) { enumDef *ed; for (ed = pt->enums; ed != NULL; ed = ed->next) { if (ed->fqcname == NULL) continue; if (compareScopedNames(ed->fqcname, snd) == 0) { ad->atype = enum_type; ad->u.ed = ed; break; } } } /* * Search the classes for one with a particular name and return it as a type. */ static void searchClasses(sipSpec *pt, moduleDef *context, scopedNameDef *cname, argDef *ad) { classDef *cd; for (cd = pt->classes; cd != NULL; cd = cd->next) { /* * Ignore an external class unless it was declared in the same context * (ie. module) as the name is being used. */ if (isExternal(cd) && cd->iff->module != context) continue; if (compareScopedNames(classFQCName(cd), cname) == 0) { ad->atype = class_type; ad->u.cd = cd; break; } } } /* * Print an error message describing an undefined type to stderr and terminate. */ static void fatalNoDefinedType(scopedNameDef *snd) { fatalScopedName(snd); fatal(" is undefined\n"); } /* * Make sure all interface files for a signature are used. */ static void ifaceFilesAreUsedBySignature(ifaceFileList **used, signatureDef *sd) { int a; ifaceFileIsUsed(used, &sd->result); for (a = 0; a < sd->nrArgs; ++a) ifaceFileIsUsed(used, &sd->args[a]); } /* * Make sure all interface files for a function are used. */ static void ifaceFilesAreUsedByOverload(ifaceFileList **used, overDef *od) { throwArgs *ta; ifaceFilesAreUsedBySignature(used, &od->pysig); if (od->cppsig != &od->pysig) ifaceFilesAreUsedBySignature(used, od->cppsig); if ((ta = od->exceptions) != NULL) { int a; for (a = 0; a < ta->nrArgs; ++a) addToUsedList(used, ta->args[a]->iff); } } /* * If a type has an interface file then add it to the the given list of used * interface files so that the header file is #included in the generated code. */ static void ifaceFileIsUsed(ifaceFileList **used, argDef *ad) { ifaceFileDef *iff; if ((iff = getIfaceFile(ad)) != NULL) { addToUsedList(used, iff); /* * For mapped type templates we also need the template arguments. * These will be in the mapped type's used list (which itself will be * empty for non-template mapped types). */ if (ad->atype == mapped_type) { ifaceFileList *iffl = iff->used; for (iffl = iff->used; iffl != NULL; iffl = iffl->next) addToUsedList(used, iffl->iff); } } } /* * Return the interface file for a type, or NULL if it doesn't have one. */ static ifaceFileDef *getIfaceFile(argDef *ad) { ifaceFileDef *iff; switch (ad->atype) { case class_type: iff = ad->u.cd->iff; break; case mapped_type: iff = ad->u.mtd->iff; break; case enum_type: if (ad->u.ed->fqcname != NULL) { if (ad->u.ed->ecd != NULL) { iff = ad->u.ed->ecd->iff; break; } if (ad->u.ed->emtd != NULL) { iff = ad->u.ed->emtd->iff; break; } } /* Drop through. */ default: iff = NULL; } return iff; } /* * Create the sorted array of numbered types for a module. */ static void createSortedNumberedTypesTable(sipSpec *pt, moduleDef *mod) { classDef *cd; mappedTypeDef *mtd; enumDef *ed; argDef *ad; int i; /* Count the how many types there are. */ mod->nrtypes = 0; for (cd = pt->classes; cd != NULL; cd = cd->next) { if (cd->iff->module != mod) continue; if (cd->iff->first_alt != cd->iff) continue; mod->nrtypes++; } for (mtd = pt->mappedtypes; mtd != NULL; mtd = mtd->next) { if (mtd->iff->module != mod) continue; if (mtd->iff->first_alt != mtd->iff) continue; mod->nrtypes++; } for (ed = pt->enums; ed != NULL; ed = ed->next) { if (ed->module != mod) continue; if (ed->fqcname == NULL) continue; if (ed->ecd != NULL && isTemplateClass(ed->ecd)) continue; if (ed->first_alt != ed) continue; mod->nrtypes++; } if (mod->nrtypes == 0) return; /* Allocate and populate the table. */ ad = mod->types = sipCalloc(mod->nrtypes, sizeof (argDef)); for (cd = pt->classes; cd != NULL; cd = cd->next) { if (cd->iff->module != mod) continue; if (cd->iff->first_alt != cd->iff) continue; ad->atype = class_type; ad->u.cd = cd; ad->name = cd->iff->name; ++ad; } for (mtd = pt->mappedtypes; mtd != NULL; mtd = mtd->next) { if (mtd->iff->module != mod) continue; if (mtd->iff->first_alt != mtd->iff) continue; ad->atype = mapped_type; ad->u.mtd = mtd; ad->name = mtd->cname; ++ad; } for (ed = pt->enums; ed != NULL; ed = ed->next) { if (ed->module != mod) continue; if (ed->fqcname == NULL) continue; if (ed->ecd != NULL && isTemplateClass(ed->ecd)) continue; if (ed->first_alt != ed) continue; ad->atype = enum_type; ad->u.ed = ed; ad->name = ed->cname; ++ad; } /* Sort the table and assign type numbers. */ qsort(mod->types, mod->nrtypes, sizeof (argDef), compareTypes); for (ad = mod->types, i = 0; i < mod->nrtypes; ++i, ++ad) { switch (ad->atype) { case class_type: ad->u.cd->iff->ifacenr = i; /* If we find a class called QObject, assume it's Qt. */ if (strcmp(ad->name->text, "QObject") == 0) mod->qobjclass = i; break; case mapped_type: ad->u.mtd->iff->ifacenr = i; break; case enum_type: ad->u.ed->enumnr = i; break; } } } /* * The qsort helper to compare two generated type names. */ static int compareTypes(const void *t1, const void *t2) { return strcmp(((argDef *)t1)->name->text, ((argDef *)t2)->name->text); } /* * Return TRUE if we are generating code for a module, ie. we are a component * of a consolidated module, or the main module where there is no consolidated * module. */ static int generatingCodeForModule(sipSpec *pt, moduleDef *mod) { if (isConsolidated(pt->module)) return (pt->module == mod->container); return (pt->module == mod); }