/**************************************************************************** ** ** Copyright (C) 2015 The Qt Company Ltd. ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the QtScript module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL-ONLY$ ** GNU Lesser General Public License Usage ** 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. ** ** If you have questions regarding the use of this file, please contact ** us via http://www.qt.io/contact-us/. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "config.h" #include "qscriptqobject_p.h" #include #include #include #include #include "../api/qscriptengine_p.h" #include "../api/qscriptable_p.h" #include "../api/qscriptcontext_p.h" #include "qscriptfunction_p.h" #include "qscriptactivationobject_p.h" #include "Error.h" #include "PrototypeFunction.h" #include "NativeFunctionWrapper.h" #include "PropertyNameArray.h" #include "JSFunction.h" #include "JSString.h" #include "JSValue.h" #include "JSArray.h" #include "RegExpObject.h" #include "RegExpConstructor.h" namespace JSC { QT_USE_NAMESPACE ASSERT_CLASS_FITS_IN_CELL(QScript::QObjectPrototype); ASSERT_CLASS_FITS_IN_CELL(QScript::QMetaObjectWrapperObject); ASSERT_CLASS_FITS_IN_CELL(QScript::QMetaObjectPrototype); ASSERT_CLASS_FITS_IN_CELL(QScript::QtFunction); ASSERT_CLASS_FITS_IN_CELL(QScript::QtPropertyFunction); } QT_BEGIN_NAMESPACE namespace QScript { struct QObjectConnection { uint marked:1; uint slotIndex:31; JSC::JSValue receiver; JSC::JSValue slot; JSC::JSValue senderWrapper; QObjectConnection(int i, JSC::JSValue r, JSC::JSValue s, JSC::JSValue sw) : marked(false), slotIndex(i), receiver(r), slot(s), senderWrapper(sw) {} QObjectConnection() : marked(false), slotIndex(0) {} bool hasTarget(JSC::JSValue r, JSC::JSValue s) const { if ((r && r.isObject()) != (receiver && receiver.isObject())) return false; if (((r && r.isObject()) && (receiver && receiver.isObject())) && (r != receiver)) { return false; } return (s == slot); } bool hasWeaklyReferencedSender() const { if (senderWrapper) { Q_ASSERT(senderWrapper.inherits(&QScriptObject::info)); QScriptObject *scriptObject = static_cast(JSC::asObject(senderWrapper)); if (!JSC::Heap::isCellMarked(scriptObject)) { QScriptObjectDelegate *delegate = scriptObject->delegate(); Q_ASSERT(delegate && (delegate->type() == QScriptObjectDelegate::QtObject)); QObjectDelegate *inst = static_cast(delegate); if ((inst->ownership() == QScriptEngine::ScriptOwnership) || ((inst->ownership() == QScriptEngine::AutoOwnership) && !inst->hasParent())) { return true; } } } return false; } void mark(JSC::MarkStack& markStack) { Q_ASSERT(!marked); if (senderWrapper) markStack.append(senderWrapper); if (receiver) markStack.append(receiver); if (slot) markStack.append(slot); marked = true; } }; class QObjectConnectionManager: public QObject { Q_OBJECT_FAKE public: QObjectConnectionManager(QScriptEnginePrivate *engine); ~QObjectConnectionManager(); bool addSignalHandler(QObject *sender, int signalIndex, JSC::JSValue receiver, JSC::JSValue slot, JSC::JSValue senderWrapper, Qt::ConnectionType type); bool removeSignalHandler(QObject *sender, int signalIndex, JSC::JSValue receiver, JSC::JSValue slot); void execute(int slotIndex, void **argv); void clearMarkBits(); int mark(JSC::MarkStack&); private: QScriptEnginePrivate *engine; int slotCounter; QVector > connections; }; static bool hasMethodAccess(const QMetaMethod &method, int index, const QScriptEngine::QObjectWrapOptions &opt) { static const int deleteLaterIndex = QObject::staticMetaObject.indexOfMethod("deleteLater()"); return (method.access() != QMetaMethod::Private) && ((index != deleteLaterIndex) || !(opt & QScriptEngine::ExcludeDeleteLater)) && (!(opt & QScriptEngine::ExcludeSlots) || (method.methodType() != QMetaMethod::Slot)); } static bool isEnumerableMetaProperty(const QMetaProperty &prop, const QMetaObject *mo, int index) { return prop.isScriptable() && prop.isValid() // the following lookup is to ensure that we have the // "most derived" occurrence of the property with this name && (mo->indexOfProperty(prop.name()) == index); } static const bool GeneratePropertyFunctions = true; static unsigned flagsForMetaProperty(const QMetaProperty &prop) { return (JSC::DontDelete | (!prop.isWritable() ? unsigned(JSC::ReadOnly) : unsigned(0)) | (GeneratePropertyFunctions ? unsigned(JSC::Getter | JSC::Setter) : unsigned(0)) | QObjectMemberAttribute); } static int indexOfMetaEnum(const QMetaObject *meta, const QByteArray &str) { QByteArray scope; QByteArray name; int scopeIdx = str.lastIndexOf("::"); if (scopeIdx != -1) { scope = str.left(scopeIdx); name = str.mid(scopeIdx + 2); } else { name = str; } for (int i = meta->enumeratorCount() - 1; i >= 0; --i) { QMetaEnum m = meta->enumerator(i); if ((m.name() == name) && (scope.isEmpty() || (m.scope() == scope))) return i; } return -1; } static inline QScriptable *scriptableFromQObject(QObject *qobj) { void *ptr = qobj->qt_metacast("QScriptable"); return reinterpret_cast(ptr); } QtFunction::QtFunction(JSC::JSValue object, int initialIndex, bool maybeOverloaded, JSC::JSGlobalData *data, WTF::PassRefPtr sid, const JSC::Identifier &ident) : JSC::InternalFunction(data, sid, ident), data(new Data(object, initialIndex, maybeOverloaded)) { } QtFunction::~QtFunction() { delete data; } JSC::CallType QtFunction::getCallData(JSC::CallData &callData) { callData.native.function = call; return JSC::CallTypeHost; } void QtFunction::markChildren(JSC::MarkStack& markStack) { if (data->object) markStack.append(data->object); JSC::InternalFunction::markChildren(markStack); } QScriptObject *QtFunction::wrapperObject() const { Q_ASSERT(JSC::asObject(data->object)->inherits(&QScriptObject::info)); return static_cast(JSC::asObject(data->object)); } QObject *QtFunction::qobject() const { QScriptObject *scriptObject = wrapperObject(); QScriptObjectDelegate *delegate = scriptObject->delegate(); Q_ASSERT(delegate && (delegate->type() == QScriptObjectDelegate::QtObject)); return static_cast(delegate)->value(); } const QMetaObject *QtFunction::metaObject() const { QObject *qobj = qobject(); if (!qobj) return 0; return qobj->metaObject(); } int QtFunction::initialIndex() const { return data->initialIndex; } bool QtFunction::maybeOverloaded() const { return data->maybeOverloaded; } int QtFunction::mostGeneralMethod(QMetaMethod *out) const { const QMetaObject *meta = metaObject(); if (!meta) return -1; int index = initialIndex(); QMetaMethod method = meta->method(index); if (maybeOverloaded() && (method.attributes() & QMetaMethod::Cloned)) { // find the most general method do { method = meta->method(--index); } while (method.attributes() & QMetaMethod::Cloned); } if (out) *out = method; return index; } QList QScript::QtFunction::overloadedIndexes() const { if (!maybeOverloaded()) return QList(); QList result; const QMetaObject *meta = metaObject(); QMetaMethod method = meta->method(initialIndex()); QByteArray name = method.name(); for (int index = mostGeneralMethod() - 1; index >= 0; --index) { if (meta->method(index).name() == name) result.append(index); } return result; } class QScriptMetaType { public: enum Kind { Invalid, Variant, MetaType, Unresolved, MetaEnum }; inline QScriptMetaType() : m_kind(Invalid) { } inline Kind kind() const { return m_kind; } int typeId() const; inline bool isValid() const { return (m_kind != Invalid); } inline bool isVariant() const { return (m_kind == Variant); } inline bool isMetaType() const { return (m_kind == MetaType); } inline bool isUnresolved() const { return (m_kind == Unresolved); } inline bool isMetaEnum() const { return (m_kind == MetaEnum); } QByteArray name() const; inline int enumeratorIndex() const { Q_ASSERT(isMetaEnum()); return m_typeId; } inline bool operator==(const QScriptMetaType &other) const { return (m_kind == other.m_kind) && (m_typeId == other.m_typeId); } static inline QScriptMetaType variant() { return QScriptMetaType(Variant); } static inline QScriptMetaType metaType(int typeId, const QByteArray &name) { return QScriptMetaType(MetaType, typeId, name); } static inline QScriptMetaType metaEnum(int enumIndex, const QByteArray &name) { return QScriptMetaType(MetaEnum, enumIndex, name); } static inline QScriptMetaType unresolved(const QByteArray &name) { return QScriptMetaType(Unresolved, /*typeId=*/0, name); } private: inline QScriptMetaType(Kind kind, int typeId = 0, const QByteArray &name = QByteArray()) : m_kind(kind), m_typeId(typeId), m_name(name) { } Kind m_kind; int m_typeId; QByteArray m_name; }; int QScriptMetaType::typeId() const { if (isVariant()) return QMetaType::QVariant; return isMetaEnum() ? QMetaType::Int : m_typeId; } QByteArray QScriptMetaType::name() const { if (!m_name.isEmpty()) return m_name; else if (m_kind == Variant) return "QVariant"; return QMetaType::typeName(typeId()); } class QScriptMetaMethod { public: inline QScriptMetaMethod() { } inline QScriptMetaMethod(const QVector &types) : m_types(types), m_firstUnresolvedIndex(-1) { QVector::const_iterator it; for (it = m_types.constBegin(); it != m_types.constEnd(); ++it) { if ((*it).kind() == QScriptMetaType::Unresolved) { m_firstUnresolvedIndex = it - m_types.constBegin(); break; } } } inline bool isValid() const { return !m_types.isEmpty(); } inline QScriptMetaType returnType() const { return m_types.at(0); } inline int argumentCount() const { return m_types.count() - 1; } inline QScriptMetaType argumentType(int arg) const { return m_types.at(arg + 1); } inline bool fullyResolved() const { return m_firstUnresolvedIndex == -1; } inline bool hasUnresolvedReturnType() const { return (m_firstUnresolvedIndex == 0); } inline int firstUnresolvedIndex() const { return m_firstUnresolvedIndex; } inline int count() const { return m_types.count(); } inline QScriptMetaType type(int index) const { return m_types.at(index); } inline QVector types() const { return m_types; } private: QVector m_types; int m_firstUnresolvedIndex; }; struct QScriptMetaArguments { int matchDistance; int index; QScriptMetaMethod method; QVarLengthArray args; inline QScriptMetaArguments(int dist, int idx, const QScriptMetaMethod &mtd, const QVarLengthArray &as) : matchDistance(dist), index(idx), method(mtd), args(as) { } inline QScriptMetaArguments() : index(-1) { } inline bool isValid() const { return (index != -1); } }; static QMetaMethod metaMethod(const QMetaObject *meta, QMetaMethod::MethodType type, int index) { if (type != QMetaMethod::Constructor) return meta->method(index); else return meta->constructor(index); } /*! \internal Derives the actual method to call based on the script arguments, \a scriptArgs, and delegates it to the given \a delegate. */ template static JSC::JSValue delegateQtMethod(JSC::ExecState *exec, QMetaMethod::MethodType callType, const JSC::ArgList &scriptArgs, const QMetaObject *meta, int initialIndex, bool maybeOverloaded, Delegate &delegate) { QScriptMetaMethod chosenMethod; int chosenIndex = -1; QVarLengthArray args; QVector candidates; QVector unresolved; QVector tooFewArgs; QVector conversionFailed; int index; QByteArray methodName; exec->clearException(); for (index = initialIndex; index >= 0; --index) { QMetaMethod method = metaMethod(meta, callType, index); if (index == initialIndex) methodName = method.name(); else if (method.name() != methodName) continue; QList parameterTypeNames = method.parameterTypes(); QVector types; types.resize(1 + parameterTypeNames.size()); QScriptMetaType *typesData = types.data(); // resolve return type QByteArray returnTypeName = method.typeName(); int rtype = method.returnType(); if ((rtype == QMetaType::UnknownType) && !returnTypeName.isEmpty()) { int enumIndex = indexOfMetaEnum(meta, returnTypeName); if (enumIndex != -1) typesData[0] = QScriptMetaType::metaEnum(enumIndex, returnTypeName); else typesData[0] = QScriptMetaType::unresolved(returnTypeName); } else { if (callType == QMetaMethod::Constructor) typesData[0] = QScriptMetaType::metaType(QMetaType::QObjectStar, "QObject*"); else if (rtype == QMetaType::QVariant) typesData[0] = QScriptMetaType::variant(); else typesData[0] = QScriptMetaType::metaType(rtype, returnTypeName); } // resolve argument types for (int i = 0; i < parameterTypeNames.count(); ++i) { QByteArray argTypeName = parameterTypeNames.at(i); int atype = QMetaType::type(argTypeName); if (atype == QMetaType::UnknownType) { int enumIndex = indexOfMetaEnum(meta, argTypeName); if (enumIndex != -1) typesData[1 + i] = QScriptMetaType::metaEnum(enumIndex, argTypeName); else typesData[1 + i] = QScriptMetaType::unresolved(argTypeName); } else if (atype == QMetaType::QVariant) { typesData[1 + i] = QScriptMetaType::variant(); } else { typesData[1 + i] = QScriptMetaType::metaType(atype, argTypeName); } } QScriptMetaMethod mtd = QScriptMetaMethod(types); if (int(scriptArgs.size()) < mtd.argumentCount()) { tooFewArgs.append(index); continue; } if (!mtd.fullyResolved()) { // remember it so we can give an error message later, if necessary unresolved.append(QScriptMetaArguments(/*matchDistance=*/INT_MAX, index, mtd, QVarLengthArray())); if (mtd.hasUnresolvedReturnType()) continue; } if (args.count() != mtd.count()) args.resize(mtd.count()); if (rtype != QMetaType::Void) { // initialize the result args[0] = QVariant(rtype, (void *)0); } // try to convert arguments bool converted = true; int matchDistance = 0; for (int i = 0; converted && i < mtd.argumentCount(); ++i) { JSC::JSValue actual; if (i < (int)scriptArgs.size()) actual = scriptArgs.at(i); else actual = JSC::jsUndefined(); QScriptMetaType argType = mtd.argumentType(i); int tid = -1; QVariant v; if (argType.isUnresolved()) { v = QVariant(QMetaType::QObjectStar, (void *)0); converted = QScriptEnginePrivate::convertToNativeQObject( exec, actual, argType.name(), reinterpret_cast(v.data())); } else if (argType.isVariant()) { if (QScriptEnginePrivate::isVariant(actual)) { v = QScriptEnginePrivate::variantValue(actual); } else { v = QScriptEnginePrivate::toVariant(exec, actual); converted = v.isValid() || actual.isUndefined() || actual.isNull(); } } else { tid = argType.typeId(); v = QVariant(tid, (void *)0); converted = QScriptEnginePrivate::convertValue(exec, actual, tid, v.data()); if (exec->hadException()) return exec->exception(); } if (!converted) { if (QScriptEnginePrivate::isVariant(actual)) { if (tid == -1) tid = argType.typeId(); QVariant vv = QScriptEnginePrivate::variantValue(actual); if (vv.canConvert(tid)) { v = vv; converted = v.convert(tid); if (converted && (vv.userType() != tid)) matchDistance += 10; } else { QByteArray vvTypeName = vv.typeName(); if (vvTypeName.endsWith('*') && (vvTypeName.left(vvTypeName.size()-1) == argType.name())) { v = QVariant(tid, *reinterpret_cast(vv.data())); converted = true; matchDistance += 10; } } } else if (actual.isNumber() || actual.isString()) { // see if it's an enum value QMetaEnum m; if (argType.isMetaEnum()) { m = meta->enumerator(argType.enumeratorIndex()); } else { int mi = indexOfMetaEnum(meta, argType.name()); if (mi != -1) m = meta->enumerator(mi); } if (m.isValid()) { if (actual.isNumber()) { int ival = QScriptEnginePrivate::toInt32(exec, actual); if (m.valueToKey(ival) != 0) { v.setValue(ival); converted = true; matchDistance += 10; } } else { JSC::UString sval = QScriptEnginePrivate::toString(exec, actual); int ival = m.keyToValue(convertToLatin1(sval)); if (ival != -1) { v.setValue(ival); converted = true; matchDistance += 10; } } } } } else { // determine how well the conversion matched if (actual.isNumber()) { switch (tid) { case QMetaType::Double: // perfect break; case QMetaType::Float: matchDistance += 1; break; case QMetaType::LongLong: case QMetaType::ULongLong: matchDistance += 2; break; case QMetaType::Long: case QMetaType::ULong: matchDistance += 3; break; case QMetaType::Int: case QMetaType::UInt: matchDistance += 4; break; case QMetaType::Short: case QMetaType::UShort: matchDistance += 5; break; case QMetaType::Char: case QMetaType::UChar: matchDistance += 6; break; default: matchDistance += 10; break; } } else if (actual.isString()) { switch (tid) { case QMetaType::QString: // perfect break; default: matchDistance += 10; break; } } else if (actual.isBoolean()) { switch (tid) { case QMetaType::Bool: // perfect break; default: matchDistance += 10; break; } } else if (QScriptEnginePrivate::isDate(actual)) { switch (tid) { case QMetaType::QDateTime: // perfect break; case QMetaType::QDate: matchDistance += 1; break; case QMetaType::QTime: matchDistance += 2; break; default: matchDistance += 10; break; } } else if (QScriptEnginePrivate::isRegExp(actual)) { switch (tid) { case QMetaType::QRegExp: // perfect break; default: matchDistance += 10; break; } } else if (QScriptEnginePrivate::isVariant(actual)) { if (argType.isVariant() || (QScriptEnginePrivate::toVariant(exec, actual).userType() == tid)) { // perfect } else { matchDistance += 10; } } else if (QScriptEnginePrivate::isArray(actual)) { switch (tid) { case QMetaType::QStringList: case QMetaType::QVariantList: matchDistance += 5; break; default: matchDistance += 10; break; } } else if (QScriptEnginePrivate::isQObject(actual)) { switch (tid) { case QMetaType::QObjectStar: // perfect break; default: if (!(QMetaType::typeFlags(tid) & QMetaType::PointerToQObject)) matchDistance += 10; break; } } else if (actual.isNull()) { switch (tid) { case QMetaType::VoidStar: case QMetaType::QObjectStar: // perfect break; default: if (!argType.name().endsWith('*')) matchDistance += 10; break; } } else { matchDistance += 10; } } if (converted) args[i+1] = v; } if (converted) { if ((scriptArgs.size() == (size_t)mtd.argumentCount()) && (matchDistance == 0)) { // perfect match, use this one chosenMethod = mtd; chosenIndex = index; break; } else { bool redundant = false; if ((callType != QMetaMethod::Constructor) && (index < meta->methodOffset())) { // it is possible that a virtual method is redeclared in a subclass, // in which case we want to ignore the superclass declaration for (int i = 0; i < candidates.size(); ++i) { const QScriptMetaArguments &other = candidates.at(i); if (mtd.types() == other.method.types()) { redundant = true; break; } } } if (!redundant) { QScriptMetaArguments metaArgs(matchDistance, index, mtd, args); if (candidates.isEmpty()) { candidates.append(metaArgs); } else { const QScriptMetaArguments &otherArgs = candidates.at(0); if ((args.count() > otherArgs.args.count()) || ((args.count() == otherArgs.args.count()) && (matchDistance <= otherArgs.matchDistance))) { candidates.prepend(metaArgs); } else { candidates.append(metaArgs); } } } } } else if (mtd.fullyResolved()) { conversionFailed.append(index); } if (!maybeOverloaded) break; } JSC::JSValue result; if ((chosenIndex == -1) && candidates.isEmpty()) { // context->calleeMetaIndex = initialIndex; //#ifndef Q_SCRIPT_NO_EVENT_NOTIFY // engine->notifyFunctionEntry(context); //#endif QString funName = QString::fromLatin1(methodName); if (!conversionFailed.isEmpty()) { QString message = QString::fromLatin1("incompatible type of argument(s) in call to %0(); candidates were\n") .arg(funName); for (int i = 0; i < conversionFailed.size(); ++i) { if (i > 0) message += QLatin1String("\n"); QMetaMethod mtd = metaMethod(meta, callType, conversionFailed.at(i)); message += QString::fromLatin1(" %0").arg(QString::fromLatin1(mtd.methodSignature().constData())); } result = JSC::throwError(exec, JSC::TypeError, message); } else if (!unresolved.isEmpty()) { QScriptMetaArguments argsInstance = unresolved.first(); int unresolvedIndex = argsInstance.method.firstUnresolvedIndex(); Q_ASSERT(unresolvedIndex != -1); QScriptMetaType unresolvedType = argsInstance.method.type(unresolvedIndex); QString unresolvedTypeName = QString::fromLatin1(unresolvedType.name()); QString message = QString::fromLatin1("cannot call %0(): ") .arg(funName); if (unresolvedIndex > 0) { message.append(QString::fromLatin1("argument %0 has unknown type `%1'"). arg(unresolvedIndex).arg(unresolvedTypeName)); } else { message.append(QString::fromLatin1("unknown return type `%0'") .arg(unresolvedTypeName)); } message.append(QString::fromLatin1(" (register the type with qScriptRegisterMetaType())")); result = JSC::throwError(exec, JSC::TypeError, message); } else { QString message = QString::fromLatin1("too few arguments in call to %0(); candidates are\n") .arg(funName); for (int i = 0; i < tooFewArgs.size(); ++i) { if (i > 0) message += QLatin1String("\n"); QMetaMethod mtd = metaMethod(meta, callType, tooFewArgs.at(i)); message += QString::fromLatin1(" %0").arg(QString::fromLatin1(mtd.methodSignature().constData())); } result = JSC::throwError(exec, JSC::SyntaxError, message); } } else { if (chosenIndex == -1) { QScriptMetaArguments metaArgs = candidates.at(0); if ((candidates.size() > 1) && (metaArgs.args.count() == candidates.at(1).args.count()) && (metaArgs.matchDistance == candidates.at(1).matchDistance)) { // ambiguous call QString message = QString::fromLatin1("ambiguous call of overloaded function %0(); candidates were\n") .arg(QLatin1String(methodName)); for (int i = 0; i < candidates.size(); ++i) { if (i > 0) message += QLatin1String("\n"); QMetaMethod mtd = metaMethod(meta, callType, candidates.at(i).index); message += QString::fromLatin1(" %0").arg(QString::fromLatin1(mtd.methodSignature().constData())); } result = JSC::throwError(exec, JSC::TypeError, message); } else { chosenMethod = metaArgs.method; chosenIndex = metaArgs.index; args = metaArgs.args; } } if (chosenIndex != -1) result = delegate(exec, callType, meta, chosenMethod, chosenIndex, args); } return result; } struct QtMethodCaller { QtMethodCaller(QObject *o) : thisQObject(o) {} JSC::JSValue operator()(JSC::ExecState *exec, QMetaMethod::MethodType callType, const QMetaObject *meta, const QScriptMetaMethod &chosenMethod, int chosenIndex, const QVarLengthArray &args) { JSC::JSValue result; QVarLengthArray array(args.count()); void **params = array.data(); for (int i = 0; i < args.count(); ++i) { const QVariant &v = args[i]; switch (chosenMethod.type(i).kind()) { case QScriptMetaType::Variant: params[i] = const_cast(&v); break; case QScriptMetaType::MetaType: case QScriptMetaType::MetaEnum: case QScriptMetaType::Unresolved: params[i] = const_cast(v.constData()); break; default: Q_ASSERT(0); } } QScriptable *scriptable = 0; if (thisQObject) scriptable = scriptableFromQObject(thisQObject); QScriptEngine *oldEngine = 0; QScriptEnginePrivate *engine = QScript::scriptEngineFromExec(exec); if (scriptable) { oldEngine = QScriptablePrivate::get(scriptable)->engine; QScriptablePrivate::get(scriptable)->engine = QScriptEnginePrivate::get(engine); } // ### fixme //#ifndef Q_SCRIPT_NO_EVENT_NOTIFY // engine->notifyFunctionEntry(context); //#endif if (callType == QMetaMethod::Constructor) { Q_ASSERT(meta != 0); meta->static_metacall(QMetaObject::CreateInstance, chosenIndex, params); } else { QMetaObject::metacall(thisQObject, QMetaObject::InvokeMetaMethod, chosenIndex, params); } if (scriptable) QScriptablePrivate::get(scriptable)->engine = oldEngine; if (exec->hadException()) { result = exec->exception() ; // propagate } else { QScriptMetaType retType = chosenMethod.returnType(); if (retType.isVariant()) { result = QScriptEnginePrivate::jscValueFromVariant(exec, *(QVariant *)params[0]); } else if (retType.typeId() != QMetaType::Void) { result = QScriptEnginePrivate::create(exec, retType.typeId(), params[0]); if (!result) result = engine->newVariant(QVariant(retType.typeId(), params[0])); } else { result = JSC::jsUndefined(); } } return result; } private: QObject *thisQObject; }; static JSC::JSValue callQtMethod(JSC::ExecState *exec, QMetaMethod::MethodType callType, QObject *thisQObject, const JSC::ArgList &scriptArgs, const QMetaObject *meta, int initialIndex, bool maybeOverloaded) { QtMethodCaller caller(thisQObject); return delegateQtMethod(exec, callType, scriptArgs, meta, initialIndex, maybeOverloaded, caller); } JSC::JSValue QtFunction::execute(JSC::ExecState *exec, JSC::JSValue thisValue, const JSC::ArgList &scriptArgs) { Q_ASSERT(data->object.inherits(&QScriptObject::info)); QScriptObject *scriptObject = static_cast(JSC::asObject(data->object)); QScriptObjectDelegate *delegate = scriptObject->delegate(); Q_ASSERT(delegate && (delegate->type() == QScriptObjectDelegate::QtObject)); QObject *qobj = static_cast(delegate)->value(); if (!qobj) return JSC::throwError(exec, JSC::GeneralError, QString::fromLatin1("cannot call function of deleted QObject")); QScriptEnginePrivate *engine = scriptEngineFromExec(exec); const QMetaObject *meta = qobj->metaObject(); QObject *thisQObject = 0; thisValue = engine->toUsableValue(thisValue); if (thisValue.inherits(&QScriptObject::info)) { delegate = static_cast(JSC::asObject(thisValue))->delegate(); if (delegate && (delegate->type() == QScriptObjectDelegate::QtObject)) thisQObject = static_cast(delegate)->value(); } if (!thisQObject) thisQObject = qobj; // ### TypeError if (!meta->cast(thisQObject)) { // invoking a function in the prototype thisQObject = qobj; } return callQtMethod(exec, QMetaMethod::Method, thisQObject, scriptArgs, meta, data->initialIndex, data->maybeOverloaded); } const JSC::ClassInfo QtFunction::info = { "QtFunction", &InternalFunction::info, 0, 0 }; JSC::JSValue JSC_HOST_CALL QtFunction::call(JSC::ExecState *exec, JSC::JSObject *callee, JSC::JSValue thisValue, const JSC::ArgList &args) { if (!callee->inherits(&QtFunction::info)) return throwError(exec, JSC::TypeError, "callee is not a QtFunction object"); QtFunction *qfun = static_cast(callee); QScriptEnginePrivate *eng_p = scriptEngineFromExec(exec); JSC::ExecState *previousFrame = eng_p->currentFrame; eng_p->currentFrame = exec; eng_p->pushContext(exec, thisValue, args, callee); JSC::JSValue result = qfun->execute(eng_p->currentFrame, thisValue, args); eng_p->popContext(); eng_p->currentFrame = previousFrame; return result; } struct QtMethodIndexReturner { JSC::JSValue operator()(JSC::ExecState *exec, QMetaMethod::MethodType, const QMetaObject *, const QScriptMetaMethod &, int chosenIndex, const QVarLengthArray &) { return JSC::jsNumber(exec, chosenIndex); } }; /*! \internal Returns the specific index of the meta-method that was used in the function call represented by the given \a context. If the method is overloaded, the actual parameters that were passed to the function are used to derive the selected index, matching the behavior of callQtMethod(). */ int QtFunction::specificIndex(const QScriptContext *context) const { if (!maybeOverloaded()) return initialIndex(); JSC::ExecState *exec = const_cast(QScriptEnginePrivate::frameForContext(context)); int argCount = exec->argumentCount(); // Create arguments list wrapper; this logic must match // JITStubs.cpp op_call_NotJSFunction, and Interpreter.cpp op_call JSC::Register* argv = exec->registers() - JSC::RegisterFile::CallFrameHeaderSize - argCount; JSC::ArgList args(argv + 1, argCount - 1); QtMethodIndexReturner returner; JSC::JSValue result = delegateQtMethod( exec, QMetaMethod::Method, args, metaObject(), initialIndex(), maybeOverloaded(), returner); if (exec->hadException() || !result || !result.isInt32()) return initialIndex(); return result.asInt32(); } const JSC::ClassInfo QtPropertyFunction::info = { "QtPropertyFunction", &InternalFunction::info, 0, 0 }; QtPropertyFunction::QtPropertyFunction(const QMetaObject *meta, int index, JSC::JSGlobalData *data, WTF::PassRefPtr sid, const JSC::Identifier &ident) : JSC::InternalFunction(data, sid, ident), data(new Data(meta, index)) { } QtPropertyFunction::~QtPropertyFunction() { delete data; } JSC::CallType QtPropertyFunction::getCallData(JSC::CallData &callData) { callData.native.function = call; return JSC::CallTypeHost; } JSC::JSValue JSC_HOST_CALL QtPropertyFunction::call( JSC::ExecState *exec, JSC::JSObject *callee, JSC::JSValue thisValue, const JSC::ArgList &args) { if (!callee->inherits(&QtPropertyFunction::info)) return throwError(exec, JSC::TypeError, "callee is not a QtPropertyFunction object"); QtPropertyFunction *qfun = static_cast(callee); return qfun->execute(exec, thisValue, args); } JSC::JSValue QtPropertyFunction::execute(JSC::ExecState *exec, JSC::JSValue thisValue, const JSC::ArgList &args) { JSC::JSValue result = JSC::jsUndefined(); QScriptEnginePrivate *engine = scriptEngineFromExec(exec); JSC::ExecState *previousFrame = engine->currentFrame; engine->currentFrame = exec; JSC::JSValue qobjectValue = engine->toUsableValue(thisValue); QObject *qobject = QScriptEnginePrivate::toQObject(exec, qobjectValue); while ((!qobject || (qobject->metaObject() != data->meta)) && JSC::asObject(qobjectValue)->prototype().isObject()) { qobjectValue = JSC::asObject(qobjectValue)->prototype(); qobject = QScriptEnginePrivate::toQObject(exec, qobjectValue); } Q_ASSERT_X(qobject, Q_FUNC_INFO, "this-object must be a QObject"); QMetaProperty prop = data->meta->property(data->index); Q_ASSERT(prop.isScriptable()); if (args.size() == 0) { // get if (prop.isValid()) { QScriptable *scriptable = scriptableFromQObject(qobject); QScriptEngine *oldEngine = 0; if (scriptable) { engine->pushContext(exec, thisValue, args, this); oldEngine = QScriptablePrivate::get(scriptable)->engine; QScriptablePrivate::get(scriptable)->engine = QScriptEnginePrivate::get(engine); } QVariant v = prop.read(qobject); if (scriptable) { QScriptablePrivate::get(scriptable)->engine = oldEngine; engine->popContext(); } result = QScriptEnginePrivate::jscValueFromVariant(exec, v); } } else { // set JSC::JSValue arg = args.at(0); QVariant v; if (prop.isEnumType() && arg.isString() && !engine->hasDemarshalFunction(prop.userType())) { // give QMetaProperty::write() a chance to convert from // string to enum value v = (QString)arg.toString(exec); } else { v = QScriptEnginePrivate::jscValueToVariant(exec, arg, prop.userType()); } QScriptable *scriptable = scriptableFromQObject(qobject); QScriptEngine *oldEngine = 0; if (scriptable) { engine->pushContext(exec, thisValue, args, this); oldEngine = QScriptablePrivate::get(scriptable)->engine; QScriptablePrivate::get(scriptable)->engine = QScriptEnginePrivate::get(engine); } prop.write(qobject, v); if (scriptable) { QScriptablePrivate::get(scriptable)->engine = oldEngine; engine->popContext(); } result = arg; } engine->currentFrame = previousFrame; return result; } const QMetaObject *QtPropertyFunction::metaObject() const { return data->meta; } int QtPropertyFunction::propertyIndex() const { return data->index; } QObjectDelegate::QObjectDelegate( QObject *object, QScriptEngine::ValueOwnership ownership, const QScriptEngine::QObjectWrapOptions &options) : data(new Data(object, ownership, options)) { } QObjectDelegate::~QObjectDelegate() { switch (data->ownership) { case QScriptEngine::QtOwnership: break; case QScriptEngine::ScriptOwnership: if (data->value) delete data->value; // ### fixme // eng->disposeQObject(value); break; case QScriptEngine::AutoOwnership: if (data->value && !data->value->parent()) delete data->value; // ### fixme // eng->disposeQObject(value); break; } delete data; } QScriptObjectDelegate::Type QObjectDelegate::type() const { return QtObject; } bool QObjectDelegate::getOwnPropertySlot(QScriptObject *object, JSC::ExecState *exec, const JSC::Identifier &propertyName, JSC::PropertySlot &slot) { //Note: this has to be kept in sync with getOwnPropertyDescriptor #ifndef QT_NO_PROPERTIES QByteArray name = convertToLatin1(propertyName.ustring()); QObject *qobject = data->value; if (!qobject) { QString message = QString::fromLatin1("cannot access member `%0' of deleted QObject") .arg(QString::fromLatin1(name)); slot.setValue(JSC::throwError(exec, JSC::GeneralError, message)); return true; } const QMetaObject *meta = qobject->metaObject(); { QHash::const_iterator it = data->cachedMembers.constFind(name); if (it != data->cachedMembers.constEnd()) { if (GeneratePropertyFunctions && (meta->indexOfProperty(name) != -1)) slot.setGetterSlot(JSC::asObject(it.value())); else slot.setValue(it.value()); return true; } } const QScriptEngine::QObjectWrapOptions &opt = data->options; QScriptEnginePrivate *eng = scriptEngineFromExec(exec); int index = -1; if (name.contains('(')) { QByteArray normalized = QMetaObject::normalizedSignature(name); if (-1 != (index = meta->indexOfMethod(normalized))) { QMetaMethod method = meta->method(index); if (hasMethodAccess(method, index, opt)) { if (!(opt & QScriptEngine::ExcludeSuperClassMethods) || (index >= meta->methodOffset())) { QtFunction *fun = new (exec)QtFunction( object, index, /*maybeOverloaded=*/false, &exec->globalData(), eng->originalGlobalObject()->functionStructure(), propertyName); slot.setValue(fun); data->cachedMembers.insert(name, fun); return true; } } } } index = meta->indexOfProperty(name); if (index != -1) { QMetaProperty prop = meta->property(index); if (prop.isScriptable()) { if (!(opt & QScriptEngine::ExcludeSuperClassProperties) || (index >= meta->propertyOffset())) { if (GeneratePropertyFunctions) { QtPropertyFunction *fun = new (exec)QtPropertyFunction( meta, index, &exec->globalData(), eng->originalGlobalObject()->functionStructure(), propertyName); data->cachedMembers.insert(name, fun); slot.setGetterSlot(fun); } else { JSC::JSValue val; if (!prop.isValid()) val = JSC::jsUndefined(); else val = QScriptEnginePrivate::jscValueFromVariant(exec, prop.read(qobject)); slot.setValue(val); } return true; } } } index = qobject->dynamicPropertyNames().indexOf(name); if (index != -1) { JSC::JSValue val = QScriptEnginePrivate::jscValueFromVariant(exec, qobject->property(name)); slot.setValue(val); return true; } const int offset = (opt & QScriptEngine::ExcludeSuperClassMethods) ? meta->methodOffset() : 0; for (index = meta->methodCount() - 1; index >= offset; --index) { QMetaMethod method = meta->method(index); if (hasMethodAccess(method, index, opt) && (method.name() == name)) { QtFunction *fun = new (exec)QtFunction( object, index, /*maybeOverloaded=*/true, &exec->globalData(), eng->originalGlobalObject()->functionStructure(), propertyName); slot.setValue(fun); data->cachedMembers.insert(name, fun); return true; } } if (!(opt & QScriptEngine::ExcludeChildObjects)) { QList children = qobject->children(); for (index = 0; index < children.count(); ++index) { QObject *child = children.at(index); if (child->objectName() == QString(propertyName.ustring())) { QScriptEngine::QObjectWrapOptions opt = QScriptEngine::PreferExistingWrapperObject; slot.setValue(eng->newQObject(child, QScriptEngine::QtOwnership, opt)); return true; } } } return QScriptObjectDelegate::getOwnPropertySlot(object, exec, propertyName, slot); #else //QT_NO_PROPERTIES return false; #endif //QT_NO_PROPERTIES } bool QObjectDelegate::getOwnPropertyDescriptor(QScriptObject *object, JSC::ExecState *exec, const JSC::Identifier &propertyName, JSC::PropertyDescriptor &descriptor) { //Note: this has to be kept in sync with getOwnPropertySlot #ifndef QT_NO_PROPERTIES QByteArray name = convertToLatin1(propertyName.ustring()); QObject *qobject = data->value; if (!qobject) { QString message = QString::fromLatin1("cannot access member `%0' of deleted QObject") .arg(QString::fromLatin1(name)); descriptor.setValue(JSC::throwError(exec, JSC::GeneralError, message)); return true; } const QScriptEngine::QObjectWrapOptions &opt = data->options; const QMetaObject *meta = qobject->metaObject(); { QHash::const_iterator it = data->cachedMembers.constFind(name); if (it != data->cachedMembers.constEnd()) { int index; if (GeneratePropertyFunctions && ((index = meta->indexOfProperty(name)) != -1)) { QMetaProperty prop = meta->property(index); descriptor.setAccessorDescriptor(it.value(), it.value(), flagsForMetaProperty(prop)); if (!prop.isWritable()) descriptor.setWritable(false); } else { unsigned attributes = QObjectMemberAttribute; if (opt & QScriptEngine::SkipMethodsInEnumeration) attributes |= JSC::DontEnum; descriptor.setDescriptor(it.value(), attributes); } return true; } } QScriptEnginePrivate *eng = scriptEngineFromExec(exec); int index = -1; if (name.contains('(')) { QByteArray normalized = QMetaObject::normalizedSignature(name); if (-1 != (index = meta->indexOfMethod(normalized))) { QMetaMethod method = meta->method(index); if (hasMethodAccess(method, index, opt)) { if (!(opt & QScriptEngine::ExcludeSuperClassMethods) || (index >= meta->methodOffset())) { QtFunction *fun = new (exec)QtFunction( object, index, /*maybeOverloaded=*/false, &exec->globalData(), eng->originalGlobalObject()->functionStructure(), propertyName); data->cachedMembers.insert(name, fun); unsigned attributes = QObjectMemberAttribute; if (opt & QScriptEngine::SkipMethodsInEnumeration) attributes |= JSC::DontEnum; descriptor.setDescriptor(fun, attributes); return true; } } } } index = meta->indexOfProperty(name); if (index != -1) { QMetaProperty prop = meta->property(index); if (prop.isScriptable()) { if (!(opt & QScriptEngine::ExcludeSuperClassProperties) || (index >= meta->propertyOffset())) { unsigned attributes = flagsForMetaProperty(prop); if (GeneratePropertyFunctions) { QtPropertyFunction *fun = new (exec)QtPropertyFunction( meta, index, &exec->globalData(), eng->originalGlobalObject()->functionStructure(), propertyName); data->cachedMembers.insert(name, fun); descriptor.setAccessorDescriptor(fun, fun, attributes); if (attributes & JSC::ReadOnly) descriptor.setWritable(false); } else { JSC::JSValue val; if (!prop.isValid()) val = JSC::jsUndefined(); else val = QScriptEnginePrivate::jscValueFromVariant(exec, prop.read(qobject)); descriptor.setDescriptor(val, attributes); } return true; } } } index = qobject->dynamicPropertyNames().indexOf(name); if (index != -1) { JSC::JSValue val = QScriptEnginePrivate::jscValueFromVariant(exec, qobject->property(name)); descriptor.setDescriptor(val, QObjectMemberAttribute); return true; } const int offset = (opt & QScriptEngine::ExcludeSuperClassMethods) ? meta->methodOffset() : 0; for (index = meta->methodCount() - 1; index >= offset; --index) { QMetaMethod method = meta->method(index); if (hasMethodAccess(method, index, opt) && (method.name() == name)) { QtFunction *fun = new (exec)QtFunction( object, index, /*maybeOverloaded=*/true, &exec->globalData(), eng->originalGlobalObject()->functionStructure(), propertyName); unsigned attributes = QObjectMemberAttribute; if (opt & QScriptEngine::SkipMethodsInEnumeration) attributes |= JSC::DontEnum; descriptor.setDescriptor(fun, attributes); data->cachedMembers.insert(name, fun); return true; } } if (!(opt & QScriptEngine::ExcludeChildObjects)) { QList children = qobject->children(); for (index = 0; index < children.count(); ++index) { QObject *child = children.at(index); if (child->objectName() == QString(propertyName.ustring())) { QScriptEngine::QObjectWrapOptions opt = QScriptEngine::PreferExistingWrapperObject; descriptor.setDescriptor(eng->newQObject(child, QScriptEngine::QtOwnership, opt), JSC::ReadOnly | JSC::DontDelete | JSC::DontEnum); return true; } } } return QScriptObjectDelegate::getOwnPropertyDescriptor(object, exec, propertyName, descriptor); #else //QT_NO_PROPERTIES return false; #endif //QT_NO_PROPERTIES } void QObjectDelegate::put(QScriptObject *object, JSC::ExecState* exec, const JSC::Identifier& propertyName, JSC::JSValue value, JSC::PutPropertySlot &slot) { #ifndef QT_NO_PROPERTIES QByteArray name = convertToLatin1(propertyName.ustring()); QObject *qobject = data->value; if (!qobject) { QString message = QString::fromLatin1("cannot access member `%0' of deleted QObject") .arg(QString::fromLatin1(name)); JSC::throwError(exec, JSC::GeneralError, message); return; } const QScriptEngine::QObjectWrapOptions &opt = data->options; const QMetaObject *meta = qobject->metaObject(); QScriptEnginePrivate *eng = scriptEngineFromExec(exec); int index = -1; if (name.contains('(')) { QByteArray normalized = QMetaObject::normalizedSignature(name); if (-1 != (index = meta->indexOfMethod(normalized))) { QMetaMethod method = meta->method(index); if (hasMethodAccess(method, index, opt)) { if (!(opt & QScriptEngine::ExcludeSuperClassMethods) || (index >= meta->methodOffset())) { data->cachedMembers.insert(name, value); return; } } } } index = meta->indexOfProperty(name); if (index != -1) { QMetaProperty prop = meta->property(index); if (prop.isScriptable()) { if (!(opt & QScriptEngine::ExcludeSuperClassProperties) || (index >= meta->propertyOffset())) { if (GeneratePropertyFunctions) { // ### ideally JSC would do this for us already, i.e. find out // that the property is a setter and call the setter. // Maybe QtPropertyFunction needs to inherit JSC::GetterSetter. JSC::JSValue fun; QHash::const_iterator it; it = data->cachedMembers.constFind(name); if (it != data->cachedMembers.constEnd()) { fun = it.value(); } else { fun = new (exec)QtPropertyFunction( meta, index, &exec->globalData(), eng->originalGlobalObject()->functionStructure(), propertyName); data->cachedMembers.insert(name, fun); } JSC::CallData callData; JSC::CallType callType = fun.getCallData(callData); JSC::JSValue argv[1] = { value }; JSC::ArgList args(argv, 1); (void)JSC::call(exec, fun, callType, callData, object, args); } else { QVariant v; if (prop.isEnumType() && value.isString() && !eng->hasDemarshalFunction(prop.userType())) { // give QMetaProperty::write() a chance to convert from // string to enum value v = (QString)value.toString(exec); } else { v = QScriptEnginePrivate::jscValueToVariant(exec, value, prop.userType()); } (void)prop.write(qobject, v); } return; } } } const int offset = (opt & QScriptEngine::ExcludeSuperClassMethods) ? meta->methodOffset() : 0; for (index = meta->methodCount() - 1; index >= offset; --index) { QMetaMethod method = meta->method(index); if (hasMethodAccess(method, index, opt) && (method.name() == name)) { data->cachedMembers.insert(name, value); return; } } index = qobject->dynamicPropertyNames().indexOf(name); if ((index != -1) || (opt & QScriptEngine::AutoCreateDynamicProperties)) { QVariant v = QScriptEnginePrivate::toVariant(exec, value); (void)qobject->setProperty(name, v); return; } QScriptObjectDelegate::put(object, exec, propertyName, value, slot); #endif //QT_NO_PROPERTIES } bool QObjectDelegate::deleteProperty(QScriptObject *object, JSC::ExecState *exec, const JSC::Identifier& propertyName) { #ifndef QT_NO_PROPERTIES QByteArray name = convertToLatin1(propertyName.ustring()); QObject *qobject = data->value; if (!qobject) { QString message = QString::fromLatin1("cannot access member `%0' of deleted QObject") .arg(QString::fromLatin1(name)); JSC::throwError(exec, JSC::GeneralError, message); return false; } const QMetaObject *meta = qobject->metaObject(); { QHash::iterator it = data->cachedMembers.find(name); if (it != data->cachedMembers.end()) { if (GeneratePropertyFunctions && (meta->indexOfProperty(name) != -1)) return false; data->cachedMembers.erase(it); return true; } } const QScriptEngine::QObjectWrapOptions &opt = data->options; int index = meta->indexOfProperty(name); if (index != -1) { QMetaProperty prop = meta->property(index); if (prop.isScriptable() && (!(opt & QScriptEngine::ExcludeSuperClassProperties) || (index >= meta->propertyOffset()))) { return false; } } index = qobject->dynamicPropertyNames().indexOf(name); if (index != -1) { (void)qobject->setProperty(name, QVariant()); return true; } return QScriptObjectDelegate::deleteProperty(object, exec, propertyName); #else //QT_NO_PROPERTIES return false; #endif //QT_NO_PROPERTIES } void QObjectDelegate::getOwnPropertyNames(QScriptObject *object, JSC::ExecState *exec, JSC::PropertyNameArray &propertyNames, JSC::EnumerationMode mode) { #ifndef QT_NO_PROPERTIES QObject *qobject = data->value; if (!qobject) { QString message = QString::fromLatin1("cannot get property names of deleted QObject"); JSC::throwError(exec, JSC::GeneralError, message); return; } const QScriptEngine::QObjectWrapOptions &opt = data->options; const QMetaObject *meta = qobject->metaObject(); { int i = (opt & QScriptEngine::ExcludeSuperClassProperties) ? meta->propertyOffset() : 0; for ( ; i < meta->propertyCount(); ++i) { QMetaProperty prop = meta->property(i); if (isEnumerableMetaProperty(prop, meta, i)) { QString name = QString::fromLatin1(prop.name()); propertyNames.add(JSC::Identifier(exec, name)); } } } { QList dpNames = qobject->dynamicPropertyNames(); for (int i = 0; i < dpNames.size(); ++i) { QString name = QString::fromLatin1(dpNames.at(i)); propertyNames.add(JSC::Identifier(exec, name)); } } if (!(opt & QScriptEngine::SkipMethodsInEnumeration)) { int i = (opt & QScriptEngine::ExcludeSuperClassMethods) ? meta->methodOffset() : 0; for ( ; i < meta->methodCount(); ++i) { QMetaMethod method = meta->method(i); if (hasMethodAccess(method, i, opt)) { QMetaMethod method = meta->method(i); QString sig = QString::fromLatin1(method.methodSignature().constData()); propertyNames.add(JSC::Identifier(exec, sig)); } } } QScriptObjectDelegate::getOwnPropertyNames(object, exec, propertyNames, mode); #endif //QT_NO_PROPERTIES } void QObjectDelegate::markChildren(QScriptObject *object, JSC::MarkStack& markStack) { QHash::const_iterator it; for (it = data->cachedMembers.constBegin(); it != data->cachedMembers.constEnd(); ++it) { JSC::JSValue val = it.value(); if (val) markStack.append(val); } QScriptObjectDelegate::markChildren(object, markStack); } bool QObjectDelegate::compareToObject(QScriptObject *, JSC::ExecState *exec, JSC::JSObject *o2) { if (!o2->inherits(&QScriptObject::info)) return false; QScriptObject *object = static_cast(o2); QScriptObjectDelegate *delegate = object->delegate(); if (!delegate || (delegate->type() != QScriptObjectDelegate::QtObject)) return false; return value() == static_cast(delegate)->value(); } static JSC::JSValue JSC_HOST_CALL qobjectProtoFuncFindChild(JSC::ExecState *exec, JSC::JSObject*, JSC::JSValue thisValue, const JSC::ArgList &args) { QScriptEnginePrivate *engine = scriptEngineFromExec(exec); thisValue = engine->toUsableValue(thisValue); if (!thisValue.inherits(&QScriptObject::info)) return throwError(exec, JSC::TypeError, "this object is not a QObject"); QScriptObject *scriptObject = static_cast(JSC::asObject(thisValue)); QScriptObjectDelegate *delegate = scriptObject->delegate(); if (!delegate || (delegate->type() != QScriptObjectDelegate::QtObject)) return throwError(exec, JSC::TypeError, "this object is not a QObject"); QObject *obj = static_cast(delegate)->value(); QString name; if (args.size() != 0) name = args.at(0).toString(exec); QObject *child = obj->findChild(name); QScriptEngine::QObjectWrapOptions opt = QScriptEngine::PreferExistingWrapperObject; return engine->newQObject(child, QScriptEngine::QtOwnership, opt); } static JSC::JSValue JSC_HOST_CALL qobjectProtoFuncFindChildren(JSC::ExecState *exec, JSC::JSObject*, JSC::JSValue thisValue, const JSC::ArgList &args) { QScriptEnginePrivate *engine = scriptEngineFromExec(exec); thisValue = engine->toUsableValue(thisValue); // extract the QObject if (!thisValue.inherits(&QScriptObject::info)) return throwError(exec, JSC::TypeError, "this object is not a QObject"); QScriptObject *scriptObject = static_cast(JSC::asObject(thisValue)); QScriptObjectDelegate *delegate = scriptObject->delegate(); if (!delegate || (delegate->type() != QScriptObjectDelegate::QtObject)) return throwError(exec, JSC::TypeError, "this object is not a QObject"); const QObject *const obj = static_cast(delegate)->value(); // find the children QList children; if (args.size() != 0) { const JSC::JSValue arg = args.at(0); if (arg.inherits(&JSC::RegExpObject::info)) { const QObjectList allChildren= obj->children(); JSC::RegExpObject *const regexp = JSC::asRegExpObject(arg); const int allChildrenCount = allChildren.size(); for (int i = 0; i < allChildrenCount; ++i) { QObject *const child = allChildren.at(i); const JSC::UString childName = child->objectName(); JSC::RegExpConstructor* regExpConstructor = engine->originalGlobalObject()->regExpConstructor(); int position; int length; regExpConstructor->performMatch(regexp->regExp(), childName, 0, position, length); if (position >= 0) children.append(child); } } else { const QString name(args.at(0).toString(exec)); children = obj->findChildren(name); } } else { children = obj->findChildren(QString()); } // create the result array with the children const int length = children.size(); JSC::JSArray *const result = JSC::constructEmptyArray(exec, length); QScriptEngine::QObjectWrapOptions opt = QScriptEngine::PreferExistingWrapperObject; for (int i = 0; i < length; ++i) { QObject *const child = children.at(i); result->put(exec, i, engine->newQObject(child, QScriptEngine::QtOwnership, opt)); } return JSC::JSValue(result); } static JSC::JSValue JSC_HOST_CALL qobjectProtoFuncToString(JSC::ExecState *exec, JSC::JSObject*, JSC::JSValue thisValue, const JSC::ArgList&) { QScriptEnginePrivate *engine = scriptEngineFromExec(exec); thisValue = engine->toUsableValue(thisValue); if (!thisValue.inherits(&QScriptObject::info)) return JSC::jsUndefined(); QScriptObject *scriptObject = static_cast(JSC::asObject(thisValue)); QScriptObjectDelegate *delegate = scriptObject->delegate(); if (!delegate || (delegate->type() != QScriptObjectDelegate::QtObject)) return JSC::jsUndefined(); QObject *obj = static_cast(delegate)->value(); const QMetaObject *meta = obj ? obj->metaObject() : &QObject::staticMetaObject; QString name = obj ? obj->objectName() : QString::fromUtf8("unnamed"); QString str = QString::fromUtf8("%0(name = \"%1\")") .arg(QLatin1String(meta->className())).arg(name); return JSC::jsString(exec, str); } QObjectPrototype::QObjectPrototype(JSC::ExecState* exec, WTF::PassRefPtr structure, JSC::Structure* prototypeFunctionStructure) : QScriptObject(structure) { setDelegate(new QObjectDelegate(new QObjectPrototypeObject(), QScriptEngine::AutoOwnership, QScriptEngine::ExcludeSuperClassMethods | QScriptEngine::ExcludeSuperClassProperties | QScriptEngine::ExcludeChildObjects)); putDirectFunction(exec, new (exec) JSC::NativeFunctionWrapper(exec, prototypeFunctionStructure, /*length=*/0, exec->propertyNames().toString, qobjectProtoFuncToString), JSC::DontEnum); putDirectFunction(exec, new (exec) JSC::NativeFunctionWrapper(exec, prototypeFunctionStructure, /*length=*/1, JSC::Identifier(exec, "findChild"), qobjectProtoFuncFindChild), JSC::DontEnum); putDirectFunction(exec, new (exec) JSC::NativeFunctionWrapper(exec, prototypeFunctionStructure, /*length=*/1, JSC::Identifier(exec, "findChildren"), qobjectProtoFuncFindChildren), JSC::DontEnum); this->structure()->setHasGetterSetterProperties(true); } const JSC::ClassInfo QMetaObjectWrapperObject::info = { "QMetaObject", 0, 0, 0 }; QMetaObjectWrapperObject::QMetaObjectWrapperObject( JSC::ExecState *exec, const QMetaObject *metaObject, JSC::JSValue ctor, WTF::PassRefPtr sid) : JSC::JSObject(sid), data(new Data(metaObject, ctor)) { if (!ctor) data->prototype = new (exec)JSC::JSObject(exec->lexicalGlobalObject()->emptyObjectStructure()); } QMetaObjectWrapperObject::~QMetaObjectWrapperObject() { delete data; } bool QMetaObjectWrapperObject::getOwnPropertySlot( JSC::ExecState *exec, const JSC::Identifier& propertyName, JSC::PropertySlot &slot) { const QMetaObject *meta = data->value; if (!meta) return false; if (propertyName == exec->propertyNames().prototype) { if (data->ctor) slot.setValue(data->ctor.get(exec, propertyName)); else slot.setValue(data->prototype); return true; } QByteArray name = convertToLatin1(propertyName.ustring()); for (int i = 0; i < meta->enumeratorCount(); ++i) { QMetaEnum e = meta->enumerator(i); for (int j = 0; j < e.keyCount(); ++j) { const char *key = e.key(j); if (!qstrcmp(key, name.constData())) { slot.setValue(JSC::JSValue(exec, e.value(j))); return true; } } } return JSC::JSObject::getOwnPropertySlot(exec, propertyName, slot); } bool QMetaObjectWrapperObject::getOwnPropertyDescriptor( JSC::ExecState* exec, const JSC::Identifier& propertyName, JSC::PropertyDescriptor& descriptor) { const QMetaObject *meta = data->value; if (!meta) return false; if (propertyName == exec->propertyNames().prototype) { descriptor.setDescriptor(data->ctor ? data->ctor.get(exec, propertyName) : data->prototype, JSC::DontDelete | JSC::DontEnum); return true; } QByteArray name = QString(propertyName.ustring()).toLatin1(); for (int i = 0; i < meta->enumeratorCount(); ++i) { QMetaEnum e = meta->enumerator(i); for (int j = 0; j < e.keyCount(); ++j) { const char *key = e.key(j); if (!qstrcmp(key, name.constData())) { descriptor.setDescriptor(JSC::JSValue(exec, e.value(j)), JSC::ReadOnly | JSC::DontDelete); return true; } } } return JSC::JSObject::getOwnPropertyDescriptor(exec, propertyName, descriptor); } void QMetaObjectWrapperObject::put(JSC::ExecState* exec, const JSC::Identifier& propertyName, JSC::JSValue value, JSC::PutPropertySlot &slot) { if (propertyName == exec->propertyNames().prototype) { if (data->ctor) data->ctor.put(exec, propertyName, value, slot); else data->prototype = value; return; } const QMetaObject *meta = data->value; if (meta) { QByteArray name = convertToLatin1(propertyName.ustring()); for (int i = 0; i < meta->enumeratorCount(); ++i) { QMetaEnum e = meta->enumerator(i); for (int j = 0; j < e.keyCount(); ++j) { if (!qstrcmp(e.key(j), name.constData())) return; } } } JSC::JSObject::put(exec, propertyName, value, slot); } bool QMetaObjectWrapperObject::deleteProperty( JSC::ExecState *exec, const JSC::Identifier& propertyName) { if (propertyName == exec->propertyNames().prototype) return false; const QMetaObject *meta = data->value; if (meta) { QByteArray name = convertToLatin1(propertyName.ustring()); for (int i = 0; i < meta->enumeratorCount(); ++i) { QMetaEnum e = meta->enumerator(i); for (int j = 0; j < e.keyCount(); ++j) { if (!qstrcmp(e.key(j), name.constData())) return false; } } } return JSC::JSObject::deleteProperty(exec, propertyName); } void QMetaObjectWrapperObject::getOwnPropertyNames(JSC::ExecState *exec, JSC::PropertyNameArray &propertyNames, JSC::EnumerationMode mode) { const QMetaObject *meta = data->value; if (!meta) return; for (int i = 0; i < meta->enumeratorCount(); ++i) { QMetaEnum e = meta->enumerator(i); for (int j = 0; j < e.keyCount(); ++j) propertyNames.add(JSC::Identifier(exec, e.key(j))); } JSC::JSObject::getOwnPropertyNames(exec, propertyNames, mode); } void QMetaObjectWrapperObject::markChildren(JSC::MarkStack& markStack) { if (data->ctor) markStack.append(data->ctor); if (data->prototype) markStack.append(data->prototype); JSC::JSObject::markChildren(markStack); } JSC::CallType QMetaObjectWrapperObject::getCallData(JSC::CallData& callData) { callData.native.function = call; return JSC::CallTypeHost; } JSC::ConstructType QMetaObjectWrapperObject::getConstructData(JSC::ConstructData& constructData) { constructData.native.function = construct; return JSC::ConstructTypeHost; } JSC::JSValue JSC_HOST_CALL QMetaObjectWrapperObject::call( JSC::ExecState *exec, JSC::JSObject *callee, JSC::JSValue thisValue, const JSC::ArgList &args) { QScriptEnginePrivate *eng_p = scriptEngineFromExec(exec); thisValue = eng_p->toUsableValue(thisValue); if (!callee->inherits(&QMetaObjectWrapperObject::info)) return throwError(exec, JSC::TypeError, "callee is not a QMetaObject"); QMetaObjectWrapperObject *self = static_cast(callee); JSC::ExecState *previousFrame = eng_p->currentFrame; eng_p->pushContext(exec, thisValue, args, callee); JSC::JSValue result = self->execute(eng_p->currentFrame, args); eng_p->popContext(); eng_p->currentFrame = previousFrame; return result; } JSC::JSObject* QMetaObjectWrapperObject::construct(JSC::ExecState *exec, JSC::JSObject *callee, const JSC::ArgList &args) { QMetaObjectWrapperObject *self = static_cast(callee); QScriptEnginePrivate *eng_p = scriptEngineFromExec(exec); JSC::ExecState *previousFrame = eng_p->currentFrame; eng_p->pushContext(exec, JSC::JSValue(), args, callee, true); JSC::JSValue result = self->execute(eng_p->currentFrame, args); eng_p->popContext(); eng_p->currentFrame = previousFrame; if (!result || !result.isObject()) return 0; return JSC::asObject(result); } JSC::JSValue QMetaObjectWrapperObject::execute(JSC::ExecState *exec, const JSC::ArgList &args) { if (data->ctor) { QScriptEnginePrivate *eng_p = QScript::scriptEngineFromExec(exec); QScriptContext *ctx = eng_p->contextForFrame(exec); JSC::CallData callData; JSC::CallType callType = data->ctor.getCallData(callData); Q_UNUSED(callType); Q_ASSERT_X(callType == JSC::CallTypeHost, Q_FUNC_INFO, "script constructors not supported"); if (data->ctor.inherits(&FunctionWithArgWrapper::info)) { FunctionWithArgWrapper *wrapper = static_cast(JSC::asObject(data->ctor)); QScriptValue result = wrapper->function()(ctx, QScriptEnginePrivate::get(eng_p), wrapper->arg()); return eng_p->scriptValueToJSCValue(result); } else { Q_ASSERT(data->ctor.inherits(&FunctionWrapper::info)); FunctionWrapper *wrapper = static_cast(JSC::asObject(data->ctor)); QScriptValue result = wrapper->function()(ctx, QScriptEnginePrivate::get(eng_p)); return eng_p->scriptValueToJSCValue(result); } } else { const QMetaObject *meta = data->value; if (meta->constructorCount() > 0) { JSC::JSValue result = callQtMethod(exec, QMetaMethod::Constructor, /*thisQObject=*/0, args, meta, meta->constructorCount()-1, /*maybeOverloaded=*/true); if (!exec->hadException()) { Q_ASSERT(result && result.inherits(&QScriptObject::info)); QScriptObject *object = static_cast(JSC::asObject(result)); QScript::QObjectDelegate *delegate = static_cast(object->delegate()); delegate->setOwnership(QScriptEngine::AutoOwnership); if (data->prototype) object->setPrototype(data->prototype); } return result; } else { QString message = QString::fromLatin1("no constructor for %0") .arg(QLatin1String(meta->className())); return JSC::throwError(exec, JSC::TypeError, message); } } } struct StaticQtMetaObject : public QObject { static const QMetaObject *get() { return &static_cast (0)->staticQtMetaObject; } }; static JSC::JSValue JSC_HOST_CALL qmetaobjectProtoFuncClassName( JSC::ExecState *exec, JSC::JSObject*, JSC::JSValue thisValue, const JSC::ArgList&) { QScriptEnginePrivate *engine = scriptEngineFromExec(exec); thisValue = engine->toUsableValue(thisValue); if (!thisValue.inherits(&QMetaObjectWrapperObject::info)) return throwError(exec, JSC::TypeError, "this object is not a QMetaObject"); const QMetaObject *meta = static_cast(JSC::asObject(thisValue))->value(); return JSC::jsString(exec, meta->className()); } QMetaObjectPrototype::QMetaObjectPrototype( JSC::ExecState *exec, WTF::PassRefPtr structure, JSC::Structure* prototypeFunctionStructure) : QMetaObjectWrapperObject(exec, StaticQtMetaObject::get(), /*ctor=*/JSC::JSValue(), structure) { putDirectFunction(exec, new (exec) JSC::NativeFunctionWrapper(exec, prototypeFunctionStructure, /*length=*/0, JSC::Identifier(exec, "className"), qmetaobjectProtoFuncClassName), JSC::DontEnum); } // Begin moc-generated code -- modify with care! Check "HAND EDIT" parts struct qt_meta_stringdata_QObjectConnectionManager_t { QByteArrayData data[3]; char stringdata[44]; }; #define QT_MOC_LITERAL(idx, ofs, len) { \ Q_REFCOUNT_INITIALIZE_STATIC, len, 0, 0, \ offsetof(qt_meta_stringdata_QObjectConnectionManager_t, stringdata) + ofs \ - idx * sizeof(QByteArrayData) \ } static const qt_meta_stringdata_QObjectConnectionManager_t qt_meta_stringdata_QObjectConnectionManager = { { QT_MOC_LITERAL(0, 0, 33), QT_MOC_LITERAL(1, 34, 7), QT_MOC_LITERAL(2, 42, 0) }, "QScript::QObjectConnectionManager\0" "execute\0\0" }; #undef QT_MOC_LITERAL static const uint qt_meta_data_QObjectConnectionManager[] = { // content: 7, // revision 0, // classname 0, 0, // classinfo 1, 14, // methods 0, 0, // properties 0, 0, // enums/sets 0, 0, // constructors 0, // flags 0, // signalCount // slots: name, argc, parameters, tag, flags 1, 0, 19, 2, 0x0a, // slots: parameters QMetaType::Void, 0 // eod }; void QObjectConnectionManager::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a) { if (_c == QMetaObject::InvokeMetaMethod) { Q_ASSERT(staticMetaObject.cast(_o)); QObjectConnectionManager *_t = static_cast(_o); // HAND EDIT: remove switch (_id), add the _id and _a parameters _t->execute(_id, _a); } } const QMetaObject QObjectConnectionManager::staticMetaObject = { { &QObject::staticMetaObject, qt_meta_stringdata_QObjectConnectionManager.data, qt_meta_data_QObjectConnectionManager, qt_static_metacall, 0, 0 } }; const QMetaObject *QObjectConnectionManager::metaObject() const { return &staticMetaObject; } void *QObjectConnectionManager::qt_metacast(const char *_clname) { if (!_clname) return 0; if (!strcmp(_clname, qt_meta_stringdata_QObjectConnectionManager.stringdata)) return static_cast(const_cast(this)); return QObject::qt_metacast(_clname); } int QObjectConnectionManager::qt_metacall(QMetaObject::Call _c, int _id, void **_a) { _id = QObject::qt_metacall(_c, _id, _a); if (_id < 0) return _id; if (_c == QMetaObject::InvokeMetaMethod) { if (_id < slotCounter) // HAND EDIT qt_static_metacall(this, _c, _id, _a); _id -= slotCounter; // HAND EDIT } return _id; } // End moc-generated code void QObjectConnectionManager::execute(int slotIndex, void **argv) { JSC::JSValue receiver; JSC::JSValue slot; JSC::JSValue senderWrapper; int signalIndex = -1; QScript::APIShim shim(engine); for (int i = 0; i < connections.size(); ++i) { const QVector &cs = connections.at(i); for (int j = 0; j < cs.size(); ++j) { const QObjectConnection &c = cs.at(j); if (c.slotIndex == slotIndex) { receiver = c.receiver; slot = c.slot; senderWrapper = c.senderWrapper; signalIndex = i; break; } } } if (!slot) { // This connection no longer exists (can happen if the signal is // emitted from another thread and the call gets queued, but the // connection is removed before the QMetaCallEvent gets processed). return; } Q_ASSERT(slot.isObject()); if (engine->isCollecting()) { qWarning("QtScript: can't execute signal handler during GC"); // we can't do a script function call during GC, // so we're forced to ignore this signal return; } #if 0 QScriptFunction *fun = engine->convertToNativeFunction(slot); if (fun == 0) { // the signal handler has been GC'ed. This can only happen when // a QObject is owned by the engine, the engine is destroyed, and // there is a script function connected to the destroyed() signal Q_ASSERT(signalIndex <= 1); // destroyed(QObject*) return; } #endif const QMetaObject *meta = sender()->metaObject(); const QMetaMethod method = meta->method(signalIndex); QList parameterTypes = method.parameterTypes(); int argc = parameterTypes.count(); JSC::ExecState *exec = engine->currentFrame; QVarLengthArray argsVector(argc); for (int i = 0; i < argc; ++i) { JSC::JSValue actual; void *arg = argv[i + 1]; QByteArray typeName = parameterTypes.at(i); int argType = QMetaType::type(parameterTypes.at(i)); if (!argType) { qWarning("QScriptEngine: Unable to handle unregistered datatype '%s' " "when invoking handler of signal %s::%s", typeName.constData(), meta->className(), method.methodSignature().constData()); actual = JSC::jsUndefined(); } else if (argType == QMetaType::QVariant) { actual = QScriptEnginePrivate::jscValueFromVariant(exec, *reinterpret_cast(arg)); } else { actual = QScriptEnginePrivate::create(exec, argType, arg); } argsVector[i] = actual; } JSC::ArgList jscArgs(argsVector.data(), argsVector.size()); JSC::JSValue senderObject; if (senderWrapper && senderWrapper.inherits(&QScriptObject::info)) // ### check if it's actually a QObject wrapper senderObject = senderWrapper; else { QScriptEngine::QObjectWrapOptions opt = QScriptEngine::PreferExistingWrapperObject; senderObject = engine->newQObject(sender(), QScriptEngine::QtOwnership, opt); } JSC::JSValue thisObject; if (receiver && receiver.isObject()) thisObject = receiver; else thisObject = engine->globalObject(); JSC::CallData callData; JSC::CallType callType = slot.getCallData(callData); if (exec->hadException()) exec->clearException(); // ### otherwise JSC asserts JSC::call(exec, slot, callType, callData, thisObject, jscArgs); if (exec->hadException()) { if (slot.inherits(&QtFunction::info) && !static_cast(JSC::asObject(slot))->qobject()) { // The function threw an error because the target QObject has been deleted. // The connections list is stale; remove the signal handler and ignore the exception. removeSignalHandler(sender(), signalIndex, receiver, slot); exec->clearException(); } else { engine->emitSignalHandlerException(); } } } QObjectConnectionManager::QObjectConnectionManager(QScriptEnginePrivate *eng) : engine(eng), slotCounter(0) { } QObjectConnectionManager::~QObjectConnectionManager() { } void QObjectConnectionManager::clearMarkBits() { for (int i = 0; i < connections.size(); ++i) { QVector &cs = connections[i]; for (int j = 0; j < cs.size(); ++j) cs[j].marked = false; } } /*! \internal Marks connections owned by this manager. Returns the number of connections that were marked by this pass (i.e., excluding connections that were already marked). */ int QObjectConnectionManager::mark(JSC::MarkStack& markStack) { int markedCount = 0; for (int i = 0; i < connections.size(); ++i) { QVector &cs = connections[i]; for (int j = 0; j < cs.size(); ++j) { QObjectConnection &c = cs[j]; if (!c.marked) { if (c.hasWeaklyReferencedSender()) { // Don't mark the connection; we don't want the script-owned // sender object to stay alive merely due to a connection. } else { c.mark(markStack); ++markedCount; } } } } return markedCount; } bool QObjectConnectionManager::addSignalHandler( QObject *sender, int signalIndex, JSC::JSValue receiver, JSC::JSValue function, JSC::JSValue senderWrapper, Qt::ConnectionType type) { if (connections.size() <= signalIndex) connections.resize(signalIndex+1); QVector &cs = connections[signalIndex]; int absSlotIndex = slotCounter + metaObject()->methodOffset(); bool ok = QMetaObject::connect(sender, signalIndex, this, absSlotIndex, type); if (ok) cs.append(QObjectConnection(slotCounter++, receiver, function, senderWrapper)); return ok; } bool QObjectConnectionManager::removeSignalHandler( QObject *sender, int signalIndex, JSC::JSValue receiver, JSC::JSValue slot) { if (connections.size() <= signalIndex) return false; QVector &cs = connections[signalIndex]; for (int i = 0; i < cs.size(); ++i) { const QObjectConnection &c = cs.at(i); if (c.hasTarget(receiver, slot)) { int absSlotIndex = c.slotIndex + metaObject()->methodOffset(); bool ok = QMetaObject::disconnect(sender, signalIndex, this, absSlotIndex); if (ok) cs.remove(i); return ok; } } return false; } QObjectData::QObjectData(QScriptEnginePrivate *eng) : engine(eng), connectionManager(0) { } QObjectData::~QObjectData() { if (connectionManager) { delete connectionManager; connectionManager = 0; } } void QObjectData::clearConnectionMarkBits() { if (connectionManager) connectionManager->clearMarkBits(); } int QObjectData::markConnections(JSC::MarkStack& markStack) { if (connectionManager) return connectionManager->mark(markStack); return 0; } // This function assumes all objects reachable elsewhere in the JS environment // (stack, heap) have been marked already (see QScriptEnginePrivate::mark()). // This determines whether any of Qt Script's internal QObject wrappers are only // weakly referenced and can be discarded. void QObjectData::markWrappers(JSC::MarkStack& markStack) { QList::iterator it; for (it = wrappers.begin(); it != wrappers.end(); ) { const QScript::QObjectWrapperInfo &info = *it; if (JSC::Heap::isCellMarked(info.object)) { ++it; } else if (info.isCollectableWhenWeaklyReferenced()) { it = wrappers.erase(it); } else { markStack.append(info.object); ++it; } } } bool QObjectData::addSignalHandler(QObject *sender, int signalIndex, JSC::JSValue receiver, JSC::JSValue slot, JSC::JSValue senderWrapper, Qt::ConnectionType type) { if (!connectionManager) connectionManager = new QObjectConnectionManager(engine); return connectionManager->addSignalHandler( sender, signalIndex, receiver, slot, senderWrapper, type); } bool QObjectData::removeSignalHandler(QObject *sender, int signalIndex, JSC::JSValue receiver, JSC::JSValue slot) { if (!connectionManager) return false; return connectionManager->removeSignalHandler( sender, signalIndex, receiver, slot); } QScriptObject *QObjectData::findWrapper(QScriptEngine::ValueOwnership ownership, const QScriptEngine::QObjectWrapOptions &options) const { for (int i = 0; i < wrappers.size(); ++i) { const QObjectWrapperInfo &info = wrappers.at(i); if ((info.ownership == ownership) && (info.options == options)) return info.object; } return 0; } void QObjectData::registerWrapper(QScriptObject *wrapper, QScriptEngine::ValueOwnership ownership, const QScriptEngine::QObjectWrapOptions &options) { wrappers.append(QObjectWrapperInfo(wrapper, ownership, options)); } } // namespace QScript QT_END_NAMESPACE namespace JSC { ASSERT_CLASS_FITS_IN_CELL(QScript::QtFunction); } #include "moc_qscriptqobject_p.cpp"