lib

rubyextension.cpp

00001 /***************************************************************************
00002  * rubyinterpreter.cpp
00003  * This file is part of the KDE project
00004  * copyright (C)2005 by Cyrille Berger (cberger@cberger.net)
00005  *
00006  * This program is free software; you can redistribute it and/or
00007  * modify it under the terms of the GNU Library General Public
00008  * License as published by the Free Software Foundation; either
00009  * version 2 of the License, or (at your option) any later version.
00010  * This program is distributed in the hope that it will be useful,
00011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013  * Library General Public License for more details.
00014  * You should have received a copy of the GNU Library General Public License
00015  * along with this program; see the file COPYING.  If not, write to
00016  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00017  * Boston, MA 02110-1301, USA.
00018  ***************************************************************************/
00019 #include "rubyextension.h"
00020 
00021 #include <st.h>
00022 
00023 #include <qmap.h>
00024 #include <qstring.h>
00025 
00026 #include "api/list.h"
00027 
00028 #include "rubyconfig.h"
00029 
00030 namespace Kross {
00031 
00032 namespace Ruby {
00033 
00034     
00035 class RubyExtensionPrivate {
00036     friend class RubyExtension;
00038     Kross::Api::Object::Ptr m_object;
00040     static VALUE s_krossObject;
00041     static VALUE s_krossException;
00042 };
00043 
00044 VALUE RubyExtensionPrivate::s_krossObject = 0;
00045 VALUE RubyExtensionPrivate::s_krossException = 0;
00046     
00047 VALUE RubyExtension::method_missing(int argc, VALUE *argv, VALUE self)
00048 {
00049 #ifdef KROSS_RUBY_EXTENSION_DEBUG
00050     krossdebug("method_missing(argc, argv, self)");
00051 #endif
00052     if(argc < 1)
00053     {
00054         return 0;
00055     }
00056 #ifdef KROSS_RUBY_EXTENSION_DEBUG
00057     krossdebug("Converting self to Kross::Api::Object");
00058 #endif
00059     
00060     Kross::Api::Object::Ptr object = toObject( self );
00061     return RubyExtension::call_method(object, argc, argv);
00062 }
00063 
00064 VALUE RubyExtension::call_method( Kross::Api::Object::Ptr object, int argc, VALUE *argv)
00065 {
00066     QString funcname = rb_id2name(SYM2ID(argv[0]));
00067     QValueList<Api::Object::Ptr> argsList;
00068 #ifdef KROSS_RUBY_EXTENSION_DEBUG
00069     krossdebug(QString("Building arguments list for function: %1 there are %2 arguments.").arg(funcname).arg(argc-1));
00070 #endif
00071     for(int i = 1; i < argc; i++)
00072     {
00073         Kross::Api::Object::Ptr obj = toObject(argv[i]);
00074         if(obj) argsList.append(obj);
00075     }
00076     Kross::Api::Object::Ptr result;
00077     try { // We need a double try/catch because, the cleaning is only done at the end of the catch, so if we had only one try/catch, kross would crash after the call to rb_exc_raise
00078         try { // We can't let a C++ exceptions propagate in the C mechanism
00079             Kross::Api::Callable* callable = dynamic_cast<Kross::Api::Callable*>(object.data());
00080             if(callable && callable->hasChild(funcname)) {
00081 #ifdef KROSS_RUBY_EXTENSION_DEBUG
00082                 krossdebug( QString("Kross::Ruby::RubyExtension::method_missing name='%1' is a child object of '%2'.").arg(funcname).arg(object->getName()) );
00083 #endif
00084                 result = callable->getChild(funcname)->call(QString::null, new Api::List(argsList));
00085             }
00086             else {
00087 #ifdef KROSS_RUBY_EXTENSION_DEBUG
00088                 krossdebug( QString("Kross::Ruby::RubyExtension::method_missing try to call function with name '%1' in object '%2'.").arg(funcname).arg(object->getName()) );
00089 #endif
00090                 result = object->call(funcname, new Api::List(argsList));
00091             }
00092         } catch(Kross::Api::Exception::Ptr exception)
00093         {
00094 #ifdef KROSS_RUBY_EXTENSION_DEBUG
00095             krossdebug("c++ exception catched, raise a ruby error");
00096 #endif
00097             throw convertFromException(exception);
00098         }  catch(...)
00099         {
00100             throw convertFromException(new Kross::Api::Exception( "Unknow error" )); // TODO: fix //i18n
00101         }
00102     } catch(VALUE v) {
00103          rb_exc_raise(v );
00104     }
00105     return toVALUE(result);
00106 }
00107 
00108 void RubyExtension::delete_object(void* object)
00109 {
00110     krossdebug("delete_object");
00111     RubyExtension* obj = static_cast<RubyExtension*>(object);
00112     if(obj)
00113         delete obj;
00114 }
00115 
00116 void RubyExtension::delete_exception(void* object)
00117 {
00118     Kross::Api::Exception* exc = static_cast<Kross::Api::Exception*>(object);
00119     exc->_KShared_unref();
00120 }
00121 
00122     
00123 RubyExtension::RubyExtension(Kross::Api::Object::Ptr object) : d(new RubyExtensionPrivate())
00124 {
00125     d->m_object = object;
00126 }
00127 
00128 
00129 RubyExtension::~RubyExtension()
00130 {
00131     krossdebug("Delete RubyExtension");
00132     delete d;
00133 }
00134 
00135 typedef QMap<QString, Kross::Api::Object::Ptr> mStrObj;
00136 
00137 int RubyExtension::convertHash_i(VALUE key, VALUE value, VALUE  vmap)
00138 {
00139     QMap<QString, Kross::Api::Object::Ptr>* map; 
00140     Data_Get_Struct(vmap, mStrObj, map);
00141     if (key != Qundef)
00142     {
00143         Kross::Api::Object::Ptr o = RubyExtension::toObject( value );
00144         if(o) map->replace(STR2CSTR(key), o);
00145     }
00146     return ST_CONTINUE;
00147 }
00148 
00149 bool RubyExtension::isOfExceptionType(VALUE value)
00150 {
00151     VALUE result = rb_funcall(value, rb_intern("kind_of?"), 1, RubyExtensionPrivate::s_krossException );
00152     return (TYPE(result) == T_TRUE);
00153 }
00154 
00155 bool RubyExtension::isOfObjectType(VALUE value)
00156 {
00157     VALUE result = rb_funcall(value, rb_intern("kind_of?"), 1, RubyExtensionPrivate::s_krossObject );
00158     return (TYPE(result) == T_TRUE);
00159 }
00160 
00161 
00162 Kross::Api::Exception::Ptr RubyExtension::convertToException(VALUE value)
00163 {
00164     if( isOfExceptionType(value) )
00165     {
00166         Kross::Api::Exception* exception;
00167         Data_Get_Struct(value, Kross::Api::Exception, exception);
00168         return exception;
00169     }
00170     return 0;
00171 }
00172 
00173 VALUE RubyExtension::convertFromException(Kross::Api::Exception::Ptr exc)
00174 {
00175     if(RubyExtensionPrivate::s_krossException == 0)
00176     {
00177         RubyExtensionPrivate::s_krossException = rb_define_class("KrossException", rb_eRuntimeError);
00178     }
00179     exc->_KShared_ref();
00180     return Data_Wrap_Struct(RubyExtensionPrivate::s_krossException, 0, RubyExtension::delete_exception, exc.data() );
00181 }
00182 
00183 
00184 Kross::Api::Object::Ptr RubyExtension::toObject(VALUE value)
00185 {
00186 #ifdef KROSS_RUBY_EXTENSION_DEBUG
00187     krossdebug(QString("RubyExtension::toObject of type %1").arg(TYPE(value)));
00188 #endif
00189     switch( TYPE( value ) )
00190     {
00191         case T_DATA:
00192         {
00193 #ifdef KROSS_RUBY_EXTENSION_DEBUG
00194             krossdebug("Object is a Kross Object");
00195 #endif
00196             if( isOfObjectType(value) )
00197             {
00198                 RubyExtension* objectExtension;
00199                 Data_Get_Struct(value, RubyExtension, objectExtension);
00200                 Kross::Api::Object::Ptr object = objectExtension->d->m_object;
00201                 return object;
00202             } else {
00203                 krosswarning("Cannot yet convert standard ruby type to kross object");
00204                 return 0;
00205             }
00206         }
00207         case T_FLOAT:
00208             return new Kross::Api::Variant(NUM2DBL(value));
00209         case T_STRING:
00210             return new Kross::Api::Variant(QString(STR2CSTR(value)));
00211         case T_ARRAY:
00212         {
00213             QValueList<Kross::Api::Object::Ptr> l;
00214             for(int i = 0; i < RARRAY(value)->len; i++)
00215             {
00216                 Kross::Api::Object::Ptr o = toObject( rb_ary_entry( value , i ) );
00217                 if(o) l.append(o);
00218             }
00219             return new Kross::Api::List(l);
00220         }
00221         case T_FIXNUM:
00222             return new Kross::Api::Variant((Q_LLONG)FIX2INT(value));
00223         case T_HASH:
00224         {
00225             QMap<QString, Kross::Api::Object::Ptr> map;
00226             VALUE vmap = Data_Wrap_Struct(rb_cObject, 0,0, &map);
00227             rb_hash_foreach(value, (int (*)(...))convertHash_i, vmap);
00228             return new Kross::Api::Dict(map);
00229         }
00230         case T_BIGNUM:
00231         {
00232             return new Kross::Api::Variant((Q_LLONG)NUM2LONG(value));
00233         }
00234         case T_TRUE:
00235         {
00236             return new Kross::Api::Variant(true);
00237         }
00238         case T_FALSE:
00239         {
00240             return new Kross::Api::Variant(false);
00241         }
00242         case T_SYMBOL:
00243         {
00244             return new Kross::Api::Variant(QString(rb_id2name(SYM2ID(value))));
00245         }
00246         case T_MATCH:
00247         case T_OBJECT:
00248         case T_FILE:
00249         case T_STRUCT:
00250         case T_REGEXP:
00251         case T_MODULE:
00252         case T_ICLASS:
00253         case T_CLASS:
00254             krosswarning(QString("This ruby type '%1' cannot be converted to a Kross::Api::Object").arg(TYPE(value)));
00255         default:
00256         case T_NIL:
00257             return 0;
00258     }
00259 }
00260 
00261 VALUE RubyExtension::toVALUE(const QString& s)
00262 {
00263     return s.isNull() ? rb_str_new2("") : rb_str_new2(s.latin1());
00264 }
00265 
00266 VALUE RubyExtension::toVALUE(QStringList list)
00267 {
00268     VALUE l = rb_ary_new();
00269     for(QStringList::ConstIterator it = list.constBegin(); it != list.constEnd(); ++it)
00270         rb_ary_push(l, toVALUE(*it));
00271     return l;
00272 }
00273 
00274 
00275 VALUE RubyExtension::toVALUE(QMap<QString, QVariant> map)
00276 {
00277     VALUE h = rb_hash_new();
00278     for(QMap<QString, QVariant>::Iterator it = map.begin(); it != map.end(); ++it)
00279         rb_hash_aset(h, toVALUE(it.key()), toVALUE(it.data()) );
00280     return h;
00281 
00282 }
00283 
00284 VALUE RubyExtension::toVALUE(QValueList<QVariant> list)
00285 {
00286     VALUE l = rb_ary_new();
00287     for(QValueList<QVariant>::Iterator it = list.begin(); it != list.end(); ++it)
00288         rb_ary_push(l, toVALUE(*it));
00289     return l;
00290 }
00291 
00292 
00293 VALUE RubyExtension::toVALUE(const QVariant& variant)
00294 {
00295     
00296     switch(variant.type()) {
00297         case QVariant::Invalid:
00298             return Qnil;
00299         case QVariant::Bool:
00300             return (variant.toBool()) ? Qtrue : Qfalse;
00301         case QVariant::Int:
00302             return INT2FIX(variant.toInt());
00303         case QVariant::UInt:
00304             return UINT2NUM(variant.toUInt());
00305         case QVariant::Double:
00306             return rb_float_new(variant.toDouble());
00307         case QVariant::Date:
00308         case QVariant::Time:
00309         case QVariant::DateTime:
00310         case QVariant::ByteArray:
00311         case QVariant::BitArray:
00312         case QVariant::CString:
00313         case QVariant::String:
00314             return toVALUE(variant.toString());
00315         case QVariant::StringList:
00316             return toVALUE(variant.toStringList());
00317         case QVariant::Map:
00318             return toVALUE(variant.toMap());
00319         case QVariant::List:
00320             return toVALUE(variant.toList());
00321 
00322         // To handle following both cases is a bit difficult
00323         // cause Python doesn't spend an easy possibility
00324         // for such large numbers (TODO maybe BigInt?). So,
00325         // we risk overflows here, but well...
00326         case QVariant::LongLong: {
00327             return INT2NUM((long)variant.toLongLong());
00328         }
00329         case QVariant::ULongLong:
00330             return UINT2NUM((unsigned long)variant.toULongLong());
00331         default: {
00332             krosswarning( QString("Kross::Ruby::RubyExtension::toVALUE(QVariant) Not possible to convert the QVariant type '%1' to a VALUE.").arg(variant.typeName()) );
00333             return Qundef;
00334         }
00335     }
00336 }
00337 
00338 VALUE RubyExtension::toVALUE(Kross::Api::Object::Ptr object)
00339 {
00340     if(! object.data()) {
00341         return 0;
00342     }
00343     if(object->getClassName() == "Kross::Api::Variant") {
00344         QVariant v = static_cast<Kross::Api::Variant*>( object.data() )->getValue();
00345         return toVALUE(v);
00346     }
00347 
00348     if(object->getClassName() == "Kross::Api::List") {
00349         Kross::Api::List* list = static_cast<Kross::Api::List*>( object.data() );
00350         return toVALUE((Kross::Api::List::Ptr)list);
00351     }
00352 
00353     if(object->getClassName() == "Kross::Api::Dict") {
00354         Kross::Api::Dict* dict = static_cast<Kross::Api::Dict*>( object.data() );
00355         return toVALUE((Kross::Api::Dict::Ptr)dict);
00356     }
00357 
00358     if(RubyExtensionPrivate::s_krossObject == 0)
00359     {
00360         RubyExtensionPrivate::s_krossObject = rb_define_class("KrossObject", rb_cObject);
00361         rb_define_method(RubyExtensionPrivate::s_krossObject, "method_missing",  (VALUE (*)(...))RubyExtension::method_missing, -1);
00362     }
00363     return Data_Wrap_Struct(RubyExtensionPrivate::s_krossObject, 0, RubyExtension::delete_object, new RubyExtension(object) );
00364 }
00365 
00366 VALUE RubyExtension::toVALUE(Kross::Api::List::Ptr list)
00367 {
00368     VALUE l = rb_ary_new();
00369     uint count = list ? list->count() : 0;
00370     for(uint i = 0; i < count; i++)
00371         rb_ary_push(l, toVALUE(list->item(i)));
00372     return l;
00373 
00374 }
00375 
00376 }
00377 
00378 }
KDE Home | KDE Accessibility Home | Description of Access Keys