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 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
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
00169 }
00170
00171 EcmaScript::~EcmaScript()
00172 {
00173
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("#!") )
00186 scriptCode.remove(0, scriptCode.indexOf('\n'));
00187
00188 const QString fileName = action()->file().isEmpty() ? action()->name() : action()->file();
00189
00190
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
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"