diff options
author | Matthew Newhook <matthew@zeroc.com> | 2015-03-21 15:35:40 -0230 |
---|---|---|
committer | Matthew Newhook <matthew@zeroc.com> | 2015-03-21 15:35:40 -0230 |
commit | 630a37d2fe66f24518299e705f958b571803c522 (patch) | |
tree | 969723791bdc4d73bb099c19d45554d0ca241ad9 /ruby/src/IceRuby/Util.cpp | |
parent | Fix some README.md markdown formatting (diff) | |
download | ice-630a37d2fe66f24518299e705f958b571803c522.tar.bz2 ice-630a37d2fe66f24518299e705f958b571803c522.tar.xz ice-630a37d2fe66f24518299e705f958b571803c522.zip |
py -> python
rb -> ruby
objc -> objective-c
cs -> csharp
Diffstat (limited to 'ruby/src/IceRuby/Util.cpp')
-rw-r--r-- | ruby/src/IceRuby/Util.cpp | 783 |
1 files changed, 783 insertions, 0 deletions
diff --git a/ruby/src/IceRuby/Util.cpp b/ruby/src/IceRuby/Util.cpp new file mode 100644 index 00000000000..bac8daddec1 --- /dev/null +++ b/ruby/src/IceRuby/Util.cpp @@ -0,0 +1,783 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2015 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +#include <Util.h> +#include <Ice/LocalException.h> +#include <Ice/Protocol.h> +#include <stdarg.h> + +#ifdef HAVE_RUBY_ENCODING_H +# include <ruby/encoding.h> +#endif + +using namespace std; +using namespace IceRuby; + +namespace +{ + +#ifndef NDEBUG +bool +checkIsInstance(VALUE p, const char* type) +{ + volatile VALUE rbType = callRuby(rb_path2class, type); + assert(!NIL_P(rbType)); + return callRuby(rb_obj_is_instance_of, p, rbType) == Qtrue; +} +#endif + +template<typename T> +bool +setVersion(VALUE p, const T& version, const char* type) +{ + assert(checkIsInstance(p, type)); + + volatile VALUE major = callRuby(rb_int2inum, version.major); + volatile VALUE minor = callRuby(rb_int2inum, version.minor); + rb_ivar_set(p, rb_intern("@major"), major); + rb_ivar_set(p, rb_intern("@minor"), minor); + + return true; +} + +template<typename T> +bool +getVersion(VALUE p, T& v, const char* type) +{ + assert(checkIsInstance(p, type)); + volatile VALUE major = callRuby(rb_ivar_get, p, rb_intern("@major")); + volatile VALUE minor = callRuby(rb_ivar_get, p, rb_intern("@minor")); + + long m; + + m = getInteger(major); + if(m < 0 || m > 255) + { + throw RubyException(rb_eTypeError, "version major must be a value between 0 and 255"); + return false; + } + v.major = m; + + m = getInteger(minor); + if(m < 0 || m > 255) + { + throw RubyException(rb_eTypeError, "version minor must be a value between 0 and 255"); + return false; + } + v.minor = m; + + return true; +} + +template<typename T> +VALUE +createVersion(const T& version, const char* type) +{ + volatile VALUE rbType = callRuby(rb_path2class, type); + assert(!NIL_P(rbType)); + + volatile VALUE obj = callRuby(rb_class_new_instance, 0, static_cast<VALUE*>(0), rbType); + + if(!setVersion<T>(obj, version, type)) + { + return Qnil; + } + + return obj; +} + +template<typename T> +VALUE +versionToString(VALUE p, const char* type) +{ + volatile VALUE rbType = callRuby(rb_path2class, type); + assert(!NIL_P(rbType)); + if(callRuby(rb_obj_is_instance_of, p, rbType) != Qtrue) + { + throw RubyException(rb_eTypeError, "argument is not an instance of %s", type); + } + + T v; + if(!getVersion<T>(p, v, type)) + { + return Qnil; + } + + ICE_RUBY_TRY + { + string s = IceInternal::versionToString<T>(v); + return createString(s); + } + ICE_RUBY_CATCH + return Qnil; +} + +template<typename T> +VALUE +stringToVersion(VALUE p, const char* type) +{ + string str = getString(p); + + ICE_RUBY_TRY + { + T v = IceInternal::stringToVersion<T>(str); + return createVersion<T>(v, type); + } + ICE_RUBY_CATCH + return Qnil; +} + +char Ice_ProtocolVersion[] = "Ice::ProtocolVersion"; +char Ice_EncodingVersion[] = "Ice::EncodingVersion"; + +} + +extern "C" +VALUE +IceRuby_stringVersion(int /*argc*/, VALUE* /*argv*/, VALUE /*self*/) +{ + ICE_RUBY_TRY + { + string s = ICE_STRING_VERSION; + return createString(s); + } + ICE_RUBY_CATCH + return Qnil; +} + +extern "C" +VALUE +IceRuby_intVersion(int /*argc*/, VALUE* /*argv*/, VALUE /*self*/) +{ + ICE_RUBY_TRY + { + return INT2FIX(ICE_INT_VERSION); + } + ICE_RUBY_CATCH + return Qnil; +} + +extern "C" +VALUE +IceRuby_currentProtocol(int /*argc*/, VALUE* /*argv*/, VALUE /*self*/) +{ + ICE_RUBY_TRY + { + return createProtocolVersion(Ice::currentProtocol); + } + ICE_RUBY_CATCH + return Qnil; +} + +extern "C" +VALUE +IceRuby_currentProtocolEncoding(int /*argc*/, VALUE* /*argv*/, VALUE /*self*/) +{ + ICE_RUBY_TRY + { + return createEncodingVersion(Ice::currentProtocolEncoding); + } + ICE_RUBY_CATCH + return Qnil; +} + +extern "C" +VALUE +IceRuby_currentEncoding(int /*argc*/, VALUE* /*argv*/, VALUE /*self*/) +{ + ICE_RUBY_TRY + { + return createEncodingVersion(Ice::currentEncoding); + } + ICE_RUBY_CATCH + return Qnil; +} + +extern "C" +VALUE +IceRuby_protocolVersionToString(VALUE /*self*/, VALUE v) +{ + return versionToString<Ice::ProtocolVersion>(v, Ice_ProtocolVersion); +} + +extern "C" +VALUE +IceRuby_stringToProtocolVersion(VALUE /*self*/, VALUE v) +{ + return stringToVersion<Ice::ProtocolVersion>(v, Ice_ProtocolVersion); +} + +extern "C" +VALUE +IceRuby_encodingVersionToString(VALUE /*self*/, VALUE v) +{ + return versionToString<Ice::EncodingVersion>(v, Ice_EncodingVersion); +} + +extern "C" +VALUE +IceRuby_stringToEncodingVersion(VALUE /*self*/, VALUE v) +{ + return stringToVersion<Ice::EncodingVersion>(v, Ice_EncodingVersion); +} + +void +IceRuby::initUtil(VALUE iceModule) +{ + rb_define_module_function(iceModule, "stringVersion", CAST_METHOD(IceRuby_stringVersion), -1); + rb_define_module_function(iceModule, "intVersion", CAST_METHOD(IceRuby_intVersion), -1); + rb_define_module_function(iceModule, "currentProtocol", CAST_METHOD(IceRuby_currentProtocol), -1); + rb_define_module_function(iceModule, "currentProtocolEncoding", CAST_METHOD(IceRuby_currentProtocolEncoding), -1); + rb_define_module_function(iceModule, "currentEncoding", CAST_METHOD(IceRuby_currentEncoding), -1); + rb_define_module_function(iceModule, "protocolVersionToString", CAST_METHOD(IceRuby_protocolVersionToString), 1); + rb_define_module_function(iceModule, "stringToProtocolVersion", CAST_METHOD(IceRuby_stringToProtocolVersion), 1); + rb_define_module_function(iceModule, "encodingVersionToString", CAST_METHOD(IceRuby_encodingVersionToString), 1); + rb_define_module_function(iceModule, "stringToEncodingVersion", CAST_METHOD(IceRuby_stringToEncodingVersion), 1); +} + +IceRuby::RubyException::RubyException() +{ + ex = rb_gv_get("$!"); +} + +IceRuby::RubyException::RubyException(VALUE exv) : + ex(exv) +{ +} + +IceRuby::RubyException::RubyException(VALUE exClass, const char* fmt, ...) +{ + va_list args; + char buf[BUFSIZ]; + + va_start(args, fmt); + vsnprintf(buf, BUFSIZ, fmt, args); + buf[BUFSIZ - 1] = '\0'; + va_end(args); + + ex = callRuby(rb_exc_new2, exClass, buf); +} + +ostream& +IceRuby::RubyException::operator<<(ostream& ostr) const +{ + volatile VALUE cls = rb_class_path(CLASS_OF(ex)); + volatile VALUE msg = rb_obj_as_string(ex); + ostr << RSTRING_PTR(cls) << ": " << RSTRING_PTR(msg); + return ostr; +} + +bool +IceRuby::isString(VALUE val) +{ + return TYPE(val) == T_STRING || callRuby(rb_respond_to, val, rb_intern("to_str")) != 0; +} + +bool +IceRuby::isArray(VALUE val) +{ + return TYPE(val) == T_ARRAY || callRuby(rb_respond_to, val, rb_intern("to_arr")) != 0; +} + +bool +IceRuby::isHash(VALUE val) +{ + return TYPE(val) == T_HASH || callRuby(rb_respond_to, val, rb_intern("to_hash")) != 0; +} + +string +IceRuby::getString(VALUE val) +{ + volatile VALUE result = callRuby(rb_string_value, &val); + return string(RSTRING_PTR(result), RSTRING_LEN(result)); +} + +VALUE +IceRuby::createString(const string& str) +{ +#ifdef HAVE_RUBY_ENCODING_H + return callRuby(rb_enc_str_new, str.c_str(), static_cast<long>(str.size()), rb_utf8_encoding()); +#else + return callRuby(rb_str_new, str.c_str(), static_cast<long>(str.size())); +#endif +} + +namespace +{ + +template <typename T> +struct RubyCallArgs +{ + volatile VALUE val; + T ret; +}; + +// +// Wrapper function to call rb_num2long with rb_protect +// +VALUE +rb_num2long_wrapper(VALUE val) +{ + RubyCallArgs<long>* data = (RubyCallArgs<long>*)val; + data->ret = rb_num2long(data->val); + return val; +} + +// +// Wrapper function to call rb_num2ll with rb_protect +// +VALUE +rb_num2ll_wrapper(VALUE val) +{ + RubyCallArgs<Ice::Long>* data = (RubyCallArgs<Ice::Long>*)val; + data->ret = rb_num2ll(data->val); + return val; +} + +} + +long +IceRuby::getInteger(VALUE val) +{ + RubyCallArgs<long> arg= {val, -1}; + int error = 0; + rb_protect(rb_num2long_wrapper, (VALUE)&arg, &error); + if(error) + { + throw RubyException(rb_eTypeError, "unable to convert value to an int"); + } + return arg.ret; +} + +Ice::Long +IceRuby::getLong(VALUE val) +{ + RubyCallArgs<Ice::Long> arg= {val, -1}; + int error = 0; + rb_protect(rb_num2ll_wrapper, (VALUE)&arg, &error); + if(error) + { + throw RubyException(rb_eTypeError, "unable to convert value to a long"); + } + return arg.ret; +} + +bool +IceRuby::arrayToStringSeq(VALUE val, vector<string>& seq) +{ + volatile VALUE arr = callRuby(rb_check_array_type, val); + if(NIL_P(arr)) + { + return false; + } + for(long i = 0; i < RARRAY_LEN(arr); ++i) + { + string s = getString(RARRAY_PTR(arr)[i]); + seq.push_back(getString(RARRAY_PTR(arr)[i])); + } + return true; +} + +VALUE +IceRuby::stringSeqToArray(const vector<string>& seq) +{ + volatile VALUE result = createArray(seq.size()); + long i = 0; + if(seq.size() > 0) + { + for(vector<string>::const_iterator p = seq.begin(); p != seq.end(); ++p, ++i) + { + RARRAY_PTR(result)[i] = createString(*p); + } + } + return result; +} + +namespace +{ + +struct HashToContextIterator : public IceRuby::HashIterator +{ + HashToContextIterator(Ice::Context& c) : ctx(c) + { + } + + virtual void element(VALUE key, VALUE value) + { + string kstr = IceRuby::getString(key); + string vstr = IceRuby::getString(value); + ctx[kstr] = vstr; + } + + Ice::Context& ctx; +}; + +} + +bool +IceRuby::hashToContext(VALUE val, Ice::Context& ctx) +{ + if(TYPE(val) != T_HASH) + { + val = callRuby(rb_convert_type, val, T_HASH, "Hash", "to_hash"); + if(NIL_P(val)) + { + return false; + } + } + HashToContextIterator iter(ctx); + hashIterate(val, iter); + return true; +} + +VALUE +IceRuby::contextToHash(const Ice::Context& ctx) +{ + volatile VALUE result = callRuby(rb_hash_new); + for(Ice::Context::const_iterator p = ctx.begin(); p != ctx.end(); ++p) + { + volatile VALUE key = callRuby(rb_str_new, p->first.c_str(), static_cast<long>(p->first.size())); + volatile VALUE value = callRuby(rb_str_new, p->second.c_str(), static_cast<long>(p->second.size())); + callRuby(rb_hash_aset, result, key, value); + } + return result; +} + +extern "C" +VALUE +IceRuby_Util_hash_foreach_callback(VALUE val, VALUE arg) +{ + VALUE key = rb_ary_entry(val, 0); + VALUE value = rb_ary_entry(val, 1); + + // + // We can't allow any C++ exceptions to propagate out of this function. + // + ICE_RUBY_TRY + { + IceRuby::HashIterator* iter = reinterpret_cast<IceRuby::HashIterator*>(arg); + iter->element(key, value); + } + ICE_RUBY_CATCH + return val; +} + +extern "C" +{ +typedef VALUE (*ICE_RUBY_HASH_FOREACH_CALLBACK)(...); +} + +void +IceRuby::hashIterate(VALUE h, HashIterator& iter) +{ + assert(TYPE(h) == T_HASH); + callRuby(rb_iterate, rb_each, h, + reinterpret_cast<ICE_RUBY_HASH_FOREACH_CALLBACK>(IceRuby_Util_hash_foreach_callback), + reinterpret_cast<VALUE>(&iter)); +} + +Ice::Identity +IceRuby::getIdentity(VALUE v) +{ + volatile VALUE cls = callRuby(rb_path2class, "Ice::Identity"); + assert(!NIL_P(cls)); + + if(callRuby(rb_obj_is_kind_of, v, cls) == Qfalse) + { + throw RubyException(rb_eTypeError, "value is not an Ice::Identity"); + } + + volatile VALUE name = callRuby(rb_iv_get, v, "@name"); + volatile VALUE category = callRuby(rb_iv_get, v, "@category"); + + if(!NIL_P(category) && !isString(category)) + { + throw RubyException(rb_eTypeError, "identity category must be a string"); + } + + if(NIL_P(name) || !isString(name)) + { + throw RubyException(rb_eTypeError, "identity name must be a string"); + } + + Ice::Identity result; + result.name = getString(name); + if(!NIL_P(category)) + { + result.category = getString(category); + } + return result; +} + +VALUE +IceRuby::createIdentity(const Ice::Identity& id) +{ + volatile VALUE cls = callRuby(rb_path2class, "Ice::Identity"); + assert(!NIL_P(cls)); + + volatile VALUE result = callRuby(rb_class_new_instance, 0, reinterpret_cast<VALUE*>(0), cls); + volatile VALUE name = callRuby(rb_str_new, id.name.c_str(), static_cast<long>(id.name.size())); + volatile VALUE category = callRuby(rb_str_new, id.category.c_str(), static_cast<long>(id.category.size())); + callRuby(rb_iv_set, result, "@name", name); + callRuby(rb_iv_set, result, "@category", category); + return result; +} + +VALUE +IceRuby::createProtocolVersion(const Ice::ProtocolVersion& v) +{ + return createVersion<Ice::ProtocolVersion>(v, Ice_ProtocolVersion); +} + +VALUE +IceRuby::createEncodingVersion(const Ice::EncodingVersion& v) +{ + return createVersion<Ice::EncodingVersion>(v, Ice_EncodingVersion); +} + +bool +IceRuby::getEncodingVersion(VALUE p, Ice::EncodingVersion& v) +{ + volatile VALUE cls = callRuby(rb_path2class, Ice_EncodingVersion); + assert(!NIL_P(cls)); + + if(callRuby(rb_obj_is_kind_of, p, cls) == Qfalse) + { + throw RubyException(rb_eTypeError, "value is not an Ice::EncodingVersion"); + } + + if(!getVersion<Ice::EncodingVersion>(p, v, Ice_EncodingVersion)) + { + return false; + } + + return true; +} + +VALUE +IceRuby::callProtected(RubyFunction func, VALUE arg) +{ + int error = 0; + volatile VALUE result = rb_protect(func, arg, &error); + if(error) + { + throw RubyException(); + } + return result; +} + +static void +setExceptionMembers(const Ice::LocalException& ex, VALUE p) +{ + // + // Transfer data members from Ice exception to Ruby exception. + // + try + { + ex.ice_throw(); + } + catch(const Ice::InitializationException& e) + { + volatile VALUE v = createString(e.reason); + callRuby(rb_iv_set, p, "@reason", v); + } + catch(const Ice::PluginInitializationException& e) + { + volatile VALUE v = createString(e.reason); + callRuby(rb_iv_set, p, "@reason", v); + } + catch(const Ice::AlreadyRegisteredException& e) + { + volatile VALUE v; + v = createString(e.kindOfObject); + callRuby(rb_iv_set, p, "@kindOfObject", v); + v = createString(e.id); + callRuby(rb_iv_set, p, "@id", v); + } + catch(const Ice::NotRegisteredException& e) + { + volatile VALUE v; + v = createString(e.kindOfObject); + callRuby(rb_iv_set, p, "@kindOfObject", v); + v = createString(e.id); + callRuby(rb_iv_set, p, "@id", v); + } + catch(const Ice::TwowayOnlyException& e) + { + volatile VALUE v = createString(e.operation); + callRuby(rb_iv_set, p, "@operation", v); + } + catch(const Ice::UnknownException& e) + { + volatile VALUE v = createString(e.unknown); + callRuby(rb_iv_set, p, "@unknown", v); + } + catch(const Ice::ObjectAdapterDeactivatedException& e) + { + volatile VALUE v = createString(e.name); + callRuby(rb_iv_set, p, "@name", v); + } + catch(const Ice::ObjectAdapterIdInUseException& e) + { + volatile VALUE v = createString(e.id); + callRuby(rb_iv_set, p, "@id", v); + } + catch(const Ice::NoEndpointException& e) + { + volatile VALUE v = createString(e.proxy); + callRuby(rb_iv_set, p, "@proxy", v); + } + catch(const Ice::EndpointParseException& e) + { + volatile VALUE v = createString(e.str); + callRuby(rb_iv_set, p, "@str", v); + } + catch(const Ice::IdentityParseException& e) + { + volatile VALUE v = createString(e.str); + callRuby(rb_iv_set, p, "@str", v); + } + catch(const Ice::ProxyParseException& e) + { + volatile VALUE v = createString(e.str); + callRuby(rb_iv_set, p, "@str", v); + } + catch(const Ice::IllegalIdentityException& e) + { + volatile VALUE v = IceRuby::createIdentity(e.id); + callRuby(rb_iv_set, p, "@id", v); + } + catch(const Ice::RequestFailedException& e) + { + volatile VALUE v; + v = IceRuby::createIdentity(e.id); + callRuby(rb_iv_set, p, "@id", v); + v = createString(e.facet); + callRuby(rb_iv_set, p, "@facet", v); + v = createString(e.operation); + callRuby(rb_iv_set, p, "@operation", v); + } + catch(const Ice::FileException& e) + { + volatile VALUE v = INT2FIX(e.error); + callRuby(rb_iv_set, p, "@error", v); + v = createString(e.path); + callRuby(rb_iv_set, p, "@path", v); + } + catch(const Ice::SyscallException& e) // This must appear after all subclasses of SyscallException. + { + volatile VALUE v = INT2FIX(e.error); + callRuby(rb_iv_set, p, "@error", v); + } + catch(const Ice::DNSException& e) + { + volatile VALUE v; + v = INT2FIX(e.error); + callRuby(rb_iv_set, p, "@error", v); + v = createString(e.host); + callRuby(rb_iv_set, p, "@host", v); + } + catch(const Ice::UnsupportedProtocolException& e) + { + VALUE m; + m = createProtocolVersion(e.bad); + callRuby(rb_iv_set, p, "@bad", m); + m = createProtocolVersion(e.supported); + callRuby(rb_iv_set, p, "@supported", m); + } + catch(const Ice::UnsupportedEncodingException& e) + { + VALUE m; + m = createEncodingVersion(e.bad); + callRuby(rb_iv_set, p, "@bad", m); + m = createEncodingVersion(e.supported); + callRuby(rb_iv_set, p, "@supported", m); + } + catch(const Ice::NoObjectFactoryException& e) + { + volatile VALUE v; + v = createString(e.reason); + callRuby(rb_iv_set, p, "@reason", v); + v = createString(e.type); + callRuby(rb_iv_set, p, "@type", v); + } + catch(const Ice::UnexpectedObjectException& e) + { + volatile VALUE v; + v = createString(e.reason); + callRuby(rb_iv_set, p, "@reason", v); + v = createString(e.type); + callRuby(rb_iv_set, p, "@type", v); + v = createString(e.expectedType); + callRuby(rb_iv_set, p, "@expectedType", v); + } + catch(const Ice::ProtocolException& e) // This must appear after all subclasses of ProtocolException. + { + volatile VALUE v = createString(e.reason); + callRuby(rb_iv_set, p, "@reason", v); + } + catch(const Ice::FeatureNotSupportedException& e) + { + volatile VALUE v = createString(e.unsupportedFeature); + callRuby(rb_iv_set, p, "@unsupportedFeature", v); + } + catch(const Ice::SecurityException& e) + { + volatile VALUE v = createString(e.reason); + callRuby(rb_iv_set, p, "@reason", v); + } + catch(const Ice::LocalException&) + { + // + // Nothing to do. + // + } +} + +VALUE +IceRuby::createArrayHelper(long sz) +{ + volatile VALUE arr = callRuby(rb_ary_new2, sz); + if(sz > 0) + { + callRubyVoid(rb_ary_store, arr, sz - 1, Qnil); + } + return arr; +} + +VALUE +IceRuby::convertLocalException(const Ice::LocalException& ex) +{ + // + // We cannot throw a C++ exception or raise a Ruby exception. If an error + // occurs while we are converting the exception, we do our best to return + // an appropriate Ruby exception. + // + try + { + string name = ex.ice_name(); + volatile VALUE cls = callRuby(rb_path2class, name.c_str()); + if(NIL_P(cls)) + { + throw RubyException(rb_eRuntimeError, "exception class `%s' not found", name.c_str()); + } + volatile VALUE result = callRuby(rb_class_new_instance, 0, reinterpret_cast<VALUE*>(0), cls); + setExceptionMembers(ex, result); + return result; + } + catch(const RubyException& e) + { + return e.ex; + } + catch(...) + { + string msg = "failure occurred while converting exception " + ex.ice_name(); + return rb_exc_new2(rb_eRuntimeError, msg.c_str()); + } +} |