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