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 QtDeclarative 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"
49 #include "../../../3rdparty/javascriptcore/DateMath.h"
51 #include <private/qdeclarativebuiltinfunctions_p.h>
52 #include <private/qdeclarativelist_p.h>
53 #include <private/qdeclarativeengine_p.h>
54 #include <private/qdeclarativeapplication_p.h>
55 #include <private/qdeclarativexmlhttprequest_p.h>
56 #include <private/qdeclarativesqldatabase_p.h>
57 #include <private/qdeclarativelocale_p.h>
59 #include "qscript_impl_p.h"
60 #include "qv8domerrors_p.h"
61 #include "qv8sqlerrors_p.h"
64 Q_DECLARE_METATYPE(QJSValue)
65 Q_DECLARE_METATYPE(QList<int>)
68 // XXX TODO: Need to check all the global functions will also work in a worker script where the
69 // QDeclarativeEngine is not available
72 static bool ObjectComparisonCallback(v8::Local<v8::Object> lhs, v8::Local<v8::Object> rhs)
77 QV8ObjectResource *lhsr = static_cast<QV8ObjectResource*>(lhs->GetExternalResource());
78 QV8ObjectResource *rhsr = static_cast<QV8ObjectResource*>(rhs->GetExternalResource());
80 Q_ASSERT(lhsr->engine == rhsr->engine);
83 QV8ObjectResource::ResourceType lhst = lhsr->resourceType();
84 QV8ObjectResource::ResourceType rhst = rhsr->resourceType();
87 case QV8ObjectResource::ValueTypeType:
88 // a value type might be equal to a variant or another value type
89 if (rhst == QV8ObjectResource::ValueTypeType) {
90 return lhsr->engine->valueTypeWrapper()->isEqual(lhsr, lhsr->engine->valueTypeWrapper()->toVariant(rhsr));
91 } else if (rhst == QV8ObjectResource::VariantType) {
92 return lhsr->engine->valueTypeWrapper()->isEqual(lhsr, lhsr->engine->variantWrapper()->toVariant(rhsr));
95 case QV8ObjectResource::VariantType:
96 // a variant might be equal to a value type or other variant.
97 if (rhst == QV8ObjectResource::VariantType) {
98 return lhsr->engine->variantWrapper()->toVariant(lhsr) ==
99 lhsr->engine->variantWrapper()->toVariant(rhsr);
100 } else if (rhst == QV8ObjectResource::ValueTypeType) {
101 return rhsr->engine->valueTypeWrapper()->isEqual(rhsr, rhsr->engine->variantWrapper()->toVariant(lhsr));
104 case QV8ObjectResource::SequenceType:
105 // a sequence might be equal to itself.
106 if (rhst == QV8ObjectResource::SequenceType) {
107 return lhsr->engine->sequenceWrapper()->isEqual(lhsr, rhsr);
119 QV8Engine::QV8Engine(QJSEngine* qq, QJSEngine::ContextOwnership ownership)
122 , m_ownsV8Context(ownership == QJSEngine::CreateNewContext)
123 , m_xmlHttpRequestData(0)
124 , m_sqlDatabaseData(0)
127 qMetaTypeId<QJSValue>();
128 qMetaTypeId<QList<int> >();
130 QByteArray v8args = qgetenv("V8ARGS");
131 // change default v8 behaviour to not relocate breakpoints across lines
132 if (!v8args.contains("breakpoint_relocation"))
133 v8args.append(" --nobreakpoint_relocation");
134 v8::V8::SetFlagsFromString(v8args.constData(), v8args.length());
136 ensurePerThreadIsolate();
138 v8::HandleScope handle_scope;
139 m_context = (ownership == QJSEngine::CreateNewContext) ? v8::Context::New() : v8::Persistent<v8::Context>::New(v8::Context::GetCurrent());
140 qPersistentRegister(m_context);
141 m_originalGlobalObject.init(m_context);
142 v8::Context::Scope context_scope(m_context);
144 v8::V8::SetUserObjectComparisonCallbackFunction(ObjectComparisonCallback);
145 QV8GCCallback::registerGcPrologueCallback();
146 m_strongReferencer = qPersistentNew(v8::Object::New());
148 m_stringWrapper.init();
149 m_contextWrapper.init(this);
150 m_qobjectWrapper.init(this);
151 m_typeWrapper.init(this);
152 m_listWrapper.init(this);
153 m_variantWrapper.init(this);
154 m_valueTypeWrapper.init(this);
155 m_sequenceWrapper.init(this);
158 v8::Handle<v8::Value> v = global()->Get(v8::String::New("Object"))->ToObject()->Get(v8::String::New("getOwnPropertyNames"));
159 m_getOwnPropertyNames = qPersistentNew<v8::Function>(v8::Handle<v8::Function>::Cast(v));
163 QV8Engine::~QV8Engine()
165 Q_ASSERT_X(v8::Isolate::GetCurrent(), "QV8Engine::~QV8Engine()", "called after v8::Isolate has exited");
166 for (int ii = 0; ii < m_extensionData.count(); ++ii)
167 delete m_extensionData[ii];
168 m_extensionData.clear();
170 qt_rem_qmlsqldatabase(this, m_sqlDatabaseData);
171 m_sqlDatabaseData = 0;
172 qt_rem_qmlxmlhttprequest(this, m_xmlHttpRequestData);
173 m_xmlHttpRequestData = 0;
174 delete m_listModelData;
177 qPersistentDispose(m_freezeObject);
178 qPersistentDispose(m_getOwnPropertyNames);
180 invalidateAllValues();
183 qPersistentDispose(m_strongReferencer);
185 m_sequenceWrapper.destroy();
186 m_valueTypeWrapper.destroy();
187 m_variantWrapper.destroy();
188 m_listWrapper.destroy();
189 m_typeWrapper.destroy();
190 m_qobjectWrapper.destroy();
191 m_contextWrapper.destroy();
192 m_stringWrapper.destroy();
194 m_originalGlobalObject.destroy();
197 qPersistentDispose(m_context);
200 QString QV8Engine::toStringStatic(v8::Handle<v8::Value> jsstr)
202 return toStringStatic(jsstr->ToString());
205 QString QV8Engine::toStringStatic(v8::Handle<v8::String> jsstr)
208 qstr.resize(jsstr->Length());
209 jsstr->Write((uint16_t*)qstr.data());
213 QVariant QV8Engine::toVariant(v8::Handle<v8::Value> value, int typeHint)
218 if (typeHint == QVariant::Bool)
219 return QVariant(value->BooleanValue());
221 if (value->IsObject()) {
222 QV8ObjectResource *r = (QV8ObjectResource *)value->ToObject()->GetExternalResource();
224 switch (r->resourceType()) {
225 case QV8ObjectResource::Context2DStyleType:
226 case QV8ObjectResource::Context2DPixelArrayType:
227 case QV8ObjectResource::SignalHandlerType:
228 case QV8ObjectResource::IncubatorType:
229 case QV8ObjectResource::VisualDataItemType:
230 case QV8ObjectResource::ContextType:
231 case QV8ObjectResource::XMLHttpRequestType:
232 case QV8ObjectResource::DOMNodeType:
233 case QV8ObjectResource::SQLDatabaseType:
234 case QV8ObjectResource::ListModelType:
235 case QV8ObjectResource::Context2DType:
236 case QV8ObjectResource::ParticleDataType:
237 case QV8ObjectResource::LocaleDataType:
239 case QV8ObjectResource::TypeType:
240 return m_typeWrapper.toVariant(r);
241 case QV8ObjectResource::QObjectType:
242 return qVariantFromValue<QObject *>(m_qobjectWrapper.toQObject(r));
243 case QV8ObjectResource::ListType:
244 return m_listWrapper.toVariant(r);
245 case QV8ObjectResource::VariantType:
246 return m_variantWrapper.toVariant(r);
247 case QV8ObjectResource::ValueTypeType:
248 return m_valueTypeWrapper.toVariant(r);
249 case QV8ObjectResource::SequenceType:
250 return m_sequenceWrapper.toVariant(r);
255 if (value->IsArray()) {
256 v8::Handle<v8::Array> array = v8::Handle<v8::Array>::Cast(value);
257 if (typeHint == qMetaTypeId<QList<QObject *> >()) {
258 QList<QObject *> list;
259 uint32_t length = array->Length();
260 for (uint32_t ii = 0; ii < length; ++ii) {
261 v8::Local<v8::Value> arrayItem = array->Get(ii);
262 if (arrayItem->IsObject()) {
263 list << toQObject(arrayItem->ToObject());
269 return qVariantFromValue<QList<QObject*> >(list);
272 bool succeeded = false;
273 QVariant retn = m_sequenceWrapper.toVariant(array, typeHint, &succeeded);
278 return toBasicVariant(value);
281 static v8::Handle<v8::Array> arrayFromStringList(QV8Engine *engine, const QStringList &list)
283 v8::Context::Scope scope(engine->context());
284 v8::Local<v8::Array> result = v8::Array::New(list.count());
285 for (int ii = 0; ii < list.count(); ++ii)
286 result->Set(ii, engine->toString(list.at(ii)));
290 static v8::Handle<v8::Array> arrayFromVariantList(QV8Engine *engine, const QVariantList &list)
292 v8::Context::Scope scope(engine->context());
293 v8::Local<v8::Array> result = v8::Array::New(list.count());
294 for (int ii = 0; ii < list.count(); ++ii)
295 result->Set(ii, engine->fromVariant(list.at(ii)));
299 static v8::Handle<v8::Object> objectFromVariantMap(QV8Engine *engine, const QVariantMap &map)
301 v8::Context::Scope scope(engine->context());
302 v8::Local<v8::Object> object = v8::Object::New();
303 for (QVariantMap::ConstIterator iter = map.begin(); iter != map.end(); ++iter)
304 object->Set(engine->toString(iter.key()), engine->fromVariant(iter.value()));
308 Q_CORE_EXPORT QString qt_regexp_toCanonical(const QString &, QRegExp::PatternSyntax);
310 v8::Handle<v8::Value> QV8Engine::fromVariant(const QVariant &variant)
312 int type = variant.userType();
313 const void *ptr = variant.constData();
315 if (type < QMetaType::User) {
316 switch (QMetaType::Type(type)) {
317 case QMetaType::Void:
318 return v8::Undefined();
319 case QMetaType::Bool:
320 return v8::Boolean::New(*reinterpret_cast<const bool*>(ptr));
322 return v8::Integer::New(*reinterpret_cast<const int*>(ptr));
323 case QMetaType::UInt:
324 return v8::Integer::NewFromUnsigned(*reinterpret_cast<const uint*>(ptr));
325 case QMetaType::LongLong:
326 return v8::Number::New(*reinterpret_cast<const qlonglong*>(ptr));
327 case QMetaType::ULongLong:
328 return v8::Number::New(*reinterpret_cast<const qulonglong*>(ptr));
329 case QMetaType::Double:
330 return v8::Number::New(*reinterpret_cast<const double*>(ptr));
331 case QMetaType::QString:
332 return m_stringWrapper.toString(*reinterpret_cast<const QString*>(ptr));
333 case QMetaType::Float:
334 return v8::Number::New(*reinterpret_cast<const float*>(ptr));
335 case QMetaType::Short:
336 return v8::Integer::New(*reinterpret_cast<const short*>(ptr));
337 case QMetaType::UShort:
338 return v8::Integer::NewFromUnsigned(*reinterpret_cast<const unsigned short*>(ptr));
339 case QMetaType::Char:
340 return v8::Integer::New(*reinterpret_cast<const char*>(ptr));
341 case QMetaType::UChar:
342 return v8::Integer::NewFromUnsigned(*reinterpret_cast<const unsigned char*>(ptr));
343 case QMetaType::QChar:
344 return v8::Integer::New((*reinterpret_cast<const QChar*>(ptr)).unicode());
345 case QMetaType::QDateTime:
346 return v8::Date::New(qtDateTimeToJsDate(*reinterpret_cast<const QDateTime *>(ptr)));
347 case QMetaType::QDate:
348 return v8::Date::New(qtDateTimeToJsDate(QDateTime(*reinterpret_cast<const QDate *>(ptr))));
349 case QMetaType::QTime:
350 return v8::Date::New(qtDateTimeToJsDate(QDateTime(QDate(1970,1,1), *reinterpret_cast<const QTime *>(ptr))));
351 case QMetaType::QRegExp:
352 return QJSConverter::toRegExp(*reinterpret_cast<const QRegExp *>(ptr));
353 case QMetaType::QObjectStar:
354 case QMetaType::QWidgetStar:
355 return newQObject(*reinterpret_cast<QObject* const *>(ptr));
356 case QMetaType::QStringList:
358 bool succeeded = false;
359 v8::Handle<v8::Value> retn = m_sequenceWrapper.fromVariant(variant, &succeeded);
362 return arrayFromStringList(this, *reinterpret_cast<const QStringList *>(ptr));
364 case QMetaType::QVariantList:
365 return arrayFromVariantList(this, *reinterpret_cast<const QVariantList *>(ptr));
366 case QMetaType::QVariantMap:
367 return objectFromVariantMap(this, *reinterpret_cast<const QVariantMap *>(ptr));
374 if (QDeclarativeValueType *vt = QDeclarativeEnginePrivate::get(m_engine)->valueTypes[type])
375 return m_valueTypeWrapper.newValueType(variant, vt);
379 if (type == qMetaTypeId<QDeclarativeListReference>()) {
380 typedef QDeclarativeListReferencePrivate QDLRP;
381 QDLRP *p = QDLRP::get((QDeclarativeListReference*)ptr);
383 return m_listWrapper.newList(p->property, p->propertyType);
387 } else if (type == qMetaTypeId<QJSValue>()) {
388 const QJSValue *value = reinterpret_cast<const QJSValue *>(ptr);
389 QJSValuePrivate *valuep = QJSValuePrivate::get(*value);
390 if (valuep->assignEngine(this))
391 return v8::Local<v8::Value>::New(*valuep);
392 } else if (type == qMetaTypeId<QList<QObject *> >()) {
393 // XXX Can this be made more by using Array as a prototype and implementing
394 // directly against QList<QObject*>?
395 const QList<QObject *> &list = *(QList<QObject *>*)ptr;
396 v8::Local<v8::Array> array = v8::Array::New(list.count());
397 for (int ii = 0; ii < list.count(); ++ii)
398 array->Set(ii, newQObject(list.at(ii)));
403 QObject *obj = QDeclarativeMetaType::toQObject(variant, &objOk);
405 return newQObject(obj);
407 bool succeeded = false;
408 v8::Handle<v8::Value> retn = m_sequenceWrapper.fromVariant(variant, &succeeded);
413 // XXX TODO: To be compatible, we still need to handle:
417 return m_variantWrapper.newVariant(variant);
420 // A handle scope and context must be entered
421 v8::Local<v8::Script> QV8Engine::qmlModeCompile(const QString &source, const QString &fileName, int lineNumber)
423 v8::Local<v8::String> v8source = m_stringWrapper.toString(source);
424 v8::Local<v8::String> v8fileName = m_stringWrapper.toString(fileName);
426 v8::ScriptOrigin origin(v8fileName, v8::Integer::New(lineNumber - 1));
428 v8::Local<v8::Script> script = v8::Script::Compile(v8source, &origin, 0, v8::Handle<v8::String>(),
429 v8::Script::QmlMode);
434 QNetworkAccessManager *QV8Engine::networkAccessManager()
436 return QDeclarativeEnginePrivate::get(m_engine)->getNetworkAccessManager();
439 const QStringHash<bool> &QV8Engine::illegalNames() const
441 return m_illegalNames;
444 // Requires a handle scope
445 v8::Local<v8::Array> QV8Engine::getOwnPropertyNames(v8::Handle<v8::Object> o)
447 // FIXME Newer v8 have API for this function
449 v8::Handle<v8::Value> args[] = { o };
450 v8::Local<v8::Value> r = m_getOwnPropertyNames->Call(global(), 1, args);
452 return v8::Array::New();
454 return v8::Local<v8::Array>::Cast(r);
457 QDeclarativeContextData *QV8Engine::callingContext()
459 return m_contextWrapper.callingContext();
462 // Converts a JS value to a QVariant.
463 // Null, Undefined -> QVariant() (invalid)
464 // Boolean -> QVariant(bool)
465 // Number -> QVariant(double)
466 // String -> QVariant(QString)
467 // Array -> QVariantList(...)
468 // Date -> QVariant(QDateTime)
469 // RegExp -> QVariant(QRegExp)
470 // [Any other object] -> QVariantMap(...)
471 QVariant QV8Engine::toBasicVariant(v8::Handle<v8::Value> value)
473 if (value->IsNull() || value->IsUndefined())
475 if (value->IsBoolean())
476 return value->ToBoolean()->Value();
477 if (value->IsInt32())
478 return value->ToInt32()->Value();
479 if (value->IsNumber())
480 return value->ToNumber()->Value();
481 if (value->IsString())
482 return m_stringWrapper.toString(value->ToString());
484 return qtDateTimeFromJsDate(v8::Handle<v8::Date>::Cast(value)->NumberValue());
485 // NOTE: since we convert QTime to JS Date, round trip will change the variant type (to QDateTime)!
487 Q_ASSERT(value->IsObject());
489 if (value->IsRegExp()) {
490 v8::Context::Scope scope(context());
491 return QJSConverter::toRegExp(v8::Handle<v8::RegExp>::Cast(value));
493 if (value->IsArray()) {
494 v8::Context::Scope scope(context());
497 v8::Handle<v8::Array> array = v8::Handle<v8::Array>::Cast(value);
498 int length = array->Length();
499 for (int ii = 0; ii < length; ++ii)
500 rv << toVariant(array->Get(ii), -1);
503 if (!value->IsFunction()) {
504 v8::Context::Scope scope(context());
505 v8::Handle<v8::Object> object = value->ToObject();
506 return variantMapFromJS(object);
514 #include <QtGui/qvector3d.h>
515 #include <QtGui/qvector4d.h>
517 struct StaticQtMetaObject : public QObject
519 static const QMetaObject *get()
520 { return &static_cast<StaticQtMetaObject*> (0)->staticQtMetaObject; }
523 void QV8Engine::initializeGlobal(v8::Handle<v8::Object> global)
525 using namespace QDeclarativeBuiltinFunctions;
527 v8::Local<v8::Object> console = v8::Object::New();
528 v8::Local<v8::Function> consoleLogFn = V8FUNCTION(consoleLog, this);
530 console->Set(v8::String::New("debug"), consoleLogFn);
531 console->Set(v8::String::New("log"), consoleLogFn);
532 console->Set(v8::String::New("info"), consoleLogFn);
533 console->Set(v8::String::New("warn"), V8FUNCTION(consoleWarn, this));
534 console->Set(v8::String::New("error"), V8FUNCTION(consoleError, this));
535 console->Set(v8::String::New("assert"), V8FUNCTION(consoleAssert, this));
537 console->Set(v8::String::New("count"), V8FUNCTION(consoleCount, this));
538 console->Set(v8::String::New("profile"), V8FUNCTION(consoleProfile, this));
539 console->Set(v8::String::New("profileEnd"), V8FUNCTION(consoleProfileEnd, this));
540 console->Set(v8::String::New("time"), V8FUNCTION(consoleTime, this));
541 console->Set(v8::String::New("timeEnd"), V8FUNCTION(consoleTimeEnd, this));
542 console->Set(v8::String::New("trace"), V8FUNCTION(consoleTrace, this));
543 console->Set(v8::String::New("exception"), V8FUNCTION(consoleException, this));
545 v8::Local<v8::Object> qt = v8::Object::New();
547 // Set all the enums from the "Qt" namespace
548 const QMetaObject *qtMetaObject = StaticQtMetaObject::get();
549 for (int ii = 0; ii < qtMetaObject->enumeratorCount(); ++ii) {
550 QMetaEnum enumerator = qtMetaObject->enumerator(ii);
551 for (int jj = 0; jj < enumerator.keyCount(); ++jj) {
552 qt->Set(v8::String::New(enumerator.key(jj)), v8::Integer::New(enumerator.value(jj)));
555 qt->Set(v8::String::New("Asynchronous"), v8::Integer::New(0));
556 qt->Set(v8::String::New("Synchronous"), v8::Integer::New(1));
558 qt->Set(v8::String::New("include"), V8FUNCTION(QV8Include::include, this));
559 qt->Set(v8::String::New("isQtObject"), V8FUNCTION(isQtObject, this));
560 qt->Set(v8::String::New("rgba"), V8FUNCTION(rgba, this));
561 qt->Set(v8::String::New("hsla"), V8FUNCTION(hsla, this));
562 qt->Set(v8::String::New("rect"), V8FUNCTION(rect, this));
563 qt->Set(v8::String::New("point"), V8FUNCTION(point, this));
564 qt->Set(v8::String::New("size"), V8FUNCTION(size, this));
565 qt->Set(v8::String::New("vector3d"), V8FUNCTION(vector3d, this));
566 qt->Set(v8::String::New("vector4d"), V8FUNCTION(vector4d, this));
568 qt->Set(v8::String::New("formatDate"), V8FUNCTION(formatDate, this));
569 qt->Set(v8::String::New("formatTime"), V8FUNCTION(formatTime, this));
570 qt->Set(v8::String::New("formatDateTime"), V8FUNCTION(formatDateTime, this));
572 qt->Set(v8::String::New("openUrlExternally"), V8FUNCTION(openUrlExternally, this));
573 qt->Set(v8::String::New("fontFamilies"), V8FUNCTION(fontFamilies, this));
574 qt->Set(v8::String::New("md5"), V8FUNCTION(md5, this));
575 qt->Set(v8::String::New("btoa"), V8FUNCTION(btoa, this));
576 qt->Set(v8::String::New("atob"), V8FUNCTION(atob, this));
577 qt->Set(v8::String::New("resolvedUrl"), V8FUNCTION(resolvedUrl, this));
578 qt->Set(v8::String::New("locale"), V8FUNCTION(locale, this));
581 qt->Set(v8::String::New("application"), newQObject(new QDeclarativeApplication(m_engine)));
582 qt->Set(v8::String::New("lighter"), V8FUNCTION(lighter, this));
583 qt->Set(v8::String::New("darker"), V8FUNCTION(darker, this));
584 qt->Set(v8::String::New("tint"), V8FUNCTION(tint, this));
585 qt->Set(v8::String::New("quit"), V8FUNCTION(quit, this));
586 qt->Set(v8::String::New("createQmlObject"), V8FUNCTION(createQmlObject, this));
587 qt->Set(v8::String::New("createComponent"), V8FUNCTION(createComponent, this));
590 global->Set(v8::String::New("qsTranslate"), V8FUNCTION(qsTranslate, this));
591 global->Set(v8::String::New("QT_TRANSLATE_NOOP"), V8FUNCTION(qsTranslateNoOp, this));
592 global->Set(v8::String::New("qsTr"), V8FUNCTION(qsTr, this));
593 global->Set(v8::String::New("QT_TR_NOOP"), V8FUNCTION(qsTrNoOp, this));
594 global->Set(v8::String::New("qsTrId"), V8FUNCTION(qsTrId, this));
595 global->Set(v8::String::New("QT_TRID_NOOP"), V8FUNCTION(qsTrIdNoOp, this));
597 global->Set(v8::String::New("print"), consoleLogFn);
598 global->Set(v8::String::New("console"), console);
599 global->Set(v8::String::New("Qt"), qt);
600 global->Set(v8::String::New("gc"), V8FUNCTION(QDeclarativeBuiltinFunctions::gc, this));
603 #define STRING_ARG "(function(stringArg) { "\
604 " String.prototype.arg = (function() {"\
605 " return stringArg.apply(this, arguments);"\
609 v8::Local<v8::Script> registerArg = v8::Script::New(v8::String::New(STRING_ARG), 0, 0, v8::Handle<v8::String>(), v8::Script::NativeMode);
610 v8::Local<v8::Value> result = registerArg->Run();
611 Q_ASSERT(result->IsFunction());
612 v8::Local<v8::Function> registerArgFunc = v8::Local<v8::Function>::Cast(result);
613 v8::Handle<v8::Value> args = V8FUNCTION(stringArg, this);
614 registerArgFunc->Call(v8::Local<v8::Object>::Cast(registerArgFunc), 1, &args);
618 QDeclarativeDateExtension::registerExtension(this);
619 QDeclarativeNumberExtension::registerExtension(this);
621 qt_add_domexceptions(this);
622 m_xmlHttpRequestData = qt_add_qmlxmlhttprequest(this);
624 qt_add_sqlexceptions(this);
625 m_sqlDatabaseData = qt_add_qmlsqldatabase(this);
628 v8::Handle<v8::Value> args[] = { global };
629 v8::Local<v8::Value> names = m_getOwnPropertyNames->Call(global, 1, args);
630 v8::Local<v8::Array> namesArray = v8::Local<v8::Array>::Cast(names);
631 for (quint32 ii = 0; ii < namesArray->Length(); ++ii)
632 m_illegalNames.insert(toString(namesArray->Get(ii)), true);
636 #define FREEZE_SOURCE "(function freeze_recur(obj) { "\
637 " if (Qt.isQtObject(obj)) return;"\
638 " if (obj != Function.connect && obj != Function.disconnect && "\
639 " obj instanceof Object) {"\
640 " var properties = Object.getOwnPropertyNames(obj);"\
641 " for (var prop in properties) { "\
642 " if (prop == \"connect\" || prop == \"disconnect\") {"\
643 " Object.freeze(obj[prop]); "\
646 " freeze_recur(obj[prop]);"\
649 " if (obj instanceof Object) {"\
650 " Object.freeze(obj);"\
654 v8::Local<v8::Script> freeze = v8::Script::New(v8::String::New(FREEZE_SOURCE));
655 v8::Local<v8::Value> result = freeze->Run();
656 Q_ASSERT(result->IsFunction());
657 m_freezeObject = qPersistentNew(v8::Local<v8::Function>::Cast(result));
662 void QV8Engine::freezeObject(v8::Handle<v8::Value> value)
664 v8::Handle<v8::Value> args[] = { value };
665 m_freezeObject->Call(global(), 1, args);
670 v8::V8::LowMemoryNotification();
671 while (!v8::V8::IdleNotification()) {}
674 #ifdef QML_GLOBAL_HANDLE_DEBUGGING
675 #include <QtCore/qthreadstorage.h>
676 static QThreadStorage<QSet<void *> *> QV8Engine_activeHandles;
678 void QV8Engine::registerHandle(void *handle)
681 qWarning("Attempting to register a null handle");
685 if (!QV8Engine_activeHandles.hasLocalData())
686 QV8Engine_activeHandles.setLocalData(new QSet<void *>);
688 if (QV8Engine_activeHandles.localData()->contains(handle)) {
689 qFatal("Handle %p already alive", handle);
691 QV8Engine_activeHandles.localData()->insert(handle);
695 void QV8Engine::releaseHandle(void *handle)
700 if (!QV8Engine_activeHandles.hasLocalData())
701 QV8Engine_activeHandles.setLocalData(new QSet<void *>);
703 if (QV8Engine_activeHandles.localData()->contains(handle)) {
704 QV8Engine_activeHandles.localData()->remove(handle);
706 qFatal("Handle %p already dead", handle);
711 struct QV8EngineRegistrationData
713 QV8EngineRegistrationData() : extensionCount(0) {}
718 Q_GLOBAL_STATIC(QV8EngineRegistrationData, registrationData);
720 QMutex *QV8Engine::registrationMutex()
722 return ®istrationData()->mutex;
725 int QV8Engine::registerExtension()
727 return registrationData()->extensionCount++;
730 void QV8Engine::setExtensionData(int index, Deletable *data)
732 if (m_extensionData.count() <= index)
733 m_extensionData.resize(index + 1);
735 if (m_extensionData.at(index))
736 delete m_extensionData.at(index);
738 m_extensionData[index] = data;
741 double QV8Engine::qtDateTimeToJsDate(const QDateTime &dt)
743 // from QScriptEngine::DateTimeToMs()
747 QDateTime utc = dt.toUTC();
748 QDate date = utc.date();
749 QTime time = utc.time();
750 QV8DateConverter::JSC::GregorianDateTime tm;
751 tm.year = date.year() - 1900;
752 tm.month = date.month() - 1;
753 tm.monthDay = date.day();
754 tm.weekDay = date.dayOfWeek();
755 tm.yearDay = date.dayOfYear();
756 tm.hour = time.hour();
757 tm.minute = time.minute();
758 tm.second = time.second();
759 return QV8DateConverter::JSC::gregorianDateTimeToMS(tm, time.msec());
762 v8::Persistent<v8::Object> *QV8Engine::findOwnerAndStrength(QObject *object, bool *shouldBeStrong)
764 QObject *parent = object->parent();
766 // if the object has JS ownership, the object's v8object owns the lifetime of the persistent value.
767 if (QDeclarativeEngine::objectOwnership(object) == QDeclarativeEngine::JavaScriptOwnership) {
768 *shouldBeStrong = false;
769 return &(QDeclarativeData::get(object)->v8object);
772 // no parent, and has CPP ownership - doesn't have an implicit parent.
773 *shouldBeStrong = true;
777 // if it is owned by CPP, it's root parent may still be owned by JS.
778 // in that case, the owner of the persistent handle is the root parent's v8object.
779 while (parent->parent())
780 parent = parent->parent();
782 if (QDeclarativeEngine::objectOwnership(parent) == QDeclarativeEngine::JavaScriptOwnership) {
783 // root parent is owned by JS. It's v8object owns the persistent value in question.
784 *shouldBeStrong = false;
785 return &(QDeclarativeData::get(parent)->v8object);
787 // root parent has CPP ownership. The persistent value should not be made weak.
788 *shouldBeStrong = true;
793 QDateTime QV8Engine::qtDateTimeFromJsDate(double jsDate)
795 // from QScriptEngine::MsToDateTime()
798 QV8DateConverter::JSC::GregorianDateTime tm;
799 QV8DateConverter::JSC::msToGregorianDateTime(jsDate, tm);
801 // from QScriptEngine::MsFromTime()
802 int ms = int(::fmod(jsDate, 1000.0));
806 QDateTime convertedUTC = QDateTime(QDate(tm.year + 1900, tm.month + 1, tm.monthDay),
807 QTime(tm.hour, tm.minute, tm.second, ms), Qt::UTC);
808 return convertedUTC.toLocalTime();
811 void QV8Engine::addRelationshipForGC(QObject *object, v8::Persistent<v8::Value> handle)
813 if (handle.IsEmpty())
816 bool handleShouldBeStrong = false;
817 v8::Persistent<v8::Object> *implicitOwner = findOwnerAndStrength(object, &handleShouldBeStrong);
818 if (handleShouldBeStrong) {
819 v8::V8::AddImplicitReferences(m_strongReferencer, &handle, 1);
820 } else if (!implicitOwner->IsEmpty()) {
821 v8::V8::AddImplicitReferences(*implicitOwner, &handle, 1);
825 void QV8Engine::addRelationshipForGC(QObject *object, QObject *other)
827 bool handleShouldBeStrong = false;
828 v8::Persistent<v8::Object> *implicitOwner = findOwnerAndStrength(object, &handleShouldBeStrong);
829 v8::Persistent<v8::Value> handle = QDeclarativeData::get(other, true)->v8object;
830 if (handleShouldBeStrong) {
831 v8::V8::AddImplicitReferences(m_strongReferencer, &handle, 1);
832 } else if (!implicitOwner->IsEmpty()) {
833 v8::V8::AddImplicitReferences(*implicitOwner, &handle, 1);
837 static QThreadStorage<QV8Engine::ThreadData*> perThreadEngineData;
839 bool QV8Engine::hasThreadData()
841 return perThreadEngineData.hasLocalData();
844 QV8Engine::ThreadData *QV8Engine::threadData()
846 Q_ASSERT(perThreadEngineData.hasLocalData());
847 return perThreadEngineData.localData();
850 void QV8Engine::ensurePerThreadIsolate()
852 if (!perThreadEngineData.hasLocalData())
853 perThreadEngineData.setLocalData(new ThreadData);
856 void QV8Engine::initDeclarativeGlobalObject()
858 v8::HandleScope handels;
859 v8::Context::Scope contextScope(m_context);
860 initializeGlobal(m_context->Global());
861 freezeObject(m_context->Global());
864 void QV8Engine::setEngine(QDeclarativeEngine *engine)
867 initDeclarativeGlobalObject();
870 void QV8Engine::setException(v8::Handle<v8::Value> value, v8::Handle<v8::Message> msg)
872 m_exception.set(value, msg);
875 v8::Handle<v8::Value> QV8Engine::throwException(v8::Handle<v8::Value> value)
878 v8::ThrowException(value);
882 void QV8Engine::clearExceptions()
887 v8::Handle<v8::Value> QV8Engine::uncaughtException() const
889 if (!hasUncaughtException())
890 return v8::Handle<v8::Value>();
894 bool QV8Engine::hasUncaughtException() const
899 int QV8Engine::uncaughtExceptionLineNumber() const
901 return m_exception.lineNumber();
904 QStringList QV8Engine::uncaughtExceptionBacktrace() const
906 return m_exception.backtrace();
911 Save the current exception on stack so it can be set again later.
912 \sa QV8Engine::restoreException
914 void QV8Engine::saveException()
921 Load a saved exception from stack. Current exception, if exists will be dropped
922 \sa QV8Engine::saveException
924 void QV8Engine::restoreException()
929 QV8Engine::Exception::Exception() {}
931 QV8Engine::Exception::~Exception()
933 Q_ASSERT_X(m_stack.isEmpty(), Q_FUNC_INFO, "Some saved exceptions left. Asymetric pop/push found.");
937 void QV8Engine::Exception::set(v8::Handle<v8::Value> value, v8::Handle<v8::Message> message)
939 Q_ASSERT_X(!value.IsEmpty(), Q_FUNC_INFO, "Throwing an empty value handle is highly suspected");
941 m_value = v8::Persistent<v8::Value>::New(value);
942 m_message = v8::Persistent<v8::Message>::New(message);
945 void QV8Engine::Exception::clear()
953 QV8Engine::Exception::operator bool() const
955 return !m_value.IsEmpty();
958 QV8Engine::Exception::operator v8::Handle<v8::Value>() const
964 int QV8Engine::Exception::lineNumber() const
966 if (m_message.IsEmpty())
968 return m_message->GetLineNumber();
971 QStringList QV8Engine::Exception::backtrace() const
973 if (m_message.IsEmpty())
974 return QStringList();
976 QStringList backtrace;
977 v8::Handle<v8::StackTrace> trace = m_message->GetStackTrace();
979 // FIXME it should not happen (SetCaptureStackTraceForUncaughtExceptions is called).
980 return QStringList();
982 for (int i = 0; i < trace->GetFrameCount(); ++i) {
983 v8::Local<v8::StackFrame> frame = trace->GetFrame(i);
984 backtrace.append(QJSConverter::toString(frame->GetFunctionName()));
985 backtrace.append(QJSConverter::toString(frame->GetFunctionName()));
986 backtrace.append(QString::fromAscii("()@"));
987 backtrace.append(QJSConverter::toString(frame->GetScriptName()));
988 backtrace.append(QString::fromAscii(":"));
989 backtrace.append(QString::number(frame->GetLineNumber()));
994 void QV8Engine::Exception::push()
996 m_stack.push(qMakePair(m_value, m_message));
1001 void QV8Engine::Exception::pop()
1003 Q_ASSERT_X(!m_stack.empty(), Q_FUNC_INFO, "Attempt to load unsaved exception found");
1004 ValueMessagePair pair = m_stack.pop();
1006 m_value = pair.first;
1007 m_message = pair.second;
1010 QScriptPassPointer<QJSValuePrivate> QV8Engine::newRegExp(const QString &pattern, const QString &flags)
1012 int f = v8::RegExp::kNone;
1014 QString::const_iterator i = flags.constBegin();
1015 for (; i != flags.constEnd(); ++i) {
1016 switch (i->unicode()) {
1018 f |= v8::RegExp::kIgnoreCase;
1021 f |= v8::RegExp::kMultiline;
1024 f |= v8::RegExp::kGlobal;
1028 // ignore a Syntax Error.
1033 v8::Handle<v8::RegExp> regexp = v8::RegExp::New(QJSConverter::toString(pattern), static_cast<v8::RegExp::Flags>(f));
1034 return new QJSValuePrivate(this, regexp);
1037 QScriptPassPointer<QJSValuePrivate> QV8Engine::newRegExp(const QRegExp ®exp)
1039 return new QJSValuePrivate(this, QJSConverter::toRegExp(regexp));
1043 // Converts a QVariantList to JS.
1044 // The result is a new Array object with length equal to the length
1045 // of the QVariantList, and the elements being the QVariantList's
1046 // elements converted to JS, recursively.
1047 v8::Local<v8::Array> QV8Engine::variantListToJS(const QVariantList &lst)
1049 v8::Local<v8::Array> result = v8::Array::New(lst.size());
1050 for (int i = 0; i < lst.size(); ++i)
1051 result->Set(i, variantToJS(lst.at(i)));
1055 // Converts a JS Array object to a QVariantList.
1056 // The result is a QVariantList with length equal to the length
1057 // of the JS Array, and elements being the JS Array's elements
1058 // converted to QVariants, recursively.
1059 QVariantList QV8Engine::variantListFromJS(v8::Handle<v8::Array> jsArray)
1061 QVariantList result;
1062 int hash = jsArray->GetIdentityHash();
1063 if (visitedConversionObjects.contains(hash))
1064 return result; // Avoid recursion.
1065 v8::HandleScope handleScope;
1066 visitedConversionObjects.insert(hash);
1067 uint32_t length = jsArray->Length();
1068 for (uint32_t i = 0; i < length; ++i)
1069 result.append(variantFromJS(jsArray->Get(i)));
1070 visitedConversionObjects.remove(hash);
1074 // Converts a QVariantMap to JS.
1075 // The result is a new Object object with property names being
1076 // the keys of the QVariantMap, and values being the values of
1077 // the QVariantMap converted to JS, recursively.
1078 v8::Local<v8::Object> QV8Engine::variantMapToJS(const QVariantMap &vmap)
1080 v8::Local<v8::Object> result = v8::Object::New();
1081 QVariantMap::const_iterator it;
1082 for (it = vmap.constBegin(); it != vmap.constEnd(); ++it)
1083 result->Set(QJSConverter::toString(it.key()), variantToJS(it.value()));
1087 // Converts a JS Object to a QVariantMap.
1088 // The result is a QVariantMap with keys being the property names
1089 // of the object, and values being the values of the JS object's
1090 // properties converted to QVariants, recursively.
1091 QVariantMap QV8Engine::variantMapFromJS(v8::Handle<v8::Object> jsObject)
1095 v8::HandleScope handleScope;
1096 v8::Handle<v8::Array> propertyNames = jsObject->GetPropertyNames();
1097 uint32_t length = propertyNames->Length();
1101 int hash = jsObject->GetIdentityHash();
1102 if (visitedConversionObjects.contains(hash))
1103 return result; // Avoid recursion.
1105 visitedConversionObjects.insert(hash);
1106 // TODO: Only object's own property names. Include non-enumerable properties.
1107 for (uint32_t i = 0; i < length; ++i) {
1108 v8::Handle<v8::Value> name = propertyNames->Get(i);
1109 result.insert(QJSConverter::toString(name->ToString()), variantFromJS(jsObject->Get(name)));
1111 visitedConversionObjects.remove(hash);
1115 // Converts the meta-type defined by the given type and data to JS.
1116 // Returns the value if conversion succeeded, an empty handle otherwise.
1117 v8::Handle<v8::Value> QV8Engine::metaTypeToJS(int type, const void *data)
1119 Q_ASSERT(data != 0);
1120 v8::Handle<v8::Value> result;
1122 // check if it's one of the types we know
1123 switch (QMetaType::Type(type)) {
1124 case QMetaType::Void:
1125 return v8::Undefined();
1126 case QMetaType::Bool:
1127 return v8::Boolean::New(*reinterpret_cast<const bool*>(data));
1128 case QMetaType::Int:
1129 return v8::Int32::New(*reinterpret_cast<const int*>(data));
1130 case QMetaType::UInt:
1131 return v8::Uint32::New(*reinterpret_cast<const uint*>(data));
1132 case QMetaType::LongLong:
1133 return v8::Number::New(double(*reinterpret_cast<const qlonglong*>(data)));
1134 case QMetaType::ULongLong:
1135 #if defined(Q_OS_WIN) && defined(_MSC_FULL_VER) && _MSC_FULL_VER <= 12008804
1136 #pragma message("** NOTE: You need the Visual Studio Processor Pack to compile support for 64bit unsigned integers.")
1137 return v8::Number::New(double((qlonglong)*reinterpret_cast<const qulonglong*>(data)));
1138 #elif defined(Q_CC_MSVC) && !defined(Q_CC_MSVC_NET)
1139 return v8::Number::New(double((qlonglong)*reinterpret_cast<const qulonglong*>(data)));
1141 return v8::Number::New(double(*reinterpret_cast<const qulonglong*>(data)));
1143 case QMetaType::Double:
1144 return v8::Number::New(double(*reinterpret_cast<const double*>(data)));
1145 case QMetaType::QString:
1146 return QJSConverter::toString(*reinterpret_cast<const QString*>(data));
1147 case QMetaType::Float:
1148 return v8::Number::New(*reinterpret_cast<const float*>(data));
1149 case QMetaType::Short:
1150 return v8::Int32::New(*reinterpret_cast<const short*>(data));
1151 case QMetaType::UShort:
1152 return v8::Uint32::New(*reinterpret_cast<const unsigned short*>(data));
1153 case QMetaType::Char:
1154 return v8::Int32::New(*reinterpret_cast<const char*>(data));
1155 case QMetaType::UChar:
1156 return v8::Uint32::New(*reinterpret_cast<const unsigned char*>(data));
1157 case QMetaType::QChar:
1158 return v8::Uint32::New((*reinterpret_cast<const QChar*>(data)).unicode());
1159 case QMetaType::QStringList:
1160 result = QJSConverter::toStringList(*reinterpret_cast<const QStringList *>(data));
1162 case QMetaType::QVariantList:
1163 result = variantListToJS(*reinterpret_cast<const QVariantList *>(data));
1165 case QMetaType::QVariantMap:
1166 result = variantMapToJS(*reinterpret_cast<const QVariantMap *>(data));
1168 case QMetaType::QDateTime:
1169 result = QJSConverter::toDateTime(*reinterpret_cast<const QDateTime *>(data));
1171 case QMetaType::QDate:
1172 result = QJSConverter::toDateTime(QDateTime(*reinterpret_cast<const QDate *>(data)));
1174 case QMetaType::QRegExp:
1175 result = QJSConverter::toRegExp(*reinterpret_cast<const QRegExp *>(data));
1177 case QMetaType::QObjectStar:
1178 case QMetaType::QWidgetStar:
1179 result = newQObject(*reinterpret_cast<QObject* const *>(data));
1181 case QMetaType::QVariant:
1182 result = variantToJS(*reinterpret_cast<const QVariant*>(data));
1185 if (type == qMetaTypeId<QJSValue>()) {
1186 return QJSValuePrivate::get(*reinterpret_cast<const QJSValue*>(data))->asV8Value(this);
1188 QByteArray typeName = QMetaType::typeName(type);
1189 if (typeName.endsWith('*') && !*reinterpret_cast<void* const *>(data)) {
1192 // Fall back to wrapping in a QVariant.
1193 result = newVariant(QVariant(type, data));
1200 // Converts a JS value to a meta-type.
1201 // data must point to a place that can store a value of the given type.
1202 // Returns true if conversion succeeded, false otherwise.
1203 bool QV8Engine::metaTypeFromJS(v8::Handle<v8::Value> value, int type, void *data) {
1204 // check if it's one of the types we know
1205 switch (QMetaType::Type(type)) {
1206 case QMetaType::Bool:
1207 *reinterpret_cast<bool*>(data) = value->ToBoolean()->Value();
1209 case QMetaType::Int:
1210 *reinterpret_cast<int*>(data) = value->ToInt32()->Value();
1212 case QMetaType::UInt:
1213 *reinterpret_cast<uint*>(data) = value->ToUint32()->Value();
1215 case QMetaType::LongLong:
1216 *reinterpret_cast<qlonglong*>(data) = qlonglong(value->ToInteger()->Value());
1218 case QMetaType::ULongLong:
1219 *reinterpret_cast<qulonglong*>(data) = qulonglong(value->ToInteger()->Value());
1221 case QMetaType::Double:
1222 *reinterpret_cast<double*>(data) = value->ToNumber()->Value();
1224 case QMetaType::QString:
1225 if (value->IsUndefined() || value->IsNull())
1226 *reinterpret_cast<QString*>(data) = QString();
1228 *reinterpret_cast<QString*>(data) = QJSConverter::toString(value->ToString());
1230 case QMetaType::Float:
1231 *reinterpret_cast<float*>(data) = value->ToNumber()->Value();
1233 case QMetaType::Short:
1234 *reinterpret_cast<short*>(data) = short(value->ToInt32()->Value());
1236 case QMetaType::UShort:
1237 *reinterpret_cast<unsigned short*>(data) = ushort(value->ToInt32()->Value()); // ### QScript::ToUInt16()
1239 case QMetaType::Char:
1240 *reinterpret_cast<char*>(data) = char(value->ToInt32()->Value());
1242 case QMetaType::UChar:
1243 *reinterpret_cast<unsigned char*>(data) = (unsigned char)(value->ToInt32()->Value());
1245 case QMetaType::QChar:
1246 if (value->IsString()) {
1247 QString str = QJSConverter::toString(v8::Handle<v8::String>::Cast(value));
1248 *reinterpret_cast<QChar*>(data) = str.isEmpty() ? QChar() : str.at(0);
1250 *reinterpret_cast<QChar*>(data) = QChar(ushort(value->ToInt32()->Value())); // ### QScript::ToUInt16()
1253 case QMetaType::QDateTime:
1254 if (value->IsDate()) {
1255 *reinterpret_cast<QDateTime *>(data) = QJSConverter::toDateTime(v8::Handle<v8::Date>::Cast(value));
1258 case QMetaType::QDate:
1259 if (value->IsDate()) {
1260 *reinterpret_cast<QDate *>(data) = QJSConverter::toDateTime(v8::Handle<v8::Date>::Cast(value)).date();
1263 case QMetaType::QRegExp:
1264 if (value->IsRegExp()) {
1265 *reinterpret_cast<QRegExp *>(data) = QJSConverter::toRegExp(v8::Handle<v8::RegExp>::Cast(value));
1268 case QMetaType::QObjectStar:
1269 if (isQObject(value) || value->IsNull()) {
1270 *reinterpret_cast<QObject* *>(data) = qtObjectFromJS(value);
1273 case QMetaType::QWidgetStar:
1274 if (isQObject(value) || value->IsNull()) {
1275 QObject *qo = qtObjectFromJS(value);
1276 if (!qo || qo->isWidgetType()) {
1277 *reinterpret_cast<QWidget* *>(data) = reinterpret_cast<QWidget*>(qo);
1281 case QMetaType::QStringList:
1282 if (value->IsArray()) {
1283 *reinterpret_cast<QStringList *>(data) = QJSConverter::toStringList(v8::Handle<v8::Array>::Cast(value));
1286 case QMetaType::QVariantList:
1287 if (value->IsArray()) {
1288 *reinterpret_cast<QVariantList *>(data) = variantListFromJS(v8::Handle<v8::Array>::Cast(value));
1291 case QMetaType::QVariantMap:
1292 if (value->IsObject()) {
1293 *reinterpret_cast<QVariantMap *>(data) = variantMapFromJS(v8::Handle<v8::Object>::Cast(value));
1296 case QMetaType::QVariant:
1297 *reinterpret_cast<QVariant*>(data) = variantFromJS(value);
1304 if (isQtVariant(value)) {
1305 const QVariant &var = variantValue(value);
1306 // ### Enable once constructInPlace() is in qt master.
1307 if (var.userType() == type) {
1308 QMetaType::constructInPlace(type, data, var.constData());
1311 if (var.canConvert(QVariant::Type(type))) {
1313 vv.convert(QVariant::Type(type));
1314 Q_ASSERT(vv.userType() == type);
1315 QMetaType::constructInPlace(type, data, vv.constData());
1322 // Try to use magic; for compatibility with qscriptvalue_cast.
1324 QByteArray name = QMetaType::typeName(type);
1325 if (convertToNativeQObject(value, name, reinterpret_cast<void* *>(data)))
1327 if (isVariant(value) && name.endsWith('*')) {
1328 int valueType = QMetaType::type(name.left(name.size()-1));
1329 QVariant &var = variantValue(value);
1330 if (valueType == var.userType()) {
1331 // We have T t, T* is requested, so return &t.
1332 *reinterpret_cast<void* *>(data) = var.data();
1335 // Look in the prototype chain.
1336 v8::Handle<v8::Value> proto = value->ToObject()->GetPrototype();
1337 while (proto->IsObject()) {
1338 bool canCast = false;
1339 if (isVariant(proto)) {
1340 canCast = (type == variantValue(proto).userType())
1341 || (valueType && (valueType == variantValue(proto).userType()));
1343 else if (isQObject(proto)) {
1344 QByteArray className = name.left(name.size()-1);
1345 if (QObject *qobject = qtObjectFromJS(proto))
1346 canCast = qobject->qt_metacast(className) != 0;
1349 QByteArray varTypeName = QMetaType::typeName(var.userType());
1350 if (varTypeName.endsWith('*'))
1351 *reinterpret_cast<void* *>(data) = *reinterpret_cast<void* *>(var.data());
1353 *reinterpret_cast<void* *>(data) = var.data();
1356 proto = proto->ToObject()->GetPrototype();
1359 } else if (value->IsNull() && name.endsWith('*')) {
1360 *reinterpret_cast<void* *>(data) = 0;
1362 } else if (type == qMetaTypeId<QJSValue>()) {
1363 *reinterpret_cast<QJSValue*>(data) = QJSValuePrivate::get(new QJSValuePrivate(this, value));
1370 // Converts a QVariant to JS.
1371 v8::Handle<v8::Value> QV8Engine::variantToJS(const QVariant &value)
1373 return metaTypeToJS(value.userType(), value.constData());
1376 // Converts a JS value to a QVariant.
1377 // Null, Undefined -> QVariant() (invalid)
1378 // Boolean -> QVariant(bool)
1379 // Number -> QVariant(double)
1380 // String -> QVariant(QString)
1381 // Array -> QVariantList(...)
1382 // Date -> QVariant(QDateTime)
1383 // RegExp -> QVariant(QRegExp)
1384 // [Any other object] -> QVariantMap(...)
1385 QVariant QV8Engine::variantFromJS(v8::Handle<v8::Value> value)
1387 Q_ASSERT(!value.IsEmpty());
1388 if (value->IsNull() || value->IsUndefined())
1390 if (value->IsBoolean())
1391 return value->ToBoolean()->Value();
1392 if (value->IsInt32())
1393 return value->ToInt32()->Value();
1394 if (value->IsNumber())
1395 return value->ToNumber()->Value();
1396 if (value->IsString())
1397 return QJSConverter::toString(value->ToString());
1398 Q_ASSERT(value->IsObject());
1399 if (value->IsArray())
1400 return variantListFromJS(v8::Handle<v8::Array>::Cast(value));
1401 if (value->IsDate())
1402 return QJSConverter::toDateTime(v8::Handle<v8::Date>::Cast(value));
1403 if (value->IsRegExp())
1404 return QJSConverter::toRegExp(v8::Handle<v8::RegExp>::Cast(value));
1405 if (isVariant(value))
1406 return variantValue(value);
1407 if (isQObject(value))
1408 return qVariantFromValue(qtObjectFromJS(value));
1409 return variantMapFromJS(value->ToObject());
1412 bool QV8Engine::convertToNativeQObject(v8::Handle<v8::Value> value,
1413 const QByteArray &targetType,
1416 if (!targetType.endsWith('*'))
1418 if (QObject *qobject = qtObjectFromJS(value)) {
1419 int start = targetType.startsWith("const ") ? 6 : 0;
1420 QByteArray className = targetType.mid(start, targetType.size()-start-1);
1421 if (void *instance = qobject->qt_metacast(className)) {
1429 QObject *QV8Engine::qtObjectFromJS(v8::Handle<v8::Value> value)
1431 if (!value->IsObject())
1434 QV8ObjectResource *r = (QV8ObjectResource *)value->ToObject()->GetExternalResource();
1437 QV8ObjectResource::ResourceType type = r->resourceType();
1438 if (type == QV8ObjectResource::QObjectType)
1439 return qobjectWrapper()->toQObject(r);
1440 else if (type == QV8ObjectResource::VariantType) {
1441 QVariant variant = variantWrapper()->toVariant(r);
1442 int type = variant.userType();
1443 if ((type == QMetaType::QObjectStar) || (type == QMetaType::QWidgetStar))
1444 return *reinterpret_cast<QObject* const *>(variant.constData());
1450 QVariant &QV8Engine::variantValue(v8::Handle<v8::Value> value)
1452 return variantWrapper()->variantValue(value);
1455 // Creates a QVariant wrapper object.
1456 v8::Local<v8::Object> QV8Engine::newVariant(const QVariant &value)
1458 return variantWrapper()->newVariant(value);
1461 QScriptPassPointer<QJSValuePrivate> QV8Engine::evaluate(v8::Handle<v8::Script> script, v8::TryCatch& tryCatch)
1463 v8::HandleScope handleScope;
1466 if (script.IsEmpty()) {
1467 v8::Handle<v8::Value> exception = tryCatch.Exception();
1468 if (exception.IsEmpty()) {
1469 // This is possible on syntax errors like { a:12, b:21 } <- missing "(", ")" around expression.
1470 return InvalidValue();
1472 setException(exception, tryCatch.Message());
1473 return new QJSValuePrivate(this, exception);
1475 v8::Handle<v8::Value> result;
1476 result = script->Run();
1477 if (result.IsEmpty()) {
1478 v8::Handle<v8::Value> exception = tryCatch.Exception();
1479 // TODO: figure out why v8 doesn't always produce an exception value
1480 //Q_ASSERT(!exception.IsEmpty());
1481 if (exception.IsEmpty())
1482 exception = v8::Exception::Error(v8::String::New("missing exception value"));
1483 setException(exception, tryCatch.Message());
1484 return new QJSValuePrivate(this, exception);
1486 return new QJSValuePrivate(this, result);
1489 QJSValue QV8Engine::scriptValueFromInternal(v8::Handle<v8::Value> value) const
1491 if (value.IsEmpty())
1492 return QJSValuePrivate::get(InvalidValue());
1493 return QJSValuePrivate::get(new QJSValuePrivate(const_cast<QV8Engine*>(this), value));
1496 QScriptPassPointer<QJSValuePrivate> QV8Engine::newArray(uint length)
1498 return new QJSValuePrivate(this, v8::Array::New(length));
1501 void QV8Engine::emitSignalHandlerException()
1503 emit q->signalHandlerException(scriptValueFromInternal(uncaughtException()));
1506 void QV8Engine::startTimer(const QString &timerName)
1508 if (!m_time.isValid())
1510 m_startedTimers[timerName] = m_time.elapsed();
1513 qint64 QV8Engine::stopTimer(const QString &timerName, bool *wasRunning)
1515 if (!m_startedTimers.contains(timerName)) {
1516 *wasRunning = false;
1520 qint64 startedAt = m_startedTimers.take(timerName);
1521 return m_time.elapsed() - startedAt;
1524 int QV8Engine::consoleCountHelper(const QString &file, int line, int column)
1526 const QString key = file + QString::number(line) + QString::number(column);
1527 int number = m_consoleCount.value(key, 0);
1529 m_consoleCount.insert(key, number);
1533 void QV8GCCallback::registerGcPrologueCallback()
1535 QV8Engine::ThreadData *td = QV8Engine::threadData();
1536 if (!td->gcPrologueCallbackRegistered) {
1537 td->gcPrologueCallbackRegistered = true;
1538 v8::V8::AddGCPrologueCallback(QV8GCCallback::garbageCollectorPrologueCallback, v8::kGCTypeMarkSweepCompact);
1542 QV8GCCallback::Node::Node(PrologueCallback callback)
1543 : prologueCallback(callback)
1547 QV8GCCallback::Node::~Node()
1553 Ensure that each persistent handle is strong if it has CPP ownership
1554 and has no implicitly JS owned object owner in its parent chain, and
1557 Any weak handle whose parent object is still alive will have an implicit
1558 reference (between the parent and the handle) added, so that it will
1561 Note that this callback is registered only for kGCTypeMarkSweepCompact
1562 collection cycles, as it is during collection cycles of that type
1563 in which weak persistent handle callbacks are called when required.
1565 void QV8GCCallback::garbageCollectorPrologueCallback(v8::GCType, v8::GCCallbackFlags)
1567 if (!QV8Engine::hasThreadData())
1570 QV8Engine::ThreadData *td = QV8Engine::threadData();
1571 QV8GCCallback::Node *currNode = td->gcCallbackNodes.first();
1574 // The client which adds itself to the list is responsible
1575 // for maintaining the correct implicit references in the
1576 // specified callback.
1577 currNode->prologueCallback(currNode);
1578 currNode = td->gcCallbackNodes.next(currNode);
1582 void QV8GCCallback::addGcCallbackNode(QV8GCCallback::Node *node)
1584 QV8Engine::ThreadData *td = QV8Engine::threadData();
1585 td->gcCallbackNodes.insert(node);
1588 QV8Engine::ThreadData::ThreadData()
1589 : gcPrologueCallbackRegistered(false)
1591 if (!v8::Isolate::GetCurrent()) {
1592 isolate = v8::Isolate::New();
1599 QV8Engine::ThreadData::~ThreadData()