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