Skip to content

Commit fd5307a

Browse files
author
Paul Davis
committed
'for each' iteration support in JavaScript.
Added the 'for each(var v in obj)' semantics to iteration. [#1 state:resolved]
1 parent 9eafe19 commit fd5307a

File tree

2 files changed

+110
-9
lines changed

2 files changed

+110
-9
lines changed

spidermonkey/pyiter.c

Lines changed: 98 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,18 +47,47 @@ finalize(JSContext* jscx, JSObject* jsobj)
4747
JSBool
4848
call(JSContext* jscx, JSObject* jsobj, uintN argc, jsval* argv, jsval* rval)
4949
{
50+
jsval objval = argv[-2];
51+
JSObject* obj = JSVAL_TO_OBJECT(objval);
52+
53+
if(argc >= 1 && JSVAL_IS_BOOLEAN(argv[0]) && !JSVAL_TO_BOOLEAN(argv[0]))
54+
{
55+
if(!JS_SetReservedSlot(jscx, obj, 2, JSVAL_TRUE))
56+
{
57+
JS_ReportError(jscx, "Failed to reset iterator flag.");
58+
return JS_FALSE;
59+
}
60+
}
61+
5062
*rval = argv[-2];
5163
return JS_TRUE;
5264
}
5365

66+
JSBool
67+
is_for_each(JSContext* cx, JSObject* obj, JSBool* rval)
68+
{
69+
jsval slot;
70+
if(!JS_GetReservedSlot(cx, obj, 2, &slot))
71+
{
72+
return JS_FALSE;
73+
}
74+
75+
if(!JSVAL_IS_BOOLEAN(slot)) return JS_FALSE;
76+
*rval = JSVAL_TO_BOOLEAN(slot);
77+
return JS_TRUE;
78+
}
79+
5480
JSBool
5581
def_next(JSContext* jscx, JSObject* jsobj, uintN argc, jsval* argv, jsval* rval)
5682
{
5783
Context* pycx = NULL;
84+
PyObject* pyobj = NULL;
5885
PyObject* iter = NULL;
5986
PyObject* next = NULL;
87+
PyObject* value = NULL;
6088
JSBool ret = JS_FALSE;
61-
89+
JSBool foreach = JS_FALSE;
90+
6291
// For StopIteration throw
6392
JSObject* glbl = JS_GetGlobalObject(jscx);
6493
jsval exc = JSVAL_VOID;
@@ -77,6 +106,13 @@ def_next(JSContext* jscx, JSObject* jsobj, uintN argc, jsval* argv, jsval* rval)
77106
goto done;
78107
}
79108

109+
pyobj = get_js_slot(jscx, jsobj, 0);
110+
if(pyobj == NULL)
111+
{
112+
JS_ReportError(jscx, "Failed to find iterated object.");
113+
goto done;
114+
}
115+
80116
next = PyIter_Next(iter);
81117
if(next == NULL && PyErr_Occurred())
82118
{
@@ -95,11 +131,33 @@ def_next(JSContext* jscx, JSObject* jsobj, uintN argc, jsval* argv, jsval* rval)
95131
goto done;
96132
}
97133

98-
*rval = py2js(pycx, next);
134+
if(!is_for_each(jscx, jsobj, &foreach))
135+
{
136+
JS_ReportError(jscx, "Failed to get iterator flag.");
137+
goto done;
138+
}
139+
140+
if(PyMapping_Check(pyobj) && foreach)
141+
{
142+
fprintf(stderr, "IS FOR EACH ON MAPPING\n");
143+
value = PyObject_GetItem(pyobj, next);
144+
if(value == NULL)
145+
{
146+
JS_ReportError(jscx, "Failed to get value in 'for each'");
147+
goto done;
148+
}
149+
*rval = py2js(pycx, value);
150+
}
151+
else
152+
{
153+
*rval = py2js(pycx, next);
154+
}
155+
99156
if(*rval != JSVAL_VOID) ret = JS_TRUE;
100157

101158
done:
102159
Py_XDECREF(next);
160+
Py_XDECREF(value);
103161
return ret;
104162
}
105163

@@ -110,8 +168,9 @@ seq_next(JSContext* jscx, JSObject* jsobj, uintN argc, jsval* argv, jsval* rval)
110168
PyObject* pyobj = NULL;
111169
PyObject* iter = NULL;
112170
PyObject* next = NULL;
113-
JSObject* jsiter = NULL;
171+
PyObject* value = NULL;
114172
JSBool ret = JS_FALSE;
173+
JSBool foreach = JS_FALSE;
115174
long maxval = -1;
116175
long currval = -1;
117176

@@ -171,19 +230,41 @@ seq_next(JSContext* jscx, JSObject* jsobj, uintN argc, jsval* argv, jsval* rval)
171230
goto done;
172231
}
173232

174-
*rval = py2js(pycx, iter);
233+
if(!is_for_each(jscx, jsobj, &foreach))
234+
{
235+
JS_ReportError(jscx, "Failed to get iterator flag.");
236+
goto done;
237+
}
238+
239+
if(foreach)
240+
{
241+
fprintf(stderr, "IS FOR EACH ON SEQUENCE\n");
242+
value = PyObject_GetItem(pyobj, iter);
243+
if(value == NULL)
244+
{
245+
JS_ReportError(jscx, "Failed to get array element in 'for each'");
246+
goto done;
247+
}
248+
*rval = py2js(pycx, value);
249+
}
250+
else
251+
{
252+
*rval = py2js(pycx, iter);
253+
}
254+
175255
next = iter;
176256
if(*rval != JSVAL_VOID) ret = JS_TRUE;
177257

178258
done:
179259
Py_XDECREF(next);
260+
Py_XDECREF(value);
180261
return ret;
181262
}
182263

183264
static JSClass
184265
js_iter_class = {
185266
"PyJSIteratorClass",
186-
JSCLASS_HAS_RESERVED_SLOTS(2),
267+
JSCLASS_HAS_RESERVED_SLOTS(3),
187268
JS_PropertyStub,
188269
JS_PropertyStub,
189270
JS_PropertyStub,
@@ -264,6 +345,12 @@ new_py_def_iter(Context* cx, PyObject* obj, jsval* rval)
264345
goto error;
265346
}
266347

348+
if(!JS_SetReservedSlot(cx->cx, jsiter, 2, JSVAL_FALSE))
349+
{
350+
PyErr_SetString(PyExc_RuntimeError, "Failed to store iterator flag.");
351+
goto error;
352+
}
353+
267354
Py_INCREF(cx);
268355
*rval = OBJECT_TO_JSVAL(jsiter);
269356
ret = JS_TRUE;
@@ -317,6 +404,12 @@ new_py_seq_iter(Context* cx, PyObject* obj, jsval* rval)
317404
goto error;
318405
}
319406

407+
if(!JS_SetReservedSlot(cx->cx, jsiter, 2, JSVAL_FALSE))
408+
{
409+
PyErr_SetString(PyExc_RuntimeError, "Failed to store iterator flag.");
410+
goto error;
411+
}
412+
320413
Py_INCREF(cx);
321414
*rval = OBJECT_TO_JSVAL(jsiter);
322415
ret = JS_TRUE;

tests/test-iterate.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,27 @@
44
# under the MIT license.
55
import t
66

7-
js_script = """
7+
js_for_script = """
88
var ret = [];
99
for(var v in data) {ret.push(v);}
1010
ret;
1111
"""
1212

13+
js_for_each_script = """
14+
var ret = [];
15+
for each(var v in data) {ret.push(v);}
16+
ret;
17+
"""
18+
1319
@t.glbl("data", {"foo": "bar", "baz": "bam"})
1420
def test_iter_py_map(cx, glbl):
15-
t.eq(cx.execute(js_script), ["foo", "baz"])
21+
t.eq(cx.execute(js_for_script), ["foo", "baz"])
22+
t.eq(cx.execute(js_for_each_script), ["bar", "bam"])
1623

17-
@t.glbl("data", ["a", "b", "c"])
24+
@t.glbl("data", ["a", 2, "zing!"])
1825
def test_iter_py_array(cx, glbl):
19-
t.eq(cx.execute(js_script), [0, 1, 2])
26+
t.eq(cx.execute(js_for_script), [0, 1, 2])
27+
t.eq(cx.execute(js_for_each_script), ["a", 2, "zing!"])
2028

2129
@t.cx()
2230
def test_iter_js_object(cx):

0 commit comments

Comments
 (0)