1 /****************************************************************************
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the QtScript module of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL-ONLY$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser
12 ** General Public License version 2.1 as published by the Free Software
13 ** Foundation and appearing in the file LICENSE.LGPL included in the
14 ** packaging of this file. Please review the following information to
15 ** ensure the GNU Lesser General Public License version 2.1 requirements
16 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 ** If you have questions regarding the use of this file, please contact
19 ** Nokia at qt-info@nokia.com.
22 ****************************************************************************/
28 // This file is not part of the Qt API. It exists purely as an
29 // implementation detail. This header file may change from version to
30 // version without notice, or even be removed.
35 #ifndef QJSVALUE_IMPL_P_H
36 #define QJSVALUE_IMPL_P_H
38 #include "qjsconverter_p.h"
39 #include "qjsvalue_p.h"
40 #include "qv8engine_p.h"
41 #include "qscriptisolate_p.h"
45 // This template is used indirectly by the Q_GLOBAL_STATIC macro below
47 class QGlobalStaticDeleter<QJSValuePrivate>
50 QGlobalStatic<QJSValuePrivate> &globalStatic;
51 QGlobalStaticDeleter(QGlobalStatic<QJSValuePrivate> &_globalStatic)
52 : globalStatic(_globalStatic)
54 globalStatic.pointer->ref.ref();
57 inline ~QGlobalStaticDeleter()
59 if (!globalStatic.pointer->ref.deref()) { // Logic copy & paste from SharedDataPointer
60 delete globalStatic.pointer;
62 globalStatic.pointer = 0;
63 globalStatic.destroyed = true;
67 Q_GLOBAL_STATIC(QJSValuePrivate, InvalidValue)
69 QJSValuePrivate* QJSValuePrivate::get(const QJSValue& q) { Q_ASSERT(q.d_ptr.data()); return q.d_ptr.data(); }
71 QJSValue QJSValuePrivate::get(const QJSValuePrivate* d)
74 return QJSValue(const_cast<QJSValuePrivate*>(d));
77 QJSValue QJSValuePrivate::get(QScriptPassPointer<QJSValuePrivate> d)
83 QJSValue QJSValuePrivate::get(QJSValuePrivate* d)
89 QJSValuePrivate::QJSValuePrivate()
90 : m_engine(0), m_state(Invalid)
94 QJSValuePrivate::QJSValuePrivate(bool value)
95 : m_engine(0), m_state(CBool), u(value)
99 QJSValuePrivate::QJSValuePrivate(int value)
100 : m_engine(0), m_state(CNumber), u(value)
104 QJSValuePrivate::QJSValuePrivate(uint value)
105 : m_engine(0), m_state(CNumber), u(value)
109 QJSValuePrivate::QJSValuePrivate(double value)
110 : m_engine(0), m_state(CNumber), u(value)
114 QJSValuePrivate::QJSValuePrivate(const QString& value)
115 : m_engine(0), m_state(CString), u(new QString(value))
119 QJSValuePrivate::QJSValuePrivate(QJSValue::SpecialValue value)
120 : m_engine(0), m_state(value == QJSValue::NullValue ? CNull : CUndefined)
124 QJSValuePrivate::QJSValuePrivate(QV8Engine* engine, bool value)
125 : m_engine(engine), m_state(JSValue)
128 v8::HandleScope handleScope;
129 m_value = v8::Persistent<v8::Value>::New(m_engine->makeJSValue(value));
130 m_engine->registerValue(this);
133 QJSValuePrivate::QJSValuePrivate(QV8Engine* engine, int value)
134 : m_engine(engine), m_state(JSValue)
137 v8::HandleScope handleScope;
138 m_value = v8::Persistent<v8::Value>::New(m_engine->makeJSValue(value));
139 m_engine->registerValue(this);
142 QJSValuePrivate::QJSValuePrivate(QV8Engine* engine, uint value)
143 : m_engine(engine), m_state(JSValue)
146 v8::HandleScope handleScope;
147 m_value = v8::Persistent<v8::Value>::New(m_engine->makeJSValue(value));
148 m_engine->registerValue(this);
151 QJSValuePrivate::QJSValuePrivate(QV8Engine* engine, double value)
152 : m_engine(engine), m_state(JSValue)
155 v8::HandleScope handleScope;
156 m_value = v8::Persistent<v8::Value>::New(m_engine->makeJSValue(value));
157 m_engine->registerValue(this);
160 QJSValuePrivate::QJSValuePrivate(QV8Engine* engine, const QString& value)
161 : m_engine(engine), m_state(JSValue)
164 v8::HandleScope handleScope;
165 m_value = v8::Persistent<v8::Value>::New(m_engine->makeJSValue(value));
166 m_engine->registerValue(this);
169 QJSValuePrivate::QJSValuePrivate(QV8Engine* engine, QJSValue::SpecialValue value)
170 : m_engine(engine), m_state(JSValue)
173 v8::HandleScope handleScope;
174 m_value = v8::Persistent<v8::Value>::New(m_engine->makeJSValue(value));
175 m_engine->registerValue(this);
178 QJSValuePrivate::QJSValuePrivate(QV8Engine *engine, v8::Handle<v8::Value> value)
179 : m_engine(engine), m_state(JSValue), m_value(v8::Persistent<v8::Value>::New(value))
182 // It shouldn't happen, v8 shows errors by returning an empty handler. This is important debug
183 // information and it can't be simply ignored.
184 Q_ASSERT(!value.IsEmpty());
185 m_engine->registerValue(this);
188 QJSValuePrivate::~QJSValuePrivate()
191 m_engine->unregisterValue(this);
192 QScriptIsolate api(m_engine);
194 } else if (isStringBased()) {
199 bool QJSValuePrivate::toBool() const
204 v8::HandleScope scope;
205 return m_value->ToBoolean()->Value();
208 return !(qIsNaN(u.m_number) || !u.m_number);
216 return u.m_string->length();
219 Q_ASSERT_X(false, "toBool()", "Not all states are included in the previous switch statement.");
220 return false; // Avoid compiler warning.
223 double QJSValuePrivate::toNumber() const
228 v8::HandleScope scope;
229 return m_value->ToNumber()->Value();
234 return u.m_bool ? 1 : 0;
242 double result = u.m_string->toDouble(&ok);
245 result = u.m_string->toInt(&ok, 0); // Try other bases.
248 if (*u.m_string == QLatin1String("Infinity"))
250 if (*u.m_string == QLatin1String("-Infinity"))
252 return u.m_string->length() ? qQNaN() : 0;
255 Q_ASSERT_X(false, "toNumber()", "Not all states are included in the previous switch statement.");
256 return 0; // Avoid compiler warning.
259 QScriptPassPointer<QJSValuePrivate> QJSValuePrivate::toObject(QV8Engine* engine) const
262 if (this->engine() && engine != this->engine()) {
263 qWarning("QJSEngine::toObject: cannot convert value created in a different engine");
264 return InvalidValue();
267 v8::HandleScope scope;
272 return new QJSValuePrivate;
274 return new QJSValuePrivate(engine, engine->makeJSValue(*u.m_string)->ToObject());
276 return new QJSValuePrivate(engine, engine->makeJSValue(u.m_number)->ToObject());
278 return new QJSValuePrivate(engine, engine->makeJSValue(u.m_bool)->ToObject());
280 if (m_value->IsObject())
281 return const_cast<QJSValuePrivate*>(this);
282 if (m_value->IsNull() || m_value->IsUndefined()) // avoid "Uncaught TypeError: Cannot convert null to object"
283 return InvalidValue();
284 return new QJSValuePrivate(engine, m_value->ToObject());
286 Q_ASSERT_X(false, Q_FUNC_INFO, "Not all states are included in this switch");
287 return InvalidValue();
292 This method is created only for QJSValue::toObject() purpose which is obsolete.
295 QScriptPassPointer<QJSValuePrivate> QJSValuePrivate::toObject() const
298 return toObject(engine());
300 // Without an engine there is not much we can do.
301 return new QJSValuePrivate;
304 QString QJSValuePrivate::toString() const
310 return u.m_bool ? QString::fromLatin1("true") : QString::fromLatin1("false");
314 return QJSConverter::toString(u.m_number);
316 return QString::fromLatin1("null");
318 return QString::fromLatin1("undefined");
320 Q_ASSERT(!m_value.IsEmpty());
321 v8::HandleScope handleScope;
322 v8::TryCatch tryCatch;
323 v8::Local<v8::String> result = m_value->ToString();
324 if (result.IsEmpty()) {
325 result = tryCatch.Exception()->ToString();
326 m_engine->setException(tryCatch.Exception(), tryCatch.Message());
328 return QJSConverter::toString(result);
331 Q_ASSERT_X(false, "toString()", "Not all states are included in the previous switch statement.");
332 return QString(); // Avoid compiler warning.
335 QVariant QJSValuePrivate::toVariant() const
341 return QVariant(u.m_bool);
343 return QVariant(*u.m_string);
345 return QVariant(u.m_number);
354 Q_ASSERT(m_state == JSValue);
355 Q_ASSERT(!m_value.IsEmpty());
358 v8::HandleScope handleScope;
359 return m_engine->variantFromJS(m_value);
362 inline QDateTime QJSValuePrivate::toDataTime() const
367 v8::HandleScope handleScope;
368 return QJSConverter::toDateTime(v8::Handle<v8::Date>::Cast(m_value));
372 inline QRegExp QJSValuePrivate::toRegExp() const
377 v8::HandleScope handleScope;
378 return QJSConverter::toRegExp(v8::Handle<v8::RegExp>::Cast(m_value));
381 QObject* QJSValuePrivate::toQObject() const
386 v8::HandleScope handleScope;
387 return engine()->qtObjectFromJS(m_value);
390 double QJSValuePrivate::toInteger() const
392 double result = toNumber();
397 return (result > 0) ? qFloor(result) : -1 * qFloor(-result);
400 qint32 QJSValuePrivate::toInt32() const
402 double result = toInteger();
403 // Orginaly it should look like that (result == 0 || qIsInf(result) || qIsNaN(result)), but
404 // some of these operation are invoked in toInteger subcall.
410 quint32 QJSValuePrivate::toUInt32() const
412 double result = toInteger();
413 // Orginaly it should look like that (result == 0 || qIsInf(result) || qIsNaN(result)), but
414 // some of these operation are invoked in toInteger subcall.
420 quint16 QJSValuePrivate::toUInt16() const
425 inline bool QJSValuePrivate::isArray() const
427 return isJSBased() && m_value->IsArray();
430 inline bool QJSValuePrivate::isBool() const
432 return m_state == CBool || (isJSBased() && m_value->IsBoolean());
435 inline bool QJSValuePrivate::isCallable() const
440 // Our C++ wrappers register function handlers but not always act as callables.
441 return v8::Object::Cast(*m_value)->IsCallable();
446 inline bool QJSValuePrivate::isError() const
450 v8::HandleScope handleScope;
451 return m_value->IsError();
454 inline bool QJSValuePrivate::isFunction() const
456 return isJSBased() && m_value->IsFunction();
459 inline bool QJSValuePrivate::isNull() const
461 return m_state == CNull || (isJSBased() && m_value->IsNull());
464 inline bool QJSValuePrivate::isNumber() const
466 return m_state == CNumber || (isJSBased() && m_value->IsNumber());
469 inline bool QJSValuePrivate::isObject() const
471 return isJSBased() && m_value->IsObject();
474 inline bool QJSValuePrivate::isString() const
476 return m_state == CString || (isJSBased() && m_value->IsString());
479 inline bool QJSValuePrivate::isUndefined() const
481 return m_state == CUndefined || (isJSBased() && m_value->IsUndefined());
484 inline bool QJSValuePrivate::isValid() const
486 return m_state != Invalid;
489 inline bool QJSValuePrivate::isVariant() const
491 return isJSBased() && m_engine->isVariant(m_value);
494 bool QJSValuePrivate::isDate() const
496 return (isJSBased() && m_value->IsDate());
499 bool QJSValuePrivate::isRegExp() const
501 return (isJSBased() && m_value->IsRegExp());
504 bool QJSValuePrivate::isQObject() const
506 return isJSBased() && engine()->isQObject(m_value);
509 inline bool QJSValuePrivate::equals(QJSValuePrivate* other)
512 return !other->isValid();
514 if (!other->isValid())
517 if (!isJSBased() && !other->isJSBased()) {
521 return other->isUndefined() || other->isNull();
523 switch (other->m_state) {
526 return u.m_number == other->toNumber();
528 return u.m_number == other->u.m_number;
533 switch (other->m_state) {
535 return u.m_bool == other->u.m_bool;
537 return toNumber() == other->u.m_number;
539 return toNumber() == other->toNumber();
544 switch (other->m_state) {
546 return toNumber() == other->toNumber();
548 return toNumber() == other->u.m_number;
550 return *u.m_string == *other->u.m_string;
555 Q_ASSERT_X(false, "QJSValue::equals", "Not all states are included in the previous switch statement.");
559 v8::HandleScope handleScope;
560 if (isJSBased() && !other->isJSBased()) {
561 if (!other->assignEngine(engine())) {
562 qWarning("QJSValue::equals: cannot compare to a value created in a different engine");
565 } else if (!isJSBased() && other->isJSBased()) {
566 if (!assignEngine(other->engine())) {
567 qWarning("QJSValue::equals: cannot compare to a value created in a different engine");
572 Q_ASSERT(this->engine() && other->engine());
573 if (this->engine() != other->engine()) {
574 qWarning("QJSValue::equals: cannot compare to a value created in a different engine");
577 return m_value->Equals(other->m_value);
580 inline bool QJSValuePrivate::strictlyEquals(QJSValuePrivate* other)
583 // We can't compare these two values without binding to the same engine.
584 if (!other->isJSBased()) {
585 if (other->assignEngine(engine()))
586 return m_value->StrictEquals(other->m_value);
589 if (other->engine() != engine()) {
590 qWarning("QJSValue::strictlyEquals: cannot compare to a value created in a different engine");
593 return m_value->StrictEquals(other->m_value);
595 if (isStringBased()) {
596 if (other->isStringBased())
597 return *u.m_string == *(other->u.m_string);
598 if (other->isJSBased()) {
599 assignEngine(other->engine());
600 return m_value->StrictEquals(other->m_value);
603 if (isNumberBased()) {
604 if (other->isJSBased()) {
605 assignEngine(other->engine());
606 return m_value->StrictEquals(other->m_value);
608 if (m_state != other->m_state)
610 if (m_state == CNumber)
611 return u.m_number == other->u.m_number;
612 Q_ASSERT(m_state == CBool);
613 return u.m_bool == other->u.m_bool;
616 if (!isValid() && !other->isValid())
622 inline bool QJSValuePrivate::lessThan(QJSValuePrivate *other) const
624 if (engine() != other->engine() && engine() && other->engine()) {
625 qWarning("QJSValue::lessThan: cannot compare to a value created in a different engine");
629 if (!isValid() || !other->isValid())
632 if (isString() && other->isString())
633 return toString() < other->toString();
635 if (isObject() || other->isObject()) {
636 v8::HandleScope handleScope;
637 QV8Engine *eng = m_engine ? engine() : other->engine();
638 // FIXME: lessThan can throw an exception which will be dropped by this code:
640 eng->saveException();
641 QScriptSharedDataPointer<QJSValuePrivate> cmp(eng->evaluate(QString::fromLatin1("(function(a,b){return a<b})")));
642 Q_ASSERT(cmp->isFunction());
643 v8::Handle<v8::Value> args[2];
644 cmp->prepareArgumentsForCall(args, QJSValueList() << QJSValuePrivate::get(this) << QJSValuePrivate::get(other));
645 QScriptSharedDataPointer<QJSValuePrivate> resultValue(cmp->call(0, 2, args));
646 bool result = resultValue->toBool();
647 eng->restoreException();
651 double nthis = toNumber();
652 double nother = other->toNumber();
653 if (qIsNaN(nthis) || qIsNaN(nother)) {
654 // Should return undefined in ECMA standard.
657 return nthis < nother;
660 inline bool QJSValuePrivate::instanceOf(QJSValuePrivate* other) const
662 if (!isObject() || !other->isFunction())
664 if (engine() != other->engine()) {
665 qWarning("QJSValue::instanceof: cannot perform operation on a value created in a different engine");
668 v8::HandleScope handleScope;
669 return instanceOf(v8::Handle<v8::Object>::Cast(other->m_value));
672 inline bool QJSValuePrivate::instanceOf(v8::Handle<v8::Object> other) const
674 Q_ASSERT(isObject());
675 Q_ASSERT(other->IsFunction());
677 v8::Handle<v8::Object> self = v8::Handle<v8::Object>::Cast(m_value);
678 v8::Handle<v8::Value> selfPrototype = self->GetPrototype();
679 v8::Handle<v8::Value> otherPrototype = other->Get(v8::String::New("prototype"));
681 while (!selfPrototype->IsNull()) {
682 if (selfPrototype->StrictEquals(otherPrototype))
684 // In general a prototype can be an object or null, but in the loop it can't be null, so
685 // we can cast it safely.
686 selfPrototype = v8::Handle<v8::Object>::Cast(selfPrototype)->GetPrototype();
691 inline QScriptPassPointer<QJSValuePrivate> QJSValuePrivate::prototype() const
694 v8::HandleScope handleScope;
695 return new QJSValuePrivate(engine(), v8::Handle<v8::Object>::Cast(m_value)->GetPrototype());
697 return InvalidValue();
700 inline void QJSValuePrivate::setPrototype(QJSValuePrivate* prototype)
702 if (isObject() && (prototype->isObject() || prototype->isNull())) {
703 if (engine() != prototype->engine()) {
704 if (prototype->engine()) {
705 qWarning("QJSValue::setPrototype() failed: cannot set a prototype created in a different engine");
708 prototype->assignEngine(engine());
710 v8::HandleScope handleScope;
711 if (!v8::Handle<v8::Object>::Cast(m_value)->SetPrototype(*prototype))
712 qWarning("QJSValue::setPrototype() failed: cyclic prototype value");
716 inline void QJSValuePrivate::setProperty(const QString& name, QJSValuePrivate* value, uint attribs)
720 v8::HandleScope handleScope;
721 setProperty(QJSConverter::toString(name), value, attribs);
724 inline void QJSValuePrivate::setProperty(v8::Handle<v8::String> name, QJSValuePrivate* value, uint attribs)
729 if (!value->isJSBased())
730 value->assignEngine(engine());
732 if (!value->isValid()) {
733 // Remove the property.
734 v8::HandleScope handleScope;
735 v8::TryCatch tryCatch;
736 v8::Handle<v8::Object> recv(v8::Object::Cast(*m_value));
737 // if (attribs & QJSValue::PropertyGetter && !(attribs & QJSValue::PropertySetter)) {
738 // v8::Local<v8::Object> descriptor = engine()->originalGlobalObject()->getOwnPropertyDescriptor(recv, name);
739 // if (!descriptor.IsEmpty()) {
740 // v8::Local<v8::Value> setter = descriptor->Get(v8::String::New("set"));
741 // if (!setter.IsEmpty() && !setter->IsUndefined()) {
742 // recv->Delete(name);
743 // engine()->originalGlobalObject()->defineGetterOrSetter(recv, name, setter, QJSValue::PropertySetter);
744 // if (tryCatch.HasCaught())
745 // engine()->setException(tryCatch.Exception(), tryCatch.Message());
749 // } else if (attribs & QJSValue::PropertySetter && !(attribs & QJSValue::PropertyGetter)) {
750 // v8::Local<v8::Object> descriptor = engine()->originalGlobalObject()->getOwnPropertyDescriptor(recv, name);
751 // if (!descriptor.IsEmpty()) {
752 // v8::Local<v8::Value> getter = descriptor->Get(v8::String::New("get"));
753 // if (!getter.IsEmpty() && !getter->IsUndefined()) {
754 // recv->Delete(name);
755 // engine()->originalGlobalObject()->defineGetterOrSetter(recv, name, getter, QJSValue::PropertyGetter);
756 // if (tryCatch.HasCaught())
757 // engine()->setException(tryCatch.Exception(), tryCatch.Message());
763 if (tryCatch.HasCaught())
764 engine()->setException(tryCatch.Exception(), tryCatch.Message());
768 if (engine() != value->engine()) {
769 qWarning("QJSValue::setProperty(%s) failed: "
770 "cannot set value created in a different engine",
771 qPrintable(QJSConverter::toString(name)));
775 v8::TryCatch tryCatch;
776 // if (attribs & (QJSValue::PropertyGetter | QJSValue::PropertySetter)) {
777 // engine()->originalGlobalObject()->defineGetterOrSetter(*this, name, value->m_value, attribs);
779 v8::Object::Cast(*m_value)->Set(name, value->m_value, v8::PropertyAttribute(attribs & QJSConverter::PropertyAttributeMask));
781 if (tryCatch.HasCaught())
782 engine()->setException(tryCatch.Exception(), tryCatch.Message());
785 inline void QJSValuePrivate::setProperty(quint32 index, QJSValuePrivate* value, uint attribs)
787 // FIXME this method should by integrated with other overloads to use the same code patch.
788 // for now it is not possible as v8 doesn't allow to set property attributes using index based api.
794 // FIXME we dont need to convert index to a string.
795 //Object::Set(int,value) do not take attributes.
796 setProperty(QString::number(index), value, attribs);
800 if (!value->isJSBased())
801 value->assignEngine(engine());
803 if (!value->isValid()) {
804 // Remove the property.
805 v8::HandleScope handleScope;
806 v8::TryCatch tryCatch;
807 v8::Object::Cast(*m_value)->Delete(index);
808 if (tryCatch.HasCaught())
809 engine()->setException(tryCatch.Exception(), tryCatch.Message());
813 if (engine() != value->engine()) {
814 qWarning("QJSValue::setProperty() failed: cannot set value created in a different engine");
818 v8::HandleScope handleScope;
819 v8::TryCatch tryCatch;
820 v8::Object::Cast(*m_value)->Set(index, value->m_value);
821 if (tryCatch.HasCaught())
822 engine()->setException(tryCatch.Exception(), tryCatch.Message());
825 inline QScriptPassPointer<QJSValuePrivate> QJSValuePrivate::property(const QString& name) const
828 return InvalidValue();
830 v8::HandleScope handleScope;
831 return property(QJSConverter::toString(name));
834 inline QScriptPassPointer<QJSValuePrivate> QJSValuePrivate::property(v8::Handle<v8::String> name) const
836 Q_ASSERT(!name.IsEmpty());
838 return InvalidValue();
839 return property<>(name);
842 inline QScriptPassPointer<QJSValuePrivate> QJSValuePrivate::property(quint32 index) const
845 return InvalidValue();
846 return property<>(index);
850 inline QScriptPassPointer<QJSValuePrivate> QJSValuePrivate::property(T name) const
852 Q_ASSERT(isObject());
853 v8::HandleScope handleScope;
854 v8::Handle<v8::Object> self(v8::Object::Cast(*m_value));
856 v8::TryCatch tryCatch;
857 v8::Handle<v8::Value> result = self->Get(name);
858 if (tryCatch.HasCaught()) {
859 result = tryCatch.Exception();
860 engine()->setException(result, tryCatch.Message());
861 return new QJSValuePrivate(engine(), result);
863 if (result.IsEmpty() || (result->IsUndefined() && !self->Has(name))) {
864 // In QtScript we make a distinction between a property that exists and has value undefined,
865 // and a property that doesn't exist; in the latter case, we should return an invalid value.
866 return InvalidValue();
868 return new QJSValuePrivate(engine(), result);
871 inline bool QJSValuePrivate::deleteProperty(const QString& name)
876 v8::HandleScope handleScope;
877 v8::Handle<v8::Object> self(v8::Handle<v8::Object>::Cast(m_value));
878 return self->Delete(QJSConverter::toString(name));
881 inline QJSValue::PropertyFlags QJSValuePrivate::propertyFlags(const QString& name) const
884 return QJSValue::PropertyFlags(0);
886 v8::HandleScope handleScope;
887 return engine()->getPropertyFlags(v8::Handle<v8::Object>::Cast(m_value), QJSConverter::toString(name));
890 inline QJSValue::PropertyFlags QJSValuePrivate::propertyFlags(v8::Handle<v8::String> name) const
893 return QJSValue::PropertyFlags(0);
895 v8::HandleScope handleScope;
896 return engine()->getPropertyFlags(v8::Handle<v8::Object>::Cast(m_value), name);
899 inline QScriptPassPointer<QJSValuePrivate> QJSValuePrivate::call(QJSValuePrivate* thisObject, const QJSValueList& args)
902 return InvalidValue();
904 v8::HandleScope handleScope;
906 // Convert all arguments and bind to the engine.
907 int argc = args.size();
908 QVarLengthArray<v8::Handle<v8::Value>, 8> argv(argc);
909 if (!prepareArgumentsForCall(argv.data(), args)) {
910 qWarning("QJSValue::call() failed: cannot call function with argument created in a different engine");
911 return InvalidValue();
914 return call(thisObject, argc, argv.data());
917 QScriptPassPointer<QJSValuePrivate> QJSValuePrivate::call(QJSValuePrivate* thisObject, int argc, v8::Handle<v8::Value> *argv)
919 QV8Engine *e = engine();
921 v8::Handle<v8::Object> recv;
923 if (!thisObject || !thisObject->isObject()) {
924 recv = v8::Handle<v8::Object>(v8::Object::Cast(*e->global()));
926 if (!thisObject->assignEngine(e)) {
927 qWarning("QJSValue::call() failed: cannot call function with thisObject created in a different engine");
928 return InvalidValue();
931 recv = v8::Handle<v8::Object>(v8::Object::Cast(*thisObject->m_value));
935 v8::Local<v8::Value> exeption = v8::Exception::TypeError(v8::String::New("Arguments must be an array"));
936 e->setException(exeption);
937 return new QJSValuePrivate(e, exeption);
940 v8::TryCatch tryCatch;
941 v8::Handle<v8::Value> result = v8::Object::Cast(*m_value)->CallAsFunction(recv, argc, argv);
943 if (result.IsEmpty()) {
944 result = tryCatch.Exception();
945 // TODO: figure out why v8 doesn't always produce an exception value.
946 //Q_ASSERT(!result.IsEmpty());
947 if (result.IsEmpty())
948 result = v8::Exception::Error(v8::String::New("missing exception value"));
949 e->setException(result, tryCatch.Message());
952 return new QJSValuePrivate(e, result);
955 inline QScriptPassPointer<QJSValuePrivate> QJSValuePrivate::construct(int argc, v8::Handle<v8::Value> *argv)
957 QV8Engine *e = engine();
960 v8::Local<v8::Value> exeption = v8::Exception::TypeError(v8::String::New("Arguments must be an array"));
961 e->setException(exeption);
962 return new QJSValuePrivate(e, exeption);
965 v8::TryCatch tryCatch;
966 v8::Handle<v8::Value> result = v8::Object::Cast(*m_value)->CallAsConstructor(argc, argv);
968 if (result.IsEmpty()) {
969 result = tryCatch.Exception();
970 e->setException(result, tryCatch.Message());
973 return new QJSValuePrivate(e, result);
976 inline QScriptPassPointer<QJSValuePrivate> QJSValuePrivate::construct(const QJSValueList& args)
979 return InvalidValue();
981 v8::HandleScope handleScope;
983 // Convert all arguments and bind to the engine.
984 int argc = args.size();
985 QVarLengthArray<v8::Handle<v8::Value>, 8> argv(argc);
986 if (!prepareArgumentsForCall(argv.data(), args)) {
987 qWarning("QJSValue::construct() failed: cannot construct function with argument created in a different engine");
988 return InvalidValue();
991 return construct(argc, argv.data());
995 * Make sure this value is associated with a v8 value belogning to this engine.
996 * If the value was invalid, or belogning to another engine, return false.
998 bool QJSValuePrivate::assignEngine(QV8Engine* engine)
1001 v8::HandleScope handleScope;
1006 m_value = v8::Persistent<v8::Value>::New(engine->makeJSValue(u.m_bool));
1009 m_value = v8::Persistent<v8::Value>::New(engine->makeJSValue(*u.m_string));
1013 m_value = v8::Persistent<v8::Value>::New(engine->makeJSValue(u.m_number));
1016 m_value = v8::Persistent<v8::Value>::New(engine->makeJSValue(QJSValue::NullValue));
1019 m_value = v8::Persistent<v8::Value>::New(engine->makeJSValue(QJSValue::UndefinedValue));
1022 if (this->engine() == engine)
1024 else if (!isJSBased())
1025 Q_ASSERT_X(!isJSBased(), "assignEngine()", "Not all states are included in the previous switch statement.");
1027 qWarning("JSValue can't be rassigned to an another engine.");
1033 m_engine->registerValue(this);
1039 reinitialize this value to an invalid value.
1041 void QJSValuePrivate::reinitialize()
1044 m_engine->unregisterValue(this);
1047 } else if (isStringBased()) {
1054 QV8Engine* QJSValuePrivate::engine() const
1059 inline QJSValuePrivate::operator v8::Handle<v8::Value>() const
1061 Q_ASSERT(isJSBased());
1065 inline QJSValuePrivate::operator v8::Handle<v8::Object>() const
1067 Q_ASSERT(isObject());
1068 return v8::Handle<v8::Object>::Cast(m_value);
1072 * Return a v8::Handle, assign to the engine if needed.
1074 v8::Handle<v8::Value> QJSValuePrivate::asV8Value(QV8Engine* engine)
1077 if (!assignEngine(engine))
1078 return v8::Handle<v8::Value>();
1080 Q_ASSERT(isJSBased());
1086 Returns true if QSV have an engine associated.
1088 bool QJSValuePrivate::isJSBased() const
1092 if (m_state >= JSValue)
1093 Q_ASSERT(!m_value.IsEmpty());
1095 Q_ASSERT(m_value.IsEmpty());
1097 return m_state >= JSValue;
1102 Returns true if current value of QSV is placed in m_number.
1104 bool QJSValuePrivate::isNumberBased() const { return m_state == CNumber || m_state == CBool; }
1108 Returns true if current value of QSV is placed in m_string.
1110 bool QJSValuePrivate::isStringBased() const { return m_state == CString; }
1114 Converts arguments and bind them to the engine.
1115 \attention argv should be big enough
1117 inline bool QJSValuePrivate::prepareArgumentsForCall(v8::Handle<v8::Value> argv[], const QJSValueList& args) const
1119 QJSValueList::const_iterator i = args.constBegin();
1120 for (int j = 0; i != args.constEnd(); j++, i++) {
1121 QJSValuePrivate* value = QJSValuePrivate::get(*i);
1122 if ((value->isJSBased() && engine() != value->engine())
1123 || (!value->isJSBased() && value->isValid() && !value->assignEngine(engine())))
1124 // Different engines are not allowed!
1126 if (value->isValid())
1129 argv[j] = engine()->makeJSValue(QJSValue::UndefinedValue);