summaryrefslogtreecommitdiff
path: root/ruby/src/IceRuby/Operation.cpp
diff options
context:
space:
mode:
authorMatthew Newhook <matthew@zeroc.com>2015-03-21 15:35:40 -0230
committerMatthew Newhook <matthew@zeroc.com>2015-03-21 15:35:40 -0230
commit630a37d2fe66f24518299e705f958b571803c522 (patch)
tree969723791bdc4d73bb099c19d45554d0ca241ad9 /ruby/src/IceRuby/Operation.cpp
parentFix some README.md markdown formatting (diff)
downloadice-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/Operation.cpp')
-rw-r--r--ruby/src/IceRuby/Operation.cpp676
1 files changed, 676 insertions, 0 deletions
diff --git a/ruby/src/IceRuby/Operation.cpp b/ruby/src/IceRuby/Operation.cpp
new file mode 100644
index 00000000000..cbfa9581070
--- /dev/null
+++ b/ruby/src/IceRuby/Operation.cpp
@@ -0,0 +1,676 @@
+// **********************************************************************
+//
+// 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 <Operation.h>
+#include <Proxy.h>
+#include <Types.h>
+#include <Util.h>
+#include <Ice/Communicator.h>
+#include <Ice/Initialize.h>
+#include <Ice/LocalException.h>
+#include <Ice/Logger.h>
+#include <Ice/Properties.h>
+#include <Ice/Proxy.h>
+#include <Slice/RubyUtil.h>
+
+using namespace std;
+using namespace IceRuby;
+using namespace Slice::Ruby;
+
+static VALUE _operationClass;
+
+namespace IceRuby
+{
+
+class ParamInfo : public UnmarshalCallback
+{
+public:
+
+ virtual void unmarshaled(VALUE, VALUE, void*);
+
+ TypeInfoPtr type;
+ bool optional;
+ int tag;
+ int pos;
+};
+typedef IceUtil::Handle<ParamInfo> ParamInfoPtr;
+typedef list<ParamInfoPtr> ParamInfoList;
+
+class OperationI : public Operation
+{
+public:
+
+ OperationI(VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE, VALUE);
+
+ virtual VALUE invoke(const Ice::ObjectPrx&, VALUE, VALUE);
+ virtual void deprecate(const string&);
+
+private:
+
+ string _name;
+ Ice::OperationMode _mode;
+ Ice::OperationMode _sendMode;
+ bool _amd;
+ Ice::FormatType _format;
+ ParamInfoList _inParams;
+ ParamInfoList _optionalInParams;
+ ParamInfoList _outParams;
+ ParamInfoList _optionalOutParams;
+ ParamInfoPtr _returnType;
+ ExceptionInfoList _exceptions;
+ string _dispatchName;
+ bool _sendsClasses;
+ bool _returnsClasses;
+ string _deprecateMessage;
+
+ void convertParams(VALUE, ParamInfoList&, int, bool&);
+ ParamInfoPtr convertParam(VALUE, int);
+ void prepareRequest(const Ice::ObjectPrx&, VALUE, Ice::OutputStreamPtr&, pair<const Ice::Byte*, const Ice::Byte*>&);
+ VALUE unmarshalResults(const vector<Ice::Byte>&, const Ice::CommunicatorPtr&);
+ VALUE unmarshalException(const vector<Ice::Byte>&, const Ice::CommunicatorPtr&);
+ bool validateException(VALUE) const;
+ void checkTwowayOnly(const Ice::ObjectPrx&) const;
+};
+typedef IceUtil::Handle<OperationI> OperationIPtr;
+
+class UserExceptionReaderFactoryI : public Ice::UserExceptionReaderFactory
+{
+public:
+
+ UserExceptionReaderFactoryI(const Ice::CommunicatorPtr& communicator) :
+ _communicator(communicator)
+ {
+ }
+
+ virtual void createAndThrow(const string& id) const
+ {
+ ExceptionInfoPtr info = lookupExceptionInfo(id);
+ if(info)
+ {
+ throw ExceptionReader(_communicator, info);
+ }
+ }
+
+private:
+
+ const Ice::CommunicatorPtr _communicator;
+};
+
+}
+
+extern "C"
+void
+IceRuby_Operation_free(OperationPtr* p)
+{
+ delete p;
+}
+
+extern "C"
+VALUE
+IceRuby_defineOperation(VALUE /*self*/, VALUE name, VALUE mode, VALUE sendMode, VALUE amd, VALUE format, VALUE inParams,
+ VALUE outParams, VALUE returnType, VALUE exceptions)
+{
+ ICE_RUBY_TRY
+ {
+ OperationIPtr op = new OperationI(name, mode, sendMode, amd, format, inParams, outParams, returnType,
+ exceptions);
+ return Data_Wrap_Struct(_operationClass, 0, IceRuby_Operation_free, new OperationPtr(op));
+ }
+ ICE_RUBY_CATCH
+ return Qnil;
+}
+
+extern "C"
+VALUE
+IceRuby_Operation_invoke(VALUE self, VALUE proxy, VALUE opArgs, VALUE ctx)
+{
+ ICE_RUBY_TRY
+ {
+ assert(TYPE(opArgs) == T_ARRAY);
+
+ OperationPtr op = getOperation(self);
+ assert(op);
+ return op->invoke(getProxy(proxy), opArgs, ctx);
+ }
+ ICE_RUBY_CATCH
+ return Qnil;
+}
+
+extern "C"
+VALUE
+IceRuby_Operation_deprecate(VALUE self, VALUE msg)
+{
+ ICE_RUBY_TRY
+ {
+ OperationPtr op = getOperation(self);
+ assert(op);
+ op->deprecate(getString(msg));
+ }
+ ICE_RUBY_CATCH
+ return Qnil;
+}
+
+//
+// Operation implementation.
+//
+IceRuby::Operation::~Operation()
+{
+}
+
+//
+// ParamInfo implementation.
+//
+void
+IceRuby::ParamInfo::unmarshaled(VALUE val, VALUE target, void* closure)
+{
+ assert(TYPE(target) == T_ARRAY);
+#ifdef ICE_64
+ long i = static_cast<long>(reinterpret_cast<long long>(closure));
+#else
+ long i = reinterpret_cast<long>(closure);
+#endif
+ RARRAY_PTR(target)[i] = val;
+}
+
+//
+// OperationI implementation.
+//
+IceRuby::OperationI::OperationI(VALUE name, VALUE mode, VALUE sendMode, VALUE amd, VALUE format, VALUE inParams,
+ VALUE outParams, VALUE returnType, VALUE exceptions)
+{
+ _name = getString(name);
+ _amd = amd == Qtrue;
+ if(_amd)
+ {
+ _dispatchName = fixIdent(_name, IdentNormal) + "_async";
+ }
+ else
+ {
+ _dispatchName = fixIdent(_name, IdentNormal);
+ }
+
+ //
+ // mode
+ //
+ volatile VALUE modeValue = callRuby(rb_funcall, mode, rb_intern("to_i"), 0);
+ assert(TYPE(modeValue) == T_FIXNUM);
+ _mode = static_cast<Ice::OperationMode>(FIX2LONG(modeValue));
+
+ //
+ // sendMode
+ //
+ volatile VALUE sendModeValue = callRuby(rb_funcall, sendMode, rb_intern("to_i"), 0);
+ assert(TYPE(sendModeValue) == T_FIXNUM);
+ _sendMode = static_cast<Ice::OperationMode>(FIX2LONG(sendModeValue));
+
+ //
+ // format
+ //
+ if(format == Qnil)
+ {
+ _format = Ice::DefaultFormat;
+ }
+ else
+ {
+ volatile VALUE formatValue = callRuby(rb_funcall, format, rb_intern("to_i"), 0);
+ assert(TYPE(formatValue) == T_FIXNUM);
+ _format = static_cast<Ice::FormatType>(FIX2LONG(formatValue));
+ }
+
+ //
+ // returnType
+ //
+ _returnsClasses = false;
+ if(!NIL_P(returnType))
+ {
+ _returnType = convertParam(returnType, 0);
+ if(!_returnType->optional)
+ {
+ _returnsClasses = _returnType->type->usesClasses();
+ }
+ }
+
+ //
+ // inParams
+ //
+ _sendsClasses = false;
+ convertParams(inParams, _inParams, 0, _sendsClasses);
+
+ //
+ // outParams
+ //
+ convertParams(outParams, _outParams, NIL_P(returnType) ? 0 : 1, _returnsClasses);
+
+ class SortFn
+ {
+ public:
+ static bool compare(const ParamInfoPtr& lhs, const ParamInfoPtr& rhs)
+ {
+ return lhs->tag < rhs->tag;
+ }
+
+ static bool isRequired(const ParamInfoPtr& i)
+ {
+ return !i->optional;
+ }
+ };
+
+ //
+ // The inParams list represents the parameters in the order of declaration.
+ // We also need a sorted list of optional parameters.
+ //
+ ParamInfoList l = _inParams;
+ copy(l.begin(), remove_if(l.begin(), l.end(), SortFn::isRequired), back_inserter(_optionalInParams));
+ _optionalInParams.sort(SortFn::compare);
+
+ //
+ // The outParams list represents the parameters in the order of declaration.
+ // We also need a sorted list of optional parameters. If the return value is
+ // optional, we must include it in this list.
+ //
+ l = _outParams;
+ copy(l.begin(), remove_if(l.begin(), l.end(), SortFn::isRequired), back_inserter(_optionalOutParams));
+ if(_returnType && _returnType->optional)
+ {
+ _optionalOutParams.push_back(_returnType);
+ }
+ _optionalOutParams.sort(SortFn::compare);
+
+ //
+ // exceptions
+ //
+ for(long i = 0; i < RARRAY_LEN(exceptions); ++i)
+ {
+ _exceptions.push_back(getException(RARRAY_PTR(exceptions)[i]));
+ }
+}
+
+VALUE
+IceRuby::OperationI::invoke(const Ice::ObjectPrx& proxy, VALUE args, VALUE hctx)
+{
+ Ice::CommunicatorPtr communicator = proxy->ice_getCommunicator();
+
+ //
+ // Marshal the input parameters to a byte sequence.
+ //
+ Ice::OutputStreamPtr os;
+ pair<const Ice::Byte*, const Ice::Byte*> params;
+ prepareRequest(proxy, args, os, params);
+
+ if(!_deprecateMessage.empty())
+ {
+ rb_warning("%s", _deprecateMessage.c_str());
+ _deprecateMessage.clear(); // Only show the warning once.
+ }
+
+ checkTwowayOnly(proxy);
+
+ //
+ // Invoke the operation.
+ //
+ Ice::ByteSeq result;
+ bool status;
+
+ if(!NIL_P(hctx))
+ {
+ Ice::Context ctx;
+ if(!hashToContext(hctx, ctx))
+ {
+ throw RubyException(rb_eArgError, "context argument must be nil or a hash");
+ }
+
+ status = proxy->ice_invoke(_name, _sendMode, params, result, ctx);
+ }
+ else
+ {
+ status = proxy->ice_invoke(_name, _sendMode, params, result);
+ }
+
+ //
+ // Process the reply.
+ //
+ if(proxy->ice_isTwoway())
+ {
+ if(!status)
+ {
+ //
+ // Unmarshal a user exception.
+ //
+ volatile VALUE ex = unmarshalException(result, communicator);
+ throw RubyException(ex);
+ }
+ else if(_outParams.size() > 0 || _returnType)
+ {
+ //
+ // Unmarshal the results. If there is more than one value to be returned, then return them
+ // in an array of the form [result, outParam1, ...]. Otherwise just return the value.
+ //
+ volatile VALUE results = unmarshalResults(result, communicator);
+
+ if(RARRAY_LEN(results)> 1)
+ {
+ return results;
+ }
+ else
+ {
+ return RARRAY_PTR(results)[0];
+ }
+ }
+ }
+
+ return Qnil;
+}
+
+void
+IceRuby::OperationI::deprecate(const string& msg)
+{
+ if(!msg.empty())
+ {
+ _deprecateMessage = msg;
+ }
+ else
+ {
+ _deprecateMessage = "operation " + _name + " is deprecated";
+ }
+}
+
+void
+IceRuby::OperationI::convertParams(VALUE v, ParamInfoList& params, int posOffset, bool& usesClasses)
+{
+ assert(TYPE(v) == T_ARRAY);
+
+ for(long i = 0; i < RARRAY_LEN(v); ++i)
+ {
+ ParamInfoPtr param = convertParam(RARRAY_PTR(v)[i], i + posOffset);
+ params.push_back(param);
+ if(!param->optional && !usesClasses)
+ {
+ usesClasses = param->type->usesClasses();
+ }
+ }
+}
+
+ParamInfoPtr
+IceRuby::OperationI::convertParam(VALUE v, int pos)
+{
+ assert(TYPE(v) == T_ARRAY);
+ ParamInfoPtr param = new ParamInfo;
+ param->type = getType(RARRAY_PTR(v)[0]);
+ param->optional = static_cast<bool>(RTEST(RARRAY_PTR(v)[1]));
+ param->tag = static_cast<int>(getInteger(RARRAY_PTR(v)[2]));
+ param->pos = pos;
+ return param;
+}
+
+void
+IceRuby::OperationI::prepareRequest(const Ice::ObjectPrx& proxy, VALUE args, Ice::OutputStreamPtr& os,
+ pair<const Ice::Byte*, const Ice::Byte*>& params)
+{
+ params.first = params.second = static_cast<const Ice::Byte*>(0);
+
+ //
+ // Validate the number of arguments.
+ //
+ long argc = RARRAY_LEN(args);
+ long paramCount = static_cast<long>(_inParams.size());
+ if(argc != paramCount)
+ {
+ string fixedName = fixIdent(_name, IdentNormal);
+ throw RubyException(rb_eArgError, "%s expects %ld in parameters", fixedName.c_str(), paramCount);
+ }
+
+ if(!_inParams.empty())
+ {
+ //
+ // Marshal the in parameters.
+ //
+ os = Ice::createOutputStream(proxy->ice_getCommunicator());
+ os->startEncapsulation(proxy->ice_getEncodingVersion(), _format);
+
+ ObjectMap objectMap;
+ ParamInfoList::iterator p;
+
+ //
+ // Validate the supplied arguments.
+ //
+ for(p = _inParams.begin(); p != _inParams.end(); ++p)
+ {
+ ParamInfoPtr info = *p;
+ volatile VALUE arg = RARRAY_PTR(args)[info->pos];
+ if((!info->optional || arg != Unset) && !info->type->validate(arg))
+ {
+ string opName = fixIdent(_name, IdentNormal);
+ throw RubyException(rb_eTypeError, "invalid value for argument %ld in operation `%s'", info->pos + 1,
+ opName.c_str());
+ }
+ }
+
+ //
+ // Marshal the required parameters.
+ //
+ for(p = _inParams.begin(); p != _inParams.end(); ++p)
+ {
+ ParamInfoPtr info = *p;
+ if(!info->optional)
+ {
+ volatile VALUE arg = RARRAY_PTR(args)[info->pos];
+ info->type->marshal(arg, os, &objectMap, false);
+ }
+ }
+
+ //
+ // Marshal the optional parameters.
+ //
+ for(p = _optionalInParams.begin(); p != _optionalInParams.end(); ++p)
+ {
+ ParamInfoPtr info = *p;
+ volatile VALUE arg = RARRAY_PTR(args)[info->pos];
+ if(arg != Unset && os->writeOptional(info->tag, info->type->optionalFormat()))
+ {
+ info->type->marshal(arg, os, &objectMap, true);
+ }
+ }
+
+ if(_sendsClasses)
+ {
+ os->writePendingObjects();
+ }
+
+ os->endEncapsulation();
+ params = os->finished();
+ }
+}
+
+VALUE
+IceRuby::OperationI::unmarshalResults(const vector<Ice::Byte>& bytes, const Ice::CommunicatorPtr& communicator)
+{
+ int numResults = static_cast<int>(_outParams.size());
+ if(_returnType)
+ {
+ numResults++;
+ }
+ assert(numResults > 0);
+
+ volatile VALUE results = createArray(numResults);
+
+ //
+ // Unmarshal the results. If there is more than one value to be returned, then return them
+ // in a tuple of the form (result, outParam1, ...). Otherwise just return the value.
+ //
+ Ice::InputStreamPtr is = Ice::wrapInputStream(communicator, bytes);
+
+ //
+ // Store a pointer to a local SlicedDataUtil object as the stream's closure.
+ // This is necessary to support object unmarshaling (see ObjectReader).
+ //
+ SlicedDataUtil util;
+ assert(!is->closure());
+ is->closure(&util);
+
+ is->startEncapsulation();
+
+ ParamInfoList::iterator p;
+
+ //
+ // Unmarshal the required out parameters.
+ //
+ for(p = _outParams.begin(); p != _outParams.end(); ++p)
+ {
+ ParamInfoPtr info = *p;
+ if(!info->optional)
+ {
+ void* closure = reinterpret_cast<void*>(info->pos);
+ info->type->unmarshal(is, info, results, closure, false);
+ }
+ }
+
+ //
+ // Unmarshal the required return value, if any.
+ //
+ if(_returnType && !_returnType->optional)
+ {
+ assert(_returnType->pos == 0);
+ void* closure = reinterpret_cast<void*>(_returnType->pos);
+ _returnType->type->unmarshal(is, _returnType, results, closure, false);
+ }
+
+ //
+ // Unmarshal the optional results. This includes an optional return value.
+ //
+ for(p = _optionalOutParams.begin(); p != _optionalOutParams.end(); ++p)
+ {
+ ParamInfoPtr info = *p;
+ if(is->readOptional(info->tag, info->type->optionalFormat()))
+ {
+ void* closure = reinterpret_cast<void*>(info->pos);
+ info->type->unmarshal(is, info, results, closure, true);
+ }
+ else
+ {
+ RARRAY_PTR(results)[info->pos] = Unset;
+ }
+ }
+
+ if(_returnsClasses)
+ {
+ is->readPendingObjects();
+ }
+
+ is->endEncapsulation();
+
+ util.update();
+
+ return results;
+}
+
+VALUE
+IceRuby::OperationI::unmarshalException(const vector<Ice::Byte>& bytes, const Ice::CommunicatorPtr& communicator)
+{
+ Ice::InputStreamPtr is = Ice::wrapInputStream(communicator, bytes);
+
+ //
+ // Store a pointer to a local SlicedDataUtil object as the stream's closure.
+ // This is necessary to support object unmarshaling (see ObjectReader).
+ //
+ SlicedDataUtil util;
+ assert(!is->closure());
+ is->closure(&util);
+
+ is->startEncapsulation();
+
+ try
+ {
+ Ice::UserExceptionReaderFactoryPtr factory = new UserExceptionReaderFactoryI(communicator);
+ is->throwException(factory);
+ }
+ catch(const ExceptionReader& r)
+ {
+ is->endEncapsulation();
+
+ volatile VALUE ex = r.getException();
+
+ if(validateException(ex))
+ {
+ util.update();
+
+ Ice::SlicedDataPtr slicedData = r.getSlicedData();
+ if(slicedData)
+ {
+ SlicedDataUtil::setMember(ex, slicedData);
+ }
+
+ return ex;
+ }
+ else
+ {
+ volatile VALUE cls = CLASS_OF(ex);
+ volatile VALUE path = callRuby(rb_class_path, cls);
+ assert(TYPE(path) == T_STRING);
+ Ice::UnknownUserException e(__FILE__, __LINE__);
+ e.unknown = RSTRING_PTR(path);
+ throw e;
+ }
+ }
+
+ throw Ice::UnknownUserException(__FILE__, __LINE__, "unknown exception");
+#ifdef __SUNPRO_CC
+ return 0;
+#endif
+
+}
+
+bool
+IceRuby::OperationI::validateException(VALUE ex) const
+{
+ for(ExceptionInfoList::const_iterator p = _exceptions.begin(); p != _exceptions.end(); ++p)
+ {
+ if(callRuby(rb_obj_is_kind_of, ex, (*p)->rubyClass))
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void
+IceRuby::OperationI::checkTwowayOnly(const Ice::ObjectPrx& proxy) const
+{
+ if((_returnType != 0 || !_outParams.empty()) && !proxy->ice_isTwoway())
+ {
+ Ice::TwowayOnlyException ex(__FILE__, __LINE__);
+ ex.operation = _name;
+ throw ex;
+ }
+}
+
+bool
+IceRuby::initOperation(VALUE iceModule)
+{
+ rb_define_module_function(iceModule, "__defineOperation", CAST_METHOD(IceRuby_defineOperation), 9);
+
+ //
+ // Define a class to represent an operation.
+ //
+ _operationClass = rb_define_class_under(iceModule, "IceRuby_Operation", rb_cObject);
+
+ rb_define_method(_operationClass, "invoke", CAST_METHOD(IceRuby_Operation_invoke), 3);
+ rb_define_method(_operationClass, "deprecate", CAST_METHOD(IceRuby_Operation_deprecate), 1);
+
+ return true;
+}
+
+IceRuby::OperationPtr
+IceRuby::getOperation(VALUE obj)
+{
+ assert(TYPE(obj) == T_DATA);
+ assert(rb_obj_is_instance_of(obj, _operationClass) == Qtrue);
+ OperationPtr* p = reinterpret_cast<OperationPtr*>(DATA_PTR(obj));
+ return *p;
+}