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>
|
2015-08-11 05:56:07 +03:00
|
|
|
#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;
|
|
|
|
}
|
|
|
|
|
2015-02-27 07:40:26 +00:00
|
|
|
#include "moc_script.cpp"
|