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