1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: http://www.qt-project.org/
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** 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"
62 Q_DECLARE_METATYPE(QJSValue)
63 Q_DECLARE_METATYPE(QList<int>)
66 // XXX TODO: Need to check all the global functions will also work in a worker script where the
67 // QDeclarativeEngine is not available
70 static bool ObjectComparisonCallback(v8::Local<v8::Object> lhs, v8::Local<v8::Object> rhs)
75 QV8ObjectResource *lhsr = static_cast<QV8ObjectResource*>(lhs->GetExternalResource());
76 QV8ObjectResource *rhsr = static_cast<QV8ObjectResource*>(rhs->GetExternalResource());
78 Q_ASSERT(lhsr->engine == rhsr->engine);
81 QV8ObjectResource::ResourceType lhst = lhsr->resourceType();
82 QV8ObjectResource::ResourceType rhst = rhsr->resourceType();
85 case QV8ObjectResource::ValueTypeType:
86 // a value type might be equal to a variant or another value type
87 if (rhst == QV8ObjectResource::ValueTypeType) {
88 return lhsr->engine->valueTypeWrapper()->isEqual(lhsr, lhsr->engine->valueTypeWrapper()->toVariant(rhsr));
89 } else if (rhst == QV8ObjectResource::VariantType) {
90 return lhsr->engine->valueTypeWrapper()->isEqual(lhsr, lhsr->engine->variantWrapper()->toVariant(rhsr));
93 case QV8ObjectResource::VariantType:
94 // a variant might be equal to a value type or other variant.
95 if (rhst == QV8ObjectResource::VariantType) {
96 return lhsr->engine->variantWrapper()->toVariant(lhsr) ==
97 lhsr->engine->variantWrapper()->toVariant(rhsr);
98 } else if (rhst == QV8ObjectResource::ValueTypeType) {
99 return rhsr->engine->valueTypeWrapper()->isEqual(rhsr, rhsr->engine->variantWrapper()->toVariant(lhsr));
102 case QV8ObjectResource::SequenceType:
103 // a sequence might be equal to itself.
104 if (rhst == QV8ObjectResource::SequenceType) {
105 return lhsr->engine->sequenceWrapper()->isEqual(lhsr, rhsr);
117 QV8Engine::QV8Engine(QJSEngine* qq, QJSEngine::ContextOwnership ownership)
120 , m_ownsV8Context(ownership == QJSEngine::CreateNewContext)
121 , m_xmlHttpRequestData(0)
122 , m_sqlDatabaseData(0)
125 qMetaTypeId<QJSValue>();
126 qMetaTypeId<QList<int> >();
128 QByteArray v8args = qgetenv("V8ARGS");
129 // change default v8 behaviour to not relocate breakpoints across lines
130 if (!v8args.contains("breakpoint_relocation"))
131 v8args.append(" --nobreakpoint_relocation");
132 v8::V8::SetFlagsFromString(v8args.constData(), v8args.length());
134 ensurePerThreadIsolate();
136 v8::HandleScope handle_scope;
137 m_context = (ownership == QJSEngine::CreateNewContext) ? v8::Context::New() : v8::Persistent<v8::Context>::New(v8::Context::GetCurrent());
138 qPersistentRegister(m_context);
139 m_originalGlobalObject.init(m_context);
140 v8::Context::Scope context_scope(m_context);
142 v8::V8::SetUserObjectComparisonCallbackFunction(ObjectComparisonCallback);
143 QV8GCCallback::registerGcPrologueCallback();
144 m_strongReferencer = qPersistentNew(v8::Object::New());
146 m_stringWrapper.init();
147 m_contextWrapper.init(this);
148 m_qobjectWrapper.init(this);
149 m_typeWrapper.init(this);
150 m_listWrapper.init(this);
151 m_variantWrapper.init(this);
152 m_valueTypeWrapper.init(this);
153 m_sequenceWrapper.init(this);
156 v8::Handle<v8::Value> v = global()->Get(v8::String::New("Object"))->ToObject()->Get(v8::String::New("getOwnPropertyNames"));
157 m_getOwnPropertyNames = qPersistentNew<v8::Function>(v8::Handle<v8::Function>::Cast(v));
161 QV8Engine::~QV8Engine()
163 Q_ASSERT_X(v8::Isolate::GetCurrent(), "QV8Engine::~QV8Engine()", "called after v8::Isolate has exited");
164 for (int ii = 0; ii < m_extensionData.count(); ++ii)
165 delete m_extensionData[ii];
166 m_extensionData.clear();
168 qt_rem_qmlsqldatabase(this, m_sqlDatabaseData);
169 m_sqlDatabaseData = 0;
170 qt_rem_qmlxmlhttprequest(this, m_xmlHttpRequestData);
171 m_xmlHttpRequestData = 0;
172 delete m_listModelData;
175 qPersistentDispose(m_freezeObject);
176 qPersistentDispose(m_getOwnPropertyNames);
178 invalidateAllValues();
181 qPersistentDispose(m_strongReferencer);
183 m_sequenceWrapper.destroy();
184 m_valueTypeWrapper.destroy();
185 m_variantWrapper.destroy();
186 m_listWrapper.destroy();
187 m_typeWrapper.destroy();
188 m_qobjectWrapper.destroy();
189 m_contextWrapper.destroy();
190 m_stringWrapper.destroy();
192 m_originalGlobalObject.destroy();
195 qPersistentDispose(m_context);
198 QString QV8Engine::toStringStatic(v8::Handle<v8::Value> jsstr)
200 return toStringStatic(jsstr->ToString());
203 QString QV8Engine::toStringStatic(v8::Handle<v8::String> jsstr)
206 qstr.resize(jsstr->Length());
207 jsstr->Write((uint16_t*)qstr.data());
211 QVariant QV8Engine::toVariant(v8::Handle<v8::Value> value, int typeHint)
216 if (typeHint == QVariant::Bool)
217 return QVariant(value->BooleanValue());
219 if (value->IsObject()) {
220 QV8ObjectResource *r = (QV8ObjectResource *)value->ToObject()->GetExternalResource();
222 switch (r->resourceType()) {
223 case QV8ObjectResource::Context2DStyleType:
224 case QV8ObjectResource::Context2DPixelArrayType:
225 case QV8ObjectResource::SignalHandlerType:
226 case QV8ObjectResource::IncubatorType:
227 case QV8ObjectResource::VisualDataItemType:
228 case QV8ObjectResource::ContextType:
229 case QV8ObjectResource::XMLHttpRequestType:
230 case QV8ObjectResource::DOMNodeType:
231 case QV8ObjectResource::SQLDatabaseType:
232 case QV8ObjectResource::ListModelType:
233 case QV8ObjectResource::Context2DType:
234 case QV8ObjectResource::ParticleDataType:
235 case QV8ObjectResource::LocaleDataType:
237 case QV8ObjectResource::TypeType:
238 return m_typeWrapper.toVariant(r);
239 case QV8ObjectResource::QObjectType:
240 return qVariantFromValue<QObject *>(m_qobjectWrapper.toQObject(r));
241 case QV8ObjectResource::ListType:
242 return m_listWrapper.toVariant(r);
243 case QV8ObjectResource::VariantType:
244 return m_variantWrapper.toVariant(r);
245 case QV8ObjectResource::ValueTypeType:
246 return m_valueTypeWrapper.toVariant(r);
247 case QV8ObjectResource::SequenceType:
248 return m_sequenceWrapper.toVariant(r);
253 if (value->IsArray()) {
254 v8::Handle<v8::Array> array = v8::Handle<v8::Array>::Cast(value);
255 if (typeHint == qMetaTypeId<QList<QObject *> >()) {
256 QList<QObject *> list;
257 uint32_t length = array->Length();
258 for (uint32_t ii = 0; ii < length; ++ii) {
259 v8::Local<v8::Value> arrayItem = array->Get(ii);
260 if (arrayItem->IsObject()) {
261 list << toQObject(arrayItem->ToObject());
267 return qVariantFromValue<QList<QObject*> >(list);
270 bool succeeded = false;
271 QVariant retn = m_sequenceWrapper.toVariant(array, typeHint, &succeeded);
276 return toBasicVariant(value);
279 static v8::Handle<v8::Array> arrayFromStringList(QV8Engine *engine, const QStringList &list)
281 v8::Context::Scope scope(engine->context());
282 v8::Local<v8::Array> result = v8::Array::New(list.count());
283 for (int ii = 0; ii < list.count(); ++ii)
284 result->Set(ii, engine->toString(list.at(ii)));
288 static v8::Handle<v8::Array> arrayFromVariantList(QV8Engine *engine, const QVariantList &list)
290 v8::Context::Scope scope(engine->context());
291 v8::Local<v8::Array> result = v8::Array::New(list.count());
292 for (int ii = 0; ii < list.count(); ++ii)
293 result->Set(ii, engine->fromVariant(list.at(ii)));
297 static v8::Handle<v8::Object> objectFromVariantMap(QV8Engine *engine, const QVariantMap &map)
299 v8::Context::Scope scope(engine->context());
300 v8::Local<v8::Object> object = v8::Object::New();
301 for (QVariantMap::ConstIterator iter = map.begin(); iter != map.end(); ++iter)
302 object->Set(engine->toString(iter.key()), engine->fromVariant(iter.value()));
306 Q_CORE_EXPORT QString qt_regexp_toCanonical(const QString &, QRegExp::PatternSyntax);
308 v8::Handle<v8::Value> QV8Engine::fromVariant(const QVariant &variant)
310 int type = variant.userType();
311 const void *ptr = variant.constData();
313 if (type < QMetaType::User) {
314 switch (QMetaType::Type(type)) {
315 case QMetaType::Void:
316 return v8::Undefined();
317 case QMetaType::Bool:
318 return v8::Boolean::New(*reinterpret_cast<const bool*>(ptr));
320 return v8::Integer::New(*reinterpret_cast<const int*>(ptr));
321 case QMetaType::UInt:
322 return v8::Integer::NewFromUnsigned(*reinterpret_cast<const uint*>(ptr));
323 case QMetaType::LongLong:
324 return v8::Number::New(*reinterpret_cast<const qlonglong*>(ptr));
325 case QMetaType::ULongLong:
326 return v8::Number::New(*reinterpret_cast<const qulonglong*>(ptr));
327 case QMetaType::Double:
328 return v8::Number::New(*reinterpret_cast<const double*>(ptr));
329 case QMetaType::QString:
330 return m_stringWrapper.toString(*reinterpret_cast<const QString*>(ptr));
331 case QMetaType::Float:
332 return v8::Number::New(*reinterpret_cast<const float*>(ptr));
333 case QMetaType::Short:
334 return v8::Integer::New(*reinterpret_cast<const short*>(ptr));
335 case QMetaType::UShort:
336 return v8::Integer::NewFromUnsigned(*reinterpret_cast<const unsigned short*>(ptr));
337 case QMetaType::Char:
338 return v8::Integer::New(*reinterpret_cast<const char*>(ptr));
339 case QMetaType::UChar:
340 return v8::Integer::NewFromUnsigned(*reinterpret_cast<const unsigned char*>(ptr));
341 case QMetaType::QChar:
342 return v8::Integer::New((*reinterpret_cast<const QChar*>(ptr)).unicode());
343 case QMetaType::QDateTime:
344 return v8::Date::New(qtDateTimeToJsDate(*reinterpret_cast<const QDateTime *>(ptr)));
345 case QMetaType::QDate:
346 return v8::Date::New(qtDateTimeToJsDate(QDateTime(*reinterpret_cast<const QDate *>(ptr))));
347 case QMetaType::QTime:
348 return v8::Date::New(qtDateTimeToJsDate(QDateTime(QDate(1970,1,1), *reinterpret_cast<const QTime *>(ptr))));
349 case QMetaType::QRegExp:
350 return QJSConverter::toRegExp(*reinterpret_cast<const QRegExp *>(ptr));
351 case QMetaType::QObjectStar:
352 case QMetaType::QWidgetStar:
353 return newQObject(*reinterpret_cast<QObject* const *>(ptr));
354 case QMetaType::QStringList:
356 bool succeeded = false;
357 v8::Handle<v8::Value> retn = m_sequenceWrapper.fromVariant(variant, &succeeded);
360 return arrayFromStringList(this, *reinterpret_cast<const QStringList *>(ptr));
362 case QMetaType::QVariantList:
363 return arrayFromVariantList(this, *reinterpret_cast<const QVariantList *>(ptr));
364 case QMetaType::QVariantMap:
365 return objectFromVariantMap(this, *reinterpret_cast<const QVariantMap *>(ptr));
372 if (QDeclarativeValueType *vt = QDeclarativeEnginePrivate::get(m_engine)->valueTypes[type])
373 return m_valueTypeWrapper.newValueType(variant, vt);
377 if (type == qMetaTypeId<QDeclarativeListReference>()) {
378 typedef QDeclarativeListReferencePrivate QDLRP;
379 QDLRP *p = QDLRP::get((QDeclarativeListReference*)ptr);
381 return m_listWrapper.newList(p->property, p->propertyType);
385 } else if (type == qMetaTypeId<QJSValue>()) {
386 const QJSValue *value = reinterpret_cast<const QJSValue *>(ptr);
387 QJSValuePrivate *valuep = QJSValuePrivate::get(*value);
388 if (valuep->assignEngine(this))
389 return v8::Local<v8::Value>::New(*valuep);
390 } else if (type == qMetaTypeId<QList<QObject *> >()) {
391 // XXX Can this be made more by using Array as a prototype and implementing
392 // directly against QList<QObject*>?
393 const QList<QObject *> &list = *(QList<QObject *>*)ptr;
394 v8::Local<v8::Array> array = v8::Array::New(list.count());
395 for (int ii = 0; ii < list.count(); ++ii)
396 array->Set(ii, newQObject(list.at(ii)));
401 QObject *obj = QDeclarativeMetaType::toQObject(variant, &objOk);
403 return newQObject(obj);
405 bool succeeded = false;
406 v8::Handle<v8::Value> retn = m_sequenceWrapper.fromVariant(variant, &succeeded);
411 // XXX TODO: To be compatible, we still need to handle:
415 return m_variantWrapper.newVariant(variant);
418 // A handle scope and context must be entered
419 v8::Local<v8::Script> QV8Engine::qmlModeCompile(const QString &source, const QString &fileName, int lineNumber)
421 v8::Local<v8::String> v8source = m_stringWrapper.toString(source);
422 v8::Local<v8::String> v8fileName = m_stringWrapper.toString(fileName);
424 v8::ScriptOrigin origin(v8fileName, v8::Integer::New(lineNumber - 1));
426 v8::Local<v8::Script> script = v8::Script::Compile(v8source, &origin, 0, v8::Handle<v8::String>(),
427 v8::Script::QmlMode);
432 QNetworkAccessManager *QV8Engine::networkAccessManager()
434 return QDeclarativeEnginePrivate::get(m_engine)->getNetworkAccessManager();
437 const QStringHash<bool> &QV8Engine::illegalNames() const
439 return m_illegalNames;
442 // Requires a handle scope
443 v8::Local<v8::Array> QV8Engine::getOwnPropertyNames(v8::Handle<v8::Object> o)
445 // FIXME Newer v8 have API for this function
447 v8::Handle<v8::Value> args[] = { o };
448 v8::Local<v8::Value> r = m_getOwnPropertyNames->Call(global(), 1, args);
450 return v8::Array::New();
452 return v8::Local<v8::Array>::Cast(r);
455 QDeclarativeContextData *QV8Engine::callingContext()
457 return m_contextWrapper.callingContext();
460 // Converts a JS value to a QVariant.
461 // Null, Undefined -> QVariant() (invalid)
462 // Boolean -> QVariant(bool)
463 // Number -> QVariant(double)
464 // String -> QVariant(QString)
465 // Array -> QVariantList(...)
466 // Date -> QVariant(QDateTime)
467 // RegExp -> QVariant(QRegExp)
468 // [Any other object] -> QVariantMap(...)
469 QVariant QV8Engine::toBasicVariant(v8::Handle<v8::Value> value)
471 if (value->IsNull() || value->IsUndefined())
473 if (value->IsBoolean())
474 return value->ToBoolean()->Value();
475 if (value->IsInt32())
476 return value->ToInt32()->Value();
477 if (value->IsNumber())
478 return value->ToNumber()->Value();
479 if (value->IsString())
480 return m_stringWrapper.toString(value->ToString());
482 return qtDateTimeFromJsDate(v8::Handle<v8::Date>::Cast(value)->NumberValue());
483 // NOTE: since we convert QTime to JS Date, round trip will change the variant type (to QDateTime)!
485 Q_ASSERT(value->IsObject());
487 if (value->IsRegExp()) {
488 v8::Context::Scope scope(context());
489 return QJSConverter::toRegExp(v8::Handle<v8::RegExp>::Cast(value));
491 if (value->IsArray()) {
492 v8::Context::Scope scope(context());
495 v8::Handle<v8::Array> array = v8::Handle<v8::Array>::Cast(value);
496 int length = array->Length();
497 for (int ii = 0; ii < length; ++ii)
498 rv << toVariant(array->Get(ii), -1);
501 if (!value->IsFunction()) {
502 v8::Context::Scope scope(context());
503 v8::Handle<v8::Object> object = value->ToObject();
504 return variantMapFromJS(object);
512 #include <QtGui/qvector3d.h>
513 #include <QtGui/qvector4d.h>
515 struct StaticQtMetaObject : public QObject
517 static const QMetaObject *get()
518 { return &static_cast<StaticQtMetaObject*> (0)->staticQtMetaObject; }
521 void QV8Engine::initializeGlobal(v8::Handle<v8::Object> global)
523 using namespace QDeclarativeBuiltinFunctions;
525 v8::Local<v8::Object> console = v8::Object::New();
526 v8::Local<v8::Function> consoleLogFn = V8FUNCTION(consoleLog, this);
528 console->Set(v8::String::New("debug"), consoleLogFn);
529 console->Set(v8::String::New("log"), consoleLogFn);
530 console->Set(v8::String::New("info"), consoleLogFn);
531 console->Set(v8::String::New("warn"), V8FUNCTION(consoleWarn, this));
532 console->Set(v8::String::New("error"), V8FUNCTION(consoleError, this));
534 console->Set(v8::String::New("count"), V8FUNCTION(consoleCount, this));
535 console->Set(v8::String::New("profile"), V8FUNCTION(consoleProfile, this));
536 console->Set(v8::String::New("profileEnd"), V8FUNCTION(consoleProfileEnd, this));
537 console->Set(v8::String::New("time"), V8FUNCTION(consoleTime, this));
538 console->Set(v8::String::New("timeEnd"), V8FUNCTION(consoleTimeEnd, this));
539 console->Set(v8::String::New("trace"), V8FUNCTION(consoleTrace, this));
541 v8::Local<v8::Object> qt = v8::Object::New();
543 // Set all the enums from the "Qt" namespace
544 const QMetaObject *qtMetaObject = StaticQtMetaObject::get();
545 for (int ii = 0; ii < qtMetaObject->enumeratorCount(); ++ii) {
546 QMetaEnum enumerator = qtMetaObject->enumerator(ii);
547 for (int jj = 0; jj < enumerator.keyCount(); ++jj) {
548 qt->Set(v8::String::New(enumerator.key(jj)), v8::Integer::New(enumerator.value(jj)));
551 qt->Set(v8::String::New("Asynchronous"), v8::Integer::New(0));
552 qt->Set(v8::String::New("Synchronous"), v8::Integer::New(1));
554 qt->Set(v8::String::New("include"), V8FUNCTION(QV8Include::include, this));
555 qt->Set(v8::String::New("isQtObject"), V8FUNCTION(isQtObject, this));
556 qt->Set(v8::String::New("rgba"), V8FUNCTION(rgba, this));
557 qt->Set(v8::String::New("hsla"), V8FUNCTION(hsla, this));
558 qt->Set(v8::String::New("rect"), V8FUNCTION(rect, this));
559 qt->Set(v8::String::New("point"), V8FUNCTION(point, this));
560 qt->Set(v8::String::New("size"), V8FUNCTION(size, this));
561 qt->Set(v8::String::New("vector3d"), V8FUNCTION(vector3d, this));
562 qt->Set(v8::String::New("vector4d"), V8FUNCTION(vector4d, this));
564 qt->Set(v8::String::New("formatDate"), V8FUNCTION(formatDate, this));
565 qt->Set(v8::String::New("formatTime"), V8FUNCTION(formatTime, this));
566 qt->Set(v8::String::New("formatDateTime"), V8FUNCTION(formatDateTime, this));
568 qt->Set(v8::String::New("openUrlExternally"), V8FUNCTION(openUrlExternally, this));
569 qt->Set(v8::String::New("fontFamilies"), V8FUNCTION(fontFamilies, this));
570 qt->Set(v8::String::New("md5"), V8FUNCTION(md5, this));
571 qt->Set(v8::String::New("btoa"), V8FUNCTION(btoa, this));
572 qt->Set(v8::String::New("atob"), V8FUNCTION(atob, this));
573 qt->Set(v8::String::New("resolvedUrl"), V8FUNCTION(resolvedUrl, this));
574 qt->Set(v8::String::New("locale"), V8FUNCTION(locale, this));
577 qt->Set(v8::String::New("application"), newQObject(new QDeclarativeApplication(m_engine)));
578 qt->Set(v8::String::New("lighter"), V8FUNCTION(lighter, this));
579 qt->Set(v8::String::New("darker"), V8FUNCTION(darker, this));
580 qt->Set(v8::String::New("tint"), V8FUNCTION(tint, this));
581 qt->Set(v8::String::New("quit"), V8FUNCTION(quit, this));
582 qt->Set(v8::String::New("createQmlObject"), V8FUNCTION(createQmlObject, this));
583 qt->Set(v8::String::New("createComponent"), V8FUNCTION(createComponent, this));
586 global->Set(v8::String::New("qsTranslate"), V8FUNCTION(qsTranslate, this));
587 global->Set(v8::String::New("QT_TRANSLATE_NOOP"), V8FUNCTION(qsTranslateNoOp, this));
588 global->Set(v8::String::New("qsTr"), V8FUNCTION(qsTr, this));
589 global->Set(v8::String::New("QT_TR_NOOP"), V8FUNCTION(qsTrNoOp, this));
590 global->Set(v8::String::New("qsTrId"), V8FUNCTION(qsTrId, this));
591 global->Set(v8::String::New("QT_TRID_NOOP"), V8FUNCTION(qsTrIdNoOp, this));
593 global->Set(v8::String::New("print"), consoleLogFn);
594 global->Set(v8::String::New("console"), console);
595 global->Set(v8::String::New("Qt"), qt);
596 global->Set(v8::String::New("gc"), V8FUNCTION(QDeclarativeBuiltinFunctions::gc, this));
599 #define STRING_ARG "(function(stringArg) { "\
600 " String.prototype.arg = (function() {"\
601 " return stringArg.apply(this, arguments);"\
605 v8::Local<v8::Script> registerArg = v8::Script::New(v8::String::New(STRING_ARG), 0, 0, v8::Handle<v8::String>(), v8::Script::NativeMode);
606 v8::Local<v8::Value> result = registerArg->Run();
607 Q_ASSERT(result->IsFunction());
608 v8::Local<v8::Function> registerArgFunc = v8::Local<v8::Function>::Cast(result);
609 v8::Handle<v8::Value> args = V8FUNCTION(stringArg, this);
610 registerArgFunc->Call(v8::Local<v8::Object>::Cast(registerArgFunc), 1, &args);
614 QDeclarativeDateExtension::registerExtension(this);
615 QDeclarativeNumberExtension::registerExtension(this);
617 qt_add_domexceptions(this);
618 m_xmlHttpRequestData = qt_add_qmlxmlhttprequest(this);
619 m_sqlDatabaseData = qt_add_qmlsqldatabase(this);
622 v8::Handle<v8::Value> args[] = { global };
623 v8::Local<v8::Value> names = m_getOwnPropertyNames->Call(global, 1, args);
624 v8::Local<v8::Array> namesArray = v8::Local<v8::Array>::Cast(names);
625 for (quint32 ii = 0; ii < namesArray->Length(); ++ii)
626 m_illegalNames.insert(toString(namesArray->Get(ii)), true);
630 #define FREEZE_SOURCE "(function freeze_recur(obj) { "\
631 " if (Qt.isQtObject(obj)) return;"\
632 " if (obj != Function.connect && obj != Function.disconnect && "\
633 " obj instanceof Object) {"\
634 " var properties = Object.getOwnPropertyNames(obj);"\
635 " for (var prop in properties) { "\
636 " if (prop == \"connect\" || prop == \"disconnect\") {"\
637 " Object.freeze(obj[prop]); "\
640 " freeze_recur(obj[prop]);"\
643 " if (obj instanceof Object) {"\
644 " Object.freeze(obj);"\
648 v8::Local<v8::Script> freeze = v8::Script::New(v8::String::New(FREEZE_SOURCE));
649 v8::Local<v8::Value> result = freeze->Run();
650 Q_ASSERT(result->IsFunction());
651 m_freezeObject = qPersistentNew(v8::Local<v8::Function>::Cast(result));
656 void QV8Engine::freezeObject(v8::Handle<v8::Value> value)
658 v8::Handle<v8::Value> args[] = { value };
659 m_freezeObject->Call(global(), 1, args);
664 v8::V8::LowMemoryNotification();
665 while (!v8::V8::IdleNotification()) {}
668 #ifdef QML_GLOBAL_HANDLE_DEBUGGING
669 #include <QtCore/qthreadstorage.h>
670 static QThreadStorage<QSet<void *> *> QV8Engine_activeHandles;
672 void QV8Engine::registerHandle(void *handle)
675 qWarning("Attempting to register a null handle");
679 if (!QV8Engine_activeHandles.hasLocalData())
680 QV8Engine_activeHandles.setLocalData(new QSet<void *>);
682 if (QV8Engine_activeHandles.localData()->contains(handle)) {
683 qFatal("Handle %p already alive", handle);
685 QV8Engine_activeHandles.localData()->insert(handle);
689 void QV8Engine::releaseHandle(void *handle)
694 if (!QV8Engine_activeHandles.hasLocalData())
695 QV8Engine_activeHandles.setLocalData(new QSet<void *>);
697 if (QV8Engine_activeHandles.localData()->contains(handle)) {
698 QV8Engine_activeHandles.localData()->remove(handle);
700 qFatal("Handle %p already dead", handle);
705 struct QV8EngineRegistrationData
707 QV8EngineRegistrationData() : extensionCount(0) {}
712 Q_GLOBAL_STATIC(QV8EngineRegistrationData, registrationData);
714 QMutex *QV8Engine::registrationMutex()
716 return ®istrationData()->mutex;
719 int QV8Engine::registerExtension()
721 return registrationData()->extensionCount++;
724 void QV8Engine::setExtensionData(int index, Deletable *data)
726 if (m_extensionData.count() <= index)
727 m_extensionData.resize(index + 1);
729 if (m_extensionData.at(index))
730 delete m_extensionData.at(index);
732 m_extensionData[index] = data;
735 double QV8Engine::qtDateTimeToJsDate(const QDateTime &dt)
737 // from QScriptEngine::DateTimeToMs()
741 QDateTime utc = dt.toUTC();
742 QDate date = utc.date();
743 QTime time = utc.time();
744 QV8DateConverter::JSC::GregorianDateTime tm;
745 tm.year = date.year() - 1900;
746 tm.month = date.month() - 1;
747 tm.monthDay = date.day();
748 tm.weekDay = date.dayOfWeek();
749 tm.yearDay = date.dayOfYear();
750 tm.hour = time.hour();
751 tm.minute = time.minute();
752 tm.second = time.second();
753 return QV8DateConverter::JSC::gregorianDateTimeToMS(tm, time.msec());
756 v8::Persistent<v8::Object> *QV8Engine::findOwnerAndStrength(QObject *object, bool *shouldBeStrong)
758 QObject *parent = object->parent();
760 // if the object has JS ownership, the object's v8object owns the lifetime of the persistent value.
761 if (QDeclarativeEngine::objectOwnership(object) == QDeclarativeEngine::JavaScriptOwnership) {
762 *shouldBeStrong = false;
763 return &(QDeclarativeData::get(object)->v8object);
766 // no parent, and has CPP ownership - doesn't have an implicit parent.
767 *shouldBeStrong = true;
771 // if it is owned by CPP, it's root parent may still be owned by JS.
772 // in that case, the owner of the persistent handle is the root parent's v8object.
773 while (parent->parent())
774 parent = parent->parent();
776 if (QDeclarativeEngine::objectOwnership(parent) == QDeclarativeEngine::JavaScriptOwnership) {
777 // root parent is owned by JS. It's v8object owns the persistent value in question.
778 *shouldBeStrong = false;
779 return &(QDeclarativeData::get(parent)->v8object);
781 // root parent has CPP ownership. The persistent value should not be made weak.
782 *shouldBeStrong = true;
787 QDateTime QV8Engine::qtDateTimeFromJsDate(double jsDate)
789 // from QScriptEngine::MsToDateTime()
792 QV8DateConverter::JSC::GregorianDateTime tm;
793 QV8DateConverter::JSC::msToGregorianDateTime(jsDate, tm);
795 // from QScriptEngine::MsFromTime()
796 int ms = int(::fmod(jsDate, 1000.0));
800 QDateTime convertedUTC = QDateTime(QDate(tm.year + 1900, tm.month + 1, tm.monthDay),
801 QTime(tm.hour, tm.minute, tm.second, ms), Qt::UTC);
802 return convertedUTC.toLocalTime();
805 void QV8Engine::addRelationshipForGC(QObject *object, v8::Persistent<v8::Value> handle)
807 if (handle.IsEmpty())
810 bool handleShouldBeStrong = false;
811 v8::Persistent<v8::Object> *implicitOwner = findOwnerAndStrength(object, &handleShouldBeStrong);
812 if (handleShouldBeStrong) {
813 v8::V8::AddImplicitReferences(m_strongReferencer, &handle, 1);
814 } else if (!implicitOwner->IsEmpty()) {
815 v8::V8::AddImplicitReferences(*implicitOwner, &handle, 1);
819 void QV8Engine::addRelationshipForGC(QObject *object, QObject *other)
821 bool handleShouldBeStrong = false;
822 v8::Persistent<v8::Object> *implicitOwner = findOwnerAndStrength(object, &handleShouldBeStrong);
823 v8::Persistent<v8::Value> handle = QDeclarativeData::get(other, true)->v8object;
824 if (handleShouldBeStrong) {
825 v8::V8::AddImplicitReferences(m_strongReferencer, &handle, 1);
826 } else if (!implicitOwner->IsEmpty()) {
827 v8::V8::AddImplicitReferences(*implicitOwner, &handle, 1);
831 static QThreadStorage<QV8Engine::ThreadData*> perThreadEngineData;
833 bool QV8Engine::hasThreadData()
835 return perThreadEngineData.hasLocalData();
838 QV8Engine::ThreadData *QV8Engine::threadData()
840 Q_ASSERT(perThreadEngineData.hasLocalData());
841 return perThreadEngineData.localData();
844 void QV8Engine::ensurePerThreadIsolate()
846 if (!perThreadEngineData.hasLocalData())
847 perThreadEngineData.setLocalData(new ThreadData);
850 void QV8Engine::initDeclarativeGlobalObject()
852 v8::HandleScope handels;
853 v8::Context::Scope contextScope(m_context);
854 initializeGlobal(m_context->Global());
855 freezeObject(m_context->Global());
858 void QV8Engine::setEngine(QDeclarativeEngine *engine)
861 initDeclarativeGlobalObject();
864 void QV8Engine::setException(v8::Handle<v8::Value> value, v8::Handle<v8::Message> msg)
866 m_exception.set(value, msg);
869 v8::Handle<v8::Value> QV8Engine::throwException(v8::Handle<v8::Value> value)
872 v8::ThrowException(value);
876 void QV8Engine::clearExceptions()
881 v8::Handle<v8::Value> QV8Engine::uncaughtException() const
883 if (!hasUncaughtException())
884 return v8::Handle<v8::Value>();
888 bool QV8Engine::hasUncaughtException() const
893 int QV8Engine::uncaughtExceptionLineNumber() const
895 return m_exception.lineNumber();
898 QStringList QV8Engine::uncaughtExceptionBacktrace() const
900 return m_exception.backtrace();
905 Save the current exception on stack so it can be set again later.
906 \sa QV8Engine::restoreException
908 void QV8Engine::saveException()
915 Load a saved exception from stack. Current exception, if exists will be dropped
916 \sa QV8Engine::saveException
918 void QV8Engine::restoreException()
923 QV8Engine::Exception::Exception() {}
925 QV8Engine::Exception::~Exception()
927 Q_ASSERT_X(m_stack.isEmpty(), Q_FUNC_INFO, "Some saved exceptions left. Asymetric pop/push found.");
931 void QV8Engine::Exception::set(v8::Handle<v8::Value> value, v8::Handle<v8::Message> message)
933 Q_ASSERT_X(!value.IsEmpty(), Q_FUNC_INFO, "Throwing an empty value handle is highly suspected");
935 m_value = v8::Persistent<v8::Value>::New(value);
936 m_message = v8::Persistent<v8::Message>::New(message);
939 void QV8Engine::Exception::clear()
947 QV8Engine::Exception::operator bool() const
949 return !m_value.IsEmpty();
952 QV8Engine::Exception::operator v8::Handle<v8::Value>() const
958 int QV8Engine::Exception::lineNumber() const
960 if (m_message.IsEmpty())
962 return m_message->GetLineNumber();
965 QStringList QV8Engine::Exception::backtrace() const
967 if (m_message.IsEmpty())
968 return QStringList();
970 QStringList backtrace;
971 v8::Handle<v8::StackTrace> trace = m_message->GetStackTrace();
973 // FIXME it should not happen (SetCaptureStackTraceForUncaughtExceptions is called).
974 return QStringList();
976 for (int i = 0; i < trace->GetFrameCount(); ++i) {
977 v8::Local<v8::StackFrame> frame = trace->GetFrame(i);
978 backtrace.append(QJSConverter::toString(frame->GetFunctionName()));
979 backtrace.append(QJSConverter::toString(frame->GetFunctionName()));
980 backtrace.append(QString::fromAscii("()@"));
981 backtrace.append(QJSConverter::toString(frame->GetScriptName()));
982 backtrace.append(QString::fromAscii(":"));
983 backtrace.append(QString::number(frame->GetLineNumber()));
988 void QV8Engine::Exception::push()
990 m_stack.push(qMakePair(m_value, m_message));
995 void QV8Engine::Exception::pop()
997 Q_ASSERT_X(!m_stack.empty(), Q_FUNC_INFO, "Attempt to load unsaved exception found");
998 ValueMessagePair pair = m_stack.pop();
1000 m_value = pair.first;
1001 m_message = pair.second;
1004 QScriptPassPointer<QJSValuePrivate> QV8Engine::newRegExp(const QString &pattern, const QString &flags)
1006 int f = v8::RegExp::kNone;
1008 QString::const_iterator i = flags.constBegin();
1009 for (; i != flags.constEnd(); ++i) {
1010 switch (i->unicode()) {
1012 f |= v8::RegExp::kIgnoreCase;
1015 f |= v8::RegExp::kMultiline;
1018 f |= v8::RegExp::kGlobal;
1022 // ignore a Syntax Error.
1027 v8::Handle<v8::RegExp> regexp = v8::RegExp::New(QJSConverter::toString(pattern), static_cast<v8::RegExp::Flags>(f));
1028 return new QJSValuePrivate(this, regexp);
1031 QScriptPassPointer<QJSValuePrivate> QV8Engine::newRegExp(const QRegExp ®exp)
1033 return new QJSValuePrivate(this, QJSConverter::toRegExp(regexp));
1037 // Converts a QVariantList to JS.
1038 // The result is a new Array object with length equal to the length
1039 // of the QVariantList, and the elements being the QVariantList's
1040 // elements converted to JS, recursively.
1041 v8::Local<v8::Array> QV8Engine::variantListToJS(const QVariantList &lst)
1043 v8::Local<v8::Array> result = v8::Array::New(lst.size());
1044 for (int i = 0; i < lst.size(); ++i)
1045 result->Set(i, variantToJS(lst.at(i)));
1049 // Converts a JS Array object to a QVariantList.
1050 // The result is a QVariantList with length equal to the length
1051 // of the JS Array, and elements being the JS Array's elements
1052 // converted to QVariants, recursively.
1053 QVariantList QV8Engine::variantListFromJS(v8::Handle<v8::Array> jsArray)
1055 QVariantList result;
1056 int hash = jsArray->GetIdentityHash();
1057 if (visitedConversionObjects.contains(hash))
1058 return result; // Avoid recursion.
1059 v8::HandleScope handleScope;
1060 visitedConversionObjects.insert(hash);
1061 uint32_t length = jsArray->Length();
1062 for (uint32_t i = 0; i < length; ++i)
1063 result.append(variantFromJS(jsArray->Get(i)));
1064 visitedConversionObjects.remove(hash);
1068 // Converts a QVariantMap to JS.
1069 // The result is a new Object object with property names being
1070 // the keys of the QVariantMap, and values being the values of
1071 // the QVariantMap converted to JS, recursively.
1072 v8::Local<v8::Object> QV8Engine::variantMapToJS(const QVariantMap &vmap)
1074 v8::Local<v8::Object> result = v8::Object::New();
1075 QVariantMap::const_iterator it;
1076 for (it = vmap.constBegin(); it != vmap.constEnd(); ++it)
1077 result->Set(QJSConverter::toString(it.key()), variantToJS(it.value()));
1081 // Converts a JS Object to a QVariantMap.
1082 // The result is a QVariantMap with keys being the property names
1083 // of the object, and values being the values of the JS object's
1084 // properties converted to QVariants, recursively.
1085 QVariantMap QV8Engine::variantMapFromJS(v8::Handle<v8::Object> jsObject)
1089 v8::HandleScope handleScope;
1090 v8::Handle<v8::Array> propertyNames = jsObject->GetPropertyNames();
1091 uint32_t length = propertyNames->Length();
1095 int hash = jsObject->GetIdentityHash();
1096 if (visitedConversionObjects.contains(hash))
1097 return result; // Avoid recursion.
1099 visitedConversionObjects.insert(hash);
1100 // TODO: Only object's own property names. Include non-enumerable properties.
1101 for (uint32_t i = 0; i < length; ++i) {
1102 v8::Handle<v8::Value> name = propertyNames->Get(i);
1103 result.insert(QJSConverter::toString(name->ToString()), variantFromJS(jsObject->Get(name)));
1105 visitedConversionObjects.remove(hash);
1109 // Converts the meta-type defined by the given type and data to JS.
1110 // Returns the value if conversion succeeded, an empty handle otherwise.
1111 v8::Handle<v8::Value> QV8Engine::metaTypeToJS(int type, const void *data)
1113 Q_ASSERT(data != 0);
1114 v8::Handle<v8::Value> result;
1116 // check if it's one of the types we know
1117 switch (QMetaType::Type(type)) {
1118 case QMetaType::Void:
1119 return v8::Undefined();
1120 case QMetaType::Bool:
1121 return v8::Boolean::New(*reinterpret_cast<const bool*>(data));
1122 case QMetaType::Int:
1123 return v8::Int32::New(*reinterpret_cast<const int*>(data));
1124 case QMetaType::UInt:
1125 return v8::Uint32::New(*reinterpret_cast<const uint*>(data));
1126 case QMetaType::LongLong:
1127 return v8::Number::New(double(*reinterpret_cast<const qlonglong*>(data)));
1128 case QMetaType::ULongLong:
1129 #if defined(Q_OS_WIN) && defined(_MSC_FULL_VER) && _MSC_FULL_VER <= 12008804
1130 #pragma message("** NOTE: You need the Visual Studio Processor Pack to compile support for 64bit unsigned integers.")
1131 return v8::Number::New(double((qlonglong)*reinterpret_cast<const qulonglong*>(data)));
1132 #elif defined(Q_CC_MSVC) && !defined(Q_CC_MSVC_NET)
1133 return v8::Number::New(double((qlonglong)*reinterpret_cast<const qulonglong*>(data)));
1135 return v8::Number::New(double(*reinterpret_cast<const qulonglong*>(data)));
1137 case QMetaType::Double:
1138 return v8::Number::New(double(*reinterpret_cast<const double*>(data)));
1139 case QMetaType::QString:
1140 return QJSConverter::toString(*reinterpret_cast<const QString*>(data));
1141 case QMetaType::Float:
1142 return v8::Number::New(*reinterpret_cast<const float*>(data));
1143 case QMetaType::Short:
1144 return v8::Int32::New(*reinterpret_cast<const short*>(data));
1145 case QMetaType::UShort:
1146 return v8::Uint32::New(*reinterpret_cast<const unsigned short*>(data));
1147 case QMetaType::Char:
1148 return v8::Int32::New(*reinterpret_cast<const char*>(data));
1149 case QMetaType::UChar:
1150 return v8::Uint32::New(*reinterpret_cast<const unsigned char*>(data));
1151 case QMetaType::QChar:
1152 return v8::Uint32::New((*reinterpret_cast<const QChar*>(data)).unicode());
1153 case QMetaType::QStringList:
1154 result = QJSConverter::toStringList(*reinterpret_cast<const QStringList *>(data));
1156 case QMetaType::QVariantList:
1157 result = variantListToJS(*reinterpret_cast<const QVariantList *>(data));
1159 case QMetaType::QVariantMap:
1160 result = variantMapToJS(*reinterpret_cast<const QVariantMap *>(data));
1162 case QMetaType::QDateTime:
1163 result = QJSConverter::toDateTime(*reinterpret_cast<const QDateTime *>(data));
1165 case QMetaType::QDate:
1166 result = QJSConverter::toDateTime(QDateTime(*reinterpret_cast<const QDate *>(data)));
1168 case QMetaType::QRegExp:
1169 result = QJSConverter::toRegExp(*reinterpret_cast<const QRegExp *>(data));
1171 case QMetaType::QObjectStar:
1172 case QMetaType::QWidgetStar:
1173 result = newQObject(*reinterpret_cast<QObject* const *>(data));
1175 case QMetaType::QVariant:
1176 result = variantToJS(*reinterpret_cast<const QVariant*>(data));
1179 if (type == qMetaTypeId<QJSValue>()) {
1180 return QJSValuePrivate::get(*reinterpret_cast<const QJSValue*>(data))->asV8Value(this);
1182 QByteArray typeName = QMetaType::typeName(type);
1183 if (typeName.endsWith('*') && !*reinterpret_cast<void* const *>(data)) {
1186 // Fall back to wrapping in a QVariant.
1187 result = newVariant(QVariant(type, data));
1194 // Converts a JS value to a meta-type.
1195 // data must point to a place that can store a value of the given type.
1196 // Returns true if conversion succeeded, false otherwise.
1197 bool QV8Engine::metaTypeFromJS(v8::Handle<v8::Value> value, int type, void *data) {
1198 // check if it's one of the types we know
1199 switch (QMetaType::Type(type)) {
1200 case QMetaType::Bool:
1201 *reinterpret_cast<bool*>(data) = value->ToBoolean()->Value();
1203 case QMetaType::Int:
1204 *reinterpret_cast<int*>(data) = value->ToInt32()->Value();
1206 case QMetaType::UInt:
1207 *reinterpret_cast<uint*>(data) = value->ToUint32()->Value();
1209 case QMetaType::LongLong:
1210 *reinterpret_cast<qlonglong*>(data) = qlonglong(value->ToInteger()->Value());
1212 case QMetaType::ULongLong:
1213 *reinterpret_cast<qulonglong*>(data) = qulonglong(value->ToInteger()->Value());
1215 case QMetaType::Double:
1216 *reinterpret_cast<double*>(data) = value->ToNumber()->Value();
1218 case QMetaType::QString:
1219 if (value->IsUndefined() || value->IsNull())
1220 *reinterpret_cast<QString*>(data) = QString();
1222 *reinterpret_cast<QString*>(data) = QJSConverter::toString(value->ToString());
1224 case QMetaType::Float:
1225 *reinterpret_cast<float*>(data) = value->ToNumber()->Value();
1227 case QMetaType::Short:
1228 *reinterpret_cast<short*>(data) = short(value->ToInt32()->Value());
1230 case QMetaType::UShort:
1231 *reinterpret_cast<unsigned short*>(data) = ushort(value->ToInt32()->Value()); // ### QScript::ToUInt16()
1233 case QMetaType::Char:
1234 *reinterpret_cast<char*>(data) = char(value->ToInt32()->Value());
1236 case QMetaType::UChar:
1237 *reinterpret_cast<unsigned char*>(data) = (unsigned char)(value->ToInt32()->Value());
1239 case QMetaType::QChar:
1240 if (value->IsString()) {
1241 QString str = QJSConverter::toString(v8::Handle<v8::String>::Cast(value));
1242 *reinterpret_cast<QChar*>(data) = str.isEmpty() ? QChar() : str.at(0);
1244 *reinterpret_cast<QChar*>(data) = QChar(ushort(value->ToInt32()->Value())); // ### QScript::ToUInt16()
1247 case QMetaType::QDateTime:
1248 if (value->IsDate()) {
1249 *reinterpret_cast<QDateTime *>(data) = QJSConverter::toDateTime(v8::Handle<v8::Date>::Cast(value));
1252 case QMetaType::QDate:
1253 if (value->IsDate()) {
1254 *reinterpret_cast<QDate *>(data) = QJSConverter::toDateTime(v8::Handle<v8::Date>::Cast(value)).date();
1257 case QMetaType::QRegExp:
1258 if (value->IsRegExp()) {
1259 *reinterpret_cast<QRegExp *>(data) = QJSConverter::toRegExp(v8::Handle<v8::RegExp>::Cast(value));
1262 case QMetaType::QObjectStar:
1263 if (isQObject(value) || value->IsNull()) {
1264 *reinterpret_cast<QObject* *>(data) = qtObjectFromJS(value);
1267 case QMetaType::QWidgetStar:
1268 if (isQObject(value) || value->IsNull()) {
1269 QObject *qo = qtObjectFromJS(value);
1270 if (!qo || qo->isWidgetType()) {
1271 *reinterpret_cast<QWidget* *>(data) = reinterpret_cast<QWidget*>(qo);
1275 case QMetaType::QStringList:
1276 if (value->IsArray()) {
1277 *reinterpret_cast<QStringList *>(data) = QJSConverter::toStringList(v8::Handle<v8::Array>::Cast(value));
1280 case QMetaType::QVariantList:
1281 if (value->IsArray()) {
1282 *reinterpret_cast<QVariantList *>(data) = variantListFromJS(v8::Handle<v8::Array>::Cast(value));
1285 case QMetaType::QVariantMap:
1286 if (value->IsObject()) {
1287 *reinterpret_cast<QVariantMap *>(data) = variantMapFromJS(v8::Handle<v8::Object>::Cast(value));
1290 case QMetaType::QVariant:
1291 *reinterpret_cast<QVariant*>(data) = variantFromJS(value);
1298 if (isQtVariant(value)) {
1299 const QVariant &var = variantValue(value);
1300 // ### Enable once constructInPlace() is in qt master.
1301 if (var.userType() == type) {
1302 QMetaType::constructInPlace(type, data, var.constData());
1305 if (var.canConvert(QVariant::Type(type))) {
1307 vv.convert(QVariant::Type(type));
1308 Q_ASSERT(vv.userType() == type);
1309 QMetaType::constructInPlace(type, data, vv.constData());
1316 // Try to use magic; for compatibility with qscriptvalue_cast.
1318 QByteArray name = QMetaType::typeName(type);
1319 if (convertToNativeQObject(value, name, reinterpret_cast<void* *>(data)))
1321 if (isVariant(value) && name.endsWith('*')) {
1322 int valueType = QMetaType::type(name.left(name.size()-1));
1323 QVariant &var = variantValue(value);
1324 if (valueType == var.userType()) {
1325 // We have T t, T* is requested, so return &t.
1326 *reinterpret_cast<void* *>(data) = var.data();
1329 // Look in the prototype chain.
1330 v8::Handle<v8::Value> proto = value->ToObject()->GetPrototype();
1331 while (proto->IsObject()) {
1332 bool canCast = false;
1333 if (isVariant(proto)) {
1334 canCast = (type == variantValue(proto).userType())
1335 || (valueType && (valueType == variantValue(proto).userType()));
1337 else if (isQObject(proto)) {
1338 QByteArray className = name.left(name.size()-1);
1339 if (QObject *qobject = qtObjectFromJS(proto))
1340 canCast = qobject->qt_metacast(className) != 0;
1343 QByteArray varTypeName = QMetaType::typeName(var.userType());
1344 if (varTypeName.endsWith('*'))
1345 *reinterpret_cast<void* *>(data) = *reinterpret_cast<void* *>(var.data());
1347 *reinterpret_cast<void* *>(data) = var.data();
1350 proto = proto->ToObject()->GetPrototype();
1353 } else if (value->IsNull() && name.endsWith('*')) {
1354 *reinterpret_cast<void* *>(data) = 0;
1356 } else if (type == qMetaTypeId<QJSValue>()) {
1357 *reinterpret_cast<QJSValue*>(data) = QJSValuePrivate::get(new QJSValuePrivate(this, value));
1364 // Converts a QVariant to JS.
1365 v8::Handle<v8::Value> QV8Engine::variantToJS(const QVariant &value)
1367 return metaTypeToJS(value.userType(), value.constData());
1370 // Converts a JS value to a QVariant.
1371 // Null, Undefined -> QVariant() (invalid)
1372 // Boolean -> QVariant(bool)
1373 // Number -> QVariant(double)
1374 // String -> QVariant(QString)
1375 // Array -> QVariantList(...)
1376 // Date -> QVariant(QDateTime)
1377 // RegExp -> QVariant(QRegExp)
1378 // [Any other object] -> QVariantMap(...)
1379 QVariant QV8Engine::variantFromJS(v8::Handle<v8::Value> value)
1381 Q_ASSERT(!value.IsEmpty());
1382 if (value->IsNull() || value->IsUndefined())
1384 if (value->IsBoolean())
1385 return value->ToBoolean()->Value();
1386 if (value->IsInt32())
1387 return value->ToInt32()->Value();
1388 if (value->IsNumber())
1389 return value->ToNumber()->Value();
1390 if (value->IsString())
1391 return QJSConverter::toString(value->ToString());
1392 Q_ASSERT(value->IsObject());
1393 if (value->IsArray())
1394 return variantListFromJS(v8::Handle<v8::Array>::Cast(value));
1395 if (value->IsDate())
1396 return QJSConverter::toDateTime(v8::Handle<v8::Date>::Cast(value));
1397 if (value->IsRegExp())
1398 return QJSConverter::toRegExp(v8::Handle<v8::RegExp>::Cast(value));
1399 if (isVariant(value))
1400 return variantValue(value);
1401 if (isQObject(value))
1402 return qVariantFromValue(qtObjectFromJS(value));
1403 return variantMapFromJS(value->ToObject());
1406 bool QV8Engine::convertToNativeQObject(v8::Handle<v8::Value> value,
1407 const QByteArray &targetType,
1410 if (!targetType.endsWith('*'))
1412 if (QObject *qobject = qtObjectFromJS(value)) {
1413 int start = targetType.startsWith("const ") ? 6 : 0;
1414 QByteArray className = targetType.mid(start, targetType.size()-start-1);
1415 if (void *instance = qobject->qt_metacast(className)) {
1423 QObject *QV8Engine::qtObjectFromJS(v8::Handle<v8::Value> value)
1425 if (!value->IsObject())
1428 QV8ObjectResource *r = (QV8ObjectResource *)value->ToObject()->GetExternalResource();
1431 QV8ObjectResource::ResourceType type = r->resourceType();
1432 if (type == QV8ObjectResource::QObjectType)
1433 return qobjectWrapper()->toQObject(r);
1434 else if (type == QV8ObjectResource::VariantType) {
1435 QVariant variant = variantWrapper()->toVariant(r);
1436 int type = variant.userType();
1437 if ((type == QMetaType::QObjectStar) || (type == QMetaType::QWidgetStar))
1438 return *reinterpret_cast<QObject* const *>(variant.constData());
1444 QVariant &QV8Engine::variantValue(v8::Handle<v8::Value> value)
1446 return variantWrapper()->variantValue(value);
1449 // Creates a QVariant wrapper object.
1450 v8::Local<v8::Object> QV8Engine::newVariant(const QVariant &value)
1452 return variantWrapper()->newVariant(value);
1455 QScriptPassPointer<QJSValuePrivate> QV8Engine::evaluate(v8::Handle<v8::Script> script, v8::TryCatch& tryCatch)
1457 v8::HandleScope handleScope;
1460 if (script.IsEmpty()) {
1461 v8::Handle<v8::Value> exception = tryCatch.Exception();
1462 if (exception.IsEmpty()) {
1463 // This is possible on syntax errors like { a:12, b:21 } <- missing "(", ")" around expression.
1464 return InvalidValue();
1466 setException(exception, tryCatch.Message());
1467 return new QJSValuePrivate(this, exception);
1469 v8::Handle<v8::Value> result;
1470 result = script->Run();
1471 if (result.IsEmpty()) {
1472 v8::Handle<v8::Value> exception = tryCatch.Exception();
1473 // TODO: figure out why v8 doesn't always produce an exception value
1474 //Q_ASSERT(!exception.IsEmpty());
1475 if (exception.IsEmpty())
1476 exception = v8::Exception::Error(v8::String::New("missing exception value"));
1477 setException(exception, tryCatch.Message());
1478 return new QJSValuePrivate(this, exception);
1480 return new QJSValuePrivate(this, result);
1483 QJSValue QV8Engine::scriptValueFromInternal(v8::Handle<v8::Value> value) const
1485 if (value.IsEmpty())
1486 return QJSValuePrivate::get(InvalidValue());
1487 return QJSValuePrivate::get(new QJSValuePrivate(const_cast<QV8Engine*>(this), value));
1490 QScriptPassPointer<QJSValuePrivate> QV8Engine::newArray(uint length)
1492 return new QJSValuePrivate(this, v8::Array::New(length));
1495 void QV8Engine::emitSignalHandlerException()
1497 emit q->signalHandlerException(scriptValueFromInternal(uncaughtException()));
1500 void QV8Engine::startTimer(const QString &timerName)
1502 if (!m_time.isValid())
1504 m_startedTimers[timerName] = m_time.elapsed();
1507 qint64 QV8Engine::stopTimer(const QString &timerName, bool *wasRunning)
1509 if (!m_startedTimers.contains(timerName)) {
1510 *wasRunning = false;
1514 qint64 startedAt = m_startedTimers.take(timerName);
1515 return m_time.elapsed() - startedAt;
1518 int QV8Engine::consoleCountHelper(const QString &file, int line, int column)
1520 const QString key = file + QString::number(line) + QString::number(column);
1521 int number = m_consoleCount.value(key, 0);
1523 m_consoleCount.insert(key, number);
1527 void QV8GCCallback::registerGcPrologueCallback()
1529 QV8Engine::ThreadData *td = QV8Engine::threadData();
1530 if (!td->gcPrologueCallbackRegistered) {
1531 td->gcPrologueCallbackRegistered = true;
1532 v8::V8::AddGCPrologueCallback(QV8GCCallback::garbageCollectorPrologueCallback, v8::kGCTypeMarkSweepCompact);
1536 QV8GCCallback::Node::Node(PrologueCallback callback)
1537 : prologueCallback(callback)
1541 QV8GCCallback::Node::~Node()
1547 Ensure that each persistent handle is strong if it has CPP ownership
1548 and has no implicitly JS owned object owner in its parent chain, and
1551 Any weak handle whose parent object is still alive will have an implicit
1552 reference (between the parent and the handle) added, so that it will
1555 Note that this callback is registered only for kGCTypeMarkSweepCompact
1556 collection cycles, as it is during collection cycles of that type
1557 in which weak persistent handle callbacks are called when required.
1559 void QV8GCCallback::garbageCollectorPrologueCallback(v8::GCType, v8::GCCallbackFlags)
1561 if (!QV8Engine::hasThreadData())
1564 QV8Engine::ThreadData *td = QV8Engine::threadData();
1565 QV8GCCallback::Node *currNode = td->gcCallbackNodes.first();
1568 // The client which adds itself to the list is responsible
1569 // for maintaining the correct implicit references in the
1570 // specified callback.
1571 currNode->prologueCallback(currNode);
1572 currNode = td->gcCallbackNodes.next(currNode);
1576 void QV8GCCallback::addGcCallbackNode(QV8GCCallback::Node *node)
1578 QV8Engine::ThreadData *td = QV8Engine::threadData();
1579 td->gcCallbackNodes.insert(node);
1582 QV8Engine::ThreadData::ThreadData()
1583 : gcPrologueCallbackRegistered(false)
1585 if (!v8::Isolate::GetCurrent()) {
1586 isolate = v8::Isolate::New();
1593 QV8Engine::ThreadData::~ThreadData()