initial import
[vuplus_webkit] / Source / JavaScriptCore / qt / api / qscriptvalue_p.h
1 /*
2     Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
3
4     This library is free software; you can redistribute it and/or
5     modify it under the terms of the GNU Library General Public
6     License as published by the Free Software Foundation; either
7     version 2 of the License, or (at your option) any later version.
8
9     This library is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12     Library General Public License for more details.
13
14     You should have received a copy of the GNU Library General Public License
15     along with this library; see the file COPYING.LIB.  If not, write to
16     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17     Boston, MA 02110-1301, USA.
18 */
19
20 #ifndef qscriptvalue_p_h
21 #define qscriptvalue_p_h
22
23 #include "qscriptconverter_p.h"
24 #include "qscriptengine_p.h"
25 #include "qscriptvalue.h"
26 #include <JavaScriptCore/JavaScript.h>
27 #include <JavaScriptCore/JSRetainPtr.h>
28 #include <JSObjectRefPrivate.h>
29 #include <QtCore/qdatetime.h>
30 #include <QtCore/qmath.h>
31 #include <QtCore/qnumeric.h>
32 #include <QtCore/qshareddata.h>
33 #include <QtCore/qvarlengtharray.h>
34
35 class QScriptEngine;
36 class QScriptValue;
37
38 /*
39   \internal
40   \class QScriptValuePrivate
41
42   Implementation of QScriptValue.
43   The implementation is based on a state machine. The states names are included in
44   QScriptValuePrivate::State. Each method should check for the current state and then perform a
45   correct action.
46
47   State:
48     Invalid -> QSVP is invalid, no assumptions should be made about class members (apart from m_value).
49     CString -> QSVP is created from QString or const char* and no JSC engine has been associated yet.
50         Current value is kept in m_string,
51     CNumber -> QSVP is created from int, uint, double... and no JSC engine has been bind yet. Current
52         value is kept in m_number
53     CBool -> QSVP is created from bool and no JSC engine has been associated yet. Current value is kept
54         in m_bool
55     CNull -> QSVP is null, but a JSC engine hasn't been associated yet.
56     CUndefined -> QSVP is undefined, but a JSC engine hasn't been associated yet.
57     JSValue -> QSVP is associated with engine, but there is no information about real type, the state
58         have really short live cycle. Normally it is created as a function call result.
59     JSPrimitive -> QSVP is associated with engine, and it is sure that it isn't a JavaScript object.
60     JSObject -> QSVP is associated with engine, and it is sure that it is a JavaScript object.
61
62   Each state keep all necessary information to invoke all methods, if not it should be changed to
63   a proper state. Changed state shouldn't be reverted.
64
65   The QScriptValuePrivate use the JSC C API directly. The QSVP type is equal to combination of
66   the JSValueRef and the JSObjectRef, and it could be automatically casted to these types by cast
67   operators.
68 */
69
70 class QScriptValuePrivate : public QSharedData {
71 public:
72     inline static QScriptValuePrivate* get(const QScriptValue& q);
73     inline static QScriptValue get(const QScriptValuePrivate* d);
74     inline static QScriptValue get(QScriptValuePrivate* d);
75
76     inline ~QScriptValuePrivate();
77
78     inline QScriptValuePrivate();
79     inline QScriptValuePrivate(const QString& string);
80     inline QScriptValuePrivate(bool value);
81     inline QScriptValuePrivate(int number);
82     inline QScriptValuePrivate(uint number);
83     inline QScriptValuePrivate(qsreal number);
84     inline QScriptValuePrivate(QScriptValue::SpecialValue value);
85
86     inline QScriptValuePrivate(const QScriptEnginePrivate* engine, bool value);
87     inline QScriptValuePrivate(const QScriptEnginePrivate* engine, int value);
88     inline QScriptValuePrivate(const QScriptEnginePrivate* engine, uint value);
89     inline QScriptValuePrivate(const QScriptEnginePrivate* engine, qsreal value);
90     inline QScriptValuePrivate(const QScriptEnginePrivate* engine, const QString& value);
91     inline QScriptValuePrivate(const QScriptEnginePrivate* engine, QScriptValue::SpecialValue value);
92
93     inline QScriptValuePrivate(const QScriptEnginePrivate* engine, JSValueRef value);
94     inline QScriptValuePrivate(const QScriptEnginePrivate* engine, JSObjectRef object);
95
96     inline bool isValid() const;
97     inline bool isBool();
98     inline bool isNumber();
99     inline bool isNull();
100     inline bool isString();
101     inline bool isUndefined();
102     inline bool isError();
103     inline bool isObject();
104     inline bool isFunction();
105     inline bool isArray();
106     inline bool isDate();
107
108     inline QString toString() const;
109     inline qsreal toNumber() const;
110     inline bool toBool() const;
111     inline qsreal toInteger() const;
112     inline qint32 toInt32() const;
113     inline quint32 toUInt32() const;
114     inline quint16 toUInt16() const;
115
116     inline QScriptValuePrivate* toObject(QScriptEnginePrivate* engine);
117     inline QScriptValuePrivate* toObject();
118     inline QDateTime toDateTime();
119     inline QScriptValuePrivate* prototype();
120     inline void setPrototype(QScriptValuePrivate* prototype);
121
122     inline bool equals(QScriptValuePrivate* other);
123     inline bool strictlyEquals(QScriptValuePrivate* other);
124     inline bool instanceOf(QScriptValuePrivate* other);
125     inline bool assignEngine(QScriptEnginePrivate* engine);
126
127     inline QScriptValuePrivate* property(const QString& name, const QScriptValue::ResolveFlags& mode);
128     inline QScriptValuePrivate* property(const QScriptStringPrivate* name, const QScriptValue::ResolveFlags& mode);
129     inline QScriptValuePrivate* property(quint32 arrayIndex, const QScriptValue::ResolveFlags& mode);
130     inline JSValueRef property(quint32 property, JSValueRef* exception);
131     inline JSValueRef property(JSStringRef property, JSValueRef* exception);
132     inline bool hasOwnProperty(quint32 property);
133     inline bool hasOwnProperty(JSStringRef property);
134     template<typename T>
135     inline QScriptValuePrivate* property(T name, const QScriptValue::ResolveFlags& mode);
136
137     inline void setProperty(const QString& name, QScriptValuePrivate* value, const QScriptValue::PropertyFlags& flags);
138     inline void setProperty(const QScriptStringPrivate* name, QScriptValuePrivate* value, const QScriptValue::PropertyFlags& flags);
139     inline void setProperty(const quint32 indexArray, QScriptValuePrivate* value, const QScriptValue::PropertyFlags& flags);
140     inline void setProperty(quint32 property, JSValueRef value, JSPropertyAttributes flags, JSValueRef* exception);
141     inline void setProperty(JSStringRef property, JSValueRef value, JSPropertyAttributes flags, JSValueRef* exception);
142     inline void deleteProperty(quint32 property, JSValueRef* exception);
143     inline void deleteProperty(JSStringRef property, JSValueRef* exception);
144     template<typename T>
145     inline void setProperty(T name, QScriptValuePrivate* value, const QScriptValue::PropertyFlags& flags);
146
147     QScriptValue::PropertyFlags propertyFlags(const QString& name, const QScriptValue::ResolveFlags& mode);
148     QScriptValue::PropertyFlags propertyFlags(const QScriptStringPrivate* name, const QScriptValue::ResolveFlags& mode);
149     QScriptValue::PropertyFlags propertyFlags(const JSStringRef name, const QScriptValue::ResolveFlags& mode);
150
151     inline QScriptValuePrivate* call(const QScriptValuePrivate* , const QScriptValueList& args);
152
153     inline operator JSValueRef() const;
154     inline operator JSObjectRef() const;
155
156     inline QScriptEnginePrivate* engine() const;
157
158 private:
159     // Please, update class documentation when you change the enum.
160     enum State {
161         Invalid = 0,
162         CString = 0x1000,
163         CNumber,
164         CBool,
165         CNull,
166         CUndefined,
167         JSValue = 0x2000, // JS values are equal or higher then this value.
168         JSPrimitive,
169         JSObject
170     } m_state;
171     QScriptEnginePtr m_engine;
172     union Value
173     {
174         bool m_bool;
175         qsreal m_number;
176         JSValueRef m_value;
177         JSObjectRef m_object;
178         QString* m_string;
179
180         Value() : m_number(0) {}
181         Value(bool value) : m_bool(value) {}
182         Value(int number) : m_number(number) {}
183         Value(uint number) : m_number(number) {}
184         Value(qsreal number) : m_number(number) {}
185         Value(JSValueRef value) : m_value(value) {}
186         Value(JSObjectRef object) : m_object(object) {}
187         Value(QString* string) : m_string(string) {}
188     } u;
189
190     inline State refinedJSValue();
191
192     inline bool isJSBased() const;
193     inline bool isNumberBased() const;
194     inline bool isStringBased() const;
195 };
196
197 QScriptValuePrivate* QScriptValuePrivate::get(const QScriptValue& q) { return q.d_ptr.data(); }
198
199 QScriptValue QScriptValuePrivate::get(const QScriptValuePrivate* d)
200 {
201     return QScriptValue(const_cast<QScriptValuePrivate*>(d));
202 }
203
204 QScriptValue QScriptValuePrivate::get(QScriptValuePrivate* d)
205 {
206     return QScriptValue(d);
207 }
208
209 QScriptValuePrivate::~QScriptValuePrivate()
210 {
211     if (isJSBased())
212         JSValueUnprotect(*m_engine, u.m_value);
213     else if (isStringBased())
214         delete u.m_string;
215 }
216
217 QScriptValuePrivate::QScriptValuePrivate()
218     : m_state(Invalid)
219 {
220 }
221
222 QScriptValuePrivate::QScriptValuePrivate(const QString& string)
223     : m_state(CString)
224     , u(new QString(string))
225 {
226 }
227
228 QScriptValuePrivate::QScriptValuePrivate(bool value)
229     : m_state(CBool)
230     , u(value)
231 {
232 }
233
234 QScriptValuePrivate::QScriptValuePrivate(int number)
235     : m_state(CNumber)
236     , u(number)
237 {
238 }
239
240 QScriptValuePrivate::QScriptValuePrivate(uint number)
241     : m_state(CNumber)
242     , u(number)
243 {
244 }
245
246 QScriptValuePrivate::QScriptValuePrivate(qsreal number)
247     : m_state(CNumber)
248     , u(number)
249 {
250 }
251
252 QScriptValuePrivate::QScriptValuePrivate(QScriptValue::SpecialValue value)
253     : m_state(value == QScriptValue::NullValue ? CNull : CUndefined)
254 {
255 }
256
257 QScriptValuePrivate::QScriptValuePrivate(const QScriptEnginePrivate* engine, bool value)
258     : m_state(JSPrimitive)
259     , m_engine(const_cast<QScriptEnginePrivate*>(engine))
260     , u(engine->makeJSValue(value))
261 {
262     Q_ASSERT(engine);
263     JSValueProtect(*m_engine, u.m_value);
264 }
265
266 QScriptValuePrivate::QScriptValuePrivate(const QScriptEnginePrivate* engine, int value)
267     : m_state(JSPrimitive)
268     , m_engine(const_cast<QScriptEnginePrivate*>(engine))
269     , u(m_engine->makeJSValue(value))
270 {
271     Q_ASSERT(engine);
272     JSValueProtect(*m_engine, u.m_value);
273 }
274
275 QScriptValuePrivate::QScriptValuePrivate(const QScriptEnginePrivate* engine, uint value)
276     : m_state(JSPrimitive)
277     , m_engine(const_cast<QScriptEnginePrivate*>(engine))
278     , u(m_engine->makeJSValue(value))
279 {
280     Q_ASSERT(engine);
281     JSValueProtect(*m_engine, u.m_value);
282 }
283
284 QScriptValuePrivate::QScriptValuePrivate(const QScriptEnginePrivate* engine, qsreal value)
285     : m_state(JSPrimitive)
286     , m_engine(const_cast<QScriptEnginePrivate*>(engine))
287     , u(m_engine->makeJSValue(value))
288 {
289     Q_ASSERT(engine);
290     JSValueProtect(*m_engine, u.m_value);
291 }
292
293 QScriptValuePrivate::QScriptValuePrivate(const QScriptEnginePrivate* engine, const QString& value)
294     : m_state(JSPrimitive)
295     , m_engine(const_cast<QScriptEnginePrivate*>(engine))
296     , u(m_engine->makeJSValue(value))
297 {
298     Q_ASSERT(engine);
299     JSValueProtect(*m_engine, u.m_value);
300 }
301
302 QScriptValuePrivate::QScriptValuePrivate(const QScriptEnginePrivate* engine, QScriptValue::SpecialValue value)
303     : m_state(JSPrimitive)
304     , m_engine(const_cast<QScriptEnginePrivate*>(engine))
305     , u(m_engine->makeJSValue(value))
306 {
307     Q_ASSERT(engine);
308     JSValueProtect(*m_engine, u.m_value);
309 }
310
311 QScriptValuePrivate::QScriptValuePrivate(const QScriptEnginePrivate* engine, JSValueRef value)
312     : m_state(JSValue)
313     , m_engine(const_cast<QScriptEnginePrivate*>(engine))
314     , u(value)
315 {
316     Q_ASSERT(engine);
317     Q_ASSERT(value);
318     JSValueProtect(*m_engine, u.m_value);
319 }
320
321 QScriptValuePrivate::QScriptValuePrivate(const QScriptEnginePrivate* engine, JSObjectRef object)
322     : m_state(JSObject)
323     , m_engine(const_cast<QScriptEnginePrivate*>(engine))
324     , u(object)
325 {
326     Q_ASSERT(engine);
327     Q_ASSERT(object);
328     JSValueProtect(*m_engine, object);
329 }
330
331 bool QScriptValuePrivate::isValid() const { return m_state != Invalid; }
332
333 bool QScriptValuePrivate::isBool()
334 {
335     switch (m_state) {
336     case CBool:
337         return true;
338     case JSValue:
339         if (refinedJSValue() != JSPrimitive)
340             return false;
341         // Fall-through.
342     case JSPrimitive:
343         return JSValueIsBoolean(*m_engine, *this);
344     default:
345         return false;
346     }
347 }
348
349 bool QScriptValuePrivate::isNumber()
350 {
351     switch (m_state) {
352     case CNumber:
353         return true;
354     case JSValue:
355         if (refinedJSValue() != JSPrimitive)
356             return false;
357         // Fall-through.
358     case JSPrimitive:
359         return JSValueIsNumber(*m_engine, *this);
360     default:
361         return false;
362     }
363 }
364
365 bool QScriptValuePrivate::isNull()
366 {
367     switch (m_state) {
368     case CNull:
369         return true;
370     case JSValue:
371         if (refinedJSValue() != JSPrimitive)
372             return false;
373         // Fall-through.
374     case JSPrimitive:
375         return JSValueIsNull(*m_engine, *this);
376     default:
377         return false;
378     }
379 }
380
381 bool QScriptValuePrivate::isString()
382 {
383     switch (m_state) {
384     case CString:
385         return true;
386     case JSValue:
387         if (refinedJSValue() != JSPrimitive)
388             return false;
389         // Fall-through.
390     case JSPrimitive:
391         return JSValueIsString(*m_engine, *this);
392     default:
393         return false;
394     }
395 }
396
397 bool QScriptValuePrivate::isUndefined()
398 {
399     switch (m_state) {
400     case CUndefined:
401         return true;
402     case JSValue:
403         if (refinedJSValue() != JSPrimitive)
404             return false;
405         // Fall-through.
406     case JSPrimitive:
407         return JSValueIsUndefined(*m_engine, *this);
408     default:
409         return false;
410     }
411 }
412
413 bool QScriptValuePrivate::isError()
414 {
415     switch (m_state) {
416     case JSValue:
417         if (refinedJSValue() != JSObject)
418             return false;
419         // Fall-through.
420     case JSObject:
421         return m_engine->isError(*this);
422     default:
423         return false;
424     }
425 }
426
427 bool QScriptValuePrivate::isObject()
428 {
429     switch (m_state) {
430     case JSValue:
431         return refinedJSValue() == JSObject;
432     case JSObject:
433         return true;
434
435     default:
436         return false;
437     }
438 }
439
440 bool QScriptValuePrivate::isFunction()
441 {
442     switch (m_state) {
443     case JSValue:
444         if (refinedJSValue() != JSObject)
445             return false;
446         // Fall-through.
447     case JSObject:
448         return JSObjectIsFunction(*m_engine, *this);
449     default:
450         return false;
451     }
452 }
453
454 bool QScriptValuePrivate::isArray()
455 {
456     switch (m_state) {
457     case JSValue:
458         if (refinedJSValue() != JSObject)
459             return false;
460         // Fall-through.
461     case JSObject:
462         return m_engine->isArray(*this);
463     default:
464         return false;
465     }
466 }
467
468 bool QScriptValuePrivate::isDate()
469 {
470     switch (m_state) {
471     case JSValue:
472         if (refinedJSValue() != JSObject)
473             return false;
474         // Fall-through.
475     case JSObject:
476         return m_engine->isDate(*this);
477     default:
478         return false;
479     }
480 }
481
482 QString QScriptValuePrivate::toString() const
483 {
484     switch (m_state) {
485     case Invalid:
486         return QString();
487     case CBool:
488         return u.m_bool ? QString::fromLatin1("true") : QString::fromLatin1("false");
489     case CString:
490         return *u.m_string;
491     case CNumber:
492         return QScriptConverter::toString(u.m_number);
493     case CNull:
494         return QString::fromLatin1("null");
495     case CUndefined:
496         return QString::fromLatin1("undefined");
497     case JSValue:
498     case JSPrimitive:
499     case JSObject:
500         JSValueRef exception = 0;
501         JSRetainPtr<JSStringRef> ptr(Adopt, JSValueToStringCopy(*m_engine, *this, &exception));
502         m_engine->setException(exception);
503         return QScriptConverter::toString(ptr.get());
504     }
505
506     Q_ASSERT_X(false, "toString()", "Not all states are included in the previous switch statement.");
507     return QString(); // Avoid compiler warning.
508 }
509
510 qsreal QScriptValuePrivate::toNumber() const
511 {
512     switch (m_state) {
513     case JSValue:
514     case JSPrimitive:
515     case JSObject:
516         {
517             JSValueRef exception = 0;
518             qsreal result = JSValueToNumber(*m_engine, *this, &exception);
519             m_engine->setException(exception);
520             return result;
521         }
522     case CNumber:
523         return u.m_number;
524     case CBool:
525         return u.m_bool ? 1 : 0;
526     case CNull:
527     case Invalid:
528         return 0;
529     case CUndefined:
530         return qQNaN();
531     case CString:
532         bool ok;
533         qsreal result = u.m_string->toDouble(&ok);
534         if (ok)
535             return result;
536         result = u.m_string->toInt(&ok, 0); // Try other bases.
537         if (ok)
538             return result;
539         if (*u.m_string == "Infinity" || *u.m_string == "-Infinity")
540             return qInf();
541         return u.m_string->length() ? qQNaN() : 0;
542     }
543
544     Q_ASSERT_X(false, "toNumber()", "Not all states are included in the previous switch statement.");
545     return 0; // Avoid compiler warning.
546 }
547
548 bool QScriptValuePrivate::toBool() const
549 {
550     switch (m_state) {
551     case JSValue:
552     case JSPrimitive:
553         return JSValueToBoolean(*m_engine, *this);
554     case JSObject:
555         return true;
556     case CNumber:
557         return !(qIsNaN(u.m_number) || !u.m_number);
558     case CBool:
559         return u.m_bool;
560     case Invalid:
561     case CNull:
562     case CUndefined:
563         return false;
564     case CString:
565         return u.m_string->length();
566     }
567
568     Q_ASSERT_X(false, "toBool()", "Not all states are included in the previous switch statement.");
569     return false; // Avoid compiler warning.
570 }
571
572 qsreal QScriptValuePrivate::toInteger() const
573 {
574     qsreal result = toNumber();
575     if (qIsNaN(result))
576         return 0;
577     if (qIsInf(result))
578         return result;
579     return (result > 0) ? qFloor(result) : -1 * qFloor(-result);
580 }
581
582 qint32 QScriptValuePrivate::toInt32() const
583 {
584     qsreal result = toInteger();
585     // Orginaly it should look like that (result == 0 || qIsInf(result) || qIsNaN(result)), but
586     // some of these operation are invoked in toInteger subcall.
587     if (qIsInf(result))
588         return 0;
589     return result;
590 }
591
592 quint32 QScriptValuePrivate::toUInt32() const
593 {
594     qsreal result = toInteger();
595     // Orginaly it should look like that (result == 0 || qIsInf(result) || qIsNaN(result)), but
596     // some of these operation are invoked in toInteger subcall.
597     if (qIsInf(result))
598         return 0;
599     return result;
600 }
601
602 quint16 QScriptValuePrivate::toUInt16() const
603 {
604     return toInt32();
605 }
606
607 /*!
608   Creates a copy of this value and converts it to an object. If this value is an object
609   then pointer to this value will be returned.
610   \attention it should not happen but if this value is bounded to a different engine that the given, the first
611   one will be used.
612   \internal
613   */
614 QScriptValuePrivate* QScriptValuePrivate::toObject(QScriptEnginePrivate* engine)
615 {
616     switch (m_state) {
617     case Invalid:
618     case CNull:
619     case CUndefined:
620         return new QScriptValuePrivate;
621     case CString:
622         {
623             // Exception can't occur here.
624             JSObjectRef object = JSValueToObject(*engine, engine->makeJSValue(*u.m_string), /* exception */ 0);
625             Q_ASSERT(object);
626             return new QScriptValuePrivate(engine, object);
627         }
628     case CNumber:
629         {
630             // Exception can't occur here.
631             JSObjectRef object = JSValueToObject(*engine, engine->makeJSValue(u.m_number), /* exception */ 0);
632             Q_ASSERT(object);
633             return new QScriptValuePrivate(engine, object);
634         }
635     case CBool:
636         {
637             // Exception can't occure here.
638             JSObjectRef object = JSValueToObject(*engine, engine->makeJSValue(u.m_bool), /* exception */ 0);
639             Q_ASSERT(object);
640             return new QScriptValuePrivate(engine, object);
641         }
642     case JSValue:
643         if (refinedJSValue() != JSPrimitive)
644             break;
645         // Fall-through.
646     case JSPrimitive:
647         {
648             if (engine != this->engine())
649                 qWarning("QScriptEngine::toObject: cannot convert value created in a different engine");
650             JSValueRef exception = 0;
651             JSObjectRef object = JSValueToObject(*m_engine, *this, &exception);
652             if (object)
653                 return new QScriptValuePrivate(m_engine.constData(), object);
654             else
655                 m_engine->setException(exception, QScriptEnginePrivate::NotNullException);
656
657         }
658         return new QScriptValuePrivate;
659     case JSObject:
660         break;
661     }
662
663     if (engine != this->engine())
664         qWarning("QScriptEngine::toObject: cannot convert value created in a different engine");
665     Q_ASSERT(m_state == JSObject);
666     return this;
667 }
668
669 /*!
670   This method is created only for QScriptValue::toObject() purpose which is obsolete.
671   \internal
672  */
673 QScriptValuePrivate* QScriptValuePrivate::toObject()
674 {
675     if (isJSBased())
676         return toObject(m_engine.data());
677
678     // Without an engine there is not much we can do.
679     return new QScriptValuePrivate;
680 }
681
682 QDateTime QScriptValuePrivate::toDateTime()
683 {
684     if (!isDate())
685         return QDateTime();
686
687     JSValueRef exception = 0;
688     qsreal t = JSValueToNumber(*m_engine, *this, &exception);
689
690     if (exception) {
691         m_engine->setException(exception, QScriptEnginePrivate::NotNullException);
692         return QDateTime();
693     }
694
695     QDateTime result;
696     result.setMSecsSinceEpoch(qint64(t));
697     return result;
698 }
699
700 inline QScriptValuePrivate* QScriptValuePrivate::prototype()
701 {
702     if (isObject()) {
703         JSValueRef prototype = JSObjectGetPrototype(*m_engine, *this);
704         if (JSValueIsNull(*m_engine, prototype))
705             return new QScriptValuePrivate(engine(), prototype);
706         // The prototype could be either a null or a JSObject, so it is safe to cast the prototype
707         // to the JSObjectRef here.
708         return new QScriptValuePrivate(engine(), const_cast<JSObjectRef>(prototype));
709     }
710     return new QScriptValuePrivate;
711 }
712
713 inline void QScriptValuePrivate::setPrototype(QScriptValuePrivate* prototype)
714 {
715     if (isObject() && prototype->isValid() && (prototype->isObject() || prototype->isNull())) {
716         if (engine() != prototype->engine()) {
717             qWarning("QScriptValue::setPrototype() failed: cannot set a prototype created in a different engine");
718             return;
719         }
720         // FIXME: This could be replaced by a new, faster API
721         // look at https://bugs.webkit.org/show_bug.cgi?id=40060
722         JSObjectSetPrototype(*m_engine, *this, *prototype);
723         JSValueRef proto = JSObjectGetPrototype(*m_engine, *this);
724         if (!JSValueIsStrictEqual(*m_engine, proto, *prototype))
725             qWarning("QScriptValue::setPrototype() failed: cyclic prototype value");
726     }
727 }
728
729 bool QScriptValuePrivate::equals(QScriptValuePrivate* other)
730 {
731     if (!isValid())
732         return !other->isValid();
733
734     if (!other->isValid())
735         return false;
736
737     if (!isJSBased() && !other->isJSBased()) {
738         switch (m_state) {
739         case CNull:
740         case CUndefined:
741             return other->isUndefined() || other->isNull();
742         case CNumber:
743             switch (other->m_state) {
744             case CBool:
745             case CString:
746                 return u.m_number == other->toNumber();
747             case CNumber:
748                 return u.m_number == other->u.m_number;
749             default:
750                 return false;
751             }
752         case CBool:
753             switch (other->m_state) {
754             case CBool:
755                 return u.m_bool == other->u.m_bool;
756             case CNumber:
757                 return toNumber() == other->u.m_number;
758             case CString:
759                 return toNumber() == other->toNumber();
760             default:
761                 return false;
762             }
763         case CString:
764             switch (other->m_state) {
765             case CBool:
766                 return toNumber() == other->toNumber();
767             case CNumber:
768                 return toNumber() == other->u.m_number;
769             case CString:
770                 return *u.m_string == *other->u.m_string;
771             default:
772                 return false;
773             }
774         default:
775             Q_ASSERT_X(false, "equals()", "Not all states are included in the previous switch statement.");
776         }
777     }
778
779     if (isJSBased() && !other->isJSBased()) {
780         if (!other->assignEngine(engine())) {
781             qWarning("equals(): Cannot compare to a value created in a different engine");
782             return false;
783         }
784     } else if (!isJSBased() && other->isJSBased()) {
785         if (!assignEngine(other->engine())) {
786             qWarning("equals(): Cannot compare to a value created in a different engine");
787             return false;
788         }
789     }
790
791     JSValueRef exception = 0;
792     bool result = JSValueIsEqual(*m_engine, *this, *other, &exception);
793     m_engine->setException(exception);
794     return result;
795 }
796
797 bool QScriptValuePrivate::strictlyEquals(QScriptValuePrivate* other)
798 {
799     if (isJSBased()) {
800         // We can't compare these two values without binding to the same engine.
801         if (!other->isJSBased()) {
802             if (other->assignEngine(engine()))
803                 return JSValueIsStrictEqual(*m_engine, *this, *other);
804             return false;
805         }
806         if (other->engine() != engine()) {
807             qWarning("strictlyEquals(): Cannot compare to a value created in a different engine");
808             return false;
809         }
810         return JSValueIsStrictEqual(*m_engine, *this, *other);
811     }
812     if (isStringBased()) {
813         if (other->isStringBased())
814             return *u.m_string == *(other->u.m_string);
815         if (other->isJSBased()) {
816             assignEngine(other->engine());
817             return JSValueIsStrictEqual(*m_engine, *this, *other);
818         }
819     }
820     if (isNumberBased()) {
821         if (other->isNumberBased())
822             return u.m_number == other->u.m_number;
823         if (other->isJSBased()) {
824             assignEngine(other->engine());
825             return JSValueIsStrictEqual(*m_engine, *this, *other);
826         }
827     }
828     if (!isValid() && !other->isValid())
829         return true;
830
831     return false;
832 }
833
834 inline bool QScriptValuePrivate::instanceOf(QScriptValuePrivate* other)
835 {
836     if (!isJSBased() || !other->isObject())
837         return false;
838     JSValueRef exception = 0;
839     bool result = JSValueIsInstanceOfConstructor(*m_engine, *this, *other, &exception);
840     m_engine->setException(exception);
841     return result;
842 }
843
844 /*!
845   Tries to assign \a engine to this value. Returns true on success; otherwise returns false.
846 */
847 bool QScriptValuePrivate::assignEngine(QScriptEnginePrivate* engine)
848 {
849     Q_ASSERT(engine);
850     JSValueRef value;
851     switch (m_state) {
852     case CBool:
853         value = engine->makeJSValue(u.m_bool);
854         break;
855     case CString:
856         value = engine->makeJSValue(*u.m_string);
857         delete u.m_string;
858         break;
859     case CNumber:
860         value = engine->makeJSValue(u.m_number);
861         break;
862     case CNull:
863         value = engine->makeJSValue(QScriptValue::NullValue);
864         break;
865     case CUndefined:
866         value = engine->makeJSValue(QScriptValue::UndefinedValue);
867         break;
868     default:
869         if (!isJSBased())
870             Q_ASSERT_X(!isJSBased(), "assignEngine()", "Not all states are included in the previous switch statement.");
871         else
872             qWarning("JSValue can't be rassigned to an another engine.");
873         return false;
874     }
875     m_engine = engine;
876     m_state = JSPrimitive;
877     u.m_value = value;
878     JSValueProtect(*m_engine, value);
879     return true;
880 }
881
882 inline QScriptValuePrivate* QScriptValuePrivate::property(const QString& name, const QScriptValue::ResolveFlags& mode)
883 {
884     JSRetainPtr<JSStringRef> propertyName(Adopt, QScriptConverter::toString(name));
885     return property<JSStringRef>(propertyName.get(), mode);
886 }
887
888 inline QScriptValuePrivate* QScriptValuePrivate::property(const QScriptStringPrivate* name, const QScriptValue::ResolveFlags& mode)
889 {
890     return property<JSStringRef>(*name, mode);
891 }
892
893 inline QScriptValuePrivate* QScriptValuePrivate::property(quint32 arrayIndex, const QScriptValue::ResolveFlags& mode)
894 {
895     return property<quint32>(arrayIndex, mode);
896 }
897
898 /*!
899     \internal
900     This method was created to unify access to the JSObjectGetPropertyAtIndex and the JSObjectGetProperty.
901 */
902 inline JSValueRef QScriptValuePrivate::property(quint32 property, JSValueRef* exception)
903 {
904     return JSObjectGetPropertyAtIndex(*m_engine, *this, property, exception);
905 }
906
907 /*!
908     \internal
909     This method was created to unify access to the JSObjectGetPropertyAtIndex and the JSObjectGetProperty.
910 */
911 inline JSValueRef QScriptValuePrivate::property(JSStringRef property, JSValueRef* exception)
912 {
913     return JSObjectGetProperty(*m_engine, *this, property, exception);
914 }
915
916 /*!
917     \internal
918     This method was created to unify acccess to hasOwnProperty, same function for an array index
919     and a property name access.
920 */
921 inline bool QScriptValuePrivate::hasOwnProperty(quint32 property)
922 {
923     Q_ASSERT(isObject());
924     // FIXME it could be faster, but JSC C API doesn't expose needed functionality.
925     JSRetainPtr<JSStringRef> propertyName(Adopt, QScriptConverter::toString(QString::number(property)));
926     return hasOwnProperty(propertyName.get());
927 }
928
929 /*!
930     \internal
931     This method was created to unify acccess to hasOwnProperty, same function for an array index
932     and a property name access.
933 */
934 inline bool QScriptValuePrivate::hasOwnProperty(JSStringRef property)
935 {
936     Q_ASSERT(isObject());
937     return m_engine->objectHasOwnProperty(*this, property);
938 }
939
940 /*!
941     \internal
942     This function gets property of an object.
943     \arg propertyName could be type of quint32 (an array index) or JSStringRef (a property name).
944 */
945 template<typename T>
946 inline QScriptValuePrivate* QScriptValuePrivate::property(T propertyName, const QScriptValue::ResolveFlags& mode)
947 {
948     if (!isObject())
949         return new QScriptValuePrivate();
950
951     if ((mode == QScriptValue::ResolveLocal) && (!hasOwnProperty(propertyName)))
952         return new QScriptValuePrivate();
953
954     JSValueRef exception = 0;
955     JSValueRef value = property(propertyName, &exception);
956
957     if (exception) {
958         m_engine->setException(exception, QScriptEnginePrivate::NotNullException);
959         return new QScriptValuePrivate(engine(), exception);
960     }
961     if (JSValueIsUndefined(*m_engine, value))
962         return new QScriptValuePrivate;
963     return new QScriptValuePrivate(engine(), value);
964 }
965
966 inline void QScriptValuePrivate::setProperty(const QString& name, QScriptValuePrivate* value, const QScriptValue::PropertyFlags& flags)
967 {
968     JSRetainPtr<JSStringRef> propertyName(Adopt, QScriptConverter::toString(name));
969     setProperty<JSStringRef>(propertyName.get(), value, flags);
970 }
971
972 inline void QScriptValuePrivate::setProperty(quint32 arrayIndex, QScriptValuePrivate* value, const QScriptValue::PropertyFlags& flags)
973 {
974     setProperty<quint32>(arrayIndex, value, flags);
975 }
976
977 inline void QScriptValuePrivate::setProperty(const QScriptStringPrivate* name, QScriptValuePrivate* value, const QScriptValue::PropertyFlags& flags)
978 {
979     setProperty<JSStringRef>(*name, value, flags);
980 }
981
982 /*!
983     \internal
984     This method was created to unify access to the JSObjectSetPropertyAtIndex and the JSObjectSetProperty.
985 */
986 inline void QScriptValuePrivate::setProperty(quint32 property, JSValueRef value, JSPropertyAttributes flags, JSValueRef* exception)
987 {
988     Q_ASSERT(isObject());
989     if (flags) {
990         // FIXME This could be better, but JSC C API doesn't expose needed functionality. It is
991         // not possible to create / modify a property attribute via an array index.
992         JSRetainPtr<JSStringRef> propertyName(Adopt, QScriptConverter::toString(QString::number(property)));
993         JSObjectSetProperty(*m_engine, *this, propertyName.get(), value, flags, exception);
994         return;
995     }
996     JSObjectSetPropertyAtIndex(*m_engine, *this, property, value, exception);
997 }
998
999 /*!
1000     \internal
1001     This method was created to unify access to the JSObjectSetPropertyAtIndex and the JSObjectSetProperty.
1002 */
1003 inline void QScriptValuePrivate::setProperty(JSStringRef property, JSValueRef value, JSPropertyAttributes flags, JSValueRef* exception)
1004 {
1005     Q_ASSERT(isObject());
1006     JSObjectSetProperty(*m_engine, *this, property, value, flags, exception);
1007 }
1008
1009 /*!
1010     \internal
1011     This method was created to unify access to the JSObjectDeleteProperty and a teoretical JSObjectDeletePropertyAtIndex
1012     which doesn't exist now.
1013 */
1014 inline void QScriptValuePrivate::deleteProperty(quint32 property, JSValueRef* exception)
1015 {
1016     // FIXME It could be faster, we need a JSC C API for deleting array index properties.
1017     JSRetainPtr<JSStringRef> propertyName(Adopt, QScriptConverter::toString(QString::number(property)));
1018     JSObjectDeleteProperty(*m_engine, *this, propertyName.get(), exception);
1019 }
1020
1021 /*!
1022     \internal
1023     This method was created to unify access to the JSObjectDeleteProperty and a teoretical JSObjectDeletePropertyAtIndex.
1024 */
1025 inline void QScriptValuePrivate::deleteProperty(JSStringRef property, JSValueRef* exception)
1026 {
1027     Q_ASSERT(isObject());
1028     JSObjectDeleteProperty(*m_engine, *this, property, exception);
1029 }
1030
1031 template<typename T>
1032 inline void QScriptValuePrivate::setProperty(T name, QScriptValuePrivate* value, const QScriptValue::PropertyFlags& flags)
1033 {
1034     if (!isObject())
1035         return;
1036
1037     if (!value->isJSBased())
1038         value->assignEngine(engine());
1039
1040     JSValueRef exception = 0;
1041     if (!value->isValid()) {
1042         // Remove the property.
1043         deleteProperty(name, &exception);
1044         m_engine->setException(exception);
1045         return;
1046     }
1047     if (m_engine != value->m_engine) {
1048         qWarning("QScriptValue::setProperty() failed: cannot set value created in a different engine");
1049         return;
1050     }
1051
1052     setProperty(name, *value, QScriptConverter::toPropertyFlags(flags), &exception);
1053     m_engine->setException(exception);
1054 }
1055
1056 inline QScriptValue::PropertyFlags QScriptValuePrivate::propertyFlags(const QString& name, const QScriptValue::ResolveFlags& mode)
1057 {
1058     JSRetainPtr<JSStringRef> propertyName(Adopt, QScriptConverter::toString(name));
1059     return propertyFlags(propertyName.get(), mode);
1060 }
1061
1062 inline QScriptValue::PropertyFlags QScriptValuePrivate::propertyFlags(const QScriptStringPrivate* name, const QScriptValue::ResolveFlags& mode)
1063 {
1064     return propertyFlags(*name, mode);
1065 }
1066
1067 inline QScriptValue::PropertyFlags QScriptValuePrivate::propertyFlags(JSStringRef name, const QScriptValue::ResolveFlags& mode)
1068 {
1069     unsigned flags = 0;
1070     if (!isObject())
1071         return QScriptValue::PropertyFlags(flags);
1072
1073     // FIXME It could be faster and nicer, but new JSC C API should be created.
1074     static JSStringRef objectName = QScriptConverter::toString("Object");
1075     static JSStringRef propertyDescriptorName = QScriptConverter::toString("getOwnPropertyDescriptor");
1076
1077     // FIXME This is dangerous if global object was modified (bug 41839).
1078     JSValueRef exception = 0;
1079     JSObjectRef globalObject = JSContextGetGlobalObject(*m_engine);
1080     JSValueRef objectConstructor = JSObjectGetProperty(*m_engine, globalObject, objectName, &exception);
1081     Q_ASSERT(JSValueIsObject(*m_engine, objectConstructor));
1082     JSValueRef propertyDescriptorGetter = JSObjectGetProperty(*m_engine, const_cast<JSObjectRef>(objectConstructor), propertyDescriptorName, &exception);
1083     Q_ASSERT(JSValueIsObject(*m_engine, propertyDescriptorGetter));
1084
1085     JSValueRef arguments[] = { *this, JSValueMakeString(*m_engine, name) };
1086     JSObjectRef propertyDescriptor
1087             = const_cast<JSObjectRef>(JSObjectCallAsFunction(*m_engine,
1088                                                             const_cast<JSObjectRef>(propertyDescriptorGetter),
1089                                                             /* thisObject */ 0,
1090                                                             /* argumentCount */ 2,
1091                                                             arguments,
1092                                                             &exception));
1093     if (exception) {
1094         // Invalid property.
1095         return QScriptValue::PropertyFlags(flags);
1096     }
1097
1098     if (!JSValueIsObject(*m_engine, propertyDescriptor)) {
1099         // Property isn't owned by this object.
1100         JSObjectRef proto;
1101         if (mode == QScriptValue::ResolveLocal
1102                 || ((proto = const_cast<JSObjectRef>(JSObjectGetPrototype(*m_engine, *this))) && JSValueIsNull(*m_engine, proto))) {
1103             return QScriptValue::PropertyFlags(flags);
1104         }
1105         QScriptValuePrivate p(engine(), proto);
1106         return p.propertyFlags(name, QScriptValue::ResolvePrototype);
1107     }
1108
1109     static JSStringRef writableName = QScriptConverter::toString("writable");
1110     static JSStringRef configurableName = QScriptConverter::toString("configurable");
1111     static JSStringRef enumerableName = QScriptConverter::toString("enumerable");
1112
1113     bool readOnly = !JSValueToBoolean(*m_engine, JSObjectGetProperty(*m_engine, propertyDescriptor, writableName, &exception));
1114     if (!exception && readOnly)
1115         flags |= QScriptValue::ReadOnly;
1116     bool undeletable = !JSValueToBoolean(*m_engine, JSObjectGetProperty(*m_engine, propertyDescriptor, configurableName, &exception));
1117     if (!exception && undeletable)
1118         flags |= QScriptValue::Undeletable;
1119     bool skipInEnum = !JSValueToBoolean(*m_engine, JSObjectGetProperty(*m_engine, propertyDescriptor, enumerableName, &exception));
1120     if (!exception && skipInEnum)
1121         flags |= QScriptValue::SkipInEnumeration;
1122
1123     return QScriptValue::PropertyFlags(flags);
1124 }
1125
1126 QScriptValuePrivate* QScriptValuePrivate::call(const QScriptValuePrivate*, const QScriptValueList& args)
1127 {
1128     switch (m_state) {
1129     case JSValue:
1130         if (refinedJSValue() != JSObject)
1131             return new QScriptValuePrivate;
1132         // Fall-through.
1133     case JSObject:
1134         {
1135             // Convert all arguments and bind to the engine.
1136             int argc = args.size();
1137             QVarLengthArray<JSValueRef, 8> argv(argc);
1138             QScriptValueList::const_iterator i = args.constBegin();
1139             for (int j = 0; i != args.constEnd(); j++, i++) {
1140                 QScriptValuePrivate* value = QScriptValuePrivate::get(*i);
1141                 if (!value->assignEngine(engine())) {
1142                     qWarning("QScriptValue::call() failed: cannot call function with values created in a different engine");
1143                     return new QScriptValuePrivate;
1144                 }
1145                 argv[j] = *value;
1146             }
1147
1148             // Make the call
1149             JSValueRef exception = 0;
1150             JSValueRef result = JSObjectCallAsFunction(*m_engine, *this, /* thisObject */ 0, argc, argv.constData(), &exception);
1151             if (!result && exception) {
1152                 m_engine->setException(exception);
1153                 return new QScriptValuePrivate(engine(), exception);
1154             }
1155             if (result && !exception)
1156                 return new QScriptValuePrivate(engine(), result);
1157         }
1158         // this QSV is not a function <-- !result && !exception. Fall-through.
1159     default:
1160         return new QScriptValuePrivate;
1161     }
1162 }
1163
1164 QScriptEnginePrivate* QScriptValuePrivate::engine() const
1165 {
1166     // As long as m_engine is an autoinitializated pointer we can safely return it without
1167     // checking current state.
1168     return m_engine.data();
1169 }
1170
1171 QScriptValuePrivate::operator JSValueRef() const
1172 {
1173     Q_ASSERT(isJSBased());
1174     Q_ASSERT(u.m_value);
1175     return u.m_value;
1176 }
1177
1178 QScriptValuePrivate::operator JSObjectRef() const
1179 {
1180     Q_ASSERT(m_state == JSObject);
1181     Q_ASSERT(u.m_object);
1182     return u.m_object;
1183 }
1184
1185 /*!
1186   \internal
1187   Refines the state of this QScriptValuePrivate. Returns the new state.
1188 */
1189 QScriptValuePrivate::State QScriptValuePrivate::refinedJSValue()
1190 {
1191     Q_ASSERT(m_state == JSValue);
1192     if (!JSValueIsObject(*m_engine, *this)) {
1193         m_state = JSPrimitive;
1194     } else {
1195         // Difference between JSValueRef and JSObjectRef is only in their type, binarywise it is the same.
1196         // As m_value and m_object are stored in the u union, it is enough to change the m_state only.
1197         m_state = JSObject;
1198     }
1199     return m_state;
1200 }
1201
1202 /*!
1203   \internal
1204   Returns true if QSV have an engine associated.
1205 */
1206 bool QScriptValuePrivate::isJSBased() const { return m_state >= JSValue; }
1207
1208 /*!
1209   \internal
1210   Returns true if current value of QSV is placed in m_number.
1211 */
1212 bool QScriptValuePrivate::isNumberBased() const { return m_state == CNumber || m_state == CBool; }
1213
1214 /*!
1215   \internal
1216   Returns true if current value of QSV is placed in m_string.
1217 */
1218 bool QScriptValuePrivate::isStringBased() const { return m_state == CString; }
1219
1220 #endif // qscriptvalue_p_h