FFIExternalObject.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. from new import instance
  2. import FFIConstants
  3. WrapperClassMap = {}
  4. DowncastMap = {}
  5. # For testing, you can turn verbose and debug on
  6. # FFIConstants.notify.setInfo(1)
  7. # FFIConstants.notify.setDebug(1)
  8. # Uncomment the notify statements if you need to debug,
  9. # otherwise leave them commented out to prevent runtime
  10. # overhead of calling them
  11. # Register a python class in the type map if it is a typed object
  12. # The type map is used for upcasting and downcasting through
  13. # the panda inheritance chain
  14. def registerInTypeMap(pythonClass):
  15. from pandac import TypedObject
  16. if issubclass(pythonClass, TypedObject.TypedObject):
  17. typeIndex = pythonClass.getClassType().getIndex()
  18. WrapperClassMap[typeIndex] = pythonClass
  19. def funcToMethod(func, clas, method_name=None):
  20. """Adds func to class so it is an accessible method; use method_name to specify the name to be used for calling the method.
  21. The new method is accessible to any instance immediately."""
  22. func.im_class=clas
  23. func.im_func=func
  24. func.im_self=None
  25. if not method_name:
  26. clas.__dict__[method_name]=func
  27. else:
  28. clas.__dict__[func.__name__]=func
  29. def FFIInstance(classdef, this = 0, userManagesMemory = 0):
  30. answer = instance(classdef)
  31. answer.this = this
  32. answer.userManagesMemory = userManagesMemory
  33. return answer
  34. class FFIExternalObject:
  35. def __init__(self, *_args):
  36. # By default, we do not manage our own memory
  37. self.userManagesMemory = 0
  38. # Start with a null this pointer
  39. self.this = 0
  40. def destructor(self):
  41. # Base destructor in case you do not define one
  42. pass
  43. def getLineage(self, thisClass, targetBaseClass):
  44. # Recursively determine the path in the heirarchy tree from thisClass
  45. # to the targetBaseClass
  46. return self.getLineageInternal(thisClass, targetBaseClass, [thisClass])
  47. def getLineageInternal(self, thisClass, targetBaseClass, chain):
  48. # Recursively determine the path in the heirarchy tree from thisClass
  49. # to the targetBaseClass
  50. #FFIConstants.notify.debug('getLineageInternal: checking %s to %s'
  51. # % (thisClass.__name__, targetBaseClass.__name__))
  52. if (targetBaseClass in thisClass.__bases__):
  53. # Found a link
  54. return chain + [targetBaseClass]
  55. elif (len(thisClass.__bases__) == 0):
  56. # No possible links
  57. return 0
  58. else:
  59. # recurse
  60. for base in thisClass.__bases__:
  61. res = self.getLineageInternal(base, targetBaseClass, chain+[base])
  62. if res:
  63. # FFIConstants.notify.debug('getLineageInternal: found path: ' + repr(res))
  64. return res
  65. # Not found anywhere
  66. return 0
  67. def getDowncastFunctions(self, thisClass, baseClass):
  68. #FFIConstants.notify.debug(
  69. # 'getDowncastFunctions: Looking for downcast function from %s to %s'
  70. # % (baseClass.__name__, thisClass.__name__))
  71. lineage = self.getLineage(thisClass, baseClass)
  72. # Start with an empty list of downcast functions
  73. downcastFunctionList = []
  74. # If it could not find the baseClass anywhere in the lineage,
  75. # return empty
  76. if not lineage:
  77. return []
  78. # Walk along the lineage looking for downcast functions from
  79. # class to class+1. Start at the top and work downwards.
  80. top = len(lineage) - 1
  81. for i in range(top):
  82. toClass = lineage[top - i - 1]
  83. fromClass = lineage[top - i]
  84. downcastFuncName = ('downcastTo' + toClass.__name__
  85. + 'From' + fromClass.__name__)
  86. # Look over this classes global modules dictionaries
  87. # for the downcast function name
  88. for globmod in toClass.__CModuleDowncasts__:
  89. func = globmod.__dict__.get(downcastFuncName)
  90. if func:
  91. #FFIConstants.notify.debug(
  92. # 'getDowncastFunctions: Found downcast function %s in %s'
  93. # % (downcastFuncName, globmod.__name__))
  94. downcastFunctionList.append(func)
  95. return downcastFunctionList
  96. def lookUpNewType(self, typeHandle, rootType):
  97. # We tried to downcast to an unknown type. Try to figure out
  98. # the lowest type we *do* know, so we can downcast to that
  99. # type instead.
  100. if typeHandle.getNumParentClasses() == 0:
  101. # This type has no parents! That shouldn't happen.
  102. FFIConstants.notify.warning("Unknown class type: %s has no parents!" % (typeHandle.getName()))
  103. return None
  104. parentType = typeHandle.getParentTowards(rootType, self)
  105. parentIndex = parentType.getIndex()
  106. parentWrapperClass = WrapperClassMap.get(parentIndex)
  107. if parentWrapperClass == None:
  108. parentWrapperClass = self.lookUpNewType(parentType, rootType)
  109. if parentWrapperClass != None:
  110. # If the parent class is known, then record that this
  111. # class is a derivation of that parent class.
  112. WrapperClassMap[typeHandle.getIndex()] = parentWrapperClass
  113. return parentWrapperClass
  114. def setPointer(self):
  115. # See what type it really is and downcast to that type (if necessary)
  116. # Look up the TypeHandle in the dict. get() returns None if it is not there
  117. index = self.getTypeIndex()
  118. exactWrapperClass = WrapperClassMap.get(index)
  119. if exactWrapperClass == None:
  120. # This is an unknown class type. Perhaps it derives from
  121. # a class type we know.
  122. exactWrapperClass = self.lookUpNewType(self.getType(), self.getClassType())
  123. # We do not need to downcast if we already have the same class
  124. if (exactWrapperClass and (exactWrapperClass != self.__class__)):
  125. # Create a new wrapper class instance
  126. #exactObject = exactWrapperClass(None)
  127. exactObject = FFIInstance(exactWrapperClass)
  128. # Get the downcast pointer that has had all the downcast
  129. # funcs called
  130. downcastObject = self.downcast(exactWrapperClass)
  131. exactObject.this = downcastObject.this
  132. exactObject.userManagesMemory = downcastObject.userManagesMemory
  133. # Make sure the original downcast object does not get
  134. # garbage collected so that the exactObject will not get
  135. # gc'd thereby transferring ownership of the object to
  136. # this new exactObject
  137. downcastObject.userManagesMemory = 0
  138. return exactObject
  139. else:
  140. return self
  141. def downcast(self, toClass):
  142. fromClass = self.__class__
  143. #FFIConstants.notify.debug('downcast: downcasting from %s to %s' % \
  144. # (fromClass.__name__, toClass.__name__))
  145. # Check the cache to see if we have looked this up before
  146. downcastChain = DowncastMap.get((fromClass, toClass))
  147. if downcastChain == None:
  148. downcastChain = self.getDowncastFunctions(toClass, fromClass)
  149. #FFIConstants.notify.debug('downcast: computed downcast chain: ' + repr(downcastChain))
  150. # Store it for next time
  151. DowncastMap[(fromClass, toClass)] = downcastChain
  152. newObject = self
  153. for downcastFunc in downcastChain:
  154. #FFIConstants.notify.debug('downcast: downcasting %s using %s' % \
  155. # (newObject.__class__.__name__, downcastFunc))
  156. newObject = downcastFunc(newObject)
  157. return newObject
  158. def compareTo(self, other):
  159. # By default, we compare the C++ pointers
  160. # Some classes will override the compareTo operator with their own
  161. # logic in C++ (like vectors and matrices for instance)
  162. try:
  163. if self.this < other.this:
  164. return -1
  165. if self.this > other.this:
  166. return 1
  167. else:
  168. return 0
  169. except:
  170. return 1
  171. def __cmp__(self, other):
  172. # Only use the C++ compareTo if they are the same class
  173. if isinstance(other, self.__class__):
  174. return self.compareTo(other)
  175. # Otherwise, they must not be the same
  176. # Just do a basic python id compare
  177. else:
  178. return cmp(id(self), id(other))
  179. def __repr__(self):
  180. # Lots of Panda classes have an output function defined that takes an Ostream
  181. # We create a LineStream for the output function to write to, then we extract
  182. # the string out of it and return it as our str
  183. try:
  184. from pandac import LineStream
  185. lineStream = LineStream.LineStream()
  186. self.output(lineStream)
  187. baseRepr = lineStream.getLine()
  188. except AssertionError, e:
  189. raise AssertionError, e
  190. except:
  191. baseRepr = ('[' + self.__class__.__name__ + ' at: ' + repr(self.this) + ']')
  192. # In any case, return the baseRepr
  193. return baseRepr
  194. def __str__(self):
  195. # This is a more complete version of printing which shows the object type
  196. # and pointer, plus the output from write() or output() whichever is defined
  197. # Print this info for all objects
  198. baseRepr = ('[' + self.__class__.__name__ + ' at: ' + repr(self.this) + ']')
  199. # Lots of Panda classes have an write or output function defined that takes an Ostream
  200. # We create a LineStream for the write or output function to write to, then we extract
  201. # the string out of it and return it as our repr
  202. from pandac import LineStream
  203. lineStream = LineStream.LineStream()
  204. try:
  205. # First try the write function, that is the better one
  206. self.write(lineStream)
  207. while lineStream.isTextAvailable():
  208. baseRepr = baseRepr + '\n' + lineStream.getLine()
  209. except AssertionError, e:
  210. raise AssertionError, e
  211. except:
  212. try:
  213. # Sometimes write insists on a seconds parameter.
  214. self.write(lineStream, 0)
  215. while lineStream.isTextAvailable():
  216. baseRepr = baseRepr + '\n' + lineStream.getLine()
  217. except AssertionError, e:
  218. raise AssertionError, e
  219. except:
  220. try:
  221. # Ok, no write function, lets try output then
  222. self.output(lineStream)
  223. while lineStream.isTextAvailable():
  224. baseRepr = baseRepr + '\n' + lineStream.getLine()
  225. except AssertionError, e:
  226. raise AssertionError, e
  227. except:
  228. pass
  229. # In any case, return the baseRepr
  230. return baseRepr
  231. def __hash__(self):
  232. return self.this