diff --git a/direct/src/ffi/FFIEnvironment.py b/direct/src/ffi/FFIEnvironment.py index 48891928d4..a4fa0eaabd 100644 --- a/direct/src/ffi/FFIEnvironment.py +++ b/direct/src/ffi/FFIEnvironment.py @@ -5,6 +5,7 @@ class FFIEnvironment: self.types = {} self.globalFunctions = [] self.globalValues = [] + self.manifests = [] def addType(self, typeDescriptor, name): if self.types.has_key(name): @@ -21,3 +22,5 @@ class FFIEnvironment: self.globalFunctions.append(typeDescriptor) def addGlobalValue(self, typeDescriptor): self.globalValues.append(typeDescriptor) + def addManifest(self, typeDescriptor): + self.manifests.append(typeDescriptor) diff --git a/direct/src/ffi/FFIInterrogateDatabase.py b/direct/src/ffi/FFIInterrogateDatabase.py index 989533aa7c..700c5b1205 100644 --- a/direct/src/ffi/FFIInterrogateDatabase.py +++ b/direct/src/ffi/FFIInterrogateDatabase.py @@ -44,6 +44,9 @@ def outputGlobalFileImports(file, methodList): # Print the standard header file.write(FFIConstants.generatedHeader) + # Import Python's builtin types + file.write('import types\n') + # Import the C modules for CModuleName in FFIConstants.CodeModuleNameList: file.write('import ' + CModuleName + '\n') @@ -370,7 +373,10 @@ class FFIInterrogateDatabase: # Prepend lib to the module name it reports because that will be the name of # the Python module we import. This is apparently stems from a makefile # discrepency in the way we build the libraries - moduleName = 'lib' + interrogate_function_module_name(functionIndex) + if interrogate_function_has_module_name(functionIndex): + moduleName = 'lib' + interrogate_function_module_name(functionIndex) + else: + moduleName = None typeIndex = functionIndex # Look at the Python wrappers for this function @@ -497,6 +503,8 @@ class FFIInterrogateDatabase: # funcSpec.name = FFIRename.methodNameFromCppName( # interrogate_function_name(funcIndex)) funcSpec.typeDescriptor = typeDesc + # Flag this function as being a constructor + funcSpec.constructor = 1 funcSpec.index = funcIndex funcSpecs.append(funcSpec) return funcSpecs @@ -528,7 +536,9 @@ class FFIInterrogateDatabase: def constructGlobal(self, globalIndex): # We really do not need the descriptor for the value, just # the getter and setter - # descriptor = self.typeIndexMap[interrogate_element_type(globalIndex)] + # typeIndex = interrogate_element_type(globalIndex) + # descriptor = self.constructDescriptor(typeIndex) + if interrogate_element_has_getter(globalIndex): getterIndex = interrogate_element_getter(globalIndex) getter = self.constructGlobalFunction(getterIndex) @@ -602,6 +612,46 @@ class FFIInterrogateDatabase: newGlob = self.constructGlobal(globalIndex) self.environment.addGlobalValue(newGlob) + + def constructManifest(self, manifestIndex): + descriptor = None + intValue = None + getter = None + + if interrogate_manifest_has_type(manifestIndex): + typeIndex = interrogate_manifest_get_type(manifestIndex) + descriptor = self.constructDescriptor(typeIndex) + + definition = interrogate_manifest_definition(manifestIndex) + + # See if this manifest is an int. There are shortcuts if it is. + # If it does have an int value, there will be no getter, we will + # just output the value in the generated code + if interrogate_manifest_has_int_value(manifestIndex): + intValue = interrogate_manifest_get_int_value(manifestIndex) + else: + # See if this manifest has a getter + if interrogate_manifest_has_getter(manifestIndex): + getterIndex = interrogate_manifest_getter(manifestIndex) + getter = self.constructGlobalFunction(getterIndex) + + manifestSpec = FFISpecs.ManifestSpecification() + manifestSpec.typeDescriptor = descriptor + manifestSpec.definition = definition + manifestSpec.intValue = intValue + manifestSpec.getter = getter + cppName = interrogate_manifest_name(manifestIndex) + manifestSpec.name = FFIRename.classNameFromCppName(cppName) + return manifestSpec + + def addManifestSymbols(self): + numManifests = interrogate_number_of_manifests() + for i in range(numManifests): + manifestIndex = interrogate_get_manifest(i) + newManifest = self.constructManifest(manifestIndex) + self.environment.addManifest(newManifest) + + def generateCode(self, codeDir, extensionsDir): FFIConstants.notify.info( 'Generating static class...') generateStaticClass(codeDir) @@ -627,6 +677,7 @@ class FFIInterrogateDatabase: # Output all the imports based on this list of functions outputGlobalFileImports(globalFile, globalFunctions) + FFIConstants.notify.info( 'Generating global value code...') for type in self.environment.globalValues: type.generateGlobalCode(globalFile) @@ -634,13 +685,17 @@ class FFIInterrogateDatabase: for type in self.environment.globalFunctions: type.generateGlobalCode(globalFile) + FFIConstants.notify.info( 'Generating manifest code...') + for type in self.environment.manifests: + type.generateGlobalCode(globalFile) + globalFile.close() FFIConstants.notify.info( 'Generating import code...') importFile = constructImportFile(codeDir) outputImportFileImports(importFile, self.environment.types.values()) - + FFIConstants.notify.info( 'Compiling code...') compileall.compile_dir(codeDir) @@ -652,4 +707,9 @@ class FFIInterrogateDatabase: self.addGlobalValues() FFIConstants.notify.info( 'Adding global functions...') self.addGlobalFunctions() + FFIConstants.notify.info( 'Adding manifests symbols...') + self.addManifestSymbols() + FFIConstants.notify.info( 'Adding environment types...') self.addEnvironmentTypes() + + diff --git a/direct/src/ffi/FFIOverload.py b/direct/src/ffi/FFIOverload.py index 1334ff2141..b040a27b6c 100644 --- a/direct/src/ffi/FFIOverload.py +++ b/direct/src/ffi/FFIOverload.py @@ -3,11 +3,13 @@ from PythonUtil import * from types import * import string import FFIConstants +import FFISpecs """ Things that are not supported: - Overloading a function based on an enum being differentiated from an int - - Type names from C++ cannot begin with __enum__ + - Type names from C++ cannot have __enum__ in their name + - Overloading static and non-static methods with the same name """ AT_not_atomic = 0 @@ -52,10 +54,16 @@ def getTypeName(classTypeDesc, typeDesc): # Convert the void type to None type... I guess... # So far we do not have any code that uses this return 'types.NoneType' + + else: + FFIConstants.notify.error("Unknown atomicType: " + typeDesc.atomicType) # If the type is an enum, we really want to treat it like an int - # To handle this, the type will have __enum__ prepended to the name - elif (typeName[0:8] == '__enum__'): + # To handle this, the type will have __enum__ in the name + # Usually it will start the typeName, but some typeNames have the + # surrounding class as part of their name + # like BoundedObject.__enum__BoundingVolumeType + elif (typeName.find('__enum__') >= 0): return 'types.IntType' # If it was not atomic or enum, it must be a class which is a @@ -146,15 +154,44 @@ class FFIMethodArgumentTreeCollection: self.methodDict = {} self.treeDict = {} - def outputOverloadedMethodHeader(self, file): - indent(file, 1, 'def ' + self.methodSpecList[0].name - + '(self, *_args):\n') - indent(file, 2, 'numArgs = len(_args)\n') + def outputOverloadedMethodHeader(self, file, nesting): + # If one is static, we assume they all are. + # The current system does not support overloading static and non-static + # methods with the same name + # 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. + if (self.methodSpecList[0].isStatic() and + (not self.methodSpecList[0].isConstructor())): + indent(file, nesting+1, 'def ' + + self.methodSpecList[0].name + '(*_args):\n') + else: + indent(file, nesting+1, 'def ' + + self.methodSpecList[0].name + '(self, *_args):\n') + indent(file, nesting+2, 'numArgs = len(_args)\n') - def outputOverloadedMethodFooter(self, file): + def outputOverloadedMethodFooter(self, file, nesting): # If the overloaded function got all the way through the if statements # it must have had the wrong number or type of arguments - indent(file, 2, "raise TypeError, 'Invalid arguments'\n\n") + indent(file, nesting+2, "raise TypeError, 'Invalid arguments'\n") + + # If this is a static method, we need to output a static version + # If one is static, we assume they all are. + # The current system does not support overloading static and non-static + # methods with the same name + # 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. + + if (self.methodSpecList[0].isStatic() and + (not self.methodSpecList[0].isConstructor())): + self.outputOverloadedStaticFooter(file, nesting) + indent(file, nesting+1, '\n') + + def outputOverloadedStaticFooter(self, file, nesting): + indent(file, nesting+1, self.methodSpecList[0].name + ' = ' + + FFIConstants.staticModuleName + '.' + FFIConstants.staticModuleName + + '(' + self.methodSpecList[0].name + ')\n') def setup(self): for method in self.methodSpecList: @@ -169,16 +206,16 @@ class FFIMethodArgumentTreeCollection: def generateCode(self, file, nesting): self.setup() - self.outputOverloadedMethodHeader(file) + self.outputOverloadedMethodHeader(file, nesting) numArgsKeys = self.treeDict.keys() numArgsKeys.sort() for numArgs in numArgsKeys: trees = self.treeDict[numArgs] for tree in trees: - indent(file, 2, 'if (numArgs == ' + `numArgs` + '):\n') + indent(file, nesting+2, 'if (numArgs == ' + `numArgs` + '):\n') tree.setup() - tree.traverse(file) - self.outputOverloadedMethodFooter(file) + tree.traverse(file, nesting+1) + self.outputOverloadedMethodFooter(file, nesting) class FFIMethodArgumentTree: """ @@ -241,7 +278,7 @@ class FFIMethodArgumentTree: # Output the function methodSpec = self.tree[0][1] indent(file, level+2, 'return ') - methodSpec.outputOverloadedCall(file, 0) + methodSpec.outputOverloadedCall(file, self.classTypeDesc, 0) else: typeName = getTypeName(self.classTypeDesc, typeDesc) indent(file, level+2, 'if (isinstance(_args[' + `level-1` + '], ' @@ -260,7 +297,7 @@ class FFIMethodArgumentTree: methodSpec = self.tree[typeDesc][1] indent(file, level+3, 'return ') numArgs = level - methodSpec.outputOverloadedCall(file, numArgs) + methodSpec.outputOverloadedCall(file, self.classTypeDesc, numArgs) diff --git a/direct/src/ffi/FFISpecs.py b/direct/src/ffi/FFISpecs.py index 022883e19a..3adb31dbd1 100644 --- a/direct/src/ffi/FFISpecs.py +++ b/direct/src/ffi/FFISpecs.py @@ -12,6 +12,18 @@ class FunctionSpecification: 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): """ @@ -73,13 +85,26 @@ class FunctionSpecification: else: return self.name - def outputOverloadedCall(self, file, numArgs): + 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[0], _args[1], _args[2]) + If it is a class (static) method, call the class method + Class.overloaded_setPos_ptrNodePath_float_float_float(_args[0], _args[1], _args[2]) + + 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 + """ - indent(file, 0, 'self.' + self.getFinalName() + '(') + if (self.isStatic() and not self.isConstructor()): + indent(file, 0, classTypeDesc.foreignTypeName + '.' + self.getFinalName() + '(') + else: + indent(file, 0, 'self.' + self.getFinalName() + '(') + for i in range(numArgs): file.write('_args[' + `i` + ']') if (i != (numArgs - 1)): @@ -230,11 +255,6 @@ class GlobalFunctionSpecification(FunctionSpecification): class MethodSpecification(FunctionSpecification): def __init__(self): FunctionSpecification.__init__(self) - def isStatic(self): - for arg in self.typeDescriptor.argumentTypes: - if arg.isThis: - return 0 - return 1 def generateConstructorCode(self, methodClass, file, nesting): self.outputConstructorHeader(methodClass, file, nesting) @@ -484,6 +504,47 @@ class GlobalValueSpecification: 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 + ' = ' + `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 = ''