/**************************************************************************** ** ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtQml module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qv8engine_p.h" #include "qv4sequenceobject_p.h" #include "private/qjsengine_p.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "qv4domerrors_p.h" #include "qv4sqlerrors_p.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include Q_DECLARE_METATYPE(QList) // XXX TODO: Need to check all the global functions will also work in a worker script where the // QQmlEngine is not available QT_BEGIN_NAMESPACE QV8Engine::QV8Engine(QJSEngine* qq) : q(qq) , m_engine(0) , m_xmlHttpRequestData(0) , m_listModelData(0) { QML_MEMORY_SCOPE_STRING("QV8Engine::QV8Engine"); qMetaTypeId(); qMetaTypeId >(); m_v4Engine = new QV4::ExecutionEngine; m_v4Engine->v8Engine = this; QV4::QObjectWrapper::initializeBindings(m_v4Engine); } QV8Engine::~QV8Engine() { for (int ii = 0; ii < m_extensionData.count(); ++ii) delete m_extensionData[ii]; m_extensionData.clear(); qt_rem_qmlxmlhttprequest(this, m_xmlHttpRequestData); m_xmlHttpRequestData = 0; delete m_listModelData; m_listModelData = 0; delete m_v4Engine; } QVariant QV8Engine::toVariant(const QV4::ValueRef value, int typeHint) { Q_ASSERT (!value->isEmpty()); QV4::Scope scope(m_v4Engine); if (QV4::VariantObject *v = value->as()) return v->data; if (typeHint == QVariant::Bool) return QVariant(value->toBoolean()); if (typeHint == QMetaType::QJsonValue) return QVariant::fromValue(QV4::JsonObject::toJsonValue(value)); if (typeHint == qMetaTypeId()) return QVariant::fromValue(QJSValue(new QJSValuePrivate(m_v4Engine, value))); if (value->asObject()) { QV4::ScopedObject object(scope, value); if (typeHint == QMetaType::QJsonObject && !value->asArrayObject() && !value->asFunctionObject()) { return QVariant::fromValue(QV4::JsonObject::toJsonObject(object)); } else if (QV4::QObjectWrapper *wrapper = object->as()) { return qVariantFromValue(wrapper->object()); } else if (QV4::QmlContextWrapper *wrapper = object->as()) { return QVariant(); } else if (QV4::QmlTypeWrapper *w = object->as()) { return w->toVariant(); } else if (QV4::QmlValueTypeWrapper *v = object->as()) { return v->toVariant(); } else if (QV4::QmlListWrapper *l = object->as()) { return l->toVariant(); } else if (object->isListType()) return QV4::SequencePrototype::toVariant(object); } if (value->asArrayObject()) { QV4::ScopedArrayObject a(scope, value); if (typeHint == qMetaTypeId >()) { QList list; uint32_t length = a->arrayLength(); QV4::Scoped qobjectWrapper(scope); for (uint32_t ii = 0; ii < length; ++ii) { qobjectWrapper = a->getIndexed(ii); if (!!qobjectWrapper) { list << qobjectWrapper->object(); } else { list << 0; } } return qVariantFromValue >(list); } else if (typeHint == QMetaType::QJsonArray) { return QVariant::fromValue(QV4::JsonObject::toJsonArray(a)); } bool succeeded = false; QVariant retn = QV4::SequencePrototype::toVariant(value, typeHint, &succeeded); if (succeeded) return retn; } return toBasicVariant(value); } static QV4::ReturnedValue arrayFromStringList(QV8Engine *engine, const QStringList &list) { QV4::ExecutionEngine *e = QV8Engine::getV4(engine); QV4::Scope scope(e); QV4::Scoped a(scope, e->newArrayObject()); int len = list.count(); a->arrayReserve(len); for (int ii = 0; ii < len; ++ii) { a->arrayData[ii].value = QV4::Encode(e->newString(list.at(ii))); a->arrayDataLen = ii + 1; } a->setArrayLengthUnchecked(len); return a.asReturnedValue(); } static QV4::ReturnedValue arrayFromVariantList(QV8Engine *engine, const QVariantList &list) { QV4::ExecutionEngine *e = QV8Engine::getV4(engine); QV4::Scope scope(e); QV4::Scoped a(scope, e->newArrayObject()); int len = list.count(); a->arrayReserve(len); for (int ii = 0; ii < len; ++ii) { a->arrayData[ii].value = engine->fromVariant(list.at(ii)); a->arrayDataLen = ii + 1; } a->setArrayLengthUnchecked(len); return a.asReturnedValue(); } static QV4::ReturnedValue objectFromVariantMap(QV8Engine *engine, const QVariantMap &map) { QV4::ExecutionEngine *e = QV8Engine::getV4(engine); QV4::Scope scope(e); QV4::ScopedObject o(scope, e->newObject()); QV4::ScopedString s(scope); QV4::ScopedValue v(scope); for (QVariantMap::ConstIterator iter = map.begin(); iter != map.end(); ++iter) o->put((s = e->newString(iter.key())), (v = engine->fromVariant(iter.value()))); return o.asReturnedValue(); } Q_CORE_EXPORT QString qt_regexp_toCanonical(const QString &, QRegExp::PatternSyntax); QV4::ReturnedValue QV8Engine::fromVariant(const QVariant &variant) { int type = variant.userType(); const void *ptr = variant.constData(); if (type < QMetaType::User) { switch (QMetaType::Type(type)) { case QMetaType::UnknownType: case QMetaType::Void: return QV4::Encode::undefined(); case QMetaType::Bool: return QV4::Encode(*reinterpret_cast(ptr)); case QMetaType::Int: return QV4::Encode(*reinterpret_cast(ptr)); case QMetaType::UInt: return QV4::Encode(*reinterpret_cast(ptr)); case QMetaType::LongLong: return QV4::Encode((double)*reinterpret_cast(ptr)); case QMetaType::ULongLong: return QV4::Encode((double)*reinterpret_cast(ptr)); case QMetaType::Double: return QV4::Encode(*reinterpret_cast(ptr)); case QMetaType::QString: return m_v4Engine->current->engine->newString(*reinterpret_cast(ptr))->asReturnedValue(); case QMetaType::Float: return QV4::Encode(*reinterpret_cast(ptr)); case QMetaType::Short: return QV4::Encode((int)*reinterpret_cast(ptr)); case QMetaType::UShort: return QV4::Encode((int)*reinterpret_cast(ptr)); case QMetaType::Char: return QV4::Encode((int)*reinterpret_cast(ptr)); case QMetaType::UChar: return QV4::Encode((int)*reinterpret_cast(ptr)); case QMetaType::QChar: return QV4::Encode((int)(*reinterpret_cast(ptr)).unicode()); case QMetaType::QDateTime: return QV4::Encode(m_v4Engine->newDateObject(*reinterpret_cast(ptr))); case QMetaType::QDate: return QV4::Encode(m_v4Engine->newDateObject(QDateTime(*reinterpret_cast(ptr)))); case QMetaType::QTime: return QV4::Encode(m_v4Engine->newDateObject(QDateTime(QDate(1970,1,1), *reinterpret_cast(ptr)))); case QMetaType::QRegExp: return QV4::Encode(m_v4Engine->newRegExpObject(*reinterpret_cast(ptr))); case QMetaType::QObjectStar: return QV4::QObjectWrapper::wrap(m_v4Engine, *reinterpret_cast(ptr)); case QMetaType::QStringList: { bool succeeded = false; QV4::Scope scope(m_v4Engine); QV4::ScopedValue retn(scope, QV4::SequencePrototype::fromVariant(m_v4Engine, variant, &succeeded)); if (succeeded) return retn.asReturnedValue(); return arrayFromStringList(this, *reinterpret_cast(ptr)); } case QMetaType::QVariantList: return arrayFromVariantList(this, *reinterpret_cast(ptr)); case QMetaType::QVariantMap: return objectFromVariantMap(this, *reinterpret_cast(ptr)); case QMetaType::QJsonValue: return QV4::JsonObject::fromJsonValue(m_v4Engine, *reinterpret_cast(ptr)); case QMetaType::QJsonObject: return QV4::JsonObject::fromJsonObject(m_v4Engine, *reinterpret_cast(ptr)); case QMetaType::QJsonArray: return QV4::JsonObject::fromJsonArray(m_v4Engine, *reinterpret_cast(ptr)); default: break; } if (QQmlValueType *vt = QQmlValueTypeFactory::valueType(type)) return QV4::QmlValueTypeWrapper::create(this, variant, vt); } else { QV4::Scope scope(m_v4Engine); if (type == qMetaTypeId()) { typedef QQmlListReferencePrivate QDLRP; QDLRP *p = QDLRP::get((QQmlListReference*)ptr); if (p->object) { return QV4::QmlListWrapper::create(this, p->property, p->propertyType); } else { return QV4::Encode::null(); } } else if (type == qMetaTypeId()) { const QJSValue *value = reinterpret_cast(ptr); QJSValuePrivate *valuep = QJSValuePrivate::get(*value); return valuep->getValue(m_v4Engine); } else if (type == qMetaTypeId >()) { // XXX Can this be made more by using Array as a prototype and implementing // directly against QList? const QList &list = *(QList*)ptr; QV4::Scoped a(scope, m_v4Engine->newArrayObject()); a->arrayReserve(list.count()); for (int ii = 0; ii < list.count(); ++ii) { a->arrayData[ii].value = QV4::QObjectWrapper::wrap(m_v4Engine, list.at(ii)); a->arrayDataLen = ii + 1; } a->setArrayLengthUnchecked(list.count()); return a.asReturnedValue(); } else if (QMetaType::typeFlags(type) & QMetaType::PointerToQObject) { return QV4::QObjectWrapper::wrap(m_v4Engine, *reinterpret_cast(ptr)); } bool objOk; QObject *obj = QQmlMetaType::toQObject(variant, &objOk); if (objOk) return QV4::QObjectWrapper::wrap(m_v4Engine, obj); bool succeeded = false; QV4::ScopedValue retn(scope, QV4::SequencePrototype::fromVariant(m_v4Engine, variant, &succeeded)); if (succeeded) return retn.asReturnedValue(); if (QQmlValueType *vt = QQmlValueTypeFactory::valueType(type)) return QV4::QmlValueTypeWrapper::create(this, variant, vt); } // XXX TODO: To be compatible, we still need to handle: // + QObjectList // + QList return QV4::Encode(m_v4Engine->newVariantObject(variant)); } QNetworkAccessManager *QV8Engine::networkAccessManager() { return QQmlEnginePrivate::get(m_engine)->getNetworkAccessManager(); } const QV4::IdentifierHash &QV8Engine::illegalNames() const { return m_illegalNames; } QQmlContextData *QV8Engine::callingContext() { return QV4::QmlContextWrapper::callingContext(m_v4Engine); } // Converts a JS value to a QVariant. // Null, Undefined -> QVariant() (invalid) // Boolean -> QVariant(bool) // Number -> QVariant(double) // String -> QVariant(QString) // Array -> QVariantList(...) // Date -> QVariant(QDateTime) // RegExp -> QVariant(QRegExp) // [Any other object] -> QVariantMap(...) QVariant QV8Engine::toBasicVariant(const QV4::ValueRef value) { if (value->isNullOrUndefined()) return QVariant(); if (value->isBoolean()) return value->booleanValue(); if (value->isInteger()) return value->integerValue(); if (value->isNumber()) return value->asDouble(); if (value->isString()) return value->stringValue()->toQString(); if (QV4::DateObject *d = value->asDateObject()) return d->toQDateTime(); // NOTE: since we convert QTime to JS Date, round trip will change the variant type (to QDateTime)! QV4::Scope scope(value->engine()); QV4::ScopedObject o(scope, value); Q_ASSERT(o); if (QV4::RegExpObject *re = o->as()) return re->toQRegExp(); if (o->asArrayObject()) { QV4::ScopedArrayObject a(scope, o); QV4::ScopedValue v(scope); QVariantList rv; int length = a->arrayLength(); for (int ii = 0; ii < length; ++ii) { v = a->getIndexed(ii); rv << toVariant(v, -1); } return rv; } if (!value->asFunctionObject()) return variantMapFromJS(o); return QVariant(); } void QV8Engine::initializeGlobal() { QV4::Scope scope(m_v4Engine); QV4::GlobalExtensions::init(m_engine, m_v4Engine->globalObject); QQmlLocale::registerStringLocaleCompare(m_v4Engine); QQmlDateExtension::registerExtension(m_v4Engine); QQmlNumberExtension::registerExtension(m_v4Engine); qt_add_domexceptions(m_v4Engine); m_xmlHttpRequestData = qt_add_qmlxmlhttprequest(this); qt_add_sqlexceptions(m_v4Engine); { m_illegalNames = QV4::IdentifierHash(m_v4Engine); for (uint i = 0; i < m_v4Engine->globalObject->internalClass->size; ++i) m_illegalNames.add(m_v4Engine->globalObject->internalClass->nameMap.at(i)->toQString(), true); } { #define FREEZE_SOURCE "(function freeze_recur(obj) { "\ " if (Qt.isQtObject(obj)) return;"\ " if (obj != Function.connect && obj != Function.disconnect && "\ " obj instanceof Object) {"\ " var properties = Object.getOwnPropertyNames(obj);"\ " for (var prop in properties) { "\ " if (prop == \"connect\" || prop == \"disconnect\") {"\ " Object.freeze(obj[prop]); "\ " continue;"\ " }"\ " freeze_recur(obj[prop]);"\ " }"\ " }"\ " if (obj instanceof Object) {"\ " Object.freeze(obj);"\ " }"\ "})" QV4::Scoped result(scope, QV4::Script::evaluate(m_v4Engine, QString::fromUtf8(FREEZE_SOURCE), QV4::ObjectRef::null())); Q_ASSERT(!!result); m_freezeObject = result; #undef FREEZE_SOURCE } } void QV8Engine::freezeObject(const QV4::ValueRef value) { QV4::Scope scope(m_v4Engine); QV4::ScopedFunctionObject f(scope, m_freezeObject.value()); QV4::ScopedCallData callData(scope, 1); callData->args[0] = value; callData->thisObject = m_v4Engine->globalObject; f->call(callData); } void QV8Engine::gc() { m_v4Engine->memoryManager->runGC(); } struct QV8EngineRegistrationData { QV8EngineRegistrationData() : extensionCount(0) {} QMutex mutex; int extensionCount; }; Q_GLOBAL_STATIC(QV8EngineRegistrationData, registrationData); QMutex *QV8Engine::registrationMutex() { return ®istrationData()->mutex; } int QV8Engine::registerExtension() { return registrationData()->extensionCount++; } void QV8Engine::setExtensionData(int index, Deletable *data) { if (m_extensionData.count() <= index) m_extensionData.resize(index + 1); if (m_extensionData.at(index)) delete m_extensionData.at(index); m_extensionData[index] = data; } void QV8Engine::initQmlGlobalObject() { initializeGlobal(); QV4::Scope scope(m_v4Engine); QV4::ScopedValue v(scope, m_v4Engine->globalObject); freezeObject(v); } void QV8Engine::setEngine(QQmlEngine *engine) { m_engine = engine; initQmlGlobalObject(); } QV4::ReturnedValue QV8Engine::global() { return m_v4Engine->globalObject->asReturnedValue(); } // 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. QV4::ReturnedValue QV8Engine::variantListToJS(const QVariantList &lst) { QV4::Scope scope(m_v4Engine); QV4::Scoped a(scope, m_v4Engine->newArrayObject()); a->arrayReserve(lst.size()); for (int i = 0; i < lst.size(); i++) { a->arrayData[i].value = variantToJS(lst.at(i)); a->arrayDataLen = i + 1; } a->setArrayLengthUnchecked(lst.size()); return a.asReturnedValue(); } // 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(QV4::ArrayObjectRef a, V8ObjectSet &visitedObjects) { QVariantList result; if (!a) return result; if (visitedObjects.contains(a)) // Avoid recursion. return result; visitedObjects.insert(a); QV4::Scope scope(a->engine()); QV4::ScopedValue v(scope); quint32 length = a->arrayLength(); for (quint32 i = 0; i < length; ++i) { v = a->getIndexed(i); result.append(variantFromJS(v, visitedObjects)); } visitedObjects.remove(a); 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. QV4::ReturnedValue QV8Engine::variantMapToJS(const QVariantMap &vmap) { QV4::Scope scope(m_v4Engine); QV4::Scoped o(scope, m_v4Engine->newObject()); QVariantMap::const_iterator it; QV4::ScopedString s(scope); for (it = vmap.constBegin(); it != vmap.constEnd(); ++it) { s = m_v4Engine->newIdentifier(it.key()); QV4::Property *p = o->insertMember(s, QV4::Attr_Data); p->value = variantToJS(it.value()); } return o.asReturnedValue(); } // 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(QV4::ObjectRef o, V8ObjectSet &visitedObjects) { QVariantMap result; if (!o || o->asFunctionObject()) return result; if (visitedObjects.contains(o)) { // Avoid recursion. // For compatibility with QVariant{List,Map} conversion, we return an // empty object (and no error is thrown). return result; } QV4::Scope scope(o->engine()); visitedObjects.insert(o); QV4::ObjectIterator it(scope, o, QV4::ObjectIterator::EnumerableOnly); QV4::ScopedValue name(scope); QV4::ScopedValue val(scope); while (1) { name = it.nextPropertyNameAsString(val); if (name->isNull()) break; QString key = name->toQStringNoThrow(); result.insert(key, variantFromJS(val, visitedObjects)); } visitedObjects.remove(o); 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. QV4::ReturnedValue QV8Engine::metaTypeToJS(int type, const void *data) { Q_ASSERT(data != 0); // check if it's one of the types we know switch (QMetaType::Type(type)) { case QMetaType::UnknownType: case QMetaType::Void: return QV4::Encode::undefined(); case QMetaType::Bool: return QV4::Encode(*reinterpret_cast(data)); case QMetaType::Int: return QV4::Encode(*reinterpret_cast(data)); case QMetaType::UInt: return QV4::Encode(*reinterpret_cast(data)); case QMetaType::LongLong: return QV4::Encode(double(*reinterpret_cast(data))); case QMetaType::ULongLong: #if defined(Q_OS_WIN) && defined(_MSC_FULL_VER) && _MSC_FULL_VER <= 12008804 #pragma message("** NOTE: You need the Visual Studio Processor Pack to compile support for 64bit unsigned integers.") return QV4::Encode(double((qlonglong)*reinterpret_cast(data))); #elif defined(Q_CC_MSVC) && !defined(Q_CC_MSVC_NET) return QV4::Encode(double((qlonglong)*reinterpret_cast(data))); #else return QV4::Encode(double(*reinterpret_cast(data))); #endif case QMetaType::Double: return QV4::Encode(*reinterpret_cast(data)); case QMetaType::QString: return m_v4Engine->current->engine->newString(*reinterpret_cast(data))->asReturnedValue(); case QMetaType::Float: return QV4::Encode(*reinterpret_cast(data)); case QMetaType::Short: return QV4::Encode((int)*reinterpret_cast(data)); case QMetaType::UShort: return QV4::Encode((int)*reinterpret_cast(data)); case QMetaType::Char: return QV4::Encode((int)*reinterpret_cast(data)); case QMetaType::UChar: return QV4::Encode((int)*reinterpret_cast(data)); case QMetaType::QChar: return QV4::Encode((int)(*reinterpret_cast(data)).unicode()); case QMetaType::QStringList: return QV4::Encode(m_v4Engine->newArrayObject(*reinterpret_cast(data))); case QMetaType::QVariantList: return variantListToJS(*reinterpret_cast(data)); case QMetaType::QVariantMap: return variantMapToJS(*reinterpret_cast(data)); case QMetaType::QDateTime: return QV4::Encode(m_v4Engine->newDateObject(*reinterpret_cast(data))); case QMetaType::QDate: return QV4::Encode(m_v4Engine->newDateObject(QDateTime(*reinterpret_cast(data)))); case QMetaType::QRegExp: return QV4::Encode(m_v4Engine->newRegExpObject(*reinterpret_cast(data))); case QMetaType::QObjectStar: return QV4::QObjectWrapper::wrap(m_v4Engine, *reinterpret_cast(data)); case QMetaType::QVariant: return variantToJS(*reinterpret_cast(data)); case QMetaType::QJsonValue: return QV4::JsonObject::fromJsonValue(m_v4Engine, *reinterpret_cast(data)); case QMetaType::QJsonObject: return QV4::JsonObject::fromJsonObject(m_v4Engine, *reinterpret_cast(data)); case QMetaType::QJsonArray: return QV4::JsonObject::fromJsonArray(m_v4Engine, *reinterpret_cast(data)); default: if (type == qMetaTypeId()) { return QJSValuePrivate::get(*reinterpret_cast(data))->getValue(m_v4Engine); } else { QByteArray typeName = QMetaType::typeName(type); if (typeName.endsWith('*') && !*reinterpret_cast(data)) { return QV4::Encode::null(); } else { // Fall back to wrapping in a QVariant. return QV4::Encode(m_v4Engine->newVariantObject(QVariant(type, data))); } } } Q_UNREACHABLE(); return 0; } // 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(const QV4::ValueRef value, int type, void *data) { QV4::Scope scope(QV8Engine::getV4(this)); // check if it's one of the types we know switch (QMetaType::Type(type)) { case QMetaType::Bool: *reinterpret_cast(data) = value->toBoolean(); return true; case QMetaType::Int: *reinterpret_cast(data) = value->toInt32(); return true; case QMetaType::UInt: *reinterpret_cast(data) = value->toUInt32(); return true; case QMetaType::LongLong: *reinterpret_cast(data) = qlonglong(value->toInteger()); return true; case QMetaType::ULongLong: *reinterpret_cast(data) = qulonglong(value->toInteger()); return true; case QMetaType::Double: *reinterpret_cast(data) = value->toNumber(); return true; case QMetaType::QString: if (value->isUndefined() || value->isNull()) *reinterpret_cast(data) = QString(); else *reinterpret_cast(data) = value->toString(m_v4Engine->current)->toQString(); return true; case QMetaType::Float: *reinterpret_cast(data) = value->toNumber(); return true; case QMetaType::Short: *reinterpret_cast(data) = short(value->toInt32()); return true; case QMetaType::UShort: *reinterpret_cast(data) = value->toUInt16(); return true; case QMetaType::Char: *reinterpret_cast(data) = char(value->toInt32()); return true; case QMetaType::UChar: *reinterpret_cast(data) = (unsigned char)(value->toInt32()); return true; case QMetaType::QChar: if (value->isString()) { QString str = value->stringValue()->toQString(); *reinterpret_cast(data) = str.isEmpty() ? QChar() : str.at(0); } else { *reinterpret_cast(data) = QChar(ushort(value->toUInt16())); } return true; case QMetaType::QDateTime: if (QV4::DateObject *d = value->asDateObject()) { *reinterpret_cast(data) = d->toQDateTime(); return true; } break; case QMetaType::QDate: if (QV4::DateObject *d = value->asDateObject()) { *reinterpret_cast(data) = d->toQDateTime().date(); return true; } break; case QMetaType::QRegExp: if (QV4::RegExpObject *r = value->as()) { *reinterpret_cast(data) = r->toQRegExp(); return true; } break; case QMetaType::QObjectStar: { QV4::QObjectWrapper *qobjectWrapper = value->as(); if (qobjectWrapper || value->isNull()) { *reinterpret_cast(data) = qtObjectFromJS(value); return true; } break; } case QMetaType::QStringList: { QV4::ScopedArrayObject a(scope, value); if (a) { *reinterpret_cast(data) = a->toQStringList(); return true; } break; } case QMetaType::QVariantList: { QV4::ScopedArrayObject a(scope, value); if (a) { *reinterpret_cast(data) = variantListFromJS(a); return true; } break; } case QMetaType::QVariantMap: { QV4::ScopedObject o(scope, value); if (o) { *reinterpret_cast(data) = variantMapFromJS(o); return true; } break; } case QMetaType::QVariant: *reinterpret_cast(data) = variantFromJS(value); return true; case QMetaType::QJsonValue: *reinterpret_cast(data) = QV4::JsonObject::toJsonValue(value); return true; case QMetaType::QJsonObject: { QV4::ScopedObject o(scope, value); *reinterpret_cast(data) = QV4::JsonObject::toJsonObject(o); return true; } case QMetaType::QJsonArray: { QV4::ScopedArrayObject a(scope, value); if (a) { *reinterpret_cast(data) = QV4::JsonObject::toJsonArray(a); return true; } break; } 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(type)) { QVariant vv = var; vv.convert(type); Q_ASSERT(vv.userType() == type); QMetaType::constructInPlace(type, data, vv.constData()); return true; } } #endif // Try to use magic; for compatibility with qscriptvalue_cast. QByteArray name = QMetaType::typeName(type); if (convertToNativeQObject(value, name, reinterpret_cast(data))) return true; if (value->as() && name.endsWith('*')) { int valueType = QMetaType::type(name.left(name.size()-1)); QVariant &var = value->as()->data; if (valueType == var.userType()) { // We have T t, T* is requested, so return &t. *reinterpret_cast(data) = var.data(); return true; } else if (value->isObject()) { // Look in the prototype chain. QV4::ScopedObject proto(scope, value->objectValue()->prototype()); while (proto) { bool canCast = false; if (QV4::VariantObject *vo = proto->as()) { const QVariant &v = vo->data; canCast = (type == v.userType()) || (valueType && (valueType == v.userType())); } else if (proto->as()) { QByteArray className = name.left(name.size()-1); QV4::ScopedObject p(scope, proto); if (QObject *qobject = qtObjectFromJS(p)) canCast = qobject->qt_metacast(className) != 0; } if (canCast) { QByteArray varTypeName = QMetaType::typeName(var.userType()); if (varTypeName.endsWith('*')) *reinterpret_cast(data) = *reinterpret_cast(var.data()); else *reinterpret_cast(data) = var.data(); return true; } proto = proto->prototype(); } } } else if (value->isNull() && name.endsWith('*')) { *reinterpret_cast(data) = 0; return true; } else if (type == qMetaTypeId()) { *reinterpret_cast(data) = QJSValuePrivate::get(new QJSValuePrivate(m_v4Engine, value)); return true; } return false; } // Converts a QVariant to JS. QV4::ReturnedValue QV8Engine::variantToJS(const QVariant &value) { return metaTypeToJS(value.userType(), value.constData()); } // Converts a JS value to a QVariant. // Undefined -> QVariant() (invalid) // Null -> QVariant((void*)0) // Boolean -> QVariant(bool) // Number -> QVariant(double) // String -> QVariant(QString) // Array -> QVariantList(...) // Date -> QVariant(QDateTime) // RegExp -> QVariant(QRegExp) // [Any other object] -> QVariantMap(...) QVariant QV8Engine::variantFromJS(const QV4::ValueRef value, V8ObjectSet &visitedObjects) { Q_ASSERT(!value->isEmpty()); if (value->isUndefined()) return QVariant(); if (value->isNull()) return QVariant(QMetaType::VoidStar, 0); if (value->isBoolean()) return value->booleanValue(); if (value->isInteger()) return value->integerValue(); if (value->isNumber()) return value->asDouble(); if (value->isString()) return value->stringValue()->toQString(); Q_ASSERT(value->isObject()); QV4::Scope scope(value->engine()); if (value->asArrayObject()) { QV4::ScopedArrayObject a(scope, value); return variantListFromJS(a, visitedObjects); } if (QV4::DateObject *d = value->asDateObject()) return d->toQDateTime(); if (QV4::RegExpObject *re = value->as()) return re->toQRegExp(); if (QV4::VariantObject *v = value->as()) return v->data; if (value->as()) return qVariantFromValue(qtObjectFromJS(value)); if (QV4::QmlValueTypeWrapper *v = value->as()) return v->toVariant(); QV4::ScopedObject o(scope, value); return variantMapFromJS(o, visitedObjects); } bool QV8Engine::convertToNativeQObject(const QV4::ValueRef 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(const QV4::ValueRef value) { if (!value->isObject()) return 0; QV4::Scope scope(m_v4Engine); QV4::Scoped v(scope, value); if (v) { QVariant variant = v->data; int type = variant.userType(); if (type == QMetaType::QObjectStar) return *reinterpret_cast(variant.constData()); } QV4::Scoped wrapper(scope, value); if (!wrapper) return 0; return wrapper->object(); } void QV8Engine::startTimer(const QString &timerName) { if (!m_time.isValid()) m_time.start(); m_startedTimers[timerName] = m_time.elapsed(); } qint64 QV8Engine::stopTimer(const QString &timerName, bool *wasRunning) { if (!m_startedTimers.contains(timerName)) { *wasRunning = false; return 0; } *wasRunning = true; qint64 startedAt = m_startedTimers.take(timerName); return m_time.elapsed() - startedAt; } int QV8Engine::consoleCountHelper(const QString &file, quint16 line, quint16 column) { const QString key = file + QString::number(line) + QString::number(column); int number = m_consoleCount.value(key, 0); number++; m_consoleCount.insert(key, number); return number; } QV4::ReturnedValue QV8Engine::toString(const QString &string) { return QV4::Encode(m_v4Engine->newString(string)); } QT_END_NAMESPACE