| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595 |
- // 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
- // [email protected] .
- //
- ////////////////////////////////////////////////////////////////////
- #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_maker) {
- _return_type = (ParameterRemap *)NULL;
- _void_return = true;
- _ForcedVoidReturn = false;
- _has_this = false;
- _blocking = false;
- _const_method = 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_maker);
- }
- ////////////////////////////////////////////////////////////////////
- // 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_maker) {
- _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_blocking) != 0)) {
- // If it's marked as a "blocking" method or function, record that.
- _blocking = true;
- }
- 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;
- _const_method = (_ftype->_flags & CPPFunctionType::F_const_method) != 0;
- if (interface_maker->synthesize_this_parameter()) {
- // If the interface_maker 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;
- param._remap = new ParameterRemapThis(_cpptype, _const_method);
- _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_maker->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_maker->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_maker->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_maker->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_maker->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();
- return_type = TypeManager::resolve_type(return_type, _cppscope);
- 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;
- }
|