// -*- Mode: C++; -*-
//                            Package   : omniORBpy
// pyExceptions.cc            Created on: 1999/07/29
//                            Author    : Duncan Grisby (dpg1)
//
//    Copyright (C) 1999 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:
//    Exception handling functions

// $Id: pyExceptions.cc,v 1.1.4.8 2006/07/05 10:46:16 dgrisby Exp $

// $Log: pyExceptions.cc,v $
// Revision 1.1.4.8  2006/07/05 10:46:16  dgrisby
// Dump exception tracebacks with traceExceptions, not traceLevel 10.
//
// Revision 1.1.4.7  2006/05/15 10:26:11  dgrisby
// More relaxation of requirements for old-style classes, for Python 2.5.
//
// Revision 1.1.4.6  2005/07/22 17:41:08  dgrisby
// Update from omnipy2_develop.
//
// Revision 1.1.4.5  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.1.4.4  2005/01/25 11:45:48  dgrisby
// Merge from omnipy2_develop; set RPM version.
//
// Revision 1.1.4.3  2005/01/07 00:22:32  dgrisby
// Big merge from omnipy2_develop.
//
// Revision 1.1.4.2  2003/05/20 17:10:23  dgrisby
// Preliminary valuetype support.
//
// Revision 1.1.4.1  2003/03/23 21:51:57  dgrisby
// New omnipy3_develop branch.
//
// Revision 1.1.2.8  2001/10/18 15:48:39  dpg1
// Track ORB core changes.
//
// Revision 1.1.2.7  2001/09/24 10:48:25  dpg1
// Meaningful minor codes.
//
// Revision 1.1.2.6  2001/08/01 10:12:36  dpg1
// Main thread policy.
//
// Revision 1.1.2.5  2001/05/29 17:10:14  dpg1
// Support for in process identity.
//
// Revision 1.1.2.4  2001/05/14 12:47:21  dpg1
// Fix memory leaks.
//
// Revision 1.1.2.3  2001/05/10 15:16:02  dpg1
// Big update to support new omniORB 4 internals.
//
// Revision 1.1.2.2  2000/11/22 14:42:05  dpg1
// Remove dead omniORB 2.8 code.
//
// Revision 1.1.2.1  2000/10/13 13:55:24  dpg1
// Initial support for omniORB 4.
//

#include <omnipy.h>
#include <pyThreadCache.h>


PyObject*
omniPy::handleSystemException(const CORBA::SystemException& ex)
{
  int dummy;
  PyObject* excc = PyDict_GetItemString(pyCORBAsysExcMap,
					(char*)ex._NP_repoId(&dummy));
  OMNIORB_ASSERT(excc);

  PyObject* exca = Py_BuildValue((char*)"(ii)", ex.minor(), ex.completed());
  PyObject* exci = PyEval_CallObject(excc, exca);
  Py_DECREF(exca);
  if (exci) {
    // If we couldn't create the exception object, there will be a
    // suitable error set already
    PyErr_SetObject(excc, exci);
    Py_DECREF(exci);
  }
  return 0;
}

PyObject*
omniPy::createPySystemException(const CORBA::SystemException& ex)
{
  int dummy;
  PyObject* excc = PyDict_GetItemString(pyCORBAsysExcMap,
					(char*)ex._NP_repoId(&dummy));
  OMNIORB_ASSERT(excc);

  PyObject* exca = Py_BuildValue((char*)"(ii)", ex.minor(), ex.completed());
  PyObject* exci = PyEval_CallObject(excc, exca);
  Py_DECREF(exca);

  return exci;
}


void
omniPy::produceSystemException(PyObject* eobj, PyObject* erepoId,
			       PyObject* etype, PyObject* etraceback)
{
  CORBA::ULong            minor  = 0;
  CORBA::CompletionStatus status = CORBA::COMPLETED_MAYBE;

  PyObject *m = 0, *c = 0, *v = 0;

  m = PyObject_GetAttrString(eobj, (char*)"minor");

  if (m) {
    if (PyInt_Check(m)) {
      minor = PyInt_AS_LONG(m);
    }
    else if (PyLong_Check(m)) {
      minor = PyLong_AsUnsignedLong(m);
      if (minor == (CORBA::ULong)-1 && PyErr_Occurred())
	PyErr_Clear();
    }
    c = PyObject_GetAttrString(eobj, (char*)"completed");

    if (c) {
      v = PyObject_GetAttrString(c, (char*)"_v");

      if (v && PyInt_Check(v))
	status = (CORBA::CompletionStatus)PyInt_AS_LONG(v);
    }
  }
  Py_XDECREF(m); Py_XDECREF(c); Py_XDECREF(v);

  // Clear any errors raised by the GetAttrs
  if (PyErr_Occurred()) PyErr_Clear();

  char* repoId = PyString_AS_STRING(erepoId);

#define THROW_SYSTEM_EXCEPTION_IF_MATCH(ex) \
  if (omni::strMatch(repoId, "IDL:omg.org/CORBA/" #ex ":1.0")) { \
    if (omniORB::traceExceptions) { \
      { \
        omniORB::logger l; \
        l << "Caught a CORBA system exception during up-call: " \
          << PyString_AS_STRING(erepoId) << "\n"; \
      } \
      PyErr_Restore(etype, eobj, etraceback); \
      PyErr_Print(); \
    } \
    else { \
      Py_XDECREF(eobj); Py_DECREF(etype); Py_XDECREF(etraceback); \
    } \
    Py_DECREF(erepoId); \
    OMNIORB_THROW(ex, minor, status); \
  }

  OMNIORB_FOR_EACH_SYS_EXCEPTION(THROW_SYSTEM_EXCEPTION_IF_MATCH)

#undef THROW_SYSTEM_EXCEPTION_IF_MATCH

  if (omniORB::trace(1)) {
    {
      omniORB::logger l;
      l << "Caught an unexpected CORBA exception during up-call: "
        << PyString_AS_STRING(erepoId) << "\n";
    }
    PyErr_Restore(etype, eobj, etraceback);
    PyErr_Print();
  }
  else {
    Py_XDECREF(eobj); Py_DECREF(etype); Py_XDECREF(etraceback);
  }
  Py_DECREF(erepoId);
  if (m && c && v)
    OMNIORB_THROW(UNKNOWN, UNKNOWN_SystemException, CORBA::COMPLETED_MAYBE);
  else
    OMNIORB_THROW(UNKNOWN, UNKNOWN_UserException, CORBA::COMPLETED_MAYBE);
}


void
omniPy::handlePythonException()
{
  OMNIORB_ASSERT(PyErr_Occurred());

  PyObject *etype, *evalue, *etraceback;
  PyObject *erepoId = 0;
  PyErr_Fetch(&etype, &evalue, &etraceback);
  PyErr_NormalizeException(&etype, &evalue, &etraceback);
  OMNIORB_ASSERT(etype);

  if (evalue)
    erepoId = PyObject_GetAttrString(evalue, (char*)"_NP_RepositoryId");

  if (!(erepoId && PyString_Check(erepoId))) {
    Py_XDECREF(erepoId);
    if (omniORB::trace(1)) {
      {
	omniORB::logger l;
	l << "Caught an unexpected Python exception during up-call.\n";
      }
      PyErr_Restore(etype, evalue, etraceback);
      PyErr_Print();
    }
    OMNIORB_THROW(UNKNOWN, UNKNOWN_PythonException,
		  CORBA::COMPLETED_MAYBE);
  }

  // Is it a LOCATION_FORWARD?
  if (omni::strMatch(PyString_AS_STRING(erepoId),
		     "omniORB.LOCATION_FORWARD")) {
    Py_DECREF(erepoId); Py_DECREF(etype); Py_XDECREF(etraceback);
    omniPy::handleLocationForward(evalue);
  }

  // System exception
  omniPy::produceSystemException(evalue, erepoId, etype, etraceback);
}


void
omniPy::handleLocationForward(PyObject* evalue)
{
  PyObject* pyfwd  = PyObject_GetAttrString(evalue, (char*)"_forward");
  PyObject* pyperm = PyObject_GetAttrString(evalue, (char*)"_perm");
  OMNIORB_ASSERT(pyfwd);
  OMNIORB_ASSERT(pyperm);

  CORBA::Boolean perm;

  if (PyInt_Check(pyperm)) {
    perm = PyInt_AS_LONG(pyperm) ? 1 : 0;
  }
  else {
    omniORB::logs(1, "Bad 'permanent' field in LOCATION_FORWARD. "
		  "Using FALSE.");
    perm = 0;
  }
  CORBA::Object_ptr fwd =
    (CORBA::Object_ptr)omniPy::getTwin(pyfwd, OBJREF_TWIN);

  if (fwd)
    CORBA::Object::_duplicate(fwd);

  Py_DECREF(pyfwd);
  Py_DECREF(pyperm);
  Py_DECREF(evalue);
  if (fwd) {
    OMNIORB_ASSERT(CORBA::Object::_PR_is_valid(fwd));
    throw omniORB::LOCATION_FORWARD(fwd, perm);
  }
  else {
    omniORB::logs(1, "Invalid object reference inside "
		  "omniORB.LOCATION_FORWARD exception");
    OMNIORB_THROW(BAD_PARAM, BAD_PARAM_WrongPythonType, CORBA::COMPLETED_NO);
  }
}



//
// Implementation of PyUserException
//

omniPy::
PyUserException::PyUserException(PyObject* desc)
  : desc_(desc), exc_(0), decref_on_del_(0)
{
  OMNIORB_ASSERT(desc_);
  pd_insertToAnyFn    = 0;
  pd_insertToAnyFnNCP = 0;

  if (omniORB::trace(25)) {
    omniORB::logger l;
    const char* repoId = PyString_AS_STRING(PyTuple_GET_ITEM(desc_, 2));
    l << "Prepare to unmarshal Python user exception " << repoId << "\n";
  }
}

omniPy::
PyUserException::PyUserException(PyObject* desc, PyObject* exc,
				 CORBA::CompletionStatus comp_status)
  : desc_(desc), exc_(exc), decref_on_del_(1)
{
  OMNIORB_ASSERT(desc_);
  OMNIORB_ASSERT(exc_);

  if (omniORB::trace(25)) {
    omniORB::logger l;
    const char* repoId = PyString_AS_STRING(PyTuple_GET_ITEM(desc_, 2));
    l << "Construct Python user exception " << repoId << "\n";
  }

  try {
    omniPy::validateType(desc_, exc_, comp_status);
  }
  catch (...) {
    Py_DECREF(exc_);
    throw;
  }

  pd_insertToAnyFn    = 0;
  pd_insertToAnyFnNCP = 0;
}

omniPy::
PyUserException::PyUserException(const PyUserException& e)
  : desc_(e.desc_), exc_(e.exc_), decref_on_del_(1)
{
  pd_insertToAnyFn    = 0;
  pd_insertToAnyFnNCP = 0;

  // Oh dear. We need to mark the exception being copied to say that
  // the exception object should not be DECREF'd when it is deleted.
  ((PyUserException&)e).decref_on_del_ = 0;
}

omniPy::
PyUserException::~PyUserException()
{
  if (decref_on_del_) {
    if (omniORB::trace(25)) {
      omniORB::logger l;
      const char* repoId = PyString_AS_STRING(PyTuple_GET_ITEM(desc_, 2));
      l << "Python user exception state " << repoId << " dropped unused\n";
    }
    omnipyThreadCache::lock _t;
    OMNIORB_ASSERT(exc_);
    Py_DECREF(exc_);
  }
}

PyObject*
omniPy::
PyUserException::setPyExceptionState()
{
  OMNIORB_ASSERT(desc_);
  OMNIORB_ASSERT(exc_);

  PyObject* excclass = PyTuple_GET_ITEM(desc_, 1);
  
  if (omniORB::trace(25)) {
    omniORB::logger l;
    const char* repoId = PyString_AS_STRING(PyTuple_GET_ITEM(desc_, 2));
    l << "Set Python user exception state " << repoId << "\n";
  }
  PyErr_SetObject(excclass, exc_);
  Py_DECREF(exc_);
  decref_on_del_ = 0;
  exc_ = 0;
  return 0;
}

void
omniPy::
PyUserException::decrefPyException()
{
  OMNIORB_ASSERT(exc_);
  Py_DECREF(exc_);
  decref_on_del_ = 0;
  exc_ = 0;
}


void
omniPy::
PyUserException::operator>>=(cdrStream& stream) const
{
  OMNIORB_ASSERT(exc_);

  if (omniORB::trace(25)) {
    omniORB::logger l;
    const char* repoId = PyString_AS_STRING(PyTuple_GET_ITEM(desc_, 2));
    l << "Marshal Python user exception " << repoId << "\n";
  }

  PyUnlockingCdrStream pystream(stream);

  int cnt = (PyTuple_GET_SIZE(desc_) - 4) / 2;

  PyObject* name;
  PyObject* value;

  int i, j;
  for (i=0,j=4; i < cnt; i++) {
    name  = PyTuple_GET_ITEM(desc_, j++);
    value = PyObject_GetAttr(exc_, name);
    Py_DECREF(value); // Exception object still holds a reference.
    omniPy::marshalPyObject(pystream, PyTuple_GET_ITEM(desc_, j++), value);
  }
}

void
omniPy::
PyUserException::operator<<=(cdrStream& stream)
{
  if (omniORB::trace(25)) {
    omniORB::logger l;
    const char* repoId = PyString_AS_STRING(PyTuple_GET_ITEM(desc_, 2));
    l << "Unmarshal Python user exception " << repoId << "\n";
  }

  PyUnlockingCdrStream pystream(stream);

  PyObject* excclass = PyTuple_GET_ITEM(desc_, 1);

  int       cnt      = (PyTuple_GET_SIZE(desc_) - 4) / 2;
  PyObject* exctuple = PyTuple_New(cnt);
  omniPy::PyRefHolder exctuple_holder(exctuple);

  int i, j;
  for (i=0, j=5; i < cnt; i++, j+=2) {
    PyTuple_SET_ITEM(exctuple, i,
		     unmarshalPyObject(pystream,
				       PyTuple_GET_ITEM(desc_, j)));
  }
  exc_ = PyEval_CallObject(excclass, exctuple);

  if (!exc_) {
    // Oh dear. Python exception constructor threw an exception.
    if (omniORB::trace(25)) {
      {
	omniORB::logger l;
	l << "Caught unexpected error trying to create an exception:\n";
      }
      PyErr_Print();
    }
    OMNIORB_THROW(INTERNAL, 0, CORBA::COMPLETED_MAYBE);
  }
}

void
omniPy::
PyUserException::_raise() const
{
  OMNIORB_ASSERT(desc_);
  OMNIORB_ASSERT(exc_);

  if (omniORB::trace(25)) {
    omniORB::logger l;
    const char* repoId = PyString_AS_STRING(PyTuple_GET_ITEM(desc_, 2));
    l << "C++ throw of Python user exception " << repoId << "\n";
  }
  throw *this;
}

const char*
omniPy::
PyUserException::_NP_repoId(int* size) const
{
  PyObject* pyrepoId = PyTuple_GET_ITEM(desc_, 2);
  OMNIORB_ASSERT(PyString_Check(pyrepoId));

  *size = PyString_GET_SIZE(pyrepoId) + 1;
  return PyString_AS_STRING(pyrepoId);
}

void
omniPy::
PyUserException::_NP_marshal(cdrStream& stream) const
{
  omnipyThreadCache::lock _t;
  *this >>= stream;
}

CORBA::Exception*
omniPy::
PyUserException::_NP_duplicate() const
{
  return new PyUserException(*this);
}

const char*
omniPy::
PyUserException::_NP_typeId() const
{
  int cannot_downcast = 0;
  OMNIORB_ASSERT(cannot_downcast);
  return 0;
}


syntax highlighted by Code2HTML, v. 0.9.1