1 /***************************************************************************
3 * This file is part of the KDE project
4 * copyright (C)2006 by Cyrille Berger (cberger@cberger.net)
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 #include "rubycallcache.h"
21 #include "rubyvariant.h"
22 #include "rubyinterpreter.h"
24 #include <kross/core/manager.h>
25 #include <kross/core/metatype.h>
28 #include <QMetaObject>
29 #include <QMetaMethod>
33 struct RubyCallCachePrivate
35 RubyCallCachePrivate(QObject
* nobject
, int nmethodindex
, bool nhasreturnvalue
, QVarLengthArray
<int> ntypes
, QVarLengthArray
<int> nmetatypes
)
36 : object(nobject
), methodindex(nmethodindex
), hasreturnvalue(nhasreturnvalue
), types(ntypes
), metatypes(nmetatypes
)
41 QMetaMethod metamethod
;
43 QVarLengthArray
<int> types
, metatypes
;
44 static VALUE s_rccObject
;
46 #ifdef KROSS_RUBY_CALLCACHE_CTORDTOR_DEBUG
47 /// \internal string for debugging.
52 VALUE
RubyCallCachePrivate::s_rccObject
= 0;
54 RubyCallCache::RubyCallCache(QObject
* object
, int methodindex
, bool hasreturnvalue
, QVarLengthArray
<int> ntypes
, QVarLengthArray
<int> nmetatypes
)
55 : d(new RubyCallCachePrivate(object
, methodindex
, hasreturnvalue
, ntypes
, nmetatypes
)), m_self(0)
58 d
->metamethod
= d
->object
->metaObject()->method(d
->methodindex
);
59 #ifdef KROSS_RUBY_CALLCACHE_CTORDTOR_DEBUG
60 d
->debuginfo
= QString("name=%1 class=%2 methodindex=%3 signature=%4").arg(object
->objectName()).arg(object
->metaObject()->className()).arg(d
->methodindex
).arg(d
->metamethod
.signature());
61 krossdebug( QString("RubyCallCache Ctor %1 ").arg(d
->debuginfo
) );
65 RubyCallCache::~RubyCallCache()
67 #ifdef KROSS_RUBY_CALLCACHE_CTORDTOR_DEBUG
68 krossdebug( QString("RubyCallCache Dtor %1 ").arg(d
->debuginfo
) );
73 VALUE
RubyCallCache::execfunction( int argc
, VALUE
*argv
)
75 int typelistcount
= d
->types
.count();
76 QVarLengthArray
<MetaType
*> variantargs( typelistcount
);
77 QVarLengthArray
<void*> voidstarargs( typelistcount
);
79 #ifdef KROSS_RUBY_CALLCACHE_DEBUG
80 krossdebug( QString("RubyCallCache::execfunction signature=%1 typeName=%2 argc=%3 typelistcount=%4").arg(d
->metamethod
.signature()).arg(d
->metamethod
.typeName()).arg(argc
).arg(typelistcount
) );
81 for(int i
= 0; i
< d
->types
.count(); ++i
)
82 krossdebug( QString(" argument index=%1 typeId=%2 typeName=%3 metaTypeId=%4").arg(i
).arg(d
->types
[i
]).arg(QVariant::typeToName( (QVariant::Type
)d
->types
[i
] )).arg(d
->metatypes
[i
]) );
85 Q_ASSERT(argc
>= typelistcount
);
87 // set the return value
90 //krossdebug( QString("RubyCallCache::execfunction argv[1]=%1").arg(argc > 1 ? STR2CSTR(rb_inspect(argv[1])) : "") );
91 MetaType
* returntype
= RubyMetaTypeFactory::create( d
->metamethod
.typeName(), d
->types
[0], d
->metatypes
[0] );
92 //MetaType* returntype = RubyMetaTypeFactory::create( d->types[0], d->metatypes[0], argc > 1 ? argv[1] : Qnil );
93 variantargs
[0] = returntype
;
94 voidstarargs
[0] = returntype
->toVoidStar();
99 voidstarargs
[0] = (void*)0;
102 // set the arguments values
103 QList
<QByteArray
> typelist
= d
->metamethod
.parameterTypes();
104 for(int idx
= 1; idx
< typelistcount
; ++idx
)
106 #ifdef KROSS_RUBY_CALLCACHE_DEBUG
107 krossdebug( QString("RubyCallCache::execfunction param idx=%1 inspect=%2 QVariantType=%3 QMetaType=%4").arg(idx
).arg(STR2CSTR(rb_inspect(argv
[idx
]))).arg(QVariant::typeToName((QVariant::Type
)d
->types
[idx
])).arg(QMetaType::typeName(d
->metatypes
[idx
])) );
110 MetaType
* metatype
= RubyMetaTypeFactory::create( typelist
[idx
-1], d
->types
[idx
], d
->metatypes
[idx
], argv
[idx
] );
111 if(! metatype
) { // Seems RubyMetaTypeFactory::create returned an invalid RubyType.
112 krosswarning( QString("RubyCallCache::execfunction Aborting cause RubyMetaTypeFactory::create returned NULL.") );
113 for(int i
= 0; i
< idx
; ++i
) // Clear already allocated instances.
114 delete variantargs
[i
];
115 return Qfalse
; // abort execution.
117 variantargs
[idx
] = metatype
;
118 voidstarargs
[idx
] = metatype
->toVoidStar();
121 // call the method now
122 int r
= d
->object
->qt_metacall(QMetaObject::InvokeMetaMethod
, d
->methodindex
, &voidstarargs
[0]);
123 #ifdef KROSS_RUBY_CALLCACHE_DEBUG
124 krossdebug( QString("RESULT nr=%1").arg(r
) );
132 // eval the return-value
133 if(d
->hasreturnvalue
)
137 if( Kross::MetaTypeHandler
* handler
= Kross::Manager::self().metaTypeHandler(d
->metamethod
.typeName()) ) {
138 #ifdef KROSS_RUBY_CALLCACHE_DEBUG
139 krossdebug( QString("Returnvalue of type '%2' has a handler").arg(d
->metamethod
.typeName()) );
141 void *ptr
= (*reinterpret_cast<void*(*)>( variantargs
[0]->toVoidStar() ));
142 result
= handler
->callHandler(ptr
);
145 result
= QVariant(variantargs
[0]->typeId(), variantargs
[0]->toVoidStar());
147 if( ! Kross::Manager::self().strictTypesEnabled() ) {
148 if( result
.type() == QVariant::Invalid
&& QByteArray(d
->metamethod
.typeName()).endsWith("*") ) {
149 //#ifdef KROSS_RUBY_CALLCACHE_DEBUG
150 krossdebug( QString("Returnvalue of type '%2' will be reinterpret_cast<QObject*>").arg(d
->metamethod
.typeName()) );
152 QObject
* obj
= (*reinterpret_cast<QObject
*(*)>( variantargs
[0]->toVoidStar() ));
153 result
.setValue( (QObject
*) obj
);
158 #ifdef KROSS_RUBY_CALLCACHE_DEBUG
159 krossdebug( QString("RubyCallCache::execfunction Returnvalue typeId=%1 metamethod.typename=%2 variant.toString=%3 variant.typeName=%4").arg(variantargs
[0]->typeId()).arg(d
->metamethod
.typeName()).arg(result
.toString()).arg(result
.typeName()) );
162 // set the return value
163 retvalue
= RubyType
<QVariant
>::toVALUE(result
);
166 // free the return-value and the arguments
167 for(int idx
= 0; idx
< typelistcount
; ++idx
)
169 delete variantargs
[idx
];
175 void RubyCallCache::delete_object(void* object
)
177 #ifdef KROSS_RUBY_CALLCACHE_CTORDTOR_DEBUG
178 krossdebug("RubyCallCache::delete_object");
180 RubyCallCache
* callcache
= static_cast< RubyCallCache
* >(object
);
185 VALUE
RubyCallCache::method_cacheexec(int argc
, VALUE
*argv
, VALUE self
)
187 #ifdef KROSS_RUBY_CALLCACHE_DEBUG
188 krossdebug("RubyCallCache::method_cacheexec");
190 RubyCallCache
* callcache
;
191 Data_Get_Struct(self
, RubyCallCache
, callcache
);
192 return callcache
->execfunction(argc
, argv
);
195 VALUE
RubyCallCache::toValue()
199 if(RubyCallCachePrivate::s_rccObject
== 0)
201 RubyCallCachePrivate::s_rccObject
= rb_define_class_under(RubyInterpreter::krossModule(), "CallCache", rb_cObject
);
202 rb_define_method(RubyCallCachePrivate::s_rccObject
, "cacheexec", (VALUE (*)(...))RubyCallCache::method_cacheexec
, -1);
204 m_self
= Data_Wrap_Struct(RubyCallCachePrivate::s_rccObject
, 0, RubyCallCache::delete_object
, this);