Console API: Add console.count
[profile/ivi/qtdeclarative.git] / src / declarative / qml / v8 / qv8engine.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: http://www.qt-project.org/
6 **
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
8 **
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.
17 **
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.
21 **
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.
29 **
30 ** Other Usage
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.
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 #include "../../../3rdparty/javascriptcore/DateMath.h"
50
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>
58
59 #include "qscript_impl_p.h"
60 #include "qv8domerrors_p.h"
61
62 Q_DECLARE_METATYPE(QJSValue)
63 Q_DECLARE_METATYPE(QList<int>)
64
65
66 // XXX TODO: Need to check all the global functions will also work in a worker script where the 
67 // QDeclarativeEngine is not available
68 QT_BEGIN_NAMESPACE
69
70 static bool ObjectComparisonCallback(v8::Local<v8::Object> lhs, v8::Local<v8::Object> rhs)
71 {
72     if (lhs == rhs)
73         return true;
74
75     QV8ObjectResource *lhsr = static_cast<QV8ObjectResource*>(lhs->GetExternalResource());
76     QV8ObjectResource *rhsr = static_cast<QV8ObjectResource*>(rhs->GetExternalResource());
77
78     Q_ASSERT(lhsr->engine == rhsr->engine);
79
80     if (lhsr && rhsr) {
81         QV8ObjectResource::ResourceType lhst = lhsr->resourceType();
82         QV8ObjectResource::ResourceType rhst = rhsr->resourceType();
83
84         switch (lhst) {
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));
91             }
92             break;
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));
100             }
101             break;
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);
106             }
107             break;
108         default:
109             break;
110         }
111     }
112
113     return false;
114 }
115
116
117 QV8Engine::QV8Engine(QJSEngine* qq, QJSEngine::ContextOwnership ownership)
118     : q(qq)
119     , m_engine(0)
120     , m_ownsV8Context(ownership == QJSEngine::CreateNewContext)
121     , m_xmlHttpRequestData(0)
122     , m_sqlDatabaseData(0)
123     , m_listModelData(0)
124 {
125     qMetaTypeId<QJSValue>();
126     qMetaTypeId<QList<int> >();
127
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());
133
134     ensurePerThreadIsolate();
135
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);
141
142     v8::V8::SetUserObjectComparisonCallbackFunction(ObjectComparisonCallback);
143     QV8GCCallback::registerGcPrologueCallback();
144     m_strongReferencer = qPersistentNew(v8::Object::New());
145
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);
154
155     {
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));
158     }
159 }
160
161 QV8Engine::~QV8Engine()
162 {
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();
167
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;
173     m_listModelData = 0;
174
175     qPersistentDispose(m_freezeObject);
176     qPersistentDispose(m_getOwnPropertyNames);
177
178     invalidateAllValues();
179     clearExceptions();
180
181     qPersistentDispose(m_strongReferencer);
182
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();
191
192     m_originalGlobalObject.destroy();
193
194     if (m_ownsV8Context)
195         qPersistentDispose(m_context);
196 }
197
198 QString QV8Engine::toStringStatic(v8::Handle<v8::Value> jsstr)
199 {
200     return toStringStatic(jsstr->ToString());
201 }
202
203 QString QV8Engine::toStringStatic(v8::Handle<v8::String> jsstr)
204 {
205     QString qstr;
206     qstr.resize(jsstr->Length());
207     jsstr->Write((uint16_t*)qstr.data());
208     return qstr;
209 }
210
211 QVariant QV8Engine::toVariant(v8::Handle<v8::Value> value, int typeHint)
212 {
213     if (value.IsEmpty()) 
214         return QVariant();
215
216     if (typeHint == QVariant::Bool)
217         return QVariant(value->BooleanValue());
218
219     if (value->IsObject()) {
220         QV8ObjectResource *r = (QV8ObjectResource *)value->ToObject()->GetExternalResource();
221         if (r) {
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:
236                 return QVariant();
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);
249             }
250         }
251     }
252
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());
262                 } else {
263                     list << 0;
264                 }
265             }
266
267             return qVariantFromValue<QList<QObject*> >(list);
268         }
269
270         bool succeeded = false;
271         QVariant retn = m_sequenceWrapper.toVariant(array, typeHint, &succeeded);
272         if (succeeded)
273             return retn;
274     }
275
276     return toBasicVariant(value);
277 }
278
279 static v8::Handle<v8::Array> arrayFromStringList(QV8Engine *engine, const QStringList &list)
280 {
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)));
285     return result;
286 }
287
288 static v8::Handle<v8::Array> arrayFromVariantList(QV8Engine *engine, const QVariantList &list)
289 {
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)));
294     return result;
295 }
296
297 static v8::Handle<v8::Object> objectFromVariantMap(QV8Engine *engine, const QVariantMap &map)
298 {
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()));
303     return object;
304 }
305
306 Q_CORE_EXPORT QString qt_regexp_toCanonical(const QString &, QRegExp::PatternSyntax);
307
308 v8::Handle<v8::Value> QV8Engine::fromVariant(const QVariant &variant)
309 {
310     int type = variant.userType();
311     const void *ptr = variant.constData();
312
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));
319             case QMetaType::Int:
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:
355                 {
356                 bool succeeded = false;
357                 v8::Handle<v8::Value> retn = m_sequenceWrapper.fromVariant(variant, &succeeded);
358                 if (succeeded)
359                     return retn;
360                 return arrayFromStringList(this, *reinterpret_cast<const QStringList *>(ptr));
361                 }
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));
366
367             default:
368                 break;
369         }
370
371         if (m_engine) {
372             if (QDeclarativeValueType *vt = QDeclarativeEnginePrivate::get(m_engine)->valueTypes[type])
373                 return m_valueTypeWrapper.newValueType(variant, vt);
374         }
375
376     } else {
377         if (type == qMetaTypeId<QDeclarativeListReference>()) {
378             typedef QDeclarativeListReferencePrivate QDLRP;
379             QDLRP *p = QDLRP::get((QDeclarativeListReference*)ptr);
380             if (p->object) {
381                 return m_listWrapper.newList(p->property, p->propertyType);
382             } else {
383                 return v8::Null();
384             }
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)));
397             return array;
398         } 
399
400         bool objOk;
401         QObject *obj = QDeclarativeMetaType::toQObject(variant, &objOk);
402         if (objOk) 
403             return newQObject(obj);
404
405         bool succeeded = false;
406         v8::Handle<v8::Value> retn = m_sequenceWrapper.fromVariant(variant, &succeeded);
407         if (succeeded)
408             return retn;
409     }
410
411     // XXX TODO: To be compatible, we still need to handle:
412     //    + QObjectList
413     //    + QList<int>
414
415     return m_variantWrapper.newVariant(variant);
416 }
417
418 // A handle scope and context must be entered
419 v8::Local<v8::Script> QV8Engine::qmlModeCompile(const QString &source, const QString &fileName, int lineNumber)
420 {
421     v8::Local<v8::String> v8source = m_stringWrapper.toString(source);
422     v8::Local<v8::String> v8fileName = m_stringWrapper.toString(fileName);
423
424     v8::ScriptOrigin origin(v8fileName, v8::Integer::New(lineNumber - 1));
425
426     v8::Local<v8::Script> script = v8::Script::Compile(v8source, &origin, 0, v8::Handle<v8::String>(), 
427                                                        v8::Script::QmlMode);
428
429     return script;
430 }
431
432 QNetworkAccessManager *QV8Engine::networkAccessManager() 
433 {
434     return QDeclarativeEnginePrivate::get(m_engine)->getNetworkAccessManager();
435 }
436
437 const QStringHash<bool> &QV8Engine::illegalNames() const
438 {
439     return m_illegalNames;
440 }
441
442 // Requires a handle scope
443 v8::Local<v8::Array> QV8Engine::getOwnPropertyNames(v8::Handle<v8::Object> o)
444 {
445     // FIXME Newer v8 have API for this function
446     v8::TryCatch tc;
447     v8::Handle<v8::Value> args[] = { o };
448     v8::Local<v8::Value> r = m_getOwnPropertyNames->Call(global(), 1, args);
449     if (tc.HasCaught())
450         return v8::Array::New();
451     else
452         return v8::Local<v8::Array>::Cast(r);
453 }
454
455 QDeclarativeContextData *QV8Engine::callingContext()
456 {
457     return m_contextWrapper.callingContext();
458 }
459
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)
470 {
471     if (value->IsNull() || value->IsUndefined())
472         return QVariant();
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());
481     if (value->IsDate())
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)!
484
485     Q_ASSERT(value->IsObject());
486
487     if (value->IsRegExp()) {
488         v8::Context::Scope scope(context());
489         return QJSConverter::toRegExp(v8::Handle<v8::RegExp>::Cast(value));
490     }
491     if (value->IsArray()) {
492         v8::Context::Scope scope(context());
493         QVariantList rv;
494
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);
499         return rv;
500     }
501     if (!value->IsFunction()) {
502         v8::Context::Scope scope(context());
503         v8::Handle<v8::Object> object = value->ToObject();
504         return variantMapFromJS(object);
505     }
506
507     return QVariant();
508 }
509
510
511
512 #include <QtGui/qvector3d.h>
513 #include <QtGui/qvector4d.h>
514
515 struct StaticQtMetaObject : public QObject
516 {
517     static const QMetaObject *get()
518         { return &static_cast<StaticQtMetaObject*> (0)->staticQtMetaObject; }
519 };
520
521 void QV8Engine::initializeGlobal(v8::Handle<v8::Object> global)
522 {
523     using namespace QDeclarativeBuiltinFunctions;
524
525     v8::Local<v8::Object> console = v8::Object::New();
526     v8::Local<v8::Function> consoleLogFn = V8FUNCTION(consoleLog, this);
527
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));
533
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));
540
541     v8::Local<v8::Object> qt = v8::Object::New();
542
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)));
549         }
550     }
551     qt->Set(v8::String::New("Asynchronous"), v8::Integer::New(0));
552     qt->Set(v8::String::New("Synchronous"), v8::Integer::New(1));
553
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));
563
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));
567
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));
575
576     if (m_engine) {
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));
584     }
585
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));
592
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));
597
598     {
599 #define STRING_ARG "(function(stringArg) { "\
600                    "    String.prototype.arg = (function() {"\
601                    "        return stringArg.apply(this, arguments);"\
602                    "    })"\
603                    "})"
604
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);
611 #undef STRING_ARG
612     }
613
614     QDeclarativeDateExtension::registerExtension(this);
615     QDeclarativeNumberExtension::registerExtension(this);
616
617     qt_add_domexceptions(this);
618     m_xmlHttpRequestData = qt_add_qmlxmlhttprequest(this);
619     m_sqlDatabaseData = qt_add_qmlsqldatabase(this);
620
621     {
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);
627     }
628
629     {
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]); "\
638                       "                continue;"\
639                       "            }"\
640                       "            freeze_recur(obj[prop]);"\
641                       "        }"\
642                       "    }"\
643                       "    if (obj instanceof Object) {"\
644                       "        Object.freeze(obj);"\
645                       "    }"\
646                       "})"
647
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));
652 #undef FREEZE_SOURCE
653     }
654 }
655
656 void QV8Engine::freezeObject(v8::Handle<v8::Value> value)
657 {
658     v8::Handle<v8::Value> args[] = { value };
659     m_freezeObject->Call(global(), 1, args);
660 }
661
662 void QV8Engine::gc()
663 {
664     v8::V8::LowMemoryNotification();
665     while (!v8::V8::IdleNotification()) {}
666 }
667
668 #ifdef QML_GLOBAL_HANDLE_DEBUGGING
669 #include <QtCore/qthreadstorage.h>
670 static QThreadStorage<QSet<void *> *> QV8Engine_activeHandles;
671
672 void QV8Engine::registerHandle(void *handle)
673 {
674     if (!handle) {
675         qWarning("Attempting to register a null handle");
676         return;
677     }
678
679     if (!QV8Engine_activeHandles.hasLocalData()) 
680         QV8Engine_activeHandles.setLocalData(new QSet<void *>);
681
682     if (QV8Engine_activeHandles.localData()->contains(handle)) {
683         qFatal("Handle %p already alive", handle);
684     } else {
685         QV8Engine_activeHandles.localData()->insert(handle);
686     }
687 }
688
689 void QV8Engine::releaseHandle(void *handle)
690 {
691     if (!handle)
692         return;
693
694     if (!QV8Engine_activeHandles.hasLocalData()) 
695         QV8Engine_activeHandles.setLocalData(new QSet<void *>);
696
697     if (QV8Engine_activeHandles.localData()->contains(handle)) {
698         QV8Engine_activeHandles.localData()->remove(handle);
699     } else {
700         qFatal("Handle %p already dead", handle);
701     }
702 }
703 #endif
704
705 struct QV8EngineRegistrationData
706 {
707     QV8EngineRegistrationData() : extensionCount(0) {}
708
709     QMutex mutex;
710     int extensionCount;
711 };
712 Q_GLOBAL_STATIC(QV8EngineRegistrationData, registrationData);
713
714 QMutex *QV8Engine::registrationMutex()
715 {
716     return &registrationData()->mutex;
717 }
718
719 int QV8Engine::registerExtension()
720 {
721     return registrationData()->extensionCount++;
722 }
723
724 void QV8Engine::setExtensionData(int index, Deletable *data)
725 {
726     if (m_extensionData.count() <= index) 
727         m_extensionData.resize(index + 1);
728
729     if (m_extensionData.at(index)) 
730         delete m_extensionData.at(index);
731
732     m_extensionData[index] = data;
733 }
734
735 double QV8Engine::qtDateTimeToJsDate(const QDateTime &dt)
736 {
737     // from QScriptEngine::DateTimeToMs()
738     if (!dt.isValid()) {
739         return qSNaN();
740     }
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());
754 }
755
756 v8::Persistent<v8::Object> *QV8Engine::findOwnerAndStrength(QObject *object, bool *shouldBeStrong)
757 {
758     QObject *parent = object->parent();
759     if (!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);
764         }
765
766         // no parent, and has CPP ownership - doesn't have an implicit parent.
767         *shouldBeStrong = true;
768         return 0;
769     }
770
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();
775
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);
780     } else {
781         // root parent has CPP ownership.  The persistent value should not be made weak.
782         *shouldBeStrong = true;
783         return 0;
784     }
785 }
786
787 QDateTime QV8Engine::qtDateTimeFromJsDate(double jsDate)
788 {
789     // from QScriptEngine::MsToDateTime()
790     if (qIsNaN(jsDate))
791         return QDateTime();
792     QV8DateConverter::JSC::GregorianDateTime tm;
793     QV8DateConverter::JSC::msToGregorianDateTime(jsDate, tm);
794
795     // from QScriptEngine::MsFromTime()
796     int ms = int(::fmod(jsDate, 1000.0));
797     if (ms < 0)
798         ms += int(1000.0);
799
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();
803 }
804
805 void QV8Engine::addRelationshipForGC(QObject *object, v8::Persistent<v8::Value> handle)
806 {
807     if (handle.IsEmpty())
808         return;
809
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);
816     }
817 }
818
819 void QV8Engine::addRelationshipForGC(QObject *object, QObject *other)
820 {
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);
828     }
829 }
830
831 static QThreadStorage<QV8Engine::ThreadData*> perThreadEngineData;
832
833 bool QV8Engine::hasThreadData()
834 {
835     return perThreadEngineData.hasLocalData();
836 }
837
838 QV8Engine::ThreadData *QV8Engine::threadData()
839 {
840     Q_ASSERT(perThreadEngineData.hasLocalData());
841     return perThreadEngineData.localData();
842 }
843
844 void QV8Engine::ensurePerThreadIsolate()
845 {
846     if (!perThreadEngineData.hasLocalData())
847         perThreadEngineData.setLocalData(new ThreadData);
848 }
849
850 void QV8Engine::initDeclarativeGlobalObject()
851 {
852     v8::HandleScope handels;
853     v8::Context::Scope contextScope(m_context);
854     initializeGlobal(m_context->Global());
855     freezeObject(m_context->Global());
856 }
857
858 void QV8Engine::setEngine(QDeclarativeEngine *engine)
859 {
860     m_engine = engine;
861     initDeclarativeGlobalObject();
862 }
863
864 void QV8Engine::setException(v8::Handle<v8::Value> value, v8::Handle<v8::Message> msg)
865 {
866     m_exception.set(value, msg);
867 }
868
869 v8::Handle<v8::Value> QV8Engine::throwException(v8::Handle<v8::Value> value)
870 {
871     setException(value);
872     v8::ThrowException(value);
873     return value;
874 }
875
876 void QV8Engine::clearExceptions()
877 {
878     m_exception.clear();
879 }
880
881 v8::Handle<v8::Value> QV8Engine::uncaughtException() const
882 {
883     if (!hasUncaughtException())
884         return v8::Handle<v8::Value>();
885     return m_exception;
886 }
887
888 bool QV8Engine::hasUncaughtException() const
889 {
890     return m_exception;
891 }
892
893 int QV8Engine::uncaughtExceptionLineNumber() const
894 {
895     return m_exception.lineNumber();
896 }
897
898 QStringList QV8Engine::uncaughtExceptionBacktrace() const
899 {
900     return m_exception.backtrace();
901 }
902
903 /*!
904   \internal
905   Save the current exception on stack so it can be set again later.
906   \sa QV8Engine::restoreException
907 */
908 void QV8Engine::saveException()
909 {
910     m_exception.push();
911 }
912
913 /*!
914   \internal
915   Load a saved exception from stack. Current exception, if exists will be dropped
916   \sa QV8Engine::saveException
917 */
918 void QV8Engine::restoreException()
919 {
920     m_exception.pop();
921 }
922
923 QV8Engine::Exception::Exception() {}
924
925 QV8Engine::Exception::~Exception()
926 {
927     Q_ASSERT_X(m_stack.isEmpty(), Q_FUNC_INFO, "Some saved exceptions left. Asymetric pop/push found.");
928     clear();
929 }
930
931 void QV8Engine::Exception::set(v8::Handle<v8::Value> value, v8::Handle<v8::Message> message)
932 {
933     Q_ASSERT_X(!value.IsEmpty(), Q_FUNC_INFO, "Throwing an empty value handle is highly suspected");
934     clear();
935     m_value = v8::Persistent<v8::Value>::New(value);
936     m_message = v8::Persistent<v8::Message>::New(message);
937 }
938
939 void QV8Engine::Exception::clear()
940 {
941     m_value.Dispose();
942     m_value.Clear();
943     m_message.Dispose();
944     m_message.Clear();
945 }
946
947 QV8Engine::Exception::operator bool() const
948 {
949     return !m_value.IsEmpty();
950 }
951
952 QV8Engine::Exception::operator v8::Handle<v8::Value>() const
953 {
954     Q_ASSERT(*this);
955     return m_value;
956 }
957
958 int QV8Engine::Exception::lineNumber() const
959 {
960     if (m_message.IsEmpty())
961         return -1;
962     return m_message->GetLineNumber();
963 }
964
965 QStringList QV8Engine::Exception::backtrace() const
966 {
967     if (m_message.IsEmpty())
968         return QStringList();
969
970     QStringList backtrace;
971     v8::Handle<v8::StackTrace> trace = m_message->GetStackTrace();
972     if (trace.IsEmpty())
973         // FIXME it should not happen (SetCaptureStackTraceForUncaughtExceptions is called).
974         return QStringList();
975
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()));
984     }
985     return backtrace;
986 }
987
988 void QV8Engine::Exception::push()
989 {
990     m_stack.push(qMakePair(m_value, m_message));
991     m_value.Clear();
992     m_message.Clear();
993 }
994
995 void QV8Engine::Exception::pop()
996 {
997     Q_ASSERT_X(!m_stack.empty(), Q_FUNC_INFO, "Attempt to load unsaved exception found");
998     ValueMessagePair pair = m_stack.pop();
999     clear();
1000     m_value = pair.first;
1001     m_message = pair.second;
1002 }
1003
1004 QScriptPassPointer<QJSValuePrivate> QV8Engine::newRegExp(const QString &pattern, const QString &flags)
1005 {
1006     int f = v8::RegExp::kNone;
1007
1008     QString::const_iterator i = flags.constBegin();
1009     for (; i != flags.constEnd(); ++i) {
1010         switch (i->unicode()) {
1011         case 'i':
1012             f |= v8::RegExp::kIgnoreCase;
1013             break;
1014         case 'm':
1015             f |= v8::RegExp::kMultiline;
1016             break;
1017         case 'g':
1018             f |= v8::RegExp::kGlobal;
1019             break;
1020         default:
1021             {
1022                 // ignore a Syntax Error.
1023             }
1024         }
1025     }
1026
1027     v8::Handle<v8::RegExp> regexp = v8::RegExp::New(QJSConverter::toString(pattern), static_cast<v8::RegExp::Flags>(f));
1028     return new QJSValuePrivate(this, regexp);
1029 }
1030
1031 QScriptPassPointer<QJSValuePrivate> QV8Engine::newRegExp(const QRegExp &regexp)
1032 {
1033     return new QJSValuePrivate(this, QJSConverter::toRegExp(regexp));
1034 }
1035
1036
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)
1042 {
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)));
1046     return result;
1047 }
1048
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)
1054 {
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);
1065     return result;
1066 }
1067
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)
1073 {
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()));
1078     return result;
1079 }
1080
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)
1086 {
1087     QVariantMap result;
1088
1089     v8::HandleScope handleScope;
1090     v8::Handle<v8::Array> propertyNames = jsObject->GetPropertyNames();
1091     uint32_t length = propertyNames->Length();
1092     if (length == 0)
1093         return result;
1094
1095     int hash = jsObject->GetIdentityHash();
1096     if (visitedConversionObjects.contains(hash))
1097         return result; // Avoid recursion.
1098
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)));
1104     }
1105     visitedConversionObjects.remove(hash);
1106     return result;
1107 }
1108
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)
1112 {
1113     Q_ASSERT(data != 0);
1114     v8::Handle<v8::Value> result;
1115
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)));
1134 #else
1135         return v8::Number::New(double(*reinterpret_cast<const qulonglong*>(data)));
1136 #endif
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));
1155         break;
1156     case QMetaType::QVariantList:
1157         result = variantListToJS(*reinterpret_cast<const QVariantList *>(data));
1158         break;
1159     case QMetaType::QVariantMap:
1160         result = variantMapToJS(*reinterpret_cast<const QVariantMap *>(data));
1161         break;
1162     case QMetaType::QDateTime:
1163         result = QJSConverter::toDateTime(*reinterpret_cast<const QDateTime *>(data));
1164         break;
1165     case QMetaType::QDate:
1166         result = QJSConverter::toDateTime(QDateTime(*reinterpret_cast<const QDate *>(data)));
1167         break;
1168     case QMetaType::QRegExp:
1169         result = QJSConverter::toRegExp(*reinterpret_cast<const QRegExp *>(data));
1170         break;
1171     case QMetaType::QObjectStar:
1172     case QMetaType::QWidgetStar:
1173         result = newQObject(*reinterpret_cast<QObject* const *>(data));
1174         break;
1175     case QMetaType::QVariant:
1176         result = variantToJS(*reinterpret_cast<const QVariant*>(data));
1177         break;
1178     default:
1179         if (type == qMetaTypeId<QJSValue>()) {
1180             return QJSValuePrivate::get(*reinterpret_cast<const QJSValue*>(data))->asV8Value(this);
1181         } else {
1182             QByteArray typeName = QMetaType::typeName(type);
1183             if (typeName.endsWith('*') && !*reinterpret_cast<void* const *>(data)) {
1184                 return v8::Null();
1185             } else {
1186                 // Fall back to wrapping in a QVariant.
1187                 result = newVariant(QVariant(type, data));
1188             }
1189         }
1190     }
1191     return result;
1192 }
1193
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();
1202         return true;
1203     case QMetaType::Int:
1204         *reinterpret_cast<int*>(data) = value->ToInt32()->Value();
1205         return true;
1206     case QMetaType::UInt:
1207         *reinterpret_cast<uint*>(data) = value->ToUint32()->Value();
1208         return true;
1209     case QMetaType::LongLong:
1210         *reinterpret_cast<qlonglong*>(data) = qlonglong(value->ToInteger()->Value());
1211         return true;
1212     case QMetaType::ULongLong:
1213         *reinterpret_cast<qulonglong*>(data) = qulonglong(value->ToInteger()->Value());
1214         return true;
1215     case QMetaType::Double:
1216         *reinterpret_cast<double*>(data) = value->ToNumber()->Value();
1217         return true;
1218     case QMetaType::QString:
1219         if (value->IsUndefined() || value->IsNull())
1220             *reinterpret_cast<QString*>(data) = QString();
1221         else
1222             *reinterpret_cast<QString*>(data) = QJSConverter::toString(value->ToString());
1223         return true;
1224     case QMetaType::Float:
1225         *reinterpret_cast<float*>(data) = value->ToNumber()->Value();
1226         return true;
1227     case QMetaType::Short:
1228         *reinterpret_cast<short*>(data) = short(value->ToInt32()->Value());
1229         return true;
1230     case QMetaType::UShort:
1231         *reinterpret_cast<unsigned short*>(data) = ushort(value->ToInt32()->Value()); // ### QScript::ToUInt16()
1232         return true;
1233     case QMetaType::Char:
1234         *reinterpret_cast<char*>(data) = char(value->ToInt32()->Value());
1235         return true;
1236     case QMetaType::UChar:
1237         *reinterpret_cast<unsigned char*>(data) = (unsigned char)(value->ToInt32()->Value());
1238         return true;
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);
1243         } else {
1244             *reinterpret_cast<QChar*>(data) = QChar(ushort(value->ToInt32()->Value())); // ### QScript::ToUInt16()
1245         }
1246         return true;
1247     case QMetaType::QDateTime:
1248         if (value->IsDate()) {
1249             *reinterpret_cast<QDateTime *>(data) = QJSConverter::toDateTime(v8::Handle<v8::Date>::Cast(value));
1250             return true;
1251         } break;
1252     case QMetaType::QDate:
1253         if (value->IsDate()) {
1254             *reinterpret_cast<QDate *>(data) = QJSConverter::toDateTime(v8::Handle<v8::Date>::Cast(value)).date();
1255             return true;
1256         } break;
1257     case QMetaType::QRegExp:
1258         if (value->IsRegExp()) {
1259             *reinterpret_cast<QRegExp *>(data) = QJSConverter::toRegExp(v8::Handle<v8::RegExp>::Cast(value));
1260             return true;
1261         } break;
1262     case QMetaType::QObjectStar:
1263         if (isQObject(value) || value->IsNull()) {
1264             *reinterpret_cast<QObject* *>(data) = qtObjectFromJS(value);
1265             return true;
1266         } break;
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);
1272                 return true;
1273             }
1274         } break;
1275     case QMetaType::QStringList:
1276         if (value->IsArray()) {
1277             *reinterpret_cast<QStringList *>(data) = QJSConverter::toStringList(v8::Handle<v8::Array>::Cast(value));
1278             return true;
1279         } break;
1280     case QMetaType::QVariantList:
1281         if (value->IsArray()) {
1282             *reinterpret_cast<QVariantList *>(data) = variantListFromJS(v8::Handle<v8::Array>::Cast(value));
1283             return true;
1284         } break;
1285     case QMetaType::QVariantMap:
1286         if (value->IsObject()) {
1287             *reinterpret_cast<QVariantMap *>(data) = variantMapFromJS(v8::Handle<v8::Object>::Cast(value));
1288             return true;
1289         } break;
1290     case QMetaType::QVariant:
1291         *reinterpret_cast<QVariant*>(data) = variantFromJS(value);
1292         return true;
1293     default:
1294     ;
1295     }
1296
1297 #if 0
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());
1303             return true;
1304         }
1305         if (var.canConvert(QVariant::Type(type))) {
1306             QVariant vv = var;
1307             vv.convert(QVariant::Type(type));
1308             Q_ASSERT(vv.userType() == type);
1309             QMetaType::constructInPlace(type, data, vv.constData());
1310             return true;
1311         }
1312
1313     }
1314 #endif
1315
1316     // Try to use magic; for compatibility with qscriptvalue_cast.
1317
1318     QByteArray name = QMetaType::typeName(type);
1319     if (convertToNativeQObject(value, name, reinterpret_cast<void* *>(data)))
1320         return true;
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();
1327             return true;
1328         } else {
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()));
1336                 }
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;
1341                 }
1342                 if (canCast) {
1343                     QByteArray varTypeName = QMetaType::typeName(var.userType());
1344                     if (varTypeName.endsWith('*'))
1345                         *reinterpret_cast<void* *>(data) = *reinterpret_cast<void* *>(var.data());
1346                     else
1347                         *reinterpret_cast<void* *>(data) = var.data();
1348                     return true;
1349                 }
1350                 proto = proto->ToObject()->GetPrototype();
1351             }
1352         }
1353     } else if (value->IsNull() && name.endsWith('*')) {
1354         *reinterpret_cast<void* *>(data) = 0;
1355         return true;
1356     } else if (type == qMetaTypeId<QJSValue>()) {
1357         *reinterpret_cast<QJSValue*>(data) = QJSValuePrivate::get(new QJSValuePrivate(this, value));
1358         return true;
1359     }
1360
1361     return false;
1362 }
1363
1364 // Converts a QVariant to JS.
1365 v8::Handle<v8::Value> QV8Engine::variantToJS(const QVariant &value)
1366 {
1367     return metaTypeToJS(value.userType(), value.constData());
1368 }
1369
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)
1380 {
1381     Q_ASSERT(!value.IsEmpty());
1382     if (value->IsNull() || value->IsUndefined())
1383         return QVariant();
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());
1404 }
1405
1406 bool QV8Engine::convertToNativeQObject(v8::Handle<v8::Value> value,
1407                                                   const QByteArray &targetType,
1408                                                   void **result)
1409 {
1410     if (!targetType.endsWith('*'))
1411         return false;
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)) {
1416             *result = instance;
1417             return true;
1418         }
1419     }
1420     return false;
1421 }
1422
1423 QObject *QV8Engine::qtObjectFromJS(v8::Handle<v8::Value> value)
1424 {
1425     if (!value->IsObject())
1426         return 0;
1427
1428     QV8ObjectResource *r = (QV8ObjectResource *)value->ToObject()->GetExternalResource();
1429     if (!r)
1430         return 0;
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());
1439     }
1440     return 0;
1441 }
1442
1443
1444 QVariant &QV8Engine::variantValue(v8::Handle<v8::Value> value)
1445 {
1446     return variantWrapper()->variantValue(value);
1447 }
1448
1449 // Creates a QVariant wrapper object.
1450 v8::Local<v8::Object> QV8Engine::newVariant(const QVariant &value)
1451 {
1452     return variantWrapper()->newVariant(value);
1453 }
1454
1455 QScriptPassPointer<QJSValuePrivate> QV8Engine::evaluate(v8::Handle<v8::Script> script, v8::TryCatch& tryCatch)
1456 {
1457     v8::HandleScope handleScope;
1458
1459     clearExceptions();
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();
1465         }
1466         setException(exception, tryCatch.Message());
1467         return new QJSValuePrivate(this, exception);
1468     }
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);
1479     }
1480     return new QJSValuePrivate(this, result);
1481 }
1482
1483 QJSValue QV8Engine::scriptValueFromInternal(v8::Handle<v8::Value> value) const
1484 {
1485     if (value.IsEmpty())
1486         return QJSValuePrivate::get(InvalidValue());
1487     return QJSValuePrivate::get(new QJSValuePrivate(const_cast<QV8Engine*>(this), value));
1488 }
1489
1490 QScriptPassPointer<QJSValuePrivate> QV8Engine::newArray(uint length)
1491 {
1492     return new QJSValuePrivate(this, v8::Array::New(length));
1493 }
1494
1495 void QV8Engine::emitSignalHandlerException()
1496 {
1497     emit q->signalHandlerException(scriptValueFromInternal(uncaughtException()));
1498 }
1499
1500 void QV8Engine::startTimer(const QString &timerName)
1501 {
1502     if (!m_time.isValid())
1503         m_time.start();
1504     m_startedTimers[timerName] = m_time.elapsed();
1505 }
1506
1507 qint64 QV8Engine::stopTimer(const QString &timerName, bool *wasRunning)
1508 {
1509     if (!m_startedTimers.contains(timerName)) {
1510         *wasRunning = false;
1511         return 0;
1512     }
1513     *wasRunning = true;
1514     qint64 startedAt = m_startedTimers.take(timerName);
1515     return m_time.elapsed() - startedAt;
1516 }
1517
1518 int QV8Engine::consoleCountHelper(const QString &file, int line, int column)
1519 {
1520     const QString key = file + QString::number(line) + QString::number(column);
1521     int number = m_consoleCount.value(key, 0);
1522     number++;
1523     m_consoleCount.insert(key, number);
1524     return number;
1525 }
1526
1527 void QV8GCCallback::registerGcPrologueCallback()
1528 {
1529     QV8Engine::ThreadData *td = QV8Engine::threadData();
1530     if (!td->gcPrologueCallbackRegistered) {
1531         td->gcPrologueCallbackRegistered = true;
1532         v8::V8::AddGCPrologueCallback(QV8GCCallback::garbageCollectorPrologueCallback, v8::kGCTypeMarkSweepCompact);
1533     }
1534 }
1535
1536 QV8GCCallback::Node::Node(PrologueCallback callback)
1537     : prologueCallback(callback)
1538 {
1539 }
1540
1541 QV8GCCallback::Node::~Node()
1542 {
1543     node.remove();
1544 }
1545
1546 /*
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
1549    weak otherwise.
1550
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
1553    not be collected.
1554
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.
1558  */
1559 void QV8GCCallback::garbageCollectorPrologueCallback(v8::GCType, v8::GCCallbackFlags)
1560 {
1561     if (!QV8Engine::hasThreadData())
1562         return;
1563
1564     QV8Engine::ThreadData *td = QV8Engine::threadData();
1565     QV8GCCallback::Node *currNode = td->gcCallbackNodes.first();
1566
1567     while (currNode) {
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);
1573     }
1574 }
1575
1576 void QV8GCCallback::addGcCallbackNode(QV8GCCallback::Node *node)
1577 {
1578     QV8Engine::ThreadData *td = QV8Engine::threadData();
1579     td->gcCallbackNodes.insert(node);
1580 }
1581
1582 QV8Engine::ThreadData::ThreadData()
1583     : gcPrologueCallbackRegistered(false)
1584 {
1585     if (!v8::Isolate::GetCurrent()) {
1586         isolate = v8::Isolate::New();
1587         isolate->Enter();
1588     } else {
1589         isolate = 0;
1590     }
1591 }
1592
1593 QV8Engine::ThreadData::~ThreadData()
1594 {
1595     if (isolate) {
1596         isolate->Exit();
1597         isolate->Dispose();
1598         isolate = 0;
1599     }
1600 }
1601
1602 QT_END_NAMESPACE
1603