Kross
script.cpp
Go to the documentation of this file.00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
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
00053
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
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
00069
00070
00071 m_self = m_engine->newQObject( m_script->action() );
00072 global.setProperty("self", m_self, QScriptValue::ReadOnly|QScriptValue::Undeletable);
00073
00074 {
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 {
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 global.setProperty(it.key(), m_engine->newQObject( it.value() ) );
00086 }
00087
00088 return ! m_engine->hasUncaughtException();
00089 }
00090
00091 void handleException() {
00092 Q_ASSERT( m_engine );
00093 Q_ASSERT( m_engine->hasUncaughtException() );
00094 const QString err = m_engine->uncaughtException().toString();
00095 const int linenr = m_engine->uncaughtExceptionLineNumber();
00096 const QString trace = m_engine->uncaughtExceptionBacktrace().join("\n");
00097 krossdebug( QString("%1, line:%2, backtrace:\n%3").arg(err).arg(linenr).arg(trace) );
00098 m_script->action()->setError(err, trace, linenr);
00099 m_engine->clearExceptions();
00100 }
00101
00102 void addObject(QObject* object, const QString& name = QString()) {
00103 Q_ASSERT( m_engine );
00104 Q_ASSERT( ! m_engine->hasUncaughtException() );
00105 QScriptValue global = m_engine->globalObject();
00106 QScriptValue value = m_engine->newQObject(object);
00107 global.setProperty(name.isEmpty() ? object->objectName() : name, value);
00108 }
00109
00110 void connectFunctions(ChildrenInterface* children) {
00111 Q_ASSERT( m_engine );
00112 Q_ASSERT( ! m_engine->hasUncaughtException() );
00113 QString eval;
00114 QScriptValue global = m_engine->globalObject();
00115 QHashIterator< QString, ChildrenInterface::Options > it( children->objectOptions() );
00116 while(it.hasNext()) {
00117 it.next();
00118 if( it.value() & ChildrenInterface::AutoConnectSignals ) {
00119 QObject* sender = children->object(it.key());
00120 if( ! sender )
00121 continue;
00122 QScriptValue obj = m_engine->globalObject().property(it.key());
00123 if( ! obj.isQObject() )
00124 continue;
00125 const QMetaObject* mo = sender->metaObject();
00126 const int count = mo->methodCount();
00127 for(int i = 0; i < count; ++i) {
00128 QMetaMethod mm = mo->method(i);
00129 const QString signature = mm.signature();
00130 const QString name = signature.left(signature.indexOf('('));
00131 if( mm.methodType() == QMetaMethod::Signal ) {
00132 QScriptValue func = global.property(name);
00133 if( ! func.isFunction() ) {
00134
00135 continue;
00136 }
00137 krossdebug( QString("EcmaScript::connectFunctions Connecting with %1.%2").arg(it.key()).arg(name) );
00138 eval += QString("try { %1.%2.connect(%3); } catch(e) { print(e); }\n").arg(it.key()).arg(name).arg(name);
00139 }
00140 }
00141 }
00142 }
00143 Q_ASSERT( ! m_engine->hasUncaughtException() );
00144 if( ! eval.isNull() ) {
00145 m_engine->evaluate(eval);
00146 Q_ASSERT( ! m_engine->hasUncaughtException() );
00147 }
00148 }
00149
00150 };
00151
00152 }
00153
00154 EcmaScript::EcmaScript(Interpreter* interpreter, Action* action) : Script(interpreter, action), d(new Private(this))
00155 {
00156
00157 }
00158
00159 EcmaScript::~EcmaScript()
00160 {
00161
00162 delete d;
00163 }
00164
00165 void EcmaScript::execute()
00166 {
00167 if( ! d->init() ) {
00168 d->handleException();
00169 return;
00170 }
00171
00172 QString scriptCode = action()->code();
00173 if( scriptCode.startsWith("#!") )
00174 scriptCode.remove(0, scriptCode.indexOf('\n'));
00175
00176 const QString fileName = action()->file().isEmpty() ? action()->name() : action()->file();
00177
00178
00179
00180 Q_ASSERT( d->m_engine );
00181
00182 if( d->m_engine->hasUncaughtException() ) {
00183 d->m_engine->clearExceptions();
00184 }
00185
00186 d->m_engine->evaluate( scriptCode, fileName );
00187
00188 if( d->m_engine->hasUncaughtException() ) {
00189 d->handleException();
00190 return;
00191 }
00192
00193
00194 d->connectFunctions( action() );
00195 }
00196
00197 QStringList EcmaScript::functionNames()
00198 {
00199 if( ! d->m_engine && ! d->init() ) {
00200 d->handleException();
00201 return QStringList();
00202 }
00203 QStringList names;
00204 QScriptValueIterator it( d->m_engine->globalObject() );
00205 while( it.hasNext() ) {
00206 it.next();
00207 if( it.value().isFunction() ) {
00208 names << it.name();
00209 }
00210 }
00211 return names;
00212 }
00213
00214 QVariant EcmaScript::callFunction(const QString& name, const QVariantList& args)
00215 {
00216 if( ! d->m_engine && ! d->init() ) {
00217 d->handleException();
00218 return QVariant();
00219 }
00220
00221 QScriptValue obj = d->m_engine->globalObject();
00222 QScriptValue function = obj.property(name);
00223 if( ! function.isFunction() ) {
00224 QString err = QString("No such function '%1'").arg(name);
00225 krosswarning( QString("EcmaScript::callFunction %1").arg(err) );
00226 setError(err);
00227 return QVariant();
00228 }
00229
00230 QScriptValueList arguments;
00231 foreach(const QVariant &v, args)
00232 arguments << d->m_engine->newVariant(v);
00233 QScriptValue result = function.call(obj, arguments);
00234 return result.toVariant();
00235 }
00236
00237 QObject* EcmaScript::engine() const
00238 {
00239 return d->m_engine;
00240 }
00241
00242 #include "script.moc"