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