SIP4 python bindings generator for TQt
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
sip4-tqt/sipgen/transform.c

3445 lines
90 KiB

/*
* The parse tree transformation module for SIP.
*
* Copyright (c) 2010 Riverbank Computing Limited <info@riverbankcomputing.com>
*
* 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 TQObject class. This flag will ripple through all derived
* classes when we set the hierarchy.
*/
if (strcmp(classBaseName(cd), TQOBJECT_OBJECT_NAME_STRING) == 0)
setIsTQObjectSubClass(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(classFTQCName(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 TQt 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(classFTQCName(dcd));
fatal("::");
fatalScopedName(classFTQCName(dcd));
fatal("(");
fatalScopedName(classFTQCName(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 PyTQt 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 TQt's tqmoc).
*/
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(classFTQCName(cd));
fatal(" and ");
fatalScopedName(classFTQCName(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 TQObject sub-class then this one is
* as well.
*/
if (isTQObjectSubClass(mro->cd))
setIsTQObjectSubClass(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(classFTQCName(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(classFTQCName(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 = &copyct->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) && pluginPyTQt3(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(classFTQCName(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(classFTQCName(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(classFTQCName(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(classFTQCName(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(classFTQCName(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(classFTQCName(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)