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 KJS::JSObject* kjsobj = (*it).first;
00243 krossdebug(QString("KjsScript::finalize published object=%1").arg( kjsobj->className().ascii() ));
00244
00245
00246
00247
00248
00249
00250
00251
00252
00253
00254
00255 foreach( QObject* child, obj->children() )
00256 if( KJSEmbed::SlotProxy* proxy = dynamic_cast< KJSEmbed::SlotProxy* >(child) )
00257 delete proxy;
00258
00259 }
00260 d->m_publishedObjects.clear();
00261
00262 d->m_autoconnect.clear();
00263 d->m_defaultFunctionNames.clear();
00264
00265 delete d->m_engine;
00266 d->m_engine = 0;
00267 }
00268
00269 void KjsScript::execute()
00270 {
00271 if(! initialize()) {
00272 krosswarning( QString("KjsScript::execute aborted cause initialize failed.") );
00273 return;
00274 }
00275
00276 QByteArray code = action()->code();
00277 if(code.startsWith("#!"))
00278 code.remove(0, code.indexOf('\n'));
00279
00280 QTextCodec *codec = QTextCodec::codecForLocale();
00281 KJS::UString c = codec ? KJS::UString(codec->toUnicode(code)) : KJS::UString(code.data(), code.size());
00282
00283 KJSEmbed::Engine::ExitStatus exitstatus = d->m_engine->execute(c);
00284
00285 KJS::Completion completion = d->m_engine->completion();
00286 KJS::Interpreter* kjsinterpreter = d->m_engine->interpreter();
00287 KJS::ExecState* exec = kjsinterpreter->globalExec();
00288
00289 if(exitstatus != KJSEmbed::Engine::Success) {
00290 ErrorInterface error = extractError(completion, exec);
00291 setError(&error);
00292 return;
00293 }
00294
00295 KJS::JSObject* kjsglobal = kjsinterpreter->globalObject();
00296 if( exec->hadException() ) {
00297 ErrorInterface error = extractError(d->m_engine->completion(), exec);
00298 krossdebug(QString("KjsScript::execute() failed: %1").arg(error.errorMessage()));
00299 setError(&error);
00300
00301 return;
00302 }
00303
00304 foreach(QObject* object, d->m_autoconnect) {
00305 const QMetaObject* metaobject = object->metaObject();
00306 const int count = metaobject->methodCount();
00307 for(int i = 0; i < count; ++i) {
00308 QMetaMethod metamethod = metaobject->method(i);
00309 if( metamethod.methodType() == QMetaMethod::Signal ) {
00310 const QString signature = metamethod.signature();
00311 const QByteArray name = signature.left(signature.indexOf('(')).toLatin1();
00312 krossdebug( QString("KjsScript::execute function=%1").arg(name.data()) );
00313
00314 KJS::Identifier id = KJS::Identifier( KJS::UString(name.data()) );
00315 KJS::JSValue *functionvalue = kjsglobal->get(exec, id);
00316 if( ! functionvalue->isObject() )
00317 continue;
00318 KJS::JSObject *function = functionvalue->toObject(exec);
00319 Q_ASSERT( ! exec->hadException() );
00320 if( exec->hadException() )
00321 continue;
00322 if ( function && function->implementsCall() ) {
00323 krossdebug( QString("KjsScript::execute connect function=%1 with signal=%2").arg(name.data()).arg(signature) );
00324
00325 QByteArray sendersignal = QString("2%1").arg(signature).toLatin1();
00326 QByteArray receiverslot = QString("1%1").arg(signature).toLatin1();
00327 KJSEmbed::SlotProxy* receiver = new KJSEmbed::SlotProxy(kjsglobal, exec->dynamicInterpreter(), object, signature.toLatin1());
00328
00329 if( connect(object, sendersignal, receiver, receiverslot) ) {
00330 krossdebug( QString("KjsScript::execute connected function=%1 with object=%2 signal=%3").arg(name.data()).arg(object->objectName()).arg(signature) );
00331 }
00332 else {
00333 krosswarning( QString("KjsScript::execute failed to connect object=%1 signal=%2").arg(object->objectName()).arg(signature) );
00334 }
00335
00336 }
00337 }
00338 }
00339
00340 }
00341 }
00342
00343 QStringList KjsScript::functionNames()
00344 {
00345 KJS::Interpreter* kjsinterpreter = d->m_engine->interpreter();
00346 KJS::ExecState* exec = kjsinterpreter->globalExec();
00347 KJS::JSObject* kjsglobal = kjsinterpreter->globalObject();
00348 if( exec->hadException() ) {
00349 return QStringList();
00350 }
00351
00352 KJS::PropertyNameArray props;
00353 kjsglobal->getPropertyNames(exec, props);
00354
00355 QStringList list;
00356 for(KJS::PropertyNameArrayIterator it = props.begin(); it != props.end(); ++it) {
00357 const char* name = it->ascii();
00358 KJS::Identifier id = KJS::Identifier(name);
00359 KJS::JSValue *value = kjsglobal->get(exec, id);
00360 if( ! value || ! value->isObject() )
00361 continue;
00362 KJS::JSObject *obj = value->toObject(exec);
00363 if( ! obj || ! obj->implementsCall() || ! obj->implementsConstruct() || ! obj->classInfo() )
00364 continue;
00365 if( d->m_defaultFunctionNames.contains(name) )
00366 continue;
00367 list << name;
00368 }
00369
00370 Q_ASSERT( ! exec->hadException() );
00371 return list;
00372 }
00373
00374 QVariant KjsScript::callFunction(const QString& name, const QVariantList& args)
00375 {
00376
00377
00378 KJS::Interpreter* kjsinterpreter = d->m_engine->interpreter();
00379 KJS::ExecState* exec = kjsinterpreter->globalExec();
00380 KJS::JSObject* kjsglobal = kjsinterpreter->globalObject();
00381 if( exec->hadException() ) {
00382 ErrorInterface error = extractError(d->m_engine->completion(), exec);
00383
00384 krossdebug(QString("KjsScript::callFunction(\"%1\") Prev error: %2").arg(name).arg(error.errorMessage()));
00385 return QVariant();
00386 }
00387
00388 KJS::Identifier id = KJS::Identifier( KJS::UString(name.toLatin1().data()) );
00389 KJS::JSValue *functionvalue = kjsglobal->get(exec, id);
00390 Q_ASSERT( ! exec->hadException() );
00391
00392 KJS::JSObject *function = functionvalue->toObject(exec);
00393 if ( ! function || ! function->implementsCall() ) {
00394 krossdebug(QString("KjsScript::callFunction(\"%1\") No such function").arg(name));
00395 setError(QString("No such function \"%1\"").arg(name));
00396 return QVariant();
00397 }
00398
00399 KJS::List kjsargs;
00400 foreach(const QVariant &variant, args) {
00401 if( qVariantCanConvert< QWidget* >(variant) ) {
00402 if( QWidget* widget = qvariant_cast< QWidget* >(variant) ) {
00403 kjsargs.append( KJSEmbed::createQObject(exec, widget, KJSEmbed::ObjectBinding::QObjOwned) );
00404 Q_ASSERT( ! exec->hadException() );
00405 continue;
00406 }
00407 }
00408 if( qVariantCanConvert< QObject* >(variant) ) {
00409 if( QObject* obj = qvariant_cast< QObject* >(variant) ) {
00410 kjsargs.append( KJSEmbed::createQObject(exec, obj, KJSEmbed::ObjectBinding::QObjOwned) );
00411 Q_ASSERT( ! exec->hadException() );
00412 continue;
00413 }
00414 }
00415 KJS::JSValue* jsvalue = KJSEmbed::convertToValue(exec, variant);
00416 Q_ASSERT( ! exec->hadException() );
00417 kjsargs.append( jsvalue );
00418 }
00419
00420 KJS::JSValue *retValue = function->call(exec, kjsglobal, kjsargs);
00421 if( exec->hadException() ) {
00422 ErrorInterface error = extractError(d->m_engine->completion(), exec);
00423
00424 krossdebug(QString("KjsScript::callFunction(\"%1\") Call failed: %2").arg(name).arg(error.errorMessage()));
00425 setError(&error);
00426 return QVariant();
00427 }
00428
00429 QVariant result = retValue ? KJSEmbed::convertToVariant(exec, retValue) : QVariant();
00430 Q_ASSERT( ! exec->hadException() );
00431 return result;
00432 }
00433
00434 QVariant KjsScript::evaluate(const QByteArray& code)
00435 {
00436 QTextCodec *codec = QTextCodec::codecForLocale();
00437 KJS::UString c = codec ? KJS::UString(codec->toUnicode(code)) : KJS::UString(code.data(), code.size());
00438
00439 KJSEmbed::Engine::ExitStatus exitstatus = d->m_engine->execute(c);
00440
00441 KJS::Completion completion = d->m_engine->completion();
00442 KJS::Interpreter* kjsinterpreter = d->m_engine->interpreter();
00443 KJS::ExecState* exec = kjsinterpreter->globalExec();
00444
00445 if(exitstatus != KJSEmbed::Engine::Success) {
00446 ErrorInterface error = extractError(completion, exec);
00447 setError(&error);
00448 return QVariant();
00449 }
00450
00451 KJS::JSValue *retValue = completion.value();
00452 QVariant result = retValue ? KJSEmbed::convertToVariant(exec, retValue) : QVariant();
00453 Q_ASSERT( ! exec->hadException() );
00454 return result;
00455 }