diff options
Diffstat (limited to 'src/script/api')
33 files changed, 13379 insertions, 0 deletions
diff --git a/src/script/api/api.pri b/src/script/api/api.pri new file mode 100644 index 0000000..aebadd5 --- /dev/null +++ b/src/script/api/api.pri @@ -0,0 +1,35 @@ +SOURCES += \ + $$PWD/qscriptclass.cpp \ + $$PWD/qscriptclasspropertyiterator.cpp \ + $$PWD/qscriptcontext.cpp \ + $$PWD/qscriptcontextinfo.cpp \ + $$PWD/qscriptengine.cpp \ + $$PWD/qscriptengineagent.cpp \ + $$PWD/qscriptextensionplugin.cpp \ + $$PWD/qscriptprogram.cpp \ + $$PWD/qscriptstring.cpp \ + $$PWD/qscriptvalue.cpp \ + $$PWD/qscriptvalueiterator.cpp \ + $$PWD/qscriptable.cpp + +HEADERS += \ + $$PWD/qscriptclass.h \ + $$PWD/qscriptclasspropertyiterator.h \ + $$PWD/qscriptcontext.h \ + $$PWD/qscriptcontext_p.h \ + $$PWD/qscriptcontextinfo.h \ + $$PWD/qscriptengine.h \ + $$PWD/qscriptengine_p.h \ + $$PWD/qscriptengineagent.h \ + $$PWD/qscriptengineagent_p.h \ + $$PWD/qscriptextensioninterface.h \ + $$PWD/qscriptextensionplugin.h \ + $$PWD/qscriptprogram.h \ + $$PWD/qscriptprogram_p.h \ + $$PWD/qscriptstring.h \ + $$PWD/qscriptstring_p.h \ + $$PWD/qscriptvalue.h \ + $$PWD/qscriptvalue_p.h \ + $$PWD/qscriptvalueiterator.h \ + $$PWD/qscriptable.h \ + $$PWD/qscriptable_p.h diff --git a/src/script/api/qscriptable.cpp b/src/script/api/qscriptable.cpp new file mode 100644 index 0000000..1580cfa --- /dev/null +++ b/src/script/api/qscriptable.cpp @@ -0,0 +1,164 @@ +/**************************************************************************** +** +** 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 "qscriptable.h" +#include "qscriptable_p.h" +#include "qscriptengine.h" + +QT_BEGIN_NAMESPACE + +/*! + \since 4.3 + \class QScriptable + + \brief The QScriptable class provides access to the Qt Script environment from Qt C++ member functions. + + \ingroup script + + + With QScriptEngine::newQObject(), you can expose the signals and + slots and properties of any QObject (or subclass) to script + code. QScriptable augments this functionality by giving your C++ + members access to the Qt Script environment they are invoked in; + conceptually, it is similar to QObject::sender(). + + By subclassing QScriptable, you get the following functions in your + class: thisObject(), argumentCount(), argument(), context() and + engine(). With these functions, you have full access to the Qt + Script environment from the slots and property access functions of + your class, when they are invoked from script code. + + For example, you can throw a Qt Script exception from a slot; + manipulate the `this' object associated with the function call; + inspect the arguments stored in the QScriptContext to know the + "real" arguments passed to the function from script code; and call + script functions from your slot. + + A typical use case of QScriptable is to implement prototype objects + for custom C++ types. You define the scriptable interface of your + custom type in a QScriptable subclass using properties and slots; + then you wrap an instance of your class using + QScriptEngine::newQObject(), and finally pass the result to + QScriptEngine::setDefaultPrototype(). See the \l{Default Prototypes Example} + to see how this can be done. + + The following is what subclassing QScriptable typically looks + like: + + \snippet doc/src/snippets/code/src_script_qscriptable.cpp 0 + + The only difference from regular QObject subclassing is that you + also inherit from QScriptable. + + In the implementation of your slots, you can then use the functions + inherited from QScriptable: + + \snippet doc/src/snippets/code/src_script_qscriptable.cpp 1 + + \sa {Default Prototypes Example}, QScriptEngine::newFunction() +*/ + +/*! + \internal +*/ +QScriptable::QScriptable() + : d_ptr(new QScriptablePrivate()) +{ + d_ptr->q_ptr = this; +} + +/*! + \internal +*/ +QScriptable::~QScriptable() +{ +} + +/*! + Returns a pointer to the QScriptEngine associated with the current + Qt function call, or 0 if the Qt function was not invoked from + script code. +*/ +QScriptEngine *QScriptable::engine() const +{ + Q_D(const QScriptable); + return d->engine; +} + +/*! + Returns a pointer to the QScriptContext associated with the current + Qt function call, or 0 if the Qt function was not invoked from + script code. +*/ +QScriptContext *QScriptable::context() const +{ + if (QScriptEngine *e = engine()) + return e->currentContext(); + + return 0; +} + +/*! + Returns the `this' object associated with the current Qt function + call, or an invalid QScriptValue if the Qt function was not invoked + from script code. +*/ + +QScriptValue QScriptable::thisObject() const +{ + if (QScriptContext *c = context()) + return c->thisObject(); + + return QScriptValue(); +} + +/*! + Returns the number of arguments passed to the function in this + invocation, or -1 if the Qt function was not invoked from script + code. + + \sa argument() +*/ +int QScriptable::argumentCount() const +{ + if (QScriptContext *c = context()) + return c->argumentCount(); + + return -1; +} + +/*! + Returns the function argument at the given \a index, or an invalid + QScriptValue if the Qt function was not invoked from script code. + + \sa argumentCount() +*/ +QScriptValue QScriptable::argument(int index) const +{ + if (QScriptContext *c = context()) + return c->argument(index); + + return QScriptValue(); +} + +QT_END_NAMESPACE diff --git a/src/script/api/qscriptable.h b/src/script/api/qscriptable.h new file mode 100644 index 0000000..0460407 --- /dev/null +++ b/src/script/api/qscriptable.h @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** 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 QSCRIPTABLE_H +#define QSCRIPTABLE_H + +#include <QtCore/qobjectdefs.h> + +#include <QtCore/qscopedpointer.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Script) + +#ifndef QT_NO_QOBJECT + +class QScriptEngine; +class QScriptContext; +class QScriptValue; + +class QScriptablePrivate; + +class Q_SCRIPT_EXPORT QScriptable +{ +public: + QScriptable(); + ~QScriptable(); + + QScriptEngine *engine() const; + QScriptContext *context() const; + QScriptValue thisObject() const; + int argumentCount() const; + QScriptValue argument(int index) const; + +private: + QScopedPointer<QScriptablePrivate> d_ptr; + + Q_DISABLE_COPY(QScriptable) + Q_DECLARE_PRIVATE(QScriptable) +}; + +#endif // QT_NO_QOBJECT + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QSCRIPTABLE_H diff --git a/src/script/api/qscriptable_p.h b/src/script/api/qscriptable_p.h new file mode 100644 index 0000000..6c3665c --- /dev/null +++ b/src/script/api/qscriptable_p.h @@ -0,0 +1,61 @@ +/**************************************************************************** +** +** 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 QSCRIPTABLE_P_H +#define QSCRIPTABLE_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> + +QT_BEGIN_NAMESPACE + +class QScriptable; +class QScriptablePrivate +{ + Q_DECLARE_PUBLIC(QScriptable) +public: + inline QScriptablePrivate() + : engine(0) + { } + + static inline QScriptablePrivate *get(QScriptable *q) + { return q->d_func(); } + + QScriptEngine *engine; + + QScriptable *q_ptr; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/script/api/qscriptclass.cpp b/src/script/api/qscriptclass.cpp new file mode 100644 index 0000000..3674d4b --- /dev/null +++ b/src/script/api/qscriptclass.cpp @@ -0,0 +1,379 @@ +/**************************************************************************** +** +** 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 "qscriptclass.h" +#include "qscriptstring.h" + +/*! + \since 4.4 + \class QScriptClass + + \brief The QScriptClass class provides an interface for defining custom behavior of (a class of) Qt Script objects. + + \ingroup script + \mainclass + + The QScriptClass class defines an interface for handling various + aspects of interaction with the Qt Script objects associated with + the class. Such objects are created by calling + QScriptEngine::newObject(), passing a pointer to the QScriptClass as + argument. + + By subclassing QScriptClass, you can define precisely how access to + properties of the objects that use your class is handled. This + enables a fully dynamic handling of properties, e.g. it's more + powerful than QScriptEngine::newQObject(). For example, you can use + QScriptClass to implement array-type objects (i.e. objects that + handle the \c{length} property, and properties whose names are valid + array indexes, in a special way), or to implement a "live" + (runtime-defined) proxy to an underlying object. + + If you just need to handle access to a set of properties that are + known at the time an object is created (i.e. "semi-statically"), you + might consider using QScriptValue::setProperty() to define + getter/setter functions for the relevant properties, rather than + subclassing QScriptClass. + + Reimplement queryProperty() to specify which properties are handled + in a custom way by your script class (i.e. should be + \bold{delegated} to the QScriptClass), and which properties should + be handled just like normal Qt Script object properties. + + Reimplement property() and setProperty() to perform the actual + access (read or write) to the properties that your class + handles. Additionally, you can reimplement propertyFlags() to + specify custom flags for your properties. + + Reimplement newIterator() to provide an iterator for objects of your + custom class. This is only necessary if objects of your class can + have custom properties that you want to be reported when an object + is used together with the QScriptValueIterator class, or when an + object is used in a for-in enumeration statement in a script. + + When implementing custom classes of objects, you typically use + QScriptValue::setData() to store instance-specific data as part of + object initialization; the data won't be accessible from scripts + directly, but you can access it in e.g. your reimplementations of + property() and setProperty() (by calling QScriptValue::data()) to + perform custom processing. + + Reimplement prototype() to provide a custom prototype object for + your script class. + + Reimplement supportsExtension() and extension() if your custom + script class supports one or more of the extensions specified by the + Extension enum. + + \sa QScriptClassPropertyIterator, QScriptEngine::newObject(), {Custom Script Class Example} +*/ + +/*! + \enum QScriptClass::Extension + + This enum specifies the possible extensions to a QScriptClass. + + \value Callable Instances of this class can be called as functions. + + \value HasInstance Instances of this class implement [[HasInstance]]. + + \sa extension() +*/ + +/*! + \enum QScriptClass::QueryFlag + + This enum describes flags that are used to query a QScriptClass + regarding how access to a property should be handled. + + \value HandlesReadAccess The QScriptClass handles read access to this property. + \value HandlesWriteAccess The QScriptClass handles write access to this property. + + \sa queryProperty() +*/ + +QT_BEGIN_NAMESPACE + +class QScriptClassPrivate +{ + Q_DECLARE_PUBLIC(QScriptClass) +public: + QScriptClassPrivate() {} + virtual ~QScriptClassPrivate() {} + + QScriptEngine *engine; + + QScriptClass *q_ptr; +}; + +/*! + Constructs a QScriptClass object to be used in the given \a engine. + + The engine does not take ownership of the QScriptClass object. +*/ +QScriptClass::QScriptClass(QScriptEngine *engine) + : d_ptr(new QScriptClassPrivate) +{ + d_ptr->q_ptr = this; + d_ptr->engine = engine; +} + +/*! + \internal +*/ +QScriptClass::QScriptClass(QScriptEngine *engine, QScriptClassPrivate &dd) + : d_ptr(&dd) +{ + d_ptr->q_ptr = this; + d_ptr->engine = engine; +} + +/*! + Destroys the QScriptClass object. + + If a QScriptClass object is deleted before the associated engine(), + any Qt Script objects using the QScriptClass will be "demoted" to + normal Qt Script objects. +*/ +QScriptClass::~QScriptClass() +{ +} + +/*! + Returns the engine that this QScriptClass is associated with. +*/ +QScriptEngine *QScriptClass::engine() const +{ + Q_D(const QScriptClass); + return d->engine; +} + +/*! + Returns the object to be used as the prototype of new instances + of this class (created with QScriptEngine::newObject()). + + The default implementation returns an invalid QScriptValue, meaning + that the standard Object prototype will be used. Reimplement this + function to provide your own custom prototype. + + Typically you initialize your prototype object in the constructor of + your class, then return it in this function. + + See the "Making Use of Prototype-Based Inheritance" section in the + QtScript documentation for more information on how prototypes are + used. +*/ +QScriptValue QScriptClass::prototype() const +{ + return QScriptValue(); +} + +/*! + Returns the name of the script class. + + Qt Script uses this name to generate a default string representation + of objects in case you do not provide a toString function. + + The default implementation returns a null string. +*/ +QString QScriptClass::name() const +{ + return QString(); +} + +/*! + Queries this script class for how access to the property with the + given \a name of the given \a object should be handled. The given \a + flags specify the aspects of interest. This function should return a + subset of \a flags to indicate which aspects of property access + should be further handled by the script class. + + For example, if the \a flags contain HandlesReadAccess, and you + would like your class to handle the reading of the property (through + the property() function), the returned flags should include + HandlesReadAccess. If the returned flags do not contain + HandlesReadAccess, the property will be handled as a normal script + object property. + + You can optionally use the \a id argument to store a value that will + subsequently be passed on to functions such as property() and + setProperty(). + + The default implementation of this function returns 0. + + Note: This function is only called if the given property isn't + already a normal property of the object. For example, say you + advertise that you want to handle read access to property \c{foo}, + but not write access; if \c{foo} is then assigned a value, it will + become a normal script object property, and subsequently you will no + longer be queried regarding read access to \c{foo}. + + \sa property() +*/ +QScriptClass::QueryFlags QScriptClass::queryProperty( + const QScriptValue &object, const QScriptString &name, + QueryFlags flags, uint *id) +{ + Q_UNUSED(object); + Q_UNUSED(name); + Q_UNUSED(flags); + Q_UNUSED(id); + return 0; +} + +/*! + Returns the value of the property with the given \a name of the given + \a object. + + The \a id argument is only useful if you assigned a value to it in + queryProperty(). + + The default implementation does nothing and returns an invalid QScriptValue. + + \sa setProperty(), propertyFlags() +*/ +QScriptValue QScriptClass::property(const QScriptValue &object, + const QScriptString &name, uint id) +{ + Q_UNUSED(object); + Q_UNUSED(name); + Q_UNUSED(id); + return QScriptValue(); +} + +/*! + Returns the flags of the property with the given \a name of the given + \a object. + + The \a id argument is only useful if you assigned a value to it in + queryProperty(). + + The default implementation returns 0. + + \sa property() +*/ +QScriptValue::PropertyFlags QScriptClass::propertyFlags( + const QScriptValue &object, const QScriptString &name, uint id) +{ + Q_UNUSED(object); + Q_UNUSED(name); + Q_UNUSED(id); + return 0; +} + +/*! + Sets the property with the given \a name of the given \a object to + the given \a value. + + The \a id argument is only useful if you assigned a value to it in + queryProperty(). + + The default implementation does nothing. + + An invalid \a value represents a request to remove the property. + + \sa property() +*/ +void QScriptClass::setProperty(QScriptValue &object, const QScriptString &name, + uint id, const QScriptValue &value) +{ + Q_UNUSED(object); + Q_UNUSED(name); + Q_UNUSED(id); + Q_UNUSED(value); +} + +/*! + Returns an iterator for traversing custom properties of the given \a + object. + + The default implementation returns 0, meaning that there are no + custom properties to traverse. + + Reimplement this function if objects of your script class can have + one or more custom properties (e.g. those reported to be handled by + queryProperty()) that you want to appear when an object's properties + are enumerated (e.g. by a for-in statement in a script). + + Qt Script takes ownership of the new iterator object. + + \sa QScriptValueIterator +*/ +QScriptClassPropertyIterator *QScriptClass::newIterator(const QScriptValue &object) +{ + Q_UNUSED(object); + return 0; +} + +/*! + Returns true if the QScriptClass supports the given \a extension; + otherwise, false is returned. By default, no extensions + are supported. + + Reimplement this function to indicate which extensions your custom + class supports. + + \sa extension() +*/ +bool QScriptClass::supportsExtension(Extension extension) const +{ + Q_UNUSED(extension); + return false; +} + +/*! + This virtual function can be reimplemented in a QScriptClass + subclass to provide support for extensions. The optional \a argument + can be provided as input to the \a extension; the result must be + returned in the form of a QVariant. You can call supportsExtension() + to check if an extension is supported by the QScriptClass. By + default, no extensions are supported, and this function returns an + invalid QVariant. + + If you implement the Callable extension, Qt Script will call this + function when an instance of your class is called as a function + (e.g. from a script or using QScriptValue::call()). The \a argument + will contain a pointer to the QScriptContext that represents the + function call, and you should return a QVariant that holds the + result of the function call. In the following example the sum of the + arguments to the script function are added up and returned: + + \snippet doc/src/snippets/code/src_script_qscriptclass.cpp 0 + + If you implement the HasInstance extension, Qt Script will call this + function as part of evaluating the \c{instanceof} operator, as + described in ECMA-262 Section 11.8.6. The \a argument is a + QScriptValueList containing two items: The first item is the object + that HasInstance is being applied to (an instance of your class), + and the second item can be any value. extension() should return true + if the value delegates behavior to the object, false otherwise. + + \sa supportsExtension() +*/ +QVariant QScriptClass::extension(Extension extension, const QVariant &argument) +{ + Q_UNUSED(extension); + Q_UNUSED(argument); + return QVariant(); +} + +QT_END_NAMESPACE diff --git a/src/script/api/qscriptclass.h b/src/script/api/qscriptclass.h new file mode 100644 index 0000000..2155c38 --- /dev/null +++ b/src/script/api/qscriptclass.h @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** 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 QSCRIPTCLASS_H +#define QSCRIPTCLASS_H + +#include <QtCore/qstring.h> + +#include <QtCore/qvariant.h> +#include <QtCore/qscopedpointer.h> +#include <QtScript/qscriptvalue.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Script) + +class QScriptString; +class QScriptClassPropertyIterator; + +class QScriptClassPrivate; +class Q_SCRIPT_EXPORT QScriptClass +{ +public: + enum QueryFlag { + HandlesReadAccess = 0x01, + HandlesWriteAccess = 0x02 + }; + Q_DECLARE_FLAGS(QueryFlags, QueryFlag) + + enum Extension { + Callable, + HasInstance + }; + + QScriptClass(QScriptEngine *engine); + virtual ~QScriptClass(); + + QScriptEngine *engine() const; + + virtual QueryFlags queryProperty(const QScriptValue &object, + const QScriptString &name, + QueryFlags flags, uint *id); + + virtual QScriptValue property(const QScriptValue &object, + const QScriptString &name, uint id); + + virtual void setProperty(QScriptValue &object, const QScriptString &name, + uint id, const QScriptValue &value); + + virtual QScriptValue::PropertyFlags propertyFlags( + const QScriptValue &object, const QScriptString &name, uint id); + + virtual QScriptClassPropertyIterator *newIterator(const QScriptValue &object); + + virtual QScriptValue prototype() const; + + virtual QString name() const; + + virtual bool supportsExtension(Extension extension) const; + virtual QVariant extension(Extension extension, + const QVariant &argument = QVariant()); + +protected: + QScriptClass(QScriptEngine *engine, QScriptClassPrivate &dd); + QScopedPointer<QScriptClassPrivate> d_ptr; + +private: + Q_DECLARE_PRIVATE(QScriptClass) + Q_DISABLE_COPY(QScriptClass) +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QScriptClass::QueryFlags) + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/script/api/qscriptclasspropertyiterator.cpp b/src/script/api/qscriptclasspropertyiterator.cpp new file mode 100644 index 0000000..ba70fff --- /dev/null +++ b/src/script/api/qscriptclasspropertyiterator.cpp @@ -0,0 +1,204 @@ +/**************************************************************************** +** +** 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 "qscriptclasspropertyiterator.h" + +#include "qscriptstring.h" + +QT_BEGIN_NAMESPACE + +/*! + \since 4.4 + \class QScriptClassPropertyIterator + + \brief The QScriptClassPropertyIterator class provides an iterator interface for custom Qt Script objects. + + \ingroup script + + This class is only relevant if you have subclassed QScriptClass and + want to provide enumeration of your custom properties (e.g. when + objects of your class are used with QScriptValueIterator, or with + the for-in statement in scripts). + + The object() function returns the Qt Script object the iterator is + traversing. + + toFront(), hasNext() and next() provide forward iteration. + + toBack(), hasPrevious() and previous() provide backward iteration. + + name(), id() and flags() return information about the last property + that was jumped over using next() or previous(). + + \sa QScriptClass::newIterator(), QScriptValueIterator +*/ + +class QScriptClassPropertyIteratorPrivate +{ + Q_DECLARE_PUBLIC(QScriptClassPropertyIterator) +public: + QScriptClassPropertyIteratorPrivate() {} + virtual ~QScriptClassPropertyIteratorPrivate() {} + + QScriptValue object; + + QScriptClassPropertyIterator *q_ptr; +}; + +/*! + Constructs an iterator for traversing \a object. + + Subclasses should ensure that the iterator is set to the front of the + sequence of properties (before the first property). +*/ +QScriptClassPropertyIterator::QScriptClassPropertyIterator(const QScriptValue &object) + : d_ptr(new QScriptClassPropertyIteratorPrivate) +{ + d_ptr->q_ptr = this; + d_ptr->object = object; +} + +/*! + \internal +*/ +QScriptClassPropertyIterator::QScriptClassPropertyIterator(const QScriptValue &object, + QScriptClassPropertyIteratorPrivate &dd) + : d_ptr(&dd) +{ + d_ptr->q_ptr = this; + d_ptr->object = object; +} + +/*! + Destroys the iterator. +*/ +QScriptClassPropertyIterator::~QScriptClassPropertyIterator() +{ +} + +/*! + Returns the Qt Script object this iterator is traversing. +*/ +QScriptValue QScriptClassPropertyIterator::object() const +{ + Q_D(const QScriptClassPropertyIterator); + return d->object; +} + +/*! + \fn bool QScriptClassPropertyIterator::hasNext() const + + Returns true if there is at least one item ahead of the iterator + (i.e. the iterator is \e not at the back of the property sequence); + otherwise returns false. + + \sa next(), hasPrevious() +*/ + +/*! + \fn void QScriptClassPropertyIterator::next() + + Advances the iterator by one position. + + Calling this function on an iterator located at the back of the + container leads to undefined results. + + \sa hasNext(), previous(), name() +*/ + +/*! + \fn bool QScriptClassPropertyIterator::hasPrevious() const + + Returns true if there is at least one item behind the iterator + (i.e. the iterator is \e not at the front of the property sequence); + otherwise returns false. + + \sa previous(), hasNext() +*/ + +/*! + \fn void QScriptClassPropertyIterator::previous() + + Moves the iterator back by one position. + + Calling this function on an iterator located at the front of the + container leads to undefined results. + + \sa hasPrevious(), next(), name() +*/ + +/*! + \fn void QScriptClassPropertyIterator::toFront() + + Moves the iterator to the front of the QScriptValue (before the + first property). + + \sa toBack(), next() +*/ + +/*! + \fn void QScriptClassPropertyIterator::toBack() + + Moves the iterator to the back of the QScriptValue (after the + last property). + + \sa toFront(), previous() +*/ + +/*! + \fn QScriptString QScriptClassPropertyIterator::name() const + + Returns the name of the last property that was jumped over using + next() or previous(). + + \sa id() +*/ + +/*! + \fn uint QScriptClassPropertyIterator::id() const + + Returns the id of the last property that was jumped over using + next() or previous(). + + The default implementation returns 0. + + \sa name() +*/ +uint QScriptClassPropertyIterator::id() const +{ + return 0; +} + +/*! + Returns the flags of the last property that was jumped over using + next() or previous(). + + The default implementation calls the propertyFlags() function of + object() with argument name(). +*/ +QScriptValue::PropertyFlags QScriptClassPropertyIterator::flags() const +{ + return object().propertyFlags(name()); +} + +QT_END_NAMESPACE diff --git a/src/script/api/qscriptclasspropertyiterator.h b/src/script/api/qscriptclasspropertyiterator.h new file mode 100644 index 0000000..a954a4d --- /dev/null +++ b/src/script/api/qscriptclasspropertyiterator.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 QSCRIPTCLASSPROPERTYITERATOR_H +#define QSCRIPTCLASSPROPERTYITERATOR_H + +#include <QtCore/qstring.h> + +#include <QtCore/qscopedpointer.h> +#include <QtScript/qscriptvalue.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Script) + +class QScriptClassPropertyIteratorPrivate; +class Q_SCRIPT_EXPORT QScriptClassPropertyIterator +{ +protected: + QScriptClassPropertyIterator(const QScriptValue &object); + +public: + virtual ~QScriptClassPropertyIterator(); + + QScriptValue object() const; + + virtual bool hasNext() const = 0; + virtual void next() = 0; + + virtual bool hasPrevious() const = 0; + virtual void previous() = 0; + + virtual void toFront() = 0; + virtual void toBack() = 0; + + virtual QScriptString name() const = 0; + virtual uint id() const; + virtual QScriptValue::PropertyFlags flags() const; + +protected: + QScriptClassPropertyIterator(const QScriptValue &object, QScriptClassPropertyIteratorPrivate &dd); + QScopedPointer<QScriptClassPropertyIteratorPrivate> d_ptr; + +private: + Q_DECLARE_PRIVATE(QScriptClassPropertyIterator) + Q_DISABLE_COPY(QScriptClassPropertyIterator) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/script/api/qscriptcontext.cpp b/src/script/api/qscriptcontext.cpp new file mode 100644 index 0000000..5454df5 --- /dev/null +++ b/src/script/api/qscriptcontext.cpp @@ -0,0 +1,784 @@ +/**************************************************************************** +** +** 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 "qscriptcontext.h" + +#include "qscriptcontext_p.h" +#include "qscriptcontextinfo.h" +#include "qscriptengine.h" +#include "qscriptengine_p.h" +#include "../bridge/qscriptactivationobject_p.h" + +#include "Arguments.h" +#include "CodeBlock.h" +#include "Error.h" +#include "JSFunction.h" +#include "JSObject.h" +#include "JSGlobalObject.h" + +#include <QtCore/qstringlist.h> + +QT_BEGIN_NAMESPACE + +/*! + \since 4.3 + \class QScriptContext + + \brief The QScriptContext class represents a Qt Script function invocation. + + \ingroup script + \mainclass + + A QScriptContext provides access to the `this' object and arguments + passed to a script function. You typically want to access this + information when you're writing a native (C++) function (see + QScriptEngine::newFunction()) that will be called from script + code. For example, when the script code + + \snippet doc/src/snippets/code/src_script_qscriptcontext.cpp 0 + + is evaluated, a QScriptContext will be created, and the context will + carry the arguments as QScriptValues; in this particular case, the + arguments will be one QScriptValue containing the number 20.5, a second + QScriptValue containing the string \c{"hello"}, and a third QScriptValue + containing a Qt Script object. + + Use argumentCount() to get the number of arguments passed to the + function, and argument() to get an argument at a certain index. The + argumentsObject() function returns a Qt Script array object + containing all the arguments; you can use the QScriptValueIterator + to iterate over its elements, or pass the array on as arguments to + another script function using QScriptValue::call(). + + Use thisObject() to get the `this' object associated with the function call, + and setThisObject() to set the `this' object. If you are implementing a + native "instance method", you typically fetch the thisObject() and access + one or more of its properties: + + \snippet doc/src/snippets/code/src_script_qscriptcontext.cpp 1 + + Use isCalledAsConstructor() to determine if the function was called + as a constructor (e.g. \c{"new foo()"} (as constructor) or just + \c{"foo()"}). When a function is called as a constructor, the + thisObject() contains the newly constructed object that the function + is expected to initialize. + + Use throwValue() or throwError() to throw an exception. + + Use callee() to obtain the QScriptValue that represents the function being + called. This can for example be used to call the function recursively. + + Use parentContext() to get a pointer to the context that precedes + this context in the activation stack. This is mostly useful for + debugging purposes (e.g. when constructing some form of backtrace). + + The activationObject() function returns the object that is used to + hold the local variables associated with this function call. You can + replace the activation object by calling setActivationObject(). A + typical usage of these functions is when you want script code to be + evaluated in the context of the parent context, e.g. to implement an + include() function: + + \snippet doc/src/snippets/code/src_script_qscriptcontext.cpp 2 + + Use backtrace() to get a human-readable backtrace associated with + this context. This can be useful for debugging purposes when + implementing native functions. The toString() function provides a + string representation of the context. (QScriptContextInfo provides + more detailed debugging-related information about the + QScriptContext.) + + Use engine() to obtain a pointer to the QScriptEngine that this context + resides in. + + \sa QScriptContextInfo, QScriptEngine::newFunction(), QScriptable +*/ + +/*! + \enum QScriptContext::ExecutionState + + This enum specifies the frameution state of the context. + + \value NormalState The context is in a normal state. + + \value ExceptionState The context is in an exceptional state. +*/ + +/*! + \enum QScriptContext::Error + + This enum specifies types of error. + + \value ReferenceError A reference error. + + \value SyntaxError A syntax error. + + \value TypeError A type error. + + \value RangeError A range error. + + \value URIError A URI error. + + \value UnknownError An unknown error. +*/ + +/*! + \internal +*/ +QScriptContext::QScriptContext() +{ + //QScriptContext doesn't exist, pointer to QScriptContext are just pointer to JSC::CallFrame + Q_ASSERT(false); +} + +/*! + Throws an exception with the given \a value. + Returns the value thrown (the same as the argument). + + \sa throwError(), state() +*/ +QScriptValue QScriptContext::throwValue(const QScriptValue &value) +{ + JSC::CallFrame *frame = QScriptEnginePrivate::frameForContext(this); + QScript::APIShim shim(QScript::scriptEngineFromExec(frame)); + JSC::JSValue jscValue = QScript::scriptEngineFromExec(frame)->scriptValueToJSCValue(value); + frame->setException(jscValue); + return value; +} + +/*! + Throws an \a error with the given \a text. + Returns the created error object. + + The \a text will be stored in the \c{message} property of the error + object. + + The error object will be initialized to contain information about + the location where the error occurred; specifically, it will have + properties \c{lineNumber}, \c{fileName} and \c{stack}. These + properties are described in \l {QtScript Extensions to ECMAScript}. + + \sa throwValue(), state() +*/ +QScriptValue QScriptContext::throwError(Error error, const QString &text) +{ + JSC::CallFrame *frame = QScriptEnginePrivate::frameForContext(this); + QScript::APIShim shim(QScript::scriptEngineFromExec(frame)); + JSC::ErrorType jscError = JSC::GeneralError; + switch (error) { + case UnknownError: + break; + case ReferenceError: + jscError = JSC::ReferenceError; + break; + case SyntaxError: + jscError = JSC::SyntaxError; + break; + case TypeError: + jscError = JSC::TypeError; + break; + case RangeError: + jscError = JSC::RangeError; + break; + case URIError: + jscError = JSC::URIError; + break; + } + JSC::JSObject *result = JSC::throwError(frame, jscError, text); + return QScript::scriptEngineFromExec(frame)->scriptValueFromJSCValue(result); +} + +/*! + \overload + + Throws an error with the given \a text. + Returns the created error object. + + \sa throwValue(), state() +*/ +QScriptValue QScriptContext::throwError(const QString &text) +{ + JSC::CallFrame *frame = QScriptEnginePrivate::frameForContext(this); + QScript::APIShim shim(QScript::scriptEngineFromExec(frame)); + JSC::JSObject *result = JSC::throwError(frame, JSC::GeneralError, text); + return QScript::scriptEngineFromExec(frame)->scriptValueFromJSCValue(result); +} + +/*! + Destroys this QScriptContext. +*/ +QScriptContext::~QScriptContext() +{ + //QScriptContext doesn't exist, pointer to QScriptContext are just pointer to JSC::CallFrame + Q_ASSERT(false); +} + +/*! + Returns the QScriptEngine that this QScriptContext belongs to. +*/ +QScriptEngine *QScriptContext::engine() const +{ + const JSC::CallFrame *frame = QScriptEnginePrivate::frameForContext(this); + return QScriptEnginePrivate::get(QScript::scriptEngineFromExec(frame)); +} + +/*! + Returns the function argument at the given \a index. + + If \a index >= argumentCount(), a QScriptValue of + the primitive type Undefined is returned. + + \sa argumentCount() +*/ +QScriptValue QScriptContext::argument(int index) const +{ + if (index < 0) + return QScriptValue(); + if (index >= argumentCount()) + return QScriptValue(QScriptValue::UndefinedValue); + QScriptValue v = argumentsObject().property(index); + return v; +} + +/*! + Returns the callee. The callee is the function object that this + QScriptContext represents an invocation of. +*/ +QScriptValue QScriptContext::callee() const +{ + const JSC::CallFrame *frame = QScriptEnginePrivate::frameForContext(this); + QScriptEnginePrivate *eng = QScript::scriptEngineFromExec(frame); + QScript::APIShim shim(eng); + if (frame->callee() == eng->originalGlobalObject()) { + // This is a pushContext()-created context; the callee is a lie. + Q_ASSERT(QScriptEnginePrivate::contextFlags(const_cast<JSC::CallFrame*>(frame)) & QScriptEnginePrivate::NativeContext); + return QScriptValue(); + } + return eng->scriptValueFromJSCValue(frame->callee()); +} + +/*! + Returns the arguments object of this QScriptContext. + + The arguments object has properties \c callee (equal to callee()) + and \c length (equal to argumentCount()), and properties \c 0, \c 1, + ..., argumentCount() - 1 that provide access to the argument + values. Initially, property \c P (0 <= \c P < argumentCount()) has + the same value as argument(\c P). In the case when \c P is less + than the number of formal parameters of the function, \c P shares + its value with the corresponding property of the activation object + (activationObject()). This means that changing this property changes + the corresponding property of the activation object and vice versa. + + \sa argument(), activationObject() +*/ +QScriptValue QScriptContext::argumentsObject() const +{ + JSC::CallFrame *frame = const_cast<JSC::ExecState*>(QScriptEnginePrivate::frameForContext(this)); + QScript::APIShim shim(QScript::scriptEngineFromExec(frame)); + + if (frame == frame->lexicalGlobalObject()->globalExec()) { + // <global> context doesn't have arguments. return an empty object + return QScriptEnginePrivate::get(QScript::scriptEngineFromExec(frame))->newObject(); + } + + //for a js function + if (frame->codeBlock() && frame->callee()) { + if (!QScriptEnginePrivate::hasValidCodeBlockRegister(frame)) { + // We have a built-in JS host call. + // codeBlock is needed by retrieveArguments(), but since it + // contains junk, we would crash. Return an invalid value for now. + return QScriptValue(); + } + JSC::JSValue result = frame->interpreter()->retrieveArguments(frame, JSC::asFunction(frame->callee())); + return QScript::scriptEngineFromExec(frame)->scriptValueFromJSCValue(result); + } + + if (frame->callerFrame()->hasHostCallFrameFlag()) { + // <eval> context doesn't have arguments. return an empty object + return QScriptEnginePrivate::get(QScript::scriptEngineFromExec(frame))->newObject(); + } + + //for a native function + if (!frame->optionalCalleeArguments() + && QScriptEnginePrivate::hasValidCodeBlockRegister(frame)) { // Make sure we don't go here for host JSFunctions + Q_ASSERT(frame->argumentCount() > 0); //we need at least 'this' otherwise we'll crash later + JSC::Arguments* arguments = new (&frame->globalData())JSC::Arguments(frame, JSC::Arguments::NoParameters); + frame->setCalleeArguments(arguments); + } + return QScript::scriptEngineFromExec(frame)->scriptValueFromJSCValue(frame->optionalCalleeArguments()); +} + +/*! + Returns true if the function was called as a constructor + (e.g. \c{"new foo()"}); otherwise returns false. + + When a function is called as constructor, the thisObject() + contains the newly constructed object to be initialized. + + \note This function is only guaranteed to work for a context + corresponding to native functions. +*/ +bool QScriptContext::isCalledAsConstructor() const +{ + JSC::CallFrame *frame = const_cast<JSC::ExecState*>(QScriptEnginePrivate::frameForContext(this)); + QScript::APIShim shim(QScript::scriptEngineFromExec(frame)); + + //For native functions, look up flags. + uint flags = QScriptEnginePrivate::contextFlags(frame); + if (flags & QScriptEnginePrivate::NativeContext) + return flags & QScriptEnginePrivate::CalledAsConstructorContext; + + //Not a native function, try to look up in the bytecode if we where called from op_construct + JSC::Instruction* returnPC = frame->returnPC(); + + if (!returnPC) + return false; + + JSC::CallFrame *callerFrame = QScriptEnginePrivate::frameForContext(parentContext()); + if (!callerFrame) + return false; + + if (returnPC[-JSC::op_construct_length].u.opcode == frame->interpreter()->getOpcode(JSC::op_construct)) { + //We are maybe called from the op_construct opcode which has 6 opperands. + //But we need to check we are not called from op_call with 4 opperands + + //we make sure that the returnPC[-1] (thisRegister) is smaller than the returnPC[-3] (registerOffset) + //as if it was an op_call, the returnPC[-1] would be the registerOffset, bigger than returnPC[-3] (funcRegister) + return returnPC[-1].u.operand < returnPC[-3].u.operand; + } + return false; +} + +/*! + Returns the parent context of this QScriptContext. +*/ +QScriptContext *QScriptContext::parentContext() const +{ + const JSC::CallFrame *frame = QScriptEnginePrivate::frameForContext(this); + QScript::APIShim shim(QScript::scriptEngineFromExec(frame)); + JSC::CallFrame *callerFrame = frame->callerFrame()->removeHostCallFrameFlag(); + return QScriptEnginePrivate::contextForFrame(callerFrame); +} + +/*! + Returns the number of arguments passed to the function + in this invocation. + + Note that the argument count can be different from the + formal number of arguments (the \c{length} property of + callee()). + + \sa argument() +*/ +int QScriptContext::argumentCount() const +{ + const JSC::CallFrame *frame = QScriptEnginePrivate::frameForContext(this); + int argc = frame->argumentCount(); + if (argc != 0) + --argc; // -1 due to "this" + return argc; +} + +/*! + \internal +*/ +QScriptValue QScriptContext::returnValue() const +{ + qWarning("QScriptContext::returnValue() not implemented"); + return QScriptValue(); +} + +/*! + \internal +*/ +void QScriptContext::setReturnValue(const QScriptValue &result) +{ + JSC::CallFrame *frame = QScriptEnginePrivate::frameForContext(this); + JSC::CallFrame *callerFrame = frame->callerFrame(); + if (!callerFrame->codeBlock()) + return; + Q_ASSERT_X(false, Q_FUNC_INFO, "check me"); + int dst = frame->registers()[JSC::RegisterFile::ReturnValueRegister].i(); // returnValueRegister() is private + callerFrame[dst] = QScript::scriptEngineFromExec(frame)->scriptValueToJSCValue(result); +} + +/*! + Returns the activation object of this QScriptContext. The activation + object provides access to the local variables associated with this + context. + + \note The activation object might not be available if there is no + active QScriptEngineAgent, as it might be optimized. + + \sa argument(), argumentsObject() +*/ + +QScriptValue QScriptContext::activationObject() const +{ + JSC::CallFrame *frame = const_cast<JSC::ExecState*>(QScriptEnginePrivate::frameForContext(this)); + QScript::APIShim shim(QScript::scriptEngineFromExec(frame)); + JSC::JSObject *result = 0; + + uint flags = QScriptEnginePrivate::contextFlags(frame); + if ((flags & QScriptEnginePrivate::NativeContext) && !(flags & QScriptEnginePrivate::HasScopeContext)) { + //For native functions, lazily create it if needed + QScript::QScriptActivationObject *scope = new (frame) QScript::QScriptActivationObject(frame); + frame->setScopeChain(frame->scopeChain()->copy()->push(scope)); + result = scope; + QScriptEnginePrivate::setContextFlags(frame, flags | QScriptEnginePrivate::HasScopeContext); + } else { + // look in scope chain + JSC::ScopeChainNode *node = frame->scopeChain(); + JSC::ScopeChainIterator it(node); + for (it = node->begin(); it != node->end(); ++it) { + if ((*it) && (*it)->isVariableObject()) { + result = *it; + break; + } + } + } + if (!result) { + if (!parentContext()) + return engine()->globalObject(); + + qWarning("QScriptContext::activationObject: could not get activation object for frame"); + return QScriptValue(); + /*JSC::CodeBlock *codeBlock = frame->codeBlock(); + if (!codeBlock) { + // non-Qt native function + Q_ASSERT(true); //### this should in theorry not happen + result = new (frame)QScript::QScriptActivationObject(frame); + } else { + // ### this is wrong + JSC::FunctionBodyNode *body = static_cast<JSC::FunctionBodyNode*>(codeBlock->ownerNode()); + result = new (frame)JSC::JSActivation(frame, body); + }*/ + } + + if (result && result->inherits(&QScript::QScriptActivationObject::info) + && (static_cast<QScript::QScriptActivationObject*>(result)->delegate() != 0)) { + // Return the object that property access is being delegated to + result = static_cast<QScript::QScriptActivationObject*>(result)->delegate(); + } + + return QScript::scriptEngineFromExec(frame)->scriptValueFromJSCValue(result); +} + +/*! + Sets the activation object of this QScriptContext to be the given \a + activation. + + If \a activation is not an object, this function does nothing. + + \note For a context corresponding to a JavaScript function, this is only + guaranteed to work if there was an QScriptEngineAgent active on the + engine while the function was evaluated. +*/ +void QScriptContext::setActivationObject(const QScriptValue &activation) +{ + if (!activation.isObject()) + return; + else if (activation.engine() != engine()) { + qWarning("QScriptContext::setActivationObject() failed: " + "cannot set an object created in " + "a different engine"); + return; + } + JSC::CallFrame *frame = QScriptEnginePrivate::frameForContext(this); + QScriptEnginePrivate *engine = QScript::scriptEngineFromExec(frame); + QScript::APIShim shim(engine); + JSC::JSObject *object = JSC::asObject(engine->scriptValueToJSCValue(activation)); + if (object == engine->originalGlobalObjectProxy) + object = engine->originalGlobalObject(); + + uint flags = QScriptEnginePrivate::contextFlags(frame); + if ((flags & QScriptEnginePrivate::NativeContext) && !(flags & QScriptEnginePrivate::HasScopeContext)) { + //For native functions, we create a scope node + JSC::JSObject *scope = object; + if (!scope->isVariableObject()) { + // Create a QScriptActivationObject that acts as a proxy + scope = new (frame) QScript::QScriptActivationObject(frame, scope); + } + frame->setScopeChain(frame->scopeChain()->copy()->push(scope)); + QScriptEnginePrivate::setContextFlags(frame, flags | QScriptEnginePrivate::HasScopeContext); + return; + } + + // else replace the first activation object in the scope chain + JSC::ScopeChainNode *node = frame->scopeChain(); + while (node != 0) { + if (node->object && node->object->isVariableObject()) { + if (!object->isVariableObject()) { + if (node->object->inherits(&QScript::QScriptActivationObject::info)) { + static_cast<QScript::QScriptActivationObject*>(node->object)->setDelegate(object); + } else { + // Create a QScriptActivationObject that acts as a proxy + node->object = new (frame) QScript::QScriptActivationObject(frame, object); + } + } else { + node->object = object; + } + break; + } + node = node->next; + } +} + +/*! + Returns the `this' object associated with this QScriptContext. +*/ +QScriptValue QScriptContext::thisObject() const +{ + JSC::CallFrame *frame = const_cast<JSC::ExecState*>(QScriptEnginePrivate::frameForContext(this)); + QScriptEnginePrivate *engine = QScript::scriptEngineFromExec(frame); + QScript::APIShim shim(engine); + JSC::JSValue result = engine->thisForContext(frame); + if (!result || result.isNull()) + result = frame->globalThisValue(); + return engine->scriptValueFromJSCValue(result); +} + +/*! + Sets the `this' object associated with this QScriptContext to be + \a thisObject. + + If \a thisObject is not an object, this function does nothing. +*/ +void QScriptContext::setThisObject(const QScriptValue &thisObject) +{ + JSC::CallFrame *frame = QScriptEnginePrivate::frameForContext(this); + QScript::APIShim shim(QScript::scriptEngineFromExec(frame)); + if (!thisObject.isObject()) + return; + if (thisObject.engine() != engine()) { + qWarning("QScriptContext::setThisObject() failed: " + "cannot set an object created in " + "a different engine"); + return; + } + if (frame == frame->lexicalGlobalObject()->globalExec()) { + engine()->setGlobalObject(thisObject); + return; + } + JSC::JSValue jscThisObject = QScript::scriptEngineFromExec(frame)->scriptValueToJSCValue(thisObject); + JSC::CodeBlock *cb = frame->codeBlock(); + if (cb != 0) { + frame[cb->thisRegister()] = jscThisObject; + } else { + JSC::Register* thisRegister = QScriptEnginePrivate::thisRegisterForFrame(frame); + thisRegister[0] = jscThisObject; + } +} + +/*! + Returns the frameution state of this QScriptContext. +*/ +QScriptContext::ExecutionState QScriptContext::state() const +{ + const JSC::CallFrame *frame = QScriptEnginePrivate::frameForContext(this); + if (frame->hadException()) + return QScriptContext::ExceptionState; + return QScriptContext::NormalState; +} + +/*! + Returns a human-readable backtrace of this QScriptContext. + + Each line is of the form \c{<function-name>(<arguments>)@<file-name>:<line-number>}. + + To access individual pieces of debugging-related information (for + example, to construct your own backtrace representation), use + QScriptContextInfo. + + \sa QScriptEngine::uncaughtExceptionBacktrace(), QScriptContextInfo, toString() +*/ +QStringList QScriptContext::backtrace() const +{ + QStringList result; + const QScriptContext *ctx = this; + while (ctx) { + result.append(ctx->toString()); + ctx = ctx->parentContext(); + } + return result; +} + +/*! + \since 4.4 + + Returns a string representation of this context. + This is useful for debugging. + + \sa backtrace() +*/ +QString QScriptContext::toString() const +{ + QScriptContextInfo info(this); + QString result; + + QString functionName = info.functionName(); + if (functionName.isEmpty()) { + if (parentContext()) { + const JSC::CallFrame *frame = QScriptEnginePrivate::frameForContext(this); + if (info.functionType() == QScriptContextInfo::ScriptFunction) + result.append(QLatin1String("<anonymous>")); + else if(frame->callerFrame()->hasHostCallFrameFlag()) + result.append(QLatin1String("<eval>")); + else + result.append(QLatin1String("<native>")); + } else { + result.append(QLatin1String("<global>")); + } + } else { + result.append(functionName); + } + + QStringList parameterNames = info.functionParameterNames(); + result.append(QLatin1Char('(')); + for (int i = 0; i < argumentCount(); ++i) { + if (i > 0) + result.append(QLatin1String(", ")); + if (i < parameterNames.count()) { + result.append(parameterNames.at(i)); + result.append(QLatin1String(" = ")); + } + QScriptValue arg = argument(i); + if (arg.isString()) + result.append(QLatin1Char('\'')); + result.append(arg.toString()); + if (arg.isString()) + result.append(QLatin1Char('\'')); + + } + result.append(QLatin1Char(')')); + + QString fileName = info.fileName(); + int lineNumber = info.lineNumber(); + result.append(QLatin1String(" at ")); + if (!fileName.isEmpty()) { + result.append(fileName); + result.append(QLatin1Char(':')); + } + result.append(QString::number(lineNumber)); + return result; +} + +/*! + \internal + \since 4.5 + + Returns the scope chain of this QScriptContext. +*/ +QScriptValueList QScriptContext::scopeChain() const +{ + activationObject(); //ensure the creation of the normal scope for native context + const JSC::CallFrame *frame = QScriptEnginePrivate::frameForContext(this); + QScriptEnginePrivate *engine = QScript::scriptEngineFromExec(frame); + QScript::APIShim shim(engine); + QScriptValueList result; + JSC::ScopeChainNode *node = frame->scopeChain(); + JSC::ScopeChainIterator it(node); + for (it = node->begin(); it != node->end(); ++it) { + JSC::JSObject *object = *it; + if (!object) + continue; + 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(); + } + result.append(engine->scriptValueFromJSCValue(object)); + } + return result; +} + +/*! + \internal + \since 4.5 + + Adds the given \a object to the front of this context's scope chain. + + If \a object is not an object, this function does nothing. +*/ +void QScriptContext::pushScope(const QScriptValue &object) +{ + activationObject(); //ensure the creation of the normal scope for native context + if (!object.isObject()) + return; + else if (object.engine() != engine()) { + qWarning("QScriptContext::pushScope() failed: " + "cannot push an object created in " + "a different engine"); + return; + } + JSC::CallFrame *frame = QScriptEnginePrivate::frameForContext(this); + QScriptEnginePrivate *engine = QScript::scriptEngineFromExec(frame); + QScript::APIShim shim(engine); + JSC::JSObject *jscObject = JSC::asObject(engine->scriptValueToJSCValue(object)); + if (jscObject == engine->originalGlobalObjectProxy) + jscObject = engine->originalGlobalObject(); + JSC::ScopeChainNode *scope = frame->scopeChain(); + Q_ASSERT(scope != 0); + if (!scope->object) { + // pushing to an "empty" chain + if (!jscObject->isGlobalObject()) { + qWarning("QScriptContext::pushScope() failed: initial object in scope chain has to be the Global Object"); + return; + } + scope->object = jscObject; + } + else + frame->setScopeChain(scope->push(jscObject)); +} + +/*! + \internal + \since 4.5 + + Removes the front object from this context's scope chain, and + returns the removed object. + + If the scope chain is already empty, this function returns an + invalid QScriptValue. +*/ +QScriptValue QScriptContext::popScope() +{ + activationObject(); //ensure the creation of the normal scope for native context + JSC::CallFrame *frame = QScriptEnginePrivate::frameForContext(this); + JSC::ScopeChainNode *scope = frame->scopeChain(); + Q_ASSERT(scope != 0); + QScriptEnginePrivate *engine = QScript::scriptEngineFromExec(frame); + QScript::APIShim shim(engine); + QScriptValue result = engine->scriptValueFromJSCValue(scope->object); + if (!scope->next) { + // We cannot have a null scope chain, so just zap the object pointer. + scope->object = 0; + } else { + frame->setScopeChain(scope->pop()); + } + return result; +} + +QT_END_NAMESPACE diff --git a/src/script/api/qscriptcontext.h b/src/script/api/qscriptcontext.h new file mode 100644 index 0000000..8bd43e6 --- /dev/null +++ b/src/script/api/qscriptcontext.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 QSCRIPTCONTEXT_H +#define QSCRIPTCONTEXT_H + +#include <QtCore/qobjectdefs.h> + +#include <QtScript/qscriptvalue.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Script) + +class QScriptContextPrivate; + +class Q_SCRIPT_EXPORT QScriptContext +{ +public: + enum ExecutionState { + NormalState, + ExceptionState + }; + + enum Error { + UnknownError, + ReferenceError, + SyntaxError, + TypeError, + RangeError, + URIError + }; + + ~QScriptContext(); + + QScriptContext *parentContext() const; + QScriptEngine *engine() const; + + ExecutionState state() const; + QScriptValue callee() const; + + int argumentCount() const; + QScriptValue argument(int index) const; + QScriptValue argumentsObject() const; + + QScriptValueList scopeChain() const; + void pushScope(const QScriptValue &object); + QScriptValue popScope(); + + QScriptValue returnValue() const; + void setReturnValue(const QScriptValue &result); + + QScriptValue activationObject() const; + void setActivationObject(const QScriptValue &activation); + + QScriptValue thisObject() const; + void setThisObject(const QScriptValue &thisObject); + + bool isCalledAsConstructor() const; + + QScriptValue throwValue(const QScriptValue &value); + QScriptValue throwError(Error error, const QString &text); + QScriptValue throwError(const QString &text); + + QStringList backtrace() const; + + QString toString() const; + +private: + QScriptContext(); + + QScriptContextPrivate *d_ptr; + + Q_DECLARE_PRIVATE(QScriptContext) + Q_DISABLE_COPY(QScriptContext) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/script/api/qscriptcontext_p.h b/src/script/api/qscriptcontext_p.h new file mode 100644 index 0000000..2919637 --- /dev/null +++ b/src/script/api/qscriptcontext_p.h @@ -0,0 +1,58 @@ +/**************************************************************************** +** +** 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 QSCRIPTCONTEXT_P_H +#define QSCRIPTCONTEXT_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> + +namespace JSC +{ + class JSObject; + class ArgList; + class ExecState; +} + +QT_BEGIN_NAMESPACE + +class QScriptEnginePrivate; + +class QScriptContext; + +QT_END_NAMESPACE + +#include "wtf/Platform.h" +#include "JSValue.h" + +#endif diff --git a/src/script/api/qscriptcontextinfo.cpp b/src/script/api/qscriptcontextinfo.cpp new file mode 100644 index 0000000..182bc4a --- /dev/null +++ b/src/script/api/qscriptcontextinfo.cpp @@ -0,0 +1,556 @@ +/**************************************************************************** +** +** 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 "qscriptcontextinfo.h" + +#include "qscriptcontext_p.h" +#include "qscriptengine.h" +#include "qscriptengine_p.h" +#include "../bridge/qscriptqobject_p.h" +#include <QtCore/qdatastream.h> +#include <QtCore/qmetaobject.h> +#include "CodeBlock.h" +#include "JSFunction.h" +#if ENABLE(JIT) +#include "MacroAssemblerCodeRef.h" +#endif + +QT_BEGIN_NAMESPACE + +/*! + \since 4.4 + \class QScriptContextInfo + + \brief The QScriptContextInfo class provides additional information about a QScriptContext. + + \ingroup script + + + QScriptContextInfo is typically used for debugging purposes. It can + provide information about the code being executed, such as the type + of the called function, and the original source code location of the + current statement. + + If the called function is executing Qt Script code, you can obtain + the script location with the functions fileName() and lineNumber(). + + You can obtain the starting line number and ending line number of a + Qt Script function definition with functionStartLineNumber() and + functionEndLineNumber(), respectively. + + For Qt Script functions and Qt methods (e.g. slots), you can call + functionParameterNames() to get the names of the formal parameters of the + function. + + For Qt methods and Qt property accessors, you can obtain the index + of the underlying QMetaMethod or QMetaProperty by calling + functionMetaIndex(). + + \sa QScriptContext, QScriptEngineAgent +*/ + +/*! + \enum QScriptContextInfo::FunctionType + + This enum specifies the type of function being called. + + \value ScriptFunction The function is a Qt Script function, i.e. it was defined through a call to QScriptEngine::evaluate(). + \value QtFunction The function is a Qt function (a signal, slot or method). + \value QtPropertyFunction The function is a Qt property getter or setter. + \value NativeFunction The function is a built-in Qt Script function, or it was defined through a call to QScriptEngine::newFunction(). +*/ + +class QScriptContextInfoPrivate +{ + Q_DECLARE_PUBLIC(QScriptContextInfo) +public: + QScriptContextInfoPrivate(); + QScriptContextInfoPrivate(const QScriptContext *context); + ~QScriptContextInfoPrivate(); + + qint64 scriptId; + int lineNumber; + int columnNumber; + QString fileName; + + QString functionName; + QScriptContextInfo::FunctionType functionType; + + int functionStartLineNumber; + int functionEndLineNumber; + int functionMetaIndex; + + QStringList parameterNames; + + QBasicAtomicInt ref; + + QScriptContextInfo *q_ptr; +}; + +/*! + \internal +*/ +QScriptContextInfoPrivate::QScriptContextInfoPrivate() +{ + ref = 0; + functionType = QScriptContextInfo::NativeFunction; + functionMetaIndex = -1; + functionStartLineNumber = -1; + functionEndLineNumber = -1; + scriptId = -1; + lineNumber = -1; + columnNumber = -1; +} + +/*! + \internal +*/ +QScriptContextInfoPrivate::QScriptContextInfoPrivate(const QScriptContext *context) +{ + Q_ASSERT(context); + ref = 0; + functionType = QScriptContextInfo::NativeFunction; + functionMetaIndex = -1; + functionStartLineNumber = -1; + functionEndLineNumber = -1; + scriptId = -1; + lineNumber = -1; + columnNumber = -1; + + JSC::CallFrame *frame = const_cast<JSC::CallFrame *>(QScriptEnginePrivate::frameForContext(context)); + + // Get the line number: + + //We need to know the context directly up in the backtrace, in order to get the line number, and adjust the global context + JSC::CallFrame *rewindContext = QScriptEnginePrivate::get(context->engine())->currentFrame; + if (QScriptEnginePrivate::contextForFrame(rewindContext) == context) { //top context + frame = rewindContext; //for retreiving the global context's "fake" frame + // An agent might have provided the line number. + lineNumber = QScript::scriptEngineFromExec(frame)->agentLineNumber; + } else { + // rewind the stack from the top in order to find the frame from the caller where the returnPC is stored + while (rewindContext && QScriptEnginePrivate::contextForFrame(rewindContext->callerFrame()->removeHostCallFrameFlag()) != context) + rewindContext = rewindContext->callerFrame()->removeHostCallFrameFlag(); + if (rewindContext) { + frame = rewindContext->callerFrame()->removeHostCallFrameFlag(); //for retreiving the global context's "fake" frame + + JSC::Instruction *returnPC = rewindContext->returnPC(); + JSC::CodeBlock *codeBlock = frame->codeBlock(); + if (returnPC && codeBlock && QScriptEnginePrivate::hasValidCodeBlockRegister(frame)) { +#if ENABLE(JIT) + JSC::JITCode code = codeBlock->getJITCode(); + unsigned jitOffset = code.offsetOf(JSC::ReturnAddressPtr(returnPC).value()); + // We can only use the JIT code offset if it's smaller than the JIT size; + // otherwise calling getBytecodeIndex() is meaningless. + if (jitOffset < code.size()) { + unsigned bytecodeOffset = codeBlock->getBytecodeIndex(frame, JSC::ReturnAddressPtr(returnPC)); +#else + unsigned bytecodeOffset = returnPC - codeBlock->instructions().begin(); +#endif + bytecodeOffset--; //because returnPC is on the next instruction. We want the current one + lineNumber = codeBlock->lineNumberForBytecodeOffset(const_cast<JSC::ExecState *>(frame), bytecodeOffset); +#if ENABLE(JIT) + } +#endif + } + } + } + + // Get the filename and the scriptId: + JSC::CodeBlock *codeBlock = frame->codeBlock(); + if (codeBlock && QScriptEnginePrivate::hasValidCodeBlockRegister(frame)) { + JSC::SourceProvider *source = codeBlock->source(); + scriptId = source->asID(); + fileName = source->url(); + } + + // Get the others information: + JSC::JSObject *callee = frame->callee(); + if (callee && callee->inherits(&JSC::InternalFunction::info)) + functionName = JSC::asInternalFunction(callee)->name(frame); + if (callee && callee->inherits(&JSC::JSFunction::info) + && !JSC::asFunction(callee)->isHostFunction()) { + functionType = QScriptContextInfo::ScriptFunction; + JSC::FunctionExecutable *body = JSC::asFunction(callee)->jsExecutable(); + functionStartLineNumber = body->lineNo(); + functionEndLineNumber = body->lastLine(); + for (size_t i = 0; i < body->parameterCount(); ++i) + parameterNames.append(body->parameterName(i)); + // ### get the function name from the AST + } else if (callee && callee->inherits(&QScript::QtFunction::info)) { + functionType = QScriptContextInfo::QtFunction; + // ### the slot can be overloaded -- need to get the particular overload from the context + functionMetaIndex = static_cast<QScript::QtFunction*>(callee)->initialIndex(); + const QMetaObject *meta = static_cast<QScript::QtFunction*>(callee)->metaObject(); + if (meta != 0) { + QMetaMethod method = meta->method(functionMetaIndex); + QList<QByteArray> formals = method.parameterNames(); + for (int i = 0; i < formals.count(); ++i) + parameterNames.append(QLatin1String(formals.at(i))); + } + } + else if (callee && callee->inherits(&QScript::QtPropertyFunction::info)) { + functionType = QScriptContextInfo::QtPropertyFunction; + functionMetaIndex = static_cast<QScript::QtPropertyFunction*>(callee)->propertyIndex(); + } +} + +/*! + \internal +*/ +QScriptContextInfoPrivate::~QScriptContextInfoPrivate() +{ +} + +/*! + Constructs a new QScriptContextInfo from the given \a context. + + The relevant information is extracted from the \a context at + construction time; i.e. if you continue script execution in the \a + context, the new state of the context will not be reflected in a + previously created QScriptContextInfo. +*/ +QScriptContextInfo::QScriptContextInfo(const QScriptContext *context) + : d_ptr(0) +{ + if (context) { + d_ptr = new QScriptContextInfoPrivate(context); + d_ptr->q_ptr = this; + } +} + +/*! + Constructs a new QScriptContextInfo from the \a other info. +*/ +QScriptContextInfo::QScriptContextInfo(const QScriptContextInfo &other) + : d_ptr(other.d_ptr) +{ +} + +/*! + Constructs a null QScriptContextInfo. + + \sa isNull() +*/ +QScriptContextInfo::QScriptContextInfo() + : d_ptr(0) +{ +} + +/*! + Destroys the QScriptContextInfo. +*/ +QScriptContextInfo::~QScriptContextInfo() +{ +} + +/*! + Assigns the \a other info to this QScriptContextInfo, + and returns a reference to this QScriptContextInfo. +*/ +QScriptContextInfo &QScriptContextInfo::operator=(const QScriptContextInfo &other) +{ + d_ptr = other.d_ptr; + return *this; +} + +/*! + Returns the ID of the script where the code being executed was + defined, or -1 if the ID is not available (i.e. a native function is + being executed). + + \sa QScriptEngineAgent::scriptLoad() +*/ +qint64 QScriptContextInfo::scriptId() const +{ + Q_D(const QScriptContextInfo); + if (!d) + return -1; + return d->scriptId; +} + +/*! + Returns the name of the file where the code being executed was + defined, if available; otherwise returns an empty string. + + For Qt Script code, this function returns the fileName argument + that was passed to QScriptEngine::evaluate(). + + \sa lineNumber(), functionName() +*/ +QString QScriptContextInfo::fileName() const +{ + Q_D(const QScriptContextInfo); + if (!d) + return QString(); + return d->fileName; +} + +/*! + Returns the line number corresponding to the statement being + executed, or -1 if the line number is not available. + + The line number is only available if Qt Script code is being + executed. + + \sa columnNumber(), fileName() +*/ +int QScriptContextInfo::lineNumber() const +{ + Q_D(const QScriptContextInfo); + if (!d) + return -1; + return d->lineNumber; +} + +/*! + \obsolete +*/ +int QScriptContextInfo::columnNumber() const +{ + Q_D(const QScriptContextInfo); + if (!d) + return -1; + return d->columnNumber; +} + +/*! + Returns the name of the called function, or an empty string if + the name is not available. + + For script functions of type QtPropertyFunction, this function + always returns the name of the property; you can use + QScriptContext::argumentCount() to differentiate between reads and + writes. + + \sa fileName(), functionType() +*/ +QString QScriptContextInfo::functionName() const +{ + Q_D(const QScriptContextInfo); + if (!d) + return QString(); + return d->functionName; +} + +/*! + Returns the type of the called function. + + \sa functionName(), QScriptContext::callee() +*/ +QScriptContextInfo::FunctionType QScriptContextInfo::functionType() const +{ + Q_D(const QScriptContextInfo); + if (!d) + return NativeFunction; + return d->functionType; +} + +/*! + Returns the line number where the definition of the called function + starts, or -1 if the line number is not available. + + The starting line number is only available if the functionType() is + ScriptFunction. + + \sa functionEndLineNumber(), fileName() +*/ +int QScriptContextInfo::functionStartLineNumber() const +{ + Q_D(const QScriptContextInfo); + if (!d) + return -1; + return d->functionStartLineNumber; +} + +/*! + Returns the line number where the definition of the called function + ends, or -1 if the line number is not available. + + The ending line number is only available if the functionType() is + ScriptFunction. + + \sa functionStartLineNumber() +*/ +int QScriptContextInfo::functionEndLineNumber() const +{ + Q_D(const QScriptContextInfo); + if (!d) + return -1; + return d->functionEndLineNumber; +} + +/*! + Returns the names of the formal parameters of the called function, + or an empty QStringList if the parameter names are not available. + + \sa QScriptContext::argument() +*/ +QStringList QScriptContextInfo::functionParameterNames() const +{ + Q_D(const QScriptContextInfo); + if (!d) + return QStringList(); + return d->parameterNames; +} + +/*! + Returns the meta index of the called function, or -1 if the meta + index is not available. + + The meta index is only available if the functionType() is QtFunction + or QtPropertyFunction. For QtFunction, the meta index can be passed + to QMetaObject::method() to obtain the corresponding method + definition; for QtPropertyFunction, the meta index can be passed to + QMetaObject::property() to obtain the corresponding property + definition. + + \sa QScriptContext::thisObject() +*/ +int QScriptContextInfo::functionMetaIndex() const +{ + Q_D(const QScriptContextInfo); + if (!d) + return -1; + return d->functionMetaIndex; +} + +/*! + Returns true if this QScriptContextInfo is null, i.e. does not + contain any information. +*/ +bool QScriptContextInfo::isNull() const +{ + Q_D(const QScriptContextInfo); + return (d == 0); +} + +/*! + Returns true if this QScriptContextInfo is equal to the \a other + info, otherwise returns false. +*/ +bool QScriptContextInfo::operator==(const QScriptContextInfo &other) const +{ + Q_D(const QScriptContextInfo); + const QScriptContextInfoPrivate *od = other.d_func(); + if (d == od) + return true; + if (!d || !od) + return false; + return ((d->scriptId == od->scriptId) + && (d->lineNumber == od->lineNumber) + && (d->columnNumber == od->columnNumber) + && (d->fileName == od->fileName) + && (d->functionName == od->functionName) + && (d->functionType == od->functionType) + && (d->functionStartLineNumber == od->functionStartLineNumber) + && (d->functionEndLineNumber == od->functionEndLineNumber) + && (d->functionMetaIndex == od->functionMetaIndex) + && (d->parameterNames == od->parameterNames)); +} + +/*! + Returns true if this QScriptContextInfo is not equal to the \a other + info, otherwise returns false. +*/ +bool QScriptContextInfo::operator!=(const QScriptContextInfo &other) const +{ + return !(*this == other); +} + +#ifndef QT_NO_DATASTREAM +/*! + \fn QDataStream &operator<<(QDataStream &stream, const QScriptContextInfo &info) + \since 4.4 + \relates QScriptContextInfo + + Writes the given \a info to the specified \a stream. +*/ +QDataStream &operator<<(QDataStream &out, const QScriptContextInfo &info) +{ + out << info.scriptId(); + out << (qint32)info.lineNumber(); + out << (qint32)info.columnNumber(); + + out << (quint32)info.functionType(); + out << (qint32)info.functionStartLineNumber(); + out << (qint32)info.functionEndLineNumber(); + out << (qint32)info.functionMetaIndex(); + + out << info.fileName(); + out << info.functionName(); + out << info.functionParameterNames(); + + return out; +} + +/*! + \fn QDataStream &operator>>(QDataStream &stream, QScriptContextInfo &info) + \since 4.4 + \relates QScriptContextInfo + + Reads a QScriptContextInfo from the specified \a stream into the + given \a info. +*/ +Q_SCRIPT_EXPORT QDataStream &operator>>(QDataStream &in, QScriptContextInfo &info) +{ + if (!info.d_ptr) { + info.d_ptr = new QScriptContextInfoPrivate(); + } + + in >> info.d_ptr->scriptId; + + qint32 line; + in >> line; + info.d_ptr->lineNumber = line; + + qint32 column; + in >> column; + info.d_ptr->columnNumber = column; + + quint32 ftype; + in >> ftype; + info.d_ptr->functionType = QScriptContextInfo::FunctionType(ftype); + + qint32 startLine; + in >> startLine; + info.d_ptr->functionStartLineNumber = startLine; + + qint32 endLine; + in >> endLine; + info.d_ptr->functionEndLineNumber = endLine; + + qint32 metaIndex; + in >> metaIndex; + info.d_ptr->functionMetaIndex = metaIndex; + + in >> info.d_ptr->fileName; + in >> info.d_ptr->functionName; + in >> info.d_ptr->parameterNames; + + return in; +} +#endif + +QT_END_NAMESPACE diff --git a/src/script/api/qscriptcontextinfo.h b/src/script/api/qscriptcontextinfo.h new file mode 100644 index 0000000..ebb407d --- /dev/null +++ b/src/script/api/qscriptcontextinfo.h @@ -0,0 +1,106 @@ +/**************************************************************************** +** +** 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 QSCRIPTCONTEXTINFO_H +#define QSCRIPTCONTEXTINFO_H + +#include <QtCore/qobjectdefs.h> + +#include <QtCore/qlist.h> +#include <QtCore/qstringlist.h> +#include <QtCore/qsharedpointer.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Script) + +class QScriptContext; +#ifndef QT_NO_DATASTREAM +class QDataStream; +#endif + +class QScriptContextInfoPrivate; +class Q_SCRIPT_EXPORT QScriptContextInfo +{ +public: +#ifndef QT_NO_DATASTREAM + friend Q_SCRIPT_EXPORT QDataStream &operator<<(QDataStream &, const QScriptContextInfo &); + friend Q_SCRIPT_EXPORT QDataStream &operator>>(QDataStream &, QScriptContextInfo &); +#endif + + enum FunctionType { + ScriptFunction, + QtFunction, + QtPropertyFunction, + NativeFunction + }; + + QScriptContextInfo(const QScriptContext *context); + QScriptContextInfo(const QScriptContextInfo &other); + QScriptContextInfo(); + ~QScriptContextInfo(); + + QScriptContextInfo &operator=(const QScriptContextInfo &other); + + bool isNull() const; + + qint64 scriptId() const; + QString fileName() const; + int lineNumber() const; +#ifdef QT_DEPRECATED + QT_DEPRECATED int columnNumber() const; +#endif + + QString functionName() const; + FunctionType functionType() const; + + QStringList functionParameterNames() const; + + int functionStartLineNumber() const; + int functionEndLineNumber() const; + + int functionMetaIndex() const; + + bool operator==(const QScriptContextInfo &other) const; + bool operator!=(const QScriptContextInfo &other) const; + +private: + QExplicitlySharedDataPointer<QScriptContextInfoPrivate> d_ptr; + + Q_DECLARE_PRIVATE(QScriptContextInfo) +}; + +typedef QList<QScriptContextInfo> QScriptContextInfoList; + +#ifndef QT_NO_DATASTREAM +Q_SCRIPT_EXPORT QDataStream &operator<<(QDataStream &, const QScriptContextInfo &); +Q_SCRIPT_EXPORT QDataStream &operator>>(QDataStream &, QScriptContextInfo &); +#endif + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/script/api/qscriptengine.cpp b/src/script/api/qscriptengine.cpp new file mode 100644 index 0000000..a3a965e --- /dev/null +++ b/src/script/api/qscriptengine.cpp @@ -0,0 +1,4456 @@ +/**************************************************************************** +** +** 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 "qscriptengine.h" +#include "qscriptsyntaxchecker_p.h" + +#include "qscriptengine_p.h" +#include "qscriptengineagent_p.h" +#include "qscriptcontext_p.h" +#include "qscriptstring_p.h" +#include "qscriptvalue_p.h" +#include "qscriptvalueiterator.h" +#include "qscriptclass.h" +#include "qscriptcontextinfo.h" +#include "qscriptprogram.h" +#include "qscriptprogram_p.h" +#include "qdebug.h" + +#include <QtCore/qstringlist.h> +#include <QtCore/qmetaobject.h> + +#include <math.h> + +#include "CodeBlock.h" +#include "Error.h" +#include "Interpreter.h" + +#include "ExceptionHelpers.h" +#include "PrototypeFunction.h" +#include "InitializeThreading.h" +#include "ObjectPrototype.h" +#include "SourceCode.h" +#include "FunctionPrototype.h" +#include "TimeoutChecker.h" +#include "JSFunction.h" +#include "Parser.h" +#include "PropertyNameArray.h" +#include "Operations.h" + +#include "bridge/qscriptfunction_p.h" +#include "bridge/qscriptclassobject_p.h" +#include "bridge/qscriptvariant_p.h" +#include "bridge/qscriptqobject_p.h" +#include "bridge/qscriptglobalobject_p.h" +#include "bridge/qscriptactivationobject_p.h" +#include "bridge/qscriptstaticscopeobject_p.h" + +#ifndef QT_NO_QOBJECT +#include <QtCore/qcoreapplication.h> +#include <QtCore/qdir.h> +#include <QtCore/qfile.h> +#include <QtCore/qfileinfo.h> +#include <QtCore/qpluginloader.h> +#include <QtCore/qset.h> +#include <QtCore/qtextstream.h> +#include "qscriptextensioninterface.h" +#endif + +Q_DECLARE_METATYPE(QScriptValue) +#ifndef QT_NO_QOBJECT +Q_DECLARE_METATYPE(QObjectList) +#endif +Q_DECLARE_METATYPE(QList<int>) + +QT_BEGIN_NAMESPACE + +/*! + \since 4.3 + \class QScriptEngine + \reentrant + + \brief The QScriptEngine class provides an environment for evaluating Qt Script code. + + \ingroup script + \mainclass + + See the \l{QtScript} documentation for information about the Qt Script language, + and how to get started with scripting your C++ application. + + \section1 Evaluating Scripts + + Use evaluate() to evaluate script code; this is the C++ equivalent + of the built-in script function \c{eval()}. + + \snippet doc/src/snippets/code/src_script_qscriptengine.cpp 0 + + evaluate() returns a QScriptValue that holds the result of the + evaluation. The QScriptValue class provides functions for converting + the result to various C++ types (e.g. QScriptValue::toString() + and QScriptValue::toNumber()). + + The following code snippet shows how a script function can be + defined and then invoked from C++ using QScriptValue::call(): + + \snippet doc/src/snippets/code/src_script_qscriptengine.cpp 1 + + As can be seen from the above snippets, a script is provided to the + engine in the form of a string. One common way of loading scripts is + by reading the contents of a file and passing it to evaluate(): + + \snippet doc/src/snippets/code/src_script_qscriptengine.cpp 2 + + Here we pass the name of the file as the second argument to + evaluate(). This does not affect evaluation in any way; the second + argument is a general-purpose string that is used to identify the + script for debugging purposes (for example, our filename will now + show up in any uncaughtExceptionBacktrace() involving the script). + + \section1 Engine Configuration + + The globalObject() function returns the \bold {Global Object} + associated with the script engine. Properties of the Global Object + are accessible from any script code (i.e. they are global + variables). Typically, before evaluating "user" scripts, you will + want to configure a script engine by adding one or more properties + to the Global Object: + + \snippet doc/src/snippets/code/src_script_qscriptengine.cpp 3 + + Adding custom properties to the scripting environment is one of the + standard means of providing a scripting API that is specific to your + application. Usually these custom properties are objects created by + the newQObject() or newObject() functions, or constructor functions + created by newFunction(). + + \section1 Script Exceptions + + evaluate() can throw a script exception (e.g. due to a syntax + error); in that case, the return value is the value that was thrown + (typically an \c{Error} object). You can check whether the + evaluation caused an exception by calling hasUncaughtException(). In + that case, you can call toString() on the error object to obtain an + error message. The current uncaught exception is also available + through uncaughtException(). + Calling clearExceptions() will cause any uncaught exceptions to be + cleared. + + \snippet doc/src/snippets/code/src_script_qscriptengine.cpp 4 + + The checkSyntax() function can be used to determine whether code can be + usefully passed to evaluate(). + + \section1 Script Object Creation + + Use newObject() to create a standard Qt Script object; this is the + C++ equivalent of the script statement \c{new Object()}. You can use + the object-specific functionality in QScriptValue to manipulate the + script object (e.g. QScriptValue::setProperty()). Similarly, use + newArray() to create a Qt Script array object. Use newDate() to + create a \c{Date} object, and newRegExp() to create a \c{RegExp} + object. + + \section1 QObject Integration + + Use newQObject() to wrap a QObject (or subclass) + pointer. newQObject() returns a proxy script object; properties, + children, and signals and slots of the QObject are available as + properties of the proxy object. No binding code is needed because it + is done dynamically using the Qt meta object system. + + \snippet doc/src/snippets/code/src_script_qscriptengine.cpp 5 + + Use qScriptConnect() to connect a C++ signal to a script function; + this is the Qt Script equivalent of QObject::connect(). When a + script function is invoked in response to a C++ signal, it can cause + a script exception; you can connect to the signalHandlerException() + signal to catch such an exception. + + Use newQMetaObject() to wrap a QMetaObject; this gives you a "script + representation" of a QObject-based class. newQMetaObject() returns a + proxy script object; enum values of the class are available as + properties of the proxy object. You can also specify a function that + will be used to construct objects of the class (e.g. when the + constructor is invoked from a script). For classes that have a + "standard" Qt constructor, Qt Script can provide a default script + constructor for you; see scriptValueFromQMetaObject(). + + See the \l{QtScript} documentation for more information on + the QObject integration. + + \section1 Support for Custom C++ Types + + Use newVariant() to wrap a QVariant. This can be used to store + values of custom (non-QObject) C++ types that have been registered + with the Qt meta-type system. To make such types scriptable, you + typically associate a prototype (delegate) object with the C++ type + by calling setDefaultPrototype(); the prototype object defines the + scripting API for the C++ type. Unlike the QObject integration, + there is no automatic binding possible here; i.e. you have to create + the scripting API yourself, for example by using the QScriptable + class. + + Use fromScriptValue() to cast from a QScriptValue to another type, + and toScriptValue() to create a QScriptValue from another value. + You can specify how the conversion of C++ types is to be performed + with qScriptRegisterMetaType() and qScriptRegisterSequenceMetaType(). + By default, Qt Script will use QVariant to store values of custom + types. + + \section1 Importing Extensions + + Use importExtension() to import plugin-based extensions into the + engine. Call availableExtensions() to obtain a list naming all the + available extensions, and importedExtensions() to obtain a list + naming only those extensions that have been imported. + + Call pushContext() to open up a new variable scope, and popContext() + to close the current scope. This is useful if you are implementing + an extension that evaluates script code containing temporary + variable definitions (e.g. \c{var foo = 123;}) that are safe to + discard when evaluation has completed. + + \section1 Native Functions + + Use newFunction() to wrap native (C++) functions, including + constructors for your own custom types, so that these can be invoked + from script code. Such functions must have the signature + QScriptEngine::FunctionSignature. You may then pass the function as + argument to newFunction(). Here is an example of a function that + returns the sum of its first two arguments: + + \snippet doc/src/snippets/code/src_script_qscriptengine.cpp 6 + + To expose this function to script code, you can set it as a property + of the Global Object: + + \snippet doc/src/snippets/code/src_script_qscriptengine.cpp 7 + + Once this is done, script code can call your function in the exact + same manner as a "normal" script function: + + \snippet doc/src/snippets/code/src_script_qscriptengine.cpp 8 + + \section1 Long-running Scripts + + If you need to evaluate possibly long-running scripts from the main + (GUI) thread, you should first call setProcessEventsInterval() to + make sure that the GUI stays responsive. You can abort a currently + running script by calling abortEvaluation(). You can determine + whether an engine is currently running a script by calling + isEvaluating(). + + \section1 Garbage Collection + + Qt Script objects may be garbage collected when they are no longer + referenced. There is no guarantee as to when automatic garbage + collection will take place. + + The collectGarbage() function can be called to explicitly request + garbage collection. + + The reportAdditionalMemoryCost() function can be called to indicate + that a Qt Script object occupies memory that isn't managed by the + scripting environment. Reporting the additional cost makes it more + likely that the garbage collector will be triggered. This can be + useful, for example, when many custom, native Qt Script objects are + allocated. + + \section1 Core Debugging/Tracing Facilities + + Since Qt 4.4, you can be notified of events pertaining to script + execution (e.g. script function calls and statement execution) + through the QScriptEngineAgent interface; see the setAgent() + function. This can be used to implement debugging and profiling of a + QScriptEngine. + + \sa QScriptValue, QScriptContext, QScriptEngineAgent + +*/ + +/*! + \enum QScriptEngine::ValueOwnership + + This enum specifies the ownership when wrapping a C++ value, e.g. by using newQObject(). + + \value QtOwnership The standard Qt ownership rules apply, i.e. the + associated object will never be explicitly deleted by the script + engine. This is the default. (QObject ownership is explained in + \l{Object Trees & Ownership}.) + + \value ScriptOwnership The value is owned by the script + environment. The associated data will be deleted when appropriate + (i.e. after the garbage collector has discovered that there are no + more live references to the value). + + \value AutoOwnership If the associated object has a parent, the Qt + ownership rules apply (QtOwnership); otherwise, the object is + owned by the script environment (ScriptOwnership). + +*/ + +/*! + \enum QScriptEngine::QObjectWrapOption + + These flags specify options when wrapping a QObject pointer with newQObject(). + + \value ExcludeChildObjects The script object will not expose child objects as properties. + \value ExcludeSuperClassMethods The script object will not expose signals and slots inherited from the superclass. + \value ExcludeSuperClassProperties The script object will not expose properties inherited from the superclass. + \value ExcludeSuperClassContents Shorthand form for ExcludeSuperClassMethods | ExcludeSuperClassProperties + \value ExcludeDeleteLater The script object will not expose the QObject::deleteLater() slot. + \value ExcludeSlots The script object will not expose the QObject's slots. + \value AutoCreateDynamicProperties Properties that don't already exist in the QObject will be created as dynamic properties of that object, rather than as properties of the script object. + \value PreferExistingWrapperObject If a wrapper object with the requested configuration already exists, return that object. + \value SkipMethodsInEnumeration Don't include methods (signals and slots) when enumerating the object's properties. +*/ + +class QScriptSyntaxCheckResultPrivate +{ +public: + QScriptSyntaxCheckResultPrivate() { ref = 0; } + ~QScriptSyntaxCheckResultPrivate() {} + + QScriptSyntaxCheckResult::State state; + int errorColumnNumber; + int errorLineNumber; + QString errorMessage; + QBasicAtomicInt ref; +}; + +class QScriptTypeInfo +{ +public: + QScriptTypeInfo() : signature(0, '\0'), marshal(0), demarshal(0) + { } + + QByteArray signature; + QScriptEngine::MarshalFunction marshal; + QScriptEngine::DemarshalFunction demarshal; + JSC::JSValue prototype; +}; + +namespace QScript +{ + +static const qsreal D32 = 4294967296.0; + +qint32 ToInt32(qsreal n) +{ + if (qIsNaN(n) || qIsInf(n) || (n == 0)) + return 0; + + qsreal sign = (n < 0) ? -1.0 : 1.0; + qsreal abs_n = fabs(n); + + n = ::fmod(sign * ::floor(abs_n), D32); + const double D31 = D32 / 2.0; + + if (sign == -1 && n < -D31) + n += D32; + + else if (sign != -1 && n >= D31) + n -= D32; + + return qint32 (n); +} + +quint32 ToUInt32(qsreal n) +{ + if (qIsNaN(n) || qIsInf(n) || (n == 0)) + return 0; + + qsreal sign = (n < 0) ? -1.0 : 1.0; + qsreal abs_n = fabs(n); + + n = ::fmod(sign * ::floor(abs_n), D32); + + if (n < 0) + n += D32; + + return quint32 (n); +} + +quint16 ToUInt16(qsreal n) +{ + static const qsreal D16 = 65536.0; + + if (qIsNaN(n) || qIsInf(n) || (n == 0)) + return 0; + + qsreal sign = (n < 0) ? -1.0 : 1.0; + qsreal abs_n = fabs(n); + + n = ::fmod(sign * ::floor(abs_n), D16); + + if (n < 0) + n += D16; + + return quint16 (n); +} + +qsreal ToInteger(qsreal n) +{ + if (qIsNaN(n)) + return 0; + + if (n == 0 || qIsInf(n)) + return n; + + int sign = n < 0 ? -1 : 1; + return sign * ::floor(::fabs(n)); +} + +#ifdef Q_CC_MSVC +// MSVC2008 crashes if these are inlined. + +QString ToString(qsreal value) +{ + return JSC::UString::from(value); +} + +qsreal ToNumber(const QString &value) +{ + return ((JSC::UString)value).toDouble(); +} + +#endif + +static const qsreal MsPerSecond = 1000.0; + +static inline int MsFromTime(qsreal t) +{ + int r = int(::fmod(t, MsPerSecond)); + return (r >= 0) ? r : r + int(MsPerSecond); +} + +/*! + \internal + Converts a JS date value (milliseconds) to a QDateTime (local time). +*/ +QDateTime MsToDateTime(JSC::ExecState *exec, qsreal t) +{ + if (qIsNaN(t)) + return QDateTime(); + JSC::GregorianDateTime tm; + JSC::msToGregorianDateTime(exec, t, /*output UTC=*/true, tm); + int ms = MsFromTime(t); + QDateTime convertedUTC = QDateTime(QDate(tm.year + 1900, tm.month + 1, tm.monthDay), + QTime(tm.hour, tm.minute, tm.second, ms), Qt::UTC); + return convertedUTC.toLocalTime(); +} + +/*! + \internal + Converts a QDateTime to a JS date value (milliseconds). +*/ +qsreal DateTimeToMs(JSC::ExecState *exec, const QDateTime &dt) +{ + if (!dt.isValid()) + return qSNaN(); + QDateTime utc = dt.toUTC(); + QDate date = utc.date(); + QTime time = utc.time(); + JSC::GregorianDateTime tm; + tm.year = date.year() - 1900; + tm.month = date.month() - 1; + tm.monthDay = date.day(); + tm.weekDay = date.dayOfWeek(); + tm.yearDay = date.dayOfYear(); + tm.hour = time.hour(); + tm.minute = time.minute(); + tm.second = time.second(); + return JSC::gregorianDateTimeToMS(exec, tm, time.msec(), /*inputIsUTC=*/true); +} + +void GlobalClientData::mark(JSC::MarkStack& markStack) +{ + engine->mark(markStack); +} + +class TimeoutCheckerProxy : public JSC::TimeoutChecker +{ +public: + TimeoutCheckerProxy(const JSC::TimeoutChecker& originalChecker) + : JSC::TimeoutChecker(originalChecker) + , m_shouldProcessEvents(false) + , m_shouldAbortEvaluation(false) + {} + + void setShouldProcessEvents(bool shouldProcess) { m_shouldProcessEvents = shouldProcess; } + void setShouldAbort(bool shouldAbort) { m_shouldAbortEvaluation = shouldAbort; } + bool shouldAbort() { return m_shouldAbortEvaluation; } + + virtual bool didTimeOut(JSC::ExecState* exec) + { + if (JSC::TimeoutChecker::didTimeOut(exec)) + return true; + + if (m_shouldProcessEvents) + QCoreApplication::processEvents(); + + return m_shouldAbortEvaluation; + } + +private: + bool m_shouldProcessEvents; + bool m_shouldAbortEvaluation; +}; + +static int toDigit(char c) +{ + if ((c >= '0') && (c <= '9')) + return c - '0'; + else if ((c >= 'a') && (c <= 'z')) + return 10 + c - 'a'; + else if ((c >= 'A') && (c <= 'Z')) + return 10 + c - 'A'; + return -1; +} + +qsreal integerFromString(const char *buf, int size, int radix) +{ + if (size == 0) + return qSNaN(); + + qsreal sign = 1.0; + int i = 0; + if (buf[0] == '+') { + ++i; + } else if (buf[0] == '-') { + sign = -1.0; + ++i; + } + + if (((size-i) >= 2) && (buf[i] == '0')) { + if (((buf[i+1] == 'x') || (buf[i+1] == 'X')) + && (radix < 34)) { + if ((radix != 0) && (radix != 16)) + return 0; + radix = 16; + i += 2; + } else { + if (radix == 0) { + radix = 8; + ++i; + } + } + } else if (radix == 0) { + radix = 10; + } + + int j = i; + for ( ; i < size; ++i) { + int d = toDigit(buf[i]); + if ((d == -1) || (d >= radix)) + break; + } + qsreal result; + if (j == i) { + if (!qstrcmp(buf, "Infinity")) + result = qInf(); + else + result = qSNaN(); + } else { + result = 0; + qsreal multiplier = 1; + for (--i ; i >= j; --i, multiplier *= radix) + result += toDigit(buf[i]) * multiplier; + } + result *= sign; + return result; +} + +qsreal integerFromString(const QString &str, int radix) +{ + QByteArray ba = str.trimmed().toUtf8(); + return integerFromString(ba.constData(), ba.size(), radix); +} + +bool isFunction(JSC::JSValue value) +{ + if (!value || !value.isObject()) + return false; + JSC::CallData callData; + return (JSC::asObject(value)->getCallData(callData) != JSC::CallTypeNone); +} + +static JSC::JSValue JSC_HOST_CALL functionConnect(JSC::ExecState*, JSC::JSObject*, JSC::JSValue, const JSC::ArgList&); +static JSC::JSValue JSC_HOST_CALL functionDisconnect(JSC::ExecState*, JSC::JSObject*, JSC::JSValue, const JSC::ArgList&); + +JSC::JSValue JSC_HOST_CALL functionDisconnect(JSC::ExecState *exec, JSC::JSObject * /*callee*/, JSC::JSValue thisObject, const JSC::ArgList &args) +{ +#ifndef QT_NO_QOBJECT + if (args.size() == 0) { + return JSC::throwError(exec, JSC::GeneralError, "Function.prototype.disconnect: no arguments given"); + } + + if (!JSC::asObject(thisObject)->inherits(&QScript::QtFunction::info)) { + return JSC::throwError(exec, JSC::TypeError, "Function.prototype.disconnect: this object is not a signal"); + } + + QScript::QtFunction *qtSignal = static_cast<QScript::QtFunction*>(JSC::asObject(thisObject)); + + const QMetaObject *meta = qtSignal->metaObject(); + if (!meta) { + return JSC::throwError(exec, JSC::TypeError, "Function.prototype.discconnect: cannot disconnect from deleted QObject"); + } + + QMetaMethod sig = meta->method(qtSignal->initialIndex()); + if (sig.methodType() != QMetaMethod::Signal) { + QString message = QString::fromLatin1("Function.prototype.disconnect: %0::%1 is not a signal") + .arg(QLatin1String(qtSignal->metaObject()->className())) + .arg(QLatin1String(sig.signature())); + return JSC::throwError(exec, JSC::TypeError, message); + } + + QScriptEnginePrivate *engine = scriptEngineFromExec(exec); + + JSC::JSValue receiver; + JSC::JSValue slot; + JSC::JSValue arg0 = args.at(0); + if (args.size() < 2) { + slot = arg0; + } else { + receiver = arg0; + JSC::JSValue arg1 = args.at(1); + if (isFunction(arg1)) + slot = arg1; + else { + QScript::SaveFrameHelper saveFrame(engine, exec); + JSC::UString propertyName = QScriptEnginePrivate::toString(exec, arg1); + slot = QScriptEnginePrivate::property(exec, arg0, propertyName, QScriptValue::ResolvePrototype); + } + } + + if (!isFunction(slot)) { + return JSC::throwError(exec, JSC::TypeError, "Function.prototype.disconnect: target is not a function"); + } + + bool ok = engine->scriptDisconnect(thisObject, receiver, slot); + if (!ok) { + QString message = QString::fromLatin1("Function.prototype.disconnect: failed to disconnect from %0::%1") + .arg(QLatin1String(qtSignal->metaObject()->className())) + .arg(QLatin1String(sig.signature())); + return JSC::throwError(exec, JSC::GeneralError, message); + } + return JSC::jsUndefined(); +#else + Q_UNUSED(eng); + return context->throwError(QScriptContext::TypeError, + QLatin1String("Function.prototype.disconnect")); +#endif // QT_NO_QOBJECT +} + +JSC::JSValue JSC_HOST_CALL functionConnect(JSC::ExecState *exec, JSC::JSObject * /*callee*/, JSC::JSValue thisObject, const JSC::ArgList &args) +{ +#ifndef QT_NO_QOBJECT + if (args.size() == 0) { + return JSC::throwError(exec, JSC::GeneralError,"Function.prototype.connect: no arguments given"); + } + + if (!JSC::asObject(thisObject)->inherits(&QScript::QtFunction::info)) { + return JSC::throwError(exec, JSC::TypeError, "Function.prototype.connect: this object is not a signal"); + } + + QScript::QtFunction *qtSignal = static_cast<QScript::QtFunction*>(JSC::asObject(thisObject)); + + const QMetaObject *meta = qtSignal->metaObject(); + if (!meta) { + return JSC::throwError(exec, JSC::TypeError, "Function.prototype.connect: cannot connect to deleted QObject"); + } + + QMetaMethod sig = meta->method(qtSignal->initialIndex()); + if (sig.methodType() != QMetaMethod::Signal) { + QString message = QString::fromLatin1("Function.prototype.connect: %0::%1 is not a signal") + .arg(QLatin1String(qtSignal->metaObject()->className())) + .arg(QLatin1String(sig.signature())); + return JSC::throwError(exec, JSC::TypeError, message); + } + + { + QList<int> overloads = qtSignal->overloadedIndexes(); + if (!overloads.isEmpty()) { + overloads.append(qtSignal->initialIndex()); + QByteArray signature = sig.signature(); + QString message = QString::fromLatin1("Function.prototype.connect: ambiguous connect to %0::%1(); candidates are\n") + .arg(QLatin1String(qtSignal->metaObject()->className())) + .arg(QLatin1String(signature.left(signature.indexOf('(')))); + for (int i = 0; i < overloads.size(); ++i) { + QMetaMethod mtd = meta->method(overloads.at(i)); + message.append(QString::fromLatin1(" %0\n").arg(QString::fromLatin1(mtd.signature()))); + } + message.append(QString::fromLatin1("Use e.g. object['%0'].connect() to connect to a particular overload") + .arg(QLatin1String(signature))); + return JSC::throwError(exec, JSC::GeneralError, message); + } + } + + QScriptEnginePrivate *engine = scriptEngineFromExec(exec); + + JSC::JSValue receiver; + JSC::JSValue slot; + JSC::JSValue arg0 = args.at(0); + if (args.size() < 2) { + slot = arg0; + } else { + receiver = arg0; + JSC::JSValue arg1 = args.at(1); + if (isFunction(arg1)) + slot = arg1; + else { + QScript::SaveFrameHelper saveFrame(engine, exec); + JSC::UString propertyName = QScriptEnginePrivate::toString(exec, arg1); + slot = QScriptEnginePrivate::property(exec, arg0, propertyName, QScriptValue::ResolvePrototype); + } + } + + if (!isFunction(slot)) { + return JSC::throwError(exec, JSC::TypeError, "Function.prototype.connect: target is not a function"); + } + + bool ok = engine->scriptConnect(thisObject, receiver, slot, Qt::AutoConnection); + if (!ok) { + QString message = QString::fromLatin1("Function.prototype.connect: failed to connect to %0::%1") + .arg(QLatin1String(qtSignal->metaObject()->className())) + .arg(QLatin1String(sig.signature())); + return JSC::throwError(exec, JSC::GeneralError, message); + } + return JSC::jsUndefined(); +#else + Q_UNUSED(eng); + Q_UNUSED(classInfo); + return context->throwError(QScriptContext::TypeError, + QLatin1String("Function.prototype.connect")); +#endif // QT_NO_QOBJECT +} + +static JSC::JSValue JSC_HOST_CALL functionPrint(JSC::ExecState*, JSC::JSObject*, JSC::JSValue, const JSC::ArgList&); +static JSC::JSValue JSC_HOST_CALL functionGC(JSC::ExecState*, JSC::JSObject*, JSC::JSValue, const JSC::ArgList&); +static JSC::JSValue JSC_HOST_CALL functionVersion(JSC::ExecState*, JSC::JSObject*, JSC::JSValue, const JSC::ArgList&); + +JSC::JSValue JSC_HOST_CALL functionPrint(JSC::ExecState* exec, JSC::JSObject*, JSC::JSValue, const JSC::ArgList& args) +{ + QString result; + for (unsigned i = 0; i < args.size(); ++i) { + if (i != 0) + result.append(QLatin1Char(' ')); + QString s(args.at(i).toString(exec)); + if (exec->hadException()) + break; + result.append(s); + } + if (exec->hadException()) + return exec->exception(); + qDebug("%s", qPrintable(result)); + return JSC::jsUndefined(); +} + +JSC::JSValue JSC_HOST_CALL functionGC(JSC::ExecState* exec, JSC::JSObject*, JSC::JSValue, const JSC::ArgList&) +{ + QScriptEnginePrivate *engine = scriptEngineFromExec(exec); + engine->collectGarbage(); + return JSC::jsUndefined(); +} + +JSC::JSValue JSC_HOST_CALL functionVersion(JSC::ExecState *exec, JSC::JSObject*, JSC::JSValue, const JSC::ArgList&) +{ + return JSC::JSValue(exec, 1); +} + +#ifndef QT_NO_TRANSLATION + +static JSC::JSValue JSC_HOST_CALL functionQsTranslate(JSC::ExecState*, JSC::JSObject*, JSC::JSValue, const JSC::ArgList&); +static JSC::JSValue JSC_HOST_CALL functionQsTranslateNoOp(JSC::ExecState*, JSC::JSObject*, JSC::JSValue, const JSC::ArgList&); +static JSC::JSValue JSC_HOST_CALL functionQsTr(JSC::ExecState*, JSC::JSObject*, JSC::JSValue, const JSC::ArgList&); +static JSC::JSValue JSC_HOST_CALL functionQsTrNoOp(JSC::ExecState*, JSC::JSObject*, JSC::JSValue, const JSC::ArgList&); +static JSC::JSValue JSC_HOST_CALL functionQsTrId(JSC::ExecState*, JSC::JSObject*, JSC::JSValue, const JSC::ArgList&); +static JSC::JSValue JSC_HOST_CALL functionQsTrIdNoOp(JSC::ExecState*, JSC::JSObject*, JSC::JSValue, const JSC::ArgList&); + +JSC::JSValue JSC_HOST_CALL functionQsTranslate(JSC::ExecState *exec, JSC::JSObject*, JSC::JSValue, const JSC::ArgList &args) +{ + if (args.size() < 2) + return JSC::throwError(exec, JSC::GeneralError, "qsTranslate() requires at least two arguments"); + if (!args.at(0).isString()) + return JSC::throwError(exec, JSC::GeneralError, "qsTranslate(): first argument (context) must be a string"); + if (!args.at(1).isString()) + return JSC::throwError(exec, JSC::GeneralError, "qsTranslate(): second argument (text) must be a string"); + if ((args.size() > 2) && !args.at(2).isString()) + return JSC::throwError(exec, JSC::GeneralError, "qsTranslate(): third argument (comment) must be a string"); + if ((args.size() > 3) && !args.at(3).isString()) + return JSC::throwError(exec, JSC::GeneralError, "qsTranslate(): fourth argument (encoding) must be a string"); + if ((args.size() > 4) && !args.at(4).isNumber()) + return JSC::throwError(exec, JSC::GeneralError, "qsTranslate(): fifth argument (n) must be a number"); +#ifndef QT_NO_QOBJECT + JSC::UString context = args.at(0).toString(exec); +#endif + JSC::UString text = args.at(1).toString(exec); +#ifndef QT_NO_QOBJECT + JSC::UString comment; + if (args.size() > 2) + comment = args.at(2).toString(exec); + QCoreApplication::Encoding encoding = QCoreApplication::UnicodeUTF8; + if (args.size() > 3) { + JSC::UString encStr = args.at(3).toString(exec); + if (encStr == "CodecForTr") + encoding = QCoreApplication::CodecForTr; + else if (encStr == "UnicodeUTF8") + encoding = QCoreApplication::UnicodeUTF8; + else + return JSC::throwError(exec, JSC::GeneralError, QString::fromLatin1("qsTranslate(): invalid encoding '%0'").arg(encStr)); + } + int n = -1; + if (args.size() > 4) + n = args.at(4).toInt32(exec); +#endif + JSC::UString result; +#ifndef QT_NO_QOBJECT + result = QCoreApplication::translate(context.UTF8String().c_str(), + text.UTF8String().c_str(), + comment.UTF8String().c_str(), + encoding, n); +#else + result = text; +#endif + return JSC::jsString(exec, result); +} + +JSC::JSValue JSC_HOST_CALL functionQsTranslateNoOp(JSC::ExecState *, JSC::JSObject*, JSC::JSValue, const JSC::ArgList &args) +{ + if (args.size() < 2) + return JSC::jsUndefined(); + return args.at(1); +} + +JSC::JSValue JSC_HOST_CALL functionQsTr(JSC::ExecState *exec, JSC::JSObject*, JSC::JSValue, const JSC::ArgList &args) +{ + if (args.size() < 1) + return JSC::throwError(exec, JSC::GeneralError, "qsTr() requires at least one argument"); + if (!args.at(0).isString()) + return JSC::throwError(exec, JSC::GeneralError, "qsTr(): first argument (text) must be a string"); + if ((args.size() > 1) && !args.at(1).isString()) + return JSC::throwError(exec, JSC::GeneralError, "qsTr(): second argument (comment) must be a string"); + if ((args.size() > 2) && !args.at(2).isNumber()) + return JSC::throwError(exec, JSC::GeneralError, "qsTr(): third argument (n) must be a number"); +#ifndef QT_NO_QOBJECT + QScriptEnginePrivate *engine = scriptEngineFromExec(exec); + JSC::UString context; + // The first non-empty source URL in the call stack determines the translation context. + { + JSC::ExecState *frame = exec->callerFrame()->removeHostCallFrameFlag(); + while (frame) { + if (frame->codeBlock() && QScriptEnginePrivate::hasValidCodeBlockRegister(frame) + && frame->codeBlock()->source() + && !frame->codeBlock()->source()->url().isEmpty()) { + context = engine->translationContextFromUrl(frame->codeBlock()->source()->url()); + break; + } + frame = frame->callerFrame()->removeHostCallFrameFlag(); + } + } +#endif + JSC::UString text = args.at(0).toString(exec); +#ifndef QT_NO_QOBJECT + JSC::UString comment; + if (args.size() > 1) + comment = args.at(1).toString(exec); + int n = -1; + if (args.size() > 2) + n = args.at(2).toInt32(exec); +#endif + JSC::UString result; +#ifndef QT_NO_QOBJECT + result = QCoreApplication::translate(context.UTF8String().c_str(), + text.UTF8String().c_str(), + comment.UTF8String().c_str(), + QCoreApplication::UnicodeUTF8, n); +#else + result = text; +#endif + return JSC::jsString(exec, result); +} + +JSC::JSValue JSC_HOST_CALL functionQsTrNoOp(JSC::ExecState *, JSC::JSObject*, JSC::JSValue, const JSC::ArgList &args) +{ + if (args.size() < 1) + return JSC::jsUndefined(); + return args.at(0); +} + +JSC::JSValue JSC_HOST_CALL functionQsTrId(JSC::ExecState *exec, JSC::JSObject*, JSC::JSValue, const JSC::ArgList &args) +{ + if (args.size() < 1) + return JSC::throwError(exec, JSC::GeneralError, "qsTrId() requires at least one argument"); + if (!args.at(0).isString()) + return JSC::throwError(exec, JSC::TypeError, "qsTrId(): first argument (id) must be a string"); + if ((args.size() > 1) && !args.at(1).isNumber()) + return JSC::throwError(exec, JSC::TypeError, "qsTrId(): second argument (n) must be a number"); + JSC::UString id = args.at(0).toString(exec); + int n = -1; + if (args.size() > 1) + n = args.at(1).toInt32(exec); + return JSC::jsString(exec, qtTrId(id.UTF8String().c_str(), n)); +} + +JSC::JSValue JSC_HOST_CALL functionQsTrIdNoOp(JSC::ExecState *, JSC::JSObject*, JSC::JSValue, const JSC::ArgList &args) +{ + if (args.size() < 1) + return JSC::jsUndefined(); + return args.at(0); +} +#endif // QT_NO_TRANSLATION + +static JSC::JSValue JSC_HOST_CALL stringProtoFuncArg(JSC::ExecState*, JSC::JSObject*, JSC::JSValue, const JSC::ArgList&); + +JSC::JSValue JSC_HOST_CALL stringProtoFuncArg(JSC::ExecState *exec, JSC::JSObject*, JSC::JSValue thisObject, const JSC::ArgList &args) +{ + QString value(thisObject.toString(exec)); + JSC::JSValue arg = (args.size() != 0) ? args.at(0) : JSC::jsUndefined(); + QString result; + if (arg.isString()) + result = value.arg(arg.toString(exec)); + else if (arg.isNumber()) + result = value.arg(arg.toNumber(exec)); + return JSC::jsString(exec, result); +} + + +#if !defined(QT_NO_QOBJECT) && !defined(QT_NO_LIBRARY) +static QScriptValue __setupPackage__(QScriptContext *ctx, QScriptEngine *eng) +{ + QString path = ctx->argument(0).toString(); + QStringList components = path.split(QLatin1Char('.')); + QScriptValue o = eng->globalObject(); + for (int i = 0; i < components.count(); ++i) { + QString name = components.at(i); + QScriptValue oo = o.property(name); + if (!oo.isValid()) { + oo = eng->newObject(); + o.setProperty(name, oo); + } + o = oo; + } + return o; +} +#endif + +} // namespace QScript + +QScriptEnginePrivate::QScriptEnginePrivate() + : originalGlobalObjectProxy(0), currentFrame(0), + qobjectPrototype(0), qmetaobjectPrototype(0), variantPrototype(0), + activeAgent(0), agentLineNumber(-1), + registeredScriptValues(0), freeScriptValues(0), freeScriptValuesCount(0), + registeredScriptStrings(0), processEventsInterval(-1), inEval(false) +{ + qMetaTypeId<QScriptValue>(); + qMetaTypeId<QList<int> >(); +#ifndef QT_NO_QOBJECT + qMetaTypeId<QObjectList>(); +#endif + + if (!QCoreApplication::instance()) { + qFatal("QScriptEngine: Must construct a Q(Core)Application before a QScriptEngine"); + return; + } + JSC::initializeThreading(); + JSC::IdentifierTable *oldTable = JSC::currentIdentifierTable(); + globalData = JSC::JSGlobalData::create().releaseRef(); + globalData->clientData = new QScript::GlobalClientData(this); + JSC::JSGlobalObject *globalObject = new (globalData)QScript::GlobalObject(); + + JSC::ExecState* exec = globalObject->globalExec(); + + scriptObjectStructure = QScriptObject::createStructure(globalObject->objectPrototype()); + staticScopeObjectStructure = QScriptStaticScopeObject::createStructure(JSC::jsNull()); + + qobjectPrototype = new (exec) QScript::QObjectPrototype(exec, QScript::QObjectPrototype::createStructure(globalObject->objectPrototype()), globalObject->prototypeFunctionStructure()); + qobjectWrapperObjectStructure = QScriptObject::createStructure(qobjectPrototype); + + qmetaobjectPrototype = new (exec) QScript::QMetaObjectPrototype(exec, QScript::QMetaObjectPrototype::createStructure(globalObject->objectPrototype()), globalObject->prototypeFunctionStructure()); + qmetaobjectWrapperObjectStructure = QScript::QMetaObjectWrapperObject::createStructure(qmetaobjectPrototype); + + variantPrototype = new (exec) QScript::QVariantPrototype(exec, QScript::QVariantPrototype::createStructure(globalObject->objectPrototype()), globalObject->prototypeFunctionStructure()); + variantWrapperObjectStructure = QScriptObject::createStructure(variantPrototype); + + globalObject->putDirectFunction(exec, new (exec)JSC::NativeFunctionWrapper(exec, globalObject->prototypeFunctionStructure(), 1, JSC::Identifier(exec, "print"), QScript::functionPrint)); + globalObject->putDirectFunction(exec, new (exec)JSC::NativeFunctionWrapper(exec, globalObject->prototypeFunctionStructure(), 0, JSC::Identifier(exec, "gc"), QScript::functionGC)); + globalObject->putDirectFunction(exec, new (exec)JSC::NativeFunctionWrapper(exec, globalObject->prototypeFunctionStructure(), 0, JSC::Identifier(exec, "version"), QScript::functionVersion)); + + // ### rather than extending Function.prototype, consider creating a QtSignal.prototype + globalObject->functionPrototype()->putDirectFunction(exec, new (exec)JSC::NativeFunctionWrapper(exec, globalObject->prototypeFunctionStructure(), 1, JSC::Identifier(exec, "disconnect"), QScript::functionDisconnect)); + globalObject->functionPrototype()->putDirectFunction(exec, new (exec)JSC::NativeFunctionWrapper(exec, globalObject->prototypeFunctionStructure(), 1, JSC::Identifier(exec, "connect"), QScript::functionConnect)); + + JSC::TimeoutChecker* originalChecker = globalData->timeoutChecker; + globalData->timeoutChecker = new QScript::TimeoutCheckerProxy(*originalChecker); + delete originalChecker; + + currentFrame = exec; + + cachedTranslationUrl = JSC::UString(); + cachedTranslationContext = JSC::UString(); + JSC::setCurrentIdentifierTable(oldTable); +} + +QScriptEnginePrivate::~QScriptEnginePrivate() +{ + QScript::APIShim shim(this); + + //disconnect all loadedScripts and generate all jsc::debugger::scriptUnload events + QHash<intptr_t,QScript::UStringSourceProviderWithFeedback*>::const_iterator it; + for (it = loadedScripts.constBegin(); it != loadedScripts.constEnd(); ++it) + it.value()->disconnectFromEngine(); + + while (!ownedAgents.isEmpty()) + delete ownedAgents.takeFirst(); + + detachAllRegisteredScriptPrograms(); + detachAllRegisteredScriptValues(); + detachAllRegisteredScriptStrings(); + qDeleteAll(m_qobjectData); + qDeleteAll(m_typeInfos); + globalData->heap.destroy(); + globalData->deref(); + while (freeScriptValues) { + QScriptValuePrivate *p = freeScriptValues; + freeScriptValues = p->next; + qFree(p); + } +} + +QVariant QScriptEnginePrivate::jscValueToVariant(JSC::ExecState *exec, JSC::JSValue value, int targetType) +{ + QVariant v(targetType, (void *)0); + if (convertValue(exec, value, targetType, v.data())) + return v; + if (uint(targetType) == QVariant::LastType) + return toVariant(exec, value); + if (isVariant(value)) { + v = 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(); +} + +JSC::JSValue QScriptEnginePrivate::arrayFromStringList(JSC::ExecState *exec, const QStringList &lst) +{ + JSC::JSValue arr = newArray(exec, lst.size()); + for (int i = 0; i < lst.size(); ++i) + setProperty(exec, arr, i, JSC::jsString(exec, lst.at(i))); + return arr; +} + +QStringList QScriptEnginePrivate::stringListFromArray(JSC::ExecState *exec, JSC::JSValue arr) +{ + QStringList lst; + uint len = toUInt32(exec, property(exec, arr, exec->propertyNames().length)); + for (uint i = 0; i < len; ++i) + lst.append(toString(exec, property(exec, arr, i))); + return lst; +} + +JSC::JSValue QScriptEnginePrivate::arrayFromVariantList(JSC::ExecState *exec, const QVariantList &lst) +{ + JSC::JSValue arr = newArray(exec, lst.size()); + for (int i = 0; i < lst.size(); ++i) + setProperty(exec, arr, i, jscValueFromVariant(exec, lst.at(i))); + return arr; +} + +QVariantList QScriptEnginePrivate::variantListFromArray(JSC::ExecState *exec, JSC::JSArray *arr) +{ + QScriptEnginePrivate *eng = QScript::scriptEngineFromExec(exec); + if (eng->visitedConversionObjects.contains(arr)) + return QVariantList(); // Avoid recursion. + eng->visitedConversionObjects.insert(arr); + QVariantList lst; + uint len = toUInt32(exec, property(exec, arr, exec->propertyNames().length)); + for (uint i = 0; i < len; ++i) + lst.append(toVariant(exec, property(exec, arr, i))); + eng->visitedConversionObjects.remove(arr); + return lst; +} + +JSC::JSValue QScriptEnginePrivate::objectFromVariantMap(JSC::ExecState *exec, const QVariantMap &vmap) +{ + JSC::JSValue obj = JSC::constructEmptyObject(exec); + QVariantMap::const_iterator it; + for (it = vmap.constBegin(); it != vmap.constEnd(); ++it) + setProperty(exec, obj, it.key(), jscValueFromVariant(exec, it.value())); + return obj; +} + +QVariantMap QScriptEnginePrivate::variantMapFromObject(JSC::ExecState *exec, JSC::JSObject *obj) +{ + QScriptEnginePrivate *eng = QScript::scriptEngineFromExec(exec); + if (eng->visitedConversionObjects.contains(obj)) + return QVariantMap(); // Avoid recursion. + eng->visitedConversionObjects.insert(obj); + JSC::PropertyNameArray propertyNames(exec); + obj->getOwnPropertyNames(exec, propertyNames, JSC::IncludeDontEnumProperties); + QVariantMap vmap; + JSC::PropertyNameArray::const_iterator it = propertyNames.begin(); + for( ; it != propertyNames.end(); ++it) + vmap.insert(it->ustring(), toVariant(exec, property(exec, obj, *it))); + eng->visitedConversionObjects.remove(obj); + return vmap; +} + +JSC::JSValue QScriptEnginePrivate::defaultPrototype(int metaTypeId) const +{ + QScriptTypeInfo *info = m_typeInfos.value(metaTypeId); + if (!info) + return JSC::JSValue(); + return info->prototype; +} + +void QScriptEnginePrivate::setDefaultPrototype(int metaTypeId, JSC::JSValue prototype) +{ + QScriptTypeInfo *info = m_typeInfos.value(metaTypeId); + if (!info) { + info = new QScriptTypeInfo(); + m_typeInfos.insert(metaTypeId, info); + } + info->prototype = prototype; +} + +JSC::JSGlobalObject *QScriptEnginePrivate::originalGlobalObject() const +{ + return globalData->head; +} + +JSC::JSObject *QScriptEnginePrivate::customGlobalObject() const +{ + QScript::GlobalObject *glob = static_cast<QScript::GlobalObject*>(originalGlobalObject()); + return glob->customGlobalObject; +} + +JSC::JSObject *QScriptEnginePrivate::getOriginalGlobalObjectProxy() +{ + if (!originalGlobalObjectProxy) { + JSC::ExecState* exec = currentFrame; + originalGlobalObjectProxy = new (exec)QScript::OriginalGlobalObjectProxy(scriptObjectStructure, originalGlobalObject()); + } + return originalGlobalObjectProxy; +} + +JSC::JSObject *QScriptEnginePrivate::globalObject() const +{ + QScript::GlobalObject *glob = static_cast<QScript::GlobalObject*>(originalGlobalObject()); + if (glob->customGlobalObject) + return glob->customGlobalObject; + return glob; +} + +void QScriptEnginePrivate::setGlobalObject(JSC::JSObject *object) +{ + if (object == globalObject()) + return; + QScript::GlobalObject *glob = static_cast<QScript::GlobalObject*>(originalGlobalObject()); + if (object == originalGlobalObjectProxy) { + glob->customGlobalObject = 0; + // Sync the internal prototype, since JSObject::prototype() is not virtual. + glob->setPrototype(originalGlobalObjectProxy->prototype()); + } else { + Q_ASSERT(object != originalGlobalObject()); + glob->customGlobalObject = object; + // Sync the internal prototype, since JSObject::prototype() is not virtual. + glob->setPrototype(object->prototype()); + } +} + +/*! + \internal + + If the given \a value is the original global object, returns the custom + global object or a proxy to the original global object; otherwise returns \a + value. +*/ +JSC::JSValue QScriptEnginePrivate::toUsableValue(JSC::JSValue value) +{ + if (!value || !value.isObject() || !JSC::asObject(value)->isGlobalObject()) + return value; + Q_ASSERT(JSC::asObject(value) == originalGlobalObject()); + if (customGlobalObject()) + return customGlobalObject(); + if (!originalGlobalObjectProxy) + originalGlobalObjectProxy = new (currentFrame)QScript::OriginalGlobalObjectProxy(scriptObjectStructure, originalGlobalObject()); + return originalGlobalObjectProxy; +} +/*! + \internal + Return the 'this' value for a given context +*/ +JSC::JSValue QScriptEnginePrivate::thisForContext(JSC::ExecState *frame) +{ + if (frame->codeBlock() != 0) { + return frame->thisValue(); + } else if(frame == frame->lexicalGlobalObject()->globalExec()) { + return frame->globalThisValue(); + } else { + JSC::Register *thisRegister = thisRegisterForFrame(frame); + return thisRegister->jsValue(); + } +} + +JSC::Register* QScriptEnginePrivate::thisRegisterForFrame(JSC::ExecState *frame) +{ + Q_ASSERT(frame->codeBlock() == 0); // only for native calls + return frame->registers() - JSC::RegisterFile::CallFrameHeaderSize - frame->argumentCount(); +} + +/*! \internal + For native context, we use the ReturnValueRegister entry in the stackframe header to store flags. + We can do that because this header is not used as the native function return their value thought C++ + + when setting flags, NativeContext should always be set + + contextFlags returns 0 for non native context + */ +uint QScriptEnginePrivate::contextFlags(JSC::ExecState *exec) +{ + if (exec->codeBlock()) + return 0; //js function doesn't have flags + + return exec->returnValueRegister(); +} + +void QScriptEnginePrivate::setContextFlags(JSC::ExecState *exec, uint flags) +{ + Q_ASSERT(!exec->codeBlock()); + exec->registers()[JSC::RegisterFile::ReturnValueRegister] = JSC::Register::withInt(flags); +} + + +void QScriptEnginePrivate::mark(JSC::MarkStack& markStack) +{ + Q_Q(QScriptEngine); + + if (originalGlobalObject()) { + markStack.append(originalGlobalObject()); + markStack.append(globalObject()); + if (originalGlobalObjectProxy) + markStack.append(originalGlobalObjectProxy); + } + + if (qobjectPrototype) + markStack.append(qobjectPrototype); + if (qmetaobjectPrototype) + markStack.append(qmetaobjectPrototype); + if (variantPrototype) + markStack.append(variantPrototype); + + { + QScriptValuePrivate *it; + for (it = registeredScriptValues; it != 0; it = it->next) { + if (it->isJSC()) + markStack.append(it->jscValue); + } + } + + { + QHash<int, QScriptTypeInfo*>::const_iterator it; + for (it = m_typeInfos.constBegin(); it != m_typeInfos.constEnd(); ++it) { + if ((*it)->prototype) + markStack.append((*it)->prototype); + } + } + + if (q) { + QScriptContext *context = q->currentContext(); + + while (context) { + JSC::ScopeChainNode *node = frameForContext(context)->scopeChain(); + JSC::ScopeChainIterator it(node); + for (it = node->begin(); it != node->end(); ++it) { + JSC::JSObject *object = *it; + if (object) + markStack.append(object); + } + + context = context->parentContext(); + } + } + +#ifndef QT_NO_QOBJECT + markStack.drain(); // make sure everything is marked before marking qobject data + { + QHash<QObject*, QScript::QObjectData*>::const_iterator it; + for (it = m_qobjectData.constBegin(); it != m_qobjectData.constEnd(); ++it) { + QScript::QObjectData *qdata = it.value(); + qdata->mark(markStack); + } + } +#endif +} + +bool QScriptEnginePrivate::isCollecting() const +{ + return globalData->heap.isBusy(); +} + +void QScriptEnginePrivate::collectGarbage() +{ + QScript::APIShim shim(this); + globalData->heap.collectAllGarbage(); +} + +void QScriptEnginePrivate::reportAdditionalMemoryCost(int size) +{ + if (size > 0) + globalData->heap.reportExtraMemoryCost(size); +} + +QScript::TimeoutCheckerProxy *QScriptEnginePrivate::timeoutChecker() const +{ + return static_cast<QScript::TimeoutCheckerProxy*>(globalData->timeoutChecker); +} + +void QScriptEnginePrivate::agentDeleted(QScriptEngineAgent *agent) +{ + ownedAgents.removeOne(agent); + if (activeAgent == agent) { + QScriptEngineAgentPrivate::get(agent)->detach(); + activeAgent = 0; + } +} + +JSC::JSValue QScriptEnginePrivate::evaluateHelper(JSC::ExecState *exec, intptr_t sourceId, + JSC::EvalExecutable *executable, + bool &compile) +{ + Q_Q(QScriptEngine); + QBoolBlocker inEvalBlocker(inEval, true); + q->currentContext()->activationObject(); //force the creation of a context for native function; + + JSC::Debugger* debugger = originalGlobalObject()->debugger(); + if (debugger) + debugger->evaluateStart(sourceId); + + q->clearExceptions(); + JSC::DynamicGlobalObjectScope dynamicGlobalObjectScope(exec, exec->scopeChain()->globalObject); + + if (compile) { + JSC::JSObject* error = executable->compile(exec, exec->scopeChain()); + if (error) { + compile = false; + exec->setException(error); + + if (debugger) { + debugger->exceptionThrow(JSC::DebuggerCallFrame(exec, error), sourceId, false); + debugger->evaluateStop(error, sourceId); + } + + return error; + } + } + + JSC::JSValue thisValue = thisForContext(exec); + JSC::JSObject* thisObject = (!thisValue || thisValue.isUndefinedOrNull()) + ? exec->dynamicGlobalObject() : thisValue.toObject(exec); + JSC::JSValue exceptionValue; + timeoutChecker()->setShouldAbort(false); + if (processEventsInterval > 0) + timeoutChecker()->reset(); + + JSC::JSValue result = exec->interpreter()->execute(executable, exec, thisObject, exec->scopeChain(), &exceptionValue); + + if (timeoutChecker()->shouldAbort()) { + if (abortResult.isError()) + exec->setException(scriptValueToJSCValue(abortResult)); + + if (debugger) + debugger->evaluateStop(scriptValueToJSCValue(abortResult), sourceId); + + return scriptValueToJSCValue(abortResult); + } + + if (exceptionValue) { + exec->setException(exceptionValue); + + if (debugger) + debugger->evaluateStop(exceptionValue, sourceId); + + return exceptionValue; + } + + if (debugger) + debugger->evaluateStop(result, sourceId); + + Q_ASSERT(!exec->hadException()); + return result; +} + +#ifndef QT_NO_QOBJECT + +JSC::JSValue QScriptEnginePrivate::newQObject( + QObject *object, QScriptEngine::ValueOwnership ownership, + const QScriptEngine::QObjectWrapOptions &options) +{ + if (!object) + return JSC::jsNull(); + JSC::ExecState* exec = currentFrame; + QScript::QObjectData *data = qobjectData(object); + bool preferExisting = (options & QScriptEngine::PreferExistingWrapperObject) != 0; + QScriptEngine::QObjectWrapOptions opt = options & ~QScriptEngine::PreferExistingWrapperObject; + QScriptObject *result = 0; + if (preferExisting) { + result = data->findWrapper(ownership, opt); + if (result) + return result; + } + result = new (exec) QScriptObject(qobjectWrapperObjectStructure); + if (preferExisting) + data->registerWrapper(result, ownership, opt); + result->setDelegate(new QScript::QObjectDelegate(object, ownership, options)); + /*if (setDefaultPrototype)*/ { + const QMetaObject *meta = object->metaObject(); + while (meta) { + QByteArray typeString = meta->className(); + typeString.append('*'); + int typeId = QMetaType::type(typeString); + if (typeId != 0) { + JSC::JSValue proto = defaultPrototype(typeId); + if (proto) { + result->setPrototype(proto); + break; + } + } + meta = meta->superClass(); + } + } + return result; +} + +JSC::JSValue QScriptEnginePrivate::newQMetaObject( + const QMetaObject *metaObject, JSC::JSValue ctor) +{ + if (!metaObject) + return JSC::jsNull(); + JSC::ExecState* exec = currentFrame; + QScript::QMetaObjectWrapperObject *result = new (exec) QScript::QMetaObjectWrapperObject(exec, metaObject, ctor, qmetaobjectWrapperObjectStructure); + return result; +} + +bool QScriptEnginePrivate::convertToNativeQObject(JSC::ExecState *exec, JSC::JSValue value, + const QByteArray &targetType, + void **result) +{ + if (!targetType.endsWith('*')) + return false; + if (QObject *qobject = toQObject(exec, value)) { + int start = targetType.startsWith("const ") ? 6 : 0; + QByteArray className = targetType.mid(start, targetType.size()-start-1); + if (void *instance = qobject->qt_metacast(className)) { + *result = instance; + return true; + } + } + return false; +} + +QScript::QObjectData *QScriptEnginePrivate::qobjectData(QObject *object) +{ + QHash<QObject*, QScript::QObjectData*>::const_iterator it; + it = m_qobjectData.constFind(object); + if (it != m_qobjectData.constEnd()) + return it.value(); + + QScript::QObjectData *data = new QScript::QObjectData(this); + m_qobjectData.insert(object, data); + QObject::connect(object, SIGNAL(destroyed(QObject*)), + q_func(), SLOT(_q_objectDestroyed(QObject*))); + return data; +} + +void QScriptEnginePrivate::_q_objectDestroyed(QObject *object) +{ + QHash<QObject*, QScript::QObjectData*>::iterator it; + it = m_qobjectData.find(object); + Q_ASSERT(it != m_qobjectData.end()); + QScript::QObjectData *data = it.value(); + m_qobjectData.erase(it); + delete data; +} + +void QScriptEnginePrivate::disposeQObject(QObject *object) +{ + // TODO +/* if (isCollecting()) { + // wait until we're done with GC before deleting it + int index = m_qobjectsToBeDeleted.indexOf(object); + if (index == -1) + m_qobjectsToBeDeleted.append(object); + } else*/ { + delete object; + } +} + +void QScriptEnginePrivate::emitSignalHandlerException() +{ + Q_Q(QScriptEngine); + emit q->signalHandlerException(q->uncaughtException()); +} + +bool QScriptEnginePrivate::scriptConnect(QObject *sender, const char *signal, + JSC::JSValue receiver, JSC::JSValue function, + Qt::ConnectionType type) +{ + Q_ASSERT(sender); + Q_ASSERT(signal); + const QMetaObject *meta = sender->metaObject(); + int index = meta->indexOfSignal(QMetaObject::normalizedSignature(signal+1)); + if (index == -1) + return false; + return scriptConnect(sender, index, receiver, function, /*wrapper=*/JSC::JSValue(), type); +} + +bool QScriptEnginePrivate::scriptDisconnect(QObject *sender, const char *signal, + JSC::JSValue receiver, JSC::JSValue function) +{ + Q_ASSERT(sender); + Q_ASSERT(signal); + const QMetaObject *meta = sender->metaObject(); + int index = meta->indexOfSignal(QMetaObject::normalizedSignature(signal+1)); + if (index == -1) + return false; + return scriptDisconnect(sender, index, receiver, function); +} + +bool QScriptEnginePrivate::scriptConnect(QObject *sender, int signalIndex, + JSC::JSValue receiver, JSC::JSValue function, + JSC::JSValue senderWrapper, + Qt::ConnectionType type) +{ + QScript::QObjectData *data = qobjectData(sender); + return data->addSignalHandler(sender, signalIndex, receiver, function, senderWrapper, type); +} + +bool QScriptEnginePrivate::scriptDisconnect(QObject *sender, int signalIndex, + JSC::JSValue receiver, JSC::JSValue function) +{ + QScript::QObjectData *data = qobjectData(sender); + if (!data) + return false; + return data->removeSignalHandler(sender, signalIndex, receiver, function); +} + +bool QScriptEnginePrivate::scriptConnect(JSC::JSValue signal, JSC::JSValue receiver, + JSC::JSValue function, Qt::ConnectionType type) +{ + QScript::QtFunction *fun = static_cast<QScript::QtFunction*>(JSC::asObject(signal)); + int index = fun->mostGeneralMethod(); + return scriptConnect(fun->qobject(), index, receiver, function, fun->wrapperObject(), type); +} + +bool QScriptEnginePrivate::scriptDisconnect(JSC::JSValue signal, JSC::JSValue receiver, + JSC::JSValue function) +{ + QScript::QtFunction *fun = static_cast<QScript::QtFunction*>(JSC::asObject(signal)); + int index = fun->mostGeneralMethod(); + return scriptDisconnect(fun->qobject(), index, receiver, function); +} + +#endif + +void QScriptEnginePrivate::detachAllRegisteredScriptPrograms() +{ + QSet<QScriptProgramPrivate*>::const_iterator it; + for (it = registeredScriptPrograms.constBegin(); it != registeredScriptPrograms.constEnd(); ++it) + (*it)->detachFromEngine(); + registeredScriptPrograms.clear(); +} + +void QScriptEnginePrivate::detachAllRegisteredScriptValues() +{ + QScriptValuePrivate *it; + QScriptValuePrivate *next; + for (it = registeredScriptValues; it != 0; it = next) { + it->detachFromEngine(); + next = it->next; + it->prev = 0; + it->next = 0; + } + registeredScriptValues = 0; +} + +void QScriptEnginePrivate::detachAllRegisteredScriptStrings() +{ + QScriptStringPrivate *it; + QScriptStringPrivate *next; + for (it = registeredScriptStrings; it != 0; it = next) { + it->detachFromEngine(); + next = it->next; + it->prev = 0; + it->next = 0; + } + registeredScriptStrings = 0; +} + +#ifndef QT_NO_REGEXP + +Q_CORE_EXPORT QString qt_regexp_toCanonical(const QString &, QRegExp::PatternSyntax); + +JSC::JSValue QScriptEnginePrivate::newRegExp(JSC::ExecState *exec, const QRegExp ®exp) +{ + JSC::JSValue buf[2]; + JSC::ArgList args(buf, sizeof(buf)); + + //convert the pattern to a ECMAScript pattern + QString pattern = qt_regexp_toCanonical(regexp.pattern(), regexp.patternSyntax()); + if (regexp.isMinimal()) { + QString ecmaPattern; + int len = pattern.length(); + ecmaPattern.reserve(len); + int i = 0; + const QChar *wc = pattern.unicode(); + bool inBracket = false; + while (i < len) { + QChar c = wc[i++]; + ecmaPattern += c; + switch (c.unicode()) { + case '?': + case '+': + case '*': + case '}': + if (!inBracket) + ecmaPattern += QLatin1Char('?'); + break; + case '\\': + if (i < len) + ecmaPattern += wc[i++]; + break; + case '[': + inBracket = true; + break; + case ']': + inBracket = false; + break; + default: + break; + } + } + pattern = ecmaPattern; + } + + JSC::UString jscPattern = pattern; + QString flags; + if (regexp.caseSensitivity() == Qt::CaseInsensitive) + flags.append(QLatin1Char('i')); + JSC::UString jscFlags = flags; + buf[0] = JSC::jsString(exec, jscPattern); + buf[1] = JSC::jsString(exec, jscFlags); + return JSC::constructRegExp(exec, args); +} + +#endif + +JSC::JSValue QScriptEnginePrivate::newRegExp(JSC::ExecState *exec, const QString &pattern, const QString &flags) +{ + JSC::JSValue buf[2]; + JSC::ArgList args(buf, sizeof(buf)); + JSC::UString jscPattern = pattern; + QString strippedFlags; + if (flags.contains(QLatin1Char('i'))) + strippedFlags += QLatin1Char('i'); + if (flags.contains(QLatin1Char('m'))) + strippedFlags += QLatin1Char('m'); + if (flags.contains(QLatin1Char('g'))) + strippedFlags += QLatin1Char('g'); + JSC::UString jscFlags = strippedFlags; + buf[0] = JSC::jsString(exec, jscPattern); + buf[1] = JSC::jsString(exec, jscFlags); + return JSC::constructRegExp(exec, args); +} + +JSC::JSValue QScriptEnginePrivate::newVariant(const QVariant &value) +{ + QScriptObject *obj = new (currentFrame) QScriptObject(variantWrapperObjectStructure); + obj->setDelegate(new QScript::QVariantDelegate(value)); + JSC::JSValue proto = defaultPrototype(value.userType()); + if (proto) + obj->setPrototype(proto); + return obj; +} + +JSC::JSValue QScriptEnginePrivate::newVariant(JSC::JSValue objectValue, + const QVariant &value) +{ + if (!isObject(objectValue)) + return newVariant(value); + JSC::JSObject *jscObject = JSC::asObject(objectValue); + if (!jscObject->inherits(&QScriptObject::info)) { + qWarning("QScriptEngine::newVariant(): changing class of non-QScriptObject not supported"); + return JSC::JSValue(); + } + QScriptObject *jscScriptObject = static_cast<QScriptObject*>(jscObject); + if (!isVariant(objectValue)) { + jscScriptObject->setDelegate(new QScript::QVariantDelegate(value)); + } else { + setVariantValue(objectValue, value); + } + return objectValue; +} + +#ifndef QT_NO_REGEXP + +QRegExp QScriptEnginePrivate::toRegExp(JSC::ExecState *exec, JSC::JSValue value) +{ + if (!isRegExp(value)) + return QRegExp(); + QString pattern = toString(exec, property(exec, value, "source", QScriptValue::ResolvePrototype)); + Qt::CaseSensitivity kase = Qt::CaseSensitive; + if (toBool(exec, property(exec, value, "ignoreCase", QScriptValue::ResolvePrototype))) + kase = Qt::CaseInsensitive; + return QRegExp(pattern, kase, QRegExp::RegExp2); +} + +#endif + +QVariant QScriptEnginePrivate::toVariant(JSC::ExecState *exec, JSC::JSValue value) +{ + if (!value) { + return QVariant(); + } else if (isObject(value)) { + if (isVariant(value)) + return variantValue(value); +#ifndef QT_NO_QOBJECT + else if (isQObject(value)) + return QVariant::fromValue(toQObject(exec, value)); +#endif + else if (isDate(value)) + return QVariant(toDateTime(exec, value)); +#ifndef QT_NO_REGEXP + else if (isRegExp(value)) + return QVariant(toRegExp(exec, value)); +#endif + else if (isArray(value)) + return variantListFromArray(exec, JSC::asArray(value)); + else if (QScriptDeclarativeClass *dc = declarativeClass(value)) + return dc->toVariant(declarativeObject(value)); + return variantMapFromObject(exec, JSC::asObject(value)); + } else if (value.isInt32()) { + return QVariant(toInt32(exec, value)); + } else if (value.isDouble()) { + return QVariant(toNumber(exec, value)); + } else if (value.isString()) { + return QVariant(toString(exec, value)); + } else if (value.isBoolean()) { + return QVariant(toBool(exec, value)); + } + return QVariant(); +} + +JSC::JSValue QScriptEnginePrivate::propertyHelper(JSC::ExecState *exec, JSC::JSValue value, const JSC::Identifier &id, int resolveMode) +{ + JSC::JSValue result; + if (!(resolveMode & QScriptValue::ResolvePrototype)) { + // Look in the object's own properties + JSC::JSObject *object = JSC::asObject(value); + JSC::PropertySlot slot(object); + if (object->getOwnPropertySlot(exec, id, slot)) + result = slot.getValue(exec, id); + } + if (!result && (resolveMode & QScriptValue::ResolveScope)) { + // ### check if it's a function object and look in the scope chain + JSC::JSValue scope = property(exec, value, "__qt_scope__", QScriptValue::ResolveLocal); + if (isObject(scope)) + result = property(exec, scope, id, resolveMode); + } + return result; +} + +JSC::JSValue QScriptEnginePrivate::propertyHelper(JSC::ExecState *exec, JSC::JSValue value, quint32 index, int resolveMode) +{ + JSC::JSValue result; + if (!(resolveMode & QScriptValue::ResolvePrototype)) { + // Look in the object's own properties + JSC::JSObject *object = JSC::asObject(value); + JSC::PropertySlot slot(object); + if (object->getOwnPropertySlot(exec, index, slot)) + result = slot.getValue(exec, index); + } + return result; +} + +void QScriptEnginePrivate::setProperty(JSC::ExecState *exec, JSC::JSValue objectValue, const JSC::Identifier &id, + JSC::JSValue value, const QScriptValue::PropertyFlags &flags) +{ + JSC::JSObject *thisObject = JSC::asObject(objectValue); + JSC::JSValue setter = thisObject->lookupSetter(exec, id); + JSC::JSValue getter = thisObject->lookupGetter(exec, id); + if ((flags & QScriptValue::PropertyGetter) || (flags & QScriptValue::PropertySetter)) { + if (!value) { + // deleting getter/setter + if ((flags & QScriptValue::PropertyGetter) && (flags & QScriptValue::PropertySetter)) { + // deleting both: just delete the property + thisObject->deleteProperty(exec, id); + } else if (flags & QScriptValue::PropertyGetter) { + // preserve setter, if there is one + thisObject->deleteProperty(exec, id); + if (setter && setter.isObject()) + thisObject->defineSetter(exec, id, JSC::asObject(setter)); + } else { // flags & QScriptValue::PropertySetter + // preserve getter, if there is one + thisObject->deleteProperty(exec, id); + if (getter && getter.isObject()) + thisObject->defineGetter(exec, id, JSC::asObject(getter)); + } + } else { + if (value.isObject()) { // ### should check if it has callData() + // defining getter/setter + if (id == exec->propertyNames().underscoreProto) { + qWarning("QScriptValue::setProperty() failed: " + "cannot set getter or setter of native property `__proto__'"); + } else { + if (flags & QScriptValue::PropertyGetter) + thisObject->defineGetter(exec, id, JSC::asObject(value)); + if (flags & QScriptValue::PropertySetter) + thisObject->defineSetter(exec, id, JSC::asObject(value)); + } + } else { + qWarning("QScriptValue::setProperty(): getter/setter must be a function"); + } + } + } else { + // setting the value + if (getter && getter.isObject() && !(setter && setter.isObject())) { + qWarning("QScriptValue::setProperty() failed: " + "property '%s' has a getter but no setter", + qPrintable(QString(id.ustring()))); + return; + } + if (!value) { + // ### check if it's a getter/setter property + thisObject->deleteProperty(exec, id); + } else if (flags != QScriptValue::KeepExistingFlags) { + if (thisObject->hasOwnProperty(exec, id)) + thisObject->deleteProperty(exec, id); // ### hmmm - can't we just update the attributes? + thisObject->putWithAttributes(exec, id, value, propertyFlagsToJSCAttributes(flags)); + } else { + JSC::PutPropertySlot slot; + thisObject->put(exec, id, value, slot); + } + } +} + +void QScriptEnginePrivate::setProperty(JSC::ExecState *exec, JSC::JSValue objectValue, quint32 index, + JSC::JSValue value, const QScriptValue::PropertyFlags &flags) +{ + if (!value) { + JSC::asObject(objectValue)->deleteProperty(exec, index); + } else { + if ((flags & QScriptValue::PropertyGetter) || (flags & QScriptValue::PropertySetter)) { + // fall back to string-based setProperty(), since there is no + // JSC::JSObject::defineGetter(unsigned) + setProperty(exec, objectValue, JSC::Identifier::from(exec, index), value, flags); + } else { + if (flags != QScriptValue::KeepExistingFlags) { + // if (JSC::asObject(d->jscValue)->hasOwnProperty(exec, arrayIndex)) + // JSC::asObject(d->jscValue)->deleteProperty(exec, arrayIndex); + unsigned attribs = 0; + if (flags & QScriptValue::ReadOnly) + attribs |= JSC::ReadOnly; + if (flags & QScriptValue::SkipInEnumeration) + attribs |= JSC::DontEnum; + if (flags & QScriptValue::Undeletable) + attribs |= JSC::DontDelete; + attribs |= flags & QScriptValue::UserRange; + JSC::asObject(objectValue)->putWithAttributes(exec, index, value, attribs); + } else { + JSC::asObject(objectValue)->put(exec, index, value); + } + } + } +} + +QScriptValue::PropertyFlags QScriptEnginePrivate::propertyFlags(JSC::ExecState *exec, JSC::JSValue value, const JSC::Identifier &id, + const QScriptValue::ResolveFlags &mode) +{ + JSC::JSObject *object = JSC::asObject(value); + unsigned attribs = 0; + JSC::PropertyDescriptor descriptor; + if (object->getOwnPropertyDescriptor(exec, id, descriptor)) + attribs = descriptor.attributes(); + else { + if ((mode & QScriptValue::ResolvePrototype) && object->prototype() && object->prototype().isObject()) { + JSC::JSValue proto = object->prototype(); + return propertyFlags(exec, proto, id, mode); + } + return 0; + } + QScriptValue::PropertyFlags result = 0; + if (attribs & JSC::ReadOnly) + result |= QScriptValue::ReadOnly; + if (attribs & JSC::DontEnum) + result |= QScriptValue::SkipInEnumeration; + if (attribs & JSC::DontDelete) + result |= QScriptValue::Undeletable; + //We cannot rely on attribs JSC::Setter/Getter because they are not necesserly set by JSC (bug?) + if (attribs & JSC::Getter || !object->lookupGetter(exec, id).isUndefinedOrNull()) + result |= QScriptValue::PropertyGetter; + if (attribs & JSC::Setter || !object->lookupSetter(exec, id).isUndefinedOrNull()) + result |= QScriptValue::PropertySetter; +#ifndef QT_NO_QOBJECT + if (attribs & QScript::QObjectMemberAttribute) + result |= QScriptValue::QObjectMember; +#endif + result |= QScriptValue::PropertyFlag(attribs & QScriptValue::UserRange); + return result; +} + +QScriptString QScriptEnginePrivate::toStringHandle(const JSC::Identifier &name) +{ + QScriptString result; + QScriptStringPrivate *p = new QScriptStringPrivate(this, name, QScriptStringPrivate::HeapAllocated); + QScriptStringPrivate::init(result, p); + registerScriptString(p); + return result; +} + +#ifdef QT_NO_QOBJECT + +QScriptEngine::QScriptEngine() + : d_ptr(new QScriptEnginePrivate) +{ + d_ptr->q_ptr = this; +} + +/*! \internal +*/ +QScriptEngine::QScriptEngine(QScriptEnginePrivate &dd) + : d_ptr(&dd) +{ + d_ptr->q_ptr = this; +} +#else + +/*! + Constructs a QScriptEngine object. + + The globalObject() is initialized to have properties as described in + \l{ECMA-262}, Section 15.1. +*/ +QScriptEngine::QScriptEngine() + : QObject(*new QScriptEnginePrivate, 0) +{ +} + +/*! + Constructs a QScriptEngine object with the given \a parent. + + The globalObject() is initialized to have properties as described in + \l{ECMA-262}, Section 15.1. +*/ + +QScriptEngine::QScriptEngine(QObject *parent) + : QObject(*new QScriptEnginePrivate, parent) +{ +} + +/*! \internal +*/ +QScriptEngine::QScriptEngine(QScriptEnginePrivate &dd, QObject *parent) + : QObject(dd, parent) +{ +} +#endif + +/*! + Destroys this QScriptEngine. +*/ +QScriptEngine::~QScriptEngine() +{ +#ifdef QT_NO_QOBJECT + delete d_ptr; + d_ptr = 0; +#endif +} + +/*! + Returns this engine's Global Object. + + By default, the Global Object contains the built-in objects that are + part of \l{ECMA-262}, such as Math, Date and String. Additionally, + you can set properties of the Global Object to make your own + extensions available to all script code. Non-local variables in + script code will be created as properties of the Global Object, as + well as local variables in global code. +*/ +QScriptValue QScriptEngine::globalObject() const +{ + Q_D(const QScriptEngine); + QScript::APIShim shim(const_cast<QScriptEnginePrivate*>(d)); + JSC::JSObject *result = d->globalObject(); + return const_cast<QScriptEnginePrivate*>(d)->scriptValueFromJSCValue(result); +} + +/*! + \since 4.5 + + Sets this engine's Global Object to be the given \a object. + If \a object is not a valid script object, this function does + nothing. + + When setting a custom global object, you may want to use + QScriptValueIterator to copy the properties of the standard Global + Object; alternatively, you can set the internal prototype of your + custom object to be the original Global Object. +*/ +void QScriptEngine::setGlobalObject(const QScriptValue &object) +{ + Q_D(QScriptEngine); + if (!object.isObject()) + return; + QScript::APIShim shim(d); + JSC::JSObject *jscObject = JSC::asObject(d->scriptValueToJSCValue(object)); + d->setGlobalObject(jscObject); +} + +/*! + Returns a QScriptValue of the primitive type Null. + + \sa undefinedValue() +*/ +QScriptValue QScriptEngine::nullValue() +{ + Q_D(QScriptEngine); + return d->scriptValueFromJSCValue(JSC::jsNull()); +} + +/*! + Returns a QScriptValue of the primitive type Undefined. + + \sa nullValue() +*/ +QScriptValue QScriptEngine::undefinedValue() +{ + Q_D(QScriptEngine); + return d->scriptValueFromJSCValue(JSC::jsUndefined()); +} + +/*! + Creates a constructor function from \a fun, with the given \a length. + The \c{prototype} property of the resulting function is set to be the + given \a prototype. The \c{constructor} property of \a prototype is + set to be the resulting function. + + When a function is called as a constructor (e.g. \c{new Foo()}), the + `this' object associated with the function call is the new object + that the function is expected to initialize; the prototype of this + default constructed object will be the function's public + \c{prototype} property. If you always want the function to behave as + a constructor (e.g. \c{Foo()} should also create a new object), or + if you need to create your own object rather than using the default + `this' object, you should make sure that the prototype of your + object is set correctly; either by setting it manually, or, when + wrapping a custom type, by having registered the defaultPrototype() + of that type. Example: + + \snippet doc/src/snippets/code/src_script_qscriptengine.cpp 9 + + To wrap a custom type and provide a constructor for it, you'd typically + do something like this: + + \snippet doc/src/snippets/code/src_script_qscriptengine.cpp 10 +*/ +QScriptValue QScriptEngine::newFunction(QScriptEngine::FunctionSignature fun, + const QScriptValue &prototype, + int length) +{ + Q_D(QScriptEngine); + QScript::APIShim shim(d); + JSC::ExecState* exec = d->currentFrame; + JSC::JSValue function = new (exec)QScript::FunctionWrapper(exec, length, JSC::Identifier(exec, ""), fun); + QScriptValue result = d->scriptValueFromJSCValue(function); + result.setProperty(QLatin1String("prototype"), prototype, + QScriptValue::Undeletable | QScriptValue::SkipInEnumeration); + const_cast<QScriptValue&>(prototype) + .setProperty(QLatin1String("constructor"), result, QScriptValue::SkipInEnumeration); + return result; +} + +#ifndef QT_NO_REGEXP + +/*! + Creates a QtScript object of class RegExp with the given + \a regexp. + + \sa QScriptValue::toRegExp() +*/ +QScriptValue QScriptEngine::newRegExp(const QRegExp ®exp) +{ + Q_D(QScriptEngine); + QScript::APIShim shim(d); + return d->scriptValueFromJSCValue(d->newRegExp(d->currentFrame, regexp)); +} + +#endif // QT_NO_REGEXP + +/*! + Creates a QtScript object holding the given variant \a value. + + If a default prototype has been registered with the meta type id of + \a value, then the prototype of the created object will be that + prototype; otherwise, the prototype will be the Object prototype + object. + + \sa setDefaultPrototype(), QScriptValue::toVariant(), reportAdditionalMemoryCost() +*/ +QScriptValue QScriptEngine::newVariant(const QVariant &value) +{ + Q_D(QScriptEngine); + QScript::APIShim shim(d); + return d->scriptValueFromJSCValue(d->newVariant(value)); +} + +/*! + \since 4.4 + \overload + + Initializes the given Qt Script \a object to hold the given variant + \a value, and returns the \a object. + + This function enables you to "promote" a plain Qt Script object + (created by the newObject() function) to a variant, or to replace + the variant contained inside an object previously created by the + newVariant() function. + + The prototype() of the \a object will remain unchanged. + + If \a object is not an object, this function behaves like the normal + newVariant(), i.e. it creates a new script object and returns it. + + This function is useful when you want to provide a script + constructor for a C++ type. If your constructor is invoked in a + \c{new} expression (QScriptContext::isCalledAsConstructor() returns + true), you can pass QScriptContext::thisObject() (the default + constructed script object) to this function to initialize the new + object. + + \sa reportAdditionalMemoryCost() +*/ +QScriptValue QScriptEngine::newVariant(const QScriptValue &object, + const QVariant &value) +{ + Q_D(QScriptEngine); + QScript::APIShim shim(d); + JSC::JSValue jsObject = d->scriptValueToJSCValue(object); + return d->scriptValueFromJSCValue(d->newVariant(jsObject, value)); +} + +#ifndef QT_NO_QOBJECT +/*! + Creates a QtScript object that wraps the given QObject \a + object, using the given \a ownership. The given \a options control + various aspects of the interaction with the resulting script object. + + Signals and slots, properties and children of \a object are + available as properties of the created QScriptValue. For more + information, see the \l{QtScript} documentation. + + If \a object is a null pointer, this function returns nullValue(). + + If a default prototype has been registered for the \a object's class + (or its superclass, recursively), the prototype of the new script + object will be set to be that default prototype. + + If the given \a object is deleted outside of QtScript's control, any + attempt to access the deleted QObject's members through the QtScript + wrapper object (either by script code or C++) will result in a + script exception. + + \sa QScriptValue::toQObject(), reportAdditionalMemoryCost() +*/ +QScriptValue QScriptEngine::newQObject(QObject *object, ValueOwnership ownership, + const QObjectWrapOptions &options) +{ + Q_D(QScriptEngine); + QScript::APIShim shim(d); + JSC::JSValue jscQObject = d->newQObject(object, ownership, options); + return d->scriptValueFromJSCValue(jscQObject); +} + +/*! + \since 4.4 + \overload + + Initializes the given \a scriptObject to hold the given \a qtObject, + and returns the \a scriptObject. + + This function enables you to "promote" a plain Qt Script object + (created by the newObject() function) to a QObject proxy, or to + replace the QObject contained inside an object previously created by + the newQObject() function. + + The prototype() of the \a scriptObject will remain unchanged. + + If \a scriptObject is not an object, this function behaves like the + normal newQObject(), i.e. it creates a new script object and returns + it. + + This function is useful when you want to provide a script + constructor for a QObject-based class. If your constructor is + invoked in a \c{new} expression + (QScriptContext::isCalledAsConstructor() returns true), you can pass + QScriptContext::thisObject() (the default constructed script object) + to this function to initialize the new object. + + \sa reportAdditionalMemoryCost() +*/ +QScriptValue QScriptEngine::newQObject(const QScriptValue &scriptObject, + QObject *qtObject, + ValueOwnership ownership, + const QObjectWrapOptions &options) +{ + Q_D(QScriptEngine); + if (!scriptObject.isObject()) + return newQObject(qtObject, ownership, options); + QScript::APIShim shim(d); + JSC::JSObject *jscObject = JSC::asObject(QScriptValuePrivate::get(scriptObject)->jscValue); + if (!jscObject->inherits(&QScriptObject::info)) { + qWarning("QScriptEngine::newQObject(): changing class of non-QScriptObject not supported"); + return QScriptValue(); + } + QScriptObject *jscScriptObject = static_cast<QScriptObject*>(jscObject); + if (!scriptObject.isQObject()) { + jscScriptObject->setDelegate(new QScript::QObjectDelegate(qtObject, ownership, options)); + } else { + QScript::QObjectDelegate *delegate = static_cast<QScript::QObjectDelegate*>(jscScriptObject->delegate()); + delegate->setValue(qtObject); + delegate->setOwnership(ownership); + delegate->setOptions(options); + } + return scriptObject; +} + +#endif // QT_NO_QOBJECT + +/*! + Creates a QtScript object of class Object. + + The prototype of the created object will be the Object + prototype object. + + \sa newArray(), QScriptValue::setProperty() +*/ +QScriptValue QScriptEngine::newObject() +{ + Q_D(QScriptEngine); + QScript::APIShim shim(d); + return d->scriptValueFromJSCValue(d->newObject()); +} + +/*! + \since 4.4 + \overload + + Creates a QtScript Object of the given class, \a scriptClass. + + The prototype of the created object will be the Object + prototype object. + + \a data, if specified, is set as the internal data of the + new object (using QScriptValue::setData()). + + \sa QScriptValue::scriptClass(), reportAdditionalMemoryCost() +*/ +QScriptValue QScriptEngine::newObject(QScriptClass *scriptClass, + const QScriptValue &data) +{ + Q_D(QScriptEngine); + QScript::APIShim shim(d); + JSC::ExecState* exec = d->currentFrame; + QScriptObject *result = new (exec) QScriptObject(d->scriptObjectStructure); + result->setDelegate(new QScript::ClassObjectDelegate(scriptClass)); + QScriptValue scriptObject = d->scriptValueFromJSCValue(result); + scriptObject.setData(data); + QScriptValue proto = scriptClass->prototype(); + if (proto.isValid()) + scriptObject.setPrototype(proto); + return scriptObject; +} + +/*! + \internal +*/ +QScriptValue QScriptEngine::newActivationObject() +{ + qWarning("QScriptEngine::newActivationObject() not implemented"); + // ### JSActivation or JSVariableObject? + return QScriptValue(); +} + +/*! + Creates a QScriptValue that wraps a native (C++) function. \a fun + must be a C++ function with signature QScriptEngine::FunctionSignature. \a + length is the number of arguments that \a fun expects; this becomes + the \c{length} property of the created QScriptValue. + + Note that \a length only gives an indication of the number of + arguments that the function expects; an actual invocation of a + function can include any number of arguments. You can check the + \l{QScriptContext::argumentCount()}{argumentCount()} of the + QScriptContext associated with the invocation to determine the + actual number of arguments passed. + + A \c{prototype} property is automatically created for the resulting + function object, to provide for the possibility that the function + will be used as a constructor. + + By combining newFunction() and the property flags + QScriptValue::PropertyGetter and QScriptValue::PropertySetter, you + can create script object properties that behave like normal + properties in script code, but are in fact accessed through + functions (analogous to how properties work in \l{Qt's Property + System}). Example: + + \snippet doc/src/snippets/code/src_script_qscriptengine.cpp 11 + + When the property \c{foo} of the script object is subsequently + accessed in script code, \c{getSetFoo()} will be invoked to handle + the access. In this particular case, we chose to store the "real" + value of \c{foo} as a property of the accessor function itself; you + are of course free to do whatever you like in this function. + + In the above example, a single native function was used to handle + both reads and writes to the property; the argument count is used to + determine if we are handling a read or write. You can also use two + separate functions; just specify the relevant flag + (QScriptValue::PropertyGetter or QScriptValue::PropertySetter) when + setting the property, e.g.: + + \snippet doc/src/snippets/code/src_script_qscriptengine.cpp 12 + + \sa QScriptValue::call() +*/ +QScriptValue QScriptEngine::newFunction(QScriptEngine::FunctionSignature fun, int length) +{ + Q_D(QScriptEngine); + QScript::APIShim shim(d); + JSC::ExecState* exec = d->currentFrame; + JSC::JSValue function = new (exec)QScript::FunctionWrapper(exec, length, JSC::Identifier(exec, ""), fun); + QScriptValue result = d->scriptValueFromJSCValue(function); + QScriptValue proto = newObject(); + result.setProperty(QLatin1String("prototype"), proto, + QScriptValue::Undeletable | QScriptValue::SkipInEnumeration); + proto.setProperty(QLatin1String("constructor"), result, QScriptValue::SkipInEnumeration); + return result; +} + +/*! + \internal + \since 4.4 +*/ +QScriptValue QScriptEngine::newFunction(QScriptEngine::FunctionWithArgSignature fun, void *arg) +{ + Q_D(QScriptEngine); + QScript::APIShim shim(d); + JSC::ExecState* exec = d->currentFrame; + JSC::JSValue function = new (exec)QScript::FunctionWithArgWrapper(exec, /*length=*/0, JSC::Identifier(exec, ""), fun, arg); + QScriptValue result = d->scriptValueFromJSCValue(function); + QScriptValue proto = newObject(); + result.setProperty(QLatin1String("prototype"), proto, + QScriptValue::Undeletable | QScriptValue::SkipInEnumeration); + proto.setProperty(QLatin1String("constructor"), result, QScriptValue::SkipInEnumeration); + return result; +} + +/*! + Creates a QtScript object of class Array with the given \a length. + + \sa newObject() +*/ +QScriptValue QScriptEngine::newArray(uint length) +{ + Q_D(QScriptEngine); + QScript::APIShim shim(d); + return d->scriptValueFromJSCValue(d->newArray(d->currentFrame, length)); +} + +/*! + Creates a QtScript object of class RegExp with the given + \a pattern and \a flags. + + The legal flags are 'g' (global), 'i' (ignore case), and 'm' + (multiline). +*/ +QScriptValue QScriptEngine::newRegExp(const QString &pattern, const QString &flags) +{ + Q_D(QScriptEngine); + QScript::APIShim shim(d); + return d->scriptValueFromJSCValue(d->newRegExp(d->currentFrame, pattern, flags)); +} + +/*! + Creates a QtScript object of class Date with the given + \a value (the number of milliseconds since 01 January 1970, + UTC). +*/ +QScriptValue QScriptEngine::newDate(qsreal value) +{ + Q_D(QScriptEngine); + QScript::APIShim shim(d); + return d->scriptValueFromJSCValue(d->newDate(d->currentFrame, value)); +} + +/*! + Creates a QtScript object of class Date from the given \a value. + + \sa QScriptValue::toDateTime() +*/ +QScriptValue QScriptEngine::newDate(const QDateTime &value) +{ + Q_D(QScriptEngine); + QScript::APIShim shim(d); + return d->scriptValueFromJSCValue(d->newDate(d->currentFrame, value)); +} + +#ifndef QT_NO_QOBJECT +/*! + Creates a QtScript object that represents a QObject class, using the + the given \a metaObject and constructor \a ctor. + + Enums of \a metaObject (declared with Q_ENUMS) are available as + properties of the created QScriptValue. When the class is called as + a function, \a ctor will be called to create a new instance of the + class. + + Example: + + \snippet doc/src/snippets/code/src_script_qscriptengine.cpp 27 + + \sa newQObject(), scriptValueFromQMetaObject() +*/ +QScriptValue QScriptEngine::newQMetaObject( + const QMetaObject *metaObject, const QScriptValue &ctor) +{ + Q_D(QScriptEngine); + QScript::APIShim shim(d); + JSC::JSValue jscCtor = d->scriptValueToJSCValue(ctor); + JSC::JSValue jscQMetaObject = d->newQMetaObject(metaObject, jscCtor); + return d->scriptValueFromJSCValue(jscQMetaObject); +} + +/*! + \fn QScriptValue QScriptEngine::scriptValueFromQMetaObject() + + Creates a QScriptValue that represents the Qt class \c{T}. + + This function is used in combination with one of the + Q_SCRIPT_DECLARE_QMETAOBJECT() macro. Example: + + \snippet doc/src/snippets/code/src_script_qscriptengine.cpp 13 + + \sa QScriptEngine::newQMetaObject() +*/ + +/*! + \fn QScriptValue qScriptValueFromQMetaObject(QScriptEngine *engine) + \since 4.3 + \relates QScriptEngine + \obsolete + + Uses \a engine to create a QScriptValue that represents the Qt class + \c{T}. + + This function is equivalent to + QScriptEngine::scriptValueFromQMetaObject(). + + \note This function was provided as a workaround for MSVC 6 + which did not support member template functions. It is advised + to use the other form in new code. + + \sa QScriptEngine::newQMetaObject() +*/ +#endif // QT_NO_QOBJECT + +/*! + \obsolete + + Returns true if \a program can be evaluated; i.e. the code is + sufficient to determine whether it appears to be a syntactically + correct program, or contains a syntax error. + + This function returns false if \a program is incomplete; i.e. the + input is syntactically correct up to the point where the input is + terminated. + + Note that this function only does a static check of \a program; + e.g. it does not check whether references to variables are + valid, and so on. + + A typical usage of canEvaluate() is to implement an interactive + interpreter for QtScript. The user is repeatedly queried for + individual lines of code; the lines are concatened internally, and + only when canEvaluate() returns true for the resulting program is it + passed to evaluate(). + + The following are some examples to illustrate the behavior of + canEvaluate(). (Note that all example inputs are assumed to have an + explicit newline as their last character, since otherwise the + QtScript parser would automatically insert a semi-colon character at + the end of the input, and this could cause canEvaluate() to produce + different results.) + + Given the input + \snippet doc/src/snippets/code/src_script_qscriptengine.cpp 14 + canEvaluate() will return true, since the program appears to be complete. + + Given the input + \snippet doc/src/snippets/code/src_script_qscriptengine.cpp 15 + canEvaluate() will return false, since the if-statement is not complete, + but is syntactically correct so far. + + Given the input + \snippet doc/src/snippets/code/src_script_qscriptengine.cpp 16 + canEvaluate() will return true, but evaluate() will throw a + SyntaxError given the same input. + + Given the input + \snippet doc/src/snippets/code/src_script_qscriptengine.cpp 17 + canEvaluate() will return true, even though the code is clearly not + syntactically valid QtScript code. evaluate() will throw a + SyntaxError when this code is evaluated. + + Given the input + \snippet doc/src/snippets/code/src_script_qscriptengine.cpp 18 + canEvaluate() will return true, but evaluate() will throw a + ReferenceError if \c{foo} is not defined in the script + environment. + + \sa evaluate(), checkSyntax() +*/ +bool QScriptEngine::canEvaluate(const QString &program) const +{ + return QScriptEnginePrivate::canEvaluate(program); +} + + +bool QScriptEnginePrivate::canEvaluate(const QString &program) +{ + QScript::SyntaxChecker checker; + QScript::SyntaxChecker::Result result = checker.checkSyntax(program); + return (result.state != QScript::SyntaxChecker::Intermediate); +} + +/*! + \since 4.5 + + Checks the syntax of the given \a program. Returns a + QScriptSyntaxCheckResult object that contains the result of the check. +*/ +QScriptSyntaxCheckResult QScriptEngine::checkSyntax(const QString &program) +{ + return QScriptEnginePrivate::checkSyntax(program); +} + +QScriptSyntaxCheckResult QScriptEnginePrivate::checkSyntax(const QString &program) +{ + QScript::SyntaxChecker checker; + QScript::SyntaxChecker::Result result = checker.checkSyntax(program); + QScriptSyntaxCheckResultPrivate *p = new QScriptSyntaxCheckResultPrivate(); + switch (result.state) { + case QScript::SyntaxChecker::Error: + p->state = QScriptSyntaxCheckResult::Error; + break; + case QScript::SyntaxChecker::Intermediate: + p->state = QScriptSyntaxCheckResult::Intermediate; + break; + case QScript::SyntaxChecker::Valid: + p->state = QScriptSyntaxCheckResult::Valid; + break; + } + p->errorLineNumber = result.errorLineNumber; + p->errorColumnNumber = result.errorColumnNumber; + p->errorMessage = result.errorMessage; + return QScriptSyntaxCheckResult(p); +} + + + +/*! + Evaluates \a program, using \a lineNumber as the base line number, + and returns the result of the evaluation. + + The script code will be evaluated in the current context. + + The evaluation of \a program can cause an exception in the + engine; in this case the return value will be the exception + that was thrown (typically an \c{Error} object). You can call + hasUncaughtException() to determine if an exception occurred in + the last call to evaluate(). + + \a lineNumber is used to specify a starting line number for \a + program; line number information reported by the engine that pertain + to this evaluation (e.g. uncaughtExceptionLineNumber()) will be + based on this argument. For example, if \a program consists of two + lines of code, and the statement on the second line causes a script + exception, uncaughtExceptionLineNumber() would return the given \a + lineNumber plus one. When no starting line number is specified, line + numbers will be 1-based. + + \a fileName is used for error reporting. For example in error objects + the file name is accessible through the "fileName" property if it's + provided with this function. + + \sa canEvaluate(), hasUncaughtException(), isEvaluating(), abortEvaluation() +*/ + +QScriptValue QScriptEngine::evaluate(const QString &program, const QString &fileName, int lineNumber) +{ + Q_D(QScriptEngine); + QScript::APIShim shim(d); + WTF::PassRefPtr<QScript::UStringSourceProviderWithFeedback> provider + = QScript::UStringSourceProviderWithFeedback::create(program, fileName, lineNumber, d); + intptr_t sourceId = provider->asID(); + JSC::SourceCode source(provider, lineNumber); //after construction of SourceCode provider variable will be null. + + JSC::ExecState* exec = d->currentFrame; + WTF::RefPtr<JSC::EvalExecutable> executable = JSC::EvalExecutable::create(exec, source); + bool compile = true; + return d->scriptValueFromJSCValue(d->evaluateHelper(exec, sourceId, executable.get(), compile)); +} + +/*! + \since 4.7 + + Evaluates the given \a program and returns the result of the + evaluation. +*/ +QScriptValue QScriptEngine::evaluate(const QScriptProgram &program) +{ + Q_D(QScriptEngine); + QScriptProgramPrivate *program_d = QScriptProgramPrivate::get(program); + if (!program_d) + return QScriptValue(); + + QScript::APIShim shim(d); + JSC::ExecState* exec = d->currentFrame; + JSC::EvalExecutable *executable = program_d->executable(exec, d); + bool compile = !program_d->isCompiled; + JSC::JSValue result = d->evaluateHelper(exec, program_d->sourceId, + executable, compile); + if (compile) + program_d->isCompiled = true; + return d->scriptValueFromJSCValue(result); +} + +/*! + Returns the current context. + + The current context is typically accessed to retrieve the arguments + and `this' object in native functions; for convenience, it is + available as the first argument in QScriptEngine::FunctionSignature. +*/ +QScriptContext *QScriptEngine::currentContext() const +{ + Q_D(const QScriptEngine); + return const_cast<QScriptEnginePrivate*>(d)->contextForFrame(d->currentFrame); +} + +/*! + 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. + + This function is useful when you want to evaluate script code + as if it were the body of a function. You can use the context's + \l{QScriptContext::activationObject()}{activationObject}() to initialize + local variables that will be available to scripts. Example: + + \snippet doc/src/snippets/code/src_script_qscriptengine.cpp 19 + + In the above example, the new variable "tmp" defined in the script + will be local to the context; in other words, the script doesn't + have any effect on the global environment. + + Returns 0 in case of stack overflow + + \sa popContext() +*/ +QScriptContext *QScriptEngine::pushContext() +{ + Q_D(QScriptEngine); + QScript::APIShim shim(d); + + JSC::CallFrame* newFrame = d->pushContext(d->currentFrame, d->currentFrame->globalData().dynamicGlobalObject, + JSC::ArgList(), /*callee = */0); + + if (agent()) + agent()->contextPush(); + + return d->contextForFrame(newFrame); +} + +/*! \internal + push a context for a native function. + JSC native function doesn't have different stackframe or context. so we need to create one. + + use popContext right after to go back to the previous context the context if no stack overflow has hapenned + + exec is the current top frame. + + return the new top frame. (might be the same as exec if a new stackframe was not needed) or 0 if stack overflow +*/ +JSC::CallFrame *QScriptEnginePrivate::pushContext(JSC::CallFrame *exec, JSC::JSValue _thisObject, + const JSC::ArgList& args, JSC::JSObject *callee, bool calledAsConstructor, + bool clearScopeChain) +{ + JSC::JSValue thisObject = _thisObject; + if (!callee) { + // callee can't be zero, as this can cause JSC to crash during GC + // marking phase if the context's Arguments object has been created. + // Fake it by using the global object. Note that this is also handled + // in QScriptContext::callee(), as that function should still return + // an invalid value. + callee = originalGlobalObject(); + } + if (calledAsConstructor) { + //JSC doesn't create default created object for native functions. so we do it + JSC::JSValue prototype = callee->get(exec, exec->propertyNames().prototype); + JSC::Structure *structure = prototype.isObject() ? JSC::asObject(prototype)->inheritorID() + : originalGlobalObject()->emptyObjectStructure(); + thisObject = new (exec) QScriptObject(structure); + } + + int flags = NativeContext; + if (calledAsConstructor) + flags |= CalledAsConstructorContext; + + //build a frame + JSC::CallFrame *newCallFrame = exec; + if (callee == 0 //called from public QScriptEngine::pushContext + || exec->returnPC() == 0 || (contextFlags(exec) & NativeContext) //called from native-native call + || (exec->codeBlock() && exec->callee() != callee)) { //the interpreter did not build a frame for us. + //We need to check if the Interpreter might have already created a frame for function called from JS. + JSC::Interpreter *interp = exec->interpreter(); + JSC::Register *oldEnd = interp->registerFile().end(); + int argc = args.size() + 1; //add "this" + JSC::Register *newEnd = oldEnd + argc + JSC::RegisterFile::CallFrameHeaderSize; + if (!interp->registerFile().grow(newEnd)) + return 0; //### Stack overflow + newCallFrame = JSC::CallFrame::create(oldEnd); + newCallFrame[0] = thisObject; + int dst = 0; + JSC::ArgList::const_iterator it; + for (it = args.begin(); it != args.end(); ++it) + newCallFrame[++dst] = *it; + newCallFrame += argc + JSC::RegisterFile::CallFrameHeaderSize; + + if (!clearScopeChain) { + newCallFrame->init(0, /*vPC=*/0, exec->scopeChain(), exec, flags | ShouldRestoreCallFrame, argc, callee); + } else { + newCallFrame->init(0, /*vPC=*/0, globalExec()->scopeChain(), exec, flags | ShouldRestoreCallFrame, argc, callee); + } + } else { + setContextFlags(newCallFrame, flags); +#if ENABLE(JIT) + exec->registers()[JSC::RegisterFile::Callee] = JSC::JSValue(callee); //JIT let the callee set the 'callee' +#endif + if (calledAsConstructor) { + //update the new created this + JSC::Register* thisRegister = thisRegisterForFrame(newCallFrame); + *thisRegister = thisObject; + } + } + currentFrame = newCallFrame; + return newCallFrame; +} + + +/*! + Pops the current execution context and restores the previous one. + This function must be used in conjunction with pushContext(). + + \sa pushContext() +*/ +void QScriptEngine::popContext() +{ + if (agent()) + agent()->contextPop(); + Q_D(QScriptEngine); + QScript::APIShim shim(d); + if (d->currentFrame->returnPC() != 0 || d->currentFrame->codeBlock() != 0 + || !currentContext()->parentContext()) { + qWarning("QScriptEngine::popContext() doesn't match with pushContext()"); + return; + } + + d->popContext(); +} + +/*! \internal + counter part of QScriptEnginePrivate::pushContext + */ +void QScriptEnginePrivate::popContext() +{ + uint flags = contextFlags(currentFrame); + bool hasScope = flags & HasScopeContext; + if (flags & ShouldRestoreCallFrame) { //normal case + JSC::RegisterFile ®isterFile = currentFrame->interpreter()->registerFile(); + JSC::Register *const newEnd = currentFrame->registers() - JSC::RegisterFile::CallFrameHeaderSize - currentFrame->argumentCount(); + if (hasScope) + currentFrame->scopeChain()->pop()->deref(); + registerFile.shrink(newEnd); + } else if(hasScope) { //the stack frame was created by the Interpreter, we don't need to rewind it. + currentFrame->setScopeChain(currentFrame->scopeChain()->pop()); + currentFrame->scopeChain()->deref(); + } + currentFrame = currentFrame->callerFrame(); +} + +/*! + Returns true if the last script evaluation resulted in an uncaught + exception; otherwise returns false. + + The exception state is cleared when evaluate() is called. + + \sa uncaughtException(), uncaughtExceptionLineNumber() +*/ +bool QScriptEngine::hasUncaughtException() const +{ + Q_D(const QScriptEngine); + JSC::ExecState* exec = d->globalExec(); + return exec->hadException() || d->currentException().isValid(); +} + +/*! + Returns the current uncaught exception, or an invalid QScriptValue + if there is no uncaught exception. + + The exception value is typically an \c{Error} object; in that case, + you can call toString() on the return value to obtain an error + message. + + \sa hasUncaughtException(), uncaughtExceptionLineNumber(), +*/ +QScriptValue QScriptEngine::uncaughtException() const +{ + Q_D(const QScriptEngine); + QScriptValue result; + JSC::ExecState* exec = d->globalExec(); + if (exec->hadException()) + result = const_cast<QScriptEnginePrivate*>(d)->scriptValueFromJSCValue(exec->exception()); + else + result = d->currentException(); + return result; +} + +/*! + Returns the line number where the last uncaught exception occurred. + + Line numbers are 1-based, unless a different base was specified as + the second argument to evaluate(). + + \sa hasUncaughtException() +*/ +int QScriptEngine::uncaughtExceptionLineNumber() const +{ + if (!hasUncaughtException()) + return -1; + return uncaughtException().property(QLatin1String("lineNumber")).toInt32(); +} + +/*! + Returns a human-readable backtrace of the last uncaught exception. + + It is in the form \c{<function-name>()@<file-name>:<line-number>}. + + \sa uncaughtException() +*/ +QStringList QScriptEngine::uncaughtExceptionBacktrace() const +{ + if (!hasUncaughtException()) + return QStringList(); +// ### currently no way to get a full backtrace from JSC without installing a +// debugger that reimplements exception() and store the backtrace there. + QScriptValue value = uncaughtException(); + if (!value.isError()) + return QStringList(); + QStringList result; + result.append(QString::fromLatin1("<anonymous>()@%0:%1") + .arg(value.property(QLatin1String("fileName")).toString()) + .arg(value.property(QLatin1String("lineNumber")).toInt32())); + return result; +} + +/*! + \since 4.4 + + Clears any uncaught exceptions in this engine. + + \sa hasUncaughtException() +*/ +void QScriptEngine::clearExceptions() +{ + Q_D(QScriptEngine); + JSC::ExecState* exec = d->currentFrame; + exec->clearException(); + d->clearCurrentException(); +} + +/*! + Returns the default prototype associated with the given \a metaTypeId, + or an invalid QScriptValue if no default prototype has been set. + + \sa setDefaultPrototype() +*/ +QScriptValue QScriptEngine::defaultPrototype(int metaTypeId) const +{ + Q_D(const QScriptEngine); + return const_cast<QScriptEnginePrivate*>(d)->scriptValueFromJSCValue(d->defaultPrototype(metaTypeId)); +} + +/*! + Sets the default prototype of the C++ type identified by the given + \a metaTypeId to \a prototype. + + The default prototype provides a script interface for values of + type \a metaTypeId when a value of that type is accessed from script + code. Whenever the script engine (implicitly or explicitly) creates + a QScriptValue from a value of type \a metaTypeId, the default + prototype will be set as the QScriptValue's prototype. + + The \a prototype object itself may be constructed using one of two + principal techniques; the simplest is to subclass QScriptable, which + enables you to define the scripting API of the type through QObject + properties and slots. Another possibility is to create a script + object by calling newObject(), and populate the object with the + desired properties (e.g. native functions wrapped with + newFunction()). + + \sa defaultPrototype(), qScriptRegisterMetaType(), QScriptable, {Default Prototypes Example} +*/ +void QScriptEngine::setDefaultPrototype(int metaTypeId, const QScriptValue &prototype) +{ + Q_D(QScriptEngine); + d->setDefaultPrototype(metaTypeId, d->scriptValueToJSCValue(prototype)); +} + +/*! + \typedef QScriptEngine::FunctionSignature + \relates QScriptEngine + + The function signature \c{QScriptValue f(QScriptContext *, QScriptEngine *)}. + + A function with such a signature can be passed to + QScriptEngine::newFunction() to wrap the function. +*/ + +/*! + \typedef QScriptEngine::FunctionWithArgSignature + \relates QScriptEngine + + The function signature \c{QScriptValue f(QScriptContext *, QScriptEngine *, void *)}. + + A function with such a signature can be passed to + QScriptEngine::newFunction() to wrap the function. +*/ + +/*! + \typedef QScriptEngine::MarshalFunction + \internal +*/ + +/*! + \typedef QScriptEngine::DemarshalFunction + \internal +*/ + +/*! + \internal +*/ +QScriptValue QScriptEngine::create(int type, const void *ptr) +{ + Q_D(QScriptEngine); + QScript::APIShim shim(d); + return d->scriptValueFromJSCValue(d->create(d->currentFrame, type, ptr)); +} + +JSC::JSValue QScriptEnginePrivate::create(JSC::ExecState *exec, int type, const void *ptr) +{ + Q_ASSERT(ptr != 0); + JSC::JSValue result; + QScriptEnginePrivate *eng = exec ? QScript::scriptEngineFromExec(exec) : 0; + QScriptTypeInfo *info = eng ? eng->m_typeInfos.value(type) : 0; + if (info && info->marshal) { + result = eng->scriptValueToJSCValue(info->marshal(eng->q_func(), ptr)); + } else { + // check if it's one of the types we know + switch (QMetaType::Type(type)) { + case QMetaType::Void: + return JSC::jsUndefined(); + case QMetaType::Bool: + return JSC::jsBoolean(*reinterpret_cast<const bool*>(ptr)); + case QMetaType::Int: + return JSC::jsNumber(exec, *reinterpret_cast<const int*>(ptr)); + case QMetaType::UInt: + return JSC::jsNumber(exec, *reinterpret_cast<const uint*>(ptr)); + case QMetaType::LongLong: + return JSC::jsNumber(exec, qsreal(*reinterpret_cast<const qlonglong*>(ptr))); + case QMetaType::ULongLong: + return JSC::jsNumber(exec, qsreal(*reinterpret_cast<const qulonglong*>(ptr))); + case QMetaType::Double: + return JSC::jsNumber(exec, qsreal(*reinterpret_cast<const double*>(ptr))); + case QMetaType::QString: + return JSC::jsString(exec, *reinterpret_cast<const QString*>(ptr)); + case QMetaType::Float: + return JSC::jsNumber(exec, *reinterpret_cast<const float*>(ptr)); + case QMetaType::Short: + return JSC::jsNumber(exec, *reinterpret_cast<const short*>(ptr)); + case QMetaType::UShort: + return JSC::jsNumber(exec, *reinterpret_cast<const unsigned short*>(ptr)); + case QMetaType::Char: + return JSC::jsNumber(exec, *reinterpret_cast<const char*>(ptr)); + case QMetaType::UChar: + return JSC::jsNumber(exec, *reinterpret_cast<const unsigned char*>(ptr)); + case QMetaType::QChar: + return JSC::jsNumber(exec, (*reinterpret_cast<const QChar*>(ptr)).unicode()); + case QMetaType::QStringList: + result = arrayFromStringList(exec, *reinterpret_cast<const QStringList *>(ptr)); + break; + case QMetaType::QVariantList: + result = arrayFromVariantList(exec, *reinterpret_cast<const QVariantList *>(ptr)); + break; + case QMetaType::QVariantMap: + result = objectFromVariantMap(exec, *reinterpret_cast<const QVariantMap *>(ptr)); + break; + case QMetaType::QDateTime: + result = newDate(exec, *reinterpret_cast<const QDateTime *>(ptr)); + break; + case QMetaType::QDate: + result = newDate(exec, QDateTime(*reinterpret_cast<const QDate *>(ptr))); + break; +#ifndef QT_NO_REGEXP + case QMetaType::QRegExp: + result = newRegExp(exec, *reinterpret_cast<const QRegExp *>(ptr)); + break; +#endif +#ifndef QT_NO_QOBJECT + case QMetaType::QObjectStar: + case QMetaType::QWidgetStar: + result = eng->newQObject(*reinterpret_cast<QObject* const *>(ptr)); + break; +#endif + case QMetaType::QVariant: + result = eng->newVariant(*reinterpret_cast<const QVariant*>(ptr)); + break; + default: + if (type == qMetaTypeId<QScriptValue>()) { + result = eng->scriptValueToJSCValue(*reinterpret_cast<const QScriptValue*>(ptr)); + if (!result) + return JSC::jsUndefined(); + } + +#ifndef QT_NO_QOBJECT + // lazy registration of some common list types + else if (type == qMetaTypeId<QObjectList>()) { + qScriptRegisterSequenceMetaType<QObjectList>(eng->q_func()); + return create(exec, type, ptr); + } +#endif + else if (type == qMetaTypeId<QList<int> >()) { + qScriptRegisterSequenceMetaType<QList<int> >(eng->q_func()); + return create(exec, type, ptr); + } + + else { + QByteArray typeName = QMetaType::typeName(type); + if (typeName.endsWith('*') && !*reinterpret_cast<void* const *>(ptr)) + return JSC::jsNull(); + else + result = eng->newVariant(QVariant(type, ptr)); + } + } + } + if (result && result.isObject() && info && info->prototype + && JSC::JSValue::strictEqual(exec, JSC::asObject(result)->prototype(), eng->originalGlobalObject()->objectPrototype())) { + JSC::asObject(result)->setPrototype(info->prototype); + } + return result; +} + +bool QScriptEnginePrivate::convertValue(JSC::ExecState *exec, JSC::JSValue value, + int type, void *ptr) +{ + QScriptEnginePrivate *eng = exec ? QScript::scriptEngineFromExec(exec) : 0; + if (eng) { + QScriptTypeInfo *info = eng->m_typeInfos.value(type); + if (info && info->demarshal) { + info->demarshal(eng->scriptValueFromJSCValue(value), ptr); + return true; + } + } + + // check if it's one of the types we know + switch (QMetaType::Type(type)) { + case QMetaType::Bool: + *reinterpret_cast<bool*>(ptr) = toBool(exec, value); + return true; + case QMetaType::Int: + *reinterpret_cast<int*>(ptr) = toInt32(exec, value); + return true; + case QMetaType::UInt: + *reinterpret_cast<uint*>(ptr) = toUInt32(exec, value); + return true; + case QMetaType::LongLong: + *reinterpret_cast<qlonglong*>(ptr) = qlonglong(toInteger(exec, value)); + return true; + case QMetaType::ULongLong: + *reinterpret_cast<qulonglong*>(ptr) = qulonglong(toInteger(exec, value)); + return true; + case QMetaType::Double: + *reinterpret_cast<double*>(ptr) = toNumber(exec, value); + return true; + case QMetaType::QString: + if (value.isUndefined() || value.isNull()) + *reinterpret_cast<QString*>(ptr) = QString(); + else + *reinterpret_cast<QString*>(ptr) = toString(exec, value); + return true; + case QMetaType::Float: + *reinterpret_cast<float*>(ptr) = toNumber(exec, value); + return true; + case QMetaType::Short: + *reinterpret_cast<short*>(ptr) = short(toInt32(exec, value)); + return true; + case QMetaType::UShort: + *reinterpret_cast<unsigned short*>(ptr) = QScript::ToUInt16(toNumber(exec, value)); + return true; + case QMetaType::Char: + *reinterpret_cast<char*>(ptr) = char(toInt32(exec, value)); + return true; + case QMetaType::UChar: + *reinterpret_cast<unsigned char*>(ptr) = (unsigned char)(toInt32(exec, value)); + return true; + case QMetaType::QChar: + if (value.isString()) { + QString str = toString(exec, value); + *reinterpret_cast<QChar*>(ptr) = str.isEmpty() ? QChar() : str.at(0); + } else { + *reinterpret_cast<QChar*>(ptr) = QChar(QScript::ToUInt16(toNumber(exec, value))); + } + return true; + case QMetaType::QDateTime: + if (isDate(value)) { + *reinterpret_cast<QDateTime *>(ptr) = toDateTime(exec, value); + return true; + } break; + case QMetaType::QDate: + if (isDate(value)) { + *reinterpret_cast<QDate *>(ptr) = toDateTime(exec, value).date(); + return true; + } break; +#ifndef QT_NO_REGEXP + case QMetaType::QRegExp: + if (isRegExp(value)) { + *reinterpret_cast<QRegExp *>(ptr) = toRegExp(exec, value); + return true; + } break; +#endif +#ifndef QT_NO_QOBJECT + case QMetaType::QObjectStar: + if (isQObject(value) || value.isNull()) { + *reinterpret_cast<QObject* *>(ptr) = toQObject(exec, value); + return true; + } break; + case QMetaType::QWidgetStar: + if (isQObject(value) || value.isNull()) { + QObject *qo = toQObject(exec, value); + if (!qo || qo->isWidgetType()) { + *reinterpret_cast<QWidget* *>(ptr) = reinterpret_cast<QWidget*>(qo); + return true; + } + } break; +#endif + case QMetaType::QStringList: + if (isArray(value)) { + *reinterpret_cast<QStringList *>(ptr) = stringListFromArray(exec, value); + return true; + } break; + case QMetaType::QVariantList: + if (isArray(value)) { + *reinterpret_cast<QVariantList *>(ptr) = variantListFromArray(exec, JSC::asArray(value)); + return true; + } break; + case QMetaType::QVariantMap: + if (isObject(value)) { + *reinterpret_cast<QVariantMap *>(ptr) = variantMapFromObject(exec, JSC::asObject(value)); + return true; + } break; + case QMetaType::QVariant: + *reinterpret_cast<QVariant*>(ptr) = toVariant(exec, value); + return true; + default: + ; + } + + QByteArray name = QMetaType::typeName(type); +#ifndef QT_NO_QOBJECT + if (convertToNativeQObject(exec, value, name, reinterpret_cast<void* *>(ptr))) + return true; +#endif + if (isVariant(value) && name.endsWith('*')) { + int valueType = QMetaType::type(name.left(name.size()-1)); + QVariant &var = variantValue(value); + if (valueType == var.userType()) { + *reinterpret_cast<void* *>(ptr) = var.data(); + return true; + } else { + // look in the prototype chain + JSC::JSValue proto = JSC::asObject(value)->prototype(); + while (proto.isObject()) { + bool canCast = false; + if (isVariant(proto)) { + canCast = (type == variantValue(proto).userType()) + || (valueType && (valueType == variantValue(proto).userType())); + } +#ifndef QT_NO_QOBJECT + else if (isQObject(proto)) { + QByteArray className = name.left(name.size()-1); + if (QObject *qobject = toQObject(exec, proto)) + canCast = qobject->qt_metacast(className) != 0; + } +#endif + if (canCast) { + QByteArray varTypeName = QMetaType::typeName(var.userType()); + if (varTypeName.endsWith('*')) + *reinterpret_cast<void* *>(ptr) = *reinterpret_cast<void* *>(var.data()); + else + *reinterpret_cast<void* *>(ptr) = var.data(); + return true; + } + proto = JSC::asObject(proto)->prototype(); + } + } + } else if (value.isNull() && name.endsWith('*')) { + *reinterpret_cast<void* *>(ptr) = 0; + return true; + } else if (type == qMetaTypeId<QScriptValue>()) { + if (!eng) + return false; + *reinterpret_cast<QScriptValue*>(ptr) = eng->scriptValueFromJSCValue(value); + return true; + } + + // lazy registration of some common list types +#ifndef QT_NO_QOBJECT + else if (type == qMetaTypeId<QObjectList>()) { + if (!eng) + return false; + qScriptRegisterSequenceMetaType<QObjectList>(eng->q_func()); + return convertValue(exec, value, type, ptr); + } +#endif + else if (type == qMetaTypeId<QList<int> >()) { + if (!eng) + return false; + qScriptRegisterSequenceMetaType<QList<int> >(eng->q_func()); + return convertValue(exec, value, type, ptr); + } + +#if 0 + if (!name.isEmpty()) { + qWarning("QScriptEngine::convert: unable to convert value to type `%s'", + name.constData()); + } +#endif + return false; +} + +bool QScriptEnginePrivate::convertNumber(qsreal value, int type, void *ptr) +{ + switch (QMetaType::Type(type)) { + case QMetaType::Bool: + *reinterpret_cast<bool*>(ptr) = QScript::ToBool(value); + return true; + case QMetaType::Int: + *reinterpret_cast<int*>(ptr) = QScript::ToInt32(value); + return true; + case QMetaType::UInt: + *reinterpret_cast<uint*>(ptr) = QScript::ToUInt32(value); + return true; + case QMetaType::LongLong: + *reinterpret_cast<qlonglong*>(ptr) = qlonglong(QScript::ToInteger(value)); + return true; + case QMetaType::ULongLong: + *reinterpret_cast<qulonglong*>(ptr) = qulonglong(QScript::ToInteger(value)); + return true; + case QMetaType::Double: + *reinterpret_cast<double*>(ptr) = value; + return true; + case QMetaType::QString: + *reinterpret_cast<QString*>(ptr) = QScript::ToString(value); + return true; + case QMetaType::Float: + *reinterpret_cast<float*>(ptr) = value; + return true; + case QMetaType::Short: + *reinterpret_cast<short*>(ptr) = short(QScript::ToInt32(value)); + return true; + case QMetaType::UShort: + *reinterpret_cast<unsigned short*>(ptr) = QScript::ToUInt16(value); + return true; + case QMetaType::Char: + *reinterpret_cast<char*>(ptr) = char(QScript::ToInt32(value)); + return true; + case QMetaType::UChar: + *reinterpret_cast<unsigned char*>(ptr) = (unsigned char)(QScript::ToInt32(value)); + return true; + case QMetaType::QChar: + *reinterpret_cast<QChar*>(ptr) = QChar(QScript::ToUInt16(value)); + return true; + default: + break; + } + return false; +} + +bool QScriptEnginePrivate::convertString(const QString &value, int type, void *ptr) +{ + switch (QMetaType::Type(type)) { + case QMetaType::Bool: + *reinterpret_cast<bool*>(ptr) = QScript::ToBool(value); + return true; + case QMetaType::Int: + *reinterpret_cast<int*>(ptr) = QScript::ToInt32(value); + return true; + case QMetaType::UInt: + *reinterpret_cast<uint*>(ptr) = QScript::ToUInt32(value); + return true; + case QMetaType::LongLong: + *reinterpret_cast<qlonglong*>(ptr) = qlonglong(QScript::ToInteger(value)); + return true; + case QMetaType::ULongLong: + *reinterpret_cast<qulonglong*>(ptr) = qulonglong(QScript::ToInteger(value)); + return true; + case QMetaType::Double: + *reinterpret_cast<double*>(ptr) = QScript::ToNumber(value); + return true; + case QMetaType::QString: + *reinterpret_cast<QString*>(ptr) = value; + return true; + case QMetaType::Float: + *reinterpret_cast<float*>(ptr) = QScript::ToNumber(value); + return true; + case QMetaType::Short: + *reinterpret_cast<short*>(ptr) = short(QScript::ToInt32(value)); + return true; + case QMetaType::UShort: + *reinterpret_cast<unsigned short*>(ptr) = QScript::ToUInt16(value); + return true; + case QMetaType::Char: + *reinterpret_cast<char*>(ptr) = char(QScript::ToInt32(value)); + return true; + case QMetaType::UChar: + *reinterpret_cast<unsigned char*>(ptr) = (unsigned char)(QScript::ToInt32(value)); + return true; + case QMetaType::QChar: + *reinterpret_cast<QChar*>(ptr) = QChar(QScript::ToUInt16(value)); + return true; + default: + break; + } + return false; +} + +bool QScriptEnginePrivate::hasDemarshalFunction(int type) const +{ + QScriptTypeInfo *info = m_typeInfos.value(type); + return info && (info->demarshal != 0); +} + +JSC::UString QScriptEnginePrivate::translationContextFromUrl(const JSC::UString &url) +{ + if (url != cachedTranslationUrl) { + cachedTranslationContext = QFileInfo(url).baseName(); + cachedTranslationUrl = url; + } + return cachedTranslationContext; +} + +/*! + \internal +*/ +bool QScriptEngine::convert(const QScriptValue &value, int type, void *ptr) +{ + Q_D(QScriptEngine); + QScript::APIShim shim(d); + return QScriptEnginePrivate::convertValue(d->currentFrame, d->scriptValueToJSCValue(value), type, ptr); +} + +/*! + \internal +*/ +bool QScriptEngine::convertV2(const QScriptValue &value, int type, void *ptr) +{ + QScriptValuePrivate *vp = QScriptValuePrivate::get(value); + if (vp) { + switch (vp->type) { + case QScriptValuePrivate::JavaScriptCore: { + if (vp->engine) { + QScript::APIShim shim(vp->engine); + return QScriptEnginePrivate::convertValue(vp->engine->currentFrame, vp->jscValue, type, ptr); + } else { + return QScriptEnginePrivate::convertValue(0, vp->jscValue, type, ptr); + } + } + case QScriptValuePrivate::Number: + return QScriptEnginePrivate::convertNumber(vp->numberValue, type, ptr); + case QScriptValuePrivate::String: + return QScriptEnginePrivate::convertString(vp->stringValue, type, ptr); + } + } + return false; +} + +/*! + \internal +*/ +void QScriptEngine::registerCustomType(int type, MarshalFunction mf, + DemarshalFunction df, + const QScriptValue &prototype) +{ + Q_D(QScriptEngine); + QScript::APIShim shim(d); + QScriptTypeInfo *info = d->m_typeInfos.value(type); + if (!info) { + info = new QScriptTypeInfo(); + d->m_typeInfos.insert(type, info); + } + info->marshal = mf; + info->demarshal = df; + info->prototype = d->scriptValueToJSCValue(prototype); +} + +/*! + \since 4.5 + + Installs translator functions on the given \a object, or on the Global + Object if no object is specified. + + The relation between Qt Script translator functions and C++ translator + functions is described in the following table: + + \table + \header \o Script Function \o Corresponding C++ Function + \row \o qsTr() \o QObject::tr() + \row \o QT_TR_NOOP() \o QT_TR_NOOP() + \row \o qsTranslate() \o QCoreApplication::translate() + \row \o QT_TRANSLATE_NOOP() \o QT_TRANSLATE_NOOP() + \row \o qsTrId() (since 4.7) \o qtTrId() + \row \o QT_TRID_NOOP() (since 4.7) \o QT_TRID_NOOP() + \endtable + + \sa {Internationalization with Qt} +*/ +void QScriptEngine::installTranslatorFunctions(const QScriptValue &object) +{ + Q_D(QScriptEngine); + QScript::APIShim shim(d); + JSC::ExecState* exec = d->currentFrame; + JSC::JSValue jscObject = d->scriptValueToJSCValue(object); + JSC::JSGlobalObject *glob = d->originalGlobalObject(); + if (!jscObject || !jscObject.isObject()) + jscObject = d->globalObject(); +// unsigned attribs = JSC::DontEnum; + +#ifndef QT_NO_TRANSLATION + JSC::asObject(jscObject)->putDirectFunction(exec, new (exec)JSC::NativeFunctionWrapper(exec, glob->prototypeFunctionStructure(), 5, JSC::Identifier(exec, "qsTranslate"), QScript::functionQsTranslate)); + JSC::asObject(jscObject)->putDirectFunction(exec, new (exec)JSC::NativeFunctionWrapper(exec, glob->prototypeFunctionStructure(), 2, JSC::Identifier(exec, "QT_TRANSLATE_NOOP"), QScript::functionQsTranslateNoOp)); + JSC::asObject(jscObject)->putDirectFunction(exec, new (exec)JSC::NativeFunctionWrapper(exec, glob->prototypeFunctionStructure(), 3, JSC::Identifier(exec, "qsTr"), QScript::functionQsTr)); + JSC::asObject(jscObject)->putDirectFunction(exec, new (exec)JSC::NativeFunctionWrapper(exec, glob->prototypeFunctionStructure(), 1, JSC::Identifier(exec, "QT_TR_NOOP"), QScript::functionQsTrNoOp)); + JSC::asObject(jscObject)->putDirectFunction(exec, new (exec)JSC::NativeFunctionWrapper(exec, glob->prototypeFunctionStructure(), 1, JSC::Identifier(exec, "qsTrId"), QScript::functionQsTrId)); + JSC::asObject(jscObject)->putDirectFunction(exec, new (exec)JSC::NativeFunctionWrapper(exec, glob->prototypeFunctionStructure(), 1, JSC::Identifier(exec, "QT_TRID_NOOP"), QScript::functionQsTrIdNoOp)); +#endif + + glob->stringPrototype()->putDirectFunction(exec, new (exec)JSC::NativeFunctionWrapper(exec, glob->prototypeFunctionStructure(), 1, JSC::Identifier(exec, "arg"), QScript::stringProtoFuncArg)); +} + +/*! + Imports the given \a extension into this QScriptEngine. Returns + undefinedValue() if the extension was successfully imported. You + can call hasUncaughtException() to check if an error occurred; in + that case, the return value is the value that was thrown by the + exception (usually an \c{Error} object). + + QScriptEngine ensures that a particular extension is only imported + once; subsequent calls to importExtension() with the same extension + name will do nothing and return undefinedValue(). + + \sa availableExtensions(), QScriptExtensionPlugin, {Creating QtScript Extensions} +*/ +QScriptValue QScriptEngine::importExtension(const QString &extension) +{ +#if defined(QT_NO_QOBJECT) || defined(QT_NO_LIBRARY) || defined(QT_NO_SETTINGS) + Q_UNUSED(extension); +#else + Q_D(QScriptEngine); + QScript::APIShim shim(d); + if (d->importedExtensions.contains(extension)) + return undefinedValue(); // already imported + + QScriptContext *context = currentContext(); + QCoreApplication *app = QCoreApplication::instance(); + if (!app) + return context->throwError(QLatin1String("No application object")); + + QObjectList staticPlugins = QPluginLoader::staticInstances(); + QStringList libraryPaths = app->libraryPaths(); + QString dot = QLatin1String("."); + QStringList pathComponents = extension.split(dot); + QString initDotJs = QLatin1String("__init__.js"); + + QString ext; + for (int i = 0; i < pathComponents.count(); ++i) { + if (!ext.isEmpty()) + ext.append(dot); + ext.append(pathComponents.at(i)); + if (d->importedExtensions.contains(ext)) + continue; // already imported + + if (d->extensionsBeingImported.contains(ext)) { + return context->throwError(QString::fromLatin1("recursive import of %0") + .arg(extension)); + } + d->extensionsBeingImported.insert(ext); + + QScriptExtensionInterface *iface = 0; + QString initjsContents; + QString initjsFileName; + + // look for the extension in static plugins + for (int j = 0; j < staticPlugins.size(); ++j) { + iface = qobject_cast<QScriptExtensionInterface*>(staticPlugins.at(j)); + if (!iface) + continue; + if (iface->keys().contains(ext)) + break; // use this one + else + iface = 0; // keep looking + } + + { + // look for __init__.js resource + QString path = QString::fromLatin1(":/qtscriptextension"); + for (int j = 0; j <= i; ++j) { + path.append(QLatin1Char('/')); + path.append(pathComponents.at(j)); + } + path.append(QLatin1Char('/')); + path.append(initDotJs); + QFile file(path); + if (file.open(QIODevice::ReadOnly)) { + QTextStream ts(&file); + initjsContents = ts.readAll(); + initjsFileName = path; + file.close(); + } + } + + if (!iface && initjsContents.isEmpty()) { + // look for the extension in library paths + for (int j = 0; j < libraryPaths.count(); ++j) { + QString libPath = libraryPaths.at(j) + QDir::separator() + QLatin1String("script"); + QDir dir(libPath); + if (!dir.exists(dot)) + continue; + + // look for C++ plugin + QFileInfoList files = dir.entryInfoList(QDir::Files); + for (int k = 0; k < files.count(); ++k) { + QFileInfo entry = files.at(k); + QString filePath = entry.canonicalFilePath(); + QPluginLoader loader(filePath); + iface = qobject_cast<QScriptExtensionInterface*>(loader.instance()); + if (iface) { + if (iface->keys().contains(ext)) + break; // use this one + else + iface = 0; // keep looking + } + } + + // look for __init__.js in the corresponding dir + QDir dirdir(libPath); + bool dirExists = dirdir.exists(); + for (int k = 0; dirExists && (k <= i); ++k) + dirExists = dirdir.cd(pathComponents.at(k)); + if (dirExists && dirdir.exists(initDotJs)) { + QFile file(dirdir.canonicalPath() + + QDir::separator() + initDotJs); + if (file.open(QIODevice::ReadOnly)) { + QTextStream ts(&file); + initjsContents = ts.readAll(); + initjsFileName = file.fileName(); + file.close(); + } + } + + if (iface || !initjsContents.isEmpty()) + break; + } + } + + if (!iface && initjsContents.isEmpty()) { + d->extensionsBeingImported.remove(ext); + return context->throwError( + QString::fromLatin1("Unable to import %0: no such extension") + .arg(extension)); + } + + // initialize the extension in a new context + QScriptContext *ctx = pushContext(); + ctx->setThisObject(globalObject()); + ctx->activationObject().setProperty(QLatin1String("__extension__"), ext, + QScriptValue::ReadOnly | QScriptValue::Undeletable); + ctx->activationObject().setProperty(QLatin1String("__setupPackage__"), + newFunction(QScript::__setupPackage__)); + ctx->activationObject().setProperty(QLatin1String("__postInit__"), QScriptValue(QScriptValue::UndefinedValue)); + + // the script is evaluated first + if (!initjsContents.isEmpty()) { + QScriptValue ret = evaluate(initjsContents, initjsFileName); + if (hasUncaughtException()) { + popContext(); + d->extensionsBeingImported.remove(ext); + return ret; + } + } + + // next, the C++ plugin is called + if (iface) { + iface->initialize(ext, this); + if (hasUncaughtException()) { + QScriptValue ret = uncaughtException(); // ctx_p->returnValue(); + popContext(); + d->extensionsBeingImported.remove(ext); + return ret; + } + } + + // if the __postInit__ function has been set, we call it + QScriptValue postInit = ctx->activationObject().property(QLatin1String("__postInit__")); + if (postInit.isFunction()) { + postInit.call(globalObject()); + if (hasUncaughtException()) { + QScriptValue ret = uncaughtException(); // ctx_p->returnValue(); + popContext(); + d->extensionsBeingImported.remove(ext); + return ret; + } + } + + popContext(); + + d->importedExtensions.insert(ext); + d->extensionsBeingImported.remove(ext); + } // for (i) +#endif // QT_NO_QOBJECT + return undefinedValue(); +} + +/*! + \since 4.4 + + Returns a list naming the available extensions that can be + imported using the importExtension() function. This list includes + extensions that have been imported. + + \sa importExtension(), importedExtensions() +*/ +QStringList QScriptEngine::availableExtensions() const +{ +#if defined(QT_NO_QOBJECT) || defined(QT_NO_LIBRARY) || defined(QT_NO_SETTINGS) + return QStringList(); +#else + QCoreApplication *app = QCoreApplication::instance(); + if (!app) + return QStringList(); + + QSet<QString> result; + + QObjectList staticPlugins = QPluginLoader::staticInstances(); + for (int i = 0; i < staticPlugins.size(); ++i) { + QScriptExtensionInterface *iface; + iface = qobject_cast<QScriptExtensionInterface*>(staticPlugins.at(i)); + if (iface) { + QStringList keys = iface->keys(); + for (int j = 0; j < keys.count(); ++j) + result << keys.at(j); + } + } + + QStringList libraryPaths = app->libraryPaths(); + for (int i = 0; i < libraryPaths.count(); ++i) { + QString libPath = libraryPaths.at(i) + QDir::separator() + QLatin1String("script"); + QDir dir(libPath); + if (!dir.exists()) + continue; + + // look for C++ plugins + QFileInfoList files = dir.entryInfoList(QDir::Files); + for (int j = 0; j < files.count(); ++j) { + QFileInfo entry = files.at(j); + QString filePath = entry.canonicalFilePath(); + QPluginLoader loader(filePath); + QScriptExtensionInterface *iface; + iface = qobject_cast<QScriptExtensionInterface*>(loader.instance()); + if (iface) { + QStringList keys = iface->keys(); + for (int k = 0; k < keys.count(); ++k) + result << keys.at(k); + } + } + + // look for scripts + QString initDotJs = QLatin1String("__init__.js"); + QList<QFileInfo> stack; + stack << dir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot); + while (!stack.isEmpty()) { + QFileInfo entry = stack.takeLast(); + QDir dd(entry.canonicalFilePath()); + if (dd.exists(initDotJs)) { + QString rpath = dir.relativeFilePath(dd.canonicalPath()); + QStringList components = rpath.split(QLatin1Char('/')); + result << components.join(QLatin1String(".")); + stack << dd.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot); + } + } + } + + QStringList lst = result.toList(); + qSort(lst); + return lst; +#endif +} + +/*! + \since 4.4 + + Returns a list naming the extensions that have been imported + using the importExtension() function. + + \sa availableExtensions() +*/ +QStringList QScriptEngine::importedExtensions() const +{ + Q_D(const QScriptEngine); + QStringList lst = d->importedExtensions.toList(); + qSort(lst); + return lst; +} + +/*! \fn QScriptValue QScriptEngine::toScriptValue(const T &value) + + Creates a QScriptValue with the given \a value. + + Note that the template type \c{T} must be known to QMetaType. + + See \l{Conversion Between QtScript and C++ Types} for a + description of the built-in type conversion provided by + QtScript. By default, the types that are not specially handled by + QtScript are represented as QVariants (e.g. the \a value is passed + to newVariant()); you can change this behavior by installing your + own type conversion functions with qScriptRegisterMetaType(). + + \sa fromScriptValue(), qScriptRegisterMetaType() +*/ + +/*! \fn T QScriptEngine::fromScriptValue(const QScriptValue &value) + + Returns the given \a value converted to the template type \c{T}. + + Note that \c{T} must be known to QMetaType. + + See \l{Conversion Between QtScript and C++ Types} for a + description of the built-in type conversion provided by + QtScript. + + \sa toScriptValue(), qScriptRegisterMetaType() +*/ + +/*! + \fn QScriptValue qScriptValueFromValue(QScriptEngine *engine, const T &value) + \since 4.3 + \relates QScriptEngine + \obsolete + + Creates a QScriptValue using the given \a engine with the given \a + value of template type \c{T}. + + This function is equivalent to QScriptEngine::toScriptValue(). + + \note This function was provided as a workaround for MSVC 6 + which did not support member template functions. It is advised + to use the other form in new code. + + \sa QScriptEngine::toScriptValue(), qscriptvalue_cast +*/ + +/*! + \fn T qScriptValueToValue(const QScriptValue &value) + \since 4.3 + \relates QScriptEngine + \obsolete + + Returns the given \a value converted to the template type \c{T}. + + This function is equivalent to QScriptEngine::fromScriptValue(). + + \note This function was provided as a workaround for MSVC 6 + which did not support member template functions. It is advised + to use the other form in new code. + + \sa QScriptEngine::fromScriptValue() +*/ + +/*! + \fn QScriptValue qScriptValueFromSequence(QScriptEngine *engine, const Container &container) + \since 4.3 + \relates QScriptEngine + + Creates an array in the form of a QScriptValue using the given \a engine + with the given \a container of template type \c{Container}. + + The \c Container type must provide a \c const_iterator class to enable the + contents of the container to be copied into the array. + + Additionally, the type of each element in the sequence should be + suitable for conversion to a QScriptValue. See + \l{Conversion Between QtScript and C++ Types} for more information + about the restrictions on types that can be used with QScriptValue. + + \sa QScriptEngine::fromScriptValue() +*/ + +/*! + \fn void qScriptValueToSequence(const QScriptValue &value, Container &container) + \since 4.3 + \relates QScriptEngine + + Copies the elements in the sequence specified by \a value to the given + \a container of template type \c{Container}. + + The \a value used is typically an array, but any container can be copied + as long as it provides a \c length property describing how many elements + it contains. + + Additionally, the type of each element in the sequence must be + suitable for conversion to a C++ type from a QScriptValue. See + \l{Conversion Between QtScript and C++ Types} for more information + about the restrictions on types that can be used with + QScriptValue. + + \sa qscriptvalue_cast() +*/ + +/*! + \fn T qscriptvalue_cast(const QScriptValue &value) + \since 4.3 + \relates QScriptValue + + Returns the given \a value converted to the template type \c{T}. + + \sa qScriptRegisterMetaType(), QScriptEngine::toScriptValue() +*/ + +/*! \fn int qScriptRegisterMetaType( + QScriptEngine *engine, + QScriptValue (*toScriptValue)(QScriptEngine *, const T &t), + void (*fromScriptValue)(const QScriptValue &, T &t), + const QScriptValue &prototype = QScriptValue()) + \relates QScriptEngine + + Registers the type \c{T} in the given \a engine. \a toScriptValue must + be a function that will convert from a value of type \c{T} to a + QScriptValue, and \a fromScriptValue a function that does the + opposite. \a prototype, if valid, is the prototype that's set on + QScriptValues returned by \a toScriptValue. + + Returns the internal ID used by QMetaType. + + You only need to call this function if you want to provide custom + conversion of values of type \c{T}, i.e. if the default + QVariant-based representation and conversion is not + appropriate. (Note that custom QObject-derived types also fall in + this category; e.g. for a QObject-derived class called MyObject, + you probably want to define conversion functions for MyObject* + that utilize QScriptEngine::newQObject() and + QScriptValue::toQObject().) + + If you only want to define a common script interface for values of + type \c{T}, and don't care how those values are represented + (i.e. storing them in QVariants is fine), use + \l{QScriptEngine::setDefaultPrototype()}{setDefaultPrototype}() + instead; this will minimize conversion costs. + + You need to declare the custom type first with + Q_DECLARE_METATYPE(). + + After a type has been registered, you can convert from a + QScriptValue to that type using + \l{QScriptEngine::fromScriptValue()}{fromScriptValue}(), and + create a QScriptValue from a value of that type using + \l{QScriptEngine::toScriptValue()}{toScriptValue}(). The engine + will take care of calling the proper conversion function when + calling C++ slots, and when getting or setting a C++ property; + i.e. the custom type may be used seamlessly on both the C++ side + and the script side. + + The following is an example of how to use this function. We will + specify custom conversion of our type \c{MyStruct}. Here's the C++ + type: + + \snippet doc/src/snippets/code/src_script_qscriptengine.cpp 20 + + We must declare it so that the type will be known to QMetaType: + + \snippet doc/src/snippets/code/src_script_qscriptengine.cpp 21 + + Next, the \c{MyStruct} conversion functions. We represent the + \c{MyStruct} value as a script object and just copy the properties: + + \snippet doc/src/snippets/code/src_script_qscriptengine.cpp 22 + + Now we can register \c{MyStruct} with the engine: + \snippet doc/src/snippets/code/src_script_qscriptengine.cpp 23 + + Working with \c{MyStruct} values is now easy: + \snippet doc/src/snippets/code/src_script_qscriptengine.cpp 24 + + If you want to be able to construct values of your custom type + from script code, you have to register a constructor function for + the type. For example: + + \snippet doc/src/snippets/code/src_script_qscriptengine.cpp 25 + + \sa qScriptRegisterSequenceMetaType(), qRegisterMetaType() +*/ + +/*! + \macro Q_SCRIPT_DECLARE_QMETAOBJECT(QMetaObject, ArgType) + \since 4.3 + \relates QScriptEngine + + Declares the given \a QMetaObject. Used in combination with + QScriptEngine::scriptValueFromQMetaObject() to make enums and + instantiation of \a QMetaObject available to script code. The + constructor generated by this macro takes a single argument of + type \a ArgType; typically the argument is the parent type of the + new instance, in which case \a ArgType is \c{QWidget*} or + \c{QObject*}. Objects created by the constructor will have + QScriptEngine::AutoOwnership ownership. +*/ + +/*! \fn int qScriptRegisterSequenceMetaType( + QScriptEngine *engine, + const QScriptValue &prototype = QScriptValue()) + \relates QScriptEngine + + Registers the sequence type \c{T} in the given \a engine. This + function provides conversion functions that convert between \c{T} + and Qt Script \c{Array} objects. \c{T} must provide a + const_iterator class and begin(), end() and push_back() + functions. If \a prototype is valid, it will be set as the + prototype of \c{Array} objects due to conversion from \c{T}; + otherwise, the standard \c{Array} prototype will be used. + + Returns the internal ID used by QMetaType. + + You need to declare the container type first with + Q_DECLARE_METATYPE(). If the element type isn't a standard Qt/C++ + type, it must be declared using Q_DECLARE_METATYPE() as well. + Example: + + \snippet doc/src/snippets/code/src_script_qscriptengine.cpp 26 + + \sa qScriptRegisterMetaType() +*/ + +/*! + Runs the garbage collector. + + The garbage collector will attempt to reclaim memory by locating and + disposing of objects that are no longer reachable in the script + environment. + + Normally you don't need to call this function; the garbage collector + will automatically be invoked when the QScriptEngine decides that + it's wise to do so (i.e. when a certain number of new objects have + been created). However, you can call this function to explicitly + request that garbage collection should be performed as soon as + possible. + + \sa reportAdditionalMemoryCost() +*/ +void QScriptEngine::collectGarbage() +{ + Q_D(QScriptEngine); + d->collectGarbage(); +} + +/*! + \since 4.7 + + Reports an additional memory cost of the given \a size, measured in + bytes, to the garbage collector. + + This function can be called to indicate that a Qt Script object has + memory associated with it that isn't managed by Qt Script itself. + Reporting the additional cost makes it more likely that the garbage + collector will be triggered. + + Note that if the additional memory is shared with objects outside + the scripting environment, the cost should not be reported, since + collecting the Qt Script object would not cause the memory to be + freed anyway. + + Negative \a size values are ignored, i.e. this function can't be + used to report that the additional memory has been deallocated. + + \sa collectGarbage() +*/ +void QScriptEngine::reportAdditionalMemoryCost(int size) +{ + Q_D(QScriptEngine); + d->reportAdditionalMemoryCost(size); +} + +/*! + + Sets the interval between calls to QCoreApplication::processEvents + to \a interval milliseconds. + + While the interpreter is running, all event processing is by default + blocked. This means for instance that the gui will not be updated + and timers will not be fired. To allow event processing during + interpreter execution one can specify the processing interval to be + a positive value, indicating the number of milliseconds between each + time QCoreApplication::processEvents() is called. + + The default value is -1, which disables event processing during + interpreter execution. + + You can use QCoreApplication::postEvent() to post an event that + performs custom processing at the next interval. For example, you + could keep track of the total running time of the script and call + abortEvaluation() when you detect that the script has been running + for a long time without completing. + + \sa processEventsInterval() +*/ +void QScriptEngine::setProcessEventsInterval(int interval) +{ + Q_D(QScriptEngine); + d->processEventsInterval = interval; + + if (interval > 0) + d->globalData->timeoutChecker->setCheckInterval(interval); + + d->timeoutChecker()->setShouldProcessEvents(interval > 0); +} + +/*! + + Returns the interval in milliseconds between calls to + QCoreApplication::processEvents() while the interpreter is running. + + \sa setProcessEventsInterval() +*/ +int QScriptEngine::processEventsInterval() const +{ + Q_D(const QScriptEngine); + return d->processEventsInterval; +} + +/*! + \since 4.4 + + Returns true if this engine is currently evaluating a script, + otherwise returns false. + + \sa evaluate(), abortEvaluation() +*/ +bool QScriptEngine::isEvaluating() const +{ + Q_D(const QScriptEngine); + return (d->currentFrame != d->globalExec()) || d->inEval; +} + +/*! + \since 4.4 + + Aborts any script evaluation currently taking place in this engine. + The given \a result is passed back as the result of the evaluation + (i.e. it is returned from the call to evaluate() being aborted). + + If the engine isn't evaluating a script (i.e. isEvaluating() returns + false), this function does nothing. + + Call this function if you need to abort a running script for some + reason, e.g. when you have detected that the script has been + running for several seconds without completing. + + \sa evaluate(), isEvaluating(), setProcessEventsInterval() +*/ +void QScriptEngine::abortEvaluation(const QScriptValue &result) +{ + Q_D(QScriptEngine); + if (!isEvaluating()) + return; + d->abortResult = result; + d->timeoutChecker()->setShouldAbort(true); + JSC::throwError(d->currentFrame, JSC::createInterruptedExecutionException(&d->currentFrame->globalData()).toObject(d->currentFrame)); +} + +#ifndef QT_NO_QOBJECT + +/*! + \since 4.4 + \relates QScriptEngine + + Creates a connection from the \a signal in the \a sender to the + given \a function. If \a receiver is an object, it will act as the + `this' object when the signal handler function is invoked. Returns + true if the connection succeeds; otherwise returns false. + + \sa qScriptDisconnect(), QScriptEngine::signalHandlerException() +*/ +bool qScriptConnect(QObject *sender, const char *signal, + const QScriptValue &receiver, const QScriptValue &function) +{ + if (!sender || !signal) + return false; + if (!function.isFunction()) + return false; + if (receiver.isObject() && (receiver.engine() != function.engine())) + return false; + QScriptEnginePrivate *engine = QScriptEnginePrivate::get(function.engine()); + QScript::APIShim shim(engine); + JSC::JSValue jscReceiver = engine->scriptValueToJSCValue(receiver); + JSC::JSValue jscFunction = engine->scriptValueToJSCValue(function); + return engine->scriptConnect(sender, signal, jscReceiver, jscFunction, + Qt::AutoConnection); +} + +/*! + \since 4.4 + \relates QScriptEngine + + Disconnects the \a signal in the \a sender from the given (\a + receiver, \a function) pair. Returns true if the connection is + successfully broken; otherwise returns false. + + \sa qScriptConnect() +*/ +bool qScriptDisconnect(QObject *sender, const char *signal, + const QScriptValue &receiver, const QScriptValue &function) +{ + if (!sender || !signal) + return false; + if (!function.isFunction()) + return false; + if (receiver.isObject() && (receiver.engine() != function.engine())) + return false; + QScriptEnginePrivate *engine = QScriptEnginePrivate::get(function.engine()); + QScript::APIShim shim(engine); + JSC::JSValue jscReceiver = engine->scriptValueToJSCValue(receiver); + JSC::JSValue jscFunction = engine->scriptValueToJSCValue(function); + return engine->scriptDisconnect(sender, signal, jscReceiver, jscFunction); +} + +/*! + \since 4.4 + \fn void QScriptEngine::signalHandlerException(const QScriptValue &exception) + + This signal is emitted when a script function connected to a signal causes + an \a exception. + + \sa qScriptConnect() +*/ + +QT_BEGIN_INCLUDE_NAMESPACE +#include "moc_qscriptengine.cpp" +QT_END_INCLUDE_NAMESPACE + +#endif // QT_NO_QOBJECT + +/*! + \since 4.4 + + Installs the given \a agent on this engine. The agent will be + notified of various events pertaining to script execution. This is + useful when you want to find out exactly what the engine is doing, + e.g. when evaluate() is called. The agent interface is the basis of + tools like debuggers and profilers. + + The engine maintains ownership of the \a agent. + + Calling this function will replace the existing agent, if any. + + \sa agent() +*/ +void QScriptEngine::setAgent(QScriptEngineAgent *agent) +{ + Q_D(QScriptEngine); + if (agent && (agent->engine() != this)) { + qWarning("QScriptEngine::setAgent(): " + "cannot set agent belonging to different engine"); + return; + } + QScript::APIShim shim(d); + if (d->activeAgent) + QScriptEngineAgentPrivate::get(d->activeAgent)->detach(); + d->activeAgent = agent; + if (agent) { + QScriptEngineAgentPrivate::get(agent)->attach(); + } +} + +/*! + \since 4.4 + + Returns the agent currently installed on this engine, or 0 if no + agent is installed. + + \sa setAgent() +*/ +QScriptEngineAgent *QScriptEngine::agent() const +{ + Q_D(const QScriptEngine); + return d->activeAgent; +} + +/*! + \since 4.4 + + Returns a handle that represents the given string, \a str. + + QScriptString can be used to quickly look up properties, and + compare property names, of script objects. + + \sa QScriptValue::property() +*/ +QScriptString QScriptEngine::toStringHandle(const QString &str) +{ + Q_D(QScriptEngine); + QScript::APIShim shim(d); + return d->toStringHandle(JSC::Identifier(d->currentFrame, str)); +} + +/*! + \since 4.5 + + Converts the given \a value to an object, if such a conversion is + possible; otherwise returns an invalid QScriptValue. The conversion + is performed according to the following table: + + \table + \header \o Input Type \o Result + \row \o Undefined \o An invalid QScriptValue. + \row \o Null \o An invalid QScriptValue. + \row \o Boolean \o A new Boolean object whose internal value is set to the value of the boolean. + \row \o Number \o A new Number object whose internal value is set to the value of the number. + \row \o String \o A new String object whose internal value is set to the value of the string. + \row \o Object \o The result is the object itself (no conversion). + \endtable + + \sa newObject() +*/ +QScriptValue QScriptEngine::toObject(const QScriptValue &value) +{ + Q_D(QScriptEngine); + QScript::APIShim shim(d); + JSC::JSValue jscValue = d->scriptValueToJSCValue(value); + if (!jscValue || jscValue.isUndefined() || jscValue.isNull()) + return QScriptValue(); + JSC::ExecState* exec = d->currentFrame; + JSC::JSValue result = jscValue.toObject(exec); + return d->scriptValueFromJSCValue(result); +} + +/*! + \internal + + Returns the object with the given \a id, or an invalid + QScriptValue if there is no object with that id. + + \sa QScriptValue::objectId() +*/ +QScriptValue QScriptEngine::objectById(qint64 id) const +{ + Q_D(const QScriptEngine); + // Assumes that the cell was not been garbage collected + return const_cast<QScriptEnginePrivate*>(d)->scriptValueFromJSCValue((JSC::JSCell*)id); +} + +/*! + \since 4.5 + \class QScriptSyntaxCheckResult + + \brief The QScriptSyntaxCheckResult class provides the result of a script syntax check. + + \ingroup script + \mainclass + + QScriptSyntaxCheckResult is returned by QScriptEngine::checkSyntax() to + provide information about the syntactical (in)correctness of a script. +*/ + +/*! + \enum QScriptSyntaxCheckResult::State + + This enum specifies the state of a syntax check. + + \value Error The program contains a syntax error. + \value Intermediate The program is incomplete. + \value Valid The program is a syntactically correct Qt Script program. +*/ + +/*! + Constructs a new QScriptSyntaxCheckResult from the \a other result. +*/ +QScriptSyntaxCheckResult::QScriptSyntaxCheckResult(const QScriptSyntaxCheckResult &other) + : d_ptr(other.d_ptr) +{ +} + +/*! + \internal +*/ +QScriptSyntaxCheckResult::QScriptSyntaxCheckResult(QScriptSyntaxCheckResultPrivate *d) + : d_ptr(d) +{ +} + +/*! + \internal +*/ +QScriptSyntaxCheckResult::QScriptSyntaxCheckResult() + : d_ptr(0) +{ +} + +/*! + Destroys this QScriptSyntaxCheckResult. +*/ +QScriptSyntaxCheckResult::~QScriptSyntaxCheckResult() +{ +} + +/*! + Returns the state of this QScriptSyntaxCheckResult. +*/ +QScriptSyntaxCheckResult::State QScriptSyntaxCheckResult::state() const +{ + Q_D(const QScriptSyntaxCheckResult); + if (!d) + return Valid; + return d->state; +} + +/*! + Returns the error line number of this QScriptSyntaxCheckResult, or -1 if + there is no error. + + \sa state(), errorMessage() +*/ +int QScriptSyntaxCheckResult::errorLineNumber() const +{ + Q_D(const QScriptSyntaxCheckResult); + if (!d) + return -1; + return d->errorLineNumber; +} + +/*! + Returns the error column number of this QScriptSyntaxCheckResult, or -1 if + there is no error. + + \sa state(), errorLineNumber() +*/ +int QScriptSyntaxCheckResult::errorColumnNumber() const +{ + Q_D(const QScriptSyntaxCheckResult); + if (!d) + return -1; + return d->errorColumnNumber; +} + +/*! + Returns the error message of this QScriptSyntaxCheckResult, or an empty + string if there is no error. + + \sa state(), errorLineNumber() +*/ +QString QScriptSyntaxCheckResult::errorMessage() const +{ + Q_D(const QScriptSyntaxCheckResult); + if (!d) + return QString(); + return d->errorMessage; +} + +/*! + Assigns the \a other result to this QScriptSyntaxCheckResult, and returns a + reference to this QScriptSyntaxCheckResult. +*/ +QScriptSyntaxCheckResult &QScriptSyntaxCheckResult::operator=(const QScriptSyntaxCheckResult &other) +{ + d_ptr = other.d_ptr; + return *this; +} + +#ifdef QT_BUILD_INTERNAL +Q_AUTOTEST_EXPORT bool qt_script_isJITEnabled() +{ +#if ENABLE(JIT) + return true; +#else + return false; +#endif +} +#endif + +#ifdef Q_CC_MSVC +// Try to prevent compiler from crashing. +#pragma optimize("", off) +#endif + +QT_END_NAMESPACE diff --git a/src/script/api/qscriptengine.h b/src/script/api/qscriptengine.h new file mode 100644 index 0000000..6d2616f --- /dev/null +++ b/src/script/api/qscriptengine.h @@ -0,0 +1,451 @@ +/**************************************************************************** +** +** 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 QSCRIPTENGINE_H +#define QSCRIPTENGINE_H + +#include <QtCore/qmetatype.h> + +#include <QtCore/qvariant.h> +#include <QtCore/qsharedpointer.h> + +#ifndef QT_NO_QOBJECT +#include <QtCore/qobject.h> +#else +#include <QtCore/qobjectdefs.h> +#endif + +#include <QtScript/qscriptvalue.h> +#include <QtScript/qscriptcontext.h> +#include <QtScript/qscriptstring.h> +#include <QtScript/qscriptprogram.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Script) + +class QDateTime; +class QScriptClass; +class QScriptEngineAgent; +class QScriptEnginePrivate; + +#ifndef QT_NO_QOBJECT + +template <class T> +inline QScriptValue qscriptQMetaObjectConstructor(QScriptContext *, QScriptEngine *, T *) +{ + return QScriptValue(); +} + +#endif // QT_NO_QOBJECT + +#ifndef QT_NO_REGEXP +class QRegExp; +#endif + +template <typename T> +inline QScriptValue qScriptValueFromValue(QScriptEngine *, const T &); + +template <typename T> +inline T qscriptvalue_cast(const QScriptValue &); + +class QScriptSyntaxCheckResultPrivate; +class Q_SCRIPT_EXPORT QScriptSyntaxCheckResult +{ +public: + enum State { + Error, + Intermediate, + Valid + }; + + QScriptSyntaxCheckResult(const QScriptSyntaxCheckResult &other); + ~QScriptSyntaxCheckResult(); + + State state() const; + int errorLineNumber() const; + int errorColumnNumber() const; + QString errorMessage() const; + + QScriptSyntaxCheckResult &operator=(const QScriptSyntaxCheckResult &other); + +private: + QScriptSyntaxCheckResult(); + QScriptSyntaxCheckResult(QScriptSyntaxCheckResultPrivate *d); + QExplicitlySharedDataPointer<QScriptSyntaxCheckResultPrivate> d_ptr; + + Q_DECLARE_PRIVATE(QScriptSyntaxCheckResult) + friend class QScriptEngine; + friend class QScriptEnginePrivate; +}; + +class Q_SCRIPT_EXPORT QScriptEngine +#ifndef QT_NO_QOBJECT + : public QObject +#endif +{ +#ifndef QT_NO_QOBJECT + Q_OBJECT +#endif +public: + enum ValueOwnership { + QtOwnership, + ScriptOwnership, + AutoOwnership + }; + + enum QObjectWrapOption { + ExcludeChildObjects = 0x0001, + ExcludeSuperClassMethods = 0x0002, + ExcludeSuperClassProperties = 0x0004, + ExcludeSuperClassContents = 0x0006, + SkipMethodsInEnumeration = 0x0008, + ExcludeDeleteLater = 0x0010, + ExcludeSlots = 0x0020, + + AutoCreateDynamicProperties = 0x0100, + PreferExistingWrapperObject = 0x0200 + }; + Q_DECLARE_FLAGS(QObjectWrapOptions, QObjectWrapOption) + + QScriptEngine(); +#ifndef QT_NO_QOBJECT + explicit QScriptEngine(QObject *parent); +#endif + virtual ~QScriptEngine(); + + QScriptValue globalObject() const; + void setGlobalObject(const QScriptValue &object); + + QScriptContext *currentContext() const; + QScriptContext *pushContext(); + void popContext(); + + bool canEvaluate(const QString &program) const; + static QScriptSyntaxCheckResult checkSyntax(const QString &program); + + QScriptValue evaluate(const QString &program, const QString &fileName = QString(), int lineNumber = 1); + + QScriptValue evaluate(const QScriptProgram &program); + + bool isEvaluating() const; + void abortEvaluation(const QScriptValue &result = QScriptValue()); + + bool hasUncaughtException() const; + QScriptValue uncaughtException() const; + int uncaughtExceptionLineNumber() const; + QStringList uncaughtExceptionBacktrace() const; + void clearExceptions(); + + QScriptValue nullValue(); + QScriptValue undefinedValue(); + + typedef QScriptValue (*FunctionSignature)(QScriptContext *, QScriptEngine *); + typedef QScriptValue (*FunctionWithArgSignature)(QScriptContext *, QScriptEngine *, void *); + + QScriptValue newFunction(FunctionSignature signature, int length = 0); + QScriptValue newFunction(FunctionSignature signature, const QScriptValue &prototype, int length = 0); + + QScriptValue newFunction(FunctionWithArgSignature signature, void *arg); + + QScriptValue newVariant(const QVariant &value); + QScriptValue newVariant(const QScriptValue &object, const QVariant &value); + +#ifndef QT_NO_REGEXP + QScriptValue newRegExp(const QRegExp ®exp); +#endif + + QScriptValue newObject(); + QScriptValue newObject(QScriptClass *scriptClass, const QScriptValue &data = QScriptValue()); + QScriptValue newArray(uint length = 0); + QScriptValue newRegExp(const QString &pattern, const QString &flags); + QScriptValue newDate(qsreal value); + QScriptValue newDate(const QDateTime &value); + QScriptValue newActivationObject(); + +#ifndef QT_NO_QOBJECT + QScriptValue newQObject(QObject *object, ValueOwnership ownership = QtOwnership, + const QObjectWrapOptions &options = 0); + QScriptValue newQObject(const QScriptValue &scriptObject, QObject *qtObject, + ValueOwnership ownership = QtOwnership, + const QObjectWrapOptions &options = 0); + + QScriptValue newQMetaObject(const QMetaObject *metaObject, const QScriptValue &ctor = QScriptValue()); + + template <class T> QScriptValue scriptValueFromQMetaObject(); + +#endif // QT_NO_QOBJECT + + + + QScriptValue defaultPrototype(int metaTypeId) const; + void setDefaultPrototype(int metaTypeId, const QScriptValue &prototype); + + + typedef QScriptValue (*MarshalFunction)(QScriptEngine *, const void *); + typedef void (*DemarshalFunction)(const QScriptValue &, void *); + + + + template <typename T> + inline QScriptValue toScriptValue(const T &value) + { + return qScriptValueFromValue(this, value); + } + template <typename T> + inline T fromScriptValue(const QScriptValue &value) + { + return qscriptvalue_cast<T>(value); + } + + void installTranslatorFunctions(const QScriptValue &object = QScriptValue()); + + QScriptValue importExtension(const QString &extension); + QStringList availableExtensions() const; + QStringList importedExtensions() const; + + void collectGarbage(); + void reportAdditionalMemoryCost(int size); + + void setProcessEventsInterval(int interval); + int processEventsInterval() const; + + void setAgent(QScriptEngineAgent *agent); + QScriptEngineAgent *agent() const; + + QScriptString toStringHandle(const QString &str); + QScriptValue toObject(const QScriptValue &value); + + QScriptValue objectById(qint64 id) const; + +#ifndef QT_NO_QOBJECT +Q_SIGNALS: + void signalHandlerException(const QScriptValue &exception); +#endif + +private: + QScriptValue create(int type, const void *ptr); + + bool convert(const QScriptValue &value, int type, void *ptr); + static bool convertV2(const QScriptValue &value, int type, void *ptr); + + void registerCustomType(int type, MarshalFunction mf, DemarshalFunction df, + const QScriptValue &prototype); + + friend inline void qScriptRegisterMetaType_helper(QScriptEngine *, + int, MarshalFunction, DemarshalFunction, const QScriptValue &); + + friend inline QScriptValue qScriptValueFromValue_helper(QScriptEngine *, int, const void *); + + friend inline bool qscriptvalue_cast_helper(const QScriptValue &, int, void *); + +protected: +#ifdef QT_NO_QOBJECT + QScopedPointer<QScriptEnginePrivate> d_ptr; + + QScriptEngine(QScriptEnginePrivate &dd); +#else + QScriptEngine(QScriptEnginePrivate &dd, QObject *parent = 0); +#endif + +private: + Q_DECLARE_PRIVATE(QScriptEngine) + Q_DISABLE_COPY(QScriptEngine) +#ifndef QT_NO_QOBJECT + Q_PRIVATE_SLOT(d_func(), void _q_objectDestroyed(QObject *)) +#endif +}; + +#ifndef QT_NO_QOBJECT + +#define Q_SCRIPT_DECLARE_QMETAOBJECT(T, _Arg1) \ +template<> inline QScriptValue qscriptQMetaObjectConstructor<T>(QScriptContext *ctx, QScriptEngine *eng, T *) \ +{ \ + _Arg1 arg1 = qscriptvalue_cast<_Arg1> (ctx->argument(0)); \ + T* t = new T(arg1); \ + if (ctx->isCalledAsConstructor()) \ + return eng->newQObject(ctx->thisObject(), t, QScriptEngine::AutoOwnership); \ + QScriptValue o = eng->newQObject(t, QScriptEngine::AutoOwnership); \ + o.setPrototype(ctx->callee().property(QString::fromLatin1("prototype"))); \ + return o; \ +} + +template <class T> QScriptValue QScriptEngine::scriptValueFromQMetaObject() +{ + typedef QScriptValue(*ConstructPtr)(QScriptContext *, QScriptEngine *, T *); + ConstructPtr cptr = qscriptQMetaObjectConstructor<T>; + return newQMetaObject(&T::staticMetaObject, + newFunction(reinterpret_cast<FunctionWithArgSignature>(cptr), 0)); +} + +#ifdef QT_DEPRECATED +template <class T> +inline QT_DEPRECATED QScriptValue qScriptValueFromQMetaObject( + QScriptEngine *engine +#ifndef qdoc + , T * /* dummy */ = 0 +#endif + ) +{ + return engine->scriptValueFromQMetaObject<T>(); +} +#endif + +#endif // QT_NO_QOBJECT + +inline QScriptValue qScriptValueFromValue_helper(QScriptEngine *engine, int type, const void *ptr) +{ + if (!engine) + return QScriptValue(); + + return engine->create(type, ptr); +} + +template <typename T> +inline QScriptValue qScriptValueFromValue(QScriptEngine *engine, const T &t) +{ + return qScriptValueFromValue_helper(engine, qMetaTypeId<T>(), &t); +} + +template <> +inline QScriptValue qScriptValueFromValue<QVariant>(QScriptEngine *engine, const QVariant &v) +{ + return qScriptValueFromValue_helper(engine, v.userType(), v.data()); +} + +inline bool qscriptvalue_cast_helper(const QScriptValue &value, int type, void *ptr) +{ + return QScriptEngine::convertV2(value, type, ptr); +} + +template<typename T> +T qscriptvalue_cast(const QScriptValue &value) +{ + T t; + const int id = qMetaTypeId<T>(); + + if (qscriptvalue_cast_helper(value, id, &t)) + return t; + else if (value.isVariant()) + return qvariant_cast<T>(value.toVariant()); + + return T(); +} + +template <> +inline QVariant qscriptvalue_cast<QVariant>(const QScriptValue &value) +{ + return value.toVariant(); +} + +#ifdef QT_DEPRECATED +template <typename T> +inline QT_DEPRECATED T qScriptValueToValue(const QScriptValue &value) +{ + return qscriptvalue_cast<T>(value); +} +#endif + +inline void qScriptRegisterMetaType_helper(QScriptEngine *eng, int type, + QScriptEngine::MarshalFunction mf, + QScriptEngine::DemarshalFunction df, + const QScriptValue &prototype) +{ + eng->registerCustomType(type, mf, df, prototype); +} + +template<typename T> +int qScriptRegisterMetaType( + QScriptEngine *eng, + QScriptValue (*toScriptValue)(QScriptEngine *, const T &t), + void (*fromScriptValue)(const QScriptValue &, T &t), + const QScriptValue &prototype = QScriptValue() +#ifndef qdoc + , T * /* dummy */ = 0 +#endif +) +{ + const int id = qRegisterMetaType<T>(); // make sure it's registered + + qScriptRegisterMetaType_helper( + eng, id, reinterpret_cast<QScriptEngine::MarshalFunction>(toScriptValue), + reinterpret_cast<QScriptEngine::DemarshalFunction>(fromScriptValue), + prototype); + + return id; +} + +template <class Container> +QScriptValue qScriptValueFromSequence(QScriptEngine *eng, const Container &cont) +{ + QScriptValue a = eng->newArray(); + typename Container::const_iterator begin = cont.begin(); + typename Container::const_iterator end = cont.end(); + typename Container::const_iterator it; + quint32 i; + for (it = begin, i = 0; it != end; ++it, ++i) + a.setProperty(i, eng->toScriptValue(*it)); + return a; +} + +template <class Container> +void qScriptValueToSequence(const QScriptValue &value, Container &cont) +{ + quint32 len = value.property(QLatin1String("length")).toUInt32(); + for (quint32 i = 0; i < len; ++i) { + QScriptValue item = value.property(i); + cont.push_back(qscriptvalue_cast<typename Container::value_type>(item)); + } +} + +template<typename T> +int qScriptRegisterSequenceMetaType( + QScriptEngine *engine, + const QScriptValue &prototype = QScriptValue() +#ifndef qdoc + , T * /* dummy */ = 0 +#endif +) +{ + return qScriptRegisterMetaType<T>(engine, qScriptValueFromSequence, + qScriptValueToSequence, prototype); +} + +#ifndef QT_NO_QOBJECT +Q_SCRIPT_EXPORT bool qScriptConnect(QObject *sender, const char *signal, + const QScriptValue &receiver, + const QScriptValue &function); +Q_SCRIPT_EXPORT bool qScriptDisconnect(QObject *sender, const char *signal, + const QScriptValue &receiver, + const QScriptValue &function); +#endif // QT_NO_QOBJECT + +Q_DECLARE_OPERATORS_FOR_FLAGS(QScriptEngine::QObjectWrapOptions) + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QSCRIPTENGINE_H diff --git a/src/script/api/qscriptengine_p.h b/src/script/api/qscriptengine_p.h new file mode 100644 index 0000000..94d195e --- /dev/null +++ b/src/script/api/qscriptengine_p.h @@ -0,0 +1,1115 @@ +/**************************************************************************** +** +** 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 QSCRIPTENGINE_P_H +#define QSCRIPTENGINE_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 "private/qobject_p.h" + +#include <QtCore/qdatetime.h> +#include <QtCore/qhash.h> +#include <QtCore/qnumeric.h> +#include <QtCore/qregexp.h> +#include <QtCore/qset.h> +#include "qscriptvalue_p.h" +#include "qscriptstring_p.h" +#include "bridge/qscriptclassobject_p.h" +#include "bridge/qscriptdeclarativeclass_p.h" +#include "bridge/qscriptdeclarativeobject_p.h" +#include "bridge/qscriptobject_p.h" +#include "bridge/qscriptqobject_p.h" +#include "bridge/qscriptvariant_p.h" + +#include "DateConstructor.h" +#include "DateInstance.h" +#include "Debugger.h" +#include "ErrorInstance.h" +#include "JSArray.h" +#include "Executable.h" +#include "Lexer.h" +#include "RefPtr.h" +#include "RegExpConstructor.h" +#include "RegExpObject.h" +#include "SourceProvider.h" +#include "Structure.h" +#include "UString.h" +#include "JSGlobalObject.h" +#include "JSValue.h" + +namespace JSC +{ + class EvalExecutable; + class ExecState; + typedef ExecState CallFrame; + class JSCell; + class JSGlobalObject; +} + + +QT_BEGIN_NAMESPACE + +class QString; +class QStringList; +class QScriptContext; +class QScriptValue; +class QScriptTypeInfo; +class QScriptEngineAgent; +class QScriptEnginePrivate; +class QScriptSyntaxCheckResult; +class QScriptEngine; +class QScriptProgramPrivate; + +namespace QScript +{ + class QObjectPrototype; + class QMetaObjectPrototype; + class QVariantPrototype; +#ifndef QT_NO_QOBJECT + class QObjectData; +#endif + class TimeoutCheckerProxy; + + qint32 ToInt32(qsreal); + quint32 ToUInt32(qsreal); + quint16 ToUInt16(qsreal); + qsreal ToInteger(qsreal); + + inline bool ToBool(qsreal); + inline bool ToBool(const QString &); + inline qint32 ToInt32(const QString &); + inline quint32 ToUInt32(const QString &); + inline quint16 ToUInt16(const QString &); + inline qsreal ToInteger(const QString &); +#ifdef Q_CC_MSVC + // MSVC2008 crashes if these are inlined. + qsreal ToNumber(const QString &); + QString ToString(qsreal); +#else + inline qsreal ToNumber(const QString &); + inline QString ToString(qsreal); +#endif + + QDateTime MsToDateTime(JSC::ExecState *, qsreal); + qsreal DateTimeToMs(JSC::ExecState *, const QDateTime &); + + //some conversion helper functions + inline QScriptEnginePrivate *scriptEngineFromExec(const JSC::ExecState *exec); + bool isFunction(JSC::JSValue value); + + inline void convertToLatin1_helper(const UChar *i, int length, char *s); + inline QByteArray convertToLatin1(const JSC::UString &str); + + class UStringSourceProviderWithFeedback; + +struct GlobalClientData : public JSC::JSGlobalData::ClientData +{ + GlobalClientData(QScriptEnginePrivate *e) + : engine(e) {} + virtual ~GlobalClientData() {} + virtual void mark(JSC::MarkStack& markStack); + + QScriptEnginePrivate *engine; +}; + +} // namespace QScript + +class QScriptEnginePrivate +#ifndef QT_NO_QOBJECT + : public QObjectPrivate +#endif +{ + Q_DECLARE_PUBLIC(QScriptEngine) +public: + QScriptEnginePrivate(); + virtual ~QScriptEnginePrivate(); + + static QScriptEnginePrivate *get(QScriptEngine *q) { return q ? q->d_func() : 0; } + static QScriptEngine *get(QScriptEnginePrivate *d) { return d ? d->q_func() : 0; } + + static inline bool isArray(JSC::JSValue); + static inline bool isDate(JSC::JSValue); + static inline bool isError(JSC::JSValue); + static inline bool isObject(JSC::JSValue); + static inline bool isRegExp(JSC::JSValue); + static inline bool isVariant(JSC::JSValue); + static inline bool isQObject(JSC::JSValue); + static inline bool isQMetaObject(JSC::JSValue); + + static inline bool toBool(JSC::ExecState *, JSC::JSValue); + static inline qsreal toInteger(JSC::ExecState *, JSC::JSValue); + static inline qsreal toNumber(JSC::ExecState *, JSC::JSValue); + static inline qint32 toInt32(JSC::ExecState *, JSC::JSValue); + static inline quint32 toUInt32(JSC::ExecState *, JSC::JSValue); + static inline quint16 toUInt16(JSC::ExecState *, JSC::JSValue); + static inline JSC::UString toString(JSC::ExecState *, JSC::JSValue); + + static inline QDateTime toDateTime(JSC::ExecState *, JSC::JSValue); +#ifndef QT_NO_REGEXP + static QRegExp toRegExp(JSC::ExecState*, JSC::JSValue); +#endif + static QVariant toVariant(JSC::ExecState *, JSC::JSValue); + static inline QObject *toQObject(JSC::ExecState *, JSC::JSValue); + static inline const QMetaObject *toQMetaObject(JSC::ExecState *, JSC::JSValue); + + static inline JSC::JSValue property(JSC::ExecState*, JSC::JSValue, const JSC::Identifier &id, + int resolveMode = QScriptValue::ResolvePrototype); + static JSC::JSValue propertyHelper(JSC::ExecState*, JSC::JSValue, const JSC::Identifier &id, int resolveMode); + static inline JSC::JSValue property(JSC::ExecState*, JSC::JSValue, quint32 index, + int resolveMode = QScriptValue::ResolvePrototype); + static JSC::JSValue propertyHelper(JSC::ExecState*, JSC::JSValue, quint32, int resolveMode); + static inline JSC::JSValue property(JSC::ExecState*, JSC::JSValue, const JSC::UString &, int resolveMode); + static inline void setProperty(JSC::ExecState*, JSC::JSValue object, const JSC::UString &name, JSC::JSValue, + const QScriptValue::PropertyFlags &flags = QScriptValue::KeepExistingFlags); + static void setProperty(JSC::ExecState*, JSC::JSValue object, const JSC::Identifier &id, JSC::JSValue, + const QScriptValue::PropertyFlags &flags = QScriptValue::KeepExistingFlags); + static void setProperty(JSC::ExecState*, JSC::JSValue object, quint32 index, JSC::JSValue, + const QScriptValue::PropertyFlags &flags = QScriptValue::KeepExistingFlags); + static QScriptValue::PropertyFlags propertyFlags(JSC::ExecState*, JSC::JSValue value, + const JSC::Identifier &id, const QScriptValue::ResolveFlags &mode); + static inline QScriptValue::PropertyFlags propertyFlags(JSC::ExecState*, JSC::JSValue value, + const JSC::UString &name, const QScriptValue::ResolveFlags &mode); + + static bool convertValue(JSC::ExecState*, JSC::JSValue value, + int type, void *ptr); + static bool convertNumber(qsreal, int type, void *ptr); + static bool convertString(const QString &, int type, void *ptr); + static JSC::JSValue create(JSC::ExecState*, int type, const void *ptr); + bool hasDemarshalFunction(int type) const; + + inline QScriptValue scriptValueFromJSCValue(JSC::JSValue value); + inline JSC::JSValue scriptValueToJSCValue(const QScriptValue &value); + static inline unsigned propertyFlagsToJSCAttributes(const QScriptValue::PropertyFlags &flags); + + static inline JSC::JSValue jscValueFromVariant(JSC::ExecState*, const QVariant &value); + static QVariant jscValueToVariant(JSC::ExecState*, JSC::JSValue value, int targetType); + static inline QVariant &variantValue(JSC::JSValue value); + static inline void setVariantValue(JSC::JSValue objectValue, const QVariant &value); + + static JSC::JSValue arrayFromStringList(JSC::ExecState*, const QStringList &lst); + static QStringList stringListFromArray(JSC::ExecState*, JSC::JSValue arr); + + static JSC::JSValue arrayFromVariantList(JSC::ExecState*, const QVariantList &lst); + static QVariantList variantListFromArray(JSC::ExecState*, JSC::JSArray *arr); + + static JSC::JSValue objectFromVariantMap(JSC::ExecState*, const QVariantMap &vmap); + static QVariantMap variantMapFromObject(JSC::ExecState*, JSC::JSObject *obj); + + JSC::JSValue defaultPrototype(int metaTypeId) const; + void setDefaultPrototype(int metaTypeId, JSC::JSValue prototype); + + static inline QScriptContext *contextForFrame(JSC::ExecState *frame); + static inline JSC::ExecState *frameForContext(QScriptContext *context); + static inline const JSC::ExecState *frameForContext(const QScriptContext *context); + + static inline bool hasValidCodeBlockRegister(JSC::ExecState *frame); + + JSC::JSGlobalObject *originalGlobalObject() const; + JSC::JSObject *getOriginalGlobalObjectProxy(); + JSC::JSObject *customGlobalObject() const; + JSC::JSObject *globalObject() const; + void setGlobalObject(JSC::JSObject *object); + inline JSC::ExecState *globalExec() const; + JSC::JSValue toUsableValue(JSC::JSValue value); + static JSC::JSValue thisForContext(JSC::ExecState *frame); + static JSC::Register *thisRegisterForFrame(JSC::ExecState *frame); + + JSC::CallFrame *pushContext(JSC::CallFrame *exec, JSC::JSValue thisObject, const JSC::ArgList& args, + JSC::JSObject *callee, bool calledAsConstructor = false, bool clearScopeChain = false); + void popContext(); + + void mark(JSC::MarkStack& markStack); + bool isCollecting() const; + void collectGarbage(); + void reportAdditionalMemoryCost(int size); + + //flags that we set on the return value register for native function. (ie when codeBlock is 0) + enum ContextFlags { + NativeContext = 1, + CalledAsConstructorContext = 2, + HasScopeContext = 4, // Specifies that the is a QScriptActivationObject + ShouldRestoreCallFrame = 8 + }; + static uint contextFlags(JSC::ExecState *); + static void setContextFlags(JSC::ExecState *, uint); + + QScript::TimeoutCheckerProxy *timeoutChecker() const; + + void agentDeleted(QScriptEngineAgent *agent); + + static inline void saveException(JSC::ExecState *, JSC::JSValue *); + static inline void restoreException(JSC::ExecState *, JSC::JSValue); + + void setCurrentException(QScriptValue exception) { m_currentException = exception; } + QScriptValue currentException() const { return m_currentException; } + void clearCurrentException() { m_currentException.d_ptr.reset(); } + + static QScriptSyntaxCheckResult checkSyntax(const QString &program); + static bool canEvaluate(const QString &program); + + inline void registerScriptProgram(QScriptProgramPrivate *program); + inline void unregisterScriptProgram(QScriptProgramPrivate *program); + void detachAllRegisteredScriptPrograms(); + + inline QScriptValuePrivate *allocateScriptValuePrivate(size_t); + inline void freeScriptValuePrivate(QScriptValuePrivate *p); + + inline void registerScriptValue(QScriptValuePrivate *value); + inline void unregisterScriptValue(QScriptValuePrivate *value); + void detachAllRegisteredScriptValues(); + + inline void registerScriptString(QScriptStringPrivate *value); + inline void unregisterScriptString(QScriptStringPrivate *value); + void detachAllRegisteredScriptStrings(); + QScriptString toStringHandle(const JSC::Identifier &name); + + static inline JSC::JSValue newArray(JSC::ExecState *, uint length); + static inline JSC::JSValue newDate(JSC::ExecState *, qsreal value); + static inline JSC::JSValue newDate(JSC::ExecState *, const QDateTime &); + inline JSC::JSValue newObject(); + +#ifndef QT_NO_REGEXP + static JSC::JSValue newRegExp(JSC::ExecState *, const QRegExp &); +#endif + + static JSC::JSValue newRegExp(JSC::ExecState *, const QString &pattern, const QString &flags); + JSC::JSValue newVariant(const QVariant &); + JSC::JSValue newVariant(JSC::JSValue objectValue, const QVariant &); + + static inline QScriptDeclarativeClass *declarativeClass(JSC::JSValue); + static inline QScriptDeclarativeClass::Object *declarativeObject(JSC::JSValue); + + JSC::UString translationContextFromUrl(const JSC::UString &); + +#ifndef QT_NO_QOBJECT + JSC::JSValue newQObject(QObject *object, + QScriptEngine::ValueOwnership ownership = QScriptEngine::QtOwnership, + const QScriptEngine:: QObjectWrapOptions &options = 0); + JSC::JSValue newQMetaObject(const QMetaObject *metaObject, + JSC::JSValue ctor); + + static bool convertToNativeQObject(JSC::ExecState*, JSC::JSValue, + const QByteArray &targetType, + void **result); + + JSC::JSValue evaluateHelper(JSC::ExecState *exec, intptr_t sourceId, + JSC::EvalExecutable *executable, + bool &compile); + + QScript::QObjectData *qobjectData(QObject *object); + void disposeQObject(QObject *object); + void emitSignalHandlerException(); + + bool scriptConnect(QObject *sender, const char *signal, + JSC::JSValue receiver, JSC::JSValue function, + Qt::ConnectionType type); + bool scriptDisconnect(QObject *sender, const char *signal, + JSC::JSValue receiver, JSC::JSValue function); + + bool scriptConnect(QObject *sender, int index, + JSC::JSValue receiver, JSC::JSValue function, + JSC::JSValue senderWrapper, + Qt::ConnectionType type); + bool scriptDisconnect(QObject *sender, int index, + JSC::JSValue receiver, JSC::JSValue function); + + bool scriptConnect(JSC::JSValue signal, JSC::JSValue receiver, + JSC::JSValue function, Qt::ConnectionType type); + bool scriptDisconnect(JSC::JSValue signal, JSC::JSValue receiver, + JSC::JSValue function); + + // private slots + void _q_objectDestroyed(QObject *); +#endif + + JSC::JSGlobalData *globalData; + JSC::JSObject *originalGlobalObjectProxy; + JSC::ExecState *currentFrame; + + WTF::RefPtr<JSC::Structure> scriptObjectStructure; + WTF::RefPtr<JSC::Structure> staticScopeObjectStructure; + + QScript::QObjectPrototype *qobjectPrototype; + WTF::RefPtr<JSC::Structure> qobjectWrapperObjectStructure; + + QScript::QMetaObjectPrototype *qmetaobjectPrototype; + WTF::RefPtr<JSC::Structure> qmetaobjectWrapperObjectStructure; + + QScript::QVariantPrototype *variantPrototype; + WTF::RefPtr<JSC::Structure> variantWrapperObjectStructure; + + QList<QScriptEngineAgent*> ownedAgents; + QScriptEngineAgent *activeAgent; + int agentLineNumber; + QScriptValuePrivate *registeredScriptValues; + QScriptValuePrivate *freeScriptValues; + static const int maxFreeScriptValues = 256; + int freeScriptValuesCount; + QScriptStringPrivate *registeredScriptStrings; + QSet<QScriptProgramPrivate*> registeredScriptPrograms; + QHash<int, QScriptTypeInfo*> m_typeInfos; + int processEventsInterval; + QScriptValue abortResult; + bool inEval; + + JSC::UString cachedTranslationUrl; + JSC::UString cachedTranslationContext; + + QSet<QString> importedExtensions; + QSet<QString> extensionsBeingImported; + + QHash<intptr_t, QScript::UStringSourceProviderWithFeedback*> loadedScripts; + QScriptValue m_currentException; + + QSet<JSC::JSObject*> visitedConversionObjects; + +#ifndef QT_NO_QOBJECT + QHash<QObject*, QScript::QObjectData*> m_qobjectData; +#endif + +#ifdef QT_NO_QOBJECT + QScriptEngine *q_ptr; +#endif +}; + +namespace QScript +{ + +class APIShim +{ +public: + APIShim(QScriptEnginePrivate *engine) + : m_engine(engine), m_oldTable(JSC::setCurrentIdentifierTable(engine->globalData->identifierTable)) + { + } + ~APIShim() + { + JSC::setCurrentIdentifierTable(m_oldTable); + } + +private: + QScriptEnginePrivate *m_engine; + JSC::IdentifierTable *m_oldTable; +}; + +/*Helper class. Main purpose is to give debugger feedback about unloading and loading scripts. + It keeps pointer to JSGlobalObject assuming that it is always the same - there is no way to update + this data. Class is internal and used as an implementation detail in and only in QScriptEngine::evaluate.*/ +class UStringSourceProviderWithFeedback: public JSC::UStringSourceProvider +{ +public: + static PassRefPtr<UStringSourceProviderWithFeedback> create( + const JSC::UString& source, const JSC::UString& url, + int lineNumber, QScriptEnginePrivate* engine) + { + return adoptRef(new UStringSourceProviderWithFeedback(source, url, lineNumber, engine)); + } + + /* Destruction means that there is no more copies of script so create scriptUnload event + and unregister script in QScriptEnginePrivate::loadedScripts */ + virtual ~UStringSourceProviderWithFeedback() + { + if (m_ptr) { + if (JSC::Debugger* debugger = this->debugger()) + debugger->scriptUnload(asID()); + m_ptr->loadedScripts.remove(asID()); + } + } + + /* set internal QScriptEnginePrivate pointer to null and create unloadScript event, should be called + only if QScriptEnginePrivate is about to be destroyed.*/ + void disconnectFromEngine() + { + if (JSC::Debugger* debugger = this->debugger()) + debugger->scriptUnload(asID()); + m_ptr = 0; + } + + int columnNumberFromOffset(int offset) const + { + for (const UChar *c = m_source.data() + offset; c >= m_source.data(); --c) { + if (JSC::Lexer::isLineTerminator(*c)) + return offset - static_cast<int>(c - data()); + } + return offset + 1; + } + +protected: + UStringSourceProviderWithFeedback(const JSC::UString& source, const JSC::UString& url, + int lineNumber, QScriptEnginePrivate* engine) + : UStringSourceProvider(source, url), + m_ptr(engine) + { + if (JSC::Debugger* debugger = this->debugger()) + debugger->scriptLoad(asID(), source, url, lineNumber); + if (m_ptr) + m_ptr->loadedScripts.insert(asID(), this); + } + + JSC::Debugger* debugger() + { + //if m_ptr is null it mean that QScriptEnginePrivate was destroyed and scriptUnload was called + //else m_ptr is stable and we can use it as normal pointer without hesitation + if(!m_ptr) + return 0; //we are in ~QScriptEnginePrivate + else + return m_ptr->originalGlobalObject()->debugger(); //QScriptEnginePrivate is still alive + } + + //trace global object and debugger instance + QScriptEnginePrivate* m_ptr; +}; + +class SaveFrameHelper +{ +public: + SaveFrameHelper(QScriptEnginePrivate *eng, + JSC::ExecState *newFrame) + : engine(eng), oldFrame(eng->currentFrame) + { + eng->currentFrame = newFrame; + } + ~SaveFrameHelper() + { + engine->currentFrame = oldFrame; + } +private: + QScriptEnginePrivate *engine; + JSC::ExecState *oldFrame; +}; + +inline QScriptEnginePrivate *scriptEngineFromExec(const JSC::ExecState *exec) +{ + return static_cast<GlobalClientData*>(exec->globalData().clientData)->engine; +} + +#ifndef Q_CC_MSVC +// MSVC2008 crashes if these are inlined. + +inline QString ToString(qsreal value) +{ + return JSC::UString::from(value); +} + +inline qsreal ToNumber(const QString &value) +{ + return ((JSC::UString)value).toDouble(); +} + +#endif + +inline qint32 ToInt32(const QString &value) +{ + return ToInt32(ToNumber(value)); +} + +inline quint32 ToUInt32(const QString &value) +{ + return ToUInt32(ToNumber(value)); +} + +inline quint16 ToUInt16(const QString &value) +{ + return ToUInt16(ToNumber(value)); +} + +inline qsreal ToInteger(const QString &value) +{ + return ToInteger(ToNumber(value)); +} + +inline bool ToBool(qsreal value) +{ + return (value != 0) && !qIsNaN(value); +} + +inline bool ToBool(const QString &value) +{ + return !value.isEmpty(); +} + +inline void convertToLatin1_helper(const UChar *i, int length, char *s) +{ + const UChar *e = i + length; + while (i != e) + *(s++) = (uchar) *(i++); + *s = '\0'; +} + +inline QByteArray convertToLatin1(const JSC::UString &str) +{ + QByteArray ba(str.size(), Qt::Uninitialized); + convertToLatin1_helper(str.data(), str.size(), ba.data()); + return ba; +} + +} // namespace QScript + +inline void QScriptEnginePrivate::registerScriptProgram(QScriptProgramPrivate *program) +{ + Q_ASSERT(!registeredScriptPrograms.contains(program)); + registeredScriptPrograms.insert(program); +} + +inline void QScriptEnginePrivate::unregisterScriptProgram(QScriptProgramPrivate *program) +{ + Q_ASSERT(registeredScriptPrograms.contains(program)); + registeredScriptPrograms.remove(program); +} + +inline QScriptValuePrivate *QScriptEnginePrivate::allocateScriptValuePrivate(size_t size) +{ + if (freeScriptValues) { + QScriptValuePrivate *p = freeScriptValues; + freeScriptValues = p->next; + --freeScriptValuesCount; + return p; + } + return reinterpret_cast<QScriptValuePrivate*>(qMalloc(size)); +} + +inline void QScriptEnginePrivate::freeScriptValuePrivate(QScriptValuePrivate *p) +{ + if (freeScriptValuesCount < maxFreeScriptValues) { + p->next = freeScriptValues; + freeScriptValues = p; + ++freeScriptValuesCount; + } else { + qFree(p); + } +} + +inline void QScriptEnginePrivate::registerScriptValue(QScriptValuePrivate *value) +{ + value->prev = 0; + value->next = registeredScriptValues; + if (registeredScriptValues) + registeredScriptValues->prev = value; + registeredScriptValues = value; +} + +inline void QScriptEnginePrivate::unregisterScriptValue(QScriptValuePrivate *value) +{ + if (value->prev) + value->prev->next = value->next; + if (value->next) + value->next->prev = value->prev; + if (value == registeredScriptValues) + registeredScriptValues = value->next; + value->prev = 0; + value->next = 0; +} + +inline JSC::JSValue QScriptEnginePrivate::jscValueFromVariant(JSC::ExecState *exec, const QVariant &v) +{ + JSC::JSValue result = create(exec, v.userType(), v.data()); + Q_ASSERT(result); + return result; +} + +inline QScriptValue QScriptEnginePrivate::scriptValueFromJSCValue(JSC::JSValue value) +{ + if (!value) + return QScriptValue(); + + QScriptValuePrivate *p_value = new (this)QScriptValuePrivate(this); + p_value->initFrom(value); + return QScriptValuePrivate::toPublic(p_value); +} + +inline JSC::JSValue QScriptEnginePrivate::scriptValueToJSCValue(const QScriptValue &value) +{ + QScriptValuePrivate *vv = QScriptValuePrivate::get(value); + if (!vv) + return JSC::JSValue(); + if (vv->type != QScriptValuePrivate::JavaScriptCore) { + Q_ASSERT(!vv->engine || vv->engine == this); + vv->engine = this; + if (vv->type == QScriptValuePrivate::Number) { + vv->initFrom(JSC::jsNumber(currentFrame, vv->numberValue)); + } else { //QScriptValuePrivate::String + vv->initFrom(JSC::jsString(currentFrame, vv->stringValue)); + } + } + return vv->jscValue; +} + +inline unsigned QScriptEnginePrivate::propertyFlagsToJSCAttributes(const QScriptValue::PropertyFlags &flags) +{ + unsigned attribs = 0; + if (flags & QScriptValue::ReadOnly) + attribs |= JSC::ReadOnly; + if (flags & QScriptValue::SkipInEnumeration) + attribs |= JSC::DontEnum; + if (flags & QScriptValue::Undeletable) + attribs |= JSC::DontDelete; + attribs |= flags & QScriptValue::UserRange; + return attribs; +} + +inline QScriptValuePrivate::~QScriptValuePrivate() +{ + if (engine) + engine->unregisterScriptValue(this); +} + +inline void QScriptValuePrivate::initFrom(JSC::JSValue value) +{ + if (value.isCell()) { + Q_ASSERT(engine != 0); + value = engine->toUsableValue(value); + } + type = JavaScriptCore; + jscValue = value; + if (engine) + engine->registerScriptValue(this); +} + +inline void QScriptValuePrivate::initFrom(qsreal value) +{ + type = Number; + numberValue = value; + if (engine) + engine->registerScriptValue(this); +} + +inline void QScriptValuePrivate::initFrom(const QString &value) +{ + type = String; + stringValue = value; + if (engine) + engine->registerScriptValue(this); +} + +inline JSC::JSValue QScriptEnginePrivate::property(JSC::ExecState *exec, JSC::JSValue value, const JSC::UString &name, int resolveMode) +{ + return property(exec, value, JSC::Identifier(exec, name), resolveMode); +} + +inline JSC::JSValue QScriptEnginePrivate::property(JSC::ExecState *exec, JSC::JSValue value, const JSC::Identifier &id, int resolveMode) +{ + Q_ASSERT(isObject(value)); + JSC::JSObject *object = JSC::asObject(value); + JSC::PropertySlot slot(object); + if ((resolveMode & QScriptValue::ResolvePrototype) && object->getPropertySlot(exec, id, slot)) + return slot.getValue(exec, id); + return propertyHelper(exec, value, id, resolveMode); +} + +inline JSC::JSValue QScriptEnginePrivate::property(JSC::ExecState *exec, JSC::JSValue value, quint32 index, int resolveMode) +{ + Q_ASSERT(isObject(value)); + JSC::JSObject *object = JSC::asObject(value); + JSC::PropertySlot slot(object); + if ((resolveMode & QScriptValue::ResolvePrototype) && object->getPropertySlot(exec, index, slot)) + return slot.getValue(exec, index); + return propertyHelper(exec, value, index, resolveMode); +} + +inline QScriptValue::PropertyFlags QScriptEnginePrivate::propertyFlags(JSC::ExecState *exec, JSC::JSValue value, + const JSC::UString &name, + const QScriptValue::ResolveFlags &mode) +{ + return propertyFlags(exec, value, JSC::Identifier(exec, name), mode); +} + +inline void QScriptEnginePrivate::setProperty(JSC::ExecState *exec, JSC::JSValue objectValue, const JSC::UString &name, + JSC::JSValue value, const QScriptValue::PropertyFlags &flags) +{ + setProperty(exec, objectValue, JSC::Identifier(exec, name), value, flags); +} + +inline JSC::JSValue QScriptValuePrivate::property(const JSC::Identifier &id, const QScriptValue::ResolveFlags &resolveMode) const +{ + return QScriptEnginePrivate::property(engine->currentFrame, jscValue, id, resolveMode); +} + +inline JSC::JSValue QScriptValuePrivate::property(quint32 index, const QScriptValue::ResolveFlags &resolveMode) const +{ + return QScriptEnginePrivate::property(engine->currentFrame, jscValue, index, resolveMode); +} + +inline JSC::JSValue QScriptValuePrivate::property(const JSC::UString &name, const QScriptValue::ResolveFlags &resolveMode) const +{ + JSC::ExecState *exec = engine->currentFrame; + return QScriptEnginePrivate::property(exec, jscValue, JSC::Identifier(exec, name), resolveMode); +} + +inline QScriptValue::PropertyFlags QScriptValuePrivate::propertyFlags( + const JSC::Identifier &id, const QScriptValue::ResolveFlags &mode) const +{ + return QScriptEnginePrivate::propertyFlags(engine->currentFrame, jscValue, id, mode); +} + +inline void QScriptValuePrivate::setProperty(const JSC::Identifier &id, const JSC::JSValue &value, + const QScriptValue::PropertyFlags &flags) +{ + QScriptEnginePrivate::setProperty(engine->currentFrame, jscValue, id, value, flags); +} + +inline void QScriptValuePrivate::setProperty(quint32 index, const JSC::JSValue &value, + const QScriptValue::PropertyFlags &flags) +{ + QScriptEnginePrivate::setProperty(engine->currentFrame, jscValue, index, value, flags); +} + +inline void QScriptValuePrivate::setProperty(const JSC::UString &name, const JSC::JSValue &value, + const QScriptValue::PropertyFlags &flags) +{ + JSC::ExecState *exec = engine->currentFrame; + QScriptEnginePrivate::setProperty(exec, jscValue, JSC::Identifier(exec, name), value, flags); +} + +inline void* QScriptValuePrivate::operator new(size_t size, QScriptEnginePrivate *engine) +{ + if (engine) + return engine->allocateScriptValuePrivate(size); + return qMalloc(size); +} + +inline void QScriptValuePrivate::operator delete(void *ptr) +{ + QScriptValuePrivate *d = reinterpret_cast<QScriptValuePrivate*>(ptr); + if (d->engine) + d->engine->freeScriptValuePrivate(d); + else + qFree(d); +} + +inline void QScriptEnginePrivate::saveException(JSC::ExecState *exec, JSC::JSValue *val) +{ + if (exec) { + *val = exec->exception(); + exec->clearException(); + } else { + *val = JSC::JSValue(); + } +} + +inline void QScriptEnginePrivate::restoreException(JSC::ExecState *exec, JSC::JSValue val) +{ + if (exec && val) + exec->setException(val); +} + +inline void QScriptEnginePrivate::registerScriptString(QScriptStringPrivate *value) +{ + Q_ASSERT(value->type == QScriptStringPrivate::HeapAllocated); + value->prev = 0; + value->next = registeredScriptStrings; + if (registeredScriptStrings) + registeredScriptStrings->prev = value; + registeredScriptStrings = value; +} + +inline void QScriptEnginePrivate::unregisterScriptString(QScriptStringPrivate *value) +{ + Q_ASSERT(value->type == QScriptStringPrivate::HeapAllocated); + if (value->prev) + value->prev->next = value->next; + if (value->next) + value->next->prev = value->prev; + if (value == registeredScriptStrings) + registeredScriptStrings = value->next; + value->prev = 0; + value->next = 0; +} + +inline QScriptContext *QScriptEnginePrivate::contextForFrame(JSC::ExecState *frame) +{ + if (frame && frame->callerFrame()->hasHostCallFrameFlag() && !frame->callee() + && frame->callerFrame()->removeHostCallFrameFlag() == QScript::scriptEngineFromExec(frame)->globalExec()) { + //skip the "fake" context created in Interpreter::execute. + frame = frame->callerFrame()->removeHostCallFrameFlag(); + } + return reinterpret_cast<QScriptContext *>(frame); +} + +inline JSC::ExecState *QScriptEnginePrivate::frameForContext(QScriptContext *context) +{ + return reinterpret_cast<JSC::ExecState*>(context); +} + +inline const JSC::ExecState *QScriptEnginePrivate::frameForContext(const QScriptContext *context) +{ + return reinterpret_cast<const JSC::ExecState*>(context); +} + +inline bool QScriptEnginePrivate::hasValidCodeBlockRegister(JSC::ExecState *frame) +{ +#if ENABLE(JIT) + // Frames created by the VM don't have their CodeBlock register + // initialized. We can detect such frames by checking if the + // callee is a host JSFunction. + JSC::JSObject *callee = frame->callee(); + return !(callee && callee->inherits(&JSC::JSFunction::info) + && JSC::asFunction(callee)->isHostFunction()); +#else + Q_UNUSED(frame); + return true; +#endif +} + +inline JSC::ExecState *QScriptEnginePrivate::globalExec() const +{ + return originalGlobalObject()->globalExec(); +} + +inline JSC::JSValue QScriptEnginePrivate::newArray(JSC::ExecState *exec, uint length) +{ + return JSC::constructEmptyArray(exec, length); +} + +inline JSC::JSValue QScriptEnginePrivate::newDate(JSC::ExecState *exec, qsreal value) +{ + JSC::JSValue val = JSC::jsNumber(exec, value); + JSC::ArgList args(&val, 1); + return JSC::constructDate(exec, args); +} + +inline JSC::JSValue QScriptEnginePrivate::newDate(JSC::ExecState *exec, const QDateTime &value) +{ + return newDate(exec, QScript::DateTimeToMs(exec, value)); +} + +inline JSC::JSValue QScriptEnginePrivate::newObject() +{ + return new (currentFrame)QScriptObject(scriptObjectStructure); +} + +inline bool QScriptEnginePrivate::isObject(JSC::JSValue value) +{ + return value && value.isObject(); +} + +inline bool QScriptEnginePrivate::isArray(JSC::JSValue value) +{ + return isObject(value) && value.inherits(&JSC::JSArray::info); +} + +inline bool QScriptEnginePrivate::isDate(JSC::JSValue value) +{ + return isObject(value) && value.inherits(&JSC::DateInstance::info); +} + +inline bool QScriptEnginePrivate::isError(JSC::JSValue value) +{ + return isObject(value) && value.inherits(&JSC::ErrorInstance::info); +} + +inline bool QScriptEnginePrivate::isRegExp(JSC::JSValue value) +{ + return isObject(value) && value.inherits(&JSC::RegExpObject::info); +} + +inline bool QScriptEnginePrivate::isVariant(JSC::JSValue value) +{ + if (!isObject(value) || !value.inherits(&QScriptObject::info)) + return false; + QScriptObject *object = static_cast<QScriptObject*>(JSC::asObject(value)); + QScriptObjectDelegate *delegate = object->delegate(); + return (delegate && (delegate->type() == QScriptObjectDelegate::Variant)); +} + +inline bool QScriptEnginePrivate::isQObject(JSC::JSValue value) +{ +#ifndef QT_NO_QOBJECT + if (!isObject(value) || !value.inherits(&QScriptObject::info)) + return false; + QScriptObject *object = static_cast<QScriptObject*>(JSC::asObject(value)); + QScriptObjectDelegate *delegate = object->delegate(); + return (delegate && (delegate->type() == QScriptObjectDelegate::QtObject || + (delegate->type() == QScriptObjectDelegate::DeclarativeClassObject && + static_cast<QScript::DeclarativeObjectDelegate*>(delegate)->scriptClass()->isQObject()))); +#else + return false; +#endif +} + +inline bool QScriptEnginePrivate::isQMetaObject(JSC::JSValue value) +{ +#ifndef QT_NO_QOBJECT + return isObject(value) && JSC::asObject(value)->inherits(&QScript::QMetaObjectWrapperObject::info); +#else + return false; +#endif +} + +inline bool QScriptEnginePrivate::toBool(JSC::ExecState *exec, JSC::JSValue value) +{ + JSC::JSValue savedException; + saveException(exec, &savedException); + bool result = value.toBoolean(exec); + restoreException(exec, savedException); + return result; +} + +inline qsreal QScriptEnginePrivate::toInteger(JSC::ExecState *exec, JSC::JSValue value) +{ + JSC::JSValue savedException; + saveException(exec, &savedException); + qsreal result = value.toInteger(exec); + restoreException(exec, savedException); + return result; +} + +inline qsreal QScriptEnginePrivate::toNumber(JSC::ExecState *exec, JSC::JSValue value) +{ + JSC::JSValue savedException; + saveException(exec, &savedException); + qsreal result = value.toNumber(exec); + restoreException(exec, savedException); + return result; +} + +inline qint32 QScriptEnginePrivate::toInt32(JSC::ExecState *exec, JSC::JSValue value) +{ + JSC::JSValue savedException; + saveException(exec, &savedException); + qint32 result = value.toInt32(exec); + restoreException(exec, savedException); + return result; +} + +inline quint32 QScriptEnginePrivate::toUInt32(JSC::ExecState *exec, JSC::JSValue value) +{ + JSC::JSValue savedException; + saveException(exec, &savedException); + quint32 result = value.toUInt32(exec); + restoreException(exec, savedException); + return result; +} + +inline quint16 QScriptEnginePrivate::toUInt16(JSC::ExecState *exec, JSC::JSValue value) +{ + // ### no equivalent function in JSC + return QScript::ToUInt16(toNumber(exec, value)); +} + +inline JSC::UString QScriptEnginePrivate::toString(JSC::ExecState *exec, JSC::JSValue value) +{ + JSC::JSValue savedException; + saveException(exec, &savedException); + JSC::UString str = value.toString(exec); + if (exec && exec->hadException() && !str.size()) { + JSC::JSValue savedException2; + saveException(exec, &savedException2); + str = savedException2.toString(exec); + restoreException(exec, savedException2); + } + if (savedException) + restoreException(exec, savedException); + return str; +} + +inline QDateTime QScriptEnginePrivate::toDateTime(JSC::ExecState *exec, JSC::JSValue value) +{ + if (!isDate(value)) + return QDateTime(); + qsreal t = static_cast<JSC::DateInstance*>(JSC::asObject(value))->internalNumber(); + return QScript::MsToDateTime(exec, t); +} + +inline QObject *QScriptEnginePrivate::toQObject(JSC::ExecState *exec, JSC::JSValue value) +{ +#ifndef QT_NO_QOBJECT + if (isObject(value) && value.inherits(&QScriptObject::info)) { + QScriptObject *object = static_cast<QScriptObject*>(JSC::asObject(value)); + QScriptObjectDelegate *delegate = object->delegate(); + if (!delegate) + return 0; + if (delegate->type() == QScriptObjectDelegate::QtObject) + return static_cast<QScript::QObjectDelegate*>(delegate)->value(); + if (delegate->type() == QScriptObjectDelegate::DeclarativeClassObject) + return static_cast<QScript::DeclarativeObjectDelegate*>(delegate)->scriptClass()->toQObject(declarativeObject(value)); + if (delegate->type() == QScriptObjectDelegate::Variant) { + QVariant var = variantValue(value); + int type = var.userType(); + if ((type == QMetaType::QObjectStar) || (type == QMetaType::QWidgetStar)) + return *reinterpret_cast<QObject* const *>(var.constData()); + } + } +#endif + return 0; +} + +inline const QMetaObject *QScriptEnginePrivate::toQMetaObject(JSC::ExecState*, JSC::JSValue value) +{ +#ifndef QT_NO_QOBJECT + if (isQMetaObject(value)) + return static_cast<QScript::QMetaObjectWrapperObject*>(JSC::asObject(value))->value(); +#endif + return 0; +} + +inline QVariant &QScriptEnginePrivate::variantValue(JSC::JSValue value) +{ + Q_ASSERT(value.inherits(&QScriptObject::info)); + QScriptObjectDelegate *delegate = static_cast<QScriptObject*>(JSC::asObject(value))->delegate(); + Q_ASSERT(delegate && (delegate->type() == QScriptObjectDelegate::Variant)); + return static_cast<QScript::QVariantDelegate*>(delegate)->value(); +} + +inline void QScriptEnginePrivate::setVariantValue(JSC::JSValue objectValue, const QVariant &value) +{ + Q_ASSERT(objectValue.inherits(&QScriptObject::info)); + QScriptObjectDelegate *delegate = static_cast<QScriptObject*>(JSC::asObject(objectValue))->delegate(); + Q_ASSERT(delegate && (delegate->type() == QScriptObjectDelegate::Variant)); + static_cast<QScript::QVariantDelegate*>(delegate)->setValue(value); +} + +inline QScriptDeclarativeClass *QScriptEnginePrivate::declarativeClass(JSC::JSValue v) +{ + if (!QScriptEnginePrivate::isObject(v) || !v.inherits(&QScriptObject::info)) + return 0; + QScriptObject *scriptObject = static_cast<QScriptObject*>(JSC::asObject(v)); + QScriptObjectDelegate *delegate = scriptObject->delegate(); + if (!delegate || (delegate->type() != QScriptObjectDelegate::DeclarativeClassObject)) + return 0; + return static_cast<QScript::DeclarativeObjectDelegate*>(delegate)->scriptClass(); +} + +inline QScriptDeclarativeClass::Object *QScriptEnginePrivate::declarativeObject(JSC::JSValue v) +{ + if (!QScriptEnginePrivate::isObject(v) || !v.inherits(&QScriptObject::info)) + return 0; + QScriptObject *scriptObject = static_cast<QScriptObject*>(JSC::asObject(v)); + QScriptObjectDelegate *delegate = scriptObject->delegate(); + if (!delegate || (delegate->type() != QScriptObjectDelegate::DeclarativeClassObject)) + return 0; + return static_cast<QScript::DeclarativeObjectDelegate*>(delegate)->object(); +} + +QT_END_NAMESPACE + +#endif diff --git a/src/script/api/qscriptengineagent.cpp b/src/script/api/qscriptengineagent.cpp new file mode 100644 index 0000000..1eb3933 --- /dev/null +++ b/src/script/api/qscriptengineagent.cpp @@ -0,0 +1,510 @@ +/**************************************************************************** +** +** 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 "qscriptengineagent.h" +#include "qscriptengineagent_p.h" +#include "qscriptengine.h" +#include "qscriptengine_p.h" + +#include "CodeBlock.h" +#include "Instruction.h" + +QT_BEGIN_NAMESPACE + +/*! + \since 4.4 + \class QScriptEngineAgent + + \brief The QScriptEngineAgent class provides an interface to report events pertaining to QScriptEngine execution. + + \ingroup script + + + The QScriptEngineAgent class is the basis of tools that monitor and/or control the execution of a + QScriptEngine, such as debuggers and profilers. + + To process script loading and unloading events, reimplement the + scriptLoad() and scriptUnload() functions. scriptLoad() is called + after the input to QScriptEngine::evaluate() has been parsed, right + before the given script is executed. The engine assigns each + script an ID, which is available as one of the arguments to + scriptLoad(); subsequently, other event handlers can use the ID to + identify a particular script. One common usage of scriptLoad() is + to retain the script text, filename and base line number (the + original input to QScriptEngine::evaluate()), so that other event + handlers can e.g. map a line number to the corresponding line of + text. + + scriptUnload() is called when the QScriptEngine has no further use + for a script; the QScriptEngineAgent may at this point safely + discard any resources associated with the script (such as the + script text). Note that after scriptUnload() has been called, the + QScriptEngine may reuse the relevant script ID for new scripts + (i.e. as argument to a subsequent call to scriptLoad()). + + Evaluating the following script will result in scriptUnload() + being called immediately after evaluation has completed: + + \snippet doc/src/snippets/code/src_script_qscriptengineagent.cpp 0 + + Evaluating the following script will \b{not} result in a call to + scriptUnload() when evaluation has completed: + + \snippet doc/src/snippets/code/src_script_qscriptengineagent.cpp 1 + + The script isn't unloaded because it defines a function (\c{cube}) + that remains in the script environment after evaluation has + completed. If a subsequent script removed the \c{cube} function + (e.g. by setting it to \c{null}), scriptUnload() would be called + when the function is garbage collected. In general terms, a script + isn't unloaded until the engine has determined that none of its + contents is referenced. + + To process script function calls and returns, reimplement the + functionEntry() and functionExit() functions. functionEntry() is + called when a script function is about to be executed; + functionExit() is called when a script function is about to return, + either normally or due to an exception. + + To process individual script statements, reimplement + positionChange(). positionChange() is called each time the engine is + about to execute a new statement of a script, and thus offers the + finest level of script monitoring. + + To process exceptions, reimplement exceptionThrow() and + exceptionCatch(). exceptionThrow() is called when a script exception + is thrown, before it has been handled. exceptionCatch() is called + when an exception handler is present, and execution is about to be + resumed at the handler code. + + \sa QScriptEngine::setAgent(), QScriptContextInfo +*/ + +/*! + \enum QScriptEngineAgent::Extension + + This enum specifies the possible extensions to a QScriptEngineAgent. + + \value DebuggerInvocationRequest The agent handles \c{debugger} script statements. + + \sa extension() +*/ + + +void QScriptEngineAgentPrivate::attach() +{ + if (engine->originalGlobalObject()->debugger()) + engine->originalGlobalObject()->setDebugger(0); + JSC::Debugger::attach(engine->originalGlobalObject()); + if (!QScriptEnginePrivate::get(engine)->isEvaluating()) + JSC::Debugger::recompileAllJSFunctions(engine->globalData); +} + +void QScriptEngineAgentPrivate::detach() +{ + JSC::Debugger::detach(engine->originalGlobalObject()); +} + +void QScriptEngineAgentPrivate::returnEvent(const JSC::DebuggerCallFrame& frame, intptr_t sourceID, int lineno) +{ + Q_UNUSED(frame); + Q_UNUSED(lineno); + Q_UNUSED(sourceID); +} + +void QScriptEngineAgentPrivate::exceptionThrow(const JSC::DebuggerCallFrame& frame, intptr_t sourceID, bool hasHandler) +{ + JSC::CallFrame *oldFrame = engine->currentFrame; + int oldAgentLineNumber = engine->agentLineNumber; + engine->currentFrame = frame.callFrame(); + QScriptValue value(engine->scriptValueFromJSCValue(frame.exception())); + engine->agentLineNumber = value.property(QLatin1String("lineNumber")).toInt32(); + q_ptr->exceptionThrow(sourceID, value, hasHandler); + engine->agentLineNumber = oldAgentLineNumber; + engine->currentFrame = oldFrame; + engine->setCurrentException(value); +}; + +void QScriptEngineAgentPrivate::exceptionCatch(const JSC::DebuggerCallFrame& frame, intptr_t sourceID) +{ + JSC::CallFrame *oldFrame = engine->currentFrame; + engine->currentFrame = frame.callFrame(); + QScriptValue value(engine->scriptValueFromJSCValue(frame.exception())); + q_ptr->exceptionCatch(sourceID, value); + engine->currentFrame = oldFrame; + engine->clearCurrentException(); +} + +void QScriptEngineAgentPrivate::atStatement(const JSC::DebuggerCallFrame& frame, intptr_t sourceID, int lineno/*, int column*/) +{ + QScript::UStringSourceProviderWithFeedback *source = engine->loadedScripts.value(sourceID); + if (!source) { + // QTBUG-6108: We don't have the source for this script, so ignore. + return; + } +// column = source->columnNumberFromOffset(column); + int column = 1; + JSC::CallFrame *oldFrame = engine->currentFrame; + int oldAgentLineNumber = engine->agentLineNumber; + engine->currentFrame = frame.callFrame(); + engine->agentLineNumber = lineno; + q_ptr->positionChange(sourceID, lineno, column); + engine->currentFrame = oldFrame; + engine->agentLineNumber = oldAgentLineNumber; +} + +void QScriptEngineAgentPrivate::functionExit(const JSC::JSValue& returnValue, intptr_t sourceID) +{ + QScriptValue result = engine->scriptValueFromJSCValue(returnValue); + q_ptr->functionExit(sourceID, result); + q_ptr->contextPop(); +} + +void QScriptEngineAgentPrivate::evaluateStop(const JSC::JSValue& returnValue, intptr_t sourceID) +{ + QScriptValue result = engine->scriptValueFromJSCValue(returnValue); + q_ptr->functionExit(sourceID, result); +} + +void QScriptEngineAgentPrivate::didReachBreakpoint(const JSC::DebuggerCallFrame& frame, + intptr_t sourceID, int lineno/*, int column*/) +{ + if (q_ptr->supportsExtension(QScriptEngineAgent::DebuggerInvocationRequest)) { + QScript::UStringSourceProviderWithFeedback *source = engine->loadedScripts.value(sourceID); + if (!source) { + // QTBUG-6108: We don't have the source for this script, so ignore. + return; + } +// column = source->columnNumberFromOffset(column); + int column = 1; + JSC::CallFrame *oldFrame = engine->currentFrame; + int oldAgentLineNumber = engine->agentLineNumber; + engine->currentFrame = frame.callFrame(); + engine->agentLineNumber = lineno; + QList<QVariant> args; + args << qint64(sourceID) << lineno << column; + q_ptr->extension(QScriptEngineAgent::DebuggerInvocationRequest, args); + engine->currentFrame = oldFrame; + engine->agentLineNumber = oldAgentLineNumber; + } +}; + +/*! + Constructs a QScriptEngineAgent object for the given \a engine. + + The engine takes ownership of the agent. + + Call QScriptEngine::setAgent() to make this agent the active + agent. +*/ +QScriptEngineAgent::QScriptEngineAgent(QScriptEngine *engine) + : d_ptr(new QScriptEngineAgentPrivate()) +{ + d_ptr->q_ptr = this; + d_ptr->engine = QScriptEnginePrivate::get(engine); + d_ptr->engine->ownedAgents.append(this); +} + +/*! + \internal +*/ +QScriptEngineAgent::QScriptEngineAgent(QScriptEngineAgentPrivate &dd, QScriptEngine *engine) + : d_ptr(&dd) +{ + d_ptr->q_ptr = this; + d_ptr->engine = QScriptEnginePrivate::get(engine); +} + +/*! + Destroys this QScriptEngineAgent. +*/ +QScriptEngineAgent::~QScriptEngineAgent() +{ + d_ptr->engine->agentDeleted(this); //### TODO: Can this throw? +} + +/*! + + This function is called when the engine has parsed a script and has + associated it with the given \a id. The id can be used to identify + this particular script in subsequent event notifications. + + \a program, \a fileName and \a baseLineNumber are the original + arguments to the QScriptEngine::evaluate() call that triggered this + event. + + This function is called just before the script is about to be + evaluated. + + You can reimplement this function to record information about the + script; for example, by retaining the script text, you can obtain + the line of text corresponding to a line number in a subsequent + call to positionChange(). + + The default implementation does nothing. + + \sa scriptUnload() +*/ +void QScriptEngineAgent::scriptLoad(qint64 id, const QString &program, + const QString &fileName, int baseLineNumber) +{ + Q_UNUSED(id); + Q_UNUSED(program); + Q_UNUSED(fileName); + Q_UNUSED(baseLineNumber); +} + +/*! + This function is called when the engine has discarded the script + identified by the given \a id. + + You can reimplement this function to clean up any resources you have + associated with the script. + + The default implementation does nothing. + + \sa scriptLoad() +*/ +void QScriptEngineAgent::scriptUnload(qint64 id) +{ + Q_UNUSED(id); +} + +/*! + This function is called when a new script context has been pushed. + + The default implementation does nothing. + + \sa contextPop(), functionEntry() +*/ +void QScriptEngineAgent::contextPush() +{ +} + +/*! + This function is called when the current script context is about to + be popped. + + The default implementation does nothing. + + \sa contextPush(), functionExit() +*/ +void QScriptEngineAgent::contextPop() +{ +} + +/*! + This function is called when a script function is called in the + engine. If the script function is not a native Qt Script function, + it resides in the script identified by \a scriptId; otherwise, \a + scriptId is -1. + + This function is called just before execution of the script function + begins. You can obtain the QScriptContext associated with the + function call with QScriptEngine::currentContext(). The arguments + passed to the function are available. + + Reimplement this function to handle this event. For example, a + debugger implementation could reimplement this function (and + functionExit()) to keep track of the call stack and provide + step-over functionality. + + The default implementation does nothing. + + \sa functionExit(), positionChange(), QScriptEngine::currentContext() +*/ +void QScriptEngineAgent::functionEntry(qint64 scriptId) +{ + Q_UNUSED(scriptId); +} + +/*! + This function is called when the currently executing script function + is about to return. If the script function is not a native Qt Script + function, it resides in the script identified by \a scriptId; + otherwise, \a scriptId is -1. The \a returnValue is the value that + the script function will return. + + This function is called just before the script function returns. + You can still access the QScriptContext associated with the + script function call with QScriptEngine::currentContext(). + + If the engine's + \l{QScriptEngine::hasUncaughtException()}{hasUncaughtException}() + function returns true, the script function is exiting due to an + exception; otherwise, the script function is returning normally. + + Reimplement this function to handle this event; typically you will + then also want to reimplement functionEntry(). + + The default implementation does nothing. + + \sa functionEntry(), QScriptEngine::hasUncaughtException() +*/ +void QScriptEngineAgent::functionExit(qint64 scriptId, + const QScriptValue &returnValue) +{ + Q_UNUSED(scriptId); + Q_UNUSED(returnValue); +} + +/*! + This function is called when the engine is about to execute a new + statement in the script identified by \a scriptId. The statement + begins on the line and column specified by \a lineNumber + This event is not generated for native Qt Script functions. + + Reimplement this function to handle this event. For example, a + debugger implementation could reimplement this function to provide + line-by-line stepping, and a profiler implementation could use it to + count the number of times each statement is executed. + + The default implementation does nothing. + + \note \a columnNumber is undefined + + \sa scriptLoad(), functionEntry() +*/ +void QScriptEngineAgent::positionChange(qint64 scriptId, + int lineNumber, int columnNumber) +{ + Q_UNUSED(scriptId); + Q_UNUSED(lineNumber); + Q_UNUSED(columnNumber); +} + +/*! + This function is called when the given \a exception has occurred in + the engine, in the script identified by \a scriptId. If the + exception was thrown by a native Qt Script function, \a scriptId is + -1. + + If \a hasHandler is true, there is a \c{catch} or \c{finally} block + that will handle the exception. If \a hasHandler is false, there is + no handler for the exception. + + Reimplement this function if you want to handle this event. For + example, a debugger can notify the user when an uncaught exception + occurs (i.e. \a hasHandler is false). + + The default implementation does nothing. + + \sa exceptionCatch() +*/ +void QScriptEngineAgent::exceptionThrow(qint64 scriptId, + const QScriptValue &exception, + bool hasHandler) +{ + Q_UNUSED(scriptId); + Q_UNUSED(exception); + Q_UNUSED(hasHandler); +} + +/*! + This function is called when the given \a exception is about to be + caught, in the script identified by \a scriptId. + + Reimplement this function if you want to handle this event. + + The default implementation does nothing. + + \sa exceptionThrow() +*/ +void QScriptEngineAgent::exceptionCatch(qint64 scriptId, + const QScriptValue &exception) +{ + Q_UNUSED(scriptId); + Q_UNUSED(exception); +} + +#if 0 +/*! + This function is called when a property of the given \a object has + been added, changed or removed. + + Reimplement this function if you want to handle this event. + + The default implementation does nothing. +*/ +void QScriptEngineAgent::propertyChange(qint64 scriptId, + const QScriptValue &object, + const QString &propertyName, + PropertyChange change) +{ + Q_UNUSED(scriptId); + Q_UNUSED(object); + Q_UNUSED(propertyName); + Q_UNUSED(change); +} +#endif + +/*! + Returns true if the QScriptEngineAgent supports the given \a + extension; otherwise, false is returned. By default, no extensions + are supported. + + \sa extension() +*/ +bool QScriptEngineAgent::supportsExtension(Extension extension) const +{ + Q_UNUSED(extension); + return false; +} + +/*! + This virtual function can be reimplemented in a QScriptEngineAgent + subclass to provide support for extensions. The optional \a argument + can be provided as input to the \a extension; the result must be + returned in the form of a QVariant. You can call supportsExtension() + to check if an extension is supported by the QScriptEngineAgent. By + default, no extensions are supported, and this function returns an + invalid QVariant. + + If you implement the DebuggerInvocationRequest extension, Qt Script + will call this function when a \c{debugger} statement is encountered + in a script. The \a argument is a QVariantList containing three + items: The first item is the scriptId (a qint64), the second item is + the line number (an int), and the third item is the column number + (an int). + + \sa supportsExtension() +*/ +QVariant QScriptEngineAgent::extension(Extension extension, + const QVariant &argument) +{ + Q_UNUSED(extension); + Q_UNUSED(argument); + return QVariant(); +} + +/*! + Returns the QScriptEngine that this agent is associated with. +*/ +QScriptEngine *QScriptEngineAgent::engine() const +{ + Q_D(const QScriptEngineAgent); + return QScriptEnginePrivate::get(d->engine); +} + +QT_END_NAMESPACE diff --git a/src/script/api/qscriptengineagent.h b/src/script/api/qscriptengineagent.h new file mode 100644 index 0000000..0750313 --- /dev/null +++ b/src/script/api/qscriptengineagent.h @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** 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 QSCRIPTENGINEAGENT_H +#define QSCRIPTENGINEAGENT_H + +#include <QtCore/qobjectdefs.h> + +#include <QtCore/qvariant.h> +#include <QtCore/qscopedpointer.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Script) + +class QScriptEngine; +class QScriptValue; + +class QScriptEngineAgentPrivate; +class Q_SCRIPT_EXPORT QScriptEngineAgent +{ +public: + enum Extension { + DebuggerInvocationRequest + }; + + QScriptEngineAgent(QScriptEngine *engine); + virtual ~QScriptEngineAgent(); + + virtual void scriptLoad(qint64 id, const QString &program, + const QString &fileName, int baseLineNumber); + virtual void scriptUnload(qint64 id); + + virtual void contextPush(); + virtual void contextPop(); + + virtual void functionEntry(qint64 scriptId); + virtual void functionExit(qint64 scriptId, + const QScriptValue &returnValue); + + virtual void positionChange(qint64 scriptId, + int lineNumber, int columnNumber); + + virtual void exceptionThrow(qint64 scriptId, + const QScriptValue &exception, + bool hasHandler); + virtual void exceptionCatch(qint64 scriptId, + const QScriptValue &exception); + + virtual bool supportsExtension(Extension extension) const; + virtual QVariant extension(Extension extension, + const QVariant &argument = QVariant()); + + QScriptEngine *engine() const; + +protected: + QScriptEngineAgent(QScriptEngineAgentPrivate &dd, QScriptEngine *engine); + QScopedPointer<QScriptEngineAgentPrivate> d_ptr; + +private: + Q_DECLARE_PRIVATE(QScriptEngineAgent) + Q_DISABLE_COPY(QScriptEngineAgent) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/script/api/qscriptengineagent_p.h b/src/script/api/qscriptengineagent_p.h new file mode 100644 index 0000000..abe4e9e --- /dev/null +++ b/src/script/api/qscriptengineagent_p.h @@ -0,0 +1,125 @@ +/**************************************************************************** +** +** 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 QSCRIPTENGINEAGENT_P_H +#define QSCRIPTENGINEAGENT_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 "Debugger.h" +#include "qscriptengineagent.h" + +#include "CallFrame.h" +#include "SourceCode.h" +#include "UString.h" +#include "DebuggerCallFrame.h" + +QT_BEGIN_NAMESPACE + +class QScriptEnginePrivate; + +class QScriptEngineAgent; +class Q_SCRIPT_EXPORT QScriptEngineAgentPrivate : public JSC::Debugger +{ + Q_DECLARE_PUBLIC(QScriptEngineAgent) +public: + static QScriptEngineAgent* get(QScriptEngineAgentPrivate* p) {return p->q_func();} + static QScriptEngineAgentPrivate* get(QScriptEngineAgent* p) {return p->d_func();} + + QScriptEngineAgentPrivate(){} + virtual ~QScriptEngineAgentPrivate(){}; + + void attach(); + void detach(); + + //scripts + virtual void sourceParsed(JSC::ExecState*, const JSC::SourceCode&, int /*errorLine*/, const JSC::UString& /*errorMsg*/) {}; + virtual void scriptUnload(qint64 id) + { + q_ptr->scriptUnload(id); + }; + virtual void scriptLoad(qint64 id, const JSC::UString &program, + const JSC::UString &fileName, int baseLineNumber) + { + q_ptr->scriptLoad(id,program, fileName, baseLineNumber); + }; + + //exceptions + virtual void exception(const JSC::DebuggerCallFrame& frame, intptr_t sourceID, int lineno, bool hasHandler) + { + Q_UNUSED(frame); + Q_UNUSED(sourceID); + Q_UNUSED(lineno); + Q_UNUSED(hasHandler); + }; + virtual void exceptionThrow(const JSC::DebuggerCallFrame& frame, intptr_t sourceID, bool hasHandler); + virtual void exceptionCatch(const JSC::DebuggerCallFrame& frame, intptr_t sourceID); + + //statements + virtual void atStatement(const JSC::DebuggerCallFrame&, intptr_t sourceID, int lineno/*, int column*/); + virtual void callEvent(const JSC::DebuggerCallFrame&, intptr_t sourceID, int lineno) + { + Q_UNUSED(lineno); + q_ptr->contextPush(); + q_ptr->functionEntry(sourceID); + }; + virtual void returnEvent(const JSC::DebuggerCallFrame& frame, intptr_t sourceID, int lineno); + virtual void willExecuteProgram(const JSC::DebuggerCallFrame& frame, intptr_t sourceID, int lineno) + { + Q_UNUSED(frame); + Q_UNUSED(sourceID); + Q_UNUSED(lineno); + }; + virtual void didExecuteProgram(const JSC::DebuggerCallFrame& frame, intptr_t sourceID, int lineno) + { + Q_UNUSED(frame); + Q_UNUSED(sourceID); + Q_UNUSED(lineno); + }; + virtual void functionExit(const JSC::JSValue& returnValue, intptr_t sourceID); + //others + virtual void didReachBreakpoint(const JSC::DebuggerCallFrame& frame, intptr_t sourceID, int lineno/*, int column*/); + + virtual void evaluateStart(intptr_t sourceID) + { + q_ptr->functionEntry(sourceID); + } + virtual void evaluateStop(const JSC::JSValue& returnValue, intptr_t sourceID); + + QScriptEnginePrivate *engine; + QScriptEngineAgent *q_ptr; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/script/api/qscriptextensioninterface.h b/src/script/api/qscriptextensioninterface.h new file mode 100644 index 0000000..4e983b3 --- /dev/null +++ b/src/script/api/qscriptextensioninterface.h @@ -0,0 +1,52 @@ +/**************************************************************************** +** +** 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 QSCRIPTEXTENSIONINTERFACE_H +#define QSCRIPTEXTENSIONINTERFACE_H + +#include <QtCore/qfactoryinterface.h> + +#include <QtCore/qobject.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Script) + +class QScriptEngine; + +struct Q_SCRIPT_EXPORT QScriptExtensionInterface + : public QFactoryInterface +{ + virtual void initialize(const QString &key, QScriptEngine *engine) = 0; +}; + +Q_DECLARE_INTERFACE(QScriptExtensionInterface, + "com.trolltech.Qt.QScriptExtensionInterface/1.0") + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QSCRIPTEXTENSIONINTERFACE_H diff --git a/src/script/api/qscriptextensionplugin.cpp b/src/script/api/qscriptextensionplugin.cpp new file mode 100644 index 0000000..3c0de1a --- /dev/null +++ b/src/script/api/qscriptextensionplugin.cpp @@ -0,0 +1,125 @@ +/**************************************************************************** +** +** 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 "qscriptextensionplugin.h" + +#include "qscriptvalue.h" +#include "qscriptengine.h" + +QT_BEGIN_NAMESPACE + +/*! + \since 4.3 + \class QScriptExtensionPlugin + \brief The QScriptExtensionPlugin class provides an abstract base for custom QScript extension plugins. + + \ingroup plugins + + QScriptExtensionPlugin is a plugin interface that makes it + possible to offer extensions that can be loaded dynamically into + applications using the QScriptEngine class. + + Writing a script extension plugin is achieved by subclassing this + base class, reimplementing the pure virtual keys() and initialize() + functions, and exporting the class using the Q_EXPORT_PLUGIN2() + macro. See \l {How to Create Qt Plugins} for details. + + \sa QScriptEngine::importExtension(), {Creating QtScript Extensions} +*/ + +/*! + \fn QStringList QScriptExtensionPlugin::keys() const + + Returns the list of keys this plugin supports. + + These keys are usually the names of the "modules" or "packages" + that are implemented in the plugin (e.g. \c{com.mycompany.MyProduct}). + + \sa initialize() +*/ + +/*! + \fn void QScriptExtensionPlugin::initialize(const QString& key, QScriptEngine *engine) + + Initializes the extension specified by \a key in the given \a engine. + The key must come from the set of keys(). + + \sa keys() +*/ + +/*! + Constructs a script extension plugin with the given \a parent. + + Note that this constructor is invoked automatically by the + Q_EXPORT_PLUGIN2() macro, so there is no need for calling it + explicitly. +*/ +QScriptExtensionPlugin::QScriptExtensionPlugin(QObject *parent) + : QObject(parent) +{ +} + +/*! + Destroys the script extension plugin. + + Note that Qt destroys a plugin automatically when it is no longer + used, so there is no need for calling the destructor explicitly. +*/ +QScriptExtensionPlugin::~QScriptExtensionPlugin() +{ +} + +/*! + + This function is provided for convenience when reimplementing + initialize(). It splits the given \a key on \c{'.'} (dot), and + ensures that there's a corresponding path of objects in the + environment of the given \a engine, creating new objects to complete + the path if necessary. E.g. if the key is "com.trolltech", after + the call to setupPackage() the script expression \c{com.trolltech} + will evaluate to an object. More specifically, the engine's Global + Object will have a property called "com", which in turn has a + property called "trolltech". + + Use this function to avoid global namespace pollution when installing + your extensions in the engine. + + \sa initialize() +*/ +QScriptValue QScriptExtensionPlugin::setupPackage( + const QString &key, QScriptEngine *engine) const +{ + QStringList components = key.split(QLatin1Char('.')); + QScriptValue o = engine->globalObject(); + for (int i = 0; i < components.count(); ++i) { + QScriptValue oo = o.property(components.at(i)); + if (!oo.isValid()) { + oo = engine->newObject(); + o.setProperty(components.at(i), oo); + } + o = oo; + } + return o; +} + +QT_END_NAMESPACE diff --git a/src/script/api/qscriptextensionplugin.h b/src/script/api/qscriptextensionplugin.h new file mode 100644 index 0000000..c5f37ab --- /dev/null +++ b/src/script/api/qscriptextensionplugin.h @@ -0,0 +1,58 @@ +/**************************************************************************** +** +** 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 QSCRIPTEXTENSIONPLUGIN_H +#define QSCRIPTEXTENSIONPLUGIN_H + +#include <QtCore/qplugin.h> + +#include <QtScript/qscriptextensioninterface.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Script) + +class QScriptValue; + +class Q_SCRIPT_EXPORT QScriptExtensionPlugin : public QObject, + public QScriptExtensionInterface +{ + Q_OBJECT + Q_INTERFACES(QScriptExtensionInterface:QFactoryInterface) +public: + explicit QScriptExtensionPlugin(QObject *parent = 0); + ~QScriptExtensionPlugin(); + + virtual QStringList keys() const = 0; + virtual void initialize(const QString &key, QScriptEngine *engine) = 0; + + QScriptValue setupPackage(const QString &key, QScriptEngine *engine) const; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QSCRIPTEXTENSIONPLUGIN_H diff --git a/src/script/api/qscriptprogram.cpp b/src/script/api/qscriptprogram.cpp new file mode 100644 index 0000000..c0e2656 --- /dev/null +++ b/src/script/api/qscriptprogram.cpp @@ -0,0 +1,216 @@ +/**************************************************************************** +** +** 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 "qscriptprogram.h" +#include "qscriptprogram_p.h" +#include "qscriptengine.h" +#include "qscriptengine_p.h" + +#include "SamplingTool.h" +#include "Executable.h" + +QT_BEGIN_NAMESPACE + +/*! + \since 4.7 + \class QScriptProgram + + \brief The QScriptProgram class encapsulates a Qt Script program. + + \ingroup script + + QScriptProgram retains the compiled representation of the script if + possible. Thus, QScriptProgram can be used to evaluate the same + script multiple times more efficiently. + + \code + QScriptEngine engine; + QScriptProgram program("1 + 2"); + QScriptValue result = engine.evaluate(program); + \endcode +*/ + +QScriptProgramPrivate::QScriptProgramPrivate(const QString &src, + const QString &fn, + int ln) + : sourceCode(src), fileName(fn), firstLineNumber(ln), + engine(0), _executable(0), sourceId(-1), isCompiled(false) +{ + ref = 0; +} + +QScriptProgramPrivate::~QScriptProgramPrivate() +{ + if (engine) { + QScript::APIShim shim(engine); + _executable.clear(); + engine->unregisterScriptProgram(this); + } +} + +QScriptProgramPrivate *QScriptProgramPrivate::get(const QScriptProgram &q) +{ + return const_cast<QScriptProgramPrivate*>(q.d_func()); +} + +JSC::EvalExecutable *QScriptProgramPrivate::executable(JSC::ExecState *exec, + QScriptEnginePrivate *eng) +{ + if (_executable) { + if (eng == engine) + return _executable.get(); + // "Migrating" to another engine; clean up old state + QScript::APIShim shim(engine); + _executable.clear(); + engine->unregisterScriptProgram(this); + } + WTF::PassRefPtr<QScript::UStringSourceProviderWithFeedback> provider + = QScript::UStringSourceProviderWithFeedback::create(sourceCode, fileName, firstLineNumber, eng); + sourceId = provider->asID(); + JSC::SourceCode source(provider, firstLineNumber); //after construction of SourceCode provider variable will be null. + _executable = JSC::EvalExecutable::create(exec, source); + engine = eng; + engine->registerScriptProgram(this); + isCompiled = false; + return _executable.get(); +} + +void QScriptProgramPrivate::detachFromEngine() +{ + _executable.clear(); + sourceId = -1; + isCompiled = false; + engine = 0; +} + +/*! + Constructs a null QScriptProgram. +*/ +QScriptProgram::QScriptProgram() + : d_ptr(0) +{ +} + +/*! + Constructs a new QScriptProgram with the given \a sourceCode, \a + fileName and \a firstLineNumber. +*/ +QScriptProgram::QScriptProgram(const QString &sourceCode, + const QString fileName, + int firstLineNumber) + : d_ptr(new QScriptProgramPrivate(sourceCode, fileName, firstLineNumber)) +{ +} + +/*! + Constructs a new QScriptProgram that is a copy of \a other. +*/ +QScriptProgram::QScriptProgram(const QScriptProgram &other) + : d_ptr(other.d_ptr) +{ +} + +/*! + Destroys this QScriptProgram. +*/ +QScriptProgram::~QScriptProgram() +{ +} + +/*! + Assigns the \a other value to this QScriptProgram. +*/ +QScriptProgram &QScriptProgram::operator=(const QScriptProgram &other) +{ + d_ptr = other.d_ptr; + return *this; +} + +/*! + Returns true if this QScriptProgram is null; otherwise + returns false. +*/ +bool QScriptProgram::isNull() const +{ + Q_D(const QScriptProgram); + return (d == 0); +} + +/*! + Returns the source code of this program. +*/ +QString QScriptProgram::sourceCode() const +{ + Q_D(const QScriptProgram); + if (!d) + return QString(); + return d->sourceCode; +} + +/*! + Returns the filename associated with this program. +*/ +QString QScriptProgram::fileName() const +{ + Q_D(const QScriptProgram); + if (!d) + return QString(); + return d->fileName; +} + +/*! + Returns the line number associated with this program. +*/ +int QScriptProgram::firstLineNumber() const +{ + Q_D(const QScriptProgram); + if (!d) + return -1; + return d->firstLineNumber; +} + +/*! + Returns true if this QScriptProgram is equal to \a other; + otherwise returns false. +*/ +bool QScriptProgram::operator==(const QScriptProgram &other) const +{ + Q_D(const QScriptProgram); + if (d == other.d_func()) + return true; + return (sourceCode() == other.sourceCode()) + && (fileName() == other.fileName()) + && (firstLineNumber() == other.firstLineNumber()); +} + +/*! + Returns true if this QScriptProgram is not equal to \a other; + otherwise returns false. +*/ +bool QScriptProgram::operator!=(const QScriptProgram &other) const +{ + return !operator==(other); +} + +QT_END_NAMESPACE diff --git a/src/script/api/qscriptprogram.h b/src/script/api/qscriptprogram.h new file mode 100644 index 0000000..b31c528 --- /dev/null +++ b/src/script/api/qscriptprogram.h @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** 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 QSCRIPTPROGRAM_H +#define QSCRIPTPROGRAM_H + +#include <QtCore/qsharedpointer.h> + +#include <QtCore/qstring.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Script) + +class QScriptProgramPrivate; +class Q_SCRIPT_EXPORT QScriptProgram +{ +public: + QScriptProgram(); + QScriptProgram(const QString &sourceCode, + const QString fileName = QString(), + int firstLineNumber = 1); + QScriptProgram(const QScriptProgram &other); + ~QScriptProgram(); + + QScriptProgram &operator=(const QScriptProgram &other); + + bool isNull() const; + + QString sourceCode() const; + QString fileName() const; + int firstLineNumber() const; + + bool operator==(const QScriptProgram &other) const; + bool operator!=(const QScriptProgram &other) const; + +private: + QExplicitlySharedDataPointer<QScriptProgramPrivate> d_ptr; + Q_DECLARE_PRIVATE(QScriptProgram) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QSCRIPTPROGRAM_H diff --git a/src/script/api/qscriptprogram_p.h b/src/script/api/qscriptprogram_p.h new file mode 100644 index 0000000..e7809ab --- /dev/null +++ b/src/script/api/qscriptprogram_p.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** 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 QSCRIPTPROGRAM_P_H +#define QSCRIPTPROGRAM_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 "RefPtr.h" + +namespace JSC +{ + class EvalExecutable; + class ExecState; +} + +QT_BEGIN_NAMESPACE + +class QScriptEnginePrivate; + +class QScriptProgramPrivate +{ +public: + QScriptProgramPrivate(const QString &sourceCode, + const QString &fileName, + int firstLineNumber); + ~QScriptProgramPrivate(); + + static QScriptProgramPrivate *get(const QScriptProgram &q); + + JSC::EvalExecutable *executable(JSC::ExecState *exec, + QScriptEnginePrivate *engine); + void detachFromEngine(); + + QBasicAtomicInt ref; + + QString sourceCode; + QString fileName; + int firstLineNumber; + + QScriptEnginePrivate *engine; + WTF::RefPtr<JSC::EvalExecutable> _executable; + intptr_t sourceId; + bool isCompiled; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/script/api/qscriptstring.cpp b/src/script/api/qscriptstring.cpp new file mode 100644 index 0000000..2930c9e --- /dev/null +++ b/src/script/api/qscriptstring.cpp @@ -0,0 +1,214 @@ +/**************************************************************************** +** +** 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" // compile on Windows +#include "qscriptstring.h" +#include "qscriptstring_p.h" +#include "qscriptengine.h" +#include "qscriptengine_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \since 4.4 + \class QScriptString + + \brief The QScriptString class acts as a handle to "interned" strings in a QScriptEngine. + + \ingroup script + + + QScriptString can be used to achieve faster (repeated) + property getting/setting, and comparison of property names, of + script objects. + + To get a QScriptString representation of a string, pass the string + to QScriptEngine::toStringHandle(). The typical usage pattern is to + register one or more pre-defined strings when setting up your script + environment, then subsequently use the relevant QScriptString as + argument to e.g. QScriptValue::property(). + + Call the toString() function to obtain the string that a + QScriptString represents. + + Call the toArrayIndex() function to convert a QScriptString to an + array index. This is useful when using QScriptClass to implement + array-like objects. +*/ + +/*! + Constructs an invalid QScriptString. +*/ +QScriptString::QScriptString() + : d_ptr(0) +{ +} + +/*! + Constructs a new QScriptString that is a copy of \a other. +*/ +QScriptString::QScriptString(const QScriptString &other) + : d_ptr(other.d_ptr) +{ + if (d_func() && (d_func()->type == QScriptStringPrivate::StackAllocated)) { + Q_ASSERT(d_func()->ref != 1); + d_ptr.detach(); + d_func()->ref = 1; + d_func()->type = QScriptStringPrivate::HeapAllocated; + d_func()->engine->registerScriptString(d_func()); + } +} + +/*! + Destroys this QScriptString. +*/ +QScriptString::~QScriptString() +{ + Q_D(QScriptString); + if (d) { + switch (d->type) { + case QScriptStringPrivate::StackAllocated: + Q_ASSERT(d->ref == 1); + d->ref.ref(); // avoid deletion + break; + case QScriptStringPrivate::HeapAllocated: + if (d->engine && (d->ref == 1)) { + // Make sure the identifier is removed from the correct engine. + QScript::APIShim shim(d->engine); + d->identifier = JSC::Identifier(); + d->engine->unregisterScriptString(d); + } + break; + } + } +} + +/*! + Assigns the \a other value to this QScriptString. +*/ +QScriptString &QScriptString::operator=(const QScriptString &other) +{ + if (d_func() && d_func()->engine && (d_func()->ref == 1) && (d_func()->type == QScriptStringPrivate::HeapAllocated)) { + // current d_ptr will be deleted at the assignment below, so unregister it first + d_func()->engine->unregisterScriptString(d_func()); + } + d_ptr = other.d_ptr; + if (d_func() && (d_func()->type == QScriptStringPrivate::StackAllocated)) { + Q_ASSERT(d_func()->ref != 1); + d_ptr.detach(); + d_func()->ref = 1; + d_func()->type = QScriptStringPrivate::HeapAllocated; + d_func()->engine->registerScriptString(d_func()); + } + return *this; +} + +/*! + Returns true if this QScriptString is valid; otherwise + returns false. +*/ +bool QScriptString::isValid() const +{ + return QScriptStringPrivate::isValid(*this); +} + +/*! + Returns true if this QScriptString is equal to \a other; + otherwise returns false. +*/ +bool QScriptString::operator==(const QScriptString &other) const +{ + Q_D(const QScriptString); + if (!d || !other.d_func()) + return d == other.d_func(); + return d->identifier == other.d_func()->identifier; +} + +/*! + Returns true if this QScriptString is not equal to \a other; + otherwise returns false. +*/ +bool QScriptString::operator!=(const QScriptString &other) const +{ + return !operator==(other); +} + +/*! + \since 4.6 + + Attempts to convert this QScriptString to a QtScript array index, + and returns the result. + + If a conversion error occurs, *\a{ok} is set to false; otherwise + *\a{ok} is set to true. +*/ +quint32 QScriptString::toArrayIndex(bool *ok) const +{ + Q_D(const QScriptString); + if (!d) { + if (ok) + *ok = false; + return -1; + } + bool tmp; + bool *okok = ok ? ok : &tmp; + quint32 result = d->identifier.toArrayIndex(okok); + if (!*okok) + result = -1; + return result; +} + +/*! + Returns the string that this QScriptString represents, or a + null string if this QScriptString is not valid. + + \sa isValid() +*/ +QString QScriptString::toString() const +{ + Q_D(const QScriptString); + if (!d || !d->engine) + return QString(); + return d->identifier.ustring(); +} + +/*! + Returns the string that this QScriptString represents, or a + null string if this QScriptString is not valid. + + \sa toString() +*/ +QScriptString::operator QString() const +{ + return toString(); +} + +uint qHash(const QScriptString &key) +{ + QScriptStringPrivate *d = QScriptStringPrivate::get(key); + if (!d) + return 0; + return qHash(d->identifier.ustring().rep()); +} + +QT_END_NAMESPACE diff --git a/src/script/api/qscriptstring.h b/src/script/api/qscriptstring.h new file mode 100644 index 0000000..a556fc5 --- /dev/null +++ b/src/script/api/qscriptstring.h @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** 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 QSCRIPTSTRING_H +#define QSCRIPTSTRING_H + +#include <QtCore/qstring.h> + +#include <QtCore/qsharedpointer.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Script) + +class QScriptStringPrivate; +class Q_SCRIPT_EXPORT QScriptString +{ +public: + QScriptString(); + QScriptString(const QScriptString &other); + ~QScriptString(); + + QScriptString &operator=(const QScriptString &other); + + bool isValid() const; + + bool operator==(const QScriptString &other) const; + bool operator!=(const QScriptString &other) const; + + quint32 toArrayIndex(bool *ok = 0) const; + + QString toString() const; + operator QString() const; + +private: + QExplicitlySharedDataPointer<QScriptStringPrivate> d_ptr; + friend class QScriptValue; + Q_DECLARE_PRIVATE(QScriptString) +}; + +Q_SCRIPT_EXPORT uint qHash(const QScriptString &key); + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QSCRIPTSTRING_H diff --git a/src/script/api/qscriptstring_p.h b/src/script/api/qscriptstring_p.h new file mode 100644 index 0000000..b632140 --- /dev/null +++ b/src/script/api/qscriptstring_p.h @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** 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 QSCRIPTSTRING_P_H +#define QSCRIPTSTRING_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 "Identifier.h" + +QT_BEGIN_NAMESPACE + +class QScriptEnginePrivate; +class QScriptStringPrivate +{ +public: + enum AllocationType { + StackAllocated, + HeapAllocated + }; + + inline QScriptStringPrivate(QScriptEnginePrivate *engine, const JSC::Identifier &id, + AllocationType type); + inline ~QScriptStringPrivate(); + static inline void init(QScriptString &q, QScriptStringPrivate *d); + + static inline QScriptStringPrivate *get(const QScriptString &q); + + inline void detachFromEngine(); + + static inline bool isValid(const QScriptString &q); + + QBasicAtomicInt ref; + QScriptEnginePrivate *engine; + JSC::Identifier identifier; + AllocationType type; + + // linked list of engine's script values + QScriptStringPrivate *prev; + QScriptStringPrivate *next; +}; + +inline QScriptStringPrivate::QScriptStringPrivate(QScriptEnginePrivate *e, const JSC::Identifier &id, + AllocationType tp) + : engine(e), identifier(id), type(tp), prev(0), next(0) +{ + ref = 0; +} + +inline QScriptStringPrivate::~QScriptStringPrivate() +{ +} + +inline void QScriptStringPrivate::init(QScriptString &q, QScriptStringPrivate *d) +{ + q.d_ptr = d; +} + +inline QScriptStringPrivate *QScriptStringPrivate::get(const QScriptString &q) +{ + return const_cast<QScriptStringPrivate*>(q.d_func()); +} + +inline void QScriptStringPrivate::detachFromEngine() +{ + engine = 0; + identifier = JSC::Identifier(); +} + +inline bool QScriptStringPrivate::isValid(const QScriptString &q) +{ + return (q.d_ptr && q.d_ptr->engine); +} + +QT_END_NAMESPACE + +#endif diff --git a/src/script/api/qscriptvalue.cpp b/src/script/api/qscriptvalue.cpp new file mode 100644 index 0000000..91ce9c8 --- /dev/null +++ b/src/script/api/qscriptvalue.cpp @@ -0,0 +1,2136 @@ +/**************************************************************************** +** +** 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 "qscriptvalue.h" + +#include "qscriptvalue_p.h" +#include "qscriptengine.h" +#include "qscriptengine_p.h" +#include "qscriptstring_p.h" + +#include "JSGlobalObject.h" +#include "JSImmediate.h" +#include "JSObject.h" +#include "JSValue.h" +#include "JSFunction.h" +#include "Identifier.h" +#include "Operations.h" +#include "Arguments.h" + +#include <QtCore/qvariant.h> +#include <QtCore/qvarlengtharray.h> +#include <QtCore/qnumeric.h> + +/*! + \since 4.3 + \class QScriptValue + + \brief The QScriptValue class acts as a container for the Qt Script data types. + + \ingroup script + \mainclass + + QScriptValue supports the types defined in the \l{ECMA-262} + standard: The primitive types, which are Undefined, Null, Boolean, + Number, and String; and the Object type. Additionally, Qt Script + has built-in support for QVariant, QObject and QMetaObject. + + For the object-based types (including Date and RegExp), use the + newT() functions in QScriptEngine (e.g. QScriptEngine::newObject()) + to create a QScriptValue of the desired type. For the primitive types, + use one of the QScriptValue constructor overloads. + + The methods named isT() (e.g. isBool(), isUndefined()) can be + used to test if a value is of a certain type. The methods named + toT() (e.g. toBool(), toString()) can be used to convert a + QScriptValue to another type. You can also use the generic + qscriptvalue_cast() function. + + Object values have zero or more properties which are themselves + QScriptValues. Use setProperty() to set a property of an object, and + call property() to retrieve the value of a property. + + \snippet doc/src/snippets/code/src_script_qscriptvalue.cpp 0 + + Each property can have a set of attributes; these are specified as + the third (optional) argument to setProperty(). The attributes of a + property can be queried by calling the propertyFlags() function. The + following code snippet creates a property that cannot be modified by + script code: + + \snippet doc/src/snippets/code/src_script_qscriptvalue.cpp 1 + + If you want to iterate over the properties of a script object, use + the QScriptValueIterator class. + + Object values have an internal \c{prototype} property, which can be + accessed with prototype() and setPrototype(). Properties added to a + prototype are shared by all objects having that prototype; this is + referred to as prototype-based inheritance. In practice, it means + that (by default) the property() function will automatically attempt + to look up look the property in the prototype() (and in the + prototype of the prototype(), and so on), if the object itself does + not have the requested property. Note that this prototype-based + lookup is not performed by setProperty(); setProperty() will always + create the property in the script object itself. For more + information, see the \l{QtScript} documentation. + + Function objects (objects for which isFunction() returns true) can + be invoked by calling call(). Constructor functions can be used to + construct new objects by calling construct(). + + Use equals(), strictlyEquals() and lessThan() to compare a QScriptValue + to another. + + Object values can have custom data associated with them; see the + setData() and data() functions. By default, this data is not + accessible to scripts; it can be used to store any data you want to + associate with the script object. Typically this is used by custom + class objects (see QScriptClass) to store a C++ type that contains + the "native" object data. + + Note that a QScriptValue for which isObject() is true only carries a + reference to an actual object; copying the QScriptValue will only + copy the object reference, not the object itself. If you want to + clone an object (i.e. copy an object's properties to another + object), you can do so with the help of a \c{for-in} statement in + script code, or QScriptValueIterator in C++. + + \sa QScriptEngine, QScriptValueIterator +*/ + +/*! + \enum QScriptValue::SpecialValue + + This enum is used to specify a single-valued type. + + \value UndefinedValue An undefined value. + + \value NullValue A null value. +*/ + +/*! + \enum QScriptValue::PropertyFlag + + This enum describes the attributes of a property. + + \value ReadOnly The property is read-only. Attempts by Qt Script code to write to the property will be ignored. + + \value Undeletable Attempts by Qt Script code to \c{delete} the property will be ignored. + + \value SkipInEnumeration The property is not to be enumerated by a \c{for-in} enumeration. + + \value PropertyGetter The property is defined by a function which will be called to get the property value. + + \value PropertySetter The property is defined by a function which will be called to set the property value. + + \omitvalue QObjectMember This flag is used to indicate that an existing property is a QObject member (a property or method). + + \value KeepExistingFlags This value is used to indicate to setProperty() that the property's flags should be left unchanged. If the property doesn't exist, the default flags (0) will be used. + + \omitvalue UserRange Flags in this range are not used by Qt Script, and can be used for custom purposes. +*/ + +/*! + \enum QScriptValue::ResolveFlag + + This enum specifies how to look up a property of an object. + + \value ResolveLocal Only check the object's own properties. + + \value ResolvePrototype Check the object's own properties first, then search the prototype chain. This is the default. + + \omitvalue ResolveScope Check the object's own properties first, then search the scope chain. + + \omitvalue ResolveFull Check the object's own properties first, then search the prototype chain, and finally search the scope chain. +*/ + +QT_BEGIN_NAMESPACE + +void QScriptValuePrivate::detachFromEngine() +{ + if (isJSC()) + jscValue = JSC::JSValue(); + engine = 0; +} + +/*! + \internal +*/ +QScriptValue::QScriptValue(QScriptValuePrivate *d) + : d_ptr(d) +{ +} + +/*! + Constructs an invalid QScriptValue. +*/ +QScriptValue::QScriptValue() + : d_ptr(0) +{ +} + +/*! + Destroys this QScriptValue. +*/ +QScriptValue::~QScriptValue() +{ +} + +/*! + Constructs a new QScriptValue that is a copy of \a other. + + Note that if \a other is an object (i.e., isObject() would return + true), then only a reference to the underlying object is copied into + the new script value (i.e., the object itself is not copied). +*/ +QScriptValue::QScriptValue(const QScriptValue &other) + : d_ptr(other.d_ptr) +{ +} + +/*! + \obsolete + + Constructs a new QScriptValue with the special \a value and + registers it with the script \a engine. +*/ +QScriptValue::QScriptValue(QScriptEngine *engine, QScriptValue::SpecialValue value) + : d_ptr(new (QScriptEnginePrivate::get(engine))QScriptValuePrivate(QScriptEnginePrivate::get(engine))) +{ + switch (value) { + case NullValue: + d_ptr->initFrom(JSC::jsNull()); + break; + case UndefinedValue: + d_ptr->initFrom(JSC::jsUndefined()); + break; + } +} + +/*! + \obsolete + + \fn QScriptValue::QScriptValue(QScriptEngine *engine, bool value) + + Constructs a new QScriptValue with the boolean \a value and + registers it with the script \a engine. +*/ +QScriptValue::QScriptValue(QScriptEngine *engine, bool val) + : d_ptr(new (QScriptEnginePrivate::get(engine))QScriptValuePrivate(QScriptEnginePrivate::get(engine))) +{ + d_ptr->initFrom(JSC::jsBoolean(val)); +} + +/*! + \fn QScriptValue::QScriptValue(QScriptEngine *engine, int value) + \obsolete + + Constructs a new QScriptValue with the integer \a value and + registers it with the script \a engine. +*/ +QScriptValue::QScriptValue(QScriptEngine *engine, int val) + : d_ptr(new (QScriptEnginePrivate::get(engine))QScriptValuePrivate(QScriptEnginePrivate::get(engine))) +{ + if (engine) { + QScript::APIShim shim(d_ptr->engine); + JSC::ExecState *exec = d_ptr->engine->currentFrame; + d_ptr->initFrom(JSC::jsNumber(exec, val)); + } else + d_ptr->initFrom(val); +} + +/*! + \fn QScriptValue::QScriptValue(QScriptEngine *engine, uint value) + \obsolete + + Constructs a new QScriptValue with the unsigned integer \a value and + registers it with the script \a engine. + */ +QScriptValue::QScriptValue(QScriptEngine *engine, uint val) + : d_ptr(new (QScriptEnginePrivate::get(engine))QScriptValuePrivate(QScriptEnginePrivate::get(engine))) +{ + if (engine) { + QScript::APIShim shim(d_ptr->engine); + JSC::ExecState *exec = d_ptr->engine->currentFrame; + d_ptr->initFrom(JSC::jsNumber(exec, val)); + } else + d_ptr->initFrom(val); +} + +/*! + \fn QScriptValue::QScriptValue(QScriptEngine *engine, qsreal value) + \obsolete + + Constructs a new QScriptValue with the qsreal \a value and + registers it with the script \a engine. +*/ +QScriptValue::QScriptValue(QScriptEngine *engine, qsreal val) + : d_ptr(new (QScriptEnginePrivate::get(engine))QScriptValuePrivate(QScriptEnginePrivate::get(engine))) +{ + if (engine) { + QScript::APIShim shim(d_ptr->engine); + JSC::ExecState *exec = d_ptr->engine->currentFrame; + d_ptr->initFrom(JSC::jsNumber(exec, val)); + } else + d_ptr->initFrom(val); +} + +/*! + \fn QScriptValue::QScriptValue(QScriptEngine *engine, const QString &value) + \obsolete + + Constructs a new QScriptValue with the string \a value and + registers it with the script \a engine. +*/ +QScriptValue::QScriptValue(QScriptEngine *engine, const QString &val) + : d_ptr(new (QScriptEnginePrivate::get(engine))QScriptValuePrivate(QScriptEnginePrivate::get(engine))) +{ + if (engine) { + QScript::APIShim shim(d_ptr->engine); + JSC::ExecState *exec = d_ptr->engine->currentFrame; + d_ptr->initFrom(JSC::jsString(exec, val)); + } else { + d_ptr->initFrom(val); + } +} + +/*! + \fn QScriptValue::QScriptValue(QScriptEngine *engine, const char *value) + \obsolete + + Constructs a new QScriptValue with the string \a value and + registers it with the script \a engine. +*/ + +#ifndef QT_NO_CAST_FROM_ASCII +QScriptValue::QScriptValue(QScriptEngine *engine, const char *val) + : d_ptr(new (QScriptEnginePrivate::get(engine))QScriptValuePrivate(QScriptEnginePrivate::get(engine))) +{ + if (engine) { + QScript::APIShim shim(d_ptr->engine); + JSC::ExecState *exec = d_ptr->engine->currentFrame; + d_ptr->initFrom(JSC::jsString(exec, val)); + } else { + d_ptr->initFrom(QString::fromAscii(val)); + } +} +#endif + +/*! + \since 4.5 + + Constructs a new QScriptValue with a special \a value. +*/ +QScriptValue::QScriptValue(SpecialValue value) + : d_ptr(new (/*engine=*/0)QScriptValuePrivate(/*engine=*/0)) +{ + switch (value) { + case NullValue: + d_ptr->initFrom(JSC::jsNull()); + break; + case UndefinedValue: + d_ptr->initFrom(JSC::jsUndefined()); + break; + } +} + +/*! + \since 4.5 + + Constructs a new QScriptValue with a boolean \a value. +*/ +QScriptValue::QScriptValue(bool value) + : d_ptr(new (/*engine=*/0)QScriptValuePrivate(/*engine=*/0)) +{ + d_ptr->initFrom(JSC::jsBoolean(value)); +} + +/*! + \since 4.5 + + Constructs a new QScriptValue with a number \a value. +*/ +QScriptValue::QScriptValue(int value) + : d_ptr(new (/*engine=*/0)QScriptValuePrivate(/*engine=*/0)) +{ + d_ptr->initFrom(value); +} + +/*! + \since 4.5 + + Constructs a new QScriptValue with a number \a value. +*/ +QScriptValue::QScriptValue(uint value) + : d_ptr(new (/*engine=*/0)QScriptValuePrivate(/*engine=*/0)) +{ + d_ptr->initFrom(value); +} + +/*! + \since 4.5 + + Constructs a new QScriptValue with a number \a value. +*/ +QScriptValue::QScriptValue(qsreal value) + : d_ptr(new (/*engine=*/0)QScriptValuePrivate(/*engine=*/0)) +{ + d_ptr->initFrom(value); +} + +/*! + \since 4.5 + + Constructs a new QScriptValue with a string \a value. +*/ +QScriptValue::QScriptValue(const QString &value) + : d_ptr(new (/*engine=*/0)QScriptValuePrivate(/*engine=*/0)) +{ + d_ptr->initFrom(value); +} + +/*! + \since 4.5 + + Constructs a new QScriptValue with a string \a value. +*/ +QScriptValue::QScriptValue(const QLatin1String &value) + : d_ptr(new (/*engine=*/0)QScriptValuePrivate(/*engine=*/0)) +{ + d_ptr->initFrom(value); +} + +/*! + \since 4.5 + + Constructs a new QScriptValue with a string \a value. +*/ + +#ifndef QT_NO_CAST_FROM_ASCII +QScriptValue::QScriptValue(const char *value) + : d_ptr(new (/*engine=*/0)QScriptValuePrivate(/*engine=*/0)) +{ + d_ptr->initFrom(QString::fromAscii(value)); +} +#endif + +/*! + Assigns the \a other value to this QScriptValue. + + Note that if \a other is an object (isObject() returns true), + only a reference to the underlying object will be assigned; + the object itself will not be copied. +*/ +QScriptValue &QScriptValue::operator=(const QScriptValue &other) +{ + d_ptr = other.d_ptr; + return *this; +} + +/*! + Returns true if this QScriptValue is an object of the Error class; + otherwise returns false. + + \sa QScriptContext::throwError() +*/ +bool QScriptValue::isError() const +{ + Q_D(const QScriptValue); + if (!d || !d->isJSC()) + return false; + return QScriptEnginePrivate::isError(d->jscValue); +} + +/*! + Returns true if this QScriptValue is an object of the Array class; + otherwise returns false. + + \sa QScriptEngine::newArray() +*/ +bool QScriptValue::isArray() const +{ + Q_D(const QScriptValue); + if (!d || !d->isJSC()) + return false; + return QScriptEnginePrivate::isArray(d->jscValue); +} + +/*! + Returns true if this QScriptValue is an object of the Date class; + otherwise returns false. + + \sa QScriptEngine::newDate() +*/ +bool QScriptValue::isDate() const +{ + Q_D(const QScriptValue); + if (!d || !d->isJSC()) + return false; + return QScriptEnginePrivate::isDate(d->jscValue); +} + +/*! + Returns true if this QScriptValue is an object of the RegExp class; + otherwise returns false. + + \sa QScriptEngine::newRegExp() +*/ +bool QScriptValue::isRegExp() const +{ + Q_D(const QScriptValue); + if (!d || !d->isJSC()) + return false; + return QScriptEnginePrivate::isRegExp(d->jscValue); +} + +/*! + If this QScriptValue is an object, returns the internal prototype + (\c{__proto__} property) of this object; otherwise returns an + invalid QScriptValue. + + \sa setPrototype(), isObject() +*/ +QScriptValue QScriptValue::prototype() const +{ + Q_D(const QScriptValue); + if (!d || !d->isObject()) + return QScriptValue(); + return d->engine->scriptValueFromJSCValue(JSC::asObject(d->jscValue)->prototype()); +} + +/*! + If this QScriptValue is an object, sets the internal prototype + (\c{__proto__} property) of this object to be \a prototype; + otherwise does nothing. + + The internal prototype should not be confused with the public + property with name "prototype"; the public prototype is usually + only set on functions that act as constructors. + + \sa prototype(), isObject() +*/ +void QScriptValue::setPrototype(const QScriptValue &prototype) +{ + Q_D(QScriptValue); + if (!d || !d->isObject()) + return; + + JSC::JSValue other = d->engine->scriptValueToJSCValue(prototype); + if (!other || !(other.isObject() || other.isNull())) + return; + + if (QScriptValuePrivate::getEngine(prototype) + && (QScriptValuePrivate::getEngine(prototype) != d->engine)) { + qWarning("QScriptValue::setPrototype() failed: " + "cannot set a prototype created in " + "a different engine"); + return; + } + JSC::JSObject *thisObject = JSC::asObject(d->jscValue); + + // check for cycle + JSC::JSValue nextPrototypeValue = other; + while (nextPrototypeValue && nextPrototypeValue.isObject()) { + JSC::JSObject *nextPrototype = JSC::asObject(nextPrototypeValue); + if (nextPrototype == thisObject) { + qWarning("QScriptValue::setPrototype() failed: cyclic prototype value"); + return; + } + nextPrototypeValue = nextPrototype->prototype(); + } + + thisObject->setPrototype(other); + + // Sync the internal Global Object prototype if appropriate. + if (((thisObject == d->engine->originalGlobalObjectProxy) + && !d->engine->customGlobalObject()) + || (thisObject == d->engine->customGlobalObject())) { + d->engine->originalGlobalObject()->setPrototype(other); + } +} + +/*! + \internal +*/ +QScriptValue QScriptValue::scope() const +{ + Q_D(const QScriptValue); + if (!d || !d->isObject()) + return QScriptValue(); + QScript::APIShim shim(d->engine); + // ### make hidden property + JSC::JSValue result = d->property("__qt_scope__", QScriptValue::ResolveLocal); + return d->engine->scriptValueFromJSCValue(result); +} + +/*! + \internal +*/ +void QScriptValue::setScope(const QScriptValue &scope) +{ + Q_D(QScriptValue); + if (!d || !d->isObject()) + return; + if (scope.isValid() && QScriptValuePrivate::getEngine(scope) + && (QScriptValuePrivate::getEngine(scope) != d->engine)) { + qWarning("QScriptValue::setScope() failed: " + "cannot set a scope object created in " + "a different engine"); + return; + } + JSC::JSValue other = d->engine->scriptValueToJSCValue(scope); + JSC::ExecState *exec = d->engine->currentFrame; + JSC::Identifier id = JSC::Identifier(exec, "__qt_scope__"); + if (!scope.isValid()) { + JSC::asObject(d->jscValue)->removeDirect(id); + } else { + // ### make hidden property + JSC::asObject(d->jscValue)->putDirect(id, other); + } +} + +/*! + Returns true if this QScriptValue is an instance of + \a other; otherwise returns false. + + This QScriptValue is considered to be an instance of \a other if + \a other is a function and the value of the \c{prototype} + property of \a other is in the prototype chain of this + QScriptValue. +*/ +bool QScriptValue::instanceOf(const QScriptValue &other) const +{ + Q_D(const QScriptValue); + if (!d || !d->isObject() || !other.isObject()) + return false; + if (QScriptValuePrivate::getEngine(other) != d->engine) { + qWarning("QScriptValue::instanceof: " + "cannot perform operation on a value created in " + "a different engine"); + return false; + } + JSC::JSValue jscProto = d->engine->scriptValueToJSCValue(other.property(QLatin1String("prototype"))); + if (!jscProto) + jscProto = JSC::jsUndefined(); + JSC::ExecState *exec = d->engine->currentFrame; + JSC::JSValue jscOther = d->engine->scriptValueToJSCValue(other); + return JSC::asObject(jscOther)->hasInstance(exec, d->jscValue, jscProto); +} + +// ### move + +namespace QScript +{ + +enum Type { + Undefined, + Null, + Boolean, + String, + Number, + Object +}; + +static Type type(const QScriptValue &v) +{ + if (v.isUndefined()) + return Undefined; + else if (v.isNull()) + return Null; + else if (v.isBoolean()) + return Boolean; + else if (v.isString()) + return String; + else if (v.isNumber()) + return Number; + Q_ASSERT(v.isObject()); + return Object; +} + +static QScriptValue ToPrimitive(const QScriptValue &object, JSC::PreferredPrimitiveType hint = JSC::NoPreference) +{ + Q_ASSERT(object.isObject()); + QScriptValuePrivate *pp = QScriptValuePrivate::get(object); + Q_ASSERT(pp->engine != 0); + QScript::APIShim shim(pp->engine); + JSC::ExecState *exec = pp->engine->currentFrame; + JSC::JSValue savedException; + QScriptEnginePrivate::saveException(exec, &savedException); + JSC::JSValue result = JSC::asObject(pp->jscValue)->toPrimitive(exec, hint); + QScriptEnginePrivate::restoreException(exec, savedException); + return pp->engine->scriptValueFromJSCValue(result); +} + +static bool IsNumerical(const QScriptValue &value) +{ + return value.isNumber() || value.isBool(); +} + +static bool LessThan(QScriptValue lhs, QScriptValue rhs) +{ + if (type(lhs) == type(rhs)) { + switch (type(lhs)) { + case Undefined: + case Null: + return false; + + case Number: + return lhs.toNumber() < rhs.toNumber(); + + case Boolean: + return lhs.toBool() < rhs.toBool(); + + case String: + return lhs.toString() < rhs.toString(); + + case Object: + break; + } // switch + } + + if (lhs.isObject()) + lhs = ToPrimitive(lhs, JSC::PreferNumber); + + if (rhs.isObject()) + rhs = ToPrimitive(rhs, JSC::PreferNumber); + + if (lhs.isString() && rhs.isString()) + return lhs.toString() < rhs.toString(); + + return lhs.toNumber() < rhs.toNumber(); +} + +static bool Equals(QScriptValue lhs, QScriptValue rhs) +{ + if (type(lhs) == type(rhs)) { + switch (type(lhs)) { + case QScript::Undefined: + case QScript::Null: + return true; + + case QScript::Number: + return lhs.toNumber() == rhs.toNumber(); + + case QScript::Boolean: + return lhs.toBool() == rhs.toBool(); + + case QScript::String: + return lhs.toString() == rhs.toString(); + + case QScript::Object: + if (lhs.isVariant()) + return lhs.strictlyEquals(rhs) || (lhs.toVariant() == rhs.toVariant()); +#ifndef QT_NO_QOBJECT + else if (lhs.isQObject()) + return (lhs.strictlyEquals(rhs)) || (lhs.toQObject() == rhs.toQObject()); +#endif + else + return lhs.strictlyEquals(rhs); + } + } + + if (lhs.isNull() && rhs.isUndefined()) + return true; + + else if (lhs.isUndefined() && rhs.isNull()) + return true; + + else if (IsNumerical(lhs) && rhs.isString()) + return lhs.toNumber() == rhs.toNumber(); + + else if (lhs.isString() && IsNumerical(rhs)) + return lhs.toNumber() == rhs.toNumber(); + + else if (lhs.isBool()) + return Equals(lhs.toNumber(), rhs); + + else if (rhs.isBool()) + return Equals(lhs, rhs.toNumber()); + + else if (lhs.isObject() && !rhs.isNull()) { + lhs = ToPrimitive(lhs); + + if (lhs.isValid() && !lhs.isObject()) + return Equals(lhs, rhs); + } + + else if (rhs.isObject() && ! lhs.isNull()) { + rhs = ToPrimitive(rhs); + if (rhs.isValid() && !rhs.isObject()) + return Equals(lhs, rhs); + } + + return false; +} + +} // namespace QScript + +/*! + Returns true if this QScriptValue is less than \a other, otherwise + returns false. The comparison follows the behavior described in + \l{ECMA-262} section 11.8.5, "The Abstract Relational Comparison + Algorithm". + + Note that if this QScriptValue or the \a other value are objects, + calling this function has side effects on the script engine, since + the engine will call the object's valueOf() function (and possibly + toString()) in an attempt to convert the object to a primitive value + (possibly resulting in an uncaught script exception). + + \sa equals() +*/ +bool QScriptValue::lessThan(const QScriptValue &other) const +{ + Q_D(const QScriptValue); + // no equivalent function in JSC? There's a jsLess() in VM/Machine.cpp + if (!isValid() || !other.isValid()) + return false; + if (QScriptValuePrivate::getEngine(other) && d->engine + && (QScriptValuePrivate::getEngine(other) != d->engine)) { + qWarning("QScriptValue::lessThan: " + "cannot compare to a value created in " + "a different engine"); + return false; + } + return QScript::LessThan(*this, other); +} + +/*! + Returns true if this QScriptValue is equal to \a other, otherwise + returns false. The comparison follows the behavior described in + \l{ECMA-262} section 11.9.3, "The Abstract Equality Comparison + Algorithm". + + This function can return true even if the type of this QScriptValue + is different from the type of the \a other value; i.e. the + comparison is not strict. For example, comparing the number 9 to + the string "9" returns true; comparing an undefined value to a null + value returns true; comparing a \c{Number} object whose primitive + value is 6 to a \c{String} object whose primitive value is "6" + returns true; and comparing the number 1 to the boolean value + \c{true} returns true. If you want to perform a comparison + without such implicit value conversion, use strictlyEquals(). + + Note that if this QScriptValue or the \a other value are objects, + calling this function has side effects on the script engine, since + the engine will call the object's valueOf() function (and possibly + toString()) in an attempt to convert the object to a primitive value + (possibly resulting in an uncaught script exception). + + \sa strictlyEquals(), lessThan() +*/ +bool QScriptValue::equals(const QScriptValue &other) const +{ + Q_D(const QScriptValue); + if (!d || !other.d_ptr) + return (d_ptr == other.d_ptr); + if (QScriptValuePrivate::getEngine(other) && d->engine + && (QScriptValuePrivate::getEngine(other) != d->engine)) { + qWarning("QScriptValue::equals: " + "cannot compare to a value created in " + "a different engine"); + return false; + } + if (d->isJSC() && other.d_ptr->isJSC()) { + QScriptEnginePrivate *eng_p = d->engine; + if (!eng_p) + eng_p = other.d_ptr->engine; + if (eng_p) { + QScript::APIShim shim(eng_p); + JSC::ExecState *exec = eng_p->currentFrame; + JSC::JSValue savedException; + QScriptEnginePrivate::saveException(exec, &savedException); + bool result = JSC::JSValue::equal(exec, d->jscValue, other.d_ptr->jscValue); + QScriptEnginePrivate::restoreException(exec, savedException); + return result; + } + } + return QScript::Equals(*this, other); +} + +/*! + Returns true if this QScriptValue is equal to \a other using strict + comparison (no conversion), otherwise returns false. The comparison + follows the behavior described in \l{ECMA-262} section 11.9.6, "The + Strict Equality Comparison Algorithm". + + If the type of this QScriptValue is different from the type of the + \a other value, this function returns false. If the types are equal, + the result depends on the type, as shown in the following table: + + \table + \header \o Type \o Result + \row \o Undefined \o true + \row \o Null \o true + \row \o Boolean \o true if both values are true, false otherwise + \row \o Number \o false if either value is NaN (Not-a-Number); true if values are equal, false otherwise + \row \o String \o true if both values are exactly the same sequence of characters, false otherwise + \row \o Object \o true if both values refer to the same object, false otherwise + \endtable + + \sa equals() +*/ +bool QScriptValue::strictlyEquals(const QScriptValue &other) const +{ + Q_D(const QScriptValue); + if (!d || !other.d_ptr) + return (d_ptr == other.d_ptr); + if (QScriptValuePrivate::getEngine(other) && d->engine + && (QScriptValuePrivate::getEngine(other) != d->engine)) { + qWarning("QScriptValue::strictlyEquals: " + "cannot compare to a value created in " + "a different engine"); + return false; + } + + if (d->type != other.d_ptr->type) { + if (d->type == QScriptValuePrivate::JavaScriptCore) { + QScriptEnginePrivate *eng_p = d->engine ? d->engine : other.d_ptr->engine; + if (eng_p) + return JSC::JSValue::strictEqual(eng_p->currentFrame, d->jscValue, eng_p->scriptValueToJSCValue(other)); + } else if (other.d_ptr->type == QScriptValuePrivate::JavaScriptCore) { + QScriptEnginePrivate *eng_p = other.d_ptr->engine ? other.d_ptr->engine : d->engine; + if (eng_p) + return JSC::JSValue::strictEqual(eng_p->currentFrame, eng_p->scriptValueToJSCValue(*this), other.d_ptr->jscValue); + } + + return false; + } + switch (d->type) { + case QScriptValuePrivate::JavaScriptCore: { + QScriptEnginePrivate *eng_p = d->engine ? d->engine : other.d_ptr->engine; + JSC::ExecState *exec = eng_p ? eng_p->currentFrame : 0; + return JSC::JSValue::strictEqual(exec, d->jscValue, other.d_ptr->jscValue); + } + case QScriptValuePrivate::Number: + return (d->numberValue == other.d_ptr->numberValue); + case QScriptValuePrivate::String: + return (d->stringValue == other.d_ptr->stringValue); + } + return false; +} + +/*! + Returns the string value of this QScriptValue, as defined in + \l{ECMA-262} section 9.8, "ToString". + + Note that if this QScriptValue is an object, calling this function + has side effects on the script engine, since the engine will call + the object's toString() function (and possibly valueOf()) in an + attempt to convert the object to a primitive value (possibly + resulting in an uncaught script exception). + + \sa isString() +*/ +QString QScriptValue::toString() const +{ + Q_D(const QScriptValue); + if (!d) + return QString(); + switch (d->type) { + case QScriptValuePrivate::JavaScriptCore: { + if (d->engine) { + QScript::APIShim shim(d->engine); + return QScriptEnginePrivate::toString(d->engine->currentFrame, d->jscValue); + } else { + return QScriptEnginePrivate::toString(0, d->jscValue); + } } + case QScriptValuePrivate::Number: + return QScript::ToString(d->numberValue); + case QScriptValuePrivate::String: + return d->stringValue; + } + return QString(); +} + +/*! + Returns the number value of this QScriptValue, as defined in + \l{ECMA-262} section 9.3, "ToNumber". + + Note that if this QScriptValue is an object, calling this function + has side effects on the script engine, since the engine will call + the object's valueOf() function (and possibly toString()) in an + attempt to convert the object to a primitive value (possibly + resulting in an uncaught script exception). + + \sa isNumber(), toInteger(), toInt32(), toUInt32(), toUInt16() +*/ +qsreal QScriptValue::toNumber() const +{ + Q_D(const QScriptValue); + if (!d) + return 0; + switch (d->type) { + case QScriptValuePrivate::JavaScriptCore: { + if (d->engine) { + QScript::APIShim shim(d->engine); + return QScriptEnginePrivate::toNumber(d->engine->currentFrame, d->jscValue); + } else { + return QScriptEnginePrivate::toNumber(0, d->jscValue); + } + } + case QScriptValuePrivate::Number: + return d->numberValue; + case QScriptValuePrivate::String: + return QScript::ToNumber(d->stringValue); + } + return 0; +} + +/*! + \obsolete + + Use toBool() instead. +*/ +bool QScriptValue::toBoolean() const +{ + Q_D(const QScriptValue); + if (!d) + return false; + switch (d->type) { + case QScriptValuePrivate::JavaScriptCore: { + if (d->engine) { + QScript::APIShim shim(d->engine); + return QScriptEnginePrivate::toBool(d->engine->currentFrame, d->jscValue); + } else { + return QScriptEnginePrivate::toBool(0, d->jscValue); + } + } + case QScriptValuePrivate::Number: + return QScript::ToBool(d->numberValue); + case QScriptValuePrivate::String: + return QScript::ToBool(d->stringValue); + } + return false; +} + +/*! + \since 4.5 + + Returns the boolean value of this QScriptValue, using the conversion + rules described in \l{ECMA-262} section 9.2, "ToBoolean". + + Note that if this QScriptValue is an object, calling this function + has side effects on the script engine, since the engine will call + the object's valueOf() function (and possibly toString()) in an + attempt to convert the object to a primitive value (possibly + resulting in an uncaught script exception). + + \sa isBool() +*/ +bool QScriptValue::toBool() const +{ + Q_D(const QScriptValue); + if (!d) + return false; + switch (d->type) { + case QScriptValuePrivate::JavaScriptCore: { + if (d->engine) { + QScript::APIShim shim(d->engine); + return QScriptEnginePrivate::toBool(d->engine->currentFrame, d->jscValue); + } else { + return QScriptEnginePrivate::toBool(0, d->jscValue); + } + } + case QScriptValuePrivate::Number: + return QScript::ToBool(d->numberValue); + case QScriptValuePrivate::String: + return QScript::ToBool(d->stringValue); + } + return false; +} + +/*! + Returns the signed 32-bit integer value of this QScriptValue, using + the conversion rules described in \l{ECMA-262} section 9.5, "ToInt32". + + Note that if this QScriptValue is an object, calling this function + has side effects on the script engine, since the engine will call + the object's valueOf() function (and possibly toString()) in an + attempt to convert the object to a primitive value (possibly + resulting in an uncaught script exception). + + \sa toNumber(), toUInt32() +*/ +qint32 QScriptValue::toInt32() const +{ + Q_D(const QScriptValue); + if (!d) + return 0; + switch (d->type) { + case QScriptValuePrivate::JavaScriptCore: { + if (d->engine) { + QScript::APIShim shim(d->engine); + return QScriptEnginePrivate::toInt32(d->engine->currentFrame, d->jscValue); + } else { + return QScriptEnginePrivate::toInt32(0, d->jscValue); + } + } + case QScriptValuePrivate::Number: + return QScript::ToInt32(d->numberValue); + case QScriptValuePrivate::String: + return QScript::ToInt32(d->stringValue); + } + return 0; +} + +/*! + Returns the unsigned 32-bit integer value of this QScriptValue, using + the conversion rules described in \l{ECMA-262} section 9.6, "ToUint32". + + Note that if this QScriptValue is an object, calling this function + has side effects on the script engine, since the engine will call + the object's valueOf() function (and possibly toString()) in an + attempt to convert the object to a primitive value (possibly + resulting in an uncaught script exception). + + \sa toNumber(), toInt32() +*/ +quint32 QScriptValue::toUInt32() const +{ + Q_D(const QScriptValue); + if (!d) + return 0; + switch (d->type) { + case QScriptValuePrivate::JavaScriptCore: { + if (d->engine) { + QScript::APIShim shim(d->engine); + return QScriptEnginePrivate::toUInt32(d->engine->currentFrame, d->jscValue); + } else { + return QScriptEnginePrivate::toUInt32(0, d->jscValue); + } + } + case QScriptValuePrivate::Number: + return QScript::ToUInt32(d->numberValue); + case QScriptValuePrivate::String: + return QScript::ToUInt32(d->stringValue); + } + return 0; +} + +/*! + Returns the unsigned 16-bit integer value of this QScriptValue, using + the conversion rules described in \l{ECMA-262} section 9.7, "ToUint16". + + Note that if this QScriptValue is an object, calling this function + has side effects on the script engine, since the engine will call + the object's valueOf() function (and possibly toString()) in an + attempt to convert the object to a primitive value (possibly + resulting in an uncaught script exception). + + \sa toNumber() +*/ +quint16 QScriptValue::toUInt16() const +{ + Q_D(const QScriptValue); + if (!d) + return 0; + switch (d->type) { + case QScriptValuePrivate::JavaScriptCore: { + if (d->engine) { + QScript::APIShim shim(d->engine); + return QScriptEnginePrivate::toUInt16(d->engine->currentFrame, d->jscValue); + } else { + return QScriptEnginePrivate::toUInt16(0, d->jscValue); + } + } + case QScriptValuePrivate::Number: + return QScript::ToUInt16(d->numberValue); + case QScriptValuePrivate::String: + return QScript::ToUInt16(d->stringValue); + } + return 0; +} + +/*! + Returns the integer value of this QScriptValue, using the conversion + rules described in \l{ECMA-262} section 9.4, "ToInteger". + + Note that if this QScriptValue is an object, calling this function + has side effects on the script engine, since the engine will call + the object's valueOf() function (and possibly toString()) in an + attempt to convert the object to a primitive value (possibly + resulting in an uncaught script exception). + + \sa toNumber() +*/ +qsreal QScriptValue::toInteger() const +{ + Q_D(const QScriptValue); + if (!d) + return 0; + switch (d->type) { + case QScriptValuePrivate::JavaScriptCore: { + if (d->engine) { + QScript::APIShim shim(d->engine); + return QScriptEnginePrivate::toInteger(d->engine->currentFrame, d->jscValue); + } else { + return QScriptEnginePrivate::toInteger(0, d->jscValue); + } + } + case QScriptValuePrivate::Number: + return QScript::ToInteger(d->numberValue); + case QScriptValuePrivate::String: + return QScript::ToInteger(d->stringValue); + } + return 0; +} + +/*! + Returns the QVariant value of this QScriptValue, if it can be + converted to a QVariant; otherwise returns an invalid QVariant. + The conversion is performed according to the following table: + + \table + \header \o Input Type \o Result + \row \o Undefined \o An invalid QVariant. + \row \o Null \o An invalid QVariant. + \row \o Boolean \o A QVariant containing the value of the boolean. + \row \o Number \o A QVariant containing the value of the number. + \row \o String \o A QVariant containing the value of the string. + \row \o QVariant Object \o The result is the QVariant value of the object (no conversion). + \row \o QObject Object \o A QVariant containing a pointer to the QObject. + \row \o Date Object \o A QVariant containing the date value (toDateTime()). + \row \o RegExp Object \o A QVariant containing the regular expression value (toRegExp()). + \row \o Array Object \o The array is converted to a QVariantList. Each element is converted to a QVariant, recursively; cyclic references are not followed. + \row \o Object \o The object is converted to a QVariantMap. Each property is converted to a QVariant, recursively; cyclic references are not followed. + \endtable + + \sa isVariant() +*/ +QVariant QScriptValue::toVariant() const +{ + Q_D(const QScriptValue); + if (!d) + return QVariant(); + switch (d->type) { + case QScriptValuePrivate::JavaScriptCore: { + if (d->engine) { + QScript::APIShim shim(d->engine); + return QScriptEnginePrivate::toVariant(d->engine->currentFrame, d->jscValue); + } else { + return QScriptEnginePrivate::toVariant(0, d->jscValue); + } + } + case QScriptValuePrivate::Number: + return QVariant(d->numberValue); + case QScriptValuePrivate::String: + return QVariant(d->stringValue); + } + return QVariant(); +} + +/*! + \obsolete + + This function is obsolete; use QScriptEngine::toObject() instead. +*/ +QScriptValue QScriptValue::toObject() const +{ + Q_D(const QScriptValue); + if (!d || !d->engine) + return QScriptValue(); + return engine()->toObject(*this); +} + +/*! + Returns a QDateTime representation of this value, in local time. + If this QScriptValue is not a date, or the value of the date is NaN + (Not-a-Number), an invalid QDateTime is returned. + + \sa isDate() +*/ +QDateTime QScriptValue::toDateTime() const +{ + Q_D(const QScriptValue); + if (!d || !d->engine) + return QDateTime(); + QScript::APIShim shim(d->engine); + return QScriptEnginePrivate::toDateTime(d->engine->currentFrame, d->jscValue); +} + +#ifndef QT_NO_REGEXP +/*! + Returns the QRegExp representation of this value. + If this QScriptValue is not a regular expression, an empty + QRegExp is returned. + + \sa isRegExp() +*/ +QRegExp QScriptValue::toRegExp() const +{ + Q_D(const QScriptValue); + if (!d || !d->engine) + return QRegExp(); + QScript::APIShim shim(d->engine); + return QScriptEnginePrivate::toRegExp(d->engine->currentFrame, d->jscValue); +} +#endif // QT_NO_REGEXP + +/*! + If this QScriptValue is a QObject, returns the QObject pointer + that the QScriptValue represents; otherwise, returns 0. + + If the QObject that this QScriptValue wraps has been deleted, + this function returns 0 (i.e. it is possible for toQObject() + to return 0 even when isQObject() returns true). + + \sa isQObject() +*/ +QObject *QScriptValue::toQObject() const +{ + Q_D(const QScriptValue); + if (!d || !d->engine) + return 0; + QScript::APIShim shim(d->engine); + return QScriptEnginePrivate::toQObject(d->engine->currentFrame, d->jscValue); +} + +/*! + If this QScriptValue is a QMetaObject, returns the QMetaObject pointer + that the QScriptValue represents; otherwise, returns 0. + + \sa isQMetaObject() +*/ +const QMetaObject *QScriptValue::toQMetaObject() const +{ + Q_D(const QScriptValue); + if (!d || !d->engine) + return 0; + QScript::APIShim shim(d->engine); + return QScriptEnginePrivate::toQMetaObject(d->engine->currentFrame, d->jscValue); +} + +/*! + Sets the value of this QScriptValue's property with the given \a name to + the given \a value. + + If this QScriptValue is not an object, this function does nothing. + + If this QScriptValue does not already have a property with name \a name, + a new property is created; the given \a flags then specify how this + property may be accessed by script code. + + If \a value is invalid, the property is removed. + + If the property is implemented using a setter function (i.e. has the + PropertySetter flag set), calling setProperty() has side-effects on + the script engine, since the setter function will be called with the + given \a value as argument (possibly resulting in an uncaught script + exception). + + Note that you cannot specify custom getter or setter functions for + built-in properties, such as the \c{length} property of Array objects + or meta properties of QObject objects. + + \sa property() +*/ + +void QScriptValue::setProperty(const QString &name, const QScriptValue &value, + const PropertyFlags &flags) +{ + Q_D(QScriptValue); + if (!d || !d->isObject()) + return; + QScript::APIShim shim(d->engine); + QScriptEnginePrivate *valueEngine = QScriptValuePrivate::getEngine(value); + if (valueEngine && (valueEngine != d->engine)) { + qWarning("QScriptValue::setProperty(%s) failed: " + "cannot set value created in a different engine", + qPrintable(name)); + return; + } + JSC::JSValue jsValue = d->engine->scriptValueToJSCValue(value); + d->setProperty(name, jsValue, flags); +} + +/*! + Returns the value of this QScriptValue's property with the given \a name, + using the given \a mode to resolve the property. + + If no such property exists, an invalid QScriptValue is returned. + + If the property is implemented using a getter function (i.e. has the + PropertyGetter flag set), calling property() has side-effects on the + script engine, since the getter function will be called (possibly + resulting in an uncaught script exception). If an exception + occurred, property() returns the value that was thrown (typically + an \c{Error} object). + + \sa setProperty(), propertyFlags(), QScriptValueIterator +*/ +QScriptValue QScriptValue::property(const QString &name, + const ResolveFlags &mode) const +{ + Q_D(const QScriptValue); + if (!d || !d->isObject()) + return QScriptValue(); + QScript::APIShim shim(d->engine); + return d->engine->scriptValueFromJSCValue(d->property(name, mode)); +} + +/*! + \overload + + Returns the property at the given \a arrayIndex, using the given \a + mode to resolve the property. + + This function is provided for convenience and performance when + working with array objects. + + If this QScriptValue is not an Array object, this function behaves + as if property() was called with the string representation of \a + arrayIndex. +*/ +QScriptValue QScriptValue::property(quint32 arrayIndex, + const ResolveFlags &mode) const +{ + Q_D(const QScriptValue); + if (!d || !d->isObject()) + return QScriptValue(); + QScript::APIShim shim(d->engine); + return d->engine->scriptValueFromJSCValue(d->property(arrayIndex, mode)); +} + +/*! + \overload + + Sets the property at the given \a arrayIndex to the given \a value. + + This function is provided for convenience and performance when + working with array objects. + + If this QScriptValue is not an Array object, this function behaves + as if setProperty() was called with the string representation of \a + arrayIndex. +*/ +void QScriptValue::setProperty(quint32 arrayIndex, const QScriptValue &value, + const PropertyFlags &flags) +{ + Q_D(QScriptValue); + if (!d || !d->isObject()) + return; + if (QScriptValuePrivate::getEngine(value) + && (QScriptValuePrivate::getEngine(value) != d->engine)) { + qWarning("QScriptValue::setProperty() failed: " + "cannot set value created in a different engine"); + return; + } + QScript::APIShim shim(d->engine); + JSC::JSValue jsValue = d->engine->scriptValueToJSCValue(value); + d->setProperty(arrayIndex, jsValue, flags); +} + +/*! + \since 4.4 + + Returns the value of this QScriptValue's property with the given \a name, + using the given \a mode to resolve the property. + + This overload of property() is useful when you need to look up the + same property repeatedly, since the lookup can be performed faster + when the name is represented as an interned string. + + \sa QScriptEngine::toStringHandle(), setProperty() +*/ +QScriptValue QScriptValue::property(const QScriptString &name, + const ResolveFlags &mode) const +{ + Q_D(const QScriptValue); + if (!d || !d->isObject() || !QScriptStringPrivate::isValid(name)) + return QScriptValue(); + QScript::APIShim shim(d->engine); + return d->engine->scriptValueFromJSCValue(d->property(name.d_ptr->identifier, mode)); +} + +/*! + \since 4.4 + + Sets the value of this QScriptValue's property with the given \a + name to the given \a value. The given \a flags specify how this + property may be accessed by script code. + + This overload of setProperty() is useful when you need to set the + same property repeatedly, since the operation can be performed + faster when the name is represented as an interned string. + + \sa QScriptEngine::toStringHandle() +*/ +void QScriptValue::setProperty(const QScriptString &name, + const QScriptValue &value, + const PropertyFlags &flags) +{ + Q_D(QScriptValue); + if (!d || !d->isObject() || !QScriptStringPrivate::isValid(name)) + return; + QScriptEnginePrivate *valueEngine = QScriptValuePrivate::getEngine(value); + if (valueEngine && (valueEngine != d->engine)) { + qWarning("QScriptValue::setProperty(%s) failed: " + "cannot set value created in a different engine", + qPrintable(name.toString())); + return; + } + QScript::APIShim shim(d->engine); + JSC::JSValue jsValue = d->engine->scriptValueToJSCValue(value); + d->setProperty(name.d_ptr->identifier, jsValue, flags); +} + +/*! + Returns the flags of the property with the given \a name, using the + given \a mode to resolve the property. + + \sa property() +*/ +QScriptValue::PropertyFlags QScriptValue::propertyFlags(const QString &name, + const ResolveFlags &mode) const +{ + Q_D(const QScriptValue); + if (!d || !d->isObject()) + return 0; + QScript::APIShim shim(d->engine); + JSC::ExecState *exec = d->engine->currentFrame; + return d->propertyFlags(JSC::Identifier(exec, name), mode); + +} + +/*! + \since 4.4 + + Returns the flags of the property with the given \a name, using the + given \a mode to resolve the property. + + \sa property() +*/ +QScriptValue::PropertyFlags QScriptValue::propertyFlags(const QScriptString &name, + const ResolveFlags &mode) const +{ + Q_D(const QScriptValue); + if (!d || !d->isObject() || !QScriptStringPrivate::isValid(name)) + return 0; + return d->propertyFlags(name.d_ptr->identifier, mode); +} + +/*! + Calls this QScriptValue as a function, using \a thisObject as + the `this' object in the function call, and passing \a args + as arguments to the function. Returns the value returned from + the function. + + If this QScriptValue is not a function, call() does nothing + and returns an invalid QScriptValue. + + Note that if \a thisObject is not an object, the global object + (see \l{QScriptEngine::globalObject()}) will be used as the + `this' object. + + Calling call() can cause an exception to occur in the script engine; + in that case, call() returns the value that was thrown (typically an + \c{Error} object). You can call + QScriptEngine::hasUncaughtException() to determine if an exception + occurred. + + \snippet doc/src/snippets/code/src_script_qscriptvalue.cpp 2 + + \sa construct() +*/ +QScriptValue QScriptValue::call(const QScriptValue &thisObject, + const QScriptValueList &args) +{ + Q_D(const QScriptValue); + if (!d || !d->isObject()) + return QScriptValue(); + QScript::APIShim shim(d->engine); + JSC::JSValue callee = d->jscValue; + JSC::CallData callData; + JSC::CallType callType = callee.getCallData(callData); + if (callType == JSC::CallTypeNone) + return QScriptValue(); + + if (QScriptValuePrivate::getEngine(thisObject) + && (QScriptValuePrivate::getEngine(thisObject) != d->engine)) { + qWarning("QScriptValue::call() failed: " + "cannot call function with thisObject created in " + "a different engine"); + return QScriptValue(); + } + + JSC::ExecState *exec = d->engine->currentFrame; + + JSC::JSValue jscThisObject = d->engine->scriptValueToJSCValue(thisObject); + if (!jscThisObject || !jscThisObject.isObject()) + jscThisObject = d->engine->globalObject(); + + QVarLengthArray<JSC::JSValue, 8> argsVector(args.size()); + for (int i = 0; i < args.size(); ++i) { + const QScriptValue &arg = args.at(i); + if (!arg.isValid()) { + argsVector[i] = JSC::jsUndefined(); + } else if (QScriptValuePrivate::getEngine(arg) + && (QScriptValuePrivate::getEngine(arg) != d->engine)) { + qWarning("QScriptValue::call() failed: " + "cannot call function with argument created in " + "a different engine"); + return QScriptValue(); + } else { + argsVector[i] = d->engine->scriptValueToJSCValue(arg); + } + } + JSC::ArgList jscArgs(argsVector.data(), argsVector.size()); + + JSC::JSValue savedException; + QScriptEnginePrivate::saveException(exec, &savedException); + JSC::JSValue result = JSC::call(exec, callee, callType, callData, jscThisObject, jscArgs); + if (exec->hadException()) { + result = exec->exception(); + } else { + QScriptEnginePrivate::restoreException(exec, savedException); + } + return d->engine->scriptValueFromJSCValue(result); +} + +/*! + Calls this QScriptValue as a function, using \a thisObject as + the `this' object in the function call, and passing \a arguments + as arguments to the function. Returns the value returned from + the function. + + If this QScriptValue is not a function, call() does nothing + and returns an invalid QScriptValue. + + \a arguments can be an arguments object, an array, null or + undefined; any other type will cause a TypeError to be thrown. + + Note that if \a thisObject is not an object, the global object + (see \l{QScriptEngine::globalObject()}) will be used as the + `this' object. + + One common usage of this function is to forward native function + calls to another function: + + \snippet doc/src/snippets/code/src_script_qscriptvalue.cpp 3 + + \sa construct(), QScriptContext::argumentsObject() +*/ +QScriptValue QScriptValue::call(const QScriptValue &thisObject, + const QScriptValue &arguments) +{ + Q_D(QScriptValue); + if (!d || !d->isObject()) + return QScriptValue(); + QScript::APIShim shim(d->engine); + JSC::JSValue callee = d->jscValue; + JSC::CallData callData; + JSC::CallType callType = callee.getCallData(callData); + if (callType == JSC::CallTypeNone) + return QScriptValue(); + + if (QScriptValuePrivate::getEngine(thisObject) + && (QScriptValuePrivate::getEngine(thisObject) != d->engine)) { + qWarning("QScriptValue::call() failed: " + "cannot call function with thisObject created in " + "a different engine"); + return QScriptValue(); + } + + JSC::ExecState *exec = d->engine->currentFrame; + + JSC::JSValue jscThisObject = d->engine->scriptValueToJSCValue(thisObject); + if (!jscThisObject || !jscThisObject.isObject()) + jscThisObject = d->engine->globalObject(); + + JSC::JSValue array = d->engine->scriptValueToJSCValue(arguments); + // copied from runtime/FunctionPrototype.cpp, functionProtoFuncApply() + JSC::MarkedArgumentBuffer applyArgs; + if (!array.isUndefinedOrNull()) { + if (!array.isObject()) { + return d->engine->scriptValueFromJSCValue(JSC::throwError(exec, JSC::TypeError, "Arguments must be an array")); + } + if (JSC::asObject(array)->classInfo() == &JSC::Arguments::info) + JSC::asArguments(array)->fillArgList(exec, applyArgs); + else if (JSC::isJSArray(&exec->globalData(), array)) + JSC::asArray(array)->fillArgList(exec, applyArgs); + else if (JSC::asObject(array)->inherits(&JSC::JSArray::info)) { + unsigned length = JSC::asArray(array)->get(exec, exec->propertyNames().length).toUInt32(exec); + for (unsigned i = 0; i < length; ++i) + applyArgs.append(JSC::asArray(array)->get(exec, i)); + } else { + return d->engine->scriptValueFromJSCValue(JSC::throwError(exec, JSC::TypeError, "Arguments must be an array")); + } + } + + JSC::JSValue savedException; + QScriptEnginePrivate::saveException(exec, &savedException); + JSC::JSValue result = JSC::call(exec, callee, callType, callData, jscThisObject, applyArgs); + if (exec->hadException()) { + result = exec->exception(); + } else { + QScriptEnginePrivate::restoreException(exec, savedException); + } + return d->engine->scriptValueFromJSCValue(result); +} + +/*! + Creates a new \c{Object} and calls this QScriptValue as a + constructor, using the created object as the `this' object and + passing \a args as arguments. If the return value from the + constructor call is an object, then that object is returned; + otherwise the default constructed object is returned. + + If this QScriptValue is not a function, construct() does nothing + and returns an invalid QScriptValue. + + Calling construct() can cause an exception to occur in the script + engine; in that case, construct() returns the value that was thrown + (typically an \c{Error} object). You can call + QScriptEngine::hasUncaughtException() to determine if an exception + occurred. + + \sa call(), QScriptEngine::newObject() +*/ +QScriptValue QScriptValue::construct(const QScriptValueList &args) +{ + Q_D(const QScriptValue); + if (!d || !d->isObject()) + return QScriptValue(); + QScript::APIShim shim(d->engine); + JSC::JSValue callee = d->jscValue; + JSC::ConstructData constructData; + JSC::ConstructType constructType = callee.getConstructData(constructData); + if (constructType == JSC::ConstructTypeNone) + return QScriptValue(); + + JSC::ExecState *exec = d->engine->currentFrame; + + QVarLengthArray<JSC::JSValue, 8> argsVector(args.size()); + for (int i = 0; i < args.size(); ++i) { + QScriptValue arg = args.at(i); + if (QScriptValuePrivate::getEngine(arg) != d->engine && QScriptValuePrivate::getEngine(arg)) { + qWarning("QScriptValue::construct() failed: " + "cannot construct function with argument created in " + "a different engine"); + return QScriptValue(); + } + if (!arg.isValid()) + argsVector[i] = JSC::jsUndefined(); + else + argsVector[i] = d->engine->scriptValueToJSCValue(args.at(i)); + } + + JSC::ArgList jscArgs(argsVector.data(), argsVector.size()); + + JSC::JSValue savedException; + QScriptEnginePrivate::saveException(exec, &savedException); + JSC::JSValue result; + JSC::JSObject *newObject = JSC::construct(exec, callee, constructType, constructData, jscArgs); + if (exec->hadException()) { + result = exec->exception(); + } else { + result = newObject; + QScriptEnginePrivate::restoreException(exec, savedException); + } + return d->engine->scriptValueFromJSCValue(result); +} + +/*! + Creates a new \c{Object} and calls this QScriptValue as a + constructor, using the created object as the `this' object and + passing \a arguments as arguments. If the return value from the + constructor call is an object, then that object is returned; + otherwise the default constructed object is returned. + + If this QScriptValue is not a function, construct() does nothing + and returns an invalid QScriptValue. + + \a arguments can be an arguments object, an array, null or + undefined. Any other type will cause a TypeError to be thrown. + + \sa call(), QScriptEngine::newObject(), QScriptContext::argumentsObject() +*/ +QScriptValue QScriptValue::construct(const QScriptValue &arguments) +{ + Q_D(QScriptValue); + if (!d || !d->isObject()) + return QScriptValue(); + QScript::APIShim shim(d->engine); + JSC::JSValue callee = d->jscValue; + JSC::ConstructData constructData; + JSC::ConstructType constructType = callee.getConstructData(constructData); + if (constructType == JSC::ConstructTypeNone) + return QScriptValue(); + + JSC::ExecState *exec = d->engine->currentFrame; + + if (QScriptValuePrivate::getEngine(arguments) != d->engine && QScriptValuePrivate::getEngine(arguments)) { + qWarning("QScriptValue::construct() failed: " + "cannot construct function with argument created in " + "a different engine"); + return QScriptValue(); + } + JSC::JSValue array = d->engine->scriptValueToJSCValue(arguments); + // copied from runtime/FunctionPrototype.cpp, functionProtoFuncApply() + JSC::MarkedArgumentBuffer applyArgs; + if (!array.isUndefinedOrNull()) { + if (!array.isObject()) { + return d->engine->scriptValueFromJSCValue(JSC::throwError(exec, JSC::TypeError, "Arguments must be an array")); + } + if (JSC::asObject(array)->classInfo() == &JSC::Arguments::info) + JSC::asArguments(array)->fillArgList(exec, applyArgs); + else if (JSC::isJSArray(&exec->globalData(), array)) + JSC::asArray(array)->fillArgList(exec, applyArgs); + else if (JSC::asObject(array)->inherits(&JSC::JSArray::info)) { + unsigned length = JSC::asArray(array)->get(exec, exec->propertyNames().length).toUInt32(exec); + for (unsigned i = 0; i < length; ++i) + applyArgs.append(JSC::asArray(array)->get(exec, i)); + } else { + return d->engine->scriptValueFromJSCValue(JSC::throwError(exec, JSC::TypeError, "Arguments must be an array")); + } + } + + JSC::JSValue savedException; + QScriptEnginePrivate::saveException(exec, &savedException); + JSC::JSValue result; + JSC::JSObject *newObject = JSC::construct(exec, callee, constructType, constructData, applyArgs); + if (exec->hadException()) { + result = exec->exception(); + } else { + result = newObject; + QScriptEnginePrivate::restoreException(exec, savedException); + } + return d->engine->scriptValueFromJSCValue(result); +} + +/*! + Returns the QScriptEngine that created this QScriptValue, + or 0 if this QScriptValue is invalid or the value is not + associated with a particular engine. +*/ +QScriptEngine *QScriptValue::engine() const +{ + Q_D(const QScriptValue); + if (!d) + return 0; + return QScriptEnginePrivate::get(d->engine); +} + +/*! + \obsolete + + Use isBool() instead. +*/ +bool QScriptValue::isBoolean() const +{ + Q_D(const QScriptValue); + return d && d->isJSC() && d->jscValue.isBoolean(); +} + +/*! + \since 4.5 + + Returns true if this QScriptValue is of the primitive type Boolean; + otherwise returns false. + + \sa toBool() +*/ +bool QScriptValue::isBool() const +{ + Q_D(const QScriptValue); + return d && d->isJSC() && d->jscValue.isBoolean(); +} + +/*! + Returns true if this QScriptValue is of the primitive type Number; + otherwise returns false. + + \sa toNumber() +*/ +bool QScriptValue::isNumber() const +{ + Q_D(const QScriptValue); + if (!d) + return false; + switch (d->type) { + case QScriptValuePrivate::JavaScriptCore: + return d->jscValue.isNumber(); + case QScriptValuePrivate::Number: + return true; + case QScriptValuePrivate::String: + return false; + } + return false; +} + +/*! + Returns true if this QScriptValue is of the primitive type String; + otherwise returns false. + + \sa toString() +*/ +bool QScriptValue::isString() const +{ + Q_D(const QScriptValue); + if (!d) + return false; + switch (d->type) { + case QScriptValuePrivate::JavaScriptCore: + return d->jscValue.isString(); + case QScriptValuePrivate::Number: + return false; + case QScriptValuePrivate::String: + return true; + } + return false; +} + +/*! + Returns true if this QScriptValue is a function; otherwise returns + false. + + \sa call() +*/ +bool QScriptValue::isFunction() const +{ + Q_D(const QScriptValue); + if (!d || !d->isJSC()) + return false; + return QScript::isFunction(d->jscValue); +} + +/*! + Returns true if this QScriptValue is of the primitive type Null; + otherwise returns false. + + \sa QScriptEngine::nullValue() +*/ +bool QScriptValue::isNull() const +{ + Q_D(const QScriptValue); + return d && d->isJSC() && d->jscValue.isNull(); +} + +/*! + Returns true if this QScriptValue is of the primitive type Undefined; + otherwise returns false. + + \sa QScriptEngine::undefinedValue() +*/ +bool QScriptValue::isUndefined() const +{ + Q_D(const QScriptValue); + return d && d->isJSC() && d->jscValue.isUndefined(); +} + +/*! + Returns true if this QScriptValue is of the Object type; otherwise + returns false. + + Note that function values, variant values, and QObject values are + objects, so this function returns true for such values. + + \sa toObject(), QScriptEngine::newObject() +*/ +bool QScriptValue::isObject() const +{ + Q_D(const QScriptValue); + return d && d->isObject(); +} + +/*! + Returns true if this QScriptValue is a variant value; + otherwise returns false. + + \sa toVariant(), QScriptEngine::newVariant() +*/ +bool QScriptValue::isVariant() const +{ + Q_D(const QScriptValue); + if (!d || !d->isJSC()) + return false; + return QScriptEnginePrivate::isVariant(d->jscValue); +} + +/*! + Returns true if this QScriptValue is a QObject; otherwise returns + false. + + Note: This function returns true even if the QObject that this + QScriptValue wraps has been deleted. + + \sa toQObject(), QScriptEngine::newQObject() +*/ +bool QScriptValue::isQObject() const +{ + Q_D(const QScriptValue); + if (!d || !d->isJSC()) + return false; + return QScriptEnginePrivate::isQObject(d->jscValue); +} + +/*! + Returns true if this QScriptValue is a QMetaObject; otherwise returns + false. + + \sa toQMetaObject(), QScriptEngine::newQMetaObject() +*/ +bool QScriptValue::isQMetaObject() const +{ + Q_D(const QScriptValue); + if (!d || !d->isJSC()) + return false; + return QScriptEnginePrivate::isQMetaObject(d->jscValue); +} + +/*! + Returns true if this QScriptValue is valid; otherwise returns + false. +*/ +bool QScriptValue::isValid() const +{ + Q_D(const QScriptValue); + return d && (!d->isJSC() || !!d->jscValue); +} + +/*! + \since 4.4 + + Returns the internal data of this QScriptValue object. QtScript uses + this property to store the primitive value of Date, String, Number + and Boolean objects. For other types of object, custom data may be + stored using setData(). +*/ +QScriptValue QScriptValue::data() const +{ + Q_D(const QScriptValue); + if (!d || !d->isObject()) + return QScriptValue(); + if (d->jscValue.inherits(&QScriptObject::info)) { + QScriptObject *scriptObject = static_cast<QScriptObject*>(JSC::asObject(d->jscValue)); + return d->engine->scriptValueFromJSCValue(scriptObject->data()); + } else { + // ### make hidden property + return property(QLatin1String("__qt_data__"), QScriptValue::ResolveLocal); + } +} + +/*! + \since 4.4 + + Sets the internal \a data of this QScriptValue object. You can use + this function to set object-specific data that won't be directly + accessible to scripts, but may be retrieved in C++ using the data() + function. + + \sa QScriptEngine::reportAdditionalMemoryCost() +*/ +void QScriptValue::setData(const QScriptValue &data) +{ + Q_D(QScriptValue); + if (!d || !d->isObject()) + return; + QScript::APIShim shim(d->engine); + JSC::JSValue other = d->engine->scriptValueToJSCValue(data); + if (d->jscValue.inherits(&QScriptObject::info)) { + QScriptObject *scriptObject = static_cast<QScriptObject*>(JSC::asObject(d->jscValue)); + scriptObject->setData(other); + } else { + JSC::ExecState *exec = d->engine->currentFrame; + JSC::Identifier id = JSC::Identifier(exec, "__qt_data__"); + if (!data.isValid()) { + JSC::asObject(d->jscValue)->removeDirect(id); + } else { + // ### make hidden property + JSC::asObject(d->jscValue)->putDirect(id, other); + } + } +} + +/*! + \since 4.4 + + Returns the custom script class that this script object is an + instance of, or 0 if the object is not of a custom class. + + \sa setScriptClass() +*/ +QScriptClass *QScriptValue::scriptClass() const +{ + Q_D(const QScriptValue); + if (!d || !d->isJSC() || !d->jscValue.inherits(&QScriptObject::info)) + return 0; + QScriptObject *scriptObject = static_cast<QScriptObject*>(JSC::asObject(d->jscValue)); + QScriptObjectDelegate *delegate = scriptObject->delegate(); + if (!delegate || (delegate->type() != QScriptObjectDelegate::ClassObject)) + return 0; + return static_cast<QScript::ClassObjectDelegate*>(delegate)->scriptClass(); +} + +/*! + \since 4.4 + + Sets the custom script class of this script object to \a scriptClass. + This can be used to "promote" a plain script object (e.g. created + by the "new" operator in a script, or by QScriptEngine::newObject() in C++) + to an object of a custom type. + + If \a scriptClass is 0, the object will be demoted to a plain + script object. + + \sa scriptClass(), setData() +*/ +void QScriptValue::setScriptClass(QScriptClass *scriptClass) +{ + Q_D(QScriptValue); + if (!d || !d->isObject()) + return; + if (!d->jscValue.inherits(&QScriptObject::info)) { + qWarning("QScriptValue::setScriptClass() failed: " + "cannot change class of non-QScriptObject"); + return; + } + QScriptObject *scriptObject = static_cast<QScriptObject*>(JSC::asObject(d->jscValue)); + if (!scriptClass) { + scriptObject->setDelegate(0); + } else { + QScriptObjectDelegate *delegate = scriptObject->delegate(); + if (!delegate || (delegate->type() != QScriptObjectDelegate::ClassObject)) { + delegate = new QScript::ClassObjectDelegate(scriptClass); + scriptObject->setDelegate(delegate); + } + static_cast<QScript::ClassObjectDelegate*>(delegate)->setScriptClass(scriptClass); + } +} + +/*! + \internal + + Returns the ID of this object, or -1 if this QScriptValue is not an + object. + + \sa QScriptEngine::objectById() +*/ +qint64 QScriptValue::objectId() const +{ + return d_ptr?d_ptr->objectId():-1; +} +QT_END_NAMESPACE diff --git a/src/script/api/qscriptvalue.h b/src/script/api/qscriptvalue.h new file mode 100644 index 0000000..9cb5d38 --- /dev/null +++ b/src/script/api/qscriptvalue.h @@ -0,0 +1,223 @@ +/**************************************************************************** +** +** 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 QSCRIPTVALUE_H +#define QSCRIPTVALUE_H + +#include <QtCore/qstring.h> + +#include <QtCore/qlist.h> +#include <QtCore/qsharedpointer.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Script) + +class QScriptClass; +class QScriptValue; +class QScriptEngine; +class QScriptString; +class QVariant; +class QObject; +struct QMetaObject; +class QDateTime; +#ifndef QT_NO_REGEXP +class QRegExp; +#endif + +typedef QList<QScriptValue> QScriptValueList; + +typedef double qsreal; + +class QScriptValuePrivate; +class QScriptEnginePrivate; +struct QScriptValuePrivatePointerDeleter; +class Q_SCRIPT_EXPORT QScriptValue +{ +public: + enum ResolveFlag { + ResolveLocal = 0x00, + ResolvePrototype = 0x01, + ResolveScope = 0x02, + ResolveFull = ResolvePrototype | ResolveScope + }; + + Q_DECLARE_FLAGS(ResolveFlags, ResolveFlag) + + enum PropertyFlag { + ReadOnly = 0x00000001, + Undeletable = 0x00000002, + SkipInEnumeration = 0x00000004, + + PropertyGetter = 0x00000008, + PropertySetter = 0x00000010, + + QObjectMember = 0x00000020, + + KeepExistingFlags = 0x00000800, + + UserRange = 0xff000000 // Users may use these as they see fit. + }; + Q_DECLARE_FLAGS(PropertyFlags, PropertyFlag) + + enum SpecialValue { + NullValue, + UndefinedValue + }; + +public: + QScriptValue(); + ~QScriptValue(); + QScriptValue(const QScriptValue &other); + QScriptValue(QScriptEngine *engine, SpecialValue val); + QScriptValue(QScriptEngine *engine, bool val); + QScriptValue(QScriptEngine *engine, int val); + QScriptValue(QScriptEngine *engine, uint val); + QScriptValue(QScriptEngine *engine, qsreal val); + QScriptValue(QScriptEngine *engine, const QString &val); +#ifndef QT_NO_CAST_FROM_ASCII + QT_ASCII_CAST_WARN_CONSTRUCTOR QScriptValue(QScriptEngine *engine, const char *val); +#endif + + QScriptValue(SpecialValue value); + QScriptValue(bool value); + QScriptValue(int value); + QScriptValue(uint value); + QScriptValue(qsreal value); + QScriptValue(const QString &value); + QScriptValue(const QLatin1String &value); +#ifndef QT_NO_CAST_FROM_ASCII + QT_ASCII_CAST_WARN_CONSTRUCTOR QScriptValue(const char *value); +#endif + + QScriptValue &operator=(const QScriptValue &other); + + QScriptEngine *engine() const; + + bool isValid() const; + bool isBool() const; + bool isBoolean() const; + bool isNumber() const; + bool isFunction() const; + bool isNull() const; + bool isString() const; + bool isUndefined() const; + bool isVariant() const; + bool isQObject() const; + bool isQMetaObject() const; + bool isObject() const; + bool isDate() const; + bool isRegExp() const; + bool isArray() const; + bool isError() const; + + QString toString() const; + qsreal toNumber() const; + bool toBool() const; + bool toBoolean() const; + qsreal toInteger() const; + qint32 toInt32() const; + quint32 toUInt32() const; + quint16 toUInt16() const; + QVariant toVariant() const; + QObject *toQObject() const; + const QMetaObject *toQMetaObject() const; + QScriptValue toObject() const; + QDateTime toDateTime() const; +#ifndef QT_NO_REGEXP + QRegExp toRegExp() const; +#endif + + bool instanceOf(const QScriptValue &other) const; + + bool lessThan(const QScriptValue &other) const; + bool equals(const QScriptValue &other) const; + bool strictlyEquals(const QScriptValue &other) const; + + QScriptValue prototype() const; + void setPrototype(const QScriptValue &prototype); + + QScriptValue scope() const; + void setScope(const QScriptValue &scope); + + QScriptValue property(const QString &name, + const ResolveFlags &mode = ResolvePrototype) const; + void setProperty(const QString &name, const QScriptValue &value, + const PropertyFlags &flags = KeepExistingFlags); + + QScriptValue property(quint32 arrayIndex, + const ResolveFlags &mode = ResolvePrototype) const; + void setProperty(quint32 arrayIndex, const QScriptValue &value, + const PropertyFlags &flags = KeepExistingFlags); + + QScriptValue property(const QScriptString &name, + const ResolveFlags &mode = ResolvePrototype) const; + void setProperty(const QScriptString &name, const QScriptValue &value, + const PropertyFlags &flags = KeepExistingFlags); + + QScriptValue::PropertyFlags propertyFlags( + const QString &name, const ResolveFlags &mode = ResolvePrototype) const; + QScriptValue::PropertyFlags propertyFlags( + const QScriptString &name, const ResolveFlags &mode = ResolvePrototype) const; + + QScriptValue call(const QScriptValue &thisObject = QScriptValue(), + const QScriptValueList &args = QScriptValueList()); + QScriptValue call(const QScriptValue &thisObject, + const QScriptValue &arguments); + QScriptValue construct(const QScriptValueList &args = QScriptValueList()); + QScriptValue construct(const QScriptValue &arguments); + + QScriptValue data() const; + void setData(const QScriptValue &data); + + QScriptClass *scriptClass() const; + void setScriptClass(QScriptClass *scriptClass); + + qint64 objectId() const; + +private: + // force compile error, prevent QScriptValue(bool) to be called + QScriptValue(void *); + // force compile error, prevent QScriptValue(QScriptEngine*, bool) to be called + QScriptValue(QScriptEngine *, void *); + + QScriptValue(QScriptValuePrivate*); + +private: + QExplicitlySharedDataPointer<QScriptValuePrivate> d_ptr; + + Q_DECLARE_PRIVATE(QScriptValue) + + friend class QScriptEnginePrivate; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QScriptValue::ResolveFlags) +Q_DECLARE_OPERATORS_FOR_FLAGS(QScriptValue::PropertyFlags) + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/script/api/qscriptvalue_p.h b/src/script/api/qscriptvalue_p.h new file mode 100644 index 0000000..c996ed3 --- /dev/null +++ b/src/script/api/qscriptvalue_p.h @@ -0,0 +1,145 @@ +/**************************************************************************** +** +** 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 QSCRIPTVALUE_P_H +#define QSCRIPTVALUE_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 "wtf/Platform.h" +#include "JSValue.h" + +QT_BEGIN_NAMESPACE + +class QString; +class QScriptEnginePrivate; + +class QScriptValue; +class QScriptValuePrivate +{ + Q_DISABLE_COPY(QScriptValuePrivate) +public: + inline void* operator new(size_t, QScriptEnginePrivate*); + inline void operator delete(void*); + + enum Type { + JavaScriptCore, + Number, + String + }; + + inline QScriptValuePrivate(QScriptEnginePrivate*); + inline ~QScriptValuePrivate(); + + inline void initFrom(JSC::JSValue value); + inline void initFrom(qsreal value); + inline void initFrom(const QString &value); + + inline bool isJSC() const; + inline bool isObject() const; + + static inline QScriptValuePrivate *get(const QScriptValue &q) + { + return q.d_ptr.data(); + } + + static inline QScriptValue toPublic(QScriptValuePrivate *d) + { + return QScriptValue(d); + } + + static inline QScriptEnginePrivate *getEngine(const QScriptValue &q) + { + if (!q.d_ptr) + return 0; + return q.d_ptr->engine; + } + + inline JSC::JSValue property(const JSC::Identifier &id, + const QScriptValue::ResolveFlags &mode = QScriptValue::ResolvePrototype) const; + inline JSC::JSValue property(quint32 index, const QScriptValue::ResolveFlags &mode = QScriptValue::ResolvePrototype) const; + inline JSC::JSValue property(const JSC::UString &, const QScriptValue::ResolveFlags &mode = QScriptValue::ResolvePrototype) const; + inline void setProperty(const JSC::UString &name, const JSC::JSValue &value, + const QScriptValue::PropertyFlags &flags = QScriptValue::KeepExistingFlags); + inline void setProperty(const JSC::Identifier &id, const JSC::JSValue &value, + const QScriptValue::PropertyFlags &flags = QScriptValue::KeepExistingFlags); + inline void setProperty(quint32 index, const JSC::JSValue &value, + const QScriptValue::PropertyFlags &flags = QScriptValue::KeepExistingFlags); + inline QScriptValue::PropertyFlags propertyFlags( + const JSC::Identifier &id, const QScriptValue::ResolveFlags &mode = QScriptValue::ResolvePrototype) const; + + void detachFromEngine(); + + qint64 objectId() + { + if ( (type == JavaScriptCore) && (engine) && jscValue.isCell() ) + return (qint64)jscValue.asCell(); + else + return -1; + } + + QScriptEnginePrivate *engine; + Type type; + JSC::JSValue jscValue; + qsreal numberValue; + QString stringValue; + + // linked list of engine's script values + QScriptValuePrivate *prev; + QScriptValuePrivate *next; + + QBasicAtomicInt ref; +}; + +inline QScriptValuePrivate::QScriptValuePrivate(QScriptEnginePrivate *e) + : engine(e), prev(0), next(0) +{ + ref = 0; +} + +inline bool QScriptValuePrivate::isJSC() const +{ + return (type == JavaScriptCore); +} + +inline bool QScriptValuePrivate::isObject() const +{ + return isJSC() && jscValue && jscValue.isObject(); +} + +// Rest of inline functions implemented in qscriptengine_p.h + +QT_END_NAMESPACE + +#endif diff --git a/src/script/api/qscriptvalueiterator.cpp b/src/script/api/qscriptvalueiterator.cpp new file mode 100644 index 0000000..5f53b46 --- /dev/null +++ b/src/script/api/qscriptvalueiterator.cpp @@ -0,0 +1,362 @@ +/**************************************************************************** +** +** 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 "qscriptvalueiterator.h" + +#include "qscriptstring.h" +#include "qscriptengine.h" +#include "qscriptengine_p.h" +#include "qscriptvalue_p.h" +#include "qlinkedlist.h" + + +#include "JSObject.h" +#include "PropertyNameArray.h" +#include "JSArray.h" +#include "JSFunction.h" + +QT_BEGIN_NAMESPACE + +/*! + \since 4.3 + \class QScriptValueIterator + + \brief The QScriptValueIterator class provides a Java-style iterator for QScriptValue. + + \ingroup script + + + The QScriptValueIterator constructor takes a QScriptValue as + argument. After construction, the iterator is located at the very + beginning of the sequence of properties. Here's how to iterate over + all the properties of a QScriptValue: + + \snippet doc/src/snippets/code/src_script_qscriptvalueiterator.cpp 0 + + The next() advances the iterator. The name(), value() and flags() + functions return the name, value and flags of the last item that was + jumped over. + + If you want to remove properties as you iterate over the + QScriptValue, use remove(). If you want to modify the value of a + property, use setValue(). + + Note that QScriptValueIterator only iterates over the QScriptValue's + own properties; i.e. it does not follow the prototype chain. You can + use a loop like this to follow the prototype chain: + + \snippet doc/src/snippets/code/src_script_qscriptvalueiterator.cpp 1 + + Note that QScriptValueIterator will not automatically skip over + properties that have the QScriptValue::SkipInEnumeration flag set; + that flag only affects iteration in script code. If you want, you + can skip over such properties with code like the following: + + \snippet doc/src/snippets/code/src_script_qscriptvalueiterator.cpp 2 + + \sa QScriptValue::property() +*/ + +class QScriptValueIteratorPrivate +{ +public: + QScriptValueIteratorPrivate() + : initialized(false) + {} + + ~QScriptValueIteratorPrivate() + { + if (!initialized) + return; + QScriptEnginePrivate *eng_p = engine(); + if (!eng_p) + return; + QScript::APIShim shim(eng_p); + propertyNames.clear(); //destroying the identifiers need to be done under the APIShim guard + } + + QScriptValuePrivate *object() const + { + return QScriptValuePrivate::get(objectValue); + } + + QScriptEnginePrivate *engine() const + { + return QScriptEnginePrivate::get(objectValue.engine()); + } + + void ensureInitialized() + { + if (initialized) + return; + QScriptEnginePrivate *eng_p = engine(); + QScript::APIShim shim(eng_p); + JSC::ExecState *exec = eng_p->globalExec(); + JSC::PropertyNameArray propertyNamesArray(exec); + JSC::asObject(object()->jscValue)->getOwnPropertyNames(exec, propertyNamesArray, JSC::IncludeDontEnumProperties); + + JSC::PropertyNameArray::const_iterator propertyNamesIt = propertyNamesArray.begin(); + for(; propertyNamesIt != propertyNamesArray.end(); ++propertyNamesIt) { + propertyNames.append(*propertyNamesIt); + } + it = propertyNames.begin(); + initialized = true; + } + + QScriptValue objectValue; + QLinkedList<JSC::Identifier> propertyNames; + QLinkedList<JSC::Identifier>::iterator it; + QLinkedList<JSC::Identifier>::iterator current; + bool initialized; +}; + +/*! + Constructs an iterator for traversing \a object. The iterator is + set to be at the front of the sequence of properties (before the + first property). +*/ +QScriptValueIterator::QScriptValueIterator(const QScriptValue &object) + : d_ptr(0) +{ + if (object.isObject()) { + d_ptr.reset(new QScriptValueIteratorPrivate()); + d_ptr->objectValue = object; + } +} + +/*! + Destroys the iterator. +*/ +QScriptValueIterator::~QScriptValueIterator() +{ +} + +/*! + Returns true if there is at least one item ahead of the iterator + (i.e. the iterator is \e not at the back of the property sequence); + otherwise returns false. + + \sa next(), hasPrevious() +*/ +bool QScriptValueIterator::hasNext() const +{ + Q_D(const QScriptValueIterator); + if (!d || !d->engine()) + return false; + + const_cast<QScriptValueIteratorPrivate*>(d)->ensureInitialized(); + return d->it != d->propertyNames.end(); +} + +/*! + Advances the iterator by one position. + + Calling this function on an iterator located at the back of the + container leads to undefined results. + + \sa hasNext(), previous(), name() +*/ +void QScriptValueIterator::next() +{ + Q_D(QScriptValueIterator); + if (!d) + return; + d->ensureInitialized(); + + d->current = d->it; + ++(d->it); +} + +/*! + Returns true if there is at least one item behind the iterator + (i.e. the iterator is \e not at the front of the property sequence); + otherwise returns false. + + \sa previous(), hasNext() +*/ +bool QScriptValueIterator::hasPrevious() const +{ + Q_D(const QScriptValueIterator); + if (!d || !d->engine()) + return false; + + const_cast<QScriptValueIteratorPrivate*>(d)->ensureInitialized(); + return d->it != d->propertyNames.begin(); +} + +/*! + Moves the iterator back by one position. + + Calling this function on an iterator located at the front of the + container leads to undefined results. + + \sa hasPrevious(), next(), name() +*/ +void QScriptValueIterator::previous() +{ + Q_D(QScriptValueIterator); + if (!d) + return; + d->ensureInitialized(); + --(d->it); + d->current = d->it; +} + +/*! + Moves the iterator to the front of the QScriptValue (before the + first property). + + \sa toBack(), next() +*/ +void QScriptValueIterator::toFront() +{ + Q_D(QScriptValueIterator); + if (!d) + return; + d->ensureInitialized(); + d->it = d->propertyNames.begin(); +} + +/*! + Moves the iterator to the back of the QScriptValue (after the + last property). + + \sa toFront(), previous() +*/ +void QScriptValueIterator::toBack() +{ + Q_D(QScriptValueIterator); + if (!d) + return; + d->ensureInitialized(); + d->it = d->propertyNames.end(); +} + +/*! + Returns the name of the last property that was jumped over using + next() or previous(). + + \sa value(), flags() +*/ +QString QScriptValueIterator::name() const +{ + Q_D(const QScriptValueIterator); + if (!d || !d->initialized || !d->engine()) + return QString(); + return d->current->ustring(); +} + +/*! + \since 4.4 + + Returns the name of the last property that was jumped over using + next() or previous(). +*/ +QScriptString QScriptValueIterator::scriptName() const +{ + Q_D(const QScriptValueIterator); + if (!d || !d->initialized || !d->engine()) + return QScriptString(); + return d->engine()->toStringHandle(*d->current); +} + +/*! + Returns the value of the last property that was jumped over using + next() or previous(). + + \sa setValue(), name() +*/ +QScriptValue QScriptValueIterator::value() const +{ + Q_D(const QScriptValueIterator); + if (!d || !d->initialized || !d->engine()) + return QScriptValue(); + QScript::APIShim shim(d->engine()); + JSC::JSValue jsValue = d->object()->property(*d->current); + return d->engine()->scriptValueFromJSCValue(jsValue); +} + +/*! + Sets the \a value of the last property that was jumped over using + next() or previous(). + + \sa value(), name() +*/ +void QScriptValueIterator::setValue(const QScriptValue &value) +{ + Q_D(QScriptValueIterator); + if (!d || !d->initialized || !d->engine()) + return; + QScript::APIShim shim(d->engine()); + JSC::JSValue jsValue = d->engine()->scriptValueToJSCValue(value); + d->object()->setProperty(*d->current, jsValue); +} + +/*! + Returns the flags of the last property that was jumped over using + next() or previous(). + + \sa value() +*/ +QScriptValue::PropertyFlags QScriptValueIterator::flags() const +{ + Q_D(const QScriptValueIterator); + if (!d || !d->initialized || !d->engine()) + return 0; + QScript::APIShim shim(d->engine()); + return d->object()->propertyFlags(*d->current); +} + +/*! + Removes the last property that was jumped over using next() + or previous(). + + \sa setValue() +*/ +void QScriptValueIterator::remove() +{ + Q_D(QScriptValueIterator); + if (!d || !d->initialized || !d->engine()) + return; + QScript::APIShim shim(d->engine()); + d->object()->setProperty(*d->current, JSC::JSValue()); + d->propertyNames.erase(d->current); +} + +/*! + Makes the iterator operate on \a object. The iterator is set to be + at the front of the sequence of properties (before the first + property). +*/ +QScriptValueIterator& QScriptValueIterator::operator=(QScriptValue &object) +{ + d_ptr.reset(); + if (object.isObject()) { + d_ptr.reset(new QScriptValueIteratorPrivate()); + d_ptr->objectValue = object; + } + return *this; +} + +QT_END_NAMESPACE diff --git a/src/script/api/qscriptvalueiterator.h b/src/script/api/qscriptvalueiterator.h new file mode 100644 index 0000000..d3dd987 --- /dev/null +++ b/src/script/api/qscriptvalueiterator.h @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** 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 QSCRIPTVALUEITERATOR_H +#define QSCRIPTVALUEITERATOR_H + +#include <QtScript/qscriptvalue.h> + +#include <QtCore/qscopedpointer.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Script) + +class QString; +class QScriptString; + +class QScriptValueIteratorPrivate; +class Q_SCRIPT_EXPORT QScriptValueIterator +{ +public: + QScriptValueIterator(const QScriptValue &value); + ~QScriptValueIterator(); + + bool hasNext() const; + void next(); + + bool hasPrevious() const; + void previous(); + + QString name() const; + QScriptString scriptName() const; + + QScriptValue value() const; + void setValue(const QScriptValue &value); + + QScriptValue::PropertyFlags flags() const; + + void remove(); + + void toFront(); + void toBack(); + + QScriptValueIterator& operator=(QScriptValue &value); + +private: + QScopedPointer<QScriptValueIteratorPrivate> d_ptr; + + Q_DECLARE_PRIVATE(QScriptValueIterator) + Q_DISABLE_COPY(QScriptValueIterator) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QSCRIPTVALUEITERATOR_H |