// Filename: functionRemap.cxx // Created by: drose (19Sep01) // //////////////////////////////////////////////////////////////////// // // PANDA 3D SOFTWARE // Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved // // All use of this software is subject to the terms of the Panda 3d // Software license. You should have received a copy of this license // along with this source code; you will also find a current copy of // the license at http://etc.cmu.edu/panda3d/docs/license/ . // // To contact the maintainers of this program write to // panda3d-general@lists.sourceforge.net . // //////////////////////////////////////////////////////////////////// #include "functionRemap.h" #include "typeManager.h" #include "interrogate.h" #include "parameterRemap.h" #include "parameterRemapThis.h" #include "interfaceMaker.h" #include "interrogateBuilder.h" #include "interrogateDatabase.h" #include "cppInstance.h" #include "cppFunctionType.h" #include "cppParameterList.h" #include "cppReferenceType.h" #include "interrogateType.h" #include "pnotify.h" extern bool inside_python_native; //////////////////////////////////////////////////////////////////// // Function: FunctionRemap::Constructor // Access: Public // Description: //////////////////////////////////////////////////////////////////// FunctionRemap:: FunctionRemap(const InterrogateType &itype, const InterrogateFunction &ifunc, CPPInstance *cppfunc, int num_default_parameters, InterfaceMaker *interface) { _return_type = (ParameterRemap *)NULL; _void_return = true; _ForcedVoidReturn = false; _has_this = false; _first_true_parameter = 0; _num_default_parameters = num_default_parameters; _type = T_normal; _wrapper_index = 0; _return_value_needs_management = false; _return_value_destructor = 0; _manage_reference_count = false; _cppfunc = cppfunc; _ftype = _cppfunc->_type->as_function_type(); _cpptype = itype._cpptype; _cppscope = itype._cppscope; _is_valid = setup_properties(ifunc, interface); } //////////////////////////////////////////////////////////////////// // Function: FunctionRemap::Destructor // Access: Public // Description: //////////////////////////////////////////////////////////////////// FunctionRemap:: ~FunctionRemap() { } //////////////////////////////////////////////////////////////////// // Function: FunctionRemap::get_parameter_name // Access: Public // Description: Returns a string that will be a suitable name for the // nth parameter in the generated code. This may not // correspond to the name of the parameter in the // original code. //////////////////////////////////////////////////////////////////// string FunctionRemap:: get_parameter_name(int n) const { ostringstream str; str << "param" << n; return str.str(); } //////////////////////////////////////////////////////////////////// // Function: FunctionRemap::call_function // Access: Public // Description: Writes a sequence of commands to the given output // stream to call the wrapped function. The parameter // values are taken from pexprs, if it is nonempty, or // are assumed to be simply the names of the parameters, // if it is empty. // // The return value is the expression to return, if we // are returning a value, or the empty string if we // return nothing. //////////////////////////////////////////////////////////////////// string FunctionRemap::call_function(ostream &out, int indent_level, bool convert_result, const string &container, const vector_string &pexprs) const { string return_expr; if (_type == T_destructor) { // A destructor wrapper is just a wrapper around the delete operator. assert(!container.empty()); assert(_cpptype != (CPPType *)NULL); if (TypeManager::is_reference_count(_cpptype)) { // Except for a reference-count type object, in which case the // destructor is a wrapper around unref_delete(). InterfaceMaker::indent(out, indent_level) << "unref_delete(" << container << ");\n"; } else { if(inside_python_native) InterfaceMaker::indent(out, indent_level) << "Dtool_Py_Delete(self); \n"; else InterfaceMaker::indent(out, indent_level) << " delete " << container << ";\n"; } } else if (_type == T_typecast_method) { // A typecast method can be invoked implicitly. string cast_expr = "(" + _return_type->get_orig_type()->get_local_name(&parser) + ")(*" + container + ")"; if (!convert_result) { return_expr = cast_expr; } else { string new_str = _return_type->prepare_return_expr(out, indent_level, cast_expr); return_expr = _return_type->get_return_expr(new_str); } } else if (_type == T_typecast) { // A regular typecast converts from a pointer type to another // pointer type. (This is different from the typecast method, // above, which converts from the concrete type to some other // type.) assert(!container.empty()); string cast_expr = "(" + _return_type->get_orig_type()->get_local_name(&parser) + ")" + container; if (!convert_result) { return_expr = cast_expr; } else { string new_str = _return_type->prepare_return_expr(out, indent_level, cast_expr); return_expr = _return_type->get_return_expr(new_str); } } else if (_type == T_constructor) { // A special case for constructors. return_expr = "new " + get_call_str(container, pexprs); if (_void_return) { nout << "Error, constructor for " << *_cpptype << " returning void.\n"; return_expr = ""; } } else if (_type == T_assignment_method) { // Another special case for assignment operators. assert(!container.empty()); InterfaceMaker::indent(out, indent_level) << get_call_str(container, pexprs) << ";\n"; string this_expr = container; string ref_expr = "*" + this_expr; if (!convert_result) { return_expr = ref_expr; } else { string new_str = _return_type->prepare_return_expr(out, indent_level, ref_expr); return_expr = _return_type->get_return_expr(new_str); // Now a simple special-case test. Often, we will have converted // the reference-returning assignment operator to a pointer. In // this case, we might inadventent generate code like "return // &(*this)", when "return this" would do. We check for this here // and undo it as a special case. // There's no real good reason to do this, other than that it // feels more satisfying to a casual perusal of the generated // code. It *is* conceivable that some broken compilers wouldn't // like "&(*this)", though. if (return_expr == "&(" + ref_expr + ")" || return_expr == "&" + ref_expr) { return_expr = this_expr; } } } else if (_void_return) { InterfaceMaker::indent(out, indent_level) << get_call_str(container, pexprs) << ";\n"; } else { string call = get_call_str(container, pexprs); if (!convert_result) { return_expr = get_call_str(container, pexprs); } else { if (_return_type->return_value_should_be_simple()) { // We have to assign the result to a temporary first; this makes // it a bit easier on poor old VC++. InterfaceMaker::indent(out, indent_level); _return_type->get_orig_type()->output_instance(out, "result", &parser); out << " = " << call << ";\n"; string new_str = _return_type->prepare_return_expr(out, indent_level, "result"); return_expr = _return_type->get_return_expr(new_str); } else { // This should be simple enough that we can return it directly. string new_str = _return_type->prepare_return_expr(out, indent_level, call); return_expr = _return_type->get_return_expr(new_str); } } } return return_expr; } //////////////////////////////////////////////////////////////////// // Function: FunctionRemap::write_orig_prototype // Access: Public // Description: Writes a line describing the original C++ method or // function. This is generally useful only within a // comment. //////////////////////////////////////////////////////////////////// void FunctionRemap:: write_orig_prototype(ostream &out, int indent_level) const { _cppfunc->output(out, indent_level, &parser, false, _num_default_parameters); } //////////////////////////////////////////////////////////////////// // Function: FunctionRemap::make_wrapper_entry // Access: Public // Description: Creates an InterrogateFunctionWrapper object // corresponding to this callable instance and stores it // in the database. //////////////////////////////////////////////////////////////////// FunctionWrapperIndex FunctionRemap:: make_wrapper_entry(FunctionIndex function_index) { _wrapper_index = InterrogateDatabase::get_ptr()->get_next_index(); InterrogateFunctionWrapper iwrapper; iwrapper._function = function_index; iwrapper._name = _wrapper_name; iwrapper._unique_name = _unique_name; if (output_function_names) { // If we're keeping the function names, record that the wrapper is // callable. iwrapper._flags |= InterrogateFunctionWrapper::F_callable_by_name; } Parameters::const_iterator pi; for (pi = _parameters.begin(); pi != _parameters.end(); ++pi) { InterrogateFunctionWrapper::Parameter param; param._parameter_flags = 0; if ((*pi)._remap->new_type_is_atomic_string()) { param._type = builder.get_atomic_string_type(); } else { param._type = builder.get_type((*pi)._remap->get_new_type(), false); } param._name = (*pi)._name; if ((*pi)._has_name) { param._parameter_flags |= InterrogateFunctionWrapper::PF_has_name; } iwrapper._parameters.push_back(param); } if (_has_this) { // If one of the parameters is "this", it must be the first one. assert(!iwrapper._parameters.empty()); iwrapper._parameters.front()._parameter_flags |= InterrogateFunctionWrapper::PF_is_this; } if (!_void_return) { iwrapper._flags |= InterrogateFunctionWrapper::F_has_return; } if (_return_type->new_type_is_atomic_string()) { iwrapper._return_type = builder.get_atomic_string_type(); } else { iwrapper._return_type = builder.get_type(_return_type->get_new_type(), false); } if (_return_value_needs_management) { iwrapper._flags |= InterrogateFunctionWrapper::F_caller_manages; FunctionIndex destructor = _return_value_destructor; if (destructor != 0) { iwrapper._return_value_destructor = destructor; } else { // We don't need to report this warning, since the FFI code // understands that if the destructor function is zero, it // should use the regular class destructor. // nout << "Warning! Destructor for " // << *_return_type->get_orig_type() // << " is unavailable.\n" // << " Cannot manage return value for:\n " // << description << "\n"; } } InterrogateDatabase::get_ptr()->add_wrapper(_wrapper_index, iwrapper); return _wrapper_index; } //////////////////////////////////////////////////////////////////// // Function: FunctionRemap::get_call_str // Access: Private // Description: Returns a string suitable for calling the wrapped // function. If pexprs is nonempty, it represents // the list of expressions that will evaluate to each // parameter value. //////////////////////////////////////////////////////////////////// string FunctionRemap:: get_call_str(const string &container, const vector_string &pexprs) const { // Build up the call to the actual function. ostringstream call; // Getters and setters are a special case. if (_type == T_getter) { if (!container.empty()) { call << "(" << container << ")->" << _expression; } else { call << _expression; } } else if (_type == T_setter) { if (!container.empty()) { call << "(" << container << ")->" << _expression; } else { call << _expression; } call << " = "; _parameters[0]._remap->pass_parameter(call, get_parameter_expr(_first_true_parameter, pexprs)); } else { if (_type == T_constructor) { // Constructors are called differently. call << _cpptype->get_local_name(&parser); } else if (_has_this && !container.empty()) { // If we have a "this" parameter, the calling convention is also // a bit different. call << "(" << container << ")->" << _cppfunc->get_local_name(); } else { call << _cppfunc->get_local_name(&parser); } call << "("; int pn = _first_true_parameter; if (pn < (int)_parameters.size()) { _parameters[pn]._remap->pass_parameter(call, get_parameter_expr(pn, pexprs)); pn++; while (pn < (int)_parameters.size()) { call << ", "; _parameters[pn]._remap->pass_parameter(call, get_parameter_expr(pn, pexprs)); pn++; } } call << ")"; } return call.str(); } //////////////////////////////////////////////////////////////////// // Function: FunctionRemap::get_parameter_expr // Access: Private // Description: Returns a string that represents the expression // associated with the nth parameter. This is just the // nth element of pexprs if it is nonempty, or the name // of the nth parameter is it is empty. //////////////////////////////////////////////////////////////////// string FunctionRemap:: get_parameter_expr(int n, const vector_string &pexprs) const { if (n < (int)pexprs.size()) { return pexprs[n]; } return get_parameter_name(n); } //////////////////////////////////////////////////////////////////// // Function: FunctionRemap::setup_properties // Access: Private // Description: Sets up the properties of the function appropriately. // Returns true if successful, or false if there is // something unacceptable about the function. //////////////////////////////////////////////////////////////////// bool FunctionRemap:: setup_properties(const InterrogateFunction &ifunc, InterfaceMaker *interface) { _function_signature = TypeManager::get_function_signature(_cppfunc, _num_default_parameters); _expression = ifunc._expression; if ((_ftype->_flags & CPPFunctionType::F_constructor) != 0) { _type = T_constructor; } else if ((_ftype->_flags & CPPFunctionType::F_destructor) != 0) { _type = T_destructor; } else if ((_ftype->_flags & CPPFunctionType::F_operator_typecast) != 0) { _type = T_typecast_method; } else if ((ifunc._flags & InterrogateFunction::F_typecast) != 0) { _type = T_typecast; } else if ((ifunc._flags & InterrogateFunction::F_getter) != 0) { _type = T_getter; } else if ((ifunc._flags & InterrogateFunction::F_setter) != 0) { _type = T_setter; } if (_cpptype != (CPPType *)NULL && ((_cppfunc->_storage_class & CPPInstance::SC_static) == 0) && _type != T_constructor) { // If this is a method, but not a static method, and not a // constructor, then we need a "this" parameter. _has_this = true; if (interface->synthesize_this_parameter()) { // If the interface demands it, the "this" parameter is treated // as any other parameter, and inserted at the beginning of the // parameter list. Parameter param; param._name = "this"; param._has_name = true; bool is_const = (_ftype->_flags & CPPFunctionType::F_const_method) != 0; param._remap = new ParameterRemapThis(_cpptype, is_const); _parameters.push_back(param); _first_true_parameter = 1; } // Also check the name of the function. If it's one of the // assignment-style operators, flag it as such. string fname = _cppfunc->get_simple_name(); if (fname == "operator =" || fname == "operator *=" || fname == "operator /=" || fname == "operator %=" || fname == "operator +=" || fname == "operator -=" || fname == "operator |=" || fname == "operator &=" || fname == "operator ^=" || fname == "operator <<=" || fname == "operator >>=") { _type = T_assignment_method; } } const CPPParameterList::Parameters ¶ms = _ftype->_parameters->_parameters; for (int i = 0; i < (int)params.size() - _num_default_parameters; i++) { CPPType *type = params[i]->_type->resolve_type(&parser, _cppscope); Parameter param; param._has_name = true; param._name = params[i]->get_simple_name(); if (param._name.empty()) { // If the parameter has no name, record it as being nameless, // but also synthesize one in case someone asks anyway. param._has_name = false; ostringstream param_name; param_name << "param" << i; param._name = param_name.str(); } param._remap = interface->remap_parameter(_cpptype, type); if (param._remap == (ParameterRemap *)NULL) { // If we can't handle one of the parameter types, we can't call // the function. return false; } param._remap->set_default_value(params[i]->_initializer); if (!param._remap->is_valid()) { return false; } _parameters.push_back(param); } if (_type == T_constructor) { // Constructors are a special case. These appear to return void // as seen by the parser, but we know they actually return a new // concrete instance. if (_cpptype == (CPPType *)NULL) { nout << "Method " << *_cppfunc << " has no struct type\n"; return false; } _return_type = interface->remap_parameter(_cpptype, _cpptype); if (_return_type != (ParameterRemap *)NULL) { _void_return = false; } } else if (_type == T_assignment_method) { // Assignment-type methods are also a special case. We munge // these to return *this, which is a semi-standard C++ convention // anyway. We just enforce it. if (_cpptype == (CPPType *)NULL) { nout << "Method " << *_cppfunc << " has no struct type\n"; return false; } else { CPPType *ref_type = CPPType::new_type(new CPPReferenceType(_cpptype)); _return_type = interface->remap_parameter(_cpptype, ref_type); if (_return_type != (ParameterRemap *)NULL) { _void_return = false; } } } else { // The normal case. CPPType *rtype = _ftype->_return_type->resolve_type(&parser, _cppscope); _return_type = interface->remap_parameter(_cpptype, rtype); if (_return_type != (ParameterRemap *)NULL) { _void_return = TypeManager::is_void(rtype); } } if (_return_type == (ParameterRemap *)NULL || !_return_type->is_valid()) { // If our return type isn't something we can deal with, treat the // function as if it returns NULL. _void_return = true; _ForcedVoidReturn = true; CPPType *void_type = TypeManager::get_void_type(); _return_type = interface->remap_parameter(_cpptype, void_type); assert(_return_type != (ParameterRemap *)NULL); } // Do we need to manage the return value? _return_value_needs_management = _return_type->return_value_needs_management(); _return_value_destructor = _return_type->get_return_value_destructor(); // Should we manage a reference count? CPPType *return_type = _return_type->get_new_type(); CPPType *return_meat_type = TypeManager::unwrap_pointer(return_type); if (manage_reference_counts && TypeManager::is_reference_count_pointer(return_type) && !TypeManager::has_protected_destructor(return_meat_type)) { // Yes! _manage_reference_count = true; _return_value_needs_management = true; // This is problematic, because we might not have the class in // question fully defined here, particularly if the class is // defined in some other library. _return_value_destructor = builder.get_destructor_for(return_meat_type); } return true; }