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