From d410ad364ec0b8877797145c68a4d7c3c98ce1c0 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 29 Jul 2011 10:25:44 +0200 Subject: Merge the QJSEngine and QJSValue development branch into master. This replaces the dependency to QtScript with two new builtin classes QJSValue and QJSEngine. This is still work in progress, development continues now in the master branch. Change-Id: I7f5487feb45c972f25a22b10cc81b9218b9805de Reviewed-on: http://codereview.qt.nokia.com/2299 Reviewed-by: Qt Sanity Bot Reviewed-by: Simon Hausmann --- src/3rdparty/v8 | 2 +- .../debugger/qdeclarativedebughelper.cpp | 4 +- src/declarative/debugger/qjsdebuggeragent.cpp | 296 +++-- src/declarative/debugger/qjsdebuggeragent_p.h | 19 +- src/declarative/declarative.pro | 2 +- src/declarative/items/context2d/qsgcontext2d.cpp | 6 +- src/declarative/items/context2d/qsgcontext2d_p.h | 6 +- src/declarative/items/qsgitem.cpp | 2 +- src/declarative/qml/qdeclarative.h | 8 +- src/declarative/qml/qdeclarativebinding.cpp | 2 +- src/declarative/qml/qdeclarativecompileddata.cpp | 4 - src/declarative/qml/qdeclarativecompiler.cpp | 8 +- src/declarative/qml/qdeclarativecompiler_p.h | 4 +- src/declarative/qml/qdeclarativecomponent.cpp | 3 +- src/declarative/qml/qdeclarativecomponent.h | 2 +- src/declarative/qml/qdeclarativecontext.cpp | 4 +- src/declarative/qml/qdeclarativecontext.h | 2 +- src/declarative/qml/qdeclarativecontext_p.h | 2 +- src/declarative/qml/qdeclarativedata_p.h | 2 +- src/declarative/qml/qdeclarativeengine.cpp | 11 +- src/declarative/qml/qdeclarativeengine.h | 7 +- src/declarative/qml/qdeclarativeengine_p.h | 5 +- src/declarative/qml/qdeclarativeexpression.cpp | 20 +- src/declarative/qml/qdeclarativemetatype.cpp | 14 +- src/declarative/qml/qdeclarativemetatype_p.h | 12 +- src/declarative/qml/qdeclarativeprivate.h | 8 +- src/declarative/qml/qdeclarativepropertycache.cpp | 8 +- src/declarative/qml/qdeclarativepropertycache_p.h | 5 +- src/declarative/qml/qdeclarativesqldatabase_p.h | 2 +- src/declarative/qml/qdeclarativetypeloader.cpp | 2 +- src/declarative/qml/qdeclarativetypeloader_p.h | 3 +- src/declarative/qml/qdeclarativetypenamecache_p.h | 2 - src/declarative/qml/qdeclarativevme.cpp | 4 +- src/declarative/qml/qdeclarativevme_p.h | 2 +- src/declarative/qml/qdeclarativevmemetaobject.cpp | 40 +- src/declarative/qml/qdeclarativeworkerscript.cpp | 10 +- src/declarative/qml/qdeclarativeworkerscript_p.h | 2 +- src/declarative/qml/qdeclarativexmlhttprequest.cpp | 5 +- src/declarative/qml/qdeclarativexmlhttprequest_p.h | 2 +- src/declarative/qml/v4/qdeclarativev4bindings.cpp | 44 +- src/declarative/qml/v4/qdeclarativev4irbuilder.cpp | 2 +- src/declarative/qml/v8/qjsconverter_p.h | 272 +++++ src/declarative/qml/v8/qjsengine.cpp | 431 ++++++++ src/declarative/qml/v8/qjsengine.h | 151 +++ src/declarative/qml/v8/qjsvalue.cpp | 1024 ++++++++++++++++++ src/declarative/qml/v8/qjsvalue.h | 165 +++ src/declarative/qml/v8/qjsvalue_impl_p.h | 1133 ++++++++++++++++++++ src/declarative/qml/v8/qjsvalue_p.h | 213 ++++ src/declarative/qml/v8/qjsvalueiterator.cpp | 294 +++++ src/declarative/qml/v8/qjsvalueiterator.h | 64 ++ src/declarative/qml/v8/qscript_impl_p.h | 41 + src/declarative/qml/v8/qscriptisolate_p.h | 71 ++ .../qml/v8/qscriptoriginalglobalobject_p.h | 158 +++ src/declarative/qml/v8/qscriptshareddata_p.h | 151 +++ src/declarative/qml/v8/qscripttools_p.h | 216 ++++ src/declarative/qml/v8/qv8bindings.cpp | 2 +- src/declarative/qml/v8/qv8engine.cpp | 751 ++++++++++++- src/declarative/qml/v8/qv8engine_impl_p.h | 131 +++ src/declarative/qml/v8/qv8engine_p.h | 108 +- src/declarative/qml/v8/qv8include.cpp | 2 +- src/declarative/qml/v8/qv8qobjectwrapper.cpp | 36 +- src/declarative/qml/v8/qv8typewrapper.cpp | 20 +- src/declarative/qml/v8/qv8variantwrapper.cpp | 38 + src/declarative/qml/v8/qv8variantwrapper_p.h | 4 + src/declarative/qml/v8/script.pri | 20 + src/declarative/qml/v8/v8.pri | 4 +- src/declarative/util/qdeclarativebind.cpp | 5 +- src/declarative/util/qdeclarativelistmodel.cpp | 4 +- src/imports/folderlistmodel/folderlistmodel.pro | 2 +- src/imports/gestures/gestures.pro | 2 +- src/imports/inputcontext/inputcontext.pro | 2 +- src/imports/inputcontext/plugin.cpp | 2 +- src/imports/testlib/main.cpp | 6 +- src/imports/testlib/testlib.pro | 2 +- src/qmltest/qmltest.pro | 2 +- src/qmltest/quicktest.cpp | 7 +- src/qtquick1/graphicsitems/qdeclarativeitem.cpp | 2 +- src/qtquick1/qtquick1.pro | 2 +- src/qtquick1/util/qdeclarativebind.cpp | 5 +- src/qtquick1/util/qdeclarativelistmodel.cpp | 2 +- src/qtquick1/util/qdeclarativelistmodel_p.h | 2 +- src/qtquick1/util/qdeclarativelistmodel_p_p.h | 2 +- .../util/qdeclarativelistmodelworkeragent_p.h | 2 +- src/qtquick1/util/qdeclarativeview.cpp | 1 - ...unction-method-to-the-Object-class-in-the.patch | 286 +++++ ...allAsConstructor-method-for-Object-in-the.patch | 397 +++++++ ...-Add-new-v8-api-to-check-if-a-value-is-an.patch | 63 ++ 87 files changed, 6465 insertions(+), 421 deletions(-) create mode 100644 src/declarative/qml/v8/qjsconverter_p.h create mode 100644 src/declarative/qml/v8/qjsengine.cpp create mode 100644 src/declarative/qml/v8/qjsengine.h create mode 100644 src/declarative/qml/v8/qjsvalue.cpp create mode 100644 src/declarative/qml/v8/qjsvalue.h create mode 100644 src/declarative/qml/v8/qjsvalue_impl_p.h create mode 100644 src/declarative/qml/v8/qjsvalue_p.h create mode 100644 src/declarative/qml/v8/qjsvalueiterator.cpp create mode 100644 src/declarative/qml/v8/qjsvalueiterator.h create mode 100644 src/declarative/qml/v8/qscript_impl_p.h create mode 100644 src/declarative/qml/v8/qscriptisolate_p.h create mode 100644 src/declarative/qml/v8/qscriptoriginalglobalobject_p.h create mode 100644 src/declarative/qml/v8/qscriptshareddata_p.h create mode 100644 src/declarative/qml/v8/qscripttools_p.h create mode 100644 src/declarative/qml/v8/qv8engine_impl_p.h create mode 100644 src/declarative/qml/v8/script.pri create mode 100644 src/v8/0009-Add-CallAsFunction-method-to-the-Object-class-in-the.patch create mode 100644 src/v8/0010-Implement-CallAsConstructor-method-for-Object-in-the.patch create mode 100644 src/v8/0011-QtScript-V8-Add-new-v8-api-to-check-if-a-value-is-an.patch (limited to 'src') 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 @@ public: 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 @@ private: } // 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 @@ public: 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 @@ protected: 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 @@ public: 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 @@ public: QDeclarativeDelayedError *erroredBindings; int inProgressCreations; - // V8 Engine - QV8Engine v8engine; + QV8Engine *v8engine() const { return q_func()->handle(); } QDeclarativeWorkerScriptEngine *getWorkerScriptEngine(); QDeclarativeWorkerScriptEngine *workerScriptEngine; @@ -256,7 +255,7 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: }; -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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ private: }; 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 @@ public: typedef QDeclarativeNotifierEndpoint Subscription; Subscription *subscriptions; - QScriptDeclarativeClass::PersistentIdentifier *identifiers; void run(Binding *, QDeclarativePropertyPrivate::WriteFlags flags); @@ -240,20 +239,19 @@ public: 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 @@ public: 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 @@ public: // 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 @@ public: // 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 @@ public: 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 @@ private: QSet m_illegalNames; + Exception m_exception; + QVariant toBasicVariant(v8::Handle); void initializeGlobal(v8::Handle); @@ -356,6 +452,10 @@ private: 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 @@ private: 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 @@ private: 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 @@ private: 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 + -- cgit v1.2.3