/**************************************************************************** ** ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/ ** ** This file is part of the QtQml module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** GNU Lesser General Public License Usage ** This file may be used under the terms of the GNU Lesser General Public ** License version 2.1 as published by the Free Software Foundation and ** appearing in the file LICENSE.LGPL included in the packaging of this ** file. Please review the following information to ensure the GNU Lesser ** General Public License version 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional ** rights. These rights are described in the Nokia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU General ** Public License version 3.0 as published by the Free Software Foundation ** and appearing in the file LICENSE.GPL included in the packaging of this ** file. Please review the following information to ensure the GNU General ** Public License version 3.0 requirements will be met: ** http://www.gnu.org/copyleft/gpl.html. ** ** Other Usage ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. ** ** ** ** ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qv8engine_p.h" #include #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 #include #include #include #include #include #include "qscript_impl_p.h" #include "qv8domerrors_p.h" #include "qv8sqlerrors_p.h" Q_DECLARE_METATYPE(QJSValue) Q_DECLARE_METATYPE(QList) // XXX TODO: Need to check all the global functions will also work in a worker script where the // QQmlEngine is not available QT_BEGIN_NAMESPACE static bool ObjectComparisonCallback(v8::Local lhs, v8::Local rhs) { if (lhs == rhs) return true; if (lhs.IsEmpty() || rhs.IsEmpty()) return false; QV8ObjectResource *lhsr = static_cast(lhs->GetExternalResource()); QV8ObjectResource *rhsr = static_cast(rhs->GetExternalResource()); if (lhsr && rhsr) { Q_ASSERT(lhsr->engine == rhsr->engine); 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(); qMetaTypeId >(); 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::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 v = global()->Get(v8::String::New("Object"))->ToObject()->Get(v8::String::New("getOwnPropertyNames")); m_getOwnPropertyNames = qPersistentNew(v8::Handle::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 jsstr) { return toStringStatic(jsstr->ToString()); } QString QV8Engine::toStringStatic(v8::Handle jsstr) { QString qstr; qstr.resize(jsstr->Length()); jsstr->Write((uint16_t*)qstr.data()); return qstr; } QVariant QV8Engine::toVariant(v8::Handle 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(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 array = v8::Handle::Cast(value); if (typeHint == qMetaTypeId >()) { QList list; uint32_t length = array->Length(); for (uint32_t ii = 0; ii < length; ++ii) { v8::Local arrayItem = array->Get(ii); if (arrayItem->IsObject()) { list << toQObject(arrayItem->ToObject()); } else { list << 0; } } return qVariantFromValue >(list); } bool succeeded = false; QVariant retn = m_sequenceWrapper.toVariant(array, typeHint, &succeeded); if (succeeded) return retn; } return toBasicVariant(value); } static v8::Handle arrayFromStringList(QV8Engine *engine, const QStringList &list) { v8::Context::Scope scope(engine->context()); v8::Local 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 arrayFromVariantList(QV8Engine *engine, const QVariantList &list) { v8::Context::Scope scope(engine->context()); v8::Local 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 objectFromVariantMap(QV8Engine *engine, const QVariantMap &map) { v8::Context::Scope scope(engine->context()); v8::Local 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 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(ptr)); case QMetaType::Int: return v8::Integer::New(*reinterpret_cast(ptr)); case QMetaType::UInt: return v8::Integer::NewFromUnsigned(*reinterpret_cast(ptr)); case QMetaType::LongLong: return v8::Number::New(*reinterpret_cast(ptr)); case QMetaType::ULongLong: return v8::Number::New(*reinterpret_cast(ptr)); case QMetaType::Double: return v8::Number::New(*reinterpret_cast(ptr)); case QMetaType::QString: return m_stringWrapper.toString(*reinterpret_cast(ptr)); case QMetaType::Float: return v8::Number::New(*reinterpret_cast(ptr)); case QMetaType::Short: return v8::Integer::New(*reinterpret_cast(ptr)); case QMetaType::UShort: return v8::Integer::NewFromUnsigned(*reinterpret_cast(ptr)); case QMetaType::Char: return v8::Integer::New(*reinterpret_cast(ptr)); case QMetaType::UChar: return v8::Integer::NewFromUnsigned(*reinterpret_cast(ptr)); case QMetaType::QChar: return v8::Integer::New((*reinterpret_cast(ptr)).unicode()); case QMetaType::QDateTime: return v8::Date::New(qtDateTimeToJsDate(*reinterpret_cast(ptr))); case QMetaType::QDate: return v8::Date::New(qtDateTimeToJsDate(QDateTime(*reinterpret_cast(ptr)))); case QMetaType::QTime: return v8::Date::New(qtDateTimeToJsDate(QDateTime(QDate(1970,1,1), *reinterpret_cast(ptr)))); case QMetaType::QRegExp: return QJSConverter::toRegExp(*reinterpret_cast(ptr)); case QMetaType::QObjectStar: case QMetaType::QWidgetStar: return newQObject(*reinterpret_cast(ptr)); case QMetaType::QStringList: { bool succeeded = false; v8::Handle retn = m_sequenceWrapper.fromVariant(variant, &succeeded); if (succeeded) return retn; return arrayFromStringList(this, *reinterpret_cast(ptr)); } case QMetaType::QVariantList: return arrayFromVariantList(this, *reinterpret_cast(ptr)); case QMetaType::QVariantMap: return objectFromVariantMap(this, *reinterpret_cast(ptr)); default: break; } if (m_engine) { if (QQmlValueType *vt = QQmlEnginePrivate::get(m_engine)->valueTypes[type]) return m_valueTypeWrapper.newValueType(variant, vt); } } else { if (type == qMetaTypeId()) { typedef QQmlListReferencePrivate QDLRP; QDLRP *p = QDLRP::get((QQmlListReference*)ptr); if (p->object) { return m_listWrapper.newList(p->property, p->propertyType); } else { return v8::Null(); } } else if (type == qMetaTypeId()) { const QJSValue *value = reinterpret_cast(ptr); QJSValuePrivate *valuep = QJSValuePrivate::get(*value); if (valuep->assignEngine(this)) return v8::Local::New(*valuep); } 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; v8::Local array = v8::Array::New(list.count()); for (int ii = 0; ii < list.count(); ++ii) array->Set(ii, newQObject(list.at(ii))); return array; } bool objOk; QObject *obj = QQmlMetaType::toQObject(variant, &objOk); if (objOk) return newQObject(obj); bool succeeded = false; v8::Handle retn = m_sequenceWrapper.fromVariant(variant, &succeeded); if (succeeded) return retn; } // XXX TODO: To be compatible, we still need to handle: // + QObjectList // + QList return m_variantWrapper.newVariant(variant); } // A handle scope and context must be entered v8::Local QV8Engine::qmlModeCompile(const QString &source, const QString &fileName, int lineNumber) { v8::Local v8source = m_stringWrapper.toString(source); v8::Local v8fileName = m_stringWrapper.toString(fileName); v8::ScriptOrigin origin(v8fileName, v8::Integer::New(lineNumber - 1)); v8::Local script = v8::Script::Compile(v8source, &origin, 0, v8::Handle(), v8::Script::QmlMode); return script; } // A handle scope and context must be entered. // source can be either ascii or utf8. v8::Local QV8Engine::qmlModeCompile(const char *source, int sourceLength, const QString &fileName, int lineNumber) { if (sourceLength == -1) sourceLength = strlen(source); v8::Local v8source = v8::String::New(source, sourceLength); v8::Local v8fileName = m_stringWrapper.toString(fileName); v8::ScriptOrigin origin(v8fileName, v8::Integer::New(lineNumber - 1)); v8::Local script = v8::Script::Compile(v8source, &origin, 0, v8::Handle(), v8::Script::QmlMode); return script; } QNetworkAccessManager *QV8Engine::networkAccessManager() { return QQmlEnginePrivate::get(m_engine)->getNetworkAccessManager(); } const QStringHash &QV8Engine::illegalNames() const { return m_illegalNames; } // Requires a handle scope v8::Local QV8Engine::getOwnPropertyNames(v8::Handle o) { // FIXME Newer v8 have API for this function v8::TryCatch tc; v8::Handle args[] = { o }; v8::Local r = m_getOwnPropertyNames->Call(global(), 1, args); if (tc.HasCaught()) return v8::Array::New(); else return v8::Local::Cast(r); } QQmlContextData *QV8Engine::callingContext() { return m_contextWrapper.callingContext(); } // Converts a JS value to a QVariant. // Null, Undefined -> QVariant() (invalid) // Boolean -> QVariant(bool) // Number -> QVariant(double) // String -> QVariant(QString) // Array -> QVariantList(...) // Date -> QVariant(QDateTime) // RegExp -> QVariant(QRegExp) // [Any other object] -> QVariantMap(...) QVariant QV8Engine::toBasicVariant(v8::Handle 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::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::Cast(value)); } if (value->IsArray()) { v8::Context::Scope scope(context()); QVariantList rv; v8::Handle array = v8::Handle::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 object = value->ToObject(); return variantMapFromJS(object); } return QVariant(); } #include #include struct StaticQtMetaObject : public QObject { static const QMetaObject *get() { return &static_cast (0)->staticQtMetaObject; } }; void QV8Engine::initializeGlobal(v8::Handle global) { using namespace QQmlBuiltinFunctions; v8::Local console = v8::Object::New(); v8::Local 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 qt = v8::Object::New(); // Set all the enums from the "Qt" namespace const QMetaObject *qtMetaObject = StaticQtMetaObject::get(); for (int ii = 0; ii < qtMetaObject->enumeratorCount(); ++ii) { QMetaEnum enumerator = qtMetaObject->enumerator(ii); for (int jj = 0; jj < enumerator.keyCount(); ++jj) { qt->Set(v8::String::New(enumerator.key(jj)), v8::Integer::New(enumerator.value(jj))); } } qt->Set(v8::String::New("Asynchronous"), v8::Integer::New(0)); qt->Set(v8::String::New("Synchronous"), v8::Integer::New(1)); qt->Set(v8::String::New("include"), V8FUNCTION(QV8Include::include, this)); qt->Set(v8::String::New("isQtObject"), V8FUNCTION(isQtObject, this)); qt->Set(v8::String::New("rgba"), V8FUNCTION(rgba, this)); qt->Set(v8::String::New("hsla"), V8FUNCTION(hsla, this)); qt->Set(v8::String::New("rect"), V8FUNCTION(rect, this)); qt->Set(v8::String::New("point"), V8FUNCTION(point, this)); qt->Set(v8::String::New("size"), V8FUNCTION(size, this)); qt->Set(v8::String::New("vector3d"), V8FUNCTION(vector3d, this)); qt->Set(v8::String::New("vector4d"), V8FUNCTION(vector4d, this)); qt->Set(v8::String::New("formatDate"), V8FUNCTION(formatDate, this)); qt->Set(v8::String::New("formatTime"), V8FUNCTION(formatTime, this)); qt->Set(v8::String::New("formatDateTime"), V8FUNCTION(formatDateTime, this)); qt->Set(v8::String::New("openUrlExternally"), V8FUNCTION(openUrlExternally, this)); qt->Set(v8::String::New("fontFamilies"), V8FUNCTION(fontFamilies, this)); qt->Set(v8::String::New("md5"), V8FUNCTION(md5, this)); qt->Set(v8::String::New("btoa"), V8FUNCTION(btoa, this)); qt->Set(v8::String::New("atob"), V8FUNCTION(atob, this)); qt->Set(v8::String::New("resolvedUrl"), V8FUNCTION(resolvedUrl, this)); qt->Set(v8::String::New("locale"), V8FUNCTION(locale, this)); if (m_engine) { qt->Set(v8::String::New("application"), newQObject(new QQuickApplication(m_engine))); qt->Set(v8::String::New("inputMethod"), newQObject(qGuiApp->inputMethod(), CppOwnership)); qt->Set(v8::String::New("lighter"), V8FUNCTION(lighter, this)); qt->Set(v8::String::New("darker"), V8FUNCTION(darker, this)); qt->Set(v8::String::New("tint"), V8FUNCTION(tint, this)); qt->Set(v8::String::New("quit"), V8FUNCTION(quit, this)); qt->Set(v8::String::New("createQmlObject"), V8FUNCTION(createQmlObject, this)); qt->Set(v8::String::New("createComponent"), V8FUNCTION(createComponent, this)); } global->Set(v8::String::New("qsTranslate"), V8FUNCTION(qsTranslate, this)); global->Set(v8::String::New("QT_TRANSLATE_NOOP"), V8FUNCTION(qsTranslateNoOp, this)); global->Set(v8::String::New("qsTr"), V8FUNCTION(qsTr, this)); global->Set(v8::String::New("QT_TR_NOOP"), V8FUNCTION(qsTrNoOp, this)); global->Set(v8::String::New("qsTrId"), V8FUNCTION(qsTrId, this)); global->Set(v8::String::New("QT_TRID_NOOP"), V8FUNCTION(qsTrIdNoOp, this)); global->Set(v8::String::New("print"), consoleLogFn); global->Set(v8::String::New("console"), console); global->Set(v8::String::New("Qt"), qt); global->Set(v8::String::New("gc"), V8FUNCTION(QQmlBuiltinFunctions::gc, this)); { #define STRING_ARG "(function(stringArg) { "\ " String.prototype.arg = (function() {"\ " return stringArg.apply(this, arguments);"\ " })"\ "})" v8::Local registerArg = v8::Script::New(v8::String::New(STRING_ARG), 0, 0, v8::Handle(), v8::Script::NativeMode); v8::Local result = registerArg->Run(); Q_ASSERT(result->IsFunction()); v8::Local registerArgFunc = v8::Local::Cast(result); v8::Handle args = V8FUNCTION(stringArg, this); registerArgFunc->Call(v8::Local::Cast(registerArgFunc), 1, &args); #undef STRING_ARG } QQmlLocale::registerStringLocaleCompare(this); QQmlDateExtension::registerExtension(this); QQmlNumberExtension::registerExtension(this); qt_add_domexceptions(this); m_xmlHttpRequestData = qt_add_qmlxmlhttprequest(this); qt_add_sqlexceptions(this); { v8::Handle args[] = { global }; v8::Local names = m_getOwnPropertyNames->Call(global, 1, args); v8::Local namesArray = v8::Local::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 freeze = v8::Script::New(v8::String::New(FREEZE_SOURCE)); v8::Local result = freeze->Run(); Q_ASSERT(result->IsFunction()); m_freezeObject = qPersistentNew(v8::Local::Cast(result)); #undef FREEZE_SOURCE } } void QV8Engine::freezeObject(v8::Handle value) { v8::Handle args[] = { value }; m_freezeObject->Call(global(), 1, args); } void QV8Engine::gc() { v8::V8::LowMemoryNotification(); while (!v8::V8::IdleNotification()) {} } #ifdef QML_GLOBAL_HANDLE_DEBUGGING #include static QThreadStorage *> 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); 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); 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 *QV8Engine::findOwnerAndStrength(QObject *object, bool *shouldBeStrong) { QObject *parent = object->parent(); if (!parent) { // if the object has JS ownership, the object's v8object owns the lifetime of the persistent value. if (QQmlEngine::objectOwnership(object) == QQmlEngine::JavaScriptOwnership) { *shouldBeStrong = false; return &(QQmlData::get(object)->v8object); } // no parent, and has CPP ownership - doesn't have an implicit parent. *shouldBeStrong = true; return 0; } // if it is owned by CPP, it's root parent may still be owned by JS. // in that case, the owner of the persistent handle is the root parent's v8object. while (parent->parent()) parent = parent->parent(); if (QQmlEngine::objectOwnership(parent) == QQmlEngine::JavaScriptOwnership) { // root parent is owned by JS. It's v8object owns the persistent value in question. *shouldBeStrong = false; return &(QQmlData::get(parent)->v8object); } else { // root parent has CPP ownership. The persistent value should not be made weak. *shouldBeStrong = true; return 0; } } QDateTime QV8Engine::qtDateTimeFromJsDate(double jsDate) { // from QScriptEngine::MsToDateTime() if (qIsNaN(jsDate)) return QDateTime(); QV8DateConverter::JSC::GregorianDateTime tm; QV8DateConverter::JSC::msToGregorianDateTime(jsDate, tm); // from QScriptEngine::MsFromTime() int ms = int(::fmod(jsDate, 1000.0)); if (ms < 0) ms += int(1000.0); QDateTime convertedUTC = QDateTime(QDate(tm.year + 1900, tm.month + 1, tm.monthDay), QTime(tm.hour, tm.minute, tm.second, ms), Qt::UTC); return convertedUTC.toLocalTime(); } void QV8Engine::addRelationshipForGC(QObject *object, v8::Persistent handle) { if (handle.IsEmpty()) return; bool handleShouldBeStrong = false; v8::Persistent *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 *implicitOwner = findOwnerAndStrength(object, &handleShouldBeStrong); v8::Persistent handle = QQmlData::get(other, true)->v8object; if (handleShouldBeStrong) { v8::V8::AddImplicitReferences(m_strongReferencer, &handle, 1); } else if (!implicitOwner->IsEmpty()) { v8::V8::AddImplicitReferences(*implicitOwner, &handle, 1); } } static QThreadStorage 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::initQmlGlobalObject() { v8::HandleScope handels; v8::Context::Scope contextScope(m_context); initializeGlobal(m_context->Global()); freezeObject(m_context->Global()); } void QV8Engine::setEngine(QQmlEngine *engine) { m_engine = engine; initQmlGlobalObject(); } void QV8Engine::setException(v8::Handle value, v8::Handle msg) { m_exception.set(value, msg); } v8::Handle QV8Engine::throwException(v8::Handle value) { setException(value); v8::ThrowException(value); return value; } void QV8Engine::clearExceptions() { m_exception.clear(); } v8::Handle QV8Engine::uncaughtException() const { if (!hasUncaughtException()) return v8::Handle(); return m_exception; } bool QV8Engine::hasUncaughtException() const { return m_exception; } int QV8Engine::uncaughtExceptionLineNumber() const { return m_exception.lineNumber(); } QStringList QV8Engine::uncaughtExceptionBacktrace() const { return m_exception.backtrace(); } /*! \internal Save the current exception on stack so it can be set again later. \sa QV8Engine::restoreException */ void QV8Engine::saveException() { m_exception.push(); } /*! \internal Load a saved exception from stack. Current exception, if exists will be dropped \sa QV8Engine::saveException */ void QV8Engine::restoreException() { m_exception.pop(); } QV8Engine::Exception::Exception() {} QV8Engine::Exception::~Exception() { Q_ASSERT_X(m_stack.isEmpty(), Q_FUNC_INFO, "Some saved exceptions left. Asymetric pop/push found."); clear(); } void QV8Engine::Exception::set(v8::Handle value, v8::Handle message) { Q_ASSERT_X(!value.IsEmpty(), Q_FUNC_INFO, "Throwing an empty value handle is highly suspected"); clear(); m_value = v8::Persistent::New(value); m_message = v8::Persistent::New(message); } void QV8Engine::Exception::clear() { m_value.Dispose(); m_value.Clear(); m_message.Dispose(); m_message.Clear(); } QV8Engine::Exception::operator bool() const { return !m_value.IsEmpty(); } QV8Engine::Exception::operator v8::Handle() const { Q_ASSERT(*this); return m_value; } int QV8Engine::Exception::lineNumber() const { if (m_message.IsEmpty()) return -1; return m_message->GetLineNumber(); } QStringList QV8Engine::Exception::backtrace() const { if (m_message.IsEmpty()) return QStringList(); QStringList backtrace; v8::Handle trace = m_message->GetStackTrace(); if (trace.IsEmpty()) // FIXME it should not happen (SetCaptureStackTraceForUncaughtExceptions is called). return QStringList(); for (int i = 0; i < trace->GetFrameCount(); ++i) { v8::Local frame = trace->GetFrame(i); backtrace.append(QJSConverter::toString(frame->GetFunctionName())); backtrace.append(QJSConverter::toString(frame->GetFunctionName())); backtrace.append(QString::fromAscii("()@")); backtrace.append(QJSConverter::toString(frame->GetScriptName())); backtrace.append(QString::fromAscii(":")); backtrace.append(QString::number(frame->GetLineNumber())); } return backtrace; } void QV8Engine::Exception::push() { m_stack.push(qMakePair(m_value, m_message)); m_value.Clear(); m_message.Clear(); } void QV8Engine::Exception::pop() { Q_ASSERT_X(!m_stack.empty(), Q_FUNC_INFO, "Attempt to load unsaved exception found"); ValueMessagePair pair = m_stack.pop(); clear(); m_value = pair.first; m_message = pair.second; } // 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 QV8Engine::variantListToJS(const QVariantList &lst) { v8::Local result = v8::Array::New(lst.size()); for (int i = 0; i < lst.size(); ++i) result->Set(i, variantToJS(lst.at(i))); return result; } // Converts a JS Array object to a QVariantList. // The result is a QVariantList with length equal to the length // of the JS Array, and elements being the JS Array's elements // converted to QVariants, recursively. QVariantList QV8Engine::variantListFromJS(v8::Handle jsArray) { QVariantList result; int hash = jsArray->GetIdentityHash(); if (visitedConversionObjects.contains(hash)) return result; // Avoid recursion. v8::HandleScope handleScope; visitedConversionObjects.insert(hash); uint32_t length = jsArray->Length(); for (uint32_t i = 0; i < length; ++i) result.append(variantFromJS(jsArray->Get(i))); visitedConversionObjects.remove(hash); return result; } // Converts a QVariantMap to JS. // The result is a new Object object with property names being // the keys of the QVariantMap, and values being the values of // the QVariantMap converted to JS, recursively. v8::Local QV8Engine::variantMapToJS(const QVariantMap &vmap) { v8::Local result = v8::Object::New(); QVariantMap::const_iterator it; for (it = vmap.constBegin(); it != vmap.constEnd(); ++it) result->Set(QJSConverter::toString(it.key()), variantToJS(it.value())); return result; } // Converts a JS Object to a QVariantMap. // The result is a QVariantMap with keys being the property names // of the object, and values being the values of the JS object's // properties converted to QVariants, recursively. QVariantMap QV8Engine::variantMapFromJS(v8::Handle jsObject) { QVariantMap result; v8::HandleScope handleScope; v8::Handle 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 name = propertyNames->Get(i); result.insert(QJSConverter::toString(name->ToString()), variantFromJS(jsObject->Get(name))); } visitedConversionObjects.remove(hash); return result; } // Converts the meta-type defined by the given type and data to JS. // Returns the value if conversion succeeded, an empty handle otherwise. v8::Handle QV8Engine::metaTypeToJS(int type, const void *data) { Q_ASSERT(data != 0); v8::Handle result; // check if it's one of the types we know switch (QMetaType::Type(type)) { case QMetaType::Void: return v8::Undefined(); case QMetaType::Bool: return v8::Boolean::New(*reinterpret_cast(data)); case QMetaType::Int: return v8::Int32::New(*reinterpret_cast(data)); case QMetaType::UInt: return v8::Uint32::New(*reinterpret_cast(data)); case QMetaType::LongLong: return v8::Number::New(double(*reinterpret_cast(data))); case QMetaType::ULongLong: #if defined(Q_OS_WIN) && defined(_MSC_FULL_VER) && _MSC_FULL_VER <= 12008804 #pragma message("** NOTE: You need the Visual Studio Processor Pack to compile support for 64bit unsigned integers.") return v8::Number::New(double((qlonglong)*reinterpret_cast(data))); #elif defined(Q_CC_MSVC) && !defined(Q_CC_MSVC_NET) return v8::Number::New(double((qlonglong)*reinterpret_cast(data))); #else return v8::Number::New(double(*reinterpret_cast(data))); #endif case QMetaType::Double: return v8::Number::New(double(*reinterpret_cast(data))); case QMetaType::QString: return QJSConverter::toString(*reinterpret_cast(data)); case QMetaType::Float: return v8::Number::New(*reinterpret_cast(data)); case QMetaType::Short: return v8::Int32::New(*reinterpret_cast(data)); case QMetaType::UShort: return v8::Uint32::New(*reinterpret_cast(data)); case QMetaType::Char: return v8::Int32::New(*reinterpret_cast(data)); case QMetaType::UChar: return v8::Uint32::New(*reinterpret_cast(data)); case QMetaType::QChar: return v8::Uint32::New((*reinterpret_cast(data)).unicode()); case QMetaType::QStringList: result = QJSConverter::toStringList(*reinterpret_cast(data)); break; case QMetaType::QVariantList: result = variantListToJS(*reinterpret_cast(data)); break; case QMetaType::QVariantMap: result = variantMapToJS(*reinterpret_cast(data)); break; case QMetaType::QDateTime: result = QJSConverter::toDateTime(*reinterpret_cast(data)); break; case QMetaType::QDate: result = QJSConverter::toDateTime(QDateTime(*reinterpret_cast(data))); break; case QMetaType::QRegExp: result = QJSConverter::toRegExp(*reinterpret_cast(data)); break; case QMetaType::QObjectStar: case QMetaType::QWidgetStar: result = newQObject(*reinterpret_cast(data)); break; case QMetaType::QVariant: result = variantToJS(*reinterpret_cast(data)); break; default: if (type == qMetaTypeId()) { return QJSValuePrivate::get(*reinterpret_cast(data))->asV8Value(this); } else { QByteArray typeName = QMetaType::typeName(type); if (typeName.endsWith('*') && !*reinterpret_cast(data)) { return v8::Null(); } else { // Fall back to wrapping in a QVariant. result = newVariant(QVariant(type, data)); } } } return result; } // Converts a JS value to a meta-type. // data must point to a place that can store a value of the given type. // Returns true if conversion succeeded, false otherwise. bool QV8Engine::metaTypeFromJS(v8::Handle value, int type, void *data) { // check if it's one of the types we know switch (QMetaType::Type(type)) { case QMetaType::Bool: *reinterpret_cast(data) = value->ToBoolean()->Value(); return true; case QMetaType::Int: *reinterpret_cast(data) = value->ToInt32()->Value(); return true; case QMetaType::UInt: *reinterpret_cast(data) = value->ToUint32()->Value(); return true; case QMetaType::LongLong: *reinterpret_cast(data) = qlonglong(value->ToInteger()->Value()); return true; case QMetaType::ULongLong: *reinterpret_cast(data) = qulonglong(value->ToInteger()->Value()); return true; case QMetaType::Double: *reinterpret_cast(data) = value->ToNumber()->Value(); return true; case QMetaType::QString: if (value->IsUndefined() || value->IsNull()) *reinterpret_cast(data) = QString(); else *reinterpret_cast(data) = QJSConverter::toString(value->ToString()); return true; case QMetaType::Float: *reinterpret_cast(data) = value->ToNumber()->Value(); return true; case QMetaType::Short: *reinterpret_cast(data) = short(value->ToInt32()->Value()); return true; case QMetaType::UShort: *reinterpret_cast(data) = ushort(value->ToInt32()->Value()); // ### QScript::ToUInt16() return true; case QMetaType::Char: *reinterpret_cast(data) = char(value->ToInt32()->Value()); return true; case QMetaType::UChar: *reinterpret_cast(data) = (unsigned char)(value->ToInt32()->Value()); return true; case QMetaType::QChar: if (value->IsString()) { QString str = QJSConverter::toString(v8::Handle::Cast(value)); *reinterpret_cast(data) = str.isEmpty() ? QChar() : str.at(0); } else { *reinterpret_cast(data) = QChar(ushort(value->ToInt32()->Value())); // ### QScript::ToUInt16() } return true; case QMetaType::QDateTime: if (value->IsDate()) { *reinterpret_cast(data) = QJSConverter::toDateTime(v8::Handle::Cast(value)); return true; } break; case QMetaType::QDate: if (value->IsDate()) { *reinterpret_cast(data) = QJSConverter::toDateTime(v8::Handle::Cast(value)).date(); return true; } break; case QMetaType::QRegExp: if (value->IsRegExp()) { *reinterpret_cast(data) = QJSConverter::toRegExp(v8::Handle::Cast(value)); return true; } break; case QMetaType::QObjectStar: if (isQObject(value) || value->IsNull()) { *reinterpret_cast(data) = qtObjectFromJS(value); return true; } break; case QMetaType::QWidgetStar: if (isQObject(value) || value->IsNull()) { QObject *qo = qtObjectFromJS(value); if (!qo || qo->isWidgetType()) { *reinterpret_cast(data) = reinterpret_cast(qo); return true; } } break; case QMetaType::QStringList: if (value->IsArray()) { *reinterpret_cast(data) = QJSConverter::toStringList(v8::Handle::Cast(value)); return true; } break; case QMetaType::QVariantList: if (value->IsArray()) { *reinterpret_cast(data) = variantListFromJS(v8::Handle::Cast(value)); return true; } break; case QMetaType::QVariantMap: if (value->IsObject()) { *reinterpret_cast(data) = variantMapFromJS(v8::Handle::Cast(value)); return true; } break; case QMetaType::QVariant: *reinterpret_cast(data) = variantFromJS(value); return true; default: ; } #if 0 if (isQtVariant(value)) { const QVariant &var = variantValue(value); // ### Enable once constructInPlace() is in qt master. if (var.userType() == type) { QMetaType::constructInPlace(type, data, var.constData()); return true; } if (var.canConvert(QVariant::Type(type))) { QVariant vv = var; vv.convert(QVariant::Type(type)); Q_ASSERT(vv.userType() == type); QMetaType::constructInPlace(type, data, vv.constData()); return true; } } #endif // Try to use magic; for compatibility with qscriptvalue_cast. QByteArray name = QMetaType::typeName(type); if (convertToNativeQObject(value, name, reinterpret_cast(data))) return true; if (isVariant(value) && name.endsWith('*')) { int valueType = QMetaType::type(name.left(name.size()-1)); QVariant &var = variantValue(value); if (valueType == var.userType()) { // We have T t, T* is requested, so return &t. *reinterpret_cast(data) = var.data(); return true; } else { // Look in the prototype chain. v8::Handle proto = value->ToObject()->GetPrototype(); while (proto->IsObject()) { bool canCast = false; if (isVariant(proto)) { canCast = (type == variantValue(proto).userType()) || (valueType && (valueType == variantValue(proto).userType())); } else if (isQObject(proto)) { QByteArray className = name.left(name.size()-1); if (QObject *qobject = qtObjectFromJS(proto)) canCast = qobject->qt_metacast(className) != 0; } if (canCast) { QByteArray varTypeName = QMetaType::typeName(var.userType()); if (varTypeName.endsWith('*')) *reinterpret_cast(data) = *reinterpret_cast(var.data()); else *reinterpret_cast(data) = var.data(); return true; } proto = proto->ToObject()->GetPrototype(); } } } else if (value->IsNull() && name.endsWith('*')) { *reinterpret_cast(data) = 0; return true; } else if (type == qMetaTypeId()) { *reinterpret_cast(data) = QJSValuePrivate::get(new QJSValuePrivate(this, value)); return true; } return false; } // Converts a QVariant to JS. v8::Handle QV8Engine::variantToJS(const QVariant &value) { return metaTypeToJS(value.userType(), value.constData()); } // Converts a JS value to a QVariant. // 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(v8::Handle value) { Q_ASSERT(!value.IsEmpty()); if (value->IsUndefined()) return QVariant(); if (value->IsNull()) return QVariant(QMetaType::VoidStar, 0); 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::Cast(value)); if (value->IsDate()) return QJSConverter::toDateTime(v8::Handle::Cast(value)); if (value->IsRegExp()) return QJSConverter::toRegExp(v8::Handle::Cast(value)); if (isVariant(value)) return variantValue(value); if (isQObject(value)) return qVariantFromValue(qtObjectFromJS(value)); return variantMapFromJS(value->ToObject()); } bool QV8Engine::convertToNativeQObject(v8::Handle value, const QByteArray &targetType, void **result) { if (!targetType.endsWith('*')) return false; if (QObject *qobject = qtObjectFromJS(value)) { int start = targetType.startsWith("const ") ? 6 : 0; QByteArray className = targetType.mid(start, targetType.size()-start-1); if (void *instance = qobject->qt_metacast(className)) { *result = instance; return true; } } return false; } QObject *QV8Engine::qtObjectFromJS(v8::Handle value) { if (!value->IsObject()) return 0; QV8ObjectResource *r = (QV8ObjectResource *)value->ToObject()->GetExternalResource(); if (!r) return 0; QV8ObjectResource::ResourceType type = r->resourceType(); if (type == QV8ObjectResource::QObjectType) return qobjectWrapper()->toQObject(r); else if (type == QV8ObjectResource::VariantType) { QVariant variant = variantWrapper()->toVariant(r); int type = variant.userType(); if ((type == QMetaType::QObjectStar) || (type == QMetaType::QWidgetStar)) return *reinterpret_cast(variant.constData()); } return 0; } QVariant &QV8Engine::variantValue(v8::Handle value) { return variantWrapper()->variantValue(value); } // Creates a QVariant wrapper object. v8::Local QV8Engine::newVariant(const QVariant &value) { return variantWrapper()->newVariant(value); } QScriptPassPointer QV8Engine::evaluate(v8::Handle script, v8::TryCatch& tryCatch) { v8::HandleScope handleScope; clearExceptions(); if (script.IsEmpty()) { v8::Handle exception = tryCatch.Exception(); if (exception.IsEmpty()) { // This is possible on syntax errors like { a:12, b:21 } <- missing "(", ")" around expression. return new QJSValuePrivate(this); } setException(exception, tryCatch.Message()); return new QJSValuePrivate(this, exception); } v8::Handle result; result = script->Run(); if (result.IsEmpty()) { v8::Handle exception = tryCatch.Exception(); // TODO: figure out why v8 doesn't always produce an exception value //Q_ASSERT(!exception.IsEmpty()); if (exception.IsEmpty()) exception = v8::Exception::Error(v8::String::New("missing exception value")); setException(exception, tryCatch.Message()); return new QJSValuePrivate(this, exception); } return new QJSValuePrivate(this, result); } QJSValue QV8Engine::scriptValueFromInternal(v8::Handle value) const { if (value.IsEmpty()) return QJSValuePrivate::get(new QJSValuePrivate(const_cast(this))); return QJSValuePrivate::get(new QJSValuePrivate(const_cast(this), value)); } QScriptPassPointer QV8Engine::newArray(uint length) { return new QJSValuePrivate(this, v8::Array::New(length)); } void QV8Engine::emitSignalHandlerException() { emit q->signalHandlerException(scriptValueFromInternal(uncaughtException())); } 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