From d410ad364ec0b8877797145c68a4d7c3c98ce1c0 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 29 Jul 2011 10:25:44 +0200 Subject: [PATCH] Merge the QJSEngine and QJSValue development branch into master. This replaces the dependency to QtScript with two new builtin classes QJSValue and QJSEngine. This is still work in progress, development continues now in the master branch. Change-Id: I7f5487feb45c972f25a22b10cc81b9218b9805de Reviewed-on: http://codereview.qt.nokia.com/2299 Reviewed-by: Qt Sanity Bot Reviewed-by: Simon Hausmann --- modules/qt_declarative.pri | 2 +- src/3rdparty/v8 | 2 +- .../debugger/qdeclarativedebughelper.cpp | 4 +- src/declarative/debugger/qjsdebuggeragent.cpp | 296 +- src/declarative/debugger/qjsdebuggeragent_p.h | 19 +- src/declarative/declarative.pro | 2 +- .../items/context2d/qsgcontext2d.cpp | 6 +- .../items/context2d/qsgcontext2d_p.h | 6 +- src/declarative/items/qsgitem.cpp | 2 +- src/declarative/qml/qdeclarative.h | 8 +- src/declarative/qml/qdeclarativebinding.cpp | 2 +- .../qml/qdeclarativecompileddata.cpp | 4 - src/declarative/qml/qdeclarativecompiler.cpp | 8 +- src/declarative/qml/qdeclarativecompiler_p.h | 4 +- src/declarative/qml/qdeclarativecomponent.cpp | 3 +- src/declarative/qml/qdeclarativecomponent.h | 2 +- src/declarative/qml/qdeclarativecontext.cpp | 4 +- src/declarative/qml/qdeclarativecontext.h | 2 +- src/declarative/qml/qdeclarativecontext_p.h | 2 +- src/declarative/qml/qdeclarativedata_p.h | 2 +- src/declarative/qml/qdeclarativeengine.cpp | 11 +- src/declarative/qml/qdeclarativeengine.h | 7 +- src/declarative/qml/qdeclarativeengine_p.h | 5 +- .../qml/qdeclarativeexpression.cpp | 20 +- src/declarative/qml/qdeclarativemetatype.cpp | 14 +- src/declarative/qml/qdeclarativemetatype_p.h | 12 +- src/declarative/qml/qdeclarativeprivate.h | 8 +- .../qml/qdeclarativepropertycache.cpp | 8 +- .../qml/qdeclarativepropertycache_p.h | 5 +- .../qml/qdeclarativesqldatabase_p.h | 2 +- .../qml/qdeclarativetypeloader.cpp | 2 +- .../qml/qdeclarativetypeloader_p.h | 3 +- .../qml/qdeclarativetypenamecache_p.h | 2 - src/declarative/qml/qdeclarativevme.cpp | 4 +- src/declarative/qml/qdeclarativevme_p.h | 2 +- .../qml/qdeclarativevmemetaobject.cpp | 40 +- .../qml/qdeclarativeworkerscript.cpp | 10 +- .../qml/qdeclarativeworkerscript_p.h | 2 +- .../qml/qdeclarativexmlhttprequest.cpp | 5 +- .../qml/qdeclarativexmlhttprequest_p.h | 2 +- .../qml/v4/qdeclarativev4bindings.cpp | 44 +- .../qml/v4/qdeclarativev4irbuilder.cpp | 2 +- src/declarative/qml/v8/qjsconverter_p.h | 272 + src/declarative/qml/v8/qjsengine.cpp | 431 ++ src/declarative/qml/v8/qjsengine.h | 151 + src/declarative/qml/v8/qjsvalue.cpp | 1024 +++ src/declarative/qml/v8/qjsvalue.h | 165 + src/declarative/qml/v8/qjsvalue_impl_p.h | 1133 +++ src/declarative/qml/v8/qjsvalue_p.h | 213 + src/declarative/qml/v8/qjsvalueiterator.cpp | 294 + src/declarative/qml/v8/qjsvalueiterator.h | 64 + src/declarative/qml/v8/qscript_impl_p.h | 41 + src/declarative/qml/v8/qscriptisolate_p.h | 71 + .../qml/v8/qscriptoriginalglobalobject_p.h | 158 + src/declarative/qml/v8/qscriptshareddata_p.h | 151 + src/declarative/qml/v8/qscripttools_p.h | 216 + src/declarative/qml/v8/qv8bindings.cpp | 2 +- src/declarative/qml/v8/qv8engine.cpp | 751 +- src/declarative/qml/v8/qv8engine_impl_p.h | 131 + src/declarative/qml/v8/qv8engine_p.h | 108 +- src/declarative/qml/v8/qv8include.cpp | 2 +- src/declarative/qml/v8/qv8qobjectwrapper.cpp | 36 +- src/declarative/qml/v8/qv8typewrapper.cpp | 20 +- src/declarative/qml/v8/qv8variantwrapper.cpp | 38 + src/declarative/qml/v8/qv8variantwrapper_p.h | 4 + src/declarative/qml/v8/script.pri | 20 + src/declarative/qml/v8/v8.pri | 4 +- src/declarative/util/qdeclarativebind.cpp | 5 +- .../util/qdeclarativelistmodel.cpp | 4 +- .../folderlistmodel/folderlistmodel.pro | 2 +- src/imports/gestures/gestures.pro | 2 +- src/imports/inputcontext/inputcontext.pro | 2 +- src/imports/inputcontext/plugin.cpp | 2 +- src/imports/testlib/main.cpp | 6 +- src/imports/testlib/testlib.pro | 2 +- src/qmltest/qmltest.pro | 2 +- src/qmltest/quicktest.cpp | 7 +- .../graphicsitems/qdeclarativeitem.cpp | 2 +- src/qtquick1/qtquick1.pro | 2 +- src/qtquick1/util/qdeclarativebind.cpp | 5 +- src/qtquick1/util/qdeclarativelistmodel.cpp | 2 +- src/qtquick1/util/qdeclarativelistmodel_p.h | 2 +- src/qtquick1/util/qdeclarativelistmodel_p_p.h | 2 +- .../util/qdeclarativelistmodelworkeragent_p.h | 2 +- src/qtquick1/util/qdeclarativeview.cpp | 1 - ...on-method-to-the-Object-class-in-the.patch | 286 + ...Constructor-method-for-Object-in-the.patch | 397 + ...new-v8-api-to-check-if-a-value-is-an.patch | 63 + tests/auto/declarative/declarative.pro | 5 +- .../qdeclarativecomponent.pro | 2 +- .../qdeclarativeconnection.pro | 2 +- .../qdeclarativedebug/qdeclarativedebug.pro | 2 +- .../qdeclarativedebugclient.pro | 2 +- .../qdeclarativedebughelper_p.h | 4 +- .../qdeclarativedebughelper.pro | 4 +- .../qdeclarativedebugservice.pro | 2 +- .../qdeclarativeecmascript.pro | 4 +- .../qdeclarativeecmascript/testtypes.cpp | 10 +- .../qdeclarativeecmascript/testtypes.h | 10 +- .../tst_qdeclarativeecmascript.cpp | 25 +- .../qdeclarativeinstruction.pro | 4 +- .../qdeclarativelanguage.pro | 4 +- .../qdeclarativelistmodel.pro | 3 +- .../qdeclarativeproperty.pro | 2 +- .../qdeclarativeqt/qdeclarativeqt.pro | 4 +- .../qdeclarativescriptdebugging.pro | 4 +- .../qdeclarativesmoothedanimation.pro | 2 +- .../qdeclarativespringanimation.pro | 2 +- .../qdeclarativesqldatabase.pro | 4 +- .../qdeclarativestates/qdeclarativestates.pro | 2 +- .../qdeclarativev4/qdeclarativev4.pro | 4 +- .../qdeclarativevaluetypes.pro | 2 +- .../qdeclarativeworkerscript.pro | 4 +- .../tst_qdeclarativeworkerscript.cpp | 2 +- .../qdeclarativexmllistmodel.pro | 4 +- .../auto/declarative/qjsengine/qjsengine.pro | 21 + .../qjsengine/script/com/__init__.js | 9 + .../script/com/trolltech/__init__.js | 9 + .../com/trolltech/recursive/__init__.js | 1 + .../com/trolltech/syntaxerror/__init__.js | 5 + .../declarative/qjsengine/tst_qjsengine.cpp | 6491 +++++++++++++++++ tests/auto/declarative/qjsvalue/qjsvalue.pro | 10 + .../declarative/qjsvalue/tst_qjsvalue.cpp | 4109 +++++++++++ .../auto/declarative/qjsvalue/tst_qjsvalue.h | 205 + .../qjsvalueiterator/qjsvalueiterator.pro | 5 + .../qjsvalueiterator/tst_qjsvalueiterator.cpp | 527 ++ .../qsganimatedimage/qsganimatedimage.pro | 1 - .../qsgborderimage/qsgborderimage.pro | 1 - .../declarative/qsgflickable/qsgflickable.pro | 1 - .../declarative/qsgflipable/qsgflipable.pro | 1 - .../declarative/qsggridview/qsggridview.pro | 1 - tests/auto/declarative/qsgitem2/qsgitem.pro | 1 - .../declarative/qsglistview/qsglistview.pro | 1 - .../declarative/qsgpathview/qsgpathview.pro | 1 - .../qsgpositioners/qsgpositioners.pro | 1 - tests/auto/declarative/qsgtext/qsgtext.pro | 1 - .../declarative/qsgtextedit/qsgtextedit.pro | 1 - .../declarative/qsgtextinput/qsgtextinput.pro | 1 - .../qsgvisualdatamodel/qsgvisualdatamodel.pro | 1 - .../qdeclarativeconnection.pro | 2 +- .../qdeclarativeflickable.pro | 2 +- .../qdeclarativeflipable.pro | 2 +- .../qdeclarativegridview.pro | 2 +- .../qdeclarativelistmodel.pro | 3 +- .../qdeclarativelistview.pro | 2 +- .../qdeclarativepathview.pro | 2 +- .../qdeclarativesmoothedanimation.pro | 2 +- .../qdeclarativespringanimation.pro | 2 +- .../qdeclarativestates/qdeclarativestates.pro | 2 +- .../qdeclarativetext/qdeclarativetext.pro | 2 +- .../qdeclarativevisualdatamodel.pro | 2 +- .../qdeclarativexmllistmodel.pro | 4 +- .../declarative/holistic/holistic.pro | 2 +- .../declarative/holistic/testtypes.h | 2 +- .../benchmarks/declarative/script/script.pro | 2 +- tools/qmlviewer/qml.pri | 2 +- 156 files changed, 17933 insertions(+), 516 deletions(-) create mode 100644 src/declarative/qml/v8/qjsconverter_p.h create mode 100644 src/declarative/qml/v8/qjsengine.cpp create mode 100644 src/declarative/qml/v8/qjsengine.h create mode 100644 src/declarative/qml/v8/qjsvalue.cpp create mode 100644 src/declarative/qml/v8/qjsvalue.h create mode 100644 src/declarative/qml/v8/qjsvalue_impl_p.h create mode 100644 src/declarative/qml/v8/qjsvalue_p.h create mode 100644 src/declarative/qml/v8/qjsvalueiterator.cpp create mode 100644 src/declarative/qml/v8/qjsvalueiterator.h create mode 100644 src/declarative/qml/v8/qscript_impl_p.h create mode 100644 src/declarative/qml/v8/qscriptisolate_p.h create mode 100644 src/declarative/qml/v8/qscriptoriginalglobalobject_p.h create mode 100644 src/declarative/qml/v8/qscriptshareddata_p.h create mode 100644 src/declarative/qml/v8/qscripttools_p.h create mode 100644 src/declarative/qml/v8/qv8engine_impl_p.h create mode 100644 src/declarative/qml/v8/script.pri create mode 100644 src/v8/0009-Add-CallAsFunction-method-to-the-Object-class-in-the.patch create mode 100644 src/v8/0010-Implement-CallAsConstructor-method-for-Object-in-the.patch create mode 100644 src/v8/0011-QtScript-V8-Add-new-v8-api-to-check-if-a-value-is-an.patch create mode 100644 tests/auto/declarative/qjsengine/qjsengine.pro create mode 100644 tests/auto/declarative/qjsengine/script/com/__init__.js create mode 100644 tests/auto/declarative/qjsengine/script/com/trolltech/__init__.js create mode 100644 tests/auto/declarative/qjsengine/script/com/trolltech/recursive/__init__.js create mode 100644 tests/auto/declarative/qjsengine/script/com/trolltech/syntaxerror/__init__.js create mode 100644 tests/auto/declarative/qjsengine/tst_qjsengine.cpp create mode 100644 tests/auto/declarative/qjsvalue/qjsvalue.pro create mode 100644 tests/auto/declarative/qjsvalue/tst_qjsvalue.cpp create mode 100644 tests/auto/declarative/qjsvalue/tst_qjsvalue.h create mode 100644 tests/auto/declarative/qjsvalueiterator/qjsvalueiterator.pro create mode 100644 tests/auto/declarative/qjsvalueiterator/tst_qjsvalueiterator.cpp diff --git a/modules/qt_declarative.pri b/modules/qt_declarative.pri index ed1750e55c..ef7db54a2b 100644 --- a/modules/qt_declarative.pri +++ b/modules/qt_declarative.pri @@ -11,7 +11,7 @@ QT.declarative.sources = $$QT_MODULE_BASE/src/declarative QT.declarative.libs = $$QT_MODULE_LIB_BASE QT.declarative.plugins = $$QT_MODULE_PLUGIN_BASE QT.declarative.imports = $$QT_MODULE_IMPORT_BASE -QT.declarative.depends = gui script network opengl xmlpatterns +QT.declarative.depends = gui network opengl xmlpatterns QT.declarative.DEFINES = QT_DECLARATIVE_LIB QT_CONFIG += declarative diff --git a/src/3rdparty/v8 b/src/3rdparty/v8 index bec11b8b7f..472c04c9e7 160000 --- a/src/3rdparty/v8 +++ b/src/3rdparty/v8 @@ -1 +1 @@ -Subproject commit bec11b8b7f89d135e7d9a823ac4fe98c70d017cf +Subproject commit 472c04c9e7a64e8734c76d2cf97a7cc5b773b788 diff --git a/src/declarative/debugger/qdeclarativedebughelper.cpp b/src/declarative/debugger/qdeclarativedebughelper.cpp index 6eea82c948..5f5d8754bb 100644 --- a/src/declarative/debugger/qdeclarativedebughelper.cpp +++ b/src/declarative/debugger/qdeclarativedebughelper.cpp @@ -39,12 +39,10 @@ ** ****************************************************************************/ -#include - #include "private/qdeclarativedebughelper_p.h" #include -#include +#include #include #include diff --git a/src/declarative/debugger/qjsdebuggeragent.cpp b/src/declarative/debugger/qjsdebuggeragent.cpp index 3169f91b59..f47def802a 100644 --- a/src/declarative/debugger/qjsdebuggeragent.cpp +++ b/src/declarative/debugger/qjsdebuggeragent.cpp @@ -46,9 +46,9 @@ #include #include #include -#include -#include -#include +#include + +#include QT_BEGIN_NAMESPACE @@ -61,9 +61,9 @@ class QJSDebuggerAgentPrivate void continueExec(); void recordKnownObjects(const QList &); - QList getLocals(QScriptContext *); + QList getLocals(void *); void positionChange(qint64 scriptId, int lineNumber, int columnNumber); - QScriptEngine *engine() { return q->engine(); } + QJSEngine *engine() { return q->engine(); } void stopped(); public: @@ -111,7 +111,7 @@ class SetupExecEnv } // anonymous namespace static JSAgentWatchData fromScriptValue(const QString &expression, - const QScriptValue &value) + const QJSValue &value) { static const QString arrayStr = QCoreApplication::translate ("Debugger::JSAgentWatchData", "[Array of length %1]"); @@ -123,7 +123,7 @@ static JSAgentWatchData fromScriptValue(const QString &expression, data.name = data.exp; data.hasChildren = false; data.value = value.toString().toUtf8(); - data.objectId = value.objectId(); + // data.objectId = value.objectId(); if (value.isArray()) { data.type = "Array"; data.value = arrayStr.arg(value.property(QLatin1String("length")).toString()).toUtf8(); @@ -167,30 +167,30 @@ static JSAgentWatchData fromScriptValue(const QString &expression, return data; } -static QList expandObject(const QScriptValue &object) +static QList expandObject(const QJSValue &object) { QList result; - QScriptValueIterator it(object); - while (it.hasNext()) { - it.next(); - if (it.flags() & QScriptValue::SkipInEnumeration) - continue; - if (/*object.isQObject() &&*/ it.value().isFunction()) { - // Cosmetics: skip all functions and slot, there are too many of them, - // and it is not useful information in the debugger. - continue; - } - JSAgentWatchData data = fromScriptValue(it.name(), it.value()); - result.append(data); - } - if (result.isEmpty()) { - JSAgentWatchData data; - data.name = ""; - data.hasChildren = false; - data.value = " "; - data.objectId = 0; - result.append(data); - } +// QScriptValueIterator it(object); +// while (it.hasNext()) { +// it.next(); +// if (it.flags() & QScriptValue::SkipInEnumeration) +// continue; +// if (/*object.isQObject() &&*/ it.value().isFunction()) { +// // Cosmetics: skip all functions and slot, there are too many of them, +// // and it is not useful information in the debugger. +// continue; +// } +// JSAgentWatchData data = fromScriptValue(it.name(), it.value()); +// result.append(data); +// } +// if (result.isEmpty()) { +// JSAgentWatchData data; +// data.name = ""; +// data.hasChildren = false; +// data.value = " "; +// data.objectId = 0; +// result.append(data); +// } return result; } @@ -206,20 +206,20 @@ void QJSDebuggerAgentPrivate::recordKnownObjects(const QList& knownObjectIds << data.objectId; } -QList QJSDebuggerAgentPrivate::getLocals(QScriptContext *ctx) +QList QJSDebuggerAgentPrivate::getLocals(void *ctx) { QList locals; - if (ctx) { - QScriptValue activationObject = ctx->activationObject(); - QScriptValue thisObject = ctx->thisObject(); - locals = expandObject(activationObject); - if (thisObject.isObject() - && thisObject.objectId() != engine()->globalObject().objectId() - && QScriptValueIterator(thisObject).hasNext()) - locals.prepend(fromScriptValue(QLatin1String("this"), thisObject)); - recordKnownObjects(locals); - knownObjectIds << activationObject.objectId(); - } +// if (ctx) { +// QScriptValue activationObject = ctx->activationObject(); +// QScriptValue thisObject = ctx->thisObject(); +// locals = expandObject(activationObject); +// if (thisObject.isObject() +// && thisObject.objectId() != engine()->globalObject().objectId() +// && QScriptValueIterator(thisObject).hasNext()) +// locals.prepend(fromScriptValue(QLatin1String("this"), thisObject)); +// recordKnownObjects(locals); +// knownObjectIds << activationObject.objectId(); +// } return locals; } @@ -228,20 +228,18 @@ QList QJSDebuggerAgentPrivate::getLocals(QScriptContext *ctx) report debugging-related events (e.g. step completion) to the given \a backend. */ -QJSDebuggerAgent::QJSDebuggerAgent(QScriptEngine *engine, QObject *parent) +QJSDebuggerAgent::QJSDebuggerAgent(QJSEngine *engine, QObject *parent) : QObject(parent) - , QScriptEngineAgent(engine) , d(new QJSDebuggerAgentPrivate(this)) { - QJSDebuggerAgent::engine()->setAgent(this); + //QJSDebuggerAgent::engine()->setAgent(this); } QJSDebuggerAgent::QJSDebuggerAgent(QDeclarativeEngine *engine, QObject *parent) : QObject(parent) - , QScriptEngineAgent(0) , d(new QJSDebuggerAgentPrivate(this)) { - QJSDebuggerAgent::engine()->setAgent(this); + //QJSDebuggerAgent::engine()->setAgent(this); } /*! @@ -249,7 +247,7 @@ QJSDebuggerAgent::QJSDebuggerAgent(QDeclarativeEngine *engine, QObject *parent) */ QJSDebuggerAgent::~QJSDebuggerAgent() { - engine()->setAgent(0); + //engine()->setAgent(0); delete d; } @@ -317,9 +315,9 @@ QList QJSDebuggerAgent::expandObjectById(quint64 objectId) { SetupExecEnv execEnv(d); - QScriptValue v; - if (d->knownObjectIds.contains(objectId)) - v = engine()->objectById(objectId); + QJSValue v; +// if (d->knownObjectIds.contains(objectId)) +// v = engine()->objectById(objectId); QList result = expandObject(v); d->recordKnownObjects(result); @@ -329,21 +327,21 @@ QList QJSDebuggerAgent::expandObjectById(quint64 objectId) QList QJSDebuggerAgent::locals() { SetupExecEnv execEnv(d); - return d->getLocals(engine()->currentContext()); + return d->getLocals(0/*engine()->currentContext()*/); } QList QJSDebuggerAgent::localsAtFrame(int frameId) { SetupExecEnv execEnv(d); - int deep = 0; - QScriptContext *ctx = engine()->currentContext(); - while (ctx && deep < frameId) { - ctx = ctx->parentContext(); - deep++; - } +// int deep = 0; +// QScriptContext *ctx = engine()->currentContext(); +// while (ctx && deep < frameId) { +// ctx = ctx->parentContext(); +// deep++; +// } - return d->getLocals(ctx); + return d->getLocals(0/*ctx*/); } QList QJSDebuggerAgent::backtrace() @@ -352,37 +350,37 @@ QList QJSDebuggerAgent::backtrace() QList backtrace; - for (QScriptContext *ctx = engine()->currentContext(); ctx; ctx = ctx->parentContext()) { - QScriptContextInfo info(ctx); - - JSAgentStackData frame; - frame.functionName = info.functionName().toUtf8(); - if (frame.functionName.isEmpty()) { - if (ctx->parentContext()) { - switch (info.functionType()) { - case QScriptContextInfo::ScriptFunction: - frame.functionName = ""; - break; - case QScriptContextInfo::NativeFunction: - frame.functionName = ""; - break; - case QScriptContextInfo::QtFunction: - case QScriptContextInfo::QtPropertyFunction: - frame.functionName = ""; - break; - } - } else { - frame.functionName = ""; - } - } - frame.lineNumber = info.lineNumber(); - // if the line number is unknown, fallback to the function line number - if (frame.lineNumber == -1) - frame.lineNumber = info.functionStartLineNumber(); - - frame.fileUrl = info.fileName().toUtf8(); - backtrace.append(frame); - } +// for (QScriptContext *ctx = engine()->currentContext(); ctx; ctx = ctx->parentContext()) { +// QScriptContextInfo info(ctx); + +// JSAgentStackData frame; +// frame.functionName = info.functionName().toUtf8(); +// if (frame.functionName.isEmpty()) { +// if (ctx->parentContext()) { +// switch (info.functionType()) { +// case QScriptContextInfo::ScriptFunction: +// frame.functionName = ""; +// break; +// case QScriptContextInfo::NativeFunction: +// frame.functionName = ""; +// break; +// case QScriptContextInfo::QtFunction: +// case QScriptContextInfo::QtPropertyFunction: +// frame.functionName = ""; +// break; +// } +// } else { +// frame.functionName = ""; +// } +// } +// frame.lineNumber = info.lineNumber(); +// // if the line number is unknown, fallback to the function line number +// if (frame.lineNumber == -1) +// frame.lineNumber = info.functionStartLineNumber(); + +// frame.fileUrl = info.fileName().toUtf8(); +// backtrace.append(frame); +// } return backtrace; } @@ -405,9 +403,9 @@ void QJSDebuggerAgent::setProperty(qint64 objectId, SetupExecEnv execEnv(d); if (d->knownObjectIds.contains(objectId)) { - QScriptValue object = engine()->objectById(objectId); + QJSValue object;// = engine()->objectById(objectId); if (object.isObject()) { - QScriptValue result = engine()->evaluate(value); + QJSValue result = engine()->evaluate(value); object.setProperty(property, result); } } @@ -457,7 +455,7 @@ void QJSDebuggerAgent::functionEntry(qint64 scriptId) /*! \reimp */ -void QJSDebuggerAgent::functionExit(qint64 scriptId, const QScriptValue &returnValue) +void QJSDebuggerAgent::functionExit(qint64 scriptId, const QJSValue &returnValue) { Q_UNUSED(scriptId); Q_UNUSED(returnValue); @@ -476,53 +474,53 @@ void QJSDebuggerAgentPrivate::positionChange(qint64 scriptId, int lineNumber, in { Q_UNUSED(columnNumber); - if (state == StoppedState) - return; //no re-entrency - - // check breakpoints - if (!breakpoints.isEmpty()) { - QHash::const_iterator it = filenames.constFind(scriptId); - QScriptContext *ctx = engine()->currentContext(); - QScriptContextInfo info(ctx); - if (it == filenames.constEnd()) { - // It is possible that the scripts are loaded before the agent is attached - QString filename = info.fileName(); - - JSAgentStackData frame; - frame.functionName = info.functionName().toUtf8(); - - QPair key = qMakePair(filename, lineNumber); - it = filenames.insert(scriptId, filename); - } - - const QString filePath = it.value(); - JSAgentBreakpoints bps = fileNameToBreakpoints.values(fileName(filePath)).toSet(); - - foreach (const JSAgentBreakpointData &bp, bps) { - if (bp.lineNumber == lineNumber) { - stopped(); - return; - } - } - } - - switch (state) { - case NoState: - case StoppedState: - // Do nothing - break; - case SteppingOutState: - if (stepDepth >= 0) - break; - //fallthough - case SteppingOverState: - if (stepDepth > 0) - break; - //fallthough - case SteppingIntoState: - stopped(); - break; - } +// if (state == StoppedState) +// return; //no re-entrency + +// // check breakpoints +// if (!breakpoints.isEmpty()) { +// QHash::const_iterator it = filenames.constFind(scriptId); +// QScriptContext *ctx = engine()->currentContext(); +// QScriptContextInfo info(ctx); +// if (it == filenames.constEnd()) { +// // It is possible that the scripts are loaded before the agent is attached +// QString filename = info.fileName(); + +// JSAgentStackData frame; +// frame.functionName = info.functionName().toUtf8(); + +// QPair key = qMakePair(filename, lineNumber); +// it = filenames.insert(scriptId, filename); +// } + +// const QString filePath = it.value(); +// JSAgentBreakpoints bps = fileNameToBreakpoints.values(fileName(filePath)).toSet(); + +// foreach (const JSAgentBreakpointData &bp, bps) { +// if (bp.lineNumber == lineNumber) { +// stopped(); +// return; +// } +// } +// } + +// switch (state) { +// case NoState: +// case StoppedState: +// // Do nothing +// break; +// case SteppingOutState: +// if (stepDepth >= 0) +// break; +// //fallthough +// case SteppingOverState: +// if (stepDepth > 0) +// break; +// //fallthough +// case SteppingIntoState: +// stopped(); +// break; +// } } @@ -530,7 +528,7 @@ void QJSDebuggerAgentPrivate::positionChange(qint64 scriptId, int lineNumber, in \reimp */ void QJSDebuggerAgent::exceptionThrow(qint64 scriptId, - const QScriptValue &exception, + const QJSValue &exception, bool hasHandler) { Q_UNUSED(scriptId); @@ -546,30 +544,16 @@ void QJSDebuggerAgent::exceptionThrow(qint64 scriptId, /*! \reimp */ -void QJSDebuggerAgent::exceptionCatch(qint64 scriptId, const QScriptValue &exception) +void QJSDebuggerAgent::exceptionCatch(qint64 scriptId, const QJSValue &exception) { Q_UNUSED(scriptId); Q_UNUSED(exception); } -bool QJSDebuggerAgent::supportsExtension(Extension extension) const -{ - return extension == QScriptEngineAgent::DebuggerInvocationRequest; -} - -QVariant QJSDebuggerAgent::extension(Extension extension, const QVariant &argument) -{ - if (extension == QScriptEngineAgent::DebuggerInvocationRequest) { - d->stopped(); - return QVariant(); - } - return QScriptEngineAgent::extension(extension, argument); -} - void QJSDebuggerAgentPrivate::stopped() { bool becauseOfException = false; - const QScriptValue &exception = QScriptValue(); + const QJSValue &exception = QJSValue(); knownObjectIds.clear(); state = StoppedState; diff --git a/src/declarative/debugger/qjsdebuggeragent_p.h b/src/declarative/debugger/qjsdebuggeragent_p.h index 309588eb2f..30cbfe67b4 100644 --- a/src/declarative/debugger/qjsdebuggeragent_p.h +++ b/src/declarative/debugger/qjsdebuggeragent_p.h @@ -53,11 +53,11 @@ // We mean it. // -#include #include +#include QT_BEGIN_NAMESPACE -class QScriptValue; +class QJSValue; class QDeclarativeEngine; QT_END_NAMESPACE @@ -136,16 +136,17 @@ inline uint qHash(const JSAgentBreakpointData &b) } -class QJSDebuggerAgent : public QObject, public QScriptEngineAgent +class QJSDebuggerAgent : public QObject { Q_OBJECT public: - QJSDebuggerAgent(QScriptEngine *engine, QObject *parent = 0); + QJSDebuggerAgent(QJSEngine *engine, QObject *parent = 0); QJSDebuggerAgent(QDeclarativeEngine *engine, QObject *parent = 0); ~QJSDebuggerAgent(); bool isInitialized() const; + QJSEngine * engine() {return 0; } void setBreakpoints(const JSAgentBreakpoints &); void setWatchExpressions(const QStringList &); @@ -175,20 +176,16 @@ class QJSDebuggerAgent : public QObject, public QScriptEngineAgent void functionEntry(qint64 scriptId); void functionExit(qint64 scriptId, - const QScriptValue &returnValue); + const QJSValue &returnValue); void positionChange(qint64 scriptId, int lineNumber, int columnNumber); void exceptionThrow(qint64 scriptId, - const QScriptValue &exception, + const QJSValue &exception, bool hasHandler); void exceptionCatch(qint64 scriptId, - const QScriptValue &exception); - - bool supportsExtension(Extension extension) const; - QVariant extension(Extension extension, - const QVariant &argument = QVariant()); + const QJSValue &exception); Q_SIGNALS: void stopped(bool becauseOfException, diff --git a/src/declarative/declarative.pro b/src/declarative/declarative.pro index aa638e468e..2d9248ef66 100644 --- a/src/declarative/declarative.pro +++ b/src/declarative/declarative.pro @@ -6,7 +6,7 @@ QPRO_PWD = $$PWD CONFIG += module MODULE_PRI += ../../modules/qt_declarative.pri -QT = core-private gui-private script-private network script opengl-private +QT = core-private gui-private network opengl-private contains(QT_CONFIG, svg): QT += svg DEFINES += QT_BUILD_DECLARATIVE_LIB QT_NO_URL_CAST_FROM_STRING win32-msvc*|win32-icc:QMAKE_LFLAGS += /BASE:0x66000000 diff --git a/src/declarative/items/context2d/qsgcontext2d.cpp b/src/declarative/items/context2d/qsgcontext2d.cpp index 97234a6315..4cfe1ba012 100644 --- a/src/declarative/items/context2d/qsgcontext2d.cpp +++ b/src/declarative/items/context2d/qsgcontext2d.cpp @@ -3361,7 +3361,7 @@ void QSGContext2D::release() } } -void QSGContext2D::processCommands(const QScriptValue& commands) +void QSGContext2D::processCommands(const QJSValue& commands) { #ifdef QSGCANVASITEM_DEBUG QElapsedTimer t; @@ -3369,7 +3369,7 @@ void QSGContext2D::processCommands(const QScriptValue& commands) #endif int ii = 0; if (commands.isArray()) { - QScriptValue cmd = commands.property(ii); + QJSValue cmd = commands.property(ii); while(cmd.isValid()) { processCommand(cmd); ii++; @@ -3460,7 +3460,7 @@ bool QSGContext2D::event(QEvent *e) return QObject::event(e); } -void QSGContext2D::processCommand(const QScriptValue& cmd) +void QSGContext2D::processCommand(const QJSValue& cmd) { int action = cmd.property(0).toInt32(); switch (action) { diff --git a/src/declarative/items/context2d/qsgcontext2d_p.h b/src/declarative/items/context2d/qsgcontext2d_p.h index 335d954fc1..f8fc9b75a2 100644 --- a/src/declarative/items/context2d/qsgcontext2d_p.h +++ b/src/declarative/items/context2d/qsgcontext2d_p.h @@ -55,7 +55,7 @@ #include #include #include -#include +#include #include #include #include @@ -323,7 +323,7 @@ public slots: void paint(QPainter* painter); void sync(); - void processCommands(const QScriptValue& commands); + void processCommands(const QJSValue& commands); signals: void changed(); void painted(); @@ -385,7 +385,7 @@ public slots: virtual bool event(QEvent *); private: - void processCommand(const QScriptValue& command); + void processCommand(const QJSValue& command); Q_DECLARE_PRIVATE(QSGContext2D) }; diff --git a/src/declarative/items/qsgitem.cpp b/src/declarative/items/qsgitem.cpp index 55d84a7fa5..043c568a82 100644 --- a/src/declarative/items/qsgitem.cpp +++ b/src/declarative/items/qsgitem.cpp @@ -42,7 +42,7 @@ #include "qsgitem.h" #include "qsgcanvas.h" -#include +#include #include "qsgcanvas_p.h" #include "qsgevent.h" diff --git a/src/declarative/qml/qdeclarative.h b/src/declarative/qml/qdeclarative.h index 38b2a841ba..4c962f890e 100644 --- a/src/declarative/qml/qdeclarative.h +++ b/src/declarative/qml/qdeclarative.h @@ -395,8 +395,8 @@ int qmlRegisterCustomType(const char *uri, int versionMajor, int versionMinor, class QDeclarativeContext; class QDeclarativeEngine; -class QScriptValue; -class QScriptEngine; +class QJSValue; +class QJSEngine; Q_DECLARATIVE_EXPORT void qmlExecuteDeferred(QObject *); Q_DECLARATIVE_EXPORT QDeclarativeContext *qmlContext(const QObject *); Q_DECLARATIVE_EXPORT QDeclarativeEngine *qmlEngine(const QObject *); @@ -454,7 +454,7 @@ Q_DECLARATIVE_EXPORT void qmlRegisterBaseTypes(const char *uri, int versionMajor \endqml */ inline int qmlRegisterModuleApi(const char *uri, int versionMajor, int versionMinor, - QScriptValue (*callback)(QDeclarativeEngine *, QScriptEngine *)) + QJSValue (*callback)(QDeclarativeEngine *, QJSEngine *)) { QDeclarativePrivate::RegisterModuleApi api = { 0, @@ -537,7 +537,7 @@ inline int qmlRegisterModuleApi(const char *uri, int versionMajor, int versionMi \endqml */ inline int qmlRegisterModuleApi(const char *uri, int versionMajor, int versionMinor, - QObject *(*callback)(QDeclarativeEngine *, QScriptEngine *)) + QObject *(*callback)(QDeclarativeEngine *, QJSEngine *)) { QDeclarativePrivate::RegisterModuleApi api = { 0, diff --git a/src/declarative/qml/qdeclarativebinding.cpp b/src/declarative/qml/qdeclarativebinding.cpp index 684726d346..069744153a 100644 --- a/src/declarative/qml/qdeclarativebinding.cpp +++ b/src/declarative/qml/qdeclarativebinding.cpp @@ -367,7 +367,7 @@ void QDeclarativeBinding::update(QDeclarativePropertyPrivate::WriteFlags flags) bool isUndefined = false; v8::HandleScope handle_scope; - v8::Context::Scope scope(ep->v8engine.context()); + v8::Context::Scope scope(ep->v8engine()->context()); v8::Local result = d->v8value(0, &isUndefined); bool needsErrorData = false; diff --git a/src/declarative/qml/qdeclarativecompileddata.cpp b/src/declarative/qml/qdeclarativecompileddata.cpp index d08a808d77..da7c3fea7b 100644 --- a/src/declarative/qml/qdeclarativecompileddata.cpp +++ b/src/declarative/qml/qdeclarativecompileddata.cpp @@ -126,7 +126,6 @@ QDeclarativeCompiledData::~QDeclarativeCompiledData() if (rootPropertyCache) rootPropertyCache->release(); - qDeleteAll(cachedPrograms); qDeleteAll(cachedClosures); for (int ii = 0; ii < v8bindings.count(); ++ii) @@ -135,12 +134,9 @@ QDeclarativeCompiledData::~QDeclarativeCompiledData() void QDeclarativeCompiledData::clear() { - qDeleteAll(cachedPrograms); qDeleteAll(cachedClosures); for (int ii = 0; ii < cachedClosures.count(); ++ii) cachedClosures[ii] = 0; - for (int ii = 0; ii < cachedPrograms.count(); ++ii) - cachedPrograms[ii] = 0; } const QMetaObject *QDeclarativeCompiledData::TypeReference::metaObject() const diff --git a/src/declarative/qml/qdeclarativecompiler.cpp b/src/declarative/qml/qdeclarativecompiler.cpp index 7a15aed460..5e1d3f60a9 100644 --- a/src/declarative/qml/qdeclarativecompiler.cpp +++ b/src/declarative/qml/qdeclarativecompiler.cpp @@ -2371,7 +2371,7 @@ bool QDeclarativeCompiler::checkDynamicMeta(QDeclarativeParser::Object *obj) if (propName.at(0).isUpper()) COMPILE_EXCEPTION(&prop, tr("Property names cannot begin with an upper case letter")); - if (enginePrivate->v8engine.illegalNames().contains(propName)) + if (enginePrivate->v8engine()->illegalNames().contains(propName)) COMPILE_EXCEPTION(&prop, tr("Illegal property name")); propNames.insert(prop.name); @@ -2384,7 +2384,7 @@ bool QDeclarativeCompiler::checkDynamicMeta(QDeclarativeParser::Object *obj) QString nameStr = QString::fromUtf8(name); if (nameStr.at(0).isUpper()) COMPILE_EXCEPTION(obj, tr("Signal names cannot begin with an upper case letter")); - if (enginePrivate->v8engine.illegalNames().contains(nameStr)) + if (enginePrivate->v8engine()->illegalNames().contains(nameStr)) COMPILE_EXCEPTION(obj, tr("Illegal signal name")); methodNames.insert(name); } @@ -2395,7 +2395,7 @@ bool QDeclarativeCompiler::checkDynamicMeta(QDeclarativeParser::Object *obj) QString nameStr = QString::fromUtf8(name); if (nameStr.at(0).isUpper()) COMPILE_EXCEPTION(obj, tr("Method names cannot begin with an upper case letter")); - if (enginePrivate->v8engine.illegalNames().contains(nameStr)) + if (enginePrivate->v8engine()->illegalNames().contains(nameStr)) COMPILE_EXCEPTION(obj, tr("Illegal method name")); methodNames.insert(name); } @@ -2689,7 +2689,7 @@ bool QDeclarativeCompiler::checkValidId(QDeclarativeParser::Value *v, const QStr } - if (enginePrivate->v8engine.illegalNames().contains(val)) + if (enginePrivate->v8engine()->illegalNames().contains(val)) COMPILE_EXCEPTION(v, tr( "ID illegally masks global JavaScript property")); return true; diff --git a/src/declarative/qml/qdeclarativecompiler_p.h b/src/declarative/qml/qdeclarativecompiler_p.h index a1dfabbd46..a2b959a568 100644 --- a/src/declarative/qml/qdeclarativecompiler_p.h +++ b/src/declarative/qml/qdeclarativecompiler_p.h @@ -76,7 +76,6 @@ class QDeclarativeComponent; class QDeclarativeContext; class QDeclarativeContextData; -class QScriptProgram; class Q_AUTOTEST_EXPORT QDeclarativeCompiledData : public QDeclarativeRefCount, public QDeclarativeCleanup { public: @@ -112,8 +111,7 @@ class Q_AUTOTEST_EXPORT QDeclarativeCompiledData : public QDeclarativeRefCount, QList primitives; QList datas; QByteArray bytecode; - QList cachedPrograms; - QList cachedClosures; + QList cachedClosures; QList propertyCaches; QList contextCaches; QList scripts; diff --git a/src/declarative/qml/qdeclarativecomponent.cpp b/src/declarative/qml/qdeclarativecomponent.cpp index b6bcc64afa..ca7f3e0f74 100644 --- a/src/declarative/qml/qdeclarativecomponent.cpp +++ b/src/declarative/qml/qdeclarativecomponent.cpp @@ -54,7 +54,6 @@ #include "private/qdeclarativescriptparser_p.h" #include "private/qdeclarativedebugtrace_p.h" #include "private/qdeclarativeenginedebug_p.h" -#include #include #include @@ -672,7 +671,7 @@ void QDeclarativeComponent::createObject(QDeclarativeV8Function *args) QDeclarativeEngine *engine = d->engine; QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine); - QV8Engine *v8engine = &ep->v8engine; + QV8Engine *v8engine = ep->v8engine(); QDeclarativeContext *ctxt = creationContext(); if (!ctxt) ctxt = engine->rootContext(); diff --git a/src/declarative/qml/qdeclarativecomponent.h b/src/declarative/qml/qdeclarativecomponent.h index bb4d886914..a3457d1446 100644 --- a/src/declarative/qml/qdeclarativecomponent.h +++ b/src/declarative/qml/qdeclarativecomponent.h @@ -47,7 +47,7 @@ #include #include -#include +#include QT_BEGIN_HEADER diff --git a/src/declarative/qml/qdeclarativecontext.cpp b/src/declarative/qml/qdeclarativecontext.cpp index d625a1fd98..ff6e628c66 100644 --- a/src/declarative/qml/qdeclarativecontext.cpp +++ b/src/declarative/qml/qdeclarativecontext.cpp @@ -50,12 +50,10 @@ #include "private/qdeclarativev4bindings_p.h" #include "private/qv8bindings_p.h" -#include +#include #include #include -#include - QT_BEGIN_NAMESPACE QDeclarativeContextPrivate::QDeclarativeContextPrivate() diff --git a/src/declarative/qml/qdeclarativecontext.h b/src/declarative/qml/qdeclarativecontext.h index d8e8506dad..9c2fd01645 100644 --- a/src/declarative/qml/qdeclarativecontext.h +++ b/src/declarative/qml/qdeclarativecontext.h @@ -44,7 +44,7 @@ #include #include -#include +#include #include #include diff --git a/src/declarative/qml/qdeclarativecontext_p.h b/src/declarative/qml/qdeclarativecontext_p.h index bb9c2ada02..5f4072130a 100644 --- a/src/declarative/qml/qdeclarativecontext_p.h +++ b/src/declarative/qml/qdeclarativecontext_p.h @@ -63,7 +63,7 @@ #include "private/qdeclarativeparser_p.h" #include -#include +#include #include #include diff --git a/src/declarative/qml/qdeclarativedata_p.h b/src/declarative/qml/qdeclarativedata_p.h index 9fe5e732b7..aebea738bd 100644 --- a/src/declarative/qml/qdeclarativedata_p.h +++ b/src/declarative/qml/qdeclarativedata_p.h @@ -53,7 +53,7 @@ // We mean it. // -#include +#include #include #include diff --git a/src/declarative/qml/qdeclarativeengine.cpp b/src/declarative/qml/qdeclarativeengine.cpp index cf672ef337..a2f724e3f8 100644 --- a/src/declarative/qml/qdeclarativeengine.cpp +++ b/src/declarative/qml/qdeclarativeengine.cpp @@ -441,7 +441,7 @@ void QDeclarativeEnginePrivate::init() Q_Q(QDeclarativeEngine); qRegisterMetaType("QVariant"); qRegisterMetaType("QDeclarativeScriptString"); - qRegisterMetaType("QScriptValue"); + qRegisterMetaType("QJSValue"); qRegisterMetaType("QDeclarativeComponent::Status"); qRegisterMetaType >("QList"); qRegisterMetaType >("QList"); @@ -449,8 +449,7 @@ void QDeclarativeEnginePrivate::init() QDeclarativeData::init(); - // Init V8 data - v8engine.init(q); + v8engine()->setEngine(q); rootContext = new QDeclarativeContext(q,true); @@ -505,7 +504,7 @@ QDeclarativeWorkerScriptEngine *QDeclarativeEnginePrivate::getWorkerScriptEngine Create a new QDeclarativeEngine with the given \a parent. */ QDeclarativeEngine::QDeclarativeEngine(QObject *parent) -: QObject(*new QDeclarativeEnginePrivate(this), parent) +: QJSEngine(*new QDeclarativeEnginePrivate(this), parent) { Q_D(QDeclarativeEngine); d->init(); @@ -1386,13 +1385,13 @@ bool QDeclarativeEngine::importPlugin(const QString &filePath, const QString &ur void QDeclarativeEngine::setOfflineStoragePath(const QString& dir) { Q_D(QDeclarativeEngine); - qt_qmlsqldatabase_setOfflineStoragePath(&d->v8engine, dir); + qt_qmlsqldatabase_setOfflineStoragePath(d->v8engine(), dir); } QString QDeclarativeEngine::offlineStoragePath() const { Q_D(const QDeclarativeEngine); - return qt_qmlsqldatabase_getOfflineStoragePath(&d->v8engine); + return qt_qmlsqldatabase_getOfflineStoragePath(d->v8engine()); } static void voidptr_destructor(void *v) diff --git a/src/declarative/qml/qdeclarativeengine.h b/src/declarative/qml/qdeclarativeengine.h index e97c2d96b0..3f90296681 100644 --- a/src/declarative/qml/qdeclarativeengine.h +++ b/src/declarative/qml/qdeclarativeengine.h @@ -45,7 +45,8 @@ #include #include #include -#include +#include +#include #include QT_BEGIN_HEADER @@ -61,12 +62,12 @@ class QDeclarativeExpression; class QDeclarativeContext; class QDeclarativeType; class QUrl; -class QScriptEngine; +class QJSEngine; class QScriptContext; class QDeclarativeImageProvider; class QNetworkAccessManager; class QDeclarativeNetworkAccessManagerFactory; -class Q_DECLARATIVE_EXPORT QDeclarativeEngine : public QObject +class Q_DECLARATIVE_EXPORT QDeclarativeEngine : public QJSEngine { Q_PROPERTY(QString offlineStoragePath READ offlineStoragePath WRITE setOfflineStoragePath) Q_OBJECT diff --git a/src/declarative/qml/qdeclarativeengine_p.h b/src/declarative/qml/qdeclarativeengine_p.h index 538e8a05a0..6ff12189da 100644 --- a/src/declarative/qml/qdeclarativeengine_p.h +++ b/src/declarative/qml/qdeclarativeengine_p.h @@ -139,8 +139,7 @@ class Q_DECLARATIVE_EXPORT QDeclarativeEnginePrivate : public QObjectPrivate QDeclarativeDelayedError *erroredBindings; int inProgressCreations; - // V8 Engine - QV8Engine v8engine; + QV8Engine *v8engine() const { return q_func()->handle(); } QDeclarativeWorkerScriptEngine *getWorkerScriptEngine(); QDeclarativeWorkerScriptEngine *workerScriptEngine; @@ -256,7 +255,7 @@ class Q_DECLARATIVE_EXPORT QDeclarativeEnginePrivate : public QObjectPrivate static void warning(QDeclarativeEnginePrivate *, const QDeclarativeError &); static void warning(QDeclarativeEnginePrivate *, const QList &); - static QV8Engine *getV8Engine(QDeclarativeEngine *e) { return &e->d_func()->v8engine; } + static QV8Engine *getV8Engine(QDeclarativeEngine *e) { return e->d_func()->v8engine(); } static QDeclarativeEnginePrivate *get(QDeclarativeEngine *e) { return e->d_func(); } static QDeclarativeEnginePrivate *get(QDeclarativeContext *c) { return (c && c->engine()) ? QDeclarativeEnginePrivate::get(c->engine()) : 0; } static QDeclarativeEnginePrivate *get(QDeclarativeContextData *c) { return (c && c->engine) ? QDeclarativeEnginePrivate::get(c->engine) : 0; } diff --git a/src/declarative/qml/qdeclarativeexpression.cpp b/src/declarative/qml/qdeclarativeexpression.cpp index ff19a07313..8806996bd4 100644 --- a/src/declarative/qml/qdeclarativeexpression.cpp +++ b/src/declarative/qml/qdeclarativeexpression.cpp @@ -144,11 +144,11 @@ QDeclarativeExpressionPrivate::evalFunction(QDeclarativeContextData *ctxt, QObje // XXX TODO: Implement script caching, like we used to do with QScriptProgram in the // QtScript days v8::HandleScope handle_scope; - v8::Context::Scope ctxtscope(ep->v8engine.context()); + v8::Context::Scope ctxtscope(ep->v8engine()->context()); v8::TryCatch tc; - v8::Local scopeobject = ep->v8engine.qmlScope(ctxt, scope); - v8::Local script = ep->v8engine.qmlModeCompile(code, filename, line); + v8::Local scopeobject = ep->v8engine()->qmlScope(ctxt, scope); + v8::Local script = ep->v8engine()->qmlModeCompile(code, filename, line); v8::Local result = script->Run(scopeobject); if (tc.HasCaught()) return v8::Persistent(); if (qmlscope) *qmlscope = qPersistentNew(scopeobject); @@ -485,9 +485,9 @@ v8::Local QDeclarativeJavaScriptExpression::evaluate(v8::Handle result; { v8::TryCatch try_catch; - v8::Handle This = ep->v8engine.global(); + v8::Handle This = ep->v8engine()->global(); if (scopeObject() && requiresThisObject()) { - v8::Handle value = ep->v8engine.newQObject(scopeObject()); + v8::Handle value = ep->v8engine()->newQObject(scopeObject()); if (value->IsObject()) This = v8::Handle::Cast(value); } @@ -498,7 +498,7 @@ v8::Local QDeclarativeJavaScriptExpression::evaluate(v8::Handlev8engine.context()); + v8::Context::Scope scope(ep->v8engine()->context()); v8::Local message = try_catch.Message(); if (!message.IsEmpty()) { QDeclarativeExpressionPrivate::exceptionToError(message, error); @@ -638,9 +638,9 @@ v8::Local QDeclarativeExpressionPrivate::v8value(QObject *secondarySc v8::Local result; QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(context()->engine); QObject *restoreSecondaryScope = 0; - restoreSecondaryScope = ep->v8engine.contextWrapper()->setSecondaryScope(v8qmlscope, secondaryScope); + restoreSecondaryScope = ep->v8engine()->contextWrapper()->setSecondaryScope(v8qmlscope, secondaryScope); result = evaluate(v8function, isUndefined); - ep->v8engine.contextWrapper()->setSecondaryScope(v8qmlscope, restoreSecondaryScope); + ep->v8engine()->contextWrapper()->setSecondaryScope(v8qmlscope, restoreSecondaryScope); return result; } else { return evaluate(v8function, isUndefined); @@ -663,9 +663,9 @@ QVariant QDeclarativeExpressionPrivate::value(QObject *secondaryScope, bool *isU { v8::HandleScope handle_scope; - v8::Context::Scope context_scope(ep->v8engine.context()); + v8::Context::Scope context_scope(ep->v8engine()->context()); v8::Local result = v8value(secondaryScope, isUndefined); - rv = ep->v8engine.toVariant(result, qMetaTypeId >()); + rv = ep->v8engine()->toVariant(result, qMetaTypeId >()); } ep->dereferenceScarceResources(); // "release" scarce resources if top-level expression evaluation is complete. diff --git a/src/declarative/qml/qdeclarativemetatype.cpp b/src/declarative/qml/qdeclarativemetatype.cpp index 18eea0b614..41705bce72 100644 --- a/src/declarative/qml/qdeclarativemetatype.cpp +++ b/src/declarative/qml/qdeclarativemetatype.cpp @@ -61,7 +61,7 @@ #include #include #include -#include +#include #include @@ -1114,7 +1114,7 @@ QT_END_NAMESPACE #include #include -Q_DECLARE_METATYPE(QScriptValue); +Q_DECLARE_METATYPE(QJSValue); Q_DECLARE_METATYPE(QDeclarativeV8Handle); QT_BEGIN_NAMESPACE @@ -1195,7 +1195,7 @@ bool QDeclarativeMetaType::canCopy(int type) default: if (type == qMetaTypeId() || - type == qMetaTypeId() || + type == qMetaTypeId() || type == qMetaTypeId() || typeCategory(type) != Unknown) { return true; @@ -1416,8 +1416,8 @@ bool QDeclarativeMetaType::copy(int type, void *data, const void *copy) if (type == qMetaTypeId()) { *static_cast(data) = *static_cast(copy); return true; - } else if (type == qMetaTypeId()) { - *static_cast(data) = *static_cast(copy); + } else if (type == qMetaTypeId()) { + *static_cast(data) = *static_cast(copy); return true; } else if (type == qMetaTypeId()) { *static_cast(data) = *static_cast(copy); @@ -1626,8 +1626,8 @@ bool QDeclarativeMetaType::copy(int type, void *data, const void *copy) if (type == qMetaTypeId()) { *static_cast(data) = NS(QVariant)(); return true; - } else if (type == qMetaTypeId()) { - *static_cast(data) = NS(QScriptValue)(); + } else if (type == qMetaTypeId()) { + *static_cast(data) = NS(QJSValue)(); return true; } else if (type == qMetaTypeId()) { *static_cast(data) = NS(QDeclarativeV8Handle)(); diff --git a/src/declarative/qml/qdeclarativemetatype_p.h b/src/declarative/qml/qdeclarativemetatype_p.h index cfc4f077f8..429aa7a98f 100644 --- a/src/declarative/qml/qdeclarativemetatype_p.h +++ b/src/declarative/qml/qdeclarativemetatype_p.h @@ -59,7 +59,7 @@ #include #include #include -#include +#include QT_BEGIN_NAMESPACE @@ -113,9 +113,9 @@ class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativeMetaType ModuleApiInstance() : scriptCallback(0), qobjectCallback(0), qobjectApi(0) {} - QScriptValue (*scriptCallback)(QDeclarativeEngine *, QScriptEngine *); - QObject *(*qobjectCallback)(QDeclarativeEngine *, QScriptEngine *); - QScriptValue scriptApi; + QJSValue (*scriptCallback)(QDeclarativeEngine *, QJSEngine *); + QObject *(*qobjectCallback)(QDeclarativeEngine *, QJSEngine *); + QJSValue scriptApi; QObject *qobjectApi; }; struct ModuleApi { @@ -123,8 +123,8 @@ class Q_DECLARATIVE_PRIVATE_EXPORT QDeclarativeMetaType inline bool operator==(const ModuleApi &) const; int major; int minor; - QScriptValue (*script)(QDeclarativeEngine *, QScriptEngine *); - QObject *(*qobject)(QDeclarativeEngine *, QScriptEngine *); + QJSValue (*script)(QDeclarativeEngine *, QJSEngine *); + QObject *(*qobject)(QDeclarativeEngine *, QJSEngine *); }; static ModuleApi moduleApi(const QByteArray &, int, int); }; diff --git a/src/declarative/qml/qdeclarativeprivate.h b/src/declarative/qml/qdeclarativeprivate.h index 9eacc1d1cb..e8e8f229cf 100644 --- a/src/declarative/qml/qdeclarativeprivate.h +++ b/src/declarative/qml/qdeclarativeprivate.h @@ -74,8 +74,8 @@ class QDeclarativeTypeInfo }; -class QScriptValue; -class QScriptEngine; +class QJSValue; +class QJSEngine; class QDeclarativeEngine; class QDeclarativeCustomParser; namespace QDeclarativePrivate @@ -243,8 +243,8 @@ namespace QDeclarativePrivate int versionMajor; int versionMinor; - QScriptValue (*scriptApi)(QDeclarativeEngine *, QScriptEngine *); - QObject *(*qobjectApi)(QDeclarativeEngine *, QScriptEngine *); + QJSValue (*scriptApi)(QDeclarativeEngine *, QJSEngine *); + QObject *(*qobjectApi)(QDeclarativeEngine *, QJSEngine *); }; enum RegistrationType { diff --git a/src/declarative/qml/qdeclarativepropertycache.cpp b/src/declarative/qml/qdeclarativepropertycache.cpp index 6ff47d7b57..d2148ad874 100644 --- a/src/declarative/qml/qdeclarativepropertycache.cpp +++ b/src/declarative/qml/qdeclarativepropertycache.cpp @@ -49,7 +49,7 @@ #include -Q_DECLARE_METATYPE(QScriptValue) +Q_DECLARE_METATYPE(QJSValue) Q_DECLARE_METATYPE(QDeclarativeV8Handle); QT_BEGIN_NAMESPACE @@ -83,8 +83,8 @@ static QDeclarativePropertyCache::Data::Flags flagsForPropertyType(int propType, if (propType < QMetaType::User && propType != QMetaType::QObjectStar && propType != QMetaType::QWidgetStar) { } else if (propType == qMetaTypeId()) { flags |= QDeclarativePropertyCache::Data::IsQmlBinding; - } else if (propType == qMetaTypeId()) { - flags |= QDeclarativePropertyCache::Data::IsQScriptValue; + } else if (propType == qMetaTypeId()) { + flags |= QDeclarativePropertyCache::Data::IsQJSValue; } else if (propType == qMetaTypeId()) { flags |= QDeclarativePropertyCache::Data::IsV8Handle; } else { @@ -518,7 +518,7 @@ QDeclarativePropertyCache::property(QDeclarativeEngine *engine, QObject *obj, rv = cache->property(name); } else { QString strname = QV8Engine::toStringStatic(name.string()); - // QString strname = ep->v8engine.toString(name); + // QString strname = ep->v8engine()->toString(name); local = QDeclarativePropertyCache::create(obj->metaObject(), strname); if (local.isValid()) rv = &local; diff --git a/src/declarative/qml/qdeclarativepropertycache_p.h b/src/declarative/qml/qdeclarativepropertycache_p.h index 621463dad9..cdbd49388f 100644 --- a/src/declarative/qml/qdeclarativepropertycache_p.h +++ b/src/declarative/qml/qdeclarativepropertycache_p.h @@ -60,7 +60,6 @@ #include "private/qhashedstring_p.h" #include -#include QT_BEGIN_NAMESPACE class QDeclarativeEngine; @@ -96,7 +95,7 @@ class Q_DECLARATIVE_EXPORT QDeclarativePropertyCache : public QDeclarativeRefCou IsEnumType = 0x00000100, // Property type is an enum IsQList = 0x00000200, // Property type is a QML list IsQmlBinding = 0x00000400, // Property type is a QDeclarativeBinding* - IsQScriptValue = 0x00000800, // Property type is a QScriptValue + IsQJSValue = 0x00000800, // Property type is a QScriptValue IsV8Handle = 0x00001000, // Property type is a QDeclarativeV8Handle // Apply only to IsFunctions @@ -127,7 +126,7 @@ class Q_DECLARATIVE_EXPORT QDeclarativePropertyCache : public QDeclarativeRefCou bool isEnum() const { return flags & IsEnumType; } bool isQList() const { return flags & IsQList; } bool isQmlBinding() const { return flags & IsQmlBinding; } - bool isQScriptValue() const { return flags & IsQScriptValue; } + bool isQJSValue() const { return flags & IsQJSValue; } bool isV8Handle() const { return flags & IsV8Handle; } bool isVMEFunction() const { return flags & IsVMEFunction; } bool hasArguments() const { return flags & HasArguments; } diff --git a/src/declarative/qml/qdeclarativesqldatabase_p.h b/src/declarative/qml/qdeclarativesqldatabase_p.h index 337f717b1e..ef88b2e4f5 100644 --- a/src/declarative/qml/qdeclarativesqldatabase_p.h +++ b/src/declarative/qml/qdeclarativesqldatabase_p.h @@ -42,7 +42,7 @@ #ifndef QDECLARATIVESQLDATABASE_P_H #define QDECLARATIVESQLDATABASE_P_H -#include +#include // // W A R N I N G // ------------- diff --git a/src/declarative/qml/qdeclarativetypeloader.cpp b/src/declarative/qml/qdeclarativetypeloader.cpp index cb3e8aef3e..97c5b38c20 100644 --- a/src/declarative/qml/qdeclarativetypeloader.cpp +++ b/src/declarative/qml/qdeclarativetypeloader.cpp @@ -1230,7 +1230,7 @@ void QDeclarativeScriptBlob::done() m_scriptData->pragmas = m_pragmas; // XXX TODO: Handle errors that occur duing the script compile - QV8Engine *v8engine = &QDeclarativeEnginePrivate::get(engine)->v8engine; + QV8Engine *v8engine = QDeclarativeEnginePrivate::get(engine)->v8engine(); v8::HandleScope handle_scope; v8::Context::Scope scope(v8engine->context()); v8::Local program = v8engine->qmlModeCompile(m_source, finalUrl().toString(), 1); diff --git a/src/declarative/qml/qdeclarativetypeloader_p.h b/src/declarative/qml/qdeclarativetypeloader_p.h index 20e16750cf..1ca6f8c9c2 100644 --- a/src/declarative/qml/qdeclarativetypeloader_p.h +++ b/src/declarative/qml/qdeclarativetypeloader_p.h @@ -55,8 +55,7 @@ #include #include -#include -#include +#include #include #include #include diff --git a/src/declarative/qml/qdeclarativetypenamecache_p.h b/src/declarative/qml/qdeclarativetypenamecache_p.h index abf18ce384..e89747b15a 100644 --- a/src/declarative/qml/qdeclarativetypenamecache_p.h +++ b/src/declarative/qml/qdeclarativetypenamecache_p.h @@ -57,8 +57,6 @@ #include "private/qdeclarativecleanup_p.h" #include "private/qdeclarativemetatype_p.h" -#include - #include QT_BEGIN_NAMESPACE diff --git a/src/declarative/qml/qdeclarativevme.cpp b/src/declarative/qml/qdeclarativevme.cpp index 3b7abe6028..45e4745c1a 100644 --- a/src/declarative/qml/qdeclarativevme.cpp +++ b/src/declarative/qml/qdeclarativevme.cpp @@ -74,7 +74,7 @@ #include #include #include -#include +#include QT_BEGIN_NAMESPACE @@ -1025,7 +1025,7 @@ v8::Persistent QDeclarativeVME::run(QDeclarativeContextData *parentC return qPersistentNew(script->m_value); QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(parentCtxt->engine); - QV8Engine *v8engine = &ep->v8engine; + QV8Engine *v8engine = ep->v8engine(); bool shared = script->pragmas & QDeclarativeParser::Object::ScriptBlock::Shared; diff --git a/src/declarative/qml/qdeclarativevme_p.h b/src/declarative/qml/qdeclarativevme_p.h index 5312e7e128..d0c98d448e 100644 --- a/src/declarative/qml/qdeclarativevme_p.h +++ b/src/declarative/qml/qdeclarativevme_p.h @@ -64,7 +64,7 @@ QT_BEGIN_NAMESPACE class QObject; -class QScriptValue; +class QJSValue; class QDeclarativeScriptData; class QDeclarativeCompiledData; class QDeclarativeCompiledData; diff --git a/src/declarative/qml/qdeclarativevmemetaobject.cpp b/src/declarative/qml/qdeclarativevmemetaobject.cpp index 247c1aa533..746c9f650b 100644 --- a/src/declarative/qml/qdeclarativevmemetaobject.cpp +++ b/src/declarative/qml/qdeclarativevmemetaobject.cpp @@ -48,7 +48,7 @@ #include "private/qdeclarativecontext_p.h" #include "private/qdeclarativebinding_p.h" -Q_DECLARE_METATYPE(QScriptValue); +Q_DECLARE_METATYPE(QJSValue); QT_BEGIN_NAMESPACE @@ -73,7 +73,7 @@ class QDeclarativeVMEVariant inline const QTime &asQTime(); inline const QDate &asQDate(); inline const QDateTime &asQDateTime(); - inline const QScriptValue &asQScriptValue(); + inline const QJSValue &asQJSValue(); inline void setValue(QObject *); inline void setValue(const QVariant &); @@ -86,7 +86,7 @@ class QDeclarativeVMEVariant inline void setValue(const QTime &); inline void setValue(const QDate &); inline void setValue(const QDateTime &); - inline void setValue(const QScriptValue &); + inline void setValue(const QJSValue &); private: int type; void *data[4]; // Large enough to hold all types @@ -135,8 +135,8 @@ void QDeclarativeVMEVariant::cleanup() } else if (type == qMetaTypeId()) { ((QVariant *)dataPtr())->~QVariant(); type = QVariant::Invalid; - } else if (type == qMetaTypeId()) { - ((QScriptValue *)dataPtr())->~QScriptValue(); + } else if (type == qMetaTypeId()) { + ((QJSValue *)dataPtr())->~QJSValue(); type = QVariant::Invalid; } @@ -245,12 +245,12 @@ const QDateTime &QDeclarativeVMEVariant::asQDateTime() return *(QDateTime *)(dataPtr()); } -const QScriptValue &QDeclarativeVMEVariant::asQScriptValue() +const QJSValue &QDeclarativeVMEVariant::asQJSValue() { - if (type != qMetaTypeId()) - setValue(QScriptValue()); + if (type != qMetaTypeId()) + setValue(QJSValue()); - return *(QScriptValue *)(dataPtr()); + return *(QJSValue *)(dataPtr()); } void QDeclarativeVMEVariant::setValue(QObject *v) @@ -367,14 +367,14 @@ void QDeclarativeVMEVariant::setValue(const QDateTime &v) } } -void QDeclarativeVMEVariant::setValue(const QScriptValue &v) +void QDeclarativeVMEVariant::setValue(const QJSValue &v) { - if (type != qMetaTypeId()) { + if (type != qMetaTypeId()) { cleanup(); - type = qMetaTypeId(); - new (dataPtr()) QScriptValue(v); + type = qMetaTypeId(); + new (dataPtr()) QJSValue(v); } else { - *(QScriptValue *)(dataPtr()) = v; + *(QJSValue *)(dataPtr()) = v; } } @@ -656,18 +656,18 @@ int QDeclarativeVMEMetaObject::metaCall(QMetaObject::Call c, int _id, void **a) QDeclarativeVMEMetaData::MethodData *data = metaData->methodData() + id; v8::HandleScope handle_scope; - v8::Context::Scope scope(ep->v8engine.context()); + v8::Context::Scope scope(ep->v8engine()->context()); v8::Handle *args = 0; if (data->parameterCount) { args = new v8::Handle[data->parameterCount]; for (int ii = 0; ii < data->parameterCount; ++ii) - args[ii] = ep->v8engine.fromVariant(*(QVariant *)a[ii + 1]); + args[ii] = ep->v8engine()->fromVariant(*(QVariant *)a[ii + 1]); } v8::TryCatch try_catch; - v8::Local result = function->Call(ep->v8engine.global(), data->parameterCount, args); + v8::Local result = function->Call(ep->v8engine()->global(), data->parameterCount, args); QVariant rv; if (try_catch.HasCaught()) { @@ -677,7 +677,7 @@ int QDeclarativeVMEMetaObject::metaCall(QMetaObject::Call c, int _id, void **a) ep->warning(error); if (a[0]) *(QVariant *)a[0] = QVariant(); } else { - if (a[0]) *(QVariant *)a[0] = ep->v8engine.toVariant(result, 0); + if (a[0]) *(QVariant *)a[0] = ep->v8engine()->toVariant(result, 0); } ep->dereferenceScarceResources(); // "release" scarce resources if top-level expression evaluation is complete. @@ -720,7 +720,7 @@ v8::Handle QDeclarativeVMEMetaObject::method(int index) QScriptValue QDeclarativeVMEMetaObject::readVarProperty(int id) { if (data[id].dataType() == qMetaTypeId()) - return data[id].asQScriptValue(); + return data[id].asQJSValue(); else if (data[id].dataType() == QMetaType::QObjectStar) return QDeclarativeEnginePrivate::get(ctxt->engine)->objectClass->newQObject(data[id].asQObject()); else @@ -732,7 +732,7 @@ QVariant QDeclarativeVMEMetaObject::readVarPropertyAsVariant(int id) { #if 0 if (data[id].dataType() == qMetaTypeId()) - return QDeclarativeEnginePrivate::get(ctxt->engine)->scriptValueToVariant(data[id].asQScriptValue()); + return QDeclarativeEnginePrivate::get(ctxt->engine)->scriptValueToVariant(data[id].asQJSValue()); else #endif if (data[id].dataType() == QMetaType::QObjectStar) diff --git a/src/declarative/qml/qdeclarativeworkerscript.cpp b/src/declarative/qml/qdeclarativeworkerscript.cpp index b519573ccf..fc9bb887e8 100644 --- a/src/declarative/qml/qdeclarativeworkerscript.cpp +++ b/src/declarative/qml/qdeclarativeworkerscript.cpp @@ -48,10 +48,9 @@ #include #include #include -#include +#include #include #include -#include #include #include #include @@ -190,7 +189,7 @@ class QDeclarativeWorkerScriptEnginePrivate : public QObject }; QDeclarativeWorkerScriptEnginePrivate::WorkerEngine::WorkerEngine(QDeclarativeWorkerScriptEnginePrivate *parent) -: p(parent), accessManager(0) +: QV8Engine(0), p(parent), accessManager(0) { } @@ -203,8 +202,7 @@ QDeclarativeWorkerScriptEnginePrivate::WorkerEngine::~WorkerEngine() void QDeclarativeWorkerScriptEnginePrivate::WorkerEngine::init() { - QV8Engine::init(0); - + initDeclarativeGlobalObject(); #define CALL_ONMESSAGE_SCRIPT \ "(function(object, message) { "\ "var isfunction = false; "\ @@ -705,7 +703,7 @@ bool QDeclarativeWorkerScript::event(QEvent *event) QDeclarativeEngine *engine = qmlEngine(this); if (engine) { WorkerDataEvent *workerEvent = static_cast(event); - QV8Engine *v8engine = &QDeclarativeEnginePrivate::get(engine)->v8engine; + QV8Engine *v8engine = QDeclarativeEnginePrivate::get(engine)->v8engine(); v8::HandleScope handle_scope; v8::Context::Scope scope(v8engine->context()); v8::Handle value = QV8Worker::deserialize(workerEvent->data(), v8engine); diff --git a/src/declarative/qml/qdeclarativeworkerscript_p.h b/src/declarative/qml/qdeclarativeworkerscript_p.h index 85910b16e9..d953b0ff18 100644 --- a/src/declarative/qml/qdeclarativeworkerscript_p.h +++ b/src/declarative/qml/qdeclarativeworkerscript_p.h @@ -57,7 +57,7 @@ #include "qdeclarativeparserstatus.h" #include -#include +#include #include QT_BEGIN_HEADER diff --git a/src/declarative/qml/qdeclarativexmlhttprequest.cpp b/src/declarative/qml/qdeclarativexmlhttprequest.cpp index 194a60f2d3..abcc283610 100644 --- a/src/declarative/qml/qdeclarativexmlhttprequest.cpp +++ b/src/declarative/qml/qdeclarativexmlhttprequest.cpp @@ -51,9 +51,8 @@ #include "qdeclarativeglobal_p.h" #include -#include -#include -#include +#include +#include #include #include #include diff --git a/src/declarative/qml/qdeclarativexmlhttprequest_p.h b/src/declarative/qml/qdeclarativexmlhttprequest_p.h index a2082db59f..c5c53a6ce1 100644 --- a/src/declarative/qml/qdeclarativexmlhttprequest_p.h +++ b/src/declarative/qml/qdeclarativexmlhttprequest_p.h @@ -42,7 +42,7 @@ #ifndef QDECLARATIVEXMLHTTPREQUEST_P_H #define QDECLARATIVEXMLHTTPREQUEST_P_H -#include +#include // // W A R N I N G // ------------- diff --git a/src/declarative/qml/v4/qdeclarativev4bindings.cpp b/src/declarative/qml/v4/qdeclarativev4bindings.cpp index 29c9ce05c5..d93d930034 100644 --- a/src/declarative/qml/v4/qdeclarativev4bindings.cpp +++ b/src/declarative/qml/v4/qdeclarativev4bindings.cpp @@ -217,7 +217,6 @@ class QDeclarativeV4BindingsPrivate : public QObjectPrivate typedef QDeclarativeNotifierEndpoint Subscription; Subscription *subscriptions; - QScriptDeclarativeClass::PersistentIdentifier *identifiers; void run(Binding *, QDeclarativePropertyPrivate::WriteFlags flags); @@ -240,20 +239,19 @@ class QDeclarativeV4BindingsPrivate : public QObjectPrivate inline void subscribeId(QDeclarativeContextData *p, int idIndex, int subIndex); inline void subscribe(QObject *o, int notifyIndex, int subIndex); - inline static qint32 toInt32(qsreal n); - static const qsreal D32; - static quint32 toUint32(qsreal n); + inline static qint32 toInt32(qreal n); + static const qreal D32; + static quint32 toUint32(qreal n); }; QDeclarativeV4BindingsPrivate::QDeclarativeV4BindingsPrivate() -: subscriptions(0), identifiers(0), program(0), bindings(0) +: subscriptions(0), program(0), bindings(0) { } QDeclarativeV4BindingsPrivate::~QDeclarativeV4BindingsPrivate() { delete [] subscriptions; subscriptions = 0; - delete [] identifiers; identifiers = 0; } int QDeclarativeV4BindingsPrivate::methodCount = -1; @@ -508,8 +506,6 @@ void QDeclarativeV4BindingsPrivate::init() { if (program->subscriptions) subscriptions = new QDeclarativeV4BindingsPrivate::Subscription[program->subscriptions]; - if (program->identifiers) - identifiers = new QScriptDeclarativeClass::PersistentIdentifier[program->identifiers]; bindings = new QDeclarativeV4BindingsPrivate::Binding[program->bindings]; } @@ -679,15 +675,15 @@ static void throwException(int id, QDeclarativeDelayedError *error, QDeclarativeEnginePrivate::warning(context->engine, error->error); } -const qsreal QDeclarativeV4BindingsPrivate::D32 = 4294967296.0; +const qreal QDeclarativeV4BindingsPrivate::D32 = 4294967296.0; -qint32 QDeclarativeV4BindingsPrivate::toInt32(qsreal n) +qint32 QDeclarativeV4BindingsPrivate::toInt32(qreal n) { if (qIsNaN(n) || qIsInf(n) || (n == 0)) return 0; double sign = (n < 0) ? -1.0 : 1.0; - qsreal abs_n = fabs(n); + qreal abs_n = fabs(n); n = ::fmod(sign * ::floor(abs_n), D32); const double D31 = D32 / 2.0; @@ -701,13 +697,13 @@ qint32 QDeclarativeV4BindingsPrivate::toInt32(qsreal n) return qint32 (n); } -inline quint32 QDeclarativeV4BindingsPrivate::toUint32(qsreal n) +inline quint32 QDeclarativeV4BindingsPrivate::toUint32(qreal n) { if (qIsNaN(n) || qIsInf(n) || (n == 0)) return 0; double sign = (n < 0) ? -1.0 : 1.0; - qsreal abs_n = fabs(n); + qreal abs_n = fabs(n); n = ::fmod(sign * ::floor(abs_n), D32); @@ -1015,7 +1011,7 @@ void QDeclarativeV4BindingsPrivate::run(int instrIndex, quint32 &executedBlocks, } else { // Delegate the conversion. This is pretty fast and it doesn't require a QScriptEngine. // Ideally we should just call the methods in the QScript namespace directly. - QScriptValue tmp(*src.getstringptr()); + QJSValue tmp(*src.getstringptr()); if (instr->unaryop.src == instr->unaryop.output) { output.cleanupString(); MARK_CLEAN_REGISTER(instr->unaryop.output); @@ -1035,7 +1031,7 @@ void QDeclarativeV4BindingsPrivate::run(int instrIndex, quint32 &executedBlocks, } else { // Delegate the conversion. This is pretty fast and it doesn't require a QScriptEngine. // Ideally we should just call the methods in the QScript namespace directly. - QScriptValue tmp(*src.getstringptr()); + QJSValue tmp(*src.getstringptr()); if (instr->unaryop.src == instr->unaryop.output) { output.cleanupString(); MARK_CLEAN_REGISTER(instr->unaryop.output); @@ -1055,7 +1051,7 @@ void QDeclarativeV4BindingsPrivate::run(int instrIndex, quint32 &executedBlocks, } else { // Delegate the conversion. This is pretty fast and it doesn't require a QScriptEngine. // Ideally we should just call the methods in the QScript namespace directly. - QScriptValue tmp(*src.getstringptr()); + QJSValue tmp(*src.getstringptr()); if (instr->unaryop.src == instr->unaryop.output) { output.cleanupString(); MARK_CLEAN_REGISTER(instr->unaryop.output); @@ -1103,7 +1099,7 @@ void QDeclarativeV4BindingsPrivate::run(int instrIndex, quint32 &executedBlocks, QML_V4_BEGIN_INSTR(MathPIReal, unaryop) { - static const qsreal qmlPI = 2.0 * qAsin(1.0); + static const qreal qmlPI = 2.0 * qAsin(1.0); Register &output = registers[instr->unaryop.output]; output.setqreal(qmlPI); } @@ -1480,16 +1476,16 @@ void QDeclarativeV4BindingsPrivate::run(int instrIndex, quint32 &executedBlocks, executedBlocks |= instr->blockop.block; QML_V4_END_INSTR(Block, blockop) + // XXX not applicable in v8 QML_V4_BEGIN_INSTR(InitString, initstring) - if (!identifiers[instr->initstring.offset].identifier) { - quint32 len = *(quint32 *)(data + instr->initstring.dataIdx); - QChar *strdata = (QChar *)(data + instr->initstring.dataIdx + sizeof(quint32)); +// if (!identifiers[instr->initstring.offset].identifier) { +// quint32 len = *(quint32 *)(data + instr->initstring.dataIdx); +// QChar *strdata = (QChar *)(data + instr->initstring.dataIdx + sizeof(quint32)); - QString str = QString::fromRawData(strdata, len); +// QString str = QString::fromRawData(strdata, len); - // XXX not applicable in v8 - // identifiers[instr->initstring.offset] = engine->objectClass->createPersistentIdentifier(str); - } +// // identifiers[instr->initstring.offset] = engine->objectClass->createPersistentIdentifier(str); +// } QML_V4_END_INSTR(InitString, initstring) QML_V4_BEGIN_INSTR(CleanupRegister, cleanup) diff --git a/src/declarative/qml/v4/qdeclarativev4irbuilder.cpp b/src/declarative/qml/v4/qdeclarativev4irbuilder.cpp index 34a59caf1a..bddfca18b4 100644 --- a/src/declarative/qml/v4/qdeclarativev4irbuilder.cpp +++ b/src/declarative/qml/v4/qdeclarativev4irbuilder.cpp @@ -436,7 +436,7 @@ bool QDeclarativeV4IRBuilder::visit(AST::IdentifierExpression *ast) if (name.at(0) == QLatin1Char('u') && name.length() == 9 && name == QLatin1String("undefined")) { _expr.code = _block->CONST(IR::UndefinedType, 0); // ### undefined value - } else if(m_engine->v8engine.illegalNames().contains(name) ) { + } else if (m_engine->v8engine()->illegalNames().contains(name) ) { if (qmlVerboseCompiler()) qWarning() << "*** illegal symbol:" << name; return false; } else if (const QDeclarativeParser::Object *obj = m_expression->ids.value(name)) { diff --git a/src/declarative/qml/v8/qjsconverter_p.h b/src/declarative/qml/v8/qjsconverter_p.h new file mode 100644 index 0000000000..4aec472c7a --- /dev/null +++ b/src/declarative/qml/v8/qjsconverter_p.h @@ -0,0 +1,272 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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-ONLY$ +** GNU Lesser General Public License Usage +** 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QJSCONVERTER_P_H +#define QJSCONVERTER_P_H + +#include "qjsvalue.h" +#include +#include +#include +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +extern char *qdtoa(double d, int mode, int ndigits, int *decpt, int *sign, char **rve, char **digits_str); +Q_CORE_EXPORT QString qt_regexp_toCanonical(const QString &, QRegExp::PatternSyntax); + +/* + \internal + \class QJSConverter + QJSValue and QJSEngine helper class. This class's responsibility is to convert values + between JS values and Qt/C++ values. + + This is a nice way to inline these functions in both QJSValue and QJSEngine. +*/ +class QJSConverter { +public: + static quint32 toArrayIndex(const QString& string) + { + // FIXME this function should be exported by JSC C API. + bool ok; + quint32 idx = string.toUInt(&ok); + if (!ok || toString(idx) != string) + idx = 0xffffffff; + + return idx; + } + + static QString toString(v8::Handle jsString) + { + if (jsString.IsEmpty()) + return QString(); + QString qstr; + qstr.resize(jsString->Length()); + jsString->Write(reinterpret_cast(qstr.data())); + return qstr; + } + + static v8::Handle toString(const QString& string) + { + return v8::String::New(reinterpret_cast(string.data()), string.size()); + } + + static QString toString(double value) + { + // FIXME this should be easier. The ideal fix is to create + // a new function in V8 API which could cover the functionality. + + if (qIsNaN(value)) + return QString::fromLatin1("NaN"); + if (qIsInf(value)) + return QString::fromLatin1(value < 0 ? "-Infinity" : "Infinity"); + if (!value) + return QString::fromLatin1("0"); + + QVarLengthArray buf; + int decpt; + int sign; + char* result = 0; + char* endresult; + (void)qdtoa(value, 0, 0, &decpt, &sign, &endresult, &result); + + if (!result) + return QString(); + + int resultLen = endresult - result; + if (decpt <= 0 && decpt > -6) { + buf.resize(-decpt + 2 + sign); + qMemSet(buf.data(), '0', -decpt + 2 + sign); + if (sign) // fix the sign. + buf[0] = '-'; + buf[sign + 1] = '.'; + buf.append(result, resultLen); + } else { + if (sign) + buf.append('-'); + int length = buf.size() - sign + resultLen; + if (decpt <= 21 && decpt > 0) { + if (length <= decpt) { + const char* zeros = "0000000000000000000000000"; + buf.append(result, resultLen); + buf.append(zeros, decpt - length); + } else { + buf.append(result, decpt); + buf.append('.'); + buf.append(result + decpt, resultLen - decpt); + } + } else if (result[0] >= '0' && result[0] <= '9') { + if (length > 1) { + buf.append(result, 1); + buf.append('.'); + buf.append(result + 1, resultLen - 1); + } else + buf.append(result, resultLen); + buf.append('e'); + buf.append(decpt >= 0 ? '+' : '-'); + int e = qAbs(decpt - 1); + if (e >= 100) + buf.append('0' + e / 100); + if (e >= 10) + buf.append('0' + (e % 100) / 10); + buf.append('0' + e % 10); + } + } + free(result); + buf.append(0); + return QString::fromLatin1(buf.constData()); + } + + enum { + PropertyAttributeMask = v8::ReadOnly | v8::DontDelete | v8::DontEnum, + }; + + // return a mask of v8::PropertyAttribute that may also contains QScriptValue::PropertyGetter or QScriptValue::PropertySetter + static uint toPropertyAttributes(const QFlags& flags) + { + uint attr = 0; + if (flags.testFlag(QJSValue::ReadOnly)) + attr |= v8::ReadOnly; + if (flags.testFlag(QJSValue::Undeletable)) + attr |= v8::DontDelete; + if (flags.testFlag(QJSValue::SkipInEnumeration)) + attr |= v8::DontEnum; +// if (flags.testFlag(QScriptValue::PropertyGetter)) +// attr |= QScriptValue::PropertyGetter; +// if (flags.testFlag(QScriptValue::PropertySetter)) +// attr |= QScriptValue::PropertySetter; + return attr; + } + + // Converts a JS RegExp to a QRegExp. + // The conversion is not 100% exact since ECMA regexp and QRegExp + // have different semantics/flags, but we try to do our best. + static QRegExp toRegExp(v8::Handle jsRegExp) + { + QString pattern = QJSConverter::toString(jsRegExp->GetSource()); + Qt::CaseSensitivity caseSensitivity = Qt::CaseSensitive; + if (jsRegExp->GetFlags() & v8::RegExp::kIgnoreCase) + caseSensitivity = Qt::CaseInsensitive; + return QRegExp(pattern, caseSensitivity, QRegExp::RegExp2); + } + + // Converts a QRegExp to a JS RegExp. + // The conversion is not 100% exact since ECMA regexp and QRegExp + // have different semantics/flags, but we try to do our best. + static v8::Handle toRegExp(const QRegExp &re) + { + // Convert the pattern to a ECMAScript pattern. + QString pattern = qt_regexp_toCanonical(re.pattern(), re.patternSyntax()); + if (re.isMinimal()) { + QString ecmaPattern; + int len = pattern.length(); + ecmaPattern.reserve(len); + int i = 0; + const QChar *wc = pattern.unicode(); + bool inBracket = false; + while (i < len) { + QChar c = wc[i++]; + ecmaPattern += c; + switch (c.unicode()) { + case '?': + case '+': + case '*': + case '}': + if (!inBracket) + ecmaPattern += QLatin1Char('?'); + break; + case '\\': + if (i < len) + ecmaPattern += wc[i++]; + break; + case '[': + inBracket = true; + break; + case ']': + inBracket = false; + break; + default: + break; + } + } + pattern = ecmaPattern; + } + + int flags = v8::RegExp::kNone; + if (re.caseSensitivity() == Qt::CaseInsensitive) + flags |= v8::RegExp::kIgnoreCase; + + return v8::RegExp::New(QJSConverter::toString(pattern), static_cast(flags)); + } + + // Converts a QStringList to JS. + // The result is a new Array object with length equal to the length + // of the QStringList, and the elements being the QStringList's + // elements converted to JS Strings. + static v8::Handle toStringList(const QStringList &lst) + { + v8::Handle result = v8::Array::New(lst.size()); + for (int i = 0; i < lst.size(); ++i) + result->Set(i, toString(lst.at(i))); + return result; + } + + // Converts a JS Array object to a QStringList. + // The result is a QStringList with length equal to the length + // of the JS Array, and elements being the JS Array's elements + // converted to QStrings. + static QStringList toStringList(v8::Handle jsArray) + { + QStringList result; + uint32_t length = jsArray->Length(); + for (uint32_t i = 0; i < length; ++i) + result.append(toString(jsArray->Get(i)->ToString())); + return result; + } + + + // Converts a JS Date to a QDateTime. + static QDateTime toDateTime(v8::Handle jsDate) + { + return QDateTime::fromMSecsSinceEpoch(jsDate->NumberValue()); + } + + // Converts a QDateTime to a JS Date. + static v8::Handle toDateTime(const QDateTime &dt) + { + double date; + if (!dt.isValid()) + date = qSNaN(); + else + date = dt.toMSecsSinceEpoch(); + return v8::Date::New(date); + } +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/declarative/qml/v8/qjsengine.cpp b/src/declarative/qml/v8/qjsengine.cpp new file mode 100644 index 0000000000..e80fcb4e7e --- /dev/null +++ b/src/declarative/qml/v8/qjsengine.cpp @@ -0,0 +1,431 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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-ONLY$ +** GNU Lesser General Public License Usage +** 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qjsengine.h" +#include "qjsvalue.h" +#include "qjsvalue_p.h" +#include "qscriptisolate_p.h" +#include "qscript_impl_p.h" +#include "qv8engine_p.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#undef Q_D +#undef Q_Q +#define Q_D(blah) +#define Q_Q(blah) + +Q_DECLARE_METATYPE(QJSValue) +Q_DECLARE_METATYPE(QObjectList) +Q_DECLARE_METATYPE(QList) + +QT_BEGIN_NAMESPACE + + +/*! + Constructs a QJSEngine object. + + The globalObject() is initialized to have properties as described in + \l{ECMA-262}, Section 15.1. +*/ +QJSEngine::QJSEngine() + : d(new QV8Engine(this)) +{ +} + +/*! + \internal +*/ +QJSEngine::QJSEngine(QJSEngine::ContextOwnership ownership) + : d(new QV8Engine(this, ownership)) +{ +} + +/*! + Constructs a QJSEngine object with the given \a parent. + + The globalObject() is initialized to have properties as described in + \l{ECMA-262}, Section 15.1. +*/ + +QJSEngine::QJSEngine(QObject *parent) + : QObject(parent) + , d(new QV8Engine(this)) +{ +} + +QJSEngine::QJSEngine(QObjectPrivate &dd, QObject *parent) + : QObject(dd, parent) + , d(new QV8Engine(this)) +{ +} + +/*! + Destroys this QJSEngine. +*/ +QJSEngine::~QJSEngine() +{ + delete d; +} + +/*! + Returns true if the last script evaluation resulted in an uncaught + exception; otherwise returns false. + + The exception state is cleared when evaluate() is called. + + \sa uncaughtException(), uncaughtExceptionLineNumber(), + uncaughtExceptionBacktrace() +*/ +bool QJSEngine::hasUncaughtException() const +{ + Q_D(const QJSEngine); + QScriptIsolate api(d); + return d->hasUncaughtException(); +} + +/*! + Returns the current uncaught exception, or an invalid QJSValue + if there is no uncaught exception. + + The exception value is typically an \c{Error} object; in that case, + you can call toString() on the return value to obtain an error + message. + + \sa hasUncaughtException(), uncaughtExceptionLineNumber(), + uncaughtExceptionBacktrace() +*/ +QJSValue QJSEngine::uncaughtException() const +{ + Q_D(const QJSEngine); + QScriptIsolate api(d); + return d->scriptValueFromInternal(d->uncaughtException()); +} + +/*! + Clears any uncaught exceptions in this engine. + + \sa hasUncaughtException() +*/ +void QJSEngine::clearExceptions() +{ + Q_D(QJSEngine); + QScriptIsolate api(d); + d->clearExceptions(); +} + + +/*! + Runs the garbage collector. + + The garbage collector will attempt to reclaim memory by locating and disposing of objects that are + no longer reachable in the script environment. + + Normally you don't need to call this function; the garbage collector will automatically be invoked + when the QJSEngine decides that it's wise to do so (i.e. when a certain number of new objects + have been created). However, you can call this function to explicitly request that garbage + collection should be performed as soon as possible. + + \sa reportAdditionalMemoryCost() +*/ +void QJSEngine::collectGarbage() +{ + Q_D(QJSEngine); + QScriptIsolate api(d); + d->collectGarbage(); +} + +/*! + Evaluates \a program, using \a lineNumber as the base line number, + and returns the result of the evaluation. + + The script code will be evaluated in the current context. + + The evaluation of \a program can cause an exception in the + engine; in this case the return value will be the exception + that was thrown (typically an \c{Error} object). You can call + hasUncaughtException() to determine if an exception occurred in + the last call to evaluate(). + + \a lineNumber is used to specify a starting line number for \a + program; line number information reported by the engine that pertain + to this evaluation (e.g. uncaughtExceptionLineNumber()) will be + based on this argument. For example, if \a program consists of two + lines of code, and the statement on the second line causes a script + exception, uncaughtExceptionLineNumber() would return the given \a + lineNumber plus one. When no starting line number is specified, line + numbers will be 1-based. + + \a fileName is used for error reporting. For example in error objects + the file name is accessible through the "fileName" property if it's + provided with this function. +*/ +QJSValue QJSEngine::evaluate(const QString& program, const QString& fileName, int lineNumber) +{ + Q_D(QJSEngine); + QScriptIsolate api(d, QScriptIsolate::NotNullEngine); + v8::HandleScope handleScope; + return QJSValuePrivate::get(d->evaluate(program, fileName, lineNumber)); +} + +QJSValue QJSEngine::nullValue() +{ + Q_D(QJSEngine); + QScriptIsolate api(d, QScriptIsolate::NotNullEngine); + v8::HandleScope handleScope; + return QJSValuePrivate::get(new QJSValuePrivate(d, v8::Null())); +} + +QJSValue QJSEngine::undefinedValue() +{ + Q_D(QJSEngine); + QScriptIsolate api(d, QScriptIsolate::NotNullEngine); + v8::HandleScope handleScope; + return QJSValuePrivate::get(new QJSValuePrivate(d, v8::Undefined())); +} + +QJSValue QJSEngine::newObject() +{ + Q_D(QJSEngine); + QScriptIsolate api(d, QScriptIsolate::NotNullEngine); + v8::HandleScope handleScope; + return QJSValuePrivate::get(new QJSValuePrivate(d, v8::Object::New())); +} + +QJSValue QJSEngine::newArray(uint length) +{ + Q_D(QJSEngine); + QScriptIsolate api(d, QScriptIsolate::NotNullEngine); + v8::HandleScope handleScope; + return QJSValuePrivate::get(d->newArray(length)); +} + +/*! + Creates a QtScript object that wraps the given QObject \a + object, using the given \a ownership. The given \a options control + various aspects of the interaction with the resulting script object. + + Signals and slots, properties and children of \a object are + available as properties of the created QJSValue. For more + information, see the \l{QtScript} documentation. + + If \a object is a null pointer, this function returns nullValue(). + + If a default prototype has been registered for the \a object's class + (or its superclass, recursively), the prototype of the new script + object will be set to be that default prototype. + + If the given \a object is deleted outside of QtScript's control, any + attempt to access the deleted QObject's members through the QtScript + wrapper object (either by script code or C++) will result in a + script exception. + + \sa QJSValue::toQObject(), reportAdditionalMemoryCost() +*/ +QJSValue QJSEngine::newQObject(QObject *object) +{ + Q_D(QJSEngine); + QScriptIsolate api(d, QScriptIsolate::NotNullEngine); + v8::HandleScope handleScope; + return d->scriptValueFromInternal(d->newQObject(object)); +} + +/*! + Creates a QtScript object holding the given variant \a value. + + If a default prototype has been registered with the meta type id of + \a value, then the prototype of the created object will be that + prototype; otherwise, the prototype will be the Object prototype + object. + + \sa setDefaultPrototype(), QJSValue::toVariant(), reportAdditionalMemoryCost() +*/ +QJSValue QJSEngine::newVariant(const QVariant &value) +{ + Q_D(QJSEngine); + QScriptIsolate api(d, QScriptIsolate::NotNullEngine); + v8::HandleScope handleScope; + return d->scriptValueFromInternal(d->newVariant(value)); +} + + +QJSValue QJSEngine::globalObject() const +{ + Q_D(const QJSEngine); + QScriptIsolate api(d, QScriptIsolate::NotNullEngine); + v8::HandleScope handleScope; + return d->scriptValueFromInternal(d->global()); +} + +QJSValue QJSEngine::toObject(const QJSValue& value) +{ + Q_D(QJSEngine); + QScriptIsolate api(d, QScriptIsolate::NotNullEngine); + v8::HandleScope handleScope; + return QJSValuePrivate::get(QJSValuePrivate::get(value)->toObject(d)); +} + +/*! + Creates a QtScript object of class Date from the given \a value. + + \sa QJSValue::toDateTime() +*/ +QJSValue QJSEngine::newDate(const QDateTime &dt) +{ + Q_D(QJSEngine); + QScriptIsolate api(d, QScriptIsolate::NotNullEngine); + v8::HandleScope handleScope; + return d->scriptValueFromInternal(v8::Handle(QJSConverter::toDateTime(dt))); +} + +/*! + Creates a QtScript object of class Date with the given + \a value (the number of milliseconds since 01 January 1970, + UTC). +*/ +QJSValue QJSEngine::newDate(double date) +{ + Q_D(QJSEngine); + QScriptIsolate api(d, QScriptIsolate::NotNullEngine); + v8::HandleScope handleScope; + return d->scriptValueFromInternal(v8::Handle(v8::Date::New(date))); +} + +/*! + Creates a QtScript object of class RegExp with the given + \a regexp. + + \sa QJSValue::toRegExp() +*/ +QJSValue QJSEngine::newRegExp(const QRegExp ®exp) +{ + Q_D(QJSEngine); + QScriptIsolate api(d, QScriptIsolate::NotNullEngine); + v8::HandleScope handleScope; + return QJSValuePrivate::get(d->newRegExp(regexp)); +} + +/*! + Creates a QtScript object of class RegExp with the given + \a pattern and \a flags. + + The legal flags are 'g' (global), 'i' (ignore case), and 'm' + (multiline). +*/ +QJSValue QJSEngine::newRegExp(const QString &pattern, const QString &flags) +{ + Q_D(QJSEngine); + QScriptIsolate api(d, QScriptIsolate::NotNullEngine); + v8::HandleScope handleScope; + return QJSValuePrivate::get(d->newRegExp(pattern, flags)); +} + +/*! + * \internal + * used by QJSEngine::toScriptValue + */ +QJSValue QJSEngine::create(int type, const void *ptr) +{ + Q_D(QJSEngine); + QScriptIsolate api(d, QScriptIsolate::NotNullEngine); + v8::HandleScope handleScope; + return d->scriptValueFromInternal(d->metaTypeToJS(type, ptr)); +} + +/*! + \internal + \since 4.5 + convert \a value to \a type, store the result in \a ptr +*/ +bool QJSEngine::convertV2(const QJSValue &value, int type, void *ptr) +{ + QJSValuePrivate *vp = QJSValuePrivate::get(value); + QV8Engine *engine = vp->engine(); + if (engine) { + QScriptIsolate api(engine, QScriptIsolate::NotNullEngine); + v8::HandleScope handleScope; + return engine->metaTypeFromJS(*vp, type, ptr); + } else { + switch (type) { + case QMetaType::Bool: + *reinterpret_cast(ptr) = vp->toBool(); + return true; + case QMetaType::Int: + *reinterpret_cast(ptr) = vp->toInt32(); + return true; + case QMetaType::UInt: + *reinterpret_cast(ptr) = vp->toUInt32(); + return true; + case QMetaType::LongLong: + *reinterpret_cast(ptr) = vp->toInteger(); + return true; + case QMetaType::ULongLong: + *reinterpret_cast(ptr) = vp->toInteger(); + return true; + case QMetaType::Double: + *reinterpret_cast(ptr) = vp->toNumber(); + return true; + case QMetaType::QString: + *reinterpret_cast(ptr) = vp->toString(); + return true; + case QMetaType::Float: + *reinterpret_cast(ptr) = vp->toNumber(); + return true; + case QMetaType::Short: + *reinterpret_cast(ptr) = vp->toInt32(); + return true; + case QMetaType::UShort: + *reinterpret_cast(ptr) = vp->toUInt16(); + return true; + case QMetaType::Char: + *reinterpret_cast(ptr) = vp->toInt32(); + return true; + case QMetaType::UChar: + *reinterpret_cast(ptr) = vp->toUInt16(); + return true; + case QMetaType::QChar: + *reinterpret_cast(ptr) = vp->toUInt16(); + return true; + default: + return false; + } + } +} + + +QT_END_NAMESPACE + +#include "moc_qjsengine.cpp" diff --git a/src/declarative/qml/v8/qjsengine.h b/src/declarative/qml/v8/qjsengine.h new file mode 100644 index 0000000000..5109cefcf4 --- /dev/null +++ b/src/declarative/qml/v8/qjsengine.h @@ -0,0 +1,151 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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-ONLY$ +** GNU Lesser General Public License Usage +** 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QJSENGINE_H +#define QJSENGINE_H + +#include + +#include +#include +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Script) + +class QDateTime; +class QV8Engine; + +class QRegExp; + +template +inline T qjsvalue_cast(const QJSValue &); + +class Q_SCRIPT_EXPORT QJSEngine + : public QObject +{ + Q_OBJECT +public: + enum ContextOwnership { + AdoptCurrentContext, + CreateNewContext + }; + + QJSEngine(); + explicit QJSEngine(ContextOwnership ownership); + explicit QJSEngine(QObject *parent); + virtual ~QJSEngine(); + + QJSValue globalObject() const; + + QJSValue evaluate(const QString &program, const QString &fileName = QString(), int lineNumber = 1); + + bool hasUncaughtException() const; + QJSValue uncaughtException() const; + void clearExceptions(); + + QJSValue nullValue(); + QJSValue undefinedValue(); + + QJSValue newVariant(const QVariant &value); + + QJSValue newRegExp(const QRegExp ®exp); + + QJSValue newObject(); + QJSValue newArray(uint length = 0); + QJSValue newRegExp(const QString &pattern, const QString &flags); + QJSValue newDate(double value); + QJSValue newDate(const QDateTime &value); + + QJSValue newQObject(QObject *object); + + template + inline QJSValue toScriptValue(const T &value) + { + return create(qMetaTypeId(), &value); + } + template + inline T fromScriptValue(const QJSValue &value) + { + return qjsvalue_cast(value); + } + + void collectGarbage(); + + QJSValue toObject(const QJSValue &value); + + QV8Engine *handle() const { return d; } + +Q_SIGNALS: + void signalHandlerException(const QJSValue &exception); + +private: + QJSValue create(int type, const void *ptr); + + static bool convertV2(const QJSValue &value, int type, void *ptr); + + friend inline bool qjsvalue_cast_helper(const QJSValue &, int, void *); + +protected: + QJSEngine(QObjectPrivate &dd, QObject *parent = 0); + +private: + QV8Engine *d; + Q_DISABLE_COPY(QJSEngine) + friend class QV8Engine; +}; + +inline bool qjsvalue_cast_helper(const QJSValue &value, int type, void *ptr) +{ + return QJSEngine::convertV2(value, type, ptr); +} + +template +T qjsvalue_cast(const QJSValue &value) +{ + T t; + const int id = qMetaTypeId(); + + if (qjsvalue_cast_helper(value, id, &t)) + return t; + else if (value.isVariant()) + return qvariant_cast(value.toVariant()); + + return T(); +} + +template <> +inline QVariant qjsvalue_cast(const QJSValue &value) +{ + return value.toVariant(); +} + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QJSENGINE_H diff --git a/src/declarative/qml/v8/qjsvalue.cpp b/src/declarative/qml/v8/qjsvalue.cpp new file mode 100644 index 0000000000..eff7b4321a --- /dev/null +++ b/src/declarative/qml/v8/qjsvalue.cpp @@ -0,0 +1,1024 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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-ONLY$ +** GNU Lesser General Public License Usage +** 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qscriptisolate_p.h" +#include "qjsengine.h" +#include "qv8engine_p.h" +#include "qjsvalue.h" +#include "qjsvalue_p.h" +#include "qscript_impl_p.h" +#include "qscriptshareddata_p.h" +#include +#include + +QT_BEGIN_NAMESPACE + +/*! + Constructs an invalid value. +*/ +QJSValue::QJSValue() + : d_ptr(InvalidValue()) +{ +} + +/*! + Constructs a new QJSValue with a boolean \a value. +*/ +QJSValue::QJSValue(bool value) + : d_ptr(new QJSValuePrivate(value)) +{ +} + +/*! + \enum QJSValue::PropertyFlag + + This enum describes the attributes of a property. + + \value ReadOnly The property is read-only. Attempts by Qt Script code to write to the property will be ignored. + + \value Undeletable Attempts by Qt Script code to \c{delete} the property will be ignored. + + \value SkipInEnumeration The property is not to be enumerated by a \c{for-in} enumeration. + + \value PropertyGetter The property is defined by a function which will be called to get the property value. + + \value PropertySetter The property is defined by a function which will be called to set the property value. + + \omitvalue QObjectMember This flag is used to indicate that an existing property is a QObject member (a property or method). + + \value KeepExistingFlags This value is used to indicate to setProperty() that the property's flags should be left unchanged. If the property doesn't exist, the default flags (0) will be used. + + \omitvalue UserRange Flags in this range are not used by Qt Script, and can be used for custom purposes. +*/ + +/*! + Constructs a new QJSValue with a number \a value. +*/ +QJSValue::QJSValue(int value) + : d_ptr(new QJSValuePrivate(value)) +{ +} + +/*! + Constructs a new QJSValue with a number \a value. +*/ +QJSValue::QJSValue(uint value) + : d_ptr(new QJSValuePrivate(value)) +{ +} + +/*! + Constructs a new QJSValue with a number \a value. +*/ +QJSValue::QJSValue(double value) + : d_ptr(new QJSValuePrivate(value)) +{ +} + +/*! + Constructs a new QJSValue with a string \a value. +*/ +QJSValue::QJSValue(const QString& value) + : d_ptr(new QJSValuePrivate(value)) +{ +} + +/*! + Constructs a new QJSValue with a special \a value. +*/ +QJSValue::QJSValue(SpecialValue value) + : d_ptr(new QJSValuePrivate(value)) +{ +} + +/*! + Constructs a new QJSValue with a string \a value. +*/ +QJSValue::QJSValue(const QLatin1String &value) + : d_ptr(new QJSValuePrivate(value)) +{ +} + +/*! + \fn QJSValue::QJSValue(const QLatin1String &value) + + Constructs a new QJSValue with a string \a value. +*/ +#ifndef QT_NO_CAST_FROM_ASCII +QJSValue::QJSValue(const char *value) + : d_ptr(new QJSValuePrivate(QString::fromAscii(value))) +{ +} +#endif + +/*! + Block automatic convertion to bool + \internal +*/ +QJSValue::QJSValue(void* d) +{ + Q_UNUSED(d); + Q_ASSERT(false); +} + +/*! + Constructs a new QJSValue from private + \internal +*/ +QJSValue::QJSValue(QJSValuePrivate* d) + : d_ptr(d) +{ +} + +/*! + Constructs a new QJSValue from private + \internal +*/ +QJSValue::QJSValue(QScriptPassPointer d) + : d_ptr(d.give()) +{ +} + +/*! + \obsolete + + Constructs a new QJSValue with the boolean \a value and + registers it with the script \a engine. +*/ +QJSValue::QJSValue(QJSEngine* engine, bool value) +{ + if (engine) { + QScriptIsolate api(QV8Engine::get(engine), QScriptIsolate::NotNullEngine); + d_ptr = new QJSValuePrivate(QV8Engine::get(engine), value); + } else { + d_ptr = new QJSValuePrivate(value); + } +} + +/*! + \obsolete + + Constructs a new QJSValue with the integer \a value and + registers it with the script \a engine. +*/ +QJSValue::QJSValue(QJSEngine* engine, int value) +{ + if (engine) { + QScriptIsolate api(QV8Engine::get(engine), QScriptIsolate::NotNullEngine); + d_ptr = new QJSValuePrivate(QV8Engine::get(engine), value); + } else { + d_ptr = new QJSValuePrivate(value); + } +} + +/*! + \obsolete + + Constructs a new QJSValue with the unsigned integer \a value and + registers it with the script \a engine. + */ +QJSValue::QJSValue(QJSEngine* engine, uint value) +{ + if (engine) { + QScriptIsolate api(QV8Engine::get(engine), QScriptIsolate::NotNullEngine); + d_ptr = new QJSValuePrivate(QV8Engine::get(engine), value); + } else { + d_ptr = new QJSValuePrivate(value); + } +} + +/*! + \obsolete + + Constructs a new QJSValue with the double \a value and + registers it with the script \a engine. +*/ +QJSValue::QJSValue(QJSEngine* engine, double value) +{ + if (engine) { + QScriptIsolate api(QV8Engine::get(engine), QScriptIsolate::NotNullEngine); + d_ptr = new QJSValuePrivate(QV8Engine::get(engine), value); + } else { + d_ptr = new QJSValuePrivate(value); + } +} + +/*! + \obsolete + + Constructs a new QJSValue with the string \a value and + registers it with the script \a engine. +*/ +QJSValue::QJSValue(QJSEngine* engine, const QString& value) +{ + if (engine) { + QScriptIsolate api(QV8Engine::get(engine), QScriptIsolate::NotNullEngine); + d_ptr = new QJSValuePrivate(QV8Engine::get(engine), value); + } else { + d_ptr = new QJSValuePrivate(value); + } +} + +/*! + \obsolete + + Constructs a new QJSValue with the string \a value and + registers it with the script \a engine. +*/ +QJSValue::QJSValue(QJSEngine* engine, const char* value) +{ + if (engine) { + QScriptIsolate api(QV8Engine::get(engine), QScriptIsolate::NotNullEngine); + d_ptr = new QJSValuePrivate(QV8Engine::get(engine), QString::fromUtf8(value)); + } else { + d_ptr = new QJSValuePrivate(QString::fromUtf8(value)); + } +} + +/*! + \obsolete + + Constructs a new QJSValue with the special \a value and + registers it with the script \a engine. +*/ +QJSValue::QJSValue(QJSEngine* engine, SpecialValue value) +{ + if (engine) { + QScriptIsolate api(QV8Engine::get(engine), QScriptIsolate::NotNullEngine); + d_ptr = new QJSValuePrivate(QV8Engine::get(engine), value); + } else { + d_ptr = new QJSValuePrivate(value); + } +} + +/*! + Constructs a new QJSValue that is a copy of \a other. + + Note that if \a other is an object (i.e., isObject() would return + true), then only a reference to the underlying object is copied into + the new script value (i.e., the object itself is not copied). +*/ +QJSValue::QJSValue(const QJSValue& other) + : d_ptr(other.d_ptr) +{ +} + +/*! + Destroys this QJSValue. +*/ +QJSValue::~QJSValue() +{ +} + +/*! + Returns true if this QJSValue is valid; otherwise returns + false. +*/ +bool QJSValue::isValid() const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return d->isValid(); +} + +/*! + Returns true if this QJSValue is of the primitive type Boolean; + otherwise returns false. + + \sa toBool() +*/ +bool QJSValue::isBool() const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return d->isBool(); +} + +/*! + \obsolete + + Use isBool() instead. + Returns true if this QJSValue is of the primitive type Boolean; + otherwise returns false. +*/ +bool QJSValue::isBoolean() const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return d->isBool(); +} + +/*! + Returns true if this QJSValue is of the primitive type Number; + otherwise returns false. + + \sa toNumber() +*/ +bool QJSValue::isNumber() const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return d->isNumber(); +} + +/*! + Returns true if this QJSValue is of the primitive type Null; + otherwise returns false. + + \sa QJSEngine::nullValue() +*/ +bool QJSValue::isNull() const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return d->isNull(); +} + +/*! + Returns true if this QJSValue is of the primitive type String; + otherwise returns false. + + \sa toString() +*/ +bool QJSValue::isString() const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return d->isString(); +} + +/*! + Returns true if this QJSValue is of the primitive type Undefined; + otherwise returns false. + + \sa QJSEngine::undefinedValue() +*/ +bool QJSValue::isUndefined() const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return d->isUndefined(); +} + +/*! + Returns true if this QJSValue is an object of the Error class; + otherwise returns false. + + \sa QScriptContext::throwError() +*/ +bool QJSValue::isError() const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return d->isError(); +} + +/*! + Returns true if this QJSValue is an object of the Array class; + otherwise returns false. + + \sa QJSEngine::newArray() +*/ +bool QJSValue::isArray() const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return d->isArray(); + } + +/*! + Returns true if this QJSValue is of the Object type; otherwise + returns false. + + Note that function values, variant values, and QObject values are + objects, so this function returns true for such values. + + \sa toObject(), QJSEngine::newObject() +*/ +bool QJSValue::isObject() const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return d->isObject(); +} + +/*! + Returns true if this QJSValue is a function; otherwise returns + false. + + \sa call() +*/ +bool QJSValue::isFunction() const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return d->isCallable(); +} + +/*! + Returns true if this QJSValue is a variant value; + otherwise returns false. + + \sa toVariant(), QJSEngine::newVariant() +*/ +bool QJSValue::isVariant() const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return d->isVariant(); +} + +/*! + Returns the string value of this QJSValue, as defined in + \l{ECMA-262} section 9.8, "ToString". + + Note that if this QJSValue is an object, calling this function + has side effects on the script engine, since the engine will call + the object's toString() function (and possibly valueOf()) in an + attempt to convert the object to a primitive value (possibly + resulting in an uncaught script exception). + + \sa isString() +*/ +QString QJSValue::toString() const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return d->toString(); +} + +/*! + Returns the number value of this QJSValue, as defined in + \l{ECMA-262} section 9.3, "ToNumber". + + Note that if this QJSValue is an object, calling this function + has side effects on the script engine, since the engine will call + the object's valueOf() function (and possibly toString()) in an + attempt to convert the object to a primitive value (possibly + resulting in an uncaught script exception). + + \sa isNumber(), toInteger(), toInt32(), toUInt32(), toUInt16() +*/ +double QJSValue::toNumber() const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return d->toNumber(); +} + +/*! + Returns the boolean value of this QJSValue, using the conversion + rules described in \l{ECMA-262} section 9.2, "ToBoolean". + + Note that if this QJSValue is an object, calling this function + has side effects on the script engine, since the engine will call + the object's valueOf() function (and possibly toString()) in an + attempt to convert the object to a primitive value (possibly + resulting in an uncaught script exception). + + \sa isBool() +*/ +bool QJSValue::toBool() const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return d->toBool(); +} + +/*! + \obsolete + + Use toBool() instead. +*/ +bool QJSValue::toBoolean() const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return d->toBool(); +} + +/*! + Returns the integer value of this QJSValue, using the conversion + rules described in \l{ECMA-262} section 9.4, "ToInteger". + + Note that if this QJSValue is an object, calling this function + has side effects on the script engine, since the engine will call + the object's valueOf() function (and possibly toString()) in an + attempt to convert the object to a primitive value (possibly + resulting in an uncaught script exception). + + \sa toNumber() +*/ +double QJSValue::toInteger() const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return d->toInteger(); +} + +/*! + Returns the signed 32-bit integer value of this QJSValue, using + the conversion rules described in \l{ECMA-262} section 9.5, "ToInt32". + + Note that if this QJSValue is an object, calling this function + has side effects on the script engine, since the engine will call + the object's valueOf() function (and possibly toString()) in an + attempt to convert the object to a primitive value (possibly + resulting in an uncaught script exception). + + \sa toNumber(), toUInt32() +*/ +qint32 QJSValue::toInt32() const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return d->toInt32(); +} + +/*! + Returns the unsigned 32-bit integer value of this QJSValue, using + the conversion rules described in \l{ECMA-262} section 9.6, "ToUint32". + + Note that if this QJSValue is an object, calling this function + has side effects on the script engine, since the engine will call + the object's valueOf() function (and possibly toString()) in an + attempt to convert the object to a primitive value (possibly + resulting in an uncaught script exception). + + \sa toNumber(), toInt32() +*/ +quint32 QJSValue::toUInt32() const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return d->toUInt32(); +} + +/*! + Returns the unsigned 16-bit integer value of this QJSValue, using + the conversion rules described in \l{ECMA-262} section 9.7, "ToUint16". + + Note that if this QJSValue is an object, calling this function + has side effects on the script engine, since the engine will call + the object's valueOf() function (and possibly toString()) in an + attempt to convert the object to a primitive value (possibly + resulting in an uncaught script exception). + + \sa toNumber() +*/ +quint16 QJSValue::toUInt16() const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return d->toUInt16(); +} + +/*! + \obsolete + + This function is obsolete; use QJSEngine::toObject() instead. +*/ +QJSValue QJSValue::toObject() const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return QJSValuePrivate::get(d->toObject()); +} + +/*! + Returns the QVariant value of this QJSValue, if it can be + converted to a QVariant; otherwise returns an invalid QVariant. + The conversion is performed according to the following table: + + \table + \header \o Input Type \o Result + \row \o Undefined \o An invalid QVariant. + \row \o Null \o An invalid QVariant. + \row \o Boolean \o A QVariant containing the value of the boolean. + \row \o Number \o A QVariant containing the value of the number. + \row \o String \o A QVariant containing the value of the string. + \row \o QVariant Object \o The result is the QVariant value of the object (no conversion). + \row \o QObject Object \o A QVariant containing a pointer to the QObject. + \row \o Date Object \o A QVariant containing the date value (toDateTime()). + \row \o RegExp Object \o A QVariant containing the regular expression value (toRegExp()). + \row \o Array Object \o The array is converted to a QVariantList. Each element is converted to a QVariant, recursively; cyclic references are not followed. + \row \o Object \o The object is converted to a QVariantMap. Each property is converted to a QVariant, recursively; cyclic references are not followed. + \endtable + + \sa isVariant() +*/ +QVariant QJSValue::toVariant() const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return d->toVariant(); +} + + +/*! + Calls this QJSValue as a function, using \a thisObject as + the `this' object in the function call, and passing \a args + as arguments to the function. Returns the value returned from + the function. + + If this QJSValue is not a function, call() does nothing + and returns an invalid QJSValue. + + Note that if \a thisObject is not an object, the global object + (see \l{QJSEngine::globalObject()}) will be used as the + `this' object. + + Calling call() can cause an exception to occur in the script engine; + in that case, call() returns the value that was thrown (typically an + \c{Error} object). You can call + QJSEngine::hasUncaughtException() to determine if an exception + occurred. + + \snippet doc/src/snippets/code/src_script_qscriptvalue.cpp 2 + + \sa construct() +*/ +QJSValue QJSValue::call(const QJSValue& thisObject, const QJSValueList& args) +{ + Q_D(QJSValue); + QScriptIsolate api(d->engine()); + return d->call(QJSValuePrivate::get(thisObject), args); +} + +/*! + Creates a new \c{Object} and calls this QJSValue as a + constructor, using the created object as the `this' object and + passing \a args as arguments. If the return value from the + constructor call is an object, then that object is returned; + otherwise the default constructed object is returned. + + If this QJSValue is not a function, construct() does nothing + and returns an invalid QJSValue. + + Calling construct() can cause an exception to occur in the script + engine; in that case, construct() returns the value that was thrown + (typically an \c{Error} object). You can call + QJSEngine::hasUncaughtException() to determine if an exception + occurred. + + \sa call(), QJSEngine::newObject() +*/ +QJSValue QJSValue::construct(const QJSValueList &args) +{ + Q_D(QJSValue); + QScriptIsolate api(d->engine()); + return QJSValuePrivate::get(d->construct(args)); +} + +/*! + Returns the QJSEngine that created this QJSValue, + or 0 if this QJSValue is invalid or the value is not + associated with a particular engine. +*/ +QJSEngine* QJSValue::engine() const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + QV8Engine* engine = d->engine(); + if (engine) + return QV8Engine::get(engine); + return 0; +} + + +/*! + If this QJSValue is an object, returns the internal prototype + (\c{__proto__} property) of this object; otherwise returns an + invalid QJSValue. + + \sa setPrototype(), isObject() +*/ +QJSValue QJSValue::prototype() const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return QJSValuePrivate::get(d->prototype()); +} + +/*! + If this QJSValue is an object, sets the internal prototype + (\c{__proto__} property) of this object to be \a prototype; + otherwise does nothing. + + The internal prototype should not be confused with the public + property with name "prototype"; the public prototype is usually + only set on functions that act as constructors. + + \sa prototype(), isObject() +*/ +void QJSValue::setPrototype(const QJSValue& prototype) +{ + Q_D(QJSValue); + QScriptIsolate api(d->engine()); + d->setPrototype(QJSValuePrivate::get(prototype)); +} + +/*! + Assigns the \a other value to this QJSValue. + + Note that if \a other is an object (isObject() returns true), + only a reference to the underlying object will be assigned; + the object itself will not be copied. +*/ +QJSValue& QJSValue::operator=(const QJSValue& other) +{ + d_ptr = other.d_ptr; + return *this; +} + +/*! + Returns true if this QJSValue is equal to \a other, otherwise + returns false. The comparison follows the behavior described in + \l{ECMA-262} section 11.9.3, "The Abstract Equality Comparison + Algorithm". + + This function can return true even if the type of this QJSValue + is different from the type of the \a other value; i.e. the + comparison is not strict. For example, comparing the number 9 to + the string "9" returns true; comparing an undefined value to a null + value returns true; comparing a \c{Number} object whose primitive + value is 6 to a \c{String} object whose primitive value is "6" + returns true; and comparing the number 1 to the boolean value + \c{true} returns true. If you want to perform a comparison + without such implicit value conversion, use strictlyEquals(). + + Note that if this QJSValue or the \a other value are objects, + calling this function has side effects on the script engine, since + the engine will call the object's valueOf() function (and possibly + toString()) in an attempt to convert the object to a primitive value + (possibly resulting in an uncaught script exception). + + \sa strictlyEquals(), lessThan() +*/ +bool QJSValue::equals(const QJSValue& other) const +{ + Q_D(const QJSValue); + QJSValuePrivate* otherValue = QJSValuePrivate::get(other); + QScriptIsolate api(d->engine() ? d->engine() : otherValue->engine()); + return d_ptr->equals(otherValue); +} + +/*! + Returns true if this QJSValue is equal to \a other using strict + comparison (no conversion), otherwise returns false. The comparison + follows the behavior described in \l{ECMA-262} section 11.9.6, "The + Strict Equality Comparison Algorithm". + + If the type of this QJSValue is different from the type of the + \a other value, this function returns false. If the types are equal, + the result depends on the type, as shown in the following table: + + \table + \header \o Type \o Result + \row \o Undefined \o true + \row \o Null \o true + \row \o Boolean \o true if both values are true, false otherwise + \row \o Number \o false if either value is NaN (Not-a-Number); true if values are equal, false otherwise + \row \o String \o true if both values are exactly the same sequence of characters, false otherwise + \row \o Object \o true if both values refer to the same object, false otherwise + \endtable + + \sa equals() +*/ +bool QJSValue::strictlyEquals(const QJSValue& other) const +{ + Q_D(const QJSValue); + QJSValuePrivate* o = QJSValuePrivate::get(other); + QScriptIsolate api(d->engine() ? d->engine() : o->engine()); + return d_ptr->strictlyEquals(o); +} + +/*! + Returns true if this QJSValue is an instance of + \a other; otherwise returns false. + + This QJSValue is considered to be an instance of \a other if + \a other is a function and the value of the \c{prototype} + property of \a other is in the prototype chain of this + QJSValue. +*/ +bool QJSValue::instanceOf(const QJSValue &other) const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return d->instanceOf(QJSValuePrivate::get(other)); +} + +/*! + Returns the value of this QJSValue's property with the given \a name, + using the given \a mode to resolve the property. + + If no such property exists, an invalid QJSValue is returned. + + If the property is implemented using a getter function (i.e. has the + PropertyGetter flag set), calling property() has side-effects on the + script engine, since the getter function will be called (possibly + resulting in an uncaught script exception). If an exception + occurred, property() returns the value that was thrown (typically + an \c{Error} object). + + \sa setProperty(), propertyFlags(), QJSValueIterator +*/ +QJSValue QJSValue::property(const QString& name) const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return QJSValuePrivate::get(d->property(name)); +} + +/*! + \overload + + Returns the property at the given \a arrayIndex, using the given \a + mode to resolve the property. + + This function is provided for convenience and performance when + working with array objects. + + If this QJSValue is not an Array object, this function behaves + as if property() was called with the string representation of \a + arrayIndex. +*/ +QJSValue QJSValue::property(quint32 arrayIndex) const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return QJSValuePrivate::get(d->property(arrayIndex)); +} + +/*! + Sets the value of this QJSValue's property with the given \a name to + the given \a value. + + If this QJSValue is not an object, this function does nothing. + + If this QJSValue does not already have a property with name \a name, + a new property is created; the given \a flags then specify how this + property may be accessed by script code. + + If \a value is invalid, the property is removed. + + If the property is implemented using a setter function (i.e. has the + PropertySetter flag set), calling setProperty() has side-effects on + the script engine, since the setter function will be called with the + given \a value as argument (possibly resulting in an uncaught script + exception). + + Note that you cannot specify custom getter or setter functions for + built-in properties, such as the \c{length} property of Array objects + or meta properties of QObject objects. + + \sa property() +*/ +void QJSValue::setProperty(const QString& name, const QJSValue& value) +{ + Q_D(QJSValue); + QScriptIsolate api(d->engine()); + d->setProperty(name, QJSValuePrivate::get(value)); +} + +/*! + \overload + + Sets the property at the given \a arrayIndex to the given \a value. + + This function is provided for convenience and performance when + working with array objects. + + If this QJSValue is not an Array object, this function behaves + as if setProperty() was called with the string representation of \a + arrayIndex. +*/ +void QJSValue::setProperty(quint32 arrayIndex, const QJSValue& value) +{ + Q_D(QJSValue); + QScriptIsolate api(d->engine()); + d->setProperty(arrayIndex, QJSValuePrivate::get(value)); +} + +/*! + Returns the flags of the property with the given \a name, using the + given \a mode to resolve the property. + + \sa property() +*/ +QJSValue::PropertyFlags QJSValue::propertyFlags(const QString& name) const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return d->propertyFlags(name); +} + +/*! + * If this QJSValue is a QObject, returns the QObject pointer + * that the QJSValue represents; otherwise, returns 0. + * + * If the QObject that this QJSValue wraps has been deleted, + * this function returns 0 (i.e. it is possible for toQObject() + * to return 0 even when isQObject() returns true). + * + * \sa isQObject() + */ +QObject *QJSValue::toQObject() const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return d->toQObject(); +} + +/*! + Returns a QDateTime representation of this value, in local time. + If this QJSValue is not a date, or the value of the date is NaN + (Not-a-Number), an invalid QDateTime is returned. + + \sa isDate() +*/ +QDateTime QJSValue::toDateTime() const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return d->toDataTime(); +} + +/*! + Returns the QRegExp representation of this value. + If this QJSValue is not a regular expression, an empty + QRegExp is returned. + + \sa isRegExp() +*/ +QRegExp QJSValue::toRegExp() const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return d->toRegExp(); +} + +/*! + Returns true if this QJSValue is an object of the Date class; + otherwise returns false. + + \sa QJSEngine::newDate() +*/ +bool QJSValue::isDate() const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return d->isDate(); +} + +/*! + Returns true if this QJSValue is an object of the RegExp class; + otherwise returns false. + + \sa QJSEngine::newRegExp() +*/ +bool QJSValue::isRegExp() const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return d->isRegExp(); +} + +/*! + Returns true if this QJSValue is a QObject; otherwise returns + false. + + Note: This function returns true even if the QObject that this + QJSValue wraps has been deleted. + + \sa toQObject(), QJSEngine::newQObject() +*/ +bool QJSValue::isQObject() const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return d->isQObject(); +} + +QT_END_NAMESPACE diff --git a/src/declarative/qml/v8/qjsvalue.h b/src/declarative/qml/v8/qjsvalue.h new file mode 100644 index 0000000000..07276bc694 --- /dev/null +++ b/src/declarative/qml/v8/qjsvalue.h @@ -0,0 +1,165 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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-ONLY$ +** GNU Lesser General Public License Usage +** 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QJSVALUE_H +#define QJSVALUE_H + +#include + +#include +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Script) + +class QJSValue; +class QJSEngine; +class QVariant; +class QObject; +struct QMetaObject; +class QDateTime; +class QRegExp; + +typedef QList QJSValueList; + +class QJSValuePrivate; +struct QScriptValuePrivatePointerDeleter; +template class QScriptPassPointer; + +class Q_SCRIPT_EXPORT QJSValue +{ +public: + enum PropertyFlag { + ReadOnly = 0x00000001, + Undeletable = 0x00000002, + SkipInEnumeration = 0x00000004 + }; + Q_DECLARE_FLAGS(PropertyFlags, PropertyFlag) + + enum SpecialValue { + NullValue, + UndefinedValue + }; + +public: + QJSValue(); + ~QJSValue(); + QJSValue(const QJSValue &other); + QJSValue(QJSEngine *engine, SpecialValue val); + QJSValue(QJSEngine *engine, bool val); + QJSValue(QJSEngine *engine, int val); + QJSValue(QJSEngine *engine, uint val); + QJSValue(QJSEngine *engine, double val); + QJSValue(QJSEngine *engine, const QString &val); + + QJSValue(SpecialValue value); + QJSValue(bool value); + QJSValue(int value); + QJSValue(uint value); + QJSValue(double value); + QJSValue(const QString &value); + QJSValue(const QLatin1String &value); +#ifndef QT_NO_CAST_FROM_ASCII + QT_ASCII_CAST_WARN_CONSTRUCTOR QJSValue(const char *str); +#endif + + QJSValue &operator=(const QJSValue &other); + + QJSEngine *engine() const; + bool isValid() const; + bool isBool() const; + bool isBoolean() const; + bool isNumber() const; + bool isFunction() const; + bool isNull() const; + bool isString() const; + bool isUndefined() const; + bool isVariant() const; + bool isQObject() const; + bool isObject() const; + bool isDate() const; + bool isRegExp() const; + bool isArray() const; + bool isError() const; + + QString toString() const; + double toNumber() const; + bool toBool() const; + bool toBoolean() const; + double toInteger() const; + qint32 toInt32() const; + quint32 toUInt32() const; + quint16 toUInt16() const; + QVariant toVariant() const; + QObject *toQObject() const; + QJSValue toObject() const; + QDateTime toDateTime() const; + QRegExp toRegExp() const; + + bool instanceOf(const QJSValue &other) const; + + bool equals(const QJSValue &other) const; + bool strictlyEquals(const QJSValue &other) const; + + QJSValue prototype() const; + void setPrototype(const QJSValue &prototype); + + QJSValue property(const QString &name) const; + void setProperty(const QString &name, const QJSValue &value); + + QJSValue property(quint32 arrayIndex) const; + void setProperty(quint32 arrayIndex, const QJSValue &value); + + QJSValue::PropertyFlags propertyFlags(const QString &name) const; + + QJSValue call(const QJSValue &thisObject = QJSValue(), + const QJSValueList &args = QJSValueList()); + QJSValue construct(const QJSValueList &args = QJSValueList()); + +private: + // force compile error, prevent QJSValue(bool) to be called + QJSValue(void *); + // force compile error, prevent QJSValue(QScriptEngine*, bool) to be called + QJSValue(QJSEngine *, void *); + QJSValue(QJSEngine *, const char *); + + QJSValue(QJSValuePrivate*); + QJSValue(QScriptPassPointer); + +private: + QExplicitlySharedDataPointer d_ptr; + + Q_DECLARE_PRIVATE(QJSValue) +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QJSValue::PropertyFlags) + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/declarative/qml/v8/qjsvalue_impl_p.h b/src/declarative/qml/v8/qjsvalue_impl_p.h new file mode 100644 index 0000000000..adff6ce945 --- /dev/null +++ b/src/declarative/qml/v8/qjsvalue_impl_p.h @@ -0,0 +1,1133 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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-ONLY$ +** GNU Lesser General Public License Usage +** 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// 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. +// + +#ifndef QJSVALUE_IMPL_P_H +#define QJSVALUE_IMPL_P_H + +#include "qjsconverter_p.h" +#include "qjsvalue_p.h" +#include "qv8engine_p.h" +#include "qscriptisolate_p.h" + +QT_BEGIN_NAMESPACE + + +QJSValuePrivate* QJSValuePrivate::get(const QJSValue& q) { Q_ASSERT(q.d_ptr.data()); return q.d_ptr.data(); } + +QJSValue QJSValuePrivate::get(const QJSValuePrivate* d) +{ + Q_ASSERT(d); + return QJSValue(const_cast(d)); +} + +QJSValue QJSValuePrivate::get(QScriptPassPointer d) +{ + Q_ASSERT(d); + return QJSValue(d); +} + +QJSValue QJSValuePrivate::get(QJSValuePrivate* d) +{ + Q_ASSERT(d); + return QJSValue(d); +} + +QJSValuePrivate::QJSValuePrivate() + : m_engine(0), m_state(Invalid) +{ +} + +QJSValuePrivate::QJSValuePrivate(bool value) + : m_engine(0), m_state(CBool), u(value) +{ +} + +QJSValuePrivate::QJSValuePrivate(int value) + : m_engine(0), m_state(CNumber), u(value) +{ +} + +QJSValuePrivate::QJSValuePrivate(uint value) + : m_engine(0), m_state(CNumber), u(value) +{ +} + +QJSValuePrivate::QJSValuePrivate(double value) + : m_engine(0), m_state(CNumber), u(value) +{ +} + +QJSValuePrivate::QJSValuePrivate(const QString& value) + : m_engine(0), m_state(CString), u(new QString(value)) +{ +} + +QJSValuePrivate::QJSValuePrivate(QJSValue::SpecialValue value) + : m_engine(0), m_state(value == QJSValue::NullValue ? CNull : CUndefined) +{ +} + +QJSValuePrivate::QJSValuePrivate(QV8Engine* engine, bool value) + : m_engine(engine), m_state(JSValue) +{ + Q_ASSERT(engine); + v8::HandleScope handleScope; + m_value = v8::Persistent::New(m_engine->makeJSValue(value)); + m_engine->registerValue(this); +} + +QJSValuePrivate::QJSValuePrivate(QV8Engine* engine, int value) + : m_engine(engine), m_state(JSValue) +{ + Q_ASSERT(engine); + v8::HandleScope handleScope; + m_value = v8::Persistent::New(m_engine->makeJSValue(value)); + m_engine->registerValue(this); +} + +QJSValuePrivate::QJSValuePrivate(QV8Engine* engine, uint value) + : m_engine(engine), m_state(JSValue) +{ + Q_ASSERT(engine); + v8::HandleScope handleScope; + m_value = v8::Persistent::New(m_engine->makeJSValue(value)); + m_engine->registerValue(this); +} + +QJSValuePrivate::QJSValuePrivate(QV8Engine* engine, double value) + : m_engine(engine), m_state(JSValue) +{ + Q_ASSERT(engine); + v8::HandleScope handleScope; + m_value = v8::Persistent::New(m_engine->makeJSValue(value)); + m_engine->registerValue(this); +} + +QJSValuePrivate::QJSValuePrivate(QV8Engine* engine, const QString& value) + : m_engine(engine), m_state(JSValue) +{ + Q_ASSERT(engine); + v8::HandleScope handleScope; + m_value = v8::Persistent::New(m_engine->makeJSValue(value)); + m_engine->registerValue(this); +} + +QJSValuePrivate::QJSValuePrivate(QV8Engine* engine, QJSValue::SpecialValue value) + : m_engine(engine), m_state(JSValue) +{ + Q_ASSERT(engine); + v8::HandleScope handleScope; + m_value = v8::Persistent::New(m_engine->makeJSValue(value)); + m_engine->registerValue(this); +} + +QJSValuePrivate::QJSValuePrivate(QV8Engine *engine, v8::Handle value) + : m_engine(engine), m_state(JSValue), m_value(v8::Persistent::New(value)) +{ + Q_ASSERT(engine); + // It shouldn't happen, v8 shows errors by returning an empty handler. This is important debug + // information and it can't be simply ignored. + Q_ASSERT(!value.IsEmpty()); + m_engine->registerValue(this); +} + +QJSValuePrivate::~QJSValuePrivate() +{ + if (isJSBased()) { + m_engine->unregisterValue(this); + QScriptIsolate api(m_engine); + m_value.Dispose(); + } else if (isStringBased()) { + delete u.m_string; + } +} + +bool QJSValuePrivate::toBool() const +{ + switch (m_state) { + case JSValue: + { + v8::HandleScope scope; + return m_value->ToBoolean()->Value(); + } + case CNumber: + return !(qIsNaN(u.m_number) || !u.m_number); + case CBool: + return u.m_bool; + case Invalid: + case CNull: + case CUndefined: + return false; + case CString: + return u.m_string->length(); + } + + Q_ASSERT_X(false, "toBool()", "Not all states are included in the previous switch statement."); + return false; // Avoid compiler warning. +} + +double QJSValuePrivate::toNumber() const +{ + switch (m_state) { + case JSValue: + { + v8::HandleScope scope; + return m_value->ToNumber()->Value(); + } + case CNumber: + return u.m_number; + case CBool: + return u.m_bool ? 1 : 0; + case CNull: + case Invalid: + return 0; + case CUndefined: + return qQNaN(); + case CString: + bool ok; + double result = u.m_string->toDouble(&ok); + if (ok) + return result; + result = u.m_string->toInt(&ok, 0); // Try other bases. + if (ok) + return result; + if (*u.m_string == QLatin1String("Infinity")) + return qInf(); + if (*u.m_string == QLatin1String("-Infinity")) + return -qInf(); + return u.m_string->length() ? qQNaN() : 0; + } + + Q_ASSERT_X(false, "toNumber()", "Not all states are included in the previous switch statement."); + return 0; // Avoid compiler warning. +} + +QScriptPassPointer QJSValuePrivate::toObject(QV8Engine* engine) const +{ + Q_ASSERT(engine); + if (this->engine() && engine != this->engine()) { + qWarning("QJSEngine::toObject: cannot convert value created in a different engine"); + return InvalidValue(); + } + + v8::HandleScope scope; + switch (m_state) { + case Invalid: + case CNull: + case CUndefined: + return new QJSValuePrivate; + case CString: + return new QJSValuePrivate(engine, engine->makeJSValue(*u.m_string)->ToObject()); + case CNumber: + return new QJSValuePrivate(engine, engine->makeJSValue(u.m_number)->ToObject()); + case CBool: + return new QJSValuePrivate(engine, engine->makeJSValue(u.m_bool)->ToObject()); + case JSValue: + if (m_value->IsObject()) + return const_cast(this); + if (m_value->IsNull() || m_value->IsUndefined()) // avoid "Uncaught TypeError: Cannot convert null to object" + return InvalidValue(); + return new QJSValuePrivate(engine, m_value->ToObject()); + default: + Q_ASSERT_X(false, Q_FUNC_INFO, "Not all states are included in this switch"); + return InvalidValue(); + } +} + +/*! + This method is created only for QJSValue::toObject() purpose which is obsolete. + \internal + */ +QScriptPassPointer QJSValuePrivate::toObject() const +{ + if (isJSBased()) + return toObject(engine()); + + // Without an engine there is not much we can do. + return new QJSValuePrivate; +} + +QString QJSValuePrivate::toString() const +{ + switch (m_state) { + case Invalid: + return QString(); + case CBool: + return u.m_bool ? QString::fromLatin1("true") : QString::fromLatin1("false"); + case CString: + return *u.m_string; + case CNumber: + return QJSConverter::toString(u.m_number); + case CNull: + return QString::fromLatin1("null"); + case CUndefined: + return QString::fromLatin1("undefined"); + case JSValue: + Q_ASSERT(!m_value.IsEmpty()); + v8::HandleScope handleScope; + v8::TryCatch tryCatch; + v8::Local result = m_value->ToString(); + if (result.IsEmpty()) { + result = tryCatch.Exception()->ToString(); + m_engine->setException(tryCatch.Exception(), tryCatch.Message()); + } + return QJSConverter::toString(result); + } + + Q_ASSERT_X(false, "toString()", "Not all states are included in the previous switch statement."); + return QString(); // Avoid compiler warning. +} + +QVariant QJSValuePrivate::toVariant() const +{ + switch (m_state) { + case Invalid: + return QVariant(); + case CBool: + return QVariant(u.m_bool); + case CString: + return QVariant(*u.m_string); + case CNumber: + return QVariant(u.m_number); + case CNull: + return QVariant(); + case CUndefined: + return QVariant(); + case JSValue: + break; + } + + Q_ASSERT(m_state == JSValue); + Q_ASSERT(!m_value.IsEmpty()); + Q_ASSERT(m_engine); + + v8::HandleScope handleScope; + return m_engine->variantFromJS(m_value); +} + +inline QDateTime QJSValuePrivate::toDataTime() const +{ + if (!isDate()) + return QDateTime(); + + v8::HandleScope handleScope; + return QJSConverter::toDateTime(v8::Handle::Cast(m_value)); + +} + +inline QRegExp QJSValuePrivate::toRegExp() const +{ + if (!isRegExp()) + return QRegExp(); + + v8::HandleScope handleScope; + return QJSConverter::toRegExp(v8::Handle::Cast(m_value)); +} + +QObject* QJSValuePrivate::toQObject() const +{ + if (!isJSBased()) + return 0; + + v8::HandleScope handleScope; + return engine()->qtObjectFromJS(m_value); +} + +double QJSValuePrivate::toInteger() const +{ + double result = toNumber(); + if (qIsNaN(result)) + return 0; + if (qIsInf(result)) + return result; + return (result > 0) ? qFloor(result) : -1 * qFloor(-result); +} + +qint32 QJSValuePrivate::toInt32() const +{ + double result = toInteger(); + // Orginaly it should look like that (result == 0 || qIsInf(result) || qIsNaN(result)), but + // some of these operation are invoked in toInteger subcall. + if (qIsInf(result)) + return 0; + return result; +} + +quint32 QJSValuePrivate::toUInt32() const +{ + double result = toInteger(); + // Orginaly it should look like that (result == 0 || qIsInf(result) || qIsNaN(result)), but + // some of these operation are invoked in toInteger subcall. + if (qIsInf(result)) + return 0; + return result; +} + +quint16 QJSValuePrivate::toUInt16() const +{ + return toInt32(); +} + +inline bool QJSValuePrivate::isArray() const +{ + return isJSBased() && m_value->IsArray(); +} + +inline bool QJSValuePrivate::isBool() const +{ + return m_state == CBool || (isJSBased() && m_value->IsBoolean()); +} + +inline bool QJSValuePrivate::isCallable() const +{ + if (isFunction()) + return true; + if (isObject()) { + // Our C++ wrappers register function handlers but not always act as callables. + return v8::Object::Cast(*m_value)->IsCallable(); + } + return false; +} + +inline bool QJSValuePrivate::isError() const +{ + if (!isJSBased()) + return false; + v8::HandleScope handleScope; + return m_value->IsError(); +} + +inline bool QJSValuePrivate::isFunction() const +{ + return isJSBased() && m_value->IsFunction(); +} + +inline bool QJSValuePrivate::isNull() const +{ + return m_state == CNull || (isJSBased() && m_value->IsNull()); +} + +inline bool QJSValuePrivate::isNumber() const +{ + return m_state == CNumber || (isJSBased() && m_value->IsNumber()); +} + +inline bool QJSValuePrivate::isObject() const +{ + return isJSBased() && m_value->IsObject(); +} + +inline bool QJSValuePrivate::isString() const +{ + return m_state == CString || (isJSBased() && m_value->IsString()); +} + +inline bool QJSValuePrivate::isUndefined() const +{ + return m_state == CUndefined || (isJSBased() && m_value->IsUndefined()); +} + +inline bool QJSValuePrivate::isValid() const +{ + return m_state != Invalid; +} + +inline bool QJSValuePrivate::isVariant() const +{ + return isJSBased() && m_engine->isVariant(m_value); +} + +bool QJSValuePrivate::isDate() const +{ + return (isJSBased() && m_value->IsDate()); +} + +bool QJSValuePrivate::isRegExp() const +{ + return (isJSBased() && m_value->IsRegExp()); +} + +bool QJSValuePrivate::isQObject() const +{ + return isJSBased() && engine()->isQObject(m_value); +} + +inline bool QJSValuePrivate::equals(QJSValuePrivate* other) +{ + if (!isValid()) + return !other->isValid(); + + if (!other->isValid()) + return false; + + if (!isJSBased() && !other->isJSBased()) { + switch (m_state) { + case CNull: + case CUndefined: + return other->isUndefined() || other->isNull(); + case CNumber: + switch (other->m_state) { + case CBool: + case CString: + return u.m_number == other->toNumber(); + case CNumber: + return u.m_number == other->u.m_number; + default: + return false; + } + case CBool: + switch (other->m_state) { + case CBool: + return u.m_bool == other->u.m_bool; + case CNumber: + return toNumber() == other->u.m_number; + case CString: + return toNumber() == other->toNumber(); + default: + return false; + } + case CString: + switch (other->m_state) { + case CBool: + return toNumber() == other->toNumber(); + case CNumber: + return toNumber() == other->u.m_number; + case CString: + return *u.m_string == *other->u.m_string; + default: + return false; + } + default: + Q_ASSERT_X(false, "QJSValue::equals", "Not all states are included in the previous switch statement."); + } + } + + v8::HandleScope handleScope; + if (isJSBased() && !other->isJSBased()) { + if (!other->assignEngine(engine())) { + qWarning("QJSValue::equals: cannot compare to a value created in a different engine"); + return false; + } + } else if (!isJSBased() && other->isJSBased()) { + if (!assignEngine(other->engine())) { + qWarning("QJSValue::equals: cannot compare to a value created in a different engine"); + return false; + } + } + + Q_ASSERT(this->engine() && other->engine()); + if (this->engine() != other->engine()) { + qWarning("QJSValue::equals: cannot compare to a value created in a different engine"); + return false; + } + return m_value->Equals(other->m_value); +} + +inline bool QJSValuePrivate::strictlyEquals(QJSValuePrivate* other) +{ + if (isJSBased()) { + // We can't compare these two values without binding to the same engine. + if (!other->isJSBased()) { + if (other->assignEngine(engine())) + return m_value->StrictEquals(other->m_value); + return false; + } + if (other->engine() != engine()) { + qWarning("QJSValue::strictlyEquals: cannot compare to a value created in a different engine"); + return false; + } + return m_value->StrictEquals(other->m_value); + } + if (isStringBased()) { + if (other->isStringBased()) + return *u.m_string == *(other->u.m_string); + if (other->isJSBased()) { + assignEngine(other->engine()); + return m_value->StrictEquals(other->m_value); + } + } + if (isNumberBased()) { + if (other->isJSBased()) { + assignEngine(other->engine()); + return m_value->StrictEquals(other->m_value); + } + if (m_state != other->m_state) + return false; + if (m_state == CNumber) + return u.m_number == other->u.m_number; + Q_ASSERT(m_state == CBool); + return u.m_bool == other->u.m_bool; + } + + if (!isValid() && !other->isValid()) + return true; + + return false; +} + +inline bool QJSValuePrivate::lessThan(QJSValuePrivate *other) const +{ + if (engine() != other->engine() && engine() && other->engine()) { + qWarning("QJSValue::lessThan: cannot compare to a value created in a different engine"); + return false; + } + + if (!isValid() || !other->isValid()) + return false; + + if (isString() && other->isString()) + return toString() < other->toString(); + + if (isObject() || other->isObject()) { + v8::HandleScope handleScope; + QV8Engine *eng = m_engine ? engine() : other->engine(); + // FIXME: lessThan can throw an exception which will be dropped by this code: + Q_ASSERT(eng); + eng->saveException(); + QScriptSharedDataPointer cmp(eng->evaluate(QString::fromLatin1("(function(a,b){return aisFunction()); + v8::Handle args[2]; + cmp->prepareArgumentsForCall(args, QJSValueList() << QJSValuePrivate::get(this) << QJSValuePrivate::get(other)); + QScriptSharedDataPointer resultValue(cmp->call(0, 2, args)); + bool result = resultValue->toBool(); + eng->restoreException(); + return result; + } + + double nthis = toNumber(); + double nother = other->toNumber(); + if (qIsNaN(nthis) || qIsNaN(nother)) { + // Should return undefined in ECMA standard. + return false; + } + return nthis < nother; +} + +inline bool QJSValuePrivate::instanceOf(QJSValuePrivate* other) const +{ + if (!isObject() || !other->isFunction()) + return false; + if (engine() != other->engine()) { + qWarning("QJSValue::instanceof: cannot perform operation on a value created in a different engine"); + return false; + } + v8::HandleScope handleScope; + return instanceOf(v8::Handle::Cast(other->m_value)); +} + +inline bool QJSValuePrivate::instanceOf(v8::Handle other) const +{ + Q_ASSERT(isObject()); + Q_ASSERT(other->IsFunction()); + + v8::Handle self = v8::Handle::Cast(m_value); + v8::Handle selfPrototype = self->GetPrototype(); + v8::Handle otherPrototype = other->Get(v8::String::New("prototype")); + + while (!selfPrototype->IsNull()) { + if (selfPrototype->StrictEquals(otherPrototype)) + return true; + // In general a prototype can be an object or null, but in the loop it can't be null, so + // we can cast it safely. + selfPrototype = v8::Handle::Cast(selfPrototype)->GetPrototype(); + } + return false; +} + +inline QScriptPassPointer QJSValuePrivate::prototype() const +{ + if (isObject()) { + v8::HandleScope handleScope; + return new QJSValuePrivate(engine(), v8::Handle::Cast(m_value)->GetPrototype()); + } + return InvalidValue(); +} + +inline void QJSValuePrivate::setPrototype(QJSValuePrivate* prototype) +{ + if (isObject() && (prototype->isObject() || prototype->isNull())) { + if (engine() != prototype->engine()) { + if (prototype->engine()) { + qWarning("QJSValue::setPrototype() failed: cannot set a prototype created in a different engine"); + return; + } + prototype->assignEngine(engine()); + } + v8::HandleScope handleScope; + if (!v8::Handle::Cast(m_value)->SetPrototype(*prototype)) + qWarning("QJSValue::setPrototype() failed: cyclic prototype value"); + } +} + +inline void QJSValuePrivate::setProperty(const QString& name, QJSValuePrivate* value, uint attribs) +{ + if (!isObject()) + return; + v8::HandleScope handleScope; + setProperty(QJSConverter::toString(name), value, attribs); +} + +inline void QJSValuePrivate::setProperty(v8::Handle name, QJSValuePrivate* value, uint attribs) +{ + if (!isObject()) + return; + + if (!value->isJSBased()) + value->assignEngine(engine()); + + if (!value->isValid()) { + // Remove the property. + v8::HandleScope handleScope; + v8::TryCatch tryCatch; + v8::Handle recv(v8::Object::Cast(*m_value)); +// if (attribs & QJSValue::PropertyGetter && !(attribs & QJSValue::PropertySetter)) { +// v8::Local descriptor = engine()->originalGlobalObject()->getOwnPropertyDescriptor(recv, name); +// if (!descriptor.IsEmpty()) { +// v8::Local setter = descriptor->Get(v8::String::New("set")); +// if (!setter.IsEmpty() && !setter->IsUndefined()) { +// recv->Delete(name); +// engine()->originalGlobalObject()->defineGetterOrSetter(recv, name, setter, QJSValue::PropertySetter); +// if (tryCatch.HasCaught()) +// engine()->setException(tryCatch.Exception(), tryCatch.Message()); +// return; +// } +// } +// } else if (attribs & QJSValue::PropertySetter && !(attribs & QJSValue::PropertyGetter)) { +// v8::Local descriptor = engine()->originalGlobalObject()->getOwnPropertyDescriptor(recv, name); +// if (!descriptor.IsEmpty()) { +// v8::Local getter = descriptor->Get(v8::String::New("get")); +// if (!getter.IsEmpty() && !getter->IsUndefined()) { +// recv->Delete(name); +// engine()->originalGlobalObject()->defineGetterOrSetter(recv, name, getter, QJSValue::PropertyGetter); +// if (tryCatch.HasCaught()) +// engine()->setException(tryCatch.Exception(), tryCatch.Message()); +// return; +// } +// } +// } + recv->Delete(name); + if (tryCatch.HasCaught()) + engine()->setException(tryCatch.Exception(), tryCatch.Message()); + return; + } + + if (engine() != value->engine()) { + qWarning("QJSValue::setProperty(%s) failed: " + "cannot set value created in a different engine", + qPrintable(QJSConverter::toString(name))); + return; + } + + v8::TryCatch tryCatch; +// if (attribs & (QJSValue::PropertyGetter | QJSValue::PropertySetter)) { +// engine()->originalGlobalObject()->defineGetterOrSetter(*this, name, value->m_value, attribs); +// } else { + v8::Object::Cast(*m_value)->Set(name, value->m_value, v8::PropertyAttribute(attribs & QJSConverter::PropertyAttributeMask)); +// } + if (tryCatch.HasCaught()) + engine()->setException(tryCatch.Exception(), tryCatch.Message()); +} + +inline void QJSValuePrivate::setProperty(quint32 index, QJSValuePrivate* value, uint attribs) +{ + // FIXME this method should by integrated with other overloads to use the same code patch. + // for now it is not possible as v8 doesn't allow to set property attributes using index based api. + + if (!isObject()) + return; + + if (attribs) { + // FIXME we dont need to convert index to a string. + //Object::Set(int,value) do not take attributes. + setProperty(QString::number(index), value, attribs); + return; + } + + if (!value->isJSBased()) + value->assignEngine(engine()); + + if (!value->isValid()) { + // Remove the property. + v8::HandleScope handleScope; + v8::TryCatch tryCatch; + v8::Object::Cast(*m_value)->Delete(index); + if (tryCatch.HasCaught()) + engine()->setException(tryCatch.Exception(), tryCatch.Message()); + return; + } + + if (engine() != value->engine()) { + qWarning("QJSValue::setProperty() failed: cannot set value created in a different engine"); + return; + } + + v8::HandleScope handleScope; + v8::TryCatch tryCatch; + v8::Object::Cast(*m_value)->Set(index, value->m_value); + if (tryCatch.HasCaught()) + engine()->setException(tryCatch.Exception(), tryCatch.Message()); +} + +inline QScriptPassPointer QJSValuePrivate::property(const QString& name) const +{ + if (!name.length()) + return InvalidValue(); + + v8::HandleScope handleScope; + return property(QJSConverter::toString(name)); +} + +inline QScriptPassPointer QJSValuePrivate::property(v8::Handle name) const +{ + Q_ASSERT(!name.IsEmpty()); + if (!isObject()) + return InvalidValue(); + return property<>(name); +} + +inline QScriptPassPointer QJSValuePrivate::property(quint32 index) const +{ + if (!isObject()) + return InvalidValue(); + return property<>(index); +} + +template +inline QScriptPassPointer QJSValuePrivate::property(T name) const +{ + Q_ASSERT(isObject()); + v8::HandleScope handleScope; + v8::Handle self(v8::Object::Cast(*m_value)); + + v8::TryCatch tryCatch; + v8::Handle result = self->Get(name); + if (tryCatch.HasCaught()) { + result = tryCatch.Exception(); + engine()->setException(result, tryCatch.Message()); + return new QJSValuePrivate(engine(), result); + } + if (result.IsEmpty() || (result->IsUndefined() && !self->Has(name))) { + // In QtScript we make a distinction between a property that exists and has value undefined, + // and a property that doesn't exist; in the latter case, we should return an invalid value. + return InvalidValue(); + } + return new QJSValuePrivate(engine(), result); +} + +inline bool QJSValuePrivate::deleteProperty(const QString& name) +{ + if (!isObject()) + return false; + + v8::HandleScope handleScope; + v8::Handle self(v8::Handle::Cast(m_value)); + return self->Delete(QJSConverter::toString(name)); +} + +inline QJSValue::PropertyFlags QJSValuePrivate::propertyFlags(const QString& name) const +{ + if (!isObject()) + return QJSValue::PropertyFlags(0); + + v8::HandleScope handleScope; + return engine()->getPropertyFlags(v8::Handle::Cast(m_value), QJSConverter::toString(name)); +} + +inline QJSValue::PropertyFlags QJSValuePrivate::propertyFlags(v8::Handle name) const +{ + if (!isObject()) + return QJSValue::PropertyFlags(0); + + v8::HandleScope handleScope; + return engine()->getPropertyFlags(v8::Handle::Cast(m_value), name); +} + +inline QScriptPassPointer QJSValuePrivate::call(QJSValuePrivate* thisObject, const QJSValueList& args) +{ + if (!isCallable()) + return InvalidValue(); + + v8::HandleScope handleScope; + + // Convert all arguments and bind to the engine. + int argc = args.size(); + QVarLengthArray, 8> argv(argc); + if (!prepareArgumentsForCall(argv.data(), args)) { + qWarning("QJSValue::call() failed: cannot call function with argument created in a different engine"); + return InvalidValue(); + } + + return call(thisObject, argc, argv.data()); +} + +QScriptPassPointer QJSValuePrivate::call(QJSValuePrivate* thisObject, int argc, v8::Handle *argv) +{ + QV8Engine *e = engine(); + + v8::Handle recv; + + if (!thisObject || !thisObject->isObject()) { + recv = v8::Handle(v8::Object::Cast(*e->global())); + } else { + if (!thisObject->assignEngine(e)) { + qWarning("QJSValue::call() failed: cannot call function with thisObject created in a different engine"); + return InvalidValue(); + } + + recv = v8::Handle(v8::Object::Cast(*thisObject->m_value)); + } + + if (argc < 0) { + v8::Local exeption = v8::Exception::TypeError(v8::String::New("Arguments must be an array")); + e->setException(exeption); + return new QJSValuePrivate(e, exeption); + } + + v8::TryCatch tryCatch; + v8::Handle result = v8::Object::Cast(*m_value)->CallAsFunction(recv, argc, argv); + + if (result.IsEmpty()) { + result = tryCatch.Exception(); + // TODO: figure out why v8 doesn't always produce an exception value. + //Q_ASSERT(!result.IsEmpty()); + if (result.IsEmpty()) + result = v8::Exception::Error(v8::String::New("missing exception value")); + e->setException(result, tryCatch.Message()); + } + + return new QJSValuePrivate(e, result); +} + +inline QScriptPassPointer QJSValuePrivate::construct(int argc, v8::Handle *argv) +{ + QV8Engine *e = engine(); + + if (argc < 0) { + v8::Local exeption = v8::Exception::TypeError(v8::String::New("Arguments must be an array")); + e->setException(exeption); + return new QJSValuePrivate(e, exeption); + } + + v8::TryCatch tryCatch; + v8::Handle result = v8::Object::Cast(*m_value)->CallAsConstructor(argc, argv); + + if (result.IsEmpty()) { + result = tryCatch.Exception(); + e->setException(result, tryCatch.Message()); + } + + return new QJSValuePrivate(e, result); +} + +inline QScriptPassPointer QJSValuePrivate::construct(const QJSValueList& args) +{ + if (!isCallable()) + return InvalidValue(); + + v8::HandleScope handleScope; + + // Convert all arguments and bind to the engine. + int argc = args.size(); + QVarLengthArray, 8> argv(argc); + if (!prepareArgumentsForCall(argv.data(), args)) { + qWarning("QJSValue::construct() failed: cannot construct function with argument created in a different engine"); + return InvalidValue(); + } + + return construct(argc, argv.data()); +} + +/*! \internal + * Make sure this value is associated with a v8 value belogning to this engine. + * If the value was invalid, or belogning to another engine, return false. + */ +bool QJSValuePrivate::assignEngine(QV8Engine* engine) +{ + Q_ASSERT(engine); + v8::HandleScope handleScope; + switch (m_state) { + case Invalid: + return false; + case CBool: + m_value = v8::Persistent::New(engine->makeJSValue(u.m_bool)); + break; + case CString: + m_value = v8::Persistent::New(engine->makeJSValue(*u.m_string)); + delete u.m_string; + break; + case CNumber: + m_value = v8::Persistent::New(engine->makeJSValue(u.m_number)); + break; + case CNull: + m_value = v8::Persistent::New(engine->makeJSValue(QJSValue::NullValue)); + break; + case CUndefined: + m_value = v8::Persistent::New(engine->makeJSValue(QJSValue::UndefinedValue)); + break; + default: + if (this->engine() == engine) + return true; + else if (!isJSBased()) + Q_ASSERT_X(!isJSBased(), "assignEngine()", "Not all states are included in the previous switch statement."); + else + qWarning("JSValue can't be rassigned to an another engine."); + return false; + } + m_engine = engine; + m_state = JSValue; + + m_engine->registerValue(this); + return true; +} + +/*! + \internal + reinitialize this value to an invalid value. +*/ +void QJSValuePrivate::reinitialize() +{ + if (isJSBased()) { + m_engine->unregisterValue(this); + m_value.Dispose(); + m_value.Clear(); + } else if (isStringBased()) { + delete u.m_string; + } + m_engine = 0; + m_state = Invalid; +} + +/*! + \internal + reinitialize this value to an JSValue. +*/ +void QJSValuePrivate::reinitialize(QV8Engine* engine, v8::Handle value) +{ + Q_ASSERT_X(this != InvalidValue(), Q_FUNC_INFO, "static invalid can't be reinitialized to a different value"); + if (isJSBased()) { + m_value.Dispose(); + // avoid double registration + m_engine->unregisterValue(this); + } else if (isStringBased()) { + delete u.m_string; + } + m_engine = engine; + m_state = JSValue; + m_value = v8::Persistent::New(value); + m_engine->registerValue(this); +} + +QV8Engine* QJSValuePrivate::engine() const +{ + return m_engine; +} + +inline QJSValuePrivate::operator v8::Handle() const +{ + Q_ASSERT(isJSBased()); + return m_value; +} + +inline QJSValuePrivate::operator v8::Handle() const +{ + Q_ASSERT(isObject()); + return v8::Handle::Cast(m_value); +} + +/*! + * Return a v8::Handle, assign to the engine if needed. + */ +v8::Handle QJSValuePrivate::asV8Value(QV8Engine* engine) +{ + if (!m_engine) { + if (!assignEngine(engine)) + return v8::Handle(); + } + Q_ASSERT(isJSBased()); + return m_value; +} + +/*! + \internal + Returns true if QSV have an engine associated. +*/ +bool QJSValuePrivate::isJSBased() const +{ +#ifndef QT_NO_DEBUG + // internals check. + if (m_state >= JSValue) + Q_ASSERT(!m_value.IsEmpty()); + else + Q_ASSERT(m_value.IsEmpty()); +#endif + return m_state >= JSValue; +} + +/*! + \internal + Returns true if current value of QSV is placed in m_number. +*/ +bool QJSValuePrivate::isNumberBased() const { return m_state == CNumber || m_state == CBool; } + +/*! + \internal + Returns true if current value of QSV is placed in m_string. +*/ +bool QJSValuePrivate::isStringBased() const { return m_state == CString; } + +/*! + \internal + Converts arguments and bind them to the engine. + \attention argv should be big enough +*/ +inline bool QJSValuePrivate::prepareArgumentsForCall(v8::Handle argv[], const QJSValueList& args) const +{ + QJSValueList::const_iterator i = args.constBegin(); + for (int j = 0; i != args.constEnd(); j++, i++) { + QJSValuePrivate* value = QJSValuePrivate::get(*i); + if ((value->isJSBased() && engine() != value->engine()) + || (!value->isJSBased() && value->isValid() && !value->assignEngine(engine()))) + // Different engines are not allowed! + return false; + if (value->isValid()) + argv[j] = *value; + else + argv[j] = engine()->makeJSValue(QJSValue::UndefinedValue); + } + return true; +} + +QT_END_NAMESPACE + +#endif diff --git a/src/declarative/qml/v8/qjsvalue_p.h b/src/declarative/qml/v8/qjsvalue_p.h new file mode 100644 index 0000000000..7b2a001f97 --- /dev/null +++ b/src/declarative/qml/v8/qjsvalue_p.h @@ -0,0 +1,213 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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-ONLY$ +** GNU Lesser General Public License Usage +** 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// 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. +// + +#ifndef QJSVALUE_P_H +#define QJSVALUE_P_H + +#include + +#include +#include +#include +#include +#include + +#include "qscripttools_p.h" +#include "qscriptshareddata_p.h" +#include "qjsvalue.h" + +QT_BEGIN_NAMESPACE + +/*! + \internal + \class QJSValuePrivate +*/ +class QJSValuePrivate + : public QSharedData + , public QScriptLinkedNode +{ +public: + inline QJSValuePrivate(); + inline static QJSValuePrivate* get(const QJSValue& q); + inline static QJSValue get(const QJSValuePrivate* d); + inline static QJSValue get(QJSValuePrivate* d); + inline static QJSValue get(QScriptPassPointer d); + inline ~QJSValuePrivate(); + + inline QJSValuePrivate(bool value); + inline QJSValuePrivate(int value); + inline QJSValuePrivate(uint value); + inline QJSValuePrivate(double value); + inline QJSValuePrivate(const QString& value); + inline QJSValuePrivate(QJSValue::SpecialValue value); + + inline QJSValuePrivate(QV8Engine *engine, bool value); + inline QJSValuePrivate(QV8Engine *engine, int value); + inline QJSValuePrivate(QV8Engine *engine, uint value); + inline QJSValuePrivate(QV8Engine *engine, double value); + inline QJSValuePrivate(QV8Engine *engine, const QString& value); + inline QJSValuePrivate(QV8Engine *engine, QJSValue::SpecialValue value); + inline QJSValuePrivate(QV8Engine *engine, v8::Handle); + inline void reinitialize(); + inline void reinitialize(QV8Engine *engine, v8::Handle value); + + inline bool toBool() const; + inline double toNumber() const; + inline QScriptPassPointer toObject() const; + inline QScriptPassPointer toObject(QV8Engine *engine) const; + inline QString toString() const; + inline double toInteger() const; + inline qint32 toInt32() const; + inline quint32 toUInt32() const; + inline quint16 toUInt16() const; + inline QDateTime toDataTime() const; + inline QRegExp toRegExp() const; + inline QObject *toQObject() const; + inline QVariant toVariant() const; + + inline bool isArray() const; + inline bool isBool() const; + inline bool isCallable() const; + inline bool isError() const; + inline bool isFunction() const; + inline bool isNull() const; + inline bool isNumber() const; + inline bool isObject() const; + inline bool isString() const; + inline bool isUndefined() const; + inline bool isValid() const; + inline bool isVariant() const; + inline bool isDate() const; + inline bool isRegExp() const; + inline bool isQObject() const; + + inline bool equals(QJSValuePrivate* other); + inline bool strictlyEquals(QJSValuePrivate* other); + inline bool lessThan(QJSValuePrivate *other) const; + inline bool instanceOf(QJSValuePrivate*) const; + inline bool instanceOf(v8::Handle other) const; + + inline QScriptPassPointer prototype() const; + inline void setPrototype(QJSValuePrivate* prototype); + + inline void setProperty(const QString &name, QJSValuePrivate *value, uint attribs = 0); + inline void setProperty(v8::Handle name, QJSValuePrivate *value, uint attribs = 0); + inline void setProperty(quint32 index, QJSValuePrivate* value, uint attribs = 0); + inline QScriptPassPointer property(const QString& name) const; + inline QScriptPassPointer property(v8::Handle name) const; + inline QScriptPassPointer property(quint32 index) const; + template + inline QScriptPassPointer property(T name) const; + inline bool deleteProperty(const QString& name); + inline QJSValue::PropertyFlags propertyFlags(const QString& name) const; + inline QJSValue::PropertyFlags propertyFlags(v8::Handle name) const; + + inline QScriptPassPointer call(QJSValuePrivate* thisObject, const QJSValueList& args); + inline QScriptPassPointer call(QJSValuePrivate* thisObject, const QJSValue& arguments); + inline QScriptPassPointer call(QJSValuePrivate* thisObject, int argc, v8::Handle< v8::Value >* argv); + inline QScriptPassPointer construct(int argc, v8::Handle *argv); + inline QScriptPassPointer construct(const QJSValueList& args); + inline QScriptPassPointer construct(const QJSValue& arguments); + + inline bool assignEngine(QV8Engine *engine); + inline QV8Engine *engine() const; + + inline operator v8::Handle() const; + inline operator v8::Handle() const; + inline v8::Handle asV8Value(QV8Engine *engine); +private: + QV8Engine *m_engine; + + // Please, update class documentation when you change the enum. + enum State { + Invalid = 0, + CString = 0x1000, + CNumber, + CBool, + CNull, + CUndefined, + JSValue = 0x2000, // V8 values are equal or higher then this value. + // JSPrimitive, + // JSObject + } m_state; + + union CValue { + bool m_bool; + double m_number; + QString* m_string; + + CValue() : m_number(0) {} + CValue(bool value) : m_bool(value) {} + CValue(int number) : m_number(number) {} + CValue(uint number) : m_number(number) {} + CValue(double number) : m_number(number) {} + CValue(QString* string) : m_string(string) {} + } u; + // v8::Persistent is not a POD, so can't be part of the union. + v8::Persistent m_value; + + Q_DISABLE_COPY(QJSValuePrivate) + inline bool isJSBased() const; + inline bool isNumberBased() const; + inline bool isStringBased() const; + inline bool prepareArgumentsForCall(v8::Handle argv[], const QJSValueList& arguments) const; +}; + +// This template is used indirectly by the Q_GLOBAL_STATIC macro below +template<> +class QGlobalStaticDeleter +{ +public: + QGlobalStatic &globalStatic; + QGlobalStaticDeleter(QGlobalStatic &_globalStatic) + : globalStatic(_globalStatic) + { + globalStatic.pointer->ref.ref(); + } + + inline ~QGlobalStaticDeleter() + { + if (!globalStatic.pointer->ref.deref()) { // Logic copy & paste from SharedDataPointer + delete globalStatic.pointer; + } + globalStatic.pointer = 0; + globalStatic.destroyed = true; + } +}; + +Q_GLOBAL_STATIC(QJSValuePrivate, InvalidValue) + +QT_END_NAMESPACE + +#endif diff --git a/src/declarative/qml/v8/qjsvalueiterator.cpp b/src/declarative/qml/v8/qjsvalueiterator.cpp new file mode 100644 index 0000000000..ca9123f0c0 --- /dev/null +++ b/src/declarative/qml/v8/qjsvalueiterator.cpp @@ -0,0 +1,294 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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-ONLY$ +** GNU Lesser General Public License Usage +** 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qjsvalueiterator.h" + +#include "qscriptisolate_p.h" +#include "qjsvalue_p.h" +#include "qv8engine_p.h" +#include "qscript_impl_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QJSValueIterator + + \brief The QJSValueIterator class provides a Java-style iterator for QJSValue. + + \ingroup script + + + The QJSValueIterator constructor takes a QJSValue as + argument. After construction, the iterator is located at the very + beginning of the sequence of properties. Here's how to iterate over + all the properties of a QJSValue: + + \snippet doc/src/snippets/code/src_script_QJSValueIterator.cpp 0 + + The next() advances the iterator. The name(), value() and flags() + functions return the name, value and flags of the last item that was + jumped over. + + If you want to remove properties as you iterate over the + QJSValue, use remove(). If you want to modify the value of a + property, use setValue(). + + Note that QJSValueIterator only iterates over the QJSValue's + own properties; i.e. it does not follow the prototype chain. You can + use a loop like this to follow the prototype chain: + + \snippet doc/src/snippets/code/src_script_QJSValueIterator.cpp 1 + + Note that QJSValueIterator will not automatically skip over + properties that have the QJSValue::SkipInEnumeration flag set; + that flag only affects iteration in script code. If you want, you + can skip over such properties with code like the following: + + \snippet doc/src/snippets/code/src_script_QJSValueIterator.cpp 2 + + \sa QJSValue::property() +*/ + +using v8::Persistent; +using v8::Local; +using v8::Array; +using v8::String; +using v8::Handle; +using v8::Object; +using v8::Value; + +// FIXME (Qt5) This class should be refactored. It should use the common Iterator interface. +// FIXME it could be faster! +class QJSValueIteratorPrivate { +public: + inline QJSValueIteratorPrivate(const QJSValuePrivate* value); + inline ~QJSValueIteratorPrivate(); + + inline bool hasNext() const; + inline bool next(); + + inline QString name() const; + + inline QScriptPassPointer value() const; + + inline bool isValid() const; + inline QV8Engine* engine() const; +private: + Q_DISABLE_COPY(QJSValueIteratorPrivate) + //void dump(QString) const; + + QScriptSharedDataPointer m_object; + QList > m_names; + QMutableListIterator > m_iterator; +}; + +inline QJSValueIteratorPrivate::QJSValueIteratorPrivate(const QJSValuePrivate* value) + : m_object(const_cast(value)) + , m_iterator(m_names) +{ + Q_ASSERT(value); + QV8Engine *engine = m_object->engine(); + QScriptIsolate api(engine); + if (!m_object->isObject()) + m_object = 0; + else { + v8::HandleScope scope; + Handle tmp = *value; + Handle obj = Handle::Cast(tmp); + Local names; + + // FIXME we need newer V8! + //names = obj->GetOwnPropertyNames(); + names = engine->getOwnPropertyNames(obj); + + // it is suboptimal, it would be better to write iterator instead + uint32_t count = names->Length(); + Local name; + m_names.reserve(count); // The count is the maximal count of values. + for (uint32_t i = count - 1; i < count; --i) { + name = names->Get(i)->ToString(); + m_names.append(v8::Persistent::New(name)); + } + + // Reinitialize the iterator. + m_iterator = m_names; + } +} + +inline QJSValueIteratorPrivate::~QJSValueIteratorPrivate() +{ + QMutableListIterator > it = m_names; + //FIXME: we need register this QJSVAlueIterator + if (engine()) { + while (it.hasNext()) { + it.next().Dispose(); + } + } else { + // FIXME leak ? + } +} + +inline bool QJSValueIteratorPrivate::hasNext() const +{ + //dump("hasNext()"); + return isValid() + ? m_iterator.hasNext() : false; +} + +inline bool QJSValueIteratorPrivate::next() +{ + //dump("next();"); + if (m_iterator.hasNext()) { + m_iterator.next(); + return true; + } + return false; +} + +inline QString QJSValueIteratorPrivate::name() const +{ + //dump("name"); + if (!isValid()) + return QString(); + + return QJSConverter::toString(m_iterator.value()); +} + +inline QScriptPassPointer QJSValueIteratorPrivate::value() const +{ + //dump("value()"); + if (!isValid()) + return InvalidValue(); + + return m_object->property(m_iterator.value()); +} + +inline bool QJSValueIteratorPrivate::isValid() const +{ + bool result = m_object ? m_object->isValid() : false; + // We know that if this object is still valid then it is an object + // if this assumption is not correct then some other logic in this class + // have to be changed too. + Q_ASSERT(!result || m_object->isObject()); + return result; +} + +inline QV8Engine* QJSValueIteratorPrivate::engine() const +{ + return m_object ? m_object->engine() : 0; +} + +//void QJSValueIteratorPrivate::dump(QString fname) const +//{ +// qDebug() << " *** " << fname << " ***"; +// foreach (Persistent name, m_names) { +// qDebug() << " - " << QJSConverter::toString(name); +// } +//} + +/*! + Constructs an iterator for traversing \a object. The iterator is + set to be at the front of the sequence of properties (before the + first property). +*/ +QJSValueIterator::QJSValueIterator(const QJSValue& object) + : d_ptr(new QJSValueIteratorPrivate(QJSValuePrivate::get(object))) +{} + +/*! + Destroys the iterator. +*/ +QJSValueIterator::~QJSValueIterator() +{} + +/*! + Returns true if there is at least one item ahead of the iterator + (i.e. the iterator is \e not at the back of the property sequence); + otherwise returns false. + + \sa next(), hasPrevious() +*/ +bool QJSValueIterator::hasNext() const +{ + Q_D(const QJSValueIterator); + QScriptIsolate api(d->engine()); + return d->hasNext(); +} + +/*! + Advances the iterator by one position. + + Calling this function on an iterator located at the back of the + container leads to undefined results. + + \sa hasNext(), previous(), name() +*/ +bool QJSValueIterator::next() +{ + Q_D(QJSValueIterator); + QScriptIsolate api(d->engine()); + return d->next(); +} + +/*! + Returns the name of the last property that was jumped over using + next() or previous(). + + \sa value(), flags() +*/ +QString QJSValueIterator::name() const +{ + Q_D(const QJSValueIterator); + QScriptIsolate api(d->engine()); + return d_ptr->name(); +} + + +/*! + Returns the value of the last property that was jumped over using + next() or previous(). + + \sa setValue(), name() +*/ +QJSValue QJSValueIterator::value() const +{ + Q_D(const QJSValueIterator); + QScriptIsolate api(d->engine()); + return QJSValuePrivate::get(d->value()); +} + + +/*! + Makes the iterator operate on \a object. The iterator is set to be + at the front of the sequence of properties (before the first + property). +*/ +QJSValueIterator& QJSValueIterator::operator=(QJSValue& object) +{ + Q_D(QJSValueIterator); + QScriptIsolate api(d->engine()); + d_ptr.reset(new QJSValueIteratorPrivate(QJSValuePrivate::get(object))); + return *this; +} + +QT_END_NAMESPACE diff --git a/src/declarative/qml/v8/qjsvalueiterator.h b/src/declarative/qml/v8/qjsvalueiterator.h new file mode 100644 index 0000000000..1ec4d4b63b --- /dev/null +++ b/src/declarative/qml/v8/qjsvalueiterator.h @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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-ONLY$ +** GNU Lesser General Public License Usage +** 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSCRIPTVALUEITERATOR_H +#define QSCRIPTVALUEITERATOR_H + +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Script) + +class QString; + +class QJSValueIteratorPrivate; +class Q_SCRIPT_EXPORT QJSValueIterator +{ +public: + QJSValueIterator(const QJSValue &value); + ~QJSValueIterator(); + + bool hasNext() const; + bool next(); + + QString name() const; + + QJSValue value() const; + QJSValueIterator& operator=(QJSValue &value); + +private: + QScopedPointer d_ptr; + + Q_DECLARE_PRIVATE(QJSValueIterator) + Q_DISABLE_COPY(QJSValueIterator) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QSCRIPTVALUEITERATOR_H diff --git a/src/declarative/qml/v8/qscript_impl_p.h b/src/declarative/qml/v8/qscript_impl_p.h new file mode 100644 index 0000000000..e66b561efe --- /dev/null +++ b/src/declarative/qml/v8/qscript_impl_p.h @@ -0,0 +1,41 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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-ONLY$ +** GNU Lesser General Public License Usage +** 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// 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. +// + +#ifndef QSCRIPT_IMPL_P_H +#define QSCRIPT_IMPL_P_H + +#include "qv8engine_impl_p.h" +#include "qjsvalue_impl_p.h" + +#endif //QSCRIPT_IMPL_P_H diff --git a/src/declarative/qml/v8/qscriptisolate_p.h b/src/declarative/qml/v8/qscriptisolate_p.h new file mode 100644 index 0000000000..fa200c00b6 --- /dev/null +++ b/src/declarative/qml/v8/qscriptisolate_p.h @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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-ONLY$ +** GNU Lesser General Public License Usage +** 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef APIPREAMBLE_P_H +#define APIPREAMBLE_P_H + +#include +#include "qv8engine_p.h" + +QT_BEGIN_NAMESPACE + +/** + \internal + Class used to switch to the right isolate. It does the same thing as v8::Isolate::Scope but + it checks for a null engine. + \attention We decided to put context switching "up" which means that it should be as high + as possible on call stack. And it should be switched at most once per public API function call. +*/ +class QScriptIsolate { +public: + // OperationMode was introduced to reduce number of checking for a null engine pointer. If we + // know that given pointer is not null than we should pass NotNullEngine as constructor argument + // that would nicely remove checking on compilation time. + enum OperationMode {Default, NotNullEngine}; + inline QScriptIsolate(const QV8Engine *engine, const OperationMode mode = Default) + : m_engine(engine) + , m_mode(mode) + { + if (m_mode == NotNullEngine || m_engine) { + Q_ASSERT(m_engine); + m_engine->context()->Enter(); + } + } + + inline ~QScriptIsolate() + { + if (m_mode == NotNullEngine || m_engine) { + m_engine->context()->Exit(); + } + } + +private: + Q_DISABLE_COPY(QScriptIsolate); + const QV8Engine *m_engine; + const OperationMode m_mode; +}; + + +QT_END_NAMESPACE + +#endif // APIPREAMBLE_P_H diff --git a/src/declarative/qml/v8/qscriptoriginalglobalobject_p.h b/src/declarative/qml/v8/qscriptoriginalglobalobject_p.h new file mode 100644 index 0000000000..c46d0e37a0 --- /dev/null +++ b/src/declarative/qml/v8/qscriptoriginalglobalobject_p.h @@ -0,0 +1,158 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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-ONLY$ +** GNU Lesser General Public License Usage +** 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSCRIPTORIGINALGLOBALOBJECT_P_H +#define QSCRIPTORIGINALGLOBALOBJECT_P_H + +#include "QtCore/qglobal.h" +#include "qjsvalue.h" + +#include + +QT_BEGIN_NAMESPACE + +class QV8Engine; + +/*! + \internal + This class is a workaround for missing V8 API functionality. This class keeps all important + properties of an original (default) global object, so we can use it even if the global object was + changed. + + FIXME this class is a container for workarounds :-) it should be replaced by proper API calls. + + The class have to be created on the QV8Engine creation time (before any change got applied to + global object). + + \attention All methods (apart from constructor) assumes that a context and a scope are prepared correctly. +*/ +class QScriptOriginalGlobalObject +{ +public: + inline QScriptOriginalGlobalObject(const QV8Engine *engine, v8::Handle context); + inline void destroy(); + + inline QJSValue::PropertyFlags getPropertyFlags(v8::Handle object, v8::Handle property); + inline v8::Local getOwnPropertyDescriptor(v8::Handle object, v8::Handle property) const; + inline bool strictlyEquals(v8::Handle object); +private: + Q_DISABLE_COPY(QScriptOriginalGlobalObject) + + // Copy of constructors and prototypes used in isType functions. + v8::Persistent m_ownPropertyDescriptor; + v8::Persistent m_globalObject; +}; + +QScriptOriginalGlobalObject::QScriptOriginalGlobalObject(const QV8Engine *engine, v8::Handle context) +{ + // Please notice that engine is not fully initialized at this point. + + v8::Context::Scope contextScope(context); + + v8::HandleScope scope; + + m_globalObject = v8::Persistent::New(context->Global()); + + v8::Local objectConstructor = m_globalObject->Get(v8::String::New("Object"))->ToObject(); + Q_ASSERT(objectConstructor->IsObject()); + { // Initialize m_ownPropertyDescriptor. + v8::Local ownPropertyDescriptor = objectConstructor->Get(v8::String::New("getOwnPropertyDescriptor")); + Q_ASSERT(!ownPropertyDescriptor.IsEmpty()); + m_ownPropertyDescriptor = v8::Persistent::New(v8::Local::Cast(ownPropertyDescriptor)); + } +} + +/*! + \internal + QScriptOriginalGlobalObject lives as long as QV8Engine that keeps it. In ~QSEP + the v8 context is removed, so we need to remove our handlers before. to break this dependency + destroy method should be called before or insight QSEP destructor. +*/ +inline void QScriptOriginalGlobalObject::destroy() +{ + m_ownPropertyDescriptor.Dispose(); + m_globalObject.Dispose(); + // After this line this instance is unusable. +} + +inline QJSValue::PropertyFlags QScriptOriginalGlobalObject::getPropertyFlags(v8::Handle object, v8::Handle property) +{ + Q_ASSERT(object->IsObject()); + Q_ASSERT(!property.IsEmpty()); + v8::Local descriptor = getOwnPropertyDescriptor(object, property); + if (descriptor.IsEmpty()) { +// // Property isn't owned by this object. +// if (!(mode & QScriptValue::ResolvePrototype)) +// return 0; + v8::Local prototype = object->GetPrototype(); + if (prototype->IsNull()) + return 0; + return getPropertyFlags(v8::Local::Cast(prototype), property); + } + v8::Local writableName = v8::String::New("writable"); + v8::Local configurableName = v8::String::New("configurable"); + v8::Local enumerableName = v8::String::New("enumerable"); +// v8::Local getName = v8::String::New("get"); +// v8::Local setName = v8::String::New("set"); + + unsigned flags = 0; + + if (!descriptor->Get(configurableName)->BooleanValue()) + flags |= QJSValue::Undeletable; + if (!descriptor->Get(enumerableName)->BooleanValue()) + flags |= QJSValue::SkipInEnumeration; + + //"writable" is only a property of the descriptor if it is not an accessor + if (descriptor->Has(writableName)) { + if (!descriptor->Get(writableName)->BooleanValue()) + flags |= QJSValue::ReadOnly; + } else { +// if (descriptor->Get(getName)->IsObject()) +// flags |= QScriptValue::PropertyGetter; +// if (descriptor->Get(setName)->IsObject()) +// flags |= QScriptValue::PropertySetter; + } + + return QJSValue::PropertyFlag(flags); +} + +inline v8::Local QScriptOriginalGlobalObject::getOwnPropertyDescriptor(v8::Handle object, v8::Handle property) const +{ + Q_ASSERT(object->IsObject()); + Q_ASSERT(!property.IsEmpty()); + // FIXME do we need try catch here? + v8::Handle argv[] = {object, property}; + v8::Local descriptor = m_ownPropertyDescriptor->Call(m_globalObject, /* argc */ 2, argv); + if (descriptor.IsEmpty() || !descriptor->IsObject()) + return v8::Local(); + return v8::Local::Cast(descriptor); +} + +inline bool QScriptOriginalGlobalObject::strictlyEquals(v8::Handle object) +{ + return m_globalObject->GetPrototype()->StrictEquals(object); +} + +QT_END_NAMESPACE + +#endif diff --git a/src/declarative/qml/v8/qscriptshareddata_p.h b/src/declarative/qml/v8/qscriptshareddata_p.h new file mode 100644 index 0000000000..6604b10e32 --- /dev/null +++ b/src/declarative/qml/v8/qscriptshareddata_p.h @@ -0,0 +1,151 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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-ONLY$ +** GNU Lesser General Public License Usage +** 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// 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. +// + +#ifndef QSCRIPTSHAREDDATA_P_H +#define QSCRIPTSHAREDDATA_P_H + +#include "qglobal.h" +#include "qshareddata.h" + +QT_BEGIN_NAMESPACE + +/*! + \internal + This class should have the same interface as the QSharedData, but implementation doesn't + need to be thread safe, so atomic ref count was replaced by normal integer value. +*/ +class QScriptSharedData +{ +public: + class ReferenceCounter { + // FIXME shouldn't it be uint or something longer? + mutable int m_ref; + ReferenceCounter(int ref) : m_ref(ref) {} + ~ReferenceCounter() { Q_ASSERT_X(!m_ref, Q_FUNC_INFO, "Memory problem found"); } + public: + bool ref() { return ++m_ref; } + bool deref() { return --m_ref; } + friend class QScriptSharedData; + }; + + ReferenceCounter ref; + inline QScriptSharedData() : ref(0) { } + +private: + Q_DISABLE_COPY(QScriptSharedData) +}; + + +template class QScriptPassPointer; + +// FIXME: that could be reimplemented to not check for a null value. +template +class QScriptSharedDataPointer : public QExplicitlySharedDataPointer +{ +public: + inline QScriptSharedDataPointer() {} + explicit QScriptSharedDataPointer(QScriptPassPointer data) : QExplicitlySharedDataPointer(data.give()) {} + explicit QScriptSharedDataPointer(T *data) : QExplicitlySharedDataPointer(data) {} + + inline QScriptSharedDataPointer &operator=(const QScriptPassPointer &other) + { + this->QExplicitlySharedDataPointer::operator =(other.give()); + return *this; + } + inline QScriptSharedDataPointer &operator=(T *other) + { + this->QExplicitlySharedDataPointer::operator =(other); + return *this; + } +}; + +// FIXME: that could be reimplemented to not check for a null value. +template +class QScriptPassPointer { +public: + QScriptPassPointer(T *data) : m_ptr(data) {} + inline QScriptPassPointer() { m_ptr = 0; } + inline QScriptPassPointer(const QScriptPassPointer &other) : m_ptr(other.give()) {} + inline ~QScriptPassPointer() { Q_ASSERT_X(!m_ptr, Q_FUNC_INFO, "Ownership of the QScriptPassPointer hasn't been taken"); } + + inline T &operator*() const { return *m_ptr; } + inline T *operator->() { return m_ptr; } + inline T *operator->() const { return m_ptr; } + inline T *data() const { return m_ptr; } + inline const T *constData() const { return m_ptr; } + + inline bool operator==(const QScriptPassPointer &other) const { return m_ptr == other.m_ptr; } + inline bool operator!=(const QScriptPassPointer &other) const { return m_ptr != other.m_ptr; } + inline bool operator==(const QScriptSharedDataPointer &other) const { return m_ptr == other.m_ptr; } + inline bool operator!=(const QScriptSharedDataPointer &other) const { return m_ptr != other.m_ptr; } + inline bool operator==(const T *ptr) const { return m_ptr == ptr; } + inline bool operator!=(const T *ptr) const { return m_ptr != ptr; } + + inline operator bool () const { return m_ptr != 0; } + inline bool operator!() const { return !m_ptr; } + + inline QScriptPassPointer & operator=(const QScriptPassPointer &other) + { + if (other.m_ptr != m_ptr) { + if (m_ptr) + delete m_ptr; + m_ptr = other.give(); + } + return *this; + } + + inline QScriptPassPointer &operator=(T *other) + { + if (other != m_ptr) { + if (m_ptr) + delete m_ptr; + m_ptr = other; + } + return *this; + } + + inline T* give() const + { + T* result = m_ptr; + m_ptr = 0; + return result; + } + +private: + mutable T* m_ptr; +}; + +QT_END_NAMESPACE + +#endif // QSCRIPTSHAREDDATA_P_H diff --git a/src/declarative/qml/v8/qscripttools_p.h b/src/declarative/qml/v8/qscripttools_p.h new file mode 100644 index 0000000000..f74fbab83f --- /dev/null +++ b/src/declarative/qml/v8/qscripttools_p.h @@ -0,0 +1,216 @@ +/**************************************************************************** +** +** Copyright (C) 2011 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-ONLY$ +** GNU Lesser General Public License Usage +** 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// 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. +// + + +#ifndef QSCRIPTTOOLS_P_H +#define QSCRIPTTOOLS_P_H + +#include + +QT_BEGIN_NAMESPACE + +template +class QScriptBagContainer; + +/*! + \internal + \interface + Helper class for a container. The purpuse of it is to add two pointer properties to a class + inheriting this class without bloating an interface. + + This class exist only as a memory storage implementation. The only way to use it is to inherit it. +*/ +class QScriptLinkedNode +{ +protected: + QScriptLinkedNode() + : m_next(0) + , m_prev(0) + {} + + ~QScriptLinkedNode() + { + Q_ASSERT_X(!isUsed(), Q_FUNC_INFO, "Destorying QScriptLinkedNode instance that still is in a container"); + } + +private: + bool isUsed() const + { + return m_next || m_prev; + } + +#if defined(Q_NO_TEMPLATE_FRIENDS) +public: +#else + template + friend class QScriptBagContainer; +#endif + QScriptLinkedNode *m_next; + QScriptLinkedNode *m_prev; +}; + +/*! + \internal + The QScriptBagContainer is a simple, low level, set like container for a pointer type castable to + QScriptLinkedNode*. + Algorithms complexity: + put: O(1) + get: O(1) + forEach: O(n) + \note This container doesn't take ownership of pointed values. + \attention All values have to be unique. +*/ +template +class QScriptBagContainer +{ +public: + QScriptBagContainer() + : m_first(0) + {} + + /*! + \internal + Add a this \a value to this container + */ + void insert(T* value) + { + //dump(Q_FUNC_INFO, value); + Q_ASSERT_X(!contains(value), Q_FUNC_INFO, "Can't insert a value which is in the bag already"); + QScriptLinkedNode* v = static_cast(value); + Q_ASSERT(v); + Q_ASSERT_X(!v->m_next && !v->m_prev, Q_FUNC_INFO, "Can't insert a value which is in an another bag"); + + if (m_first) + m_first->m_prev = v; + + v->m_next = m_first; + v->m_prev = 0; + m_first = v; + } + + /*! + \internal + Remove this \a value from this container + */ + void remove(T* value) + { + //dump(Q_FUNC_INFO, value); + QScriptLinkedNode* v = static_cast(value); + Q_ASSERT(v); + + if (!v->m_next && !v->m_prev && m_first != v) { + // ignore that value as it is not registered at all + // FIXME: That may be optimized out if unregister call is removed from ~QtDataBase + return; + } + + Q_ASSERT_X(contains(value), Q_FUNC_INFO, "Can't remove a value which is not in the bag"); + Q_ASSERT(v->m_prev || (m_first == v && !v->m_prev)); + + if (v->m_next) + v->m_next->m_prev= v->m_prev; + + if (v->m_prev) + v->m_prev->m_next = v->m_next; + else + m_first = v->m_next; + // reset removed value + v->m_next = v->m_prev = 0; + } + + /*! + \internal + Call \a fun for each element in this container. Fun should accept T* as a parameter. + \note In general it is not allowed to change this container by calling put() or get() unless + given value is the same as currently procceded by forEach. + */ + template + void forEach(Functor fun) + { + //dump(Q_FUNC_INFO); + QScriptLinkedNode *i = m_first; + QScriptLinkedNode *tmp; + while (i) { + tmp = i; + i = i->m_next; + fun(static_cast(tmp)); + } + } + + /*! + \internal + Clear this container. + */ + void clear() + { + m_first = 0; + } + + /*! + \internal + Returns true if this container is empty; false otherwise. + */ + bool isEmpty() const + { + return !m_first; + } + +// void dump(const char* msg, T* obj = 0) const +// { +// qDebug() << msg << obj; +// qDebug() << m_first; +// QScriptLinkedNode *i = m_first; +// while (i) { +// qDebug() <<" - " << i << "(" << i->m_prev << ", " << i->m_next <<")"; +// i = i->m_next; +// } +// } + +private: + bool contains(T *value) const + { + QScriptLinkedNode *i = m_first; + while (i) { + if (static_cast(i) == value) + return true; + i = i->m_next; + } + return false; + } + QScriptLinkedNode *m_first; +}; + +QT_END_NAMESPACE + +#endif //QSCRIPTTOOLS_P_H diff --git a/src/declarative/qml/v8/qv8bindings.cpp b/src/declarative/qml/v8/qv8bindings.cpp index 4f5543e690..c45274a97f 100644 --- a/src/declarative/qml/v8/qv8bindings.cpp +++ b/src/declarative/qml/v8/qv8bindings.cpp @@ -129,7 +129,7 @@ void QV8BindingsPrivate::Binding::update(QDeclarativePropertyPrivate::WriteFlags ep->referenceScarceResources(); v8::HandleScope handle_scope; - v8::Context::Scope scope(ep->v8engine.context()); + v8::Context::Scope scope(ep->v8engine()->context()); v8::Local result = evaluate(v8::Handle::Cast(parent->functions->Get(index)), &isUndefined); diff --git a/src/declarative/qml/v8/qv8engine.cpp b/src/declarative/qml/v8/qv8engine.cpp index 0294a60871..acbfb92b15 100644 --- a/src/declarative/qml/v8/qv8engine.cpp +++ b/src/declarative/qml/v8/qv8engine.cpp @@ -49,6 +49,7 @@ #include #include #include +#include #include @@ -58,10 +59,15 @@ #include #include #include -#include #include #include +#include "qscript_impl_p.h" + +Q_DECLARE_METATYPE(QJSValue) +Q_DECLARE_METATYPE(QList) + + // XXX TODO: Need to check all the global functions will also work in a worker script where the // QDeclarativeEngine is not available QT_BEGIN_NAMESPACE @@ -94,9 +100,41 @@ static bool ObjectComparisonCallback(v8::Local lhs, v8::Local::New(v8::Context::GetCurrent())) + , m_originalGlobalObject(this, m_context) + , m_xmlHttpRequestData(0) + , m_sqlDatabaseData(0) + , m_listModelData(0) { + qMetaTypeId(); + qMetaTypeId >(); + + QByteArray v8args = qgetenv("V8ARGS"); + if (!v8args.isEmpty()) + v8::V8::SetFlagsFromString(v8args.constData(), v8args.length()); + + v8::HandleScope handle_scope; + qPersistentRegister(m_context); + v8::Context::Scope context_scope(m_context); + + v8::V8::SetUserObjectComparisonCallbackFunction(ObjectComparisonCallback); + + m_stringWrapper.init(); + m_contextWrapper.init(this); + m_qobjectWrapper.init(this); + m_typeWrapper.init(this); + m_listWrapper.init(this); + m_variantWrapper.init(this); + m_valueTypeWrapper.init(this); + + { + v8::Handle v = global()->Get(v8::String::New("Object"))->ToObject()->Get(v8::String::New("getOwnPropertyNames")); + m_getOwnPropertyNames = qPersistentNew(v8::Handle::Cast(v)); + } } QV8Engine::~QV8Engine() @@ -114,6 +152,10 @@ QV8Engine::~QV8Engine() qPersistentDispose(m_freezeObject); qPersistentDispose(m_getOwnPropertyNames); + + invalidateAllValues(); + clearExceptions(); + m_valueTypeWrapper.destroy(); m_variantWrapper.destroy(); m_listWrapper.destroy(); @@ -121,39 +163,11 @@ QV8Engine::~QV8Engine() m_qobjectWrapper.destroy(); m_contextWrapper.destroy(); m_stringWrapper.destroy(); - qPersistentDispose(m_context); -} - -void QV8Engine::init(QDeclarativeEngine *engine) -{ - m_engine = engine; - - QByteArray v8args = qgetenv("V8ARGS"); - if (!v8args.isEmpty()) - v8::V8::SetFlagsFromString(v8args.constData(), v8args.length()); - - v8::HandleScope handle_scope; - m_context = v8::Context::New(); - qPersistentRegister(m_context); - v8::Context::Scope context_scope(m_context); - - v8::V8::SetUserObjectComparisonCallbackFunction(ObjectComparisonCallback); - m_stringWrapper.init(); - m_contextWrapper.init(this); - m_qobjectWrapper.init(this); - m_typeWrapper.init(this); - m_listWrapper.init(this); - m_variantWrapper.init(this); - m_valueTypeWrapper.init(this); + m_originalGlobalObject.destroy(); - { - v8::Handle v = global()->Get(v8::String::New("Object"))->ToObject()->Get(v8::String::New("getOwnPropertyNames")); - m_getOwnPropertyNames = qPersistentNew(v8::Handle::Cast(v)); - } - - initializeGlobal(m_context->Global()); - freezeObject(m_context->Global()); + if (m_ownsV8Context) + qPersistentDispose(m_context); } QString QV8Engine::toStringStatic(v8::Handle jsstr) @@ -357,8 +371,10 @@ v8::Handle QV8Engine::fromVariant(const QVariant &variant) break; } - if (QDeclarativeValueType *vt = QDeclarativeEnginePrivate::get(m_engine)->valueTypes[type]) - return m_valueTypeWrapper.newValueType(variant, vt); + if (m_engine) { + if (QDeclarativeValueType *vt = QDeclarativeEnginePrivate::get(m_engine)->valueTypes[type]) + return m_valueTypeWrapper.newValueType(variant, vt); + } } else { if (type == qMetaTypeId()) { @@ -386,7 +402,7 @@ v8::Handle QV8Engine::fromVariant(const QVariant &variant) } // XXX TODO: To be compatible, we still need to handle: - // + QScriptValue + // + QJSValue // + QObjectList // + QList @@ -420,6 +436,7 @@ const QSet &QV8Engine::illegalNames() const // Requires a handle scope v8::Local QV8Engine::getOwnPropertyNames(v8::Handle o) { + // FIXME Newer v8 have API for this function v8::TryCatch tc; v8::Handle args[] = { o }; v8::Local r = m_getOwnPropertyNames->Call(global(), 1, args); @@ -531,9 +548,6 @@ void QV8Engine::initializeGlobal(v8::Handle global) } } - if (m_engine) - qt->Set(v8::String::New("application"), newQObject(new QDeclarativeApplication(m_engine))); - qt->Set(v8::String::New("include"), V8FUNCTION(QV8Include::include, this)); qt->Set(v8::String::New("isQtObject"), V8FUNCTION(isQtObject, this)); qt->Set(v8::String::New("rgba"), V8FUNCTION(rgba, this)); @@ -543,12 +557,6 @@ void QV8Engine::initializeGlobal(v8::Handle global) qt->Set(v8::String::New("size"), V8FUNCTION(size, this)); qt->Set(v8::String::New("vector3d"), V8FUNCTION(vector3d, this)); - if (m_engine) { - qt->Set(v8::String::New("lighter"), V8FUNCTION(lighter, this)); - qt->Set(v8::String::New("darker"), V8FUNCTION(darker, this)); - qt->Set(v8::String::New("tint"), V8FUNCTION(tint, this)); - } - qt->Set(v8::String::New("formatDate"), V8FUNCTION(formatDate, this)); qt->Set(v8::String::New("formatTime"), V8FUNCTION(formatTime, this)); qt->Set(v8::String::New("formatDateTime"), V8FUNCTION(formatDateTime, this)); @@ -561,6 +569,10 @@ void QV8Engine::initializeGlobal(v8::Handle global) qt->Set(v8::String::New("resolvedUrl"), V8FUNCTION(resolvedUrl, this)); if (m_engine) { + qt->Set(v8::String::New("application"), newQObject(new QDeclarativeApplication(m_engine))); + qt->Set(v8::String::New("lighter"), V8FUNCTION(lighter, this)); + qt->Set(v8::String::New("darker"), V8FUNCTION(darker, this)); + qt->Set(v8::String::New("tint"), V8FUNCTION(tint, this)); qt->Set(v8::String::New("quit"), V8FUNCTION(quit, this)); qt->Set(v8::String::New("createQmlObject"), V8FUNCTION(createQmlObject, this)); qt->Set(v8::String::New("createComponent"), V8FUNCTION(createComponent, this)); @@ -1658,5 +1670,652 @@ v8::Handle QV8Engine::qsTrIdNoOp(const v8::Arguments &args) return args[0]; } +void QV8Engine::initDeclarativeGlobalObject() +{ + v8::HandleScope handels; + v8::Context::Scope contextScope(m_context); + initializeGlobal(m_context->Global()); + freezeObject(m_context->Global()); +} + +void QV8Engine::setEngine(QDeclarativeEngine *engine) +{ + m_engine = engine; + initDeclarativeGlobalObject(); +} + +void QV8Engine::setException(v8::Handle value, v8::Handle msg) +{ + m_exception.set(value, msg); +} + +v8::Handle QV8Engine::throwException(v8::Handle value) +{ + setException(value); + v8::ThrowException(value); + return value; +} + +void QV8Engine::clearExceptions() +{ + m_exception.clear(); +} + +v8::Handle QV8Engine::uncaughtException() const +{ + if (!hasUncaughtException()) + return v8::Handle(); + return m_exception; +} + +bool QV8Engine::hasUncaughtException() const +{ + return m_exception; +} + +int QV8Engine::uncaughtExceptionLineNumber() const +{ + return m_exception.lineNumber(); +} + +QStringList QV8Engine::uncaughtExceptionBacktrace() const +{ + return m_exception.backtrace(); +} + +/*! + \internal + Save the current exception on stack so it can be set again later. + \sa QV8Engine::restoreException +*/ +void QV8Engine::saveException() +{ + m_exception.push(); +} + +/*! + \internal + Load a saved exception from stack. Current exception, if exists will be dropped + \sa QV8Engine::saveException +*/ +void QV8Engine::restoreException() +{ + m_exception.pop(); +} + +QV8Engine::Exception::Exception() {} + +QV8Engine::Exception::~Exception() +{ + Q_ASSERT_X(m_stack.isEmpty(), Q_FUNC_INFO, "Some saved exceptions left. Asymetric pop/push found."); + clear(); +} + +void QV8Engine::Exception::set(v8::Handle value, v8::Handle message) +{ + Q_ASSERT_X(!value.IsEmpty(), Q_FUNC_INFO, "Throwing an empty value handle is highly suspected"); + clear(); + m_value = v8::Persistent::New(value); + m_message = v8::Persistent::New(message); +} + +void QV8Engine::Exception::clear() +{ + m_value.Dispose(); + m_value.Clear(); + m_message.Dispose(); + m_message.Clear(); +} + +QV8Engine::Exception::operator bool() const +{ + return !m_value.IsEmpty(); +} + +QV8Engine::Exception::operator v8::Handle() const +{ + Q_ASSERT(*this); + return m_value; +} + +int QV8Engine::Exception::lineNumber() const +{ + if (m_message.IsEmpty()) + return -1; + return m_message->GetLineNumber(); +} + +QStringList QV8Engine::Exception::backtrace() const +{ + if (m_message.IsEmpty()) + return QStringList(); + + QStringList backtrace; + v8::Handle trace = m_message->GetStackTrace(); + if (trace.IsEmpty()) + // FIXME it should not happen (SetCaptureStackTraceForUncaughtExceptions is called). + return QStringList(); + + for (int i = 0; i < trace->GetFrameCount(); ++i) { + v8::Local frame = trace->GetFrame(i); + backtrace.append(QJSConverter::toString(frame->GetFunctionName())); + backtrace.append(QJSConverter::toString(frame->GetFunctionName())); + backtrace.append(QString::fromAscii("()@")); + backtrace.append(QJSConverter::toString(frame->GetScriptName())); + backtrace.append(QString::fromAscii(":")); + backtrace.append(QString::number(frame->GetLineNumber())); + } + return backtrace; +} + +void QV8Engine::Exception::push() +{ + m_stack.push(qMakePair(m_value, m_message)); + m_value.Clear(); + m_message.Clear(); +} + +void QV8Engine::Exception::pop() +{ + Q_ASSERT_X(!m_stack.empty(), Q_FUNC_INFO, "Attempt to load unsaved exception found"); + ValueMessagePair pair = m_stack.pop(); + clear(); + m_value = pair.first; + m_message = pair.second; +} + +QScriptPassPointer QV8Engine::newRegExp(const QString &pattern, const QString &flags) +{ + int f = v8::RegExp::kNone; + + QString::const_iterator i = flags.constBegin(); + for (; i != flags.constEnd(); ++i) { + switch (i->unicode()) { + case 'i': + f |= v8::RegExp::kIgnoreCase; + break; + case 'm': + f |= v8::RegExp::kMultiline; + break; + case 'g': + f |= v8::RegExp::kGlobal; + break; + default: + { + // ignore a Syntax Error. + } + } + } + + v8::Handle regexp = v8::RegExp::New(QJSConverter::toString(pattern), static_cast(f)); + return new QJSValuePrivate(this, regexp); +} + +QScriptPassPointer QV8Engine::newRegExp(const QRegExp ®exp) +{ + return new QJSValuePrivate(this, QJSConverter::toRegExp(regexp)); +} + + +// Converts a QVariantList to JS. +// The result is a new Array object with length equal to the length +// of the QVariantList, and the elements being the QVariantList's +// elements converted to JS, recursively. +v8::Handle QV8Engine::variantListToJS(const QVariantList &lst) +{ + v8::Handle result = v8::Array::New(lst.size()); + for (int i = 0; i < lst.size(); ++i) + result->Set(i, variantToJS(lst.at(i))); + return result; +} + +// Converts a JS Array object to a QVariantList. +// The result is a QVariantList with length equal to the length +// of the JS Array, and elements being the JS Array's elements +// converted to QVariants, recursively. +QVariantList QV8Engine::variantListFromJS(v8::Handle jsArray) +{ + QVariantList result; + int hash = jsArray->GetIdentityHash(); + if (visitedConversionObjects.contains(hash)) + return result; // Avoid recursion. + v8::HandleScope handleScope; + visitedConversionObjects.insert(hash); + uint32_t length = jsArray->Length(); + for (uint32_t i = 0; i < length; ++i) + result.append(variantFromJS(jsArray->Get(i))); + visitedConversionObjects.remove(hash); + return result; +} + +// Converts a QVariantMap to JS. +// The result is a new Object object with property names being +// the keys of the QVariantMap, and values being the values of +// the QVariantMap converted to JS, recursively. +v8::Handle QV8Engine::variantMapToJS(const QVariantMap &vmap) +{ + v8::Handle result = v8::Object::New(); + QVariantMap::const_iterator it; + for (it = vmap.constBegin(); it != vmap.constEnd(); ++it) + result->Set(QJSConverter::toString(it.key()), variantToJS(it.value())); + return result; +} + +// Converts a JS Object to a QVariantMap. +// The result is a QVariantMap with keys being the property names +// of the object, and values being the values of the JS object's +// properties converted to QVariants, recursively. +QVariantMap QV8Engine::variantMapFromJS(v8::Handle jsObject) +{ + QVariantMap result; + int hash = jsObject->GetIdentityHash(); + if (visitedConversionObjects.contains(hash)) + return result; // Avoid recursion. + visitedConversionObjects.insert(hash); + v8::HandleScope handleScope; + // TODO: Only object's own property names. Include non-enumerable properties. + v8::Handle propertyNames = jsObject->GetPropertyNames(); + uint32_t length = propertyNames->Length(); + for (uint32_t i = 0; i < length; ++i) { + v8::Handle name = propertyNames->Get(i); + result.insert(QJSConverter::toString(name->ToString()), variantFromJS(jsObject->Get(name))); + } + visitedConversionObjects.remove(hash); + return result; +} + +// Converts the meta-type defined by the given type and data to JS. +// Returns the value if conversion succeeded, an empty handle otherwise. +v8::Handle QV8Engine::metaTypeToJS(int type, const void *data) +{ + Q_ASSERT(data != 0); + v8::Handle result; + + // check if it's one of the types we know + switch (QMetaType::Type(type)) { + case QMetaType::Void: + return v8::Undefined(); + case QMetaType::Bool: + return v8::Boolean::New(*reinterpret_cast(data)); + case QMetaType::Int: + return v8::Int32::New(*reinterpret_cast(data)); + case QMetaType::UInt: + return v8::Uint32::New(*reinterpret_cast(data)); + case QMetaType::LongLong: + return v8::Number::New(double(*reinterpret_cast(data))); + case QMetaType::ULongLong: +#if defined(Q_OS_WIN) && defined(_MSC_FULL_VER) && _MSC_FULL_VER <= 12008804 +#pragma message("** NOTE: You need the Visual Studio Processor Pack to compile support for 64bit unsigned integers.") + return v8::Number::New(double((qlonglong)*reinterpret_cast(data))); +#elif defined(Q_CC_MSVC) && !defined(Q_CC_MSVC_NET) + return v8::Number::New(double((qlonglong)*reinterpret_cast(data))); +#else + return v8::Number::New(double(*reinterpret_cast(data))); +#endif + case QMetaType::Double: + return v8::Number::New(double(*reinterpret_cast(data))); + case QMetaType::QString: + return QJSConverter::toString(*reinterpret_cast(data)); + case QMetaType::Float: + return v8::Number::New(*reinterpret_cast(data)); + case QMetaType::Short: + return v8::Int32::New(*reinterpret_cast(data)); + case QMetaType::UShort: + return v8::Uint32::New(*reinterpret_cast(data)); + case QMetaType::Char: + return v8::Int32::New(*reinterpret_cast(data)); + case QMetaType::UChar: + return v8::Uint32::New(*reinterpret_cast(data)); + case QMetaType::QChar: + return v8::Uint32::New((*reinterpret_cast(data)).unicode()); + case QMetaType::QStringList: + result = QJSConverter::toStringList(*reinterpret_cast(data)); + break; + case QMetaType::QVariantList: + result = variantListToJS(*reinterpret_cast(data)); + break; + case QMetaType::QVariantMap: + result = variantMapToJS(*reinterpret_cast(data)); + break; + case QMetaType::QDateTime: + result = QJSConverter::toDateTime(*reinterpret_cast(data)); + break; + case QMetaType::QDate: + result = QJSConverter::toDateTime(QDateTime(*reinterpret_cast(data))); + break; + case QMetaType::QRegExp: + result = QJSConverter::toRegExp(*reinterpret_cast(data)); + break; + case QMetaType::QObjectStar: + case QMetaType::QWidgetStar: + result = newQObject(*reinterpret_cast(data)); + break; + case QMetaType::QVariant: + result = variantToJS(*reinterpret_cast(data)); + break; + default: + if (type == qMetaTypeId()) { + return QJSValuePrivate::get(*reinterpret_cast(data))->asV8Value(this); + } else { + QByteArray typeName = QMetaType::typeName(type); + if (typeName.endsWith('*') && !*reinterpret_cast(data)) { + return v8::Null(); + } else { + // Fall back to wrapping in a QVariant. + result = newVariant(QVariant(type, data)); + } + } + } + return result; +} + +// Converts a JS value to a meta-type. +// data must point to a place that can store a value of the given type. +// Returns true if conversion succeeded, false otherwise. +bool QV8Engine::metaTypeFromJS(v8::Handle value, int type, void *data) { + // check if it's one of the types we know + switch (QMetaType::Type(type)) { + case QMetaType::Bool: + *reinterpret_cast(data) = value->ToBoolean()->Value(); + return true; + case QMetaType::Int: + *reinterpret_cast(data) = value->ToInt32()->Value(); + return true; + case QMetaType::UInt: + *reinterpret_cast(data) = value->ToUint32()->Value(); + return true; + case QMetaType::LongLong: + *reinterpret_cast(data) = qlonglong(value->ToInteger()->Value()); + return true; + case QMetaType::ULongLong: + *reinterpret_cast(data) = qulonglong(value->ToInteger()->Value()); + return true; + case QMetaType::Double: + *reinterpret_cast(data) = value->ToNumber()->Value(); + return true; + case QMetaType::QString: + if (value->IsUndefined() || value->IsNull()) + *reinterpret_cast(data) = QString(); + else + *reinterpret_cast(data) = QJSConverter::toString(value->ToString()); + return true; + case QMetaType::Float: + *reinterpret_cast(data) = value->ToNumber()->Value(); + return true; + case QMetaType::Short: + *reinterpret_cast(data) = short(value->ToInt32()->Value()); + return true; + case QMetaType::UShort: + *reinterpret_cast(data) = ushort(value->ToInt32()->Value()); // ### QScript::ToUInt16() + return true; + case QMetaType::Char: + *reinterpret_cast(data) = char(value->ToInt32()->Value()); + return true; + case QMetaType::UChar: + *reinterpret_cast(data) = (unsigned char)(value->ToInt32()->Value()); + return true; + case QMetaType::QChar: + if (value->IsString()) { + QString str = QJSConverter::toString(v8::Handle::Cast(value)); + *reinterpret_cast(data) = str.isEmpty() ? QChar() : str.at(0); + } else { + *reinterpret_cast(data) = QChar(ushort(value->ToInt32()->Value())); // ### QScript::ToUInt16() + } + return true; + case QMetaType::QDateTime: + if (value->IsDate()) { + *reinterpret_cast(data) = QJSConverter::toDateTime(v8::Handle::Cast(value)); + return true; + } break; + case QMetaType::QDate: + if (value->IsDate()) { + *reinterpret_cast(data) = QJSConverter::toDateTime(v8::Handle::Cast(value)).date(); + return true; + } break; + case QMetaType::QRegExp: + if (value->IsRegExp()) { + *reinterpret_cast(data) = QJSConverter::toRegExp(v8::Handle::Cast(value)); + return true; + } break; + case QMetaType::QObjectStar: + if (isQObject(value) || value->IsNull()) { + *reinterpret_cast(data) = qtObjectFromJS(value); + return true; + } break; + case QMetaType::QWidgetStar: + if (isQObject(value) || value->IsNull()) { + QObject *qo = qtObjectFromJS(value); + if (!qo || qo->isWidgetType()) { + *reinterpret_cast(data) = reinterpret_cast(qo); + return true; + } + } break; + case QMetaType::QStringList: + if (value->IsArray()) { + *reinterpret_cast(data) = QJSConverter::toStringList(v8::Handle::Cast(value)); + return true; + } break; + case QMetaType::QVariantList: + if (value->IsArray()) { + *reinterpret_cast(data) = variantListFromJS(v8::Handle::Cast(value)); + return true; + } break; + case QMetaType::QVariantMap: + if (value->IsObject()) { + *reinterpret_cast(data) = variantMapFromJS(v8::Handle::Cast(value)); + return true; + } break; + case QMetaType::QVariant: + *reinterpret_cast(data) = variantFromJS(value); + return true; + default: + ; + } + +#if 0 + if (isQtVariant(value)) { + const QVariant &var = variantValue(value); + // ### Enable once constructInPlace() is in qt master. + if (var.userType() == type) { + QMetaType::constructInPlace(type, data, var.constData()); + return true; + } + if (var.canConvert(QVariant::Type(type))) { + QVariant vv = var; + vv.convert(QVariant::Type(type)); + Q_ASSERT(vv.userType() == type); + QMetaType::constructInPlace(type, data, vv.constData()); + return true; + } + + } +#endif + + // Try to use magic. + + QByteArray name = QMetaType::typeName(type); + if (convertToNativeQObject(value, name, reinterpret_cast(data))) + return true; + if (isVariant(value) && name.endsWith('*')) { + int valueType = QMetaType::type(name.left(name.size()-1)); + QVariant var = variantValue(value); + if (valueType == var.userType()) { + // We have T t, T* is requested, so return &t. + *reinterpret_cast(data) = var.data(); + return true; + } else { + // Look in the prototype chain. + v8::Handle proto = value->ToObject()->GetPrototype(); + while (proto->IsObject()) { + bool canCast = false; + if (isVariant(proto)) { + canCast = (type == variantValue(proto).userType()) + || (valueType && (valueType == variantValue(proto).userType())); + } + else if (isQObject(proto)) { + QByteArray className = name.left(name.size()-1); + if (QObject *qobject = qtObjectFromJS(proto)) + canCast = qobject->qt_metacast(className) != 0; + } + if (canCast) { + QByteArray varTypeName = QMetaType::typeName(var.userType()); + if (varTypeName.endsWith('*')) + *reinterpret_cast(data) = *reinterpret_cast(var.data()); + else + *reinterpret_cast(data) = var.data(); + return true; + } + proto = proto->ToObject()->GetPrototype(); + } + } + } else if (value->IsNull() && name.endsWith('*')) { + *reinterpret_cast(data) = 0; + return true; + } else if (type == qMetaTypeId()) { + *reinterpret_cast(data) = QJSValuePrivate::get(new QJSValuePrivate(this, value)); + return true; + } + + return false; +} + +// Converts a QVariant to JS. +v8::Handle QV8Engine::variantToJS(const QVariant &value) +{ + return metaTypeToJS(value.userType(), value.constData()); +} + +// Converts a JS value to a QVariant. +// Null, Undefined -> QVariant() (invalid) +// Boolean -> QVariant(bool) +// Number -> QVariant(double) +// String -> QVariant(QString) +// Array -> QVariantList(...) +// Date -> QVariant(QDateTime) +// RegExp -> QVariant(QRegExp) +// [Any other object] -> QVariantMap(...) +QVariant QV8Engine::variantFromJS(v8::Handle value) +{ + Q_ASSERT(!value.IsEmpty()); + if (value->IsNull() || value->IsUndefined()) + return QVariant(); + if (value->IsBoolean()) + return value->ToBoolean()->Value(); + else if (value->IsInt32()) + return value->ToInt32()->Value(); + else if (value->IsNumber()) + return value->ToNumber()->Value(); + if (value->IsString()) + return QJSConverter::toString(value->ToString()); + Q_ASSERT(value->IsObject()); + if (value->IsArray()) + return variantListFromJS(v8::Handle::Cast(value)); + if (value->IsDate()) + return QJSConverter::toDateTime(v8::Handle::Cast(value)); + if (value->IsRegExp()) + return QJSConverter::toRegExp(v8::Handle::Cast(value)); + if (isVariant(value)) + return variantValue(value); + if (isQObject(value)) + return qVariantFromValue(qtObjectFromJS(value)); + return variantMapFromJS(value->ToObject()); +} + +bool QV8Engine::convertToNativeQObject(v8::Handle value, + const QByteArray &targetType, + void **result) +{ + if (!targetType.endsWith('*')) + return false; + if (QObject *qobject = qtObjectFromJS(value)) { + int start = targetType.startsWith("const ") ? 6 : 0; + QByteArray className = targetType.mid(start, targetType.size()-start-1); + if (void *instance = qobject->qt_metacast(className)) { + *result = instance; + return true; + } + } + return false; +} + +QObject *QV8Engine::qtObjectFromJS(v8::Handle value) +{ + if (!value->IsObject()) + return 0; + + QV8ObjectResource *r = (QV8ObjectResource *)value->ToObject()->GetExternalResource(); + if (!r) + return 0; + QV8ObjectResource::ResourceType type = r->resourceType(); + if (type == QV8ObjectResource::QObjectType) + return qobjectWrapper()->toQObject(r); + else if (type == QV8ObjectResource::VariantType) { + QVariant variant = variantWrapper()->toVariant(r); + int type = variant.userType(); + if ((type == QMetaType::QObjectStar) || (type == QMetaType::QWidgetStar)) + return *reinterpret_cast(variant.constData()); + } + return 0; +} + + +QVariant QV8Engine::variantValue(v8::Handle value) +{ + Q_ASSERT(isVariant(value)); + return QV8Engine::toVariant(value, -1 /*whateever magic hint is*/); +} + +// Creates a QVariant wrapper object. +v8::Handle QV8Engine::newVariant(const QVariant &value) +{ + v8::Handle instance = variantWrapper()->newVariant(value); + return instance; +} + +QScriptPassPointer QV8Engine::evaluate(v8::Handle script, v8::TryCatch& tryCatch) +{ + v8::HandleScope handleScope; + + clearExceptions(); + if (script.IsEmpty()) { + v8::Handle exception = tryCatch.Exception(); + if (exception.IsEmpty()) { + // This is possible on syntax errors like { a:12, b:21 } <- missing "(", ")" around expression. + return InvalidValue(); + } + setException(exception, tryCatch.Message()); + return new QJSValuePrivate(this, exception); + } + v8::Handle result; + result = script->Run(); + if (result.IsEmpty()) { + v8::Handle exception = tryCatch.Exception(); + // TODO: figure out why v8 doesn't always produce an exception value + //Q_ASSERT(!exception.IsEmpty()); + if (exception.IsEmpty()) + exception = v8::Exception::Error(v8::String::New("missing exception value")); + setException(exception, tryCatch.Message()); + return new QJSValuePrivate(this, exception); + } + return new QJSValuePrivate(this, result); +} + +QJSValue QV8Engine::scriptValueFromInternal(v8::Handle value) const +{ + if (value.IsEmpty()) + return QJSValuePrivate::get(InvalidValue()); + return QJSValuePrivate::get(new QJSValuePrivate(const_cast(this), value)); +} + +QScriptPassPointer QV8Engine::newArray(uint length) +{ + return new QJSValuePrivate(this, v8::Array::New(length)); +} + +void QV8Engine::emitSignalHandlerException() +{ + emit q->signalHandlerException(scriptValueFromInternal(uncaughtException())); +} + QT_END_NAMESPACE diff --git a/src/declarative/qml/v8/qv8engine_impl_p.h b/src/declarative/qml/v8/qv8engine_impl_p.h new file mode 100644 index 0000000000..5c56efdf39 --- /dev/null +++ b/src/declarative/qml/v8/qv8engine_impl_p.h @@ -0,0 +1,131 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL-ONLY$ +** GNU Lesser General Public License Usage +** 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV8ENGINE_IMPL_P_H +#define QV8ENGINE_IMPL_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 "qv8engine_p.h" +#include "qjsvalue_p.h" +#include "qjsconverter_p.h" + +QT_BEGIN_NAMESPACE + +inline v8::Handle QV8Engine::makeJSValue(bool value) +{ + return value ? v8::True() : v8::False(); +} + +inline v8::Handle QV8Engine::makeJSValue(int value) +{ + return v8::Integer::New(value); +} + +inline v8::Handle QV8Engine::makeJSValue(uint value) +{ + return v8::Integer::NewFromUnsigned(value); +} + +inline v8::Handle QV8Engine::makeJSValue(double value) +{ + return v8::Number::New(value); +} + +inline v8::Handle QV8Engine::makeJSValue(QJSValue::SpecialValue value) { + if (value == QJSValue::NullValue) + return v8::Null(); + return v8::Undefined(); +} + +inline v8::Handle QV8Engine::makeJSValue(const QString& value) +{ + return QJSConverter::toString(value); +} + +class QtScriptBagCleaner +{ +public: + template + void operator () (T* value) const + { + value->reinitialize(); + } +}; + +inline void QV8Engine::registerValue(QJSValuePrivate *data) +{ + m_values.insert(data); +} + +inline void QV8Engine::unregisterValue(QJSValuePrivate *data) +{ + m_values.remove(data); +} + +inline void QV8Engine::invalidateAllValues() +{ + QtScriptBagCleaner invalidator; + m_values.forEach(invalidator); + m_values.clear(); +} + +/*! + \internal + \note property can be index (v8::Integer) or a property (v8::String) name, according to ECMA script + property would be converted to a string. +*/ +inline QJSValue::PropertyFlags QV8Engine::getPropertyFlags(v8::Handle object, v8::Handle property) +{ + QJSValue::PropertyFlags flags = m_originalGlobalObject.getPropertyFlags(object, property); + return flags; +} + +QScriptPassPointer QV8Engine::evaluate(const QString& program, const QString& fileName, int lineNumber) +{ + v8::TryCatch tryCatch; + v8::ScriptOrigin scriptOrigin(QJSConverter::toString(fileName), v8::Integer::New(lineNumber - 1)); + v8::Handle script; + script = v8::Script::Compile(QJSConverter::toString(program), &scriptOrigin); + if (script.IsEmpty()) { + // TODO: Why don't we get the exception, as with Script::Compile()? + // Q_ASSERT(tryCatch.HasCaught()); + v8::Handle error = v8::Exception::SyntaxError(v8::String::New("")); + setException(error); + return new QJSValuePrivate(this, error); + } + return evaluate(script, tryCatch); +} + +QT_END_NAMESPACE + +#endif // QV8ENGINE_IMPL_P_H diff --git a/src/declarative/qml/v8/qv8engine_p.h b/src/declarative/qml/v8/qv8engine_p.h index 340945c6ee..b95e55002b 100644 --- a/src/declarative/qml/v8/qv8engine_p.h +++ b/src/declarative/qml/v8/qv8engine_p.h @@ -57,7 +57,14 @@ #include #include #include +#include +#include + #include +#include +#include +#include "qscriptoriginalglobalobject_p.h" +#include "qscripttools_p.h" #include @@ -210,18 +217,49 @@ class QDeclarativeContextData; class Q_DECLARATIVE_EXPORT QV8Engine { public: - QV8Engine(); + static QV8Engine* get(QJSEngine* q) { Q_ASSERT(q); return q->handle(); } + static QJSEngine* get(QV8Engine* d) { Q_ASSERT(d); return d->q; } + + QV8Engine(QJSEngine* qq,QJSEngine::ContextOwnership ownership = QJSEngine::CreateNewContext); ~QV8Engine(); struct Deletable { virtual ~Deletable() {} }; - void init(QDeclarativeEngine *); + class Exception + { + typedef QPair, v8::Persistent > ValueMessagePair; + + v8::Persistent m_value; + v8::Persistent m_message; + QStack m_stack; + + Q_DISABLE_COPY(Exception) + public: + inline Exception(); + inline ~Exception(); + inline void set(v8::Handle value, v8::Handle message); + inline void clear(); + inline operator bool() const; + inline operator v8::Handle() const; + inline int lineNumber() const; + inline QStringList backtrace() const; + + inline void push(); + inline void pop(); + }; + void initDeclarativeGlobalObject(); + void setEngine(QDeclarativeEngine *engine); QDeclarativeEngine *engine() { return m_engine; } v8::Local global() { return m_context->Global(); } - v8::Handle context() { return m_context; } + v8::Handle context() const { return m_context; } + + inline void registerValue(QJSValuePrivate *data); + inline void unregisterValue(QJSValuePrivate *data); + inline void invalidateAllValues(); + QV8ContextWrapper *contextWrapper() { return &m_contextWrapper; } QV8QObjectWrapper *qobjectWrapper() { return &m_qobjectWrapper; } QV8TypeWrapper *typeWrapper() { return &m_typeWrapper; } @@ -237,6 +275,7 @@ class Q_DECLARATIVE_EXPORT QV8Engine QDeclarativeContextData *callingContext(); v8::Local getOwnPropertyNames(v8::Handle); + inline QJSValue::PropertyFlags getPropertyFlags(v8::Handle object, v8::Handle property); void freezeObject(v8::Handle); inline QString toString(v8::Handle string); @@ -257,6 +296,9 @@ class Q_DECLARATIVE_EXPORT QV8Engine // Return the QML global "scope" object for the \a ctxt context and \a scope object. inline v8::Local qmlScope(QDeclarativeContextData *ctxt, QObject *scope); + QScriptPassPointer newRegExp(const QRegExp ®exp); + QScriptPassPointer newRegExp(const QString &pattern, const QString &flags); + // Return a JS wrapper for the given QObject \a object inline v8::Handle newQObject(QObject *object); inline bool isQObject(v8::Handle); @@ -281,8 +323,19 @@ class Q_DECLARATIVE_EXPORT QV8Engine // Return the list of illegal id names (the names of the properties on the global object) const QSet &illegalNames() const; + inline void collectGarbage() { gc(); } static void gc(); + void clearExceptions(); + void setException(v8::Handle value, v8::Handle message = v8::Handle()); + v8::Handle throwException(v8::Handle value); + bool hasUncaughtException() const; + int uncaughtExceptionLineNumber() const; + QStringList uncaughtExceptionBacktrace() const; + v8::Handle uncaughtException() const; + void saveException(); + void restoreException(); + #ifdef QML_GLOBAL_HANDLE_DEBUGGING // Used for handle debugging static void registerHandle(void *); @@ -295,9 +348,50 @@ class Q_DECLARATIVE_EXPORT QV8Engine inline Deletable *extensionData(int) const; void setExtensionData(int, Deletable *); -private: + inline v8::Handle makeJSValue(bool value); + inline v8::Handle makeJSValue(int value); + inline v8::Handle makeJSValue(uint value); + inline v8::Handle makeJSValue(double value); + inline v8::Handle makeJSValue(QJSValue::SpecialValue value); + inline v8::Handle makeJSValue(const QString& value); + + inline QScriptPassPointer evaluate(const QString &program, const QString &fileName = QString(), int lineNumber = 1); + QScriptPassPointer evaluate(v8::Handle script, v8::TryCatch& tryCatch); + + QScriptPassPointer newArray(uint length); + v8::Handle newVariant(const QVariant &variant); + QScriptPassPointer newVariant(QJSValuePrivate* value, const QVariant &variant); + + v8::Handle variantListToJS(const QVariantList &lst); + QVariantList variantListFromJS(v8::Handle jsArray); + + v8::Handle variantMapToJS(const QVariantMap &vmap); + QVariantMap variantMapFromJS(v8::Handle jsObject); + + v8::Handle variantToJS(const QVariant &value); + QVariant variantFromJS(v8::Handle value); + + v8::Handle metaTypeToJS(int type, const void *data); + bool metaTypeFromJS(v8::Handle value, int type, void *data); + + bool convertToNativeQObject(v8::Handle value, + const QByteArray &targetType, + void **result); + + QVariant variantValue(v8::Handle value); + + QJSValue scriptValueFromInternal(v8::Handle) const; + + void emitSignalHandlerException(); + + QObject *qtObjectFromJS(v8::Handle value); + QSet visitedConversionObjects; +protected: + QJSEngine* q; QDeclarativeEngine *m_engine; + bool m_ownsV8Context; v8::Persistent m_context; + QScriptOriginalGlobalObject m_originalGlobalObject; QV8StringWrapper m_stringWrapper; QV8ContextWrapper m_contextWrapper; @@ -318,6 +412,8 @@ class Q_DECLARATIVE_EXPORT QV8Engine QSet m_illegalNames; + Exception m_exception; + QVariant toBasicVariant(v8::Handle); void initializeGlobal(v8::Handle); @@ -356,6 +452,10 @@ class Q_DECLARATIVE_EXPORT QV8Engine double qtDateTimeToJsDate(const QDateTime &dt); QDateTime qtDateTimeFromJsDate(double jsDate); +private: + QScriptBagContainer m_values; + + Q_DISABLE_COPY(QV8Engine) }; // Allocate a new Persistent handle. *ALL* persistent handles in QML must be allocated diff --git a/src/declarative/qml/v8/qv8include.cpp b/src/declarative/qml/v8/qv8include.cpp index e2161a9001..71937d0aad 100644 --- a/src/declarative/qml/v8/qv8include.cpp +++ b/src/declarative/qml/v8/qv8include.cpp @@ -41,7 +41,7 @@ #include "qv8include_p.h" -#include +#include #include #include #include diff --git a/src/declarative/qml/v8/qv8qobjectwrapper.cpp b/src/declarative/qml/v8/qv8qobjectwrapper.cpp index cc0380e684..f97f427ede 100644 --- a/src/declarative/qml/v8/qv8qobjectwrapper.cpp +++ b/src/declarative/qml/v8/qv8qobjectwrapper.cpp @@ -48,15 +48,17 @@ #include #include #include +#include +#include -#include +#include #include #include #include QT_BEGIN_NAMESPACE -Q_DECLARE_METATYPE(QScriptValue); +Q_DECLARE_METATYPE(QJSValue); Q_DECLARE_METATYPE(QDeclarativeV8Handle); #if defined(__GNUC__) @@ -137,7 +139,7 @@ struct MetaCallArgument { QString *qstringPtr; QVariant *qvariantPtr; QList *qlistPtr; - QScriptValue *qscriptValuePtr; + QJSValue *qjsValuePtr; QDeclarativeV8Handle *handlePtr; }; @@ -691,7 +693,9 @@ v8::Handle QV8QObjectWrapper::Enumerator(const v8::AccessorInfo &info QStringList result; - QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(resource->engine->engine()); + QDeclarativeEnginePrivate *ep = resource->engine->engine() + ? QDeclarativeEnginePrivate::get(resource->engine->engine()) + : 0; QDeclarativePropertyCache *cache = 0; QDeclarativeData *ddata = QDeclarativeData::get(object); @@ -699,7 +703,7 @@ v8::Handle QV8QObjectWrapper::Enumerator(const v8::AccessorInfo &info cache = ddata->propertyCache; if (!cache) { - cache = ep->cache(object); + cache = ep ? ep->cache(object) : 0; if (cache) { if (ddata) { cache->addref(); ddata->propertyCache = cache; } } else { @@ -1837,8 +1841,8 @@ void MetaCallArgument::cleanup() qstringPtr->~QString(); } else if (type == -1 || type == QMetaType::QVariant) { qvariantPtr->~QVariant(); - } else if (type == qMetaTypeId()) { - qscriptValuePtr->~QScriptValue(); + } else if (type == qMetaTypeId()) { + qjsValuePtr->~QJSValue(); } else if (type == qMetaTypeId >()) { qlistPtr->~QList(); } @@ -1857,8 +1861,8 @@ void MetaCallArgument::initAsType(int callType) if (type != 0) { cleanup(); type = 0; } if (callType == 0) return; - if (callType == qMetaTypeId()) { - qscriptValuePtr = new (&allocData) QScriptValue(); + if (callType == qMetaTypeId()) { + qjsValuePtr = new (&allocData) QJSValue(); type = callType; } else if (callType == QMetaType::Int || callType == QMetaType::UInt || @@ -1891,9 +1895,9 @@ void MetaCallArgument::fromValue(int callType, QV8Engine *engine, v8::Handle()) { - qscriptValuePtr = new (&allocData) QScriptValue(); - type = qMetaTypeId(); + if (callType == qMetaTypeId()) { + qjsValuePtr = new (&allocData) QJSValue(QJSValuePrivate::get(new QJSValuePrivate(engine, value))); + type = qMetaTypeId(); } else if (callType == QMetaType::Int) { intValue = quint32(value->Int32Value()); type = callType; @@ -1939,7 +1943,7 @@ void MetaCallArgument::fromValue(int callType, QV8Engine *engine, v8::Handleengine()); + QDeclarativeEnginePrivate *ep = engine->engine() ? QDeclarativeEnginePrivate::get(engine->engine()) : 0; QVariant v = engine->toVariant(value, -1); if (v.userType() == callType) { @@ -1947,7 +1951,7 @@ void MetaCallArgument::fromValue(int callType, QV8Engine *engine, v8::Handleconvert((QVariant::Type)callType); - } else if (const QMetaObject *mo = ep->rawMetaObjectForType(callType)) { + } else if (const QMetaObject *mo = ep ? ep->rawMetaObjectForType(callType) : 0) { QObject *obj = ep->toQObject(v); if (obj) { @@ -1965,8 +1969,8 @@ void MetaCallArgument::fromValue(int callType, QV8Engine *engine, v8::Handle MetaCallArgument::toValue(QV8Engine *engine) { - if (type == qMetaTypeId()) { - return v8::Undefined(); + if (type == qMetaTypeId()) { + return QJSValuePrivate::get(*qjsValuePtr)->asV8Value(engine); } else if (type == QMetaType::Int) { return v8::Integer::New(int(intValue)); } else if (type == QMetaType::UInt) { diff --git a/src/declarative/qml/v8/qv8typewrapper.cpp b/src/declarative/qml/v8/qv8typewrapper.cpp index 39d03dbeea..fe30670fc2 100644 --- a/src/declarative/qml/v8/qv8typewrapper.cpp +++ b/src/declarative/qml/v8/qv8typewrapper.cpp @@ -173,10 +173,12 @@ v8::Handle QV8TypeWrapper::Getter(v8::Local property, return v8engine->typeWrapper()->newObject(object, d->type, resource->mode); } else if (QDeclarativeMetaType::ModuleApiInstance *moduleApi = typeNamespace->moduleApi()) { - // XXX TODO: Currently module APIs are implemented against QScriptValues. Consequently we - // can't do anything for script module apis here until the QtScript/V8 binding is complete. - if (moduleApi->qobjectCallback) { - moduleApi->qobjectApi = moduleApi->qobjectCallback(v8engine->engine(), 0); + if (moduleApi->scriptCallback) { + moduleApi->scriptApi = moduleApi->scriptCallback(v8engine->engine(), v8engine->engine()); + moduleApi->scriptCallback = 0; + moduleApi->qobjectCallback = 0; + } else if (moduleApi->qobjectCallback) { + moduleApi->qobjectApi = moduleApi->qobjectCallback(v8engine->engine(), v8engine->engine()); moduleApi->scriptCallback = 0; moduleApi->qobjectCallback = 0; } @@ -225,10 +227,12 @@ v8::Handle QV8TypeWrapper::Setter(v8::Local property, QV8QObjectWrapper::IgnoreRevision); } else if (resource->typeNamespace) { if (QDeclarativeMetaType::ModuleApiInstance *moduleApi = resource->typeNamespace->moduleApi()) { - // XXX TODO: Currently module APIs are implemented against QScriptValues. Consequently we - // can't do anything for script module apis here until the QtScript/V8 binding is complete. - if (moduleApi->qobjectCallback) { - moduleApi->qobjectApi = moduleApi->qobjectCallback(v8engine->engine(), 0); + if (moduleApi->scriptCallback) { + moduleApi->scriptApi = moduleApi->scriptCallback(v8engine->engine(), v8engine->engine()); + moduleApi->scriptCallback = 0; + moduleApi->qobjectCallback = 0; + } else if (moduleApi->qobjectCallback) { + moduleApi->qobjectApi = moduleApi->qobjectCallback(v8engine->engine(), v8engine->engine()); moduleApi->scriptCallback = 0; moduleApi->qobjectCallback = 0; } diff --git a/src/declarative/qml/v8/qv8variantwrapper.cpp b/src/declarative/qml/v8/qv8variantwrapper.cpp index a5602fbbad..d4097d7f74 100644 --- a/src/declarative/qml/v8/qv8variantwrapper.cpp +++ b/src/declarative/qml/v8/qv8variantwrapper.cpp @@ -71,6 +71,7 @@ void QV8VariantWrapper::init(QV8Engine *engine) { m_engine = engine; m_toString = qPersistentNew(v8::FunctionTemplate::New(ToString)->GetFunction()); + m_valueOf = qPersistentNew(v8::FunctionTemplate::New(ValueOf)->GetFunction()); { v8::Local ft = v8::FunctionTemplate::New(); @@ -80,6 +81,9 @@ void QV8VariantWrapper::init(QV8Engine *engine) ft->InstanceTemplate()->SetAccessor(v8::String::New("toString"), ToStringGetter, 0, m_toString, v8::DEFAULT, v8::PropertyAttribute(v8::ReadOnly | v8::DontDelete)); + ft->InstanceTemplate()->SetAccessor(v8::String::New("valueOf"), ValueOfGetter, 0, + m_valueOf, v8::DEFAULT, + v8::PropertyAttribute(v8::ReadOnly | v8::DontDelete)); m_constructor = qPersistentNew(ft->GetFunction()); } { @@ -98,6 +102,9 @@ void QV8VariantWrapper::init(QV8Engine *engine) ft->InstanceTemplate()->SetAccessor(v8::String::New("toString"), ToStringGetter, 0, m_toString, v8::DEFAULT, v8::PropertyAttribute(v8::ReadOnly | v8::DontDelete)); + ft->InstanceTemplate()->SetAccessor(v8::String::New("valueOf"), ValueOfGetter, 0, + m_valueOf, v8::DEFAULT, + v8::PropertyAttribute(v8::ReadOnly | v8::DontDelete)); m_scarceConstructor = qPersistentNew(ft->GetFunction()); } @@ -105,6 +112,7 @@ void QV8VariantWrapper::init(QV8Engine *engine) void QV8VariantWrapper::destroy() { + qPersistentDispose(m_valueOf); qPersistentDispose(m_toString); qPersistentDispose(m_destroy); qPersistentDispose(m_preserve); @@ -185,6 +193,13 @@ v8::Handle QV8VariantWrapper::ToStringGetter(v8::Local pr return info.Data(); } +v8::Handle QV8VariantWrapper::ValueOfGetter(v8::Local property, + const v8::AccessorInfo &info) +{ + Q_UNUSED(property); + return info.Data(); +} + v8::Handle QV8VariantWrapper::Preserve(const v8::Arguments &args) { QV8VariantResource *resource = v8_resource_cast(args.This()); @@ -217,4 +232,27 @@ v8::Handle QV8VariantWrapper::ToString(const v8::Arguments &args) } } +v8::Handle QV8VariantWrapper::ValueOf(const v8::Arguments &args) +{ + QV8VariantResource *resource = v8_resource_cast(args.This()); + if (resource) { + QVariant v = resource->data; + switch (v.type()) { + case QVariant::Invalid: + return v8::Undefined(); + case QVariant::String: + return resource->engine->toString(v.toString()); + case QVariant::Int: + case QVariant::Double: + case QVariant::UInt: + return v8::Number::New(v.toDouble()); + case QVariant::Bool: + return v8::Boolean::New(v.toBool()); + default: + break; + } + } + return args.This(); +} + QT_END_NAMESPACE diff --git a/src/declarative/qml/v8/qv8variantwrapper_p.h b/src/declarative/qml/v8/qv8variantwrapper_p.h index 9e165f37f4..de74bc9e12 100644 --- a/src/declarative/qml/v8/qv8variantwrapper_p.h +++ b/src/declarative/qml/v8/qv8variantwrapper_p.h @@ -87,9 +87,12 @@ class QV8VariantWrapper const v8::AccessorInfo &info); static v8::Handle ToStringGetter(v8::Local property, const v8::AccessorInfo &info); + static v8::Handle ValueOfGetter(v8::Local property, + const v8::AccessorInfo &info); static v8::Handle Preserve(const v8::Arguments &args); static v8::Handle Destroy(const v8::Arguments &args); static v8::Handle ToString(const v8::Arguments &args); + static v8::Handle ValueOf(const v8::Arguments &args); QV8Engine *m_engine; v8::Persistent m_constructor; @@ -97,6 +100,7 @@ class QV8VariantWrapper v8::Persistent m_preserve; v8::Persistent m_destroy; v8::Persistent m_toString; + v8::Persistent m_valueOf; }; QT_END_NAMESPACE diff --git a/src/declarative/qml/v8/script.pri b/src/declarative/qml/v8/script.pri new file mode 100644 index 0000000000..04a23d1f2b --- /dev/null +++ b/src/declarative/qml/v8/script.pri @@ -0,0 +1,20 @@ + +INCLUDEPATH += $$PWD + +SOURCES += \ + $$PWD/qjsengine.cpp \ + $$PWD/qjsvalue.cpp \ + $$PWD/qjsvalueiterator.cpp \ + +HEADERS += \ + $$PWD/qjsengine.h \ + $$PWD/qjsvalue.h \ + $$PWD/qjsvalue_p.h \ + $$PWD/qjsvalueiterator.h \ + $$PWD/qjsvalue_impl_p.h \ + $$PWD/qjsconverter_p.h \ + $$PWD/qscriptisolate_p.h \ + $$PWD/qscriptshareddata_p.h \ + $$PWD/qscripttools_p.h \ + $$PWD/qscript_impl_p.h \ + $$PWD/qscriptoriginalglobalobject_p.h diff --git a/src/declarative/qml/v8/v8.pri b/src/declarative/qml/v8/v8.pri index 61e0184884..97b3d679df 100644 --- a/src/declarative/qml/v8/v8.pri +++ b/src/declarative/qml/v8/v8.pri @@ -1,6 +1,8 @@ INCLUDEPATH += $$PWD/../../../3rdparty/javascriptcore INCLUDEPATH += $$PWD +include(script.pri) + HEADERS += \ $$PWD/qv8_p.h \ $$PWD/qv8stringwrapper_p.h \ @@ -16,6 +18,7 @@ HEADERS += \ $$PWD/qv8worker_p.h \ $$PWD/qv8bindings_p.h \ $$PWD/../../../3rdparty/javascriptcore/DateMath.h \ + $$PWD/qv8engine_impl_p.h SOURCES += \ $$PWD/qv8stringwrapper.cpp \ @@ -31,4 +34,3 @@ SOURCES += \ $$PWD/qv8worker.cpp \ $$PWD/qv8bindings.cpp \ $$PWD/../../../3rdparty/javascriptcore/DateMath.cpp \ - diff --git a/src/declarative/util/qdeclarativebind.cpp b/src/declarative/util/qdeclarativebind.cpp index 6038aca8d5..726adf96d4 100644 --- a/src/declarative/util/qdeclarativebind.cpp +++ b/src/declarative/util/qdeclarativebind.cpp @@ -52,9 +52,8 @@ #include #include -#include -#include -#include +#include +#include #include diff --git a/src/declarative/util/qdeclarativelistmodel.cpp b/src/declarative/util/qdeclarativelistmodel.cpp index d1496fc886..035502140b 100644 --- a/src/declarative/util/qdeclarativelistmodel.cpp +++ b/src/declarative/util/qdeclarativelistmodel.cpp @@ -485,7 +485,7 @@ ModelObject *ModelNode::object(const NestedListModel *model) { if (!objectCache) { QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(qmlEngine(model->m_listModel)); - objectCache = new ModelObject(this, const_cast(model), &ep->v8engine); + objectCache = new ModelObject(this, const_cast(model), ep->v8engine()); QHash::iterator it; for (it = properties.begin(); it != properties.end(); ++it) { @@ -1451,7 +1451,7 @@ v8::Handle NestedListModel::get(int index) const if (!node) return v8::Undefined();; - return QDeclarativeEnginePrivate::get(eng)->v8engine.newQObject(node->object(this)); + return QDeclarativeEnginePrivate::get(eng)->v8engine()->newQObject(node->object(this)); } void NestedListModel::set(int index, v8::Handle valuemap, QList *roles) diff --git a/src/imports/folderlistmodel/folderlistmodel.pro b/src/imports/folderlistmodel/folderlistmodel.pro index e55a5b2a29..31192fd764 100644 --- a/src/imports/folderlistmodel/folderlistmodel.pro +++ b/src/imports/folderlistmodel/folderlistmodel.pro @@ -2,7 +2,7 @@ TARGET = qmlfolderlistmodelplugin TARGETPATH = Qt/labs/folderlistmodel include(../qimportbase.pri) -QT += declarative script +QT += declarative SOURCES += qdeclarativefolderlistmodel.cpp plugin.cpp HEADERS += qdeclarativefolderlistmodel.h diff --git a/src/imports/gestures/gestures.pro b/src/imports/gestures/gestures.pro index 5c009dde51..d94b402f4c 100644 --- a/src/imports/gestures/gestures.pro +++ b/src/imports/gestures/gestures.pro @@ -2,7 +2,7 @@ TARGET = qmlgesturesplugin TARGETPATH = Qt/labs/gestures include(../qimportbase.pri) -QT += core-private gui-private declarative-private script-private qtquick1 qtquick1-private +QT += core-private gui-private declarative-private qtquick1 qtquick1-private SOURCES += qdeclarativegesturearea.cpp plugin.cpp HEADERS += qdeclarativegesturearea_p.h diff --git a/src/imports/inputcontext/inputcontext.pro b/src/imports/inputcontext/inputcontext.pro index 9c7ddf4e8b..cbad82c5b1 100755 --- a/src/imports/inputcontext/inputcontext.pro +++ b/src/imports/inputcontext/inputcontext.pro @@ -2,7 +2,7 @@ TARGET = qmlinputcontextplugin TARGETPATH = Qt/labs/inputcontext include(../qimportbase.pri) -QT += declarative script +QT += declarative SOURCES += \ declarativeinputcontext.cpp \ diff --git a/src/imports/inputcontext/plugin.cpp b/src/imports/inputcontext/plugin.cpp index 36de469f1f..5ce9e5b475 100644 --- a/src/imports/inputcontext/plugin.cpp +++ b/src/imports/inputcontext/plugin.cpp @@ -48,7 +48,7 @@ QT_BEGIN_NAMESPACE -static QObject *createContext(QDeclarativeEngine *, QScriptEngine *) +static QObject *createContext(QDeclarativeEngine *, QJSEngine *) { return new InputContextModule; } diff --git a/src/imports/testlib/main.cpp b/src/imports/testlib/main.cpp index 5781b3ddf1..db0a029a9a 100644 --- a/src/imports/testlib/main.cpp +++ b/src/imports/testlib/main.cpp @@ -41,10 +41,8 @@ #include #include -#include -#include -#include -#include +#include +#include #include "QtQuickTest/private/quicktestresult_p.h" #include "QtQuickTest/private/quicktestevent_p.h" #include "private/qtestoptions_p.h" diff --git a/src/imports/testlib/testlib.pro b/src/imports/testlib/testlib.pro index 9980a7551f..5adde2ae85 100644 --- a/src/imports/testlib/testlib.pro +++ b/src/imports/testlib/testlib.pro @@ -21,7 +21,7 @@ symbian { } -QT += declarative script qmltest qmltest-private declarative-private script-private core-private testlib +QT += declarative qmltest qmltest-private declarative-private core-private testlib SOURCES += main.cpp HEADERS += diff --git a/src/qmltest/qmltest.pro b/src/qmltest/qmltest.pro index 6e79be0b09..418136aa0a 100644 --- a/src/qmltest/qmltest.pro +++ b/src/qmltest/qmltest.pro @@ -7,7 +7,7 @@ CONFIG += module CONFIG += dll warn_on MODULE_PRI += ../../modules/qt_qmltest.pri -QT += testlib-private declarative script testlib qtquick1 +QT += testlib-private declarative testlib qtquick1 DEFINES += QT_BUILD_QUICK_TEST_LIB QT_NO_URL_CAST_FROM_STRING diff --git a/src/qmltest/quicktest.cpp b/src/qmltest/quicktest.cpp index a2c494513b..3f0c5325ed 100644 --- a/src/qmltest/quicktest.cpp +++ b/src/qmltest/quicktest.cpp @@ -52,9 +52,8 @@ #include #define QUICK_TEST_SCENEGRAPH 1 #endif -#include -#include -#include +#include +#include #include #include #include @@ -73,7 +72,7 @@ QT_BEGIN_NAMESPACE class Q_DECLARATIVE_EXPORT QDeclarativeDebugHelper { public: - static QScriptEngine *getScriptEngine(QDeclarativeEngine *engine); + static QJSEngine *getScriptEngine(QDeclarativeEngine *engine); static void setAnimationSlowDownFactor(qreal factor); static void enableDebugging(); }; diff --git a/src/qtquick1/graphicsitems/qdeclarativeitem.cpp b/src/qtquick1/graphicsitems/qdeclarativeitem.cpp index d2bfc578dd..494ed46ad5 100644 --- a/src/qtquick1/graphicsitems/qdeclarativeitem.cpp +++ b/src/qtquick1/graphicsitems/qdeclarativeitem.cpp @@ -59,7 +59,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/qtquick1/qtquick1.pro b/src/qtquick1/qtquick1.pro index e3dd0298cd..7fc670f801 100644 --- a/src/qtquick1/qtquick1.pro +++ b/src/qtquick1/qtquick1.pro @@ -7,7 +7,7 @@ CONFIG += module CONFIG += dll warn_on MODULE_PRI += ../../modules/qt_qtquick1.pri -QT += testlib-private declarative script testlib declarative-private core-private gui-private script-private network +QT += testlib-private declarative testlib declarative-private core-private gui-private network DEFINES += QT_NO_URL_CAST_FROM_STRING load(qt_module_config) diff --git a/src/qtquick1/util/qdeclarativebind.cpp b/src/qtquick1/util/qdeclarativebind.cpp index f6463be05d..50514234a3 100644 --- a/src/qtquick1/util/qdeclarativebind.cpp +++ b/src/qtquick1/util/qdeclarativebind.cpp @@ -49,9 +49,8 @@ #include #include -#include -#include -#include +#include +#include #include diff --git a/src/qtquick1/util/qdeclarativelistmodel.cpp b/src/qtquick1/util/qdeclarativelistmodel.cpp index 5d60ed9658..520d9ac388 100644 --- a/src/qtquick1/util/qdeclarativelistmodel.cpp +++ b/src/qtquick1/util/qdeclarativelistmodel.cpp @@ -52,7 +52,7 @@ #include #include #include -#include +#include Q_DECLARE_METATYPE(QListModelInterface *) diff --git a/src/qtquick1/util/qdeclarativelistmodel_p.h b/src/qtquick1/util/qdeclarativelistmodel_p.h index 21398f7213..1be5e6001a 100644 --- a/src/qtquick1/util/qdeclarativelistmodel_p.h +++ b/src/qtquick1/util/qdeclarativelistmodel_p.h @@ -51,7 +51,7 @@ #include #include #include -#include +#include QT_BEGIN_HEADER diff --git a/src/qtquick1/util/qdeclarativelistmodel_p_p.h b/src/qtquick1/util/qdeclarativelistmodel_p_p.h index e34f6d850d..ee39ba45b1 100644 --- a/src/qtquick1/util/qdeclarativelistmodel_p_p.h +++ b/src/qtquick1/util/qdeclarativelistmodel_p_p.h @@ -58,7 +58,7 @@ #include "QtQuick1/private/qdeclarativeopenmetaobject_p.h" #include -#include +#include QT_BEGIN_HEADER diff --git a/src/qtquick1/util/qdeclarativelistmodelworkeragent_p.h b/src/qtquick1/util/qdeclarativelistmodelworkeragent_p.h index f0979c4b31..a769185607 100644 --- a/src/qtquick1/util/qdeclarativelistmodelworkeragent_p.h +++ b/src/qtquick1/util/qdeclarativelistmodelworkeragent_p.h @@ -55,7 +55,7 @@ #include -#include +#include #include #include #include diff --git a/src/qtquick1/util/qdeclarativeview.cpp b/src/qtquick1/util/qdeclarativeview.cpp index 32f2183cad..d10d34fcd1 100644 --- a/src/qtquick1/util/qdeclarativeview.cpp +++ b/src/qtquick1/util/qdeclarativeview.cpp @@ -51,7 +51,6 @@ #include #include -#include #include #include #include diff --git a/src/v8/0009-Add-CallAsFunction-method-to-the-Object-class-in-the.patch b/src/v8/0009-Add-CallAsFunction-method-to-the-Object-class-in-the.patch new file mode 100644 index 0000000000..6cd9294d31 --- /dev/null +++ b/src/v8/0009-Add-CallAsFunction-method-to-the-Object-class-in-the.patch @@ -0,0 +1,286 @@ +From 5719ba59309e85f3ca47da6b64df66e710f3016f Mon Sep 17 00:00:00 2001 +From: "ager@chromium.org" +Date: Wed, 4 May 2011 13:03:08 +0000 +Subject: [PATCH] Add CallAsFunction method to the Object class in the API + +Patch by Peter Varga. + +BUG=v8:1336 +TEST=cctest/test-api/CallAsFunction + +Review URL: http://codereview.chromium.org/6883045 + +git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@7781 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 +--- + include/v8.h | 8 +++ + src/api.cc | 31 +++++++++++ + src/execution.cc | 24 ++++++++ + src/execution.h | 2 + + test/cctest/test-api.cc | 135 ++++++++++++++++++++++++++++++++++------------- + 5 files changed, 163 insertions(+), 37 deletions(-) + +diff --git a/include/v8.h b/include/v8.h +index 4dcbf28..78ee7e6 100644 +--- a/include/v8.h ++++ b/include/v8.h +@@ -1606,6 +1606,14 @@ class Object : public Value { + V8EXPORT ExternalArrayType GetIndexedPropertiesExternalArrayDataType(); + V8EXPORT int GetIndexedPropertiesExternalArrayDataLength(); + ++ /** ++ * Call an Object as a function if a callback is set by the ++ * ObjectTemplate::SetCallAsFunctionHandler method. ++ */ ++ V8EXPORT Local CallAsFunction(Handle recv, ++ int argc, ++ Handle argv[]); ++ + V8EXPORT static Local New(); + static inline Object* Cast(Value* obj); + private: +diff --git a/src/api.cc b/src/api.cc +index 792e488..c72857d 100644 +--- a/src/api.cc ++++ b/src/api.cc +@@ -3255,6 +3255,37 @@ int v8::Object::GetIndexedPropertiesExternalArrayDataLength() { + } + + ++Local Object::CallAsFunction(v8::Handle recv, int argc, ++ v8::Handle argv[]) { ++ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate(); ++ ON_BAILOUT(isolate, "v8::Object::CallAsFunction()", ++ return Local()); ++ LOG_API(isolate, "Object::CallAsFunction"); ++ ENTER_V8(isolate); ++ HandleScope scope; ++ i::Handle obj = Utils::OpenHandle(this); ++ i::Handle recv_obj = Utils::OpenHandle(*recv); ++ STATIC_ASSERT(sizeof(v8::Handle) == sizeof(i::Object**)); ++ i::Object*** args = reinterpret_cast(argv); ++ i::Handle fun = i::Handle(); ++ if (obj->IsJSFunction()) { ++ fun = i::Handle::cast(obj); ++ } else { ++ EXCEPTION_PREAMBLE(isolate); ++ i::Handle delegate = ++ i::Execution::TryGetFunctionDelegate(obj, &has_pending_exception); ++ EXCEPTION_BAILOUT_CHECK(isolate, Local()); ++ fun = i::Handle::cast(delegate); ++ recv_obj = obj; ++ } ++ EXCEPTION_PREAMBLE(isolate); ++ i::Handle returned = ++ i::Execution::Call(fun, recv_obj, argc, args, &has_pending_exception); ++ EXCEPTION_BAILOUT_CHECK(isolate, Local()); ++ return scope.Close(Utils::ToLocal(returned)); ++} ++ ++ + Local Function::NewInstance() const { + return NewInstance(0, NULL); + } +diff --git a/src/execution.cc b/src/execution.cc +index eb26438..850dec5 100644 +--- a/src/execution.cc ++++ b/src/execution.cc +@@ -234,6 +234,30 @@ Handle Execution::GetFunctionDelegate(Handle object) { + } + + ++Handle Execution::TryGetFunctionDelegate(Handle object, ++ bool* has_pending_exception) { ++ ASSERT(!object->IsJSFunction()); ++ Isolate* isolate = Isolate::Current(); ++ ++ // Objects created through the API can have an instance-call handler ++ // that should be used when calling the object as a function. ++ if (object->IsHeapObject() && ++ HeapObject::cast(*object)->map()->has_instance_call_handler()) { ++ return Handle( ++ isolate->global_context()->call_as_function_delegate()); ++ } ++ ++ // If the Object doesn't have an instance-call handler we should ++ // throw a non-callable exception. ++ i::Handle error_obj = isolate->factory()->NewTypeError( ++ "called_non_callable", i::HandleVector(&object, 1)); ++ isolate->Throw(*error_obj); ++ *has_pending_exception = true; ++ ++ return isolate->factory()->undefined_value(); ++} ++ ++ + Handle Execution::GetConstructorDelegate(Handle object) { + ASSERT(!object->IsJSFunction()); + Isolate* isolate = Isolate::Current(); +diff --git a/src/execution.h b/src/execution.h +index d4b80d2..e89d6ba 100644 +--- a/src/execution.h ++++ b/src/execution.h +@@ -138,6 +138,8 @@ class Execution : public AllStatic { + // Get a function delegate (or undefined) for the given non-function + // object. Used for support calling objects as functions. + static Handle GetFunctionDelegate(Handle object); ++ static Handle TryGetFunctionDelegate(Handle object, ++ bool* has_pending_exception); + + // Get a function delegate (or undefined) for the given non-function + // object. Used for support calling objects as constructors. +diff --git a/test/cctest/test-api.cc b/test/cctest/test-api.cc +index e2a7fb1..c6affe5 100644 +--- a/test/cctest/test-api.cc ++++ b/test/cctest/test-api.cc +@@ -6962,50 +6962,111 @@ THREADED_TEST(CallAsFunction) { + v8::HandleScope scope; + LocalContext context; + +- Local t = v8::FunctionTemplate::New(); +- Local instance_template = t->InstanceTemplate(); +- instance_template->SetCallAsFunctionHandler(call_as_function); +- Local instance = t->GetFunction()->NewInstance(); +- context->Global()->Set(v8_str("obj"), instance); +- v8::TryCatch try_catch; +- Local value; +- CHECK(!try_catch.HasCaught()); ++ { Local t = v8::FunctionTemplate::New(); ++ Local instance_template = t->InstanceTemplate(); ++ instance_template->SetCallAsFunctionHandler(call_as_function); ++ Local instance = t->GetFunction()->NewInstance(); ++ context->Global()->Set(v8_str("obj"), instance); ++ v8::TryCatch try_catch; ++ Local value; ++ CHECK(!try_catch.HasCaught()); + +- value = CompileRun("obj(42)"); +- CHECK(!try_catch.HasCaught()); +- CHECK_EQ(42, value->Int32Value()); ++ value = CompileRun("obj(42)"); ++ CHECK(!try_catch.HasCaught()); ++ CHECK_EQ(42, value->Int32Value()); + +- value = CompileRun("(function(o){return o(49)})(obj)"); +- CHECK(!try_catch.HasCaught()); +- CHECK_EQ(49, value->Int32Value()); ++ value = CompileRun("(function(o){return o(49)})(obj)"); ++ CHECK(!try_catch.HasCaught()); ++ CHECK_EQ(49, value->Int32Value()); + +- // test special case of call as function +- value = CompileRun("[obj]['0'](45)"); +- CHECK(!try_catch.HasCaught()); +- CHECK_EQ(45, value->Int32Value()); ++ // test special case of call as function ++ value = CompileRun("[obj]['0'](45)"); ++ CHECK(!try_catch.HasCaught()); ++ CHECK_EQ(45, value->Int32Value()); + +- value = CompileRun("obj.call = Function.prototype.call;" +- "obj.call(null, 87)"); +- CHECK(!try_catch.HasCaught()); +- CHECK_EQ(87, value->Int32Value()); ++ value = CompileRun("obj.call = Function.prototype.call;" ++ "obj.call(null, 87)"); ++ CHECK(!try_catch.HasCaught()); ++ CHECK_EQ(87, value->Int32Value()); + +- // Regression tests for bug #1116356: Calling call through call/apply +- // must work for non-function receivers. +- const char* apply_99 = "Function.prototype.call.apply(obj, [this, 99])"; +- value = CompileRun(apply_99); +- CHECK(!try_catch.HasCaught()); +- CHECK_EQ(99, value->Int32Value()); ++ // Regression tests for bug #1116356: Calling call through call/apply ++ // must work for non-function receivers. ++ const char* apply_99 = "Function.prototype.call.apply(obj, [this, 99])"; ++ value = CompileRun(apply_99); ++ CHECK(!try_catch.HasCaught()); ++ CHECK_EQ(99, value->Int32Value()); + +- const char* call_17 = "Function.prototype.call.call(obj, this, 17)"; +- value = CompileRun(call_17); +- CHECK(!try_catch.HasCaught()); +- CHECK_EQ(17, value->Int32Value()); ++ const char* call_17 = "Function.prototype.call.call(obj, this, 17)"; ++ value = CompileRun(call_17); ++ CHECK(!try_catch.HasCaught()); ++ CHECK_EQ(17, value->Int32Value()); + +- // Check that the call-as-function handler can be called through +- // new. +- value = CompileRun("new obj(43)"); +- CHECK(!try_catch.HasCaught()); +- CHECK_EQ(-43, value->Int32Value()); ++ // Check that the call-as-function handler can be called through ++ // new. ++ value = CompileRun("new obj(43)"); ++ CHECK(!try_catch.HasCaught()); ++ CHECK_EQ(-43, value->Int32Value()); ++ ++ // Check that the call-as-function handler can be called through ++ // the API. ++ v8::Handle args[] = { v8_num(28) }; ++ value = instance->CallAsFunction(instance, 1, args); ++ CHECK(!try_catch.HasCaught()); ++ CHECK_EQ(28, value->Int32Value()); ++ } ++ ++ { Local t = v8::FunctionTemplate::New(); ++ Local instance_template = t->InstanceTemplate(); ++ Local instance = t->GetFunction()->NewInstance(); ++ context->Global()->Set(v8_str("obj2"), instance); ++ v8::TryCatch try_catch; ++ Local value; ++ CHECK(!try_catch.HasCaught()); ++ ++ // Call an object without call-as-function handler through the JS ++ value = CompileRun("obj2(28)"); ++ CHECK(value.IsEmpty()); ++ CHECK(try_catch.HasCaught()); ++ String::AsciiValue exception_value1(try_catch.Exception()); ++ CHECK_EQ(*exception_value1, ++ "TypeError: Property 'obj2' of object " ++ "# is not a function"); ++ try_catch.Reset(); ++ ++ // Call an object without call-as-function handler through the API ++ value = CompileRun("obj2(28)"); ++ v8::Handle args[] = { v8_num(28) }; ++ value = instance->CallAsFunction(instance, 1, args); ++ CHECK(value.IsEmpty()); ++ CHECK(try_catch.HasCaught()); ++ String::AsciiValue exception_value2(try_catch.Exception()); ++ CHECK_EQ(*exception_value2, "TypeError: [object Object] is not a function"); ++ try_catch.Reset(); ++ } ++ ++ { Local t = v8::FunctionTemplate::New(); ++ Local instance_template = t->InstanceTemplate(); ++ instance_template->SetCallAsFunctionHandler(ThrowValue); ++ Local instance = t->GetFunction()->NewInstance(); ++ context->Global()->Set(v8_str("obj3"), instance); ++ v8::TryCatch try_catch; ++ Local value; ++ CHECK(!try_catch.HasCaught()); ++ ++ // Catch the exception which is thrown by call-as-function handler ++ value = CompileRun("obj3(22)"); ++ CHECK(try_catch.HasCaught()); ++ String::AsciiValue exception_value1(try_catch.Exception()); ++ CHECK_EQ(*exception_value1, "22"); ++ try_catch.Reset(); ++ ++ v8::Handle args[] = { v8_num(23) }; ++ value = instance->CallAsFunction(instance, 1, args); ++ CHECK(try_catch.HasCaught()); ++ String::AsciiValue exception_value2(try_catch.Exception()); ++ CHECK_EQ(*exception_value2, "23"); ++ try_catch.Reset(); ++ } + } + + +-- +1.7.5.4 + diff --git a/src/v8/0010-Implement-CallAsConstructor-method-for-Object-in-the.patch b/src/v8/0010-Implement-CallAsConstructor-method-for-Object-in-the.patch new file mode 100644 index 0000000000..7d90f0dfbd --- /dev/null +++ b/src/v8/0010-Implement-CallAsConstructor-method-for-Object-in-the.patch @@ -0,0 +1,397 @@ +From fd2cc52576e8c89f3dffc2b4b5a9cc9c48a96f32 Mon Sep 17 00:00:00 2001 +From: "ager@chromium.org" +Date: Fri, 6 May 2011 11:07:52 +0000 +Subject: [PATCH] Implement CallAsConstructor method for Object in the API + +Patch by Peter Varga. + +BUG=v8:1348 +TEST=cctest/test-api/ConstructorForObject + +Review URL: http://codereview.chromium.org/6902108 + +git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@7803 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 +--- + include/v8.h | 8 ++ + src/api.cc | 41 +++++++++- + src/execution.cc | 28 +++++++ + src/execution.h | 2 + + test/cctest/test-api.cc | 205 +++++++++++++++++++++++++++++++++++++++++++++-- + 5 files changed, 276 insertions(+), 8 deletions(-) + +diff --git a/include/v8.h b/include/v8.h +index 4921823..5fc8059 100644 +--- a/include/v8.h ++++ b/include/v8.h +@@ -1614,6 +1614,14 @@ class Object : public Value { + int argc, + Handle argv[]); + ++ /** ++ * Call an Object as a consturctor if a callback is set by the ++ * ObjectTemplate::SetCallAsFunctionHandler method. ++ * Note: This method behaves like the Function::NewInstance method. ++ */ ++ V8EXPORT Local CallAsConstructor(int argc, ++ Handle argv[]); ++ + V8EXPORT static Local New(); + static inline Object* Cast(Value* obj); + private: +diff --git a/src/api.cc b/src/api.cc +index c5c66a7..9194641 100644 +--- a/src/api.cc ++++ b/src/api.cc +@@ -3262,7 +3262,7 @@ Local Object::CallAsFunction(v8::Handle recv, int argc, + return Local()); + LOG_API(isolate, "Object::CallAsFunction"); + ENTER_V8(isolate); +- HandleScope scope; ++ i::HandleScope scope(isolate); + i::Handle obj = Utils::OpenHandle(this); + i::Handle recv_obj = Utils::OpenHandle(*recv); + STATIC_ASSERT(sizeof(v8::Handle) == sizeof(i::Object**)); +@@ -3282,7 +3282,44 @@ Local Object::CallAsFunction(v8::Handle recv, int argc, + i::Handle returned = + i::Execution::Call(fun, recv_obj, argc, args, &has_pending_exception); + EXCEPTION_BAILOUT_CHECK(isolate, Local()); +- return scope.Close(Utils::ToLocal(returned)); ++ return Utils::ToLocal(scope.CloseAndEscape(returned)); ++} ++ ++ ++Local Object::CallAsConstructor(int argc, ++ v8::Handle argv[]) { ++ i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate(); ++ ON_BAILOUT(isolate, "v8::Object::CallAsConstructor()", ++ return Local()); ++ LOG_API(isolate, "Object::CallAsConstructor"); ++ ENTER_V8(isolate); ++ i::HandleScope scope(isolate); ++ i::Handle obj = Utils::OpenHandle(this); ++ STATIC_ASSERT(sizeof(v8::Handle) == sizeof(i::Object**)); ++ i::Object*** args = reinterpret_cast(argv); ++ if (obj->IsJSFunction()) { ++ i::Handle fun = i::Handle::cast(obj); ++ EXCEPTION_PREAMBLE(isolate); ++ i::Handle returned = ++ i::Execution::New(fun, argc, args, &has_pending_exception); ++ EXCEPTION_BAILOUT_CHECK(isolate, Local()); ++ return Utils::ToLocal(scope.CloseAndEscape( ++ i::Handle::cast(returned))); ++ } ++ EXCEPTION_PREAMBLE(isolate); ++ i::Handle delegate = ++ i::Execution::TryGetConstructorDelegate(obj, &has_pending_exception); ++ EXCEPTION_BAILOUT_CHECK(isolate, Local()); ++ if (!delegate->IsUndefined()) { ++ i::Handle fun = i::Handle::cast(delegate); ++ EXCEPTION_PREAMBLE(isolate); ++ i::Handle returned = ++ i::Execution::Call(fun, obj, argc, args, &has_pending_exception); ++ EXCEPTION_BAILOUT_CHECK(isolate, Local()); ++ ASSERT(!delegate->IsUndefined()); ++ return Utils::ToLocal(scope.CloseAndEscape(returned)); ++ } ++ return Local(); + } + + +diff --git a/src/execution.cc b/src/execution.cc +index 4ab3e78..db74492 100644 +--- a/src/execution.cc ++++ b/src/execution.cc +@@ -277,6 +277,34 @@ Handle Execution::GetConstructorDelegate(Handle object) { + } + + ++Handle Execution::TryGetConstructorDelegate( ++ Handle object, ++ bool* has_pending_exception) { ++ ASSERT(!object->IsJSFunction()); ++ Isolate* isolate = Isolate::Current(); ++ ++ // If you return a function from here, it will be called when an ++ // attempt is made to call the given object as a constructor. ++ ++ // Objects created through the API can have an instance-call handler ++ // that should be used when calling the object as a function. ++ if (object->IsHeapObject() && ++ HeapObject::cast(*object)->map()->has_instance_call_handler()) { ++ return Handle( ++ isolate->global_context()->call_as_constructor_delegate()); ++ } ++ ++ // If the Object doesn't have an instance-call handler we should ++ // throw a non-callable exception. ++ i::Handle error_obj = isolate->factory()->NewTypeError( ++ "called_non_callable", i::HandleVector(&object, 1)); ++ isolate->Throw(*error_obj); ++ *has_pending_exception = true; ++ ++ return isolate->factory()->undefined_value(); ++} ++ ++ + bool StackGuard::IsStackOverflow() { + ExecutionAccess access(isolate_); + return (thread_local_.jslimit_ != kInterruptLimit && +diff --git a/src/execution.h b/src/execution.h +index 74189a2..7b6a48c 100644 +--- a/src/execution.h ++++ b/src/execution.h +@@ -146,6 +146,8 @@ class Execution : public AllStatic { + // Get a function delegate (or undefined) for the given non-function + // object. Used for support calling objects as constructors. + static Handle GetConstructorDelegate(Handle object); ++ static Handle TryGetConstructorDelegate(Handle object, ++ bool* has_pending_exception); + }; + + +diff --git a/test/cctest/test-api.cc b/test/cctest/test-api.cc +index 1bcc232..f48d5b4 100644 +--- a/test/cctest/test-api.cc ++++ b/test/cctest/test-api.cc +@@ -6747,6 +6747,200 @@ THREADED_TEST(Constructor) { + CHECK(value->BooleanValue()); + } + ++ ++static Handle ConstructorCallback(const Arguments& args) { ++ ApiTestFuzzer::Fuzz(); ++ Local This; ++ ++ if (args.IsConstructCall()) { ++ Local Holder = args.Holder(); ++ This = Object::New(); ++ Local proto = Holder->GetPrototype(); ++ if (proto->IsObject()) { ++ This->SetPrototype(proto); ++ } ++ } else { ++ This = args.This(); ++ } ++ ++ This->Set(v8_str("a"), args[0]); ++ return This; ++} ++ ++ ++static Handle FakeConstructorCallback(const Arguments& args) { ++ ApiTestFuzzer::Fuzz(); ++ return args[0]; ++} ++ ++ ++THREADED_TEST(ConstructorForObject) { ++ v8::HandleScope handle_scope; ++ LocalContext context; ++ ++ { Local instance_template = ObjectTemplate::New(); ++ instance_template->SetCallAsFunctionHandler(ConstructorCallback); ++ Local instance = instance_template->NewInstance(); ++ context->Global()->Set(v8_str("obj"), instance); ++ v8::TryCatch try_catch; ++ Local value; ++ CHECK(!try_catch.HasCaught()); ++ ++ // Call the Object's constructor with a 32-bit signed integer. ++ value = CompileRun("(function() { var o = new obj(28); return o.a; })()"); ++ CHECK(!try_catch.HasCaught()); ++ CHECK(value->IsInt32()); ++ CHECK_EQ(28, value->Int32Value()); ++ ++ Local args1[] = { v8_num(28) }; ++ Local value_obj1 = instance->CallAsConstructor(1, args1); ++ CHECK(value_obj1->IsObject()); ++ Local object1 = Local::Cast(value_obj1); ++ value = object1->Get(v8_str("a")); ++ CHECK(value->IsInt32()); ++ CHECK(!try_catch.HasCaught()); ++ CHECK_EQ(28, value->Int32Value()); ++ ++ // Call the Object's constructor with a String. ++ value = CompileRun( ++ "(function() { var o = new obj('tipli'); return o.a; })()"); ++ CHECK(!try_catch.HasCaught()); ++ CHECK(value->IsString()); ++ String::AsciiValue string_value1(value->ToString()); ++ CHECK_EQ("tipli", *string_value1); ++ ++ Local args2[] = { v8_str("tipli") }; ++ Local value_obj2 = instance->CallAsConstructor(1, args2); ++ CHECK(value_obj2->IsObject()); ++ Local object2 = Local::Cast(value_obj2); ++ value = object2->Get(v8_str("a")); ++ CHECK(!try_catch.HasCaught()); ++ CHECK(value->IsString()); ++ String::AsciiValue string_value2(value->ToString()); ++ CHECK_EQ("tipli", *string_value2); ++ ++ // Call the Object's constructor with a Boolean. ++ value = CompileRun("(function() { var o = new obj(true); return o.a; })()"); ++ CHECK(!try_catch.HasCaught()); ++ CHECK(value->IsBoolean()); ++ CHECK_EQ(true, value->BooleanValue()); ++ ++ Handle args3[] = { v8::Boolean::New(true) }; ++ Local value_obj3 = instance->CallAsConstructor(1, args3); ++ CHECK(value_obj3->IsObject()); ++ Local object3 = Local::Cast(value_obj3); ++ value = object3->Get(v8_str("a")); ++ CHECK(!try_catch.HasCaught()); ++ CHECK(value->IsBoolean()); ++ CHECK_EQ(true, value->BooleanValue()); ++ ++ // Call the Object's constructor with undefined. ++ Handle args4[] = { v8::Undefined() }; ++ Local value_obj4 = instance->CallAsConstructor(1, args4); ++ CHECK(value_obj4->IsObject()); ++ Local object4 = Local::Cast(value_obj4); ++ value = object4->Get(v8_str("a")); ++ CHECK(!try_catch.HasCaught()); ++ CHECK(value->IsUndefined()); ++ ++ // Call the Object's constructor with null. ++ Handle args5[] = { v8::Null() }; ++ Local value_obj5 = instance->CallAsConstructor(1, args5); ++ CHECK(value_obj5->IsObject()); ++ Local object5 = Local::Cast(value_obj5); ++ value = object5->Get(v8_str("a")); ++ CHECK(!try_catch.HasCaught()); ++ CHECK(value->IsNull()); ++ } ++ ++ // Check exception handling when there is no constructor set for the Object. ++ { Local instance_template = ObjectTemplate::New(); ++ Local instance = instance_template->NewInstance(); ++ context->Global()->Set(v8_str("obj2"), instance); ++ v8::TryCatch try_catch; ++ Local value; ++ CHECK(!try_catch.HasCaught()); ++ ++ value = CompileRun("new obj2(28)"); ++ CHECK(try_catch.HasCaught()); ++ String::AsciiValue exception_value1(try_catch.Exception()); ++ CHECK_EQ("TypeError: object is not a function", *exception_value1); ++ try_catch.Reset(); ++ ++ Local args[] = { v8_num(29) }; ++ value = instance->CallAsConstructor(1, args); ++ CHECK(try_catch.HasCaught()); ++ String::AsciiValue exception_value2(try_catch.Exception()); ++ CHECK_EQ("TypeError: # is not a function", *exception_value2); ++ try_catch.Reset(); ++ } ++ ++ // Check the case when constructor throws exception. ++ { Local instance_template = ObjectTemplate::New(); ++ instance_template->SetCallAsFunctionHandler(ThrowValue); ++ Local instance = instance_template->NewInstance(); ++ context->Global()->Set(v8_str("obj3"), instance); ++ v8::TryCatch try_catch; ++ Local value; ++ CHECK(!try_catch.HasCaught()); ++ ++ value = CompileRun("new obj3(22)"); ++ CHECK(try_catch.HasCaught()); ++ String::AsciiValue exception_value1(try_catch.Exception()); ++ CHECK_EQ("22", *exception_value1); ++ try_catch.Reset(); ++ ++ Local args[] = { v8_num(23) }; ++ value = instance->CallAsConstructor(1, args); ++ CHECK(try_catch.HasCaught()); ++ String::AsciiValue exception_value2(try_catch.Exception()); ++ CHECK_EQ("23", *exception_value2); ++ try_catch.Reset(); ++ } ++ ++ // Check whether constructor returns with an object or non-object. ++ { Local function_template = ++ FunctionTemplate::New(FakeConstructorCallback); ++ Local function = function_template->GetFunction(); ++ Local instance1 = function; ++ context->Global()->Set(v8_str("obj4"), instance1); ++ v8::TryCatch try_catch; ++ Local value; ++ CHECK(!try_catch.HasCaught()); ++ ++ CHECK(instance1->IsObject()); ++ CHECK(instance1->IsFunction()); ++ ++ value = CompileRun("new obj4(28)"); ++ CHECK(!try_catch.HasCaught()); ++ CHECK(value->IsObject()); ++ ++ Local args1[] = { v8_num(28) }; ++ value = instance1->CallAsConstructor(1, args1); ++ CHECK(!try_catch.HasCaught()); ++ CHECK(value->IsObject()); ++ ++ Local instance_template = ObjectTemplate::New(); ++ instance_template->SetCallAsFunctionHandler(FakeConstructorCallback); ++ Local instance2 = instance_template->NewInstance(); ++ context->Global()->Set(v8_str("obj5"), instance2); ++ CHECK(!try_catch.HasCaught()); ++ ++ CHECK(instance2->IsObject()); ++ CHECK(!instance2->IsFunction()); ++ ++ value = CompileRun("new obj5(28)"); ++ CHECK(!try_catch.HasCaught()); ++ CHECK(!value->IsObject()); ++ ++ Local args2[] = { v8_num(28) }; ++ value = instance2->CallAsConstructor(1, args2); ++ CHECK(!try_catch.HasCaught()); ++ CHECK(!value->IsObject()); ++ } ++} ++ ++ + THREADED_TEST(FunctionDescriptorException) { + v8::HandleScope handle_scope; + LocalContext context; +@@ -7029,9 +7223,8 @@ THREADED_TEST(CallAsFunction) { + CHECK(value.IsEmpty()); + CHECK(try_catch.HasCaught()); + String::AsciiValue exception_value1(try_catch.Exception()); +- CHECK_EQ(*exception_value1, +- "TypeError: Property 'obj2' of object " +- "# is not a function"); ++ CHECK_EQ("TypeError: Property 'obj2' of object # is not a function", ++ *exception_value1); + try_catch.Reset(); + + // Call an object without call-as-function handler through the API +@@ -7041,7 +7234,7 @@ THREADED_TEST(CallAsFunction) { + CHECK(value.IsEmpty()); + CHECK(try_catch.HasCaught()); + String::AsciiValue exception_value2(try_catch.Exception()); +- CHECK_EQ(*exception_value2, "TypeError: [object Object] is not a function"); ++ CHECK_EQ("TypeError: [object Object] is not a function", *exception_value2); + try_catch.Reset(); + } + +@@ -7058,14 +7251,14 @@ THREADED_TEST(CallAsFunction) { + value = CompileRun("obj3(22)"); + CHECK(try_catch.HasCaught()); + String::AsciiValue exception_value1(try_catch.Exception()); +- CHECK_EQ(*exception_value1, "22"); ++ CHECK_EQ("22", *exception_value1); + try_catch.Reset(); + + v8::Handle args[] = { v8_num(23) }; + value = instance->CallAsFunction(instance, 1, args); + CHECK(try_catch.HasCaught()); + String::AsciiValue exception_value2(try_catch.Exception()); +- CHECK_EQ(*exception_value2, "23"); ++ CHECK_EQ("23", *exception_value2); + try_catch.Reset(); + } + } +-- +1.7.5.4 + diff --git a/src/v8/0011-QtScript-V8-Add-new-v8-api-to-check-if-a-value-is-an.patch b/src/v8/0011-QtScript-V8-Add-new-v8-api-to-check-if-a-value-is-an.patch new file mode 100644 index 0000000000..0558ce19f6 --- /dev/null +++ b/src/v8/0011-QtScript-V8-Add-new-v8-api-to-check-if-a-value-is-an.patch @@ -0,0 +1,63 @@ +From 859c452847317efe1131e337fcd51514de616ea2 Mon Sep 17 00:00:00 2001 +From: Jedrzej Nowacki +Date: Tue, 7 Dec 2010 11:56:42 +0100 +Subject: [PATCH] QtScript/V8: Add new v8 api to check if a value is an error. + +New function v8::Value::IsError was created. + +This API is experimental and added only for the purposes of our +research. +--- + include/v8.h | 5 +++++ + src/api.cc | 6 ++++++ + src/heap.h | 1 + + 3 files changed, 12 insertions(+), 0 deletions(-) + +diff --git a/include/v8.h b/include/v8.h +index 303cb7a..f992cb2 100644 +--- a/include/v8.h ++++ b/include/v8.h +@@ -937,6 +937,11 @@ class Value : public Data { + */ + V8EXPORT bool IsRegExp() const; + ++ /** ++ * Returns true if this value is an Error. ++ */ ++ V8EXPORT bool IsError() const; ++ + V8EXPORT Local ToBoolean() const; + V8EXPORT Local ToNumber() const; + V8EXPORT Local ToString() const; +diff --git a/src/api.cc b/src/api.cc +index fd4a76b..5ada246 100644 +--- a/src/api.cc ++++ b/src/api.cc +@@ -2108,6 +2108,12 @@ bool Value::IsRegExp() const { + return obj->IsJSRegExp(); + } + ++bool Value::IsError() const { ++ if (IsDeadCheck(i::Isolate::Current(), "v8::Value::IsError()")) return false; ++ i::Handle obj = Utils::OpenHandle(this); ++ return obj->HasSpecificClassOf(HEAP->Error_symbol()); ++} ++ + + Local Value::ToString() const { + i::Handle obj = Utils::OpenHandle(this); +diff --git a/src/heap.h b/src/heap.h +index 8cbf378..db90bb9 100644 +--- a/src/heap.h ++++ b/src/heap.h +@@ -169,6 +169,7 @@ inline Heap* _inline_get_heap_(); + V(string_symbol, "string") \ + V(String_symbol, "String") \ + V(Date_symbol, "Date") \ ++ V(Error_symbol, "Error") \ + V(this_symbol, "this") \ + V(to_string_symbol, "toString") \ + V(char_at_symbol, "CharAt") \ +-- +1.7.4.15.g7811d + diff --git a/tests/auto/declarative/declarative.pro b/tests/auto/declarative/declarative.pro index 43a2a682de..a99656a3ee 100644 --- a/tests/auto/declarative/declarative.pro +++ b/tests/auto/declarative/declarative.pro @@ -20,7 +20,10 @@ PUBLICTESTS += \ qdeclarativepixmapcache \ qdeclarativeqt \ qdeclarativetranslation \ - qdeclarativexmlhttprequest + qdeclarativexmlhttprequest \ + qjsvalue \ + qjsvalueiterator \ + qjsengine PRIVATETESTS += \ qdeclarativeanimations \ diff --git a/tests/auto/declarative/qdeclarativecomponent/qdeclarativecomponent.pro b/tests/auto/declarative/qdeclarativecomponent/qdeclarativecomponent.pro index 7109f2da65..50d63fea42 100644 --- a/tests/auto/declarative/qdeclarativecomponent/qdeclarativecomponent.pro +++ b/tests/auto/declarative/qdeclarativecomponent/qdeclarativecomponent.pro @@ -1,6 +1,6 @@ load(qttest_p4) contains(QT_CONFIG,declarative): QT += declarative -QT += script network +QT += network macx:CONFIG -= app_bundle SOURCES += tst_qdeclarativecomponent.cpp diff --git a/tests/auto/declarative/qdeclarativeconnection/qdeclarativeconnection.pro b/tests/auto/declarative/qdeclarativeconnection/qdeclarativeconnection.pro index 564b088817..49150c8481 100644 --- a/tests/auto/declarative/qdeclarativeconnection/qdeclarativeconnection.pro +++ b/tests/auto/declarative/qdeclarativeconnection/qdeclarativeconnection.pro @@ -14,4 +14,4 @@ symbian: { CONFIG += parallel_test -QT += core-private gui-private declarative-private script-private opengl-private +QT += core-private gui-private declarative-private opengl-private diff --git a/tests/auto/declarative/qdeclarativedebug/qdeclarativedebug.pro b/tests/auto/declarative/qdeclarativedebug/qdeclarativedebug.pro index 430224e1e8..cd5577219d 100644 --- a/tests/auto/declarative/qdeclarativedebug/qdeclarativedebug.pro +++ b/tests/auto/declarative/qdeclarativedebug/qdeclarativedebug.pro @@ -8,4 +8,4 @@ SOURCES += tst_qdeclarativedebug.cpp \ CONFIG += parallel_test -QT += core-private gui-private declarative-private script-private +QT += core-private gui-private declarative-private diff --git a/tests/auto/declarative/qdeclarativedebugclient/qdeclarativedebugclient.pro b/tests/auto/declarative/qdeclarativedebugclient/qdeclarativedebugclient.pro index 880255b6b2..189133a526 100644 --- a/tests/auto/declarative/qdeclarativedebugclient/qdeclarativedebugclient.pro +++ b/tests/auto/declarative/qdeclarativedebugclient/qdeclarativedebugclient.pro @@ -5,4 +5,4 @@ macx:CONFIG -= app_bundle HEADERS += ../shared/debugutil_p.h SOURCES += tst_qdeclarativedebugclient.cpp \ ../shared/debugutil.cpp -QT += core-private gui-private declarative-private script-private +QT += core-private gui-private declarative-private diff --git a/tests/auto/declarative/qdeclarativedebughelper/private_headers/qdeclarativedebughelper_p.h b/tests/auto/declarative/qdeclarativedebughelper/private_headers/qdeclarativedebughelper_p.h index 84956d0bb7..c08f6fd639 100644 --- a/tests/auto/declarative/qdeclarativedebughelper/private_headers/qdeclarativedebughelper_p.h +++ b/tests/auto/declarative/qdeclarativedebughelper/private_headers/qdeclarativedebughelper_p.h @@ -48,7 +48,7 @@ QT_BEGIN_HEADER QT_BEGIN_NAMESPACE -class QScriptEngine; +class QJSEngine; class QDeclarativeEngine; // Helper methods to access private API through a stable interface @@ -56,7 +56,7 @@ class QDeclarativeEngine; class Q_DECLARATIVE_EXPORT QDeclarativeDebugHelper { public: - static QScriptEngine *getScriptEngine(QDeclarativeEngine *engine); + static QJSEngine *getScriptEngine(QDeclarativeEngine *engine); static void setAnimationSlowDownFactor(qreal factor); // Enables remote debugging functionality diff --git a/tests/auto/declarative/qdeclarativedebughelper/qdeclarativedebughelper.pro b/tests/auto/declarative/qdeclarativedebughelper/qdeclarativedebughelper.pro index e9e8e73319..1e62c12363 100644 --- a/tests/auto/declarative/qdeclarativedebughelper/qdeclarativedebughelper.pro +++ b/tests/auto/declarative/qdeclarativedebughelper/qdeclarativedebughelper.pro @@ -1,6 +1,6 @@ load(qttest_p4) -contains(QT_CONFIG,declarative): QT += network declarative script +contains(QT_CONFIG,declarative): QT += network declarative macx:CONFIG -= app_bundle SOURCES += tst_qdeclarativedebughelper.cpp -QT += core-private gui-private declarative-private script-private +QT += core-private gui-private declarative-private diff --git a/tests/auto/declarative/qdeclarativedebugservice/qdeclarativedebugservice.pro b/tests/auto/declarative/qdeclarativedebugservice/qdeclarativedebugservice.pro index 55ddeb28a3..785e8a7bf1 100644 --- a/tests/auto/declarative/qdeclarativedebugservice/qdeclarativedebugservice.pro +++ b/tests/auto/declarative/qdeclarativedebugservice/qdeclarativedebugservice.pro @@ -8,4 +8,4 @@ SOURCES += tst_qdeclarativedebugservice.cpp \ CONFIG += parallel_test -QT += core-private gui-private declarative-private script-private +QT += core-private gui-private declarative-private diff --git a/tests/auto/declarative/qdeclarativeecmascript/qdeclarativeecmascript.pro b/tests/auto/declarative/qdeclarativeecmascript/qdeclarativeecmascript.pro index 03834ed428..4b7aff339c 100644 --- a/tests/auto/declarative/qdeclarativeecmascript/qdeclarativeecmascript.pro +++ b/tests/auto/declarative/qdeclarativeecmascript/qdeclarativeecmascript.pro @@ -1,5 +1,5 @@ load(qttest_p4) -contains(QT_CONFIG,declarative): QT += declarative script network +contains(QT_CONFIG,declarative): QT += declarative network macx:CONFIG -= app_bundle SOURCES += tst_qdeclarativeecmascript.cpp \ @@ -22,4 +22,4 @@ symbian: { CONFIG += parallel_test -QT += core-private gui-private declarative-private script-private +QT += core-private gui-private declarative-private diff --git a/tests/auto/declarative/qdeclarativeecmascript/testtypes.cpp b/tests/auto/declarative/qdeclarativeecmascript/testtypes.cpp index ea8d2c01c7..160a57215b 100644 --- a/tests/auto/declarative/qdeclarativeecmascript/testtypes.cpp +++ b/tests/auto/declarative/qdeclarativeecmascript/testtypes.cpp @@ -42,7 +42,7 @@ #include #include #include -#include +#include class BaseExtensionObject : public QObject { @@ -101,17 +101,17 @@ class QWidgetDeclarativeUI : public QObject void setWidth(int) { } }; -static QScriptValue script_api(QDeclarativeEngine *engine, QScriptEngine *scriptEngine) +static QJSValue script_api(QDeclarativeEngine *engine, QJSEngine *scriptEngine) { Q_UNUSED(engine) static int testProperty = 13; - QScriptValue v = scriptEngine->newObject(); + QJSValue v = scriptEngine->newObject(); v.setProperty("scriptTestProperty", testProperty++); return v; } -static QObject *qobject_api(QDeclarativeEngine *engine, QScriptEngine *scriptEngine) +static QObject *qobject_api(QDeclarativeEngine *engine, QJSEngine *scriptEngine) { Q_UNUSED(engine) Q_UNUSED(scriptEngine) @@ -122,7 +122,7 @@ static QObject *qobject_api(QDeclarativeEngine *engine, QScriptEngine *scriptEng return o; } -static QObject *qobject_api_engine_parent(QDeclarativeEngine *engine, QScriptEngine *scriptEngine) +static QObject *qobject_api_engine_parent(QDeclarativeEngine *engine, QJSEngine *scriptEngine) { Q_UNUSED(scriptEngine) diff --git a/tests/auto/declarative/qdeclarativeecmascript/testtypes.h b/tests/auto/declarative/qdeclarativeecmascript/testtypes.h index aebfb22b7c..afb361e2d7 100644 --- a/tests/auto/declarative/qdeclarativeecmascript/testtypes.h +++ b/tests/auto/declarative/qdeclarativeecmascript/testtypes.h @@ -53,7 +53,7 @@ #include #include #include -#include +#include #include #include @@ -581,7 +581,7 @@ class MyDerivedObject : public MyTypeObject } }; -Q_DECLARE_METATYPE(QScriptValue); +Q_DECLARE_METATYPE(QJSValue); class MyInvokableBaseObject : public QObject { Q_OBJECT @@ -614,7 +614,7 @@ class MyInvokableObject : public MyInvokableBaseObject Q_INVOKABLE QPointF method_NoArgs_QPointF() { invoke(3); return QPointF(123, 4.5); } Q_INVOKABLE QObject *method_NoArgs_QObject() { invoke(4); return this; } Q_INVOKABLE MyInvokableObject *method_NoArgs_unknown() { invoke(5); return this; } - Q_INVOKABLE QScriptValue method_NoArgs_QScriptValue() { invoke(6); return QScriptValue("Hello world"); } + Q_INVOKABLE QJSValue method_NoArgs_QScriptValue() { invoke(6); return QJSValue("Hello world"); } Q_INVOKABLE QVariant method_NoArgs_QVariant() { invoke(7); return QVariant("QML rocks"); } Q_INVOKABLE void method_int(int a) { invoke(8); m_actuals << a; } @@ -623,8 +623,8 @@ class MyInvokableObject : public MyInvokableBaseObject Q_INVOKABLE void method_QString(QString a) { invoke(11); m_actuals << a; } Q_INVOKABLE void method_QPointF(QPointF a) { invoke(12); m_actuals << a; } Q_INVOKABLE void method_QObject(QObject *a) { invoke(13); m_actuals << qVariantFromValue(a); } - Q_INVOKABLE void method_QScriptValue(QScriptValue a) { invoke(14); m_actuals << qVariantFromValue(a); } - Q_INVOKABLE void method_intQScriptValue(int a, QScriptValue b) { invoke(15); m_actuals << a << qVariantFromValue(b); } + Q_INVOKABLE void method_QScriptValue(QJSValue a) { invoke(14); m_actuals << qVariantFromValue(a); } + Q_INVOKABLE void method_intQScriptValue(int a, QJSValue b) { invoke(15); m_actuals << a << qVariantFromValue(b); } Q_INVOKABLE void method_overload(int a) { invoke(16); m_actuals << a; } Q_INVOKABLE void method_overload(int a, int b) { invoke(17); m_actuals << a << b; } diff --git a/tests/auto/declarative/qdeclarativeecmascript/tst_qdeclarativeecmascript.cpp b/tests/auto/declarative/qdeclarativeecmascript/tst_qdeclarativeecmascript.cpp index b1bc5bda2b..6c629a80f7 100644 --- a/tests/auto/declarative/qdeclarativeecmascript/tst_qdeclarativeecmascript.cpp +++ b/tests/auto/declarative/qdeclarativeecmascript/tst_qdeclarativeecmascript.cpp @@ -49,7 +49,6 @@ #include #include #include -#include #include "testtypes.h" #include "testhttpserver.h" #include "../../../shared/util.h" @@ -1600,7 +1599,7 @@ void tst_qdeclarativeecmascript::callQtInvokables() QDeclarativeEngine qmlengine; QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(&qmlengine); - QV8Engine *engine = &ep->v8engine; + QV8Engine *engine = ep->v8engine(); v8::HandleScope handle_scope; v8::Context::Scope scope(engine->context()); @@ -1693,8 +1692,6 @@ void tst_qdeclarativeecmascript::callQtInvokables() QCOMPARE(o.invoked(), 5); QCOMPARE(o.actuals().count(), 0); - // XXX enable once qml/qtscript integration is implemented -#if 0 o.reset(); { v8::Handle ret = EVALUATE("object.method_NoArgs_QScriptValue()"); @@ -1704,7 +1701,6 @@ void tst_qdeclarativeecmascript::callQtInvokables() QCOMPARE(o.invoked(), 6); QCOMPARE(o.actuals().count(), 0); } -#endif o.reset(); QVERIFY(EVALUATE_VALUE("object.method_NoArgs_QVariant()", engine->toString("QML rocks"))); @@ -1920,35 +1916,33 @@ void tst_qdeclarativeecmascript::callQtInvokables() QCOMPARE(o.actuals().count(), 1); QCOMPARE(o.actuals().at(0), qVariantFromValue((QObject *)&o)); - // XXX enable once qml/qtscript integration is implemented -#if 0 o.reset(); QVERIFY(EVALUATE_VALUE("object.method_QScriptValue(null)", v8::Undefined())); QCOMPARE(o.error(), false); QCOMPARE(o.invoked(), 14); QCOMPARE(o.actuals().count(), 1); - QVERIFY(qvariant_cast(o.actuals().at(0)).isNull()); + QVERIFY(qvariant_cast(o.actuals().at(0)).isNull()); o.reset(); QVERIFY(EVALUATE_VALUE("object.method_QScriptValue(undefined)", v8::Undefined())); QCOMPARE(o.error(), false); QCOMPARE(o.invoked(), 14); QCOMPARE(o.actuals().count(), 1); - QVERIFY(qvariant_cast(o.actuals().at(0)).isUndefined()); + QVERIFY(qvariant_cast(o.actuals().at(0)).isUndefined()); o.reset(); QVERIFY(EVALUATE_VALUE("object.method_QScriptValue(19)", v8::Undefined())); QCOMPARE(o.error(), false); QCOMPARE(o.invoked(), 14); QCOMPARE(o.actuals().count(), 1); - QVERIFY(qvariant_cast(o.actuals().at(0)).strictlyEquals(QScriptValue(engine, 19))); + QVERIFY(qvariant_cast(o.actuals().at(0)).strictlyEquals(QJSValue(19))); o.reset(); QVERIFY(EVALUATE_VALUE("object.method_QScriptValue([19, 20])", v8::Undefined())); QCOMPARE(o.error(), false); QCOMPARE(o.invoked(), 14); QCOMPARE(o.actuals().count(), 1); - QVERIFY(qvariant_cast(o.actuals().at(0)).isArray()); + QVERIFY(qvariant_cast(o.actuals().at(0)).isArray()); o.reset(); QVERIFY(EVALUATE_VALUE("object.method_intQScriptValue(4, null)", v8::Undefined())); @@ -1956,7 +1950,7 @@ void tst_qdeclarativeecmascript::callQtInvokables() QCOMPARE(o.invoked(), 15); QCOMPARE(o.actuals().count(), 2); QCOMPARE(o.actuals().at(0), QVariant(4)); - QVERIFY(qvariant_cast(o.actuals().at(1)).isNull()); + QVERIFY(qvariant_cast(o.actuals().at(1)).isNull()); o.reset(); QVERIFY(EVALUATE_VALUE("object.method_intQScriptValue(8, undefined)", v8::Undefined())); @@ -1964,7 +1958,7 @@ void tst_qdeclarativeecmascript::callQtInvokables() QCOMPARE(o.invoked(), 15); QCOMPARE(o.actuals().count(), 2); QCOMPARE(o.actuals().at(0), QVariant(8)); - QVERIFY(qvariant_cast(o.actuals().at(1)).isUndefined()); + QVERIFY(qvariant_cast(o.actuals().at(1)).isUndefined()); o.reset(); QVERIFY(EVALUATE_VALUE("object.method_intQScriptValue(3, 19)", v8::Undefined())); @@ -1972,7 +1966,7 @@ void tst_qdeclarativeecmascript::callQtInvokables() QCOMPARE(o.invoked(), 15); QCOMPARE(o.actuals().count(), 2); QCOMPARE(o.actuals().at(0), QVariant(3)); - QVERIFY(qvariant_cast(o.actuals().at(1)).strictlyEquals(QScriptValue(engine, 19))); + QVERIFY(qvariant_cast(o.actuals().at(1)).strictlyEquals(QJSValue(19))); o.reset(); QVERIFY(EVALUATE_VALUE("object.method_intQScriptValue(44, [19, 20])", v8::Undefined())); @@ -1980,8 +1974,7 @@ void tst_qdeclarativeecmascript::callQtInvokables() QCOMPARE(o.invoked(), 15); QCOMPARE(o.actuals().count(), 2); QCOMPARE(o.actuals().at(0), QVariant(44)); - QVERIFY(qvariant_cast(o.actuals().at(1)).isArray()); -#endif + QVERIFY(qvariant_cast(o.actuals().at(1)).isArray()); o.reset(); QVERIFY(EVALUATE_ERROR("object.method_overload()")); diff --git a/tests/auto/declarative/qdeclarativeinstruction/qdeclarativeinstruction.pro b/tests/auto/declarative/qdeclarativeinstruction/qdeclarativeinstruction.pro index a3ca541485..a4cdd81266 100644 --- a/tests/auto/declarative/qdeclarativeinstruction/qdeclarativeinstruction.pro +++ b/tests/auto/declarative/qdeclarativeinstruction/qdeclarativeinstruction.pro @@ -1,5 +1,5 @@ load(qttest_p4) -contains(QT_CONFIG,declarative): QT += declarative script +contains(QT_CONFIG,declarative): QT += declarative SOURCES += tst_qdeclarativeinstruction.cpp macx:CONFIG -= app_bundle @@ -9,4 +9,4 @@ macx:CONFIG -= app_bundle CONFIG += parallel_test -QT += core-private gui-private declarative-private script-private +QT += core-private gui-private declarative-private diff --git a/tests/auto/declarative/qdeclarativelanguage/qdeclarativelanguage.pro b/tests/auto/declarative/qdeclarativelanguage/qdeclarativelanguage.pro index 88dc6128ed..71d2066608 100644 --- a/tests/auto/declarative/qdeclarativelanguage/qdeclarativelanguage.pro +++ b/tests/auto/declarative/qdeclarativelanguage/qdeclarativelanguage.pro @@ -1,6 +1,6 @@ load(qttest_p4) contains(QT_CONFIG,declarative): QT += declarative -QT += script network +QT += network macx:CONFIG -= app_bundle SOURCES += tst_qdeclarativelanguage.cpp \ @@ -20,4 +20,4 @@ symbian: { } CONFIG += parallel_test -QT += core-private gui-private declarative-private script-private +QT += core-private gui-private declarative-private diff --git a/tests/auto/declarative/qdeclarativelistmodel/qdeclarativelistmodel.pro b/tests/auto/declarative/qdeclarativelistmodel/qdeclarativelistmodel.pro index 85340e70e6..83ea7992ed 100644 --- a/tests/auto/declarative/qdeclarativelistmodel/qdeclarativelistmodel.pro +++ b/tests/auto/declarative/qdeclarativelistmodel/qdeclarativelistmodel.pro @@ -1,6 +1,5 @@ load(qttest_p4) contains(QT_CONFIG,declarative): QT += declarative -QT += script macx:CONFIG -= app_bundle SOURCES += tst_qdeclarativelistmodel.cpp @@ -15,4 +14,4 @@ symbian: { CONFIG += parallel_test -QT += core-private gui-private declarative-private script-private opengl-private +QT += core-private gui-private declarative-private opengl-private diff --git a/tests/auto/declarative/qdeclarativeproperty/qdeclarativeproperty.pro b/tests/auto/declarative/qdeclarativeproperty/qdeclarativeproperty.pro index d96b26aee9..fd55ed5160 100644 --- a/tests/auto/declarative/qdeclarativeproperty/qdeclarativeproperty.pro +++ b/tests/auto/declarative/qdeclarativeproperty/qdeclarativeproperty.pro @@ -14,4 +14,4 @@ symbian: { CONFIG += parallel_test -QT += core-private gui-private declarative-private script-private +QT += core-private gui-private declarative-private diff --git a/tests/auto/declarative/qdeclarativeqt/qdeclarativeqt.pro b/tests/auto/declarative/qdeclarativeqt/qdeclarativeqt.pro index cd9dabf3bd..304e168e00 100644 --- a/tests/auto/declarative/qdeclarativeqt/qdeclarativeqt.pro +++ b/tests/auto/declarative/qdeclarativeqt/qdeclarativeqt.pro @@ -1,5 +1,5 @@ load(qttest_p4) -contains(QT_CONFIG,declarative): QT += declarative script +contains(QT_CONFIG,declarative): QT += declarative SOURCES += tst_qdeclarativeqt.cpp macx:CONFIG -= app_bundle @@ -16,4 +16,4 @@ symbian: { CONFIG += parallel_test -QT += core-private gui-private declarative-private script-private +QT += core-private gui-private declarative-private diff --git a/tests/auto/declarative/qdeclarativescriptdebugging/qdeclarativescriptdebugging.pro b/tests/auto/declarative/qdeclarativescriptdebugging/qdeclarativescriptdebugging.pro index 9187bea10b..cedb92743e 100644 --- a/tests/auto/declarative/qdeclarativescriptdebugging/qdeclarativescriptdebugging.pro +++ b/tests/auto/declarative/qdeclarativescriptdebugging/qdeclarativescriptdebugging.pro @@ -1,5 +1,5 @@ load(qttest_p4) -contains(QT_CONFIG,declarative): QT += declarative script +contains(QT_CONFIG,declarative): QT += declarative macx:CONFIG -= app_bundle SOURCES += tst_qdeclarativescriptdebugging.cpp @@ -18,4 +18,4 @@ symbian: { CONFIG += parallel_test -QT += core-private gui-private declarative-private script-private +QT += core-private gui-private declarative-private diff --git a/tests/auto/declarative/qdeclarativesmoothedanimation/qdeclarativesmoothedanimation.pro b/tests/auto/declarative/qdeclarativesmoothedanimation/qdeclarativesmoothedanimation.pro index 5bc4261a28..c8f8e9139b 100644 --- a/tests/auto/declarative/qdeclarativesmoothedanimation/qdeclarativesmoothedanimation.pro +++ b/tests/auto/declarative/qdeclarativesmoothedanimation/qdeclarativesmoothedanimation.pro @@ -14,4 +14,4 @@ symbian: { CONFIG += parallel_test -QT += core-private gui-private declarative-private script-private +QT += core-private gui-private declarative-private diff --git a/tests/auto/declarative/qdeclarativespringanimation/qdeclarativespringanimation.pro b/tests/auto/declarative/qdeclarativespringanimation/qdeclarativespringanimation.pro index 9885c6521e..f2b5928915 100644 --- a/tests/auto/declarative/qdeclarativespringanimation/qdeclarativespringanimation.pro +++ b/tests/auto/declarative/qdeclarativespringanimation/qdeclarativespringanimation.pro @@ -14,4 +14,4 @@ symbian: { CONFIG += parallel_test -QT += core-private gui-private declarative-private script-private +QT += core-private gui-private declarative-private diff --git a/tests/auto/declarative/qdeclarativesqldatabase/qdeclarativesqldatabase.pro b/tests/auto/declarative/qdeclarativesqldatabase/qdeclarativesqldatabase.pro index c48cdf7b55..a51eaf34de 100644 --- a/tests/auto/declarative/qdeclarativesqldatabase/qdeclarativesqldatabase.pro +++ b/tests/auto/declarative/qdeclarativesqldatabase/qdeclarativesqldatabase.pro @@ -1,6 +1,6 @@ load(qttest_p4) contains(QT_CONFIG,declarative): QT += declarative -QT += sql script +QT += sql macx:CONFIG -= app_bundle SOURCES += tst_qdeclarativesqldatabase.cpp @@ -15,4 +15,4 @@ symbian: { CONFIG += parallel_test -QT += core-private gui-private declarative-private script-private +QT += core-private gui-private declarative-private diff --git a/tests/auto/declarative/qdeclarativestates/qdeclarativestates.pro b/tests/auto/declarative/qdeclarativestates/qdeclarativestates.pro index c34b8d8319..9823c21836 100644 --- a/tests/auto/declarative/qdeclarativestates/qdeclarativestates.pro +++ b/tests/auto/declarative/qdeclarativestates/qdeclarativestates.pro @@ -13,4 +13,4 @@ symbian: { } CONFIG += parallel_test -QT += core-private gui-private declarative-private script-private opengl-private +QT += core-private gui-private declarative-private opengl-private diff --git a/tests/auto/declarative/qdeclarativev4/qdeclarativev4.pro b/tests/auto/declarative/qdeclarativev4/qdeclarativev4.pro index 0a2005d15d..9b66edff41 100644 --- a/tests/auto/declarative/qdeclarativev4/qdeclarativev4.pro +++ b/tests/auto/declarative/qdeclarativev4/qdeclarativev4.pro @@ -1,5 +1,5 @@ load(qttest_p4) -contains(QT_CONFIG,declarative): QT += declarative script network +contains(QT_CONFIG,declarative): QT += declarative network macx:CONFIG -= app_bundle SOURCES += tst_qdeclarativev4.cpp \ @@ -16,4 +16,4 @@ symbian: { CONFIG += parallel_test -QT += core-private gui-private declarative-private script-private +QT += core-private gui-private declarative-private diff --git a/tests/auto/declarative/qdeclarativevaluetypes/qdeclarativevaluetypes.pro b/tests/auto/declarative/qdeclarativevaluetypes/qdeclarativevaluetypes.pro index f5567e96ed..f1c64398e9 100644 --- a/tests/auto/declarative/qdeclarativevaluetypes/qdeclarativevaluetypes.pro +++ b/tests/auto/declarative/qdeclarativevaluetypes/qdeclarativevaluetypes.pro @@ -17,4 +17,4 @@ symbian: { CONFIG += parallel_test -QT += core-private gui-private declarative-private script-private +QT += core-private gui-private declarative-private diff --git a/tests/auto/declarative/qdeclarativeworkerscript/qdeclarativeworkerscript.pro b/tests/auto/declarative/qdeclarativeworkerscript/qdeclarativeworkerscript.pro index 08ae7c164c..d7dfe96c65 100644 --- a/tests/auto/declarative/qdeclarativeworkerscript/qdeclarativeworkerscript.pro +++ b/tests/auto/declarative/qdeclarativeworkerscript/qdeclarativeworkerscript.pro @@ -1,5 +1,5 @@ load(qttest_p4) -contains(QT_CONFIG,declarative): QT += declarative script +contains(QT_CONFIG,declarative): QT += declarative macx:CONFIG -= app_bundle SOURCES += tst_qdeclarativeworkerscript.cpp @@ -14,4 +14,4 @@ symbian: { CONFIG += parallel_test -QT += core-private gui-private declarative-private script-private +QT += core-private gui-private declarative-private diff --git a/tests/auto/declarative/qdeclarativeworkerscript/tst_qdeclarativeworkerscript.cpp b/tests/auto/declarative/qdeclarativeworkerscript/tst_qdeclarativeworkerscript.cpp index cbf152bf8b..5e67360e15 100644 --- a/tests/auto/declarative/qdeclarativeworkerscript/tst_qdeclarativeworkerscript.cpp +++ b/tests/auto/declarative/qdeclarativeworkerscript/tst_qdeclarativeworkerscript.cpp @@ -43,7 +43,7 @@ #include #include #include -#include +#include #include #include diff --git a/tests/auto/declarative/qdeclarativexmllistmodel/qdeclarativexmllistmodel.pro b/tests/auto/declarative/qdeclarativexmllistmodel/qdeclarativexmllistmodel.pro index bedd0a4602..139d4b4e0b 100644 --- a/tests/auto/declarative/qdeclarativexmllistmodel/qdeclarativexmllistmodel.pro +++ b/tests/auto/declarative/qdeclarativexmllistmodel/qdeclarativexmllistmodel.pro @@ -1,5 +1,5 @@ load(qttest_p4) -contains(QT_CONFIG,declarative): QT += declarative script gui network +contains(QT_CONFIG,declarative): QT += declarative gui network contains(QT_CONFIG,xmlpatterns) { QT += xmlpatterns DEFINES += QTEST_XMLPATTERNS @@ -18,4 +18,4 @@ symbian: { CONFIG += parallel_test -QT += core-private gui-private declarative-private script-private +QT += core-private gui-private declarative-private diff --git a/tests/auto/declarative/qjsengine/qjsengine.pro b/tests/auto/declarative/qjsengine/qjsengine.pro new file mode 100644 index 0000000000..87e52ce25d --- /dev/null +++ b/tests/auto/declarative/qjsengine/qjsengine.pro @@ -0,0 +1,21 @@ +load(qttest_p4) +QT += declarative +macx:CONFIG -= app_bundle +SOURCES += tst_qjsengine.cpp + +wince* { + DEFINES += SRCDIR=\\\"./\\\" +} else:!symbian { + DEFINES += SRCDIR=\\\"$$PWD\\\" +} + +wince*|symbian: { + addFiles.files = script + addFiles.path = . + DEPLOYMENT += addFiles +} + +symbian: { + TARGET.UID3 = 0xE0340006 + DEFINES += SYMBIAN_SRCDIR_UID=$$lower($$replace(TARGET.UID3,"0x","")) +} diff --git a/tests/auto/declarative/qjsengine/script/com/__init__.js b/tests/auto/declarative/qjsengine/script/com/__init__.js new file mode 100644 index 0000000000..7db3ee4cac --- /dev/null +++ b/tests/auto/declarative/qjsengine/script/com/__init__.js @@ -0,0 +1,9 @@ +var wasDefinedAlready = (this["com"] != undefined); +__setupPackage__("com"); +com.wasDefinedAlready = wasDefinedAlready; +com.name = __extension__; +com.level = 1; + +com.postInitCallCount = 0; +com.originalPostInit = __postInit__; +__postInit__ = function() { ++com.postInitCallCount; }; diff --git a/tests/auto/declarative/qjsengine/script/com/trolltech/__init__.js b/tests/auto/declarative/qjsengine/script/com/trolltech/__init__.js new file mode 100644 index 0000000000..a55b1328ba --- /dev/null +++ b/tests/auto/declarative/qjsengine/script/com/trolltech/__init__.js @@ -0,0 +1,9 @@ +var wasDefinedAlready = (com["trolltech"] != undefined); +__setupPackage__("com.trolltech"); +com.trolltech.wasDefinedAlready = wasDefinedAlready; +com.trolltech.name = __extension__; +com.trolltech.level = com.level + 1; + +com.trolltech.postInitCallCount = 0; +com.trolltech.originalPostInit = __postInit__; +__postInit__ = function() { ++com.trolltech.postInitCallCount; }; diff --git a/tests/auto/declarative/qjsengine/script/com/trolltech/recursive/__init__.js b/tests/auto/declarative/qjsengine/script/com/trolltech/recursive/__init__.js new file mode 100644 index 0000000000..2f4cad48da --- /dev/null +++ b/tests/auto/declarative/qjsengine/script/com/trolltech/recursive/__init__.js @@ -0,0 +1 @@ +__import__("com.trolltech.recursive"); diff --git a/tests/auto/declarative/qjsengine/script/com/trolltech/syntaxerror/__init__.js b/tests/auto/declarative/qjsengine/script/com/trolltech/syntaxerror/__init__.js new file mode 100644 index 0000000000..55bc35b5f2 --- /dev/null +++ b/tests/auto/declarative/qjsengine/script/com/trolltech/syntaxerror/__init__.js @@ -0,0 +1,5 @@ +function () { +} + +0 = 1; + diff --git a/tests/auto/declarative/qjsengine/tst_qjsengine.cpp b/tests/auto/declarative/qjsengine/tst_qjsengine.cpp new file mode 100644 index 0000000000..49febdbf5e --- /dev/null +++ b/tests/auto/declarative/qjsengine/tst_qjsengine.cpp @@ -0,0 +1,6491 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite 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 + +#include +#include +#include +#include +#include + +Q_DECLARE_METATYPE(QList) +Q_DECLARE_METATYPE(QObjectList) + +//TESTED_CLASS= +//TESTED_FILES= + +#if defined(Q_OS_SYMBIAN) +# define STRINGIFY(x) #x +# define TOSTRING(x) STRINGIFY(x) +# define SRCDIR "C:/Private/" TOSTRING(SYMBIAN_SRCDIR_UID) +#endif + +// The JavaScriptCore GC marks the C stack. To try to ensure that there is +// no JSObject* left in stack memory by the compiler, we call this function +// to zap some bytes of memory before calling collectGarbage(). +static void zapSomeStack() +{ + char buf[4096]; + memset(buf, 0, sizeof(buf)); +} + +static void collectGarbage_helper(QJSEngine &eng) +{ + zapSomeStack(); + eng.collectGarbage(); +} + +class tst_QJSEngine : public QObject +{ + Q_OBJECT + +public: + tst_QJSEngine(); + virtual ~tst_QJSEngine(); + +private slots: + void constructWithParent(); +#if 0 // FIXME: no QScriptContext + void currentContext(); + void pushPopContext(); +#endif +#if 0 // FIXME: No prototype API in QScriptEngine + void getSetDefaultPrototype_int(); + void getSetDefaultPrototype_customType(); +#endif +#if 0 // FIXME: no QScriptContext + void newFunction(); + void newFunctionWithArg(); + void newFunctionWithProto(); +#endif + void newObject(); + void newArray(); + void newArray_HooliganTask218092(); + void newArray_HooliganTask233836(); + void newVariant(); +#if 0 // FIXME: No prototype API in QScriptEngine + void newVariant_defaultPrototype(); +#endif +#if 0 // ###FIXME: No QVariant object promotion API + void newVariant_promoteObject(); + void newVariant_replaceValue(); +#endif + void newVariant_valueOfToString(); +#if 0 // ###FIXME: No QVariant object promotion API + void newVariant_promoteNonObject(); + void newVariant_promoteNonQScriptObject(); +#endif + void newRegExp(); + void jsRegExp(); + void newDate(); + void jsParseDate(); + void newQObject(); + void newQObject_ownership(); + void newQObject_promoteObject(); + void newQObject_sameQObject(); +#if 0 // FIXME: No prototype API in QScriptEngine + void newQObject_defaultPrototype(); +#endif + void newQObject_promoteNonObject(); + void newQObject_promoteNonQScriptObject(); +#if 0 // ### FIXME: No QScript Metaobject support right now + void newQMetaObject(); + void newActivationObject(); +#endif +#if 0 // ###FIXME: No setGlobalObject support - yay + void getSetGlobalObjectSimple(); + void getSetGlobalObject(); +#endif + void globalObjectProperties(); + void globalObjectEquals(); +#if 0 // ###FIXME: No QScriptValueIterator API + void globalObjectProperties_enumerate(); +#endif + void createGlobalObjectProperty(); + void globalObjectGetterSetterProperty(); +#if 0 // ###FIXME: No support for setting the global object + void customGlobalObjectWithPrototype(); +#endif + void globalObjectWithCustomPrototype(); + void builtinFunctionNames_data(); + void builtinFunctionNames(); +#if 0 // ###FIXME: No syntax checking result + void checkSyntax_data(); + void checkSyntax(); +#endif +#if 0 // ###FIXME: No support for canEvaluate + void canEvaluate_data(); + void canEvaluate(); +#endif + void evaluate_data(); + void evaluate(); +#if 0 // ###FIXME: no support for c-style callbacks + void nestedEvaluate(); +#endif +#if 0 // ### FIXME: No c-style callbacks + void uncaughtException(); +#endif + void errorMessage_QT679(); + void valueConversion_basic(); +#if 0 // FIXME: No API for custom types + void valueConversion_customType(); + void valueConversion_sequence(); +#endif + void valueConversion_QVariant(); +#if 0 // FIXME: No support for custom types + void valueConversion_hooliganTask248802(); +#endif + void valueConversion_basic2(); + void valueConversion_dateTime(); + void valueConversion_regExp(); +#if 0 // FIXME: No qScriptValueFromValue + void qScriptValueFromValue_noEngine(); +#endif +#if 0 // ###FIXME: No QScriptContext + void importExtension(); + void infiniteRecursion(); +#endif +#if 0 // FIXME: No support for default prototypes + void castWithPrototypeChain(); +#endif + void castWithMultipleInheritance(); +#if 0 // ###FIXME: ScriptOwnership + void collectGarbage(); +#endif +#if 0 // ###FIXME: no reportAdditionalMemoryCost API + void reportAdditionalMemoryCost(); +#endif + void gcWithNestedDataStructure(); +#if 0 // ###FIXME: No processEvents handling + void processEventsWhileRunning(); + void processEventsWhileRunning_function(); + void throwErrorFromProcessEvents_data(); + void throwErrorFromProcessEvents(); + void disableProcessEventsInterval(); +#endif +#if 0 // ###FIXME: No QScriptValueIterator API + void stacktrace(); +#endif + void numberParsing_data(); + void numberParsing(); + void automaticSemicolonInsertion(); +#if 0 // ###FIXME: no abortEvaluation API + void abortEvaluation_notEvaluating(); + void abortEvaluation_data(); + void abortEvaluation(); + void abortEvaluation_tryCatch(); + void abortEvaluation_fromNative(); + void abortEvaluation_QTBUG9433(); +#endif +#if 0 // ###FIXME: no QScriptEngine::isEvaluating + void isEvaluating_notEvaluating(); + void isEvaluating_fromNative(); + void isEvaluating_fromEvent(); +#endif +#if 0 // ###FIXME: depracated + void printFunctionWithCustomHandler(); + void printThrowsException(); +#endif + void errorConstructors(); + void argumentsProperty_globalContext(); + void argumentsProperty_JS(); +#if 0 // ###FIXME: no QScriptContext API + void argumentsProperty_evaluateInNativeFunction(); +#endif + void jsNumberClass(); + void jsForInStatement_simple(); + void jsForInStatement_prototypeProperties(); + void jsForInStatement_mutateWhileIterating(); + void jsForInStatement_arrays(); + void jsForInStatement_nullAndUndefined(); + void jsFunctionDeclarationAsStatement(); + void stringObjects(); + void jsStringPrototypeReplaceBugs(); + void getterSetterThisObject_global(); + void getterSetterThisObject_plain(); + void getterSetterThisObject_prototypeChain(); +#if 0 // ###FIXME: no QScriptContext API + void getterSetterThisObject_activation(); +#endif + void jsContinueInSwitch(); + void jsShadowReadOnlyPrototypeProperty(); + void toObject(); + void jsReservedWords_data(); + void jsReservedWords(); + void jsFutureReservedWords_data(); + void jsFutureReservedWords(); + void jsThrowInsideWithStatement(); +#if 0 // ###FIXME: No QScriptEngineAgent API + void getSetAgent_ownership(); + void getSetAgent_deleteAgent(); + void getSetAgent_differentEngine(); +#endif +#if 0 // ###FIXME: No QScriptString API + void reentrancy_stringHandles(); +#endif +#if 0 // ###FIXME: No processEventsInterval API + void reentrancy_processEventsInterval(); +#endif +#if 0 // FIXME: No support for custom types + void reentrancy_typeConversion(); +#endif + void reentrancy_globalObjectProperties(); + void reentrancy_Array(); + void reentrancy_objectCreation(); + void jsIncDecNonObjectProperty(); +#if 0 // ###FIXME: no installTranslatorFunctions API + void installTranslatorFunctions(); + void translateScript_data(); + void translateScript(); + void translateScript_crossScript(); + void translateScript_callQsTrFromNative(); + void translateScript_trNoOp(); + void translateScript_callQsTrFromCpp(); + void translateWithInvalidArgs_data(); + void translateWithInvalidArgs(); + void translationContext_data(); + void translationContext(); + void translateScriptIdBased(); + void translateScriptUnicode_data(); + void translateScriptUnicode(); + void translateScriptUnicodeIdBased_data(); + void translateScriptUnicodeIdBased(); + void translateFromBuiltinCallback(); +#endif +#if 0 // ###FIXME: No QScriptValue::scope API + void functionScopes(); +#endif +#if 0 // ###FIXME: No QScriptContext API + void nativeFunctionScopes(); +#endif +#if 0 // ###FIXME: No QScriptProgram API + void evaluateProgram(); + void evaluateProgram_customScope(); + void evaluateProgram_closure(); + void evaluateProgram_executeLater(); + void evaluateProgram_multipleEngines(); + void evaluateProgram_empty(); +#endif +#if 0 // ###FIXME: No QScriptContext API + void collectGarbageAfterConnect(); + void collectGarbageAfterNativeArguments(); + void promoteThisObjectToQObjectInConstructor(); +#endif +#if 0 // ###FIXME: No QScript MetaObject API + void scriptValueFromQMetaObject(); +#endif + + void qRegExpInport_data(); + void qRegExpInport(); +#if 0 // ###FIXME: No QScriptContext API + void reentrency(); +#endif +#if 0 // ###FIXME: No QSCriptDeclarativeClass API + void newFixedStaticScopeObject(); + void newGrowingStaticScopeObject(); +#endif + void dateRoundtripJSQtJS(); + void dateRoundtripQtJSQt(); + void dateConversionJSQt(); + void dateConversionQtJS(); +}; + +tst_QJSEngine::tst_QJSEngine() +{ +} + +tst_QJSEngine::~tst_QJSEngine() +{ +} + +void tst_QJSEngine::constructWithParent() +{ + QPointer ptr; + { + QObject obj; + QJSEngine *engine = new QJSEngine(&obj); + ptr = engine; + } + QVERIFY(ptr == 0); +} + +#if 0 // FIXME: no QScriptContext +void tst_QJSEngine::currentContext() +{ + QScriptEngine eng; + QScriptContext *globalCtx = eng.currentContext(); + QVERIFY(globalCtx != 0); + QVERIFY(globalCtx->parentContext() == 0); + QCOMPARE(globalCtx->engine(), &eng); + QCOMPARE(globalCtx->argumentCount(), 0); + QCOMPARE(globalCtx->backtrace().size(), 1); + QVERIFY(!globalCtx->isCalledAsConstructor()); + QVERIFY(!globalCtx->callee().isValid()); + QCOMPARE(globalCtx->state(), QScriptContext::NormalState); + QVERIFY(globalCtx->thisObject().strictlyEquals(eng.globalObject())); + QVERIFY(globalCtx->activationObject().strictlyEquals(eng.globalObject())); + QVERIFY(globalCtx->argumentsObject().isObject()); +} + +void tst_QJSEngine::pushPopContext() +{ + QScriptEngine eng; + QScriptContext *globalCtx = eng.currentContext(); + QScriptContext *ctx = eng.pushContext(); + QVERIFY(ctx != 0); + QCOMPARE(ctx->parentContext(), globalCtx); + QVERIFY(!ctx->isCalledAsConstructor()); + QVERIFY(!ctx->callee().isValid()); + QVERIFY(ctx->thisObject().strictlyEquals(eng.globalObject())); + QCOMPARE(ctx->argumentCount(), 0); + QCOMPARE(ctx->backtrace().size(), 2); + QCOMPARE(ctx->engine(), &eng); + QCOMPARE(ctx->state(), QScriptContext::NormalState); + QVERIFY(ctx->activationObject().isObject()); + QVERIFY(ctx->argumentsObject().isObject()); + + QScriptContext *ctx2 = eng.pushContext(); + QVERIFY(ctx2 != 0); + QCOMPARE(ctx2->parentContext(), ctx); + QVERIFY(!ctx2->activationObject().strictlyEquals(ctx->activationObject())); + QVERIFY(!ctx2->argumentsObject().strictlyEquals(ctx->argumentsObject())); + + eng.popContext(); + eng.popContext(); + QTest::ignoreMessage(QtWarningMsg, "QScriptEngine::popContext() doesn't match with pushContext()"); + eng.popContext(); // ignored + QTest::ignoreMessage(QtWarningMsg, "QScriptEngine::popContext() doesn't match with pushContext()"); + eng.popContext(); // ignored +} + +static QScriptValue myFunction(QScriptContext *, QScriptEngine *eng) +{ + return eng->nullValue(); +} + +static QScriptValue myFunctionWithVoidArg(QScriptContext *, QScriptEngine *eng, void *) +{ + return eng->nullValue(); +} + +static QScriptValue myThrowingFunction(QScriptContext *ctx, QScriptEngine *) +{ + return ctx->throwError("foo"); +} + +static QScriptValue myFunctionThatReturns(QScriptContext *, QScriptEngine *eng) +{ + return QScriptValue(eng, 42); +} + +static QScriptValue myFunctionThatReturnsWithoutEngine(QScriptContext *, QScriptEngine *) +{ + return QScriptValue(1024); +} + +static QScriptValue myFunctionThatReturnsWrongEngine(QScriptContext *, QScriptEngine *, void *arg) +{ + QScriptEngine* wrongEngine = reinterpret_cast(arg); + return QScriptValue(wrongEngine, 42); +} + +static QScriptValue sumFunction(QScriptContext *context, QScriptEngine *engine) +{ + int sum = 0; + + for (int i = 0; i < context->argumentCount(); i++) { + QScriptValue n = context->argument(i); + if (n.isNumber()) + sum += n.toInteger(); + } + + return QScriptValue(engine, sum); +} + +void tst_QJSEngine::newFunction() +{ + QScriptEngine eng; + { + QScriptValue fun = eng.newFunction(myFunction); + QCOMPARE(fun.isValid(), true); + QCOMPARE(fun.isFunction(), true); + QCOMPARE(fun.isObject(), true); + QCOMPARE(fun.scriptClass(), (QScriptClass*)0); + // a prototype property is automatically constructed + { + QScriptValue prot = fun.property("prototype", QScriptValue::ResolveLocal); + QVERIFY(prot.isObject()); + QVERIFY(prot.property("constructor").strictlyEquals(fun)); + QCOMPARE(fun.propertyFlags("prototype"), QScriptValue::Undeletable | QScriptValue::SkipInEnumeration); + QCOMPARE(prot.propertyFlags("constructor"), QScriptValue::SkipInEnumeration); + } + // prototype should be Function.prototype + QCOMPARE(fun.prototype().isValid(), true); + QCOMPARE(fun.prototype().isFunction(), true); + QCOMPARE(fun.prototype().strictlyEquals(eng.evaluate("Function.prototype")), true); + + QCOMPARE(fun.call().isNull(), true); + QCOMPARE(fun.construct().isObject(), true); + } +} + +void tst_QJSEngine::newFunctionWithArg() +{ + QScriptEngine eng; + { + QScriptValue fun = eng.newFunction(myFunctionWithVoidArg, (void*)this); + QVERIFY(fun.isFunction()); + QCOMPARE(fun.scriptClass(), (QScriptClass*)0); + // a prototype property is automatically constructed + { + QScriptValue prot = fun.property("prototype", QScriptValue::ResolveLocal); + QVERIFY(prot.isObject()); + QVERIFY(prot.property("constructor").strictlyEquals(fun)); + QCOMPARE(fun.propertyFlags("prototype"), QScriptValue::Undeletable | QScriptValue::SkipInEnumeration); + QCOMPARE(prot.propertyFlags("constructor"), QScriptValue::SkipInEnumeration); + } + // prototype should be Function.prototype + QCOMPARE(fun.prototype().isValid(), true); + QCOMPARE(fun.prototype().isFunction(), true); + QCOMPARE(fun.prototype().strictlyEquals(eng.evaluate("Function.prototype")), true); + + QCOMPARE(fun.call().isNull(), true); + QCOMPARE(fun.construct().isObject(), true); + } +} + +void tst_QJSEngine::newFunctionWithProto() +{ + QScriptEngine eng; + { + QScriptValue proto = eng.newObject(); + QScriptValue fun = eng.newFunction(myFunction, proto); + QCOMPARE(fun.isValid(), true); + QCOMPARE(fun.isFunction(), true); + QCOMPARE(fun.isObject(), true); + // internal prototype should be Function.prototype + QCOMPARE(fun.prototype().isValid(), true); + QCOMPARE(fun.prototype().isFunction(), true); + QCOMPARE(fun.prototype().strictlyEquals(eng.evaluate("Function.prototype")), true); + // public prototype should be the one we passed + QCOMPARE(fun.property("prototype").strictlyEquals(proto), true); + QCOMPARE(fun.propertyFlags("prototype"), QScriptValue::Undeletable | QScriptValue::SkipInEnumeration); + QCOMPARE(proto.property("constructor").strictlyEquals(fun), true); + QCOMPARE(proto.propertyFlags("constructor"), QScriptValue::SkipInEnumeration); + + QCOMPARE(fun.call().isNull(), true); + QCOMPARE(fun.construct().isObject(), true); + } + // whether the return value is correct + { + QScriptValue fun = eng.newFunction(myFunctionThatReturns); + QCOMPARE(fun.isValid(), true); + QCOMPARE(fun.isFunction(), true); + QCOMPARE(fun.isObject(), true); + + QScriptValue result = fun.call(); + QCOMPARE(result.isNumber(), true); + QCOMPARE(result.toInt32(), 42); + } + // whether the return value is assigned to the correct engine + { + QScriptValue fun = eng.newFunction(myFunctionThatReturnsWithoutEngine); + QCOMPARE(fun.isValid(), true); + QCOMPARE(fun.isFunction(), true); + QCOMPARE(fun.isObject(), true); + + QScriptValue result = fun.call(); + QCOMPARE(result.engine(), &eng); + QCOMPARE(result.isNumber(), true); + QCOMPARE(result.toInt32(), 1024); + } + // whether the return value is undefined when returning a value with wrong engine + { + QScriptEngine wrongEngine; + + QScriptValue fun = eng.newFunction(myFunctionThatReturnsWrongEngine, reinterpret_cast(&wrongEngine)); + QCOMPARE(fun.isValid(), true); + QCOMPARE(fun.isFunction(), true); + QCOMPARE(fun.isObject(), true); + + QTest::ignoreMessage(QtWarningMsg, "QScriptValue::call(): Value from different engine returned from native function, returning undefined value instead."); + QScriptValue result = fun.call(); + QCOMPARE(result.isValid(), true); + QCOMPARE(result.isUndefined(), true); + } + // checking if arguments are passed correctly + { + QScriptEngine wrongEngine; + + QScriptValue fun = eng.newFunction(sumFunction); + QCOMPARE(fun.isValid(), true); + QCOMPARE(fun.isFunction(), true); + QCOMPARE(fun.isObject(), true); + + QScriptValue result = fun.call(); + QCOMPARE(result.isNumber(), true); + QCOMPARE(result.toInt32(), 0); + + result = fun.call(QScriptValue(), QScriptValueList() << 1); + QCOMPARE(result.isNumber(), true); + QCOMPARE(result.toInt32(), 1); + + result = fun.call(QScriptValue(), QScriptValueList() << 1 << 2 << 3); + QCOMPARE(result.isNumber(), true); + QCOMPARE(result.toInt32(), 6); + + result = fun.call(QScriptValue(), QScriptValueList() << 1 << 2 << 3 << 4); + QCOMPARE(result.isNumber(), true); + QCOMPARE(result.toInt32(), 10); + } +} +#endif + +void tst_QJSEngine::newObject() +{ + QJSEngine eng; + QJSValue object = eng.newObject(); + QCOMPARE(object.isValid(), true); + QCOMPARE(object.isObject(), true); + QCOMPARE(object.isFunction(), false); +// ###FIXME: No QScriptClass QCOMPARE(object.scriptClass(), (QScriptClass*)0); + // prototype should be Object.prototype + QCOMPARE(object.prototype().isValid(), true); + QCOMPARE(object.prototype().isObject(), true); + QCOMPARE(object.prototype().strictlyEquals(eng.evaluate("Object.prototype")), true); +} + +void tst_QJSEngine::newArray() +{ + QJSEngine eng; + QJSValue array = eng.newArray(); + QCOMPARE(array.isValid(), true); + QCOMPARE(array.isArray(), true); + QCOMPARE(array.isObject(), true); + QVERIFY(!array.isFunction()); +// ###FIXME: No QScriptClass QCOMPARE(array.scriptClass(), (QScriptClass*)0); + // prototype should be Array.prototype + QCOMPARE(array.prototype().isValid(), true); + QCOMPARE(array.prototype().isArray(), true); + QCOMPARE(array.prototype().strictlyEquals(eng.evaluate("Array.prototype")), true); +} + +void tst_QJSEngine::newArray_HooliganTask218092() +{ + QJSEngine eng; + { + QJSValue ret = eng.evaluate("[].splice(0, 0, 'a')"); + QVERIFY(ret.isArray()); + QCOMPARE(ret.property("length").toInt32(), 0); + } + { + QJSValue ret = eng.evaluate("['a'].splice(0, 1, 'b')"); + QVERIFY(ret.isArray()); + QCOMPARE(ret.property("length").toInt32(), 1); + } + { + QJSValue ret = eng.evaluate("['a', 'b'].splice(0, 1, 'c')"); + QVERIFY(ret.isArray()); + QCOMPARE(ret.property("length").toInt32(), 1); + } + { + QJSValue ret = eng.evaluate("['a', 'b', 'c'].splice(0, 2, 'd')"); + QVERIFY(ret.isArray()); + QCOMPARE(ret.property("length").toInt32(), 2); + } + { + QJSValue ret = eng.evaluate("['a', 'b', 'c'].splice(1, 2, 'd', 'e', 'f')"); + QVERIFY(ret.isArray()); + QCOMPARE(ret.property("length").toInt32(), 2); + } +} + +void tst_QJSEngine::newArray_HooliganTask233836() +{ + QJSEngine eng; + { + // According to ECMA-262, this should cause a RangeError. + QJSValue ret = eng.evaluate("a = new Array(4294967295); a.push('foo')"); + QVERIFY(ret.isError() && ret.toString().contains(QLatin1String("RangeError"))); + } + { + QJSValue ret = eng.newArray(0xFFFFFFFF); + QEXPECT_FAIL("", "The maximum length of arrays is defined by v8 currently and differs from QtScript", Abort); + QCOMPARE(ret.property("length").toUInt32(), uint(0xFFFFFFFF)); + ret.setProperty(0xFFFFFFFF, 123); + QCOMPARE(ret.property("length").toUInt32(), uint(0xFFFFFFFF)); + QVERIFY(ret.property(0xFFFFFFFF).isNumber()); + QCOMPARE(ret.property(0xFFFFFFFF).toInt32(), 123); + ret.setProperty(123, 456); + QCOMPARE(ret.property("length").toUInt32(), uint(0xFFFFFFFF)); + QVERIFY(ret.property(123).isNumber()); + QCOMPARE(ret.property(123).toInt32(), 456); + } +} + +void tst_QJSEngine::newVariant() +{ + QJSEngine eng; + { + QJSValue opaque = eng.newVariant(QVariant()); + QCOMPARE(opaque.isValid(), true); + QCOMPARE(opaque.isVariant(), true); + QVERIFY(!opaque.isFunction()); + QCOMPARE(opaque.isObject(), true); + QCOMPARE(opaque.prototype().isValid(), true); + QEXPECT_FAIL("", "FIXME: newly created QObject's prototype is an JS Object", Continue); + QCOMPARE(opaque.prototype().isVariant(), true); + QVERIFY(opaque.property("valueOf").call(opaque).isUndefined()); + } +} + +#if 0 // FIXME: No prototype API in QScriptEngine +void tst_QJSEngine::newVariant_defaultPrototype() +{ + // default prototype should be set automatically + QScriptEngine eng; + { + QScriptValue proto = eng.newObject(); + eng.setDefaultPrototype(qMetaTypeId(), proto); + QScriptValue ret = eng.newVariant(QVariant(QString::fromLatin1("hello"))); + QVERIFY(ret.isVariant()); +// ###FIXME: No QScriptClass QCOMPARE(ret.scriptClass(), (QScriptClass*)0); + QVERIFY(ret.prototype().strictlyEquals(proto)); + eng.setDefaultPrototype(qMetaTypeId(), QScriptValue()); + QScriptValue ret2 = eng.newVariant(QVariant(QString::fromLatin1("hello"))); + QVERIFY(ret2.isVariant()); + QVERIFY(!ret2.prototype().strictlyEquals(proto)); + } +} +#endif + +#if 0 // ###FIXME: No QVariant object promotion API +void tst_QJSEngine::newVariant_promoteObject() +{ + // "promote" plain object to variant + QScriptEngine eng; + { + QScriptValue object = eng.newObject(); + object.setProperty("foo", eng.newObject()); + object.setProperty("bar", object.property("foo")); + QVERIFY(object.property("foo").isObject()); + QVERIFY(!object.property("foo").isVariant()); + QScriptValue originalProto = object.property("foo").prototype(); + QSKIP("It is not possible to promote plain object to a wrapper", SkipAll); + QScriptValue ret = eng.newVariant(object.property("foo"), QVariant(123)); + QVERIFY(ret.isValid()); + QVERIFY(ret.strictlyEquals(object.property("foo"))); + QVERIFY(ret.isVariant()); + QVERIFY(object.property("foo").isVariant()); + QVERIFY(object.property("bar").isVariant()); + QCOMPARE(ret.toVariant(), QVariant(123)); + QVERIFY(ret.prototype().strictlyEquals(originalProto)); + } +} + +void tst_QJSEngine::newVariant_replaceValue() +{ + // replace value of existing object + QScriptEngine eng; + { + QScriptValue object = eng.newVariant(QVariant(123)); + for (int x = 0; x < 2; ++x) { + QScriptValue ret = eng.newVariant(object, QVariant(456)); + QVERIFY(ret.isValid()); + QVERIFY(ret.strictlyEquals(object)); + QVERIFY(ret.isVariant()); + QCOMPARE(ret.toVariant(), QVariant(456)); + } + } +} +#endif + +void tst_QJSEngine::newVariant_valueOfToString() +{ + // valueOf() and toString() + QJSEngine eng; + { + QJSValue object = eng.newVariant(QVariant(123)); + QJSValue value = object.property("valueOf").call(object); + QVERIFY(value.isNumber()); + QCOMPARE(value.toInt32(), 123); + QCOMPARE(object.toString(), QString::fromLatin1("123")); + QCOMPARE(object.toVariant().toString(), object.toString()); + } + { + QJSValue object = eng.newVariant(QVariant(QString::fromLatin1("hello"))); + QJSValue value = object.property("valueOf").call(object); + QVERIFY(value.isString()); + QCOMPARE(value.toString(), QString::fromLatin1("hello")); + QCOMPARE(object.toString(), QString::fromLatin1("hello")); + QCOMPARE(object.toVariant().toString(), object.toString()); + } + { + QJSValue object = eng.newVariant(QVariant(false)); + QJSValue value = object.property("valueOf").call(object); + QVERIFY(value.isBoolean()); + QCOMPARE(value.toBoolean(), false); + QCOMPARE(object.toString(), QString::fromLatin1("false")); + QCOMPARE(object.toVariant().toString(), object.toString()); + } + { + QJSValue object = eng.newVariant(QVariant(QPoint(10, 20))); + QJSValue value = object.property("valueOf").call(object); + QVERIFY(value.isObject()); + QVERIFY(value.strictlyEquals(object)); + QCOMPARE(object.toString(), QString::fromLatin1("QVariant(QPoint)")); + } +} + +#if 0 // ###FIXME: No QVariant object promotion API +void tst_QJSEngine::newVariant_promoteNonObject() +{ + QScriptEngine eng; + { + QVariant var(456); + QScriptValue ret = eng.newVariant(123, var); + QVERIFY(ret.isVariant()); + QCOMPARE(ret.toVariant(), var); + } +} + +void tst_QJSEngine::newVariant_promoteNonQScriptObject() +{ + QSKIP("This test relay on limitation of QtScript JSC implementation", SkipAll); + QScriptEngine eng; + { + QTest::ignoreMessage(QtWarningMsg, "QScriptEngine::newVariant(): changing class of non-QScriptObject not supported"); + QScriptValue ret = eng.newVariant(eng.newArray(), 123); + QVERIFY(!ret.isValid()); + } +} +#endif + +void tst_QJSEngine::newRegExp() +{ + QJSEngine eng; + for (int x = 0; x < 2; ++x) { + QJSValue rexp; + if (x == 0) + rexp = eng.newRegExp("foo", "bar"); + else + rexp = eng.newRegExp(QRegExp("foo")); + QCOMPARE(rexp.isValid(), true); + QCOMPARE(rexp.isRegExp(), true); + QCOMPARE(rexp.isObject(), true); + QVERIFY(rexp.isFunction()); // in JSC, RegExp objects are callable + // prototype should be RegExp.prototype + QCOMPARE(rexp.prototype().isValid(), true); + QCOMPARE(rexp.prototype().isObject(), true); + QCOMPARE(rexp.prototype().isRegExp(), false); + QCOMPARE(rexp.prototype().strictlyEquals(eng.evaluate("RegExp.prototype")), true); + + QCOMPARE(rexp.toRegExp().pattern(), QRegExp("foo").pattern()); + } +} + +void tst_QJSEngine::jsRegExp() +{ + // See ECMA-262 Section 15.10, "RegExp Objects". + // These should really be JS-only tests, as they test the implementation's + // ECMA-compliance, not the C++ API. Compliance should already be covered + // by the Mozilla tests (qscriptjstestsuite). + // We can consider updating the expected results of this test if the + // RegExp implementation changes. + + QJSEngine eng; + QJSValue r = eng.evaluate("/foo/gim"); + QVERIFY(r.isRegExp()); + QCOMPARE(r.toString(), QString::fromLatin1("/foo/gim")); + + QJSValue rxCtor = eng.globalObject().property("RegExp"); + QJSValue r2 = rxCtor.call(QJSValue(), QJSValueList() << r); + QVERIFY(r2.isRegExp()); + QVERIFY(r2.strictlyEquals(r)); + + QJSValue r3 = rxCtor.call(QJSValue(), QJSValueList() << r << "gim"); + QVERIFY(r3.isError()); + QVERIFY(r3.toString().contains(QString::fromLatin1("TypeError"))); // Cannot supply flags when constructing one RegExp from another + + QJSValue r4 = rxCtor.call(QJSValue(), QJSValueList() << "foo" << "gim"); + QVERIFY(r4.isRegExp()); + + QJSValue r5 = rxCtor.construct(QJSValueList() << r); + QVERIFY(r5.isRegExp()); + QCOMPARE(r5.toString(), QString::fromLatin1("/foo/gim")); + // In JSC, constructing a RegExp from another produces the same identical object. + // This is different from SpiderMonkey and old back-end. + QVERIFY(!r5.strictlyEquals(r)); + + QEXPECT_FAIL("", "V8 and jsc ignores invalid flags", Continue); //https://bugs.webkit.org/show_bug.cgi?id=41614 + QJSValue r6 = rxCtor.construct(QJSValueList() << "foo" << "bar"); + QVERIFY(r6.isError()); + // QVERIFY(r6.toString().contains(QString::fromLatin1("SyntaxError"))); // Invalid regular expression flag + + + QJSValue r7 = eng.evaluate("/foo/gimp"); + /* v8 and jsc ignores invalid flags + QVERIFY(r7.isError()); + QVERIFY(r7.toString().contains(QString::fromLatin1("SyntaxError"))); // Invalid regular expression flag + */ + + // JSC doesn't complain about duplicate flags. + QJSValue r8 = eng.evaluate("/foo/migmigmig"); + QVERIFY(r8.isRegExp()); + QCOMPARE(r8.toString(), QString::fromLatin1("/foo/gim")); + + QJSValue r9 = rxCtor.construct(); + QVERIFY(r9.isRegExp()); + QCOMPARE(r9.toString(), QString::fromLatin1("/(?:)/")); + + QJSValue r10 = rxCtor.construct(QJSValueList() << "" << "gim"); + QVERIFY(r10.isRegExp()); + QCOMPARE(r10.toString(), QString::fromLatin1("/(?:)/gim")); + + QJSValue r11 = rxCtor.construct(QJSValueList() << "{1.*}" << "g"); + QVERIFY(r11.isRegExp()); + QCOMPARE(r11.toString(), QString::fromLatin1("/{1.*}/g")); +} + +void tst_QJSEngine::newDate() +{ + QJSEngine eng; + + { + QJSValue date = eng.newDate(0); + QCOMPARE(date.isValid(), true); + QCOMPARE(date.isDate(), true); + QCOMPARE(date.isObject(), true); + QVERIFY(!date.isFunction()); + // prototype should be Date.prototype + QCOMPARE(date.prototype().isValid(), true); + QCOMPARE(date.prototype().isDate(), true); + QCOMPARE(date.prototype().strictlyEquals(eng.evaluate("Date.prototype")), true); + } + + { + QDateTime dt = QDateTime(QDate(1, 2, 3), QTime(4, 5, 6, 7), Qt::LocalTime); + QJSValue date = eng.newDate(dt); + QCOMPARE(date.isValid(), true); + QCOMPARE(date.isDate(), true); + QCOMPARE(date.isObject(), true); + // prototype should be Date.prototype + QCOMPARE(date.prototype().isValid(), true); + QCOMPARE(date.prototype().isDate(), true); + QCOMPARE(date.prototype().strictlyEquals(eng.evaluate("Date.prototype")), true); + + QCOMPARE(date.toDateTime(), dt); + } + + { + QDateTime dt = QDateTime(QDate(1, 2, 3), QTime(4, 5, 6, 7), Qt::UTC); + QJSValue date = eng.newDate(dt); + // toDateTime() result should be in local time + QCOMPARE(date.toDateTime(), dt.toLocalTime()); + } +} + +void tst_QJSEngine::jsParseDate() +{ + QJSEngine eng; + // Date.parse() should return NaN when it fails + { + QJSValue ret = eng.evaluate("Date.parse()"); + QVERIFY(ret.isNumber()); + QVERIFY(qIsNaN(ret.toNumber())); + } + + // Date.parse() should be able to parse the output of Date().toString() +#ifndef Q_WS_WIN // TODO: Test and remove this since 169701 has been fixed + { + QJSValue ret = eng.evaluate("var x = new Date(); var s = x.toString(); s == new Date(Date.parse(s)).toString()"); + QVERIFY(ret.isBoolean()); + QCOMPARE(ret.toBoolean(), true); + } +#endif +} + +void tst_QJSEngine::newQObject() +{ + QJSEngine eng; + + { + QJSValue qobject = eng.newQObject(0); + QCOMPARE(qobject.isValid(), true); + QCOMPARE(qobject.isNull(), true); + QCOMPARE(qobject.isObject(), false); + QCOMPARE(qobject.toQObject(), (QObject *)0); + } + { + QJSValue qobject = eng.newQObject(this); + QCOMPARE(qobject.isValid(), true); + QCOMPARE(qobject.isQObject(), true); + QCOMPARE(qobject.isObject(), true); + QCOMPARE(qobject.toQObject(), (QObject *)this); + QVERIFY(!qobject.isFunction()); + // prototype should be QObject.prototype + QCOMPARE(qobject.prototype().isValid(), true); + QEXPECT_FAIL("", "FIXME: newly created QObject's prototype is an JS Object", Continue); + QCOMPARE(qobject.prototype().isQObject(), true); +// ###FIXME: No QScriptClass QCOMPARE(qobject.scriptClass(), (QScriptClass*)0); + } +} + +void tst_QJSEngine::newQObject_ownership() +{ +#if 0 // FIXME: ownership tests need to be revivewed + QScriptEngine eng; + { + QPointer ptr = new QObject(); + QVERIFY(ptr != 0); + { + QScriptValue v = eng.newQObject(ptr, QScriptEngine::ScriptOwnership); + } + eng.evaluate("gc()"); + if (ptr) + QEXPECT_FAIL("", "In the JSC-based back-end, script-owned QObjects are not always deleted immediately during GC", Continue); + QVERIFY(ptr == 0); + } + { + QPointer ptr = new QObject(); + QVERIFY(ptr != 0); + { + QScriptValue v = eng.newQObject(ptr, QScriptEngine::QtOwnership); + } + QObject *before = ptr; + eng.evaluate("gc()"); + QVERIFY(ptr == before); + delete ptr; + } + { + QObject *parent = new QObject(); + QObject *child = new QObject(parent); + QScriptValue v = eng.newQObject(child, QScriptEngine::QtOwnership); + QCOMPARE(v.toQObject(), child); + delete parent; + QCOMPARE(v.toQObject(), (QObject *)0); + } + { + QPointer ptr = new QObject(); + QVERIFY(ptr != 0); + { + QScriptValue v = eng.newQObject(ptr, QScriptEngine::AutoOwnership); + } + eng.evaluate("gc()"); + // no parent, so it should be like ScriptOwnership + if (ptr) + QEXPECT_FAIL("", "In the JSC-based back-end, script-owned QObjects are not always deleted immediately during GC", Continue); + QVERIFY(ptr == 0); + } + { + QObject *parent = new QObject(); + QPointer child = new QObject(parent); + QVERIFY(child != 0); + { + QScriptValue v = eng.newQObject(child, QScriptEngine::AutoOwnership); + } + eng.evaluate("gc()"); + // has parent, so it should be like QtOwnership + QVERIFY(child != 0); + delete parent; + } +#endif +} + +void tst_QJSEngine::newQObject_promoteObject() +{ +#if 0 // ### FIXME: object promotion is not supported + QScriptEngine eng; + // "promote" plain object to QObject + { + QScriptValue obj = eng.newObject(); + QScriptValue originalProto = obj.prototype(); + QScriptValue ret = eng.newQObject(obj, this); + QVERIFY(ret.isValid()); + QVERIFY(ret.isQObject()); + QVERIFY(ret.strictlyEquals(obj)); + QVERIFY(obj.isQObject()); + QCOMPARE(ret.toQObject(), (QObject *)this); + QVERIFY(ret.prototype().strictlyEquals(originalProto)); + QScriptValue val = ret.property("objectName"); + QVERIFY(val.isString()); + } + // "promote" variant object to QObject + { + QScriptValue obj = eng.newVariant(123); + QVERIFY(obj.isVariant()); + QScriptValue originalProto = obj.prototype(); + QScriptValue ret = eng.newQObject(obj, this); + QVERIFY(ret.isQObject()); + QVERIFY(ret.strictlyEquals(obj)); + QVERIFY(obj.isQObject()); + QCOMPARE(ret.toQObject(), (QObject *)this); + QVERIFY(ret.prototype().strictlyEquals(originalProto)); + } + // replace QObject* of existing object + { + QScriptValue object = eng.newVariant(123); + QScriptValue originalProto = object.prototype(); + QObject otherQObject; + for (int x = 0; x < 2; ++x) { + QScriptValue ret = eng.newQObject(object, &otherQObject); + QVERIFY(ret.isValid()); + QVERIFY(ret.isQObject()); + QVERIFY(ret.strictlyEquals(object)); + QCOMPARE(ret.toQObject(), (QObject *)&otherQObject); + QVERIFY(ret.prototype().strictlyEquals(originalProto)); + } + } +#endif +} + +void tst_QJSEngine::newQObject_sameQObject() +{ +#if 0 // ###FIXME: No QObjectWrapOptions API + QSKIP("This test stongly relay on strictlyEquals feature that would change in near future", SkipAll); + QScriptEngine eng; + // calling newQObject() several times with same object + for (int x = 0; x < 2; ++x) { + QObject qobj; + // the default is to create a new wrapper object + QScriptValue obj1 = eng.newQObject(&qobj); + QScriptValue obj2 = eng.newQObject(&qobj); + QVERIFY(!obj2.strictlyEquals(obj1)); + + QScriptEngine::QObjectWrapOptions opt = 0; + bool preferExisting = (x != 0); + if (preferExisting) + opt |= QScriptEngine::PreferExistingWrapperObject; + + QScriptValue obj3 = eng.newQObject(&qobj, QScriptEngine::AutoOwnership, opt); + QVERIFY(!obj3.strictlyEquals(obj2)); + QScriptValue obj4 = eng.newQObject(&qobj, QScriptEngine::AutoOwnership, opt); + QCOMPARE(obj4.strictlyEquals(obj3), preferExisting); + + QScriptValue obj5 = eng.newQObject(&qobj, QScriptEngine::ScriptOwnership, opt); + QVERIFY(!obj5.strictlyEquals(obj4)); + QScriptValue obj6 = eng.newQObject(&qobj, QScriptEngine::ScriptOwnership, opt); + QCOMPARE(obj6.strictlyEquals(obj5), preferExisting); + + QScriptValue obj7 = eng.newQObject(&qobj, QScriptEngine::ScriptOwnership, + QScriptEngine::ExcludeSuperClassMethods | opt); + QVERIFY(!obj7.strictlyEquals(obj6)); + QScriptValue obj8 = eng.newQObject(&qobj, QScriptEngine::ScriptOwnership, + QScriptEngine::ExcludeSuperClassMethods | opt); + QCOMPARE(obj8.strictlyEquals(obj7), preferExisting); + } +#endif +} + +#if 0 // FIXME: No prototype API in QScriptEngine +void tst_QJSEngine::newQObject_defaultPrototype() +{ + QScriptEngine eng; + // newQObject() should set the default prototype, if one has been registered + { + QScriptValue oldQObjectProto = eng.defaultPrototype(qMetaTypeId()); + + QScriptValue qobjectProto = eng.newObject(); + eng.setDefaultPrototype(qMetaTypeId(), qobjectProto); + { + QScriptValue ret = eng.newQObject(this); + QVERIFY(ret.prototype().equals(qobjectProto)); + } + QScriptValue tstProto = eng.newObject(); + int typeId = qRegisterMetaType("tst_QJSEngine*"); + eng.setDefaultPrototype(typeId, tstProto); + { + QScriptValue ret = eng.newQObject(this); + QVERIFY(ret.prototype().equals(tstProto)); + } + + eng.setDefaultPrototype(qMetaTypeId(), oldQObjectProto); + eng.setDefaultPrototype(typeId, QScriptValue()); + } +} +#endif + +void tst_QJSEngine::newQObject_promoteNonObject() +{ +#if 0 // ### FIXME: object promotion is not supported + QScriptEngine eng; + { + QScriptValue ret = eng.newQObject(123, this); + QVERIFY(ret.isQObject()); + QCOMPARE(ret.toQObject(), this); + } +#endif +} + +void tst_QJSEngine::newQObject_promoteNonQScriptObject() +{ +#if 0 // ### FIXME: object promotion is not supported + QSKIP("Promotion of non QScriptObjects kind of works (there is not difference between Object and Array, look at comments in newQObject implementation).", SkipAll); + QScriptEngine eng; + { + QTest::ignoreMessage(QtWarningMsg, "QScriptEngine::newQObject(): changing class of non-QScriptObject not supported"); + QScriptValue ret = eng.newQObject(eng.newArray(), this); + QVERIFY(!ret.isValid()); + } +#endif +} + +#if 0 // ### FIXME: No QScript Metaobject support right now +QT_BEGIN_NAMESPACE +Q_SCRIPT_DECLARE_QMETAOBJECT(QObject, QObject*) +Q_SCRIPT_DECLARE_QMETAOBJECT(QWidget, QWidget*) +QT_END_NAMESPACE + +static QScriptValue myConstructor(QScriptContext *ctx, QScriptEngine *eng) +{ + QScriptValue obj; + if (ctx->isCalledAsConstructor()) { + obj = ctx->thisObject(); + } else { + obj = eng->newObject(); + obj.setPrototype(ctx->callee().property("prototype")); + } + obj.setProperty("isCalledAsConstructor", QScriptValue(eng, ctx->isCalledAsConstructor())); + return obj; +} + +static QScriptValue instanceofJS(const QScriptValue &inst, const QScriptValue &ctor) +{ + return inst.engine()->evaluate("(function(inst, ctor) { return inst instanceof ctor; })") + .call(QScriptValue(), QScriptValueList() << inst << ctor); +} + +void tst_QJSEngine::newQMetaObject() +{ + QScriptEngine eng; +#if 0 + QScriptValue qclass = eng.newQMetaObject(); + QScriptValue qclass2 = eng.newQMetaObject(); +#else + QScriptValue qclass = qScriptValueFromQMetaObject(&eng); + QScriptValue qclass2 = qScriptValueFromQMetaObject(&eng); +#endif + QCOMPARE(qclass.isValid(), true); + QCOMPARE(qclass.isQMetaObject(), true); + QCOMPARE(qclass.toQMetaObject(), &QObject::staticMetaObject); + QCOMPARE(qclass.isFunction(), true); + QVERIFY(qclass.property("prototype").isObject()); + + QCOMPARE(qclass2.isValid(), true); + QCOMPARE(qclass2.isQMetaObject(), true); + QCOMPARE(qclass2.toQMetaObject(), &QWidget::staticMetaObject); + QCOMPARE(qclass2.isFunction(), true); + QVERIFY(qclass2.property("prototype").isObject()); + + // prototype should be QMetaObject.prototype + QCOMPARE(qclass.prototype().isObject(), true); + QCOMPARE(qclass2.prototype().isObject(), true); + + QScriptValue instance = qclass.construct(); + QCOMPARE(instance.isQObject(), true); + QCOMPARE(instance.toQObject()->metaObject(), qclass.toQMetaObject()); + QEXPECT_FAIL("", "FIXME: newQMetaObject not implemented properly yet", Abort); + QVERIFY(instance.instanceOf(qclass)); + QVERIFY(instanceofJS(instance, qclass).strictlyEquals(true)); + + QScriptValue instance2 = qclass2.construct(); + QCOMPARE(instance2.isQObject(), true); + QCOMPARE(instance2.toQObject()->metaObject(), qclass2.toQMetaObject()); + QVERIFY(instance2.instanceOf(qclass2)); + QVERIFY(instanceofJS(instance2, qclass2).strictlyEquals(true)); + QVERIFY(!instance2.instanceOf(qclass)); + QVERIFY(instanceofJS(instance2, qclass).strictlyEquals(false)); + + QScriptValueList args; + args << instance; + QScriptValue instance3 = qclass.construct(args); + QCOMPARE(instance3.isQObject(), true); + QCOMPARE(instance3.toQObject()->parent(), instance.toQObject()); + QVERIFY(instance3.instanceOf(qclass)); + QVERIFY(instanceofJS(instance3, qclass).strictlyEquals(true)); + QVERIFY(!instance3.instanceOf(qclass2)); + QVERIFY(instanceofJS(instance3, qclass2).strictlyEquals(false)); + args.clear(); + + QPointer qpointer1 = instance.toQObject(); + QPointer qpointer2 = instance2.toQObject(); + QPointer qpointer3 = instance3.toQObject(); + + QVERIFY(qpointer1); + QVERIFY(qpointer2); + QVERIFY(qpointer3); + + // verify that AutoOwnership is in effect + instance = QScriptValue(); + collectGarbage_helper(eng); + + QVERIFY(!qpointer1); + QVERIFY(qpointer2); + QVERIFY(!qpointer3); // was child of instance + + QVERIFY(instance.toQObject() == 0); + QVERIFY(instance3.toQObject() == 0); // was child of instance + QVERIFY(instance2.toQObject() != 0); + instance2 = QScriptValue(); + collectGarbage_helper(eng); + QVERIFY(instance2.toQObject() == 0); + + // with custom constructor + QScriptValue ctor = eng.newFunction(myConstructor); + QScriptValue qclass3 = eng.newQMetaObject(&QObject::staticMetaObject, ctor); + QVERIFY(qclass3.property("prototype").equals(ctor.property("prototype"))); + { + QScriptValue ret = qclass3.call(); + QVERIFY(ret.isObject()); + QVERIFY(ret.property("isCalledAsConstructor").isBoolean()); + QVERIFY(!ret.property("isCalledAsConstructor").toBoolean()); + QVERIFY(ret.instanceOf(qclass3)); + QVERIFY(instanceofJS(ret, qclass3).strictlyEquals(true)); + QVERIFY(!ret.instanceOf(qclass)); + QVERIFY(instanceofJS(ret, qclass).strictlyEquals(false)); + } + { + QScriptValue ret = qclass3.construct(); + QVERIFY(ret.isObject()); + QVERIFY(ret.property("isCalledAsConstructor").isBoolean()); + QVERIFY(ret.property("isCalledAsConstructor").toBoolean()); + QVERIFY(ret.instanceOf(qclass3)); + QVERIFY(instanceofJS(ret, qclass3).strictlyEquals(true)); + QVERIFY(!ret.instanceOf(qclass2)); + QVERIFY(instanceofJS(ret, qclass2).strictlyEquals(false)); + } + + // subclassing + qclass2.setProperty("prototype", qclass.construct()); + QVERIFY(qclass2.construct().instanceOf(qclass)); + QVERIFY(instanceofJS(qclass2.construct(), qclass).strictlyEquals(true)); + + // with meta-constructor + QScriptValue qclass4 = eng.newQMetaObject(&QObject::staticMetaObject); + { + QScriptValue inst = qclass4.construct(); + QVERIFY(inst.isQObject()); + QVERIFY(inst.toQObject() != 0); + QCOMPARE(inst.toQObject()->parent(), (QObject*)0); + QVERIFY(inst.instanceOf(qclass4)); + QVERIFY(instanceofJS(inst, qclass4).strictlyEquals(true)); + QVERIFY(!inst.instanceOf(qclass3)); + QVERIFY(instanceofJS(inst, qclass3).strictlyEquals(false)); + } + { + QScriptValue inst = qclass4.construct(QScriptValueList() << eng.newQObject(this)); + QVERIFY(inst.isQObject()); + QVERIFY(inst.toQObject() != 0); + QCOMPARE(inst.toQObject()->parent(), (QObject*)this); + QVERIFY(inst.instanceOf(qclass4)); + QVERIFY(instanceofJS(inst, qclass4).strictlyEquals(true)); + QVERIFY(!inst.instanceOf(qclass2)); + QVERIFY(instanceofJS(inst, qclass2).strictlyEquals(false)); + } +} +#endif + +#if 0 // ###FIXME: No activation object support +void tst_QJSEngine::newActivationObject() +{ + QSKIP("internal function not implemented in JSC-based back-end", SkipAll); + QScriptEngine eng; + QScriptValue act = eng.newActivationObject(); + QEXPECT_FAIL("", "", Continue); + QCOMPARE(act.isValid(), true); + QEXPECT_FAIL("", "", Continue); + QCOMPARE(act.isObject(), true); + QVERIFY(!act.isFunction()); + QScriptValue v(&eng, 123); + act.setProperty("prop", v); + QEXPECT_FAIL("", "", Continue); + QCOMPARE(act.property("prop").strictlyEquals(v), true); + QCOMPARE(act.scope().isValid(), false); + QEXPECT_FAIL("", "", Continue); + QVERIFY(act.prototype().isNull()); +} +#endif + +#if 0 // ###FIXME: No setGlobalObject support - yay +void tst_QJSEngine::getSetGlobalObjectSimple() +{ + QScriptEngine engine; + QScriptValue object = engine.newObject(); + object.setProperty("foo", 123); + engine.evaluate("var bar = 100"); + engine.setGlobalObject(object); + engine.evaluate("rab = 100"); + QVERIFY(engine.globalObject().property("rab").isValid()); + QVERIFY(engine.globalObject().property("foo").isValid()); + QVERIFY(!engine.globalObject().property("bar").isValid()); +} + +void tst_QJSEngine::getSetGlobalObject() +{ + QScriptEngine eng; + QScriptValue glob = eng.globalObject(); + glob = QScriptValue(); // kill reference to old global object + collectGarbage_helper(eng); + + glob = eng.globalObject(); + QCOMPARE(glob.isValid(), true); + QCOMPARE(glob.isObject(), true); + QVERIFY(!glob.isFunction()); + QVERIFY(eng.currentContext()->thisObject().strictlyEquals(glob)); + QVERIFY(eng.currentContext()->activationObject().strictlyEquals(glob)); + QEXPECT_FAIL("", "FIXME: Do we really want to enforce this? ECMA standard says that it is implementation dependent, skipping for now", Continue); + QCOMPARE(glob.toString(), QString::fromLatin1("[object global]")); + // prototype should be Object.prototype + QCOMPARE(glob.prototype().isValid(), true); + QCOMPARE(glob.prototype().isObject(), true); + QEXPECT_FAIL("", "FIXME: Do we really want to enforce this? ECMA standard says that it is implementation dependent, skipping for now", Continue); + QCOMPARE(glob.prototype().strictlyEquals(eng.evaluate("Object.prototype")), true); + + eng.setGlobalObject(glob); + QVERIFY(eng.globalObject().equals(glob)); + eng.setGlobalObject(123); + QVERIFY(eng.globalObject().equals(glob)); + + QScriptValue obj = eng.newObject(); + eng.setGlobalObject(obj); + QVERIFY(eng.globalObject().strictlyEquals(obj)); + QVERIFY(eng.currentContext()->thisObject().strictlyEquals(obj)); + QVERIFY(eng.currentContext()->activationObject().strictlyEquals(obj)); + QVERIFY(eng.evaluate("this").strictlyEquals(obj)); + QEXPECT_FAIL("", "FIXME: Do we really want to enforce this? ECMA standard says that it is implementation dependent, skipping for now", Continue); + QCOMPARE(eng.globalObject().toString(), QString::fromLatin1("[object global]")); + + collectGarbage_helper(eng); + glob = QScriptValue(); // kill reference to old global object + collectGarbage_helper(eng); + obj = eng.newObject(); + eng.setGlobalObject(obj); + QVERIFY(eng.globalObject().strictlyEquals(obj)); + QVERIFY(eng.currentContext()->thisObject().strictlyEquals(obj)); + QVERIFY(eng.currentContext()->activationObject().strictlyEquals(obj)); + + collectGarbage_helper(eng); + QVERIFY(eng.globalObject().strictlyEquals(obj)); + QVERIFY(eng.currentContext()->thisObject().strictlyEquals(obj)); + QVERIFY(eng.currentContext()->activationObject().strictlyEquals(obj)); + + QVERIFY(!obj.property("foo").isValid()); + eng.evaluate("var foo = 123"); + { + QScriptValue ret = obj.property("foo"); + QVERIFY(ret.isNumber()); + QCOMPARE(ret.toInt32(), 123); + } + + QVERIFY(!obj.property("bar").isValid()); + eng.evaluate("bar = 456"); + { + QScriptValue ret = obj.property("bar"); + QVERIFY(ret.isNumber()); + QCOMPARE(ret.toInt32(), 456); + } + + QVERIFY(!obj.property("baz").isValid()); + eng.evaluate("this['baz'] = 789"); + { + QScriptValue ret = obj.property("baz"); + QVERIFY(ret.isNumber()); + QCOMPARE(ret.toInt32(), 789); + } + + { + QScriptValue ret = eng.evaluate("(function() { return this; })()"); + QVERIFY(ret.strictlyEquals(obj)); + } + + // Delete property. + { + QScriptValue ret = eng.evaluate("delete foo"); + QVERIFY(ret.isBool()); + QVERIFY(ret.toBool()); + QVERIFY(!obj.property("foo").isValid()); + } + + // Getter/setter property. + //the custom global object have an interceptor + QVERIFY(eng.evaluate("this.__defineGetter__('oof', function() { return this.bar; })").isUndefined()); + QVERIFY(eng.evaluate("this.__defineSetter__('oof', function(v) { this.bar = v; })").isUndefined()); + QVERIFY(eng.evaluate("this.__lookupGetter__('oof')").isFunction()); + QVERIFY(eng.evaluate("this.__lookupSetter__('oof')").isFunction()); + eng.evaluate("oof = 123"); + QVERIFY(eng.evaluate("oof").equals(obj.property("bar"))); + + // Enumeration. + { + QScriptValue ret = eng.evaluate("a = []; for (var p in this) a.push(p); a"); + QCOMPARE(ret.toString(), QString::fromLatin1("bar,baz,oof,p,a")); + } +} +#endif + +#if 0 // ###FIXME: no c-style callbacks +static QScriptValue getSetFoo(QScriptContext *ctx, QScriptEngine *) +{ + if (ctx->argumentCount() > 0) + ctx->thisObject().setProperty("foo", ctx->argument(0)); + return ctx->thisObject().property("foo"); +} +#endif + +void tst_QJSEngine::globalObjectProperties() +{ + // See ECMA-262 Section 15.1, "The Global Object". + + QJSEngine eng; + QJSValue global = eng.globalObject(); + + QVERIFY(global.property("NaN").isNumber()); + QVERIFY(qIsNaN(global.property("NaN").toNumber())); + QCOMPARE(global.propertyFlags("NaN"), QJSValue::SkipInEnumeration | QJSValue::Undeletable); + + QVERIFY(global.property("Infinity").isNumber()); + QVERIFY(qIsInf(global.property("Infinity").toNumber())); + QCOMPARE(global.propertyFlags("NaN"), QJSValue::SkipInEnumeration | QJSValue::Undeletable); + + QVERIFY(global.property("undefined").isUndefined()); + QCOMPARE(global.propertyFlags("undefined"), QJSValue::SkipInEnumeration | QJSValue::Undeletable); + + QVERIFY(global.property("eval").isFunction()); + QCOMPARE(global.propertyFlags("eval"), QJSValue::SkipInEnumeration); + + QVERIFY(global.property("parseInt").isFunction()); + QCOMPARE(global.propertyFlags("parseInt"), QJSValue::SkipInEnumeration); + + QVERIFY(global.property("parseFloat").isFunction()); + QCOMPARE(global.propertyFlags("parseFloat"), QJSValue::SkipInEnumeration); + + QVERIFY(global.property("isNaN").isFunction()); + QCOMPARE(global.propertyFlags("isNaN"), QJSValue::SkipInEnumeration); + + QVERIFY(global.property("isFinite").isFunction()); + QCOMPARE(global.propertyFlags("isFinite"), QJSValue::SkipInEnumeration); + + QVERIFY(global.property("decodeURI").isFunction()); + QCOMPARE(global.propertyFlags("decodeURI"), QJSValue::SkipInEnumeration); + + QVERIFY(global.property("decodeURIComponent").isFunction()); + QCOMPARE(global.propertyFlags("decodeURIComponent"), QJSValue::SkipInEnumeration); + + QVERIFY(global.property("encodeURI").isFunction()); + QCOMPARE(global.propertyFlags("encodeURI"), QJSValue::SkipInEnumeration); + + QVERIFY(global.property("encodeURIComponent").isFunction()); + QCOMPARE(global.propertyFlags("encodeURIComponent"), QJSValue::SkipInEnumeration); + + QVERIFY(global.property("Object").isFunction()); + QCOMPARE(global.propertyFlags("Object"), QJSValue::SkipInEnumeration); + QVERIFY(global.property("Function").isFunction()); + QCOMPARE(global.propertyFlags("Function"), QJSValue::SkipInEnumeration); + QVERIFY(global.property("Array").isFunction()); + QCOMPARE(global.propertyFlags("Array"), QJSValue::SkipInEnumeration); + QVERIFY(global.property("String").isFunction()); + QCOMPARE(global.propertyFlags("String"), QJSValue::SkipInEnumeration); + QVERIFY(global.property("Boolean").isFunction()); + QCOMPARE(global.propertyFlags("Boolean"), QJSValue::SkipInEnumeration); + QVERIFY(global.property("Number").isFunction()); + QCOMPARE(global.propertyFlags("Number"), QJSValue::SkipInEnumeration); + QVERIFY(global.property("Date").isFunction()); + QCOMPARE(global.propertyFlags("Date"), QJSValue::SkipInEnumeration); + QVERIFY(global.property("RegExp").isFunction()); + QCOMPARE(global.propertyFlags("RegExp"), QJSValue::SkipInEnumeration); + QVERIFY(global.property("Error").isFunction()); + QCOMPARE(global.propertyFlags("Error"), QJSValue::SkipInEnumeration); + QVERIFY(global.property("EvalError").isFunction()); + QCOMPARE(global.propertyFlags("EvalError"), QJSValue::SkipInEnumeration); + QVERIFY(global.property("RangeError").isFunction()); + QCOMPARE(global.propertyFlags("RangeError"), QJSValue::SkipInEnumeration); + QVERIFY(global.property("ReferenceError").isFunction()); + QCOMPARE(global.propertyFlags("ReferenceError"), QJSValue::SkipInEnumeration); + QVERIFY(global.property("SyntaxError").isFunction()); + QCOMPARE(global.propertyFlags("SyntaxError"), QJSValue::SkipInEnumeration); + QVERIFY(global.property("TypeError").isFunction()); + QCOMPARE(global.propertyFlags("TypeError"), QJSValue::SkipInEnumeration); + QVERIFY(global.property("URIError").isFunction()); + QCOMPARE(global.propertyFlags("URIError"), QJSValue::SkipInEnumeration); + QVERIFY(global.property("Math").isObject()); + QVERIFY(!global.property("Math").isFunction()); + QCOMPARE(global.propertyFlags("Math"), QJSValue::SkipInEnumeration); +} + +void tst_QJSEngine::globalObjectEquals() +{ + QJSEngine eng; + QJSValue o = eng.globalObject(); + QVERIFY(o.strictlyEquals(eng.globalObject())); + QVERIFY(o.equals(eng.globalObject())); +} + +#if 0 // ###FIXME: No QScriptValueIterator API +void tst_QJSEngine::globalObjectProperties_enumerate() +{ + QScriptEngine eng; + QScriptValue global = eng.globalObject(); + + QSet expectedNames; + expectedNames + << "isNaN" + << "parseFloat" + << "String" + << "EvalError" + << "URIError" + << "Math" + << "encodeURIComponent" + << "RangeError" + << "eval" + << "isFinite" + << "ReferenceError" + << "Infinity" + << "Function" + << "RegExp" + << "Number" + << "parseInt" + << "Object" + << "decodeURI" + << "TypeError" + << "Boolean" + << "encodeURI" + << "NaN" + << "Error" + << "decodeURIComponent" + << "Date" + << "Array" + << "escape" + << "unescape" + << "SyntaxError" + << "undefined" + // non-standard + << "gc" + << "version" + << "print" + // JavaScriptCore + << "JSON" + // V8 + << "execScript" //execScript for IE compatibility. + ; + QSet actualNames; + { + QScriptValueIterator it(global); + while (it.hasNext()) { + it.next(); + actualNames.insert(it.name()); + } + } + + QSet remainingNames = actualNames; + { + QSet::const_iterator it; + for (it = expectedNames.constBegin(); it != expectedNames.constEnd(); ++it) { + QString name = *it; + QVERIFY(actualNames.contains(name)); + remainingNames.remove(name); + } + } + QVERIFY(remainingNames.isEmpty()); +} +#endif + +void tst_QJSEngine::createGlobalObjectProperty() +{ + QJSEngine eng; + QJSValue global = eng.globalObject(); + // create property with no attributes + { + QString name = QString::fromLatin1("foo"); + QVERIFY(!global.property(name).isValid()); + QJSValue val(123); + global.setProperty(name, val); + QVERIFY(global.property(name).equals(val)); + QVERIFY(global.propertyFlags(name) == 0); + global.setProperty(name, QJSValue()); + QVERIFY(!global.property(name).isValid()); + } + // create property with attributes +#if 0 // ###FIXME: setProperty with flags is not supported + { + QString name = QString::fromLatin1("bar"); + QVERIFY(!global.property(name).isValid()); + QScriptValue val(QString::fromLatin1("ciao")); + QScriptValue::PropertyFlags flags = QScriptValue::ReadOnly | QScriptValue::SkipInEnumeration; + global.setProperty(name, val, flags); + QVERIFY(global.property(name).equals(val)); + //QEXPECT_FAIL("", "QTBUG-6134: custom Global Object properties don't retain attributes", Continue); + QCOMPARE(global.propertyFlags(name), flags); + global.setProperty(name, QScriptValue()); + QVERIFY(!global.property(name).isValid()); + } +#endif +} + +void tst_QJSEngine::globalObjectGetterSetterProperty() +{ +#if 0 // ###FIXME: No c-style callbacks + QScriptEngine engine; + QScriptValue global = engine.globalObject(); + global.setProperty("bar", engine.newFunction(getSetFoo), + QScriptValue::PropertySetter | QScriptValue::PropertyGetter); + global.setProperty("foo", 123); + QVERIFY(global.property("bar").equals(global.property("foo"))); + QVERIFY(engine.evaluate("bar").equals(global.property("foo"))); + global.setProperty("bar", 456); + QVERIFY(global.property("bar").equals(global.property("foo"))); + + engine.evaluate("__defineGetter__('baz', function() { return 789; })"); + QVERIFY(engine.evaluate("baz").equals(789)); + QVERIFY(global.property("baz").equals(789)); +#endif +} + +#if 0 // ###FIXME: No support for setting the global object +void tst_QJSEngine::customGlobalObjectWithPrototype() +{ + for (int x = 0; x < 2; ++x) { + QScriptEngine engine; + QScriptValue wrap = engine.newObject(); + QScriptValue global = engine.globalObject(); + QScriptValue originalGlobalProto = global.prototype(); + if (!x) { + // Set prototype before setting global object + wrap.setPrototype(global); + QVERIFY(wrap.prototype().strictlyEquals(global)); + engine.setGlobalObject(wrap); + } else { + // Set prototype after setting global object + engine.setGlobalObject(wrap); + wrap.setPrototype(global); + QVERIFY(wrap.prototype().strictlyEquals(global)); + } + { + QScriptValue ret = engine.evaluate("print"); + QVERIFY(ret.isFunction()); + QVERIFY(ret.strictlyEquals(wrap.property("print"))); + } + { + QScriptValue ret = engine.evaluate("this.print"); + QVERIFY(ret.isFunction()); + QVERIFY(ret.strictlyEquals(wrap.property("print"))); + } + { + QScriptValue ret = engine.evaluate("hasOwnProperty('print')"); + QVERIFY(ret.isBool()); + if (x) QEXPECT_FAIL("", "Why?", Continue); + QVERIFY(!ret.toBool()); + } + { + QScriptValue ret = engine.evaluate("this.hasOwnProperty('print')"); + QVERIFY(ret.isBool()); + if (x) QEXPECT_FAIL("", "Why?", Continue); + QVERIFY(!ret.toBool()); + } + + QScriptValue anotherProto = engine.newObject(); + anotherProto.setProperty("anotherProtoProperty", 123); + global.setPrototype(anotherProto); + { + QScriptValue ret = engine.evaluate("print"); + QVERIFY(ret.isFunction()); + QVERIFY(ret.strictlyEquals(wrap.property("print"))); + } + { + QScriptValue ret = engine.evaluate("anotherProtoProperty"); + QVERIFY(ret.isNumber()); + QVERIFY(ret.strictlyEquals(wrap.property("anotherProtoProperty"))); + } + { + QScriptValue ret = engine.evaluate("this.anotherProtoProperty"); + QVERIFY(ret.isNumber()); + QVERIFY(ret.strictlyEquals(wrap.property("anotherProtoProperty"))); + } + + wrap.setPrototype(anotherProto); + { + QScriptValue ret = engine.evaluate("print"); + QVERIFY(ret.isError()); + QCOMPARE(ret.toString(), QString::fromLatin1("ReferenceError: print is not defined")); + } + { + QScriptValue ret = engine.evaluate("anotherProtoProperty"); + QVERIFY(ret.isNumber()); + QVERIFY(ret.strictlyEquals(wrap.property("anotherProtoProperty"))); + } + QVERIFY(global.prototype().strictlyEquals(anotherProto)); + + global.setPrototype(originalGlobalProto); + engine.setGlobalObject(global); + { + QScriptValue ret = engine.evaluate("anotherProtoProperty"); + QVERIFY(ret.isError()); + QVERIFY(ret.toString().startsWith("ReferenceError: ")); + } + { + QScriptValue ret = engine.evaluate("print"); + QVERIFY(ret.isFunction()); + QVERIFY(ret.strictlyEquals(global.property("print"))); + } + QVERIFY(!anotherProto.property("print").isValid()); + } +} +#endif + +void tst_QJSEngine::globalObjectWithCustomPrototype() +{ + QJSEngine engine; + QJSValue proto = engine.newObject(); + proto.setProperty("protoProperty", 123); + QJSValue global = engine.globalObject(); + QJSValue originalProto = global.prototype(); + global.setPrototype(proto); + { + QJSValue ret = engine.evaluate("protoProperty"); + QEXPECT_FAIL("", "Replacing the prototype of the global object is currently unsupported (see also v8 issue 1078)", Abort); + QVERIFY(ret.isNumber()); + QVERIFY(ret.strictlyEquals(global.property("protoProperty"))); + } + { + QJSValue ret = engine.evaluate("this.protoProperty"); + QVERIFY(ret.isNumber()); + QVERIFY(ret.strictlyEquals(global.property("protoProperty"))); + } + { + QJSValue ret = engine.evaluate("hasOwnProperty('protoProperty')"); + QVERIFY(ret.isBool()); + QVERIFY(!ret.toBool()); + } + { + QJSValue ret = engine.evaluate("this.hasOwnProperty('protoProperty')"); + QVERIFY(ret.isBool()); + QVERIFY(!ret.toBool()); + } + + // Custom prototype set from JS + { + QJSValue ret = engine.evaluate("this.__proto__ = { 'a': 123 }; a"); + QVERIFY(ret.isNumber()); + QVERIFY(ret.strictlyEquals(global.property("a"))); + } +} + +void tst_QJSEngine::builtinFunctionNames_data() +{ + QTest::addColumn("expression"); + QTest::addColumn("expectedName"); + + // See ECMA-262 Chapter 15, "Standard Built-in ECMAScript Objects". + + QTest::newRow("parseInt") << QString("parseInt") << QString("parseInt"); + QTest::newRow("parseFloat") << QString("parseFloat") << QString("parseFloat"); + QTest::newRow("isNaN") << QString("isNaN") << QString("isNaN"); + QTest::newRow("isFinite") << QString("isFinite") << QString("isFinite"); + QTest::newRow("decodeURI") << QString("decodeURI") << QString("decodeURI"); + QTest::newRow("decodeURIComponent") << QString("decodeURIComponent") << QString("decodeURIComponent"); + QTest::newRow("encodeURI") << QString("encodeURI") << QString("encodeURI"); + QTest::newRow("encodeURIComponent") << QString("encodeURIComponent") << QString("encodeURIComponent"); + QTest::newRow("escape") << QString("escape") << QString("escape"); + QTest::newRow("unescape") << QString("unescape") << QString("unescape"); + + QTest::newRow("Array") << QString("Array") << QString("Array"); + QTest::newRow("Array.prototype.toString") << QString("Array.prototype.toString") << QString("toString"); + QTest::newRow("Array.prototype.toLocaleString") << QString("Array.prototype.toLocaleString") << QString("toLocaleString"); + QTest::newRow("Array.prototype.concat") << QString("Array.prototype.concat") << QString("concat"); + QTest::newRow("Array.prototype.join") << QString("Array.prototype.join") << QString("join"); + QTest::newRow("Array.prototype.pop") << QString("Array.prototype.pop") << QString("pop"); + QTest::newRow("Array.prototype.push") << QString("Array.prototype.push") << QString("push"); + QTest::newRow("Array.prototype.reverse") << QString("Array.prototype.reverse") << QString("reverse"); + QTest::newRow("Array.prototype.shift") << QString("Array.prototype.shift") << QString("shift"); + QTest::newRow("Array.prototype.slice") << QString("Array.prototype.slice") << QString("slice"); + QTest::newRow("Array.prototype.sort") << QString("Array.prototype.sort") << QString("sort"); + QTest::newRow("Array.prototype.splice") << QString("Array.prototype.splice") << QString("splice"); + QTest::newRow("Array.prototype.unshift") << QString("Array.prototype.unshift") << QString("unshift"); + + QTest::newRow("Boolean") << QString("Boolean") << QString("Boolean"); + QTest::newRow("Boolean.prototype.toString") << QString("Boolean.prototype.toString") << QString("toString"); + + QTest::newRow("Date") << QString("Date") << QString("Date"); + QTest::newRow("Date.prototype.toString") << QString("Date.prototype.toString") << QString("toString"); + QTest::newRow("Date.prototype.toDateString") << QString("Date.prototype.toDateString") << QString("toDateString"); + QTest::newRow("Date.prototype.toTimeString") << QString("Date.prototype.toTimeString") << QString("toTimeString"); + QTest::newRow("Date.prototype.toLocaleString") << QString("Date.prototype.toLocaleString") << QString("toLocaleString"); + QTest::newRow("Date.prototype.toLocaleDateString") << QString("Date.prototype.toLocaleDateString") << QString("toLocaleDateString"); + QTest::newRow("Date.prototype.toLocaleTimeString") << QString("Date.prototype.toLocaleTimeString") << QString("toLocaleTimeString"); + QTest::newRow("Date.prototype.valueOf") << QString("Date.prototype.valueOf") << QString("valueOf"); + QTest::newRow("Date.prototype.getTime") << QString("Date.prototype.getTime") << QString("getTime"); + QTest::newRow("Date.prototype.getYear") << QString("Date.prototype.getYear") << QString("getYear"); + QTest::newRow("Date.prototype.getFullYear") << QString("Date.prototype.getFullYear") << QString("getFullYear"); + QTest::newRow("Date.prototype.getUTCFullYear") << QString("Date.prototype.getUTCFullYear") << QString("getUTCFullYear"); + QTest::newRow("Date.prototype.getMonth") << QString("Date.prototype.getMonth") << QString("getMonth"); + QTest::newRow("Date.prototype.getUTCMonth") << QString("Date.prototype.getUTCMonth") << QString("getUTCMonth"); + QTest::newRow("Date.prototype.getDate") << QString("Date.prototype.getDate") << QString("getDate"); + QTest::newRow("Date.prototype.getUTCDate") << QString("Date.prototype.getUTCDate") << QString("getUTCDate"); + QTest::newRow("Date.prototype.getDay") << QString("Date.prototype.getDay") << QString("getDay"); + QTest::newRow("Date.prototype.getUTCDay") << QString("Date.prototype.getUTCDay") << QString("getUTCDay"); + QTest::newRow("Date.prototype.getHours") << QString("Date.prototype.getHours") << QString("getHours"); + QTest::newRow("Date.prototype.getUTCHours") << QString("Date.prototype.getUTCHours") << QString("getUTCHours"); + QTest::newRow("Date.prototype.getMinutes") << QString("Date.prototype.getMinutes") << QString("getMinutes"); + QTest::newRow("Date.prototype.getUTCMinutes") << QString("Date.prototype.getUTCMinutes") << QString("getUTCMinutes"); + QTest::newRow("Date.prototype.getSeconds") << QString("Date.prototype.getSeconds") << QString("getSeconds"); + QTest::newRow("Date.prototype.getUTCSeconds") << QString("Date.prototype.getUTCSeconds") << QString("getUTCSeconds"); + QTest::newRow("Date.prototype.getMilliseconds") << QString("Date.prototype.getMilliseconds") << QString("getMilliseconds"); + QTest::newRow("Date.prototype.getUTCMilliseconds") << QString("Date.prototype.getUTCMilliseconds") << QString("getUTCMilliseconds"); + QTest::newRow("Date.prototype.getTimezoneOffset") << QString("Date.prototype.getTimezoneOffset") << QString("getTimezoneOffset"); + QTest::newRow("Date.prototype.setTime") << QString("Date.prototype.setTime") << QString("setTime"); + QTest::newRow("Date.prototype.setMilliseconds") << QString("Date.prototype.setMilliseconds") << QString("setMilliseconds"); + QTest::newRow("Date.prototype.setUTCMilliseconds") << QString("Date.prototype.setUTCMilliseconds") << QString("setUTCMilliseconds"); + QTest::newRow("Date.prototype.setSeconds") << QString("Date.prototype.setSeconds") << QString("setSeconds"); + QTest::newRow("Date.prototype.setUTCSeconds") << QString("Date.prototype.setUTCSeconds") << QString("setUTCSeconds"); + QTest::newRow("Date.prototype.setMinutes") << QString("Date.prototype.setMinutes") << QString("setMinutes"); + QTest::newRow("Date.prototype.setUTCMinutes") << QString("Date.prototype.setUTCMinutes") << QString("setUTCMinutes"); + QTest::newRow("Date.prototype.setHours") << QString("Date.prototype.setHours") << QString("setHours"); + QTest::newRow("Date.prototype.setUTCHours") << QString("Date.prototype.setUTCHours") << QString("setUTCHours"); + QTest::newRow("Date.prototype.setDate") << QString("Date.prototype.setDate") << QString("setDate"); + QTest::newRow("Date.prototype.setUTCDate") << QString("Date.prototype.setUTCDate") << QString("setUTCDate"); + QTest::newRow("Date.prototype.setMonth") << QString("Date.prototype.setMonth") << QString("setMonth"); + QTest::newRow("Date.prototype.setUTCMonth") << QString("Date.prototype.setUTCMonth") << QString("setUTCMonth"); + QTest::newRow("Date.prototype.setYear") << QString("Date.prototype.setYear") << QString("setYear"); + QTest::newRow("Date.prototype.setFullYear") << QString("Date.prototype.setFullYear") << QString("setFullYear"); + QTest::newRow("Date.prototype.setUTCFullYear") << QString("Date.prototype.setUTCFullYear") << QString("setUTCFullYear"); + QTest::newRow("Date.prototype.toUTCString") << QString("Date.prototype.toUTCString") << QString("toUTCString"); + QTest::newRow("Date.prototype.toGMTString") << QString("Date.prototype.toGMTString") << QString("toGMTString"); + + QTest::newRow("Error") << QString("Error") << QString("Error"); +// QTest::newRow("Error.prototype.backtrace") << QString("Error.prototype.backtrace") << QString("backtrace"); + QTest::newRow("Error.prototype.toString") << QString("Error.prototype.toString") << QString("toString"); + + QTest::newRow("EvalError") << QString("EvalError") << QString("EvalError"); + QTest::newRow("RangeError") << QString("RangeError") << QString("RangeError"); + QTest::newRow("ReferenceError") << QString("ReferenceError") << QString("ReferenceError"); + QTest::newRow("SyntaxError") << QString("SyntaxError") << QString("SyntaxError"); + QTest::newRow("TypeError") << QString("TypeError") << QString("TypeError"); + QTest::newRow("URIError") << QString("URIError") << QString("URIError"); + + QTest::newRow("Function") << QString("Function") << QString("Function"); + QTest::newRow("Function.prototype.toString") << QString("Function.prototype.toString") << QString("toString"); + QTest::newRow("Function.prototype.apply") << QString("Function.prototype.apply") << QString("apply"); + QTest::newRow("Function.prototype.call") << QString("Function.prototype.call") << QString("call"); +/* In V8, those function are only there for signals + QTest::newRow("Function.prototype.connect") << QString("Function.prototype.connect") << QString("connect"); + QTest::newRow("Function.prototype.disconnect") << QString("Function.prototype.disconnect") << QString("disconnect");*/ + + QTest::newRow("Math.abs") << QString("Math.abs") << QString("abs"); + QTest::newRow("Math.acos") << QString("Math.acos") << QString("acos"); + QTest::newRow("Math.asin") << QString("Math.asin") << QString("asin"); + QTest::newRow("Math.atan") << QString("Math.atan") << QString("atan"); + QTest::newRow("Math.atan2") << QString("Math.atan2") << QString("atan2"); + QTest::newRow("Math.ceil") << QString("Math.ceil") << QString("ceil"); + QTest::newRow("Math.cos") << QString("Math.cos") << QString("cos"); + QTest::newRow("Math.exp") << QString("Math.exp") << QString("exp"); + QTest::newRow("Math.floor") << QString("Math.floor") << QString("floor"); + QTest::newRow("Math.log") << QString("Math.log") << QString("log"); + QTest::newRow("Math.max") << QString("Math.max") << QString("max"); + QTest::newRow("Math.min") << QString("Math.min") << QString("min"); + QTest::newRow("Math.pow") << QString("Math.pow") << QString("pow"); + QTest::newRow("Math.random") << QString("Math.random") << QString("random"); + QTest::newRow("Math.round") << QString("Math.round") << QString("round"); + QTest::newRow("Math.sin") << QString("Math.sin") << QString("sin"); + QTest::newRow("Math.sqrt") << QString("Math.sqrt") << QString("sqrt"); + QTest::newRow("Math.tan") << QString("Math.tan") << QString("tan"); + + QTest::newRow("Number") << QString("Number") << QString("Number"); + QTest::newRow("Number.prototype.toString") << QString("Number.prototype.toString") << QString("toString"); + QTest::newRow("Number.prototype.toLocaleString") << QString("Number.prototype.toLocaleString") << QString("toLocaleString"); + QTest::newRow("Number.prototype.valueOf") << QString("Number.prototype.valueOf") << QString("valueOf"); + QTest::newRow("Number.prototype.toFixed") << QString("Number.prototype.toFixed") << QString("toFixed"); + QTest::newRow("Number.prototype.toExponential") << QString("Number.prototype.toExponential") << QString("toExponential"); + QTest::newRow("Number.prototype.toPrecision") << QString("Number.prototype.toPrecision") << QString("toPrecision"); + + QTest::newRow("Object") << QString("Object") << QString("Object"); + QTest::newRow("Object.prototype.toString") << QString("Object.prototype.toString") << QString("toString"); + QTest::newRow("Object.prototype.toLocaleString") << QString("Object.prototype.toLocaleString") << QString("toLocaleString"); + QTest::newRow("Object.prototype.valueOf") << QString("Object.prototype.valueOf") << QString("valueOf"); + QTest::newRow("Object.prototype.hasOwnProperty") << QString("Object.prototype.hasOwnProperty") << QString("hasOwnProperty"); + QTest::newRow("Object.prototype.isPrototypeOf") << QString("Object.prototype.isPrototypeOf") << QString("isPrototypeOf"); + QTest::newRow("Object.prototype.propertyIsEnumerable") << QString("Object.prototype.propertyIsEnumerable") << QString("propertyIsEnumerable"); + QTest::newRow("Object.prototype.__defineGetter__") << QString("Object.prototype.__defineGetter__") << QString("__defineGetter__"); + QTest::newRow("Object.prototype.__defineSetter__") << QString("Object.prototype.__defineSetter__") << QString("__defineSetter__"); + + QTest::newRow("RegExp") << QString("RegExp") << QString("RegExp"); + QTest::newRow("RegExp.prototype.exec") << QString("RegExp.prototype.exec") << QString("exec"); + QTest::newRow("RegExp.prototype.test") << QString("RegExp.prototype.test") << QString("test"); + QTest::newRow("RegExp.prototype.toString") << QString("RegExp.prototype.toString") << QString("toString"); + + QTest::newRow("String") << QString("String") << QString("String"); + QTest::newRow("String.prototype.toString") << QString("String.prototype.toString") << QString("toString"); + QTest::newRow("String.prototype.valueOf") << QString("String.prototype.valueOf") << QString("valueOf"); + QTest::newRow("String.prototype.charAt") << QString("String.prototype.charAt") << QString("charAt"); + QTest::newRow("String.prototype.charCodeAt") << QString("String.prototype.charCodeAt") << QString("charCodeAt"); + QTest::newRow("String.prototype.concat") << QString("String.prototype.concat") << QString("concat"); + QTest::newRow("String.prototype.indexOf") << QString("String.prototype.indexOf") << QString("indexOf"); + QTest::newRow("String.prototype.lastIndexOf") << QString("String.prototype.lastIndexOf") << QString("lastIndexOf"); + QTest::newRow("String.prototype.localeCompare") << QString("String.prototype.localeCompare") << QString("localeCompare"); + QTest::newRow("String.prototype.match") << QString("String.prototype.match") << QString("match"); + QTest::newRow("String.prototype.replace") << QString("String.prototype.replace") << QString("replace"); + QTest::newRow("String.prototype.search") << QString("String.prototype.search") << QString("search"); + QTest::newRow("String.prototype.slice") << QString("String.prototype.slice") << QString("slice"); + QTest::newRow("String.prototype.split") << QString("String.prototype.split") << QString("split"); + QTest::newRow("String.prototype.substring") << QString("String.prototype.substring") << QString("substring"); + QTest::newRow("String.prototype.toLowerCase") << QString("String.prototype.toLowerCase") << QString("toLowerCase"); + QTest::newRow("String.prototype.toLocaleLowerCase") << QString("String.prototype.toLocaleLowerCase") << QString("toLocaleLowerCase"); + QTest::newRow("String.prototype.toUpperCase") << QString("String.prototype.toUpperCase") << QString("toUpperCase"); + QTest::newRow("String.prototype.toLocaleUpperCase") << QString("String.prototype.toLocaleUpperCase") << QString("toLocaleUpperCase"); +} + +void tst_QJSEngine::builtinFunctionNames() +{ + QFETCH(QString, expression); + QFETCH(QString, expectedName); + QJSEngine eng; + // The "name" property is actually non-standard, but JSC supports it. + QJSValue ret = eng.evaluate(QString::fromLatin1("%0.name").arg(expression)); + QVERIFY(ret.isString()); + QCOMPARE(ret.toString(), expectedName); +} + +#if 0 // ###FIXME: No syntax checking result +void tst_QJSEngine::checkSyntax_data() +{ + QTest::addColumn("code"); + QTest::addColumn("expectedState"); + QTest::addColumn("errorLineNumber"); + QTest::addColumn("errorColumnNumber"); + QTest::addColumn("errorMessage"); + + QTest::newRow("0") + << QString("0") << int(QScriptSyntaxCheckResult::Valid) + << -1 << -1 << ""; + QTest::newRow("if (") + << QString("if (\n") << int(QScriptSyntaxCheckResult::Intermediate) + << 0 << -1 << "Uncaught SyntaxError: Unexpected end of input"; + QTest::newRow("if else") + << QString("\nif else") << int(QScriptSyntaxCheckResult::Error) + << 2 << 3 << "Uncaught SyntaxError: Unexpected token else"; + QTest::newRow("foo[") + << QString("foo[") << int(QScriptSyntaxCheckResult::Intermediate) + << 1 << 4 << "Uncaught SyntaxError: Unexpected end of input"; + QTest::newRow("foo['bar']") + << QString("foo['bar']") << int(QScriptSyntaxCheckResult::Valid) + << -1 << -1 << ""; + + QTest::newRow("/*") + << QString("/*") << int(QScriptSyntaxCheckResult::Error) + << 1 << 0 << "Uncaught SyntaxError: Unexpected token ILLEGAL"; + QTest::newRow("/*\nMy comment") + << QString("/*\nMy comment") << int(QScriptSyntaxCheckResult::Error) + << 1 << 0 << "Uncaught SyntaxError: Unexpected token ILLEGAL"; + QTest::newRow("/*\nMy comment */\nfoo = 10") + << QString("/*\nMy comment */\nfoo = 10") << int(QScriptSyntaxCheckResult::Valid) + << -1 << -1 << ""; + QTest::newRow("foo = 10 /*") + << QString("foo = 10 /*") << int(QScriptSyntaxCheckResult::Error) + << 1 << 9 << "Uncaught SyntaxError: Unexpected token ILLEGAL"; + QTest::newRow("foo = 10; /*") + << QString("foo = 10; /*") << int(QScriptSyntaxCheckResult::Error) + << 1 << 10 << "Uncaught SyntaxError: Unexpected token ILLEGAL"; + QTest::newRow("foo = 10 /* My comment */") + << QString("foo = 10 /* My comment */") << int(QScriptSyntaxCheckResult::Valid) + << -1 << -1 << ""; + + QTest::newRow("/=/") + << QString("/=/") << int(QScriptSyntaxCheckResult::Valid) << -1 << -1 << ""; + QTest::newRow("/=/g") + << QString("/=/g") << int(QScriptSyntaxCheckResult::Valid) << -1 << -1 << ""; + QTest::newRow("/a/") + << QString("/a/") << int(QScriptSyntaxCheckResult::Valid) << -1 << -1 << ""; + QTest::newRow("/a/g") + << QString("/a/g") << int(QScriptSyntaxCheckResult::Valid) << -1 << -1 << ""; +} + +void tst_QJSEngine::checkSyntax() +{ + QFETCH(QString, code); + QFETCH(int, expectedState); + QFETCH(int, errorLineNumber); + QFETCH(int, errorColumnNumber); + QFETCH(QString, errorMessage); + + QScriptSyntaxCheckResult result = QScriptEngine::checkSyntax(code); + QCOMPARE(int(result.state()), expectedState); + QCOMPARE(result.errorLineNumber(), errorLineNumber); + QCOMPARE(result.errorColumnNumber(), errorColumnNumber); + QCOMPARE(result.errorMessage(), errorMessage); + + // assignment + { + QScriptSyntaxCheckResult copy = result; + QCOMPARE(copy.state(), result.state()); + QCOMPARE(copy.errorLineNumber(), result.errorLineNumber()); + QCOMPARE(copy.errorColumnNumber(), result.errorColumnNumber()); + QCOMPARE(copy.errorMessage(), result.errorMessage()); + } + { + QScriptSyntaxCheckResult copy(result); + QCOMPARE(copy.state(), result.state()); + QCOMPARE(copy.errorLineNumber(), result.errorLineNumber()); + QCOMPARE(copy.errorColumnNumber(), result.errorColumnNumber()); + QCOMPARE(copy.errorMessage(), result.errorMessage()); + } +} +#endif + +#if 0 // ###FIXME: No support for canEvaluate +void tst_QJSEngine::canEvaluate_data() +{ + QTest::addColumn("code"); + QTest::addColumn("expectSuccess"); + + QTest::newRow("") << QString("") << true; + QTest::newRow("0") << QString("0") << true; + QTest::newRow("!") << QString("!\n") << false; + QTest::newRow("if (") << QString("if (\n") << false; + QTest::newRow("if (10) //") << QString("if (10) //\n") << false; + QTest::newRow("a = 1; if (") << QString("a = 1;\nif (\n") << false; + QTest::newRow("./test.js") << QString("./test.js\n") << true; + QTest::newRow("if (0) print(1)") << QString("if (0)\nprint(1)\n") << true; + QTest::newRow("0 = ") << QString("0 = \n") << false; + QTest::newRow("0 = 0") << QString("0 = 0\n") << true; + QTest::newRow("foo[") << QString("foo[") << false; + QTest::newRow("foo[") << QString("foo[\n") << false; + QTest::newRow("foo['bar']") << QString("foo['bar']") << true; + + //v8 does thinks unterminated comments are error rather than unfinished +// QTest::newRow("/*") << QString("/*") << false; +// QTest::newRow("/*\nMy comment") << QString("/*\nMy comment") << false; + QTest::newRow("/*\nMy comment */\nfoo = 10") << QString("/*\nMy comment */\nfoo = 10") << true; +// QTest::newRow("foo = 10 /*") << QString("foo = 10 /*") << false; +// QTest::newRow("foo = 10; /*") << QString("foo = 10; /*") << false; + QTest::newRow("foo = 10 /* My comment */") << QString("foo = 10 /* My comment */") << true; + + QTest::newRow("/=/") << QString("/=/") << true; + QTest::newRow("/=/g") << QString("/=/g") << true; + QTest::newRow("/a/") << QString("/a/") << true; + QTest::newRow("/a/g") << QString("/a/g") << true; +} + +void tst_QJSEngine::canEvaluate() +{ + QFETCH(QString, code); + QFETCH(bool, expectSuccess); + + QScriptEngine eng; + QCOMPARE(eng.canEvaluate(code), expectSuccess); +} +#endif + +void tst_QJSEngine::evaluate_data() +{ + QTest::addColumn("code"); + QTest::addColumn("lineNumber"); + QTest::addColumn("expectHadError"); + QTest::addColumn("expectErrorLineNumber"); + + QTest::newRow("(newline)") << QString("\n") << -1 << false << -1; + QTest::newRow("0 //") << QString("0 //") << -1 << false << -1; + QTest::newRow("/* */") << QString("/* */") << -1 << false << -1; + QTest::newRow("//") << QString("//") << -1 << false << -1; + QTest::newRow("(spaces)") << QString(" ") << -1 << false << -1; + QTest::newRow("(empty)") << QString("") << -1 << false << -1; + QTest::newRow("0") << QString("0") << -1 << false << -1; + QTest::newRow("0=1") << QString("\n0=1;\n") << -1 << true << 2; + QTest::newRow("a=1") << QString("a=1\n") << -1 << false << -1; + QTest::newRow("a=1;K") << QString("a=1;\nK") << -1 << true << 2; + + QTest::newRow("f()") << QString("function f()\n" + "{\n" + " var a;\n" + " var b=\";\n" // here's the error + "}\n" + "f();\n") + << -1 << true << 4; + + QTest::newRow("0") << QString("0") << 10 << false << -1; + QTest::newRow("0=1") << QString("\n\n0=1\n") << 10 << true << 12; + QTest::newRow("a=1") << QString("a=1\n") << 10 << false << -1; + QTest::newRow("a=1;K") << QString("a=1;\n\nK") << 10 << true << 12; + + QTest::newRow("f()") << QString("function f()\n" + "{\n" + " var a;\n" + "\n\n" + " var b=\";\n" // here's the error + "}\n" + "f();\n") + << 10 << true << 15; + QTest::newRow("functionThatDoesntExist()") + << QString(";\n;\n;\nfunctionThatDoesntExist()") + << -1 << true << 4; + QTest::newRow("for (var p in this) { continue labelThatDoesntExist; }") + << QString("for (var p in this) {\ncontinue labelThatDoesntExist; }") + << 4 << true << 5; + QTest::newRow("duplicateLabel: { duplicateLabel: ; }") + << QString("duplicateLabel: { duplicateLabel: ; }") + << 12 << true << 12; + + QTest::newRow("/=/") << QString("/=/") << -1 << false << -1; + QTest::newRow("/=/g") << QString("/=/g") << -1 << false << -1; + QTest::newRow("/a/") << QString("/a/") << -1 << false << -1; + QTest::newRow("/a/g") << QString("/a/g") << -1 << false << -1; + QTest::newRow("/a/gim") << QString("/a/gim") << -1 << false << -1; + QTest::newRow("/a/gimp") << QString("/a/gimp") << 1 << true << 1; +} + +void tst_QJSEngine::evaluate() +{ + QFETCH(QString, code); + QFETCH(int, lineNumber); + QFETCH(bool, expectHadError); + QFETCH(int, expectErrorLineNumber); + + QJSEngine eng; + QJSValue ret; + if (lineNumber != -1) + ret = eng.evaluate(code, /*fileName =*/QString(), lineNumber); + else + ret = eng.evaluate(code); + QEXPECT_FAIL("/a/gimp", "v8 ignore invalid flags", Abort); + QCOMPARE(eng.hasUncaughtException(), expectHadError); +#if 0 // ###FIXME: No support for the line number of an uncaught exception + QEXPECT_FAIL("f()", "SyntaxError do not report line number", Continue); + QEXPECT_FAIL("duplicateLabel: { duplicateLabel: ; }", "SyntaxError do not report line number", Continue); + QCOMPARE(eng.uncaughtExceptionLineNumber(), expectErrorLineNumber); +#endif + if (eng.hasUncaughtException() && ret.isError()) { + QEXPECT_FAIL("", "we have no more lineNumber property ", Continue); + QVERIFY(ret.property("lineNumber").strictlyEquals(QJSValue(&eng, expectErrorLineNumber))); + } else { +#if 0 // ###FIXME: No support for the backtrace of an uncaught exception + QVERIFY(eng.uncaughtExceptionBacktrace().isEmpty()); +#endif + } +} + +#if 0 // ###FIXME: no support for c-style callbacks +static QScriptValue eval_nested(QScriptContext *ctx, QScriptEngine *eng) +{ + QScriptValue result = eng->newObject(); + eng->evaluate("var bar = 'local';"); + result.setProperty("thisObjectIdBefore", ctx->thisObject().property("id")); + QScriptValue evaluatedThisObject = eng->evaluate("this"); + result.setProperty("thisObjectIdAfter", ctx->thisObject().property("id")); + result.setProperty("evaluatedThisObjectId", evaluatedThisObject.property("id")); + result.setProperty("local_bar", eng->evaluate("bar")); + + return result; +} + +// Tests that nested evaluate uses the "this" that was passed. +void tst_QJSEngine::nestedEvaluate() +{ + QScriptEngine eng; + QScriptValue fun = eng.newFunction(eval_nested); + eng.globalObject().setProperty("fun", fun); + // From JS function call + { + QScriptValue result = eng.evaluate("o = { id:'foo'}; o.fun = fun; o.fun()"); + QCOMPARE(result.property("local_bar").toString(), QString("local")); + QCOMPARE(result.property("thisObjectIdBefore").toString(), QString("foo")); + QCOMPARE(result.property("thisObjectIdAfter").toString(), QString("foo")); + QCOMPARE(result.property("evaluatedThisObjectId").toString(), QString("foo")); + QScriptValue bar = eng.evaluate("bar"); // Was introduced in local scope. + QVERIFY(bar.isError()); + QVERIFY(bar.toString().contains(QString::fromLatin1("ReferenceError"))); + } + // From QScriptValue::call() + { + QScriptValue result = fun.call(eng.evaluate("p = { id:'foo' }") , QScriptValueList() ); + QCOMPARE(result.property("local_bar").toString(), QString("local")); + QCOMPARE(result.property("thisObjectIdBefore").toString(), QString("foo")); + QCOMPARE(result.property("thisObjectIdAfter").toString(), QString("foo")); + QCOMPARE(result.property("evaluatedThisObjectId").toString(), QString("foo")); + QScriptValue bar = eng.evaluate("bar"); + QVERIFY(bar.isError()); + QVERIFY(bar.toString().contains(QString::fromLatin1("ReferenceError"))); + } +} +#endif + +#if 0 // ### FIXME: No c-style callbacks +void tst_QJSEngine::uncaughtException() +{ + QScriptEngine eng; + QScriptValue fun = eng.newFunction(myFunction); + QScriptValue throwFun = eng.newFunction(myThrowingFunction); + for (int x = -1; x < 2; ++x) { + { + QScriptValue ret = eng.evaluate("a = 10;\nb = 20;\n0 = 0;\n", /*fileName=*/QString(), /*lineNumber=*/x); + QVERIFY(eng.hasUncaughtException()); + QCOMPARE(eng.uncaughtExceptionLineNumber(), x+2); + QVERIFY(eng.uncaughtException().strictlyEquals(ret)); + (void)ret.toString(); + QVERIFY(eng.hasUncaughtException()); + QVERIFY(eng.uncaughtException().strictlyEquals(ret)); + QVERIFY(fun.call().isNull()); + QVERIFY(eng.hasUncaughtException()); + QCOMPARE(eng.uncaughtExceptionLineNumber(), x+2); + QVERIFY(eng.uncaughtException().strictlyEquals(ret)); + eng.clearExceptions(); + QVERIFY(!eng.hasUncaughtException()); + QCOMPARE(eng.uncaughtExceptionLineNumber(), -1); + QVERIFY(!eng.uncaughtException().isValid()); + + eng.evaluate("2 = 3"); + QVERIFY(eng.hasUncaughtException()); + QScriptValue ret2 = throwFun.call(); + QVERIFY(ret2.isError()); + QVERIFY(eng.hasUncaughtException()); + QVERIFY(eng.uncaughtException().strictlyEquals(ret2)); + QCOMPARE(eng.uncaughtExceptionLineNumber(), 0); + eng.clearExceptions(); + QVERIFY(!eng.hasUncaughtException()); + eng.evaluate("1 + 2"); + QVERIFY(!eng.hasUncaughtException()); + } + { + QScriptValue ret = eng.evaluate("a = 10"); + QVERIFY(!eng.hasUncaughtException()); + QVERIFY(!eng.uncaughtException().isValid()); + } + { + QScriptValue ret = eng.evaluate("1 = 2"); + QVERIFY(eng.hasUncaughtException()); + eng.clearExceptions(); + QVERIFY(!eng.hasUncaughtException()); + } + { + eng.globalObject().setProperty("throwFun", throwFun); + eng.evaluate("1;\nthrowFun();"); + QVERIFY(eng.hasUncaughtException()); + QCOMPARE(eng.uncaughtExceptionLineNumber(), 2); + eng.clearExceptions(); + QVERIFY(!eng.hasUncaughtException()); + } + } +} +#endif + +void tst_QJSEngine::errorMessage_QT679() +{ + QJSEngine engine; + engine.globalObject().setProperty("foo", 15); + QJSValue error = engine.evaluate("'hello world';\nfoo.bar.blah"); + QVERIFY(error.isError()); + QVERIFY(error.toString().startsWith(QString::fromLatin1("TypeError: "))); +} + +struct Foo { +public: + int x, y; + Foo() : x(-1), y(-1) { } +}; + +Q_DECLARE_METATYPE(Foo) +Q_DECLARE_METATYPE(Foo*) + +#if 0 // FIXME: No prototype API in QScriptEngine +void tst_QJSEngine::getSetDefaultPrototype_int() +{ + QScriptEngine eng; + + QScriptValue object = eng.newObject(); + QCOMPARE(eng.defaultPrototype(qMetaTypeId()).isValid(), false); + eng.setDefaultPrototype(qMetaTypeId(), object); + QCOMPARE(eng.defaultPrototype(qMetaTypeId()).strictlyEquals(object), true); + QScriptValue value = eng.newVariant(int(123)); + QCOMPARE(value.prototype().isObject(), true); + QCOMPARE(value.prototype().strictlyEquals(object), true); + + eng.setDefaultPrototype(qMetaTypeId(), QScriptValue()); + QCOMPARE(eng.defaultPrototype(qMetaTypeId()).isValid(), false); + QScriptValue value2 = eng.newVariant(int(123)); + QCOMPARE(value2.prototype().strictlyEquals(object), false); +} + +void tst_QJSEngine::getSetDefaultPrototype_customType() +{ + QScriptEngine eng; + + QScriptValue object = eng.newObject(); + QCOMPARE(eng.defaultPrototype(qMetaTypeId()).isValid(), false); + eng.setDefaultPrototype(qMetaTypeId(), object); + QCOMPARE(eng.defaultPrototype(qMetaTypeId()).strictlyEquals(object), true); + QScriptValue value = eng.newVariant(qVariantFromValue(Foo())); + QCOMPARE(value.prototype().isObject(), true); + QCOMPARE(value.prototype().strictlyEquals(object), true); + + eng.setDefaultPrototype(qMetaTypeId(), QScriptValue()); + QCOMPARE(eng.defaultPrototype(qMetaTypeId()).isValid(), false); + QScriptValue value2 = eng.newVariant(qVariantFromValue(Foo())); + QCOMPARE(value2.prototype().strictlyEquals(object), false); +} +#endif + +static QJSValue fooToScriptValue(QJSEngine *eng, const Foo &foo) +{ + QJSValue obj = eng->newObject(); + obj.setProperty("x", QJSValue(eng, foo.x)); + obj.setProperty("y", QJSValue(eng, foo.y)); + return obj; +} + +static void fooFromScriptValue(const QJSValue &value, Foo &foo) +{ + foo.x = value.property("x").toInt32(); + foo.y = value.property("y").toInt32(); +} + +static QJSValue fooToScriptValueV2(QJSEngine *eng, const Foo &foo) +{ + return QJSValue(eng, foo.x); +} + +static void fooFromScriptValueV2(const QJSValue &value, Foo &foo) +{ + foo.x = value.toInt32(); +} + +Q_DECLARE_METATYPE(QLinkedList) +Q_DECLARE_METATYPE(QList) +Q_DECLARE_METATYPE(QVector) +Q_DECLARE_METATYPE(QStack) +Q_DECLARE_METATYPE(QQueue) +Q_DECLARE_METATYPE(QLinkedList >) + +void tst_QJSEngine::valueConversion_basic() +{ + QJSEngine eng; + { + QJSValue num = eng.toScriptValue(123); + QCOMPARE(num.isNumber(), true); + QCOMPARE(num.strictlyEquals(QJSValue(&eng, 123)), true); + + int inum = eng.fromScriptValue(num); + QCOMPARE(inum, 123); + + QString snum = eng.fromScriptValue(num); + QCOMPARE(snum, QLatin1String("123")); + } + { + QJSValue num = eng.toScriptValue(123); + QCOMPARE(num.isNumber(), true); + QCOMPARE(num.strictlyEquals(QJSValue(&eng, 123)), true); + + int inum = eng.fromScriptValue(num); + QCOMPARE(inum, 123); + + QString snum = eng.fromScriptValue(num); + QCOMPARE(snum, QLatin1String("123")); + } + { + QJSValue num(&eng, 123); + QCOMPARE(eng.fromScriptValue(num), char(123)); + QCOMPARE(eng.fromScriptValue(num), (unsigned char)(123)); + QCOMPARE(eng.fromScriptValue(num), short(123)); + QCOMPARE(eng.fromScriptValue(num), (unsigned short)(123)); + QCOMPARE(eng.fromScriptValue(num), float(123)); + QCOMPARE(eng.fromScriptValue(num), double(123)); + QCOMPARE(eng.fromScriptValue(num), qlonglong(123)); + QCOMPARE(eng.fromScriptValue(num), qulonglong(123)); + } + { + QJSValue num(123); + QCOMPARE(eng.fromScriptValue(num), char(123)); + QCOMPARE(eng.fromScriptValue(num), (unsigned char)(123)); + QCOMPARE(eng.fromScriptValue(num), short(123)); + QCOMPARE(eng.fromScriptValue(num), (unsigned short)(123)); + QCOMPARE(eng.fromScriptValue(num), float(123)); + QCOMPARE(eng.fromScriptValue(num), double(123)); + QCOMPARE(eng.fromScriptValue(num), qlonglong(123)); + QCOMPARE(eng.fromScriptValue(num), qulonglong(123)); + } + + { + QJSValue num = eng.toScriptValue(Q_INT64_C(0x100000000)); + QCOMPARE(eng.fromScriptValue(num), Q_INT64_C(0x100000000)); + QCOMPARE(eng.fromScriptValue(num), Q_UINT64_C(0x100000000)); + } + + { + QChar c = QLatin1Char('c'); + QJSValue str = QJSValue(&eng, QLatin1String("ciao")); + QCOMPARE(eng.fromScriptValue(str), c); + QJSValue code = QJSValue(&eng, c.unicode()); + QCOMPARE(eng.fromScriptValue(code), c); + QCOMPARE(eng.fromScriptValue(eng.toScriptValue(c)), c); + } +} + +#if 0 // FIXME: No API for custom types +void tst_QJSEngine::valueConversion_customType() +{ + QScriptEngine eng; + { + // a type that we don't have built-in conversion of + // (it's stored as a variant) + QTime tm(1, 2, 3, 4); + QScriptValue val = eng.toScriptValue(tm); + QCOMPARE(eng.fromScriptValue(val), tm); + } + + { + Foo foo; + foo.x = 12; + foo.y = 34; + QScriptValue fooVal = eng.toScriptValue(foo); + QCOMPARE(fooVal.isVariant(), true); + + Foo foo2 = eng.fromScriptValue(fooVal); + QCOMPARE(foo2.x, foo.x); + QCOMPARE(foo2.y, foo.y); + } + + qScriptRegisterMetaType(&eng, fooToScriptValue, fooFromScriptValue); + + { + Foo foo; + foo.x = 12; + foo.y = 34; + QScriptValue fooVal = eng.toScriptValue(foo); + QCOMPARE(fooVal.isObject(), true); + QVERIFY(fooVal.prototype().strictlyEquals(eng.evaluate("Object.prototype"))); + QCOMPARE(fooVal.property("x").strictlyEquals(QScriptValue(&eng, 12)), true); + QCOMPARE(fooVal.property("y").strictlyEquals(QScriptValue(&eng, 34)), true); + fooVal.setProperty("x", QScriptValue(&eng, 56)); + fooVal.setProperty("y", QScriptValue(&eng, 78)); + + Foo foo2 = eng.fromScriptValue(fooVal); + QCOMPARE(foo2.x, 56); + QCOMPARE(foo2.y, 78); + + QScriptValue fooProto = eng.newObject(); + eng.setDefaultPrototype(qMetaTypeId(), fooProto); + QScriptValue fooVal2 = eng.toScriptValue(foo2); + QVERIFY(fooVal2.prototype().strictlyEquals(fooProto)); + QVERIFY(fooVal2.property("x").strictlyEquals(QScriptValue(&eng, 56))); + QVERIFY(fooVal2.property("y").strictlyEquals(QScriptValue(&eng, 78))); + } +} + +void tst_QJSEngine::valueConversion_sequence() +{ + QScriptEngine eng; + qScriptRegisterSequenceMetaType >(&eng); + + { + QLinkedList lst; + lst << QLatin1String("foo") << QLatin1String("bar"); + QScriptValue lstVal = eng.toScriptValue(lst); + QCOMPARE(lstVal.isArray(), true); + QCOMPARE(lstVal.property("length").toInt32(), 2); + QCOMPARE(lstVal.property("0").isString(), true); + QCOMPARE(lstVal.property("0").toString(), QLatin1String("foo")); + QCOMPARE(lstVal.property("1").isString(), true); + QCOMPARE(lstVal.property("1").toString(), QLatin1String("bar")); + } + + qScriptRegisterSequenceMetaType >(&eng); + qScriptRegisterSequenceMetaType >(&eng); + qScriptRegisterSequenceMetaType >(&eng); + qScriptRegisterSequenceMetaType >(&eng); + qScriptRegisterSequenceMetaType > >(&eng); + + { + QLinkedList > lst; + QStack first; first << 13 << 49; lst << first; + QStack second; second << 99999;lst << second; + QScriptValue lstVal = eng.toScriptValue(lst); + QCOMPARE(lstVal.isArray(), true); + QCOMPARE(lstVal.property("length").toInt32(), 2); + QCOMPARE(lstVal.property("0").isArray(), true); + QCOMPARE(lstVal.property("0").property("length").toInt32(), 2); + QCOMPARE(lstVal.property("0").property("0").toInt32(), first.at(0)); + QCOMPARE(lstVal.property("0").property("1").toInt32(), first.at(1)); + QCOMPARE(lstVal.property("1").isArray(), true); + QCOMPARE(lstVal.property("1").property("length").toInt32(), 1); + QCOMPARE(lstVal.property("1").property("0").toInt32(), second.at(0)); + QCOMPARE(qscriptvalue_cast >(lstVal.property("0")), first); + QCOMPARE(qscriptvalue_cast >(lstVal.property("1")), second); + QCOMPARE(qscriptvalue_cast > >(lstVal), lst); + } + + // pointers + { + Foo foo; + { + QScriptValue v = eng.toScriptValue(&foo); + Foo *pfoo = qscriptvalue_cast(v); + QCOMPARE(pfoo, &foo); + } + { + Foo *pfoo = 0; + QScriptValue v = eng.toScriptValue(pfoo); + QCOMPARE(v.isNull(), true); + QVERIFY(qscriptvalue_cast(v) == 0); + } + } + + // QList and QObjectList should be converted from/to arrays by default + { + QList lst; + lst << 1 << 2 << 3; + QScriptValue val = eng.toScriptValue(lst); + QVERIFY(val.isArray()); + QCOMPARE(val.property("length").toInt32(), lst.size()); + QCOMPARE(val.property(0).toInt32(), lst.at(0)); + QCOMPARE(val.property(1).toInt32(), lst.at(1)); + QCOMPARE(val.property(2).toInt32(), lst.at(2)); + + QCOMPARE(qscriptvalue_cast >(val), lst); + } + { + QObjectList lst; + lst << this; + QScriptValue val = eng.toScriptValue(lst); + QVERIFY(val.isArray()); + QCOMPARE(val.property("length").toInt32(), lst.size()); + QCOMPARE(val.property(0).toQObject(), (QObject *)this); + + QCOMPARE(qscriptvalue_cast(val), lst); + } +} +#endif + +void tst_QJSEngine::valueConversion_QVariant() +{ + QJSEngine eng; + // qScriptValueFromValue() should be "smart" when the argument is a QVariant + { + QJSValue val = eng.toScriptValue(QVariant()); + QVERIFY(!val.isVariant()); + QVERIFY(val.isUndefined()); + } + // Checking nested QVariants + { + QVariant tmp1; + QVariant tmp2(QMetaType::QVariant, &tmp1); + QVERIFY(QMetaType::Type(tmp2.type()) == QMetaType::QVariant); + + QJSValue val1 = eng.toScriptValue(tmp1); + QJSValue val2 = eng.toScriptValue(tmp2); + QVERIFY(val1.isValid()); + QVERIFY(val2.isValid()); + QVERIFY(val1.isUndefined()); + QEXPECT_FAIL("", "Variant are unrwapped, maybe we should not...", Continue); + QVERIFY(!val2.isUndefined()); + QVERIFY(!val1.isVariant()); + QEXPECT_FAIL("", "Variant are unrwapped, maybe we should not...", Continue); + QVERIFY(val2.isVariant()); + } + { + QVariant tmp1(123); + QVariant tmp2(QMetaType::QVariant, &tmp1); + QVariant tmp3(QMetaType::QVariant, &tmp2); + QVERIFY(QMetaType::Type(tmp1.type()) == QMetaType::Int); + QVERIFY(QMetaType::Type(tmp2.type()) == QMetaType::QVariant); + QVERIFY(QMetaType::Type(tmp3.type()) == QMetaType::QVariant); + + QJSValue val1 = eng.toScriptValue(tmp2); + QJSValue val2 = eng.toScriptValue(tmp3); + QVERIFY(val1.isValid()); + QVERIFY(val2.isValid()); + QEXPECT_FAIL("", "Variant are unrwapped, maybe we should not...", Continue); + QVERIFY(val1.isVariant()); + QEXPECT_FAIL("", "Variant are unrwapped, maybe we should not...", Continue); + QVERIFY(val2.isVariant()); + QVERIFY(val1.toVariant().toInt() == 123); + QVERIFY(eng.toScriptValue(val2.toVariant()).toVariant().toInt() == 123); + } + { + QJSValue val = eng.toScriptValue(QVariant(true)); + QVERIFY(!val.isVariant()); + QVERIFY(val.isBoolean()); + QCOMPARE(val.toBoolean(), true); + } + { + QJSValue val = eng.toScriptValue(QVariant(int(123))); + QVERIFY(!val.isVariant()); + QVERIFY(val.isNumber()); + QCOMPARE(val.toNumber(), qreal(123)); + } + { + QJSValue val = eng.toScriptValue(QVariant(qreal(1.25))); + QVERIFY(!val.isVariant()); + QVERIFY(val.isNumber()); + QCOMPARE(val.toNumber(), qreal(1.25)); + } + { + QString str = QString::fromLatin1("ciao"); + QJSValue val = eng.toScriptValue(QVariant(str)); + QVERIFY(!val.isVariant()); + QVERIFY(val.isString()); + QCOMPARE(val.toString(), str); + } + { + QJSValue val = eng.toScriptValue(qVariantFromValue((QObject*)this)); + QVERIFY(!val.isVariant()); + QVERIFY(val.isQObject()); + QCOMPARE(val.toQObject(), (QObject*)this); + } + { + QVariant var = qVariantFromValue(QPoint(123, 456)); + QJSValue val = eng.toScriptValue(var); + QVERIFY(val.isVariant()); + QCOMPARE(val.toVariant(), var); + } + + QCOMPARE(qjsvalue_cast(QJSValue(123)), QVariant(123)); +} + +#if 0 // FIXME: No support for custom types +void tst_QJSEngine::valueConversion_hooliganTask248802() +{ + QScriptEngine eng; + qScriptRegisterMetaType(&eng, fooToScriptValueV2, fooFromScriptValueV2); + { + QScriptValue num(&eng, 123); + Foo foo = eng.fromScriptValue(num); + QCOMPARE(foo.x, 123); + } + { + QScriptValue num(123); + Foo foo = eng.fromScriptValue(num); + QCOMPARE(foo.x, -1); + } + { + QScriptValue str(&eng, QLatin1String("123")); + Foo foo = eng.fromScriptValue(str); + QCOMPARE(foo.x, 123); + } + +} +#endif + +void tst_QJSEngine::valueConversion_basic2() +{ + QJSEngine eng; + // more built-in types + { + QJSValue val = eng.toScriptValue(uint(123)); + QVERIFY(val.isNumber()); + QCOMPARE(val.toInt32(), 123); + } + { + QJSValue val = eng.toScriptValue(qulonglong(123)); + QVERIFY(val.isNumber()); + QCOMPARE(val.toInt32(), 123); + } + { + QJSValue val = eng.toScriptValue(float(123)); + QVERIFY(val.isNumber()); + QCOMPARE(val.toInt32(), 123); + } + { + QJSValue val = eng.toScriptValue(short(123)); + QVERIFY(val.isNumber()); + QCOMPARE(val.toInt32(), 123); + } + { + QJSValue val = eng.toScriptValue(ushort(123)); + QVERIFY(val.isNumber()); + QCOMPARE(val.toInt32(), 123); + } + { + QJSValue val = eng.toScriptValue(char(123)); + QVERIFY(val.isNumber()); + QCOMPARE(val.toInt32(), 123); + } + { + QJSValue val = eng.toScriptValue(uchar(123)); + QVERIFY(val.isNumber()); + QCOMPARE(val.toInt32(), 123); + } +} + +void tst_QJSEngine::valueConversion_dateTime() +{ + QJSEngine eng; + { + QDateTime in = QDateTime::currentDateTime(); + QJSValue val = eng.toScriptValue(in); + QVERIFY(val.isDate()); + QCOMPARE(val.toDateTime(), in); + } + { + QDate in = QDate::currentDate(); + QJSValue val = eng.toScriptValue(in); + QVERIFY(val.isDate()); + QCOMPARE(val.toDateTime().date(), in); + } +} + +void tst_QJSEngine::valueConversion_regExp() +{ + QJSEngine eng; + { + QRegExp in = QRegExp("foo"); + QJSValue val = eng.toScriptValue(in); + QVERIFY(val.isRegExp()); + QRegExp out = val.toRegExp(); + QEXPECT_FAIL("", "QTBUG-6136: JSC-based back-end doesn't preserve QRegExp::patternSyntax (always uses RegExp2)", Continue); + QCOMPARE(out.patternSyntax(), in.patternSyntax()); + QCOMPARE(out.pattern(), in.pattern()); + QCOMPARE(out.caseSensitivity(), in.caseSensitivity()); + QCOMPARE(out.isMinimal(), in.isMinimal()); + } + { + QRegExp in = QRegExp("foo", Qt::CaseSensitive, QRegExp::RegExp2); + QJSValue val = eng.toScriptValue(in); + QVERIFY(val.isRegExp()); + QCOMPARE(val.toRegExp(), in); + } + { + QRegExp in = QRegExp("foo"); + in.setMinimal(true); + QJSValue val = eng.toScriptValue(in); + QVERIFY(val.isRegExp()); + QEXPECT_FAIL("", "QTBUG-6136: JSC-based back-end doesn't preserve QRegExp::minimal (always false)", Continue); + QCOMPARE(val.toRegExp().isMinimal(), in.isMinimal()); + } +} + +#if 0 // FIXME: No qScriptValueFromValue +void tst_QJSEngine::qScriptValueFromValue_noEngine() +{ + QVERIFY(!qScriptValueFromValue(0, 123).isValid()); + QVERIFY(!qScriptValueFromValue(0, QVariant(123)).isValid()); +} +#endif + +#if 0 // ###FIXME: No QScriptContext +static QScriptValue __import__(QScriptContext *ctx, QScriptEngine *eng) +{ + return eng->importExtension(ctx->argument(0).toString()); +} + +void tst_QJSEngine::importExtension() +{ + QStringList libPaths = QCoreApplication::instance()->libraryPaths(); + QCoreApplication::instance()->setLibraryPaths(QStringList() << SRCDIR); + + QStringList availableExtensions; + { + QScriptEngine eng; + QVERIFY(eng.importedExtensions().isEmpty()); + QStringList ret = eng.availableExtensions(); + QCOMPARE(ret.size(), 4); + QCOMPARE(ret.at(0), QString::fromLatin1("com")); + QCOMPARE(ret.at(1), QString::fromLatin1("com.trolltech")); + QCOMPARE(ret.at(2), QString::fromLatin1("com.trolltech.recursive")); + QCOMPARE(ret.at(3), QString::fromLatin1("com.trolltech.syntaxerror")); + availableExtensions = ret; + } + + // try to import something that doesn't exist + { + QScriptEngine eng; + QScriptValue ret = eng.importExtension("this.extension.does.not.exist"); + QCOMPARE(eng.hasUncaughtException(), true); + QCOMPARE(ret.isError(), true); + QCOMPARE(ret.toString(), QString::fromLatin1("Error: Unable to import this.extension.does.not.exist: no such extension")); + } + + { + QScriptEngine eng; + for (int x = 0; x < 2; ++x) { + QCOMPARE(eng.globalObject().property("com").isValid(), x == 1); + QScriptValue ret = eng.importExtension("com.trolltech"); + QCOMPARE(eng.hasUncaughtException(), false); + QCOMPARE(ret.isUndefined(), true); + + QScriptValue com = eng.globalObject().property("com"); + QCOMPARE(com.isObject(), true); + QCOMPARE(com.property("wasDefinedAlready") + .strictlyEquals(QScriptValue(&eng, false)), true); + QCOMPARE(com.property("name") + .strictlyEquals(QScriptValue(&eng, "com")), true); + QCOMPARE(com.property("level") + .strictlyEquals(QScriptValue(&eng, 1)), true); + QVERIFY(com.property("originalPostInit").isUndefined()); + QVERIFY(com.property("postInitCallCount").strictlyEquals(1)); + + QScriptValue trolltech = com.property("trolltech"); + QCOMPARE(trolltech.isObject(), true); + QCOMPARE(trolltech.property("wasDefinedAlready") + .strictlyEquals(QScriptValue(&eng, false)), true); + QCOMPARE(trolltech.property("name") + .strictlyEquals(QScriptValue(&eng, "com.trolltech")), true); + QCOMPARE(trolltech.property("level") + .strictlyEquals(QScriptValue(&eng, 2)), true); + QVERIFY(trolltech.property("originalPostInit").isUndefined()); + QVERIFY(trolltech.property("postInitCallCount").strictlyEquals(1)); + } + QStringList imp = eng.importedExtensions(); + QCOMPARE(imp.size(), 2); + QCOMPARE(imp.at(0), QString::fromLatin1("com")); + QCOMPARE(imp.at(1), QString::fromLatin1("com.trolltech")); + QCOMPARE(eng.availableExtensions(), availableExtensions); + } + + // recursive import should throw an error + { + QScriptEngine eng; + QVERIFY(eng.importedExtensions().isEmpty()); + eng.globalObject().setProperty("__import__", eng.newFunction(__import__)); + QScriptValue ret = eng.importExtension("com.trolltech.recursive"); + QCOMPARE(eng.hasUncaughtException(), true); + QVERIFY(ret.isError()); + QCOMPARE(ret.toString(), QString::fromLatin1("Error: recursive import of com.trolltech.recursive")); + QStringList imp = eng.importedExtensions(); + QCOMPARE(imp.size(), 2); + QCOMPARE(imp.at(0), QString::fromLatin1("com")); + QCOMPARE(imp.at(1), QString::fromLatin1("com.trolltech")); + QCOMPARE(eng.availableExtensions(), availableExtensions); + } + + { + QScriptEngine eng; + eng.globalObject().setProperty("__import__", eng.newFunction(__import__)); + for (int x = 0; x < 2; ++x) { + if (x == 0) + QVERIFY(eng.importedExtensions().isEmpty()); + QScriptValue ret = eng.importExtension("com.trolltech.syntaxerror"); + QVERIFY(eng.hasUncaughtException()); + QEXPECT_FAIL("", "JSC throws syntax error eagerly", Continue); + QCOMPARE(eng.uncaughtExceptionLineNumber(), 4); + QVERIFY(ret.isError()); + QVERIFY(ret.toString().contains(QLatin1String("SyntaxError"))); + } + QStringList imp = eng.importedExtensions(); + QCOMPARE(imp.size(), 2); + QCOMPARE(imp.at(0), QString::fromLatin1("com")); + QCOMPARE(imp.at(1), QString::fromLatin1("com.trolltech")); + QCOMPARE(eng.availableExtensions(), availableExtensions); + } + + QCoreApplication::instance()->setLibraryPaths(libPaths); +} + +static QScriptValue recurse(QScriptContext *ctx, QScriptEngine *eng) +{ + Q_UNUSED(eng); + return ctx->callee().call(); +} + +static QScriptValue recurse2(QScriptContext *ctx, QScriptEngine *eng) +{ + Q_UNUSED(eng); + return ctx->callee().construct(); +} + +void tst_QJSEngine::infiniteRecursion() +{ + // Infinite recursion in JS should cause the VM to throw an error + // when the JS stack is exhausted. + // The exact error is back-end specific and subject to change. + const QString stackOverflowError = QString::fromLatin1("RangeError: Maximum call stack size exceeded"); + QScriptEngine eng; + { + QScriptValue ret = eng.evaluate("function foo() { foo(); }; foo();"); + QCOMPARE(ret.isError(), true); + QVERIFY(ret.toString().startsWith(stackOverflowError)); + } +#if 0 //The native C++ stack overflow before the JS stack + { + QScriptValue fun = eng.newFunction(recurse); + QScriptValue ret = fun.call(); + QCOMPARE(ret.isError(), true); + QCOMPARE(ret.toString(), stackOverflowError); + } + { + QScriptValue fun = eng.newFunction(recurse2); + QScriptValue ret = fun.construct(); + QCOMPARE(ret.isError(), true); + QCOMPARE(ret.toString(), stackOverflowError); + } +#endif +} +#endif + +struct Bar { + int a; +}; + +struct Baz : public Bar { + int b; +}; + +Q_DECLARE_METATYPE(Bar*) +Q_DECLARE_METATYPE(Baz*) + +Q_DECLARE_METATYPE(QGradient) +Q_DECLARE_METATYPE(QGradient*) +Q_DECLARE_METATYPE(QLinearGradient) + +#if 0 // FIXME: No support for default prototypes +class Zoo : public QObject +{ + Q_OBJECT +public: + Zoo() { } +public slots: + Baz *toBaz(Bar *b) { return reinterpret_cast(b); } +}; + +void tst_QJSEngine::castWithPrototypeChain() +{ + QScriptEngine eng; + Bar bar; + Baz baz; + QScriptValue barProto = eng.toScriptValue(&bar); + QScriptValue bazProto = eng.toScriptValue(&baz); + eng.setDefaultPrototype(qMetaTypeId(), barProto); + eng.setDefaultPrototype(qMetaTypeId(), bazProto); + + Baz baz2; + baz2.a = 123; + baz2.b = 456; + QScriptValue baz2Value = eng.toScriptValue(&baz2); + { + // qscriptvalue_cast() does magic; if the QScriptValue contains + // t of type T, and the target type is T*, &t is returned. + Baz *pbaz = qscriptvalue_cast(baz2Value); + QVERIFY(pbaz != 0); + QCOMPARE(pbaz->b, baz2.b); + + Zoo zoo; + QScriptValue scriptZoo = eng.newQObject(&zoo); + QScriptValue toBaz = scriptZoo.property("toBaz"); + QVERIFY(toBaz.isFunction()); + + // no relation between Bar and Baz's proto --> casting fails + { + Bar *pbar = qscriptvalue_cast(baz2Value); + QVERIFY(pbar == 0); + } + + { + QScriptValue ret = toBaz.call(scriptZoo, QScriptValueList() << baz2Value); + QVERIFY(ret.isError()); + QCOMPARE(ret.toString(), QLatin1String("TypeError: incompatible type of argument(s) in call to toBaz(); candidates were\n toBaz(Bar*)")); + } + + // establish chain -- now casting should work + // Why? because qscriptvalue_cast() does magic again. + // It the instance itself is not of type T, qscriptvalue_cast() + // searches the prototype chain for T, and if it finds one, it infers + // that the instance can also be casted to that type. This cast is + // _not_ safe and thus relies on the developer doing the right thing. + // This is an undocumented feature to enable qscriptvalue_cast() to + // be used by prototype functions to cast the JS this-object to C++. + bazProto.setPrototype(barProto); + + { + Bar *pbar = qscriptvalue_cast(baz2Value); + QVERIFY(pbar != 0); + QCOMPARE(pbar->a, baz2.a); + } + + { + QScriptValue ret = toBaz.call(scriptZoo, QScriptValueList() << baz2Value); + QEXPECT_FAIL("", "Cannot convert Baz* to Bar*", Continue); + QVERIFY(!ret.isError()); + QEXPECT_FAIL("", "Cannot convert Baz* to Bar*", Continue); + QCOMPARE(qscriptvalue_cast(ret), pbaz); + } + } + + bazProto.setPrototype(barProto.prototype()); // kill chain + { + Baz *pbaz = qscriptvalue_cast(baz2Value); + QVERIFY(pbaz != 0); + // should not work anymore + Bar *pbar = qscriptvalue_cast(baz2Value); + QVERIFY(pbar == 0); + } + + bazProto.setPrototype(eng.newQObject(this)); + { + Baz *pbaz = qscriptvalue_cast(baz2Value); + QVERIFY(pbaz != 0); + // should not work now either + Bar *pbar = qscriptvalue_cast(baz2Value); + QVERIFY(pbar == 0); + } + + { + QScriptValue b = eng.toScriptValue(QBrush()); + b.setPrototype(barProto); + // this shows that a "wrong" cast is possible, if you + // don't play by the rules (the pointer is actually a QBrush*)... + Bar *pbar = qscriptvalue_cast(b); + QVERIFY(pbar != 0); + } + + { + QScriptValue gradientProto = eng.toScriptValue(QGradient()); + QScriptValue linearGradientProto = eng.toScriptValue(QLinearGradient()); + linearGradientProto.setPrototype(gradientProto); + QLinearGradient lg(10, 20, 30, 40); + QScriptValue linearGradient = eng.toScriptValue(lg); + { + QGradient *pgrad = qscriptvalue_cast(linearGradient); + QVERIFY(pgrad == 0); + } + linearGradient.setPrototype(linearGradientProto); + { + QGradient *pgrad = qscriptvalue_cast(linearGradient); + QVERIFY(pgrad != 0); + QCOMPARE(pgrad->type(), QGradient::LinearGradient); + QLinearGradient *plingrad = static_cast(pgrad); + QCOMPARE(plingrad->start(), lg.start()); + QCOMPARE(plingrad->finalStop(), lg.finalStop()); + } + } +} +#endif + +class Klazz : public QWidget, + public QStandardItem, + public QGraphicsItem +{ + Q_OBJECT +public: + Klazz(QWidget *parent = 0) : QWidget(parent) { } + virtual QRectF boundingRect() const { return QRectF(); } + virtual void paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget*) { } +}; + +Q_DECLARE_METATYPE(Klazz*) +Q_DECLARE_METATYPE(QStandardItem*) + +void tst_QJSEngine::castWithMultipleInheritance() +{ + QJSEngine eng; + Klazz klz; + QJSValue v = eng.newQObject(&klz); + + QCOMPARE(qjsvalue_cast(v), &klz); + QCOMPARE(qjsvalue_cast(v), (QWidget *)&klz); + QCOMPARE(qjsvalue_cast(v), (QObject *)&klz); + QCOMPARE(qjsvalue_cast(v), (QStandardItem *)&klz); + QCOMPARE(qjsvalue_cast(v), (QGraphicsItem *)&klz); +} + +#if 0 // ###FIXME: ScriptOwnership +void tst_QJSEngine::collectGarbage() +{ + QScriptEngine eng; + eng.evaluate("a = new Object(); a = new Object(); a = new Object()"); + QScriptValue a = eng.newObject(); + a = eng.newObject(); + a = eng.newObject(); + QPointer ptr = new QObject(); + QVERIFY(ptr != 0); + (void)eng.newQObject(ptr, QScriptEngine::ScriptOwnership); + collectGarbage_helper(eng); + QVERIFY(ptr == 0); +} +#endif + +#if 0 // ###FIXME: no reportAdditionalMemoryCost API +void tst_QJSEngine::reportAdditionalMemoryCost() +{ + QScriptEngine eng; + // There isn't any reliable way to test whether calling + // this function affects garbage collection responsiveness; + // the best we can do is call it with a few different values. + for (int x = 0; x < 100; ++x) { + eng.reportAdditionalMemoryCost(0); + eng.reportAdditionalMemoryCost(10); + eng.reportAdditionalMemoryCost(1000); + eng.reportAdditionalMemoryCost(10000); + eng.reportAdditionalMemoryCost(100000); + eng.reportAdditionalMemoryCost(1000000); + eng.reportAdditionalMemoryCost(10000000); + eng.reportAdditionalMemoryCost(-1); + eng.reportAdditionalMemoryCost(-1000); + QScriptValue obj = eng.newObject(); + eng.collectGarbage(); + } +} +#endif + +void tst_QJSEngine::gcWithNestedDataStructure() +{ + // The GC must be able to traverse deeply nested objects, otherwise this + // test would crash. + QJSEngine eng; + eng.evaluate( + "function makeList(size)" + "{" + " var head = { };" + " var l = head;" + " for (var i = 0; i < size; ++i) {" + " l.data = i + \"\";" + " l.next = { }; l = l.next;" + " }" + " l.next = null;" + " return head;" + "}"); + QCOMPARE(eng.hasUncaughtException(), false); + const int size = 200; + QJSValue head = eng.evaluate(QString::fromLatin1("makeList(%0)").arg(size)); + QCOMPARE(eng.hasUncaughtException(), false); + for (int x = 0; x < 2; ++x) { + if (x == 1) + eng.evaluate("gc()"); + QJSValue l = head; + // Make sure all the nodes are still alive. + for (int i = 0; i < 200; ++i) { + QCOMPARE(l.property("data").toString(), QString::number(i)); + l = l.property("next"); + } + } +} + +#if 0 // ###FIXME: No processEvents handling +class EventReceiver : public QObject +{ +public: + EventReceiver() { + received = false; + } + + bool event(QEvent *e) { + received |= (e->type() == QEvent::User + 1); + return QObject::event(e); + } + + bool received; +}; + +void tst_QJSEngine::processEventsWhileRunning() +{ + for (int x = 0; x < 2; ++x) { + QScriptEngine eng; + if (x == 0) + eng.pushContext(); + + // This is running for a silly amount of time just to make sure + // the script doesn't finish before event processing is triggered. + QString script = QString::fromLatin1( + "var end = Number(new Date()) + 2000;" + "var x = 0;" + "while (Number(new Date()) < end) {" + " ++x;" + "}"); + + EventReceiver receiver; + QCoreApplication::postEvent(&receiver, new QEvent(QEvent::Type(QEvent::User+1))); + + eng.evaluate(script); + QVERIFY(!eng.hasUncaughtException()); + QVERIFY(!receiver.received); + + QCOMPARE(eng.processEventsInterval(), -1); + eng.setProcessEventsInterval(100); + eng.evaluate(script); + QVERIFY(!eng.hasUncaughtException()); + QVERIFY(receiver.received); + + if (x == 0) + eng.popContext(); + } +} + +void tst_QJSEngine::processEventsWhileRunning_function() +{ + QScriptEngine eng; + QScriptValue script = eng.evaluate(QString::fromLatin1( + "(function() { var end = Number(new Date()) + 2000;" + "var x = 0;" + "while (Number(new Date()) < end) {" + " ++x;" + "} })")); + + eng.setProcessEventsInterval(100); + + for (int x = 0; x < 2; ++x) { + EventReceiver receiver; + QCoreApplication::postEvent(&receiver, new QEvent(QEvent::Type(QEvent::User+1))); + QVERIFY(!eng.hasUncaughtException()); + QVERIFY(!receiver.received); + QCOMPARE(eng.processEventsInterval(), 100); + + if (x) script.call(); + else script.construct(); + + QVERIFY(!eng.hasUncaughtException()); + QVERIFY(receiver.received); + } +} + + +class EventReceiver2 : public QObject +{ +public: + EventReceiver2(QScriptEngine *eng) { + engine = eng; + } + + bool event(QEvent *e) { + if (e->type() == QEvent::User + 1) { + engine->currentContext()->throwError("Killed"); + } + return QObject::event(e); + } + + QScriptEngine *engine; +}; + +void tst_QJSEngine::throwErrorFromProcessEvents_data() +{ + QTest::addColumn("script"); + QTest::addColumn("error"); + + QTest::newRow("while (1)") + << QString::fromLatin1("while (1) { }") + << QString::fromLatin1("Error: Killed"); + QTest::newRow("while (1) i++") + << QString::fromLatin1("i = 0; while (1) { i++; }") + << QString::fromLatin1("Error: Killed"); + // Unlike abortEvaluation(), scripts should be able to catch the + // exception. + QTest::newRow("try catch") + << QString::fromLatin1("try {" + " while (1) { }" + "} catch(e) {" + " throw new Error('Caught');" + "}") + << QString::fromLatin1("Error: Caught"); +} + +void tst_QJSEngine::throwErrorFromProcessEvents() +{ + QFETCH(QString, script); + QFETCH(QString, error); + + QScriptEngine eng; + + EventReceiver2 receiver(&eng); + QCoreApplication::postEvent(&receiver, new QEvent(QEvent::Type(QEvent::User+1))); + + eng.setProcessEventsInterval(100); + QScriptValue ret = eng.evaluate(script); + QVERIFY(ret.isError()); + QCOMPARE(ret.toString(), error); +} + +void tst_QJSEngine::disableProcessEventsInterval() +{ + QScriptEngine eng; + eng.setProcessEventsInterval(100); + QCOMPARE(eng.processEventsInterval(), 100); + eng.setProcessEventsInterval(0); + QCOMPARE(eng.processEventsInterval(), 0); + eng.setProcessEventsInterval(-1); + QCOMPARE(eng.processEventsInterval(), -1); + eng.setProcessEventsInterval(-100); + QCOMPARE(eng.processEventsInterval(), -100); +} +#endif + +#if 0 // ###FIXME: No QScriptValueIterator API +void tst_QJSEngine::stacktrace() +{ + QString script = QString::fromLatin1( + "function foo(counter) {\n" + " switch (counter) {\n" + " case 0: foo(counter+1); break;\n" + " case 1: foo(counter+1); break;\n" + " case 2: foo(counter+1); break;\n" + " case 3: foo(counter+1); break;\n" + " case 4: foo(counter+1); break;\n" + " default:\n" + " throw new Error('blah');\n" + " }\n" + "}\n" + "foo(0);"); + + const QString fileName("testfile"); + + QStringList backtrace; + backtrace << "foo(5)@testfile:9" + << "foo(4)@testfile:7" + << "foo(3)@testfile:6" + << "foo(2)@testfile:5" + << "foo(1)@testfile:4" + << "foo(0)@testfile:3" + << "()@testfile:12"; + + QScriptEngine eng; + QScriptValue result = eng.evaluate(script, fileName); + QVERIFY(eng.hasUncaughtException()); + QVERIFY(result.isError()); + + QEXPECT_FAIL("", "QTBUG-6139: uncaughtExceptionBacktrace() doesn't give the full backtrace", Abort); + // ###FIXME: no uncahgutExceptionBacktrace: QCOMPARE(eng.uncaughtExceptionBacktrace(), backtrace); + QVERIFY(eng.hasUncaughtException()); + QVERIFY(result.strictlyEquals(eng.uncaughtException())); + + QCOMPARE(result.property("fileName").toString(), fileName); + QCOMPARE(result.property("lineNumber").toInt32(), 9); + + QScriptValue stack = result.property("stack"); + QVERIFY(stack.isArray()); + + QCOMPARE(stack.property("length").toInt32(), 7); + + QScriptValueIterator it(stack); + int counter = 5; + while (it.hasNext()) { + it.next(); + QScriptValue obj = it.value(); + QScriptValue frame = obj.property("frame"); + + QCOMPARE(obj.property("fileName").toString(), fileName); + if (counter >= 0) { + QScriptValue callee = frame.property("arguments").property("callee"); + QVERIFY(callee.strictlyEquals(eng.globalObject().property("foo"))); + QCOMPARE(obj.property("functionName").toString(), QString("foo")); + int line = obj.property("lineNumber").toInt32(); + if (counter == 5) + QCOMPARE(line, 9); + else + QCOMPARE(line, 3 + counter); + } else { + QVERIFY(frame.strictlyEquals(eng.globalObject())); + QVERIFY(obj.property("functionName").toString().isEmpty()); + } + + --counter; + } + + { + QScriptValue bt = result.property("backtrace").call(result); + QCOMPARE(qscriptvalue_cast(bt), backtrace); + } + + // throw something that isn't an Error object + eng.clearExceptions(); + // ###FIXME: No uncaughtExceptionBacktrace: QVERIFY(eng.uncaughtExceptionBacktrace().isEmpty()); + QString script2 = QString::fromLatin1( + "function foo(counter) {\n" + " switch (counter) {\n" + " case 0: foo(counter+1); break;\n" + " case 1: foo(counter+1); break;\n" + " case 2: foo(counter+1); break;\n" + " case 3: foo(counter+1); break;\n" + " case 4: foo(counter+1); break;\n" + " default:\n" + " throw 'just a string';\n" + " }\n" + "}\n" + "foo(0);"); + + QScriptValue result2 = eng.evaluate(script2, fileName); + QVERIFY(eng.hasUncaughtException()); + QVERIFY(!result2.isError()); + QVERIFY(result2.isString()); + + // ###FIXME: No uncaughtExceptionBacktrace: QCOMPARE(eng.uncaughtExceptionBacktrace(), backtrace); + QVERIFY(eng.hasUncaughtException()); + + eng.clearExceptions(); + QVERIFY(!eng.hasUncaughtException()); + // ###FIXME: No uncaughtExceptionBacktrace: QVERIFY(eng.uncaughtExceptionBacktrace().isEmpty()); +} +#endif + +void tst_QJSEngine::numberParsing_data() +{ + QTest::addColumn("string"); + QTest::addColumn("expect"); + + QTest::newRow("decimal 0") << QString("0") << qreal(0); + QTest::newRow("octal 0") << QString("00") << qreal(00); + QTest::newRow("hex 0") << QString("0x0") << qreal(0x0); + QTest::newRow("decimal 100") << QString("100") << qreal(100); + QTest::newRow("hex 100") << QString("0x100") << qreal(0x100); + QTest::newRow("octal 100") << QString("0100") << qreal(0100); + QTest::newRow("decimal 4G") << QString("4294967296") << qreal(Q_UINT64_C(4294967296)); + QTest::newRow("hex 4G") << QString("0x100000000") << qreal(Q_UINT64_C(0x100000000)); + QTest::newRow("octal 4G") << QString("040000000000") << qreal(Q_UINT64_C(040000000000)); + QTest::newRow("0.5") << QString("0.5") << qreal(0.5); + QTest::newRow("1.5") << QString("1.5") << qreal(1.5); + QTest::newRow("1e2") << QString("1e2") << qreal(100); +} + +void tst_QJSEngine::numberParsing() +{ + QFETCH(QString, string); + QFETCH(qreal, expect); + + QJSEngine eng; + QJSValue ret = eng.evaluate(string); + QVERIFY(ret.isNumber()); + qreal actual = ret.toNumber(); + QCOMPARE(actual, expect); +} + +// see ECMA-262, section 7.9 +// This is testing ECMA compliance, not our C++ API, but it's important that +// the back-end is conformant in this regard. +void tst_QJSEngine::automaticSemicolonInsertion() +{ + QJSEngine eng; + { + QJSValue ret = eng.evaluate("{ 1 2 } 3"); + QVERIFY(ret.isError()); + QVERIFY(ret.toString().contains("SyntaxError")); + } + { + QJSValue ret = eng.evaluate("{ 1\n2 } 3"); + QVERIFY(ret.isNumber()); + QCOMPARE(ret.toInt32(), 3); + } + { + QJSValue ret = eng.evaluate("for (a; b\n)"); + QVERIFY(ret.isError()); + QVERIFY(ret.toString().contains("SyntaxError")); + } + { + QJSValue ret = eng.evaluate("(function() { return\n1 + 2 })()"); + QVERIFY(ret.isUndefined()); + } + { + eng.evaluate("c = 2; b = 1"); + QJSValue ret = eng.evaluate("a = b\n++c"); + QVERIFY(ret.isNumber()); + QCOMPARE(ret.toInt32(), 3); + } + { + QJSValue ret = eng.evaluate("if (a > b)\nelse c = d"); + QVERIFY(ret.isError()); + QVERIFY(ret.toString().contains("SyntaxError")); + } + { + eng.evaluate("function c() { return { foo: function() { return 5; } } }"); + eng.evaluate("b = 1; d = 2; e = 3"); + QJSValue ret = eng.evaluate("a = b + c\n(d + e).foo()"); + QVERIFY(ret.isNumber()); + QCOMPARE(ret.toInt32(), 6); + } + { + QJSValue ret = eng.evaluate("throw\n1"); + QVERIFY(ret.isError()); + QVERIFY(ret.toString().contains("SyntaxError")); + } + { + QJSValue ret = eng.evaluate("a = Number(1)\n++a"); + QVERIFY(ret.isNumber()); + QCOMPARE(ret.toInt32(), 2); + } + + // "a semicolon is never inserted automatically if the semicolon + // would then be parsed as an empty statement" + { + eng.evaluate("a = 123"); + QJSValue ret = eng.evaluate("if (0)\n ++a; a"); + QVERIFY(ret.isNumber()); + QCOMPARE(ret.toInt32(), 123); + } + { + eng.evaluate("a = 123"); + QJSValue ret = eng.evaluate("if (0)\n --a; a"); + QVERIFY(ret.isNumber()); + QCOMPARE(ret.toInt32(), 123); + } + { + eng.evaluate("a = 123"); + QJSValue ret = eng.evaluate("if ((0))\n ++a; a"); + QVERIFY(ret.isNumber()); + QCOMPARE(ret.toInt32(), 123); + } + { + eng.evaluate("a = 123"); + QJSValue ret = eng.evaluate("if ((0))\n --a; a"); + QVERIFY(ret.isNumber()); + QCOMPARE(ret.toInt32(), 123); + } + { + eng.evaluate("a = 123"); + QJSValue ret = eng.evaluate("if (0\n)\n ++a; a"); + QVERIFY(ret.isNumber()); + QCOMPARE(ret.toInt32(), 123); + } + { + eng.evaluate("a = 123"); + QJSValue ret = eng.evaluate("if (0\n ++a; a"); + QVERIFY(ret.isError()); + } + { + eng.evaluate("a = 123"); + QJSValue ret = eng.evaluate("if (0))\n ++a; a"); + QVERIFY(ret.isError()); + } + { + QJSValue ret = eng.evaluate("n = 0; for (i = 0; i < 10; ++i)\n ++n; n"); + QVERIFY(ret.isNumber()); + QCOMPARE(ret.toInt32(), 10); + } + { + QJSValue ret = eng.evaluate("n = 30; for (i = 0; i < 10; ++i)\n --n; n"); + QVERIFY(ret.isNumber()); + QCOMPARE(ret.toInt32(), 20); + } + { + QJSValue ret = eng.evaluate("n = 0; for (var i = 0; i < 10; ++i)\n ++n; n"); + QVERIFY(ret.isNumber()); + QCOMPARE(ret.toInt32(), 10); + } + { + QJSValue ret = eng.evaluate("n = 30; for (var i = 0; i < 10; ++i)\n --n; n"); + QVERIFY(ret.isNumber()); + QCOMPARE(ret.toInt32(), 20); + } + { + QJSValue ret = eng.evaluate("n = 0; i = 0; while (i++ < 10)\n ++n; n"); + QVERIFY(ret.isNumber()); + QCOMPARE(ret.toInt32(), 10); + } + { + QJSValue ret = eng.evaluate("n = 30; i = 0; while (i++ < 10)\n --n; n"); + QVERIFY(ret.isNumber()); + QCOMPARE(ret.toInt32(), 20); + } + { + QJSValue ret = eng.evaluate("o = { a: 0, b: 1, c: 2 }; n = 0; for (i in o)\n ++n; n"); + QVERIFY(ret.isNumber()); + QCOMPARE(ret.toInt32(), 3); + } + { + QJSValue ret = eng.evaluate("o = { a: 0, b: 1, c: 2 }; n = 9; for (i in o)\n --n; n"); + QVERIFY(ret.isNumber()); + QCOMPARE(ret.toInt32(), 6); + } + { + QJSValue ret = eng.evaluate("o = { a: 0, b: 1, c: 2 }; n = 0; for (var i in o)\n ++n; n"); + QVERIFY(ret.isNumber()); + QCOMPARE(ret.toInt32(), 3); + } + { + QJSValue ret = eng.evaluate("o = { a: 0, b: 1, c: 2 }; n = 9; for (var i in o)\n --n; n"); + QVERIFY(ret.isNumber()); + QCOMPARE(ret.toInt32(), 6); + } + { + QJSValue ret = eng.evaluate("o = { n: 3 }; n = 5; with (o)\n ++n; n"); + QVERIFY(ret.isNumber()); + QCOMPARE(ret.toInt32(), 5); + } + { + QJSValue ret = eng.evaluate("o = { n: 3 }; n = 10; with (o)\n --n; n"); + QVERIFY(ret.isNumber()); + QCOMPARE(ret.toInt32(), 10); + } + { + QJSValue ret = eng.evaluate("n = 5; i = 0; do\n ++n; while (++i < 10); n"); + QVERIFY(ret.isNumber()); + QCOMPARE(ret.toInt32(), 15); + } + { + QJSValue ret = eng.evaluate("n = 20; i = 0; do\n --n; while (++i < 10); n"); + QVERIFY(ret.isNumber()); + QCOMPARE(ret.toInt32(), 10); + } + + { + QJSValue ret = eng.evaluate("n = 1; i = 0; if (n) i\n++n; n"); + QVERIFY(ret.isNumber()); + QCOMPARE(ret.toInt32(), 2); + } + { + QJSValue ret = eng.evaluate("n = 1; i = 0; if (n) i\n--n; n"); + QVERIFY(ret.isNumber()); + QCOMPARE(ret.toInt32(), 0); + } + + { + QJSValue ret = eng.evaluate("if (0)"); + QVERIFY(ret.isError()); + } + { + QJSValue ret = eng.evaluate("while (0)"); + QVERIFY(ret.isError()); + } + { + QJSValue ret = eng.evaluate("for (;;)"); + QVERIFY(ret.isError()); + } + { + QJSValue ret = eng.evaluate("for (p in this)"); + QVERIFY(ret.isError()); + } + { + QJSValue ret = eng.evaluate("with (this)"); + QVERIFY(ret.isError()); + } + { + QJSValue ret = eng.evaluate("do"); + QVERIFY(ret.isError()); + } +} + +#if 0 // ###FIXME: no abortEvaluation API +class EventReceiver3 : public QObject +{ +public: + enum AbortionResult { + None = 0, + String = 1, + Error = 2, + Number = 3 + }; + + EventReceiver3(QScriptEngine *eng) { + engine = eng; + resultType = None; + } + + bool event(QEvent *e) { + if (e->type() == QEvent::User + 1) { + switch (resultType) { + case None: + engine->abortEvaluation(); + break; + case String: + engine->abortEvaluation(QScriptValue(engine, QString::fromLatin1("Aborted"))); + break; + case Error: + engine->abortEvaluation(engine->currentContext()->throwError("AbortedWithError")); + break; + case Number: + engine->abortEvaluation(QScriptValue(1234)); + } + } + return QObject::event(e); + } + + AbortionResult resultType; + QScriptEngine *engine; +}; + +static QScriptValue myFunctionAbortingEvaluation(QScriptContext *, QScriptEngine *eng) +{ + eng->abortEvaluation(); + return eng->nullValue(); // should be ignored +} + +void tst_QJSEngine::abortEvaluation_notEvaluating() +{ + QScriptEngine eng; + + eng.abortEvaluation(); + QVERIFY(!eng.hasUncaughtException()); + + eng.abortEvaluation(123); + { + QScriptValue ret = eng.evaluate("'ciao'"); + QVERIFY(ret.isString()); + QCOMPARE(ret.toString(), QString::fromLatin1("ciao")); + } +} + +void tst_QJSEngine::abortEvaluation_data() +{ + QTest::addColumn("script"); + + QTest::newRow("while (1)") + << QString::fromLatin1("while (1) { }"); + QTest::newRow("while (1) i++") + << QString::fromLatin1("i = 0; while (1) { i++; }"); + QTest::newRow("try catch") + << QString::fromLatin1("try {" + " while (1) { }" + "} catch(e) {" + " throw new Error('Caught');" + "}"); +} + +void tst_QJSEngine::abortEvaluation() +{ + QFETCH(QString, script); + + QScriptEngine eng; + EventReceiver3 receiver(&eng); + + eng.setProcessEventsInterval(100); + for (int x = 0; x < 4; ++x) { + QCoreApplication::postEvent(&receiver, new QEvent(QEvent::Type(QEvent::User+1))); + receiver.resultType = EventReceiver3::AbortionResult(x); + QScriptValue ret = eng.evaluate(script); + switch (receiver.resultType) { + case EventReceiver3::None: + QVERIFY(!eng.hasUncaughtException()); + QVERIFY(!ret.isValid()); + break; + case EventReceiver3::Number: + QVERIFY(!eng.hasUncaughtException()); + QVERIFY(ret.isNumber()); + QCOMPARE(ret.toInt32(), 1234); + break; + case EventReceiver3::String: + QVERIFY(!eng.hasUncaughtException()); + QVERIFY(ret.isString()); + QCOMPARE(ret.toString(), QString::fromLatin1("Aborted")); + break; + case EventReceiver3::Error: + QVERIFY(eng.hasUncaughtException()); + QVERIFY(ret.isError()); + QCOMPARE(ret.toString(), QString::fromLatin1("Error: AbortedWithError")); + break; + } + } + +} + +void tst_QJSEngine::abortEvaluation_tryCatch() +{ + QSKIP("It crashes", SkipAll); + QScriptEngine eng; + EventReceiver3 receiver(&eng); + eng.setProcessEventsInterval(100); + // scripts cannot intercept the abortion with try/catch + for (int y = 0; y < 4; ++y) { + QCoreApplication::postEvent(&receiver, new QEvent(QEvent::Type(QEvent::User+1))); + receiver.resultType = EventReceiver3::AbortionResult(y); + QScriptValue ret = eng.evaluate(QString::fromLatin1( + "while (1) {\n" + " try {\n" + " (function() { while (1) { } })();\n" + " } catch (e) {\n" + " ;\n" + " }\n" + "}")); + switch (receiver.resultType) { + case EventReceiver3::None: + QVERIFY(!eng.hasUncaughtException()); + QVERIFY(!ret.isValid()); + break; + case EventReceiver3::Number: + QVERIFY(!eng.hasUncaughtException()); + QVERIFY(ret.isNumber()); + QCOMPARE(ret.toInt32(), 1234); + break; + case EventReceiver3::String: + QVERIFY(!eng.hasUncaughtException()); + QVERIFY(ret.isString()); + QCOMPARE(ret.toString(), QString::fromLatin1("Aborted")); + break; + case EventReceiver3::Error: + QVERIFY(eng.hasUncaughtException()); + QVERIFY(ret.isError()); + break; + } + } +} + +void tst_QJSEngine::abortEvaluation_fromNative() +{ + QScriptEngine eng; + QScriptValue fun = eng.newFunction(myFunctionAbortingEvaluation); + eng.globalObject().setProperty("myFunctionAbortingEvaluation", fun); + QScriptValue ret = eng.evaluate("myFunctionAbortingEvaluation()"); + QVERIFY(!ret.isValid()); +} + +class ThreadedEngine : public QThread { + Q_OBJECT; + +private: + QScriptEngine* m_engine; +protected: + void run() { + m_engine = new QScriptEngine(); + m_engine->setGlobalObject(m_engine->newQObject(this)); + m_engine->evaluate("while (1) { sleep(); }"); + delete m_engine; + } + +public slots: + void sleep() + { + QTest::qSleep(25); + m_engine->abortEvaluation(); + } +}; + +void tst_QJSEngine::abortEvaluation_QTBUG9433() +{ + ThreadedEngine engine; + engine.start(); + QVERIFY(engine.isRunning()); + QTest::qSleep(50); + for (uint i = 0; i < 50; ++i) { // up to ~2500 ms + if (engine.isFinished()) + return; + QTest::qSleep(50); + } + if (!engine.isFinished()) { + engine.terminate(); + engine.wait(7000); + QFAIL("abortEvaluation doesn't work"); + } + +} +#endif + +#if 0 // ###FIXME: no QScriptEngine::isEvaluating +static QScriptValue myFunctionReturningIsEvaluating(QScriptContext *, QScriptEngine *eng) +{ + return QScriptValue(eng, eng->isEvaluating()); +} + +class EventReceiver4 : public QObject +{ +public: + EventReceiver4(QScriptEngine *eng) { + engine = eng; + wasEvaluating = false; + } + + bool event(QEvent *e) { + if (e->type() == QEvent::User + 1) { + wasEvaluating = engine->isEvaluating(); + } + return QObject::event(e); + } + + QScriptEngine *engine; + bool wasEvaluating; +}; + +void tst_QJSEngine::isEvaluating_notEvaluating() +{ + QScriptEngine eng; + + QVERIFY(!eng.isEvaluating()); + + eng.evaluate(""); + QVERIFY(!eng.isEvaluating()); + eng.evaluate("123"); + QVERIFY(!eng.isEvaluating()); + eng.evaluate("0 = 0"); + QVERIFY(!eng.isEvaluating()); +} + +void tst_QJSEngine::isEvaluating_fromNative() +{ + QScriptEngine eng; + QScriptValue fun = eng.newFunction(myFunctionReturningIsEvaluating); + eng.globalObject().setProperty("myFunctionReturningIsEvaluating", fun); + QScriptValue ret = eng.evaluate("myFunctionReturningIsEvaluating()"); + QVERIFY(ret.isBoolean()); + QVERIFY(ret.toBoolean()); + ret = fun.call(); + QVERIFY(ret.isBoolean()); + QVERIFY(ret.toBoolean()); + ret = myFunctionReturningIsEvaluating(eng.currentContext(), &eng); + QVERIFY(ret.isBoolean()); + QVERIFY(!ret.toBoolean()); +} + +void tst_QJSEngine::isEvaluating_fromEvent() +{ + QScriptEngine eng; + EventReceiver4 receiver(&eng); + QCoreApplication::postEvent(&receiver, new QEvent(QEvent::Type(QEvent::User+1))); + + QString script = QString::fromLatin1( + "var end = Number(new Date()) + 1000;" + "var x = 0;" + "while (Number(new Date()) < end) {" + " ++x;" + "}"); + + eng.setProcessEventsInterval(100); + eng.evaluate(script); + QVERIFY(receiver.wasEvaluating); +} +#endif + +static QtMsgType theMessageType; +static QString theMessage; + +static void myMsgHandler(QtMsgType type, const char *msg) +{ + theMessageType = type; + theMessage = QString::fromLatin1(msg); +} + +#if 0 +void tst_QJSEngine::printFunctionWithCustomHandler() +{ + // The built-in print() function passes the output to Qt's message + // handler. By installing a custom message handler, the output can be + // redirected without changing the print() function itself. + // This behavior is not documented. + QJSEngine eng; + QtMsgHandler oldHandler = qInstallMsgHandler(myMsgHandler); + QVERIFY(eng.globalObject().property("print").isFunction()); + + theMessageType = QtSystemMsg; + QVERIFY(theMessage.isEmpty()); + QVERIFY(eng.evaluate("print('test')").isUndefined()); + QCOMPARE(theMessageType, QtDebugMsg); + QCOMPARE(theMessage, QString::fromLatin1("test")); + + theMessageType = QtSystemMsg; + theMessage.clear(); + QVERIFY(eng.evaluate("print(3, true, 'little pigs')").isUndefined()); + QCOMPARE(theMessageType, QtDebugMsg); + QCOMPARE(theMessage, QString::fromLatin1("3 true little pigs")); + + qInstallMsgHandler(oldHandler); +} + +void tst_QJSEngine::printThrowsException() +{ + // If an argument to print() causes an exception to be thrown when + // it's converted to a string, print() should propagate the exception. + QJSEngine eng; + QJSValue ret = eng.evaluate("print({ toString: function() { throw 'foo'; } });"); + QVERIFY(eng.hasUncaughtException()); + QVERIFY(ret.strictlyEquals(QJSValue(&eng, QLatin1String("foo")))); +} +#endif + +void tst_QJSEngine::errorConstructors() +{ + QJSEngine eng; + QStringList prefixes; + prefixes << "" << "Eval" << "Range" << "Reference" << "Syntax" << "Type" << "URI"; + for (int x = 0; x < 3; ++x) { + for (int i = 0; i < prefixes.size(); ++i) { + QString name = prefixes.at(i) + QLatin1String("Error"); + QString code = QString(i+1, QLatin1Char('\n')); + if (x == 0) + code += QLatin1String("throw "); + else if (x == 1) + code += QLatin1String("new "); + code += name + QLatin1String("()"); + QJSValue ret = eng.evaluate(code); + QVERIFY(ret.isError()); + QCOMPARE(eng.hasUncaughtException(), x == 0); + eng.clearExceptions(); + QVERIFY(ret.toString().startsWith(name)); + //QTBUG-6138: JSC doesn't assign lineNumber when errors are not thrown + QEXPECT_FAIL("", "we have no more lineNumber property ", Continue); + QCOMPARE(ret.property("lineNumber").toInt32(), i+2); + } + } +} + +void tst_QJSEngine::argumentsProperty_globalContext() +{ + QJSEngine eng; + { + // Unlike function calls, the global context doesn't have an + // arguments property. + QJSValue ret = eng.evaluate("arguments"); + QVERIFY(ret.isError()); + QVERIFY(ret.toString().contains(QString::fromLatin1("ReferenceError"))); + } + eng.evaluate("arguments = 10"); + { + QJSValue ret = eng.evaluate("arguments"); + QVERIFY(ret.isNumber()); + QCOMPARE(ret.toInt32(), 10); + } + QVERIFY(eng.evaluate("delete arguments").toBoolean()); + QVERIFY(!eng.globalObject().property("arguments").isValid()); +} + +void tst_QJSEngine::argumentsProperty_JS() +{ + { + QJSEngine eng; + eng.evaluate("o = { arguments: 123 }"); + QJSValue ret = eng.evaluate("with (o) { arguments; }"); + QVERIFY(ret.isNumber()); + QCOMPARE(ret.toInt32(), 123); + } + { + QJSEngine eng; + QVERIFY(!eng.globalObject().property("arguments").isValid()); + // This is testing ECMA-262 compliance. In function calls, "arguments" + // appears like a local variable, and it can be replaced. + QJSValue ret = eng.evaluate("(function() { arguments = 456; return arguments; })()"); + QVERIFY(ret.isNumber()); + QCOMPARE(ret.toInt32(), 456); + QVERIFY(!eng.globalObject().property("arguments").isValid()); + } +} + +#if 0 // ###FIXME: no QScriptContext API +static QScriptValue argumentsProperty_fun(QScriptContext *, QScriptEngine *eng) +{ + // Since evaluate() is done in the current context, "arguments" should + // refer to currentContext()->argumentsObject(). + // This is for consistency with the built-in JS eval() function. + eng->evaluate("var a = arguments[0];"); + eng->evaluate("arguments[0] = 200;"); + return eng->evaluate("a + arguments[0]"); +} + +void tst_QJSEngine::argumentsProperty_evaluateInNativeFunction() +{ + QScriptEngine eng; + QScriptValue fun = eng.newFunction(argumentsProperty_fun); + eng.globalObject().setProperty("fun", eng.newFunction(argumentsProperty_fun)); + QScriptValue result = eng.evaluate("fun(18)"); + QVERIFY(result.isNumber()); + QCOMPARE(result.toInt32(), 200+18); +} +#endif + +void tst_QJSEngine::jsNumberClass() +{ + // See ECMA-262 Section 15.7, "Number Objects". + + QJSEngine eng; + + QJSValue ctor = eng.globalObject().property("Number"); + QVERIFY(ctor.property("length").isNumber()); + QCOMPARE(ctor.property("length").toNumber(), qreal(1)); + QJSValue proto = ctor.property("prototype"); + QVERIFY(proto.isObject()); + { + QJSValue::PropertyFlags flags = QJSValue::SkipInEnumeration + | QJSValue::Undeletable + | QJSValue::ReadOnly; + QCOMPARE(ctor.propertyFlags("prototype"), flags); + QVERIFY(ctor.property("MAX_VALUE").isNumber()); + QCOMPARE(ctor.propertyFlags("MAX_VALUE"), flags); + QVERIFY(ctor.property("MIN_VALUE").isNumber()); + QCOMPARE(ctor.propertyFlags("MIN_VALUE"), flags); + QVERIFY(ctor.property("NaN").isNumber()); + QCOMPARE(ctor.propertyFlags("NaN"), flags); + QVERIFY(ctor.property("NEGATIVE_INFINITY").isNumber()); + QCOMPARE(ctor.propertyFlags("NEGATIVE_INFINITY"), flags); + QVERIFY(ctor.property("POSITIVE_INFINITY").isNumber()); + QCOMPARE(ctor.propertyFlags("POSITIVE_INFINITY"), flags); + } + QVERIFY(proto.instanceOf(eng.globalObject().property("Object"))); + QCOMPARE(proto.toNumber(), qreal(0)); + QVERIFY(proto.property("constructor").strictlyEquals(ctor)); + + { + QJSValue ret = eng.evaluate("Number()"); + QVERIFY(ret.isNumber()); + QCOMPARE(ret.toNumber(), qreal(0)); + } + { + QJSValue ret = eng.evaluate("Number(123)"); + QVERIFY(ret.isNumber()); + QCOMPARE(ret.toNumber(), qreal(123)); + } + { + QJSValue ret = eng.evaluate("Number('456')"); + QVERIFY(ret.isNumber()); + QCOMPARE(ret.toNumber(), qreal(456)); + } + { + QJSValue ret = eng.evaluate("new Number()"); + QVERIFY(!ret.isNumber()); + QVERIFY(ret.isObject()); + QCOMPARE(ret.toNumber(), qreal(0)); + } + { + QJSValue ret = eng.evaluate("new Number(123)"); + QVERIFY(!ret.isNumber()); + QVERIFY(ret.isObject()); + QCOMPARE(ret.toNumber(), qreal(123)); + } + { + QJSValue ret = eng.evaluate("new Number('456')"); + QVERIFY(!ret.isNumber()); + QVERIFY(ret.isObject()); + QCOMPARE(ret.toNumber(), qreal(456)); + } + + QVERIFY(proto.property("toString").isFunction()); + { + QJSValue ret = eng.evaluate("new Number(123).toString()"); + QVERIFY(ret.isString()); + QCOMPARE(ret.toString(), QString::fromLatin1("123")); + } + { + QJSValue ret = eng.evaluate("new Number(123).toString(8)"); + QVERIFY(ret.isString()); + QCOMPARE(ret.toString(), QString::fromLatin1("173")); + } + { + QJSValue ret = eng.evaluate("new Number(123).toString(16)"); + QVERIFY(ret.isString()); + QCOMPARE(ret.toString(), QString::fromLatin1("7b")); + } + QVERIFY(proto.property("toLocaleString").isFunction()); + { + QJSValue ret = eng.evaluate("new Number(123).toLocaleString()"); + QVERIFY(ret.isString()); + QCOMPARE(ret.toString(), QString::fromLatin1("123")); + } + QVERIFY(proto.property("valueOf").isFunction()); + { + QJSValue ret = eng.evaluate("new Number(123).valueOf()"); + QVERIFY(ret.isNumber()); + QCOMPARE(ret.toNumber(), qreal(123)); + } + QVERIFY(proto.property("toExponential").isFunction()); + { + QJSValue ret = eng.evaluate("new Number(123).toExponential()"); + QVERIFY(ret.isString()); + QCOMPARE(ret.toString(), QString::fromLatin1("1.23e+2")); + } + QVERIFY(proto.property("toFixed").isFunction()); + { + QJSValue ret = eng.evaluate("new Number(123).toFixed()"); + QVERIFY(ret.isString()); + QCOMPARE(ret.toString(), QString::fromLatin1("123")); + } + QVERIFY(proto.property("toPrecision").isFunction()); + { + QJSValue ret = eng.evaluate("new Number(123).toPrecision()"); + QVERIFY(ret.isString()); + QCOMPARE(ret.toString(), QString::fromLatin1("123")); + } +} + +void tst_QJSEngine::jsForInStatement_simple() +{ + QJSEngine eng; + { + QJSValue ret = eng.evaluate("o = { }; r = []; for (var p in o) r[r.length] = p; r"); + QStringList lst = qjsvalue_cast(ret); + QVERIFY(lst.isEmpty()); + } + { + QJSValue ret = eng.evaluate("o = { p: 123 }; r = [];" + "for (var p in o) r[r.length] = p; r"); + QStringList lst = qjsvalue_cast(ret); + QCOMPARE(lst.size(), 1); + QCOMPARE(lst.at(0), QString::fromLatin1("p")); + } + { + QJSValue ret = eng.evaluate("o = { p: 123, q: 456 }; r = [];" + "for (var p in o) r[r.length] = p; r"); + QStringList lst = qjsvalue_cast(ret); + QCOMPARE(lst.size(), 2); + QCOMPARE(lst.at(0), QString::fromLatin1("p")); + QCOMPARE(lst.at(1), QString::fromLatin1("q")); + } +} + +void tst_QJSEngine::jsForInStatement_prototypeProperties() +{ + QJSEngine eng; + { + QJSValue ret = eng.evaluate("o = { }; o.__proto__ = { p: 123 }; r = [];" + "for (var p in o) r[r.length] = p; r"); + QStringList lst = qjsvalue_cast(ret); + QCOMPARE(lst.size(), 1); + QCOMPARE(lst.at(0), QString::fromLatin1("p")); + } + { + QJSValue ret = eng.evaluate("o = { p: 123 }; o.__proto__ = { q: 456 }; r = [];" + "for (var p in o) r[r.length] = p; r"); + QStringList lst = qjsvalue_cast(ret); + QCOMPARE(lst.size(), 2); + QCOMPARE(lst.at(0), QString::fromLatin1("p")); + QCOMPARE(lst.at(1), QString::fromLatin1("q")); + } + { + // shadowed property + QJSValue ret = eng.evaluate("o = { p: 123 }; o.__proto__ = { p: 456 }; r = [];" + "for (var p in o) r[r.length] = p; r"); + QStringList lst = qjsvalue_cast(ret); + QCOMPARE(lst.size(), 1); + QCOMPARE(lst.at(0), QString::fromLatin1("p")); + } + +} + +void tst_QJSEngine::jsForInStatement_mutateWhileIterating() +{ + QJSEngine eng; + // deleting property during enumeration + { + QJSValue ret = eng.evaluate("o = { p: 123 }; r = [];" + "for (var p in o) { r[r.length] = p; delete r[p]; } r"); + QStringList lst = qjsvalue_cast(ret); + QCOMPARE(lst.size(), 1); + QCOMPARE(lst.at(0), QString::fromLatin1("p")); + } + { + QJSValue ret = eng.evaluate("o = { p: 123, q: 456 }; r = [];" + "for (var p in o) { r[r.length] = p; delete o.q; } r"); + QStringList lst = qjsvalue_cast(ret); + QCOMPARE(lst.size(), 1); + QCOMPARE(lst.at(0), QString::fromLatin1("p")); + } + + // adding property during enumeration + { + QJSValue ret = eng.evaluate("o = { p: 123 }; r = [];" + "for (var p in o) { r[r.length] = p; o.q = 456; } r"); + QStringList lst = qjsvalue_cast(ret); + QCOMPARE(lst.size(), 1); + QCOMPARE(lst.at(0), QString::fromLatin1("p")); + } + +} + +void tst_QJSEngine::jsForInStatement_arrays() +{ + QJSEngine eng; + { + QJSValue ret = eng.evaluate("a = [123, 456]; r = [];" + "for (var p in a) r[r.length] = p; r"); + QStringList lst = qjsvalue_cast(ret); + QCOMPARE(lst.size(), 2); + QCOMPARE(lst.at(0), QString::fromLatin1("0")); + QCOMPARE(lst.at(1), QString::fromLatin1("1")); + } + { + QJSValue ret = eng.evaluate("a = [123, 456]; a.foo = 'bar'; r = [];" + "for (var p in a) r[r.length] = p; r"); + QStringList lst = qjsvalue_cast(ret); + QCOMPARE(lst.size(), 3); + QCOMPARE(lst.at(0), QString::fromLatin1("0")); + QCOMPARE(lst.at(1), QString::fromLatin1("1")); + QCOMPARE(lst.at(2), QString::fromLatin1("foo")); + } + { + QJSValue ret = eng.evaluate("a = [123, 456]; a.foo = 'bar';" + "b = [111, 222, 333]; b.bar = 'baz';" + "a.__proto__ = b; r = [];" + "for (var p in a) r[r.length] = p; r"); + QStringList lst = qjsvalue_cast(ret); + QCOMPARE(lst.size(), 5); + QCOMPARE(lst.at(0), QString::fromLatin1("0")); + QCOMPARE(lst.at(1), QString::fromLatin1("1")); + QCOMPARE(lst.at(2), QString::fromLatin1("foo")); + QCOMPARE(lst.at(3), QString::fromLatin1("2")); + QCOMPARE(lst.at(4), QString::fromLatin1("bar")); + } +} + +void tst_QJSEngine::jsForInStatement_nullAndUndefined() +{ + QJSEngine eng; + { + QJSValue ret = eng.evaluate("r = true; for (var p in undefined) r = false; r"); + QVERIFY(ret.isBool()); + QVERIFY(ret.toBool()); + } + { + QJSValue ret = eng.evaluate("r = true; for (var p in null) r = false; r"); + QVERIFY(ret.isBool()); + QVERIFY(ret.toBool()); + } +} + +void tst_QJSEngine::jsFunctionDeclarationAsStatement() +{ + // ECMA-262 does not allow function declarations to be used as statements, + // but several popular implementations (including JSC) do. See the NOTE + // at the beginning of chapter 12 in ECMA-262 5th edition, where it's + // recommended that implementations either disallow this usage or issue + // a warning. + // Since we had a bug report long ago about QtScript not supporting this + // "feature" (and thus deviating from other implementations), we still + // check this behavior. + + QJSEngine eng; + QVERIFY(!eng.globalObject().property("bar").isValid()); + eng.evaluate("function foo(arg) {\n" + " if (arg == 'bar')\n" + " function bar() { return 'bar'; }\n" + " else\n" + " function baz() { return 'baz'; }\n" + " return (arg == 'bar') ? bar : baz;\n" + "}"); + QVERIFY(!eng.globalObject().property("bar").isValid()); + QVERIFY(!eng.globalObject().property("baz").isValid()); + QVERIFY(eng.evaluate("foo").isFunction()); + { + QJSValue ret = eng.evaluate("foo('bar')"); + QVERIFY(ret.isFunction()); + QJSValue ret2 = ret.call(QJSValue()); + QCOMPARE(ret2.toString(), QString::fromLatin1("bar")); + QVERIFY(!eng.globalObject().property("bar").isValid()); + QVERIFY(!eng.globalObject().property("baz").isValid()); + } + { + QJSValue ret = eng.evaluate("foo('baz')"); + QVERIFY(ret.isFunction()); + QJSValue ret2 = ret.call(QJSValue()); + QCOMPARE(ret2.toString(), QString::fromLatin1("baz")); + QVERIFY(!eng.globalObject().property("bar").isValid()); + QVERIFY(!eng.globalObject().property("baz").isValid()); + } +} + +void tst_QJSEngine::stringObjects() +{ + // See ECMA-262 Section 15.5, "String Objects". + + QJSEngine eng; + QString str("ciao"); + // in C++ + { + QJSValue obj = QJSValue(&eng, str).toObject(); + QCOMPARE(obj.property("length").toInt32(), str.length()); + QCOMPARE(obj.propertyFlags("length"), QJSValue::PropertyFlags(QJSValue::Undeletable | QJSValue::SkipInEnumeration | QJSValue::ReadOnly)); + for (int i = 0; i < str.length(); ++i) { + QString pname = QString::number(i); + QVERIFY(obj.property(pname).isString()); + QCOMPARE(obj.property(pname).toString(), QString(str.at(i))); + QEXPECT_FAIL("", "FIXME: This is V8 issue 862. ECMA script standard 15.5.5.2 compliance.", Continue); + QCOMPARE(obj.propertyFlags(pname), QJSValue::PropertyFlags(QJSValue::Undeletable | QJSValue::ReadOnly)); + obj.setProperty(pname, QJSValue()); + obj.setProperty(pname, QJSValue(&eng, 123)); + QVERIFY(obj.property(pname).isString()); + QCOMPARE(obj.property(pname).toString(), QString(str.at(i))); + } + QVERIFY(!obj.property("-1").isValid()); + QVERIFY(!obj.property(QString::number(str.length())).isValid()); + + QJSValue val(&eng, 123); + obj.setProperty("-1", val); + QVERIFY(obj.property("-1").strictlyEquals(val)); + obj.setProperty("100", val); + QVERIFY(obj.property("100").strictlyEquals(val)); + } + + // in script + { + QJSValue ret = eng.evaluate("s = new String('ciao'); r = []; for (var p in s) r.push(p); r"); + QVERIFY(ret.isArray()); + QStringList lst = qjsvalue_cast(ret); + QCOMPARE(lst.size(), str.length()); + for (int i = 0; i < str.length(); ++i) + QCOMPARE(lst.at(i), QString::number(i)); + + QJSValue ret2 = eng.evaluate("s[0] = 123; s[0]"); + QVERIFY(ret2.isString()); + QCOMPARE(ret2.toString().length(), 1); + QCOMPARE(ret2.toString().at(0), str.at(0)); + + QJSValue ret3 = eng.evaluate("s[-1] = 123; s[-1]"); + QVERIFY(ret3.isNumber()); + QCOMPARE(ret3.toInt32(), 123); + + QJSValue ret4 = eng.evaluate("s[s.length] = 456; s[s.length]"); + QVERIFY(ret4.isNumber()); + QCOMPARE(ret4.toInt32(), 456); + + QJSValue ret5 = eng.evaluate("delete s[0]"); + QVERIFY(ret5.isBoolean()); + QEXPECT_FAIL("", "FIXME: This is V8 bug, please report it! ECMA script standard 15.5.5.2", Abort); + QVERIFY(!ret5.toBoolean()); + + QJSValue ret6 = eng.evaluate("delete s[-1]"); + QVERIFY(ret6.isBoolean()); + QVERIFY(ret6.toBoolean()); + + QJSValue ret7 = eng.evaluate("delete s[s.length]"); + QVERIFY(ret7.isBoolean()); + QVERIFY(ret7.toBoolean()); + } +} + +void tst_QJSEngine::jsStringPrototypeReplaceBugs() +{ + QJSEngine eng; + // task 212440 + { + QJSValue ret = eng.evaluate("replace_args = []; \"a a a\".replace(/(a)/g, function() { replace_args.push(arguments); }); replace_args"); + QVERIFY(ret.isArray()); + int len = ret.property("length").toInt32(); + QCOMPARE(len, 3); + for (int i = 0; i < len; ++i) { + QJSValue args = ret.property(i); + QCOMPARE(args.property("length").toInt32(), 4); + QCOMPARE(args.property(0).toString(), QString::fromLatin1("a")); // matched string + QCOMPARE(args.property(1).toString(), QString::fromLatin1("a")); // capture + QVERIFY(args.property(2).isNumber()); + QCOMPARE(args.property(2).toInt32(), i*2); // index of match + QCOMPARE(args.property(3).toString(), QString::fromLatin1("a a a")); + } + } + // task 212501 + { + QJSValue ret = eng.evaluate("\"foo\".replace(/a/g, function() {})"); + QVERIFY(ret.isString()); + QCOMPARE(ret.toString(), QString::fromLatin1("foo")); + } +} + +void tst_QJSEngine::getterSetterThisObject_global() +{ + { + QJSEngine eng; + // read + eng.evaluate("__defineGetter__('x', function() { return this; });"); + { + QJSValue ret = eng.evaluate("x"); + QVERIFY(ret.equals(eng.globalObject())); + } + { + QJSValue ret = eng.evaluate("(function() { return x; })()"); + QVERIFY(ret.equals(eng.globalObject())); + } + { + QJSValue ret = eng.evaluate("with (this) x"); + QVERIFY(ret.equals(eng.globalObject())); + } + { + QJSValue ret = eng.evaluate("with ({}) x"); + QVERIFY(ret.equals(eng.globalObject())); + } + { + QJSValue ret = eng.evaluate("(function() { with ({}) return x; })()"); + QVERIFY(ret.equals(eng.globalObject())); + } + // write + eng.evaluate("__defineSetter__('x', function() { return this; });"); + { + QJSValue ret = eng.evaluate("x = 'foo'"); + // SpiderMonkey says setter return value, JSC says RHS. + QVERIFY(ret.isString()); + QCOMPARE(ret.toString(), QString::fromLatin1("foo")); + } + { + QJSValue ret = eng.evaluate("(function() { return x = 'foo'; })()"); + // SpiderMonkey says setter return value, JSC says RHS. + QVERIFY(ret.isString()); + QCOMPARE(ret.toString(), QString::fromLatin1("foo")); + } + { + QJSValue ret = eng.evaluate("with (this) x = 'foo'"); + // SpiderMonkey says setter return value, JSC says RHS. + QVERIFY(ret.isString()); + QCOMPARE(ret.toString(), QString::fromLatin1("foo")); + } + { + QJSValue ret = eng.evaluate("with ({}) x = 'foo'"); + // SpiderMonkey says setter return value, JSC says RHS. + QVERIFY(ret.isString()); + QCOMPARE(ret.toString(), QString::fromLatin1("foo")); + } + { + QJSValue ret = eng.evaluate("(function() { with ({}) return x = 'foo'; })()"); + // SpiderMonkey says setter return value, JSC says RHS. + QVERIFY(ret.isString()); + QCOMPARE(ret.toString(), QString::fromLatin1("foo")); + } + } +} + +void tst_QJSEngine::getterSetterThisObject_plain() +{ + { + QJSEngine eng; + eng.evaluate("o = {}"); + // read + eng.evaluate("o.__defineGetter__('x', function() { return this; })"); + QVERIFY(eng.evaluate("o.x === o").toBoolean()); + QVERIFY(eng.evaluate("with (o) x").equals(eng.evaluate("o"))); + QVERIFY(eng.evaluate("(function() { with (o) return x; })() === o").toBoolean()); + eng.evaluate("q = {}; with (o) with (q) x").equals(eng.evaluate("o")); + // write + eng.evaluate("o.__defineSetter__('x', function() { return this; });"); + // SpiderMonkey says setter return value, JSC says RHS. + QVERIFY(eng.evaluate("(o.x = 'foo') === 'foo'").toBoolean()); + QVERIFY(eng.evaluate("with (o) x = 'foo'").equals("foo")); + QVERIFY(eng.evaluate("with (o) with (q) x = 'foo'").equals("foo")); + } +} + +void tst_QJSEngine::getterSetterThisObject_prototypeChain() +{ + { + QJSEngine eng; + eng.evaluate("o = {}; p = {}; o.__proto__ = p"); + // read + eng.evaluate("p.__defineGetter__('x', function() { return this; })"); + QVERIFY(eng.evaluate("o.x === o").toBoolean()); + QVERIFY(eng.evaluate("with (o) x").equals(eng.evaluate("o"))); + QVERIFY(eng.evaluate("(function() { with (o) return x; })() === o").toBoolean()); + eng.evaluate("q = {}; with (o) with (q) x").equals(eng.evaluate("o")); + eng.evaluate("with (q) with (o) x").equals(eng.evaluate("o")); + // write + eng.evaluate("o.__defineSetter__('x', function() { return this; });"); + // SpiderMonkey says setter return value, JSC says RHS. + QVERIFY(eng.evaluate("(o.x = 'foo') === 'foo'").toBoolean()); + QVERIFY(eng.evaluate("with (o) x = 'foo'").equals("foo")); + QVERIFY(eng.evaluate("with (o) with (q) x = 'foo'").equals("foo")); + } +} + +#if 0 // ###FIXME: no QScriptContext API +void tst_QJSEngine::getterSetterThisObject_activation() +{ + { + QScriptEngine eng; + QScriptContext *ctx = eng.pushContext(); + QVERIFY(ctx != 0); + QScriptValue act = ctx->activationObject(); + act.setProperty("act", act); + // read + eng.evaluate("act.__defineGetter__('x', function() { return this; })"); + QVERIFY(eng.evaluate("x === act").toBoolean()); + QEXPECT_FAIL("", "QTBUG-17605: Not possible to implement local variables as getter/setter properties", Abort); + QVERIFY(!eng.hasUncaughtException()); + QVERIFY(eng.evaluate("with (act) x").equals("foo")); + QVERIFY(eng.evaluate("(function() { with (act) return x; })() === act").toBoolean()); + eng.evaluate("q = {}; with (act) with (q) x").equals(eng.evaluate("act")); + eng.evaluate("with (q) with (act) x").equals(eng.evaluate("act")); + // write + eng.evaluate("act.__defineSetter__('x', function() { return this; });"); + QVERIFY(eng.evaluate("(x = 'foo') === 'foo'").toBoolean()); + QVERIFY(eng.evaluate("with (act) x = 'foo'").equals("foo")); + QVERIFY(eng.evaluate("with (act) with (q) x = 'foo'").equals("foo")); + eng.popContext(); + } +} +#endif + +void tst_QJSEngine::jsContinueInSwitch() +{ + // This is testing ECMA-262 compliance, not C++ API. + + QJSEngine eng; + // switch - continue + { + QJSValue ret = eng.evaluate("switch (1) { default: continue; }"); + QVERIFY(ret.isError()); + } + // for - switch - case - continue + { + QJSValue ret = eng.evaluate("j = 0; for (i = 0; i < 100000; ++i) {\n" + " switch (i) {\n" + " case 1: ++j; continue;\n" + " case 100: ++j; continue;\n" + " case 1000: ++j; continue;\n" + " }\n" + "}; j"); + QVERIFY(ret.isNumber()); + QCOMPARE(ret.toInt32(), 3); + } + // for - switch - case - default - continue + { + QJSValue ret = eng.evaluate("j = 0; for (i = 0; i < 100000; ++i) {\n" + " switch (i) {\n" + " case 1: ++j; continue;\n" + " case 100: ++j; continue;\n" + " case 1000: ++j; continue;\n" + " default: if (i < 50000) break; else continue;\n" + " }\n" + "}; j"); + QVERIFY(ret.isNumber()); + QCOMPARE(ret.toInt32(), 3); + } + // switch - for - continue + { + QJSValue ret = eng.evaluate("j = 123; switch (j) {\n" + " case 123:\n" + " for (i = 0; i < 100000; ++i) {\n" + " continue;\n" + " }\n" + "}; i\n"); + QVERIFY(ret.isNumber()); + QCOMPARE(ret.toInt32(), 100000); + } + // switch - switch - continue + { + QJSValue ret = eng.evaluate("i = 1; switch (i) { default: switch (i) { case 1: continue; } }"); + QVERIFY(ret.isError()); + } + // for - switch - switch - continue + { + QJSValue ret = eng.evaluate("j = 0; for (i = 0; i < 100000; ++i) {\n" + " switch (i) {\n" + " case 1:\n" + " switch (i) {\n" + " case 1: ++j; continue;\n" + " }\n" + " }\n" + "}; j"); + QVERIFY(ret.isNumber()); + QCOMPARE(ret.toInt32(), 1); + } + // switch - for - switch - continue + { + QJSValue ret = eng.evaluate("j = 123; switch (j) {\n" + " case 123:\n" + " for (i = 0; i < 100000; ++i) {\n" + " switch (i) {\n" + " case 1:\n" + " ++j; continue;\n" + " }\n" + " }\n" + "}; i\n"); + QVERIFY(ret.isNumber()); + QCOMPARE(ret.toInt32(), 100000); + } +} + +void tst_QJSEngine::jsShadowReadOnlyPrototypeProperty() +{ + // SpiderMonkey has different behavior than JSC and V8; it disallows + // creating a property on the instance if there's a property with the + // same name in the prototype, and that property is read-only. We + // adopted that behavior in the old (4.5) QtScript back-end, but it + // just seems weird -- and non-compliant. Adopt the JSC behavior instead. + QJSEngine eng; + QVERIFY(eng.evaluate("o = {}; o.__proto__ = parseInt; o.length").isNumber()); + QCOMPARE(eng.evaluate("o.length = 123; o.length").toInt32(), 123); + QVERIFY(eng.evaluate("o.hasOwnProperty('length')").toBoolean()); +} + +void tst_QJSEngine::toObject() +{ + QJSEngine eng; + + QVERIFY(!eng.toObject(eng.undefinedValue()).isValid()); + + QVERIFY(!eng.toObject(eng.nullValue()).isValid()); + + QJSValue falskt(false); + { + QJSValue tmp = eng.toObject(falskt); + QVERIFY(tmp.isObject()); + QCOMPARE(tmp.toNumber(), falskt.toNumber()); + } + QVERIFY(falskt.isBool()); + + QJSValue sant(true); + { + QJSValue tmp = eng.toObject(sant); + QVERIFY(tmp.isObject()); + QCOMPARE(tmp.toNumber(), sant.toNumber()); + } + QVERIFY(sant.isBool()); + + QJSValue number(123.0); + { + QJSValue tmp = eng.toObject(number); + QVERIFY(tmp.isObject()); + QCOMPARE(tmp.toNumber(), number.toNumber()); + } + QVERIFY(number.isNumber()); + + QJSValue str = QJSValue(&eng, QString("ciao")); + { + QJSValue tmp = eng.toObject(str); + QVERIFY(tmp.isObject()); + QCOMPARE(tmp.toString(), str.toString()); + } + QVERIFY(str.isString()); + + QJSValue object = eng.newObject(); + { + QJSValue tmp = eng.toObject(object); + QVERIFY(tmp.isObject()); + QVERIFY(tmp.strictlyEquals(object)); + } + + QJSValue qobject = eng.newQObject(this); + QVERIFY(eng.toObject(qobject).strictlyEquals(qobject)); + + QVERIFY(!eng.toObject(QJSValue()).isValid()); + + // v1 constructors + + QJSValue boolValue(&eng, true); + { + QJSValue ret = eng.toObject(boolValue); + QVERIFY(ret.isObject()); + QCOMPARE(ret.toBool(), boolValue.toBool()); + } + QVERIFY(boolValue.isBool()); + + QJSValue numberValue(&eng, 123.0); + { + QJSValue ret = eng.toObject(numberValue); + QVERIFY(ret.isObject()); + QCOMPARE(ret.toNumber(), numberValue.toNumber()); + } + QVERIFY(numberValue.isNumber()); + + QJSValue stringValue(&eng, QString::fromLatin1("foo")); + { + QJSValue ret = eng.toObject(stringValue); + QVERIFY(ret.isObject()); + QCOMPARE(ret.toString(), stringValue.toString()); + } + QVERIFY(stringValue.isString()); +} + +void tst_QJSEngine::jsReservedWords_data() +{ + QTest::addColumn("word"); + QTest::newRow("break") << QString("break"); + QTest::newRow("case") << QString("case"); + QTest::newRow("catch") << QString("catch"); + QTest::newRow("continue") << QString("continue"); + QTest::newRow("default") << QString("default"); + QTest::newRow("delete") << QString("delete"); + QTest::newRow("do") << QString("do"); + QTest::newRow("else") << QString("else"); + QTest::newRow("false") << QString("false"); + QTest::newRow("finally") << QString("finally"); + QTest::newRow("for") << QString("for"); + QTest::newRow("function") << QString("function"); + QTest::newRow("if") << QString("if"); + QTest::newRow("in") << QString("in"); + QTest::newRow("instanceof") << QString("instanceof"); + QTest::newRow("new") << QString("new"); + QTest::newRow("null") << QString("null"); + QTest::newRow("return") << QString("return"); + QTest::newRow("switch") << QString("switch"); + QTest::newRow("this") << QString("this"); + QTest::newRow("throw") << QString("throw"); + QTest::newRow("true") << QString("true"); + QTest::newRow("try") << QString("try"); + QTest::newRow("typeof") << QString("typeof"); + QTest::newRow("var") << QString("var"); + QTest::newRow("void") << QString("void"); + QTest::newRow("while") << QString("while"); + QTest::newRow("with") << QString("with"); +} + +void tst_QJSEngine::jsReservedWords() +{ + // See ECMA-262 Section 7.6.1, "Reserved Words". + // We prefer that the implementation is less strict than the spec; e.g. + // it's good to allow reserved words as identifiers in object literals, + // and when accessing properties using dot notation. + + QFETCH(QString, word); + { + QJSEngine eng; + QJSValue ret = eng.evaluate(word + " = 123"); + QVERIFY(ret.isError()); + QString str = ret.toString(); + QVERIFY(str.startsWith("SyntaxError") || str.startsWith("ReferenceError")); + } + { + QJSEngine eng; + QJSValue ret = eng.evaluate("var " + word + " = 123"); + QVERIFY(ret.isError()); + QVERIFY(ret.toString().startsWith("SyntaxError")); + } + { + QJSEngine eng; + QJSValue ret = eng.evaluate("o = {}; o." + word + " = 123"); + // in the old back-end, in SpiderMonkey and in v8, this is allowed, but not in JSC + QVERIFY(!ret.isError()); + QVERIFY(ret.strictlyEquals(eng.evaluate("o." + word))); + } + { + QJSEngine eng; + QJSValue ret = eng.evaluate("o = { " + word + ": 123 }"); + // in the old back-end, in SpiderMonkey and in v8, this is allowed, but not in JSC + QVERIFY(!ret.isError()); + QVERIFY(ret.property(word).isNumber()); + } + { + // SpiderMonkey allows this, but we don't + QJSEngine eng; + QJSValue ret = eng.evaluate("function " + word + "() {}"); + QVERIFY(ret.isError()); + QVERIFY(ret.toString().startsWith("SyntaxError")); + } +} + +void tst_QJSEngine::jsFutureReservedWords_data() +{ + QTest::addColumn("word"); + QTest::addColumn("allowed"); + QTest::newRow("abstract") << QString("abstract") << true; + QTest::newRow("boolean") << QString("boolean") << true; + QTest::newRow("byte") << QString("byte") << true; + QTest::newRow("char") << QString("char") << true; + QTest::newRow("class") << QString("class") << false; + QTest::newRow("const") << QString("const") << false; + QTest::newRow("debugger") << QString("debugger") << false; + QTest::newRow("double") << QString("double") << true; + QTest::newRow("enum") << QString("enum") << false; + QTest::newRow("export") << QString("export") << false; + QTest::newRow("extends") << QString("extends") << false; + QTest::newRow("final") << QString("final") << true; + QTest::newRow("float") << QString("float") << true; + QTest::newRow("goto") << QString("goto") << true; + QTest::newRow("implements") << QString("implements") << true; + QTest::newRow("import") << QString("import") << false; + QTest::newRow("int") << QString("int") << true; + QTest::newRow("interface") << QString("interface") << true; + QTest::newRow("long") << QString("long") << true; + QTest::newRow("native") << QString("native") << true; + QTest::newRow("package") << QString("package") << true; + QTest::newRow("private") << QString("private") << true; + QTest::newRow("protected") << QString("protected") << true; + QTest::newRow("public") << QString("public") << true; + QTest::newRow("short") << QString("short") << true; + QTest::newRow("static") << QString("static") << true; + QTest::newRow("super") << QString("super") << false; + QTest::newRow("synchronized") << QString("synchronized") << true; + QTest::newRow("throws") << QString("throws") << true; + QTest::newRow("transient") << QString("transient") << true; + QTest::newRow("volatile") << QString("volatile") << true; +} + +void tst_QJSEngine::jsFutureReservedWords() +{ + QSKIP("Fails", SkipAll); + // See ECMA-262 Section 7.6.1.2, "Future Reserved Words". + // In real-world implementations, most of these words are + // actually allowed as normal identifiers. + + QFETCH(QString, word); + QFETCH(bool, allowed); + { + QJSEngine eng; + QJSValue ret = eng.evaluate(word + " = 123"); + QCOMPARE(!ret.isError(), allowed); + } + { + QJSEngine eng; + QJSValue ret = eng.evaluate("var " + word + " = 123"); + QCOMPARE(!ret.isError(), allowed); + } + { + // this should probably be allowed (see task 162567) + QJSEngine eng; + QJSValue ret = eng.evaluate("o = {}; o." + word + " = 123"); + QCOMPARE(ret.isNumber(), allowed); + QCOMPARE(!ret.isError(), allowed); + } + { + // this should probably be allowed (see task 162567) + QJSEngine eng; + QJSValue ret = eng.evaluate("o = { " + word + ": 123 }"); + QCOMPARE(!ret.isError(), allowed); + } +} + +void tst_QJSEngine::jsThrowInsideWithStatement() +{ + // This is testing ECMA-262 compliance, not C++ API. + + // task 209988 + QJSEngine eng; + { + QJSValue ret = eng.evaluate( + "try {" + " o = { bad : \"bug\" };" + " with (o) {" + " throw 123;" + " }" + "} catch (e) {" + " bad;" + "}"); + QVERIFY(ret.isError()); + QVERIFY(ret.toString().contains(QString::fromLatin1("ReferenceError"))); + } + { + QJSValue ret = eng.evaluate( + "try {" + " o = { bad : \"bug\" };" + " with (o) {" + " throw 123;" + " }" + "} finally {" + " bad;" + "}"); + QVERIFY(ret.isError()); + QVERIFY(ret.toString().contains(QString::fromLatin1("ReferenceError"))); + } + { + eng.clearExceptions(); + QJSValue ret = eng.evaluate( + "o = { bug : \"no bug\" };" + "with (o) {" + " try {" + " throw 123;" + " } finally {" + " bug;" + " }" + "}"); + QVERIFY(ret.isNumber()); + QCOMPARE(ret.toInt32(), 123); + QVERIFY(eng.hasUncaughtException()); + } + { + eng.clearExceptions(); + QJSValue ret = eng.evaluate( + "o = { bug : \"no bug\" };" + "with (o) {" + " throw 123;" + "}"); + QVERIFY(ret.isNumber()); + QJSValue ret2 = eng.evaluate("bug"); + QVERIFY(ret2.isError()); + QVERIFY(ret2.toString().contains(QString::fromLatin1("ReferenceError"))); + } +} + +#if 0 // ###FIXME: No QScriptEngineAgent API +class TestAgent : public QScriptEngineAgent +{ +public: + TestAgent(QScriptEngine *engine) : QScriptEngineAgent(engine) {} +}; + +void tst_QJSEngine::getSetAgent_ownership() +{ + // engine deleted before agent --> agent deleted too + QScriptEngine *eng = new QScriptEngine; + QCOMPARE(eng->agent(), (QScriptEngineAgent*)0); + TestAgent *agent = new TestAgent(eng); + eng->setAgent(agent); + QCOMPARE(eng->agent(), (QScriptEngineAgent*)agent); + eng->setAgent(0); // the engine maintains ownership of the old agent + QCOMPARE(eng->agent(), (QScriptEngineAgent*)0); + delete eng; +} + +void tst_QJSEngine::getSetAgent_deleteAgent() +{ + // agent deleted before engine --> engine's agent should become 0 + QScriptEngine *eng = new QScriptEngine; + TestAgent *agent = new TestAgent(eng); + eng->setAgent(agent); + QCOMPARE(eng->agent(), (QScriptEngineAgent*)agent); + delete agent; + QCOMPARE(eng->agent(), (QScriptEngineAgent*)0); + eng->evaluate("(function(){ return 123; })()"); + delete eng; +} + +void tst_QJSEngine::getSetAgent_differentEngine() +{ + QScriptEngine eng; + QScriptEngine eng2; + TestAgent *agent = new TestAgent(&eng); + QTest::ignoreMessage(QtWarningMsg, "QScriptEngine::setAgent(): cannot set agent belonging to different engine"); + eng2.setAgent(agent); + QCOMPARE(eng2.agent(), (QScriptEngineAgent*)0); +} +#endif + +#if 0 // ###FIXME: No QScriptString API +void tst_QJSEngine::reentrancy_stringHandles() +{ + QScriptEngine eng1; + QScriptEngine eng2; + QScriptString s1 = eng1.toStringHandle("foo"); + QScriptString s2 = eng2.toStringHandle("foo"); + QVERIFY(s1 != s2); +} +#endif + +#if 0 // ###FIXME: No processEventsInterval API +void tst_QJSEngine::reentrancy_processEventsInterval() +{ + QScriptEngine eng1; + QScriptEngine eng2; + eng1.setProcessEventsInterval(123); + QCOMPARE(eng2.processEventsInterval(), -1); + eng2.setProcessEventsInterval(456); + QCOMPARE(eng1.processEventsInterval(), 123); +} +#endif + +#if 0 // FIXME: No support for custom types +void tst_QJSEngine::reentrancy_typeConversion() +{ + QScriptEngine eng1; + QScriptEngine eng2; + qScriptRegisterMetaType(&eng1, fooToScriptValue, fooFromScriptValue); + Foo foo; + foo.x = 12; + foo.y = 34; + { + QScriptValue fooVal = qScriptValueFromValue(&eng1, foo); + QVERIFY(fooVal.isObject()); + QVERIFY(!fooVal.isVariant()); + QCOMPARE(fooVal.property("x").toInt32(), 12); + QCOMPARE(fooVal.property("y").toInt32(), 34); + fooVal.setProperty("x", 56); + fooVal.setProperty("y", 78); + + Foo foo2 = eng.fromScriptValue(fooVal); + QCOMPARE(foo2.x, 56); + QCOMPARE(foo2.y, 78); + } + { + QScriptValue fooVal = qScriptValueFromValue(&eng2, foo); + QVERIFY(fooVal.isVariant()); + + Foo foo2 = eng.fromScriptValue(fooVal); + QCOMPARE(foo2.x, 12); + QCOMPARE(foo2.y, 34); + } + QVERIFY(!eng1.defaultPrototype(qMetaTypeId()).isValid()); + QVERIFY(!eng2.defaultPrototype(qMetaTypeId()).isValid()); + QScriptValue proto1 = eng1.newObject(); + eng1.setDefaultPrototype(qMetaTypeId(), proto1); + QVERIFY(!eng2.defaultPrototype(qMetaTypeId()).isValid()); + QScriptValue proto2 = eng2.newObject(); + eng2.setDefaultPrototype(qMetaTypeId(), proto2); + QVERIFY(eng2.defaultPrototype(qMetaTypeId()).isValid()); + QVERIFY(eng1.defaultPrototype(qMetaTypeId()).strictlyEquals(proto1)); +} +#endif + +void tst_QJSEngine::reentrancy_globalObjectProperties() +{ + QJSEngine eng1; + QJSEngine eng2; + QVERIFY(!eng2.globalObject().property("a").isValid()); + eng1.evaluate("a = 10"); + QVERIFY(eng1.globalObject().property("a").isNumber()); + QVERIFY(!eng2.globalObject().property("a").isValid()); + eng2.evaluate("a = 20"); + QVERIFY(eng2.globalObject().property("a").isNumber()); + QCOMPARE(eng1.globalObject().property("a").toInt32(), 10); +} + +void tst_QJSEngine::reentrancy_Array() +{ + // weird bug with JSC backend + { + QJSEngine eng; + QCOMPARE(eng.evaluate("Array()").toString(), QString()); + eng.evaluate("Array.prototype.toString"); + QCOMPARE(eng.evaluate("Array()").toString(), QString()); + } + { + QJSEngine eng; + QCOMPARE(eng.evaluate("Array()").toString(), QString()); + } +} + +void tst_QJSEngine::reentrancy_objectCreation() +{ + QJSEngine eng1; + QJSEngine eng2; + { + QJSValue d1 = eng1.newDate(0); + QJSValue d2 = eng2.newDate(0); + QCOMPARE(d1.toDateTime(), d2.toDateTime()); + QCOMPARE(d2.toDateTime(), d1.toDateTime()); + } + { + QJSValue r1 = eng1.newRegExp("foo", "gim"); + QJSValue r2 = eng2.newRegExp("foo", "gim"); + QCOMPARE(r1.toRegExp(), r2.toRegExp()); + QCOMPARE(r2.toRegExp(), r1.toRegExp()); + } + { + QJSValue o1 = eng1.newQObject(this); + QJSValue o2 = eng2.newQObject(this); + QCOMPARE(o1.toQObject(), o2.toQObject()); + QCOMPARE(o2.toQObject(), o1.toQObject()); + } +#if 0 // ###FIXME: No QScriptEngine::newQMetaObject API + { + QScriptValue mo1 = eng1.newQMetaObject(&staticMetaObject); + QScriptValue mo2 = eng2.newQMetaObject(&staticMetaObject); + QCOMPARE(mo1.toQMetaObject(), mo2.toQMetaObject()); + QCOMPARE(mo2.toQMetaObject(), mo1.toQMetaObject()); + } +#endif +} + +void tst_QJSEngine::jsIncDecNonObjectProperty() +{ + // This is testing ECMA-262 compliance, not C++ API. + + QJSEngine eng; + { + QJSValue ret = eng.evaluate("var a; a.n++"); + QVERIFY(ret.isError()); + QVERIFY(ret.toString().contains(QString::fromLatin1("TypeError"))); + } + { + QJSValue ret = eng.evaluate("var a; a.n--"); + QVERIFY(ret.isError()); + QVERIFY(ret.toString().contains(QString::fromLatin1("TypeError"))); + } + { + QJSValue ret = eng.evaluate("var a = null; a.n++"); + QVERIFY(ret.isError()); + QVERIFY(ret.toString().contains(QString::fromLatin1("TypeError"))); + } + { + QJSValue ret = eng.evaluate("var a = null; a.n--"); + QVERIFY(ret.isError()); + QVERIFY(ret.toString().contains(QString::fromLatin1("TypeError"))); + } + { + QJSValue ret = eng.evaluate("var a; ++a.n"); + QVERIFY(ret.isError()); + QVERIFY(ret.toString().contains(QString::fromLatin1("TypeError"))); + } + { + QJSValue ret = eng.evaluate("var a; --a.n"); + QVERIFY(ret.isError()); + QVERIFY(ret.toString().contains(QString::fromLatin1("TypeError"))); + } + { + QJSValue ret = eng.evaluate("var a; a.n += 1"); + QVERIFY(ret.isError()); + QVERIFY(ret.toString().contains(QString::fromLatin1("TypeError"))); + } + { + QJSValue ret = eng.evaluate("var a; a.n -= 1"); + QVERIFY(ret.isError()); + QVERIFY(ret.toString().contains(QString::fromLatin1("TypeError"))); + } + { + QJSValue ret = eng.evaluate("var a = 'ciao'; a.length++"); + QVERIFY(ret.isNumber()); + QCOMPARE(ret.toInt32(), 4); + } + { + QJSValue ret = eng.evaluate("var a = 'ciao'; a.length--"); + QVERIFY(ret.isNumber()); + QCOMPARE(ret.toInt32(), 4); + } + { + QJSValue ret = eng.evaluate("var a = 'ciao'; ++a.length"); + QVERIFY(ret.isNumber()); + QCOMPARE(ret.toInt32(), 5); + } + { + QJSValue ret = eng.evaluate("var a = 'ciao'; --a.length"); + QVERIFY(ret.isNumber()); + QCOMPARE(ret.toInt32(), 3); + } +} + +#if 0 // ###FIXME: no installTranslatorFunctions API +void tst_QJSEngine::installTranslatorFunctions() +{ + QScriptEngine eng; + QScriptValue global = eng.globalObject(); + QVERIFY(!global.property("qsTranslate").isValid()); + QVERIFY(!global.property("QT_TRANSLATE_NOOP").isValid()); + QVERIFY(!global.property("qsTr").isValid()); + QVERIFY(!global.property("QT_TR_NOOP").isValid()); + QVERIFY(!global.property("qsTrId").isValid()); + QVERIFY(!global.property("QT_TRID_NOOP").isValid()); + QVERIFY(!global.property("String").property("prototype").property("arg").isValid()); + + eng.installTranslatorFunctions(); + QVERIFY(global.property("qsTranslate").isFunction()); + QVERIFY(global.property("QT_TRANSLATE_NOOP").isFunction()); + QVERIFY(global.property("qsTr").isFunction()); + QVERIFY(global.property("QT_TR_NOOP").isFunction()); + QVERIFY(global.property("qsTrId").isFunction()); + QVERIFY(global.property("QT_TRID_NOOP").isFunction()); + QVERIFY(global.property("String").property("prototype").property("arg").isFunction()); + + { + QScriptValue ret = eng.evaluate("qsTr('foo')"); + QVERIFY(ret.isString()); + QCOMPARE(ret.toString(), QString::fromLatin1("foo")); + } + { + QScriptValue ret = eng.evaluate("qsTranslate('foo', 'bar')"); + QVERIFY(ret.isString()); + QCOMPARE(ret.toString(), QString::fromLatin1("bar")); + } + { + QScriptValue ret = eng.evaluate("QT_TR_NOOP('foo')"); + QVERIFY(ret.isString()); + QCOMPARE(ret.toString(), QString::fromLatin1("foo")); + } + { + QScriptValue ret = eng.evaluate("QT_TRANSLATE_NOOP('foo', 'bar')"); + QVERIFY(ret.isString()); + QCOMPARE(ret.toString(), QString::fromLatin1("bar")); + } + { + QScriptValue ret = eng.evaluate("'foo%0'.arg('bar')"); + QEXPECT_FAIL("Custom global object", "FIXME: why we expect that String prototype exists?", Abort); + QVERIFY(ret.isString()); + QCOMPARE(ret.toString(), QString::fromLatin1("foobar")); + } + { + QScriptValue ret = eng.evaluate("'foo%0'.arg(123)"); + QVERIFY(ret.isString()); + QCOMPARE(ret.toString(), QString::fromLatin1("foo123")); + } + { + // Maybe this should throw an error? + QScriptValue ret = eng.evaluate("'foo%0'.arg()"); + QVERIFY(ret.isString()); + QCOMPARE(ret.toString(), QString()); + } + + { + QScriptValue ret = eng.evaluate("qsTrId('foo')"); + QVERIFY(ret.isString()); + QCOMPARE(ret.toString(), QString::fromLatin1("foo")); + } + { + QScriptValue ret = eng.evaluate("QT_TRID_NOOP('foo')"); + QVERIFY(ret.isString()); + QCOMPARE(ret.toString(), QString::fromLatin1("foo")); + } + QVERIFY(eng.evaluate("QT_TRID_NOOP()").isUndefined()); +} + +class TranslationScope +{ +public: + TranslationScope(const QString &fileName) + { + translator.load(fileName); + QCoreApplication::instance()->installTranslator(&translator); + } + ~TranslationScope() + { + QCoreApplication::instance()->removeTranslator(&translator); + } + +private: + QTranslator translator; +}; + +void tst_QJSEngine::translateScript_data() +{ + QTest::addColumn("expression"); + QTest::addColumn("fileName"); + QTest::addColumn("expectedTranslation"); + + QString fileName = QString::fromLatin1("translatable.js"); + // Top-level + QTest::newRow("qsTr('One')@translatable.js") + << QString::fromLatin1("qsTr('One')") << fileName << QString::fromLatin1("En"); + QTest::newRow("qsTr('Hello')@translatable.js") + << QString::fromLatin1("qsTr('Hello')") << fileName << QString::fromLatin1("Hallo"); + // From function + QTest::newRow("(function() { return qsTr('One'); })()@translatable.js") + << QString::fromLatin1("(function() { return qsTr('One'); })()") << fileName << QString::fromLatin1("En"); + QTest::newRow("(function() { return qsTr('Hello'); })()@translatable.js") + << QString::fromLatin1("(function() { return qsTr('Hello'); })()") << fileName << QString::fromLatin1("Hallo"); + // From eval + QTest::newRow("eval('qsTr(\\'One\\')')@translatable.js") + << QString::fromLatin1("eval('qsTr(\\'One\\')')") << fileName << QString::fromLatin1("En"); + QTest::newRow("eval('qsTr(\\'Hello\\')')@translatable.js") + << QString::fromLatin1("eval('qsTr(\\'Hello\\')')") << fileName << QString::fromLatin1("Hallo"); + // Plural + QTest::newRow("qsTr('%n message(s) saved', '', 1)@translatable.js") + << QString::fromLatin1("qsTr('%n message(s) saved', '', 1)") << fileName << QString::fromLatin1("1 melding lagret"); + QTest::newRow("qsTr('%n message(s) saved', '', 3).arg@translatable.js") + << QString::fromLatin1("qsTr('%n message(s) saved', '', 3)") << fileName << QString::fromLatin1("3 meldinger lagret"); + + // Top-level + QTest::newRow("qsTranslate('FooContext', 'Two')@translatable.js") + << QString::fromLatin1("qsTranslate('FooContext', 'Two')") << fileName << QString::fromLatin1("To"); + QTest::newRow("qsTranslate('FooContext', 'Goodbye')@translatable.js") + << QString::fromLatin1("qsTranslate('FooContext', 'Goodbye')") << fileName << QString::fromLatin1("Farvel"); + // From eval + QTest::newRow("eval('qsTranslate(\\'FooContext\\', \\'Two\\')')@translatable.js") + << QString::fromLatin1("eval('qsTranslate(\\'FooContext\\', \\'Two\\')')") << fileName << QString::fromLatin1("To"); + QTest::newRow("eval('qsTranslate(\\'FooContext\\', \\'Goodbye\\')')@translatable.js") + << QString::fromLatin1("eval('qsTranslate(\\'FooContext\\', \\'Goodbye\\')')") << fileName << QString::fromLatin1("Farvel"); + + QTest::newRow("qsTranslate('FooContext', 'Goodbye', '', 'UnicodeUTF8')@translatable.js") + << QString::fromLatin1("qsTranslate('FooContext', 'Goodbye', '', 'UnicodeUTF8')") << fileName << QString::fromLatin1("Farvel"); + QTest::newRow("qsTranslate('FooContext', 'Goodbye', '', 'CodecForTr')@translatable.js") + << QString::fromLatin1("qsTranslate('FooContext', 'Goodbye', '', 'CodecForTr')") << fileName << QString::fromLatin1("Farvel"); + + QTest::newRow("qsTranslate('FooContext', 'Goodbye', '', 'UnicodeUTF8', 42)@translatable.js") + << QString::fromLatin1("qsTranslate('FooContext', 'Goodbye', '', 'UnicodeUTF8', 42)") << fileName << QString::fromLatin1("Goodbye"); + + QTest::newRow("qsTr('One', 'not the same one')@translatable.js") + << QString::fromLatin1("qsTr('One', 'not the same one')") << fileName << QString::fromLatin1("Enda en"); + + QTest::newRow("qsTr('One', 'not the same one', 42)@translatable.js") + << QString::fromLatin1("qsTr('One', 'not the same one', 42)") << fileName << QString::fromLatin1("One"); + + // Plural + QTest::newRow("qsTranslate('FooContext', '%n fooish bar(s) found', '', 'UnicodeUTF8', 1)@translatable.js") + << QString::fromLatin1("qsTranslate('FooContext', '%n fooish bar(s) found', '', 'UnicodeUTF8', 1)") << fileName << QString::fromLatin1("1 fooaktig bar funnet"); + QTest::newRow("qsTranslate('FooContext', '%n fooish bar(s) found', '', 'UnicodeUTF8', 2)@translatable.js") + << QString::fromLatin1("qsTranslate('FooContext', '%n fooish bar(s) found', '', 'UnicodeUTF8', 2)") << fileName << QString::fromLatin1("2 fooaktige barer funnet"); + + // Don't exist in translation + QTest::newRow("qsTr('Three')@translatable.js") + << QString::fromLatin1("qsTr('Three')") << fileName << QString::fromLatin1("Three"); + QTest::newRow("qsTranslate('FooContext', 'So long')@translatable.js") + << QString::fromLatin1("qsTranslate('FooContext', 'So long')") << fileName << QString::fromLatin1("So long"); + QTest::newRow("qsTranslate('BarContext', 'Goodbye')@translatable.js") + << QString::fromLatin1("qsTranslate('BarContext', 'Goodbye')") << fileName << QString::fromLatin1("Goodbye"); + + // Translate strings from the second script (translatable2.js) + + QString fileName2 = QString::fromLatin1("translatable2.js"); + QTest::newRow("qsTr('Three')@translatable2.js") + << QString::fromLatin1("qsTr('Three')") << fileName2 << QString::fromLatin1("Tre"); + QTest::newRow("qsTr('Happy birthday!')@translatable2.js") + << QString::fromLatin1("qsTr('Happy birthday!')") << fileName2 << QString::fromLatin1("Gratulerer med dagen!"); + + // Not translated because translation is only in translatable.js + QTest::newRow("qsTr('One')@translatable2.js") + << QString::fromLatin1("qsTr('One')") << fileName2 << QString::fromLatin1("One"); + QTest::newRow("(function() { return qsTr('One'); })()@translatable2.js") + << QString::fromLatin1("(function() { return qsTr('One'); })()") << fileName2 << QString::fromLatin1("One"); + + // For qsTranslate() the filename shouldn't matter + QTest::newRow("qsTranslate('FooContext', 'Two')@translatable2.js") + << QString::fromLatin1("qsTranslate('FooContext', 'Two')") << fileName2 << QString::fromLatin1("To"); + QTest::newRow("qsTranslate('BarContext', 'Congratulations!')@translatable.js") + << QString::fromLatin1("qsTranslate('BarContext', 'Congratulations!')") << fileName << QString::fromLatin1("Gratulerer!"); +} + +void tst_QJSEngine::translateScript() +{ + QFETCH(QString, expression); + QFETCH(QString, fileName); + QFETCH(QString, expectedTranslation); + + QScriptEngine engine; + + TranslationScope tranScope(":/translations/translatable_la"); + engine.installTranslatorFunctions(); + + QCOMPARE(engine.evaluate(expression, fileName).toString(), expectedTranslation); + QVERIFY(!engine.hasUncaughtException()); +} + +void tst_QJSEngine::translateScript_crossScript() +{ + QScriptEngine engine; + TranslationScope tranScope(":/translations/translatable_la"); + engine.installTranslatorFunctions(); + + QString fileName = QString::fromLatin1("translatable.js"); + QString fileName2 = QString::fromLatin1("translatable2.js"); + // qsTr() should use the innermost filename as context + engine.evaluate("function foo(s) { return bar(s); }", fileName); + engine.evaluate("function bar(s) { return qsTr(s); }", fileName2); + QCOMPARE(engine.evaluate("bar('Three')", fileName2).toString(), QString::fromLatin1("Tre")); + QCOMPARE(engine.evaluate("bar('Three')", fileName).toString(), QString::fromLatin1("Tre")); + QCOMPARE(engine.evaluate("bar('One')", fileName2).toString(), QString::fromLatin1("One")); + + engine.evaluate("function foo(s) { return bar(s); }", fileName2); + engine.evaluate("function bar(s) { return qsTr(s); }", fileName); + QCOMPARE(engine.evaluate("bar('Three')", fileName2).toString(), QString::fromLatin1("Three")); + QCOMPARE(engine.evaluate("bar('One')", fileName).toString(), QString::fromLatin1("En")); + QCOMPARE(engine.evaluate("bar('One')", fileName2).toString(), QString::fromLatin1("En")); +} + +static QScriptValue callQsTr(QScriptContext *ctx, QScriptEngine *eng) +{ + return eng->globalObject().property("qsTr").call(ctx->thisObject(), ctx->argumentsObject()); +} + +void tst_QJSEngine::translateScript_callQsTrFromNative() +{ + QScriptEngine engine; + TranslationScope tranScope(":/translations/translatable_la"); + engine.installTranslatorFunctions(); + + QString fileName = QString::fromLatin1("translatable.js"); + QString fileName2 = QString::fromLatin1("translatable2.js"); + // Calling qsTr() from a native function + engine.globalObject().setProperty("qsTrProxy", engine.newFunction(callQsTr)); + QCOMPARE(engine.evaluate("qsTrProxy('One')", fileName).toString(), QString::fromLatin1("En")); + QCOMPARE(engine.evaluate("qsTrProxy('One')", fileName2).toString(), QString::fromLatin1("One")); + QCOMPARE(engine.evaluate("qsTrProxy('Three')", fileName).toString(), QString::fromLatin1("Three")); + QCOMPARE(engine.evaluate("qsTrProxy('Three')", fileName2).toString(), QString::fromLatin1("Tre")); +} + +void tst_QJSEngine::translateScript_trNoOp() +{ + QScriptEngine engine; + TranslationScope tranScope(":/translations/translatable_la"); + engine.installTranslatorFunctions(); + + QVERIFY(engine.evaluate("QT_TR_NOOP()").isUndefined()); + QCOMPARE(engine.evaluate("QT_TR_NOOP('One')").toString(), QString::fromLatin1("One")); + + QVERIFY(engine.evaluate("QT_TRANSLATE_NOOP()").isUndefined()); + QVERIFY(engine.evaluate("QT_TRANSLATE_NOOP('FooContext')").isUndefined()); + QCOMPARE(engine.evaluate("QT_TRANSLATE_NOOP('FooContext', 'Two')").toString(), QString::fromLatin1("Two")); +} + +void tst_QJSEngine::translateScript_callQsTrFromCpp() +{ + QScriptEngine engine; + TranslationScope tranScope(":/translations/translatable_la"); + engine.installTranslatorFunctions(); + + // There is no context, but it shouldn't crash + QCOMPARE(engine.globalObject().property("qsTr").call( + QScriptValue(), QScriptValueList() << "One").toString(), QString::fromLatin1("One")); +} + +void tst_QJSEngine::translateWithInvalidArgs_data() +{ + QTest::addColumn("expression"); + QTest::addColumn("expectedError"); + + QTest::newRow("qsTr()") << "qsTr()" << "Error: qsTr() requires at least one argument"; + QTest::newRow("qsTr(123)") << "qsTr(123)" << "Error: qsTr(): first argument (text) must be a string"; + QTest::newRow("qsTr('foo', 123)") << "qsTr('foo', 123)" << "Error: qsTr(): second argument (comment) must be a string"; + QTest::newRow("qsTr('foo', 'bar', 'baz')") << "qsTr('foo', 'bar', 'baz')" << "Error: qsTr(): third argument (n) must be a number"; + QTest::newRow("qsTr('foo', 'bar', true)") << "qsTr('foo', 'bar', true)" << "Error: qsTr(): third argument (n) must be a number"; + + QTest::newRow("qsTranslate()") << "qsTranslate()" << "Error: qsTranslate() requires at least two arguments"; + QTest::newRow("qsTranslate('foo')") << "qsTranslate('foo')" << "Error: qsTranslate() requires at least two arguments"; + QTest::newRow("qsTranslate(123, 'foo')") << "qsTranslate(123, 'foo')" << "Error: qsTranslate(): first argument (context) must be a string"; + QTest::newRow("qsTranslate('foo', 123)") << "qsTranslate('foo', 123)" << "Error: qsTranslate(): second argument (text) must be a string"; + QTest::newRow("qsTranslate('foo', 'bar', 123)") << "qsTranslate('foo', 'bar', 123)" << "Error: qsTranslate(): third argument (comment) must be a string"; + QTest::newRow("qsTranslate('foo', 'bar', 'baz', 123)") << "qsTranslate('foo', 'bar', 'baz', 123)" << "Error: qsTranslate(): fourth argument (encoding) must be a string"; + QTest::newRow("qsTranslate('foo', 'bar', 'baz', 'zab', 'rab')") << "qsTranslate('foo', 'bar', 'baz', 'zab', 'rab')" << "Error: qsTranslate(): fifth argument (n) must be a number"; + QTest::newRow("qsTranslate('foo', 'bar', 'baz', 'zab', 123)") << "qsTranslate('foo', 'bar', 'baz', 'zab', 123)" << "Error: qsTranslate(): invalid encoding 'zab'"; + + QTest::newRow("qsTrId()") << "qsTrId()" << "Error: qsTrId() requires at least one argument"; + QTest::newRow("qsTrId(123)") << "qsTrId(123)" << "TypeError: qsTrId(): first argument (id) must be a string"; + QTest::newRow("qsTrId('foo', 'bar')") << "qsTrId('foo', 'bar')" << "TypeError: qsTrId(): second argument (n) must be a number"; +} + +void tst_QJSEngine::translateWithInvalidArgs() +{ + QFETCH(QString, expression); + QFETCH(QString, expectedError); + QScriptEngine engine; + engine.installTranslatorFunctions(); + QScriptValue result = engine.evaluate(expression); + QVERIFY(result.isError()); + QCOMPARE(result.toString(), expectedError); +} + +void tst_QJSEngine::translationContext_data() +{ + QTest::addColumn("path"); + QTest::addColumn("text"); + QTest::addColumn("expectedTranslation"); + + QTest::newRow("translatable.js") << "translatable.js" << "One" << "En"; + QTest::newRow("/translatable.js") << "/translatable.js" << "One" << "En"; + QTest::newRow("/foo/translatable.js") << "/foo/translatable.js" << "One" << "En"; + QTest::newRow("/foo/bar/translatable.js") << "/foo/bar/translatable.js" << "One" << "En"; + QTest::newRow("./translatable.js") << "./translatable.js" << "One" << "En"; + QTest::newRow("../translatable.js") << "../translatable.js" << "One" << "En"; + QTest::newRow("foo/translatable.js") << "foo/translatable.js" << "One" << "En"; + QTest::newRow("file:///home/qt/translatable.js") << "file:///home/qt/translatable.js" << "One" << "En"; + QTest::newRow(":/resources/translatable.js") << ":/resources/translatable.js" << "One" << "En"; + QTest::newRow("/translatable.js.foo") << "/translatable.js.foo" << "One" << "En"; + QTest::newRow("/translatable.txt") << "/translatable.txt" << "One" << "En"; + QTest::newRow("translatable") << "translatable" << "One" << "En"; + QTest::newRow("foo/translatable") << "foo/translatable" << "One" << "En"; + + QTest::newRow("native separators") + << (QDir::toNativeSeparators(QDir::currentPath()) + QDir::separator() + "translatable.js") + << "One" << "En"; + + QTest::newRow("translatable.js/") << "translatable.js/" << "One" << "One"; + QTest::newRow("nosuchscript.js") << "" << "One" << "One"; + QTest::newRow("(empty)") << "" << "One" << "One"; +} + +void tst_QJSEngine::translationContext() +{ + TranslationScope tranScope(":/translations/translatable_la"); + + QScriptEngine engine; + engine.installTranslatorFunctions(); + + QFETCH(QString, path); + QFETCH(QString, text); + QFETCH(QString, expectedTranslation); + QScriptValue ret = engine.evaluate(QString::fromLatin1("qsTr('%0')").arg(text), path); + QVERIFY(ret.isString()); + QCOMPARE(ret.toString(), expectedTranslation); +} + +void tst_QJSEngine::translateScriptIdBased() +{ + QScriptEngine engine; + + TranslationScope tranScope(":/translations/idtranslatable_la"); + engine.installTranslatorFunctions(); + + QString fileName = QString::fromLatin1("idtranslatable.js"); + + QHash expectedTranslations; + expectedTranslations["qtn_foo_bar"] = "First string"; + expectedTranslations["qtn_needle"] = "Second string"; + expectedTranslations["qtn_haystack"] = "Third string"; + expectedTranslations["qtn_bar_baz"] = "Fourth string"; + + QHash::const_iterator it; + for (it = expectedTranslations.constBegin(); it != expectedTranslations.constEnd(); ++it) { + for (int x = 0; x < 2; ++x) { + QString fn; + if (x) + fn = fileName; + // Top-level + QCOMPARE(engine.evaluate(QString::fromLatin1("qsTrId('%0')") + .arg(it.key()), fn).toString(), + it.value()); + QCOMPARE(engine.evaluate(QString::fromLatin1("QT_TRID_NOOP('%0')") + .arg(it.key()), fn).toString(), + it.key()); + // From function + QCOMPARE(engine.evaluate(QString::fromLatin1("(function() { return qsTrId('%0'); })()") + .arg(it.key()), fn).toString(), + it.value()); + QCOMPARE(engine.evaluate(QString::fromLatin1("(function() { return QT_TRID_NOOP('%0'); })()") + .arg(it.key()), fn).toString(), + it.key()); + } + } + + // Plural form + QCOMPARE(engine.evaluate("qsTrId('qtn_bar_baz', 10)").toString(), + QString::fromLatin1("10 fooish bar(s) found")); + QCOMPARE(engine.evaluate("qsTrId('qtn_foo_bar', 10)").toString(), + QString::fromLatin1("qtn_foo_bar")); // Doesn't have plural +} + +// How to add a new test row: +// - Find a nice list of Unicode characters to choose from +// - Write source string/context/comment in .js using Unicode escape sequences (\uABCD) +// - Update corresponding .ts file (e.g. lupdate foo.js -ts foo.ts -codecfortr UTF-8) +// - Enter translation in Linguist +// - Update corresponding .qm file (e.g. lrelease foo.ts) +// - Evaluate script that performs translation; make sure the correct result is returned +// (e.g. by setting the resulting string as the text of a QLabel and visually verifying +// that it looks the same as what you entered in Linguist :-) ) +// - Generate the expectedTranslation column data using toUtf8().toHex() +void tst_QJSEngine::translateScriptUnicode_data() +{ + QTest::addColumn("expression"); + QTest::addColumn("fileName"); + QTest::addColumn("expectedTranslation"); + + QString fileName = QString::fromLatin1("translatable-unicode.js"); + QTest::newRow("qsTr('H\\u2082O')@translatable-unicode.js") + << QString::fromLatin1("qsTr('H\\u2082O')") << fileName << QString::fromUtf8("\xcd\xbb\xcd\xbc\xcd\xbd"); + QTest::newRow("qsTranslate('\\u010C\\u0101\\u011F\\u0115', 'CO\\u2082')@translatable-unicode.js") + << QString::fromLatin1("qsTranslate('\\u010C\\u0101\\u011F\\u0115', 'CO\\u2082')") << fileName << QString::fromUtf8("\xd7\x91\xd7\x9a\xd7\xa2"); + QTest::newRow("qsTr('\\u0391\\u0392\\u0393')@translatable-unicode.js") + << QString::fromLatin1("qsTr('\\u0391\\u0392\\u0393')") << fileName << QString::fromUtf8("\xd3\x9c\xd2\xb4\xd1\xbc"); + QTest::newRow("qsTranslate('\\u010C\\u0101\\u011F\\u0115', '\\u0414\\u0415\\u0416')@translatable-unicode.js") + << QString::fromLatin1("qsTranslate('\\u010C\\u0101\\u011F\\u0115', '\\u0414\\u0415\\u0416')") << fileName << QString::fromUtf8("\xd8\xae\xd8\xb3\xd8\xb3"); + QTest::newRow("qsTr('H\\u2082O', 'not the same H\\u2082O')@translatable-unicode.js") + << QString::fromLatin1("qsTr('H\\u2082O', 'not the same H\\u2082O')") << fileName << QString::fromUtf8("\xd4\xb6\xd5\x8a\xd5\x92"); + QTest::newRow("qsTr('H\\u2082O')") + << QString::fromLatin1("qsTr('H\\u2082O')") << QString() << QString::fromUtf8("\x48\xe2\x82\x82\x4f"); + QTest::newRow("qsTranslate('\\u010C\\u0101\\u011F\\u0115', 'CO\\u2082')") + << QString::fromLatin1("qsTranslate('\\u010C\\u0101\\u011F\\u0115', 'CO\\u2082')") << QString() << QString::fromUtf8("\xd7\x91\xd7\x9a\xd7\xa2"); +} + +void tst_QJSEngine::translateScriptUnicode() +{ + QFETCH(QString, expression); + QFETCH(QString, fileName); + QFETCH(QString, expectedTranslation); + + QScriptEngine engine; + + TranslationScope tranScope(":/translations/translatable-unicode"); + engine.installTranslatorFunctions(); + + QCOMPARE(engine.evaluate(expression, fileName).toString(), expectedTranslation); + QVERIFY(!engine.hasUncaughtException()); +} + +void tst_QJSEngine::translateScriptUnicodeIdBased_data() +{ + QTest::addColumn("expression"); + QTest::addColumn("expectedTranslation"); + + QTest::newRow("qsTrId('\\u01F8\\u01D2\\u0199\\u01D0\\u01E1'')") + << QString::fromLatin1("qsTrId('\\u01F8\\u01D2\\u0199\\u01D0\\u01E1')") << QString::fromUtf8("\xc6\xa7\xc6\xb0\xc6\x88\xc8\xbc\xc8\x9d\xc8\xbf\xc8\x99"); + QTest::newRow("qsTrId('\\u0191\\u01CE\\u0211\\u0229\\u019C\\u018E\\u019A\\u01D0')") + << QString::fromLatin1("qsTrId('\\u0191\\u01CE\\u0211\\u0229\\u019C\\u018E\\u019A\\u01D0')") << QString::fromUtf8("\xc7\xa0\xc8\xa1\xc8\x8b\xc8\x85\xc8\x95"); + QTest::newRow("qsTrId('\\u0181\\u01A1\\u0213\\u018F\\u018C', 10)") + << QString::fromLatin1("qsTrId('\\u0181\\u01A1\\u0213\\u018F\\u018C', 10)") << QString::fromUtf8("\x31\x30\x20\xc6\x92\xc6\xa1\xc7\x92\x28\xc8\x99\x29"); + QTest::newRow("qsTrId('\\u0181\\u01A1\\u0213\\u018F\\u018C')") + << QString::fromLatin1("qsTrId('\\u0181\\u01A1\\u0213\\u018F\\u018C')") << QString::fromUtf8("\xc6\x91\xc6\xb0\xc7\xb9"); + QTest::newRow("qsTrId('\\u01CD\\u0180\\u01A8\\u0190\\u019E\\u01AB')") + << QString::fromLatin1("qsTrId('\\u01CD\\u0180\\u01A8\\u0190\\u019E\\u01AB')") << QString::fromUtf8("\xc7\x8d\xc6\x80\xc6\xa8\xc6\x90\xc6\x9e\xc6\xab"); +} + +void tst_QJSEngine::translateScriptUnicodeIdBased() +{ + QFETCH(QString, expression); + QFETCH(QString, expectedTranslation); + + QScriptEngine engine; + + TranslationScope tranScope(":/translations/idtranslatable-unicode"); + engine.installTranslatorFunctions(); + + QCOMPARE(engine.evaluate(expression).toString(), expectedTranslation); + QVERIFY(!engine.hasUncaughtException()); +} + +void tst_QJSEngine::translateFromBuiltinCallback() +{ + QScriptEngine eng; + eng.installTranslatorFunctions(); + + // Callback has no translation context. + eng.evaluate("function foo() { qsTr('foo'); }"); + + // Stack at translation time will be: + // qsTr, foo, forEach, global + // qsTr() needs to walk to the outer-most (global) frame before it finds + // a translation context, and this should not crash. + eng.evaluate("[10,20].forEach(foo)", "script.js"); +} +#endif + +#if 0 // ###FIXME: No QScriptValue::scope API +void tst_QJSEngine::functionScopes() +{ + QScriptEngine eng; + { + // top-level functions have only the global object in their scope + QScriptValue fun = eng.evaluate("(function() {})"); + QVERIFY(fun.isFunction()); + QEXPECT_FAIL("", "QScriptValue::scope() is internal, not implemented", Abort); + QVERIFY(fun.scope().isObject()); + QVERIFY(fun.scope().strictlyEquals(eng.globalObject())); + QVERIFY(!eng.globalObject().scope().isValid()); + } + { + QScriptValue fun = eng.globalObject().property("Object"); + QVERIFY(fun.isFunction()); + // native built-in functions don't have scope + QVERIFY(!fun.scope().isValid()); + } + { + // closure + QScriptValue fun = eng.evaluate("(function(arg) { var foo = arg; return function() { return foo; }; })(123)"); + QVERIFY(fun.isFunction()); + { + QScriptValue ret = fun.call(); + QVERIFY(ret.isNumber()); + QCOMPARE(ret.toInt32(), 123); + } + QScriptValue scope = fun.scope(); + QVERIFY(scope.isObject()); + { + QScriptValue ret = scope.property("foo"); + QVERIFY(ret.isNumber()); + QCOMPARE(ret.toInt32(), 123); + QCOMPARE(scope.propertyFlags("foo"), QScriptValue::Undeletable); + } + { + QScriptValue ret = scope.property("arg"); + QVERIFY(ret.isNumber()); + QCOMPARE(ret.toInt32(), 123); + QCOMPARE(scope.propertyFlags("arg"), QScriptValue::Undeletable | QScriptValue::SkipInEnumeration); + } + + scope.setProperty("foo", 456); + { + QScriptValue ret = fun.call(); + QVERIFY(ret.isNumber()); + QCOMPARE(ret.toInt32(), 456); + } + + scope = scope.scope(); + QVERIFY(scope.isObject()); + QVERIFY(scope.strictlyEquals(eng.globalObject())); + } +} +#endif + +#if 0 // ###FIXME: No QScriptContext API +static QScriptValue counter_inner(QScriptContext *ctx, QScriptEngine *) +{ + QScriptValue outerAct = ctx->callee().scope(); + double count = outerAct.property("count").toNumber(); + outerAct.setProperty("count", count+1); + return count; +} + +static QScriptValue counter(QScriptContext *ctx, QScriptEngine *eng) +{ + QScriptValue act = ctx->activationObject(); + act.setProperty("count", ctx->argument(0).toInt32()); + QScriptValue result = eng->newFunction(counter_inner); + result.setScope(act); + return result; +} + +static QScriptValue counter_hybrid(QScriptContext *ctx, QScriptEngine *eng) +{ + QScriptValue act = ctx->activationObject(); + act.setProperty("count", ctx->argument(0).toInt32()); + return eng->evaluate("(function() { return count++; })"); +} + +void tst_QJSEngine::nativeFunctionScopes() +{ + QScriptEngine eng; + { + QScriptValue fun = eng.newFunction(counter); + QScriptValue cnt = fun.call(QScriptValue(), QScriptValueList() << 123); + QVERIFY(cnt.isFunction()); + { + QScriptValue ret = cnt.call(); + QVERIFY(ret.isNumber()); + QEXPECT_FAIL("", "QScriptValue::setScope not implemented", Continue); + QCOMPARE(ret.toInt32(), 123); + } + } + { + QScriptValue fun = eng.newFunction(counter_hybrid); + QScriptValue cnt = fun.call(QScriptValue(), QScriptValueList() << 123); + QVERIFY(cnt.isFunction()); + { + QScriptValue ret = cnt.call(); + QVERIFY(ret.isNumber()); + QCOMPARE(ret.toInt32(), 123); + } + } + + //from http://doc.trolltech.com/latest/qtscript.html#nested-functions-and-the-scope-chain + { + QScriptEngine eng; + eng.evaluate("function counter() { var count = 0; return function() { return count++; } }\n" + "var c1 = counter(); var c2 = counter(); "); + QCOMPARE(eng.evaluate("c1()").toString(), QString::fromLatin1("0")); + QCOMPARE(eng.evaluate("c1()").toString(), QString::fromLatin1("1")); + QCOMPARE(eng.evaluate("c2()").toString(), QString::fromLatin1("0")); + QCOMPARE(eng.evaluate("c2()").toString(), QString::fromLatin1("1")); + QVERIFY(!eng.hasUncaughtException()); + } + { + QScriptEngine eng; + eng.globalObject().setProperty("counter", eng.newFunction(counter)); + eng.evaluate("var c1 = counter(); var c2 = counter(); "); + QCOMPARE(eng.evaluate("c1()").toString(), QString::fromLatin1("0")); + QEXPECT_FAIL("", "QScriptValue::setScope not implemented", Continue); + QCOMPARE(eng.evaluate("c1()").toString(), QString::fromLatin1("1")); + QCOMPARE(eng.evaluate("c2()").toString(), QString::fromLatin1("0")); + QEXPECT_FAIL("", "QScriptValue::setScope not implemented", Continue); + QCOMPARE(eng.evaluate("c2()").toString(), QString::fromLatin1("1")); + QVERIFY(!eng.hasUncaughtException()); + } + { + QScriptEngine eng; + eng.globalObject().setProperty("counter", eng.newFunction(counter_hybrid)); + eng.evaluate("var c1 = counter(); var c2 = counter(); "); + QCOMPARE(eng.evaluate("c1()").toString(), QString::fromLatin1("0")); + QCOMPARE(eng.evaluate("c1()").toString(), QString::fromLatin1("1")); + QCOMPARE(eng.evaluate("c2()").toString(), QString::fromLatin1("0")); + QCOMPARE(eng.evaluate("c2()").toString(), QString::fromLatin1("1")); + QVERIFY(!eng.hasUncaughtException()); + } +} +#endif + +#if 0 // ###FIXME: No QScriptProgram API +static QScriptValue createProgram(QScriptContext *ctx, QScriptEngine *eng) +{ + QString code = ctx->argument(0).toString(); + QScriptProgram result(code); + return qScriptValueFromValue(eng, result); +} + +void tst_QJSEngine::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)); + } +} + +void tst_QJSEngine::evaluateProgram_customScope() +{ + QScriptEngine eng; + { + QScriptProgram program("a"); + QVERIFY(!program.isNull()); + { + QScriptValue ret = eng.evaluate(program); + QVERIFY(ret.isError()); + QCOMPARE(ret.toString(), QString::fromLatin1("ReferenceError: a is not defined")); + } + + 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(); + } +} + +void tst_QJSEngine::evaluateProgram_closure() +{ + QScriptEngine eng; + { + 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()); + } + } +} + +void tst_QJSEngine::evaluateProgram_executeLater() +{ + QScriptEngine eng; + // Program created in a function call, then executed later + { + QScriptValue fun = eng.newFunction(createProgram); + QScriptProgram program = qscriptvalue_cast( + 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: a is not defined")); + } + eng.globalObject().setProperty("a", 122); + { + QScriptValue ret = eng.evaluate(program); + QVERIFY(!ret.isError()); + QVERIFY(ret.isNumber()); + QCOMPARE(ret.toInt32(), 123); + } + } +} + +void tst_QJSEngine::evaluateProgram_multipleEngines() +{ + QScriptEngine eng; + { + 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); + } + } + } +} + +void tst_QJSEngine::evaluateProgram_empty() +{ + QScriptEngine eng; + { + QScriptProgram program; + QVERIFY(program.isNull()); + QScriptValue ret = eng.evaluate(program); + QVERIFY(!ret.isValid()); + } +} +#endif + +#if 0 // ###FIXME: No ScriptOwnership API +void tst_QJSEngine::collectGarbageAfterConnect() +{ + // QTBUG-6366 + QScriptEngine engine; + QPointer widget = new QWidget; + engine.globalObject().setProperty( + "widget", engine.newQObject(widget, QScriptEngine::ScriptOwnership)); + QVERIFY(engine.evaluate("widget.customContextMenuRequested.connect(\n" + " function() { print('hello'); }\n" + ");") + .isUndefined()); + QVERIFY(widget != 0); + engine.evaluate("widget = null;"); + // The connection should not keep the widget alive. + collectGarbage_helper(engine); + QVERIFY(widget == 0); +} +#endif + +#if 0 // ###FIXME: No QScriptContext API +void tst_QJSEngine::collectGarbageAfterNativeArguments() +{ + // QTBUG-17788 + QScriptEngine eng; + QScriptContext *ctx = eng.pushContext(); + QScriptValue arguments = ctx->argumentsObject(); + // Shouldn't crash when marking the arguments object. + collectGarbage_helper(eng); +} + +static QScriptValue constructQObjectFromThisObject(QScriptContext *ctx, QScriptEngine *eng) +{ + if (!ctx->isCalledAsConstructor()) { + qWarning("%s: ctx->isCalledAsConstructor() returned false", Q_FUNC_INFO); + return QScriptValue(); + } + return eng->newQObject(ctx->thisObject(), new QObject, QScriptEngine::ScriptOwnership); +} + +void tst_QJSEngine::promoteThisObjectToQObjectInConstructor() +{ + QScriptEngine engine; + QScriptValue ctor = engine.newFunction(constructQObjectFromThisObject); + engine.globalObject().setProperty("Ctor", ctor); + QScriptValue object = engine.evaluate("new Ctor"); + QVERIFY(!object.isError()); + QVERIFY(object.isQObject()); + QVERIFY(object.toQObject() != 0); + QVERIFY(object.property("objectName").isString()); + QVERIFY(object.property("deleteLater").isFunction()); +} +#endif + +static QRegExp minimal(QRegExp r) { r.setMinimal(true); return r; } + +void tst_QJSEngine::qRegExpInport_data() +{ + QTest::addColumn("rx"); + QTest::addColumn("string"); + QTest::addColumn("matched"); + + QTest::newRow("normal") << QRegExp("(test|foo)") << "test _ foo _ test _ Foo"; + QTest::newRow("normal2") << QRegExp("(Test|Foo)") << "test _ foo _ test _ Foo"; + QTest::newRow("case insensitive)") << QRegExp("(test|foo)", Qt::CaseInsensitive) << "test _ foo _ test _ Foo"; + QTest::newRow("case insensitive2)") << QRegExp("(Test|Foo)", Qt::CaseInsensitive) << "test _ foo _ test _ Foo"; + QTest::newRow("b(a*)(b*)") << QRegExp("b(a*)(b*)", Qt::CaseInsensitive) << "aaabbBbaAabaAaababaaabbaaab"; + QTest::newRow("greedy") << QRegExp("a*(a*)", Qt::CaseInsensitive, QRegExp::RegExp2) << "aaaabaaba"; + // this one will fail because we do not support the QRegExp::RegExp in JSC + //QTest::newRow("not_greedy") << QRegExp("a*(a*)", Qt::CaseInsensitive, QRegExp::RegExp) << "aaaabaaba"; + QTest::newRow("willcard") << QRegExp("*.txt", Qt::CaseSensitive, QRegExp::Wildcard) << "file.txt"; + QTest::newRow("willcard 2") << QRegExp("a?b.txt", Qt::CaseSensitive, QRegExp::Wildcard) << "ab.txt abb.rtc acb.txt"; + QTest::newRow("slash") << QRegExp("g/.*/s", Qt::CaseInsensitive, QRegExp::RegExp2) << "string/string/string"; + QTest::newRow("slash2") << QRegExp("g / .* / s", Qt::CaseInsensitive, QRegExp::RegExp2) << "string / string / string"; + QTest::newRow("fixed") << QRegExp("a*aa.a(ba)*a\\ba", Qt::CaseInsensitive, QRegExp::FixedString) << "aa*aa.a(ba)*a\\ba"; + QTest::newRow("fixed insensitive") << QRegExp("A*A", Qt::CaseInsensitive, QRegExp::FixedString) << "a*A A*a A*A a*a"; + QTest::newRow("fixed sensitive") << QRegExp("A*A", Qt::CaseSensitive, QRegExp::FixedString) << "a*A A*a A*A a*a"; + QTest::newRow("html") << QRegExp("(.*)", Qt::CaseSensitive, QRegExp::RegExp2) << "bolditalicbold"; + QTest::newRow("html minimal") << minimal(QRegExp("(.*)", Qt::CaseSensitive, QRegExp::RegExp2)) << "bolditalicbold"; + QTest::newRow("aaa") << QRegExp("a{2,5}") << "aAaAaaaaaAa"; + QTest::newRow("aaa minimal") << minimal(QRegExp("a{2,5}")) << "aAaAaaaaaAa"; + QTest::newRow("minimal") << minimal(QRegExp(".*\\} [*8]")) << "}?} ?} *"; + QTest::newRow(".? minimal") << minimal(QRegExp(".?")) << ".?"; + QTest::newRow(".+ minimal") << minimal(QRegExp(".+")) << ".+"; + QTest::newRow("[.?] minimal") << minimal(QRegExp("[.?]")) << ".?"; + QTest::newRow("[.+] minimal") << minimal(QRegExp("[.+]")) << ".+"; +} + +void tst_QJSEngine::qRegExpInport() +{ + QFETCH(QRegExp, rx); + QFETCH(QString, string); + + QJSEngine eng; + QJSValue rexp; + rexp = eng.newRegExp(rx); + + QCOMPARE(rexp.isValid(), true); + QCOMPARE(rexp.isRegExp(), true); + QVERIFY(rexp.isFunction()); + + QJSValue func = eng.evaluate("(function(string, regexp) { return string.match(regexp); })"); + QJSValue result = func.call(QJSValue(), QJSValueList() << string << rexp); + + rx.indexIn(string); + for (int i = 0; i <= rx.captureCount(); i++) { + QCOMPARE(result.property(i).toString(), rx.cap(i)); + } +} + +// QScriptValue::toDateTime() returns a local time, whereas JS dates +// are always stored as UTC. QtScript must respect the current time +// zone, and correctly adjust for daylight saving time that may be in +// effect at a given date (QTBUG-9770). +void tst_QJSEngine::dateRoundtripJSQtJS() +{ + uint secs = QDateTime(QDate(2009, 1, 1)).toUTC().toTime_t(); + QJSEngine eng; + for (int i = 0; i < 8000; ++i) { + QJSValue jsDate = eng.evaluate(QString::fromLatin1("new Date(%0)").arg(secs * 1000.0)); + QDateTime qtDate = jsDate.toDateTime(); + QJSValue jsDate2 = eng.newDate(qtDate); + if (jsDate2.toNumber() != jsDate.toNumber()) + QFAIL(qPrintable(jsDate.toString())); + secs += 2*60*60; + } +} + +void tst_QJSEngine::dateRoundtripQtJSQt() +{ + QDateTime qtDate = QDateTime(QDate(2009, 1, 1)); + QJSEngine eng; + for (int i = 0; i < 8000; ++i) { + QJSValue jsDate = eng.newDate(qtDate); + QDateTime qtDate2 = jsDate.toDateTime(); + if (qtDate2 != qtDate) + QFAIL(qPrintable(qtDate.toString())); + qtDate = qtDate.addSecs(2*60*60); + } +} + +void tst_QJSEngine::dateConversionJSQt() +{ + uint secs = QDateTime(QDate(2009, 1, 1)).toUTC().toTime_t(); + QJSEngine eng; + for (int i = 0; i < 8000; ++i) { + QJSValue jsDate = eng.evaluate(QString::fromLatin1("new Date(%0)").arg(secs * 1000.0)); + QDateTime qtDate = jsDate.toDateTime(); + QString qtUTCDateStr = qtDate.toUTC().toString(Qt::ISODate); + QString jsUTCDateStr = jsDate.property("toISOString").call(jsDate).toString(); + jsUTCDateStr.remove(jsUTCDateStr.length() - 5, 4); // get rid of milliseconds (".000") + if (qtUTCDateStr != jsUTCDateStr) + QFAIL(qPrintable(jsDate.toString())); + secs += 2*60*60; + } +} + +void tst_QJSEngine::dateConversionQtJS() +{ + QDateTime qtDate = QDateTime(QDate(2009, 1, 1)); + QJSEngine eng; + for (int i = 0; i < 8000; ++i) { + QJSValue jsDate = eng.newDate(qtDate); + QString jsUTCDateStr = jsDate.property("toISOString").call(jsDate).toString(); + jsUTCDateStr.remove(jsUTCDateStr.length() - 5, 4); // get rid of milliseconds (".000") + QString qtUTCDateStr = qtDate.toUTC().toString(Qt::ISODate); + if (jsUTCDateStr != qtUTCDateStr) + QFAIL(qPrintable(qtDate.toString())); + qtDate = qtDate.addSecs(2*60*60); + } +} + +#if 0 // ###FIXME: No QScriptContext API +static QScriptValue createAnotherEngine(QScriptContext *, QScriptEngine *) +{ + QScriptEngine eng; + eng.evaluate("function foo(x, y) { return x + y; }" ); + eng.evaluate("hello = 5; world = 6" ); + return eng.evaluate("foo(hello,world)").toInt32(); +} + + +void tst_QJSEngine::reentrency() +{ + QScriptEngine eng; + eng.globalObject().setProperty("foo", eng.newFunction(createAnotherEngine)); + eng.evaluate("function bar() { return foo(); } hello = 9; function getHello() { return hello; }"); + QCOMPARE(eng.evaluate("foo() + getHello() + foo()").toInt32(), 5+6 + 9 + 5+6); + QCOMPARE(eng.evaluate("foo").call().toInt32(), 5+6); + QCOMPARE(eng.evaluate("hello").toInt32(), 9); + QCOMPARE(eng.evaluate("foo() + hello").toInt32(), 5+6+9); +} +#endif + +#if 0 // ###FIXME: No QSCriptDeclarativeClass API +void tst_QJSEngine::newFixedStaticScopeObject() +{ + // "Static scope objects" is an optimization we do for QML. + // It enables the creation of JS objects that can guarantee to the + // compiler that no properties will be added or removed. This enables + // the compiler to generate a very simple (fast) property access, as + // opposed to a full virtual lookup. Due to the inherent use of scope + // chains in QML, this can make a huge difference (10x improvement for + // benchmark in QTBUG-8576). + // Ideally we would not need a special object type for this, and the + // VM would dynamically optimize it to be fast... + // See also QScriptEngine benchmark. + + QScriptEngine eng; + static const int propertyCount = 4; + QString names[] = { "foo", "bar", "baz", "Math" }; + QScriptValue values[] = { 123, "ciao", true, false }; + QScriptValue::PropertyFlags flags[] = { QScriptValue::Undeletable, + QScriptValue::ReadOnly | QScriptValue::Undeletable, + QScriptValue::SkipInEnumeration | QScriptValue::Undeletable, + QScriptValue::Undeletable }; + QScriptValue scope = QScriptDeclarativeClass::newStaticScopeObject(&eng, propertyCount, names, values, flags); + + // Query property. + for (int i = 0; i < propertyCount; ++i) { + for (int x = 0; x < 2; ++x) { + if (x) { + // Properties can't be deleted. + scope.setProperty(names[i], QScriptValue()); + } + QVERIFY(scope.property(names[i]).equals(values[i])); + QCOMPARE(scope.propertyFlags(names[i]), flags[i]); + } + } + + // Property that doesn't exist. + QVERIFY(!scope.property("noSuchProperty").isValid()); + QCOMPARE(scope.propertyFlags("noSuchProperty"), QScriptValue::PropertyFlags()); + + // Write to writable property. + { + QScriptValue oldValue = scope.property("foo"); + QVERIFY(oldValue.isNumber()); + QScriptValue newValue = oldValue.toNumber() * 2; + scope.setProperty("foo", newValue); + QVERIFY(scope.property("foo").equals(newValue)); + scope.setProperty("foo", oldValue); + QVERIFY(scope.property("foo").equals(oldValue)); + } + + // Write to read-only property. + scope.setProperty("bar", 456); + QVERIFY(scope.property("bar").equals("ciao")); + + // Iterate. + { + QScriptValueIterator it(scope); + QSet iteratedNames; + while (it.hasNext()) { + it.next(); + iteratedNames.insert(it.name()); + } + for (int i = 0; i < propertyCount; ++i) + QVERIFY(iteratedNames.contains(names[i])); + } + + // Push it on the scope chain of a new context. + QScriptContext *ctx = eng.pushContext(); + ctx->pushScope(scope); + QCOMPARE(ctx->scopeChain().size(), 3); // Global Object, native activation, custom scope + QEXPECT_FAIL("", "activationObject has not been implemented yet", Continue); + QVERIFY(ctx->activationObject().equals(scope)); + + // Read property from JS. + for (int i = 0; i < propertyCount; ++i) { + for (int x = 0; x < 2; ++x) { + if (x) { + // Property can't be deleted from JS. + QScriptValue ret = eng.evaluate(QString::fromLatin1("delete %0").arg(names[i])); + QVERIFY(ret.equals(false)); + } + QVERIFY(eng.evaluate(names[i]).equals(values[i])); + } + } + + // Property that doesn't exist. + QVERIFY(eng.evaluate("noSuchProperty").equals("ReferenceError: noSuchProperty is not defined")); + + // Write property from JS. + { + QScriptValue oldValue = eng.evaluate("foo"); + QVERIFY(oldValue.isNumber()); + QScriptValue newValue = oldValue.toNumber() * 2; + QVERIFY(eng.evaluate("foo = foo * 2; foo").equals(newValue)); + scope.setProperty("foo", oldValue); + QVERIFY(eng.evaluate("foo").equals(oldValue)); + } + + // Write to read-only property. + QVERIFY(eng.evaluate("bar = 456; bar").equals("ciao")); + + // Create a closure and return properties from there. + { + QScriptValue props = eng.evaluate("(function() { var baz = 'shadow'; return [foo, bar, baz, Math, Array]; })()"); + QVERIFY(props.isArray()); + // "foo" and "bar" come from scope object. + QVERIFY(props.property(0).equals(scope.property("foo"))); + QVERIFY(props.property(1).equals(scope.property("bar"))); + // "baz" shadows property in scope object. + QVERIFY(props.property(2).equals("shadow")); + // "Math" comes from scope object, and shadows Global Object's "Math". + QVERIFY(props.property(3).equals(scope.property("Math"))); + QVERIFY(!props.property(3).equals(eng.globalObject().property("Math"))); + // "Array" comes from Global Object. + QVERIFY(props.property(4).equals(eng.globalObject().property("Array"))); + } + + // As with normal JS, assigning to an undefined variable will create + // the property on the Global Object, not the inner scope. + QVERIFY(!eng.globalObject().property("newProperty").isValid()); + QVERIFY(eng.evaluate("(function() { newProperty = 789; })()").isUndefined()); + QVERIFY(!scope.property("newProperty").isValid()); + QVERIFY(eng.globalObject().property("newProperty").isNumber()); + + // Nested static scope. + { + static const int propertyCount2 = 2; + QString names2[] = { "foo", "hum" }; + QScriptValue values2[] = { 321, "hello" }; + QScriptValue::PropertyFlags flags2[] = { QScriptValue::Undeletable, + QScriptValue::ReadOnly | QScriptValue::Undeletable }; + QScriptValue scope2 = QScriptDeclarativeClass::newStaticScopeObject(&eng, propertyCount2, names2, values2, flags2); + ctx->pushScope(scope2); + + // "foo" shadows scope.foo. + QVERIFY(eng.evaluate("foo").equals(scope2.property("foo"))); + QVERIFY(!eng.evaluate("foo").equals(scope.property("foo"))); + // "hum" comes from scope2. + QVERIFY(eng.evaluate("hum").equals(scope2.property("hum"))); + // "Array" comes from Global Object. + QVERIFY(eng.evaluate("Array").equals(eng.globalObject().property("Array"))); + + ctx->popScope(); + } + + QScriptValue fun = eng.evaluate("(function() { return foo; })"); + QVERIFY(fun.isFunction()); + eng.popContext(); + // Function's scope chain persists after popContext(). + QVERIFY(fun.call().equals(scope.property("foo"))); +} + +void tst_QJSEngine::newGrowingStaticScopeObject() +{ + // The main use case for a growing static scope object is to set it as + // the activation object of a QScriptContext, so that all JS variable + // declarations end up in that object. It needs to be "growable" since + // we don't know in advance how many variables a script will declare. + + QScriptEngine eng; + QScriptValue scope = QScriptDeclarativeClass::newStaticScopeObject(&eng); + + // Initially empty. + QVERIFY(!QScriptValueIterator(scope).hasNext()); + QVERIFY(!scope.property("foo").isValid()); + + // Add a static property. + scope.setProperty("foo", 123); + QVERIFY(scope.property("foo").equals(123)); + QEXPECT_FAIL("", "FIXME: newStaticScopeObject not properly implemented", Abort); + QCOMPARE(scope.propertyFlags("foo"), QScriptValue::Undeletable); + + // Modify existing property. + scope.setProperty("foo", 456); + QVERIFY(scope.property("foo").equals(456)); + + // Add a read-only property. + scope.setProperty("bar", "ciao", QScriptValue::ReadOnly); + QVERIFY(scope.property("bar").equals("ciao")); + QCOMPARE(scope.propertyFlags("bar"), QScriptValue::ReadOnly | QScriptValue::Undeletable); + + // Attempt to modify read-only property. + scope.setProperty("bar", "hello"); + QVERIFY(scope.property("bar").equals("ciao")); + + // Properties can't be deleted. + scope.setProperty("foo", QScriptValue()); + QVERIFY(scope.property("foo").equals(456)); + scope.setProperty("bar", QScriptValue()); + QVERIFY(scope.property("bar").equals("ciao")); + + // Iterate. + { + QScriptValueIterator it(scope); + QSet iteratedNames; + while (it.hasNext()) { + it.next(); + iteratedNames.insert(it.name()); + } + QCOMPARE(iteratedNames.size(), 2); + QVERIFY(iteratedNames.contains("foo")); + QVERIFY(iteratedNames.contains("bar")); + } + + // Push it on the scope chain of a new context. + QScriptContext *ctx = eng.pushContext(); + ctx->pushScope(scope); + QCOMPARE(ctx->scopeChain().size(), 3); // Global Object, native activation, custom scope + QVERIFY(ctx->activationObject().equals(scope)); + + // Read property from JS. + QVERIFY(eng.evaluate("foo").equals(scope.property("foo"))); + QVERIFY(eng.evaluate("bar").equals(scope.property("bar"))); + + // Write property from JS. + { + QScriptValue oldValue = eng.evaluate("foo"); + QVERIFY(oldValue.isNumber()); + QScriptValue newValue = oldValue.toNumber() * 2; + QVERIFY(eng.evaluate("foo = foo * 2; foo").equals(newValue)); + scope.setProperty("foo", oldValue); + QVERIFY(eng.evaluate("foo").equals(oldValue)); + } + + // Write to read-only property. + QVERIFY(eng.evaluate("bar = 456; bar").equals("ciao")); + + // Shadow property. + QVERIFY(eng.evaluate("Math").equals(eng.globalObject().property("Math"))); + scope.setProperty("Math", "fake Math"); + QVERIFY(eng.evaluate("Math").equals(scope.property("Math"))); + + // Variable declarations will create properties on the scope. + eng.evaluate("var baz = 456"); + QVERIFY(scope.property("baz").equals(456)); + + // Function declarations will create properties on the scope. + eng.evaluate("function fun() { return baz; }"); + QVERIFY(scope.property("fun").isFunction()); + QVERIFY(scope.property("fun").call().equals(scope.property("baz"))); + + // Demonstrate the limitation of a growable static scope: Once a function that + // uses the scope has been compiled, it won't pick up properties that are added + // to the scope later. + { + QScriptValue fun = eng.evaluate("(function() { return futureProperty; })"); + QVERIFY(fun.isFunction()); + QVERIFY(fun.call().toString().contains(QString::fromLatin1("ReferenceError"))); + scope.setProperty("futureProperty", "added after the function was compiled"); + // If scope were dynamic, this would return the new property. + QVERIFY(fun.call().toString().contains(QString::fromLatin1("ReferenceError"))); + } + + eng.popContext(); +} +#endif + +#if 0 // ###FIXME: No QScript MetaObject API +QT_BEGIN_NAMESPACE +Q_SCRIPT_DECLARE_QMETAOBJECT(QStandardItemModel, QObject*) +QT_END_NAMESPACE + +void tst_QJSEngine::scriptValueFromQMetaObject() +{ + QScriptEngine eng; + { + QScriptValue meta = eng.scriptValueFromQMetaObject(); + QVERIFY(meta.isQMetaObject()); + QCOMPARE(meta.toQMetaObject(), &QScriptEngine::staticMetaObject); + // Because of missing Q_SCRIPT_DECLARE_QMETAOBJECT() for QScriptEngine. + QEXPECT_FAIL("", "FIXME: because construct never returns invalid values", Continue); + QVERIFY(!meta.construct().isValid()); + } + { + QScriptValue meta = eng.scriptValueFromQMetaObject(); + QVERIFY(meta.isQMetaObject()); + QCOMPARE(meta.toQMetaObject(), &QStandardItemModel::staticMetaObject); + QScriptValue obj = meta.construct(QScriptValueList() << eng.newQObject(&eng)); + QVERIFY(obj.isQObject()); + QStandardItemModel *model = qobject_cast(obj.toQObject()); + QVERIFY(model != 0); + QCOMPARE(model->parent(), (QObject*)&eng); + } +} +#endif + +QTEST_MAIN(tst_QJSEngine) + +#include "tst_qjsengine.moc" + diff --git a/tests/auto/declarative/qjsvalue/qjsvalue.pro b/tests/auto/declarative/qjsvalue/qjsvalue.pro new file mode 100644 index 0000000000..182196a3ed --- /dev/null +++ b/tests/auto/declarative/qjsvalue/qjsvalue.pro @@ -0,0 +1,10 @@ +load(qttest_p4) +QT += declarative +SOURCES += tst_qjsvalue.cpp +HEADERS += tst_qjsvalue.h + +win32-msvc* { + # With -O2, MSVC takes up to 24 minutes to compile this test! + QMAKE_CXXFLAGS_RELEASE -= -O1 -O2 + QMAKE_CXXFLAGS_RELEASE += -Od +} diff --git a/tests/auto/declarative/qjsvalue/tst_qjsvalue.cpp b/tests/auto/declarative/qjsvalue/tst_qjsvalue.cpp new file mode 100644 index 0000000000..9f9e066003 --- /dev/null +++ b/tests/auto/declarative/qjsvalue/tst_qjsvalue.cpp @@ -0,0 +1,4109 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite 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 "tst_qjsvalue.h" +#include + +//TESTED_CLASS= +//TESTED_FILES= + +QT_BEGIN_NAMESPACE +extern bool qt_script_isJITEnabled(); +QT_END_NAMESPACE + +tst_QJSValue::tst_QJSValue() + : engine(0) +{ +} + +tst_QJSValue::~tst_QJSValue() +{ + if (engine) + delete engine; +} + +void tst_QJSValue::ctor_invalid() +{ + QJSEngine eng; + { + QJSValue v; + QCOMPARE(v.isValid(), false); + QCOMPARE(v.engine(), (QJSEngine *)0); + } +} + +void tst_QJSValue::ctor_undefinedWithEngine() +{ + QJSEngine eng; + { + QJSValue v(&eng, QJSValue::UndefinedValue); + QCOMPARE(v.isValid(), true); + QCOMPARE(v.isUndefined(), true); + QCOMPARE(v.isObject(), false); + QCOMPARE(v.engine(), &eng); + } +} + +void tst_QJSValue::ctor_nullWithEngine() +{ + QJSEngine eng; + { + QJSValue v(&eng, QJSValue::NullValue); + QCOMPARE(v.isValid(), true); + QCOMPARE(v.isNull(), true); + QCOMPARE(v.isObject(), false); + QCOMPARE(v.engine(), &eng); + } +} + +void tst_QJSValue::ctor_boolWithEngine() +{ + QJSEngine eng; + { + QJSValue v(&eng, false); + QCOMPARE(v.isValid(), true); + QCOMPARE(v.isBoolean(), true); + QCOMPARE(v.isBool(), true); + QCOMPARE(v.isObject(), false); + QCOMPARE(v.toBoolean(), false); + QCOMPARE(v.engine(), &eng); + } +} + +void tst_QJSValue::ctor_intWithEngine() +{ + QJSEngine eng; + { + QJSValue v(&eng, int(1)); + QCOMPARE(v.isValid(), true); + QCOMPARE(v.isNumber(), true); + QCOMPARE(v.isObject(), false); + QCOMPARE(v.toNumber(), 1.0); + QCOMPARE(v.engine(), &eng); + } +} + +void tst_QJSValue::ctor_int() +{ + { + QJSValue v(int(0x43211234)); + QVERIFY(v.isNumber()); + QCOMPARE(v.toInt32(), 0x43211234); + } + { + QJSValue v(int(1)); + QCOMPARE(v.isValid(), true); + QCOMPARE(v.isNumber(), true); + QCOMPARE(v.isObject(), false); + QCOMPARE(v.toNumber(), 1.0); + QCOMPARE(v.engine(), (QJSEngine *)0); + } +} + +void tst_QJSValue::ctor_uintWithEngine() +{ + QJSEngine eng; + { + QJSValue v(&eng, uint(1)); + QCOMPARE(v.isValid(), true); + QCOMPARE(v.isNumber(), true); + QCOMPARE(v.isObject(), false); + QCOMPARE(v.toNumber(), 1.0); + QCOMPARE(v.engine(), &eng); + } +} + +void tst_QJSValue::ctor_uint() +{ + { + QJSValue v(uint(0x43211234)); + QVERIFY(v.isNumber()); + QCOMPARE(v.toUInt32(), uint(0x43211234)); + } + { + QJSValue v(uint(1)); + QCOMPARE(v.isValid(), true); + QCOMPARE(v.isNumber(), true); + QCOMPARE(v.isObject(), false); + QCOMPARE(v.toNumber(), 1.0); + QCOMPARE(v.engine(), (QJSEngine *)0); + } +} + +void tst_QJSValue::ctor_floatWithEngine() +{ + QJSEngine eng; + { + QJSValue v(&eng, 1.0); + QCOMPARE(v.isValid(), true); + QCOMPARE(v.isNumber(), true); + QCOMPARE(v.isObject(), false); + QCOMPARE(v.toNumber(), 1.0); + QCOMPARE(v.engine(), &eng); + } +} + +void tst_QJSValue::ctor_float() +{ + { + QJSValue v(12345678910.5); + QVERIFY(v.isNumber()); + QCOMPARE(v.toNumber(), 12345678910.5); + } + { + QJSValue v(1.0); + QCOMPARE(v.isValid(), true); + QCOMPARE(v.isNumber(), true); + QCOMPARE(v.isObject(), false); + QCOMPARE(v.toNumber(), 1.0); + QCOMPARE(v.engine(), (QJSEngine *)0); + } +} + +void tst_QJSValue::ctor_stringWithEngine() +{ + QJSEngine eng; + { + QJSValue v(&eng, QLatin1String("ciao")); + QCOMPARE(v.isValid(), true); + QCOMPARE(v.isString(), true); + QCOMPARE(v.isObject(), false); + QCOMPARE(v.toString(), QLatin1String("ciao")); + QCOMPARE(v.engine(), &eng); + } +} + +void tst_QJSValue::ctor_string() +{ + { + QJSValue v(QString("ciao")); + QCOMPARE(v.isValid(), true); + QCOMPARE(v.isString(), true); + QCOMPARE(v.isObject(), false); + QCOMPARE(v.toString(), QLatin1String("ciao")); + QCOMPARE(v.engine(), (QJSEngine *)0); + } + { + QJSValue v("ciao"); + QCOMPARE(v.isValid(), true); + QCOMPARE(v.isString(), true); + QCOMPARE(v.isObject(), false); + QCOMPARE(v.toString(), QLatin1String("ciao")); + QCOMPARE(v.engine(), (QJSEngine *)0); + } +} + +void tst_QJSValue::ctor_copyAndAssignWithEngine() +{ + QJSEngine eng; + // copy constructor, operator= + { + QJSValue v(&eng, 1.0); + QJSValue v2(v); + QCOMPARE(v2.strictlyEquals(v), true); + QCOMPARE(v2.engine(), &eng); + + QJSValue v3(v); + QCOMPARE(v3.strictlyEquals(v), true); + QCOMPARE(v3.strictlyEquals(v2), true); + QCOMPARE(v3.engine(), &eng); + + QJSValue v4(&eng, 2.0); + QCOMPARE(v4.strictlyEquals(v), false); + v3 = v4; + QCOMPARE(v3.strictlyEquals(v), false); + QCOMPARE(v3.strictlyEquals(v4), true); + + v2 = QJSValue(); + QCOMPARE(v2.strictlyEquals(v), false); + QCOMPARE(v.toNumber(), 1.0); + + QJSValue v5(v); + QCOMPARE(v5.strictlyEquals(v), true); + v = QJSValue(); + QCOMPARE(v5.strictlyEquals(v), false); + QCOMPARE(v5.toNumber(), 1.0); + } +} + +void tst_QJSValue::ctor_undefined() +{ + QJSValue v(QJSValue::UndefinedValue); + QCOMPARE(v.isValid(), true); + QCOMPARE(v.isUndefined(), true); + QCOMPARE(v.isObject(), false); + QCOMPARE(v.engine(), (QJSEngine *)0); +} + +void tst_QJSValue::ctor_null() +{ + QJSValue v(QJSValue::NullValue); + QCOMPARE(v.isValid(), true); + QCOMPARE(v.isNull(), true); + QCOMPARE(v.isObject(), false); + QCOMPARE(v.engine(), (QJSEngine *)0); +} + +void tst_QJSValue::ctor_bool() +{ + QJSValue v(false); + QCOMPARE(v.isValid(), true); + QCOMPARE(v.isBoolean(), true); + QCOMPARE(v.isBool(), true); + QCOMPARE(v.isObject(), false); + QCOMPARE(v.toBoolean(), false); + QCOMPARE(v.engine(), (QJSEngine *)0); +} + +void tst_QJSValue::ctor_copyAndAssign() +{ + QJSValue v(1.0); + QJSValue v2(v); + QCOMPARE(v2.strictlyEquals(v), true); + QCOMPARE(v2.engine(), (QJSEngine *)0); + + QJSValue v3(v); + QCOMPARE(v3.strictlyEquals(v), true); + QCOMPARE(v3.strictlyEquals(v2), true); + QCOMPARE(v3.engine(), (QJSEngine *)0); + + QJSValue v4(2.0); + QCOMPARE(v4.strictlyEquals(v), false); + v3 = v4; + QCOMPARE(v3.strictlyEquals(v), false); + QCOMPARE(v3.strictlyEquals(v4), true); + + v2 = QJSValue(); + QCOMPARE(v2.strictlyEquals(v), false); + QCOMPARE(v.toNumber(), 1.0); + + QJSValue v5(v); + QCOMPARE(v5.strictlyEquals(v), true); + v = QJSValue(); + QCOMPARE(v5.strictlyEquals(v), false); + QCOMPARE(v5.toNumber(), 1.0); +} + +void tst_QJSValue::ctor_nullEngine() +{ + // 0 engine + QVERIFY(QJSValue(0, QJSValue::UndefinedValue).isUndefined()); + QVERIFY(QJSValue(0, QJSValue::NullValue).isNull()); + QVERIFY(QJSValue(0, false).isBool()); + QVERIFY(QJSValue(0, int(1)).isNumber()); + QVERIFY(QJSValue(0, uint(1)).isNumber()); + QVERIFY(QJSValue(0, 1.0).isNumber()); + QVERIFY(QJSValue(0, QString("ciao")).isString()); +} + +#if 0 // FIXME: No c-style callbacks currently +static QJSValue myFunction(QScriptContext *, QScriptEngine *eng) +{ + return eng->undefinedValue(); +} +#endif + +void tst_QJSValue::toString() +{ + QJSEngine eng; + + QJSValue undefined = eng.undefinedValue(); + QCOMPARE(undefined.toString(), QString("undefined")); + QCOMPARE(qjsvalue_cast(undefined), QString()); + + QJSValue null = eng.nullValue(); + QCOMPARE(null.toString(), QString("null")); + QCOMPARE(qjsvalue_cast(null), QString()); + + { + QJSValue falskt = QJSValue(&eng, false); + QCOMPARE(falskt.toString(), QString("false")); + QCOMPARE(qjsvalue_cast(falskt), QString("false")); + + QJSValue sant = QJSValue(&eng, true); + QCOMPARE(sant.toString(), QString("true")); + QCOMPARE(qjsvalue_cast(sant), QString("true")); + } + { + QJSValue number = QJSValue(&eng, 123); + QCOMPARE(number.toString(), QString("123")); + QCOMPARE(qjsvalue_cast(number), QString("123")); + } + { + QJSValue number = QJSValue(&eng, 6.37e-8); + QCOMPARE(number.toString(), QString("6.37e-8")); + } + { + QJSValue number = QJSValue(&eng, -6.37e-8); + QCOMPARE(number.toString(), QString("-6.37e-8")); + + QJSValue str = QJSValue(&eng, QString("ciao")); + QCOMPARE(str.toString(), QString("ciao")); + QCOMPARE(qjsvalue_cast(str), QString("ciao")); + } + + QJSValue object = eng.newObject(); + QCOMPARE(object.toString(), QString("[object Object]")); + QCOMPARE(qjsvalue_cast(object), QString("[object Object]")); + + // FIXME: No c-style callbacks currently +#if 0 + QJSValue fun = eng.newFunction(myFunction); + QCOMPARE(fun.toString().simplified(), QString("function () { [native code] }")); + QCOMPARE(qscriptvalue_cast(fun).simplified(), QString("function () { [native code] }")); +#endif + + // toString() that throws exception + { + QJSValue objectObject = eng.evaluate( + "(function(){" + " o = { };" + " o.toString = function() { throw new Error('toString'); };" + " return o;" + "})()"); + QCOMPARE(objectObject.toString(), QLatin1String("Error: toString")); + QVERIFY(eng.hasUncaughtException()); + QCOMPARE(eng.uncaughtException().toString(), QLatin1String("Error: toString")); + } + { + eng.clearExceptions(); + QJSValue objectObject = eng.evaluate( + "(function(){" + " var f = function() {};" + " f.prototype = Date;" + " return new f;" + "})()"); + QVERIFY(!eng.hasUncaughtException()); + QVERIFY(objectObject.isObject()); + QCOMPARE(objectObject.toString(), QString::fromLatin1("TypeError: Function.prototype.toString is not generic")); + QVERIFY(eng.hasUncaughtException()); + eng.clearExceptions(); + } + + QJSValue inv = QJSValue(); + QCOMPARE(inv.toString(), QString()); + + // V2 constructors + { + QJSValue falskt = QJSValue(false); + QCOMPARE(falskt.toString(), QString("false")); + QCOMPARE(qjsvalue_cast(falskt), QString("false")); + + QJSValue sant = QJSValue(true); + QCOMPARE(sant.toString(), QString("true")); + QCOMPARE(qjsvalue_cast(sant), QString("true")); + + QJSValue number = QJSValue(123); + QCOMPARE(number.toString(), QString("123")); + QCOMPARE(qjsvalue_cast(number), QString("123")); + + QJSValue number2(int(0x43211234)); + QCOMPARE(number2.toString(), QString("1126240820")); + + QJSValue str = QJSValue(QString("ciao")); + QCOMPARE(str.toString(), QString("ciao")); + QCOMPARE(qjsvalue_cast(str), QString("ciao")); + } + + // variant should use internal valueOf(), then fall back to QVariant::toString(), + // then fall back to "QVariant(typename)" + QJSValue variant = eng.newVariant(123); + QVERIFY(variant.isVariant()); + QCOMPARE(variant.toString(), QString::fromLatin1("123")); + variant = eng.newVariant(QByteArray("hello")); + QVERIFY(variant.isVariant()); + QCOMPARE(variant.toString(), QString::fromLatin1("hello")); + variant = eng.newVariant(QVariant(QPoint(10, 20))); + QVERIFY(variant.isVariant()); + QCOMPARE(variant.toString(), QString::fromLatin1("QVariant(QPoint)")); + variant = eng.newVariant(QUrl()); + QVERIFY(variant.toString().isEmpty()); +} + +void tst_QJSValue::toNumber() +{ + QJSEngine eng; + + QJSValue undefined = eng.undefinedValue(); + QCOMPARE(qIsNaN(undefined.toNumber()), true); + QCOMPARE(qIsNaN(qjsvalue_cast(undefined)), true); + + QJSValue null = eng.nullValue(); + QCOMPARE(null.toNumber(), 0.0); + QCOMPARE(qjsvalue_cast(null), 0.0); + + { + QJSValue falskt = QJSValue(&eng, false); + QCOMPARE(falskt.toNumber(), 0.0); + QCOMPARE(qjsvalue_cast(falskt), 0.0); + + QJSValue sant = QJSValue(&eng, true); + QCOMPARE(sant.toNumber(), 1.0); + QCOMPARE(qjsvalue_cast(sant), 1.0); + + QJSValue number = QJSValue(&eng, 123.0); + QCOMPARE(number.toNumber(), 123.0); + QCOMPARE(qjsvalue_cast(number), 123.0); + + QJSValue str = QJSValue(&eng, QString("ciao")); + QCOMPARE(qIsNaN(str.toNumber()), true); + QCOMPARE(qIsNaN(qjsvalue_cast(str)), true); + + QJSValue str2 = QJSValue(&eng, QString("123")); + QCOMPARE(str2.toNumber(), 123.0); + QCOMPARE(qjsvalue_cast(str2), 123.0); + } + + QJSValue object = eng.newObject(); + QCOMPARE(qIsNaN(object.toNumber()), true); + QCOMPARE(qIsNaN(qjsvalue_cast(object)), true); + + // FIXME: No c-style callbacks currently +#if 0 + QJSValue fun = eng.newFunction(myFunction); + QCOMPARE(qIsNaN(fun.toNumber()), true); + QCOMPARE(qIsNaN(qscriptvalue_cast(fun)), true); +#endif + + QJSValue inv = QJSValue(); + QCOMPARE(inv.toNumber(), 0.0); + QCOMPARE(qjsvalue_cast(inv), 0.0); + + // V2 constructors + { + QJSValue falskt = QJSValue(false); + QCOMPARE(falskt.toNumber(), 0.0); + QCOMPARE(qjsvalue_cast(falskt), 0.0); + + QJSValue sant = QJSValue(true); + QCOMPARE(sant.toNumber(), 1.0); + QCOMPARE(qjsvalue_cast(sant), 1.0); + + QJSValue number = QJSValue(123.0); + QCOMPARE(number.toNumber(), 123.0); + QCOMPARE(qjsvalue_cast(number), 123.0); + + QJSValue number2(int(0x43211234)); + QCOMPARE(number2.toNumber(), 1126240820.0); + + QJSValue str = QJSValue(QString("ciao")); + QCOMPARE(qIsNaN(str.toNumber()), true); + QCOMPARE(qIsNaN(qjsvalue_cast(str)), true); + + QJSValue str2 = QJSValue(QString("123")); + QCOMPARE(str2.toNumber(), 123.0); + QCOMPARE(qjsvalue_cast(str2), 123.0); + } +} + +void tst_QJSValue::toBoolean() // deprecated +{ + QJSEngine eng; + + QJSValue undefined = eng.undefinedValue(); + QCOMPARE(undefined.toBoolean(), false); + QCOMPARE(qjsvalue_cast(undefined), false); + + QJSValue null = eng.nullValue(); + QCOMPARE(null.toBoolean(), false); + QCOMPARE(qjsvalue_cast(null), false); + + { + QJSValue falskt = QJSValue(&eng, false); + QCOMPARE(falskt.toBoolean(), false); + QCOMPARE(qjsvalue_cast(falskt), false); + + QJSValue sant = QJSValue(&eng, true); + QCOMPARE(sant.toBoolean(), true); + QCOMPARE(qjsvalue_cast(sant), true); + + QJSValue number = QJSValue(&eng, 0.0); + QCOMPARE(number.toBoolean(), false); + QCOMPARE(qjsvalue_cast(number), false); + + QJSValue number2 = QJSValue(&eng, qSNaN()); + QCOMPARE(number2.toBoolean(), false); + QCOMPARE(qjsvalue_cast(number2), false); + + QJSValue number3 = QJSValue(&eng, 123.0); + QCOMPARE(number3.toBoolean(), true); + QCOMPARE(qjsvalue_cast(number3), true); + + QJSValue number4 = QJSValue(&eng, -456.0); + QCOMPARE(number4.toBoolean(), true); + QCOMPARE(qjsvalue_cast(number4), true); + + QJSValue str = QJSValue(&eng, QString("")); + QCOMPARE(str.toBoolean(), false); + QCOMPARE(qjsvalue_cast(str), false); + + QJSValue str2 = QJSValue(&eng, QString("123")); + QCOMPARE(str2.toBoolean(), true); + QCOMPARE(qjsvalue_cast(str2), true); + } + + QJSValue object = eng.newObject(); + QCOMPARE(object.toBoolean(), true); + QCOMPARE(qjsvalue_cast(object), true); + + // FIXME: No c-style callbacks currently +#if 0 + QJSValue fun = eng.newFunction(myFunction); + QCOMPARE(fun.toBoolean(), true); + QCOMPARE(qscriptvalue_cast(fun), true); +#endif + + QJSValue inv = QJSValue(); + QCOMPARE(inv.toBoolean(), false); + QCOMPARE(qjsvalue_cast(inv), false); + + // V2 constructors + { + QJSValue falskt = QJSValue(false); + QCOMPARE(falskt.toBoolean(), false); + QCOMPARE(qjsvalue_cast(falskt), false); + + QJSValue sant = QJSValue(true); + QCOMPARE(sant.toBoolean(), true); + QCOMPARE(qjsvalue_cast(sant), true); + + QJSValue number = QJSValue(0.0); + QCOMPARE(number.toBoolean(), false); + QCOMPARE(qjsvalue_cast(number), false); + + QJSValue number2 = QJSValue(qSNaN()); + QCOMPARE(number2.toBoolean(), false); + QCOMPARE(qjsvalue_cast(number2), false); + + QJSValue number3 = QJSValue(123.0); + QCOMPARE(number3.toBoolean(), true); + QCOMPARE(qjsvalue_cast(number3), true); + + QJSValue number4 = QJSValue(-456.0); + QCOMPARE(number4.toBoolean(), true); + QCOMPARE(qjsvalue_cast(number4), true); + + QJSValue number5 = QJSValue(0x43211234); + QCOMPARE(number5.toBoolean(), true); + + QJSValue str = QJSValue(QString("")); + QCOMPARE(str.toBoolean(), false); + QCOMPARE(qjsvalue_cast(str), false); + + QJSValue str2 = QJSValue(QString("123")); + QCOMPARE(str2.toBoolean(), true); + QCOMPARE(qjsvalue_cast(str2), true); + } +} + +void tst_QJSValue::toBool() +{ + QJSEngine eng; + + QJSValue undefined = eng.undefinedValue(); + QCOMPARE(undefined.toBool(), false); + QCOMPARE(qjsvalue_cast(undefined), false); + + QJSValue null = eng.nullValue(); + QCOMPARE(null.toBool(), false); + QCOMPARE(qjsvalue_cast(null), false); + + { + QJSValue falskt = QJSValue(&eng, false); + QCOMPARE(falskt.toBool(), false); + QCOMPARE(qjsvalue_cast(falskt), false); + + QJSValue sant = QJSValue(&eng, true); + QCOMPARE(sant.toBool(), true); + QCOMPARE(qjsvalue_cast(sant), true); + + QJSValue number = QJSValue(&eng, 0.0); + QCOMPARE(number.toBool(), false); + QCOMPARE(qjsvalue_cast(number), false); + + QJSValue number2 = QJSValue(&eng, qSNaN()); + QCOMPARE(number2.toBool(), false); + QCOMPARE(qjsvalue_cast(number2), false); + + QJSValue number3 = QJSValue(&eng, 123.0); + QCOMPARE(number3.toBool(), true); + QCOMPARE(qjsvalue_cast(number3), true); + + QJSValue number4 = QJSValue(&eng, -456.0); + QCOMPARE(number4.toBool(), true); + QCOMPARE(qjsvalue_cast(number4), true); + + QJSValue str = QJSValue(&eng, QString("")); + QCOMPARE(str.toBool(), false); + QCOMPARE(qjsvalue_cast(str), false); + + QJSValue str2 = QJSValue(&eng, QString("123")); + QCOMPARE(str2.toBool(), true); + QCOMPARE(qjsvalue_cast(str2), true); + } + + QJSValue object = eng.newObject(); + QCOMPARE(object.toBool(), true); + QCOMPARE(qjsvalue_cast(object), true); + + // FIXME: No c-style callbacks currently +#if 0 + QJSValue fun = eng.newFunction(myFunction); + QCOMPARE(fun.toBool(), true); + QCOMPARE(qscriptvalue_cast(fun), true); +#endif + + QJSValue inv = QJSValue(); + QCOMPARE(inv.toBool(), false); + QCOMPARE(qjsvalue_cast(inv), false); + + // V2 constructors + { + QJSValue falskt = QJSValue(false); + QCOMPARE(falskt.toBool(), false); + QCOMPARE(qjsvalue_cast(falskt), false); + + QJSValue sant = QJSValue(true); + QCOMPARE(sant.toBool(), true); + QCOMPARE(qjsvalue_cast(sant), true); + + QJSValue number = QJSValue(0.0); + QCOMPARE(number.toBool(), false); + QCOMPARE(qjsvalue_cast(number), false); + + QJSValue number2 = QJSValue(qSNaN()); + QCOMPARE(number2.toBool(), false); + QCOMPARE(qjsvalue_cast(number2), false); + + QJSValue number3 = QJSValue(123.0); + QCOMPARE(number3.toBool(), true); + QCOMPARE(qjsvalue_cast(number3), true); + + QJSValue number4 = QJSValue(-456.0); + QCOMPARE(number4.toBool(), true); + QCOMPARE(qjsvalue_cast(number4), true); + + QJSValue number5 = QJSValue(0x43211234); + QCOMPARE(number5.toBool(), true); + + QJSValue str = QJSValue(QString("")); + QCOMPARE(str.toBool(), false); + QCOMPARE(qjsvalue_cast(str), false); + + QJSValue str2 = QJSValue(QString("123")); + QCOMPARE(str2.toBool(), true); + QCOMPARE(qjsvalue_cast(str2), true); + } +} + +void tst_QJSValue::toInteger() +{ + QJSEngine eng; + + { + QJSValue number = QJSValue(&eng, 123.0); + QCOMPARE(number.toInteger(), 123.0); + + QJSValue number2 = QJSValue(&eng, qSNaN()); + QCOMPARE(number2.toInteger(), 0.0); + + QJSValue number3 = QJSValue(&eng, qInf()); + QCOMPARE(qIsInf(number3.toInteger()), true); + + QJSValue number4 = QJSValue(&eng, 0.5); + QCOMPARE(number4.toInteger(), 0.0); + + QJSValue number5 = QJSValue(&eng, 123.5); + QCOMPARE(number5.toInteger(), 123.0); + + QJSValue number6 = QJSValue(&eng, -456.5); + QCOMPARE(number6.toInteger(), -456.0); + + QJSValue str = QJSValue(&eng, QLatin1String("123.0")); + QCOMPARE(str.toInteger(), 123.0); + + QJSValue str2 = QJSValue(&eng, QLatin1String("NaN")); + QCOMPARE(str2.toInteger(), 0.0); + + QJSValue str3 = QJSValue(&eng, QLatin1String("Infinity")); + QCOMPARE(qIsInf(str3.toInteger()), true); + + QJSValue str4 = QJSValue(&eng, QLatin1String("0.5")); + QCOMPARE(str4.toInteger(), 0.0); + + QJSValue str5 = QJSValue(&eng, QLatin1String("123.5")); + QCOMPARE(str5.toInteger(), 123.0); + + QJSValue str6 = QJSValue(&eng, QLatin1String("-456.5")); + QCOMPARE(str6.toInteger(), -456.0); + } + // V2 constructors + { + QJSValue number = QJSValue(123.0); + QCOMPARE(number.toInteger(), 123.0); + + QJSValue number2 = QJSValue(qSNaN()); + QCOMPARE(number2.toInteger(), 0.0); + + QJSValue number3 = QJSValue(qInf()); + QCOMPARE(qIsInf(number3.toInteger()), true); + + QJSValue number4 = QJSValue(0.5); + QCOMPARE(number4.toInteger(), 0.0); + + QJSValue number5 = QJSValue(123.5); + QCOMPARE(number5.toInteger(), 123.0); + + QJSValue number6 = QJSValue(-456.5); + QCOMPARE(number6.toInteger(), -456.0); + + QJSValue number7 = QJSValue(0x43211234); + QCOMPARE(number7.toInteger(), qreal(0x43211234)); + + QJSValue str = QJSValue("123.0"); + QCOMPARE(str.toInteger(), 123.0); + + QJSValue str2 = QJSValue("NaN"); + QCOMPARE(str2.toInteger(), 0.0); + + QJSValue str3 = QJSValue("Infinity"); + QCOMPARE(qIsInf(str3.toInteger()), true); + + QJSValue str4 = QJSValue("0.5"); + QCOMPARE(str4.toInteger(), 0.0); + + QJSValue str5 = QJSValue("123.5"); + QCOMPARE(str5.toInteger(), 123.0); + + QJSValue str6 = QJSValue("-456.5"); + QCOMPARE(str6.toInteger(), -456.0); + } + + QJSValue inv; + QCOMPARE(inv.toInteger(), 0.0); +} + +void tst_QJSValue::toInt32() +{ + QJSEngine eng; + + { + QJSValue zer0 = QJSValue(&eng, 0.0); + QCOMPARE(zer0.toInt32(), 0); + QCOMPARE(qjsvalue_cast(zer0), 0); + + QJSValue number = QJSValue(&eng, 123.0); + QCOMPARE(number.toInt32(), 123); + QCOMPARE(qjsvalue_cast(number), 123); + + QJSValue number2 = QJSValue(&eng, qSNaN()); + QCOMPARE(number2.toInt32(), 0); + QCOMPARE(qjsvalue_cast(number2), 0); + + QJSValue number3 = QJSValue(&eng, +qInf()); + QCOMPARE(number3.toInt32(), 0); + QCOMPARE(qjsvalue_cast(number3), 0); + + QJSValue number3_2 = QJSValue(&eng, -qInf()); + QCOMPARE(number3_2.toInt32(), 0); + QCOMPARE(qjsvalue_cast(number3_2), 0); + + QJSValue number4 = QJSValue(&eng, 0.5); + QCOMPARE(number4.toInt32(), 0); + QCOMPARE(qjsvalue_cast(number4), 0); + + QJSValue number5 = QJSValue(&eng, 123.5); + QCOMPARE(number5.toInt32(), 123); + QCOMPARE(qjsvalue_cast(number5), 123); + + QJSValue number6 = QJSValue(&eng, -456.5); + QCOMPARE(number6.toInt32(), -456); + QCOMPARE(qjsvalue_cast(number6), -456); + + QJSValue str = QJSValue(&eng, QLatin1String("123.0")); + QCOMPARE(str.toInt32(), 123); + QCOMPARE(qjsvalue_cast(str), 123); + + QJSValue str2 = QJSValue(&eng, QLatin1String("NaN")); + QCOMPARE(str2.toInt32(), 0); + QCOMPARE(qjsvalue_cast(str2), 0); + + QJSValue str3 = QJSValue(&eng, QLatin1String("Infinity")); + QCOMPARE(str3.toInt32(), 0); + QCOMPARE(qjsvalue_cast(str3), 0); + + QJSValue str3_2 = QJSValue(&eng, QLatin1String("-Infinity")); + QCOMPARE(str3_2.toInt32(), 0); + QCOMPARE(qjsvalue_cast(str3_2), 0); + + QJSValue str4 = QJSValue(&eng, QLatin1String("0.5")); + QCOMPARE(str4.toInt32(), 0); + QCOMPARE(qjsvalue_cast(str4), 0); + + QJSValue str5 = QJSValue(&eng, QLatin1String("123.5")); + QCOMPARE(str5.toInt32(), 123); + QCOMPARE(qjsvalue_cast(str5), 123); + + QJSValue str6 = QJSValue(&eng, QLatin1String("-456.5")); + QCOMPARE(str6.toInt32(), -456); + QCOMPARE(qjsvalue_cast(str6), -456); + } + // V2 constructors + { + QJSValue zer0 = QJSValue(0.0); + QCOMPARE(zer0.toInt32(), 0); + QCOMPARE(qjsvalue_cast(zer0), 0); + + QJSValue number = QJSValue(123.0); + QCOMPARE(number.toInt32(), 123); + QCOMPARE(qjsvalue_cast(number), 123); + + QJSValue number2 = QJSValue(qSNaN()); + QCOMPARE(number2.toInt32(), 0); + QCOMPARE(qjsvalue_cast(number2), 0); + + QJSValue number3 = QJSValue(+qInf()); + QCOMPARE(number3.toInt32(), 0); + QCOMPARE(qjsvalue_cast(number3), 0); + + QJSValue number3_2 = QJSValue(-qInf()); + QCOMPARE(number3_2.toInt32(), 0); + QCOMPARE(qjsvalue_cast(number3_2), 0); + + QJSValue number4 = QJSValue(0.5); + QCOMPARE(number4.toInt32(), 0); + QCOMPARE(qjsvalue_cast(number4), 0); + + QJSValue number5 = QJSValue(123.5); + QCOMPARE(number5.toInt32(), 123); + QCOMPARE(qjsvalue_cast(number5), 123); + + QJSValue number6 = QJSValue(-456.5); + QCOMPARE(number6.toInt32(), -456); + QCOMPARE(qjsvalue_cast(number6), -456); + + QJSValue number7 = QJSValue(0x43211234); + QCOMPARE(number7.toInt32(), 0x43211234); + + QJSValue str = QJSValue("123.0"); + QCOMPARE(str.toInt32(), 123); + QCOMPARE(qjsvalue_cast(str), 123); + + QJSValue str2 = QJSValue("NaN"); + QCOMPARE(str2.toInt32(), 0); + QCOMPARE(qjsvalue_cast(str2), 0); + + QJSValue str3 = QJSValue("Infinity"); + QCOMPARE(str3.toInt32(), 0); + QCOMPARE(qjsvalue_cast(str3), 0); + + QJSValue str3_2 = QJSValue("-Infinity"); + QCOMPARE(str3_2.toInt32(), 0); + QCOMPARE(qjsvalue_cast(str3_2), 0); + + QJSValue str4 = QJSValue("0.5"); + QCOMPARE(str4.toInt32(), 0); + QCOMPARE(qjsvalue_cast(str4), 0); + + QJSValue str5 = QJSValue("123.5"); + QCOMPARE(str5.toInt32(), 123); + QCOMPARE(qjsvalue_cast(str5), 123); + + QJSValue str6 = QJSValue("-456.5"); + QCOMPARE(str6.toInt32(), -456); + QCOMPARE(qjsvalue_cast(str6), -456); + } + + QJSValue inv; + QCOMPARE(inv.toInt32(), 0); + QCOMPARE(qjsvalue_cast(inv), 0); +} + +void tst_QJSValue::toUInt32() +{ + QJSEngine eng; + + { + QJSValue zer0 = QJSValue(&eng, 0.0); + QCOMPARE(zer0.toUInt32(), quint32(0)); + QCOMPARE(qjsvalue_cast(zer0), quint32(0)); + + QJSValue number = QJSValue(&eng, 123.0); + QCOMPARE(number.toUInt32(), quint32(123)); + QCOMPARE(qjsvalue_cast(number), quint32(123)); + + QJSValue number2 = QJSValue(&eng, qSNaN()); + QCOMPARE(number2.toUInt32(), quint32(0)); + QCOMPARE(qjsvalue_cast(number2), quint32(0)); + + QJSValue number3 = QJSValue(&eng, +qInf()); + QCOMPARE(number3.toUInt32(), quint32(0)); + QCOMPARE(qjsvalue_cast(number3), quint32(0)); + + QJSValue number3_2 = QJSValue(&eng, -qInf()); + QCOMPARE(number3_2.toUInt32(), quint32(0)); + QCOMPARE(qjsvalue_cast(number3_2), quint32(0)); + + QJSValue number4 = QJSValue(&eng, 0.5); + QCOMPARE(number4.toUInt32(), quint32(0)); + + QJSValue number5 = QJSValue(&eng, 123.5); + QCOMPARE(number5.toUInt32(), quint32(123)); + + QJSValue number6 = QJSValue(&eng, -456.5); + QCOMPARE(number6.toUInt32(), quint32(-456)); + QCOMPARE(qjsvalue_cast(number6), quint32(-456)); + + QJSValue str = QJSValue(&eng, QLatin1String("123.0")); + QCOMPARE(str.toUInt32(), quint32(123)); + QCOMPARE(qjsvalue_cast(str), quint32(123)); + + QJSValue str2 = QJSValue(&eng, QLatin1String("NaN")); + QCOMPARE(str2.toUInt32(), quint32(0)); + QCOMPARE(qjsvalue_cast(str2), quint32(0)); + + QJSValue str3 = QJSValue(&eng, QLatin1String("Infinity")); + QCOMPARE(str3.toUInt32(), quint32(0)); + QCOMPARE(qjsvalue_cast(str3), quint32(0)); + + QJSValue str3_2 = QJSValue(&eng, QLatin1String("-Infinity")); + QCOMPARE(str3_2.toUInt32(), quint32(0)); + QCOMPARE(qjsvalue_cast(str3_2), quint32(0)); + + QJSValue str4 = QJSValue(&eng, QLatin1String("0.5")); + QCOMPARE(str4.toUInt32(), quint32(0)); + QCOMPARE(qjsvalue_cast(str4), quint32(0)); + + QJSValue str5 = QJSValue(&eng, QLatin1String("123.5")); + QCOMPARE(str5.toUInt32(), quint32(123)); + QCOMPARE(qjsvalue_cast(str5), quint32(123)); + + QJSValue str6 = QJSValue(&eng, QLatin1String("-456.5")); + QCOMPARE(str6.toUInt32(), quint32(-456)); + QCOMPARE(qjsvalue_cast(str6), quint32(-456)); + } + // V2 constructors + { + QJSValue zer0 = QJSValue(0.0); + QCOMPARE(zer0.toUInt32(), quint32(0)); + QCOMPARE(qjsvalue_cast(zer0), quint32(0)); + + QJSValue number = QJSValue(123.0); + QCOMPARE(number.toUInt32(), quint32(123)); + QCOMPARE(qjsvalue_cast(number), quint32(123)); + + QJSValue number2 = QJSValue(qSNaN()); + QCOMPARE(number2.toUInt32(), quint32(0)); + QCOMPARE(qjsvalue_cast(number2), quint32(0)); + + QJSValue number3 = QJSValue(+qInf()); + QCOMPARE(number3.toUInt32(), quint32(0)); + QCOMPARE(qjsvalue_cast(number3), quint32(0)); + + QJSValue number3_2 = QJSValue(-qInf()); + QCOMPARE(number3_2.toUInt32(), quint32(0)); + QCOMPARE(qjsvalue_cast(number3_2), quint32(0)); + + QJSValue number4 = QJSValue(0.5); + QCOMPARE(number4.toUInt32(), quint32(0)); + + QJSValue number5 = QJSValue(123.5); + QCOMPARE(number5.toUInt32(), quint32(123)); + + QJSValue number6 = QJSValue(-456.5); + QCOMPARE(number6.toUInt32(), quint32(-456)); + QCOMPARE(qjsvalue_cast(number6), quint32(-456)); + + QJSValue number7 = QJSValue(0x43211234); + QCOMPARE(number7.toUInt32(), quint32(0x43211234)); + + QJSValue str = QJSValue(QLatin1String("123.0")); + QCOMPARE(str.toUInt32(), quint32(123)); + QCOMPARE(qjsvalue_cast(str), quint32(123)); + + QJSValue str2 = QJSValue(QLatin1String("NaN")); + QCOMPARE(str2.toUInt32(), quint32(0)); + QCOMPARE(qjsvalue_cast(str2), quint32(0)); + + QJSValue str3 = QJSValue(QLatin1String("Infinity")); + QCOMPARE(str3.toUInt32(), quint32(0)); + QCOMPARE(qjsvalue_cast(str3), quint32(0)); + + QJSValue str3_2 = QJSValue(QLatin1String("-Infinity")); + QCOMPARE(str3_2.toUInt32(), quint32(0)); + QCOMPARE(qjsvalue_cast(str3_2), quint32(0)); + + QJSValue str4 = QJSValue(QLatin1String("0.5")); + QCOMPARE(str4.toUInt32(), quint32(0)); + QCOMPARE(qjsvalue_cast(str4), quint32(0)); + + QJSValue str5 = QJSValue(QLatin1String("123.5")); + QCOMPARE(str5.toUInt32(), quint32(123)); + QCOMPARE(qjsvalue_cast(str5), quint32(123)); + + QJSValue str6 = QJSValue(QLatin1String("-456.5")); + QCOMPARE(str6.toUInt32(), quint32(-456)); + QCOMPARE(qjsvalue_cast(str6), quint32(-456)); + } + + QJSValue inv; + QCOMPARE(inv.toUInt32(), quint32(0)); + QCOMPARE(qjsvalue_cast(inv), quint32(0)); +} + +void tst_QJSValue::toUInt16() +{ + QJSEngine eng; + + { + QJSValue zer0 = QJSValue(&eng, 0.0); + QCOMPARE(zer0.toUInt16(), quint16(0)); + QCOMPARE(qjsvalue_cast(zer0), quint16(0)); + + QJSValue number = QJSValue(&eng, 123.0); + QCOMPARE(number.toUInt16(), quint16(123)); + QCOMPARE(qjsvalue_cast(number), quint16(123)); + + QJSValue number2 = QJSValue(&eng, qSNaN()); + QCOMPARE(number2.toUInt16(), quint16(0)); + QCOMPARE(qjsvalue_cast(number2), quint16(0)); + + QJSValue number3 = QJSValue(&eng, +qInf()); + QCOMPARE(number3.toUInt16(), quint16(0)); + QCOMPARE(qjsvalue_cast(number3), quint16(0)); + + QJSValue number3_2 = QJSValue(&eng, -qInf()); + QCOMPARE(number3_2.toUInt16(), quint16(0)); + QCOMPARE(qjsvalue_cast(number3_2), quint16(0)); + + QJSValue number4 = QJSValue(&eng, 0.5); + QCOMPARE(number4.toUInt16(), quint16(0)); + + QJSValue number5 = QJSValue(&eng, 123.5); + QCOMPARE(number5.toUInt16(), quint16(123)); + + QJSValue number6 = QJSValue(&eng, -456.5); + QCOMPARE(number6.toUInt16(), quint16(-456)); + QCOMPARE(qjsvalue_cast(number6), quint16(-456)); + + QJSValue number7 = QJSValue(&eng, 0x10000); + QCOMPARE(number7.toUInt16(), quint16(0)); + QCOMPARE(qjsvalue_cast(number7), quint16(0)); + + QJSValue number8 = QJSValue(&eng, 0x10001); + QCOMPARE(number8.toUInt16(), quint16(1)); + QCOMPARE(qjsvalue_cast(number8), quint16(1)); + + QJSValue str = QJSValue(&eng, QLatin1String("123.0")); + QCOMPARE(str.toUInt16(), quint16(123)); + QCOMPARE(qjsvalue_cast(str), quint16(123)); + + QJSValue str2 = QJSValue(&eng, QLatin1String("NaN")); + QCOMPARE(str2.toUInt16(), quint16(0)); + QCOMPARE(qjsvalue_cast(str2), quint16(0)); + + QJSValue str3 = QJSValue(&eng, QLatin1String("Infinity")); + QCOMPARE(str3.toUInt16(), quint16(0)); + QCOMPARE(qjsvalue_cast(str3), quint16(0)); + + QJSValue str3_2 = QJSValue(&eng, QLatin1String("-Infinity")); + QCOMPARE(str3_2.toUInt16(), quint16(0)); + QCOMPARE(qjsvalue_cast(str3_2), quint16(0)); + + QJSValue str4 = QJSValue(&eng, QLatin1String("0.5")); + QCOMPARE(str4.toUInt16(), quint16(0)); + + QJSValue str5 = QJSValue(&eng, QLatin1String("123.5")); + QCOMPARE(str5.toUInt16(), quint16(123)); + + QJSValue str6 = QJSValue(&eng, QLatin1String("-456.5")); + QCOMPARE(str6.toUInt16(), quint16(-456)); + QCOMPARE(qjsvalue_cast(str6), quint16(-456)); + + QJSValue str7 = QJSValue(&eng, QLatin1String("0x10000")); + QCOMPARE(str7.toUInt16(), quint16(0)); + QCOMPARE(qjsvalue_cast(str7), quint16(0)); + + QJSValue str8 = QJSValue(&eng, QLatin1String("0x10001")); + QCOMPARE(str8.toUInt16(), quint16(1)); + QCOMPARE(qjsvalue_cast(str8), quint16(1)); + } + // V2 constructors + { + QJSValue zer0 = QJSValue(0.0); + QCOMPARE(zer0.toUInt16(), quint16(0)); + QCOMPARE(qjsvalue_cast(zer0), quint16(0)); + + QJSValue number = QJSValue(123.0); + QCOMPARE(number.toUInt16(), quint16(123)); + QCOMPARE(qjsvalue_cast(number), quint16(123)); + + QJSValue number2 = QJSValue(qSNaN()); + QCOMPARE(number2.toUInt16(), quint16(0)); + QCOMPARE(qjsvalue_cast(number2), quint16(0)); + + QJSValue number3 = QJSValue(+qInf()); + QCOMPARE(number3.toUInt16(), quint16(0)); + QCOMPARE(qjsvalue_cast(number3), quint16(0)); + + QJSValue number3_2 = QJSValue(-qInf()); + QCOMPARE(number3_2.toUInt16(), quint16(0)); + QCOMPARE(qjsvalue_cast(number3_2), quint16(0)); + + QJSValue number4 = QJSValue(0.5); + QCOMPARE(number4.toUInt16(), quint16(0)); + + QJSValue number5 = QJSValue(123.5); + QCOMPARE(number5.toUInt16(), quint16(123)); + + QJSValue number6 = QJSValue(-456.5); + QCOMPARE(number6.toUInt16(), quint16(-456)); + QCOMPARE(qjsvalue_cast(number6), quint16(-456)); + + QJSValue number7 = QJSValue(0x10000); + QCOMPARE(number7.toUInt16(), quint16(0)); + QCOMPARE(qjsvalue_cast(number7), quint16(0)); + + QJSValue number8 = QJSValue(0x10001); + QCOMPARE(number8.toUInt16(), quint16(1)); + QCOMPARE(qjsvalue_cast(number8), quint16(1)); + + QJSValue str = QJSValue(QLatin1String("123.0")); + QCOMPARE(str.toUInt16(), quint16(123)); + QCOMPARE(qjsvalue_cast(str), quint16(123)); + + QJSValue str2 = QJSValue(QLatin1String("NaN")); + QCOMPARE(str2.toUInt16(), quint16(0)); + QCOMPARE(qjsvalue_cast(str2), quint16(0)); + + QJSValue str3 = QJSValue(QLatin1String("Infinity")); + QCOMPARE(str3.toUInt16(), quint16(0)); + QCOMPARE(qjsvalue_cast(str3), quint16(0)); + + QJSValue str3_2 = QJSValue(QLatin1String("-Infinity")); + QCOMPARE(str3_2.toUInt16(), quint16(0)); + QCOMPARE(qjsvalue_cast(str3_2), quint16(0)); + + QJSValue str4 = QJSValue("0.5"); + QCOMPARE(str4.toUInt16(), quint16(0)); + + QJSValue str5 = QJSValue("123.5"); + QCOMPARE(str5.toUInt16(), quint16(123)); + + QJSValue str6 = QJSValue("-456.5"); + QCOMPARE(str6.toUInt16(), quint16(-456)); + QCOMPARE(qjsvalue_cast(str6), quint16(-456)); + + QJSValue str7 = QJSValue("0x10000"); + QCOMPARE(str7.toUInt16(), quint16(0)); + QCOMPARE(qjsvalue_cast(str7), quint16(0)); + + QJSValue str8 = QJSValue("0x10001"); + QCOMPARE(str8.toUInt16(), quint16(1)); + QCOMPARE(qjsvalue_cast(str8), quint16(1)); + } + + QJSValue inv; + QCOMPARE(inv.toUInt16(), quint16(0)); + QCOMPARE(qjsvalue_cast(inv), quint16(0)); +} + +#if defined Q_CC_MSVC && _MSC_VER < 1300 +Q_DECLARE_METATYPE(QVariant) +#endif + +void tst_QJSValue::toVariant() +{ + QJSEngine eng; + + QJSValue undefined = eng.undefinedValue(); + QCOMPARE(undefined.toVariant(), QVariant()); + QCOMPARE(qjsvalue_cast(undefined), QVariant()); + + QJSValue null = eng.nullValue(); + QCOMPARE(null.toVariant(), QVariant()); + QCOMPARE(qjsvalue_cast(null), QVariant()); + + { + QJSValue number = QJSValue(&eng, 123.0); + QCOMPARE(number.toVariant(), QVariant(123.0)); + QCOMPARE(qjsvalue_cast(number), QVariant(123.0)); + + QJSValue intNumber = QJSValue(&eng, (qint32)123); + QCOMPARE(intNumber.toVariant().type(), QVariant((qint32)123).type()); + QCOMPARE((qjsvalue_cast(number)).type(), QVariant((qint32)123).type()); + + QJSValue falskt = QJSValue(&eng, false); + QCOMPARE(falskt.toVariant(), QVariant(false)); + QCOMPARE(qjsvalue_cast(falskt), QVariant(false)); + + QJSValue sant = QJSValue(&eng, true); + QCOMPARE(sant.toVariant(), QVariant(true)); + QCOMPARE(qjsvalue_cast(sant), QVariant(true)); + + QJSValue str = QJSValue(&eng, QString("ciao")); + QCOMPARE(str.toVariant(), QVariant(QString("ciao"))); + QCOMPARE(qjsvalue_cast(str), QVariant(QString("ciao"))); + } + + QVariant var(QChar(0x007A)); + QJSValue opaque = eng.newVariant(var); + QVERIFY(opaque.isVariant()); + QCOMPARE(opaque.toVariant(), var); + + QJSValue object = eng.newObject(); + QCOMPARE(object.toVariant(), QVariant(QVariantMap())); + + QJSValue qobject = eng.newQObject(this); + { + QVariant var = qobject.toVariant(); + QCOMPARE(var.userType(), int(QMetaType::QObjectStar)); + QCOMPARE(qVariantValue(var), (QObject *)this); + } + + { + QDateTime dateTime = QDateTime(QDate(1980, 10, 4)); + QJSValue dateObject = eng.newDate(dateTime); + QVariant var = dateObject.toVariant(); + QCOMPARE(var, QVariant(dateTime)); + } + + { + QRegExp rx = QRegExp("[0-9a-z]+", Qt::CaseSensitive, QRegExp::RegExp2); + QJSValue rxObject = eng.newRegExp(rx); + QVariant var = rxObject.toVariant(); + QCOMPARE(var, QVariant(rx)); + } + + QJSValue inv; + QCOMPARE(inv.toVariant(), QVariant()); + QCOMPARE(qjsvalue_cast(inv), QVariant()); + + // V2 constructors + { + QJSValue number = QJSValue(123.0); + QCOMPARE(number.toVariant(), QVariant(123.0)); + QCOMPARE(qjsvalue_cast(number), QVariant(123.0)); + + QJSValue falskt = QJSValue(false); + QCOMPARE(falskt.toVariant(), QVariant(false)); + QCOMPARE(qjsvalue_cast(falskt), QVariant(false)); + + QJSValue sant = QJSValue(true); + QCOMPARE(sant.toVariant(), QVariant(true)); + QCOMPARE(qjsvalue_cast(sant), QVariant(true)); + + QJSValue str = QJSValue(QString("ciao")); + QCOMPARE(str.toVariant(), QVariant(QString("ciao"))); + QCOMPARE(qjsvalue_cast(str), QVariant(QString("ciao"))); + } + +#if 0 // FIXME: No automatic sequence conversion + // array + { + QVariantList listIn; + listIn << 123 << "hello"; + QJSValue array = qScriptValueFromValue(&eng, listIn); + QVERIFY(array.isArray()); + QCOMPARE(array.property("length").toInt32(), 2); + QVariant ret = array.toVariant(); + QCOMPARE(ret.type(), QVariant::List); + QVariantList listOut = ret.toList(); + QCOMPARE(listOut.size(), listIn.size()); + for (int i = 0; i < listIn.size(); ++i) + QVERIFY(listOut.at(i) == listIn.at(i)); + // round-trip conversion + QJSValue array2 = qScriptValueFromValue(&eng, ret); + QVERIFY(array2.isArray()); + QCOMPARE(array2.property("length").toInt32(), array.property("length").toInt32()); + for (int i = 0; i < array.property("length").toInt32(); ++i) + QVERIFY(array2.property(i).strictlyEquals(array.property(i))); + } +#endif +} + +void tst_QJSValue::toQObject_nonQObject_data() +{ + newEngine(); + QTest::addColumn("value"); + + QTest::newRow("invalid") << QJSValue(); + QTest::newRow("bool(false)") << QJSValue(false); + QTest::newRow("bool(true)") << QJSValue(true); + QTest::newRow("int") << QJSValue(123); + QTest::newRow("string") << QJSValue(QString::fromLatin1("ciao")); + QTest::newRow("undefined") << QJSValue(QJSValue::UndefinedValue); + QTest::newRow("null") << QJSValue(QJSValue::NullValue); + + QTest::newRow("bool bound(false)") << QJSValue(engine, false); + QTest::newRow("bool bound(true)") << QJSValue(engine, true); + QTest::newRow("int bound") << QJSValue(engine, 123); + QTest::newRow("string bound") << QJSValue(engine, QString::fromLatin1("ciao")); + QTest::newRow("undefined bound") << engine->undefinedValue(); + QTest::newRow("null bound") << engine->nullValue(); + QTest::newRow("object") << engine->newObject(); + QTest::newRow("array") << engine->newArray(); + QTest::newRow("date") << engine->newDate(124); + QTest::newRow("variant(12345)") << engine->newVariant(12345); + QTest::newRow("variant((QObject*)0)") << engine->newVariant(qVariantFromValue((QObject*)0)); + QTest::newRow("newQObject(0)") << engine->newQObject(0); +} + + +void tst_QJSValue::toQObject_nonQObject() +{ + QFETCH(QJSValue, value); + QCOMPARE(value.toQObject(), (QObject *)0); + QCOMPARE(qjsvalue_cast(value), (QObject *)0); +} + +// unfortunately, this is necessary in order to do qscriptvalue_cast(...) +Q_DECLARE_METATYPE(QPushButton*); + +void tst_QJSValue::toQObject() +{ + QJSEngine eng; + + QJSValue qobject = eng.newQObject(this); + QCOMPARE(qobject.toQObject(), (QObject *)this); + QCOMPARE(qjsvalue_cast(qobject), (QObject *)this); + QCOMPARE(qjsvalue_cast(qobject), (QWidget *)0); + + QWidget widget; + QJSValue qwidget = eng.newQObject(&widget); + QCOMPARE(qwidget.toQObject(), (QObject *)&widget); + QCOMPARE(qjsvalue_cast(qwidget), (QObject *)&widget); + QCOMPARE(qjsvalue_cast(qwidget), &widget); + + QPushButton button; + QJSValue qbutton = eng.newQObject(&button); + QCOMPARE(qbutton.toQObject(), (QObject *)&button); + QCOMPARE(qjsvalue_cast(qbutton), (QObject *)&button); + QCOMPARE(qjsvalue_cast(qbutton), (QWidget *)&button); + QCOMPARE(qjsvalue_cast(qbutton), &button); + + // wrapping a QObject* as variant + QJSValue variant = eng.newVariant(qVariantFromValue((QObject*)&button)); + QCOMPARE(variant.toQObject(), (QObject*)&button); + QCOMPARE(qjsvalue_cast(variant), (QObject*)&button); + QCOMPARE(qjsvalue_cast(variant), (QWidget*)&button); + QCOMPARE(qjsvalue_cast(variant), &button); + + QJSValue variant2 = eng.newVariant(qVariantFromValue((QWidget*)&button)); + QCOMPARE(variant2.toQObject(), (QObject*)&button); + QCOMPARE(qjsvalue_cast(variant2), (QObject*)&button); + QCOMPARE(qjsvalue_cast(variant2), (QWidget*)&button); + QCOMPARE(qjsvalue_cast(variant2), &button); + + QJSValue variant3 = eng.newVariant(qVariantFromValue(&button)); + QCOMPARE(variant3.toQObject(), (QObject*)0); + QCOMPARE(qjsvalue_cast(variant3), (QObject*)0); + QCOMPARE(qjsvalue_cast(variant3), (QWidget*)0); + QCOMPARE(qjsvalue_cast(variant3), &button); +} + +void tst_QJSValue::toObject() +{ + QJSEngine eng; + + QJSValue undefined = eng.undefinedValue(); + QCOMPARE(undefined.toObject().isValid(), false); + QVERIFY(undefined.isUndefined()); + + QJSValue null = eng.nullValue(); + QCOMPARE(null.toObject().isValid(), false); + QVERIFY(null.isNull()); + + { + QJSValue falskt = QJSValue(&eng, false); + { + QJSValue tmp = falskt.toObject(); + QCOMPARE(tmp.isObject(), true); + QCOMPARE(tmp.toNumber(), falskt.toNumber()); + } + QVERIFY(falskt.isBool()); + + QJSValue sant = QJSValue(&eng, true); + { + QJSValue tmp = sant.toObject(); + QCOMPARE(tmp.isObject(), true); + QCOMPARE(tmp.toNumber(), sant.toNumber()); + } + QVERIFY(sant.isBool()); + + QJSValue number = QJSValue(&eng, 123.0); + { + QJSValue tmp = number.toObject(); + QCOMPARE(tmp.isObject(), true); + QCOMPARE(tmp.toNumber(), number.toNumber()); + } + QVERIFY(number.isNumber()); + + QJSValue str = QJSValue(&eng, QString("ciao")); + { + QJSValue tmp = str.toObject(); + QCOMPARE(tmp.isObject(), true); + QCOMPARE(tmp.toString(), str.toString()); + } + QVERIFY(str.isString()); + } + + QJSValue object = eng.newObject(); + { + QJSValue tmp = object.toObject(); + QCOMPARE(tmp.isObject(), true); + } + + QJSValue qobject = eng.newQObject(this); + QCOMPARE(qobject.toObject().isValid(), true); + + QJSValue inv; + QCOMPARE(inv.toObject().isValid(), false); + + // V2 constructors: in this case, you have to use QScriptEngine::toObject() + { + QJSValue undefined = QJSValue(QJSValue::UndefinedValue); + QVERIFY(!undefined.toObject().isValid()); + QVERIFY(!eng.toObject(undefined).isValid()); + QVERIFY(undefined.isUndefined()); + + QJSValue null = QJSValue(QJSValue::NullValue); + QVERIFY(!null.toObject().isValid()); + QVERIFY(!eng.toObject(null).isValid()); + QVERIFY(null.isNull()); + + QJSValue falskt = QJSValue(false); + QVERIFY(!falskt.toObject().isValid()); + { + QJSValue tmp = eng.toObject(falskt); + QVERIFY(tmp.isObject()); + QVERIFY(tmp.toBool()); + } + QVERIFY(falskt.isBool()); + + QJSValue sant = QJSValue(true); + QVERIFY(!sant.toObject().isValid()); + { + QJSValue tmp = eng.toObject(sant); + QVERIFY(tmp.isObject()); + QVERIFY(tmp.toBool()); + } + QVERIFY(sant.isBool()); + + QJSValue number = QJSValue(123.0); + QVERIFY(!number.toObject().isValid()); + { + QJSValue tmp = eng.toObject(number); + QVERIFY(tmp.isObject()); + QCOMPARE(tmp.toInt32(), number.toInt32()); + } + QVERIFY(number.isNumber()); + + QJSValue str = QJSValue(QString::fromLatin1("ciao")); + QVERIFY(!str.toObject().isValid()); + { + QJSValue tmp = eng.toObject(str); + QVERIFY(tmp.isObject()); + QCOMPARE(tmp.toString(), QString::fromLatin1("ciao")); + } + QVERIFY(str.isString()); + } +} + +void tst_QJSValue::toDateTime() +{ + QJSEngine eng; + QDateTime dt = eng.evaluate("new Date(0)").toDateTime(); + QVERIFY(dt.isValid()); + QCOMPARE(dt.timeSpec(), Qt::LocalTime); + QCOMPARE(dt.toUTC(), QDateTime(QDate(1970, 1, 1), QTime(0, 0, 0), Qt::UTC)); + + QVERIFY(!eng.evaluate("[]").toDateTime().isValid()); + QVERIFY(!eng.evaluate("{}").toDateTime().isValid()); + QVERIFY(!eng.globalObject().toDateTime().isValid()); + QVERIFY(!QJSValue().toDateTime().isValid()); + QVERIFY(!QJSValue(123).toDateTime().isValid()); + QVERIFY(!QJSValue(false).toDateTime().isValid()); + QVERIFY(!eng.nullValue().toDateTime().isValid()); + QVERIFY(!eng.undefinedValue().toDateTime().isValid()); +} + +void tst_QJSValue::toRegExp() +{ + QJSEngine eng; + { + QRegExp rx = eng.evaluate("/foo/").toRegExp(); + QVERIFY(rx.isValid()); + QCOMPARE(rx.patternSyntax(), QRegExp::RegExp2); + QCOMPARE(rx.pattern(), QString::fromLatin1("foo")); + QCOMPARE(rx.caseSensitivity(), Qt::CaseSensitive); + QVERIFY(!rx.isMinimal()); + } + { + QRegExp rx = eng.evaluate("/bar/gi").toRegExp(); + QVERIFY(rx.isValid()); + QCOMPARE(rx.patternSyntax(), QRegExp::RegExp2); + QCOMPARE(rx.pattern(), QString::fromLatin1("bar")); + QCOMPARE(rx.caseSensitivity(), Qt::CaseInsensitive); + QVERIFY(!rx.isMinimal()); + } + + QVERIFY(eng.evaluate("[]").toRegExp().isEmpty()); + QVERIFY(eng.evaluate("{}").toRegExp().isEmpty()); + QVERIFY(eng.globalObject().toRegExp().isEmpty()); + QVERIFY(QJSValue().toRegExp().isEmpty()); + QVERIFY(QJSValue(123).toRegExp().isEmpty()); + QVERIFY(QJSValue(false).toRegExp().isEmpty()); + QVERIFY(eng.nullValue().toRegExp().isEmpty()); + QVERIFY(eng.undefinedValue().toRegExp().isEmpty()); +} + +void tst_QJSValue::instanceOf_twoEngines() +{ + QJSEngine eng; + QJSValue obj = eng.newObject(); + QJSEngine otherEngine; + QTest::ignoreMessage(QtWarningMsg, "QJSValue::instanceof: cannot perform operation on a value created in a different engine"); + QCOMPARE(obj.instanceOf(otherEngine.globalObject().property("Object")), false); +} + +void tst_QJSValue::instanceOf() +{ + QJSEngine eng; + QJSValue obj = eng.newObject(); + QCOMPARE(obj.instanceOf(eng.evaluate("Object.prototype")), false); + QCOMPARE(obj.instanceOf(eng.evaluate("Array.prototype")), false); + QCOMPARE(obj.instanceOf(eng.evaluate("Function.prototype")), false); + QCOMPARE(obj.instanceOf(eng.evaluate("QObject.prototype")), false); + QCOMPARE(obj.instanceOf(QJSValue(&eng, 123)), false); + QCOMPARE(obj.instanceOf(eng.undefinedValue()), false); + QCOMPARE(obj.instanceOf(eng.nullValue()), false); + QCOMPARE(obj.instanceOf(QJSValue()), false); + + QCOMPARE(obj.instanceOf(eng.evaluate("Object")), true); + QCOMPARE(obj.instanceOf(eng.evaluate("Array")), false); + QCOMPARE(obj.instanceOf(eng.evaluate("Function")), false); + QCOMPARE(obj.instanceOf(eng.evaluate("QObject")), false); + + QJSValue arr = eng.newArray(); + QVERIFY(arr.isArray()); + QCOMPARE(arr.instanceOf(eng.evaluate("Object.prototype")), false); + QCOMPARE(arr.instanceOf(eng.evaluate("Array.prototype")), false); + QCOMPARE(arr.instanceOf(eng.evaluate("Function.prototype")), false); + QCOMPARE(arr.instanceOf(eng.evaluate("QObject.prototype")), false); + QCOMPARE(arr.instanceOf(eng.evaluate("Object")), true); + QCOMPARE(arr.instanceOf(eng.evaluate("Array")), true); + QCOMPARE(arr.instanceOf(eng.evaluate("Function")), false); + QCOMPARE(arr.instanceOf(eng.evaluate("QObject")), false); + + QCOMPARE(QJSValue().instanceOf(arr), false); +} + +void tst_QJSValue::isArray_data() +{ + newEngine(); + + QTest::addColumn("value"); + QTest::addColumn("array"); + + QTest::newRow("[]") << engine->evaluate("[]") << true; + QTest::newRow("{}") << engine->evaluate("{}") << false; + QTest::newRow("globalObject") << engine->globalObject() << false; + QTest::newRow("invalid") << QJSValue() << false; + QTest::newRow("number") << QJSValue(123) << false; + QTest::newRow("bool") << QJSValue(false) << false; + QTest::newRow("null") << engine->nullValue() << false; + QTest::newRow("undefined") << engine->undefinedValue() << false; +} + +void tst_QJSValue::isArray() +{ + QFETCH(QJSValue, value); + QFETCH(bool, array); + + QCOMPARE(value.isArray(), array); +} + +void tst_QJSValue::isDate_data() +{ + newEngine(); + + QTest::addColumn("value"); + QTest::addColumn("date"); + + QTest::newRow("date") << engine->evaluate("new Date()") << true; + QTest::newRow("[]") << engine->evaluate("[]") << false; + QTest::newRow("{}") << engine->evaluate("{}") << false; + QTest::newRow("globalObject") << engine->globalObject() << false; + QTest::newRow("invalid") << QJSValue() << false; + QTest::newRow("number") << QJSValue(123) << false; + QTest::newRow("bool") << QJSValue(false) << false; + QTest::newRow("null") << engine->nullValue() << false; + QTest::newRow("undefined") << engine->undefinedValue() << false; +} + +void tst_QJSValue::isDate() +{ + QFETCH(QJSValue, value); + QFETCH(bool, date); + + QCOMPARE(value.isDate(), date); +} + +void tst_QJSValue::isError_propertiesOfGlobalObject() +{ + QStringList errors; + errors << "Error" + << "EvalError" + << "RangeError" + << "ReferenceError" + << "SyntaxError" + << "TypeError" + << "URIError"; + QJSEngine eng; + for (int i = 0; i < errors.size(); ++i) { + QJSValue ctor = eng.globalObject().property(errors.at(i)); + QVERIFY(ctor.isFunction()); + QVERIFY(ctor.property("prototype").isError()); + } +} + +void tst_QJSValue::isError_data() +{ + newEngine(); + + QTest::addColumn("value"); + QTest::addColumn("error"); + + QTest::newRow("syntax error") << engine->evaluate("%fsdg's") << true; + QTest::newRow("[]") << engine->evaluate("[]") << false; + QTest::newRow("{}") << engine->evaluate("{}") << false; + QTest::newRow("globalObject") << engine->globalObject() << false; + QTest::newRow("invalid") << QJSValue() << false; + QTest::newRow("number") << QJSValue(123) << false; + QTest::newRow("bool") << QJSValue(false) << false; + QTest::newRow("null") << engine->nullValue() << false; + QTest::newRow("undefined") << engine->undefinedValue() << false; + QTest::newRow("newObject") << engine->newObject() << false; + QTest::newRow("new Object") << engine->evaluate("new Object()") << false; +} + +void tst_QJSValue::isError() +{ + QFETCH(QJSValue, value); + QFETCH(bool, error); + + QCOMPARE(value.isError(), error); +} + +void tst_QJSValue::isRegExp_data() +{ + newEngine(); + + QTest::addColumn("value"); + QTest::addColumn("regexp"); + + QTest::newRow("/foo/") << engine->evaluate("/foo/") << true; + QTest::newRow("[]") << engine->evaluate("[]") << false; + QTest::newRow("{}") << engine->evaluate("{}") << false; + QTest::newRow("globalObject") << engine->globalObject() << false; + QTest::newRow("invalid") << QJSValue() << false; + QTest::newRow("number") << QJSValue(123) << false; + QTest::newRow("bool") << QJSValue(false) << false; + QTest::newRow("null") << engine->nullValue() << false; + QTest::newRow("undefined") << engine->undefinedValue() << false; +} + +void tst_QJSValue::isRegExp() +{ + QFETCH(QJSValue, value); + QFETCH(bool, regexp); + + QCOMPARE(value.isRegExp(), regexp); +} + +#if 0 // FIXME: No c-style callbacks currently +static QJSValue getter(QScriptContext *ctx, QScriptEngine *) +{ + return ctx->thisObject().property("x"); +} + +static QJSValue setter(QScriptContext *ctx, QScriptEngine *) +{ + ctx->thisObject().setProperty("x", ctx->argument(0)); + return ctx->argument(0); +} + +static QJSValue getterSetter(QScriptContext *ctx, QScriptEngine *) +{ + if (ctx->argumentCount() > 0) + ctx->thisObject().setProperty("x", ctx->argument(0)); + return ctx->thisObject().property("x"); +} + +static QJSValue getterSetterThrowingError(QScriptContext *ctx, QScriptEngine *) +{ + if (ctx->argumentCount() > 0) + return ctx->throwError("set foo"); + else + return ctx->throwError("get foo"); +} + +static QJSValue getSet__proto__(QScriptContext *ctx, QScriptEngine *) +{ + if (ctx->argumentCount() > 0) + ctx->callee().setProperty("value", ctx->argument(0)); + return ctx->callee().property("value"); +} +#endif + +void tst_QJSValue::getSetProperty_HooliganTask162051() +{ + QJSEngine eng; + // task 162051 -- detecting whether the property is an array index or not + QVERIFY(eng.evaluate("a = []; a['00'] = 123; a['00']").strictlyEquals(QJSValue(&eng, 123))); + QVERIFY(eng.evaluate("a.length").strictlyEquals(QJSValue(&eng, 0))); + QVERIFY(eng.evaluate("a.hasOwnProperty('00')").strictlyEquals(QJSValue(&eng, true))); + QVERIFY(eng.evaluate("a.hasOwnProperty('0')").strictlyEquals(QJSValue(&eng, false))); + QVERIFY(eng.evaluate("a[0]").isUndefined()); + QVERIFY(eng.evaluate("a[0.5] = 456; a[0.5]").strictlyEquals(QJSValue(&eng, 456))); + QVERIFY(eng.evaluate("a.length").strictlyEquals(QJSValue(&eng, 0))); + QVERIFY(eng.evaluate("a.hasOwnProperty('0.5')").strictlyEquals(QJSValue(&eng, true))); + QVERIFY(eng.evaluate("a[0]").isUndefined()); + QVERIFY(eng.evaluate("a[0] = 789; a[0]").strictlyEquals(QJSValue(&eng, 789))); + QVERIFY(eng.evaluate("a.length").strictlyEquals(QJSValue(&eng, 1))); +} + +void tst_QJSValue::getSetProperty_HooliganTask183072() +{ + QJSEngine eng; + // task 183072 -- 0x800000000 is not an array index + eng.evaluate("a = []; a[0x800000000] = 123"); + QVERIFY(eng.evaluate("a.length").strictlyEquals(QJSValue(&eng, 0))); + QVERIFY(eng.evaluate("a[0]").isUndefined()); + QVERIFY(eng.evaluate("a[0x800000000]").strictlyEquals(QJSValue(&eng, 123))); +} + +void tst_QJSValue::getSetProperty_propertyRemoval() +{ + // test property removal (setProperty(QJSValue())) + QJSEngine eng; + QJSValue object = eng.newObject(); + QJSValue str = QJSValue(&eng, QLatin1String("bar")); + QJSValue num = QJSValue(&eng, 123.0); + + object.setProperty("foo", num); + QCOMPARE(object.property("foo").strictlyEquals(num), true); + object.setProperty("bar", str); + QCOMPARE(object.property("bar").strictlyEquals(str), true); + object.setProperty("foo", QJSValue()); + QCOMPARE(object.property("foo").isValid(), false); + QCOMPARE(object.property("bar").strictlyEquals(str), true); + object.setProperty("foo", num); + QCOMPARE(object.property("foo").strictlyEquals(num), true); + QCOMPARE(object.property("bar").strictlyEquals(str), true); + object.setProperty("bar", QJSValue()); + QCOMPARE(object.property("bar").isValid(), false); + QCOMPARE(object.property("foo").strictlyEquals(num), true); + object.setProperty("foo", QJSValue()); + object.setProperty("foo", QJSValue()); + + eng.globalObject().setProperty("object3", object); + QCOMPARE(eng.evaluate("object3.hasOwnProperty('foo')") + .strictlyEquals(QJSValue(&eng, false)), true); + object.setProperty("foo", num); + QCOMPARE(eng.evaluate("object3.hasOwnProperty('foo')") + .strictlyEquals(QJSValue(&eng, true)), true); + eng.globalObject().setProperty("object3", QJSValue()); + QCOMPARE(eng.evaluate("this.hasOwnProperty('object3')") + .strictlyEquals(QJSValue(&eng, false)), true); +} + +void tst_QJSValue::getSetProperty_resolveMode() +{ + // test ResolveMode + QJSEngine eng; + QJSValue object = eng.newObject(); + QJSValue prototype = eng.newObject(); + object.setPrototype(prototype); + QJSValue num2 = QJSValue(&eng, 456.0); + prototype.setProperty("propertyInPrototype", num2); + // default is ResolvePrototype + QCOMPARE(object.property("propertyInPrototype") + .strictlyEquals(num2), true); +#if 0 // FIXME: ResolveFlags removed from API + QCOMPARE(object.property("propertyInPrototype", QJSValue::ResolvePrototype) + .strictlyEquals(num2), true); + QCOMPARE(object.property("propertyInPrototype", QJSValue::ResolveLocal) + .isValid(), false); + QCOMPARE(object.property("propertyInPrototype", QJSValue::ResolveScope) + .strictlyEquals(num2), false); + QCOMPARE(object.property("propertyInPrototype", QJSValue::ResolveFull) + .strictlyEquals(num2), true); +#endif +} + +void tst_QJSValue::getSetProperty_twoEngines() +{ + QJSEngine engine; + QJSValue object = engine.newObject(); + + QJSEngine otherEngine; + QJSValue otherNum = QJSValue(&otherEngine, 123); + QTest::ignoreMessage(QtWarningMsg, "QJSValue::setProperty(oof) failed: cannot set value created in a different engine"); + object.setProperty("oof", otherNum); + QCOMPARE(object.property("oof").isValid(), false); +} + + +void tst_QJSValue::getSetProperty_gettersAndSetters() +{ +#if 0 // FIXME: No setters/getters right now + QScriptEngine eng; + QJSValue str = QJSValue(&eng, QLatin1String("bar")); + QJSValue num = QJSValue(&eng, 123.0); + QJSValue object = eng.newObject(); + for (int x = 0; x < 2; ++x) { + object.setProperty("foo", QJSValue()); + // getter() returns this.x + object.setProperty("foo", eng.newFunction(getter), + QJSValue::PropertyGetter | QJSValue::UserRange); + QCOMPARE(object.propertyFlags("foo") & ~QJSValue::UserRange, + QJSValue::PropertyGetter ); + + QEXPECT_FAIL("", "QTBUG-17615: User-range flags are not retained for getter/setter properties", Continue); + QCOMPARE(object.propertyFlags("foo"), + QJSValue::PropertyGetter | QJSValue::UserRange); + object.setProperty("x", num); + QCOMPARE(object.property("foo").strictlyEquals(num), true); + + // setter() sets this.x + object.setProperty("foo", eng.newFunction(setter), + QJSValue::PropertySetter); + QCOMPARE(object.propertyFlags("foo") & ~QJSValue::UserRange, + QJSValue::PropertySetter | QJSValue::PropertyGetter); + + QCOMPARE(object.propertyFlags("foo"), + QJSValue::PropertySetter | QJSValue::PropertyGetter); + object.setProperty("foo", str); + QCOMPARE(object.property("x").strictlyEquals(str), true); + QCOMPARE(object.property("foo").strictlyEquals(str), true); + + // kill the getter + object.setProperty("foo", QJSValue(), QJSValue::PropertyGetter); + QVERIFY(!(object.propertyFlags("foo") & QJSValue::PropertyGetter)); + QVERIFY(object.propertyFlags("foo") & QJSValue::PropertySetter); + QCOMPARE(object.property("foo").isUndefined(), true); + + // setter should still work + object.setProperty("foo", num); + QCOMPARE(object.property("x").strictlyEquals(num), true); + + // kill the setter too + object.setProperty("foo", QJSValue(), QJSValue::PropertySetter); + QVERIFY(!(object.propertyFlags("foo") & QJSValue::PropertySetter)); + // now foo is just a regular property + object.setProperty("foo", str); + QCOMPARE(object.property("x").strictlyEquals(num), true); + QCOMPARE(object.property("foo").strictlyEquals(str), true); + } + + for (int x = 0; x < 2; ++x) { + object.setProperty("foo", QJSValue()); + // setter() sets this.x + object.setProperty("foo", eng.newFunction(setter), QJSValue::PropertySetter); + object.setProperty("foo", str); + QCOMPARE(object.property("x").strictlyEquals(str), true); + QCOMPARE(object.property("foo").isUndefined(), true); + + // getter() returns this.x + object.setProperty("foo", eng.newFunction(getter), QJSValue::PropertyGetter); + object.setProperty("x", num); + QCOMPARE(object.property("foo").strictlyEquals(num), true); + + // kill the setter + object.setProperty("foo", QJSValue(), QJSValue::PropertySetter); + object.setProperty("foo", str); + + // getter should still work + QCOMPARE(object.property("foo").strictlyEquals(num), true); + + // kill the getter too + object.setProperty("foo", QJSValue(), QJSValue::PropertyGetter); + // now foo is just a regular property + object.setProperty("foo", str); + QCOMPARE(object.property("x").strictlyEquals(num), true); + QCOMPARE(object.property("foo").strictlyEquals(str), true); + } + + // use a single function as both getter and setter + object.setProperty("foo", QJSValue()); + object.setProperty("foo", eng.newFunction(getterSetter), + QJSValue::PropertyGetter | QJSValue::PropertySetter); + QCOMPARE(object.propertyFlags("foo"), + QJSValue::PropertyGetter | QJSValue::PropertySetter); + object.setProperty("x", num); + QCOMPARE(object.property("foo").strictlyEquals(num), true); + + // killing the getter will preserve the setter, even though they are the same function + object.setProperty("foo", QJSValue(), QJSValue::PropertyGetter); + QVERIFY(object.propertyFlags("foo") & QJSValue::PropertySetter); + QCOMPARE(object.property("foo").isUndefined(), true); +#endif +} + +void tst_QJSValue::getSetProperty_gettersAndSettersThrowErrorNative() +{ +#if 0 // FIXME: No setters/getters right now + // getter/setter that throws an error + QScriptEngine eng; + QJSValue str = QJSValue(&eng, "bar"); + QJSValue object = eng.newObject(); + + object.setProperty("foo", eng.newFunction(getterSetterThrowingError), + QJSValue::PropertyGetter | QJSValue::PropertySetter); + QVERIFY(!eng.hasUncaughtException()); + QJSValue ret = object.property("foo"); + QVERIFY(ret.isError()); + QVERIFY(eng.hasUncaughtException()); + QVERIFY(ret.strictlyEquals(eng.uncaughtException())); + QCOMPARE(ret.toString(), QLatin1String("Error: get foo")); + eng.evaluate("Object"); // clear exception state... + QVERIFY(!eng.hasUncaughtException()); + object.setProperty("foo", str); + QVERIFY(eng.hasUncaughtException()); + QCOMPARE(eng.uncaughtException().toString(), QLatin1String("Error: set foo")); +#endif +} + +void tst_QJSValue::getSetProperty_gettersAndSettersThrowErrorJS() +{ + // getter/setter that throws an error (from js function) + QJSEngine eng; + QJSValue str = QJSValue(&eng, QLatin1String("bar")); + + eng.evaluate("o = new Object; " + "o.__defineGetter__('foo', function() { throw new Error('get foo') }); " + "o.__defineSetter__('foo', function() { throw new Error('set foo') }); "); + QJSValue object = eng.evaluate("o"); + QVERIFY(!eng.hasUncaughtException()); + QJSValue ret = object.property("foo"); + QVERIFY(ret.isError()); + QVERIFY(eng.hasUncaughtException()); + QVERIFY(ret.strictlyEquals(eng.uncaughtException())); + QCOMPARE(ret.toString(), QLatin1String("Error: get foo")); + eng.evaluate("Object"); // clear exception state... + QVERIFY(!eng.hasUncaughtException()); + object.setProperty("foo", str); + QVERIFY(eng.hasUncaughtException()); + QCOMPARE(eng.uncaughtException().toString(), QLatin1String("Error: set foo")); +} + +void tst_QJSValue::getSetProperty_gettersAndSettersOnNative() +{ +#if 0 // FIXME: No c-style functions right now + // attempt to install getter+setter on built-in (native) property + QScriptEngine eng; + QJSValue object = eng.newObject(); + QVERIFY(object.property("__proto__").strictlyEquals(object.prototype())); + + QJSValue fun = eng.newFunction(getSet__proto__); + fun.setProperty("value", QJSValue(&eng, "boo")); +/* QTest::ignoreMessage(QtWarningMsg, "QJSValue::setProperty() failed: " + "cannot set getter or setter of native property " + "`__proto__'");*/ + object.setProperty("__proto__", fun, + QJSValue::PropertyGetter | QJSValue::PropertySetter + | QJSValue::UserRange); + QVERIFY(object.property("__proto__").strictlyEquals(object.prototype())); + + object.setProperty("__proto__", QJSValue(), + QJSValue::PropertyGetter | QJSValue::PropertySetter); + QVERIFY(object.property("__proto__").strictlyEquals(object.prototype())); +#endif +} + +void tst_QJSValue::getSetProperty_gettersAndSettersOnGlobalObject() +{ +#if 0 // FIXME: No c-style functions right now + // global property that's a getter+setter + QScriptEngine eng; + eng.globalObject().setProperty("globalGetterSetterProperty", eng.newFunction(getterSetter), + QJSValue::PropertyGetter | QJSValue::PropertySetter); + eng.evaluate("globalGetterSetterProperty = 123"); + { + QJSValue ret = eng.evaluate("globalGetterSetterProperty"); + QVERIFY(ret.isNumber()); + QVERIFY(ret.strictlyEquals(QJSValue(&eng, 123))); + } + QCOMPARE(eng.evaluate("typeof globalGetterSetterProperty").toString(), + QString::fromLatin1("number")); + { + QJSValue ret = eng.evaluate("this.globalGetterSetterProperty()"); + QVERIFY(ret.isError()); + QCOMPARE(ret.toString(), QString::fromLatin1("TypeError: Property 'globalGetterSetterProperty' of object # is not a function")); + } + { + QJSValue ret = eng.evaluate("new this.globalGetterSetterProperty()"); + QVERIFY(ret.isError()); + QCOMPARE(ret.toString(), QString::fromLatin1("TypeError: number is not a function")); + } +#endif +} + +void tst_QJSValue::getSetProperty_gettersAndSettersChange() +{ +#if 0 // FIXME: No setters/getters API right now + // "upgrading" an existing property to become a getter+setter + QScriptEngine eng; + QJSValue object = eng.newObject(); + QJSValue num(&eng, 123); + object.setProperty("foo", num); + object.setProperty("foo", eng.newFunction(getterSetter), + QJSValue::PropertyGetter | QJSValue::PropertySetter); + QVERIFY(!object.property("x").isValid()); + object.setProperty("foo", num); + QVERIFY(object.property("x").equals(num)); + + eng.globalObject().setProperty("object", object); + QJSValue res = eng.evaluate("object.x = 89; var a = object.foo; object.foo = 65; a"); + QCOMPARE(res.toInt32(), 89); + QCOMPARE(object.property("x").toInt32(), 65); + QCOMPARE(object.property("foo").toInt32(), 65); +#endif +} + +void tst_QJSValue::getSetProperty_array() +{ + QJSEngine eng; + QJSValue str = QJSValue(&eng, QLatin1String("bar")); + QJSValue num = QJSValue(&eng, 123.0); + QJSValue array = eng.newArray(); + + QVERIFY(array.isArray()); + array.setProperty(0, num); + QCOMPARE(array.property(0).toNumber(), num.toNumber()); + QCOMPARE(array.property("0").toNumber(), num.toNumber()); + QCOMPARE(array.property("length").toUInt32(), quint32(1)); + array.setProperty(1, str); + QCOMPARE(array.property(1).toString(), str.toString()); + QCOMPARE(array.property("1").toString(), str.toString()); + QCOMPARE(array.property("length").toUInt32(), quint32(2)); + array.setProperty("length", QJSValue(&eng, 1)); + QCOMPARE(array.property("length").toUInt32(), quint32(1)); + QCOMPARE(array.property(1).isValid(), false); +} + +void tst_QJSValue::getSetProperty_gettersAndSettersStupid() +{ +#if 0 // FIXME: No setters/getters API right now + //removing unexisting Setter or Getter should not crash. + QScriptEngine eng; + QJSValue num = QJSValue(&eng, 123.0); + + { + QJSValue object = eng.newObject(); + object.setProperty("foo", QJSValue(), QJSValue::PropertyGetter); + QVERIFY(!object.property("foo").isValid()); + object.setProperty("foo", num); + QCOMPARE(object.property("foo").strictlyEquals(num), true); + } + + { + QJSValue object = eng.newObject(); + object.setProperty("foo", QJSValue(), QJSValue::PropertySetter); + QVERIFY(!object.property("foo").isValid()); + object.setProperty("foo", num); + QCOMPARE(object.property("foo").strictlyEquals(num), true); + } + + { + QJSValue object = eng.globalObject(); + object.setProperty("foo", QJSValue(), QJSValue::PropertySetter); + object.setProperty("foo", QJSValue(), QJSValue::PropertyGetter); + QVERIFY(!object.property("foo").isValid()); + object.setProperty("foo", num); + QCOMPARE(object.property("foo").strictlyEquals(num), true); + } +#endif +} + +void tst_QJSValue::getSetProperty() +{ + QJSEngine eng; + + QJSValue object = eng.newObject(); + + QJSValue str = QJSValue(&eng, QLatin1String("bar")); + object.setProperty("foo", str); + QCOMPARE(object.property("foo").toString(), str.toString()); + + QJSValue num = QJSValue(&eng, 123.0); + object.setProperty("baz", num); + QCOMPARE(object.property("baz").toNumber(), num.toNumber()); + + QJSValue strstr = QJSValue("bar"); + QCOMPARE(strstr.engine(), (QJSEngine *)0); + object.setProperty("foo", strstr); + QCOMPARE(object.property("foo").toString(), strstr.toString()); + QCOMPARE(strstr.engine(), &eng); // the value has been bound to the engine + + QJSValue numnum = QJSValue(123.0); + object.setProperty("baz", numnum); + QCOMPARE(object.property("baz").toNumber(), numnum.toNumber()); + + QJSValue inv; + inv.setProperty("foo", num); + QCOMPARE(inv.property("foo").isValid(), false); + + eng.globalObject().setProperty("object", object); + +#if 0 // FIXME: no setProperty API with flags + // ReadOnly + object.setProperty("readOnlyProperty", num, QJSValue::ReadOnly); + QCOMPARE(object.propertyFlags("readOnlyProperty"), QJSValue::ReadOnly); + QCOMPARE(object.property("readOnlyProperty").strictlyEquals(num), true); + eng.evaluate("object.readOnlyProperty = !object.readOnlyProperty"); + QCOMPARE(object.property("readOnlyProperty").strictlyEquals(num), true); + // should still be part of enumeration + { + QJSValue ret = eng.evaluate( + "found = false;" + "for (var p in object) {" + " if (p == 'readOnlyProperty') {" + " found = true; break;" + " }" + "} found"); + QCOMPARE(ret.strictlyEquals(QJSValue(&eng, true)), true); + } + // should still be deletable + { + QJSValue ret = eng.evaluate("delete object.readOnlyProperty"); + QCOMPARE(ret.strictlyEquals(QJSValue(&eng, true)), true); + QCOMPARE(object.property("readOnlyProperty").isValid(), false); + } + + // Undeletable + object.setProperty("undeletableProperty", num, QJSValue::Undeletable); + QCOMPARE(object.propertyFlags("undeletableProperty"), QJSValue::Undeletable); + QCOMPARE(object.property("undeletableProperty").strictlyEquals(num), true); + { + QJSValue ret = eng.evaluate("delete object.undeletableProperty"); + QCOMPARE(ret.strictlyEquals(QJSValue(&eng, true)), false); + QCOMPARE(object.property("undeletableProperty").strictlyEquals(num), true); + } + // should still be writable + eng.evaluate("object.undeletableProperty = object.undeletableProperty + 1"); + QCOMPARE(object.property("undeletableProperty").toNumber(), num.toNumber() + 1); + // should still be part of enumeration + { + QJSValue ret = eng.evaluate( + "found = false;" + "for (var p in object) {" + " if (p == 'undeletableProperty') {" + " found = true; break;" + " }" + "} found"); + QCOMPARE(ret.strictlyEquals(QJSValue(&eng, true)), true); + } + // should still be deletable from C++ + object.setProperty("undeletableProperty", QJSValue()); + QEXPECT_FAIL("", "QTBUG-17617: With JSC-based back-end, undeletable properties can't be deleted from C++", Continue); + QVERIFY(!object.property("undeletableProperty").isValid()); + QEXPECT_FAIL("", "QTBUG-17617: With JSC-based back-end, undeletable properties can't be deleted from C++", Continue); + QCOMPARE(object.propertyFlags("undeletableProperty"), 0); + + // SkipInEnumeration + object.setProperty("dontEnumProperty", num, QJSValue::SkipInEnumeration); + QCOMPARE(object.propertyFlags("dontEnumProperty"), QJSValue::SkipInEnumeration); + QCOMPARE(object.property("dontEnumProperty").strictlyEquals(num), true); + // should not be part of enumeration + { + QJSValue ret = eng.evaluate( + "found = false;" + "for (var p in object) {" + " if (p == 'dontEnumProperty') {" + " found = true; break;" + " }" + "} found"); + QCOMPARE(ret.strictlyEquals(QJSValue(&eng, false)), true); + } + // should still be writable + eng.evaluate("object.dontEnumProperty = object.dontEnumProperty + 1"); + QCOMPARE(object.property("dontEnumProperty").toNumber(), num.toNumber() + 1); + // should still be deletable + { + QJSValue ret = eng.evaluate("delete object.dontEnumProperty"); + QCOMPARE(ret.strictlyEquals(QJSValue(&eng, true)), true); + QCOMPARE(object.property("dontEnumProperty").isValid(), false); + } + + // change flags + object.setProperty("flagProperty", str); + QCOMPARE(object.propertyFlags("flagProperty"), static_cast(0)); + + QEXPECT_FAIL("", "FIXME: v8 does not support changing flags of existing properties", Continue); + //v8::i::JSObject::SetProperty(LookupResult* result, ... ) does not take in account the attributes + // if the result->isFound() + object.setProperty("flagProperty", str, QJSValue::ReadOnly); + QCOMPARE(object.propertyFlags("flagProperty"), QJSValue::ReadOnly); + + QEXPECT_FAIL("", "FIXME: v8 does not support changing flags of existing properties", Continue); + object.setProperty("flagProperty", str, object.propertyFlags("flagProperty") | QJSValue::SkipInEnumeration); + QCOMPARE(object.propertyFlags("flagProperty"), QJSValue::ReadOnly | QJSValue::SkipInEnumeration); + + QEXPECT_FAIL("", "FIXME: v8 does not support changing flags of existing properties", Continue); + object.setProperty("flagProperty", str, QJSValue::KeepExistingFlags); + QCOMPARE(object.propertyFlags("flagProperty"), QJSValue::ReadOnly | QJSValue::SkipInEnumeration); + + QEXPECT_FAIL("", "FIXME: v8 does not support UserRange", Continue); + object.setProperty("flagProperty", str, QJSValue::UserRange); + QCOMPARE(object.propertyFlags("flagProperty"), QJSValue::UserRange); + + // flags of property in the prototype + { + QJSValue object2 = eng.newObject(); + object2.setPrototype(object); + QCOMPARE(object2.propertyFlags("flagProperty", QJSValue::ResolveLocal), 0); + QEXPECT_FAIL("", "FIXME: v8 does not support UserRange", Continue); + QCOMPARE(object2.propertyFlags("flagProperty"), QJSValue::UserRange); + } + + // using interned strings + QScriptString foo = eng.toStringHandle("foo"); + + object.setProperty(foo, QJSValue()); + QVERIFY(!object.property(foo).isValid()); + + object.setProperty(foo, num); + QVERIFY(object.property(foo).strictlyEquals(num)); + QVERIFY(object.property("foo").strictlyEquals(num)); + QVERIFY(object.propertyFlags(foo) == 0); +#endif + + // Setting index property on non-Array + object.setProperty(13, num); + QVERIFY(object.property(13).equals(num)); +} + +void tst_QJSValue::arrayElementGetterSetter() +{ +#if 0 // FIXME: No c-style functions + QScriptEngine eng; + QJSValue obj = eng.newObject(); + obj.setProperty(1, eng.newFunction(getterSetter), QJSValue::PropertyGetter|QJSValue::PropertySetter); + { + QJSValue num(123); + obj.setProperty("x", num); + QJSValue ret = obj.property(1); + QVERIFY(ret.isValid()); + QVERIFY(ret.equals(num)); + } + { + QJSValue num(456); + obj.setProperty(1, num); + QJSValue ret = obj.property(1); + QVERIFY(ret.isValid()); + QVERIFY(ret.equals(num)); + QVERIFY(ret.equals(obj.property("1"))); + } + QCOMPARE(obj.propertyFlags("1"), QJSValue::PropertyGetter|QJSValue::PropertySetter); + + obj.setProperty(1, QJSValue(), QJSValue::PropertyGetter|QJSValue::PropertySetter); + QVERIFY(obj.propertyFlags("1") == 0); +#endif +} + +void tst_QJSValue::getSetPrototype_cyclicPrototype() +{ + QJSEngine eng; + QJSValue prototype = eng.newObject(); + QJSValue object = eng.newObject(); + object.setPrototype(prototype); + + QJSValue previousPrototype = prototype.prototype(); + QTest::ignoreMessage(QtWarningMsg, "QJSValue::setPrototype() failed: cyclic prototype value"); + prototype.setPrototype(prototype); + QCOMPARE(prototype.prototype().strictlyEquals(previousPrototype), true); + + object.setPrototype(prototype); + QTest::ignoreMessage(QtWarningMsg, "QJSValue::setPrototype() failed: cyclic prototype value"); + prototype.setPrototype(object); + QCOMPARE(prototype.prototype().strictlyEquals(previousPrototype), true); + +} + +void tst_QJSValue::getSetPrototype_evalCyclicPrototype() +{ + QJSEngine eng; + QJSValue ret = eng.evaluate("o = { }; p = { }; o.__proto__ = p; p.__proto__ = o"); + QCOMPARE(eng.hasUncaughtException(), true); + QVERIFY(ret.strictlyEquals(eng.uncaughtException())); + QCOMPARE(ret.isError(), true); + QCOMPARE(ret.toString(), QLatin1String("Error: Cyclic __proto__ value")); +} + +void tst_QJSValue::getSetPrototype_eval() +{ + QJSEngine eng; + QJSValue ret = eng.evaluate("p = { }; p.__proto__ = { }"); + QCOMPARE(eng.hasUncaughtException(), false); + QCOMPARE(ret.isError(), false); +} + +void tst_QJSValue::getSetPrototype_invalidPrototype() +{ + QJSEngine eng; + QJSValue inv; + QJSValue object = eng.newObject(); + QJSValue proto = object.prototype(); + QVERIFY(object.prototype().strictlyEquals(proto)); + inv.setPrototype(object); + QCOMPARE(inv.prototype().isValid(), false); + object.setPrototype(inv); + QVERIFY(object.prototype().strictlyEquals(proto)); +} + +void tst_QJSValue::getSetPrototype_twoEngines() +{ + QJSEngine eng; + QJSValue prototype = eng.newObject(); + QJSValue object = eng.newObject(); + object.setPrototype(prototype); + QJSEngine otherEngine; + QJSValue newPrototype = otherEngine.newObject(); + QTest::ignoreMessage(QtWarningMsg, "QJSValue::setPrototype() failed: cannot set a prototype created in a different engine"); + object.setPrototype(newPrototype); + QCOMPARE(object.prototype().strictlyEquals(prototype), true); + +} + +void tst_QJSValue::getSetPrototype_null() +{ + QJSEngine eng; + QJSValue object = eng.newObject(); + object.setPrototype(QJSValue(QJSValue::NullValue)); + QVERIFY(object.prototype().isNull()); + + QJSValue newProto = eng.newObject(); + object.setPrototype(newProto); + QVERIFY(object.prototype().equals(newProto)); + + object.setPrototype(QJSValue(&eng, QJSValue::NullValue)); + QVERIFY(object.prototype().isNull()); +} + +void tst_QJSValue::getSetPrototype_notObjectOrNull() +{ + QJSEngine eng; + QJSValue object = eng.newObject(); + QJSValue originalProto = object.prototype(); + + // bool + object.setPrototype(true); + QVERIFY(object.prototype().equals(originalProto)); + object.setPrototype(QJSValue(&eng, true)); + QVERIFY(object.prototype().equals(originalProto)); + + // number + object.setPrototype(123); + QVERIFY(object.prototype().equals(originalProto)); + object.setPrototype(QJSValue(&eng, 123)); + QVERIFY(object.prototype().equals(originalProto)); + + // string + object.setPrototype("foo"); + QVERIFY(object.prototype().equals(originalProto)); + object.setPrototype(QJSValue(&eng, QLatin1String("foo"))); + QVERIFY(object.prototype().equals(originalProto)); + + // undefined + object.setPrototype(QJSValue(QJSValue::UndefinedValue)); + QVERIFY(object.prototype().equals(originalProto)); + object.setPrototype(QJSValue(&eng, QJSValue::UndefinedValue)); + QVERIFY(object.prototype().equals(originalProto)); +} + +void tst_QJSValue::getSetPrototype() +{ + QJSEngine eng; + QJSValue prototype = eng.newObject(); + QJSValue object = eng.newObject(); + object.setPrototype(prototype); + QCOMPARE(object.prototype().strictlyEquals(prototype), true); +} + +void tst_QJSValue::getSetScope() +{ +#if 0 // FIXME: No QJSValue::scope + QScriptEngine eng; + + QJSValue object = eng.newObject(); + QCOMPARE(object.scope().isValid(), false); + + QJSValue object2 = eng.newObject(); + object2.setScope(object); + + QEXPECT_FAIL("", "FIXME: scope not implemented yet", Abort); + QCOMPARE(object2.scope().strictlyEquals(object), true); + + object.setProperty("foo", 123); + QVERIFY(!object2.property("foo").isValid()); + { + QJSValue ret = object2.property("foo", QJSValue::ResolveScope); + QVERIFY(ret.isNumber()); + QCOMPARE(ret.toInt32(), 123); + } + + QJSValue inv; + inv.setScope(object); + QCOMPARE(inv.scope().isValid(), false); + + QScriptEngine otherEngine; + QJSValue object3 = otherEngine.newObject(); + QTest::ignoreMessage(QtWarningMsg, "QJSValue::setScope() failed: cannot set a scope object created in a different engine"); + object2.setScope(object3); + QCOMPARE(object2.scope().strictlyEquals(object), true); + + object2.setScope(QJSValue()); + QVERIFY(!object2.scope().isValid()); +#endif +} + +void tst_QJSValue::getSetData_objects_data() +{ +#if 0 // FIXME: no setData/data API + newEngine(); + + QTest::addColumn("object"); + + QTest::newRow("object from evaluate") << engine->evaluate("new Object()"); + QTest::newRow("object from engine") << engine->newObject(); + QTest::newRow("Array") << engine->newArray(); + QTest::newRow("Date") << engine->newDate(12324); + QTest::newRow("QObject") << engine->newQObject(this); + QTest::newRow("RegExp") << engine->newRegExp(QRegExp()); +#endif +} + +void tst_QJSValue::getSetData_objects() +{ +#if 0 // FIXME: no setData/data API + QFETCH(QJSValue, object); + + QVERIFY(!object.data().isValid()); + QJSValue v1(true); + object.setData(v1); + QVERIFY(object.data().strictlyEquals(v1)); + QJSValue v2(123); + object.setData(v2); + QVERIFY(object.data().strictlyEquals(v2)); + QJSValue v3 = engine->newObject(); + object.setData(v3); + QVERIFY(object.data().strictlyEquals(v3)); + object.setData(QJSValue()); + QVERIFY(!object.data().isValid()); +#endif +} + +void tst_QJSValue::getSetData_nonObjects_data() +{ +#if 0 // FIXME: no setData/data API + newEngine(); + + QTest::addColumn("value"); + + QTest::newRow("undefined (bound)") << engine->undefinedValue(); + QTest::newRow("null (bound)") << engine->nullValue(); + QTest::newRow("string (bound)") << QJSValue(engine, "Pong"); + QTest::newRow("bool (bound)") << QJSValue(engine, false); + + QTest::newRow("undefined") << QJSValue(QJSValue::UndefinedValue); + QTest::newRow("null") << QJSValue(QJSValue::NullValue); + QTest::newRow("string") << QJSValue("Pong"); + QTest::newRow("bool") << QJSValue(true); +#endif +} + +void tst_QJSValue::getSetData_nonObjects() +{ +#if 0 // FIXME: no setData/data API + QFETCH(QJSValue, value); + + QVERIFY(!value.data().isValid()); + QJSValue v1(true); + value.setData(v1); + QVERIFY(!value.data().isValid()); + QJSValue v2(123); + value.setData(v2); + QVERIFY(!value.data().isValid()); + QJSValue v3 = engine->newObject(); + value.setData(v3); + QVERIFY(!value.data().isValid()); + value.setData(QJSValue()); + QVERIFY(!value.data().isValid()); +#endif +} + +void tst_QJSValue::setData_QTBUG15144() +{ +#if 0 // FIXME: no setData/data API + QScriptEngine eng; + QJSValue obj = eng.newObject(); + for (int i = 0; i < 10000; ++i) { + // Create an object with property 'fooN' on it, and immediately kill + // the reference to the object so it and the property name become garbage. + eng.evaluate(QString::fromLatin1("o = {}; o.foo%0 = 10; o = null;").arg(i)); + // Setting the data will cause a JS string to be allocated, which could + // trigger a GC. This should not cause a crash. + obj.setData("foodfight"); + } +#endif +} + +#if 0 // FIXME: no QScriptClass +class TestScriptClass : public QScriptClass +{ +public: + TestScriptClass(QScriptEngine *engine) : QScriptClass(engine) {} +}; + +void tst_QJSValue::getSetScriptClass_emptyClass_data() +{ + newEngine(); + QTest::addColumn("value"); + + QTest::newRow("invalid") << QJSValue(); + QTest::newRow("number") << QJSValue(123); + QTest::newRow("string") << QJSValue("pong"); + QTest::newRow("bool") << QJSValue(false); + QTest::newRow("null") << QJSValue(QJSValue::NullValue); + QTest::newRow("undefined") << QJSValue(QJSValue::UndefinedValue); + + QTest::newRow("number") << QJSValue(engine, 123); + QTest::newRow("string") << QJSValue(engine, "pong"); + QTest::newRow("bool") << QJSValue(engine, true); + QTest::newRow("null") << QJSValue(engine->nullValue()); + QTest::newRow("undefined") << QJSValue(engine->undefinedValue()); + QTest::newRow("object") << QJSValue(engine->newObject()); + QTest::newRow("date") << QJSValue(engine->evaluate("new Date()")); + QTest::newRow("qobject") << QJSValue(engine->newQObject(this)); +} + +void tst_QJSValue::getSetScriptClass_emptyClass() +{ + QFETCH(QJSValue, value); + QCOMPARE(value.scriptClass(), (QScriptClass*)0); +} + +void tst_QJSValue::getSetScriptClass_JSObjectFromCpp() +{ + QScriptEngine eng; + TestScriptClass testClass(&eng); + // object created in C++ (newObject()) + { + QJSValue obj = eng.newObject(); + obj.setScriptClass(&testClass); + QCOMPARE(obj.scriptClass(), (QScriptClass*)&testClass); + obj.setScriptClass(0); + QCOMPARE(obj.scriptClass(), (QScriptClass*)0); + } +} + +void tst_QJSValue::getSetScriptClass_JSObjectFromJS() +{ + QScriptEngine eng; + TestScriptClass testClass(&eng); + // object created in JS + { + QJSValue obj = eng.evaluate("new Object"); + QVERIFY(!eng.hasUncaughtException()); + QVERIFY(obj.isObject()); + QCOMPARE(obj.scriptClass(), (QScriptClass*)0); + obj.setScriptClass(&testClass); + QCOMPARE(obj.scriptClass(), (QScriptClass*)&testClass); + obj.setScriptClass(0); + QCOMPARE(obj.scriptClass(), (QScriptClass*)0); + } +} + +void tst_QJSValue::getSetScriptClass_QVariant() +{ + QScriptEngine eng; + TestScriptClass testClass(&eng); + // object that already has a(n internal) class + { + QJSValue obj = eng.newVariant(QUrl("http://example.com")); + QVERIFY(obj.isVariant()); + QCOMPARE(obj.scriptClass(), (QScriptClass*)0); + obj.setScriptClass(&testClass); + QCOMPARE(obj.scriptClass(), (QScriptClass*)&testClass); + QVERIFY(obj.isObject()); + QVERIFY(!obj.isVariant()); + QCOMPARE(obj.toVariant(), QVariant(QVariantMap())); + } +} + +void tst_QJSValue::getSetScriptClass_QObject() +{ + QScriptEngine eng; + TestScriptClass testClass(&eng); + { + QJSValue obj = eng.newQObject(this); + QVERIFY(obj.isQObject()); + obj.setScriptClass(&testClass); + QCOMPARE(obj.scriptClass(), (QScriptClass*)&testClass); + QVERIFY(obj.isObject()); + QVERIFY(!obj.isQObject()); + QVERIFY(obj.toQObject() == 0); + } +} +#endif + +#if 0 // FIXME: No c-style callbacks +static QJSValue getArg(QScriptContext *ctx, QScriptEngine *) +{ + return ctx->argument(0); +} + +static QJSValue evaluateArg(QScriptContext *, QScriptEngine *eng) +{ + return eng->evaluate("arguments[0]"); +} + +static QJSValue addArgs(QScriptContext *, QScriptEngine *eng) +{ + return eng->evaluate("arguments[0] + arguments[1]"); +} + +static QJSValue returnInvalidValue(QScriptContext *, QScriptEngine *) +{ + return QJSValue(); +} +#endif + +void tst_QJSValue::call_function() +{ + QJSEngine eng; + QJSValue fun = eng.evaluate("(function() { return 1; })"); + QVERIFY(fun.isFunction()); + QJSValue result = fun.call(); + QVERIFY(result.isNumber()); + QCOMPARE(result.toInt32(), 1); +} + +void tst_QJSValue::call_object() +{ + QJSEngine eng; + QJSValue Object = eng.evaluate("Object"); + QCOMPARE(Object.isFunction(), true); + QJSValue result = Object.call(Object); + QCOMPARE(result.isObject(), true); +} + +void tst_QJSValue::call_newObjects() +{ + QJSEngine eng; + // test that call() doesn't construct new objects + QJSValue Number = eng.evaluate("Number"); + QJSValue Object = eng.evaluate("Object"); + QCOMPARE(Object.isFunction(), true); + QJSValueList args; + args << QJSValue(&eng, 123); + QJSValue result = Number.call(Object, args); + QCOMPARE(result.strictlyEquals(args.at(0)), true); +} + +void tst_QJSValue::call_this() +{ + QJSEngine eng; + // test that correct "this" object is used + QJSValue fun = eng.evaluate("(function() { return this; })"); + QCOMPARE(fun.isFunction(), true); + + QJSValue numberObject = QJSValue(&eng, 123.0).toObject(); + QJSValue result = fun.call(numberObject); + QCOMPARE(result.isObject(), true); + QCOMPARE(result.toNumber(), 123.0); +} + +void tst_QJSValue::call_arguments() +{ + QJSEngine eng; + // test that correct arguments are passed + + QJSValue fun = eng.evaluate("(function() { return arguments[0]; })"); + QCOMPARE(fun.isFunction(), true); + { + QJSValue result = fun.call(eng.undefinedValue()); + QCOMPARE(result.isUndefined(), true); + } + { + QJSValueList args; + args << QJSValue(&eng, 123.0); + QJSValue result = fun.call(eng.undefinedValue(), args); + QCOMPARE(result.isNumber(), true); + QCOMPARE(result.toNumber(), 123.0); + } + // V2 constructors + { + QJSValueList args; + args << QJSValue(123.0); + QJSValue result = fun.call(eng.undefinedValue(), args); + QCOMPARE(result.isNumber(), true); + QCOMPARE(result.toNumber(), 123.0); + } +#if 0 // FIXME: The feature of interpreting a passed array as argument list has been removed from the API + { + QJSValue args = eng.newArray(); + args.setProperty(0, 123); + QJSValue result = fun.call(eng.undefinedValue(), args); + QVERIFY(result.isNumber()); + QCOMPARE(result.toNumber(), 123.0); + } +#endif +} + +void tst_QJSValue::call() +{ + QJSEngine eng; + { + QJSValue fun = eng.evaluate("(function() { return arguments[1]; })"); + QCOMPARE(fun.isFunction(), true); + + { + QJSValueList args; + args << QJSValue(&eng, 123.0) << QJSValue(&eng, 456.0); + QJSValue result = fun.call(eng.undefinedValue(), args); + QCOMPARE(result.isNumber(), true); + QCOMPARE(result.toNumber(), 456.0); + } +#if 0 // FIXME: The feature of interpreting a passed array as argument list has been removed from the API + { + QJSValue args = eng.newArray(); + args.setProperty(0, 123); + args.setProperty(1, 456); + QJSValue result = fun.call(eng.undefinedValue(), args); + QVERIFY(result.isNumber()); + QCOMPARE(result.toNumber(), 456.0); + } +#endif + } + { + QJSValue fun = eng.evaluate("(function() { throw new Error('foo'); })"); + QCOMPARE(fun.isFunction(), true); + QVERIFY(!eng.hasUncaughtException()); + + { + QJSValue result = fun.call(); + QCOMPARE(result.isError(), true); + QCOMPARE(eng.hasUncaughtException(), true); + QVERIFY(result.strictlyEquals(eng.uncaughtException())); + } + } +#if 0 // FIXME: No c-style callbacks + { + eng.clearExceptions(); + QJSValue fun = eng.newFunction(getArg); + { + QJSValueList args; + args << QJSValue(&eng, 123.0); + QJSValue result = fun.call(eng.undefinedValue(), args); + QVERIFY(!eng.hasUncaughtException()); + QCOMPARE(result.isNumber(), true); + QCOMPARE(result.toNumber(), 123.0); + } + // V2 constructors + { + QJSValueList args; + args << QJSValue(123.0); + QJSValue result = fun.call(eng.undefinedValue(), args); + QCOMPARE(result.isNumber(), true); + QCOMPARE(result.toNumber(), 123.0); + } +#if 0 // FIXME: The feature of interpreting a passed array as argument list has been removed from the API + { + QJSValue args = eng.newArray(); + args.setProperty(0, 123); + QJSValue result = fun.call(eng.undefinedValue(), args); + QVERIFY(result.isNumber()); + QCOMPARE(result.toNumber(), 123.0); + } +#endif + } + { + QJSValue fun = eng.newFunction(evaluateArg); + { + QJSValueList args; + args << QJSValue(&eng, 123.0); + QJSValue result = fun.call(eng.undefinedValue(), args); + QVERIFY(!eng.hasUncaughtException()); + QCOMPARE(result.isNumber(), true); + QCOMPARE(result.toNumber(), 123.0); + } + } +#endif +} + +void tst_QJSValue::call_invalidArguments() +{ +#if 0 // FIXME: No c-style callbacks + // test that invalid arguments are handled gracefully + QScriptEngine eng; + { + QJSValue fun = eng.newFunction(getArg); + { + QJSValueList args; + args << QJSValue(); + QJSValue ret = fun.call(QJSValue(), args); + QVERIFY(!eng.hasUncaughtException()); + QCOMPARE(ret.isValid(), true); + QCOMPARE(ret.isUndefined(), true); + } + } + { + QJSValue fun = eng.newFunction(evaluateArg); + { + QJSValueList args; + args << QJSValue(); + QJSValue ret = fun.call(QJSValue(), args); + QCOMPARE(ret.isValid(), true); + QCOMPARE(ret.isUndefined(), true); + } + } + { + QJSValue fun = eng.newFunction(addArgs); + { + QJSValueList args; + args << QJSValue() << QJSValue(); + QJSValue ret = fun.call(QJSValue(), args); + QCOMPARE(ret.isValid(), true); + QCOMPARE(ret.isNumber(), true); + QCOMPARE(qIsNaN(ret.toNumber()), true); + } + } +#endif +} + +void tst_QJSValue::call_invalidReturn() +{ +#if 0 // FIXME: No c-style callbacks + // test that invalid return value is handled gracefully + QScriptEngine eng; + QJSValue fun = eng.newFunction(returnInvalidValue); + eng.globalObject().setProperty("returnInvalidValue", fun); + QJSValue ret = eng.evaluate("returnInvalidValue() + returnInvalidValue()"); + QCOMPARE(ret.isValid(), true); + QCOMPARE(ret.isNumber(), true); + QCOMPARE(qIsNaN(ret.toNumber()), true); +#endif +} + +void tst_QJSValue::call_twoEngines() +{ + QJSEngine eng; + QJSValue object = eng.evaluate("Object"); + QJSEngine otherEngine; + QJSValue fun = otherEngine.evaluate("(function() { return 1; })"); + QVERIFY(fun.isFunction()); + QTest::ignoreMessage(QtWarningMsg, "QJSValue::call() failed: " + "cannot call function with thisObject created in " + "a different engine"); + QCOMPARE(fun.call(object).isValid(), false); + QTest::ignoreMessage(QtWarningMsg, "QJSValue::call() failed: " + "cannot call function with argument created in " + "a different engine"); + QCOMPARE(fun.call(QJSValue(), QJSValueList() << QJSValue(&eng, 123)).isValid(), false); + { + QJSValue fun = eng.evaluate("Object"); + QVERIFY(fun.isFunction()); + QJSEngine eng2; + QJSValue objectInDifferentEngine = eng2.newObject(); + QJSValueList args; + args << objectInDifferentEngine; + QTest::ignoreMessage(QtWarningMsg, "QJSValue::call() failed: cannot call function with argument created in a different engine"); + fun.call(QJSValue(), args); + } +} + +void tst_QJSValue::call_array() +{ +#if 0 // FIXME: The feature of interpreting an array as argument list has been removed from the API + QScriptEngine eng; + QJSValue fun = eng.evaluate("(function() { return arguments; })"); + QVERIFY(fun.isFunction()); + QJSValue array = eng.newArray(3); + array.setProperty(0, QJSValue(&eng, 123.0)); + array.setProperty(1, QJSValue(&eng, 456.0)); + array.setProperty(2, QJSValue(&eng, 789.0)); + // call with single array object as arguments + QJSValue ret = fun.call(QJSValue(), array); + QVERIFY(!eng.hasUncaughtException()); + QCOMPARE(ret.isError(), false); + QCOMPARE(ret.property(0).strictlyEquals(array.property(0)), true); + QCOMPARE(ret.property(1).strictlyEquals(array.property(1)), true); + QCOMPARE(ret.property(2).strictlyEquals(array.property(2)), true); + // call with arguments object as arguments + QJSValue ret2 = fun.call(QJSValue(), ret); + QCOMPARE(ret2.isError(), false); + QCOMPARE(ret2.property(0).strictlyEquals(ret.property(0)), true); + QCOMPARE(ret2.property(1).strictlyEquals(ret.property(1)), true); + QCOMPARE(ret2.property(2).strictlyEquals(ret.property(2)), true); + // call with null as arguments + QJSValue ret3 = fun.call(QJSValue(), eng.nullValue()); + QCOMPARE(ret3.isError(), false); + QCOMPARE(ret3.property("length").isNumber(), true); + QCOMPARE(ret3.property("length").toNumber(), 0.0); + // call with undefined as arguments + QJSValue ret4 = fun.call(QJSValue(), eng.undefinedValue()); + QCOMPARE(ret4.isError(), false); + QCOMPARE(ret4.property("length").isNumber(), true); + QCOMPARE(ret4.property("length").toNumber(), 0.0); + // call with something else as arguments + QJSValue ret5 = fun.call(QJSValue(), QJSValue(&eng, 123.0)); + QCOMPARE(ret5.isError(), true); + // call with a non-array object as arguments + QJSValue ret6 = fun.call(QJSValue(), eng.globalObject()); + QVERIFY(ret6.isError()); + QCOMPARE(ret6.toString(), QString::fromLatin1("TypeError: Arguments must be an array")); +#endif +} + + +void tst_QJSValue::call_nonFunction_data() +{ + newEngine(); + QTest::addColumn("value"); + + QTest::newRow("invalid") << QJSValue(); + QTest::newRow("bool") << QJSValue(false); + QTest::newRow("int") << QJSValue(123); + QTest::newRow("string") << QJSValue(QString::fromLatin1("ciao")); + QTest::newRow("undefined") << QJSValue(QJSValue::UndefinedValue); + QTest::newRow("null") << QJSValue(QJSValue::NullValue); + + QTest::newRow("bool bound") << QJSValue(engine, false); + QTest::newRow("int bound") << QJSValue(engine, 123); + QTest::newRow("string bound") << QJSValue(engine, QString::fromLatin1("ciao")); + QTest::newRow("undefined bound") << engine->undefinedValue(); + QTest::newRow("null bound") << engine->nullValue(); +} + +void tst_QJSValue::call_nonFunction() +{ + // calling things that are not functions + QFETCH(QJSValue, value); + QVERIFY(!value.call().isValid()); +} + +#if 0 // FIXME: no c-style callbacks +static QJSValue ctorReturningUndefined(QScriptContext *ctx, QScriptEngine *) +{ + ctx->thisObject().setProperty("foo", 123); + return QJSValue(QJSValue::UndefinedValue); +} + +static QJSValue ctorReturningNewObject(QScriptContext *, QScriptEngine *eng) +{ + QJSValue result = eng->newObject(); + result.setProperty("bar", 456); + return result; +} +#endif + +void tst_QJSValue::construct_nonFunction_data() +{ + newEngine(); + QTest::addColumn("value"); + + QTest::newRow("invalid") << QJSValue(); + QTest::newRow("bool") << QJSValue(false); + QTest::newRow("int") << QJSValue(123); + QTest::newRow("string") << QJSValue(QString::fromLatin1("ciao")); + QTest::newRow("undefined") << QJSValue(QJSValue::UndefinedValue); + QTest::newRow("null") << QJSValue(QJSValue::NullValue); + + QTest::newRow("bool bound") << QJSValue(engine, false); + QTest::newRow("int bound") << QJSValue(engine, 123); + QTest::newRow("string bound") << QJSValue(engine, QString::fromLatin1("ciao")); + QTest::newRow("undefined bound") << engine->undefinedValue(); + QTest::newRow("null bound") << engine->nullValue(); +} + +void tst_QJSValue::construct_nonFunction() +{ + QFETCH(QJSValue, value); + QVERIFY(!value.construct().isValid()); +} + +void tst_QJSValue::construct_simple() +{ + QJSEngine eng; + QJSValue fun = eng.evaluate("(function () { this.foo = 123; })"); + QVERIFY(fun.isFunction()); + QJSValue ret = fun.construct(); + QVERIFY(ret.isObject()); + QVERIFY(ret.instanceOf(fun)); + QCOMPARE(ret.property("foo").toInt32(), 123); +} + +void tst_QJSValue::construct_newObjectJS() +{ + QJSEngine eng; + // returning a different object overrides the default-constructed one + QJSValue fun = eng.evaluate("(function () { return { bar: 456 }; })"); + QVERIFY(fun.isFunction()); + QJSValue ret = fun.construct(); + QVERIFY(ret.isObject()); + QVERIFY(!ret.instanceOf(fun)); + QCOMPARE(ret.property("bar").toInt32(), 456); +} + +#if 0 // FIXME: no c-style callbacks +void tst_QJSValue::construct_undefined() +{ + QScriptEngine eng; + QJSValue fun = eng.newFunction(ctorReturningUndefined); + QJSValue ret = fun.construct(); + QVERIFY(ret.isObject()); + QVERIFY(ret.instanceOf(fun)); + QCOMPARE(ret.property("foo").toInt32(), 123); +} + +void tst_QJSValue::construct_newObjectCpp() +{ + QScriptEngine eng; + QJSValue fun = eng.newFunction(ctorReturningNewObject); + QJSValue ret = fun.construct(); + QVERIFY(ret.isObject()); + QVERIFY(!ret.instanceOf(fun)); + QCOMPARE(ret.property("bar").toInt32(), 456); +} +#endif + +void tst_QJSValue::construct_arg() +{ + QJSEngine eng; + QJSValue Number = eng.evaluate("Number"); + QCOMPARE(Number.isFunction(), true); + QJSValueList args; + args << QJSValue(&eng, 123); + QJSValue ret = Number.construct(args); + QCOMPARE(ret.isObject(), true); + QCOMPARE(ret.toNumber(), args.at(0).toNumber()); +} + +void tst_QJSValue::construct_proto() +{ + QJSEngine eng; + // test that internal prototype is set correctly + QJSValue fun = eng.evaluate("(function() { return this.__proto__; })"); + QCOMPARE(fun.isFunction(), true); + QCOMPARE(fun.property("prototype").isObject(), true); + QJSValue ret = fun.construct(); + QCOMPARE(fun.property("prototype").strictlyEquals(ret), true); +} + +void tst_QJSValue::construct_returnInt() +{ + QJSEngine eng; + // test that we return the new object even if a non-object value is returned from the function + QJSValue fun = eng.evaluate("(function() { return 123; })"); + QCOMPARE(fun.isFunction(), true); + QJSValue ret = fun.construct(); + QCOMPARE(ret.isObject(), true); +} + +void tst_QJSValue::construct_throw() +{ + QJSEngine eng; + QJSValue fun = eng.evaluate("(function() { throw new Error('foo'); })"); + QCOMPARE(fun.isFunction(), true); + QJSValue ret = fun.construct(); + QCOMPARE(ret.isError(), true); + QCOMPARE(eng.hasUncaughtException(), true); + QVERIFY(ret.strictlyEquals(eng.uncaughtException())); +} + +#if 0 // FIXME: The feature of interpreting an array as argument list has been removed from the API +void tst_QJSValue::construct() +{ + QScriptEngine eng; + QJSValue fun = eng.evaluate("(function() { return arguments; })"); + QVERIFY(fun.isFunction()); + QJSValue array = eng.newArray(3); + array.setProperty(0, QJSValue(&eng, 123.0)); + array.setProperty(1, QJSValue(&eng, 456.0)); + array.setProperty(2, QJSValue(&eng, 789.0)); + // construct with single array object as arguments + QJSValue ret = fun.construct(array); + QVERIFY(!eng.hasUncaughtException()); + QVERIFY(ret.isValid()); + QVERIFY(ret.isObject()); + QCOMPARE(ret.property(0).strictlyEquals(array.property(0)), true); + QCOMPARE(ret.property(1).strictlyEquals(array.property(1)), true); + QCOMPARE(ret.property(2).strictlyEquals(array.property(2)), true); + // construct with arguments object as arguments + QJSValue ret2 = fun.construct(ret); + QCOMPARE(ret2.property(0).strictlyEquals(ret.property(0)), true); + QCOMPARE(ret2.property(1).strictlyEquals(ret.property(1)), true); + QCOMPARE(ret2.property(2).strictlyEquals(ret.property(2)), true); + // construct with null as arguments + QJSValue ret3 = fun.construct(eng.nullValue()); + QCOMPARE(ret3.isError(), false); + QCOMPARE(ret3.property("length").isNumber(), true); + QCOMPARE(ret3.property("length").toNumber(), 0.0); + // construct with undefined as arguments + QJSValue ret4 = fun.construct(eng.undefinedValue()); + QCOMPARE(ret4.isError(), false); + QCOMPARE(ret4.property("length").isNumber(), true); + QCOMPARE(ret4.property("length").toNumber(), 0.0); + // construct with something else as arguments + QJSValue ret5 = fun.construct(QJSValue(&eng, 123.0)); + QCOMPARE(ret5.isError(), true); + // construct with a non-array object as arguments + QJSValue ret6 = fun.construct(eng.globalObject()); + QVERIFY(ret6.isError()); + QCOMPARE(ret6.toString(), QString::fromLatin1("TypeError: Arguments must be an array")); +} +#endif + +void tst_QJSValue::construct_twoEngines() +{ + QJSEngine engine; + QJSEngine otherEngine; + QJSValue ctor = engine.evaluate("(function (a, b) { this.foo = 123; })"); + QJSValue arg(&otherEngine, 124567); + QTest::ignoreMessage(QtWarningMsg, "QJSValue::construct() failed: cannot construct function with argument created in a different engine"); + QVERIFY(!ctor.construct(QJSValueList() << arg).isValid()); + QTest::ignoreMessage(QtWarningMsg, "QJSValue::construct() failed: cannot construct function with argument created in a different engine"); + QVERIFY(!ctor.construct(QJSValueList() << arg << otherEngine.newObject()).isValid()); +} + +void tst_QJSValue::construct_constructorThrowsPrimitive() +{ + QJSEngine eng; + QJSValue fun = eng.evaluate("(function() { throw 123; })"); + QVERIFY(fun.isFunction()); + // construct(QJSValueList) + { + QJSValue ret = fun.construct(); + QVERIFY(ret.isNumber()); + QCOMPARE(ret.toNumber(), 123.0); + QVERIFY(eng.hasUncaughtException()); + QVERIFY(ret.strictlyEquals(eng.uncaughtException())); + eng.clearExceptions(); + } +#if 0 // FIXME: The feature of interpreting an array as argument list has been removed from the API + // construct(QJSValue) + { + QJSValue ret = fun.construct(eng.newArray()); + QVERIFY(ret.isNumber()); + QCOMPARE(ret.toNumber(), 123.0); + QVERIFY(eng.hasUncaughtException()); + QVERIFY(ret.strictlyEquals(eng.uncaughtException())); + eng.clearExceptions(); + } +#endif +} + +#if 0 // FIXME: No QJSValue::lessThan +void tst_QJSValue::lessThan() +{ + QScriptEngine eng; + + QVERIFY(!QJSValue().lessThan(QJSValue())); + + QJSValue num = QJSValue(&eng, 123); + QCOMPARE(num.lessThan(QJSValue(&eng, 124)), true); + QCOMPARE(num.lessThan(QJSValue(&eng, 122)), false); + QCOMPARE(num.lessThan(QJSValue(&eng, 123)), false); + QCOMPARE(num.lessThan(QJSValue(&eng, "124")), true); + QCOMPARE(num.lessThan(QJSValue(&eng, "122")), false); + QCOMPARE(num.lessThan(QJSValue(&eng, "123")), false); + QCOMPARE(num.lessThan(QJSValue(&eng, qSNaN())), false); + QCOMPARE(num.lessThan(QJSValue(&eng, +qInf())), true); + QCOMPARE(num.lessThan(QJSValue(&eng, -qInf())), false); + QCOMPARE(num.lessThan(num), false); + QCOMPARE(num.lessThan(QJSValue(&eng, 124).toObject()), true); + QCOMPARE(num.lessThan(QJSValue(&eng, 122).toObject()), false); + QCOMPARE(num.lessThan(QJSValue(&eng, 123).toObject()), false); + QCOMPARE(num.lessThan(QJSValue(&eng, "124").toObject()), true); + QCOMPARE(num.lessThan(QJSValue(&eng, "122").toObject()), false); + QCOMPARE(num.lessThan(QJSValue(&eng, "123").toObject()), false); + QCOMPARE(num.lessThan(QJSValue(&eng, qSNaN()).toObject()), false); + QCOMPARE(num.lessThan(QJSValue(&eng, +qInf()).toObject()), true); + QCOMPARE(num.lessThan(QJSValue(&eng, -qInf()).toObject()), false); + QCOMPARE(num.lessThan(num.toObject()), false); + QCOMPARE(num.lessThan(QJSValue()), false); + + QJSValue str = QJSValue(&eng, "123"); + QCOMPARE(str.lessThan(QJSValue(&eng, "124")), true); + QCOMPARE(str.lessThan(QJSValue(&eng, "122")), false); + QCOMPARE(str.lessThan(QJSValue(&eng, "123")), false); + QCOMPARE(str.lessThan(QJSValue(&eng, 124)), true); + QCOMPARE(str.lessThan(QJSValue(&eng, 122)), false); + QCOMPARE(str.lessThan(QJSValue(&eng, 123)), false); + QCOMPARE(str.lessThan(str), false); + QCOMPARE(str.lessThan(QJSValue(&eng, "124").toObject()), true); + QCOMPARE(str.lessThan(QJSValue(&eng, "122").toObject()), false); + QCOMPARE(str.lessThan(QJSValue(&eng, "123").toObject()), false); + QCOMPARE(str.lessThan(QJSValue(&eng, 124).toObject()), true); + QCOMPARE(str.lessThan(QJSValue(&eng, 122).toObject()), false); + QCOMPARE(str.lessThan(QJSValue(&eng, 123).toObject()), false); + QCOMPARE(str.lessThan(str.toObject()), false); + QCOMPARE(str.lessThan(QJSValue()), false); + + // V2 constructors + QJSValue num2 = QJSValue(123); + QCOMPARE(num2.lessThan(QJSValue(124)), true); + QCOMPARE(num2.lessThan(QJSValue(122)), false); + QCOMPARE(num2.lessThan(QJSValue(123)), false); + QCOMPARE(num2.lessThan(QJSValue("124")), true); + QCOMPARE(num2.lessThan(QJSValue("122")), false); + QCOMPARE(num2.lessThan(QJSValue("123")), false); + QCOMPARE(num2.lessThan(QJSValue(qSNaN())), false); + QCOMPARE(num2.lessThan(QJSValue(+qInf())), true); + QCOMPARE(num2.lessThan(QJSValue(-qInf())), false); + QCOMPARE(num2.lessThan(num), false); + QCOMPARE(num2.lessThan(QJSValue()), false); + + QJSValue str2 = QJSValue("123"); + QCOMPARE(str2.lessThan(QJSValue("124")), true); + QCOMPARE(str2.lessThan(QJSValue("122")), false); + QCOMPARE(str2.lessThan(QJSValue("123")), false); + QCOMPARE(str2.lessThan(QJSValue(124)), true); + QCOMPARE(str2.lessThan(QJSValue(122)), false); + QCOMPARE(str2.lessThan(QJSValue(123)), false); + QCOMPARE(str2.lessThan(str), false); + QCOMPARE(str2.lessThan(QJSValue()), false); + + QJSValue obj1 = eng.newObject(); + QJSValue obj2 = eng.newObject(); + QCOMPARE(obj1.lessThan(obj2), false); + QCOMPARE(obj2.lessThan(obj1), false); + QCOMPARE(obj1.lessThan(obj1), false); + QCOMPARE(obj2.lessThan(obj2), false); + + QJSValue date1 = eng.newDate(QDateTime(QDate(2000, 1, 1))); + QJSValue date2 = eng.newDate(QDateTime(QDate(1999, 1, 1))); + QCOMPARE(date1.lessThan(date2), false); + QCOMPARE(date2.lessThan(date1), true); + QCOMPARE(date1.lessThan(date1), false); + QCOMPARE(date2.lessThan(date2), false); + QCOMPARE(date1.lessThan(QJSValue()), false); + + QCOMPARE(QJSValue().lessThan(date2), false); + + QScriptEngine otherEngine; + QTest::ignoreMessage(QtWarningMsg, "QJSValue::lessThan: " + "cannot compare to a value created in " + "a different engine"); + QCOMPARE(date1.lessThan(QJSValue(&otherEngine, 123)), false); +} +#endif + +void tst_QJSValue::equals() +{ + QJSEngine eng; + + QVERIFY(QJSValue().equals(QJSValue())); + + QJSValue num = QJSValue(&eng, 123); + QCOMPARE(num.equals(QJSValue(&eng, 123)), true); + QCOMPARE(num.equals(QJSValue(&eng, 321)), false); + QCOMPARE(num.equals(QJSValue(&eng, QLatin1String("123"))), true); + QCOMPARE(num.equals(QJSValue(&eng, QLatin1String("321"))), false); + QCOMPARE(num.equals(QJSValue(&eng, 123).toObject()), true); + QCOMPARE(num.equals(QJSValue(&eng, 321).toObject()), false); + QCOMPARE(num.equals(QJSValue(&eng, QLatin1String("123")).toObject()), true); + QCOMPARE(num.equals(QJSValue(&eng, QLatin1String("321")).toObject()), false); + QVERIFY(num.toObject().equals(num)); + QCOMPARE(num.equals(QJSValue()), false); + + QJSValue str = QJSValue(&eng, QLatin1String("123")); + QCOMPARE(str.equals(QJSValue(&eng, QLatin1String("123"))), true); + QCOMPARE(str.equals(QJSValue(&eng, QLatin1String("321"))), false); + QCOMPARE(str.equals(QJSValue(&eng, 123)), true); + QCOMPARE(str.equals(QJSValue(&eng, 321)), false); + QCOMPARE(str.equals(QJSValue(&eng, QLatin1String("123")).toObject()), true); + QCOMPARE(str.equals(QJSValue(&eng, QLatin1String("321")).toObject()), false); + QCOMPARE(str.equals(QJSValue(&eng, 123).toObject()), true); + QCOMPARE(str.equals(QJSValue(&eng, 321).toObject()), false); + QVERIFY(str.toObject().equals(str)); + QCOMPARE(str.equals(QJSValue()), false); + + QJSValue num2 = QJSValue(123); + QCOMPARE(num2.equals(QJSValue(123)), true); + QCOMPARE(num2.equals(QJSValue(321)), false); + QCOMPARE(num2.equals(QJSValue("123")), true); + QCOMPARE(num2.equals(QJSValue("321")), false); + QCOMPARE(num2.equals(QJSValue()), false); + + QJSValue str2 = QJSValue("123"); + QCOMPARE(str2.equals(QJSValue("123")), true); + QCOMPARE(str2.equals(QJSValue("321")), false); + QCOMPARE(str2.equals(QJSValue(123)), true); + QCOMPARE(str2.equals(QJSValue(321)), false); + QCOMPARE(str2.equals(QJSValue()), false); + + QJSValue date1 = eng.newDate(QDateTime(QDate(2000, 1, 1))); + QJSValue date2 = eng.newDate(QDateTime(QDate(1999, 1, 1))); + QCOMPARE(date1.equals(date2), false); + QCOMPARE(date1.equals(date1), true); + QCOMPARE(date2.equals(date2), true); + + QJSValue undefined = eng.undefinedValue(); + QJSValue null = eng.nullValue(); + QCOMPARE(undefined.equals(undefined), true); + QCOMPARE(null.equals(null), true); + QCOMPARE(undefined.equals(null), true); + QCOMPARE(null.equals(undefined), true); + QCOMPARE(undefined.equals(QJSValue()), false); + QCOMPARE(null.equals(QJSValue()), false); + QVERIFY(!null.equals(num)); + QVERIFY(!undefined.equals(num)); + + QJSValue sant = QJSValue(&eng, true); + QVERIFY(sant.equals(QJSValue(&eng, 1))); + QVERIFY(sant.equals(QJSValue(&eng, QLatin1String("1")))); + QVERIFY(sant.equals(sant)); + QVERIFY(sant.equals(QJSValue(&eng, 1).toObject())); + QVERIFY(sant.equals(QJSValue(&eng, QLatin1String("1")).toObject())); + QVERIFY(sant.equals(sant.toObject())); + QVERIFY(sant.toObject().equals(sant)); + QVERIFY(!sant.equals(QJSValue(&eng, 0))); + QVERIFY(!sant.equals(undefined)); + QVERIFY(!sant.equals(null)); + + QJSValue falskt = QJSValue(&eng, false); + QVERIFY(falskt.equals(QJSValue(&eng, 0))); + QVERIFY(falskt.equals(QJSValue(&eng, QLatin1String("0")))); + QVERIFY(falskt.equals(falskt)); + QVERIFY(falskt.equals(QJSValue(&eng, 0).toObject())); + QVERIFY(falskt.equals(QJSValue(&eng, QLatin1String("0")).toObject())); + QVERIFY(falskt.equals(falskt.toObject())); + QVERIFY(falskt.toObject().equals(falskt)); + QVERIFY(!falskt.equals(sant)); + QVERIFY(!falskt.equals(undefined)); + QVERIFY(!falskt.equals(null)); + + QJSValue obj1 = eng.newObject(); + QJSValue obj2 = eng.newObject(); + QCOMPARE(obj1.equals(obj2), false); + QCOMPARE(obj2.equals(obj1), false); + QCOMPARE(obj1.equals(obj1), true); + QCOMPARE(obj2.equals(obj2), true); + + QJSValue qobj1 = eng.newQObject(this); + QJSValue qobj2 = eng.newQObject(this); + QJSValue qobj3 = eng.newQObject(0); + + // FIXME: No ScriptOwnership: QJSValue qobj4 = eng.newQObject(new QObject(), QScriptEngine::ScriptOwnership); + QJSValue qobj4 = eng.newQObject(new QObject()); + + QVERIFY(qobj1.equals(qobj2)); // compares the QObject pointers + QVERIFY(!qobj2.equals(qobj4)); // compares the QObject pointers + QVERIFY(!qobj2.equals(obj2)); // compares the QObject pointers + + QJSValue compareFun = eng.evaluate("(function(a, b) { return a == b; })"); + QVERIFY(compareFun.isFunction()); + { + QJSValue ret = compareFun.call(QJSValue(), QJSValueList() << qobj1 << qobj2); + QVERIFY(ret.isBool()); + ret = compareFun.call(QJSValue(), QJSValueList() << qobj1 << qobj3); + QVERIFY(ret.isBool()); + QVERIFY(!ret.toBool()); + ret = compareFun.call(QJSValue(), QJSValueList() << qobj1 << qobj4); + QVERIFY(ret.isBool()); + QVERIFY(!ret.toBool()); + ret = compareFun.call(QJSValue(), QJSValueList() << qobj1 << obj1); + QVERIFY(ret.isBool()); + QVERIFY(!ret.toBool()); + } + + { + QJSValue var1 = eng.newVariant(QVariant(false)); + QJSValue var2 = eng.newVariant(QVariant(false)); + QEXPECT_FAIL("", "FIXME: QVariant comparison does not work with v8", Continue); + QVERIFY(var1.equals(var2)); + { + QJSValue ret = compareFun.call(QJSValue(), QJSValueList() << var1 << var2); + QVERIFY(ret.isBool()); + } + } + { + QJSValue var1 = eng.newVariant(QVariant(false)); + QJSValue var2 = eng.newVariant(QVariant(0)); + // QVariant::operator==() performs type conversion + QEXPECT_FAIL("", "FIXME: QVariant comparison does not work with v8", Continue); + QVERIFY(var1.equals(var2)); + } + { + QJSValue var1 = eng.newVariant(QVariant(QStringList() << "a")); + QJSValue var2 = eng.newVariant(QVariant(QStringList() << "a")); + QEXPECT_FAIL("", "FIXME: QVariant comparison does not work with v8", Continue); + QVERIFY(var1.equals(var2)); + } + { + QJSValue var1 = eng.newVariant(QVariant(QStringList() << "a")); + QJSValue var2 = eng.newVariant(QVariant(QStringList() << "b")); + QVERIFY(!var1.equals(var2)); + } + { + QJSValue var1 = eng.newVariant(QVariant(QPoint(1, 2))); + QJSValue var2 = eng.newVariant(QVariant(QPoint(1, 2))); + QEXPECT_FAIL("", "FIXME: QVariant comparison does not work with v8", Continue); + QVERIFY(var1.equals(var2)); + } + { + QJSValue var1 = eng.newVariant(QVariant(QPoint(1, 2))); + QJSValue var2 = eng.newVariant(QVariant(QPoint(3, 4))); + QVERIFY(!var1.equals(var2)); + } + { + QJSValue var1 = eng.newVariant(QVariant(int(1))); + QJSValue var2 = eng.newVariant(QVariant(double(1))); + // QVariant::operator==() performs type conversion + QEXPECT_FAIL("", "FIXME: QVariant comparison does not work with v8", Continue); + QVERIFY(var1.equals(var2)); + } + { + QJSValue var1 = eng.newVariant(QVariant(QString::fromLatin1("123"))); + QJSValue var2 = eng.newVariant(QVariant(double(123))); + QJSValue var3(QString::fromLatin1("123")); + QJSValue var4(123); + + QVERIFY(var1.equals(var1)); + QEXPECT_FAIL("", "FIXME: QVariant comparison does not work with v8", Continue); + QVERIFY(var1.equals(var2)); + QVERIFY(var1.equals(var3)); + QVERIFY(var1.equals(var4)); + + QEXPECT_FAIL("", "FIXME: QVariant comparison does not work with v8", Continue); + QVERIFY(var2.equals(var1)); + QVERIFY(var2.equals(var2)); + QVERIFY(var2.equals(var3)); + QVERIFY(var2.equals(var4)); + + QVERIFY(var3.equals(var1)); + QVERIFY(var3.equals(var2)); + QVERIFY(var3.equals(var3)); + QVERIFY(var3.equals(var4)); + + QVERIFY(var4.equals(var1)); + QVERIFY(var4.equals(var2)); + QVERIFY(var4.equals(var3)); + QVERIFY(var4.equals(var4)); + } + + QJSEngine otherEngine; + QTest::ignoreMessage(QtWarningMsg, "QJSValue::equals: " + "cannot compare to a value created in " + "a different engine"); + QCOMPARE(date1.equals(QJSValue(&otherEngine, 123)), false); +} + +void tst_QJSValue::strictlyEquals() +{ + QJSEngine eng; + + QVERIFY(QJSValue().strictlyEquals(QJSValue())); + + QJSValue num = QJSValue(&eng, 123); + QCOMPARE(num.strictlyEquals(QJSValue(&eng, 123)), true); + QCOMPARE(num.strictlyEquals(QJSValue(&eng, 321)), false); + QCOMPARE(num.strictlyEquals(QJSValue(&eng, QLatin1String("123"))), false); + QCOMPARE(num.strictlyEquals(QJSValue(&eng, QLatin1String("321"))), false); + QCOMPARE(num.strictlyEquals(QJSValue(&eng, 123).toObject()), false); + QCOMPARE(num.strictlyEquals(QJSValue(&eng, 321).toObject()), false); + QCOMPARE(num.strictlyEquals(QJSValue(&eng, QLatin1String("123")).toObject()), false); + QCOMPARE(num.strictlyEquals(QJSValue(&eng, QLatin1String("321")).toObject()), false); + QVERIFY(!num.toObject().strictlyEquals(num)); + QVERIFY(!num.strictlyEquals(QJSValue())); + QVERIFY(!QJSValue().strictlyEquals(num)); + + QJSValue str = QJSValue(&eng, QLatin1String("123")); + QCOMPARE(str.strictlyEquals(QJSValue(&eng, QLatin1String("123"))), true); + QCOMPARE(str.strictlyEquals(QJSValue(&eng, QLatin1String("321"))), false); + QCOMPARE(str.strictlyEquals(QJSValue(&eng, 123)), false); + QCOMPARE(str.strictlyEquals(QJSValue(&eng, 321)), false); + QCOMPARE(str.strictlyEquals(QJSValue(&eng, QLatin1String("123")).toObject()), false); + QCOMPARE(str.strictlyEquals(QJSValue(&eng, QLatin1String("321")).toObject()), false); + QCOMPARE(str.strictlyEquals(QJSValue(&eng, 123).toObject()), false); + QCOMPARE(str.strictlyEquals(QJSValue(&eng, 321).toObject()), false); + QVERIFY(!str.toObject().strictlyEquals(str)); + QVERIFY(!str.strictlyEquals(QJSValue())); + + QJSValue num2 = QJSValue(123); + QCOMPARE(num2.strictlyEquals(QJSValue(123)), true); + QCOMPARE(num2.strictlyEquals(QJSValue(321)), false); + QCOMPARE(num2.strictlyEquals(QJSValue("123")), false); + QCOMPARE(num2.strictlyEquals(QJSValue("321")), false); + QVERIFY(!num2.strictlyEquals(QJSValue())); + + QJSValue str2 = QJSValue("123"); + QCOMPARE(str2.strictlyEquals(QJSValue("123")), true); + QCOMPARE(str2.strictlyEquals(QJSValue("321")), false); + QCOMPARE(str2.strictlyEquals(QJSValue(123)), false); + QCOMPARE(str2.strictlyEquals(QJSValue(321)), false); + QVERIFY(!str2.strictlyEquals(QJSValue())); + + QJSValue date1 = eng.newDate(QDateTime(QDate(2000, 1, 1))); + QJSValue date2 = eng.newDate(QDateTime(QDate(1999, 1, 1))); + QCOMPARE(date1.strictlyEquals(date2), false); + QCOMPARE(date1.strictlyEquals(date1), true); + QCOMPARE(date2.strictlyEquals(date2), true); + QVERIFY(!date1.strictlyEquals(QJSValue())); + + QJSValue undefined = eng.undefinedValue(); + QJSValue null = eng.nullValue(); + QCOMPARE(undefined.strictlyEquals(undefined), true); + QCOMPARE(null.strictlyEquals(null), true); + QCOMPARE(undefined.strictlyEquals(null), false); + QCOMPARE(null.strictlyEquals(undefined), false); + QVERIFY(!null.strictlyEquals(QJSValue())); + + QJSValue sant = QJSValue(&eng, true); + QVERIFY(!sant.strictlyEquals(QJSValue(&eng, 1))); + QVERIFY(!sant.strictlyEquals(QJSValue(&eng, QLatin1String("1")))); + QVERIFY(sant.strictlyEquals(sant)); + QVERIFY(!sant.strictlyEquals(QJSValue(&eng, 1).toObject())); + QVERIFY(!sant.strictlyEquals(QJSValue(&eng, QLatin1String("1")).toObject())); + QVERIFY(!sant.strictlyEquals(sant.toObject())); + QVERIFY(!sant.toObject().strictlyEquals(sant)); + QVERIFY(!sant.strictlyEquals(QJSValue(&eng, 0))); + QVERIFY(!sant.strictlyEquals(undefined)); + QVERIFY(!sant.strictlyEquals(null)); + QVERIFY(!sant.strictlyEquals(QJSValue())); + + QJSValue falskt = QJSValue(&eng, false); + QVERIFY(!falskt.strictlyEquals(QJSValue(&eng, 0))); + QVERIFY(!falskt.strictlyEquals(QJSValue(&eng, QLatin1String("0")))); + QVERIFY(falskt.strictlyEquals(falskt)); + QVERIFY(!falskt.strictlyEquals(QJSValue(&eng, 0).toObject())); + QVERIFY(!falskt.strictlyEquals(QJSValue(&eng, QLatin1String("0")).toObject())); + QVERIFY(!falskt.strictlyEquals(falskt.toObject())); + QVERIFY(!falskt.toObject().strictlyEquals(falskt)); + QVERIFY(!falskt.strictlyEquals(sant)); + QVERIFY(!falskt.strictlyEquals(undefined)); + QVERIFY(!falskt.strictlyEquals(null)); + QVERIFY(!falskt.strictlyEquals(QJSValue())); + + QVERIFY(!QJSValue(false).strictlyEquals(123)); + QVERIFY(!QJSValue(QJSValue::UndefinedValue).strictlyEquals(123)); + QVERIFY(!QJSValue(QJSValue::NullValue).strictlyEquals(123)); + QVERIFY(!QJSValue(false).strictlyEquals("ciao")); + QVERIFY(!QJSValue(QJSValue::UndefinedValue).strictlyEquals("ciao")); + QVERIFY(!QJSValue(QJSValue::NullValue).strictlyEquals("ciao")); + QVERIFY(QJSValue(&eng, QLatin1String("ciao")).strictlyEquals("ciao")); + QVERIFY(QJSValue("ciao").strictlyEquals(QJSValue(&eng, QLatin1String("ciao")))); + QVERIFY(!QJSValue("ciao").strictlyEquals(123)); + QVERIFY(!QJSValue("ciao").strictlyEquals(QJSValue(&eng, 123))); + QVERIFY(!QJSValue(123).strictlyEquals("ciao")); + QVERIFY(!QJSValue(123).strictlyEquals(QJSValue(&eng, QLatin1String("ciao")))); + QVERIFY(!QJSValue(&eng, 123).strictlyEquals("ciao")); + + QJSValue obj1 = eng.newObject(); + QJSValue obj2 = eng.newObject(); + QCOMPARE(obj1.strictlyEquals(obj2), false); + QCOMPARE(obj2.strictlyEquals(obj1), false); + QCOMPARE(obj1.strictlyEquals(obj1), true); + QCOMPARE(obj2.strictlyEquals(obj2), true); + QVERIFY(!obj1.strictlyEquals(QJSValue())); + + QJSValue qobj1 = eng.newQObject(this); + QJSValue qobj2 = eng.newQObject(this); + QVERIFY(qobj1.strictlyEquals(qobj2)); + + { + QJSValue var1 = eng.newVariant(QVariant(false)); + QJSValue var2 = eng.newVariant(QVariant(false)); + QVERIFY(!var1.strictlyEquals(var2)); + QVERIFY(!var1.strictlyEquals(QJSValue())); + } + { + QJSValue var1 = eng.newVariant(QVariant(false)); + QJSValue var2 = eng.newVariant(QVariant(0)); + QVERIFY(!var1.strictlyEquals(var2)); + } + { + QJSValue var1 = eng.newVariant(QVariant(QStringList() << "a")); + QJSValue var2 = eng.newVariant(QVariant(QStringList() << "a")); + QVERIFY(!var1.strictlyEquals(var2)); + } + { + QJSValue var1 = eng.newVariant(QVariant(QStringList() << "a")); + QJSValue var2 = eng.newVariant(QVariant(QStringList() << "b")); + QVERIFY(!var1.strictlyEquals(var2)); + } + { + QJSValue var1 = eng.newVariant(QVariant(QPoint(1, 2))); + QJSValue var2 = eng.newVariant(QVariant(QPoint(1, 2))); + QVERIFY(!var1.strictlyEquals(var2)); + } + { + QJSValue var1 = eng.newVariant(QVariant(QPoint(1, 2))); + QJSValue var2 = eng.newVariant(QVariant(QPoint(3, 4))); + QVERIFY(!var1.strictlyEquals(var2)); + } + + QJSEngine otherEngine; + QTest::ignoreMessage(QtWarningMsg, "QJSValue::strictlyEquals: " + "cannot compare to a value created in " + "a different engine"); + QCOMPARE(date1.strictlyEquals(QJSValue(&otherEngine, 123)), false); +} + +Q_DECLARE_METATYPE(int*) +Q_DECLARE_METATYPE(double*) +Q_DECLARE_METATYPE(QColor*) +Q_DECLARE_METATYPE(QBrush*) + +void tst_QJSValue::castToPointer() +{ + QJSEngine eng; + { + QJSValue v = eng.newVariant(int(123)); + int *ip = qjsvalue_cast(v); + QVERIFY(ip != 0); + QCOMPARE(*ip, 123); + QEXPECT_FAIL("", "Pointer magic for variants is currently not supported by QJSEngine", Abort); + *ip = 456; + QCOMPARE(qjsvalue_cast(v), 456); + + double *dp = qjsvalue_cast(v); + QVERIFY(dp == 0); + + QJSValue v2 = eng.newVariant(qVariantFromValue(ip)); + QCOMPARE(qjsvalue_cast(v2), ip); + } + { + QColor c(123, 210, 231); + QJSValue v = eng.newVariant(c); + QColor *cp = qjsvalue_cast(v); + QVERIFY(cp != 0); + QCOMPARE(*cp, c); + + QBrush *bp = qjsvalue_cast(v); + QVERIFY(bp == 0); + + QJSValue v2 = eng.newVariant(qVariantFromValue(cp)); + QCOMPARE(qjsvalue_cast(v2), cp); + } +} + +void tst_QJSValue::prettyPrinter_data() +{ + QTest::addColumn("function"); + QTest::addColumn("expected"); + QTest::newRow("function() { }") << QString("function() { }") << QString("function () { }"); + QTest::newRow("function foo() { }") << QString("(function foo() { })") << QString("function foo() { }"); + QTest::newRow("function foo(bar) { }") << QString("(function foo(bar) { })") << QString("function foo(bar) { }"); + QTest::newRow("function foo(bar, baz) { }") << QString("(function foo(bar, baz) { })") << QString("function foo(bar, baz) { }"); + QTest::newRow("this") << QString("function() { this; }") << QString("function () { this; }"); + QTest::newRow("identifier") << QString("function(a) { a; }") << QString("function (a) { a; }"); + QTest::newRow("null") << QString("function() { null; }") << QString("function () { null; }"); + QTest::newRow("true") << QString("function() { true; }") << QString("function () { true; }"); + QTest::newRow("false") << QString("function() { false; }") << QString("function () { false; }"); + QTest::newRow("string") << QString("function() { 'test'; }") << QString("function () { \'test\'; }"); + QTest::newRow("string") << QString("function() { \"test\"; }") << QString("function () { \"test\"; }"); + QTest::newRow("number") << QString("function() { 123; }") << QString("function () { 123; }"); + QTest::newRow("number") << QString("function() { 123.456; }") << QString("function () { 123.456; }"); + QTest::newRow("regexp") << QString("function() { /hello/; }") << QString("function () { /hello/; }"); + QTest::newRow("regexp") << QString("function() { /hello/gim; }") << QString("function () { /hello/gim; }"); + QTest::newRow("array") << QString("function() { []; }") << QString("function () { []; }"); + QTest::newRow("array") << QString("function() { [10]; }") << QString("function () { [10]; }"); + QTest::newRow("array") << QString("function() { [10, 20, 30]; }") << QString("function () { [10, 20, 30]; }"); + QTest::newRow("array") << QString("function() { [10, 20, , 40]; }") << QString("function () { [10, 20, , 40]; }"); + QTest::newRow("array") << QString("function() { [,]; }") << QString("function () { [,]; }"); + QTest::newRow("array") << QString("function() { [, 10]; }") << QString("function () { [, 10]; }"); + QTest::newRow("array") << QString("function() { [, 10, ]; }") << QString("function () { [, 10, ]; }"); + QTest::newRow("array") << QString("function() { [, 10, ,]; }") << QString("function () { [, 10, ,]; }"); + QTest::newRow("array") << QString("function() { [[10], [20]]; }") << QString("function () { [[10], [20]]; }"); + QTest::newRow("member") << QString("function() { a.b; }") << QString("function () { a.b; }"); + QTest::newRow("member") << QString("function() { a.b.c; }") << QString("function () { a.b.c; }"); + QTest::newRow("call") << QString("function() { f(); }") << QString("function () { f(); }"); + QTest::newRow("call") << QString("function() { f(a); }") << QString("function () { f(a); }"); + QTest::newRow("call") << QString("function() { f(a, b); }") << QString("function () { f(a, b); }"); + QTest::newRow("new") << QString("function() { new C(); }") << QString("function () { new C(); }"); + QTest::newRow("new") << QString("function() { new C(a); }") << QString("function () { new C(a); }"); + QTest::newRow("new") << QString("function() { new C(a, b); }") << QString("function () { new C(a, b); }"); + QTest::newRow("++") << QString("function() { a++; }") << QString("function () { a++; }"); + QTest::newRow("++") << QString("function() { ++a; }") << QString("function () { ++a; }"); + QTest::newRow("--") << QString("function() { a--; }") << QString("function () { a--; }"); + QTest::newRow("--") << QString("function() { --a; }") << QString("function () { --a; }"); + QTest::newRow("delete") << QString("function() { delete a; }") << QString("function () { delete a; }"); + QTest::newRow("void") << QString("function() { void a; }") << QString("function () { void a; }"); + QTest::newRow("typeof") << QString("function() { typeof a; }") << QString("function () { typeof a; }"); + QTest::newRow("+") << QString("function() { +a; }") << QString("function () { +a; }"); + QTest::newRow("-") << QString("function() { -a; }") << QString("function () { -a; }"); + QTest::newRow("~") << QString("function() { ~a; }") << QString("function () { ~a; }"); + QTest::newRow("!") << QString("function() { !a; }") << QString("function () { !a; }"); + QTest::newRow("+") << QString("function() { a + b; }") << QString("function () { a + b; }"); + QTest::newRow("&&") << QString("function() { a && b; }") << QString("function () { a && b; }"); + QTest::newRow("&=") << QString("function() { a &= b; }") << QString("function () { a &= b; }"); + QTest::newRow("=") << QString("function() { a = b; }") << QString("function () { a = b; }"); + QTest::newRow("&") << QString("function() { a & b; }") << QString("function () { a & b; }"); + QTest::newRow("|") << QString("function() { a | b; }") << QString("function () { a | b; }"); + QTest::newRow("^") << QString("function() { a ^ b; }") << QString("function () { a ^ b; }"); + QTest::newRow("-=") << QString("function() { a -= b; }") << QString("function () { a -= b; }"); + QTest::newRow("/") << QString("function() { a / b; }") << QString("function () { a / b; }"); + QTest::newRow("/=") << QString("function() { a /= b; }") << QString("function () { a /= b; }"); + QTest::newRow("==") << QString("function() { a == b; }") << QString("function () { a == b; }"); + QTest::newRow(">=") << QString("function() { a >= b; }") << QString("function () { a >= b; }"); + QTest::newRow(">") << QString("function() { a > b; }") << QString("function () { a > b; }"); + QTest::newRow("in") << QString("function() { a in b; }") << QString("function () { a in b; }"); + QTest::newRow("+=") << QString("function() { a += b; }") << QString("function () { a += b; }"); + QTest::newRow("instanceof") << QString("function() { a instanceof b; }") << QString("function () { a instanceof b; }"); + QTest::newRow("<=") << QString("function() { a <= b; }") << QString("function () { a <= b; }"); + QTest::newRow("<<") << QString("function() { a << b; }") << QString("function () { a << b; }"); + QTest::newRow("<<=") << QString("function() { a <<= b; }") << QString("function () { a <<= b; }"); + QTest::newRow("<") << QString("function() { a < b; }") << QString("function () { a < b; }"); + QTest::newRow("%") << QString("function() { a % b; }") << QString("function () { a % b; }"); + QTest::newRow("%=") << QString("function() { a %= b; }") << QString("function () { a %= b; }"); + QTest::newRow("*") << QString("function() { a * b; }") << QString("function () { a * b; }"); + QTest::newRow("*=") << QString("function() { a *= b; }") << QString("function () { a *= b; }"); + QTest::newRow("!=") << QString("function() { a != b; }") << QString("function () { a != b; }"); + QTest::newRow("||") << QString("function() { a || b; }") << QString("function () { a || b; }"); + QTest::newRow("|=") << QString("function() { a |= b; }") << QString("function () { a |= b; }"); + QTest::newRow(">>") << QString("function() { a >> b; }") << QString("function () { a >> b; }"); + QTest::newRow(">>=") << QString("function() { a >>= b; }") << QString("function () { a >>= b; }"); + QTest::newRow("===") << QString("function() { a === b; }") << QString("function () { a === b; }"); + QTest::newRow("!==") << QString("function() { a !== b; }") << QString("function () { a !== b; }"); + QTest::newRow("-") << QString("function() { a - b; }") << QString("function () { a - b; }"); + QTest::newRow(">>>") << QString("function() { a >>> b; }") << QString("function () { a >>> b; }"); + QTest::newRow(">>>=") << QString("function() { a >>>= b; }") << QString("function () { a >>>= b; }"); + QTest::newRow("^=") << QString("function() { a ^= b; }") << QString("function () { a ^= b; }"); + QTest::newRow("? :") << QString("function() { a ? b : c; }") << QString("function () { a ? b : c; }"); + QTest::newRow("a; b; c") << QString("function() { a; b; c; }") << QString("function () { a; b; c; }"); + QTest::newRow("var a;") << QString("function() { var a; }") << QString("function () { var a; }"); + QTest::newRow("var a, b;") << QString("function() { var a, b; }") << QString("function () { var a, b; }"); + QTest::newRow("var a = 10;") << QString("function() { var a = 10; }") << QString("function () { var a = 10; }"); + QTest::newRow("var a, b = 20;") << QString("function() { var a, b = 20; }") << QString("function () { var a, b = 20; }"); + QTest::newRow("var a = 10, b = 20;") << QString("function() { var a = 10, b = 20; }") << QString("function () { var a = 10, b = 20; }"); + QTest::newRow("if") << QString("function() { if (a) b; }") << QString("function () { if (a) b; }"); + QTest::newRow("if") << QString("function() { if (a) { b; c; } }") << QString("function () { if (a) { b; c; } }"); + QTest::newRow("if-else") << QString("function() { if (a) b; else c; }") << QString("function () { if (a) b; else c; }"); + QTest::newRow("if-else") << QString("function() { if (a) { b; c; } else { d; e; } }") << QString("function () { if (a) { b; c; } else { d; e; } }"); + QTest::newRow("do-while") << QString("function() { do { a; } while (b); }") << QString("function () { do { a; } while (b); }"); + QTest::newRow("do-while") << QString("function() { do { a; b; c; } while (d); }") << QString("function () { do { a; b; c; } while (d); }"); + QTest::newRow("while") << QString("function() { while (a) { b; } }") << QString("function () { while (a) { b; } }"); + QTest::newRow("while") << QString("function() { while (a) { b; c; } }") << QString("function () { while (a) { b; c; } }"); + QTest::newRow("for") << QString("function() { for (a; b; c) { } }") << QString("function () { for (a; b; c) { } }"); + QTest::newRow("for") << QString("function() { for (; a; b) { } }") << QString("function () { for (; a; b) { } }"); + QTest::newRow("for") << QString("function() { for (; ; a) { } }") << QString("function () { for (; ; a) { } }"); + QTest::newRow("for") << QString("function() { for (; ; ) { } }") << QString("function () { for (; ; ) { } }"); + QTest::newRow("for") << QString("function() { for (var a; b; c) { } }") << QString("function () { for (var a; b; c) { } }"); + QTest::newRow("for") << QString("function() { for (var a, b, c; d; e) { } }") << QString("function () { for (var a, b, c; d; e) { } }"); + QTest::newRow("continue") << QString("function() { for (; ; ) { continue; } }") << QString("function () { for (; ; ) { continue; } }"); + QTest::newRow("break") << QString("function() { for (; ; ) { break; } }") << QString("function () { for (; ; ) { break; } }"); + QTest::newRow("return") << QString("function() { return; }") << QString("function () { return; }"); + QTest::newRow("return") << QString("function() { return 10; }") << QString("function () { return 10; }"); + QTest::newRow("with") << QString("function() { with (a) { b; } }") << QString("function () { with (a) { b; } }"); + QTest::newRow("with") << QString("function() { with (a) { b; c; } }") << QString("function () { with (a) { b; c; } }"); + QTest::newRow("switch") << QString("function() { switch (a) { } }") << QString("function () { switch (a) { } }"); + QTest::newRow("switch") << QString("function() { switch (a) { case 1: ; } }") << QString("function () { switch (a) { case 1: ; } }"); + QTest::newRow("switch") << QString("function() { switch (a) { case 1: b; break; } }") << QString("function () { switch (a) { case 1: b; break; } }"); + QTest::newRow("switch") << QString("function() { switch (a) { case 1: b; break; case 2: break; } }") << QString("function () { switch (a) { case 1: b; break; case 2: break; } }"); + QTest::newRow("switch") << QString("function() { switch (a) { case 1: case 2: ; } }") << QString("function () { switch (a) { case 1: case 2: ; } }"); + QTest::newRow("switch") << QString("function() { switch (a) { case 1: default: ; } }") << QString("function () { switch (a) { case 1: default: ; } }"); + QTest::newRow("switch") << QString("function() { switch (a) { case 1: default: ; case 3: ; } }") << QString("function () { switch (a) { case 1: default: ; case 3: ; } }"); + QTest::newRow("label") << QString("function() { a: b; }") << QString("function () { a: b; }"); + QTest::newRow("throw") << QString("function() { throw a; }") << QString("function () { throw a; }"); + QTest::newRow("try-catch") << QString("function() { try { a; } catch (e) { b; } }") << QString("function () { try { a; } catch (e) { b; } }"); + QTest::newRow("try-finally") << QString("function() { try { a; } finally { b; } }") << QString("function () { try { a; } finally { b; } }"); + QTest::newRow("try-catch-finally") << QString("function() { try { a; } catch (e) { b; } finally { c; } }") << QString("function () { try { a; } catch (e) { b; } finally { c; } }"); + QTest::newRow("a + b + c + d") << QString("function() { a + b + c + d; }") << QString("function () { a + b + c + d; }"); + QTest::newRow("a + b - c") << QString("function() { a + b - c; }") << QString("function () { a + b - c; }"); + QTest::newRow("a + -b") << QString("function() { a + -b; }") << QString("function () { a + -b; }"); + QTest::newRow("a + ~b") << QString("function() { a + ~b; }") << QString("function () { a + ~b; }"); + QTest::newRow("a + !b") << QString("function() { a + !b; }") << QString("function () { a + !b; }"); + QTest::newRow("a + +b") << QString("function() { a + +b; }") << QString("function () { a + +b; }"); + QTest::newRow("(a + b) - c") << QString("function() { (a + b) - c; }") << QString("function () { (a + b) - c; }"); + QTest::newRow("(a - b + c") << QString("function() { a - b + c; }") << QString("function () { a - b + c; }"); + QTest::newRow("(a - (b + c)") << QString("function() { a - (b + c); }") << QString("function () { a - (b + c); }"); + QTest::newRow("a + -(b + c)") << QString("function() { a + -(b + c); }") << QString("function () { a + -(b + c); }"); + QTest::newRow("a + ~(b + c)") << QString("function() { a + ~(b + c); }") << QString("function () { a + ~(b + c); }"); + QTest::newRow("a + !(b + c)") << QString("function() { a + !(b + c); }") << QString("function () { a + !(b + c); }"); + QTest::newRow("a + +(b + c)") << QString("function() { a + +(b + c); }") << QString("function () { a + +(b + c); }"); + QTest::newRow("a + b * c") << QString("function() { a + b * c; }") << QString("function () { a + b * c; }"); + QTest::newRow("(a + b) * c") << QString("function() { (a + b) * c; }") << QString("function () { (a + b) * c; }"); + QTest::newRow("(a + b) * (c + d)") << QString("function() { (a + b) * (c + d); }") << QString("function () { (a + b) * (c + d); }"); + QTest::newRow("a + (b * c)") << QString("function() { a + (b * c); }") << QString("function () { a + (b * c); }"); + QTest::newRow("a + (b / c)") << QString("function() { a + (b / c); }") << QString("function () { a + (b / c); }"); + QTest::newRow("(a / b) * c") << QString("function() { (a / b) * c; }") << QString("function () { (a / b) * c; }"); + QTest::newRow("a / (b * c)") << QString("function() { a / (b * c); }") << QString("function () { a / (b * c); }"); + QTest::newRow("a / (b % c)") << QString("function() { a / (b % c); }") << QString("function () { a / (b % c); }"); + QTest::newRow("a && b || c") << QString("function() { a && b || c; }") << QString("function () { a && b || c; }"); + QTest::newRow("a && (b || c)") << QString("function() { a && (b || c); }") << QString("function () { a && (b || c); }"); + QTest::newRow("a & b | c") << QString("function() { a & b | c; }") << QString("function () { a & b | c; }"); + QTest::newRow("a & (b | c)") << QString("function() { a & (b | c); }") << QString("function () { a & (b | c); }"); + QTest::newRow("a & b | c ^ d") << QString("function() { a & b | c ^ d; }") << QString("function () { a & b | c ^ d; }"); + QTest::newRow("a & (b | c ^ d)") << QString("function() { a & (b | c ^ d); }") << QString("function () { a & (b | c ^ d); }"); + QTest::newRow("(a & b | c) ^ d") << QString("function() { (a & b | c) ^ d; }") << QString("function () { (a & b | c) ^ d; }"); + QTest::newRow("a << b + c") << QString("function() { a << b + c; }") << QString("function () { a << b + c; }"); + QTest::newRow("(a << b) + c") << QString("function() { (a << b) + c; }") << QString("function () { (a << b) + c; }"); + QTest::newRow("a >> b + c") << QString("function() { a >> b + c; }") << QString("function () { a >> b + c; }"); + QTest::newRow("(a >> b) + c") << QString("function() { (a >> b) + c; }") << QString("function () { (a >> b) + c; }"); + QTest::newRow("a >>> b + c") << QString("function() { a >>> b + c; }") << QString("function () { a >>> b + c; }"); + QTest::newRow("(a >>> b) + c") << QString("function() { (a >>> b) + c; }") << QString("function () { (a >>> b) + c; }"); + QTest::newRow("a == b || c != d") << QString("function() { a == b || c != d; }") << QString("function () { a == b || c != d; }"); + QTest::newRow("a == (b || c != d)") << QString("function() { a == (b || c != d); }") << QString("function () { a == (b || c != d); }"); + QTest::newRow("a === b || c !== d") << QString("function() { a === b || c !== d; }") << QString("function () { a === b || c !== d; }"); + QTest::newRow("a === (b || c !== d)") << QString("function() { a === (b || c !== d); }") << QString("function () { a === (b || c !== d); }"); + QTest::newRow("a &= b + c") << QString("function() { a &= b + c; }") << QString("function () { a &= b + c; }"); + QTest::newRow("debugger") << QString("function() { debugger; }") << QString("function () { debugger; }"); +} + +void tst_QJSValue::prettyPrinter() +{ + QFETCH(QString, function); + QFETCH(QString, expected); + QJSEngine eng; + QJSValue val = eng.evaluate("(" + function + ")"); + QVERIFY(val.isFunction()); + QString actual = val.toString(); + int count = qMin(actual.size(), expected.size()); +// qDebug() << actual << expected; + for (int i = 0; i < count; ++i) { +// qDebug() << i << actual.at(i) << expected.at(i); + QCOMPARE(actual.at(i), expected.at(i)); + } + QCOMPARE(actual.size(), expected.size()); +} + +void tst_QJSValue::engineDeleted() +{ + QJSEngine *eng = new QJSEngine; + QJSValue v1(eng, 123); + QVERIFY(v1.isNumber()); + QJSValue v2(eng, QString("ciao")); + QVERIFY(v2.isString()); + QJSValue v3 = eng->newObject(); + QVERIFY(v3.isObject()); + QJSValue v4 = eng->newQObject(this); + QVERIFY(v4.isQObject()); + QJSValue v5 = "Hello"; + QVERIFY(v2.isString()); + + delete eng; + + QVERIFY(!v1.isValid()); + QVERIFY(v1.engine() == 0); + QVERIFY(!v2.isValid()); + QVERIFY(v2.engine() == 0); + QVERIFY(!v3.isValid()); + QVERIFY(v3.engine() == 0); + QVERIFY(!v4.isValid()); + QVERIFY(v4.engine() == 0); + QVERIFY(v5.isValid()); + QVERIFY(v5.engine() == 0); + + QVERIFY(!v3.property("foo").isValid()); +} + +void tst_QJSValue::valueOfWithClosure() +{ + QJSEngine eng; + // valueOf() + { + QJSValue obj = eng.evaluate("o = {}; (function(foo) { o.valueOf = function() { return foo; } })(123); o"); + QVERIFY(obj.isObject()); + QCOMPARE(obj.toInt32(), 123); + } + // toString() + { + QJSValue obj = eng.evaluate("o = {}; (function(foo) { o.toString = function() { return foo; } })('ciao'); o"); + QVERIFY(obj.isObject()); + QCOMPARE(obj.toString(), QString::fromLatin1("ciao")); + } +} + +#if 0 // FIXME: no objectId() +void tst_QJSValue::objectId() +{ + QCOMPARE(QJSValue().objectId(), (qint64)-1); + QCOMPARE(QJSValue(QJSValue::UndefinedValue).objectId(), (qint64)-1); + QCOMPARE(QJSValue(QJSValue::NullValue).objectId(), (qint64)-1); + QCOMPARE(QJSValue(false).objectId(), (qint64)-1); + QCOMPARE(QJSValue(123).objectId(), (qint64)-1); + QCOMPARE(QJSValue(uint(123)).objectId(), (qint64)-1); + QCOMPARE(QJSValue(123.5).objectId(), (qint64)-1); + QCOMPARE(QJSValue("ciao").objectId(), (qint64)-1); + + QScriptEngine eng; + QJSValue o1 = eng.newObject(); + QVERIFY(o1.objectId() != -1); + QJSValue o2 = eng.newObject(); + QVERIFY(o2.objectId() != -1); + QVERIFY(o1.objectId() != o2.objectId()); + + QVERIFY(eng.objectById(o1.objectId()).strictlyEquals(o1)); + QVERIFY(eng.objectById(o2.objectId()).strictlyEquals(o2)); + + qint64 globalObjectId = -1; + { + QJSValue global = eng.globalObject(); + globalObjectId = global.objectId(); + QVERIFY(globalObjectId != -1); + QVERIFY(eng.objectById(globalObjectId).strictlyEquals(global)); + } + QJSValue obj = eng.objectById(globalObjectId); + QVERIFY(obj.isObject()); + QVERIFY(obj.strictlyEquals(eng.globalObject())); +} +#endif + +void tst_QJSValue::nestedObjectToVariant_data() +{ + QTest::addColumn("program"); + QTest::addColumn("expected"); + + // Array literals + QTest::newRow("[[]]") + << QString::fromLatin1("[[]]") + << QVariant(QVariantList() << (QVariant(QVariantList()))); + QTest::newRow("[[123]]") + << QString::fromLatin1("[[123]]") + << QVariant(QVariantList() << (QVariant(QVariantList() << 123))); + QTest::newRow("[[], 123]") + << QString::fromLatin1("[[], 123]") + << QVariant(QVariantList() << QVariant(QVariantList()) << 123); + + // Cyclic arrays + QTest::newRow("var a=[]; a.push(a)") + << QString::fromLatin1("var a=[]; a.push(a); a") + << QVariant(QVariantList() << QVariant(QVariantList())); + QTest::newRow("var a=[]; a.push(123, a)") + << QString::fromLatin1("var a=[]; a.push(123, a); a") + << QVariant(QVariantList() << 123 << QVariant(QVariantList())); + QTest::newRow("var a=[]; var b=[]; a.push(b); b.push(a)") + << QString::fromLatin1("var a=[]; var b=[]; a.push(b); b.push(a); a") + << QVariant(QVariantList() << QVariant(QVariantList() << QVariant(QVariantList()))); + QTest::newRow("var a=[]; var b=[]; a.push(123, b); b.push(456, a)") + << QString::fromLatin1("var a=[]; var b=[]; a.push(123, b); b.push(456, a); a") + << QVariant(QVariantList() << 123 << QVariant(QVariantList() << 456 << QVariant(QVariantList()))); + + // Object literals + { + QVariantMap m; + m["a"] = QVariantMap(); + QTest::newRow("{ a:{} }") + << QString::fromLatin1("({ a:{} })") + << QVariant(m); + } + { + QVariantMap m, m2; + m2["b"] = 10; + m2["c"] = 20; + m["a"] = m2; + QTest::newRow("{ a:{b:10, c:20} }") + << QString::fromLatin1("({ a:{b:10, c:20} })") + << QVariant(m); + } + { + QVariantMap m; + m["a"] = 10; + m["b"] = QVariantList() << 20 << 30; + QTest::newRow("{ a:10, b:[20, 30]}") + << QString::fromLatin1("({ a:10, b:[20,30]})") + << QVariant(m); + } + + // Cyclic objects + { + QVariantMap m; + m["p"] = QVariantMap(); + QTest::newRow("var o={}; o.p=o") + << QString::fromLatin1("var o={}; o.p=o; o") + << QVariant(m); + } + { + QVariantMap m; + m["p"] = 123; + m["q"] = QVariantMap(); + QTest::newRow("var o={}; o.p=123; o.q=o") + << QString::fromLatin1("var o={}; o.p=123; o.q=o; o") + << QVariant(m); + } +} + +void tst_QJSValue::nestedObjectToVariant() +{ + QJSEngine eng; + QFETCH(QString, program); + QFETCH(QVariant, expected); + QJSValue o = eng.evaluate(program); + QVERIFY(!o.isError()); + QVERIFY(o.isObject()); + QCOMPARE(o.toVariant(), expected); +} + +void tst_QJSValue::propertyFlags_data() +{ + QTest::addColumn("program"); + QTest::addColumn("expected"); + + QTest::newRow("nothing") << "" << 0u; +#if 0 // FIXME: No getter/setter API + QTest::newRow("getter") << "o.__defineGetter__('prop', function() { return 'blah' } );\n" << uint(QJSValue::PropertyGetter); + QTest::newRow("setter") << "o.__defineSetter__('prop', function(a) { this.setted_prop2 = a; } );\n" << uint(QJSValue::PropertySetter); + QTest::newRow("getterSetter") << "o.__defineGetter__('prop', function() { return 'ploup' } );\n" + "o.__defineSetter__('prop', function(a) { this.setted_prop3 = a; } );\n" << uint(QJSValue::PropertySetter|QJSValue::PropertyGetter); +#endif + QTest::newRow("nothing2") << "o.prop = 'nothing'" << 0u; +} + +void tst_QJSValue::propertyFlags() +{ + QFETCH(QString, program); + QFETCH(uint, expected); + QJSEngine eng; + eng.evaluate("o = new Object;"); + eng.evaluate(program); + QJSValue o = eng.evaluate("o"); + + QCOMPARE(uint(o.propertyFlags("prop")), expected); +} + + +QTEST_MAIN(tst_QJSValue) diff --git a/tests/auto/declarative/qjsvalue/tst_qjsvalue.h b/tests/auto/declarative/qjsvalue/tst_qjsvalue.h new file mode 100644 index 0000000000..b605066aef --- /dev/null +++ b/tests/auto/declarative/qjsvalue/tst_qjsvalue.h @@ -0,0 +1,205 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite 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 TST_QJSVALUE_H +#define TST_QJSVALUE_H + +#include +#include +#include +#include +#include + +Q_DECLARE_METATYPE(QVariant) +Q_DECLARE_METATYPE(QJSValue) + +class tst_QJSValue : public QObject +{ + Q_OBJECT + +public: + tst_QJSValue(); + virtual ~tst_QJSValue(); + +private slots: + void toObject(); + + void ctor_invalid(); + void ctor_undefinedWithEngine(); + void ctor_undefined(); + void ctor_nullWithEngine(); + void ctor_null(); + void ctor_boolWithEngine(); + void ctor_bool(); + void ctor_intWithEngine(); + void ctor_int(); + void ctor_uintWithEngine(); + void ctor_uint(); + void ctor_floatWithEngine(); + void ctor_float(); + void ctor_stringWithEngine(); + void ctor_string(); + void ctor_copyAndAssignWithEngine(); + void ctor_copyAndAssign(); + void ctor_nullEngine(); + + void toString(); + void toNumber(); + void toBoolean(); + void toBool(); + void toInteger(); + void toInt32(); + void toUInt32(); + void toUInt16(); + void toVariant(); + void toQObject_nonQObject_data(); + void toQObject_nonQObject(); + void toQObject(); + void toDateTime(); + void toRegExp(); + void instanceOf_twoEngines(); + void instanceOf(); + void isArray_data(); + void isArray(); + void isDate(); + void isDate_data(); + void isError_propertiesOfGlobalObject(); + void isError_data(); + void isError(); + void isRegExp_data(); + void isRegExp(); + +#if 0 // FIXME: No QScriptValue::lessThan + void lessThan(); +#endif + void equals(); + void strictlyEquals(); + + void getSetPrototype_cyclicPrototype(); + void getSetPrototype_evalCyclicPrototype(); + void getSetPrototype_eval(); + void getSetPrototype_invalidPrototype(); + void getSetPrototype_twoEngines(); + void getSetPrototype_null(); + void getSetPrototype_notObjectOrNull(); + void getSetPrototype(); + void getSetScope(); + void getSetProperty_HooliganTask162051(); + void getSetProperty_HooliganTask183072(); + void getSetProperty_propertyRemoval(); + void getSetProperty_resolveMode(); + void getSetProperty_twoEngines(); + void getSetProperty_gettersAndSetters(); + void getSetProperty_gettersAndSettersThrowErrorNative(); + void getSetProperty_gettersAndSettersThrowErrorJS(); + void getSetProperty_gettersAndSettersOnNative(); + void getSetProperty_gettersAndSettersOnGlobalObject(); + void getSetProperty_gettersAndSettersChange(); + void getSetProperty_gettersAndSettersStupid(); + void getSetProperty_array(); + void getSetProperty(); + void arrayElementGetterSetter(); + void getSetData_objects_data(); + void getSetData_objects(); + void getSetData_nonObjects_data(); + void getSetData_nonObjects(); + void setData_QTBUG15144(); +#if 0 // FIXME: no QScriptClass + void getSetScriptClass_emptyClass_data(); + void getSetScriptClass_emptyClass(); + void getSetScriptClass_JSObjectFromCpp(); + void getSetScriptClass_JSObjectFromJS(); + void getSetScriptClass_QVariant(); + void getSetScriptClass_QObject(); +#endif + void call_function(); + void call_object(); + void call_newObjects(); + void call_this(); + void call_arguments(); + void call(); + void call_invalidArguments(); + void call_invalidReturn(); + void call_twoEngines(); + void call_array(); + void call_nonFunction_data(); + void call_nonFunction(); + void construct_nonFunction_data(); + void construct_nonFunction(); + void construct_simple(); + void construct_newObjectJS(); +#if 0 // FIXME: no c-style callbacks + void construct_undefined(); + void construct_newObjectCpp(); +#endif + void construct_arg(); + void construct_proto(); + void construct_returnInt(); + void construct_throw(); +#if 0 // FIXME: The feature of interpreting an array as argument list has been removed from the API + void construct(); +#endif + void construct_twoEngines(); + void construct_constructorThrowsPrimitive(); + void castToPointer(); + void prettyPrinter_data(); + void prettyPrinter(); + void engineDeleted(); + void valueOfWithClosure(); +#if 0 // FIXME: no objectId() + void objectId(); +#endif + void nestedObjectToVariant_data(); + void nestedObjectToVariant(); + void propertyFlags_data(); + void propertyFlags(); + +private: + void newEngine() + { + if (engine) + delete engine; + engine = new QJSEngine(); + } + QJSEngine *engine; +}; + +#endif diff --git a/tests/auto/declarative/qjsvalueiterator/qjsvalueiterator.pro b/tests/auto/declarative/qjsvalueiterator/qjsvalueiterator.pro new file mode 100644 index 0000000000..e5137fcdf1 --- /dev/null +++ b/tests/auto/declarative/qjsvalueiterator/qjsvalueiterator.pro @@ -0,0 +1,5 @@ +load(qttest_p4) +QT = core declarative +SOURCES += tst_qjsvalueiterator.cpp + + diff --git a/tests/auto/declarative/qjsvalueiterator/tst_qjsvalueiterator.cpp b/tests/auto/declarative/qjsvalueiterator/tst_qjsvalueiterator.cpp new file mode 100644 index 0000000000..1cd5952c54 --- /dev/null +++ b/tests/auto/declarative/qjsvalueiterator/tst_qjsvalueiterator.cpp @@ -0,0 +1,527 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite 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 + +#include +#include +#include + +//TESTED_CLASS= +//TESTED_FILES= + +Q_DECLARE_METATYPE(QJSValue); + +class tst_QJSValueIterator : public QObject +{ + Q_OBJECT + +public: + tst_QJSValueIterator(); + virtual ~tst_QJSValueIterator(); + +private slots: + void iterateForward_data(); + void iterateForward(); + void iterateArray_data(); + void iterateArray(); + void iterateString(); +#if 0 + void iterateGetterSetter(); +#endif + void assignObjectToIterator(); + void iterateNonObject(); + void iterateOverObjectFromDeletedEngine(); +}; + +tst_QJSValueIterator::tst_QJSValueIterator() +{ +} + +tst_QJSValueIterator::~tst_QJSValueIterator() +{ +} + +void tst_QJSValueIterator::iterateForward_data() +{ + QTest::addColumn("propertyNames"); + QTest::addColumn("propertyValues"); + + QTest::newRow("no properties") + << QStringList() << QStringList(); + QTest::newRow("foo=bar") + << (QStringList() << "foo") + << (QStringList() << "bar"); + QTest::newRow("foo=bar, baz=123") + << (QStringList() << "foo" << "baz") + << (QStringList() << "bar" << "123"); + QTest::newRow("foo=bar, baz=123, rab=oof") + << (QStringList() << "foo" << "baz" << "rab") + << (QStringList() << "bar" << "123" << "oof"); +} + +void tst_QJSValueIterator::iterateForward() +{ + QFETCH(QStringList, propertyNames); + QFETCH(QStringList, propertyValues); + QMap pmap; + QVERIFY(propertyNames.size() == propertyValues.size()); + + QJSEngine engine; + QJSValue object = engine.newObject(); + for (int i = 0; i < propertyNames.size(); ++i) { + QString name = propertyNames.at(i); + QString value = propertyValues.at(i); + pmap.insert(name, value); + object.setProperty(name, QJSValue(&engine, value)); + } + QJSValue otherObject = engine.newObject(); + otherObject.setProperty("foo", QJSValue(&engine, 123456)); + otherObject.setProperty("protoProperty", QJSValue(&engine, 654321)); + object.setPrototype(otherObject); // should not affect iterator + + QStringList lst; + QJSValueIterator it(object); + while (!pmap.isEmpty()) { + QCOMPARE(it.hasNext(), true); + QCOMPARE(it.hasNext(), true); + it.next(); + QString name = it.name(); + QCOMPARE(pmap.contains(name), true); + QCOMPARE(it.name(), name); + QCOMPARE(it.value().strictlyEquals(QJSValue(&engine, pmap.value(name))), true); + pmap.remove(name); + lst.append(name); + } + + QCOMPARE(it.hasNext(), false); + QCOMPARE(it.hasNext(), false); + + it = object; + for (int i = 0; i < lst.count(); ++i) { + QCOMPARE(it.hasNext(), true); + it.next(); + QCOMPARE(it.name(), lst.at(i)); + } +} + +void tst_QJSValueIterator::iterateArray_data() +{ + QTest::addColumn("propertyNames"); + QTest::addColumn("propertyValues"); + + QTest::newRow("no elements") << QStringList() << QStringList(); + + QTest::newRow("0=foo, 1=barr") + << (QStringList() << "0" << "1") + << (QStringList() << "foo" << "bar"); + + + QTest::newRow("0=foo, 3=barr") + << (QStringList() << "0" << "1" << "2" << "3") + << (QStringList() << "foo" << "" << "" << "bar"); +} + +void tst_QJSValueIterator::iterateArray() +{ + QFETCH(QStringList, propertyNames); + QFETCH(QStringList, propertyValues); + + QJSEngine engine; + QJSValue array = engine.newArray(); + + // Fill the array + for (int i = 0; i < propertyNames.size(); ++i) { + array.setProperty(propertyNames.at(i), propertyValues.at(i)); + } + + // Iterate thru array properties. Note that the QJSValueIterator doesn't guarantee + // any order on the iteration! + int length = array.property("length").toInt32(); + QCOMPARE(length, propertyNames.size()); + + bool iteratedThruLength = false; + QHash arrayProperties; + QJSValueIterator it(array); + + // Iterate forward + while (it.hasNext()) { + it.next(); + + const QString name = it.name(); + if (name == QString::fromLatin1("length")) { + QVERIFY(it.value().isNumber()); + QCOMPARE(it.value().toInt32(), length); + QVERIFY2(!iteratedThruLength, "'length' appeared more than once during iteration."); + iteratedThruLength = true; + continue; + } + + // Storing the properties we iterate in a hash to compare with test data. + QVERIFY2(!arrayProperties.contains(name), "property appeared more than once during iteration."); + arrayProperties.insert(name, it.value()); + QVERIFY(it.value().strictlyEquals(array.property(name))); + } + + // Verify properties + QVERIFY(iteratedThruLength); + QCOMPARE(arrayProperties.size(), propertyNames.size()); + for (int i = 0; i < propertyNames.size(); ++i) { + QVERIFY(arrayProperties.contains(propertyNames.at(i))); + QCOMPARE(arrayProperties.value(propertyNames.at(i)).toString(), propertyValues.at(i)); + } + +#if 0 + + // Iterate backwards + arrayProperties.clear(); + iteratedThruLength = false; + it.toBack(); + + while (it.hasPrevious()) { + it.previous(); + + const QString name = it.name(); + if (name == QString::fromLatin1("length")) { + QVERIFY(it.value().isNumber()); + QCOMPARE(it.value().toInt32(), length); + QCOMPARE(it.flags(), QScriptValue::SkipInEnumeration | QScriptValue::Undeletable); + QVERIFY2(!iteratedThruLength, "'length' appeared more than once during iteration."); + iteratedThruLength = true; + continue; + } + + // Storing the properties we iterate in a hash to compare with test data. + QVERIFY2(!arrayProperties.contains(name), "property appeared more than once during iteration."); + arrayProperties.insert(name, it.value()); + QCOMPARE(it.flags(), array.propertyFlags(name)); + QVERIFY(it.value().strictlyEquals(array.property(name))); + } + + // Verify properties + QVERIFY(iteratedThruLength); + QCOMPARE(arrayProperties.size(), propertyNames.size()); + for (int i = 0; i < propertyNames.size(); ++i) { + QVERIFY(arrayProperties.contains(propertyNames.at(i))); + QCOMPARE(arrayProperties.value(propertyNames.at(i)).toString(), propertyValues.at(i)); + } + + // ### Do we still need this test? + // Forward test again but as object + arrayProperties.clear(); + iteratedThruLength = false; + QJSValue arrayObject = engine.toObject(array); + QJSValueIterator it2(arrayObject); + + while (it2.hasNext()) { + it2.next(); + + const QString name = it2.name(); + if (name == QString::fromLatin1("length")) { + QVERIFY(it2.value().isNumber()); + QCOMPARE(it2.value().toInt32(), length); + QCOMPARE(it2.flags(), QScriptValue::SkipInEnumeration | QScriptValue::Undeletable); + QVERIFY2(!iteratedThruLength, "'length' appeared more than once during iteration."); + iteratedThruLength = true; + continue; + } + + // Storing the properties we iterate in a hash to compare with test data. + QVERIFY2(!arrayProperties.contains(name), "property appeared more than once during iteration."); + arrayProperties.insert(name, it2.value()); + QCOMPARE(it2.flags(), arrayObject.propertyFlags(name)); + QVERIFY(it2.value().strictlyEquals(arrayObject.property(name))); + } + + // Verify properties + QVERIFY(iteratedThruLength); + QCOMPARE(arrayProperties.size(), propertyNames.size()); + for (int i = 0; i < propertyNames.size(); ++i) { + QVERIFY(arrayProperties.contains(propertyNames.at(i))); + QCOMPARE(arrayProperties.value(propertyNames.at(i)).toString(), propertyValues.at(i)); + } +#endif +} + +void tst_QJSValueIterator::iterateString() +{ + QJSEngine engine; + QJSValue str = QJSValue(&engine, QString::fromLatin1("ciao")); + QVERIFY(str.isString()); + QJSValue obj = str.toObject(); + QVERIFY(obj.property("length").isNumber()); + int length = obj.property("length").toInt32(); + QCOMPARE(length, 4); + + QJSValueIterator it(obj); + QHash stringProperties; + bool iteratedThruLength = false; + + while (it.hasNext()) { + it.next(); + const QString name = it.name(); + + if (name == QString::fromLatin1("length")) { + QVERIFY(it.value().isNumber()); + QCOMPARE(it.value().toInt32(), length); + QVERIFY2(!iteratedThruLength, "'length' appeared more than once during iteration."); + iteratedThruLength = true; + continue; + } + + QVERIFY2(!stringProperties.contains(name), "property appeared more than once during iteration."); + stringProperties.insert(name, it.value()); + QVERIFY(it.value().strictlyEquals(obj.property(name))); + } + + QVERIFY(iteratedThruLength); + QCOMPARE(stringProperties.size(), length); +#if 0 + // And going backwards + iteratedThruLength = false; + stringProperties.clear(); + it.toBack(); + + while (it.hasPrevious()) { + it.previous(); + const QString name = it.name(); + + if (name == QString::fromLatin1("length")) { + QVERIFY(it.value().isNumber()); + QCOMPARE(it.value().toInt32(), length); + QVERIFY2(!iteratedThruLength, "'length' appeared more than once during iteration."); + iteratedThruLength = true; + continue; + } + + QVERIFY2(!stringProperties.contains(name), "property appeared more than once during iteration."); + stringProperties.insert(name, it.value()); + QVERIFY(it.value().strictlyEquals(obj.property(name))); + } +#endif +} + +#if 0 // FIXME what we should to keep from here? +static QJSValue myGetterSetter(QScriptContext *ctx, QJSEngine *) +{ + if (ctx->argumentCount() == 1) + ctx->thisObject().setProperty("bar", ctx->argument(0)); + return ctx->thisObject().property("bar"); +} + +static QJSValue myGetter(QScriptContext *ctx, QJSEngine *) +{ + return ctx->thisObject().property("bar"); +} + +static QJSValue mySetter(QScriptContext *ctx, QJSEngine *) +{ + ctx->thisObject().setProperty("bar", ctx->argument(0)); + return ctx->argument(0); +} + +void tst_QJSValueIterator::iterateGetterSetter() +{ + // unified getter/setter function + { + QJSEngine eng; + QJSValue obj = eng.newObject(); + obj.setProperty("foo", eng.newFunction(myGetterSetter), + QScriptValue::PropertyGetter | QScriptValue::PropertySetter); + QJSValue val(&eng, 123); + obj.setProperty("foo", val); + QVERIFY(obj.property("bar").strictlyEquals(val)); + QVERIFY(obj.property("foo").strictlyEquals(val)); + + QJSValueIterator it(obj); + QVERIFY(it.hasNext()); + it.next(); + QCOMPARE(it.name(), QString::fromLatin1("foo")); + QCOMPARE(it.flags(), QScriptValue::PropertyFlags(QScriptValue::PropertyGetter | QScriptValue::PropertySetter)); + QVERIFY(it.value().strictlyEquals(val)); + QJSValue val2(&eng, 456); + it.setValue(val2); + QVERIFY(obj.property("bar").strictlyEquals(val2)); + QVERIFY(obj.property("foo").strictlyEquals(val2)); + + QVERIFY(it.hasNext()); + it.next(); + QCOMPARE(it.name(), QString::fromLatin1("bar")); + QVERIFY(!it.hasNext()); + + QVERIFY(it.hasPrevious()); + it.previous(); + QCOMPARE(it.name(), QString::fromLatin1("bar")); + QVERIFY(it.hasPrevious()); + it.previous(); + QCOMPARE(it.name(), QString::fromLatin1("foo")); + QCOMPARE(it.flags(), QScriptValue::PropertyFlags(QScriptValue::PropertyGetter | QScriptValue::PropertySetter)); + QVERIFY(it.value().strictlyEquals(val2)); + it.setValue(val); + QVERIFY(obj.property("bar").strictlyEquals(val)); + QVERIFY(obj.property("foo").strictlyEquals(val)); + } + // separate getter/setter function + for (int x = 0; x < 2; ++x) { + QJSEngine eng; + QJSValue obj = eng.newObject(); + if (x == 0) { + obj.setProperty("foo", eng.newFunction(myGetter), QScriptValue::PropertyGetter); + obj.setProperty("foo", eng.newFunction(mySetter), QScriptValue::PropertySetter); + } else { + obj.setProperty("foo", eng.newFunction(mySetter), QScriptValue::PropertySetter); + obj.setProperty("foo", eng.newFunction(myGetter), QScriptValue::PropertyGetter); + } + QJSValue val(&eng, 123); + obj.setProperty("foo", val); + QVERIFY(obj.property("bar").strictlyEquals(val)); + QVERIFY(obj.property("foo").strictlyEquals(val)); + + QJSValueIterator it(obj); + QVERIFY(it.hasNext()); + it.next(); + QCOMPARE(it.name(), QString::fromLatin1("foo")); + QVERIFY(it.value().strictlyEquals(val)); + QJSValue val2(&eng, 456); + it.setValue(val2); + QVERIFY(obj.property("bar").strictlyEquals(val2)); + QVERIFY(obj.property("foo").strictlyEquals(val2)); + + QVERIFY(it.hasNext()); + it.next(); + QCOMPARE(it.name(), QString::fromLatin1("bar")); + QVERIFY(!it.hasNext()); + + QVERIFY(it.hasPrevious()); + it.previous(); + QCOMPARE(it.name(), QString::fromLatin1("bar")); + QVERIFY(it.hasPrevious()); + it.previous(); + QCOMPARE(it.name(), QString::fromLatin1("foo")); + QVERIFY(it.value().strictlyEquals(val2)); + it.setValue(val); + QVERIFY(obj.property("bar").strictlyEquals(val)); + QVERIFY(obj.property("foo").strictlyEquals(val)); + } +} +#endif + +void tst_QJSValueIterator::assignObjectToIterator() +{ + QJSEngine eng; + QJSValue obj1 = eng.newObject(); + obj1.setProperty("foo", 123); + QJSValue obj2 = eng.newObject(); + obj2.setProperty("bar", 456); + + QJSValueIterator it(obj1); + QVERIFY(it.hasNext()); + it.next(); + it = obj2; + QVERIFY(it.hasNext()); + it.next(); + QCOMPARE(it.name(), QString::fromLatin1("bar")); + + it = obj1; + QVERIFY(it.hasNext()); + it.next(); + QCOMPARE(it.name(), QString::fromLatin1("foo")); + + it = obj2; + QVERIFY(it.hasNext()); + it.next(); + QCOMPARE(it.name(), QString::fromLatin1("bar")); + + it = obj2; + QVERIFY(it.hasNext()); + it.next(); + QCOMPARE(it.name(), QString::fromLatin1("bar")); +} + +void tst_QJSValueIterator::iterateNonObject() +{ + QJSValueIterator it(123); + QVERIFY(!it.hasNext()); + it.next(); + it.name(); + it.value(); + QJSValue num(5); + it = num; + QVERIFY(!it.hasNext()); +} + +void tst_QJSValueIterator::iterateOverObjectFromDeletedEngine() +{ + QJSEngine *engine = new QJSEngine; + QJSValue objet = engine->newObject(); + + // populate object with properties + QHash properties; + properties.insert("foo",1235); + properties.insert("oof",5321); + properties.insert("ofo",3521); + QHash::const_iterator i = properties.constBegin(); + for (; i != properties.constEnd(); ++i) { + objet.setProperty(i.key(), i.value()); + } + + // start iterating + QJSValueIterator it(objet); + it.next(); + QVERIFY(properties.contains(it.name())); + + delete engine; + + QVERIFY(!objet.isValid()); + QVERIFY(it.name().isEmpty()); + QVERIFY(!it.value().isValid()); + + QVERIFY(!it.hasNext()); + it.next(); + + QVERIFY(it.name().isEmpty()); + QVERIFY(!it.value().isValid()); + +} + +QTEST_MAIN(tst_QJSValueIterator) +#include "tst_qjsvalueiterator.moc" diff --git a/tests/auto/declarative/qsganimatedimage/qsganimatedimage.pro b/tests/auto/declarative/qsganimatedimage/qsganimatedimage.pro index c1a3f00aff..5da2ba95b9 100644 --- a/tests/auto/declarative/qsganimatedimage/qsganimatedimage.pro +++ b/tests/auto/declarative/qsganimatedimage/qsganimatedimage.pro @@ -15,4 +15,3 @@ symbian: { CONFIG += parallel_test QT += core-private gui-private declarative-private -QT += script-private diff --git a/tests/auto/declarative/qsgborderimage/qsgborderimage.pro b/tests/auto/declarative/qsgborderimage/qsgborderimage.pro index f75405bf43..db7fe77e1d 100644 --- a/tests/auto/declarative/qsgborderimage/qsgborderimage.pro +++ b/tests/auto/declarative/qsgborderimage/qsgborderimage.pro @@ -16,4 +16,3 @@ symbian: { CONFIG += parallel_test QT += core-private gui-private declarative-private -QT += script-private diff --git a/tests/auto/declarative/qsgflickable/qsgflickable.pro b/tests/auto/declarative/qsgflickable/qsgflickable.pro index 70956f3755..d5e5767e90 100644 --- a/tests/auto/declarative/qsgflickable/qsgflickable.pro +++ b/tests/auto/declarative/qsgflickable/qsgflickable.pro @@ -15,4 +15,3 @@ symbian: { CONFIG += parallel_test QT += core-private gui-private declarative-private -QT += script-private diff --git a/tests/auto/declarative/qsgflipable/qsgflipable.pro b/tests/auto/declarative/qsgflipable/qsgflipable.pro index 6cd52ec85f..c3998c1f9c 100644 --- a/tests/auto/declarative/qsgflipable/qsgflipable.pro +++ b/tests/auto/declarative/qsgflipable/qsgflipable.pro @@ -15,4 +15,3 @@ symbian: { CONFIG += parallel_test QT += core-private gui-private declarative-private -QT += script-private diff --git a/tests/auto/declarative/qsggridview/qsggridview.pro b/tests/auto/declarative/qsggridview/qsggridview.pro index 963df7a9b7..a8b3484f0d 100644 --- a/tests/auto/declarative/qsggridview/qsggridview.pro +++ b/tests/auto/declarative/qsggridview/qsggridview.pro @@ -15,5 +15,4 @@ symbian: { CONFIG += parallel_test QT += core-private gui-private declarative-private -QT += script-private QT += opengl-private diff --git a/tests/auto/declarative/qsgitem2/qsgitem.pro b/tests/auto/declarative/qsgitem2/qsgitem.pro index 9226b4178d..afab61dec3 100644 --- a/tests/auto/declarative/qsgitem2/qsgitem.pro +++ b/tests/auto/declarative/qsgitem2/qsgitem.pro @@ -15,5 +15,4 @@ symbian: { CONFIG += parallel_test QT += core-private gui-private declarative-private -QT += script-private opengl-private QT += opengl-private diff --git a/tests/auto/declarative/qsglistview/qsglistview.pro b/tests/auto/declarative/qsglistview/qsglistview.pro index 8650d75210..9b31bcb7bf 100644 --- a/tests/auto/declarative/qsglistview/qsglistview.pro +++ b/tests/auto/declarative/qsglistview/qsglistview.pro @@ -15,5 +15,4 @@ symbian: { CONFIG += parallel_test QT += core-private gui-private declarative-private -QT += script-private QT += opengl-private diff --git a/tests/auto/declarative/qsgpathview/qsgpathview.pro b/tests/auto/declarative/qsgpathview/qsgpathview.pro index 70279757a5..eb188666bb 100644 --- a/tests/auto/declarative/qsgpathview/qsgpathview.pro +++ b/tests/auto/declarative/qsgpathview/qsgpathview.pro @@ -15,4 +15,3 @@ symbian: { CONFIG += parallel_test QT += core-private gui-private declarative-private -QT += script-private diff --git a/tests/auto/declarative/qsgpositioners/qsgpositioners.pro b/tests/auto/declarative/qsgpositioners/qsgpositioners.pro index 9602c721c2..bf8110ffd4 100644 --- a/tests/auto/declarative/qsgpositioners/qsgpositioners.pro +++ b/tests/auto/declarative/qsgpositioners/qsgpositioners.pro @@ -14,5 +14,4 @@ symbian: { CONFIG += parallel_test QT += core-private gui-private declarative-private -QT += script-private QT += opengl-private diff --git a/tests/auto/declarative/qsgtext/qsgtext.pro b/tests/auto/declarative/qsgtext/qsgtext.pro index 99aac1982f..5254d1ebeb 100644 --- a/tests/auto/declarative/qsgtext/qsgtext.pro +++ b/tests/auto/declarative/qsgtext/qsgtext.pro @@ -20,5 +20,4 @@ symbian: { CONFIG += parallel_test QT += core-private gui-private declarative-private -QT += script-private QT += opengl-private diff --git a/tests/auto/declarative/qsgtextedit/qsgtextedit.pro b/tests/auto/declarative/qsgtextedit/qsgtextedit.pro index be8d98ba01..fea4b71369 100644 --- a/tests/auto/declarative/qsgtextedit/qsgtextedit.pro +++ b/tests/auto/declarative/qsgtextedit/qsgtextedit.pro @@ -13,5 +13,4 @@ symbian: { DEFINES += SRCDIR=\\\"$$PWD\\\" } QT += core-private gui-private declarative-private -QT += script-private QT += opengl-private diff --git a/tests/auto/declarative/qsgtextinput/qsgtextinput.pro b/tests/auto/declarative/qsgtextinput/qsgtextinput.pro index d3d03e7db1..56a811fa6d 100644 --- a/tests/auto/declarative/qsgtextinput/qsgtextinput.pro +++ b/tests/auto/declarative/qsgtextinput/qsgtextinput.pro @@ -13,5 +13,4 @@ symbian: { } QT += core-private gui-private declarative-private -QT += script-private QT += opengl-private diff --git a/tests/auto/declarative/qsgvisualdatamodel/qsgvisualdatamodel.pro b/tests/auto/declarative/qsgvisualdatamodel/qsgvisualdatamodel.pro index 7b961b6298..8f433c1208 100644 --- a/tests/auto/declarative/qsgvisualdatamodel/qsgvisualdatamodel.pro +++ b/tests/auto/declarative/qsgvisualdatamodel/qsgvisualdatamodel.pro @@ -15,4 +15,3 @@ symbian: { CONFIG += parallel_test QT += core-private gui-private declarative-private -QT += script-private diff --git a/tests/auto/qtquick1/qdeclarativeconnection/qdeclarativeconnection.pro b/tests/auto/qtquick1/qdeclarativeconnection/qdeclarativeconnection.pro index fb1ef04ee1..6c3bec845a 100644 --- a/tests/auto/qtquick1/qdeclarativeconnection/qdeclarativeconnection.pro +++ b/tests/auto/qtquick1/qdeclarativeconnection/qdeclarativeconnection.pro @@ -14,4 +14,4 @@ symbian: { CONFIG += parallel_test -QT += core-private gui-private declarative-private script-private qtquick1-private +QT += core-private gui-private declarative-private qtquick1-private diff --git a/tests/auto/qtquick1/qdeclarativeflickable/qdeclarativeflickable.pro b/tests/auto/qtquick1/qdeclarativeflickable/qdeclarativeflickable.pro index 79b61d81c4..c01478380b 100644 --- a/tests/auto/qtquick1/qdeclarativeflickable/qdeclarativeflickable.pro +++ b/tests/auto/qtquick1/qdeclarativeflickable/qdeclarativeflickable.pro @@ -14,4 +14,4 @@ symbian: { CONFIG += parallel_test -QT += core-private gui-private declarative-private script-private qtquick1-private +QT += core-private gui-private declarative-private qtquick1-private diff --git a/tests/auto/qtquick1/qdeclarativeflipable/qdeclarativeflipable.pro b/tests/auto/qtquick1/qdeclarativeflipable/qdeclarativeflipable.pro index e08611cbbd..64d5d0a518 100644 --- a/tests/auto/qtquick1/qdeclarativeflipable/qdeclarativeflipable.pro +++ b/tests/auto/qtquick1/qdeclarativeflipable/qdeclarativeflipable.pro @@ -14,4 +14,4 @@ symbian: { CONFIG += parallel_test -QT += core-private gui-private declarative-private script-private qtquick1-private +QT += core-private gui-private declarative-private qtquick1-private diff --git a/tests/auto/qtquick1/qdeclarativegridview/qdeclarativegridview.pro b/tests/auto/qtquick1/qdeclarativegridview/qdeclarativegridview.pro index bdff620e95..eb125599e1 100644 --- a/tests/auto/qtquick1/qdeclarativegridview/qdeclarativegridview.pro +++ b/tests/auto/qtquick1/qdeclarativegridview/qdeclarativegridview.pro @@ -14,4 +14,4 @@ symbian: { CONFIG += parallel_test -QT += core-private gui-private declarative-private script-private qtquick1-private +QT += core-private gui-private declarative-private qtquick1-private diff --git a/tests/auto/qtquick1/qdeclarativelistmodel/qdeclarativelistmodel.pro b/tests/auto/qtquick1/qdeclarativelistmodel/qdeclarativelistmodel.pro index b1e51f28bf..059f923c17 100644 --- a/tests/auto/qtquick1/qdeclarativelistmodel/qdeclarativelistmodel.pro +++ b/tests/auto/qtquick1/qdeclarativelistmodel/qdeclarativelistmodel.pro @@ -1,6 +1,5 @@ load(qttest_p4) contains(QT_CONFIG,declarative): QT += declarative qtquick1 -QT += script macx:CONFIG -= app_bundle SOURCES += tst_qdeclarativelistmodel.cpp @@ -15,4 +14,4 @@ symbian: { CONFIG += parallel_test -QT += core-private gui-private declarative-private script-private qtquick1-private +QT += core-private gui-private declarative-private qtquick1-private diff --git a/tests/auto/qtquick1/qdeclarativelistview/qdeclarativelistview.pro b/tests/auto/qtquick1/qdeclarativelistview/qdeclarativelistview.pro index 0454d4476a..5eb154dad2 100644 --- a/tests/auto/qtquick1/qdeclarativelistview/qdeclarativelistview.pro +++ b/tests/auto/qtquick1/qdeclarativelistview/qdeclarativelistview.pro @@ -14,4 +14,4 @@ symbian: { } CONFIG += parallel_test -QT += core-private gui-private declarative-private script-private qtquick1-private +QT += core-private gui-private declarative-private qtquick1-private diff --git a/tests/auto/qtquick1/qdeclarativepathview/qdeclarativepathview.pro b/tests/auto/qtquick1/qdeclarativepathview/qdeclarativepathview.pro index 251277ef57..4233096a2d 100644 --- a/tests/auto/qtquick1/qdeclarativepathview/qdeclarativepathview.pro +++ b/tests/auto/qtquick1/qdeclarativepathview/qdeclarativepathview.pro @@ -14,4 +14,4 @@ symbian: { CONFIG += parallel_test -QT += core-private gui-private declarative-private script-private qtquick1-private +QT += core-private gui-private declarative-private qtquick1-private diff --git a/tests/auto/qtquick1/qdeclarativesmoothedanimation/qdeclarativesmoothedanimation.pro b/tests/auto/qtquick1/qdeclarativesmoothedanimation/qdeclarativesmoothedanimation.pro index f9a0b17f81..99094cf101 100644 --- a/tests/auto/qtquick1/qdeclarativesmoothedanimation/qdeclarativesmoothedanimation.pro +++ b/tests/auto/qtquick1/qdeclarativesmoothedanimation/qdeclarativesmoothedanimation.pro @@ -14,4 +14,4 @@ symbian: { CONFIG += parallel_test -QT += core-private gui-private declarative-private script-private qtquick1-private +QT += core-private gui-private declarative-private qtquick1-private diff --git a/tests/auto/qtquick1/qdeclarativespringanimation/qdeclarativespringanimation.pro b/tests/auto/qtquick1/qdeclarativespringanimation/qdeclarativespringanimation.pro index 10132374e2..56f9c2df17 100644 --- a/tests/auto/qtquick1/qdeclarativespringanimation/qdeclarativespringanimation.pro +++ b/tests/auto/qtquick1/qdeclarativespringanimation/qdeclarativespringanimation.pro @@ -14,4 +14,4 @@ symbian: { CONFIG += parallel_test -QT += core-private gui-private declarative-private script-private qtquick1-private +QT += core-private gui-private declarative-private qtquick1-private diff --git a/tests/auto/qtquick1/qdeclarativestates/qdeclarativestates.pro b/tests/auto/qtquick1/qdeclarativestates/qdeclarativestates.pro index 79990aa31a..b80ab88f01 100644 --- a/tests/auto/qtquick1/qdeclarativestates/qdeclarativestates.pro +++ b/tests/auto/qtquick1/qdeclarativestates/qdeclarativestates.pro @@ -13,4 +13,4 @@ symbian: { } CONFIG += parallel_test -QT += core-private gui-private declarative-private script-private qtquick1-private +QT += core-private gui-private declarative-private qtquick1-private diff --git a/tests/auto/qtquick1/qdeclarativetext/qdeclarativetext.pro b/tests/auto/qtquick1/qdeclarativetext/qdeclarativetext.pro index fbb88ae1fa..ad9a667d57 100644 --- a/tests/auto/qtquick1/qdeclarativetext/qdeclarativetext.pro +++ b/tests/auto/qtquick1/qdeclarativetext/qdeclarativetext.pro @@ -19,4 +19,4 @@ symbian: { CONFIG += parallel_test -QT += core-private gui-private declarative-private script-private qtquick1-private +QT += core-private gui-private declarative-private qtquick1-private diff --git a/tests/auto/qtquick1/qdeclarativevisualdatamodel/qdeclarativevisualdatamodel.pro b/tests/auto/qtquick1/qdeclarativevisualdatamodel/qdeclarativevisualdatamodel.pro index 93019a53c5..a5a0add30f 100644 --- a/tests/auto/qtquick1/qdeclarativevisualdatamodel/qdeclarativevisualdatamodel.pro +++ b/tests/auto/qtquick1/qdeclarativevisualdatamodel/qdeclarativevisualdatamodel.pro @@ -14,4 +14,4 @@ symbian: { CONFIG += parallel_test -QT += core-private gui-private declarative-private script-private qtquick1-private +QT += core-private gui-private declarative-private qtquick1-private diff --git a/tests/auto/qtquick1/qdeclarativexmllistmodel/qdeclarativexmllistmodel.pro b/tests/auto/qtquick1/qdeclarativexmllistmodel/qdeclarativexmllistmodel.pro index 6a4a50c7a3..6acfe35dca 100644 --- a/tests/auto/qtquick1/qdeclarativexmllistmodel/qdeclarativexmllistmodel.pro +++ b/tests/auto/qtquick1/qdeclarativexmllistmodel/qdeclarativexmllistmodel.pro @@ -1,5 +1,5 @@ load(qttest_p4) -contains(QT_CONFIG,declarative): QT += declarative script gui network qtquick1 +contains(QT_CONFIG,declarative): QT += declarative gui network qtquick1 contains(QT_CONFIG,xmlpatterns) { QT += xmlpatterns DEFINES += QTEST_XMLPATTERNS @@ -18,4 +18,4 @@ symbian: { CONFIG += parallel_test -QT += core-private gui-private declarative-private script-private qtquick1-private +QT += core-private gui-private declarative-private qtquick1-private diff --git a/tests/benchmarks/declarative/holistic/holistic.pro b/tests/benchmarks/declarative/holistic/holistic.pro index 7f27696d60..fadb04db88 100644 --- a/tests/benchmarks/declarative/holistic/holistic.pro +++ b/tests/benchmarks/declarative/holistic/holistic.pro @@ -1,7 +1,7 @@ load(qttest_p4) TEMPLATE = app TARGET = tst_holistic -QT += declarative script network +QT += declarative network macx:CONFIG -= app_bundle CONFIG += release diff --git a/tests/benchmarks/declarative/holistic/testtypes.h b/tests/benchmarks/declarative/holistic/testtypes.h index 5a2b1c8ab1..7d1a59da44 100644 --- a/tests/benchmarks/declarative/holistic/testtypes.h +++ b/tests/benchmarks/declarative/holistic/testtypes.h @@ -53,7 +53,7 @@ #include #include #include -#include +#include #include #include diff --git a/tests/benchmarks/declarative/script/script.pro b/tests/benchmarks/declarative/script/script.pro index 75faa5cf38..01df571fda 100644 --- a/tests/benchmarks/declarative/script/script.pro +++ b/tests/benchmarks/declarative/script/script.pro @@ -1,7 +1,7 @@ load(qttest_p4) TEMPLATE = app TARGET = tst_script -QT += declarative script +QT += declarative macx:CONFIG -= app_bundle CONFIG += release diff --git a/tools/qmlviewer/qml.pri b/tools/qmlviewer/qml.pri index 9ac5adc50e..f5d3f80feb 100644 --- a/tools/qmlviewer/qml.pri +++ b/tools/qmlviewer/qml.pri @@ -1,4 +1,4 @@ -QT += core-private gui-private declarative-private script network sql +QT += core-private gui-private declarative-private network sql contains(QT_CONFIG, opengl) { QT += opengl DEFINES += GL_SUPPORTED