• Skip to content
  • Skip to link menu
KDE 4.2 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     // publish our own action and the manager
00195     d->publishObject(exec, "self", action());
00196     d->publishObject(exec, "Kross", &Manager::self());
00197 
00198     d->m_defaultFunctionNames = functionNames();
00199     d->m_defaultFunctionNames << "Kross";
00200 
00201     { // publish the global objects.
00202         QHash< QString, QObject* > objects = Manager::self().objects();
00203         QHash< QString, QObject* >::Iterator it(objects.begin()), end(objects.end());
00204         for(; it != end; ++it)
00205             d->publishObject(exec, it.key(), it.value());
00206     }
00207 
00208     { // publish the local objects.
00209         QHash< QString, QObject* > objects = action()->objects();
00210         QHash< QString, QObject* >::Iterator it(objects.begin()), end(objects.end());
00211         for(; it != end; ++it)
00212             d->publishObject(exec, it.key(), it.value());
00213     }
00214 
00215     /*
00216     { // some debugging
00217         krossdebug( QString("Global object") );
00218         KJS::JSObject* obj = kjsinterpreter->globalObject();
00219         KJS::ExecState* exec = kjsinterpreter->globalExec();
00220         KJS::PropertyNameArray props;
00221         obj->getPropertyNames(exec, props);
00222         for(KJS::PropertyNameArrayIterator it = props.begin(); it != props.end(); ++it)
00223             krossdebug( QString("  property name=%1").arg( it->ascii() ) );
00224     }
00225     */
00226 
00227     return true;
00228 }
00229 
00230 void KjsScript::finalize()
00231 {
00232     KJS::Interpreter* kjsinterpreter = d->m_engine->interpreter();
00233     KJS::ExecState* exec = kjsinterpreter->globalExec();
00234     Q_UNUSED(exec);
00235 
00236     QList< QPair<KJS::JSObject*, QPointer<QObject> > >::Iterator it( d->m_publishedObjects.begin() );
00237     QList< QPair<KJS::JSObject*, QPointer<QObject> > >::Iterator end( d->m_publishedObjects.end() );
00238     for(; it != end; ++it) {
00239         QObject* obj = (*it).second;
00240         if( ! obj )
00241             continue;
00242         KJS::JSObject* kjsobj = (*it).first;
00243         krossdebug(QString("KjsScript::finalize published object=%1").arg( kjsobj->className().ascii() ));
00244         /*
00245         KJSEmbed::QObjectBinding *imp = KJSEmbed::extractBindingImp<KJSEmbed::QObjectBinding>(exec, kjsobj);
00246         Q_ASSERT(imp);
00247         QObject* obj = imp->object<QObject>();
00248         Q_ASSERT(obj);
00249         */
00250 
00251         // try to remove all pending slotproxy's the dirty way... please note, that we can't
00252         // do it using findChildren since the slotproxy's are handcraftet QObject's and don't
00253         // implement all of the QObject functionality. Also it seems KjsEmbed does some wired
00254         // things with the slotproxy's what prevents us from doing it another more nicer way.
00255         foreach( QObject* child, obj->children() )
00256             if( KJSEmbed::SlotProxy* proxy = dynamic_cast< KJSEmbed::SlotProxy* >(child) )
00257                 delete proxy;
00258         //delete kjsobj; //don't delete since that will be done by kjs, right?
00259     }
00260     d->m_publishedObjects.clear();
00261 
00262     d->m_autoconnect.clear();
00263     d->m_defaultFunctionNames.clear();
00264 
00265     delete d->m_engine;
00266     d->m_engine = 0;
00267 }
00268 
00269 void KjsScript::execute()
00270 {
00271     if(! initialize()) {
00272         krosswarning( QString("KjsScript::execute aborted cause initialize failed.") );
00273         return;
00274     }
00275 
00276     QByteArray code = action()->code();
00277     if(code.startsWith("#!")) // remove optional shebang-line
00278         code.remove(0, code.indexOf('\n'));
00279 
00280     QTextCodec *codec = QTextCodec::codecForLocale();
00281     KJS::UString c = codec ? KJS::UString(codec->toUnicode(code)) : KJS::UString(code.data(), code.size());
00282     //krossdebug( QString("KjsScript::execute code=\n%1").arg(c.qstring()) );
00283     KJSEmbed::Engine::ExitStatus exitstatus = d->m_engine->execute(c);
00284 
00285     KJS::Completion completion = d->m_engine->completion();
00286     KJS::Interpreter* kjsinterpreter = d->m_engine->interpreter();
00287     KJS::ExecState* exec = kjsinterpreter->globalExec();
00288 
00289     if(exitstatus != KJSEmbed::Engine::Success) {
00290         ErrorInterface error = extractError(completion, exec);
00291         setError(&error);
00292         return;
00293     }
00294 
00295     KJS::JSObject* kjsglobal = kjsinterpreter->globalObject();
00296     if( exec->hadException() ) {
00297         ErrorInterface error = extractError(d->m_engine->completion(), exec);
00298         krossdebug(QString("KjsScript::execute() failed: %1").arg(error.errorMessage()));
00299         setError(&error);
00300         //exec->clearException();
00301         return;
00302     }
00303 
00304     foreach(QObject* object, d->m_autoconnect) {
00305         const QMetaObject* metaobject = object->metaObject();
00306         const int count = metaobject->methodCount();
00307         for(int i = 0; i < count; ++i) {
00308             QMetaMethod metamethod = metaobject->method(i);
00309             if( metamethod.methodType() == QMetaMethod::Signal ) {
00310                 const QString signature = metamethod.signature();
00311                 const QByteArray name = signature.left(signature.indexOf('(')).toLatin1();
00312                 krossdebug( QString("KjsScript::execute function=%1").arg(name.data()) );
00313 
00314                 KJS::Identifier id = KJS::Identifier( KJS::UString(name.data()) );
00315                 KJS::JSValue *functionvalue = kjsglobal->get(exec, id);
00316                 if( ! functionvalue->isObject() )
00317                     continue;
00318                 KJS::JSObject *function = functionvalue->toObject(exec);
00319                 Q_ASSERT( ! exec->hadException() );
00320                 if( exec->hadException() )
00321                     continue;
00322                 if ( function && function->implementsCall() ) {
00323                     krossdebug( QString("KjsScript::execute connect function=%1 with signal=%2").arg(name.data()).arg(signature) );
00324 
00325                     QByteArray sendersignal = QString("2%1").arg(signature).toLatin1();
00326                     QByteArray receiverslot = QString("1%1").arg(signature).toLatin1();
00327                     KJSEmbed::SlotProxy* receiver = new KJSEmbed::SlotProxy(kjsglobal, exec->dynamicInterpreter(), object, signature.toLatin1());
00328 
00329                     if( connect(object, sendersignal, receiver, receiverslot) ) {
00330                         krossdebug( QString("KjsScript::execute connected function=%1 with object=%2 signal=%3").arg(name.data()).arg(object->objectName()).arg(signature) );
00331                     }
00332                     else {
00333                         krosswarning( QString("KjsScript::execute failed to connect object=%1 signal=%2").arg(object->objectName()).arg(signature) );
00334                     }
00335 
00336                 }
00337             }
00338         }
00339 
00340     }
00341 }
00342 
00343 QStringList KjsScript::functionNames()
00344 {
00345     KJS::Interpreter* kjsinterpreter = d->m_engine->interpreter();
00346     KJS::ExecState* exec = kjsinterpreter->globalExec();
00347     KJS::JSObject* kjsglobal = kjsinterpreter->globalObject();
00348     if( exec->hadException() ) {
00349         return QStringList();
00350     }
00351 
00352     KJS::PropertyNameArray props;
00353     kjsglobal->getPropertyNames(exec, props);
00354 
00355     QStringList list;
00356     for(KJS::PropertyNameArrayIterator it = props.begin(); it != props.end(); ++it) {
00357         const char* name = it->ascii();
00358         KJS::Identifier id = KJS::Identifier(name);
00359         KJS::JSValue *value = kjsglobal->get(exec, id);
00360         if( ! value || ! value->isObject() )
00361             continue;
00362         KJS::JSObject *obj = value->toObject(exec);
00363         if( ! obj || ! obj->implementsCall() || ! obj->implementsConstruct() || ! obj->classInfo() )
00364             continue;
00365         if( d->m_defaultFunctionNames.contains(name) )
00366             continue;
00367         list << name;
00368     }
00369 
00370     Q_ASSERT( ! exec->hadException() );
00371     return list;
00372 }
00373 
00374 QVariant KjsScript::callFunction(const QString& name, const QVariantList& args)
00375 {
00376     //if( hadError() ) return QVariant(); // check if we had a prev error and abort if that's the case
00377 
00378     KJS::Interpreter* kjsinterpreter = d->m_engine->interpreter();
00379     KJS::ExecState* exec = kjsinterpreter->globalExec();
00380     KJS::JSObject* kjsglobal = kjsinterpreter->globalObject();
00381     if( exec->hadException() ) {
00382         ErrorInterface error = extractError(d->m_engine->completion(), exec);
00383         //setError(&error);
00384         krossdebug(QString("KjsScript::callFunction(\"%1\") Prev error: %2").arg(name).arg(error.errorMessage()));
00385         return QVariant();
00386     }
00387 
00388     KJS::Identifier id = KJS::Identifier( KJS::UString(name.toLatin1().data()) );
00389     KJS::JSValue *functionvalue = kjsglobal->get(exec, id);
00390     Q_ASSERT( ! exec->hadException() );
00391 
00392     KJS::JSObject *function = functionvalue->toObject(exec);
00393     if ( ! function || ! function->implementsCall() ) {
00394         krossdebug(QString("KjsScript::callFunction(\"%1\") No such function").arg(name));
00395         setError(QString("No such function \"%1\"").arg(name));
00396         return QVariant();
00397     }
00398 
00399     KJS::List kjsargs;
00400     foreach(const QVariant &variant, args) {
00401         if( qVariantCanConvert< QWidget* >(variant) ) {
00402             if( QWidget* widget = qvariant_cast< QWidget* >(variant) ) {
00403                 kjsargs.append( KJSEmbed::createQObject(exec, widget, KJSEmbed::ObjectBinding::QObjOwned) );
00404                 Q_ASSERT( ! exec->hadException() );
00405                 continue;
00406             }
00407         }
00408         if( qVariantCanConvert< QObject* >(variant) ) {
00409             if( QObject* obj = qvariant_cast< QObject* >(variant) ) {
00410                 kjsargs.append( KJSEmbed::createQObject(exec, obj, KJSEmbed::ObjectBinding::QObjOwned) );
00411                 Q_ASSERT( ! exec->hadException() );
00412                 continue;
00413             }
00414         }
00415         KJS::JSValue* jsvalue = KJSEmbed::convertToValue(exec, variant);
00416         Q_ASSERT( ! exec->hadException() );
00417         kjsargs.append( jsvalue );
00418     }
00419 
00420     KJS::JSValue *retValue = function->call(exec, kjsglobal, kjsargs);
00421     if( exec->hadException() ) {
00422         ErrorInterface error = extractError(d->m_engine->completion(), exec);
00423         //exec->clearException();
00424         krossdebug(QString("KjsScript::callFunction(\"%1\") Call failed: %2").arg(name).arg(error.errorMessage()));
00425         setError(&error);
00426         return QVariant();
00427     }
00428 
00429     QVariant result = retValue ? KJSEmbed::convertToVariant(exec, retValue) : QVariant();
00430     Q_ASSERT( ! exec->hadException() );
00431     return result;
00432 }
00433 
00434 QVariant KjsScript::evaluate(const QByteArray& code)
00435 {
00436     QTextCodec *codec = QTextCodec::codecForLocale();
00437     KJS::UString c = codec ? KJS::UString(codec->toUnicode(code)) : KJS::UString(code.data(), code.size());
00438 
00439     KJSEmbed::Engine::ExitStatus exitstatus = d->m_engine->execute(c);
00440 
00441     KJS::Completion completion = d->m_engine->completion();
00442     KJS::Interpreter* kjsinterpreter = d->m_engine->interpreter();
00443     KJS::ExecState* exec = kjsinterpreter->globalExec();
00444 
00445     if(exitstatus != KJSEmbed::Engine::Success) {
00446         ErrorInterface error = extractError(completion, exec);
00447         setError(&error);
00448         return QVariant();
00449     }
00450 
00451     KJS::JSValue *retValue = completion.value();
00452     QVariant result = retValue ? KJSEmbed::convertToVariant(exec, retValue) : QVariant();
00453     Q_ASSERT( ! exec->hadException() );
00454     return result;
00455 }

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
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • Kross
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.7
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