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