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
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 {
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 {
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
00217
00218
00219
00220
00221
00222
00223
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
00244
00245
00246
00247
00248
00249
00250
00251
00252
00253 foreach( QObject* child, obj->children() )
00254 if( KJSEmbed::SlotProxy* proxy = dynamic_cast< KJSEmbed::SlotProxy* >(child) )
00255 delete proxy;
00256
00257
00258
00259
00260
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("#!"))
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
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
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
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
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
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 }