// -*- Mode: C++; -*-
//                            Package   : omniORBpy
// omnipy.h                   Created on: 2000/02/24
//                            Author    : Duncan Grisby (dpg1)
//
//    Copyright (C) 2000 AT&T Laboratories Cambridge
//
//    This file is part of the omniORBpy library
//
//    The omniORBpy library is free software; you can redistribute it
//    and/or modify it under the terms of the GNU Lesser General
//    Public License as published by the Free Software Foundation;
//    either version 2.1 of the License, or (at your option) any later
//    version.
//
//    This library is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU Lesser General Public License for more details.
//
//    You should have received a copy of the GNU Lesser General Public
//    License along with this library; if not, write to the Free
//    Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
//    MA 02111-1307, USA
//
//
// Description:
//    Master header file for omnipy internals

#ifndef _omnipy_h_
#define _omnipy_h_

// $Log: omnipy.h,v $
// Revision 1.3.2.13  2007/01/19 11:11:09  dgrisby
// Avoid assertion failure if an unexpected C++ exception occurs during
// an invocation.
//
// Revision 1.3.2.12  2006/07/26 17:50:43  dgrisby
// Reuse existing omniIOR object when converting C++ object reference to Python.
//
// Revision 1.3.2.11  2006/07/19 09:40:39  dgrisby
// Track ORB core changes.
//
// Revision 1.3.2.10  2006/05/24 18:33:04  dgrisby
// Unlock interpreter lock before clearing value tracker in cdrMarshal /
// cdrUnmarshal.
//
// Revision 1.3.2.9  2006/05/15 10:26:11  dgrisby
// More relaxation of requirements for old-style classes, for Python 2.5.
//
// Revision 1.3.2.8  2006/01/19 17:28:44  dgrisby
// Merge from omnipy2_develop.
//
// Revision 1.3.2.7  2005/11/09 12:33:32  dgrisby
// Support POA LocalObjects.
//
// Revision 1.3.2.6  2005/06/24 17:36:08  dgrisby
// Support for receiving valuetypes inside Anys; relax requirement for
// old style classes in a lot of places.
//
// Revision 1.3.2.5  2005/04/25 18:27:41  dgrisby
// Maintain forwarded location when narrowing forwarded references.
//
// Revision 1.3.2.4  2005/01/07 00:22:32  dgrisby
// Big merge from omnipy2_develop.
//
// Revision 1.3.2.3  2003/07/10 22:13:25  dgrisby
// Abstract interface support.
//
// Revision 1.3.2.2  2003/05/20 17:10:23  dgrisby
// Preliminary valuetype support.
//
// Revision 1.3.2.1  2003/03/23 21:51:57  dgrisby
// New omnipy3_develop branch.
//
// Revision 1.2.4.20  2003/03/12 11:17:02  dgrisby
// Registration of external pseudo object creation functions.
//
// Revision 1.2.4.19  2002/11/27 00:18:25  dgrisby
// Per thread / per objref timeouts.
//
// Revision 1.2.4.18  2002/05/26 00:55:36  dgrisby
// C++ API to convert object references to/from Python.
//
// Revision 1.2.4.17  2002/03/18 12:40:38  dpg1
// Support overriding _non_existent.
//
// Revision 1.2.4.16  2002/01/18 15:49:44  dpg1
// Context support. New system exception construction. Fix None call problem.
//
// Revision 1.2.4.15  2001/10/18 15:48:39  dpg1
// Track ORB core changes.
//
// Revision 1.2.4.14  2001/09/24 10:48:25  dpg1
// Meaningful minor codes.
//
// Revision 1.2.4.13  2001/08/21 10:52:41  dpg1
// Update to new ORB core APIs.
//
// Revision 1.2.4.12  2001/08/15 10:37:14  dpg1
// Track ORB core object table changes.
//
// Revision 1.2.4.11  2001/06/11 13:06:26  dpg1
// Support for PortableServer::Current.
//
// Revision 1.2.4.10  2001/06/01 11:09:26  dpg1
// Make use of new omni::ptrStrCmp() and omni::strCmp().
//
// Revision 1.2.4.9  2001/05/29 17:10:14  dpg1
// Support for in process identity.
//
// Revision 1.2.4.8  2001/05/14 12:47:21  dpg1
// Fix memory leaks.
//
// Revision 1.2.4.7  2001/05/10 15:16:01  dpg1
// Big update to support new omniORB 4 internals.
//
// Revision 1.2.4.6  2001/04/09 15:22:15  dpg1
// Fixed point support.
//
// Revision 1.2.4.5  2001/03/13 10:38:07  dpg1
// Fixes from omnipy1_develop
//
// Revision 1.2.4.4  2000/12/04 18:57:23  dpg1
// Fix deadlock when trying to lock omniORB internal lock while holding
// the Python interpreter lock.
//

#if defined(__VMS)
#include <Python.h>
#else
#include PYTHON_INCLUDE
#endif

#include <omniORB4/CORBA.h>
#include <omniORB4/callDescriptor.h>
#include <omniORB4/minorCode.h>
#include <exceptiondefs.h>
#include <objectTable.h>
#include <orbParameters.h>
#include <omniORBpy.h>
#include "omnipy_sysdep.h"


OMNI_USING_NAMESPACE(omni)

////////////////////////////////////////////////////////////////////////////
// Data structure to manage C++ twins of Python objects                   //
////////////////////////////////////////////////////////////////////////////

extern "C" {
  struct omnipyTwin {
    PyObject_HEAD
    void* ob_twin;
  };
}

// Twin attribute names
#define ORB_TWIN        omniPy::pyORB_TWIN
#define OBJREF_TWIN     omniPy::pyOBJREF_TWIN
#define SERVANT_TWIN    omniPy::pySERVANT_TWIN
#define POA_TWIN        omniPy::pyPOA_TWIN
#define POAMANAGER_TWIN omniPy::pyPOAMANAGER_TWIN
#define POACURRENT_TWIN omniPy::pyPOACURRENT_TWIN


// Useful macro
#define RAISE_PY_BAD_PARAM_IF(x,minor) \
  if (x) { \
    CORBA::BAD_PARAM _ex(minor, CORBA::COMPLETED_NO); \
    return omniPy::handleSystemException(_ex); \
  }

class omniPy {
public:

  ////////////////////////////////////////////////////////////////////////////
  // The global Python interpreter state                                    //
  ////////////////////////////////////////////////////////////////////////////

  static PyInterpreterState* pyInterpreter;

  ////////////////////////////////////////////////////////////////////////////
  // Global pointers to Python objects                                      //
  ////////////////////////////////////////////////////////////////////////////

  static PyObject* py_omnipymodule;    	// _omnipy module
  static PyObject* pyCORBAmodule;      	// CORBA module
  static PyObject* pyCORBAsysExcMap;   	//  The system exception map
  static PyObject* pyCORBAAnyClass;    	//  Any class
  static PyObject* pyCORBATypeCodeClass;//  TypeCode class
  static PyObject* pyCORBAContextClass;	//  Context class
  static PyObject* pyCORBAValueBase;    //  ValueBase class
  static PyObject* pyCORBAValueBaseDesc;//  Descriptor for ValueBase
  static PyObject* pyomniORBmodule;    	// The omniORB module
  static PyObject* pyomniORBobjrefMap; 	//  The objref class map
  static PyObject* pyomniORBskeletonMap;//  The skeleton class map
  static PyObject* pyomniORBtypeMap;   	//  Type map
  static PyObject* pyomniORBvalueMap;  	//  Value factory map
  static PyObject* pyomniORBwordMap;   	//  Reserved word map
  static PyObject* pyomniORBpoaCache;   //  POA cache
  static PyObject* pyomniORBUnknownValueBase;
                                        //  Base class for unknown valuetypes
  static PyObject* pyPortableServerModule;
                                        // Portable server module
  static PyObject* pyServantClass;     	// Servant class
  static PyObject* pyCreateTypeCode;   	// Function to create a TypeCode object
  static PyObject* pyWorkerThreadClass;	// Worker thread class
  static PyObject* pyWorkerThreadDel;  	// Method to delete worker thread
  static PyObject* pyEmptyTuple;       	// Zero element tuple

  ////////////////////////////////////////////////////////////////////////////
  // Twin names and other 'static' strings                                  //
  ////////////////////////////////////////////////////////////////////////////

  static PyObject* pyORB_TWIN;
  static PyObject* pyOBJREF_TWIN;
  static PyObject* pySERVANT_TWIN;
  static PyObject* pyPOA_TWIN;
  static PyObject* pyPOAMANAGER_TWIN;
  static PyObject* pyPOACURRENT_TWIN;
  static PyObject* pyNP_RepositoryId;

  ////////////////////////////////////////////////////////////////////////////
  // Constant strings to facilitate comparison by pointer                   //
  ////////////////////////////////////////////////////////////////////////////

  static const char* string_Py_omniObjRef;
  static const char* string_Py_omniServant;
  static const char* string_Py_ServantActivator;
  static const char* string_Py_ServantLocator;
  static const char* string_Py_AdapterActivator;


  ////////////////////////////////////////////////////////////////////////////
  // Pointer to the ORB                                                     //
  ////////////////////////////////////////////////////////////////////////////

  static CORBA::ORB_ptr orb;


  ////////////////////////////////////////////////////////////////////////////
  // C++ API object                                                         //
  ////////////////////////////////////////////////////////////////////////////

  static omniORBpyAPI cxxAPI;


  ////////////////////////////////////////////////////////////////////////////
  // Twin object handling                                                   //
  ////////////////////////////////////////////////////////////////////////////

  static PyObject* newTwin(void* twin);

  static
  inline void
  setTwin(PyObject* obj, void* twin, PyObject* name)
  {
    PyObject* ot = newTwin(twin);
    PyObject_SetAttr(obj, name, ot);
    Py_DECREF(ot);
  }

  static
  inline void
  setExistingTwin(PyObject* obj, PyObject* ot, PyObject* name)
  {
    PyObject_SetAttr(obj, name, ot);
    Py_DECREF(ot);
  }

  static
  inline void*
  getTwin(PyObject* obj, PyObject* name)
  {
    void* twin;
    PyObject* ot = PyObject_GetAttr(obj, name);
    if (ot) {
      twin = ((omnipyTwin*)ot)->ob_twin;
      Py_DECREF(ot);
    }
    else {
      PyErr_Clear();
      twin = 0;
    }
    return twin;
  }

  static
  inline void
  remTwin(PyObject* obj, PyObject* name)
  {
    PyObject_DelAttr(obj, name);
  }

  ////////////////////////////////////////////////////////////////////////////
  // Module initialisation functions                                        //
  ////////////////////////////////////////////////////////////////////////////

  static void initORBFunc        (PyObject* d);
  static void initPOAFunc        (PyObject* d);
  static void initPOAManagerFunc (PyObject* d);
  static void initPOACurrentFunc (PyObject* d);
  static void initInterceptorFunc(PyObject* d);
  static void initomniFunc       (PyObject* d);


  ////////////////////////////////////////////////////////////////////////////
  // Utility functions                                                      //
  ////////////////////////////////////////////////////////////////////////////

  // Set the Python execution state to handle a system exception.
  // Returns a NULL PyObject so you can say
  //   return handleSystemException(ex).
  static
  PyObject* handleSystemException(const CORBA::SystemException& ex);

  // Create a new Python object for the given system exception
  static
  PyObject* createPySystemException(const CORBA::SystemException& ex);

  // Throw a C++ system exception equivalent to the given Python exception
  static
  void produceSystemException(PyObject* eobj, PyObject* erepoId,
			      PyObject* etype, PyObject* etraceback);

  // Handle the current Python exception. An exception must have
  // occurred. Deals with system exceptions and
  // omniORB.LocationForward; all other exceptions print a traceback
  // and raise UNKNOWN.
  static
  void handlePythonException();

  // Handle the omniORB.LocationForward exception in the argument.
  static
  void handleLocationForward(PyObject* evalue);
  
  // Ensure there is an omni_thread associated with the calling thread.
  static
  omni_thread* ensureOmniThread();

  // IsInstance function for all Python versions.
  static inline
  CORBA::Boolean isInstance(PyObject* o, PyObject* c)
  {
#if PY_VERSION_HEX >= 0x02010000
    return PyObject_IsInstance(o,c);
#else
    if (!PyInstance_Check(a_o)) return 0;
    PyObject* acls = (PyObject*)((PyInstanceObject*)o)->in_class;
    return PyClass_IsSubclass(acls, c);
#endif
  }

  // IsSubclass function for all Python versions
  static inline
  CORBA::Boolean isSubclass(PyObject* o, PyObject* c)
  {
#if PY_VERSION_HEX >= 0x02010000
    return PyObject_IsSubclass(o,c);
#else
    return PyClass_IsSubclass(o, c);
#endif
  }

  ////////////////////////////////////////////////////////////////////////////
  // Fixed point                                                            //
  ////////////////////////////////////////////////////////////////////////////

  // Create a new omnipyFixedObject.
  static
  PyObject* newFixedObject(const CORBA::Fixed& f);

  // Version for CORBA.fixed() function
  static
  PyObject* newFixedObject(PyObject* self, PyObject* args);


  ////////////////////////////////////////////////////////////////////////////
  // Python object creation functions                                       //
  ////////////////////////////////////////////////////////////////////////////

  static
  PyObject* createPyPOAObject(const PortableServer::POA_ptr poa);

  static
  PyObject* createPyPOAManagerObject(const PortableServer::POAManager_ptr pm);

  static
  PyObject* createPyPOACurrentObject(const PortableServer::Current_ptr pc);


  ////////////////////////////////////////////////////////////////////////////
  // Object reference functions                                             //
  ////////////////////////////////////////////////////////////////////////////

  // Create the Python object relating to a CORBA object reference
  //
  // Caller must hold the Python interpreter lock.
  static
  PyObject* createPyCorbaObjRef(const char* targetRepoId,
				const CORBA::Object_ptr objref);

  static
  PyObject* createPyPseudoObjRef(const CORBA::Object_ptr objref);


  // Functions which mirror omni::createObjRef(). These versions don't
  // look for C++ proxy factories, and spot local Python servants.
  //
  // Caller must NOT hold the Python interpreter lock.
  static
  omniObjRef* createObjRef(const char*        targetRepoId,
			   omniIOR*           ior,
			   CORBA::Boolean     locked,
			   omniIdentity*      id = 0,
			   CORBA::Boolean     type_verified = 0,
			   CORBA::Boolean     is_forwarded = 0);

  static
  omniObjRef* createLocalObjRef(const char*         mostDerivedRepoId,
				const char*         targetRepoId,
				omniObjTableEntry*  entry,
				omniObjRef*         orig_ref,
				CORBA::Boolean      type_verified = 0);

  static
  omniObjRef* createLocalObjRef(const char* 	    mostDerivedRepoId,
				const char* 	    targetRepoId,
				const _CORBA_Octet* key,
				int                 keysize,
				omniObjRef*         orig_ref,
				CORBA::Boolean      type_verified = 0);

  // When a POA creates a reference to a Python servant, it does not
  // have a proxy object factory for it, so it creates an
  // omniAnonObjRef. This function converts one of them into a
  // Py_omniObjRef with a reference to the local servant.
  //
  // Caller must NOT hold the Python interpreter lock.
  static
  CORBA::Object_ptr makeLocalObjRef(const char* targetRepoId,
				    const CORBA::Object_ptr objref);

  // Copy a Python object reference in an argument or return value.
  // Compares the type of the objref with the target type, and creates
  // a new objref of the target type if they are not compatible.
  // Throws BAD_PARAM if the Python object is not an object reference.
  //
  // Caller must hold the Python interpreter lock.
  static
  PyObject* copyObjRefArgument(PyObject*               pytargetRepoId,
			       PyObject*               pyobjref,
			       CORBA::CompletionStatus compstatus);

  // Mirror of omniURI::stringToObject(). Caller must hold the Python
  // interpreter lock.
  static
  CORBA::Object_ptr stringToObject(const char* uri);

  // Mirror of CORBA::UnMarshalObjRef(). Caller must hold the Python
  // interpreter lock.
  static
  CORBA::Object_ptr UnMarshalObjRef(const char* repoId, cdrStream& s);



  ////////////////////////////////////////////////////////////////////////////
  // Recursive marshalling functions                                        //
  ////////////////////////////////////////////////////////////////////////////
  
  // Helper function to return the TypeCode kind of a descriptor
  static inline
  CORBA::ULong descriptorToTK(PyObject* d_o)
  {
    if (PyInt_Check(d_o))
      return PyInt_AS_LONG(d_o);
    else
      return PyInt_AS_LONG(PyTuple_GET_ITEM(d_o, 0));
  }

  // Validate that the argument has the type specified by the
  // descriptor. If it has not, throw CORBA::BAD_PARAM with the given
  // completion status.
  //
  typedef void (*ValidateTypeFn)(PyObject*, PyObject*,
				 CORBA::CompletionStatus,
				 PyObject*);

  static const ValidateTypeFn validateTypeFns[];

  static void validateTypeIndirect(PyObject* d_o, PyObject* a_o,
				   CORBA::CompletionStatus compstatus,
				   PyObject* track);

  static inline
  void validateType(PyObject* d_o, PyObject* a_o,
		    CORBA::CompletionStatus compstatus,
		    PyObject* track = 0)
  {
    CORBA::ULong tk = descriptorToTK(d_o);

    if (tk <= 33) { // tk_local_interface
      validateTypeFns[tk](d_o, a_o, compstatus, track);
    }
    else if (tk == 0xffffffff) { // Indirection
      validateTypeIndirect(d_o, a_o, compstatus, track);
    }
    else OMNIORB_THROW(BAD_TYPECODE, BAD_TYPECODE_UnknownKind, compstatus);
  }

  // Marshal the given argument object a_o, which has the type
  // specified by d_o. This function MUST NOT be called without having
  // first called validateType() with the same arguments, since it
  // performs no argument type checking.
  //
  typedef void (*MarshalPyObjectFn)(cdrStream& stream, PyObject*, PyObject*);

  static const MarshalPyObjectFn marshalPyObjectFns[];

  static void marshalPyObjectIndirect(cdrStream& stream,
				      PyObject* d_o, PyObject* a_o);

  static inline
  void marshalPyObject(cdrStream& stream, PyObject* d_o, PyObject* a_o)
  {
    CORBA::ULong tk = descriptorToTK(d_o);

    if (tk <= 33) { // tk_local_interface
      marshalPyObjectFns[tk](stream, d_o, a_o);
    }
    else if (tk == 0xffffffff) { // Indirection
      marshalPyObjectIndirect(stream, d_o, a_o);
    }
    else OMNIORB_ASSERT(0);
  }

  // Unmarshal a PyObject, which has the type specified by d_o.
  //
  typedef PyObject* (*UnmarshalPyObjectFn)(cdrStream& stream, PyObject*);

  static const UnmarshalPyObjectFn unmarshalPyObjectFns[];

  static PyObject* unmarshalPyObjectIndirect(cdrStream& stream, PyObject* d_o);

  static inline
  PyObject* unmarshalPyObject(cdrStream& stream, PyObject* d_o)
  {
    CORBA::ULong tk = descriptorToTK(d_o);

    if (tk <= 33) { // tk_local_interface
      return unmarshalPyObjectFns[tk](stream, d_o);
    }
    else if (tk == 0xffffffff) { // Indirection
      return unmarshalPyObjectIndirect(stream, d_o);
    }
    else OMNIORB_THROW(BAD_TYPECODE,
		       BAD_TYPECODE_UnknownKind,
		       (CORBA::CompletionStatus)stream.completion());
    return 0;
  }

  // Take a descriptor and an argument object, and return a "copy" of
  // the argument. Immutable types need not be copied. If the argument
  // does not match the descriptor, throws BAD_PARAM.
  //
  typedef PyObject* (*CopyArgumentFn)(PyObject*, PyObject*,
				      CORBA::CompletionStatus);

  static const CopyArgumentFn copyArgumentFns[];

  static PyObject* copyArgumentIndirect(PyObject* d_o, PyObject* a_o,
					CORBA::CompletionStatus compstatus);

  static inline
  PyObject* copyArgument(PyObject* d_o, PyObject* a_o,
			 CORBA::CompletionStatus compstatus)
  {
    CORBA::ULong tk = descriptorToTK(d_o);

    if (tk <= 33) { // tk_local_interface
      PyObject* r = copyArgumentFns[tk](d_o, a_o, compstatus);
      OMNIORB_ASSERT(r);
      return r;
    }
    else if (tk == 0xffffffff) { // Indirection
      return copyArgumentIndirect(d_o, a_o, compstatus);
    }
    else OMNIORB_THROW(BAD_TYPECODE, BAD_TYPECODE_UnknownKind, compstatus);
    return 0; // For dumb compilers
  }

  static inline
  void marshalRawPyString(cdrStream& stream, PyObject* pystring)
  {
    CORBA::ULong slen = PyString_GET_SIZE(pystring) + 1;
    slen >>= stream;
    char* str = PyString_AS_STRING(pystring);
    stream.put_octet_array((const CORBA::Octet*)((const char*)str), slen);
  }

  static inline PyObject*
  unmarshalRawPyString(cdrStream& stream)
  {
    CORBA::ULong len; len <<= stream;

    if (!stream.checkInputOverrun(1, len))
      OMNIORB_THROW(MARSHAL, MARSHAL_PassEndOfMessage,
		    (CORBA::CompletionStatus)stream.completion());

    PyObject* pystring = PyString_FromStringAndSize(0, len - 1);

    stream.get_octet_array((_CORBA_Octet*)PyString_AS_STRING(pystring), len);
    return pystring;
  }


  ////////////////////////////////////////////////////////////////////////////
  // Valuetype / abstract interface marshalling functions                   //
  ////////////////////////////////////////////////////////////////////////////

  static void
  validateTypeValue(PyObject* d_o, PyObject* a_o,
		    CORBA::CompletionStatus compstatus,
		    PyObject* track);

  static void
  validateTypeValueBox(PyObject* d_o, PyObject* a_o,
		       CORBA::CompletionStatus compstatus,
		       PyObject* track);

  static void
  validateTypeAbstractInterface(PyObject* d_o, PyObject* a_o,
				CORBA::CompletionStatus compstatus,
				PyObject* track);

  static void
  marshalPyObjectValue(cdrStream& stream, PyObject* d_o, PyObject* a_o);

  static void
  marshalPyObjectValueBox(cdrStream& stream, PyObject* d_o, PyObject* a_o);

  static void
  marshalPyObjectAbstractInterface(cdrStream& stream,
				   PyObject* d_o, PyObject* a_o);

  static PyObject*
  unmarshalPyObjectValue(cdrStream& stream, PyObject* d_o);
  // Shared by Value and ValueBox

  static PyObject*
  unmarshalPyObjectAbstractInterface(cdrStream& stream, PyObject* d_o);

  static PyObject*
  copyArgumentValue(PyObject* d_o, PyObject* a_o,
		    CORBA::CompletionStatus compstatus);

  static PyObject*
  copyArgumentValueBox(PyObject* d_o, PyObject* a_o,
		       CORBA::CompletionStatus compstatus);

  static PyObject*
  copyArgumentAbstractInterface(PyObject* d_o, PyObject* a_o,
				CORBA::CompletionStatus compstatus);


  ////////////////////////////////////////////////////////////////////////////
  // TypeCode and Any support functions                                     //
  ////////////////////////////////////////////////////////////////////////////

  // Marshal a type descriptor as a TypeCode:
  static
  void marshalTypeCode(cdrStream& stream, PyObject* d_o);

  // Unmarshal a TypeCode, returning a descriptor:
  static
  PyObject* unmarshalTypeCode(cdrStream& stream);

  ////////////////////////////////////////////////////////////////////////////
  // Context support functions                                              //
  ////////////////////////////////////////////////////////////////////////////

  // Validate a Context object.
  static
  void validateContext(PyObject* c_o, CORBA::CompletionStatus compstatus);

  // Marshal context c_o, filtered according to pattern list p_o.
  static
  void marshalContext(cdrStream& stream, PyObject* p_o, PyObject* c_o);

  // Unmarshal context. Trust the sender to correctly filter.
  static
  PyObject* unmarshalContext(cdrStream& stream);

  // Filter context c_o according to pattern list p_o. Returns a new Context.
  static
  PyObject* filterContext(PyObject* p_o, PyObject* c_o);


  ////////////////////////////////////////////////////////////////////////////
  // Interceptor functions                                                  //
  ////////////////////////////////////////////////////////////////////////////

  // Register ORB interceptors if need be
  static
  void registerInterceptors();


  ////////////////////////////////////////////////////////////////////////////
  // Proxy call descriptor object                                           //
  ////////////////////////////////////////////////////////////////////////////

  static
  void Py_localCallBackFunction(omniCallDescriptor* cd, omniServant* svnt);

  class Py_omniCallDescriptor : public omniCallDescriptor {
  public:

    inline Py_omniCallDescriptor(const char* op, int op_len,
				 CORBA::Boolean oneway,
				 PyObject* in_d, PyObject* out_d,
				 PyObject* exc_d, PyObject* ctxt_d,
				 PyObject* args, CORBA::Boolean is_upcall)

      : omniCallDescriptor(Py_localCallBackFunction, op, op_len,
			   oneway, 0, 0, is_upcall),
      in_d_(in_d),
      out_d_(out_d),
      exc_d_(exc_d),
      ctxt_d_(ctxt_d),
      args_(args),
      result_(0),
      in_marshal_(0)
    {
      OMNIORB_ASSERT(PyTuple_Check(in_d));
      tstate_ = 0;
      in_l_   = PyTuple_GET_SIZE(in_d_);
      if (oneway) {
	OMNIORB_ASSERT(out_d_ == Py_None);
	out_l_ = -1;
      }
      else {
	OMNIORB_ASSERT(PyTuple_Check(out_d));
	out_l_ = PyTuple_GET_SIZE(out_d_);
      }
      if (args_) {
	OMNIORB_ASSERT(!is_upcall);
	Py_INCREF(args_);
      }
    }

    virtual ~Py_omniCallDescriptor();

    inline void releaseInterpreterLock() {
      OMNIORB_ASSERT(!tstate_);
      tstate_ = PyEval_SaveThread();
    }

    inline void reacquireInterpreterLock() {
      OMNIORB_ASSERT(tstate_);
      PyEval_RestoreThread(tstate_);
      tstate_ = 0;
    }

    inline void ensureInterpreterLock() {
      if (tstate_) {
        PyEval_RestoreThread(tstate_);
        tstate_ = 0;
      }
    }

    inline PyObject* args() { return args_; }

    // Extract and take ownership of stored results
    inline PyObject* result() { PyObject* r = result_; result_ = 0; return r; }

    //
    // Client side methods

    virtual void initialiseCall(cdrStream&);
    virtual void marshalArguments(cdrStream& stream);
    virtual void unmarshalReturnedValues(cdrStream& stream);
    virtual void userException(cdrStream& stream, _OMNI_NS(IOP_C)* iop_client,
			       const char* repoId);

    inline void systemException(const CORBA::SystemException& ex) {
      if (tstate_) {
	PyEval_RestoreThread(tstate_);
	tstate_ = 0;
      }
      handleSystemException(ex);
    }

    //
    // Server side methods

    virtual void unmarshalArguments(cdrStream& stream);

    // Throws BAD_PARAM if result is bad. _Always_ consumes result.
    void         setAndValidateReturnedValues(PyObject* result);

    // Simply set the returned values
    void         setReturnedValues(PyObject* result) { result_ = result; }

    // Marshal the returned values, and release the stored result
    virtual void marshalReturnedValues(cdrStream& stream);

  public:
    PyObject*      in_d_;
    int            in_l_;
    PyObject*      out_d_;
    int            out_l_;
    PyObject*      exc_d_;
    PyObject*      ctxt_d_;

  private:
    PyObject*      args_;
    PyObject*      result_;

    PyThreadState* tstate_;
    CORBA::Boolean in_marshal_;

    Py_omniCallDescriptor(const Py_omniCallDescriptor&);
    Py_omniCallDescriptor& operator=(const Py_omniCallDescriptor&);
  };


  ////////////////////////////////////////////////////////////////////////////
  // Servant object                                                         //
  ////////////////////////////////////////////////////////////////////////////

  class Py_omniServant : public virtual PortableServer::ServantBase {

  public:

    Py_omniServant(PyObject* pyservant, PyObject* opdict, const char* repoId);

    virtual ~Py_omniServant();

    virtual CORBA::Boolean _dispatch(omniCallHandle& handle);

    void remote_dispatch(Py_omniCallDescriptor* pycd);
    void local_dispatch (Py_omniCallDescriptor* pycd);

    PyObject* py_this();

    virtual void*                   _ptrToInterface(const char* repoId);
    virtual const char*             _mostDerivedRepoId();
    virtual CORBA::Boolean          _is_a(const char* logical_type_id);
    virtual PortableServer::POA_ptr _default_POA();
    virtual CORBA::Boolean          _non_existent();

    inline PyObject* pyServant() { Py_INCREF(pyservant_); return pyservant_; }

    // _add_ref and _remove_ref lock the Python interpreter lock
    // _locked versions assume the interpreter lock is already locked
    virtual void                    _add_ref();
    virtual void                    _remove_ref();
    void                            _locked_add_ref();
    void                            _locked_remove_ref();

  private:
    PyObject* pyservant_;	// Python servant object
    PyObject* opdict_;		// Operation descriptor dictionary
    PyObject* pyskeleton_;	// Skeleton class object
    char*     repoId_;
    int       refcount_;

    // Not implemented:
    Py_omniServant(const Py_omniServant&);
    Py_omniServant& operator=(const Py_omniServant&);
  };

  // Function to find or create a Py_omniServant object for a Python
  // servant object. If the Python object is not an instance of a
  // class derived from PortableServer.Servant, returns 0.
  //
  // Caller must hold the Python interpreter lock.
  static Py_omniServant* getServantForPyObject(PyObject* pyservant);


  ////////////////////////////////////////////////////////////////////////////
  // ServantManager / AdapterActivator implementations                      //
  ////////////////////////////////////////////////////////////////////////////

  class Py_ServantActivator
  {
  public:
    Py_ServantActivator(PyObject* pysa) : pysa_(pysa) { Py_INCREF(pysa_); }
    ~Py_ServantActivator() { Py_DECREF(pysa_); }

    PortableServer::Servant incarnate(const PortableServer::ObjectId& oid,
				      PortableServer::POA_ptr         poa);

    void etherealize(const PortableServer::ObjectId& oid,
		     PortableServer::POA_ptr         poa,
		     PortableServer::Servant         serv,
		     CORBA::Boolean                  cleanup_in_progress,
		     CORBA::Boolean                  remaining_activations);

    inline PyObject* pyobj() { return pysa_; }

  private:
    PyObject* pysa_;

    // Not implemented
    Py_ServantActivator(const Py_ServantActivator&);
    Py_ServantActivator& operator=(const Py_ServantActivator&);
  };

  class Py_ServantLocator
  {
  public:
    Py_ServantLocator(PyObject* pysl) : pysl_(pysl) { Py_INCREF(pysl_); }
    ~Py_ServantLocator() { Py_DECREF(pysl_); }

    PortableServer::Servant preinvoke(const PortableServer::ObjectId& oid,
				      PortableServer::POA_ptr poa,
				      const char*             operation,
				      void*&                  cookie);

    void postinvoke(const PortableServer::ObjectId& oid,
		    PortableServer::POA_ptr         poa,
		    const char*                     operation,
		    void*                           cookie,
		    PortableServer::Servant         serv);

    inline PyObject* pyobj() { return pysl_; }

  private:
    PyObject* pysl_;

    // Not implemented
    Py_ServantLocator(const Py_ServantLocator&);
    Py_ServantLocator& operator=(const Py_ServantLocator&);
  };

  class Py_AdapterActivator
  {
  public:
    Py_AdapterActivator(PyObject* pyaa) : pyaa_(pyaa) { Py_INCREF(pyaa_); }
    ~Py_AdapterActivator() { Py_DECREF(pyaa_); }

    CORBA::Boolean unknown_adapter(PortableServer::POA_ptr parent,
				   const char*             name);

    inline PyObject* pyobj() { return pyaa_; }

  private:
    PyObject* pyaa_;

    // Not implemented
    Py_AdapterActivator(const Py_AdapterActivator&);
    Py_AdapterActivator& operator=(const Py_AdapterActivator&);
  };

  // Function to create a C++ local object for a Python object. If the
  // Python object is not an instance of a mapped local object,
  // returns 0.
  //
  // Caller must hold the Python interpreter lock.
  static CORBA::LocalObject_ptr getLocalObjectForPyObject(PyObject* pyobj);

  // Convert a LocalObject to the underlying Python object. If the
  // object is not a suitable Python LocalObject, throw INV_OBJREF.
  //
  // Caller must hold the Python interpreter lock.
  static PyObject* getPyObjectForLocalObject(CORBA::LocalObject_ptr lobj);


  ////////////////////////////////////////////////////////////////////////////
  // PyUserException is a special CORBA::UserException                      //
  ////////////////////////////////////////////////////////////////////////////

  class PyUserException : public CORBA::UserException {
  public:
    // Constructor used during unmarshalling
    PyUserException(PyObject* desc);

    // Constructor used during marshalling. Throws BAD_PARAM with the
    // given completion status if the exception object doesn't match
    // the descriptor.
    // Always consumes reference to exc.
    PyUserException(PyObject* desc, PyObject* exc,
		    CORBA::CompletionStatus comp_status);

    // Copy constructor
    PyUserException(const PyUserException& e);

    virtual ~PyUserException();

    // Set the Python exception state to the contents of this exception.
    // Caller must hold the Python interpreter lock.
    // Returns 0 so callers can do "return ex.setPyExceptionState()".
    PyObject* setPyExceptionState();

    // DECREF the contained Python exception object. Caller must huld
    // the Python interpreter lock.
    void decrefPyException();

    // Marshalling operators for exception body, not including
    // repository id:

    // Caller must not hold interpreter lock
    void operator>>=(cdrStream& stream) const;

    // Caller must hold interpreter lock
    void operator<<=(cdrStream& stream);

    // Inherited virtual functions
    virtual void              _raise() const;
    virtual const char*       _NP_repoId(int* size)          const;
    virtual void              _NP_marshal(cdrStream& stream) const;
    virtual CORBA::Exception* _NP_duplicate()         	     const;
    virtual const char*       _NP_typeId()            	     const;

  private:
    PyObject* 	   desc_;          // Descriptor tuple
    PyObject* 	   exc_;           // The exception object
    CORBA::Boolean decref_on_del_; // True if exc_ should be DECREF'd when
				   // this object is deleted.
  };


  ////////////////////////////////////////////////////////////////////////////
  // InterpreterUnlocker releases the Python interpreter lock               //
  ////////////////////////////////////////////////////////////////////////////

  class InterpreterUnlocker {
  public:
    inline InterpreterUnlocker() {
      tstate_ = PyEval_SaveThread();
    }
    inline ~InterpreterUnlocker() {
      PyEval_RestoreThread(tstate_);
    }
    inline void lock() {
      PyEval_RestoreThread(tstate_);
    }
    inline void unlock() {
      tstate_ = PyEval_SaveThread();
    }
  private:
    PyThreadState* tstate_;
  };

  ////////////////////////////////////////////////////////////////////////////
  // ValueTrackerClearer safely clears a ValueTracker                       //
  ////////////////////////////////////////////////////////////////////////////

  class ValueTrackerClearer {
  public:
    inline ValueTrackerClearer(cdrStream& s) : s_(s) {}
    inline ~ValueTrackerClearer() {
      if (s_.valueTracker()) {
        InterpreterUnlocker u;
        s_.clearValueTracker();
      }
    };
  private:
    cdrStream& s_;
  };


  ////////////////////////////////////////////////////////////////////////////
  // PyUnlockingCdrStream unlocks the interpreter lock around blocking calls//
  ////////////////////////////////////////////////////////////////////////////

  class PyUnlockingCdrStream : public cdrStreamAdapter {
  public:
    PyUnlockingCdrStream(cdrStream& stream)
      : cdrStreamAdapter(stream)
    {
    }

    ~PyUnlockingCdrStream() { }

    // Override virtual functions in cdrStreamAdapter
    void put_octet_array(const _CORBA_Octet* b, int size,
			 omni::alignment_t align=omni::ALIGN_1);
    void get_octet_array(_CORBA_Octet* b,int size,
			 omni::alignment_t align=omni::ALIGN_1);
    void skipInput(_CORBA_ULong size);

    void copy_to(cdrStream&, int size, omni::alignment_t align=omni::ALIGN_1);

    void fetchInputData(omni::alignment_t,size_t);
    _CORBA_Boolean reserveOutputSpaceForPrimitiveType(omni::alignment_t align,
						      size_t required);
    _CORBA_Boolean maybeReserveOutputSpace(omni::alignment_t align,
					   size_t required);
  };

  ////////////////////////////////////////////////////////////////////////////
  // PyRefHolder holds a references to a Python object                      //
  ////////////////////////////////////////////////////////////////////////////

  class PyRefHolder {
  public:
    inline PyRefHolder(PyObject* obj) : obj_(obj) {}

    inline ~PyRefHolder() {
      Py_XDECREF(obj_);
    }
    inline PyObject* retn() {
      PyObject* r = obj_; obj_ = 0; return r;
    }
    inline PyObject* change(PyObject* o) {
      Py_XDECREF(obj_); obj_ = o; return o;
    }
  private:
    PyObject* obj_;
  };

};

#ifdef HAS_Cplusplus_catch_exception_by_base

#define OMNIPY_CATCH_AND_HANDLE_SYSTEM_EXCEPTIONS \
catch (const CORBA::SystemException& ex) { \
  return omniPy::handleSystemException(ex); \
}
#else

#define OMNIPY_CATCH_AND_HANDLE_SPECIFIED_EXCEPTION(exc) \
catch (const CORBA::exc& ex) { \
  return omniPy::handleSystemException(ex); \
}
#define OMNIPY_CATCH_AND_HANDLE_SYSTEM_EXCEPTIONS \
  OMNIORB_FOR_EACH_SYS_EXCEPTION(OMNIPY_CATCH_AND_HANDLE_SPECIFIED_EXCEPTION)

#endif


#endif // _omnipy_h_


syntax highlighted by Code2HTML, v. 0.9.1