diff options
Diffstat (limited to 'src/qml/qml/v8')
57 files changed, 15218 insertions, 0 deletions
diff --git a/src/qml/qml/v8/notes.txt b/src/qml/qml/v8/notes.txt new file mode 100644 index 0000000000..a4006b93c6 --- /dev/null +++ b/src/qml/qml/v8/notes.txt @@ -0,0 +1,4 @@ +Removed backwards compatible imports - QTBUG-17518 + +autotest print() taking objects that don't ToString() +autotest QQmlV8Function diff --git a/src/qml/qml/v8/qjsconverter_impl_p.h b/src/qml/qml/v8/qjsconverter_impl_p.h new file mode 100644 index 0000000000..10b8ab5fae --- /dev/null +++ b/src/qml/qml/v8/qjsconverter_impl_p.h @@ -0,0 +1,268 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qjsconverter_p.h" + +#ifndef QJSCONVERTER_IMPL_P_H +#define QJSCONVERTER_IMPL_P_H + +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); + + +quint32 QJSConverter::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; +} + +QString QJSConverter::toString(v8::Handle<v8::String> jsString) +{ + if (jsString.IsEmpty()) + return QString(); + QString qstr; + qstr.resize(jsString->Length()); + jsString->Write(reinterpret_cast<uint16_t*>(qstr.data())); + return qstr; +} + +v8::Local<v8::String> QJSConverter::toString(const QString& string) +{ + return v8::String::New(reinterpret_cast<const uint16_t*>(string.data()), string.size()); +} + +QString QJSConverter::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<char, 25> 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()); +} + +// return a mask of v8::PropertyAttribute that may also contains QScriptValue::PropertyGetter or QScriptValue::PropertySetter +uint QJSConverter::toPropertyAttributes(const QFlags<QJSValuePrivate::PropertyFlag>& flags) +{ + uint attr = 0; + if (flags.testFlag(QJSValuePrivate::ReadOnly)) + attr |= v8::ReadOnly; + if (flags.testFlag(QJSValuePrivate::Undeletable)) + attr |= v8::DontDelete; + if (flags.testFlag(QJSValuePrivate::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. +QRegExp QJSConverter::toRegExp(v8::Handle<v8::RegExp> 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. +v8::Local<v8::RegExp> QJSConverter::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<v8::RegExp::Flags>(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. +v8::Local<v8::Array> QJSConverter::toStringList(const QStringList &lst) +{ + v8::Local<v8::Array> 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. +QStringList QJSConverter::toStringList(v8::Handle<v8::Array> 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. +QDateTime QJSConverter::toDateTime(v8::Handle<v8::Date> jsDate) +{ + return QDateTime::fromMSecsSinceEpoch(jsDate->NumberValue()); +} + +// Converts a QDateTime to a JS Date. +v8::Local<v8::Value> QJSConverter::toDateTime(const QDateTime &dt) +{ + double date; + if (!dt.isValid()) + date = qSNaN(); + else + date = dt.toMSecsSinceEpoch(); + return v8::Date::New(date); +} + +QT_END_NAMESPACE + +#endif // QJSCONVERTER_IMPL_P_H diff --git a/src/qml/qml/v8/qjsconverter_p.h b/src/qml/qml/v8/qjsconverter_p.h new file mode 100644 index 0000000000..29fef3c700 --- /dev/null +++ b/src/qml/qml/v8/qjsconverter_p.h @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** 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 +** us via http://www.qt-project.org/. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QJSCONVERTER_P_H +#define QJSCONVERTER_P_H + +#include "qjsvalue_p.h" +#include <QtCore/qglobal.h> +#include <QtCore/qnumeric.h> +#include <QtCore/qstring.h> +#include <QtCore/qstringlist.h> +#include <QtCore/qvarlengtharray.h> +#include <QtCore/qregexp.h> +#include <QtCore/qdatetime.h> + +#include <private/qv8_p.h> + +QT_BEGIN_NAMESPACE + +/* + \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 inline quint32 toArrayIndex(const QString& string); + + static inline QString toString(v8::Handle<v8::String> jsString); + static inline v8::Local<v8::String> toString(const QString& string); + static inline QString toString(double value); + + enum { + PropertyAttributeMask = v8::ReadOnly | v8::DontDelete | v8::DontEnum, + }; + // return a mask of v8::PropertyAttribute that may also contains QScriptValue::PropertyGetter or QScriptValue::PropertySetter + static inline uint toPropertyAttributes(const QFlags<QJSValuePrivate::PropertyFlag>& flags); + + // 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 inline QRegExp toRegExp(v8::Handle<v8::RegExp> jsRegExp); + + // 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 inline v8::Local<v8::RegExp> toRegExp(const QRegExp &re); + + // 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 inline v8::Local<v8::Array> toStringList(const QStringList &lst); + + // 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 inline QStringList toStringList(v8::Handle<v8::Array> jsArray); + + // Converts a JS Date to a QDateTime. + static inline QDateTime toDateTime(v8::Handle<v8::Date> jsDate); + + // Converts a QDateTime to a JS Date. + static inline v8::Local<v8::Value> toDateTime(const QDateTime &dt); +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/qml/qml/v8/qjsengine.cpp b/src/qml/qml/v8/qjsengine.cpp new file mode 100644 index 0000000000..3121d1b361 --- /dev/null +++ b/src/qml/qml/v8/qjsengine.cpp @@ -0,0 +1,476 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** 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 +** us via http://www.qt-project.org/. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qjsengine.h" +#include "qjsengine_p.h" +#include "qjsvalue.h" +#include "qjsvalue_p.h" +#include "qscriptisolate_p.h" +#include "qscript_impl_p.h" +#include "qv8engine_p.h" + +#include <QtCore/qdatetime.h> +#include <QtCore/qmetaobject.h> +#include <QtCore/qstringlist.h> +#include <QtCore/qvariant.h> +#include <QtCore/qdatetime.h> + +#include <QtCore/qcoreapplication.h> +#include <QtCore/qdir.h> +#include <QtCore/qfile.h> +#include <QtCore/qfileinfo.h> +#include <QtCore/qpluginloader.h> +#include <qthread.h> +#include <qmutex.h> +#include <qwaitcondition.h> + +#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<int>) + +/*! + \since 5.0 + \class QJSEngine + + \brief The QJSEngine class provides an environment for evaluating JavaScript code. + + \ingroup qtjavascript + \mainclass + + \section1 Evaluating Scripts + + Use evaluate() to evaluate script code. + + \snippet doc/src/snippets/code/src_script_qjsengine.cpp 0 + + evaluate() returns a QJSValue that holds the result of the + evaluation. The QJSValue class provides functions for converting + the result to various C++ types (e.g. QJSValue::toString() + and QJSValue::toNumber()). + + The following code snippet shows how a script function can be + defined and then invoked from C++ using QJSValue::call(): + + \snippet doc/src/snippets/code/src_script_qjsengine.cpp 1 + + As can be seen from the above snippets, a script is provided to the + engine in the form of a string. One common way of loading scripts is + by reading the contents of a file and passing it to evaluate(): + + \snippet doc/src/snippets/code/src_script_qjsengine.cpp 2 + + Here we pass the name of the file as the second argument to + evaluate(). This does not affect evaluation in any way; the second + argument is a general-purpose string that is used to identify the + script for debugging purposes (for example, our filename will now + show up in any uncaughtExceptionBacktrace() involving the script). + + \section1 Engine Configuration + + The globalObject() function returns the \bold {Global Object} + associated with the script engine. Properties of the Global Object + are accessible from any script code (i.e. they are global + variables). Typically, before evaluating "user" scripts, you will + want to configure a script engine by adding one or more properties + to the Global Object: + + \snippet doc/src/snippets/code/src_script_qjsengine.cpp 3 + + Adding custom properties to the scripting environment is one of the + standard means of providing a scripting API that is specific to your + application. Usually these custom properties are objects created by + the newQObject() or newObject() functions. + + \section1 Script Exceptions + + evaluate() can throw a script exception (e.g. due to a syntax + error); in that case, the return value is the value that was thrown + (typically an \c{Error} object). You can check whether the + evaluation caused an exception by calling hasUncaughtException(). In + that case, you can call toString() on the error object to obtain an + error message. The current uncaught exception is also available + through uncaughtException(). + Calling clearExceptions() will cause any uncaught exceptions to be + cleared. + + \snippet doc/src/snippets/code/src_script_qjsengine.cpp 4 + + \section1 Script Object Creation + + Use newObject() to create a JavaScript object; this is the + C++ equivalent of the script statement \c{new Object()}. You can use + the object-specific functionality in QJSValue to manipulate the + script object (e.g. QJSValue::setProperty()). Similarly, use + newArray() to create a JavaScript array object. + + \section1 QObject Integration + + Use newQObject() to wrap a QObject (or subclass) + pointer. newQObject() returns a proxy script object; properties, + children, and signals and slots of the QObject are available as + properties of the proxy object. No binding code is needed because it + is done dynamically using the Qt meta object system. + + \snippet doc/src/snippets/code/src_script_qjsengine.cpp 5 + + \sa QJSValue, {Making Applications Scriptable} + +*/ + +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)) +{ +} + +#ifdef QT_DEPRECATED + +/*! + \internal +*/ +QJSEngine::QJSEngine(QJSEngine::ContextOwnership ownership) + : d(new QV8Engine(this, ownership)) +{ +} + +#endif // QT_DEPRECATED + +/*! + 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(QJSEnginePrivate &dd, QObject *parent) + : QObject(dd, parent) + , d(new QV8Engine(this)) +{ +} + +/*! + Destroys this QJSEngine. +*/ +QJSEngine::~QJSEngine() +{ + delete d; +} + +/*! + \fn QV8Engine *QJSEngine::handle() const + \internal +*/ + +#ifdef QT_DEPRECATED + +/*! + \obsolete + + 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(); +} + +/*! + \obsolete + + 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()); +} + +/*! + \obsolete + + Clears any uncaught exceptions in this engine. + + \sa hasUncaughtException() +*/ +void QJSEngine::clearExceptions() +{ + Q_D(QJSEngine); + QScriptIsolate api(d); + d->clearExceptions(); +} + +#endif // QT_DEPRECATED + +/*! + 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)); +} + +/*! + Creates a JavaScript object of class Object. + + The prototype of the created object will be the Object + prototype object. + + \sa newArray(), QJSValue::setProperty() +*/ +QJSValue QJSEngine::newObject() +{ + Q_D(QJSEngine); + QScriptIsolate api(d, QScriptIsolate::NotNullEngine); + v8::HandleScope handleScope; + return QJSValuePrivate::get(new QJSValuePrivate(d, v8::Object::New())); +} + +/*! + Creates a JavaScript object of class Array with the given \a length. + + \sa newObject() +*/ +QJSValue QJSEngine::newArray(uint length) +{ + Q_D(QJSEngine); + QScriptIsolate api(d, QScriptIsolate::NotNullEngine); + v8::HandleScope handleScope; + return QJSValuePrivate::get(d->newArray(length)); +} + +/*! + Creates a JavaScript object that wraps the given QObject \a + object, using JavaScriptOwnership. 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. + + If \a object is a null pointer, this function returns a null value. + + 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 the engine's control, any + attempt to access the deleted QObject's members through the JavaScript + 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, QV8Engine::JavaScriptOwnership)); +} + +/*! + Returns this engine's Global Object. + + By default, the Global Object contains the built-in objects that are + part of \l{ECMA-262}, such as Math, Date and String. Additionally, + you can set properties of the Global Object to make your own + extensions available to all script code. Non-local variables in + script code will be created as properties of the Global Object, as + well as local variables in global code. +*/ +QJSValue QJSEngine::globalObject() const +{ + Q_D(const QJSEngine); + QScriptIsolate api(d, QScriptIsolate::NotNullEngine); + v8::HandleScope handleScope; + return d->scriptValueFromInternal(d->global()); +} + +/*! + * \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<bool*>(ptr) = vp->toBool(); + return true; + case QMetaType::Int: + *reinterpret_cast<int*>(ptr) = vp->toInt32(); + return true; + case QMetaType::UInt: + *reinterpret_cast<uint*>(ptr) = vp->toUInt32(); + return true; + case QMetaType::LongLong: + *reinterpret_cast<qlonglong*>(ptr) = vp->toInteger(); + return true; + case QMetaType::ULongLong: + *reinterpret_cast<qulonglong*>(ptr) = vp->toInteger(); + return true; + case QMetaType::Double: + *reinterpret_cast<double*>(ptr) = vp->toNumber(); + return true; + case QMetaType::QString: + *reinterpret_cast<QString*>(ptr) = vp->toString(); + return true; + case QMetaType::Float: + *reinterpret_cast<float*>(ptr) = vp->toNumber(); + return true; + case QMetaType::Short: + *reinterpret_cast<short*>(ptr) = vp->toInt32(); + return true; + case QMetaType::UShort: + *reinterpret_cast<unsigned short*>(ptr) = vp->toUInt16(); + return true; + case QMetaType::Char: + *reinterpret_cast<char*>(ptr) = vp->toInt32(); + return true; + case QMetaType::UChar: + *reinterpret_cast<unsigned char*>(ptr) = vp->toUInt16(); + return true; + case QMetaType::QChar: + *reinterpret_cast<QChar*>(ptr) = vp->toUInt16(); + return true; + default: + return false; + } + } +} + +/*! \fn QJSValue QJSEngine::toScriptValue(const T &value) + + Creates a QJSValue with the given \a value. + + \sa fromScriptValue() +*/ + +/*! \fn T QJSEngine::fromScriptValue(const QJSValue &value) + + Returns the given \a value converted to the template type \c{T}. + + \sa toScriptValue() +*/ + +QT_END_NAMESPACE + +#include "moc_qjsengine.cpp" diff --git a/src/qml/qml/v8/qjsengine.h b/src/qml/qml/v8/qjsengine.h new file mode 100644 index 0000000000..1521c752d4 --- /dev/null +++ b/src/qml/qml/v8/qjsengine.h @@ -0,0 +1,141 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** 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 +** us via http://www.qt-project.org/. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QJSENGINE_H +#define QJSENGINE_H + +#include <QtCore/qmetatype.h> + +#include <QtCore/qvariant.h> +#include <QtCore/qsharedpointer.h> +#include <QtCore/qobject.h> +#include <QtQml/qjsvalue.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + + +class QV8Engine; + +template <typename T> +inline T qjsvalue_cast(const QJSValue &); + +class QJSEnginePrivate; +class Q_QML_EXPORT QJSEngine + : public QObject +{ + Q_OBJECT +public: +#ifdef QT_DEPRECATED + enum ContextOwnership { + AdoptCurrentContext, + CreateNewContext + }; + QT_DEPRECATED explicit QJSEngine(ContextOwnership ownership); +#endif + + QJSEngine(); + explicit QJSEngine(QObject *parent); + virtual ~QJSEngine(); + + QJSValue globalObject() const; + + QJSValue evaluate(const QString &program, const QString &fileName = QString(), int lineNumber = 1); + + QJSValue newObject(); + QJSValue newArray(uint length = 0); + + QJSValue newQObject(QObject *object); + + template <typename T> + inline QJSValue toScriptValue(const T &value) + { + return create(qMetaTypeId<T>(), &value); + } + template <typename T> + inline T fromScriptValue(const QJSValue &value) + { + return qjsvalue_cast<T>(value); + } + + void collectGarbage(); + + QV8Engine *handle() const { return d; } + +#ifdef QT_DEPRECATED + QT_DEPRECATED bool hasUncaughtException() const; + QT_DEPRECATED QJSValue uncaughtException() const; + QT_DEPRECATED void clearExceptions(); +#endif + +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(QJSEnginePrivate &dd, QObject *parent = 0); + +private: + QV8Engine *d; + Q_DISABLE_COPY(QJSEngine) + Q_DECLARE_PRIVATE(QJSEngine) + friend class QV8Engine; +}; + +inline bool qjsvalue_cast_helper(const QJSValue &value, int type, void *ptr) +{ + return QJSEngine::convertV2(value, type, ptr); +} + +template<typename T> +T qjsvalue_cast(const QJSValue &value) +{ + T t; + const int id = qMetaTypeId<T>(); + + if (qjsvalue_cast_helper(value, id, &t)) + return t; + else if (value.isVariant()) + return qvariant_cast<T>(value.toVariant()); + + return T(); +} + +template <> +inline QVariant qjsvalue_cast<QVariant>(const QJSValue &value) +{ + return value.toVariant(); +} + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QJSENGINE_H diff --git a/src/qml/qml/v8/qjsengine_p.h b/src/qml/qml/v8/qjsengine_p.h new file mode 100644 index 0000000000..ecd5f7cc86 --- /dev/null +++ b/src/qml/qml/v8/qjsengine_p.h @@ -0,0 +1,57 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** 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 +** us via http://www.qt-project.org/. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QJSENGINE_P_H +#define QJSENGINE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/private/qobject_p.h> +#include "qjsengine.h" + + +QT_BEGIN_NAMESPACE + + +class QJSEnginePrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QJSEngine) + +public: + static QJSEnginePrivate* get(QJSEngine*e) { return e->d_func(); } + + QJSEnginePrivate() {} +}; + +QT_END_NAMESPACE + +#endif // QJSENGINE_P_H diff --git a/src/qml/qml/v8/qjsvalue.cpp b/src/qml/qml/v8/qjsvalue.cpp new file mode 100644 index 0000000000..e0a925c3bb --- /dev/null +++ b/src/qml/qml/v8/qjsvalue.cpp @@ -0,0 +1,856 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** 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 +** us via http://www.qt-project.org/. +** +** $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 <QtCore/qstring.h> + +/*! + \since 5.0 + \class QJSValue + + \brief The QJSValue class acts as a container for Qt/JavaScript data types. + + \ingroup qtjavascript + \mainclass + + QJSValue supports the types defined in the \l{ECMA-262} + standard: The primitive types, which are Undefined, Null, Boolean, + Number, and String; and the Object type. Additionally, built-in + support is provided for Qt/C++ types such as QVariant and QObject. + + For the object-based types (including Date and RegExp), use the + newT() functions in QJSEngine (e.g. QJSEngine::newObject()) + to create a QJSValue of the desired type. For the primitive types, + use one of the QJSValue constructor overloads. + + The methods named isT() (e.g. isBool(), isUndefined()) can be + used to test if a value is of a certain type. The methods named + toT() (e.g. toBool(), toString()) can be used to convert a + QJSValue to another type. You can also use the generic + QJSValue_cast() function. + + Object values have zero or more properties which are themselves + QJSValues. Use setProperty() to set a property of an object, and + call property() to retrieve the value of a property. + + \snippet doc/src/snippets/code/src_script_qjsvalue.cpp 0 + + If you want to iterate over the properties of a script object, use + the QJSValueIterator class. + + Object values have an internal \c{prototype} property, which can be + accessed with prototype() and setPrototype(). + + Function objects (objects for which isCallable()) returns true) can + be invoked by calling call(). Constructor functions can be used to + construct new objects by calling callAsConstructor(). + + Use equals() or strictlyEquals() to compare a QJSValue to another. + + Note that a QJSValue for which isObject() is true only carries a + reference to an actual object; copying the QJSValue will only + copy the object reference, not the object itself. If you want to + clone an object (i.e. copy an object's properties to another + object), you can do so with the help of a \c{for-in} statement in + script code, or QJSValueIterator in C++. + + \sa QJSEngine, QJSValueIterator +*/ + +/*! + \enum QJSValue::SpecialValue + + This enum is used to specify a single-valued type. + + \value UndefinedValue An undefined value. + + \value NullValue A null value. +*/ + +QT_BEGIN_NAMESPACE + +/*! + Constructs a new QJSValue with a boolean \a value. +*/ +QJSValue::QJSValue(bool value) + : d_ptr(new QJSValuePrivate(value)) +{ +} + +/*! + 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)) +{ +} + +/*! + 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 + +/*! + Constructs a new QJSValue from private + \internal +*/ +QJSValue::QJSValue(QJSValuePrivate* d) + : d_ptr(d) +{ +} + +/*! + Constructs a new QJSValue from private + \internal +*/ +QJSValue::QJSValue(QScriptPassPointer<QJSValuePrivate> d) + : d_ptr(d.give()) +{ +} + +/*! + 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 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(); +} + +/*! + 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. +*/ +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. +*/ +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. +*/ +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 QJSEngine::newObject() +*/ +bool QJSValue::isObject() const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return d->isObject(); +} + +/*! + Returns true if this QJSValue can be called a function, otherwise + returns false. + + \sa call() +*/ +bool QJSValue::isCallable() 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() +*/ +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(), toInt(), toUInt() +*/ +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(); +} + +/*! + 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(), toUInt() +*/ +qint32 QJSValue::toInt() 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(), toInt() +*/ +quint32 QJSValue::toUInt() const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return d->toUInt32(); +} + +/*! + 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. + \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, passing \a args as arguments + to the function, and using the globalObject() as the "this"-object. + Returns the value returned from the function. + + If this QJSValue is not callable, call() does nothing and + returns an undefined QJSValue. + + 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. + + \sa isCallable(), callWithInstance(), callAsConstructor() +*/ +QJSValue QJSValue::call(const QJSValueList &args) +{ + Q_D(QJSValue); + QScriptIsolate api(d->engine()); + return d->call(/*thisObject=*/0, args); +} + +/*! + Calls this QJSValue as a function, using \a instance 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 undefined QJSValue. + + Note that if \a instance 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. + + \sa call() +*/ +QJSValue QJSValue::callWithInstance(const QJSValue &instance, const QJSValueList &args) +{ + Q_D(QJSValue); + QScriptIsolate api(d->engine()); + return d->call(QJSValuePrivate::get(instance), 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, callAsConstructor() does + nothing and returns an undefined QJSValue. + + Calling this function can cause an exception to occur in the + script engine; in that case, the value that was thrown + (typically an \c{Error} object) is returned. You can call + QJSEngine::hasUncaughtException() to determine if an exception + occurred. + + \sa call(), QJSEngine::newObject() +*/ +QJSValue QJSValue::callAsConstructor(const QJSValueList &args) +{ + Q_D(QJSValue); + QScriptIsolate api(d->engine()); + return QJSValuePrivate::get(d->callAsConstructor(args)); +} + +#ifdef QT_DEPRECATED + +/*! + \obsolete + + 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; +} + +#endif // QT_DEPRECATED + +/*! + If this QJSValue is an object, returns the internal prototype + (\c{__proto__} property) of this object; otherwise returns an + undefined 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() +*/ +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 the value of this QJSValue's property with the given \a name. + If no such property exists, an undefined 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(), hasProperty(), 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. + + 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. + + \sa property(), deleteProperty() +*/ +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)); +} + +/*! + Attempts to delete this object's property of the given \a name. + Returns true if the property was deleted, otherwise returns false. + + The behavior of this function is consistent with the JavaScript + delete operator. In particular: + + \list + \o Non-configurable properties cannot be deleted. + \o This function will return true even if this object doesn't + have a property of the given \a name (i.e., non-existent + properties are "trivially deletable"). + \o If this object doesn't have an own property of the given + \a name, but an object in the prototype() chain does, the + prototype object's property is not deleted, and this function + returns true. + \endlist + + \sa setProperty(), hasOwnProperty() +*/ +bool QJSValue::deleteProperty(const QString &name) +{ + Q_D(QJSValue); + QScriptIsolate api(d->engine()); + return d->deleteProperty(name); +} + +/*! + Returns true if this object has a property of the given \a name, + otherwise returns false. + + \sa property(), hasOwnProperty() +*/ +bool QJSValue::hasProperty(const QString &name) const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return d->hasProperty(name); +} + +/*! + Returns true if this object has an own (not prototype-inherited) + property of the given \a name, otherwise returns false. + + \sa property(), hasProperty() +*/ +bool QJSValue::hasOwnProperty(const QString &name) const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return d->hasOwnProperty(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 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. +*/ +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/qml/qml/v8/qjsvalue.h b/src/qml/qml/v8/qjsvalue.h new file mode 100644 index 0000000000..30ea2e7345 --- /dev/null +++ b/src/qml/qml/v8/qjsvalue.h @@ -0,0 +1,143 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** 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 +** us via http://www.qt-project.org/. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QJSVALUE_H +#define QJSVALUE_H + +#include <QtQml/qtqmlglobal.h> + +#include <QtCore/qstring.h> +#include <QtCore/qlist.h> +#include <QtCore/qsharedpointer.h> +#include <QtCore/qshareddata.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + + +class QJSValue; +class QJSEngine; +class QVariant; +class QObject; +struct QMetaObject; +class QDateTime; + +typedef QList<QJSValue> QJSValueList; + +class QJSValuePrivate; +struct QScriptValuePrivatePointerDeleter; +template <class T> class QScriptPassPointer; + +class Q_QML_EXPORT QJSValue +{ +public: + enum SpecialValue { + NullValue, + UndefinedValue + }; + +public: + QJSValue(SpecialValue value = UndefinedValue); + ~QJSValue(); + QJSValue(const QJSValue &other); + + 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); + + bool isBool() const; + bool isNumber() 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; + qint32 toInt() const; + quint32 toUInt() const; + bool toBool() const; + QVariant toVariant() const; + QObject *toQObject() const; + QDateTime toDateTime() 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); + + bool hasProperty(const QString &name) const; + bool hasOwnProperty(const QString &name) const; + + QJSValue property(quint32 arrayIndex) const; + void setProperty(quint32 arrayIndex, const QJSValue &value); + + bool deleteProperty(const QString &name); + + bool isCallable() const; + QJSValue call(const QJSValueList &args = QJSValueList()); + QJSValue callWithInstance(const QJSValue &instance, const QJSValueList &args = QJSValueList()); + QJSValue callAsConstructor(const QJSValueList &args = QJSValueList()); + +#ifdef QT_DEPRECATED + QT_DEPRECATED QJSEngine *engine() const; +#endif + +private: + // force compile error, prevent QJSValue(bool) to be called + + QJSValue(void *) Q_DECL_EQ_DELETE; + + QJSValue(QJSValuePrivate*); + QJSValue(QScriptPassPointer<QJSValuePrivate>); + +private: + QExplicitlySharedDataPointer<QJSValuePrivate> d_ptr; + + Q_DECLARE_PRIVATE(QJSValue) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/qml/qml/v8/qjsvalue_impl_p.h b/src/qml/qml/v8/qjsvalue_impl_p.h new file mode 100644 index 0000000000..cd33859c50 --- /dev/null +++ b/src/qml/qml/v8/qjsvalue_impl_p.h @@ -0,0 +1,977 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** 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 +** us via http://www.qt-project.org/. +** +** $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<QJSValuePrivate*>(d)); +} + +QJSValue QJSValuePrivate::get(QScriptPassPointer<QJSValuePrivate> d) +{ + Q_ASSERT(d); + return QJSValue(d); +} + +QJSValue QJSValuePrivate::get(QJSValuePrivate* d) +{ + Q_ASSERT(d); + return QJSValue(d); +} + +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<v8::Value>::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<v8::Value>::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<v8::Value>::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<v8::Value>::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<v8::Value>::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<v8::Value>::New(m_engine->makeJSValue(value)); + m_engine->registerValue(this); +} + +QJSValuePrivate::QJSValuePrivate(QV8Engine *engine, v8::Handle<v8::Value> value) + : m_engine(engine), m_state(JSValue), m_value(v8::Persistent<v8::Value>::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 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 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. +} + +QString QJSValuePrivate::toString() const +{ + switch (m_state) { + 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<v8::String> 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 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<v8::Date>::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; + + // Must use floor explicitly rather than qFloor here. On some + // platforms qFloor will cast the value to a single precision float and use + // floorf() which results in test failures. + return (result > 0) ? floor(result) : -1 * floor(-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; + + // The explicit casts are required to avoid undefined behaviour. For example, casting + // a negative double directly to an unsigned int on ARM NEON FPU results in the value + // being set to zero. Casting to a signed int first ensures well defined behaviour. + return (quint32) (qint32) 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::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 (!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; + } + + return (isUndefined() && other->isUndefined()) + || (isNull() && other->isNull()); +} + +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 (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<QJSValuePrivate> cmp(eng->evaluate(QString::fromLatin1("(function(a,b){return a<b})"))); + Q_ASSERT(cmp->isFunction()); + v8::Handle<v8::Value> args[2]; + cmp->prepareArgumentsForCall(args, QJSValueList() << QJSValuePrivate::get(this) << QJSValuePrivate::get(other)); + QScriptSharedDataPointer<QJSValuePrivate> 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 QScriptPassPointer<QJSValuePrivate> QJSValuePrivate::prototype() const +{ + if (isObject()) { + v8::HandleScope handleScope; + return new QJSValuePrivate(engine(), v8::Handle<v8::Object>::Cast(m_value)->GetPrototype()); + } + return new QJSValuePrivate(); +} + +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<v8::Object>::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<v8::String> name, QJSValuePrivate* value, uint attribs) +{ + if (!isObject()) + return; + + if (!value->isJSBased()) + value->assignEngine(engine()); + + 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 (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> QJSValuePrivate::property(const QString& name) const +{ + if (!isObject()) + return new QJSValuePrivate(); + if (!name.length()) + return new QJSValuePrivate(engine()); + + v8::HandleScope handleScope; + return property(QJSConverter::toString(name)); +} + +inline QScriptPassPointer<QJSValuePrivate> QJSValuePrivate::property(v8::Handle<v8::String> name) const +{ + Q_ASSERT(!name.IsEmpty()); + if (!isObject()) + return new QJSValuePrivate(); + return property<>(name); +} + +inline QScriptPassPointer<QJSValuePrivate> QJSValuePrivate::property(quint32 index) const +{ + if (!isObject()) + return new QJSValuePrivate(); + return property<>(index); +} + +template<typename T> +inline QScriptPassPointer<QJSValuePrivate> QJSValuePrivate::property(T name) const +{ + Q_ASSERT(isObject()); + v8::HandleScope handleScope; + v8::Handle<v8::Object> self(v8::Object::Cast(*m_value)); + + v8::TryCatch tryCatch; + v8::Handle<v8::Value> result = self->Get(name); + if (tryCatch.HasCaught()) { + result = tryCatch.Exception(); + engine()->setException(result, tryCatch.Message()); + return new QJSValuePrivate(engine(), result); + } + if (result.IsEmpty()) + return new QJSValuePrivate(engine()); + return new QJSValuePrivate(engine(), result); +} + +inline bool QJSValuePrivate::deleteProperty(const QString& name) +{ + if (!isObject()) + return false; + + v8::HandleScope handleScope; + v8::Handle<v8::Object> self(v8::Handle<v8::Object>::Cast(m_value)); + return self->Delete(QJSConverter::toString(name)); +} + +inline bool QJSValuePrivate::hasProperty(const QString &name) const +{ + if (!isObject()) + return false; + + v8::HandleScope handleScope; + v8::Handle<v8::Object> self(v8::Handle<v8::Object>::Cast(m_value)); + return self->Has(QJSConverter::toString(name)); +} + +inline bool QJSValuePrivate::hasOwnProperty(const QString &name) const +{ + if (!isObject()) + return false; + + v8::HandleScope handleScope; + v8::Handle<v8::Object> self(v8::Handle<v8::Object>::Cast(m_value)); + return self->HasOwnProperty(QJSConverter::toString(name)); +} + +inline QJSValuePrivate::PropertyFlags QJSValuePrivate::propertyFlags(const QString& name) const +{ + if (!isObject()) + return QJSValuePrivate::PropertyFlags(0); + + v8::HandleScope handleScope; + return engine()->getPropertyFlags(v8::Handle<v8::Object>::Cast(m_value), QJSConverter::toString(name)); +} + +inline QJSValuePrivate::PropertyFlags QJSValuePrivate::propertyFlags(v8::Handle<v8::String> name) const +{ + if (!isObject()) + return QJSValuePrivate::PropertyFlags(0); + + v8::HandleScope handleScope; + return engine()->getPropertyFlags(v8::Handle<v8::Object>::Cast(m_value), name); +} + +inline QScriptPassPointer<QJSValuePrivate> QJSValuePrivate::call(QJSValuePrivate* thisObject, const QJSValueList& args) +{ + if (!isCallable()) + return new QJSValuePrivate(); + + v8::HandleScope handleScope; + + // Convert all arguments and bind to the engine. + int argc = args.size(); + QVarLengthArray<v8::Handle<v8::Value>, 8> argv(argc); + if (!prepareArgumentsForCall(argv.data(), args)) { + qWarning("QJSValue::call() failed: cannot call function with argument created in a different engine"); + return new QJSValuePrivate(engine()); + } + + return call(thisObject, argc, argv.data()); +} + +QScriptPassPointer<QJSValuePrivate> QJSValuePrivate::call(QJSValuePrivate* thisObject, int argc, v8::Handle<v8::Value> *argv) +{ + QV8Engine *e = engine(); + + v8::Handle<v8::Object> recv; + + if (!thisObject || !thisObject->isObject()) { + recv = v8::Handle<v8::Object>(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 new QJSValuePrivate(engine()); + } + + recv = v8::Handle<v8::Object>(v8::Object::Cast(*thisObject->m_value)); + } + + if (argc < 0) { + v8::Local<v8::Value> 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<v8::Value> 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> QJSValuePrivate::callAsConstructor(int argc, v8::Handle<v8::Value> *argv) +{ + QV8Engine *e = engine(); + + if (argc < 0) { + v8::Local<v8::Value> 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<v8::Value> 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> QJSValuePrivate::callAsConstructor(const QJSValueList& args) +{ + if (!isCallable()) + return new QJSValuePrivate(); + + v8::HandleScope handleScope; + + // Convert all arguments and bind to the engine. + int argc = args.size(); + QVarLengthArray<v8::Handle<v8::Value>, 8> argv(argc); + if (!prepareArgumentsForCall(argv.data(), args)) { + qWarning("QJSValue::callAsConstructor() failed: cannot construct function with argument created in a different engine"); + return new QJSValuePrivate(engine()); + } + + return callAsConstructor(argc, argv.data()); +} + +/*! \internal + * Make sure this value is associated with a v8 value belonging to this engine. + * If the value belongs to another engine, returns false. + */ +bool QJSValuePrivate::assignEngine(QV8Engine* engine) +{ + Q_ASSERT(engine); + v8::HandleScope handleScope; + switch (m_state) { + case CBool: + m_value = v8::Persistent<v8::Value>::New(engine->makeJSValue(u.m_bool)); + break; + case CString: + m_value = v8::Persistent<v8::Value>::New(engine->makeJSValue(*u.m_string)); + delete u.m_string; + break; + case CNumber: + m_value = v8::Persistent<v8::Value>::New(engine->makeJSValue(u.m_number)); + break; + case CNull: + m_value = v8::Persistent<v8::Value>::New(engine->makeJSValue(QJSValue::NullValue)); + break; + case CUndefined: + m_value = v8::Persistent<v8::Value>::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 + Invalidates this value (makes it undefined). + + Does not remove the value from the engine's list of + registered values; that's the responsibility of the caller. +*/ +void QJSValuePrivate::invalidate() +{ + if (isJSBased()) { + m_value.Dispose(); + m_value.Clear(); + } else if (isStringBased()) { + delete u.m_string; + } + m_engine = 0; + m_state = CUndefined; +} + +QV8Engine* QJSValuePrivate::engine() const +{ + return m_engine; +} + +inline QJSValuePrivate::operator v8::Handle<v8::Value>() const +{ + Q_ASSERT(isJSBased()); + return m_value; +} + +inline QJSValuePrivate::operator v8::Handle<v8::Object>() const +{ + Q_ASSERT(isObject()); + return v8::Handle<v8::Object>::Cast(m_value); +} + +/*! + * Return a v8::Handle, assign to the engine if needed. + */ +v8::Handle<v8::Value> QJSValuePrivate::asV8Value(QV8Engine* engine) +{ + if (!m_engine) { + if (!assignEngine(engine)) + return v8::Handle<v8::Value>(); + } + 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<v8::Value> 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->assignEngine(engine()))) + // Different engines are not allowed! + return false; + argv[j] = *value; + } + return true; +} + +QT_END_NAMESPACE + +#endif diff --git a/src/qml/qml/v8/qjsvalue_p.h b/src/qml/qml/v8/qjsvalue_p.h new file mode 100644 index 0000000000..3eccba64bd --- /dev/null +++ b/src/qml/qml/v8/qjsvalue_p.h @@ -0,0 +1,195 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** 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 +** us via http://www.qt-project.org/. +** +** $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 <private/qv8_p.h> + +#include <QtCore/qbytearray.h> +#include <QtCore/qdatetime.h> +#include <QtCore/qmath.h> +#include <QtCore/qvarlengtharray.h> +#include <qdebug.h> + +#include <private/qintrusivelist_p.h> +#include "qscriptshareddata_p.h" +#include "qjsvalue.h" + +QT_BEGIN_NAMESPACE + +class QV8Engine; + +/*! + \internal + \class QJSValuePrivate +*/ +class QJSValuePrivate + : public QSharedData +{ +public: + enum PropertyFlag { + ReadOnly = 0x00000001, + Undeletable = 0x00000002, + SkipInEnumeration = 0x00000004 + }; + Q_DECLARE_FLAGS(PropertyFlags, PropertyFlag) + + 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<QJSValuePrivate> 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 = QJSValue::UndefinedValue); + + 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 = QJSValue::UndefinedValue); + inline QJSValuePrivate(QV8Engine *engine, v8::Handle<v8::Value>); + inline void invalidate(); + + inline bool toBool() const; + inline double toNumber() 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 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 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 QScriptPassPointer<QJSValuePrivate> prototype() const; + inline void setPrototype(QJSValuePrivate* prototype); + + inline void setProperty(const QString &name, QJSValuePrivate *value, uint attribs = 0); + inline void setProperty(v8::Handle<v8::String> name, QJSValuePrivate *value, uint attribs = 0); + inline void setProperty(quint32 index, QJSValuePrivate* value, uint attribs = 0); + inline QScriptPassPointer<QJSValuePrivate> property(const QString& name) const; + inline QScriptPassPointer<QJSValuePrivate> property(v8::Handle<v8::String> name) const; + inline QScriptPassPointer<QJSValuePrivate> property(quint32 index) const; + template<typename T> + inline QScriptPassPointer<QJSValuePrivate> property(T name) const; + inline bool deleteProperty(const QString& name); + inline bool hasProperty(const QString &name) const; + inline bool hasOwnProperty(const QString &name) const; + inline PropertyFlags propertyFlags(const QString& name) const; + inline PropertyFlags propertyFlags(v8::Handle<v8::String> name) const; + + inline QScriptPassPointer<QJSValuePrivate> call(QJSValuePrivate* thisObject, const QJSValueList& args); + inline QScriptPassPointer<QJSValuePrivate> call(QJSValuePrivate* thisObject, const QJSValue& arguments); + inline QScriptPassPointer<QJSValuePrivate> call(QJSValuePrivate* thisObject, int argc, v8::Handle< v8::Value >* argv); + inline QScriptPassPointer<QJSValuePrivate> callAsConstructor(int argc, v8::Handle<v8::Value> *argv); + inline QScriptPassPointer<QJSValuePrivate> callAsConstructor(const QJSValueList& args); + inline QScriptPassPointer<QJSValuePrivate> callAsConstructor(const QJSValue& arguments); + + inline bool assignEngine(QV8Engine *engine); + inline QV8Engine *engine() const; + + inline operator v8::Handle<v8::Value>() const; + inline operator v8::Handle<v8::Object>() const; + inline v8::Handle<v8::Value> asV8Value(QV8Engine *engine); +private: + QIntrusiveListNode m_node; + QV8Engine *m_engine; + + // Please, update class documentation when you change the enum. + enum State { + 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<v8::Value> m_value; + + Q_DISABLE_COPY(QJSValuePrivate) + inline bool isJSBased() const; + inline bool isNumberBased() const; + inline bool isStringBased() const; + inline bool prepareArgumentsForCall(v8::Handle<v8::Value> argv[], const QJSValueList& arguments) const; + + friend class QV8Engine; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QJSValuePrivate::PropertyFlags) + +QT_END_NAMESPACE + +#endif diff --git a/src/qml/qml/v8/qjsvalueiterator.cpp b/src/qml/qml/v8/qjsvalueiterator.cpp new file mode 100644 index 0000000000..4c3fa15fd3 --- /dev/null +++ b/src/qml/qml/v8/qjsvalueiterator.cpp @@ -0,0 +1,157 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** 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 +** us via http://www.qt-project.org/. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qjsvalueiterator.h" +#include "qjsvalueiterator_p.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 qtjavascript + + + 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() and value() + functions return the name and value of the last item that was + jumped over. + + 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() +*/ + +/*! + 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() +*/ +bool QJSValueIterator::hasNext() const +{ + Q_D(const QJSValueIterator); + QScriptIsolate api(d->engine()); + return d->hasNext(); +} + +/*! + Advances the iterator by one position. + 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. + + Calling this function on an iterator located at the back of the + container leads to undefined results. + + \sa hasNext(), 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(). + + \sa value() +*/ +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(). + + \sa 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/qml/qml/v8/qjsvalueiterator.h b/src/qml/qml/v8/qjsvalueiterator.h new file mode 100644 index 0000000000..c47f07d43b --- /dev/null +++ b/src/qml/qml/v8/qjsvalueiterator.h @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** 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 +** us via http://www.qt-project.org/. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSCRIPTVALUEITERATOR_H +#define QSCRIPTVALUEITERATOR_H + +#include <QtQml/qjsvalue.h> +#include <QtCore/qscopedpointer.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + + +class QString; + +class QJSValueIteratorPrivate; +class Q_QML_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<QJSValueIteratorPrivate> d_ptr; + + Q_DECLARE_PRIVATE(QJSValueIterator) + Q_DISABLE_COPY(QJSValueIterator) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QSCRIPTVALUEITERATOR_H diff --git a/src/qml/qml/v8/qjsvalueiterator_impl_p.h b/src/qml/qml/v8/qjsvalueiterator_impl_p.h new file mode 100644 index 0000000000..131296ecac --- /dev/null +++ b/src/qml/qml/v8/qjsvalueiterator_impl_p.h @@ -0,0 +1,121 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml 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 +** us via http://www.qt-project.org/. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QJSVALUEITERATOR_IMPL_P_H +#define QJSVALUEITERATOR_IMPL_P_H + +#include "qjsvalueiterator_p.h" +#include <private/qv8engine_p.h> +#include "qjsconverter_p.h" + +inline QJSValueIteratorPrivate::QJSValueIteratorPrivate(const QJSValuePrivate* value) + : m_object(const_cast<QJSValuePrivate*>(value)) + , m_index(0) + , m_count(0) +{ + Q_ASSERT(value); + QV8Engine *engine = m_object->engine(); + if (!m_object->isObject()) + m_object = 0; + else { + QScriptIsolate api(engine, QScriptIsolate::NotNullEngine); + v8::HandleScope scope; + + v8::Handle<v8::Value> tmp = *value; + v8::Handle<v8::Object> obj = v8::Handle<v8::Object>::Cast(tmp); + v8::Local<v8::Array> names; + + // FIXME we need newer V8! + //names = obj->GetOwnPropertyNames(); + names = engine->getOwnPropertyNames(obj); + m_names = v8::Persistent<v8::Array>::New(names); + m_count = names->Length(); + + engine->registerValueIterator(this); + } +} + +inline QJSValueIteratorPrivate::~QJSValueIteratorPrivate() +{ + if (isValid()) { + engine()->unregisterValueIterator(this); + m_names.Dispose(); + } +} + +inline void QJSValueIteratorPrivate::invalidate() +{ + m_names.Dispose(); + m_object.reset(); + m_index = 0; + m_count = 0; +} + +inline bool QJSValueIteratorPrivate::hasNext() const +{ + return isValid() ? m_index < m_count : false; +} + +inline bool QJSValueIteratorPrivate::next() +{ + if (hasNext()) { + ++m_index; + return true; + } + return false; +} + +inline QString QJSValueIteratorPrivate::name() const +{ + if (!isValid()) + return QString(); + + v8::HandleScope handleScope; + return QJSConverter::toString(m_names->Get(m_index - 1)->ToString()); +} + +inline QScriptPassPointer<QJSValuePrivate> QJSValueIteratorPrivate::value() const +{ + if (!isValid()) + return new QJSValuePrivate(); + + v8::HandleScope handleScope; + return m_object->property(m_names->Get(m_index - 1)->ToString()); +} + +inline bool QJSValueIteratorPrivate::isValid() const +{ + bool result = m_object ? !m_object->isUndefined() : 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; +} + +#endif // QJSVALUEITERATOR_IMPL_P_H diff --git a/src/qml/qml/v8/qjsvalueiterator_p.h b/src/qml/qml/v8/qjsvalueiterator_p.h new file mode 100644 index 0000000000..2a5bcdec22 --- /dev/null +++ b/src/qml/qml/v8/qjsvalueiterator_p.h @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml 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 +** us via http://www.qt-project.org/. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QJSVALUEITERATOR_P_H +#define QJSVALUEITERATOR_P_H + +#include <private/qintrusivelist_p.h> +#include "qjsvalue_p.h" + +#include <private/qv8_p.h> + +QT_BEGIN_NAMESPACE + +class QV8Engine; + +class QJSValueIteratorPrivate +{ +public: + inline QJSValueIteratorPrivate(const QJSValuePrivate* value); + inline ~QJSValueIteratorPrivate(); + + inline bool hasNext() const; + inline bool next(); + + inline QString name() const; + + inline QScriptPassPointer<QJSValuePrivate> value() const; + + inline bool isValid() const; + inline QV8Engine* engine() const; + + inline void invalidate(); +private: + Q_DISABLE_COPY(QJSValueIteratorPrivate) + + QIntrusiveListNode m_node; + QScriptSharedDataPointer<QJSValuePrivate> m_object; + v8::Persistent<v8::Array> m_names; + uint32_t m_index; + uint32_t m_count; + + friend class QV8Engine; +}; + + +QT_END_NAMESPACE + +#endif // QJSVALUEITERATOR_P_H diff --git a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp new file mode 100644 index 0000000000..5284832ae1 --- /dev/null +++ b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp @@ -0,0 +1,1320 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqmlbuiltinfunctions_p.h" + +#include <QtQml/qqmlcomponent.h> +#include <private/qqmlengine_p.h> +#include <private/qqmlcomponent_p.h> +#include <private/qqmlstringconverters_p.h> +#include <private/qqmllocale_p.h> +#include <private/qv8engine_p.h> +#include <private/qjsconverter_impl_p.h> + +#include <private/qv8profilerservice_p.h> +#include <private/qqmlprofilerservice_p.h> + +#include <QtCore/qstring.h> +#include <QtCore/qdatetime.h> +#include <QtCore/qcryptographichash.h> +#include <QtCore/qrect.h> +#include <QtCore/qsize.h> +#include <QtCore/qpoint.h> +#include <QtCore/qurl.h> +#include <QtCore/qfile.h> +#include <QtCore/qcoreapplication.h> + +#include <QtGui/qcolor.h> +#include <QtGui/qvector3d.h> +#include <QtGui/qvector4d.h> +#include <QtGui/qdesktopservices.h> +#include <QtGui/qfontdatabase.h> + +QT_BEGIN_NAMESPACE + +namespace QQmlBuiltinFunctions { + +enum ConsoleLogTypes { + Log, + Warn, + Error +}; + +static void jsContext(v8::Handle<v8::Value> *file, int *line, v8::Handle<v8::Value> *function) { + v8::Local<v8::StackTrace> stackTrace = v8::StackTrace::CurrentStackTrace(1); + if (stackTrace->GetFrameCount()) { + v8::Local<v8::StackFrame> frame = stackTrace->GetFrame(0); + *file = frame->GetScriptName(); + *line = frame->GetLineNumber(); + *function = frame->GetFunctionName(); + } +} + +static QString jsStack() { + QStringList stackFrames; + + //The v8 default is currently 10 stack frames. + v8::Handle<v8::StackTrace> stackTrace = + v8::StackTrace::CurrentStackTrace(10, v8::StackTrace::kOverview); + int stackCount = stackTrace->GetFrameCount(); + + for (int i = 0; i < stackCount; i++) { + v8::Local<v8::StackFrame> frame = stackTrace->GetFrame(i); + v8::Handle<v8::String> function(frame->GetFunctionName()); + v8::Handle<v8::String> script(frame->GetScriptName()); + int lineNumber = frame->GetLineNumber(); + int columnNumber = frame->GetColumn(); + + QString stackFrame = + QString::fromLatin1("%1 (%2:%3:%4)").arg(QJSConverter::toString(function), + QJSConverter::toString(script), + QString::number(lineNumber), + QString::number(columnNumber)); + stackFrames.append(stackFrame); + } + return stackFrames.join(QLatin1String("\n")); +} + +v8::Handle<v8::Value> console(ConsoleLogTypes logType, const v8::Arguments &args, + bool printStack = false) +{ + v8::HandleScope handleScope; + + QString result; + for (int i = 0; i < args.Length(); ++i) { + if (i != 0) + result.append(QLatin1Char(' ')); + + v8::Local<v8::Value> value = args[i]; + //Check for Object Type + if (value->IsObject() && !value->IsFunction() + && !value->IsArray() && !value->IsDate() + && !value->IsRegExp()) { + result.append(QLatin1String("Object")); + } else { + v8::Local<v8::String> jsstr = value->ToString(); + QString tmp = V8ENGINE()->toString(jsstr); + if (value->IsArray()) + result.append(QString::fromLatin1("[%1]").arg(tmp)); + else + result.append(tmp); + } + } + + if (printStack) { + result.append(QLatin1String("\n")); + result.append(jsStack()); + } + + v8::Handle<v8::Value> fileHandle; + v8::Handle<v8::Value> functionHandle; + int line; + + jsContext(&fileHandle, &line, &functionHandle); + + switch (logType) { + case Log: + QMessageLogger(*v8::String::AsciiValue(fileHandle), line, + *v8::String::AsciiValue(functionHandle)).debug("%s", qPrintable(result)); + break; + case Warn: + QMessageLogger(*v8::String::AsciiValue(fileHandle), line, + *v8::String::AsciiValue(functionHandle)).warning("%s", qPrintable(result)); + break; + case Error: + QMessageLogger(*v8::String::AsciiValue(fileHandle), line, + *v8::String::AsciiValue(functionHandle)).critical("%s", qPrintable(result)); + break; + default: + break; + } + + return v8::Undefined(); +} + +v8::Handle<v8::Value> gc(const v8::Arguments &args) +{ + Q_UNUSED(args); + QV8Engine::gc(); + return v8::Undefined(); +} + +v8::Handle<v8::Value> consoleError(const v8::Arguments &args) +{ + return console(Error, args); +} + +v8::Handle<v8::Value> consoleLog(const v8::Arguments &args) +{ + //console.log + //console.debug + //console.info + //print + return console(Log, args); +} + +v8::Handle<v8::Value> consoleProfile(const v8::Arguments &args) +{ + //DeclarativeDebugTrace cannot handle nested profiling + //although v8 can handle several profiling at once, + //we do not allow that. Hence, we pass an empty(default) title + Q_UNUSED(args); + QString title; + + + + v8::Handle<v8::Value> file; + v8::Handle<v8::Value> function; + int line; + jsContext(&file, &line, &function); + + if (QQmlProfilerService::startProfiling()) { + QV8ProfilerService::instance()->startProfiling(title); + + QMessageLogger(*v8::String::AsciiValue(file), line, + *v8::String::AsciiValue(function)).debug("Profiling started."); + } else { + QMessageLogger(*v8::String::AsciiValue(file), line, + *v8::String::AsciiValue(function)).warning( + "Profiling is already in progress. First, end current profiling session."); + } + + return v8::Undefined(); +} + +v8::Handle<v8::Value> consoleProfileEnd(const v8::Arguments &args) +{ + //DeclarativeDebugTrace cannot handle nested profiling + //although v8 can handle several profiling at once, + //we do not allow that. Hence, we pass an empty(default) title + Q_UNUSED(args); + QString title; + + v8::Handle<v8::Value> file; + v8::Handle<v8::Value> function; + int line; + jsContext(&file, &line, &function); + + if (QQmlProfilerService::stopProfiling()) { + QV8ProfilerService *profiler = QV8ProfilerService::instance(); + profiler->stopProfiling(title); + QQmlProfilerService::sendProfilingData(); + profiler->sendProfilingData(); + + QMessageLogger(*v8::String::AsciiValue(file), line, + *v8::String::AsciiValue(function)).debug("Profiling ended."); + } else { + QMessageLogger(*v8::String::AsciiValue(file), line, + *v8::String::AsciiValue(function)).warning("Profiling was not started."); + } + + return v8::Undefined(); +} + +v8::Handle<v8::Value> consoleTime(const v8::Arguments &args) +{ + if (args.Length() != 1) + V8THROW_ERROR("console.time(): Invalid arguments"); + QString name = V8ENGINE()->toString(args[0]); + V8ENGINE()->startTimer(name); + return v8::Undefined(); +} + +v8::Handle<v8::Value> consoleTimeEnd(const v8::Arguments &args) +{ + if (args.Length() != 1) + V8THROW_ERROR("console.time(): Invalid arguments"); + QString name = V8ENGINE()->toString(args[0]); + bool wasRunning; + qint64 elapsed = V8ENGINE()->stopTimer(name, &wasRunning); + if (wasRunning) { + qDebug("%s: %llims", qPrintable(name), elapsed); + } + return v8::Undefined(); +} + +v8::Handle<v8::Value> consoleCount(const v8::Arguments &args) +{ + // first argument: name to print. Ignore any additional arguments + QString name; + if (args.Length() > 0) + name = V8ENGINE()->toString(args[0]); + + v8::Handle<v8::StackTrace> stackTrace = + v8::StackTrace::CurrentStackTrace(1, v8::StackTrace::kOverview); + + if (stackTrace->GetFrameCount()) { + v8::Local<v8::StackFrame> frame = stackTrace->GetFrame(0); + + QString scriptName = V8ENGINE()->toString(frame->GetScriptName()); + QString functionName = V8ENGINE()->toString(frame->GetFunctionName()); + int line = frame->GetLineNumber(); + int column = frame->GetColumn(); + + int value = V8ENGINE()->consoleCountHelper(scriptName, line, column); + QString message = name + QLatin1String(": ") + QString::number(value); + + QMessageLogger(qPrintable(scriptName), line, + qPrintable(functionName)).debug("%s", qPrintable(message)); + } + + return v8::Undefined(); +} + +v8::Handle<v8::Value> consoleTrace(const v8::Arguments &args) +{ + if (args.Length() != 0) + V8THROW_ERROR("console.trace(): Invalid arguments"); + + QString stack = jsStack(); + + v8::Handle<v8::Value> file; + v8::Handle<v8::Value> function; + int line; + jsContext(&file, &line, &function); + + QMessageLogger(*v8::String::AsciiValue(file), line, *v8::String::AsciiValue(function)).debug( + "%s", qPrintable(stack)); + return v8::Undefined(); +} + +v8::Handle<v8::Value> consoleWarn(const v8::Arguments &args) +{ + return console(Warn, args); +} + +v8::Handle<v8::Value> consoleAssert(const v8::Arguments &args) +{ + if (args.Length() == 0) + V8THROW_ERROR("console.assert(): Missing argument"); + + if (!args[0]->ToBoolean()->Value()) { + QString message; + for (int i = 1; i < args.Length(); ++i) { + if (i != 1) + message.append(QLatin1Char(' ')); + + v8::Local<v8::Value> value = args[i]; + message.append(V8ENGINE()->toString(value->ToString())); + } + + QString stack = jsStack(); + + v8::Handle<v8::Value> file; + v8::Handle<v8::Value> function; + int line; + jsContext(&file, &line, &function); + + QMessageLogger(*v8::String::AsciiValue(file), line, *v8::String::AsciiValue(function)).critical( + "%s\n%s", qPrintable(message), qPrintable(stack)); + + } + return v8::Undefined(); +} + +v8::Handle<v8::Value> consoleException(const v8::Arguments &args) +{ + if (args.Length() == 0) + V8THROW_ERROR("console.exception(): Missing argument"); + + console(Error, args, true); + + return v8::Undefined(); +} + +v8::Handle<v8::Value> stringArg(const v8::Arguments &args) +{ + QString value = V8ENGINE()->toString(args.This()->ToString()); + if (args.Length() != 1) + V8THROW_ERROR("String.arg(): Invalid arguments"); + + v8::Handle<v8::Value> arg = args[0]; + if (arg->IsUint32()) + return V8ENGINE()->toString(value.arg(arg->Uint32Value())); + else if (arg->IsInt32()) + return V8ENGINE()->toString(value.arg(arg->Int32Value())); + else if (arg->IsNumber()) + return V8ENGINE()->toString(value.arg(arg->NumberValue())); + else if (arg->IsBoolean()) + return V8ENGINE()->toString(value.arg(arg->BooleanValue())); + + return V8ENGINE()->toString(value.arg(V8ENGINE()->toString(arg))); +} + +/*! +\qmlmethod bool Qt::isQtObject(object) +Returns true if \c object is a valid reference to a Qt or QML object, otherwise false. +*/ +v8::Handle<v8::Value> isQtObject(const v8::Arguments &args) +{ + if (args.Length() == 0) + return v8::Boolean::New(false); + + return v8::Boolean::New(0 != V8ENGINE()->toQObject(args[0])); +} + +/*! +\qmlmethod color Qt::rgba(real red, real green, real blue, real alpha) + +Returns a color with the specified \c red, \c green, \c blue and \c alpha components. +All components should be in the range 0-1 inclusive. +*/ +v8::Handle<v8::Value> rgba(const v8::Arguments &args) +{ + int argCount = args.Length(); + if (argCount < 3 || argCount > 4) + V8THROW_ERROR("Qt.rgba(): Invalid arguments"); + + double r = args[0]->NumberValue(); + double g = args[1]->NumberValue(); + double b = args[2]->NumberValue(); + double a = (argCount == 4) ? args[3]->NumberValue() : 1; + + if (r < 0.0) r=0.0; + if (r > 1.0) r=1.0; + if (g < 0.0) g=0.0; + if (g > 1.0) g=1.0; + if (b < 0.0) b=0.0; + if (b > 1.0) b=1.0; + if (a < 0.0) a=0.0; + if (a > 1.0) a=1.0; + + return V8ENGINE()->fromVariant(QVariant::fromValue(QColor::fromRgbF(r, g, b, a))); +} + +/*! +\qmlmethod color Qt::hsla(real hue, real saturation, real lightness, real alpha) + +Returns a color with the specified \c hue, \c saturation, \c lightness and \c alpha components. +All components should be in the range 0-1 inclusive. +*/ +v8::Handle<v8::Value> hsla(const v8::Arguments &args) +{ + int argCount = args.Length(); + if (argCount < 3 || argCount > 4) + V8THROW_ERROR("Qt.hsla(): Invalid arguments"); + + double h = args[0]->NumberValue(); + double s = args[1]->NumberValue(); + double l = args[2]->NumberValue(); + double a = (argCount == 4) ? args[3]->NumberValue() : 1; + + if (h < 0.0) h=0.0; + if (h > 1.0) h=1.0; + if (s < 0.0) s=0.0; + if (s > 1.0) s=1.0; + if (l < 0.0) l=0.0; + if (l > 1.0) l=1.0; + if (a < 0.0) a=0.0; + if (a > 1.0) a=1.0; + + return V8ENGINE()->fromVariant(QVariant::fromValue(QColor::fromHslF(h, s, l, a))); +} + +/*! +\qmlmethod rect Qt::rect(int x, int y, int width, int height) + +Returns a \c rect with the top-left corner at \c x, \c y and the specified \c width and \c height. + +The returned object has \c x, \c y, \c width and \c height attributes with the given values. +*/ +v8::Handle<v8::Value> rect(const v8::Arguments &args) +{ + if (args.Length() != 4) + V8THROW_ERROR("Qt.rect(): Invalid arguments"); + + double x = args[0]->NumberValue(); + double y = args[1]->NumberValue(); + double w = args[2]->NumberValue(); + double h = args[3]->NumberValue(); + + return V8ENGINE()->fromVariant(QVariant::fromValue(QRectF(x, y, w, h))); +} + +/*! +\qmlmethod point Qt::point(int x, int y) +Returns a Point with the specified \c x and \c y coordinates. +*/ +v8::Handle<v8::Value> point(const v8::Arguments &args) +{ + if (args.Length() != 2) + V8THROW_ERROR("Qt.point(): Invalid arguments"); + + double x = args[0]->ToNumber()->Value(); + double y = args[1]->ToNumber()->Value(); + + return V8ENGINE()->fromVariant(QVariant::fromValue(QPointF(x, y))); +} + +/*! +\qmlmethod Qt::size(int width, int height) +Returns a Size with the specified \c width and \c height. +*/ +v8::Handle<v8::Value> size(const v8::Arguments &args) +{ + if (args.Length() != 2) + V8THROW_ERROR("Qt.size(): Invalid arguments"); + + double w = args[0]->ToNumber()->Value(); + double h = args[1]->ToNumber()->Value(); + + return V8ENGINE()->fromVariant(QVariant::fromValue(QSizeF(w, h))); +} + +/*! +\qmlmethod Qt::vector3d(real x, real y, real z) +Returns a Vector3D with the specified \c x, \c y and \c z. +*/ +v8::Handle<v8::Value> vector3d(const v8::Arguments &args) +{ + if (args.Length() != 3) + V8THROW_ERROR("Qt.vector(): Invalid arguments"); + + double x = args[0]->ToNumber()->Value(); + double y = args[1]->ToNumber()->Value(); + double z = args[2]->ToNumber()->Value(); + + return V8ENGINE()->fromVariant(QVariant::fromValue(QVector3D(x, y, z))); +} + +/*! +\qmlmethod Qt::vector4d(real x, real y, real z, real w) +Returns a Vector4D with the specified \c x, \c y, \c z and \c w. +*/ +v8::Handle<v8::Value> vector4d(const v8::Arguments &args) +{ + if (args.Length() != 4) + V8THROW_ERROR("Qt.vector4d(): Invalid arguments"); + + double x = args[0]->NumberValue(); + double y = args[1]->NumberValue(); + double z = args[2]->NumberValue(); + double w = args[3]->NumberValue(); + + return V8ENGINE()->fromVariant(QVariant::fromValue(QVector4D(x, y, z, w))); +} + +/*! +\qmlmethod color Qt::lighter(color baseColor, real factor) +Returns a color lighter than \c baseColor by the \c factor provided. + +If the factor is greater than 1.0, this functions returns a lighter color. +Setting factor to 1.5 returns a color that is 50% brighter. If the factor is less than 1.0, +the return color is darker, but we recommend using the Qt.darker() function for this purpose. +If the factor is 0 or negative, the return value is unspecified. + +The function converts the current RGB color to HSV, multiplies the value (V) component +by factor and converts the color back to RGB. + +If \c factor is not supplied, returns a color 50% lighter than \c baseColor (factor 1.5). +*/ +v8::Handle<v8::Value> lighter(const v8::Arguments &args) +{ + if (args.Length() != 1 && args.Length() != 2) + V8THROW_ERROR("Qt.lighter(): Invalid arguments"); + + QColor color; + QVariant v = V8ENGINE()->toVariant(args[0], -1); + if (v.userType() == QVariant::Color) { + color = v.value<QColor>(); + } else if (v.userType() == QVariant::String) { + bool ok = false; + color = QQmlStringConverters::colorFromString(v.toString(), &ok); + if (!ok) { + return v8::Null(); + } + } else { + return v8::Null(); + } + + qreal factor = 1.5; + if (args.Length() == 2) + factor = args[1]->ToNumber()->Value(); + + color = color.lighter(int(qRound(factor*100.))); + return V8ENGINE()->fromVariant(QVariant::fromValue(color)); +} + +/*! +\qmlmethod color Qt::darker(color baseColor, real factor) +Returns a color darker than \c baseColor by the \c factor provided. + +If the factor is greater than 1.0, this function returns a darker color. +Setting factor to 3.0 returns a color that has one-third the brightness. +If the factor is less than 1.0, the return color is lighter, but we recommend using +the Qt.lighter() function for this purpose. If the factor is 0 or negative, the return +value is unspecified. + +The function converts the current RGB color to HSV, divides the value (V) component +by factor and converts the color back to RGB. + +If \c factor is not supplied, returns a color 50% darker than \c baseColor (factor 2.0). +*/ +v8::Handle<v8::Value> darker(const v8::Arguments &args) +{ + if (args.Length() != 1 && args.Length() != 2) + V8THROW_ERROR("Qt.darker(): Invalid arguments"); + + QColor color; + QVariant v = V8ENGINE()->toVariant(args[0], -1); + if (v.userType() == QVariant::Color) { + color = v.value<QColor>(); + } else if (v.userType() == QVariant::String) { + bool ok = false; + color = QQmlStringConverters::colorFromString(v.toString(), &ok); + if (!ok) { + return v8::Null(); + } + } else { + return v8::Null(); + } + + qreal factor = 2.0; + if (args.Length() == 2) + factor = args[1]->ToNumber()->Value(); + + color = color.darker(int(qRound(factor*100.))); + return V8ENGINE()->fromVariant(QVariant::fromValue(color)); +} + +/*! + \qmlmethod color Qt::tint(color baseColor, color tintColor) + This function allows tinting one color with another. + + The tint color should usually be mostly transparent, or you will not be + able to see the underlying color. The below example provides a slight red + tint by having the tint color be pure red which is only 1/16th opaque. + + \qml + Item { + Rectangle { + x: 0; width: 80; height: 80 + color: "lightsteelblue" + } + Rectangle { + x: 100; width: 80; height: 80 + color: Qt.tint("lightsteelblue", "#10FF0000") + } + } + \endqml + \image declarative-rect_tint.png + + Tint is most useful when a subtle change is intended to be conveyed due to some event; you can then use tinting to more effectively tune the visible color. +*/ +v8::Handle<v8::Value> tint(const v8::Arguments &args) +{ + if (args.Length() != 2) + V8THROW_ERROR("Qt.tint(): Invalid arguments"); + + // base color + QColor color; + QVariant v = V8ENGINE()->toVariant(args[0], -1); + if (v.userType() == QVariant::Color) { + color = v.value<QColor>(); + } else if (v.userType() == QVariant::String) { + bool ok = false; + color = QQmlStringConverters::colorFromString(v.toString(), &ok); + if (!ok) { + return v8::Null(); + } + } else { + return v8::Null(); + } + + // tint color + QColor tintColor; + v = V8ENGINE()->toVariant(args[1], -1); + if (v.userType() == QVariant::Color) { + tintColor = v.value<QColor>(); + } else if (v.userType() == QVariant::String) { + bool ok = false; + tintColor = QQmlStringConverters::colorFromString(v.toString(), &ok); + if (!ok) { + return v8::Null(); + } + } else { + return v8::Null(); + } + + // tint the base color and return the final color + QColor finalColor; + int a = tintColor.alpha(); + if (a == 0xFF) + finalColor = tintColor; + else if (a == 0x00) + finalColor = color; + else { + qreal a = tintColor.alphaF(); + qreal inv_a = 1.0 - a; + + finalColor.setRgbF(tintColor.redF() * a + color.redF() * inv_a, + tintColor.greenF() * a + color.greenF() * inv_a, + tintColor.blueF() * a + color.blueF() * inv_a, + a + inv_a * color.alphaF()); + } + + return V8ENGINE()->fromVariant(QVariant::fromValue(finalColor)); +} + +/*! +\qmlmethod string Qt::formatDate(datetime date, variant format) + +Returns a string representation of \c date, optionally formatted according +to \c format. + +The \a date parameter may be a JavaScript \c Date object, a \l{date}{date} +property, a QDate, or QDateTime value. The \a format parameter may be any of +the possible format values as described for +\l{QML:Qt::formatDateTime()}{Qt.formatDateTime()}. + +If \a format is not specified, \a date is formatted using +\l {Qt::DefaultLocaleShortDate}{Qt.DefaultLocaleShortDate}. + +\sa Locale +*/ +v8::Handle<v8::Value> formatDate(const v8::Arguments &args) +{ + if (args.Length() < 1 || args.Length() > 2) + V8THROW_ERROR("Qt.formatDate(): Invalid arguments"); + + Qt::DateFormat enumFormat = Qt::DefaultLocaleShortDate; + QDate date = V8ENGINE()->toVariant(args[0], -1).toDateTime().date(); + QString formattedDate; + if (args.Length() == 2) { + if (args[1]->IsString()) { + QString format = V8ENGINE()->toVariant(args[1], -1).toString(); + formattedDate = date.toString(format); + } else if (args[1]->IsNumber()) { + quint32 intFormat = args[1]->ToNumber()->Value(); + Qt::DateFormat format = Qt::DateFormat(intFormat); + formattedDate = date.toString(format); + } else { + V8THROW_ERROR("Qt.formatDate(): Invalid date format"); + } + } else { + formattedDate = date.toString(enumFormat); + } + + return V8ENGINE()->fromVariant(QVariant::fromValue(formattedDate)); +} + +/*! +\qmlmethod string Qt::formatTime(datetime time, variant format) + +Returns a string representation of \c time, optionally formatted according to +\c format. + +The \a time parameter may be a JavaScript \c Date object, a QTime, or QDateTime +value. The \a format parameter may be any of the possible format values as +described for \l{QML:Qt::formatDateTime()}{Qt.formatDateTime()}. + +If \a format is not specified, \a time is formatted using +\l {Qt::DefaultLocaleShortDate}{Qt.DefaultLocaleShortDate}. + +\sa Locale +*/ +v8::Handle<v8::Value> formatTime(const v8::Arguments &args) +{ + if (args.Length() < 1 || args.Length() > 2) + V8THROW_ERROR("Qt.formatTime(): Invalid arguments"); + + QVariant argVariant = V8ENGINE()->toVariant(args[0], -1); + QTime time; + if (args[0]->IsDate() || (argVariant.type() == QVariant::String)) + time = argVariant.toDateTime().time(); + else // if (argVariant.type() == QVariant::Time), or invalid. + time = argVariant.toTime(); + + Qt::DateFormat enumFormat = Qt::DefaultLocaleShortDate; + QString formattedTime; + if (args.Length() == 2) { + if (args[1]->IsString()) { + QString format = V8ENGINE()->toVariant(args[1], -1).toString(); + formattedTime = time.toString(format); + } else if (args[1]->IsNumber()) { + quint32 intFormat = args[1]->ToNumber()->Value(); + Qt::DateFormat format = Qt::DateFormat(intFormat); + formattedTime = time.toString(format); + } else { + V8THROW_ERROR("Qt.formatTime(): Invalid time format"); + } + } else { + formattedTime = time.toString(enumFormat); + } + + return V8ENGINE()->fromVariant(QVariant::fromValue(formattedTime)); +} + +/*! +\qmlmethod string Qt::formatDateTime(datetime dateTime, variant format) + +Returns a string representation of \c datetime, optionally formatted according to +\c format. + +The \a date parameter may be a JavaScript \c Date object, a \l{date}{date} +property, a QDate, QTime, or QDateTime value. + +If \a format is not provided, \a dateTime is formatted using +\l {Qt::DefaultLocaleShortDate}{Qt.DefaultLocaleShortDate}. Otherwise, +\a format should be either: + +\list +\o One of the Qt::DateFormat enumeration values, such as + \c Qt.DefaultLocaleShortDate or \c Qt.ISODate +\o A string that specifies the format of the returned string, as detailed below. +\endlist + +If \a format specifies a format string, it should use the following expressions +to specify the date: + + \table + \header \i Expression \i Output + \row \i d \i the day as number without a leading zero (1 to 31) + \row \i dd \i the day as number with a leading zero (01 to 31) + \row \i ddd + \i the abbreviated localized day name (e.g. 'Mon' to 'Sun'). + Uses QDate::shortDayName(). + \row \i dddd + \i the long localized day name (e.g. 'Monday' to 'Qt::Sunday'). + Uses QDate::longDayName(). + \row \i M \i the month as number without a leading zero (1-12) + \row \i MM \i the month as number with a leading zero (01-12) + \row \i MMM + \i the abbreviated localized month name (e.g. 'Jan' to 'Dec'). + Uses QDate::shortMonthName(). + \row \i MMMM + \i the long localized month name (e.g. 'January' to 'December'). + Uses QDate::longMonthName(). + \row \i yy \i the year as two digit number (00-99) + \row \i yyyy \i the year as four digit number + \endtable + +In addition the following expressions can be used to specify the time: + + \table + \header \i Expression \i Output + \row \i h + \i the hour without a leading zero (0 to 23 or 1 to 12 if AM/PM display) + \row \i hh + \i the hour with a leading zero (00 to 23 or 01 to 12 if AM/PM display) + \row \i m \i the minute without a leading zero (0 to 59) + \row \i mm \i the minute with a leading zero (00 to 59) + \row \i s \i the second without a leading zero (0 to 59) + \row \i ss \i the second with a leading zero (00 to 59) + \row \i z \i the milliseconds without leading zeroes (0 to 999) + \row \i zzz \i the milliseconds with leading zeroes (000 to 999) + \row \i AP + \i use AM/PM display. \e AP will be replaced by either "AM" or "PM". + \row \i ap + \i use am/pm display. \e ap will be replaced by either "am" or "pm". + \endtable + + All other input characters will be ignored. Any sequence of characters that + are enclosed in single quotes will be treated as text and not be used as an + expression. Two consecutive single quotes ("''") are replaced by a single quote + in the output. + +For example, if the following date/time value was specified: + + \code + // 21 May 2001 14:13:09 + var dateTime = new Date(2001, 5, 21, 14, 13, 09) + \endcode + +This \a dateTime value could be passed to \c Qt.formatDateTime(), +\l {QML:Qt::formatDate()}{Qt.formatDate()} or \l {QML:Qt::formatTime()}{Qt.formatTime()} +with the \a format values below to produce the following results: + + \table + \header \i Format \i Result + \row \i "dd.MM.yyyy" \i 21.05.2001 + \row \i "ddd MMMM d yy" \i Tue May 21 01 + \row \i "hh:mm:ss.zzz" \i 14:13:09.042 + \row \i "h:m:s ap" \i 2:13:9 pm + \endtable + + \sa Locale +*/ +v8::Handle<v8::Value> formatDateTime(const v8::Arguments &args) +{ + if (args.Length() < 1 || args.Length() > 2) + V8THROW_ERROR("Qt.formatDateTime(): Invalid arguments"); + + Qt::DateFormat enumFormat = Qt::DefaultLocaleShortDate; + QDateTime dt = V8ENGINE()->toVariant(args[0], -1).toDateTime(); + QString formattedDt; + if (args.Length() == 2) { + if (args[1]->IsString()) { + QString format = V8ENGINE()->toVariant(args[1], -1).toString(); + formattedDt = dt.toString(format); + } else if (args[1]->IsNumber()) { + quint32 intFormat = args[1]->ToNumber()->Value(); + Qt::DateFormat format = Qt::DateFormat(intFormat); + formattedDt = dt.toString(format); + } else { + V8THROW_ERROR("Qt.formatDateTime(): Invalid datetime format"); + } + } else { + formattedDt = dt.toString(enumFormat); + } + + return V8ENGINE()->fromVariant(QVariant::fromValue(formattedDt)); +} + +/*! +\qmlmethod bool Qt::openUrlExternally(url target) +Attempts to open the specified \c target url in an external application, based on the user's desktop preferences. Returns true if it succeeds, and false otherwise. +*/ +v8::Handle<v8::Value> openUrlExternally(const v8::Arguments &args) +{ + if (args.Length() != 1) + return V8ENGINE()->fromVariant(false); + + bool ret = false; +#ifndef QT_NO_DESKTOPSERVICES + ret = QDesktopServices::openUrl(V8ENGINE()->toVariant(resolvedUrl(args), -1).toUrl()); +#endif + return V8ENGINE()->fromVariant(ret); +} + +/*! + \qmlmethod url Qt::resolvedUrl(url url) + Returns \a url resolved relative to the URL of the caller. +*/ +v8::Handle<v8::Value> resolvedUrl(const v8::Arguments &args) +{ + QUrl url = V8ENGINE()->toVariant(args[0], -1).toUrl(); + QQmlEngine *e = V8ENGINE()->engine(); + QQmlEnginePrivate *p = 0; + if (e) p = QQmlEnginePrivate::get(e); + if (p) { + QQmlContextData *ctxt = V8ENGINE()->callingContext(); + if (ctxt) + return V8ENGINE()->toString(ctxt->resolvedUrl(url).toString()); + else + return V8ENGINE()->toString(url.toString()); + } + + return V8ENGINE()->toString(e->baseUrl().resolved(url).toString()); +} + +/*! +\qmlmethod list<string> Qt::fontFamilies() +Returns a list of the font families available to the application. +*/ +v8::Handle<v8::Value> fontFamilies(const v8::Arguments &args) +{ + if (args.Length() != 0) + V8THROW_ERROR("Qt.fontFamilies(): Invalid arguments"); + + QFontDatabase database; + return V8ENGINE()->fromVariant(database.families()); +} + +/*! +\qmlmethod string Qt::md5(data) +Returns a hex string of the md5 hash of \c data. +*/ +v8::Handle<v8::Value> md5(const v8::Arguments &args) +{ + if (args.Length() != 1) + V8THROW_ERROR("Qt.md5(): Invalid arguments"); + + QByteArray data = V8ENGINE()->toString(args[0]->ToString()).toUtf8(); + QByteArray result = QCryptographicHash::hash(data, QCryptographicHash::Md5); + return V8ENGINE()->toString(QLatin1String(result.toHex())); +} + +/*! +\qmlmethod string Qt::btoa(data) +Binary to ASCII - this function returns a base64 encoding of \c data. +*/ +v8::Handle<v8::Value> btoa(const v8::Arguments &args) +{ + if (args.Length() != 1) + V8THROW_ERROR("Qt.btoa(): Invalid arguments"); + + QByteArray data = V8ENGINE()->toString(args[0]->ToString()).toUtf8(); + + return V8ENGINE()->toString(QLatin1String(data.toBase64())); +} + +/*! +\qmlmethod string Qt::atob(data) +ASCII to binary - this function returns a base64 decoding of \c data. +*/ +v8::Handle<v8::Value> atob(const v8::Arguments &args) +{ + if (args.Length() != 1) + V8THROW_ERROR("Qt.atob(): Invalid arguments"); + + QByteArray data = V8ENGINE()->toString(args[0]->ToString()).toUtf8(); + + return V8ENGINE()->toString(QLatin1String(QByteArray::fromBase64(data))); +} + +/*! +\qmlmethod Qt::quit() +This function causes the QQmlEngine::quit() signal to be emitted. +Within the \l {QML Viewer}, this causes the launcher application to exit; +to quit a C++ application when this method is called, connect the +QQmlEngine::quit() signal to the QCoreApplication::quit() slot. +*/ +v8::Handle<v8::Value> quit(const v8::Arguments &args) +{ + QQmlEnginePrivate::get(V8ENGINE()->engine())->sendQuit(); + return v8::Undefined(); +} + +/*! +\qmlmethod object Qt::createQmlObject(string qml, object parent, string filepath) + +Returns a new object created from the given \a string of QML which will have the specified \a parent, +or \c null if there was an error in creating the object. + +If \a filepath is specified, it will be used for error reporting for the created object. + +Example (where \c parentItem is the id of an existing QML item): + +\snippet doc/src/snippets/qml/createQmlObject.qml 0 + +In the case of an error, a QtScript Error object is thrown. This object has an additional property, +\c qmlErrors, which is an array of the errors encountered. +Each object in this array has the members \c lineNumber, \c columnNumber, \c fileName and \c message. +For example, if the above snippet had misspelled color as 'colro' then the array would contain an object like the following: +{ "lineNumber" : 1, "columnNumber" : 32, "fileName" : "dynamicSnippet1", "message" : "Cannot assign to non-existent property \"colro\""}. + +Note that this function returns immediately, and therefore may not work if +the \a qml string loads new components (that is, external QML files that have not yet been loaded). +If this is the case, consider using \l{QML:Qt::createComponent()}{Qt.createComponent()} instead. + +See \l {Dynamic Object Management in QML} for more information on using this function. +*/ +v8::Handle<v8::Value> createQmlObject(const v8::Arguments &args) +{ + if (args.Length() < 2 || args.Length() > 3) + V8THROW_ERROR("Qt.createQmlObject(): Invalid arguments"); + + struct Error { + static v8::Local<v8::Value> create(QV8Engine *engine, const QList<QQmlError> &errors) { + QString errorstr = QLatin1String("Qt.createQmlObject(): failed to create object: "); + + v8::Local<v8::Array> qmlerrors = v8::Array::New(errors.count()); + for (int ii = 0; ii < errors.count(); ++ii) { + const QQmlError &error = errors.at(ii); + errorstr += QLatin1String("\n ") + error.toString(); + v8::Local<v8::Object> qmlerror = v8::Object::New(); + qmlerror->Set(v8::String::New("lineNumber"), v8::Integer::New(error.line())); + qmlerror->Set(v8::String::New("columnNumber"), v8::Integer::New(error.column())); + qmlerror->Set(v8::String::New("fileName"), engine->toString(error.url().toString())); + qmlerror->Set(v8::String::New("message"), engine->toString(error.description())); + qmlerrors->Set(ii, qmlerror); + } + + v8::Local<v8::Value> error = v8::Exception::Error(engine->toString(errorstr)); + v8::Local<v8::Object> errorObject = error->ToObject(); + errorObject->Set(v8::String::New("qmlErrors"), qmlerrors); + return error; + } + }; + + QV8Engine *v8engine = V8ENGINE(); + QQmlEngine *engine = v8engine->engine(); + + QQmlContextData *context = v8engine->callingContext(); + QQmlContext *effectiveContext = 0; + if (context->isPragmaLibraryContext) + effectiveContext = engine->rootContext(); + else + effectiveContext = context->asQQmlContext(); + Q_ASSERT(context && effectiveContext); + + QString qml = v8engine->toString(args[0]->ToString()); + if (qml.isEmpty()) + return v8::Null(); + + QUrl url; + if (args.Length() > 2) + url = QUrl(v8engine->toString(args[2]->ToString())); + else + url = QUrl(QLatin1String("inline")); + + if (url.isValid() && url.isRelative()) + url = context->resolvedUrl(url); + + QObject *parentArg = v8engine->toQObject(args[1]); + if (!parentArg) + V8THROW_ERROR("Qt.createQmlObject(): Missing parent object"); + + QQmlComponent component(engine); + component.setData(qml.toUtf8(), url); + + if (component.isError()) { + v8::ThrowException(Error::create(v8engine, component.errors())); + return v8::Undefined(); + } + + if (!component.isReady()) + V8THROW_ERROR("Qt.createQmlObject(): Component is not ready"); + + QObject *obj = component.beginCreate(effectiveContext); + if (obj) + QQmlData::get(obj, true)->setImplicitDestructible(); + component.completeCreate(); + + if (component.isError()) { + v8::ThrowException(Error::create(v8engine, component.errors())); + return v8::Undefined(); + } + + Q_ASSERT(obj); + + obj->setParent(parentArg); + + QList<QQmlPrivate::AutoParentFunction> functions = QQmlMetaType::parentFunctions(); + for (int ii = 0; ii < functions.count(); ++ii) { + if (QQmlPrivate::Parented == functions.at(ii)(obj, parentArg)) + break; + } + + return v8engine->newQObject(obj); +} + +/*! +\qmlmethod object Qt::createComponent(url) + +Returns a \l Component object created using the QML file at the specified \a url, +or \c null if an empty string was given. + +The returned component's \l Component::status property indicates whether the +component was successfully created. If the status is \c Component.Error, +see \l Component::errorString() for an error description. + +Call \l {Component::createObject()}{Component.createObject()} on the returned +component to create an object instance of the component. + +For example: + +\snippet doc/src/snippets/qml/createComponent-simple.qml 0 + +See \l {Dynamic Object Management in QML} for more information on using this function. + +To create a QML object from an arbitrary string of QML (instead of a file), +use \l{QML:Qt::createQmlObject()}{Qt.createQmlObject()}. +*/ +v8::Handle<v8::Value> createComponent(const v8::Arguments &args) +{ + if (args.Length() != 1) + V8THROW_ERROR("Qt.createComponent(): Invalid arguments"); + + QV8Engine *v8engine = V8ENGINE(); + QQmlEngine *engine = v8engine->engine(); + + QQmlContextData *context = v8engine->callingContext(); + QQmlContextData *effectiveContext = context; + if (context->isPragmaLibraryContext) + effectiveContext = 0; + Q_ASSERT(context); + + QString arg = v8engine->toString(args[0]->ToString()); + if (arg.isEmpty()) + return v8::Null(); + + QUrl url = context->resolvedUrl(QUrl(arg)); + QQmlComponent *c = new QQmlComponent(engine, url, engine); + QQmlComponentPrivate::get(c)->creationContext = effectiveContext; + QQmlData::get(c, true)->setImplicitDestructible(); + return v8engine->newQObject(c); +} + +v8::Handle<v8::Value> qsTranslate(const v8::Arguments &args) +{ + if (args.Length() < 2) + V8THROW_ERROR("qsTranslate() requires at least two arguments"); + if (!args[0]->IsString()) + V8THROW_ERROR("qsTranslate(): first argument (context) must be a string"); + if (!args[1]->IsString()) + V8THROW_ERROR("qsTranslate(): second argument (text) must be a string"); + if ((args.Length() > 2) && !args[2]->IsString()) + V8THROW_ERROR("qsTranslate(): third argument (comment) must be a string"); + if ((args.Length() > 3) && !args[3]->IsString()) + V8THROW_ERROR("qsTranslate(): fourth argument (encoding) must be a string"); + + QV8Engine *v8engine = V8ENGINE(); + QString context = v8engine->toString(args[0]); + QString text = v8engine->toString(args[1]); + QString comment; + if (args.Length() > 2) comment = v8engine->toString(args[2]); + + QCoreApplication::Encoding encoding = QCoreApplication::UnicodeUTF8; + if (args.Length() > 3) { + QString encStr = v8engine->toString(args[3]); + if (encStr == QLatin1String("CodecForTr")) { + encoding = QCoreApplication::CodecForTr; + } else if (encStr == QLatin1String("UnicodeUTF8")) { + encoding = QCoreApplication::UnicodeUTF8; + } else { + QString msg = QString::fromLatin1("qsTranslate(): invalid encoding '%0'").arg(encStr); + V8THROW_ERROR((uint16_t *)msg.constData()); + } + } + + int n = -1; + if (args.Length() > 4) + n = args[4]->Int32Value(); + + QString result = QCoreApplication::translate(context.toUtf8().constData(), + text.toUtf8().constData(), + comment.toUtf8().constData(), + encoding, n); + + return v8engine->toString(result); +} + +v8::Handle<v8::Value> qsTranslateNoOp(const v8::Arguments &args) +{ + if (args.Length() < 2) + return v8::Undefined(); + return args[1]; +} + +v8::Handle<v8::Value> qsTr(const v8::Arguments &args) +{ + if (args.Length() < 1) + V8THROW_ERROR("qsTr() requires at least one argument"); + if (!args[0]->IsString()) + V8THROW_ERROR("qsTr(): first argument (text) must be a string"); + if ((args.Length() > 1) && !args[1]->IsString()) + V8THROW_ERROR("qsTr(): second argument (comment) must be a string"); + if ((args.Length() > 2) && !args[2]->IsNumber()) + V8THROW_ERROR("qsTr(): third argument (n) must be a number"); + + QV8Engine *v8engine = V8ENGINE(); + QQmlContextData *ctxt = v8engine->callingContext(); + + QString path = ctxt->url.toString(); + int lastSlash = path.lastIndexOf(QLatin1Char('/')); + QString context = (lastSlash > -1) ? path.mid(lastSlash + 1, path.length()-lastSlash-5) : QString(); + + QString text = v8engine->toString(args[0]); + QString comment; + if (args.Length() > 1) + comment = v8engine->toString(args[1]); + int n = -1; + if (args.Length() > 2) + n = args[2]->Int32Value(); + + QString result = QCoreApplication::translate(context.toUtf8().constData(), text.toUtf8().constData(), + comment.toUtf8().constData(), QCoreApplication::UnicodeUTF8, n); + + return v8engine->toString(result); +} + +v8::Handle<v8::Value> qsTrNoOp(const v8::Arguments &args) +{ + if (args.Length() < 1) + return v8::Undefined(); + return args[0]; +} + +v8::Handle<v8::Value> qsTrId(const v8::Arguments &args) +{ + if (args.Length() < 1) + V8THROW_ERROR("qsTrId() requires at least one argument"); + if (!args[0]->IsString()) + V8THROW_TYPE("qsTrId(): first argument (id) must be a string"); + if (args.Length() > 1 && !args[1]->IsNumber()) + V8THROW_TYPE("qsTrId(): second argument (n) must be a number"); + + int n = -1; + if (args.Length() > 1) + n = args[1]->Int32Value(); + + QV8Engine *v8engine = V8ENGINE(); + return v8engine->toString(qtTrId(v8engine->toString(args[0]).toUtf8().constData(), n)); +} + +v8::Handle<v8::Value> qsTrIdNoOp(const v8::Arguments &args) +{ + if (args.Length() < 1) + return v8::Undefined(); + return args[0]; +} + + +/*! + \qmlmethod Qt::locale(name) + + Returns a JS object representing the locale with the specified + name, which has the format "language[_territory][.codeset][@modifier]" + or "C", where: + + \list + \o language is a lowercase, two-letter, ISO 639 language code, + \o territory is an uppercase, two-letter, ISO 3166 country code, + \o and codeset and modifier are ignored. + \endlist + + If the string violates the locale format, or language is not a + valid ISO 369 code, the "C" locale is used instead. If country + is not present, or is not a valid ISO 3166 code, the most + appropriate country is chosen for the specified language. + + \sa QtQuick2::Locale +*/ +v8::Handle<v8::Value> locale(const v8::Arguments &args) +{ + QString code; + if (args.Length() > 1) + V8THROW_ERROR("locale() requires 0 or 1 argument"); + if (args.Length() == 1 && !args[0]->IsString()) + V8THROW_TYPE("locale(): argument (locale code) must be a string"); + + QV8Engine *v8engine = V8ENGINE(); + if (args.Length() == 1) + code = v8engine->toString(args[0]); + + return QQmlLocale::locale(v8engine, code); +} + +} // namespace QQmlBuiltinFunctions + +QT_END_NAMESPACE diff --git a/src/qml/qml/v8/qqmlbuiltinfunctions_p.h b/src/qml/qml/v8/qqmlbuiltinfunctions_p.h new file mode 100644 index 0000000000..ddb1c64243 --- /dev/null +++ b/src/qml/qml/v8/qqmlbuiltinfunctions_p.h @@ -0,0 +1,110 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLBUILTINFUNCTIONS_P_H +#define QQMLBUILTINFUNCTIONS_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qglobal.h> +#include <private/qv8_p.h> + +QT_BEGIN_NAMESPACE + +namespace QQmlBuiltinFunctions +{ +v8::Handle<v8::Value> gc(const v8::Arguments &args); +v8::Handle<v8::Value> consoleError(const v8::Arguments &args); +v8::Handle<v8::Value> consoleLog(const v8::Arguments &args); +v8::Handle<v8::Value> consoleProfile(const v8::Arguments &args); +v8::Handle<v8::Value> consoleProfileEnd(const v8::Arguments &args); +v8::Handle<v8::Value> consoleTime(const v8::Arguments &args); +v8::Handle<v8::Value> consoleTimeEnd(const v8::Arguments &args); +v8::Handle<v8::Value> consoleCount(const v8::Arguments &args); +v8::Handle<v8::Value> consoleTrace(const v8::Arguments &args); +v8::Handle<v8::Value> consoleWarn(const v8::Arguments &args); +v8::Handle<v8::Value> consoleAssert(const v8::Arguments &args); +v8::Handle<v8::Value> consoleException(const v8::Arguments &args); +v8::Handle<v8::Value> isQtObject(const v8::Arguments &args); +v8::Handle<v8::Value> rgba(const v8::Arguments &args); +v8::Handle<v8::Value> hsla(const v8::Arguments &args); +v8::Handle<v8::Value> rect(const v8::Arguments &args); +v8::Handle<v8::Value> point(const v8::Arguments &args); +v8::Handle<v8::Value> size(const v8::Arguments &args); +v8::Handle<v8::Value> vector3d(const v8::Arguments &args); +v8::Handle<v8::Value> vector4d(const v8::Arguments &args); +v8::Handle<v8::Value> lighter(const v8::Arguments &args); +v8::Handle<v8::Value> darker(const v8::Arguments &args); +v8::Handle<v8::Value> tint(const v8::Arguments &args); +v8::Handle<v8::Value> formatDate(const v8::Arguments &args); +v8::Handle<v8::Value> formatTime(const v8::Arguments &args); +v8::Handle<v8::Value> formatDateTime(const v8::Arguments &args); +v8::Handle<v8::Value> openUrlExternally(const v8::Arguments &args); +v8::Handle<v8::Value> fontFamilies(const v8::Arguments &args); +v8::Handle<v8::Value> md5(const v8::Arguments &args); +v8::Handle<v8::Value> btoa(const v8::Arguments &args); +v8::Handle<v8::Value> atob(const v8::Arguments &args); +v8::Handle<v8::Value> quit(const v8::Arguments &args); +v8::Handle<v8::Value> resolvedUrl(const v8::Arguments &args); +v8::Handle<v8::Value> createQmlObject(const v8::Arguments &args); +v8::Handle<v8::Value> createComponent(const v8::Arguments &args); +v8::Handle<v8::Value> qsTranslate(const v8::Arguments &args); +v8::Handle<v8::Value> qsTranslateNoOp(const v8::Arguments &args); +v8::Handle<v8::Value> qsTr(const v8::Arguments &args); +v8::Handle<v8::Value> qsTrNoOp(const v8::Arguments &args); +v8::Handle<v8::Value> qsTrId(const v8::Arguments &args); +v8::Handle<v8::Value> qsTrIdNoOp(const v8::Arguments &args); +v8::Handle<v8::Value> stringArg(const v8::Arguments &args); +v8::Handle<v8::Value> locale(const v8::Arguments &args); +} + +QT_END_NAMESPACE + +#endif // QQMLBUILTINFUNCTIONS_P_H diff --git a/src/qml/qml/v8/qscript_impl_p.h b/src/qml/qml/v8/qscript_impl_p.h new file mode 100644 index 0000000000..fdbf2f0097 --- /dev/null +++ b/src/qml/qml/v8/qscript_impl_p.h @@ -0,0 +1,43 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** 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 +** us via http://www.qt-project.org/. +** +** $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" +#include "qjsvalueiterator_impl_p.h" +#include "qjsconverter_impl_p.h" + +#endif //QSCRIPT_IMPL_P_H diff --git a/src/qml/qml/v8/qscriptisolate_p.h b/src/qml/qml/v8/qscriptisolate_p.h new file mode 100644 index 0000000000..4afa74756f --- /dev/null +++ b/src/qml/qml/v8/qscriptisolate_p.h @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** 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 +** us via http://www.qt-project.org/. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef APIPREAMBLE_P_H +#define APIPREAMBLE_P_H + +#include <private/qv8_p.h> +#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/qml/qml/v8/qscriptoriginalglobalobject_p.h b/src/qml/qml/v8/qscriptoriginalglobalobject_p.h new file mode 100644 index 0000000000..12321cc71a --- /dev/null +++ b/src/qml/qml/v8/qscriptoriginalglobalobject_p.h @@ -0,0 +1,159 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** 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 +** us via http://www.qt-project.org/. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSCRIPTORIGINALGLOBALOBJECT_P_H +#define QSCRIPTORIGINALGLOBALOBJECT_P_H + +#include "QtCore/qglobal.h" +#include "qjsvalue_p.h" + +#include <private/qv8_p.h> + +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() {} + inline void init(v8::Handle<v8::Context> context); + inline void destroy(); + + inline QJSValuePrivate::PropertyFlags getPropertyFlags(v8::Handle<v8::Object> object, v8::Handle<v8::Value> property); + inline v8::Local<v8::Object> getOwnPropertyDescriptor(v8::Handle<v8::Object> object, v8::Handle<v8::Value> property) const; + inline bool strictlyEquals(v8::Handle<v8::Object> object); +private: + Q_DISABLE_COPY(QScriptOriginalGlobalObject) + + // Copy of constructors and prototypes used in isType functions. + v8::Persistent<v8::Function> m_ownPropertyDescriptor; + v8::Persistent<v8::Object> m_globalObject; +}; + +void QScriptOriginalGlobalObject::init(v8::Handle<v8::Context> context) +{ + // Please notice that engine is not fully initialized at this point. + + v8::Context::Scope contextScope(context); + + v8::HandleScope scope; + + m_globalObject = v8::Persistent<v8::Object>::New(context->Global()); + + v8::Local<v8::Object> objectConstructor = m_globalObject->Get(v8::String::New("Object"))->ToObject(); + Q_ASSERT(objectConstructor->IsObject()); + { // Initialize m_ownPropertyDescriptor. + v8::Local<v8::Value> ownPropertyDescriptor = objectConstructor->Get(v8::String::New("getOwnPropertyDescriptor")); + Q_ASSERT(!ownPropertyDescriptor.IsEmpty()); + m_ownPropertyDescriptor = v8::Persistent<v8::Function>::New(v8::Local<v8::Function>::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 QJSValuePrivate::PropertyFlags QScriptOriginalGlobalObject::getPropertyFlags(v8::Handle<v8::Object> object, v8::Handle<v8::Value> property) +{ + Q_ASSERT(object->IsObject()); + Q_ASSERT(!property.IsEmpty()); + v8::Local<v8::Object> descriptor = getOwnPropertyDescriptor(object, property); + if (descriptor.IsEmpty()) { +// // Property isn't owned by this object. +// if (!(mode & QScriptValue::ResolvePrototype)) +// return 0; + v8::Local<v8::Value> prototype = object->GetPrototype(); + if (prototype->IsNull()) + return 0; + return getPropertyFlags(v8::Local<v8::Object>::Cast(prototype), property); + } + v8::Local<v8::String> writableName = v8::String::New("writable"); + v8::Local<v8::String> configurableName = v8::String::New("configurable"); + v8::Local<v8::String> enumerableName = v8::String::New("enumerable"); +// v8::Local<v8::String> getName = v8::String::New("get"); +// v8::Local<v8::String> setName = v8::String::New("set"); + + unsigned flags = 0; + + if (!descriptor->Get(configurableName)->BooleanValue()) + flags |= QJSValuePrivate::Undeletable; + if (!descriptor->Get(enumerableName)->BooleanValue()) + flags |= QJSValuePrivate::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 |= QJSValuePrivate::ReadOnly; + } else { +// if (descriptor->Get(getName)->IsObject()) +// flags |= QScriptValue::PropertyGetter; +// if (descriptor->Get(setName)->IsObject()) +// flags |= QScriptValue::PropertySetter; + } + + return QJSValuePrivate::PropertyFlag(flags); +} + +inline v8::Local<v8::Object> QScriptOriginalGlobalObject::getOwnPropertyDescriptor(v8::Handle<v8::Object> object, v8::Handle<v8::Value> property) const +{ + Q_ASSERT(object->IsObject()); + Q_ASSERT(!property.IsEmpty()); + // FIXME do we need try catch here? + v8::Handle<v8::Value> argv[] = {object, property}; + v8::Local<v8::Value> descriptor = m_ownPropertyDescriptor->Call(m_globalObject, /* argc */ 2, argv); + if (descriptor.IsEmpty() || !descriptor->IsObject()) + return v8::Local<v8::Object>(); + return v8::Local<v8::Object>::Cast(descriptor); +} + +inline bool QScriptOriginalGlobalObject::strictlyEquals(v8::Handle<v8::Object> object) +{ + return m_globalObject->GetPrototype()->StrictEquals(object); +} + +QT_END_NAMESPACE + +#endif diff --git a/src/qml/qml/v8/qscriptshareddata_p.h b/src/qml/qml/v8/qscriptshareddata_p.h new file mode 100644 index 0000000000..df95b26206 --- /dev/null +++ b/src/qml/qml/v8/qscriptshareddata_p.h @@ -0,0 +1,151 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** 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 +** us via http://www.qt-project.org/. +** +** $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 T> class QScriptPassPointer; + +// FIXME: that could be reimplemented to not check for a null value. +template<class T> +class QScriptSharedDataPointer : public QExplicitlySharedDataPointer<T> +{ +public: + inline QScriptSharedDataPointer() {} + explicit QScriptSharedDataPointer(QScriptPassPointer<T> data) : QExplicitlySharedDataPointer<T>(data.give()) {} + explicit QScriptSharedDataPointer(T *data) : QExplicitlySharedDataPointer<T>(data) {} + + inline QScriptSharedDataPointer<T> &operator=(const QScriptPassPointer<T> &other) + { + this->QExplicitlySharedDataPointer<T>::operator =(other.give()); + return *this; + } + inline QScriptSharedDataPointer<T> &operator=(T *other) + { + this->QExplicitlySharedDataPointer<T>::operator =(other); + return *this; + } +}; + +// FIXME: that could be reimplemented to not check for a null value. +template <class T> +class QScriptPassPointer { +public: + QScriptPassPointer(T *data) : m_ptr(data) {} + inline QScriptPassPointer() { m_ptr = 0; } + inline QScriptPassPointer(const QScriptPassPointer<T> &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<T> &other) const { return m_ptr == other.m_ptr; } + inline bool operator!=(const QScriptPassPointer<T> &other) const { return m_ptr != other.m_ptr; } + inline bool operator==(const QScriptSharedDataPointer<T> &other) const { return m_ptr == other.m_ptr; } + inline bool operator!=(const QScriptSharedDataPointer<T> &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<T> & operator=(const QScriptPassPointer<T> &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/qml/qml/v8/qscripttools_p.h b/src/qml/qml/v8/qscripttools_p.h new file mode 100644 index 0000000000..fcea205f61 --- /dev/null +++ b/src/qml/qml/v8/qscripttools_p.h @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** 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 +** us via http://www.qt-project.org/. +** +** $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 <private/qintrusivelist_p.h> + +QT_BEGIN_NAMESPACE + +template<class N, QIntrusiveListNode N::*member> +class QScriptIntrusiveList : public QIntrusiveList<N, member> +{ +public: + inline void insert(N *n); + inline void remove(N *n); +}; + +template<class N, QIntrusiveListNode N::*member> +void QScriptIntrusiveList<N, member>::insert(N *n) +{ + Q_ASSERT_X(!this->contains(n), Q_FUNC_INFO, "Can't insert a value which is in the list already"); + Q_ASSERT_X(!(n->*member).isInList(), Q_FUNC_INFO, "Can't insert a value which is in another list"); + QIntrusiveList<N, member>::insert(n); +} + +template<class N, QIntrusiveListNode N::*member> +void QScriptIntrusiveList<N, member>::remove(N *n) +{ + Q_ASSERT_X(this->contains(n), Q_FUNC_INFO, "Can't remove a value which is not in the list"); + QIntrusiveList<N, member>::remove(n); +} + +QT_END_NAMESPACE + +#endif //QSCRIPTTOOLS_P_H diff --git a/src/qml/qml/v8/qv8_p.h b/src/qml/qml/v8/qv8_p.h new file mode 100644 index 0000000000..d6a06593f5 --- /dev/null +++ b/src/qml/qml/v8/qv8_p.h @@ -0,0 +1,42 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <private/v8.h> diff --git a/src/qml/qml/v8/qv8bindings.cpp b/src/qml/qml/v8/qv8bindings.cpp new file mode 100644 index 0000000000..76fbea137e --- /dev/null +++ b/src/qml/qml/v8/qv8bindings.cpp @@ -0,0 +1,285 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv8bindings_p.h" + +#include <private/qv8_p.h> +#include <private/qqmlbinding_p.h> +#include <private/qqmlcompiler_p.h> +#include <private/qqmlproperty_p.h> +#include <private/qqmlbinding_p_p.h> +#include <private/qqmlexpression_p.h> +#include <private/qobject_p.h> +#include <private/qqmltrace_p.h> +#include <private/qqmlprofilerservice_p.h> + +QT_BEGIN_NAMESPACE + +static QQmlJavaScriptExpression::VTable QV8Bindings_Binding_jsvtable = { + QV8Bindings::Binding::expressionIdentifier, + QV8Bindings::Binding::expressionChanged +}; + +QV8Bindings::Binding::Binding() +: QQmlJavaScriptExpression(&QV8Bindings_Binding_jsvtable), target(0), parent(0) +{ +} + +void QV8Bindings::Binding::setEnabled(bool e, QQmlPropertyPrivate::WriteFlags flags) +{ + if (enabledFlag() != e) { + setEnabledFlag(e); + + if (e) update(flags); + } +} + +void QV8Bindings::refresh() +{ + int count = functions()->Length(); + for (int ii = 0; ii < count; ++ii) + bindings[ii].refresh(); +} + +void QV8Bindings::Binding::refresh() +{ + update(); +} + +int QV8Bindings::Binding::propertyIndex() const +{ + return instruction->property.encodedIndex(); +} + +QObject *QV8Bindings::Binding::object() const +{ + return target; +} + +void QV8Bindings::Binding::update(QQmlPropertyPrivate::WriteFlags flags) +{ + if (!enabledFlag()) + return; + + QQmlTrace trace("V8 Binding Update"); + trace.addDetail("URL", parent->url()); + trace.addDetail("Line", instruction->line); + trace.addDetail("Column", instruction->column); + + QQmlBindingProfiler prof(parent->urlString(), instruction->line, instruction->column); + + QQmlContextData *context = parent->context(); + if (!context || !context->isValid()) + return; + + if (!updatingFlag()) { + setUpdatingFlag(true); + QQmlEnginePrivate *ep = QQmlEnginePrivate::get(context->engine); + + bool isUndefined = false; + + DeleteWatcher watcher(this); + ep->referenceScarceResources(); + + v8::HandleScope handle_scope; + v8::Context::Scope scope(ep->v8engine()->context()); + v8::Local<v8::Value> result = + evaluate(context, + v8::Handle<v8::Function>::Cast(parent->functions()->Get(instruction->value)), + &isUndefined); + + trace.event("writing V8 result"); + bool needsErrorData = false; + if (!watcher.wasDeleted() && !hasError()) { + typedef QQmlPropertyPrivate PP; + needsErrorData = !PP::writeBinding(target, instruction->property, context, this, result, + isUndefined, flags); + } + + if (!watcher.wasDeleted()) { + + if (needsErrorData) { + QUrl url = parent->url(); + if (url.isEmpty()) url = QUrl(QLatin1String("<Unknown File>")); + + delayedError()->error.setUrl(url); + delayedError()->error.setLine(instruction->line); + delayedError()->error.setColumn(-1); + } + + if (hasError()) { + if (!delayedError()->addError(ep)) ep->warning(delayedError()->error); + } else { + clearError(); + } + + setUpdatingFlag(false); + } + + ep->dereferenceScarceResources(); + + } else { + QQmlProperty p = QQmlPropertyPrivate::restore(target, instruction->property, + context); + QQmlBindingPrivate::printBindingLoopError(p); + } +} + +QString QV8Bindings::Binding::expressionIdentifier(QQmlJavaScriptExpression *e) +{ + Binding *This = static_cast<Binding *>(e); + return This->parent->urlString() + QLatin1String(":") + + QString::number(This->instruction->line); +} + +void QV8Bindings::Binding::expressionChanged(QQmlJavaScriptExpression *e) +{ + Binding *This = static_cast<Binding *>(e); + This->update(QQmlPropertyPrivate::DontRemoveBinding); +} + +void QV8Bindings::Binding::destroy() +{ + setEnabledFlag(false); + removeFromObject(); + clear(); + clearError(); + parent->release(); +} + +QV8Bindings::QV8Bindings(QQmlCompiledData::V8Program *program, + int line, + QQmlContextData *context) +: program(program), bindings(0), refCount(1) +{ + program->cdata->addref(); + + QV8Engine *engine = QQmlEnginePrivate::getV8Engine(context->engine); + + if (program->bindings.IsEmpty()) { + v8::HandleScope handle_scope; + v8::Context::Scope scope(engine->context()); + + v8::Local<v8::Script> script; + bool compileFailed = false; + { + v8::TryCatch try_catch; + const QByteArray &source = program->program; + script = engine->qmlModeCompile(source.constData(), source.length(), + program->cdata->name, line); + if (try_catch.HasCaught()) { + // The binding was not compiled. There are some exceptional cases which the + // expression rewriter does not rewrite properly (e.g., \r-terminated lines + // are not rewritten correctly but this bug is demed out-of-scope to fix for + // performance reasons; see QTBUG-24064). + compileFailed = true; + QQmlError error; + error.setDescription(QString(QLatin1String("Exception occurred during compilation of binding at line: %1")).arg(line)); + v8::Local<v8::Message> message = try_catch.Message(); + if (!message.IsEmpty()) + QQmlExpressionPrivate::exceptionToError(message, error); + QQmlEnginePrivate::get(engine->engine())->warning(error); + program->bindings = qPersistentNew(v8::Array::New()); + } + } + + if (!compileFailed) { + v8::Local<v8::Value> result = script->Run(engine->contextWrapper()->sharedContext()); + if (result->IsArray()) { + program->bindings = qPersistentNew(v8::Local<v8::Array>::Cast(result)); + program->program.clear(); // We don't need the source anymore + } + } + } + + int bindingsCount = functions()->Length(); + if (bindingsCount) bindings = new QV8Bindings::Binding[bindingsCount]; + + setContext(context); +} + +QV8Bindings::~QV8Bindings() +{ + program->cdata->release(); + program = 0; + + delete [] bindings; + bindings = 0; +} + +QQmlAbstractBinding * +QV8Bindings::configBinding(QObject *target, QObject *scope, + const QQmlInstruction::instr_assignBinding *i) +{ + if (!bindings) // initialization failed. + return 0; + + QV8Bindings::Binding *rv = bindings + i->value; + + rv->instruction = i; + rv->target = target; + rv->setScopeObject(scope); + rv->setUseSharedContext(true); + rv->setNotifyOnValueChanged(true); + rv->parent = this; + + addref(); // This is decremented in Binding::destroy() + + return rv; +} + +const QUrl &QV8Bindings::url() const +{ + return program->cdata->url; +} + +const QString &QV8Bindings::urlString() const +{ + return program->cdata->name; +} + +v8::Persistent<v8::Array> &QV8Bindings::functions() const +{ + return program->bindings; +} + + +QT_END_NAMESPACE diff --git a/src/qml/qml/v8/qv8bindings_p.h b/src/qml/qml/v8/qv8bindings_p.h new file mode 100644 index 0000000000..ad5b2cb8b0 --- /dev/null +++ b/src/qml/qml/v8/qv8bindings_p.h @@ -0,0 +1,148 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV8BINDINGS_P_H +#define QV8BINDINGS_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 <private/qqmlpropertycache_p.h> +#include <private/qqmlinstruction_p.h> +#include <private/qqmlexpression_p.h> +#include <private/qqmlcompiler_p.h> +#include <private/qqmlbinding_p.h> +#include <private/qflagpointer_p.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QQmlCompiledData; + +class QV8BindingsPrivate; +class QV8Bindings : public QQmlAbstractExpression +{ +public: + QV8Bindings(QQmlCompiledData::V8Program *, + int line, + QQmlContextData *context); + virtual ~QV8Bindings(); + + QQmlAbstractBinding *configBinding(QObject *target, QObject *scope, + const QQmlInstruction::instr_assignBinding *); + + // Inherited from QQmlAbstractExpression + virtual void refresh(); + + struct Binding : public QQmlJavaScriptExpression, + public QQmlAbstractBinding { + Binding(); + + void update() { QQmlAbstractBinding::update(); } + void refresh(); + + // "Inherited" from QQmlJavaScriptExpression + static QString expressionIdentifier(QQmlJavaScriptExpression *); + static void expressionChanged(QQmlJavaScriptExpression *); + + // Inherited from QQmlAbstractBinding + virtual void setEnabled(bool, QQmlPropertyPrivate::WriteFlags flags); + virtual void update(QQmlPropertyPrivate::WriteFlags flags); + virtual void destroy(); + virtual int propertyIndex() const; + virtual QObject *object() const; + + QObject *target; + QV8Bindings *parent; + + // To save memory, we store flags inside the instruction pointer. + // flag1: enabled + // flag2: updating + QFlagPointer<const QQmlInstruction::instr_assignBinding> instruction; + + inline bool enabledFlag() const { return instruction.flag(); } + inline void setEnabledFlag(bool v) { instruction.setFlagValue(v); } + inline bool updatingFlag() const { return instruction.flag2(); } + inline void setUpdatingFlag(bool v) { instruction.setFlag2Value(v); } + }; + + inline void addref(); + inline void release(); + +private: + Q_DISABLE_COPY(QV8Bindings) + + const QUrl &url() const; + const QString &urlString() const; + v8::Persistent<v8::Array> &functions() const; + + QQmlCompiledData::V8Program *program; + Binding *bindings; + int refCount; +}; + +void QV8Bindings::addref() +{ + ++refCount; +} + +void QV8Bindings::release() +{ + if (0 == --refCount) + delete this; +} + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QV8BINDINGS_P_H + + diff --git a/src/qml/qml/v8/qv8contextwrapper.cpp b/src/qml/qml/v8/qv8contextwrapper.cpp new file mode 100644 index 0000000000..246b716aa0 --- /dev/null +++ b/src/qml/qml/v8/qv8contextwrapper.cpp @@ -0,0 +1,455 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv8contextwrapper_p.h" +#include "qv8engine_p.h" + +#include <private/qqmlengine_p.h> +#include <private/qqmlcontext_p.h> + +QT_BEGIN_NAMESPACE + +static QString internal(QLatin1String("You've stumbled onto an internal implementation detail " + "that should never have been exposed.")); + +class QV8ContextResource : public QV8ObjectResource +{ + V8_RESOURCE_TYPE(ContextType); + +public: + QV8ContextResource(QV8Engine *engine, QQmlContextData *context, QObject *scopeObject); + ~QV8ContextResource(); + + inline QQmlContextData *getContext() const; + inline QObject *getScopeObject() const; + + quint32 isSharedContext:1; + quint32 hasSubContexts:1; + quint32 readOnly:1; + quint32 dummy:29; + + QObject *secondaryScope; + + // This is a pretty horrible hack, and an abuse of external strings. When we create a + // sub-context (a context created by a Qt.include() in an external javascript file), + // we pass a specially crafted SubContext external string as the v8::Script::Data() to + // the script, which contains a pointer to the context. We can then access the + // v8::Script::Data() later on to resolve names and URLs against the sub-context instead + // of the main outer context. + struct SubContext : public v8::String::ExternalStringResource { + SubContext(QQmlContextData *context) : context(context) {} + QQmlGuardedContextData context; + + virtual const uint16_t* data() const { return (const uint16_t *)internal.constData(); } + virtual size_t length() const { return internal.length(); } + }; + +private: + QQmlGuardedContextData context; + QQmlGuard<QObject> scopeObject; + +}; + +QV8ContextResource::QV8ContextResource(QV8Engine *engine, QQmlContextData *context, QObject *scopeObject) +: QV8ObjectResource(engine), isSharedContext(false), hasSubContexts(false), readOnly(true), + secondaryScope(0), context(context), scopeObject(scopeObject) +{ +} + +QV8ContextResource::~QV8ContextResource() +{ + if (context && context->isJSContext) + context->destroy(); +} + +// Returns the scope object +QObject *QV8ContextResource::getScopeObject() const +{ + if (isSharedContext) + return QQmlEnginePrivate::get(engine->engine())->sharedScope; + else + return scopeObject; +} + +// Returns the context, including resolving a subcontext +QQmlContextData *QV8ContextResource::getContext() const +{ + if (isSharedContext) + return QQmlEnginePrivate::get(engine->engine())->sharedContext; + + if (!hasSubContexts) + return context; + + v8::Local<v8::Value> callingdata = v8::Context::GetCallingScriptData(); + if (callingdata.IsEmpty() || !callingdata->IsString()) + return context; + + v8::Local<v8::String> callingstring = callingdata->ToString(); + Q_ASSERT(callingstring->IsExternal()); + Q_ASSERT(callingstring->GetExternalStringResource()); + + SubContext *sc = static_cast<SubContext *>(callingstring->GetExternalStringResource()); + return sc->context; +} + +QV8ContextWrapper::QV8ContextWrapper() +: m_engine(0) +{ +} + +QV8ContextWrapper::~QV8ContextWrapper() +{ +} + +void QV8ContextWrapper::destroy() +{ + qPersistentDispose(m_sharedContext); + qPersistentDispose(m_urlConstructor); + qPersistentDispose(m_constructor); +} + +void QV8ContextWrapper::init(QV8Engine *engine) +{ + m_engine = engine; + { + v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New(); + ft->InstanceTemplate()->SetHasExternalResource(true); + ft->InstanceTemplate()->SetFallbackPropertyHandler(Getter, Setter); + m_constructor = qPersistentNew<v8::Function>(ft->GetFunction()); + } + { + v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New(); + ft->InstanceTemplate()->SetHasExternalResource(true); + ft->InstanceTemplate()->SetFallbackPropertyHandler(NullGetter, NullSetter); + m_urlConstructor = qPersistentNew<v8::Function>(ft->GetFunction()); + } + { + v8::Local<v8::Object> sharedContext = m_constructor->NewInstance(); + QV8ContextResource *r = new QV8ContextResource(engine, 0, 0); + r->isSharedContext = true; + sharedContext->SetExternalResource(r); + m_sharedContext = qPersistentNew<v8::Object>(sharedContext); + } +} + +v8::Local<v8::Object> QV8ContextWrapper::qmlScope(QQmlContextData *ctxt, QObject *scope) +{ + // XXX NewInstance() should be optimized + v8::Local<v8::Object> rv = m_constructor->NewInstance(); + QV8ContextResource *r = new QV8ContextResource(m_engine, ctxt, scope); + rv->SetExternalResource(r); + return rv; +} + +v8::Local<v8::Object> QV8ContextWrapper::urlScope(const QUrl &url) +{ + QQmlContextData *context = new QQmlContextData; + context->url = url; + context->isInternal = true; + context->isJSContext = true; + + // XXX NewInstance() should be optimized + v8::Local<v8::Object> rv = m_urlConstructor->NewInstance(); + QV8ContextResource *r = new QV8ContextResource(m_engine, context, 0); + rv->SetExternalResource(r); + return rv; +} + +void QV8ContextWrapper::setReadOnly(v8::Handle<v8::Object> qmlglobal, bool readOnly) +{ + QV8ContextResource *resource = v8_resource_cast<QV8ContextResource>(qmlglobal); + Q_ASSERT(resource); + resource->readOnly = readOnly; +} + +void QV8ContextWrapper::addSubContext(v8::Handle<v8::Object> qmlglobal, v8::Handle<v8::Script> script, + QQmlContextData *ctxt) +{ + QV8ContextResource *resource = v8_resource_cast<QV8ContextResource>(qmlglobal); + Q_ASSERT(resource); + resource->hasSubContexts = true; + script->SetData(v8::String::NewExternal(new QV8ContextResource::SubContext(ctxt))); +} + +QObject *QV8ContextWrapper::setSecondaryScope(v8::Handle<v8::Object> ctxt, QObject *scope) +{ + QV8ContextResource *resource = v8_resource_cast<QV8ContextResource>(ctxt); + if (!resource) return 0; + + QObject *rv = resource->secondaryScope; + resource->secondaryScope = scope; + return rv; +} + +QQmlContextData *QV8ContextWrapper::callingContext() +{ + v8::Local<v8::Object> qmlglobal = v8::Context::GetCallingQmlGlobal(); + if (qmlglobal.IsEmpty()) return 0; + + QV8ContextResource *r = v8_resource_cast<QV8ContextResource>(qmlglobal); + return r?r->getContext():0; +} + +QQmlContextData *QV8ContextWrapper::context(v8::Handle<v8::Value> value) +{ + if (!value->IsObject()) + return 0; + + v8::Handle<v8::Object> qmlglobal = v8::Handle<v8::Object>::Cast(value); + QV8ContextResource *r = v8_resource_cast<QV8ContextResource>(qmlglobal); + return r?r->getContext():0; +} + +v8::Handle<v8::Value> QV8ContextWrapper::NullGetter(v8::Local<v8::String> property, + const v8::AccessorInfo &info) +{ + QV8ContextResource *resource = v8_resource_check<QV8ContextResource>(info.This()); + + QV8Engine *engine = resource->engine; + + QString error = QLatin1String("Can't find variable: ") + engine->toString(property); + v8::ThrowException(v8::Exception::ReferenceError(engine->toString(error))); + return v8::Undefined(); +} + +v8::Handle<v8::Value> QV8ContextWrapper::Getter(v8::Local<v8::String> property, + const v8::AccessorInfo &info) +{ + QV8ContextResource *resource = v8_resource_check<QV8ContextResource>(info.This()); + + // Its possible we could delay the calculation of the "actual" context (in the case + // of sub contexts) until it is definately needed. + QQmlContextData *context = resource->getContext(); + QQmlContextData *expressionContext = context; + + if (!context) + return v8::Undefined(); + + if (v8::Context::GetCallingQmlGlobal() != info.This()) + return v8::Handle<v8::Value>(); + + // Search type (attached property/enum/imported scripts) names + // Secondary scope object + // while (context) { + // Search context properties + // Search scope object + // Search context object + // context = context->parent + // } + + QV8Engine *engine = resource->engine; + + QObject *scopeObject = resource->getScopeObject(); + + QHashedV8String propertystring(property); + + if (context->imports && QV8Engine::startsWithUpper(property)) { + // Search for attached properties, enums and imported scripts + QQmlTypeNameCache::Result r = context->imports->query(propertystring); + + if (r.isValid()) { + if (r.scriptIndex != -1) { + int index = r.scriptIndex; + if (index < context->importedScripts.count()) + return context->importedScripts.at(index); + else + return v8::Undefined(); + } else if (r.type) { + return engine->typeWrapper()->newObject(scopeObject, r.type); + } else if (r.importNamespace) { + return engine->typeWrapper()->newObject(scopeObject, context->imports, r.importNamespace); + } + Q_ASSERT(!"Unreachable"); + } + + // Fall through + } + + QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine->engine()); + QV8QObjectWrapper *qobjectWrapper = engine->qobjectWrapper(); + + if (resource->secondaryScope) { + v8::Handle<v8::Value> result = qobjectWrapper->getProperty(resource->secondaryScope, propertystring, + QV8QObjectWrapper::IgnoreRevision); + if (!result.IsEmpty()) return result; + } + + while (context) { + // Search context properties + if (context->propertyNames) { + int propertyIdx = context->propertyNames->value(propertystring); + + if (propertyIdx != -1) { + + if (propertyIdx < context->idValueCount) { + + ep->captureProperty(&context->idValues[propertyIdx].bindings); + return engine->newQObject(context->idValues[propertyIdx]); + } else { + + QQmlContextPrivate *cp = context->asQQmlContextPrivate(); + + ep->captureProperty(context->asQQmlContext(), -1, + propertyIdx + cp->notifyIndex); + + const QVariant &value = cp->propertyValues.at(propertyIdx); + if (value.userType() == qMetaTypeId<QList<QObject*> >()) { + QQmlListProperty<QObject> prop(context->asQQmlContext(), (void*)propertyIdx, + 0, + QQmlContextPrivate::context_count, + QQmlContextPrivate::context_at); + return engine->listWrapper()->newList(prop, qMetaTypeId<QQmlListProperty<QObject> >()); + } else { + return engine->fromVariant(cp->propertyValues.at(propertyIdx)); + } + } + } + } + + // Search scope object + if (scopeObject) { + v8::Handle<v8::Value> result = qobjectWrapper->getProperty(scopeObject, propertystring, + QV8QObjectWrapper::CheckRevision); + if (!result.IsEmpty()) return result; + } + scopeObject = 0; + + + // Search context object + if (context->contextObject) { + v8::Handle<v8::Value> result = qobjectWrapper->getProperty(context->contextObject, propertystring, + QV8QObjectWrapper::CheckRevision); + if (!result.IsEmpty()) return result; + } + + context = context->parent; + } + + expressionContext->unresolvedNames = true; + + QString error = QLatin1String("Can't find variable: ") + engine->toString(property); + v8::ThrowException(v8::Exception::ReferenceError(engine->toString(error))); + return v8::Undefined(); +} + +v8::Handle<v8::Value> QV8ContextWrapper::NullSetter(v8::Local<v8::String> property, + v8::Local<v8::Value>, + const v8::AccessorInfo &info) +{ + QV8ContextResource *resource = v8_resource_check<QV8ContextResource>(info.This()); + + QV8Engine *engine = resource->engine; + + if (!resource->readOnly) { + return v8::Handle<v8::Value>(); + } else { + QString error = QLatin1String("Invalid write to global property \"") + engine->toString(property) + + QLatin1String("\""); + v8::ThrowException(v8::Exception::Error(engine->toString(error))); + return v8::Handle<v8::Value>(); + } +} + +v8::Handle<v8::Value> QV8ContextWrapper::Setter(v8::Local<v8::String> property, + v8::Local<v8::Value> value, + const v8::AccessorInfo &info) +{ + QV8ContextResource *resource = v8_resource_check<QV8ContextResource>(info.This()); + + // Its possible we could delay the calculation of the "actual" context (in the case + // of sub contexts) until it is definately needed. + QQmlContextData *context = resource->getContext(); + QQmlContextData *expressionContext = context; + + if (!context) + return v8::Undefined(); + + if (v8::Context::GetCallingQmlGlobal() != info.This()) + return v8::Handle<v8::Value>(); + + // See QV8ContextWrapper::Getter for resolution order + + QV8Engine *engine = resource->engine; + QObject *scopeObject = resource->getScopeObject(); + + QHashedV8String propertystring(property); + + QV8QObjectWrapper *qobjectWrapper = engine->qobjectWrapper(); + + // Search scope object + if (resource->secondaryScope && + qobjectWrapper->setProperty(resource->secondaryScope, propertystring, value, + QV8QObjectWrapper::IgnoreRevision)) + return value; + + while (context) { + // Search context properties + if (context->propertyNames && -1 != context->propertyNames->value(propertystring)) + return value; + + // Search scope object + if (scopeObject && + qobjectWrapper->setProperty(scopeObject, propertystring, value, QV8QObjectWrapper::CheckRevision)) + return value; + scopeObject = 0; + + // Search context object + if (context->contextObject && + qobjectWrapper->setProperty(context->contextObject, propertystring, value, + QV8QObjectWrapper::CheckRevision)) + return value; + + context = context->parent; + } + + expressionContext->unresolvedNames = true; + + if (!resource->readOnly) { + return v8::Handle<v8::Value>(); + } else { + QString error = QLatin1String("Invalid write to global property \"") + engine->toString(property) + + QLatin1String("\""); + v8::ThrowException(v8::Exception::Error(engine->toString(error))); + return v8::Undefined(); + } +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/v8/qv8contextwrapper_p.h b/src/qml/qml/v8/qv8contextwrapper_p.h new file mode 100644 index 0000000000..117f16ab39 --- /dev/null +++ b/src/qml/qml/v8/qv8contextwrapper_p.h @@ -0,0 +1,120 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV8CONTEXTWRAPPER_P_H +#define QV8CONTEXTWRAPPER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qglobal.h> +#include <private/qv8_p.h> + +QT_BEGIN_NAMESPACE + +class QUrl; +class QObject; +class QV8Engine; +class QQmlContextData; +class QV8ContextWrapper +{ +public: + QV8ContextWrapper(); + ~QV8ContextWrapper(); + + void init(QV8Engine *); + void destroy(); + + v8::Local<v8::Object> qmlScope(QQmlContextData *ctxt, QObject *scope); + v8::Local<v8::Object> urlScope(const QUrl &); + + void setReadOnly(v8::Handle<v8::Object>, bool); + + void addSubContext(v8::Handle<v8::Object> qmlglobal, v8::Handle<v8::Script>, + QQmlContextData *ctxt); + + // XXX We only use the secondary scope to pass the "arguments" of the signal to + // on<SignalName> properties. Instead of doing this we should rewrite the + // JavaScript closure function to accept these arguments as named parameters. + // To keep backwards compatibility we have to check that the argument names are + // not members of the QV8Engine::illegalNames() set. + QObject *setSecondaryScope(v8::Handle<v8::Object>, QObject *); + + QQmlContextData *callingContext(); + QQmlContextData *context(v8::Handle<v8::Value>); + + inline v8::Handle<v8::Object> sharedContext() const; + +private: + static v8::Handle<v8::Value> NullGetter(v8::Local<v8::String> property, + const v8::AccessorInfo &info); + static v8::Handle<v8::Value> NullSetter(v8::Local<v8::String> property, + v8::Local<v8::Value> value, + const v8::AccessorInfo &info); + static v8::Handle<v8::Value> Getter(v8::Local<v8::String> property, + const v8::AccessorInfo &info); + static v8::Handle<v8::Value> Setter(v8::Local<v8::String> property, + v8::Local<v8::Value> value, + const v8::AccessorInfo &info); + + QV8Engine *m_engine; + v8::Persistent<v8::Function> m_constructor; + v8::Persistent<v8::Function> m_urlConstructor; + v8::Persistent<v8::Object> m_sharedContext; +}; + +v8::Handle<v8::Object> QV8ContextWrapper::sharedContext() const +{ + return m_sharedContext; +} + +QT_END_NAMESPACE + +#endif // QV8CONTEXTWRAPPER_P_H + diff --git a/src/qml/qml/v8/qv8debug_p.h b/src/qml/qml/v8/qv8debug_p.h new file mode 100644 index 0000000000..4e1ec3e2ca --- /dev/null +++ b/src/qml/qml/v8/qv8debug_p.h @@ -0,0 +1,42 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <private/v8-debug.h> diff --git a/src/qml/qml/v8/qv8domerrors.cpp b/src/qml/qml/v8/qv8domerrors.cpp new file mode 100644 index 0000000000..7b8f10a27e --- /dev/null +++ b/src/qml/qml/v8/qv8domerrors.cpp @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv8domerrors_p.h" +#include "qv8engine_p.h" + +QT_BEGIN_NAMESPACE + +void qt_add_domexceptions(QV8Engine *engine) +{ + // DOM Exception + v8::PropertyAttribute attributes = (v8::PropertyAttribute)(v8::ReadOnly | v8::DontEnum | v8::DontDelete); + + v8::Local<v8::Object> domexception = v8::Object::New(); + domexception->Set(v8::String::New("INDEX_SIZE_ERR"), v8::Integer::New(DOMEXCEPTION_INDEX_SIZE_ERR), attributes); + domexception->Set(v8::String::New("DOMSTRING_SIZE_ERR"), v8::Integer::New(DOMEXCEPTION_DOMSTRING_SIZE_ERR), attributes); + domexception->Set(v8::String::New("HIERARCHY_REQUEST_ERR"), v8::Integer::New(DOMEXCEPTION_HIERARCHY_REQUEST_ERR), attributes); + domexception->Set(v8::String::New("WRONG_DOCUMENT_ERR"), v8::Integer::New(DOMEXCEPTION_WRONG_DOCUMENT_ERR), attributes); + domexception->Set(v8::String::New("INVALID_CHARACTER_ERR"), v8::Integer::New(DOMEXCEPTION_INVALID_CHARACTER_ERR), attributes); + domexception->Set(v8::String::New("NO_DATA_ALLOWED_ERR"), v8::Integer::New(DOMEXCEPTION_NO_DATA_ALLOWED_ERR), attributes); + domexception->Set(v8::String::New("NO_MODIFICATION_ALLOWED_ERR"), v8::Integer::New(DOMEXCEPTION_NO_MODIFICATION_ALLOWED_ERR), attributes); + domexception->Set(v8::String::New("NOT_FOUND_ERR"), v8::Integer::New(DOMEXCEPTION_NOT_FOUND_ERR), attributes); + domexception->Set(v8::String::New("NOT_SUPPORTED_ERR"), v8::Integer::New(DOMEXCEPTION_NOT_SUPPORTED_ERR), attributes); + domexception->Set(v8::String::New("INUSE_ATTRIBUTE_ERR"), v8::Integer::New(DOMEXCEPTION_INUSE_ATTRIBUTE_ERR), attributes); + domexception->Set(v8::String::New("INVALID_STATE_ERR"), v8::Integer::New(DOMEXCEPTION_INVALID_STATE_ERR), attributes); + domexception->Set(v8::String::New("SYNTAX_ERR"), v8::Integer::New(DOMEXCEPTION_SYNTAX_ERR), attributes); + domexception->Set(v8::String::New("INVALID_MODIFICATION_ERR"), v8::Integer::New(DOMEXCEPTION_INVALID_MODIFICATION_ERR), attributes); + domexception->Set(v8::String::New("NAMESPACE_ERR"), v8::Integer::New(DOMEXCEPTION_NAMESPACE_ERR), attributes); + domexception->Set(v8::String::New("INVALID_ACCESS_ERR"), v8::Integer::New(DOMEXCEPTION_INVALID_ACCESS_ERR), attributes); + domexception->Set(v8::String::New("VALIDATION_ERR"), v8::Integer::New(DOMEXCEPTION_VALIDATION_ERR), attributes); + domexception->Set(v8::String::New("TYPE_MISMATCH_ERR"), v8::Integer::New(DOMEXCEPTION_TYPE_MISMATCH_ERR), attributes); + engine->global()->Set(v8::String::New("DOMException"), domexception); +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/v8/qv8domerrors_p.h b/src/qml/qml/v8/qv8domerrors_p.h new file mode 100644 index 0000000000..5d5f277d55 --- /dev/null +++ b/src/qml/qml/v8/qv8domerrors_p.h @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV8DOMERRORS_P_H +#define QV8DOMERRORS_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qglobal.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE +// From DOM-Level-3-Core spec +// http://www.w3.org/TR/DOM-Level-3-Core/core.html +#define DOMEXCEPTION_INDEX_SIZE_ERR 1 +#define DOMEXCEPTION_DOMSTRING_SIZE_ERR 2 +#define DOMEXCEPTION_HIERARCHY_REQUEST_ERR 3 +#define DOMEXCEPTION_WRONG_DOCUMENT_ERR 4 +#define DOMEXCEPTION_INVALID_CHARACTER_ERR 5 +#define DOMEXCEPTION_NO_DATA_ALLOWED_ERR 6 +#define DOMEXCEPTION_NO_MODIFICATION_ALLOWED_ERR 7 +#define DOMEXCEPTION_NOT_FOUND_ERR 8 +#define DOMEXCEPTION_NOT_SUPPORTED_ERR 9 +#define DOMEXCEPTION_INUSE_ATTRIBUTE_ERR 10 +#define DOMEXCEPTION_INVALID_STATE_ERR 11 +#define DOMEXCEPTION_SYNTAX_ERR 12 +#define DOMEXCEPTION_INVALID_MODIFICATION_ERR 13 +#define DOMEXCEPTION_NAMESPACE_ERR 14 +#define DOMEXCEPTION_INVALID_ACCESS_ERR 15 +#define DOMEXCEPTION_VALIDATION_ERR 16 +#define DOMEXCEPTION_TYPE_MISMATCH_ERR 17 + +#define V8THROW_DOM(error, string) { \ + v8::Local<v8::Value> v = v8::Exception::Error(v8::String::New(string)); \ + v->ToObject()->Set(v8::String::New("code"), v8::Integer::New(error)); \ + v8::ThrowException(v); \ + return v8::Handle<v8::Value>(); \ +} +class QV8Engine; +void qt_add_domexceptions(QV8Engine *engine); + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QV8DOMERRORS_P_H diff --git a/src/qml/qml/v8/qv8engine.cpp b/src/qml/qml/v8/qv8engine.cpp new file mode 100644 index 0000000000..4c2cce1525 --- /dev/null +++ b/src/qml/qml/v8/qv8engine.cpp @@ -0,0 +1,1598 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv8engine_p.h" + +#include <QtGui/QGuiApplication> + +#include "qv8contextwrapper_p.h" +#include "qv8valuetypewrapper_p.h" +#include "qv8sequencewrapper_p.h" +#include "qv8include_p.h" +#include "qjsengine_p.h" +#include "../../../3rdparty/javascriptcore/DateMath.h" + +#include <private/qqmlbuiltinfunctions_p.h> +#include <private/qqmllist_p.h> +#include <private/qqmlengine_p.h> +#include <private/qquickapplication_p.h> +#include <private/qqmlxmlhttprequest_p.h> +#include <private/qqmllocale_p.h> + +#include "qscript_impl_p.h" +#include "qv8domerrors_p.h" +#include "qv8sqlerrors_p.h" + + +Q_DECLARE_METATYPE(QJSValue) +Q_DECLARE_METATYPE(QList<int>) + + +// XXX TODO: Need to check all the global functions will also work in a worker script where the +// QQmlEngine is not available +QT_BEGIN_NAMESPACE + +static bool ObjectComparisonCallback(v8::Local<v8::Object> lhs, v8::Local<v8::Object> rhs) +{ + if (lhs == rhs) + return true; + + QV8ObjectResource *lhsr = static_cast<QV8ObjectResource*>(lhs->GetExternalResource()); + QV8ObjectResource *rhsr = static_cast<QV8ObjectResource*>(rhs->GetExternalResource()); + + Q_ASSERT(lhsr->engine == rhsr->engine); + + if (lhsr && rhsr) { + QV8ObjectResource::ResourceType lhst = lhsr->resourceType(); + QV8ObjectResource::ResourceType rhst = rhsr->resourceType(); + + switch (lhst) { + case QV8ObjectResource::ValueTypeType: + // a value type might be equal to a variant or another value type + if (rhst == QV8ObjectResource::ValueTypeType) { + return lhsr->engine->valueTypeWrapper()->isEqual(lhsr, lhsr->engine->valueTypeWrapper()->toVariant(rhsr)); + } else if (rhst == QV8ObjectResource::VariantType) { + return lhsr->engine->valueTypeWrapper()->isEqual(lhsr, lhsr->engine->variantWrapper()->toVariant(rhsr)); + } + break; + case QV8ObjectResource::VariantType: + // a variant might be equal to a value type or other variant. + if (rhst == QV8ObjectResource::VariantType) { + return lhsr->engine->variantWrapper()->toVariant(lhsr) == + lhsr->engine->variantWrapper()->toVariant(rhsr); + } else if (rhst == QV8ObjectResource::ValueTypeType) { + return rhsr->engine->valueTypeWrapper()->isEqual(rhsr, rhsr->engine->variantWrapper()->toVariant(lhsr)); + } + break; + case QV8ObjectResource::SequenceType: + // a sequence might be equal to itself. + if (rhst == QV8ObjectResource::SequenceType) { + return lhsr->engine->sequenceWrapper()->isEqual(lhsr, rhsr); + } + break; + default: + break; + } + } + + return false; +} + + +QV8Engine::QV8Engine(QJSEngine* qq, QJSEngine::ContextOwnership ownership) + : q(qq) + , m_engine(0) + , m_ownsV8Context(ownership == QJSEngine::CreateNewContext) + , m_xmlHttpRequestData(0) + , m_listModelData(0) +{ + qMetaTypeId<QJSValue>(); + qMetaTypeId<QList<int> >(); + + QByteArray v8args = qgetenv("V8ARGS"); + // change default v8 behaviour to not relocate breakpoints across lines + if (!v8args.contains("breakpoint_relocation")) + v8args.append(" --nobreakpoint_relocation"); + v8::V8::SetFlagsFromString(v8args.constData(), v8args.length()); + + ensurePerThreadIsolate(); + + v8::HandleScope handle_scope; + m_context = (ownership == QJSEngine::CreateNewContext) ? v8::Context::New() : v8::Persistent<v8::Context>::New(v8::Context::GetCurrent()); + qPersistentRegister(m_context); + m_originalGlobalObject.init(m_context); + v8::Context::Scope context_scope(m_context); + + v8::V8::SetUserObjectComparisonCallbackFunction(ObjectComparisonCallback); + QV8GCCallback::registerGcPrologueCallback(); + m_strongReferencer = qPersistentNew(v8::Object::New()); + + 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_sequenceWrapper.init(this); + + { + v8::Handle<v8::Value> v = global()->Get(v8::String::New("Object"))->ToObject()->Get(v8::String::New("getOwnPropertyNames")); + m_getOwnPropertyNames = qPersistentNew<v8::Function>(v8::Handle<v8::Function>::Cast(v)); + } +} + +QV8Engine::~QV8Engine() +{ + Q_ASSERT_X(v8::Isolate::GetCurrent(), "QV8Engine::~QV8Engine()", "called after v8::Isolate has exited"); + for (int ii = 0; ii < m_extensionData.count(); ++ii) + delete m_extensionData[ii]; + m_extensionData.clear(); + + qt_rem_qmlxmlhttprequest(this, m_xmlHttpRequestData); + m_xmlHttpRequestData = 0; + delete m_listModelData; + m_listModelData = 0; + + qPersistentDispose(m_freezeObject); + qPersistentDispose(m_getOwnPropertyNames); + + invalidateAllValues(); + clearExceptions(); + + qPersistentDispose(m_strongReferencer); + + m_sequenceWrapper.destroy(); + m_valueTypeWrapper.destroy(); + m_variantWrapper.destroy(); + m_listWrapper.destroy(); + m_typeWrapper.destroy(); + m_qobjectWrapper.destroy(); + m_contextWrapper.destroy(); + m_stringWrapper.destroy(); + + m_originalGlobalObject.destroy(); + + if (m_ownsV8Context) + qPersistentDispose(m_context); +} + +QString QV8Engine::toStringStatic(v8::Handle<v8::Value> jsstr) +{ + return toStringStatic(jsstr->ToString()); +} + +QString QV8Engine::toStringStatic(v8::Handle<v8::String> jsstr) +{ + QString qstr; + qstr.resize(jsstr->Length()); + jsstr->Write((uint16_t*)qstr.data()); + return qstr; +} + +QVariant QV8Engine::toVariant(v8::Handle<v8::Value> value, int typeHint) +{ + if (value.IsEmpty()) + return QVariant(); + + if (typeHint == QVariant::Bool) + return QVariant(value->BooleanValue()); + + if (value->IsObject()) { + QV8ObjectResource *r = (QV8ObjectResource *)value->ToObject()->GetExternalResource(); + if (r) { + switch (r->resourceType()) { + case QV8ObjectResource::Context2DStyleType: + case QV8ObjectResource::Context2DPixelArrayType: + case QV8ObjectResource::SignalHandlerType: + case QV8ObjectResource::IncubatorType: + case QV8ObjectResource::VisualDataItemType: + case QV8ObjectResource::ContextType: + case QV8ObjectResource::XMLHttpRequestType: + case QV8ObjectResource::DOMNodeType: + case QV8ObjectResource::SQLDatabaseType: + case QV8ObjectResource::ListModelType: + case QV8ObjectResource::Context2DType: + case QV8ObjectResource::ParticleDataType: + case QV8ObjectResource::LocaleDataType: + return QVariant(); + case QV8ObjectResource::TypeType: + return m_typeWrapper.toVariant(r); + case QV8ObjectResource::QObjectType: + return qVariantFromValue<QObject *>(m_qobjectWrapper.toQObject(r)); + case QV8ObjectResource::ListType: + return m_listWrapper.toVariant(r); + case QV8ObjectResource::VariantType: + return m_variantWrapper.toVariant(r); + case QV8ObjectResource::ValueTypeType: + return m_valueTypeWrapper.toVariant(r); + case QV8ObjectResource::SequenceType: + return m_sequenceWrapper.toVariant(r); + } + } + } + + if (value->IsArray()) { + v8::Handle<v8::Array> array = v8::Handle<v8::Array>::Cast(value); + if (typeHint == qMetaTypeId<QList<QObject *> >()) { + QList<QObject *> list; + uint32_t length = array->Length(); + for (uint32_t ii = 0; ii < length; ++ii) { + v8::Local<v8::Value> arrayItem = array->Get(ii); + if (arrayItem->IsObject()) { + list << toQObject(arrayItem->ToObject()); + } else { + list << 0; + } + } + + return qVariantFromValue<QList<QObject*> >(list); + } + + bool succeeded = false; + QVariant retn = m_sequenceWrapper.toVariant(array, typeHint, &succeeded); + if (succeeded) + return retn; + } + + return toBasicVariant(value); +} + +static v8::Handle<v8::Array> arrayFromStringList(QV8Engine *engine, const QStringList &list) +{ + v8::Context::Scope scope(engine->context()); + v8::Local<v8::Array> result = v8::Array::New(list.count()); + for (int ii = 0; ii < list.count(); ++ii) + result->Set(ii, engine->toString(list.at(ii))); + return result; +} + +static v8::Handle<v8::Array> arrayFromVariantList(QV8Engine *engine, const QVariantList &list) +{ + v8::Context::Scope scope(engine->context()); + v8::Local<v8::Array> result = v8::Array::New(list.count()); + for (int ii = 0; ii < list.count(); ++ii) + result->Set(ii, engine->fromVariant(list.at(ii))); + return result; +} + +static v8::Handle<v8::Object> objectFromVariantMap(QV8Engine *engine, const QVariantMap &map) +{ + v8::Context::Scope scope(engine->context()); + v8::Local<v8::Object> object = v8::Object::New(); + for (QVariantMap::ConstIterator iter = map.begin(); iter != map.end(); ++iter) + object->Set(engine->toString(iter.key()), engine->fromVariant(iter.value())); + return object; +} + +Q_CORE_EXPORT QString qt_regexp_toCanonical(const QString &, QRegExp::PatternSyntax); + +v8::Handle<v8::Value> QV8Engine::fromVariant(const QVariant &variant) +{ + int type = variant.userType(); + const void *ptr = variant.constData(); + + if (type < QMetaType::User) { + switch (QMetaType::Type(type)) { + case QMetaType::Void: + return v8::Undefined(); + case QMetaType::Bool: + return v8::Boolean::New(*reinterpret_cast<const bool*>(ptr)); + case QMetaType::Int: + return v8::Integer::New(*reinterpret_cast<const int*>(ptr)); + case QMetaType::UInt: + return v8::Integer::NewFromUnsigned(*reinterpret_cast<const uint*>(ptr)); + case QMetaType::LongLong: + return v8::Number::New(*reinterpret_cast<const qlonglong*>(ptr)); + case QMetaType::ULongLong: + return v8::Number::New(*reinterpret_cast<const qulonglong*>(ptr)); + case QMetaType::Double: + return v8::Number::New(*reinterpret_cast<const double*>(ptr)); + case QMetaType::QString: + return m_stringWrapper.toString(*reinterpret_cast<const QString*>(ptr)); + case QMetaType::Float: + return v8::Number::New(*reinterpret_cast<const float*>(ptr)); + case QMetaType::Short: + return v8::Integer::New(*reinterpret_cast<const short*>(ptr)); + case QMetaType::UShort: + return v8::Integer::NewFromUnsigned(*reinterpret_cast<const unsigned short*>(ptr)); + case QMetaType::Char: + return v8::Integer::New(*reinterpret_cast<const char*>(ptr)); + case QMetaType::UChar: + return v8::Integer::NewFromUnsigned(*reinterpret_cast<const unsigned char*>(ptr)); + case QMetaType::QChar: + return v8::Integer::New((*reinterpret_cast<const QChar*>(ptr)).unicode()); + case QMetaType::QDateTime: + return v8::Date::New(qtDateTimeToJsDate(*reinterpret_cast<const QDateTime *>(ptr))); + case QMetaType::QDate: + return v8::Date::New(qtDateTimeToJsDate(QDateTime(*reinterpret_cast<const QDate *>(ptr)))); + case QMetaType::QTime: + return v8::Date::New(qtDateTimeToJsDate(QDateTime(QDate(1970,1,1), *reinterpret_cast<const QTime *>(ptr)))); + case QMetaType::QRegExp: + return QJSConverter::toRegExp(*reinterpret_cast<const QRegExp *>(ptr)); + case QMetaType::QObjectStar: + case QMetaType::QWidgetStar: + return newQObject(*reinterpret_cast<QObject* const *>(ptr)); + case QMetaType::QStringList: + { + bool succeeded = false; + v8::Handle<v8::Value> retn = m_sequenceWrapper.fromVariant(variant, &succeeded); + if (succeeded) + return retn; + return arrayFromStringList(this, *reinterpret_cast<const QStringList *>(ptr)); + } + case QMetaType::QVariantList: + return arrayFromVariantList(this, *reinterpret_cast<const QVariantList *>(ptr)); + case QMetaType::QVariantMap: + return objectFromVariantMap(this, *reinterpret_cast<const QVariantMap *>(ptr)); + + default: + break; + } + + if (m_engine) { + if (QQmlValueType *vt = QQmlEnginePrivate::get(m_engine)->valueTypes[type]) + return m_valueTypeWrapper.newValueType(variant, vt); + } + + } else { + if (type == qMetaTypeId<QQmlListReference>()) { + typedef QQmlListReferencePrivate QDLRP; + QDLRP *p = QDLRP::get((QQmlListReference*)ptr); + if (p->object) { + return m_listWrapper.newList(p->property, p->propertyType); + } else { + return v8::Null(); + } + } else if (type == qMetaTypeId<QJSValue>()) { + const QJSValue *value = reinterpret_cast<const QJSValue *>(ptr); + QJSValuePrivate *valuep = QJSValuePrivate::get(*value); + if (valuep->assignEngine(this)) + return v8::Local<v8::Value>::New(*valuep); + } else if (type == qMetaTypeId<QList<QObject *> >()) { + // XXX Can this be made more by using Array as a prototype and implementing + // directly against QList<QObject*>? + const QList<QObject *> &list = *(QList<QObject *>*)ptr; + v8::Local<v8::Array> array = v8::Array::New(list.count()); + for (int ii = 0; ii < list.count(); ++ii) + array->Set(ii, newQObject(list.at(ii))); + return array; + } + + bool objOk; + QObject *obj = QQmlMetaType::toQObject(variant, &objOk); + if (objOk) + return newQObject(obj); + + bool succeeded = false; + v8::Handle<v8::Value> retn = m_sequenceWrapper.fromVariant(variant, &succeeded); + if (succeeded) + return retn; + } + + // XXX TODO: To be compatible, we still need to handle: + // + QObjectList + // + QList<int> + + return m_variantWrapper.newVariant(variant); +} + +// A handle scope and context must be entered +v8::Local<v8::Script> QV8Engine::qmlModeCompile(const QString &source, + const QString &fileName, + int lineNumber) +{ + v8::Local<v8::String> v8source = m_stringWrapper.toString(source); + v8::Local<v8::String> v8fileName = m_stringWrapper.toString(fileName); + + v8::ScriptOrigin origin(v8fileName, v8::Integer::New(lineNumber - 1)); + + v8::Local<v8::Script> script = v8::Script::Compile(v8source, &origin, 0, v8::Handle<v8::String>(), + v8::Script::QmlMode); + + return script; +} + +// A handle scope and context must be entered. +// source can be either ascii or utf8. +v8::Local<v8::Script> QV8Engine::qmlModeCompile(const char *source, int sourceLength, + const QString &fileName, + int lineNumber) +{ + if (sourceLength == -1) + sourceLength = strlen(source); + + v8::Local<v8::String> v8source = v8::String::New(source, sourceLength); + v8::Local<v8::String> v8fileName = m_stringWrapper.toString(fileName); + + v8::ScriptOrigin origin(v8fileName, v8::Integer::New(lineNumber - 1)); + + v8::Local<v8::Script> script = v8::Script::Compile(v8source, &origin, 0, v8::Handle<v8::String>(), + v8::Script::QmlMode); + + return script; +} + +QNetworkAccessManager *QV8Engine::networkAccessManager() +{ + return QQmlEnginePrivate::get(m_engine)->getNetworkAccessManager(); +} + +const QStringHash<bool> &QV8Engine::illegalNames() const +{ + return m_illegalNames; +} + +// Requires a handle scope +v8::Local<v8::Array> QV8Engine::getOwnPropertyNames(v8::Handle<v8::Object> o) +{ + // FIXME Newer v8 have API for this function + v8::TryCatch tc; + v8::Handle<v8::Value> args[] = { o }; + v8::Local<v8::Value> r = m_getOwnPropertyNames->Call(global(), 1, args); + if (tc.HasCaught()) + return v8::Array::New(); + else + return v8::Local<v8::Array>::Cast(r); +} + +QQmlContextData *QV8Engine::callingContext() +{ + return m_contextWrapper.callingContext(); +} + +// 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::toBasicVariant(v8::Handle<v8::Value> value) +{ + if (value->IsNull() || value->IsUndefined()) + return QVariant(); + if (value->IsBoolean()) + return value->ToBoolean()->Value(); + if (value->IsInt32()) + return value->ToInt32()->Value(); + if (value->IsNumber()) + return value->ToNumber()->Value(); + if (value->IsString()) + return m_stringWrapper.toString(value->ToString()); + if (value->IsDate()) + return qtDateTimeFromJsDate(v8::Handle<v8::Date>::Cast(value)->NumberValue()); + // NOTE: since we convert QTime to JS Date, round trip will change the variant type (to QDateTime)! + + Q_ASSERT(value->IsObject()); + + if (value->IsRegExp()) { + v8::Context::Scope scope(context()); + return QJSConverter::toRegExp(v8::Handle<v8::RegExp>::Cast(value)); + } + if (value->IsArray()) { + v8::Context::Scope scope(context()); + QVariantList rv; + + v8::Handle<v8::Array> array = v8::Handle<v8::Array>::Cast(value); + int length = array->Length(); + for (int ii = 0; ii < length; ++ii) + rv << toVariant(array->Get(ii), -1); + return rv; + } + if (!value->IsFunction()) { + v8::Context::Scope scope(context()); + v8::Handle<v8::Object> object = value->ToObject(); + return variantMapFromJS(object); + } + + return QVariant(); +} + + + +#include <QtGui/qvector3d.h> +#include <QtGui/qvector4d.h> + +struct StaticQtMetaObject : public QObject +{ + static const QMetaObject *get() + { return &static_cast<StaticQtMetaObject*> (0)->staticQtMetaObject; } +}; + +void QV8Engine::initializeGlobal(v8::Handle<v8::Object> global) +{ + using namespace QQmlBuiltinFunctions; + + v8::Local<v8::Object> console = v8::Object::New(); + v8::Local<v8::Function> consoleLogFn = V8FUNCTION(consoleLog, this); + + console->Set(v8::String::New("debug"), consoleLogFn); + console->Set(v8::String::New("log"), consoleLogFn); + console->Set(v8::String::New("info"), consoleLogFn); + console->Set(v8::String::New("warn"), V8FUNCTION(consoleWarn, this)); + console->Set(v8::String::New("error"), V8FUNCTION(consoleError, this)); + console->Set(v8::String::New("assert"), V8FUNCTION(consoleAssert, this)); + + console->Set(v8::String::New("count"), V8FUNCTION(consoleCount, this)); + console->Set(v8::String::New("profile"), V8FUNCTION(consoleProfile, this)); + console->Set(v8::String::New("profileEnd"), V8FUNCTION(consoleProfileEnd, this)); + console->Set(v8::String::New("time"), V8FUNCTION(consoleTime, this)); + console->Set(v8::String::New("timeEnd"), V8FUNCTION(consoleTimeEnd, this)); + console->Set(v8::String::New("trace"), V8FUNCTION(consoleTrace, this)); + console->Set(v8::String::New("exception"), V8FUNCTION(consoleException, this)); + + v8::Local<v8::Object> qt = v8::Object::New(); + + // Set all the enums from the "Qt" namespace + const QMetaObject *qtMetaObject = StaticQtMetaObject::get(); + for (int ii = 0; ii < qtMetaObject->enumeratorCount(); ++ii) { + QMetaEnum enumerator = qtMetaObject->enumerator(ii); + for (int jj = 0; jj < enumerator.keyCount(); ++jj) { + qt->Set(v8::String::New(enumerator.key(jj)), v8::Integer::New(enumerator.value(jj))); + } + } + qt->Set(v8::String::New("Asynchronous"), v8::Integer::New(0)); + qt->Set(v8::String::New("Synchronous"), v8::Integer::New(1)); + + 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)); + qt->Set(v8::String::New("hsla"), V8FUNCTION(hsla, this)); + qt->Set(v8::String::New("rect"), V8FUNCTION(rect, this)); + qt->Set(v8::String::New("point"), V8FUNCTION(point, this)); + qt->Set(v8::String::New("size"), V8FUNCTION(size, this)); + qt->Set(v8::String::New("vector3d"), V8FUNCTION(vector3d, this)); + qt->Set(v8::String::New("vector4d"), V8FUNCTION(vector4d, 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)); + + qt->Set(v8::String::New("openUrlExternally"), V8FUNCTION(openUrlExternally, this)); + qt->Set(v8::String::New("fontFamilies"), V8FUNCTION(fontFamilies, this)); + qt->Set(v8::String::New("md5"), V8FUNCTION(md5, this)); + qt->Set(v8::String::New("btoa"), V8FUNCTION(btoa, this)); + qt->Set(v8::String::New("atob"), V8FUNCTION(atob, this)); + qt->Set(v8::String::New("resolvedUrl"), V8FUNCTION(resolvedUrl, this)); + qt->Set(v8::String::New("locale"), V8FUNCTION(locale, this)); + + if (m_engine) { + qt->Set(v8::String::New("application"), newQObject(new QQuickApplication(m_engine))); + qt->Set(v8::String::New("inputMethod"), newQObject(qGuiApp->inputMethod(), CppOwnership)); + 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)); + } + + global->Set(v8::String::New("qsTranslate"), V8FUNCTION(qsTranslate, this)); + global->Set(v8::String::New("QT_TRANSLATE_NOOP"), V8FUNCTION(qsTranslateNoOp, this)); + global->Set(v8::String::New("qsTr"), V8FUNCTION(qsTr, this)); + global->Set(v8::String::New("QT_TR_NOOP"), V8FUNCTION(qsTrNoOp, this)); + global->Set(v8::String::New("qsTrId"), V8FUNCTION(qsTrId, this)); + global->Set(v8::String::New("QT_TRID_NOOP"), V8FUNCTION(qsTrIdNoOp, this)); + + global->Set(v8::String::New("print"), consoleLogFn); + global->Set(v8::String::New("console"), console); + global->Set(v8::String::New("Qt"), qt); + global->Set(v8::String::New("gc"), V8FUNCTION(QQmlBuiltinFunctions::gc, this)); + + { +#define STRING_ARG "(function(stringArg) { "\ + " String.prototype.arg = (function() {"\ + " return stringArg.apply(this, arguments);"\ + " })"\ + "})" + + v8::Local<v8::Script> registerArg = v8::Script::New(v8::String::New(STRING_ARG), 0, 0, v8::Handle<v8::String>(), v8::Script::NativeMode); + v8::Local<v8::Value> result = registerArg->Run(); + Q_ASSERT(result->IsFunction()); + v8::Local<v8::Function> registerArgFunc = v8::Local<v8::Function>::Cast(result); + v8::Handle<v8::Value> args = V8FUNCTION(stringArg, this); + registerArgFunc->Call(v8::Local<v8::Object>::Cast(registerArgFunc), 1, &args); +#undef STRING_ARG + } + + QQmlLocale::registerStringLocaleCompare(this); + QQmlDateExtension::registerExtension(this); + QQmlNumberExtension::registerExtension(this); + + qt_add_domexceptions(this); + m_xmlHttpRequestData = qt_add_qmlxmlhttprequest(this); + + qt_add_sqlexceptions(this); + + { + v8::Handle<v8::Value> args[] = { global }; + v8::Local<v8::Value> names = m_getOwnPropertyNames->Call(global, 1, args); + v8::Local<v8::Array> namesArray = v8::Local<v8::Array>::Cast(names); + for (quint32 ii = 0; ii < namesArray->Length(); ++ii) + m_illegalNames.insert(toString(namesArray->Get(ii)), true); + } + + { +#define FREEZE_SOURCE "(function freeze_recur(obj) { "\ + " if (Qt.isQtObject(obj)) return;"\ + " if (obj != Function.connect && obj != Function.disconnect && "\ + " obj instanceof Object) {"\ + " var properties = Object.getOwnPropertyNames(obj);"\ + " for (var prop in properties) { "\ + " if (prop == \"connect\" || prop == \"disconnect\") {"\ + " Object.freeze(obj[prop]); "\ + " continue;"\ + " }"\ + " freeze_recur(obj[prop]);"\ + " }"\ + " }"\ + " if (obj instanceof Object) {"\ + " Object.freeze(obj);"\ + " }"\ + "})" + + v8::Local<v8::Script> freeze = v8::Script::New(v8::String::New(FREEZE_SOURCE)); + v8::Local<v8::Value> result = freeze->Run(); + Q_ASSERT(result->IsFunction()); + m_freezeObject = qPersistentNew(v8::Local<v8::Function>::Cast(result)); +#undef FREEZE_SOURCE + } +} + +void QV8Engine::freezeObject(v8::Handle<v8::Value> value) +{ + v8::Handle<v8::Value> args[] = { value }; + m_freezeObject->Call(global(), 1, args); +} + +void QV8Engine::gc() +{ + v8::V8::LowMemoryNotification(); + while (!v8::V8::IdleNotification()) {} +} + +#ifdef QML_GLOBAL_HANDLE_DEBUGGING +#include <QtCore/qthreadstorage.h> +static QThreadStorage<QSet<void *> *> QV8Engine_activeHandles; + +void QV8Engine::registerHandle(void *handle) +{ + if (!handle) { + qWarning("Attempting to register a null handle"); + return; + } + + if (!QV8Engine_activeHandles.hasLocalData()) + QV8Engine_activeHandles.setLocalData(new QSet<void *>); + + if (QV8Engine_activeHandles.localData()->contains(handle)) { + qFatal("Handle %p already alive", handle); + } else { + QV8Engine_activeHandles.localData()->insert(handle); + } +} + +void QV8Engine::releaseHandle(void *handle) +{ + if (!handle) + return; + + if (!QV8Engine_activeHandles.hasLocalData()) + QV8Engine_activeHandles.setLocalData(new QSet<void *>); + + if (QV8Engine_activeHandles.localData()->contains(handle)) { + QV8Engine_activeHandles.localData()->remove(handle); + } else { + qFatal("Handle %p already dead", handle); + } +} +#endif + +struct QV8EngineRegistrationData +{ + QV8EngineRegistrationData() : extensionCount(0) {} + + QMutex mutex; + int extensionCount; +}; +Q_GLOBAL_STATIC(QV8EngineRegistrationData, registrationData); + +QMutex *QV8Engine::registrationMutex() +{ + return ®istrationData()->mutex; +} + +int QV8Engine::registerExtension() +{ + return registrationData()->extensionCount++; +} + +void QV8Engine::setExtensionData(int index, Deletable *data) +{ + if (m_extensionData.count() <= index) + m_extensionData.resize(index + 1); + + if (m_extensionData.at(index)) + delete m_extensionData.at(index); + + m_extensionData[index] = data; +} + +double QV8Engine::qtDateTimeToJsDate(const QDateTime &dt) +{ + // from QScriptEngine::DateTimeToMs() + if (!dt.isValid()) { + return qSNaN(); + } + QDateTime utc = dt.toUTC(); + QDate date = utc.date(); + QTime time = utc.time(); + QV8DateConverter::JSC::GregorianDateTime tm; + tm.year = date.year() - 1900; + tm.month = date.month() - 1; + tm.monthDay = date.day(); + tm.weekDay = date.dayOfWeek(); + tm.yearDay = date.dayOfYear(); + tm.hour = time.hour(); + tm.minute = time.minute(); + tm.second = time.second(); + return QV8DateConverter::JSC::gregorianDateTimeToMS(tm, time.msec()); +} + +v8::Persistent<v8::Object> *QV8Engine::findOwnerAndStrength(QObject *object, bool *shouldBeStrong) +{ + QObject *parent = object->parent(); + if (!parent) { + // if the object has JS ownership, the object's v8object owns the lifetime of the persistent value. + if (QQmlEngine::objectOwnership(object) == QQmlEngine::JavaScriptOwnership) { + *shouldBeStrong = false; + return &(QQmlData::get(object)->v8object); + } + + // no parent, and has CPP ownership - doesn't have an implicit parent. + *shouldBeStrong = true; + return 0; + } + + // if it is owned by CPP, it's root parent may still be owned by JS. + // in that case, the owner of the persistent handle is the root parent's v8object. + while (parent->parent()) + parent = parent->parent(); + + if (QQmlEngine::objectOwnership(parent) == QQmlEngine::JavaScriptOwnership) { + // root parent is owned by JS. It's v8object owns the persistent value in question. + *shouldBeStrong = false; + return &(QQmlData::get(parent)->v8object); + } else { + // root parent has CPP ownership. The persistent value should not be made weak. + *shouldBeStrong = true; + return 0; + } +} + +QDateTime QV8Engine::qtDateTimeFromJsDate(double jsDate) +{ + // from QScriptEngine::MsToDateTime() + if (qIsNaN(jsDate)) + return QDateTime(); + QV8DateConverter::JSC::GregorianDateTime tm; + QV8DateConverter::JSC::msToGregorianDateTime(jsDate, tm); + + // from QScriptEngine::MsFromTime() + int ms = int(::fmod(jsDate, 1000.0)); + if (ms < 0) + ms += int(1000.0); + + QDateTime convertedUTC = QDateTime(QDate(tm.year + 1900, tm.month + 1, tm.monthDay), + QTime(tm.hour, tm.minute, tm.second, ms), Qt::UTC); + return convertedUTC.toLocalTime(); +} + +void QV8Engine::addRelationshipForGC(QObject *object, v8::Persistent<v8::Value> handle) +{ + if (handle.IsEmpty()) + return; + + bool handleShouldBeStrong = false; + v8::Persistent<v8::Object> *implicitOwner = findOwnerAndStrength(object, &handleShouldBeStrong); + if (handleShouldBeStrong) { + v8::V8::AddImplicitReferences(m_strongReferencer, &handle, 1); + } else if (!implicitOwner->IsEmpty()) { + v8::V8::AddImplicitReferences(*implicitOwner, &handle, 1); + } +} + +void QV8Engine::addRelationshipForGC(QObject *object, QObject *other) +{ + bool handleShouldBeStrong = false; + v8::Persistent<v8::Object> *implicitOwner = findOwnerAndStrength(object, &handleShouldBeStrong); + v8::Persistent<v8::Value> handle = QQmlData::get(other, true)->v8object; + if (handleShouldBeStrong) { + v8::V8::AddImplicitReferences(m_strongReferencer, &handle, 1); + } else if (!implicitOwner->IsEmpty()) { + v8::V8::AddImplicitReferences(*implicitOwner, &handle, 1); + } +} + +static QThreadStorage<QV8Engine::ThreadData*> perThreadEngineData; + +bool QV8Engine::hasThreadData() +{ + return perThreadEngineData.hasLocalData(); +} + +QV8Engine::ThreadData *QV8Engine::threadData() +{ + Q_ASSERT(perThreadEngineData.hasLocalData()); + return perThreadEngineData.localData(); +} + +void QV8Engine::ensurePerThreadIsolate() +{ + if (!perThreadEngineData.hasLocalData()) + perThreadEngineData.setLocalData(new ThreadData); +} + +void QV8Engine::initDeclarativeGlobalObject() +{ + v8::HandleScope handels; + v8::Context::Scope contextScope(m_context); + initializeGlobal(m_context->Global()); + freezeObject(m_context->Global()); +} + +void QV8Engine::setEngine(QQmlEngine *engine) +{ + m_engine = engine; + initDeclarativeGlobalObject(); +} + +void QV8Engine::setException(v8::Handle<v8::Value> value, v8::Handle<v8::Message> msg) +{ + m_exception.set(value, msg); +} + +v8::Handle<v8::Value> QV8Engine::throwException(v8::Handle<v8::Value> value) +{ + setException(value); + v8::ThrowException(value); + return value; +} + +void QV8Engine::clearExceptions() +{ + m_exception.clear(); +} + +v8::Handle<v8::Value> QV8Engine::uncaughtException() const +{ + if (!hasUncaughtException()) + return v8::Handle<v8::Value>(); + 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<v8::Value> value, v8::Handle<v8::Message> message) +{ + Q_ASSERT_X(!value.IsEmpty(), Q_FUNC_INFO, "Throwing an empty value handle is highly suspected"); + clear(); + m_value = v8::Persistent<v8::Value>::New(value); + m_message = v8::Persistent<v8::Message>::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<v8::Value>() 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<v8::StackTrace> 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<v8::StackFrame> 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; +} + + +// 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::Local<v8::Array> QV8Engine::variantListToJS(const QVariantList &lst) +{ + v8::Local<v8::Array> 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<v8::Array> 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::Local<v8::Object> QV8Engine::variantMapToJS(const QVariantMap &vmap) +{ + v8::Local<v8::Object> 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<v8::Object> jsObject) +{ + QVariantMap result; + + v8::HandleScope handleScope; + v8::Handle<v8::Array> propertyNames = jsObject->GetPropertyNames(); + uint32_t length = propertyNames->Length(); + if (length == 0) + return result; + + int hash = jsObject->GetIdentityHash(); + if (visitedConversionObjects.contains(hash)) + return result; // Avoid recursion. + + visitedConversionObjects.insert(hash); + // TODO: Only object's own property names. Include non-enumerable properties. + for (uint32_t i = 0; i < length; ++i) { + v8::Handle<v8::Value> 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<v8::Value> QV8Engine::metaTypeToJS(int type, const void *data) +{ + Q_ASSERT(data != 0); + v8::Handle<v8::Value> 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<const bool*>(data)); + case QMetaType::Int: + return v8::Int32::New(*reinterpret_cast<const int*>(data)); + case QMetaType::UInt: + return v8::Uint32::New(*reinterpret_cast<const uint*>(data)); + case QMetaType::LongLong: + return v8::Number::New(double(*reinterpret_cast<const qlonglong*>(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<const qulonglong*>(data))); +#elif defined(Q_CC_MSVC) && !defined(Q_CC_MSVC_NET) + return v8::Number::New(double((qlonglong)*reinterpret_cast<const qulonglong*>(data))); +#else + return v8::Number::New(double(*reinterpret_cast<const qulonglong*>(data))); +#endif + case QMetaType::Double: + return v8::Number::New(double(*reinterpret_cast<const double*>(data))); + case QMetaType::QString: + return QJSConverter::toString(*reinterpret_cast<const QString*>(data)); + case QMetaType::Float: + return v8::Number::New(*reinterpret_cast<const float*>(data)); + case QMetaType::Short: + return v8::Int32::New(*reinterpret_cast<const short*>(data)); + case QMetaType::UShort: + return v8::Uint32::New(*reinterpret_cast<const unsigned short*>(data)); + case QMetaType::Char: + return v8::Int32::New(*reinterpret_cast<const char*>(data)); + case QMetaType::UChar: + return v8::Uint32::New(*reinterpret_cast<const unsigned char*>(data)); + case QMetaType::QChar: + return v8::Uint32::New((*reinterpret_cast<const QChar*>(data)).unicode()); + case QMetaType::QStringList: + result = QJSConverter::toStringList(*reinterpret_cast<const QStringList *>(data)); + break; + case QMetaType::QVariantList: + result = variantListToJS(*reinterpret_cast<const QVariantList *>(data)); + break; + case QMetaType::QVariantMap: + result = variantMapToJS(*reinterpret_cast<const QVariantMap *>(data)); + break; + case QMetaType::QDateTime: + result = QJSConverter::toDateTime(*reinterpret_cast<const QDateTime *>(data)); + break; + case QMetaType::QDate: + result = QJSConverter::toDateTime(QDateTime(*reinterpret_cast<const QDate *>(data))); + break; + case QMetaType::QRegExp: + result = QJSConverter::toRegExp(*reinterpret_cast<const QRegExp *>(data)); + break; + case QMetaType::QObjectStar: + case QMetaType::QWidgetStar: + result = newQObject(*reinterpret_cast<QObject* const *>(data)); + break; + case QMetaType::QVariant: + result = variantToJS(*reinterpret_cast<const QVariant*>(data)); + break; + default: + if (type == qMetaTypeId<QJSValue>()) { + return QJSValuePrivate::get(*reinterpret_cast<const QJSValue*>(data))->asV8Value(this); + } else { + QByteArray typeName = QMetaType::typeName(type); + if (typeName.endsWith('*') && !*reinterpret_cast<void* const *>(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<v8::Value> value, int type, void *data) { + // check if it's one of the types we know + switch (QMetaType::Type(type)) { + case QMetaType::Bool: + *reinterpret_cast<bool*>(data) = value->ToBoolean()->Value(); + return true; + case QMetaType::Int: + *reinterpret_cast<int*>(data) = value->ToInt32()->Value(); + return true; + case QMetaType::UInt: + *reinterpret_cast<uint*>(data) = value->ToUint32()->Value(); + return true; + case QMetaType::LongLong: + *reinterpret_cast<qlonglong*>(data) = qlonglong(value->ToInteger()->Value()); + return true; + case QMetaType::ULongLong: + *reinterpret_cast<qulonglong*>(data) = qulonglong(value->ToInteger()->Value()); + return true; + case QMetaType::Double: + *reinterpret_cast<double*>(data) = value->ToNumber()->Value(); + return true; + case QMetaType::QString: + if (value->IsUndefined() || value->IsNull()) + *reinterpret_cast<QString*>(data) = QString(); + else + *reinterpret_cast<QString*>(data) = QJSConverter::toString(value->ToString()); + return true; + case QMetaType::Float: + *reinterpret_cast<float*>(data) = value->ToNumber()->Value(); + return true; + case QMetaType::Short: + *reinterpret_cast<short*>(data) = short(value->ToInt32()->Value()); + return true; + case QMetaType::UShort: + *reinterpret_cast<unsigned short*>(data) = ushort(value->ToInt32()->Value()); // ### QScript::ToUInt16() + return true; + case QMetaType::Char: + *reinterpret_cast<char*>(data) = char(value->ToInt32()->Value()); + return true; + case QMetaType::UChar: + *reinterpret_cast<unsigned char*>(data) = (unsigned char)(value->ToInt32()->Value()); + return true; + case QMetaType::QChar: + if (value->IsString()) { + QString str = QJSConverter::toString(v8::Handle<v8::String>::Cast(value)); + *reinterpret_cast<QChar*>(data) = str.isEmpty() ? QChar() : str.at(0); + } else { + *reinterpret_cast<QChar*>(data) = QChar(ushort(value->ToInt32()->Value())); // ### QScript::ToUInt16() + } + return true; + case QMetaType::QDateTime: + if (value->IsDate()) { + *reinterpret_cast<QDateTime *>(data) = QJSConverter::toDateTime(v8::Handle<v8::Date>::Cast(value)); + return true; + } break; + case QMetaType::QDate: + if (value->IsDate()) { + *reinterpret_cast<QDate *>(data) = QJSConverter::toDateTime(v8::Handle<v8::Date>::Cast(value)).date(); + return true; + } break; + case QMetaType::QRegExp: + if (value->IsRegExp()) { + *reinterpret_cast<QRegExp *>(data) = QJSConverter::toRegExp(v8::Handle<v8::RegExp>::Cast(value)); + return true; + } break; + case QMetaType::QObjectStar: + if (isQObject(value) || value->IsNull()) { + *reinterpret_cast<QObject* *>(data) = qtObjectFromJS(value); + return true; + } break; + case QMetaType::QWidgetStar: + if (isQObject(value) || value->IsNull()) { + QObject *qo = qtObjectFromJS(value); + if (!qo || qo->isWidgetType()) { + *reinterpret_cast<QWidget* *>(data) = reinterpret_cast<QWidget*>(qo); + return true; + } + } break; + case QMetaType::QStringList: + if (value->IsArray()) { + *reinterpret_cast<QStringList *>(data) = QJSConverter::toStringList(v8::Handle<v8::Array>::Cast(value)); + return true; + } break; + case QMetaType::QVariantList: + if (value->IsArray()) { + *reinterpret_cast<QVariantList *>(data) = variantListFromJS(v8::Handle<v8::Array>::Cast(value)); + return true; + } break; + case QMetaType::QVariantMap: + if (value->IsObject()) { + *reinterpret_cast<QVariantMap *>(data) = variantMapFromJS(v8::Handle<v8::Object>::Cast(value)); + return true; + } break; + case QMetaType::QVariant: + *reinterpret_cast<QVariant*>(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; for compatibility with qscriptvalue_cast. + + QByteArray name = QMetaType::typeName(type); + if (convertToNativeQObject(value, name, reinterpret_cast<void* *>(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<void* *>(data) = var.data(); + return true; + } else { + // Look in the prototype chain. + v8::Handle<v8::Value> 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<void* *>(data) = *reinterpret_cast<void* *>(var.data()); + else + *reinterpret_cast<void* *>(data) = var.data(); + return true; + } + proto = proto->ToObject()->GetPrototype(); + } + } + } else if (value->IsNull() && name.endsWith('*')) { + *reinterpret_cast<void* *>(data) = 0; + return true; + } else if (type == qMetaTypeId<QJSValue>()) { + *reinterpret_cast<QJSValue*>(data) = QJSValuePrivate::get(new QJSValuePrivate(this, value)); + return true; + } + + return false; +} + +// Converts a QVariant to JS. +v8::Handle<v8::Value> 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<v8::Value> value) +{ + Q_ASSERT(!value.IsEmpty()); + if (value->IsNull() || value->IsUndefined()) + return QVariant(); + if (value->IsBoolean()) + return value->ToBoolean()->Value(); + if (value->IsInt32()) + return value->ToInt32()->Value(); + 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<v8::Array>::Cast(value)); + if (value->IsDate()) + return QJSConverter::toDateTime(v8::Handle<v8::Date>::Cast(value)); + if (value->IsRegExp()) + return QJSConverter::toRegExp(v8::Handle<v8::RegExp>::Cast(value)); + if (isVariant(value)) + return variantValue(value); + if (isQObject(value)) + return qVariantFromValue(qtObjectFromJS(value)); + return variantMapFromJS(value->ToObject()); +} + +bool QV8Engine::convertToNativeQObject(v8::Handle<v8::Value> 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<v8::Value> 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<QObject* const *>(variant.constData()); + } + return 0; +} + + +QVariant &QV8Engine::variantValue(v8::Handle<v8::Value> value) +{ + return variantWrapper()->variantValue(value); +} + +// Creates a QVariant wrapper object. +v8::Local<v8::Object> QV8Engine::newVariant(const QVariant &value) +{ + return variantWrapper()->newVariant(value); +} + +QScriptPassPointer<QJSValuePrivate> QV8Engine::evaluate(v8::Handle<v8::Script> script, v8::TryCatch& tryCatch) +{ + v8::HandleScope handleScope; + + clearExceptions(); + if (script.IsEmpty()) { + v8::Handle<v8::Value> exception = tryCatch.Exception(); + if (exception.IsEmpty()) { + // This is possible on syntax errors like { a:12, b:21 } <- missing "(", ")" around expression. + return new QJSValuePrivate(this); + } + setException(exception, tryCatch.Message()); + return new QJSValuePrivate(this, exception); + } + v8::Handle<v8::Value> result; + result = script->Run(); + if (result.IsEmpty()) { + v8::Handle<v8::Value> 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<v8::Value> value) const +{ + if (value.IsEmpty()) + return QJSValuePrivate::get(new QJSValuePrivate(const_cast<QV8Engine*>(this))); + return QJSValuePrivate::get(new QJSValuePrivate(const_cast<QV8Engine*>(this), value)); +} + +QScriptPassPointer<QJSValuePrivate> QV8Engine::newArray(uint length) +{ + return new QJSValuePrivate(this, v8::Array::New(length)); +} + +void QV8Engine::emitSignalHandlerException() +{ + emit q->signalHandlerException(scriptValueFromInternal(uncaughtException())); +} + +void QV8Engine::startTimer(const QString &timerName) +{ + if (!m_time.isValid()) + m_time.start(); + m_startedTimers[timerName] = m_time.elapsed(); +} + +qint64 QV8Engine::stopTimer(const QString &timerName, bool *wasRunning) +{ + if (!m_startedTimers.contains(timerName)) { + *wasRunning = false; + return 0; + } + *wasRunning = true; + qint64 startedAt = m_startedTimers.take(timerName); + return m_time.elapsed() - startedAt; +} + +int QV8Engine::consoleCountHelper(const QString &file, int line, int column) +{ + const QString key = file + QString::number(line) + QString::number(column); + int number = m_consoleCount.value(key, 0); + number++; + m_consoleCount.insert(key, number); + return number; +} + +void QV8GCCallback::registerGcPrologueCallback() +{ + QV8Engine::ThreadData *td = QV8Engine::threadData(); + if (!td->gcPrologueCallbackRegistered) { + td->gcPrologueCallbackRegistered = true; + v8::V8::AddGCPrologueCallback(QV8GCCallback::garbageCollectorPrologueCallback, v8::kGCTypeMarkSweepCompact); + } +} + +QV8GCCallback::Node::Node(PrologueCallback callback) + : prologueCallback(callback) +{ +} + +QV8GCCallback::Node::~Node() +{ + node.remove(); +} + +/* + Ensure that each persistent handle is strong if it has CPP ownership + and has no implicitly JS owned object owner in its parent chain, and + weak otherwise. + + Any weak handle whose parent object is still alive will have an implicit + reference (between the parent and the handle) added, so that it will + not be collected. + + Note that this callback is registered only for kGCTypeMarkSweepCompact + collection cycles, as it is during collection cycles of that type + in which weak persistent handle callbacks are called when required. + */ +void QV8GCCallback::garbageCollectorPrologueCallback(v8::GCType, v8::GCCallbackFlags) +{ + if (!QV8Engine::hasThreadData()) + return; + + QV8Engine::ThreadData *td = QV8Engine::threadData(); + QV8GCCallback::Node *currNode = td->gcCallbackNodes.first(); + + while (currNode) { + // The client which adds itself to the list is responsible + // for maintaining the correct implicit references in the + // specified callback. + currNode->prologueCallback(currNode); + currNode = td->gcCallbackNodes.next(currNode); + } +} + +void QV8GCCallback::addGcCallbackNode(QV8GCCallback::Node *node) +{ + QV8Engine::ThreadData *td = QV8Engine::threadData(); + td->gcCallbackNodes.insert(node); +} + +QV8Engine::ThreadData::ThreadData() + : gcPrologueCallbackRegistered(false) +{ + if (!v8::Isolate::GetCurrent()) { + isolate = v8::Isolate::New(); + isolate->Enter(); + } else { + isolate = 0; + } +} + +QV8Engine::ThreadData::~ThreadData() +{ + if (isolate) { + isolate->Exit(); + isolate->Dispose(); + isolate = 0; + } +} + +QT_END_NAMESPACE + diff --git a/src/qml/qml/v8/qv8engine_impl_p.h b/src/qml/qml/v8/qv8engine_impl_p.h new file mode 100644 index 0000000000..ebb21f851c --- /dev/null +++ b/src/qml/qml/v8/qv8engine_impl_p.h @@ -0,0 +1,155 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml 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 +** us via http://www.qt-project.org/. +** +** $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" +#include "qjsvalueiterator_p.h" + +QT_BEGIN_NAMESPACE + +inline v8::Handle<v8::Value> QV8Engine::makeJSValue(bool value) +{ + return value ? v8::True() : v8::False(); +} + +inline v8::Local<v8::Value> QV8Engine::makeJSValue(int value) +{ + return v8::Integer::New(value); +} + +inline v8::Local<v8::Value> QV8Engine::makeJSValue(uint value) +{ + return v8::Integer::NewFromUnsigned(value); +} + +inline v8::Local<v8::Value> QV8Engine::makeJSValue(double value) +{ + return v8::Number::New(value); +} + +inline v8::Handle<v8::Value> QV8Engine::makeJSValue(QJSValue::SpecialValue value) { + if (value == QJSValue::NullValue) + return v8::Null(); + return v8::Undefined(); +} + +inline v8::Local<v8::Value> QV8Engine::makeJSValue(const QString &value) +{ + return QJSConverter::toString(value); +} + +class QtScriptBagCleaner +{ +public: + template<class T> + void operator () (T* value) const + { + value->reinitialize(); + } + void operator () (QJSValueIteratorPrivate *iterator) const + { + iterator->invalidate(); + } +}; + +inline void QV8Engine::registerValue(QJSValuePrivate *data) +{ + m_values.insert(data); +} + +inline void QV8Engine::unregisterValue(QJSValuePrivate *data) +{ + m_values.remove(data); +} + +inline void QV8Engine::invalidateAllValues() +{ + ValueList::iterator it; + for (it = m_values.begin(); it != m_values.end(); it = it.erase()) + (*it)->invalidate(); + Q_ASSERT(m_values.isEmpty()); +} + +inline void QV8Engine::registerValueIterator(QJSValueIteratorPrivate *data) +{ + m_valueIterators.insert(data); +} + +inline void QV8Engine::unregisterValueIterator(QJSValueIteratorPrivate *data) +{ + m_valueIterators.remove(data); +} + +inline void QV8Engine::invalidateAllIterators() +{ + ValueIteratorList::iterator it; + for (it = m_valueIterators.begin(); it != m_valueIterators.end(); it = it.erase()) + (*it)->invalidate(); + Q_ASSERT(m_valueIterators.isEmpty()); +} + +/*! + \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 QJSValuePrivate::PropertyFlags QV8Engine::getPropertyFlags(v8::Handle<v8::Object> object, v8::Handle<v8::Value> property) +{ + QJSValuePrivate::PropertyFlags flags = m_originalGlobalObject.getPropertyFlags(object, property); + return flags; +} + +QScriptPassPointer<QJSValuePrivate> 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<v8::Script> 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<v8::Value> 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/qml/qml/v8/qv8engine_p.h b/src/qml/qml/v8/qv8engine_p.h new file mode 100644 index 0000000000..22a8d7599f --- /dev/null +++ b/src/qml/qml/v8/qv8engine_p.h @@ -0,0 +1,631 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLV8ENGINE_P_H +#define QQMLV8ENGINE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qglobal.h> +#include <QtCore/qvariant.h> +#include <QtCore/qset.h> +#include <QtCore/qmutex.h> +#include <QtCore/qstack.h> +#include <QtCore/qstringlist.h> +#include <QtCore/QElapsedTimer> +#include <QtCore/QThreadStorage> + +#include <private/qv8_p.h> +#include <qjsengine.h> +#include <qjsvalue.h> +#include "qjsvalue_p.h" +#include "qjsvalueiterator_p.h" +#include "qscriptoriginalglobalobject_p.h" +#include "qscripttools_p.h" + +#include <private/qqmlpropertycache_p.h> + +#include "qv8contextwrapper_p.h" +#include "qv8qobjectwrapper_p.h" +#include "qv8stringwrapper_p.h" +#include "qv8typewrapper_p.h" +#include "qv8listwrapper_p.h" +#include "qv8variantwrapper_p.h" +#include "qv8valuetypewrapper_p.h" +#include "qv8sequencewrapper_p.h" + +QT_BEGIN_NAMESPACE + + +// Uncomment the following line to enable global handle debugging. When enabled, all the persistent +// handles allocated using qPersistentNew() (or registered with qPersistentRegsiter()) and disposed +// with qPersistentDispose() are tracked. If you try and do something illegal, like double disposing +// a handle, qFatal() is called. +// #define QML_GLOBAL_HANDLE_DEBUGGING + +#define V8_RESOURCE_TYPE(resourcetype) \ +public: \ + enum { V8ResourceType = QV8ObjectResource:: resourcetype }; \ + virtual QV8ObjectResource::ResourceType resourceType() const { return QV8ObjectResource:: resourcetype; } \ +private: + +#define V8ENGINE() ((QV8Engine *)v8::External::Unwrap(args.Data())) +#define V8FUNCTION(function, engine) v8::FunctionTemplate::New(function, v8::External::Wrap((QV8Engine*)engine))->GetFunction() +#define V8THROW_ERROR(string) { \ + v8::ThrowException(v8::Exception::Error(v8::String::New(string))); \ + return v8::Handle<v8::Value>(); \ +} +#define V8THROW_TYPE(string) { \ + v8::ThrowException(v8::Exception::TypeError(v8::String::New(string))); \ + return v8::Handle<v8::Value>(); \ +} +#define V8ENGINE_ACCESSOR() ((QV8Engine *)v8::External::Unwrap(info.Data())); +#define V8THROW_ERROR_SETTER(string) { \ + v8::ThrowException(v8::Exception::Error(v8::String::New(string))); \ + return; \ +} + +#define V8_DEFINE_EXTENSION(dataclass, datafunction) \ + static inline dataclass *datafunction(QV8Engine *engine) \ + { \ + static int extensionId = -1; \ + if (extensionId == -1) { \ + QV8Engine::registrationMutex()->lock(); \ + if (extensionId == -1) \ + extensionId = QV8Engine::registerExtension(); \ + QV8Engine::registrationMutex()->unlock(); \ + } \ + dataclass *rv = (dataclass *)engine->extensionData(extensionId); \ + if (!rv) { \ + rv = new dataclass(engine); \ + engine->setExtensionData(extensionId, rv); \ + } \ + return rv; \ + } \ + + +class QV8Engine; +class QV8ObjectResource : public v8::Object::ExternalResource +{ +public: + QV8ObjectResource(QV8Engine *engine) : engine(engine) { Q_ASSERT(engine); } + enum ResourceType { ContextType, QObjectType, TypeType, ListType, VariantType, + ValueTypeType, XMLHttpRequestType, DOMNodeType, SQLDatabaseType, + ListModelType, Context2DType, Context2DStyleType, Context2DPixelArrayType, + ParticleDataType, SignalHandlerType, IncubatorType, VisualDataItemType, + SequenceType, LocaleDataType }; + virtual ResourceType resourceType() const = 0; + + QV8Engine *engine; +}; + +template<class T> +inline T *v8_resource_cast(v8::Handle<v8::Object> object) { + QV8ObjectResource *resource = static_cast<QV8ObjectResource *>(object->GetExternalResource()); + return (resource && (quint32)resource->resourceType() == (quint32)T::V8ResourceType)?static_cast<T *>(resource):0; +} + +template<class T> +inline T *v8_resource_check(v8::Handle<v8::Object> object) { + T *resource = static_cast<T *>(object->GetExternalResource()); + Q_ASSERT(resource && resource->resourceType() == (quint32)T::V8ResourceType); + return resource; +} + +// Used to allow a QObject method take and return raw V8 handles without having to expose +// v8 in the public API. +// Use like this: +// class MyClass : public QObject { +// Q_OBJECT +// ... +// Q_INVOKABLE void myMethod(QQmlV8Function*); +// }; +// The QQmlV8Function - and consequently the arguments and return value - only remains +// valid during the call. If the return value isn't set within myMethod(), the will return +// undefined. +class QV8Engine; +class QQmlV8Function +{ +public: + int Length() const { return _ac; } + v8::Local<v8::Value> operator[](int idx) { return (*_a)->Get(idx); } + QQmlContextData *context() { return _c; } + v8::Handle<v8::Object> qmlGlobal() { return *_g; } + void returnValue(v8::Handle<v8::Value> rv) { *_r = rv; } + QV8Engine *engine() const { return _e; } +private: + friend class QV8QObjectWrapper; + QQmlV8Function(); + QQmlV8Function(const QQmlV8Function &); + QQmlV8Function &operator=(const QQmlV8Function &); + + QQmlV8Function(int length, v8::Handle<v8::Object> &args, + v8::Handle<v8::Value> &rv, v8::Handle<v8::Object> &global, + QQmlContextData *c, QV8Engine *e) + : _ac(length), _a(&args), _r(&rv), _g(&global), _c(c), _e(e) {} + + int _ac; + v8::Handle<v8::Object> *_a; + v8::Handle<v8::Value> *_r; + v8::Handle<v8::Object> *_g; + QQmlContextData *_c; + QV8Engine *_e; +}; + +class QQmlV8Handle +{ +public: + QQmlV8Handle() : d(0) {} + QQmlV8Handle(const QQmlV8Handle &other) : d(other.d) {} + QQmlV8Handle &operator=(const QQmlV8Handle &other) { d = other.d; return *this; } + + static QQmlV8Handle fromHandle(v8::Handle<v8::Value> h) { + return QQmlV8Handle(*h); + } + v8::Handle<v8::Value> toHandle() const { + return v8::Handle<v8::Value>((v8::Value *)d); + } +private: + QQmlV8Handle(void *d) : d(d) {} + void *d; +}; + +class QObject; +class QQmlEngine; +class QQmlValueType; +class QNetworkAccessManager; +class QQmlContextData; + +class Q_AUTOTEST_EXPORT QV8GCCallback +{ +private: + class ThreadData; +public: + static void garbageCollectorPrologueCallback(v8::GCType, v8::GCCallbackFlags); + static void registerGcPrologueCallback(); + + class Q_AUTOTEST_EXPORT Node { + public: + typedef void (*PrologueCallback)(Node *node); + Node(PrologueCallback callback); + ~Node(); + + QIntrusiveListNode node; + PrologueCallback prologueCallback; + }; + + static void addGcCallbackNode(Node *node); +}; + +class Q_QML_EXPORT QV8Engine +{ +public: + 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); + virtual ~QV8Engine(); + + // This enum should be in sync with QQmlEngine::ObjectOwnership + enum ObjectOwnership { CppOwnership, JavaScriptOwnership }; + + struct Deletable { + virtual ~Deletable() {} + }; + + class Exception + { + typedef QPair<v8::Persistent<v8::Value>, v8::Persistent<v8::Message> > ValueMessagePair; + + v8::Persistent<v8::Value> m_value; + v8::Persistent<v8::Message> m_message; + QStack<ValueMessagePair> m_stack; + + Q_DISABLE_COPY(Exception) + public: + inline Exception(); + inline ~Exception(); + inline void set(v8::Handle<v8::Value> value, v8::Handle<v8::Message> message); + inline void clear(); + inline operator bool() const; + inline operator v8::Handle<v8::Value>() const; + inline int lineNumber() const; + inline QStringList backtrace() const; + + inline void push(); + inline void pop(); + }; + + void initDeclarativeGlobalObject(); + void setEngine(QQmlEngine *engine); + QQmlEngine *engine() { return m_engine; } + v8::Local<v8::Object> global() { return m_context->Global(); } + v8::Handle<v8::Context> context() const { return m_context; } + + inline void registerValue(QJSValuePrivate *data); + inline void unregisterValue(QJSValuePrivate *data); + inline void invalidateAllValues(); + + inline void registerValueIterator(QJSValueIteratorPrivate *data); + inline void unregisterValueIterator(QJSValueIteratorPrivate *data); + inline void invalidateAllIterators(); + + QV8ContextWrapper *contextWrapper() { return &m_contextWrapper; } + QV8QObjectWrapper *qobjectWrapper() { return &m_qobjectWrapper; } + QV8TypeWrapper *typeWrapper() { return &m_typeWrapper; } + QV8ListWrapper *listWrapper() { return &m_listWrapper; } + QV8VariantWrapper *variantWrapper() { return &m_variantWrapper; } + QV8ValueTypeWrapper *valueTypeWrapper() { return &m_valueTypeWrapper; } + QV8SequenceWrapper *sequenceWrapper() { return &m_sequenceWrapper; } + + void *xmlHttpRequestData() { return m_xmlHttpRequestData; } + + Deletable *listModelData() { return m_listModelData; } + void setListModelData(Deletable *d) { if (m_listModelData) delete m_listModelData; m_listModelData = d; } + + QQmlContextData *callingContext(); + + v8::Local<v8::Array> getOwnPropertyNames(v8::Handle<v8::Object>); + inline QJSValuePrivate::PropertyFlags getPropertyFlags(v8::Handle<v8::Object> object, v8::Handle<v8::Value> property); + void freezeObject(v8::Handle<v8::Value>); + + inline QString toString(v8::Handle<v8::Value> string); + inline QString toString(v8::Handle<v8::String> string); + static QString toStringStatic(v8::Handle<v8::Value>); + static QString toStringStatic(v8::Handle<v8::String>); + static inline bool startsWithUpper(v8::Handle<v8::String>); + + QVariant toVariant(v8::Handle<v8::Value>, int typeHint); + v8::Handle<v8::Value> fromVariant(const QVariant &); + inline bool isVariant(v8::Handle<v8::Value>); + + // Compile \a source (from \a fileName at \a lineNumber) in QML mode + v8::Local<v8::Script> qmlModeCompile(const QString &source, + const QString &fileName = QString(), + int lineNumber = 1); + v8::Local<v8::Script> qmlModeCompile(const char *source, int sourceLength = -1, + const QString &fileName = QString(), + int lineNumber = 1); + + // Return the QML global "scope" object for the \a ctxt context and \a scope object. + inline v8::Local<v8::Object> qmlScope(QQmlContextData *ctxt, QObject *scope); + + // Return a JS wrapper for the given QObject \a object + inline v8::Handle<v8::Value> newQObject(QObject *object); + inline v8::Handle<v8::Value> newQObject(QObject *object, const ObjectOwnership ownership); + inline bool isQObject(v8::Handle<v8::Value>); + inline QObject *toQObject(v8::Handle<v8::Value>); + + // Return a JS string for the given QString \a string + inline v8::Local<v8::String> toString(const QString &string); + + // Create a new value type object + inline v8::Handle<v8::Value> newValueType(QObject *, int coreIndex, QQmlValueType *); + inline v8::Handle<v8::Value> newValueType(const QVariant &, QQmlValueType *); + + // Create a new sequence type object + inline v8::Handle<v8::Value> newSequence(int sequenceType, QObject *, int coreIndex, bool *succeeded); + + // Create a new QVariant object. This doesn't examine the type of the variant, but always returns + // a QVariant wrapper + inline v8::Handle<v8::Value> newQVariant(const QVariant &); + + // Return the network access manager for this engine. By default this returns the network + // access manager of the QQmlEngine. It is overridden in the case of a threaded v8 + // instance (like in WorkerScript). + virtual QNetworkAccessManager *networkAccessManager(); + + // Return the list of illegal id names (the names of the properties on the global object) + const QStringHash<bool> &illegalNames() const; + + inline void collectGarbage() { gc(); } + static void gc(); + + void clearExceptions(); + void setException(v8::Handle<v8::Value> value, v8::Handle<v8::Message> message = v8::Handle<v8::Message>()); + v8::Handle<v8::Value> throwException(v8::Handle<v8::Value> value); + bool hasUncaughtException() const; + int uncaughtExceptionLineNumber() const; + QStringList uncaughtExceptionBacktrace() const; + v8::Handle<v8::Value> uncaughtException() const; + void saveException(); + void restoreException(); + +#ifdef QML_GLOBAL_HANDLE_DEBUGGING + // Used for handle debugging + static void registerHandle(void *); + static void releaseHandle(void *); +#endif + + static QMutex *registrationMutex(); + static int registerExtension(); + + inline Deletable *extensionData(int) const; + void setExtensionData(int, Deletable *); + + inline v8::Handle<v8::Value> makeJSValue(bool value); + inline v8::Local<v8::Value> makeJSValue(int value); + inline v8::Local<v8::Value> makeJSValue(uint value); + inline v8::Local<v8::Value> makeJSValue(double value); + inline v8::Handle<v8::Value> makeJSValue(QJSValue::SpecialValue value); + inline v8::Local<v8::Value> makeJSValue(const QString &value); + + inline QScriptPassPointer<QJSValuePrivate> evaluate(const QString &program, const QString &fileName = QString(), int lineNumber = 1); + QScriptPassPointer<QJSValuePrivate> evaluate(v8::Handle<v8::Script> script, v8::TryCatch& tryCatch); + + QScriptPassPointer<QJSValuePrivate> newArray(uint length); + v8::Local<v8::Object> newVariant(const QVariant &variant); + + v8::Local<v8::Array> variantListToJS(const QVariantList &lst); + QVariantList variantListFromJS(v8::Handle<v8::Array> jsArray); + + v8::Local<v8::Object> variantMapToJS(const QVariantMap &vmap); + QVariantMap variantMapFromJS(v8::Handle<v8::Object> jsObject); + + v8::Handle<v8::Value> variantToJS(const QVariant &value); + QVariant variantFromJS(v8::Handle<v8::Value> value); + + v8::Handle<v8::Value> metaTypeToJS(int type, const void *data); + bool metaTypeFromJS(v8::Handle<v8::Value> value, int type, void *data); + + bool convertToNativeQObject(v8::Handle<v8::Value> value, + const QByteArray &targetType, + void **result); + + QVariant &variantValue(v8::Handle<v8::Value> value); + + QJSValue scriptValueFromInternal(v8::Handle<v8::Value>) const; + + void emitSignalHandlerException(); + + // used for console.time(), console.timeEnd() + void startTimer(const QString &timerName); + qint64 stopTimer(const QString &timerName, bool *wasRunning); + + // used for console.count() + int consoleCountHelper(const QString &file, int line, int column); + + QObject *qtObjectFromJS(v8::Handle<v8::Value> value); + QSet<int> visitedConversionObjects; + + static QDateTime qtDateTimeFromJsDate(double jsDate); + + void addRelationshipForGC(QObject *object, v8::Persistent<v8::Value> handle); + void addRelationshipForGC(QObject *object, QObject *other); + + struct ThreadData { + ThreadData(); + ~ThreadData(); + v8::Isolate* isolate; + bool gcPrologueCallbackRegistered; + QIntrusiveList<QV8GCCallback::Node, &QV8GCCallback::Node::node> gcCallbackNodes; + }; + + static bool hasThreadData(); + static ThreadData* threadData(); + static void ensurePerThreadIsolate(); + + v8::Persistent<v8::Object> m_strongReferencer; + +protected: + QJSEngine* q; + QQmlEngine *m_engine; + bool m_ownsV8Context; + v8::Persistent<v8::Context> m_context; + QScriptOriginalGlobalObject m_originalGlobalObject; + + QV8StringWrapper m_stringWrapper; + QV8ContextWrapper m_contextWrapper; + QV8QObjectWrapper m_qobjectWrapper; + QV8TypeWrapper m_typeWrapper; + QV8ListWrapper m_listWrapper; + QV8VariantWrapper m_variantWrapper; + QV8ValueTypeWrapper m_valueTypeWrapper; + QV8SequenceWrapper m_sequenceWrapper; + + v8::Persistent<v8::Function> m_getOwnPropertyNames; + v8::Persistent<v8::Function> m_freezeObject; + + void *m_xmlHttpRequestData; + + QVector<Deletable *> m_extensionData; + Deletable *m_listModelData; + + QStringHash<bool> m_illegalNames; + + Exception m_exception; + + QElapsedTimer m_time; + QHash<QString, qint64> m_startedTimers; + + QHash<QString, quint32> m_consoleCount; + + QVariant toBasicVariant(v8::Handle<v8::Value>); + + void initializeGlobal(v8::Handle<v8::Object>); + + double qtDateTimeToJsDate(const QDateTime &dt); + +private: + static v8::Persistent<v8::Object> *findOwnerAndStrength(QObject *object, bool *shouldBeStrong); + + typedef QScriptIntrusiveList<QJSValuePrivate, &QJSValuePrivate::m_node> ValueList; + ValueList m_values; + typedef QScriptIntrusiveList<QJSValueIteratorPrivate, &QJSValueIteratorPrivate::m_node> ValueIteratorList; + ValueIteratorList m_valueIterators; + + Q_DISABLE_COPY(QV8Engine) +}; + +// Allocate a new Persistent handle. *ALL* persistent handles in QML must be allocated +// using this method. +template<class T> +v8::Persistent<T> qPersistentNew(v8::Handle<T> that) +{ + v8::Persistent<T> rv = v8::Persistent<T>::New(that); +#ifdef QML_GLOBAL_HANDLE_DEBUGGING + QV8Engine::registerHandle(*rv); +#endif + return rv; +} + +// Register a Persistent handle that was returned to you by V8 (such as by +// v8::Context::New). This allows us to do handle tracking on these handles too. +template<class T> +void qPersistentRegister(v8::Persistent<T> handle) +{ +#ifdef QML_GLOBAL_HANDLE_DEBUGGING + QV8Engine::registerHandle(*handle); +#else + Q_UNUSED(handle); +#endif +} + +// Dispose and clear a persistent handle. *ALL* persistent handles in QML must be +// disposed using this method. +template<class T> +void qPersistentDispose(v8::Persistent<T> &that) +{ +#ifdef QML_GLOBAL_HANDLE_DEBUGGING + QV8Engine::releaseHandle(*that); +#endif + that.Dispose(); + that.Clear(); +} + +QString QV8Engine::toString(v8::Handle<v8::Value> string) +{ + return m_stringWrapper.toString(string->ToString()); +} + +QString QV8Engine::toString(v8::Handle<v8::String> string) +{ + return m_stringWrapper.toString(string); +} + +bool QV8Engine::isVariant(v8::Handle<v8::Value> value) +{ + return m_variantWrapper.isVariant(value); +} + +v8::Local<v8::Object> QV8Engine::qmlScope(QQmlContextData *ctxt, QObject *scope) +{ + return m_contextWrapper.qmlScope(ctxt, scope); +} + +bool QV8Engine::isQObject(v8::Handle<v8::Value> obj) +{ + return obj->IsObject()?m_qobjectWrapper.isQObject(v8::Handle<v8::Object>::Cast(obj)):false; +} + +QObject *QV8Engine::toQObject(v8::Handle<v8::Value> obj) +{ + return obj->IsObject()?m_qobjectWrapper.toQObject(v8::Handle<v8::Object>::Cast(obj)):0; +} + +v8::Handle<v8::Value> QV8Engine::newQObject(QObject *object) +{ + return m_qobjectWrapper.newQObject(object); +} + +v8::Handle<v8::Value> QV8Engine::newQObject(QObject *object, const ObjectOwnership ownership) +{ + if (!object) + return v8::Null(); + + v8::Handle<v8::Value> result = newQObject(object); + QQmlData *ddata = QQmlData::get(object, true); + if (ownership == JavaScriptOwnership && ddata) { + ddata->indestructible = false; + ddata->explicitIndestructibleSet = true; + } + return result; +} + +v8::Local<v8::String> QV8Engine::toString(const QString &string) +{ + return m_stringWrapper.toString(string); +} + +v8::Handle<v8::Value> QV8Engine::newValueType(QObject *object, int property, QQmlValueType *type) +{ + return m_valueTypeWrapper.newValueType(object, property, type); +} + +v8::Handle<v8::Value> QV8Engine::newValueType(const QVariant &value, QQmlValueType *type) +{ + return m_valueTypeWrapper.newValueType(value, type); +} + +v8::Handle<v8::Value> QV8Engine::newSequence(int sequenceType, QObject *object, int property, bool *succeeded) +{ + return m_sequenceWrapper.newSequence(sequenceType, object, property, succeeded); +} + +// XXX Can this be made more optimal? It is called prior to resolving each and every +// unqualified name in QV8ContextWrapper. +bool QV8Engine::startsWithUpper(v8::Handle<v8::String> string) +{ + uint16_t c = string->GetCharacter(0); + return (c >= 'A' && c <= 'Z') || + (c > 127 && QChar::category(c) == QChar::Letter_Uppercase); +} + +QV8Engine::Deletable *QV8Engine::extensionData(int index) const +{ + if (index < m_extensionData.count()) + return m_extensionData[index]; + else + return 0; +} + +QT_END_NAMESPACE + +#endif // QQMLV8ENGINE_P_H diff --git a/src/qml/qml/v8/qv8include.cpp b/src/qml/qml/v8/qv8include.cpp new file mode 100644 index 0000000000..89f60f256e --- /dev/null +++ b/src/qml/qml/v8/qv8include.cpp @@ -0,0 +1,244 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv8include_p.h" + +#include <QtQml/qjsengine.h> +#include <QtNetwork/qnetworkrequest.h> +#include <QtNetwork/qnetworkreply.h> +#include <QtCore/qfile.h> + +#include <private/qqmlengine_p.h> + +QT_BEGIN_NAMESPACE + +QV8Include::QV8Include(const QUrl &url, QV8Engine *engine, QQmlContextData *context, + v8::Handle<v8::Object> qmlglobal, v8::Handle<v8::Function> callback) +: m_engine(engine), m_network(0), m_reply(0), m_url(url), m_redirectCount(0), m_context(context) +{ + m_qmlglobal = qPersistentNew<v8::Object>(qmlglobal); + if (!callback.IsEmpty()) + m_callbackFunction = qPersistentNew<v8::Function>(callback); + + m_resultObject = qPersistentNew<v8::Object>(resultValue()); + + m_network = engine->networkAccessManager(); + + QNetworkRequest request; + request.setUrl(url); + + m_reply = m_network->get(request); + QObject::connect(m_reply, SIGNAL(finished()), this, SLOT(finished())); +} + +QV8Include::~QV8Include() +{ + delete m_reply; m_reply = 0; + qPersistentDispose(m_callbackFunction); + qPersistentDispose(m_resultObject); +} + +v8::Local<v8::Object> QV8Include::resultValue(Status status) +{ + // XXX It seems inefficient to create this object from scratch each time. + v8::Local<v8::Object> result = v8::Object::New(); + result->Set(v8::String::New("OK"), v8::Integer::New(Ok)); + result->Set(v8::String::New("LOADING"), v8::Integer::New(Loading)); + result->Set(v8::String::New("NETWORK_ERROR"), v8::Integer::New(NetworkError)); + result->Set(v8::String::New("EXCEPTION"), v8::Integer::New(Exception)); + + result->Set(v8::String::New("status"), v8::Integer::New(status)); + + return result; +} + +void QV8Include::callback(QV8Engine *engine, v8::Handle<v8::Function> callback, v8::Handle<v8::Object> status) +{ + if (!callback.IsEmpty()) { + v8::Handle<v8::Value> args[] = { status }; + v8::TryCatch tc; + callback->Call(engine->global(), 1, args); + } +} + +v8::Handle<v8::Object> QV8Include::result() +{ + return m_resultObject; +} + +#define INCLUDE_MAXIMUM_REDIRECT_RECURSION 15 +void QV8Include::finished() +{ + m_redirectCount++; + + if (m_redirectCount < INCLUDE_MAXIMUM_REDIRECT_RECURSION) { + QVariant redirect = m_reply->attribute(QNetworkRequest::RedirectionTargetAttribute); + if (redirect.isValid()) { + m_url = m_url.resolved(redirect.toUrl()); + delete m_reply; + + QNetworkRequest request; + request.setUrl(m_url); + + m_reply = m_network->get(request); + QObject::connect(m_reply, SIGNAL(finished()), this, SLOT(finished())); + return; + } + } + + v8::HandleScope handle_scope; + + if (m_reply->error() == QNetworkReply::NoError) { + QByteArray data = m_reply->readAll(); + + QString code = QString::fromUtf8(data); + QQmlScript::Parser::extractPragmas(code); + + QQmlContextData *importContext = new QQmlContextData; + importContext->isInternal = true; + importContext->isJSContext = true; + importContext->url = m_url; + importContext->isPragmaLibraryContext = m_context->isPragmaLibraryContext; + importContext->setParent(m_context, true); + + v8::Context::Scope ctxtscope(m_engine->context()); + v8::TryCatch try_catch; + + v8::Local<v8::Script> script = m_engine->qmlModeCompile(code, m_url.toString()); + + if (!try_catch.HasCaught()) { + m_engine->contextWrapper()->addSubContext(m_qmlglobal, script, importContext); + script->Run(m_qmlglobal); + } + + if (try_catch.HasCaught()) { + m_resultObject->Set(v8::String::New("status"), v8::Integer::New(Exception)); + m_resultObject->Set(v8::String::New("exception"), try_catch.Exception()); + } else { + m_resultObject->Set(v8::String::New("status"), v8::Integer::New(Ok)); + } + } else { + m_resultObject->Set(v8::String::New("status"), v8::Integer::New(NetworkError)); + } + + callback(m_engine, m_callbackFunction, m_resultObject); + + disconnect(); + deleteLater(); +} + +/* + Documented in qv8engine.cpp +*/ +v8::Handle<v8::Value> QV8Include::include(const v8::Arguments &args) +{ + if (args.Length() == 0) + return v8::Undefined(); + + QV8Engine *engine = V8ENGINE(); + QQmlContextData *context = engine->callingContext(); + + if (!context || !context->isJSContext) + V8THROW_ERROR("Qt.include(): Can only be called from JavaScript files"); + + QUrl url(context->resolvedUrl(QUrl(engine->toString(args[0]->ToString())))); + + v8::Local<v8::Function> callbackFunction; + if (args.Length() >= 2 && args[1]->IsFunction()) + callbackFunction = v8::Local<v8::Function>::Cast(args[1]); + + QString localFile = QQmlEnginePrivate::urlToLocalFileOrQrc(url); + + v8::Local<v8::Object> result; + + if (localFile.isEmpty()) { + + QV8Include *i = new QV8Include(url, engine, context, + v8::Context::GetCallingQmlGlobal(), + callbackFunction); + result = v8::Local<v8::Object>::New(i->result()); + + } else { + + QFile f(localFile); + + if (f.open(QIODevice::ReadOnly)) { + QByteArray data = f.readAll(); + QString code = QString::fromUtf8(data); + QQmlScript::Parser::extractPragmas(code); + + QQmlContextData *importContext = new QQmlContextData; + importContext->isInternal = true; + importContext->isJSContext = true; + importContext->url = url; + importContext->setParent(context, true); + + v8::TryCatch try_catch; + + v8::Local<v8::Script> script = engine->qmlModeCompile(code, url.toString()); + + if (!try_catch.HasCaught()) { + v8::Local<v8::Object> qmlglobal = v8::Context::GetCallingQmlGlobal(); + engine->contextWrapper()->addSubContext(qmlglobal, script, importContext); + script->Run(qmlglobal); + } + + if (try_catch.HasCaught()) { + result = resultValue(Exception); + result->Set(v8::String::New("exception"), try_catch.Exception()); + } else { + result = resultValue(Ok); + } + + } else { + result = resultValue(NetworkError); + } + + callback(engine, callbackFunction, result); + } + + if (result.IsEmpty()) + return v8::Undefined(); + else + return result; +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/v8/qv8include_p.h b/src/qml/qml/v8/qv8include_p.h new file mode 100644 index 0000000000..f1e57b3eee --- /dev/null +++ b/src/qml/qml/v8/qv8include_p.h @@ -0,0 +1,113 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV8INCLUDE_P_H +#define QV8INCLUDE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qobject.h> +#include <QtCore/qurl.h> + +#include <private/qqmlcontext_p.h> +#include <private/qqmlguard_p.h> + +#include <private/qv8_p.h> + +QT_BEGIN_NAMESPACE + +class QQmlEngine; +class QNetworkAccessManager; +class QNetworkReply; +class QV8Engine; +class QV8Include : public QObject +{ + Q_OBJECT +public: + enum Status { + Ok = 0, + Loading = 1, + NetworkError = 2, + Exception = 3 + }; + + static v8::Handle<v8::Value> include(const v8::Arguments &args); + +private slots: + void finished(); + +private: + QV8Include(const QUrl &, QV8Engine *, QQmlContextData *, + v8::Handle<v8::Object>, v8::Handle<v8::Function>); + ~QV8Include(); + + v8::Handle<v8::Object> result(); + + static v8::Local<v8::Object> resultValue(Status status = Loading); + static void callback(QV8Engine *engine, v8::Handle<v8::Function> callback, v8::Handle<v8::Object> status); + + QV8Engine *m_engine; + QNetworkAccessManager *m_network; + QQmlGuard<QNetworkReply> m_reply; + + QUrl m_url; + int m_redirectCount; + + v8::Persistent<v8::Function> m_callbackFunction; + v8::Persistent<v8::Object> m_resultObject; + + QQmlGuardedContextData m_context; + v8::Persistent<v8::Object> m_qmlglobal; +}; + +QT_END_NAMESPACE + +#endif // QV8INCLUDE_P_H + diff --git a/src/qml/qml/v8/qv8listwrapper.cpp b/src/qml/qml/v8/qv8listwrapper.cpp new file mode 100644 index 0000000000..d6eab7af34 --- /dev/null +++ b/src/qml/qml/v8/qv8listwrapper.cpp @@ -0,0 +1,194 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv8listwrapper_p.h" +#include "qv8engine_p.h" +#include <private/qqmllist_p.h> + +QT_BEGIN_NAMESPACE + +class QV8ListResource : public QV8ObjectResource +{ + V8_RESOURCE_TYPE(ListType); +public: + QV8ListResource(QV8Engine *engine) : QV8ObjectResource(engine) {} + + QQmlGuard<QObject> object; + QQmlListProperty<QObject> property; + int propertyType; +}; + +QV8ListWrapper::QV8ListWrapper() +: m_engine(0) +{ +} + +QV8ListWrapper::~QV8ListWrapper() +{ +} + +void QV8ListWrapper::init(QV8Engine *engine) +{ + m_engine = engine; + v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New(); + ft->InstanceTemplate()->SetFallbackPropertyHandler(Getter, Setter, 0, 0, Enumerator); + ft->InstanceTemplate()->SetIndexedPropertyHandler(IndexedGetter); + ft->InstanceTemplate()->SetAccessor(v8::String::New("length"), LengthGetter, 0, + v8::Handle<v8::Value>(), v8::DEFAULT, + v8::PropertyAttribute(v8::ReadOnly | v8::DontDelete | v8::DontEnum)); + ft->InstanceTemplate()->SetHasExternalResource(true); + m_constructor = qPersistentNew<v8::Function>(ft->GetFunction()); +} + +void QV8ListWrapper::destroy() +{ + qPersistentDispose(m_constructor); +} + +v8::Handle<v8::Value> QV8ListWrapper::newList(QObject *object, int propId, int propType) +{ + if (!object || propId == -1) + return v8::Null(); + + // XXX NewInstance() should be optimized + v8::Local<v8::Object> rv = m_constructor->NewInstance(); + QV8ListResource *r = new QV8ListResource(m_engine); + r->object = object; + r->propertyType = propType; + void *args[] = { &r->property, 0 }; + QMetaObject::metacall(object, QMetaObject::ReadProperty, propId, args); + rv->SetExternalResource(r); + return rv; +} + +v8::Handle<v8::Value> QV8ListWrapper::newList(const QQmlListProperty<QObject> &prop, int propType) +{ + // XXX NewInstance() should be optimized + v8::Local<v8::Object> rv = m_constructor->NewInstance(); + QV8ListResource *r = new QV8ListResource(m_engine); + r->object = prop.object; + r->property = prop; + r->propertyType = propType; + rv->SetExternalResource(r); + return rv; +} + +QVariant QV8ListWrapper::toVariant(v8::Handle<v8::Object> obj) +{ + QV8ListResource *resource = v8_resource_cast<QV8ListResource>(obj); + if (resource) return toVariant(resource); + else return QVariant(); +} + +QVariant QV8ListWrapper::toVariant(QV8ObjectResource *r) +{ + Q_ASSERT(r->resourceType() == QV8ObjectResource::ListType); + QV8ListResource *resource = static_cast<QV8ListResource *>(r); + + if (!resource->object) + return QVariant(); + + return QVariant::fromValue(QQmlListReferencePrivate::init(resource->property, resource->propertyType, + m_engine->engine())); +} + +v8::Handle<v8::Value> QV8ListWrapper::Getter(v8::Local<v8::String> property, + const v8::AccessorInfo &info) +{ + Q_UNUSED(property); + Q_UNUSED(info); + return v8::Handle<v8::Value>(); +} + +v8::Handle<v8::Value> QV8ListWrapper::Setter(v8::Local<v8::String> property, + v8::Local<v8::Value> value, + const v8::AccessorInfo &info) +{ + Q_UNUSED(property); + Q_UNUSED(info); + return value; +} + +v8::Handle<v8::Value> QV8ListWrapper::IndexedGetter(uint32_t index, const v8::AccessorInfo &info) +{ + QV8ListResource *resource = v8_resource_cast<QV8ListResource>(info.This()); + + if (!resource || resource->object.isNull()) return v8::Undefined(); + + quint32 count = resource->property.count?resource->property.count(&resource->property):0; + if (index < count && resource->property.at) { + return resource->engine->newQObject(resource->property.at(&resource->property, index)); + } else { + return v8::Undefined(); + } +} + +v8::Handle<v8::Value> QV8ListWrapper::LengthGetter(v8::Local<v8::String> property, + const v8::AccessorInfo &info) +{ + Q_UNUSED(property); + + QV8ListResource *resource = v8_resource_cast<QV8ListResource>(info.This()); + + if (!resource || resource->object.isNull()) return v8::Undefined(); + + quint32 count = resource->property.count?resource->property.count(&resource->property):0; + + return v8::Integer::NewFromUnsigned(count); +} + +v8::Handle<v8::Array> QV8ListWrapper::Enumerator(const v8::AccessorInfo &info) +{ + QV8ListResource *resource = v8_resource_cast<QV8ListResource>(info.This()); + + if (!resource || resource->object.isNull()) return v8::Array::New(); + + quint32 count = resource->property.count?resource->property.count(&resource->property):0; + + v8::Local<v8::Array> rv = v8::Array::New(count); + + for (uint ii = 0; ii < count; ++ii) + rv->Set(ii, v8::Number::New(ii)); + + return rv; +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/v8/qv8listwrapper_p.h b/src/qml/qml/v8/qv8listwrapper_p.h new file mode 100644 index 0000000000..1e4bab06d5 --- /dev/null +++ b/src/qml/qml/v8/qv8listwrapper_p.h @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV8LISTWRAPPER_P_H +#define QV8LISTWRAPPER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qglobal.h> +#include <QtQml/qqmllist.h> +#include <private/qv8_p.h> + +QT_BEGIN_NAMESPACE + +class QV8Engine; +class QV8ObjectResource; +class QV8ListWrapper +{ +public: + QV8ListWrapper(); + ~QV8ListWrapper(); + + void init(QV8Engine *); + void destroy(); + + v8::Handle<v8::Value> newList(QObject *, int, int); + v8::Handle<v8::Value> newList(const QQmlListProperty<QObject> &, int); + QVariant toVariant(v8::Handle<v8::Object>); + QVariant toVariant(QV8ObjectResource *); + +private: + static v8::Handle<v8::Value> Getter(v8::Local<v8::String> property, + const v8::AccessorInfo &info); + static v8::Handle<v8::Value> Setter(v8::Local<v8::String> property, + v8::Local<v8::Value> value, + const v8::AccessorInfo &info); + static v8::Handle<v8::Value> IndexedGetter(uint32_t index, + const v8::AccessorInfo &info); + static v8::Handle<v8::Value> LengthGetter(v8::Local<v8::String> property, + const v8::AccessorInfo &info); + static v8::Handle<v8::Array> Enumerator(const v8::AccessorInfo &info); + + QV8Engine *m_engine; + v8::Persistent<v8::Function> m_constructor; +}; + +QT_END_NAMESPACE + +#endif // QV8LISTWRAPPER_P_H + diff --git a/src/qml/qml/v8/qv8profiler_p.h b/src/qml/qml/v8/qv8profiler_p.h new file mode 100644 index 0000000000..45df5a17c4 --- /dev/null +++ b/src/qml/qml/v8/qv8profiler_p.h @@ -0,0 +1,42 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <private/v8-profiler.h> diff --git a/src/qml/qml/v8/qv8qobjectwrapper.cpp b/src/qml/qml/v8/qv8qobjectwrapper.cpp new file mode 100644 index 0000000000..b84ae339be --- /dev/null +++ b/src/qml/qml/v8/qv8qobjectwrapper.cpp @@ -0,0 +1,2113 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv8qobjectwrapper_p.h" +#include "qv8contextwrapper_p.h" +#include "qv8engine_p.h" + +#include <private/qqmlguard_p.h> +#include <private/qqmlpropertycache_p.h> +#include <private/qqmlengine_p.h> +#include <private/qqmlvmemetaobject_p.h> +#include <private/qqmlbinding_p.h> +#include <private/qjsvalue_p.h> +#include <private/qscript_impl_p.h> +#include <private/qqmlaccessors_p.h> +#include <private/qqmlexpression_p.h> + +#include <QtQml/qjsvalue.h> +#include <QtCore/qvarlengtharray.h> +#include <QtCore/qtimer.h> +#include <QtCore/qatomic.h> + +Q_DECLARE_METATYPE(QJSValue); +Q_DECLARE_METATYPE(QQmlV8Handle); + +QT_BEGIN_NAMESPACE + +#if defined(__GNUC__) +# if (__GNUC__ * 100 + __GNUC_MINOR__) >= 405 +// The code in this file does not violate strict aliasing, but GCC thinks it does +// so turn off the warnings for us to have a clean build +# pragma GCC diagnostic ignored "-Wstrict-aliasing" +# endif +#endif + +#define QOBJECT_TOSTRING_INDEX -2 +#define QOBJECT_DESTROY_INDEX -3 + +// XXX TODO: Need to review all calls to QQmlEngine *engine() to confirm QObjects work +// correctly in a worker thread + +class QV8QObjectResource : public QV8ObjectResource +{ + V8_RESOURCE_TYPE(QObjectType); + +public: + QV8QObjectResource(QV8Engine *engine, QObject *object); + + QQmlGuard<QObject> object; +}; + +class QV8QObjectInstance : public QQmlGuard<QObject> +{ +public: + QV8QObjectInstance(QObject *o, QV8QObjectWrapper *w) + : QQmlGuard<QObject>(o), wrapper(w) + { + } + + ~QV8QObjectInstance() + { + qPersistentDispose(v8object); + } + + virtual void objectDestroyed(QObject *o) + { + if (wrapper) + wrapper->m_taintedObjects.remove(o); + delete this; + } + + v8::Persistent<v8::Object> v8object; + QV8QObjectWrapper *wrapper; +}; + +class QV8SignalHandlerResource : public QV8ObjectResource +{ + V8_RESOURCE_TYPE(SignalHandlerType) +public: + QV8SignalHandlerResource(QV8Engine *engine, QObject *object, int index); + + QQmlGuard<QObject> object; + int index; +}; + +namespace { + +template<typename A, typename B, typename C, typename D, typename E> +class MaxSizeOf5 { + template<typename Z, typename X> + struct SMax { + static const size_t Size = sizeof(Z) > sizeof(X) ? sizeof(Z) : sizeof(X); + }; +public: + static const size_t Size = SMax<A, SMax<B, SMax<C, SMax<D, E> > > >::Size; +}; + +struct CallArgument { + inline CallArgument(); + inline ~CallArgument(); + inline void *dataPtr(); + + inline void initAsType(int type); + inline void fromValue(int type, QV8Engine *, v8::Handle<v8::Value>); + inline v8::Handle<v8::Value> toValue(QV8Engine *); + +private: + CallArgument(const CallArgument &); + + inline void cleanup(); + + union { + float floatValue; + double doubleValue; + quint32 intValue; + bool boolValue; + QObject *qobjectPtr; + + char allocData[MaxSizeOf5<QVariant, + QString, + QList<QObject *>, + QJSValue, + QQmlV8Handle>::Size]; + qint64 q_for_alignment; + }; + + // Pointers to allocData + union { + QString *qstringPtr; + QVariant *qvariantPtr; + QList<QObject *> *qlistPtr; + QJSValue *qjsValuePtr; + QQmlV8Handle *handlePtr; + }; + + int type; +}; +} + +QV8QObjectResource::QV8QObjectResource(QV8Engine *engine, QObject *object) +: QV8ObjectResource(engine), object(object) +{ +} + +QV8SignalHandlerResource::QV8SignalHandlerResource(QV8Engine *engine, QObject *object, int index) +: QV8ObjectResource(engine), object(object), index(index) +{ +} + +static QAtomicInt objectIdCounter(1); + +QV8QObjectWrapper::QV8QObjectWrapper() +: m_engine(0), m_id(objectIdCounter.fetchAndAddOrdered(1)) +{ +} + +QV8QObjectWrapper::~QV8QObjectWrapper() +{ + for (TaintedHash::Iterator iter = m_taintedObjects.begin(); + iter != m_taintedObjects.end(); + ++iter) { + (*iter)->wrapper = 0; + } + m_taintedObjects.clear(); +} + +void QV8QObjectWrapper::destroy() +{ + qDeleteAll(m_connections); + m_connections.clear(); + + qPersistentDispose(m_hiddenObject); + qPersistentDispose(m_destroySymbol); + qPersistentDispose(m_toStringSymbol); + qPersistentDispose(m_signalHandlerConstructor); + qPersistentDispose(m_methodConstructor); + qPersistentDispose(m_constructor); +} + +struct ReadAccessor { + static inline void Indirect(QObject *object, const QQmlPropertyData &property, + void *output, QQmlNotifier **n) + { + Q_ASSERT(n == 0); + Q_UNUSED(n); + + void *args[] = { output, 0 }; + QMetaObject::metacall(object, QMetaObject::ReadProperty, property.coreIndex, args); + } + + static inline void Direct(QObject *object, const QQmlPropertyData &property, + void *output, QQmlNotifier **n) + { + Q_ASSERT(n == 0); + Q_UNUSED(n); + + void *args[] = { output, 0 }; + object->qt_metacall(QMetaObject::ReadProperty, property.coreIndex, args); + } + + static inline void Accessor(QObject *object, const QQmlPropertyData &property, + void *output, QQmlNotifier **n) + { + Q_ASSERT(property.accessors); + + property.accessors->read(object, property.accessorData, output); + if (n) property.accessors->notifier(object, property.accessorData, n); + } +}; + +static inline v8::Handle<v8::Value> valueToHandle(QV8Engine *, int v) +{ return v8::Integer::New(v); } +static inline v8::Handle<v8::Value> valueToHandle(QV8Engine *, uint v) +{ return v8::Integer::NewFromUnsigned(v); } +static inline v8::Handle<v8::Value> valueToHandle(QV8Engine *, bool v) +{ return v8::Boolean::New(v); } +static inline v8::Handle<v8::Value> valueToHandle(QV8Engine *e, const QString &v) +{ return e->toString(v); } +static inline v8::Handle<v8::Value> valueToHandle(QV8Engine *, float v) +{ return v8::Number::New(v); } +static inline v8::Handle<v8::Value> valueToHandle(QV8Engine *, double v) +{ return v8::Number::New(v); } +static inline v8::Handle<v8::Value> valueToHandle(QV8Engine *e, QObject *v) +{ return e->newQObject(v); } + +template<typename T, void (*ReadFunction)(QObject *, const QQmlPropertyData &, + void *, QQmlNotifier **)> +static v8::Handle<v8::Value> GenericValueGetter(v8::Local<v8::String>, const v8::AccessorInfo &info) +{ + v8::Handle<v8::Object> This = info.This(); + QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(This); + + QObject *object = resource->object; + if (!object) return v8::Undefined(); + + QQmlPropertyData *property = + (QQmlPropertyData *)v8::External::Unwrap(info.Data()); + + QQmlEngine *engine = resource->engine->engine(); + QQmlEnginePrivate *ep = engine?QQmlEnginePrivate::get(engine):0; + + T value = T(); + + if (ep && ep->propertyCapture) { + if (ReadFunction == ReadAccessor::Accessor && property->accessors->notifier) { + QQmlNotifier *notifier = 0; + ReadFunction(object, *property, &value, ¬ifier); + if (notifier) ep->captureProperty(notifier); + } else if (!property->isConstant()) { + ep->captureProperty(object, property->coreIndex, property->notifyIndex); + ReadFunction(object, *property, &value, 0); + } else { + ReadFunction(object, *property, &value, 0); + } + } else { + ReadFunction(object, *property, &value, 0); + } + + return valueToHandle(resource->engine, value); +} + +#define FAST_GETTER_FUNCTION(property, cpptype) \ + (property->hasAccessors()?((v8::AccessorGetter)GenericValueGetter<cpptype, &ReadAccessor::Accessor>):(property->isDirect()?((v8::AccessorGetter)GenericValueGetter<cpptype, &ReadAccessor::Direct>):((v8::AccessorGetter)GenericValueGetter<cpptype, &ReadAccessor::Indirect>))) + +static quint32 toStringHash = -1; +static quint32 destroyHash = -1; + +void QV8QObjectWrapper::init(QV8Engine *engine) +{ + m_engine = engine; + + m_toStringSymbol = qPersistentNew<v8::String>(v8::String::NewSymbol("toString")); + m_destroySymbol = qPersistentNew<v8::String>(v8::String::NewSymbol("destroy")); + m_hiddenObject = qPersistentNew<v8::Object>(v8::Object::New()); + + m_toStringString = QHashedV8String(m_toStringSymbol); + m_destroyString = QHashedV8String(m_destroySymbol); + + toStringHash = m_toStringString.hash(); + destroyHash = m_destroyString.hash(); + + { + v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New(); + ft->InstanceTemplate()->SetFallbackPropertyHandler(Getter, Setter, Query, 0, Enumerator); + ft->InstanceTemplate()->SetHasExternalResource(true); + m_constructor = qPersistentNew<v8::Function>(ft->GetFunction()); + } + { + v8::ScriptOrigin origin(m_hiddenObject); // Hack to allow us to identify these functions +#define CREATE_FUNCTION_SOURCE \ + "(function(method) { "\ + "return (function(object, data, qmlglobal) { "\ + "return (function() { "\ + "return method(object, data, qmlglobal, arguments.length, arguments); "\ + "});"\ + "});"\ + "})" + v8::Local<v8::Script> script = v8::Script::New(v8::String::New(CREATE_FUNCTION_SOURCE), &origin, 0, + v8::Handle<v8::String>(), v8::Script::NativeMode); +#undef CREATE_FUNCTION_SOURCE + v8::Local<v8::Function> fn = v8::Local<v8::Function>::Cast(script->Run()); + v8::Handle<v8::Value> invokeFn = v8::FunctionTemplate::New(Invoke)->GetFunction(); + v8::Handle<v8::Value> args[] = { invokeFn }; + v8::Local<v8::Function> createFn = v8::Local<v8::Function>::Cast(fn->Call(engine->global(), 1, args)); + m_methodConstructor = qPersistentNew<v8::Function>(createFn); + } + + v8::Local<v8::Function> connect = V8FUNCTION(Connect, engine); + v8::Local<v8::Function> disconnect = V8FUNCTION(Disconnect, engine); + + { + v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New(); + ft->InstanceTemplate()->SetHasExternalResource(true); + ft->PrototypeTemplate()->Set(v8::String::New("connect"), connect, v8::DontEnum); + ft->PrototypeTemplate()->Set(v8::String::New("disconnect"), disconnect, v8::DontEnum); + m_signalHandlerConstructor = qPersistentNew<v8::Function>(ft->GetFunction()); + } + + { + v8::Local<v8::Object> prototype = engine->global()->Get(v8::String::New("Function"))->ToObject()->Get(v8::String::New("prototype"))->ToObject(); + prototype->Set(v8::String::New("connect"), connect, v8::DontEnum); + prototype->Set(v8::String::New("disconnect"), disconnect, v8::DontEnum); + } +} + +bool QV8QObjectWrapper::isQObject(v8::Handle<v8::Object> obj) +{ + return v8_resource_cast<QV8QObjectResource>(obj) != 0; +} + +QObject *QV8QObjectWrapper::toQObject(v8::Handle<v8::Object> obj) +{ + QV8QObjectResource *r = v8_resource_cast<QV8QObjectResource>(obj); + return r?r->object:0; +} + +// r *MUST* be a QV8ObjectResource (r->type() == QV8ObjectResource::QObjectType) +QObject *QV8QObjectWrapper::toQObject(QV8ObjectResource *r) +{ + Q_ASSERT(r->resourceType() == QV8ObjectResource::QObjectType); + return static_cast<QV8QObjectResource *>(r)->object; +} + +// Load value properties +template<void (*ReadFunction)(QObject *, const QQmlPropertyData &, + void *, QQmlNotifier **)> +static v8::Handle<v8::Value> LoadProperty(QV8Engine *engine, QObject *object, + const QQmlPropertyData &property, + QQmlNotifier **notifier) +{ + Q_ASSERT(!property.isFunction()); + + if (property.isQObject()) { + QObject *rv = 0; + ReadFunction(object, property, &rv, notifier); + return engine->newQObject(rv); + } else if (property.isQList()) { + return engine->listWrapper()->newList(object, property.coreIndex, property.propType); + } else if (property.propType == QMetaType::QReal) { + qreal v = 0; + ReadFunction(object, property, &v, notifier); + return valueToHandle(engine, v); + } else if (property.propType == QMetaType::Int || property.isEnum()) { + int v = 0; + ReadFunction(object, property, &v, notifier); + return valueToHandle(engine, v); + } else if (property.propType == QMetaType::Bool) { + bool v = false; + ReadFunction(object, property, &v, notifier); + return valueToHandle(engine, v); + } else if (property.propType == QMetaType::QString) { + QString v; + ReadFunction(object, property, &v, notifier); + return valueToHandle(engine, v); + } else if (property.propType == QMetaType::UInt) { + uint v = 0; + ReadFunction(object, property, &v, notifier); + return valueToHandle(engine, v); + } else if (property.propType == QMetaType::Float) { + float v = 0; + ReadFunction(object, property, &v, notifier); + return valueToHandle(engine, v); + } else if (property.propType == QMetaType::Double) { + double v = 0; + ReadFunction(object, property, &v, notifier); + return valueToHandle(engine, v); + } else if (property.isV8Handle()) { + QQmlV8Handle handle; + ReadFunction(object, property, &handle, notifier); + return handle.toHandle(); + } else if (property.isQVariant()) { + QVariant v; + ReadFunction(object, property, &v, notifier); + return engine->fromVariant(v); + } else if (QQmlValueTypeFactory::isValueType((uint)property.propType) + && engine->engine()) { + Q_ASSERT(notifier == 0); + + QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine->engine()); + QQmlValueType *valueType = ep->valueTypes[property.propType]; + if (valueType) + return engine->newValueType(object, property.coreIndex, valueType); + } else { + Q_ASSERT(notifier == 0); + + // see if it's a sequence type + bool succeeded = false; + v8::Handle<v8::Value> retn = engine->newSequence(property.propType, object, property.coreIndex, + &succeeded); + if (succeeded) + return retn; + } + + if (property.propType == QVariant::Invalid) { + QMetaProperty p = object->metaObject()->property(property.coreIndex); + qWarning("QMetaProperty::read: Unable to handle unregistered datatype '%s' for property " + "'%s::%s'", p.typeName(), object->metaObject()->className(), p.name()); + return v8::Undefined(); + } else { + QVariant v(property.propType, (void *)0); + ReadFunction(object, property, v.data(), notifier); + return engine->fromVariant(v); + } +} + +v8::Handle<v8::Value> QV8QObjectWrapper::GetProperty(QV8Engine *engine, QObject *object, + v8::Handle<v8::Value> *objectHandle, + const QHashedV8String &property, + QV8QObjectWrapper::RevisionMode revisionMode) +{ + // XXX More recent versions of V8 introduced "Callable" objects. It is possible that these + // will be a faster way of creating QObject method objects. + struct MethodClosure { + static v8::Handle<v8::Value> create(QV8Engine *engine, QObject *object, + v8::Handle<v8::Value> *objectHandle, + int index) { + v8::Handle<v8::Value> argv[] = { + objectHandle?*objectHandle:engine->newQObject(object), + v8::Integer::New(index) + }; + return engine->qobjectWrapper()->m_methodConstructor->Call(engine->global(), 2, argv); + } + static v8::Handle<v8::Value> createWithGlobal(QV8Engine *engine, QObject *object, + v8::Handle<v8::Value> *objectHandle, + int index) { + v8::Handle<v8::Value> argv[] = { + objectHandle?*objectHandle:engine->newQObject(object), + v8::Integer::New(index), + v8::Context::GetCallingQmlGlobal() + }; + return engine->qobjectWrapper()->m_methodConstructor->Call(engine->global(), 3, argv); + } + }; + + { + // Comparing the hash first actually makes a measurable difference here, at least on x86 + quint32 hash = property.hash(); + if (hash == toStringHash && engine->qobjectWrapper()->m_toStringString == property) { + return MethodClosure::create(engine, object, objectHandle, QOBJECT_TOSTRING_INDEX); + } else if (hash == destroyHash && engine->qobjectWrapper()->m_destroyString == property) { + return MethodClosure::create(engine, object, objectHandle, QOBJECT_DESTROY_INDEX); + } + } + + QQmlPropertyData local; + QQmlPropertyData *result = 0; + { + QQmlData *ddata = QQmlData::get(object, false); + if (ddata && ddata->propertyCache) + result = ddata->propertyCache->property(property); + else + result = QQmlPropertyCache::property(engine->engine(), object, property, local); + } + + if (!result) + return v8::Handle<v8::Value>(); + + if (revisionMode == QV8QObjectWrapper::CheckRevision && result->revision != 0) { + QQmlData *ddata = QQmlData::get(object); + if (ddata && ddata->propertyCache && !ddata->propertyCache->isAllowedInRevision(result)) + return v8::Handle<v8::Value>(); + } + + if (result->isFunction()) { + if (result->isVMEFunction()) { + return ((QQmlVMEMetaObject *)(object->metaObject()))->vmeMethod(result->coreIndex); + } else if (result->isV8Function()) { + return MethodClosure::createWithGlobal(engine, object, objectHandle, result->coreIndex); + } else if (result->isSignalHandler()) { + v8::Local<v8::Object> handler = engine->qobjectWrapper()->m_signalHandlerConstructor->NewInstance(); + QV8SignalHandlerResource *r = new QV8SignalHandlerResource(engine, object, result->coreIndex); + handler->SetExternalResource(r); + return handler; + } else { + return MethodClosure::create(engine, object, objectHandle, result->coreIndex); + } + } + + QQmlEnginePrivate *ep = + engine->engine()?QQmlEnginePrivate::get(engine->engine()):0; + + if (result->hasAccessors()) { + QQmlNotifier *n = 0; + QQmlNotifier **nptr = 0; + + if (ep && ep->propertyCapture && result->accessors->notifier) + nptr = &n; + + v8::Handle<v8::Value> rv = LoadProperty<ReadAccessor::Accessor>(engine, object, *result, nptr); + + if (result->accessors->notifier) { + if (n) ep->captureProperty(n); + } else { + ep->captureProperty(object, result->coreIndex, result->notifyIndex); + } + + return rv; + } + + if (ep && !result->isConstant()) { + + if (result->coreIndex == 0) + ep->captureProperty(QQmlData::get(object, true)->objectNameNotifier()); + else + ep->captureProperty(object, result->coreIndex, result->notifyIndex); + } + + if (result->isVMEProperty()) { + typedef QQmlVMEMetaObject VMEMO; + VMEMO *vmemo = const_cast<VMEMO *>(static_cast<const VMEMO *>(object->metaObject())); + return vmemo->vmeProperty(result->coreIndex); + } else if (result->isDirect()) { + return LoadProperty<ReadAccessor::Direct>(engine, object, *result, 0); + } else { + return LoadProperty<ReadAccessor::Indirect>(engine, object, *result, 0); + } +} + +// Setter for writable properties. Shared between the interceptor and fast property accessor +static inline void StoreProperty(QV8Engine *engine, QObject *object, QQmlPropertyData *property, + v8::Handle<v8::Value> value) +{ + QQmlBinding *newBinding = 0; + + if (value->IsFunction()) { + QQmlContextData *context = engine->callingContext(); + v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(value); + + v8::Local<v8::StackTrace> trace = + v8::StackTrace::CurrentStackTrace(1, (v8::StackTrace::StackTraceOptions)(v8::StackTrace::kLineNumber | + v8::StackTrace::kScriptName)); + v8::Local<v8::StackFrame> frame = trace->GetFrame(0); + int lineNumber = frame->GetLineNumber(); + int columNumber = frame->GetColumn(); + QString url = engine->toString(frame->GetScriptName()); + + newBinding = new QQmlBinding(&function, object, context); + newBinding->setSourceLocation(url, lineNumber, columNumber); + newBinding->setTarget(object, *property, context); + newBinding->setEvaluateFlags(newBinding->evaluateFlags() | + QQmlBinding::RequiresThisObject); + } + + QQmlAbstractBinding *oldBinding = + QQmlPropertyPrivate::setBinding(object, property->coreIndex, -1, newBinding); + if (oldBinding) + oldBinding->destroy(); + +#define PROPERTY_STORE(cpptype, value) \ + cpptype o = value; \ + int status = -1; \ + int flags = 0; \ + void *argv[] = { &o, 0, &status, &flags }; \ + QMetaObject::metacall(object, QMetaObject::WriteProperty, property->coreIndex, argv); + + + if (value->IsNull() && property->isQObject()) { + PROPERTY_STORE(QObject*, 0); + } else if (value->IsUndefined() && property->isResettable()) { + void *a[] = { 0 }; + QMetaObject::metacall(object, QMetaObject::ResetProperty, property->coreIndex, a); + } else if (value->IsUndefined() && property->propType == qMetaTypeId<QVariant>()) { + PROPERTY_STORE(QVariant, QVariant()); + } else if (value->IsUndefined()) { + QString error = QLatin1String("Cannot assign [undefined] to ") + + QLatin1String(QMetaType::typeName(property->propType)); + v8::ThrowException(v8::Exception::Error(engine->toString(error))); + } else if (value->IsFunction()) { + // this is handled by the binding creation above + } else if (property->propType == QMetaType::Int && value->IsNumber()) { + PROPERTY_STORE(int, qRound(value->ToNumber()->Value())); + } else if (property->propType == QMetaType::QReal && value->IsNumber()) { + PROPERTY_STORE(qreal, qreal(value->ToNumber()->Value())); + } else if (property->propType == QMetaType::Float && value->IsNumber()) { + PROPERTY_STORE(float, float(value->ToNumber()->Value())); + } else if (property->propType == QMetaType::Double && value->IsNumber()) { + PROPERTY_STORE(double, double(value->ToNumber()->Value())); + } else if (property->propType == QMetaType::QString && value->IsString()) { + PROPERTY_STORE(QString, engine->toString(value->ToString())); + } else if (property->isVMEProperty()) { + static_cast<QQmlVMEMetaObject *>(const_cast<QMetaObject *>(object->metaObject()))->setVMEProperty(property->coreIndex, value); + } else { + QVariant v; + if (property->isQList()) + v = engine->toVariant(value, qMetaTypeId<QList<QObject *> >()); + else + v = engine->toVariant(value, property->propType); + + QQmlContextData *context = engine->callingContext(); + if (!QQmlPropertyPrivate::write(object, *property, v, context)) { + const char *valueType = 0; + if (v.userType() == QVariant::Invalid) valueType = "null"; + else valueType = QMetaType::typeName(v.userType()); + + QString error = QLatin1String("Cannot assign ") + + QLatin1String(valueType) + + QLatin1String(" to ") + + QLatin1String(QMetaType::typeName(property->propType)); + v8::ThrowException(v8::Exception::Error(engine->toString(error))); + } + } +} + +bool QV8QObjectWrapper::SetProperty(QV8Engine *engine, QObject *object, const QHashedV8String &property, + v8::Handle<v8::Value> value, QV8QObjectWrapper::RevisionMode revisionMode) +{ + if (engine->qobjectWrapper()->m_toStringString == property || + engine->qobjectWrapper()->m_destroyString == property) + return true; + + QQmlPropertyData local; + QQmlPropertyData *result = 0; + result = QQmlPropertyCache::property(engine->engine(), object, property, local); + + if (!result) + return false; + + if (revisionMode == QV8QObjectWrapper::CheckRevision && result->revision != 0) { + QQmlData *ddata = QQmlData::get(object); + if (ddata && ddata->propertyCache && !ddata->propertyCache->isAllowedInRevision(result)) + return false; + } + + if (!result->isWritable() && !result->isQList()) { + QString error = QLatin1String("Cannot assign to read-only property \"") + + engine->toString(property.string()) + QLatin1Char('\"'); + v8::ThrowException(v8::Exception::Error(engine->toString(error))); + return true; + } + + StoreProperty(engine, object, result, value); + + return true; +} + +v8::Handle<v8::Value> QV8QObjectWrapper::Getter(v8::Local<v8::String> property, + const v8::AccessorInfo &info) +{ + QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This()); + + if (resource->object.isNull()) + return v8::Handle<v8::Value>(); + + QObject *object = resource->object; + + QHashedV8String propertystring(property); + + QV8Engine *v8engine = resource->engine; + v8::Handle<v8::Value> This = info.This(); + v8::Handle<v8::Value> result = GetProperty(v8engine, object, &This, propertystring, + QV8QObjectWrapper::IgnoreRevision); + if (!result.IsEmpty()) + return result; + + if (QV8Engine::startsWithUpper(property)) { + // Check for attached properties + QQmlContextData *context = v8engine->callingContext(); + + if (context && context->imports) { + QQmlTypeNameCache::Result r = context->imports->query(propertystring); + + if (r.isValid()) { + if (r.scriptIndex != -1) { + return v8::Undefined(); + } else if (r.type) { + return v8engine->typeWrapper()->newObject(object, r.type, QV8TypeWrapper::ExcludeEnums); + } else if (r.importNamespace) { + return v8engine->typeWrapper()->newObject(object, context->imports, r.importNamespace, + QV8TypeWrapper::ExcludeEnums); + } + Q_ASSERT(!"Unreachable"); + } + } + } + + return v8::Handle<v8::Value>(); +} + +v8::Handle<v8::Value> QV8QObjectWrapper::Setter(v8::Local<v8::String> property, + v8::Local<v8::Value> value, + const v8::AccessorInfo &info) +{ + QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This()); + + if (resource->object.isNull()) + return value; + + QObject *object = resource->object; + + QHashedV8String propertystring(property); + + QV8Engine *v8engine = resource->engine; + bool result = SetProperty(v8engine, object, propertystring, value, QV8QObjectWrapper::IgnoreRevision); + + if (!result) { + QString error = QLatin1String("Cannot assign to non-existent property \"") + + v8engine->toString(property) + QLatin1Char('\"'); + v8::ThrowException(v8::Exception::Error(v8engine->toString(error))); + return value; + } + + return value; +} + +v8::Handle<v8::Integer> QV8QObjectWrapper::Query(v8::Local<v8::String> property, + const v8::AccessorInfo &info) +{ + QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This()); + + if (resource->object.isNull()) + return v8::Handle<v8::Integer>(); + + QV8Engine *engine = resource->engine; + QObject *object = resource->object; + + QHashedV8String propertystring(property); + + QQmlPropertyData local; + QQmlPropertyData *result = 0; + result = QQmlPropertyCache::property(engine->engine(), object, propertystring, local); + + if (!result) + return v8::Handle<v8::Integer>(); + else if (!result->isWritable() && !result->isQList()) + return v8::Integer::New(v8::ReadOnly | v8::DontDelete); + else + return v8::Integer::New(v8::DontDelete); +} + +v8::Handle<v8::Array> QV8QObjectWrapper::Enumerator(const v8::AccessorInfo &info) +{ + QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This()); + + if (resource->object.isNull()) + return v8::Array::New(); + + QObject *object = resource->object; + + QStringList result; + + QQmlEnginePrivate *ep = resource->engine->engine() + ? QQmlEnginePrivate::get(resource->engine->engine()) + : 0; + + QQmlPropertyCache *cache = 0; + QQmlData *ddata = QQmlData::get(object); + if (ddata) + cache = ddata->propertyCache; + + if (!cache) { + cache = ep ? ep->cache(object) : 0; + if (cache) { + if (ddata) { cache->addref(); ddata->propertyCache = cache; } + } else { + // Not cachable - fall back to QMetaObject (eg. dynamic meta object) + const QMetaObject *mo = object->metaObject(); + int pc = mo->propertyCount(); + int po = mo->propertyOffset(); + for (int i=po; i<pc; ++i) + result << QString::fromUtf8(mo->property(i).name()); + } + } else { + result = cache->propertyNames(); + } + + v8::Local<v8::Array> rv = v8::Array::New(result.count()); + + for (int ii = 0; ii < result.count(); ++ii) + rv->Set(ii, resource->engine->toString(result.at(ii))); + + return rv; +} + +static void FastValueSetter(v8::Local<v8::String>, v8::Local<v8::Value> value, + const v8::AccessorInfo& info) +{ + QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This()); + + if (resource->object.isNull()) + return; + + QObject *object = resource->object; + + QQmlPropertyData *property = + (QQmlPropertyData *)v8::External::Unwrap(info.Data()); + + int index = property->coreIndex; + + QQmlData *ddata = QQmlData::get(object, false); + Q_ASSERT(ddata); + Q_ASSERT(ddata->propertyCache); + + QQmlPropertyData *pdata = ddata->propertyCache->property(index); + Q_ASSERT(pdata); + + Q_ASSERT(pdata->isWritable() || pdata->isQList()); + + StoreProperty(resource->engine, object, pdata, value); +} + +static void FastValueSetterReadOnly(v8::Local<v8::String> property, v8::Local<v8::Value>, + const v8::AccessorInfo& info) +{ + QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(info.This()); + + if (resource->object.isNull()) + return; + + QV8Engine *v8engine = resource->engine; + + QString error = QLatin1String("Cannot assign to read-only property \"") + + v8engine->toString(property) + QLatin1Char('\"'); + v8::ThrowException(v8::Exception::Error(v8engine->toString(error))); +} + +static void WeakQObjectReferenceCallback(v8::Persistent<v8::Value> handle, void *) +{ + Q_ASSERT(handle->IsObject()); + + QV8QObjectResource *resource = v8_resource_check<QV8QObjectResource>(handle->ToObject()); + + Q_ASSERT(resource); + + QObject *object = resource->object; + if (object) { + QQmlData *ddata = QQmlData::get(object, false); + if (ddata) { + ddata->v8object.Clear(); + if (!object->parent() && !ddata->indestructible) + object->deleteLater(); + } + } + + qPersistentDispose(handle); +} + +static void WeakQObjectInstanceCallback(v8::Persistent<v8::Value> handle, void *data) +{ + QV8QObjectInstance *instance = (QV8QObjectInstance *)data; + instance->v8object.Clear(); + qPersistentDispose(handle); +} + +v8::Local<v8::Object> QQmlPropertyCache::newQObject(QObject *object, QV8Engine *engine) +{ + Q_ASSERT(object); + Q_ASSERT(this->engine); + + Q_ASSERT(QQmlData::get(object, false)); + Q_ASSERT(QQmlData::get(object, false)->propertyCache == this); + + // Setup constructor + if (constructor.IsEmpty()) { + v8::Local<v8::FunctionTemplate> ft; + + QString toString = QLatin1String("toString"); + QString destroy = QLatin1String("destroy"); + + // As we use hash linking, it is possible that iterating over the values can give duplicates. + // To combat this, we must unique'ify our properties. + StringCache uniqueHash; + if (stringCache.isLinked()) + uniqueHash.reserve(stringCache.count()); + + // XXX TODO: Enables fast property accessors. These more than double the property access + // performance, but the cost of setting up this structure hasn't been measured so + // its not guarenteed that this is a win overall. We need to try and measure the cost. + for (StringCache::ConstIterator iter = stringCache.begin(); iter != stringCache.end(); ++iter) { + if (stringCache.isLinked()) { + if (uniqueHash.contains(iter)) + continue; + uniqueHash.insert(iter); + } + + QQmlPropertyData *property = *iter; + if (property->notFullyResolved()) resolve(property); + + if (property->isFunction()) + continue; + + v8::AccessorGetter fastgetter = 0; + v8::AccessorSetter fastsetter = FastValueSetter; + if (!property->isWritable()) + fastsetter = FastValueSetterReadOnly; + + if (property->isQObject()) + fastgetter = FAST_GETTER_FUNCTION(property, QObject*); + else if (property->propType == QMetaType::Int || property->isEnum()) + fastgetter = FAST_GETTER_FUNCTION(property, int); + else if (property->propType == QMetaType::Bool) + fastgetter = FAST_GETTER_FUNCTION(property, bool); + else if (property->propType == QMetaType::QString) + fastgetter = FAST_GETTER_FUNCTION(property, QString); + else if (property->propType == QMetaType::UInt) + fastgetter = FAST_GETTER_FUNCTION(property, uint); + else if (property->propType == QMetaType::Float) + fastgetter = FAST_GETTER_FUNCTION(property, float); + else if (property->propType == QMetaType::Double) + fastgetter = FAST_GETTER_FUNCTION(property, double); + + if (fastgetter) { + QString name = iter.key(); + if (name == toString || name == destroy) + continue; + + if (ft.IsEmpty()) { + ft = v8::FunctionTemplate::New(); + ft->InstanceTemplate()->SetFallbackPropertyHandler(QV8QObjectWrapper::Getter, + QV8QObjectWrapper::Setter, + QV8QObjectWrapper::Query, + 0, + QV8QObjectWrapper::Enumerator); + ft->InstanceTemplate()->SetHasExternalResource(true); + } + + // We wrap the raw QQmlPropertyData pointer here. This is safe as the + // pointer will remain valid at least as long as the lifetime of any QObject's of + // this type and the property accessor checks if the object is 0 (deleted) before + // dereferencing the pointer. + ft->InstanceTemplate()->SetAccessor(engine->toString(name), fastgetter, fastsetter, + v8::External::Wrap(property)); + } + } + + if (ft.IsEmpty()) { + constructor = qPersistentNew<v8::Function>(engine->qobjectWrapper()->m_constructor); + } else { + ft->InstanceTemplate()->SetFallbackPropertyHandler(QV8QObjectWrapper::Getter, + QV8QObjectWrapper::Setter, + QV8QObjectWrapper::Query, + 0, + QV8QObjectWrapper::Enumerator); + ft->InstanceTemplate()->SetHasExternalResource(true); + constructor = qPersistentNew<v8::Function>(ft->GetFunction()); + } + + QQmlCleanup::addToEngine(this->engine); + } + + v8::Local<v8::Object> result = constructor->NewInstance(); + QV8QObjectResource *r = new QV8QObjectResource(engine, object); + result->SetExternalResource(r); + return result; +} + +v8::Local<v8::Object> QV8QObjectWrapper::newQObject(QObject *object, QQmlData *ddata, QV8Engine *engine) +{ + v8::Local<v8::Object> rv; + + if (!ddata->propertyCache && engine->engine()) { + ddata->propertyCache = QQmlEnginePrivate::get(engine->engine())->cache(object); + if (ddata->propertyCache) ddata->propertyCache->addref(); + } + + if (ddata->propertyCache && ddata->propertyCache->qmlEngine() == engine->engine()) { + rv = ddata->propertyCache->newQObject(object, engine); + } else { + // XXX NewInstance() should be optimized + rv = m_constructor->NewInstance(); + QV8QObjectResource *r = new QV8QObjectResource(engine, object); + rv->SetExternalResource(r); + } + + return rv; +} + +/* +As V8 doesn't support an equality callback, for QObject's we have to return exactly the same +V8 handle for subsequent calls to newQObject for the same QObject. To do this we have a two +pronged strategy: + 1. If there is no current outstanding V8 handle to the QObject, we create one and store a + persistent handle in QQmlData::v8object. We mark the QV8QObjectWrapper that + "owns" this handle by setting the QQmlData::v8objectid to the id of this + QV8QObjectWrapper. + 2. If another QV8QObjectWrapper has create the handle in QQmlData::v8object we create + an entry in the m_taintedObject hash where we store the handle and mark the object as + "tainted" in the QQmlData::hasTaintedV8Object flag. +We have to mark the object as tainted to ensure that we search our m_taintedObject hash even +in the case that the original QV8QObjectWrapper owner of QQmlData::v8object has +released the handle. +*/ +v8::Handle<v8::Value> QV8QObjectWrapper::newQObject(QObject *object) +{ + if (!object) + return v8::Null(); + + if (QObjectPrivate::get(object)->wasDeleted) + return v8::Undefined(); + + QQmlData *ddata = QQmlData::get(object, true); + + if (!ddata) + return v8::Undefined(); + + if (ddata->v8objectid == m_id && !ddata->v8object.IsEmpty()) { + // We own the v8object + return v8::Local<v8::Object>::New(ddata->v8object); + } else if (ddata->v8object.IsEmpty() && + (ddata->v8objectid == m_id || // We own the QObject + ddata->v8objectid == 0 || // No one owns the QObject + !ddata->hasTaintedV8Object)) { // Someone else has used the QObject, but it isn't tainted + + v8::Local<v8::Object> rv = newQObject(object, ddata, m_engine); + ddata->v8object = qPersistentNew<v8::Object>(rv); + ddata->v8object.MakeWeak(0, WeakQObjectReferenceCallback); + ddata->v8objectid = m_id; + return rv; + + } else { + // If this object is tainted, we have to check to see if it is in our + // tainted object list + TaintedHash::Iterator iter = + ddata->hasTaintedV8Object?m_taintedObjects.find(object):m_taintedObjects.end(); + bool found = iter != m_taintedObjects.end(); + + // If our tainted handle doesn't exist or has been collected, and there isn't + // a handle in the ddata, we can assume ownership of the ddata->v8object + if ((!found || (*iter)->v8object.IsEmpty()) && ddata->v8object.IsEmpty()) { + v8::Local<v8::Object> rv = newQObject(object, ddata, m_engine); + ddata->v8object = qPersistentNew<v8::Object>(rv); + ddata->v8object.MakeWeak(0, WeakQObjectReferenceCallback); + ddata->v8objectid = m_id; + + if (found) { + delete (*iter); + m_taintedObjects.erase(iter); + } + + return rv; + } else if (!found) { + QV8QObjectInstance *instance = new QV8QObjectInstance(object, this); + iter = m_taintedObjects.insert(object, instance); + ddata->hasTaintedV8Object = true; + } + + if ((*iter)->v8object.IsEmpty()) { + v8::Local<v8::Object> rv = newQObject(object, ddata, m_engine); + (*iter)->v8object = qPersistentNew<v8::Object>(rv); + (*iter)->v8object.MakeWeak((*iter), WeakQObjectInstanceCallback); + } + + return v8::Local<v8::Object>::New((*iter)->v8object); + } +} + +QPair<QObject *, int> QV8QObjectWrapper::ExtractQtSignal(QV8Engine *engine, v8::Handle<v8::Object> object) +{ + if (object->IsFunction()) + return ExtractQtMethod(engine, v8::Handle<v8::Function>::Cast(object)); + + if (QV8SignalHandlerResource *resource = v8_resource_cast<QV8SignalHandlerResource>(object)) + return qMakePair(resource->object.data(), resource->index); + + return qMakePair((QObject *)0, -1); +} + +QPair<QObject *, int> QV8QObjectWrapper::ExtractQtMethod(QV8Engine *engine, v8::Handle<v8::Function> function) +{ + v8::ScriptOrigin origin = function->GetScriptOrigin(); + if (origin.ResourceName()->StrictEquals(engine->qobjectWrapper()->m_hiddenObject)) { + + // This is one of our special QObject method wrappers + v8::Handle<v8::Value> args[] = { engine->qobjectWrapper()->m_hiddenObject }; + v8::Local<v8::Value> data = function->Call(engine->global(), 1, args); + + if (data->IsArray()) { + v8::Local<v8::Array> array = v8::Local<v8::Array>::Cast(data); + return qMakePair(engine->toQObject(array->Get(0)), array->Get(1)->Int32Value()); + } + + // In theory this can't fall through, but I suppose V8 might run out of memory or something + } + + return qMakePair((QObject *)0, -1); +} + +class QV8QObjectConnectionList : public QObject, public QQmlGuard<QObject> +{ +public: + QV8QObjectConnectionList(QObject *object, QV8Engine *engine); + ~QV8QObjectConnectionList(); + + struct Connection { + Connection() + : needsDestroy(false) {} + Connection(const Connection &other) + : thisObject(other.thisObject), function(other.function), needsDestroy(false) {} + Connection &operator=(const Connection &other) { + thisObject = other.thisObject; + function = other.function; + needsDestroy = other.needsDestroy; + return *this; + } + + v8::Persistent<v8::Object> thisObject; + v8::Persistent<v8::Function> function; + + void dispose() { + qPersistentDispose(thisObject); + qPersistentDispose(function); + } + + bool needsDestroy; + }; + + struct ConnectionList : public QList<Connection> { + ConnectionList() : connectionsInUse(0), connectionsNeedClean(false) {} + int connectionsInUse; + bool connectionsNeedClean; + }; + + QV8Engine *engine; + + typedef QHash<int, ConnectionList> SlotHash; + SlotHash slotHash; + bool needsDestroy; + int inUse; + + virtual void objectDestroyed(QObject *); + virtual int qt_metacall(QMetaObject::Call, int, void **); +}; + +QV8QObjectConnectionList::QV8QObjectConnectionList(QObject *object, QV8Engine *engine) +: QQmlGuard<QObject>(object), engine(engine), needsDestroy(false), inUse(0) +{ +} + +QV8QObjectConnectionList::~QV8QObjectConnectionList() +{ + for (SlotHash::Iterator iter = slotHash.begin(); iter != slotHash.end(); ++iter) { + QList<Connection> &connections = *iter; + for (int ii = 0; ii < connections.count(); ++ii) { + qPersistentDispose(connections[ii].thisObject); + qPersistentDispose(connections[ii].function); + } + } + slotHash.clear(); +} + +void QV8QObjectConnectionList::objectDestroyed(QObject *object) +{ + engine->qobjectWrapper()->m_connections.remove(object); + + if (inUse) + needsDestroy = true; + else + delete this; +} + +int QV8QObjectConnectionList::qt_metacall(QMetaObject::Call method, int index, void **metaArgs) +{ + if (method == QMetaObject::InvokeMetaMethod) { + SlotHash::Iterator iter = slotHash.find(index); + if (iter == slotHash.end()) + return -1; + ConnectionList &connectionList = *iter; + if (connectionList.isEmpty()) + return -1; + + inUse++; + + connectionList.connectionsInUse++; + + QList<Connection> connections = connectionList; + + QVarLengthArray<int, 9> dummy; + int *argsTypes = QQmlPropertyCache::methodParameterTypes(data(), index, dummy, 0); + + v8::HandleScope handle_scope; + v8::Context::Scope scope(engine->context()); + + int argCount = argsTypes?argsTypes[0]:0; + QVarLengthArray<v8::Handle<v8::Value>, 9> args(argCount); + + for (int ii = 0; ii < argCount; ++ii) { + int type = argsTypes[ii + 1]; + if (type == qMetaTypeId<QVariant>()) { + args[ii] = engine->fromVariant(*((QVariant *)metaArgs[ii + 1])); + } else { + args[ii] = engine->fromVariant(QVariant(type, metaArgs[ii + 1])); + } + } + + for (int ii = 0; ii < connections.count(); ++ii) { + Connection &connection = connections[ii]; + if (connection.needsDestroy) + continue; + + v8::TryCatch try_catch; + if (connection.thisObject.IsEmpty()) { + connection.function->Call(engine->global(), argCount, args.data()); + } else { + connection.function->Call(connection.thisObject, argCount, args.data()); + } + + if (try_catch.HasCaught()) { + QQmlError error; + error.setDescription(QString(QLatin1String("Unknown exception occurred during evaluation of connected function: %1")).arg(engine->toString(connection.function->GetName()))); + v8::Local<v8::Message> message = try_catch.Message(); + if (!message.IsEmpty()) + QQmlExpressionPrivate::exceptionToError(message, error); + QQmlEnginePrivate::get(engine->engine())->warning(error); + } + } + + connectionList.connectionsInUse--; + if (connectionList.connectionsInUse == 0 && connectionList.connectionsNeedClean) { + for (QList<Connection>::Iterator iter = connectionList.begin(); + iter != connectionList.end(); ) { + if (iter->needsDestroy) { + iter->dispose(); + iter = connectionList.erase(iter); + } else { + ++iter; + } + } + } + + inUse--; + if (inUse == 0 && needsDestroy) + delete this; + } + + return -1; +} + +v8::Handle<v8::Value> QV8QObjectWrapper::Connect(const v8::Arguments &args) +{ + if (args.Length() == 0) + V8THROW_ERROR("Function.prototype.connect: no arguments given"); + + QV8Engine *engine = V8ENGINE(); + + QPair<QObject *, int> signalInfo = ExtractQtSignal(engine, args.This()); + QObject *signalObject = signalInfo.first; + int signalIndex = signalInfo.second; + + if (signalIndex == -1) + V8THROW_ERROR("Function.prototype.connect: this object is not a signal"); + + if (!signalObject) + V8THROW_ERROR("Function.prototype.connect: cannot connect to deleted QObject"); + + if (signalIndex < 0 || signalObject->metaObject()->method(signalIndex).methodType() != QMetaMethod::Signal) + V8THROW_ERROR("Function.prototype.connect: this object is not a signal"); + + v8::Local<v8::Value> functionValue; + v8::Local<v8::Value> functionThisValue; + + if (args.Length() == 1) { + functionValue = args[0]; + } else { + functionThisValue = args[0]; + functionValue = args[1]; + } + + if (!functionValue->IsFunction()) + V8THROW_ERROR("Function.prototype.connect: target is not a function"); + + if (!functionThisValue.IsEmpty() && !functionThisValue->IsObject()) + V8THROW_ERROR("Function.prototype.connect: target this is not an object"); + + QV8QObjectWrapper *qobjectWrapper = engine->qobjectWrapper(); + QHash<QObject *, QV8QObjectConnectionList *> &connections = qobjectWrapper->m_connections; + QHash<QObject *, QV8QObjectConnectionList *>::Iterator iter = connections.find(signalObject); + if (iter == connections.end()) + iter = connections.insert(signalObject, new QV8QObjectConnectionList(signalObject, engine)); + + QV8QObjectConnectionList *connectionList = *iter; + QV8QObjectConnectionList::SlotHash::Iterator slotIter = connectionList->slotHash.find(signalIndex); + if (slotIter == connectionList->slotHash.end()) { + slotIter = connectionList->slotHash.insert(signalIndex, QV8QObjectConnectionList::ConnectionList()); + QMetaObject::connect(signalObject, signalIndex, connectionList, signalIndex); + } + + QV8QObjectConnectionList::Connection connection; + if (!functionThisValue.IsEmpty()) + connection.thisObject = qPersistentNew<v8::Object>(functionThisValue->ToObject()); + connection.function = qPersistentNew<v8::Function>(v8::Handle<v8::Function>::Cast(functionValue)); + + slotIter->append(connection); + + return v8::Undefined(); +} + +v8::Handle<v8::Value> QV8QObjectWrapper::Disconnect(const v8::Arguments &args) +{ + if (args.Length() == 0) + V8THROW_ERROR("Function.prototype.disconnect: no arguments given"); + + QV8Engine *engine = V8ENGINE(); + + QPair<QObject *, int> signalInfo = ExtractQtSignal(engine, args.This()); + QObject *signalObject = signalInfo.first; + int signalIndex = signalInfo.second; + + if (signalIndex == -1) + V8THROW_ERROR("Function.prototype.disconnect: this object is not a signal"); + + if (!signalObject) + V8THROW_ERROR("Function.prototype.disconnect: cannot disconnect from deleted QObject"); + + if (signalIndex < 0 || signalObject->metaObject()->method(signalIndex).methodType() != QMetaMethod::Signal) + V8THROW_ERROR("Function.prototype.disconnect: this object is not a signal"); + + v8::Local<v8::Value> functionValue; + v8::Local<v8::Value> functionThisValue; + + if (args.Length() == 1) { + functionValue = args[0]; + } else { + functionThisValue = args[0]; + functionValue = args[1]; + } + + if (!functionValue->IsFunction()) + V8THROW_ERROR("Function.prototype.disconnect: target is not a function"); + + if (!functionThisValue.IsEmpty() && !functionThisValue->IsObject()) + V8THROW_ERROR("Function.prototype.disconnect: target this is not an object"); + + QV8QObjectWrapper *qobjectWrapper = engine->qobjectWrapper(); + QHash<QObject *, QV8QObjectConnectionList *> &connectionsList = qobjectWrapper->m_connections; + QHash<QObject *, QV8QObjectConnectionList *>::Iterator iter = connectionsList.find(signalObject); + if (iter == connectionsList.end()) + return v8::Undefined(); // Nothing to disconnect from + + QV8QObjectConnectionList *connectionList = *iter; + QV8QObjectConnectionList::SlotHash::Iterator slotIter = connectionList->slotHash.find(signalIndex); + if (slotIter == connectionList->slotHash.end()) + return v8::Undefined(); // Nothing to disconnect from + + QV8QObjectConnectionList::ConnectionList &connections = *slotIter; + + v8::Local<v8::Function> function = v8::Local<v8::Function>::Cast(functionValue); + QPair<QObject *, int> functionData = ExtractQtMethod(engine, function); + + if (functionData.second != -1) { + // This is a QObject function wrapper + for (int ii = 0; ii < connections.count(); ++ii) { + QV8QObjectConnectionList::Connection &connection = connections[ii]; + + if (connection.thisObject.IsEmpty() == functionThisValue.IsEmpty() && + (connection.thisObject.IsEmpty() || connection.thisObject->StrictEquals(functionThisValue))) { + + QPair<QObject *, int> connectedFunctionData = ExtractQtMethod(engine, connection.function); + if (connectedFunctionData == functionData) { + // Match! + if (connections.connectionsInUse) { + connection.needsDestroy = true; + connections.connectionsNeedClean = true; + } else { + connection.dispose(); + connections.removeAt(ii); + } + return v8::Undefined(); + } + } + } + + } else { + // This is a normal JS function + for (int ii = 0; ii < connections.count(); ++ii) { + QV8QObjectConnectionList::Connection &connection = connections[ii]; + if (connection.function->StrictEquals(function) && + connection.thisObject.IsEmpty() == functionThisValue.IsEmpty() && + (connection.thisObject.IsEmpty() || connection.thisObject->StrictEquals(functionThisValue))) { + // Match! + if (connections.connectionsInUse) { + connection.needsDestroy = true; + connections.connectionsNeedClean = true; + } else { + connection.dispose(); + connections.removeAt(ii); + } + return v8::Undefined(); + } + } + } + + return v8::Undefined(); +} + +/*! + \fn v8::Handle<v8::Value> QV8QObjectWrapper::getProperty(QObject *object, const QHashedV8String &property, QV8QObjectWrapper::RevisionMode revisionMode) + + Get the \a property of \a object. Returns an empty handle if the property doesn't exist. + + Only searches for real properties of \a object (including methods), not attached properties etc. +*/ + +/* + \fn bool QV8QObjectWrapper::setProperty(QObject *object, const QHashedV8String &property, v8::Handle<v8::Value> value, RevisionMode revisionMode) + + Set the \a property of \a object to \a value. + + Returns true if the property was "set" - even if this results in an exception being thrown - + and false if the object has no such property. + + Only searches for real properties of \a object (including methods), not attached properties etc. +*/ + +namespace { +struct CallArgs +{ + CallArgs(int length, v8::Handle<v8::Object> *args) : _length(length), _args(args) {} + int Length() const { return _length; } + v8::Local<v8::Value> operator[](int idx) { return (*_args)->Get(idx); } + +private: + int _length; + v8::Handle<v8::Object> *_args; +}; +} + +static v8::Handle<v8::Value> CallMethod(QObject *object, int index, int returnType, int argCount, + int *argTypes, QV8Engine *engine, CallArgs &callArgs) +{ + if (argCount > 0) { + + QVarLengthArray<CallArgument, 9> args(argCount + 1); + args[0].initAsType(returnType); + + for (int ii = 0; ii < argCount; ++ii) + args[ii + 1].fromValue(argTypes[ii], engine, callArgs[ii]); + + QVarLengthArray<void *, 9> argData(args.count()); + for (int ii = 0; ii < args.count(); ++ii) + argData[ii] = args[ii].dataPtr(); + + QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, argData.data()); + + return args[0].toValue(engine); + + } else if (returnType != 0) { + + CallArgument arg; + arg.initAsType(returnType); + + void *args[] = { arg.dataPtr() }; + + QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, args); + + return arg.toValue(engine); + + } else { + + void *args[] = { 0 }; + QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, index, args); + return v8::Undefined(); + + } +} + +/*! + Returns the match score for converting \a actual to be of type \a conversionType. A + zero score means "perfect match" whereas a higher score is worse. + + The conversion table is copied out of the QtScript callQtMethod() function. +*/ +static int MatchScore(v8::Handle<v8::Value> actual, int conversionType) +{ + if (actual->IsNumber()) { + switch (conversionType) { + case QMetaType::Double: + return 0; + case QMetaType::Float: + return 1; + case QMetaType::LongLong: + case QMetaType::ULongLong: + return 2; + case QMetaType::Long: + case QMetaType::ULong: + return 3; + case QMetaType::Int: + case QMetaType::UInt: + return 4; + case QMetaType::Short: + case QMetaType::UShort: + return 5; + break; + case QMetaType::Char: + case QMetaType::UChar: + return 6; + default: + return 10; + } + } else if (actual->IsString()) { + switch (conversionType) { + case QMetaType::QString: + return 0; + default: + return 10; + } + } else if (actual->IsBoolean()) { + switch (conversionType) { + case QMetaType::Bool: + return 0; + default: + return 10; + } + } else if (actual->IsDate()) { + switch (conversionType) { + case QMetaType::QDateTime: + return 0; + case QMetaType::QDate: + return 1; + case QMetaType::QTime: + return 2; + default: + return 10; + } + } else if (actual->IsRegExp()) { + switch (conversionType) { + case QMetaType::QRegExp: + return 0; + default: + return 10; + } + } else if (actual->IsArray()) { + switch (conversionType) { + case QMetaType::QStringList: + case QMetaType::QVariantList: + return 5; + default: + return 10; + } + } else if (actual->IsNull()) { + switch (conversionType) { + case QMetaType::VoidStar: + case QMetaType::QObjectStar: + return 0; + default: { + const char *typeName = QMetaType::typeName(conversionType); + if (typeName && typeName[strlen(typeName) - 1] == '*') + return 0; + else + return 10; + } + } + } else if (actual->IsObject()) { + v8::Handle<v8::Object> obj = v8::Handle<v8::Object>::Cast(actual); + + QV8ObjectResource *r = static_cast<QV8ObjectResource *>(obj->GetExternalResource()); + if (r && r->resourceType() == QV8ObjectResource::QObjectType) { + switch (conversionType) { + case QMetaType::QObjectStar: + return 0; + default: + return 10; + } + } else if (r && r->resourceType() == QV8ObjectResource::VariantType) { + if (conversionType == qMetaTypeId<QVariant>()) + return 0; + else if (r->engine->toVariant(actual, -1).userType() == conversionType) + return 0; + else + return 10; + } else { + return 10; + } + + } else { + return 10; + } +} + +static inline int QMetaObject_methods(const QMetaObject *metaObject) +{ + struct Private + { + int revision; + int className; + int classInfoCount, classInfoData; + int methodCount, methodData; + }; + + return reinterpret_cast<const Private *>(metaObject->d.data)->methodCount; +} + +static QByteArray QMetaMethod_name(const QMetaMethod &m) +{ + QByteArray sig = m.signature(); + int paren = sig.indexOf('('); + if (paren == -1) + return sig; + else + return sig.left(paren); +} + +/*! +Returns the next related method, if one, or 0. +*/ +static const QQmlPropertyData * RelatedMethod(QObject *object, + const QQmlPropertyData *current, + QQmlPropertyData &dummy) +{ + QQmlPropertyCache *cache = QQmlData::get(object)->propertyCache; + if (!current->isOverload()) + return 0; + + Q_ASSERT(!current->overrideIndexIsProperty); + + if (cache) { + return cache->method(current->overrideIndex); + } else { + const QMetaObject *mo = object->metaObject(); + int methodOffset = mo->methodCount() - QMetaObject_methods(mo); + + while (methodOffset > current->overrideIndex) { + mo = mo->superClass(); + methodOffset -= QMetaObject_methods(mo); + } + + QMetaMethod method = mo->method(current->overrideIndex); + dummy.load(method); + + // Look for overloaded methods + QByteArray methodName = QMetaMethod_name(method); + for (int ii = current->overrideIndex - 1; ii >= methodOffset; --ii) { + if (methodName == QMetaMethod_name(mo->method(ii))) { + dummy.setFlags(dummy.getFlags() | QQmlPropertyData::IsOverload); + dummy.overrideIndexIsProperty = 0; + dummy.overrideIndex = ii; + return &dummy; + } + } + + return &dummy; + } +} + +static v8::Handle<v8::Value> CallPrecise(QObject *object, const QQmlPropertyData &data, + QV8Engine *engine, CallArgs &callArgs) +{ + if (data.hasArguments()) { + + int *args = 0; + QVarLengthArray<int, 9> dummy; + QByteArray unknownTypeError; + + args = QQmlPropertyCache::methodParameterTypes(object, data.coreIndex, dummy, + &unknownTypeError); + + if (!args) { + QString typeName = QString::fromLatin1(unknownTypeError); + QString error = QString::fromLatin1("Unknown method parameter type: %1").arg(typeName); + v8::ThrowException(v8::Exception::Error(engine->toString(error))); + return v8::Handle<v8::Value>(); + } + + if (args[0] > callArgs.Length()) { + QString error = QLatin1String("Insufficient arguments"); + v8::ThrowException(v8::Exception::Error(engine->toString(error))); + return v8::Handle<v8::Value>(); + } + + return CallMethod(object, data.coreIndex, data.propType, args[0], args + 1, engine, callArgs); + + } else { + + return CallMethod(object, data.coreIndex, data.propType, 0, 0, engine, callArgs); + + } +} + +/*! +Resolve the overloaded method to call. The algorithm works conceptually like this: + 1. Resolve the set of overloads it is *possible* to call. + Impossible overloads include those that have too many parameters or have parameters + of unknown type. + 2. Filter the set of overloads to only contain those with the closest number of + parameters. + For example, if we are called with 3 parameters and there are 2 overloads that + take 2 parameters and one that takes 3, eliminate the 2 parameter overloads. + 3. Find the best remaining overload based on its match score. + If two or more overloads have the same match score, call the last one. The match + score is constructed by adding the matchScore() result for each of the parameters. +*/ +static v8::Handle<v8::Value> CallOverloaded(QObject *object, const QQmlPropertyData &data, + QV8Engine *engine, CallArgs &callArgs) +{ + int argumentCount = callArgs.Length(); + + const QQmlPropertyData *best = 0; + int bestParameterScore = INT_MAX; + int bestMatchScore = INT_MAX; + + QQmlPropertyData dummy; + const QQmlPropertyData *attempt = &data; + + do { + QVarLengthArray<int, 9> dummy; + int methodArgumentCount = 0; + int *methodArgTypes = 0; + if (attempt->hasArguments()) { + typedef QQmlPropertyCache PC; + int *args = PC::methodParameterTypes(object, attempt->coreIndex, dummy, 0); + if (!args) // Must be an unknown argument + continue; + + methodArgumentCount = args[0]; + methodArgTypes = args + 1; + } + + if (methodArgumentCount > argumentCount) + continue; // We don't have sufficient arguments to call this method + + int methodParameterScore = argumentCount - methodArgumentCount; + if (methodParameterScore > bestParameterScore) + continue; // We already have a better option + + int methodMatchScore = 0; + for (int ii = 0; ii < methodArgumentCount; ++ii) + methodMatchScore += MatchScore(callArgs[ii], methodArgTypes[ii]); + + if (bestParameterScore > methodParameterScore || bestMatchScore > methodMatchScore) { + best = attempt; + bestParameterScore = methodParameterScore; + bestMatchScore = methodMatchScore; + } + + if (bestParameterScore == 0 && bestMatchScore == 0) + break; // We can't get better than that + + } while((attempt = RelatedMethod(object, attempt, dummy)) != 0); + + if (best) { + return CallPrecise(object, *best, engine, callArgs); + } else { + QString error = QLatin1String("Unable to determine callable overload. Candidates are:"); + const QQmlPropertyData *candidate = &data; + while (candidate) { + error += QLatin1String("\n ") + + QString::fromUtf8(object->metaObject()->method(candidate->coreIndex).signature()); + candidate = RelatedMethod(object, candidate, dummy); + } + + v8::ThrowException(v8::Exception::Error(engine->toString(error))); + return v8::Handle<v8::Value>(); + } +} + +static v8::Handle<v8::Value> ToString(QV8Engine *engine, QObject *object, int, v8::Handle<v8::Object>) +{ + QString result; + if (object) { + QString objectName = object->objectName(); + + result += QString::fromUtf8(object->metaObject()->className()); + result += QLatin1String("(0x"); + result += QString::number((quintptr)object,16); + + if (!objectName.isEmpty()) { + result += QLatin1String(", \""); + result += objectName; + result += QLatin1Char('\"'); + } + + result += QLatin1Char(')'); + } else { + result = QLatin1String("null"); + } + + return engine->toString(result); +} + +static v8::Handle<v8::Value> Destroy(QV8Engine *, QObject *object, int argCount, v8::Handle<v8::Object> args) +{ + QQmlData *ddata = QQmlData::get(object, false); + if (!ddata || ddata->indestructible) { + const char *error = "Invalid attempt to destroy() an indestructible object"; + v8::ThrowException(v8::Exception::Error(v8::String::New(error))); + return v8::Undefined(); + } + + int delay = 0; + if (argCount > 0) + delay = args->Get(0)->Uint32Value(); + + if (delay > 0) + QTimer::singleShot(delay, object, SLOT(deleteLater())); + else + object->deleteLater(); + + return v8::Undefined(); +} + +v8::Handle<v8::Value> QV8QObjectWrapper::Invoke(const v8::Arguments &args) +{ + // object, index, qmlglobal, argCount, args + Q_ASSERT(args.Length() == 5); + Q_ASSERT(args[0]->IsObject()); + + QV8QObjectResource *resource = v8_resource_cast<QV8QObjectResource>(args[0]->ToObject()); + + if (!resource) + return v8::Undefined(); + + int argCount = args[3]->Int32Value(); + v8::Handle<v8::Object> arguments = v8::Handle<v8::Object>::Cast(args[4]); + + // Special hack to return info about this closure. + if (argCount == 1 && arguments->Get(0)->StrictEquals(resource->engine->qobjectWrapper()->m_hiddenObject)) { + v8::Local<v8::Array> data = v8::Array::New(2); + data->Set(0, args[0]); + data->Set(1, args[1]); + return data; + } + + QObject *object = resource->object; + int index = args[1]->Int32Value(); + + if (!object) + return v8::Undefined(); + + if (index < 0) { + // Builtin functions + if (index == QOBJECT_TOSTRING_INDEX) { + return ToString(resource->engine, object, argCount, arguments); + } else if (index == QOBJECT_DESTROY_INDEX) { + return Destroy(resource->engine, object, argCount, arguments); + } else { + return v8::Undefined(); + } + } + + QQmlPropertyData method; + + if (QQmlData *ddata = static_cast<QQmlData *>(QObjectPrivate::get(object)->declarativeData)) { + if (ddata->propertyCache) { + QQmlPropertyData *d = ddata->propertyCache->method(index); + if (!d) + return v8::Undefined(); + method = *d; + } + } + + if (method.coreIndex == -1) { + method.load(object->metaObject()->method(index)); + + if (method.coreIndex == -1) + return v8::Undefined(); + } + + if (method.isV8Function()) { + v8::Handle<v8::Value> rv; + v8::Handle<v8::Object> qmlglobal = args[2]->ToObject(); + + QQmlV8Function func(argCount, arguments, rv, qmlglobal, + resource->engine->contextWrapper()->context(qmlglobal), + resource->engine); + QQmlV8Function *funcptr = &func; + + void *args[] = { 0, &funcptr }; + QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, method.coreIndex, args); + + if (rv.IsEmpty()) return v8::Undefined(); + return rv; + } + + CallArgs callArgs(argCount, &arguments); + if (!method.isOverload()) { + return CallPrecise(object, method, resource->engine, callArgs); + } else { + return CallOverloaded(object, method, resource->engine, callArgs); + } +} + +CallArgument::CallArgument() +: type(QVariant::Invalid) +{ +} + +CallArgument::~CallArgument() +{ + cleanup(); +} + +void CallArgument::cleanup() +{ + if (type == QMetaType::QString) { + qstringPtr->~QString(); + } else if (type == -1 || type == QMetaType::QVariant) { + qvariantPtr->~QVariant(); + } else if (type == qMetaTypeId<QJSValue>()) { + qjsValuePtr->~QJSValue(); + } else if (type == qMetaTypeId<QList<QObject *> >()) { + qlistPtr->~QList<QObject *>(); + } +} + +void *CallArgument::dataPtr() +{ + if (type == -1) + return qvariantPtr->data(); + else + return (void *)&allocData; +} + +void CallArgument::initAsType(int callType) +{ + if (type != 0) { cleanup(); type = 0; } + if (callType == 0) return; + + if (callType == qMetaTypeId<QJSValue>()) { + qjsValuePtr = new (&allocData) QJSValue(); + type = callType; + } else if (callType == QMetaType::Int || + callType == QMetaType::UInt || + callType == QMetaType::Bool || + callType == QMetaType::Double || + callType == QMetaType::Float) { + type = callType; + } else if (callType == QMetaType::QObjectStar) { + qobjectPtr = 0; + type = callType; + } else if (callType == QMetaType::QString) { + qstringPtr = new (&allocData) QString(); + type = callType; + } else if (callType == QMetaType::QVariant) { + type = callType; + qvariantPtr = new (&allocData) QVariant(); + } else if (callType == qMetaTypeId<QList<QObject *> >()) { + type = callType; + qlistPtr = new (&allocData) QList<QObject *>(); + } else if (callType == qMetaTypeId<QQmlV8Handle>()) { + type = callType; + handlePtr = new (&allocData) QQmlV8Handle; + } else { + type = -1; + qvariantPtr = new (&allocData) QVariant(callType, (void *)0); + } +} + +void CallArgument::fromValue(int callType, QV8Engine *engine, v8::Handle<v8::Value> value) +{ + if (type != 0) { cleanup(); type = 0; } + + if (callType == qMetaTypeId<QJSValue>()) { + qjsValuePtr = new (&allocData) QJSValue(QJSValuePrivate::get(new QJSValuePrivate(engine, value))); + type = qMetaTypeId<QJSValue>(); + } else if (callType == QMetaType::Int) { + intValue = quint32(value->Int32Value()); + type = callType; + } else if (callType == QMetaType::UInt) { + intValue = quint32(value->Uint32Value()); + type = callType; + } else if (callType == QMetaType::Bool) { + boolValue = value->BooleanValue(); + type = callType; + } else if (callType == QMetaType::Double) { + doubleValue = double(value->NumberValue()); + type = callType; + } else if (callType == QMetaType::Float) { + floatValue = float(value->NumberValue()); + type = callType; + } else if (callType == QMetaType::QString) { + if (value->IsNull() || value->IsUndefined()) + qstringPtr = new (&allocData) QString(); + else + qstringPtr = new (&allocData) QString(engine->toString(value->ToString())); + type = callType; + } else if (callType == QMetaType::QObjectStar) { + qobjectPtr = engine->toQObject(value); + type = callType; + } else if (callType == qMetaTypeId<QVariant>()) { + qvariantPtr = new (&allocData) QVariant(engine->toVariant(value, -1)); + type = callType; + } else if (callType == qMetaTypeId<QList<QObject*> >()) { + qlistPtr = new (&allocData) QList<QObject *>(); + if (value->IsArray()) { + v8::Handle<v8::Array> array = v8::Handle<v8::Array>::Cast(value); + uint32_t length = array->Length(); + for (uint32_t ii = 0; ii < length; ++ii) + qlistPtr->append(engine->toQObject(array->Get(ii))); + } else { + qlistPtr->append(engine->toQObject(value)); + } + type = callType; + } else if (callType == qMetaTypeId<QQmlV8Handle>()) { + handlePtr = new (&allocData) QQmlV8Handle(QQmlV8Handle::fromHandle(value)); + type = callType; + } else { + qvariantPtr = new (&allocData) QVariant(); + type = -1; + + QQmlEnginePrivate *ep = engine->engine() ? QQmlEnginePrivate::get(engine->engine()) : 0; + QVariant v = engine->toVariant(value, -1); + + if (v.userType() == callType) { + *qvariantPtr = v; + } else if (v.canConvert((QVariant::Type)callType)) { + *qvariantPtr = v; + qvariantPtr->convert((QVariant::Type)callType); + } else if (const QMetaObject *mo = ep ? ep->rawMetaObjectForType(callType) : 0) { + QObject *obj = ep->toQObject(v); + + if (obj) { + const QMetaObject *objMo = obj->metaObject(); + while (objMo && objMo != mo) objMo = objMo->superClass(); + if (!objMo) obj = 0; + } + + *qvariantPtr = QVariant(callType, &obj); + } else { + *qvariantPtr = QVariant(callType, (void *)0); + } + } +} + +v8::Handle<v8::Value> CallArgument::toValue(QV8Engine *engine) +{ + if (type == qMetaTypeId<QJSValue>()) { + return QJSValuePrivate::get(*qjsValuePtr)->asV8Value(engine); + } else if (type == QMetaType::Int) { + return v8::Integer::New(int(intValue)); + } else if (type == QMetaType::UInt) { + return v8::Integer::NewFromUnsigned(intValue); + } else if (type == QMetaType::Bool) { + return v8::Boolean::New(boolValue); + } else if (type == QMetaType::Double) { + return v8::Number::New(doubleValue); + } else if (type == QMetaType::Float) { + return v8::Number::New(floatValue); + } else if (type == QMetaType::QString) { + return engine->toString(*qstringPtr); + } else if (type == QMetaType::QObjectStar) { + QObject *object = qobjectPtr; + if (object) + QQmlData::get(object, true)->setImplicitDestructible(); + return engine->newQObject(object); + } else if (type == qMetaTypeId<QList<QObject *> >()) { + // XXX Can this be made more by using Array as a prototype and implementing + // directly against QList<QObject*>? + QList<QObject *> &list = *qlistPtr; + v8::Local<v8::Array> array = v8::Array::New(list.count()); + for (int ii = 0; ii < list.count(); ++ii) + array->Set(ii, engine->newQObject(list.at(ii))); + return array; + } else if (type == qMetaTypeId<QQmlV8Handle>()) { + return handlePtr->toHandle(); + } else if (type == -1 || type == qMetaTypeId<QVariant>()) { + QVariant value = *qvariantPtr; + v8::Handle<v8::Value> rv = engine->fromVariant(value); + if (QObject *object = engine->toQObject(rv)) + QQmlData::get(object, true)->setImplicitDestructible(); + return rv; + } else { + return v8::Undefined(); + } +} + +QT_END_NAMESPACE + diff --git a/src/qml/qml/v8/qv8qobjectwrapper_p.h b/src/qml/qml/v8/qv8qobjectwrapper_p.h new file mode 100644 index 0000000000..f7b965690b --- /dev/null +++ b/src/qml/qml/v8/qv8qobjectwrapper_p.h @@ -0,0 +1,159 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV8QOBJECTWRAPPER_P_H +#define QV8QOBJECTWRAPPER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qglobal.h> +#include <QtCore/qmetatype.h> +#include <QtCore/qpair.h> +#include <QtCore/qhash.h> +#include <private/qv8_p.h> +#include <private/qhashedstring_p.h> +#include <private/qqmldata_p.h> +#include <private/qqmlpropertycache_p.h> + +QT_BEGIN_NAMESPACE + +class QObject; +class QV8Engine; +class QQmlData; +class QV8ObjectResource; +class QV8QObjectInstance; +class QV8QObjectConnectionList; +class QQmlPropertyCache; +class Q_QML_EXPORT QV8QObjectWrapper +{ +public: + QV8QObjectWrapper(); + ~QV8QObjectWrapper(); + + void init(QV8Engine *); + void destroy(); + + v8::Handle<v8::Value> newQObject(QObject *object); + bool isQObject(v8::Handle<v8::Object>); + QObject *toQObject(v8::Handle<v8::Object>); + static QObject *toQObject(QV8ObjectResource *); + + enum RevisionMode { IgnoreRevision, CheckRevision }; + inline v8::Handle<v8::Value> getProperty(QObject *, const QHashedV8String &, RevisionMode); + inline bool setProperty(QObject *, const QHashedV8String &, v8::Handle<v8::Value>, RevisionMode); + +private: + friend class QQmlPropertyCache; + friend class QV8QObjectConnectionList; + friend class QV8QObjectInstance; + + v8::Local<v8::Object> newQObject(QObject *, QQmlData *, QV8Engine *); + static v8::Handle<v8::Value> GetProperty(QV8Engine *, QObject *, v8::Handle<v8::Value> *, + const QHashedV8String &, QV8QObjectWrapper::RevisionMode); + static bool SetProperty(QV8Engine *, QObject *, const QHashedV8String &, + v8::Handle<v8::Value>, QV8QObjectWrapper::RevisionMode); + static v8::Handle<v8::Value> Getter(v8::Local<v8::String> property, + const v8::AccessorInfo &info); + static v8::Handle<v8::Value> Setter(v8::Local<v8::String> property, + v8::Local<v8::Value> value, + const v8::AccessorInfo &info); + static v8::Handle<v8::Integer> Query(v8::Local<v8::String> property, + const v8::AccessorInfo &info); + static v8::Handle<v8::Array> Enumerator(const v8::AccessorInfo &info); + static v8::Handle<v8::Value> Connect(const v8::Arguments &args); + static v8::Handle<v8::Value> Disconnect(const v8::Arguments &args); + static v8::Handle<v8::Value> Invoke(const v8::Arguments &args); + static QPair<QObject *, int> ExtractQtMethod(QV8Engine *, v8::Handle<v8::Function>); + static QPair<QObject *, int> ExtractQtSignal(QV8Engine *, v8::Handle<v8::Object>); + + QV8Engine *m_engine; + quint32 m_id; + v8::Persistent<v8::Function> m_constructor; + v8::Persistent<v8::Function> m_methodConstructor; + v8::Persistent<v8::Function> m_signalHandlerConstructor; + v8::Persistent<v8::String> m_toStringSymbol; + v8::Persistent<v8::String> m_destroySymbol; + QHashedV8String m_toStringString; + QHashedV8String m_destroyString; + v8::Persistent<v8::Object> m_hiddenObject; + QHash<QObject *, QV8QObjectConnectionList *> m_connections; + typedef QHash<QObject *, QV8QObjectInstance *> TaintedHash; + TaintedHash m_taintedObjects; +}; + +v8::Handle<v8::Value> QV8QObjectWrapper::getProperty(QObject *object, const QHashedV8String &string, + RevisionMode mode) +{ + QQmlData *dd = QQmlData::get(object, false); + if (!dd || !dd->propertyCache || m_toStringString == string || m_destroyString == string || + dd->propertyCache->property(string)) { + return GetProperty(m_engine, object, 0, string, mode); + } else { + return v8::Handle<v8::Value>(); + } +} + +bool QV8QObjectWrapper::setProperty(QObject *object, const QHashedV8String &string, + v8::Handle<v8::Value> value, RevisionMode mode) +{ + QQmlData *dd = QQmlData::get(object, false); + if (!dd || !dd->propertyCache || m_toStringString == string || m_destroyString == string || + dd->propertyCache->property(string)) { + return SetProperty(m_engine, object, string, value, mode); + } else { + return false; + } +} + +QT_END_NAMESPACE + +#endif // QV8QOBJECTWRAPPER_P_H + + diff --git a/src/qml/qml/v8/qv8sequencewrapper.cpp b/src/qml/qml/v8/qv8sequencewrapper.cpp new file mode 100644 index 0000000000..883ed1b60c --- /dev/null +++ b/src/qml/qml/v8/qv8sequencewrapper.cpp @@ -0,0 +1,264 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtQml/qqml.h> + +#include "qv8sequencewrapper_p.h" +#include "qv8sequencewrapper_p_p.h" +#include "qv8engine_p.h" + +QT_BEGIN_NAMESPACE + +QV8SequenceWrapper::QV8SequenceWrapper() + : m_engine(0) +{ +} + +QV8SequenceWrapper::~QV8SequenceWrapper() +{ +} + +#define REGISTER_QML_SEQUENCE_METATYPE(unused, unused2, SequenceType, unused3) qRegisterMetaType<SequenceType>(); +void QV8SequenceWrapper::init(QV8Engine *engine) +{ + FOREACH_QML_SEQUENCE_TYPE(REGISTER_QML_SEQUENCE_METATYPE) + + m_engine = engine; + m_toString = qPersistentNew<v8::Function>(v8::FunctionTemplate::New(ToString)->GetFunction()); + m_valueOf = qPersistentNew<v8::Function>(v8::FunctionTemplate::New(ValueOf)->GetFunction()); + v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New(); + ft->InstanceTemplate()->SetFallbackPropertyHandler(Getter, Setter); + ft->InstanceTemplate()->SetIndexedPropertyHandler(IndexedGetter, IndexedSetter, 0, IndexedDeleter, IndexedEnumerator); + ft->InstanceTemplate()->SetAccessor(v8::String::New("length"), LengthGetter, LengthSetter, + v8::Handle<v8::Value>(), v8::DEFAULT, + v8::PropertyAttribute(v8::DontDelete | v8::DontEnum)); + ft->InstanceTemplate()->SetAccessor(v8::String::New("toString"), ToStringGetter, 0, + m_toString, v8::DEFAULT, + v8::PropertyAttribute(v8::ReadOnly | v8::DontDelete | v8::DontEnum)); + ft->InstanceTemplate()->SetAccessor(v8::String::New("valueOf"), ValueOfGetter, 0, + m_valueOf, v8::DEFAULT, + v8::PropertyAttribute(v8::ReadOnly | v8::DontDelete | v8::DontEnum)); + ft->InstanceTemplate()->SetHasExternalResource(true); + ft->InstanceTemplate()->MarkAsUseUserObjectComparison(); + m_constructor = qPersistentNew<v8::Function>(ft->GetFunction()); +} +#undef REGISTER_QML_SEQUENCE_METATYPE + +void QV8SequenceWrapper::destroy() +{ + qPersistentDispose(m_toString); + qPersistentDispose(m_valueOf); + qPersistentDispose(m_constructor); +} + +bool QV8SequenceWrapper::isEqual(QV8ObjectResource *lhs, QV8ObjectResource *rhs) +{ + Q_ASSERT(lhs && rhs && lhs->resourceType() == QV8ObjectResource::SequenceType && rhs->resourceType() == QV8ObjectResource::SequenceType); + QV8SequenceResource *lr = static_cast<QV8SequenceResource *>(lhs); + QV8SequenceResource *rr = static_cast<QV8SequenceResource *>(rhs); + return lr->isEqual(rr); +} + +quint32 QV8SequenceWrapper::sequenceLength(QV8ObjectResource *r) +{ + Q_ASSERT(r->resourceType() == QV8ObjectResource::SequenceType); + QV8SequenceResource *sr = static_cast<QV8SequenceResource *>(r); + Q_ASSERT(sr); + return sr->lengthGetter(); +} + +#define NEW_REFERENCE_SEQUENCE(ElementType, ElementTypeName, SequenceType, unused) \ + if (sequenceType == qMetaTypeId<SequenceType>()) { \ + r = new QV8##ElementTypeName##SequenceResource(m_engine, object, propertyIndex); \ + } else + +v8::Local<v8::Object> QV8SequenceWrapper::newSequence(int sequenceType, QObject *object, int propertyIndex, bool *succeeded) +{ + // This function is called when the property is a QObject Q_PROPERTY of + // the given sequence type. Internally we store a typed-sequence + // (as well as object ptr + property index for updated-read and write-back) + // and so access/mutate avoids variant conversion. + *succeeded = true; + QV8SequenceResource *r = 0; + FOREACH_QML_SEQUENCE_TYPE(NEW_REFERENCE_SEQUENCE) { /* else */ *succeeded = false; return v8::Local<v8::Object>(); } + + v8::Local<v8::Object> rv = m_constructor->NewInstance(); + rv->SetExternalResource(r); + rv->SetPrototype(v8::Array::New(1)->GetPrototype()); + return rv; +} +#undef NEW_REFERENCE_SEQUENCE + +#define NEW_COPY_SEQUENCE(ElementType, ElementTypeName, SequenceType, unused) \ + if (sequenceType == qMetaTypeId<SequenceType>()) { \ + r = new QV8##ElementTypeName##SequenceResource(m_engine, v.value<SequenceType>()); \ + } else + +v8::Local<v8::Object> QV8SequenceWrapper::fromVariant(const QVariant& v, bool *succeeded) +{ + // This function is called when assigning a sequence value to a normal JS var + // in a JS block. Internally, we store a sequence of the specified type. + // Access and mutation is extremely fast since it will not need to modify any + // QObject property. + int sequenceType = v.userType(); + *succeeded = true; + QV8SequenceResource *r = 0; + FOREACH_QML_SEQUENCE_TYPE(NEW_COPY_SEQUENCE) { /* else */ *succeeded = false; return v8::Local<v8::Object>(); } + + v8::Local<v8::Object> rv = m_constructor->NewInstance(); + rv->SetExternalResource(r); + rv->SetPrototype(v8::Array::New(1)->GetPrototype()); + return rv; +} +#undef NEW_COPY_SEQUENCE + +QVariant QV8SequenceWrapper::toVariant(QV8ObjectResource *r) +{ + Q_ASSERT(r->resourceType() == QV8ObjectResource::SequenceType); + QV8SequenceResource *resource = static_cast<QV8SequenceResource *>(r); + return resource->toVariant(); +} + +#define SEQUENCE_TO_VARIANT(ElementType, ElementTypeName, SequenceType, unused) \ + if (typeHint == qMetaTypeId<SequenceType>()) { \ + return QV8##ElementTypeName##SequenceResource::toVariant(m_engine, array, length, succeeded); \ + } else + +QVariant QV8SequenceWrapper::toVariant(v8::Handle<v8::Array> array, int typeHint, bool *succeeded) +{ + *succeeded = true; + uint32_t length = array->Length(); + FOREACH_QML_SEQUENCE_TYPE(SEQUENCE_TO_VARIANT) { /* else */ *succeeded = false; return QVariant(); } +} +#undef SEQUENCE_TO_VARIANT + +v8::Handle<v8::Value> QV8SequenceWrapper::IndexedSetter(quint32 index, v8::Local<v8::Value> value, const v8::AccessorInfo &info) +{ + QV8SequenceResource *sr = v8_resource_cast<QV8SequenceResource>(info.This()); + Q_ASSERT(sr); + return sr->indexedSetter(index, value); +} + +v8::Handle<v8::Value> QV8SequenceWrapper::IndexedGetter(quint32 index, const v8::AccessorInfo &info) +{ + QV8SequenceResource *sr = v8_resource_cast<QV8SequenceResource>(info.This()); + Q_ASSERT(sr); + return sr->indexedGetter(index); +} + +v8::Handle<v8::Boolean> QV8SequenceWrapper::IndexedDeleter(quint32 index, const v8::AccessorInfo &info) +{ + QV8SequenceResource *sr = v8_resource_cast<QV8SequenceResource>(info.This()); + Q_ASSERT(sr); + return sr->indexedDeleter(index); +} + +v8::Handle<v8::Array> QV8SequenceWrapper::IndexedEnumerator(const v8::AccessorInfo &info) +{ + QV8SequenceResource *sr = v8_resource_cast<QV8SequenceResource>(info.This()); + Q_ASSERT(sr); + return sr->indexedEnumerator(); +} + +v8::Handle<v8::Value> QV8SequenceWrapper::LengthGetter(v8::Local<v8::String> property, const v8::AccessorInfo &info) +{ + Q_UNUSED(property); + QV8SequenceResource *sr = v8_resource_cast<QV8SequenceResource>(info.This()); + Q_ASSERT(sr); + return v8::Integer::NewFromUnsigned(sr->lengthGetter()); +} + +void QV8SequenceWrapper::LengthSetter(v8::Local<v8::String> property, v8::Local<v8::Value> value, const v8::AccessorInfo &info) +{ + Q_UNUSED(property); + QV8SequenceResource *sr = v8_resource_cast<QV8SequenceResource>(info.This()); + Q_ASSERT(sr); + sr->lengthSetter(value); +} + +v8::Handle<v8::Value> QV8SequenceWrapper::ToStringGetter(v8::Local<v8::String> property, const v8::AccessorInfo &info) +{ + Q_UNUSED(property); + return info.Data(); +} + +v8::Handle<v8::Value> QV8SequenceWrapper::ValueOfGetter(v8::Local<v8::String> property, + const v8::AccessorInfo &info) +{ + Q_UNUSED(property); + return info.Data(); +} + +v8::Handle<v8::Value> QV8SequenceWrapper::ToString(const v8::Arguments &args) +{ + QV8SequenceResource *sr = v8_resource_cast<QV8SequenceResource>(args.This()); + Q_ASSERT(sr); + return sr->toString(); +} + +v8::Handle<v8::Value> QV8SequenceWrapper::ValueOf(const v8::Arguments &args) +{ + QV8SequenceResource *sr = v8_resource_cast<QV8SequenceResource>(args.This()); + Q_ASSERT(sr); + v8::Handle<v8::Value> tostringValue = sr->toString(); + if (!tostringValue.IsEmpty()) + return tostringValue; + return v8::Integer::NewFromUnsigned(sr->lengthGetter()); +} + +v8::Handle<v8::Value> QV8SequenceWrapper::Getter(v8::Local<v8::String> property, + const v8::AccessorInfo &info) +{ + Q_UNUSED(property); + Q_UNUSED(info); + return v8::Handle<v8::Value>(); +} + +v8::Handle<v8::Value> QV8SequenceWrapper::Setter(v8::Local<v8::String> property, + v8::Local<v8::Value> value, + const v8::AccessorInfo &info) +{ + Q_UNUSED(property); + Q_UNUSED(info); + return value; +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/v8/qv8sequencewrapper_p.h b/src/qml/qml/v8/qv8sequencewrapper_p.h new file mode 100644 index 0000000000..104135ff76 --- /dev/null +++ b/src/qml/qml/v8/qv8sequencewrapper_p.h @@ -0,0 +1,106 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV8SEQUENCEWRAPPER_P_H +#define QV8SEQUENCEWRAPPER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qglobal.h> +#include <QtCore/qvariant.h> +#include <private/qv8_p.h> + +QT_BEGIN_NAMESPACE + +class QV8Engine; +class QV8ObjectResource; +class QV8SequenceWrapper +{ +public: + QV8SequenceWrapper(); + ~QV8SequenceWrapper(); + + void init(QV8Engine *); + void destroy(); + + bool isEqual(QV8ObjectResource *lhs, const QVariant &rhs); + bool isEqual(QV8ObjectResource *lhs, QV8ObjectResource *rhs); + quint32 sequenceLength(QV8ObjectResource *); + + v8::Local<v8::Object> newSequence(int sequenceTypeId, QObject *object, int propertyIndex, bool *succeeded); + v8::Local<v8::Object> fromVariant(const QVariant& v, bool *succeeded); + QVariant toVariant(QV8ObjectResource *); + QVariant toVariant(v8::Handle<v8::Array> array, int typeHint, bool *succeeded); + +private: + QV8Engine *m_engine; + + v8::Persistent<v8::Function> m_constructor; + v8::Persistent<v8::Function> m_toString; + v8::Persistent<v8::Function> m_valueOf; + + static v8::Handle<v8::Value> IndexedGetter(quint32 index, const v8::AccessorInfo &info); + static v8::Handle<v8::Value> IndexedSetter(quint32 index, v8::Local<v8::Value> value, const v8::AccessorInfo &info); + static v8::Handle<v8::Boolean> IndexedDeleter(quint32 index, const v8::AccessorInfo &info); + static v8::Handle<v8::Array> IndexedEnumerator(const v8::AccessorInfo &info); + static v8::Handle<v8::Value> LengthGetter(v8::Local<v8::String> property, const v8::AccessorInfo &info); + static void LengthSetter(v8::Local<v8::String> property, v8::Local<v8::Value> value, const v8::AccessorInfo &info); + static v8::Handle<v8::Value> ToStringGetter(v8::Local<v8::String> property, const v8::AccessorInfo &info); + static v8::Handle<v8::Value> ToString(const v8::Arguments &args); + static v8::Handle<v8::Value> ValueOfGetter(v8::Local<v8::String> property, const v8::AccessorInfo &info); + static v8::Handle<v8::Value> ValueOf(const v8::Arguments &args); + static v8::Handle<v8::Value> Getter(v8::Local<v8::String> property, const v8::AccessorInfo &info); + static v8::Handle<v8::Value> Setter(v8::Local<v8::String> property, v8::Local<v8::Value> value, const v8::AccessorInfo &info); +}; + + +QT_END_NAMESPACE + +#endif // QV8SEQUENCEWRAPPER_P_H diff --git a/src/qml/qml/v8/qv8sequencewrapper_p_p.h b/src/qml/qml/v8/qv8sequencewrapper_p_p.h new file mode 100644 index 0000000000..9d519809fa --- /dev/null +++ b/src/qml/qml/v8/qv8sequencewrapper_p_p.h @@ -0,0 +1,503 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV8SEQUENCEWRAPPER_P_P_H +#define QV8SEQUENCEWRAPPER_P_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 <private/qqmlengine_p.h> +#include <private/qqmlmetatype_p.h> + +QT_BEGIN_NAMESPACE + +/*! + \internal + \class QV8SequenceResource + \brief The abstract base class of the external resource used in sequence type objects + + Every sequence type object returned by QV8SequenceWrapper::fromVariant() or + QV8SequenceWrapper::newSequence() has a type-specific QV8SequenceResource which + contains the type name, the meta type ids of the sequence and sequence element + types, as well as either the sequence data (copy) or object pointer and property + index (reference) data associated with the sequence. + */ +class QV8SequenceResource : public QV8ObjectResource +{ + V8_RESOURCE_TYPE(SequenceType); + +public: + virtual ~QV8SequenceResource() {} + + enum ObjectType { Reference, Copy }; + + virtual QVariant toVariant() = 0; + virtual bool isEqual(const QV8SequenceResource *v) = 0; + + virtual quint32 lengthGetter() = 0; + virtual void lengthSetter(v8::Handle<v8::Value> value) = 0; + virtual v8::Handle<v8::Value> indexedSetter(quint32 index, v8::Handle<v8::Value> value) = 0; + virtual v8::Handle<v8::Value> indexedGetter(quint32 index) = 0; + virtual v8::Handle<v8::Boolean> indexedDeleter(quint32 index) = 0; + virtual v8::Handle<v8::Array> indexedEnumerator() = 0; + virtual v8::Handle<v8::Value> toString() = 0; + + ObjectType objectType; + QByteArray typeName; + int sequenceMetaTypeId; + int elementMetaTypeId; + +protected: + QV8SequenceResource(QV8Engine *engine, ObjectType type, const QByteArray &name, int sequenceId, int elementId) + : QV8ObjectResource(engine), objectType(type), typeName(name), sequenceMetaTypeId(sequenceId), elementMetaTypeId(elementId) + { + } +}; + +// helper function to generate valid warnings if errors occur during sequence operations. +static void generateWarning(QV8Engine *engine, const QString& description) +{ + if (!engine) + return; + v8::Local<v8::StackTrace> currStack = v8::StackTrace::CurrentStackTrace(1); + if (currStack.IsEmpty()) + return; + v8::Local<v8::StackFrame> currFrame = currStack->GetFrame(0); + if (currFrame.IsEmpty()) + return; + + QQmlError retn; + retn.setDescription(description); + retn.setLine(currFrame->GetLineNumber()); + retn.setUrl(QUrl(engine->toString(currFrame->GetScriptName()))); + QQmlEnginePrivate::warning(engine->engine(), retn); +} + + +static int convertV8ValueToInt(QV8Engine *, v8::Handle<v8::Value> v) +{ + return v->Int32Value(); +} + +static v8::Handle<v8::Value> convertIntToV8Value(QV8Engine *, int v) +{ + return v8::Integer::New(v); +} + +static QString convertIntToString(QV8Engine *, int v) +{ + return QString::number(v); +} + +static qreal convertV8ValueToReal(QV8Engine *, v8::Handle<v8::Value> v) +{ + return v->NumberValue(); +} + +static v8::Handle<v8::Value> convertRealToV8Value(QV8Engine *, qreal v) +{ + return v8::Number::New(v); +} + +static QString convertRealToString(QV8Engine *, qreal v) +{ + return QString::number(v); +} + +static bool convertV8ValueToBool(QV8Engine *, v8::Handle<v8::Value> v) +{ + return v->BooleanValue(); +} + +static v8::Handle<v8::Value> convertBoolToV8Value(QV8Engine *, bool v) +{ + return v8::Boolean::New(v); +} + +static QString convertBoolToString(QV8Engine *, bool v) +{ + if (v) + return QLatin1String("true"); + return QLatin1String("false"); +} + +static QString convertV8ValueToString(QV8Engine *e, v8::Handle<v8::Value> v) +{ + return e->toString(v->ToString()); +} + +static v8::Handle<v8::Value> convertStringToV8Value(QV8Engine *e, const QString &v) +{ + return e->toString(v); +} + +static QString convertStringToString(QV8Engine *, const QString &v) +{ + return v; +} + +static QString convertV8ValueToQString(QV8Engine *e, v8::Handle<v8::Value> v) +{ + return e->toString(v->ToString()); +} + +static v8::Handle<v8::Value> convertQStringToV8Value(QV8Engine *e, const QString &v) +{ + return e->toString(v); +} + +static QString convertQStringToString(QV8Engine *, const QString &v) +{ + return v; +} + +static QUrl convertV8ValueToUrl(QV8Engine *e, v8::Handle<v8::Value> v) +{ + QUrl u; + u.setEncodedUrl(e->toString(v->ToString()).toUtf8(), QUrl::TolerantMode); + return u; +} + +static v8::Handle<v8::Value> convertUrlToV8Value(QV8Engine *e, const QUrl &v) +{ + return e->toString(QLatin1String(v.toEncoded().data())); +} + +static QString convertUrlToString(QV8Engine *, const QUrl &v) +{ + return v.toString(); +} + + +/* + \internal + \class QV8<Type>SequenceResource + \brief The external resource used in sequence type objects + + Every sequence type object returned by QV8SequenceWrapper::newSequence() has + a QV8<Type>SequenceResource which contains a property index and a pointer + to the object which contains the property. + + Every sequence type object returned by QV8SequenceWrapper::fromVariant() has + a QV8<Type>SequenceResource which contains a copy of the sequence value. + Operations on the sequence are implemented directly in terms of that sequence data. + + There exists one QV8<Type>SequenceResource instance for every JavaScript Object + (sequence) instance returned from QV8SequenceWrapper::newSequence() or + QV8SequenceWrapper::fromVariant(). + */ + +// F(elementType, elementTypeName, sequenceType, defaultValue) +#define FOREACH_QML_SEQUENCE_TYPE(F) \ + F(int, Int, QList<int>, 0) \ + F(qreal, Real, QList<qreal>, 0.0) \ + F(bool, Bool, QList<bool>, false) \ + F(QString, String, QList<QString>, QString()) \ + F(QString, QString, QStringList, QString()) \ + F(QUrl, Url, QList<QUrl>, QUrl()) + +#define QML_SEQUENCE_TYPE_RESOURCE(SequenceElementType, SequenceElementTypeName, SequenceType, DefaultValue, ConversionToV8fn, ConversionFromV8fn, ToStringfn) \ + QT_END_NAMESPACE \ + Q_DECLARE_METATYPE(SequenceType) \ + QT_BEGIN_NAMESPACE \ + class QV8##SequenceElementTypeName##SequenceResource : public QV8SequenceResource { \ + public:\ + QV8##SequenceElementTypeName##SequenceResource(QV8Engine *engine, QObject *obj, int propIdx) \ + : QV8SequenceResource(engine, QV8SequenceResource::Reference, #SequenceType, qMetaTypeId<SequenceType>(), qMetaTypeId<SequenceElementType>()) \ + , object(obj), propertyIndex(propIdx) \ + { \ + } \ + QV8##SequenceElementTypeName##SequenceResource(QV8Engine *engine, const SequenceType &value) \ + : QV8SequenceResource(engine, QV8SequenceResource::Copy, #SequenceType, qMetaTypeId<SequenceType>(), qMetaTypeId<SequenceElementType>()) \ + , object(0), propertyIndex(-1), c(value) \ + { \ + } \ + ~QV8##SequenceElementTypeName##SequenceResource() \ + { \ + } \ + static QVariant toVariant(QV8Engine *e, v8::Handle<v8::Array> array, uint32_t length, bool *succeeded) \ + { \ + SequenceType list; \ + for (uint32_t ii = 0; ii < length; ++ii) { \ + list.append(ConversionFromV8fn(e, array->Get(ii))); \ + } \ + *succeeded = true; \ + return QVariant::fromValue<SequenceType>(list); \ + } \ + QVariant toVariant() \ + { \ + if (objectType == QV8SequenceResource::Reference) { \ + if (!object) \ + return QVariant(); \ + loadReference(); \ + } \ + return QVariant::fromValue<SequenceType>(c); \ + } \ + bool isEqual(const QV8SequenceResource *v) \ + { \ + /* Note: two different sequences can never be equal (even if they */ \ + /* contain the same elements in the same order) in order to */ \ + /* maintain JavaScript semantics. However, if they both reference */ \ + /* the same QObject+propertyIndex, they are equal. */ \ + if (objectType == QV8SequenceResource::Reference && v->objectType == QV8SequenceResource::Reference) { \ + if (sequenceMetaTypeId == v->sequenceMetaTypeId) { \ + const QV8##SequenceElementTypeName##SequenceResource *rhs = static_cast<const QV8##SequenceElementTypeName##SequenceResource *>(v); \ + return (object != 0 && object == rhs->object && propertyIndex == rhs->propertyIndex); \ + } \ + } else if (objectType == QV8SequenceResource::Copy && v->objectType == QV8SequenceResource::Copy) { \ + if (sequenceMetaTypeId == v->sequenceMetaTypeId) { \ + const QV8##SequenceElementTypeName##SequenceResource *rhs = static_cast<const QV8##SequenceElementTypeName##SequenceResource *>(v); \ + return (this == rhs); \ + } \ + } \ + return false; \ + } \ + quint32 lengthGetter() \ + { \ + if (objectType == QV8SequenceResource::Reference) { \ + if (!object) \ + return 0; \ + loadReference(); \ + } \ + return static_cast<quint32>(c.count()); \ + } \ + void lengthSetter(v8::Handle<v8::Value> value) \ + { \ + /* Get the new required length */ \ + if (value.IsEmpty() || !value->IsUint32()) \ + return; \ + quint32 newLength = value->Uint32Value(); \ + /* Qt containers have int (rather than uint) allowable indexes. */ \ + if (newLength > INT_MAX) { \ + generateWarning(engine, QLatin1String("Index out of range during length set")); \ + return; \ + } \ + /* Read the sequence from the QObject property if we're a reference */ \ + if (objectType == QV8SequenceResource::Reference) { \ + if (!object) \ + return; \ + loadReference(); \ + } \ + /* Determine whether we need to modify the sequence */ \ + qint32 newCount = static_cast<qint32>(newLength); \ + qint32 count = c.count(); \ + if (newCount == count) { \ + return; \ + } else if (newCount > count) { \ + /* according to ECMA262r3 we need to insert */ \ + /* undefined values increasing length to newLength. */ \ + /* We cannot, so we insert default-values instead. */ \ + while (newCount > count++) { \ + QT_TRY { \ + c.append(DefaultValue); \ + } QT_CATCH (std::bad_alloc &exception) { \ + generateWarning(engine, QString(QLatin1String(exception.what()) \ + + QLatin1String(" during length set"))); \ + return; /* failed; don't write back any result. */ \ + } \ + } \ + } else { \ + /* according to ECMA262r3 we need to remove */ \ + /* elements until the sequence is the required length. */ \ + while (newCount < count) { \ + count--; \ + c.removeAt(count); \ + } \ + } \ + /* write back if required. */ \ + if (objectType == QV8SequenceResource::Reference) { \ + /* write back. already checked that object is non-null, so skip that check here. */ \ + storeReference(); \ + } \ + return; \ + } \ + v8::Handle<v8::Value> indexedSetter(quint32 index, v8::Handle<v8::Value> value) \ + { \ + /* Qt containers have int (rather than uint) allowable indexes. */ \ + if (index > INT_MAX) { \ + generateWarning(engine, QLatin1String("Index out of range during indexed set")); \ + return v8::Undefined(); \ + } \ + if (objectType == QV8SequenceResource::Reference) { \ + if (!object) \ + return v8::Undefined(); \ + loadReference(); \ + } \ + /* modify the sequence */ \ + SequenceElementType elementValue = ConversionFromV8fn(engine, value); \ + qint32 count = c.count(); \ + qint32 signedIdx = static_cast<qint32>(index); \ + if (signedIdx == count) { \ + c.append(elementValue); \ + } else if (signedIdx < count) { \ + c[index] = elementValue; \ + } else { \ + /* according to ECMA262r3 we need to insert */ \ + /* the value at the given index, increasing length to index+1. */ \ + QT_TRY { \ + while (signedIdx > count++) { \ + c.append(DefaultValue); \ + } \ + c.append(elementValue); \ + } QT_CATCH (std::bad_alloc &exception) { \ + generateWarning(engine, QString(QLatin1String(exception.what()) \ + + QLatin1String(" during indexed set"))); \ + return v8::Undefined(); /* failed; don't write back any result. */ \ + } \ + } \ + /* write back. already checked that object is non-null, so skip that check here. */ \ + if (objectType == QV8SequenceResource::Reference) \ + storeReference(); \ + return value; \ + } \ + v8::Handle<v8::Value> indexedGetter(quint32 index) \ + { \ + /* Qt containers have int (rather than uint) allowable indexes. */ \ + if (index > INT_MAX) { \ + generateWarning(engine, QLatin1String("Index out of range during indexed get")); \ + return v8::Undefined(); \ + } \ + if (objectType == QV8SequenceResource::Reference) { \ + if (!object) \ + return v8::Undefined(); \ + loadReference(); \ + } \ + qint32 count = c.count(); \ + qint32 signedIdx = static_cast<qint32>(index); \ + if (signedIdx < count) \ + return ConversionToV8fn(engine, c.at(signedIdx)); \ + return v8::Undefined(); \ + } \ + v8::Handle<v8::Boolean> indexedDeleter(quint32 index) \ + { \ + /* Qt containers have int (rather than uint) allowable indexes. */ \ + if (index > INT_MAX) \ + return v8::Boolean::New(false); \ + /* Read in the sequence from the QObject */ \ + if (objectType == QV8SequenceResource::Reference) { \ + if (!object) \ + return v8::Boolean::New(false); \ + loadReference(); \ + } \ + qint32 signedIdx = static_cast<qint32>(index); \ + if (signedIdx < c.count()) { \ + /* according to ECMA262r3 it should be Undefined, */ \ + /* but we cannot, so we insert a default-value instead. */ \ + c.replace(signedIdx, DefaultValue); \ + if (objectType == QV8SequenceResource::Reference) { \ + /* write back. already checked that object is non-null, so skip that check here. */ \ + storeReference(); \ + } \ + return v8::Boolean::New(true); \ + } \ + return v8::Boolean::New(false); \ + } \ + v8::Handle<v8::Array> indexedEnumerator() \ + { \ + if (objectType == QV8SequenceResource::Reference) { \ + if (!object) \ + return v8::Handle<v8::Array>(); \ + loadReference(); \ + } \ + qint32 count = c.count(); \ + v8::Local<v8::Array> retn = v8::Array::New(count); \ + for (qint32 i = 0; i < count; ++i) { \ + retn->Set(static_cast<quint32>(i), v8::Integer::NewFromUnsigned(static_cast<quint32>(i))); \ + } \ + return retn; \ + } \ + v8::Handle<v8::Value> toString() \ + { \ + if (objectType == QV8SequenceResource::Reference) { \ + if (!object) \ + return v8::Undefined(); \ + loadReference(); \ + } \ + QString str; \ + qint32 count = c.count(); \ + for (qint32 i = 0; i < count; ++i) { \ + str += QString(QLatin1String("%1,")).arg(ToStringfn(engine, c[i])); \ + } \ + str.chop(1); \ + return engine->toString(str); \ + } \ + void loadReference() \ + { \ + Q_ASSERT(object); \ + Q_ASSERT(objectType == QV8SequenceResource::Reference); \ + void *a[] = { &c, 0 }; \ + QMetaObject::metacall(object, QMetaObject::ReadProperty, propertyIndex, a); \ + } \ + void storeReference() \ + { \ + Q_ASSERT(object); \ + Q_ASSERT(objectType == QV8SequenceResource::Reference); \ + int status = -1; \ + QQmlPropertyPrivate::WriteFlags flags = \ + QQmlPropertyPrivate::DontRemoveBinding; \ + void *a[] = { &c, 0, &status, &flags }; \ + QMetaObject::metacall(object, QMetaObject::WriteProperty, propertyIndex, a); \ + } \ + private: \ + QQmlGuard<QObject> object; \ + int propertyIndex; \ + SequenceType c; \ + }; + +#define GENERATE_QML_SEQUENCE_TYPE_RESOURCE(ElementType, ElementTypeName, SequenceType, DefaultValue) \ + QML_SEQUENCE_TYPE_RESOURCE(ElementType, ElementTypeName, SequenceType, DefaultValue, convert##ElementTypeName##ToV8Value, convertV8ValueTo##ElementTypeName, convert##ElementTypeName##ToString) + +FOREACH_QML_SEQUENCE_TYPE(GENERATE_QML_SEQUENCE_TYPE_RESOURCE) +#undef GENERATE_QML_SEQUENCE_TYPE_RESOURCE +#undef QML_SEQUENCE_TYPE_RESOURCE + +QT_END_NAMESPACE + +#endif // QV8SEQUENCEWRAPPER_P_P_H diff --git a/src/qml/qml/v8/qv8sqlerrors.cpp b/src/qml/qml/v8/qv8sqlerrors.cpp new file mode 100644 index 0000000000..8c5856ea18 --- /dev/null +++ b/src/qml/qml/v8/qv8sqlerrors.cpp @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv8sqlerrors_p.h" +#include "qv8engine_p.h" + +QT_BEGIN_NAMESPACE + +void qt_add_sqlexceptions(QV8Engine *engine) +{ + // SQL Exception + v8::PropertyAttribute attributes = (v8::PropertyAttribute)(v8::ReadOnly | v8::DontEnum | v8::DontDelete); + + v8::Local<v8::Object> sqlexception = v8::Object::New(); + sqlexception->Set(v8::String::New("UNKNOWN_ERR"), v8::Integer::New(SQLEXCEPTION_UNKNOWN_ERR), attributes); + sqlexception->Set(v8::String::New("DATABASE_ERR"), v8::Integer::New(SQLEXCEPTION_DATABASE_ERR), attributes); + sqlexception->Set(v8::String::New("VERSION_ERR"), v8::Integer::New(SQLEXCEPTION_VERSION_ERR), attributes); + sqlexception->Set(v8::String::New("TOO_LARGE_ERR"), v8::Integer::New(SQLEXCEPTION_TOO_LARGE_ERR), attributes); + sqlexception->Set(v8::String::New("QUOTA_ERR"), v8::Integer::New(SQLEXCEPTION_QUOTA_ERR), attributes); + sqlexception->Set(v8::String::New("SYNTAX_ERR"), v8::Integer::New(SQLEXCEPTION_SYNTAX_ERR), attributes); + sqlexception->Set(v8::String::New("CONSTRAINT_ERR"), v8::Integer::New(SQLEXCEPTION_CONSTRAINT_ERR), attributes); + sqlexception->Set(v8::String::New("TIMEOUT_ERR"), v8::Integer::New(SQLEXCEPTION_TIMEOUT_ERR), attributes); + engine->global()->Set(v8::String::New("SQLException"), sqlexception); +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/v8/qv8sqlerrors_p.h b/src/qml/qml/v8/qv8sqlerrors_p.h new file mode 100644 index 0000000000..c799be6e7c --- /dev/null +++ b/src/qml/qml/v8/qv8sqlerrors_p.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV8SQLERRORS_P_H +#define QV8SQLERRORS_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qglobal.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE +#define SQLEXCEPTION_UNKNOWN_ERR 1 +#define SQLEXCEPTION_DATABASE_ERR 2 +#define SQLEXCEPTION_VERSION_ERR 3 +#define SQLEXCEPTION_TOO_LARGE_ERR 4 +#define SQLEXCEPTION_QUOTA_ERR 5 +#define SQLEXCEPTION_SYNTAX_ERR 6 +#define SQLEXCEPTION_CONSTRAINT_ERR 7 +#define SQLEXCEPTION_TIMEOUT_ERR 8 + +class QV8Engine; +void qt_add_sqlexceptions(QV8Engine *engine); + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QV8SQLERRORS_P_H diff --git a/src/qml/qml/v8/qv8stringwrapper.cpp b/src/qml/qml/v8/qv8stringwrapper.cpp new file mode 100644 index 0000000000..d4abbdc60b --- /dev/null +++ b/src/qml/qml/v8/qv8stringwrapper.cpp @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv8stringwrapper_p.h" +#include "qjsconverter_p.h" +#include "qjsconverter_impl_p.h" + +QT_BEGIN_NAMESPACE + +QV8StringWrapper::QV8StringWrapper() +{ +} + +QV8StringWrapper::~QV8StringWrapper() +{ +} + +void QV8StringWrapper::init() +{ +} + +void QV8StringWrapper::destroy() +{ +} + +v8::Local<v8::String> QV8StringWrapper::toString(const QString &qstr) +{ + return QJSConverter::toString(qstr); +} + +QString QV8StringWrapper::toString(v8::Handle<v8::String> jsstr) +{ + if (jsstr.IsEmpty()) { + return QString(); + } else { + return QJSConverter::toString(jsstr); + } +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/v8/qv8stringwrapper_p.h b/src/qml/qml/v8/qv8stringwrapper_p.h new file mode 100644 index 0000000000..1609720298 --- /dev/null +++ b/src/qml/qml/v8/qv8stringwrapper_p.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLV8STRINGWRAPPER_P_H +#define QQMLV8STRINGWRAPPER_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 <QtQml/qtqmlglobal.h> + +#include <QtCore/qstring.h> +#include <private/qv8_p.h> + +QT_BEGIN_NAMESPACE + +class Q_QML_EXPORT QV8StringWrapper +{ +public: + QV8StringWrapper(); + ~QV8StringWrapper(); + + void init(); + void destroy(); + + v8::Local<v8::String> toString(const QString &); + QString toString(v8::Handle<v8::String>); +}; + +QT_END_NAMESPACE + +#endif // QQMLV8STRINGWRAPPER_P_H diff --git a/src/qml/qml/v8/qv8typewrapper.cpp b/src/qml/qml/v8/qv8typewrapper.cpp new file mode 100644 index 0000000000..dbf369e678 --- /dev/null +++ b/src/qml/qml/v8/qv8typewrapper.cpp @@ -0,0 +1,314 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv8contextwrapper_p.h" +#include "qv8engine_p.h" + +#include <private/qqmlengine_p.h> +#include <private/qqmlcontext_p.h> + +#include <private/qjsvalue_p.h> +#include <private/qscript_impl_p.h> + +QT_BEGIN_NAMESPACE + +class QV8TypeResource : public QV8ObjectResource +{ + V8_RESOURCE_TYPE(TypeType); + +public: + QV8TypeResource(QV8Engine *engine); + virtual ~QV8TypeResource(); + + QV8TypeWrapper::TypeNameMode mode; + + QQmlGuard<QObject> object; + + QQmlType *type; + QQmlTypeNameCache *typeNamespace; + const void *importNamespace; +}; + +QV8TypeResource::QV8TypeResource(QV8Engine *engine) +: QV8ObjectResource(engine), mode(QV8TypeWrapper::IncludeEnums), type(0), typeNamespace(0), importNamespace(0) +{ +} + +QV8TypeResource::~QV8TypeResource() +{ + if (typeNamespace) typeNamespace->release(); +} + +QV8TypeWrapper::QV8TypeWrapper() +: m_engine(0) +{ +} + +QV8TypeWrapper::~QV8TypeWrapper() +{ +} + +void QV8TypeWrapper::destroy() +{ + qPersistentDispose(m_constructor); +} + +void QV8TypeWrapper::init(QV8Engine *engine) +{ + m_engine = engine; + v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New(); + ft->InstanceTemplate()->SetNamedPropertyHandler(Getter, Setter); + ft->InstanceTemplate()->SetHasExternalResource(true); + m_constructor = qPersistentNew<v8::Function>(ft->GetFunction()); +} + +// Returns a type wrapper for type t on o. This allows access of enums, and attached properties. +v8::Local<v8::Object> QV8TypeWrapper::newObject(QObject *o, QQmlType *t, TypeNameMode mode) +{ + Q_ASSERT(t); + // XXX NewInstance() should be optimized + v8::Local<v8::Object> rv = m_constructor->NewInstance(); + QV8TypeResource *r = new QV8TypeResource(m_engine); + r->mode = mode; r->object = o; r->type = t; + rv->SetExternalResource(r); + return rv; +} + +// Returns a type wrapper for importNamespace (of t) on o. This allows nested resolution of a type in a +// namespace. +v8::Local<v8::Object> QV8TypeWrapper::newObject(QObject *o, QQmlTypeNameCache *t, + const void *importNamespace, TypeNameMode mode) +{ + Q_ASSERT(t); + Q_ASSERT(importNamespace); + // XXX NewInstance() should be optimized + v8::Local<v8::Object> rv = m_constructor->NewInstance(); + QV8TypeResource *r = new QV8TypeResource(m_engine); + t->addref(); + r->mode = mode; r->object = o; r->typeNamespace = t; r->importNamespace = importNamespace; + rv->SetExternalResource(r); + return rv; +} + +QVariant QV8TypeWrapper::toVariant(QV8ObjectResource *r) +{ + Q_ASSERT(r->resourceType() == QV8ObjectResource::TypeType); + QV8TypeResource *resource = static_cast<QV8TypeResource *>(r); + QV8Engine *v8engine = resource->engine; + + if (resource->typeNamespace) { + if (QQmlMetaType::ModuleApiInstance *moduleApi = resource->typeNamespace->moduleApi(resource->importNamespace)) { + 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; + } + + if (moduleApi->qobjectApi) { + return QVariant::fromValue<QObject*>(moduleApi->qobjectApi); + } + } + } + + // only QObject Module API can be converted to a variant. + return QVariant(); +} + +v8::Handle<v8::Value> QV8TypeWrapper::Getter(v8::Local<v8::String> property, + const v8::AccessorInfo &info) +{ + QV8TypeResource *resource = v8_resource_cast<QV8TypeResource>(info.This()); + + if (!resource) + return v8::Undefined(); + + QV8Engine *v8engine = resource->engine; + QObject *object = resource->object; + + QHashedV8String propertystring(property); + + if (resource->type) { + QQmlType *type = resource->type; + + if (QV8Engine::startsWithUpper(property)) { + int value = type->enumValue(propertystring); + if (-1 != value) + return v8::Integer::New(value); + + // Fall through to return empty handle + + } else if (resource->object) { + QObject *ao = qmlAttachedPropertiesObjectById(type->attachedPropertiesId(), object); + if (ao) + return v8engine->qobjectWrapper()->getProperty(ao, propertystring, + QV8QObjectWrapper::IgnoreRevision); + + // Fall through to return empty handle + } + + // Fall through to return empty handle + + } else if (resource->typeNamespace) { + Q_ASSERT(resource->importNamespace); + QQmlTypeNameCache::Result r = resource->typeNamespace->query(propertystring, + resource->importNamespace); + + if (r.isValid()) { + if (r.type) { + return v8engine->typeWrapper()->newObject(object, r.type, resource->mode); + } else if (r.scriptIndex != -1) { + int index = r.scriptIndex; + QQmlContextData *context = v8engine->callingContext(); + if (index < context->importedScripts.count()) + return context->importedScripts.at(index); + } + + return v8::Undefined(); + } else if (QQmlMetaType::ModuleApiInstance *moduleApi = resource->typeNamespace->moduleApi(resource->importNamespace)) { + + 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; + } + + if (moduleApi->qobjectApi) { + // check for enum value + if (QV8Engine::startsWithUpper(property)) { + if (resource->mode == IncludeEnums) { + QString name = v8engine->toString(property); + + // ### Optimize + QByteArray enumName = name.toUtf8(); + const QMetaObject *metaObject = moduleApi->qobjectApi->metaObject(); + for (int ii = metaObject->enumeratorCount() - 1; ii >= 0; --ii) { + QMetaEnum e = metaObject->enumerator(ii); + bool ok; + int value = e.keyToValue(enumName.constData(), &ok); + if (ok) + return v8::Integer::New(value); + } + } + } + + // check for property. + v8::Handle<v8::Value> rv = v8engine->qobjectWrapper()->getProperty(moduleApi->qobjectApi, propertystring, QV8QObjectWrapper::IgnoreRevision); + return rv; + } else if (!moduleApi->scriptApi.isUndefined()) { + // NOTE: if used in a binding, changes will not trigger re-evaluation since non-NOTIFYable. + QJSValuePrivate *apiprivate = QJSValuePrivate::get(moduleApi->scriptApi); + QScopedPointer<QJSValuePrivate> propertyValue(apiprivate->property(property).give()); + return propertyValue->asV8Value(v8engine); + } else { + return v8::Handle<v8::Value>(); + } + } + + // Fall through to return empty handle + + } else { + Q_ASSERT(!"Unreachable"); + } + + return v8::Handle<v8::Value>(); +} + +v8::Handle<v8::Value> QV8TypeWrapper::Setter(v8::Local<v8::String> property, + v8::Local<v8::Value> value, + const v8::AccessorInfo &info) +{ + QV8TypeResource *resource = v8_resource_cast<QV8TypeResource>(info.This()); + + if (!resource) + return value; + + QV8Engine *v8engine = resource->engine; + + QHashedV8String propertystring(property); + + if (resource->type && resource->object) { + QQmlType *type = resource->type; + QObject *object = resource->object; + QObject *ao = qmlAttachedPropertiesObjectById(type->attachedPropertiesId(), object); + if (ao) + v8engine->qobjectWrapper()->setProperty(ao, propertystring, value, + QV8QObjectWrapper::IgnoreRevision); + } else if (resource->typeNamespace) { + if (QQmlMetaType::ModuleApiInstance *moduleApi = resource->typeNamespace->moduleApi(resource->importNamespace)) { + 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; + } + + if (moduleApi->qobjectApi) { + v8engine->qobjectWrapper()->setProperty(moduleApi->qobjectApi, propertystring, value, + QV8QObjectWrapper::IgnoreRevision); + } else if (!moduleApi->scriptApi.isUndefined()) { + QScopedPointer<QJSValuePrivate> setvalp(new QJSValuePrivate(v8engine, value)); + QJSValuePrivate *apiprivate = QJSValuePrivate::get(moduleApi->scriptApi); + if (apiprivate->propertyFlags(property) & QJSValuePrivate::ReadOnly) { + QString error = QLatin1String("Cannot assign to read-only property \"") + + v8engine->toString(property) + QLatin1Char('\"'); + v8::ThrowException(v8::Exception::Error(v8engine->toString(error))); + } else { + apiprivate->setProperty(property, setvalp.data()); + } + } + } + } + + return value; +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/v8/qv8typewrapper_p.h b/src/qml/qml/v8/qv8typewrapper_p.h new file mode 100644 index 0000000000..8b658da6fb --- /dev/null +++ b/src/qml/qml/v8/qv8typewrapper_p.h @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV8TYPEWRAPPER_P_H +#define QV8TYPEWRAPPER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qglobal.h> +#include <private/qv8_p.h> + +QT_BEGIN_NAMESPACE + +class QObject; +class QV8Engine; +class QQmlType; +class QQmlTypeNameCache; +class QV8TypeWrapper +{ +public: + QV8TypeWrapper(); + ~QV8TypeWrapper(); + + void init(QV8Engine *); + void destroy(); + + enum TypeNameMode { IncludeEnums, ExcludeEnums }; + v8::Local<v8::Object> newObject(QObject *, QQmlType *, TypeNameMode = IncludeEnums); + v8::Local<v8::Object> newObject(QObject *, QQmlTypeNameCache *, const void *, + TypeNameMode = IncludeEnums); + QVariant toVariant(QV8ObjectResource *); + +private: + static v8::Handle<v8::Value> Getter(v8::Local<v8::String> property, + const v8::AccessorInfo &info); + static v8::Handle<v8::Value> Setter(v8::Local<v8::String> property, + v8::Local<v8::Value> value, + const v8::AccessorInfo &info); + + QV8Engine *m_engine; + v8::Persistent<v8::Function> m_constructor; +}; + +QT_END_NAMESPACE + +#endif // QV8TYPEWRAPPER_P_H + diff --git a/src/qml/qml/v8/qv8valuetypewrapper.cpp b/src/qml/qml/v8/qv8valuetypewrapper.cpp new file mode 100644 index 0000000000..54d871d5f0 --- /dev/null +++ b/src/qml/qml/v8/qv8valuetypewrapper.cpp @@ -0,0 +1,387 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv8valuetypewrapper_p.h" +#include "qv8engine_p.h" + +#include <private/qqmlvaluetype_p.h> +#include <private/qqmlbinding_p.h> + +QT_BEGIN_NAMESPACE + +class QV8ValueTypeResource : public QV8ObjectResource +{ + V8_RESOURCE_TYPE(ValueTypeType); + +public: + enum ObjectType { Reference, Copy }; + + QV8ValueTypeResource(QV8Engine *engine, ObjectType objectType); + + ObjectType objectType; + QQmlValueType *type; +}; + +class QV8ValueTypeReferenceResource : public QV8ValueTypeResource +{ +public: + QV8ValueTypeReferenceResource(QV8Engine *engine); + + QQmlGuard<QObject> object; + int property; +}; + +class QV8ValueTypeCopyResource : public QV8ValueTypeResource +{ +public: + QV8ValueTypeCopyResource(QV8Engine *engine); + + QVariant value; +}; + +QV8ValueTypeResource::QV8ValueTypeResource(QV8Engine *engine, ObjectType objectType) +: QV8ObjectResource(engine), objectType(objectType) +{ +} + +QV8ValueTypeReferenceResource::QV8ValueTypeReferenceResource(QV8Engine *engine) +: QV8ValueTypeResource(engine, Reference) +{ +} + +QV8ValueTypeCopyResource::QV8ValueTypeCopyResource(QV8Engine *engine) +: QV8ValueTypeResource(engine, Copy) +{ +} + +QV8ValueTypeWrapper::QV8ValueTypeWrapper() +: m_engine(0) +{ +} + +QV8ValueTypeWrapper::~QV8ValueTypeWrapper() +{ +} + +void QV8ValueTypeWrapper::destroy() +{ + qPersistentDispose(m_toString); + qPersistentDispose(m_constructor); + qPersistentDispose(m_toStringSymbol); +} + +static quint32 toStringHash = -1; + +void QV8ValueTypeWrapper::init(QV8Engine *engine) +{ + m_engine = engine; + m_toString = qPersistentNew<v8::Function>(v8::FunctionTemplate::New(ToString)->GetFunction()); + v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New(); + ft->InstanceTemplate()->SetNamedPropertyHandler(Getter, Setter); + ft->InstanceTemplate()->SetHasExternalResource(true); + ft->InstanceTemplate()->MarkAsUseUserObjectComparison(); + ft->InstanceTemplate()->SetAccessor(v8::String::New("toString"), ToStringGetter, 0, + m_toString, v8::DEFAULT, + v8::PropertyAttribute(v8::ReadOnly | v8::DontDelete)); + m_constructor = qPersistentNew<v8::Function>(ft->GetFunction()); + + m_toStringSymbol = qPersistentNew<v8::String>(v8::String::NewSymbol("toString")); + m_toStringString = QHashedV8String(m_toStringSymbol); + toStringHash = m_toStringString.hash(); +} + +v8::Local<v8::Object> QV8ValueTypeWrapper::newValueType(QObject *object, int property, QQmlValueType *type) +{ + // XXX NewInstance() should be optimized + v8::Local<v8::Object> rv = m_constructor->NewInstance(); + QV8ValueTypeReferenceResource *r = new QV8ValueTypeReferenceResource(m_engine); + r->type = type; r->object = object; r->property = property; + rv->SetExternalResource(r); + return rv; +} + +v8::Local<v8::Object> QV8ValueTypeWrapper::newValueType(const QVariant &value, QQmlValueType *type) +{ + // XXX NewInstance() should be optimized + v8::Local<v8::Object> rv = m_constructor->NewInstance(); + QV8ValueTypeCopyResource *r = new QV8ValueTypeCopyResource(m_engine); + r->type = type; r->value = value; + rv->SetExternalResource(r); + return rv; +} + +QVariant QV8ValueTypeWrapper::toVariant(v8::Handle<v8::Object> obj) +{ + QV8ValueTypeResource *r = v8_resource_cast<QV8ValueTypeResource>(obj); + if (r) return toVariant(r); + else return QVariant(); +} + +QVariant QV8ValueTypeWrapper::toVariant(QV8ObjectResource *r) +{ + Q_ASSERT(r->resourceType() == QV8ObjectResource::ValueTypeType); + QV8ValueTypeResource *resource = static_cast<QV8ValueTypeResource *>(r); + + if (resource->objectType == QV8ValueTypeResource::Reference) { + QV8ValueTypeReferenceResource *reference = static_cast<QV8ValueTypeReferenceResource *>(resource); + + if (reference->object) { + reference->type->read(reference->object, reference->property); + return reference->type->value(); + } else { + return QVariant(); + } + + } else { + Q_ASSERT(resource->objectType == QV8ValueTypeResource::Copy); + + QV8ValueTypeCopyResource *copy = static_cast<QV8ValueTypeCopyResource *>(resource); + + return copy->value; + } +} + +bool QV8ValueTypeWrapper::isEqual(QV8ObjectResource *r, const QVariant& value) +{ + Q_ASSERT(r->resourceType() == QV8ObjectResource::ValueTypeType); + QV8ValueTypeResource *resource = static_cast<QV8ValueTypeResource *>(r); + + if (resource->objectType == QV8ValueTypeResource::Reference) { + QV8ValueTypeReferenceResource *reference = static_cast<QV8ValueTypeReferenceResource *>(resource); + if (reference->object) { + reference->type->read(reference->object, reference->property); + return reference->type->isEqual(value); + } else { + return false; + } + } else { + Q_ASSERT(resource->objectType == QV8ValueTypeResource::Copy); + QV8ValueTypeCopyResource *copy = static_cast<QV8ValueTypeCopyResource *>(resource); + return (value == copy->value); + } +} + +v8::Handle<v8::Value> QV8ValueTypeWrapper::ToStringGetter(v8::Local<v8::String> property, + const v8::AccessorInfo &info) +{ + Q_UNUSED(property); + return info.Data(); +} + +v8::Handle<v8::Value> QV8ValueTypeWrapper::ToString(const v8::Arguments &args) +{ + QV8ValueTypeResource *resource = v8_resource_cast<QV8ValueTypeResource>(args.This()); + if (resource) { + if (resource->objectType == QV8ValueTypeResource::Reference) { + QV8ValueTypeReferenceResource *reference = static_cast<QV8ValueTypeReferenceResource *>(resource); + if (reference->object) { + reference->type->read(reference->object, reference->property); + return resource->engine->toString(resource->type->toString()); + } else { + return v8::Undefined(); + } + } else { + Q_ASSERT(resource->objectType == QV8ValueTypeResource::Copy); + QV8ValueTypeCopyResource *copy = static_cast<QV8ValueTypeCopyResource *>(resource); + QString result = copy->value.toString(); + if (result.isEmpty() && !copy->value.canConvert(QVariant::String)) { + result = QString::fromLatin1("QVariant(%0)").arg(QString::fromLatin1(copy->value.typeName())); + } + return resource->engine->toString(result); + } + } else { + return v8::Undefined(); + } +} + +v8::Handle<v8::Value> QV8ValueTypeWrapper::Getter(v8::Local<v8::String> property, + const v8::AccessorInfo &info) +{ + QV8ValueTypeResource *r = v8_resource_cast<QV8ValueTypeResource>(info.This()); + if (!r) return v8::Handle<v8::Value>(); + + QHashedV8String propertystring(property); + + { + // Comparing the hash first actually makes a measurable difference here, at least on x86 + quint32 hash = propertystring.hash(); + if (hash == toStringHash && + r->engine->valueTypeWrapper()->m_toStringString == propertystring) { + return r->engine->valueTypeWrapper()->m_toString; + } + } + + QQmlPropertyData local; + QQmlPropertyData *result = 0; + { + QQmlData *ddata = QQmlData::get(r->type, false); + if (ddata && ddata->propertyCache) + result = ddata->propertyCache->property(propertystring); + else + result = QQmlPropertyCache::property(r->engine->engine(), r->type, + propertystring, local); + } + + if (!result) + return v8::Handle<v8::Value>(); + + if (r->objectType == QV8ValueTypeResource::Reference) { + QV8ValueTypeReferenceResource *reference = static_cast<QV8ValueTypeReferenceResource *>(r); + + if (!reference->object) + return v8::Handle<v8::Value>(); + + r->type->read(reference->object, reference->property); + } else { + Q_ASSERT(r->objectType == QV8ValueTypeResource::Copy); + + QV8ValueTypeCopyResource *copy = static_cast<QV8ValueTypeCopyResource *>(r); + + r->type->setValue(copy->value); + } + +#define VALUE_TYPE_LOAD(metatype, cpptype, constructor) \ + if (result->propType == metatype) { \ + cpptype v; \ + void *args[] = { &v, 0 }; \ + r->type->qt_metacall(QMetaObject::ReadProperty, result->coreIndex, args); \ + return constructor(v); \ + } + + // These four types are the most common used by the value type wrappers + VALUE_TYPE_LOAD(QMetaType::QReal, qreal, v8::Number::New); + VALUE_TYPE_LOAD(QMetaType::Int, int, v8::Integer::New); + VALUE_TYPE_LOAD(QMetaType::QString, QString, r->engine->toString); + VALUE_TYPE_LOAD(QMetaType::Bool, bool, v8::Boolean::New); + + QVariant v(result->propType, (void *)0); + void *args[] = { v.data(), 0 }; + r->type->qt_metacall(QMetaObject::ReadProperty, result->coreIndex, args); + return r->engine->fromVariant(v); +#undef VALUE_TYPE_ACCESSOR +} + +v8::Handle<v8::Value> QV8ValueTypeWrapper::Setter(v8::Local<v8::String> property, + v8::Local<v8::Value> value, + const v8::AccessorInfo &info) +{ + QV8ValueTypeResource *r = v8_resource_cast<QV8ValueTypeResource>(info.This()); + if (!r) return value; + + QByteArray propName = r->engine->toString(property).toUtf8(); + int index = r->type->metaObject()->indexOfProperty(propName.constData()); + if (index == -1) + return value; + + if (r->objectType == QV8ValueTypeResource::Reference) { + QV8ValueTypeReferenceResource *reference = static_cast<QV8ValueTypeReferenceResource *>(r); + + if (!reference->object || + !reference->object->metaObject()->property(reference->property).isWritable()) + return value; + + r->type->read(reference->object, reference->property); + QMetaProperty p = r->type->metaObject()->property(index); + + QQmlBinding *newBinding = 0; + + if (value->IsFunction()) { + QQmlContextData *context = r->engine->callingContext(); + v8::Handle<v8::Function> function = v8::Handle<v8::Function>::Cast(value); + + QQmlPropertyData cacheData; + cacheData.setFlags(QQmlPropertyData::IsWritable | + QQmlPropertyData::IsValueTypeVirtual); + cacheData.propType = reference->object->metaObject()->property(reference->property).userType(); + cacheData.coreIndex = reference->property; + cacheData.valueTypeFlags = 0; + cacheData.valueTypeCoreIndex = index; + cacheData.valueTypePropType = p.userType(); + + v8::Local<v8::StackTrace> trace = + v8::StackTrace::CurrentStackTrace(1, + (v8::StackTrace::StackTraceOptions)(v8::StackTrace::kLineNumber | + v8::StackTrace::kScriptName)); + v8::Local<v8::StackFrame> frame = trace->GetFrame(0); + int lineNumber = frame->GetLineNumber(); + int columnNumber = frame->GetColumn(); + QString url = r->engine->toString(frame->GetScriptName()); + + newBinding = new QQmlBinding(&function, reference->object, context); + newBinding->setSourceLocation(url, lineNumber, columnNumber); + newBinding->setTarget(reference->object, cacheData, context); + newBinding->setEvaluateFlags(newBinding->evaluateFlags() | + QQmlBinding::RequiresThisObject); + } + + QQmlAbstractBinding *oldBinding = + QQmlPropertyPrivate::setBinding(reference->object, reference->property, index, newBinding); + if (oldBinding) + oldBinding->destroy(); + + if (!value->IsFunction()) { + QVariant v = r->engine->toVariant(value, -1); + + if (p.isEnumType() && (QMetaType::Type)v.type() == QMetaType::Double) + v = v.toInt(); + + p.write(reference->type, v); + + reference->type->write(reference->object, reference->property, 0); + } + + } else { + Q_ASSERT(r->objectType == QV8ValueTypeResource::Copy); + + QV8ValueTypeCopyResource *copy = static_cast<QV8ValueTypeCopyResource *>(r); + + QVariant v = r->engine->toVariant(value, -1); + + r->type->setValue(copy->value); + QMetaProperty p = r->type->metaObject()->property(index); + p.write(r->type, v); + copy->value = r->type->value(); + } + + return value; +} + +QT_END_NAMESPACE diff --git a/src/qml/qml/v8/qv8valuetypewrapper_p.h b/src/qml/qml/v8/qv8valuetypewrapper_p.h new file mode 100644 index 0000000000..b80d3cbbba --- /dev/null +++ b/src/qml/qml/v8/qv8valuetypewrapper_p.h @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV8VALUETYPEWRAPPER_P_H +#define QV8VALUETYPEWRAPPER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qglobal.h> +#include <QtQml/qqmllist.h> +#include <private/qv8_p.h> +#include <private/qhashedstring_p.h> + +QT_BEGIN_NAMESPACE + +class QV8Engine; +class QV8ObjectResource; +class QQmlValueType; +class QV8ValueTypeWrapper +{ +public: + QV8ValueTypeWrapper(); + ~QV8ValueTypeWrapper(); + + void init(QV8Engine *); + void destroy(); + + v8::Local<v8::Object> newValueType(QObject *, int, QQmlValueType *); + v8::Local<v8::Object> newValueType(const QVariant &, QQmlValueType *); + + QVariant toVariant(v8::Handle<v8::Object>); + QVariant toVariant(QV8ObjectResource *); + + static bool isEqual(QV8ObjectResource *, const QVariant& value); + +private: + static v8::Handle<v8::Value> ToStringGetter(v8::Local<v8::String> property, + const v8::AccessorInfo &info); + static v8::Handle<v8::Value> ToString(const v8::Arguments &args); + static v8::Handle<v8::Value> Getter(v8::Local<v8::String> property, + const v8::AccessorInfo &info); + static v8::Handle<v8::Value> Setter(v8::Local<v8::String> property, + v8::Local<v8::Value> value, + const v8::AccessorInfo &info); + + QV8Engine *m_engine; + v8::Persistent<v8::Function> m_constructor; + v8::Persistent<v8::Function> m_toString; + v8::Persistent<v8::String> m_toStringSymbol; + QHashedV8String m_toStringString; +}; + +QT_END_NAMESPACE + +#endif // QV8VALUETYPEWRAPPER_P_H + + diff --git a/src/qml/qml/v8/qv8variantresource_p.h b/src/qml/qml/v8/qv8variantresource_p.h new file mode 100644 index 0000000000..0b6328cb54 --- /dev/null +++ b/src/qml/qml/v8/qv8variantresource_p.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV8VARIANTRESOURCE_P_H +#define QV8VARIANTRESOURCE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qglobal.h> +#include <private/qv8_p.h> +#include <private/qv8engine_p.h> +#include <private/qqmlengine_p.h> + +QT_BEGIN_NAMESPACE + +class QV8VariantResource : public QV8ObjectResource, + public QQmlEnginePrivate::ScarceResourceData +{ + V8_RESOURCE_TYPE(VariantType) + +public: + QV8VariantResource(QV8Engine *engine, const QVariant &data); + + void addVmePropertyReference(); + void removeVmePropertyReference(); + + bool m_isScarceResource; + int m_vmePropertyReferenceCount; +}; + +QT_END_NAMESPACE + +#endif // QV8VARIANTRESOURCE_P_H + diff --git a/src/qml/qml/v8/qv8variantwrapper.cpp b/src/qml/qml/v8/qv8variantwrapper.cpp new file mode 100644 index 0000000000..4b1fc643f6 --- /dev/null +++ b/src/qml/qml/v8/qv8variantwrapper.cpp @@ -0,0 +1,279 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv8variantwrapper_p.h" +#include "qv8variantresource_p.h" +#include "qv8engine_p.h" +#include <private/qqmlengine_p.h> + +QT_BEGIN_NAMESPACE + +QV8VariantResource::QV8VariantResource(QV8Engine *engine, const QVariant &data) +: QV8ObjectResource(engine), QQmlEnginePrivate::ScarceResourceData(data), m_isScarceResource(false), m_vmePropertyReferenceCount(0) +{ +} + +void QV8VariantResource::addVmePropertyReference() +{ + if (m_isScarceResource && ++m_vmePropertyReferenceCount == 1) { + // remove from the ep->scarceResources list + // since it is now no longer eligible to be + // released automatically by the engine. + node.remove(); + } +} + +void QV8VariantResource::removeVmePropertyReference() +{ + if (m_isScarceResource && --m_vmePropertyReferenceCount == 0) { + // and add to the ep->scarceResources list + // since it is now eligible to be released + // automatically by the engine. + QQmlEnginePrivate::get(engine->engine())->scarceResources.insert(this); + } +} + +QV8VariantWrapper::QV8VariantWrapper() +: m_engine(0) +{ +} + +QV8VariantWrapper::~QV8VariantWrapper() +{ +} + +void QV8VariantWrapper::init(QV8Engine *engine) +{ + m_engine = engine; + m_toString = qPersistentNew<v8::Function>(v8::FunctionTemplate::New(ToString)->GetFunction()); + m_valueOf = qPersistentNew<v8::Function>(v8::FunctionTemplate::New(ValueOf)->GetFunction()); + + { + v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New(); + ft->InstanceTemplate()->SetFallbackPropertyHandler(Getter, Setter); + ft->InstanceTemplate()->SetHasExternalResource(true); + ft->InstanceTemplate()->MarkAsUseUserObjectComparison(); + 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<v8::Function>(ft->GetFunction()); + } + { + m_preserve = qPersistentNew<v8::Function>(v8::FunctionTemplate::New(Preserve)->GetFunction()); + m_destroy = qPersistentNew<v8::Function>(v8::FunctionTemplate::New(Destroy)->GetFunction()); + v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New(); + ft->InstanceTemplate()->SetFallbackPropertyHandler(Getter, Setter); + ft->InstanceTemplate()->SetHasExternalResource(true); + ft->InstanceTemplate()->MarkAsUseUserObjectComparison(); + ft->InstanceTemplate()->SetAccessor(v8::String::New("preserve"), PreserveGetter, 0, + m_preserve, v8::DEFAULT, + v8::PropertyAttribute(v8::ReadOnly | v8::DontDelete)); + ft->InstanceTemplate()->SetAccessor(v8::String::New("destroy"), DestroyGetter, 0, + m_destroy, v8::DEFAULT, + v8::PropertyAttribute(v8::ReadOnly | v8::DontDelete)); + 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<v8::Function>(ft->GetFunction()); + } + +} + +void QV8VariantWrapper::destroy() +{ + qPersistentDispose(m_valueOf); + qPersistentDispose(m_toString); + qPersistentDispose(m_destroy); + qPersistentDispose(m_preserve); + qPersistentDispose(m_scarceConstructor); + qPersistentDispose(m_constructor); +} + +v8::Local<v8::Object> QV8VariantWrapper::newVariant(const QVariant &value) +{ + bool scarceResource = value.type() == QVariant::Pixmap || + value.type() == QVariant::Image; + + // XXX NewInstance() should be optimized + v8::Local<v8::Object> rv; + QV8VariantResource *r = new QV8VariantResource(m_engine, value); + + if (scarceResource) { + QQmlEnginePrivate *ep = QQmlEnginePrivate::get(m_engine->engine()); + Q_ASSERT(ep->scarceResourcesRefCount); + rv = m_scarceConstructor->NewInstance(); + r->m_isScarceResource = true; + ep->scarceResources.insert(r); + } else { + rv = m_constructor->NewInstance(); + } + + rv->SetExternalResource(r); + return rv; +} + +bool QV8VariantWrapper::isVariant(v8::Handle<v8::Value> value) +{ + return value->IsObject() && v8_resource_cast<QV8VariantResource>(value->ToObject()); +} + +QVariant QV8VariantWrapper::toVariant(v8::Handle<v8::Object> obj) +{ + QV8VariantResource *r = v8_resource_cast<QV8VariantResource>(obj); + return r?r->data:QVariant(); +} + +QVariant QV8VariantWrapper::toVariant(QV8ObjectResource *r) +{ + Q_ASSERT(r->resourceType() == QV8ObjectResource::VariantType); + return static_cast<QV8VariantResource *>(r)->data; +} + +QVariant &QV8VariantWrapper::variantValue(v8::Handle<v8::Value> value) +{ + Q_ASSERT(isVariant(value)); + QV8VariantResource *r = v8_resource_cast<QV8VariantResource>(value->ToObject()); + return static_cast<QV8VariantResource *>(r)->data; +} + +v8::Handle<v8::Value> QV8VariantWrapper::Getter(v8::Local<v8::String> /* property */, + const v8::AccessorInfo & /* info */) +{ + return v8::Handle<v8::Value>(); +} + +v8::Handle<v8::Value> QV8VariantWrapper::Setter(v8::Local<v8::String> /* property */, + v8::Local<v8::Value> value, + const v8::AccessorInfo & /* info */) +{ + return value; +} + +v8::Handle<v8::Value> QV8VariantWrapper::PreserveGetter(v8::Local<v8::String> property, + const v8::AccessorInfo &info) +{ + Q_UNUSED(property); + return info.Data(); +} + +v8::Handle<v8::Value> QV8VariantWrapper::DestroyGetter(v8::Local<v8::String> property, + const v8::AccessorInfo &info) +{ + Q_UNUSED(property); + return info.Data(); +} + +v8::Handle<v8::Value> QV8VariantWrapper::ToStringGetter(v8::Local<v8::String> property, + const v8::AccessorInfo &info) +{ + Q_UNUSED(property); + return info.Data(); +} + +v8::Handle<v8::Value> QV8VariantWrapper::ValueOfGetter(v8::Local<v8::String> property, + const v8::AccessorInfo &info) +{ + Q_UNUSED(property); + return info.Data(); +} + +v8::Handle<v8::Value> QV8VariantWrapper::Preserve(const v8::Arguments &args) +{ + QV8VariantResource *resource = v8_resource_cast<QV8VariantResource>(args.This()); + if (resource) { + resource->node.remove(); + } + return v8::Undefined(); +} + +v8::Handle<v8::Value> QV8VariantWrapper::Destroy(const v8::Arguments &args) +{ + QV8VariantResource *resource = v8_resource_cast<QV8VariantResource>(args.This()); + if (resource) { + resource->data = QVariant(); + resource->node.remove(); + } + return v8::Undefined(); +} + +v8::Handle<v8::Value> QV8VariantWrapper::ToString(const v8::Arguments &args) +{ + QV8VariantResource *resource = v8_resource_cast<QV8VariantResource>(args.This()); + if (resource) { + QString result = resource->data.toString(); + if (result.isEmpty() && !resource->data.canConvert(QVariant::String)) + result = QString::fromLatin1("QVariant(%0)").arg(QString::fromLatin1(resource->data.typeName())); + return resource->engine->toString(result); + } else { + return v8::Undefined(); + } +} + +v8::Handle<v8::Value> QV8VariantWrapper::ValueOf(const v8::Arguments &args) +{ + QV8VariantResource *resource = v8_resource_cast<QV8VariantResource>(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/qml/qml/v8/qv8variantwrapper_p.h b/src/qml/qml/v8/qv8variantwrapper_p.h new file mode 100644 index 0000000000..877155c8ca --- /dev/null +++ b/src/qml/qml/v8/qv8variantwrapper_p.h @@ -0,0 +1,110 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV8VARIANTWRAPPER_P_H +#define QV8VARIANTWRAPPER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qglobal.h> +#include <QtQml/qqmllist.h> +#include <private/qv8_p.h> + +QT_BEGIN_NAMESPACE + +class QV8Engine; +class QV8ObjectResource; +class QV8VariantWrapper +{ +public: + QV8VariantWrapper(); + ~QV8VariantWrapper(); + + void init(QV8Engine *); + void destroy(); + + v8::Local<v8::Object> newVariant(const QVariant &); + bool isVariant(v8::Handle<v8::Value>); + static QVariant toVariant(v8::Handle<v8::Object>); + static QVariant toVariant(QV8ObjectResource *); + QVariant &variantValue(v8::Handle<v8::Value>); + +private: + static v8::Handle<v8::Value> Getter(v8::Local<v8::String> property, + const v8::AccessorInfo &info); + static v8::Handle<v8::Value> Setter(v8::Local<v8::String> property, + v8::Local<v8::Value> value, + const v8::AccessorInfo &info); + static v8::Handle<v8::Value> PreserveGetter(v8::Local<v8::String> property, + const v8::AccessorInfo &info); + static v8::Handle<v8::Value> DestroyGetter(v8::Local<v8::String> property, + const v8::AccessorInfo &info); + static v8::Handle<v8::Value> ToStringGetter(v8::Local<v8::String> property, + const v8::AccessorInfo &info); + static v8::Handle<v8::Value> ValueOfGetter(v8::Local<v8::String> property, + const v8::AccessorInfo &info); + static v8::Handle<v8::Value> Preserve(const v8::Arguments &args); + static v8::Handle<v8::Value> Destroy(const v8::Arguments &args); + static v8::Handle<v8::Value> ToString(const v8::Arguments &args); + static v8::Handle<v8::Value> ValueOf(const v8::Arguments &args); + + QV8Engine *m_engine; + v8::Persistent<v8::Function> m_constructor; + v8::Persistent<v8::Function> m_scarceConstructor; + v8::Persistent<v8::Function> m_preserve; + v8::Persistent<v8::Function> m_destroy; + v8::Persistent<v8::Function> m_toString; + v8::Persistent<v8::Function> m_valueOf; +}; + +QT_END_NAMESPACE + +#endif // QV8VARIANTWRAPPER_P_H + diff --git a/src/qml/qml/v8/qv8worker.cpp b/src/qml/qml/v8/qv8worker.cpp new file mode 100644 index 0000000000..6ea527166c --- /dev/null +++ b/src/qml/qml/v8/qv8worker.cpp @@ -0,0 +1,392 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv8worker_p.h" + +#include <private/qquicklistmodel_p.h> +#include <private/qquicklistmodelworkeragent_p.h> + +QT_BEGIN_NAMESPACE + +// We allow the following JavaScript types to be passed between the main and +// the secondary thread: +// + undefined +// + null +// + Boolean +// + String +// + Function +// + Array +// + "Simple" Objects +// + Number +// + Date +// + RegExp +// <quint8 type><quint24 size><data> + +enum Type { + WorkerUndefined, + WorkerNull, + WorkerTrue, + WorkerFalse, + WorkerString, + WorkerFunction, + WorkerArray, + WorkerObject, + WorkerInt32, + WorkerUint32, + WorkerNumber, + WorkerDate, + WorkerRegexp, + WorkerListModel, + WorkerSequence +}; + +static inline quint32 valueheader(Type type, quint32 size = 0) +{ + return quint8(type) << 24 | (size & 0xFFFFFF); +} + +static inline Type headertype(quint32 header) +{ + return (Type)(header >> 24); +} + +static inline quint32 headersize(quint32 header) +{ + return header & 0xFFFFFF; +} + +static inline void push(QByteArray &data, quint32 value) +{ + data.append((const char *)&value, sizeof(quint32)); +} + +static inline void push(QByteArray &data, double value) +{ + data.append((const char *)&value, sizeof(double)); +} + +static inline void push(QByteArray &data, void *ptr) +{ + data.append((const char *)&ptr, sizeof(void *)); +} + +static inline void reserve(QByteArray &data, int extra) +{ + data.reserve(data.size() + extra); +} + +static inline quint32 popUint32(const char *&data) +{ + quint32 rv = *((quint32 *)data); + data += sizeof(quint32); + return rv; +} + +static inline double popDouble(const char *&data) +{ + double rv = *((double *)data); + data += sizeof(double); + return rv; +} + +static inline void *popPtr(const char *&data) +{ + void *rv = *((void **)data); + data += sizeof(void *); + return rv; +} + +// XXX TODO: Check that worker script is exception safe in the case of +// serialization/deserialization failures + +#define ALIGN(size) (((size) + 3) & ~3) +void QV8Worker::serialize(QByteArray &data, v8::Handle<v8::Value> v, QV8Engine *engine) +{ + if (v.IsEmpty()) { + } else if (v->IsUndefined()) { + push(data, valueheader(WorkerUndefined)); + } else if (v->IsNull()) { + push(data, valueheader(WorkerNull)); + } else if (v->IsTrue()) { + push(data, valueheader(WorkerTrue)); + } else if (v->IsFalse()) { + push(data, valueheader(WorkerFalse)); + } else if (v->IsString()) { + v8::Handle<v8::String> string = v->ToString(); + int length = string->Length() + 1; + if (length > 0xFFFFFF) { + push(data, valueheader(WorkerUndefined)); + return; + } + int utf16size = ALIGN(length * sizeof(uint16_t)); + + reserve(data, utf16size + sizeof(quint32)); + push(data, valueheader(WorkerString, length)); + + int offset = data.size(); + data.resize(data.size() + utf16size); + char *buffer = data.data() + offset; + + string->Write((uint16_t*)buffer); + } else if (v->IsFunction()) { + // XXX TODO: Implement passing function objects between the main and + // worker scripts + push(data, valueheader(WorkerUndefined)); + } else if (v->IsArray()) { + v8::Handle<v8::Array> array = v8::Handle<v8::Array>::Cast(v); + uint32_t length = array->Length(); + if (length > 0xFFFFFF) { + push(data, valueheader(WorkerUndefined)); + return; + } + reserve(data, sizeof(quint32) + length * sizeof(quint32)); + push(data, valueheader(WorkerArray, length)); + for (uint32_t ii = 0; ii < length; ++ii) + serialize(data, array->Get(ii), engine); + } else if (v->IsInt32()) { + reserve(data, 2 * sizeof(quint32)); + push(data, valueheader(WorkerInt32)); + push(data, (quint32)v->Int32Value()); + } else if (v->IsUint32()) { + reserve(data, 2 * sizeof(quint32)); + push(data, valueheader(WorkerUint32)); + push(data, v->Uint32Value()); + } else if (v->IsNumber()) { + reserve(data, sizeof(quint32) + sizeof(double)); + push(data, valueheader(WorkerNumber)); + push(data, v->NumberValue()); + } else if (v->IsDate()) { + reserve(data, sizeof(quint32) + sizeof(double)); + push(data, valueheader(WorkerDate)); + push(data, v8::Handle<v8::Date>::Cast(v)->NumberValue()); + } else if (v->IsRegExp()) { + v8::Handle<v8::RegExp> regexp = v8::Handle<v8::RegExp>::Cast(v); + quint32 flags = regexp->GetFlags(); + v8::Local<v8::String> source = regexp->GetSource(); + + int length = source->Length() + 1; + if (length > 0xFFFFFF) { + push(data, valueheader(WorkerUndefined)); + return; + } + int utf16size = ALIGN(length * sizeof(uint16_t)); + + reserve(data, sizeof(quint32) + utf16size); + push(data, valueheader(WorkerRegexp, flags)); + push(data, (quint32)length); + int offset = data.size(); + data.resize(data.size() + utf16size); + char *buffer = data.data() + offset; + + source->Write((uint16_t*)buffer); + } else if (v->IsObject() && !v->ToObject()->GetExternalResource()) { + v8::Handle<v8::Object> object = v->ToObject(); + v8::Local<v8::Array> properties = engine->getOwnPropertyNames(object); + quint32 length = properties->Length(); + if (length > 0xFFFFFF) { + push(data, valueheader(WorkerUndefined)); + return; + } + push(data, valueheader(WorkerObject, length)); + v8::TryCatch tc; + for (quint32 ii = 0; ii < length; ++ii) { + v8::Local<v8::String> str = properties->Get(ii)->ToString(); + serialize(data, str, engine); + + v8::Local<v8::Value> val = object->Get(str); + if (tc.HasCaught()) { + serialize(data, v8::Undefined(), engine); + tc.Reset(); + } else { + serialize(data, val, engine); + } + } + } else if (engine->isQObject(v)) { + // XXX TODO: Generalize passing objects between the main thread and worker scripts so + // that others can trivially plug in their elements. + QQuickListModel *lm = qobject_cast<QQuickListModel *>(engine->toQObject(v)); + if (lm && lm->agent()) { + QQuickListModelWorkerAgent *agent = lm->agent(); + agent->addref(); + push(data, valueheader(WorkerListModel)); + push(data, (void *)agent); + return; + } + // No other QObject's are allowed to be sent + push(data, valueheader(WorkerUndefined)); + } else { + // we can convert sequences, but not other types with external data. + if (v->IsObject()) { + v8::Handle<v8::Object> seqObj = v->ToObject(); + QV8ObjectResource *r = static_cast<QV8ObjectResource *>(seqObj->GetExternalResource()); + if (r->resourceType() == QV8ObjectResource::SequenceType) { + QVariant sequenceVariant = engine->sequenceWrapper()->toVariant(r); + if (!sequenceVariant.isNull()) { + // valid sequence. we generate a length (sequence length + 1 for the sequence type) + uint32_t seqLength = engine->sequenceWrapper()->sequenceLength(r); + uint32_t length = seqLength + 1; + if (length > 0xFFFFFF) { + push(data, valueheader(WorkerUndefined)); + return; + } + reserve(data, sizeof(quint32) + length * sizeof(quint32)); + push(data, valueheader(WorkerSequence, length)); + serialize(data, v8::Integer::New(sequenceVariant.userType()), engine); // sequence type + for (uint32_t ii = 0; ii < seqLength; ++ii) { + serialize(data, seqObj->Get(ii), engine); // sequence elements + } + + return; + } + } + } + + // not a sequence. + push(data, valueheader(WorkerUndefined)); + } +} + +v8::Handle<v8::Value> QV8Worker::deserialize(const char *&data, QV8Engine *engine) +{ + quint32 header = popUint32(data); + Type type = headertype(header); + + switch (type) { + case WorkerUndefined: + return v8::Undefined(); + case WorkerNull: + return v8::Null(); + case WorkerTrue: + return v8::True(); + case WorkerFalse: + return v8::False(); + case WorkerString: + { + quint32 size = headersize(header); + v8::Local<v8::String> string = v8::String::New((uint16_t*)data, size - 1); + data += ALIGN(size * sizeof(uint16_t)); + return string; + } + case WorkerFunction: + Q_ASSERT(!"Unreachable"); + break; + case WorkerArray: + { + quint32 size = headersize(header); + v8::Local<v8::Array> array = v8::Array::New(size); + for (quint32 ii = 0; ii < size; ++ii) { + array->Set(ii, deserialize(data, engine)); + } + return array; + } + case WorkerObject: + { + quint32 size = headersize(header); + v8::Local<v8::Object> o = v8::Object::New(); + for (quint32 ii = 0; ii < size; ++ii) { + v8::Handle<v8::Value> name = deserialize(data, engine); + v8::Handle<v8::Value> value = deserialize(data, engine); + o->Set(name, value); + } + return o; + } + case WorkerInt32: + return v8::Integer::New((qint32)popUint32(data)); + case WorkerUint32: + return v8::Integer::NewFromUnsigned(popUint32(data)); + case WorkerNumber: + return v8::Number::New(popDouble(data)); + case WorkerDate: + return v8::Date::New(popDouble(data)); + case WorkerRegexp: + { + quint32 flags = headersize(header); + quint32 length = popUint32(data); + v8::Local<v8::String> source = v8::String::New((uint16_t*)data, length - 1); + data += ALIGN(length * sizeof(uint16_t)); + return v8::RegExp::New(source, (v8::RegExp::Flags)flags); + } + case WorkerListModel: + { + void *ptr = popPtr(data); + QQuickListModelWorkerAgent *agent = (QQuickListModelWorkerAgent *)ptr; + v8::Handle<v8::Value> rv = engine->newQObject(agent); + if (rv->IsObject()) { + QQuickListModelWorkerAgent::VariantRef ref(agent); + QVariant var = qVariantFromValue(ref); + rv->ToObject()->SetHiddenValue(v8::String::New("qml::ref"), engine->fromVariant(var)); + } + agent->release(); + agent->setV8Engine(engine); + return rv; + } + case WorkerSequence: + { + bool succeeded = false; + quint32 length = headersize(header); + quint32 seqLength = length - 1; + int sequenceType = deserialize(data, engine)->Int32Value(); + v8::Local<v8::Array> array = v8::Array::New(seqLength); + for (quint32 ii = 0; ii < seqLength; ++ii) + array->Set(ii, deserialize(data, engine)); + QVariant seqVariant = engine->sequenceWrapper()->toVariant(array, sequenceType, &succeeded); + return engine->sequenceWrapper()->fromVariant(seqVariant, &succeeded); + } + } + Q_ASSERT(!"Unreachable"); + return v8::Undefined(); +} + +QByteArray QV8Worker::serialize(v8::Handle<v8::Value> value, QV8Engine *engine) +{ + QByteArray rv; + serialize(rv, value, engine); + return rv; +} + +v8::Handle<v8::Value> QV8Worker::deserialize(const QByteArray &data, QV8Engine *engine) +{ + const char *stream = data.constData(); + return deserialize(stream, engine); +} + +QT_END_NAMESPACE + diff --git a/src/qml/qml/v8/qv8worker_p.h b/src/qml/qml/v8/qv8worker_p.h new file mode 100644 index 0000000000..d398d21f60 --- /dev/null +++ b/src/qml/qml/v8/qv8worker_p.h @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV8WORKER_P_H +#define QV8WORKER_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" + +QT_BEGIN_NAMESPACE + +class QV8Worker { +public: + struct SavedData { + }; + + static QByteArray serialize(v8::Handle<v8::Value>, QV8Engine *); + static v8::Handle<v8::Value> deserialize(const QByteArray &, QV8Engine *); + +private: + static void serialize(QByteArray &, v8::Handle<v8::Value>, QV8Engine *); + static v8::Handle<v8::Value> deserialize(const char *&, QV8Engine *); +}; + +QT_END_NAMESPACE + +#endif // QV8WORKER_P_H diff --git a/src/qml/qml/v8/script.pri b/src/qml/qml/v8/script.pri new file mode 100644 index 0000000000..3439413f5e --- /dev/null +++ b/src/qml/qml/v8/script.pri @@ -0,0 +1,21 @@ +SOURCES += \ + $$PWD/qjsengine.cpp \ + $$PWD/qjsvalue.cpp \ + $$PWD/qjsvalueiterator.cpp \ + +HEADERS += \ + $$PWD/qjsengine.h \ + $$PWD/qjsengine_p.h \ + $$PWD/qjsvalue.h \ + $$PWD/qjsvalue_p.h \ + $$PWD/qjsvalueiterator.h \ + $$PWD/qjsvalue_impl_p.h \ + $$PWD/qjsconverter_p.h \ + $$PWD/qjsconverter_impl_p.h \ + $$PWD/qscriptisolate_p.h \ + $$PWD/qscriptshareddata_p.h \ + $$PWD/qscripttools_p.h \ + $$PWD/qscript_impl_p.h \ + $$PWD/qscriptoriginalglobalobject_p.h \ + $$PWD/qjsvalueiterator_p.h \ + $$PWD/qjsvalueiterator_impl_p.h diff --git a/src/qml/qml/v8/v8.pri b/src/qml/qml/v8/v8.pri new file mode 100644 index 0000000000..de492a8ce5 --- /dev/null +++ b/src/qml/qml/v8/v8.pri @@ -0,0 +1,45 @@ +INCLUDEPATH += $$PWD/../../../3rdparty/javascriptcore + +include(script.pri) + +HEADERS += \ + $$PWD/qv8_p.h \ + $$PWD/qv8debug_p.h \ + $$PWD/qv8profiler_p.h \ + $$PWD/qv8stringwrapper_p.h \ + $$PWD/qv8engine_p.h \ + $$PWD/qv8sequencewrapper_p.h \ + $$PWD/qv8sequencewrapper_p_p.h \ + $$PWD/qv8contextwrapper_p.h \ + $$PWD/qv8qobjectwrapper_p.h \ + $$PWD/qv8typewrapper_p.h \ + $$PWD/qv8listwrapper_p.h \ + $$PWD/qv8variantwrapper_p.h \ + $$PWD/qv8variantresource_p.h \ + $$PWD/qv8valuetypewrapper_p.h \ + $$PWD/qv8include_p.h \ + $$PWD/qv8worker_p.h \ + $$PWD/qv8bindings_p.h \ + $$PWD/../../../3rdparty/javascriptcore/DateMath.h \ + $$PWD/qv8engine_impl_p.h \ + $$PWD/qv8domerrors_p.h \ + $$PWD/qv8sqlerrors_p.h \ + $$PWD/qqmlbuiltinfunctions_p.h + +SOURCES += \ + $$PWD/qv8stringwrapper.cpp \ + $$PWD/qv8engine.cpp \ + $$PWD/qv8sequencewrapper.cpp \ + $$PWD/qv8contextwrapper.cpp \ + $$PWD/qv8qobjectwrapper.cpp \ + $$PWD/qv8typewrapper.cpp \ + $$PWD/qv8listwrapper.cpp \ + $$PWD/qv8variantwrapper.cpp \ + $$PWD/qv8valuetypewrapper.cpp \ + $$PWD/qv8include.cpp \ + $$PWD/qv8worker.cpp \ + $$PWD/qv8bindings.cpp \ + $$PWD/../../../3rdparty/javascriptcore/DateMath.cpp \ + $$PWD/qv8domerrors.cpp \ + $$PWD/qv8sqlerrors.cpp \ + $$PWD/qqmlbuiltinfunctions.cpp
\ No newline at end of file |