doc: fix some more typos
[profile/ivi/qtdeclarative.git] / src / qml / qml / v8 / qjsvalue_impl_p.h
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the QtQml module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16 **
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
20 **
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
28 **
29 ** Other Usage
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 //
43 //  W A R N I N G
44 //  -------------
45 //
46 // This file is not part of the Qt API.  It exists purely as an
47 // implementation detail.  This header file may change from version to
48 // version without notice, or even be removed.
49 //
50 // We mean it.
51 //
52
53 #ifndef QJSVALUE_IMPL_P_H
54 #define QJSVALUE_IMPL_P_H
55
56 #include "qjsconverter_p.h"
57 #include "qjsvalue_p.h"
58 #include "qv8engine_p.h"
59 #include "qscriptisolate_p.h"
60
61 QT_BEGIN_NAMESPACE
62
63 QJSValuePrivate* QJSValuePrivate::get(const QJSValue& q) { Q_ASSERT(q.d_ptr.data()); return q.d_ptr.data(); }
64
65 QJSValue QJSValuePrivate::get(const QJSValuePrivate* d)
66 {
67     Q_ASSERT(d);
68     return QJSValue(const_cast<QJSValuePrivate*>(d));
69 }
70
71 QJSValue QJSValuePrivate::get(QScriptPassPointer<QJSValuePrivate> d)
72 {
73     Q_ASSERT(d);
74     return QJSValue(d);
75 }
76
77 QJSValue QJSValuePrivate::get(QJSValuePrivate* d)
78 {
79     Q_ASSERT(d);
80     return QJSValue(d);
81 }
82
83 QJSValuePrivate::QJSValuePrivate(bool value)
84     : m_engine(0), m_state(CBool), u(value)
85 {
86 }
87
88 QJSValuePrivate::QJSValuePrivate(int value)
89     : m_engine(0), m_state(CNumber), u(value)
90 {
91 }
92
93 QJSValuePrivate::QJSValuePrivate(uint value)
94     : m_engine(0), m_state(CNumber), u(value)
95 {
96 }
97
98 QJSValuePrivate::QJSValuePrivate(double value)
99     : m_engine(0), m_state(CNumber), u(value)
100 {
101 }
102
103 QJSValuePrivate::QJSValuePrivate(const QString& value)
104     : m_engine(0), m_state(CString), u(new QString(value))
105 {
106 }
107
108 QJSValuePrivate::QJSValuePrivate(QJSValue::SpecialValue value)
109     : m_engine(0), m_state(value == QJSValue::NullValue ? CNull : CUndefined)
110 {
111 }
112
113 QJSValuePrivate::QJSValuePrivate(QV8Engine* engine, bool value)
114     : m_engine(engine), m_state(JSValue)
115 {
116     Q_ASSERT(engine);
117     v8::HandleScope handleScope;
118     m_value = v8::Persistent<v8::Value>::New(m_engine->makeJSValue(value));
119     m_engine->registerValue(this);
120 }
121
122 QJSValuePrivate::QJSValuePrivate(QV8Engine* engine, int value)
123     : m_engine(engine), m_state(JSValue)
124 {
125     Q_ASSERT(engine);
126     v8::HandleScope handleScope;
127     m_value = v8::Persistent<v8::Value>::New(m_engine->makeJSValue(value));
128     m_engine->registerValue(this);
129 }
130
131 QJSValuePrivate::QJSValuePrivate(QV8Engine* engine, uint value)
132     : m_engine(engine), m_state(JSValue)
133 {
134     Q_ASSERT(engine);
135     v8::HandleScope handleScope;
136     m_value = v8::Persistent<v8::Value>::New(m_engine->makeJSValue(value));
137     m_engine->registerValue(this);
138 }
139
140 QJSValuePrivate::QJSValuePrivate(QV8Engine* engine, double value)
141     : m_engine(engine), m_state(JSValue)
142 {
143     Q_ASSERT(engine);
144     v8::HandleScope handleScope;
145     m_value = v8::Persistent<v8::Value>::New(m_engine->makeJSValue(value));
146     m_engine->registerValue(this);
147 }
148
149 QJSValuePrivate::QJSValuePrivate(QV8Engine* engine, const QString& value)
150     : m_engine(engine), m_state(JSValue)
151 {
152     Q_ASSERT(engine);
153     v8::HandleScope handleScope;
154     m_value = v8::Persistent<v8::Value>::New(m_engine->makeJSValue(value));
155     m_engine->registerValue(this);
156 }
157
158 QJSValuePrivate::QJSValuePrivate(QV8Engine* engine, QJSValue::SpecialValue value)
159     : m_engine(engine), m_state(JSValue)
160 {
161     Q_ASSERT(engine);
162     v8::HandleScope handleScope;
163     m_value = v8::Persistent<v8::Value>::New(m_engine->makeJSValue(value));
164     m_engine->registerValue(this);
165 }
166
167 QJSValuePrivate::QJSValuePrivate(QV8Engine *engine, v8::Handle<v8::Value> value)
168     : m_engine(engine), m_state(JSValue), m_value(v8::Persistent<v8::Value>::New(value))
169 {
170     Q_ASSERT(engine);
171     // It shouldn't happen, v8 shows errors by returning an empty handler. This is important debug
172     // information and it can't be simply ignored.
173     Q_ASSERT(!value.IsEmpty());
174     m_engine->registerValue(this);
175 }
176
177 QJSValuePrivate::~QJSValuePrivate()
178 {
179     if (isJSBased()) {
180         m_engine->unregisterValue(this);
181         QScriptIsolate api(m_engine);
182         m_value.Dispose();
183     } else if (isStringBased()) {
184         delete u.m_string;
185     }
186 }
187
188 bool QJSValuePrivate::toBool() const
189 {
190     switch (m_state) {
191     case JSValue:
192         {
193             v8::HandleScope scope;
194             return m_value->ToBoolean()->Value();
195         }
196     case CNumber:
197         return !(qIsNaN(u.m_number) || !u.m_number);
198     case CBool:
199         return u.m_bool;
200     case CNull:
201     case CUndefined:
202         return false;
203     case CString:
204         return u.m_string->length();
205     }
206
207     Q_ASSERT_X(false, "toBool()", "Not all states are included in the previous switch statement.");
208     return false; // Avoid compiler warning.
209 }
210
211 double QJSValuePrivate::toNumber() const
212 {
213     switch (m_state) {
214     case JSValue:
215     {
216         v8::HandleScope scope;
217         return m_value->ToNumber()->Value();
218     }
219     case CNumber:
220         return u.m_number;
221     case CBool:
222         return u.m_bool ? 1 : 0;
223     case CNull:
224     case CUndefined:
225         return qQNaN();
226     case CString:
227         bool ok;
228         double result = u.m_string->toDouble(&ok);
229         if (ok)
230             return result;
231         result = u.m_string->toInt(&ok, 0); // Try other bases.
232         if (ok)
233             return result;
234         if (*u.m_string == QLatin1String("Infinity"))
235             return qInf();
236         if (*u.m_string == QLatin1String("-Infinity"))
237             return -qInf();
238         return u.m_string->length() ? qQNaN() : 0;
239     }
240
241     Q_ASSERT_X(false, "toNumber()", "Not all states are included in the previous switch statement.");
242     return 0; // Avoid compiler warning.
243 }
244
245 QString QJSValuePrivate::toString() const
246 {
247     switch (m_state) {
248     case CBool:
249         return u.m_bool ? QString::fromLatin1("true") : QString::fromLatin1("false");
250     case CString:
251         return *u.m_string;
252     case CNumber:
253         return QJSConverter::toString(u.m_number);
254     case CNull:
255         return QString::fromLatin1("null");
256     case CUndefined:
257         return QString::fromLatin1("undefined");
258     case JSValue:
259         Q_ASSERT(!m_value.IsEmpty());
260         v8::HandleScope handleScope;
261         v8::TryCatch tryCatch;
262         v8::Local<v8::String> result = m_value->ToString();
263         if (result.IsEmpty())
264             result = tryCatch.Exception()->ToString();
265         return QJSConverter::toString(result);
266     }
267
268     Q_ASSERT_X(false, "toString()", "Not all states are included in the previous switch statement.");
269     return QString(); // Avoid compiler warning.
270 }
271
272 QVariant QJSValuePrivate::toVariant() const
273 {
274     switch (m_state) {
275         case CBool:
276             return QVariant(u.m_bool);
277         case CString:
278             return QVariant(*u.m_string);
279         case CNumber:
280             return QVariant(u.m_number);
281         case CNull:
282             return QVariant(QMetaType::VoidStar, 0);
283         case CUndefined:
284             return QVariant();
285         case JSValue:
286             break;
287     }
288
289     Q_ASSERT(m_state == JSValue);
290     Q_ASSERT(!m_value.IsEmpty());
291     Q_ASSERT(m_engine);
292
293     v8::HandleScope handleScope;
294     return m_engine->variantFromJS(m_value);
295 }
296
297 inline QDateTime QJSValuePrivate::toDataTime() const
298 {
299     if (!isDate())
300         return QDateTime();
301
302     v8::HandleScope handleScope;
303     return QJSConverter::toDateTime(v8::Handle<v8::Date>::Cast(m_value));
304
305 }
306
307 QObject* QJSValuePrivate::toQObject() const
308 {
309     if (!isJSBased())
310         return 0;
311
312     v8::HandleScope handleScope;
313     return engine()->qtObjectFromJS(m_value);
314 }
315
316 double QJSValuePrivate::toInteger() const
317 {
318     double result = toNumber();
319     if (qIsNaN(result))
320         return 0;
321     if (qIsInf(result))
322         return result;
323
324     // Must use floor explicitly rather than qFloor here. On some
325     // platforms qFloor will cast the value to a single precision float and use
326     // floorf() which results in test failures.
327     return (result > 0) ? floor(result) : -1 * floor(-result);
328 }
329
330 qint32 QJSValuePrivate::toInt32() const
331 {
332     double result = toInteger();
333     // Orginaly it should look like that (result == 0 || qIsInf(result) || qIsNaN(result)), but
334     // some of these operation are invoked in toInteger subcall.
335     if (qIsInf(result))
336         return 0;
337     return result;
338 }
339
340 quint32 QJSValuePrivate::toUInt32() const
341 {
342     double result = toInteger();
343     // Orginaly it should look like that (result == 0 || qIsInf(result) || qIsNaN(result)), but
344     // some of these operation are invoked in toInteger subcall.
345     if (qIsInf(result))
346         return 0;
347
348     // The explicit casts are required to avoid undefined behaviour. For example, casting
349     // a negative double directly to an unsigned int on ARM NEON FPU results in the value
350     // being set to zero. Casting to a signed int first ensures well defined behaviour.
351     return (quint32) (qint32) result;
352 }
353
354 quint16 QJSValuePrivate::toUInt16() const
355 {
356     return toInt32();
357 }
358
359 inline bool QJSValuePrivate::isArray() const
360 {
361     return isJSBased() && m_value->IsArray();
362 }
363
364 inline bool QJSValuePrivate::isBool() const
365 {
366     return m_state == CBool || (isJSBased() && m_value->IsBoolean());
367 }
368
369 inline bool QJSValuePrivate::isCallable() const
370 {
371     if (isFunction())
372         return true;
373     if (isObject()) {
374         // Our C++ wrappers register function handlers but not always act as callables.
375         return v8::Object::Cast(*m_value)->IsCallable();
376     }
377     return false;
378 }
379
380 inline bool QJSValuePrivate::isError() const
381 {
382     if (!isJSBased())
383         return false;
384     v8::HandleScope handleScope;
385     return m_value->IsError();
386 }
387
388 inline bool QJSValuePrivate::isFunction() const
389 {
390     return isJSBased() && m_value->IsFunction();
391 }
392
393 inline bool QJSValuePrivate::isNull() const
394 {
395     return m_state == CNull || (isJSBased() && m_value->IsNull());
396 }
397
398 inline bool QJSValuePrivate::isNumber() const
399 {
400     return m_state == CNumber || (isJSBased() && m_value->IsNumber());
401 }
402
403 inline bool QJSValuePrivate::isObject() const
404 {
405     return isJSBased() && m_value->IsObject();
406 }
407
408 inline bool QJSValuePrivate::isString() const
409 {
410     return m_state == CString || (isJSBased() && m_value->IsString());
411 }
412
413 inline bool QJSValuePrivate::isUndefined() const
414 {
415     return m_state == CUndefined || (isJSBased() && m_value->IsUndefined());
416 }
417
418 inline bool QJSValuePrivate::isVariant() const
419 {
420     return isJSBased() && m_engine->isVariant(m_value);
421 }
422
423 bool QJSValuePrivate::isDate() const
424 {
425     return (isJSBased() && m_value->IsDate());
426 }
427
428 bool QJSValuePrivate::isRegExp() const
429 {
430     return (isJSBased() && m_value->IsRegExp());
431 }
432
433 bool QJSValuePrivate::isQObject() const
434 {
435     return isJSBased() && engine()->isQObject(m_value);
436 }
437
438 inline bool QJSValuePrivate::equals(QJSValuePrivate* other)
439 {
440     if (!isJSBased() && !other->isJSBased()) {
441         switch (m_state) {
442         case CNull:
443         case CUndefined:
444             return other->isUndefined() || other->isNull();
445         case CNumber:
446             switch (other->m_state) {
447             case CBool:
448             case CString:
449                 return u.m_number == other->toNumber();
450             case CNumber:
451                 return u.m_number == other->u.m_number;
452             default:
453                 return false;
454             }
455         case CBool:
456             switch (other->m_state) {
457             case CBool:
458                 return u.m_bool == other->u.m_bool;
459             case CNumber:
460                 return toNumber() == other->u.m_number;
461             case CString:
462                 return toNumber() == other->toNumber();
463             default:
464                 return false;
465             }
466         case CString:
467             switch (other->m_state) {
468             case CBool:
469                 return toNumber() == other->toNumber();
470             case CNumber:
471                 return toNumber() == other->u.m_number;
472             case CString:
473                 return *u.m_string == *other->u.m_string;
474             default:
475                 return false;
476             }
477         default:
478             Q_ASSERT_X(false, "QJSValue::equals", "Not all states are included in the previous switch statement.");
479         }
480     }
481
482     v8::HandleScope handleScope;
483     if (isJSBased() && !other->isJSBased()) {
484         if (!other->assignEngine(engine())) {
485             qWarning("QJSValue::equals: cannot compare to a value created in a different engine");
486             return false;
487         }
488     } else if (!isJSBased() && other->isJSBased()) {
489         if (!assignEngine(other->engine())) {
490             qWarning("QJSValue::equals: cannot compare to a value created in a different engine");
491             return false;
492         }
493     }
494
495     Q_ASSERT(this->engine() && other->engine());
496     if (this->engine() != other->engine()) {
497         qWarning("QJSValue::equals: cannot compare to a value created in a different engine");
498         return false;
499     }
500     return m_value->Equals(other->m_value);
501 }
502
503 inline bool QJSValuePrivate::strictlyEquals(QJSValuePrivate* other)
504 {
505     if (isJSBased()) {
506         // We can't compare these two values without binding to the same engine.
507         if (!other->isJSBased()) {
508             if (other->assignEngine(engine()))
509                 return m_value->StrictEquals(other->m_value);
510             return false;
511         }
512         if (other->engine() != engine()) {
513             qWarning("QJSValue::strictlyEquals: cannot compare to a value created in a different engine");
514             return false;
515         }
516         return m_value->StrictEquals(other->m_value);
517     }
518     if (isStringBased()) {
519         if (other->isStringBased())
520             return *u.m_string == *(other->u.m_string);
521         if (other->isJSBased()) {
522             assignEngine(other->engine());
523             return m_value->StrictEquals(other->m_value);
524         }
525     }
526     if (isNumberBased()) {
527         if (other->isJSBased()) {
528             assignEngine(other->engine());
529             return m_value->StrictEquals(other->m_value);
530         }
531         if (m_state != other->m_state)
532             return false;
533         if (m_state == CNumber)
534             return u.m_number == other->u.m_number;
535         Q_ASSERT(m_state == CBool);
536         return u.m_bool == other->u.m_bool;
537     }
538
539     return (isUndefined() && other->isUndefined())
540             || (isNull() && other->isNull());
541 }
542
543 inline QScriptPassPointer<QJSValuePrivate> QJSValuePrivate::prototype() const
544 {
545     if (isObject()) {
546         v8::HandleScope handleScope;
547         return new QJSValuePrivate(engine(), v8::Handle<v8::Object>::Cast(m_value)->GetPrototype());
548     }
549     return new QJSValuePrivate();
550 }
551
552 inline void QJSValuePrivate::setPrototype(QJSValuePrivate* prototype)
553 {
554     if (isObject() && (prototype->isObject() || prototype->isNull())) {
555         if (engine() != prototype->engine()) {
556             if (prototype->engine()) {
557                 qWarning("QJSValue::setPrototype() failed: cannot set a prototype created in a different engine");
558                 return;
559             }
560             prototype->assignEngine(engine());
561         }
562         v8::HandleScope handleScope;
563         if (!v8::Handle<v8::Object>::Cast(m_value)->SetPrototype(*prototype))
564             qWarning("QJSValue::setPrototype() failed: cyclic prototype value");
565     }
566 }
567
568 inline void QJSValuePrivate::setProperty(const QString& name, QJSValuePrivate* value, uint attribs)
569 {
570     if (!isObject())
571         return;
572     v8::HandleScope handleScope;
573     setProperty(QJSConverter::toString(name), value, attribs);
574 }
575
576 inline void QJSValuePrivate::setProperty(v8::Handle<v8::String> name, QJSValuePrivate* value, uint attribs)
577 {
578     if (!isObject())
579         return;
580
581     if (!value->isJSBased())
582         value->assignEngine(engine());
583
584     if (engine() != value->engine()) {
585         qWarning("QJSValue::setProperty(%s) failed: "
586                  "cannot set value created in a different engine",
587                  qPrintable(QJSConverter::toString(name)));
588         return;
589     }
590
591     v8::TryCatch tryCatch;
592 //    if (attribs & (QJSValue::PropertyGetter | QJSValue::PropertySetter)) {
593 //        engine()->originalGlobalObject()->defineGetterOrSetter(*this, name, value->m_value, attribs);
594 //    } else {
595         v8::Object::Cast(*m_value)->Set(name, value->m_value, v8::PropertyAttribute(attribs & QJSConverter::PropertyAttributeMask));
596 //    }
597 }
598
599 inline void QJSValuePrivate::setProperty(quint32 index, QJSValuePrivate* value, uint attribs)
600 {
601     // FIXME this method should by integrated with other overloads to use the same code patch.
602     // for now it is not possible as v8 doesn't allow to set property attributes using index based api.
603
604     if (!isObject())
605         return;
606
607     if (attribs) {
608         // FIXME we don't need to convert index to a string.
609         //Object::Set(int,value) do not take attributes.
610         setProperty(QString::number(index), value, attribs);
611         return;
612     }
613
614     if (!value->isJSBased())
615         value->assignEngine(engine());
616
617     if (engine() != value->engine()) {
618         qWarning("QJSValue::setProperty() failed: cannot set value created in a different engine");
619         return;
620     }
621
622     v8::HandleScope handleScope;
623     v8::Object::Cast(*m_value)->Set(index, value->m_value);
624 }
625
626 inline QScriptPassPointer<QJSValuePrivate> QJSValuePrivate::property(const QString& name) const
627 {
628     if (!isObject())
629         return new QJSValuePrivate();
630     if (!name.length())
631         return new QJSValuePrivate(engine());
632
633     v8::HandleScope handleScope;
634     return property(QJSConverter::toString(name));
635 }
636
637 inline QScriptPassPointer<QJSValuePrivate> QJSValuePrivate::property(v8::Handle<v8::String> name) const
638 {
639     Q_ASSERT(!name.IsEmpty());
640     if (!isObject())
641         return new QJSValuePrivate();
642     return property<>(name);
643 }
644
645 inline QScriptPassPointer<QJSValuePrivate> QJSValuePrivate::property(quint32 index) const
646 {
647     if (!isObject())
648         return new QJSValuePrivate();
649     return property<>(index);
650 }
651
652 template<typename T>
653 inline QScriptPassPointer<QJSValuePrivate> QJSValuePrivate::property(T name) const
654 {
655     Q_ASSERT(isObject());
656     v8::HandleScope handleScope;
657     v8::Handle<v8::Object> self(v8::Object::Cast(*m_value));
658
659     v8::TryCatch tryCatch;
660     v8::Handle<v8::Value> result = self->Get(name);
661     if (tryCatch.HasCaught())
662         result = tryCatch.Exception();
663     if (result.IsEmpty())
664         return new QJSValuePrivate(engine());
665     return new QJSValuePrivate(engine(), result);
666 }
667
668 inline bool QJSValuePrivate::deleteProperty(const QString& name)
669 {
670     if (!isObject())
671         return false;
672
673     v8::HandleScope handleScope;
674     v8::Handle<v8::Object> self(v8::Handle<v8::Object>::Cast(m_value));
675     return self->Delete(QJSConverter::toString(name));
676 }
677
678 inline bool QJSValuePrivate::hasProperty(const QString &name) const
679 {
680     if (!isObject())
681         return false;
682
683     v8::HandleScope handleScope;
684     v8::Handle<v8::Object> self(v8::Handle<v8::Object>::Cast(m_value));
685     return self->Has(QJSConverter::toString(name));
686 }
687
688 inline bool QJSValuePrivate::hasOwnProperty(const QString &name) const
689 {
690     if (!isObject())
691         return false;
692
693     v8::HandleScope handleScope;
694     v8::Handle<v8::Object> self(v8::Handle<v8::Object>::Cast(m_value));
695     return self->HasOwnProperty(QJSConverter::toString(name));
696 }
697
698 inline QJSValuePrivate::PropertyFlags QJSValuePrivate::propertyFlags(const QString& name) const
699 {
700     if (!isObject())
701         return QJSValuePrivate::PropertyFlags(0);
702
703     v8::HandleScope handleScope;
704     return engine()->getPropertyFlags(v8::Handle<v8::Object>::Cast(m_value), QJSConverter::toString(name));
705 }
706
707 inline QJSValuePrivate::PropertyFlags QJSValuePrivate::propertyFlags(v8::Handle<v8::String> name) const
708 {
709     if (!isObject())
710         return QJSValuePrivate::PropertyFlags(0);
711
712     v8::HandleScope handleScope;
713     return engine()->getPropertyFlags(v8::Handle<v8::Object>::Cast(m_value), name);
714 }
715
716 inline QScriptPassPointer<QJSValuePrivate> QJSValuePrivate::call(QJSValuePrivate* thisObject, const QJSValueList& args)
717 {
718     if (!isCallable())
719         return new QJSValuePrivate();
720
721     v8::HandleScope handleScope;
722
723     // Convert all arguments and bind to the engine.
724     int argc = args.size();
725     QVarLengthArray<v8::Handle<v8::Value>, 8> argv(argc);
726     if (!prepareArgumentsForCall(argv.data(), args)) {
727         qWarning("QJSValue::call() failed: cannot call function with argument created in a different engine");
728         return new QJSValuePrivate(engine());
729     }
730
731     return call(thisObject, argc, argv.data());
732 }
733
734 QScriptPassPointer<QJSValuePrivate> QJSValuePrivate::call(QJSValuePrivate* thisObject, int argc, v8::Handle<v8::Value> *argv)
735 {
736     QV8Engine *e = engine();
737
738     v8::Handle<v8::Object> recv;
739
740     if (!thisObject || !thisObject->isObject()) {
741         recv = v8::Handle<v8::Object>(v8::Object::Cast(*e->global()));
742     } else {
743         if (!thisObject->assignEngine(e)) {
744             qWarning("QJSValue::call() failed: cannot call function with thisObject created in a different engine");
745             return new QJSValuePrivate(engine());
746         }
747
748         recv = v8::Handle<v8::Object>(v8::Object::Cast(*thisObject->m_value));
749     }
750
751     if (argc < 0) {
752         v8::Local<v8::Value> exeption = v8::Exception::TypeError(v8::String::New("Arguments must be an array"));
753         return new QJSValuePrivate(e, exeption);
754     }
755
756     v8::TryCatch tryCatch;
757     v8::Handle<v8::Value> result = v8::Object::Cast(*m_value)->CallAsFunction(recv, argc, argv);
758
759     if (result.IsEmpty()) {
760         result = tryCatch.Exception();
761         // TODO: figure out why v8 doesn't always produce an exception value.
762         //Q_ASSERT(!result.IsEmpty());
763         if (result.IsEmpty())
764             result = v8::Exception::Error(v8::String::New("missing exception value"));
765     }
766
767     return new QJSValuePrivate(e, result);
768 }
769
770 inline QScriptPassPointer<QJSValuePrivate> QJSValuePrivate::callAsConstructor(int argc, v8::Handle<v8::Value> *argv)
771 {
772     QV8Engine *e = engine();
773
774     if (argc < 0) {
775         v8::Local<v8::Value> exeption = v8::Exception::TypeError(v8::String::New("Arguments must be an array"));
776         return new QJSValuePrivate(e, exeption);
777     }
778
779     v8::TryCatch tryCatch;
780     v8::Handle<v8::Value> result = v8::Object::Cast(*m_value)->CallAsConstructor(argc, argv);
781
782     if (result.IsEmpty())
783         result = tryCatch.Exception();
784
785     return new QJSValuePrivate(e, result);
786 }
787
788 inline QScriptPassPointer<QJSValuePrivate> QJSValuePrivate::callAsConstructor(const QJSValueList& args)
789 {
790     if (!isCallable())
791         return new QJSValuePrivate();
792
793     v8::HandleScope handleScope;
794
795     // Convert all arguments and bind to the engine.
796     int argc = args.size();
797     QVarLengthArray<v8::Handle<v8::Value>, 8> argv(argc);
798     if (!prepareArgumentsForCall(argv.data(), args)) {
799         qWarning("QJSValue::callAsConstructor() failed: cannot construct function with argument created in a different engine");
800         return new QJSValuePrivate(engine());
801     }
802
803     return callAsConstructor(argc, argv.data());
804 }
805
806 /*! \internal
807  * Make sure this value is associated with a v8 value belonging to this engine.
808  * If the value belongs to another engine, returns false.
809  */
810 bool QJSValuePrivate::assignEngine(QV8Engine* engine)
811 {
812     Q_ASSERT(engine);
813     v8::HandleScope handleScope;
814     switch (m_state) {
815     case CBool:
816         m_value = v8::Persistent<v8::Value>::New(engine->makeJSValue(u.m_bool));
817         break;
818     case CString:
819         m_value = v8::Persistent<v8::Value>::New(engine->makeJSValue(*u.m_string));
820         delete u.m_string;
821         break;
822     case CNumber:
823         m_value = v8::Persistent<v8::Value>::New(engine->makeJSValue(u.m_number));
824         break;
825     case CNull:
826         m_value = v8::Persistent<v8::Value>::New(engine->makeJSValue(QJSValue::NullValue));
827         break;
828     case CUndefined:
829         m_value = v8::Persistent<v8::Value>::New(engine->makeJSValue(QJSValue::UndefinedValue));
830         break;
831     default:
832         if (this->engine() == engine)
833             return true;
834         else if (!isJSBased())
835             Q_ASSERT_X(!isJSBased(), "assignEngine()", "Not all states are included in the previous switch statement.");
836         else
837             qWarning("JSValue can't be rassigned to an another engine.");
838         return false;
839     }
840     m_engine = engine;
841     m_state = JSValue;
842
843     m_engine->registerValue(this);
844     return true;
845 }
846
847 /*!
848   \internal
849   Invalidates this value (makes it undefined).
850
851   Does not remove the value from the engine's list of
852   registered values; that's the responsibility of the caller.
853 */
854 void QJSValuePrivate::invalidate()
855 {
856     if (isJSBased()) {
857         m_value.Dispose();
858         m_value.Clear();
859     } else if (isStringBased()) {
860         delete u.m_string;
861     }
862     m_engine = 0;
863     m_state = CUndefined;
864 }
865
866 QV8Engine* QJSValuePrivate::engine() const
867 {
868     return m_engine;
869 }
870
871 inline QJSValuePrivate::operator v8::Handle<v8::Value>() const
872 {
873     Q_ASSERT(isJSBased());
874     return m_value;
875 }
876
877 inline QJSValuePrivate::operator v8::Handle<v8::Object>() const
878 {
879     Q_ASSERT(isObject());
880     return v8::Handle<v8::Object>::Cast(m_value);
881 }
882
883 inline v8::Handle<v8::Value> QJSValuePrivate::handle() const
884 {
885     return m_value;
886 }
887
888 /*!
889  * Return a v8::Handle, assign to the engine if needed.
890  */
891 v8::Handle<v8::Value> QJSValuePrivate::asV8Value(QV8Engine* engine)
892 {
893     if (!m_engine) {
894         if (!assignEngine(engine))
895             return v8::Handle<v8::Value>();
896     }
897     Q_ASSERT(isJSBased());
898     return m_value;
899 }
900
901 /*!
902   \internal
903   Returns true if QSV have an engine associated.
904 */
905 bool QJSValuePrivate::isJSBased() const
906 {
907 #ifndef QT_NO_DEBUG
908     // internals check.
909     if (m_state >= JSValue)
910         Q_ASSERT(!m_value.IsEmpty());
911     else
912         Q_ASSERT(m_value.IsEmpty());
913 #endif
914     return m_state >= JSValue;
915 }
916
917 /*!
918   \internal
919   Returns true if current value of QSV is placed in m_number.
920 */
921 bool QJSValuePrivate::isNumberBased() const { return m_state == CNumber || m_state == CBool; }
922
923 /*!
924   \internal
925   Returns true if current value of QSV is placed in m_string.
926 */
927 bool QJSValuePrivate::isStringBased() const { return m_state == CString; }
928
929 /*!
930   \internal
931   Converts arguments and bind them to the engine.
932   \attention argv should be big enough
933 */
934 inline bool QJSValuePrivate::prepareArgumentsForCall(v8::Handle<v8::Value> argv[], const QJSValueList& args) const
935 {
936     QJSValueList::const_iterator i = args.constBegin();
937     for (int j = 0; i != args.constEnd(); j++, i++) {
938         QJSValuePrivate* value = QJSValuePrivate::get(*i);
939         if ((value->isJSBased() && engine() != value->engine())
940                 || (!value->isJSBased() && !value->assignEngine(engine())))
941             // Different engines are not allowed!
942             return false;
943         argv[j] = *value;
944     }
945     return true;
946 }
947
948 QT_END_NAMESPACE
949
950 #endif