1/****************************************************************************
2**
3** Copyright (C) 2015 The Qt Company Ltd.
4** Contact: http://www.qt.io/licensing/
5**
6** This file is part of the QtScript module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "config.h"
41#include "qscriptvalueiterator.h"
42
43#include "qscriptstring.h"
44#include "qscriptengine.h"
45#include "qscriptengine_p.h"
46#include "qscriptvalue_p.h"
47
48#include "JSObject.h"
49#include "PropertyNameArray.h"
50#include "JSArray.h"
51#include "JSFunction.h"
52
53#include <list>
54
55QT_BEGIN_NAMESPACE
56
57/*!
58 \since 4.3
59 \class QScriptValueIterator
60 \inmodule QtScript
61 \brief The QScriptValueIterator class provides a Java-style iterator for QScriptValue.
62
63 \ingroup script
64
65
66 The QScriptValueIterator constructor takes a QScriptValue as
67 argument. After construction, the iterator is located at the very
68 beginning of the sequence of properties. Here's how to iterate over
69 all the properties of a QScriptValue:
70
71 \snippet code/src_script_qscriptvalueiterator.cpp 0
72
73 The next() advances the iterator. The name(), value() and flags()
74 functions return the name, value and flags of the last item that was
75 jumped over.
76
77 If you want to remove properties as you iterate over the
78 QScriptValue, use remove(). If you want to modify the value of a
79 property, use setValue().
80
81 Note that QScriptValueIterator only iterates over the QScriptValue's
82 own properties; i.e. it does not follow the prototype chain. You can
83 use a loop like this to follow the prototype chain:
84
85 \snippet code/src_script_qscriptvalueiterator.cpp 1
86
87 Note that QScriptValueIterator will not automatically skip over
88 properties that have the QScriptValue::SkipInEnumeration flag set;
89 that flag only affects iteration in script code. If you want, you
90 can skip over such properties with code like the following:
91
92 \snippet code/src_script_qscriptvalueiterator.cpp 2
93
94 \sa QScriptValue::property()
95*/
96
97class QScriptValueIteratorPrivate
98{
99public:
100 QScriptValueIteratorPrivate()
101 : initialized(false)
102 {}
103
104 ~QScriptValueIteratorPrivate()
105 {
106 if (!initialized)
107 return;
108 QScriptEnginePrivate *eng_p = engine();
109 if (!eng_p)
110 return;
111 QScript::APIShim shim(eng_p);
112 propertyNames.clear(); //destroying the identifiers need to be done under the APIShim guard
113 }
114
115 QScriptValuePrivate *object() const
116 {
117 return QScriptValuePrivate::get(q: objectValue);
118 }
119
120 QScriptEnginePrivate *engine() const
121 {
122 return QScriptEnginePrivate::get(q: objectValue.engine());
123 }
124
125 void ensureInitialized()
126 {
127 if (initialized)
128 return;
129 QScriptEnginePrivate *eng_p = engine();
130 QScript::APIShim shim(eng_p);
131 JSC::ExecState *exec = eng_p->globalExec();
132 JSC::PropertyNameArray propertyNamesArray(exec);
133 JSC::asObject(value: object()->jscValue)->getOwnPropertyNames(exec, propertyNamesArray, JSC::IncludeDontEnumProperties);
134
135 JSC::PropertyNameArray::const_iterator propertyNamesIt = propertyNamesArray.begin();
136 for(; propertyNamesIt != propertyNamesArray.end(); ++propertyNamesIt) {
137 propertyNames.push_back(x: *propertyNamesIt);
138 }
139 it = propertyNames.begin();
140 initialized = true;
141 }
142
143 QScriptValue objectValue;
144 std::list<JSC::Identifier> propertyNames;
145 std::list<JSC::Identifier>::iterator it;
146 std::list<JSC::Identifier>::iterator current;
147 bool initialized;
148};
149
150/*!
151 Constructs an iterator for traversing \a object. The iterator is
152 set to be at the front of the sequence of properties (before the
153 first property).
154*/
155QScriptValueIterator::QScriptValueIterator(const QScriptValue &object)
156 : d_ptr(0)
157{
158 if (object.isObject()) {
159 d_ptr.reset(other: new QScriptValueIteratorPrivate());
160 d_ptr->objectValue = object;
161 }
162}
163
164/*!
165 Destroys the iterator.
166*/
167QScriptValueIterator::~QScriptValueIterator()
168{
169}
170
171/*!
172 Returns true if there is at least one item ahead of the iterator
173 (i.e. the iterator is \e not at the back of the property sequence);
174 otherwise returns false.
175
176 \sa next(), hasPrevious()
177*/
178bool QScriptValueIterator::hasNext() const
179{
180 Q_D(const QScriptValueIterator);
181 if (!d || !d->engine())
182 return false;
183
184 const_cast<QScriptValueIteratorPrivate*>(d)->ensureInitialized();
185 return d->it != d->propertyNames.end();
186}
187
188/*!
189 Advances the iterator by one position.
190
191 Calling this function on an iterator located at the back of the
192 container leads to undefined results.
193
194 \sa hasNext(), previous(), name()
195*/
196void QScriptValueIterator::next()
197{
198 Q_D(QScriptValueIterator);
199 if (!d)
200 return;
201 d->ensureInitialized();
202
203 d->current = d->it;
204 ++(d->it);
205}
206
207/*!
208 Returns true if there is at least one item behind the iterator
209 (i.e. the iterator is \e not at the front of the property sequence);
210 otherwise returns false.
211
212 \sa previous(), hasNext()
213*/
214bool QScriptValueIterator::hasPrevious() const
215{
216 Q_D(const QScriptValueIterator);
217 if (!d || !d->engine())
218 return false;
219
220 const_cast<QScriptValueIteratorPrivate*>(d)->ensureInitialized();
221 return d->it != d->propertyNames.begin();
222}
223
224/*!
225 Moves the iterator back by one position.
226
227 Calling this function on an iterator located at the front of the
228 container leads to undefined results.
229
230 \sa hasPrevious(), next(), name()
231*/
232void QScriptValueIterator::previous()
233{
234 Q_D(QScriptValueIterator);
235 if (!d)
236 return;
237 d->ensureInitialized();
238 --(d->it);
239 d->current = d->it;
240}
241
242/*!
243 Moves the iterator to the front of the QScriptValue (before the
244 first property).
245
246 \sa toBack(), next()
247*/
248void QScriptValueIterator::toFront()
249{
250 Q_D(QScriptValueIterator);
251 if (!d)
252 return;
253 d->ensureInitialized();
254 d->it = d->propertyNames.begin();
255}
256
257/*!
258 Moves the iterator to the back of the QScriptValue (after the
259 last property).
260
261 \sa toFront(), previous()
262*/
263void QScriptValueIterator::toBack()
264{
265 Q_D(QScriptValueIterator);
266 if (!d)
267 return;
268 d->ensureInitialized();
269 d->it = d->propertyNames.end();
270}
271
272/*!
273 Returns the name of the last property that was jumped over using
274 next() or previous().
275
276 \sa value(), flags()
277*/
278QString QScriptValueIterator::name() const
279{
280 Q_D(const QScriptValueIterator);
281 if (!d || !d->initialized || !d->engine())
282 return QString();
283 return d->current->ustring();
284}
285
286/*!
287 \since 4.4
288
289 Returns the name of the last property that was jumped over using
290 next() or previous().
291*/
292QScriptString QScriptValueIterator::scriptName() const
293{
294 Q_D(const QScriptValueIterator);
295 if (!d || !d->initialized || !d->engine())
296 return QScriptString();
297 return d->engine()->toStringHandle(name: *d->current);
298}
299
300/*!
301 Returns the value of the last property that was jumped over using
302 next() or previous().
303
304 \sa setValue(), name()
305*/
306QScriptValue QScriptValueIterator::value() const
307{
308 Q_D(const QScriptValueIterator);
309 if (!d || !d->initialized || !d->engine())
310 return QScriptValue();
311 QScript::APIShim shim(d->engine());
312 JSC::JSValue jsValue = d->object()->property(id: *d->current);
313 return d->engine()->scriptValueFromJSCValue(value: jsValue);
314}
315
316/*!
317 Sets the \a value of the last property that was jumped over using
318 next() or previous().
319
320 \sa value(), name()
321*/
322void QScriptValueIterator::setValue(const QScriptValue &value)
323{
324 Q_D(QScriptValueIterator);
325 if (!d || !d->initialized || !d->engine())
326 return;
327 QScript::APIShim shim(d->engine());
328 JSC::JSValue jsValue = d->engine()->scriptValueToJSCValue(value);
329 d->object()->setProperty(id: *d->current, value: jsValue);
330}
331
332/*!
333 Returns the flags of the last property that was jumped over using
334 next() or previous().
335
336 \sa value()
337*/
338QScriptValue::PropertyFlags QScriptValueIterator::flags() const
339{
340 Q_D(const QScriptValueIterator);
341 if (!d || !d->initialized || !d->engine())
342 return {};
343 QScript::APIShim shim(d->engine());
344 return d->object()->propertyFlags(id: *d->current);
345}
346
347/*!
348 Removes the last property that was jumped over using next()
349 or previous().
350
351 \sa setValue()
352*/
353void QScriptValueIterator::remove()
354{
355 Q_D(QScriptValueIterator);
356 if (!d || !d->initialized || !d->engine())
357 return;
358 QScript::APIShim shim(d->engine());
359 d->object()->setProperty(id: *d->current, JSC::JSValue());
360 d->propertyNames.erase(position: d->current);
361}
362
363/*!
364 Makes the iterator operate on \a object. The iterator is set to be
365 at the front of the sequence of properties (before the first
366 property).
367*/
368QScriptValueIterator& QScriptValueIterator::operator=(QScriptValue &object)
369{
370 d_ptr.reset();
371 if (object.isObject()) {
372 d_ptr.reset(other: new QScriptValueIteratorPrivate());
373 d_ptr->objectValue = object;
374 }
375 return *this;
376}
377
378QT_END_NAMESPACE
379

source code of qtscript/src/script/api/qscriptvalueiterator.cpp