Trim trailing whitespace.
[profile/ivi/qtdeclarative.git] / src / qml / qml / v8 / qv8engine.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the QtQml module of the Qt Toolkit.
7 **
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.
16 **
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.
20 **
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.
28 **
29 ** Other Usage
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.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qv8engine_p.h"
43
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
50 #include <private/qqmlbuiltinfunctions_p.h>
51 #include <private/qqmllist_p.h>
52 #include <private/qqmlengine_p.h>
53 #include <private/qqmlxmlhttprequest_p.h>
54 #include <private/qqmllocale_p.h>
55 #include <private/qqmlglobal_p.h>
56 #include <private/qqmlmemoryprofiler_p.h>
57
58 #include "qscript_impl_p.h"
59 #include "qv8domerrors_p.h"
60 #include "qv8sqlerrors_p.h"
61
62 #include <QtCore/qjsonarray.h>
63 #include <QtCore/qjsonobject.h>
64 #include <QtCore/qjsonvalue.h>
65
66 Q_DECLARE_METATYPE(QJSValue)
67 Q_DECLARE_METATYPE(QList<int>)
68
69
70 // XXX TODO: Need to check all the global functions will also work in a worker script where the
71 // QQmlEngine is not available
72 QT_BEGIN_NAMESPACE
73
74 static bool ObjectComparisonCallback(v8::Local<v8::Object> lhs, v8::Local<v8::Object> rhs)
75 {
76     if (lhs == rhs)
77         return true;
78
79     if (lhs.IsEmpty() || rhs.IsEmpty())
80         return false;
81
82     QV8ObjectResource *lhsr = static_cast<QV8ObjectResource*>(lhs->GetExternalResource());
83     QV8ObjectResource *rhsr = static_cast<QV8ObjectResource*>(rhs->GetExternalResource());
84
85     if (lhsr && rhsr) {
86         Q_ASSERT(lhsr->engine == rhsr->engine);
87         QV8ObjectResource::ResourceType lhst = lhsr->resourceType();
88         QV8ObjectResource::ResourceType rhst = rhsr->resourceType();
89
90         switch (lhst) {
91         case QV8ObjectResource::ValueTypeType:
92             // a value type might be equal to a variant or another value type
93             if (rhst == QV8ObjectResource::ValueTypeType) {
94                 return lhsr->engine->valueTypeWrapper()->isEqual(lhsr, lhsr->engine->valueTypeWrapper()->toVariant(rhsr));
95             } else if (rhst == QV8ObjectResource::VariantType) {
96                 return lhsr->engine->valueTypeWrapper()->isEqual(lhsr, lhsr->engine->variantWrapper()->toVariant(rhsr));
97             }
98             break;
99         case QV8ObjectResource::VariantType:
100             // a variant might be equal to a value type or other variant.
101             if (rhst == QV8ObjectResource::VariantType) {
102                 return lhsr->engine->variantWrapper()->toVariant(lhsr) ==
103                        lhsr->engine->variantWrapper()->toVariant(rhsr);
104             } else if (rhst == QV8ObjectResource::ValueTypeType) {
105                 return rhsr->engine->valueTypeWrapper()->isEqual(rhsr, rhsr->engine->variantWrapper()->toVariant(lhsr));
106             }
107             break;
108         case QV8ObjectResource::SequenceType:
109             // a sequence might be equal to itself.
110             if (rhst == QV8ObjectResource::SequenceType) {
111                 return lhsr->engine->sequenceWrapper()->isEqual(lhsr, rhsr);
112             }
113             break;
114         default:
115             break;
116         }
117     }
118
119     return false;
120 }
121
122
123 QV8Engine::QV8Engine(QJSEngine* qq, ContextOwnership ownership)
124     : q(qq)
125     , m_engine(0)
126     , m_ownsV8Context(ownership == CreateNewContext)
127     , m_xmlHttpRequestData(0)
128     , m_listModelData(0)
129     , m_application(0)
130 {
131     QML_MEMORY_SCOPE_STRING("QV8Engine::QV8Engine");
132     qMetaTypeId<QJSValue>();
133     qMetaTypeId<QList<int> >();
134
135     QByteArray v8args = qgetenv("V8ARGS");
136     // change default v8 behaviour to not relocate breakpoints across lines
137     if (!v8args.contains("breakpoint_relocation"))
138         v8args.append(" --nobreakpoint_relocation");
139     v8::V8::SetFlagsFromString(v8args.constData(), v8args.length());
140
141     ensurePerThreadIsolate();
142
143     v8::HandleScope handle_scope;
144     m_context = (ownership == CreateNewContext) ? v8::Context::New() : v8::Persistent<v8::Context>::New(v8::Context::GetCurrent());
145     qPersistentRegister(m_context);
146     m_originalGlobalObject.init(m_context);
147     v8::Context::Scope context_scope(m_context);
148
149     v8::V8::SetUserObjectComparisonCallbackFunction(ObjectComparisonCallback);
150     QV8GCCallback::registerGcPrologueCallback();
151     m_strongReferencer = qPersistentNew(v8::Object::New());
152
153     m_bindingFlagKey = qPersistentNew(v8::String::New("qml::binding"));
154
155     m_stringWrapper.init();
156     m_contextWrapper.init(this);
157     m_qobjectWrapper.init(this);
158     m_typeWrapper.init(this);
159     m_listWrapper.init(this);
160     m_variantWrapper.init(this);
161     m_valueTypeWrapper.init(this);
162     m_sequenceWrapper.init(this);
163     m_jsonWrapper.init(this);
164
165     {
166     v8::Handle<v8::Value> v = global()->Get(v8::String::New("Object"))->ToObject()->Get(v8::String::New("getOwnPropertyNames"));
167     m_getOwnPropertyNames = qPersistentNew<v8::Function>(v8::Handle<v8::Function>::Cast(v));
168     }
169 }
170
171 QV8Engine::~QV8Engine()
172 {
173     Q_ASSERT_X(v8::Isolate::GetCurrent(), "QV8Engine::~QV8Engine()", "called after v8::Isolate has exited");
174     for (int ii = 0; ii < m_extensionData.count(); ++ii)
175         delete m_extensionData[ii];
176     m_extensionData.clear();
177
178     qt_rem_qmlxmlhttprequest(this, m_xmlHttpRequestData);
179     m_xmlHttpRequestData = 0;
180     delete m_listModelData;
181     m_listModelData = 0;
182
183     qPersistentDispose(m_freezeObject);
184     qPersistentDispose(m_getOwnPropertyNames);
185
186     invalidateAllValues();
187
188     qPersistentDispose(m_strongReferencer);
189
190     m_jsonWrapper.destroy();
191     m_sequenceWrapper.destroy();
192     m_valueTypeWrapper.destroy();
193     m_variantWrapper.destroy();
194     m_listWrapper.destroy();
195     m_typeWrapper.destroy();
196     m_qobjectWrapper.destroy();
197     m_contextWrapper.destroy();
198     m_stringWrapper.destroy();
199
200     qPersistentDispose(m_bindingFlagKey);
201
202     m_originalGlobalObject.destroy();
203
204     if (m_ownsV8Context)
205         qPersistentDispose(m_context);
206 }
207
208 QString QV8Engine::toStringStatic(v8::Handle<v8::Value> jsstr)
209 {
210     return toStringStatic(jsstr->ToString());
211 }
212
213 QString QV8Engine::toStringStatic(v8::Handle<v8::String> jsstr)
214 {
215     QString qstr;
216     qstr.resize(jsstr->Length());
217     jsstr->Write((uint16_t*)qstr.data());
218     return qstr;
219 }
220
221 QVariant QV8Engine::toVariant(v8::Handle<v8::Value> value, int typeHint)
222 {
223     if (value.IsEmpty())
224         return QVariant();
225
226     if (typeHint == QVariant::Bool)
227         return QVariant(value->BooleanValue());
228
229     if (typeHint == QMetaType::QJsonValue)
230         return QVariant::fromValue(jsonValueFromJS(value));
231
232     if (typeHint == qMetaTypeId<QJSValue>())
233         return QVariant::fromValue(scriptValueFromInternal(value));
234
235     if (value->IsObject()) {
236         QV8ObjectResource *r = (QV8ObjectResource *)value->ToObject()->GetExternalResource();
237         if (r) {
238             switch (r->resourceType()) {
239             case QV8ObjectResource::Context2DStyleType:
240             case QV8ObjectResource::Context2DPixelArrayType:
241             case QV8ObjectResource::SignalHandlerType:
242             case QV8ObjectResource::IncubatorType:
243             case QV8ObjectResource::VisualDataItemType:
244             case QV8ObjectResource::ContextType:
245             case QV8ObjectResource::XMLHttpRequestType:
246             case QV8ObjectResource::DOMNodeType:
247             case QV8ObjectResource::SQLDatabaseType:
248             case QV8ObjectResource::ListModelType:
249             case QV8ObjectResource::Context2DType:
250             case QV8ObjectResource::ParticleDataType:
251             case QV8ObjectResource::LocaleDataType:
252             case QV8ObjectResource::ChangeSetArrayType:
253                 return QVariant();
254             case QV8ObjectResource::TypeType:
255                 return m_typeWrapper.toVariant(r);
256             case QV8ObjectResource::QObjectType:
257                 return qVariantFromValue<QObject *>(m_qobjectWrapper.toQObject(r));
258             case QV8ObjectResource::ListType:
259                 return m_listWrapper.toVariant(r);
260             case QV8ObjectResource::VariantType:
261                 return m_variantWrapper.toVariant(r);
262             case QV8ObjectResource::ValueTypeType:
263                 return m_valueTypeWrapper.toVariant(r);
264             case QV8ObjectResource::SequenceType:
265                 return m_sequenceWrapper.toVariant(r);
266             }
267         } else if (typeHint == QMetaType::QJsonObject
268                    && !value->IsArray() && !value->IsFunction()) {
269             return QVariant::fromValue(jsonObjectFromJS(value));
270         }
271     }
272
273     if (value->IsArray()) {
274         v8::Handle<v8::Array> array = v8::Handle<v8::Array>::Cast(value);
275         if (typeHint == qMetaTypeId<QList<QObject *> >()) {
276             QList<QObject *> list;
277             uint32_t length = array->Length();
278             for (uint32_t ii = 0; ii < length; ++ii) {
279                 v8::Local<v8::Value> arrayItem = array->Get(ii);
280                 if (arrayItem->IsObject()) {
281                     list << toQObject(arrayItem->ToObject());
282                 } else {
283                     list << 0;
284                 }
285             }
286
287             return qVariantFromValue<QList<QObject*> >(list);
288         } else if (typeHint == QMetaType::QJsonArray) {
289             return QVariant::fromValue(jsonArrayFromJS(value));
290         }
291
292         bool succeeded = false;
293         QVariant retn = m_sequenceWrapper.toVariant(array, typeHint, &succeeded);
294         if (succeeded)
295             return retn;
296     }
297
298     return toBasicVariant(value);
299 }
300
301 static v8::Handle<v8::Array> arrayFromStringList(QV8Engine *engine, const QStringList &list)
302 {
303     v8::Context::Scope scope(engine->context());
304     v8::Local<v8::Array> result = v8::Array::New(list.count());
305     for (int ii = 0; ii < list.count(); ++ii)
306         result->Set(ii, engine->toString(list.at(ii)));
307     return result;
308 }
309
310 static v8::Handle<v8::Array> arrayFromVariantList(QV8Engine *engine, const QVariantList &list)
311 {
312     v8::Context::Scope scope(engine->context());
313     v8::Local<v8::Array> result = v8::Array::New(list.count());
314     for (int ii = 0; ii < list.count(); ++ii)
315         result->Set(ii, engine->fromVariant(list.at(ii)));
316     return result;
317 }
318
319 static v8::Handle<v8::Object> objectFromVariantMap(QV8Engine *engine, const QVariantMap &map)
320 {
321     v8::Context::Scope scope(engine->context());
322     v8::Local<v8::Object> object = v8::Object::New();
323     for (QVariantMap::ConstIterator iter = map.begin(); iter != map.end(); ++iter)
324         object->Set(engine->toString(iter.key()), engine->fromVariant(iter.value()));
325     return object;
326 }
327
328 Q_CORE_EXPORT QString qt_regexp_toCanonical(const QString &, QRegExp::PatternSyntax);
329
330 v8::Handle<v8::Value> QV8Engine::fromVariant(const QVariant &variant)
331 {
332     int type = variant.userType();
333     const void *ptr = variant.constData();
334
335     if (type < QMetaType::User) {
336         switch (QMetaType::Type(type)) {
337             case QMetaType::UnknownType:
338             case QMetaType::Void:
339                 return v8::Undefined();
340             case QMetaType::Bool:
341                 return v8::Boolean::New(*reinterpret_cast<const bool*>(ptr));
342             case QMetaType::Int:
343                 return v8::Integer::New(*reinterpret_cast<const int*>(ptr));
344             case QMetaType::UInt:
345                 return v8::Integer::NewFromUnsigned(*reinterpret_cast<const uint*>(ptr));
346             case QMetaType::LongLong:
347                 return v8::Number::New(*reinterpret_cast<const qlonglong*>(ptr));
348             case QMetaType::ULongLong:
349                 return v8::Number::New(*reinterpret_cast<const qulonglong*>(ptr));
350             case QMetaType::Double:
351                 return v8::Number::New(*reinterpret_cast<const double*>(ptr));
352             case QMetaType::QString:
353                 return m_stringWrapper.toString(*reinterpret_cast<const QString*>(ptr));
354             case QMetaType::Float:
355                 return v8::Number::New(*reinterpret_cast<const float*>(ptr));
356             case QMetaType::Short:
357                 return v8::Integer::New(*reinterpret_cast<const short*>(ptr));
358             case QMetaType::UShort:
359                 return v8::Integer::NewFromUnsigned(*reinterpret_cast<const unsigned short*>(ptr));
360             case QMetaType::Char:
361                 return v8::Integer::New(*reinterpret_cast<const char*>(ptr));
362             case QMetaType::UChar:
363                 return v8::Integer::NewFromUnsigned(*reinterpret_cast<const unsigned char*>(ptr));
364             case QMetaType::QChar:
365                 return v8::Integer::New((*reinterpret_cast<const QChar*>(ptr)).unicode());
366             case QMetaType::QDateTime:
367                 return v8::Date::New(qtDateTimeToJsDate(*reinterpret_cast<const QDateTime *>(ptr)));
368             case QMetaType::QDate:
369                 return v8::Date::New(qtDateTimeToJsDate(QDateTime(*reinterpret_cast<const QDate *>(ptr))));
370             case QMetaType::QTime:
371                 return v8::Date::New(qtDateTimeToJsDate(QDateTime(QDate(1970,1,1), *reinterpret_cast<const QTime *>(ptr))));
372             case QMetaType::QRegExp:
373                 return QJSConverter::toRegExp(*reinterpret_cast<const QRegExp *>(ptr));
374             case QMetaType::QObjectStar:
375                 return newQObject(*reinterpret_cast<QObject* const *>(ptr));
376             case QMetaType::QStringList:
377                 {
378                 bool succeeded = false;
379                 v8::Handle<v8::Value> retn = m_sequenceWrapper.fromVariant(variant, &succeeded);
380                 if (succeeded)
381                     return retn;
382                 return arrayFromStringList(this, *reinterpret_cast<const QStringList *>(ptr));
383                 }
384             case QMetaType::QVariantList:
385                 return arrayFromVariantList(this, *reinterpret_cast<const QVariantList *>(ptr));
386             case QMetaType::QVariantMap:
387                 return objectFromVariantMap(this, *reinterpret_cast<const QVariantMap *>(ptr));
388             case QMetaType::QJsonValue:
389                 return jsonValueToJS(*reinterpret_cast<const QJsonValue *>(ptr));
390             case QMetaType::QJsonObject:
391                 return jsonObjectToJS(*reinterpret_cast<const QJsonObject *>(ptr));
392             case QMetaType::QJsonArray:
393                 return jsonArrayToJS(*reinterpret_cast<const QJsonArray *>(ptr));
394
395             default:
396                 break;
397         }
398
399         if (QQmlValueType *vt = QQmlValueTypeFactory::valueType(type))
400             return m_valueTypeWrapper.newValueType(variant, vt);
401     } else {
402         if (type == qMetaTypeId<QQmlListReference>()) {
403             typedef QQmlListReferencePrivate QDLRP;
404             QDLRP *p = QDLRP::get((QQmlListReference*)ptr);
405             if (p->object) {
406                 return m_listWrapper.newList(p->property, p->propertyType);
407             } else {
408                 return v8::Null();
409             }
410         } else if (type == qMetaTypeId<QJSValue>()) {
411             const QJSValue *value = reinterpret_cast<const QJSValue *>(ptr);
412             QJSValuePrivate *valuep = QJSValuePrivate::get(*value);
413             if (valuep->assignEngine(this))
414                 return v8::Local<v8::Value>::New(*valuep);
415         } else if (type == qMetaTypeId<QList<QObject *> >()) {
416             // XXX Can this be made more by using Array as a prototype and implementing
417             // directly against QList<QObject*>?
418             const QList<QObject *> &list = *(QList<QObject *>*)ptr;
419             v8::Local<v8::Array> array = v8::Array::New(list.count());
420             for (int ii = 0; ii < list.count(); ++ii)
421                 array->Set(ii, newQObject(list.at(ii)));
422             return array;
423         }
424
425         bool objOk;
426         QObject *obj = QQmlMetaType::toQObject(variant, &objOk);
427         if (objOk)
428             return newQObject(obj);
429
430         bool succeeded = false;
431         v8::Handle<v8::Value> retn = m_sequenceWrapper.fromVariant(variant, &succeeded);
432         if (succeeded)
433             return retn;
434
435         if (QQmlValueType *vt = QQmlValueTypeFactory::valueType(type))
436             return m_valueTypeWrapper.newValueType(variant, vt);
437     }
438
439     // XXX TODO: To be compatible, we still need to handle:
440     //    + QObjectList
441     //    + QList<int>
442
443     return m_variantWrapper.newVariant(variant);
444 }
445
446 // A handle scope and context must be entered
447 v8::Local<v8::Script> QV8Engine::qmlModeCompile(const QString &source,
448                                                 const QString &fileName,
449                                                 quint16 lineNumber)
450 {
451     v8::Local<v8::String> v8source = m_stringWrapper.toString(source);
452     v8::Local<v8::String> v8fileName = m_stringWrapper.toString(fileName);
453
454     v8::ScriptOrigin origin(v8fileName, v8::Integer::New(lineNumber - 1));
455
456     v8::Local<v8::Script> script = v8::Script::Compile(v8source, &origin, 0, v8::Handle<v8::String>(),
457                                                        v8::Script::QmlMode);
458
459     return script;
460 }
461
462 // A handle scope and context must be entered.
463 // source can be either ascii or utf8.
464 v8::Local<v8::Script> QV8Engine::qmlModeCompile(const char *source, int sourceLength,
465                                                 const QString &fileName,
466                                                 quint16 lineNumber)
467 {
468     if (sourceLength == -1)
469         sourceLength = int(strlen(source));
470
471     v8::Local<v8::String> v8source = v8::String::New(source, sourceLength);
472     v8::Local<v8::String> v8fileName = m_stringWrapper.toString(fileName);
473
474     v8::ScriptOrigin origin(v8fileName, v8::Integer::New(lineNumber - 1));
475
476     v8::Local<v8::Script> script = v8::Script::Compile(v8source, &origin, 0, v8::Handle<v8::String>(),
477                                                        v8::Script::QmlMode);
478
479     return script;
480 }
481
482 QNetworkAccessManager *QV8Engine::networkAccessManager()
483 {
484     return QQmlEnginePrivate::get(m_engine)->getNetworkAccessManager();
485 }
486
487 const QStringHash<bool> &QV8Engine::illegalNames() const
488 {
489     return m_illegalNames;
490 }
491
492 // Requires a handle scope
493 v8::Local<v8::Array> QV8Engine::getOwnPropertyNames(v8::Handle<v8::Object> o)
494 {
495     // FIXME Newer v8 have API for this function
496     v8::TryCatch tc;
497     v8::Handle<v8::Value> args[] = { o };
498     v8::Local<v8::Value> r = m_getOwnPropertyNames->Call(global(), 1, args);
499     if (tc.HasCaught())
500         return v8::Array::New();
501     else
502         return v8::Local<v8::Array>::Cast(r);
503 }
504
505 QQmlContextData *QV8Engine::callingContext()
506 {
507     return m_contextWrapper.callingContext();
508 }
509
510 // Converts a JS value to a QVariant.
511 // Null, Undefined -> QVariant() (invalid)
512 // Boolean -> QVariant(bool)
513 // Number -> QVariant(double)
514 // String -> QVariant(QString)
515 // Array -> QVariantList(...)
516 // Date -> QVariant(QDateTime)
517 // RegExp -> QVariant(QRegExp)
518 // [Any other object] -> QVariantMap(...)
519 QVariant QV8Engine::toBasicVariant(v8::Handle<v8::Value> value)
520 {
521     if (value->IsNull() || value->IsUndefined())
522         return QVariant();
523     if (value->IsBoolean())
524         return value->ToBoolean()->Value();
525     if (value->IsInt32())
526         return value->ToInt32()->Value();
527     if (value->IsNumber())
528         return value->ToNumber()->Value();
529     if (value->IsString())
530         return m_stringWrapper.toString(value->ToString());
531     if (value->IsDate())
532         return qtDateTimeFromJsDate(v8::Handle<v8::Date>::Cast(value)->NumberValue());
533     // NOTE: since we convert QTime to JS Date, round trip will change the variant type (to QDateTime)!
534
535     Q_ASSERT(value->IsObject());
536
537     if (value->IsRegExp()) {
538         v8::Context::Scope scope(context());
539         return QJSConverter::toRegExp(v8::Handle<v8::RegExp>::Cast(value));
540     }
541     if (value->IsArray()) {
542         v8::Context::Scope scope(context());
543         QVariantList rv;
544
545         v8::Handle<v8::Array> array = v8::Handle<v8::Array>::Cast(value);
546         int length = array->Length();
547         for (int ii = 0; ii < length; ++ii)
548             rv << toVariant(array->Get(ii), -1);
549         return rv;
550     }
551     if (!value->IsFunction()) {
552         v8::Context::Scope scope(context());
553         v8::Handle<v8::Object> object = value->ToObject();
554         return variantMapFromJS(object);
555     }
556
557     return QVariant();
558 }
559
560
561
562 struct StaticQtMetaObject : public QObject
563 {
564     static const QMetaObject *get()
565         { return &static_cast<StaticQtMetaObject*> (0)->staticQtMetaObject; }
566 };
567
568 void QV8Engine::initializeGlobal(v8::Handle<v8::Object> global)
569 {
570     using namespace QQmlBuiltinFunctions;
571
572     v8::Local<v8::Object> console = v8::Object::New();
573     v8::Local<v8::Function> consoleLogFn = V8FUNCTION(consoleLog, this);
574
575     console->Set(v8::String::New("debug"), consoleLogFn);
576     console->Set(v8::String::New("log"), consoleLogFn);
577     console->Set(v8::String::New("info"), consoleLogFn);
578     console->Set(v8::String::New("warn"), V8FUNCTION(consoleWarn, this));
579     console->Set(v8::String::New("error"), V8FUNCTION(consoleError, this));
580     console->Set(v8::String::New("assert"), V8FUNCTION(consoleAssert, this));
581
582     console->Set(v8::String::New("count"), V8FUNCTION(consoleCount, this));
583     console->Set(v8::String::New("profile"), V8FUNCTION(consoleProfile, this));
584     console->Set(v8::String::New("profileEnd"), V8FUNCTION(consoleProfileEnd, this));
585     console->Set(v8::String::New("time"), V8FUNCTION(consoleTime, this));
586     console->Set(v8::String::New("timeEnd"), V8FUNCTION(consoleTimeEnd, this));
587     console->Set(v8::String::New("trace"), V8FUNCTION(consoleTrace, this));
588     console->Set(v8::String::New("exception"), V8FUNCTION(consoleException, this));
589
590     v8::Local<v8::Object> qt = v8::Object::New();
591
592     // Set all the enums from the "Qt" namespace
593     const QMetaObject *qtMetaObject = StaticQtMetaObject::get();
594     for (int ii = 0; ii < qtMetaObject->enumeratorCount(); ++ii) {
595         QMetaEnum enumerator = qtMetaObject->enumerator(ii);
596         for (int jj = 0; jj < enumerator.keyCount(); ++jj) {
597             qt->Set(v8::String::New(enumerator.key(jj)), v8::Integer::New(enumerator.value(jj)));
598         }
599     }
600     qt->Set(v8::String::New("Asynchronous"), v8::Integer::New(0));
601     qt->Set(v8::String::New("Synchronous"), v8::Integer::New(1));
602
603     qt->Set(v8::String::New("include"), V8FUNCTION(QV8Include::include, this));
604     qt->Set(v8::String::New("isQtObject"), V8FUNCTION(isQtObject, this));
605     qt->Set(v8::String::New("rgba"), V8FUNCTION(rgba, this));
606     qt->Set(v8::String::New("hsla"), V8FUNCTION(hsla, this));
607     qt->Set(v8::String::New("colorEqual"), V8FUNCTION(colorEqual, this));
608     qt->Set(v8::String::New("font"), V8FUNCTION(font, this));
609     qt->Set(v8::String::New("rect"), V8FUNCTION(rect, this));
610     qt->Set(v8::String::New("point"), V8FUNCTION(point, this));
611     qt->Set(v8::String::New("size"), V8FUNCTION(size, this));
612
613     qt->Set(v8::String::New("vector2d"), V8FUNCTION(vector2d, this));
614     qt->Set(v8::String::New("vector3d"), V8FUNCTION(vector3d, this));
615     qt->Set(v8::String::New("vector4d"), V8FUNCTION(vector4d, this));
616     qt->Set(v8::String::New("quaternion"), V8FUNCTION(quaternion, this));
617     qt->Set(v8::String::New("matrix4x4"), V8FUNCTION(matrix4x4, this));
618
619     qt->Set(v8::String::New("formatDate"), V8FUNCTION(formatDate, this));
620     qt->Set(v8::String::New("formatTime"), V8FUNCTION(formatTime, this));
621     qt->Set(v8::String::New("formatDateTime"), V8FUNCTION(formatDateTime, this));
622
623     qt->Set(v8::String::New("openUrlExternally"), V8FUNCTION(openUrlExternally, this));
624     qt->Set(v8::String::New("fontFamilies"), V8FUNCTION(fontFamilies, this));
625     qt->Set(v8::String::New("md5"), V8FUNCTION(md5, this));
626     qt->Set(v8::String::New("btoa"), V8FUNCTION(btoa, this));
627     qt->Set(v8::String::New("atob"), V8FUNCTION(atob, this));
628     qt->Set(v8::String::New("resolvedUrl"), V8FUNCTION(resolvedUrl, this));
629     qt->Set(v8::String::New("locale"), V8FUNCTION(locale, this));
630     qt->Set(v8::String::New("binding"), V8FUNCTION(binding, this));
631
632     if (m_engine) {
633         qt->SetAccessor(v8::String::New("application"), getApplication, 0, v8::External::New(this));
634         qt->SetAccessor(v8::String::New("inputMethod"), getInputMethod, 0, v8::External::New(this));
635         qt->Set(v8::String::New("lighter"), V8FUNCTION(lighter, this));
636         qt->Set(v8::String::New("darker"), V8FUNCTION(darker, this));
637         qt->Set(v8::String::New("tint"), V8FUNCTION(tint, this));
638         qt->Set(v8::String::New("quit"), V8FUNCTION(quit, this));
639         qt->Set(v8::String::New("createQmlObject"), V8FUNCTION(createQmlObject, this));
640         qt->Set(v8::String::New("createComponent"), V8FUNCTION(createComponent, this));
641     }
642
643     global->Set(v8::String::New("qsTranslate"), V8FUNCTION(qsTranslate, this));
644     global->Set(v8::String::New("QT_TRANSLATE_NOOP"), V8FUNCTION(qsTranslateNoOp, this));
645     global->Set(v8::String::New("qsTr"), V8FUNCTION(qsTr, this));
646     global->Set(v8::String::New("QT_TR_NOOP"), V8FUNCTION(qsTrNoOp, this));
647     global->Set(v8::String::New("qsTrId"), V8FUNCTION(qsTrId, this));
648     global->Set(v8::String::New("QT_TRID_NOOP"), V8FUNCTION(qsTrIdNoOp, this));
649
650     global->Set(v8::String::New("print"), consoleLogFn);
651     global->Set(v8::String::New("console"), console);
652     global->Set(v8::String::New("Qt"), qt);
653     global->Set(v8::String::New("gc"), V8FUNCTION(QQmlBuiltinFunctions::gc, this));
654
655     {
656 #define STRING_ARG "(function(stringArg) { "\
657                    "    String.prototype.arg = (function() {"\
658                    "        return stringArg.apply(this, arguments);"\
659                    "    })"\
660                    "})"
661
662         v8::Local<v8::Script> registerArg = v8::Script::New(v8::String::New(STRING_ARG), 0, 0, v8::Handle<v8::String>(), v8::Script::NativeMode);
663         v8::Local<v8::Value> result = registerArg->Run();
664         Q_ASSERT(result->IsFunction());
665         v8::Local<v8::Function> registerArgFunc = v8::Local<v8::Function>::Cast(result);
666         v8::Handle<v8::Value> args = V8FUNCTION(stringArg, this);
667         registerArgFunc->Call(v8::Local<v8::Object>::Cast(registerArgFunc), 1, &args);
668 #undef STRING_ARG
669     }
670
671     QQmlLocale::registerStringLocaleCompare(this);
672     QQmlDateExtension::registerExtension(this);
673     QQmlNumberExtension::registerExtension(this);
674
675     qt_add_domexceptions(this);
676     m_xmlHttpRequestData = qt_add_qmlxmlhttprequest(this);
677
678     qt_add_sqlexceptions(this);
679
680     {
681     v8::Handle<v8::Value> args[] = { global };
682     v8::Local<v8::Value> names = m_getOwnPropertyNames->Call(global, 1, args);
683     v8::Local<v8::Array> namesArray = v8::Local<v8::Array>::Cast(names);
684     for (quint32 ii = 0; ii < namesArray->Length(); ++ii)
685         m_illegalNames.insert(toString(namesArray->Get(ii)), true);
686     }
687
688     {
689 #define FREEZE_SOURCE "(function freeze_recur(obj) { "\
690                       "    if (Qt.isQtObject(obj)) return;"\
691                       "    if (obj != Function.connect && obj != Function.disconnect && "\
692                       "        obj instanceof Object) {"\
693                       "        var properties = Object.getOwnPropertyNames(obj);"\
694                       "        for (var prop in properties) { "\
695                       "            if (prop == \"connect\" || prop == \"disconnect\") {"\
696                       "                Object.freeze(obj[prop]); "\
697                       "                continue;"\
698                       "            }"\
699                       "            freeze_recur(obj[prop]);"\
700                       "        }"\
701                       "    }"\
702                       "    if (obj instanceof Object) {"\
703                       "        Object.freeze(obj);"\
704                       "    }"\
705                       "})"
706
707     v8::Local<v8::Script> freeze = v8::Script::New(v8::String::New(FREEZE_SOURCE));
708     v8::Local<v8::Value> result = freeze->Run();
709     Q_ASSERT(result->IsFunction());
710     m_freezeObject = qPersistentNew(v8::Local<v8::Function>::Cast(result));
711 #undef FREEZE_SOURCE
712     }
713 }
714
715 void QV8Engine::freezeObject(v8::Handle<v8::Value> value)
716 {
717     v8::Handle<v8::Value> args[] = { value };
718     m_freezeObject->Call(global(), 1, args);
719 }
720
721 void QV8Engine::gc()
722 {
723     v8::V8::LowMemoryNotification();
724     while (!v8::V8::IdleNotification()) {}
725 }
726
727 #ifdef QML_GLOBAL_HANDLE_DEBUGGING
728 #include <QtCore/qthreadstorage.h>
729 static QThreadStorage<QSet<void *> *> QV8Engine_activeHandles;
730
731 void QV8Engine::registerHandle(void *handle)
732 {
733     if (!handle) {
734         qWarning("Attempting to register a null handle");
735         return;
736     }
737
738     if (!QV8Engine_activeHandles.hasLocalData())
739         QV8Engine_activeHandles.setLocalData(new QSet<void *>);
740
741     if (QV8Engine_activeHandles.localData()->contains(handle)) {
742         qFatal("Handle %p already alive", handle);
743     } else {
744         QV8Engine_activeHandles.localData()->insert(handle);
745     }
746 }
747
748 void QV8Engine::releaseHandle(void *handle)
749 {
750     if (!handle)
751         return;
752
753     if (!QV8Engine_activeHandles.hasLocalData())
754         QV8Engine_activeHandles.setLocalData(new QSet<void *>);
755
756     if (QV8Engine_activeHandles.localData()->contains(handle)) {
757         QV8Engine_activeHandles.localData()->remove(handle);
758     } else {
759         qFatal("Handle %p already dead", handle);
760     }
761 }
762 #endif
763
764 struct QV8EngineRegistrationData
765 {
766     QV8EngineRegistrationData() : extensionCount(0) {}
767
768     QMutex mutex;
769     int extensionCount;
770 };
771 Q_GLOBAL_STATIC(QV8EngineRegistrationData, registrationData);
772
773 QMutex *QV8Engine::registrationMutex()
774 {
775     return &registrationData()->mutex;
776 }
777
778 int QV8Engine::registerExtension()
779 {
780     return registrationData()->extensionCount++;
781 }
782
783 void QV8Engine::setExtensionData(int index, Deletable *data)
784 {
785     if (m_extensionData.count() <= index)
786         m_extensionData.resize(index + 1);
787
788     if (m_extensionData.at(index))
789         delete m_extensionData.at(index);
790
791     m_extensionData[index] = data;
792 }
793
794 double QV8Engine::qtDateTimeToJsDate(const QDateTime &dt)
795 {
796     if (!dt.isValid()) {
797         return qSNaN();
798     }
799
800     return dt.toMSecsSinceEpoch();
801 }
802
803 QDateTime QV8Engine::qtDateTimeFromJsDate(double jsDate)
804 {
805     if (qIsNaN(jsDate))
806         return QDateTime();
807
808     return QDateTime::fromMSecsSinceEpoch(jsDate);
809 }
810
811 v8::Persistent<v8::Object> *QV8Engine::findOwnerAndStrength(QObject *object, bool *shouldBeStrong)
812 {
813     QObject *parent = object->parent();
814     if (!parent) {
815         // if the object has JS ownership, the object's v8object owns the lifetime of the persistent value.
816         if (QQmlEngine::objectOwnership(object) == QQmlEngine::JavaScriptOwnership) {
817             *shouldBeStrong = false;
818             return &(QQmlData::get(object)->v8object);
819         }
820
821         // no parent, and has CPP ownership - doesn't have an implicit parent.
822         *shouldBeStrong = true;
823         return 0;
824     }
825
826     // if it is owned by CPP, it's root parent may still be owned by JS.
827     // in that case, the owner of the persistent handle is the root parent's v8object.
828     while (parent->parent())
829         parent = parent->parent();
830
831     if (QQmlEngine::objectOwnership(parent) == QQmlEngine::JavaScriptOwnership) {
832         // root parent is owned by JS.  It's v8object owns the persistent value in question.
833         *shouldBeStrong = false;
834         return &(QQmlData::get(parent)->v8object);
835     } else {
836         // root parent has CPP ownership.  The persistent value should not be made weak.
837         *shouldBeStrong = true;
838         return 0;
839     }
840 }
841
842 void QV8Engine::addRelationshipForGC(QObject *object, v8::Persistent<v8::Value> handle)
843 {
844     if (!object || handle.IsEmpty())
845         return;
846
847     bool handleShouldBeStrong = false;
848     v8::Persistent<v8::Object> *implicitOwner = findOwnerAndStrength(object, &handleShouldBeStrong);
849     if (handleShouldBeStrong) {
850         v8::V8::AddImplicitReferences(m_strongReferencer, &handle, 1);
851     } else if (!implicitOwner->IsEmpty()) {
852         v8::V8::AddImplicitReferences(*implicitOwner, &handle, 1);
853     }
854 }
855
856 void QV8Engine::addRelationshipForGC(QObject *object, QObject *other)
857 {
858     if (!object || !other)
859         return;
860
861     bool handleShouldBeStrong = false;
862     v8::Persistent<v8::Object> *implicitOwner = findOwnerAndStrength(object, &handleShouldBeStrong);
863     v8::Persistent<v8::Value> handle = QQmlData::get(other, true)->v8object;
864     if (handle.IsEmpty()) // no JS data to keep alive.
865         return;
866     else if (handleShouldBeStrong)
867         v8::V8::AddImplicitReferences(m_strongReferencer, &handle, 1);
868     else if (!implicitOwner->IsEmpty())
869         v8::V8::AddImplicitReferences(*implicitOwner, &handle, 1);
870 }
871
872 static QThreadStorage<QV8Engine::ThreadData*> perThreadEngineData;
873
874 bool QV8Engine::hasThreadData()
875 {
876     return perThreadEngineData.hasLocalData();
877 }
878
879 QV8Engine::ThreadData *QV8Engine::threadData()
880 {
881     Q_ASSERT(perThreadEngineData.hasLocalData());
882     return perThreadEngineData.localData();
883 }
884
885 void QV8Engine::ensurePerThreadIsolate()
886 {
887     if (!perThreadEngineData.hasLocalData())
888         perThreadEngineData.setLocalData(new ThreadData);
889 }
890
891 void QV8Engine::initQmlGlobalObject()
892 {
893     v8::HandleScope handels;
894     v8::Context::Scope contextScope(m_context);
895     initializeGlobal(m_context->Global());
896     freezeObject(m_context->Global());
897 }
898
899 void QV8Engine::setEngine(QQmlEngine *engine)
900 {
901     m_engine = engine;
902     initQmlGlobalObject();
903 }
904
905 v8::Handle<v8::Value> QV8Engine::throwException(v8::Handle<v8::Value> value)
906 {
907     v8::ThrowException(value);
908     return value;
909 }
910
911 // Converts a QVariantList to JS.
912 // The result is a new Array object with length equal to the length
913 // of the QVariantList, and the elements being the QVariantList's
914 // elements converted to JS, recursively.
915 v8::Local<v8::Array> QV8Engine::variantListToJS(const QVariantList &lst)
916 {
917     v8::Local<v8::Array> result = v8::Array::New(lst.size());
918     for (int i = 0; i < lst.size(); ++i)
919         result->Set(i, variantToJS(lst.at(i)));
920     return result;
921 }
922
923 // Converts a JS Array object to a QVariantList.
924 // The result is a QVariantList with length equal to the length
925 // of the JS Array, and elements being the JS Array's elements
926 // converted to QVariants, recursively.
927 QVariantList QV8Engine::variantListFromJS(v8::Handle<v8::Array> jsArray,
928                                           V8ObjectSet &visitedObjects)
929 {
930     QVariantList result;
931     if (visitedObjects.contains(jsArray))
932         return result; // Avoid recursion.
933     v8::HandleScope handleScope;
934     visitedObjects.insert(jsArray);
935     uint32_t length = jsArray->Length();
936     for (uint32_t i = 0; i < length; ++i)
937         result.append(variantFromJS(jsArray->Get(i), visitedObjects));
938     visitedObjects.remove(jsArray);
939     return result;
940 }
941
942 // Converts a QVariantMap to JS.
943 // The result is a new Object object with property names being
944 // the keys of the QVariantMap, and values being the values of
945 // the QVariantMap converted to JS, recursively.
946 v8::Local<v8::Object> QV8Engine::variantMapToJS(const QVariantMap &vmap)
947 {
948     v8::Local<v8::Object> result = v8::Object::New();
949     QVariantMap::const_iterator it;
950     for (it = vmap.constBegin(); it != vmap.constEnd(); ++it)
951         result->Set(QJSConverter::toString(it.key()), variantToJS(it.value()));
952     return result;
953 }
954
955 // Converts a JS Object to a QVariantMap.
956 // The result is a QVariantMap with keys being the property names
957 // of the object, and values being the values of the JS object's
958 // properties converted to QVariants, recursively.
959 QVariantMap QV8Engine::variantMapFromJS(v8::Handle<v8::Object> jsObject,
960                                         V8ObjectSet &visitedObjects)
961 {
962     QVariantMap result;
963
964     v8::HandleScope handleScope;
965     v8::Handle<v8::Array> propertyNames = jsObject->GetPropertyNames();
966     uint32_t length = propertyNames->Length();
967     if (length == 0)
968         return result;
969
970     if (visitedObjects.contains(jsObject))
971         return result; // Avoid recursion.
972
973     visitedObjects.insert(jsObject);
974     // TODO: Only object's own property names. Include non-enumerable properties.
975     for (uint32_t i = 0; i < length; ++i) {
976         v8::Handle<v8::Value> name = propertyNames->Get(i);
977         result.insert(QJSConverter::toString(name->ToString()),
978                       variantFromJS(jsObject->Get(name), visitedObjects));
979     }
980     visitedObjects.remove(jsObject);
981     return result;
982 }
983
984 // Converts the meta-type defined by the given type and data to JS.
985 // Returns the value if conversion succeeded, an empty handle otherwise.
986 v8::Handle<v8::Value> QV8Engine::metaTypeToJS(int type, const void *data)
987 {
988     Q_ASSERT(data != 0);
989     v8::Handle<v8::Value> result;
990
991     // check if it's one of the types we know
992     switch (QMetaType::Type(type)) {
993     case QMetaType::UnknownType:
994     case QMetaType::Void:
995         return v8::Undefined();
996     case QMetaType::Bool:
997         return v8::Boolean::New(*reinterpret_cast<const bool*>(data));
998     case QMetaType::Int:
999         return v8::Int32::New(*reinterpret_cast<const int*>(data));
1000     case QMetaType::UInt:
1001         return v8::Uint32::New(*reinterpret_cast<const uint*>(data));
1002     case QMetaType::LongLong:
1003         return v8::Number::New(double(*reinterpret_cast<const qlonglong*>(data)));
1004     case QMetaType::ULongLong:
1005 #if defined(Q_OS_WIN) && defined(_MSC_FULL_VER) && _MSC_FULL_VER <= 12008804
1006 #pragma message("** NOTE: You need the Visual Studio Processor Pack to compile support for 64bit unsigned integers.")
1007         return v8::Number::New(double((qlonglong)*reinterpret_cast<const qulonglong*>(data)));
1008 #elif defined(Q_CC_MSVC) && !defined(Q_CC_MSVC_NET)
1009         return v8::Number::New(double((qlonglong)*reinterpret_cast<const qulonglong*>(data)));
1010 #else
1011         return v8::Number::New(double(*reinterpret_cast<const qulonglong*>(data)));
1012 #endif
1013     case QMetaType::Double:
1014         return v8::Number::New(double(*reinterpret_cast<const double*>(data)));
1015     case QMetaType::QString:
1016         return QJSConverter::toString(*reinterpret_cast<const QString*>(data));
1017     case QMetaType::Float:
1018         return v8::Number::New(*reinterpret_cast<const float*>(data));
1019     case QMetaType::Short:
1020         return v8::Int32::New(*reinterpret_cast<const short*>(data));
1021     case QMetaType::UShort:
1022         return v8::Uint32::New(*reinterpret_cast<const unsigned short*>(data));
1023     case QMetaType::Char:
1024         return v8::Int32::New(*reinterpret_cast<const char*>(data));
1025     case QMetaType::UChar:
1026         return v8::Uint32::New(*reinterpret_cast<const unsigned char*>(data));
1027     case QMetaType::QChar:
1028         return v8::Uint32::New((*reinterpret_cast<const QChar*>(data)).unicode());
1029     case QMetaType::QStringList:
1030         result = QJSConverter::toStringList(*reinterpret_cast<const QStringList *>(data));
1031         break;
1032     case QMetaType::QVariantList:
1033         result = variantListToJS(*reinterpret_cast<const QVariantList *>(data));
1034         break;
1035     case QMetaType::QVariantMap:
1036         result = variantMapToJS(*reinterpret_cast<const QVariantMap *>(data));
1037         break;
1038     case QMetaType::QDateTime:
1039         result = QJSConverter::toDateTime(*reinterpret_cast<const QDateTime *>(data));
1040         break;
1041     case QMetaType::QDate:
1042         result = QJSConverter::toDateTime(QDateTime(*reinterpret_cast<const QDate *>(data)));
1043         break;
1044     case QMetaType::QRegExp:
1045         result = QJSConverter::toRegExp(*reinterpret_cast<const QRegExp *>(data));
1046         break;
1047     case QMetaType::QObjectStar:
1048         result = newQObject(*reinterpret_cast<QObject* const *>(data));
1049         break;
1050     case QMetaType::QVariant:
1051         result = variantToJS(*reinterpret_cast<const QVariant*>(data));
1052         break;
1053     case QMetaType::QJsonValue:
1054         result = jsonValueToJS(*reinterpret_cast<const QJsonValue *>(data));
1055         break;
1056     case QMetaType::QJsonObject:
1057         result = jsonObjectToJS(*reinterpret_cast<const QJsonObject *>(data));
1058         break;
1059     case QMetaType::QJsonArray:
1060         result = jsonArrayToJS(*reinterpret_cast<const QJsonArray *>(data));
1061         break;
1062     default:
1063         if (type == qMetaTypeId<QJSValue>()) {
1064             return QJSValuePrivate::get(*reinterpret_cast<const QJSValue*>(data))->asV8Value(this);
1065         } else {
1066             QByteArray typeName = QMetaType::typeName(type);
1067             if (typeName.endsWith('*') && !*reinterpret_cast<void* const *>(data)) {
1068                 return v8::Null();
1069             } else {
1070                 // Fall back to wrapping in a QVariant.
1071                 result = newVariant(QVariant(type, data));
1072             }
1073         }
1074     }
1075     return result;
1076 }
1077
1078 // Converts a JS value to a meta-type.
1079 // data must point to a place that can store a value of the given type.
1080 // Returns true if conversion succeeded, false otherwise.
1081 bool QV8Engine::metaTypeFromJS(v8::Handle<v8::Value> value, int type, void *data) {
1082     // check if it's one of the types we know
1083     switch (QMetaType::Type(type)) {
1084     case QMetaType::Bool:
1085         *reinterpret_cast<bool*>(data) = value->ToBoolean()->Value();
1086         return true;
1087     case QMetaType::Int:
1088         *reinterpret_cast<int*>(data) = value->ToInt32()->Value();
1089         return true;
1090     case QMetaType::UInt:
1091         *reinterpret_cast<uint*>(data) = value->ToUint32()->Value();
1092         return true;
1093     case QMetaType::LongLong:
1094         *reinterpret_cast<qlonglong*>(data) = qlonglong(value->ToInteger()->Value());
1095         return true;
1096     case QMetaType::ULongLong:
1097         *reinterpret_cast<qulonglong*>(data) = qulonglong(value->ToInteger()->Value());
1098         return true;
1099     case QMetaType::Double:
1100         *reinterpret_cast<double*>(data) = value->ToNumber()->Value();
1101         return true;
1102     case QMetaType::QString:
1103         if (value->IsUndefined() || value->IsNull())
1104             *reinterpret_cast<QString*>(data) = QString();
1105         else
1106             *reinterpret_cast<QString*>(data) = QJSConverter::toString(value->ToString());
1107         return true;
1108     case QMetaType::Float:
1109         *reinterpret_cast<float*>(data) = value->ToNumber()->Value();
1110         return true;
1111     case QMetaType::Short:
1112         *reinterpret_cast<short*>(data) = short(value->ToInt32()->Value());
1113         return true;
1114     case QMetaType::UShort:
1115         *reinterpret_cast<unsigned short*>(data) = ushort(value->ToInt32()->Value()); // ### QScript::ToUInt16()
1116         return true;
1117     case QMetaType::Char:
1118         *reinterpret_cast<char*>(data) = char(value->ToInt32()->Value());
1119         return true;
1120     case QMetaType::UChar:
1121         *reinterpret_cast<unsigned char*>(data) = (unsigned char)(value->ToInt32()->Value());
1122         return true;
1123     case QMetaType::QChar:
1124         if (value->IsString()) {
1125             QString str = QJSConverter::toString(v8::Handle<v8::String>::Cast(value));
1126             *reinterpret_cast<QChar*>(data) = str.isEmpty() ? QChar() : str.at(0);
1127         } else {
1128             *reinterpret_cast<QChar*>(data) = QChar(ushort(value->ToInt32()->Value())); // ### QScript::ToUInt16()
1129         }
1130         return true;
1131     case QMetaType::QDateTime:
1132         if (value->IsDate()) {
1133             *reinterpret_cast<QDateTime *>(data) = QJSConverter::toDateTime(v8::Handle<v8::Date>::Cast(value));
1134             return true;
1135         } break;
1136     case QMetaType::QDate:
1137         if (value->IsDate()) {
1138             *reinterpret_cast<QDate *>(data) = QJSConverter::toDateTime(v8::Handle<v8::Date>::Cast(value)).date();
1139             return true;
1140         } break;
1141     case QMetaType::QRegExp:
1142         if (value->IsRegExp()) {
1143             *reinterpret_cast<QRegExp *>(data) = QJSConverter::toRegExp(v8::Handle<v8::RegExp>::Cast(value));
1144             return true;
1145         } break;
1146     case QMetaType::QObjectStar:
1147         if (isQObject(value) || value->IsNull()) {
1148             *reinterpret_cast<QObject* *>(data) = qtObjectFromJS(value);
1149             return true;
1150         } break;
1151     case QMetaType::QStringList:
1152         if (value->IsArray()) {
1153             *reinterpret_cast<QStringList *>(data) = QJSConverter::toStringList(v8::Handle<v8::Array>::Cast(value));
1154             return true;
1155         } break;
1156     case QMetaType::QVariantList:
1157         if (value->IsArray()) {
1158             *reinterpret_cast<QVariantList *>(data) = variantListFromJS(v8::Handle<v8::Array>::Cast(value));
1159             return true;
1160         } break;
1161     case QMetaType::QVariantMap:
1162         if (value->IsObject()) {
1163             *reinterpret_cast<QVariantMap *>(data) = variantMapFromJS(v8::Handle<v8::Object>::Cast(value));
1164             return true;
1165         } break;
1166     case QMetaType::QVariant:
1167         *reinterpret_cast<QVariant*>(data) = variantFromJS(value);
1168         return true;
1169     case QMetaType::QJsonValue:
1170         *reinterpret_cast<QJsonValue *>(data) = jsonValueFromJS(value);
1171         return true;
1172     case QMetaType::QJsonObject:
1173         *reinterpret_cast<QJsonObject *>(data) = jsonObjectFromJS(value);
1174         return true;
1175     case QMetaType::QJsonArray:
1176         *reinterpret_cast<QJsonArray *>(data) = jsonArrayFromJS(value);
1177         return true;
1178     default:
1179     ;
1180     }
1181
1182 #if 0
1183     if (isQtVariant(value)) {
1184         const QVariant &var = variantValue(value);
1185         // ### Enable once constructInPlace() is in qt master.
1186         if (var.userType() == type) {
1187             QMetaType::constructInPlace(type, data, var.constData());
1188             return true;
1189         }
1190         if (var.canConvert(type)) {
1191             QVariant vv = var;
1192             vv.convert(type);
1193             Q_ASSERT(vv.userType() == type);
1194             QMetaType::constructInPlace(type, data, vv.constData());
1195             return true;
1196         }
1197
1198     }
1199 #endif
1200
1201     // Try to use magic; for compatibility with qscriptvalue_cast.
1202
1203     QByteArray name = QMetaType::typeName(type);
1204     if (convertToNativeQObject(value, name, reinterpret_cast<void* *>(data)))
1205         return true;
1206     if (isVariant(value) && name.endsWith('*')) {
1207         int valueType = QMetaType::type(name.left(name.size()-1));
1208         QVariant &var = variantValue(value);
1209         if (valueType == var.userType()) {
1210             // We have T t, T* is requested, so return &t.
1211             *reinterpret_cast<void* *>(data) = var.data();
1212             return true;
1213         } else {
1214             // Look in the prototype chain.
1215             v8::Handle<v8::Value> proto = value->ToObject()->GetPrototype();
1216             while (proto->IsObject()) {
1217                 bool canCast = false;
1218                 if (isVariant(proto)) {
1219                     canCast = (type == variantValue(proto).userType())
1220                               || (valueType && (valueType == variantValue(proto).userType()));
1221                 }
1222                 else if (isQObject(proto)) {
1223                     QByteArray className = name.left(name.size()-1);
1224                     if (QObject *qobject = qtObjectFromJS(proto))
1225                         canCast = qobject->qt_metacast(className) != 0;
1226                 }
1227                 if (canCast) {
1228                     QByteArray varTypeName = QMetaType::typeName(var.userType());
1229                     if (varTypeName.endsWith('*'))
1230                         *reinterpret_cast<void* *>(data) = *reinterpret_cast<void* *>(var.data());
1231                     else
1232                         *reinterpret_cast<void* *>(data) = var.data();
1233                     return true;
1234                 }
1235                 proto = proto->ToObject()->GetPrototype();
1236             }
1237         }
1238     } else if (value->IsNull() && name.endsWith('*')) {
1239         *reinterpret_cast<void* *>(data) = 0;
1240         return true;
1241     } else if (type == qMetaTypeId<QJSValue>()) {
1242         *reinterpret_cast<QJSValue*>(data) = QJSValuePrivate::get(new QJSValuePrivate(this, value));
1243         return true;
1244     }
1245
1246     return false;
1247 }
1248
1249 // Converts a QVariant to JS.
1250 v8::Handle<v8::Value> QV8Engine::variantToJS(const QVariant &value)
1251 {
1252     return metaTypeToJS(value.userType(), value.constData());
1253 }
1254
1255 // Converts a JS value to a QVariant.
1256 // Undefined -> QVariant() (invalid)
1257 // Null -> QVariant((void*)0)
1258 // Boolean -> QVariant(bool)
1259 // Number -> QVariant(double)
1260 // String -> QVariant(QString)
1261 // Array -> QVariantList(...)
1262 // Date -> QVariant(QDateTime)
1263 // RegExp -> QVariant(QRegExp)
1264 // [Any other object] -> QVariantMap(...)
1265 QVariant QV8Engine::variantFromJS(v8::Handle<v8::Value> value,
1266                                   V8ObjectSet &visitedObjects)
1267 {
1268     Q_ASSERT(!value.IsEmpty());
1269     if (value->IsUndefined())
1270         return QVariant();
1271     if (value->IsNull())
1272         return QVariant(QMetaType::VoidStar, 0);
1273     if (value->IsBoolean())
1274         return value->ToBoolean()->Value();
1275     if (value->IsInt32())
1276         return value->ToInt32()->Value();
1277     if (value->IsNumber())
1278         return value->ToNumber()->Value();
1279     if (value->IsString())
1280         return QJSConverter::toString(value->ToString());
1281     Q_ASSERT(value->IsObject());
1282     if (value->IsArray())
1283         return variantListFromJS(v8::Handle<v8::Array>::Cast(value), visitedObjects);
1284     if (value->IsDate())
1285         return QJSConverter::toDateTime(v8::Handle<v8::Date>::Cast(value));
1286     if (value->IsRegExp())
1287         return QJSConverter::toRegExp(v8::Handle<v8::RegExp>::Cast(value));
1288     if (isVariant(value))
1289         return variantValue(value);
1290     if (isQObject(value))
1291         return qVariantFromValue(qtObjectFromJS(value));
1292     return variantMapFromJS(value->ToObject(), visitedObjects);
1293 }
1294
1295 v8::Handle<v8::Value> QV8Engine::jsonValueToJS(const QJsonValue &value)
1296 {
1297     return m_jsonWrapper.fromJsonValue(value);
1298 }
1299
1300 QJsonValue QV8Engine::jsonValueFromJS(v8::Handle<v8::Value> value)
1301 {
1302     return m_jsonWrapper.toJsonValue(value);
1303 }
1304
1305 v8::Local<v8::Object> QV8Engine::jsonObjectToJS(const QJsonObject &object)
1306 {
1307     return m_jsonWrapper.fromJsonObject(object);
1308 }
1309
1310 QJsonObject QV8Engine::jsonObjectFromJS(v8::Handle<v8::Value> value)
1311 {
1312     return m_jsonWrapper.toJsonObject(value);
1313 }
1314
1315 v8::Local<v8::Array> QV8Engine::jsonArrayToJS(const QJsonArray &array)
1316 {
1317     return m_jsonWrapper.fromJsonArray(array);
1318 }
1319
1320 QJsonArray QV8Engine::jsonArrayFromJS(v8::Handle<v8::Value> value)
1321 {
1322     return m_jsonWrapper.toJsonArray(value);
1323 }
1324
1325 bool QV8Engine::convertToNativeQObject(v8::Handle<v8::Value> value,
1326                                                   const QByteArray &targetType,
1327                                                   void **result)
1328 {
1329     if (!targetType.endsWith('*'))
1330         return false;
1331     if (QObject *qobject = qtObjectFromJS(value)) {
1332         int start = targetType.startsWith("const ") ? 6 : 0;
1333         QByteArray className = targetType.mid(start, targetType.size()-start-1);
1334         if (void *instance = qobject->qt_metacast(className)) {
1335             *result = instance;
1336             return true;
1337         }
1338     }
1339     return false;
1340 }
1341
1342 QObject *QV8Engine::qtObjectFromJS(v8::Handle<v8::Value> value)
1343 {
1344     if (!value->IsObject())
1345         return 0;
1346
1347     QV8ObjectResource *r = (QV8ObjectResource *)value->ToObject()->GetExternalResource();
1348     if (!r)
1349         return 0;
1350     QV8ObjectResource::ResourceType type = r->resourceType();
1351     if (type == QV8ObjectResource::QObjectType)
1352         return qobjectWrapper()->toQObject(r);
1353     else if (type == QV8ObjectResource::VariantType) {
1354         QVariant variant = variantWrapper()->toVariant(r);
1355         int type = variant.userType();
1356         if (type == QMetaType::QObjectStar)
1357             return *reinterpret_cast<QObject* const *>(variant.constData());
1358     }
1359     return 0;
1360 }
1361
1362
1363 QVariant &QV8Engine::variantValue(v8::Handle<v8::Value> value)
1364 {
1365     return variantWrapper()->variantValue(value);
1366 }
1367
1368 // Creates a QVariant wrapper object.
1369 v8::Local<v8::Object> QV8Engine::newVariant(const QVariant &value)
1370 {
1371     return variantWrapper()->newVariant(value);
1372 }
1373
1374 QScriptPassPointer<QJSValuePrivate> QV8Engine::evaluate(v8::Handle<v8::Script> script, v8::TryCatch& tryCatch)
1375 {
1376     v8::HandleScope handleScope;
1377
1378     if (script.IsEmpty()) {
1379         v8::Handle<v8::Value> exception = tryCatch.Exception();
1380         if (exception.IsEmpty()) {
1381             // This is possible on syntax errors like { a:12, b:21 } <- missing "(", ")" around expression.
1382             return new QJSValuePrivate(this);
1383         }
1384         return new QJSValuePrivate(this, exception);
1385     }
1386     v8::Handle<v8::Value> result;
1387     result = script->Run();
1388     if (result.IsEmpty()) {
1389         v8::Handle<v8::Value> exception = tryCatch.Exception();
1390         // TODO: figure out why v8 doesn't always produce an exception value
1391         //Q_ASSERT(!exception.IsEmpty());
1392         if (exception.IsEmpty())
1393             exception = v8::Exception::Error(v8::String::New("missing exception value"));
1394         return new QJSValuePrivate(this, exception);
1395     }
1396     return new QJSValuePrivate(this, result);
1397 }
1398
1399 QJSValue QV8Engine::scriptValueFromInternal(v8::Handle<v8::Value> value) const
1400 {
1401     if (value.IsEmpty())
1402         return QJSValuePrivate::get(new QJSValuePrivate(const_cast<QV8Engine*>(this)));
1403     return QJSValuePrivate::get(new QJSValuePrivate(const_cast<QV8Engine*>(this), value));
1404 }
1405
1406 QScriptPassPointer<QJSValuePrivate> QV8Engine::newArray(uint length)
1407 {
1408     return new QJSValuePrivate(this, v8::Array::New(length));
1409 }
1410
1411 void QV8Engine::startTimer(const QString &timerName)
1412 {
1413     if (!m_time.isValid())
1414         m_time.start();
1415     m_startedTimers[timerName] = m_time.elapsed();
1416 }
1417
1418 qint64 QV8Engine::stopTimer(const QString &timerName, bool *wasRunning)
1419 {
1420     if (!m_startedTimers.contains(timerName)) {
1421         *wasRunning = false;
1422         return 0;
1423     }
1424     *wasRunning = true;
1425     qint64 startedAt = m_startedTimers.take(timerName);
1426     return m_time.elapsed() - startedAt;
1427 }
1428
1429 int QV8Engine::consoleCountHelper(const QString &file, quint16 line, quint16 column)
1430 {
1431     const QString key = file + QString::number(line) + QString::number(column);
1432     int number = m_consoleCount.value(key, 0);
1433     number++;
1434     m_consoleCount.insert(key, number);
1435     return number;
1436 }
1437
1438 v8::Handle<v8::Value> QV8Engine::getApplication(v8::Local<v8::String>, const v8::AccessorInfo &info)
1439 {
1440     QV8Engine *engine = reinterpret_cast<QV8Engine*>(v8::External::Unwrap(info.Data()));
1441     if (!engine->m_application) {
1442         // Only allocate an application object once
1443         engine->m_application = QQml_guiProvider()->application(engine->m_engine);
1444     }
1445     return engine->newQObject(engine->m_application);
1446 }
1447
1448 v8::Handle<v8::Value> QV8Engine::getInputMethod(v8::Local<v8::String>, const v8::AccessorInfo &info)
1449 {
1450     QV8Engine *engine = reinterpret_cast<QV8Engine*>(v8::External::Unwrap(info.Data()));
1451     return engine->newQObject(QQml_guiProvider()->inputMethod(), CppOwnership);
1452 }
1453
1454 void QV8GCCallback::registerGcPrologueCallback()
1455 {
1456     QV8Engine::ThreadData *td = QV8Engine::threadData();
1457     if (!td->gcPrologueCallbackRegistered) {
1458         td->gcPrologueCallbackRegistered = true;
1459         v8::V8::AddGCPrologueCallback(QV8GCCallback::garbageCollectorPrologueCallback, v8::kGCTypeMarkSweepCompact);
1460     }
1461 }
1462
1463 QV8GCCallback::Node::Node(PrologueCallback callback)
1464     : prologueCallback(callback)
1465 {
1466 }
1467
1468 QV8GCCallback::Node::~Node()
1469 {
1470     node.remove();
1471 }
1472
1473 /*
1474    Ensure that each persistent handle is strong if it has CPP ownership
1475    and has no implicitly JS owned object owner in its parent chain, and
1476    weak otherwise.
1477
1478    Any weak handle whose parent object is still alive will have an implicit
1479    reference (between the parent and the handle) added, so that it will
1480    not be collected.
1481
1482    Note that this callback is registered only for kGCTypeMarkSweepCompact
1483    collection cycles, as it is during collection cycles of that type
1484    in which weak persistent handle callbacks are called when required.
1485  */
1486 void QV8GCCallback::garbageCollectorPrologueCallback(v8::GCType, v8::GCCallbackFlags)
1487 {
1488     if (!QV8Engine::hasThreadData())
1489         return;
1490
1491     QV8Engine::ThreadData *td = QV8Engine::threadData();
1492     QV8GCCallback::Node *currNode = td->gcCallbackNodes.first();
1493
1494     while (currNode) {
1495         // The client which adds itself to the list is responsible
1496         // for maintaining the correct implicit references in the
1497         // specified callback.
1498         currNode->prologueCallback(currNode);
1499         currNode = td->gcCallbackNodes.next(currNode);
1500     }
1501 }
1502
1503 void QV8GCCallback::addGcCallbackNode(QV8GCCallback::Node *node)
1504 {
1505     QV8Engine::ThreadData *td = QV8Engine::threadData();
1506     td->gcCallbackNodes.insert(node);
1507 }
1508
1509 QV8Engine::ThreadData::ThreadData()
1510     : gcPrologueCallbackRegistered(false)
1511 {
1512     if (!v8::Isolate::GetCurrent()) {
1513         isolate = v8::Isolate::New();
1514         isolate->Enter();
1515     } else {
1516         isolate = 0;
1517     }
1518 }
1519
1520 QV8Engine::ThreadData::~ThreadData()
1521 {
1522     if (isolate) {
1523         isolate->Exit();
1524         isolate->Dispose();
1525         isolate = 0;
1526     }
1527 }
1528
1529 QT_END_NAMESPACE
1530