kdelibs/kross/qts/script.cpp

275 lines
10 KiB
C++
Raw Normal View History

2014-11-13 01:04:59 +02:00
/***************************************************************************
* script.cpp
* This file is part of the KDE project
* copyright (C)2007-2008 by Sebastian Sauer (mail@dipe.org)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
* You should have received a copy of the GNU Library General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
***************************************************************************/
#include "script.h"
#include <QMetaObject>
#include <QtCore/qmetaobject.h>
2014-11-13 01:04:59 +02:00
#include <QScriptEngine>
#include <QScriptValueIterator>
#include <kapplication.h>
using namespace Kross;
namespace Kross {
/// \internal private d-pointer class.
class EcmaScript::Private
{
public:
EcmaScript* m_script;
QScriptEngine* m_engine;
QScriptValue m_kross;
QScriptValue m_self;
explicit Private(EcmaScript* script) : m_script(script), m_engine(0) {}
~Private() { delete m_engine; }
bool init() {
if( m_script->action()->hadError() )
m_script->action()->clearError();
delete m_engine;
m_engine = new QScriptEngine();
m_engine->installTranslatorFunctions();
// load the Kross QScriptExtensionPlugin plugin that provides
// us a bridge between Kross and QtScript. See here plugin.h
m_engine->importExtension("kross");
if( m_engine->hasUncaughtException() ) {
handleException();
delete m_engine;
m_engine = 0;
return false;
}
// the Kross QScriptExtensionPlugin exports the "Kross" property.
QScriptValue global = m_engine->globalObject();
m_kross = global.property("Kross");
Q_ASSERT( m_kross.isQObject() );
Q_ASSERT( ! m_engine->hasUncaughtException() );
// Attach our Kross::Action instance to be able to access it in
// scripts. Just like at the Kjs-backend we publish our own
// action as "self".
m_self = m_engine->newQObject( m_script->action() );
global.setProperty("self", m_self, QScriptValue::ReadOnly|QScriptValue::Undeletable);
{ // publish the global objects.
QHash< QString, QObject* > objects = Manager::self().objects();
QHash< QString, QObject* >::Iterator it(objects.begin()), end(objects.end());
for(; it != end; ++it)
global.setProperty(it.key(), m_engine->newQObject( it.value() ) );
}
{ // publish the local objects.
QHash< QString, QObject* > objects = m_script->action()->objects();
QHash< QString, QObject* >::Iterator it(objects.begin()), end(objects.end());
for(; it != end; ++it) {
copyEnumsToProperties( it.value() );
global.setProperty(it.key(), m_engine->newQObject( it.value() ) );
}
}
return ! m_engine->hasUncaughtException();
}
void copyEnumsToProperties(QObject* object) {
const QMetaObject* meta = object->metaObject();
for (int i = 0; i < meta->enumeratorCount(); ++i) {
QMetaEnum metaenum = meta->enumerator(i);
for (int j = 0; j < metaenum.keyCount(); ++j) {
object->setProperty(metaenum.key(j), metaenum.value(j));
}
}
}
void handleException() {
Q_ASSERT( m_engine );
Q_ASSERT( m_engine->hasUncaughtException() );
const QString err = m_engine->uncaughtException().toString();
const int linenr = m_engine->uncaughtExceptionLineNumber();
const QString trace = m_engine->uncaughtExceptionBacktrace().join("\n");
krossdebug( QString("%1, line:%2, backtrace:\n%3").arg(err).arg(linenr).arg(trace) );
m_script->action()->setError(err, trace, linenr);
m_engine->clearExceptions();
}
void addObject(QObject* object, const QString& name = QString()) {
Q_ASSERT( m_engine );
Q_ASSERT( ! m_engine->hasUncaughtException() );
QScriptValue global = m_engine->globalObject();
QScriptValue value = m_engine->newQObject(object);
global.setProperty(name.isEmpty() ? object->objectName() : name, value);
}
void connectFunctions(ChildrenInterface* children) {
Q_ASSERT( m_engine );
Q_ASSERT( ! m_engine->hasUncaughtException() );
QString eval;
QScriptValue global = m_engine->globalObject();
QHashIterator< QString, ChildrenInterface::Options > it( children->objectOptions() );
while(it.hasNext()) {
it.next();
if( it.value() & ChildrenInterface::AutoConnectSignals ) {
QObject* sender = children->object(it.key());
if( ! sender )
continue;
QScriptValue obj = m_engine->globalObject().property(it.key());
if( ! obj.isQObject() )
continue;
const QMetaObject* mo = sender->metaObject();
const int count = mo->methodCount();
for(int i = 0; i < count; ++i) {
QMetaMethod mm = mo->method(i);
const QString signature = mm.signature();
const QString name = signature.left(signature.indexOf('('));
if( mm.methodType() == QMetaMethod::Signal ) {
QScriptValue func = global.property(name);
if( ! func.isFunction() ) {
//krossdebug( QString("EcmaScript::connectFunctions No function to connect with %1.%2").arg(it.key()).arg(name) );
continue;
}
krossdebug( QString("EcmaScript::connectFunctions Connecting with %1.%2").arg(it.key()).arg(name) );
eval += QString("try { %1.%2.connect(%3); } catch(e) { print(e); }\n").arg(it.key()).arg(name).arg(name);
}
}
}
}
Q_ASSERT( ! m_engine->hasUncaughtException() );
if( ! eval.isNull() ) {
m_engine->evaluate(eval);
Q_ASSERT( ! m_engine->hasUncaughtException() );
}
}
};
}
EcmaScript::EcmaScript(Interpreter* interpreter, Action* action) : Script(interpreter, action), d(new Private(this))
{
//krossdebug( QString("EcmaScript::EcmaScript") );
}
EcmaScript::~EcmaScript()
{
//krossdebug( QString("EcmaScript::~EcmaScript") );
delete d;
}
void EcmaScript::execute()
{
if( ! d->init() ) {
d->handleException();
return;
}
QString scriptCode = action()->code();
if( scriptCode.startsWith(QLatin1String("#!")) ) // remove optional shebang-line
scriptCode.remove(0, scriptCode.indexOf('\n'));
const QString fileName = action()->file().isEmpty() ? action()->name() : action()->file();
//krossdebug( QString("EcmaScript::execute fileName=%1 scriptCode=\n%2").arg(fileName).arg(scriptCode) );
Q_ASSERT( d->m_engine );
if( d->m_engine->hasUncaughtException() ) {
d->m_engine->clearExceptions();
}
d->m_engine->evaluate( scriptCode, fileName );
if( d->m_engine->hasUncaughtException() ) {
d->handleException();
return;
}
//d->connectFunctions( &Manager::self() );
d->connectFunctions( action() );
}
QStringList EcmaScript::functionNames()
{
if( ! d->m_engine && ! d->init() ) {
d->handleException();
return QStringList();
}
QStringList names;
QScriptValueIterator it( d->m_engine->globalObject() );
while( it.hasNext() ) {
it.next();
if( it.value().isFunction() ) {
names << it.name();
}
}
return names;
}
QVariant EcmaScript::callFunction(const QString& name, const QVariantList& args)
{
if( ! d->m_engine && ! d->init() ) {
d->handleException();
return QVariant();
}
QScriptValue obj = d->m_engine->globalObject();
QScriptValue function = obj.property(name);
if( ! function.isFunction() ) {
QString err = QString("No such function '%1'").arg(name);
krosswarning( QString("EcmaScript::callFunction %1").arg(err) );
setError(err);
return QVariant();
}
QScriptValueList arguments;
foreach(const QVariant &v, args)
arguments << d->m_engine->toScriptValue(v);
QScriptValue result = function.call(obj, arguments);
if( d->m_engine->hasUncaughtException() ) {
d->handleException();
return QVariant();
}
return result.toVariant();
}
QVariant EcmaScript::evaluate(const QByteArray& code)
{
if( ! d->m_engine && ! d->init() ) {
d->handleException();
return QVariant();
}
QScriptValue result = d->m_engine->evaluate(code);
if( d->m_engine->hasUncaughtException() ) {
d->handleException();
return QVariant();
}
return result.toVariant();
}
QObject* EcmaScript::engine() const
{
return d->m_engine;
}
#include "moc_script.cpp"