diff options
author | Simon Hausmann <simon.hausmann@nokia.com> | 2009-06-16 16:18:58 (GMT) |
---|---|---|
committer | Simon Hausmann <simon.hausmann@nokia.com> | 2009-06-16 16:18:59 (GMT) |
commit | d612b4789f7ec891ada16afbbbf1c13ab0f0e575 (patch) | |
tree | 0e25f0dd66abbe087220c3de9c258bc6215db639 /src/script/bridge | |
parent | 94e39aff7dd02d4a631d5c40c6f5a5f6fa424035 (diff) | |
download | Qt-d612b4789f7ec891ada16afbbbf1c13ab0f0e575.zip Qt-d612b4789f7ec891ada16afbbbf1c13ab0f0e575.tar.gz Qt-d612b4789f7ec891ada16afbbbf1c13ab0f0e575.tar.bz2 |
Import JSC-based Qt Script from Kent's tree.
Diffstat (limited to 'src/script/bridge')
-rw-r--r-- | src/script/bridge/bridge.pri | 9 | ||||
-rw-r--r-- | src/script/bridge/qscriptfunction.cpp | 137 | ||||
-rw-r--r-- | src/script/bridge/qscriptfunction_p.h | 92 | ||||
-rw-r--r-- | src/script/bridge/qscriptqobject.cpp | 1338 | ||||
-rw-r--r-- | src/script/bridge/qscriptqobject_p.h | 134 | ||||
-rw-r--r-- | src/script/bridge/qscriptvariant.cpp | 102 | ||||
-rw-r--r-- | src/script/bridge/qscriptvariant_p.h | 72 |
7 files changed, 1884 insertions, 0 deletions
diff --git a/src/script/bridge/bridge.pri b/src/script/bridge/bridge.pri new file mode 100644 index 0000000..585ccc0 --- /dev/null +++ b/src/script/bridge/bridge.pri @@ -0,0 +1,9 @@ +SOURCES += \ + $$PWD/qscriptfunction.cpp \ + $$PWD/qscriptvariant.cpp \ + $$PWD/qscriptqobject.cpp + +HEADERS += \ + $$PWD/qscriptfunction_p.h \ + $$PWD/qscriptvariant_p.h \ + $$PWD/qscriptqobject_p.h diff --git a/src/script/bridge/qscriptfunction.cpp b/src/script/bridge/qscriptfunction.cpp new file mode 100644 index 0000000..fd51df1 --- /dev/null +++ b/src/script/bridge/qscriptfunction.cpp @@ -0,0 +1,137 @@ +/**************************************************************************** +** +** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $TROLLTECH_DUAL_LICENSE$ +** +****************************************************************************/ + +#include "qscriptfunction_p.h" + +#ifndef QT_NO_SCRIPT + +#include "private/qscriptengine_p.h" +#include "qscriptcontext.h" +#include "private/qscriptcontext_p.h" + +#include "JSGlobalObject.h" + +QT_BEGIN_NAMESPACE + +namespace JSC +{ +ASSERT_CLASS_FITS_IN_CELL(QScript::FunctionWrapper); +ASSERT_CLASS_FITS_IN_CELL(QScript::FunctionWithArgWrapper); +} + +namespace QScript +{ + +FunctionWrapper::FunctionWrapper(QScriptEngine *engine, int length, const JSC::Identifier &name, + QScriptEngine::FunctionSignature function) + : JSC::PrototypeFunction(QScriptEnginePrivate::get(engine)->globalObject->globalExec(), + length, name, proxyCall), data(new Data()) +{ + data->engine = engine; + data->function = function; +} + +FunctionWrapper::~FunctionWrapper() +{ + delete data; +} + +JSC::ConstructType FunctionWrapper::getConstructData(JSC::ConstructData& consData) +{ + consData.native.function = proxyConstruct; + return JSC::ConstructTypeHost; +} + +JSC::JSValue FunctionWrapper::proxyCall(JSC::ExecState *, JSC::JSObject *callee, + JSC::JSValue thisObject, const JSC::ArgList &args) +{ + FunctionWrapper *self = static_cast<FunctionWrapper*>(callee); + QScriptEnginePrivate *eng_p = QScriptEnginePrivate::get(self->data->engine); + QScriptContextPrivate ctx_p(callee, thisObject, args, + /*calledAsConstructor=*/false, eng_p); + QScriptContext *ctx = QScriptContextPrivate::create(ctx_p); + QScriptValue result = self->data->function(ctx, self->data->engine); + delete ctx; + return eng_p->scriptValueToJSCValue(result); +} + +JSC::JSObject* FunctionWrapper::proxyConstruct(JSC::ExecState *, JSC::JSObject *callee, + const JSC::ArgList &args) +{ + FunctionWrapper *self = static_cast<FunctionWrapper*>(callee); + QScriptValue object = self->data->engine->newObject(); + QScriptEnginePrivate *eng_p = QScriptEnginePrivate::get(self->data->engine); + QScriptContextPrivate ctx_p(callee, eng_p->scriptValueToJSCValue(object), + args, /*calledAsConstructor=*/true, eng_p); + QScriptContext *ctx = QScriptContextPrivate::create(ctx_p); + QScriptValue result = self->data->function(ctx, self->data->engine); + delete ctx; + if (result.isObject()) + return static_cast<JSC::JSObject*>(JSC::asObject(eng_p->scriptValueToJSCValue(result))); + return static_cast<JSC::JSObject*>(JSC::asObject(eng_p->scriptValueToJSCValue(object))); +} + +FunctionWithArgWrapper::FunctionWithArgWrapper(QScriptEngine *engine, int length, const JSC::Identifier &name, + QScriptEngine::FunctionWithArgSignature function, void *arg) + : JSC::PrototypeFunction(QScriptEnginePrivate::get(engine)->globalObject->globalExec(), + length, name, /*proxyCall*/0), data(new Data()) +{ + Q_ASSERT_X(false, Q_FUNC_INFO, "implement me"); + data->engine = engine; + data->function = function; + data->arg = arg; +} + +FunctionWithArgWrapper::~FunctionWithArgWrapper() +{ + delete data; +} + +JSC::ConstructType FunctionWithArgWrapper::getConstructData(JSC::ConstructData& consData) +{ + consData.native.function = proxyConstruct; + return JSC::ConstructTypeHost; +} + +JSC::JSValue FunctionWithArgWrapper::proxyCall(JSC::ExecState *, JSC::JSObject *callee, + JSC::JSValue thisObject, const JSC::ArgList &args) +{ + FunctionWithArgWrapper *self = static_cast<FunctionWithArgWrapper*>(callee); + QScriptEnginePrivate *eng_p = QScriptEnginePrivate::get(self->data->engine); + QScriptContextPrivate ctx_p(callee, thisObject, args, + /*calledAsConstructor=*/false, eng_p); + QScriptContext *ctx = QScriptContextPrivate::create(ctx_p); + QScriptValue result = self->data->function(ctx, self->data->engine, self->data->arg); + delete ctx; + return eng_p->scriptValueToJSCValue(result); +} + +JSC::JSObject* FunctionWithArgWrapper::proxyConstruct(JSC::ExecState *, JSC::JSObject *callee, + const JSC::ArgList &args) +{ + FunctionWithArgWrapper *self = static_cast<FunctionWithArgWrapper*>(callee); + QScriptValue object = self->data->engine->newObject(); + QScriptEnginePrivate *eng_p = QScriptEnginePrivate::get(self->data->engine); + QScriptContextPrivate ctx_p(callee, eng_p->scriptValueToJSCValue(object), + args, /*calledAsConstructor=*/true, eng_p); + QScriptContext *ctx = QScriptContextPrivate::create(ctx_p); + QScriptValue result = self->data->function(ctx, self->data->engine, self->data->arg); + delete ctx; + if (result.isObject()) + return static_cast<JSC::JSObject*>(JSC::asObject(eng_p->scriptValueToJSCValue(result))); + return static_cast<JSC::JSObject*>(JSC::asObject(eng_p->scriptValueToJSCValue(object))); +} + +} // namespace QScript + +QT_END_NAMESPACE + +#endif // QT_NO_SCRIPT diff --git a/src/script/bridge/qscriptfunction_p.h b/src/script/bridge/qscriptfunction_p.h new file mode 100644 index 0000000..87e3d59 --- /dev/null +++ b/src/script/bridge/qscriptfunction_p.h @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $TROLLTECH_DUAL_LICENSE$ +** +****************************************************************************/ + +#ifndef QSCRIPTFUNCTION_P_H +#define QSCRIPTFUNCTIOn_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qglobal.h> + +#ifndef QT_NO_SCRIPT + +#include "qscriptengine.h" + +#include "PrototypeFunction.h" + +QT_BEGIN_NAMESPACE + +namespace QScript +{ + +class FunctionWrapper : public JSC::PrototypeFunction // ### subclass InternalFunction instead +{ +public: + // work around CELL_SIZE limitation + struct Data + { + QScriptEngine *engine; + QScriptEngine::FunctionSignature function; + }; + + FunctionWrapper(QScriptEngine *, int length, const JSC::Identifier&, QScriptEngine::FunctionSignature); + ~FunctionWrapper(); + +private: + virtual JSC::ConstructType getConstructData(JSC::ConstructData&); + + static JSC::JSValue JSC_HOST_CALL proxyCall(JSC::ExecState *, JSC::JSObject *, JSC::JSValue, const JSC::ArgList &); + static JSC::JSObject* proxyConstruct(JSC::ExecState *, JSC::JSObject *, const JSC::ArgList &); + +private: + Data *data; +}; + +class FunctionWithArgWrapper : public JSC::PrototypeFunction +{ +public: + // work around CELL_SIZE limitation + struct Data + { + QScriptEngine *engine; + QScriptEngine::FunctionWithArgSignature function; + void *arg; + }; + + FunctionWithArgWrapper(QScriptEngine *, int length, const JSC::Identifier&, QScriptEngine::FunctionWithArgSignature, void *); + ~FunctionWithArgWrapper(); + +private: + virtual JSC::ConstructType getConstructData(JSC::ConstructData&); + + static JSC::JSValue proxyCall(JSC::ExecState *, JSC::JSObject *, JSC::JSValue , const JSC::ArgList &); + static JSC::JSObject* proxyConstruct(JSC::ExecState *, JSC::JSObject *, const JSC::ArgList &); + +private: + Data *data; +}; + +} // namespace QScript + +QT_END_NAMESPACE + +#endif // QT_NO_SCRIPT + +#endif diff --git a/src/script/bridge/qscriptqobject.cpp b/src/script/bridge/qscriptqobject.cpp new file mode 100644 index 0000000..84f6108d --- /dev/null +++ b/src/script/bridge/qscriptqobject.cpp @@ -0,0 +1,1338 @@ +/**************************************************************************** +** +** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $TROLLTECH_DUAL_LICENSE$ +** +****************************************************************************/ + +#include "qscriptqobject_p.h" + +#ifndef QT_NO_SCRIPT + +#include <QtCore/qmetaobject.h> +#include <QtCore/qvarlengtharray.h> +#include <QtCore/qdebug.h> +#include <QtScript/qscriptable.h> +#include "../api/qscriptengine_p.h" +#include "../api/qscriptable_p.h" + +#include "Error.h" +#include "PrototypeFunction.h" +#include "PropertyNameArray.h" +#include "JSFunction.h" +#include "JSString.h" +#include "JSValue.h" + +QT_BEGIN_NAMESPACE + +namespace JSC +{ +ASSERT_CLASS_FITS_IN_CELL(QScript::QObjectWrapperObject); +ASSERT_CLASS_FITS_IN_CELL(QScript::QObjectPrototype); +} + +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.isObject() != receiver.isObject()) + return false; + if ((r.isObject() && receiver.isObject()) + && (r != receiver)) { + return false; + } + return (s == slot); + } + + void mark() + { + Q_ASSERT_X(false, Q_FUNC_INFO, "implement me"); +/* if (senderWrapper && !senderWrapper->isMarked()) { + // see if the sender should be marked or not + if ((inst->ownership == QScriptEngine::ScriptOwnership) + || ((inst->ownership == QScriptEngine::AutoOwnership) + && inst->value && !inst->value->parent())) { + senderWrapper.invalidate(); + } else { + senderWrapper.mark(generation); + } + }*/ + if (receiver) + receiver.mark(); + if (slot) + slot.mark(); + } +}; + +class QObjectConnectionManager: public QObject +{ +public: + QObjectConnectionManager(QScriptEnginePrivate *engine); + ~QObjectConnectionManager(); + + bool addSignalHandler(QObject *sender, const char *signal, + JSC::JSValue receiver, + JSC::JSValue slot, + JSC::JSValue senderWrapper = 0); + bool removeSignalHandler(QObject *sender, const char *signal, + 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<QVector<QObjectConnection> > 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 int indexOfMetaEnum(const QMetaObject *meta, const QByteArray &str) +{ + QByteArray scope; + QByteArray name; + int scopeIdx = str.indexOf("::"); + 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<QScriptable*>(ptr); +} + +JSC::UString qtStringToJSCUString(const QString &str); +QString qtStringFromJSCUString(const JSC::UString &str); + +static JSC::JSValue JSC_HOST_CALL QtFunction_call(JSC::ExecState *exec, JSC::JSObject*, + JSC::JSValue thisValue, const JSC::ArgList &args); + +class QtFunction: public JSC::InternalFunction +{ +public: + // work around CELL_SIZE limitation + struct Data + { + JSC::JSValue object; + int initialIndex; + bool maybeOverloaded; + + Data(JSC::JSValue o, int ii, bool mo) + : object(o), initialIndex(ii), maybeOverloaded(mo) {} + }; + + QtFunction(JSC::JSValue object, int initialIndex, bool maybeOverloaded, + JSC::JSGlobalData*, WTF::PassRefPtr<JSC::Structure>, const JSC::Identifier&); + virtual ~QtFunction(); + + virtual JSC::CallType getCallData(JSC::CallData&); + virtual void mark(); + + virtual const JSC::ClassInfo* classInfo() const { return &info; } + static const JSC::ClassInfo info; + + JSC::JSValue call(JSC::ExecState *exec, JSC::JSValue thisValue, + const JSC::ArgList &args); + +private: + Data *data; +}; + +QtFunction::QtFunction(JSC::JSValue object, int initialIndex, bool maybeOverloaded, + JSC::JSGlobalData *data, WTF::PassRefPtr<JSC::Structure> 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 = QtFunction_call; + return JSC::CallTypeHost; +} + +void QtFunction::mark() +{ + data->object.mark(); +} + +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; } + + 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<QScriptMetaType> &types) + : m_name(name), m_types(types), m_firstUnresolvedIndex(-1) + { + QVector<QScriptMetaType>::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); } + +private: + QByteArray m_name; + QVector<QScriptMetaType> m_types; + int m_firstUnresolvedIndex; +}; + +struct QScriptMetaArguments +{ + int matchDistance; + int index; + QScriptMetaMethod method; + QVarLengthArray<QVariant, 9> args; + + inline QScriptMetaArguments(int dist, int idx, const QScriptMetaMethod &mtd, + const QVarLengthArray<QVariant, 9> &as) + : matchDistance(dist), index(idx), method(mtd), args(as) { } + inline QScriptMetaArguments() + : index(-1) { } + + inline bool isValid() const + { return (index != -1); } +}; + +JSC::JSValue QtFunction::call(JSC::ExecState *exec, JSC::JSValue thisValue, + const JSC::ArgList &scriptArgs) +{ + Q_ASSERT(data->object.isObject(&QObjectWrapperObject::info)); + QObjectWrapperObject *wrapper = static_cast<QObjectWrapperObject*>(JSC::asObject(data->object)); + QScriptEnginePrivate *engine = static_cast<QScript::GlobalObject*>(exec->dynamicGlobalObject())->engine; + QObject *qobj = wrapper->value(); + Q_ASSERT_X(qobj != 0, "QtFunction::call", "handle the case when QObject has been deleted"); + + const QMetaObject *meta = qobj->metaObject(); + QObject *thisQObject; + if (thisValue.isObject(&QObjectWrapperObject::info)) + thisQObject = static_cast<QObjectWrapperObject*>(JSC::asObject(thisValue))->value(); + else + thisQObject = qobj; // ### TypeError + + if (!meta->cast(thisQObject)) { + // invoking a function in the prototype + thisQObject = qobj; + } + + QByteArray funName; + QScriptMetaMethod chosenMethod; + int chosenIndex = -1; + QVarLengthArray<QVariant, 9> args; + QVector<QScriptMetaArguments> candidates; + QVector<QScriptMetaArguments> unresolved; + QVector<int> tooFewArgs; + QVector<int> conversionFailed; + int index; + for (index = data->initialIndex; index >= 0; --index) { + QMetaMethod method = meta->method(index); + + if (index == data->initialIndex) + funName = methodName(method); + else { + if (methodName(method) != funName) + continue; + } + + QVector<QScriptMetaType> 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<QByteArray> 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 (args.count() < 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<QVariant, 9>())); + 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 = engine->scriptValueFromJSCValue(scriptArgs.at(i)); + 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<void* *>(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 (engine->hasUncaughtException()) +// return; + } + + 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<void* *>(vv.data())); + converted = true; + matchDistance += 10; + } + } + } else if (actual.isNumber()) { + // 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()) { + int ival = actual.toInt32(); + if (m.valueToKey(ival) != 0) { + 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 { + QScriptMetaArguments metaArgs(matchDistance, index, mtd, args); + if (candidates.isEmpty()) { + candidates.append(metaArgs); + } else { + 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 (!data->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 = meta->method(conversionFailed.at(i)); + message += QString::fromLatin1(" %0").arg(QString::fromLatin1(mtd.signature())); + } + result = JSC::throwError(exec, JSC::TypeError, qtStringToJSCUString(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, qtStringToJSCUString(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 = meta->method(tooFewArgs.at(i)); + message += QString::fromLatin1(" %0").arg(QString::fromLatin1(mtd.signature())); + } + result = JSC::throwError(exec, JSC::SyntaxError, qtStringToJSCUString(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 = meta->method(candidates.at(i).index); + message += QString::fromLatin1(" %0").arg(QString::fromLatin1(mtd.signature())); + } + result = JSC::throwError(exec, JSC::TypeError, qtStringToJSCUString(message)); + } else { + chosenMethod = metaArgs.method; + chosenIndex = metaArgs.index; + args = metaArgs.args; + } + } + + if (chosenIndex != -1) { + // call it +// context->calleeMetaIndex = chosenIndex; + + QVarLengthArray<void*, 9> 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<QVariant*>(&v); + break; + case QScriptMetaType::MetaType: + case QScriptMetaType::MetaEnum: + case QScriptMetaType::Unresolved: + params[i] = const_cast<void*>(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*/ { + Q_ASSERT(thisQObject != 0); + thisQObject->qt_metacall(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 JSC::JSValue(); +} + +const JSC::ClassInfo QtFunction::info = { "QtFunction", 0, 0, 0 }; + +JSC::JSValue 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); + return static_cast<QtFunction*>(callee)->call(exec, thisValue, args); +} + +const JSC::ClassInfo QObjectWrapperObject::info = { "QObject", 0, 0, 0 }; + +QObjectWrapperObject::QObjectWrapperObject( + QObject *object, QScriptEngine::ValueOwnership ownership, + const QScriptEngine::QObjectWrapOptions &options, + WTF::PassRefPtr<JSC::Structure> sid) + : JSC::JSObject(sid), data(new Data(object, ownership, options)) +{ +} + +QObjectWrapperObject::~QObjectWrapperObject() +{ + 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; +} + +bool QObjectWrapperObject::getOwnPropertySlot(JSC::ExecState *exec, + const JSC::Identifier &propertyName, + JSC::PropertySlot &slot) +{ + QByteArray name = qtStringFromJSCUString(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, qtStringToJSCUString(message)); + return false; + } + + const QScriptEngine::QObjectWrapOptions &opt = data->options; + const QMetaObject *meta = qobject->metaObject(); + QScriptEnginePrivate *eng = static_cast<QScript::GlobalObject*>(exec->dynamicGlobalObject())->engine; + 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( + this, index, /*maybeOverloaded=*/false, + &exec->globalData(), exec->dynamicGlobalObject()->functionStructure(), + propertyName); + slot.setValue(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())) { + JSC::JSValue 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( + this, index, /*maybeOverloaded=*/true, + &exec->globalData(), exec->dynamicGlobalObject()->functionStructure(), + propertyName); + slot.setValue(fun); + return true; + } + } + + if (!(opt & QScriptEngine::ExcludeChildObjects)) { + QList<QObject*> children = qobject->children(); + for (index = 0; index < children.count(); ++index) { + QObject *child = children.at(index); + if (child->objectName() == qtStringFromJSCUString(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 JSC::JSObject::getOwnPropertySlot(exec, propertyName, slot); +} + +void QObjectWrapperObject::put(JSC::ExecState* exec, const JSC::Identifier& propertyName, + JSC::JSValue value, JSC::PutPropertySlot &slot) +{ + QByteArray name = qtStringFromJSCUString(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, qtStringToJSCUString(message)); + return; + } + + const QScriptEngine::QObjectWrapOptions &opt = data->options; + const QMetaObject *meta = qobject->metaObject(); + QScriptEnginePrivate *eng = static_cast<QScript::GlobalObject*>(exec->dynamicGlobalObject())->engine; + 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())) { + Q_ASSERT(0); + return; + } + } + } + } + + index = meta->indexOfProperty(name); + if (index != -1) { + QMetaProperty prop = meta->property(index); + if (prop.isScriptable()) { + if (!(opt & QScriptEngine::ExcludeSuperClassProperties) + || (index >= meta->propertyOffset())) { + QVariant v = eng->jscValueToVariant(value, prop.userType()); + (void)prop.write(qobject, v); + return; + } + } + } + + index = qobject->dynamicPropertyNames().indexOf(name); + if (index != -1) { + QVariant v = eng->scriptValueFromJSCValue(value).toVariant(); + (void)qobject->setProperty(name, v); + return; + } + + JSC::JSObject::put(exec, propertyName, value, slot); +} + +bool QObjectWrapperObject::deleteProperty(JSC::ExecState *exec, + const JSC::Identifier& propertyName) +{ + QByteArray name = qtStringFromJSCUString(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, qtStringToJSCUString(message)); + return false; + } + + int index = qobject->dynamicPropertyNames().indexOf(name); + if (index != -1) { + (void)qobject->setProperty(name, QVariant()); + return true; + } + + return JSC::JSObject::deleteProperty(exec, propertyName); +} + +void QObjectWrapperObject::getPropertyNames(JSC::ExecState *exec, JSC::PropertyNameArray &propertyNames) +{ + QObject *qobject = data->value; + if (!qobject) { + QString message = QString::fromLatin1("cannot get property names of deleted QObject"); + JSC::throwError(exec, JSC::GeneralError, qtStringToJSCUString(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, qtStringToJSCUString(name))); + } + } + } + + { + QList<QByteArray> dpNames = qobject->dynamicPropertyNames(); + for (int i = 0; i < dpNames.size(); ++i) { + QString name = QString::fromLatin1(dpNames.at(i)); + propertyNames.add(JSC::Identifier(exec, qtStringToJSCUString(name))); + } + } + + { + 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 name = QString::fromLatin1(methodName(method)); + propertyNames.add(JSC::Identifier(exec, qtStringToJSCUString(name))); + QString sig = QString::fromLatin1(method.signature()); + propertyNames.add(JSC::Identifier(exec, qtStringToJSCUString(sig))); + } + } + } + + JSC::JSObject::getPropertyNames(exec, propertyNames); +} + +static JSC::JSValue JSC_HOST_CALL qobjectProtoFuncToString(JSC::ExecState *exec, JSC::JSObject*, + JSC::JSValue thisValue, const JSC::ArgList&) +{ + if (!thisValue.isObject(&QObjectWrapperObject::info)) + return throwError(exec, JSC::TypeError); + QObject *obj = static_cast<QObjectWrapperObject*>(JSC::asObject(thisValue))->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, qtStringToJSCUString(str)); +} + +QObjectPrototype::QObjectPrototype(JSC::ExecState* exec, WTF::PassRefPtr<JSC::Structure> structure, + JSC::Structure* prototypeFunctionStructure) + : QObjectWrapperObject(new QObject(), QScriptEngine::AutoOwnership, /*options=*/0, structure) +{ + putDirectFunction(exec, new (exec) JSC::PrototypeFunction(exec, prototypeFunctionStructure, /*length=*/0, exec->propertyNames().toString, qobjectProtoFuncToString), JSC::DontEnum); + // ### findChild(), findChildren() +} + +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<void*>(const_cast<QObjectConnectionManager*>(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<QObjectConnection> &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 != 0); + +#if 0 + // ### fixme + if (engine->isCollecting()) { + // we can't do a script function call during GC, + // so we're forced to ignore this signal + return; + } + + 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<QByteArray> parameterTypes = method.parameterTypes(); + int argc = parameterTypes.count(); + + JSC::ExecState *exec = engine->globalObject->globalExec(); + JSC::ArgList jscArgs; + for (int i = 0; i < argc; ++i) { + int argType = QMetaType::type(parameterTypes.at(i)); + QScriptValue arg = engine->create(argType, argv[i + 1]); + Q_ASSERT_X(false, Q_FUNC_INFO, "implement me"); +#if 0 + jscArgs.append(engine->scriptValueToJSCValue(arg)); +#endif + } + + JSC::JSValue senderObject; + if (senderWrapper && senderWrapper.isObject(&QObjectWrapperObject::info)) + 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 = exec->dynamicGlobalObject(); + + (void)static_cast<JSC::JSFunction*>(JSC::asFunction(slot))->call(exec, 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<QObjectConnection> &cs = connections[i]; + for (int j = 0; j < cs.size(); ++j) + cs[j].mark(); + } +} + +bool QObjectConnectionManager::addSignalHandler( + QObject *sender, const char *signal, JSC::JSValue receiver, + JSC::JSValue function, JSC::JSValue senderWrapper) +{ + Q_ASSERT(sender != 0); + Q_ASSERT(signal != 0); + Q_ASSERT(function.isObject(&JSC::JSFunction::info)); + const QMetaObject *meta = sender->metaObject(); + int signalIndex = meta->indexOfSignal(QMetaObject::normalizedSignature(signal+1)); + if (signalIndex == -1) + return false; + if (connections.size() <= signalIndex) + connections.resize(signalIndex+1); + QVector<QObjectConnection> &cs = connections[signalIndex]; + int absSlotIndex = slotCounter + metaObject()->methodOffset(); + bool ok = QMetaObject::connect(sender, signalIndex, this, absSlotIndex); + if (ok) + cs.append(QObjectConnection(slotCounter++, receiver, function, senderWrapper)); + return ok; +} + +bool QObjectConnectionManager::removeSignalHandler( + QObject *sender, const char *signal, + JSC::JSValue receiver, JSC::JSValue slot) +{ + Q_ASSERT(sender != 0); + Q_ASSERT(signal != 0); + const QMetaObject *meta = sender->metaObject(); + int signalIndex = meta->indexOfSignal(QMetaObject::normalizedSignature(signal+1)); + if (signalIndex == -1) + return false; + if (connections.size() <= signalIndex) + return false; + QVector<QObjectConnection> &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::mark() +{ + if (connectionManager) + connectionManager->mark(); +} + +bool QObjectData::addSignalHandler(QObject *sender, + const char *signal, + JSC::JSValue receiver, + JSC::JSValue slot, + JSC::JSValue senderWrapper) +{ + if (!connectionManager) + connectionManager = new QObjectConnectionManager(engine); + return connectionManager->addSignalHandler( + sender, signal, receiver, slot, senderWrapper); +} + +bool QObjectData::removeSignalHandler(QObject *sender, + const char *signal, + JSC::JSValue receiver, + JSC::JSValue slot) +{ + if (!connectionManager) + return false; + return connectionManager->removeSignalHandler( + sender, signal, receiver, slot); +} + +} // namespace QScript + +namespace JSC +{ + ASSERT_CLASS_FITS_IN_CELL(QScript::QtFunction); +} + +QT_END_NAMESPACE + +#endif // QT_NO_SCRIPT diff --git a/src/script/bridge/qscriptqobject_p.h b/src/script/bridge/qscriptqobject_p.h new file mode 100644 index 0000000..3c2f2d2 --- /dev/null +++ b/src/script/bridge/qscriptqobject_p.h @@ -0,0 +1,134 @@ +/**************************************************************************** +** +** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $TROLLTECH_DUAL_LICENSE$ +** +****************************************************************************/ + +#ifndef QSCRIPTQOBJECT_P_H +#define QSCRIPTQOBJECT_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qobject.h> + +#ifndef QT_NO_SCRIPT + +#include "qscriptengine.h" +#include <QtCore/qpointer.h> + +#include "JSObject.h" + +QT_BEGIN_NAMESPACE + +namespace QScript +{ + +class QObjectWrapperObject : public JSC::JSObject +{ +public: + // work around CELL_SIZE limitation + struct Data + { + QPointer<QObject> value; + QScriptEngine::ValueOwnership ownership; + QScriptEngine::QObjectWrapOptions options; + + Data(QObject *o, QScriptEngine::ValueOwnership own, + QScriptEngine::QObjectWrapOptions opt) + : value(o), ownership(own), options(opt) {} + }; + + explicit QObjectWrapperObject( + QObject *object, QScriptEngine::ValueOwnership ownership, + const QScriptEngine::QObjectWrapOptions &options, + WTF::PassRefPtr<JSC::Structure> sid); + ~QObjectWrapperObject(); + + virtual bool getOwnPropertySlot(JSC::ExecState*, + const JSC::Identifier& propertyName, + JSC::PropertySlot&); + virtual void put(JSC::ExecState* exec, const JSC::Identifier& propertyName, + JSC::JSValue, JSC::PutPropertySlot&); + virtual bool deleteProperty(JSC::ExecState*, + const JSC::Identifier& propertyName); + virtual void getPropertyNames(JSC::ExecState*, JSC::PropertyNameArray&); + + virtual const JSC::ClassInfo* classInfo() const { return &info; } + static const JSC::ClassInfo info; + + inline QObject *value() const { return data->value; } + inline void setValue(QObject* value) { data->value = value; } + + inline QScriptEngine::ValueOwnership ownership() const + { return data->ownership; } + inline void setOwnership(QScriptEngine::ValueOwnership ownership) + { data->ownership = ownership; } + + inline QScriptEngine::QObjectWrapOptions options() const + { return data->options; } + inline void setOptions(QScriptEngine::QObjectWrapOptions options) + { data->options = options; } + + static WTF::PassRefPtr<JSC::Structure> createStructureID(JSC::JSValue prototype) + { + return JSC::Structure::create(prototype, JSC::TypeInfo(JSC::ObjectType)); + } + +protected: + Data *data; +}; + +class QObjectPrototype : public QObjectWrapperObject +{ +public: + QObjectPrototype(JSC::ExecState*, WTF::PassRefPtr<JSC::Structure>, + JSC::Structure* prototypeFunctionStructure); +}; + +class QObjectConnectionManager; + +class QObjectData // : public QObjectUserData +{ +public: + QObjectData(QScriptEnginePrivate *engine); + ~QObjectData(); + + bool addSignalHandler(QObject *sender, + const char *signal, + JSC::JSValue receiver, + JSC::JSValue slot, + JSC::JSValue senderWrapper = 0); + bool removeSignalHandler(QObject *sender, + const char *signal, + JSC::JSValue receiver, + JSC::JSValue slot); + + void mark(); + +private: + QScriptEnginePrivate *engine; + QScript::QObjectConnectionManager *connectionManager; +// QList<QScriptQObjectWrapperInfo> wrappers; +}; + +} // namespace QScript + +QT_END_NAMESPACE + +#endif // QT_NO_SCRIPT + +#endif diff --git a/src/script/bridge/qscriptvariant.cpp b/src/script/bridge/qscriptvariant.cpp new file mode 100644 index 0000000..0255961 --- /dev/null +++ b/src/script/bridge/qscriptvariant.cpp @@ -0,0 +1,102 @@ +/**************************************************************************** +** +** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $TROLLTECH_DUAL_LICENSE$ +** +****************************************************************************/ + +#include "qscriptvariant_p.h" + +#ifndef QT_NO_SCRIPT + +#include "Error.h" +#include "PrototypeFunction.h" +#include "JSString.h" + +QT_BEGIN_NAMESPACE + +namespace JSC +{ +ASSERT_CLASS_FITS_IN_CELL(QScript::QVariantWrapperObject); +ASSERT_CLASS_FITS_IN_CELL(QScript::QVariantPrototype); +} + +namespace QScript +{ + +JSC::UString qtStringToJSCUString(const QString &str); + +const JSC::ClassInfo QVariantWrapperObject::info = { "QVariant", 0, 0, 0 }; + +QVariantWrapperObject::QVariantWrapperObject(WTF::PassRefPtr<JSC::Structure> sid) + : JSC::JSObject(sid), data(new Data()) +{ +} + +QVariantWrapperObject::~QVariantWrapperObject() +{ + delete data; +} + +static JSC::JSValue JSC_HOST_CALL variantProtoFuncToString(JSC::ExecState *exec, JSC::JSObject*, + JSC::JSValue thisValue, const JSC::ArgList&) +{ + if (!thisValue.isObject(&QVariantWrapperObject::info)) + return throwError(exec, JSC::TypeError); + const QVariant &v = static_cast<QVariantWrapperObject*>(JSC::asObject(thisValue))->value(); + // ### check the type + return JSC::jsString(exec, QScript::qtStringToJSCUString(v.toString())); +} + +static JSC::JSValue JSC_HOST_CALL variantProtoFuncValueOf(JSC::ExecState *exec, JSC::JSObject*, + JSC::JSValue thisValue, const JSC::ArgList&) +{ + if (!thisValue.isObject(&QVariantWrapperObject::info)) + return throwError(exec, JSC::TypeError); + const QVariant &v = static_cast<QVariantWrapperObject*>(JSC::asObject(thisValue))->value(); + switch (v.type()) { + case QVariant::Invalid: + return JSC::jsUndefined(); + + case QVariant::String: + return JSC::jsString(exec, QScript::qtStringToJSCUString(v.toString())); + + case QVariant::Int: + return JSC::jsNumber(exec, v.toInt()); + + case QVariant::Bool: + return JSC::jsBoolean(v.toBool()); + + case QVariant::Double: + return JSC::jsNumber(exec, v.toDouble()); + +// case QVariant::Char: +// return JSC::jsNumber(exec, v.toChar().unicode()); + + case QVariant::UInt: + return JSC::jsNumber(exec, v.toUInt()); + default: + ; + } + return thisValue; +} + +QVariantPrototype::QVariantPrototype(JSC::ExecState* exec, WTF::PassRefPtr<JSC::Structure> structure, + JSC::Structure* prototypeFunctionStructure) + : QVariantWrapperObject(structure) +{ + setValue(QVariant()); + + putDirectFunction(exec, new (exec) JSC::PrototypeFunction(exec, prototypeFunctionStructure, 0, exec->propertyNames().toString, variantProtoFuncToString), JSC::DontEnum); + putDirectFunction(exec, new (exec) JSC::PrototypeFunction(exec, prototypeFunctionStructure, 0, exec->propertyNames().valueOf, variantProtoFuncValueOf), JSC::DontEnum); +} + +} // namespace QScript + +QT_END_NAMESPACE + +#endif // QT_NO_SCRIPT diff --git a/src/script/bridge/qscriptvariant_p.h b/src/script/bridge/qscriptvariant_p.h new file mode 100644 index 0000000..da0bb26 --- /dev/null +++ b/src/script/bridge/qscriptvariant_p.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the $MODULE$ of the Qt Toolkit. +** +** $TROLLTECH_DUAL_LICENSE$ +** +****************************************************************************/ + +#ifndef QSCRIPTVARIANT_P_H +#define QSCRIPTVARIANT_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qvariant.h> + +#ifndef QT_NO_SCRIPT + +#include "JSObject.h" + +QT_BEGIN_NAMESPACE + +namespace QScript +{ + +class QVariantWrapperObject : public JSC::JSObject +{ +public: + // work around CELL_SIZE limitation + struct Data + { + QVariant value; + }; + + explicit QVariantWrapperObject(WTF::PassRefPtr<JSC::Structure> sid); + ~QVariantWrapperObject(); + + virtual const JSC::ClassInfo* classInfo() const { return &info; } + static const JSC::ClassInfo info; + + inline const QVariant &value() const { return data->value; } + inline void setValue(const QVariant &value) { data->value = value; } + +private: + Data *data; +}; + +class QVariantPrototype : public QVariantWrapperObject +{ +public: + QVariantPrototype(JSC::ExecState*, WTF::PassRefPtr<JSC::Structure>, + JSC::Structure* prototypeFunctionStructure); +}; + +} // namespace QScript + +QT_END_NAMESPACE + +#endif // QT_NO_SCRIPT + +#endif |