969 lines
30 KiB
C++
969 lines
30 KiB
C++
/**
|
|
* PANDA 3D SOFTWARE
|
|
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
|
*
|
|
* All use of this software is subject to the terms of the revised BSD
|
|
* license. You should have received a copy of this license along
|
|
* with this source code in a file named "LICENSE."
|
|
*
|
|
* @file interfaceMaker.cxx
|
|
* @author drose
|
|
* @date 2001-09-19
|
|
*/
|
|
|
|
#include "interfaceMaker.h"
|
|
#include "interrogateBuilder.h"
|
|
#include "typeManager.h"
|
|
#include "interrogate.h"
|
|
#include "functionRemap.h"
|
|
#include "parameterRemap.h"
|
|
#include "parameterRemapThis.h"
|
|
#include "parameterRemapUnchanged.h"
|
|
#include "parameterRemapReferenceToPointer.h"
|
|
#include "parameterRemapConcreteToPointer.h"
|
|
#include "parameterRemapEnumToInt.h"
|
|
#include "parameterRemapConstToNonConst.h"
|
|
#include "parameterRemapReferenceToConcrete.h"
|
|
#include "parameterRemapCharStarToString.h"
|
|
#include "parameterRemapBasicStringToString.h"
|
|
#include "parameterRemapBasicStringRefToString.h"
|
|
#include "parameterRemapBasicStringPtrToString.h"
|
|
#include "parameterRemapPTToPointer.h"
|
|
|
|
#include "interrogateDatabase.h"
|
|
#include "interrogateManifest.h"
|
|
#include "interrogateElement.h"
|
|
#include "cppFunctionType.h"
|
|
#include "cppParameterList.h"
|
|
#include "cppMakeSeq.h"
|
|
#include "cppStructType.h"
|
|
#include "pnotify.h"
|
|
|
|
using std::ostream;
|
|
using std::ostringstream;
|
|
using std::string;
|
|
|
|
InterrogateType dummy_type;
|
|
|
|
/**
|
|
*
|
|
*/
|
|
InterfaceMaker::Function::
|
|
Function(const string &name,
|
|
const InterrogateType &itype,
|
|
const InterrogateFunction &ifunc) :
|
|
_name(name),
|
|
_itype(itype),
|
|
_ifunc(ifunc)
|
|
{
|
|
_has_this = false;
|
|
_flags = 0;
|
|
_args_type = AT_unknown;
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
InterfaceMaker::Function::
|
|
~Function() {
|
|
Remaps::iterator ri;
|
|
for (ri = _remaps.begin(); ri != _remaps.end(); ++ri) {
|
|
delete (*ri);
|
|
}
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
InterfaceMaker::MakeSeq::
|
|
MakeSeq(const string &name, const InterrogateMakeSeq &imake_seq) :
|
|
_name(name),
|
|
_imake_seq(imake_seq),
|
|
_length_getter(nullptr),
|
|
_element_getter(nullptr)
|
|
{
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
InterfaceMaker::Property::
|
|
Property(const InterrogateElement &ielement) :
|
|
_ielement(ielement),
|
|
_length_function(nullptr),
|
|
_has_function(nullptr),
|
|
_clear_function(nullptr),
|
|
_deleter(nullptr),
|
|
_inserter(nullptr),
|
|
_getkey_function(nullptr),
|
|
_has_this(false)
|
|
{
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
InterfaceMaker::Object::
|
|
Object(const InterrogateType &itype) :
|
|
_itype(itype),
|
|
_protocol_types(0)
|
|
{
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
InterfaceMaker::Object::
|
|
~Object() {
|
|
}
|
|
|
|
/**
|
|
* To be called after all of the methods have been added, this checks which
|
|
* protocols this object appears to support (based on the methods it
|
|
* provides).
|
|
*/
|
|
void InterfaceMaker::Object::
|
|
check_protocols() {
|
|
int flags = 0;
|
|
|
|
Functions::const_iterator fi;
|
|
for (fi = _constructors.begin(); fi != _constructors.end(); ++fi) {
|
|
Function *func = (*fi);
|
|
flags |= func->_flags;
|
|
}
|
|
for (fi = _methods.begin(); fi != _methods.end(); ++fi) {
|
|
Function *func = (*fi);
|
|
flags |= func->_flags;
|
|
|
|
if (func->_ifunc.get_name() == "__traverse__") {
|
|
// If we have a method named __traverse__, we implement Python's cyclic
|
|
// garbage collection protocol.
|
|
//XXX disabled for now because it's too unstable.
|
|
//_protocol_types |= PT_python_gc;
|
|
}
|
|
}
|
|
|
|
if ((flags & (FunctionRemap::F_getitem_int | FunctionRemap::F_size)) ==
|
|
(FunctionRemap::F_getitem_int | FunctionRemap::F_size)) {
|
|
// If we have both a getitem that receives an int, and a size, then we
|
|
// implement the sequence protocol: you can iterate through the elements
|
|
// of this object.
|
|
_protocol_types |= PT_sequence;
|
|
|
|
} else if (flags & FunctionRemap::F_getitem) {
|
|
// If we have any getitem, then we implement the mapping protocol.
|
|
_protocol_types |= PT_mapping;
|
|
}
|
|
|
|
if (flags & FunctionRemap::F_make_copy) {
|
|
// It's not exactly a protocol, but if we have a make_copy() method, we
|
|
// can use it to synthesize a __copy__ and __deepcopy__ Python method to
|
|
// support the copy module.
|
|
_protocol_types |= PT_make_copy;
|
|
} else if (flags & FunctionRemap::F_copy_constructor) {
|
|
// Ditto for the copy constructor.
|
|
_protocol_types |= PT_copy_constructor;
|
|
}
|
|
|
|
if (flags & FunctionRemap::F_iter) {
|
|
_protocol_types |= PT_iter;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns true if the first method found with the indicated name is a static
|
|
* method, false if it is an instance method. This does not test all
|
|
* overloads of the indicated name, merely the first one found.
|
|
*/
|
|
bool InterfaceMaker::Object::
|
|
is_static_method(const string &name) {
|
|
Functions::const_iterator fi;
|
|
for (fi = _methods.begin(); fi != _methods.end(); ++fi) {
|
|
Function *func = (*fi);
|
|
if (!func->_remaps.empty()) {
|
|
FunctionRemap *remap = func->_remaps.front();
|
|
string method_name = remap->_cppfunc->get_simple_name();
|
|
if (method_name == name) {
|
|
return !func->_has_this;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Didn't find the requested function.
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
InterfaceMaker::
|
|
InterfaceMaker(InterrogateModuleDef *def) :
|
|
_def(def)
|
|
{
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
InterfaceMaker::
|
|
~InterfaceMaker() {
|
|
Objects::iterator oi;
|
|
for (oi = _objects.begin(); oi != _objects.end(); ++oi) {
|
|
Object *object = (*oi).second;
|
|
delete object;
|
|
}
|
|
FunctionsByIndex::iterator fi;
|
|
for (fi = _functions.begin(); fi != _functions.end(); ++fi) {
|
|
delete (*fi).second;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Walks through the set of functions in the database and generates wrappers
|
|
* for each function, storing these in the database. No actual code should be
|
|
* output yet; this just updates the database with the wrapper information.
|
|
*/
|
|
void InterfaceMaker::
|
|
generate_wrappers() {
|
|
InterrogateDatabase *idb = InterrogateDatabase::get_ptr();
|
|
|
|
// We use a while loop rather than a simple for loop, because we might
|
|
// increase the number of types recursively during the traversal.
|
|
int ti = 0;
|
|
while (ti < idb->get_num_all_types()) {
|
|
TypeIndex type_index = idb->get_all_type(ti++);
|
|
record_object(type_index);
|
|
}
|
|
|
|
int num_global_elements = idb->get_num_global_elements();
|
|
for (int gi = 0; gi < num_global_elements; ++gi) {
|
|
printf(" Global Type = %d", gi);
|
|
TypeIndex type_index = idb->get_global_element(gi);
|
|
record_object(type_index);
|
|
}
|
|
|
|
int num_functions = idb->get_num_global_functions();
|
|
for (int fi = 0; fi < num_functions; fi++) {
|
|
FunctionIndex func_index = idb->get_global_function(fi);
|
|
record_function(dummy_type, func_index);
|
|
}
|
|
|
|
int num_manifests = idb->get_num_global_manifests();
|
|
for (int mi = 0; mi < num_manifests; mi++) {
|
|
ManifestIndex manifest_index = idb->get_global_manifest(mi);
|
|
const InterrogateManifest &iman = idb->get_manifest(manifest_index);
|
|
if (iman.has_getter()) {
|
|
FunctionIndex func_index = iman.get_getter();
|
|
record_function(dummy_type, func_index);
|
|
}
|
|
printf(" Manifests %d\n", mi);
|
|
}
|
|
|
|
int num_elements = idb->get_num_global_elements();
|
|
for (int ei = 0; ei < num_elements; ei++) {
|
|
printf(" Element %d\n", ei);
|
|
|
|
ElementIndex element_index = idb->get_global_element(ei);
|
|
const InterrogateElement &ielement = idb->get_element(element_index);
|
|
if (ielement.has_getter()) {
|
|
FunctionIndex func_index = ielement.get_getter();
|
|
record_function(dummy_type, func_index);
|
|
}
|
|
if (ielement.has_setter()) {
|
|
FunctionIndex func_index = ielement.get_setter();
|
|
record_function(dummy_type, func_index);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Generates the list of #include ... whatever that's required by this
|
|
* particular interface to the indicated output stream.
|
|
*/
|
|
void InterfaceMaker::
|
|
write_includes(ostream &) {
|
|
}
|
|
|
|
/**
|
|
* Generates the list of function prototypes corresponding to the functions
|
|
* that will be output in write_functions().
|
|
*/
|
|
void InterfaceMaker::
|
|
write_prototypes(ostream &out,ostream *out_h) {
|
|
_function_writers.write_prototypes(out);
|
|
}
|
|
|
|
/**
|
|
* Generates the list of functions that are appropriate for this interface.
|
|
*/
|
|
void InterfaceMaker::
|
|
write_functions(ostream &out) {
|
|
_function_writers.write_code(out);
|
|
}
|
|
|
|
/**
|
|
* Generates whatever additional code is required to support a module file.
|
|
*/
|
|
void InterfaceMaker::
|
|
write_module(ostream &, ostream *out_h, InterrogateModuleDef *) {
|
|
}
|
|
|
|
/**
|
|
* Allocates a new ParameterRemap object suitable to the indicated parameter
|
|
* type. If struct_type is non-NULL, it is the type of the enclosing class
|
|
* for the function (method) in question.
|
|
*
|
|
* The return value is a newly-allocated ParameterRemap object, if the
|
|
* parameter type is acceptable, or NULL if the parameter type cannot be
|
|
* handled.
|
|
*/
|
|
ParameterRemap *InterfaceMaker::
|
|
remap_parameter(CPPType *struct_type, CPPType *param_type) {
|
|
nassertr(param_type != nullptr, nullptr);
|
|
|
|
if (convert_strings) {
|
|
if (TypeManager::is_char_pointer(param_type)) {
|
|
return new ParameterRemapCharStarToString(param_type);
|
|
}
|
|
if (TypeManager::is_wchar_pointer(param_type)) {
|
|
return new ParameterRemapWCharStarToWString(param_type);
|
|
}
|
|
|
|
// If we're exporting a method of basic_string<char> itself, don't convert
|
|
// basic_string<char>'s to atomic strings.
|
|
|
|
if (struct_type == nullptr ||
|
|
!(TypeManager::is_basic_string_char(struct_type) ||
|
|
TypeManager::is_basic_string_wchar(struct_type))) {
|
|
if (TypeManager::is_basic_string_char(param_type)) {
|
|
return new ParameterRemapBasicStringToString(param_type);
|
|
|
|
} else if (TypeManager::is_const_ref_to_basic_string_char(param_type)) {
|
|
return new ParameterRemapBasicStringRefToString(param_type);
|
|
|
|
} else if (TypeManager::is_const_ptr_to_basic_string_char(param_type)) {
|
|
return new ParameterRemapBasicStringPtrToString(param_type);
|
|
|
|
} else if (TypeManager::is_basic_string_wchar(param_type)) {
|
|
return new ParameterRemapBasicWStringToWString(param_type);
|
|
|
|
} else if (TypeManager::is_const_ref_to_basic_string_wchar(param_type)) {
|
|
return new ParameterRemapBasicWStringRefToWString(param_type);
|
|
|
|
} else if (TypeManager::is_const_ptr_to_basic_string_char(param_type)) {
|
|
return new ParameterRemapBasicStringPtrToString(param_type);
|
|
|
|
} else if (TypeManager::is_const_ptr_to_basic_string_wchar(param_type)) {
|
|
return new ParameterRemapBasicWStringPtrToWString(param_type);
|
|
|
|
} else if (TypeManager::is_reference(param_type) ||
|
|
TypeManager::is_pointer(param_type)) {
|
|
// Python strings are immutable, so we can't wrap a non-const pointer
|
|
// or reference to a string.
|
|
CPPType *pt_type = TypeManager::unwrap(param_type);
|
|
if (TypeManager::is_basic_string_char(pt_type) ||
|
|
TypeManager::is_basic_string_wchar(pt_type)) {
|
|
return nullptr;
|
|
}
|
|
}
|
|
}
|
|
if (struct_type == nullptr ||
|
|
!TypeManager::is_vector_unsigned_char(struct_type)) {
|
|
if (TypeManager::is_vector_unsigned_char(param_type)) {
|
|
if (TypeManager::is_reference(param_type)) {
|
|
return new ParameterRemapReferenceToConcrete(param_type);
|
|
} else if (TypeManager::is_const(param_type)) {
|
|
return new ParameterRemapConstToNonConst(param_type);
|
|
} else {
|
|
return new ParameterRemapUnchanged(param_type);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (manage_reference_counts) {
|
|
if (TypeManager::is_pointer_to_base(param_type) ||
|
|
TypeManager::is_const_ref_to_pointer_to_base(param_type))
|
|
{
|
|
CPPType *pt_type = TypeManager::unwrap_reference(param_type);
|
|
|
|
// Don't convert PointerTo<>'s to pointers for methods of the PointerTo
|
|
// itself!
|
|
if (struct_type == nullptr ||
|
|
!(pt_type->get_local_name(&parser) == struct_type->get_local_name(&parser))) {
|
|
return new ParameterRemapPTToPointer(param_type);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (TypeManager::is_reference(param_type)) {
|
|
return new ParameterRemapReferenceToPointer(param_type);
|
|
|
|
} else if (TypeManager::is_struct(param_type)) {
|
|
return new ParameterRemapConcreteToPointer(param_type);
|
|
|
|
/*
|
|
} else if (TypeManager::is_enum(param_type) || TypeManager::is_const_ref_to_enum(param_type)) {
|
|
return new ParameterRemapEnumToInt(param_type);
|
|
*/
|
|
|
|
// } else if (TypeManager::is_const_simple(param_type)) { return new
|
|
// ParameterRemapConstToNonConst(param_type);
|
|
|
|
} else if (TypeManager::is_const_ref_to_simple(param_type)) {
|
|
return new ParameterRemapReferenceToConcrete(param_type);
|
|
|
|
} else if (TypeManager::is_pointer(param_type) ||
|
|
TypeManager::is_void(param_type) ||
|
|
TypeManager::is_simple(param_type) ||
|
|
TypeManager::is_simple_array(param_type)) {
|
|
return new ParameterRemapUnchanged(param_type);
|
|
|
|
} else {
|
|
// Here's something we have a problem with.
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This method should be overridden and redefined to return true for
|
|
* interfaces that require the implicit "this" parameter, if present, to be
|
|
* passed as the first parameter to any wrapper functions.
|
|
*/
|
|
bool InterfaceMaker::
|
|
synthesize_this_parameter() {
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* This method should be overridden and redefined to return true for
|
|
* interfaces that require overloaded instances of a function to be defined as
|
|
* separate functions (each with its own hashed name), or false for interfaces
|
|
* that can support overloading natively, and thus only require one wrapper
|
|
* function per each overloaded input function.
|
|
*/
|
|
bool InterfaceMaker::
|
|
separate_overloading() {
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* This method should be overridden and redefined to return false for
|
|
* interfaces that don't support global functions and should therefore will
|
|
* only accept function remaps that have a class associated.
|
|
*/
|
|
bool InterfaceMaker::
|
|
wrap_global_functions() {
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Fills up the indicated vector with all of the FunctionRemap pointers
|
|
* created by this InterfaceMaker. It is the user's responsibility to empty
|
|
* the vector before calling this function; the new pointers will simply be
|
|
* added to the end.
|
|
*/
|
|
void InterfaceMaker::
|
|
get_function_remaps(std::vector<FunctionRemap *> &remaps) {
|
|
FunctionsByIndex::iterator fi;
|
|
for (fi = _functions.begin(); fi != _functions.end(); ++fi) {
|
|
Function *func = (*fi).second;
|
|
Function::Remaps::const_iterator ri;
|
|
for (ri = func->_remaps.begin(); ri != func->_remaps.end(); ++ri) {
|
|
FunctionRemap *remap = (*ri);
|
|
remaps.push_back(remap);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
*
|
|
*/
|
|
ostream &InterfaceMaker::
|
|
indent(ostream &out, int indent_level) {
|
|
for (int i = 0; i < indent_level; i++) {
|
|
out << ' ';
|
|
}
|
|
return out;
|
|
}
|
|
|
|
/**
|
|
* Creates a FunctionRemap object corresponding to the particular function
|
|
* wrapper.
|
|
*/
|
|
FunctionRemap *InterfaceMaker::
|
|
make_function_remap(const InterrogateType &itype,
|
|
const InterrogateFunction &ifunc,
|
|
CPPInstance *cppfunc, int num_default_parameters) {
|
|
FunctionRemap *remap =
|
|
new FunctionRemap(itype, ifunc, cppfunc, num_default_parameters, this);
|
|
if (remap->_is_valid) {
|
|
if (separate_overloading()) {
|
|
hash_function_signature(remap);
|
|
remap->_unique_name =
|
|
get_unique_prefix() + _def->library_hash_name + remap->_hash;
|
|
remap->_wrapper_name =
|
|
get_wrapper_prefix() + _def->library_hash_name + remap->_hash;
|
|
remap->_reported_name = remap->_wrapper_name;
|
|
|
|
if (true_wrapper_names) {
|
|
remap->_reported_name =
|
|
InterrogateBuilder::clean_identifier(remap->_cppfunc->get_local_name(&parser));
|
|
}
|
|
}
|
|
return remap;
|
|
}
|
|
|
|
// No such FunctionRemap is valid. Return NULL.
|
|
delete remap;
|
|
return nullptr;
|
|
}
|
|
|
|
/**
|
|
* Returns the function name that will be used to wrap the indicated function.
|
|
*
|
|
* This is the name for the overall wrapper function, including all of the
|
|
* overloaded instances. Interfaces that must define a different wrapper for
|
|
* each FunctionRemap object (i.e. for each instance of an overloaded
|
|
* function) need not define a name here.
|
|
*/
|
|
string InterfaceMaker::
|
|
get_wrapper_name(const InterrogateType &itype,
|
|
const InterrogateFunction &ifunc,
|
|
FunctionIndex func_index) {
|
|
string func_name = ifunc.get_scoped_name();
|
|
string clean_name = InterrogateBuilder::clean_identifier(func_name);
|
|
|
|
ostringstream new_name;
|
|
new_name << get_wrapper_prefix() << clean_name << "_" << func_index;
|
|
return new_name.str();
|
|
}
|
|
|
|
/**
|
|
* Returns the prefix string used to generate wrapper function names.
|
|
*/
|
|
string InterfaceMaker::
|
|
get_wrapper_prefix() {
|
|
return "xx_";
|
|
}
|
|
|
|
/**
|
|
* Returns the prefix string used to generate unique symbolic names, which are
|
|
* not necessarily C-callable function names.
|
|
*/
|
|
string InterfaceMaker::
|
|
get_unique_prefix() {
|
|
return "x";
|
|
}
|
|
|
|
/**
|
|
* Records the indicated function, along with all of its FunctionRemap flavors
|
|
* and FunctionWriter helpers, for future output. Returns the new Function
|
|
* pointer.
|
|
*/
|
|
InterfaceMaker::Function *InterfaceMaker::
|
|
record_function(const InterrogateType &itype, FunctionIndex func_index) {
|
|
assert(func_index != 0);
|
|
|
|
if (_functions.count(func_index)) {
|
|
// Already exists.
|
|
return _functions[func_index];
|
|
}
|
|
|
|
InterrogateDatabase *idb = InterrogateDatabase::get_ptr();
|
|
const InterrogateFunction &ifunc = idb->get_function(func_index);
|
|
|
|
string wrapper_name = get_wrapper_name(itype, ifunc, func_index);
|
|
Function *func = new Function(wrapper_name, itype, ifunc);
|
|
_functions[func_index] = func;
|
|
|
|
// printf(" Function Name = %s\n", ifunc.get_name().c_str());
|
|
|
|
// Now get all the valid FunctionRemaps for the function.
|
|
if (ifunc._instances != nullptr) {
|
|
InterrogateFunction::Instances::const_iterator ii;
|
|
for (ii = ifunc._instances->begin(); ii != ifunc._instances->end(); ++ii) {
|
|
CPPInstance *cppfunc = (*ii).second;
|
|
CPPFunctionType *ftype = cppfunc->_type->as_function_type();
|
|
int max_default_parameters = 0;
|
|
|
|
if (separate_overloading()) {
|
|
// Count up the number of default parameters this function might take.
|
|
CPPParameterList *parameters = ftype->_parameters;
|
|
CPPParameterList::Parameters::reverse_iterator pi;
|
|
for (pi = parameters->_parameters.rbegin();
|
|
pi != parameters->_parameters.rend();
|
|
++pi) {
|
|
CPPInstance *param = (*pi);
|
|
if (param->_initializer != nullptr) {
|
|
// This parameter has a default value.
|
|
max_default_parameters++;
|
|
} else {
|
|
// The first parameter without a default value ends the search.
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Now make a different wrapper for each combination of default
|
|
// parameters. This will happen only if separate_overloading(), tested
|
|
// above, returned true; otherwise, max_default_parameters will be 0 and
|
|
// the loop will only be traversed once.
|
|
for (int num_default_parameters = 0;
|
|
num_default_parameters <= max_default_parameters;
|
|
num_default_parameters++) {
|
|
FunctionRemap *remap =
|
|
make_function_remap(itype, ifunc, cppfunc, num_default_parameters);
|
|
if (remap != nullptr) {
|
|
|
|
func->_remaps.push_back(remap);
|
|
|
|
// If *any* of the variants of this function has a "this" pointer,
|
|
// the entire set of functions is deemed to have a "this" pointer.
|
|
if (remap->_has_this || (remap->_flags & FunctionRemap::F_explicit_self) != 0) {
|
|
func->_has_this = true;
|
|
}
|
|
|
|
func->_flags |= remap->_flags;
|
|
func->_args_type = (ArgsType)((int)func->_args_type | (int)remap->_args_type);
|
|
|
|
// Make a wrapper for the function.
|
|
FunctionWrapperIndex wrapper_index =
|
|
remap->make_wrapper_entry(func_index);
|
|
if (wrapper_index != 0) {
|
|
InterrogateFunction &mod_ifunc = idb->update_function(func_index);
|
|
record_function_wrapper(mod_ifunc, wrapper_index);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return func;
|
|
}
|
|
|
|
/**
|
|
* Associates the function wrapper with its function in the appropriate
|
|
* structures in the database.
|
|
*/
|
|
void InterfaceMaker::
|
|
record_function_wrapper(InterrogateFunction &, FunctionWrapperIndex) {
|
|
}
|
|
|
|
/**
|
|
* Records the indicated type, which may be a struct type, along with all of
|
|
* its associated methods, if any.
|
|
*/
|
|
InterfaceMaker::Object *InterfaceMaker::
|
|
record_object(TypeIndex type_index) {
|
|
if (type_index == 0) {
|
|
// An invalid type.
|
|
return nullptr;
|
|
}
|
|
|
|
Objects::iterator oi = _objects.find(type_index);
|
|
if (oi != _objects.end()) {
|
|
// The object has previously been recorded.
|
|
return (*oi).second;
|
|
}
|
|
|
|
InterrogateDatabase *idb = InterrogateDatabase::get_ptr();
|
|
const InterrogateType &itype = idb->get_type(type_index);
|
|
|
|
Object *object = new Object(itype);
|
|
bool inserted = _objects.insert(Objects::value_type(type_index, object)).second;
|
|
assert(inserted);
|
|
|
|
Function *function;
|
|
|
|
int num_constructors = itype.number_of_constructors();
|
|
for (int ci = 0; ci < num_constructors; ci++) {
|
|
function = record_function(itype, itype.get_constructor(ci));
|
|
object->_constructors.push_back(function);
|
|
}
|
|
|
|
int num_methods = itype.number_of_methods();
|
|
int mi;
|
|
for (mi = 0; mi < num_methods; mi++) {
|
|
function = record_function(itype, itype.get_method(mi));
|
|
object->_methods.push_back(function);
|
|
}
|
|
|
|
int num_casts = itype.number_of_casts();
|
|
for (mi = 0; mi < num_casts; mi++) {
|
|
function = record_function(itype, itype.get_cast(mi));
|
|
object->_methods.push_back(function);
|
|
}
|
|
|
|
int num_derivations = itype.number_of_derivations();
|
|
for (int di = 0; di < num_derivations; di++) {
|
|
if (itype.derivation_has_upcast(di)) {
|
|
record_function(itype, itype.derivation_get_upcast(di));
|
|
}
|
|
if (itype.derivation_has_downcast(di)) {
|
|
// Downcasts are methods of the base class, not the child class.
|
|
TypeIndex base_type_index = itype.get_derivation(di);
|
|
const InterrogateType &base_type = idb->get_type(base_type_index);
|
|
record_function(base_type, itype.derivation_get_downcast(di));
|
|
}
|
|
}
|
|
|
|
int num_elements = itype.number_of_elements();
|
|
for (int ei = 0; ei < num_elements; ei++) {
|
|
ElementIndex element_index = itype.get_element(ei);
|
|
const InterrogateElement &ielement = idb->get_element(element_index);
|
|
if (ielement.has_getter()) {
|
|
FunctionIndex func_index = ielement.get_getter();
|
|
record_function(itype, func_index);
|
|
}
|
|
if (ielement.has_setter()) {
|
|
FunctionIndex func_index = ielement.get_setter();
|
|
record_function(itype, func_index);
|
|
}
|
|
}
|
|
|
|
object->check_protocols();
|
|
|
|
int num_nested = itype.number_of_nested_types();
|
|
for (int ni = 0; ni < num_nested; ni++) {
|
|
TypeIndex nested_index = itype.get_nested_type(ni);
|
|
record_object(nested_index);
|
|
}
|
|
|
|
return object;
|
|
}
|
|
|
|
/**
|
|
* Does any additional processing that we might want to do on the return value
|
|
* for the function, just before we return it. Returns the string
|
|
* representing the new return value after processing.
|
|
*/
|
|
string InterfaceMaker::
|
|
manage_return_value(ostream &out, int indent_level,
|
|
FunctionRemap *remap, const string &return_expr) const {
|
|
if (remap->_manage_reference_count) {
|
|
// If we're managing reference counts, and we're about to return a
|
|
// reference countable object, then increment its count.
|
|
if (return_expr == "return_value") {
|
|
// If the expression is just a variable name, we can just ref it
|
|
// directly.
|
|
output_ref(out, indent_level, remap, return_expr);
|
|
return return_expr;
|
|
|
|
} else {
|
|
// Otherwise, we should probably assign it to a temporary first, so we
|
|
// don't invoke the function twice or something.
|
|
CPPType *type = remap->_return_type->get_temporary_type();
|
|
indent(out, indent_level);
|
|
type->output_instance(out, "refcount", &parser);
|
|
out << " = " << return_expr << ";\n";
|
|
|
|
indent(out, indent_level)
|
|
<< "if (" << return_expr << " != nullptr) {\n";
|
|
indent(out, indent_level + 2)
|
|
<< "(" << return_expr << ")->ref();\n";
|
|
indent(out, indent_level)
|
|
<< "}\n";
|
|
output_ref(out, indent_level, remap, "refcount");
|
|
return remap->_return_type->temporary_to_return("refcount");
|
|
}
|
|
}
|
|
|
|
// Otherwise, just return the expression unchanged.
|
|
return return_expr;
|
|
}
|
|
|
|
/**
|
|
* Cleans up the given return value by deleting it or decrementing its
|
|
* reference count or whatever is appropriate.
|
|
*/
|
|
void InterfaceMaker::
|
|
delete_return_value(ostream &out, int indent_level,
|
|
FunctionRemap *remap, const string &return_expr) const {
|
|
if (remap->_manage_reference_count) {
|
|
// If we're managing reference counts, and we're about to return a
|
|
// reference countable object, then decrement its count.
|
|
output_unref(out, indent_level, remap, return_expr);
|
|
|
|
} else if (remap->_return_value_needs_management) {
|
|
// We should just delete it directly.
|
|
indent(out, indent_level) << "delete " << return_expr << ";\n";
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Outputs the code to increment the reference count for the indicated
|
|
* variable name.
|
|
*/
|
|
void InterfaceMaker::
|
|
output_ref(ostream &out, int indent_level, FunctionRemap *remap,
|
|
const string &varname) const {
|
|
|
|
if (TypeManager::is_pointer_to_base(remap->_return_type->get_temporary_type())) {
|
|
// Actually, we have it stored in a PointerTo. No need to do anything.
|
|
return;
|
|
}
|
|
|
|
if (remap->_type == FunctionRemap::T_constructor ||
|
|
remap->_type == FunctionRemap::T_typecast) {
|
|
// In either of these cases, we can safely assume the pointer will never
|
|
// be NULL.
|
|
indent(out, indent_level)
|
|
<< varname << "->ref();\n";
|
|
|
|
} else {
|
|
// However, in the general case, we have to check for that before we
|
|
// attempt to ref it.
|
|
|
|
indent(out, indent_level)
|
|
<< "if (" << varname << " != nullptr) {\n";
|
|
indent(out, indent_level + 2)
|
|
<< varname << "->ref();\n";
|
|
indent(out, indent_level)
|
|
<< "}\n";
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Outputs the code to decrement the reference count for the indicated
|
|
* variable name.
|
|
*/
|
|
void InterfaceMaker::
|
|
output_unref(ostream &out, int indent_level, FunctionRemap *remap,
|
|
const string &varname) const {
|
|
|
|
if (TypeManager::is_pointer_to_base(remap->_return_type->get_temporary_type())) {
|
|
// Actually, we have it stored in a PointerTo. No need to do anything.
|
|
return;
|
|
}
|
|
|
|
if (remap->_type == FunctionRemap::T_constructor ||
|
|
remap->_type == FunctionRemap::T_typecast) {
|
|
// In either of these cases, we can safely assume the pointer will never
|
|
// be NULL.
|
|
indent(out, indent_level)
|
|
<< "unref_delete(" << varname << ");\n";
|
|
|
|
} else {
|
|
// However, in the general case, we have to check for that before we
|
|
// attempt to ref it.
|
|
|
|
indent(out, indent_level)
|
|
<< "if (" << varname << " != nullptr) {\n";
|
|
|
|
if (TypeManager::is_pointer_to_base(remap->_return_type->get_temporary_type())) {
|
|
// We're sure the reference count won't reach zero since we have it
|
|
// stored in a PointerTo, so call the unref() method directly.
|
|
indent(out, indent_level + 2)
|
|
<< varname << "->unref();\n";
|
|
} else {
|
|
indent(out, indent_level + 2)
|
|
<< "unref_delete(" << varname << ");\n";
|
|
}
|
|
|
|
indent(out, indent_level)
|
|
<< "}\n";
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Generates a unique string that corresponds to the function signature for
|
|
* the indicated FunctionRemap object, and stores the generated string in the
|
|
* _hash member of the FunctionRemap.
|
|
*/
|
|
void InterfaceMaker::
|
|
hash_function_signature(FunctionRemap *remap) {
|
|
string hash = InterrogateBuilder::hash_string(remap->_function_signature, 5);
|
|
|
|
// Now make sure we don't have another function with the same hash.
|
|
WrappersByHash::iterator hi;
|
|
hi = _wrappers_by_hash.find(hash);
|
|
if (hi == _wrappers_by_hash.end()) {
|
|
// No other name; we're in the clear.
|
|
_wrappers_by_hash[hash] = remap;
|
|
remap->_hash = hash;
|
|
return;
|
|
}
|
|
|
|
if ((*hi).second != nullptr &&
|
|
(*hi).second->_function_signature == remap->_function_signature) {
|
|
// The same function signature has already appeared. This shouldn't
|
|
// happen.
|
|
nout << "Internal error! Function signature "
|
|
<< remap->_function_signature << " repeated!\n";
|
|
remap->_hash = hash;
|
|
abort();
|
|
return;
|
|
}
|
|
|
|
// We have a conflict. Extend both strings to resolve the ambiguity.
|
|
if ((*hi).second != nullptr) {
|
|
FunctionRemap *other_remap = (*hi).second;
|
|
(*hi).second = nullptr;
|
|
other_remap->_hash +=
|
|
InterrogateBuilder::hash_string(other_remap->_function_signature, 11);
|
|
bool inserted = _wrappers_by_hash.insert
|
|
(WrappersByHash::value_type(other_remap->_hash, other_remap)).second;
|
|
if (!inserted) {
|
|
nout << "Internal error! Hash " << other_remap->_hash
|
|
<< " already appears!\n";
|
|
}
|
|
}
|
|
|
|
hash += InterrogateBuilder::hash_string(remap->_function_signature, 11);
|
|
bool inserted = _wrappers_by_hash.insert
|
|
(WrappersByHash::value_type(hash, remap)).second;
|
|
|
|
if (!inserted) {
|
|
// Huh. We still have a conflict. This should be extremely rare. Well,
|
|
// just tack on a letter until it's resolved.
|
|
string old_hash = hash;
|
|
for (char ch = 'a'; ch <= 'z' && !inserted; ch++) {
|
|
hash = old_hash + ch;
|
|
inserted = _wrappers_by_hash.insert
|
|
(WrappersByHash::value_type(hash, remap)).second;
|
|
}
|
|
if (!inserted) {
|
|
nout << "Internal error! Too many conflicts with hash "
|
|
<< hash << "\n";
|
|
}
|
|
}
|
|
|
|
remap->_hash = hash;
|
|
}
|
|
|
|
/**
|
|
* Generates a string to output a spammy message to notify indicating we have
|
|
* just called this function.
|
|
*/
|
|
void InterfaceMaker::
|
|
write_spam_message(ostream &out, FunctionRemap *remap) const {
|
|
ostringstream strm;
|
|
remap->write_orig_prototype(strm, 0);
|
|
string prototype = strm.str();
|
|
|
|
out <<
|
|
" if (interrogatedb_cat.is_spam()) {\n"
|
|
" interrogatedb_cat.spam() << \"";
|
|
|
|
for (string::const_iterator si = prototype.begin();
|
|
si != prototype.end();
|
|
++si) {
|
|
switch (*si) {
|
|
case '"':
|
|
out << "\\\"";
|
|
break;
|
|
|
|
case '\\':
|
|
out << "\\\\";
|
|
break;
|
|
|
|
default:
|
|
out << *si;
|
|
}
|
|
}
|
|
|
|
out << "\\n\";\n"
|
|
" }\n";
|
|
}
|