diff options
Diffstat (limited to 'src/declarative/qml/v8/qv8engine.cpp')
-rw-r--r-- | src/declarative/qml/v8/qv8engine.cpp | 1598 |
1 files changed, 0 insertions, 1598 deletions
diff --git a/src/declarative/qml/v8/qv8engine.cpp b/src/declarative/qml/v8/qv8engine.cpp deleted file mode 100644 index 04589fe244..0000000000 --- a/src/declarative/qml/v8/qv8engine.cpp +++ /dev/null @@ -1,1598 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/ -** -** This file is part of the QtDeclarative module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** This file may be used under the terms of the GNU Lesser General Public -** License version 2.1 as published by the Free Software Foundation and -** appearing in the file LICENSE.LGPL included in the packaging of this -** file. Please review the following information to ensure the GNU Lesser -** General Public License version 2.1 requirements will be met: -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU General -** Public License version 3.0 as published by the Free Software Foundation -** and appearing in the file LICENSE.GPL included in the packaging of this -** file. Please review the following information to ensure the GNU General -** Public License version 3.0 requirements will be met: -** http://www.gnu.org/copyleft/gpl.html. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qv8engine_p.h" - -#include <QtGui/QGuiApplication> - -#include "qv8contextwrapper_p.h" -#include "qv8valuetypewrapper_p.h" -#include "qv8sequencewrapper_p.h" -#include "qv8include_p.h" -#include "qjsengine_p.h" -#include "../../../3rdparty/javascriptcore/DateMath.h" - -#include <private/qdeclarativebuiltinfunctions_p.h> -#include <private/qdeclarativelist_p.h> -#include <private/qdeclarativeengine_p.h> -#include <private/qdeclarativeapplication_p.h> -#include <private/qdeclarativexmlhttprequest_p.h> -#include <private/qdeclarativelocale_p.h> - -#include "qscript_impl_p.h" -#include "qv8domerrors_p.h" -#include "qv8sqlerrors_p.h" - - -Q_DECLARE_METATYPE(QJSValue) -Q_DECLARE_METATYPE(QList<int>) - - -// XXX TODO: Need to check all the global functions will also work in a worker script where the -// QDeclarativeEngine is not available -QT_BEGIN_NAMESPACE - -static bool ObjectComparisonCallback(v8::Local<v8::Object> lhs, v8::Local<v8::Object> rhs) -{ - if (lhs == rhs) - return true; - - QV8ObjectResource *lhsr = static_cast<QV8ObjectResource*>(lhs->GetExternalResource()); - QV8ObjectResource *rhsr = static_cast<QV8ObjectResource*>(rhs->GetExternalResource()); - - Q_ASSERT(lhsr->engine == rhsr->engine); - - if (lhsr && rhsr) { - QV8ObjectResource::ResourceType lhst = lhsr->resourceType(); - QV8ObjectResource::ResourceType rhst = rhsr->resourceType(); - - switch (lhst) { - case QV8ObjectResource::ValueTypeType: - // a value type might be equal to a variant or another value type - if (rhst == QV8ObjectResource::ValueTypeType) { - return lhsr->engine->valueTypeWrapper()->isEqual(lhsr, lhsr->engine->valueTypeWrapper()->toVariant(rhsr)); - } else if (rhst == QV8ObjectResource::VariantType) { - return lhsr->engine->valueTypeWrapper()->isEqual(lhsr, lhsr->engine->variantWrapper()->toVariant(rhsr)); - } - break; - case QV8ObjectResource::VariantType: - // a variant might be equal to a value type or other variant. - if (rhst == QV8ObjectResource::VariantType) { - return lhsr->engine->variantWrapper()->toVariant(lhsr) == - lhsr->engine->variantWrapper()->toVariant(rhsr); - } else if (rhst == QV8ObjectResource::ValueTypeType) { - return rhsr->engine->valueTypeWrapper()->isEqual(rhsr, rhsr->engine->variantWrapper()->toVariant(lhsr)); - } - break; - case QV8ObjectResource::SequenceType: - // a sequence might be equal to itself. - if (rhst == QV8ObjectResource::SequenceType) { - return lhsr->engine->sequenceWrapper()->isEqual(lhsr, rhsr); - } - break; - default: - break; - } - } - - return false; -} - - -QV8Engine::QV8Engine(QJSEngine* qq, QJSEngine::ContextOwnership ownership) - : q(qq) - , m_engine(0) - , m_ownsV8Context(ownership == QJSEngine::CreateNewContext) - , m_xmlHttpRequestData(0) - , m_listModelData(0) -{ - qMetaTypeId<QJSValue>(); - qMetaTypeId<QList<int> >(); - - QByteArray v8args = qgetenv("V8ARGS"); - // change default v8 behaviour to not relocate breakpoints across lines - if (!v8args.contains("breakpoint_relocation")) - v8args.append(" --nobreakpoint_relocation"); - v8::V8::SetFlagsFromString(v8args.constData(), v8args.length()); - - ensurePerThreadIsolate(); - - v8::HandleScope handle_scope; - m_context = (ownership == QJSEngine::CreateNewContext) ? v8::Context::New() : v8::Persistent<v8::Context>::New(v8::Context::GetCurrent()); - qPersistentRegister(m_context); - m_originalGlobalObject.init(m_context); - v8::Context::Scope context_scope(m_context); - - v8::V8::SetUserObjectComparisonCallbackFunction(ObjectComparisonCallback); - QV8GCCallback::registerGcPrologueCallback(); - m_strongReferencer = qPersistentNew(v8::Object::New()); - - m_stringWrapper.init(); - m_contextWrapper.init(this); - m_qobjectWrapper.init(this); - m_typeWrapper.init(this); - m_listWrapper.init(this); - m_variantWrapper.init(this); - m_valueTypeWrapper.init(this); - m_sequenceWrapper.init(this); - - { - v8::Handle<v8::Value> v = global()->Get(v8::String::New("Object"))->ToObject()->Get(v8::String::New("getOwnPropertyNames")); - m_getOwnPropertyNames = qPersistentNew<v8::Function>(v8::Handle<v8::Function>::Cast(v)); - } -} - -QV8Engine::~QV8Engine() -{ - Q_ASSERT_X(v8::Isolate::GetCurrent(), "QV8Engine::~QV8Engine()", "called after v8::Isolate has exited"); - for (int ii = 0; ii < m_extensionData.count(); ++ii) - delete m_extensionData[ii]; - m_extensionData.clear(); - - qt_rem_qmlxmlhttprequest(this, m_xmlHttpRequestData); - m_xmlHttpRequestData = 0; - delete m_listModelData; - m_listModelData = 0; - - qPersistentDispose(m_freezeObject); - qPersistentDispose(m_getOwnPropertyNames); - - invalidateAllValues(); - clearExceptions(); - - qPersistentDispose(m_strongReferencer); - - m_sequenceWrapper.destroy(); - m_valueTypeWrapper.destroy(); - m_variantWrapper.destroy(); - m_listWrapper.destroy(); - m_typeWrapper.destroy(); - m_qobjectWrapper.destroy(); - m_contextWrapper.destroy(); - m_stringWrapper.destroy(); - - m_originalGlobalObject.destroy(); - - if (m_ownsV8Context) - qPersistentDispose(m_context); -} - -QString QV8Engine::toStringStatic(v8::Handle<v8::Value> jsstr) -{ - return toStringStatic(jsstr->ToString()); -} - -QString QV8Engine::toStringStatic(v8::Handle<v8::String> jsstr) -{ - QString qstr; - qstr.resize(jsstr->Length()); - jsstr->Write((uint16_t*)qstr.data()); - return qstr; -} - -QVariant QV8Engine::toVariant(v8::Handle<v8::Value> value, int typeHint) -{ - if (value.IsEmpty()) - return QVariant(); - - if (typeHint == QVariant::Bool) - return QVariant(value->BooleanValue()); - - if (value->IsObject()) { - QV8ObjectResource *r = (QV8ObjectResource *)value->ToObject()->GetExternalResource(); - if (r) { - switch (r->resourceType()) { - case QV8ObjectResource::Context2DStyleType: - case QV8ObjectResource::Context2DPixelArrayType: - case QV8ObjectResource::SignalHandlerType: - case QV8ObjectResource::IncubatorType: - case QV8ObjectResource::VisualDataItemType: - case QV8ObjectResource::ContextType: - case QV8ObjectResource::XMLHttpRequestType: - case QV8ObjectResource::DOMNodeType: - case QV8ObjectResource::SQLDatabaseType: - case QV8ObjectResource::ListModelType: - case QV8ObjectResource::Context2DType: - case QV8ObjectResource::ParticleDataType: - case QV8ObjectResource::LocaleDataType: - return QVariant(); - case QV8ObjectResource::TypeType: - return m_typeWrapper.toVariant(r); - case QV8ObjectResource::QObjectType: - return qVariantFromValue<QObject *>(m_qobjectWrapper.toQObject(r)); - case QV8ObjectResource::ListType: - return m_listWrapper.toVariant(r); - case QV8ObjectResource::VariantType: - return m_variantWrapper.toVariant(r); - case QV8ObjectResource::ValueTypeType: - return m_valueTypeWrapper.toVariant(r); - case QV8ObjectResource::SequenceType: - return m_sequenceWrapper.toVariant(r); - } - } - } - - if (value->IsArray()) { - v8::Handle<v8::Array> array = v8::Handle<v8::Array>::Cast(value); - if (typeHint == qMetaTypeId<QList<QObject *> >()) { - QList<QObject *> list; - uint32_t length = array->Length(); - for (uint32_t ii = 0; ii < length; ++ii) { - v8::Local<v8::Value> arrayItem = array->Get(ii); - if (arrayItem->IsObject()) { - list << toQObject(arrayItem->ToObject()); - } else { - list << 0; - } - } - - return qVariantFromValue<QList<QObject*> >(list); - } - - bool succeeded = false; - QVariant retn = m_sequenceWrapper.toVariant(array, typeHint, &succeeded); - if (succeeded) - return retn; - } - - return toBasicVariant(value); -} - -static v8::Handle<v8::Array> arrayFromStringList(QV8Engine *engine, const QStringList &list) -{ - v8::Context::Scope scope(engine->context()); - v8::Local<v8::Array> result = v8::Array::New(list.count()); - for (int ii = 0; ii < list.count(); ++ii) - result->Set(ii, engine->toString(list.at(ii))); - return result; -} - -static v8::Handle<v8::Array> arrayFromVariantList(QV8Engine *engine, const QVariantList &list) -{ - v8::Context::Scope scope(engine->context()); - v8::Local<v8::Array> result = v8::Array::New(list.count()); - for (int ii = 0; ii < list.count(); ++ii) - result->Set(ii, engine->fromVariant(list.at(ii))); - return result; -} - -static v8::Handle<v8::Object> objectFromVariantMap(QV8Engine *engine, const QVariantMap &map) -{ - v8::Context::Scope scope(engine->context()); - v8::Local<v8::Object> object = v8::Object::New(); - for (QVariantMap::ConstIterator iter = map.begin(); iter != map.end(); ++iter) - object->Set(engine->toString(iter.key()), engine->fromVariant(iter.value())); - return object; -} - -Q_CORE_EXPORT QString qt_regexp_toCanonical(const QString &, QRegExp::PatternSyntax); - -v8::Handle<v8::Value> QV8Engine::fromVariant(const QVariant &variant) -{ - int type = variant.userType(); - const void *ptr = variant.constData(); - - if (type < QMetaType::User) { - switch (QMetaType::Type(type)) { - case QMetaType::Void: - return v8::Undefined(); - case QMetaType::Bool: - return v8::Boolean::New(*reinterpret_cast<const bool*>(ptr)); - case QMetaType::Int: - return v8::Integer::New(*reinterpret_cast<const int*>(ptr)); - case QMetaType::UInt: - return v8::Integer::NewFromUnsigned(*reinterpret_cast<const uint*>(ptr)); - case QMetaType::LongLong: - return v8::Number::New(*reinterpret_cast<const qlonglong*>(ptr)); - case QMetaType::ULongLong: - return v8::Number::New(*reinterpret_cast<const qulonglong*>(ptr)); - case QMetaType::Double: - return v8::Number::New(*reinterpret_cast<const double*>(ptr)); - case QMetaType::QString: - return m_stringWrapper.toString(*reinterpret_cast<const QString*>(ptr)); - case QMetaType::Float: - return v8::Number::New(*reinterpret_cast<const float*>(ptr)); - case QMetaType::Short: - return v8::Integer::New(*reinterpret_cast<const short*>(ptr)); - case QMetaType::UShort: - return v8::Integer::NewFromUnsigned(*reinterpret_cast<const unsigned short*>(ptr)); - case QMetaType::Char: - return v8::Integer::New(*reinterpret_cast<const char*>(ptr)); - case QMetaType::UChar: - return v8::Integer::NewFromUnsigned(*reinterpret_cast<const unsigned char*>(ptr)); - case QMetaType::QChar: - return v8::Integer::New((*reinterpret_cast<const QChar*>(ptr)).unicode()); - case QMetaType::QDateTime: - return v8::Date::New(qtDateTimeToJsDate(*reinterpret_cast<const QDateTime *>(ptr))); - case QMetaType::QDate: - return v8::Date::New(qtDateTimeToJsDate(QDateTime(*reinterpret_cast<const QDate *>(ptr)))); - case QMetaType::QTime: - return v8::Date::New(qtDateTimeToJsDate(QDateTime(QDate(1970,1,1), *reinterpret_cast<const QTime *>(ptr)))); - case QMetaType::QRegExp: - return QJSConverter::toRegExp(*reinterpret_cast<const QRegExp *>(ptr)); - case QMetaType::QObjectStar: - case QMetaType::QWidgetStar: - return newQObject(*reinterpret_cast<QObject* const *>(ptr)); - case QMetaType::QStringList: - { - bool succeeded = false; - v8::Handle<v8::Value> retn = m_sequenceWrapper.fromVariant(variant, &succeeded); - if (succeeded) - return retn; - return arrayFromStringList(this, *reinterpret_cast<const QStringList *>(ptr)); - } - case QMetaType::QVariantList: - return arrayFromVariantList(this, *reinterpret_cast<const QVariantList *>(ptr)); - case QMetaType::QVariantMap: - return objectFromVariantMap(this, *reinterpret_cast<const QVariantMap *>(ptr)); - - default: - break; - } - - if (m_engine) { - if (QDeclarativeValueType *vt = QDeclarativeEnginePrivate::get(m_engine)->valueTypes[type]) - return m_valueTypeWrapper.newValueType(variant, vt); - } - - } else { - if (type == qMetaTypeId<QDeclarativeListReference>()) { - typedef QDeclarativeListReferencePrivate QDLRP; - QDLRP *p = QDLRP::get((QDeclarativeListReference*)ptr); - if (p->object) { - return m_listWrapper.newList(p->property, p->propertyType); - } else { - return v8::Null(); - } - } else if (type == qMetaTypeId<QJSValue>()) { - const QJSValue *value = reinterpret_cast<const QJSValue *>(ptr); - QJSValuePrivate *valuep = QJSValuePrivate::get(*value); - if (valuep->assignEngine(this)) - return v8::Local<v8::Value>::New(*valuep); - } else if (type == qMetaTypeId<QList<QObject *> >()) { - // XXX Can this be made more by using Array as a prototype and implementing - // directly against QList<QObject*>? - const QList<QObject *> &list = *(QList<QObject *>*)ptr; - v8::Local<v8::Array> array = v8::Array::New(list.count()); - for (int ii = 0; ii < list.count(); ++ii) - array->Set(ii, newQObject(list.at(ii))); - return array; - } - - bool objOk; - QObject *obj = QDeclarativeMetaType::toQObject(variant, &objOk); - if (objOk) - return newQObject(obj); - - bool succeeded = false; - v8::Handle<v8::Value> retn = m_sequenceWrapper.fromVariant(variant, &succeeded); - if (succeeded) - return retn; - } - - // XXX TODO: To be compatible, we still need to handle: - // + QObjectList - // + QList<int> - - return m_variantWrapper.newVariant(variant); -} - -// A handle scope and context must be entered -v8::Local<v8::Script> QV8Engine::qmlModeCompile(const QString &source, - const QString &fileName, - int lineNumber) -{ - v8::Local<v8::String> v8source = m_stringWrapper.toString(source); - v8::Local<v8::String> v8fileName = m_stringWrapper.toString(fileName); - - v8::ScriptOrigin origin(v8fileName, v8::Integer::New(lineNumber - 1)); - - v8::Local<v8::Script> script = v8::Script::Compile(v8source, &origin, 0, v8::Handle<v8::String>(), - v8::Script::QmlMode); - - return script; -} - -// A handle scope and context must be entered. -// source can be either ascii or utf8. -v8::Local<v8::Script> QV8Engine::qmlModeCompile(const char *source, int sourceLength, - const QString &fileName, - int lineNumber) -{ - if (sourceLength == -1) - sourceLength = strlen(source); - - v8::Local<v8::String> v8source = v8::String::New(source, sourceLength); - v8::Local<v8::String> v8fileName = m_stringWrapper.toString(fileName); - - v8::ScriptOrigin origin(v8fileName, v8::Integer::New(lineNumber - 1)); - - v8::Local<v8::Script> script = v8::Script::Compile(v8source, &origin, 0, v8::Handle<v8::String>(), - v8::Script::QmlMode); - - return script; -} - -QNetworkAccessManager *QV8Engine::networkAccessManager() -{ - return QDeclarativeEnginePrivate::get(m_engine)->getNetworkAccessManager(); -} - -const QStringHash<bool> &QV8Engine::illegalNames() const -{ - return m_illegalNames; -} - -// Requires a handle scope -v8::Local<v8::Array> QV8Engine::getOwnPropertyNames(v8::Handle<v8::Object> o) -{ - // FIXME Newer v8 have API for this function - v8::TryCatch tc; - v8::Handle<v8::Value> args[] = { o }; - v8::Local<v8::Value> r = m_getOwnPropertyNames->Call(global(), 1, args); - if (tc.HasCaught()) - return v8::Array::New(); - else - return v8::Local<v8::Array>::Cast(r); -} - -QDeclarativeContextData *QV8Engine::callingContext() -{ - return m_contextWrapper.callingContext(); -} - -// Converts a JS value to a QVariant. -// Null, Undefined -> QVariant() (invalid) -// Boolean -> QVariant(bool) -// Number -> QVariant(double) -// String -> QVariant(QString) -// Array -> QVariantList(...) -// Date -> QVariant(QDateTime) -// RegExp -> QVariant(QRegExp) -// [Any other object] -> QVariantMap(...) -QVariant QV8Engine::toBasicVariant(v8::Handle<v8::Value> value) -{ - if (value->IsNull() || value->IsUndefined()) - return QVariant(); - if (value->IsBoolean()) - return value->ToBoolean()->Value(); - if (value->IsInt32()) - return value->ToInt32()->Value(); - if (value->IsNumber()) - return value->ToNumber()->Value(); - if (value->IsString()) - return m_stringWrapper.toString(value->ToString()); - if (value->IsDate()) - return qtDateTimeFromJsDate(v8::Handle<v8::Date>::Cast(value)->NumberValue()); - // NOTE: since we convert QTime to JS Date, round trip will change the variant type (to QDateTime)! - - Q_ASSERT(value->IsObject()); - - if (value->IsRegExp()) { - v8::Context::Scope scope(context()); - return QJSConverter::toRegExp(v8::Handle<v8::RegExp>::Cast(value)); - } - if (value->IsArray()) { - v8::Context::Scope scope(context()); - QVariantList rv; - - v8::Handle<v8::Array> array = v8::Handle<v8::Array>::Cast(value); - int length = array->Length(); - for (int ii = 0; ii < length; ++ii) - rv << toVariant(array->Get(ii), -1); - return rv; - } - if (!value->IsFunction()) { - v8::Context::Scope scope(context()); - v8::Handle<v8::Object> object = value->ToObject(); - return variantMapFromJS(object); - } - - return QVariant(); -} - - - -#include <QtGui/qvector3d.h> -#include <QtGui/qvector4d.h> - -struct StaticQtMetaObject : public QObject -{ - static const QMetaObject *get() - { return &static_cast<StaticQtMetaObject*> (0)->staticQtMetaObject; } -}; - -void QV8Engine::initializeGlobal(v8::Handle<v8::Object> global) -{ - using namespace QDeclarativeBuiltinFunctions; - - v8::Local<v8::Object> console = v8::Object::New(); - v8::Local<v8::Function> consoleLogFn = V8FUNCTION(consoleLog, this); - - console->Set(v8::String::New("debug"), consoleLogFn); - console->Set(v8::String::New("log"), consoleLogFn); - console->Set(v8::String::New("info"), consoleLogFn); - console->Set(v8::String::New("warn"), V8FUNCTION(consoleWarn, this)); - console->Set(v8::String::New("error"), V8FUNCTION(consoleError, this)); - console->Set(v8::String::New("assert"), V8FUNCTION(consoleAssert, this)); - - console->Set(v8::String::New("count"), V8FUNCTION(consoleCount, this)); - console->Set(v8::String::New("profile"), V8FUNCTION(consoleProfile, this)); - console->Set(v8::String::New("profileEnd"), V8FUNCTION(consoleProfileEnd, this)); - console->Set(v8::String::New("time"), V8FUNCTION(consoleTime, this)); - console->Set(v8::String::New("timeEnd"), V8FUNCTION(consoleTimeEnd, this)); - console->Set(v8::String::New("trace"), V8FUNCTION(consoleTrace, this)); - console->Set(v8::String::New("exception"), V8FUNCTION(consoleException, this)); - - v8::Local<v8::Object> qt = v8::Object::New(); - - // Set all the enums from the "Qt" namespace - const QMetaObject *qtMetaObject = StaticQtMetaObject::get(); - for (int ii = 0; ii < qtMetaObject->enumeratorCount(); ++ii) { - QMetaEnum enumerator = qtMetaObject->enumerator(ii); - for (int jj = 0; jj < enumerator.keyCount(); ++jj) { - qt->Set(v8::String::New(enumerator.key(jj)), v8::Integer::New(enumerator.value(jj))); - } - } - qt->Set(v8::String::New("Asynchronous"), v8::Integer::New(0)); - qt->Set(v8::String::New("Synchronous"), v8::Integer::New(1)); - - qt->Set(v8::String::New("include"), V8FUNCTION(QV8Include::include, this)); - qt->Set(v8::String::New("isQtObject"), V8FUNCTION(isQtObject, this)); - qt->Set(v8::String::New("rgba"), V8FUNCTION(rgba, this)); - qt->Set(v8::String::New("hsla"), V8FUNCTION(hsla, this)); - qt->Set(v8::String::New("rect"), V8FUNCTION(rect, this)); - qt->Set(v8::String::New("point"), V8FUNCTION(point, this)); - qt->Set(v8::String::New("size"), V8FUNCTION(size, this)); - qt->Set(v8::String::New("vector3d"), V8FUNCTION(vector3d, this)); - qt->Set(v8::String::New("vector4d"), V8FUNCTION(vector4d, this)); - - qt->Set(v8::String::New("formatDate"), V8FUNCTION(formatDate, this)); - qt->Set(v8::String::New("formatTime"), V8FUNCTION(formatTime, this)); - qt->Set(v8::String::New("formatDateTime"), V8FUNCTION(formatDateTime, this)); - - qt->Set(v8::String::New("openUrlExternally"), V8FUNCTION(openUrlExternally, this)); - qt->Set(v8::String::New("fontFamilies"), V8FUNCTION(fontFamilies, this)); - qt->Set(v8::String::New("md5"), V8FUNCTION(md5, this)); - qt->Set(v8::String::New("btoa"), V8FUNCTION(btoa, this)); - qt->Set(v8::String::New("atob"), V8FUNCTION(atob, this)); - qt->Set(v8::String::New("resolvedUrl"), V8FUNCTION(resolvedUrl, this)); - qt->Set(v8::String::New("locale"), V8FUNCTION(locale, this)); - - if (m_engine) { - qt->Set(v8::String::New("application"), newQObject(new QDeclarativeApplication(m_engine))); - qt->Set(v8::String::New("inputMethod"), newQObject(qGuiApp->inputMethod(), CppOwnership)); - qt->Set(v8::String::New("lighter"), V8FUNCTION(lighter, this)); - qt->Set(v8::String::New("darker"), V8FUNCTION(darker, this)); - qt->Set(v8::String::New("tint"), V8FUNCTION(tint, this)); - qt->Set(v8::String::New("quit"), V8FUNCTION(quit, this)); - qt->Set(v8::String::New("createQmlObject"), V8FUNCTION(createQmlObject, this)); - qt->Set(v8::String::New("createComponent"), V8FUNCTION(createComponent, this)); - } - - global->Set(v8::String::New("qsTranslate"), V8FUNCTION(qsTranslate, this)); - global->Set(v8::String::New("QT_TRANSLATE_NOOP"), V8FUNCTION(qsTranslateNoOp, this)); - global->Set(v8::String::New("qsTr"), V8FUNCTION(qsTr, this)); - global->Set(v8::String::New("QT_TR_NOOP"), V8FUNCTION(qsTrNoOp, this)); - global->Set(v8::String::New("qsTrId"), V8FUNCTION(qsTrId, this)); - global->Set(v8::String::New("QT_TRID_NOOP"), V8FUNCTION(qsTrIdNoOp, this)); - - global->Set(v8::String::New("print"), consoleLogFn); - global->Set(v8::String::New("console"), console); - global->Set(v8::String::New("Qt"), qt); - global->Set(v8::String::New("gc"), V8FUNCTION(QDeclarativeBuiltinFunctions::gc, this)); - - { -#define STRING_ARG "(function(stringArg) { "\ - " String.prototype.arg = (function() {"\ - " return stringArg.apply(this, arguments);"\ - " })"\ - "})" - - v8::Local<v8::Script> registerArg = v8::Script::New(v8::String::New(STRING_ARG), 0, 0, v8::Handle<v8::String>(), v8::Script::NativeMode); - v8::Local<v8::Value> result = registerArg->Run(); - Q_ASSERT(result->IsFunction()); - v8::Local<v8::Function> registerArgFunc = v8::Local<v8::Function>::Cast(result); - v8::Handle<v8::Value> args = V8FUNCTION(stringArg, this); - registerArgFunc->Call(v8::Local<v8::Object>::Cast(registerArgFunc), 1, &args); -#undef STRING_ARG - } - - QDeclarativeLocale::registerStringLocaleCompare(this); - QDeclarativeDateExtension::registerExtension(this); - QDeclarativeNumberExtension::registerExtension(this); - - qt_add_domexceptions(this); - m_xmlHttpRequestData = qt_add_qmlxmlhttprequest(this); - - qt_add_sqlexceptions(this); - - { - v8::Handle<v8::Value> args[] = { global }; - v8::Local<v8::Value> names = m_getOwnPropertyNames->Call(global, 1, args); - v8::Local<v8::Array> namesArray = v8::Local<v8::Array>::Cast(names); - for (quint32 ii = 0; ii < namesArray->Length(); ++ii) - m_illegalNames.insert(toString(namesArray->Get(ii)), true); - } - - { -#define FREEZE_SOURCE "(function freeze_recur(obj) { "\ - " if (Qt.isQtObject(obj)) return;"\ - " if (obj != Function.connect && obj != Function.disconnect && "\ - " obj instanceof Object) {"\ - " var properties = Object.getOwnPropertyNames(obj);"\ - " for (var prop in properties) { "\ - " if (prop == \"connect\" || prop == \"disconnect\") {"\ - " Object.freeze(obj[prop]); "\ - " continue;"\ - " }"\ - " freeze_recur(obj[prop]);"\ - " }"\ - " }"\ - " if (obj instanceof Object) {"\ - " Object.freeze(obj);"\ - " }"\ - "})" - - v8::Local<v8::Script> freeze = v8::Script::New(v8::String::New(FREEZE_SOURCE)); - v8::Local<v8::Value> result = freeze->Run(); - Q_ASSERT(result->IsFunction()); - m_freezeObject = qPersistentNew(v8::Local<v8::Function>::Cast(result)); -#undef FREEZE_SOURCE - } -} - -void QV8Engine::freezeObject(v8::Handle<v8::Value> value) -{ - v8::Handle<v8::Value> args[] = { value }; - m_freezeObject->Call(global(), 1, args); -} - -void QV8Engine::gc() -{ - v8::V8::LowMemoryNotification(); - while (!v8::V8::IdleNotification()) {} -} - -#ifdef QML_GLOBAL_HANDLE_DEBUGGING -#include <QtCore/qthreadstorage.h> -static QThreadStorage<QSet<void *> *> QV8Engine_activeHandles; - -void QV8Engine::registerHandle(void *handle) -{ - if (!handle) { - qWarning("Attempting to register a null handle"); - return; - } - - if (!QV8Engine_activeHandles.hasLocalData()) - QV8Engine_activeHandles.setLocalData(new QSet<void *>); - - if (QV8Engine_activeHandles.localData()->contains(handle)) { - qFatal("Handle %p already alive", handle); - } else { - QV8Engine_activeHandles.localData()->insert(handle); - } -} - -void QV8Engine::releaseHandle(void *handle) -{ - if (!handle) - return; - - if (!QV8Engine_activeHandles.hasLocalData()) - QV8Engine_activeHandles.setLocalData(new QSet<void *>); - - if (QV8Engine_activeHandles.localData()->contains(handle)) { - QV8Engine_activeHandles.localData()->remove(handle); - } else { - qFatal("Handle %p already dead", handle); - } -} -#endif - -struct QV8EngineRegistrationData -{ - QV8EngineRegistrationData() : extensionCount(0) {} - - QMutex mutex; - int extensionCount; -}; -Q_GLOBAL_STATIC(QV8EngineRegistrationData, registrationData); - -QMutex *QV8Engine::registrationMutex() -{ - return ®istrationData()->mutex; -} - -int QV8Engine::registerExtension() -{ - return registrationData()->extensionCount++; -} - -void QV8Engine::setExtensionData(int index, Deletable *data) -{ - if (m_extensionData.count() <= index) - m_extensionData.resize(index + 1); - - if (m_extensionData.at(index)) - delete m_extensionData.at(index); - - m_extensionData[index] = data; -} - -double QV8Engine::qtDateTimeToJsDate(const QDateTime &dt) -{ - // from QScriptEngine::DateTimeToMs() - if (!dt.isValid()) { - return qSNaN(); - } - QDateTime utc = dt.toUTC(); - QDate date = utc.date(); - QTime time = utc.time(); - QV8DateConverter::JSC::GregorianDateTime tm; - tm.year = date.year() - 1900; - tm.month = date.month() - 1; - tm.monthDay = date.day(); - tm.weekDay = date.dayOfWeek(); - tm.yearDay = date.dayOfYear(); - tm.hour = time.hour(); - tm.minute = time.minute(); - tm.second = time.second(); - return QV8DateConverter::JSC::gregorianDateTimeToMS(tm, time.msec()); -} - -v8::Persistent<v8::Object> *QV8Engine::findOwnerAndStrength(QObject *object, bool *shouldBeStrong) -{ - QObject *parent = object->parent(); - if (!parent) { - // if the object has JS ownership, the object's v8object owns the lifetime of the persistent value. - if (QDeclarativeEngine::objectOwnership(object) == QDeclarativeEngine::JavaScriptOwnership) { - *shouldBeStrong = false; - return &(QDeclarativeData::get(object)->v8object); - } - - // no parent, and has CPP ownership - doesn't have an implicit parent. - *shouldBeStrong = true; - return 0; - } - - // if it is owned by CPP, it's root parent may still be owned by JS. - // in that case, the owner of the persistent handle is the root parent's v8object. - while (parent->parent()) - parent = parent->parent(); - - if (QDeclarativeEngine::objectOwnership(parent) == QDeclarativeEngine::JavaScriptOwnership) { - // root parent is owned by JS. It's v8object owns the persistent value in question. - *shouldBeStrong = false; - return &(QDeclarativeData::get(parent)->v8object); - } else { - // root parent has CPP ownership. The persistent value should not be made weak. - *shouldBeStrong = true; - return 0; - } -} - -QDateTime QV8Engine::qtDateTimeFromJsDate(double jsDate) -{ - // from QScriptEngine::MsToDateTime() - if (qIsNaN(jsDate)) - return QDateTime(); - QV8DateConverter::JSC::GregorianDateTime tm; - QV8DateConverter::JSC::msToGregorianDateTime(jsDate, tm); - - // from QScriptEngine::MsFromTime() - int ms = int(::fmod(jsDate, 1000.0)); - if (ms < 0) - ms += int(1000.0); - - QDateTime convertedUTC = QDateTime(QDate(tm.year + 1900, tm.month + 1, tm.monthDay), - QTime(tm.hour, tm.minute, tm.second, ms), Qt::UTC); - return convertedUTC.toLocalTime(); -} - -void QV8Engine::addRelationshipForGC(QObject *object, v8::Persistent<v8::Value> handle) -{ - if (handle.IsEmpty()) - return; - - bool handleShouldBeStrong = false; - v8::Persistent<v8::Object> *implicitOwner = findOwnerAndStrength(object, &handleShouldBeStrong); - if (handleShouldBeStrong) { - v8::V8::AddImplicitReferences(m_strongReferencer, &handle, 1); - } else if (!implicitOwner->IsEmpty()) { - v8::V8::AddImplicitReferences(*implicitOwner, &handle, 1); - } -} - -void QV8Engine::addRelationshipForGC(QObject *object, QObject *other) -{ - bool handleShouldBeStrong = false; - v8::Persistent<v8::Object> *implicitOwner = findOwnerAndStrength(object, &handleShouldBeStrong); - v8::Persistent<v8::Value> handle = QDeclarativeData::get(other, true)->v8object; - if (handleShouldBeStrong) { - v8::V8::AddImplicitReferences(m_strongReferencer, &handle, 1); - } else if (!implicitOwner->IsEmpty()) { - v8::V8::AddImplicitReferences(*implicitOwner, &handle, 1); - } -} - -static QThreadStorage<QV8Engine::ThreadData*> perThreadEngineData; - -bool QV8Engine::hasThreadData() -{ - return perThreadEngineData.hasLocalData(); -} - -QV8Engine::ThreadData *QV8Engine::threadData() -{ - Q_ASSERT(perThreadEngineData.hasLocalData()); - return perThreadEngineData.localData(); -} - -void QV8Engine::ensurePerThreadIsolate() -{ - if (!perThreadEngineData.hasLocalData()) - perThreadEngineData.setLocalData(new ThreadData); -} - -void QV8Engine::initDeclarativeGlobalObject() -{ - v8::HandleScope handels; - v8::Context::Scope contextScope(m_context); - initializeGlobal(m_context->Global()); - freezeObject(m_context->Global()); -} - -void QV8Engine::setEngine(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; -} - - -// Converts a QVariantList to JS. -// The result is a new Array object with length equal to the length -// of the QVariantList, and the elements being the QVariantList's -// elements converted to JS, recursively. -v8::Local<v8::Array> QV8Engine::variantListToJS(const QVariantList &lst) -{ - v8::Local<v8::Array> result = v8::Array::New(lst.size()); - for (int i = 0; i < lst.size(); ++i) - result->Set(i, variantToJS(lst.at(i))); - return result; -} - -// Converts a JS Array object to a QVariantList. -// The result is a QVariantList with length equal to the length -// of the JS Array, and elements being the JS Array's elements -// converted to QVariants, recursively. -QVariantList QV8Engine::variantListFromJS(v8::Handle<v8::Array> jsArray) -{ - QVariantList result; - int hash = jsArray->GetIdentityHash(); - if (visitedConversionObjects.contains(hash)) - return result; // Avoid recursion. - v8::HandleScope handleScope; - visitedConversionObjects.insert(hash); - uint32_t length = jsArray->Length(); - for (uint32_t i = 0; i < length; ++i) - result.append(variantFromJS(jsArray->Get(i))); - visitedConversionObjects.remove(hash); - return result; -} - -// Converts a QVariantMap to JS. -// The result is a new Object object with property names being -// the keys of the QVariantMap, and values being the values of -// the QVariantMap converted to JS, recursively. -v8::Local<v8::Object> QV8Engine::variantMapToJS(const QVariantMap &vmap) -{ - v8::Local<v8::Object> result = v8::Object::New(); - QVariantMap::const_iterator it; - for (it = vmap.constBegin(); it != vmap.constEnd(); ++it) - result->Set(QJSConverter::toString(it.key()), variantToJS(it.value())); - return result; -} - -// Converts a JS Object to a QVariantMap. -// The result is a QVariantMap with keys being the property names -// of the object, and values being the values of the JS object's -// properties converted to QVariants, recursively. -QVariantMap QV8Engine::variantMapFromJS(v8::Handle<v8::Object> jsObject) -{ - QVariantMap result; - - v8::HandleScope handleScope; - v8::Handle<v8::Array> propertyNames = jsObject->GetPropertyNames(); - uint32_t length = propertyNames->Length(); - if (length == 0) - return result; - - int hash = jsObject->GetIdentityHash(); - if (visitedConversionObjects.contains(hash)) - return result; // Avoid recursion. - - visitedConversionObjects.insert(hash); - // TODO: Only object's own property names. Include non-enumerable properties. - for (uint32_t i = 0; i < length; ++i) { - v8::Handle<v8::Value> name = propertyNames->Get(i); - result.insert(QJSConverter::toString(name->ToString()), variantFromJS(jsObject->Get(name))); - } - visitedConversionObjects.remove(hash); - return result; -} - -// Converts the meta-type defined by the given type and data to JS. -// Returns the value if conversion succeeded, an empty handle otherwise. -v8::Handle<v8::Value> QV8Engine::metaTypeToJS(int type, const void *data) -{ - Q_ASSERT(data != 0); - v8::Handle<v8::Value> result; - - // check if it's one of the types we know - switch (QMetaType::Type(type)) { - case QMetaType::Void: - return v8::Undefined(); - case QMetaType::Bool: - return v8::Boolean::New(*reinterpret_cast<const bool*>(data)); - case QMetaType::Int: - return v8::Int32::New(*reinterpret_cast<const int*>(data)); - case QMetaType::UInt: - return v8::Uint32::New(*reinterpret_cast<const uint*>(data)); - case QMetaType::LongLong: - return v8::Number::New(double(*reinterpret_cast<const qlonglong*>(data))); - case QMetaType::ULongLong: -#if defined(Q_OS_WIN) && defined(_MSC_FULL_VER) && _MSC_FULL_VER <= 12008804 -#pragma message("** NOTE: You need the Visual Studio Processor Pack to compile support for 64bit unsigned integers.") - return v8::Number::New(double((qlonglong)*reinterpret_cast<const qulonglong*>(data))); -#elif defined(Q_CC_MSVC) && !defined(Q_CC_MSVC_NET) - return v8::Number::New(double((qlonglong)*reinterpret_cast<const qulonglong*>(data))); -#else - return v8::Number::New(double(*reinterpret_cast<const qulonglong*>(data))); -#endif - case QMetaType::Double: - return v8::Number::New(double(*reinterpret_cast<const double*>(data))); - case QMetaType::QString: - return QJSConverter::toString(*reinterpret_cast<const QString*>(data)); - case QMetaType::Float: - return v8::Number::New(*reinterpret_cast<const float*>(data)); - case QMetaType::Short: - return v8::Int32::New(*reinterpret_cast<const short*>(data)); - case QMetaType::UShort: - return v8::Uint32::New(*reinterpret_cast<const unsigned short*>(data)); - case QMetaType::Char: - return v8::Int32::New(*reinterpret_cast<const char*>(data)); - case QMetaType::UChar: - return v8::Uint32::New(*reinterpret_cast<const unsigned char*>(data)); - case QMetaType::QChar: - return v8::Uint32::New((*reinterpret_cast<const QChar*>(data)).unicode()); - case QMetaType::QStringList: - result = QJSConverter::toStringList(*reinterpret_cast<const QStringList *>(data)); - break; - case QMetaType::QVariantList: - result = variantListToJS(*reinterpret_cast<const QVariantList *>(data)); - break; - case QMetaType::QVariantMap: - result = variantMapToJS(*reinterpret_cast<const QVariantMap *>(data)); - break; - case QMetaType::QDateTime: - result = QJSConverter::toDateTime(*reinterpret_cast<const QDateTime *>(data)); - break; - case QMetaType::QDate: - result = QJSConverter::toDateTime(QDateTime(*reinterpret_cast<const QDate *>(data))); - break; - case QMetaType::QRegExp: - result = QJSConverter::toRegExp(*reinterpret_cast<const QRegExp *>(data)); - break; - case QMetaType::QObjectStar: - case QMetaType::QWidgetStar: - result = newQObject(*reinterpret_cast<QObject* const *>(data)); - break; - case QMetaType::QVariant: - result = variantToJS(*reinterpret_cast<const QVariant*>(data)); - break; - default: - if (type == qMetaTypeId<QJSValue>()) { - return QJSValuePrivate::get(*reinterpret_cast<const QJSValue*>(data))->asV8Value(this); - } else { - QByteArray typeName = QMetaType::typeName(type); - if (typeName.endsWith('*') && !*reinterpret_cast<void* const *>(data)) { - return v8::Null(); - } else { - // Fall back to wrapping in a QVariant. - result = newVariant(QVariant(type, data)); - } - } - } - return result; -} - -// Converts a JS value to a meta-type. -// data must point to a place that can store a value of the given type. -// Returns true if conversion succeeded, false otherwise. -bool QV8Engine::metaTypeFromJS(v8::Handle<v8::Value> value, int type, void *data) { - // check if it's one of the types we know - switch (QMetaType::Type(type)) { - case QMetaType::Bool: - *reinterpret_cast<bool*>(data) = value->ToBoolean()->Value(); - return true; - case QMetaType::Int: - *reinterpret_cast<int*>(data) = value->ToInt32()->Value(); - return true; - case QMetaType::UInt: - *reinterpret_cast<uint*>(data) = value->ToUint32()->Value(); - return true; - case QMetaType::LongLong: - *reinterpret_cast<qlonglong*>(data) = qlonglong(value->ToInteger()->Value()); - return true; - case QMetaType::ULongLong: - *reinterpret_cast<qulonglong*>(data) = qulonglong(value->ToInteger()->Value()); - return true; - case QMetaType::Double: - *reinterpret_cast<double*>(data) = value->ToNumber()->Value(); - return true; - case QMetaType::QString: - if (value->IsUndefined() || value->IsNull()) - *reinterpret_cast<QString*>(data) = QString(); - else - *reinterpret_cast<QString*>(data) = QJSConverter::toString(value->ToString()); - return true; - case QMetaType::Float: - *reinterpret_cast<float*>(data) = value->ToNumber()->Value(); - return true; - case QMetaType::Short: - *reinterpret_cast<short*>(data) = short(value->ToInt32()->Value()); - return true; - case QMetaType::UShort: - *reinterpret_cast<unsigned short*>(data) = ushort(value->ToInt32()->Value()); // ### QScript::ToUInt16() - return true; - case QMetaType::Char: - *reinterpret_cast<char*>(data) = char(value->ToInt32()->Value()); - return true; - case QMetaType::UChar: - *reinterpret_cast<unsigned char*>(data) = (unsigned char)(value->ToInt32()->Value()); - return true; - case QMetaType::QChar: - if (value->IsString()) { - QString str = QJSConverter::toString(v8::Handle<v8::String>::Cast(value)); - *reinterpret_cast<QChar*>(data) = str.isEmpty() ? QChar() : str.at(0); - } else { - *reinterpret_cast<QChar*>(data) = QChar(ushort(value->ToInt32()->Value())); // ### QScript::ToUInt16() - } - return true; - case QMetaType::QDateTime: - if (value->IsDate()) { - *reinterpret_cast<QDateTime *>(data) = QJSConverter::toDateTime(v8::Handle<v8::Date>::Cast(value)); - return true; - } break; - case QMetaType::QDate: - if (value->IsDate()) { - *reinterpret_cast<QDate *>(data) = QJSConverter::toDateTime(v8::Handle<v8::Date>::Cast(value)).date(); - return true; - } break; - case QMetaType::QRegExp: - if (value->IsRegExp()) { - *reinterpret_cast<QRegExp *>(data) = QJSConverter::toRegExp(v8::Handle<v8::RegExp>::Cast(value)); - return true; - } break; - case QMetaType::QObjectStar: - if (isQObject(value) || value->IsNull()) { - *reinterpret_cast<QObject* *>(data) = qtObjectFromJS(value); - return true; - } break; - case QMetaType::QWidgetStar: - if (isQObject(value) || value->IsNull()) { - QObject *qo = qtObjectFromJS(value); - if (!qo || qo->isWidgetType()) { - *reinterpret_cast<QWidget* *>(data) = reinterpret_cast<QWidget*>(qo); - return true; - } - } break; - case QMetaType::QStringList: - if (value->IsArray()) { - *reinterpret_cast<QStringList *>(data) = QJSConverter::toStringList(v8::Handle<v8::Array>::Cast(value)); - return true; - } break; - case QMetaType::QVariantList: - if (value->IsArray()) { - *reinterpret_cast<QVariantList *>(data) = variantListFromJS(v8::Handle<v8::Array>::Cast(value)); - return true; - } break; - case QMetaType::QVariantMap: - if (value->IsObject()) { - *reinterpret_cast<QVariantMap *>(data) = variantMapFromJS(v8::Handle<v8::Object>::Cast(value)); - return true; - } break; - case QMetaType::QVariant: - *reinterpret_cast<QVariant*>(data) = variantFromJS(value); - return true; - default: - ; - } - -#if 0 - if (isQtVariant(value)) { - const QVariant &var = variantValue(value); - // ### Enable once constructInPlace() is in qt master. - if (var.userType() == type) { - QMetaType::constructInPlace(type, data, var.constData()); - return true; - } - if (var.canConvert(QVariant::Type(type))) { - QVariant vv = var; - vv.convert(QVariant::Type(type)); - Q_ASSERT(vv.userType() == type); - QMetaType::constructInPlace(type, data, vv.constData()); - return true; - } - - } -#endif - - // Try to use magic; for compatibility with qscriptvalue_cast. - - QByteArray name = QMetaType::typeName(type); - if (convertToNativeQObject(value, name, reinterpret_cast<void* *>(data))) - return true; - if (isVariant(value) && name.endsWith('*')) { - int valueType = QMetaType::type(name.left(name.size()-1)); - QVariant &var = variantValue(value); - if (valueType == var.userType()) { - // We have T t, T* is requested, so return &t. - *reinterpret_cast<void* *>(data) = var.data(); - return true; - } else { - // Look in the prototype chain. - v8::Handle<v8::Value> proto = value->ToObject()->GetPrototype(); - while (proto->IsObject()) { - bool canCast = false; - if (isVariant(proto)) { - canCast = (type == variantValue(proto).userType()) - || (valueType && (valueType == variantValue(proto).userType())); - } - else if (isQObject(proto)) { - QByteArray className = name.left(name.size()-1); - if (QObject *qobject = qtObjectFromJS(proto)) - canCast = qobject->qt_metacast(className) != 0; - } - if (canCast) { - QByteArray varTypeName = QMetaType::typeName(var.userType()); - if (varTypeName.endsWith('*')) - *reinterpret_cast<void* *>(data) = *reinterpret_cast<void* *>(var.data()); - else - *reinterpret_cast<void* *>(data) = var.data(); - return true; - } - proto = proto->ToObject()->GetPrototype(); - } - } - } else if (value->IsNull() && name.endsWith('*')) { - *reinterpret_cast<void* *>(data) = 0; - return true; - } else if (type == qMetaTypeId<QJSValue>()) { - *reinterpret_cast<QJSValue*>(data) = QJSValuePrivate::get(new QJSValuePrivate(this, value)); - return true; - } - - return false; -} - -// Converts a QVariant to JS. -v8::Handle<v8::Value> QV8Engine::variantToJS(const QVariant &value) -{ - return metaTypeToJS(value.userType(), value.constData()); -} - -// Converts a JS value to a QVariant. -// Null, Undefined -> QVariant() (invalid) -// Boolean -> QVariant(bool) -// Number -> QVariant(double) -// String -> QVariant(QString) -// Array -> QVariantList(...) -// Date -> QVariant(QDateTime) -// RegExp -> QVariant(QRegExp) -// [Any other object] -> QVariantMap(...) -QVariant QV8Engine::variantFromJS(v8::Handle<v8::Value> value) -{ - Q_ASSERT(!value.IsEmpty()); - if (value->IsNull() || value->IsUndefined()) - return QVariant(); - if (value->IsBoolean()) - return value->ToBoolean()->Value(); - if (value->IsInt32()) - return value->ToInt32()->Value(); - if (value->IsNumber()) - return value->ToNumber()->Value(); - if (value->IsString()) - return QJSConverter::toString(value->ToString()); - Q_ASSERT(value->IsObject()); - if (value->IsArray()) - return variantListFromJS(v8::Handle<v8::Array>::Cast(value)); - if (value->IsDate()) - return QJSConverter::toDateTime(v8::Handle<v8::Date>::Cast(value)); - if (value->IsRegExp()) - return QJSConverter::toRegExp(v8::Handle<v8::RegExp>::Cast(value)); - if (isVariant(value)) - return variantValue(value); - if (isQObject(value)) - return qVariantFromValue(qtObjectFromJS(value)); - return variantMapFromJS(value->ToObject()); -} - -bool QV8Engine::convertToNativeQObject(v8::Handle<v8::Value> value, - const QByteArray &targetType, - void **result) -{ - if (!targetType.endsWith('*')) - return false; - if (QObject *qobject = qtObjectFromJS(value)) { - int start = targetType.startsWith("const ") ? 6 : 0; - QByteArray className = targetType.mid(start, targetType.size()-start-1); - if (void *instance = qobject->qt_metacast(className)) { - *result = instance; - return true; - } - } - return false; -} - -QObject *QV8Engine::qtObjectFromJS(v8::Handle<v8::Value> value) -{ - if (!value->IsObject()) - return 0; - - QV8ObjectResource *r = (QV8ObjectResource *)value->ToObject()->GetExternalResource(); - if (!r) - return 0; - QV8ObjectResource::ResourceType type = r->resourceType(); - if (type == QV8ObjectResource::QObjectType) - return qobjectWrapper()->toQObject(r); - else if (type == QV8ObjectResource::VariantType) { - QVariant variant = variantWrapper()->toVariant(r); - int type = variant.userType(); - if ((type == QMetaType::QObjectStar) || (type == QMetaType::QWidgetStar)) - return *reinterpret_cast<QObject* const *>(variant.constData()); - } - return 0; -} - - -QVariant &QV8Engine::variantValue(v8::Handle<v8::Value> value) -{ - return variantWrapper()->variantValue(value); -} - -// Creates a QVariant wrapper object. -v8::Local<v8::Object> QV8Engine::newVariant(const QVariant &value) -{ - return variantWrapper()->newVariant(value); -} - -QScriptPassPointer<QJSValuePrivate> QV8Engine::evaluate(v8::Handle<v8::Script> script, v8::TryCatch& tryCatch) -{ - v8::HandleScope handleScope; - - clearExceptions(); - if (script.IsEmpty()) { - v8::Handle<v8::Value> exception = tryCatch.Exception(); - if (exception.IsEmpty()) { - // This is possible on syntax errors like { a:12, b:21 } <- missing "(", ")" around expression. - return new QJSValuePrivate(this); - } - setException(exception, tryCatch.Message()); - return new QJSValuePrivate(this, exception); - } - v8::Handle<v8::Value> result; - result = script->Run(); - if (result.IsEmpty()) { - v8::Handle<v8::Value> exception = tryCatch.Exception(); - // TODO: figure out why v8 doesn't always produce an exception value - //Q_ASSERT(!exception.IsEmpty()); - if (exception.IsEmpty()) - exception = v8::Exception::Error(v8::String::New("missing exception value")); - setException(exception, tryCatch.Message()); - return new QJSValuePrivate(this, exception); - } - return new QJSValuePrivate(this, result); -} - -QJSValue QV8Engine::scriptValueFromInternal(v8::Handle<v8::Value> value) const -{ - if (value.IsEmpty()) - return QJSValuePrivate::get(new QJSValuePrivate(const_cast<QV8Engine*>(this))); - return QJSValuePrivate::get(new QJSValuePrivate(const_cast<QV8Engine*>(this), value)); -} - -QScriptPassPointer<QJSValuePrivate> QV8Engine::newArray(uint length) -{ - return new QJSValuePrivate(this, v8::Array::New(length)); -} - -void QV8Engine::emitSignalHandlerException() -{ - emit q->signalHandlerException(scriptValueFromInternal(uncaughtException())); -} - -void QV8Engine::startTimer(const QString &timerName) -{ - if (!m_time.isValid()) - m_time.start(); - m_startedTimers[timerName] = m_time.elapsed(); -} - -qint64 QV8Engine::stopTimer(const QString &timerName, bool *wasRunning) -{ - if (!m_startedTimers.contains(timerName)) { - *wasRunning = false; - return 0; - } - *wasRunning = true; - qint64 startedAt = m_startedTimers.take(timerName); - return m_time.elapsed() - startedAt; -} - -int QV8Engine::consoleCountHelper(const QString &file, int line, int column) -{ - const QString key = file + QString::number(line) + QString::number(column); - int number = m_consoleCount.value(key, 0); - number++; - m_consoleCount.insert(key, number); - return number; -} - -void QV8GCCallback::registerGcPrologueCallback() -{ - QV8Engine::ThreadData *td = QV8Engine::threadData(); - if (!td->gcPrologueCallbackRegistered) { - td->gcPrologueCallbackRegistered = true; - v8::V8::AddGCPrologueCallback(QV8GCCallback::garbageCollectorPrologueCallback, v8::kGCTypeMarkSweepCompact); - } -} - -QV8GCCallback::Node::Node(PrologueCallback callback) - : prologueCallback(callback) -{ -} - -QV8GCCallback::Node::~Node() -{ - node.remove(); -} - -/* - Ensure that each persistent handle is strong if it has CPP ownership - and has no implicitly JS owned object owner in its parent chain, and - weak otherwise. - - Any weak handle whose parent object is still alive will have an implicit - reference (between the parent and the handle) added, so that it will - not be collected. - - Note that this callback is registered only for kGCTypeMarkSweepCompact - collection cycles, as it is during collection cycles of that type - in which weak persistent handle callbacks are called when required. - */ -void QV8GCCallback::garbageCollectorPrologueCallback(v8::GCType, v8::GCCallbackFlags) -{ - if (!QV8Engine::hasThreadData()) - return; - - QV8Engine::ThreadData *td = QV8Engine::threadData(); - QV8GCCallback::Node *currNode = td->gcCallbackNodes.first(); - - while (currNode) { - // The client which adds itself to the list is responsible - // for maintaining the correct implicit references in the - // specified callback. - currNode->prologueCallback(currNode); - currNode = td->gcCallbackNodes.next(currNode); - } -} - -void QV8GCCallback::addGcCallbackNode(QV8GCCallback::Node *node) -{ - QV8Engine::ThreadData *td = QV8Engine::threadData(); - td->gcCallbackNodes.insert(node); -} - -QV8Engine::ThreadData::ThreadData() - : gcPrologueCallbackRegistered(false) -{ - if (!v8::Isolate::GetCurrent()) { - isolate = v8::Isolate::New(); - isolate->Enter(); - } else { - isolate = 0; - } -} - -QV8Engine::ThreadData::~ThreadData() -{ - if (isolate) { - isolate->Exit(); - isolate->Dispose(); - isolate = 0; - } -} - -QT_END_NAMESPACE - |