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 | |
55 | QT_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 | |
97 | class QScriptValueIteratorPrivate |
98 | { |
99 | public: |
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 | */ |
155 | QScriptValueIterator::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 | */ |
167 | QScriptValueIterator::~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 | */ |
178 | bool 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 | */ |
196 | void 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 | */ |
214 | bool 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 | */ |
232 | void 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 | */ |
248 | void 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 | */ |
263 | void 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 | */ |
278 | QString 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 | */ |
292 | QScriptString 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 | */ |
306 | QScriptValue 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 | */ |
322 | void 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 | */ |
338 | QScriptValue::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 | */ |
353 | void 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 | */ |
368 | QScriptValueIterator& 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 | |
378 | QT_END_NAMESPACE |
379 | |