1 : /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=8 sw=4 et tw=78:
3 : *
4 : * ***** BEGIN LICENSE BLOCK *****
5 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1 *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Original Code is Mozilla Communicator client code, released
17 : * March 31, 1998.
18 : *
19 : * The Initial Developer of the Original Code is
20 : * Netscape Communications Corporation.
21 : * Portions created by the Initial Developer are Copyright (C) 1998
22 : * the Initial Developer. All Rights Reserved.
23 : *
24 : * Contributor(s):
25 : *
26 : * Alternatively, the contents of this file may be used under the terms of
27 : * either of the GNU General Public License Version 2 or later (the "GPL"),
28 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 : * in which case the provisions of the GPL or the LGPL are applicable instead
30 : * of those above. If you wish to allow use of your version of this file only
31 : * under the terms of either the GPL or the LGPL, and not to allow others to
32 : * use your version of this file under the terms of the MPL, indicate your
33 : * decision by deleting the provisions above and replace them with the notice
34 : * and other provisions required by the GPL or the LGPL. If you do not delete
35 : * the provisions above, a recipient may use your version of this file under
36 : * the terms of any one of the MPL, the GPL or the LGPL.
37 : *
38 : * ***** END LICENSE BLOCK ***** */
39 :
40 : #ifndef jsiter_h___
41 : #define jsiter_h___
42 :
43 : /*
44 : * JavaScript iterators.
45 : */
46 : #include "jscntxt.h"
47 : #include "jsprvtd.h"
48 : #include "jspubtd.h"
49 : #include "jsversion.h"
50 :
51 : #include "gc/Barrier.h"
52 : #include "vm/Stack.h"
53 :
54 : /*
55 : * For cacheable native iterators, whether the iterator is currently active.
56 : * Not serialized by XDR.
57 : */
58 : #define JSITER_ACTIVE 0x1000
59 : #define JSITER_UNREUSABLE 0x2000
60 :
61 : namespace js {
62 :
63 : struct NativeIterator {
64 : HeapPtrObject obj;
65 : HeapPtr<JSFlatString> *props_array;
66 : HeapPtr<JSFlatString> *props_cursor;
67 : HeapPtr<JSFlatString> *props_end;
68 : const Shape **shapes_array;
69 : uint32_t shapes_length;
70 : uint32_t shapes_key;
71 : uint32_t flags;
72 : JSObject *next; /* Forms cx->enumerators list, garbage otherwise. */
73 :
74 47768440 : bool isKeyIter() const { return (flags & JSITER_FOREACH) == 0; }
75 :
76 17203 : inline HeapPtr<JSFlatString> *begin() const {
77 17203 : return props_array;
78 : }
79 :
80 28801 : inline HeapPtr<JSFlatString> *end() const {
81 28801 : return props_end;
82 : }
83 :
84 18 : size_t numKeys() const {
85 18 : return end() - begin();
86 : }
87 :
88 13498559 : HeapPtr<JSFlatString> *current() const {
89 13498559 : JS_ASSERT(props_cursor < props_end);
90 13498559 : return props_cursor;
91 : }
92 :
93 13494832 : void incCursor() {
94 13494832 : props_cursor = props_cursor + 1;
95 13494832 : }
96 :
97 : static NativeIterator *allocateIterator(JSContext *cx, uint32_t slength,
98 : const js::AutoIdVector &props);
99 : void init(JSObject *obj, unsigned flags, uint32_t slength, uint32_t key);
100 :
101 : void mark(JSTracer *trc);
102 : };
103 :
104 : class ElementIteratorObject : public JSObject {
105 : public:
106 : enum {
107 : TargetSlot,
108 : IndexSlot,
109 : NumSlots
110 : };
111 :
112 : static JSObject *create(JSContext *cx, JSObject *target);
113 :
114 : inline uint32_t getIndex() const;
115 : inline void setIndex(uint32_t index);
116 : inline JSObject *getTargetObject() const;
117 :
118 : /*
119 : Array iterators are like this:
120 :
121 : Array.prototype[iterate] = function () {
122 : for (var i = 0; i < (this.length >>> 0); i++) {
123 : var desc = Object.getOwnPropertyDescriptor(this, i);
124 : yield desc === undefined ? undefined : this[i];
125 : }
126 : }
127 :
128 : This has the following implications:
129 :
130 : - Array iterators are generic; Array.prototype[iterate] can be transferred to
131 : any other object to create iterators over it.
132 :
133 : - The next() method of an Array iterator is non-reentrant. Trying to reenter,
134 : e.g. by using it on an object with a length getter that calls it.next() on
135 : the same iterator, causes a TypeError.
136 :
137 : - The iterator fetches obj.length every time its next() method is called.
138 :
139 : - The iterator converts obj.length to a whole number using ToUint32. As a
140 : consequence the iterator can't go on forever; it can yield at most 2^32-1
141 : values. Then i will be 0xffffffff, and no possible length value will be
142 : greater than that.
143 :
144 : - The iterator does not skip "array holes". When it encounters a hole, it
145 : yields undefined.
146 :
147 : - The iterator never consults the prototype chain.
148 :
149 : - If an element has a getter which throws, the exception is propagated, and
150 : the iterator is closed (that is, all future calls to next() will simply
151 : throw StopIteration).
152 :
153 : Note that if next() were reentrant, even more details of its inner
154 : workings would be observable.
155 : */
156 :
157 : /*
158 : * If there are any more elements to visit, store the value of the next
159 : * element in *vp, increment the index, and return true. If not, call
160 : * vp->setMagic(JS_NO_ITER_VALUE) and return true. Return false on error.
161 : */
162 : bool iteratorNext(JSContext *cx, Value *vp);
163 : };
164 :
165 : bool
166 : VectorToIdArray(JSContext *cx, js::AutoIdVector &props, JSIdArray **idap);
167 :
168 : bool
169 : GetIterator(JSContext *cx, JSObject *obj, unsigned flags, js::Value *vp);
170 :
171 : bool
172 : VectorToKeyIterator(JSContext *cx, JSObject *obj, unsigned flags, js::AutoIdVector &props, js::Value *vp);
173 :
174 : bool
175 : VectorToValueIterator(JSContext *cx, JSObject *obj, unsigned flags, js::AutoIdVector &props, js::Value *vp);
176 :
177 : /*
178 : * Creates either a key or value iterator, depending on flags. For a value
179 : * iterator, performs value-lookup to convert the given list of jsids.
180 : */
181 : bool
182 : EnumeratedIdVectorToIterator(JSContext *cx, JSObject *obj, unsigned flags, js::AutoIdVector &props, js::Value *vp);
183 :
184 : /*
185 : * Convert the value stored in *vp to its iteration object. The flags should
186 : * contain JSITER_ENUMERATE if js::ValueToIterator is called when enumerating
187 : * for-in semantics are required, and when the caller can guarantee that the
188 : * iterator will never be exposed to scripts.
189 : */
190 : extern JSBool
191 : ValueToIterator(JSContext *cx, unsigned flags, js::Value *vp);
192 :
193 : extern bool
194 : CloseIterator(JSContext *cx, JSObject *iterObj);
195 :
196 : extern bool
197 : UnwindIteratorForException(JSContext *cx, JSObject *obj);
198 :
199 : extern void
200 : UnwindIteratorForUncatchableException(JSContext *cx, JSObject *obj);
201 :
202 : }
203 :
204 : extern bool
205 : js_SuppressDeletedProperty(JSContext *cx, JSObject *obj, jsid id);
206 :
207 : extern bool
208 : js_SuppressDeletedElement(JSContext *cx, JSObject *obj, uint32_t index);
209 :
210 : extern bool
211 : js_SuppressDeletedElements(JSContext *cx, JSObject *obj, uint32_t begin, uint32_t end);
212 :
213 : /*
214 : * IteratorMore() indicates whether another value is available. It might
215 : * internally call iterobj.next() and then cache the value until its
216 : * picked up by IteratorNext(). The value is cached in the current context.
217 : */
218 : extern JSBool
219 : js_IteratorMore(JSContext *cx, JSObject *iterobj, js::Value *rval);
220 :
221 : extern JSBool
222 : js_IteratorNext(JSContext *cx, JSObject *iterobj, js::Value *rval);
223 :
224 : extern JSBool
225 : js_ThrowStopIteration(JSContext *cx);
226 :
227 : namespace js {
228 :
229 : /*
230 : * Get the next value from an iterator object.
231 : *
232 : * On success, store the next value in *vp and return true; if there are no
233 : * more values, store the magic value JS_NO_ITER_VALUE in *vp and return true.
234 : */
235 : inline bool
236 4203 : Next(JSContext *cx, JSObject *iter, Value *vp)
237 : {
238 4203 : if (!js_IteratorMore(cx, iter, vp))
239 9 : return false;
240 4194 : if (vp->toBoolean())
241 3969 : return js_IteratorNext(cx, iter, vp);
242 225 : vp->setMagic(JS_NO_ITER_VALUE);
243 225 : return true;
244 : }
245 :
246 : /*
247 : * Imitate a for-of loop. This does the equivalent of the JS code:
248 : *
249 : * for (let v of iterable)
250 : * op(v);
251 : *
252 : * But the actual signature of op must be:
253 : * bool op(JSContext *cx, const Value &v);
254 : *
255 : * There is no feature like JS 'break'. op must return false only
256 : * in case of exception or error.
257 : */
258 : template <class Op>
259 : bool
260 369 : ForOf(JSContext *cx, const Value &iterable, Op op)
261 : {
262 369 : Value iterv(iterable);
263 369 : if (!ValueToIterator(cx, JSITER_FOR_OF, &iterv))
264 99 : return false;
265 270 : JSObject *iter = &iterv.toObject();
266 :
267 270 : bool ok = true;
268 4518 : while (ok) {
269 : Value v;
270 4203 : ok = Next(cx, iter, &v);
271 4203 : if (ok) {
272 4194 : if (v.isMagic(JS_NO_ITER_VALUE))
273 225 : break;
274 3969 : ok = op(cx, v);
275 : }
276 : }
277 :
278 270 : bool throwing = !ok && cx->isExceptionPending();
279 : Value exc;
280 270 : if (throwing) {
281 45 : exc = cx->getPendingException();
282 45 : cx->clearPendingException();
283 : }
284 270 : bool closedOK = CloseIterator(cx, iter);
285 270 : if (throwing && closedOK)
286 45 : cx->setPendingException(exc);
287 270 : return ok && closedOK;
288 : }
289 :
290 : } /* namespace js */
291 :
292 : #if JS_HAS_GENERATORS
293 :
294 : /*
295 : * Generator state codes.
296 : */
297 : typedef enum JSGeneratorState {
298 : JSGEN_NEWBORN, /* not yet started */
299 : JSGEN_OPEN, /* started by a .next() or .send(undefined) call */
300 : JSGEN_RUNNING, /* currently executing via .next(), etc., call */
301 : JSGEN_CLOSING, /* close method is doing asynchronous return */
302 : JSGEN_CLOSED /* closed, cannot be started or closed again */
303 : } JSGeneratorState;
304 :
305 : struct JSGenerator {
306 : js::HeapPtrObject obj;
307 : JSGeneratorState state;
308 : js::FrameRegs regs;
309 : JSObject *enumerators;
310 : js::StackFrame *floating;
311 : js::HeapValue floatingStack[1];
312 :
313 214794 : js::StackFrame *floatingFrame() {
314 214794 : return floating;
315 : }
316 :
317 59987 : js::StackFrame *liveFrame() {
318 59987 : JS_ASSERT((state == JSGEN_RUNNING || state == JSGEN_CLOSING) ==
319 119974 : (regs.fp() != floatingFrame()));
320 59987 : return regs.fp();
321 : }
322 : };
323 :
324 : extern JSObject *
325 : js_NewGenerator(JSContext *cx);
326 :
327 : /*
328 : * Generator stack frames do not have stable pointers since they get copied to
329 : * and from the generator object and the stack (see SendToGenerator). This is a
330 : * problem for Block and With objects, which need to store a pointer to the
331 : * enclosing stack frame. The solution is for Block and With objects to store
332 : * a pointer to the "floating" stack frame stored in the generator object,
333 : * since it is stable, and maintain, in the generator object, a pointer to the
334 : * "live" stack frame (either a copy on the stack or the floating frame). Thus,
335 : * Block and With objects must "normalize" to and from the floating/live frames
336 : * in the case of generators using the following functions.
337 : */
338 : inline js::StackFrame *
339 87329 : js_FloatingFrameIfGenerator(JSContext *cx, js::StackFrame *fp)
340 : {
341 87329 : if (JS_UNLIKELY(fp->isGeneratorFrame()))
342 1175 : return cx->generatorFor(fp)->floatingFrame();
343 86154 : return fp;
344 : }
345 :
346 : /* Given a floating frame, given the JSGenerator containing it. */
347 : extern JSGenerator *
348 : js_FloatingFrameToGenerator(js::StackFrame *fp);
349 :
350 : inline js::StackFrame *
351 551063 : js_LiveFrameIfGenerator(js::StackFrame *fp)
352 : {
353 551063 : return fp->isGeneratorFrame() ? js_FloatingFrameToGenerator(fp)->liveFrame() : fp;
354 : }
355 :
356 : #endif
357 :
358 : extern JSObject *
359 : js_InitIteratorClasses(JSContext *cx, JSObject *obj);
360 :
361 : #endif /* jsiter_h___ */
|