00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include "kjsscript.h"
00021 #include "../core/action.h"
00022 #include "../core/manager.h"
00023 #include "../core/interpreter.h"
00024
00025
00026 #include <kjs/interpreter.h>
00027 #include <kjs/ustring.h>
00028 #include <kjs/object.h>
00029 #include <kjs/PropertyNameArray.h>
00030
00031 #include <kjs/function_object.h>
00032
00033
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
00139
00140
00141
00142
00143
00144
00145
00146
00147
00148
00149
00150
00151
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();
00184 clearError();
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 d->m_defaultFunctionNames = functionNames();
00195 d->m_defaultFunctionNames << "Kross";
00196
00197 {
00198 QHash< QString, QObject* > objects = Manager::self().objects();
00199 QHash< QString, QObject* >::Iterator it(objects.begin()), end(objects.end());
00200 for(; it != end; ++it)
00201 d->publishObject(exec, it.key(), it.value());
00202 }
00203
00204 {
00205 QHash< QString, QObject* > objects = action()->objects();
00206 QHash< QString, QObject* >::Iterator it(objects.begin()), end(objects.end());
00207 for(; it != end; ++it)
00208 d->publishObject(exec, it.key(), it.value());
00209 }
00210
00211
00212
00213
00214
00215
00216
00217
00218
00219
00220
00221
00222
00223 return true;
00224 }
00225
00226 void KjsScript::finalize()
00227 {
00228 KJS::Interpreter* kjsinterpreter = d->m_engine->interpreter();
00229 KJS::ExecState* exec = kjsinterpreter->globalExec();
00230
00231 QList< QPair<KJS::JSObject*, QPointer<QObject> > >::Iterator it( d->m_publishedObjects.begin() );
00232 QList< QPair<KJS::JSObject*, QPointer<QObject> > >::Iterator end( d->m_publishedObjects.end() );
00233 for(; it != end; ++it) {
00234 QObject* obj = (*it).second;
00235 if( ! obj )
00236 continue;
00237 KJS::JSObject* kjsobj = (*it).first;
00238 krossdebug(QString("KjsScript::finalize published object=%1").arg( kjsobj->className().ascii() ));
00239
00240
00241
00242
00243
00244
00245
00246
00247
00248
00249
00250 foreach( QObject* child, obj->children() )
00251 if( KJSEmbed::SlotProxy* proxy = dynamic_cast< KJSEmbed::SlotProxy* >(child) )
00252 delete proxy;
00253
00254 }
00255 d->m_publishedObjects.clear();
00256
00257 d->m_autoconnect.clear();
00258 d->m_defaultFunctionNames.clear();
00259
00260 delete d->m_engine;
00261 d->m_engine = 0;
00262 }
00263
00264 void KjsScript::execute()
00265 {
00266 if(! initialize()) {
00267 krosswarning( QString("KjsScript::execute aborted cause initialize failed.") );
00268 return;
00269 }
00270
00271 QByteArray code = action()->code();
00272 if(code.startsWith("#!"))
00273 code.remove(0, code.indexOf('\n'));
00274
00275 QTextCodec *codec = QTextCodec::codecForLocale();
00276 KJS::UString c = codec ? KJS::UString(codec->toUnicode(code)) : KJS::UString(code.data(), code.size());
00277
00278 KJSEmbed::Engine::ExitStatus exitstatus = d->m_engine->execute(c);
00279
00280 KJS::Completion completion = d->m_engine->completion();
00281 KJS::Interpreter* kjsinterpreter = d->m_engine->interpreter();
00282 KJS::ExecState* exec = kjsinterpreter->globalExec();
00283
00284 if(exitstatus != KJSEmbed::Engine::Success) {
00285 ErrorInterface error = extractError(completion, exec);
00286 setError(&error);
00287 return;
00288 }
00289
00290 KJS::JSObject* kjsglobal = kjsinterpreter->globalObject();
00291 Q_ASSERT( ! exec->hadException() );
00292
00293 foreach(QObject* object, d->m_autoconnect) {
00294 const QMetaObject* metaobject = object->metaObject();
00295 const int count = metaobject->methodCount();
00296 for(int i = 0; i < count; ++i) {
00297 QMetaMethod metamethod = metaobject->method(i);
00298 if( metamethod.methodType() == QMetaMethod::Signal ) {
00299 const QString signature = metamethod.signature();
00300 const QByteArray name = signature.left(signature.indexOf('(')).toLatin1();
00301 krossdebug( QString("KjsScript::execute function=%1").arg(name.data()) );
00302
00303 KJS::Identifier id = KJS::Identifier( KJS::UString(name.data()) );
00304 KJS::JSValue *functionvalue = kjsglobal->get(exec, id);
00305 if( ! functionvalue->isObject() )
00306 continue;
00307 KJS::JSObject *function = functionvalue->toObject(exec);
00308 Q_ASSERT( ! exec->hadException() );
00309 if( exec->hadException() )
00310 continue;
00311 if ( function && function->implementsCall() ) {
00312 krossdebug( QString("KjsScript::execute connect function=%1 with signal=%2").arg(name.data()).arg(signature) );
00313
00314 QByteArray sendersignal = QString("2%1").arg(signature).toLatin1();
00315 QByteArray receiverslot = QString("1%1").arg(signature).toLatin1();
00316 KJSEmbed::SlotProxy* receiver = new KJSEmbed::SlotProxy(kjsglobal, exec->dynamicInterpreter(), object, signature.toLatin1());
00317
00318 if( connect(object, sendersignal, receiver, receiverslot) ) {
00319 krossdebug( QString("KjsScript::execute connected function=%1 with object=%2 signal=%3").arg(name.data()).arg(object->objectName()).arg(signature) );
00320 }
00321 else {
00322 krosswarning( QString("KjsScript::execute failed to connect object=%1 signal=%2").arg(object->objectName()).arg(signature) );
00323 }
00324
00325 }
00326 }
00327 }
00328
00329 }
00330 }
00331
00332 QStringList KjsScript::functionNames()
00333 {
00334 KJS::Interpreter* kjsinterpreter = d->m_engine->interpreter();
00335 KJS::ExecState* exec = kjsinterpreter->globalExec();
00336 KJS::JSObject* kjsglobal = kjsinterpreter->globalObject();
00337 if( exec->hadException() ) {
00338 return QStringList();
00339 }
00340
00341 KJS::PropertyNameArray props;
00342 kjsglobal->getPropertyNames(exec, props);
00343
00344 QStringList list;
00345 for(KJS::PropertyNameArrayIterator it = props.begin(); it != props.end(); ++it) {
00346 const char* name = it->ascii();
00347 KJS::Identifier id = KJS::Identifier(name);
00348 KJS::JSValue *value = kjsglobal->get(exec, id);
00349 if( ! value || ! value->isObject() )
00350 continue;
00351 KJS::JSObject *obj = value->toObject(exec);
00352 if( ! obj || ! obj->implementsCall() || ! obj->implementsConstruct() || ! obj->classInfo() )
00353 continue;
00354 if( d->m_defaultFunctionNames.contains(name) )
00355 continue;
00356 list << name;
00357 }
00358
00359 Q_ASSERT( ! exec->hadException() );
00360 return list;
00361 }
00362
00363 QVariant KjsScript::callFunction(const QString& name, const QVariantList& args)
00364 {
00365
00366
00367 KJS::Interpreter* kjsinterpreter = d->m_engine->interpreter();
00368 KJS::ExecState* exec = kjsinterpreter->globalExec();
00369 KJS::JSObject* kjsglobal = kjsinterpreter->globalObject();
00370 if( exec->hadException() ) {
00371 ErrorInterface error = extractError(d->m_engine->completion(), exec);
00372
00373 krossdebug(QString("KjsScript::callFunction(\"%1\") Prev error: %2").arg(name).arg(error.errorMessage()));
00374 return QVariant();
00375 }
00376
00377 KJS::Identifier id = KJS::Identifier( KJS::UString(name.toLatin1().data()) );
00378 KJS::JSValue *functionvalue = kjsglobal->get(exec, id);
00379 Q_ASSERT( ! exec->hadException() );
00380
00381 KJS::JSObject *function = functionvalue->toObject(exec);
00382 if ( ! function || ! function->implementsCall() ) {
00383 krossdebug(QString("KjsScript::callFunction(\"%1\") No such function").arg(name));
00384 setError(QString("No such function \"%1\"").arg(name));
00385 return QVariant();
00386 }
00387
00388 KJS::List kjsargs;
00389 foreach(QVariant variant, args) {
00390 if( qVariantCanConvert< QWidget* >(variant) ) {
00391 if( QWidget* widget = qvariant_cast< QWidget* >(variant) ) {
00392 kjsargs.append( KJSEmbed::createQObject(exec, widget, KJSEmbed::ObjectBinding::QObjOwned) );
00393 Q_ASSERT( ! exec->hadException() );
00394 continue;
00395 }
00396 }
00397 if( qVariantCanConvert< QObject* >(variant) ) {
00398 if( QObject* obj = qvariant_cast< QObject* >(variant) ) {
00399 kjsargs.append( KJSEmbed::createQObject(exec, obj, KJSEmbed::ObjectBinding::QObjOwned) );
00400 Q_ASSERT( ! exec->hadException() );
00401 continue;
00402 }
00403 }
00404 KJS::JSValue* jsvalue = KJSEmbed::convertToValue(exec, variant);
00405 Q_ASSERT( ! exec->hadException() );
00406 kjsargs.append( jsvalue );
00407 }
00408
00409 KJS::JSValue *retValue = function->call(exec, kjsglobal, kjsargs);
00410 if( exec->hadException() ) {
00411 ErrorInterface error = extractError(d->m_engine->completion(), exec);
00412
00413 krossdebug(QString("KjsScript::callFunction(\"%1\") Call failed: %2").arg(name).arg(error.errorMessage()));
00414 setError(&error);
00415 return QVariant();
00416 }
00417
00418 QVariant result = KJSEmbed::convertToVariant(exec, retValue);
00419 Q_ASSERT( ! exec->hadException() );
00420 return result;
00421 }
00422