c296d38b22a11f33c2ce2edf99200f82a4683b44
[profile/ivi/qtdeclarative.git] / src / declarative / qml / v8 / qjsvalue_impl_p.h
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: http://www.qt-project.org/
6 **
7 ** This file is part of the QtScript module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL-ONLY$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser
12 ** General Public License version 2.1 as published by the Free Software
13 ** Foundation and appearing in the file LICENSE.LGPL included in the
14 ** packaging of this file.  Please review the following information to
15 ** ensure the GNU Lesser General Public License version 2.1 requirements
16 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 **
18 ** If you have questions regarding the use of this file, please contact
19 ** us via http://www.qt-project.org/.
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 // This template is used indirectly by the Q_GLOBAL_STATIC macro below
46 template<>
47 class QGlobalStaticDeleter<QJSValuePrivate>
48 {
49 public:
50     QGlobalStatic<QJSValuePrivate> &globalStatic;
51     QGlobalStaticDeleter(QGlobalStatic<QJSValuePrivate> &_globalStatic)
52         : globalStatic(_globalStatic)
53     {
54         globalStatic.pointer.load()->ref.ref();
55     }
56
57     inline ~QGlobalStaticDeleter()
58     {
59         if (!globalStatic.pointer.load()->ref.deref()) { // Logic copy & paste from SharedDataPointer
60             delete globalStatic.pointer.load();
61         }
62         globalStatic.pointer.store(0);
63         globalStatic.destroyed = true;
64     }
65 };
66
67 Q_GLOBAL_STATIC(QJSValuePrivate, InvalidValue)
68
69 QJSValuePrivate* QJSValuePrivate::get(const QJSValue& q) { Q_ASSERT(q.d_ptr.data()); return q.d_ptr.data(); }
70
71 QJSValue QJSValuePrivate::get(const QJSValuePrivate* d)
72 {
73     Q_ASSERT(d);
74     return QJSValue(const_cast<QJSValuePrivate*>(d));
75 }
76
77 QJSValue QJSValuePrivate::get(QScriptPassPointer<QJSValuePrivate> d)
78 {
79     Q_ASSERT(d);
80     return QJSValue(d);
81 }
82
83 QJSValue QJSValuePrivate::get(QJSValuePrivate* d)
84 {
85     Q_ASSERT(d);
86     return QJSValue(d);
87 }
88
89 QJSValuePrivate::QJSValuePrivate()
90     : m_engine(0), m_state(Invalid)
91 {
92 }
93
94 QJSValuePrivate::QJSValuePrivate(bool value)
95     : m_engine(0), m_state(CBool), u(value)
96 {
97 }
98
99 QJSValuePrivate::QJSValuePrivate(int value)
100     : m_engine(0), m_state(CNumber), u(value)
101 {
102 }
103
104 QJSValuePrivate::QJSValuePrivate(uint value)
105     : m_engine(0), m_state(CNumber), u(value)
106 {
107 }
108
109 QJSValuePrivate::QJSValuePrivate(double value)
110     : m_engine(0), m_state(CNumber), u(value)
111 {
112 }
113
114 QJSValuePrivate::QJSValuePrivate(const QString& value)
115     : m_engine(0), m_state(CString), u(new QString(value))
116 {
117 }
118
119 QJSValuePrivate::QJSValuePrivate(QJSValue::SpecialValue value)
120     : m_engine(0), m_state(value == QJSValue::NullValue ? CNull : CUndefined)
121 {
122 }
123
124 QJSValuePrivate::QJSValuePrivate(QV8Engine* engine, bool value)
125     : m_engine(engine), m_state(JSValue)
126 {
127     Q_ASSERT(engine);
128     v8::HandleScope handleScope;
129     m_value = v8::Persistent<v8::Value>::New(m_engine->makeJSValue(value));
130     m_engine->registerValue(this);
131 }
132
133 QJSValuePrivate::QJSValuePrivate(QV8Engine* engine, int value)
134     : m_engine(engine), m_state(JSValue)
135 {
136     Q_ASSERT(engine);
137     v8::HandleScope handleScope;
138     m_value = v8::Persistent<v8::Value>::New(m_engine->makeJSValue(value));
139     m_engine->registerValue(this);
140 }
141
142 QJSValuePrivate::QJSValuePrivate(QV8Engine* engine, uint value)
143     : m_engine(engine), m_state(JSValue)
144 {
145     Q_ASSERT(engine);
146     v8::HandleScope handleScope;
147     m_value = v8::Persistent<v8::Value>::New(m_engine->makeJSValue(value));
148     m_engine->registerValue(this);
149 }
150
151 QJSValuePrivate::QJSValuePrivate(QV8Engine* engine, double value)
152     : m_engine(engine), m_state(JSValue)
153 {
154     Q_ASSERT(engine);
155     v8::HandleScope handleScope;
156     m_value = v8::Persistent<v8::Value>::New(m_engine->makeJSValue(value));
157     m_engine->registerValue(this);
158 }
159
160 QJSValuePrivate::QJSValuePrivate(QV8Engine* engine, const QString& value)
161     : m_engine(engine), m_state(JSValue)
162 {
163     Q_ASSERT(engine);
164     v8::HandleScope handleScope;
165     m_value = v8::Persistent<v8::Value>::New(m_engine->makeJSValue(value));
166     m_engine->registerValue(this);
167 }
168
169 QJSValuePrivate::QJSValuePrivate(QV8Engine* engine, QJSValue::SpecialValue value)
170     : m_engine(engine), m_state(JSValue)
171 {
172     Q_ASSERT(engine);
173     v8::HandleScope handleScope;
174     m_value = v8::Persistent<v8::Value>::New(m_engine->makeJSValue(value));
175     m_engine->registerValue(this);
176 }
177
178 QJSValuePrivate::QJSValuePrivate(QV8Engine *engine, v8::Handle<v8::Value> value)
179     : m_engine(engine), m_state(JSValue), m_value(v8::Persistent<v8::Value>::New(value))
180 {
181     Q_ASSERT(engine);
182     // It shouldn't happen, v8 shows errors by returning an empty handler. This is important debug
183     // information and it can't be simply ignored.
184     Q_ASSERT(!value.IsEmpty());
185     m_engine->registerValue(this);
186 }
187
188 QJSValuePrivate::~QJSValuePrivate()
189 {
190     if (isJSBased()) {
191         m_engine->unregisterValue(this);
192         QScriptIsolate api(m_engine);
193         m_value.Dispose();
194     } else if (isStringBased()) {
195         delete u.m_string;
196     }
197 }
198
199 bool QJSValuePrivate::toBool() const
200 {
201     switch (m_state) {
202     case JSValue:
203         {
204             v8::HandleScope scope;
205             return m_value->ToBoolean()->Value();
206         }
207     case CNumber:
208         return !(qIsNaN(u.m_number) || !u.m_number);
209     case CBool:
210         return u.m_bool;
211     case Invalid:
212     case CNull:
213     case CUndefined:
214         return false;
215     case CString:
216         return u.m_string->length();
217     }
218
219     Q_ASSERT_X(false, "toBool()", "Not all states are included in the previous switch statement.");
220     return false; // Avoid compiler warning.
221 }
222
223 double QJSValuePrivate::toNumber() const
224 {
225     switch (m_state) {
226     case JSValue:
227     {
228         v8::HandleScope scope;
229         return m_value->ToNumber()->Value();
230     }
231     case CNumber:
232         return u.m_number;
233     case CBool:
234         return u.m_bool ? 1 : 0;
235     case CNull:
236     case Invalid:
237         return 0;
238     case CUndefined:
239         return qQNaN();
240     case CString:
241         bool ok;
242         double result = u.m_string->toDouble(&ok);
243         if (ok)
244             return result;
245         result = u.m_string->toInt(&ok, 0); // Try other bases.
246         if (ok)
247             return result;
248         if (*u.m_string == QLatin1String("Infinity"))
249             return qInf();
250         if (*u.m_string == QLatin1String("-Infinity"))
251             return -qInf();
252         return u.m_string->length() ? qQNaN() : 0;
253     }
254
255     Q_ASSERT_X(false, "toNumber()", "Not all states are included in the previous switch statement.");
256     return 0; // Avoid compiler warning.
257 }
258
259 QScriptPassPointer<QJSValuePrivate> QJSValuePrivate::toObject(QV8Engine* engine) const
260 {
261     Q_ASSERT(engine);
262     if (this->engine() && engine != this->engine()) {
263         qWarning("QJSEngine::toObject: cannot convert value created in a different engine");
264         return InvalidValue();
265     }
266
267     v8::HandleScope scope;
268     switch (m_state) {
269     case Invalid:
270     case CNull:
271     case CUndefined:
272         return new QJSValuePrivate;
273     case CString:
274         return new QJSValuePrivate(engine, engine->makeJSValue(*u.m_string)->ToObject());
275     case CNumber:
276         return new QJSValuePrivate(engine, engine->makeJSValue(u.m_number)->ToObject());
277     case CBool:
278         return new QJSValuePrivate(engine, engine->makeJSValue(u.m_bool)->ToObject());
279     case JSValue:
280         if (m_value->IsObject())
281             return const_cast<QJSValuePrivate*>(this);
282         if (m_value->IsNull() || m_value->IsUndefined()) // avoid "Uncaught TypeError: Cannot convert null to object"
283             return InvalidValue();
284         return new QJSValuePrivate(engine, m_value->ToObject());
285     default:
286         Q_ASSERT_X(false, Q_FUNC_INFO, "Not all states are included in this switch");
287         return InvalidValue();
288     }
289 }
290
291 /*!
292   This method is created only for QJSValue::toObject() purpose which is obsolete.
293   \internal
294  */
295 QScriptPassPointer<QJSValuePrivate> QJSValuePrivate::toObject() const
296 {
297     if (isJSBased())
298         return toObject(engine());
299
300     // Without an engine there is not much we can do.
301     return new QJSValuePrivate;
302 }
303
304 QString QJSValuePrivate::toString() const
305 {
306     switch (m_state) {
307     case Invalid:
308         return QString();
309     case CBool:
310         return u.m_bool ? QString::fromLatin1("true") : QString::fromLatin1("false");
311     case CString:
312         return *u.m_string;
313     case CNumber:
314         return QJSConverter::toString(u.m_number);
315     case CNull:
316         return QString::fromLatin1("null");
317     case CUndefined:
318         return QString::fromLatin1("undefined");
319     case JSValue:
320         Q_ASSERT(!m_value.IsEmpty());
321         v8::HandleScope handleScope;
322         v8::TryCatch tryCatch;
323         v8::Local<v8::String> result = m_value->ToString();
324         if (result.IsEmpty()) {
325             result = tryCatch.Exception()->ToString();
326             m_engine->setException(tryCatch.Exception(), tryCatch.Message());
327         }
328         return QJSConverter::toString(result);
329     }
330
331     Q_ASSERT_X(false, "toString()", "Not all states are included in the previous switch statement.");
332     return QString(); // Avoid compiler warning.
333 }
334
335 QVariant QJSValuePrivate::toVariant() const
336 {
337     switch (m_state) {
338         case Invalid:
339             return QVariant();
340         case CBool:
341             return QVariant(u.m_bool);
342         case CString:
343             return QVariant(*u.m_string);
344         case CNumber:
345             return QVariant(u.m_number);
346         case CNull:
347             return QVariant();
348         case CUndefined:
349             return QVariant();
350         case JSValue:
351             break;
352     }
353
354     Q_ASSERT(m_state == JSValue);
355     Q_ASSERT(!m_value.IsEmpty());
356     Q_ASSERT(m_engine);
357
358     v8::HandleScope handleScope;
359     return m_engine->variantFromJS(m_value);
360 }
361
362 inline QDateTime QJSValuePrivate::toDataTime() const
363 {
364     if (!isDate())
365         return QDateTime();
366
367     v8::HandleScope handleScope;
368     return QJSConverter::toDateTime(v8::Handle<v8::Date>::Cast(m_value));
369
370 }
371
372 inline QRegExp QJSValuePrivate::toRegExp() const
373 {
374     if (!isRegExp())
375         return QRegExp();
376
377     v8::HandleScope handleScope;
378     return QJSConverter::toRegExp(v8::Handle<v8::RegExp>::Cast(m_value));
379 }
380
381 QObject* QJSValuePrivate::toQObject() const
382 {
383     if (!isJSBased())
384         return 0;
385
386     v8::HandleScope handleScope;
387     return engine()->qtObjectFromJS(m_value);
388 }
389
390 double QJSValuePrivate::toInteger() const
391 {
392     double result = toNumber();
393     if (qIsNaN(result))
394         return 0;
395     if (qIsInf(result))
396         return result;
397     return (result > 0) ? qFloor(result) : -1 * qFloor(-result);
398 }
399
400 qint32 QJSValuePrivate::toInt32() const
401 {
402     double result = toInteger();
403     // Orginaly it should look like that (result == 0 || qIsInf(result) || qIsNaN(result)), but
404     // some of these operation are invoked in toInteger subcall.
405     if (qIsInf(result))
406         return 0;
407     return result;
408 }
409
410 quint32 QJSValuePrivate::toUInt32() const
411 {
412     double result = toInteger();
413     // Orginaly it should look like that (result == 0 || qIsInf(result) || qIsNaN(result)), but
414     // some of these operation are invoked in toInteger subcall.
415     if (qIsInf(result))
416         return 0;
417     return result;
418 }
419
420 quint16 QJSValuePrivate::toUInt16() const
421 {
422     return toInt32();
423 }
424
425 inline bool QJSValuePrivate::isArray() const
426 {
427     return isJSBased() && m_value->IsArray();
428 }
429
430 inline bool QJSValuePrivate::isBool() const
431 {
432     return m_state == CBool || (isJSBased() && m_value->IsBoolean());
433 }
434
435 inline bool QJSValuePrivate::isCallable() const
436 {
437     if (isFunction())
438         return true;
439     if (isObject()) {
440         // Our C++ wrappers register function handlers but not always act as callables.
441         return v8::Object::Cast(*m_value)->IsCallable();
442     }
443     return false;
444 }
445
446 inline bool QJSValuePrivate::isError() const
447 {
448     if (!isJSBased())
449         return false;
450     v8::HandleScope handleScope;
451     return m_value->IsError();
452 }
453
454 inline bool QJSValuePrivate::isFunction() const
455 {
456     return isJSBased() && m_value->IsFunction();
457 }
458
459 inline bool QJSValuePrivate::isNull() const
460 {
461     return m_state == CNull || (isJSBased() && m_value->IsNull());
462 }
463
464 inline bool QJSValuePrivate::isNumber() const
465 {
466     return m_state == CNumber || (isJSBased() && m_value->IsNumber());
467 }
468
469 inline bool QJSValuePrivate::isObject() const
470 {
471     return isJSBased() && m_value->IsObject();
472 }
473
474 inline bool QJSValuePrivate::isString() const
475 {
476     return m_state == CString || (isJSBased() && m_value->IsString());
477 }
478
479 inline bool QJSValuePrivate::isUndefined() const
480 {
481     return m_state == CUndefined || (isJSBased() && m_value->IsUndefined());
482 }
483
484 inline bool QJSValuePrivate::isValid() const
485 {
486     return m_state != Invalid;
487 }
488
489 inline bool QJSValuePrivate::isVariant() const
490 {
491     return isJSBased() && m_engine->isVariant(m_value);
492 }
493
494 bool QJSValuePrivate::isDate() const
495 {
496     return (isJSBased() && m_value->IsDate());
497 }
498
499 bool QJSValuePrivate::isRegExp() const
500 {
501     return (isJSBased() && m_value->IsRegExp());
502 }
503
504 bool QJSValuePrivate::isQObject() const
505 {
506     return isJSBased() && engine()->isQObject(m_value);
507 }
508
509 inline bool QJSValuePrivate::equals(QJSValuePrivate* other)
510 {
511     if (!isValid())
512         return !other->isValid();
513
514     if (!other->isValid())
515         return false;
516
517     if (!isJSBased() && !other->isJSBased()) {
518         switch (m_state) {
519         case CNull:
520         case CUndefined:
521             return other->isUndefined() || other->isNull();
522         case CNumber:
523             switch (other->m_state) {
524             case CBool:
525             case CString:
526                 return u.m_number == other->toNumber();
527             case CNumber:
528                 return u.m_number == other->u.m_number;
529             default:
530                 return false;
531             }
532         case CBool:
533             switch (other->m_state) {
534             case CBool:
535                 return u.m_bool == other->u.m_bool;
536             case CNumber:
537                 return toNumber() == other->u.m_number;
538             case CString:
539                 return toNumber() == other->toNumber();
540             default:
541                 return false;
542             }
543         case CString:
544             switch (other->m_state) {
545             case CBool:
546                 return toNumber() == other->toNumber();
547             case CNumber:
548                 return toNumber() == other->u.m_number;
549             case CString:
550                 return *u.m_string == *other->u.m_string;
551             default:
552                 return false;
553             }
554         default:
555             Q_ASSERT_X(false, "QJSValue::equals", "Not all states are included in the previous switch statement.");
556         }
557     }
558
559     v8::HandleScope handleScope;
560     if (isJSBased() && !other->isJSBased()) {
561         if (!other->assignEngine(engine())) {
562             qWarning("QJSValue::equals: cannot compare to a value created in a different engine");
563             return false;
564         }
565     } else if (!isJSBased() && other->isJSBased()) {
566         if (!assignEngine(other->engine())) {
567             qWarning("QJSValue::equals: cannot compare to a value created in a different engine");
568             return false;
569         }
570     }
571
572     Q_ASSERT(this->engine() && other->engine());
573     if (this->engine() != other->engine()) {
574         qWarning("QJSValue::equals: cannot compare to a value created in a different engine");
575         return false;
576     }
577     return m_value->Equals(other->m_value);
578 }
579
580 inline bool QJSValuePrivate::strictlyEquals(QJSValuePrivate* other)
581 {
582     if (isJSBased()) {
583         // We can't compare these two values without binding to the same engine.
584         if (!other->isJSBased()) {
585             if (other->assignEngine(engine()))
586                 return m_value->StrictEquals(other->m_value);
587             return false;
588         }
589         if (other->engine() != engine()) {
590             qWarning("QJSValue::strictlyEquals: cannot compare to a value created in a different engine");
591             return false;
592         }
593         return m_value->StrictEquals(other->m_value);
594     }
595     if (isStringBased()) {
596         if (other->isStringBased())
597             return *u.m_string == *(other->u.m_string);
598         if (other->isJSBased()) {
599             assignEngine(other->engine());
600             return m_value->StrictEquals(other->m_value);
601         }
602     }
603     if (isNumberBased()) {
604         if (other->isJSBased()) {
605             assignEngine(other->engine());
606             return m_value->StrictEquals(other->m_value);
607         }
608         if (m_state != other->m_state)
609             return false;
610         if (m_state == CNumber)
611             return u.m_number == other->u.m_number;
612         Q_ASSERT(m_state == CBool);
613         return u.m_bool == other->u.m_bool;
614     }
615
616     if (!isValid() && !other->isValid())
617         return true;
618
619     return false;
620 }
621
622 inline bool QJSValuePrivate::lessThan(QJSValuePrivate *other) const
623 {
624     if (engine() != other->engine() && engine() && other->engine()) {
625         qWarning("QJSValue::lessThan: cannot compare to a value created in a different engine");
626         return false;
627     }
628
629     if (!isValid() || !other->isValid())
630         return false;
631
632     if (isString() && other->isString())
633         return toString() < other->toString();
634
635     if (isObject() || other->isObject()) {
636         v8::HandleScope handleScope;
637         QV8Engine *eng = m_engine ? engine() : other->engine();
638         // FIXME: lessThan can throw an exception which will be dropped by this code:
639         Q_ASSERT(eng);
640         eng->saveException();
641         QScriptSharedDataPointer<QJSValuePrivate> cmp(eng->evaluate(QString::fromLatin1("(function(a,b){return a<b})")));
642         Q_ASSERT(cmp->isFunction());
643         v8::Handle<v8::Value> args[2];
644         cmp->prepareArgumentsForCall(args, QJSValueList() << QJSValuePrivate::get(this) << QJSValuePrivate::get(other));
645         QScriptSharedDataPointer<QJSValuePrivate> resultValue(cmp->call(0, 2, args));
646         bool result = resultValue->toBool();
647         eng->restoreException();
648         return result;
649     }
650
651     double nthis = toNumber();
652     double nother = other->toNumber();
653     if (qIsNaN(nthis) || qIsNaN(nother)) {
654         // Should return undefined in ECMA standard.
655         return false;
656     }
657     return nthis < nother;
658 }
659
660 inline bool QJSValuePrivate::instanceOf(QJSValuePrivate* other) const
661 {
662     if (!isObject() || !other->isFunction())
663         return false;
664     if (engine() != other->engine()) {
665         qWarning("QJSValue::instanceof: cannot perform operation on a value created in a different engine");
666         return false;
667     }
668     v8::HandleScope handleScope;
669     return instanceOf(v8::Handle<v8::Object>::Cast(other->m_value));
670 }
671
672 inline bool QJSValuePrivate::instanceOf(v8::Handle<v8::Object> other) const
673 {
674     Q_ASSERT(isObject());
675     Q_ASSERT(other->IsFunction());
676
677     v8::Handle<v8::Object> self = v8::Handle<v8::Object>::Cast(m_value);
678     v8::Handle<v8::Value> selfPrototype = self->GetPrototype();
679     v8::Handle<v8::Value> otherPrototype = other->Get(v8::String::New("prototype"));
680
681     while (!selfPrototype->IsNull()) {
682         if (selfPrototype->StrictEquals(otherPrototype))
683             return true;
684         // In general a prototype can be an object or null, but in the loop it can't be null, so
685         // we can cast it safely.
686         selfPrototype = v8::Handle<v8::Object>::Cast(selfPrototype)->GetPrototype();
687     }
688     return false;
689 }
690
691 inline QScriptPassPointer<QJSValuePrivate> QJSValuePrivate::prototype() const
692 {
693     if (isObject()) {
694         v8::HandleScope handleScope;
695         return new QJSValuePrivate(engine(), v8::Handle<v8::Object>::Cast(m_value)->GetPrototype());
696     }
697     return InvalidValue();
698 }
699
700 inline void QJSValuePrivate::setPrototype(QJSValuePrivate* prototype)
701 {
702     if (isObject() && (prototype->isObject() || prototype->isNull())) {
703         if (engine() != prototype->engine()) {
704             if (prototype->engine()) {
705                 qWarning("QJSValue::setPrototype() failed: cannot set a prototype created in a different engine");
706                 return;
707             }
708             prototype->assignEngine(engine());
709         }
710         v8::HandleScope handleScope;
711         if (!v8::Handle<v8::Object>::Cast(m_value)->SetPrototype(*prototype))
712             qWarning("QJSValue::setPrototype() failed: cyclic prototype value");
713     }
714 }
715
716 inline void QJSValuePrivate::setProperty(const QString& name, QJSValuePrivate* value, uint attribs)
717 {
718     if (!isObject())
719         return;
720     v8::HandleScope handleScope;
721     setProperty(QJSConverter::toString(name), value, attribs);
722 }
723
724 inline void QJSValuePrivate::setProperty(v8::Handle<v8::String> name, QJSValuePrivate* value, uint attribs)
725 {
726     if (!isObject())
727         return;
728
729     if (!value->isJSBased())
730         value->assignEngine(engine());
731
732     if (!value->isValid()) {
733         // Remove the property.
734         v8::HandleScope handleScope;
735         v8::TryCatch tryCatch;
736         v8::Handle<v8::Object> recv(v8::Object::Cast(*m_value));
737 //        if (attribs & QJSValue::PropertyGetter && !(attribs & QJSValue::PropertySetter)) {
738 //            v8::Local<v8::Object> descriptor = engine()->originalGlobalObject()->getOwnPropertyDescriptor(recv, name);
739 //            if (!descriptor.IsEmpty()) {
740 //                v8::Local<v8::Value> setter = descriptor->Get(v8::String::New("set"));
741 //                if (!setter.IsEmpty() && !setter->IsUndefined()) {
742 //                    recv->Delete(name);
743 //                    engine()->originalGlobalObject()->defineGetterOrSetter(recv, name, setter, QJSValue::PropertySetter);
744 //                    if (tryCatch.HasCaught())
745 //                        engine()->setException(tryCatch.Exception(), tryCatch.Message());
746 //                    return;
747 //                }
748 //            }
749 //        } else if (attribs & QJSValue::PropertySetter && !(attribs & QJSValue::PropertyGetter)) {
750 //            v8::Local<v8::Object> descriptor = engine()->originalGlobalObject()->getOwnPropertyDescriptor(recv, name);
751 //            if (!descriptor.IsEmpty()) {
752 //                v8::Local<v8::Value> getter = descriptor->Get(v8::String::New("get"));
753 //                if (!getter.IsEmpty() && !getter->IsUndefined()) {
754 //                    recv->Delete(name);
755 //                    engine()->originalGlobalObject()->defineGetterOrSetter(recv, name, getter, QJSValue::PropertyGetter);
756 //                    if (tryCatch.HasCaught())
757 //                        engine()->setException(tryCatch.Exception(), tryCatch.Message());
758 //                    return;
759 //                }
760 //            }
761 //        }
762         recv->Delete(name);
763         if (tryCatch.HasCaught())
764             engine()->setException(tryCatch.Exception(), tryCatch.Message());
765         return;
766     }
767
768     if (engine() != value->engine()) {
769         qWarning("QJSValue::setProperty(%s) failed: "
770                  "cannot set value created in a different engine",
771                  qPrintable(QJSConverter::toString(name)));
772         return;
773     }
774
775     v8::TryCatch tryCatch;
776 //    if (attribs & (QJSValue::PropertyGetter | QJSValue::PropertySetter)) {
777 //        engine()->originalGlobalObject()->defineGetterOrSetter(*this, name, value->m_value, attribs);
778 //    } else {
779         v8::Object::Cast(*m_value)->Set(name, value->m_value, v8::PropertyAttribute(attribs & QJSConverter::PropertyAttributeMask));
780 //    }
781     if (tryCatch.HasCaught())
782         engine()->setException(tryCatch.Exception(), tryCatch.Message());
783 }
784
785 inline void QJSValuePrivate::setProperty(quint32 index, QJSValuePrivate* value, uint attribs)
786 {
787     // FIXME this method should by integrated with other overloads to use the same code patch.
788     // for now it is not possible as v8 doesn't allow to set property attributes using index based api.
789
790     if (!isObject())
791         return;
792
793     if (attribs) {
794         // FIXME we dont need to convert index to a string.
795         //Object::Set(int,value) do not take attributes.
796         setProperty(QString::number(index), value, attribs);
797         return;
798     }
799
800     if (!value->isJSBased())
801         value->assignEngine(engine());
802
803     if (!value->isValid()) {
804         // Remove the property.
805         v8::HandleScope handleScope;
806         v8::TryCatch tryCatch;
807         v8::Object::Cast(*m_value)->Delete(index);
808         if (tryCatch.HasCaught())
809             engine()->setException(tryCatch.Exception(), tryCatch.Message());
810         return;
811     }
812
813     if (engine() != value->engine()) {
814         qWarning("QJSValue::setProperty() failed: cannot set value created in a different engine");
815         return;
816     }
817
818     v8::HandleScope handleScope;
819     v8::TryCatch tryCatch;
820     v8::Object::Cast(*m_value)->Set(index, value->m_value);
821     if (tryCatch.HasCaught())
822         engine()->setException(tryCatch.Exception(), tryCatch.Message());
823 }
824
825 inline QScriptPassPointer<QJSValuePrivate> QJSValuePrivate::property(const QString& name) const
826 {
827     if (!name.length())
828         return InvalidValue();
829     if (!isObject())
830         return InvalidValue();
831
832     v8::HandleScope handleScope;
833     return property(QJSConverter::toString(name));
834 }
835
836 inline QScriptPassPointer<QJSValuePrivate> QJSValuePrivate::property(v8::Handle<v8::String> name) const
837 {
838     Q_ASSERT(!name.IsEmpty());
839     if (!isObject())
840         return InvalidValue();
841     return property<>(name);
842 }
843
844 inline QScriptPassPointer<QJSValuePrivate> QJSValuePrivate::property(quint32 index) const
845 {
846     if (!isObject())
847         return InvalidValue();
848     return property<>(index);
849 }
850
851 template<typename T>
852 inline QScriptPassPointer<QJSValuePrivate> QJSValuePrivate::property(T name) const
853 {
854     Q_ASSERT(isObject());
855     v8::HandleScope handleScope;
856     v8::Handle<v8::Object> self(v8::Object::Cast(*m_value));
857
858     v8::TryCatch tryCatch;
859     v8::Handle<v8::Value> result = self->Get(name);
860     if (tryCatch.HasCaught()) {
861         result = tryCatch.Exception();
862         engine()->setException(result, tryCatch.Message());
863         return new QJSValuePrivate(engine(), result);
864     }
865     if (result.IsEmpty() || (result->IsUndefined() && !self->Has(name))) {
866         // In QtScript we make a distinction between a property that exists and has value undefined,
867         // and a property that doesn't exist; in the latter case, we should return an invalid value.
868         return InvalidValue();
869     }
870     return new QJSValuePrivate(engine(), result);
871 }
872
873 inline bool QJSValuePrivate::deleteProperty(const QString& name)
874 {
875     if (!isObject())
876         return false;
877
878     v8::HandleScope handleScope;
879     v8::Handle<v8::Object> self(v8::Handle<v8::Object>::Cast(m_value));
880     return self->Delete(QJSConverter::toString(name));
881 }
882
883 inline bool QJSValuePrivate::hasProperty(const QString &name) const
884 {
885     if (!isObject())
886         return false;
887
888     v8::HandleScope handleScope;
889     v8::Handle<v8::Object> self(v8::Handle<v8::Object>::Cast(m_value));
890     return self->Has(QJSConverter::toString(name));
891 }
892
893 inline bool QJSValuePrivate::hasOwnProperty(const QString &name) const
894 {
895     if (!isObject())
896         return false;
897
898     v8::HandleScope handleScope;
899     v8::Handle<v8::Object> self(v8::Handle<v8::Object>::Cast(m_value));
900     return self->HasOwnProperty(QJSConverter::toString(name));
901 }
902
903 inline QJSValue::PropertyFlags QJSValuePrivate::propertyFlags(const QString& name) const
904 {
905     if (!isObject())
906         return QJSValue::PropertyFlags(0);
907
908     v8::HandleScope handleScope;
909     return engine()->getPropertyFlags(v8::Handle<v8::Object>::Cast(m_value), QJSConverter::toString(name));
910 }
911
912 inline QJSValue::PropertyFlags QJSValuePrivate::propertyFlags(v8::Handle<v8::String> name) const
913 {
914     if (!isObject())
915         return QJSValue::PropertyFlags(0);
916
917     v8::HandleScope handleScope;
918     return engine()->getPropertyFlags(v8::Handle<v8::Object>::Cast(m_value), name);
919 }
920
921 inline QScriptPassPointer<QJSValuePrivate> QJSValuePrivate::call(QJSValuePrivate* thisObject, const QJSValueList& args)
922 {
923     if (!isCallable())
924         return InvalidValue();
925
926     v8::HandleScope handleScope;
927
928     // Convert all arguments and bind to the engine.
929     int argc = args.size();
930     QVarLengthArray<v8::Handle<v8::Value>, 8> argv(argc);
931     if (!prepareArgumentsForCall(argv.data(), args)) {
932         qWarning("QJSValue::call() failed: cannot call function with argument created in a different engine");
933         return InvalidValue();
934     }
935
936     return call(thisObject, argc, argv.data());
937 }
938
939 QScriptPassPointer<QJSValuePrivate> QJSValuePrivate::call(QJSValuePrivate* thisObject, int argc, v8::Handle<v8::Value> *argv)
940 {
941     QV8Engine *e = engine();
942
943     v8::Handle<v8::Object> recv;
944
945     if (!thisObject || !thisObject->isObject()) {
946         recv = v8::Handle<v8::Object>(v8::Object::Cast(*e->global()));
947     } else {
948         if (!thisObject->assignEngine(e)) {
949             qWarning("QJSValue::call() failed: cannot call function with thisObject created in a different engine");
950             return InvalidValue();
951         }
952
953         recv = v8::Handle<v8::Object>(v8::Object::Cast(*thisObject->m_value));
954     }
955
956     if (argc < 0) {
957         v8::Local<v8::Value> exeption = v8::Exception::TypeError(v8::String::New("Arguments must be an array"));
958         e->setException(exeption);
959         return new QJSValuePrivate(e, exeption);
960     }
961
962     v8::TryCatch tryCatch;
963     v8::Handle<v8::Value> result = v8::Object::Cast(*m_value)->CallAsFunction(recv, argc, argv);
964
965     if (result.IsEmpty()) {
966         result = tryCatch.Exception();
967         // TODO: figure out why v8 doesn't always produce an exception value.
968         //Q_ASSERT(!result.IsEmpty());
969         if (result.IsEmpty())
970             result = v8::Exception::Error(v8::String::New("missing exception value"));
971         e->setException(result, tryCatch.Message());
972     }
973
974     return new QJSValuePrivate(e, result);
975 }
976
977 inline QScriptPassPointer<QJSValuePrivate> QJSValuePrivate::callAsConstructor(int argc, v8::Handle<v8::Value> *argv)
978 {
979     QV8Engine *e = engine();
980
981     if (argc < 0) {
982         v8::Local<v8::Value> exeption = v8::Exception::TypeError(v8::String::New("Arguments must be an array"));
983         e->setException(exeption);
984         return new QJSValuePrivate(e, exeption);
985     }
986
987     v8::TryCatch tryCatch;
988     v8::Handle<v8::Value> result = v8::Object::Cast(*m_value)->CallAsConstructor(argc, argv);
989
990     if (result.IsEmpty()) {
991         result = tryCatch.Exception();
992         e->setException(result, tryCatch.Message());
993     }
994
995     return new QJSValuePrivate(e, result);
996 }
997
998 inline QScriptPassPointer<QJSValuePrivate> QJSValuePrivate::callAsConstructor(const QJSValueList& args)
999 {
1000     if (!isCallable())
1001         return InvalidValue();
1002
1003     v8::HandleScope handleScope;
1004
1005     // Convert all arguments and bind to the engine.
1006     int argc = args.size();
1007     QVarLengthArray<v8::Handle<v8::Value>, 8> argv(argc);
1008     if (!prepareArgumentsForCall(argv.data(), args)) {
1009         qWarning("QJSValue::callAsConstructor() failed: cannot construct function with argument created in a different engine");
1010         return InvalidValue();
1011     }
1012
1013     return callAsConstructor(argc, argv.data());
1014 }
1015
1016 /*! \internal
1017  * Make sure this value is associated with a v8 value belogning to this engine.
1018  * If the value was invalid, or belogning to another engine, return false.
1019  */
1020 bool QJSValuePrivate::assignEngine(QV8Engine* engine)
1021 {
1022     Q_ASSERT(engine);
1023     v8::HandleScope handleScope;
1024     switch (m_state) {
1025     case Invalid:
1026         return false;
1027     case CBool:
1028         m_value = v8::Persistent<v8::Value>::New(engine->makeJSValue(u.m_bool));
1029         break;
1030     case CString:
1031         m_value = v8::Persistent<v8::Value>::New(engine->makeJSValue(*u.m_string));
1032         delete u.m_string;
1033         break;
1034     case CNumber:
1035         m_value = v8::Persistent<v8::Value>::New(engine->makeJSValue(u.m_number));
1036         break;
1037     case CNull:
1038         m_value = v8::Persistent<v8::Value>::New(engine->makeJSValue(QJSValue::NullValue));
1039         break;
1040     case CUndefined:
1041         m_value = v8::Persistent<v8::Value>::New(engine->makeJSValue(QJSValue::UndefinedValue));
1042         break;
1043     default:
1044         if (this->engine() == engine)
1045             return true;
1046         else if (!isJSBased())
1047             Q_ASSERT_X(!isJSBased(), "assignEngine()", "Not all states are included in the previous switch statement.");
1048         else
1049             qWarning("JSValue can't be rassigned to an another engine.");
1050         return false;
1051     }
1052     m_engine = engine;
1053     m_state = JSValue;
1054
1055     m_engine->registerValue(this);
1056     return true;
1057 }
1058
1059 /*!
1060   \internal
1061   Invalidates this value.
1062
1063   Does not remove the value from the engine's list of
1064   registered values; that's the responsibility of the caller.
1065 */
1066 void QJSValuePrivate::invalidate()
1067 {
1068     if (isJSBased()) {
1069         m_value.Dispose();
1070         m_value.Clear();
1071     } else if (isStringBased()) {
1072         delete u.m_string;
1073     }
1074     m_engine = 0;
1075     m_state = Invalid;
1076 }
1077
1078 QV8Engine* QJSValuePrivate::engine() const
1079 {
1080     return m_engine;
1081 }
1082
1083 inline QJSValuePrivate::operator v8::Handle<v8::Value>() const
1084 {
1085     Q_ASSERT(isJSBased());
1086     return m_value;
1087 }
1088
1089 inline QJSValuePrivate::operator v8::Handle<v8::Object>() const
1090 {
1091     Q_ASSERT(isObject());
1092     return v8::Handle<v8::Object>::Cast(m_value);
1093 }
1094
1095 /*!
1096  * Return a v8::Handle, assign to the engine if needed.
1097  */
1098 v8::Handle<v8::Value> QJSValuePrivate::asV8Value(QV8Engine* engine)
1099 {
1100     if (!m_engine) {
1101         if (!assignEngine(engine))
1102             return v8::Handle<v8::Value>();
1103     }
1104     Q_ASSERT(isJSBased());
1105     return m_value;
1106 }
1107
1108 /*!
1109   \internal
1110   Returns true if QSV have an engine associated.
1111 */
1112 bool QJSValuePrivate::isJSBased() const
1113 {
1114 #ifndef QT_NO_DEBUG
1115     // internals check.
1116     if (m_state >= JSValue)
1117         Q_ASSERT(!m_value.IsEmpty());
1118     else
1119         Q_ASSERT(m_value.IsEmpty());
1120 #endif
1121     return m_state >= JSValue;
1122 }
1123
1124 /*!
1125   \internal
1126   Returns true if current value of QSV is placed in m_number.
1127 */
1128 bool QJSValuePrivate::isNumberBased() const { return m_state == CNumber || m_state == CBool; }
1129
1130 /*!
1131   \internal
1132   Returns true if current value of QSV is placed in m_string.
1133 */
1134 bool QJSValuePrivate::isStringBased() const { return m_state == CString; }
1135
1136 /*!
1137   \internal
1138   Converts arguments and bind them to the engine.
1139   \attention argv should be big enough
1140 */
1141 inline bool QJSValuePrivate::prepareArgumentsForCall(v8::Handle<v8::Value> argv[], const QJSValueList& args) const
1142 {
1143     QJSValueList::const_iterator i = args.constBegin();
1144     for (int j = 0; i != args.constEnd(); j++, i++) {
1145         QJSValuePrivate* value = QJSValuePrivate::get(*i);
1146         if ((value->isJSBased() && engine() != value->engine())
1147                 || (!value->isJSBased() && value->isValid() && !value->assignEngine(engine())))
1148             // Different engines are not allowed!
1149             return false;
1150         if (value->isValid())
1151             argv[j] = *value;
1152         else
1153             argv[j] = engine()->makeJSValue(QJSValue::UndefinedValue);
1154     }
1155     return true;
1156 }
1157
1158 QT_END_NAMESPACE
1159
1160 #endif