diff options
Diffstat (limited to 'python/sip/sipgen/transform.c')
-rw-r--r-- | python/sip/sipgen/transform.c | 2856 |
1 files changed, 2856 insertions, 0 deletions
diff --git a/python/sip/sipgen/transform.c b/python/sip/sipgen/transform.c new file mode 100644 index 00000000..036a124b --- /dev/null +++ b/python/sip/sipgen/transform.c @@ -0,0 +1,2856 @@ +/* + * The parse tree transformation module for SIP. + * + * Copyright (c) 2007 + * Riverbank Computing Limited <info@riverbankcomputing.co.uk> + * + * 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. + * + * SIP is supplied WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + */ + + +#include <stddef.h> +#include <string.h> +#include <stdlib.h> + +#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(sipSpec *pt, moduleDef *mod); +static void addUniqueModule(moduleDef *mod, moduleDef *imp); +static void ensureInput(classDef *,overDef *,argDef *); +static void defaultInput(argDef *); +static void defaultOutput(classDef *,overDef *,argDef *); +static void assignClassNrs(sipSpec *,moduleDef *,nodeDef *); +static void assignEnumNrs(sipSpec *pt); +static void positionClass(classDef *); +static void addNodeToParent(nodeDef *,classDef *); +static void addAutoOverload(sipSpec *,classDef *,overDef *); +static void ifaceFileIsUsed(sipSpec *, ifaceFileDef *, argDef *); +static void ifaceFilesAreUsed(sipSpec *, ifaceFileDef *, overDef *); +static void ifaceFilesAreUsedByMethod(sipSpec *, classDef *, memberDef *); +static void ifaceFilesAreUsedFromOther(sipSpec *pt, signatureDef *sd); +static void scopeDefaultValue(sipSpec *,classDef *,argDef *); +static void setHierarchy(sipSpec *,classDef *,classDef *,classList **); +static void transformCtors(sipSpec *,classDef *); +static void transformCasts(sipSpec *,classDef *); +static void addDefaultCopyCtor(classDef *); +static void transformOverloads(sipSpec *,classDef *,overDef *); +static void transformVariableList(sipSpec *); +static void transformMappedTypes(sipSpec *); +static void getVisibleMembers(sipSpec *,classDef *); +static void getVirtuals(sipSpec *pt,classDef *cd); +static void getClassVirtuals(classDef *,classDef *); +static void transformTypedefs(sipSpec *pt); +static void resolveMappedTypeTypes(sipSpec *,mappedTypeDef *); +static void resolveCtorTypes(sipSpec *,classDef *,ctorDef *); +static void resolveFuncTypes(sipSpec *,moduleDef *,classDef *,overDef *); +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 searchScope(sipSpec *,classDef *,scopedNameDef *,argDef *); +static void searchMappedTypes(sipSpec *,scopedNameDef *,argDef *); +static void searchTypedefs(sipSpec *,scopedNameDef *,argDef *); +static void searchEnums(sipSpec *,scopedNameDef *,argDef *); +static void searchClasses(sipSpec *,moduleDef *mod,scopedNameDef *,argDef *); +static void appendToMRO(mroDef *,mroDef ***,classDef *); +static void moveClassCasts(sipSpec *pt, classDef *cd); +static void moveGlobalSlot(sipSpec *pt, memberDef *gmd); +static void filterVirtualHandlers(moduleDef *mod); +static ifaceFileDef *getIfaceFile(argDef *ad); +static mappedTypeDef *instantiateMappedTypeTemplate(sipSpec *pt, moduleDef *mod, mappedTypeTmplDef *mtt, argDef *type); +static classDef *getProxy(sipSpec *pt, classDef *cd); + + +/* + * Transform the parse tree. + */ + +void transform(sipSpec *pt) +{ + moduleDef *mod; + moduleListDef *mld; + classDef *cd, *rev, **tail; + classList *newl; + overDef *od; + mappedTypeDef *mtd; + virtHandlerDef *vhd; + int nr; + + if (pt -> module -> name == NULL) + fatal("No %%Module has been specified for the module\n"); + + /* + * 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, eg. when assigning class + * numbers. 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. */ + for (mod = pt->modules; mod != NULL; mod = mod->next) + setAllImports(pt, mod); + + /* 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 typedefs, variables and global functions. */ + transformTypedefs(pt); + transformVariableList(pt); + transformOverloads(pt,NULL,pt -> overs); + + /* Transform class ctors, functions and casts. */ + for (cd = pt -> classes; cd != NULL; cd = cd -> next) + { + transformCtors(pt,cd); + + if (!pt -> genc) + { + transformOverloads(pt,cd,cd -> overs); + transformCasts(pt, cd); + } + } + + /* Transform mapped types based on templates. */ + transformMappedTypes(pt); + + /* 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); + + /* + * Go through each class and add it to it's defining module's tree of + * classes. The tree reflects the namespace hierarchy. + */ + for (cd = pt -> classes; cd != NULL; cd = cd -> next) + addNodeToParent(&cd -> iff -> module -> root,cd); + + for (cd = pt -> classes; cd != NULL; cd = cd -> next) + positionClass(cd); + + /* Assign module specific class numbers for all modules. */ + for (mod = pt->modules; mod != NULL; mod = mod->next) + assignClassNrs(pt, mod, &mod->root); + + /* Assign module specific enum numbers for all enums. */ + assignEnumNrs(pt); + + /* 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); + + /* Allocate mapped types numbers. */ + for (mtd = pt -> mappedtypes; mtd != NULL; mtd = mtd -> next) + mtd -> mappednr = mtd -> iff -> module -> nrmappedtypes++; + + /* + * 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) + { + memberDef *md; + + for (cd = pt -> classes; cd != NULL; cd = cd -> next) + if (cd->iff->module == pt->module) + moveClassCasts(pt, cd); + + for (md = pt->othfuncs; md != NULL; md = md->next) + if (md->slot != no_slot && md->module == pt->module) + moveGlobalSlot(pt, md); + } + + /* Generate the different class views. */ + for (cd = pt -> classes; cd != NULL; cd = cd -> next) + { + ifaceFileDef *iff = cd -> iff; + + if (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 (iff -> type == namespace_iface && iff -> module == pt -> module) + { + memberDef *md; + + for (md = cd -> members; md != NULL; md = md -> next) + ifaceFilesAreUsedByMethod(pt, cd, md); + } + } + + /* + * In case there are any global functions that need external interface + * files. + */ + for (od = pt -> overs; od != NULL; od = od -> next) + if (od->common->module == pt->module) + ifaceFilesAreUsedFromOther(pt, &od->pysig); + + /* + * Remove redundant virtual handlers. It's important that earlier, + * ie. those at the deepest level of %Import, are done first. + */ + nr = 0; + + for (mld = pt->module->allimports; mld != NULL; mld = mld->next) + { + mld->module->modulenr = nr++; + filterVirtualHandlers(mld->module); + } + + pt->module->modulenr = nr; + filterVirtualHandlers(pt->module); + + /* + * 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 = pt->module->virthandlers; vhd != NULL; vhd = vhd->next) + if (!isDuplicateVH(vhd)) + ifaceFilesAreUsedFromOther(pt, vhd->cppsig); + + /* Update proxies with some information from the real classes. */ + for (cd = pt->proxies; cd != NULL; cd = cd->next) + cd->classnr = cd->real->classnr; +} + + +/* + * 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(sipSpec *pt, 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(pt, 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 any class casts to its correct class, or publish as a ctor extender. + */ +static void moveClassCasts(sipSpec *pt, 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 the destination class is in a different module then use + * a proxy. + */ + if (dcd->iff->module != pt->module) + dcd = getProxy(pt, dcd); + + /* Create the new ctor. */ + ct = sipMalloc(sizeof (ctorDef)); + + ct->ctorflags = SECT_IS_PUBLIC | CTOR_CAST; + ct->cppsig = &ct->pysig; + ct->exceptions = NULL; + ct->methodcode = NULL; + ct->prehook = NULL; + ct->posthook = NULL; + ct->next = NULL; + + /* Add the source class as the only argument. */ + 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(pt, dcd->iff, 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, memberDef *gmd) +{ + overDef **odp = &pt->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]; + + 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 == 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 == enum_type) + { + mdhead = &arg1->u.ed->slots; + odhead = &arg1->u.ed->overs; + mod = arg1->u.ed->module; + nd = arg1->u.ed->pyname; + second = TRUE; + } + else + { + 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(pt, 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. */ + setIsPublic(od); + od->common = md; + od->next = *odhead; + + *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; + } + } +} + + +/* + * Create a proxy for a class if it doesn't already exist. Proxies are used as + * containers for cross-module extenders. + */ +static classDef *getProxy(sipSpec *pt, classDef *cd) +{ + classDef *pcd; + + for (pcd = pt->proxies; pcd != NULL; pcd = pcd->next) + if (pcd->iff == cd->iff) + return pcd; + + pcd = sipMalloc(sizeof (classDef)); + + pcd->classflags = 0; + pcd->userflags = 0; + pcd->classnr = -1; + pcd->pyname = cd->pyname; + pcd->iff = cd->iff; + pcd->ecd = cd->ecd; + pcd->real = cd; + pcd->node = NULL; + pcd->supers = cd->supers; + pcd->mro = cd->mro; + pcd->td = NULL; + pcd->ctors = NULL; + pcd->defctor = NULL; + pcd->dealloccode = NULL; + pcd->dtorcode = NULL; + pcd->dtorexceptions = NULL; + pcd->members = NULL; + pcd->overs = NULL; + pcd->casts = NULL; + pcd->vmembers = NULL; + pcd->visible = NULL; + pcd->cppcode = NULL; + pcd->hdrcode = NULL; + pcd->convtosubcode = NULL; + pcd->subbase = NULL; + pcd->convtocode = NULL; + pcd->travcode = NULL; + pcd->clearcode = NULL; + pcd->readbufcode = NULL; + pcd->writebufcode = NULL; + pcd->segcountcode = NULL; + pcd->charbufcode = NULL; + pcd->next = pt->proxies; + + pt->proxies = pcd; + + return pcd; +} + + +/* + * 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 filterVirtualHandlers(moduleDef *mod) +{ + virtHandlerDef *vhd; + + 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 (cd -> iff -> module == pt -> 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 (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. */ + for (cl = cd -> supers; cl != NULL; cl = cl -> next) + { + mroDef *mro; + + /* + * 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 the super-class is a QObject sub-class then this one is + * as well. + */ + if (isQObjectSubClass(mro->cd)) + setIsQObjectSubClass(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; + } + } + } + + /* + * 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. + */ +static void transformTypedefs(sipSpec *pt) +{ + typedefDef *td; + + for (td = pt -> typedefs; td != NULL; td = td -> next) + getBaseType(pt, td->module, td -> ecd, &td -> type); +} + + +/* + * Transform the data types for mapped types based on a template. + */ +static void transformMappedTypes(sipSpec *pt) +{ + mappedTypeDef *mt; + + for (mt = pt -> mappedtypes; mt != NULL; mt = mt -> next) + { + /* Nothing to do if this isn't template based. */ + + if (mt -> type.atype == template_type) + resolveMappedTypeTypes(pt,mt); + } +} + + +/* + * 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"); + } + } +} + + +/* + * 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) + { + getBaseType(pt, cd->iff->module, cd, &al->arg); + + if (al->arg.atype != class_type) + { + fatalScopedName(classFQCName(cd)); + fatal(" operator cast must be to a class\n"); + } + } +} + + +/* + * Add a default copy ctor is required. + */ +static void addDefaultCopyCtor(classDef *cd) +{ + ctorDef *copyct; + mroDef *mro; + + /* See if there is a private copy ctor in the hierarchy. */ + + copyct = NULL; + + 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) || ad -> atype != class_type || + ad -> u.cd != mro -> cd) + continue; + + /* Stop now if the copy ctor is private. */ + if (isPrivateCtor(ct)) + return; + + /* + * Remember if it's in the class we are dealing with. + */ + if (mro == cd -> mro) + copyct = ct; + + break; + } + } + + if (copyct == NULL) + { + ctorDef **tailp; + + /* Create a default public copy ctor. */ + + copyct = sipMalloc(sizeof (ctorDef)); + + copyct -> ctorflags = SECT_IS_PUBLIC; + copyct -> pysig.nrArgs = 1; + copyct -> pysig.args[0].name = "other"; + 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; + copyct -> exceptions = NULL; + copyct -> methodcode = NULL; + copyct -> prehook = NULL; + copyct -> posthook = NULL; + copyct -> next = NULL; + + /* 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 transformOverloads(sipSpec *pt, classDef *scope, overDef *overs) +{ + overDef *od; + + for (od = overs; od != NULL; od = od -> next) + { + overDef *prev; + + resolveFuncTypes(pt, od->common->module, 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; + + if (samePythonSignature(&prev->pysig, &od->pysig)) + { + if (scope != NULL) + { + fatalScopedName(classFQCName(scope)); + fatal("::"); + } + + fatal("%s() has overloaded functions with the same Python signature\n", od->common->pyname->text); + } + } + } +} + + +/* + * Transform the data types for the variables. + */ +static void transformVariableList(sipSpec *pt) +{ + varDef *vd; + + for (vd = pt -> vars; vd != NULL; vd = vd -> next) + 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; + + /* + * If the base class is in the main module, see if it needs to + * publish any protected enums. + */ + if (cd -> iff -> module == pt -> module) + { + enumDef *ed; + + for (ed = pt -> enums; ed != NULL; ed = ed -> next) + { + /* Skip unless we are the publisher. */ + if (ed -> pcd != mrocd) + continue; + + /* + * If we are not in the main module then the + * base class must take over as the publisher. + */ + if (mrocd -> iff -> module != pt -> module) + ed -> pcd = 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; + + addToUsedList(&cd->iff->used, mrocd->iff); + + cd -> visible = vl; + + for (od = mrocd -> overs; od != NULL; od = od -> next) + if (od -> common == md) + { + if (isAbstract(od)) + setIsAbstractClass(cd); + + ifaceFilesAreUsed(pt, cd->iff, od); + + /* See if we need the name. */ + if (cd->iff->module != pt->module) + continue; + + if (isProtected(od) || (isSignal(od) && !optNoEmitters(pt))) + setIsUsedName(md->pyname); + } + } + } + } +} + + +/* + * 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 main module. + */ + 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 (cd->iff->module == pt->module) + { + /* Make sure we get the name. */ + setIsUsedName(vod -> o.common -> pyname); + + ifaceFilesAreUsed(pt, cd->iff, &vod -> o); + } + } +} + + +/* + * 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; + templateDef *td = mt -> type.u.td; + + for (a = 0; a < td -> types.nrArgs; ++a) + { + getBaseType(pt, mt->iff->module, NULL, &td->types.args[a]); + + ifaceFileIsUsed(pt, mt->iff, &td->types.args[a]); + } +} + + +/* + * 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(pt, scope->iff, ad); + scopeDefaultValue(pt,scope,ad); + } +} + + +/* + * Resolve the types of a function. + */ +static void resolveFuncTypes(sipSpec *pt, moduleDef *mod, classDef *scope, overDef *od) +{ + argDef *res; + + /* Handle any C++ signature. */ + if (od -> cppsig != &od -> pysig) + { + int a; + + getBaseType(pt,mod, scope, &od->cppsig->result); + + for (a = 0; a < od -> cppsig -> nrArgs; ++a) + getBaseType(pt, mod, scope, &od->cppsig->args[a]); + } + + /* Handle the Python signature. */ + resolvePySigTypes(pt, mod, scope, od, &od->pysig,isSignal(od)); + + /* These slots must return int. */ + res = &od -> pysig.result; + + 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"); + } + } + 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 %%Method code, a valid %%VirtualCatcherCode and a valid C++ signature\n",od -> cppname); + + fatal("%s() unsupported function argument type - provide %%Method code 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 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 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) + { + fatalScopedName(vd -> fqcname); + fatal(" has an unsupported type\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(pt, vd->ecd->iff, vtype); + else + ifaceFileIsUsed(pt, NULL, vtype); + + /* + * Instance variables or static class variables (unless they are + * constants) need a handler. + */ + if (vd -> ecd != NULL && vd -> accessfunc == NULL && + (!isStaticVar(vd) || vtype -> nrderefs != 0 || !isConstArg(vtype))) + { + 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 sstring_type: + case ustring_type: + case string_type: + case wstring_type: + if (isReference(ad)) + { + if (outputs && ad -> nrderefs <= 1) + { + defaultOutput(cd,od,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(cd,od,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 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(cd,od,ad); + return TRUE; + } + } + else if (ad -> nrderefs == 0) + { + ensureInput(cd,od,ad); + return TRUE; + } + else if (ad -> nrderefs == 1 && outputs) + { + defaultOutput(cd,od,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(cd,od,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(cd,od,ad); + return TRUE; + } + + break; + + case struct_type: + case void_type: + if (isReference(ad)) + { + if (ad -> nrderefs == 1 && outputs) + { + defaultOutput(cd,od,ad); + return TRUE; + } + } + else if (ad -> nrderefs == 1) + { + ensureInput(cd,od,ad); + return TRUE; + } + else if (ad -> nrderefs == 2 && outputs) + { + defaultOutput(cd,od,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(classDef *cd,overDef *od,argDef *ad) +{ + if (isOutArg(ad)) + { + if (isConstArg(ad)) + { + if (cd != NULL) + { + fatalScopedName(classFQCName(cd)); + fatal("::"); + } + + if (od != NULL) + fatal("%s",od -> cppname); + + fatal("() const argument cannot have /Out/ specified\n"); + } + } + else if (!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 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) +{ + 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; + + 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) +#define pyAsFloat(t) ((t) == cfloat_type || (t) == float_type || \ + (t) == cdouble_type || (t) == double_type) +#define pyAsInt(t) ((t) == cint_type || (t) == bool_type || \ + (t) == short_type || (t) == ushort_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) + +/* + * 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); + } + + /* 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) + 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 (!sameScopedName(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 (!sameScopedName(a1 -> u.sname,a2 -> u.sname) != 0) + return FALSE; + + break; + + case defined_type: + if (!sameScopedName(a1 -> u.snd,a2 -> u.snd)) + 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; +} + + +/* + * Return TRUE if two scoped names are the same. + */ + +int sameScopedName(scopedNameDef *snd1,scopedNameDef *snd2) +{ + while (snd1 != NULL && snd2 != NULL && strcmp(snd1 -> name,snd2 -> name) == 0) + { + snd1 = snd1 -> next; + snd2 = snd2 -> next; + } + + return (snd1 == NULL && snd2 == NULL); +} + + +/* + * 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 *defscope, 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 (defscope != NULL) + searchScope(pt,defscope,snd,type); + + if (type -> atype == no_type) + searchMappedTypes(pt,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, defscope, &type->u.sa->args[sa]); + } + + /* See if the type refers to an instantiated template. */ + if (type->atype == template_type) + { + classDef *cd; + + for (cd = pt->classes; cd != NULL; cd = cd->next) + if (cd->td != NULL && + sameScopedName(cd->td->fqname, type->u.td->fqname) && + sameSignature(&cd->td->types, &type->u.td->types, TRUE)) + { + type->atype = class_type; + type->u.cd = cd; + + break; + } + } + + /* Replace the base type if it has been mapped. */ + if (type -> atype == struct_type || type -> atype == template_type) + { + searchMappedTypes(pt,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 (sameScopedName(type->u.td->fqname, mtt->mt->type.u.td->fqname) && sameTemplateSignature(&type->u.td->types, &mtt->mt->type.u.td->types, TRUE)) + { + type->u.mtd = instantiateMappedTypeTemplate(pt, mod, mtt, type); + type->atype = mapped_type; + + 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(type); + + mtd->iff = findIfaceFile(pt, mod, type->u.td->fqname, mappedtype_iface, type); + mtd->iff->module = mod; + + mtd->hdrcode = templateCode(pt, &mtd->iff->used, mtt->mt->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; +} + + +/* + * Search for a name in a scope and return the corresponding type. + */ + +static void searchScope(sipSpec *pt,classDef *scope,scopedNameDef *snd, + argDef *ad) +{ + scopedNameDef *tmpsnd = NULL; + mroDef *mro; + + for (mro = 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,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,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)) + { + /* 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. + */ + +static void searchTypedefs(sipSpec *pt,scopedNameDef *snd,argDef *ad) +{ + typedefDef *td; + + for (td = pt -> typedefs; td != NULL; td = td -> next) + if (sameScopedName(td -> fqname,snd)) + { + /* Copy the type. */ + + ad -> atype = td -> type.atype; + ad -> argflags |= td -> type.argflags; + ad -> nrderefs += td -> type.nrderefs; + ad -> u = td -> type.u; + + 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 (sameScopedName(ed -> fqcname,snd)) + { + 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 *mod, 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 != mod) + continue; + + if (sameScopedName(classFQCName(cd), cname)) + { + 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 external interface files for all other functions of a module + * are used. + */ +static void ifaceFilesAreUsedFromOther(sipSpec *pt, signatureDef *sd) +{ + int a; + ifaceFileDef *iff; + + if ((iff = getIfaceFile(&sd->result)) != NULL && iff->module != pt->module) + addToUsedList(&pt->used, iff); + + for (a = 0; a < sd->nrArgs; ++a) + if ((iff = getIfaceFile(&sd->args[a])) != NULL && iff->module != pt->module) + addToUsedList(&pt->used, iff); +} + + +/* + * Make sure all interface files for all overloads of a method are used. + */ +static void ifaceFilesAreUsedByMethod(sipSpec *pt, classDef *cd, memberDef *md) +{ + overDef *od; + + for (od = cd -> overs; od != NULL; od = od -> next) + if (od -> common == md) + ifaceFilesAreUsed(pt, cd->iff, od); +} + + +/* + * Make sure all interface files for a signature are used. + */ +static void ifaceFilesAreUsed(sipSpec *pt, ifaceFileDef *iff, overDef *od) +{ + int a; + + ifaceFileIsUsed(pt, iff, &od->pysig.result); + + for (a = 0; a < od->pysig.nrArgs; ++a) + ifaceFileIsUsed(pt, iff, &od->pysig.args[a]); + + if (od->cppsig != &od->pysig) + { + ifaceFileIsUsed(pt, iff, &od->cppsig->result); + + for (a = 0; a < od->cppsig->nrArgs; ++a) + ifaceFileIsUsed(pt, iff, &od->cppsig->args[a]); + } +} + + +/* + * If a type has an interface file then add it to the appropriate list of used + * interface files so that the header file is #included in the generated code. + */ +static void ifaceFileIsUsed(sipSpec *pt, ifaceFileDef *iff, argDef *ad) +{ + ifaceFileDef *usediff; + + if ((usediff = getIfaceFile(ad)) != NULL && usediff != iff) + { + ifaceFileList *iffl, **used; + + used = (iff != NULL ? &iff->used : &pt->used); + + iffl = addToUsedList(used, usediff); + + /* + * If the type is a protected enum then its scoping shadow + * class is needed in the generated header file. + */ + if (ad->atype == enum_type && isProtectedEnum(ad->u.ed)) + iffl->header = TRUE; + } +} + + +/* + * 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 && ad -> u.ed -> ecd != NULL) + { + iff = ad -> u.ed -> ecd -> iff; + break; + } + + /* Drop through. */ + + default: + iff = NULL; + } + + return iff; +} + + +/* + * Position a class so that it is after all its super-classes. + */ +static void positionClass(classDef *cd) +{ + classList *cl; + + /* See if it has already been done. */ + if (cd -> node -> ordered) + return; + + for (cl = cd -> supers; cl != NULL; cl = cl -> next) + { + nodeDef **ndp, *nd1, *nd2, *rp; + + /* Ignore super-classes from different modules. */ + if (cl -> cd -> iff -> module != cd -> iff -> module) + continue; + + /* Make sure the super-class is positioned. */ + positionClass(cl -> cd); + + /* + * Find ancestors of the two that are siblings (ie. they have a + * common parent). + */ + rp = &cd -> iff -> module -> root; + + for (nd1 = cd -> node; nd1 != rp; nd1 = nd1 -> parent) + { + for (nd2 = cl -> cd -> node; nd2 != rp; nd2 = nd2 -> parent) + if (nd1 -> parent == nd2 -> parent) + break; + + if (nd2 != rp) + break; + } + + /* + * The first node must appear after the second in the common + * parent's list of children. + */ + for (ndp = &nd1 -> parent -> child; *ndp != NULL; ndp = &(*ndp) -> next) + { + nodeDef *nd = *ndp; + + if (nd == nd2) + break; + + if (nd == nd1) + { + /* Remove this one from the list. */ + *ndp = nd -> next; + + /* Find the super-class ancestor. */ + while (*ndp != nd2) + ndp = &(*ndp) -> next; + + /* + * Put this one back after the super-class + * ancestor. + */ + nd -> next = (*ndp) -> next; + (*ndp) -> next = nd; + + break; + } + } + } + + cd -> node -> ordered = TRUE; +} + + +/* + * Make sure a class is in the namespace tree. + */ +static void addNodeToParent(nodeDef *root,classDef *cd) +{ + nodeDef *nd, *parent; + + /* Skip classes already in the tree. */ + if (cd -> node != NULL) + return; + + /* Add this child to the parent. */ + nd = sipMalloc(sizeof (nodeDef)); + + nd -> ordered = FALSE; + nd -> cd = cd; + nd -> child = NULL; + + /* Get the address of the parent node. */ + if (cd -> ecd == NULL) + parent = root; + else + { + /* Make sure the parent is in the tree. */ + addNodeToParent(root,cd -> ecd); + + parent = cd -> ecd -> node; + } + + nd -> parent = parent; + + /* Insert this at the head of the parent's children. */ + nd -> next = parent -> child; + parent -> child = nd; + + /* Remember where we are in the tree. */ + cd -> node = nd; +} + + +/* + * Assign the module specific class number for a class and all it's children. + */ +static void assignClassNrs(sipSpec *pt,moduleDef *mod,nodeDef *nd) +{ + classDef *cd; + nodeDef *cnd; + + /* Assign the class if it's not the root. */ + if ((cd = nd -> cd) != NULL) + { + cd -> classnr = mod -> nrclasses++; + + /* + * If we find a class defined in the main module called QObject, assume + * it's Qt. + */ + if (mod == pt -> module && strcmp(classBaseName(cd),"QObject") == 0) + pt -> qobjclass = cd -> classnr; + } + + /* Assign all it's children. */ + for (cnd = nd -> child; cnd != NULL; cnd = cnd -> next) + assignClassNrs(pt,mod,cnd); +} + + +/* + * Assign the module specific enum number for all named enums. + */ +static void assignEnumNrs(sipSpec *pt) +{ + enumDef *ed; + + for (ed = pt -> enums; ed != NULL; ed = ed -> next) + if (ed -> fqcname != NULL) + ed -> enumnr = ed -> module -> nrenums++; +} |