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