• Skip to content
  • Skip to link menu
KDE 4.0 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • Contact Us
 

Kross

kjsscript.cpp

Go to the documentation of this file.
00001 /***************************************************************************
00002  * kjsscript.cpp
00003  * This file is part of the KDE project
00004  * copyright (C)2004-2006 by Sebastian Sauer (mail@dipe.org)
00005  *
00006  * This program is free software; you can redistribute it and/or
00007  * modify it under the terms of the GNU Library General Public
00008  * License as published by the Free Software Foundation; either
00009  * version 2 of the License, or (at your option) any later version.
00010  * This program is distributed in the hope that it will be useful,
00011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013  * Library General Public License for more details.
00014  * You should have received a copy of the GNU Library General Public License
00015  * along with this program; see the file COPYING.  If not, write to
00016  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00017  * Boston, MA 02110-1301, USA.
00018  ***************************************************************************/
00019 
00020 #include "kjsscript.h"
00021 #include "../core/action.h"
00022 #include "../core/manager.h"
00023 #include "../core/interpreter.h"
00024 
00025 // for Kjs
00026 #include <kjs/interpreter.h>
00027 #include <kjs/ustring.h>
00028 #include <kjs/object.h>
00029 #include <kjs/PropertyNameArray.h>
00030 //#include <kjs/array_instance.h>
00031 #include <kjs/function_object.h>
00032 
00033 // for KjsEmbed
00034 #include <kjsembed/kjsembed.h>
00035 #include <kjsembed/qobject_binding.h>
00036 #include <kjsembed/variant_binding.h>
00037 #include <kjsembed/slotproxy.h>
00038 
00039 #include <QMetaObject>
00040 #include <QMetaMethod>
00041 #include <QPointer>
00042 #include <QTextCodec>
00043 
00044 using namespace Kross;
00045 
00046 namespace Kross {
00047 
00049     static ErrorInterface extractError(const KJS::Completion& completion, KJS::ExecState* exec)
00050     {
00051         QString type;
00052         switch( completion.complType() ) {
00053             case KJS::Normal: type = "Normal"; break;
00054             case KJS::Break: type = "Break"; break;
00055             case KJS::Continue: type = "Continue"; break;
00056             case KJS::ReturnValue: type = "ReturnValue"; break;
00057             case KJS::Throw: {
00058                 type = "Throw";
00059             } break;
00060             case KJS::Interrupted: type = "Interrupted"; break;
00061             default: type = "Unknown"; break;
00062         }
00063 
00064         KJS::JSValue* value = completion.value();
00065         int lineno = -1;
00066         if( value && value->type() == KJS::ObjectType ) {
00067             KJS::JSValue* linevalue = value->getObject()->get(exec, "line");
00068             if( linevalue && linevalue->type() == KJS::NumberType )
00069                 lineno = linevalue->toInt32(exec);
00070         }
00071         const QString message = QString("%1%2: %3").arg( type ).arg((lineno >= 0) ? QString(" line %1").arg(lineno) : "").arg(value ? value->toString(exec).qstring() : "NULL");
00072 
00073         ErrorInterface err;
00074         err.setError(message, QString(), lineno);
00075         return err;
00076     }
00077 
00079     class KjsScriptPrivate
00080     {
00081         public:
00085             KJSEmbed::Engine* m_engine;
00086 
00090             QList< QPair<KJS::JSObject*, QPointer<QObject> > > m_publishedObjects;
00091 
00097             QList< QObject* > m_autoconnect;
00098 
00102             QStringList m_defaultFunctionNames;
00103 
00110             void addFunctions(ChildrenInterface* children)
00111             {
00112                 QHashIterator< QString, ChildrenInterface::Options > it( children->objectOptions() );
00113                 while(it.hasNext()) {
00114                     it.next();
00115                     if( it.value() & ChildrenInterface::AutoConnectSignals ) {
00116                         QObject* sender = children->object( it.key() );
00117                         if( sender ) {
00118                             krossdebug( QString("KjsScript::addFunctions sender name=%1 className=%2").arg(sender->objectName()).arg(sender->metaObject()->className()) );
00119                             m_autoconnect.append( sender );
00120                         }
00121                     }
00122                 }
00123             }
00124 
00126             bool publishObject(KJS::ExecState* exec, const QString &name, QObject* object)
00127             {
00128                 Q_UNUSED(exec);
00129 
00130                 KJS::JSObject* obj = m_engine->addObject(object, name.isEmpty() ? object->objectName() : name);
00131                 if( ! obj ) {
00132                     krosswarning( QString("Failed to publish the QObject name=\"%1\" objectName=\"%2\"").arg(name).arg(object ? object->objectName() : "NULL") );
00133                     return false;
00134                 }
00135                 m_publishedObjects << QPair<KJS::JSObject*, QPointer<QObject> >(obj, object);
00136 
00137                 /*
00138                 bool restricted = interpreter()->interpreterInfo()->optionValue("restricted", true).toBool();
00139                 if( restricted ) {
00140                     KJSEmbed::QObjectBinding* objImp = KJSEmbed::extractBindingImp<KJSEmbed::QObjectBinding>(exec, obj);
00141                     objImp->setAccess(
00142                         KJSEmbed::QObjectBinding::ScriptableSlots |
00143                         KJSEmbed::QObjectBinding::NonScriptableSlots |
00144                         KJSEmbed::QObjectBinding::PublicSlots |
00145                         KJSEmbed::QObjectBinding::ScriptableSignals |
00146                         KJSEmbed::QObjectBinding::NonScriptableSignals |
00147                         KJSEmbed::QObjectBinding::PublicSignals |
00148                         KJSEmbed::QObjectBinding::ScriptableProperties |
00149                         KJSEmbed::QObjectBinding::NonScriptableProperties |
00150                         KJSEmbed::QObjectBinding::GetParentObject |
00151                         KJSEmbed::QObjectBinding::ChildObjects
00152                     );
00153                 }
00154                 */
00155                 return true;
00156             }
00157 
00158     };
00159 
00160 }
00161 
00162 KjsScript::KjsScript(Interpreter* interpreter, Action* action)
00163     : Script(interpreter, action)
00164     , d(new KjsScriptPrivate())
00165 {
00166     krossdebug( QString("KjsScript::KjsScript") );
00167     d->m_engine = 0;
00168 
00169     d->addFunctions( &Manager::self() );
00170     d->addFunctions( action );
00171 }
00172 
00173 KjsScript::~KjsScript()
00174 {
00175     krossdebug( QString("KjsScript::~KjsScript") );
00176     finalize();
00177     delete d;
00178 }
00179 
00180 bool KjsScript::initialize()
00181 {
00182     if( d->m_engine )
00183         finalize(); // finalize before initialize
00184     clearError(); // clear previous errors.
00185 
00186     krossdebug( QString("KjsScript::initialize") );
00187 
00188     d->m_engine = new KJSEmbed::Engine();
00189 
00190     KJS::Interpreter* kjsinterpreter = d->m_engine->interpreter();
00191     kjsinterpreter->setShouldPrintExceptions(true);
00192     KJS::ExecState* exec = kjsinterpreter->globalExec();
00193 
00194     d->m_defaultFunctionNames = functionNames();
00195     d->m_defaultFunctionNames << "Kross";
00196 
00197     { // publish the global objects.
00198         QHash< QString, QObject* > objects = Manager::self().objects();
00199         QHash< QString, QObject* >::Iterator it(objects.begin()), end(objects.end());
00200         for(; it != end; ++it)
00201             d->publishObject(exec, it.key(), it.value());
00202     }
00203 
00204     { // publish the local objects.
00205         QHash< QString, QObject* > objects = action()->objects();
00206         QHash< QString, QObject* >::Iterator it(objects.begin()), end(objects.end());
00207         for(; it != end; ++it)
00208             d->publishObject(exec, it.key(), it.value());
00209     }
00210 
00211     /*
00212     { // some debugging
00213         krossdebug( QString("Global object") );
00214         KJS::JSObject* obj = kjsinterpreter->globalObject();
00215         KJS::ExecState* exec = kjsinterpreter->globalExec();
00216         KJS::PropertyNameArray props;
00217         obj->getPropertyNames(exec, props);
00218         for(KJS::PropertyNameArrayIterator it = props.begin(); it != props.end(); ++it)
00219             krossdebug( QString("  property name=%1").arg( it->ascii() ) );
00220     }
00221     */
00222 
00223     return true;
00224 }
00225 
00226 void KjsScript::finalize()
00227 {
00228     KJS::Interpreter* kjsinterpreter = d->m_engine->interpreter();
00229     KJS::ExecState* exec = kjsinterpreter->globalExec();
00230 
00231     QList< QPair<KJS::JSObject*, QPointer<QObject> > >::Iterator it( d->m_publishedObjects.begin() );
00232     QList< QPair<KJS::JSObject*, QPointer<QObject> > >::Iterator end( d->m_publishedObjects.end() );
00233     for(; it != end; ++it) {
00234         QObject* obj = (*it).second;
00235         if( ! obj )
00236             continue;
00237         KJS::JSObject* kjsobj = (*it).first;
00238         krossdebug(QString("KjsScript::finalize published object=%1").arg( kjsobj->className().ascii() ));
00239         /*
00240         KJSEmbed::QObjectBinding *imp = KJSEmbed::extractBindingImp<KJSEmbed::QObjectBinding>(exec, kjsobj);
00241         Q_ASSERT(imp);
00242         QObject* obj = imp->object<QObject>();
00243         Q_ASSERT(obj);
00244         */
00245 
00246         // try to remove all pending slotproxy's the dirty way... please note, that we can't
00247         // do it using findChildren since the slotproxy's are handcraftet QObject's and don't
00248         // implement all of the QObject functionality. Also it seems KjsEmbed does some wired
00249         // things with the slotproxy's what prevents us from doing it another more nicer way.
00250         foreach( QObject* child, obj->children() )
00251             if( KJSEmbed::SlotProxy* proxy = dynamic_cast< KJSEmbed::SlotProxy* >(child) )
00252                 delete proxy;
00253         //delete kjsobj; //don't delete since that will be done by kjs, right?
00254     }
00255     d->m_publishedObjects.clear();
00256 
00257     d->m_autoconnect.clear();
00258     d->m_defaultFunctionNames.clear();
00259 
00260     delete d->m_engine;
00261     d->m_engine = 0;
00262 }
00263 
00264 void KjsScript::execute()
00265 {
00266     if(! initialize()) {
00267         krosswarning( QString("KjsScript::execute aborted cause initialize failed.") );
00268         return;
00269     }
00270 
00271     QByteArray code = action()->code();
00272     if(code.startsWith("#!")) // remove optional shebang-line
00273         code.remove(0, code.indexOf('\n'));
00274 
00275     QTextCodec *codec = QTextCodec::codecForLocale();
00276     KJS::UString c = codec ? KJS::UString(codec->toUnicode(code)) : KJS::UString(code.data(), code.size());
00277     //krossdebug( QString("KjsScript::execute code=\n%1").arg(c.qstring()) );
00278     KJSEmbed::Engine::ExitStatus exitstatus = d->m_engine->execute(c);
00279 
00280     KJS::Completion completion = d->m_engine->completion();
00281     KJS::Interpreter* kjsinterpreter = d->m_engine->interpreter();
00282     KJS::ExecState* exec = kjsinterpreter->globalExec();
00283 
00284     if(exitstatus != KJSEmbed::Engine::Success) {
00285         ErrorInterface error = extractError(completion, exec);
00286         setError(&error);
00287         return;
00288     }
00289 
00290     KJS::JSObject* kjsglobal = kjsinterpreter->globalObject();
00291     Q_ASSERT( ! exec->hadException() );
00292 
00293     foreach(QObject* object, d->m_autoconnect) {
00294         const QMetaObject* metaobject = object->metaObject();
00295         const int count = metaobject->methodCount();
00296         for(int i = 0; i < count; ++i) {
00297             QMetaMethod metamethod = metaobject->method(i);
00298             if( metamethod.methodType() == QMetaMethod::Signal ) {
00299                 const QString signature = metamethod.signature();
00300                 const QByteArray name = signature.left(signature.indexOf('(')).toLatin1();
00301                 krossdebug( QString("KjsScript::execute function=%1").arg(name.data()) );
00302 
00303                 KJS::Identifier id = KJS::Identifier( KJS::UString(name.data()) );
00304                 KJS::JSValue *functionvalue = kjsglobal->get(exec, id);
00305                 if( ! functionvalue->isObject() )
00306                     continue;
00307                 KJS::JSObject *function = functionvalue->toObject(exec);
00308                 Q_ASSERT( ! exec->hadException() );
00309                 if( exec->hadException() )
00310                     continue;
00311                 if ( function && function->implementsCall() ) {
00312                     krossdebug( QString("KjsScript::execute connect function=%1 with signal=%2").arg(name.data()).arg(signature) );
00313 
00314                     QByteArray sendersignal = QString("2%1").arg(signature).toLatin1();
00315                     QByteArray receiverslot = QString("1%1").arg(signature).toLatin1();
00316                     KJSEmbed::SlotProxy* receiver = new KJSEmbed::SlotProxy(kjsglobal, exec->dynamicInterpreter(), object, signature.toLatin1());
00317 
00318                     if( connect(object, sendersignal, receiver, receiverslot) ) {
00319                         krossdebug( QString("KjsScript::execute connected function=%1 with object=%2 signal=%3").arg(name.data()).arg(object->objectName()).arg(signature) );
00320                     }
00321                     else {
00322                         krosswarning( QString("KjsScript::execute failed to connect object=%1 signal=%2").arg(object->objectName()).arg(signature) );
00323                     }
00324 
00325                 }
00326             }
00327         }
00328 
00329     }
00330 }
00331 
00332 QStringList KjsScript::functionNames()
00333 {
00334     KJS::Interpreter* kjsinterpreter = d->m_engine->interpreter();
00335     KJS::ExecState* exec = kjsinterpreter->globalExec();
00336     KJS::JSObject* kjsglobal = kjsinterpreter->globalObject();
00337     if( exec->hadException() ) {
00338         return QStringList();
00339     }
00340 
00341     KJS::PropertyNameArray props;
00342     kjsglobal->getPropertyNames(exec, props);
00343 
00344     QStringList list;
00345     for(KJS::PropertyNameArrayIterator it = props.begin(); it != props.end(); ++it) {
00346         const char* name = it->ascii();
00347         KJS::Identifier id = KJS::Identifier(name);
00348         KJS::JSValue *value = kjsglobal->get(exec, id);
00349         if( ! value || ! value->isObject() )
00350             continue;
00351         KJS::JSObject *obj = value->toObject(exec);
00352         if( ! obj || ! obj->implementsCall() || ! obj->implementsConstruct() || ! obj->classInfo() )
00353             continue;
00354         if( d->m_defaultFunctionNames.contains(name) )
00355             continue;
00356         list << name;
00357     }
00358 
00359     Q_ASSERT( ! exec->hadException() );
00360     return list;
00361 }
00362 
00363 QVariant KjsScript::callFunction(const QString& name, const QVariantList& args)
00364 {
00365     //if( hadError() ) return QVariant(); // check if we had a prev error and abort if that's the case
00366 
00367     KJS::Interpreter* kjsinterpreter = d->m_engine->interpreter();
00368     KJS::ExecState* exec = kjsinterpreter->globalExec();
00369     KJS::JSObject* kjsglobal = kjsinterpreter->globalObject();
00370     if( exec->hadException() ) {
00371         ErrorInterface error = extractError(d->m_engine->completion(), exec);
00372         //setError(&error);
00373         krossdebug(QString("KjsScript::callFunction(\"%1\") Prev error: %2").arg(name).arg(error.errorMessage()));
00374         return QVariant();
00375     }
00376 
00377     KJS::Identifier id = KJS::Identifier( KJS::UString(name.toLatin1().data()) );
00378     KJS::JSValue *functionvalue = kjsglobal->get(exec, id);
00379     Q_ASSERT( ! exec->hadException() );
00380 
00381     KJS::JSObject *function = functionvalue->toObject(exec);
00382     if ( ! function || ! function->implementsCall() ) {
00383         krossdebug(QString("KjsScript::callFunction(\"%1\") No such function").arg(name));
00384         setError(QString("No such function \"%1\"").arg(name));
00385         return QVariant();
00386     }
00387 
00388     KJS::List kjsargs;
00389     foreach(QVariant variant, args) {
00390         if( qVariantCanConvert< QWidget* >(variant) ) {
00391             if( QWidget* widget = qvariant_cast< QWidget* >(variant) ) {
00392                 kjsargs.append( KJSEmbed::createQObject(exec, widget, KJSEmbed::ObjectBinding::QObjOwned) );
00393                 Q_ASSERT( ! exec->hadException() );
00394                 continue;
00395             }
00396         }
00397         if( qVariantCanConvert< QObject* >(variant) ) {
00398             if( QObject* obj = qvariant_cast< QObject* >(variant) ) {
00399                 kjsargs.append( KJSEmbed::createQObject(exec, obj, KJSEmbed::ObjectBinding::QObjOwned) );
00400                 Q_ASSERT( ! exec->hadException() );
00401                 continue;
00402             }
00403         }
00404         KJS::JSValue* jsvalue = KJSEmbed::convertToValue(exec, variant);
00405         Q_ASSERT( ! exec->hadException() );
00406         kjsargs.append( jsvalue );
00407     }
00408 
00409     KJS::JSValue *retValue = function->call(exec, kjsglobal, kjsargs);
00410     if( exec->hadException() ) {
00411         ErrorInterface error = extractError(d->m_engine->completion(), exec);
00412         //exec->clearException();
00413         krossdebug(QString("KjsScript::callFunction(\"%1\") Call failed: %2").arg(name).arg(error.errorMessage()));
00414         setError(&error);
00415         return QVariant();
00416     }
00417 
00418     QVariant result = KJSEmbed::convertToVariant(exec, retValue);
00419     Q_ASSERT( ! exec->hadException() );
00420     return result;
00421 }
00422 

Kross

Skip menu "Kross"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • KIO
  • KIOSlave
  • KJS
  •   WTF
  • KJSEmbed
  • KNewStuff
  • KParts
  • Kross
  • KUtils
  • Nepomuk
  •   core
  • Phonon
  •   Backend
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.4
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal