]>
Commit | Line | Data |
---|---|---|
f9bf01c6 A |
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> | |
4e4e5a6f A |
27 | #include <JavaScriptCore/JSRetainPtr.h> |
28 | #include <QtCore/qmath.h> | |
29 | #include <QtCore/qnumeric.h> | |
f9bf01c6 A |
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 | |
4e4e5a6f | 42 | QScriptValuePrivate::State. Each method should check for the current state and then perform a |
f9bf01c6 A |
43 | correct action. |
44 | ||
4e4e5a6f | 45 | State: |
f9bf01c6 A |
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. | |
4e4e5a6f | 57 | JSPrimitive -> QSVP is associated with engine, and it is sure that it isn't a JavaScript object. |
f9bf01c6 A |
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 | ||
4e4e5a6f A |
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); | |
f9bf01c6 A |
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. | |
4e4e5a6f | 121 | enum State { |
f9bf01c6 A |
122 | Invalid = 0, |
123 | CString = 0x1000, | |
124 | CNumber, | |
125 | CBool, | |
126 | CSpecial, | |
127 | JSValue = 0x2000, // JS values are equal or higher then this value. | |
4e4e5a6f | 128 | JSPrimitive, |
f9bf01c6 A |
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*); | |
4e4e5a6f | 140 | inline State refinedJSValue(); |
f9bf01c6 A |
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 | ||
4e4e5a6f A |
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)) | |
f9bf01c6 | 217 | { |
4e4e5a6f A |
218 | Q_ASSERT(engine); |
219 | JSValueProtect(context(), m_value); | |
f9bf01c6 A |
220 | } |
221 | ||
4e4e5a6f A |
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)) | |
f9bf01c6 | 226 | { |
4e4e5a6f A |
227 | Q_ASSERT(engine); |
228 | JSValueProtect(context(), m_value); | |
f9bf01c6 A |
229 | } |
230 | ||
4e4e5a6f A |
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)) | |
f9bf01c6 | 235 | { |
4e4e5a6f A |
236 | Q_ASSERT(engine); |
237 | JSValueProtect(context(), m_value); | |
f9bf01c6 A |
238 | } |
239 | ||
4e4e5a6f A |
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)) | |
f9bf01c6 | 244 | { |
4e4e5a6f A |
245 | Q_ASSERT(engine); |
246 | JSValueProtect(context(), m_value); | |
f9bf01c6 A |
247 | } |
248 | ||
4e4e5a6f A |
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)) | |
f9bf01c6 | 253 | { |
4e4e5a6f A |
254 | Q_ASSERT(engine); |
255 | JSValueProtect(context(), m_value); | |
f9bf01c6 A |
256 | } |
257 | ||
4e4e5a6f A |
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)) | |
f9bf01c6 | 262 | { |
4e4e5a6f A |
263 | Q_ASSERT(engine); |
264 | JSValueProtect(context(), m_value); | |
f9bf01c6 A |
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); | |
4e4e5a6f | 273 | Q_ASSERT(value); |
f9bf01c6 A |
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); | |
4e4e5a6f A |
284 | Q_ASSERT(value); |
285 | Q_ASSERT(object); | |
f9bf01c6 A |
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: | |
4e4e5a6f | 297 | if (refinedJSValue() != JSPrimitive) |
f9bf01c6 A |
298 | return false; |
299 | // Fall-through. | |
4e4e5a6f | 300 | case JSPrimitive: |
f9bf01c6 A |
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: | |
4e4e5a6f | 311 | return true; |
f9bf01c6 | 312 | case JSValue: |
4e4e5a6f | 313 | if (refinedJSValue() != JSPrimitive) |
f9bf01c6 A |
314 | return false; |
315 | // Fall-through. | |
4e4e5a6f | 316 | case JSPrimitive: |
f9bf01c6 A |
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: | |
4e4e5a6f | 329 | if (refinedJSValue() != JSPrimitive) |
f9bf01c6 A |
330 | return false; |
331 | // Fall-through. | |
4e4e5a6f | 332 | case JSPrimitive: |
f9bf01c6 A |
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: | |
4e4e5a6f | 345 | if (refinedJSValue() != JSPrimitive) |
f9bf01c6 A |
346 | return false; |
347 | // Fall-through. | |
4e4e5a6f | 348 | case JSPrimitive: |
f9bf01c6 A |
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: | |
4e4e5a6f | 361 | if (refinedJSValue() != JSPrimitive) |
f9bf01c6 A |
362 | return false; |
363 | // Fall-through. | |
4e4e5a6f | 364 | case JSPrimitive: |
f9bf01c6 A |
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: | |
4e4e5a6f | 375 | if (refinedJSValue() != JSObject) |
f9bf01c6 A |
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) { | |
f9bf01c6 | 388 | case JSValue: |
4e4e5a6f A |
389 | return refinedJSValue() == JSObject; |
390 | case JSObject: | |
f9bf01c6 | 391 | return true; |
4e4e5a6f | 392 | |
f9bf01c6 A |
393 | default: |
394 | return false; | |
395 | } | |
396 | } | |
397 | ||
398 | bool QScriptValuePrivate::isFunction() | |
399 | { | |
400 | switch (m_state) { | |
401 | case JSValue: | |
4e4e5a6f | 402 | if (refinedJSValue() != JSObject) |
f9bf01c6 | 403 | return false; |
f9bf01c6 A |
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: | |
4e4e5a6f | 422 | return QScriptConverter::toString(m_number); |
f9bf01c6 A |
423 | case CSpecial: |
424 | return m_number == QScriptValue::NullValue ? QString::fromLatin1("null") : QString::fromLatin1("undefined"); | |
425 | case JSValue: | |
4e4e5a6f | 426 | case JSPrimitive: |
f9bf01c6 | 427 | case JSObject: |
4e4e5a6f A |
428 | JSRetainPtr<JSStringRef> ptr(Adopt, JSValueToStringCopy(context(), value(), /* exception */ 0)); |
429 | return QScriptConverter::toString(ptr.get()); | |
f9bf01c6 A |
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 | { | |
f9bf01c6 A |
438 | switch (m_state) { |
439 | case JSValue: | |
4e4e5a6f | 440 | case JSPrimitive: |
f9bf01c6 A |
441 | case JSObject: |
442 | return JSValueToNumber(context(), value(), /* exception */ 0); | |
443 | case CNumber: | |
f9bf01c6 | 444 | return m_number; |
4e4e5a6f A |
445 | case CBool: |
446 | return m_number ? 1 : 0; | |
f9bf01c6 | 447 | case Invalid: |
4e4e5a6f | 448 | return 0; |
f9bf01c6 | 449 | case CSpecial: |
4e4e5a6f | 450 | return m_number == QScriptValue::NullValue ? 0 : qQNaN(); |
f9bf01c6 | 451 | case CString: |
4e4e5a6f A |
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; | |
f9bf01c6 A |
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: | |
4e4e5a6f | 472 | case JSPrimitive: |
f9bf01c6 | 473 | return JSValueToBoolean(context(), value()); |
4e4e5a6f A |
474 | case JSObject: |
475 | return true; | |
f9bf01c6 | 476 | case CNumber: |
4e4e5a6f | 477 | return !(qIsNaN(m_number) || !m_number); |
f9bf01c6 A |
478 | case CBool: |
479 | return m_number; | |
480 | case Invalid: | |
481 | case CSpecial: | |
482 | return false; | |
483 | case CString: | |
4e4e5a6f | 484 | return m_string.length(); |
f9bf01c6 A |
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 | { | |
4e4e5a6f A |
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); | |
f9bf01c6 A |
499 | } |
500 | ||
501 | qint32 QScriptValuePrivate::toInt32() const | |
502 | { | |
4e4e5a6f A |
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; | |
f9bf01c6 A |
509 | } |
510 | ||
511 | quint32 QScriptValuePrivate::toUInt32() const | |
512 | { | |
4e4e5a6f A |
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; | |
f9bf01c6 A |
519 | } |
520 | ||
521 | quint16 QScriptValuePrivate::toUInt16() const | |
522 | { | |
4e4e5a6f | 523 | return toInt32(); |
f9bf01c6 A |
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; | |
4e4e5a6f | 599 | m_state = JSPrimitive; |
f9bf01c6 A |
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: | |
4e4e5a6f | 608 | if (refinedJSValue() != JSObject) |
f9bf01c6 | 609 | return new QScriptValuePrivate; |
f9bf01c6 A |
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()); | |
4e4e5a6f A |
683 | JSStringRef errorAttrName = QScriptConverter::toString(name); |
684 | JSValueRef error = JSObjectGetProperty(context(), globalObject, errorAttrName, /* exception */ 0); | |
685 | JSStringRelease(errorAttrName); | |
f9bf01c6 A |
686 | return JSValueIsInstanceOfConstructor(context(), value(), JSValueToObject(context(), error, /* exception */ 0), /* exception */ 0); |
687 | } | |
688 | ||
4e4e5a6f A |
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 | ||
f9bf01c6 A |
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 |