1 //
2 // Copyright (c) ZeroC, Inc. All rights reserved.
3 //
4 
5 #include <Util.h>
6 #include <Ice/LocalException.h>
7 #include <Ice/Protocol.h>
8 #include <stdarg.h>
9 
10 #ifdef HAVE_RUBY_ENCODING_H
11 #  include <ruby/encoding.h>
12 #endif
13 
14 using namespace std;
15 using namespace IceRuby;
16 
17 namespace
18 {
19 
20 template<typename T>
21 bool
setVersion(VALUE p,const T & version)22 setVersion(VALUE p, const T& version)
23 {
24     volatile VALUE major = callRuby(rb_int2inum, version.major);
25     volatile VALUE minor = callRuby(rb_int2inum, version.minor);
26     rb_ivar_set(p, rb_intern("@major"), major);
27     rb_ivar_set(p, rb_intern("@minor"), minor);
28 
29     return true;
30 }
31 
32 template<typename T>
33 bool
getVersion(VALUE p,T & v)34 getVersion(VALUE p, T& v)
35 {
36     volatile VALUE major = callRuby(rb_ivar_get, p, rb_intern("@major"));
37     volatile VALUE minor = callRuby(rb_ivar_get, p, rb_intern("@minor"));
38 
39     long m;
40 
41     m = getInteger(major);
42     if(m < 0 || m > 255)
43     {
44         throw RubyException(rb_eTypeError, "version major must be a value between 0 and 255");
45         return false;
46     }
47     v.major = m;
48 
49     m = getInteger(minor);
50     if(m < 0 || m > 255)
51     {
52         throw RubyException(rb_eTypeError, "version minor must be a value between 0 and 255");
53         return false;
54     }
55     v.minor = m;
56 
57     return true;
58 }
59 
60 template<typename T>
61 VALUE
createVersion(const T & version,const char * type)62 createVersion(const T& version, const char* type)
63 {
64     volatile VALUE rbType = callRuby(rb_path2class, type);
65     assert(!NIL_P(rbType));
66 
67     volatile VALUE obj = callRuby(rb_class_new_instance, 0, static_cast<VALUE*>(0), rbType);
68 
69     if(!setVersion<T>(obj, version))
70     {
71         return Qnil;
72     }
73 
74     return obj;
75 }
76 
77 template<typename T>
78 VALUE
versionToString(VALUE p,const char * type)79 versionToString(VALUE p, const char* type)
80 {
81     volatile VALUE rbType = callRuby(rb_path2class, type);
82     assert(!NIL_P(rbType));
83     if(callRuby(rb_obj_is_instance_of, p, rbType) != Qtrue)
84     {
85         throw RubyException(rb_eTypeError, "argument is not an instance of %s", type);
86     }
87 
88     T v;
89     if(!getVersion<T>(p, v))
90     {
91         return Qnil;
92     }
93 
94     ICE_RUBY_TRY
95     {
96         string s = IceInternal::versionToString<T>(v);
97         return createString(s);
98     }
99     ICE_RUBY_CATCH
100     return Qnil;
101 }
102 
103 template<typename T>
104 VALUE
stringToVersion(VALUE p,const char * type)105 stringToVersion(VALUE p, const char* type)
106 {
107     string str = getString(p);
108 
109     ICE_RUBY_TRY
110     {
111         T v = IceInternal::stringToVersion<T>(str);
112         return createVersion<T>(v, type);
113     }
114     ICE_RUBY_CATCH
115     return Qnil;
116 }
117 
118 char Ice_ProtocolVersion[] = "Ice::ProtocolVersion";
119 char Ice_EncodingVersion[] = "Ice::EncodingVersion";
120 
121 }
122 
123 extern "C"
124 VALUE
IceRuby_stringVersion(int,VALUE *,VALUE)125 IceRuby_stringVersion(int /*argc*/, VALUE* /*argv*/, VALUE /*self*/)
126 {
127     ICE_RUBY_TRY
128     {
129         string s = ICE_STRING_VERSION;
130         return createString(s);
131     }
132     ICE_RUBY_CATCH
133     return Qnil;
134 }
135 
136 extern "C"
137 VALUE
IceRuby_intVersion(int,VALUE *,VALUE)138 IceRuby_intVersion(int /*argc*/, VALUE* /*argv*/, VALUE /*self*/)
139 {
140     ICE_RUBY_TRY
141     {
142         return INT2FIX(ICE_INT_VERSION);
143     }
144     ICE_RUBY_CATCH
145     return Qnil;
146 }
147 
148 extern "C"
149 VALUE
IceRuby_currentProtocol(int,VALUE *,VALUE)150 IceRuby_currentProtocol(int /*argc*/, VALUE* /*argv*/, VALUE /*self*/)
151 {
152     ICE_RUBY_TRY
153     {
154         return createProtocolVersion(Ice::currentProtocol);
155     }
156     ICE_RUBY_CATCH
157     return Qnil;
158 }
159 
160 extern "C"
161 VALUE
IceRuby_currentProtocolEncoding(int,VALUE *,VALUE)162 IceRuby_currentProtocolEncoding(int /*argc*/, VALUE* /*argv*/, VALUE /*self*/)
163 {
164     ICE_RUBY_TRY
165     {
166         return createEncodingVersion(Ice::currentProtocolEncoding);
167     }
168     ICE_RUBY_CATCH
169     return Qnil;
170 }
171 
172 extern "C"
173 VALUE
IceRuby_currentEncoding(int,VALUE *,VALUE)174 IceRuby_currentEncoding(int /*argc*/, VALUE* /*argv*/, VALUE /*self*/)
175 {
176     ICE_RUBY_TRY
177     {
178         return createEncodingVersion(Ice::currentEncoding);
179     }
180     ICE_RUBY_CATCH
181     return Qnil;
182 }
183 
184 extern "C"
185 VALUE
IceRuby_protocolVersionToString(VALUE,VALUE v)186 IceRuby_protocolVersionToString(VALUE /*self*/, VALUE v)
187 {
188     return versionToString<Ice::ProtocolVersion>(v, Ice_ProtocolVersion);
189 }
190 
191 extern "C"
192 VALUE
IceRuby_stringToProtocolVersion(VALUE,VALUE v)193 IceRuby_stringToProtocolVersion(VALUE /*self*/, VALUE v)
194 {
195     return stringToVersion<Ice::ProtocolVersion>(v, Ice_ProtocolVersion);
196 }
197 
198 extern "C"
199 VALUE
IceRuby_encodingVersionToString(VALUE,VALUE v)200 IceRuby_encodingVersionToString(VALUE /*self*/, VALUE v)
201 {
202     return versionToString<Ice::EncodingVersion>(v, Ice_EncodingVersion);
203 }
204 
205 extern "C"
206 VALUE
IceRuby_stringToEncodingVersion(VALUE,VALUE v)207 IceRuby_stringToEncodingVersion(VALUE /*self*/, VALUE v)
208 {
209     return stringToVersion<Ice::EncodingVersion>(v, Ice_EncodingVersion);
210 }
211 
212 void
initUtil(VALUE iceModule)213 IceRuby::initUtil(VALUE iceModule)
214 {
215     rb_define_module_function(iceModule, "stringVersion", CAST_METHOD(IceRuby_stringVersion), -1);
216     rb_define_module_function(iceModule, "intVersion", CAST_METHOD(IceRuby_intVersion), -1);
217     rb_define_module_function(iceModule, "currentProtocol", CAST_METHOD(IceRuby_currentProtocol), -1);
218     rb_define_module_function(iceModule, "currentProtocolEncoding", CAST_METHOD(IceRuby_currentProtocolEncoding), -1);
219     rb_define_module_function(iceModule, "currentEncoding", CAST_METHOD(IceRuby_currentEncoding), -1);
220     rb_define_module_function(iceModule, "protocolVersionToString", CAST_METHOD(IceRuby_protocolVersionToString), 1);
221     rb_define_module_function(iceModule, "stringToProtocolVersion", CAST_METHOD(IceRuby_stringToProtocolVersion), 1);
222     rb_define_module_function(iceModule, "encodingVersionToString", CAST_METHOD(IceRuby_encodingVersionToString), 1);
223     rb_define_module_function(iceModule, "stringToEncodingVersion", CAST_METHOD(IceRuby_stringToEncodingVersion), 1);
224 }
225 
RubyException()226 IceRuby::RubyException::RubyException()
227 {
228     ex = rb_gv_get("$!");
229 }
230 
RubyException(VALUE exv)231 IceRuby::RubyException::RubyException(VALUE exv) :
232     ex(exv)
233 {
234 }
235 
RubyException(VALUE exClass,const char * fmt,...)236 IceRuby::RubyException::RubyException(VALUE exClass, const char* fmt, ...)
237 {
238     va_list args;
239     char buf[BUFSIZ];
240 
241     va_start(args, fmt);
242     vsnprintf(buf, BUFSIZ, fmt, args);
243     buf[BUFSIZ - 1] = '\0';
244     va_end(args);
245 
246     ex = callRuby(rb_exc_new2, exClass, buf);
247 }
248 
249 ostream&
operator <<(ostream & ostr) const250 IceRuby::RubyException::operator<<(ostream& ostr) const
251 {
252     volatile VALUE cls = rb_class_path(CLASS_OF(ex));
253     volatile VALUE msg = rb_obj_as_string(ex);
254     ostr << RSTRING_PTR(cls) << ": " << RSTRING_PTR(msg);
255     return ostr;
256 }
257 
258 bool
isString(VALUE val)259 IceRuby::isString(VALUE val)
260 {
261     return TYPE(val) == T_STRING || callRuby(rb_respond_to, val, rb_intern("to_str")) != 0;
262 }
263 
264 bool
isArray(VALUE val)265 IceRuby::isArray(VALUE val)
266 {
267     return TYPE(val) == T_ARRAY || callRuby(rb_respond_to, val, rb_intern("to_arr")) != 0;
268 }
269 
270 bool
isHash(VALUE val)271 IceRuby::isHash(VALUE val)
272 {
273     return TYPE(val) == T_HASH || callRuby(rb_respond_to, val, rb_intern("to_hash")) != 0;
274 }
275 
276 string
getString(VALUE val)277 IceRuby::getString(VALUE val)
278 {
279     volatile VALUE result = callRuby(rb_string_value, &val);
280     return string(RSTRING_PTR(result), RSTRING_LEN(result));
281 }
282 
283 VALUE
createString(const string & str)284 IceRuby::createString(const string& str)
285 {
286 #ifdef HAVE_RUBY_ENCODING_H
287     return callRuby(rb_enc_str_new, str.c_str(), static_cast<long>(str.size()), rb_utf8_encoding());
288 #else
289     return callRuby(rb_str_new, str.c_str(), static_cast<long>(str.size()));
290 #endif
291 }
292 
293 namespace
294 {
295 
296 template <typename T>
297 struct RubyCallArgs
298 {
299     volatile VALUE val;
300     T ret;
301 };
302 
303 //
304 // Wrapper function to call rb_num2long with rb_protect
305 //
306 VALUE
rb_num2long_wrapper(VALUE val)307 rb_num2long_wrapper(VALUE val)
308 {
309     RubyCallArgs<long>* data = (RubyCallArgs<long>*)val;
310     data->ret = rb_num2long(data->val);
311     return val;
312 }
313 
314 //
315 // Wrapper function to call rb_num2ll with rb_protect
316 //
317 VALUE
rb_num2ll_wrapper(VALUE val)318 rb_num2ll_wrapper(VALUE val)
319 {
320     RubyCallArgs<Ice::Long>* data = (RubyCallArgs<Ice::Long>*)val;
321     data->ret = rb_num2ll(data->val);
322     return val;
323 }
324 
325 }
326 
327 long
getInteger(VALUE val)328 IceRuby::getInteger(VALUE val)
329 {
330     RubyCallArgs<long> arg= {val, -1};
331     int error = 0;
332     rb_protect(rb_num2long_wrapper, (VALUE)&arg, &error);
333     if(error)
334     {
335         throw RubyException(rb_eTypeError, "unable to convert value to an int");
336     }
337     return arg.ret;
338 }
339 
340 Ice::Long
getLong(VALUE val)341 IceRuby::getLong(VALUE val)
342 {
343     RubyCallArgs<Ice::Long> arg= {val, -1};
344     int error = 0;
345     rb_protect(rb_num2ll_wrapper, (VALUE)&arg, &error);
346     if(error)
347     {
348         throw RubyException(rb_eTypeError, "unable to convert value to a long");
349     }
350     return arg.ret;
351 }
352 
353 bool
arrayToStringSeq(VALUE val,vector<string> & seq)354 IceRuby::arrayToStringSeq(VALUE val, vector<string>& seq)
355 {
356     volatile VALUE arr = callRuby(rb_check_array_type, val);
357     if(NIL_P(arr))
358     {
359         return false;
360     }
361     for(long i = 0; i < RARRAY_LEN(arr); ++i)
362     {
363         string s = getString(RARRAY_AREF(arr, i));
364         seq.push_back(getString(RARRAY_AREF(arr, i)));
365     }
366     return true;
367 }
368 
369 VALUE
stringSeqToArray(const vector<string> & seq)370 IceRuby::stringSeqToArray(const vector<string>& seq)
371 {
372     volatile VALUE result = createArray(seq.size());
373     long i = 0;
374     if(seq.size() > 0)
375     {
376         for(vector<string>::const_iterator p = seq.begin(); p != seq.end(); ++p, ++i)
377         {
378             RARRAY_ASET(result, i, createString(*p));
379         }
380     }
381     return result;
382 }
383 
384 VALUE
createNumSeq(const vector<Ice::Byte> & v)385 IceRuby::createNumSeq(const vector<Ice::Byte>& v)
386 {
387     volatile VALUE result = createArray(v.size());
388     long i = 0;
389     if(v.size() > 0)
390     {
391         for(vector<Ice::Byte>::const_iterator p = v.begin(); p != v.end(); ++p, ++i)
392         {
393             RARRAY_ASET(result, i, INT2FIX(*p));
394         }
395     }
396     return result;
397 }
398 
399 namespace
400 {
401 
402 struct HashToContextIterator : public IceRuby::HashIterator
403 {
HashToContextIterator__anon243556910311::HashToContextIterator404     HashToContextIterator(Ice::Context& c) : ctx(c)
405     {
406     }
407 
element__anon243556910311::HashToContextIterator408     virtual void element(VALUE key, VALUE value)
409     {
410         string kstr = IceRuby::getString(key);
411         string vstr = IceRuby::getString(value);
412         ctx[kstr] = vstr;
413     }
414 
415     Ice::Context& ctx;
416 };
417 
418 }
419 
420 bool
hashToContext(VALUE val,Ice::Context & ctx)421 IceRuby::hashToContext(VALUE val, Ice::Context& ctx)
422 {
423     if(TYPE(val) != T_HASH)
424     {
425         val = callRuby(rb_convert_type, val, T_HASH, "Hash", "to_hash");
426         if(NIL_P(val))
427         {
428             return false;
429         }
430     }
431     HashToContextIterator iter(ctx);
432     hashIterate(val, iter);
433     return true;
434 }
435 
436 VALUE
contextToHash(const Ice::Context & ctx)437 IceRuby::contextToHash(const Ice::Context& ctx)
438 {
439     volatile VALUE result = callRuby(rb_hash_new);
440     for(Ice::Context::const_iterator p = ctx.begin(); p != ctx.end(); ++p)
441     {
442         volatile VALUE key = createString(p->first);
443         volatile VALUE value = createString(p->second);
444         callRuby(rb_hash_aset, result, key, value);
445     }
446     return result;
447 }
448 
449 extern "C"
450 VALUE
IceRuby_Util_hash_foreach_callback(VALUE val,VALUE arg)451 IceRuby_Util_hash_foreach_callback(VALUE val, VALUE arg)
452 {
453     VALUE key = rb_ary_entry(val, 0);
454     VALUE value = rb_ary_entry(val, 1);
455 
456     //
457     // We can't allow any C++ exceptions to propagate out of this function.
458     //
459     ICE_RUBY_TRY
460     {
461         IceRuby::HashIterator* iter = reinterpret_cast<IceRuby::HashIterator*>(arg);
462         iter->element(key, value);
463     }
464     ICE_RUBY_CATCH
465     return val;
466 }
467 
468 extern "C"
469 {
470 typedef VALUE (*ICE_RUBY_HASH_FOREACH_CALLBACK)(...);
471 }
472 
473 void
hashIterate(VALUE h,HashIterator & iter)474 IceRuby::hashIterate(VALUE h, HashIterator& iter)
475 {
476     assert(TYPE(h) == T_HASH);
477     callRuby(rb_iterate, rb_each, h,
478              reinterpret_cast<ICE_RUBY_HASH_FOREACH_CALLBACK>(IceRuby_Util_hash_foreach_callback),
479              reinterpret_cast<VALUE>(&iter));
480 }
481 
482 Ice::Identity
getIdentity(VALUE v)483 IceRuby::getIdentity(VALUE v)
484 {
485     volatile VALUE cls = callRuby(rb_path2class, "Ice::Identity");
486     assert(!NIL_P(cls));
487 
488     if(callRuby(rb_obj_is_kind_of, v, cls) == Qfalse)
489     {
490         throw RubyException(rb_eTypeError, "value is not an Ice::Identity");
491     }
492 
493     volatile VALUE name = callRuby(rb_iv_get, v, "@name");
494     volatile VALUE category = callRuby(rb_iv_get, v, "@category");
495 
496     if(!NIL_P(category) && !isString(category))
497     {
498         throw RubyException(rb_eTypeError, "identity category must be a string");
499     }
500 
501     if(NIL_P(name) || !isString(name))
502     {
503         throw RubyException(rb_eTypeError, "identity name must be a string");
504     }
505 
506     Ice::Identity result;
507     result.name = getString(name);
508     if(!NIL_P(category))
509     {
510         result.category = getString(category);
511     }
512     return result;
513 }
514 
515 VALUE
createIdentity(const Ice::Identity & id)516 IceRuby::createIdentity(const Ice::Identity& id)
517 {
518     volatile VALUE cls = callRuby(rb_path2class, "Ice::Identity");
519     assert(!NIL_P(cls));
520 
521     volatile VALUE result = callRuby(rb_class_new_instance, 0, reinterpret_cast<VALUE*>(0), cls);
522     volatile VALUE name = createString(id.name);
523     volatile VALUE category = createString(id.category);
524     callRuby(rb_iv_set, result, "@name", name);
525     callRuby(rb_iv_set, result, "@category", category);
526     return result;
527 }
528 
529 VALUE
createProtocolVersion(const Ice::ProtocolVersion & v)530 IceRuby::createProtocolVersion(const Ice::ProtocolVersion& v)
531 {
532     return createVersion<Ice::ProtocolVersion>(v, Ice_ProtocolVersion);
533 }
534 
535 VALUE
createEncodingVersion(const Ice::EncodingVersion & v)536 IceRuby::createEncodingVersion(const Ice::EncodingVersion& v)
537 {
538     return createVersion<Ice::EncodingVersion>(v, Ice_EncodingVersion);
539 }
540 
541 bool
getEncodingVersion(VALUE p,Ice::EncodingVersion & v)542 IceRuby::getEncodingVersion(VALUE p, Ice::EncodingVersion& v)
543 {
544     volatile VALUE cls = callRuby(rb_path2class, Ice_EncodingVersion);
545     assert(!NIL_P(cls));
546 
547     if(callRuby(rb_obj_is_kind_of, p, cls) == Qfalse)
548     {
549         throw RubyException(rb_eTypeError, "value is not an Ice::EncodingVersion");
550     }
551 
552     if(!getVersion<Ice::EncodingVersion>(p, v))
553     {
554         return false;
555     }
556 
557     return true;
558 }
559 
560 VALUE
callProtected(RubyFunction func,VALUE arg)561 IceRuby::callProtected(RubyFunction func, VALUE arg)
562 {
563     int error = 0;
564     volatile VALUE result = rb_protect(func, arg, &error);
565     if(error)
566     {
567         throw RubyException();
568     }
569     return result;
570 }
571 
572 static void
setExceptionMembers(const Ice::LocalException & ex,VALUE p)573 setExceptionMembers(const Ice::LocalException& ex, VALUE p)
574 {
575     //
576     // Transfer data members from Ice exception to Ruby exception.
577     //
578     try
579     {
580         ex.ice_throw();
581     }
582     catch(const Ice::InitializationException& e)
583     {
584         volatile VALUE v = createString(e.reason);
585         callRuby(rb_iv_set, p, "@reason", v);
586     }
587     catch(const Ice::PluginInitializationException& e)
588     {
589         volatile VALUE v = createString(e.reason);
590         callRuby(rb_iv_set, p, "@reason", v);
591     }
592     catch(const Ice::AlreadyRegisteredException& e)
593     {
594         volatile VALUE v;
595         v = createString(e.kindOfObject);
596         callRuby(rb_iv_set, p, "@kindOfObject", v);
597         v = createString(e.id);
598         callRuby(rb_iv_set, p, "@id", v);
599     }
600     catch(const Ice::NotRegisteredException& e)
601     {
602         volatile VALUE v;
603         v = createString(e.kindOfObject);
604         callRuby(rb_iv_set, p, "@kindOfObject", v);
605         v = createString(e.id);
606         callRuby(rb_iv_set, p, "@id", v);
607     }
608     catch(const Ice::TwowayOnlyException& e)
609     {
610         volatile VALUE v = createString(e.operation);
611         callRuby(rb_iv_set, p, "@operation", v);
612     }
613     catch(const Ice::UnknownException& e)
614     {
615         volatile VALUE v = createString(e.unknown);
616         callRuby(rb_iv_set, p, "@unknown", v);
617     }
618     catch(const Ice::ObjectAdapterDeactivatedException& e)
619     {
620         volatile VALUE v = createString(e.name);
621         callRuby(rb_iv_set, p, "@name", v);
622     }
623     catch(const Ice::ObjectAdapterIdInUseException& e)
624     {
625         volatile VALUE v = createString(e.id);
626         callRuby(rb_iv_set, p, "@id", v);
627     }
628     catch(const Ice::NoEndpointException& e)
629     {
630         volatile VALUE v = createString(e.proxy);
631         callRuby(rb_iv_set, p, "@proxy", v);
632     }
633     catch(const Ice::EndpointParseException& e)
634     {
635         volatile VALUE v = createString(e.str);
636         callRuby(rb_iv_set, p, "@str", v);
637     }
638     catch(const Ice::EndpointSelectionTypeParseException& e)
639     {
640         volatile VALUE v = createString(e.str);
641         callRuby(rb_iv_set, p, "@str", v);
642     }
643     catch(const Ice::VersionParseException& e)
644     {
645         volatile VALUE v = createString(e.str);
646         callRuby(rb_iv_set, p, "@str", v);
647     }
648     catch(const Ice::IdentityParseException& e)
649     {
650         volatile VALUE v = createString(e.str);
651         callRuby(rb_iv_set, p, "@str", v);
652     }
653     catch(const Ice::ProxyParseException& e)
654     {
655         volatile VALUE v = createString(e.str);
656         callRuby(rb_iv_set, p, "@str", v);
657     }
658     catch(const Ice::IllegalIdentityException& e)
659     {
660         volatile VALUE v = IceRuby::createIdentity(e.id);
661         callRuby(rb_iv_set, p, "@id", v);
662     }
663     catch(const Ice::IllegalServantException& e)
664     {
665         volatile VALUE v = createString(e.reason);
666         callRuby(rb_iv_set, p, "@reason", v);
667     }
668     catch(const Ice::RequestFailedException& e)
669     {
670         volatile VALUE v;
671         v = IceRuby::createIdentity(e.id);
672         callRuby(rb_iv_set, p, "@id", v);
673         v = createString(e.facet);
674         callRuby(rb_iv_set, p, "@facet", v);
675         v = createString(e.operation);
676         callRuby(rb_iv_set, p, "@operation", v);
677     }
678     catch(const Ice::FileException& e)
679     {
680         volatile VALUE v = INT2FIX(e.error);
681         callRuby(rb_iv_set, p, "@error", v);
682         v = createString(e.path);
683         callRuby(rb_iv_set, p, "@path", v);
684     }
685     catch(const Ice::SyscallException& e) // This must appear after all subclasses of SyscallException.
686     {
687         volatile VALUE v = INT2FIX(e.error);
688         callRuby(rb_iv_set, p, "@error", v);
689     }
690     catch(const Ice::DNSException& e)
691     {
692         volatile VALUE v;
693         v = INT2FIX(e.error);
694         callRuby(rb_iv_set, p, "@error", v);
695         v = createString(e.host);
696         callRuby(rb_iv_set, p, "@host", v);
697     }
698     catch(const Ice::BadMagicException& e)
699     {
700         volatile VALUE v = createNumSeq(e.badMagic);
701         callRuby(rb_iv_set, p, "@badMagic", v);
702     }
703     catch(const Ice::UnsupportedProtocolException& e)
704     {
705         VALUE m;
706         m = createProtocolVersion(e.bad);
707         callRuby(rb_iv_set, p, "@bad", m);
708         m = createProtocolVersion(e.supported);
709         callRuby(rb_iv_set, p, "@supported", m);
710     }
711     catch(const Ice::UnsupportedEncodingException& e)
712     {
713         VALUE m;
714         m = createEncodingVersion(e.bad);
715         callRuby(rb_iv_set, p, "@bad", m);
716         m = createEncodingVersion(e.supported);
717         callRuby(rb_iv_set, p, "@supported", m);
718     }
719     catch(const Ice::NoValueFactoryException& e)
720     {
721         volatile VALUE v;
722         v = createString(e.reason);
723         callRuby(rb_iv_set, p, "@reason", v);
724         v = createString(e.type);
725         callRuby(rb_iv_set, p, "@type", v);
726     }
727     catch(const Ice::UnexpectedObjectException& e)
728     {
729         volatile VALUE v;
730         v = createString(e.reason);
731         callRuby(rb_iv_set, p, "@reason", v);
732         v = createString(e.type);
733         callRuby(rb_iv_set, p, "@type", v);
734         v = createString(e.expectedType);
735         callRuby(rb_iv_set, p, "@expectedType", v);
736     }
737     catch(const Ice::ProtocolException& e) // This must appear after all subclasses of ProtocolException.
738     {
739         volatile VALUE v = createString(e.reason);
740         callRuby(rb_iv_set, p, "@reason", v);
741     }
742     catch(const Ice::ConnectionManuallyClosedException& e)
743     {
744         callRuby(rb_iv_set, p, "@graceful", e.graceful ? Qtrue : Qfalse);
745     }
746     catch(const Ice::FeatureNotSupportedException& e)
747     {
748         volatile VALUE v = createString(e.unsupportedFeature);
749         callRuby(rb_iv_set, p, "@unsupportedFeature", v);
750     }
751     catch(const Ice::SecurityException& e)
752     {
753         volatile VALUE v = createString(e.reason);
754         callRuby(rb_iv_set, p, "@reason", v);
755     }
756     catch(const Ice::LocalException&)
757     {
758         //
759         // Nothing to do.
760         //
761     }
762 }
763 
764 VALUE
createArrayHelper(long sz)765 IceRuby::createArrayHelper(long sz)
766 {
767     volatile VALUE arr = callRuby(rb_ary_new2, sz);
768     if(sz > 0)
769     {
770         callRubyVoid(rb_ary_store, arr, sz - 1, Qnil);
771     }
772     return arr;
773 }
774 
775 VALUE
convertLocalException(const Ice::LocalException & ex)776 IceRuby::convertLocalException(const Ice::LocalException& ex)
777 {
778     //
779     // We cannot throw a C++ exception or raise a Ruby exception. If an error
780     // occurs while we are converting the exception, we do our best to return
781     // an appropriate Ruby exception.
782     //
783     try
784     {
785         string name = ex.ice_id().substr(2);
786         volatile VALUE cls = callRuby(rb_path2class, name.c_str());
787         if(NIL_P(cls))
788         {
789             throw RubyException(rb_eRuntimeError, "exception class `%s' not found", name.c_str());
790         }
791         volatile VALUE result = callRuby(rb_class_new_instance, 0, reinterpret_cast<VALUE*>(0), cls);
792         setExceptionMembers(ex, result);
793         return result;
794     }
795     catch(const RubyException& e)
796     {
797         return e.ex;
798     }
799     catch(...)
800     {
801         string msg = "failure occurred while converting exception " + ex.ice_id();
802         return rb_exc_new2(rb_eRuntimeError, msg.c_str());
803     }
804 }
805