diff options
Diffstat (limited to 'src/declarative/qml/v8')
25 files changed, 5423 insertions, 77 deletions
diff --git a/src/declarative/qml/v8/qjsconverter_p.h b/src/declarative/qml/v8/qjsconverter_p.h new file mode 100644 index 0000000000..4aec472c7a --- /dev/null +++ b/src/declarative/qml/v8/qjsconverter_p.h @@ -0,0 +1,272 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtScript module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL-ONLY$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QJSCONVERTER_P_H +#define QJSCONVERTER_P_H + +#include "qjsvalue.h" +#include <QtCore/qglobal.h> +#include <QtCore/qnumeric.h> +#include <QtCore/qstring.h> +#include <QtCore/qvarlengtharray.h> +#include <QtCore/qregexp.h> +#include <QtCore/qdatetime.h> + +#include <private/qv8_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); + +/* + \internal + \class QJSConverter + QJSValue and QJSEngine helper class. This class's responsibility is to convert values + between JS values and Qt/C++ values. + + This is a nice way to inline these functions in both QJSValue and QJSEngine. +*/ +class QJSConverter { +public: + static quint32 toArrayIndex(const QString& string) + { + // FIXME this function should be exported by JSC C API. + bool ok; + quint32 idx = string.toUInt(&ok); + if (!ok || toString(idx) != string) + idx = 0xffffffff; + + return idx; + } + + static QString toString(v8::Handle<v8::String> jsString) + { + if (jsString.IsEmpty()) + return QString(); + QString qstr; + qstr.resize(jsString->Length()); + jsString->Write(reinterpret_cast<uint16_t*>(qstr.data())); + return qstr; + } + + static v8::Handle<v8::String> toString(const QString& string) + { + return v8::String::New(reinterpret_cast<const uint16_t*>(string.data()), string.size()); + } + + static QString toString(double value) + { + // FIXME this should be easier. The ideal fix is to create + // a new function in V8 API which could cover the functionality. + + if (qIsNaN(value)) + return QString::fromLatin1("NaN"); + if (qIsInf(value)) + return QString::fromLatin1(value < 0 ? "-Infinity" : "Infinity"); + if (!value) + return QString::fromLatin1("0"); + + QVarLengthArray<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()); + } + + enum { + PropertyAttributeMask = v8::ReadOnly | v8::DontDelete | v8::DontEnum, + }; + + // return a mask of v8::PropertyAttribute that may also contains QScriptValue::PropertyGetter or QScriptValue::PropertySetter + static uint toPropertyAttributes(const QFlags<QJSValue::PropertyFlag>& flags) + { + uint attr = 0; + if (flags.testFlag(QJSValue::ReadOnly)) + attr |= v8::ReadOnly; + if (flags.testFlag(QJSValue::Undeletable)) + attr |= v8::DontDelete; + if (flags.testFlag(QJSValue::SkipInEnumeration)) + attr |= v8::DontEnum; +// if (flags.testFlag(QScriptValue::PropertyGetter)) +// attr |= QScriptValue::PropertyGetter; +// if (flags.testFlag(QScriptValue::PropertySetter)) +// attr |= QScriptValue::PropertySetter; + return attr; + } + + // Converts a JS RegExp to a QRegExp. + // The conversion is not 100% exact since ECMA regexp and QRegExp + // have different semantics/flags, but we try to do our best. + static QRegExp toRegExp(v8::Handle<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. + static v8::Handle<v8::RegExp> 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. + static v8::Handle<v8::Array> toStringList(const QStringList &lst) + { + v8::Handle<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. + static QStringList 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. + static QDateTime toDateTime(v8::Handle<v8::Date> jsDate) + { + return QDateTime::fromMSecsSinceEpoch(jsDate->NumberValue()); + } + + // Converts a QDateTime to a JS Date. + static v8::Handle<v8::Value> toDateTime(const QDateTime &dt) + { + double date; + if (!dt.isValid()) + date = qSNaN(); + else + date = dt.toMSecsSinceEpoch(); + return v8::Date::New(date); + } +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/declarative/qml/v8/qjsengine.cpp b/src/declarative/qml/v8/qjsengine.cpp new file mode 100644 index 0000000000..e80fcb4e7e --- /dev/null +++ b/src/declarative/qml/v8/qjsengine.cpp @@ -0,0 +1,431 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtScript module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL-ONLY$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qjsengine.h" +#include "qjsvalue.h" +#include "qjsvalue_p.h" +#include "qscriptisolate_p.h" +#include "qscript_impl_p.h" +#include "qv8engine_p.h" + +#include <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>) + +QT_BEGIN_NAMESPACE + + +/*! + Constructs a QJSEngine object. + + The globalObject() is initialized to have properties as described in + \l{ECMA-262}, Section 15.1. +*/ +QJSEngine::QJSEngine() + : d(new QV8Engine(this)) +{ +} + +/*! + \internal +*/ +QJSEngine::QJSEngine(QJSEngine::ContextOwnership ownership) + : d(new QV8Engine(this, ownership)) +{ +} + +/*! + Constructs a QJSEngine object with the given \a parent. + + The globalObject() is initialized to have properties as described in + \l{ECMA-262}, Section 15.1. +*/ + +QJSEngine::QJSEngine(QObject *parent) + : QObject(parent) + , d(new QV8Engine(this)) +{ +} + +QJSEngine::QJSEngine(QObjectPrivate &dd, QObject *parent) + : QObject(dd, parent) + , d(new QV8Engine(this)) +{ +} + +/*! + Destroys this QJSEngine. +*/ +QJSEngine::~QJSEngine() +{ + delete d; +} + +/*! + Returns true if the last script evaluation resulted in an uncaught + exception; otherwise returns false. + + The exception state is cleared when evaluate() is called. + + \sa uncaughtException(), uncaughtExceptionLineNumber(), + uncaughtExceptionBacktrace() +*/ +bool QJSEngine::hasUncaughtException() const +{ + Q_D(const QJSEngine); + QScriptIsolate api(d); + return d->hasUncaughtException(); +} + +/*! + Returns the current uncaught exception, or an invalid QJSValue + if there is no uncaught exception. + + The exception value is typically an \c{Error} object; in that case, + you can call toString() on the return value to obtain an error + message. + + \sa hasUncaughtException(), uncaughtExceptionLineNumber(), + uncaughtExceptionBacktrace() +*/ +QJSValue QJSEngine::uncaughtException() const +{ + Q_D(const QJSEngine); + QScriptIsolate api(d); + return d->scriptValueFromInternal(d->uncaughtException()); +} + +/*! + Clears any uncaught exceptions in this engine. + + \sa hasUncaughtException() +*/ +void QJSEngine::clearExceptions() +{ + Q_D(QJSEngine); + QScriptIsolate api(d); + d->clearExceptions(); +} + + +/*! + Runs the garbage collector. + + The garbage collector will attempt to reclaim memory by locating and disposing of objects that are + no longer reachable in the script environment. + + Normally you don't need to call this function; the garbage collector will automatically be invoked + when the QJSEngine decides that it's wise to do so (i.e. when a certain number of new objects + have been created). However, you can call this function to explicitly request that garbage + collection should be performed as soon as possible. + + \sa reportAdditionalMemoryCost() +*/ +void QJSEngine::collectGarbage() +{ + Q_D(QJSEngine); + QScriptIsolate api(d); + d->collectGarbage(); +} + +/*! + Evaluates \a program, using \a lineNumber as the base line number, + and returns the result of the evaluation. + + The script code will be evaluated in the current context. + + The evaluation of \a program can cause an exception in the + engine; in this case the return value will be the exception + that was thrown (typically an \c{Error} object). You can call + hasUncaughtException() to determine if an exception occurred in + the last call to evaluate(). + + \a lineNumber is used to specify a starting line number for \a + program; line number information reported by the engine that pertain + to this evaluation (e.g. uncaughtExceptionLineNumber()) will be + based on this argument. For example, if \a program consists of two + lines of code, and the statement on the second line causes a script + exception, uncaughtExceptionLineNumber() would return the given \a + lineNumber plus one. When no starting line number is specified, line + numbers will be 1-based. + + \a fileName is used for error reporting. For example in error objects + the file name is accessible through the "fileName" property if it's + provided with this function. +*/ +QJSValue QJSEngine::evaluate(const QString& program, const QString& fileName, int lineNumber) +{ + Q_D(QJSEngine); + QScriptIsolate api(d, QScriptIsolate::NotNullEngine); + v8::HandleScope handleScope; + return QJSValuePrivate::get(d->evaluate(program, fileName, lineNumber)); +} + +QJSValue QJSEngine::nullValue() +{ + Q_D(QJSEngine); + QScriptIsolate api(d, QScriptIsolate::NotNullEngine); + v8::HandleScope handleScope; + return QJSValuePrivate::get(new QJSValuePrivate(d, v8::Null())); +} + +QJSValue QJSEngine::undefinedValue() +{ + Q_D(QJSEngine); + QScriptIsolate api(d, QScriptIsolate::NotNullEngine); + v8::HandleScope handleScope; + return QJSValuePrivate::get(new QJSValuePrivate(d, v8::Undefined())); +} + +QJSValue QJSEngine::newObject() +{ + Q_D(QJSEngine); + QScriptIsolate api(d, QScriptIsolate::NotNullEngine); + v8::HandleScope handleScope; + return QJSValuePrivate::get(new QJSValuePrivate(d, v8::Object::New())); +} + +QJSValue QJSEngine::newArray(uint length) +{ + Q_D(QJSEngine); + QScriptIsolate api(d, QScriptIsolate::NotNullEngine); + v8::HandleScope handleScope; + return QJSValuePrivate::get(d->newArray(length)); +} + +/*! + Creates a QtScript object that wraps the given QObject \a + object, using the given \a ownership. The given \a options control + various aspects of the interaction with the resulting script object. + + Signals and slots, properties and children of \a object are + available as properties of the created QJSValue. For more + information, see the \l{QtScript} documentation. + + If \a object is a null pointer, this function returns nullValue(). + + If a default prototype has been registered for the \a object's class + (or its superclass, recursively), the prototype of the new script + object will be set to be that default prototype. + + If the given \a object is deleted outside of QtScript's control, any + attempt to access the deleted QObject's members through the QtScript + wrapper object (either by script code or C++) will result in a + script exception. + + \sa QJSValue::toQObject(), reportAdditionalMemoryCost() +*/ +QJSValue QJSEngine::newQObject(QObject *object) +{ + Q_D(QJSEngine); + QScriptIsolate api(d, QScriptIsolate::NotNullEngine); + v8::HandleScope handleScope; + return d->scriptValueFromInternal(d->newQObject(object)); +} + +/*! + Creates a QtScript object holding the given variant \a value. + + If a default prototype has been registered with the meta type id of + \a value, then the prototype of the created object will be that + prototype; otherwise, the prototype will be the Object prototype + object. + + \sa setDefaultPrototype(), QJSValue::toVariant(), reportAdditionalMemoryCost() +*/ +QJSValue QJSEngine::newVariant(const QVariant &value) +{ + Q_D(QJSEngine); + QScriptIsolate api(d, QScriptIsolate::NotNullEngine); + v8::HandleScope handleScope; + return d->scriptValueFromInternal(d->newVariant(value)); +} + + +QJSValue QJSEngine::globalObject() const +{ + Q_D(const QJSEngine); + QScriptIsolate api(d, QScriptIsolate::NotNullEngine); + v8::HandleScope handleScope; + return d->scriptValueFromInternal(d->global()); +} + +QJSValue QJSEngine::toObject(const QJSValue& value) +{ + Q_D(QJSEngine); + QScriptIsolate api(d, QScriptIsolate::NotNullEngine); + v8::HandleScope handleScope; + return QJSValuePrivate::get(QJSValuePrivate::get(value)->toObject(d)); +} + +/*! + Creates a QtScript object of class Date from the given \a value. + + \sa QJSValue::toDateTime() +*/ +QJSValue QJSEngine::newDate(const QDateTime &dt) +{ + Q_D(QJSEngine); + QScriptIsolate api(d, QScriptIsolate::NotNullEngine); + v8::HandleScope handleScope; + return d->scriptValueFromInternal(v8::Handle<v8::Value>(QJSConverter::toDateTime(dt))); +} + +/*! + Creates a QtScript object of class Date with the given + \a value (the number of milliseconds since 01 January 1970, + UTC). +*/ +QJSValue QJSEngine::newDate(double date) +{ + Q_D(QJSEngine); + QScriptIsolate api(d, QScriptIsolate::NotNullEngine); + v8::HandleScope handleScope; + return d->scriptValueFromInternal(v8::Handle<v8::Value>(v8::Date::New(date))); +} + +/*! + Creates a QtScript object of class RegExp with the given + \a regexp. + + \sa QJSValue::toRegExp() +*/ +QJSValue QJSEngine::newRegExp(const QRegExp ®exp) +{ + Q_D(QJSEngine); + QScriptIsolate api(d, QScriptIsolate::NotNullEngine); + v8::HandleScope handleScope; + return QJSValuePrivate::get(d->newRegExp(regexp)); +} + +/*! + Creates a QtScript object of class RegExp with the given + \a pattern and \a flags. + + The legal flags are 'g' (global), 'i' (ignore case), and 'm' + (multiline). +*/ +QJSValue QJSEngine::newRegExp(const QString &pattern, const QString &flags) +{ + Q_D(QJSEngine); + QScriptIsolate api(d, QScriptIsolate::NotNullEngine); + v8::HandleScope handleScope; + return QJSValuePrivate::get(d->newRegExp(pattern, flags)); +} + +/*! + * \internal + * used by QJSEngine::toScriptValue + */ +QJSValue QJSEngine::create(int type, const void *ptr) +{ + Q_D(QJSEngine); + QScriptIsolate api(d, QScriptIsolate::NotNullEngine); + v8::HandleScope handleScope; + return d->scriptValueFromInternal(d->metaTypeToJS(type, ptr)); +} + +/*! + \internal + \since 4.5 + convert \a value to \a type, store the result in \a ptr +*/ +bool QJSEngine::convertV2(const QJSValue &value, int type, void *ptr) +{ + QJSValuePrivate *vp = QJSValuePrivate::get(value); + QV8Engine *engine = vp->engine(); + if (engine) { + QScriptIsolate api(engine, QScriptIsolate::NotNullEngine); + v8::HandleScope handleScope; + return engine->metaTypeFromJS(*vp, type, ptr); + } else { + switch (type) { + case QMetaType::Bool: + *reinterpret_cast<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; + } + } +} + + +QT_END_NAMESPACE + +#include "moc_qjsengine.cpp" diff --git a/src/declarative/qml/v8/qjsengine.h b/src/declarative/qml/v8/qjsengine.h new file mode 100644 index 0000000000..5109cefcf4 --- /dev/null +++ b/src/declarative/qml/v8/qjsengine.h @@ -0,0 +1,151 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtScript module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL-ONLY$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QJSENGINE_H +#define QJSENGINE_H + +#include <QtCore/qmetatype.h> + +#include <QtCore/qvariant.h> +#include <QtCore/qsharedpointer.h> +#include <QtCore/qobject.h> +#include <QtDeclarative/qjsvalue.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Script) + +class QDateTime; +class QV8Engine; + +class QRegExp; + +template <typename T> +inline T qjsvalue_cast(const QJSValue &); + +class Q_SCRIPT_EXPORT QJSEngine + : public QObject +{ + Q_OBJECT +public: + enum ContextOwnership { + AdoptCurrentContext, + CreateNewContext + }; + + QJSEngine(); + explicit QJSEngine(ContextOwnership ownership); + explicit QJSEngine(QObject *parent); + virtual ~QJSEngine(); + + QJSValue globalObject() const; + + QJSValue evaluate(const QString &program, const QString &fileName = QString(), int lineNumber = 1); + + bool hasUncaughtException() const; + QJSValue uncaughtException() const; + void clearExceptions(); + + QJSValue nullValue(); + QJSValue undefinedValue(); + + QJSValue newVariant(const QVariant &value); + + QJSValue newRegExp(const QRegExp ®exp); + + QJSValue newObject(); + QJSValue newArray(uint length = 0); + QJSValue newRegExp(const QString &pattern, const QString &flags); + QJSValue newDate(double value); + QJSValue newDate(const QDateTime &value); + + QJSValue newQObject(QObject *object); + + template <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(); + + QJSValue toObject(const QJSValue &value); + + QV8Engine *handle() const { return d; } + +Q_SIGNALS: + void signalHandlerException(const QJSValue &exception); + +private: + QJSValue create(int type, const void *ptr); + + static bool convertV2(const QJSValue &value, int type, void *ptr); + + friend inline bool qjsvalue_cast_helper(const QJSValue &, int, void *); + +protected: + QJSEngine(QObjectPrivate &dd, QObject *parent = 0); + +private: + QV8Engine *d; + Q_DISABLE_COPY(QJSEngine) + friend class QV8Engine; +}; + +inline bool qjsvalue_cast_helper(const QJSValue &value, int type, void *ptr) +{ + return QJSEngine::convertV2(value, type, ptr); +} + +template<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/declarative/qml/v8/qjsvalue.cpp b/src/declarative/qml/v8/qjsvalue.cpp new file mode 100644 index 0000000000..eff7b4321a --- /dev/null +++ b/src/declarative/qml/v8/qjsvalue.cpp @@ -0,0 +1,1024 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtScript module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL-ONLY$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qscriptisolate_p.h" +#include "qjsengine.h" +#include "qv8engine_p.h" +#include "qjsvalue.h" +#include "qjsvalue_p.h" +#include "qscript_impl_p.h" +#include "qscriptshareddata_p.h" +#include <QtCore/qregexp.h> +#include <QtCore/qstring.h> + +QT_BEGIN_NAMESPACE + +/*! + Constructs an invalid value. +*/ +QJSValue::QJSValue() + : d_ptr(InvalidValue()) +{ +} + +/*! + Constructs a new QJSValue with a boolean \a value. +*/ +QJSValue::QJSValue(bool value) + : d_ptr(new QJSValuePrivate(value)) +{ +} + +/*! + \enum QJSValue::PropertyFlag + + This enum describes the attributes of a property. + + \value ReadOnly The property is read-only. Attempts by Qt Script code to write to the property will be ignored. + + \value Undeletable Attempts by Qt Script code to \c{delete} the property will be ignored. + + \value SkipInEnumeration The property is not to be enumerated by a \c{for-in} enumeration. + + \value PropertyGetter The property is defined by a function which will be called to get the property value. + + \value PropertySetter The property is defined by a function which will be called to set the property value. + + \omitvalue QObjectMember This flag is used to indicate that an existing property is a QObject member (a property or method). + + \value KeepExistingFlags This value is used to indicate to setProperty() that the property's flags should be left unchanged. If the property doesn't exist, the default flags (0) will be used. + + \omitvalue UserRange Flags in this range are not used by Qt Script, and can be used for custom purposes. +*/ + +/*! + Constructs a new QJSValue with a number \a value. +*/ +QJSValue::QJSValue(int value) + : d_ptr(new QJSValuePrivate(value)) +{ +} + +/*! + Constructs a new QJSValue with a number \a value. +*/ +QJSValue::QJSValue(uint value) + : d_ptr(new QJSValuePrivate(value)) +{ +} + +/*! + Constructs a new QJSValue with a number \a value. +*/ +QJSValue::QJSValue(double value) + : d_ptr(new QJSValuePrivate(value)) +{ +} + +/*! + Constructs a new QJSValue with a string \a value. +*/ +QJSValue::QJSValue(const QString& value) + : d_ptr(new QJSValuePrivate(value)) +{ +} + +/*! + Constructs a new QJSValue with a special \a value. +*/ +QJSValue::QJSValue(SpecialValue value) + : d_ptr(new QJSValuePrivate(value)) +{ +} + +/*! + Constructs a new QJSValue with a string \a value. +*/ +QJSValue::QJSValue(const QLatin1String &value) + : d_ptr(new QJSValuePrivate(value)) +{ +} + +/*! + \fn QJSValue::QJSValue(const QLatin1String &value) + + Constructs a new QJSValue with a string \a value. +*/ +#ifndef QT_NO_CAST_FROM_ASCII +QJSValue::QJSValue(const char *value) + : d_ptr(new QJSValuePrivate(QString::fromAscii(value))) +{ +} +#endif + +/*! + Block automatic convertion to bool + \internal +*/ +QJSValue::QJSValue(void* d) +{ + Q_UNUSED(d); + Q_ASSERT(false); +} + +/*! + Constructs a new QJSValue from private + \internal +*/ +QJSValue::QJSValue(QJSValuePrivate* d) + : d_ptr(d) +{ +} + +/*! + Constructs a new QJSValue from private + \internal +*/ +QJSValue::QJSValue(QScriptPassPointer<QJSValuePrivate> d) + : d_ptr(d.give()) +{ +} + +/*! + \obsolete + + Constructs a new QJSValue with the boolean \a value and + registers it with the script \a engine. +*/ +QJSValue::QJSValue(QJSEngine* engine, bool value) +{ + if (engine) { + QScriptIsolate api(QV8Engine::get(engine), QScriptIsolate::NotNullEngine); + d_ptr = new QJSValuePrivate(QV8Engine::get(engine), value); + } else { + d_ptr = new QJSValuePrivate(value); + } +} + +/*! + \obsolete + + Constructs a new QJSValue with the integer \a value and + registers it with the script \a engine. +*/ +QJSValue::QJSValue(QJSEngine* engine, int value) +{ + if (engine) { + QScriptIsolate api(QV8Engine::get(engine), QScriptIsolate::NotNullEngine); + d_ptr = new QJSValuePrivate(QV8Engine::get(engine), value); + } else { + d_ptr = new QJSValuePrivate(value); + } +} + +/*! + \obsolete + + Constructs a new QJSValue with the unsigned integer \a value and + registers it with the script \a engine. + */ +QJSValue::QJSValue(QJSEngine* engine, uint value) +{ + if (engine) { + QScriptIsolate api(QV8Engine::get(engine), QScriptIsolate::NotNullEngine); + d_ptr = new QJSValuePrivate(QV8Engine::get(engine), value); + } else { + d_ptr = new QJSValuePrivate(value); + } +} + +/*! + \obsolete + + Constructs a new QJSValue with the double \a value and + registers it with the script \a engine. +*/ +QJSValue::QJSValue(QJSEngine* engine, double value) +{ + if (engine) { + QScriptIsolate api(QV8Engine::get(engine), QScriptIsolate::NotNullEngine); + d_ptr = new QJSValuePrivate(QV8Engine::get(engine), value); + } else { + d_ptr = new QJSValuePrivate(value); + } +} + +/*! + \obsolete + + Constructs a new QJSValue with the string \a value and + registers it with the script \a engine. +*/ +QJSValue::QJSValue(QJSEngine* engine, const QString& value) +{ + if (engine) { + QScriptIsolate api(QV8Engine::get(engine), QScriptIsolate::NotNullEngine); + d_ptr = new QJSValuePrivate(QV8Engine::get(engine), value); + } else { + d_ptr = new QJSValuePrivate(value); + } +} + +/*! + \obsolete + + Constructs a new QJSValue with the string \a value and + registers it with the script \a engine. +*/ +QJSValue::QJSValue(QJSEngine* engine, const char* value) +{ + if (engine) { + QScriptIsolate api(QV8Engine::get(engine), QScriptIsolate::NotNullEngine); + d_ptr = new QJSValuePrivate(QV8Engine::get(engine), QString::fromUtf8(value)); + } else { + d_ptr = new QJSValuePrivate(QString::fromUtf8(value)); + } +} + +/*! + \obsolete + + Constructs a new QJSValue with the special \a value and + registers it with the script \a engine. +*/ +QJSValue::QJSValue(QJSEngine* engine, SpecialValue value) +{ + if (engine) { + QScriptIsolate api(QV8Engine::get(engine), QScriptIsolate::NotNullEngine); + d_ptr = new QJSValuePrivate(QV8Engine::get(engine), value); + } else { + d_ptr = new QJSValuePrivate(value); + } +} + +/*! + Constructs a new QJSValue that is a copy of \a other. + + Note that if \a other is an object (i.e., isObject() would return + true), then only a reference to the underlying object is copied into + the new script value (i.e., the object itself is not copied). +*/ +QJSValue::QJSValue(const QJSValue& other) + : d_ptr(other.d_ptr) +{ +} + +/*! + Destroys this QJSValue. +*/ +QJSValue::~QJSValue() +{ +} + +/*! + Returns true if this QJSValue is valid; otherwise returns + false. +*/ +bool QJSValue::isValid() const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return d->isValid(); +} + +/*! + Returns true if this QJSValue is of the primitive type Boolean; + otherwise returns false. + + \sa toBool() +*/ +bool QJSValue::isBool() const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return d->isBool(); +} + +/*! + \obsolete + + Use isBool() instead. + Returns true if this QJSValue is of the primitive type Boolean; + otherwise returns false. +*/ +bool QJSValue::isBoolean() const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return d->isBool(); +} + +/*! + Returns true if this QJSValue is of the primitive type Number; + otherwise returns false. + + \sa toNumber() +*/ +bool QJSValue::isNumber() const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return d->isNumber(); +} + +/*! + Returns true if this QJSValue is of the primitive type Null; + otherwise returns false. + + \sa QJSEngine::nullValue() +*/ +bool QJSValue::isNull() const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return d->isNull(); +} + +/*! + Returns true if this QJSValue is of the primitive type String; + otherwise returns false. + + \sa toString() +*/ +bool QJSValue::isString() const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return d->isString(); +} + +/*! + Returns true if this QJSValue is of the primitive type Undefined; + otherwise returns false. + + \sa QJSEngine::undefinedValue() +*/ +bool QJSValue::isUndefined() const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return d->isUndefined(); +} + +/*! + Returns true if this QJSValue is an object of the Error class; + otherwise returns false. + + \sa QScriptContext::throwError() +*/ +bool QJSValue::isError() const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return d->isError(); +} + +/*! + Returns true if this QJSValue is an object of the Array class; + otherwise returns false. + + \sa QJSEngine::newArray() +*/ +bool QJSValue::isArray() const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return d->isArray(); + } + +/*! + Returns true if this QJSValue is of the Object type; otherwise + returns false. + + Note that function values, variant values, and QObject values are + objects, so this function returns true for such values. + + \sa toObject(), QJSEngine::newObject() +*/ +bool QJSValue::isObject() const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return d->isObject(); +} + +/*! + Returns true if this QJSValue is a function; otherwise returns + false. + + \sa call() +*/ +bool QJSValue::isFunction() const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return d->isCallable(); +} + +/*! + Returns true if this QJSValue is a variant value; + otherwise returns false. + + \sa toVariant(), QJSEngine::newVariant() +*/ +bool QJSValue::isVariant() const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return d->isVariant(); +} + +/*! + Returns the string value of this QJSValue, as defined in + \l{ECMA-262} section 9.8, "ToString". + + Note that if this QJSValue is an object, calling this function + has side effects on the script engine, since the engine will call + the object's toString() function (and possibly valueOf()) in an + attempt to convert the object to a primitive value (possibly + resulting in an uncaught script exception). + + \sa isString() +*/ +QString QJSValue::toString() const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return d->toString(); +} + +/*! + Returns the number value of this QJSValue, as defined in + \l{ECMA-262} section 9.3, "ToNumber". + + Note that if this QJSValue is an object, calling this function + has side effects on the script engine, since the engine will call + the object's valueOf() function (and possibly toString()) in an + attempt to convert the object to a primitive value (possibly + resulting in an uncaught script exception). + + \sa isNumber(), toInteger(), toInt32(), toUInt32(), toUInt16() +*/ +double QJSValue::toNumber() const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return d->toNumber(); +} + +/*! + Returns the boolean value of this QJSValue, using the conversion + rules described in \l{ECMA-262} section 9.2, "ToBoolean". + + Note that if this QJSValue is an object, calling this function + has side effects on the script engine, since the engine will call + the object's valueOf() function (and possibly toString()) in an + attempt to convert the object to a primitive value (possibly + resulting in an uncaught script exception). + + \sa isBool() +*/ +bool QJSValue::toBool() const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return d->toBool(); +} + +/*! + \obsolete + + Use toBool() instead. +*/ +bool QJSValue::toBoolean() const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return d->toBool(); +} + +/*! + Returns the integer value of this QJSValue, using the conversion + rules described in \l{ECMA-262} section 9.4, "ToInteger". + + Note that if this QJSValue is an object, calling this function + has side effects on the script engine, since the engine will call + the object's valueOf() function (and possibly toString()) in an + attempt to convert the object to a primitive value (possibly + resulting in an uncaught script exception). + + \sa toNumber() +*/ +double QJSValue::toInteger() const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return d->toInteger(); +} + +/*! + Returns the signed 32-bit integer value of this QJSValue, using + the conversion rules described in \l{ECMA-262} section 9.5, "ToInt32". + + Note that if this QJSValue is an object, calling this function + has side effects on the script engine, since the engine will call + the object's valueOf() function (and possibly toString()) in an + attempt to convert the object to a primitive value (possibly + resulting in an uncaught script exception). + + \sa toNumber(), toUInt32() +*/ +qint32 QJSValue::toInt32() const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return d->toInt32(); +} + +/*! + Returns the unsigned 32-bit integer value of this QJSValue, using + the conversion rules described in \l{ECMA-262} section 9.6, "ToUint32". + + Note that if this QJSValue is an object, calling this function + has side effects on the script engine, since the engine will call + the object's valueOf() function (and possibly toString()) in an + attempt to convert the object to a primitive value (possibly + resulting in an uncaught script exception). + + \sa toNumber(), toInt32() +*/ +quint32 QJSValue::toUInt32() const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return d->toUInt32(); +} + +/*! + Returns the unsigned 16-bit integer value of this QJSValue, using + the conversion rules described in \l{ECMA-262} section 9.7, "ToUint16". + + Note that if this QJSValue is an object, calling this function + has side effects on the script engine, since the engine will call + the object's valueOf() function (and possibly toString()) in an + attempt to convert the object to a primitive value (possibly + resulting in an uncaught script exception). + + \sa toNumber() +*/ +quint16 QJSValue::toUInt16() const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return d->toUInt16(); +} + +/*! + \obsolete + + This function is obsolete; use QJSEngine::toObject() instead. +*/ +QJSValue QJSValue::toObject() const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return QJSValuePrivate::get(d->toObject()); +} + +/*! + Returns the QVariant value of this QJSValue, if it can be + converted to a QVariant; otherwise returns an invalid QVariant. + The conversion is performed according to the following table: + + \table + \header \o Input Type \o Result + \row \o Undefined \o An invalid QVariant. + \row \o Null \o An invalid QVariant. + \row \o Boolean \o A QVariant containing the value of the boolean. + \row \o Number \o A QVariant containing the value of the number. + \row \o String \o A QVariant containing the value of the string. + \row \o QVariant Object \o The result is the QVariant value of the object (no conversion). + \row \o QObject Object \o A QVariant containing a pointer to the QObject. + \row \o Date Object \o A QVariant containing the date value (toDateTime()). + \row \o RegExp Object \o A QVariant containing the regular expression value (toRegExp()). + \row \o Array Object \o The array is converted to a QVariantList. Each element is converted to a QVariant, recursively; cyclic references are not followed. + \row \o Object \o The object is converted to a QVariantMap. Each property is converted to a QVariant, recursively; cyclic references are not followed. + \endtable + + \sa isVariant() +*/ +QVariant QJSValue::toVariant() const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return d->toVariant(); +} + + +/*! + Calls this QJSValue as a function, using \a thisObject as + the `this' object in the function call, and passing \a args + as arguments to the function. Returns the value returned from + the function. + + If this QJSValue is not a function, call() does nothing + and returns an invalid QJSValue. + + Note that if \a thisObject is not an object, the global object + (see \l{QJSEngine::globalObject()}) will be used as the + `this' object. + + Calling call() can cause an exception to occur in the script engine; + in that case, call() returns the value that was thrown (typically an + \c{Error} object). You can call + QJSEngine::hasUncaughtException() to determine if an exception + occurred. + + \snippet doc/src/snippets/code/src_script_qscriptvalue.cpp 2 + + \sa construct() +*/ +QJSValue QJSValue::call(const QJSValue& thisObject, const QJSValueList& args) +{ + Q_D(QJSValue); + QScriptIsolate api(d->engine()); + return d->call(QJSValuePrivate::get(thisObject), args); +} + +/*! + Creates a new \c{Object} and calls this QJSValue as a + constructor, using the created object as the `this' object and + passing \a args as arguments. If the return value from the + constructor call is an object, then that object is returned; + otherwise the default constructed object is returned. + + If this QJSValue is not a function, construct() does nothing + and returns an invalid QJSValue. + + Calling construct() can cause an exception to occur in the script + engine; in that case, construct() returns the value that was thrown + (typically an \c{Error} object). You can call + QJSEngine::hasUncaughtException() to determine if an exception + occurred. + + \sa call(), QJSEngine::newObject() +*/ +QJSValue QJSValue::construct(const QJSValueList &args) +{ + Q_D(QJSValue); + QScriptIsolate api(d->engine()); + return QJSValuePrivate::get(d->construct(args)); +} + +/*! + Returns the QJSEngine that created this QJSValue, + or 0 if this QJSValue is invalid or the value is not + associated with a particular engine. +*/ +QJSEngine* QJSValue::engine() const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + QV8Engine* engine = d->engine(); + if (engine) + return QV8Engine::get(engine); + return 0; +} + + +/*! + If this QJSValue is an object, returns the internal prototype + (\c{__proto__} property) of this object; otherwise returns an + invalid QJSValue. + + \sa setPrototype(), isObject() +*/ +QJSValue QJSValue::prototype() const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return QJSValuePrivate::get(d->prototype()); +} + +/*! + If this QJSValue is an object, sets the internal prototype + (\c{__proto__} property) of this object to be \a prototype; + otherwise does nothing. + + The internal prototype should not be confused with the public + property with name "prototype"; the public prototype is usually + only set on functions that act as constructors. + + \sa prototype(), isObject() +*/ +void QJSValue::setPrototype(const QJSValue& prototype) +{ + Q_D(QJSValue); + QScriptIsolate api(d->engine()); + d->setPrototype(QJSValuePrivate::get(prototype)); +} + +/*! + Assigns the \a other value to this QJSValue. + + Note that if \a other is an object (isObject() returns true), + only a reference to the underlying object will be assigned; + the object itself will not be copied. +*/ +QJSValue& QJSValue::operator=(const QJSValue& other) +{ + d_ptr = other.d_ptr; + return *this; +} + +/*! + Returns true if this QJSValue is equal to \a other, otherwise + returns false. The comparison follows the behavior described in + \l{ECMA-262} section 11.9.3, "The Abstract Equality Comparison + Algorithm". + + This function can return true even if the type of this QJSValue + is different from the type of the \a other value; i.e. the + comparison is not strict. For example, comparing the number 9 to + the string "9" returns true; comparing an undefined value to a null + value returns true; comparing a \c{Number} object whose primitive + value is 6 to a \c{String} object whose primitive value is "6" + returns true; and comparing the number 1 to the boolean value + \c{true} returns true. If you want to perform a comparison + without such implicit value conversion, use strictlyEquals(). + + Note that if this QJSValue or the \a other value are objects, + calling this function has side effects on the script engine, since + the engine will call the object's valueOf() function (and possibly + toString()) in an attempt to convert the object to a primitive value + (possibly resulting in an uncaught script exception). + + \sa strictlyEquals(), lessThan() +*/ +bool QJSValue::equals(const QJSValue& other) const +{ + Q_D(const QJSValue); + QJSValuePrivate* otherValue = QJSValuePrivate::get(other); + QScriptIsolate api(d->engine() ? d->engine() : otherValue->engine()); + return d_ptr->equals(otherValue); +} + +/*! + Returns true if this QJSValue is equal to \a other using strict + comparison (no conversion), otherwise returns false. The comparison + follows the behavior described in \l{ECMA-262} section 11.9.6, "The + Strict Equality Comparison Algorithm". + + If the type of this QJSValue is different from the type of the + \a other value, this function returns false. If the types are equal, + the result depends on the type, as shown in the following table: + + \table + \header \o Type \o Result + \row \o Undefined \o true + \row \o Null \o true + \row \o Boolean \o true if both values are true, false otherwise + \row \o Number \o false if either value is NaN (Not-a-Number); true if values are equal, false otherwise + \row \o String \o true if both values are exactly the same sequence of characters, false otherwise + \row \o Object \o true if both values refer to the same object, false otherwise + \endtable + + \sa equals() +*/ +bool QJSValue::strictlyEquals(const QJSValue& other) const +{ + Q_D(const QJSValue); + QJSValuePrivate* o = QJSValuePrivate::get(other); + QScriptIsolate api(d->engine() ? d->engine() : o->engine()); + return d_ptr->strictlyEquals(o); +} + +/*! + Returns true if this QJSValue is an instance of + \a other; otherwise returns false. + + This QJSValue is considered to be an instance of \a other if + \a other is a function and the value of the \c{prototype} + property of \a other is in the prototype chain of this + QJSValue. +*/ +bool QJSValue::instanceOf(const QJSValue &other) const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return d->instanceOf(QJSValuePrivate::get(other)); +} + +/*! + Returns the value of this QJSValue's property with the given \a name, + using the given \a mode to resolve the property. + + If no such property exists, an invalid QJSValue is returned. + + If the property is implemented using a getter function (i.e. has the + PropertyGetter flag set), calling property() has side-effects on the + script engine, since the getter function will be called (possibly + resulting in an uncaught script exception). If an exception + occurred, property() returns the value that was thrown (typically + an \c{Error} object). + + \sa setProperty(), propertyFlags(), QJSValueIterator +*/ +QJSValue QJSValue::property(const QString& name) const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return QJSValuePrivate::get(d->property(name)); +} + +/*! + \overload + + Returns the property at the given \a arrayIndex, using the given \a + mode to resolve the property. + + This function is provided for convenience and performance when + working with array objects. + + If this QJSValue is not an Array object, this function behaves + as if property() was called with the string representation of \a + arrayIndex. +*/ +QJSValue QJSValue::property(quint32 arrayIndex) const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return QJSValuePrivate::get(d->property(arrayIndex)); +} + +/*! + Sets the value of this QJSValue's property with the given \a name to + the given \a value. + + If this QJSValue is not an object, this function does nothing. + + If this QJSValue does not already have a property with name \a name, + a new property is created; the given \a flags then specify how this + property may be accessed by script code. + + If \a value is invalid, the property is removed. + + If the property is implemented using a setter function (i.e. has the + PropertySetter flag set), calling setProperty() has side-effects on + the script engine, since the setter function will be called with the + given \a value as argument (possibly resulting in an uncaught script + exception). + + Note that you cannot specify custom getter or setter functions for + built-in properties, such as the \c{length} property of Array objects + or meta properties of QObject objects. + + \sa property() +*/ +void QJSValue::setProperty(const QString& name, const QJSValue& value) +{ + Q_D(QJSValue); + QScriptIsolate api(d->engine()); + d->setProperty(name, QJSValuePrivate::get(value)); +} + +/*! + \overload + + Sets the property at the given \a arrayIndex to the given \a value. + + This function is provided for convenience and performance when + working with array objects. + + If this QJSValue is not an Array object, this function behaves + as if setProperty() was called with the string representation of \a + arrayIndex. +*/ +void QJSValue::setProperty(quint32 arrayIndex, const QJSValue& value) +{ + Q_D(QJSValue); + QScriptIsolate api(d->engine()); + d->setProperty(arrayIndex, QJSValuePrivate::get(value)); +} + +/*! + Returns the flags of the property with the given \a name, using the + given \a mode to resolve the property. + + \sa property() +*/ +QJSValue::PropertyFlags QJSValue::propertyFlags(const QString& name) const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return d->propertyFlags(name); +} + +/*! + * If this QJSValue is a QObject, returns the QObject pointer + * that the QJSValue represents; otherwise, returns 0. + * + * If the QObject that this QJSValue wraps has been deleted, + * this function returns 0 (i.e. it is possible for toQObject() + * to return 0 even when isQObject() returns true). + * + * \sa isQObject() + */ +QObject *QJSValue::toQObject() const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return d->toQObject(); +} + +/*! + Returns a QDateTime representation of this value, in local time. + If this QJSValue is not a date, or the value of the date is NaN + (Not-a-Number), an invalid QDateTime is returned. + + \sa isDate() +*/ +QDateTime QJSValue::toDateTime() const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return d->toDataTime(); +} + +/*! + Returns the QRegExp representation of this value. + If this QJSValue is not a regular expression, an empty + QRegExp is returned. + + \sa isRegExp() +*/ +QRegExp QJSValue::toRegExp() const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return d->toRegExp(); +} + +/*! + Returns true if this QJSValue is an object of the Date class; + otherwise returns false. + + \sa QJSEngine::newDate() +*/ +bool QJSValue::isDate() const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return d->isDate(); +} + +/*! + Returns true if this QJSValue is an object of the RegExp class; + otherwise returns false. + + \sa QJSEngine::newRegExp() +*/ +bool QJSValue::isRegExp() const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return d->isRegExp(); +} + +/*! + Returns true if this QJSValue is a QObject; otherwise returns + false. + + Note: This function returns true even if the QObject that this + QJSValue wraps has been deleted. + + \sa toQObject(), QJSEngine::newQObject() +*/ +bool QJSValue::isQObject() const +{ + Q_D(const QJSValue); + QScriptIsolate api(d->engine()); + return d->isQObject(); +} + +QT_END_NAMESPACE diff --git a/src/declarative/qml/v8/qjsvalue.h b/src/declarative/qml/v8/qjsvalue.h new file mode 100644 index 0000000000..07276bc694 --- /dev/null +++ b/src/declarative/qml/v8/qjsvalue.h @@ -0,0 +1,165 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtScript module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL-ONLY$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QJSVALUE_H +#define QJSVALUE_H + +#include <QtCore/qstring.h> + +#include <QtCore/qlist.h> +#include <QtCore/qsharedpointer.h> +#include <QtCore/qshareddata.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Script) + +class QJSValue; +class QJSEngine; +class QVariant; +class QObject; +struct QMetaObject; +class QDateTime; +class QRegExp; + +typedef QList<QJSValue> QJSValueList; + +class QJSValuePrivate; +struct QScriptValuePrivatePointerDeleter; +template <class T> class QScriptPassPointer; + +class Q_SCRIPT_EXPORT QJSValue +{ +public: + enum PropertyFlag { + ReadOnly = 0x00000001, + Undeletable = 0x00000002, + SkipInEnumeration = 0x00000004 + }; + Q_DECLARE_FLAGS(PropertyFlags, PropertyFlag) + + enum SpecialValue { + NullValue, + UndefinedValue + }; + +public: + QJSValue(); + ~QJSValue(); + QJSValue(const QJSValue &other); + QJSValue(QJSEngine *engine, SpecialValue val); + QJSValue(QJSEngine *engine, bool val); + QJSValue(QJSEngine *engine, int val); + QJSValue(QJSEngine *engine, uint val); + QJSValue(QJSEngine *engine, double val); + QJSValue(QJSEngine *engine, const QString &val); + + QJSValue(SpecialValue value); + QJSValue(bool value); + QJSValue(int value); + QJSValue(uint value); + QJSValue(double value); + QJSValue(const QString &value); + QJSValue(const QLatin1String &value); +#ifndef QT_NO_CAST_FROM_ASCII + QT_ASCII_CAST_WARN_CONSTRUCTOR QJSValue(const char *str); +#endif + + QJSValue &operator=(const QJSValue &other); + + QJSEngine *engine() const; + bool isValid() const; + bool isBool() const; + bool isBoolean() const; + bool isNumber() const; + bool isFunction() const; + bool isNull() const; + bool isString() const; + bool isUndefined() const; + bool isVariant() const; + bool isQObject() const; + bool isObject() const; + bool isDate() const; + bool isRegExp() const; + bool isArray() const; + bool isError() const; + + QString toString() const; + double toNumber() const; + bool toBool() const; + bool toBoolean() const; + double toInteger() const; + qint32 toInt32() const; + quint32 toUInt32() const; + quint16 toUInt16() const; + QVariant toVariant() const; + QObject *toQObject() const; + QJSValue toObject() const; + QDateTime toDateTime() const; + QRegExp toRegExp() const; + + bool instanceOf(const QJSValue &other) const; + + bool equals(const QJSValue &other) const; + bool strictlyEquals(const QJSValue &other) const; + + QJSValue prototype() const; + void setPrototype(const QJSValue &prototype); + + QJSValue property(const QString &name) const; + void setProperty(const QString &name, const QJSValue &value); + + QJSValue property(quint32 arrayIndex) const; + void setProperty(quint32 arrayIndex, const QJSValue &value); + + QJSValue::PropertyFlags propertyFlags(const QString &name) const; + + QJSValue call(const QJSValue &thisObject = QJSValue(), + const QJSValueList &args = QJSValueList()); + QJSValue construct(const QJSValueList &args = QJSValueList()); + +private: + // force compile error, prevent QJSValue(bool) to be called + QJSValue(void *); + // force compile error, prevent QJSValue(QScriptEngine*, bool) to be called + QJSValue(QJSEngine *, void *); + QJSValue(QJSEngine *, const char *); + + QJSValue(QJSValuePrivate*); + QJSValue(QScriptPassPointer<QJSValuePrivate>); + +private: + QExplicitlySharedDataPointer<QJSValuePrivate> d_ptr; + + Q_DECLARE_PRIVATE(QJSValue) +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QJSValue::PropertyFlags) + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/declarative/qml/v8/qjsvalue_impl_p.h b/src/declarative/qml/v8/qjsvalue_impl_p.h new file mode 100644 index 0000000000..adff6ce945 --- /dev/null +++ b/src/declarative/qml/v8/qjsvalue_impl_p.h @@ -0,0 +1,1133 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtScript module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL-ONLY$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QJSVALUE_IMPL_P_H +#define QJSVALUE_IMPL_P_H + +#include "qjsconverter_p.h" +#include "qjsvalue_p.h" +#include "qv8engine_p.h" +#include "qscriptisolate_p.h" + +QT_BEGIN_NAMESPACE + + +QJSValuePrivate* QJSValuePrivate::get(const QJSValue& q) { Q_ASSERT(q.d_ptr.data()); return q.d_ptr.data(); } + +QJSValue QJSValuePrivate::get(const QJSValuePrivate* d) +{ + Q_ASSERT(d); + return QJSValue(const_cast<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() + : m_engine(0), m_state(Invalid) +{ +} + +QJSValuePrivate::QJSValuePrivate(bool value) + : m_engine(0), m_state(CBool), u(value) +{ +} + +QJSValuePrivate::QJSValuePrivate(int value) + : m_engine(0), m_state(CNumber), u(value) +{ +} + +QJSValuePrivate::QJSValuePrivate(uint value) + : m_engine(0), m_state(CNumber), u(value) +{ +} + +QJSValuePrivate::QJSValuePrivate(double value) + : m_engine(0), m_state(CNumber), u(value) +{ +} + +QJSValuePrivate::QJSValuePrivate(const QString& value) + : m_engine(0), m_state(CString), u(new QString(value)) +{ +} + +QJSValuePrivate::QJSValuePrivate(QJSValue::SpecialValue value) + : m_engine(0), m_state(value == QJSValue::NullValue ? CNull : CUndefined) +{ +} + +QJSValuePrivate::QJSValuePrivate(QV8Engine* engine, bool value) + : m_engine(engine), m_state(JSValue) +{ + Q_ASSERT(engine); + v8::HandleScope handleScope; + m_value = v8::Persistent<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 Invalid: + case CNull: + case CUndefined: + return false; + case CString: + return u.m_string->length(); + } + + Q_ASSERT_X(false, "toBool()", "Not all states are included in the previous switch statement."); + return false; // Avoid compiler warning. +} + +double QJSValuePrivate::toNumber() const +{ + switch (m_state) { + case JSValue: + { + v8::HandleScope scope; + return m_value->ToNumber()->Value(); + } + case CNumber: + return u.m_number; + case CBool: + return u.m_bool ? 1 : 0; + case CNull: + case Invalid: + return 0; + case CUndefined: + return qQNaN(); + case CString: + bool ok; + double result = u.m_string->toDouble(&ok); + if (ok) + return result; + result = u.m_string->toInt(&ok, 0); // Try other bases. + if (ok) + return result; + if (*u.m_string == QLatin1String("Infinity")) + return qInf(); + if (*u.m_string == QLatin1String("-Infinity")) + return -qInf(); + return u.m_string->length() ? qQNaN() : 0; + } + + Q_ASSERT_X(false, "toNumber()", "Not all states are included in the previous switch statement."); + return 0; // Avoid compiler warning. +} + +QScriptPassPointer<QJSValuePrivate> QJSValuePrivate::toObject(QV8Engine* engine) const +{ + Q_ASSERT(engine); + if (this->engine() && engine != this->engine()) { + qWarning("QJSEngine::toObject: cannot convert value created in a different engine"); + return InvalidValue(); + } + + v8::HandleScope scope; + switch (m_state) { + case Invalid: + case CNull: + case CUndefined: + return new QJSValuePrivate; + case CString: + return new QJSValuePrivate(engine, engine->makeJSValue(*u.m_string)->ToObject()); + case CNumber: + return new QJSValuePrivate(engine, engine->makeJSValue(u.m_number)->ToObject()); + case CBool: + return new QJSValuePrivate(engine, engine->makeJSValue(u.m_bool)->ToObject()); + case JSValue: + if (m_value->IsObject()) + return const_cast<QJSValuePrivate*>(this); + if (m_value->IsNull() || m_value->IsUndefined()) // avoid "Uncaught TypeError: Cannot convert null to object" + return InvalidValue(); + return new QJSValuePrivate(engine, m_value->ToObject()); + default: + Q_ASSERT_X(false, Q_FUNC_INFO, "Not all states are included in this switch"); + return InvalidValue(); + } +} + +/*! + This method is created only for QJSValue::toObject() purpose which is obsolete. + \internal + */ +QScriptPassPointer<QJSValuePrivate> QJSValuePrivate::toObject() const +{ + if (isJSBased()) + return toObject(engine()); + + // Without an engine there is not much we can do. + return new QJSValuePrivate; +} + +QString QJSValuePrivate::toString() const +{ + switch (m_state) { + case Invalid: + return QString(); + case CBool: + return u.m_bool ? QString::fromLatin1("true") : QString::fromLatin1("false"); + case CString: + return *u.m_string; + case CNumber: + return QJSConverter::toString(u.m_number); + case CNull: + return QString::fromLatin1("null"); + case CUndefined: + return QString::fromLatin1("undefined"); + case JSValue: + Q_ASSERT(!m_value.IsEmpty()); + v8::HandleScope handleScope; + v8::TryCatch tryCatch; + v8::Local<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 Invalid: + return QVariant(); + case CBool: + return QVariant(u.m_bool); + case CString: + return QVariant(*u.m_string); + case CNumber: + return QVariant(u.m_number); + case CNull: + return QVariant(); + case CUndefined: + return QVariant(); + case JSValue: + break; + } + + Q_ASSERT(m_state == JSValue); + Q_ASSERT(!m_value.IsEmpty()); + Q_ASSERT(m_engine); + + v8::HandleScope handleScope; + return m_engine->variantFromJS(m_value); +} + +inline QDateTime QJSValuePrivate::toDataTime() const +{ + if (!isDate()) + return QDateTime(); + + v8::HandleScope handleScope; + return QJSConverter::toDateTime(v8::Handle<v8::Date>::Cast(m_value)); + +} + +inline QRegExp QJSValuePrivate::toRegExp() const +{ + if (!isRegExp()) + return QRegExp(); + + v8::HandleScope handleScope; + return QJSConverter::toRegExp(v8::Handle<v8::RegExp>::Cast(m_value)); +} + +QObject* QJSValuePrivate::toQObject() const +{ + if (!isJSBased()) + return 0; + + v8::HandleScope handleScope; + return engine()->qtObjectFromJS(m_value); +} + +double QJSValuePrivate::toInteger() const +{ + double result = toNumber(); + if (qIsNaN(result)) + return 0; + if (qIsInf(result)) + return result; + return (result > 0) ? qFloor(result) : -1 * qFloor(-result); +} + +qint32 QJSValuePrivate::toInt32() const +{ + double result = toInteger(); + // Orginaly it should look like that (result == 0 || qIsInf(result) || qIsNaN(result)), but + // some of these operation are invoked in toInteger subcall. + if (qIsInf(result)) + return 0; + return result; +} + +quint32 QJSValuePrivate::toUInt32() const +{ + double result = toInteger(); + // Orginaly it should look like that (result == 0 || qIsInf(result) || qIsNaN(result)), but + // some of these operation are invoked in toInteger subcall. + if (qIsInf(result)) + return 0; + return result; +} + +quint16 QJSValuePrivate::toUInt16() const +{ + return toInt32(); +} + +inline bool QJSValuePrivate::isArray() const +{ + return isJSBased() && m_value->IsArray(); +} + +inline bool QJSValuePrivate::isBool() const +{ + return m_state == CBool || (isJSBased() && m_value->IsBoolean()); +} + +inline bool QJSValuePrivate::isCallable() const +{ + if (isFunction()) + return true; + if (isObject()) { + // Our C++ wrappers register function handlers but not always act as callables. + return v8::Object::Cast(*m_value)->IsCallable(); + } + return false; +} + +inline bool QJSValuePrivate::isError() const +{ + if (!isJSBased()) + return false; + v8::HandleScope handleScope; + return m_value->IsError(); +} + +inline bool QJSValuePrivate::isFunction() const +{ + return isJSBased() && m_value->IsFunction(); +} + +inline bool QJSValuePrivate::isNull() const +{ + return m_state == CNull || (isJSBased() && m_value->IsNull()); +} + +inline bool QJSValuePrivate::isNumber() const +{ + return m_state == CNumber || (isJSBased() && m_value->IsNumber()); +} + +inline bool QJSValuePrivate::isObject() const +{ + return isJSBased() && m_value->IsObject(); +} + +inline bool QJSValuePrivate::isString() const +{ + return m_state == CString || (isJSBased() && m_value->IsString()); +} + +inline bool QJSValuePrivate::isUndefined() const +{ + return m_state == CUndefined || (isJSBased() && m_value->IsUndefined()); +} + +inline bool QJSValuePrivate::isValid() const +{ + return m_state != Invalid; +} + +inline bool QJSValuePrivate::isVariant() const +{ + return isJSBased() && m_engine->isVariant(m_value); +} + +bool QJSValuePrivate::isDate() const +{ + return (isJSBased() && m_value->IsDate()); +} + +bool QJSValuePrivate::isRegExp() const +{ + return (isJSBased() && m_value->IsRegExp()); +} + +bool QJSValuePrivate::isQObject() const +{ + return isJSBased() && engine()->isQObject(m_value); +} + +inline bool QJSValuePrivate::equals(QJSValuePrivate* other) +{ + if (!isValid()) + return !other->isValid(); + + if (!other->isValid()) + return false; + + if (!isJSBased() && !other->isJSBased()) { + switch (m_state) { + case CNull: + case CUndefined: + return other->isUndefined() || other->isNull(); + case CNumber: + switch (other->m_state) { + case CBool: + case CString: + return u.m_number == other->toNumber(); + case CNumber: + return u.m_number == other->u.m_number; + default: + return false; + } + case CBool: + switch (other->m_state) { + case CBool: + return u.m_bool == other->u.m_bool; + case CNumber: + return toNumber() == other->u.m_number; + case CString: + return toNumber() == other->toNumber(); + default: + return false; + } + case CString: + switch (other->m_state) { + case CBool: + return toNumber() == other->toNumber(); + case CNumber: + return toNumber() == other->u.m_number; + case CString: + return *u.m_string == *other->u.m_string; + default: + return false; + } + default: + Q_ASSERT_X(false, "QJSValue::equals", "Not all states are included in the previous switch statement."); + } + } + + v8::HandleScope handleScope; + if (isJSBased() && !other->isJSBased()) { + if (!other->assignEngine(engine())) { + qWarning("QJSValue::equals: cannot compare to a value created in a different engine"); + return false; + } + } else if (!isJSBased() && other->isJSBased()) { + if (!assignEngine(other->engine())) { + qWarning("QJSValue::equals: cannot compare to a value created in a different engine"); + return false; + } + } + + Q_ASSERT(this->engine() && other->engine()); + if (this->engine() != other->engine()) { + qWarning("QJSValue::equals: cannot compare to a value created in a different engine"); + return false; + } + return m_value->Equals(other->m_value); +} + +inline bool QJSValuePrivate::strictlyEquals(QJSValuePrivate* other) +{ + if (isJSBased()) { + // We can't compare these two values without binding to the same engine. + if (!other->isJSBased()) { + if (other->assignEngine(engine())) + return m_value->StrictEquals(other->m_value); + return false; + } + if (other->engine() != engine()) { + qWarning("QJSValue::strictlyEquals: cannot compare to a value created in a different engine"); + return false; + } + return m_value->StrictEquals(other->m_value); + } + if (isStringBased()) { + if (other->isStringBased()) + return *u.m_string == *(other->u.m_string); + if (other->isJSBased()) { + assignEngine(other->engine()); + return m_value->StrictEquals(other->m_value); + } + } + if (isNumberBased()) { + if (other->isJSBased()) { + assignEngine(other->engine()); + return m_value->StrictEquals(other->m_value); + } + if (m_state != other->m_state) + return false; + if (m_state == CNumber) + return u.m_number == other->u.m_number; + Q_ASSERT(m_state == CBool); + return u.m_bool == other->u.m_bool; + } + + if (!isValid() && !other->isValid()) + return true; + + return false; +} + +inline bool QJSValuePrivate::lessThan(QJSValuePrivate *other) const +{ + if (engine() != other->engine() && engine() && other->engine()) { + qWarning("QJSValue::lessThan: cannot compare to a value created in a different engine"); + return false; + } + + if (!isValid() || !other->isValid()) + return false; + + if (isString() && other->isString()) + return toString() < other->toString(); + + if (isObject() || other->isObject()) { + v8::HandleScope handleScope; + QV8Engine *eng = m_engine ? engine() : other->engine(); + // FIXME: lessThan can throw an exception which will be dropped by this code: + Q_ASSERT(eng); + eng->saveException(); + QScriptSharedDataPointer<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 bool QJSValuePrivate::instanceOf(QJSValuePrivate* other) const +{ + if (!isObject() || !other->isFunction()) + return false; + if (engine() != other->engine()) { + qWarning("QJSValue::instanceof: cannot perform operation on a value created in a different engine"); + return false; + } + v8::HandleScope handleScope; + return instanceOf(v8::Handle<v8::Object>::Cast(other->m_value)); +} + +inline bool QJSValuePrivate::instanceOf(v8::Handle<v8::Object> other) const +{ + Q_ASSERT(isObject()); + Q_ASSERT(other->IsFunction()); + + v8::Handle<v8::Object> self = v8::Handle<v8::Object>::Cast(m_value); + v8::Handle<v8::Value> selfPrototype = self->GetPrototype(); + v8::Handle<v8::Value> otherPrototype = other->Get(v8::String::New("prototype")); + + while (!selfPrototype->IsNull()) { + if (selfPrototype->StrictEquals(otherPrototype)) + return true; + // In general a prototype can be an object or null, but in the loop it can't be null, so + // we can cast it safely. + selfPrototype = v8::Handle<v8::Object>::Cast(selfPrototype)->GetPrototype(); + } + return false; +} + +inline QScriptPassPointer<QJSValuePrivate> QJSValuePrivate::prototype() const +{ + if (isObject()) { + v8::HandleScope handleScope; + return new QJSValuePrivate(engine(), v8::Handle<v8::Object>::Cast(m_value)->GetPrototype()); + } + return InvalidValue(); +} + +inline void QJSValuePrivate::setPrototype(QJSValuePrivate* prototype) +{ + if (isObject() && (prototype->isObject() || prototype->isNull())) { + if (engine() != prototype->engine()) { + if (prototype->engine()) { + qWarning("QJSValue::setPrototype() failed: cannot set a prototype created in a different engine"); + return; + } + prototype->assignEngine(engine()); + } + v8::HandleScope handleScope; + if (!v8::Handle<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 (!value->isValid()) { + // Remove the property. + v8::HandleScope handleScope; + v8::TryCatch tryCatch; + v8::Handle<v8::Object> recv(v8::Object::Cast(*m_value)); +// if (attribs & QJSValue::PropertyGetter && !(attribs & QJSValue::PropertySetter)) { +// v8::Local<v8::Object> descriptor = engine()->originalGlobalObject()->getOwnPropertyDescriptor(recv, name); +// if (!descriptor.IsEmpty()) { +// v8::Local<v8::Value> setter = descriptor->Get(v8::String::New("set")); +// if (!setter.IsEmpty() && !setter->IsUndefined()) { +// recv->Delete(name); +// engine()->originalGlobalObject()->defineGetterOrSetter(recv, name, setter, QJSValue::PropertySetter); +// if (tryCatch.HasCaught()) +// engine()->setException(tryCatch.Exception(), tryCatch.Message()); +// return; +// } +// } +// } else if (attribs & QJSValue::PropertySetter && !(attribs & QJSValue::PropertyGetter)) { +// v8::Local<v8::Object> descriptor = engine()->originalGlobalObject()->getOwnPropertyDescriptor(recv, name); +// if (!descriptor.IsEmpty()) { +// v8::Local<v8::Value> getter = descriptor->Get(v8::String::New("get")); +// if (!getter.IsEmpty() && !getter->IsUndefined()) { +// recv->Delete(name); +// engine()->originalGlobalObject()->defineGetterOrSetter(recv, name, getter, QJSValue::PropertyGetter); +// if (tryCatch.HasCaught()) +// engine()->setException(tryCatch.Exception(), tryCatch.Message()); +// return; +// } +// } +// } + recv->Delete(name); + if (tryCatch.HasCaught()) + engine()->setException(tryCatch.Exception(), tryCatch.Message()); + return; + } + + if (engine() != value->engine()) { + qWarning("QJSValue::setProperty(%s) failed: " + "cannot set value created in a different engine", + qPrintable(QJSConverter::toString(name))); + return; + } + + v8::TryCatch tryCatch; +// if (attribs & (QJSValue::PropertyGetter | QJSValue::PropertySetter)) { +// engine()->originalGlobalObject()->defineGetterOrSetter(*this, name, value->m_value, attribs); +// } else { + v8::Object::Cast(*m_value)->Set(name, value->m_value, v8::PropertyAttribute(attribs & QJSConverter::PropertyAttributeMask)); +// } + if (tryCatch.HasCaught()) + engine()->setException(tryCatch.Exception(), tryCatch.Message()); +} + +inline void QJSValuePrivate::setProperty(quint32 index, QJSValuePrivate* value, uint attribs) +{ + // FIXME this method should by integrated with other overloads to use the same code patch. + // for now it is not possible as v8 doesn't allow to set property attributes using index based api. + + if (!isObject()) + return; + + if (attribs) { + // FIXME we dont need to convert index to a string. + //Object::Set(int,value) do not take attributes. + setProperty(QString::number(index), value, attribs); + return; + } + + if (!value->isJSBased()) + value->assignEngine(engine()); + + if (!value->isValid()) { + // Remove the property. + v8::HandleScope handleScope; + v8::TryCatch tryCatch; + v8::Object::Cast(*m_value)->Delete(index); + if (tryCatch.HasCaught()) + engine()->setException(tryCatch.Exception(), tryCatch.Message()); + return; + } + + if (engine() != value->engine()) { + qWarning("QJSValue::setProperty() failed: cannot set value created in a different engine"); + return; + } + + v8::HandleScope handleScope; + v8::TryCatch tryCatch; + v8::Object::Cast(*m_value)->Set(index, value->m_value); + if (tryCatch.HasCaught()) + engine()->setException(tryCatch.Exception(), tryCatch.Message()); +} + +inline QScriptPassPointer<QJSValuePrivate> QJSValuePrivate::property(const QString& name) const +{ + if (!name.length()) + return InvalidValue(); + + 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 InvalidValue(); + return property<>(name); +} + +inline QScriptPassPointer<QJSValuePrivate> QJSValuePrivate::property(quint32 index) const +{ + if (!isObject()) + return InvalidValue(); + 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() || (result->IsUndefined() && !self->Has(name))) { + // In QtScript we make a distinction between a property that exists and has value undefined, + // and a property that doesn't exist; in the latter case, we should return an invalid value. + return InvalidValue(); + } + return new QJSValuePrivate(engine(), result); +} + +inline bool QJSValuePrivate::deleteProperty(const QString& name) +{ + if (!isObject()) + return false; + + v8::HandleScope handleScope; + v8::Handle<v8::Object> self(v8::Handle<v8::Object>::Cast(m_value)); + return self->Delete(QJSConverter::toString(name)); +} + +inline QJSValue::PropertyFlags QJSValuePrivate::propertyFlags(const QString& name) const +{ + if (!isObject()) + return QJSValue::PropertyFlags(0); + + v8::HandleScope handleScope; + return engine()->getPropertyFlags(v8::Handle<v8::Object>::Cast(m_value), QJSConverter::toString(name)); +} + +inline QJSValue::PropertyFlags QJSValuePrivate::propertyFlags(v8::Handle<v8::String> name) const +{ + if (!isObject()) + return QJSValue::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 InvalidValue(); + + 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 InvalidValue(); + } + + 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 InvalidValue(); + } + + 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::construct(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::construct(const QJSValueList& args) +{ + if (!isCallable()) + return InvalidValue(); + + 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::construct() failed: cannot construct function with argument created in a different engine"); + return InvalidValue(); + } + + return construct(argc, argv.data()); +} + +/*! \internal + * Make sure this value is associated with a v8 value belogning to this engine. + * If the value was invalid, or belogning to another engine, return false. + */ +bool QJSValuePrivate::assignEngine(QV8Engine* engine) +{ + Q_ASSERT(engine); + v8::HandleScope handleScope; + switch (m_state) { + case Invalid: + return false; + case CBool: + m_value = v8::Persistent<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 + reinitialize this value to an invalid value. +*/ +void QJSValuePrivate::reinitialize() +{ + if (isJSBased()) { + m_engine->unregisterValue(this); + m_value.Dispose(); + m_value.Clear(); + } else if (isStringBased()) { + delete u.m_string; + } + m_engine = 0; + m_state = Invalid; +} + +/*! + \internal + reinitialize this value to an JSValue. +*/ +void QJSValuePrivate::reinitialize(QV8Engine* engine, v8::Handle<v8::Value> value) +{ + Q_ASSERT_X(this != InvalidValue(), Q_FUNC_INFO, "static invalid can't be reinitialized to a different value"); + if (isJSBased()) { + m_value.Dispose(); + // avoid double registration + m_engine->unregisterValue(this); + } else if (isStringBased()) { + delete u.m_string; + } + m_engine = engine; + m_state = JSValue; + m_value = v8::Persistent<v8::Value>::New(value); + m_engine->registerValue(this); +} + +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->isValid() && !value->assignEngine(engine()))) + // Different engines are not allowed! + return false; + if (value->isValid()) + argv[j] = *value; + else + argv[j] = engine()->makeJSValue(QJSValue::UndefinedValue); + } + return true; +} + +QT_END_NAMESPACE + +#endif diff --git a/src/declarative/qml/v8/qjsvalue_p.h b/src/declarative/qml/v8/qjsvalue_p.h new file mode 100644 index 0000000000..7b2a001f97 --- /dev/null +++ b/src/declarative/qml/v8/qjsvalue_p.h @@ -0,0 +1,213 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtScript module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL-ONLY$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QJSVALUE_P_H +#define QJSVALUE_P_H + +#include <private/qv8_p.h> + +#include <QtCore/qbytearray.h> +#include <QtCore/qdatetime.h> +#include <QtCore/qmath.h> +#include <QtCore/qvarlengtharray.h> +#include <qdebug.h> + +#include "qscripttools_p.h" +#include "qscriptshareddata_p.h" +#include "qjsvalue.h" + +QT_BEGIN_NAMESPACE + +/*! + \internal + \class QJSValuePrivate +*/ +class QJSValuePrivate + : public QSharedData + , public QScriptLinkedNode +{ +public: + inline QJSValuePrivate(); + inline static QJSValuePrivate* get(const QJSValue& q); + inline static QJSValue get(const QJSValuePrivate* d); + inline static QJSValue get(QJSValuePrivate* d); + inline static QJSValue get(QScriptPassPointer<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); + + inline QJSValuePrivate(QV8Engine *engine, bool value); + inline QJSValuePrivate(QV8Engine *engine, int value); + inline QJSValuePrivate(QV8Engine *engine, uint value); + inline QJSValuePrivate(QV8Engine *engine, double value); + inline QJSValuePrivate(QV8Engine *engine, const QString& value); + inline QJSValuePrivate(QV8Engine *engine, QJSValue::SpecialValue value); + inline QJSValuePrivate(QV8Engine *engine, v8::Handle<v8::Value>); + inline void reinitialize(); + inline void reinitialize(QV8Engine *engine, v8::Handle<v8::Value> value); + + inline bool toBool() const; + inline double toNumber() const; + inline QScriptPassPointer<QJSValuePrivate> toObject() const; + inline QScriptPassPointer<QJSValuePrivate> toObject(QV8Engine *engine) const; + inline QString toString() const; + inline double toInteger() const; + inline qint32 toInt32() const; + inline quint32 toUInt32() const; + inline quint16 toUInt16() const; + inline QDateTime toDataTime() const; + inline QRegExp toRegExp() const; + inline QObject *toQObject() const; + inline QVariant toVariant() const; + + inline bool isArray() const; + inline bool isBool() const; + inline bool isCallable() const; + inline bool isError() const; + inline bool isFunction() const; + inline bool isNull() const; + inline bool isNumber() const; + inline bool isObject() const; + inline bool isString() const; + inline bool isUndefined() const; + inline bool isValid() const; + inline bool isVariant() const; + inline bool isDate() const; + inline bool isRegExp() const; + inline bool isQObject() const; + + inline bool equals(QJSValuePrivate* other); + inline bool strictlyEquals(QJSValuePrivate* other); + inline bool lessThan(QJSValuePrivate *other) const; + inline bool instanceOf(QJSValuePrivate*) const; + inline bool instanceOf(v8::Handle<v8::Object> 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 QJSValue::PropertyFlags propertyFlags(const QString& name) const; + inline QJSValue::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> construct(int argc, v8::Handle<v8::Value> *argv); + inline QScriptPassPointer<QJSValuePrivate> construct(const QJSValueList& args); + inline QScriptPassPointer<QJSValuePrivate> construct(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: + QV8Engine *m_engine; + + // Please, update class documentation when you change the enum. + enum State { + Invalid = 0, + CString = 0x1000, + CNumber, + CBool, + CNull, + CUndefined, + JSValue = 0x2000, // V8 values are equal or higher then this value. + // JSPrimitive, + // JSObject + } m_state; + + union CValue { + bool m_bool; + double m_number; + QString* m_string; + + CValue() : m_number(0) {} + CValue(bool value) : m_bool(value) {} + CValue(int number) : m_number(number) {} + CValue(uint number) : m_number(number) {} + CValue(double number) : m_number(number) {} + CValue(QString* string) : m_string(string) {} + } u; + // v8::Persistent is not a POD, so can't be part of the union. + v8::Persistent<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; +}; + +// This template is used indirectly by the Q_GLOBAL_STATIC macro below +template<> +class QGlobalStaticDeleter<QJSValuePrivate> +{ +public: + QGlobalStatic<QJSValuePrivate> &globalStatic; + QGlobalStaticDeleter(QGlobalStatic<QJSValuePrivate> &_globalStatic) + : globalStatic(_globalStatic) + { + globalStatic.pointer->ref.ref(); + } + + inline ~QGlobalStaticDeleter() + { + if (!globalStatic.pointer->ref.deref()) { // Logic copy & paste from SharedDataPointer + delete globalStatic.pointer; + } + globalStatic.pointer = 0; + globalStatic.destroyed = true; + } +}; + +Q_GLOBAL_STATIC(QJSValuePrivate, InvalidValue) + +QT_END_NAMESPACE + +#endif diff --git a/src/declarative/qml/v8/qjsvalueiterator.cpp b/src/declarative/qml/v8/qjsvalueiterator.cpp new file mode 100644 index 0000000000..ca9123f0c0 --- /dev/null +++ b/src/declarative/qml/v8/qjsvalueiterator.cpp @@ -0,0 +1,294 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtScript module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL-ONLY$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qjsvalueiterator.h" + +#include "qscriptisolate_p.h" +#include "qjsvalue_p.h" +#include "qv8engine_p.h" +#include "qscript_impl_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QJSValueIterator + + \brief The QJSValueIterator class provides a Java-style iterator for QJSValue. + + \ingroup script + + + The QJSValueIterator constructor takes a QJSValue as + argument. After construction, the iterator is located at the very + beginning of the sequence of properties. Here's how to iterate over + all the properties of a QJSValue: + + \snippet doc/src/snippets/code/src_script_QJSValueIterator.cpp 0 + + The next() advances the iterator. The name(), value() and flags() + functions return the name, value and flags of the last item that was + jumped over. + + If you want to remove properties as you iterate over the + QJSValue, use remove(). If you want to modify the value of a + property, use setValue(). + + Note that QJSValueIterator only iterates over the QJSValue's + own properties; i.e. it does not follow the prototype chain. You can + use a loop like this to follow the prototype chain: + + \snippet doc/src/snippets/code/src_script_QJSValueIterator.cpp 1 + + Note that QJSValueIterator will not automatically skip over + properties that have the QJSValue::SkipInEnumeration flag set; + that flag only affects iteration in script code. If you want, you + can skip over such properties with code like the following: + + \snippet doc/src/snippets/code/src_script_QJSValueIterator.cpp 2 + + \sa QJSValue::property() +*/ + +using v8::Persistent; +using v8::Local; +using v8::Array; +using v8::String; +using v8::Handle; +using v8::Object; +using v8::Value; + +// FIXME (Qt5) This class should be refactored. It should use the common Iterator interface. +// FIXME it could be faster! +class QJSValueIteratorPrivate { +public: + inline QJSValueIteratorPrivate(const QJSValuePrivate* value); + inline ~QJSValueIteratorPrivate(); + + inline bool hasNext() const; + inline bool next(); + + inline QString name() const; + + inline QScriptPassPointer<QJSValuePrivate> value() const; + + inline bool isValid() const; + inline QV8Engine* engine() const; +private: + Q_DISABLE_COPY(QJSValueIteratorPrivate) + //void dump(QString) const; + + QScriptSharedDataPointer<QJSValuePrivate> m_object; + QList<v8::Persistent<v8::String> > m_names; + QMutableListIterator<v8::Persistent<v8::String> > m_iterator; +}; + +inline QJSValueIteratorPrivate::QJSValueIteratorPrivate(const QJSValuePrivate* value) + : m_object(const_cast<QJSValuePrivate*>(value)) + , m_iterator(m_names) +{ + Q_ASSERT(value); + QV8Engine *engine = m_object->engine(); + QScriptIsolate api(engine); + if (!m_object->isObject()) + m_object = 0; + else { + v8::HandleScope scope; + Handle<Value> tmp = *value; + Handle<Object> obj = Handle<Object>::Cast(tmp); + Local<Array> names; + + // FIXME we need newer V8! + //names = obj->GetOwnPropertyNames(); + names = engine->getOwnPropertyNames(obj); + + // it is suboptimal, it would be better to write iterator instead + uint32_t count = names->Length(); + Local<String> name; + m_names.reserve(count); // The count is the maximal count of values. + for (uint32_t i = count - 1; i < count; --i) { + name = names->Get(i)->ToString(); + m_names.append(v8::Persistent<v8::String>::New(name)); + } + + // Reinitialize the iterator. + m_iterator = m_names; + } +} + +inline QJSValueIteratorPrivate::~QJSValueIteratorPrivate() +{ + QMutableListIterator<v8::Persistent<v8::String> > it = m_names; + //FIXME: we need register this QJSVAlueIterator + if (engine()) { + while (it.hasNext()) { + it.next().Dispose(); + } + } else { + // FIXME leak ? + } +} + +inline bool QJSValueIteratorPrivate::hasNext() const +{ + //dump("hasNext()"); + return isValid() + ? m_iterator.hasNext() : false; +} + +inline bool QJSValueIteratorPrivate::next() +{ + //dump("next();"); + if (m_iterator.hasNext()) { + m_iterator.next(); + return true; + } + return false; +} + +inline QString QJSValueIteratorPrivate::name() const +{ + //dump("name"); + if (!isValid()) + return QString(); + + return QJSConverter::toString(m_iterator.value()); +} + +inline QScriptPassPointer<QJSValuePrivate> QJSValueIteratorPrivate::value() const +{ + //dump("value()"); + if (!isValid()) + return InvalidValue(); + + return m_object->property(m_iterator.value()); +} + +inline bool QJSValueIteratorPrivate::isValid() const +{ + bool result = m_object ? m_object->isValid() : false; + // We know that if this object is still valid then it is an object + // if this assumption is not correct then some other logic in this class + // have to be changed too. + Q_ASSERT(!result || m_object->isObject()); + return result; +} + +inline QV8Engine* QJSValueIteratorPrivate::engine() const +{ + return m_object ? m_object->engine() : 0; +} + +//void QJSValueIteratorPrivate::dump(QString fname) const +//{ +// qDebug() << " *** " << fname << " ***"; +// foreach (Persistent<String> name, m_names) { +// qDebug() << " - " << QJSConverter::toString(name); +// } +//} + +/*! + Constructs an iterator for traversing \a object. The iterator is + set to be at the front of the sequence of properties (before the + first property). +*/ +QJSValueIterator::QJSValueIterator(const QJSValue& object) + : d_ptr(new QJSValueIteratorPrivate(QJSValuePrivate::get(object))) +{} + +/*! + Destroys the iterator. +*/ +QJSValueIterator::~QJSValueIterator() +{} + +/*! + Returns true if there is at least one item ahead of the iterator + (i.e. the iterator is \e not at the back of the property sequence); + otherwise returns false. + + \sa next(), hasPrevious() +*/ +bool QJSValueIterator::hasNext() const +{ + Q_D(const QJSValueIterator); + QScriptIsolate api(d->engine()); + return d->hasNext(); +} + +/*! + Advances the iterator by one position. + + Calling this function on an iterator located at the back of the + container leads to undefined results. + + \sa hasNext(), previous(), name() +*/ +bool QJSValueIterator::next() +{ + Q_D(QJSValueIterator); + QScriptIsolate api(d->engine()); + return d->next(); +} + +/*! + Returns the name of the last property that was jumped over using + next() or previous(). + + \sa value(), flags() +*/ +QString QJSValueIterator::name() const +{ + Q_D(const QJSValueIterator); + QScriptIsolate api(d->engine()); + return d_ptr->name(); +} + + +/*! + Returns the value of the last property that was jumped over using + next() or previous(). + + \sa setValue(), name() +*/ +QJSValue QJSValueIterator::value() const +{ + Q_D(const QJSValueIterator); + QScriptIsolate api(d->engine()); + return QJSValuePrivate::get(d->value()); +} + + +/*! + Makes the iterator operate on \a object. The iterator is set to be + at the front of the sequence of properties (before the first + property). +*/ +QJSValueIterator& QJSValueIterator::operator=(QJSValue& object) +{ + Q_D(QJSValueIterator); + QScriptIsolate api(d->engine()); + d_ptr.reset(new QJSValueIteratorPrivate(QJSValuePrivate::get(object))); + return *this; +} + +QT_END_NAMESPACE diff --git a/src/declarative/qml/v8/qjsvalueiterator.h b/src/declarative/qml/v8/qjsvalueiterator.h new file mode 100644 index 0000000000..1ec4d4b63b --- /dev/null +++ b/src/declarative/qml/v8/qjsvalueiterator.h @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtScript module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL-ONLY$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSCRIPTVALUEITERATOR_H +#define QSCRIPTVALUEITERATOR_H + +#include <QtDeclarative/qjsvalue.h> +#include <QtCore/qscopedpointer.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Script) + +class QString; + +class QJSValueIteratorPrivate; +class Q_SCRIPT_EXPORT QJSValueIterator +{ +public: + QJSValueIterator(const QJSValue &value); + ~QJSValueIterator(); + + bool hasNext() const; + bool next(); + + QString name() const; + + QJSValue value() const; + QJSValueIterator& operator=(QJSValue &value); + +private: + QScopedPointer<QJSValueIteratorPrivate> d_ptr; + + Q_DECLARE_PRIVATE(QJSValueIterator) + Q_DISABLE_COPY(QJSValueIterator) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QSCRIPTVALUEITERATOR_H diff --git a/src/declarative/qml/v8/qscript_impl_p.h b/src/declarative/qml/v8/qscript_impl_p.h new file mode 100644 index 0000000000..e66b561efe --- /dev/null +++ b/src/declarative/qml/v8/qscript_impl_p.h @@ -0,0 +1,41 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtScript module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL-ONLY$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QSCRIPT_IMPL_P_H +#define QSCRIPT_IMPL_P_H + +#include "qv8engine_impl_p.h" +#include "qjsvalue_impl_p.h" + +#endif //QSCRIPT_IMPL_P_H diff --git a/src/declarative/qml/v8/qscriptisolate_p.h b/src/declarative/qml/v8/qscriptisolate_p.h new file mode 100644 index 0000000000..fa200c00b6 --- /dev/null +++ b/src/declarative/qml/v8/qscriptisolate_p.h @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtScript module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL-ONLY$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef APIPREAMBLE_P_H +#define APIPREAMBLE_P_H + +#include <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/declarative/qml/v8/qscriptoriginalglobalobject_p.h b/src/declarative/qml/v8/qscriptoriginalglobalobject_p.h new file mode 100644 index 0000000000..c46d0e37a0 --- /dev/null +++ b/src/declarative/qml/v8/qscriptoriginalglobalobject_p.h @@ -0,0 +1,158 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtScript module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL-ONLY$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSCRIPTORIGINALGLOBALOBJECT_P_H +#define QSCRIPTORIGINALGLOBALOBJECT_P_H + +#include "QtCore/qglobal.h" +#include "qjsvalue.h" + +#include <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(const QV8Engine *engine, v8::Handle<v8::Context> context); + inline void destroy(); + + inline QJSValue::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; +}; + +QScriptOriginalGlobalObject::QScriptOriginalGlobalObject(const QV8Engine *engine, 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 QJSValue::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 |= QJSValue::Undeletable; + if (!descriptor->Get(enumerableName)->BooleanValue()) + flags |= QJSValue::SkipInEnumeration; + + //"writable" is only a property of the descriptor if it is not an accessor + if (descriptor->Has(writableName)) { + if (!descriptor->Get(writableName)->BooleanValue()) + flags |= QJSValue::ReadOnly; + } else { +// if (descriptor->Get(getName)->IsObject()) +// flags |= QScriptValue::PropertyGetter; +// if (descriptor->Get(setName)->IsObject()) +// flags |= QScriptValue::PropertySetter; + } + + return QJSValue::PropertyFlag(flags); +} + +inline v8::Local<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/declarative/qml/v8/qscriptshareddata_p.h b/src/declarative/qml/v8/qscriptshareddata_p.h new file mode 100644 index 0000000000..6604b10e32 --- /dev/null +++ b/src/declarative/qml/v8/qscriptshareddata_p.h @@ -0,0 +1,151 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtScript module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL-ONLY$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QSCRIPTSHAREDDATA_P_H +#define QSCRIPTSHAREDDATA_P_H + +#include "qglobal.h" +#include "qshareddata.h" + +QT_BEGIN_NAMESPACE + +/*! + \internal + This class should have the same interface as the QSharedData, but implementation doesn't + need to be thread safe, so atomic ref count was replaced by normal integer value. +*/ +class QScriptSharedData +{ +public: + class ReferenceCounter { + // FIXME shouldn't it be uint or something longer? + mutable int m_ref; + ReferenceCounter(int ref) : m_ref(ref) {} + ~ReferenceCounter() { Q_ASSERT_X(!m_ref, Q_FUNC_INFO, "Memory problem found"); } + public: + bool ref() { return ++m_ref; } + bool deref() { return --m_ref; } + friend class QScriptSharedData; + }; + + ReferenceCounter ref; + inline QScriptSharedData() : ref(0) { } + +private: + Q_DISABLE_COPY(QScriptSharedData) +}; + + +template <class 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/declarative/qml/v8/qscripttools_p.h b/src/declarative/qml/v8/qscripttools_p.h new file mode 100644 index 0000000000..f74fbab83f --- /dev/null +++ b/src/declarative/qml/v8/qscripttools_p.h @@ -0,0 +1,216 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtScript module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL-ONLY$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + + +#ifndef QSCRIPTTOOLS_P_H +#define QSCRIPTTOOLS_P_H + +#include <qdebug.h> + +QT_BEGIN_NAMESPACE + +template<class T> +class QScriptBagContainer; + +/*! + \internal + \interface + Helper class for a container. The purpuse of it is to add two pointer properties to a class + inheriting this class without bloating an interface. + + This class exist only as a memory storage implementation. The only way to use it is to inherit it. +*/ +class QScriptLinkedNode +{ +protected: + QScriptLinkedNode() + : m_next(0) + , m_prev(0) + {} + + ~QScriptLinkedNode() + { + Q_ASSERT_X(!isUsed(), Q_FUNC_INFO, "Destorying QScriptLinkedNode instance that still is in a container"); + } + +private: + bool isUsed() const + { + return m_next || m_prev; + } + +#if defined(Q_NO_TEMPLATE_FRIENDS) +public: +#else + template<class T> + friend class QScriptBagContainer; +#endif + QScriptLinkedNode *m_next; + QScriptLinkedNode *m_prev; +}; + +/*! + \internal + The QScriptBagContainer is a simple, low level, set like container for a pointer type castable to + QScriptLinkedNode*. + Algorithms complexity: + put: O(1) + get: O(1) + forEach: O(n) + \note This container doesn't take ownership of pointed values. + \attention All values have to be unique. +*/ +template<class T> +class QScriptBagContainer +{ +public: + QScriptBagContainer() + : m_first(0) + {} + + /*! + \internal + Add a this \a value to this container + */ + void insert(T* value) + { + //dump(Q_FUNC_INFO, value); + Q_ASSERT_X(!contains(value), Q_FUNC_INFO, "Can't insert a value which is in the bag already"); + QScriptLinkedNode* v = static_cast<QScriptLinkedNode*>(value); + Q_ASSERT(v); + Q_ASSERT_X(!v->m_next && !v->m_prev, Q_FUNC_INFO, "Can't insert a value which is in an another bag"); + + if (m_first) + m_first->m_prev = v; + + v->m_next = m_first; + v->m_prev = 0; + m_first = v; + } + + /*! + \internal + Remove this \a value from this container + */ + void remove(T* value) + { + //dump(Q_FUNC_INFO, value); + QScriptLinkedNode* v = static_cast<QScriptLinkedNode*>(value); + Q_ASSERT(v); + + if (!v->m_next && !v->m_prev && m_first != v) { + // ignore that value as it is not registered at all + // FIXME: That may be optimized out if unregister call is removed from ~QtDataBase + return; + } + + Q_ASSERT_X(contains(value), Q_FUNC_INFO, "Can't remove a value which is not in the bag"); + Q_ASSERT(v->m_prev || (m_first == v && !v->m_prev)); + + if (v->m_next) + v->m_next->m_prev= v->m_prev; + + if (v->m_prev) + v->m_prev->m_next = v->m_next; + else + m_first = v->m_next; + // reset removed value + v->m_next = v->m_prev = 0; + } + + /*! + \internal + Call \a fun for each element in this container. Fun should accept T* as a parameter. + \note In general it is not allowed to change this container by calling put() or get() unless + given value is the same as currently procceded by forEach. + */ + template<class Functor> + void forEach(Functor fun) + { + //dump(Q_FUNC_INFO); + QScriptLinkedNode *i = m_first; + QScriptLinkedNode *tmp; + while (i) { + tmp = i; + i = i->m_next; + fun(static_cast<T*>(tmp)); + } + } + + /*! + \internal + Clear this container. + */ + void clear() + { + m_first = 0; + } + + /*! + \internal + Returns true if this container is empty; false otherwise. + */ + bool isEmpty() const + { + return !m_first; + } + +// void dump(const char* msg, T* obj = 0) const +// { +// qDebug() << msg << obj; +// qDebug() << m_first; +// QScriptLinkedNode *i = m_first; +// while (i) { +// qDebug() <<" - " << i << "(" << i->m_prev << ", " << i->m_next <<")"; +// i = i->m_next; +// } +// } + +private: + bool contains(T *value) const + { + QScriptLinkedNode *i = m_first; + while (i) { + if (static_cast<T*>(i) == value) + return true; + i = i->m_next; + } + return false; + } + QScriptLinkedNode *m_first; +}; + +QT_END_NAMESPACE + +#endif //QSCRIPTTOOLS_P_H diff --git a/src/declarative/qml/v8/qv8bindings.cpp b/src/declarative/qml/v8/qv8bindings.cpp index 4f5543e690..c45274a97f 100644 --- a/src/declarative/qml/v8/qv8bindings.cpp +++ b/src/declarative/qml/v8/qv8bindings.cpp @@ -129,7 +129,7 @@ void QV8BindingsPrivate::Binding::update(QDeclarativePropertyPrivate::WriteFlags ep->referenceScarceResources(); v8::HandleScope handle_scope; - v8::Context::Scope scope(ep->v8engine.context()); + v8::Context::Scope scope(ep->v8engine()->context()); v8::Local<v8::Value> result = evaluate(v8::Handle<v8::Function>::Cast(parent->functions->Get(index)), &isUndefined); diff --git a/src/declarative/qml/v8/qv8engine.cpp b/src/declarative/qml/v8/qv8engine.cpp index 0294a60871..acbfb92b15 100644 --- a/src/declarative/qml/v8/qv8engine.cpp +++ b/src/declarative/qml/v8/qv8engine.cpp @@ -49,6 +49,7 @@ #include <private/qdeclarativeengine_p.h> #include <private/qdeclarativecomponent_p.h> #include <private/qdeclarativestringconverters_p.h> +#include <private/qdeclarativeapplication_p.h> #include <QtDeclarative/qdeclarativecomponent.h> @@ -58,10 +59,15 @@ #include <QtCore/qnumeric.h> #include <QtGui/qdesktopservices.h> #include <QtGui/qfontdatabase.h> -#include <private/qdeclarativeapplication_p.h> #include <private/qdeclarativexmlhttprequest_p.h> #include <private/qdeclarativesqldatabase_p.h> +#include "qscript_impl_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 // QDeclarativeEngine is not available QT_BEGIN_NAMESPACE @@ -94,9 +100,41 @@ static bool ObjectComparisonCallback(v8::Local<v8::Object> lhs, v8::Local<v8::Ob return false; } -QV8Engine::QV8Engine() -: m_xmlHttpRequestData(0), m_sqlDatabaseData(0), m_listModelData(0) +QV8Engine::QV8Engine(QJSEngine* qq, QJSEngine::ContextOwnership ownership) + : q(qq) + , m_engine(0) + , m_ownsV8Context(ownership == QJSEngine::CreateNewContext) + , m_context((ownership == QJSEngine::CreateNewContext) ? v8::Context::New() : v8::Persistent<v8::Context>::New(v8::Context::GetCurrent())) + , m_originalGlobalObject(this, m_context) + , m_xmlHttpRequestData(0) + , m_sqlDatabaseData(0) + , m_listModelData(0) { + qMetaTypeId<QJSValue>(); + qMetaTypeId<QList<int> >(); + + QByteArray v8args = qgetenv("V8ARGS"); + if (!v8args.isEmpty()) + v8::V8::SetFlagsFromString(v8args.constData(), v8args.length()); + + v8::HandleScope handle_scope; + qPersistentRegister(m_context); + v8::Context::Scope context_scope(m_context); + + v8::V8::SetUserObjectComparisonCallbackFunction(ObjectComparisonCallback); + + m_stringWrapper.init(); + m_contextWrapper.init(this); + m_qobjectWrapper.init(this); + m_typeWrapper.init(this); + m_listWrapper.init(this); + m_variantWrapper.init(this); + m_valueTypeWrapper.init(this); + + { + v8::Handle<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() @@ -114,6 +152,10 @@ QV8Engine::~QV8Engine() qPersistentDispose(m_freezeObject); qPersistentDispose(m_getOwnPropertyNames); + + invalidateAllValues(); + clearExceptions(); + m_valueTypeWrapper.destroy(); m_variantWrapper.destroy(); m_listWrapper.destroy(); @@ -121,39 +163,11 @@ QV8Engine::~QV8Engine() m_qobjectWrapper.destroy(); m_contextWrapper.destroy(); m_stringWrapper.destroy(); - qPersistentDispose(m_context); -} - -void QV8Engine::init(QDeclarativeEngine *engine) -{ - m_engine = engine; - - QByteArray v8args = qgetenv("V8ARGS"); - if (!v8args.isEmpty()) - v8::V8::SetFlagsFromString(v8args.constData(), v8args.length()); - - v8::HandleScope handle_scope; - m_context = v8::Context::New(); - qPersistentRegister(m_context); - v8::Context::Scope context_scope(m_context); - - v8::V8::SetUserObjectComparisonCallbackFunction(ObjectComparisonCallback); - m_stringWrapper.init(); - m_contextWrapper.init(this); - m_qobjectWrapper.init(this); - m_typeWrapper.init(this); - m_listWrapper.init(this); - m_variantWrapper.init(this); - m_valueTypeWrapper.init(this); + m_originalGlobalObject.destroy(); - { - v8::Handle<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)); - } - - initializeGlobal(m_context->Global()); - freezeObject(m_context->Global()); + if (m_ownsV8Context) + qPersistentDispose(m_context); } QString QV8Engine::toStringStatic(v8::Handle<v8::Value> jsstr) @@ -357,8 +371,10 @@ v8::Handle<v8::Value> QV8Engine::fromVariant(const QVariant &variant) break; } - if (QDeclarativeValueType *vt = QDeclarativeEnginePrivate::get(m_engine)->valueTypes[type]) - return m_valueTypeWrapper.newValueType(variant, vt); + if (m_engine) { + if (QDeclarativeValueType *vt = QDeclarativeEnginePrivate::get(m_engine)->valueTypes[type]) + return m_valueTypeWrapper.newValueType(variant, vt); + } } else { if (type == qMetaTypeId<QDeclarativeListReference>()) { @@ -386,7 +402,7 @@ v8::Handle<v8::Value> QV8Engine::fromVariant(const QVariant &variant) } // XXX TODO: To be compatible, we still need to handle: - // + QScriptValue + // + QJSValue // + QObjectList // + QList<int> @@ -420,6 +436,7 @@ const QSet<QString> &QV8Engine::illegalNames() const // 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); @@ -531,9 +548,6 @@ void QV8Engine::initializeGlobal(v8::Handle<v8::Object> global) } } - if (m_engine) - qt->Set(v8::String::New("application"), newQObject(new QDeclarativeApplication(m_engine))); - qt->Set(v8::String::New("include"), V8FUNCTION(QV8Include::include, this)); qt->Set(v8::String::New("isQtObject"), V8FUNCTION(isQtObject, this)); qt->Set(v8::String::New("rgba"), V8FUNCTION(rgba, this)); @@ -543,12 +557,6 @@ void QV8Engine::initializeGlobal(v8::Handle<v8::Object> global) qt->Set(v8::String::New("size"), V8FUNCTION(size, this)); qt->Set(v8::String::New("vector3d"), V8FUNCTION(vector3d, this)); - if (m_engine) { - qt->Set(v8::String::New("lighter"), V8FUNCTION(lighter, this)); - qt->Set(v8::String::New("darker"), V8FUNCTION(darker, this)); - qt->Set(v8::String::New("tint"), V8FUNCTION(tint, this)); - } - qt->Set(v8::String::New("formatDate"), V8FUNCTION(formatDate, this)); qt->Set(v8::String::New("formatTime"), V8FUNCTION(formatTime, this)); qt->Set(v8::String::New("formatDateTime"), V8FUNCTION(formatDateTime, this)); @@ -561,6 +569,10 @@ void QV8Engine::initializeGlobal(v8::Handle<v8::Object> global) qt->Set(v8::String::New("resolvedUrl"), V8FUNCTION(resolvedUrl, this)); if (m_engine) { + qt->Set(v8::String::New("application"), newQObject(new QDeclarativeApplication(m_engine))); + qt->Set(v8::String::New("lighter"), V8FUNCTION(lighter, this)); + qt->Set(v8::String::New("darker"), V8FUNCTION(darker, this)); + qt->Set(v8::String::New("tint"), V8FUNCTION(tint, this)); qt->Set(v8::String::New("quit"), V8FUNCTION(quit, this)); qt->Set(v8::String::New("createQmlObject"), V8FUNCTION(createQmlObject, this)); qt->Set(v8::String::New("createComponent"), V8FUNCTION(createComponent, this)); @@ -1658,5 +1670,652 @@ v8::Handle<v8::Value> QV8Engine::qsTrIdNoOp(const v8::Arguments &args) return args[0]; } +void QV8Engine::initDeclarativeGlobalObject() +{ + v8::HandleScope handels; + v8::Context::Scope contextScope(m_context); + initializeGlobal(m_context->Global()); + freezeObject(m_context->Global()); +} + +void QV8Engine::setEngine(QDeclarativeEngine *engine) +{ + m_engine = engine; + initDeclarativeGlobalObject(); +} + +void QV8Engine::setException(v8::Handle<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; +} + +QScriptPassPointer<QJSValuePrivate> QV8Engine::newRegExp(const QString &pattern, const QString &flags) +{ + int f = v8::RegExp::kNone; + + QString::const_iterator i = flags.constBegin(); + for (; i != flags.constEnd(); ++i) { + switch (i->unicode()) { + case 'i': + f |= v8::RegExp::kIgnoreCase; + break; + case 'm': + f |= v8::RegExp::kMultiline; + break; + case 'g': + f |= v8::RegExp::kGlobal; + break; + default: + { + // ignore a Syntax Error. + } + } + } + + v8::Handle<v8::RegExp> regexp = v8::RegExp::New(QJSConverter::toString(pattern), static_cast<v8::RegExp::Flags>(f)); + return new QJSValuePrivate(this, regexp); +} + +QScriptPassPointer<QJSValuePrivate> QV8Engine::newRegExp(const QRegExp ®exp) +{ + return new QJSValuePrivate(this, QJSConverter::toRegExp(regexp)); +} + + +// Converts a QVariantList to JS. +// The result is a new Array object with length equal to the length +// of the QVariantList, and the elements being the QVariantList's +// elements converted to JS, recursively. +v8::Handle<v8::Array> QV8Engine::variantListToJS(const QVariantList &lst) +{ + v8::Handle<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::Handle<v8::Object> QV8Engine::variantMapToJS(const QVariantMap &vmap) +{ + v8::Handle<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; + int hash = jsObject->GetIdentityHash(); + if (visitedConversionObjects.contains(hash)) + return result; // Avoid recursion. + visitedConversionObjects.insert(hash); + v8::HandleScope handleScope; + // TODO: Only object's own property names. Include non-enumerable properties. + v8::Handle<v8::Array> propertyNames = jsObject->GetPropertyNames(); + uint32_t length = propertyNames->Length(); + 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. + + 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(); + else if (value->IsInt32()) + return value->ToInt32()->Value(); + else if (value->IsNumber()) + return value->ToNumber()->Value(); + if (value->IsString()) + return QJSConverter::toString(value->ToString()); + Q_ASSERT(value->IsObject()); + if (value->IsArray()) + return variantListFromJS(v8::Handle<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) +{ + Q_ASSERT(isVariant(value)); + return QV8Engine::toVariant(value, -1 /*whateever magic hint is*/); +} + +// Creates a QVariant wrapper object. +v8::Handle<v8::Object> QV8Engine::newVariant(const QVariant &value) +{ + v8::Handle<v8::Object> instance = variantWrapper()->newVariant(value); + return instance; +} + +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 InvalidValue(); + } + 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(InvalidValue()); + 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())); +} + QT_END_NAMESPACE diff --git a/src/declarative/qml/v8/qv8engine_impl_p.h b/src/declarative/qml/v8/qv8engine_impl_p.h new file mode 100644 index 0000000000..5c56efdf39 --- /dev/null +++ b/src/declarative/qml/v8/qv8engine_impl_p.h @@ -0,0 +1,131 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL-ONLY$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV8ENGINE_IMPL_P_H +#define QV8ENGINE_IMPL_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qv8engine_p.h" +#include "qjsvalue_p.h" +#include "qjsconverter_p.h" + +QT_BEGIN_NAMESPACE + +inline v8::Handle<v8::Value> QV8Engine::makeJSValue(bool value) +{ + return value ? v8::True() : v8::False(); +} + +inline v8::Handle<v8::Value> QV8Engine::makeJSValue(int value) +{ + return v8::Integer::New(value); +} + +inline v8::Handle<v8::Value> QV8Engine::makeJSValue(uint value) +{ + return v8::Integer::NewFromUnsigned(value); +} + +inline v8::Handle<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::Handle<v8::Value> QV8Engine::makeJSValue(const QString& value) +{ + return QJSConverter::toString(value); +} + +class QtScriptBagCleaner +{ +public: + template<class T> + void operator () (T* value) const + { + value->reinitialize(); + } +}; + +inline void QV8Engine::registerValue(QJSValuePrivate *data) +{ + m_values.insert(data); +} + +inline void QV8Engine::unregisterValue(QJSValuePrivate *data) +{ + m_values.remove(data); +} + +inline void QV8Engine::invalidateAllValues() +{ + QtScriptBagCleaner invalidator; + m_values.forEach(invalidator); + m_values.clear(); +} + +/*! + \internal + \note property can be index (v8::Integer) or a property (v8::String) name, according to ECMA script + property would be converted to a string. +*/ +inline QJSValue::PropertyFlags QV8Engine::getPropertyFlags(v8::Handle<v8::Object> object, v8::Handle<v8::Value> property) +{ + QJSValue::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/declarative/qml/v8/qv8engine_p.h b/src/declarative/qml/v8/qv8engine_p.h index 340945c6ee..b95e55002b 100644 --- a/src/declarative/qml/v8/qv8engine_p.h +++ b/src/declarative/qml/v8/qv8engine_p.h @@ -57,7 +57,14 @@ #include <QtCore/qvariant.h> #include <QtCore/qset.h> #include <QtCore/qmutex.h> +#include <QtCore/qstack.h> +#include <QtCore/qstringlist.h> + #include <private/qv8_p.h> +#include <qjsengine.h> +#include <qjsvalue.h> +#include "qscriptoriginalglobalobject_p.h" +#include "qscripttools_p.h" #include <private/qdeclarativepropertycache_p.h> @@ -210,18 +217,49 @@ class QDeclarativeContextData; class Q_DECLARATIVE_EXPORT QV8Engine { public: - QV8Engine(); + static QV8Engine* get(QJSEngine* q) { Q_ASSERT(q); return q->handle(); } + static QJSEngine* get(QV8Engine* d) { Q_ASSERT(d); return d->q; } + + QV8Engine(QJSEngine* qq,QJSEngine::ContextOwnership ownership = QJSEngine::CreateNewContext); ~QV8Engine(); struct Deletable { virtual ~Deletable() {} }; - void init(QDeclarativeEngine *); + class Exception + { + typedef QPair<v8::Persistent<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(QDeclarativeEngine *engine); QDeclarativeEngine *engine() { return m_engine; } v8::Local<v8::Object> global() { return m_context->Global(); } - v8::Handle<v8::Context> context() { return m_context; } + v8::Handle<v8::Context> context() const { return m_context; } + + inline void registerValue(QJSValuePrivate *data); + inline void unregisterValue(QJSValuePrivate *data); + inline void invalidateAllValues(); + QV8ContextWrapper *contextWrapper() { return &m_contextWrapper; } QV8QObjectWrapper *qobjectWrapper() { return &m_qobjectWrapper; } QV8TypeWrapper *typeWrapper() { return &m_typeWrapper; } @@ -237,6 +275,7 @@ public: QDeclarativeContextData *callingContext(); v8::Local<v8::Array> getOwnPropertyNames(v8::Handle<v8::Object>); + inline QJSValue::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); @@ -257,6 +296,9 @@ public: // Return the QML global "scope" object for the \a ctxt context and \a scope object. inline v8::Local<v8::Object> qmlScope(QDeclarativeContextData *ctxt, QObject *scope); + QScriptPassPointer<QJSValuePrivate> newRegExp(const QRegExp ®exp); + QScriptPassPointer<QJSValuePrivate> newRegExp(const QString &pattern, const QString &flags); + // Return a JS wrapper for the given QObject \a object inline v8::Handle<v8::Value> newQObject(QObject *object); inline bool isQObject(v8::Handle<v8::Value>); @@ -281,8 +323,19 @@ public: // Return the list of illegal id names (the names of the properties on the global object) const QSet<QString> &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 *); @@ -295,9 +348,50 @@ public: inline Deletable *extensionData(int) const; void setExtensionData(int, Deletable *); -private: + inline v8::Handle<v8::Value> makeJSValue(bool value); + inline v8::Handle<v8::Value> makeJSValue(int value); + inline v8::Handle<v8::Value> makeJSValue(uint value); + inline v8::Handle<v8::Value> makeJSValue(double value); + inline v8::Handle<v8::Value> makeJSValue(QJSValue::SpecialValue value); + inline v8::Handle<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::Handle<v8::Object> newVariant(const QVariant &variant); + QScriptPassPointer<QJSValuePrivate> newVariant(QJSValuePrivate* value, const QVariant &variant); + + v8::Handle<v8::Array> variantListToJS(const QVariantList &lst); + QVariantList variantListFromJS(v8::Handle<v8::Array> jsArray); + + v8::Handle<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(); + + QObject *qtObjectFromJS(v8::Handle<v8::Value> value); + QSet<int> visitedConversionObjects; +protected: + QJSEngine* q; QDeclarativeEngine *m_engine; + bool m_ownsV8Context; v8::Persistent<v8::Context> m_context; + QScriptOriginalGlobalObject m_originalGlobalObject; QV8StringWrapper m_stringWrapper; QV8ContextWrapper m_contextWrapper; @@ -318,6 +412,8 @@ private: QSet<QString> m_illegalNames; + Exception m_exception; + QVariant toBasicVariant(v8::Handle<v8::Value>); void initializeGlobal(v8::Handle<v8::Object>); @@ -356,6 +452,10 @@ private: double qtDateTimeToJsDate(const QDateTime &dt); QDateTime qtDateTimeFromJsDate(double jsDate); +private: + QScriptBagContainer<QJSValuePrivate> m_values; + + Q_DISABLE_COPY(QV8Engine) }; // Allocate a new Persistent handle. *ALL* persistent handles in QML must be allocated diff --git a/src/declarative/qml/v8/qv8include.cpp b/src/declarative/qml/v8/qv8include.cpp index e2161a9001..71937d0aad 100644 --- a/src/declarative/qml/v8/qv8include.cpp +++ b/src/declarative/qml/v8/qv8include.cpp @@ -41,7 +41,7 @@ #include "qv8include_p.h" -#include <QtScript/qscriptengine.h> +#include <QtDeclarative/qjsengine.h> #include <QtNetwork/qnetworkrequest.h> #include <QtNetwork/qnetworkreply.h> #include <QtCore/qfile.h> diff --git a/src/declarative/qml/v8/qv8qobjectwrapper.cpp b/src/declarative/qml/v8/qv8qobjectwrapper.cpp index cc0380e684..f97f427ede 100644 --- a/src/declarative/qml/v8/qv8qobjectwrapper.cpp +++ b/src/declarative/qml/v8/qv8qobjectwrapper.cpp @@ -48,15 +48,17 @@ #include <private/qdeclarativeengine_p.h> #include <private/qdeclarativevmemetaobject_p.h> #include <private/qdeclarativebinding_p.h> +#include <private/qjsvalue_p.h> +#include <private/qscript_impl_p.h> -#include <QtScript/qscriptvalue.h> +#include <QtDeclarative/qjsvalue.h> #include <QtCore/qvarlengtharray.h> #include <QtCore/qtimer.h> #include <QtCore/qatomic.h> QT_BEGIN_NAMESPACE -Q_DECLARE_METATYPE(QScriptValue); +Q_DECLARE_METATYPE(QJSValue); Q_DECLARE_METATYPE(QDeclarativeV8Handle); #if defined(__GNUC__) @@ -137,7 +139,7 @@ private: QString *qstringPtr; QVariant *qvariantPtr; QList<QObject *> *qlistPtr; - QScriptValue *qscriptValuePtr; + QJSValue *qjsValuePtr; QDeclarativeV8Handle *handlePtr; }; @@ -691,7 +693,9 @@ v8::Handle<v8::Array> QV8QObjectWrapper::Enumerator(const v8::AccessorInfo &info QStringList result; - QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(resource->engine->engine()); + QDeclarativeEnginePrivate *ep = resource->engine->engine() + ? QDeclarativeEnginePrivate::get(resource->engine->engine()) + : 0; QDeclarativePropertyCache *cache = 0; QDeclarativeData *ddata = QDeclarativeData::get(object); @@ -699,7 +703,7 @@ v8::Handle<v8::Array> QV8QObjectWrapper::Enumerator(const v8::AccessorInfo &info cache = ddata->propertyCache; if (!cache) { - cache = ep->cache(object); + cache = ep ? ep->cache(object) : 0; if (cache) { if (ddata) { cache->addref(); ddata->propertyCache = cache; } } else { @@ -1837,8 +1841,8 @@ void MetaCallArgument::cleanup() qstringPtr->~QString(); } else if (type == -1 || type == QMetaType::QVariant) { qvariantPtr->~QVariant(); - } else if (type == qMetaTypeId<QScriptValue>()) { - qscriptValuePtr->~QScriptValue(); + } else if (type == qMetaTypeId<QJSValue>()) { + qjsValuePtr->~QJSValue(); } else if (type == qMetaTypeId<QList<QObject *> >()) { qlistPtr->~QList<QObject *>(); } @@ -1857,8 +1861,8 @@ void MetaCallArgument::initAsType(int callType) if (type != 0) { cleanup(); type = 0; } if (callType == 0) return; - if (callType == qMetaTypeId<QScriptValue>()) { - qscriptValuePtr = new (&allocData) QScriptValue(); + if (callType == qMetaTypeId<QJSValue>()) { + qjsValuePtr = new (&allocData) QJSValue(); type = callType; } else if (callType == QMetaType::Int || callType == QMetaType::UInt || @@ -1891,9 +1895,9 @@ void MetaCallArgument::fromValue(int callType, QV8Engine *engine, v8::Handle<v8: { if (type != 0) { cleanup(); type = 0; } - if (callType == qMetaTypeId<QScriptValue>()) { - qscriptValuePtr = new (&allocData) QScriptValue(); - type = qMetaTypeId<QScriptValue>(); + 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; @@ -1939,7 +1943,7 @@ void MetaCallArgument::fromValue(int callType, QV8Engine *engine, v8::Handle<v8: qvariantPtr = new (&allocData) QVariant(); type = -1; - QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(engine->engine()); + QDeclarativeEnginePrivate *ep = engine->engine() ? QDeclarativeEnginePrivate::get(engine->engine()) : 0; QVariant v = engine->toVariant(value, -1); if (v.userType() == callType) { @@ -1947,7 +1951,7 @@ void MetaCallArgument::fromValue(int callType, QV8Engine *engine, v8::Handle<v8: } else if (v.canConvert((QVariant::Type)callType)) { *qvariantPtr = v; qvariantPtr->convert((QVariant::Type)callType); - } else if (const QMetaObject *mo = ep->rawMetaObjectForType(callType)) { + } else if (const QMetaObject *mo = ep ? ep->rawMetaObjectForType(callType) : 0) { QObject *obj = ep->toQObject(v); if (obj) { @@ -1965,8 +1969,8 @@ void MetaCallArgument::fromValue(int callType, QV8Engine *engine, v8::Handle<v8: v8::Handle<v8::Value> MetaCallArgument::toValue(QV8Engine *engine) { - if (type == qMetaTypeId<QScriptValue>()) { - return v8::Undefined(); + 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) { diff --git a/src/declarative/qml/v8/qv8typewrapper.cpp b/src/declarative/qml/v8/qv8typewrapper.cpp index 39d03dbeea..fe30670fc2 100644 --- a/src/declarative/qml/v8/qv8typewrapper.cpp +++ b/src/declarative/qml/v8/qv8typewrapper.cpp @@ -173,10 +173,12 @@ v8::Handle<v8::Value> QV8TypeWrapper::Getter(v8::Local<v8::String> property, return v8engine->typeWrapper()->newObject(object, d->type, resource->mode); } else if (QDeclarativeMetaType::ModuleApiInstance *moduleApi = typeNamespace->moduleApi()) { - // XXX TODO: Currently module APIs are implemented against QScriptValues. Consequently we - // can't do anything for script module apis here until the QtScript/V8 binding is complete. - if (moduleApi->qobjectCallback) { - moduleApi->qobjectApi = moduleApi->qobjectCallback(v8engine->engine(), 0); + if (moduleApi->scriptCallback) { + moduleApi->scriptApi = moduleApi->scriptCallback(v8engine->engine(), v8engine->engine()); + moduleApi->scriptCallback = 0; + moduleApi->qobjectCallback = 0; + } else if (moduleApi->qobjectCallback) { + moduleApi->qobjectApi = moduleApi->qobjectCallback(v8engine->engine(), v8engine->engine()); moduleApi->scriptCallback = 0; moduleApi->qobjectCallback = 0; } @@ -225,10 +227,12 @@ v8::Handle<v8::Value> QV8TypeWrapper::Setter(v8::Local<v8::String> property, QV8QObjectWrapper::IgnoreRevision); } else if (resource->typeNamespace) { if (QDeclarativeMetaType::ModuleApiInstance *moduleApi = resource->typeNamespace->moduleApi()) { - // XXX TODO: Currently module APIs are implemented against QScriptValues. Consequently we - // can't do anything for script module apis here until the QtScript/V8 binding is complete. - if (moduleApi->qobjectCallback) { - moduleApi->qobjectApi = moduleApi->qobjectCallback(v8engine->engine(), 0); + if (moduleApi->scriptCallback) { + moduleApi->scriptApi = moduleApi->scriptCallback(v8engine->engine(), v8engine->engine()); + moduleApi->scriptCallback = 0; + moduleApi->qobjectCallback = 0; + } else if (moduleApi->qobjectCallback) { + moduleApi->qobjectApi = moduleApi->qobjectCallback(v8engine->engine(), v8engine->engine()); moduleApi->scriptCallback = 0; moduleApi->qobjectCallback = 0; } diff --git a/src/declarative/qml/v8/qv8variantwrapper.cpp b/src/declarative/qml/v8/qv8variantwrapper.cpp index a5602fbbad..d4097d7f74 100644 --- a/src/declarative/qml/v8/qv8variantwrapper.cpp +++ b/src/declarative/qml/v8/qv8variantwrapper.cpp @@ -71,6 +71,7 @@ void QV8VariantWrapper::init(QV8Engine *engine) { m_engine = engine; m_toString = qPersistentNew<v8::Function>(v8::FunctionTemplate::New(ToString)->GetFunction()); + m_valueOf = qPersistentNew<v8::Function>(v8::FunctionTemplate::New(ValueOf)->GetFunction()); { v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New(); @@ -80,6 +81,9 @@ void QV8VariantWrapper::init(QV8Engine *engine) ft->InstanceTemplate()->SetAccessor(v8::String::New("toString"), ToStringGetter, 0, m_toString, v8::DEFAULT, v8::PropertyAttribute(v8::ReadOnly | v8::DontDelete)); + ft->InstanceTemplate()->SetAccessor(v8::String::New("valueOf"), ValueOfGetter, 0, + m_valueOf, v8::DEFAULT, + v8::PropertyAttribute(v8::ReadOnly | v8::DontDelete)); m_constructor = qPersistentNew<v8::Function>(ft->GetFunction()); } { @@ -98,6 +102,9 @@ void QV8VariantWrapper::init(QV8Engine *engine) ft->InstanceTemplate()->SetAccessor(v8::String::New("toString"), ToStringGetter, 0, m_toString, v8::DEFAULT, v8::PropertyAttribute(v8::ReadOnly | v8::DontDelete)); + ft->InstanceTemplate()->SetAccessor(v8::String::New("valueOf"), ValueOfGetter, 0, + m_valueOf, v8::DEFAULT, + v8::PropertyAttribute(v8::ReadOnly | v8::DontDelete)); m_scarceConstructor = qPersistentNew<v8::Function>(ft->GetFunction()); } @@ -105,6 +112,7 @@ void QV8VariantWrapper::init(QV8Engine *engine) void QV8VariantWrapper::destroy() { + qPersistentDispose(m_valueOf); qPersistentDispose(m_toString); qPersistentDispose(m_destroy); qPersistentDispose(m_preserve); @@ -185,6 +193,13 @@ v8::Handle<v8::Value> QV8VariantWrapper::ToStringGetter(v8::Local<v8::String> pr 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()); @@ -217,4 +232,27 @@ v8::Handle<v8::Value> QV8VariantWrapper::ToString(const v8::Arguments &args) } } +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/declarative/qml/v8/qv8variantwrapper_p.h b/src/declarative/qml/v8/qv8variantwrapper_p.h index 9e165f37f4..de74bc9e12 100644 --- a/src/declarative/qml/v8/qv8variantwrapper_p.h +++ b/src/declarative/qml/v8/qv8variantwrapper_p.h @@ -87,9 +87,12 @@ private: const v8::AccessorInfo &info); static v8::Handle<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; @@ -97,6 +100,7 @@ private: 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 diff --git a/src/declarative/qml/v8/script.pri b/src/declarative/qml/v8/script.pri new file mode 100644 index 0000000000..04a23d1f2b --- /dev/null +++ b/src/declarative/qml/v8/script.pri @@ -0,0 +1,20 @@ + +INCLUDEPATH += $$PWD + +SOURCES += \ + $$PWD/qjsengine.cpp \ + $$PWD/qjsvalue.cpp \ + $$PWD/qjsvalueiterator.cpp \ + +HEADERS += \ + $$PWD/qjsengine.h \ + $$PWD/qjsvalue.h \ + $$PWD/qjsvalue_p.h \ + $$PWD/qjsvalueiterator.h \ + $$PWD/qjsvalue_impl_p.h \ + $$PWD/qjsconverter_p.h \ + $$PWD/qscriptisolate_p.h \ + $$PWD/qscriptshareddata_p.h \ + $$PWD/qscripttools_p.h \ + $$PWD/qscript_impl_p.h \ + $$PWD/qscriptoriginalglobalobject_p.h diff --git a/src/declarative/qml/v8/v8.pri b/src/declarative/qml/v8/v8.pri index 61e0184884..97b3d679df 100644 --- a/src/declarative/qml/v8/v8.pri +++ b/src/declarative/qml/v8/v8.pri @@ -1,6 +1,8 @@ INCLUDEPATH += $$PWD/../../../3rdparty/javascriptcore INCLUDEPATH += $$PWD +include(script.pri) + HEADERS += \ $$PWD/qv8_p.h \ $$PWD/qv8stringwrapper_p.h \ @@ -16,6 +18,7 @@ HEADERS += \ $$PWD/qv8worker_p.h \ $$PWD/qv8bindings_p.h \ $$PWD/../../../3rdparty/javascriptcore/DateMath.h \ + $$PWD/qv8engine_impl_p.h SOURCES += \ $$PWD/qv8stringwrapper.cpp \ @@ -31,4 +34,3 @@ SOURCES += \ $$PWD/qv8worker.cpp \ $$PWD/qv8bindings.cpp \ $$PWD/../../../3rdparty/javascriptcore/DateMath.cpp \ - |