File indexing completed on 2024-04-21 04:02:59

0001 //
0002 // C++ Implementation: cscripteval
0003 //
0004 // Description: 
0005 //
0006 /*
0007 Copyright 2010-2011 Tomas Mecir <kmuddy@kmuddy.com>
0008 
0009 This program is free software; you can redistribute it and/or
0010 modify it under the terms of the GNU General Public License as
0011 published by the Free Software Foundation; either version 2 of 
0012 the License, or (at your option) any later version.
0013 
0014 This program is distributed in the hope that it will be useful,
0015 but WITHOUT ANY WARRANTY; without even the implied warranty of
0016 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0017 GNU General Public License for more details.
0018 
0019 You should have received a copy of the GNU General Public License
0020 along with this program.  If not, see <http://www.gnu.org/licenses/>.
0021 */
0022 
0023 #include "cscripteval.h"
0024 #include <QCoreApplication>
0025 #include <QScriptContext>
0026 #include <QScriptEngine>
0027 #include <QScriptSyntaxCheckResult>
0028 
0029 class TooLongEvent : public QEvent { public: TooLongEvent() : QEvent(QEvent::User) {}; };
0030 
0031 class cScriptEval::Private {
0032 public:
0033 
0034   QScriptEngine *engine;
0035 
0036   void doEval (QString script, QMap<QString, QVariant> variables, cScriptEval *obj)
0037   {
0038     QScriptContext *context = engine->pushContext();
0039 
0040     QMapIterator<QString, QVariant> it(variables);
0041     while (it.hasNext()) {
0042       it.next();
0043       QScriptValue val;
0044       if (it.value().type() == QVariant::StringList)
0045         val = engine->toScriptValue (it.value().toStringList());
0046       else
0047         val = engine->newVariant (it.value());
0048       context->activationObject().setProperty (it.key(), val);
0049     }
0050 
0051     QCoreApplication::postEvent (obj, new TooLongEvent, 50);  // a super high-priority event
0052     engine->evaluate (script);
0053     engine->popContext();
0054 
0055     if (engine->hasUncaughtException()) {
0056       obj->invokeEvent ("message", obj->sess(), "Error in script: " + engine->uncaughtException().toString());
0057       engine->clearExceptions ();
0058     }
0059   }
0060 };
0061 
0062 cScriptEval::cScriptEval (int sess) : cActionBase ("scripteval", sess)
0063 {
0064   d = new Private;
0065   d->engine = new QScriptEngine;
0066   d->engine->setProcessEventsInterval (4000);
0067 }
0068 
0069 cScriptEval::~cScriptEval ()
0070 {
0071   abort ();
0072 
0073   delete d->engine;
0074   delete d;
0075 }
0076 
0077 void cScriptEval::addObject (QString name, QObject *object)
0078 {
0079   d->engine->globalObject().setProperty (name, d->engine->newQObject (object));
0080 }
0081 
0082 void cScriptEval::eval (QString script, QMap<QString, QVariant> variables)
0083 {
0084   d->doEval (script, variables, this);
0085 }
0086 
0087 QString cScriptEval::validate (QString script)
0088 {
0089   QScriptSyntaxCheckResult res = QScriptEngine::checkSyntax (script);
0090   if (res.state() == QScriptSyntaxCheckResult::Valid) return QString();
0091   if (res.state() != QScriptSyntaxCheckResult::Error) return QString("(unfinished)");
0092   // errorMessage() only returns an empty string, so I can't use it. No idea why.
0093   // return "At line "+QString::number(res.errorLineNumber())+": " + res.errorMessage();
0094   return "Error at line "+QString::number(res.errorLineNumber());
0095 }
0096 
0097 void cScriptEval::abort ()
0098 {
0099   if (d->engine->isEvaluating())
0100     d->engine->abortEvaluation();
0101 }
0102 
0103 bool cScriptEval::event (QEvent *e)
0104 {
0105   if (!dynamic_cast<TooLongEvent *>(e)) return QObject::event (e);
0106 
0107   if (!d->engine->isEvaluating()) return true;
0108 
0109   invokeEvent ("message", sess(), "Script execution is taking too long, aborting.");
0110   abort();
0111   return true;
0112 }
0113 
0114 #include "moc_cscripteval.cpp"