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     kdDebug() << "method_missing(argc, argv, self)" << endl;
00051 #endif
00052     if(argc < 1)
00053     {
00054         return 0;
00055     }
00056 #ifdef KROSS_RUBY_EXTENSION_DEBUG
00057     kdDebug() << "Converting self to Kross::Api::Object" << endl;
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     kdDebug() << "Building arguments list for function : " << funcname << " there are " << (argc-1) << " arguments." << endl;
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             if(object->hasChild(funcname)) {
00080 #ifdef KROSS_RUBY_EXTENSION_DEBUG
00081                 kdDebug() << QString("Kross::Ruby::RubyExtension::method_missing name='%1' is a child object of '%2'.").arg(funcname).arg(object->getName()) << endl;
00082 #endif
00083                 result = object->getChild(funcname)->call(QString::null, new Api::List(argsList));
00084             }
00085             else {
00086 #ifdef KROSS_RUBY_EXTENSION_DEBUG
00087                 kdDebug() << QString("Kross::Ruby::RubyExtension::method_missing try to call function with name '%1' in object '%2'.").arg(funcname).arg(object->getName()) << endl;
00088 #endif
00089                 result = object->call(funcname, new Api::List(argsList));
00090             }
00091         } catch(Kross::Api::Exception::Ptr exception)
00092         {
00093 #ifdef KROSS_RUBY_EXTENSION_DEBUG
00094             kdDebug() << "c++ exception catched, raise a ruby error" << endl;
00095 #endif
00096             throw convertFromException(exception);
00097         }  catch(...)
00098         {
00099             throw convertFromException(new Kross::Api::Exception( "Unknow error" )); // TODO: fix //i18n
00100         }
00101     } catch(VALUE v) {
00102          rb_exc_raise(v );
00103     }
00104     return toVALUE(result);
00105 }
00106 
00107 void RubyExtension::delete_object(void* object)
00108 {
00109     kdDebug() << "delete_object" << endl;
00110     RubyExtension* obj = static_cast<RubyExtension*>(object);
00111     if(obj)
00112         delete obj;
00113 }
00114 
00115 void RubyExtension::delete_exception(void* object)
00116 {
00117     Kross::Api::Exception* exc = static_cast<Kross::Api::Exception*>(object);
00118     exc->_KShared_unref();
00119 }
00120 
00121     
00122 RubyExtension::RubyExtension(Kross::Api::Object::Ptr object) : d(new RubyExtensionPrivate())
00123 {
00124     d->m_object = object;
00125 }
00126 
00127 
00128 RubyExtension::~RubyExtension()
00129 {
00130     kdDebug() << "Delete RubyExtension" << endl;
00131     delete d;
00132 }
00133 
00134 typedef QMap<QString, Kross::Api::Object::Ptr> mStrObj;
00135 
00136 int RubyExtension::convertHash_i(VALUE key, VALUE value, VALUE  vmap)
00137 {
00138     QMap<QString, Kross::Api::Object::Ptr>* map; 
00139     Data_Get_Struct(vmap, mStrObj, map);
00140     if (key != Qundef)
00141     {
00142         Kross::Api::Object::Ptr o = RubyExtension::toObject( value );
00143         if(o) map->replace(STR2CSTR(key), o);
00144     }
00145     return ST_CONTINUE;
00146 }
00147 
00148 bool RubyExtension::isOfExceptionType(VALUE value)
00149 {
00150     VALUE result = rb_funcall(value, rb_intern("kind_of?"), 1, RubyExtensionPrivate::s_krossException );
00151     return (TYPE(result) == T_TRUE);
00152 }
00153 
00154 bool RubyExtension::isOfObjectType(VALUE value)
00155 {
00156     VALUE result = rb_funcall(value, rb_intern("kind_of?"), 1, RubyExtensionPrivate::s_krossObject );
00157     return (TYPE(result) == T_TRUE);
00158 }
00159 
00160 
00161 Kross::Api::Exception::Ptr RubyExtension::convertToException(VALUE value)
00162 {
00163     if( isOfExceptionType(value) )
00164     {
00165         Kross::Api::Exception* exception;
00166         Data_Get_Struct(value, Kross::Api::Exception, exception);
00167         return exception;
00168     }
00169     return 0;
00170 }
00171 
00172 VALUE RubyExtension::convertFromException(Kross::Api::Exception::Ptr exc)
00173 {
00174     if(RubyExtensionPrivate::s_krossException == 0)
00175     {
00176         RubyExtensionPrivate::s_krossException = rb_define_class("KrossException", rb_eRuntimeError);
00177     }
00178     exc->_KShared_ref();
00179     return Data_Wrap_Struct(RubyExtensionPrivate::s_krossException, 0, RubyExtension::delete_exception, exc.data() );
00180 }
00181 
00182 
00183 Kross::Api::Object::Ptr RubyExtension::toObject(VALUE value)
00184 {
00185 #ifdef KROSS_RUBY_EXTENSION_DEBUG
00186     kdDebug() << "RubyExtension::toObject of type " << TYPE(value) << endl;
00187 #endif
00188     switch( TYPE( value ) )
00189     {
00190         case T_DATA:
00191         {
00192 #ifdef KROSS_RUBY_EXTENSION_DEBUG
00193             kdDebug() << "Object is a Kross Object" << endl;
00194 #endif
00195             if( isOfObjectType(value) )
00196             {
00197                 RubyExtension* objectExtension;
00198                 Data_Get_Struct(value, RubyExtension, objectExtension);
00199                 Kross::Api::Object::Ptr object = objectExtension->d->m_object;
00200                 return object;
00201             } else {
00202                 kdWarning() << "Cannot yet convert standard ruby type to kross object" << endl;
00203                 return 0;
00204             }
00205         }
00206         case T_FLOAT:
00207             return new Kross::Api::Variant(NUM2DBL(value));
00208         case T_STRING:
00209             return new Kross::Api::Variant(QString(STR2CSTR(value)));
00210         case T_ARRAY:
00211         {
00212             QValueList<Kross::Api::Object::Ptr> l;
00213             for(int i = 0; i < RARRAY(value)->len; i++)
00214             {
00215                 Kross::Api::Object::Ptr o = toObject( rb_ary_entry( value , i ) );
00216                 if(o) l.append(o);
00217             }
00218             return new Kross::Api::List(l);
00219         }
00220         case T_FIXNUM:
00221             return new Kross::Api::Variant((Q_LLONG)FIX2INT(value));
00222         case T_HASH:
00223         {
00224             QMap<QString, Kross::Api::Object::Ptr> map;
00225             VALUE vmap = Data_Wrap_Struct(rb_cObject, 0,0, &map);
00226             rb_hash_foreach(value, (int (*)(...))convertHash_i, vmap);
00227             return new Kross::Api::Dict(map);
00228         }
00229         case T_BIGNUM:
00230         {
00231             return new Kross::Api::Variant((Q_LLONG)NUM2LONG(value));
00232         }
00233         case T_TRUE:
00234         {
00235             return new Kross::Api::Variant(true);
00236         }
00237         case T_FALSE:
00238         {
00239             return new Kross::Api::Variant(false);
00240         }
00241         case T_SYMBOL:
00242         {
00243             return new Kross::Api::Variant(QString(rb_id2name(SYM2ID(value))));
00244         }
00245         case T_MATCH:
00246         case T_OBJECT:
00247         case T_FILE:
00248         case T_STRUCT:
00249         case T_REGEXP:
00250         case T_MODULE:
00251         case T_ICLASS:
00252         case T_CLASS:
00253             kdWarning() << QString("This ruby type '%1' cannot be converted to a Kross::Api::Object").arg(TYPE(value)) << endl;
00254         default:
00255         case T_NIL:
00256             return 0;
00257     }
00258 }
00259 
00260 VALUE RubyExtension::toVALUE(const QString& s)
00261 {
00262     return s.isNull() ? rb_str_new2("") : rb_str_new2(s.latin1());
00263 }
00264 
00265 VALUE RubyExtension::toVALUE(QStringList list)
00266 {
00267     VALUE l = rb_ary_new();
00268     for(QStringList::ConstIterator it = list.constBegin(); it != list.constEnd(); ++it)
00269         rb_ary_push(l, toVALUE(*it));
00270     return l;
00271 }
00272 
00273 
00274 VALUE RubyExtension::toVALUE(QMap<QString, QVariant> map)
00275 {
00276     VALUE h = rb_hash_new();
00277     for(QMap<QString, QVariant>::Iterator it = map.begin(); it != map.end(); ++it)
00278         rb_hash_aset(h, toVALUE(it.key()), toVALUE(it.data()) );
00279     return h;
00280 
00281 }
00282 
00283 VALUE RubyExtension::toVALUE(QValueList<QVariant> list)
00284 {
00285     VALUE l = rb_ary_new();
00286     for(QValueList<QVariant>::Iterator it = list.begin(); it != list.end(); ++it)
00287         rb_ary_push(l, toVALUE(*it));
00288     return l;
00289 }
00290 
00291 
00292 VALUE RubyExtension::toVALUE(const QVariant& variant)
00293 {
00294     
00295     switch(variant.type()) {
00296         case QVariant::Invalid:
00297             return Qnil;
00298         case QVariant::Bool:
00299             return (variant.toBool()) ? Qtrue : Qfalse;
00300         case QVariant::Int:
00301             return INT2FIX(variant.toInt());
00302         case QVariant::UInt:
00303             return UINT2NUM(variant.toUInt());
00304         case QVariant::Double:
00305             return rb_float_new(variant.toDouble());
00306         case QVariant::Date:
00307         case QVariant::Time:
00308         case QVariant::DateTime:
00309         case QVariant::ByteArray:
00310         case QVariant::BitArray:
00311         case QVariant::CString:
00312         case QVariant::String:
00313             return toVALUE(variant.toString());
00314         case QVariant::StringList:
00315             return toVALUE(variant.toStringList());
00316         case QVariant::Map:
00317             return toVALUE(variant.toMap());
00318         case QVariant::List:
00319             return toVALUE(variant.toList());
00320 
00321         // To handle following both cases is a bit difficult
00322         // cause Python doesn't spend an easy possibility
00323         // for such large numbers (TODO maybe BigInt?). So,
00324         // we risk overflows here, but well...
00325         case QVariant::LongLong: {
00326             return INT2NUM((long)variant.toLongLong());
00327         }
00328         case QVariant::ULongLong:
00329             return UINT2NUM((unsigned long)variant.toULongLong());
00330         default: {
00331             kdWarning() << QString("Kross::Ruby::RubyExtension::toVALUE(QVariant) Not possible to convert the QVariant type '%1' to a VALUE.").arg(variant.typeName()) << endl;
00332             return Qundef;
00333         }
00334     }
00335 }
00336 
00337 VALUE RubyExtension::toVALUE(Kross::Api::Object::Ptr object)
00338 {
00339     if(! object) {
00340         return 0;
00341     }
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