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"
46 QJSValuePrivate* QJSValuePrivate::get(const QJSValue& q) { Q_ASSERT(q.d_ptr.data()); return q.d_ptr.data(); }
48 QJSValue QJSValuePrivate::get(const QJSValuePrivate* d)
51 return QJSValue(const_cast<QJSValuePrivate*>(d));
54 QJSValue QJSValuePrivate::get(QScriptPassPointer<QJSValuePrivate> d)
60 QJSValue QJSValuePrivate::get(QJSValuePrivate* d)
66 QJSValuePrivate::QJSValuePrivate()
67 : m_engine(0), m_state(Invalid)
71 QJSValuePrivate::QJSValuePrivate(bool value)
72 : m_engine(0), m_state(CBool), u(value)
76 QJSValuePrivate::QJSValuePrivate(int value)
77 : m_engine(0), m_state(CNumber), u(value)
81 QJSValuePrivate::QJSValuePrivate(uint value)
82 : m_engine(0), m_state(CNumber), u(value)
86 QJSValuePrivate::QJSValuePrivate(double value)
87 : m_engine(0), m_state(CNumber), u(value)
91 QJSValuePrivate::QJSValuePrivate(const QString& value)
92 : m_engine(0), m_state(CString), u(new QString(value))
96 QJSValuePrivate::QJSValuePrivate(QJSValue::SpecialValue value)
97 : m_engine(0), m_state(value == QJSValue::NullValue ? CNull : CUndefined)
101 QJSValuePrivate::QJSValuePrivate(QV8Engine* engine, bool value)
102 : m_engine(engine), m_state(JSValue)
105 v8::HandleScope handleScope;
106 m_value = v8::Persistent<v8::Value>::New(m_engine->makeJSValue(value));
107 m_engine->registerValue(this);
110 QJSValuePrivate::QJSValuePrivate(QV8Engine* engine, int value)
111 : m_engine(engine), m_state(JSValue)
114 v8::HandleScope handleScope;
115 m_value = v8::Persistent<v8::Value>::New(m_engine->makeJSValue(value));
116 m_engine->registerValue(this);
119 QJSValuePrivate::QJSValuePrivate(QV8Engine* engine, uint value)
120 : m_engine(engine), m_state(JSValue)
123 v8::HandleScope handleScope;
124 m_value = v8::Persistent<v8::Value>::New(m_engine->makeJSValue(value));
125 m_engine->registerValue(this);
128 QJSValuePrivate::QJSValuePrivate(QV8Engine* engine, double value)
129 : m_engine(engine), m_state(JSValue)
132 v8::HandleScope handleScope;
133 m_value = v8::Persistent<v8::Value>::New(m_engine->makeJSValue(value));
134 m_engine->registerValue(this);
137 QJSValuePrivate::QJSValuePrivate(QV8Engine* engine, const QString& value)
138 : m_engine(engine), m_state(JSValue)
141 v8::HandleScope handleScope;
142 m_value = v8::Persistent<v8::Value>::New(m_engine->makeJSValue(value));
143 m_engine->registerValue(this);
146 QJSValuePrivate::QJSValuePrivate(QV8Engine* engine, QJSValue::SpecialValue value)
147 : m_engine(engine), m_state(JSValue)
150 v8::HandleScope handleScope;
151 m_value = v8::Persistent<v8::Value>::New(m_engine->makeJSValue(value));
152 m_engine->registerValue(this);
155 QJSValuePrivate::QJSValuePrivate(QV8Engine *engine, v8::Handle<v8::Value> value)
156 : m_engine(engine), m_state(JSValue), m_value(v8::Persistent<v8::Value>::New(value))
159 // It shouldn't happen, v8 shows errors by returning an empty handler. This is important debug
160 // information and it can't be simply ignored.
161 Q_ASSERT(!value.IsEmpty());
162 m_engine->registerValue(this);
165 QJSValuePrivate::~QJSValuePrivate()
168 m_engine->unregisterValue(this);
169 QScriptIsolate api(m_engine);
171 } else if (isStringBased()) {
176 bool QJSValuePrivate::toBool() const
181 v8::HandleScope scope;
182 return m_value->ToBoolean()->Value();
185 return !(qIsNaN(u.m_number) || !u.m_number);
193 return u.m_string->length();
196 Q_ASSERT_X(false, "toBool()", "Not all states are included in the previous switch statement.");
197 return false; // Avoid compiler warning.
200 double QJSValuePrivate::toNumber() const
205 v8::HandleScope scope;
206 return m_value->ToNumber()->Value();
211 return u.m_bool ? 1 : 0;
219 double result = u.m_string->toDouble(&ok);
222 result = u.m_string->toInt(&ok, 0); // Try other bases.
225 if (*u.m_string == QLatin1String("Infinity"))
227 if (*u.m_string == QLatin1String("-Infinity"))
229 return u.m_string->length() ? qQNaN() : 0;
232 Q_ASSERT_X(false, "toNumber()", "Not all states are included in the previous switch statement.");
233 return 0; // Avoid compiler warning.
236 QScriptPassPointer<QJSValuePrivate> QJSValuePrivate::toObject(QV8Engine* engine) const
239 if (this->engine() && engine != this->engine()) {
240 qWarning("QJSEngine::toObject: cannot convert value created in a different engine");
241 return InvalidValue();
244 v8::HandleScope scope;
249 return new QJSValuePrivate;
251 return new QJSValuePrivate(engine, engine->makeJSValue(*u.m_string)->ToObject());
253 return new QJSValuePrivate(engine, engine->makeJSValue(u.m_number)->ToObject());
255 return new QJSValuePrivate(engine, engine->makeJSValue(u.m_bool)->ToObject());
257 if (m_value->IsObject())
258 return const_cast<QJSValuePrivate*>(this);
259 if (m_value->IsNull() || m_value->IsUndefined()) // avoid "Uncaught TypeError: Cannot convert null to object"
260 return InvalidValue();
261 return new QJSValuePrivate(engine, m_value->ToObject());
263 Q_ASSERT_X(false, Q_FUNC_INFO, "Not all states are included in this switch");
264 return InvalidValue();
269 This method is created only for QJSValue::toObject() purpose which is obsolete.
272 QScriptPassPointer<QJSValuePrivate> QJSValuePrivate::toObject() const
275 return toObject(engine());
277 // Without an engine there is not much we can do.
278 return new QJSValuePrivate;
281 QString QJSValuePrivate::toString() const
287 return u.m_bool ? QString::fromLatin1("true") : QString::fromLatin1("false");
291 return QJSConverter::toString(u.m_number);
293 return QString::fromLatin1("null");
295 return QString::fromLatin1("undefined");
297 Q_ASSERT(!m_value.IsEmpty());
298 v8::HandleScope handleScope;
299 v8::TryCatch tryCatch;
300 v8::Local<v8::String> result = m_value->ToString();
301 if (result.IsEmpty()) {
302 result = tryCatch.Exception()->ToString();
303 m_engine->setException(tryCatch.Exception(), tryCatch.Message());
305 return QJSConverter::toString(result);
308 Q_ASSERT_X(false, "toString()", "Not all states are included in the previous switch statement.");
309 return QString(); // Avoid compiler warning.
312 QVariant QJSValuePrivate::toVariant() const
318 return QVariant(u.m_bool);
320 return QVariant(*u.m_string);
322 return QVariant(u.m_number);
331 Q_ASSERT(m_state == JSValue);
332 Q_ASSERT(!m_value.IsEmpty());
335 v8::HandleScope handleScope;
336 return m_engine->variantFromJS(m_value);
339 inline QDateTime QJSValuePrivate::toDataTime() const
344 v8::HandleScope handleScope;
345 return QJSConverter::toDateTime(v8::Handle<v8::Date>::Cast(m_value));
349 inline QRegExp QJSValuePrivate::toRegExp() const
354 v8::HandleScope handleScope;
355 return QJSConverter::toRegExp(v8::Handle<v8::RegExp>::Cast(m_value));
358 QObject* QJSValuePrivate::toQObject() const
363 v8::HandleScope handleScope;
364 return engine()->qtObjectFromJS(m_value);
367 double QJSValuePrivate::toInteger() const
369 double result = toNumber();
374 return (result > 0) ? qFloor(result) : -1 * qFloor(-result);
377 qint32 QJSValuePrivate::toInt32() const
379 double result = toInteger();
380 // Orginaly it should look like that (result == 0 || qIsInf(result) || qIsNaN(result)), but
381 // some of these operation are invoked in toInteger subcall.
387 quint32 QJSValuePrivate::toUInt32() const
389 double result = toInteger();
390 // Orginaly it should look like that (result == 0 || qIsInf(result) || qIsNaN(result)), but
391 // some of these operation are invoked in toInteger subcall.
397 quint16 QJSValuePrivate::toUInt16() const
402 inline bool QJSValuePrivate::isArray() const
404 return isJSBased() && m_value->IsArray();
407 inline bool QJSValuePrivate::isBool() const
409 return m_state == CBool || (isJSBased() && m_value->IsBoolean());
412 inline bool QJSValuePrivate::isCallable() const
417 // Our C++ wrappers register function handlers but not always act as callables.
418 return v8::Object::Cast(*m_value)->IsCallable();
423 inline bool QJSValuePrivate::isError() const
427 v8::HandleScope handleScope;
428 return m_value->IsError();
431 inline bool QJSValuePrivate::isFunction() const
433 return isJSBased() && m_value->IsFunction();
436 inline bool QJSValuePrivate::isNull() const
438 return m_state == CNull || (isJSBased() && m_value->IsNull());
441 inline bool QJSValuePrivate::isNumber() const
443 return m_state == CNumber || (isJSBased() && m_value->IsNumber());
446 inline bool QJSValuePrivate::isObject() const
448 return isJSBased() && m_value->IsObject();
451 inline bool QJSValuePrivate::isString() const
453 return m_state == CString || (isJSBased() && m_value->IsString());
456 inline bool QJSValuePrivate::isUndefined() const
458 return m_state == CUndefined || (isJSBased() && m_value->IsUndefined());
461 inline bool QJSValuePrivate::isValid() const
463 return m_state != Invalid;
466 inline bool QJSValuePrivate::isVariant() const
468 return isJSBased() && m_engine->isVariant(m_value);
471 bool QJSValuePrivate::isDate() const
473 return (isJSBased() && m_value->IsDate());
476 bool QJSValuePrivate::isRegExp() const
478 return (isJSBased() && m_value->IsRegExp());
481 bool QJSValuePrivate::isQObject() const
483 return isJSBased() && engine()->isQObject(m_value);
486 inline bool QJSValuePrivate::equals(QJSValuePrivate* other)
489 return !other->isValid();
491 if (!other->isValid())
494 if (!isJSBased() && !other->isJSBased()) {
498 return other->isUndefined() || other->isNull();
500 switch (other->m_state) {
503 return u.m_number == other->toNumber();
505 return u.m_number == other->u.m_number;
510 switch (other->m_state) {
512 return u.m_bool == other->u.m_bool;
514 return toNumber() == other->u.m_number;
516 return toNumber() == other->toNumber();
521 switch (other->m_state) {
523 return toNumber() == other->toNumber();
525 return toNumber() == other->u.m_number;
527 return *u.m_string == *other->u.m_string;
532 Q_ASSERT_X(false, "QJSValue::equals", "Not all states are included in the previous switch statement.");
536 v8::HandleScope handleScope;
537 if (isJSBased() && !other->isJSBased()) {
538 if (!other->assignEngine(engine())) {
539 qWarning("QJSValue::equals: cannot compare to a value created in a different engine");
542 } else if (!isJSBased() && other->isJSBased()) {
543 if (!assignEngine(other->engine())) {
544 qWarning("QJSValue::equals: cannot compare to a value created in a different engine");
549 Q_ASSERT(this->engine() && other->engine());
550 if (this->engine() != other->engine()) {
551 qWarning("QJSValue::equals: cannot compare to a value created in a different engine");
554 return m_value->Equals(other->m_value);
557 inline bool QJSValuePrivate::strictlyEquals(QJSValuePrivate* other)
560 // We can't compare these two values without binding to the same engine.
561 if (!other->isJSBased()) {
562 if (other->assignEngine(engine()))
563 return m_value->StrictEquals(other->m_value);
566 if (other->engine() != engine()) {
567 qWarning("QJSValue::strictlyEquals: cannot compare to a value created in a different engine");
570 return m_value->StrictEquals(other->m_value);
572 if (isStringBased()) {
573 if (other->isStringBased())
574 return *u.m_string == *(other->u.m_string);
575 if (other->isJSBased()) {
576 assignEngine(other->engine());
577 return m_value->StrictEquals(other->m_value);
580 if (isNumberBased()) {
581 if (other->isJSBased()) {
582 assignEngine(other->engine());
583 return m_value->StrictEquals(other->m_value);
585 if (m_state != other->m_state)
587 if (m_state == CNumber)
588 return u.m_number == other->u.m_number;
589 Q_ASSERT(m_state == CBool);
590 return u.m_bool == other->u.m_bool;
593 if (!isValid() && !other->isValid())
599 inline bool QJSValuePrivate::lessThan(QJSValuePrivate *other) const
601 if (engine() != other->engine() && engine() && other->engine()) {
602 qWarning("QJSValue::lessThan: cannot compare to a value created in a different engine");
606 if (!isValid() || !other->isValid())
609 if (isString() && other->isString())
610 return toString() < other->toString();
612 if (isObject() || other->isObject()) {
613 v8::HandleScope handleScope;
614 QV8Engine *eng = m_engine ? engine() : other->engine();
615 // FIXME: lessThan can throw an exception which will be dropped by this code:
617 eng->saveException();
618 QScriptSharedDataPointer<QJSValuePrivate> cmp(eng->evaluate(QString::fromLatin1("(function(a,b){return a<b})")));
619 Q_ASSERT(cmp->isFunction());
620 v8::Handle<v8::Value> args[2];
621 cmp->prepareArgumentsForCall(args, QJSValueList() << QJSValuePrivate::get(this) << QJSValuePrivate::get(other));
622 QScriptSharedDataPointer<QJSValuePrivate> resultValue(cmp->call(0, 2, args));
623 bool result = resultValue->toBool();
624 eng->restoreException();
628 double nthis = toNumber();
629 double nother = other->toNumber();
630 if (qIsNaN(nthis) || qIsNaN(nother)) {
631 // Should return undefined in ECMA standard.
634 return nthis < nother;
637 inline bool QJSValuePrivate::instanceOf(QJSValuePrivate* other) const
639 if (!isObject() || !other->isFunction())
641 if (engine() != other->engine()) {
642 qWarning("QJSValue::instanceof: cannot perform operation on a value created in a different engine");
645 v8::HandleScope handleScope;
646 return instanceOf(v8::Handle<v8::Object>::Cast(other->m_value));
649 inline bool QJSValuePrivate::instanceOf(v8::Handle<v8::Object> other) const
651 Q_ASSERT(isObject());
652 Q_ASSERT(other->IsFunction());
654 v8::Handle<v8::Object> self = v8::Handle<v8::Object>::Cast(m_value);
655 v8::Handle<v8::Value> selfPrototype = self->GetPrototype();
656 v8::Handle<v8::Value> otherPrototype = other->Get(v8::String::New("prototype"));
658 while (!selfPrototype->IsNull()) {
659 if (selfPrototype->StrictEquals(otherPrototype))
661 // In general a prototype can be an object or null, but in the loop it can't be null, so
662 // we can cast it safely.
663 selfPrototype = v8::Handle<v8::Object>::Cast(selfPrototype)->GetPrototype();
668 inline QScriptPassPointer<QJSValuePrivate> QJSValuePrivate::prototype() const
671 v8::HandleScope handleScope;
672 return new QJSValuePrivate(engine(), v8::Handle<v8::Object>::Cast(m_value)->GetPrototype());
674 return InvalidValue();
677 inline void QJSValuePrivate::setPrototype(QJSValuePrivate* prototype)
679 if (isObject() && (prototype->isObject() || prototype->isNull())) {
680 if (engine() != prototype->engine()) {
681 if (prototype->engine()) {
682 qWarning("QJSValue::setPrototype() failed: cannot set a prototype created in a different engine");
685 prototype->assignEngine(engine());
687 v8::HandleScope handleScope;
688 if (!v8::Handle<v8::Object>::Cast(m_value)->SetPrototype(*prototype))
689 qWarning("QJSValue::setPrototype() failed: cyclic prototype value");
693 inline void QJSValuePrivate::setProperty(const QString& name, QJSValuePrivate* value, uint attribs)
697 v8::HandleScope handleScope;
698 setProperty(QJSConverter::toString(name), value, attribs);
701 inline void QJSValuePrivate::setProperty(v8::Handle<v8::String> name, QJSValuePrivate* value, uint attribs)
706 if (!value->isJSBased())
707 value->assignEngine(engine());
709 if (!value->isValid()) {
710 // Remove the property.
711 v8::HandleScope handleScope;
712 v8::TryCatch tryCatch;
713 v8::Handle<v8::Object> recv(v8::Object::Cast(*m_value));
714 // if (attribs & QJSValue::PropertyGetter && !(attribs & QJSValue::PropertySetter)) {
715 // v8::Local<v8::Object> descriptor = engine()->originalGlobalObject()->getOwnPropertyDescriptor(recv, name);
716 // if (!descriptor.IsEmpty()) {
717 // v8::Local<v8::Value> setter = descriptor->Get(v8::String::New("set"));
718 // if (!setter.IsEmpty() && !setter->IsUndefined()) {
719 // recv->Delete(name);
720 // engine()->originalGlobalObject()->defineGetterOrSetter(recv, name, setter, QJSValue::PropertySetter);
721 // if (tryCatch.HasCaught())
722 // engine()->setException(tryCatch.Exception(), tryCatch.Message());
726 // } else if (attribs & QJSValue::PropertySetter && !(attribs & QJSValue::PropertyGetter)) {
727 // v8::Local<v8::Object> descriptor = engine()->originalGlobalObject()->getOwnPropertyDescriptor(recv, name);
728 // if (!descriptor.IsEmpty()) {
729 // v8::Local<v8::Value> getter = descriptor->Get(v8::String::New("get"));
730 // if (!getter.IsEmpty() && !getter->IsUndefined()) {
731 // recv->Delete(name);
732 // engine()->originalGlobalObject()->defineGetterOrSetter(recv, name, getter, QJSValue::PropertyGetter);
733 // if (tryCatch.HasCaught())
734 // engine()->setException(tryCatch.Exception(), tryCatch.Message());
740 if (tryCatch.HasCaught())
741 engine()->setException(tryCatch.Exception(), tryCatch.Message());
745 if (engine() != value->engine()) {
746 qWarning("QJSValue::setProperty(%s) failed: "
747 "cannot set value created in a different engine",
748 qPrintable(QJSConverter::toString(name)));
752 v8::TryCatch tryCatch;
753 // if (attribs & (QJSValue::PropertyGetter | QJSValue::PropertySetter)) {
754 // engine()->originalGlobalObject()->defineGetterOrSetter(*this, name, value->m_value, attribs);
756 v8::Object::Cast(*m_value)->Set(name, value->m_value, v8::PropertyAttribute(attribs & QJSConverter::PropertyAttributeMask));
758 if (tryCatch.HasCaught())
759 engine()->setException(tryCatch.Exception(), tryCatch.Message());
762 inline void QJSValuePrivate::setProperty(quint32 index, QJSValuePrivate* value, uint attribs)
764 // FIXME this method should by integrated with other overloads to use the same code patch.
765 // for now it is not possible as v8 doesn't allow to set property attributes using index based api.
771 // FIXME we dont need to convert index to a string.
772 //Object::Set(int,value) do not take attributes.
773 setProperty(QString::number(index), value, attribs);
777 if (!value->isJSBased())
778 value->assignEngine(engine());
780 if (!value->isValid()) {
781 // Remove the property.
782 v8::HandleScope handleScope;
783 v8::TryCatch tryCatch;
784 v8::Object::Cast(*m_value)->Delete(index);
785 if (tryCatch.HasCaught())
786 engine()->setException(tryCatch.Exception(), tryCatch.Message());
790 if (engine() != value->engine()) {
791 qWarning("QJSValue::setProperty() failed: cannot set value created in a different engine");
795 v8::HandleScope handleScope;
796 v8::TryCatch tryCatch;
797 v8::Object::Cast(*m_value)->Set(index, value->m_value);
798 if (tryCatch.HasCaught())
799 engine()->setException(tryCatch.Exception(), tryCatch.Message());
802 inline QScriptPassPointer<QJSValuePrivate> QJSValuePrivate::property(const QString& name) const
805 return InvalidValue();
807 v8::HandleScope handleScope;
808 return property(QJSConverter::toString(name));
811 inline QScriptPassPointer<QJSValuePrivate> QJSValuePrivate::property(v8::Handle<v8::String> name) const
813 Q_ASSERT(!name.IsEmpty());
815 return InvalidValue();
816 return property<>(name);
819 inline QScriptPassPointer<QJSValuePrivate> QJSValuePrivate::property(quint32 index) const
822 return InvalidValue();
823 return property<>(index);
827 inline QScriptPassPointer<QJSValuePrivate> QJSValuePrivate::property(T name) const
829 Q_ASSERT(isObject());
830 v8::HandleScope handleScope;
831 v8::Handle<v8::Object> self(v8::Object::Cast(*m_value));
833 v8::TryCatch tryCatch;
834 v8::Handle<v8::Value> result = self->Get(name);
835 if (tryCatch.HasCaught()) {
836 result = tryCatch.Exception();
837 engine()->setException(result, tryCatch.Message());
838 return new QJSValuePrivate(engine(), result);
840 if (result.IsEmpty() || (result->IsUndefined() && !self->Has(name))) {
841 // In QtScript we make a distinction between a property that exists and has value undefined,
842 // and a property that doesn't exist; in the latter case, we should return an invalid value.
843 return InvalidValue();
845 return new QJSValuePrivate(engine(), result);
848 inline bool QJSValuePrivate::deleteProperty(const QString& name)
853 v8::HandleScope handleScope;
854 v8::Handle<v8::Object> self(v8::Handle<v8::Object>::Cast(m_value));
855 return self->Delete(QJSConverter::toString(name));
858 inline QJSValue::PropertyFlags QJSValuePrivate::propertyFlags(const QString& name) const
861 return QJSValue::PropertyFlags(0);
863 v8::HandleScope handleScope;
864 return engine()->getPropertyFlags(v8::Handle<v8::Object>::Cast(m_value), QJSConverter::toString(name));
867 inline QJSValue::PropertyFlags QJSValuePrivate::propertyFlags(v8::Handle<v8::String> name) const
870 return QJSValue::PropertyFlags(0);
872 v8::HandleScope handleScope;
873 return engine()->getPropertyFlags(v8::Handle<v8::Object>::Cast(m_value), name);
876 inline QScriptPassPointer<QJSValuePrivate> QJSValuePrivate::call(QJSValuePrivate* thisObject, const QJSValueList& args)
879 return InvalidValue();
881 v8::HandleScope handleScope;
883 // Convert all arguments and bind to the engine.
884 int argc = args.size();
885 QVarLengthArray<v8::Handle<v8::Value>, 8> argv(argc);
886 if (!prepareArgumentsForCall(argv.data(), args)) {
887 qWarning("QJSValue::call() failed: cannot call function with argument created in a different engine");
888 return InvalidValue();
891 return call(thisObject, argc, argv.data());
894 QScriptPassPointer<QJSValuePrivate> QJSValuePrivate::call(QJSValuePrivate* thisObject, int argc, v8::Handle<v8::Value> *argv)
896 QV8Engine *e = engine();
898 v8::Handle<v8::Object> recv;
900 if (!thisObject || !thisObject->isObject()) {
901 recv = v8::Handle<v8::Object>(v8::Object::Cast(*e->global()));
903 if (!thisObject->assignEngine(e)) {
904 qWarning("QJSValue::call() failed: cannot call function with thisObject created in a different engine");
905 return InvalidValue();
908 recv = v8::Handle<v8::Object>(v8::Object::Cast(*thisObject->m_value));
912 v8::Local<v8::Value> exeption = v8::Exception::TypeError(v8::String::New("Arguments must be an array"));
913 e->setException(exeption);
914 return new QJSValuePrivate(e, exeption);
917 v8::TryCatch tryCatch;
918 v8::Handle<v8::Value> result = v8::Object::Cast(*m_value)->CallAsFunction(recv, argc, argv);
920 if (result.IsEmpty()) {
921 result = tryCatch.Exception();
922 // TODO: figure out why v8 doesn't always produce an exception value.
923 //Q_ASSERT(!result.IsEmpty());
924 if (result.IsEmpty())
925 result = v8::Exception::Error(v8::String::New("missing exception value"));
926 e->setException(result, tryCatch.Message());
929 return new QJSValuePrivate(e, result);
932 inline QScriptPassPointer<QJSValuePrivate> QJSValuePrivate::construct(int argc, v8::Handle<v8::Value> *argv)
934 QV8Engine *e = engine();
937 v8::Local<v8::Value> exeption = v8::Exception::TypeError(v8::String::New("Arguments must be an array"));
938 e->setException(exeption);
939 return new QJSValuePrivate(e, exeption);
942 v8::TryCatch tryCatch;
943 v8::Handle<v8::Value> result = v8::Object::Cast(*m_value)->CallAsConstructor(argc, argv);
945 if (result.IsEmpty()) {
946 result = tryCatch.Exception();
947 e->setException(result, tryCatch.Message());
950 return new QJSValuePrivate(e, result);
953 inline QScriptPassPointer<QJSValuePrivate> QJSValuePrivate::construct(const QJSValueList& args)
956 return InvalidValue();
958 v8::HandleScope handleScope;
960 // Convert all arguments and bind to the engine.
961 int argc = args.size();
962 QVarLengthArray<v8::Handle<v8::Value>, 8> argv(argc);
963 if (!prepareArgumentsForCall(argv.data(), args)) {
964 qWarning("QJSValue::construct() failed: cannot construct function with argument created in a different engine");
965 return InvalidValue();
968 return construct(argc, argv.data());
972 * Make sure this value is associated with a v8 value belogning to this engine.
973 * If the value was invalid, or belogning to another engine, return false.
975 bool QJSValuePrivate::assignEngine(QV8Engine* engine)
978 v8::HandleScope handleScope;
983 m_value = v8::Persistent<v8::Value>::New(engine->makeJSValue(u.m_bool));
986 m_value = v8::Persistent<v8::Value>::New(engine->makeJSValue(*u.m_string));
990 m_value = v8::Persistent<v8::Value>::New(engine->makeJSValue(u.m_number));
993 m_value = v8::Persistent<v8::Value>::New(engine->makeJSValue(QJSValue::NullValue));
996 m_value = v8::Persistent<v8::Value>::New(engine->makeJSValue(QJSValue::UndefinedValue));
999 if (this->engine() == engine)
1001 else if (!isJSBased())
1002 Q_ASSERT_X(!isJSBased(), "assignEngine()", "Not all states are included in the previous switch statement.");
1004 qWarning("JSValue can't be rassigned to an another engine.");
1010 m_engine->registerValue(this);
1016 reinitialize this value to an invalid value.
1018 void QJSValuePrivate::reinitialize()
1021 m_engine->unregisterValue(this);
1024 } else if (isStringBased()) {
1033 reinitialize this value to an JSValue.
1035 void QJSValuePrivate::reinitialize(QV8Engine* engine, v8::Handle<v8::Value> value)
1037 Q_ASSERT_X(this != InvalidValue(), Q_FUNC_INFO, "static invalid can't be reinitialized to a different value");
1040 // avoid double registration
1041 m_engine->unregisterValue(this);
1042 } else if (isStringBased()) {
1047 m_value = v8::Persistent<v8::Value>::New(value);
1048 m_engine->registerValue(this);
1051 QV8Engine* QJSValuePrivate::engine() const
1056 inline QJSValuePrivate::operator v8::Handle<v8::Value>() const
1058 Q_ASSERT(isJSBased());
1062 inline QJSValuePrivate::operator v8::Handle<v8::Object>() const
1064 Q_ASSERT(isObject());
1065 return v8::Handle<v8::Object>::Cast(m_value);
1069 * Return a v8::Handle, assign to the engine if needed.
1071 v8::Handle<v8::Value> QJSValuePrivate::asV8Value(QV8Engine* engine)
1074 if (!assignEngine(engine))
1075 return v8::Handle<v8::Value>();
1077 Q_ASSERT(isJSBased());
1083 Returns true if QSV have an engine associated.
1085 bool QJSValuePrivate::isJSBased() const
1089 if (m_state >= JSValue)
1090 Q_ASSERT(!m_value.IsEmpty());
1092 Q_ASSERT(m_value.IsEmpty());
1094 return m_state >= JSValue;
1099 Returns true if current value of QSV is placed in m_number.
1101 bool QJSValuePrivate::isNumberBased() const { return m_state == CNumber || m_state == CBool; }
1105 Returns true if current value of QSV is placed in m_string.
1107 bool QJSValuePrivate::isStringBased() const { return m_state == CString; }
1111 Converts arguments and bind them to the engine.
1112 \attention argv should be big enough
1114 inline bool QJSValuePrivate::prepareArgumentsForCall(v8::Handle<v8::Value> argv[], const QJSValueList& args) const
1116 QJSValueList::const_iterator i = args.constBegin();
1117 for (int j = 0; i != args.constEnd(); j++, i++) {
1118 QJSValuePrivate* value = QJSValuePrivate::get(*i);
1119 if ((value->isJSBased() && engine() != value->engine())
1120 || (!value->isJSBased() && value->isValid() && !value->assignEngine(engine())))
1121 // Different engines are not allowed!
1123 if (value->isValid())
1126 argv[j] = engine()->makeJSValue(QJSValue::UndefinedValue);