summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKent Hansen <khansen@trolltech.com>2009-10-26 13:21:27 +0100
committerKent Hansen <khansen@trolltech.com>2009-10-28 10:15:32 +0100
commit0533c709188d07be0c165838b503124cb6b271e1 (patch)
treed866357d3ac735e9d1527816f83379e6e5746f78
parent8cda1c6fef8bd9c4635743fae05158306c2ab09f (diff)
Say hello to QScriptProgram :-)
QScriptProgram encapsulates a Qt Script program (AKA a script). It retains the compiled representation of the script, so that repeated evaluation of the same script becomes faster. An overload of QScriptEngine::evaluate() that takes a QScriptProgram has been added. Reviewed-by: Olivier Goffart
-rw-r--r--src/script/api/api.pri3
-rw-r--r--src/script/api/qscriptengine.cpp151
-rw-r--r--src/script/api/qscriptengine.h3
-rw-r--r--src/script/api/qscriptengine_p.h5
-rw-r--r--src/script/api/qscriptprogram.cpp225
-rw-r--r--src/script/api/qscriptprogram.h86
-rw-r--r--src/script/api/qscriptprogram_p.h95
-rw-r--r--tests/auto/qscriptengine/tst_qscriptengine.cpp149
-rw-r--r--tests/auto/qscriptengineagent/tst_qscriptengineagent.cpp86
-rw-r--r--tests/benchmarks/qscriptengine/tst_qscriptengine.cpp19
10 files changed, 763 insertions, 59 deletions
diff --git a/src/script/api/api.pri b/src/script/api/api.pri
index 17ec9b6e08..aebadd5b1c 100644
--- a/src/script/api/api.pri
+++ b/src/script/api/api.pri
@@ -6,6 +6,7 @@ SOURCES += \
$$PWD/qscriptengine.cpp \
$$PWD/qscriptengineagent.cpp \
$$PWD/qscriptextensionplugin.cpp \
+ $$PWD/qscriptprogram.cpp \
$$PWD/qscriptstring.cpp \
$$PWD/qscriptvalue.cpp \
$$PWD/qscriptvalueiterator.cpp \
@@ -23,6 +24,8 @@ HEADERS += \
$$PWD/qscriptengineagent_p.h \
$$PWD/qscriptextensioninterface.h \
$$PWD/qscriptextensionplugin.h \
+ $$PWD/qscriptprogram.h \
+ $$PWD/qscriptprogram_p.h \
$$PWD/qscriptstring.h \
$$PWD/qscriptstring_p.h \
$$PWD/qscriptvalue.h \
diff --git a/src/script/api/qscriptengine.cpp b/src/script/api/qscriptengine.cpp
index 928872333e..2b60a46a89 100644
--- a/src/script/api/qscriptengine.cpp
+++ b/src/script/api/qscriptengine.cpp
@@ -51,6 +51,8 @@
#include "qscriptvalue_p.h"
#include "qscriptvalueiterator.h"
#include "qscriptclass.h"
+#include "qscriptprogram.h"
+#include "qscriptprogram_p.h"
#include "qdebug.h"
#include <QtCore/qstringlist.h>
@@ -1155,6 +1157,73 @@ void QScriptEnginePrivate::agentDeleted(QScriptEngineAgent *agent)
}
}
+JSC::JSValue QScriptEnginePrivate::evaluateHelper(JSC::ExecState *exec, intptr_t sourceId,
+ JSC::EvalExecutable *executable,
+ bool &compile)
+{
+ Q_Q(QScriptEngine);
+ JSC::JSLock lock(false); // ### hmmm
+ QBoolBlocker inEvalBlocker(inEval, true);
+ q->currentContext()->activationObject(); //force the creation of a context for native function;
+
+ JSC::Debugger* debugger = originalGlobalObject()->debugger();
+ if (debugger)
+ debugger->evaluateStart(sourceId);
+
+ q->clearExceptions();
+ JSC::DynamicGlobalObjectScope dynamicGlobalObjectScope(exec, exec->scopeChain()->globalObject());
+
+ if (compile) {
+ JSC::JSObject* error = executable->compile(exec, exec->scopeChain());
+ if (error) {
+ compile = false;
+ exec->setException(error);
+
+ if (debugger) {
+ debugger->exceptionThrow(JSC::DebuggerCallFrame(exec, error), sourceId, false);
+ debugger->evaluateStop(error, sourceId);
+ }
+
+ return error;
+ }
+ }
+
+ JSC::JSValue thisValue = thisForContext(exec);
+ JSC::JSObject* thisObject = (!thisValue || thisValue.isUndefinedOrNull())
+ ? exec->dynamicGlobalObject() : thisValue.toObject(exec);
+ JSC::JSValue exceptionValue;
+ timeoutChecker()->setShouldAbort(false);
+ if (processEventsInterval > 0)
+ timeoutChecker()->reset();
+
+ JSC::JSValue result = exec->interpreter()->execute(executable, exec, thisObject, exec->scopeChain(), &exceptionValue);
+
+ if (timeoutChecker()->shouldAbort()) {
+ if (abortResult.isError())
+ exec->setException(scriptValueToJSCValue(abortResult));
+
+ if (debugger)
+ debugger->evaluateStop(scriptValueToJSCValue(abortResult), sourceId);
+
+ return scriptValueToJSCValue(abortResult);
+ }
+
+ if (exceptionValue) {
+ exec->setException(exceptionValue);
+
+ if (debugger)
+ debugger->evaluateStop(exceptionValue, sourceId);
+
+ return exceptionValue;
+ }
+
+ if (debugger)
+ debugger->evaluateStop(result, sourceId);
+
+ Q_ASSERT(!exec->hadException());
+ return result;
+}
+
#ifndef QT_NO_QOBJECT
JSC::JSValue QScriptEnginePrivate::newQObject(
@@ -2115,75 +2184,41 @@ QScriptSyntaxCheckResult QScriptEnginePrivate::checkSyntax(const QString &progra
QScriptValue QScriptEngine::evaluate(const QString &program, const QString &fileName, int lineNumber)
{
Q_D(QScriptEngine);
-
- JSC::JSLock lock(false); // ### hmmm
- QBoolBlocker inEval(d->inEval, true);
- currentContext()->activationObject(); //force the creation of a context for native function;
-
- JSC::Debugger* debugger = d->originalGlobalObject()->debugger();
-
- JSC::UString jscProgram = program;
- JSC::UString jscFileName = fileName;
- JSC::ExecState* exec = d->currentFrame;
WTF::PassRefPtr<QScript::UStringSourceProviderWithFeedback> provider
- = QScript::UStringSourceProviderWithFeedback::create(jscProgram, jscFileName, lineNumber, d);
+ = QScript::UStringSourceProviderWithFeedback::create(program, fileName, lineNumber, d);
intptr_t sourceId = provider->asID();
JSC::SourceCode source(provider, lineNumber); //after construction of SourceCode provider variable will be null.
- if (debugger)
- debugger->evaluateStart(sourceId);
-
- clearExceptions();
- JSC::DynamicGlobalObjectScope dynamicGlobalObjectScope(exec, exec->scopeChain()->globalObject());
-
+ JSC::ExecState* exec = d->currentFrame;
JSC::EvalExecutable executable(exec, source);
- JSC::JSObject* error = executable.compile(exec, exec->scopeChain());
- if (error) {
- exec->setException(error);
-
- if (debugger) {
- debugger->exceptionThrow(JSC::DebuggerCallFrame(exec, error), sourceId, false);
- debugger->evaluateStop(error, sourceId);
- }
-
- return d->scriptValueFromJSCValue(error);
- }
-
- JSC::JSValue thisValue = d->thisForContext(exec);
- JSC::JSObject* thisObject = (!thisValue || thisValue.isUndefinedOrNull()) ? exec->dynamicGlobalObject() : thisValue.toObject(exec);
- JSC::JSValue exceptionValue;
- d->timeoutChecker()->setShouldAbort(false);
- if (d->processEventsInterval > 0)
- d->timeoutChecker()->reset();
- JSC::JSValue result = exec->interpreter()->execute(&executable, exec, thisObject, exec->scopeChain(), &exceptionValue);
-
- if (d->timeoutChecker()->shouldAbort()) {
- if (d->abortResult.isError())
- exec->setException(d->scriptValueToJSCValue(d->abortResult));
-
- if (debugger)
- debugger->evaluateStop(d->scriptValueToJSCValue(d->abortResult), sourceId);
-
- return d->abortResult;
- }
-
- if (exceptionValue) {
- exec->setException(exceptionValue);
-
- if (debugger)
- debugger->evaluateStop(exceptionValue, sourceId);
+ bool compile = true;
+ return d->scriptValueFromJSCValue(d->evaluateHelper(exec, sourceId, &executable, compile));
+}
- return d->scriptValueFromJSCValue(exceptionValue);
- }
+/*!
+ \internal
+ \since 4.6
- if (debugger)
- debugger->evaluateStop(result, sourceId);
+ Evaluates the given \a program and returns the result of the
+ evaluation.
+*/
+QScriptValue QScriptEngine::evaluate(const QScriptProgram &program)
+{
+ Q_D(QScriptEngine);
+ QScriptProgramPrivate *program_d = QScriptProgramPrivate::get(program);
+ if (!program_d)
+ return QScriptValue();
- Q_ASSERT(!exec->hadException());
+ JSC::ExecState* exec = d->currentFrame;
+ JSC::EvalExecutable *executable = program_d->executable(exec, d);
+ bool compile = !program_d->isCompiled;
+ JSC::JSValue result = d->evaluateHelper(exec, program_d->sourceId,
+ executable, compile);
+ if (compile)
+ program_d->isCompiled = true;
return d->scriptValueFromJSCValue(result);
}
-
/*!
Returns the current context.
diff --git a/src/script/api/qscriptengine.h b/src/script/api/qscriptengine.h
index 701f9c69f3..3f438da142 100644
--- a/src/script/api/qscriptengine.h
+++ b/src/script/api/qscriptengine.h
@@ -67,6 +67,7 @@ class QDateTime;
class QScriptClass;
class QScriptEngineAgent;
class QScriptEnginePrivate;
+class QScriptProgram;
#ifndef QT_NO_QOBJECT
@@ -166,6 +167,8 @@ public:
QScriptValue evaluate(const QString &program, const QString &fileName = QString(), int lineNumber = 1);
+ QScriptValue evaluate(const QScriptProgram &program);
+
bool isEvaluating() const;
void abortEvaluation(const QScriptValue &result = QScriptValue());
diff --git a/src/script/api/qscriptengine_p.h b/src/script/api/qscriptengine_p.h
index ec7c144016..d12b0f4a59 100644
--- a/src/script/api/qscriptengine_p.h
+++ b/src/script/api/qscriptengine_p.h
@@ -70,6 +70,7 @@
namespace JSC
{
+ class EvalExecutable;
class ExecState;
typedef ExecState CallFrame;
class JSCell;
@@ -211,6 +212,10 @@ public:
const QByteArray &targetType,
void **result);
+ JSC::JSValue evaluateHelper(JSC::ExecState *exec, intptr_t sourceId,
+ JSC::EvalExecutable *executable,
+ bool &compile);
+
QScript::QObjectData *qobjectData(QObject *object);
void disposeQObject(QObject *object);
void emitSignalHandlerException();
diff --git a/src/script/api/qscriptprogram.cpp b/src/script/api/qscriptprogram.cpp
new file mode 100644
index 0000000000..c30f381d86
--- /dev/null
+++ b/src/script/api/qscriptprogram.cpp
@@ -0,0 +1,225 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtScript module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "config.h"
+#include "qscriptprogram.h"
+#include "qscriptprogram_p.h"
+#include "qscriptengine.h"
+#include "qscriptengine_p.h"
+
+#include "Executable.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \internal
+
+ \since 4.6
+ \class QScriptProgram
+
+ \brief The QScriptProgram class encapsulates a Qt Script program.
+
+ \ingroup script
+
+ QScriptProgram retains the compiled representation of the script if
+ possible. Thus, QScriptProgram can be used to evaluate the same
+ script multiple times more efficiently.
+
+ \code
+ QScriptEngine engine;
+ QScriptProgram program("1 + 2");
+ QScriptValue result = engine.evaluate(program);
+ \endcode
+*/
+
+QScriptProgramPrivate::QScriptProgramPrivate(const QString &src,
+ const QString &fn,
+ int ln)
+ : sourceCode(src), fileName(fn), firstLineNumber(ln),
+ engine(0), _executable(0), sourceId(-1), isCompiled(false)
+{
+ ref = 0;
+}
+
+QScriptProgramPrivate::~QScriptProgramPrivate()
+{
+ delete _executable;
+}
+
+QScriptProgramPrivate *QScriptProgramPrivate::get(const QScriptProgram &q)
+{
+ return const_cast<QScriptProgramPrivate*>(q.d_func());
+}
+
+JSC::EvalExecutable *QScriptProgramPrivate::executable(JSC::ExecState *exec,
+ QScriptEnginePrivate *eng)
+{
+ if (_executable) {
+ if (eng == engine)
+ return _executable;
+ delete _executable;
+ }
+ WTF::PassRefPtr<QScript::UStringSourceProviderWithFeedback> provider
+ = QScript::UStringSourceProviderWithFeedback::create(sourceCode, fileName, firstLineNumber, eng);
+ sourceId = provider->asID();
+ JSC::SourceCode source(provider, firstLineNumber); //after construction of SourceCode provider variable will be null.
+ _executable = new JSC::EvalExecutable(exec, source);
+ engine = eng;
+ isCompiled = false;
+ return _executable;
+}
+
+/*!
+ Constructs a null QScriptProgram.
+*/
+QScriptProgram::QScriptProgram()
+ : d_ptr(0)
+{
+}
+
+/*!
+ Constructs a new QScriptProgram with the given \a sourceCode, \a
+ fileName and \a firstLineNumber.
+*/
+QScriptProgram::QScriptProgram(const QString &sourceCode,
+ const QString fileName,
+ int firstLineNumber)
+ : d_ptr(new QScriptProgramPrivate(sourceCode, fileName, firstLineNumber))
+{
+}
+
+/*!
+ Constructs a new QScriptProgram that is a copy of \a other.
+*/
+QScriptProgram::QScriptProgram(const QScriptProgram &other)
+ : d_ptr(other.d_ptr)
+{
+}
+
+/*!
+ Destroys this QScriptProgram.
+*/
+QScriptProgram::~QScriptProgram()
+{
+ Q_D(QScriptProgram);
+ // if (d->engine && (d->ref == 1))
+ // d->engine->unregisterScriptProgram(d);
+}
+
+/*!
+ Assigns the \a other value to this QScriptProgram.
+*/
+QScriptProgram &QScriptProgram::operator=(const QScriptProgram &other)
+{
+ // if (d_func() && d_func()->engine && (d_func()->ref == 1))
+ // d_func()->engine->unregisterScriptProgram(d_func());
+ // }
+ d_ptr = other.d_ptr;
+ return *this;
+}
+
+/*!
+ Returns true if this QScriptProgram is null; otherwise
+ returns false.
+*/
+bool QScriptProgram::isNull() const
+{
+ Q_D(const QScriptProgram);
+ return (d == 0);
+}
+
+/*!
+ Returns the source code of this program.
+*/
+QString QScriptProgram::sourceCode() const
+{
+ Q_D(const QScriptProgram);
+ if (!d)
+ return QString();
+ return d->sourceCode;
+}
+
+/*!
+ Returns the filename associated with this program.
+*/
+QString QScriptProgram::fileName() const
+{
+ Q_D(const QScriptProgram);
+ if (!d)
+ return QString();
+ return d->fileName;
+}
+
+/*!
+ Returns the line number associated with this program.
+*/
+int QScriptProgram::firstLineNumber() const
+{
+ Q_D(const QScriptProgram);
+ if (!d)
+ return -1;
+ return d->firstLineNumber;
+}
+
+/*!
+ Returns true if this QScriptProgram is equal to \a other;
+ otherwise returns false.
+*/
+bool QScriptProgram::operator==(const QScriptProgram &other) const
+{
+ Q_D(const QScriptProgram);
+ if (d == other.d_func())
+ return true;
+ return (sourceCode() == other.sourceCode())
+ && (fileName() == other.fileName())
+ && (firstLineNumber() == other.firstLineNumber());
+}
+
+/*!
+ Returns true if this QScriptProgram is not equal to \a other;
+ otherwise returns false.
+*/
+bool QScriptProgram::operator!=(const QScriptProgram &other) const
+{
+ return !operator==(other);
+}
+
+QT_END_NAMESPACE
diff --git a/src/script/api/qscriptprogram.h b/src/script/api/qscriptprogram.h
new file mode 100644
index 0000000000..de891cdbb3
--- /dev/null
+++ b/src/script/api/qscriptprogram.h
@@ -0,0 +1,86 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtScript module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSCRIPTPROGRAM_H
+#define QSCRIPTPROGRAM_H
+
+#include <QtCore/qsharedpointer.h>
+
+#include <QtCore/qstring.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Script)
+
+class QScriptProgramPrivate;
+class Q_SCRIPT_EXPORT QScriptProgram
+{
+public:
+ QScriptProgram();
+ QScriptProgram(const QString &sourceCode,
+ const QString fileName = QString(),
+ int firstLineNumber = 1);
+ QScriptProgram(const QScriptProgram &other);
+ ~QScriptProgram();
+
+ QScriptProgram &operator=(const QScriptProgram &other);
+
+ bool isNull() const;
+
+ QString sourceCode() const;
+ QString fileName() const;
+ int firstLineNumber() const;
+
+ bool operator==(const QScriptProgram &other) const;
+ bool operator!=(const QScriptProgram &other) const;
+
+private:
+ QExplicitlySharedDataPointer<QScriptProgramPrivate> d_ptr;
+ Q_DECLARE_PRIVATE(QScriptProgram)
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QSCRIPTPROGRAM_H
diff --git a/src/script/api/qscriptprogram_p.h b/src/script/api/qscriptprogram_p.h
new file mode 100644
index 0000000000..5175079d4e
--- /dev/null
+++ b/src/script/api/qscriptprogram_p.h
@@ -0,0 +1,95 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtScript module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSCRIPTPROGRAM_P_H
+#define QSCRIPTPROGRAM_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qobjectdefs.h>
+
+namespace JSC
+{
+ class EvalExecutable;
+ class ExecState;
+}
+
+QT_BEGIN_NAMESPACE
+
+class QScriptEnginePrivate;
+
+class QScriptProgramPrivate
+{
+public:
+ QScriptProgramPrivate(const QString &sourceCode,
+ const QString &fileName,
+ int firstLineNumber);
+ ~QScriptProgramPrivate();
+
+ static QScriptProgramPrivate *get(const QScriptProgram &q);
+
+ JSC::EvalExecutable *executable(JSC::ExecState *exec,
+ QScriptEnginePrivate *engine);
+
+ QBasicAtomicInt ref;
+
+ QString sourceCode;
+ QString fileName;
+ int firstLineNumber;
+
+ QScriptEnginePrivate *engine;
+ JSC::EvalExecutable *_executable;
+ intptr_t sourceId;
+ bool isCompiled;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/tests/auto/qscriptengine/tst_qscriptengine.cpp b/tests/auto/qscriptengine/tst_qscriptengine.cpp
index 25ee00f5ef..804534fe47 100644
--- a/tests/auto/qscriptengine/tst_qscriptengine.cpp
+++ b/tests/auto/qscriptengine/tst_qscriptengine.cpp
@@ -44,6 +44,7 @@
#include <qscriptengine.h>
#include <qscriptengineagent.h>
+#include <qscriptprogram.h>
#include <qscriptvalueiterator.h>
#include <qgraphicsitem.h>
#include <qstandarditemmodel.h>
@@ -52,6 +53,7 @@
Q_DECLARE_METATYPE(QList<int>)
Q_DECLARE_METATYPE(QObjectList)
+Q_DECLARE_METATYPE(QScriptProgram)
//TESTED_CLASS=
//TESTED_FILES=
@@ -151,6 +153,7 @@ private slots:
void installTranslatorFunctions();
void functionScopes();
void nativeFunctionScopes();
+ void evaluateProgram();
void qRegExpInport_data();
void qRegExpInport();
@@ -4289,6 +4292,152 @@ void tst_QScriptEngine::nativeFunctionScopes()
}
}
+static QScriptValue createProgram(QScriptContext *ctx, QScriptEngine *eng)
+{
+ QString code = ctx->argument(0).toString();
+ QScriptProgram result(code);
+ return qScriptValueFromValue(eng, result);
+}
+
+void tst_QScriptEngine::evaluateProgram()
+{
+ QScriptEngine eng;
+
+ {
+ QString code("1 + 2");
+ QString fileName("hello.js");
+ int lineNumber(123);
+ QScriptProgram program(code, fileName, lineNumber);
+ QVERIFY(!program.isNull());
+ QCOMPARE(program.sourceCode(), code);
+ QCOMPARE(program.fileName(), fileName);
+ QCOMPARE(program.firstLineNumber(), lineNumber);
+
+ QScriptValue expected = eng.evaluate(code);
+ for (int x = 0; x < 10; ++x) {
+ QScriptValue ret = eng.evaluate(program);
+ QVERIFY(ret.equals(expected));
+ }
+
+ // operator=
+ QScriptProgram sameProgram = program;
+ QVERIFY(sameProgram == program);
+ QVERIFY(eng.evaluate(sameProgram).equals(expected));
+
+ // copy constructor
+ QScriptProgram sameProgram2(program);
+ QVERIFY(sameProgram2 == program);
+ QVERIFY(eng.evaluate(sameProgram2).equals(expected));
+
+ QScriptProgram differentProgram("2 + 3");
+ QVERIFY(differentProgram != program);
+ QVERIFY(!eng.evaluate(differentProgram).equals(expected));
+ }
+
+ // Program that accesses variable in the scope
+ {
+ QScriptProgram program("a");
+ QVERIFY(!program.isNull());
+ {
+ QScriptValue ret = eng.evaluate(program);
+ QVERIFY(ret.isError());
+ QCOMPARE(ret.toString(), QString::fromLatin1("ReferenceError: Can't find variable: a"));
+ }
+
+ QScriptValue obj = eng.newObject();
+ obj.setProperty("a", 123);
+ QScriptContext *ctx = eng.currentContext();
+ ctx->pushScope(obj);
+ {
+ QScriptValue ret = eng.evaluate(program);
+ QVERIFY(!ret.isError());
+ QVERIFY(ret.equals(obj.property("a")));
+ }
+
+ obj.setProperty("a", QScriptValue());
+ {
+ QScriptValue ret = eng.evaluate(program);
+ QVERIFY(ret.isError());
+ }
+
+ QScriptValue obj2 = eng.newObject();
+ obj2.setProperty("a", 456);
+ ctx->pushScope(obj2);
+ {
+ QScriptValue ret = eng.evaluate(program);
+ QVERIFY(!ret.isError());
+ QVERIFY(ret.equals(obj2.property("a")));
+ }
+
+ ctx->popScope();
+ }
+
+ // Program that creates closure
+ {
+ QScriptProgram program("(function() { var count = 0; return function() { return count++; }; })");
+ QVERIFY(!program.isNull());
+ QScriptValue createCounter = eng.evaluate(program);
+ QVERIFY(createCounter.isFunction());
+ QScriptValue counter = createCounter.call();
+ QVERIFY(counter.isFunction());
+ {
+ QScriptValue ret = counter.call();
+ QVERIFY(ret.isNumber());
+ }
+ QScriptValue counter2 = createCounter.call();
+ QVERIFY(counter2.isFunction());
+ QVERIFY(!counter2.equals(counter));
+ {
+ QScriptValue ret = counter2.call();
+ QVERIFY(ret.isNumber());
+ }
+ }
+
+ // Program created in a function call, then executed later
+ {
+ QScriptValue fun = eng.newFunction(createProgram);
+ QScriptProgram program = qscriptvalue_cast<QScriptProgram>(
+ fun.call(QScriptValue(), QScriptValueList() << "a + 1"));
+ QVERIFY(!program.isNull());
+ eng.globalObject().setProperty("a", QScriptValue());
+ {
+ QScriptValue ret = eng.evaluate(program);
+ QVERIFY(ret.isError());
+ QCOMPARE(ret.toString(), QString::fromLatin1("ReferenceError: Can't find variable: a"));
+ }
+ eng.globalObject().setProperty("a", 122);
+ {
+ QScriptValue ret = eng.evaluate(program);
+ QVERIFY(!ret.isError());
+ QVERIFY(ret.isNumber());
+ QCOMPARE(ret.toInt32(), 123);
+ }
+ }
+
+ // Same program run in different engines
+ {
+ QString code("1 + 2");
+ QScriptProgram program(code);
+ QVERIFY(!program.isNull());
+ double expected = eng.evaluate(program).toNumber();
+ for (int x = 0; x < 2; ++x) {
+ QScriptEngine eng2;
+ for (int y = 0; y < 2; ++y) {
+ double ret = eng2.evaluate(program).toNumber();
+ QCOMPARE(ret, expected);
+ }
+ }
+ }
+
+ // No program
+ {
+ QScriptProgram program;
+ QVERIFY(program.isNull());
+ QScriptValue ret = eng.evaluate(program);
+ QVERIFY(!ret.isValid());
+ }
+}
+
static QRegExp minimal(QRegExp r) { r.setMinimal(true); return r; }
void tst_QScriptEngine::qRegExpInport_data()
diff --git a/tests/auto/qscriptengineagent/tst_qscriptengineagent.cpp b/tests/auto/qscriptengineagent/tst_qscriptengineagent.cpp
index 82c8ccd1ae..032c34b298 100644
--- a/tests/auto/qscriptengineagent/tst_qscriptengineagent.cpp
+++ b/tests/auto/qscriptengineagent/tst_qscriptengineagent.cpp
@@ -44,6 +44,7 @@
#include <QtScript/qscriptengineagent.h>
#include <QtScript/qscriptengine.h>
+#include <QtScript/qscriptprogram.h>
#include <qscriptvalueiterator.h>
//TESTED_CLASS=
@@ -110,6 +111,9 @@ private slots:
void extension();
void isEvaluatingInExtension();
void hasUncaughtException();
+ void evaluateProgram();
+ void evaluateProgram_SyntaxError();
+ void evaluateNullProgram();
private:
double m_testProperty;
@@ -2219,6 +2223,88 @@ void tst_QScriptEngineAgent::hasUncaughtException()
QVERIFY2(spy->isPass(), "At least one of a functionExit event should set hasUncaughtException flag.");
}
+void tst_QScriptEngineAgent::evaluateProgram()
+{
+ QScriptEngine eng;
+ QScriptProgram program("1 + 2", "foo.js", 123);
+ ScriptEngineSpy *spy = new ScriptEngineSpy(&eng);
+ qint64 scriptId = -1;
+ for (int x = 0; x < 10; ++x) {
+ spy->clear();
+ (void)eng.evaluate(program);
+ QCOMPARE(spy->count(), (x == 0) ? 4 : 3);
+
+ if (x == 0) {
+ // script is only loaded on first execution
+ QCOMPARE(spy->at(0).type, ScriptEngineEvent::ScriptLoad);
+ scriptId = spy->at(0).scriptId;
+ QVERIFY(scriptId != -1);
+ QCOMPARE(spy->at(0).script, program.sourceCode());
+ QCOMPARE(spy->at(0).fileName, program.fileName());
+ QCOMPARE(spy->at(0).lineNumber, program.firstLineNumber());
+ spy->removeFirst();
+ }
+
+ QCOMPARE(spy->at(0).type, ScriptEngineEvent::FunctionEntry); // evaluate()
+ QCOMPARE(spy->at(0).scriptId, scriptId);
+
+ QCOMPARE(spy->at(1).type, ScriptEngineEvent::PositionChange);
+ QCOMPARE(spy->at(1).scriptId, scriptId);
+ QCOMPARE(spy->at(1).lineNumber, program.firstLineNumber());
+
+ QCOMPARE(spy->at(2).type, ScriptEngineEvent::FunctionExit); // evaluate()
+ QCOMPARE(spy->at(2).scriptId, scriptId);
+ QVERIFY(spy->at(2).value.isNumber());
+ QCOMPARE(spy->at(2).value.toNumber(), qsreal(3));
+ }
+}
+
+void tst_QScriptEngineAgent::evaluateProgram_SyntaxError()
+{
+ QScriptEngine eng;
+ QScriptProgram program("this is not valid syntax", "foo.js", 123);
+ ScriptEngineSpy *spy = new ScriptEngineSpy(&eng);
+ qint64 scriptId = -1;
+ for (int x = 0; x < 10; ++x) {
+ spy->clear();
+ (void)eng.evaluate(program);
+ QCOMPARE(spy->count(), (x == 0) ? 8 : 7);
+
+ if (x == 0) {
+ // script is only loaded on first execution
+ QCOMPARE(spy->at(0).type, ScriptEngineEvent::ScriptLoad);
+ scriptId = spy->at(0).scriptId;
+ QVERIFY(scriptId != -1);
+ QCOMPARE(spy->at(0).script, program.sourceCode());
+ QCOMPARE(spy->at(0).fileName, program.fileName());
+ QCOMPARE(spy->at(0).lineNumber, program.firstLineNumber());
+ spy->removeFirst();
+ }
+
+ QCOMPARE(spy->at(0).type, ScriptEngineEvent::FunctionEntry); // evaluate()
+ QCOMPARE(spy->at(0).scriptId, scriptId);
+
+ QCOMPARE(spy->at(1).type, ScriptEngineEvent::ContextPush); // SyntaxError constructor
+ QCOMPARE(spy->at(2).type, ScriptEngineEvent::FunctionEntry); // SyntaxError constructor
+ QCOMPARE(spy->at(3).type, ScriptEngineEvent::FunctionExit); // SyntaxError constructor
+ QCOMPARE(spy->at(4).type, ScriptEngineEvent::ContextPop); // SyntaxError constructor
+
+ QCOMPARE(spy->at(5).type, ScriptEngineEvent::ExceptionThrow);
+ QVERIFY(spy->at(5).value.isError());
+ QCOMPARE(spy->at(5).value.toString(), QString::fromLatin1("SyntaxError: Parse error"));
+
+ QCOMPARE(spy->at(6).type, ScriptEngineEvent::FunctionExit); // evaluate()
+ QCOMPARE(spy->at(6).scriptId, scriptId);
+ }
+}
+
+void tst_QScriptEngineAgent::evaluateNullProgram()
+{
+ QScriptEngine eng;
+ ScriptEngineSpy *spy = new ScriptEngineSpy(&eng);
+ (void)eng.evaluate(QScriptProgram());
+ QCOMPARE(spy->count(), 0);
+}
QTEST_MAIN(tst_QScriptEngineAgent)
#include "tst_qscriptengineagent.moc"
diff --git a/tests/benchmarks/qscriptengine/tst_qscriptengine.cpp b/tests/benchmarks/qscriptengine/tst_qscriptengine.cpp
index 4f011c4261..8d5f6e6ef6 100644
--- a/tests/benchmarks/qscriptengine/tst_qscriptengine.cpp
+++ b/tests/benchmarks/qscriptengine/tst_qscriptengine.cpp
@@ -60,6 +60,8 @@ private slots:
void constructor();
void evaluate_data();
void evaluate();
+ void evaluateProgram_data();
+ void evaluateProgram();
void connectAndDisconnect();
void newObject();
void newQObject();
@@ -153,6 +155,22 @@ void tst_QScriptEngine::connectAndDisconnect()
}
}
+void tst_QScriptEngine::evaluateProgram_data()
+{
+ evaluate_data();
+}
+
+void tst_QScriptEngine::evaluateProgram()
+{
+ QFETCH(QString, code);
+ QScriptEngine engine;
+ QScriptProgram program(code);
+
+ QBENCHMARK {
+ (void)engine.evaluate(program);
+ }
+}
+
void tst_QScriptEngine::newObject()
{
QScriptEngine engine;
@@ -241,6 +259,5 @@ void tst_QScriptEngine::nativeCall()
}
}
-
QTEST_MAIN(tst_QScriptEngine)
#include "tst_qscriptengine.moc"