/**************************************************************************** ** ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). ** Contact: Nokia Corporation (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 Technology Preview License Agreement accompanying ** this package. ** ** 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.1, included in the file LGPL_EXCEPTION.txt in this ** package. ** ** If you have questions regarding the use of this file, please contact ** Nokia at qt-info@nokia.com. ** ** ** ** ** ** ** ** ** $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 "Error.h" #include "PrototypeFunction.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 { int slotIndex; JSC::JSValue receiver; JSC::JSValue slot; JSC::JSValue senderWrapper; QObjectConnection(int i, JSC::JSValue r, JSC::JSValue s, JSC::JSValue sw) : slotIndex(i), receiver(r), slot(s), senderWrapper(sw) {} QObjectConnection() : slotIndex(-1) {} 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); } void mark() { if (senderWrapper && !senderWrapper.marked()) { // see if the sender should be marked or not Q_ASSERT(senderWrapper.isObject(&QScriptObject::info)); QScriptObject *scriptObject = static_cast(JSC::asObject(senderWrapper)); 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->value() && !inst->value()->parent())) { senderWrapper = JSC::JSValue(); } else { senderWrapper.mark(); } } if (receiver && !receiver.marked()) receiver.mark(); if (slot && !slot.marked()) slot.mark(); } }; class QObjectNotifyCaller : public QObject { public: void callConnectNotify(const char *signal) { connectNotify(signal); } void callDisconnectNotify(const char *signal) { disconnectNotify(signal); } }; class QObjectConnectionManager: public QObject { 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); static const QMetaObject staticMetaObject; virtual const QMetaObject *metaObject() const; virtual void *qt_metacast(const char *); virtual int qt_metacall(QMetaObject::Call, int, void **argv); void execute(int slotIndex, void **argv); void mark(); private: QScriptEnginePrivate *engine; int slotCounter; QVector > connections; }; static bool hasMethodAccess(const QMetaMethod &method, int index, const QScriptEngine::QObjectWrapOptions &opt) { return (method.access() != QMetaMethod::Private) && ((index != 2) || !(opt & QScriptEngine::ExcludeDeleteLater)); } 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 inline QByteArray methodName(const QMetaMethod &method) { QByteArray signature = method.signature(); return signature.left(signature.indexOf('(')); } static QVariant variantFromValue(QScriptEnginePrivate *eng, int targetType, const QScriptValue &value) { QVariant v(targetType, (void *)0); Q_ASSERT(eng); if (QScriptEnginePrivate::convert(value, targetType, v.data(), eng)) return v; if (uint(targetType) == QVariant::LastType) return value.toVariant(); if (value.isVariant()) { v = value.toVariant(); if (v.canConvert(QVariant::Type(targetType))) { v.convert(QVariant::Type(targetType)); return v; } QByteArray typeName = v.typeName(); if (typeName.endsWith('*') && (QMetaType::type(typeName.left(typeName.size()-1)) == targetType)) { return QVariant(targetType, *reinterpret_cast(v.data())); } } return QVariant(); } 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::mark() { Q_ASSERT(!marked()); if (data->object && !data->object.marked()) data->object.mark(); JSC::InternalFunction::mark(); } 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; QString name = functionName(); const QMetaObject *meta = metaObject(); for (int index = mostGeneralMethod() - 1; index >= 0; --index) { QString otherName = QString::fromLatin1(methodName(meta->method(index))); if (otherName == name) result.append(index); } return result; } QString QtFunction::functionName() const { const QMetaObject *meta = metaObject(); if (!meta) return QString(); QMetaMethod method = meta->method(initialIndex()); return QLatin1String(methodName(method)); } 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::type("QVariant"); return isMetaEnum() ? 2/*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 QByteArray &name, const QVector &types) : m_name(name), 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(); } QByteArray name() const { return m_name; } 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: QByteArray m_name; 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); } static JSC::JSValue callQtMethod(JSC::ExecState *exec, QMetaMethod::MethodType callType, QObject *thisQObject, const JSC::ArgList &scriptArgs, const QMetaObject *meta, int initialIndex, bool maybeOverloaded) { QByteArray funName; QScriptMetaMethod chosenMethod; int chosenIndex = -1; QVarLengthArray args; QVector candidates; QVector unresolved; QVector tooFewArgs; QVector conversionFailed; int index; exec->clearException(); QScriptEnginePrivate *engine = QScript::scriptEngineFromExec(exec); for (index = initialIndex; index >= 0; --index) { QMetaMethod method = metaMethod(meta, callType, index); if (index == initialIndex) funName = methodName(method); else { if (methodName(method) != funName) continue; } QVector types; // resolve return type QByteArray returnTypeName = method.typeName(); int rtype = QMetaType::type(returnTypeName); if ((rtype == 0) && !returnTypeName.isEmpty()) { if (returnTypeName == "QVariant") { types.append(QScriptMetaType::variant()); } else { int enumIndex = indexOfMetaEnum(meta, returnTypeName); if (enumIndex != -1) types.append(QScriptMetaType::metaEnum(enumIndex, returnTypeName)); else types.append(QScriptMetaType::unresolved(returnTypeName)); } } else { if (callType == QMetaMethod::Constructor) types.append(QScriptMetaType::metaType(QMetaType::QObjectStar, "QObject*")); else if (returnTypeName == "QVariant") types.append(QScriptMetaType::variant()); else types.append(QScriptMetaType::metaType(rtype, returnTypeName)); } // resolve argument types QList parameterTypeNames = method.parameterTypes(); for (int i = 0; i < parameterTypeNames.count(); ++i) { QByteArray argTypeName = parameterTypeNames.at(i); int atype = QMetaType::type(argTypeName); if (atype == 0) { if (argTypeName == "QVariant") { types.append(QScriptMetaType::variant()); } else { int enumIndex = indexOfMetaEnum(meta, argTypeName); if (enumIndex != -1) types.append(QScriptMetaType::metaEnum(enumIndex, argTypeName)); else types.append(QScriptMetaType::unresolved(argTypeName)); } } else { if (argTypeName == "QVariant") types.append(QScriptMetaType::variant()); else types.append(QScriptMetaType::metaType(atype, argTypeName)); } } QScriptMetaMethod mtd = QScriptMetaMethod(methodName(method), 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()); QScriptMetaType retType = mtd.returnType(); args[0] = QVariant(retType.typeId(), (void *)0); // the result // try to convert arguments bool converted = true; int matchDistance = 0; for (int i = 0; converted && i < mtd.argumentCount(); ++i) { QScriptValue actual; if (i < (int)scriptArgs.size()) actual = engine->scriptValueFromJSCValue(scriptArgs.at(i)); else actual = QScriptValue(QScriptValue::UndefinedValue); QScriptMetaType argType = mtd.argumentType(i); int tid = -1; QVariant v; if (argType.isUnresolved()) { v = QVariant(QMetaType::QObjectStar, (void *)0); converted = engine->convertToNativeQObject( actual, argType.name(), reinterpret_cast(v.data())); } else if (argType.isVariant()) { if (actual.isVariant()) { v = actual.toVariant(); } else { v = actual.toVariant(); converted = v.isValid() || actual.isUndefined() || actual.isNull(); } } else { tid = argType.typeId(); v = QVariant(tid, (void *)0); converted = QScriptEnginePrivate::convert(actual, tid, v.data(), engine); if (exec->hadException()) return exec->exception(); } if (!converted) { if (actual.isVariant()) { if (tid == -1) tid = argType.typeId(); QVariant vv = actual.toVariant(); if (vv.canConvert(QVariant::Type(tid))) { v = vv; converted = v.convert(QVariant::Type(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 = actual.toInt32(); if (m.valueToKey(ival) != 0) { qVariantSetValue(v, ival); converted = true; matchDistance += 10; } } else { QString sval = actual.toString(); int ival = m.keyToValue(sval.toLatin1()); if (ival != -1) { qVariantSetValue(v, 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 (actual.isDate()) { 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 (actual.isRegExp()) { switch (tid) { case QMetaType::QRegExp: // perfect break; default: matchDistance += 10; break; } } else if (actual.isVariant()) { if (argType.isVariant() || (actual.toVariant().userType() == tid)) { // perfect } else { matchDistance += 10; } } else if (actual.isArray()) { switch (tid) { case QMetaType::QStringList: case QMetaType::QVariantList: matchDistance += 5; break; default: matchDistance += 10; break; } } else if (actual.isQObject()) { switch (tid) { case QMetaType::QObjectStar: case QMetaType::QWidgetStar: // perfect break; default: matchDistance += 10; break; } } else if (actual.isNull()) { switch (tid) { case QMetaType::VoidStar: case QMetaType::QObjectStar: case QMetaType::QWidgetStar: // 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 if (!conversionFailed.isEmpty()) { QString message = QString::fromLatin1("incompatible type of argument(s) in call to %0(); candidates were\n") .arg(QLatin1String(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.signature())); } 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(QString::fromLatin1(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(QLatin1String(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.signature())); } 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(funName)); 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.signature())); } result = JSC::throwError(exec, JSC::TypeError, message); } else { chosenMethod = metaArgs.method; chosenIndex = metaArgs.index; args = metaArgs.args; } } if (chosenIndex != -1) { // call it // context->calleeMetaIndex = chosenIndex; 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; 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 = engine->jscValueFromVariant(*(QVariant *)params[0]); } else if (retType.typeId() != 0) { result = engine->scriptValueToJSCValue(engine->create(retType.typeId(), params[0])); if (!result) { QScriptValue sv = QScriptEnginePrivate::get(engine)->newVariant(QVariant(retType.typeId(), params[0])); result = engine->scriptValueToJSCValue(sv); } } else { result = JSC::jsUndefined(); } } } } return result; } JSC::JSValue QtFunction::execute(JSC::ExecState *exec, JSC::JSValue thisValue, const JSC::ArgList &scriptArgs) { Q_ASSERT(data->object.isObject(&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(); Q_ASSERT_X(qobj != 0, "QtFunction::call", "handle the case when QObject has been deleted"); QScriptEnginePrivate *engine = scriptEngineFromExec(exec); const QMetaObject *meta = qobj->metaObject(); QObject *thisQObject = 0; thisValue = engine->toUsableValue(thisValue); if (thisValue.isObject(&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->isObject(&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; } 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->isObject(&QtPropertyFunction::info)) return throwError(exec, JSC::TypeError, "callee is not a QtPropertyFunction object"); QtPropertyFunction *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; } JSC::JSValue QtPropertyFunction::execute(JSC::ExecState *exec, JSC::JSValue thisValue, const JSC::ArgList &args) { JSC::JSValue result = JSC::jsUndefined(); // ### don't go via QScriptValue QScriptEnginePrivate *engine = scriptEngineFromExec(exec); thisValue = engine->toUsableValue(thisValue); QScriptValue object = engine->scriptValueFromJSCValue(thisValue); QObject *qobject = object.toQObject(); while ((!qobject || (qobject->metaObject() != data->meta)) && object.prototype().isObject()) { object = object.prototype(); qobject = object.toQObject(); } 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) { oldEngine = QScriptablePrivate::get(scriptable)->engine; QScriptablePrivate::get(scriptable)->engine = QScriptEnginePrivate::get(engine); } QVariant v = prop.read(qobject); if (scriptable) QScriptablePrivate::get(scriptable)->engine = oldEngine; result = engine->jscValueFromVariant(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 { // ### don't go via QScriptValue QScriptValue tmp = engine->scriptValueFromJSCValue(arg); v = variantFromValue(engine, prop.userType(), tmp); } QScriptable *scriptable = scriptableFromQObject(qobject); QScriptEngine *oldEngine = 0; if (scriptable) { oldEngine = QScriptablePrivate::get(scriptable)->engine; QScriptablePrivate::get(scriptable)->engine = QScriptEnginePrivate::get(engine); } prop.write(qobject, v); if (scriptable) QScriptablePrivate::get(scriptable)->engine = oldEngine; result = arg; } 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) { QByteArray name = QString(propertyName.ustring()).toLatin1(); 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 = eng->jscValueFromVariant(prop.read(qobject)); slot.setValue(val); } return true; } } } index = qobject->dynamicPropertyNames().indexOf(name); if (index != -1) { JSC::JSValue val = eng->jscValueFromVariant(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) && (methodName(method) == 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; QScriptValue tmp = QScriptEnginePrivate::get(eng)->newQObject(child, QScriptEngine::QtOwnership, opt); slot.setValue(eng->scriptValueToJSCValue(tmp)); return true; } } } return QScriptObjectDelegate::getOwnPropertySlot(object, exec, propertyName, slot); } void QObjectDelegate::put(QScriptObject *object, JSC::ExecState* exec, const JSC::Identifier& propertyName, JSC::JSValue value, JSC::PutPropertySlot &slot) { QByteArray name = ((QString)propertyName.ustring()).toLatin1(); 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 = eng->jscValueToVariant(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) && (methodName(method) == name)) { data->cachedMembers.insert(name, value); return; } } index = qobject->dynamicPropertyNames().indexOf(name); if ((index != -1) || (opt & QScriptEngine::AutoCreateDynamicProperties)) { QVariant v = eng->scriptValueFromJSCValue(value).toVariant(); (void)qobject->setProperty(name, v); return; } QScriptObjectDelegate::put(object, exec, propertyName, value, slot); } bool QObjectDelegate::deleteProperty(QScriptObject *object, JSC::ExecState *exec, const JSC::Identifier& propertyName, bool checkDontDelete) { QByteArray name = ((QString)propertyName.ustring()).toLatin1(); 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, checkDontDelete); } bool QObjectDelegate::getPropertyAttributes(const QScriptObject *object, JSC::ExecState *exec, const JSC::Identifier &propertyName, unsigned &attributes) const { // ### try to avoid duplicating logic from getOwnPropertySlot() QByteArray name = ((QString)propertyName.ustring()).toLatin1(); QObject *qobject = data->value; if (!qobject) return false; const QScriptEngine::QObjectWrapOptions &opt = data->options; const QMetaObject *meta = qobject->metaObject(); 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())) { attributes = QObjectMemberAttribute; if (opt & QScriptEngine::SkipMethodsInEnumeration) attributes |= JSC::DontEnum; return true; } } } } index = meta->indexOfProperty(name); if (index != -1) { QMetaProperty prop = meta->property(index); if (prop.isScriptable()) { if (!(opt & QScriptEngine::ExcludeSuperClassProperties) || (index >= meta->propertyOffset())) { attributes = flagsForMetaProperty(prop); return true; } } } index = qobject->dynamicPropertyNames().indexOf(name); if (index != -1) { attributes = 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) && (methodName(method) == name)) { attributes = QObjectMemberAttribute; if (opt & QScriptEngine::SkipMethodsInEnumeration) attributes |= JSC::DontEnum; 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())) { attributes = JSC::ReadOnly | JSC::DontDelete | JSC::DontEnum; return true; } } } return QScriptObjectDelegate::getPropertyAttributes(object, exec, propertyName, attributes); } void QObjectDelegate::getPropertyNames(QScriptObject *object, JSC::ExecState *exec, JSC::PropertyNameArray &propertyNames, unsigned listedAttributes) { 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.signature()); propertyNames.add(JSC::Identifier(exec, sig)); } } } QScriptObjectDelegate::getPropertyNames(object, exec, propertyNames, listedAttributes); } void QObjectDelegate::mark(QScriptObject *object) { QHash::const_iterator it; for (it = data->cachedMembers.constBegin(); it != data->cachedMembers.constEnd(); ++it) { JSC::JSValue val = it.value(); if (val && !val.marked()) val.mark(); } QScriptObjectDelegate::mark(object); } 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.isObject(&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 = qFindChild(obj, 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.isObject(&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.isObject(&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 = qFindChildren(obj, name); } } else { children = qFindChildren(obj, 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.isObject(&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::PrototypeFunction(exec, prototypeFunctionStructure, /*length=*/0, exec->propertyNames().toString, qobjectProtoFuncToString), JSC::DontEnum); putDirectFunction(exec, new (exec) JSC::PrototypeFunction(exec, prototypeFunctionStructure, /*length=*/1, JSC::Identifier(exec, "findChild"), qobjectProtoFuncFindChild), JSC::DontEnum); putDirectFunction(exec, new (exec) JSC::PrototypeFunction(exec, prototypeFunctionStructure, /*length=*/1, JSC::Identifier(exec, "findChildren"), qobjectProtoFuncFindChildren), JSC::DontEnum); } 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 = 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())) { slot.setValue(JSC::JSValue(exec, e.value(j))); return true; } } } return JSC::JSObject::getOwnPropertySlot(exec, propertyName, slot); } 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 = 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) { 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, bool checkDontDelete) { if (propertyName == exec->propertyNames().prototype) return false; const QMetaObject *meta = data->value; if (meta) { 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) { if (!qstrcmp(e.key(j), name.constData())) return false; } } } return JSC::JSObject::deleteProperty(exec, propertyName, checkDontDelete); } bool QMetaObjectWrapperObject::getPropertyAttributes(JSC::ExecState *exec, const JSC::Identifier &propertyName, unsigned &attributes) const { if (propertyName == exec->propertyNames().prototype) { attributes = JSC::DontDelete; return true; } const QMetaObject *meta = data->value; if (meta) { 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) { if (!qstrcmp(e.key(j), name.constData())) { attributes = JSC::ReadOnly | JSC::DontDelete; return true; } } } } return JSC::JSObject::getPropertyAttributes(exec, propertyName, attributes); } void QMetaObjectWrapperObject::getPropertyNames(JSC::ExecState *exec, JSC::PropertyNameArray &propertyNames, unsigned listedAttributes) { 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::getPropertyNames(exec, propertyNames, listedAttributes); } void QMetaObjectWrapperObject::mark() { Q_ASSERT(!marked()); if (data->ctor && !data->ctor.marked()) data->ctor.mark(); if (data->prototype && !data->prototype.marked()) data->prototype.mark(); JSC::JSObject::mark(); } 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->isObject(&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.isObject(&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.isObject(&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.isObject(&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.isObject(&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::PrototypeFunction(exec, prototypeFunctionStructure, /*length=*/0, JSC::Identifier(exec, "className"), qmetaobjectProtoFuncClassName), JSC::DontEnum); } static const uint qt_meta_data_QObjectConnectionManager[] = { // content: 1, // revision 0, // classname 0, 0, // classinfo 1, 10, // methods 0, 0, // properties 0, 0, // enums/sets // slots: signature, parameters, type, tag, flags 35, 34, 34, 34, 0x0a, 0 // eod }; static const char qt_meta_stringdata_QObjectConnectionManager[] = { "QScript::QObjectConnectionManager\0\0execute()\0" }; const QMetaObject QObjectConnectionManager::staticMetaObject = { { &QObject::staticMetaObject, qt_meta_stringdata_QObjectConnectionManager, qt_meta_data_QObjectConnectionManager, 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)) 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) { execute(_id, _a); _id -= slotCounter; } return _id; } void QObjectConnectionManager::execute(int slotIndex, void **argv) { JSC::JSValue receiver; JSC::JSValue slot; JSC::JSValue senderWrapper; int signalIndex = -1; 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; } } } Q_ASSERT(slot && 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) { // ### optimize -- no need to convert via QScriptValue QScriptValue actual; void *arg = argv[i + 1]; QByteArray typeName = parameterTypes.at(i); int argType = QMetaType::type(parameterTypes.at(i)); if (!argType) { if (typeName == "QVariant") { actual = engine->scriptValueFromVariant(*reinterpret_cast(arg)); } else { qWarning("QScriptEngine: Unable to handle unregistered datatype '%s' " "when invoking handler of signal %s::%s", typeName.constData(), meta->className(), method.signature()); actual = QScriptValue(QScriptValue::UndefinedValue); } } else { actual = engine->create(argType, arg); } argsVector[i] = engine->scriptValueToJSCValue(actual); } JSC::ArgList jscArgs(argsVector.data(), argsVector.size()); JSC::JSValue senderObject; if (senderWrapper && senderWrapper.isObject(&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()) { engine->emitSignalHandlerException(); } } QObjectConnectionManager::QObjectConnectionManager(QScriptEnginePrivate *eng) : engine(eng), slotCounter(0) { } QObjectConnectionManager::~QObjectConnectionManager() { } void QObjectConnectionManager::mark() { for (int i = 0; i < connections.size(); ++i) { QVector &cs = connections[i]; for (int j = 0; j < cs.size(); ++j) cs[j].mark(); } } 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)); QMetaMethod signal = sender->metaObject()->method(signalIndex); QByteArray signalString; signalString.append('2'); // signal code signalString.append(signal.signature()); static_cast(sender)->callConnectNotify(signalString); } 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); QMetaMethod signal = sender->metaObject()->method(signalIndex); QByteArray signalString; signalString.append('2'); // signal code signalString.append(signal.signature()); static_cast(sender)->callDisconnectNotify(signalString); } return ok; } } return false; } QObjectData::QObjectData(QScriptEnginePrivate *eng) : engine(eng), connectionManager(0) { } QObjectData::~QObjectData() { if (connectionManager) { delete connectionManager; connectionManager = 0; } } void QObjectData::mark() { if (connectionManager) connectionManager->mark(); { QList::iterator it; for (it = wrappers.begin(); it != wrappers.end(); ) { const QScript::QObjectWrapperInfo &info = *it; if (info.object->marked()) { ++it; } else { it = wrappers.erase(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"