Kross
script.cpp
Go to the documentation of this file.
00001 /*************************************************************************** 00002 * script.cpp 00003 * This file is part of the KDE project 00004 * copyright (C)2007-2008 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 "script.h" 00021 00022 #include <QMetaObject> 00023 #include <QMetaMethod> 00024 #include <QScriptEngine> 00025 #include <QScriptValueIterator> 00026 00027 #include <kapplication.h> 00028 00029 using namespace Kross; 00030 00031 namespace Kross { 00032 00034 class EcmaScript::Private 00035 { 00036 public: 00037 EcmaScript* m_script; 00038 QScriptEngine* m_engine; 00039 QScriptValue m_kross; 00040 QScriptValue m_self; 00041 00042 explicit Private(EcmaScript* script) : m_script(script), m_engine(0) {} 00043 ~Private() { delete m_engine; } 00044 00045 bool init() { 00046 if( m_script->action()->hadError() ) 00047 m_script->action()->clearError(); 00048 00049 delete m_engine; 00050 m_engine = new QScriptEngine(); 00051 00052 // load the Kross QScriptExtensionPlugin plugin that provides 00053 // us a bridge between Kross and QtScript. See here plugin.h 00054 m_engine->importExtension("kross"); 00055 if( m_engine->hasUncaughtException() ) { 00056 handleException(); 00057 delete m_engine; 00058 m_engine = 0; 00059 return false; 00060 } 00061 00062 // the Kross QScriptExtensionPlugin exports the "Kross" property. 00063 QScriptValue global = m_engine->globalObject(); 00064 m_kross = global.property("Kross"); 00065 Q_ASSERT( m_kross.isQObject() ); 00066 Q_ASSERT( ! m_engine->hasUncaughtException() ); 00067 00068 // Attach our Kross::Action instance to be able to access it in 00069 // scripts. Just like at the Kjs-backend we publish our own 00070 // action as "self". 00071 m_self = m_engine->newQObject( m_script->action() ); 00072 global.setProperty("self", m_self, QScriptValue::ReadOnly|QScriptValue::Undeletable); 00073 00074 { // publish the global objects. 00075 QHash< QString, QObject* > objects = Manager::self().objects(); 00076 QHash< QString, QObject* >::Iterator it(objects.begin()), end(objects.end()); 00077 for(; it != end; ++it) 00078 global.setProperty(it.key(), m_engine->newQObject( it.value() ) ); 00079 } 00080 00081 { // publish the local objects. 00082 QHash< QString, QObject* > objects = m_script->action()->objects(); 00083 QHash< QString, QObject* >::Iterator it(objects.begin()), end(objects.end()); 00084 for(; it != end; ++it) { 00085 copyEnumsToProperties( it.value() ); 00086 global.setProperty(it.key(), m_engine->newQObject( it.value() ) ); 00087 } 00088 } 00089 00090 return ! m_engine->hasUncaughtException(); 00091 } 00092 00093 void copyEnumsToProperties(QObject* object) { 00094 const QMetaObject* meta = object->metaObject(); 00095 for (int i = 0; i < meta->enumeratorCount(); ++i) { 00096 QMetaEnum metaenum = meta->enumerator(i); 00097 for (int j = 0; j < metaenum.keyCount(); ++j) { 00098 object->setProperty(metaenum.key(j), metaenum.value(j)); 00099 } 00100 } 00101 } 00102 00103 void handleException() { 00104 Q_ASSERT( m_engine ); 00105 Q_ASSERT( m_engine->hasUncaughtException() ); 00106 const QString err = m_engine->uncaughtException().toString(); 00107 const int linenr = m_engine->uncaughtExceptionLineNumber(); 00108 const QString trace = m_engine->uncaughtExceptionBacktrace().join("\n"); 00109 krossdebug( QString("%1, line:%2, backtrace:\n%3").arg(err).arg(linenr).arg(trace) ); 00110 m_script->action()->setError(err, trace, linenr); 00111 m_engine->clearExceptions(); 00112 } 00113 00114 void addObject(QObject* object, const QString& name = QString()) { 00115 Q_ASSERT( m_engine ); 00116 Q_ASSERT( ! m_engine->hasUncaughtException() ); 00117 QScriptValue global = m_engine->globalObject(); 00118 QScriptValue value = m_engine->newQObject(object); 00119 global.setProperty(name.isEmpty() ? object->objectName() : name, value); 00120 } 00121 00122 void connectFunctions(ChildrenInterface* children) { 00123 Q_ASSERT( m_engine ); 00124 Q_ASSERT( ! m_engine->hasUncaughtException() ); 00125 QString eval; 00126 QScriptValue global = m_engine->globalObject(); 00127 QHashIterator< QString, ChildrenInterface::Options > it( children->objectOptions() ); 00128 while(it.hasNext()) { 00129 it.next(); 00130 if( it.value() & ChildrenInterface::AutoConnectSignals ) { 00131 QObject* sender = children->object(it.key()); 00132 if( ! sender ) 00133 continue; 00134 QScriptValue obj = m_engine->globalObject().property(it.key()); 00135 if( ! obj.isQObject() ) 00136 continue; 00137 const QMetaObject* mo = sender->metaObject(); 00138 const int count = mo->methodCount(); 00139 for(int i = 0; i < count; ++i) { 00140 QMetaMethod mm = mo->method(i); 00141 const QString signature = mm.signature(); 00142 const QString name = signature.left(signature.indexOf('(')); 00143 if( mm.methodType() == QMetaMethod::Signal ) { 00144 QScriptValue func = global.property(name); 00145 if( ! func.isFunction() ) { 00146 //krossdebug( QString("EcmaScript::connectFunctions No function to connect with %1.%2").arg(it.key()).arg(name) ); 00147 continue; 00148 } 00149 krossdebug( QString("EcmaScript::connectFunctions Connecting with %1.%2").arg(it.key()).arg(name) ); 00150 eval += QString("try { %1.%2.connect(%3); } catch(e) { print(e); }\n").arg(it.key()).arg(name).arg(name); 00151 } 00152 } 00153 } 00154 } 00155 Q_ASSERT( ! m_engine->hasUncaughtException() ); 00156 if( ! eval.isNull() ) { 00157 m_engine->evaluate(eval); 00158 Q_ASSERT( ! m_engine->hasUncaughtException() ); 00159 } 00160 } 00161 00162 }; 00163 00164 } 00165 00166 EcmaScript::EcmaScript(Interpreter* interpreter, Action* action) : Script(interpreter, action), d(new Private(this)) 00167 { 00168 //krossdebug( QString("EcmaScript::EcmaScript") ); 00169 } 00170 00171 EcmaScript::~EcmaScript() 00172 { 00173 //krossdebug( QString("EcmaScript::~EcmaScript") ); 00174 delete d; 00175 } 00176 00177 void EcmaScript::execute() 00178 { 00179 if( ! d->init() ) { 00180 d->handleException(); 00181 return; 00182 } 00183 00184 QString scriptCode = action()->code(); 00185 if( scriptCode.startsWith(QLatin1String("#!")) ) // remove optional shebang-line 00186 scriptCode.remove(0, scriptCode.indexOf('\n')); 00187 00188 const QString fileName = action()->file().isEmpty() ? action()->name() : action()->file(); 00189 00190 //krossdebug( QString("EcmaScript::execute fileName=%1 scriptCode=\n%2").arg(fileName).arg(scriptCode) ); 00191 00192 Q_ASSERT( d->m_engine ); 00193 00194 if( d->m_engine->hasUncaughtException() ) { 00195 d->m_engine->clearExceptions(); 00196 } 00197 00198 d->m_engine->evaluate( scriptCode, fileName ); 00199 00200 if( d->m_engine->hasUncaughtException() ) { 00201 d->handleException(); 00202 return; 00203 } 00204 00205 //d->connectFunctions( &Manager::self() ); 00206 d->connectFunctions( action() ); 00207 } 00208 00209 QStringList EcmaScript::functionNames() 00210 { 00211 if( ! d->m_engine && ! d->init() ) { 00212 d->handleException(); 00213 return QStringList(); 00214 } 00215 QStringList names; 00216 QScriptValueIterator it( d->m_engine->globalObject() ); 00217 while( it.hasNext() ) { 00218 it.next(); 00219 if( it.value().isFunction() ) { 00220 names << it.name(); 00221 } 00222 } 00223 return names; 00224 } 00225 00226 QVariant EcmaScript::callFunction(const QString& name, const QVariantList& args) 00227 { 00228 if( ! d->m_engine && ! d->init() ) { 00229 d->handleException(); 00230 return QVariant(); 00231 } 00232 00233 QScriptValue obj = d->m_engine->globalObject(); 00234 QScriptValue function = obj.property(name); 00235 if( ! function.isFunction() ) { 00236 QString err = QString("No such function '%1'").arg(name); 00237 krosswarning( QString("EcmaScript::callFunction %1").arg(err) ); 00238 setError(err); 00239 return QVariant(); 00240 } 00241 00242 QScriptValueList arguments; 00243 foreach(const QVariant &v, args) 00244 arguments << d->m_engine->toScriptValue(v); 00245 QScriptValue result = function.call(obj, arguments); 00246 if( d->m_engine->hasUncaughtException() ) { 00247 d->handleException(); 00248 return QVariant(); 00249 } 00250 return result.toVariant(); 00251 } 00252 00253 QVariant EcmaScript::evaluate(const QByteArray& code) 00254 { 00255 if( ! d->m_engine && ! d->init() ) { 00256 d->handleException(); 00257 return QVariant(); 00258 } 00259 00260 QScriptValue result = d->m_engine->evaluate(code); 00261 if( d->m_engine->hasUncaughtException() ) { 00262 d->handleException(); 00263 return QVariant(); 00264 } 00265 return result.toVariant(); 00266 } 00267 00268 QObject* EcmaScript::engine() const 00269 { 00270 return d->m_engine; 00271 } 00272 00273 #include "script.moc"
KDE 4.6 API Reference