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