1 /****************************************************************************
3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 ** This file is part of the QtScript module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
10 ** This file contains pre-release code and may not be distributed.
11 ** You may use this file in accordance with the terms and conditions
12 ** contained in the either Technology Preview License Agreement or the
13 ** Beta Release License Agreement.
15 ** GNU Lesser General Public License Usage
16 ** Alternatively, this file may be used under the terms of the GNU Lesser
17 ** General Public License version 2.1 as published by the Free Software
18 ** Foundation and appearing in the file LICENSE.LGPL included in the
19 ** packaging of this file. Please review the following information to
20 ** ensure the GNU Lesser General Public License version 2.1 requirements
21 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23 ** In addition, as a special exception, Nokia gives you certain
24 ** additional rights. These rights are described in the Nokia Qt LGPL
25 ** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
28 ** GNU General Public License Usage
29 ** Alternatively, this file may be used under the terms of the GNU
30 ** General Public License version 3.0 as published by the Free Software
31 ** Foundation and appearing in the file LICENSE.GPL included in the
32 ** packaging of this file. Please review the following information to
33 ** ensure the GNU General Public License version 3.0 requirements will be
34 ** met: http://www.gnu.org/copyleft/gpl.html.
36 ** If you are unsure which license is appropriate for your use, please
37 ** contact the sales department at http://www.qtsoftware.com/contact.
40 ****************************************************************************/
42 #include "qscriptclass.h"
46 #include <QtCore/qstringlist.h>
48 #include "qscriptclasspropertyiterator.h"
49 #include "qscriptstring.h"
50 #include "qscriptstring_p.h"
51 #include "qscriptclass_p.h"
52 #include "qscriptclassinfo_p.h"
53 #include "qscriptengine_p.h"
54 #include "qscriptcontext_p.h"
55 #include "qscriptvalueimpl_p.h"
56 #include "qscriptmember_p.h"
57 #include "qscriptobject_p.h"
58 #include "qscriptfunction_p.h"
60 Q_DECLARE_METATYPE(QScriptContext
*)
61 Q_DECLARE_METATYPE(QScriptValueList
)
69 \brief The QScriptClass class provides an interface for defining custom behavior of (a class of) Qt Script objects.
74 The QScriptClass class defines an interface for handling various
75 aspects of interaction with the Qt Script objects associated with
76 the class. Such objects are created by calling
77 QScriptEngine::newObject(), passing a pointer to the QScriptClass as
80 By subclassing QScriptClass, you can define precisely how access to
81 properties of the objects that use your class is handled. This
82 enables a fully dynamic handling of properties, e.g. it's more
83 powerful than QScriptEngine::newQObject(). For example, you can use
84 QScriptClass to implement array-type objects (i.e. objects that
85 handle the \c{length} property, and properties whose names are valid
86 array indexes, in a special way), or to implement a "live"
87 (runtime-defined) proxy to an underlying object.
89 If you just need to handle access to a set of properties that are
90 known at the time an object is created (i.e. "semi-statically"), you
91 might consider using QScriptValue::setProperty() to define
92 getter/setter functions for the relevant properties, rather than
93 subclassing QScriptClass.
95 Reimplement queryProperty() to specify which properties are handled
96 in a custom way by your script class (i.e. should be
97 \bold{delegated} to the QScriptClass), and which properties should
98 be handled just like normal Qt Script object properties.
100 Reimplement property() and setProperty() to perform the actual
101 access (read or write) to the properties that your class
102 handles. Additionally, you can reimplement propertyFlags() to
103 specify custom flags for your properties.
105 Reimplement newIterator() to provide an iterator for objects of your
106 custom class. This is only necessary if objects of your class can
107 have custom properties that you want to be reported when an object
108 is used together with the QScriptValueIterator class, or when an
109 object is used in a for-in enumeration statement in a script.
111 When implementing custom classes of objects, you typically use
112 QScriptValue::setData() to store instance-specific data as part of
113 object initialization; the data won't be accessible from scripts
114 directly, but you can access it in e.g. your reimplementations of
115 property() and setProperty() (by calling QScriptValue::data()) to
116 perform custom processing.
118 Reimplement prototype() to provide a custom prototype object for
121 Reimplement supportsExtension() and extension() if your custom
122 script class supports one or more of the extensions specified by the
125 \sa QScriptClassPropertyIterator, QScriptEngine::newObject(), {Custom Script Class Example}
129 \enum QScriptClass::Extension
131 This enum specifies the possible extensions to a QScriptClass.
133 \value Callable Instances of this class can be called as functions.
135 \value HasInstance Instances of this class implement [[HasInstance]].
141 \enum QScriptClass::QueryFlag
143 This enum describes flags that are used to query a QScriptClass
144 regarding how access to a property should be handled.
146 \value HandlesReadAccess The QScriptClass handles read access to this property.
147 \value HandlesWriteAccess The QScriptClass handles write access to this property.
152 class QScriptCustomClassData
: public QScriptClassData
155 QScriptCustomClassData(QScriptClass
*klass
);
156 ~QScriptCustomClassData();
158 virtual void mark(const QScriptValueImpl
&object
, int generation
);
159 virtual bool resolve(const QScriptValueImpl
&object
, QScriptNameIdImpl
*nameId
,
160 QScript::Member
*member
, QScriptValueImpl
*base
,
161 QScript::AccessMode access
);
162 virtual bool get(const QScriptValueImpl
&obj
, const QScript::Member
&m
,
163 QScriptValueImpl
*result
);
164 virtual bool put(QScriptValueImpl
*object
, const QScript::Member
&member
,
165 const QScriptValueImpl
&value
);
166 virtual bool removeMember(const QScriptValueImpl
&object
,
167 const QScript::Member
&member
);
168 virtual bool implementsHasInstance(const QScriptValueImpl
&object
);
169 virtual bool hasInstance(const QScriptValueImpl
&object
,
170 const QScriptValueImpl
&value
);
171 virtual QScriptClassDataIterator
*newIterator(const QScriptValueImpl
&object
);
173 QScriptClass
*scriptClass() const;
176 QScriptClass
*m_class
;
179 class QScriptCustomClassDataIterator
: public QScriptClassDataIterator
182 QScriptCustomClassDataIterator(const QScriptValueImpl
&object
,
183 QScriptClass
*klass
);
184 virtual ~QScriptCustomClassDataIterator();
186 virtual bool hasNext() const;
187 virtual void next(QScript::Member
*member
);
189 virtual bool hasPrevious() const;
190 virtual void previous(QScript::Member
*member
);
192 virtual void toFront();
193 virtual void toBack();
196 void iteratorToMember(QScript::Member
*member
);
198 QScriptClassPropertyIterator
*m_it
;
201 QScriptCustomClassData::QScriptCustomClassData(QScriptClass
*klass
)
206 QScriptCustomClassData::~QScriptCustomClassData()
210 void QScriptCustomClassData::mark(const QScriptValueImpl
&, int)
214 bool QScriptCustomClassData::resolve(const QScriptValueImpl
&object
, QScriptNameIdImpl
*nameId
,
215 QScript::Member
*member
, QScriptValueImpl
*base
,
216 QScript::AccessMode access
)
219 QScriptClass::QueryFlags queryIn
= 0;
220 if (access
& QScript::Read
)
221 queryIn
|= QScriptClass::HandlesReadAccess
;
222 if (access
& QScript::Write
)
223 queryIn
|= QScriptClass::HandlesWriteAccess
;
224 QScriptEnginePrivate
*eng
= object
.engine();
225 QScriptString str
= eng
->internedString(nameId
);
226 QScriptClass::QueryFlags queryOut
;
227 queryOut
= m_class
->queryProperty(eng
->toPublic(object
), str
, queryIn
, &id
);
228 if (queryOut
& queryIn
) {
231 QScriptValue::PropertyFlags flags
= m_class
->propertyFlags(eng
->toPublic(object
), str
, id
);
232 member
->native(nameId
, id
, flags
);
238 bool QScriptCustomClassData::get(const QScriptValueImpl
&object
, const QScript::Member
&member
,
239 QScriptValueImpl
*result
)
241 QScriptEnginePrivate
*eng
= object
.engine();
242 QScriptString str
= eng
->internedString(member
.nameId());
243 *result
= eng
->toImpl(m_class
->property(eng
->toPublic(object
), str
, member
.id()));
244 if (!result
->isValid())
245 *result
= eng
->undefinedValue();
249 bool QScriptCustomClassData::put(QScriptValueImpl
*object
, const QScript::Member
&member
,
250 const QScriptValueImpl
&value
)
252 QScriptEnginePrivate
*eng
= object
->engine();
253 QScriptString str
= eng
->internedString(member
.nameId());
254 QScriptValue publicObject
= eng
->toPublic(*object
);
255 m_class
->setProperty(publicObject
, str
, member
.id(), eng
->toPublic(value
));
259 bool QScriptCustomClassData::removeMember(const QScriptValueImpl
&object
,
260 const QScript::Member
&member
)
262 QScriptEnginePrivate
*eng
= object
.engine();
263 QScriptString str
= eng
->internedString(member
.nameId());
264 QScriptValue publicObject
= eng
->toPublic(object
);
265 m_class
->setProperty(publicObject
, str
, member
.id(), QScriptValue());
269 bool QScriptCustomClassData::implementsHasInstance(const QScriptValueImpl
&object
)
271 if (object
.classInfo() != QScriptClassPrivate::get(m_class
)->classInfo())
273 return m_class
->supportsExtension(QScriptClass::HasInstance
);
276 bool QScriptCustomClassData::hasInstance(const QScriptValueImpl
&object
,
277 const QScriptValueImpl
&value
)
279 QScriptEnginePrivate
*eng
= object
.engine();
280 QScriptValueList arguments
;
281 arguments
<< eng
->toPublic(object
) << eng
->toPublic(value
);
282 QVariant ret
= m_class
->extension(QScriptClass::HasInstance
, qVariantFromValue(arguments
));
286 QScriptClassDataIterator
*QScriptCustomClassData::newIterator(const QScriptValueImpl
&object
)
288 return new QScriptCustomClassDataIterator(object
, m_class
);
291 QScriptClass
*QScriptCustomClassData::scriptClass() const
298 QScriptCustomClassDataIterator::QScriptCustomClassDataIterator(const QScriptValueImpl
&object
,
301 QScriptEnginePrivate
*eng
= object
.engine();
302 m_it
= klass
->newIterator(eng
->toPublic(object
));
305 QScriptCustomClassDataIterator::~QScriptCustomClassDataIterator()
313 bool QScriptCustomClassDataIterator::hasNext() const
315 return m_it
&& m_it
->hasNext();
318 void QScriptCustomClassDataIterator::next(QScript::Member
*member
)
322 iteratorToMember(member
);
326 bool QScriptCustomClassDataIterator::hasPrevious() const
328 return m_it
&& m_it
->hasPrevious();
331 void QScriptCustomClassDataIterator::previous(QScript::Member
*member
)
335 iteratorToMember(member
);
339 void QScriptCustomClassDataIterator::toFront()
345 void QScriptCustomClassDataIterator::toBack()
351 void QScriptCustomClassDataIterator::iteratorToMember(QScript::Member
*member
)
353 QScriptString str
= m_it
->name();
354 QScriptNameIdImpl
*nameId
= 0;
356 nameId
= QScriptStringPrivate::get(str
)->nameId
;
357 member
->native(nameId
, m_it
->id(), m_it
->flags());
362 QScriptClassPrivate::QScriptClassPrivate(QScriptClass
*q
)
363 : engine(0), m_classInfo(0), q_ptr(q
)
367 QScriptClassPrivate::~QScriptClassPrivate()
370 // classInfo is owned by engine
371 // set the data to the normal Object class data
372 delete m_classInfo
->data();
373 QScriptEnginePrivate
*eng_p
= QScriptEnginePrivate::get(engine
);
374 m_classInfo
->setData(eng_p
->m_class_object
->data());
378 QScriptClassPrivate
*QScriptClassPrivate::get(QScriptClass
*klass
)
380 return klass
->d_func();
383 QScriptClassInfo
*QScriptClassPrivate::classInfo()
388 QScriptEnginePrivate
*eng_p
= QScriptEnginePrivate::get(engine
);
389 int classType
= eng_p
->registerCustomClassType();
390 if (q
->supportsExtension(QScriptClass::Callable
))
391 classType
|= QScriptClassInfo::FunctionBased
;
392 QString name
= q
->name();
394 name
= QLatin1String("Object");
395 m_classInfo
= eng_p
->registerClass(name
, classType
);
396 m_classInfo
->setData(new QScriptCustomClassData(q_func()));
400 QScriptClass
*QScriptClassPrivate::classFromInfo(QScriptClassInfo
*info
)
402 QScriptCustomClassData
*data
= static_cast<QScriptCustomClassData
*>(info
->data());
404 return data
->scriptClass();
407 static QScriptValueImpl
callScriptClassFunction(QScriptContextPrivate
*ctx
,
408 QScriptEnginePrivate
*eng
,
409 QScriptClassInfo
*classInfo
)
411 qMetaTypeId
<QScriptContext
*>();
412 if (QScriptClassData
*data
= classInfo
->data()) {
413 QScriptCustomClassData
*customData
= static_cast<QScriptCustomClassData
*>(data
);
414 QScriptClass
*klass
= customData
->scriptClass();
415 QVariant arg
= qVariantFromValue(QScriptContextPrivate::get(ctx
));
416 QVariant ret
= klass
->extension(QScriptClass::Callable
, arg
);
417 QScriptValueImpl val
= eng
->valueFromVariant(ret
);
421 return eng
->undefinedValue();
424 QScriptFunction
*QScriptClassPrivate::newFunction()
426 return new QScript::C2Function(callScriptClassFunction
, /*length=*/0,
427 classInfo(), /*name=*/QString());
431 Constructs a QScriptClass object to be used in the given \a engine.
433 The engine does not take ownership of the QScriptClass object.
435 QScriptClass::QScriptClass(QScriptEngine
*engine
)
436 : d_ptr(new QScriptClassPrivate(this))
438 d_ptr
->engine
= engine
;
444 QScriptClass::QScriptClass(QScriptEngine
*engine
, QScriptClassPrivate
&dd
)
448 d_ptr
->engine
= engine
;
452 Destroys the QScriptClass object.
454 If a QScriptClass object is deleted before the associated engine(),
455 any Qt Script objects using the QScriptClass will be "demoted" to
456 normal Qt Script objects.
458 QScriptClass::~QScriptClass()
465 Returns the engine that this QScriptClass is associated with.
467 QScriptEngine
*QScriptClass::engine() const
469 Q_D(const QScriptClass
);
474 Returns the object to be used as the prototype of new instances
475 of this class (created with QScriptEngine::newObject()).
477 The default implementation returns an invalid QScriptValue, meaning
478 that the standard Object prototype will be used. Reimplement this
479 function to provide your own custom prototype.
481 Typically you initialize your prototype object in the constructor of
482 your class, then return it in this function.
484 See the "Making Use of Prototype-Based Inheritance" section in the
485 QtScript documentation for more information on how prototypes are
488 QScriptValue
QScriptClass::prototype() const
490 return QScriptValue();
494 Returns the name of the script class.
496 Qt Script uses this name to generate a default string representation
497 of objects in case you do not provide a toString function.
499 The default implementation returns a null string.
501 QString
QScriptClass::name() const
507 Queries this script class for how access to the property with the
508 given \a name of the given \a object should be handled. The given \a
509 flags specify the aspects of interest. This function should return a
510 subset of \a flags to indicate which aspects of property access
511 should be further handled by the script class.
513 For example, if the \a flags contain HandlesReadAccess, and you
514 would like your class to handle the reading of the property (through
515 the property() function), the returned flags should include
516 HandlesReadAccess. If the returned flags do not contain
517 HandlesReadAccess, the property will be handled as a normal script
520 You can optionally use the \a id argument to store a value that will
521 subsequently be passed on to functions such as property() and
524 The default implementation of this function returns 0.
526 Note: This function is only called if the given property isn't
527 already a normal property of the object. For example, say you
528 advertise that you want to handle read access to property \c{foo},
529 but not write access; if \c{foo} is then assigned a value, it will
530 become a normal script object property, and subsequently you will no
531 longer be queried regarding read access to \c{foo}.
535 QScriptClass::QueryFlags
QScriptClass::queryProperty(
536 const QScriptValue
&object
, const QScriptString
&name
,
537 QueryFlags flags
, uint
*id
)
547 Returns the value of the property with the given \a name of the given
550 The \a id argument is only useful if you assigned a value to it in
553 The default implementation does nothing and returns an invalid QScriptValue.
555 \sa setProperty(), propertyFlags()
557 QScriptValue
QScriptClass::property(const QScriptValue
&object
,
558 const QScriptString
&name
, uint id
)
563 return QScriptValue();
567 Returns the flags of the property with the given \a name of the given
570 The \a id argument is only useful if you assigned a value to it in
573 The default implementation returns 0.
577 QScriptValue::PropertyFlags
QScriptClass::propertyFlags(
578 const QScriptValue
&object
, const QScriptString
&name
, uint id
)
587 Sets the property with the given \a name of the given \a object to
590 The \a id argument is only useful if you assigned a value to it in
593 The default implementation does nothing.
595 An invalid \a value represents a request to remove the property.
599 void QScriptClass::setProperty(QScriptValue
&object
, const QScriptString
&name
,
600 uint id
, const QScriptValue
&value
)
609 Returns an iterator for traversing custom properties of the given \a
612 The default implementation returns 0, meaning that there are no
613 custom properties to traverse.
615 Reimplement this function if objects of your script class can have
616 one or more custom properties (e.g. those reported to be handled by
617 queryProperty()) that you want to appear when an object's properties
618 are enumerated (e.g. by a for-in statement in a script).
620 Qt Script takes ownership of the new iterator object.
622 \sa QScriptValueIterator
624 QScriptClassPropertyIterator
*QScriptClass::newIterator(const QScriptValue
&object
)
631 Returns true if the QScriptClass supports the given \a extension;
632 otherwise, false is returned. By default, no extensions
635 Reimplement this function to indicate which extensions your custom
640 bool QScriptClass::supportsExtension(Extension extension
) const
647 This virtual function can be reimplemented in a QScriptClass
648 subclass to provide support for extensions. The optional \a argument
649 can be provided as input to the \a extension; the result must be
650 returned in the form of a QVariant. You can call supportsExtension()
651 to check if an extension is supported by the QScriptClass. By
652 default, no extensions are supported, and this function returns an
655 If you implement the Callable extension, Qt Script will call this
656 function when an instance of your class is called as a function
657 (e.g. from a script or using QScriptValue::call()). The \a argument
658 will contain a pointer to the QScriptContext that represents the
659 function call, and you should return a QVariant that holds the
660 result of the function call. In the following example the sum of the
661 arguments to the script function are added up and returned:
663 \snippet doc/src/snippets/code/src_script_qscriptclass.cpp 0
665 If you implement the HasInstance extension, Qt Script will call this
666 function as part of evaluating the \c{instanceof} operator, as
667 described in ECMA-262 Section 11.8.6. The \a argument is a
668 QScriptValueList containing two items: The first item is the object
669 that HasInstance is being applied to (an instance of your class),
670 and the second item can be any value. extension() should return true
671 if the value delegates behavior to the object, false otherwise.
673 \sa supportsExtension()
675 QVariant
QScriptClass::extension(Extension extension
, const QVariant
&argument
)
684 #endif // QT_NO_SCRIPT