Kross

qts-interpreter/script.cpp
1 /***************************************************************************
2  * script.cpp
3  * This file is part of the KDE project
4  * copyright (C)2007-2008 by Sebastian Sauer ([email protected])
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  * Library General Public License for more details.
14  * You should have received a copy of the GNU Library General Public License
15  * along with this program; see the file COPYING. If not, write to
16  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  ***************************************************************************/
19 
20 #include "script.h"
21 #include "kross_qtscript_debug.h"
22 
23 #include <QMetaObject>
24 #include <QMetaMethod>
25 #include <QScriptEngine>
26 #include <QScriptValueIterator>
27 
28 using namespace Kross;
29 
30 namespace Kross
31 {
32 
33 /// \internal private d-pointer class.
34 class EcmaScript::Private
35 {
36 public:
37  EcmaScript *m_script;
38  QScriptEngine *m_engine;
39  QScriptValue m_kross;
40  QScriptValue m_self;
41 
42  explicit Private(EcmaScript *script) : m_script(script), m_engine(nullptr) {}
43  ~Private()
44  {
45  delete m_engine;
46  }
47 
48  bool init()
49  {
50  if (m_script->action()->hadError()) {
51  m_script->action()->clearError();
52  }
53 
54  delete m_engine;
55  m_engine = new QScriptEngine();
56  m_engine->installTranslatorFunctions();
57 
58  // load the Kross QScriptExtensionPlugin plugin that provides
59  // us a bridge between Kross and QtScript. See here plugin.h
60  m_engine->importExtension("kross");
61  if (m_engine->hasUncaughtException()) {
62  handleException();
63  delete m_engine;
64  m_engine = nullptr;
65  return false;
66  }
67 
68  // the Kross QScriptExtensionPlugin exports the "Kross" property.
69  QScriptValue global = m_engine->globalObject();
70  m_kross = global.property("Kross");
71  Q_ASSERT(m_kross.isQObject());
72  Q_ASSERT(! m_engine->hasUncaughtException());
73 
74  // Attach our Kross::Action instance to be able to access it in
75  // scripts. Just like at the Kjs-backend we publish our own
76  // action as "self".
77  m_self = m_engine->newQObject(m_script->action());
78  global.setProperty("self", m_self, QScriptValue::ReadOnly | QScriptValue::Undeletable);
79 
80  {
81  // publish the global objects.
83  QHash< QString, QObject * >::Iterator it(objects.begin()), end(objects.end());
84  for (; it != end; ++it) {
85  global.setProperty(it.key(), m_engine->newQObject(it.value()));
86  }
87  }
88 
89  {
90  // publish the local objects.
91  QHash< QString, QObject * > objects = m_script->action()->objects();
92  QHash< QString, QObject * >::Iterator it(objects.begin()), end(objects.end());
93  for (; it != end; ++it) {
94  copyEnumsToProperties(it.value());
95  global.setProperty(it.key(), m_engine->newQObject(it.value()));
96  }
97  }
98 
99  return ! m_engine->hasUncaughtException();
100  }
101 
102  void copyEnumsToProperties(QObject *object)
103  {
104  const QMetaObject *meta = object->metaObject();
105  for (int i = 0; i < meta->enumeratorCount(); ++i) {
106  QMetaEnum metaenum = meta->enumerator(i);
107  for (int j = 0; j < metaenum.keyCount(); ++j) {
108  object->setProperty(metaenum.key(j), metaenum.value(j));
109  }
110  }
111  }
112 
113  void handleException()
114  {
115  Q_ASSERT(m_engine);
116  Q_ASSERT(m_engine->hasUncaughtException());
117  const QString err = m_engine->uncaughtException().toString();
118  const int linenr = m_engine->uncaughtExceptionLineNumber();
119  const QString trace = m_engine->uncaughtExceptionBacktrace().join("\n");
120  qCDebug(KROSS_QTSCRIPT_LOG) << QStringLiteral("%1, line:%2, backtrace:\n%3").arg(err).arg(linenr).arg(trace);
121  m_script->action()->setError(err, trace, linenr);
122  m_engine->clearExceptions();
123  }
124 
125  void addObject(QObject *object, const QString &name = QString())
126  {
127  Q_ASSERT(m_engine);
128  Q_ASSERT(! m_engine->hasUncaughtException());
129  QScriptValue global = m_engine->globalObject();
130  QScriptValue value = m_engine->newQObject(object);
131  global.setProperty(name.isEmpty() ? object->objectName() : name, value);
132  }
133 
134  void connectFunctions(ChildrenInterface *children)
135  {
136  Q_ASSERT(m_engine);
137  Q_ASSERT(! m_engine->hasUncaughtException());
138  QString eval;
139  QScriptValue global = m_engine->globalObject();
141  while (it.hasNext()) {
142  it.next();
143  if (it.value() & ChildrenInterface::AutoConnectSignals) {
144  QObject *sender = children->object(it.key());
145  if (! sender) {
146  continue;
147  }
148  QScriptValue obj = m_engine->globalObject().property(it.key());
149  if (! obj.isQObject()) {
150  continue;
151  }
152  const QMetaObject *mo = sender->metaObject();
153  const int count = mo->methodCount();
154  for (int i = 0; i < count; ++i) {
155  QMetaMethod mm = mo->method(i);
156  const QString signature = mm.methodSignature();
157  const QString name = signature.left(signature.indexOf('('));
158  if (mm.methodType() == QMetaMethod::Signal) {
159  QScriptValue func = global.property(name);
160  if (! func.isFunction()) {
161  //krossdebug( QString("EcmaScript::connectFunctions No function to connect with %1.%2").arg(it.key()).arg(name) );
162  continue;
163  }
164  qCDebug(KROSS_QTSCRIPT_LOG) << "EcmaScript::connectFunctions Connecting with " <<
165  it.key() << "." << name;
166  eval += QString("try { %1.%2.connect(%3); } catch(e) { print(e); }\n").arg(it.key()).arg(name).arg(name);
167  }
168  }
169  }
170  }
171  Q_ASSERT(! m_engine->hasUncaughtException());
172  if (! eval.isNull()) {
173  m_engine->evaluate(eval);
174  Q_ASSERT(! m_engine->hasUncaughtException());
175  }
176  }
177 
178 };
179 
180 }
181 
182 EcmaScript::EcmaScript(Interpreter *interpreter, Action *action) : Script(interpreter, action), d(new Private(this))
183 {
184  //krossdebug( QString("EcmaScript::EcmaScript") );
185 }
186 
188 {
189  //krossdebug( QString("EcmaScript::~EcmaScript") );
190  delete d;
191 }
192 
194 {
195  if (! d->init()) {
196  d->handleException();
197  return;
198  }
199 
200  QString scriptCode = action()->code();
201  if (scriptCode.startsWith(QLatin1String("#!"))) { // remove optional shebang-line
202  scriptCode.remove(0, scriptCode.indexOf('\n'));
203  }
204 
205  const QString fileName = action()->file().isEmpty() ? action()->name() : action()->file();
206 
207  //krossdebug( QString("EcmaScript::execute fileName=%1 scriptCode=\n%2").arg(fileName).arg(scriptCode) );
208 
209  Q_ASSERT(d->m_engine);
210 
211  if (d->m_engine->hasUncaughtException()) {
212  d->m_engine->clearExceptions();
213  }
214 
215  d->m_engine->evaluate(scriptCode, fileName);
216 
217  if (d->m_engine->hasUncaughtException()) {
218  d->handleException();
219  return;
220  }
221 
222  //d->connectFunctions( &Manager::self() );
223  d->connectFunctions(action());
224 }
225 
227 {
228  if (! d->m_engine && ! d->init()) {
229  d->handleException();
230  return QStringList();
231  }
232  QStringList names;
233  QScriptValueIterator it(d->m_engine->globalObject());
234  while (it.hasNext()) {
235  it.next();
236  if (it.value().isFunction()) {
237  names << it.name();
238  }
239  }
240  return names;
241 }
242 
243 QVariant EcmaScript::callFunction(const QString &name, const QVariantList &args)
244 {
245  if (! d->m_engine && ! d->init()) {
246  d->handleException();
247  return QVariant();
248  }
249 
250  QScriptValue obj = d->m_engine->globalObject();
251  QScriptValue function = obj.property(name);
252  if (! function.isFunction()) {
253  QString err = QString("No such function '%1'").arg(name);
254  qCWarning(KROSS_QTSCRIPT_LOG) << "EcmaScript::callFunction " << err;
255  setError(err);
256  return QVariant();
257  }
258 
259  QScriptValueList arguments;
260  foreach (const QVariant &v, args) {
261  arguments << d->m_engine->toScriptValue(v);
262  }
263  QScriptValue result = function.call(obj, arguments);
264  if (d->m_engine->hasUncaughtException()) {
265  d->handleException();
266  return QVariant();
267  }
268  return result.toVariant();
269 }
270 
272 {
273  if (! d->m_engine && ! d->init()) {
274  d->handleException();
275  return QVariant();
276  }
277 
278  QScriptValue result = d->m_engine->evaluate(code);
279  if (d->m_engine->hasUncaughtException()) {
280  d->handleException();
281  return QVariant();
282  }
283  return result.toVariant();
284 }
285 
287 {
288  return d->m_engine;
289 }
290 
bool hadError() const
int indexOf(QChar ch, int from, Qt::CaseSensitivity cs) const const
QString name(const QVariant &location)
Base class for interpreter dependent functionality each script provides.
Definition: core/script.h:44
Definition: action.cpp:36
int keyCount() const const
QByteArray methodSignature() const const
QObject * sender() const const
QHash< QString, Options > objectOptions() const
const QObjectList & children() const const
void execute() override
Executes the script.
virtual const QMetaObject * metaObject() const const
QString file() const
Definition: action.cpp:366
QString & remove(int position, int n)
QString name() const
Definition: action.cpp:285
QHash< QString, QObject * > objects() const
QObject * engine() const
QVariant property(const char *name) const const
void clearError()
Clear the error.
int value(int index) const const
bool isEmpty() const const
int methodCount() const const
Interface for managing Object collections.
QHash::iterator begin()
bool startsWith(const QString &s, Qt::CaseSensitivity cs) const const
QStringList functionNames() override
QByteArray code() const
Definition: action.cpp:332
Interpreter * interpreter() const
Definition: core/script.cpp:57
Base class for interpreter implementations.
QMetaEnum enumerator(int index) const const
QHashIterator::Item next()
QCA_EXPORT void init()
QObject * object(const QString &name) const
int enumeratorCount() const const
QMetaMethod::MethodType methodType() const const
const QList< QKeySequence > & end()
auto connect signals with scripting functions.
static Manager & self()
Return the Manager instance.
Definition: manager.cpp:74
QVariant callFunction(const QString &name, const QVariantList &args=QVariantList()) override
Execute a function.
void setError(const QString &errormessage, const QString &tracemessage=QString(), long lineno=-1)
Set the error message.
QVariant evaluate(const QByteArray &code) override
Evaluate some scripting code.
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const const
QString left(int n) const const
Action * action() const
Definition: core/script.cpp:62
The Action class is an abstract container to deal with scripts like a single standalone script file...
Definition: action.h:95
QHash::iterator end()
The EcmaScript class implements a Kross::Script to handle a single script.
QMetaMethod method(int index) const const
const char * key(int index) const const
~EcmaScript() override
Destructor.
EcmaScript(Kross::Interpreter *interpreter, Kross::Action *action)
Constructor.
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Sun Jun 20 2021 22:58:38 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.