1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtQml module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qv8engine_p.h"
44 #include "qv8contextwrapper_p.h"
45 #include "qv8valuetypewrapper_p.h"
46 #include "qv8sequencewrapper_p.h"
47 #include "qv8include_p.h"
48 #include "qjsengine_p.h"
50 #include <private/qqmlbuiltinfunctions_p.h>
51 #include <private/qqmllist_p.h>
52 #include <private/qqmlengine_p.h>
53 #include <private/qqmlxmlhttprequest_p.h>
54 #include <private/qqmllocale_p.h>
55 #include <private/qqmlglobal_p.h>
56 #include <private/qqmlmemoryprofiler_p.h>
58 #include "qscript_impl_p.h"
59 #include "qv8domerrors_p.h"
60 #include "qv8sqlerrors_p.h"
62 #include <QtCore/qjsonarray.h>
63 #include <QtCore/qjsonobject.h>
64 #include <QtCore/qjsonvalue.h>
66 Q_DECLARE_METATYPE(QJSValue)
67 Q_DECLARE_METATYPE(QList<int>)
70 // XXX TODO: Need to check all the global functions will also work in a worker script where the
71 // QQmlEngine is not available
74 static bool ObjectComparisonCallback(v8::Local<v8::Object> lhs, v8::Local<v8::Object> rhs)
79 if (lhs.IsEmpty() || rhs.IsEmpty())
82 QV8ObjectResource *lhsr = static_cast<QV8ObjectResource*>(lhs->GetExternalResource());
83 QV8ObjectResource *rhsr = static_cast<QV8ObjectResource*>(rhs->GetExternalResource());
86 Q_ASSERT(lhsr->engine == rhsr->engine);
87 QV8ObjectResource::ResourceType lhst = lhsr->resourceType();
88 QV8ObjectResource::ResourceType rhst = rhsr->resourceType();
91 case QV8ObjectResource::ValueTypeType:
92 // a value type might be equal to a variant or another value type
93 if (rhst == QV8ObjectResource::ValueTypeType) {
94 return lhsr->engine->valueTypeWrapper()->isEqual(lhsr, lhsr->engine->valueTypeWrapper()->toVariant(rhsr));
95 } else if (rhst == QV8ObjectResource::VariantType) {
96 return lhsr->engine->valueTypeWrapper()->isEqual(lhsr, lhsr->engine->variantWrapper()->toVariant(rhsr));
99 case QV8ObjectResource::VariantType:
100 // a variant might be equal to a value type or other variant.
101 if (rhst == QV8ObjectResource::VariantType) {
102 return lhsr->engine->variantWrapper()->toVariant(lhsr) ==
103 lhsr->engine->variantWrapper()->toVariant(rhsr);
104 } else if (rhst == QV8ObjectResource::ValueTypeType) {
105 return rhsr->engine->valueTypeWrapper()->isEqual(rhsr, rhsr->engine->variantWrapper()->toVariant(lhsr));
108 case QV8ObjectResource::SequenceType:
109 // a sequence might be equal to itself.
110 if (rhst == QV8ObjectResource::SequenceType) {
111 return lhsr->engine->sequenceWrapper()->isEqual(lhsr, rhsr);
123 QV8Engine::QV8Engine(QJSEngine* qq, ContextOwnership ownership)
126 , m_ownsV8Context(ownership == CreateNewContext)
127 , m_xmlHttpRequestData(0)
131 QML_MEMORY_SCOPE_STRING("QV8Engine::QV8Engine");
132 qMetaTypeId<QJSValue>();
133 qMetaTypeId<QList<int> >();
135 QByteArray v8args = qgetenv("V8ARGS");
136 // change default v8 behaviour to not relocate breakpoints across lines
137 if (!v8args.contains("breakpoint_relocation"))
138 v8args.append(" --nobreakpoint_relocation");
139 v8::V8::SetFlagsFromString(v8args.constData(), v8args.length());
141 ensurePerThreadIsolate();
143 v8::HandleScope handle_scope;
144 m_context = (ownership == CreateNewContext) ? v8::Context::New() : v8::Persistent<v8::Context>::New(v8::Context::GetCurrent());
145 qPersistentRegister(m_context);
146 m_originalGlobalObject.init(m_context);
147 v8::Context::Scope context_scope(m_context);
149 v8::V8::SetUserObjectComparisonCallbackFunction(ObjectComparisonCallback);
150 QV8GCCallback::registerGcPrologueCallback();
151 m_strongReferencer = qPersistentNew(v8::Object::New());
153 m_bindingFlagKey = qPersistentNew(v8::String::New("qml::binding"));
155 m_stringWrapper.init();
156 m_contextWrapper.init(this);
157 m_qobjectWrapper.init(this);
158 m_typeWrapper.init(this);
159 m_listWrapper.init(this);
160 m_variantWrapper.init(this);
161 m_valueTypeWrapper.init(this);
162 m_sequenceWrapper.init(this);
163 m_jsonWrapper.init(this);
166 v8::Handle<v8::Value> v = global()->Get(v8::String::New("Object"))->ToObject()->Get(v8::String::New("getOwnPropertyNames"));
167 m_getOwnPropertyNames = qPersistentNew<v8::Function>(v8::Handle<v8::Function>::Cast(v));
171 QV8Engine::~QV8Engine()
173 Q_ASSERT_X(v8::Isolate::GetCurrent(), "QV8Engine::~QV8Engine()", "called after v8::Isolate has exited");
174 for (int ii = 0; ii < m_extensionData.count(); ++ii)
175 delete m_extensionData[ii];
176 m_extensionData.clear();
178 qt_rem_qmlxmlhttprequest(this, m_xmlHttpRequestData);
179 m_xmlHttpRequestData = 0;
180 delete m_listModelData;
183 qPersistentDispose(m_freezeObject);
184 qPersistentDispose(m_getOwnPropertyNames);
186 invalidateAllValues();
188 qPersistentDispose(m_strongReferencer);
190 m_jsonWrapper.destroy();
191 m_sequenceWrapper.destroy();
192 m_valueTypeWrapper.destroy();
193 m_variantWrapper.destroy();
194 m_listWrapper.destroy();
195 m_typeWrapper.destroy();
196 m_qobjectWrapper.destroy();
197 m_contextWrapper.destroy();
198 m_stringWrapper.destroy();
200 qPersistentDispose(m_bindingFlagKey);
202 m_originalGlobalObject.destroy();
205 qPersistentDispose(m_context);
208 QString QV8Engine::toStringStatic(v8::Handle<v8::Value> jsstr)
210 return toStringStatic(jsstr->ToString());
213 QString QV8Engine::toStringStatic(v8::Handle<v8::String> jsstr)
216 qstr.resize(jsstr->Length());
217 jsstr->Write((uint16_t*)qstr.data());
221 QVariant QV8Engine::toVariant(v8::Handle<v8::Value> value, int typeHint)
226 if (typeHint == QVariant::Bool)
227 return QVariant(value->BooleanValue());
229 if (typeHint == QMetaType::QJsonValue)
230 return QVariant::fromValue(jsonValueFromJS(value));
232 if (typeHint == qMetaTypeId<QJSValue>())
233 return QVariant::fromValue(scriptValueFromInternal(value));
235 if (value->IsObject()) {
236 QV8ObjectResource *r = (QV8ObjectResource *)value->ToObject()->GetExternalResource();
238 switch (r->resourceType()) {
239 case QV8ObjectResource::Context2DStyleType:
240 case QV8ObjectResource::Context2DPixelArrayType:
241 case QV8ObjectResource::SignalHandlerType:
242 case QV8ObjectResource::IncubatorType:
243 case QV8ObjectResource::VisualDataItemType:
244 case QV8ObjectResource::ContextType:
245 case QV8ObjectResource::XMLHttpRequestType:
246 case QV8ObjectResource::DOMNodeType:
247 case QV8ObjectResource::SQLDatabaseType:
248 case QV8ObjectResource::ListModelType:
249 case QV8ObjectResource::Context2DType:
250 case QV8ObjectResource::ParticleDataType:
251 case QV8ObjectResource::LocaleDataType:
252 case QV8ObjectResource::ChangeSetArrayType:
254 case QV8ObjectResource::TypeType:
255 return m_typeWrapper.toVariant(r);
256 case QV8ObjectResource::QObjectType:
257 return qVariantFromValue<QObject *>(m_qobjectWrapper.toQObject(r));
258 case QV8ObjectResource::ListType:
259 return m_listWrapper.toVariant(r);
260 case QV8ObjectResource::VariantType:
261 return m_variantWrapper.toVariant(r);
262 case QV8ObjectResource::ValueTypeType:
263 return m_valueTypeWrapper.toVariant(r);
264 case QV8ObjectResource::SequenceType:
265 return m_sequenceWrapper.toVariant(r);
267 } else if (typeHint == QMetaType::QJsonObject
268 && !value->IsArray() && !value->IsFunction()) {
269 return QVariant::fromValue(jsonObjectFromJS(value));
273 if (value->IsArray()) {
274 v8::Handle<v8::Array> array = v8::Handle<v8::Array>::Cast(value);
275 if (typeHint == qMetaTypeId<QList<QObject *> >()) {
276 QList<QObject *> list;
277 uint32_t length = array->Length();
278 for (uint32_t ii = 0; ii < length; ++ii) {
279 v8::Local<v8::Value> arrayItem = array->Get(ii);
280 if (arrayItem->IsObject()) {
281 list << toQObject(arrayItem->ToObject());
287 return qVariantFromValue<QList<QObject*> >(list);
288 } else if (typeHint == QMetaType::QJsonArray) {
289 return QVariant::fromValue(jsonArrayFromJS(value));
292 bool succeeded = false;
293 QVariant retn = m_sequenceWrapper.toVariant(array, typeHint, &succeeded);
298 return toBasicVariant(value);
301 static v8::Handle<v8::Array> arrayFromStringList(QV8Engine *engine, const QStringList &list)
303 v8::Context::Scope scope(engine->context());
304 v8::Local<v8::Array> result = v8::Array::New(list.count());
305 for (int ii = 0; ii < list.count(); ++ii)
306 result->Set(ii, engine->toString(list.at(ii)));
310 static v8::Handle<v8::Array> arrayFromVariantList(QV8Engine *engine, const QVariantList &list)
312 v8::Context::Scope scope(engine->context());
313 v8::Local<v8::Array> result = v8::Array::New(list.count());
314 for (int ii = 0; ii < list.count(); ++ii)
315 result->Set(ii, engine->fromVariant(list.at(ii)));
319 static v8::Handle<v8::Object> objectFromVariantMap(QV8Engine *engine, const QVariantMap &map)
321 v8::Context::Scope scope(engine->context());
322 v8::Local<v8::Object> object = v8::Object::New();
323 for (QVariantMap::ConstIterator iter = map.begin(); iter != map.end(); ++iter)
324 object->Set(engine->toString(iter.key()), engine->fromVariant(iter.value()));
328 Q_CORE_EXPORT QString qt_regexp_toCanonical(const QString &, QRegExp::PatternSyntax);
330 v8::Handle<v8::Value> QV8Engine::fromVariant(const QVariant &variant)
332 int type = variant.userType();
333 const void *ptr = variant.constData();
335 if (type < QMetaType::User) {
336 switch (QMetaType::Type(type)) {
337 case QMetaType::UnknownType:
338 case QMetaType::Void:
339 return v8::Undefined();
340 case QMetaType::Bool:
341 return v8::Boolean::New(*reinterpret_cast<const bool*>(ptr));
343 return v8::Integer::New(*reinterpret_cast<const int*>(ptr));
344 case QMetaType::UInt:
345 return v8::Integer::NewFromUnsigned(*reinterpret_cast<const uint*>(ptr));
346 case QMetaType::LongLong:
347 return v8::Number::New(*reinterpret_cast<const qlonglong*>(ptr));
348 case QMetaType::ULongLong:
349 return v8::Number::New(*reinterpret_cast<const qulonglong*>(ptr));
350 case QMetaType::Double:
351 return v8::Number::New(*reinterpret_cast<const double*>(ptr));
352 case QMetaType::QString:
353 return m_stringWrapper.toString(*reinterpret_cast<const QString*>(ptr));
354 case QMetaType::Float:
355 return v8::Number::New(*reinterpret_cast<const float*>(ptr));
356 case QMetaType::Short:
357 return v8::Integer::New(*reinterpret_cast<const short*>(ptr));
358 case QMetaType::UShort:
359 return v8::Integer::NewFromUnsigned(*reinterpret_cast<const unsigned short*>(ptr));
360 case QMetaType::Char:
361 return v8::Integer::New(*reinterpret_cast<const char*>(ptr));
362 case QMetaType::UChar:
363 return v8::Integer::NewFromUnsigned(*reinterpret_cast<const unsigned char*>(ptr));
364 case QMetaType::QChar:
365 return v8::Integer::New((*reinterpret_cast<const QChar*>(ptr)).unicode());
366 case QMetaType::QDateTime:
367 return v8::Date::New(qtDateTimeToJsDate(*reinterpret_cast<const QDateTime *>(ptr)));
368 case QMetaType::QDate:
369 return v8::Date::New(qtDateTimeToJsDate(QDateTime(*reinterpret_cast<const QDate *>(ptr))));
370 case QMetaType::QTime:
371 return v8::Date::New(qtDateTimeToJsDate(QDateTime(QDate(1970,1,1), *reinterpret_cast<const QTime *>(ptr))));
372 case QMetaType::QRegExp:
373 return QJSConverter::toRegExp(*reinterpret_cast<const QRegExp *>(ptr));
374 case QMetaType::QObjectStar:
375 return newQObject(*reinterpret_cast<QObject* const *>(ptr));
376 case QMetaType::QStringList:
378 bool succeeded = false;
379 v8::Handle<v8::Value> retn = m_sequenceWrapper.fromVariant(variant, &succeeded);
382 return arrayFromStringList(this, *reinterpret_cast<const QStringList *>(ptr));
384 case QMetaType::QVariantList:
385 return arrayFromVariantList(this, *reinterpret_cast<const QVariantList *>(ptr));
386 case QMetaType::QVariantMap:
387 return objectFromVariantMap(this, *reinterpret_cast<const QVariantMap *>(ptr));
388 case QMetaType::QJsonValue:
389 return jsonValueToJS(*reinterpret_cast<const QJsonValue *>(ptr));
390 case QMetaType::QJsonObject:
391 return jsonObjectToJS(*reinterpret_cast<const QJsonObject *>(ptr));
392 case QMetaType::QJsonArray:
393 return jsonArrayToJS(*reinterpret_cast<const QJsonArray *>(ptr));
399 if (QQmlValueType *vt = QQmlValueTypeFactory::valueType(type))
400 return m_valueTypeWrapper.newValueType(variant, vt);
402 if (type == qMetaTypeId<QQmlListReference>()) {
403 typedef QQmlListReferencePrivate QDLRP;
404 QDLRP *p = QDLRP::get((QQmlListReference*)ptr);
406 return m_listWrapper.newList(p->property, p->propertyType);
410 } else if (type == qMetaTypeId<QJSValue>()) {
411 const QJSValue *value = reinterpret_cast<const QJSValue *>(ptr);
412 QJSValuePrivate *valuep = QJSValuePrivate::get(*value);
413 if (valuep->assignEngine(this))
414 return v8::Local<v8::Value>::New(*valuep);
415 } else if (type == qMetaTypeId<QList<QObject *> >()) {
416 // XXX Can this be made more by using Array as a prototype and implementing
417 // directly against QList<QObject*>?
418 const QList<QObject *> &list = *(QList<QObject *>*)ptr;
419 v8::Local<v8::Array> array = v8::Array::New(list.count());
420 for (int ii = 0; ii < list.count(); ++ii)
421 array->Set(ii, newQObject(list.at(ii)));
426 QObject *obj = QQmlMetaType::toQObject(variant, &objOk);
428 return newQObject(obj);
430 bool succeeded = false;
431 v8::Handle<v8::Value> retn = m_sequenceWrapper.fromVariant(variant, &succeeded);
435 if (QQmlValueType *vt = QQmlValueTypeFactory::valueType(type))
436 return m_valueTypeWrapper.newValueType(variant, vt);
439 // XXX TODO: To be compatible, we still need to handle:
443 return m_variantWrapper.newVariant(variant);
446 // A handle scope and context must be entered
447 v8::Local<v8::Script> QV8Engine::qmlModeCompile(const QString &source,
448 const QString &fileName,
451 v8::Local<v8::String> v8source = m_stringWrapper.toString(source);
452 v8::Local<v8::String> v8fileName = m_stringWrapper.toString(fileName);
454 v8::ScriptOrigin origin(v8fileName, v8::Integer::New(lineNumber - 1));
456 v8::Local<v8::Script> script = v8::Script::Compile(v8source, &origin, 0, v8::Handle<v8::String>(),
457 v8::Script::QmlMode);
462 // A handle scope and context must be entered.
463 // source can be either ascii or utf8.
464 v8::Local<v8::Script> QV8Engine::qmlModeCompile(const char *source, int sourceLength,
465 const QString &fileName,
468 if (sourceLength == -1)
469 sourceLength = int(strlen(source));
471 v8::Local<v8::String> v8source = v8::String::New(source, sourceLength);
472 v8::Local<v8::String> v8fileName = m_stringWrapper.toString(fileName);
474 v8::ScriptOrigin origin(v8fileName, v8::Integer::New(lineNumber - 1));
476 v8::Local<v8::Script> script = v8::Script::Compile(v8source, &origin, 0, v8::Handle<v8::String>(),
477 v8::Script::QmlMode);
482 QNetworkAccessManager *QV8Engine::networkAccessManager()
484 return QQmlEnginePrivate::get(m_engine)->getNetworkAccessManager();
487 const QStringHash<bool> &QV8Engine::illegalNames() const
489 return m_illegalNames;
492 // Requires a handle scope
493 v8::Local<v8::Array> QV8Engine::getOwnPropertyNames(v8::Handle<v8::Object> o)
495 // FIXME Newer v8 have API for this function
497 v8::Handle<v8::Value> args[] = { o };
498 v8::Local<v8::Value> r = m_getOwnPropertyNames->Call(global(), 1, args);
500 return v8::Array::New();
502 return v8::Local<v8::Array>::Cast(r);
505 QQmlContextData *QV8Engine::callingContext()
507 return m_contextWrapper.callingContext();
510 // Converts a JS value to a QVariant.
511 // Null, Undefined -> QVariant() (invalid)
512 // Boolean -> QVariant(bool)
513 // Number -> QVariant(double)
514 // String -> QVariant(QString)
515 // Array -> QVariantList(...)
516 // Date -> QVariant(QDateTime)
517 // RegExp -> QVariant(QRegExp)
518 // [Any other object] -> QVariantMap(...)
519 QVariant QV8Engine::toBasicVariant(v8::Handle<v8::Value> value)
521 if (value->IsNull() || value->IsUndefined())
523 if (value->IsBoolean())
524 return value->ToBoolean()->Value();
525 if (value->IsInt32())
526 return value->ToInt32()->Value();
527 if (value->IsNumber())
528 return value->ToNumber()->Value();
529 if (value->IsString())
530 return m_stringWrapper.toString(value->ToString());
532 return qtDateTimeFromJsDate(v8::Handle<v8::Date>::Cast(value)->NumberValue());
533 // NOTE: since we convert QTime to JS Date, round trip will change the variant type (to QDateTime)!
535 Q_ASSERT(value->IsObject());
537 if (value->IsRegExp()) {
538 v8::Context::Scope scope(context());
539 return QJSConverter::toRegExp(v8::Handle<v8::RegExp>::Cast(value));
541 if (value->IsArray()) {
542 v8::Context::Scope scope(context());
545 v8::Handle<v8::Array> array = v8::Handle<v8::Array>::Cast(value);
546 int length = array->Length();
547 for (int ii = 0; ii < length; ++ii)
548 rv << toVariant(array->Get(ii), -1);
551 if (!value->IsFunction()) {
552 v8::Context::Scope scope(context());
553 v8::Handle<v8::Object> object = value->ToObject();
554 return variantMapFromJS(object);
562 struct StaticQtMetaObject : public QObject
564 static const QMetaObject *get()
565 { return &static_cast<StaticQtMetaObject*> (0)->staticQtMetaObject; }
568 void QV8Engine::initializeGlobal(v8::Handle<v8::Object> global)
570 using namespace QQmlBuiltinFunctions;
572 v8::Local<v8::Object> console = v8::Object::New();
573 v8::Local<v8::Function> consoleLogFn = V8FUNCTION(consoleLog, this);
575 console->Set(v8::String::New("debug"), consoleLogFn);
576 console->Set(v8::String::New("log"), consoleLogFn);
577 console->Set(v8::String::New("info"), consoleLogFn);
578 console->Set(v8::String::New("warn"), V8FUNCTION(consoleWarn, this));
579 console->Set(v8::String::New("error"), V8FUNCTION(consoleError, this));
580 console->Set(v8::String::New("assert"), V8FUNCTION(consoleAssert, this));
582 console->Set(v8::String::New("count"), V8FUNCTION(consoleCount, this));
583 console->Set(v8::String::New("profile"), V8FUNCTION(consoleProfile, this));
584 console->Set(v8::String::New("profileEnd"), V8FUNCTION(consoleProfileEnd, this));
585 console->Set(v8::String::New("time"), V8FUNCTION(consoleTime, this));
586 console->Set(v8::String::New("timeEnd"), V8FUNCTION(consoleTimeEnd, this));
587 console->Set(v8::String::New("trace"), V8FUNCTION(consoleTrace, this));
588 console->Set(v8::String::New("exception"), V8FUNCTION(consoleException, this));
590 v8::Local<v8::Object> qt = v8::Object::New();
592 // Set all the enums from the "Qt" namespace
593 const QMetaObject *qtMetaObject = StaticQtMetaObject::get();
594 for (int ii = 0; ii < qtMetaObject->enumeratorCount(); ++ii) {
595 QMetaEnum enumerator = qtMetaObject->enumerator(ii);
596 for (int jj = 0; jj < enumerator.keyCount(); ++jj) {
597 qt->Set(v8::String::New(enumerator.key(jj)), v8::Integer::New(enumerator.value(jj)));
600 qt->Set(v8::String::New("Asynchronous"), v8::Integer::New(0));
601 qt->Set(v8::String::New("Synchronous"), v8::Integer::New(1));
603 qt->Set(v8::String::New("include"), V8FUNCTION(QV8Include::include, this));
604 qt->Set(v8::String::New("isQtObject"), V8FUNCTION(isQtObject, this));
605 qt->Set(v8::String::New("rgba"), V8FUNCTION(rgba, this));
606 qt->Set(v8::String::New("hsla"), V8FUNCTION(hsla, this));
607 qt->Set(v8::String::New("colorEqual"), V8FUNCTION(colorEqual, this));
608 qt->Set(v8::String::New("font"), V8FUNCTION(font, this));
609 qt->Set(v8::String::New("rect"), V8FUNCTION(rect, this));
610 qt->Set(v8::String::New("point"), V8FUNCTION(point, this));
611 qt->Set(v8::String::New("size"), V8FUNCTION(size, this));
613 qt->Set(v8::String::New("vector2d"), V8FUNCTION(vector2d, this));
614 qt->Set(v8::String::New("vector3d"), V8FUNCTION(vector3d, this));
615 qt->Set(v8::String::New("vector4d"), V8FUNCTION(vector4d, this));
616 qt->Set(v8::String::New("quaternion"), V8FUNCTION(quaternion, this));
617 qt->Set(v8::String::New("matrix4x4"), V8FUNCTION(matrix4x4, this));
619 qt->Set(v8::String::New("formatDate"), V8FUNCTION(formatDate, this));
620 qt->Set(v8::String::New("formatTime"), V8FUNCTION(formatTime, this));
621 qt->Set(v8::String::New("formatDateTime"), V8FUNCTION(formatDateTime, this));
623 qt->Set(v8::String::New("openUrlExternally"), V8FUNCTION(openUrlExternally, this));
624 qt->Set(v8::String::New("fontFamilies"), V8FUNCTION(fontFamilies, this));
625 qt->Set(v8::String::New("md5"), V8FUNCTION(md5, this));
626 qt->Set(v8::String::New("btoa"), V8FUNCTION(btoa, this));
627 qt->Set(v8::String::New("atob"), V8FUNCTION(atob, this));
628 qt->Set(v8::String::New("resolvedUrl"), V8FUNCTION(resolvedUrl, this));
629 qt->Set(v8::String::New("locale"), V8FUNCTION(locale, this));
630 qt->Set(v8::String::New("binding"), V8FUNCTION(binding, this));
633 qt->SetAccessor(v8::String::New("application"), getApplication, 0, v8::External::New(this));
634 qt->SetAccessor(v8::String::New("inputMethod"), getInputMethod, 0, v8::External::New(this));
635 qt->Set(v8::String::New("lighter"), V8FUNCTION(lighter, this));
636 qt->Set(v8::String::New("darker"), V8FUNCTION(darker, this));
637 qt->Set(v8::String::New("tint"), V8FUNCTION(tint, this));
638 qt->Set(v8::String::New("quit"), V8FUNCTION(quit, this));
639 qt->Set(v8::String::New("createQmlObject"), V8FUNCTION(createQmlObject, this));
640 qt->Set(v8::String::New("createComponent"), V8FUNCTION(createComponent, this));
643 global->Set(v8::String::New("qsTranslate"), V8FUNCTION(qsTranslate, this));
644 global->Set(v8::String::New("QT_TRANSLATE_NOOP"), V8FUNCTION(qsTranslateNoOp, this));
645 global->Set(v8::String::New("qsTr"), V8FUNCTION(qsTr, this));
646 global->Set(v8::String::New("QT_TR_NOOP"), V8FUNCTION(qsTrNoOp, this));
647 global->Set(v8::String::New("qsTrId"), V8FUNCTION(qsTrId, this));
648 global->Set(v8::String::New("QT_TRID_NOOP"), V8FUNCTION(qsTrIdNoOp, this));
650 global->Set(v8::String::New("print"), consoleLogFn);
651 global->Set(v8::String::New("console"), console);
652 global->Set(v8::String::New("Qt"), qt);
653 global->Set(v8::String::New("gc"), V8FUNCTION(QQmlBuiltinFunctions::gc, this));
656 #define STRING_ARG "(function(stringArg) { "\
657 " String.prototype.arg = (function() {"\
658 " return stringArg.apply(this, arguments);"\
662 v8::Local<v8::Script> registerArg = v8::Script::New(v8::String::New(STRING_ARG), 0, 0, v8::Handle<v8::String>(), v8::Script::NativeMode);
663 v8::Local<v8::Value> result = registerArg->Run();
664 Q_ASSERT(result->IsFunction());
665 v8::Local<v8::Function> registerArgFunc = v8::Local<v8::Function>::Cast(result);
666 v8::Handle<v8::Value> args = V8FUNCTION(stringArg, this);
667 registerArgFunc->Call(v8::Local<v8::Object>::Cast(registerArgFunc), 1, &args);
671 QQmlLocale::registerStringLocaleCompare(this);
672 QQmlDateExtension::registerExtension(this);
673 QQmlNumberExtension::registerExtension(this);
675 qt_add_domexceptions(this);
676 m_xmlHttpRequestData = qt_add_qmlxmlhttprequest(this);
678 qt_add_sqlexceptions(this);
681 v8::Handle<v8::Value> args[] = { global };
682 v8::Local<v8::Value> names = m_getOwnPropertyNames->Call(global, 1, args);
683 v8::Local<v8::Array> namesArray = v8::Local<v8::Array>::Cast(names);
684 for (quint32 ii = 0; ii < namesArray->Length(); ++ii)
685 m_illegalNames.insert(toString(namesArray->Get(ii)), true);
689 #define FREEZE_SOURCE "(function freeze_recur(obj) { "\
690 " if (Qt.isQtObject(obj)) return;"\
691 " if (obj != Function.connect && obj != Function.disconnect && "\
692 " obj instanceof Object) {"\
693 " var properties = Object.getOwnPropertyNames(obj);"\
694 " for (var prop in properties) { "\
695 " if (prop == \"connect\" || prop == \"disconnect\") {"\
696 " Object.freeze(obj[prop]); "\
699 " freeze_recur(obj[prop]);"\
702 " if (obj instanceof Object) {"\
703 " Object.freeze(obj);"\
707 v8::Local<v8::Script> freeze = v8::Script::New(v8::String::New(FREEZE_SOURCE));
708 v8::Local<v8::Value> result = freeze->Run();
709 Q_ASSERT(result->IsFunction());
710 m_freezeObject = qPersistentNew(v8::Local<v8::Function>::Cast(result));
715 void QV8Engine::freezeObject(v8::Handle<v8::Value> value)
717 v8::Handle<v8::Value> args[] = { value };
718 m_freezeObject->Call(global(), 1, args);
723 v8::V8::LowMemoryNotification();
724 while (!v8::V8::IdleNotification()) {}
727 #ifdef QML_GLOBAL_HANDLE_DEBUGGING
728 #include <QtCore/qthreadstorage.h>
729 static QThreadStorage<QSet<void *> *> QV8Engine_activeHandles;
731 void QV8Engine::registerHandle(void *handle)
734 qWarning("Attempting to register a null handle");
738 if (!QV8Engine_activeHandles.hasLocalData())
739 QV8Engine_activeHandles.setLocalData(new QSet<void *>);
741 if (QV8Engine_activeHandles.localData()->contains(handle)) {
742 qFatal("Handle %p already alive", handle);
744 QV8Engine_activeHandles.localData()->insert(handle);
748 void QV8Engine::releaseHandle(void *handle)
753 if (!QV8Engine_activeHandles.hasLocalData())
754 QV8Engine_activeHandles.setLocalData(new QSet<void *>);
756 if (QV8Engine_activeHandles.localData()->contains(handle)) {
757 QV8Engine_activeHandles.localData()->remove(handle);
759 qFatal("Handle %p already dead", handle);
764 struct QV8EngineRegistrationData
766 QV8EngineRegistrationData() : extensionCount(0) {}
771 Q_GLOBAL_STATIC(QV8EngineRegistrationData, registrationData);
773 QMutex *QV8Engine::registrationMutex()
775 return ®istrationData()->mutex;
778 int QV8Engine::registerExtension()
780 return registrationData()->extensionCount++;
783 void QV8Engine::setExtensionData(int index, Deletable *data)
785 if (m_extensionData.count() <= index)
786 m_extensionData.resize(index + 1);
788 if (m_extensionData.at(index))
789 delete m_extensionData.at(index);
791 m_extensionData[index] = data;
794 double QV8Engine::qtDateTimeToJsDate(const QDateTime &dt)
800 return dt.toMSecsSinceEpoch();
803 QDateTime QV8Engine::qtDateTimeFromJsDate(double jsDate)
808 return QDateTime::fromMSecsSinceEpoch(jsDate);
811 v8::Persistent<v8::Object> *QV8Engine::findOwnerAndStrength(QObject *object, bool *shouldBeStrong)
813 QObject *parent = object->parent();
815 // if the object has JS ownership, the object's v8object owns the lifetime of the persistent value.
816 if (QQmlEngine::objectOwnership(object) == QQmlEngine::JavaScriptOwnership) {
817 *shouldBeStrong = false;
818 return &(QQmlData::get(object)->v8object);
821 // no parent, and has CPP ownership - doesn't have an implicit parent.
822 *shouldBeStrong = true;
826 // if it is owned by CPP, it's root parent may still be owned by JS.
827 // in that case, the owner of the persistent handle is the root parent's v8object.
828 while (parent->parent())
829 parent = parent->parent();
831 if (QQmlEngine::objectOwnership(parent) == QQmlEngine::JavaScriptOwnership) {
832 // root parent is owned by JS. It's v8object owns the persistent value in question.
833 *shouldBeStrong = false;
834 return &(QQmlData::get(parent)->v8object);
836 // root parent has CPP ownership. The persistent value should not be made weak.
837 *shouldBeStrong = true;
842 void QV8Engine::addRelationshipForGC(QObject *object, v8::Persistent<v8::Value> handle)
844 if (!object || handle.IsEmpty())
847 bool handleShouldBeStrong = false;
848 v8::Persistent<v8::Object> *implicitOwner = findOwnerAndStrength(object, &handleShouldBeStrong);
849 if (handleShouldBeStrong) {
850 v8::V8::AddImplicitReferences(m_strongReferencer, &handle, 1);
851 } else if (!implicitOwner->IsEmpty()) {
852 v8::V8::AddImplicitReferences(*implicitOwner, &handle, 1);
856 void QV8Engine::addRelationshipForGC(QObject *object, QObject *other)
858 if (!object || !other)
861 bool handleShouldBeStrong = false;
862 v8::Persistent<v8::Object> *implicitOwner = findOwnerAndStrength(object, &handleShouldBeStrong);
863 v8::Persistent<v8::Value> handle = QQmlData::get(other, true)->v8object;
864 if (handle.IsEmpty()) // no JS data to keep alive.
866 else if (handleShouldBeStrong)
867 v8::V8::AddImplicitReferences(m_strongReferencer, &handle, 1);
868 else if (!implicitOwner->IsEmpty())
869 v8::V8::AddImplicitReferences(*implicitOwner, &handle, 1);
872 static QThreadStorage<QV8Engine::ThreadData*> perThreadEngineData;
874 bool QV8Engine::hasThreadData()
876 return perThreadEngineData.hasLocalData();
879 QV8Engine::ThreadData *QV8Engine::threadData()
881 Q_ASSERT(perThreadEngineData.hasLocalData());
882 return perThreadEngineData.localData();
885 void QV8Engine::ensurePerThreadIsolate()
887 if (!perThreadEngineData.hasLocalData())
888 perThreadEngineData.setLocalData(new ThreadData);
891 void QV8Engine::initQmlGlobalObject()
893 v8::HandleScope handels;
894 v8::Context::Scope contextScope(m_context);
895 initializeGlobal(m_context->Global());
896 freezeObject(m_context->Global());
899 void QV8Engine::setEngine(QQmlEngine *engine)
902 initQmlGlobalObject();
905 v8::Handle<v8::Value> QV8Engine::throwException(v8::Handle<v8::Value> value)
907 v8::ThrowException(value);
911 // Converts a QVariantList to JS.
912 // The result is a new Array object with length equal to the length
913 // of the QVariantList, and the elements being the QVariantList's
914 // elements converted to JS, recursively.
915 v8::Local<v8::Array> QV8Engine::variantListToJS(const QVariantList &lst)
917 v8::Local<v8::Array> result = v8::Array::New(lst.size());
918 for (int i = 0; i < lst.size(); ++i)
919 result->Set(i, variantToJS(lst.at(i)));
923 // Converts a JS Array object to a QVariantList.
924 // The result is a QVariantList with length equal to the length
925 // of the JS Array, and elements being the JS Array's elements
926 // converted to QVariants, recursively.
927 QVariantList QV8Engine::variantListFromJS(v8::Handle<v8::Array> jsArray,
928 V8ObjectSet &visitedObjects)
931 if (visitedObjects.contains(jsArray))
932 return result; // Avoid recursion.
933 v8::HandleScope handleScope;
934 visitedObjects.insert(jsArray);
935 uint32_t length = jsArray->Length();
936 for (uint32_t i = 0; i < length; ++i)
937 result.append(variantFromJS(jsArray->Get(i), visitedObjects));
938 visitedObjects.remove(jsArray);
942 // Converts a QVariantMap to JS.
943 // The result is a new Object object with property names being
944 // the keys of the QVariantMap, and values being the values of
945 // the QVariantMap converted to JS, recursively.
946 v8::Local<v8::Object> QV8Engine::variantMapToJS(const QVariantMap &vmap)
948 v8::Local<v8::Object> result = v8::Object::New();
949 QVariantMap::const_iterator it;
950 for (it = vmap.constBegin(); it != vmap.constEnd(); ++it)
951 result->Set(QJSConverter::toString(it.key()), variantToJS(it.value()));
955 // Converts a JS Object to a QVariantMap.
956 // The result is a QVariantMap with keys being the property names
957 // of the object, and values being the values of the JS object's
958 // properties converted to QVariants, recursively.
959 QVariantMap QV8Engine::variantMapFromJS(v8::Handle<v8::Object> jsObject,
960 V8ObjectSet &visitedObjects)
964 v8::HandleScope handleScope;
965 v8::Handle<v8::Array> propertyNames = jsObject->GetPropertyNames();
966 uint32_t length = propertyNames->Length();
970 if (visitedObjects.contains(jsObject))
971 return result; // Avoid recursion.
973 visitedObjects.insert(jsObject);
974 // TODO: Only object's own property names. Include non-enumerable properties.
975 for (uint32_t i = 0; i < length; ++i) {
976 v8::Handle<v8::Value> name = propertyNames->Get(i);
977 result.insert(QJSConverter::toString(name->ToString()),
978 variantFromJS(jsObject->Get(name), visitedObjects));
980 visitedObjects.remove(jsObject);
984 // Converts the meta-type defined by the given type and data to JS.
985 // Returns the value if conversion succeeded, an empty handle otherwise.
986 v8::Handle<v8::Value> QV8Engine::metaTypeToJS(int type, const void *data)
989 v8::Handle<v8::Value> result;
991 // check if it's one of the types we know
992 switch (QMetaType::Type(type)) {
993 case QMetaType::UnknownType:
994 case QMetaType::Void:
995 return v8::Undefined();
996 case QMetaType::Bool:
997 return v8::Boolean::New(*reinterpret_cast<const bool*>(data));
999 return v8::Int32::New(*reinterpret_cast<const int*>(data));
1000 case QMetaType::UInt:
1001 return v8::Uint32::New(*reinterpret_cast<const uint*>(data));
1002 case QMetaType::LongLong:
1003 return v8::Number::New(double(*reinterpret_cast<const qlonglong*>(data)));
1004 case QMetaType::ULongLong:
1005 #if defined(Q_OS_WIN) && defined(_MSC_FULL_VER) && _MSC_FULL_VER <= 12008804
1006 #pragma message("** NOTE: You need the Visual Studio Processor Pack to compile support for 64bit unsigned integers.")
1007 return v8::Number::New(double((qlonglong)*reinterpret_cast<const qulonglong*>(data)));
1008 #elif defined(Q_CC_MSVC) && !defined(Q_CC_MSVC_NET)
1009 return v8::Number::New(double((qlonglong)*reinterpret_cast<const qulonglong*>(data)));
1011 return v8::Number::New(double(*reinterpret_cast<const qulonglong*>(data)));
1013 case QMetaType::Double:
1014 return v8::Number::New(double(*reinterpret_cast<const double*>(data)));
1015 case QMetaType::QString:
1016 return QJSConverter::toString(*reinterpret_cast<const QString*>(data));
1017 case QMetaType::Float:
1018 return v8::Number::New(*reinterpret_cast<const float*>(data));
1019 case QMetaType::Short:
1020 return v8::Int32::New(*reinterpret_cast<const short*>(data));
1021 case QMetaType::UShort:
1022 return v8::Uint32::New(*reinterpret_cast<const unsigned short*>(data));
1023 case QMetaType::Char:
1024 return v8::Int32::New(*reinterpret_cast<const char*>(data));
1025 case QMetaType::UChar:
1026 return v8::Uint32::New(*reinterpret_cast<const unsigned char*>(data));
1027 case QMetaType::QChar:
1028 return v8::Uint32::New((*reinterpret_cast<const QChar*>(data)).unicode());
1029 case QMetaType::QStringList:
1030 result = QJSConverter::toStringList(*reinterpret_cast<const QStringList *>(data));
1032 case QMetaType::QVariantList:
1033 result = variantListToJS(*reinterpret_cast<const QVariantList *>(data));
1035 case QMetaType::QVariantMap:
1036 result = variantMapToJS(*reinterpret_cast<const QVariantMap *>(data));
1038 case QMetaType::QDateTime:
1039 result = QJSConverter::toDateTime(*reinterpret_cast<const QDateTime *>(data));
1041 case QMetaType::QDate:
1042 result = QJSConverter::toDateTime(QDateTime(*reinterpret_cast<const QDate *>(data)));
1044 case QMetaType::QRegExp:
1045 result = QJSConverter::toRegExp(*reinterpret_cast<const QRegExp *>(data));
1047 case QMetaType::QObjectStar:
1048 result = newQObject(*reinterpret_cast<QObject* const *>(data));
1050 case QMetaType::QVariant:
1051 result = variantToJS(*reinterpret_cast<const QVariant*>(data));
1053 case QMetaType::QJsonValue:
1054 result = jsonValueToJS(*reinterpret_cast<const QJsonValue *>(data));
1056 case QMetaType::QJsonObject:
1057 result = jsonObjectToJS(*reinterpret_cast<const QJsonObject *>(data));
1059 case QMetaType::QJsonArray:
1060 result = jsonArrayToJS(*reinterpret_cast<const QJsonArray *>(data));
1063 if (type == qMetaTypeId<QJSValue>()) {
1064 return QJSValuePrivate::get(*reinterpret_cast<const QJSValue*>(data))->asV8Value(this);
1066 QByteArray typeName = QMetaType::typeName(type);
1067 if (typeName.endsWith('*') && !*reinterpret_cast<void* const *>(data)) {
1070 // Fall back to wrapping in a QVariant.
1071 result = newVariant(QVariant(type, data));
1078 // Converts a JS value to a meta-type.
1079 // data must point to a place that can store a value of the given type.
1080 // Returns true if conversion succeeded, false otherwise.
1081 bool QV8Engine::metaTypeFromJS(v8::Handle<v8::Value> value, int type, void *data) {
1082 // check if it's one of the types we know
1083 switch (QMetaType::Type(type)) {
1084 case QMetaType::Bool:
1085 *reinterpret_cast<bool*>(data) = value->ToBoolean()->Value();
1087 case QMetaType::Int:
1088 *reinterpret_cast<int*>(data) = value->ToInt32()->Value();
1090 case QMetaType::UInt:
1091 *reinterpret_cast<uint*>(data) = value->ToUint32()->Value();
1093 case QMetaType::LongLong:
1094 *reinterpret_cast<qlonglong*>(data) = qlonglong(value->ToInteger()->Value());
1096 case QMetaType::ULongLong:
1097 *reinterpret_cast<qulonglong*>(data) = qulonglong(value->ToInteger()->Value());
1099 case QMetaType::Double:
1100 *reinterpret_cast<double*>(data) = value->ToNumber()->Value();
1102 case QMetaType::QString:
1103 if (value->IsUndefined() || value->IsNull())
1104 *reinterpret_cast<QString*>(data) = QString();
1106 *reinterpret_cast<QString*>(data) = QJSConverter::toString(value->ToString());
1108 case QMetaType::Float:
1109 *reinterpret_cast<float*>(data) = value->ToNumber()->Value();
1111 case QMetaType::Short:
1112 *reinterpret_cast<short*>(data) = short(value->ToInt32()->Value());
1114 case QMetaType::UShort:
1115 *reinterpret_cast<unsigned short*>(data) = ushort(value->ToInt32()->Value()); // ### QScript::ToUInt16()
1117 case QMetaType::Char:
1118 *reinterpret_cast<char*>(data) = char(value->ToInt32()->Value());
1120 case QMetaType::UChar:
1121 *reinterpret_cast<unsigned char*>(data) = (unsigned char)(value->ToInt32()->Value());
1123 case QMetaType::QChar:
1124 if (value->IsString()) {
1125 QString str = QJSConverter::toString(v8::Handle<v8::String>::Cast(value));
1126 *reinterpret_cast<QChar*>(data) = str.isEmpty() ? QChar() : str.at(0);
1128 *reinterpret_cast<QChar*>(data) = QChar(ushort(value->ToInt32()->Value())); // ### QScript::ToUInt16()
1131 case QMetaType::QDateTime:
1132 if (value->IsDate()) {
1133 *reinterpret_cast<QDateTime *>(data) = QJSConverter::toDateTime(v8::Handle<v8::Date>::Cast(value));
1136 case QMetaType::QDate:
1137 if (value->IsDate()) {
1138 *reinterpret_cast<QDate *>(data) = QJSConverter::toDateTime(v8::Handle<v8::Date>::Cast(value)).date();
1141 case QMetaType::QRegExp:
1142 if (value->IsRegExp()) {
1143 *reinterpret_cast<QRegExp *>(data) = QJSConverter::toRegExp(v8::Handle<v8::RegExp>::Cast(value));
1146 case QMetaType::QObjectStar:
1147 if (isQObject(value) || value->IsNull()) {
1148 *reinterpret_cast<QObject* *>(data) = qtObjectFromJS(value);
1151 case QMetaType::QStringList:
1152 if (value->IsArray()) {
1153 *reinterpret_cast<QStringList *>(data) = QJSConverter::toStringList(v8::Handle<v8::Array>::Cast(value));
1156 case QMetaType::QVariantList:
1157 if (value->IsArray()) {
1158 *reinterpret_cast<QVariantList *>(data) = variantListFromJS(v8::Handle<v8::Array>::Cast(value));
1161 case QMetaType::QVariantMap:
1162 if (value->IsObject()) {
1163 *reinterpret_cast<QVariantMap *>(data) = variantMapFromJS(v8::Handle<v8::Object>::Cast(value));
1166 case QMetaType::QVariant:
1167 *reinterpret_cast<QVariant*>(data) = variantFromJS(value);
1169 case QMetaType::QJsonValue:
1170 *reinterpret_cast<QJsonValue *>(data) = jsonValueFromJS(value);
1172 case QMetaType::QJsonObject:
1173 *reinterpret_cast<QJsonObject *>(data) = jsonObjectFromJS(value);
1175 case QMetaType::QJsonArray:
1176 *reinterpret_cast<QJsonArray *>(data) = jsonArrayFromJS(value);
1183 if (isQtVariant(value)) {
1184 const QVariant &var = variantValue(value);
1185 // ### Enable once constructInPlace() is in qt master.
1186 if (var.userType() == type) {
1187 QMetaType::constructInPlace(type, data, var.constData());
1190 if (var.canConvert(type)) {
1193 Q_ASSERT(vv.userType() == type);
1194 QMetaType::constructInPlace(type, data, vv.constData());
1201 // Try to use magic; for compatibility with qscriptvalue_cast.
1203 QByteArray name = QMetaType::typeName(type);
1204 if (convertToNativeQObject(value, name, reinterpret_cast<void* *>(data)))
1206 if (isVariant(value) && name.endsWith('*')) {
1207 int valueType = QMetaType::type(name.left(name.size()-1));
1208 QVariant &var = variantValue(value);
1209 if (valueType == var.userType()) {
1210 // We have T t, T* is requested, so return &t.
1211 *reinterpret_cast<void* *>(data) = var.data();
1214 // Look in the prototype chain.
1215 v8::Handle<v8::Value> proto = value->ToObject()->GetPrototype();
1216 while (proto->IsObject()) {
1217 bool canCast = false;
1218 if (isVariant(proto)) {
1219 canCast = (type == variantValue(proto).userType())
1220 || (valueType && (valueType == variantValue(proto).userType()));
1222 else if (isQObject(proto)) {
1223 QByteArray className = name.left(name.size()-1);
1224 if (QObject *qobject = qtObjectFromJS(proto))
1225 canCast = qobject->qt_metacast(className) != 0;
1228 QByteArray varTypeName = QMetaType::typeName(var.userType());
1229 if (varTypeName.endsWith('*'))
1230 *reinterpret_cast<void* *>(data) = *reinterpret_cast<void* *>(var.data());
1232 *reinterpret_cast<void* *>(data) = var.data();
1235 proto = proto->ToObject()->GetPrototype();
1238 } else if (value->IsNull() && name.endsWith('*')) {
1239 *reinterpret_cast<void* *>(data) = 0;
1241 } else if (type == qMetaTypeId<QJSValue>()) {
1242 *reinterpret_cast<QJSValue*>(data) = QJSValuePrivate::get(new QJSValuePrivate(this, value));
1249 // Converts a QVariant to JS.
1250 v8::Handle<v8::Value> QV8Engine::variantToJS(const QVariant &value)
1252 return metaTypeToJS(value.userType(), value.constData());
1255 // Converts a JS value to a QVariant.
1256 // Undefined -> QVariant() (invalid)
1257 // Null -> QVariant((void*)0)
1258 // Boolean -> QVariant(bool)
1259 // Number -> QVariant(double)
1260 // String -> QVariant(QString)
1261 // Array -> QVariantList(...)
1262 // Date -> QVariant(QDateTime)
1263 // RegExp -> QVariant(QRegExp)
1264 // [Any other object] -> QVariantMap(...)
1265 QVariant QV8Engine::variantFromJS(v8::Handle<v8::Value> value,
1266 V8ObjectSet &visitedObjects)
1268 Q_ASSERT(!value.IsEmpty());
1269 if (value->IsUndefined())
1271 if (value->IsNull())
1272 return QVariant(QMetaType::VoidStar, 0);
1273 if (value->IsBoolean())
1274 return value->ToBoolean()->Value();
1275 if (value->IsInt32())
1276 return value->ToInt32()->Value();
1277 if (value->IsNumber())
1278 return value->ToNumber()->Value();
1279 if (value->IsString())
1280 return QJSConverter::toString(value->ToString());
1281 Q_ASSERT(value->IsObject());
1282 if (value->IsArray())
1283 return variantListFromJS(v8::Handle<v8::Array>::Cast(value), visitedObjects);
1284 if (value->IsDate())
1285 return QJSConverter::toDateTime(v8::Handle<v8::Date>::Cast(value));
1286 if (value->IsRegExp())
1287 return QJSConverter::toRegExp(v8::Handle<v8::RegExp>::Cast(value));
1288 if (isVariant(value))
1289 return variantValue(value);
1290 if (isQObject(value))
1291 return qVariantFromValue(qtObjectFromJS(value));
1292 if (isValueType(value))
1293 return toValueType(value);
1294 return variantMapFromJS(value->ToObject(), visitedObjects);
1297 v8::Handle<v8::Value> QV8Engine::jsonValueToJS(const QJsonValue &value)
1299 return m_jsonWrapper.fromJsonValue(value);
1302 QJsonValue QV8Engine::jsonValueFromJS(v8::Handle<v8::Value> value)
1304 return m_jsonWrapper.toJsonValue(value);
1307 v8::Local<v8::Object> QV8Engine::jsonObjectToJS(const QJsonObject &object)
1309 return m_jsonWrapper.fromJsonObject(object);
1312 QJsonObject QV8Engine::jsonObjectFromJS(v8::Handle<v8::Value> value)
1314 return m_jsonWrapper.toJsonObject(value);
1317 v8::Local<v8::Array> QV8Engine::jsonArrayToJS(const QJsonArray &array)
1319 return m_jsonWrapper.fromJsonArray(array);
1322 QJsonArray QV8Engine::jsonArrayFromJS(v8::Handle<v8::Value> value)
1324 return m_jsonWrapper.toJsonArray(value);
1327 bool QV8Engine::convertToNativeQObject(v8::Handle<v8::Value> value,
1328 const QByteArray &targetType,
1331 if (!targetType.endsWith('*'))
1333 if (QObject *qobject = qtObjectFromJS(value)) {
1334 int start = targetType.startsWith("const ") ? 6 : 0;
1335 QByteArray className = targetType.mid(start, targetType.size()-start-1);
1336 if (void *instance = qobject->qt_metacast(className)) {
1344 QObject *QV8Engine::qtObjectFromJS(v8::Handle<v8::Value> value)
1346 if (!value->IsObject())
1349 QV8ObjectResource *r = (QV8ObjectResource *)value->ToObject()->GetExternalResource();
1352 QV8ObjectResource::ResourceType type = r->resourceType();
1353 if (type == QV8ObjectResource::QObjectType)
1354 return qobjectWrapper()->toQObject(r);
1355 else if (type == QV8ObjectResource::VariantType) {
1356 QVariant variant = variantWrapper()->toVariant(r);
1357 int type = variant.userType();
1358 if (type == QMetaType::QObjectStar)
1359 return *reinterpret_cast<QObject* const *>(variant.constData());
1365 QVariant &QV8Engine::variantValue(v8::Handle<v8::Value> value)
1367 return variantWrapper()->variantValue(value);
1370 // Creates a QVariant wrapper object.
1371 v8::Local<v8::Object> QV8Engine::newVariant(const QVariant &value)
1373 return variantWrapper()->newVariant(value);
1376 QScriptPassPointer<QJSValuePrivate> QV8Engine::evaluate(v8::Handle<v8::Script> script, v8::TryCatch& tryCatch)
1378 v8::HandleScope handleScope;
1380 if (script.IsEmpty()) {
1381 v8::Handle<v8::Value> exception = tryCatch.Exception();
1382 if (exception.IsEmpty()) {
1383 // This is possible on syntax errors like { a:12, b:21 } <- missing "(", ")" around expression.
1384 return new QJSValuePrivate(this);
1386 return new QJSValuePrivate(this, exception);
1388 v8::Handle<v8::Value> result;
1389 result = script->Run();
1390 if (result.IsEmpty()) {
1391 v8::Handle<v8::Value> exception = tryCatch.Exception();
1392 // TODO: figure out why v8 doesn't always produce an exception value
1393 //Q_ASSERT(!exception.IsEmpty());
1394 if (exception.IsEmpty())
1395 exception = v8::Exception::Error(v8::String::New("missing exception value"));
1396 return new QJSValuePrivate(this, exception);
1398 return new QJSValuePrivate(this, result);
1401 QJSValue QV8Engine::scriptValueFromInternal(v8::Handle<v8::Value> value) const
1403 if (value.IsEmpty())
1404 return QJSValuePrivate::get(new QJSValuePrivate(const_cast<QV8Engine*>(this)));
1405 return QJSValuePrivate::get(new QJSValuePrivate(const_cast<QV8Engine*>(this), value));
1408 QScriptPassPointer<QJSValuePrivate> QV8Engine::newArray(uint length)
1410 return new QJSValuePrivate(this, v8::Array::New(length));
1413 void QV8Engine::startTimer(const QString &timerName)
1415 if (!m_time.isValid())
1417 m_startedTimers[timerName] = m_time.elapsed();
1420 qint64 QV8Engine::stopTimer(const QString &timerName, bool *wasRunning)
1422 if (!m_startedTimers.contains(timerName)) {
1423 *wasRunning = false;
1427 qint64 startedAt = m_startedTimers.take(timerName);
1428 return m_time.elapsed() - startedAt;
1431 int QV8Engine::consoleCountHelper(const QString &file, quint16 line, quint16 column)
1433 const QString key = file + QString::number(line) + QString::number(column);
1434 int number = m_consoleCount.value(key, 0);
1436 m_consoleCount.insert(key, number);
1440 v8::Handle<v8::Value> QV8Engine::getApplication(v8::Local<v8::String>, const v8::AccessorInfo &info)
1442 QV8Engine *engine = reinterpret_cast<QV8Engine*>(v8::External::Unwrap(info.Data()));
1443 if (!engine->m_application) {
1444 // Only allocate an application object once
1445 engine->m_application = QQml_guiProvider()->application(engine->m_engine);
1447 return engine->newQObject(engine->m_application);
1450 v8::Handle<v8::Value> QV8Engine::getInputMethod(v8::Local<v8::String>, const v8::AccessorInfo &info)
1452 QV8Engine *engine = reinterpret_cast<QV8Engine*>(v8::External::Unwrap(info.Data()));
1453 return engine->newQObject(QQml_guiProvider()->inputMethod(), CppOwnership);
1456 void QV8GCCallback::registerGcPrologueCallback()
1458 QV8Engine::ThreadData *td = QV8Engine::threadData();
1459 if (!td->gcPrologueCallbackRegistered) {
1460 td->gcPrologueCallbackRegistered = true;
1461 v8::V8::AddGCPrologueCallback(QV8GCCallback::garbageCollectorPrologueCallback, v8::kGCTypeMarkSweepCompact);
1465 QV8GCCallback::Node::Node(PrologueCallback callback)
1466 : prologueCallback(callback)
1470 QV8GCCallback::Node::~Node()
1476 Ensure that each persistent handle is strong if it has CPP ownership
1477 and has no implicitly JS owned object owner in its parent chain, and
1480 Any weak handle whose parent object is still alive will have an implicit
1481 reference (between the parent and the handle) added, so that it will
1484 Note that this callback is registered only for kGCTypeMarkSweepCompact
1485 collection cycles, as it is during collection cycles of that type
1486 in which weak persistent handle callbacks are called when required.
1488 void QV8GCCallback::garbageCollectorPrologueCallback(v8::GCType, v8::GCCallbackFlags)
1490 if (!QV8Engine::hasThreadData())
1493 QV8Engine::ThreadData *td = QV8Engine::threadData();
1494 QV8GCCallback::Node *currNode = td->gcCallbackNodes.first();
1497 // The client which adds itself to the list is responsible
1498 // for maintaining the correct implicit references in the
1499 // specified callback.
1500 currNode->prologueCallback(currNode);
1501 currNode = td->gcCallbackNodes.next(currNode);
1505 void QV8GCCallback::addGcCallbackNode(QV8GCCallback::Node *node)
1507 QV8Engine::ThreadData *td = QV8Engine::threadData();
1508 td->gcCallbackNodes.insert(node);
1511 QV8Engine::ThreadData::ThreadData()
1512 : gcPrologueCallbackRegistered(false)
1514 if (!v8::Isolate::GetCurrent()) {
1515 isolate = v8::Isolate::New();
1522 QV8Engine::ThreadData::~ThreadData()