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