diff options
Diffstat (limited to 'lib/kross/ruby')
-rw-r--r-- | lib/kross/ruby/Makefile.am | 16 | ||||
-rw-r--r-- | lib/kross/ruby/rubyconfig.h | 42 | ||||
-rw-r--r-- | lib/kross/ruby/rubyextension.cpp | 378 | ||||
-rw-r--r-- | lib/kross/ruby/rubyextension.h | 162 | ||||
-rw-r--r-- | lib/kross/ruby/rubyinterpreter.cpp | 149 | ||||
-rw-r--r-- | lib/kross/ruby/rubyinterpreter.h | 73 | ||||
-rw-r--r-- | lib/kross/ruby/rubymodule.cpp | 68 | ||||
-rw-r--r-- | lib/kross/ruby/rubymodule.h | 73 | ||||
-rw-r--r-- | lib/kross/ruby/rubyscript.cpp | 193 | ||||
-rw-r--r-- | lib/kross/ruby/rubyscript.h | 98 |
10 files changed, 1252 insertions, 0 deletions
diff --git a/lib/kross/ruby/Makefile.am b/lib/kross/ruby/Makefile.am new file mode 100644 index 00000000..fb8ddfac --- /dev/null +++ b/lib/kross/ruby/Makefile.am @@ -0,0 +1,16 @@ +include $(top_srcdir)/lib/kross/Makefile.global + +INCLUDES = -I$(top_srcdir)/lib/kross -I$(RUBY_INCLUDEDIR) $(all_includes) +METASOURCES = AUTO +kde_module_LTLIBRARIES = krossruby.la + +krossruby_la_LDFLAGS = $(KDE_PLUGIN) $(all_libraries) $(RUBY_LIBRUBYARG) -module $(VER_INFO) + +krossruby_la_LIBADD = \ + $(LIB_QT) \ + $(LIB_KDECORE) \ + ../api/libkrossapi.la \ + ../main/libkrossmain.la + +noinst_HEADERS = rubyinterpreter.h rubyextension.h rubyscript.h rubyconfig.h +krossruby_la_SOURCES = rubyinterpreter.cpp rubyextension.cpp rubyscript.cpp rubymodule.cpp diff --git a/lib/kross/ruby/rubyconfig.h b/lib/kross/ruby/rubyconfig.h new file mode 100644 index 00000000..81a74c55 --- /dev/null +++ b/lib/kross/ruby/rubyconfig.h @@ -0,0 +1,42 @@ +/*************************************************************************** + * pythonconfig.h + * This file is part of the KDE project + * copyright (C)2005 by Cyrille Berger (cberger@cberger.net) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * You should have received a copy of the GNU Library General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ***************************************************************************/ + +#ifndef KROSS_RUBY_CONFIG_H +#define KROSS_RUBY_CONFIG_H + +#include "../main/krossconfig.h" + +namespace Kross { + +/** + * The Ruby plugin for the \a Kross scripting framework. + * + * @author Cyrille Berger + * @sa http://www.ruby-lang.org + */ +namespace Ruby { +// #define KROSS_RUBY_SCRIPT_DEBUG +// #define KROSS_RUBY_INTERPRETER_DEBUG +// #define KROSS_RUBY_EXTENSION_DEBUG +// #define KROSS_RUBY_MODULE_DEBUG +} +} + +#endif + diff --git a/lib/kross/ruby/rubyextension.cpp b/lib/kross/ruby/rubyextension.cpp new file mode 100644 index 00000000..2c022cee --- /dev/null +++ b/lib/kross/ruby/rubyextension.cpp @@ -0,0 +1,378 @@ +/*************************************************************************** + * rubyinterpreter.cpp + * This file is part of the KDE project + * copyright (C)2005 by Cyrille Berger (cberger@cberger.net) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * You should have received a copy of the GNU Library General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ***************************************************************************/ +#include "rubyextension.h" + +#include <st.h> + +#include <qmap.h> +#include <qstring.h> + +#include "api/list.h" + +#include "rubyconfig.h" + +namespace Kross { + +namespace Ruby { + + +class RubyExtensionPrivate { + friend class RubyExtension; + /// The \a Kross::Api::Object this RubyExtension wraps. + Kross::Api::Object::Ptr m_object; + /// + static VALUE s_krossObject; + static VALUE s_krossException; +}; + +VALUE RubyExtensionPrivate::s_krossObject = 0; +VALUE RubyExtensionPrivate::s_krossException = 0; + +VALUE RubyExtension::method_missing(int argc, VALUE *argv, VALUE self) +{ +#ifdef KROSS_RUBY_EXTENSION_DEBUG + krossdebug("method_missing(argc, argv, self)"); +#endif + if(argc < 1) + { + return 0; + } +#ifdef KROSS_RUBY_EXTENSION_DEBUG + krossdebug("Converting self to Kross::Api::Object"); +#endif + + Kross::Api::Object::Ptr object = toObject( self ); + return RubyExtension::call_method(object, argc, argv); +} + +VALUE RubyExtension::call_method( Kross::Api::Object::Ptr object, int argc, VALUE *argv) +{ + QString funcname = rb_id2name(SYM2ID(argv[0])); + QValueList<Api::Object::Ptr> argsList; +#ifdef KROSS_RUBY_EXTENSION_DEBUG + krossdebug(QString("Building arguments list for function: %1 there are %2 arguments.").arg(funcname).arg(argc-1)); +#endif + for(int i = 1; i < argc; i++) + { + Kross::Api::Object::Ptr obj = toObject(argv[i]); + if(obj) argsList.append(obj); + } + Kross::Api::Object::Ptr result; + 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 + try { // We can't let a C++ exceptions propagate in the C mechanism + Kross::Api::Callable* callable = dynamic_cast<Kross::Api::Callable*>(object.data()); + if(callable && callable->hasChild(funcname)) { +#ifdef KROSS_RUBY_EXTENSION_DEBUG + krossdebug( QString("Kross::Ruby::RubyExtension::method_missing name='%1' is a child object of '%2'.").arg(funcname).arg(object->getName()) ); +#endif + result = callable->getChild(funcname)->call(QString::null, new Api::List(argsList)); + } + else { +#ifdef KROSS_RUBY_EXTENSION_DEBUG + krossdebug( QString("Kross::Ruby::RubyExtension::method_missing try to call function with name '%1' in object '%2'.").arg(funcname).arg(object->getName()) ); +#endif + result = object->call(funcname, new Api::List(argsList)); + } + } catch(Kross::Api::Exception::Ptr exception) + { +#ifdef KROSS_RUBY_EXTENSION_DEBUG + krossdebug("c++ exception catched, raise a ruby error"); +#endif + throw convertFromException(exception); + } catch(...) + { + throw convertFromException(new Kross::Api::Exception( "Unknow error" )); // TODO: fix //i18n + } + } catch(VALUE v) { + rb_exc_raise(v ); + } + return toVALUE(result); +} + +void RubyExtension::delete_object(void* object) +{ + krossdebug("delete_object"); + RubyExtension* obj = static_cast<RubyExtension*>(object); + if(obj) + delete obj; +} + +void RubyExtension::delete_exception(void* object) +{ + Kross::Api::Exception* exc = static_cast<Kross::Api::Exception*>(object); + exc->_KShared_unref(); +} + + +RubyExtension::RubyExtension(Kross::Api::Object::Ptr object) : d(new RubyExtensionPrivate()) +{ + d->m_object = object; +} + + +RubyExtension::~RubyExtension() +{ + krossdebug("Delete RubyExtension"); + delete d; +} + +typedef QMap<QString, Kross::Api::Object::Ptr> mStrObj; + +int RubyExtension::convertHash_i(VALUE key, VALUE value, VALUE vmap) +{ + QMap<QString, Kross::Api::Object::Ptr>* map; + Data_Get_Struct(vmap, mStrObj, map); + if (key != Qundef) + { + Kross::Api::Object::Ptr o = RubyExtension::toObject( value ); + if(o) map->replace(STR2CSTR(key), o); + } + return ST_CONTINUE; +} + +bool RubyExtension::isOfExceptionType(VALUE value) +{ + VALUE result = rb_funcall(value, rb_intern("kind_of?"), 1, RubyExtensionPrivate::s_krossException ); + return (TYPE(result) == T_TRUE); +} + +bool RubyExtension::isOfObjectType(VALUE value) +{ + VALUE result = rb_funcall(value, rb_intern("kind_of?"), 1, RubyExtensionPrivate::s_krossObject ); + return (TYPE(result) == T_TRUE); +} + + +Kross::Api::Exception::Ptr RubyExtension::convertToException(VALUE value) +{ + if( isOfExceptionType(value) ) + { + Kross::Api::Exception* exception; + Data_Get_Struct(value, Kross::Api::Exception, exception); + return exception; + } + return 0; +} + +VALUE RubyExtension::convertFromException(Kross::Api::Exception::Ptr exc) +{ + if(RubyExtensionPrivate::s_krossException == 0) + { + RubyExtensionPrivate::s_krossException = rb_define_class("KrossException", rb_eRuntimeError); + } + exc->_KShared_ref(); + return Data_Wrap_Struct(RubyExtensionPrivate::s_krossException, 0, RubyExtension::delete_exception, exc.data() ); +} + + +Kross::Api::Object::Ptr RubyExtension::toObject(VALUE value) +{ +#ifdef KROSS_RUBY_EXTENSION_DEBUG + krossdebug(QString("RubyExtension::toObject of type %1").arg(TYPE(value))); +#endif + switch( TYPE( value ) ) + { + case T_DATA: + { +#ifdef KROSS_RUBY_EXTENSION_DEBUG + krossdebug("Object is a Kross Object"); +#endif + if( isOfObjectType(value) ) + { + RubyExtension* objectExtension; + Data_Get_Struct(value, RubyExtension, objectExtension); + Kross::Api::Object::Ptr object = objectExtension->d->m_object; + return object; + } else { + krosswarning("Cannot yet convert standard ruby type to kross object"); + return 0; + } + } + case T_FLOAT: + return new Kross::Api::Variant(NUM2DBL(value)); + case T_STRING: + return new Kross::Api::Variant(QString(STR2CSTR(value))); + case T_ARRAY: + { + QValueList<Kross::Api::Object::Ptr> l; + for(int i = 0; i < RARRAY(value)->len; i++) + { + Kross::Api::Object::Ptr o = toObject( rb_ary_entry( value , i ) ); + if(o) l.append(o); + } + return new Kross::Api::List(l); + } + case T_FIXNUM: + return new Kross::Api::Variant((Q_LLONG)FIX2INT(value)); + case T_HASH: + { + QMap<QString, Kross::Api::Object::Ptr> map; + VALUE vmap = Data_Wrap_Struct(rb_cObject, 0,0, &map); + rb_hash_foreach(value, (int (*)(...))convertHash_i, vmap); + return new Kross::Api::Dict(map); + } + case T_BIGNUM: + { + return new Kross::Api::Variant((Q_LLONG)NUM2LONG(value)); + } + case T_TRUE: + { + return new Kross::Api::Variant(true); + } + case T_FALSE: + { + return new Kross::Api::Variant(false); + } + case T_SYMBOL: + { + return new Kross::Api::Variant(QString(rb_id2name(SYM2ID(value)))); + } + case T_MATCH: + case T_OBJECT: + case T_FILE: + case T_STRUCT: + case T_REGEXP: + case T_MODULE: + case T_ICLASS: + case T_CLASS: + krosswarning(QString("This ruby type '%1' cannot be converted to a Kross::Api::Object").arg(TYPE(value))); + default: + case T_NIL: + return 0; + } +} + +VALUE RubyExtension::toVALUE(const QString& s) +{ + return s.isNull() ? rb_str_new2("") : rb_str_new2(s.latin1()); +} + +VALUE RubyExtension::toVALUE(QStringList list) +{ + VALUE l = rb_ary_new(); + for(QStringList::ConstIterator it = list.constBegin(); it != list.constEnd(); ++it) + rb_ary_push(l, toVALUE(*it)); + return l; +} + + +VALUE RubyExtension::toVALUE(QMap<QString, QVariant> map) +{ + VALUE h = rb_hash_new(); + for(QMap<QString, QVariant>::Iterator it = map.begin(); it != map.end(); ++it) + rb_hash_aset(h, toVALUE(it.key()), toVALUE(it.data()) ); + return h; + +} + +VALUE RubyExtension::toVALUE(QValueList<QVariant> list) +{ + VALUE l = rb_ary_new(); + for(QValueList<QVariant>::Iterator it = list.begin(); it != list.end(); ++it) + rb_ary_push(l, toVALUE(*it)); + return l; +} + + +VALUE RubyExtension::toVALUE(const QVariant& variant) +{ + + switch(variant.type()) { + case QVariant::Invalid: + return Qnil; + case QVariant::Bool: + return (variant.toBool()) ? Qtrue : Qfalse; + case QVariant::Int: + return INT2FIX(variant.toInt()); + case QVariant::UInt: + return UINT2NUM(variant.toUInt()); + case QVariant::Double: + return rb_float_new(variant.toDouble()); + case QVariant::Date: + case QVariant::Time: + case QVariant::DateTime: + case QVariant::ByteArray: + case QVariant::BitArray: + case QVariant::CString: + case QVariant::String: + return toVALUE(variant.toString()); + case QVariant::StringList: + return toVALUE(variant.toStringList()); + case QVariant::Map: + return toVALUE(variant.toMap()); + case QVariant::List: + return toVALUE(variant.toList()); + + // To handle following both cases is a bit difficult + // cause Python doesn't spend an easy possibility + // for such large numbers (TODO maybe BigInt?). So, + // we risk overflows here, but well... + case QVariant::LongLong: { + return INT2NUM((long)variant.toLongLong()); + } + case QVariant::ULongLong: + return UINT2NUM((unsigned long)variant.toULongLong()); + default: { + krosswarning( QString("Kross::Ruby::RubyExtension::toVALUE(QVariant) Not possible to convert the QVariant type '%1' to a VALUE.").arg(variant.typeName()) ); + return Qundef; + } + } +} + +VALUE RubyExtension::toVALUE(Kross::Api::Object::Ptr object) +{ + if(! object.data()) { + return 0; + } + if(object->getClassName() == "Kross::Api::Variant") { + QVariant v = static_cast<Kross::Api::Variant*>( object.data() )->getValue(); + return toVALUE(v); + } + + if(object->getClassName() == "Kross::Api::List") { + Kross::Api::List* list = static_cast<Kross::Api::List*>( object.data() ); + return toVALUE((Kross::Api::List::Ptr)list); + } + + if(object->getClassName() == "Kross::Api::Dict") { + Kross::Api::Dict* dict = static_cast<Kross::Api::Dict*>( object.data() ); + return toVALUE((Kross::Api::Dict::Ptr)dict); + } + + if(RubyExtensionPrivate::s_krossObject == 0) + { + RubyExtensionPrivate::s_krossObject = rb_define_class("KrossObject", rb_cObject); + rb_define_method(RubyExtensionPrivate::s_krossObject, "method_missing", (VALUE (*)(...))RubyExtension::method_missing, -1); + } + return Data_Wrap_Struct(RubyExtensionPrivate::s_krossObject, 0, RubyExtension::delete_object, new RubyExtension(object) ); +} + +VALUE RubyExtension::toVALUE(Kross::Api::List::Ptr list) +{ + VALUE l = rb_ary_new(); + uint count = list ? list->count() : 0; + for(uint i = 0; i < count; i++) + rb_ary_push(l, toVALUE(list->item(i))); + return l; + +} + +} + +} diff --git a/lib/kross/ruby/rubyextension.h b/lib/kross/ruby/rubyextension.h new file mode 100644 index 00000000..74041048 --- /dev/null +++ b/lib/kross/ruby/rubyextension.h @@ -0,0 +1,162 @@ +/*************************************************************************** + * rubyinterpreter.cpp + * This file is part of the KDE project + * copyright (C)2005 by Cyrille Berger (cberger@cberger.net) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * You should have received a copy of the GNU Library General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ***************************************************************************/ +#ifndef KROSS_RUBYRUBYEXTENSION_H +#define KROSS_RUBYRUBYEXTENSION_H + +#include <ruby.h> + +#include <api/class.h> +#include <api/dict.h> +#include <api/list.h> +#include <api/object.h> + +namespace Kross { + +namespace Ruby { + +class RubyExtensionPrivate; + +/** + * This class wraps a \a Kross::Api::Object into the world of ruby. + * @author Cyrille Berger + */ +class RubyExtension{ + friend class RubyInterpreter; + friend class RubyModule; + friend class RubyScript; + public: + /** + * Constructor. + * + * @param object The \a Kross::Api::Object instance this + * extension provides access to. + */ + RubyExtension(Kross::Api::Object::Ptr object); + /** + * Destructor. + */ + ~RubyExtension(); + private: + /** + * This function will catch functions that are undefined. + */ + static VALUE method_missing(int argc, VALUE *argv, VALUE self); + /** + * This function will call a function in a Kross object + * @param obj kross object which contains the function + * @param argc the number of argument + * @param argv the lists of arguments (the first argument is the Ruby ID of the function) + */ + static VALUE call_method( Kross::Api::Object::Ptr obj, int argc, VALUE *argv); + /** + * This function is called by ruby to delete a RubyExtension object + */ + static void delete_object(void* object); + /** + * This function is called by ruby to delete a RubyExtension object + */ + static void delete_exception(void* object); + private: // Tests + /** + * Test if the ruby object is an exception. + */ + static bool isOfExceptionType(VALUE obj); + /** + * Test if the ruby object is an object + */ + static bool isOfObjectType(VALUE obj); + private: //Converting functions + /** + * Convert a ruby object to the exception type. + * @return 0 if the object wasn't an exception. + */ + static Kross::Api::Exception::Ptr convertToException(VALUE obj); + /** + * Wrap an exception in a ruby object. + */ + static VALUE convertFromException(Kross::Api::Exception::Ptr exc); + /** + * This function iterats through a ruby hash + */ + static int convertHash_i(VALUE key, VALUE value, VALUE vmap); + /** + * Converts a \a VALUE into a \a Kross::Api::Object. + * \param object The ruby VALUE to convert. + * \return The to a Kross::Api::Object converted Py::Object. + */ + static Kross::Api::Object::Ptr toObject(VALUE value); + /** + * Converts a QString to a VALUE. If + * the QString isNull() then a "" will + * be returned. + * \param s The QString to convert. + * \return The converted QString. + */ + static VALUE toVALUE(const QString& s); + + /** + * Converts a QStringList to a VALUE. + * \param list The QStringList to convert. + * \return The converted QStringList. + */ + static VALUE toVALUE(QStringList list); + + /** + * Converts a QMap to a VALUE. + * \param map The QMap to convert. + * \return The converted QMap. + */ + static VALUE toVALUE(QMap<QString, QVariant> map); + + /** + * Converts a QValueList to a VALUE. + * \param list The QValueList to convert. + * \return The converted QValueList. + */ + static VALUE toVALUE(QValueList<QVariant> list); + /** + * Converts a QVariant to a VALUE. + * \param variant The QVariant to convert. + * \return The converted QVariant. + */ + static VALUE toVALUE(const QVariant& variant); + + /** + * Converts a \a Kross::Api::Object to a VALUE. + * \param object The Kross::Api::Object to convert. + * \return The converted Kross::Api::Object. + */ + static VALUE toVALUE(Kross::Api::Object::Ptr object); + + /** + * Converts a \a Kross::Api::List into a VALUE. + * \param list The Kross::Api::List to convert. + * \return The converted Kross::Api::List. + */ + static VALUE toVALUE(Kross::Api::List::Ptr list); + private: + /// Private d-pointer. + RubyExtensionPrivate* d; + }; + +} + +} + +#endif diff --git a/lib/kross/ruby/rubyinterpreter.cpp b/lib/kross/ruby/rubyinterpreter.cpp new file mode 100644 index 00000000..805ae722 --- /dev/null +++ b/lib/kross/ruby/rubyinterpreter.cpp @@ -0,0 +1,149 @@ +/*************************************************************************** + * rubyinterpreter.cpp + * This file is part of the KDE project + * copyright (C)2005 by Cyrille Berger (cberger@cberger.net) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * You should have received a copy of the GNU Library General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ***************************************************************************/ +#include "rubyinterpreter.h" + +#include <map> + +#include <qregexp.h> +#include <ksharedptr.h> + +#include <api/exception.h> +#include <api/module.h> +#include <main/manager.h> + +#include "rubyconfig.h" +#include "rubyextension.h" +#include "rubymodule.h" +#include "rubyscript.h" + +extern "C" +{ + /** + * Exported and loadable function as entry point to use + * the \a RubyInterpreter. + * The krossruby library will be loaded dynamicly at runtime from e.g. + * \a Kross::Api::Manager::getInterpreter and this exported + * function will be used to return an instance of the + * \a RubyInterpreter implementation. + */ + void* krossinterpreter(Kross::Api::InterpreterInfo* info) + { +#ifdef KROSS_RUBY_INTERPRETER_DEBUG + krossdebug("krossinterpreter(info)"); +#endif + try { + return new Kross::Ruby::RubyInterpreter(info); + } + catch(Kross::Api::Exception::Ptr e) { + Kross::krosswarning("krossinterpreter(Kross::Api::InterpreterInfo* info): Unhandled exception."); + } + return 0; + } +}; + + +namespace Kross { + +namespace Ruby { +typedef std::map<QString, VALUE> mStrVALUE; +typedef mStrVALUE::iterator mStrVALUE_it; +typedef mStrVALUE::const_iterator mStrVALUE_cit; +class RubyInterpreterPrivate { + friend class RubyInterpreter; +}; + +RubyInterpreterPrivate* RubyInterpreter::d = 0; + +RubyInterpreter::RubyInterpreter(Kross::Api::InterpreterInfo* info): Kross::Api::Interpreter(info) +{ +#ifdef KROSS_RUBY_INTERPRETER_DEBUG + krossdebug("RubyInterpreter::RubyInterpreter(info)"); +#endif + if(d == 0) + { + initRuby(); + } + if(info->hasOption("safelevel") ) + { + rb_set_safe_level( info->getOption("safelevel")->value.toInt() ); + } else { + rb_set_safe_level(4); // if the safelevel option is undefined, set it to maximum level + } +} + + +RubyInterpreter::~RubyInterpreter() +{ + finalizeRuby(); +} + + +Kross::Api::Script* RubyInterpreter::createScript(Kross::Api::ScriptContainer* scriptcontainer) +{ + return new RubyScript(this, scriptcontainer); +} + +void RubyInterpreter::initRuby() +{ + d = new RubyInterpreterPrivate(); + ruby_init(); + ruby_init_loadpath(); + rb_define_global_function("require", (VALUE (*)(...))RubyInterpreter::require, 1); +} + +void RubyInterpreter::finalizeRuby() +{ + delete d; + d = 0; + ruby_finalize(); +} + +VALUE RubyInterpreter::require (VALUE obj, VALUE name) +{ +#ifdef KROSS_RUBY_INTERPRETER_DEBUG + krossdebug("RubyInterpreter::require(obj,name)"); +#endif + QString modname = StringValuePtr(name); + if(modname.startsWith("kross")) { + krossdebug( QString("RubyInterpreter::require() module=%1").arg(modname) ); + if( modname.find( QRegExp("[^a-zA-Z0-9\\_\\-]") ) >= 0 ) { + krosswarning( QString("Denied import of Kross module '%1' cause of untrusted chars.").arg(modname) ); + } + else { + Kross::Api::Module::Ptr module = Kross::Api::Manager::scriptManager()->loadModule(modname); + if(module) + { + new RubyModule(module, modname); +// VALUE rmodule = rb_define_module(modname.ascii()); +// rb_define_module_function(); +// VALUE rm = RubyExtension::toVALUE(module); +// rb_define_variable( ("$" + modname).ascii(), & RubyInterpreter::d->m_modules.insert( mStrVALUE::value_type( modname, rm) ).first->second ); + return Qtrue; + } + krosswarning( QString("Loading of Kross module '%1' failed.").arg(modname) ); + } + } else { + return rb_f_require(obj, name); + } + return Qfalse; +} + +} + +} diff --git a/lib/kross/ruby/rubyinterpreter.h b/lib/kross/ruby/rubyinterpreter.h new file mode 100644 index 00000000..f16f2b3d --- /dev/null +++ b/lib/kross/ruby/rubyinterpreter.h @@ -0,0 +1,73 @@ +/*************************************************************************** + * rubyinterpreter.h + * This file is part of the KDE project + * copyright (C)2005 by Cyrille Berger (cberger@cberger.net) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * You should have received a copy of the GNU Library General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ***************************************************************************/ +#ifndef KROSS_RUBYRUBYINTERPRETER_H +#define KROSS_RUBYRUBYINTERPRETER_H + +#include <ruby.h> + +#include <api/interpreter.h> + +namespace Kross { + +namespace Ruby { + +class RubyInterpreterPrivate; +/** + * This class is the bridget between Kross and Ruby. + * @author Cyrille Berger + */ +class RubyInterpreter : public Kross::Api::Interpreter +{ + public: + + /** + * Constructor + * + * @param info The \a Kross::Api::InterpreterInfo instance + * that describes this \a RubyInterpreter . + */ + RubyInterpreter(Kross::Api::InterpreterInfo* info); + + /** + * Destructor. + */ + virtual ~RubyInterpreter(); + + /** + * Factory method to create and return a new \a RubyScript instance. + */ + virtual Kross::Api::Script* createScript(Kross::Api::ScriptContainer* scriptcontainer); + + private: + /// Initialize the ruby interpreter. + void initRuby(); + /// Finalize the ruby interpreter. + void finalizeRuby(); + /// Load an external plugin / module. + static VALUE require (VALUE, VALUE); + private: + /// Private d-pointer. + static RubyInterpreterPrivate* d; +}; + +} + +} + +#endif diff --git a/lib/kross/ruby/rubymodule.cpp b/lib/kross/ruby/rubymodule.cpp new file mode 100644 index 00000000..b002f24c --- /dev/null +++ b/lib/kross/ruby/rubymodule.cpp @@ -0,0 +1,68 @@ +/*************************************************************************** + * rubyinterpreter.cpp + * This file is part of the KDE project + * copyright (C)2005 by Cyrille Berger (cberger@cberger.net) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * You should have received a copy of the GNU Library General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ***************************************************************************/ + +#include "rubymodule.h" + +#include "rubyconfig.h" +#include "rubyextension.h" + +namespace Kross { + +namespace Ruby { + +class RubyModulePrivate { + friend class RubyModule; + /// The \a Kross::Api::Module this RubyExtension wraps. + Kross::Api::Module::Ptr m_module; + +}; + +RubyModule::RubyModule(Kross::Api::Module::Ptr mod, QString modname) : d(new RubyModulePrivate) +{ + d->m_module = mod; + modname = modname.left(1).upper() + modname.right(modname.length() - 1 ); + krossdebug(QString("Module: %1").arg(modname)); + VALUE rmodule = rb_define_module(modname.ascii()); + rb_define_module_function(rmodule,"method_missing", (VALUE (*)(...))RubyModule::method_missing, -1); + VALUE rm = RubyExtension::toVALUE( mod.data() ); + rb_define_const(rmodule, "MODULEOBJ", rm); +} + +RubyModule::~RubyModule() +{ +} + +VALUE RubyModule::method_missing(int argc, VALUE *argv, VALUE self) +{ +#ifdef KROSS_RUBY_MODULE_DEBUG + QString funcname = rb_id2name(SYM2ID(argv[0])); + krossdebug(QString("Function %1 missing in a module").arg(funcname)); +#endif + + VALUE rubyObjectModule = rb_funcall( self, rb_intern("const_get"), 1, ID2SYM(rb_intern("MODULEOBJ")) ); + RubyModule* objectModule; + Data_Get_Struct(rubyObjectModule, RubyModule, objectModule); + Kross::Api::Object::Ptr object = (Kross::Api::Object*)objectModule->d->m_module; + + return RubyExtension::call_method(object, argc, argv); +} + +} + +} diff --git a/lib/kross/ruby/rubymodule.h b/lib/kross/ruby/rubymodule.h new file mode 100644 index 00000000..0e4c278c --- /dev/null +++ b/lib/kross/ruby/rubymodule.h @@ -0,0 +1,73 @@ +/*************************************************************************** + * rubyinterpreter.cpp + * This file is part of the KDE project + * copyright (C)2005 by Cyrille Berger (cberger@cberger.net) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * You should have received a copy of the GNU Library General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ***************************************************************************/ + +#ifndef KROSS_RUBYRUBYMODULE_H +#define KROSS_RUBYRUBYMODULE_H + +#include <ruby.h> + +#include <qstring.h> + +#include <api/object.h> +#include <api/module.h> + +namespace Kross { + +namespace Ruby { + +class RubyModulePrivate; + +/** + * A ruby module. + * @author Cyrille Berger + */ +class RubyModule { + public: + + /** + * Constructor. + * + * @param mod The \a Kross::Api::Module this RubyExtension + * wraps. + * @param modname The name the module will be published as. + */ + RubyModule(Kross::Api::Module::Ptr mod, QString modname); + + /** + * Destructor. + */ + ~RubyModule(); + + private: + + /** + * This function will catch functions that are undefined. + */ + static VALUE method_missing(int argc, VALUE *argv, VALUE self); + + private: + /// Private d-pointer. + RubyModulePrivate* d; +}; + +} + +} + +#endif diff --git a/lib/kross/ruby/rubyscript.cpp b/lib/kross/ruby/rubyscript.cpp new file mode 100644 index 00000000..fa0ee1d0 --- /dev/null +++ b/lib/kross/ruby/rubyscript.cpp @@ -0,0 +1,193 @@ +/*************************************************************************** + * rubyscript.h + * This file is part of the KDE project + * copyright (C)2005 by Cyrille Berger (cberger@cberger.net) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * You should have received a copy of the GNU Library General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ***************************************************************************/ + +#include "rubyscript.h" + +#include <ruby.h> +#include <env.h> +#include <rubysig.h> +#include <node.h> + +#include <main/scriptcontainer.h> + +#include "rubyconfig.h" +#include "rubyextension.h" +#include "rubyinterpreter.h" + +extern NODE *ruby_eval_tree; + +namespace Kross { + +namespace Ruby { + +class RubyScriptPrivate { + friend class RubyScript; + RubyScriptPrivate() : m_compile(0) { } + RNode* m_compile; + /// A list of functionnames. + QStringList m_functions; + + /// A list of classnames. + QStringList m_classes; +}; + +RubyScript::RubyScript(Kross::Api::Interpreter* interpreter, Kross::Api::ScriptContainer* scriptcontainer) + : Kross::Api::Script(interpreter, scriptcontainer), d(new RubyScriptPrivate()) +{ +} + + +RubyScript::~RubyScript() +{ +} + +#define selectScript() \ + NODE* old_tree = ruby_eval_tree; \ + ruby_eval_tree = d->m_compile; +#define unselectScript() \ + d->m_compile = 0; \ + ruby_eval_tree = old_tree; + +void RubyScript::compile() +{ +#ifdef KROSS_RUBY_SCRIPT_DEBUG + krossdebug("RubyScript::compile()"); +#endif + int critical; + + ruby_nerrs = 0; + ruby_errinfo = Qnil; + VALUE src = RubyExtension::toVALUE( m_scriptcontainer->getCode() ); + StringValue(src); + critical = rb_thread_critical; + rb_thread_critical = Qtrue; + ruby_in_eval++; + d->m_compile = rb_compile_string((char*) m_scriptcontainer->getName().latin1(), src, 0); + ruby_in_eval--; + rb_thread_critical = critical; + + if (ruby_nerrs != 0) + { +#ifdef KROSS_RUBY_SCRIPT_DEBUG + krossdebug("Compilation has failed"); +#endif + setException( new Kross::Api::Exception(QString("Failed to compile ruby code: %1").arg(STR2CSTR( rb_obj_as_string(ruby_errinfo) )), 0) ); // TODO: get the error + d->m_compile = 0; + } +#ifdef KROSS_RUBY_SCRIPT_DEBUG + krossdebug("Compilation was successfull"); +#endif +} + +const QStringList& RubyScript::getFunctionNames() +{ +#ifdef KROSS_RUBY_SCRIPT_DEBUG + krossdebug("RubyScript::getFunctionNames()"); +#endif + if(d->m_compile == 0) + { + compile(); + } + return d->m_functions; +} + +Kross::Api::Object::Ptr RubyScript::execute() +{ +#ifdef KROSS_RUBY_SCRIPT_DEBUG + krossdebug("RubyScript::execute()"); +#endif + if(d->m_compile == 0) + { + compile(); + } +#ifdef KROSS_RUBY_SCRIPT_DEBUG + krossdebug("Start execution"); +#endif + selectScript(); + int result = ruby_exec(); + if (result != 0) + { +#ifdef KROSS_RUBY_SCRIPT_DEBUG + krossdebug("Execution has failed"); +#endif + if( TYPE( ruby_errinfo ) == T_DATA && RubyExtension::isOfExceptionType( ruby_errinfo ) ) + { +#ifdef KROSS_RUBY_SCRIPT_DEBUG + krossdebug("Kross exception"); +#endif + setException( RubyExtension::convertToException( ruby_errinfo ) ); + } else { + setException( new Kross::Api::Exception(QString("Failed to execute ruby code: %1").arg(STR2CSTR( rb_obj_as_string(ruby_errinfo) )), 0) ); // TODO: get the error + } + } + + unselectScript(); +#ifdef KROSS_RUBY_SCRIPT_DEBUG + krossdebug("Execution is finished"); +#endif + return 0; +} + +Kross::Api::Object::Ptr RubyScript::callFunction(const QString& name, Kross::Api::List::Ptr args) +{ + Q_UNUSED(name) + Q_UNUSED(args) +#ifdef KROSS_RUBY_SCRIPT_DEBUG + krossdebug("RubyScript::callFunction()"); +#endif + if(d->m_compile == 0) + { + compile(); + } + selectScript(); + unselectScript(); + return 0; +} + +const QStringList& RubyScript::getClassNames() +{ +#ifdef KROSS_RUBY_SCRIPT_DEBUG + krossdebug("RubyScript::getClassNames()"); +#endif + if(d->m_compile == 0) + { + compile(); + } + return d->m_classes; +} + +Kross::Api::Object::Ptr RubyScript::classInstance(const QString& name) +{ + Q_UNUSED(name) +#ifdef KROSS_RUBY_SCRIPT_DEBUG + krossdebug("RubyScript::classInstance()"); +#endif + if(d->m_compile == 0) + { + compile(); + } + selectScript(); + unselectScript(); + return 0; +} + + +} + +} diff --git a/lib/kross/ruby/rubyscript.h b/lib/kross/ruby/rubyscript.h new file mode 100644 index 00000000..cc6eda43 --- /dev/null +++ b/lib/kross/ruby/rubyscript.h @@ -0,0 +1,98 @@ +/*************************************************************************** + * rubyscript.h + * This file is part of the KDE project + * copyright (C)2005 by Cyrille Berger (cberger@cberger.net) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * You should have received a copy of the GNU Library General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + ***************************************************************************/ + +#ifndef KROSS_RUBYRUBYSCRIPT_H +#define KROSS_RUBYRUBYSCRIPT_H + +#include <api/script.h> + +namespace Kross { + +namespace Ruby { + +class RubyScriptPrivate; + +/** + * Handle ruby scripts. This class implements + * \a Kross::Api::Script for ruby. + * @author Cyrille Berger + */ +class RubyScript : public Kross::Api::Script +{ + public: + + /** + * Constructor. + * + * @param interpreter The @a RubyInterpreter instance used to + * create this script. + * @param scriptcontainer The @a Kross::Api::ScriptContainer + * instance this @a RubyScript does handle the + * backend-work for. + */ + RubyScript(Kross::Api::Interpreter* interpreter, Kross::Api::ScriptContainer* scriptcontainer); + + /** + * Destructor. + */ + ~RubyScript(); + + /** + * Return a list of callable functionnames this + * script spends. + */ + virtual const QStringList& getFunctionNames(); + + /** + * Execute the script. + */ + virtual Kross::Api::Object::Ptr execute(); + + /** + * Call a function. + */ + virtual Kross::Api::Object::Ptr callFunction(const QString& name, Kross::Api::List::Ptr args); + + /** + * Return a list of class types this script supports. + */ + virtual const QStringList& getClassNames(); + + /** + * Create and return a new class instance. + */ + virtual Kross::Api::Object::Ptr classInstance(const QString& name); + + private: + + /** + * Compile the script. + */ + void compile(); + + private: + /// Private d-pointer. + RubyScriptPrivate* d; +}; + +} + +} + +#endif |