/* * The parse tree transformation module for SIP. * * Copyright (c) 2007 * 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. * * 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(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++; }