From b3fa4b906ccfdc006752152e8472d40eafe9756d Mon Sep 17 00:00:00 2001 From: Dave Schuyler Date: Fri, 27 May 2005 00:25:19 +0000 Subject: [PATCH] python 2.4 stuff --- direct/src/dcparser/dcPacker.cxx | 2724 +++++++++-------- .../src/distributed/cConnectionRepository.cxx | 1064 +++---- .../interrogate/interfaceMakerPythonObj.cxx | 1287 ++++---- .../interfaceMakerPythonSimple.cxx | 981 +++--- 4 files changed, 3048 insertions(+), 3008 deletions(-) diff --git a/direct/src/dcparser/dcPacker.cxx b/direct/src/dcparser/dcPacker.cxx index f8130f1b1a..5ddc55ff5e 100755 --- a/direct/src/dcparser/dcPacker.cxx +++ b/direct/src/dcparser/dcPacker.cxx @@ -1,1359 +1,1365 @@ -// Filename: dcPacker.cxx -// Created by: drose (15Jun04) -// -//////////////////////////////////////////////////////////////////// -// -// 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 "dcPacker.h" -#include "dcSwitch.h" -#include "dcParserDefs.h" -#include "dcLexerDefs.h" -#include "dcClassParameter.h" -#include "dcSwitchParameter.h" -#include "dcClass.h" - -DCPacker::StackElement *DCPacker::StackElement::_deleted_chain = NULL; -int DCPacker::StackElement::_num_ever_allocated = 0; - -//////////////////////////////////////////////////////////////////// -// Function: DCPacker::Constructor -// Access: Published -// Description: -//////////////////////////////////////////////////////////////////// -DCPacker:: -DCPacker() { - _mode = M_idle; - _unpack_data = NULL; - _unpack_length = 0; - _owns_unpack_data = false; - _unpack_p = 0; - _live_catalog = NULL; - _parse_error = false; - _pack_error = false; - _range_error = false; - _stack = NULL; - - clear(); -} - -//////////////////////////////////////////////////////////////////// -// Function: DCPacker::Destructor -// Access: Published -// Description: -//////////////////////////////////////////////////////////////////// -DCPacker:: -~DCPacker() { - clear_data(); - clear(); -} - -//////////////////////////////////////////////////////////////////// -// Function: DCPacker::begin_pack -// Access: Published -// Description: Begins a packing session. The parameter is the DC -// object that describes the packing format; it may be a -// DCParameter or DCField. -// -// Unless you call clear_data() between sessions, -// multiple packing sessions will be concatenated -// together into the same buffer. If you wish to add -// bytes to the buffer between packing sessions, use -// append_data() or get_write_pointer(). -//////////////////////////////////////////////////////////////////// -void DCPacker:: -begin_pack(const DCPackerInterface *root) { - nassertv(_mode == M_idle); - - _mode = M_pack; - _parse_error = false; - _pack_error = false; - _range_error = false; - - _root = root; - _catalog = NULL; - _live_catalog = NULL; - - _current_field = root; - _current_parent = NULL; - _current_field_index = 0; - _num_nested_fields = 0; -} - -//////////////////////////////////////////////////////////////////// -// Function: DCPacker::end_pack -// Access: Published, Virtual -// Description: Finishes a packing session. -// -// The return value is true on success, or false if -// there has been some error during packing. -//////////////////////////////////////////////////////////////////// -bool DCPacker:: -end_pack() { - nassertr(_mode == M_pack, false); - - _mode = M_idle; - - if (_stack != NULL || _current_field != NULL || _current_parent != NULL) { - _pack_error = true; - } - - clear(); - - return !had_error(); -} - -//////////////////////////////////////////////////////////////////// -// Function: DCPacker::set_unpack_data -// Access: Public -// Description: Sets up the unpack_data pointer. You may call this -// before calling the version of begin_unpack() that -// takes only one parameter. -//////////////////////////////////////////////////////////////////// -void DCPacker:: -set_unpack_data(const string &data) { - nassertv(_mode == M_idle); - - char *buffer = new char[data.length()]; - memcpy(buffer, data.data(), data.length()); - set_unpack_data(buffer, data.length(), true); -} - -//////////////////////////////////////////////////////////////////// -// Function: DCPacker::set_unpack_data -// Access: Public -// Description: Sets up the unpack_data pointer. You may call this -// before calling the version of begin_unpack() that -// takes only one parameter. -//////////////////////////////////////////////////////////////////// -void DCPacker:: -set_unpack_data(const char *unpack_data, size_t unpack_length, - bool owns_unpack_data) { - nassertv(_mode == M_idle); - - if (_owns_unpack_data) { - delete[] _unpack_data; - } - _unpack_data = unpack_data; - _unpack_length = unpack_length; - _owns_unpack_data = owns_unpack_data; - _unpack_p = 0; -} - -//////////////////////////////////////////////////////////////////// -// Function: DCPacker::begin_unpack -// Access: Public -// Description: Begins an unpacking session. You must have -// previously called set_unpack_data() to specify a -// buffer to unpack. -// -// If there was data left in the buffer after a previous -// begin_unpack() .. end_unpack() session, the new -// session will resume from the current point. This -// method may be used, therefore, to unpack a sequence -// of objects from the same buffer. -//////////////////////////////////////////////////////////////////// -void DCPacker:: -begin_unpack(const DCPackerInterface *root) { - nassertv(_mode == M_idle); - nassertv(_unpack_data != NULL); - - _mode = M_unpack; - _parse_error = false; - _pack_error = false; - _range_error = false; - - _root = root; - _catalog = NULL; - _live_catalog = NULL; - - _current_field = root; - _current_parent = NULL; - _current_field_index = 0; - _num_nested_fields = 0; -} - -//////////////////////////////////////////////////////////////////// -// Function: DCPacker::end_unpack -// Access: Published -// Description: Finishes the unpacking session. -// -// The return value is true on success, or false if -// there has been some error during unpacking (or if all -// fields have not been unpacked). -//////////////////////////////////////////////////////////////////// -bool DCPacker:: -end_unpack() { - nassertr(_mode == M_unpack, false); - - _mode = M_idle; - - if (_stack != NULL || _current_field != NULL || _current_parent != NULL) { - // This happens if we have not unpacked all of the fields. - // However, this is not an error if we have called seek() during - // the unpack session (in which case the _catalog will be - // non-NULL). On the other hand, if the catalog is still NULL, - // then we have never called seek() and it is an error not to - // unpack all values. - if (_catalog == (DCPackerCatalog *)NULL) { - _pack_error = true; - } - } - - clear(); - - return !had_error(); -} - -//////////////////////////////////////////////////////////////////// -// Function: DCPacker::begin_repack -// Access: Public -// Description: Begins a repacking session. You must have previously -// called set_unpack_data() to specify a buffer to -// unpack. -// -// Unlike begin_pack() or begin_unpack() you may not -// concatenate the results of multiple begin_repack() -// sessions in one buffer. -// -// Also, unlike in packing or unpacking modes, you may -// not walk through the fields from beginning to end, or -// even pack two consecutive fields at once. Instead, -// you must call seek() for each field you wish to -// modify and pack only that one field; then call seek() -// again to modify another field. -//////////////////////////////////////////////////////////////////// -void DCPacker:: -begin_repack(const DCPackerInterface *root) { - nassertv(_mode == M_idle); - nassertv(_unpack_data != NULL); - nassertv(_unpack_p == 0); - - _mode = M_repack; - _parse_error = false; - _pack_error = false; - _range_error = false; - _pack_data.clear(); - - // In repack mode, we immediately get the catalog, since we know - // we'll need it. - _root = root; - _catalog = _root->get_catalog(); - _live_catalog = _catalog->get_live_catalog(_unpack_data, _unpack_length); - if (_live_catalog == NULL) { - _pack_error = true; - } - - // We don't begin at the first field in repack mode. Instead, you - // must explicitly call seek(). - _current_field = NULL; - _current_parent = NULL; - _current_field_index = 0; - _num_nested_fields = 0; -} - -//////////////////////////////////////////////////////////////////// -// Function: DCPacker::end_repack -// Access: Published -// Description: Finishes the repacking session. -// -// The return value is true on success, or false if -// there has been some error during repacking (or if all -// fields have not been repacked). -//////////////////////////////////////////////////////////////////// -bool DCPacker:: -end_repack() { - nassertr(_mode == M_repack, false); - - // Put the rest of the data onto the pack stream. - _pack_data.append_data(_unpack_data + _unpack_p, _unpack_length - _unpack_p); - - _mode = M_idle; - clear(); - - return !had_error(); -} - -//////////////////////////////////////////////////////////////////// -// Function: DCPacker::seek -// Access: Published -// Description: Sets the current unpack (or repack) position to the -// named field. In unpack mode, the next call to -// unpack_*() or push() will begin to read the named -// field. In repack mode, the next call to pack_*() or -// push() will modify the named field. -// -// Returns true if successful, false if the field is not -// known (or if the packer is in an invalid mode). -//////////////////////////////////////////////////////////////////// -bool DCPacker:: -seek(const string &field_name) { - if (_catalog == (DCPackerCatalog *)NULL) { - _catalog = _root->get_catalog(); - _live_catalog = _catalog->get_live_catalog(_unpack_data, _unpack_length); - } - nassertr(_catalog != (DCPackerCatalog *)NULL, false); - if (_live_catalog == NULL) { - _pack_error = true; - return false; - } - - int seek_index = _live_catalog->find_entry_by_name(field_name); - if (seek_index < 0) { - // The field was not known. - _pack_error = true; - return false; - } - - return seek(seek_index); -} - -//////////////////////////////////////////////////////////////////// -// Function: DCPacker::seek -// Access: Published -// Description: Seeks to the field indentified by seek_index, which -// was returned by an earlier call to -// DCField::find_seek_index() to get the index of some -// nested field. Also see the version of seek() that -// accepts a field name. -// -// Returns true if successful, false if the field is not -// known (or if the packer is in an invalid mode). -//////////////////////////////////////////////////////////////////// -bool DCPacker:: -seek(int seek_index) { - if (_catalog == (DCPackerCatalog *)NULL) { - _catalog = _root->get_catalog(); - _live_catalog = _catalog->get_live_catalog(_unpack_data, _unpack_length); - } - nassertr(_catalog != (DCPackerCatalog *)NULL, false); - if (_live_catalog == NULL) { - _pack_error = true; - return false; - } - - if (_mode == M_unpack) { - const DCPackerCatalog::Entry &entry = _live_catalog->get_entry(seek_index); - - // If we are seeking, we don't need to remember our current stack - // position. - clear_stack(); - _current_field = entry._field; - _current_parent = entry._parent; - _current_field_index = entry._field_index; - _num_nested_fields = _current_parent->get_num_nested_fields(); - _unpack_p = _live_catalog->get_begin(seek_index); - - // We don't really need _push_marker and _pop_marker now, except - // that we should set _push_marker in case we have just seeked to - // a switch parameter, and we should set _pop_marker to 0 just so - // it won't get in the way. - _push_marker = _unpack_p; - _pop_marker = 0; - - return true; - - } else if (_mode == M_repack) { - nassertr(_catalog != (DCPackerCatalog *)NULL, false); - - if (_stack != NULL || _current_field != NULL) { - // It is an error to reseek while the stack is nonempty--that - // means we haven't finished packing the current field. - _pack_error = true; - return false; - } - const DCPackerCatalog::Entry &entry = _live_catalog->get_entry(seek_index); - - if (entry._parent->as_switch_parameter() != (DCSwitchParameter *)NULL) { - // If the parent is a DCSwitch, that can only mean that the - // seeked field is a switch parameter. We can't support seeking - // to a switch parameter and modifying it directly--what would - // happen to all of the related fields? Instead, you'll have to - // seek to the switch itself and repack the whole entity. - _pack_error = true; - return false; - } - - size_t begin = _live_catalog->get_begin(seek_index); - if (begin < _unpack_p) { - // Whoops, we are seeking fields out-of-order. That means we - // need to write the entire record and start again. - _pack_data.append_data(_unpack_data + _unpack_p, _unpack_length - _unpack_p); - size_t length = _pack_data.get_length(); - char *buffer = _pack_data.take_data(); - set_unpack_data(buffer, length, true); - _unpack_p = 0; - - _catalog->release_live_catalog(_live_catalog); - _live_catalog = _catalog->get_live_catalog(_unpack_data, _unpack_length); - - if (_live_catalog == NULL) { - _pack_error = true; - return false; - } - - begin = _live_catalog->get_begin(seek_index); - } - - // Now copy the bytes from _unpack_p to begin from the - // _unpack_data to the _pack_data. These are the bytes we just - // skipped over with the call to seek(). - _pack_data.append_data(_unpack_data + _unpack_p, begin - _unpack_p); - - // And set the packer up to pack the indicated field (but no - // subsequent fields). - _current_field = entry._field; - _current_parent = entry._parent; - _current_field_index = entry._field_index; - _num_nested_fields = 1; - _unpack_p = _live_catalog->get_end(seek_index); - - // Set up push_marker and pop_marker so we won't try to advance - // beyond this field. - _push_marker = begin; - _pop_marker = _live_catalog->get_end(seek_index); - - return true; - } - - // Invalid mode. - _pack_error = true; - return false; -} - -//////////////////////////////////////////////////////////////////// -// Function: DCPacker::push -// Access: Published -// Description: Marks the beginning of a nested series of fields. -// -// This must be called before filling the elements of an -// array or the individual fields in a structure field. -// It must also be balanced by a matching pop(). -// -// It is necessary to use push() / pop() only if -// has_nested_fields() returns true. -//////////////////////////////////////////////////////////////////// -void DCPacker:: -push() { - if (!has_nested_fields()) { - _pack_error = true; - - } else { - StackElement *element = new StackElement; - element->_current_parent = _current_parent; - element->_current_field_index = _current_field_index; - element->_push_marker = _push_marker; - element->_pop_marker = _pop_marker; - element->_next = _stack; - _stack = element; - _current_parent = _current_field; - - - // Now deal with the length prefix that might or might not be - // before a sequence of nested fields. - int num_nested_fields = _current_parent->get_num_nested_fields(); - size_t length_bytes = _current_parent->get_num_length_bytes(); - - if (_mode == M_pack || _mode == M_repack) { - // Reserve length_bytes for when we figure out what the length - // is. - _push_marker = _pack_data.get_length(); - _pop_marker = 0; - _pack_data.append_junk(length_bytes); - - } else if (_mode == M_unpack) { - // Read length_bytes to determine the end of this nested - // sequence. - _push_marker = _unpack_p; - _pop_marker = 0; - - if (length_bytes != 0) { - if (_unpack_p + length_bytes > _unpack_length) { - _pack_error = true; - - } else { - size_t length; - if (length_bytes == 4) { - length = DCPackerInterface::do_unpack_uint32 - (_unpack_data + _unpack_p); - _unpack_p += 4; - } else { - length = DCPackerInterface::do_unpack_uint16 - (_unpack_data + _unpack_p); - _unpack_p += 2; - } - _pop_marker = _unpack_p + length; - - // The explicit length trumps the number of nested fields - // reported by get_num_nested_fields(). - if (length == 0) { - num_nested_fields = 0; - } else { - num_nested_fields = _current_parent->calc_num_nested_fields(length); - } - } - } - } else { - _pack_error = true; - } - - - // Now point to the first field in the nested range. - _num_nested_fields = num_nested_fields; - _current_field_index = 0; - - if (_num_nested_fields >= 0 && - _current_field_index >= _num_nested_fields) { - _current_field = NULL; - - } else { - _current_field = _current_parent->get_nested_field(_current_field_index); - } - } -} - -//////////////////////////////////////////////////////////////////// -// Function: DCPacker::pop -// Access: Published -// Description: Marks the end of a nested series of fields. -// -// This must be called to match a previous push() only -// after all the expected number of nested fields have -// been packed. It is an error to call it too early, or -// too late. -//////////////////////////////////////////////////////////////////// -void DCPacker:: -pop() { - if (_current_field != NULL && _num_nested_fields >= 0) { - // Oops, didn't pack or unpack enough values. - _pack_error = true; - - } else if (_mode == M_unpack && _pop_marker != 0 && - _unpack_p != _pop_marker) { - // Didn't unpack the right number of values. - _pack_error = true; - } - - if (_stack == NULL) { - // Unbalanced pop(). - _pack_error = true; - - } else { - if (!_current_parent->validate_num_nested_fields(_current_field_index)) { - // Incorrect number of nested elements. - _pack_error = true; - } - - if (_mode == M_pack || _mode == M_repack) { - size_t length_bytes = _current_parent->get_num_length_bytes(); - if (length_bytes != 0) { - // Now go back and fill in the length of the array. - size_t length = _pack_data.get_length() - _push_marker - length_bytes; - if (length_bytes == 4) { - DCPackerInterface::do_pack_uint32 - (_pack_data.get_rewrite_pointer(_push_marker, 4), length); - } else { - DCPackerInterface::validate_uint_limits(length, 16, _range_error); - DCPackerInterface::do_pack_uint16 - (_pack_data.get_rewrite_pointer(_push_marker, 2), length); - } - } - } - - _current_field = _current_parent; - _current_parent = _stack->_current_parent; - _current_field_index = _stack->_current_field_index; - _push_marker = _stack->_push_marker; - _pop_marker = _stack->_pop_marker; - _num_nested_fields = (_current_parent == NULL) ? 0 : _current_parent->get_num_nested_fields(); - - StackElement *next = _stack->_next; - delete _stack; - _stack = next; - } - - advance(); -} - -//////////////////////////////////////////////////////////////////// -// Function: DCPacker::pack_default_value -// Access: Published -// Description: Adds the default value for the current element into -// the stream. If no default has been set for the -// current element, creates a sensible default. -//////////////////////////////////////////////////////////////////// -void DCPacker:: -pack_default_value() { - nassertv(_mode == M_pack || _mode == M_repack); - if (_current_field == NULL) { - _pack_error = true; - } else { - if (_current_field->pack_default_value(_pack_data, _pack_error)) { - advance(); - - } else { - // If the single field didn't know how to pack a default value, - // try packing nested fields. - push(); - while (more_nested_fields()) { - pack_default_value(); - } - pop(); - } - } -} - -//////////////////////////////////////////////////////////////////// -// Function: DCPacker::unpack_validate -// Access: Published -// Description: Internally unpacks the current numeric or string -// value and validates it against the type range limits, -// but does not return the value. If the current field -// contains nested fields, validates all of them. -//////////////////////////////////////////////////////////////////// -void DCPacker:: -unpack_validate() { - nassertv(_mode == M_unpack); - if (_current_field == NULL) { - _pack_error = true; - - } else { - if (_current_field->unpack_validate(_unpack_data, _unpack_length, _unpack_p, - _pack_error, _range_error)) { - advance(); - } else { - // If the single field couldn't be validated, try validating - // nested fields. - push(); - while (more_nested_fields()) { - unpack_validate(); - } - pop(); - } - } -} - -//////////////////////////////////////////////////////////////////// -// Function: DCPacker::unpack_skip -// Access: Published -// Description: Skips the current field without unpacking it and -// advances to the next field. If the current field -// contains nested fields, skips all of them. -//////////////////////////////////////////////////////////////////// -void DCPacker:: -unpack_skip() { - nassertv(_mode == M_unpack); - if (_current_field == NULL) { - _pack_error = true; - - } else { - if (_current_field->unpack_skip(_unpack_data, _unpack_length, _unpack_p, - _pack_error)) { - advance(); - - } else { - // If the single field couldn't be skipped, try skipping nested fields. - push(); - while (more_nested_fields()) { - unpack_skip(); - } - pop(); - } - } -} - -#ifdef HAVE_PYTHON -//////////////////////////////////////////////////////////////////// -// Function: DCPacker::pack_object -// Access: Published -// Description: Packs the Python object of whatever type into the -// packer. Each numeric object and string object maps -// to the corresponding pack_value() call; a tuple or -// sequence maps to a push() followed by all of the -// tuple's contents followed by a pop(). -//////////////////////////////////////////////////////////////////// -void DCPacker:: -pack_object(PyObject *object) { - nassertv(_mode == M_pack || _mode == M_repack); - - if (PyInt_Check(object)) { - pack_int(PyInt_AS_LONG(object)); - } else if (PyFloat_Check(object)) { - pack_double(PyFloat_AS_DOUBLE(object)); - } else if (PyLong_Check(object)) { - pack_int64(PyLong_AsLongLong(object)); - } else if (PyString_Check(object) || PyUnicode_Check(object)) { - char *buffer; - int length; - PyString_AsStringAndSize(object, &buffer, &length); - if (buffer) { - pack_string(string(buffer, length)); - } - } else { - // For some reason, PySequence_Check() is incorrectly reporting - // that a class instance is a sequence, even if it doesn't provide - // __len__, so we double-check by testing for __len__ explicitly. - bool is_sequence = - (PySequence_Check(object) != 0) && - (PyObject_HasAttrString(object, "__len__") != 0); - bool is_instance = false; - - const DCClass *dclass = NULL; - const DCPackerInterface *current_field = get_current_field(); - if (current_field != (DCPackerInterface *)NULL) { - const DCClassParameter *class_param = get_current_field()->as_class_parameter(); - if (class_param != (DCClassParameter *)NULL) { - dclass = class_param->get_class(); - - if (dclass->has_class_def()) { - PyObject *class_def = dclass->get_class_def(); - is_instance = (PyObject_IsInstance(object, dclass->get_class_def()) != 0); - Py_DECREF(class_def); - } - } - } - - // If dclass is not NULL, the packer is expecting a class object. - // There are then two cases: (1) the user has supplied a matching - // class object, or (2) the user has supplied a sequence object. - // Unfortunately, it may be difficult to differentiate these two - // cases, since a class object may also be a sequence object. - - // The rule to differentiate them is: - - // (1) If the supplied class object is an instance of the expected - // class object, it is considered to be a class object. - - // (2) Otherwise, if the supplied class object has a __len__() - // method (i.e. PySequence_Check() returns true), then it is - // considered to be a sequence. - - // (3) Otherwise, it is considered to be a class object. - - if (dclass != (DCClass *)NULL && (is_instance || !is_sequence)) { - // The supplied object is either an instance of the expected - // class object, or it is not a sequence--this is case (1) or - // (3). - pack_class_object(dclass, object); - } else if (is_sequence) { - // The supplied object is not an instance of the expected class - // object, but it is a sequence. This is case (2). - push(); - int size = PySequence_Size(object); - for (int i = 0; i < size; ++i) { - PyObject *element = PySequence_GetItem(object, i); - if (element != (PyObject *)NULL) { - pack_object(element); - Py_DECREF(element); - } else { - cerr << "Unable to extract item " << i << " from sequence.\n"; - } - } - pop(); - } else { - // The supplied object is not a sequence, and we weren't - // expecting a class parameter. This is none of the above, an - // error. - ostringstream strm; - PyObject *str = PyObject_Str(object); - strm << "Don't know how to pack object: " - << PyString_AsString(str) << "\n"; - Py_DECREF(str); - nassert_raise(strm.str()); - _pack_error = true; - } - } -} -#endif // HAVE_PYTHON - -#ifdef HAVE_PYTHON -//////////////////////////////////////////////////////////////////// -// Function: DCPacker::unpack_object -// Access: Published -// Description: Unpacks a Python object of the appropriate type from -// the stream for the current field. This may be an -// integer or a string for a simple field object; if the -// current field represents a list of fields it will be -// a tuple. -//////////////////////////////////////////////////////////////////// -PyObject *DCPacker:: -unpack_object() { - PyObject *object = NULL; - - DCPackType pack_type = get_pack_type(); - - switch (pack_type) { - case PT_invalid: - object = Py_None; - unpack_skip(); - break; - - case PT_double: - { - double value = unpack_double(); - object = PyFloat_FromDouble(value); - } - break; - - case PT_int: - { - int value = unpack_int(); - object = PyInt_FromLong(value); - } - break; - - case PT_uint: - { - unsigned int value = unpack_uint(); - if (value & 0x80000000) { - object = PyLong_FromUnsignedLong(value); - } else { - object = PyInt_FromLong(value); - } - } - break; - - case PT_int64: - { - PN_int64 value = unpack_int64(); - object = PyLong_FromLongLong(value); - } - break; - - case PT_uint64: - { - PN_uint64 value = unpack_uint64(); - object = PyLong_FromUnsignedLongLong(value); - } - break; - - case PT_string: - case PT_blob: - { - string str; - unpack_string(str); - object = PyString_FromStringAndSize(str.data(), str.size()); - } - break; - - case PT_class: - { - const DCClassParameter *class_param = get_current_field()->as_class_parameter(); - if (class_param != (DCClassParameter *)NULL) { - const DCClass *dclass = class_param->get_class(); - if (dclass->has_class_def()) { - // If we know what kind of class object this is and it has a - // valid constructor, create the class object instead of - // just a tuple. - object = unpack_class_object(dclass); - break; - } - } - } - // If we don't know what kind of class object it is, or it doesn't - // have a constructor, fall through and make a tuple. - default: - { - // First, build up a list from the nested objects. - object = PyList_New(0); - - push(); - while (more_nested_fields()) { - PyObject *element = unpack_object(); - PyList_Append(object, element); - Py_DECREF(element); - } - pop(); - - if (pack_type != PT_array) { - // For these other kinds of objects, we'll convert the list - // into a tuple. - PyObject *tuple = PyList_AsTuple(object); - Py_DECREF(object); - object = tuple; - } - } - break; - } - - return object; -} -#endif // HAVE_PYTHON - - -//////////////////////////////////////////////////////////////////// -// Function: DCPacker::parse_and_pack -// Access: Published -// Description: Parses an object's value according to the DC file -// syntax (e.g. as a default value string) and packs it. -// Returns true on success, false on a parse error. -//////////////////////////////////////////////////////////////////// -bool DCPacker:: -parse_and_pack(const string &formatted_object) { - istringstream strm(formatted_object); - return parse_and_pack(strm); -} - -//////////////////////////////////////////////////////////////////// -// Function: DCPacker::parse_and_pack -// Access: Published -// Description: Parses an object's value according to the DC file -// syntax (e.g. as a default value string) and packs it. -// Returns true on success, false on a parse error. -//////////////////////////////////////////////////////////////////// -bool DCPacker:: -parse_and_pack(istream &in) { - dc_init_parser_parameter_value(in, "parse_and_pack", *this); - dcyyparse(); - dc_cleanup_parser(); - - bool parse_error = (dc_error_count() != 0); - if (parse_error) { - _parse_error = true; - } - - return !parse_error; -} - -//////////////////////////////////////////////////////////////////// -// Function: DCPacker::unpack_and_format -// Access: Published -// Description: Unpacks an object and formats its value into a syntax -// suitable for parsing in the dc file (e.g. as a -// default value), or as an input to parse_object. -//////////////////////////////////////////////////////////////////// -string DCPacker:: -unpack_and_format(bool show_field_names) { - ostringstream strm; - unpack_and_format(strm, show_field_names); - return strm.str(); -} - -//////////////////////////////////////////////////////////////////// -// Function: DCPacker::unpack_and_format -// Access: Published -// Description: Unpacks an object and formats its value into a syntax -// suitable for parsing in the dc file (e.g. as a -// default value), or as an input to parse_object. -//////////////////////////////////////////////////////////////////// -void DCPacker:: -unpack_and_format(ostream &out, bool show_field_names) { - DCPackType pack_type = get_pack_type(); - - if (show_field_names && !get_current_field_name().empty()) { - nassertv(_current_field != (DCPackerInterface *)NULL); - const DCField *field = _current_field->as_field(); - if (field != (DCField *)NULL && - field->as_parameter() != (DCParameter *)NULL) { - out << field->get_name() << " = "; - } - } - - switch (pack_type) { - case PT_invalid: - out << ""; - break; - - case PT_double: - out << unpack_double(); - break; - - case PT_int: - out << unpack_int(); - break; - - case PT_uint: - out << unpack_uint(); - break; - - case PT_int64: - out << unpack_int64(); - break; - - case PT_uint64: - out << unpack_uint64(); - break; - - case PT_string: - enquote_string(out, '"', unpack_string()); - break; - - case PT_blob: - output_hex_string(out, unpack_literal_value()); - break; - - default: - { - switch (pack_type) { - case PT_array: - out << '['; - break; - - case PT_field: - case PT_switch: - out << '('; - break; - - case PT_class: - default: - out << '{'; - break; - } - - push(); - while (more_nested_fields() && !had_pack_error()) { - unpack_and_format(out, show_field_names); - - if (more_nested_fields()) { - out << ", "; - } - } - pop(); - - switch (pack_type) { - case PT_array: - out << ']'; - break; - - case PT_field: - case PT_switch: - out << ')'; - break; - - case PT_class: - default: - out << '}'; - break; - } - } - break; - } -} - -//////////////////////////////////////////////////////////////////// -// Function: DCPacker::enquote_string -// Access: Public, Static -// Description: Outputs the indicated string within quotation marks. -//////////////////////////////////////////////////////////////////// -void DCPacker:: -enquote_string(ostream &out, char quote_mark, const string &str) { - out << quote_mark; - for (string::const_iterator pi = str.begin(); - pi != str.end(); - ++pi) { - if ((*pi) == quote_mark || (*pi) == '\\') { - out << '\\' << (*pi); - - } else if (!isprint(*pi)) { - char buffer[10]; - sprintf(buffer, "%02x", (unsigned char)(*pi)); - out << "\\x" << buffer; - - } else { - out << (*pi); - } - } - out << quote_mark; -} - -//////////////////////////////////////////////////////////////////// -// Function: DCPacker::output_hex_string -// Access: Public, Static -// Description: Outputs the indicated string as a hex constant. -//////////////////////////////////////////////////////////////////// -void DCPacker:: -output_hex_string(ostream &out, const string &str) { - out << '<'; - for (string::const_iterator pi = str.begin(); - pi != str.end(); - ++pi) { - char buffer[10]; - sprintf(buffer, "%02x", (unsigned char)(*pi)); - out << buffer; - } - out << '>'; -} - -//////////////////////////////////////////////////////////////////// -// Function: DCPacker::handle_switch -// Access: Private -// Description: When we advance past the key field on a switch -// record, we suddenly have more fields available--all -// the appropriate alternate fields in the switch. -// -// This function is called when we detect this -// condition; it switches the _current_parent to the -// appropriate case of the switch record. -//////////////////////////////////////////////////////////////////// -void DCPacker:: -handle_switch(const DCSwitchParameter *switch_parameter) { - // First, get the value from the key. This is either found in the - // unpack or the pack data, depending on what mode we're in. - const DCPackerInterface *new_parent = NULL; - - if (_mode == M_pack || _mode == M_repack) { - const char *data = _pack_data.get_data(); - new_parent = switch_parameter->apply_switch - (data + _push_marker, _pack_data.get_length() - _push_marker); - - } else if (_mode == M_unpack) { - new_parent = switch_parameter->apply_switch - (_unpack_data + _push_marker, _unpack_p - _push_marker); - } - - if (new_parent == (DCPackerInterface *)NULL) { - // This means an invalid value was packed for the key. - _range_error = true; - return; - } - - _last_switch = switch_parameter; - - // Now substitute in the switch case for the previous parent (which - // replaces the switch node itself). This will suddenly make a slew - // of new fields appear. - _current_parent = new_parent; - _num_nested_fields = _current_parent->get_num_nested_fields(); - - if (_num_nested_fields < 0 || - _current_field_index < _num_nested_fields) { - _current_field = _current_parent->get_nested_field(_current_field_index); - } -} - -//////////////////////////////////////////////////////////////////// -// Function: DCPacker::clear -// Access: Private -// Description: Resets the data structures after a pack or unpack -// sequence. -//////////////////////////////////////////////////////////////////// -void DCPacker:: -clear() { - clear_stack(); - _current_field = NULL; - _current_parent = NULL; - _current_field_index = 0; - _num_nested_fields = 0; - _push_marker = 0; - _pop_marker = 0; - _last_switch = NULL; - - if (_live_catalog != (DCPackerCatalog::LiveCatalog *)NULL) { - _catalog->release_live_catalog(_live_catalog); - _live_catalog = NULL; - } - _catalog = NULL; - _root = NULL; -} - -//////////////////////////////////////////////////////////////////// -// Function: DCPacker::clear_stack -// Access: Private -// Description: Empties the stack. -//////////////////////////////////////////////////////////////////// -void DCPacker:: -clear_stack() { - while (_stack != (StackElement *)NULL) { - StackElement *next = _stack->_next; - delete _stack; - _stack = next; - } -} - -#ifdef HAVE_PYTHON -//////////////////////////////////////////////////////////////////// -// Function: DCPacker::pack_class_object -// Access: Private -// Description: Given that the current element is a ClassParameter -// for a Python class object, try to extract the -// appropriate values from the class object and pack in. -//////////////////////////////////////////////////////////////////// -void DCPacker:: -pack_class_object(const DCClass *dclass, PyObject *object) { - push(); - while (more_nested_fields() && !_pack_error) { - const DCField *field = get_current_field()->as_field(); - nassertv(field != (DCField *)NULL); - get_class_element(dclass, object, field); - } - pop(); -} -#endif // HAVE_PYTHON - -#ifdef HAVE_PYTHON -//////////////////////////////////////////////////////////////////// -// Function: DCPacker::unpack_class_object -// Access: Private -// Description: Given that the current element is a ClassParameter -// for a Python class for which we have a valid -// constructor, unpack it and fill in its values. -//////////////////////////////////////////////////////////////////// -PyObject *DCPacker:: -unpack_class_object(const DCClass *dclass) { - PyObject *class_def = dclass->get_class_def(); - nassertr(class_def != (PyObject *)NULL, NULL); - - PyObject *object = NULL; - - if (!dclass->has_constructor()) { - // If the class uses a default constructor, go ahead and create - // the Python object for it now. - object = PyObject_CallObject(class_def, NULL); - if (object == (PyObject *)NULL) { - return NULL; - } - } - - push(); - if (object == (PyObject *)NULL && more_nested_fields()) { - // The first nested field will be the constructor. - const DCField *field = get_current_field()->as_field(); - nassertr(field != (DCField *)NULL, object); - nassertr(field == dclass->get_constructor(), object); - - set_class_element(class_def, object, field); - - // By now, the object should have been constructed. - if (object == (PyObject *)NULL) { - return NULL; - } - } - while (more_nested_fields()) { - const DCField *field = get_current_field()->as_field(); - nassertr(field != (DCField *)NULL, object); - - set_class_element(class_def, object, field); - } - pop(); - - return object; -} -#endif // HAVE_PYTHON - - -#ifdef HAVE_PYTHON -//////////////////////////////////////////////////////////////////// -// Function: DCPacker::set_class_element -// Access: Private -// Description: Unpacks the current element and stuffs it on the -// Python class object in whatever way is appropriate. -//////////////////////////////////////////////////////////////////// -void DCPacker:: -set_class_element(PyObject *class_def, PyObject *&object, - const DCField *field) { - string field_name = field->get_name(); - DCPackType pack_type = get_pack_type(); - - if (field_name.empty()) { - switch (pack_type) { - case PT_class: - case PT_switch: - // If the field has no name, but it is one of these container - // objects, we want to unpack its nested objects directly into - // the class. - push(); - while (more_nested_fields()) { - const DCField *field = get_current_field()->as_field(); - nassertv(field != (DCField *)NULL); - nassertv(object != (PyObject *)NULL); - set_class_element(class_def, object, field); - } - pop(); - break; - - default: - // Otherwise, we just skip over the field. - unpack_skip(); - } - - } else { - // If the field does have a name, we will want to store it on the - // class, either by calling a method (for a PT_field pack_type) or - // by setting a value (for any other kind of pack_type). - - PyObject *element = unpack_object(); - - if (pack_type == PT_field) { - if (object == (PyObject *)NULL) { - // If the object hasn't been constructed yet, assume this is - // the constructor. - object = PyObject_CallObject(class_def, element); - - } else { - if (PyObject_HasAttrString(object, (char *)field_name.c_str())) { - PyObject *func = PyObject_GetAttrString(object, (char *)field_name.c_str()); - if (func != (PyObject *)NULL) { - PyObject *result = PyObject_CallObject(func, element); - Py_XDECREF(result); - Py_DECREF(func); - } - } - } - - } else { - nassertv(object != (PyObject *)NULL); - PyObject_SetAttrString(object, (char *)field_name.c_str(), element); - } - - Py_DECREF(element); - } -} -#endif // HAVE_PYTHON - - -#ifdef HAVE_PYTHON -//////////////////////////////////////////////////////////////////// -// Function: DCPacker::get_class_element -// Access: Private -// Description: Gets the current element from the Python object and -// packs it. -//////////////////////////////////////////////////////////////////// -void DCPacker:: -get_class_element(const DCClass *dclass, PyObject *object, - const DCField *field) { - string field_name = field->get_name(); - DCPackType pack_type = get_pack_type(); - - if (field_name.empty()) { - switch (pack_type) { - case PT_class: - case PT_switch: - // If the field has no name, but it is one of these container - // objects, we want to get its nested objects directly from - // the class. - push(); - while (more_nested_fields() && !_pack_error) { - const DCField *field = get_current_field()->as_field(); - nassertv(field != (DCField *)NULL); - get_class_element(dclass, object, field); - } - pop(); - break; - - default: - // Otherwise, we just pack the default value. - pack_default_value(); - } - - } else { - // If the field does have a name, we will want to get it from the - // class and pack it. It just so happens that there's already a - // method that does this on DCClass. - - if (!dclass->pack_required_field(*this, object, field)) { - _pack_error = true; - } - } -} -#endif // HAVE_PYTHON +// Filename: dcPacker.cxx +// Created by: drose (15Jun04) +// +//////////////////////////////////////////////////////////////////// +// +// 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 "dcPacker.h" +#include "dcSwitch.h" +#include "dcParserDefs.h" +#include "dcLexerDefs.h" +#include "dcClassParameter.h" +#include "dcSwitchParameter.h" +#include "dcClass.h" + +DCPacker::StackElement *DCPacker::StackElement::_deleted_chain = NULL; +int DCPacker::StackElement::_num_ever_allocated = 0; + +//////////////////////////////////////////////////////////////////// +// Function: DCPacker::Constructor +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +DCPacker:: +DCPacker() { + _mode = M_idle; + _unpack_data = NULL; + _unpack_length = 0; + _owns_unpack_data = false; + _unpack_p = 0; + _live_catalog = NULL; + _parse_error = false; + _pack_error = false; + _range_error = false; + _stack = NULL; + + clear(); +} + +//////////////////////////////////////////////////////////////////// +// Function: DCPacker::Destructor +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +DCPacker:: +~DCPacker() { + clear_data(); + clear(); +} + +//////////////////////////////////////////////////////////////////// +// Function: DCPacker::begin_pack +// Access: Published +// Description: Begins a packing session. The parameter is the DC +// object that describes the packing format; it may be a +// DCParameter or DCField. +// +// Unless you call clear_data() between sessions, +// multiple packing sessions will be concatenated +// together into the same buffer. If you wish to add +// bytes to the buffer between packing sessions, use +// append_data() or get_write_pointer(). +//////////////////////////////////////////////////////////////////// +void DCPacker:: +begin_pack(const DCPackerInterface *root) { + nassertv(_mode == M_idle); + + _mode = M_pack; + _parse_error = false; + _pack_error = false; + _range_error = false; + + _root = root; + _catalog = NULL; + _live_catalog = NULL; + + _current_field = root; + _current_parent = NULL; + _current_field_index = 0; + _num_nested_fields = 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: DCPacker::end_pack +// Access: Published, Virtual +// Description: Finishes a packing session. +// +// The return value is true on success, or false if +// there has been some error during packing. +//////////////////////////////////////////////////////////////////// +bool DCPacker:: +end_pack() { + nassertr(_mode == M_pack, false); + + _mode = M_idle; + + if (_stack != NULL || _current_field != NULL || _current_parent != NULL) { + _pack_error = true; + } + + clear(); + + return !had_error(); +} + +//////////////////////////////////////////////////////////////////// +// Function: DCPacker::set_unpack_data +// Access: Public +// Description: Sets up the unpack_data pointer. You may call this +// before calling the version of begin_unpack() that +// takes only one parameter. +//////////////////////////////////////////////////////////////////// +void DCPacker:: +set_unpack_data(const string &data) { + nassertv(_mode == M_idle); + + char *buffer = new char[data.length()]; + memcpy(buffer, data.data(), data.length()); + set_unpack_data(buffer, data.length(), true); +} + +//////////////////////////////////////////////////////////////////// +// Function: DCPacker::set_unpack_data +// Access: Public +// Description: Sets up the unpack_data pointer. You may call this +// before calling the version of begin_unpack() that +// takes only one parameter. +//////////////////////////////////////////////////////////////////// +void DCPacker:: +set_unpack_data(const char *unpack_data, size_t unpack_length, + bool owns_unpack_data) { + nassertv(_mode == M_idle); + + if (_owns_unpack_data) { + delete[] _unpack_data; + } + _unpack_data = unpack_data; + _unpack_length = unpack_length; + _owns_unpack_data = owns_unpack_data; + _unpack_p = 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: DCPacker::begin_unpack +// Access: Public +// Description: Begins an unpacking session. You must have +// previously called set_unpack_data() to specify a +// buffer to unpack. +// +// If there was data left in the buffer after a previous +// begin_unpack() .. end_unpack() session, the new +// session will resume from the current point. This +// method may be used, therefore, to unpack a sequence +// of objects from the same buffer. +//////////////////////////////////////////////////////////////////// +void DCPacker:: +begin_unpack(const DCPackerInterface *root) { + nassertv(_mode == M_idle); + nassertv(_unpack_data != NULL); + + _mode = M_unpack; + _parse_error = false; + _pack_error = false; + _range_error = false; + + _root = root; + _catalog = NULL; + _live_catalog = NULL; + + _current_field = root; + _current_parent = NULL; + _current_field_index = 0; + _num_nested_fields = 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: DCPacker::end_unpack +// Access: Published +// Description: Finishes the unpacking session. +// +// The return value is true on success, or false if +// there has been some error during unpacking (or if all +// fields have not been unpacked). +//////////////////////////////////////////////////////////////////// +bool DCPacker:: +end_unpack() { + nassertr(_mode == M_unpack, false); + + _mode = M_idle; + + if (_stack != NULL || _current_field != NULL || _current_parent != NULL) { + // This happens if we have not unpacked all of the fields. + // However, this is not an error if we have called seek() during + // the unpack session (in which case the _catalog will be + // non-NULL). On the other hand, if the catalog is still NULL, + // then we have never called seek() and it is an error not to + // unpack all values. + if (_catalog == (DCPackerCatalog *)NULL) { + _pack_error = true; + } + } + + clear(); + + return !had_error(); +} + +//////////////////////////////////////////////////////////////////// +// Function: DCPacker::begin_repack +// Access: Public +// Description: Begins a repacking session. You must have previously +// called set_unpack_data() to specify a buffer to +// unpack. +// +// Unlike begin_pack() or begin_unpack() you may not +// concatenate the results of multiple begin_repack() +// sessions in one buffer. +// +// Also, unlike in packing or unpacking modes, you may +// not walk through the fields from beginning to end, or +// even pack two consecutive fields at once. Instead, +// you must call seek() for each field you wish to +// modify and pack only that one field; then call seek() +// again to modify another field. +//////////////////////////////////////////////////////////////////// +void DCPacker:: +begin_repack(const DCPackerInterface *root) { + nassertv(_mode == M_idle); + nassertv(_unpack_data != NULL); + nassertv(_unpack_p == 0); + + _mode = M_repack; + _parse_error = false; + _pack_error = false; + _range_error = false; + _pack_data.clear(); + + // In repack mode, we immediately get the catalog, since we know + // we'll need it. + _root = root; + _catalog = _root->get_catalog(); + _live_catalog = _catalog->get_live_catalog(_unpack_data, _unpack_length); + if (_live_catalog == NULL) { + _pack_error = true; + } + + // We don't begin at the first field in repack mode. Instead, you + // must explicitly call seek(). + _current_field = NULL; + _current_parent = NULL; + _current_field_index = 0; + _num_nested_fields = 0; +} + +//////////////////////////////////////////////////////////////////// +// Function: DCPacker::end_repack +// Access: Published +// Description: Finishes the repacking session. +// +// The return value is true on success, or false if +// there has been some error during repacking (or if all +// fields have not been repacked). +//////////////////////////////////////////////////////////////////// +bool DCPacker:: +end_repack() { + nassertr(_mode == M_repack, false); + + // Put the rest of the data onto the pack stream. + _pack_data.append_data(_unpack_data + _unpack_p, _unpack_length - _unpack_p); + + _mode = M_idle; + clear(); + + return !had_error(); +} + +//////////////////////////////////////////////////////////////////// +// Function: DCPacker::seek +// Access: Published +// Description: Sets the current unpack (or repack) position to the +// named field. In unpack mode, the next call to +// unpack_*() or push() will begin to read the named +// field. In repack mode, the next call to pack_*() or +// push() will modify the named field. +// +// Returns true if successful, false if the field is not +// known (or if the packer is in an invalid mode). +//////////////////////////////////////////////////////////////////// +bool DCPacker:: +seek(const string &field_name) { + if (_catalog == (DCPackerCatalog *)NULL) { + _catalog = _root->get_catalog(); + _live_catalog = _catalog->get_live_catalog(_unpack_data, _unpack_length); + } + nassertr(_catalog != (DCPackerCatalog *)NULL, false); + if (_live_catalog == NULL) { + _pack_error = true; + return false; + } + + int seek_index = _live_catalog->find_entry_by_name(field_name); + if (seek_index < 0) { + // The field was not known. + _pack_error = true; + return false; + } + + return seek(seek_index); +} + +//////////////////////////////////////////////////////////////////// +// Function: DCPacker::seek +// Access: Published +// Description: Seeks to the field indentified by seek_index, which +// was returned by an earlier call to +// DCField::find_seek_index() to get the index of some +// nested field. Also see the version of seek() that +// accepts a field name. +// +// Returns true if successful, false if the field is not +// known (or if the packer is in an invalid mode). +//////////////////////////////////////////////////////////////////// +bool DCPacker:: +seek(int seek_index) { + if (_catalog == (DCPackerCatalog *)NULL) { + _catalog = _root->get_catalog(); + _live_catalog = _catalog->get_live_catalog(_unpack_data, _unpack_length); + } + nassertr(_catalog != (DCPackerCatalog *)NULL, false); + if (_live_catalog == NULL) { + _pack_error = true; + return false; + } + + if (_mode == M_unpack) { + const DCPackerCatalog::Entry &entry = _live_catalog->get_entry(seek_index); + + // If we are seeking, we don't need to remember our current stack + // position. + clear_stack(); + _current_field = entry._field; + _current_parent = entry._parent; + _current_field_index = entry._field_index; + _num_nested_fields = _current_parent->get_num_nested_fields(); + _unpack_p = _live_catalog->get_begin(seek_index); + + // We don't really need _push_marker and _pop_marker now, except + // that we should set _push_marker in case we have just seeked to + // a switch parameter, and we should set _pop_marker to 0 just so + // it won't get in the way. + _push_marker = _unpack_p; + _pop_marker = 0; + + return true; + + } else if (_mode == M_repack) { + nassertr(_catalog != (DCPackerCatalog *)NULL, false); + + if (_stack != NULL || _current_field != NULL) { + // It is an error to reseek while the stack is nonempty--that + // means we haven't finished packing the current field. + _pack_error = true; + return false; + } + const DCPackerCatalog::Entry &entry = _live_catalog->get_entry(seek_index); + + if (entry._parent->as_switch_parameter() != (DCSwitchParameter *)NULL) { + // If the parent is a DCSwitch, that can only mean that the + // seeked field is a switch parameter. We can't support seeking + // to a switch parameter and modifying it directly--what would + // happen to all of the related fields? Instead, you'll have to + // seek to the switch itself and repack the whole entity. + _pack_error = true; + return false; + } + + size_t begin = _live_catalog->get_begin(seek_index); + if (begin < _unpack_p) { + // Whoops, we are seeking fields out-of-order. That means we + // need to write the entire record and start again. + _pack_data.append_data(_unpack_data + _unpack_p, _unpack_length - _unpack_p); + size_t length = _pack_data.get_length(); + char *buffer = _pack_data.take_data(); + set_unpack_data(buffer, length, true); + _unpack_p = 0; + + _catalog->release_live_catalog(_live_catalog); + _live_catalog = _catalog->get_live_catalog(_unpack_data, _unpack_length); + + if (_live_catalog == NULL) { + _pack_error = true; + return false; + } + + begin = _live_catalog->get_begin(seek_index); + } + + // Now copy the bytes from _unpack_p to begin from the + // _unpack_data to the _pack_data. These are the bytes we just + // skipped over with the call to seek(). + _pack_data.append_data(_unpack_data + _unpack_p, begin - _unpack_p); + + // And set the packer up to pack the indicated field (but no + // subsequent fields). + _current_field = entry._field; + _current_parent = entry._parent; + _current_field_index = entry._field_index; + _num_nested_fields = 1; + _unpack_p = _live_catalog->get_end(seek_index); + + // Set up push_marker and pop_marker so we won't try to advance + // beyond this field. + _push_marker = begin; + _pop_marker = _live_catalog->get_end(seek_index); + + return true; + } + + // Invalid mode. + _pack_error = true; + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: DCPacker::push +// Access: Published +// Description: Marks the beginning of a nested series of fields. +// +// This must be called before filling the elements of an +// array or the individual fields in a structure field. +// It must also be balanced by a matching pop(). +// +// It is necessary to use push() / pop() only if +// has_nested_fields() returns true. +//////////////////////////////////////////////////////////////////// +void DCPacker:: +push() { + if (!has_nested_fields()) { + _pack_error = true; + + } else { + StackElement *element = new StackElement; + element->_current_parent = _current_parent; + element->_current_field_index = _current_field_index; + element->_push_marker = _push_marker; + element->_pop_marker = _pop_marker; + element->_next = _stack; + _stack = element; + _current_parent = _current_field; + + + // Now deal with the length prefix that might or might not be + // before a sequence of nested fields. + int num_nested_fields = _current_parent->get_num_nested_fields(); + size_t length_bytes = _current_parent->get_num_length_bytes(); + + if (_mode == M_pack || _mode == M_repack) { + // Reserve length_bytes for when we figure out what the length + // is. + _push_marker = _pack_data.get_length(); + _pop_marker = 0; + _pack_data.append_junk(length_bytes); + + } else if (_mode == M_unpack) { + // Read length_bytes to determine the end of this nested + // sequence. + _push_marker = _unpack_p; + _pop_marker = 0; + + if (length_bytes != 0) { + if (_unpack_p + length_bytes > _unpack_length) { + _pack_error = true; + + } else { + size_t length; + if (length_bytes == 4) { + length = DCPackerInterface::do_unpack_uint32 + (_unpack_data + _unpack_p); + _unpack_p += 4; + } else { + length = DCPackerInterface::do_unpack_uint16 + (_unpack_data + _unpack_p); + _unpack_p += 2; + } + _pop_marker = _unpack_p + length; + + // The explicit length trumps the number of nested fields + // reported by get_num_nested_fields(). + if (length == 0) { + num_nested_fields = 0; + } else { + num_nested_fields = _current_parent->calc_num_nested_fields(length); + } + } + } + } else { + _pack_error = true; + } + + + // Now point to the first field in the nested range. + _num_nested_fields = num_nested_fields; + _current_field_index = 0; + + if (_num_nested_fields >= 0 && + _current_field_index >= _num_nested_fields) { + _current_field = NULL; + + } else { + _current_field = _current_parent->get_nested_field(_current_field_index); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: DCPacker::pop +// Access: Published +// Description: Marks the end of a nested series of fields. +// +// This must be called to match a previous push() only +// after all the expected number of nested fields have +// been packed. It is an error to call it too early, or +// too late. +//////////////////////////////////////////////////////////////////// +void DCPacker:: +pop() { + if (_current_field != NULL && _num_nested_fields >= 0) { + // Oops, didn't pack or unpack enough values. + _pack_error = true; + + } else if (_mode == M_unpack && _pop_marker != 0 && + _unpack_p != _pop_marker) { + // Didn't unpack the right number of values. + _pack_error = true; + } + + if (_stack == NULL) { + // Unbalanced pop(). + _pack_error = true; + + } else { + if (!_current_parent->validate_num_nested_fields(_current_field_index)) { + // Incorrect number of nested elements. + _pack_error = true; + } + + if (_mode == M_pack || _mode == M_repack) { + size_t length_bytes = _current_parent->get_num_length_bytes(); + if (length_bytes != 0) { + // Now go back and fill in the length of the array. + size_t length = _pack_data.get_length() - _push_marker - length_bytes; + if (length_bytes == 4) { + DCPackerInterface::do_pack_uint32 + (_pack_data.get_rewrite_pointer(_push_marker, 4), length); + } else { + DCPackerInterface::validate_uint_limits(length, 16, _range_error); + DCPackerInterface::do_pack_uint16 + (_pack_data.get_rewrite_pointer(_push_marker, 2), length); + } + } + } + + _current_field = _current_parent; + _current_parent = _stack->_current_parent; + _current_field_index = _stack->_current_field_index; + _push_marker = _stack->_push_marker; + _pop_marker = _stack->_pop_marker; + _num_nested_fields = (_current_parent == NULL) ? 0 : _current_parent->get_num_nested_fields(); + + StackElement *next = _stack->_next; + delete _stack; + _stack = next; + } + + advance(); +} + +//////////////////////////////////////////////////////////////////// +// Function: DCPacker::pack_default_value +// Access: Published +// Description: Adds the default value for the current element into +// the stream. If no default has been set for the +// current element, creates a sensible default. +//////////////////////////////////////////////////////////////////// +void DCPacker:: +pack_default_value() { + nassertv(_mode == M_pack || _mode == M_repack); + if (_current_field == NULL) { + _pack_error = true; + } else { + if (_current_field->pack_default_value(_pack_data, _pack_error)) { + advance(); + + } else { + // If the single field didn't know how to pack a default value, + // try packing nested fields. + push(); + while (more_nested_fields()) { + pack_default_value(); + } + pop(); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: DCPacker::unpack_validate +// Access: Published +// Description: Internally unpacks the current numeric or string +// value and validates it against the type range limits, +// but does not return the value. If the current field +// contains nested fields, validates all of them. +//////////////////////////////////////////////////////////////////// +void DCPacker:: +unpack_validate() { + nassertv(_mode == M_unpack); + if (_current_field == NULL) { + _pack_error = true; + + } else { + if (_current_field->unpack_validate(_unpack_data, _unpack_length, _unpack_p, + _pack_error, _range_error)) { + advance(); + } else { + // If the single field couldn't be validated, try validating + // nested fields. + push(); + while (more_nested_fields()) { + unpack_validate(); + } + pop(); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: DCPacker::unpack_skip +// Access: Published +// Description: Skips the current field without unpacking it and +// advances to the next field. If the current field +// contains nested fields, skips all of them. +//////////////////////////////////////////////////////////////////// +void DCPacker:: +unpack_skip() { + nassertv(_mode == M_unpack); + if (_current_field == NULL) { + _pack_error = true; + + } else { + if (_current_field->unpack_skip(_unpack_data, _unpack_length, _unpack_p, + _pack_error)) { + advance(); + + } else { + // If the single field couldn't be skipped, try skipping nested fields. + push(); + while (more_nested_fields()) { + unpack_skip(); + } + pop(); + } + } +} + +#ifdef HAVE_PYTHON +//////////////////////////////////////////////////////////////////// +// Function: DCPacker::pack_object +// Access: Published +// Description: Packs the Python object of whatever type into the +// packer. Each numeric object and string object maps +// to the corresponding pack_value() call; a tuple or +// sequence maps to a push() followed by all of the +// tuple's contents followed by a pop(). +//////////////////////////////////////////////////////////////////// +void DCPacker:: +pack_object(PyObject *object) { + nassertv(_mode == M_pack || _mode == M_repack); + + #ifdef USE_PYTHON_2_2_OR_EARLIER + if (PyInt_Check(object)) { + #else + if (PyLong_Check(object)) { + pack_int(PyLong_AsUnsignedLong(object)); + } else if (PyInt_Check(object)) { + #endif + pack_int(PyInt_AS_LONG(object)); + } else if (PyFloat_Check(object)) { + pack_double(PyFloat_AS_DOUBLE(object)); + } else if (PyLong_Check(object)) { + pack_int64(PyLong_AsLongLong(object)); + } else if (PyString_Check(object) || PyUnicode_Check(object)) { + char *buffer; + int length; + PyString_AsStringAndSize(object, &buffer, &length); + if (buffer) { + pack_string(string(buffer, length)); + } + } else { + // For some reason, PySequence_Check() is incorrectly reporting + // that a class instance is a sequence, even if it doesn't provide + // __len__, so we double-check by testing for __len__ explicitly. + bool is_sequence = + (PySequence_Check(object) != 0) && + (PyObject_HasAttrString(object, "__len__") != 0); + bool is_instance = false; + + const DCClass *dclass = NULL; + const DCPackerInterface *current_field = get_current_field(); + if (current_field != (DCPackerInterface *)NULL) { + const DCClassParameter *class_param = get_current_field()->as_class_parameter(); + if (class_param != (DCClassParameter *)NULL) { + dclass = class_param->get_class(); + + if (dclass->has_class_def()) { + PyObject *class_def = dclass->get_class_def(); + is_instance = (PyObject_IsInstance(object, dclass->get_class_def()) != 0); + Py_DECREF(class_def); + } + } + } + + // If dclass is not NULL, the packer is expecting a class object. + // There are then two cases: (1) the user has supplied a matching + // class object, or (2) the user has supplied a sequence object. + // Unfortunately, it may be difficult to differentiate these two + // cases, since a class object may also be a sequence object. + + // The rule to differentiate them is: + + // (1) If the supplied class object is an instance of the expected + // class object, it is considered to be a class object. + + // (2) Otherwise, if the supplied class object has a __len__() + // method (i.e. PySequence_Check() returns true), then it is + // considered to be a sequence. + + // (3) Otherwise, it is considered to be a class object. + + if (dclass != (DCClass *)NULL && (is_instance || !is_sequence)) { + // The supplied object is either an instance of the expected + // class object, or it is not a sequence--this is case (1) or + // (3). + pack_class_object(dclass, object); + } else if (is_sequence) { + // The supplied object is not an instance of the expected class + // object, but it is a sequence. This is case (2). + push(); + int size = PySequence_Size(object); + for (int i = 0; i < size; ++i) { + PyObject *element = PySequence_GetItem(object, i); + if (element != (PyObject *)NULL) { + pack_object(element); + Py_DECREF(element); + } else { + cerr << "Unable to extract item " << i << " from sequence.\n"; + } + } + pop(); + } else { + // The supplied object is not a sequence, and we weren't + // expecting a class parameter. This is none of the above, an + // error. + ostringstream strm; + PyObject *str = PyObject_Str(object); + strm << "Don't know how to pack object: " + << PyString_AsString(str) << "\n"; + Py_DECREF(str); + nassert_raise(strm.str()); + _pack_error = true; + } + } +} +#endif // HAVE_PYTHON + +#ifdef HAVE_PYTHON +//////////////////////////////////////////////////////////////////// +// Function: DCPacker::unpack_object +// Access: Published +// Description: Unpacks a Python object of the appropriate type from +// the stream for the current field. This may be an +// integer or a string for a simple field object; if the +// current field represents a list of fields it will be +// a tuple. +//////////////////////////////////////////////////////////////////// +PyObject *DCPacker:: +unpack_object() { + PyObject *object = NULL; + + DCPackType pack_type = get_pack_type(); + + switch (pack_type) { + case PT_invalid: + object = Py_None; + unpack_skip(); + break; + + case PT_double: + { + double value = unpack_double(); + object = PyFloat_FromDouble(value); + } + break; + + case PT_int: + { + int value = unpack_int(); + object = PyInt_FromLong(value); + } + break; + + case PT_uint: + { + unsigned int value = unpack_uint(); + if (value & 0x80000000) { + object = PyLong_FromUnsignedLong(value); + } else { + object = PyInt_FromLong(value); + } + } + break; + + case PT_int64: + { + PN_int64 value = unpack_int64(); + object = PyLong_FromLongLong(value); + } + break; + + case PT_uint64: + { + PN_uint64 value = unpack_uint64(); + object = PyLong_FromUnsignedLongLong(value); + } + break; + + case PT_string: + case PT_blob: + { + string str; + unpack_string(str); + object = PyString_FromStringAndSize(str.data(), str.size()); + } + break; + + case PT_class: + { + const DCClassParameter *class_param = get_current_field()->as_class_parameter(); + if (class_param != (DCClassParameter *)NULL) { + const DCClass *dclass = class_param->get_class(); + if (dclass->has_class_def()) { + // If we know what kind of class object this is and it has a + // valid constructor, create the class object instead of + // just a tuple. + object = unpack_class_object(dclass); + break; + } + } + } + // If we don't know what kind of class object it is, or it doesn't + // have a constructor, fall through and make a tuple. + default: + { + // First, build up a list from the nested objects. + object = PyList_New(0); + + push(); + while (more_nested_fields()) { + PyObject *element = unpack_object(); + PyList_Append(object, element); + Py_DECREF(element); + } + pop(); + + if (pack_type != PT_array) { + // For these other kinds of objects, we'll convert the list + // into a tuple. + PyObject *tuple = PyList_AsTuple(object); + Py_DECREF(object); + object = tuple; + } + } + break; + } + + return object; +} +#endif // HAVE_PYTHON + + +//////////////////////////////////////////////////////////////////// +// Function: DCPacker::parse_and_pack +// Access: Published +// Description: Parses an object's value according to the DC file +// syntax (e.g. as a default value string) and packs it. +// Returns true on success, false on a parse error. +//////////////////////////////////////////////////////////////////// +bool DCPacker:: +parse_and_pack(const string &formatted_object) { + istringstream strm(formatted_object); + return parse_and_pack(strm); +} + +//////////////////////////////////////////////////////////////////// +// Function: DCPacker::parse_and_pack +// Access: Published +// Description: Parses an object's value according to the DC file +// syntax (e.g. as a default value string) and packs it. +// Returns true on success, false on a parse error. +//////////////////////////////////////////////////////////////////// +bool DCPacker:: +parse_and_pack(istream &in) { + dc_init_parser_parameter_value(in, "parse_and_pack", *this); + dcyyparse(); + dc_cleanup_parser(); + + bool parse_error = (dc_error_count() != 0); + if (parse_error) { + _parse_error = true; + } + + return !parse_error; +} + +//////////////////////////////////////////////////////////////////// +// Function: DCPacker::unpack_and_format +// Access: Published +// Description: Unpacks an object and formats its value into a syntax +// suitable for parsing in the dc file (e.g. as a +// default value), or as an input to parse_object. +//////////////////////////////////////////////////////////////////// +string DCPacker:: +unpack_and_format(bool show_field_names) { + ostringstream strm; + unpack_and_format(strm, show_field_names); + return strm.str(); +} + +//////////////////////////////////////////////////////////////////// +// Function: DCPacker::unpack_and_format +// Access: Published +// Description: Unpacks an object and formats its value into a syntax +// suitable for parsing in the dc file (e.g. as a +// default value), or as an input to parse_object. +//////////////////////////////////////////////////////////////////// +void DCPacker:: +unpack_and_format(ostream &out, bool show_field_names) { + DCPackType pack_type = get_pack_type(); + + if (show_field_names && !get_current_field_name().empty()) { + nassertv(_current_field != (DCPackerInterface *)NULL); + const DCField *field = _current_field->as_field(); + if (field != (DCField *)NULL && + field->as_parameter() != (DCParameter *)NULL) { + out << field->get_name() << " = "; + } + } + + switch (pack_type) { + case PT_invalid: + out << ""; + break; + + case PT_double: + out << unpack_double(); + break; + + case PT_int: + out << unpack_int(); + break; + + case PT_uint: + out << unpack_uint(); + break; + + case PT_int64: + out << unpack_int64(); + break; + + case PT_uint64: + out << unpack_uint64(); + break; + + case PT_string: + enquote_string(out, '"', unpack_string()); + break; + + case PT_blob: + output_hex_string(out, unpack_literal_value()); + break; + + default: + { + switch (pack_type) { + case PT_array: + out << '['; + break; + + case PT_field: + case PT_switch: + out << '('; + break; + + case PT_class: + default: + out << '{'; + break; + } + + push(); + while (more_nested_fields() && !had_pack_error()) { + unpack_and_format(out, show_field_names); + + if (more_nested_fields()) { + out << ", "; + } + } + pop(); + + switch (pack_type) { + case PT_array: + out << ']'; + break; + + case PT_field: + case PT_switch: + out << ')'; + break; + + case PT_class: + default: + out << '}'; + break; + } + } + break; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: DCPacker::enquote_string +// Access: Public, Static +// Description: Outputs the indicated string within quotation marks. +//////////////////////////////////////////////////////////////////// +void DCPacker:: +enquote_string(ostream &out, char quote_mark, const string &str) { + out << quote_mark; + for (string::const_iterator pi = str.begin(); + pi != str.end(); + ++pi) { + if ((*pi) == quote_mark || (*pi) == '\\') { + out << '\\' << (*pi); + + } else if (!isprint(*pi)) { + char buffer[10]; + sprintf(buffer, "%02x", (unsigned char)(*pi)); + out << "\\x" << buffer; + + } else { + out << (*pi); + } + } + out << quote_mark; +} + +//////////////////////////////////////////////////////////////////// +// Function: DCPacker::output_hex_string +// Access: Public, Static +// Description: Outputs the indicated string as a hex constant. +//////////////////////////////////////////////////////////////////// +void DCPacker:: +output_hex_string(ostream &out, const string &str) { + out << '<'; + for (string::const_iterator pi = str.begin(); + pi != str.end(); + ++pi) { + char buffer[10]; + sprintf(buffer, "%02x", (unsigned char)(*pi)); + out << buffer; + } + out << '>'; +} + +//////////////////////////////////////////////////////////////////// +// Function: DCPacker::handle_switch +// Access: Private +// Description: When we advance past the key field on a switch +// record, we suddenly have more fields available--all +// the appropriate alternate fields in the switch. +// +// This function is called when we detect this +// condition; it switches the _current_parent to the +// appropriate case of the switch record. +//////////////////////////////////////////////////////////////////// +void DCPacker:: +handle_switch(const DCSwitchParameter *switch_parameter) { + // First, get the value from the key. This is either found in the + // unpack or the pack data, depending on what mode we're in. + const DCPackerInterface *new_parent = NULL; + + if (_mode == M_pack || _mode == M_repack) { + const char *data = _pack_data.get_data(); + new_parent = switch_parameter->apply_switch + (data + _push_marker, _pack_data.get_length() - _push_marker); + + } else if (_mode == M_unpack) { + new_parent = switch_parameter->apply_switch + (_unpack_data + _push_marker, _unpack_p - _push_marker); + } + + if (new_parent == (DCPackerInterface *)NULL) { + // This means an invalid value was packed for the key. + _range_error = true; + return; + } + + _last_switch = switch_parameter; + + // Now substitute in the switch case for the previous parent (which + // replaces the switch node itself). This will suddenly make a slew + // of new fields appear. + _current_parent = new_parent; + _num_nested_fields = _current_parent->get_num_nested_fields(); + + if (_num_nested_fields < 0 || + _current_field_index < _num_nested_fields) { + _current_field = _current_parent->get_nested_field(_current_field_index); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: DCPacker::clear +// Access: Private +// Description: Resets the data structures after a pack or unpack +// sequence. +//////////////////////////////////////////////////////////////////// +void DCPacker:: +clear() { + clear_stack(); + _current_field = NULL; + _current_parent = NULL; + _current_field_index = 0; + _num_nested_fields = 0; + _push_marker = 0; + _pop_marker = 0; + _last_switch = NULL; + + if (_live_catalog != (DCPackerCatalog::LiveCatalog *)NULL) { + _catalog->release_live_catalog(_live_catalog); + _live_catalog = NULL; + } + _catalog = NULL; + _root = NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: DCPacker::clear_stack +// Access: Private +// Description: Empties the stack. +//////////////////////////////////////////////////////////////////// +void DCPacker:: +clear_stack() { + while (_stack != (StackElement *)NULL) { + StackElement *next = _stack->_next; + delete _stack; + _stack = next; + } +} + +#ifdef HAVE_PYTHON +//////////////////////////////////////////////////////////////////// +// Function: DCPacker::pack_class_object +// Access: Private +// Description: Given that the current element is a ClassParameter +// for a Python class object, try to extract the +// appropriate values from the class object and pack in. +//////////////////////////////////////////////////////////////////// +void DCPacker:: +pack_class_object(const DCClass *dclass, PyObject *object) { + push(); + while (more_nested_fields() && !_pack_error) { + const DCField *field = get_current_field()->as_field(); + nassertv(field != (DCField *)NULL); + get_class_element(dclass, object, field); + } + pop(); +} +#endif // HAVE_PYTHON + +#ifdef HAVE_PYTHON +//////////////////////////////////////////////////////////////////// +// Function: DCPacker::unpack_class_object +// Access: Private +// Description: Given that the current element is a ClassParameter +// for a Python class for which we have a valid +// constructor, unpack it and fill in its values. +//////////////////////////////////////////////////////////////////// +PyObject *DCPacker:: +unpack_class_object(const DCClass *dclass) { + PyObject *class_def = dclass->get_class_def(); + nassertr(class_def != (PyObject *)NULL, NULL); + + PyObject *object = NULL; + + if (!dclass->has_constructor()) { + // If the class uses a default constructor, go ahead and create + // the Python object for it now. + object = PyObject_CallObject(class_def, NULL); + if (object == (PyObject *)NULL) { + return NULL; + } + } + + push(); + if (object == (PyObject *)NULL && more_nested_fields()) { + // The first nested field will be the constructor. + const DCField *field = get_current_field()->as_field(); + nassertr(field != (DCField *)NULL, object); + nassertr(field == dclass->get_constructor(), object); + + set_class_element(class_def, object, field); + + // By now, the object should have been constructed. + if (object == (PyObject *)NULL) { + return NULL; + } + } + while (more_nested_fields()) { + const DCField *field = get_current_field()->as_field(); + nassertr(field != (DCField *)NULL, object); + + set_class_element(class_def, object, field); + } + pop(); + + return object; +} +#endif // HAVE_PYTHON + + +#ifdef HAVE_PYTHON +//////////////////////////////////////////////////////////////////// +// Function: DCPacker::set_class_element +// Access: Private +// Description: Unpacks the current element and stuffs it on the +// Python class object in whatever way is appropriate. +//////////////////////////////////////////////////////////////////// +void DCPacker:: +set_class_element(PyObject *class_def, PyObject *&object, + const DCField *field) { + string field_name = field->get_name(); + DCPackType pack_type = get_pack_type(); + + if (field_name.empty()) { + switch (pack_type) { + case PT_class: + case PT_switch: + // If the field has no name, but it is one of these container + // objects, we want to unpack its nested objects directly into + // the class. + push(); + while (more_nested_fields()) { + const DCField *field = get_current_field()->as_field(); + nassertv(field != (DCField *)NULL); + nassertv(object != (PyObject *)NULL); + set_class_element(class_def, object, field); + } + pop(); + break; + + default: + // Otherwise, we just skip over the field. + unpack_skip(); + } + + } else { + // If the field does have a name, we will want to store it on the + // class, either by calling a method (for a PT_field pack_type) or + // by setting a value (for any other kind of pack_type). + + PyObject *element = unpack_object(); + + if (pack_type == PT_field) { + if (object == (PyObject *)NULL) { + // If the object hasn't been constructed yet, assume this is + // the constructor. + object = PyObject_CallObject(class_def, element); + + } else { + if (PyObject_HasAttrString(object, (char *)field_name.c_str())) { + PyObject *func = PyObject_GetAttrString(object, (char *)field_name.c_str()); + if (func != (PyObject *)NULL) { + PyObject *result = PyObject_CallObject(func, element); + Py_XDECREF(result); + Py_DECREF(func); + } + } + } + + } else { + nassertv(object != (PyObject *)NULL); + PyObject_SetAttrString(object, (char *)field_name.c_str(), element); + } + + Py_DECREF(element); + } +} +#endif // HAVE_PYTHON + + +#ifdef HAVE_PYTHON +//////////////////////////////////////////////////////////////////// +// Function: DCPacker::get_class_element +// Access: Private +// Description: Gets the current element from the Python object and +// packs it. +//////////////////////////////////////////////////////////////////// +void DCPacker:: +get_class_element(const DCClass *dclass, PyObject *object, + const DCField *field) { + string field_name = field->get_name(); + DCPackType pack_type = get_pack_type(); + + if (field_name.empty()) { + switch (pack_type) { + case PT_class: + case PT_switch: + // If the field has no name, but it is one of these container + // objects, we want to get its nested objects directly from + // the class. + push(); + while (more_nested_fields() && !_pack_error) { + const DCField *field = get_current_field()->as_field(); + nassertv(field != (DCField *)NULL); + get_class_element(dclass, object, field); + } + pop(); + break; + + default: + // Otherwise, we just pack the default value. + pack_default_value(); + } + + } else { + // If the field does have a name, we will want to get it from the + // class and pack it. It just so happens that there's already a + // method that does this on DCClass. + + if (!dclass->pack_required_field(*this, object, field)) { + _pack_error = true; + } + } +} +#endif // HAVE_PYTHON diff --git a/direct/src/distributed/cConnectionRepository.cxx b/direct/src/distributed/cConnectionRepository.cxx index 37b6b3e2aa..eefcd424bb 100644 --- a/direct/src/distributed/cConnectionRepository.cxx +++ b/direct/src/distributed/cConnectionRepository.cxx @@ -1,528 +1,536 @@ -// Filename: cConnectionRepository.cxx -// Created by: drose (17May04) -// -//////////////////////////////////////////////////////////////////// -// -// 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 "cConnectionRepository.h" -#include "dcmsgtypes.h" -#include "dcClass.h" -#include "dcPacker.h" - -#include "config_distributed.h" -#include "httpChannel.h" -#include "urlSpec.h" -#include "datagramIterator.h" -#include "throw_event.h" -#include "pStatTimer.h" - - -const string CConnectionRepository::_overflow_event_name = "CRDatagramOverflow"; - -#ifndef CPPPARSER -PStatCollector CConnectionRepository::_update_pcollector("App:Show code:readerPollTask:Update"); -#endif // CPPPARSER - -//////////////////////////////////////////////////////////////////// -// Function: CConnectionRepository::Constructor -// Access: Published -// Description: -//////////////////////////////////////////////////////////////////// -CConnectionRepository:: -CConnectionRepository() : -#ifdef HAVE_PYTHON - _python_repository(NULL), -#endif -#ifdef HAVE_SSL - _http_conn(NULL), -#endif -#ifdef HAVE_NSPR - _cw(&_qcm, 0), - _qcr(&_qcm, 0), -#endif - _client_datagram(true), - _simulated_disconnect(false), - _verbose(distributed_cat.is_spam()), - _msg_channel(0), - _msg_sender(0), - _sec_code(0), - _msg_type(0) -{ -#if defined(HAVE_NSPR) && defined(SIMULATE_NETWORK_DELAY) - if (min_lag != 0.0 || max_lag != 0.0) { - _qcr.start_delay(min_lag, max_lag); - } -#endif -} - -//////////////////////////////////////////////////////////////////// -// Function: CConnectionRepository::Destructor -// Access: Published -// Description: -//////////////////////////////////////////////////////////////////// -CConnectionRepository:: -~CConnectionRepository() { - disconnect(); -} - -#ifdef HAVE_SSL -//////////////////////////////////////////////////////////////////// -// Function: CConnectionRepository::set_connection_http -// Access: Published -// Description: Once a connection has been established via the HTTP -// interface, gets the connection and uses it. The -// supplied HTTPChannel object must have a connection -// available via get_connection(). -//////////////////////////////////////////////////////////////////// -void CConnectionRepository:: -set_connection_http(HTTPChannel *channel) { - disconnect(); - nassertv(channel->is_connection_ready()); - _http_conn = channel->get_connection(); -} -#endif // HAVE_SSL - - -#ifdef HAVE_NSPR -//////////////////////////////////////////////////////////////////// -// Function: CConnectionRepository::try_connect_nspr -// Access: Published -// Description: Uses NSPR to try to connect to the server and port -// named in the indicated URL. Returns true if -// successful, false otherwise. -//////////////////////////////////////////////////////////////////// -bool CConnectionRepository:: -try_connect_nspr(const URLSpec &url) { - disconnect(); - - _nspr_conn = - _qcm.open_TCP_client_connection(url.get_server(), url.get_port(), - game_server_timeout_ms); - - if (_nspr_conn != (Connection *)NULL) { - _nspr_conn->set_no_delay(true); - _qcr.add_connection(_nspr_conn); - return true; - } - - return false; -} -#endif // HAVE_NSPR - -//////////////////////////////////////////////////////////////////// -// Function: CConnectionRepository::check_datagram -// Access: Published -// Description: Returns true if a new datagram is available, false -// otherwise. If the return value is true, the new -// datagram may be retrieved via get_datagram(), or -// preferably, with get_datagram_iterator() and -// get_msg_type(). -//////////////////////////////////////////////////////////////////// -bool CConnectionRepository:: -check_datagram() { - if (_simulated_disconnect) { - return false; - } - while (do_check_datagram()) { -#ifndef NDEBUG - if (get_verbose()) { - describe_message(nout, "receive ", _dg.get_message()); - } -#endif // NDEBUG - - // Start breaking apart the datagram. - _di = DatagramIterator(_dg); - - if (!_client_datagram) { - _msg_channel = _di.get_uint64(); - _msg_sender = _di.get_uint64(); - _sec_code = _di.get_uint8(); - -#ifdef HAVE_PYTHON - // For now, we need to stuff this field onto the Python - // structure, to support legacy code that expects to find it - // there. - if (_python_repository != (PyObject *)NULL) { - PyObject *value = PyLong_FromUnsignedLongLong(_msg_sender); - PyObject_SetAttrString(_python_repository, "msgSender", value); - Py_DECREF(value); - } -#endif // HAVE_PYTHON - } - - _msg_type = _di.get_uint16(); - // Is this a message that we can process directly? - switch (_msg_type) { -#ifdef HAVE_PYTHON - case CLIENT_OBJECT_UPDATE_FIELD: - case STATESERVER_OBJECT_UPDATE_FIELD: - if (!handle_update_field()) { - return false; - } - break; -#endif // HAVE_PYTHON - - default: - // Some unknown message; let the caller deal with it. - return true; - } - } - - // No datagrams available. - return false; -} - -//////////////////////////////////////////////////////////////////// -// Function: CConnectionRepository::is_connected -// Access: Published -// Description: Returns true if the connection to the gameserver is -// established and still good, false if we are not -// connected. A false value means either (a) we never -// successfully connected, (b) we explicitly called -// disconnect(), or (c) we were connected, but the -// connection was spontaneously lost. -//////////////////////////////////////////////////////////////////// -bool CConnectionRepository:: -is_connected() { -#ifdef HAVE_NSPR - if (_nspr_conn) { - if (_qcm.reset_connection_available()) { - PT(Connection) reset_connection; - if (_qcm.get_reset_connection(reset_connection)) { - _qcm.close_connection(reset_connection); - if (reset_connection == _nspr_conn) { - // Whoops, lost our connection. - _nspr_conn = NULL; - return false; - } - } - } - return true; - } -#endif // HAVE_NSPR - -#ifdef HAVE_SSL - if (_http_conn) { - if (!_http_conn->is_closed()) { - return true; - } - - // Connection lost. - delete _http_conn; - _http_conn = NULL; - } -#endif // HAVE_SSL - - return false; -} - -//////////////////////////////////////////////////////////////////// -// Function: CConnectionRepository::send_datagram -// Access: Published -// Description: Queues the indicated datagram for sending to the -// server. It may not get send immediately if -// collect_tcp is in effect; call flush() to guarantee -// it is sent now. -//////////////////////////////////////////////////////////////////// -bool CConnectionRepository:: -send_datagram(const Datagram &dg) { - if (_simulated_disconnect) { - distributed_cat.warning() - << "Unable to send datagram during simulated disconnect.\n"; - return false; - } - -#ifndef NDEBUG - if (get_verbose()) { - describe_message(nout, "send ", dg.get_message()); - } -#endif // NDEBUG - -#ifdef HAVE_NSPR - if (_nspr_conn) { - _cw.send(dg, _nspr_conn); - return true; - } -#endif // HAVE_NSPR - -#ifdef HAVE_SSL - if (_http_conn) { - if (!_http_conn->send_datagram(dg)) { - distributed_cat.warning() - << "Could not send datagram.\n"; - return false; - } - - return true; - } -#endif // HAVE_SSL - - distributed_cat.warning() - << "Unable to send datagram after connection is closed.\n"; - return false; -} - -//////////////////////////////////////////////////////////////////// -// Function: CConnectionRepository::consider_flush -// Access: Published -// Description: Sends the most recently queued data if enough time -// has elapsed. This only has meaning if -// set_collect_tcp() has been set to true. -//////////////////////////////////////////////////////////////////// -bool CConnectionRepository:: -consider_flush() { - if (_simulated_disconnect) { - return false; - } - -#ifdef HAVE_NSPR - if (_nspr_conn) { - return _nspr_conn->consider_flush(); - } -#endif // HAVE_NSPR - -#ifdef HAVE_SSL - if (_http_conn) { - return _http_conn->consider_flush(); - } -#endif // HAVE_SSL - - return false; -} - -//////////////////////////////////////////////////////////////////// -// Function: CConnectionRepository::flush -// Access: Published -// Description: Sends the most recently queued data now. This only -// has meaning if set_collect_tcp() has been set to -// true. -//////////////////////////////////////////////////////////////////// -bool CConnectionRepository:: -flush() { - if (_simulated_disconnect) { - return false; - } - -#ifdef HAVE_NSPR - if (_nspr_conn) { - return _nspr_conn->flush(); - } -#endif // HAVE_NSPR - -#ifdef HAVE_SSL - if (_http_conn) { - return _http_conn->flush(); - } -#endif // HAVE_SSL - - return false; -} - -//////////////////////////////////////////////////////////////////// -// Function: CConnectionRepository::disconnect -// Access: Published -// Description: Closes the connection to the server. -//////////////////////////////////////////////////////////////////// -void CConnectionRepository:: -disconnect() { -#ifdef HAVE_NSPR - if (_nspr_conn) { - _qcm.close_connection(_nspr_conn); - _nspr_conn = NULL; - } -#endif // HAVE_NSPR - -#ifdef HAVE_SSL - if (_http_conn) { - _http_conn->close(); - delete _http_conn; - _http_conn = NULL; - } -#endif // HAVE_SSL - - _simulated_disconnect = false; -} - -//////////////////////////////////////////////////////////////////// -// Function: CConnectionRepository::do_check_datagram -// Access: Private -// Description: The private implementation of check_datagram(), this -// gets one datagram if it is available. -//////////////////////////////////////////////////////////////////// -bool CConnectionRepository:: -do_check_datagram() { -#ifdef HAVE_NSPR - if (_nspr_conn) { - _nspr_conn->consider_flush(); - if (_qcr.get_overflow_flag()) { - throw_event(get_overflow_event_name()); - _qcr.reset_overflow_flag(); - } - return (_qcr.data_available() && _qcr.get_data(_dg)); - } -#endif // HAVE_NSPR - -#ifdef HAVE_SSL - if (_http_conn) { - _http_conn->consider_flush(); - return _http_conn->receive_datagram(_dg); - } -#endif // HAVE_SSL - - return false; -} - -//////////////////////////////////////////////////////////////////// -// Function: CConnectionRepository::handle_update_field -// Access: Private -// Description: Directly handles an update message on a field. -// Python never touches the datagram; it just gets its -// distributed method called with the appropriate -// parameters. Returns true if everything is ok, false -// if there was an error processing the field's update -// method. -//////////////////////////////////////////////////////////////////// -bool CConnectionRepository:: -handle_update_field() { -#ifdef HAVE_PYTHON - PStatTimer timer(_update_pcollector); - int do_id = _di.get_uint32(); - if (_python_repository != (PyObject *)NULL) { - PyObject *doId2do = - PyObject_GetAttrString(_python_repository, "doId2do"); - nassertr(doId2do != NULL, false); - - PyObject *doId = PyInt_FromLong(do_id); - PyObject *distobj = PyDict_GetItem(doId2do, doId); - Py_DECREF(doId); - Py_DECREF(doId2do); - - if (distobj != NULL) { - PyObject *dclass_obj = PyObject_GetAttrString(distobj, "dclass"); - nassertr(dclass_obj != NULL, false); - - PyObject *dclass_this = PyObject_GetAttrString(dclass_obj, "this"); - Py_DECREF(dclass_obj); - nassertr(dclass_this != NULL, false); - - DCClass *dclass = (DCClass *)PyInt_AsLong(dclass_this); - Py_DECREF(dclass_this); - - // It's a good idea to ensure the reference count to distobj is - // raised while we call the update method--otherwise, the update - // method might get into trouble if it tried to delete the - // object from the doId2do map. - Py_INCREF(distobj); - dclass->receive_update(distobj, _di); - Py_DECREF(distobj); - - if (PyErr_Occurred()) { - return false; - } - } - } -#endif // HAVE_PYTHON - - return true; -} - -#ifndef NDEBUG -//////////////////////////////////////////////////////////////////// -// Function: CConnectionRepository::describe_message -// Access: Private -// Description: Unpacks the message and reformats it for user -// consumption, writing a description on the indicated -// output stream. -//////////////////////////////////////////////////////////////////// -void CConnectionRepository:: -describe_message(ostream &out, const string &prefix, - const string &message_data) const { - DCPacker packer; - packer.set_unpack_data(message_data); - CHANNEL_TYPE do_id; - int msg_type; - bool is_update = false; - - if (!_client_datagram) { - packer.RAW_UNPACK_CHANNEL(); // msg_channel - packer.RAW_UNPACK_CHANNEL(); // msg_sender - packer.raw_unpack_uint8(); // sec_code - msg_type = packer.raw_unpack_uint16(); - is_update = (msg_type == STATESERVER_OBJECT_UPDATE_FIELD); - - } else { - msg_type = packer.raw_unpack_uint16(); - is_update = (msg_type == CLIENT_OBJECT_UPDATE_FIELD); - } - - if (!is_update) { - out << prefix << "message " << msg_type << "\n"; - - } else { - // It's an update message. Figure out what dclass the object is - // based on its doId, so we can decode the rest of the message. - do_id = packer.raw_unpack_uint32(); - DCClass *dclass = NULL; - -#ifdef HAVE_PYTHON - if (_python_repository != (PyObject *)NULL) { - PyObject *doId2do = - PyObject_GetAttrString(_python_repository, "doId2do"); - nassertv(doId2do != NULL); - - PyObject *doId = PyInt_FromLong(do_id); - PyObject *distobj = PyDict_GetItem(doId2do, doId); - Py_DECREF(doId); - Py_DECREF(doId2do); - - if (distobj != NULL) { - PyObject *dclass_obj = PyObject_GetAttrString(distobj, "dclass"); - nassertv(dclass_obj != NULL); - - PyObject *dclass_this = PyObject_GetAttrString(dclass_obj, "this"); - Py_DECREF(dclass_obj); - nassertv(dclass_this != NULL); - - dclass = (DCClass *)PyInt_AsLong(dclass_this); - Py_DECREF(dclass_this); - } - } -#endif // HAVE_PYTHON - - int field_id = packer.raw_unpack_uint16(); - - if (dclass == (DCClass *)NULL) { - out << prefix << "update for unknown object " << do_id - << ", field " << field_id << "\n"; - - } else { - out << prefix << "update for " << dclass->get_name() - << " " << do_id << ": "; - DCField *field = dclass->get_field_by_index(field_id); - if (field == (DCField *)NULL) { - out << "unknown field " << field_id << "\n"; - - } else { - out << field->get_name(); - packer.begin_unpack(field); - packer.unpack_and_format(out); - packer.end_unpack(); - out << "\n"; - } - } - } -} -#endif // NDEBUG - +// Filename: cConnectionRepository.cxx +// Created by: drose (17May04) +// +//////////////////////////////////////////////////////////////////// +// +// 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 "cConnectionRepository.h" +#include "dcmsgtypes.h" +#include "dcClass.h" +#include "dcPacker.h" + +#include "config_distributed.h" +#include "httpChannel.h" +#include "urlSpec.h" +#include "datagramIterator.h" +#include "throw_event.h" +#include "pStatTimer.h" + + +const string CConnectionRepository::_overflow_event_name = "CRDatagramOverflow"; + +#ifndef CPPPARSER +PStatCollector CConnectionRepository::_update_pcollector("App:Show code:readerPollTask:Update"); +#endif // CPPPARSER + +//////////////////////////////////////////////////////////////////// +// Function: CConnectionRepository::Constructor +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +CConnectionRepository:: +CConnectionRepository() : +#ifdef HAVE_PYTHON + _python_repository(NULL), +#endif +#ifdef HAVE_SSL + _http_conn(NULL), +#endif +#ifdef HAVE_NSPR + _cw(&_qcm, 0), + _qcr(&_qcm, 0), +#endif + _client_datagram(true), + _simulated_disconnect(false), + _verbose(distributed_cat.is_spam()), + _msg_channel(0), + _msg_sender(0), + _sec_code(0), + _msg_type(0) +{ +#if defined(HAVE_NSPR) && defined(SIMULATE_NETWORK_DELAY) + if (min_lag != 0.0 || max_lag != 0.0) { + _qcr.start_delay(min_lag, max_lag); + } +#endif +} + +//////////////////////////////////////////////////////////////////// +// Function: CConnectionRepository::Destructor +// Access: Published +// Description: +//////////////////////////////////////////////////////////////////// +CConnectionRepository:: +~CConnectionRepository() { + disconnect(); +} + +#ifdef HAVE_SSL +//////////////////////////////////////////////////////////////////// +// Function: CConnectionRepository::set_connection_http +// Access: Published +// Description: Once a connection has been established via the HTTP +// interface, gets the connection and uses it. The +// supplied HTTPChannel object must have a connection +// available via get_connection(). +//////////////////////////////////////////////////////////////////// +void CConnectionRepository:: +set_connection_http(HTTPChannel *channel) { + disconnect(); + nassertv(channel->is_connection_ready()); + _http_conn = channel->get_connection(); +} +#endif // HAVE_SSL + + +#ifdef HAVE_NSPR +//////////////////////////////////////////////////////////////////// +// Function: CConnectionRepository::try_connect_nspr +// Access: Published +// Description: Uses NSPR to try to connect to the server and port +// named in the indicated URL. Returns true if +// successful, false otherwise. +//////////////////////////////////////////////////////////////////// +bool CConnectionRepository:: +try_connect_nspr(const URLSpec &url) { + disconnect(); + + _nspr_conn = + _qcm.open_TCP_client_connection(url.get_server(), url.get_port(), + game_server_timeout_ms); + + if (_nspr_conn != (Connection *)NULL) { + _nspr_conn->set_no_delay(true); + _qcr.add_connection(_nspr_conn); + return true; + } + + return false; +} +#endif // HAVE_NSPR + +//////////////////////////////////////////////////////////////////// +// Function: CConnectionRepository::check_datagram +// Access: Published +// Description: Returns true if a new datagram is available, false +// otherwise. If the return value is true, the new +// datagram may be retrieved via get_datagram(), or +// preferably, with get_datagram_iterator() and +// get_msg_type(). +//////////////////////////////////////////////////////////////////// +bool CConnectionRepository:: +check_datagram() { + if (_simulated_disconnect) { + return false; + } + while (do_check_datagram()) { +#ifndef NDEBUG + if (get_verbose()) { + describe_message(nout, "receive ", _dg.get_message()); + } +#endif // NDEBUG + + // Start breaking apart the datagram. + _di = DatagramIterator(_dg); + + if (!_client_datagram) { + _msg_channel = _di.get_uint64(); + _msg_sender = _di.get_uint64(); + _sec_code = _di.get_uint8(); + +#ifdef HAVE_PYTHON + // For now, we need to stuff this field onto the Python + // structure, to support legacy code that expects to find it + // there. + if (_python_repository != (PyObject *)NULL) { + PyObject *value = PyLong_FromUnsignedLongLong(_msg_sender); + PyObject_SetAttrString(_python_repository, "msgSender", value); + Py_DECREF(value); + } +#endif // HAVE_PYTHON + } + + _msg_type = _di.get_uint16(); + // Is this a message that we can process directly? + switch (_msg_type) { +#ifdef HAVE_PYTHON + case CLIENT_OBJECT_UPDATE_FIELD: + case STATESERVER_OBJECT_UPDATE_FIELD: + if (!handle_update_field()) { + return false; + } + break; +#endif // HAVE_PYTHON + + default: + // Some unknown message; let the caller deal with it. + return true; + } + } + + // No datagrams available. + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: CConnectionRepository::is_connected +// Access: Published +// Description: Returns true if the connection to the gameserver is +// established and still good, false if we are not +// connected. A false value means either (a) we never +// successfully connected, (b) we explicitly called +// disconnect(), or (c) we were connected, but the +// connection was spontaneously lost. +//////////////////////////////////////////////////////////////////// +bool CConnectionRepository:: +is_connected() { +#ifdef HAVE_NSPR + if (_nspr_conn) { + if (_qcm.reset_connection_available()) { + PT(Connection) reset_connection; + if (_qcm.get_reset_connection(reset_connection)) { + _qcm.close_connection(reset_connection); + if (reset_connection == _nspr_conn) { + // Whoops, lost our connection. + _nspr_conn = NULL; + return false; + } + } + } + return true; + } +#endif // HAVE_NSPR + +#ifdef HAVE_SSL + if (_http_conn) { + if (!_http_conn->is_closed()) { + return true; + } + + // Connection lost. + delete _http_conn; + _http_conn = NULL; + } +#endif // HAVE_SSL + + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: CConnectionRepository::send_datagram +// Access: Published +// Description: Queues the indicated datagram for sending to the +// server. It may not get send immediately if +// collect_tcp is in effect; call flush() to guarantee +// it is sent now. +//////////////////////////////////////////////////////////////////// +bool CConnectionRepository:: +send_datagram(const Datagram &dg) { + if (_simulated_disconnect) { + distributed_cat.warning() + << "Unable to send datagram during simulated disconnect.\n"; + return false; + } + +#ifndef NDEBUG + if (get_verbose()) { + describe_message(nout, "send ", dg.get_message()); + } +#endif // NDEBUG + +#ifdef HAVE_NSPR + if (_nspr_conn) { + _cw.send(dg, _nspr_conn); + return true; + } +#endif // HAVE_NSPR + +#ifdef HAVE_SSL + if (_http_conn) { + if (!_http_conn->send_datagram(dg)) { + distributed_cat.warning() + << "Could not send datagram.\n"; + return false; + } + + return true; + } +#endif // HAVE_SSL + + distributed_cat.warning() + << "Unable to send datagram after connection is closed.\n"; + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: CConnectionRepository::consider_flush +// Access: Published +// Description: Sends the most recently queued data if enough time +// has elapsed. This only has meaning if +// set_collect_tcp() has been set to true. +//////////////////////////////////////////////////////////////////// +bool CConnectionRepository:: +consider_flush() { + if (_simulated_disconnect) { + return false; + } + +#ifdef HAVE_NSPR + if (_nspr_conn) { + return _nspr_conn->consider_flush(); + } +#endif // HAVE_NSPR + +#ifdef HAVE_SSL + if (_http_conn) { + return _http_conn->consider_flush(); + } +#endif // HAVE_SSL + + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: CConnectionRepository::flush +// Access: Published +// Description: Sends the most recently queued data now. This only +// has meaning if set_collect_tcp() has been set to +// true. +//////////////////////////////////////////////////////////////////// +bool CConnectionRepository:: +flush() { + if (_simulated_disconnect) { + return false; + } + +#ifdef HAVE_NSPR + if (_nspr_conn) { + return _nspr_conn->flush(); + } +#endif // HAVE_NSPR + +#ifdef HAVE_SSL + if (_http_conn) { + return _http_conn->flush(); + } +#endif // HAVE_SSL + + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: CConnectionRepository::disconnect +// Access: Published +// Description: Closes the connection to the server. +//////////////////////////////////////////////////////////////////// +void CConnectionRepository:: +disconnect() { +#ifdef HAVE_NSPR + if (_nspr_conn) { + _qcm.close_connection(_nspr_conn); + _nspr_conn = NULL; + } +#endif // HAVE_NSPR + +#ifdef HAVE_SSL + if (_http_conn) { + _http_conn->close(); + delete _http_conn; + _http_conn = NULL; + } +#endif // HAVE_SSL + + _simulated_disconnect = false; +} + +//////////////////////////////////////////////////////////////////// +// Function: CConnectionRepository::do_check_datagram +// Access: Private +// Description: The private implementation of check_datagram(), this +// gets one datagram if it is available. +//////////////////////////////////////////////////////////////////// +bool CConnectionRepository:: +do_check_datagram() { +#ifdef HAVE_NSPR + if (_nspr_conn) { + _nspr_conn->consider_flush(); + if (_qcr.get_overflow_flag()) { + throw_event(get_overflow_event_name()); + _qcr.reset_overflow_flag(); + } + return (_qcr.data_available() && _qcr.get_data(_dg)); + } +#endif // HAVE_NSPR + +#ifdef HAVE_SSL + if (_http_conn) { + _http_conn->consider_flush(); + return _http_conn->receive_datagram(_dg); + } +#endif // HAVE_SSL + + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: CConnectionRepository::handle_update_field +// Access: Private +// Description: Directly handles an update message on a field. +// Python never touches the datagram; it just gets its +// distributed method called with the appropriate +// parameters. Returns true if everything is ok, false +// if there was an error processing the field's update +// method. +//////////////////////////////////////////////////////////////////// +bool CConnectionRepository:: +handle_update_field() { +#ifdef HAVE_PYTHON + PStatTimer timer(_update_pcollector); + unsigned int do_id = _di.get_uint32(); + if (_python_repository != (PyObject *)NULL) { + PyObject *doId2do = + PyObject_GetAttrString(_python_repository, "doId2do"); + nassertr(doId2do != NULL, false); + + #ifdef USE_PYTHON_2_2_OR_EARLIER + PyObject *doId = PyInt_FromLong(do_id); + #else + PyObject *doId = PyLong_FromUnsignedLong(do_id); + #endif + PyObject *distobj = PyDict_GetItem(doId2do, doId); + Py_DECREF(doId); + Py_DECREF(doId2do); + + if (distobj != NULL) { + PyObject *dclass_obj = PyObject_GetAttrString(distobj, "dclass"); + nassertr(dclass_obj != NULL, false); + + PyObject *dclass_this = PyObject_GetAttrString(dclass_obj, "this"); + Py_DECREF(dclass_obj); + nassertr(dclass_this != NULL, false); + + DCClass *dclass = (DCClass *)PyInt_AsLong(dclass_this); + Py_DECREF(dclass_this); + + // It's a good idea to ensure the reference count to distobj is + // raised while we call the update method--otherwise, the update + // method might get into trouble if it tried to delete the + // object from the doId2do map. + Py_INCREF(distobj); + dclass->receive_update(distobj, _di); + Py_DECREF(distobj); + + if (PyErr_Occurred()) { + return false; + } + } + } +#endif // HAVE_PYTHON + + return true; +} + +#ifndef NDEBUG +//////////////////////////////////////////////////////////////////// +// Function: CConnectionRepository::describe_message +// Access: Private +// Description: Unpacks the message and reformats it for user +// consumption, writing a description on the indicated +// output stream. +//////////////////////////////////////////////////////////////////// +void CConnectionRepository:: +describe_message(ostream &out, const string &prefix, + const string &message_data) const { + DCPacker packer; + packer.set_unpack_data(message_data); + CHANNEL_TYPE do_id; + int msg_type; + bool is_update = false; + + if (!_client_datagram) { + packer.RAW_UNPACK_CHANNEL(); // msg_channel + packer.RAW_UNPACK_CHANNEL(); // msg_sender + packer.raw_unpack_uint8(); // sec_code + msg_type = packer.raw_unpack_uint16(); + is_update = (msg_type == STATESERVER_OBJECT_UPDATE_FIELD); + + } else { + msg_type = packer.raw_unpack_uint16(); + is_update = (msg_type == CLIENT_OBJECT_UPDATE_FIELD); + } + + if (!is_update) { + out << prefix << "message " << msg_type << "\n"; + + } else { + // It's an update message. Figure out what dclass the object is + // based on its doId, so we can decode the rest of the message. + do_id = packer.raw_unpack_uint32(); + DCClass *dclass = NULL; + +#ifdef HAVE_PYTHON + if (_python_repository != (PyObject *)NULL) { + PyObject *doId2do = + PyObject_GetAttrString(_python_repository, "doId2do"); + nassertv(doId2do != NULL); + + #ifdef USE_PYTHON_2_2_OR_EARLIER + PyObject *doId = PyInt_FromLong(do_id); + #else + PyObject *doId = PyLong_FromUnsignedLong(do_id); + #endif + PyObject *distobj = PyDict_GetItem(doId2do, doId); + Py_DECREF(doId); + Py_DECREF(doId2do); + + if (distobj != NULL) { + PyObject *dclass_obj = PyObject_GetAttrString(distobj, "dclass"); + nassertv(dclass_obj != NULL); + + PyObject *dclass_this = PyObject_GetAttrString(dclass_obj, "this"); + Py_DECREF(dclass_obj); + nassertv(dclass_this != NULL); + + dclass = (DCClass *)PyInt_AsLong(dclass_this); + Py_DECREF(dclass_this); + } + } +#endif // HAVE_PYTHON + + int field_id = packer.raw_unpack_uint16(); + + if (dclass == (DCClass *)NULL) { + out << prefix << "update for unknown object " << do_id + << ", field " << field_id << "\n"; + + } else { + out << prefix << "update for " << dclass->get_name() + << " " << do_id << ": "; + DCField *field = dclass->get_field_by_index(field_id); + if (field == (DCField *)NULL) { + out << "unknown field " << field_id << "\n"; + + } else { + out << field->get_name(); + packer.begin_unpack(field); + packer.unpack_and_format(out); + packer.end_unpack(); + out << "\n"; + } + } + } +} +#endif // NDEBUG + diff --git a/dtool/src/interrogate/interfaceMakerPythonObj.cxx b/dtool/src/interrogate/interfaceMakerPythonObj.cxx index 4b71941277..541258061f 100644 --- a/dtool/src/interrogate/interfaceMakerPythonObj.cxx +++ b/dtool/src/interrogate/interfaceMakerPythonObj.cxx @@ -1,637 +1,650 @@ -// Filename: interfaceMakerPythonObj.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 "interfaceMakerPythonObj.h" -#include "interrogateBuilder.h" -#include "interrogate.h" -#include "functionRemap.h" -#include "parameterRemapUnchanged.h" -#include "typeManager.h" -#include "functionWriterPtrFromPython.h" -#include "functionWriterPtrToPython.h" - -#include "interrogateDatabase.h" -#include "interrogateType.h" -#include "interrogateFunction.h" -#include "cppFunctionType.h" - -//////////////////////////////////////////////////////////////////// -// Function: InterfaceMakerPythonObj::Constructor -// Access: Public -// Description: -//////////////////////////////////////////////////////////////////// -InterfaceMakerPythonObj:: -InterfaceMakerPythonObj(InterrogateModuleDef *def) : - InterfaceMakerPython(def) -{ -} - -//////////////////////////////////////////////////////////////////// -// Function: InterfaceMakerPythonObj::Destructor -// Access: Public, Virtual -// Description: -//////////////////////////////////////////////////////////////////// -InterfaceMakerPythonObj:: -~InterfaceMakerPythonObj() { -} - -//////////////////////////////////////////////////////////////////// -// Function: InterfaceMakerPythonObj::write_prototypes -// Access: Public, Virtual -// Description: Generates the list of function prototypes -// corresponding to the functions that will be output in -// write_functions(). -//////////////////////////////////////////////////////////////////// -void InterfaceMakerPythonObj:: -write_prototypes(ostream &out,ostream *out_h) { - Functions::iterator fi; - for (fi = _functions.begin(); fi != _functions.end(); ++fi) { - Function *func = (*fi); - write_prototype_for(out, func); - } - - out << "\n"; - InterfaceMakerPython::write_prototypes(out,out_h); -} - -//////////////////////////////////////////////////////////////////// -// Function: InterfaceMakerPythonObj::write_functions -// Access: Public, Virtual -// Description: Generates the list of functions that are appropriate -// for this interface. This function is called *before* -// write_prototypes(), above. -//////////////////////////////////////////////////////////////////// -void InterfaceMakerPythonObj:: -write_functions(ostream &out) { - Functions::iterator fi; - for (fi = _functions.begin(); fi != _functions.end(); ++fi) { - Function *func = (*fi); - write_function_for(out, func); - } - - InterfaceMakerPython::write_functions(out); - - // Finally, generate all the make-class-wrapper functions. - Objects::iterator oi; - for (oi = _objects.begin(); oi != _objects.end(); ++oi) { - Object *object = (*oi).second; - - write_class_wrapper(out, object); - } -} - -//////////////////////////////////////////////////////////////////// -// Function: InterfaceMakerPythonObj::write_module -// Access: Public, Virtual -// Description: Generates whatever additional code is required to -// support a module file. -//////////////////////////////////////////////////////////////////// -void InterfaceMakerPythonObj:: -write_module(ostream &out,ostream *out_h, InterrogateModuleDef *def) { - InterfaceMakerPython::write_module(out,out_h, def); - - out << "static PyMethodDef python_obj_funcs[] = {\n"; - - Objects::iterator oi; - for (oi = _objects.begin(); oi != _objects.end(); ++oi) { - Object *object = (*oi).second; - - Functions::iterator fi; - for (fi = object->_constructors.begin(); - fi != object->_constructors.end(); - ++fi) { - Function *func = (*fi); - out << " { \"" << func->_ifunc.get_name() << "\", &" << func->_name - << ", METH_VARARGS },\n"; - } - } - out << " { NULL, NULL }\n" - << "};\n\n" - - << "#ifdef _WIN32\n" - << "extern \"C\" __declspec(dllexport) void init" << def->library_name << "();\n" - << "#else\n" - << "extern \"C\" void init" << def->library_name << "();\n" - << "#endif\n\n" - - << "void init" << def->library_name << "() {\n" - << " Py_InitModule(\"" << def->library_name - << "\", python_obj_funcs);\n" - << "}\n\n"; -} - -//////////////////////////////////////////////////////////////////// -// Function: InterfaceMakerPythonObj::synthesize_this_parameter -// Access: Public, Virtual -// Description: 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 InterfaceMakerPythonObj:: -synthesize_this_parameter() { - return true; -} - -//////////////////////////////////////////////////////////////////// -// Function: InterfaceMakerPythonObj::get_builder_name -// Access: Public, Static -// Description: Returns the name of the InterfaceMaker function -// generated to define the Python class for the -// indicated struct type. -//////////////////////////////////////////////////////////////////// -string InterfaceMakerPythonObj:: -get_builder_name(CPPType *struct_type) { - return "get_python_class_" + - InterrogateBuilder::clean_identifier(struct_type->get_local_name(&parser)); -} - -//////////////////////////////////////////////////////////////////// -// Function: InterfaceMakerPythonObj::get_wrapper_prefix -// Access: Protected, Virtual -// Description: Returns the prefix string used to generate wrapper -// function names. -//////////////////////////////////////////////////////////////////// -string InterfaceMakerPythonObj:: -get_wrapper_prefix() { - return "wpo_"; -} - -//////////////////////////////////////////////////////////////////// -// Function: InterfaceMakerPythonObj::write_class_wrapper -// Access: Private -// Description: Writes a function that will define the Python class. -//////////////////////////////////////////////////////////////////// -void InterfaceMakerPythonObj:: -write_class_wrapper(ostream &out, InterfaceMaker::Object *object) { - CPPType *struct_type = object->_itype._cpptype; - if (struct_type == (CPPType *)NULL) { - return; - } - - string name = get_builder_name(struct_type); - string python_name = - InterrogateBuilder::clean_identifier(struct_type->get_simple_name()); - - out << "/*\n" - << " * Generate unique Python class for " - << struct_type->get_local_name(&parser) << "\n" - << " */\n" - << "PyObject *\n" - << name << "() {\n" - << " static PyObject *wrapper = (PyObject *)NULL;\n" - << " static PyMethodDef methods[] = {\n"; - - int methods_size = 0; - int class_methods_size = 0; - - Functions::iterator fi; - for (fi = object->_methods.begin(); fi != object->_methods.end(); ++fi) { - Function *func = (*fi); - if (func->_has_this) { - out << " { \"" << func->_ifunc.get_name() << "\", &" << func->_name - << ", METH_VARARGS },\n"; - methods_size++; - } - } - - out << " };\n" - << " static const int methods_size = " << methods_size << ";\n\n" - << " static PyMethodDef class_methods[] = {\n"; - - for (fi = object->_methods.begin(); fi != object->_methods.end(); ++fi) { - Function *func = (*fi); - if (!func->_has_this) { - out << " { \"" << func->_ifunc.get_name() << "\", &" << func->_name - << ", METH_VARARGS },\n"; - class_methods_size++; - } - } - - out << " };\n" - << " static const int class_methods_size = " << class_methods_size << ";\n\n" - << " if (wrapper == (PyObject *)NULL) {\n" - << " int i;\n" - << " PyObject *bases = PyTuple_New(0);\n" - << " PyObject *dict = PyDict_New();\n" - << " PyObject *name = PyString_FromString(\"" - << python_name << "\");\n" - << " wrapper = PyClass_New(bases, dict, name);\n" - << " for (i = 0; i < methods_size; i++) {\n" - << " PyObject *function, *method;\n" - << " function = PyCFunction_New(&methods[i], (PyObject *)NULL);\n" - << " method = PyMethod_New(function, (PyObject *)NULL, wrapper);\n" - << " PyDict_SetItemString(dict, methods[i].ml_name, method);\n" - << " }\n" - << " for (i = 0; i < class_methods_size; i++) {\n" - << " PyObject *function;\n" - << " function = PyCFunction_New(&class_methods[i], (PyObject *)NULL);\n" - << " PyDict_SetItemString(dict, class_methods[i].ml_name, function);\n" - << " }\n" - << " }\n" - << " return wrapper;\n" - << "}\n\n"; -} - -//////////////////////////////////////////////////////////////////// -// Function: InterfaceMakerPythonObj::write_prototype_for -// Access: Private -// Description: Writes the prototype for the indicated function. -//////////////////////////////////////////////////////////////////// -void InterfaceMakerPythonObj:: -write_prototype_for(ostream &out, InterfaceMaker::Function *func) { - out << "static PyObject *" - << func->_name << "(PyObject *self, PyObject *args);\n"; -} - -//////////////////////////////////////////////////////////////////// -// Function: InterfaceMakerPythonObj::write_function_for -// Access: Private -// Description: Writes the definition for a function that will call -// the indicated C++ function or method. -//////////////////////////////////////////////////////////////////// -void InterfaceMakerPythonObj:: -write_function_for(ostream &out, InterfaceMaker::Function *func) { - Function::Remaps::const_iterator ri; - out << "/*\n" - << " * Python object wrapper for\n"; - for (ri = func->_remaps.begin(); ri != func->_remaps.end(); ++ri) { - FunctionRemap *remap = (*ri); - out << " * "; - remap->write_orig_prototype(out, 0); - out << "\n"; - } - out << " */\n"; - - out << "static PyObject *" - << func->_name << "(PyObject *, PyObject *args) {\n"; - - // Now write out each instance of the overloaded function. - string expected_params = "Arguments must match one of:"; - - for (ri = func->_remaps.begin(); ri != func->_remaps.end(); ++ri) { - FunctionRemap *remap = (*ri); - expected_params += "\\n "; - write_function_instance(out, 2, func, remap, expected_params); - } - - // If we get here in the generated code, none of the parameters were - // valid. Generate an error exception. (We don't rely on the error - // already generated by ParseTuple(), because it only reports the - // error for one flavor of the function, whereas we might accept - // multiple flavors for the different overloaded C++ function - // signatures. - - out << " PyErr_SetString(PyExc_TypeError, \"" << expected_params << "\");\n" - << " return (PyObject *)NULL;\n"; - - out << "}\n\n"; -} - -//////////////////////////////////////////////////////////////////// -// Function: InterfaceMakerPythonObj::write_function_instance -// Access: Private -// Description: Writes out the part of a function that handles a -// single instance of an overloaded function. -//////////////////////////////////////////////////////////////////// -void InterfaceMakerPythonObj:: -write_function_instance(ostream &out, int indent_level, - InterfaceMaker::Function *func, - FunctionRemap *remap, string &expected_params) { - indent(out, indent_level) << "{\n"; - indent(out, indent_level + 2) << "/* "; - remap->write_orig_prototype(out, 0); - out << " */\n\n"; - - string format_specifiers; - string parameter_list; - vector_string pexprs; - string extra_convert; - string extra_param_check; - string extra_cleanup; - - // Make one pass through the parameter list. We will output a - // one-line temporary variable definition for each parameter, while - // simultaneously building the ParseTuple() function call and also - // the parameter expression list for call_function(). - - expected_params += remap->_cppfunc->get_simple_name(); - expected_params += "("; - - int pn; - for (pn = 0; pn < (int)remap->_parameters.size(); pn++) { - if (pn != 0) { - expected_params += ", "; - } - - indent(out, indent_level + 2); - CPPType *orig_type = remap->_parameters[pn]._remap->get_orig_type(); - CPPType *type = remap->_parameters[pn]._remap->get_new_type(); - string param_name = remap->get_parameter_name(pn); - - // This is the string to convert our local variable to the - // appropriate C++ type. Normally this is just a cast. - string pexpr_string = - "(" + type->get_local_name(&parser) + ")" + param_name; - - if (remap->_parameters[pn]._remap->new_type_is_atomic_string()) { - if (TypeManager::is_char_pointer(orig_type)) { - out << "char *" << param_name; - format_specifiers += "s"; - parameter_list += ", &" + param_name; - - } else { - out << "char *" << param_name - << "_str; int " << param_name << "_len"; - format_specifiers += "s#"; - parameter_list += ", &" + param_name - + "_str, &" + param_name + "_len"; - pexpr_string = "basic_string(" + - param_name + "_str, " + - param_name + "_len)"; - } - expected_params += "string"; - - } else if (TypeManager::is_bool(type)) { - out << "PyObject *" << param_name; - format_specifiers += "O"; - parameter_list += ", &" + param_name; - pexpr_string = "(PyObject_IsTrue(" + param_name + ")!=0)"; - expected_params += "bool"; - - } else if (TypeManager::is_unsigned_longlong(type)) { - out << "PyObject *" << param_name; - format_specifiers += "O"; - parameter_list += ", &" + param_name; - extra_convert += " PyObject *" + param_name + "_long = PyNumber_Long(" + param_name + ");"; - extra_param_check += "|| (" + param_name + "_long == NULL)"; - pexpr_string = "PyLong_AsUnsignedLongLong(" + param_name + "_long)"; - extra_cleanup += " Py_XDECREF(" + param_name + "_long);"; - expected_params += "long"; - - } else if (TypeManager::is_longlong(type)) { - out << "PyObject *" << param_name; - format_specifiers += "O"; - parameter_list += ", &" + param_name; - extra_convert += " PyObject *" + param_name + "_long = PyNumber_Long(" + param_name + ");"; - extra_param_check += "|| (" + param_name + "_long == NULL)"; - pexpr_string = "PyLong_AsLongLong(" + param_name + "_long)"; - extra_cleanup += " Py_XDECREF(" + param_name + "_long);"; - expected_params += "long"; - - } else if (TypeManager::is_integer(type)) { - out << "int " << param_name; - format_specifiers += "i"; - parameter_list += ", &" + param_name; - expected_params += "integer"; - - } else if (TypeManager::is_float(type)) { - out << "double " << param_name; - format_specifiers += "d"; - parameter_list += ", &" + param_name; - expected_params += "float"; - - } else if (TypeManager::is_char_pointer(type)) { - out << "char *" << param_name; - format_specifiers += "s"; - parameter_list += ", &" + param_name; - expected_params += "string"; - - } else if (TypeManager::is_pointer(type)) { - FunctionWriterPtrFromPython *writer = get_ptr_from_python(type); - writer->get_pointer_type()->output_instance(out, param_name, &parser); - format_specifiers += "O&"; - parameter_list += ", &" + writer->get_name() + ", &" + param_name; - expected_params += writer->get_type()->get_preferred_name(); - - } else { - // Ignore a parameter. - out << "PyObject *" << param_name; - format_specifiers += "O"; - parameter_list += ", &" + param_name; - expected_params += "any"; - } - - if (remap->_parameters[pn]._has_name) { - expected_params += " " + remap->_parameters[pn]._name; - } - - out << ";\n"; - pexprs.push_back(pexpr_string); - } - expected_params += ")"; - - indent(out, indent_level + 2) - << "if (PyArg_ParseTuple(args, \"" << format_specifiers - << "\"" << parameter_list << ")) {\n"; - - if (!extra_convert.empty()) { - indent(out, indent_level + 3) - << extra_convert << "\n"; - } - - if (!extra_param_check.empty()) { - indent(out, indent_level + 4) - << "if (" << extra_param_check.substr(3) << ") {\n"; - if (!extra_cleanup.empty()) { - indent(out, indent_level + 5) - << extra_cleanup << "\n"; - } - indent(out, indent_level + 6) - << "PyErr_SetString(PyExc_TypeError, \"Invalid parameters.\");\n"; - indent(out, indent_level + 6) - << "return (PyObject *)NULL;\n"; - indent(out, indent_level + 4) - << "}\n"; - } - - if (track_interpreter) { - indent(out, indent_level + 4) - << "in_interpreter = 0;\n"; - } - - if (!remap->_void_return && - remap->_return_type->new_type_is_atomic_string()) { - // Treat strings as a special case. We don't want to format the - // return expression. - string return_expr = - remap->call_function(out, indent_level + 4, false, "param0", pexprs); - - CPPType *type = remap->_return_type->get_orig_type(); - indent(out, indent_level + 4); - type->output_instance(out, "return_value", &parser); - out << " = " << return_expr << ";\n"; - - if (track_interpreter) { - indent(out, indent_level + 4) - << "in_interpreter = 1;\n"; - } - if (!extra_cleanup.empty()) { - indent(out, indent_level + 3) - << extra_cleanup << "\n"; - } - - return_expr = manage_return_value(out, indent_level + 4, remap, "return_value"); - test_assert(out, indent_level + 4); - pack_return_value(out, indent_level + 4, remap, return_expr); - - } else { - string return_expr = - remap->call_function(out, indent_level + 4, true, "param0", pexprs); - if (return_expr.empty()) { - if (track_interpreter) { - indent(out, indent_level + 4) - << "in_interpreter = 1;\n"; - } - if (!extra_cleanup.empty()) { - indent(out, indent_level + 3) - << extra_cleanup << "\n"; - } - test_assert(out, indent_level + 4); - indent(out, indent_level + 4) - << "return Py_BuildValue(\"\");\n"; - - } else { - CPPType *type = remap->_return_type->get_temporary_type(); - indent(out, indent_level + 4); - type->output_instance(out, "return_value", &parser); - out << " = " << return_expr << ";\n"; - if (track_interpreter) { - indent(out, indent_level + 4) - << "in_interpreter = 1;\n"; - } - if (!extra_cleanup.empty()) { - indent(out, indent_level + 3) - << extra_cleanup << "\n"; - } - - return_expr = manage_return_value(out, indent_level + 4, remap, "return_value"); - test_assert(out, indent_level + 4); - pack_return_value(out, indent_level + 4, remap, remap->_return_type->temporary_to_return(return_expr)); - } - } - - indent(out, indent_level + 2) << "}\n"; - indent(out, indent_level + 2) - << "PyErr_Clear();\n"; // Clear the error generated by ParseTuple() - indent(out, indent_level) - << "}\n"; -} - -//////////////////////////////////////////////////////////////////// -// Function: InterfaceMakerPythonObj::pack_return_value -// Access: Private -// Description: Outputs a command to pack the indicated expression, -// of the return_type type, as a Python return value. -//////////////////////////////////////////////////////////////////// -void InterfaceMakerPythonObj:: -pack_return_value(ostream &out, int indent_level, - FunctionRemap *remap, string return_expr) { - CPPType *orig_type = remap->_return_type->get_orig_type(); - CPPType *type = remap->_return_type->get_new_type(); - - if (remap->_return_type->new_type_is_atomic_string()) { - if (TypeManager::is_char_pointer(orig_type)) { - indent(out, indent_level) - << "return PyString_FromString(" << return_expr << ");\n"; - - } else { - indent(out, indent_level) - << "return PyString_FromStringAndSize(" - << return_expr << ".data(), " << return_expr << ".length());\n"; - } - - } else if (TypeManager::is_unsigned_longlong(type)) { - indent(out, indent_level) - << "return PyLong_FromUnsignedLongLong(" << return_expr << ");\n"; - - } else if (TypeManager::is_longlong(type)) { - indent(out, indent_level) - << "return PyLong_FromLongLong(" << return_expr << ");\n"; - - } else if (TypeManager::is_integer(type)) { - indent(out, indent_level) - << "return PyInt_FromLong(" << return_expr << ");\n"; - - } else if (TypeManager::is_float(type)) { - indent(out, indent_level) - << "return PyFloat_FromDouble(" << return_expr << ");\n"; - - } else if (TypeManager::is_char_pointer(type)) { - indent(out, indent_level) - << "return PyString_FromString(" << return_expr << ");\n"; - - } else if (TypeManager::is_pointer(type)) { - bool caller_manages = remap->_return_value_needs_management; - - FunctionWriterPtrToPython *writer = get_ptr_to_python(type); - indent(out, indent_level) - << "return " << writer->get_name() << "((" - << writer->get_pointer_type()->get_local_name(&parser) << ")" - << return_expr << ", " << caller_manages << ");\n"; - - } else { - // Return None. - indent(out, indent_level) - << "return Py_BuildValue(\"\");\n"; - } -} - -//////////////////////////////////////////////////////////////////// -// Function: InterfaceMakerPythonObj::get_ptr_from_python -// Access: Private -// Description: Returns a FunctionWriter pointer suitable for -// converting from a Python wrapper of the indicated -// type to the corresponding C++ pointer. -//////////////////////////////////////////////////////////////////// -FunctionWriterPtrFromPython *InterfaceMakerPythonObj:: -get_ptr_from_python(CPPType *type) { - PtrConverter::iterator ci; - ci = _from_python.find(type); - if (ci != _from_python.end()) { - // We've previously used this type. - return (FunctionWriterPtrFromPython *)(*ci).second; - } - - FunctionWriter *writer = - _function_writers.add_writer(new FunctionWriterPtrFromPython(type)); - _from_python.insert(PtrConverter::value_type(type, writer)); - return (FunctionWriterPtrFromPython *)writer; -} - -//////////////////////////////////////////////////////////////////// -// Function: InterfaceMakerPythonObj::get_ptr_to_python -// Access: Private -// Description: Returns a FunctionWriter pointer suitable for -// converting from a C++ pointer of the indicated -// type to the corresponding Python wrapper. -//////////////////////////////////////////////////////////////////// -FunctionWriterPtrToPython *InterfaceMakerPythonObj:: -get_ptr_to_python(CPPType *type) { - PtrConverter::iterator ci; - ci = _to_python.find(type); - if (ci != _to_python.end()) { - // We've previously used this type. - return (FunctionWriterPtrToPython *)(*ci).second; - } - - FunctionWriter *writer = - _function_writers.add_writer(new FunctionWriterPtrToPython(type)); - _to_python.insert(PtrConverter::value_type(type, writer)); - return (FunctionWriterPtrToPython *)writer; -} +// Filename: interfaceMakerPythonObj.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 "interfaceMakerPythonObj.h" +#include "interrogateBuilder.h" +#include "interrogate.h" +#include "functionRemap.h" +#include "parameterRemapUnchanged.h" +#include "typeManager.h" +#include "functionWriterPtrFromPython.h" +#include "functionWriterPtrToPython.h" + +#include "interrogateDatabase.h" +#include "interrogateType.h" +#include "interrogateFunction.h" +#include "cppFunctionType.h" + +//////////////////////////////////////////////////////////////////// +// Function: InterfaceMakerPythonObj::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +InterfaceMakerPythonObj:: +InterfaceMakerPythonObj(InterrogateModuleDef *def) : + InterfaceMakerPython(def) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: InterfaceMakerPythonObj::Destructor +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +InterfaceMakerPythonObj:: +~InterfaceMakerPythonObj() { +} + +//////////////////////////////////////////////////////////////////// +// Function: InterfaceMakerPythonObj::write_prototypes +// Access: Public, Virtual +// Description: Generates the list of function prototypes +// corresponding to the functions that will be output in +// write_functions(). +//////////////////////////////////////////////////////////////////// +void InterfaceMakerPythonObj:: +write_prototypes(ostream &out,ostream *out_h) { + Functions::iterator fi; + for (fi = _functions.begin(); fi != _functions.end(); ++fi) { + Function *func = (*fi); + write_prototype_for(out, func); + } + + out << "\n"; + InterfaceMakerPython::write_prototypes(out,out_h); +} + +//////////////////////////////////////////////////////////////////// +// Function: InterfaceMakerPythonObj::write_functions +// Access: Public, Virtual +// Description: Generates the list of functions that are appropriate +// for this interface. This function is called *before* +// write_prototypes(), above. +//////////////////////////////////////////////////////////////////// +void InterfaceMakerPythonObj:: +write_functions(ostream &out) { + Functions::iterator fi; + for (fi = _functions.begin(); fi != _functions.end(); ++fi) { + Function *func = (*fi); + write_function_for(out, func); + } + + InterfaceMakerPython::write_functions(out); + + // Finally, generate all the make-class-wrapper functions. + Objects::iterator oi; + for (oi = _objects.begin(); oi != _objects.end(); ++oi) { + Object *object = (*oi).second; + + write_class_wrapper(out, object); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: InterfaceMakerPythonObj::write_module +// Access: Public, Virtual +// Description: Generates whatever additional code is required to +// support a module file. +//////////////////////////////////////////////////////////////////// +void InterfaceMakerPythonObj:: +write_module(ostream &out,ostream *out_h, InterrogateModuleDef *def) { + InterfaceMakerPython::write_module(out,out_h, def); + + out << "static PyMethodDef python_obj_funcs[] = {\n"; + + Objects::iterator oi; + for (oi = _objects.begin(); oi != _objects.end(); ++oi) { + Object *object = (*oi).second; + + Functions::iterator fi; + for (fi = object->_constructors.begin(); + fi != object->_constructors.end(); + ++fi) { + Function *func = (*fi); + out << " { \"" << func->_ifunc.get_name() << "\", &" << func->_name + << ", METH_VARARGS },\n"; + } + } + out << " { NULL, NULL }\n" + << "};\n\n" + + << "#ifdef _WIN32\n" + << "extern \"C\" __declspec(dllexport) void init" << def->library_name << "();\n" + << "#else\n" + << "extern \"C\" void init" << def->library_name << "();\n" + << "#endif\n\n" + + << "void init" << def->library_name << "() {\n" + << " Py_InitModule(\"" << def->library_name + << "\", python_obj_funcs);\n" + << "}\n\n"; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterfaceMakerPythonObj::synthesize_this_parameter +// Access: Public, Virtual +// Description: 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 InterfaceMakerPythonObj:: +synthesize_this_parameter() { + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterfaceMakerPythonObj::get_builder_name +// Access: Public, Static +// Description: Returns the name of the InterfaceMaker function +// generated to define the Python class for the +// indicated struct type. +//////////////////////////////////////////////////////////////////// +string InterfaceMakerPythonObj:: +get_builder_name(CPPType *struct_type) { + return "get_python_class_" + + InterrogateBuilder::clean_identifier(struct_type->get_local_name(&parser)); +} + +//////////////////////////////////////////////////////////////////// +// Function: InterfaceMakerPythonObj::get_wrapper_prefix +// Access: Protected, Virtual +// Description: Returns the prefix string used to generate wrapper +// function names. +//////////////////////////////////////////////////////////////////// +string InterfaceMakerPythonObj:: +get_wrapper_prefix() { + return "wpo_"; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterfaceMakerPythonObj::write_class_wrapper +// Access: Private +// Description: Writes a function that will define the Python class. +//////////////////////////////////////////////////////////////////// +void InterfaceMakerPythonObj:: +write_class_wrapper(ostream &out, InterfaceMaker::Object *object) { + CPPType *struct_type = object->_itype._cpptype; + if (struct_type == (CPPType *)NULL) { + return; + } + + string name = get_builder_name(struct_type); + string python_name = + InterrogateBuilder::clean_identifier(struct_type->get_simple_name()); + + out << "/*\n" + << " * Generate unique Python class for " + << struct_type->get_local_name(&parser) << "\n" + << " */\n" + << "PyObject *\n" + << name << "() {\n" + << " static PyObject *wrapper = (PyObject *)NULL;\n" + << " static PyMethodDef methods[] = {\n"; + + int methods_size = 0; + int class_methods_size = 0; + + Functions::iterator fi; + for (fi = object->_methods.begin(); fi != object->_methods.end(); ++fi) { + Function *func = (*fi); + if (func->_has_this) { + out << " { \"" << func->_ifunc.get_name() << "\", &" << func->_name + << ", METH_VARARGS },\n"; + methods_size++; + } + } + + out << " };\n" + << " static const int methods_size = " << methods_size << ";\n\n" + << " static PyMethodDef class_methods[] = {\n"; + + for (fi = object->_methods.begin(); fi != object->_methods.end(); ++fi) { + Function *func = (*fi); + if (!func->_has_this) { + out << " { \"" << func->_ifunc.get_name() << "\", &" << func->_name + << ", METH_VARARGS },\n"; + class_methods_size++; + } + } + + out << " };\n" + << " static const int class_methods_size = " << class_methods_size << ";\n\n" + << " if (wrapper == (PyObject *)NULL) {\n" + << " int i;\n" + << " PyObject *bases = PyTuple_New(0);\n" + << " PyObject *dict = PyDict_New();\n" + << " PyObject *name = PyString_FromString(\"" + << python_name << "\");\n" + << " wrapper = PyClass_New(bases, dict, name);\n" + << " for (i = 0; i < methods_size; ++i) {\n" + << " PyObject *function, *method;\n" + << " function = PyCFunction_New(&methods[i], (PyObject *)NULL);\n" + << " method = PyMethod_New(function, (PyObject *)NULL, wrapper);\n" + << " PyDict_SetItemString(dict, methods[i].ml_name, method);\n" + << " }\n" + << " for (i = 0; i < class_methods_size; ++i) {\n" + << " PyObject *function;\n" + << " function = PyCFunction_New(&class_methods[i], (PyObject *)NULL);\n" + << " PyDict_SetItemString(dict, class_methods[i].ml_name, function);\n" + << " }\n" + << " }\n" + << " return wrapper;\n" + << "}\n\n"; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterfaceMakerPythonObj::write_prototype_for +// Access: Private +// Description: Writes the prototype for the indicated function. +//////////////////////////////////////////////////////////////////// +void InterfaceMakerPythonObj:: +write_prototype_for(ostream &out, InterfaceMaker::Function *func) { + out << "static PyObject *" + << func->_name << "(PyObject *self, PyObject *args);\n"; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterfaceMakerPythonObj::write_function_for +// Access: Private +// Description: Writes the definition for a function that will call +// the indicated C++ function or method. +//////////////////////////////////////////////////////////////////// +void InterfaceMakerPythonObj:: +write_function_for(ostream &out, InterfaceMaker::Function *func) { + Function::Remaps::const_iterator ri; + out << "/*\n" + << " * Python object wrapper for\n"; + for (ri = func->_remaps.begin(); ri != func->_remaps.end(); ++ri) { + FunctionRemap *remap = (*ri); + out << " * "; + remap->write_orig_prototype(out, 0); + out << "\n"; + } + out << " */\n"; + + out << "static PyObject *" + << func->_name << "(PyObject *, PyObject *args) {\n"; + + // Now write out each instance of the overloaded function. + string expected_params = "Arguments must match one of:"; + + for (ri = func->_remaps.begin(); ri != func->_remaps.end(); ++ri) { + FunctionRemap *remap = (*ri); + expected_params += "\\n "; + write_function_instance(out, 2, func, remap, expected_params); + } + + // If we get here in the generated code, none of the parameters were + // valid. Generate an error exception. (We don't rely on the error + // already generated by ParseTuple(), because it only reports the + // error for one flavor of the function, whereas we might accept + // multiple flavors for the different overloaded C++ function + // signatures. + + out << " PyErr_SetString(PyExc_TypeError, \"" << expected_params << "\");\n" + << " return (PyObject *)NULL;\n"; + + out << "}\n\n"; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterfaceMakerPythonObj::write_function_instance +// Access: Private +// Description: Writes out the part of a function that handles a +// single instance of an overloaded function. +//////////////////////////////////////////////////////////////////// +void InterfaceMakerPythonObj:: +write_function_instance(ostream &out, int indent_level, + InterfaceMaker::Function *func, + FunctionRemap *remap, string &expected_params) { + indent(out, indent_level) << "{\n"; + indent(out, indent_level + 2) << "/* "; + remap->write_orig_prototype(out, 0); + out << " */\n\n"; + + string format_specifiers; + string parameter_list; + vector_string pexprs; + string extra_convert; + string extra_param_check; + string extra_cleanup; + + // Make one pass through the parameter list. We will output a + // one-line temporary variable definition for each parameter, while + // simultaneously building the ParseTuple() function call and also + // the parameter expression list for call_function(). + + expected_params += remap->_cppfunc->get_simple_name(); + expected_params += "("; + + int pn; + for (pn = 0; pn < (int)remap->_parameters.size(); pn++) { + if (pn != 0) { + expected_params += ", "; + } + + indent(out, indent_level + 2); + CPPType *orig_type = remap->_parameters[pn]._remap->get_orig_type(); + CPPType *type = remap->_parameters[pn]._remap->get_new_type(); + string param_name = remap->get_parameter_name(pn); + + // This is the string to convert our local variable to the + // appropriate C++ type. Normally this is just a cast. + string pexpr_string = + "(" + type->get_local_name(&parser) + ")" + param_name; + + if (remap->_parameters[pn]._remap->new_type_is_atomic_string()) { + if (TypeManager::is_char_pointer(orig_type)) { + out << "char *" << param_name; + format_specifiers += "s"; + parameter_list += ", &" + param_name; + + } else { + out << "char *" << param_name + << "_str; int " << param_name << "_len"; + format_specifiers += "s#"; + parameter_list += ", &" + param_name + + "_str, &" + param_name + "_len"; + pexpr_string = "basic_string(" + + param_name + "_str, " + + param_name + "_len)"; + } + expected_params += "string"; + + } else if (TypeManager::is_bool(type)) { + out << "PyObject *" << param_name; + format_specifiers += "O"; + parameter_list += ", &" + param_name; + pexpr_string = "(PyObject_IsTrue(" + param_name + ")!=0)"; + expected_params += "bool"; + + } else if (TypeManager::is_unsigned_longlong(type)) { + out << "PyObject *" << param_name; + format_specifiers += "O"; + parameter_list += ", &" + param_name; + extra_convert += " PyObject *" + param_name + "_long = PyNumber_Long(" + param_name + ");"; + extra_param_check += "|| (" + param_name + "_long == NULL)"; + pexpr_string = "PyLong_AsUnsignedLongLong(" + param_name + "_long)"; + extra_cleanup += " Py_XDECREF(" + param_name + "_long);"; + expected_params += "long"; + + } else if (TypeManager::is_longlong(type)) { + out << "PyObject *" << param_name; + format_specifiers += "O"; + parameter_list += ", &" + param_name; + extra_convert += " PyObject *" + param_name + "_long = PyNumber_Long(" + param_name + ");"; + extra_param_check += "|| (" + param_name + "_long == NULL)"; + pexpr_string = "PyLong_AsLongLong(" + param_name + "_long)"; + extra_cleanup += " Py_XDECREF(" + param_name + "_long);"; + expected_params += "long"; + + #ifndef USE_PYTHON_2_2_OR_EARLIER + } else if (TypeManager::is_unsigned_integer(type)) { + out << "unsigned int " << param_name; + format_specifiers += "I"; // This requires Python 2.3 or better + parameter_list += ", &" + param_name; + #endif + + } else if (TypeManager::is_integer(type)) { + out << "int " << param_name; + format_specifiers += "i"; + parameter_list += ", &" + param_name; + expected_params += "integer"; + + } else if (TypeManager::is_float(type)) { + out << "double " << param_name; + format_specifiers += "d"; + parameter_list += ", &" + param_name; + expected_params += "float"; + + } else if (TypeManager::is_char_pointer(type)) { + out << "char *" << param_name; + format_specifiers += "s"; + parameter_list += ", &" + param_name; + expected_params += "string"; + + } else if (TypeManager::is_pointer(type)) { + FunctionWriterPtrFromPython *writer = get_ptr_from_python(type); + writer->get_pointer_type()->output_instance(out, param_name, &parser); + format_specifiers += "O&"; + parameter_list += ", &" + writer->get_name() + ", &" + param_name; + expected_params += writer->get_type()->get_preferred_name(); + + } else { + // Ignore a parameter. + out << "PyObject *" << param_name; + format_specifiers += "O"; + parameter_list += ", &" + param_name; + expected_params += "any"; + } + + if (remap->_parameters[pn]._has_name) { + expected_params += " " + remap->_parameters[pn]._name; + } + + out << ";\n"; + pexprs.push_back(pexpr_string); + } + expected_params += ")"; + + indent(out, indent_level + 2) + << "if (PyArg_ParseTuple(args, \"" << format_specifiers + << "\"" << parameter_list << ")) {\n"; + + if (!extra_convert.empty()) { + indent(out, indent_level + 3) + << extra_convert << "\n"; + } + + if (!extra_param_check.empty()) { + indent(out, indent_level + 4) + << "if (" << extra_param_check.substr(3) << ") {\n"; + if (!extra_cleanup.empty()) { + indent(out, indent_level + 5) + << extra_cleanup << "\n"; + } + indent(out, indent_level + 6) + << "PyErr_SetString(PyExc_TypeError, \"Invalid parameters.\");\n"; + indent(out, indent_level + 6) + << "return (PyObject *)NULL;\n"; + indent(out, indent_level + 4) + << "}\n"; + } + + if (track_interpreter) { + indent(out, indent_level + 4) + << "in_interpreter = 0;\n"; + } + + if (!remap->_void_return && + remap->_return_type->new_type_is_atomic_string()) { + // Treat strings as a special case. We don't want to format the + // return expression. + string return_expr = + remap->call_function(out, indent_level + 4, false, "param0", pexprs); + + CPPType *type = remap->_return_type->get_orig_type(); + indent(out, indent_level + 4); + type->output_instance(out, "return_value", &parser); + out << " = " << return_expr << ";\n"; + + if (track_interpreter) { + indent(out, indent_level + 4) + << "in_interpreter = 1;\n"; + } + if (!extra_cleanup.empty()) { + indent(out, indent_level + 3) + << extra_cleanup << "\n"; + } + + return_expr = manage_return_value(out, indent_level + 4, remap, "return_value"); + test_assert(out, indent_level + 4); + pack_return_value(out, indent_level + 4, remap, return_expr); + + } else { + string return_expr = + remap->call_function(out, indent_level + 4, true, "param0", pexprs); + if (return_expr.empty()) { + if (track_interpreter) { + indent(out, indent_level + 4) + << "in_interpreter = 1;\n"; + } + if (!extra_cleanup.empty()) { + indent(out, indent_level + 3) + << extra_cleanup << "\n"; + } + test_assert(out, indent_level + 4); + indent(out, indent_level + 4) + << "return Py_BuildValue(\"\");\n"; + + } else { + CPPType *type = remap->_return_type->get_temporary_type(); + indent(out, indent_level + 4); + type->output_instance(out, "return_value", &parser); + out << " = " << return_expr << ";\n"; + if (track_interpreter) { + indent(out, indent_level + 4) + << "in_interpreter = 1;\n"; + } + if (!extra_cleanup.empty()) { + indent(out, indent_level + 3) + << extra_cleanup << "\n"; + } + + return_expr = manage_return_value(out, indent_level + 4, remap, "return_value"); + test_assert(out, indent_level + 4); + pack_return_value(out, indent_level + 4, remap, remap->_return_type->temporary_to_return(return_expr)); + } + } + + indent(out, indent_level + 2) << "}\n"; + indent(out, indent_level + 2) + << "PyErr_Clear();\n"; // Clear the error generated by ParseTuple() + indent(out, indent_level) + << "}\n"; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterfaceMakerPythonObj::pack_return_value +// Access: Private +// Description: Outputs a command to pack the indicated expression, +// of the return_type type, as a Python return value. +//////////////////////////////////////////////////////////////////// +void InterfaceMakerPythonObj:: +pack_return_value(ostream &out, int indent_level, + FunctionRemap *remap, string return_expr) { + CPPType *orig_type = remap->_return_type->get_orig_type(); + CPPType *type = remap->_return_type->get_new_type(); + + if (remap->_return_type->new_type_is_atomic_string()) { + if (TypeManager::is_char_pointer(orig_type)) { + indent(out, indent_level) + << "return PyString_FromString(" << return_expr << ");\n"; + + } else { + indent(out, indent_level) + << "return PyString_FromStringAndSize(" + << return_expr << ".data(), " << return_expr << ".length());\n"; + } + + } else if (TypeManager::is_unsigned_longlong(type)) { + indent(out, indent_level) + << "return PyLong_FromUnsignedLongLong(" << return_expr << ");\n"; + + } else if (TypeManager::is_longlong(type)) { + indent(out, indent_level) + << "return PyLong_FromLongLong(" << return_expr << ");\n"; + + #ifndef USE_PYTHON_2_2_OR_EARLIER + } else if (TypeManager::is_unsigned_integer(type)) { + indent(out, indent_level) + << "return PyLong_FromUnsignedLong(" << return_expr << ");\n"; + #endif + + } else if (TypeManager::is_integer(type)) { + indent(out, indent_level) + << "return PyInt_FromLong(" << return_expr << ");\n"; + + } else if (TypeManager::is_float(type)) { + indent(out, indent_level) + << "return PyFloat_FromDouble(" << return_expr << ");\n"; + + } else if (TypeManager::is_char_pointer(type)) { + indent(out, indent_level) + << "return PyString_FromString(" << return_expr << ");\n"; + + } else if (TypeManager::is_pointer(type)) { + bool caller_manages = remap->_return_value_needs_management; + + FunctionWriterPtrToPython *writer = get_ptr_to_python(type); + indent(out, indent_level) + << "return " << writer->get_name() << "((" + << writer->get_pointer_type()->get_local_name(&parser) << ")" + << return_expr << ", " << caller_manages << ");\n"; + + } else { + // Return None. + indent(out, indent_level) + << "return Py_BuildValue(\"\");\n"; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: InterfaceMakerPythonObj::get_ptr_from_python +// Access: Private +// Description: Returns a FunctionWriter pointer suitable for +// converting from a Python wrapper of the indicated +// type to the corresponding C++ pointer. +//////////////////////////////////////////////////////////////////// +FunctionWriterPtrFromPython *InterfaceMakerPythonObj:: +get_ptr_from_python(CPPType *type) { + PtrConverter::iterator ci; + ci = _from_python.find(type); + if (ci != _from_python.end()) { + // We've previously used this type. + return (FunctionWriterPtrFromPython *)(*ci).second; + } + + FunctionWriter *writer = + _function_writers.add_writer(new FunctionWriterPtrFromPython(type)); + _from_python.insert(PtrConverter::value_type(type, writer)); + return (FunctionWriterPtrFromPython *)writer; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterfaceMakerPythonObj::get_ptr_to_python +// Access: Private +// Description: Returns a FunctionWriter pointer suitable for +// converting from a C++ pointer of the indicated +// type to the corresponding Python wrapper. +//////////////////////////////////////////////////////////////////// +FunctionWriterPtrToPython *InterfaceMakerPythonObj:: +get_ptr_to_python(CPPType *type) { + PtrConverter::iterator ci; + ci = _to_python.find(type); + if (ci != _to_python.end()) { + // We've previously used this type. + return (FunctionWriterPtrToPython *)(*ci).second; + } + + FunctionWriter *writer = + _function_writers.add_writer(new FunctionWriterPtrToPython(type)); + _to_python.insert(PtrConverter::value_type(type, writer)); + return (FunctionWriterPtrToPython *)writer; +} diff --git a/dtool/src/interrogate/interfaceMakerPythonSimple.cxx b/dtool/src/interrogate/interfaceMakerPythonSimple.cxx index 40f683a585..5611a7fbf2 100644 --- a/dtool/src/interrogate/interfaceMakerPythonSimple.cxx +++ b/dtool/src/interrogate/interfaceMakerPythonSimple.cxx @@ -1,484 +1,497 @@ -// Filename: interfaceMakerPythonSimple.cxx -// Created by: drose (01Oct01) -// -//////////////////////////////////////////////////////////////////// -// -// 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 "interfaceMakerPythonSimple.h" -#include "interrogateBuilder.h" -#include "interrogate.h" -#include "functionRemap.h" -#include "parameterRemapUnchanged.h" -#include "typeManager.h" - -#include "interrogateDatabase.h" -#include "interrogateType.h" -#include "interrogateFunction.h" -#include "cppFunctionType.h" - -//////////////////////////////////////////////////////////////////// -// Function: InterfaceMakerPythonSimple::Constructor -// Access: Public -// Description: -//////////////////////////////////////////////////////////////////// -InterfaceMakerPythonSimple:: -InterfaceMakerPythonSimple(InterrogateModuleDef *def) : - InterfaceMakerPython(def) -{ -} - -//////////////////////////////////////////////////////////////////// -// Function: InterfaceMakerPythonSimple::Destructor -// Access: Public, Virtual -// Description: -//////////////////////////////////////////////////////////////////// -InterfaceMakerPythonSimple:: -~InterfaceMakerPythonSimple() { -} - -//////////////////////////////////////////////////////////////////// -// Function: InterfaceMakerPythonSimple::write_prototypes -// Access: Public, Virtual -// Description: Generates the list of function prototypes -// corresponding to the functions that will be output in -// write_functions(). -//////////////////////////////////////////////////////////////////// -void InterfaceMakerPythonSimple:: -write_prototypes(ostream &out,ostream *out_h) { - Functions::iterator fi; - for (fi = _functions.begin(); fi != _functions.end(); ++fi) { - Function *func = (*fi); - write_prototype_for(out, func); - } - - out << "\n"; - InterfaceMakerPython::write_prototypes(out,out_h); -} - -//////////////////////////////////////////////////////////////////// -// Function: InterfaceMakerPythonSimple::write_functions -// Access: Public, Virtual -// Description: Generates the list of functions that are appropriate -// for this interface. This function is called *before* -// write_prototypes(), above. -//////////////////////////////////////////////////////////////////// -void InterfaceMakerPythonSimple:: -write_functions(ostream &out) { - Functions::iterator fi; - for (fi = _functions.begin(); fi != _functions.end(); ++fi) { - Function *func = (*fi); - write_function_for(out, func); - } - - InterfaceMakerPython::write_functions(out); -} - -//////////////////////////////////////////////////////////////////// -// Function: InterfaceMakerPythonSimple::write_module -// Access: Public, Virtual -// Description: Generates whatever additional code is required to -// support a module file. -//////////////////////////////////////////////////////////////////// -void InterfaceMakerPythonSimple:: -write_module(ostream &out,ostream *out_h, InterrogateModuleDef *def) { - InterfaceMakerPython::write_module(out,out_h, def); - - out << "static PyMethodDef python_simple_funcs[] = {\n"; - - Functions::iterator fi; - for (fi = _functions.begin(); fi != _functions.end(); ++fi) { - Function *func = (*fi); - Function::Remaps::const_iterator ri; - for (ri = func->_remaps.begin(); ri != func->_remaps.end(); ++ri) { - FunctionRemap *remap = (*ri); - out << " { \"" << remap->_reported_name << "\", &" - << remap->_wrapper_name << ", METH_VARARGS },\n"; - } - } - out << " { NULL, NULL }\n" - << "};\n\n" - - << "#ifdef _WIN32\n" - << "extern \"C\" __declspec(dllexport) void init" << def->library_name << "();\n" - << "#else\n" - << "extern \"C\" void init" << def->library_name << "();\n" - << "#endif\n\n" - - << "void init" << def->library_name << "() {\n" - << " Py_InitModule(\"" << def->library_name - << "\", python_simple_funcs);\n" - << "}\n\n"; -} - -//////////////////////////////////////////////////////////////////// -// Function: InterfaceMakerPythonSimple::synthesize_this_parameter -// Access: Public, Virtual -// Description: 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 InterfaceMakerPythonSimple:: -synthesize_this_parameter() { - return true; -} - -//////////////////////////////////////////////////////////////////// -// Function: InterfaceMakerPythonSimple::get_wrapper_prefix -// Access: Protected, Virtual -// Description: Returns the prefix string used to generate wrapper -// function names. -//////////////////////////////////////////////////////////////////// -string InterfaceMakerPythonSimple:: -get_wrapper_prefix() { - return "_inP"; -} - -//////////////////////////////////////////////////////////////////// -// Function: InterfaceMakerPythonSimple::get_unique_prefix -// Access: Protected, Virtual -// Description: Returns the prefix string used to generate unique -// symbolic names, which are not necessarily C-callable -// function names. -//////////////////////////////////////////////////////////////////// -string InterfaceMakerPythonSimple:: -get_unique_prefix() { - return "p"; -} - -//////////////////////////////////////////////////////////////////// -// Function: InterfaceMakerPythonSimple::record_function_wrapper -// Access: Protected, Virtual -// Description: Associates the function wrapper with its function in -// the appropriate structures in the database. -//////////////////////////////////////////////////////////////////// -void InterfaceMakerPythonSimple:: -record_function_wrapper(InterrogateFunction &ifunc, - FunctionWrapperIndex wrapper_index) { - ifunc._python_wrappers.push_back(wrapper_index); -} - -//////////////////////////////////////////////////////////////////// -// Function: InterfaceMakerPythonSimple::write_prototype_for -// Access: Private -// Description: Writes the prototype for the indicated function. -//////////////////////////////////////////////////////////////////// -void InterfaceMakerPythonSimple:: -write_prototype_for(ostream &out, InterfaceMaker::Function *func) { - Function::Remaps::const_iterator ri; - - for (ri = func->_remaps.begin(); ri != func->_remaps.end(); ++ri) { - FunctionRemap *remap = (*ri); - if (!output_function_names) { - // If we're not saving the function names, don't export it from - // the library. - out << "static "; - } else { - out << "extern \"C\" "; - } - out << "PyObject *" - << remap->_wrapper_name << "(PyObject *self, PyObject *args);\n"; - } -} - -//////////////////////////////////////////////////////////////////// -// Function: InterfaceMakerPythonSimple::write_function_for -// Access: Private -// Description: Writes the definition for a function that will call -// the indicated C++ function or method. -//////////////////////////////////////////////////////////////////// -void InterfaceMakerPythonSimple:: -write_function_for(ostream &out, InterfaceMaker::Function *func) { - Function::Remaps::const_iterator ri; - - for (ri = func->_remaps.begin(); ri != func->_remaps.end(); ++ri) { - FunctionRemap *remap = (*ri); - write_function_instance(out, func, remap); - } -} - -//////////////////////////////////////////////////////////////////// -// Function: InterfaceMakerPythonSimple::write_function_instance -// Access: Private -// Description: Writes out the particular function that handles a -// single instance of an overloaded function. -//////////////////////////////////////////////////////////////////// -void InterfaceMakerPythonSimple::write_function_instance(ostream &out, InterfaceMaker::Function *func, - FunctionRemap *remap) { - out << "/*\n" - << " * Python simple wrapper for\n" - << " * "; - remap->write_orig_prototype(out, 0); - out << "\n" - << " */\n"; - - if (!output_function_names) { - // If we're not saving the function names, don't export it from - // the library. - out << "static "; - } - - out << "PyObject *\n" - << remap->_wrapper_name << "(PyObject *, PyObject *args) {\n"; - - if (generate_spam) { - write_spam_message(out, remap); - } - - string format_specifiers; - string parameter_list; - string container; - vector_string pexprs; - string extra_convert; - string extra_param_check; - string extra_cleanup; - - // Make one pass through the parameter list. We will output a - // one-line temporary variable definition for each parameter, while - // simultaneously building the ParseTuple() function call and also - // the parameter expression list for call_function(). - - int pn; - for (pn = 0; pn < (int)remap->_parameters.size(); pn++) { - indent(out, 2); - CPPType *orig_type = remap->_parameters[pn]._remap->get_orig_type(); - CPPType *type = remap->_parameters[pn]._remap->get_new_type(); - string param_name = remap->get_parameter_name(pn); - - // This is the string to convert our local variable to the - // appropriate C++ type. Normally this is just a cast. - string pexpr_string = - "(" + type->get_local_name(&parser) + ")" + param_name; - - if (remap->_parameters[pn]._remap->new_type_is_atomic_string()) { - if (TypeManager::is_char_pointer(orig_type)) { - out << "char *" << param_name; - format_specifiers += "s"; - parameter_list += ", &" + param_name; - - } else { - out << "char *" << param_name - << "_str; int " << param_name << "_len"; - format_specifiers += "s#"; - parameter_list += ", &" + param_name - + "_str, &" + param_name + "_len"; - pexpr_string = "basic_string(" + - param_name + "_str, " + - param_name + "_len)"; - } - - } else if (TypeManager::is_bool(type)) { - out << "PyObject *" << param_name; - format_specifiers += "O"; - parameter_list += ", &" + param_name; - pexpr_string = "(PyObject_IsTrue(" + param_name + ")!=0)"; - - } else if (TypeManager::is_unsigned_longlong(type)) { - out << "PyObject *" << param_name; - format_specifiers += "O"; - parameter_list += ", &" + param_name; - extra_convert += " PyObject *" + param_name + "_long = PyNumber_Long(" + param_name + ");"; - extra_param_check += "|| (" + param_name + "_long == NULL)"; - pexpr_string = "PyLong_AsUnsignedLongLong(" + param_name + "_long)"; - extra_cleanup += " Py_XDECREF(" + param_name + "_long);"; - - } else if (TypeManager::is_longlong(type)) { - out << "PyObject *" << param_name; - format_specifiers += "O"; - parameter_list += ", &" + param_name; - extra_convert += " PyObject *" + param_name + "_long = PyNumber_Long(" + param_name + ");"; - extra_param_check += "|| (" + param_name + "_long == NULL)"; - pexpr_string = "PyLong_AsLongLong(" + param_name + "_long)"; - extra_cleanup += " Py_XDECREF(" + param_name + "_long);"; - - } else if (TypeManager::is_integer(type)) { - out << "int " << param_name; - format_specifiers += "i"; - parameter_list += ", &" + param_name; - - } else if (TypeManager::is_float(type)) { - out << "double " << param_name; - format_specifiers += "d"; - parameter_list += ", &" + param_name; - - } else if (TypeManager::is_char_pointer(type)) { - out << "char *" << param_name; - format_specifiers += "s"; - parameter_list += ", &" + param_name; - - } else if (TypeManager::is_pointer_to_PyObject(type)) { - out << "PyObject *" << param_name; - format_specifiers += "O"; - parameter_list += ", &" + param_name; - pexpr_string = param_name; - - } else if (TypeManager::is_pointer(type)) { - out << "int " << param_name; - format_specifiers += "i"; - parameter_list += ", &" + param_name; - - } else { - // Ignore a parameter. - out << "PyObject *" << param_name; - format_specifiers += "O"; - parameter_list += ", &" + param_name; - } - - out << ";\n"; - if (remap->_has_this && pn == 0) { - // The "this" parameter gets passed in separately. - container = pexpr_string; - } - pexprs.push_back(pexpr_string); - } - - out << " if (PyArg_ParseTuple(args, \"" << format_specifiers - << "\"" << parameter_list << ")) {\n"; - - if (!extra_convert.empty()) { - out << " " << extra_convert << "\n"; - } - - if (!extra_param_check.empty()) { - out << " if (" << extra_param_check.substr(3) << ") {\n"; - if (!extra_cleanup.empty()) { - out << " " << extra_cleanup << "\n"; - } - out << " PyErr_SetString(PyExc_TypeError, \"Invalid parameters.\");\n" - << " return (PyObject *)NULL;\n" - << " }\n"; - } - - if (track_interpreter) { - out << " in_interpreter = 0;\n"; - } - - if (!remap->_void_return && - remap->_return_type->new_type_is_atomic_string()) { - // Treat strings as a special case. We don't want to format the - // return expression. - string return_expr = remap->call_function(out, 4, false, container, pexprs); - CPPType *type = remap->_return_type->get_orig_type(); - out << " "; - type->output_instance(out, "return_value", &parser); - out << " = " << return_expr << ";\n"; - - if (track_interpreter) { - out << " in_interpreter = 1;\n"; - } - if (!extra_cleanup.empty()) { - out << " " << extra_cleanup << "\n"; - } - - return_expr = manage_return_value(out, 4, remap, "return_value"); - test_assert(out, 4); - pack_return_value(out, 4, remap, return_expr); - - } else { - string return_expr = remap->call_function(out, 4, true, container, pexprs); - if (return_expr.empty()) { - if (track_interpreter) { - out << " in_interpreter = 1;\n"; - } - if (!extra_cleanup.empty()) { - out << " " << extra_cleanup << "\n"; - } - test_assert(out, 4); - out << " return Py_BuildValue(\"\");\n"; - - } else { - CPPType *type = remap->_return_type->get_temporary_type(); - out << " "; - type->output_instance(out, "return_value", &parser); - out << " = " << return_expr << ";\n"; - if (track_interpreter) { - out << " in_interpreter = 1;\n"; - } - if (!extra_cleanup.empty()) { - out << " " << extra_cleanup << "\n"; - } - - return_expr = manage_return_value(out, 4, remap, "return_value"); - test_assert(out, 4); - pack_return_value(out, 4, remap, remap->_return_type->temporary_to_return(return_expr)); - } - } - - out << " }\n"; - - out << " return (PyObject *)NULL;\n"; - out << "}\n\n"; -} - -//////////////////////////////////////////////////////////////////// -// Function: InterfaceMakerPythonSimple::pack_return_value -// Access: Private -// Description: Outputs a command to pack the indicated expression, -// of the return_type type, as a Python return value. -//////////////////////////////////////////////////////////////////// -void InterfaceMakerPythonSimple:: -pack_return_value(ostream &out, int indent_level, - FunctionRemap *remap, string return_expr) { - CPPType *orig_type = remap->_return_type->get_orig_type(); - CPPType *type = remap->_return_type->get_new_type(); - - if (remap->_return_type->new_type_is_atomic_string()) - { - if (TypeManager::is_char_pointer(orig_type)) { - indent(out, indent_level) - << "return PyString_FromString(" << return_expr << ");\n"; - - } else { - indent(out, indent_level) - << "return PyString_FromStringAndSize(" - << return_expr << ".data(), " << return_expr << ".length());\n"; - } - - } else if (TypeManager::is_unsigned_longlong(type)) - { - indent(out, indent_level) - << "return PyLong_FromUnsignedLongLong(" << return_expr << ");\n"; - - } else if (TypeManager::is_longlong(type)) - { - indent(out, indent_level) - << "return PyLong_FromLongLong(" << return_expr << ");\n"; - - } else if (TypeManager::is_integer(type)) { - indent(out, indent_level) - << "return PyInt_FromLong(" << return_expr << ");\n"; - - } else if (TypeManager::is_float(type)) { - indent(out, indent_level) - << "return PyFloat_FromDouble(" << return_expr << ");\n"; - - } else if (TypeManager::is_char_pointer(type)) { - indent(out, indent_level) - << "return PyString_FromString(" << return_expr << ");\n"; - - } else if (TypeManager::is_pointer_to_PyObject(type)) { - indent(out, indent_level) - << "return " << return_expr << ";\n"; - - } else if (TypeManager::is_pointer(type)) { - indent(out, indent_level) - << "return PyInt_FromLong((int)" << return_expr << ");\n"; - - } else { - // Return None. - indent(out, indent_level) - << "return Py_BuildValue(\"\");\n"; - } -} +// Filename: interfaceMakerPythonSimple.cxx +// Created by: drose (01Oct01) +// +//////////////////////////////////////////////////////////////////// +// +// 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 "interfaceMakerPythonSimple.h" +#include "interrogateBuilder.h" +#include "interrogate.h" +#include "functionRemap.h" +#include "parameterRemapUnchanged.h" +#include "typeManager.h" + +#include "interrogateDatabase.h" +#include "interrogateType.h" +#include "interrogateFunction.h" +#include "cppFunctionType.h" + +//////////////////////////////////////////////////////////////////// +// Function: InterfaceMakerPythonSimple::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +InterfaceMakerPythonSimple:: +InterfaceMakerPythonSimple(InterrogateModuleDef *def) : + InterfaceMakerPython(def) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: InterfaceMakerPythonSimple::Destructor +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +InterfaceMakerPythonSimple:: +~InterfaceMakerPythonSimple() { +} + +//////////////////////////////////////////////////////////////////// +// Function: InterfaceMakerPythonSimple::write_prototypes +// Access: Public, Virtual +// Description: Generates the list of function prototypes +// corresponding to the functions that will be output in +// write_functions(). +//////////////////////////////////////////////////////////////////// +void InterfaceMakerPythonSimple:: +write_prototypes(ostream &out,ostream *out_h) { + Functions::iterator fi; + for (fi = _functions.begin(); fi != _functions.end(); ++fi) { + Function *func = (*fi); + write_prototype_for(out, func); + } + + out << "\n"; + InterfaceMakerPython::write_prototypes(out,out_h); +} + +//////////////////////////////////////////////////////////////////// +// Function: InterfaceMakerPythonSimple::write_functions +// Access: Public, Virtual +// Description: Generates the list of functions that are appropriate +// for this interface. This function is called *before* +// write_prototypes(), above. +//////////////////////////////////////////////////////////////////// +void InterfaceMakerPythonSimple:: +write_functions(ostream &out) { + Functions::iterator fi; + for (fi = _functions.begin(); fi != _functions.end(); ++fi) { + Function *func = (*fi); + write_function_for(out, func); + } + + InterfaceMakerPython::write_functions(out); +} + +//////////////////////////////////////////////////////////////////// +// Function: InterfaceMakerPythonSimple::write_module +// Access: Public, Virtual +// Description: Generates whatever additional code is required to +// support a module file. +//////////////////////////////////////////////////////////////////// +void InterfaceMakerPythonSimple:: +write_module(ostream &out,ostream *out_h, InterrogateModuleDef *def) { + InterfaceMakerPython::write_module(out,out_h, def); + + out << "static PyMethodDef python_simple_funcs[] = {\n"; + + Functions::iterator fi; + for (fi = _functions.begin(); fi != _functions.end(); ++fi) { + Function *func = (*fi); + Function::Remaps::const_iterator ri; + for (ri = func->_remaps.begin(); ri != func->_remaps.end(); ++ri) { + FunctionRemap *remap = (*ri); + out << " { \"" << remap->_reported_name << "\", &" + << remap->_wrapper_name << ", METH_VARARGS },\n"; + } + } + out << " { NULL, NULL }\n" + << "};\n\n" + + << "#ifdef _WIN32\n" + << "extern \"C\" __declspec(dllexport) void init" << def->library_name << "();\n" + << "#else\n" + << "extern \"C\" void init" << def->library_name << "();\n" + << "#endif\n\n" + + << "void init" << def->library_name << "() {\n" + << " Py_InitModule(\"" << def->library_name + << "\", python_simple_funcs);\n" + << "}\n\n"; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterfaceMakerPythonSimple::synthesize_this_parameter +// Access: Public, Virtual +// Description: 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 InterfaceMakerPythonSimple:: +synthesize_this_parameter() { + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterfaceMakerPythonSimple::get_wrapper_prefix +// Access: Protected, Virtual +// Description: Returns the prefix string used to generate wrapper +// function names. +//////////////////////////////////////////////////////////////////// +string InterfaceMakerPythonSimple:: +get_wrapper_prefix() { + return "_inP"; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterfaceMakerPythonSimple::get_unique_prefix +// Access: Protected, Virtual +// Description: Returns the prefix string used to generate unique +// symbolic names, which are not necessarily C-callable +// function names. +//////////////////////////////////////////////////////////////////// +string InterfaceMakerPythonSimple:: +get_unique_prefix() { + return "p"; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterfaceMakerPythonSimple::record_function_wrapper +// Access: Protected, Virtual +// Description: Associates the function wrapper with its function in +// the appropriate structures in the database. +//////////////////////////////////////////////////////////////////// +void InterfaceMakerPythonSimple:: +record_function_wrapper(InterrogateFunction &ifunc, + FunctionWrapperIndex wrapper_index) { + ifunc._python_wrappers.push_back(wrapper_index); +} + +//////////////////////////////////////////////////////////////////// +// Function: InterfaceMakerPythonSimple::write_prototype_for +// Access: Private +// Description: Writes the prototype for the indicated function. +//////////////////////////////////////////////////////////////////// +void InterfaceMakerPythonSimple:: +write_prototype_for(ostream &out, InterfaceMaker::Function *func) { + Function::Remaps::const_iterator ri; + + for (ri = func->_remaps.begin(); ri != func->_remaps.end(); ++ri) { + FunctionRemap *remap = (*ri); + if (!output_function_names) { + // If we're not saving the function names, don't export it from + // the library. + out << "static "; + } else { + out << "extern \"C\" "; + } + out << "PyObject *" + << remap->_wrapper_name << "(PyObject *self, PyObject *args);\n"; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: InterfaceMakerPythonSimple::write_function_for +// Access: Private +// Description: Writes the definition for a function that will call +// the indicated C++ function or method. +//////////////////////////////////////////////////////////////////// +void InterfaceMakerPythonSimple:: +write_function_for(ostream &out, InterfaceMaker::Function *func) { + Function::Remaps::const_iterator ri; + + for (ri = func->_remaps.begin(); ri != func->_remaps.end(); ++ri) { + FunctionRemap *remap = (*ri); + write_function_instance(out, func, remap); + } +} + +//////////////////////////////////////////////////////////////////// +// Function: InterfaceMakerPythonSimple::write_function_instance +// Access: Private +// Description: Writes out the particular function that handles a +// single instance of an overloaded function. +//////////////////////////////////////////////////////////////////// +void InterfaceMakerPythonSimple::write_function_instance(ostream &out, InterfaceMaker::Function *func, + FunctionRemap *remap) { + out << "/*\n" + << " * Python simple wrapper for\n" + << " * "; + remap->write_orig_prototype(out, 0); + out << "\n" + << " */\n"; + + if (!output_function_names) { + // If we're not saving the function names, don't export it from + // the library. + out << "static "; + } + + out << "PyObject *\n" + << remap->_wrapper_name << "(PyObject *, PyObject *args) {\n"; + + if (generate_spam) { + write_spam_message(out, remap); + } + + string format_specifiers; + string parameter_list; + string container; + vector_string pexprs; + string extra_convert; + string extra_param_check; + string extra_cleanup; + + // Make one pass through the parameter list. We will output a + // one-line temporary variable definition for each parameter, while + // simultaneously building the ParseTuple() function call and also + // the parameter expression list for call_function(). + + int pn; + for (pn = 0; pn < (int)remap->_parameters.size(); ++pn) { + indent(out, 2); + CPPType *orig_type = remap->_parameters[pn]._remap->get_orig_type(); + CPPType *type = remap->_parameters[pn]._remap->get_new_type(); + string param_name = remap->get_parameter_name(pn); + + // This is the string to convert our local variable to the + // appropriate C++ type. Normally this is just a cast. + string pexpr_string = + "(" + type->get_local_name(&parser) + ")" + param_name; + + if (remap->_parameters[pn]._remap->new_type_is_atomic_string()) { + if (TypeManager::is_char_pointer(orig_type)) { + out << "char *" << param_name; + format_specifiers += "s"; + parameter_list += ", &" + param_name; + + } else { + out << "char *" << param_name + << "_str; int " << param_name << "_len"; + format_specifiers += "s#"; + parameter_list += ", &" + param_name + + "_str, &" + param_name + "_len"; + pexpr_string = "basic_string(" + + param_name + "_str, " + + param_name + "_len)"; + } + + } else if (TypeManager::is_bool(type)) { + out << "PyObject *" << param_name; + format_specifiers += "O"; + parameter_list += ", &" + param_name; + pexpr_string = "(PyObject_IsTrue(" + param_name + ")!=0)"; + + } else if (TypeManager::is_unsigned_longlong(type)) { + out << "PyObject *" << param_name; + format_specifiers += "O"; + parameter_list += ", &" + param_name; + extra_convert += " PyObject *" + param_name + "_long = PyNumber_Long(" + param_name + ");"; + extra_param_check += "|| (" + param_name + "_long == NULL)"; + pexpr_string = "PyLong_AsUnsignedLongLong(" + param_name + "_long)"; + extra_cleanup += " Py_XDECREF(" + param_name + "_long);"; + + } else if (TypeManager::is_longlong(type)) { + out << "PyObject *" << param_name; + format_specifiers += "O"; + parameter_list += ", &" + param_name; + extra_convert += " PyObject *" + param_name + "_long = PyNumber_Long(" + param_name + ");"; + extra_param_check += "|| (" + param_name + "_long == NULL)"; + pexpr_string = "PyLong_AsLongLong(" + param_name + "_long)"; + extra_cleanup += " Py_XDECREF(" + param_name + "_long);"; + + #ifndef USE_PYTHON_2_2_OR_EARLIER + } else if (TypeManager::is_unsigned_integer(type)) { + out << "unsigned int " << param_name; + format_specifiers += "I"; // This requires Python 2.3 or better + parameter_list += ", &" + param_name; + #endif + + } else if (TypeManager::is_integer(type)) { + out << "int " << param_name; + format_specifiers += "i"; + parameter_list += ", &" + param_name; + + } else if (TypeManager::is_float(type)) { + out << "double " << param_name; + format_specifiers += "d"; + parameter_list += ", &" + param_name; + + } else if (TypeManager::is_char_pointer(type)) { + out << "char *" << param_name; + format_specifiers += "s"; + parameter_list += ", &" + param_name; + + } else if (TypeManager::is_pointer_to_PyObject(type)) { + out << "PyObject *" << param_name; + format_specifiers += "O"; + parameter_list += ", &" + param_name; + pexpr_string = param_name; + + } else if (TypeManager::is_pointer(type)) { + out << "int " << param_name; + format_specifiers += "i"; + parameter_list += ", &" + param_name; + + } else { + // Ignore a parameter. + out << "PyObject *" << param_name; + format_specifiers += "O"; + parameter_list += ", &" + param_name; + } + + out << ";\n"; + if (remap->_has_this && pn == 0) { + // The "this" parameter gets passed in separately. + container = pexpr_string; + } + pexprs.push_back(pexpr_string); + } + + out << " if (PyArg_ParseTuple(args, \"" << format_specifiers + << "\"" << parameter_list << ")) {\n"; + + if (!extra_convert.empty()) { + out << " " << extra_convert << "\n"; + } + + if (!extra_param_check.empty()) { + out << " if (" << extra_param_check.substr(3) << ") {\n"; + if (!extra_cleanup.empty()) { + out << " " << extra_cleanup << "\n"; + } + out << " PyErr_SetString(PyExc_TypeError, \"Invalid parameters.\");\n" + << " return (PyObject *)NULL;\n" + << " }\n"; + } + + if (track_interpreter) { + out << " in_interpreter = 0;\n"; + } + + if (!remap->_void_return && + remap->_return_type->new_type_is_atomic_string()) { + // Treat strings as a special case. We don't want to format the + // return expression. + string return_expr = remap->call_function(out, 4, false, container, pexprs); + CPPType *type = remap->_return_type->get_orig_type(); + out << " "; + type->output_instance(out, "return_value", &parser); + out << " = " << return_expr << ";\n"; + + if (track_interpreter) { + out << " in_interpreter = 1;\n"; + } + if (!extra_cleanup.empty()) { + out << " " << extra_cleanup << "\n"; + } + + return_expr = manage_return_value(out, 4, remap, "return_value"); + test_assert(out, 4); + pack_return_value(out, 4, remap, return_expr); + + } else { + string return_expr = remap->call_function(out, 4, true, container, pexprs); + if (return_expr.empty()) { + if (track_interpreter) { + out << " in_interpreter = 1;\n"; + } + if (!extra_cleanup.empty()) { + out << " " << extra_cleanup << "\n"; + } + test_assert(out, 4); + out << " return Py_BuildValue(\"\");\n"; + + } else { + CPPType *type = remap->_return_type->get_temporary_type(); + out << " "; + type->output_instance(out, "return_value", &parser); + out << " = " << return_expr << ";\n"; + if (track_interpreter) { + out << " in_interpreter = 1;\n"; + } + if (!extra_cleanup.empty()) { + out << " " << extra_cleanup << "\n"; + } + + return_expr = manage_return_value(out, 4, remap, "return_value"); + test_assert(out, 4); + pack_return_value(out, 4, remap, remap->_return_type->temporary_to_return(return_expr)); + } + } + + out << " }\n"; + + out << " return (PyObject *)NULL;\n"; + out << "}\n\n"; +} + +//////////////////////////////////////////////////////////////////// +// Function: InterfaceMakerPythonSimple::pack_return_value +// Access: Private +// Description: Outputs a command to pack the indicated expression, +// of the return_type type, as a Python return value. +//////////////////////////////////////////////////////////////////// +void InterfaceMakerPythonSimple:: +pack_return_value(ostream &out, int indent_level, + FunctionRemap *remap, string return_expr) { + CPPType *orig_type = remap->_return_type->get_orig_type(); + CPPType *type = remap->_return_type->get_new_type(); + + if (remap->_return_type->new_type_is_atomic_string()) + { + if (TypeManager::is_char_pointer(orig_type)) { + indent(out, indent_level) + << "return PyString_FromString(" << return_expr << ");\n"; + + } else { + indent(out, indent_level) + << "return PyString_FromStringAndSize(" + << return_expr << ".data(), " << return_expr << ".length());\n"; + } + + } else if (TypeManager::is_unsigned_longlong(type)) + { + indent(out, indent_level) + << "return PyLong_FromUnsignedLongLong(" << return_expr << ");\n"; + + } else if (TypeManager::is_longlong(type)) + { + indent(out, indent_level) + << "return PyLong_FromLongLong(" << return_expr << ");\n"; + + #ifndef USE_PYTHON_2_2_OR_EARLIER + } else if (TypeManager::is_unsigned_integer(type)) { + indent(out, indent_level) + << "return PyLong_FromUnsignedLong(" << return_expr << ");\n"; + #endif + + } else if (TypeManager::is_integer(type)) { + indent(out, indent_level) + << "return PyInt_FromLong(" << return_expr << ");\n"; + + } else if (TypeManager::is_float(type)) { + indent(out, indent_level) + << "return PyFloat_FromDouble(" << return_expr << ");\n"; + + } else if (TypeManager::is_char_pointer(type)) { + indent(out, indent_level) + << "return PyString_FromString(" << return_expr << ");\n"; + + } else if (TypeManager::is_pointer_to_PyObject(type)) { + indent(out, indent_level) + << "return " << return_expr << ";\n"; + + } else if (TypeManager::is_pointer(type)) { + indent(out, indent_level) + << "return PyInt_FromLong((int)" << return_expr << ");\n"; + + } else { + // Return None. + indent(out, indent_level) + << "return Py_BuildValue(\"\");\n"; + } +}