diff options
Diffstat (limited to 'src/script/qscriptvalueimpl.cpp')
-rw-r--r-- | src/script/qscriptvalueimpl.cpp | 448 |
1 files changed, 448 insertions, 0 deletions
diff --git a/src/script/qscriptvalueimpl.cpp b/src/script/qscriptvalueimpl.cpp new file mode 100644 index 0000000..15d1b8a --- /dev/null +++ b/src/script/qscriptvalueimpl.cpp @@ -0,0 +1,448 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtScript module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qscriptvalueimpl_p.h" + +#ifndef QT_NO_SCRIPT + +#include "qscriptengine_p.h" +#include "qscriptvalueimpl_p.h" +#include "qscriptcontext_p.h" +#include "qscriptmember_p.h" +#include "qscriptobject_p.h" + +QT_BEGIN_NAMESPACE + +static void dfs(QScriptObject *instance, QHash<QScriptObject*, int> &dfn, int n) +{ + bool found = dfn.contains(instance); + dfn[instance] = n; + + if (found) + return; + + if (instance->m_prototype.isObject()) + dfs (instance->m_prototype.m_object_value, dfn, n + 1); + + if (instance->m_scope.isObject()) + dfs (instance->m_scope.m_object_value, dfn, n + 1); +} + + +static bool checkCycle(QScriptObject *instance, const QHash<QScriptObject*, int> &dfn) +{ + int n = dfn.value(instance); + + if (instance->m_prototype.isObject()) { + if (n >= dfn.value(instance->m_prototype.m_object_value)) + return true; + } + + if (instance->m_scope.isObject()) { + if (n >= dfn.value(instance->m_scope.m_object_value)) + return true; + } + + return false; +} + +bool QScriptValueImpl::detectedCycle() const +{ + QHash<QScriptObject*, int> dfn; + dfs(m_object_value, dfn, 0); + return checkCycle(m_object_value, dfn); +} + +bool QScriptValueImpl::instanceOf(const QScriptValueImpl &value) const +{ + if (! isObject() || ! value.isObject() || !value.implementsHasInstance()) + return false; + return value.hasInstance(*this); +} + +bool QScriptValueImpl::implementsHasInstance() const +{ + Q_ASSERT(isObject()); + if (isFunction()) + return true; + if (QScriptClassData *odata = classInfo()->data()) { + return odata->implementsHasInstance(*this); + } + return false; +} + +bool QScriptValueImpl::hasInstance(const QScriptValueImpl &value) const +{ + Q_ASSERT(isObject()); + + if (QScriptClassData *odata = classInfo()->data()) { + if (odata->implementsHasInstance(*this)) + return odata->hasInstance(*this, value); + } + if (!isFunction()) + return false; + + // [[HasInstance] for function objects + + if (!value.isObject()) + return false; + + QScriptEnginePrivate *eng = engine(); + QScriptValueImpl proto = property(eng->idTable()->id_prototype); + if (!proto.isObject()) { + QScriptContextPrivate *ctx = eng->currentContext(); + ctx->throwTypeError(QLatin1String("instanceof: 'prototype' property is not an object")); + return false; + } + + QScriptObject *target = proto.m_object_value; + QScriptValueImpl v = value; + while (true) { + v = v.prototype(); + if (!v.isObject()) + break; + if (target == v.m_object_value) + return true; + } + return false; +} + +bool QScriptValueImpl::resolve_helper(QScriptNameIdImpl *nameId, QScript::Member *member, + QScriptValueImpl *object, QScriptValue::ResolveFlags mode, + QScript::AccessMode access) const +{ + QScriptObject *object_data = m_object_value; + + QScriptEnginePrivate *eng_p = engine(); + + if (nameId == eng_p->idTable()->id___proto__) { + member->native(nameId, /*id=*/0, QScriptValue::Undeletable); + *object = *this; + return true; + } + + // If not found anywhere else, search in the extra members. + if (QScriptClassData *odata = classInfo()->data()) { + *object = *this; + + if (odata->resolve(*this, nameId, member, object, access)) + return true; + } + + if (mode & QScriptValue::ResolvePrototype) { + // For values and other non object based types, search in class's prototype + const QScriptValueImpl &proto = object_data->m_prototype; + + if (proto.isObject() + && proto.resolve(nameId, member, object, mode, access)) { + return true; + } + } + + if ((mode & QScriptValue::ResolveScope) && object_data->m_scope.isValid()) + return object_data->m_scope.resolve(nameId, member, object, mode, access); + + return false; +} + +void QScriptValueImpl::setProperty(QScriptNameIdImpl *nameId, + const QScriptValueImpl &value, + const QScriptValue::PropertyFlags &flags) +{ + if (!isObject()) + return; + + QScriptValueImpl base; + QScript::Member member; + + QScriptValue::ResolveFlags mode = QScriptValue::ResolveLocal; + // if we are not setting a setter or getter, look in prototype too + if (!(flags & (QScriptValue::PropertyGetter | QScriptValue::PropertySetter))) + mode |= QScriptValue::ResolvePrototype; + + if (resolve(nameId, &member, &base, mode, QScript::ReadWrite)) { + // we resolved an existing property with that name + if (flags & (QScriptValue::PropertyGetter | QScriptValue::PropertySetter)) { + // setting the getter or setter of a property in this object + if (member.isNativeProperty()) { + if (value.isValid()) { + qWarning("QScriptValue::setProperty() failed: " + "cannot set getter or setter of native property `%s'", + qPrintable(nameId->s)); + } + return; + } + if (member.isSetter()) { + // the property we resolved is a setter + if (!(flags & QScriptValue::PropertySetter) && !member.isGetter()) { + // find the getter, if not, create one + if (!m_object_value->findGetter(&member)) { + if (!value.isValid()) + return; // don't create property for invalid value + createMember(nameId, &member, flags); + } + } + } else if (member.isGetter()) { + // the property we resolved is a getter + if (!(flags & QScriptValue::PropertyGetter)) { + // find the setter, if not, create one + if (!m_object_value->findSetter(&member)) { + if (!value.isValid()) + return; // don't create property for invalid value + createMember(nameId, &member, flags); + } + } + } else { + // the property is a normal property -- change the flags + uint newFlags = flags & ~QScript::Member::InternalRange; + newFlags |= QScript::Member::ObjectProperty; + member.resetFlags(newFlags); + base.m_object_value->m_members[member.id()].resetFlags(newFlags); + } + Q_ASSERT(member.isValid()); + if (!value.isValid()) { + // remove the property + removeMember(member); + return; + } + } else { + // setting the value + if (member.isGetterOrSetter()) { + // call the setter + QScriptValueImpl setter; + if (member.isObjectProperty() && !member.isSetter()) { + if (!base.m_object_value->findSetter(&member)) { + qWarning("QScriptValue::setProperty() failed: " + "property '%s' has a getter but no setter", + qPrintable(nameId->s)); + return; + } + } + base.get(member, &setter); + setter.call(*this, QScriptValueImplList() << value); + return; + } else { + if (base.m_object_value != m_object_value) { + if (!value.isValid()) + return; // don't create property for invalid value + createMember(nameId, &member, flags); + base = *this; + } else { + if (!value.isValid()) { + // remove the property + removeMember(member); + return; + } + } + if (flags != QScriptValue::KeepExistingFlags) { + // change flags + if (member.isNativeProperty()) { + qWarning("QScriptValue::setProperty(%s): " + "cannot change flags of a native property", + qPrintable(nameId->s)); + } else { + uint newFlags = member.flags() & QScript::Member::InternalRange; + newFlags |= flags & ~QScript::Member::InternalRange; + base.m_object_value->m_members[member.id()].resetFlags(newFlags); + } + } + } + } + } else { + // property does not exist + if (!value.isValid()) + return; // don't create property for invalid value + createMember(nameId, &member, flags & ~QScript::Member::InternalRange); + base = *this; + } + + base.put(member, value); +} + +QVariant QScriptValueImpl::toVariant() const +{ + switch (m_type) { + case QScript::InvalidType: + return QVariant(); + + case QScript::UndefinedType: + case QScript::NullType: + case QScript::PointerType: + case QScript::ReferenceType: + break; + + case QScript::BooleanType: + return QVariant(m_bool_value); + + case QScript::IntegerType: + return QVariant(m_int_value); + + case QScript::NumberType: + return QVariant(m_number_value); + + case QScript::StringType: + return QVariant(m_string_value->s); + + case QScript::LazyStringType: + return QVariant(*m_lazy_string_value); + + case QScript::ObjectType: + if (isDate()) + return QVariant(toDateTime()); + +#ifndef QT_NO_REGEXP + if (isRegExp()) + return QVariant(toRegExp()); +#endif + if (isVariant()) + return variantValue(); + +#ifndef QT_NO_QOBJECT + if (isQObject()) + return qVariantFromValue(toQObject()); +#endif + + QScriptValueImpl v = engine()->toPrimitive(*this); + if (!v.isObject()) + return v.toVariant(); + break; + } // switch + return QVariant(); +} + +QDebug &operator<<(QDebug &d, const QScriptValueImpl &object) +{ + d.nospace() << "QScriptValue("; + + switch (object.type()) { + case QScript::InvalidType: + d.nospace() << "Invalid)"; + return d; + + case QScript::BooleanType: + d.nospace() << "bool=" << object.toBoolean(); + break; + + case QScript::IntegerType: + d.nospace() << "int=" << object.toInt32(); + break; + + case QScript::NumberType: + d.nospace() << "qsreal=" << object.toNumber(); + break; + + case QScript::LazyStringType: + case QScript::StringType: + d.nospace() << "string=" << object.toString(); + break; + + case QScript::ReferenceType: + d.nospace() << "reference"; + break; + + case QScript::NullType: + d.nospace() << "null"; + break; + + case QScript::UndefinedType: + d.nospace() << "undefined"; + break; + + case QScript::PointerType: + d.nospace() << "pointer"; + break; + + case QScript::ObjectType: + d.nospace() << object.classInfo()->name() << ",{"; + QScriptObject *od = object.objectValue(); + for (int i=0; i<od->memberCount(); ++i) { + if (i != 0) + d << ","; + + QScript::Member m; + od->member(i, &m); + + if (m.isValid() && m.isObjectProperty()) { + d << object.engine()->toString(m.nameId()); + QScriptValueImpl o; + od->get(m, &o); + d.nospace() << QLatin1String(":") + << (o.classInfo() + ? o.classInfo()->name() + : QLatin1String("?")); + } + } + + d.nospace() << "} scope={"; + QScriptValueImpl scope = object.scope(); + while (scope.isValid()) { + Q_ASSERT(scope.isObject()); + d.nospace() << " " << scope.objectValue(); + scope = scope.scope(); + } + d.nospace() << "}"; + break; + } + + d << ")"; + return d; +} + +void QScriptValueImpl::destroyObjectData() +{ + Q_ASSERT(isObject()); + m_object_value->finalizeData(); +} + +bool QScriptValueImpl::isMarked(int generation) const +{ + if (isString()) + return (m_string_value->used != 0); + else if (isObject()) { + QScript::GCBlock *block = QScript::GCBlock::get(m_object_value); + return (block->generation == generation); + } + return false; +} + +QT_END_NAMESPACE + +#endif // QT_NO_SCRIPT |