1 // -*- Mode: C++; -*-
2 //                            Package   : omniORBpy
3 // pyExceptions.cc            Created on: 1999/07/29
4 //                            Author    : Duncan Grisby (dpg1)
5 //
6 //    Copyright (C) 2003-2014 Apasphere Ltd
7 //    Copyright (C) 1999 AT&T Laboratories Cambridge
8 //
9 //    This file is part of the omniORBpy library
10 //
11 //    The omniORBpy library is free software; you can redistribute it
12 //    and/or modify it under the terms of the GNU Lesser General
13 //    Public License as published by the Free Software Foundation;
14 //    either version 2.1 of the License, or (at your option) any later
15 //    version.
16 //
17 //    This library is distributed in the hope that it will be useful,
18 //    but WITHOUT ANY WARRANTY; without even the implied warranty of
19 //    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20 //    GNU Lesser General Public License for more details.
21 //
22 //    You should have received a copy of the GNU Lesser General Public
23 //    License along with this library. If not, see http://www.gnu.org/licenses/
24 //
25 //
26 // Description:
27 //    Exception handling functions
28 
29 #include <omnipy.h>
30 #include <pyThreadCache.h>
31 
32 
33 void
raise(const char * file,int line,CORBA::ULong minor,CORBA::CompletionStatus completed,PyObject * message)34 Py_BAD_PARAM::raise(const char* file, int line,
35 		    CORBA::ULong minor, CORBA::CompletionStatus completed,
36 		    PyObject* message)
37 {
38   if (omniORB::traceExceptions) {
39     omniORB::logger log;
40     log << "throw BAD_PARAM from " << _OMNI_NS(omniExHelper)::strip(file)
41 	<< ":" << line << " (";
42 
43     const char* description =
44       _OMNI_NS(minorCode2String)(_OMNI_NS(BAD_PARAM_LookupTable),minor);
45 
46     if (description) {
47       log << omniORB::logger::exceptionStatus(completed, description) << ")\n";
48     }
49     else {
50       log << omniORB::logger::exceptionStatus(completed, minor) << ")\n";
51     }
52   }
53   throw Py_BAD_PARAM(minor, completed, message);
54 }
55 
56 CORBA::Exception*
_NP_duplicate() const57 Py_BAD_PARAM::_NP_duplicate() const
58 {
59   return new Py_BAD_PARAM(*this);
60 }
61 
62 void
_raise() const63 Py_BAD_PARAM::_raise() const
64 {
65   throw *this;
66 }
67 
68 const char*
_NP_typeId() const69 Py_BAD_PARAM::_NP_typeId() const
70 {
71   return "Exception/SystemException/BAD_PARAM/Py_BAD_PARAM";
72 }
73 
74 Py_BAD_PARAM*
_downcast(CORBA::Exception * e)75 Py_BAD_PARAM::_downcast(CORBA::Exception* e)
76 {
77   return (Py_BAD_PARAM*)
78     _NP_is_a(e, "Exception/SystemException/BAD_PARAM/Py_BAD_PARAM");
79 }
80 
81 
82 PyObject*
handleSystemException(const CORBA::SystemException & ex,PyObject * info)83 omniPy::handleSystemException(const CORBA::SystemException& ex, PyObject* info)
84 {
85   int dummy;
86   PyObject* excc = PyDict_GetItemString(pyCORBAsysExcMap,
87 					(char*)ex._NP_repoId(&dummy));
88   OMNIORB_ASSERT(excc);
89 
90   PyObject* exca;
91   if (info) {
92     exca = Py_BuildValue((char*)"(iiO)", ex.minor(), ex.completed(), info);
93 
94     if (omniORB::traceExceptions) {
95       PyObject* info_repr = PyObject_Repr(info);
96       omniORB::logger log;
97       log << "BAD_PARAM info: " << String_AsString(info_repr) << "\n";
98       Py_DECREF(info_repr);
99     }
100   }
101   else {
102     exca = Py_BuildValue((char*)"(ii)", ex.minor(), ex.completed());
103   }
104 
105   PyObject* exci = PyEval_CallObject(excc, exca);
106   Py_DECREF(exca);
107   if (exci) {
108     // If we couldn't create the exception object, there will be a
109     // suitable error set already
110     PyErr_SetObject(excc, exci);
111     Py_DECREF(exci);
112   }
113   return 0;
114 }
115 
116 PyObject*
createPySystemException(const CORBA::SystemException & ex)117 omniPy::createPySystemException(const CORBA::SystemException& ex)
118 {
119   int dummy;
120   PyObject* excc = PyDict_GetItemString(pyCORBAsysExcMap,
121 					(char*)ex._NP_repoId(&dummy));
122   OMNIORB_ASSERT(excc);
123 
124   PyObject* exca = Py_BuildValue((char*)"(ii)", ex.minor(), ex.completed());
125   PyObject* exci = PyEval_CallObject(excc, exca);
126   Py_DECREF(exca);
127 
128   return exci;
129 }
130 
131 
132 void
produceSystemException(PyObject * eobj,PyObject * erepoId,PyObject * etype,PyObject * etraceback)133 omniPy::produceSystemException(PyObject* eobj, PyObject* erepoId,
134 			       PyObject* etype, PyObject* etraceback)
135 {
136   CORBA::ULong            minor  = 0;
137   CORBA::CompletionStatus status = CORBA::COMPLETED_MAYBE;
138 
139   PyRefHolder m(PyObject_GetAttrString(eobj, (char*)"minor"));
140   PyRefHolder c(PyObject_GetAttrString(eobj, (char*)"completed"));
141 
142   try {
143     if (m.valid())
144       minor = getULongVal(m);
145 
146     if (c.valid())
147       status = (CORBA::CompletionStatus)getEnumVal(c);
148   }
149   catch (Py_BAD_PARAM& bp) {
150     bp.logInfoAndDrop("Invalid data inside system exception");
151   }
152 
153   // Clear any errors raised by the GetAttrs
154   if (PyErr_Occurred()) PyErr_Clear();
155 
156   const char* repoId = String_AS_STRING(erepoId);
157 
158 #define THROW_SYSTEM_EXCEPTION_IF_MATCH(ex) \
159   if (omni::strMatch(repoId, "IDL:omg.org/CORBA/" #ex ":1.0")) { \
160     if (omniORB::traceExceptions) { \
161       { \
162 	PyObject* erepr = PyObject_Repr(eobj); \
163         omniORB::logger l; \
164         l << "Caught a CORBA system exception during up-call: " \
165           << String_AsString(erepr) << "\n";                    \
166         Py_DECREF(erepr); \
167       } \
168       PyErr_Restore(etype, eobj, etraceback); \
169       PyErr_Print(); \
170     } \
171     else { \
172       Py_XDECREF(eobj); Py_DECREF(etype); Py_XDECREF(etraceback); \
173     } \
174     Py_DECREF(erepoId); \
175     OMNIORB_THROW(ex, minor, status); \
176   }
177 
178   OMNIORB_FOR_EACH_SYS_EXCEPTION(THROW_SYSTEM_EXCEPTION_IF_MATCH)
179 
180 #undef THROW_SYSTEM_EXCEPTION_IF_MATCH
181 
182   if (omniORB::trace(1)) {
183     {
184       PyObject* erepr = PyObject_Repr(eobj);
185       omniORB::logger l;
186       l << "Caught an unexpected CORBA exception during up-call: "
187         << String_AsString(erepr) << "\n";
188       Py_DECREF(erepr);
189     }
190     PyErr_Restore(etype, eobj, etraceback);
191     PyErr_Print();
192   }
193   else {
194     Py_XDECREF(eobj); Py_DECREF(etype); Py_XDECREF(etraceback);
195   }
196   Py_DECREF(erepoId);
197 
198   if (m.valid() && c.valid())
199     OMNIORB_THROW(UNKNOWN, UNKNOWN_SystemException, CORBA::COMPLETED_MAYBE);
200   else
201     OMNIORB_THROW(UNKNOWN, UNKNOWN_UserException, CORBA::COMPLETED_MAYBE);
202 }
203 
204 
205 void
handlePythonException()206 omniPy::handlePythonException()
207 {
208   OMNIORB_ASSERT(PyErr_Occurred());
209 
210   PyObject *etype, *evalue, *etraceback;
211   PyObject *erepoId = 0;
212   PyErr_Fetch(&etype, &evalue, &etraceback);
213   PyErr_NormalizeException(&etype, &evalue, &etraceback);
214   OMNIORB_ASSERT(etype);
215 
216   if (evalue)
217     erepoId = PyObject_GetAttrString(evalue, (char*)"_NP_RepositoryId");
218 
219   if (!(erepoId && String_Check(erepoId))) {
220     PyErr_Clear();
221     Py_XDECREF(erepoId);
222     if (omniORB::trace(1)) {
223       {
224 	omniORB::logger l;
225 	l << "Caught an unexpected Python exception during up-call.\n";
226       }
227       PyErr_Restore(etype, evalue, etraceback);
228       PyErr_Print();
229     }
230     OMNIORB_THROW(UNKNOWN, UNKNOWN_PythonException,
231 		  CORBA::COMPLETED_MAYBE);
232   }
233 
234   // Is it a LOCATION_FORWARD?
235   if (omni::strMatch(String_AS_STRING(erepoId), "omniORB.LOCATION_FORWARD")) {
236     Py_DECREF(erepoId); Py_DECREF(etype); Py_XDECREF(etraceback);
237     omniPy::handleLocationForward(evalue);
238   }
239 
240   // System exception
241   omniPy::produceSystemException(evalue, erepoId, etype, etraceback);
242 }
243 
244 
245 PyObject*
raiseScopedException(PyObject * module,const char * scope,const char * cls)246 omniPy::raiseScopedException(PyObject* module, const char* scope,
247                              const char* cls)
248 {
249   omniPy::PyRefHolder excs(PyObject_GetAttrString(module, (char*)scope));
250   omniPy::PyRefHolder excc(PyObject_GetAttrString(excs,   (char*)cls));
251   omniPy::PyRefHolder exci(PyObject_CallObject(excc, omniPy::pyEmptyTuple));
252 
253   PyErr_SetObject(excc, exci);
254   return 0;
255 }
256 
257 
258 void
handleLocationForward(PyObject * evalue)259 omniPy::handleLocationForward(PyObject* evalue)
260 {
261   PyObject* pyfwd  = PyObject_GetAttrString(evalue, (char*)"_forward");
262   PyObject* pyperm = PyObject_GetAttrString(evalue, (char*)"_perm");
263   OMNIORB_ASSERT(pyfwd);
264   OMNIORB_ASSERT(pyperm);
265 
266   CORBA::Boolean perm = PyObject_IsTrue(pyperm);
267 
268   if (PyErr_Occurred()) {
269     perm = 0;
270 
271     if (omniORB::trace(1)) {
272       omniORB::logs(1, "Invalid 'permanent' attribute in LOCATION_FORWARD.");
273       PyErr_Print();
274     }
275     else
276       PyErr_Clear();
277   }
278 
279   CORBA::Object_ptr fwd = omniPy::getObjRef(pyfwd);
280 
281   if (fwd)
282     CORBA::Object::_duplicate(fwd);
283 
284   Py_DECREF(pyfwd);
285   Py_DECREF(pyperm);
286   Py_DECREF(evalue);
287 
288   if (fwd) {
289     OMNIORB_ASSERT(CORBA::Object::_PR_is_valid(fwd));
290     throw omniORB::LOCATION_FORWARD(fwd, perm);
291   }
292   else {
293     omniORB::logs(1, "Invalid object reference inside "
294 		  "omniORB.LOCATION_FORWARD exception");
295     OMNIORB_THROW(BAD_PARAM, BAD_PARAM_WrongPythonType, CORBA::COMPLETED_NO);
296   }
297 }
298 
299 
300 
301 //
302 // Implementation of PyUserException
303 //
304 
305 omniPy::
PyUserException(PyObject * desc)306 PyUserException::PyUserException(PyObject* desc)
307   : desc_(desc), exc_(0), decref_on_del_(0)
308 {
309   OMNIORB_ASSERT(desc_);
310   pd_insertToAnyFn    = 0;
311   pd_insertToAnyFnNCP = 0;
312 
313   if (omniORB::trace(25)) {
314     omniORB::logger l;
315     const char* repoId = String_AS_STRING(PyTuple_GET_ITEM(desc_, 2));
316     l << "Prepare to unmarshal Python user exception " << repoId << "\n";
317   }
318 }
319 
320 omniPy::
PyUserException(PyObject * desc,PyObject * exc,CORBA::CompletionStatus comp_status)321 PyUserException::PyUserException(PyObject* desc, PyObject* exc,
322 				 CORBA::CompletionStatus comp_status)
323   : desc_(desc), exc_(exc), decref_on_del_(1)
324 {
325   OMNIORB_ASSERT(desc_);
326   OMNIORB_ASSERT(exc_);
327 
328   if (omniORB::trace(25)) {
329     omniORB::logger l;
330     const char* repoId = String_AS_STRING(PyTuple_GET_ITEM(desc_, 2));
331     l << "Construct Python user exception " << repoId << "\n";
332   }
333 
334   try {
335     omniPy::validateType(desc_, exc_, comp_status);
336   }
337   catch (...) {
338     Py_DECREF(exc_);
339     throw;
340   }
341 
342   pd_insertToAnyFn    = 0;
343   pd_insertToAnyFnNCP = 0;
344 }
345 
346 omniPy::
PyUserException(const PyUserException & e)347 PyUserException::PyUserException(const PyUserException& e)
348   : desc_(e.desc_), exc_(e.exc_), decref_on_del_(1)
349 {
350   pd_insertToAnyFn    = 0;
351   pd_insertToAnyFnNCP = 0;
352 
353   // Oh dear. We need to mark the exception being copied to say that
354   // the exception object should not be DECREF'd when it is deleted.
355   ((PyUserException&)e).decref_on_del_ = 0;
356 }
357 
358 omniPy::
~PyUserException()359 PyUserException::~PyUserException()
360 {
361   if (decref_on_del_) {
362     if (omniORB::trace(25)) {
363       omniORB::logger l;
364       const char* repoId = String_AS_STRING(PyTuple_GET_ITEM(desc_, 2));
365       l << "Python user exception state " << repoId << " dropped unused\n";
366     }
367     omnipyThreadCache::lock _t;
368     OMNIORB_ASSERT(exc_);
369     Py_DECREF(exc_);
370   }
371 }
372 
373 PyObject*
374 omniPy::
setPyExceptionState()375 PyUserException::setPyExceptionState()
376 {
377   OMNIORB_ASSERT(desc_);
378   OMNIORB_ASSERT(exc_);
379 
380   PyObject* excclass = PyTuple_GET_ITEM(desc_, 1);
381 
382   if (omniORB::trace(25)) {
383     omniORB::logger l;
384     const char* repoId = String_AS_STRING(PyTuple_GET_ITEM(desc_, 2));
385     l << "Set Python user exception state " << repoId << "\n";
386   }
387   PyErr_SetObject(excclass, exc_);
388   Py_DECREF(exc_);
389   decref_on_del_ = 0;
390   exc_ = 0;
391   return 0;
392 }
393 
394 void
395 omniPy::
decrefPyException()396 PyUserException::decrefPyException()
397 {
398   OMNIORB_ASSERT(exc_);
399   Py_DECREF(exc_);
400   decref_on_del_ = 0;
401   exc_ = 0;
402 }
403 
404 
405 void
406 omniPy::
operator >>=(cdrStream & stream) const407 PyUserException::operator>>=(cdrStream& stream) const
408 {
409   OMNIORB_ASSERT(exc_);
410 
411   if (omniORB::trace(25)) {
412     omniORB::logger l;
413     const char* repoId = String_AS_STRING(PyTuple_GET_ITEM(desc_, 2));
414     l << "Marshal Python user exception " << repoId << "\n";
415   }
416 
417   PyUnlockingCdrStream pystream(stream);
418 
419   int cnt = (PyTuple_GET_SIZE(desc_) - 4) / 2;
420 
421   PyObject* name;
422   PyObject* value;
423 
424   int i, j;
425   for (i=0,j=4; i < cnt; i++) {
426     name  = PyTuple_GET_ITEM(desc_, j++);
427     value = PyObject_GetAttr(exc_, name);
428     Py_DECREF(value); // Exception object still holds a reference.
429     omniPy::marshalPyObject(pystream, PyTuple_GET_ITEM(desc_, j++), value);
430   }
431 }
432 
433 void
434 omniPy::
operator <<=(cdrStream & stream)435 PyUserException::operator<<=(cdrStream& stream)
436 {
437   if (omniORB::trace(25)) {
438     omniORB::logger l;
439     const char* repoId = String_AS_STRING(PyTuple_GET_ITEM(desc_, 2));
440     l << "Unmarshal Python user exception " << repoId << "\n";
441   }
442 
443   PyUnlockingCdrStream pystream(stream);
444 
445   PyObject* excclass = PyTuple_GET_ITEM(desc_, 1);
446 
447   int       cnt      = (PyTuple_GET_SIZE(desc_) - 4) / 2;
448   PyObject* exctuple = PyTuple_New(cnt);
449   omniPy::PyRefHolder exctuple_holder(exctuple);
450 
451   int i, j;
452   for (i=0, j=5; i < cnt; i++, j+=2) {
453     PyTuple_SET_ITEM(exctuple, i,
454 		     unmarshalPyObject(pystream,
455 				       PyTuple_GET_ITEM(desc_, j)));
456   }
457   exc_ = PyEval_CallObject(excclass, exctuple);
458 
459   if (!exc_) {
460     // Oh dear. Python exception constructor threw an exception.
461     if (omniORB::trace(1)) {
462       {
463 	omniORB::logger l;
464 	l << "Caught unexpected error trying to create an exception:\n";
465       }
466       PyErr_Print();
467     }
468     else
469       PyErr_Clear();
470 
471     OMNIORB_THROW(INTERNAL, 0, CORBA::COMPLETED_MAYBE);
472   }
473 }
474 
475 void
476 omniPy::
_raise() const477 PyUserException::_raise() const
478 {
479   OMNIORB_ASSERT(desc_);
480   OMNIORB_ASSERT(exc_);
481 
482   if (omniORB::trace(25)) {
483     omniORB::logger l;
484     const char* repoId = String_AS_STRING(PyTuple_GET_ITEM(desc_, 2));
485     l << "C++ throw of Python user exception " << repoId << "\n";
486   }
487   throw *this;
488 }
489 
490 const char*
491 omniPy::
_NP_repoId(int * size) const492 PyUserException::_NP_repoId(int* size) const
493 {
494   PyObject* pyrepoId = PyTuple_GET_ITEM(desc_, 2);
495   OMNIORB_ASSERT(String_Check(pyrepoId));
496 
497   CORBA::ULong len;
498   const char*  repoId = String_AS_STRING_AND_SIZE(pyrepoId, len);
499 
500   *size = len + 1;
501   return repoId;
502 }
503 
504 void
505 omniPy::
_NP_marshal(cdrStream & stream) const506 PyUserException::_NP_marshal(cdrStream& stream) const
507 {
508   omnipyThreadCache::lock _t;
509   *this >>= stream;
510 }
511 
512 CORBA::Exception*
513 omniPy::
_NP_duplicate() const514 PyUserException::_NP_duplicate() const
515 {
516   return new PyUserException(*this);
517 }
518 
519 const char*
520 omniPy::PyUserException::
521 _PD_typeId = "Exception/UserException/omniPy::PyUserException";
522 
523 const char*
524 omniPy::
_NP_typeId() const525 PyUserException::_NP_typeId() const
526 {
527   return _PD_typeId;
528 }
529 
530 omniPy::PyUserException*
_downcast(CORBA::Exception * ex)531 omniPy::PyUserException::_downcast(CORBA::Exception* ex)
532 {
533   return (omniPy::PyUserException*)_NP_is_a(ex, _PD_typeId);
534 }
535 
536 const omniPy::PyUserException*
_downcast(const CORBA::Exception * ex)537 omniPy::PyUserException::_downcast(const CORBA::Exception* ex)
538 {
539   return (const omniPy::PyUserException*)_NP_is_a(ex, _PD_typeId);
540 }
541