• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <QtCore/qmath.h>
28 #include <QtCore/qnumeric.h>
29 #include <QtCore/qshareddata.h>
30 #include <QtCore/qvarlengtharray.h>
31 
32 class QScriptEngine;
33 class QScriptValue;
34 
35 /*
36   \internal
37   \class QScriptValuePrivate
38 
39   Implementation of QScriptValue.
40   The implementation is based on a state machine. The states names are included in
41   QScriptValuePrivate::States. Each method should check for the current state and then perform a
42   correct action.
43 
44   States:
45     Invalid -> QSVP is invalid, no assumptions should be made about class members (apart from m_value).
46     CString -> QSVP is created from QString or const char* and no JSC engine has been associated yet.
47         Current value is kept in m_string,
48     CNumber -> QSVP is created from int, uint, double... and no JSC engine has been bind yet. Current
49         value is kept in m_number
50     CBool -> QSVP is created from bool and no JSC engine has been associated yet. Current value is kept
51         in m_number
52     CSpecial -> QSVP is Undefined or Null, but a JSC engine hasn't been associated yet, current value
53         is kept in m_number (cast of QScriptValue::SpecialValue)
54     JSValue -> QSVP is associated with engine, but there is no information about real type, the state
55         have really short live cycle. Normally it is created as a function call result.
56     JSNative -> QSVP is associated with engine, and it is sure that it isn't a JavaScript object.
57     JSObject -> QSVP is associated with engine, and it is sure that it is a JavaScript object.
58 
59   Each state keep all necessary information to invoke all methods, if not it should be changed to
60   a proper state. Changed state shouldn't be reverted.
61 */
62 
63 class QScriptValuePrivate : public QSharedData {
64 public:
65     inline static QScriptValuePrivate* get(const QScriptValue& q);
66     inline static QScriptValue get(const QScriptValuePrivate* d);
67     inline static QScriptValue get(QScriptValuePrivate* d);
68 
69     inline ~QScriptValuePrivate();
70 
71     inline QScriptValuePrivate();
72     inline QScriptValuePrivate(const QString& string);
73     inline QScriptValuePrivate(bool value);
74     inline QScriptValuePrivate(int number);
75     inline QScriptValuePrivate(uint number);
76     inline QScriptValuePrivate(qsreal number);
77     inline QScriptValuePrivate(QScriptValue::SpecialValue value);
78 
79     inline QScriptValuePrivate(const QScriptEngine* engine, bool value);
80     inline QScriptValuePrivate(const QScriptEngine* engine, int value);
81     inline QScriptValuePrivate(const QScriptEngine* engine, uint value);
82     inline QScriptValuePrivate(const QScriptEngine* engine, qsreal value);
83     inline QScriptValuePrivate(const QScriptEngine* engine, const QString& value);
84     inline QScriptValuePrivate(const QScriptEngine* engine, QScriptValue::SpecialValue value);
85 
86     inline QScriptValuePrivate(const QScriptEnginePrivate* engine, JSValueRef value);
87     inline QScriptValuePrivate(const QScriptEnginePrivate* engine, JSValueRef value, JSObjectRef object);
88 
89     inline bool isValid() const;
90     inline bool isBool();
91     inline bool isNumber();
92     inline bool isNull();
93     inline bool isString();
94     inline bool isUndefined();
95     inline bool isError();
96     inline bool isObject();
97     inline bool isFunction();
98 
99     inline QString toString() const;
100     inline qsreal toNumber() const;
101     inline bool toBool() const;
102     inline qsreal toInteger() const;
103     inline qint32 toInt32() const;
104     inline quint32 toUInt32() const;
105     inline quint16 toUInt16() const;
106 
107     inline bool equals(QScriptValuePrivate* other);
108     inline bool strictlyEquals(const QScriptValuePrivate* other) const;
109     inline bool assignEngine(QScriptEnginePrivate* engine);
110 
111     inline QScriptValuePrivate* call(const QScriptValuePrivate* , const QScriptValueList& args);
112 
113     inline JSGlobalContextRef context() const;
114     inline JSValueRef value() const;
115     inline JSObjectRef object() const;
116     inline QScriptEnginePrivate* engine() const;
117 
118 private:
119     // Please, update class documentation when you change the enum.
120     enum States {
121         Invalid = 0,
122         CString = 0x1000,
123         CNumber,
124         CBool,
125         CSpecial,
126         JSValue = 0x2000, // JS values are equal or higher then this value.
127         JSNative,
128         JSObject
129     } m_state;
130     QScriptEnginePtr m_engine;
131     QString m_string;
132     qsreal m_number;
133     JSValueRef m_value;
134     JSObjectRef m_object;
135 
136     inline void setValue(JSValueRef);
137 
138     inline bool inherits(const char*);
139 
140     inline bool isJSBased() const;
141     inline bool isNumberBased() const;
142     inline bool isStringBased() const;
143 };
144 
get(const QScriptValue & q)145 QScriptValuePrivate* QScriptValuePrivate::get(const QScriptValue& q) { return q.d_ptr.data(); }
146 
get(const QScriptValuePrivate * d)147 QScriptValue QScriptValuePrivate::get(const QScriptValuePrivate* d)
148 {
149     return QScriptValue(const_cast<QScriptValuePrivate*>(d));
150 }
151 
get(QScriptValuePrivate * d)152 QScriptValue QScriptValuePrivate::get(QScriptValuePrivate* d)
153 {
154     return QScriptValue(d);
155 }
156 
~QScriptValuePrivate()157 QScriptValuePrivate::~QScriptValuePrivate()
158 {
159     if (m_value)
160         JSValueUnprotect(context(), m_value);
161 }
162 
QScriptValuePrivate()163 QScriptValuePrivate::QScriptValuePrivate()
164     : m_state(Invalid)
165     , m_value(0)
166 {
167 }
168 
QScriptValuePrivate(const QString & string)169 QScriptValuePrivate::QScriptValuePrivate(const QString& string)
170     : m_state(CString)
171     , m_string(string)
172     , m_value(0)
173 {
174 }
175 
QScriptValuePrivate(bool value)176 QScriptValuePrivate::QScriptValuePrivate(bool value)
177     : m_state(CBool)
178     , m_number(value)
179     , m_value(0)
180 {
181 }
182 
QScriptValuePrivate(int number)183 QScriptValuePrivate::QScriptValuePrivate(int number)
184     : m_state(CNumber)
185     , m_number(number)
186     , m_value(0)
187 {
188 }
189 
QScriptValuePrivate(uint number)190 QScriptValuePrivate::QScriptValuePrivate(uint number)
191     : m_state(CNumber)
192     , m_number(number)
193     , m_value(0)
194 {
195 }
196 
QScriptValuePrivate(qsreal number)197 QScriptValuePrivate::QScriptValuePrivate(qsreal number)
198     : m_state(CNumber)
199     , m_number(number)
200     , m_value(0)
201 {
202 }
203 
QScriptValuePrivate(QScriptValue::SpecialValue value)204 QScriptValuePrivate::QScriptValuePrivate(QScriptValue::SpecialValue value)
205     : m_state(CSpecial)
206     , m_number(value)
207     , m_value(0)
208 {
209 }
210 
QScriptValuePrivate(const QScriptEngine * engine,bool value)211 QScriptValuePrivate::QScriptValuePrivate(const QScriptEngine* engine, bool value)
212     : m_state(JSNative)
213 {
214     if (!engine) {
215         // slower path reinitialization
216         m_state = CBool;
217         m_number = value;
218         m_value = 0;
219     } else {
220         m_engine = QScriptEnginePrivate::get(engine);
221         m_value = m_engine->makeJSValue(value);
222         JSValueProtect(context(), m_value);
223     }
224 }
225 
QScriptValuePrivate(const QScriptEngine * engine,int value)226 QScriptValuePrivate::QScriptValuePrivate(const QScriptEngine* engine, int value)
227     : m_state(JSNative)
228 {
229     if (!engine) {
230         // slower path reinitialization
231         m_state = CNumber;
232         m_number = value;
233         m_value = 0;
234     } else {
235         m_engine = QScriptEnginePrivate::get(engine);
236         m_value = m_engine->makeJSValue(value);
237         JSValueProtect(context(), m_value);
238     }
239 }
240 
QScriptValuePrivate(const QScriptEngine * engine,uint value)241 QScriptValuePrivate::QScriptValuePrivate(const QScriptEngine* engine, uint value)
242     : m_state(JSNative)
243 {
244     if (!engine) {
245         // slower path reinitialization
246         m_state = CNumber;
247         m_number = value;
248         m_value = 0;
249     } else {
250         m_engine = QScriptEnginePrivate::get(engine);
251         m_value = m_engine->makeJSValue(value);
252         JSValueProtect(context(), m_value);
253     }
254 }
255 
QScriptValuePrivate(const QScriptEngine * engine,qsreal value)256 QScriptValuePrivate::QScriptValuePrivate(const QScriptEngine* engine, qsreal value)
257     : m_state(JSNative)
258 {
259     if (!engine) {
260         // slower path reinitialization
261         m_state = CNumber;
262         m_number = value;
263         m_value = 0;
264     } else {
265         m_engine = QScriptEnginePrivate::get(engine);
266         m_value = m_engine->makeJSValue(value);
267         JSValueProtect(context(), m_value);
268     }
269 }
270 
QScriptValuePrivate(const QScriptEngine * engine,const QString & value)271 QScriptValuePrivate::QScriptValuePrivate(const QScriptEngine* engine, const QString& value)
272     : m_state(JSNative)
273 {
274     if (!engine) {
275         // slower path reinitialization
276         m_state = CString;
277         m_string = value;
278         m_value = 0;
279     } else {
280         m_engine = QScriptEnginePrivate::get(engine);
281         m_value = m_engine->makeJSValue(value);
282         JSValueProtect(context(), m_value);
283     }
284 }
285 
QScriptValuePrivate(const QScriptEngine * engine,QScriptValue::SpecialValue value)286 QScriptValuePrivate::QScriptValuePrivate(const QScriptEngine* engine, QScriptValue::SpecialValue value)
287     : m_state(JSNative)
288 {
289     if (!engine) {
290         // slower path reinitialization
291         m_state = CSpecial;
292         m_number = value;
293         m_value = 0;
294     } else {
295         m_engine = QScriptEnginePrivate::get(engine);
296         m_value = m_engine->makeJSValue(value);
297         JSValueProtect(context(), m_value);
298     }
299 }
300 
QScriptValuePrivate(const QScriptEnginePrivate * engine,JSValueRef value)301 QScriptValuePrivate::QScriptValuePrivate(const QScriptEnginePrivate* engine, JSValueRef value)
302     : m_state(JSValue)
303     , m_engine(const_cast<QScriptEnginePrivate*>(engine))
304     , m_value(value)
305 {
306     Q_ASSERT(engine);
307     JSValueProtect(context(), m_value);
308 }
309 
QScriptValuePrivate(const QScriptEnginePrivate * engine,JSValueRef value,JSObjectRef object)310 QScriptValuePrivate::QScriptValuePrivate(const QScriptEnginePrivate* engine, JSValueRef value, JSObjectRef object)
311     : m_state(JSObject)
312     , m_engine(const_cast<QScriptEnginePrivate*>(engine))
313     , m_value(value)
314     , m_object(object)
315 {
316     Q_ASSERT(engine);
317     JSValueProtect(context(), m_value);
318 }
319 
isValid()320 bool QScriptValuePrivate::isValid() const { return m_state != Invalid; }
321 
isBool()322 bool QScriptValuePrivate::isBool()
323 {
324     switch (m_state) {
325     case CBool:
326         return true;
327     case JSValue:
328         if (isObject())
329             return false;
330         // Fall-through.
331     case JSNative:
332         return JSValueIsBoolean(context(), value());
333     default:
334         return false;
335     }
336 }
337 
isNumber()338 bool QScriptValuePrivate::isNumber()
339 {
340     switch (m_state) {
341     case CNumber:
342         return true;
343     case JSValue:
344         if (isObject())
345             return false;
346         // Fall-through.
347     case JSNative:
348         return JSValueIsNumber(context(), value());
349     default:
350         return false;
351     }
352 }
353 
isNull()354 bool QScriptValuePrivate::isNull()
355 {
356     switch (m_state) {
357     case CSpecial:
358         return m_number == static_cast<int>(QScriptValue::NullValue);
359     case JSValue:
360         if (isObject())
361             return false;
362         // Fall-through.
363     case JSNative:
364         return JSValueIsNull(context(), value());
365     default:
366         return false;
367     }
368 }
369 
isString()370 bool QScriptValuePrivate::isString()
371 {
372     switch (m_state) {
373     case CString:
374         return true;
375     case JSValue:
376         if (isObject())
377             return false;
378         // Fall-through.
379     case JSNative:
380         return JSValueIsString(context(), value());
381     default:
382         return false;
383     }
384 }
385 
isUndefined()386 bool QScriptValuePrivate::isUndefined()
387 {
388     switch (m_state) {
389     case CSpecial:
390         return m_number == static_cast<int>(QScriptValue::UndefinedValue);
391     case JSValue:
392         if (isObject())
393             return false;
394         // Fall-through.
395     case JSNative:
396         return JSValueIsUndefined(context(), value());
397     default:
398         return false;
399     }
400 }
401 
isError()402 bool QScriptValuePrivate::isError()
403 {
404     switch (m_state) {
405     case JSValue:
406         if (!isObject())
407             return false;
408         // Fall-through.
409     case JSObject:
410         return inherits("Error");
411     default:
412         return false;
413     }
414 }
415 
isObject()416 bool QScriptValuePrivate::isObject()
417 {
418     switch (m_state) {
419     case JSObject:
420         return true;
421     case JSValue:
422         m_object = JSValueToObject(context(), value(), /* exception */ 0);
423         if (!m_object)
424             return false;
425         m_state = JSObject;
426         return true;
427     default:
428         return false;
429     }
430 }
431 
isFunction()432 bool QScriptValuePrivate::isFunction()
433 {
434     switch (m_state) {
435     case JSValue:
436         m_object = JSValueToObject(context(), value(), /* exception */ 0);
437         if (!m_object)
438             return false;
439         m_state = JSObject;
440         // Fall-through.
441     case JSObject:
442         return JSObjectIsFunction(context(), object());
443     default:
444         return false;
445     }
446 }
447 
toString()448 QString QScriptValuePrivate::toString() const
449 {
450     switch (m_state) {
451     case Invalid:
452         return QString();
453     case CBool:
454         return m_number ? QString::fromLatin1("true") : QString::fromLatin1("false");
455     case CString:
456         return m_string;
457     case CNumber:
458         return QString::number(m_number);
459     case CSpecial:
460         return m_number == QScriptValue::NullValue ? QString::fromLatin1("null") : QString::fromLatin1("undefined");
461     case JSValue:
462     case JSNative:
463     case JSObject:
464         return QScriptConverter::toString(JSValueToStringCopy(context(), value(), /* exception */ 0));
465     }
466 
467     Q_ASSERT_X(false, "toString()", "Not all states are included in the previous switch statement.");
468     return QString(); // Avoid compiler warning.
469 }
470 
toNumber()471 qsreal QScriptValuePrivate::toNumber() const
472 {
473     switch (m_state) {
474     case JSValue:
475     case JSNative:
476     case JSObject:
477         return JSValueToNumber(context(), value(), /* exception */ 0);
478     case CNumber:
479         return m_number;
480     case CBool:
481         return m_number ? 1 : 0;
482     case Invalid:
483         return 0;
484     case CSpecial:
485         return m_number == QScriptValue::NullValue ? 0 : qQNaN();
486     case CString:
487         bool ok;
488         qsreal result = m_string.toDouble(&ok);
489         if (ok)
490             return result;
491         result = m_string.toInt(&ok, 0); // Try other bases.
492         if (ok)
493             return result;
494         if (m_string == "Infinity" || m_string == "-Infinity")
495             return qInf();
496         return m_string.length() ? qQNaN() : 0;
497     }
498 
499     Q_ASSERT_X(false, "toNumber()", "Not all states are included in the previous switch statement.");
500     return 0; // Avoid compiler warning.
501 }
502 
toBool()503 bool QScriptValuePrivate::toBool() const
504 {
505     switch (m_state) {
506     case JSValue:
507     case JSNative:
508         return JSValueToBoolean(context(), value());
509     case JSObject:
510         return true;
511     case CNumber:
512         return !(qIsNaN(m_number) || !m_number);
513     case CBool:
514         return m_number;
515     case Invalid:
516     case CSpecial:
517         return false;
518     case CString:
519         return m_string.length();
520     }
521 
522     Q_ASSERT_X(false, "toBool()", "Not all states are included in the previous switch statement.");
523     return false; // Avoid compiler warning.
524 }
525 
toInteger()526 qsreal QScriptValuePrivate::toInteger() const
527 {
528     // TODO it is not true implementation!
529     return toNumber();
530 }
531 
toInt32()532 qint32 QScriptValuePrivate::toInt32() const
533 {
534     // TODO it is not true implementation!
535     return toNumber();
536 }
537 
toUInt32()538 quint32 QScriptValuePrivate::toUInt32() const
539 {
540     // TODO it is not true implementation!
541     return toNumber();
542 }
543 
toUInt16()544 quint16 QScriptValuePrivate::toUInt16() const
545 {
546     // TODO it is not true implementation!
547     return toNumber();
548 }
549 
550 
equals(QScriptValuePrivate * other)551 bool QScriptValuePrivate::equals(QScriptValuePrivate* other)
552 {
553     if (!isValid() || !other->isValid())
554         return false;
555 
556     if ((m_state == other->m_state) && !isJSBased()) {
557         if (isNumberBased())
558             return m_number == other->m_number;
559         return m_string == other->m_string;
560     }
561 
562     if (isJSBased() && !other->isJSBased()) {
563         if (!other->assignEngine(engine())) {
564             qWarning("equals(): Cannot compare to a value created in a different engine");
565             return false;
566         }
567     } else if (!isJSBased() && other->isJSBased()) {
568         if (!other->assignEngine(other->engine())) {
569             qWarning("equals(): Cannot compare to a value created in a different engine");
570             return false;
571         }
572     }
573 
574     return JSValueIsEqual(context(), value(), other->value(), /* exception */ 0);
575 }
576 
strictlyEquals(const QScriptValuePrivate * other)577 bool QScriptValuePrivate::strictlyEquals(const QScriptValuePrivate* other) const
578 {
579     if (m_state != other->m_state)
580         return false;
581     if (isJSBased()) {
582         if (other->engine() != engine()) {
583             qWarning("strictlyEquals(): Cannot compare to a value created in a different engine");
584             return false;
585         }
586         return JSValueIsStrictEqual(context(), value(), other->value());
587     }
588     if (isStringBased())
589         return m_string == other->m_string;
590     if (isNumberBased())
591         return m_number == other->m_number;
592 
593     return false; // Invalid state.
594 }
595 
596 /*!
597   Tries to assign \a engine to this value. Returns true on success; otherwise returns false.
598 */
assignEngine(QScriptEnginePrivate * engine)599 bool QScriptValuePrivate::assignEngine(QScriptEnginePrivate* engine)
600 {
601     JSValueRef value;
602     switch (m_state) {
603     case CBool:
604         value = engine->makeJSValue(static_cast<bool>(m_number));
605         break;
606     case CString:
607         value = engine->makeJSValue(m_string);
608         break;
609     case CNumber:
610         value = engine->makeJSValue(m_number);
611         break;
612     case CSpecial:
613         value = engine->makeJSValue(static_cast<QScriptValue::SpecialValue>(m_number));
614         break;
615     default:
616         if (!isJSBased())
617             Q_ASSERT_X(!isJSBased(), "assignEngine()", "Not all states are included in the previous switch statement.");
618         else
619             qWarning("JSValue can't be rassigned to an another engine.");
620         return false;
621     }
622     m_engine = engine;
623     m_state = JSNative;
624     setValue(value);
625     return true;
626 }
627 
call(const QScriptValuePrivate *,const QScriptValueList & args)628 QScriptValuePrivate* QScriptValuePrivate::call(const QScriptValuePrivate*, const QScriptValueList& args)
629 {
630     switch (m_state) {
631     case JSValue:
632         m_object = JSValueToObject(context(), value(), /* exception */ 0);
633         if (!object()) {
634             m_state = JSValue;
635             return new QScriptValuePrivate;
636         }
637         m_state = JSObject;
638         // Fall-through.
639     case JSObject:
640         {
641             // Convert all arguments and bind to the engine.
642             int argc = args.size();
643             QVarLengthArray<JSValueRef, 8> argv(argc);
644             QScriptValueList::const_iterator i = args.constBegin();
645             for (int j = 0; i != args.constEnd(); j++, i++) {
646                 QScriptValuePrivate* value = QScriptValuePrivate::get(*i);
647                 if (!value->assignEngine(engine())) {
648                     qWarning("QScriptValue::call() failed: cannot call function with values created in a different engine");
649                     return new QScriptValuePrivate;
650                 }
651                 argv[j] = value->value();
652             }
653 
654             // Make the call
655             JSValueRef exception = 0;
656             JSValueRef result = JSObjectCallAsFunction(context(), object(), /* thisObject */ 0, argc, argv.constData(), &exception);
657             if (!result && exception)
658                 return new QScriptValuePrivate(engine(), exception);
659             if (result && !exception)
660                 return new QScriptValuePrivate(engine(), result);
661         }
662         // this QSV is not a function <-- !result && !exception. Fall-through.
663     default:
664         return new QScriptValuePrivate;
665     }
666 }
667 
engine()668 QScriptEnginePrivate* QScriptValuePrivate::engine() const
669 {
670     // As long as m_engine is an autoinitializated pointer we can safely return it without
671     // checking current state.
672     return m_engine.data();
673 }
674 
context()675 JSGlobalContextRef QScriptValuePrivate::context() const
676 {
677     Q_ASSERT(isJSBased());
678     return m_engine->context();
679 }
680 
value()681 JSValueRef QScriptValuePrivate::value() const
682 {
683     Q_ASSERT(isJSBased());
684     return m_value;
685 }
686 
object()687 JSObjectRef QScriptValuePrivate::object() const
688 {
689     Q_ASSERT(m_state == JSObject);
690     return m_object;
691 }
692 
setValue(JSValueRef value)693 void QScriptValuePrivate::setValue(JSValueRef value)
694 {
695     if (m_value)
696         JSValueUnprotect(context(), m_value);
697     if (value)
698         JSValueProtect(context(), value);
699     m_value = value;
700 }
701 
702 /*!
703   \internal
704   Returns true if QSV is created from constructor with the given \a name, it has to be a
705   built-in type.
706 */
inherits(const char * name)707 bool QScriptValuePrivate::inherits(const char* name)
708 {
709     Q_ASSERT(isJSBased());
710     JSObjectRef globalObject = JSContextGetGlobalObject(context());
711     JSValueRef error = JSObjectGetProperty(context(), globalObject, QScriptConverter::toString(name), 0);
712     return JSValueIsInstanceOfConstructor(context(), value(), JSValueToObject(context(), error, /* exception */ 0), /* exception */ 0);
713 }
714 
715 /*!
716   \internal
717   Returns true if QSV have an engine associated.
718 */
isJSBased()719 bool QScriptValuePrivate::isJSBased() const { return m_state >= JSValue; }
720 
721 /*!
722   \internal
723   Returns true if current value of QSV is placed in m_number.
724 */
isNumberBased()725 bool QScriptValuePrivate::isNumberBased() const { return !isJSBased() && !isStringBased() && m_state != Invalid; }
726 
727 /*!
728   \internal
729   Returns true if current value of QSV is placed in m_string.
730 */
isStringBased()731 bool QScriptValuePrivate::isStringBased() const { return m_state == CString; }
732 
733 #endif // qscriptvalue_p_h
734