Poodletooth-iLand/panda/direct/ffi/FFISpecs.py
Master Jumblespeed d882959bfa switch to remote
2015-05-18 22:11:33 -04:00

586 lines
26 KiB
Python
Executable file

import FFIConstants
import FFITypes
import FFIOverload
import string
from direct.showbase.PythonUtil import *
augmentedAssignments = ['__iadd__', '__isub__', '__imul__', '__idiv__',
'__ior__', '__iand__', '__ixor__',
'__ilshift__', '__irshift__']
class FunctionSpecification:
def __init__(self):
self.name = ''
self.typeDescriptor = None
self.index = 0
self.overloaded = 0
# Is this function a constructor
self.constructor = 0
def isConstructor(self):
return self.constructor
def isStatic(self):
for arg in self.typeDescriptor.argumentTypes:
if arg.isThis:
return 0
# No args were this pointers, must be static
return 1
def outputTypeChecking(self, methodClass, args, file, nesting):
"""
Output an assert statement to check the type of each arg in this method
This can be turned off with a command line parameter in generatePythonCode
It is valid to pass in None for methodClass if you are not in any methodClass
"""
if FFIConstants.wantTypeChecking:
for i in range(len(args)):
methodArgSpec = args[i]
typeDesc = methodArgSpec.typeDescriptor.recursiveTypeDescriptor()
typeName = FFIOverload.getTypeName(methodClass, typeDesc)
# We only do type checking on class types. C++ can do
# type checking on the primitive types, and will do a
# better job anyway.
if typeDesc.__class__ == FFITypes.ClassTypeDescriptor:
# Get the real return type (not derived)
if ((not typeDesc.isNested) and
# Do not put our own module in the import list
(methodClass != typeDesc)):
indent(file, nesting, 'import ' + typeDesc.foreignTypeName + '\n')
indent(file, nesting, 'if not isinstance(' +
methodArgSpec.name + ', ' + typeName + '):\n')
indent(file, nesting + 1,
'raise TypeError, "Invalid argument %s, expected <%s>"\n' % (i, typeDesc.foreignTypeName))
def outputCFunctionComment(self, file, nesting):
"""
Output a docstring to the file describing the C++ call with type info
Also output the C++ comment from interrogate.
"""
if FFIConstants.wantComments:
indent(file, nesting, '"""\n')
# Output the function prototype
if self.typeDescriptor.prototype:
indent(file, nesting, self.typeDescriptor.prototype + '\n')
# Output the function comment
if self.typeDescriptor.comment:
# To insert tabs into the comment, replace all newlines with a newline+tabs
comment = string.replace(self.typeDescriptor.comment,
'\n', ('\n' + (' ' * nesting)))
indent(file, nesting, comment)
indent(file, 0, '\n')
indent(file, nesting, '"""\n')
def getFinalName(self):
"""
Return the name of the function given that it might be overloaded
If it is overloaded, prepend "overloaded", then append the types of
each argument to make it unique.
So "getChild(int)" becomes "overloaded_getChild_int(int)"
"""
if self.overloaded:
name = 'private__overloaded_' + self.name
for methodArgSpec in self.typeDescriptor.argumentTypes:
name = name + '_' + methodArgSpec.typeDescriptor.foreignTypeName
return name
else:
return self.name
def outputOverloadedCall(self, file, classTypeDesc, numArgs):
"""
Write the function call to call this overloaded method
For example:
self.overloaded_setPos_ptrNodePath_float_float_float(*_args)
If it is a class (static) method, call the class method
Class.overloaded_setPos_ptrNodePath_float_float_float(*_args)
Constructors are not treated as static. They are special because
they are not really constructors, they are instance methods that fill
in the this pointer.
These do not get indented because they are not the beginning of the line
If classTypeDesc is None, then this is a global function and should
output code as such
"""
if classTypeDesc:
if (self.isStatic() and not self.isConstructor()):
if numArgs:
indent(file, 0, classTypeDesc.foreignTypeName + '.' + self.getFinalName() + '(*_args)\n')
else:
indent(file, 0, classTypeDesc.foreignTypeName + '.' + self.getFinalName() + '()\n')
else:
if numArgs:
indent(file, 0, 'self.' + self.getFinalName() + '(*_args)\n')
else:
indent(file, 0, 'self.' + self.getFinalName() + '()\n')
else:
if numArgs:
indent(file, 0, self.getFinalName() + '(*_args)\n')
else:
indent(file, 0, self.getFinalName() + '()\n')
class GlobalFunctionSpecification(FunctionSpecification):
def __init__(self):
FunctionSpecification.__init__(self)
# Use generateCode when creating a global (non-class) function
def generateGlobalCode(self, file):
self.outputHeader(file)
self.outputBody(file)
self.outputFooter(file)
# Use generateCode when creating a global (non-class) function
def generateGlobalDowncastCode(self, file):
self.outputHeader(file)
self.outputBody(file, 0, 0) # no downcast, no type checking
self.outputFooter(file)
# Use generateMethodCode when creating a global->class function
def generateMethodCode(self, methodClass, file, nesting):
self.outputMethodHeader(methodClass, file, nesting)
self.outputMethodBody(methodClass, file, nesting)
self.outputMethodFooter(methodClass, file, nesting)
##################################################
## Global Function Code Generation
##################################################
def outputHeader(self, file):
argTypes = self.typeDescriptor.argumentTypes
indent(file, 0, 'def ' + self.getFinalName() + '(')
for i in range(len(argTypes)):
file.write(argTypes[i].name)
if (i < (len(argTypes)-1)):
file.write(', ')
file.write('):\n')
def outputBody(self, file, needsDowncast=1, typeChecking=1):
# The method body will look something like
# returnValue = PandaGlobal.method(arg)
# returnObject = NodePath()
# returnObject.this = returnValue
# returnObject.userManagesMemory = 1 (optional)
# return returnObject
self.outputCFunctionComment(file, 1)
argTypes = self.typeDescriptor.argumentTypes
# The global downcast functions do not need type checking, and
# in fact have a problem where they assert you are downcasting
# from the immediate superclass when in fact you may be downcasting
# from a class way up the chain as long as their is single
# inheritance up to it
if typeChecking:
self.outputTypeChecking(None, argTypes, file, 1)
indent(file, 1, 'returnValue = ' + self.typeDescriptor.moduleName
+ '.' + self.typeDescriptor.wrapperName + '(')
for i in range(len(argTypes)):
file.write(argTypes[i].passName())
if (i < (len(argTypes)-1)):
file.write(', ')
file.write(')\n')
#indent(file, 1, 'if returnValue is None:\n')
#indent(file, 2, 'return None\n')
returnType = self.typeDescriptor.returnType.recursiveTypeDescriptor()
returnType.generateReturnValueWrapper(None, file,
self.typeDescriptor.userManagesMemory,
needsDowncast, 1)
def outputFooter(self, file):
indent(file, 0, '\n')
##################################################
## Class Method Code Generation
##################################################
def outputMethodHeader(self, methodClass, file, nesting):
argTypes = self.typeDescriptor.argumentTypes
indent(file, nesting, 'def ' + self.getFinalName() + '(')
for i in range(len(argTypes)):
# Instead of the first argument, put self
if (i == 0):
file.write('self')
else:
file.write(argTypes[i].name)
if (i < (len(argTypes)-1)):
file.write(', ')
file.write('):\n')
def outputMethodBody(self, methodClass, file, nesting):
# The method body will look something like
# returnValue = PandaGlobal.method(self.this, arg)
# returnValue.userManagesMemory = 1 (optional)
# return returnValue
self.outputCFunctionComment(file, nesting+2)
argTypes = self.typeDescriptor.argumentTypes
self.outputTypeChecking(methodClass, argTypes[1:], file, nesting+2)
indent(file, nesting+2, 'returnValue = ' + self.typeDescriptor.moduleName
+ '.' + self.typeDescriptor.wrapperName + '(')
for i in range(len(argTypes)):
# Instead of the first argument, put self.this
if (i == 0):
file.write('self.this')
else:
file.write(argTypes[i].passName())
if (i < (len(argTypes)-1)):
file.write(', ')
file.write(')\n')
indent(file, 1, 'if returnValue is None:\n')
indent(file, 2, 'return None\n')
returnType = self.typeDescriptor.returnType.recursiveTypeDescriptor()
returnType.generateReturnValueWrapper(methodClass, file,
self.typeDescriptor.userManagesMemory,
1, nesting+2)
def outputMethodFooter(self, methodClass, file, nesting):
indent(file, nesting+1, '\n')
class MethodSpecification(FunctionSpecification):
def __init__(self):
FunctionSpecification.__init__(self)
def generateConstructorCode(self, methodClass, file, nesting):
self.outputConstructorHeader(methodClass, file, nesting)
self.outputConstructorBody(methodClass, file, nesting)
self.outputConstructorFooter(methodClass, file, nesting)
def generateDestructorCode(self, methodClass, file, nesting):
self.outputDestructorHeader(methodClass, file, nesting)
self.outputDestructorBody(methodClass, file, nesting)
self.outputDestructorFooter(methodClass, file, nesting)
def generateMethodCode(self, methodClass, file, nesting):
self.outputMethodHeader(methodClass, file, nesting)
self.outputMethodBody(methodClass, file, nesting)
self.outputMethodFooter(methodClass, file, nesting)
def generateStaticCode(self, methodClass, file, nesting):
self.outputStaticHeader(methodClass, file, nesting)
self.outputStaticBody(methodClass, file, nesting)
self.outputStaticFooter(methodClass, file, nesting)
def generateInheritedMethodCode(self, methodClass, parentList, file, nesting, needsDowncast):
self.outputInheritedMethodHeader(methodClass, parentList, file, nesting, needsDowncast)
self.outputInheritedMethodBody(methodClass, parentList, file, nesting, needsDowncast)
self.outputInheritedMethodFooter(methodClass, parentList, file, nesting, needsDowncast)
def generateUpcastMethodCode(self, methodClass, file, nesting):
# The upcast method code is just like regular code, but the
# return value wrapper does not have downcasting instructions
self.outputMethodHeader(methodClass, file, nesting)
self.outputMethodBody(methodClass, file, nesting, 0) # no downcast
self.outputMethodFooter(methodClass, file, nesting)
##################################################
## Constructor Code Generation
##################################################
def outputConstructorHeader(self, methodClass, file, nesting):
argTypes = self.typeDescriptor.argumentTypes
thislessArgTypes = self.typeDescriptor.thislessArgTypes()
indent(file, nesting+1, 'def ' + self.getFinalName() + '(self')
if (len(thislessArgTypes) > 0):
file.write(', ')
for i in range(len(thislessArgTypes)):
file.write(thislessArgTypes[i].name)
if (i < (len(thislessArgTypes)-1)):
file.write(', ')
file.write('):\n')
self.outputCFunctionComment(file, nesting+2)
def outputConstructorBody(self, methodClass, file, nesting):
# The method body will look something like
# self.this = panda.Class_constructor(arg)
# self.userManagesMemory = 1 (optional)
argTypes = self.typeDescriptor.argumentTypes
thislessArgTypes = self.typeDescriptor.thislessArgTypes()
self.outputTypeChecking(methodClass, thislessArgTypes, file, nesting+2)
indent(file, nesting+2, 'self.this = ' + self.typeDescriptor.moduleName + '.'
+ self.typeDescriptor.wrapperName + '(')
# Do not pass self into the constructor
for i in range(len(thislessArgTypes)):
file.write(thislessArgTypes[i].passName())
if (i < (len(thislessArgTypes)-1)):
file.write(', ')
file.write(')\n')
indent(file, nesting+2, 'assert self.this != 0\n')
if self.typeDescriptor.userManagesMemory:
indent(file, nesting+2, 'self.userManagesMemory = 1\n')
def outputConstructorFooter(self, methodClass, file, nesting):
indent(file, nesting+1, '\n')
##################################################
## Destructor Code Generation
##################################################
def outputDestructorHeader(self, methodClass, file, nesting):
argTypes = self.typeDescriptor.argumentTypes
thislessArgTypes = self.typeDescriptor.thislessArgTypes()
indent(file, nesting+1, 'def ' + self.getFinalName() + '(self')
if (len(thislessArgTypes) > 0):
file.write(', ')
for i in range(len(thislessArgTypes)):
file.write(thislessArgTypes[i].name)
if (i < (len(thislessArgTypes)-1)):
file.write(', ')
file.write('):\n')
self.outputCFunctionComment(file, nesting+2)
def outputDestructorBody(self, methodClass, file, nesting):
# The method body will look something like
# panda.Class_destructor(self.this)
functionName = (self.typeDescriptor.moduleName + '.'
+ self.typeDescriptor.wrapperName)
# Make sure the module and function have not been deleted first
# This only happens during shutdown
indent(file, nesting+2, 'if (' + self.typeDescriptor.moduleName + ' and ' +
functionName + '):\n')
indent(file, nesting+3, functionName + '(self.this)\n')
def outputDestructorFooter(self, methodClass, file, nesting):
indent(file, nesting+1, '\n')
##################################################
## Method Code Generation
##################################################
def outputMethodHeader(self, methodClass, file, nesting):
argTypes = self.typeDescriptor.argumentTypes
thislessArgTypes = self.typeDescriptor.thislessArgTypes()
indent(file, nesting, 'def ' + self.getFinalName() + '(self')
if (len(thislessArgTypes) > 0):
file.write(', ')
for i in range(len(thislessArgTypes)):
file.write(thislessArgTypes[i].name)
if (i < (len(thislessArgTypes)-1)):
file.write(', ')
file.write('):\n')
def outputMethodBody(self, methodClass, file, nesting, needsDowncast=1):
# The method body will look something like
# returnValue = panda.Class_method(self.this, arg)
# returnValue.userManagesMemory = 1 (optional)
# return returnValue
self.outputCFunctionComment(file, nesting+2)
argTypes = self.typeDescriptor.argumentTypes
thislessArgTypes = self.typeDescriptor.thislessArgTypes()
self.outputTypeChecking(methodClass, thislessArgTypes, file, nesting+2)
indent(file, nesting+2, 'returnValue = ' + self.typeDescriptor.moduleName + '.'
+ self.typeDescriptor.wrapperName + '(')
file.write('self.this')
if (len(thislessArgTypes) > 0):
file.write(', ')
for i in range(len(thislessArgTypes)):
file.write(thislessArgTypes[i].passName())
if (i < (len(thislessArgTypes)-1)):
file.write(', ')
file.write(')\n')
# If this is an augmented assignment operator like +=, we have special rules
# In this case we simply call the C++ function, make sure we got the same
# return value back, then return self. Otherwise if you let it go through the
# normal system, it actually deletes the old Python object causing the C++ memory
# to be deleted then returns a new Python shadow object with the old C++ pointer... BAD!
if self.getFinalName() in augmentedAssignments:
indent(file, nesting+2, 'assert self.this == returnValue\n')
indent(file, nesting+2, 'return self\n')
else:
returnType = self.typeDescriptor.returnType.recursiveTypeDescriptor()
returnType.generateReturnValueWrapper(methodClass, file,
self.typeDescriptor.userManagesMemory,
needsDowncast, nesting+2)
def outputMethodFooter(self, methodClass, file, nesting):
indent(file, nesting, 'FFIExternalObject.funcToMethod(' +self.getFinalName()+ ',' + methodClass.foreignTypeName + ",'" +self.getFinalName() +"')\n")
indent(file, nesting, 'del ' + self.getFinalName()+' \n')
indent(file, nesting+1,'\n')
#indent(file, nesting, methodClass.foreignTypeName +'.'+ self.getFinalName() + ' = staticmethod(' + self.getFinalName() + ')\n')
#indent(file, nesting,'del ' + self.getFinalName()+' \n')
#indent(file, nesting+1, '\n')
indent(file, nesting+1, '\n')
##################################################
## Static Method Code Generation
##################################################
def outputStaticHeader(self, methodClass, file, nesting):
argTypes = self.typeDescriptor.argumentTypes
indent(file, nesting, 'def ' + self.getFinalName() + '(')
for i in range(len(argTypes)):
file.write(argTypes[i].name)
if (i < (len(argTypes)-1)):
file.write(', ')
file.write('):\n')
def outputStaticBody(self, methodClass, file, nesting):
# The method body will look something like
# returnValue = panda.class_method(self.this, arg)
# returnValue.userManagesMemory = 1 (optional)
# return returnValue
self.outputCFunctionComment(file, nesting+2)
argTypes = self.typeDescriptor.argumentTypes
thislessArgTypes = self.typeDescriptor.thislessArgTypes()
self.outputTypeChecking(methodClass, thislessArgTypes, file, nesting+2)
indent(file, nesting+2, 'returnValue = ' + self.typeDescriptor.moduleName + '.'
+ self.typeDescriptor.wrapperName + '(')
# Static methods do not take the this parameter
if (len(thislessArgTypes) > 0):
for i in range(len(thislessArgTypes)):
file.write(thislessArgTypes[i].passName())
if (i < (len(thislessArgTypes)-1)):
file.write(', ')
file.write(')\n')
returnType = self.typeDescriptor.returnType.recursiveTypeDescriptor()
returnType.generateReturnValueWrapper(methodClass, file,
self.typeDescriptor.userManagesMemory,
1, nesting+2)
def outputStaticFooter(self, methodClass, file, nesting):
indent(file, nesting, methodClass.foreignTypeName +'.'+ self.getFinalName() + ' = staticmethod(' + self.getFinalName() + ')\n')
indent(file, nesting,'del ' + self.getFinalName()+' \n')
indent(file, nesting+1, '\n')
##################################################
## Upcast Method Code Generation
##################################################
def outputInheritedMethodHeader(self, methodClass, parentList, file, nesting, needsDowncast):
argTypes = self.typeDescriptor.argumentTypes
thislessArgTypes = self.typeDescriptor.thislessArgTypes()
indent(file, nesting, 'def ' + self.getFinalName() + '(self')
if (len(thislessArgTypes) > 0):
file.write(', ')
for i in range(len(thislessArgTypes)):
file.write(thislessArgTypes[i].name)
if (i < (len(thislessArgTypes)-1)):
file.write(', ')
file.write('):\n')
def outputInheritedMethodBody(self, methodClass, parentList, file, nesting, needsDowncast):
# The method body will look something like
# upcastSelf = self.upcastToParentClass()
# returnValue = libpanda.method(upcastSelf.this, arg)
# returnValue.userManagesMemory = 1 (optional)
# return returnValue
self.outputCFunctionComment(file, nesting+2)
argTypes = self.typeDescriptor.argumentTypes
thislessArgTypes = self.typeDescriptor.thislessArgTypes()
self.outputTypeChecking(methodClass, thislessArgTypes, file, nesting+2)
indent(file, nesting+2, 'upcastSelf = self\n')
for i in range(len(parentList)):
# Only output the upcast call if that parent class defines it
parentClass = parentList[i]
methodName = 'upcastTo' + parentClass.foreignTypeName
if (i != 0):
childClass = parentList[i-1]
if childClass.hasMethodNamed(methodName):
indent(file, nesting+2, 'upcastSelf = upcastSelf.' + methodName + '()\n')
else:
indent(file, nesting+2, '# upcastSelf = upcastSelf.' + methodName + '()\n')
else:
if methodClass.hasMethodNamed(methodName):
indent(file, nesting+2, 'upcastSelf = upcastSelf.' + methodName + '()\n')
else:
indent(file, nesting+2, '# upcastSelf = upcastSelf.' + methodName + '()\n')
indent(file, nesting+2, 'returnValue = ' + self.typeDescriptor.moduleName
+ '.' + self.typeDescriptor.wrapperName + '(upcastSelf.this')
if (len(thislessArgTypes) > 0):
file.write(', ')
for i in range(len(thislessArgTypes)):
file.write(thislessArgTypes[i].passName())
if (i < (len(thislessArgTypes)-1)):
file.write(', ')
file.write(')\n')
returnType = self.typeDescriptor.returnType.recursiveTypeDescriptor()
# Generate the return value code with no downcast instructions
returnType.generateReturnValueWrapper(methodClass, file,
self.typeDescriptor.userManagesMemory,
needsDowncast, nesting+2)
def outputInheritedMethodFooter(self, methodClass, parentList, file, nesting, needsDowncast):
indent(file, nesting, 'FFIExternalObject.funcToMethod(' +self.getFinalName()+ ',' + methodClass.foreignTypeName + ",'" +self.getFinalName() +"')\n")
indent(file, nesting, 'del ' + self.getFinalName()+' \n')
indent(file, nesting+1,'\n')
class GlobalValueSpecification:
def __init__(self):
self.name = ''
# We really do not need the descriptor for the value, just
# the getter and setter
# self.typeDescriptor = None
# To be filled in with a GlobalFunctionSpecification
self.getter = None
# To be filled in with a GlobalFunctionSpecification
self.setter = None
def generateGlobalCode(self, file):
indent(file, 0, '# Global value: ' + self.name + '\n')
if self.getter:
self.getter.generateGlobalCode(file)
if self.setter:
self.setter.generateGlobalCode(file)
indent(file, 0, '\n')
# Manifest symbols
class ManifestSpecification:
def __init__(self):
self.name = ''
# We are not currently using the type descriptor
self.typeDescriptor = None
# To be filled in with a GlobalFunctionSpecification
# if this manifest has one
self.getter = None
# Manifests that have int values have their int value defined
# instead of having to call a getter (because there are so many of them)
self.intValue = None
# The string definition of this manifest
self.definition = None
def generateGlobalCode(self, file):
# Note, if the manifest has no value and no getter we do not output anything
# even though they may be defined in the C++ sense. Without any values
# they are pretty useless in Python
# If it has an int value, just output that instead of bothering
# with a getter
if (self.intValue != None):
indent(file, 0, '# Manifest: ' + self.name + '\n')
indent(file, 0, (self.name + ' = ' + repr(self.intValue) + '\n'))
indent(file, 0, '\n')
elif self.definition:
indent(file, 0, ('# Manifest: ' + self.name + ' definition: ' +
self.definition + '\n'))
# Out put the getter
if self.getter:
self.getter.generateGlobalCode(file)
indent(file, 0, '\n')
class MethodArgumentSpecification:
def __init__(self):
self.name = ''
self.typeDescriptor = None
# By default it is not the this pointer
self.isThis = 0
def passName(self):
if (self.typeDescriptor.recursiveTypeDescriptor().__class__ == \
FFITypes.ClassTypeDescriptor):
return self.name + '.this'
else:
return self.name