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

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
  • KPty
  • Kross
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.6.1
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