File indexing completed on 2024-04-21 15:33:21

0001 # -*- coding: utf-8 -*-
0002 #     Copyright 2009 Simon Edwards <simon@simonzone.com>
0003 #
0004 # This program is free software; you can redistribute it and/or modify
0005 # it under the terms of the GNU General Public License as published by
0006 # the Free Software Foundation; either version 2 of the License, or
0007 # (at your option) any later version.
0008 #
0009 # This program is distributed in the hope that it will be useful,
0010 # but WITHOUT ANY WARRANTY; without even the implied warranty of
0011 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0012 # GNU General Public License for more details.
0013 #
0014 # You should have received a copy of the GNU General Public License
0015 # along with this program; if not, write to the
0016 # Free Software Foundation, Inc.,
0017 # 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
0018 
0019 import os.path
0020 import re
0021 from .sealed import sealed
0022 import kbindinggenerator.sipsymboldata as sipsymboldata
0023 import kbindinggenerator.cppsymboldata as cppsymboldata
0024 
0025 class CppToSipTransformer(object):
0026     @sealed
0027     def __init__(self):
0028         self._sipsym = None
0029         self._exportMacros = None
0030         self._ignoredBaseClasses = []
0031         self._copyrightNotice = None
0032         self._cppScope = None
0033         
0034     def setExportMacros(self, macroList):
0035         self._exportMacros = set(macroList) if macroList is not None else None
0036         
0037     def setIgnoreBaseClasses(self, baseClassList):
0038         self._ignoredBaseClasses = baseClassList if baseClassList is not None else []
0039         
0040     def setCopyrightNotice(self,noticeText):
0041         self._copyrightNotice = noticeText
0042         
0043     def convert(self, cppScope, sipsymboldata):
0044         self._cppScope = cppScope
0045         self._sipsym = sipsymboldata
0046         sipScope = self._sipsym.newScope()
0047         sipScope.setHeaderFilename(cppScope.headerFilename())
0048         
0049         if self._copyrightNotice is not None:
0050             for line in self._copyrightNotice.split('\n'):
0051                 comment = self._sipsym.Comment(sipScope)
0052                 comment.setValue(line+'\n')
0053         
0054         self._convertScope(cppScope,sipScope)
0055         return sipScope
0056         
0057     def _convertScope(self,cppScope,destScope):
0058         for item in cppScope:
0059             #print("==================================")
0060             #print(item.format())
0061             if isinstance(item,cppsymboldata.SymbolData.CppClass):
0062                 self._convertClass(item,destScope)
0063                 
0064             elif isinstance(item,cppsymboldata.SymbolData.Function):
0065                 self._convertFunction(item,destScope)
0066                 
0067             elif isinstance(item,cppsymboldata.SymbolData.Variable):
0068                 self._convertVariable(item,destScope)
0069 
0070             elif isinstance(item,cppsymboldata.SymbolData.Namespace):
0071                 self._convertNamespace(item,destScope)
0072 
0073             elif isinstance(item,cppsymboldata.SymbolData.Enum):
0074                 self._convertEnum(item,destScope)
0075 
0076             elif isinstance(item,cppsymboldata.SymbolData.EnumTypedef):
0077                 self._convertEnumTypedef(item,destScope)
0078                 
0079             elif isinstance(item,cppsymboldata.SymbolData.Typedef):
0080                 self._convertTypedef(item,destScope)
0081                 
0082     def _convertFunction(self,cppFunction,destScope):
0083         isCtor = isinstance(cppFunction,cppsymboldata.SymbolData.Constructor)
0084         isDtor = isinstance(cppFunction,cppsymboldata.SymbolData.Destructor)
0085 
0086         if len(cppFunction.template())!=0:
0087             # Ignore templated functions.
0088             return
0089         
0090         # Private funcions/methods are not copied.
0091         if cppFunction.access()==cppsymboldata.SymbolData.ACCESS_PRIVATE and not isCtor and not isDtor:
0092             return
0093             
0094         # Ignore these operators.
0095         if cppFunction.name() in ['operator =', 'operator ++', 'operator --']:
0096             return
0097             
0098         if isCtor:
0099             sipFunction = self._sipsym.Constructor(destScope,cppFunction.name(),filename=cppFunction._filename,lineno=cppFunction._lineno)
0100         elif isDtor:
0101             sipFunction = self._sipsym.Destructor(destScope,cppFunction.name(),filename=cppFunction._filename,lineno=cppFunction._lineno)
0102         else:
0103             sipFunction = self._sipsym.Function(destScope,cppFunction.name(),filename=cppFunction._filename,lineno=cppFunction._lineno)
0104             sipFunction.setReturn(self._convertArgument(cppFunction.return_()))
0105         sipFunction.setAccess(cppFunction.access())
0106         sipFunction.setArguments( [self._convertArgument(x) for x in cppFunction.arguments() if x.argumentType()!="void"] )
0107         if cppFunction.storage()=='static':
0108             sipFunction.setStorage('static')
0109             
0110         for qual in cppFunction.qualifier():
0111             sipFunction.addQualifier(qual)
0112                    
0113     def _convertClass(self,cppClass,parentScope):
0114         if not self._isClassExported(cppClass) or cppClass.opaque():
0115             return None
0116     
0117         sipClass = self._sipsym.SipClass(parentScope, cppClass.name(),filename=cppClass._filename,lineno=cppClass._lineno)
0118         sipClass.setBases( [base for base in cppClass.bases() if base not in self._ignoredBaseClasses] )
0119 
0120         if self._cppScope.headerFilename() is not None:
0121             includeDirective = self._sipsym.SipDirective(sipClass,"%TypeHeaderCode")
0122             includeDirective.setBody(
0123 """%%TypeHeaderCode
0124 #include <%s>
0125 %%End
0126 """ % (self._cppScope.headerFilename(),))
0127         
0128         self._convertScope(cppClass,sipClass)
0129     
0130     def _isClassExported(self,cppClass):
0131         if self._exportMacros is not None:
0132             if self._exportMacros.isdisjoint( set( (x.name() for x in cppClass.macros()))):
0133                 return False
0134         return True
0135 
0136     def _convertArgument(self,cppArgument):
0137         argumentType = cppArgument.argumentType()
0138         argumentType = {
0139             'short int': 'short',
0140             'unsigned short int': 'unsigned short',
0141             'long unsigned int': 'unsigned long'
0142             }.get(argumentType,argumentType)
0143         
0144         defaultValue = cppArgument.defaultValue()
0145         if defaultValue=='true':
0146             defaultValue = '1'
0147         if defaultValue=='false':
0148             defaultValue = '0'
0149             
0150         return self._sipsym.Argument(argumentType, cppArgument.name(), defaultValue)
0151 
0152     def _convertVariable(self,cppVariable,parentScope):
0153         if cppVariable.access()!=cppsymboldata.SymbolData.ACCESS_PUBLIC:
0154             return
0155             
0156         sipVariable = self._sipsym.Variable(parentScope, cppVariable.name())
0157         sipVariable.setArgument(self._convertArgument(cppVariable.argument()))
0158         if cppVariable.storage()=='static':
0159             sipVariable.setStorage('static')
0160         sipVariable.setAccess(cppVariable.access())
0161         return sipVariable
0162     
0163     def _convertNamespace(self,cppNamespace,parentScope):
0164         sipNamespace = self._sipsym.Namespace(parentScope, cppNamespace.name())
0165         self._convertScope(cppNamespace,sipNamespace)
0166         if len(sipNamespace)==0:
0167             del parentScope[parentScope.index(sipNamespace)]
0168     
0169     def _convertEnum(self,cppEnum,parentScope):
0170         if cppEnum.access()==cppsymboldata.SymbolData.ACCESS_PRIVATE:
0171             return
0172     
0173         sipEnum = self._sipsym.Enum(parentScope, cppEnum.name())
0174         sipEnum.setAccess(cppEnum.access())
0175         for item in cppEnum:
0176             sipEnum.append(self._sipsym.Enumerator(item.name(),item.value()))
0177 
0178     def _convertTypedef(self,cppTypedef,parentScope):
0179         sipTypedef = self._sipsym.Typedef(parentScope, cppTypedef.name(),
0180             filename=cppTypedef._filename,lineno=cppTypedef._lineno)
0181         sipTypedef.setAccess(cppTypedef.access())
0182         sipTypedef.setArgumentType(cppTypedef.argumentType())
0183         return sipTypedef
0184         
0185     def _convertEnumTypedef(self,cppEnumTypedef,parentScope):
0186         sipEnum = self._sipsym.Enum(parentScope, cppEnumTypedef.name())
0187         sipEnum.setAccess(cppEnumTypedef.enum().access())
0188         for item in cppEnumTypedef.enum():
0189             sipEnum.append(item)
0190         return sipEnum
0191 
0192 ###########################################################################
0193 def ExpandClassNames(sipsym,scope):
0194     for item in scope:
0195         if isinstance(item,sipsym.SipClass):
0196             _ExpandClassNamesForClass(sipsym,item)
0197             
0198         elif isinstance(item,sipsym.Namespace):
0199             ExpandClassNames(sipsym,item)
0200             
0201         elif isinstance(item,sipsym.Constructor):
0202             _ExpandClassNamesForArguments(sipsym,scope,item)
0203             
0204         elif isinstance(item,sipsym.Function):
0205             _ExpandClassNamesForFunction(sipsym,scope,item)
0206             
0207         elif isinstance(item,sipsym.Variable):
0208             _ExpandClassNamesForVariable(sipsym,scope,item)
0209 
0210 def _ExpandClassNamesForClass(sipsym,sipClass):
0211     # Use FQNs when talking about base classes, otherwise SIP fails.
0212     fqnBaseList = []
0213     for base in sipClass.bases():
0214         try:
0215             baseObject = sipsym.lookupType(base,sipClass.parentScope())
0216             fqnBaseList.append(baseObject.fqName())
0217         except KeyError:
0218             fqnBaseList.append(base)
0219     sipClass.setBases(fqnBaseList)
0220     
0221     ExpandClassNames(sipsym,sipClass)
0222 
0223 def _ExpandClassNamesForFunction(sipsym,context,sipFunction):
0224     sipFunction.setReturn(_ExpandArgument(sipsym,context,sipFunction.return_()))
0225     
0226     _ExpandClassNamesForArguments(sipsym,context,sipFunction)
0227     
0228 def _ExpandClassNamesForVariable(sipsym,context,sipVariable):
0229     sipVariable.setArgument(_ExpandArgument(sipsym,context,sipVariable.argument()))
0230     
0231 def _ExpandClassNamesForArguments(sipsym,context,sipFunction):
0232     sipFunction.setArguments( [_ExpandArgument(sipsym,context,argument) for argument in sipFunction.arguments()] )
0233 
0234 _PrimitiveTypes = ["char","signed char","unsigned char","wchar_t","int","unsigned",
0235     "unsigned int","short","unsigned short","long","unsigned long","long long",
0236     "unsigned long long","float","double","bool","void"]
0237     
0238 _templateArgRegex = re.compile(r'^([^<]*)<(.*)>$')
0239 
0240 def _ExpandArgument(sipsym,context,argument):
0241     className = _ExpandArgumentType(sipsym,context,argument.argumentType())
0242 
0243     defaultValue = argument.defaultValue()
0244     if defaultValue is not None:
0245         if defaultValue.endswith("()"):
0246             try:
0247                 valueObject = sipsym.lookupType(defaultValue[:-2],context)
0248                 defaultValue = valueObject.fqName() + "()"
0249             except KeyError:
0250                 pass
0251         else:
0252             enum = sipsym.lookupEnum(defaultValue,context)
0253             if enum is not None and enum.name() is not None:
0254                 defaultValue =  enum.fqName() + "::" + defaultValue
0255             else:
0256                 try:
0257                     valueObject = sipsym.lookupType(defaultValue,context)
0258                     defaultValue = valueObject.fqName()
0259                 except KeyError:
0260                     pass
0261             
0262     newArgument = sipsym.Argument(className, argument.name(), defaultValue,
0263                     argument.template(), argument.defaultTypes())
0264     newArgument.setAnnotations(argument.annotations())
0265     return newArgument
0266     
0267 def _ExpandArgumentType(sipsym,context,origClassName):
0268     className = origClassName
0269 
0270     suffix = ""
0271     if className.endswith('*'):
0272         className = className[:-1]
0273         suffix = '*'
0274     if className.endswith('&'):
0275         className = className[:-1]
0276         suffix = '&'
0277         
0278     prefix = ""
0279     if className.startswith("const "):
0280         className = className[6:]
0281         prefix = "const "
0282     
0283     match = re.match(_templateArgRegex,className)
0284     if match is not None:
0285         # Got a templated thingy.
0286         firstPart = _ExpandArgumentType(sipsym,context,match.group(1))
0287         containedPart = _ExpandArgumentType(sipsym,context,match.group(2))
0288         return "%s%s<%s>%s" % (prefix,firstPart,containedPart,suffix)
0289         
0290     else:
0291         if className not in _PrimitiveTypes:
0292             #    return argument
0293             try:
0294                 classObject = sipsym.lookupType(className,context)
0295                 if classObject.fqName()==className:
0296                     return origClassName # Nothing to do.
0297                 className = classObject.fqName()
0298             except KeyError:
0299                 print("Warning: %s Unrecognized type '%s' was found when expanding argument type names." % (context.sourceLocation(),className))
0300                 return origClassName
0301 
0302         return prefix + className + suffix
0303 
0304 ###########################################################################
0305 class MethodAnnotationRule(object):
0306     @sealed
0307     def __init__(self,methodTypeMatch,parameterTypeMatch,parameterNameMatch,annotations):
0308         self._methodTypeMatch = methodTypeMatch
0309         
0310         if parameterTypeMatch=='*':
0311             self._parameterTypeMatch = None
0312         else:
0313             self._parameterTypeMatch = set([parameterTypeMatch]) if isinstance(parameterTypeMatch,str) else set(parameterTypeMatch)
0314         
0315         self._parameterNameMatch = set([parameterNameMatch]) if isinstance(parameterNameMatch,str) else set(parameterNameMatch)
0316         self._annotations = [annotations] if isinstance(annotations,str) else annotations
0317        
0318     def apply(self,symbolData,sipFunction,sipClass):
0319         matchCtor = self._methodTypeMatch in ('ctor','all')
0320         matchDtor = self._methodTypeMatch in ('dtor','all')
0321         matchFunc = self._methodTypeMatch in ('function','all')
0322 
0323         isCtor = isinstance(sipFunction,symbolData.Constructor)
0324         isDtor = isinstance(sipFunction,symbolData.Destructor)
0325         isFunc = isinstance(sipFunction,symbolData.Function)
0326         if (matchCtor and isCtor) or (matchDtor and isDtor) or \
0327                 (matchFunc and isFunc and not isCtor and not isDtor):
0328             for argument in sipFunction.arguments():
0329                 if self._parameterTypeMatch is None or argument.argumentType() in self._parameterTypeMatch:
0330                     if argument.name() in self._parameterNameMatch:
0331                         newAnnotations = list(argument.annotations())
0332                         for anno in self._annotations:
0333                             if anno not in newAnnotations:
0334                                 newAnnotations.append(anno)
0335                                 argument.setAnnotations(newAnnotations)
0336             
0337     def __str__(self):
0338         return "ClassMatch: " + repr(self._classMatch) + " MethodTypeMatch: " + methodTypeMatch + \
0339             " MethodNameMatch: " + repr(methodNameMatch) + " Annotations: " + repr(self._annotations)
0340 
0341 class PySlotRule(object):
0342     @sealed
0343     def __init__(self,className=None,namespaceName=None,arg1Name=None,arg2Name=None):
0344         self._className = className
0345         self._namespaceName = namespaceName
0346         self._arg1Name = arg1Name
0347         self._arg2Name = arg2Name
0348 
0349     def apply(self,symbolData,sipFunction,sipClassOrNamespace):
0350         if self._className is not None and not isinstance(sipClassOrNamespace,symbolData.SipClass):
0351             return
0352         if self._namespaceName is not None and not isinstance(sipClassOrNamespace,symbolData.Namespace):
0353             return
0354             
0355         if isinstance(sipFunction,symbolData.Function):
0356             args = sipFunction.arguments()
0357             if len(args) >= 2:
0358                 for i in range(len(args)-1):
0359                     arg1 = args[i]
0360                     arg2 = args[i+1]
0361                     if arg1.name()==self._arg1Name and arg2.name()==self._arg2Name:
0362                         args = list(args)
0363                         newArg1 = symbolData.Argument("SIP_RXOBJ_CON", None, None, None, None)
0364                         args[i] = newArg1
0365                         
0366                         newArg2 = symbolData.Argument("SIP_SLOT_CON ()", None, None, None, None)
0367                         args[i+1] = newArg2
0368                         sipFunction.setArguments(args)
0369                         break
0370 
0371     def __str__(self):
0372         return "PySlotRule ClassName: " + self._className + " Arg1: " + self._arg1 + " Arg2: " + self._arg2
0373 
0374 class SipAnnotator(object):
0375     @sealed
0376     def __init__(self):
0377         self._methodAnnotationRules = None
0378         self._sipsym = sipsymboldata.SymbolData()
0379 
0380     def setMethodAnnotationRules(self,rules):
0381         self._methodAnnotationRules = rules
0382         
0383     def applyRules(self,sipTree):
0384         for item in sipTree:
0385             if isinstance(item,self._sipsym.SipClass) or isinstance(item,self._sipsym.Namespace):
0386                 self._applyRulesToClassOrNamespace(item)
0387 
0388     def _applyRulesToClassOrNamespace(self,sipClass):
0389         for item in sipClass:
0390             if isinstance(item,self._sipsym.Function):
0391                 self._applyRulesToFunction(self._methodAnnotationRules,item,sipClass)
0392             elif isinstance(item,self._sipsym.Constructor):
0393                 self._applyRulesToFunction(self._methodAnnotationRules,item,sipClass)
0394             elif isinstance(item,self._sipsym.Destructor):
0395                 self._applyRulesToFunction(self._methodAnnotationRules,item,sipClass)
0396                 
0397         self.applyRules(sipClass)
0398         
0399     def _applyRulesToFunction(self,rules,sipFunction,sipClass=None):
0400         for rule in rules:
0401             rule.apply(self._sipsym, sipFunction, sipClass)
0402 
0403 ###########################################################################
0404 
0405 def UpdateConvertToSubClassCodeDirectives(symbolData,scopeList,ignoreClassList=[]):
0406     """Insert of update CTSCC directives
0407     
0408     Insert or update the Sip ConvertToSubClassCode directives in the given list of scopes.
0409     
0410     Keyword arguments:
0411     symbolData -- 
0412     scopeList -- List of scopes containing the classes which need to be updated.
0413     ignoreClassList -- List of class names which should be ignored.
0414     """
0415     _UpdateConvertToSubClassCodeDirectives(symbolData,scopeList,ignoreClassList).run()
0416 
0417 class _UpdateConvertToSubClassCodeDirectives(object):
0418     def __init__(self,symbolData,scopeList,ignoreClassList):
0419         self._symbolData = symbolData
0420         self._scopeList = scopeList
0421         self._ignoreClassList = ignoreClassList
0422         self._subclassList = None
0423         self.INDENT = "    "
0424         
0425     def run(self):
0426         if len(self._scopeList)==0:
0427             return
0428         
0429         # Collect all of the classes that we need to consider.
0430         self._classList = self._findClasses(self._scopeList)
0431         
0432         # Filter out uninteresting classes from the _classList.
0433         # FIXME compare the fqn instead of name().
0434         self._subclassList = [class_ for class_ in self._classList
0435             if len(class_.bases())!=0 and class_.name() not in self._ignoreClassList]
0436         
0437         # Build a mapping from class objects to their subclasses.
0438         classToSubclassMapping = {}
0439         topSuperClasses = set()
0440         for class_ in self._subclassList:
0441             topSuperClass = self._updateSubclassBaseMapping(classToSubclassMapping,class_)
0442             if topSuperClass is None:
0443                 topSuperClass = class_
0444             topSuperClasses.add(topSuperClass)
0445         
0446         for class_ in topSuperClasses:
0447             self._insertCTSCC(classToSubclassMapping,class_)
0448             
0449     def _updateSubclassBaseMapping(self,mapping,class_):
0450         lastBase = None
0451         
0452         for baseName in class_.bases():
0453             try:
0454                 base = self._symbolData.lookupType(baseName,class_.parentScope())
0455                 if isinstance(base, self._symbolData.Typedef):
0456                     print("Warning: %s Skipping typedef base '%s' while updating CTSCC." % (class_.sourceLocation(),baseName))
0457                     continue
0458                 
0459                 if lastBase is None:
0460                     lastBase = base
0461                 subClassList = mapping.setdefault(base,set())
0462                 subClassList.add(class_)
0463                 top = self._updateSubclassBaseMapping(mapping,base)
0464                 if top is not None:
0465                     lastBase = top
0466             except KeyError:
0467                 print("Warning: %s Unrecognized type '%s' was found when updating CTSCC." % (class_.sourceLocation(),baseName))
0468         return lastBase
0469 
0470     def _generateCTSCC(self,classToSubclassMapping,class_):
0471         return "%%ConvertToSubClassCode\n    // CTSCC for subclasses of '%s'\n    sipType = NULL;\n\n%s%%End" % (class_.name(),self._generateCTSCCPart(classToSubclassMapping,class_,self.INDENT))
0472             
0473     def _generateCTSCCPart(self,classToSubclassMapping,class_,indent="",joiner=""):
0474         accu = []
0475         
0476         subclasses = list(classToSubclassMapping.get(class_,set()))
0477         def namekey(class_): return class_.fqName()
0478         subclasses.sort(key=namekey)
0479         
0480         if class_ in self._subclassList:
0481             accu.append(indent)
0482             accu.append(joiner)
0483             joiner = ""
0484             accu.append("if (dynamic_cast<")
0485             accu.append(class_.fqName())
0486             accu.append("*>(sipCpp))\n")
0487             if len(subclasses)!=0:
0488                 accu.append(indent);
0489                 accu.append(self.INDENT)
0490                 accu.append("{\n")
0491                 
0492             if class_ in self._subclassList:
0493                 accu.append(indent)
0494                 accu.append(self.INDENT)
0495                 accu.append("sipType = sipType_")
0496                 accu.append(class_.fqName().replace("::","_"))
0497                 accu.append(";\n")
0498             extraIndent = self.INDENT                
0499         else:
0500             extraIndent = ""
0501         
0502         for subclass in subclasses:
0503             accu.append(self._generateCTSCCPart(classToSubclassMapping,subclass,indent+extraIndent,joiner))
0504             joiner = "else "
0505         
0506         if class_ in self._subclassList and len(subclasses)!=0:
0507             accu.append(indent)
0508             accu.append(self.INDENT)
0509             accu.append("}\n")
0510 
0511         return "".join(accu)
0512         
0513     def _insertCTSCC(self,classToSubclassMapping,class_):
0514         subclasses = self._findAllSubclasses(classToSubclassMapping,class_)
0515         subclasses.append(class_)
0516         
0517         # Find an existing %ConvertToSubClassCode block.
0518         directiveClass = None
0519         for subclass in subclasses:
0520             directive = self._findDirective(subclass,"%ConvertToSubClassCode")
0521             if directive is not None:
0522                 directiveClass = subclass
0523                 break
0524         else:
0525             # Create a new %ConvertToSubClassCode block.
0526             # Try the root first.
0527             if class_ in self._classList:
0528                 directiveClass = class_
0529             else:
0530                 # Choose a subclass, use the first one by name.
0531                 def namekey(class_): return class_.name()
0532                 subclasses.sort(key=namekey)
0533                 directiveClass = subclasses[0]
0534             directive = self._symbolData.SipDirective(directiveClass,"%ConvertToSubClassCode")
0535         directive.setBody(self._generateCTSCC(classToSubclassMapping,class_))
0536         
0537         # Update the %TypeHeaderCode #include list.
0538         headerCode = self._findDirective(directiveClass.topScope(),"%ModuleHeaderCode")
0539         if headerCode is None:
0540             headerCode = self._symbolData.SipDirective(directiveClass.topScope(),"%ModuleHeaderCode")
0541         
0542         fileScopes = list(set( (subclass.topScope().headerFilename() for subclass in subclasses) ))
0543         def key(x): return os.path.basename(x)
0544         fileScopes.sort(key=key)
0545         
0546         headerCode.setBody("%ModuleHeaderCode\n//ctscc\n" +
0547             "".join(["#include <"+str(x)+">\n" for x in fileScopes]) +
0548             "%End")
0549         
0550     def _findDirective(self,class_,directiveName):
0551         for item in class_:
0552             if isinstance(item,self._symbolData.SipDirective):
0553                 if item.name()==directiveName:
0554                     return item
0555         return None
0556 
0557     def _findAllSubclasses(self,classToSubclassMapping,class_):
0558         result = []
0559         if class_ in self._subclassList:
0560             result.append(class_)
0561         for subclass in classToSubclassMapping.get(class_,[]):
0562             result.extend(self._findAllSubclasses(classToSubclassMapping,subclass))
0563         return result
0564         
0565     def _findClasses(self,scope):
0566         classList = []
0567         for item in scope:
0568             if isinstance(item,self._symbolData.SipClass):
0569                 classList.append(item)
0570             if isinstance(item,self._symbolData.Entity):
0571                 classList.extend(self._findClasses(item))
0572         return classList
0573     
0574 ###########################################################################
0575 def SanityCheckSip(symbolData,scopeList):
0576     _SanityCheckSip(symbolData,scopeList).run()
0577     
0578 class _SanityCheckSip(object):
0579     def __init__(self,symbolData,scopeList):
0580         self._symbolData = symbolData
0581         self._scopeList = scopeList
0582         
0583     def run(self):
0584         for scope in self._scopeList:
0585             self._checkScope(scope)
0586             
0587     def _checkScope(self,scope):
0588         for item in scope:
0589             if isinstance(item,self._symbolData.SipClass):
0590                 self._checkClass(item)
0591             elif isinstance(item,self._symbolData.Function):
0592                 self._checkFunction(item)
0593                 
0594     def _checkClass(self,sipClass):
0595         # Check the base classes.
0596         for baseName in sipClass.bases():
0597             try:
0598                 self._symbolData.lookupType(baseName,sipClass.parentScope())
0599             except KeyError:
0600                 print("Error: %s Unknown base class '%s'" % (sipClass.sourceLocation(),baseName))
0601     
0602         for item in sipClass:
0603             if isinstance(item,self._symbolData.Constructor) or isinstance(item,self._symbolData.Destructor) or \
0604             isinstance(item,self._symbolData.Function):
0605                 self._checkFunction(item)
0606 
0607     def _checkFunction(self,function):
0608         if function.ignore():
0609             return
0610 
0611         for arg in function.arguments():
0612             if arg.argumentType().endswith('&') and 'In' not in arg.annotations() and 'Out' not in arg.annotations():
0613                 print("Error: %s Parameter '%s' requires a /In/ or /Out/ annotation." % (function.sourceLocation(),arg.name()))