summaryrefslogtreecommitdiffstats
path: root/lib/kross/ruby
diff options
context:
space:
mode:
Diffstat (limited to 'lib/kross/ruby')
-rw-r--r--lib/kross/ruby/Makefile.am16
-rw-r--r--lib/kross/ruby/rubyconfig.h42
-rw-r--r--lib/kross/ruby/rubyextension.cpp378
-rw-r--r--lib/kross/ruby/rubyextension.h162
-rw-r--r--lib/kross/ruby/rubyinterpreter.cpp149
-rw-r--r--lib/kross/ruby/rubyinterpreter.h73
-rw-r--r--lib/kross/ruby/rubymodule.cpp68
-rw-r--r--lib/kross/ruby/rubymodule.h73
-rw-r--r--lib/kross/ruby/rubyscript.cpp193
-rw-r--r--lib/kross/ruby/rubyscript.h98
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