mirror of
https://github.com/Sneed-Group/Poodletooth-iLand
synced 2024-12-25 12:42:41 -06:00
268 lines
11 KiB
Python
268 lines
11 KiB
Python
from new import instance
|
|
import FFIConstants
|
|
|
|
WrapperClassMap = {}
|
|
|
|
DowncastMap = {}
|
|
|
|
# For testing, you can turn verbose and debug on
|
|
# FFIConstants.notify.setInfo(1)
|
|
# FFIConstants.notify.setDebug(1)
|
|
|
|
# Uncomment the notify statements if you need to debug,
|
|
# otherwise leave them commented out to prevent runtime
|
|
# overhead of calling them
|
|
|
|
|
|
|
|
# Register a python class in the type map if it is a typed object
|
|
# The type map is used for upcasting and downcasting through
|
|
# the panda inheritance chain
|
|
def registerInTypeMap(pythonClass):
|
|
from pandac import TypedObject
|
|
if issubclass(pythonClass, TypedObject.TypedObject):
|
|
typeIndex = pythonClass.getClassType().getIndex()
|
|
WrapperClassMap[typeIndex] = pythonClass
|
|
|
|
|
|
def funcToMethod(func, clas, method_name=None):
|
|
"""Adds func to class so it is an accessible method; use method_name to specify the name to be used for calling the method.
|
|
The new method is accessible to any instance immediately."""
|
|
func.im_class=clas
|
|
func.im_func=func
|
|
func.im_self=None
|
|
if not method_name:
|
|
clas.__dict__[method_name]=func
|
|
else:
|
|
clas.__dict__[func.__name__]=func
|
|
|
|
|
|
def FFIInstance(classdef, this = 0, userManagesMemory = 0):
|
|
answer = instance(classdef)
|
|
answer.this = this
|
|
answer.userManagesMemory = userManagesMemory
|
|
return answer
|
|
|
|
class FFIExternalObject:
|
|
def __init__(self, *_args):
|
|
# By default, we do not manage our own memory
|
|
self.userManagesMemory = 0
|
|
# Start with a null this pointer
|
|
self.this = 0
|
|
|
|
def destructor(self):
|
|
# Base destructor in case you do not define one
|
|
pass
|
|
|
|
def getLineage(self, thisClass, targetBaseClass):
|
|
# Recursively determine the path in the heirarchy tree from thisClass
|
|
# to the targetBaseClass
|
|
return self.getLineageInternal(thisClass, targetBaseClass, [thisClass])
|
|
|
|
def getLineageInternal(self, thisClass, targetBaseClass, chain):
|
|
# Recursively determine the path in the heirarchy tree from thisClass
|
|
# to the targetBaseClass
|
|
#FFIConstants.notify.debug('getLineageInternal: checking %s to %s'
|
|
# % (thisClass.__name__, targetBaseClass.__name__))
|
|
if (targetBaseClass in thisClass.__bases__):
|
|
# Found a link
|
|
return chain + [targetBaseClass]
|
|
elif (len(thisClass.__bases__) == 0):
|
|
# No possible links
|
|
return 0
|
|
else:
|
|
# recurse
|
|
for base in thisClass.__bases__:
|
|
res = self.getLineageInternal(base, targetBaseClass, chain+[base])
|
|
if res:
|
|
# FFIConstants.notify.debug('getLineageInternal: found path: ' + repr(res))
|
|
return res
|
|
# Not found anywhere
|
|
return 0
|
|
|
|
def getDowncastFunctions(self, thisClass, baseClass):
|
|
#FFIConstants.notify.debug(
|
|
# 'getDowncastFunctions: Looking for downcast function from %s to %s'
|
|
# % (baseClass.__name__, thisClass.__name__))
|
|
lineage = self.getLineage(thisClass, baseClass)
|
|
# Start with an empty list of downcast functions
|
|
downcastFunctionList = []
|
|
|
|
# If it could not find the baseClass anywhere in the lineage,
|
|
# return empty
|
|
if not lineage:
|
|
return []
|
|
|
|
# Walk along the lineage looking for downcast functions from
|
|
# class to class+1. Start at the top and work downwards.
|
|
top = len(lineage) - 1
|
|
for i in range(top):
|
|
toClass = lineage[top - i - 1]
|
|
fromClass = lineage[top - i]
|
|
downcastFuncName = ('downcastTo' + toClass.__name__
|
|
+ 'From' + fromClass.__name__)
|
|
# Look over this classes global modules dictionaries
|
|
# for the downcast function name
|
|
for globmod in toClass.__CModuleDowncasts__:
|
|
func = globmod.__dict__.get(downcastFuncName)
|
|
if func:
|
|
#FFIConstants.notify.debug(
|
|
# 'getDowncastFunctions: Found downcast function %s in %s'
|
|
# % (downcastFuncName, globmod.__name__))
|
|
downcastFunctionList.append(func)
|
|
return downcastFunctionList
|
|
|
|
def lookUpNewType(self, typeHandle, rootType):
|
|
# We tried to downcast to an unknown type. Try to figure out
|
|
# the lowest type we *do* know, so we can downcast to that
|
|
# type instead.
|
|
if typeHandle.getNumParentClasses() == 0:
|
|
# This type has no parents! That shouldn't happen.
|
|
FFIConstants.notify.warning("Unknown class type: %s has no parents!" % (typeHandle.getName()))
|
|
return None
|
|
|
|
parentType = typeHandle.getParentTowards(rootType, self)
|
|
parentIndex = parentType.getIndex()
|
|
parentWrapperClass = WrapperClassMap.get(parentIndex)
|
|
if parentWrapperClass == None:
|
|
parentWrapperClass = self.lookUpNewType(parentType, rootType)
|
|
|
|
if parentWrapperClass != None:
|
|
# If the parent class is known, then record that this
|
|
# class is a derivation of that parent class.
|
|
WrapperClassMap[typeHandle.getIndex()] = parentWrapperClass
|
|
|
|
return parentWrapperClass
|
|
|
|
def setPointer(self):
|
|
# See what type it really is and downcast to that type (if necessary)
|
|
# Look up the TypeHandle in the dict. get() returns None if it is not there
|
|
index = self.getTypeIndex()
|
|
exactWrapperClass = WrapperClassMap.get(index)
|
|
if exactWrapperClass == None:
|
|
# This is an unknown class type. Perhaps it derives from
|
|
# a class type we know.
|
|
exactWrapperClass = self.lookUpNewType(self.getType(), self.getClassType())
|
|
|
|
# We do not need to downcast if we already have the same class
|
|
if (exactWrapperClass and (exactWrapperClass != self.__class__)):
|
|
# Create a new wrapper class instance
|
|
#exactObject = exactWrapperClass(None)
|
|
exactObject = FFIInstance(exactWrapperClass)
|
|
# Get the downcast pointer that has had all the downcast
|
|
# funcs called
|
|
downcastObject = self.downcast(exactWrapperClass)
|
|
exactObject.this = downcastObject.this
|
|
exactObject.userManagesMemory = downcastObject.userManagesMemory
|
|
# Make sure the original downcast object does not get
|
|
# garbage collected so that the exactObject will not get
|
|
# gc'd thereby transferring ownership of the object to
|
|
# this new exactObject
|
|
downcastObject.userManagesMemory = 0
|
|
return exactObject
|
|
else:
|
|
return self
|
|
|
|
def downcast(self, toClass):
|
|
fromClass = self.__class__
|
|
#FFIConstants.notify.debug('downcast: downcasting from %s to %s' % \
|
|
# (fromClass.__name__, toClass.__name__))
|
|
|
|
# Check the cache to see if we have looked this up before
|
|
downcastChain = DowncastMap.get((fromClass, toClass))
|
|
if downcastChain == None:
|
|
downcastChain = self.getDowncastFunctions(toClass, fromClass)
|
|
#FFIConstants.notify.debug('downcast: computed downcast chain: ' + repr(downcastChain))
|
|
# Store it for next time
|
|
DowncastMap[(fromClass, toClass)] = downcastChain
|
|
newObject = self
|
|
for downcastFunc in downcastChain:
|
|
#FFIConstants.notify.debug('downcast: downcasting %s using %s' % \
|
|
# (newObject.__class__.__name__, downcastFunc))
|
|
newObject = downcastFunc(newObject)
|
|
return newObject
|
|
|
|
def compareTo(self, other):
|
|
# By default, we compare the C++ pointers
|
|
# Some classes will override the compareTo operator with their own
|
|
# logic in C++ (like vectors and matrices for instance)
|
|
try:
|
|
if self.this < other.this:
|
|
return -1
|
|
if self.this > other.this:
|
|
return 1
|
|
else:
|
|
return 0
|
|
except:
|
|
return 1
|
|
|
|
def __cmp__(self, other):
|
|
# Only use the C++ compareTo if they are the same class
|
|
if isinstance(other, self.__class__):
|
|
return self.compareTo(other)
|
|
# Otherwise, they must not be the same
|
|
# Just do a basic python id compare
|
|
else:
|
|
return cmp(id(self), id(other))
|
|
|
|
def __repr__(self):
|
|
# Lots of Panda classes have an output function defined that takes an Ostream
|
|
# We create a LineStream for the output function to write to, then we extract
|
|
# the string out of it and return it as our str
|
|
try:
|
|
from pandac import LineStream
|
|
lineStream = LineStream.LineStream()
|
|
self.output(lineStream)
|
|
baseRepr = lineStream.getLine()
|
|
except AssertionError, e:
|
|
raise AssertionError, e
|
|
except:
|
|
baseRepr = ('[' + self.__class__.__name__ + ' at: ' + repr(self.this) + ']')
|
|
# In any case, return the baseRepr
|
|
return baseRepr
|
|
|
|
def __str__(self):
|
|
# This is a more complete version of printing which shows the object type
|
|
# and pointer, plus the output from write() or output() whichever is defined
|
|
# Print this info for all objects
|
|
baseRepr = ('[' + self.__class__.__name__ + ' at: ' + repr(self.this) + ']')
|
|
# Lots of Panda classes have an write or output function defined that takes an Ostream
|
|
# We create a LineStream for the write or output function to write to, then we extract
|
|
# the string out of it and return it as our repr
|
|
from pandac import LineStream
|
|
lineStream = LineStream.LineStream()
|
|
try:
|
|
# First try the write function, that is the better one
|
|
self.write(lineStream)
|
|
while lineStream.isTextAvailable():
|
|
baseRepr = baseRepr + '\n' + lineStream.getLine()
|
|
except AssertionError, e:
|
|
raise AssertionError, e
|
|
except:
|
|
try:
|
|
# Sometimes write insists on a seconds parameter.
|
|
self.write(lineStream, 0)
|
|
while lineStream.isTextAvailable():
|
|
baseRepr = baseRepr + '\n' + lineStream.getLine()
|
|
except AssertionError, e:
|
|
raise AssertionError, e
|
|
except:
|
|
try:
|
|
# Ok, no write function, lets try output then
|
|
self.output(lineStream)
|
|
while lineStream.isTextAvailable():
|
|
baseRepr = baseRepr + '\n' + lineStream.getLine()
|
|
except AssertionError, e:
|
|
raise AssertionError, e
|
|
except:
|
|
pass
|
|
# In any case, return the baseRepr
|
|
return baseRepr
|
|
|
|
def __hash__(self):
|
|
return self.this
|
|
|
|
|
|
|
|
|
|
|