diff options
Diffstat (limited to 'src/script/bridge')
21 files changed, 5812 insertions, 0 deletions
diff --git a/src/script/bridge/bridge.pri b/src/script/bridge/bridge.pri new file mode 100644 index 0000000..ab0a322 --- /dev/null +++ b/src/script/bridge/bridge.pri @@ -0,0 +1,23 @@ +SOURCES += \ + $$PWD/qscriptfunction.cpp \ + $$PWD/qscriptobject.cpp \ + $$PWD/qscriptclassobject.cpp \ + $$PWD/qscriptvariant.cpp \ + $$PWD/qscriptqobject.cpp \ + $$PWD/qscriptglobalobject.cpp \ + $$PWD/qscriptactivationobject.cpp \ + $$PWD/qscriptstaticscopeobject.cpp \ + $$PWD/qscriptdeclarativeobject.cpp \ + $$PWD/qscriptdeclarativeclass.cpp + +HEADERS += \ + $$PWD/qscriptfunction_p.h \ + $$PWD/qscriptobject_p.h \ + $$PWD/qscriptclassobject_p.h \ + $$PWD/qscriptvariant_p.h \ + $$PWD/qscriptqobject_p.h \ + $$PWD/qscriptglobalobject_p.h \ + $$PWD/qscriptactivationobject_p.h \ + $$PWD/qscriptstaticscopeobject_p.h \ + $$PWD/qscriptdeclarativeobject_p.h \ + $$PWD/qscriptdeclarativeclass_p.h diff --git a/src/script/bridge/qscriptactivationobject.cpp b/src/script/bridge/qscriptactivationobject.cpp new file mode 100644 index 0000000..88be4f9 --- /dev/null +++ b/src/script/bridge/qscriptactivationobject.cpp @@ -0,0 +1,154 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtScript module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL-ONLY$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "config.h" +#include "qscriptactivationobject_p.h" + +#include "JSVariableObject.h" + +namespace JSC +{ + ASSERT_CLASS_FITS_IN_CELL(QT_PREPEND_NAMESPACE(QScript::QScriptActivationObject)); +} + +QT_BEGIN_NAMESPACE + +/*! + \class QScript::QScriptActivationObject + \internal + + Represent a scope for native function call. +*/ + +namespace QScript +{ + +const JSC::ClassInfo QScriptActivationObject::info = { "QScriptActivationObject", 0, 0, 0 }; + +QScriptActivationObject::QScriptActivationObject(JSC::ExecState *callFrame, JSC::JSObject *delegate) + : JSC::JSVariableObject(callFrame->globalData().activationStructure, + new QScriptActivationObjectData(callFrame->registers(), delegate)) +{ +} + +QScriptActivationObject::~QScriptActivationObject() +{ + delete d_ptr(); +} + +bool QScriptActivationObject::getOwnPropertySlot(JSC::ExecState* exec, const JSC::Identifier& propertyName, JSC::PropertySlot& slot) +{ + if (d_ptr()->delegate != 0) + return d_ptr()->delegate->getOwnPropertySlot(exec, propertyName, slot); + return JSC::JSVariableObject::getOwnPropertySlot(exec, propertyName, slot); +} + +bool QScriptActivationObject::getOwnPropertyDescriptor(JSC::ExecState* exec, const JSC::Identifier& propertyName, JSC::PropertyDescriptor& descriptor) +{ + if (d_ptr()->delegate != 0) + return d_ptr()->delegate->getOwnPropertyDescriptor(exec, propertyName, descriptor); + return JSC::JSVariableObject::getOwnPropertyDescriptor(exec, propertyName, descriptor); +} + +void QScriptActivationObject::getOwnPropertyNames(JSC::ExecState* exec, JSC::PropertyNameArray& propertyNames, JSC::EnumerationMode mode) +{ + if (d_ptr()->delegate != 0) { + d_ptr()->delegate->getOwnPropertyNames(exec, propertyNames, mode); + return; + } + return JSC::JSVariableObject::getOwnPropertyNames(exec, propertyNames, mode); +} + +void QScriptActivationObject::putWithAttributes(JSC::ExecState *exec, const JSC::Identifier &propertyName, JSC::JSValue value, unsigned attributes) +{ + if (d_ptr()->delegate != 0) { + d_ptr()->delegate->putWithAttributes(exec, propertyName, value, attributes); + return; + } + + if (symbolTablePutWithAttributes(propertyName, value, attributes)) + return; + + JSC::PutPropertySlot slot; + JSObject::putWithAttributes(exec, propertyName, value, attributes, true, slot); +} + +void QScriptActivationObject::put(JSC::ExecState* exec, const JSC::Identifier& propertyName, JSC::JSValue value, JSC::PutPropertySlot& slot) +{ + if (d_ptr()->delegate != 0) { + d_ptr()->delegate->put(exec, propertyName, value, slot); + return; + } + JSC::JSVariableObject::put(exec, propertyName, value, slot); +} + +void QScriptActivationObject::put(JSC::ExecState* exec, unsigned propertyName, JSC::JSValue value) +{ + if (d_ptr()->delegate != 0) { + d_ptr()->delegate->put(exec, propertyName, value); + return; + } + JSC::JSVariableObject::put(exec, propertyName, value); +} + +bool QScriptActivationObject::deleteProperty(JSC::ExecState* exec, const JSC::Identifier& propertyName) +{ + if (d_ptr()->delegate != 0) + return d_ptr()->delegate->deleteProperty(exec, propertyName); + return JSC::JSVariableObject::deleteProperty(exec, propertyName); +} + +void QScriptActivationObject::defineGetter(JSC::ExecState* exec, const JSC::Identifier& propertyName, JSC::JSObject* getterFunction) +{ + if (d_ptr()->delegate != 0) + d_ptr()->delegate->defineGetter(exec, propertyName, getterFunction); + else + JSC::JSVariableObject::defineGetter(exec, propertyName, getterFunction); +} + +void QScriptActivationObject::defineSetter(JSC::ExecState* exec, const JSC::Identifier& propertyName, JSC::JSObject* setterFunction) +{ + if (d_ptr()->delegate != 0) + d_ptr()->delegate->defineSetter(exec, propertyName, setterFunction); + else + JSC::JSVariableObject::defineSetter(exec, propertyName, setterFunction); +} + +JSC::JSValue QScriptActivationObject::lookupGetter(JSC::ExecState* exec, const JSC::Identifier& propertyName) +{ + if (d_ptr()->delegate != 0) + return d_ptr()->delegate->lookupGetter(exec, propertyName); + return JSC::JSVariableObject::lookupGetter(exec, propertyName); +} + +JSC::JSValue QScriptActivationObject::lookupSetter(JSC::ExecState* exec, const JSC::Identifier& propertyName) +{ + if (d_ptr()->delegate != 0) + return d_ptr()->delegate->lookupSetter(exec, propertyName); + return JSC::JSVariableObject::lookupSetter(exec, propertyName); +} + +} // namespace QScript + +QT_END_NAMESPACE + diff --git a/src/script/bridge/qscriptactivationobject_p.h b/src/script/bridge/qscriptactivationobject_p.h new file mode 100644 index 0000000..a4c69a9 --- /dev/null +++ b/src/script/bridge/qscriptactivationobject_p.h @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtScript module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL-ONLY$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSCRIPTACTIVATIONOBJECT_P_H +#define QSCRIPTACTIVATIONOBJECT_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/qobjectdefs.h> + +#include "JSVariableObject.h" + +QT_BEGIN_NAMESPACE + +namespace QScript +{ + +class QScriptActivationObject : public JSC::JSVariableObject { +public: + QScriptActivationObject(JSC::ExecState *callFrame, JSC::JSObject *delegate = 0); + virtual ~QScriptActivationObject(); + virtual bool isDynamicScope() const { return true; } + + virtual bool getOwnPropertySlot(JSC::ExecState*, const JSC::Identifier& propertyName, JSC::PropertySlot&); + virtual bool getOwnPropertyDescriptor(JSC::ExecState*, const JSC::Identifier& propertyName, JSC::PropertyDescriptor&); + virtual void getOwnPropertyNames(JSC::ExecState*, JSC::PropertyNameArray&, JSC::EnumerationMode mode = JSC::ExcludeDontEnumProperties); + + virtual void putWithAttributes(JSC::ExecState *exec, const JSC::Identifier &propertyName, JSC::JSValue value, unsigned attributes); + virtual void put(JSC::ExecState*, const JSC::Identifier& propertyName, JSC::JSValue value, JSC::PutPropertySlot&); + virtual void put(JSC::ExecState*, unsigned propertyName, JSC::JSValue value); + + virtual bool deleteProperty(JSC::ExecState*, const JSC::Identifier& propertyName); + + virtual void defineGetter(JSC::ExecState*, const JSC::Identifier& propertyName, JSC::JSObject* getterFunction); + virtual void defineSetter(JSC::ExecState*, const JSC::Identifier& propertyName, JSC::JSObject* setterFunction); + virtual JSC::JSValue lookupGetter(JSC::ExecState*, const JSC::Identifier& propertyName); + virtual JSC::JSValue lookupSetter(JSC::ExecState*, const JSC::Identifier& propertyName); + + virtual const JSC::ClassInfo* classInfo() const { return &info; } + static const JSC::ClassInfo info; + + struct QScriptActivationObjectData : public JSVariableObjectData { + QScriptActivationObjectData(JSC::Register* registers, JSC::JSObject *dlg) + : JSVariableObjectData(&symbolTable, registers), + delegate(dlg) + { } + JSC::SymbolTable symbolTable; + JSC::JSObject *delegate; + }; + + JSC::JSObject *delegate() const + { return d_ptr()->delegate; } + void setDelegate(JSC::JSObject *delegate) + { d_ptr()->delegate = delegate; } + + QScriptActivationObjectData *d_ptr() const { return static_cast<QScriptActivationObjectData *>(d); } +}; + +} // namespace QScript + +QT_END_NAMESPACE + +#endif diff --git a/src/script/bridge/qscriptclassobject.cpp b/src/script/bridge/qscriptclassobject.cpp new file mode 100644 index 0000000..2085756 --- /dev/null +++ b/src/script/bridge/qscriptclassobject.cpp @@ -0,0 +1,280 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtScript module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL-ONLY$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "config.h" +#include "qscriptclassobject_p.h" + +#include "../api/qscriptengine.h" +#include "../api/qscriptengine_p.h" +#include "../api/qscriptcontext.h" +#include "../api/qscriptcontext_p.h" +#include "../api/qscriptclass.h" +#include "../api/qscriptclasspropertyiterator.h" + +#include "Error.h" +#include "PropertyNameArray.h" + +Q_DECLARE_METATYPE(QScriptContext*) +Q_DECLARE_METATYPE(QScriptValue) +Q_DECLARE_METATYPE(QScriptValueList) + +QT_BEGIN_NAMESPACE + +namespace QScript +{ + +ClassObjectDelegate::ClassObjectDelegate(QScriptClass *scriptClass) + : m_scriptClass(scriptClass) +{ +} + +ClassObjectDelegate::~ClassObjectDelegate() +{ +} + +QScriptObjectDelegate::Type ClassObjectDelegate::type() const +{ + return ClassObject; +} + +bool ClassObjectDelegate::getOwnPropertySlot(QScriptObject* object, + JSC::ExecState *exec, + const JSC::Identifier &propertyName, + JSC::PropertySlot &slot) +{ + QScriptEnginePrivate *engine = scriptEngineFromExec(exec); + QScript::SaveFrameHelper saveFrame(engine, exec); + // for compatibility with the old back-end, normal JS properties + // are queried first. + if (QScriptObjectDelegate::getOwnPropertySlot(object, exec, propertyName, slot)) + return true; + + QScriptValue scriptObject = engine->scriptValueFromJSCValue(object); + QScriptString scriptName; + QScriptStringPrivate scriptName_d(engine, propertyName, QScriptStringPrivate::StackAllocated); + QScriptStringPrivate::init(scriptName, &scriptName_d); + uint id = 0; + QScriptClass::QueryFlags flags = m_scriptClass->queryProperty( + scriptObject, scriptName, QScriptClass::HandlesReadAccess, &id); + if (flags & QScriptClass::HandlesReadAccess) { + QScriptValue value = m_scriptClass->property(scriptObject, scriptName, id); + if (!value.isValid()) { + // The class claims to have the property, but returned an invalid + // value. Silently convert to undefined to avoid the invalid value + // "escaping" into JS. + value = QScriptValue(QScriptValue::UndefinedValue); + } + slot.setValue(engine->scriptValueToJSCValue(value)); + return true; + } + return false; +} + +bool ClassObjectDelegate::getOwnPropertyDescriptor(QScriptObject *object, + JSC::ExecState *exec, + const JSC::Identifier &propertyName, + JSC::PropertyDescriptor &descriptor) +{ + QScriptEnginePrivate *engine = scriptEngineFromExec(exec); + QScript::SaveFrameHelper saveFrame(engine, exec); + // for compatibility with the old back-end, normal JS properties + // are queried first. + if (QScriptObjectDelegate::getOwnPropertyDescriptor(object, exec, propertyName, descriptor)) + return true; + + QScriptValue scriptObject = engine->scriptValueFromJSCValue(object); + QScriptString scriptName; + QScriptStringPrivate scriptName_d(engine, propertyName, QScriptStringPrivate::StackAllocated); + QScriptStringPrivate::init(scriptName, &scriptName_d); + uint id = 0; + QScriptClass::QueryFlags qflags = m_scriptClass->queryProperty( + scriptObject, scriptName, QScriptClass::HandlesReadAccess, &id); + if (qflags & QScriptClass::HandlesReadAccess) { + QScriptValue::PropertyFlags pflags = m_scriptClass->propertyFlags(scriptObject, scriptName, id); + unsigned attribs = 0; + if (pflags & QScriptValue::ReadOnly) + attribs |= JSC::ReadOnly; + if (pflags & QScriptValue::SkipInEnumeration) + attribs |= JSC::DontEnum; + if (pflags & QScriptValue::Undeletable) + attribs |= JSC::DontDelete; + if (pflags & QScriptValue::PropertyGetter) + attribs |= JSC::Getter; + if (pflags & QScriptValue::PropertySetter) + attribs |= JSC::Setter; + attribs |= pflags & QScriptValue::UserRange; + // Rather than calling the getter, we could return an access descriptor here. + QScriptValue value = m_scriptClass->property(scriptObject, scriptName, id); + if (!value.isValid()) { + // The class claims to have the property, but returned an invalid + // value. Silently convert to undefined to avoid the invalid value + // "escaping" into JS. + value = QScriptValue(QScriptValue::UndefinedValue); + } + descriptor.setDescriptor(engine->scriptValueToJSCValue(value), attribs); + return true; + } + return false; +} + +void ClassObjectDelegate::put(QScriptObject* object, JSC::ExecState *exec, + const JSC::Identifier &propertyName, + JSC::JSValue value, JSC::PutPropertySlot &slot) +{ + QScriptEnginePrivate *engine = scriptEngineFromExec(exec); + QScript::SaveFrameHelper saveFrame(engine, exec); + QScriptValue scriptObject = engine->scriptValueFromJSCValue(object); + QScriptString scriptName; + QScriptStringPrivate scriptName_d(engine, propertyName, QScriptStringPrivate::StackAllocated); + QScriptStringPrivate::init(scriptName, &scriptName_d); + uint id = 0; + QScriptClass::QueryFlags flags = m_scriptClass->queryProperty( + scriptObject, scriptName, QScriptClass::HandlesWriteAccess, &id); + if (flags & QScriptClass::HandlesWriteAccess) { + m_scriptClass->setProperty(scriptObject, scriptName, id, engine->scriptValueFromJSCValue(value)); + return; + } + QScriptObjectDelegate::put(object, exec, propertyName, value, slot); +} + +bool ClassObjectDelegate::deleteProperty(QScriptObject* object, JSC::ExecState *exec, + const JSC::Identifier &propertyName) +{ + // ### avoid duplication of put() + QScriptEnginePrivate *engine = scriptEngineFromExec(exec); + QScript::SaveFrameHelper saveFrame(engine, exec); + QScriptValue scriptObject = engine->scriptValueFromJSCValue(object); + QScriptString scriptName; + QScriptStringPrivate scriptName_d(engine, propertyName, QScriptStringPrivate::StackAllocated); + QScriptStringPrivate::init(scriptName, &scriptName_d); + uint id = 0; + QScriptClass::QueryFlags flags = m_scriptClass->queryProperty( + scriptObject, scriptName, QScriptClass::HandlesWriteAccess, &id); + if (flags & QScriptClass::HandlesWriteAccess) { + if (m_scriptClass->propertyFlags(scriptObject, scriptName, id) & QScriptValue::Undeletable) + return false; + m_scriptClass->setProperty(scriptObject, scriptName, id, QScriptValue()); + return true; + } + return QScriptObjectDelegate::deleteProperty(object, exec, propertyName); +} + +void ClassObjectDelegate::getOwnPropertyNames(QScriptObject* object, JSC::ExecState *exec, + JSC::PropertyNameArray &propertyNames, + JSC::EnumerationMode mode) +{ + // For compatibility with the old back-end, normal JS properties + // are added first. + QScriptObjectDelegate::getOwnPropertyNames(object, exec, propertyNames, mode); + + QScriptEnginePrivate *engine = scriptEngineFromExec(exec); + QScript::SaveFrameHelper saveFrame(engine, exec); + QScriptValue scriptObject = engine->scriptValueFromJSCValue(object); + QScriptClassPropertyIterator *it = m_scriptClass->newIterator(scriptObject); + if (it != 0) { + while (it->hasNext()) { + it->next(); + QString name = it->name().toString(); + propertyNames.add(JSC::Identifier(exec, name)); + } + delete it; + } +} + +JSC::CallType ClassObjectDelegate::getCallData(QScriptObject*, JSC::CallData &callData) +{ + if (!m_scriptClass->supportsExtension(QScriptClass::Callable)) + return JSC::CallTypeNone; + callData.native.function = call; + return JSC::CallTypeHost; +} + +JSC::JSValue JSC_HOST_CALL ClassObjectDelegate::call(JSC::ExecState *exec, JSC::JSObject *callee, + JSC::JSValue thisValue, const JSC::ArgList &args) +{ + if (!callee->inherits(&QScriptObject::info)) + return JSC::throwError(exec, JSC::TypeError, "callee is not a ClassObject object"); + QScriptObject *obj = static_cast<QScriptObject*>(callee); + QScriptObjectDelegate *delegate = obj->delegate(); + if (!delegate || (delegate->type() != QScriptObjectDelegate::ClassObject)) + return JSC::throwError(exec, JSC::TypeError, "callee is not a ClassObject object"); + + QScriptClass *scriptClass = static_cast<ClassObjectDelegate*>(delegate)->scriptClass(); + QScriptEnginePrivate *eng_p = scriptEngineFromExec(exec); + + JSC::ExecState *oldFrame = eng_p->currentFrame; + eng_p->pushContext(exec, thisValue, args, callee); + QScriptContext *ctx = eng_p->contextForFrame(eng_p->currentFrame); + QScriptValue scriptObject = eng_p->scriptValueFromJSCValue(obj); + QVariant result = scriptClass->extension(QScriptClass::Callable, QVariant::fromValue(ctx)); + eng_p->popContext(); + eng_p->currentFrame = oldFrame; + return QScriptEnginePrivate::jscValueFromVariant(exec, result); +} + +JSC::ConstructType ClassObjectDelegate::getConstructData(QScriptObject*, JSC::ConstructData &constructData) +{ + if (!m_scriptClass->supportsExtension(QScriptClass::Callable)) + return JSC::ConstructTypeNone; + constructData.native.function = construct; + return JSC::ConstructTypeHost; +} + +JSC::JSObject* ClassObjectDelegate::construct(JSC::ExecState *exec, JSC::JSObject *callee, + const JSC::ArgList &args) +{ + Q_ASSERT(callee->inherits(&QScriptObject::info)); + QScriptObject *obj = static_cast<QScriptObject*>(callee); + QScriptObjectDelegate *delegate = obj->delegate(); + QScriptClass *scriptClass = static_cast<ClassObjectDelegate*>(delegate)->scriptClass(); + + QScriptEnginePrivate *eng_p = scriptEngineFromExec(exec); + JSC::ExecState *oldFrame = eng_p->currentFrame; + eng_p->pushContext(exec, JSC::JSValue(), args, callee, true); + QScriptContext *ctx = eng_p->contextForFrame(eng_p->currentFrame); + + QScriptValue defaultObject = ctx->thisObject(); + QScriptValue result = qvariant_cast<QScriptValue>(scriptClass->extension(QScriptClass::Callable, QVariant::fromValue(ctx))); + if (!result.isObject()) + result = defaultObject; + eng_p->popContext(); + eng_p->currentFrame = oldFrame; + return JSC::asObject(eng_p->scriptValueToJSCValue(result)); +} + +bool ClassObjectDelegate::hasInstance(QScriptObject* object, JSC::ExecState *exec, + JSC::JSValue value, JSC::JSValue proto) +{ + if (!scriptClass()->supportsExtension(QScriptClass::HasInstance)) + return QScriptObjectDelegate::hasInstance(object, exec, value, proto); + QScriptValueList args; + QScriptEnginePrivate *eng_p = scriptEngineFromExec(exec); + QScript::SaveFrameHelper saveFrame(eng_p, exec); + args << eng_p->scriptValueFromJSCValue(object) << eng_p->scriptValueFromJSCValue(value); + QVariant result = scriptClass()->extension(QScriptClass::HasInstance, QVariant::fromValue(args)); + return result.toBool(); +} + +} // namespace QScript + +QT_END_NAMESPACE diff --git a/src/script/bridge/qscriptclassobject_p.h b/src/script/bridge/qscriptclassobject_p.h new file mode 100644 index 0000000..10b727a --- /dev/null +++ b/src/script/bridge/qscriptclassobject_p.h @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtScript module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL-ONLY$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSCRIPTCLASSOBJECT_P_H +#define QSCRIPTCLASSOBJECT_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/qobjectdefs.h> + +#include "qscriptobject_p.h" + +QT_BEGIN_NAMESPACE + +class QScriptClass; + +namespace QScript +{ + +class ClassObjectDelegate : public QScriptObjectDelegate +{ +public: + ClassObjectDelegate(QScriptClass *scriptClass); + ~ClassObjectDelegate(); + + inline QScriptClass *scriptClass() const; + inline void setScriptClass(QScriptClass *scriptClass); + + virtual Type type() const; + + virtual bool getOwnPropertySlot(QScriptObject*, JSC::ExecState*, + const JSC::Identifier& propertyName, + JSC::PropertySlot&); + virtual bool getOwnPropertyDescriptor(QScriptObject*, JSC::ExecState*, + const JSC::Identifier& propertyName, + JSC::PropertyDescriptor&); + virtual void put(QScriptObject*, JSC::ExecState* exec, + const JSC::Identifier& propertyName, + JSC::JSValue, JSC::PutPropertySlot&); + virtual bool deleteProperty(QScriptObject*, JSC::ExecState*, + const JSC::Identifier& propertyName); + virtual void getOwnPropertyNames(QScriptObject*, JSC::ExecState*, + JSC::PropertyNameArray&, + JSC::EnumerationMode mode = JSC::ExcludeDontEnumProperties); + + virtual JSC::CallType getCallData(QScriptObject*, JSC::CallData&); + static JSC::JSValue JSC_HOST_CALL call(JSC::ExecState*, JSC::JSObject*, + JSC::JSValue, const JSC::ArgList&); + virtual JSC::ConstructType getConstructData(QScriptObject*, JSC::ConstructData&); + static JSC::JSObject* construct(JSC::ExecState*, JSC::JSObject*, + const JSC::ArgList&); + + virtual bool hasInstance(QScriptObject*, JSC::ExecState*, + JSC::JSValue value, JSC::JSValue proto); + +private: + QScriptClass *m_scriptClass; +}; + +inline QScriptClass *ClassObjectDelegate::scriptClass() const +{ + return m_scriptClass; +} + +inline void ClassObjectDelegate::setScriptClass(QScriptClass *scriptClass) +{ + Q_ASSERT(scriptClass != 0); + m_scriptClass = scriptClass; +} + +} // namespace QScript + +QT_END_NAMESPACE + +#endif diff --git a/src/script/bridge/qscriptdeclarativeclass.cpp b/src/script/bridge/qscriptdeclarativeclass.cpp new file mode 100644 index 0000000..6126b32 --- /dev/null +++ b/src/script/bridge/qscriptdeclarativeclass.cpp @@ -0,0 +1,601 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL-ONLY$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qscriptdeclarativeclass_p.h" +#include "qscriptdeclarativeobject_p.h" +#include "qscriptobject_p.h" +#include "qscriptstaticscopeobject_p.h" +#include <QtScript/qscriptstring.h> +#include <QtScript/qscriptengine.h> +#include <QtScript/qscriptengineagent.h> +#include <private/qscriptengine_p.h> +#include <private/qscriptvalue_p.h> +#include <private/qscriptqobject_p.h> +#include <private/qscriptactivationobject_p.h> +#include <QtCore/qstringlist.h> + +QT_BEGIN_NAMESPACE + +/*! +\class QScriptDeclarativeClass::Value +\internal +\brief The QScriptDeclarativeClass::Value class acts as a container for JavaScript data types. + +QScriptDeclarativeClass::Value class is similar to QScriptValue, but it is slightly faster. +Unlike QScriptValue, however, Value instances cannot be stored as they may not survive garbage +collection. If you need to store a Value, convert it to a QScriptValue and store that. +*/ + +QScriptDeclarativeClass::Value::Value() +{ + new (this) JSC::JSValue(JSC::jsUndefined()); +} + +QScriptDeclarativeClass::Value::Value(const Value &other) +{ + new (this) JSC::JSValue((JSC::JSValue &)other); +} + +static QScriptDeclarativeClass::Value jscToValue(const JSC::JSValue &val) +{ + return QScriptDeclarativeClass::Value((QScriptDeclarativeClass::Value &)val); +} + +QScriptDeclarativeClass::Value::Value(QScriptContext *ctxt, int value) +{ + new (this) JSC::JSValue(QScriptEnginePrivate::frameForContext(ctxt), value); +} + +QScriptDeclarativeClass::Value::Value(QScriptContext *ctxt, uint value) +{ + new (this) JSC::JSValue(QScriptEnginePrivate::frameForContext(ctxt), value); +} + +QScriptDeclarativeClass::Value::Value(QScriptContext *, bool value) +{ + if (value) + new (this) JSC::JSValue(JSC::JSValue::JSTrue); + else + new (this) JSC::JSValue(JSC::JSValue::JSFalse); +} + +QScriptDeclarativeClass::Value::Value(QScriptContext *ctxt, double value) +{ + new (this) JSC::JSValue(QScriptEnginePrivate::frameForContext(ctxt), value); +} + +QScriptDeclarativeClass::Value::Value(QScriptContext *ctxt, float value) +{ + new (this) JSC::JSValue(QScriptEnginePrivate::frameForContext(ctxt), value); +} + +QScriptDeclarativeClass::Value::Value(QScriptContext *ctxt, const QString &value) +{ + new (this) JSC::JSValue(JSC::jsString(QScriptEnginePrivate::frameForContext(ctxt), value)); +} + +QScriptDeclarativeClass::Value::Value(QScriptContext *ctxt, const QScriptValue &value) +{ + new (this) JSC::JSValue(QScriptEnginePrivate::get(ctxt->engine())->scriptValueToJSCValue(value)); +} + +QScriptDeclarativeClass::Value::Value(QScriptEngine *eng, int value) +{ + new (this) JSC::JSValue(QScriptEnginePrivate::get(eng)->currentFrame, value); +} + +QScriptDeclarativeClass::Value::Value(QScriptEngine *eng, uint value) +{ + new (this) JSC::JSValue(QScriptEnginePrivate::get(eng)->currentFrame, value); +} + +QScriptDeclarativeClass::Value::Value(QScriptEngine *eng, bool value) +{ + if (value) + new (this) JSC::JSValue(JSC::JSValue::JSTrue); + else + new (this) JSC::JSValue(JSC::JSValue::JSFalse); +} + +QScriptDeclarativeClass::Value::Value(QScriptEngine *eng, double value) +{ + new (this) JSC::JSValue(QScriptEnginePrivate::get(eng)->currentFrame, value); +} + +QScriptDeclarativeClass::Value::Value(QScriptEngine *eng, float value) +{ + new (this) JSC::JSValue(QScriptEnginePrivate::get(eng)->currentFrame, value); +} + +QScriptDeclarativeClass::Value::Value(QScriptEngine *eng, const QString &value) +{ + new (this) JSC::JSValue(JSC::jsString(QScriptEnginePrivate::get(eng)->currentFrame, value)); +} + +QScriptDeclarativeClass::Value::Value(QScriptEngine *eng, const QScriptValue &value) +{ + new (this) JSC::JSValue(QScriptEnginePrivate::get(eng)->scriptValueToJSCValue(value)); +} + +QScriptDeclarativeClass::Value::~Value() +{ + ((JSC::JSValue *)(this))->~JSValue(); +} + +QScriptValue QScriptDeclarativeClass::Value::toScriptValue(QScriptEngine *engine) const +{ + return QScriptEnginePrivate::get(engine)->scriptValueFromJSCValue((JSC::JSValue &)*this); +} + +QScriptDeclarativeClass::PersistentIdentifier::PersistentIdentifier() + : identifier(0), engine(0) +{ + new (&d) JSC::Identifier(); +} + +QScriptDeclarativeClass::PersistentIdentifier::~PersistentIdentifier() +{ + if (engine) { + QScript::APIShim shim(engine); + ((JSC::Identifier &)d).JSC::Identifier::~Identifier(); + } else { + ((JSC::Identifier &)d).JSC::Identifier::~Identifier(); + } +} + +QScriptDeclarativeClass::PersistentIdentifier::PersistentIdentifier(const PersistentIdentifier &other) +{ + identifier = other.identifier; + engine = other.engine; + new (&d) JSC::Identifier((JSC::Identifier &)(other.d)); +} + +QScriptDeclarativeClass::PersistentIdentifier & +QScriptDeclarativeClass::PersistentIdentifier::operator=(const PersistentIdentifier &other) +{ + identifier = other.identifier; + engine = other.engine; + ((JSC::Identifier &)d) = (JSC::Identifier &)(other.d); + return *this; +} + +QString QScriptDeclarativeClass::PersistentIdentifier::toString() const +{ + return ((JSC::Identifier &)d).ustring(); +} + +QScriptDeclarativeClass::QScriptDeclarativeClass(QScriptEngine *engine) +: d_ptr(new QScriptDeclarativeClassPrivate) +{ + Q_ASSERT(sizeof(void*) == sizeof(JSC::Identifier)); + d_ptr->q_ptr = this; + d_ptr->engine = engine; +} + +QScriptValue QScriptDeclarativeClass::newObject(QScriptEngine *engine, + QScriptDeclarativeClass *scriptClass, + Object *object) +{ + Q_ASSERT(engine); + Q_ASSERT(scriptClass); + + QScriptEnginePrivate *p = static_cast<QScriptEnginePrivate *>(QObjectPrivate::get(engine)); + QScript::APIShim shim(p); + + JSC::ExecState* exec = p->currentFrame; + QScriptObject *result = new (exec) QScriptObject(p->scriptObjectStructure); + result->setDelegate(new QScript::DeclarativeObjectDelegate(scriptClass, object)); + return p->scriptValueFromJSCValue(result); +} + +QScriptDeclarativeClass::Value +QScriptDeclarativeClass::newObjectValue(QScriptEngine *engine, + QScriptDeclarativeClass *scriptClass, + Object *object) +{ + Q_ASSERT(engine); + Q_ASSERT(scriptClass); + + QScriptEnginePrivate *p = static_cast<QScriptEnginePrivate *>(QObjectPrivate::get(engine)); + QScript::APIShim shim(p); + + JSC::ExecState* exec = p->currentFrame; + QScriptObject *result = new (exec) QScriptObject(p->scriptObjectStructure); + result->setDelegate(new QScript::DeclarativeObjectDelegate(scriptClass, object)); + return jscToValue(JSC::JSValue(result)); +} + +QScriptDeclarativeClass *QScriptDeclarativeClass::scriptClass(const QScriptValue &v) +{ + QScriptValuePrivate *d = QScriptValuePrivate::get(v); + if (!d || !d->isJSC()) + return 0; + return QScriptEnginePrivate::declarativeClass(d->jscValue); +} + +QScriptDeclarativeClass::Object *QScriptDeclarativeClass::object(const QScriptValue &v) +{ + QScriptValuePrivate *d = QScriptValuePrivate::get(v); + if (!d || !d->isJSC()) + return 0; + return QScriptEnginePrivate::declarativeObject(d->jscValue); +} + +QScriptValue QScriptDeclarativeClass::function(const QScriptValue &v, const Identifier &name) +{ + QScriptValuePrivate *d = QScriptValuePrivate::get(v); + + if (!d->isObject()) + return QScriptValue(); + + QScript::APIShim shim(d->engine); + JSC::ExecState *exec = d->engine->currentFrame; + JSC::JSObject *object = d->jscValue.getObject(); + JSC::PropertySlot slot(const_cast<JSC::JSObject*>(object)); + JSC::JSValue result; + + JSC::Identifier id(exec, (JSC::UString::Rep *)name); + + if (const_cast<JSC::JSObject*>(object)->getOwnPropertySlot(exec, id, slot)) { + result = slot.getValue(exec, id); + if (QScript::isFunction(result)) + return d->engine->scriptValueFromJSCValue(result); + } + + return QScriptValue(); +} + +QScriptValue QScriptDeclarativeClass::property(const QScriptValue &v, const Identifier &name) +{ + QScriptValuePrivate *d = QScriptValuePrivate::get(v); + + if (!d->isObject()) + return QScriptValue(); + + QScript::APIShim shim(d->engine); + JSC::ExecState *exec = d->engine->currentFrame; + JSC::JSObject *object = d->jscValue.getObject(); + JSC::PropertySlot slot(const_cast<JSC::JSObject*>(object)); + JSC::JSValue result; + + JSC::Identifier id(exec, (JSC::UString::Rep *)name); + + if (const_cast<JSC::JSObject*>(object)->getOwnPropertySlot(exec, id, slot)) { + result = slot.getValue(exec, id); + return d->engine->scriptValueFromJSCValue(result); + } + + return QScriptValue(); +} + +QScriptDeclarativeClass::Value +QScriptDeclarativeClass::functionValue(const QScriptValue &v, const Identifier &name) +{ + QScriptValuePrivate *d = QScriptValuePrivate::get(v); + + if (!d->isObject()) + return Value(); + + QScript::APIShim shim(d->engine); + JSC::ExecState *exec = d->engine->currentFrame; + JSC::JSObject *object = d->jscValue.getObject(); + JSC::PropertySlot slot(const_cast<JSC::JSObject*>(object)); + JSC::JSValue result; + + JSC::Identifier id(exec, (JSC::UString::Rep *)name); + + if (const_cast<JSC::JSObject*>(object)->getOwnPropertySlot(exec, id, slot)) { + result = slot.getValue(exec, id); + if (QScript::isFunction(result)) + return jscToValue(result); + } + + return Value(); +} + +QScriptDeclarativeClass::Value +QScriptDeclarativeClass::propertyValue(const QScriptValue &v, const Identifier &name) +{ + QScriptValuePrivate *d = QScriptValuePrivate::get(v); + + if (!d->isObject()) + return Value(); + + QScript::APIShim shim(d->engine); + JSC::ExecState *exec = d->engine->currentFrame; + JSC::JSObject *object = d->jscValue.getObject(); + JSC::PropertySlot slot(const_cast<JSC::JSObject*>(object)); + JSC::JSValue result; + + JSC::Identifier id(exec, (JSC::UString::Rep *)name); + + if (const_cast<JSC::JSObject*>(object)->getOwnPropertySlot(exec, id, slot)) { + result = slot.getValue(exec, id); + return jscToValue(result); + } + + return Value(); +} + +/* +Returns the scope chain entry at \a index. If index is less than 0, returns +entries starting at the end. For example, scopeChainValue(context, -1) will return +the value last in the scope chain. +*/ +QScriptValue QScriptDeclarativeClass::scopeChainValue(QScriptContext *context, int index) +{ + context->activationObject(); //ensure the creation of the normal scope for native context + const JSC::CallFrame *frame = QScriptEnginePrivate::frameForContext(context); + QScriptEnginePrivate *engine = QScript::scriptEngineFromExec(frame); + QScript::APIShim shim(engine); + + JSC::ScopeChainNode *node = frame->scopeChain(); + JSC::ScopeChainIterator it(node); + + if (index < 0) { + int count = 0; + for (it = node->begin(); it != node->end(); ++it) + ++count; + + index = qAbs(index); + if (index > count) + return QScriptValue(); + else + index = count - index; + } + + for (it = node->begin(); it != node->end(); ++it) { + + if (index == 0) { + + JSC::JSObject *object = *it; + if (!object) return QScriptValue(); + + if (object->inherits(&QScript::QScriptActivationObject::info) + && (static_cast<QScript::QScriptActivationObject*>(object)->delegate() != 0)) { + // Return the object that property access is being delegated to + object = static_cast<QScript::QScriptActivationObject*>(object)->delegate(); + } + return engine->scriptValueFromJSCValue(object); + + } else { + --index; + } + + } + + return QScriptValue(); +} + +/*! + Enters a new execution context and returns the associated + QScriptContext object. + + Once you are done with the context, you should call popContext() to + restore the old context. + + By default, the `this' object of the new context is the Global Object. + The context's \l{QScriptContext::callee()}{callee}() will be invalid. + + Unlike pushContext(), the default scope chain is reset to include + only the global object and the QScriptContext's activation object. + + \sa QScriptEngine::popContext() +*/ +QScriptContext * QScriptDeclarativeClass::pushCleanContext(QScriptEngine *engine) +{ + if (!engine) + return 0; + + QScriptEnginePrivate *d = QScriptEnginePrivate::get(engine); + QScript::APIShim shim(d); + + JSC::CallFrame* newFrame = d->pushContext(d->currentFrame, + d->currentFrame->globalData().dynamicGlobalObject, + JSC::ArgList(), /*callee = */0, false, true); + + if (engine->agent()) + engine->agent()->contextPush(); + + return d->contextForFrame(newFrame); +} + +QScriptDeclarativeClass::~QScriptDeclarativeClass() +{ +} + +QScriptEngine *QScriptDeclarativeClass::engine() const +{ + return d_ptr->engine; +} + +bool QScriptDeclarativeClass::supportsCall() const +{ + return d_ptr->supportsCall; +} + +void QScriptDeclarativeClass::setSupportsCall(bool c) +{ + d_ptr->supportsCall = c; +} + +QScriptDeclarativeClass::PersistentIdentifier +QScriptDeclarativeClass::createPersistentIdentifier(const QString &str) +{ + QScriptEnginePrivate *p = + static_cast<QScriptEnginePrivate *>(QObjectPrivate::get(d_ptr->engine)); + QScript::APIShim shim(p); + JSC::ExecState* exec = p->currentFrame; + + PersistentIdentifier rv(p); + new (&rv.d) JSC::Identifier(exec, (UChar *)str.constData(), str.size()); + rv.identifier = (void *)((JSC::Identifier &)rv.d).ustring().rep(); + return rv; +} + +QScriptDeclarativeClass::PersistentIdentifier +QScriptDeclarativeClass::createPersistentIdentifier(const Identifier &id) +{ + QScriptEnginePrivate *p = + static_cast<QScriptEnginePrivate *>(QObjectPrivate::get(d_ptr->engine)); + QScript::APIShim shim(p); + JSC::ExecState* exec = p->currentFrame; + + PersistentIdentifier rv(p); + new (&rv.d) JSC::Identifier(exec, (JSC::UString::Rep *)id); + rv.identifier = (void *)((JSC::Identifier &)rv.d).ustring().rep(); + return rv; +} + +QString QScriptDeclarativeClass::toString(const Identifier &identifier) +{ + JSC::UString::Rep *r = (JSC::UString::Rep *)identifier; + return QString((QChar *)r->data(), r->size()); +} + +bool QScriptDeclarativeClass::startsWithUpper(const Identifier &identifier) +{ + JSC::UString::Rep *r = (JSC::UString::Rep *)identifier; + if (r->size() < 1) + return false; + return QChar::category((ushort)(r->data()[0])) == QChar::Letter_Uppercase; +} + +quint32 QScriptDeclarativeClass::toArrayIndex(const Identifier &identifier, bool *ok) +{ + JSC::UString::Rep *r = (JSC::UString::Rep *)identifier; + JSC::UString s(r); + return s.toArrayIndex(ok); +} + +QScriptClass::QueryFlags +QScriptDeclarativeClass::queryProperty(Object *object, const Identifier &name, + QScriptClass::QueryFlags flags) +{ + Q_UNUSED(object); + Q_UNUSED(name); + Q_UNUSED(flags); + return 0; +} + +QScriptDeclarativeClass::Value +QScriptDeclarativeClass::property(Object *object, const Identifier &name) +{ + Q_UNUSED(object); + Q_UNUSED(name); + return Value(); +} + +void QScriptDeclarativeClass::setProperty(Object *object, const Identifier &name, + const QScriptValue &value) +{ + Q_UNUSED(object); + Q_UNUSED(name); + Q_UNUSED(value); +} + +QScriptValue::PropertyFlags +QScriptDeclarativeClass::propertyFlags(Object *object, const Identifier &name) +{ + Q_UNUSED(object); + Q_UNUSED(name); + return 0; +} + +QScriptDeclarativeClass::Value QScriptDeclarativeClass::call(Object *object, + QScriptContext *ctxt) +{ + Q_UNUSED(object); + Q_UNUSED(ctxt); + return Value(); +} + +bool QScriptDeclarativeClass::compare(Object *o, Object *o2) +{ + return o == o2; +} + +QStringList QScriptDeclarativeClass::propertyNames(Object *object) +{ + Q_UNUSED(object); + return QStringList(); +} + +bool QScriptDeclarativeClass::isQObject() const +{ + return false; +} + +QObject *QScriptDeclarativeClass::toQObject(Object *, bool *ok) +{ + if (ok) *ok = false; + return 0; +} + +QVariant QScriptDeclarativeClass::toVariant(Object *, bool *ok) +{ + if (ok) *ok = false; + return QVariant(); +} + +QScriptContext *QScriptDeclarativeClass::context() const +{ + return d_ptr->context; +} + +/*! + Creates a scope object with a fixed set of undeletable properties. +*/ +QScriptValue QScriptDeclarativeClass::newStaticScopeObject( + QScriptEngine *engine, int propertyCount, const QString *names, + const QScriptValue *values, const QScriptValue::PropertyFlags *flags) +{ + QScriptEnginePrivate *eng_p = QScriptEnginePrivate::get(engine); + QScript::APIShim shim(eng_p); + JSC::ExecState *exec = eng_p->currentFrame; + QScriptStaticScopeObject::PropertyInfo *props = new QScriptStaticScopeObject::PropertyInfo[propertyCount]; + for (int i = 0; i < propertyCount; ++i) { + unsigned attribs = QScriptEnginePrivate::propertyFlagsToJSCAttributes(flags[i]); + Q_ASSERT_X(attribs & JSC::DontDelete, Q_FUNC_INFO, "All properties must be undeletable"); + JSC::Identifier id = JSC::Identifier(exec, names[i]); + JSC::JSValue jsval = eng_p->scriptValueToJSCValue(values[i]); + props[i] = QScriptStaticScopeObject::PropertyInfo(id, jsval, attribs); + } + QScriptValue result = eng_p->scriptValueFromJSCValue(new (exec)QScriptStaticScopeObject(eng_p->staticScopeObjectStructure, + propertyCount, props)); + delete[] props; + return result; +} + +/*! + Creates a static scope object that's initially empty, but to which new + properties can be added. +*/ +QScriptValue QScriptDeclarativeClass::newStaticScopeObject(QScriptEngine *engine) +{ + QScriptEnginePrivate *eng_p = QScriptEnginePrivate::get(engine); + QScript::APIShim shim(eng_p); + return eng_p->scriptValueFromJSCValue(new (eng_p->currentFrame)QScriptStaticScopeObject(eng_p->staticScopeObjectStructure)); +} + +QT_END_NAMESPACE diff --git a/src/script/bridge/qscriptdeclarativeclass_p.h b/src/script/bridge/qscriptdeclarativeclass_p.h new file mode 100644 index 0000000..e4c18f5 --- /dev/null +++ b/src/script/bridge/qscriptdeclarativeclass_p.h @@ -0,0 +1,156 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL-ONLY$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSCRIPTDECLARATIVECLASS_P_H +#define QSCRIPTDECLARATIVECLASS_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/qobjectdefs.h> +#include <QtScript/qscriptvalue.h> +#include <QtScript/qscriptclass.h> + +QT_BEGIN_NAMESPACE + +class QScriptDeclarativeClassPrivate; +class PersistentIdentifierPrivate; +class QScriptContext; +class Q_SCRIPT_EXPORT QScriptDeclarativeClass +{ +public: +#define QT_HAVE_QSCRIPTDECLARATIVECLASS_VALUE + class Q_SCRIPT_EXPORT Value + { + public: + Value(); + Value(const Value &); + + Value(QScriptContext *, int); + Value(QScriptContext *, uint); + Value(QScriptContext *, bool); + Value(QScriptContext *, double); + Value(QScriptContext *, float); + Value(QScriptContext *, const QString &); + Value(QScriptContext *, const QScriptValue &); + Value(QScriptEngine *, int); + Value(QScriptEngine *, uint); + Value(QScriptEngine *, bool); + Value(QScriptEngine *, double); + Value(QScriptEngine *, float); + Value(QScriptEngine *, const QString &); + Value(QScriptEngine *, const QScriptValue &); + ~Value(); + + QScriptValue toScriptValue(QScriptEngine *) const; + private: + char dummy[8]; + }; + + typedef void* Identifier; + + struct Object { virtual ~Object() {} }; + + static QScriptValue newObject(QScriptEngine *, QScriptDeclarativeClass *, Object *); + static Value newObjectValue(QScriptEngine *, QScriptDeclarativeClass *, Object *); + static QScriptDeclarativeClass *scriptClass(const QScriptValue &); + static Object *object(const QScriptValue &); + + static QScriptValue function(const QScriptValue &, const Identifier &); + static QScriptValue property(const QScriptValue &, const Identifier &); + static Value functionValue(const QScriptValue &, const Identifier &); + static Value propertyValue(const QScriptValue &, const Identifier &); + + static QScriptValue scopeChainValue(QScriptContext *, int index); + static QScriptContext *pushCleanContext(QScriptEngine *); + + static QScriptValue newStaticScopeObject( + QScriptEngine *, int propertyCount, const QString *names, + const QScriptValue *values, const QScriptValue::PropertyFlags *flags); + static QScriptValue newStaticScopeObject(QScriptEngine *); + + class Q_SCRIPT_EXPORT PersistentIdentifier + { + public: + Identifier identifier; + + PersistentIdentifier(); + ~PersistentIdentifier(); + PersistentIdentifier(const PersistentIdentifier &other); + PersistentIdentifier &operator=(const PersistentIdentifier &other); + + QString toString() const; + private: + friend class QScriptDeclarativeClass; + PersistentIdentifier(QScriptEnginePrivate *e) : identifier(0), engine(e), d(0) {} + QScriptEnginePrivate *engine; + void *d; + }; + + QScriptDeclarativeClass(QScriptEngine *engine); + virtual ~QScriptDeclarativeClass(); + + QScriptEngine *engine() const; + + bool supportsCall() const; + void setSupportsCall(bool); + + PersistentIdentifier createPersistentIdentifier(const QString &); + PersistentIdentifier createPersistentIdentifier(const Identifier &); + + QString toString(const Identifier &); + bool startsWithUpper(const Identifier &); + quint32 toArrayIndex(const Identifier &, bool *ok); + + virtual QScriptClass::QueryFlags queryProperty(Object *, const Identifier &, + QScriptClass::QueryFlags flags); + + virtual Value property(Object *, const Identifier &); + virtual void setProperty(Object *, const Identifier &name, const QScriptValue &); + virtual QScriptValue::PropertyFlags propertyFlags(Object *, const Identifier &); + virtual Value call(Object *, QScriptContext *); + virtual bool compare(Object *, Object *); + + virtual QStringList propertyNames(Object *); + + virtual bool isQObject() const; + virtual QObject *toQObject(Object *, bool *ok = 0); + virtual QVariant toVariant(Object *, bool *ok = 0); + + QScriptContext *context() const; +protected: + friend class QScriptDeclarativeClassPrivate; + QScopedPointer<QScriptDeclarativeClassPrivate> d_ptr; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/script/bridge/qscriptdeclarativeobject.cpp b/src/script/bridge/qscriptdeclarativeobject.cpp new file mode 100644 index 0000000..201f2c0 --- /dev/null +++ b/src/script/bridge/qscriptdeclarativeobject.cpp @@ -0,0 +1,190 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL-ONLY$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "config.h" +#include "qscriptdeclarativeobject_p.h" + +#include "../api/qscriptengine.h" +#include "../api/qscriptengine_p.h" +#include "../api/qscriptcontext.h" +#include "../api/qscriptcontext_p.h" +#include "../api/qscriptclass.h" +#include "../api/qscriptclasspropertyiterator.h" + +#include "Error.h" +#include "PropertyNameArray.h" + +#include <QtCore/qstringlist.h> + +Q_DECLARE_METATYPE(QScriptContext*) +Q_DECLARE_METATYPE(QScriptValue) +Q_DECLARE_METATYPE(QScriptValueList) + +QT_BEGIN_NAMESPACE + +namespace QScript +{ + +DeclarativeObjectDelegate::DeclarativeObjectDelegate(QScriptDeclarativeClass *c, + QScriptDeclarativeClass::Object *o) +: m_class(c), m_object(o) +{ +} + +DeclarativeObjectDelegate::~DeclarativeObjectDelegate() +{ + delete m_object; +} + +QScriptObjectDelegate::Type DeclarativeObjectDelegate::type() const +{ + return DeclarativeClassObject; +} + +bool DeclarativeObjectDelegate::getOwnPropertySlot(QScriptObject* object, + JSC::ExecState *exec, + const JSC::Identifier &propertyName, + JSC::PropertySlot &slot) +{ + QScriptDeclarativeClass::Identifier identifier = (void *)propertyName.ustring().rep(); + + QScriptDeclarativeClassPrivate *p = QScriptDeclarativeClassPrivate::get(m_class); + p->context = reinterpret_cast<QScriptContext *>(exec); + QScriptClass::QueryFlags flags = + m_class->queryProperty(m_object, identifier, QScriptClass::HandlesReadAccess); + if (flags & QScriptClass::HandlesReadAccess) { + QScriptDeclarativeClass::Value val = m_class->property(m_object, identifier); + p->context = 0; + slot.setValue((const JSC::JSValue &)val); + return true; + } + p->context = 0; + + return QScriptObjectDelegate::getOwnPropertySlot(object, exec, propertyName, slot); +} + +void DeclarativeObjectDelegate::put(QScriptObject* object, JSC::ExecState *exec, + const JSC::Identifier &propertyName, + JSC::JSValue value, JSC::PutPropertySlot &slot) +{ + QScriptEnginePrivate *engine = scriptEngineFromExec(exec); + QScript::SaveFrameHelper saveFrame(engine, exec); + QScriptDeclarativeClass::Identifier identifier = (void *)propertyName.ustring().rep(); + + QScriptDeclarativeClassPrivate *p = QScriptDeclarativeClassPrivate::get(m_class); + p->context = reinterpret_cast<QScriptContext *>(exec); + QScriptClass::QueryFlags flags = + m_class->queryProperty(m_object, identifier, QScriptClass::HandlesWriteAccess); + if (flags & QScriptClass::HandlesWriteAccess) { + m_class->setProperty(m_object, identifier, engine->scriptValueFromJSCValue(value)); + p->context = 0; + return; + } + p->context = 0; + + QScriptObjectDelegate::put(object, exec, propertyName, value, slot); +} + +bool DeclarativeObjectDelegate::deleteProperty(QScriptObject* object, JSC::ExecState *exec, + const JSC::Identifier &propertyName) +{ + return QScriptObjectDelegate::deleteProperty(object, exec, propertyName); +} + +void DeclarativeObjectDelegate::getOwnPropertyNames(QScriptObject* object, JSC::ExecState *exec, + JSC::PropertyNameArray &propertyNames, + JSC::EnumerationMode mode) +{ + QStringList properties = m_class->propertyNames(m_object); + for (int ii = 0; ii < properties.count(); ++ii) { + const QString &name = properties.at(ii); + propertyNames.add(JSC::Identifier(exec, name)); + } + + QScriptObjectDelegate::getOwnPropertyNames(object, exec, propertyNames, mode); +} + +JSC::CallType DeclarativeObjectDelegate::getCallData(QScriptObject *object, JSC::CallData &callData) +{ + if (!QScriptDeclarativeClassPrivate::get(m_class)->supportsCall) + return JSC::CallTypeNone; + callData.native.function = call; + return JSC::CallTypeHost; +} + +JSC::JSValue DeclarativeObjectDelegate::call(JSC::ExecState *exec, JSC::JSObject *callee, + JSC::JSValue thisValue, const JSC::ArgList &args) +{ + if (!callee->inherits(&QScriptObject::info)) + return JSC::throwError(exec, JSC::TypeError, "callee is not a DeclarativeObject object"); + QScriptObject *obj = static_cast<QScriptObject*>(callee); + QScriptObjectDelegate *delegate = obj->delegate(); + if (!delegate || (delegate->type() != QScriptObjectDelegate::DeclarativeClassObject)) + return JSC::throwError(exec, JSC::TypeError, "callee is not a DeclarativeObject object"); + + QScriptDeclarativeClass *scriptClass = static_cast<DeclarativeObjectDelegate*>(delegate)->m_class; + QScriptEnginePrivate *eng_p = scriptEngineFromExec(exec); + + QScript::SaveFrameHelper saveFrame(eng_p, exec); + eng_p->pushContext(exec, thisValue, args, callee); + QScriptContext *ctxt = eng_p->contextForFrame(eng_p->currentFrame); + + QScriptValue scriptObject = eng_p->scriptValueFromJSCValue(obj); + QScriptDeclarativeClass::Value result = + scriptClass->call(static_cast<DeclarativeObjectDelegate*>(delegate)->m_object, ctxt); + + eng_p->popContext(); + return (JSC::JSValue &)(result); +} + +JSC::ConstructType DeclarativeObjectDelegate::getConstructData(QScriptObject* object, JSC::ConstructData &constructData) +{ + return QScriptObjectDelegate::getConstructData(object, constructData); +} + +bool DeclarativeObjectDelegate::hasInstance(QScriptObject* object, JSC::ExecState *exec, + JSC::JSValue value, JSC::JSValue proto) +{ + return QScriptObjectDelegate::hasInstance(object, exec, value, proto); +} + +bool DeclarativeObjectDelegate::compareToObject(QScriptObject *o, JSC::ExecState *exec, JSC::JSObject *o2) +{ + if (!o2->inherits(&QScriptObject::info)) + return false; + + QScriptObject *scriptObject = static_cast<QScriptObject*>(o2); + QScriptObjectDelegate *delegate = scriptObject->delegate(); + if (!delegate || (delegate->type() != QScriptObjectDelegate::DeclarativeClassObject)) + return false; + + DeclarativeObjectDelegate *other = static_cast<DeclarativeObjectDelegate*>(delegate); + if (m_class != other->m_class) + return false; + else + return m_class->compare(m_object, other->m_object); +} + +} // namespace QScript + +QT_END_NAMESPACE diff --git a/src/script/bridge/qscriptdeclarativeobject_p.h b/src/script/bridge/qscriptdeclarativeobject_p.h new file mode 100644 index 0000000..b2a30d7 --- /dev/null +++ b/src/script/bridge/qscriptdeclarativeobject_p.h @@ -0,0 +1,109 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL-ONLY$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSCRIPTDECLARATIVEOBJECT_P_H +#define QSCRIPTDECLARATIVEOBJECT_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/qobjectdefs.h> + +#include "config.h" +#include "qscriptobject_p.h" +#include "qscriptdeclarativeclass_p.h" + +QT_BEGIN_NAMESPACE + +class QScriptClass; + +class QScriptDeclarativeClassPrivate +{ +public: + QScriptDeclarativeClassPrivate() : engine(0), q_ptr(0), context(0), supportsCall(false) {} + + QScriptEngine *engine; + QScriptDeclarativeClass *q_ptr; + QScriptContext *context; + bool supportsCall:1; + + static QScriptDeclarativeClassPrivate *get(QScriptDeclarativeClass *c) { + return c->d_ptr.data(); + } +}; + +namespace QScript +{ + +class DeclarativeObjectDelegate : public QScriptObjectDelegate +{ +public: + DeclarativeObjectDelegate(QScriptDeclarativeClass *c, QScriptDeclarativeClass::Object *o); + ~DeclarativeObjectDelegate(); + + virtual Type type() const; + + QScriptDeclarativeClass *scriptClass() const { return m_class; } + QScriptDeclarativeClass::Object *object() const { return m_object; } + + virtual bool getOwnPropertySlot(QScriptObject*, JSC::ExecState*, + const JSC::Identifier& propertyName, + JSC::PropertySlot&); + virtual void put(QScriptObject*, JSC::ExecState* exec, + const JSC::Identifier& propertyName, + JSC::JSValue, JSC::PutPropertySlot&); + virtual bool deleteProperty(QScriptObject*, JSC::ExecState*, + const JSC::Identifier& propertyName); + virtual void getOwnPropertyNames(QScriptObject*, JSC::ExecState*, + JSC::PropertyNameArray&, + JSC::EnumerationMode mode = JSC::ExcludeDontEnumProperties); + + virtual JSC::CallType getCallData(QScriptObject*, JSC::CallData&); + static JSC::JSValue JSC_HOST_CALL call(JSC::ExecState*, JSC::JSObject*, + JSC::JSValue, const JSC::ArgList&); + + virtual JSC::ConstructType getConstructData(QScriptObject*, JSC::ConstructData&); + + virtual bool hasInstance(QScriptObject*, JSC::ExecState*, + JSC::JSValue value, JSC::JSValue proto); + + bool compareToObject(QScriptObject *, JSC::ExecState *, JSC::JSObject *); + +private: + QScriptDeclarativeClass *m_class; + QScriptDeclarativeClass::Object *m_object; +}; + +} // namespace QScript + +QT_END_NAMESPACE + +#endif diff --git a/src/script/bridge/qscriptfunction.cpp b/src/script/bridge/qscriptfunction.cpp new file mode 100644 index 0000000..0480ce7 --- /dev/null +++ b/src/script/bridge/qscriptfunction.cpp @@ -0,0 +1,176 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtScript module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL-ONLY$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "config.h" +#include "qscriptfunction_p.h" + +#include "private/qscriptengine_p.h" +#include "qscriptcontext.h" +#include "private/qscriptcontext_p.h" +#include "private/qscriptvalue_p.h" +#include "qscriptactivationobject_p.h" +#include "qscriptobject_p.h" + +#include "JSGlobalObject.h" +#include "DebuggerCallFrame.h" +#include "Debugger.h" + +namespace JSC +{ +ASSERT_CLASS_FITS_IN_CELL(QT_PREPEND_NAMESPACE(QScript::FunctionWrapper)); +ASSERT_CLASS_FITS_IN_CELL(QT_PREPEND_NAMESPACE(QScript::FunctionWithArgWrapper)); +} + +QT_BEGIN_NAMESPACE + +namespace QScript +{ + +const JSC::ClassInfo FunctionWrapper::info = { "QtNativeFunctionWrapper", &PrototypeFunction::info, 0, 0 }; +const JSC::ClassInfo FunctionWithArgWrapper::info = { "QtNativeFunctionWithArgWrapper", &PrototypeFunction::info, 0, 0 }; + +FunctionWrapper::FunctionWrapper(JSC::ExecState *exec, int length, const JSC::Identifier &name, + QScriptEngine::FunctionSignature function) + : JSC::PrototypeFunction(exec, length, name, proxyCall), + data(new Data()) +{ + data->function = function; +} + +FunctionWrapper::~FunctionWrapper() +{ + delete data; +} + +JSC::ConstructType FunctionWrapper::getConstructData(JSC::ConstructData& consData) +{ + consData.native.function = proxyConstruct; + consData.native.function.doNotCallDebuggerFunctionExit(); + return JSC::ConstructTypeHost; +} + +JSC::JSValue FunctionWrapper::proxyCall(JSC::ExecState *exec, JSC::JSObject *callee, + JSC::JSValue thisObject, const JSC::ArgList &args) +{ + FunctionWrapper *self = static_cast<FunctionWrapper*>(callee); + QScriptEnginePrivate *eng_p = QScript::scriptEngineFromExec(exec); + + JSC::ExecState *oldFrame = eng_p->currentFrame; + eng_p->pushContext(exec, thisObject, args, callee); + QScriptContext *ctx = eng_p->contextForFrame(eng_p->currentFrame); + + QScriptValue result = self->data->function(ctx, QScriptEnginePrivate::get(eng_p)); + if (!result.isValid()) + result = QScriptValue(QScriptValue::UndefinedValue); + + eng_p->popContext(); + eng_p->currentFrame = oldFrame; + + return eng_p->scriptValueToJSCValue(result); +} + +JSC::JSObject* FunctionWrapper::proxyConstruct(JSC::ExecState *exec, JSC::JSObject *callee, + const JSC::ArgList &args) +{ + FunctionWrapper *self = static_cast<FunctionWrapper*>(callee); + QScriptEnginePrivate *eng_p = QScript::scriptEngineFromExec(exec); + + JSC::ExecState *oldFrame = eng_p->currentFrame; + eng_p->pushContext(exec, JSC::JSValue(), args, callee, true); + QScriptContext *ctx = eng_p->contextForFrame(eng_p->currentFrame); + + QScriptValue result = self->data->function(ctx, QScriptEnginePrivate::get(eng_p)); + + if (JSC::Debugger* debugger = eng_p->originalGlobalObject()->debugger()) + debugger->functionExit(QScriptValuePrivate::get(result)->jscValue, -1); + + if (!result.isObject()) + result = ctx->thisObject(); + + eng_p->popContext(); + eng_p->currentFrame = oldFrame; + + return JSC::asObject(eng_p->scriptValueToJSCValue(result)); +} + +FunctionWithArgWrapper::FunctionWithArgWrapper(JSC::ExecState *exec, int length, const JSC::Identifier &name, + QScriptEngine::FunctionWithArgSignature function, void *arg) + : JSC::PrototypeFunction(exec, length, name, proxyCall), + data(new Data()) +{ + 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 *exec, JSC::JSObject *callee, + JSC::JSValue thisObject, const JSC::ArgList &args) +{ + FunctionWithArgWrapper *self = static_cast<FunctionWithArgWrapper*>(callee); + QScriptEnginePrivate *eng_p = QScript::scriptEngineFromExec(exec); + + JSC::ExecState *oldFrame = eng_p->currentFrame; + eng_p->pushContext(exec, thisObject, args, callee); + QScriptContext *ctx = eng_p->contextForFrame(eng_p->currentFrame); + + QScriptValue result = self->data->function(ctx, QScriptEnginePrivate::get(eng_p), self->data->arg); + + eng_p->popContext(); + eng_p->currentFrame = oldFrame; + + return eng_p->scriptValueToJSCValue(result); +} + +JSC::JSObject* FunctionWithArgWrapper::proxyConstruct(JSC::ExecState *exec, JSC::JSObject *callee, + const JSC::ArgList &args) +{ + FunctionWithArgWrapper *self = static_cast<FunctionWithArgWrapper*>(callee); + QScriptEnginePrivate *eng_p = QScript::scriptEngineFromExec(exec); + + JSC::ExecState *oldFrame = eng_p->currentFrame; + eng_p->pushContext(exec, JSC::JSValue(), args, callee, true); + QScriptContext *ctx = eng_p->contextForFrame(eng_p->currentFrame); + + QScriptValue result = self->data->function(ctx, QScriptEnginePrivate::get(eng_p) , self->data->arg); + if (!result.isObject()) + result = ctx->thisObject(); + + eng_p->popContext(); + eng_p->currentFrame = oldFrame; + + return JSC::asObject(eng_p->scriptValueToJSCValue(result)); +} + +} // namespace QScript + +QT_END_NAMESPACE diff --git a/src/script/bridge/qscriptfunction_p.h b/src/script/bridge/qscriptfunction_p.h new file mode 100644 index 0000000..075ba52 --- /dev/null +++ b/src/script/bridge/qscriptfunction_p.h @@ -0,0 +1,119 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtScript module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL-ONLY$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_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> + +#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::FunctionSignature function; + }; + + FunctionWrapper(JSC::ExecState*, int length, const JSC::Identifier&, + QScriptEngine::FunctionSignature); + ~FunctionWrapper(); + + virtual const JSC::ClassInfo* classInfo() const { return &info; } + static const JSC::ClassInfo info; + + QScriptEngine::FunctionSignature function() const + { return data->function; } + +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::FunctionWithArgSignature function; + void *arg; + }; + + FunctionWithArgWrapper(JSC::ExecState*, int length, const JSC::Identifier&, + QScriptEngine::FunctionWithArgSignature, void *); + ~FunctionWithArgWrapper(); + + virtual const JSC::ClassInfo* classInfo() const { return &info; } + static const JSC::ClassInfo info; + + QScriptEngine::FunctionWithArgSignature function() const + { return data->function; } + + void *arg() const + { return data->arg; } + +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; +}; + +} // namespace QScript + +QT_END_NAMESPACE + +#endif diff --git a/src/script/bridge/qscriptglobalobject.cpp b/src/script/bridge/qscriptglobalobject.cpp new file mode 100644 index 0000000..ee016e9 --- /dev/null +++ b/src/script/bridge/qscriptglobalobject.cpp @@ -0,0 +1,158 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtScript module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL-ONLY$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "config.h" +#include "qscriptglobalobject_p.h" + +#include "../api/qscriptengine.h" +#include "../api/qscriptengine_p.h" + +namespace JSC +{ +QT_USE_NAMESPACE + +ASSERT_CLASS_FITS_IN_CELL(QScript::GlobalObject); +ASSERT_CLASS_FITS_IN_CELL(QScript::OriginalGlobalObjectProxy); + +} // namespace JSC + +QT_BEGIN_NAMESPACE + +namespace QScript +{ + +GlobalObject::GlobalObject() + : JSC::JSGlobalObject(), customGlobalObject(0) +{ +} + +GlobalObject::~GlobalObject() +{ +} + +void GlobalObject::markChildren(JSC::MarkStack& markStack) +{ + JSC::JSGlobalObject::markChildren(markStack); + if (customGlobalObject) + markStack.append(customGlobalObject); +} + +bool GlobalObject::getOwnPropertySlot(JSC::ExecState* exec, + const JSC::Identifier& propertyName, + JSC::PropertySlot& slot) +{ + QScriptEnginePrivate *engine = scriptEngineFromExec(exec); + if (propertyName == exec->propertyNames().arguments && engine->currentFrame->argumentCount() > 0) { + JSC::JSValue args = engine->scriptValueToJSCValue(engine->contextForFrame(engine->currentFrame)->argumentsObject()); + slot.setValue(args); + return true; + } + if (customGlobalObject) + return customGlobalObject->getOwnPropertySlot(exec, propertyName, slot); + return JSC::JSGlobalObject::getOwnPropertySlot(exec, propertyName, slot); +} + +bool GlobalObject::getOwnPropertyDescriptor(JSC::ExecState* exec, + const JSC::Identifier& propertyName, + JSC::PropertyDescriptor& descriptor) +{ + // Must match the logic of getOwnPropertySlot(). + QScriptEnginePrivate *engine = scriptEngineFromExec(exec); + if (propertyName == exec->propertyNames().arguments && engine->currentFrame->argumentCount() > 0) { + // ### Can we get rid of this special handling of the arguments property? + JSC::JSValue args = engine->scriptValueToJSCValue(engine->contextForFrame(engine->currentFrame)->argumentsObject()); + descriptor.setValue(args); + return true; + } + if (customGlobalObject) + return customGlobalObject->getOwnPropertyDescriptor(exec, propertyName, descriptor); + return JSC::JSGlobalObject::getOwnPropertyDescriptor(exec, propertyName, descriptor); +} + +void GlobalObject::put(JSC::ExecState* exec, const JSC::Identifier& propertyName, + JSC::JSValue value, JSC::PutPropertySlot& slot) +{ + if (customGlobalObject) + customGlobalObject->put(exec, propertyName, value, slot); + else + JSC::JSGlobalObject::put(exec, propertyName, value, slot); +} + +void GlobalObject::putWithAttributes(JSC::ExecState* exec, const JSC::Identifier& propertyName, + JSC::JSValue value, unsigned attributes) +{ + if (customGlobalObject) + customGlobalObject->putWithAttributes(exec, propertyName, value, attributes); + else + JSC::JSGlobalObject::putWithAttributes(exec, propertyName, value, attributes); +} + +bool GlobalObject::deleteProperty(JSC::ExecState* exec, const JSC::Identifier& propertyName) +{ + if (customGlobalObject) + return customGlobalObject->deleteProperty(exec, propertyName); + return JSC::JSGlobalObject::deleteProperty(exec, propertyName); +} + +void GlobalObject::getOwnPropertyNames(JSC::ExecState* exec, JSC::PropertyNameArray& propertyNames, + JSC::EnumerationMode mode) +{ + if (customGlobalObject) + customGlobalObject->getOwnPropertyNames(exec, propertyNames, mode); + else + JSC::JSGlobalObject::getOwnPropertyNames(exec, propertyNames, mode); +} + +void GlobalObject::defineGetter(JSC::ExecState* exec, const JSC::Identifier& propertyName, JSC::JSObject* getterFunction, unsigned attributes) +{ + if (customGlobalObject) + customGlobalObject->defineGetter(exec, propertyName, getterFunction, attributes); + else + JSC::JSGlobalObject::defineGetter(exec, propertyName, getterFunction, attributes); +} + +void GlobalObject::defineSetter(JSC::ExecState* exec, const JSC::Identifier& propertyName, JSC::JSObject* setterFunction, unsigned attributes) +{ + if (customGlobalObject) + customGlobalObject->defineSetter(exec, propertyName, setterFunction, attributes); + else + JSC::JSGlobalObject::defineSetter(exec, propertyName, setterFunction, attributes); +} + +JSC::JSValue GlobalObject::lookupGetter(JSC::ExecState* exec, const JSC::Identifier& propertyName) +{ + if (customGlobalObject) + return customGlobalObject->lookupGetter(exec, propertyName); + return JSC::JSGlobalObject::lookupGetter(exec, propertyName); +} + +JSC::JSValue GlobalObject::lookupSetter(JSC::ExecState* exec, const JSC::Identifier& propertyName) +{ + if (customGlobalObject) + return customGlobalObject->lookupSetter(exec, propertyName); + return JSC::JSGlobalObject::lookupSetter(exec, propertyName); +} + +} // namespace QScript + +QT_END_NAMESPACE diff --git a/src/script/bridge/qscriptglobalobject_p.h b/src/script/bridge/qscriptglobalobject_p.h new file mode 100644 index 0000000..e17efdf --- /dev/null +++ b/src/script/bridge/qscriptglobalobject_p.h @@ -0,0 +1,127 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtScript module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL-ONLY$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSCRIPTGLOBALOBJECT_P_H +#define QSCRIPTGLOBALOBJECT_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/qobjectdefs.h> + +#include "JSGlobalObject.h" + +QT_BEGIN_NAMESPACE + +namespace QScript +{ + +class GlobalObject : public JSC::JSGlobalObject +{ +public: + GlobalObject(); + virtual ~GlobalObject(); + virtual JSC::UString className() const { return "global"; } + virtual void markChildren(JSC::MarkStack&); + virtual bool getOwnPropertySlot(JSC::ExecState*, + const JSC::Identifier& propertyName, + JSC::PropertySlot&); + virtual bool getOwnPropertyDescriptor(JSC::ExecState*, + const JSC::Identifier& propertyName, + JSC::PropertyDescriptor&); + virtual void put(JSC::ExecState* exec, const JSC::Identifier& propertyName, + JSC::JSValue, JSC::PutPropertySlot&); + virtual void putWithAttributes(JSC::ExecState* exec, const JSC::Identifier& propertyName, + JSC::JSValue value, unsigned attributes); + virtual bool deleteProperty(JSC::ExecState*, + const JSC::Identifier& propertyName); + virtual void getOwnPropertyNames(JSC::ExecState*, JSC::PropertyNameArray&, + JSC::EnumerationMode mode = JSC::ExcludeDontEnumProperties); + virtual void defineGetter(JSC::ExecState*, const JSC::Identifier& propertyName, JSC::JSObject* getterFunction, unsigned attributes = 0); + virtual void defineSetter(JSC::ExecState*, const JSC::Identifier& propertyName, JSC::JSObject* setterFunction, unsigned attributes = 0); + virtual JSC::JSValue lookupGetter(JSC::ExecState*, const JSC::Identifier& propertyName); + virtual JSC::JSValue lookupSetter(JSC::ExecState*, const JSC::Identifier& propertyName); + +public: + JSC::JSObject *customGlobalObject; +}; + +class OriginalGlobalObjectProxy : public JSC::JSObject +{ +public: + explicit OriginalGlobalObjectProxy(WTF::PassRefPtr<JSC::Structure> sid, + JSC::JSGlobalObject *object) + : JSC::JSObject(sid), originalGlobalObject(object) + {} + virtual ~OriginalGlobalObjectProxy() + {} + virtual JSC::UString className() const + { return originalGlobalObject->className(); } + virtual void markChildren(JSC::MarkStack& markStack) + { + markStack.append(originalGlobalObject); + JSC::JSObject::markChildren(markStack); + } + virtual bool getOwnPropertySlot(JSC::ExecState* exec, + const JSC::Identifier& propertyName, + JSC::PropertySlot& slot) + { return originalGlobalObject->JSC::JSGlobalObject::getOwnPropertySlot(exec, propertyName, slot); } + virtual bool getOwnPropertyDescriptor(JSC::ExecState* exec, + const JSC::Identifier& propertyName, + JSC::PropertyDescriptor& descriptor) + { return originalGlobalObject->JSC::JSGlobalObject::getOwnPropertyDescriptor(exec, propertyName, descriptor); } + virtual void put(JSC::ExecState* exec, const JSC::Identifier& propertyName, + JSC::JSValue value, JSC::PutPropertySlot& slot) + { originalGlobalObject->JSC::JSGlobalObject::put(exec, propertyName, value, slot); } + virtual void putWithAttributes(JSC::ExecState* exec, const JSC::Identifier& propertyName, JSC::JSValue value, unsigned attributes) + { originalGlobalObject->JSC::JSGlobalObject::putWithAttributes(exec, propertyName, value, attributes); } + virtual bool deleteProperty(JSC::ExecState* exec, + const JSC::Identifier& propertyName) + { return originalGlobalObject->JSC::JSGlobalObject::deleteProperty(exec, propertyName); } + virtual void getOwnPropertyNames(JSC::ExecState* exec, JSC::PropertyNameArray& propertyNames, JSC::EnumerationMode mode = JSC::ExcludeDontEnumProperties) + { originalGlobalObject->JSC::JSGlobalObject::getOwnPropertyNames(exec, propertyNames, mode); } + virtual void defineGetter(JSC::ExecState* exec, const JSC::Identifier& propertyName, JSC::JSObject* getterFunction, unsigned attributes) + { originalGlobalObject->JSC::JSGlobalObject::defineGetter(exec, propertyName, getterFunction, attributes); } + virtual void defineSetter(JSC::ExecState* exec, const JSC::Identifier& propertyName, JSC::JSObject* setterFunction, unsigned attributes) + { originalGlobalObject->JSC::JSGlobalObject::defineSetter(exec, propertyName, setterFunction, attributes); } + virtual JSC::JSValue lookupGetter(JSC::ExecState* exec, const JSC::Identifier& propertyName) + { return originalGlobalObject->JSC::JSGlobalObject::lookupGetter(exec, propertyName); } + virtual JSC::JSValue lookupSetter(JSC::ExecState* exec, const JSC::Identifier& propertyName) + { return originalGlobalObject->JSC::JSGlobalObject::lookupSetter(exec, propertyName); } +private: + JSC::JSGlobalObject *originalGlobalObject; +}; + +} // namespace QScript + +QT_END_NAMESPACE + +#endif diff --git a/src/script/bridge/qscriptobject.cpp b/src/script/bridge/qscriptobject.cpp new file mode 100644 index 0000000..5d57c66 --- /dev/null +++ b/src/script/bridge/qscriptobject.cpp @@ -0,0 +1,222 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtScript module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL-ONLY$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "config.h" +#include "qscriptobject_p.h" +#include "private/qobject_p.h" + +namespace JSC +{ +//QT_USE_NAMESPACE +ASSERT_CLASS_FITS_IN_CELL(QT_PREPEND_NAMESPACE(QScriptObject)); +ASSERT_CLASS_FITS_IN_CELL(QT_PREPEND_NAMESPACE(QScriptObjectPrototype)); +} + +QT_BEGIN_NAMESPACE + +// masquerading as JSC::JSObject +const JSC::ClassInfo QScriptObject::info = { "Object", 0, 0, 0 }; + +QScriptObject::Data::~Data() +{ + delete delegate; +} + +QScriptObject::QScriptObject(WTF::PassRefPtr<JSC::Structure> sid) + : JSC::JSObject(sid), d(0) +{ +} + +QScriptObject::~QScriptObject() +{ + delete d; +} + +bool QScriptObject::getOwnPropertySlot(JSC::ExecState* exec, + const JSC::Identifier& propertyName, + JSC::PropertySlot& slot) +{ + if (!d || !d->delegate) + return JSC::JSObject::getOwnPropertySlot(exec, propertyName, slot); + return d->delegate->getOwnPropertySlot(this, exec, propertyName, slot); +} + +bool QScriptObject::getOwnPropertyDescriptor(JSC::ExecState* exec, + const JSC::Identifier& propertyName, + JSC::PropertyDescriptor& descriptor) +{ + if (!d || !d->delegate) + return JSC::JSObject::getOwnPropertyDescriptor(exec, propertyName, descriptor); + return d->delegate->getOwnPropertyDescriptor(this, exec, propertyName, descriptor); +} + +void QScriptObject::put(JSC::ExecState* exec, const JSC::Identifier& propertyName, + JSC::JSValue value, JSC::PutPropertySlot& slot) +{ + if (!d || !d->delegate) { + JSC::JSObject::put(exec, propertyName, value, slot); + return; + } + d->delegate->put(this, exec, propertyName, value, slot); +} + +bool QScriptObject::deleteProperty(JSC::ExecState* exec, + const JSC::Identifier& propertyName) +{ + if (!d || !d->delegate) + return JSC::JSObject::deleteProperty(exec, propertyName); + return d->delegate->deleteProperty(this, exec, propertyName); +} + +void QScriptObject::getOwnPropertyNames(JSC::ExecState* exec, JSC::PropertyNameArray& propertyNames, + JSC::EnumerationMode mode) +{ + if (!d || !d->delegate) { + JSC::JSObject::getOwnPropertyNames(exec, propertyNames, mode); + return; + } + d->delegate->getOwnPropertyNames(this, exec, propertyNames, mode); +} + +bool QScriptObject::compareToObject(JSC::ExecState* exec, JSC::JSObject *other) +{ + if (!d || !d->delegate) { + return JSC::JSObject::compareToObject(exec, other); + } + return d->delegate->compareToObject(this, exec, other); +} + +void QScriptObject::markChildren(JSC::MarkStack& markStack) +{ + if (!d) + d = new Data(); + if (d->isMarking) + return; + QBoolBlocker markBlocker(d->isMarking, true); + if (d && d->data) + markStack.append(d->data); + if (!d || !d->delegate) { + JSC::JSObject::markChildren(markStack); + return; + } + d->delegate->markChildren(this, markStack); +} + +JSC::CallType QScriptObject::getCallData(JSC::CallData &data) +{ + if (!d || !d->delegate) + return JSC::JSObject::getCallData(data); + return d->delegate->getCallData(this, data); +} + +JSC::ConstructType QScriptObject::getConstructData(JSC::ConstructData &data) +{ + if (!d || !d->delegate) + return JSC::JSObject::getConstructData(data); + return d->delegate->getConstructData(this, data); +} + +bool QScriptObject::hasInstance(JSC::ExecState* exec, JSC::JSValue value, JSC::JSValue proto) +{ + if (!d || !d->delegate) + return JSC::JSObject::hasInstance(exec, value, proto); + return d->delegate->hasInstance(this, exec, value, proto); +} + +QScriptObjectPrototype::QScriptObjectPrototype(JSC::ExecState*, WTF::PassRefPtr<JSC::Structure> structure, + JSC::Structure* /*prototypeFunctionStructure*/) + : QScriptObject(structure) +{ +} + +QScriptObjectDelegate::QScriptObjectDelegate() +{ +} + +QScriptObjectDelegate::~QScriptObjectDelegate() +{ +} + +bool QScriptObjectDelegate::getOwnPropertySlot(QScriptObject* object, JSC::ExecState* exec, + const JSC::Identifier& propertyName, + JSC::PropertySlot& slot) +{ + return object->JSC::JSObject::getOwnPropertySlot(exec, propertyName, slot); +} + +bool QScriptObjectDelegate::getOwnPropertyDescriptor(QScriptObject* object, JSC::ExecState* exec, + const JSC::Identifier& propertyName, + JSC::PropertyDescriptor& descriptor) +{ + return object->JSC::JSObject::getOwnPropertyDescriptor(exec, propertyName, descriptor); +} + + +void QScriptObjectDelegate::put(QScriptObject* object, JSC::ExecState* exec, + const JSC::Identifier& propertyName, + JSC::JSValue value, JSC::PutPropertySlot& slot) +{ + object->JSC::JSObject::put(exec, propertyName, value, slot); +} + +bool QScriptObjectDelegate::deleteProperty(QScriptObject* object, JSC::ExecState* exec, + const JSC::Identifier& propertyName) +{ + return object->JSC::JSObject::deleteProperty(exec, propertyName); +} + +void QScriptObjectDelegate::getOwnPropertyNames(QScriptObject* object, JSC::ExecState* exec, + JSC::PropertyNameArray& propertyNames, + JSC::EnumerationMode mode) +{ + object->JSC::JSObject::getOwnPropertyNames(exec, propertyNames, mode); +} + +void QScriptObjectDelegate::markChildren(QScriptObject* object, JSC::MarkStack& markStack) +{ + // ### should this call the virtual function instead?? + object->JSC::JSObject::markChildren(markStack); +} + +JSC::CallType QScriptObjectDelegate::getCallData(QScriptObject* object, JSC::CallData& data) +{ + return object->JSC::JSObject::getCallData(data); +} + +JSC::ConstructType QScriptObjectDelegate::getConstructData(QScriptObject* object, JSC::ConstructData& data) +{ + return object->JSC::JSObject::getConstructData(data); +} + +bool QScriptObjectDelegate::hasInstance(QScriptObject* object, JSC::ExecState* exec, + JSC::JSValue value, JSC::JSValue proto) +{ + return object->JSC::JSObject::hasInstance(exec, value, proto); +} + +bool QScriptObjectDelegate::compareToObject(QScriptObject* object, JSC::ExecState* exec, JSC::JSObject* o) +{ + return object->JSC::JSObject::compareToObject(exec, o); +} + +QT_END_NAMESPACE diff --git a/src/script/bridge/qscriptobject_p.h b/src/script/bridge/qscriptobject_p.h new file mode 100644 index 0000000..c9613ea --- /dev/null +++ b/src/script/bridge/qscriptobject_p.h @@ -0,0 +1,176 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtScript module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL-ONLY$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSCRIPTOBJECT_P_H +#define QSCRIPTOBJECT_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/qobjectdefs.h> + +#include "JSObject.h" + +QT_BEGIN_NAMESPACE + +class QScriptObjectDelegate; + +class QScriptObject : public JSC::JSObject +{ +public: + // work around CELL_SIZE limitation + struct Data + { + JSC::JSValue data; // QScriptValue::data + QScriptObjectDelegate *delegate; + bool isMarking; // recursion guard + + Data() : delegate(0), isMarking(false) {} + ~Data(); + }; + + explicit QScriptObject(WTF::PassRefPtr<JSC::Structure> sid); + virtual ~QScriptObject(); + + virtual bool getOwnPropertySlot(JSC::ExecState*, + const JSC::Identifier& propertyName, + JSC::PropertySlot&); + virtual bool getOwnPropertyDescriptor(JSC::ExecState*, const JSC::Identifier&, JSC::PropertyDescriptor&); + 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 getOwnPropertyNames(JSC::ExecState*, JSC::PropertyNameArray&, + JSC::EnumerationMode mode = JSC::ExcludeDontEnumProperties); + virtual void markChildren(JSC::MarkStack& markStack); + virtual JSC::CallType getCallData(JSC::CallData&); + virtual JSC::ConstructType getConstructData(JSC::ConstructData&); + virtual bool hasInstance(JSC::ExecState*, JSC::JSValue value, JSC::JSValue proto); + virtual bool compareToObject(JSC::ExecState*, JSC::JSObject*); + + virtual const JSC::ClassInfo* classInfo() const { return &info; } + static const JSC::ClassInfo info; + + static WTF::PassRefPtr<JSC::Structure> createStructure(JSC::JSValue prototype) + { + return JSC::Structure::create(prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags)); + } + + inline JSC::JSValue data() const; + inline void setData(JSC::JSValue data); + + inline QScriptObjectDelegate *delegate() const; + inline void setDelegate(QScriptObjectDelegate *delegate); + +protected: + static const unsigned StructureFlags = JSC::ImplementsHasInstance | JSC::OverridesHasInstance | JSC::OverridesGetOwnPropertySlot | JSC::OverridesMarkChildren | JSC::OverridesGetPropertyNames | JSObject::StructureFlags; + + Data *d; +}; + +class QScriptObjectPrototype : public QScriptObject +{ +public: + QScriptObjectPrototype(JSC::ExecState*, WTF::PassRefPtr<JSC::Structure>, + JSC::Structure* prototypeFunctionStructure); +}; + +class QScriptObjectDelegate +{ +public: + enum Type { + QtObject, + Variant, + ClassObject, + DeclarativeClassObject + }; + + QScriptObjectDelegate(); + virtual ~QScriptObjectDelegate(); + + virtual Type type() const = 0; + + virtual bool getOwnPropertySlot(QScriptObject*, JSC::ExecState*, + const JSC::Identifier& propertyName, + JSC::PropertySlot&); + virtual bool getOwnPropertyDescriptor(QScriptObject*, JSC::ExecState*, + const JSC::Identifier& propertyName, + JSC::PropertyDescriptor&); + virtual void put(QScriptObject*, JSC::ExecState* exec, const JSC::Identifier& propertyName, + JSC::JSValue, JSC::PutPropertySlot&); + virtual bool deleteProperty(QScriptObject*, JSC::ExecState*, + const JSC::Identifier& propertyName); + virtual void getOwnPropertyNames(QScriptObject*, JSC::ExecState*, JSC::PropertyNameArray&, + JSC::EnumerationMode mode = JSC::ExcludeDontEnumProperties); + virtual void markChildren(QScriptObject*, JSC::MarkStack& markStack); + virtual JSC::CallType getCallData(QScriptObject*, JSC::CallData&); + virtual JSC::ConstructType getConstructData(QScriptObject*, JSC::ConstructData&); + virtual bool hasInstance(QScriptObject*, JSC::ExecState*, + JSC::JSValue value, JSC::JSValue proto); + virtual bool compareToObject(QScriptObject*, JSC::ExecState*, JSC::JSObject*); + +private: + Q_DISABLE_COPY(QScriptObjectDelegate) +}; + +inline JSC::JSValue QScriptObject::data() const +{ + if (!d) + return JSC::JSValue(); + return d->data; +} + +inline void QScriptObject::setData(JSC::JSValue data) +{ + if (!d) + d = new Data(); + d->data = data; +} + +inline QScriptObjectDelegate *QScriptObject::delegate() const +{ + if (!d) + return 0; + return d->delegate; +} + +inline void QScriptObject::setDelegate(QScriptObjectDelegate *delegate) +{ + if (!d) + d = new Data(); + else + delete d->delegate; + d->delegate = delegate; +} + +QT_END_NAMESPACE + +#endif diff --git a/src/script/bridge/qscriptqobject.cpp b/src/script/bridge/qscriptqobject.cpp new file mode 100644 index 0000000..cee8319 --- /dev/null +++ b/src/script/bridge/qscriptqobject.cpp @@ -0,0 +1,2317 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtScript module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL-ONLY$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "config.h" +#include "qscriptqobject_p.h" + +#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 "../api/qscriptcontext_p.h" +#include "qscriptfunction_p.h" + +#include "Error.h" +#include "PrototypeFunction.h" +#include "NativeFunctionWrapper.h" +#include "PropertyNameArray.h" +#include "JSFunction.h" +#include "JSString.h" +#include "JSValue.h" +#include "JSArray.h" +#include "RegExpObject.h" +#include "RegExpConstructor.h" + +namespace JSC +{ +QT_USE_NAMESPACE +ASSERT_CLASS_FITS_IN_CELL(QScript::QObjectPrototype); +ASSERT_CLASS_FITS_IN_CELL(QScript::QMetaObjectWrapperObject); +ASSERT_CLASS_FITS_IN_CELL(QScript::QMetaObjectPrototype); +ASSERT_CLASS_FITS_IN_CELL(QScript::QtFunction); +ASSERT_CLASS_FITS_IN_CELL(QScript::QtPropertyFunction); +} + +QT_BEGIN_NAMESPACE + +namespace QScript +{ + +struct QObjectConnection +{ + 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(JSC::MarkStack& markStack) + { + if (senderWrapper) { + // see if the sender should be marked or not; + // if the C++ object is owned by script, we don't want + // it to stay alive due to a script connection. + Q_ASSERT(senderWrapper.inherits(&QScriptObject::info)); + QScriptObject *scriptObject = static_cast<QScriptObject*>(JSC::asObject(senderWrapper)); + if (!JSC::Heap::isCellMarked(scriptObject)) { + QScriptObjectDelegate *delegate = scriptObject->delegate(); + Q_ASSERT(delegate && (delegate->type() == QScriptObjectDelegate::QtObject)); + QObjectDelegate *inst = static_cast<QObjectDelegate*>(delegate); + if ((inst->ownership() == QScriptEngine::ScriptOwnership) + || ((inst->ownership() == QScriptEngine::AutoOwnership) + && inst->value() && !inst->value()->parent())) { + senderWrapper = JSC::JSValue(); + } else { + markStack.append(senderWrapper); + } + } + } + if (receiver) + markStack.append(receiver); + if (slot) + markStack.append(slot); + } +}; + +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(JSC::MarkStack&); + +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)) + && (!(opt & QScriptEngine::ExcludeSlots) || (method.methodType() != QMetaMethod::Slot)); +} + +static bool isEnumerableMetaProperty(const QMetaProperty &prop, + const QMetaObject *mo, int index) +{ + return prop.isScriptable() && prop.isValid() + // the following lookup is to ensure that we have the + // "most derived" occurrence of the property with this name + && (mo->indexOfProperty(prop.name()) == index); +} + +/*! \internal + Calculates the length of the name of the given \a method by looking + for the first '(' character. +*/ +static inline int methodNameLength(const QMetaMethod &method) +{ + const char *signature = method.signature(); + const char *s = signature; + while (*s && (*s != '(')) + ++s; + return s - signature; +} + +/*! \internal + Makes a deep copy of the first \a nameLength characters of the given + method \a signature and returns the copy. +*/ +static inline QByteArray methodName(const char *signature, int nameLength) +{ + return QByteArray(signature, nameLength); +} + +/*! \internal + + Returns true if the name of the given \a method is the same as that + specified by the (signature, nameLength) pair, otherwise returns + false. +*/ +static inline bool methodNameEquals(const QMetaMethod &method, + const char *signature, int nameLength) +{ + const char *otherSignature = method.signature(); + return !qstrncmp(otherSignature, signature, nameLength) + && (otherSignature[nameLength] == '('); +} + +static QVariant variantFromValue(JSC::ExecState *exec, int targetType, JSC::JSValue value) +{ + QVariant v(targetType, (void *)0); + if (QScriptEnginePrivate::convertValue(exec, value, targetType, v.data())) + return v; + if (uint(targetType) == QVariant::LastType) + return QScriptEnginePrivate::toVariant(exec, value); + if (QScriptEnginePrivate::isVariant(value)) { + v = QScriptEnginePrivate::variantValue(value); + 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<void* *>(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<QScriptable*>(ptr); +} + +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 = call; + return JSC::CallTypeHost; +} + +void QtFunction::markChildren(JSC::MarkStack& markStack) +{ + if (data->object) + markStack.append(data->object); + JSC::InternalFunction::markChildren(markStack); +} + +QScriptObject *QtFunction::wrapperObject() const +{ + Q_ASSERT(JSC::asObject(data->object)->inherits(&QScriptObject::info)); + return static_cast<QScriptObject*>(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<QScript::QObjectDelegate*>(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<int> QScript::QtFunction::overloadedIndexes() const +{ + if (!maybeOverloaded()) + return QList<int>(); + QList<int> result; + const QMetaObject *meta = metaObject(); + QMetaMethod method = meta->method(initialIndex()); + int nameLength = methodNameLength(method); + for (int index = mostGeneralMethod() - 1; index >= 0; --index) { + if (methodNameEquals(meta->method(index), method.signature(), nameLength)) + result.append(index); + } + return result; +} + +class QScriptMetaType +{ +public: + enum Kind { + Invalid, + Variant, + MetaType, + Unresolved, + MetaEnum + }; + + inline QScriptMetaType() + : m_kind(Invalid) { } + + inline Kind kind() const + { return m_kind; } + + int typeId() const; + + inline bool isValid() const + { return (m_kind != Invalid); } + + inline bool isVariant() const + { return (m_kind == Variant); } + + inline bool isMetaType() const + { return (m_kind == MetaType); } + + inline bool isUnresolved() const + { return (m_kind == Unresolved); } + + inline bool isMetaEnum() const + { return (m_kind == MetaEnum); } + + QByteArray name() const; + + inline int enumeratorIndex() const + { Q_ASSERT(isMetaEnum()); return m_typeId; } + + inline bool operator==(const QScriptMetaType &other) const + { + return (m_kind == other.m_kind) && (m_typeId == other.m_typeId); + } + + static inline QScriptMetaType variant() + { return QScriptMetaType(Variant); } + + static inline QScriptMetaType metaType(int typeId, const QByteArray &name) + { return QScriptMetaType(MetaType, typeId, name); } + + static inline QScriptMetaType metaEnum(int enumIndex, const QByteArray &name) + { return QScriptMetaType(MetaEnum, enumIndex, name); } + + static inline QScriptMetaType unresolved(const QByteArray &name) + { return QScriptMetaType(Unresolved, /*typeId=*/0, name); } + +private: + inline QScriptMetaType(Kind kind, int typeId = 0, const QByteArray &name = QByteArray()) + : m_kind(kind), m_typeId(typeId), m_name(name) { } + + Kind m_kind; + int m_typeId; + QByteArray m_name; +}; + +int QScriptMetaType::typeId() const +{ + if (isVariant()) + return QMetaType::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 QVector<QScriptMetaType> &types) + : 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(); } + + 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<QScriptMetaType> types() const + { return m_types; } + +private: + 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); } +}; + +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) +{ + QScriptMetaMethod chosenMethod; + int chosenIndex = -1; + QVarLengthArray<QVariant, 9> args; + QVector<QScriptMetaArguments> candidates; + QVector<QScriptMetaArguments> unresolved; + QVector<int> tooFewArgs; + QVector<int> conversionFailed; + int index; + int nameLength = 0; + const char *initialMethodSignature = 0; + exec->clearException(); + QScriptEnginePrivate *engine = QScript::scriptEngineFromExec(exec); + for (index = initialIndex; index >= 0; --index) { + QMetaMethod method = metaMethod(meta, callType, index); + + if (index == initialIndex) { + initialMethodSignature = method.signature(); + nameLength = methodNameLength(method); + } else { + if (!methodNameEquals(method, initialMethodSignature, nameLength)) + continue; + } + + QList<QByteArray> parameterTypeNames = method.parameterTypes(); + + QVector<QScriptMetaType> types; + types.resize(1 + parameterTypeNames.size()); + QScriptMetaType *typesData = types.data(); + // resolve return type + QByteArray returnTypeName = method.typeName(); + int rtype = QMetaType::type(returnTypeName); + if ((rtype == 0) && !returnTypeName.isEmpty()) { + int enumIndex = indexOfMetaEnum(meta, returnTypeName); + if (enumIndex != -1) + typesData[0] = QScriptMetaType::metaEnum(enumIndex, returnTypeName); + else + typesData[0] = QScriptMetaType::unresolved(returnTypeName); + } else { + if (callType == QMetaMethod::Constructor) + typesData[0] = QScriptMetaType::metaType(QMetaType::QObjectStar, "QObject*"); + else if (rtype == QMetaType::QVariant) + typesData[0] = QScriptMetaType::variant(); + else + typesData[0] = QScriptMetaType::metaType(rtype, returnTypeName); + } + + // resolve argument types + for (int i = 0; i < parameterTypeNames.count(); ++i) { + QByteArray argTypeName = parameterTypeNames.at(i); + int atype = QMetaType::type(argTypeName); + if (atype == 0) { + int enumIndex = indexOfMetaEnum(meta, argTypeName); + if (enumIndex != -1) + typesData[1 + i] = QScriptMetaType::metaEnum(enumIndex, argTypeName); + else + typesData[1 + i] = QScriptMetaType::unresolved(argTypeName); + } else if (atype == QMetaType::QVariant) { + typesData[1 + i] = QScriptMetaType::variant(); + } else { + typesData[1 + i] = QScriptMetaType::metaType(atype, argTypeName); + } + } + + QScriptMetaMethod mtd = QScriptMetaMethod(types); + + if (int(scriptArgs.size()) < mtd.argumentCount()) { + tooFewArgs.append(index); + continue; + } + + if (!mtd.fullyResolved()) { + // remember it so we can give an error message later, if necessary + unresolved.append(QScriptMetaArguments(/*matchDistance=*/INT_MAX, index, + mtd, QVarLengthArray<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) { + JSC::JSValue actual; + if (i < (int)scriptArgs.size()) + actual = scriptArgs.at(i); + else + actual = JSC::jsUndefined(); + QScriptMetaType argType = mtd.argumentType(i); + int tid = -1; + QVariant v; + if (argType.isUnresolved()) { + v = QVariant(QMetaType::QObjectStar, (void *)0); + converted = QScriptEnginePrivate::convertToNativeQObject( + exec, actual, argType.name(), reinterpret_cast<void* *>(v.data())); + } else if (argType.isVariant()) { + if (QScriptEnginePrivate::isVariant(actual)) { + v = QScriptEnginePrivate::variantValue(actual); + } else { + v = QScriptEnginePrivate::toVariant(exec, actual); + converted = v.isValid() || actual.isUndefined() || actual.isNull(); + } + } else { + tid = argType.typeId(); + v = QVariant(tid, (void *)0); + converted = QScriptEnginePrivate::convertValue(exec, actual, tid, v.data()); + if (exec->hadException()) + return exec->exception(); + } + + if (!converted) { + if (QScriptEnginePrivate::isVariant(actual)) { + if (tid == -1) + tid = argType.typeId(); + QVariant vv = QScriptEnginePrivate::variantValue(actual); + if (vv.canConvert(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() || actual.isString()) { + // see if it's an enum value + QMetaEnum m; + if (argType.isMetaEnum()) { + m = meta->enumerator(argType.enumeratorIndex()); + } else { + int mi = indexOfMetaEnum(meta, argType.name()); + if (mi != -1) + m = meta->enumerator(mi); + } + if (m.isValid()) { + if (actual.isNumber()) { + int ival = QScriptEnginePrivate::toInt32(exec, actual); + if (m.valueToKey(ival) != 0) { + v.setValue(ival); + converted = true; + matchDistance += 10; + } + } else { + JSC::UString sval = QScriptEnginePrivate::toString(exec, actual); + int ival = m.keyToValue(convertToLatin1(sval)); + if (ival != -1) { + v.setValue(ival); + converted = true; + matchDistance += 10; + } + } + } + } + } else { + // determine how well the conversion matched + if (actual.isNumber()) { + switch (tid) { + case QMetaType::Double: + // perfect + break; + case QMetaType::Float: + matchDistance += 1; + break; + case QMetaType::LongLong: + case QMetaType::ULongLong: + matchDistance += 2; + break; + case QMetaType::Long: + case QMetaType::ULong: + matchDistance += 3; + break; + case QMetaType::Int: + case QMetaType::UInt: + matchDistance += 4; + break; + case QMetaType::Short: + case QMetaType::UShort: + matchDistance += 5; + break; + case QMetaType::Char: + case QMetaType::UChar: + matchDistance += 6; + break; + default: + matchDistance += 10; + break; + } + } else if (actual.isString()) { + switch (tid) { + case QMetaType::QString: + // perfect + break; + default: + matchDistance += 10; + break; + } + } else if (actual.isBoolean()) { + switch (tid) { + case QMetaType::Bool: + // perfect + break; + default: + matchDistance += 10; + break; + } + } else if (QScriptEnginePrivate::isDate(actual)) { + switch (tid) { + case QMetaType::QDateTime: + // perfect + break; + case QMetaType::QDate: + matchDistance += 1; + break; + case QMetaType::QTime: + matchDistance += 2; + break; + default: + matchDistance += 10; + break; + } + } else if (QScriptEnginePrivate::isRegExp(actual)) { + switch (tid) { + case QMetaType::QRegExp: + // perfect + break; + default: + matchDistance += 10; + break; + } + } else if (QScriptEnginePrivate::isVariant(actual)) { + if (argType.isVariant() + || (QScriptEnginePrivate::toVariant(exec, actual).userType() == tid)) { + // perfect + } else { + matchDistance += 10; + } + } else if (QScriptEnginePrivate::isArray(actual)) { + switch (tid) { + case QMetaType::QStringList: + case QMetaType::QVariantList: + matchDistance += 5; + break; + default: + matchDistance += 10; + break; + } + } else if (QScriptEnginePrivate::isQObject(actual)) { + switch (tid) { + case QMetaType::QObjectStar: + 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 + QString funName = QString::fromLatin1(methodName(initialMethodSignature, nameLength)); + if (!conversionFailed.isEmpty()) { + QString message = QString::fromLatin1("incompatible type of argument(s) in call to %0(); candidates were\n") + .arg(funName); + for (int i = 0; i < conversionFailed.size(); ++i) { + if (i > 0) + message += QLatin1String("\n"); + QMetaMethod mtd = metaMethod(meta, callType, conversionFailed.at(i)); + message += QString::fromLatin1(" %0").arg(QString::fromLatin1(mtd.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(funName); + if (unresolvedIndex > 0) { + message.append(QString::fromLatin1("argument %0 has unknown type `%1'"). + arg(unresolvedIndex).arg(unresolvedTypeName)); + } else { + message.append(QString::fromLatin1("unknown return type `%0'") + .arg(unresolvedTypeName)); + } + message.append(QString::fromLatin1(" (register the type with qScriptRegisterMetaType())")); + result = JSC::throwError(exec, JSC::TypeError, message); + } else { + QString message = QString::fromLatin1("too few arguments in call to %0(); candidates are\n") + .arg(funName); + for (int i = 0; i < tooFewArgs.size(); ++i) { + if (i > 0) + message += QLatin1String("\n"); + QMetaMethod mtd = metaMethod(meta, callType, tooFewArgs.at(i)); + message += QString::fromLatin1(" %0").arg(QString::fromLatin1(mtd.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 + QByteArray funName = methodName(initialMethodSignature, nameLength); + 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<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 { + QMetaObject::metacall(thisQObject, QMetaObject::InvokeMetaMethod, chosenIndex, params); + } + + if (scriptable) + QScriptablePrivate::get(scriptable)->engine = oldEngine; + + if (exec->hadException()) { + result = exec->exception() ; // propagate + } else { + QScriptMetaType retType = chosenMethod.returnType(); + if (retType.isVariant()) { + result = QScriptEnginePrivate::jscValueFromVariant(exec, *(QVariant *)params[0]); + } else if (retType.typeId() != 0) { + result = QScriptEnginePrivate::create(exec, retType.typeId(), params[0]); + if (!result) + result = engine->newVariant(QVariant(retType.typeId(), params[0])); + } else { + result = JSC::jsUndefined(); + } + } + } + } + + return result; +} + +JSC::JSValue QtFunction::execute(JSC::ExecState *exec, JSC::JSValue thisValue, + const JSC::ArgList &scriptArgs) +{ + Q_ASSERT(data->object.inherits(&QScriptObject::info)); + QScriptObject *scriptObject = static_cast<QScriptObject*>(JSC::asObject(data->object)); + QScriptObjectDelegate *delegate = scriptObject->delegate(); + Q_ASSERT(delegate && (delegate->type() == QScriptObjectDelegate::QtObject)); + QObject *qobj = static_cast<QScript::QObjectDelegate*>(delegate)->value(); + if (!qobj) + return JSC::throwError(exec, JSC::GeneralError, QString::fromLatin1("cannot call function of deleted QObject")); + QScriptEnginePrivate *engine = scriptEngineFromExec(exec); + + const QMetaObject *meta = qobj->metaObject(); + QObject *thisQObject = 0; + thisValue = engine->toUsableValue(thisValue); + if (thisValue.inherits(&QScriptObject::info)) { + delegate = static_cast<QScriptObject*>(JSC::asObject(thisValue))->delegate(); + if (delegate && (delegate->type() == QScriptObjectDelegate::QtObject)) + thisQObject = static_cast<QScript::QObjectDelegate*>(delegate)->value(); + } + if (!thisQObject) + thisQObject = qobj; // ### TypeError + + if (!meta->cast(thisQObject)) { + // invoking a function in the prototype + thisQObject = qobj; + } + + return callQtMethod(exec, QMetaMethod::Method, thisQObject, scriptArgs, + meta, data->initialIndex, data->maybeOverloaded); +} + +const JSC::ClassInfo QtFunction::info = { "QtFunction", &InternalFunction::info, 0, 0 }; + +JSC::JSValue JSC_HOST_CALL QtFunction::call(JSC::ExecState *exec, JSC::JSObject *callee, + JSC::JSValue thisValue, const JSC::ArgList &args) +{ + if (!callee->inherits(&QtFunction::info)) + return throwError(exec, JSC::TypeError, "callee is not a QtFunction object"); + QtFunction *qfun = static_cast<QtFunction*>(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<JSC::Structure> sid, + const JSC::Identifier &ident) + : JSC::InternalFunction(data, sid, ident), + data(new Data(meta, index)) +{ +} + +QtPropertyFunction::~QtPropertyFunction() +{ + delete data; +} + +JSC::CallType QtPropertyFunction::getCallData(JSC::CallData &callData) +{ + callData.native.function = call; + return JSC::CallTypeHost; +} + +JSC::JSValue JSC_HOST_CALL QtPropertyFunction::call( + JSC::ExecState *exec, JSC::JSObject *callee, + JSC::JSValue thisValue, const JSC::ArgList &args) +{ + if (!callee->inherits(&QtPropertyFunction::info)) + return throwError(exec, JSC::TypeError, "callee is not a QtPropertyFunction object"); + QtPropertyFunction *qfun = static_cast<QtPropertyFunction*>(callee); + return qfun->execute(exec, thisValue, args); +} + +JSC::JSValue QtPropertyFunction::execute(JSC::ExecState *exec, + JSC::JSValue thisValue, + const JSC::ArgList &args) +{ + JSC::JSValue result = JSC::jsUndefined(); + + QScriptEnginePrivate *engine = scriptEngineFromExec(exec); + JSC::ExecState *previousFrame = engine->currentFrame; + engine->currentFrame = exec; + + JSC::JSValue qobjectValue = engine->toUsableValue(thisValue); + QObject *qobject = QScriptEnginePrivate::toQObject(exec, qobjectValue); + while ((!qobject || (qobject->metaObject() != data->meta)) + && JSC::asObject(qobjectValue)->prototype().isObject()) { + qobjectValue = JSC::asObject(qobjectValue)->prototype(); + qobject = QScriptEnginePrivate::toQObject(exec, qobjectValue); + } + Q_ASSERT_X(qobject, Q_FUNC_INFO, "this-object must be a QObject"); + + QMetaProperty prop = data->meta->property(data->index); + Q_ASSERT(prop.isScriptable()); + if (args.size() == 0) { + // get + if (prop.isValid()) { + QScriptable *scriptable = scriptableFromQObject(qobject); + QScriptEngine *oldEngine = 0; + if (scriptable) { + engine->pushContext(exec, thisValue, args, this); + oldEngine = QScriptablePrivate::get(scriptable)->engine; + QScriptablePrivate::get(scriptable)->engine = QScriptEnginePrivate::get(engine); + } + + QVariant v = prop.read(qobject); + + if (scriptable) { + QScriptablePrivate::get(scriptable)->engine = oldEngine; + engine->popContext(); + } + + result = QScriptEnginePrivate::jscValueFromVariant(exec, v); + } + } else { + // set + JSC::JSValue arg = args.at(0); + QVariant v; + if (prop.isEnumType() && arg.isString() + && !engine->hasDemarshalFunction(prop.userType())) { + // give QMetaProperty::write() a chance to convert from + // string to enum value + v = (QString)arg.toString(exec); + } else { + v = variantFromValue(exec, prop.userType(), arg); + } + + QScriptable *scriptable = scriptableFromQObject(qobject); + QScriptEngine *oldEngine = 0; + if (scriptable) { + engine->pushContext(exec, thisValue, args, this); + oldEngine = QScriptablePrivate::get(scriptable)->engine; + QScriptablePrivate::get(scriptable)->engine = QScriptEnginePrivate::get(engine); + } + + prop.write(qobject, v); + + if (scriptable) { + QScriptablePrivate::get(scriptable)->engine = oldEngine; + engine->popContext(); + } + + result = arg; + } + engine->currentFrame = previousFrame; + return result; +} + +const QMetaObject *QtPropertyFunction::metaObject() const +{ + return data->meta; +} + +int QtPropertyFunction::propertyIndex() const +{ + return data->index; +} + + +QObjectDelegate::QObjectDelegate( + QObject *object, QScriptEngine::ValueOwnership ownership, + const QScriptEngine::QObjectWrapOptions &options) + : data(new Data(object, ownership, options)) +{ +} + +QObjectDelegate::~QObjectDelegate() +{ + switch (data->ownership) { + case QScriptEngine::QtOwnership: + break; + case QScriptEngine::ScriptOwnership: + if (data->value) + delete data->value; // ### fixme +// eng->disposeQObject(value); + break; + case QScriptEngine::AutoOwnership: + if (data->value && !data->value->parent()) + delete data->value; // ### fixme +// eng->disposeQObject(value); + break; + } + delete data; +} + +QScriptObjectDelegate::Type QObjectDelegate::type() const +{ + return QtObject; +} + +bool QObjectDelegate::getOwnPropertySlot(QScriptObject *object, JSC::ExecState *exec, + const JSC::Identifier &propertyName, + JSC::PropertySlot &slot) +{ + //Note: this has to be kept in sync with getOwnPropertyDescriptor +#ifndef QT_NO_PROPERTIES + QByteArray name = convertToLatin1(propertyName.ustring()); + QObject *qobject = data->value; + if (!qobject) { + QString message = QString::fromLatin1("cannot access member `%0' of deleted QObject") + .arg(QString::fromLatin1(name)); + slot.setValue(JSC::throwError(exec, JSC::GeneralError, message)); + return true; + } + + const QMetaObject *meta = qobject->metaObject(); + { + QHash<QByteArray, JSC::JSValue>::const_iterator it = data->cachedMembers.constFind(name); + if (it != data->cachedMembers.constEnd()) { + if (GeneratePropertyFunctions && (meta->indexOfProperty(name) != -1)) + slot.setGetterSlot(JSC::asObject(it.value())); + else + slot.setValue(it.value()); + return true; + } + } + + const QScriptEngine::QObjectWrapOptions &opt = data->options; + QScriptEnginePrivate *eng = scriptEngineFromExec(exec); + int index = -1; + if (name.contains('(')) { + QByteArray normalized = QMetaObject::normalizedSignature(name); + if (-1 != (index = meta->indexOfMethod(normalized))) { + QMetaMethod method = meta->method(index); + if (hasMethodAccess(method, index, opt)) { + if (!(opt & QScriptEngine::ExcludeSuperClassMethods) + || (index >= meta->methodOffset())) { + QtFunction *fun = new (exec)QtFunction( + object, index, /*maybeOverloaded=*/false, + &exec->globalData(), eng->originalGlobalObject()->functionStructure(), + propertyName); + slot.setValue(fun); + data->cachedMembers.insert(name, fun); + return true; + } + } + } + } + + index = meta->indexOfProperty(name); + if (index != -1) { + QMetaProperty prop = meta->property(index); + if (prop.isScriptable()) { + if (!(opt & QScriptEngine::ExcludeSuperClassProperties) + || (index >= meta->propertyOffset())) { + if (GeneratePropertyFunctions) { + QtPropertyFunction *fun = new (exec)QtPropertyFunction( + meta, index, &exec->globalData(), + eng->originalGlobalObject()->functionStructure(), + propertyName); + data->cachedMembers.insert(name, fun); + slot.setGetterSlot(fun); + } else { + JSC::JSValue val; + if (!prop.isValid()) + val = JSC::jsUndefined(); + else + val = QScriptEnginePrivate::jscValueFromVariant(exec, prop.read(qobject)); + slot.setValue(val); + } + return true; + } + } + } + + index = qobject->dynamicPropertyNames().indexOf(name); + if (index != -1) { + JSC::JSValue val = QScriptEnginePrivate::jscValueFromVariant(exec, qobject->property(name)); + slot.setValue(val); + return true; + } + + const int offset = (opt & QScriptEngine::ExcludeSuperClassMethods) + ? meta->methodOffset() : 0; + for (index = meta->methodCount() - 1; index >= offset; --index) { + QMetaMethod method = meta->method(index); + if (hasMethodAccess(method, index, opt) + && methodNameEquals(method, name.constData(), name.length())) { + 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<QObject*> children = qobject->children(); + for (index = 0; index < children.count(); ++index) { + QObject *child = children.at(index); + if (child->objectName() == QString(propertyName.ustring())) { + QScriptEngine::QObjectWrapOptions opt = QScriptEngine::PreferExistingWrapperObject; + slot.setValue(eng->newQObject(child, QScriptEngine::QtOwnership, opt)); + return true; + } + } + } + + return QScriptObjectDelegate::getOwnPropertySlot(object, exec, propertyName, slot); +#else //QT_NO_PROPERTIES + return false; +#endif //QT_NO_PROPERTIES +} + + +bool QObjectDelegate::getOwnPropertyDescriptor(QScriptObject *object, JSC::ExecState *exec, + const JSC::Identifier &propertyName, + JSC::PropertyDescriptor &descriptor) +{ + //Note: this has to be kept in sync with getOwnPropertySlot +#ifndef QT_NO_PROPERTIES + QByteArray name = convertToLatin1(propertyName.ustring()); + QObject *qobject = data->value; + if (!qobject) { + QString message = QString::fromLatin1("cannot access member `%0' of deleted QObject") + .arg(QString::fromLatin1(name)); + descriptor.setValue(JSC::throwError(exec, JSC::GeneralError, message)); + return true; + } + + const QScriptEngine::QObjectWrapOptions &opt = data->options; + + const QMetaObject *meta = qobject->metaObject(); + { + QHash<QByteArray, JSC::JSValue>::const_iterator it = data->cachedMembers.constFind(name); + if (it != data->cachedMembers.constEnd()) { + int index; + if (GeneratePropertyFunctions && ((index = meta->indexOfProperty(name)) != -1)) { + QMetaProperty prop = meta->property(index); + descriptor.setAccessorDescriptor(it.value(), it.value(), flagsForMetaProperty(prop)); + if (!prop.isWritable()) + descriptor.setWritable(false); + } else { + unsigned attributes = QObjectMemberAttribute; + if (opt & QScriptEngine::SkipMethodsInEnumeration) + attributes |= JSC::DontEnum; + descriptor.setDescriptor(it.value(), attributes); + } + return true; + } + } + + QScriptEnginePrivate *eng = scriptEngineFromExec(exec); + int index = -1; + if (name.contains('(')) { + QByteArray normalized = QMetaObject::normalizedSignature(name); + if (-1 != (index = meta->indexOfMethod(normalized))) { + QMetaMethod method = meta->method(index); + if (hasMethodAccess(method, index, opt)) { + if (!(opt & QScriptEngine::ExcludeSuperClassMethods) + || (index >= meta->methodOffset())) { + QtFunction *fun = new (exec)QtFunction( + object, index, /*maybeOverloaded=*/false, + &exec->globalData(), eng->originalGlobalObject()->functionStructure(), + propertyName); + data->cachedMembers.insert(name, fun); + unsigned attributes = QObjectMemberAttribute; + if (opt & QScriptEngine::SkipMethodsInEnumeration) + attributes |= JSC::DontEnum; + descriptor.setDescriptor(fun, attributes); + return true; + } + } + } + } + + index = meta->indexOfProperty(name); + if (index != -1) { + QMetaProperty prop = meta->property(index); + if (prop.isScriptable()) { + if (!(opt & QScriptEngine::ExcludeSuperClassProperties) + || (index >= meta->propertyOffset())) { + unsigned attributes = flagsForMetaProperty(prop); + if (GeneratePropertyFunctions) { + QtPropertyFunction *fun = new (exec)QtPropertyFunction( + meta, index, &exec->globalData(), + eng->originalGlobalObject()->functionStructure(), + propertyName); + data->cachedMembers.insert(name, fun); + descriptor.setAccessorDescriptor(fun, fun, attributes); + if (attributes & JSC::ReadOnly) + descriptor.setWritable(false); + } else { + JSC::JSValue val; + if (!prop.isValid()) + val = JSC::jsUndefined(); + else + val = QScriptEnginePrivate::jscValueFromVariant(exec, prop.read(qobject)); + descriptor.setDescriptor(val, attributes); + } + return true; + } + } + } + + index = qobject->dynamicPropertyNames().indexOf(name); + if (index != -1) { + JSC::JSValue val = QScriptEnginePrivate::jscValueFromVariant(exec, qobject->property(name)); + descriptor.setDescriptor(val, QObjectMemberAttribute); + return true; + } + + const int offset = (opt & QScriptEngine::ExcludeSuperClassMethods) + ? meta->methodOffset() : 0; + for (index = meta->methodCount() - 1; index >= offset; --index) { + QMetaMethod method = meta->method(index); + if (hasMethodAccess(method, index, opt) + && methodNameEquals(method, name.constData(), name.length())) { + QtFunction *fun = new (exec)QtFunction( + object, index, /*maybeOverloaded=*/true, + &exec->globalData(), eng->originalGlobalObject()->functionStructure(), + propertyName); + unsigned attributes = QObjectMemberAttribute; + if (opt & QScriptEngine::SkipMethodsInEnumeration) + attributes |= JSC::DontEnum; + descriptor.setDescriptor(fun, attributes); + data->cachedMembers.insert(name, fun); + return true; + } + } + + if (!(opt & QScriptEngine::ExcludeChildObjects)) { + QList<QObject*> children = qobject->children(); + for (index = 0; index < children.count(); ++index) { + QObject *child = children.at(index); + if (child->objectName() == QString(propertyName.ustring())) { + QScriptEngine::QObjectWrapOptions opt = QScriptEngine::PreferExistingWrapperObject; + descriptor.setDescriptor(eng->newQObject(child, QScriptEngine::QtOwnership, opt), + JSC::ReadOnly | JSC::DontDelete | JSC::DontEnum); + return true; + } + } + } + + return QScriptObjectDelegate::getOwnPropertyDescriptor(object, exec, propertyName, descriptor); +#else //QT_NO_PROPERTIES + return false; +#endif //QT_NO_PROPERTIES +} + +void QObjectDelegate::put(QScriptObject *object, JSC::ExecState* exec, + const JSC::Identifier& propertyName, + JSC::JSValue value, JSC::PutPropertySlot &slot) +{ +#ifndef QT_NO_PROPERTIES + QByteArray name = convertToLatin1(propertyName.ustring()); + QObject *qobject = data->value; + if (!qobject) { + QString message = QString::fromLatin1("cannot access member `%0' of deleted QObject") + .arg(QString::fromLatin1(name)); + JSC::throwError(exec, JSC::GeneralError, message); + return; + } + + const QScriptEngine::QObjectWrapOptions &opt = data->options; + const QMetaObject *meta = qobject->metaObject(); + QScriptEnginePrivate *eng = scriptEngineFromExec(exec); + int index = -1; + if (name.contains('(')) { + QByteArray normalized = QMetaObject::normalizedSignature(name); + if (-1 != (index = meta->indexOfMethod(normalized))) { + QMetaMethod method = meta->method(index); + if (hasMethodAccess(method, index, opt)) { + if (!(opt & QScriptEngine::ExcludeSuperClassMethods) + || (index >= meta->methodOffset())) { + data->cachedMembers.insert(name, value); + return; + } + } + } + } + + index = meta->indexOfProperty(name); + if (index != -1) { + QMetaProperty prop = meta->property(index); + if (prop.isScriptable()) { + if (!(opt & QScriptEngine::ExcludeSuperClassProperties) + || (index >= meta->propertyOffset())) { + if (GeneratePropertyFunctions) { + // ### ideally JSC would do this for us already, i.e. find out + // that the property is a setter and call the setter. + // Maybe QtPropertyFunction needs to inherit JSC::GetterSetter. + JSC::JSValue fun; + QHash<QByteArray, JSC::JSValue>::const_iterator it; + it = data->cachedMembers.constFind(name); + if (it != data->cachedMembers.constEnd()) { + fun = it.value(); + } else { + fun = new (exec)QtPropertyFunction( + meta, index, &exec->globalData(), + eng->originalGlobalObject()->functionStructure(), + propertyName); + data->cachedMembers.insert(name, fun); + } + JSC::CallData callData; + JSC::CallType callType = fun.getCallData(callData); + JSC::JSValue argv[1] = { value }; + JSC::ArgList args(argv, 1); + (void)JSC::call(exec, fun, callType, callData, object, args); + } else { + QVariant v; + if (prop.isEnumType() && value.isString() + && !eng->hasDemarshalFunction(prop.userType())) { + // give QMetaProperty::write() a chance to convert from + // string to enum value + v = (QString)value.toString(exec); + } else { + v = QScriptEnginePrivate::jscValueToVariant(exec, value, prop.userType()); + } + (void)prop.write(qobject, v); + } + return; + } + } + } + + const int offset = (opt & QScriptEngine::ExcludeSuperClassMethods) + ? meta->methodOffset() : 0; + for (index = meta->methodCount() - 1; index >= offset; --index) { + QMetaMethod method = meta->method(index); + if (hasMethodAccess(method, index, opt) + && methodNameEquals(method, name.constData(), name.length())) { + data->cachedMembers.insert(name, value); + return; + } + } + + index = qobject->dynamicPropertyNames().indexOf(name); + if ((index != -1) || (opt & QScriptEngine::AutoCreateDynamicProperties)) { + QVariant v = QScriptEnginePrivate::toVariant(exec, value); + (void)qobject->setProperty(name, v); + return; + } + + QScriptObjectDelegate::put(object, exec, propertyName, value, slot); +#endif //QT_NO_PROPERTIES +} + +bool QObjectDelegate::deleteProperty(QScriptObject *object, JSC::ExecState *exec, + const JSC::Identifier& propertyName) +{ +#ifndef QT_NO_PROPERTIES + QByteArray name = convertToLatin1(propertyName.ustring()); + QObject *qobject = data->value; + if (!qobject) { + QString message = QString::fromLatin1("cannot access member `%0' of deleted QObject") + .arg(QString::fromLatin1(name)); + JSC::throwError(exec, JSC::GeneralError, message); + return false; + } + + const QMetaObject *meta = qobject->metaObject(); + { + QHash<QByteArray, JSC::JSValue>::iterator it = data->cachedMembers.find(name); + if (it != data->cachedMembers.end()) { + if (GeneratePropertyFunctions && (meta->indexOfProperty(name) != -1)) + return false; + data->cachedMembers.erase(it); + return true; + } + } + + const QScriptEngine::QObjectWrapOptions &opt = data->options; + int index = meta->indexOfProperty(name); + if (index != -1) { + QMetaProperty prop = meta->property(index); + if (prop.isScriptable() && + (!(opt & QScriptEngine::ExcludeSuperClassProperties) + || (index >= meta->propertyOffset()))) { + return false; + } + } + + index = qobject->dynamicPropertyNames().indexOf(name); + if (index != -1) { + (void)qobject->setProperty(name, QVariant()); + return true; + } + + return QScriptObjectDelegate::deleteProperty(object, exec, propertyName); +#else //QT_NO_PROPERTIES + return false; +#endif //QT_NO_PROPERTIES +} + +void QObjectDelegate::getOwnPropertyNames(QScriptObject *object, JSC::ExecState *exec, + JSC::PropertyNameArray &propertyNames, + JSC::EnumerationMode mode) +{ +#ifndef QT_NO_PROPERTIES + QObject *qobject = data->value; + if (!qobject) { + QString message = QString::fromLatin1("cannot get property names of deleted QObject"); + JSC::throwError(exec, JSC::GeneralError, message); + return; + } + + const QScriptEngine::QObjectWrapOptions &opt = data->options; + const QMetaObject *meta = qobject->metaObject(); + { + int i = (opt & QScriptEngine::ExcludeSuperClassProperties) + ? meta->propertyOffset() : 0; + for ( ; i < meta->propertyCount(); ++i) { + QMetaProperty prop = meta->property(i); + if (isEnumerableMetaProperty(prop, meta, i)) { + QString name = QString::fromLatin1(prop.name()); + propertyNames.add(JSC::Identifier(exec, name)); + } + } + } + + { + QList<QByteArray> 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::getOwnPropertyNames(object, exec, propertyNames, mode); +#endif //QT_NO_PROPERTIES +} + +void QObjectDelegate::markChildren(QScriptObject *object, JSC::MarkStack& markStack) +{ + QHash<QByteArray, JSC::JSValue>::const_iterator it; + for (it = data->cachedMembers.constBegin(); it != data->cachedMembers.constEnd(); ++it) { + JSC::JSValue val = it.value(); + if (val) + markStack.append(val); + } + + QScriptObjectDelegate::markChildren(object, markStack); +} + +bool QObjectDelegate::compareToObject(QScriptObject *, JSC::ExecState *exec, JSC::JSObject *o2) +{ + if (!o2->inherits(&QScriptObject::info)) + return false; + QScriptObject *object = static_cast<QScriptObject*>(o2); + QScriptObjectDelegate *delegate = object->delegate(); + if (!delegate || (delegate->type() != QScriptObjectDelegate::QtObject)) + return false; + return value() == static_cast<QObjectDelegate *>(delegate)->value(); +} + +static JSC::JSValue JSC_HOST_CALL qobjectProtoFuncFindChild(JSC::ExecState *exec, JSC::JSObject*, + JSC::JSValue thisValue, const JSC::ArgList &args) +{ + QScriptEnginePrivate *engine = scriptEngineFromExec(exec); + thisValue = engine->toUsableValue(thisValue); + if (!thisValue.inherits(&QScriptObject::info)) + return throwError(exec, JSC::TypeError, "this object is not a QObject"); + QScriptObject *scriptObject = static_cast<QScriptObject*>(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<QObjectDelegate*>(delegate)->value(); + QString name; + if (args.size() != 0) + name = args.at(0).toString(exec); + QObject *child = obj->findChild<QObject*>(name); + QScriptEngine::QObjectWrapOptions opt = QScriptEngine::PreferExistingWrapperObject; + return engine->newQObject(child, QScriptEngine::QtOwnership, opt); +} + +static JSC::JSValue JSC_HOST_CALL qobjectProtoFuncFindChildren(JSC::ExecState *exec, JSC::JSObject*, + JSC::JSValue thisValue, const JSC::ArgList &args) +{ + QScriptEnginePrivate *engine = scriptEngineFromExec(exec); + thisValue = engine->toUsableValue(thisValue); + // extract the QObject + if (!thisValue.inherits(&QScriptObject::info)) + return throwError(exec, JSC::TypeError, "this object is not a QObject"); + QScriptObject *scriptObject = static_cast<QScriptObject*>(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<QObjectDelegate*>(delegate)->value(); + + // find the children + QList<QObject *> children; + if (args.size() != 0) { + const JSC::JSValue arg = args.at(0); + if (arg.inherits(&JSC::RegExpObject::info)) { + const QObjectList allChildren= obj->children(); + + JSC::RegExpObject *const regexp = JSC::asRegExpObject(arg); + + const int allChildrenCount = allChildren.size(); + for (int i = 0; i < allChildrenCount; ++i) { + QObject *const child = allChildren.at(i); + const JSC::UString childName = child->objectName(); + JSC::RegExpConstructor* regExpConstructor = engine->originalGlobalObject()->regExpConstructor(); + int position; + int length; + regExpConstructor->performMatch(regexp->regExp(), childName, 0, position, length); + if (position >= 0) + children.append(child); + } + } else { + const QString name(args.at(0).toString(exec)); + children = obj->findChildren<QObject*>(name); + } + } else { + children = obj->findChildren<QObject*>(QString()); + } + // create the result array with the children + const int length = children.size(); + JSC::JSArray *const result = JSC::constructEmptyArray(exec, length); + + QScriptEngine::QObjectWrapOptions opt = QScriptEngine::PreferExistingWrapperObject; + for (int i = 0; i < length; ++i) { + QObject *const child = children.at(i); + result->put(exec, i, engine->newQObject(child, QScriptEngine::QtOwnership, opt)); + } + return JSC::JSValue(result); +} + +static JSC::JSValue JSC_HOST_CALL qobjectProtoFuncToString(JSC::ExecState *exec, JSC::JSObject*, + JSC::JSValue thisValue, const JSC::ArgList&) +{ + QScriptEnginePrivate *engine = scriptEngineFromExec(exec); + thisValue = engine->toUsableValue(thisValue); + if (!thisValue.inherits(&QScriptObject::info)) + return JSC::jsUndefined(); + QScriptObject *scriptObject = static_cast<QScriptObject*>(JSC::asObject(thisValue)); + QScriptObjectDelegate *delegate = scriptObject->delegate(); + if (!delegate || (delegate->type() != QScriptObjectDelegate::QtObject)) + return JSC::jsUndefined(); + QObject *obj = static_cast<QObjectDelegate*>(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<JSC::Structure> structure, + JSC::Structure* prototypeFunctionStructure) + : QScriptObject(structure) +{ + setDelegate(new QObjectDelegate(new QObjectPrototypeObject(), QScriptEngine::AutoOwnership, + QScriptEngine::ExcludeSuperClassMethods + | QScriptEngine::ExcludeSuperClassProperties + | QScriptEngine::ExcludeChildObjects)); + + putDirectFunction(exec, new (exec) JSC::NativeFunctionWrapper(exec, prototypeFunctionStructure, /*length=*/0, exec->propertyNames().toString, qobjectProtoFuncToString), JSC::DontEnum); + putDirectFunction(exec, new (exec) JSC::NativeFunctionWrapper(exec, prototypeFunctionStructure, /*length=*/1, JSC::Identifier(exec, "findChild"), qobjectProtoFuncFindChild), JSC::DontEnum); + putDirectFunction(exec, new (exec) JSC::NativeFunctionWrapper(exec, prototypeFunctionStructure, /*length=*/1, JSC::Identifier(exec, "findChildren"), qobjectProtoFuncFindChildren), JSC::DontEnum); + this->structure()->setHasGetterSetterProperties(true); +} + +const JSC::ClassInfo QMetaObjectWrapperObject::info = { "QMetaObject", 0, 0, 0 }; + +QMetaObjectWrapperObject::QMetaObjectWrapperObject( + JSC::ExecState *exec, const QMetaObject *metaObject, JSC::JSValue ctor, + WTF::PassRefPtr<JSC::Structure> sid) + : JSC::JSObject(sid), + data(new Data(metaObject, ctor)) +{ + if (!ctor) + data->prototype = new (exec)JSC::JSObject(exec->lexicalGlobalObject()->emptyObjectStructure()); +} + +QMetaObjectWrapperObject::~QMetaObjectWrapperObject() +{ + delete data; +} + +bool QMetaObjectWrapperObject::getOwnPropertySlot( + JSC::ExecState *exec, const JSC::Identifier& propertyName, + JSC::PropertySlot &slot) +{ + const QMetaObject *meta = data->value; + if (!meta) + return false; + + if (propertyName == exec->propertyNames().prototype) { + if (data->ctor) + slot.setValue(data->ctor.get(exec, propertyName)); + else + slot.setValue(data->prototype); + return true; + } + + QByteArray name = convertToLatin1(propertyName.ustring()); + + for (int i = 0; i < meta->enumeratorCount(); ++i) { + QMetaEnum e = meta->enumerator(i); + for (int j = 0; j < e.keyCount(); ++j) { + const char *key = e.key(j); + if (!qstrcmp(key, name.constData())) { + slot.setValue(JSC::JSValue(exec, e.value(j))); + return true; + } + } + } + + return JSC::JSObject::getOwnPropertySlot(exec, propertyName, slot); +} + +bool QMetaObjectWrapperObject::getOwnPropertyDescriptor( + JSC::ExecState* exec, const JSC::Identifier& propertyName, + JSC::PropertyDescriptor& descriptor) +{ + const QMetaObject *meta = data->value; + if (!meta) + return false; + + if (propertyName == exec->propertyNames().prototype) { + descriptor.setDescriptor(data->ctor + ? data->ctor.get(exec, propertyName) + : data->prototype, + JSC::DontDelete | JSC::DontEnum); + return true; + } + + QByteArray name = QString(propertyName.ustring()).toLatin1(); + + for (int i = 0; i < meta->enumeratorCount(); ++i) { + QMetaEnum e = meta->enumerator(i); + for (int j = 0; j < e.keyCount(); ++j) { + const char *key = e.key(j); + if (!qstrcmp(key, name.constData())) { + descriptor.setDescriptor(JSC::JSValue(exec, e.value(j)), + JSC::ReadOnly | JSC::DontDelete); + return true; + } + } + } + + return JSC::JSObject::getOwnPropertyDescriptor(exec, propertyName, descriptor); +} + +void QMetaObjectWrapperObject::put(JSC::ExecState* exec, const JSC::Identifier& propertyName, + JSC::JSValue value, JSC::PutPropertySlot &slot) +{ + if (propertyName == exec->propertyNames().prototype) { + if (data->ctor) + data->ctor.put(exec, propertyName, value, slot); + else + data->prototype = value; + return; + } + const QMetaObject *meta = data->value; + if (meta) { + QByteArray name = convertToLatin1(propertyName.ustring()); + for (int i = 0; i < meta->enumeratorCount(); ++i) { + QMetaEnum e = meta->enumerator(i); + for (int j = 0; j < e.keyCount(); ++j) { + if (!qstrcmp(e.key(j), name.constData())) + return; + } + } + } + JSC::JSObject::put(exec, propertyName, value, slot); +} + +bool QMetaObjectWrapperObject::deleteProperty( + JSC::ExecState *exec, const JSC::Identifier& propertyName) +{ + if (propertyName == exec->propertyNames().prototype) + return false; + const QMetaObject *meta = data->value; + if (meta) { + QByteArray name = convertToLatin1(propertyName.ustring()); + for (int i = 0; i < meta->enumeratorCount(); ++i) { + QMetaEnum e = meta->enumerator(i); + for (int j = 0; j < e.keyCount(); ++j) { + if (!qstrcmp(e.key(j), name.constData())) + return false; + } + } + } + return JSC::JSObject::deleteProperty(exec, propertyName); +} + +void QMetaObjectWrapperObject::getOwnPropertyNames(JSC::ExecState *exec, + JSC::PropertyNameArray &propertyNames, + JSC::EnumerationMode mode) +{ + const QMetaObject *meta = data->value; + if (!meta) + return; + for (int i = 0; i < meta->enumeratorCount(); ++i) { + QMetaEnum e = meta->enumerator(i); + for (int j = 0; j < e.keyCount(); ++j) + propertyNames.add(JSC::Identifier(exec, e.key(j))); + } + JSC::JSObject::getOwnPropertyNames(exec, propertyNames, mode); +} + +void QMetaObjectWrapperObject::markChildren(JSC::MarkStack& markStack) +{ + if (data->ctor) + markStack.append(data->ctor); + if (data->prototype) + markStack.append(data->prototype); + JSC::JSObject::markChildren(markStack); +} + +JSC::CallType QMetaObjectWrapperObject::getCallData(JSC::CallData& callData) +{ + callData.native.function = call; + return JSC::CallTypeHost; +} + +JSC::ConstructType QMetaObjectWrapperObject::getConstructData(JSC::ConstructData& constructData) +{ + constructData.native.function = construct; + return JSC::ConstructTypeHost; +} + +JSC::JSValue JSC_HOST_CALL QMetaObjectWrapperObject::call( + JSC::ExecState *exec, JSC::JSObject *callee, + JSC::JSValue thisValue, const JSC::ArgList &args) +{ + QScriptEnginePrivate *eng_p = scriptEngineFromExec(exec); + thisValue = eng_p->toUsableValue(thisValue); + if (!callee->inherits(&QMetaObjectWrapperObject::info)) + return throwError(exec, JSC::TypeError, "callee is not a QMetaObject"); + QMetaObjectWrapperObject *self = static_cast<QMetaObjectWrapperObject*>(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<QMetaObjectWrapperObject*>(callee); + QScriptEnginePrivate *eng_p = scriptEngineFromExec(exec); + JSC::ExecState *previousFrame = eng_p->currentFrame; + eng_p->pushContext(exec, JSC::JSValue(), args, callee, true); + JSC::JSValue result = self->execute(eng_p->currentFrame, args); + eng_p->popContext(); + eng_p->currentFrame = previousFrame; + if (!result || !result.isObject()) + return 0; + return JSC::asObject(result); +} + +JSC::JSValue QMetaObjectWrapperObject::execute(JSC::ExecState *exec, + const JSC::ArgList &args) +{ + if (data->ctor) { + QScriptEnginePrivate *eng_p = QScript::scriptEngineFromExec(exec); + QScriptContext *ctx = eng_p->contextForFrame(exec); + JSC::CallData callData; + JSC::CallType callType = data->ctor.getCallData(callData); + Q_UNUSED(callType); + Q_ASSERT_X(callType == JSC::CallTypeHost, Q_FUNC_INFO, "script constructors not supported"); + if (data->ctor.inherits(&FunctionWithArgWrapper::info)) { + FunctionWithArgWrapper *wrapper = static_cast<FunctionWithArgWrapper*>(JSC::asObject(data->ctor)); + QScriptValue result = wrapper->function()(ctx, QScriptEnginePrivate::get(eng_p), wrapper->arg()); + return eng_p->scriptValueToJSCValue(result); + } else { + Q_ASSERT(data->ctor.inherits(&FunctionWrapper::info)); + FunctionWrapper *wrapper = static_cast<FunctionWrapper*>(JSC::asObject(data->ctor)); + QScriptValue result = wrapper->function()(ctx, QScriptEnginePrivate::get(eng_p)); + return eng_p->scriptValueToJSCValue(result); + } + } else { + const QMetaObject *meta = data->value; + if (meta->constructorCount() > 0) { + JSC::JSValue result = callQtMethod(exec, QMetaMethod::Constructor, /*thisQObject=*/0, + args, meta, meta->constructorCount()-1, /*maybeOverloaded=*/true); + if (!exec->hadException()) { + Q_ASSERT(result && result.inherits(&QScriptObject::info)); + QScriptObject *object = static_cast<QScriptObject*>(JSC::asObject(result)); + QScript::QObjectDelegate *delegate = static_cast<QScript::QObjectDelegate*>(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<StaticQtMetaObject*> (0)->staticQtMetaObject; } +}; + +static JSC::JSValue JSC_HOST_CALL qmetaobjectProtoFuncClassName( + JSC::ExecState *exec, JSC::JSObject*, JSC::JSValue thisValue, const JSC::ArgList&) +{ + QScriptEnginePrivate *engine = scriptEngineFromExec(exec); + thisValue = engine->toUsableValue(thisValue); + if (!thisValue.inherits(&QMetaObjectWrapperObject::info)) + return throwError(exec, JSC::TypeError, "this object is not a QMetaObject"); + const QMetaObject *meta = static_cast<QMetaObjectWrapperObject*>(JSC::asObject(thisValue))->value(); + return JSC::jsString(exec, meta->className()); +} + +QMetaObjectPrototype::QMetaObjectPrototype( + JSC::ExecState *exec, WTF::PassRefPtr<JSC::Structure> structure, + JSC::Structure* prototypeFunctionStructure) + : QMetaObjectWrapperObject(exec, StaticQtMetaObject::get(), /*ctor=*/JSC::JSValue(), structure) +{ + putDirectFunction(exec, new (exec) JSC::NativeFunctionWrapper(exec, prototypeFunctionStructure, /*length=*/0, JSC::Identifier(exec, "className"), qmetaobjectProtoFuncClassName), JSC::DontEnum); +} + +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; + QScript::APIShim shim(engine); + 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 && 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<QByteArray> parameterTypes = method.parameterTypes(); + int argc = parameterTypes.count(); + + JSC::ExecState *exec = engine->currentFrame; + QVarLengthArray<JSC::JSValue, 8> argsVector(argc); + for (int i = 0; i < argc; ++i) { + JSC::JSValue actual; + void *arg = argv[i + 1]; + QByteArray typeName = parameterTypes.at(i); + int argType = QMetaType::type(parameterTypes.at(i)); + if (!argType) { + qWarning("QScriptEngine: Unable to handle unregistered datatype '%s' " + "when invoking handler of signal %s::%s", + typeName.constData(), meta->className(), method.signature()); + actual = JSC::jsUndefined(); + } else if (argType == QMetaType::QVariant) { + actual = QScriptEnginePrivate::jscValueFromVariant(exec, *reinterpret_cast<QVariant*>(arg)); + } else { + actual = QScriptEnginePrivate::create(exec, argType, arg); + } + argsVector[i] = actual; + } + JSC::ArgList jscArgs(argsVector.data(), argsVector.size()); + + JSC::JSValue senderObject; + if (senderWrapper && senderWrapper.inherits(&QScriptObject::info)) // ### check if it's actually a QObject wrapper + senderObject = senderWrapper; + else { + QScriptEngine::QObjectWrapOptions opt = QScriptEngine::PreferExistingWrapperObject; + senderObject = engine->newQObject(sender(), QScriptEngine::QtOwnership, opt); + } + + JSC::JSValue thisObject; + if (receiver && receiver.isObject()) + thisObject = receiver; + else + thisObject = engine->globalObject(); + + JSC::CallData callData; + JSC::CallType callType = slot.getCallData(callData); + if (exec->hadException()) + exec->clearException(); // ### otherwise JSC asserts + JSC::call(exec, slot, callType, callData, thisObject, jscArgs); + + if (exec->hadException()) { + if (slot.inherits(&QtFunction::info) && !static_cast<QtFunction*>(JSC::asObject(slot))->qobject()) { + // The function threw an error because the target QObject has been deleted. + // The connections list is stale; remove the signal handler and ignore the exception. + removeSignalHandler(sender(), signalIndex, receiver, slot); + exec->clearException(); + } else { + engine->emitSignalHandlerException(); + } + } +} + +QObjectConnectionManager::QObjectConnectionManager(QScriptEnginePrivate *eng) + : engine(eng), slotCounter(0) +{ +} + +QObjectConnectionManager::~QObjectConnectionManager() +{ +} + +void QObjectConnectionManager::mark(JSC::MarkStack& markStack) +{ + for (int i = 0; i < connections.size(); ++i) { + QVector<QObjectConnection> &cs = connections[i]; + for (int j = 0; j < cs.size(); ++j) + cs[j].mark(markStack); + } +} + +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<QObjectConnection> &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<QObjectNotifyCaller*>(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<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); + QMetaMethod signal = sender->metaObject()->method(signalIndex); + QByteArray signalString; + signalString.append('2'); // signal code + signalString.append(signal.signature()); + static_cast<QScript::QObjectNotifyCaller*>(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(JSC::MarkStack& markStack) +{ + if (connectionManager) + connectionManager->mark(markStack); + { + QList<QScript::QObjectWrapperInfo>::iterator it; + for (it = wrappers.begin(); it != wrappers.end(); ) { + const QScript::QObjectWrapperInfo &info = *it; + // ### don't mark if there are no other references. + // we need something like isMarked() + markStack.append(info.object); + ++it; + } + } +} + +bool QObjectData::addSignalHandler(QObject *sender, + int signalIndex, + JSC::JSValue receiver, + JSC::JSValue slot, + JSC::JSValue senderWrapper, + Qt::ConnectionType type) +{ + if (!connectionManager) + connectionManager = new QObjectConnectionManager(engine); + return connectionManager->addSignalHandler( + sender, signalIndex, receiver, slot, senderWrapper, type); +} + +bool QObjectData::removeSignalHandler(QObject *sender, + int signalIndex, + JSC::JSValue receiver, + JSC::JSValue slot) +{ + if (!connectionManager) + return false; + return connectionManager->removeSignalHandler( + sender, signalIndex, receiver, slot); +} + +QScriptObject *QObjectData::findWrapper(QScriptEngine::ValueOwnership ownership, + const QScriptEngine::QObjectWrapOptions &options) const +{ + for (int i = 0; i < wrappers.size(); ++i) { + const QObjectWrapperInfo &info = wrappers.at(i); + if ((info.ownership == ownership) && (info.options == options)) + return info.object; + } + return 0; +} + +void QObjectData::registerWrapper(QScriptObject *wrapper, + QScriptEngine::ValueOwnership ownership, + const QScriptEngine::QObjectWrapOptions &options) +{ + wrappers.append(QObjectWrapperInfo(wrapper, ownership, options)); +} + +} // namespace QScript + +QT_END_NAMESPACE + +namespace JSC +{ + ASSERT_CLASS_FITS_IN_CELL(QScript::QtFunction); +} + +#include "moc_qscriptqobject_p.cpp" + diff --git a/src/script/bridge/qscriptqobject_p.h b/src/script/bridge/qscriptqobject_p.h new file mode 100644 index 0000000..7e62540 --- /dev/null +++ b/src/script/bridge/qscriptqobject_p.h @@ -0,0 +1,322 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtScript module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL-ONLY$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_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 "qscriptobject_p.h" + +#include "qscriptengine.h" +#include <QtCore/qpointer.h> + +#include "InternalFunction.h" + +QT_BEGIN_NAMESPACE + +namespace QScript +{ + +enum AttributeExtension { + // ### Make sure there's no conflict with JSC::Attribute + QObjectMemberAttribute = 1 << 12 +}; + +class QObjectDelegate : public QScriptObjectDelegate +{ +public: + struct Data + { + QPointer<QObject> value; + QScriptEngine::ValueOwnership ownership; + QScriptEngine::QObjectWrapOptions options; + + QHash<QByteArray, JSC::JSValue> cachedMembers; + + Data(QObject *o, QScriptEngine::ValueOwnership own, + QScriptEngine::QObjectWrapOptions opt) + : value(o), ownership(own), options(opt) {} + }; + + QObjectDelegate( + QObject *object, QScriptEngine::ValueOwnership ownership, + const QScriptEngine::QObjectWrapOptions &options); + ~QObjectDelegate(); + + virtual Type type() const; + + virtual bool getOwnPropertySlot(QScriptObject*, JSC::ExecState*, + const JSC::Identifier& propertyName, + JSC::PropertySlot&); + virtual bool getOwnPropertyDescriptor(QScriptObject*, JSC::ExecState*, + const JSC::Identifier& propertyName, + JSC::PropertyDescriptor&); + + virtual void put(QScriptObject*, JSC::ExecState* exec, + const JSC::Identifier& propertyName, + JSC::JSValue, JSC::PutPropertySlot&); + virtual bool deleteProperty(QScriptObject*, JSC::ExecState*, + const JSC::Identifier& propertyName); + virtual void getOwnPropertyNames(QScriptObject*, JSC::ExecState*, + JSC::PropertyNameArray&, + JSC::EnumerationMode mode = JSC::ExcludeDontEnumProperties); + virtual void markChildren(QScriptObject*, JSC::MarkStack& markStack); + virtual bool compareToObject(QScriptObject*, JSC::ExecState*, JSC::JSObject*); + + 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; } + +protected: + Data *data; +}; + +class QObjectPrototypeObject : public QObject +{ + Q_OBJECT +public: + QObjectPrototypeObject(QObject *parent = 0) + : QObject(parent) { } + ~QObjectPrototypeObject() { } +}; + +class QObjectPrototype : public QScriptObject +{ +public: + QObjectPrototype(JSC::ExecState*, WTF::PassRefPtr<JSC::Structure>, + JSC::Structure* prototypeFunctionStructure); +}; + +class QObjectConnectionManager; + +struct QObjectWrapperInfo +{ + QObjectWrapperInfo(QScriptObject *obj, + QScriptEngine::ValueOwnership own, + const QScriptEngine::QObjectWrapOptions &opt) + : object(obj), ownership(own), options(opt) {} + + QScriptObject *object; + QScriptEngine::ValueOwnership ownership; + QScriptEngine::QObjectWrapOptions options; +}; + +class QObjectData // : public QObjectUserData +{ +public: + QObjectData(QScriptEnginePrivate *engine); + ~QObjectData(); + + 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); + + QScriptObject *findWrapper(QScriptEngine::ValueOwnership ownership, + const QScriptEngine::QObjectWrapOptions &options) const; + void registerWrapper(QScriptObject *wrapper, + QScriptEngine::ValueOwnership ownership, + const QScriptEngine::QObjectWrapOptions &options); + + void mark(JSC::MarkStack&); + +private: + QScriptEnginePrivate *engine; + QScript::QObjectConnectionManager *connectionManager; + QList<QScript::QObjectWrapperInfo> wrappers; +}; + +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 markChildren(JSC::MarkStack&); + + virtual const JSC::ClassInfo* classInfo() const { return &info; } + static const JSC::ClassInfo info; + + static JSC::JSValue JSC_HOST_CALL call(JSC::ExecState*, JSC::JSObject*, + JSC::JSValue, const JSC::ArgList&); + + JSC::JSValue execute(JSC::ExecState *exec, JSC::JSValue thisValue, + const JSC::ArgList &args); + + QScriptObject *wrapperObject() const; + QObject *qobject() const; + const QMetaObject *metaObject() const; + int initialIndex() const; + bool maybeOverloaded() const; + int mostGeneralMethod(QMetaMethod *out = 0) const; + QList<int> overloadedIndexes() const; + +private: + Data *data; +}; + +class QtPropertyFunction: public JSC::InternalFunction +{ +public: + // work around CELL_SIZE limitation + struct Data + { + const QMetaObject *meta; + int index; + + Data(const QMetaObject *m, int i) + : meta(m), index(i) {} + }; + + QtPropertyFunction(const QMetaObject *meta, int index, + JSC::JSGlobalData*, WTF::PassRefPtr<JSC::Structure>, + const JSC::Identifier&); + virtual ~QtPropertyFunction(); + + virtual JSC::CallType getCallData(JSC::CallData&); + + virtual const JSC::ClassInfo* classInfo() const { return &info; } + static const JSC::ClassInfo info; + + static JSC::JSValue JSC_HOST_CALL call(JSC::ExecState*, JSC::JSObject*, + JSC::JSValue, const JSC::ArgList&); + + JSC::JSValue execute(JSC::ExecState *exec, JSC::JSValue thisValue, + const JSC::ArgList &args); + + const QMetaObject *metaObject() const; + int propertyIndex() const; + +private: + Data *data; +}; + +class QMetaObjectWrapperObject : public JSC::JSObject +{ +public: + // work around CELL_SIZE limitation + struct Data + { + const QMetaObject *value; + JSC::JSValue ctor; + JSC::JSValue prototype; + + Data(const QMetaObject *mo, JSC::JSValue c) + : value(mo), ctor(c) {} + }; + + explicit QMetaObjectWrapperObject( + JSC::ExecState *, const QMetaObject *metaobject, JSC::JSValue ctor, + WTF::PassRefPtr<JSC::Structure> sid); + ~QMetaObjectWrapperObject(); + + virtual bool getOwnPropertySlot(JSC::ExecState*, + const JSC::Identifier& propertyName, + JSC::PropertySlot&); + virtual bool getOwnPropertyDescriptor(JSC::ExecState*, + const JSC::Identifier& propertyName, + JSC::PropertyDescriptor&); + 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 getOwnPropertyNames(JSC::ExecState*, JSC::PropertyNameArray&, + JSC::EnumerationMode mode = JSC::ExcludeDontEnumProperties); + virtual void markChildren(JSC::MarkStack& markStack); + + virtual JSC::CallType getCallData(JSC::CallData&); + virtual JSC::ConstructType getConstructData(JSC::ConstructData&); + + virtual const JSC::ClassInfo* classInfo() const { return &info; } + static const JSC::ClassInfo info; + + static JSC::JSValue JSC_HOST_CALL call(JSC::ExecState*, JSC::JSObject*, + JSC::JSValue, const JSC::ArgList&); + static JSC::JSObject* construct(JSC::ExecState *, JSC::JSObject *, const JSC::ArgList &); + + JSC::JSValue execute(JSC::ExecState *exec, const JSC::ArgList &args); + + inline const QMetaObject *value() const { return data->value; } + inline void setValue(const QMetaObject* value) { data->value = value; } + + static WTF::PassRefPtr<JSC::Structure> createStructure(JSC::JSValue prototype) + { + return JSC::Structure::create(prototype, JSC::TypeInfo(JSC::ObjectType, StructureFlags)); + } + +protected: + static const unsigned StructureFlags = JSC::OverridesGetOwnPropertySlot | JSC::OverridesMarkChildren | JSC::OverridesGetPropertyNames | JSC::ImplementsHasInstance | JSObject::StructureFlags; + + Data *data; +}; + +class QMetaObjectPrototype : public QMetaObjectWrapperObject +{ +public: + QMetaObjectPrototype(JSC::ExecState*, WTF::PassRefPtr<JSC::Structure>, + JSC::Structure* prototypeFunctionStructure); +}; + +} // namespace QScript + +QT_END_NAMESPACE + +#endif diff --git a/src/script/bridge/qscriptstaticscopeobject.cpp b/src/script/bridge/qscriptstaticscopeobject.cpp new file mode 100644 index 0000000..1a2fa0f --- /dev/null +++ b/src/script/bridge/qscriptstaticscopeobject.cpp @@ -0,0 +1,157 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtScript module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL-ONLY$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "config.h" +#include "qscriptstaticscopeobject_p.h" + +namespace JSC +{ + ASSERT_CLASS_FITS_IN_CELL(QT_PREPEND_NAMESPACE(QScriptStaticScopeObject)); +} + +QT_BEGIN_NAMESPACE + +/*! + \class QScriptStaticScopeObject + \internal + + Represents a static scope object. + + This class allows the VM to determine at JS script compile time whether + the object has a given property or not. If the object has the property, + a fast, index-based read/write operation will be used. If the object + doesn't have the property, the compiler knows it can safely skip this + object when dynamically resolving the property. Either way, this can + greatly improve performance. + + \sa QScriptContext::pushScope() +*/ + +const JSC::ClassInfo QScriptStaticScopeObject::info = { "QScriptStaticScopeObject", 0, 0, 0 }; + +/*! + Creates a static scope object with a fixed set of undeletable properties. + + It's not possible to add new properties to the object after construction. +*/ +QScriptStaticScopeObject::QScriptStaticScopeObject(WTF::NonNullPassRefPtr<JSC::Structure> structure, + int propertyCount, const PropertyInfo* props) + : JSC::JSVariableObject(structure, new Data(/*canGrow=*/false)) +{ + int index = growRegisterArray(propertyCount); + for (int i = 0; i < propertyCount; ++i, --index) { + const PropertyInfo& prop = props[i]; + JSC::SymbolTableEntry entry(index, prop.attributes); + symbolTable().add(prop.identifier.ustring().rep(), entry); + registerAt(index) = prop.value; + } +} + +/*! + Creates an empty static scope object. + + Properties can be added to the object after construction, either by + calling QScriptValue::setProperty(), or by pushing the object on the + scope chain; variable declarations ("var" statements) and function + declarations in JavaScript will create properties on the scope object. + + Note that once the scope object has been used in a closure and the + resulting function has been compiled, it's no longer safe to add + properties to the scope object (because the VM will bypass this + object the next time the function is executed). +*/ +QScriptStaticScopeObject::QScriptStaticScopeObject(WTF::NonNullPassRefPtr<JSC::Structure> structure) + : JSC::JSVariableObject(structure, new Data(/*canGrow=*/true)) +{ +} + +QScriptStaticScopeObject::~QScriptStaticScopeObject() +{ + delete d_ptr(); +} + +bool QScriptStaticScopeObject::getOwnPropertySlot(JSC::ExecState*, const JSC::Identifier& propertyName, JSC::PropertySlot& slot) +{ + return symbolTableGet(propertyName, slot); +} + +bool QScriptStaticScopeObject::getOwnPropertyDescriptor(JSC::ExecState*, const JSC::Identifier& propertyName, JSC::PropertyDescriptor& descriptor) +{ + return symbolTableGet(propertyName, descriptor); +} + +void QScriptStaticScopeObject::putWithAttributes(JSC::ExecState* exec, const JSC::Identifier &propertyName, JSC::JSValue value, unsigned attributes) +{ + if (symbolTablePutWithAttributes(propertyName, value, attributes)) + return; + Q_ASSERT(d_ptr()->canGrow); + addSymbolTableProperty(propertyName, value, attributes); +} + +void QScriptStaticScopeObject::put(JSC::ExecState* exec, const JSC::Identifier& propertyName, JSC::JSValue value, JSC::PutPropertySlot&) +{ + if (symbolTablePut(propertyName, value)) + return; + Q_ASSERT(d_ptr()->canGrow); + addSymbolTableProperty(propertyName, value, /*attributes=*/0); +} + +bool QScriptStaticScopeObject::deleteProperty(JSC::ExecState*, const JSC::Identifier&) +{ + return false; +} + +void QScriptStaticScopeObject::markChildren(JSC::MarkStack& markStack) +{ + JSC::Register* registerArray = d_ptr()->registerArray.get(); + if (!registerArray) + return; + markStack.appendValues(reinterpret_cast<JSC::JSValue*>(registerArray), d_ptr()->registerArraySize); +} + +void QScriptStaticScopeObject::addSymbolTableProperty(const JSC::Identifier& name, JSC::JSValue value, unsigned attributes) +{ + int index = growRegisterArray(1); + JSC::SymbolTableEntry newEntry(index, attributes | JSC::DontDelete); + symbolTable().add(name.ustring().rep(), newEntry); + registerAt(index) = value; +} + +/*! + Grows the register array by \a count elements, and returns the offset of + the newly added elements (note that the register file grows downwards, + starting at index -1). +*/ +int QScriptStaticScopeObject::growRegisterArray(int count) +{ + size_t oldSize = d_ptr()->registerArraySize; + size_t newSize = oldSize + count; + JSC::Register* registerArray = new JSC::Register[newSize]; + if (d_ptr()->registerArray) + memcpy(registerArray + count, d_ptr()->registerArray.get(), oldSize * sizeof(JSC::Register)); + setRegisters(registerArray + newSize, registerArray); + d_ptr()->registerArraySize = newSize; + return -oldSize - 1; +} + +QT_END_NAMESPACE diff --git a/src/script/bridge/qscriptstaticscopeobject_p.h b/src/script/bridge/qscriptstaticscopeobject_p.h new file mode 100644 index 0000000..4b83692 --- /dev/null +++ b/src/script/bridge/qscriptstaticscopeobject_p.h @@ -0,0 +1,103 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtScript module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL-ONLY$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSCRIPTSTATICSCOPEOBJECT_P_H +#define QSCRIPTSTATICSCOPEOBJECT_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/qobjectdefs.h> + +#include "JSVariableObject.h" + +QT_BEGIN_NAMESPACE + +class QScriptStaticScopeObject : public JSC::JSVariableObject { +public: + struct PropertyInfo { + PropertyInfo(const JSC::Identifier& i, JSC::JSValue v, unsigned a) + : identifier(i), value(v), attributes(a) + { } + PropertyInfo() {} + + JSC::Identifier identifier; + JSC::JSValue value; + unsigned attributes; + }; + + QScriptStaticScopeObject(WTF::NonNullPassRefPtr<JSC::Structure> structure, + int propertyCount, const PropertyInfo*); + QScriptStaticScopeObject(WTF::NonNullPassRefPtr<JSC::Structure> structure); + virtual ~QScriptStaticScopeObject(); + + virtual bool isDynamicScope() const { return false; } + + virtual bool getOwnPropertySlot(JSC::ExecState*, const JSC::Identifier& propertyName, JSC::PropertySlot&); + virtual bool getOwnPropertyDescriptor(JSC::ExecState*, const JSC::Identifier& propertyName, JSC::PropertyDescriptor&); + + virtual void putWithAttributes(JSC::ExecState *exec, const JSC::Identifier &propertyName, JSC::JSValue value, unsigned attributes); + virtual void put(JSC::ExecState*, const JSC::Identifier& propertyName, JSC::JSValue value, JSC::PutPropertySlot&); + + virtual bool deleteProperty(JSC::ExecState*, const JSC::Identifier& propertyName); + + virtual void markChildren(JSC::MarkStack&); + + virtual const JSC::ClassInfo* classInfo() const { return &info; } + static const JSC::ClassInfo info; + + static WTF::PassRefPtr<JSC::Structure> createStructure(JSC::JSValue proto) { + return JSC::Structure::create(proto, JSC::TypeInfo(JSC::ObjectType, StructureFlags)); + } + +protected: + static const unsigned StructureFlags = JSC::OverridesGetOwnPropertySlot | JSC::NeedsThisConversion | JSC::OverridesMarkChildren | JSC::OverridesGetPropertyNames | JSC::JSVariableObject::StructureFlags; + + struct Data : public JSVariableObjectData { + Data(bool canGrow_) + : JSVariableObjectData(&symbolTable, /*registers=*/0), + canGrow(canGrow_), registerArraySize(0) + { } + bool canGrow; + int registerArraySize; + JSC::SymbolTable symbolTable; + }; + + Data* d_ptr() const { return static_cast<Data*>(JSVariableObject::d); } + +private: + void addSymbolTableProperty(const JSC::Identifier&, JSC::JSValue, unsigned attributes); + int growRegisterArray(int); +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/script/bridge/qscriptvariant.cpp b/src/script/bridge/qscriptvariant.cpp new file mode 100644 index 0000000..e083d89 --- /dev/null +++ b/src/script/bridge/qscriptvariant.cpp @@ -0,0 +1,151 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtScript module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL-ONLY$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "config.h" +#include "qscriptvariant_p.h" + +#include "../api/qscriptengine.h" +#include "../api/qscriptengine_p.h" + +#include "Error.h" +#include "PrototypeFunction.h" +#include "JSFunction.h" +#include "NativeFunctionWrapper.h" +#include "JSString.h" + +namespace JSC +{ +QT_USE_NAMESPACE +ASSERT_CLASS_FITS_IN_CELL(QScript::QVariantPrototype); +} + +QT_BEGIN_NAMESPACE + +namespace QScript +{ + +QVariantDelegate::QVariantDelegate(const QVariant &value) + : m_value(value) +{ +} + +QVariantDelegate::~QVariantDelegate() +{ +} + +QVariant &QVariantDelegate::value() +{ + return m_value; +} + +void QVariantDelegate::setValue(const QVariant &value) +{ + m_value = value; +} + +QScriptObjectDelegate::Type QVariantDelegate::type() const +{ + return Variant; +} + +static JSC::JSValue JSC_HOST_CALL variantProtoFuncValueOf(JSC::ExecState *exec, JSC::JSObject*, + JSC::JSValue thisValue, const JSC::ArgList&) +{ + QScriptEnginePrivate *engine = scriptEngineFromExec(exec); + thisValue = engine->toUsableValue(thisValue); + if (!thisValue.inherits(&QScriptObject::info)) + return throwError(exec, JSC::TypeError); + QScriptObjectDelegate *delegate = static_cast<QScriptObject*>(JSC::asObject(thisValue))->delegate(); + if (!delegate || (delegate->type() != QScriptObjectDelegate::Variant)) + return throwError(exec, JSC::TypeError); + const QVariant &v = static_cast<QVariantDelegate*>(delegate)->value(); + switch (v.type()) { + case QVariant::Invalid: + return JSC::jsUndefined(); + case QVariant::String: + return JSC::jsString(exec, 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; +} + +static JSC::JSValue JSC_HOST_CALL variantProtoFuncToString(JSC::ExecState *exec, JSC::JSObject *callee, + JSC::JSValue thisValue, const JSC::ArgList &args) +{ + QScriptEnginePrivate *engine = scriptEngineFromExec(exec); + thisValue = engine->toUsableValue(thisValue); + if (!thisValue.inherits(&QScriptObject::info)) + return throwError(exec, JSC::TypeError, "This object is not a QVariant"); + QScriptObjectDelegate *delegate = static_cast<QScriptObject*>(JSC::asObject(thisValue))->delegate(); + if (!delegate || (delegate->type() != QScriptObjectDelegate::Variant)) + return throwError(exec, JSC::TypeError, "This object is not a QVariant"); + const QVariant &v = static_cast<QVariantDelegate*>(delegate)->value(); + JSC::UString result; + JSC::JSValue value = variantProtoFuncValueOf(exec, callee, thisValue, args); + if (value.isObject()) { + result = v.toString(); + if (result.isEmpty() && !v.canConvert(QVariant::String)) + result = QString::fromLatin1("QVariant(%0)").arg(QString::fromLatin1(v.typeName())); + } else { + result = value.toString(exec); + } + return JSC::jsString(exec, result); +} + +bool QVariantDelegate::compareToObject(QScriptObject *, JSC::ExecState *exec, JSC::JSObject *o2) +{ + const QVariant &variant1 = value(); + return variant1 == QScriptEnginePrivate::toVariant(exec, o2); +} + +QVariantPrototype::QVariantPrototype(JSC::ExecState* exec, WTF::PassRefPtr<JSC::Structure> structure, + JSC::Structure* prototypeFunctionStructure) + : QScriptObject(structure) +{ + setDelegate(new QVariantDelegate(QVariant())); + + putDirectFunction(exec, new (exec) JSC::NativeFunctionWrapper(exec, prototypeFunctionStructure, 0, exec->propertyNames().toString, variantProtoFuncToString), JSC::DontEnum); + putDirectFunction(exec, new (exec) JSC::NativeFunctionWrapper(exec, prototypeFunctionStructure, 0, exec->propertyNames().valueOf, variantProtoFuncValueOf), JSC::DontEnum); +} + + +} // namespace QScript + +QT_END_NAMESPACE diff --git a/src/script/bridge/qscriptvariant_p.h b/src/script/bridge/qscriptvariant_p.h new file mode 100644 index 0000000..7469337 --- /dev/null +++ b/src/script/bridge/qscriptvariant_p.h @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtScript module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL-ONLY$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_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> + +#include "qscriptobject_p.h" + +QT_BEGIN_NAMESPACE + +namespace QScript +{ + +class QVariantDelegate : public QScriptObjectDelegate +{ +public: + QVariantDelegate(const QVariant &value); + ~QVariantDelegate(); + + virtual bool compareToObject(QScriptObject*, JSC::ExecState*, JSC::JSObject*); + + QVariant &value(); + void setValue(const QVariant &value); + + Type type() const; + +private: + QVariant m_value; +}; + +class QVariantPrototype : public QScriptObject +{ +public: + QVariantPrototype(JSC::ExecState*, WTF::PassRefPtr<JSC::Structure>, + JSC::Structure* prototypeFunctionStructure); +}; + +} // namespace QScript + +QT_END_NAMESPACE + +#endif |