Poodletooth-iLand/panda/direct/ffi/FFIExternalObject.py
2015-03-03 17:10:12 -05:00

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