1 /***************************************************************************
3 * This file is part of the KDE project
4 * copyright (C)2007 by Sebastian Sauer (mail@dipe.org)
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public License
15 * along with this program; see the file COPYING. If not, write to
16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 ***************************************************************************/
20 #ifndef KROSS_RUBYFUNCTION_H
21 #define KROSS_RUBYFUNCTION_H
23 #include "rubyconfig.h"
24 #include <kross/core/krossconfig.h>
25 #include <kross/core/manager.h>
26 #include <kross/core/metafunction.h>
30 #include <QMetaObject>
31 #include <QMetaMethod>
37 * The RubyFunction class implements a QObject to provide
38 * an adaptor between Qt signals+slots and ruby functions.
40 class RubyFunction
: public MetaFunction
47 * \param sender The QObject instance that sends the signal.
48 * \param signal The signature of the signal the QObject emits.
49 * \param method The callable ruby methods that should
50 * be executed if the QObject emits the signal.
52 RubyFunction(QObject
* sender
, const QByteArray
& signal
, VALUE method
)
53 : MetaFunction(sender
, signal
), m_method(method
)
55 #ifdef KROSS_RUBY_FUNCTION_CTORDTOR_DEBUG
56 m_debuginfo
= QString("sender=%1 signature=%2 method=%3")
57 .arg( sender
? QString("%1 (%2)").arg(sender
->objectName()).arg(sender
->metaObject()->className()) : "NULL" )
59 .arg( STR2CSTR(rb_inspect(method
)) );
60 krossdebug( QString("RubyFunction Ctor %1").arg(m_debuginfo
) );
62 rb_gc_register_address(&m_method
);
68 virtual ~RubyFunction()
70 #ifdef KROSS_RUBY_FUNCTION_CTORDTOR_DEBUG
71 krossdebug( QString("RubyFunction Dtor %1").arg(m_debuginfo
) );
73 rb_gc_unregister_address(&m_method
);
77 * This static function is called by Ruby if the callFunction bellow
78 * raises an exception.
80 static VALUE
callFunctionException(VALUE args
, VALUE error
)
82 //#ifdef KROSS_RUBY_FUNCTION_DEBUG
83 krossdebug( QString("RubyFunction callFunctionException args=%1 error=%2")
84 .arg( STR2CSTR(rb_inspect(args
)) ).arg( STR2CSTR(rb_inspect(error
)) ) );
89 VALUE info
= rb_gv_get("$!");
90 VALUE bt
= rb_funcall(info
, rb_intern("backtrace"), 0);
91 VALUE message
= RARRAY(bt
)->ptr
[0];
92 fprintf(stderr
,"%s: %s (%s)\n", STR2CSTR(message
), STR2CSTR(rb_obj_as_string(info
)), rb_class2name(CLASS_OF(info
)));
93 for(int i
= 1; i
< RARRAY(bt
)->len
; ++i
)
94 if( TYPE(RARRAY(bt
)->ptr
[i
]) == T_STRING
)
95 fprintf(stderr
,"\tfrom %s\n", STR2CSTR(RARRAY(bt
)->ptr
[i
]));
101 * This static function is called by Ruby to perform actions within
102 * a rescue block to provide with the callFunctionException function
103 * above an exception handler.
105 static VALUE
callFunction(VALUE args
)
107 #ifdef KROSS_RUBY_FUNCTION_DEBUG
108 krossdebug( QString("RubyFunction callFunction args=%1").arg(STR2CSTR(rb_inspect(args
))) );
110 Q_ASSERT( TYPE(args
) == T_ARRAY
);
111 VALUE self
= rb_ary_entry(args
, 0);
112 int argsize
= FIX2INT( rb_ary_entry(args
, 1) );
113 VALUE arguments
= rb_ary_entry(args
, 2);
114 VALUE
* argumentsP
= new VALUE
[argsize
];
115 for(int idx
= 0; idx
< argsize
; idx
++)
117 argumentsP
[idx
] = rb_ary_entry(arguments
, idx
+1);
119 //krossdebug(QString("RubyScript::callExecute script=%1").arg(STR2CSTR( rb_inspect(script) )));
120 //krossdebug(QString("RubyScript::callExecute fileName=%1").arg(STR2CSTR( rb_inspect(fileName) )));
121 //krossdebug(QString("RubyScript::callExecute src=%1").arg(STR2CSTR( rb_inspect(src) )));
122 VALUE result
= rb_funcall2(self
, rb_intern("call"), argsize
, argumentsP
);
128 * This method got called if a method this QObject instance
129 * defines should be invoked.
131 int qt_metacall(QMetaObject::Call _c
, int _id
, void **_a
)
133 _id
= QObject::qt_metacall(_c
, _id
, _a
);
134 #ifdef KROSS_RUBY_FUNCTION_DEBUG
135 //krossdebug(QString("RubyFunction::qt_metacall id=%1").arg(_id));
137 if(_id
>= 0 && _c
== QMetaObject::InvokeMetaMethod
) {
140 // convert the arguments
141 QMetaMethod method
= metaObject()->method( metaObject()->indexOfMethod(m_signature
) );
142 QList
<QByteArray
> params
= method
.parameterTypes();
143 const int argsize
= params
.size();
144 VALUE args
= rb_ary_new2(argsize
);
146 foreach(QByteArray param
, params
) {
147 int tp
= QVariant::nameToType( param
.constData() );
149 case QVariant::Invalid
: // fall through
150 case QVariant::UserType
: {
151 tp
= QMetaType::type( param
.constData() );
152 #ifdef KROSS_RUBY_FUNCTION_DEBUG
153 krossdebug( QString("RubyFunction::qt_metacall: metatypeId=%1").arg(tp
) );
156 case QMetaType::QObjectStar
: {
157 QObject
* obj
= (*reinterpret_cast< QObject
*(*)>( _a
[idx
] ));
158 rb_ary_store(args
, idx
, RubyExtension::toVALUE( new RubyExtension(obj
), true /*owner*/ ) );
160 case QMetaType::QWidgetStar
: {
161 QWidget
* obj
= (*reinterpret_cast< QWidget
*(*)>( _a
[idx
] ));
162 rb_ary_store(args
, idx
, RubyExtension::toVALUE( new RubyExtension(obj
), true /*owner*/ ) );
165 rb_ary_store(args
, idx
, Qnil
);
170 QVariant
v(tp
, _a
[idx
]);
172 if( ! Kross::Manager::self().strictTypesEnabled() ) {
173 if( v
.type() == QVariant::Invalid
&& QByteArray(param
.constData()).endsWith("*") ) {
174 QObject
* obj
= (*reinterpret_cast<QObject
*(*)>( _a
[idx
] ));
175 v
.setValue( (QObject
*) obj
);
179 #ifdef KROSS_RUBY_FUNCTION_DEBUG
180 krossdebug( QString("RubyFunction::qt_metacall argument param=%1 typeId=%2").arg(param
.constData()).arg(tp
) );
182 rb_ary_store(args
, idx
, RubyType
<QVariant
>::toVALUE(v
));
185 //rb_gc_register_address(&args[idx-1]);
189 /* makes no sense to init the stack here since we share one stack anyway and it's handled in the interpreter already
190 if (ruby_in_eval == 0) {
191 #ifdef RUBY_INIT_STACK
197 // call the ruby function
198 //VALUE result = rb_funcall2(m_method, rb_intern("call"), argsize, args);
202 VALUE argarray
= rb_ary_new2(3);
203 rb_ary_store(argarray
, 0, m_method
); //self
204 rb_ary_store(argarray
, 1, INT2FIX(argsize
));
205 rb_ary_store(argarray
, 2, args
);
206 VALUE result
= rb_rescue2((VALUE(*)(...))callFunction
, argarray
, (VALUE(*)(...))callFunctionException
, Qnil
, rb_eException
, 0);
209 // finally set the returnvalue
210 m_tmpResult
= RubyType
<QVariant
>::toVariant(result
);
211 #ifdef KROSS_RUBY_FUNCTION_DEBUG
212 QObject
* sender
= QObject::sender();
213 krossdebug( QString("RubyFunction::qt_metacall sender.objectName=%1 sender.className=%2 result=%3 variantresult=%4").arg(sender
->objectName()).arg(sender
->metaObject()->className()).arg(STR2CSTR(rb_inspect(result
))).arg(m_tmpResult
.toString()) );
215 //_a[0] = Kross::MetaTypeVariant<QVariant>(d->tmpResult).toVoidStar();
216 _a
[0] = &(m_tmpResult
);
218 //for(int i = 0; i < argsize; ++i) rb_gc_unregister_address(&args[i]);
219 #ifdef KROSS_RUBY_EXPLICIT_GC
230 /// The pointer to the Ruby method.
232 /// Dummy variable used to store the last result of a method call.
233 QVariant m_tmpResult
;
235 #ifdef KROSS_RUBY_FUNCTION_CTORDTOR_DEBUG
236 /// \internal string for debugging.