2121
2222const char PyIterableProxyHandler::family = 0 ;
2323
24+ // TODO merge shared _next code
25+
2426bool PyIterableProxyHandler::iterable_next (JSContext *cx, unsigned argc, JS::Value *vp) {
2527 JS::CallArgs args = JS::CallArgsFromVp (argc, vp);
2628 JS::RootedObject thisObj (cx);
@@ -41,7 +43,7 @@ bool PyIterableProxyHandler::iterable_next(JSContext *cx, unsigned argc, JS::Val
4143 PyErr_Clear ();
4244 }
4345 else {
44- return NULL ;
46+ return false ;
4547 }
4648 }
4749
@@ -66,10 +68,139 @@ JSMethodDef PyIterableProxyHandler::iterable_methods[] = {
6668 {NULL , NULL , 0 }
6769};
6870
71+
72+ // IterableIterator
73+
74+ enum {
75+ IterableIteratorSlotIterableObject,
76+ IterableIteratorSlotCount
77+ };
78+
79+ static JSClass iterableIteratorClass = {" IterableIterator" , JSCLASS_HAS_RESERVED_SLOTS (IterableIteratorSlotCount)};
80+
81+ static bool iterator_next (JSContext *cx, unsigned argc, JS::Value *vp) {
82+ JS::CallArgs args = JS::CallArgsFromVp (argc, vp);
83+ JS::RootedObject thisObj (cx);
84+ if (!args.computeThis (cx, &thisObj)) return false ;
85+
86+ PyObject *it = JS::GetMaybePtrFromReservedSlot<PyObject>(thisObj, IterableIteratorSlotIterableObject);
87+
88+ JS::RootedObject result (cx, JS_NewPlainObject (cx));
89+
90+ PyObject *(*iternext)(PyObject *) = *Py_TYPE (it)->tp_iternext ;
91+
92+ PyObject *item = iternext (it);
93+
94+ if (item == NULL ) {
95+ if (PyErr_Occurred ()) {
96+ if (PyErr_ExceptionMatches (PyExc_StopIteration) ||
97+ PyErr_ExceptionMatches (PyExc_SystemError)) { // TODO this handles a result like SystemError: Objects/dictobject.c:1778: bad argument to internal function. Why are we getting that?
98+ PyErr_Clear ();
99+ }
100+ else {
101+ return false ;
102+ }
103+ }
104+
105+ JS::RootedValue done (cx, JS::BooleanValue (true ));
106+ if (!JS_SetProperty (cx, result, " done" , done)) return false ;
107+ args.rval ().setObject (*result);
108+ return result;
109+ }
110+
111+ JS::RootedValue done (cx, JS::BooleanValue (false ));
112+ if (!JS_SetProperty (cx, result, " done" , done)) return false ;
113+
114+ JS::RootedValue value (cx, jsTypeFactory (cx, item));
115+ if (!JS_SetProperty (cx, result, " value" , value)) return false ;
116+
117+ args.rval ().setObject (*result);
118+ return true ;
119+ }
120+
121+ static JSFunctionSpec iterable_iterator_methods[] = {
122+ JS_FN (" next" , iterator_next, 0 , JSPROP_ENUMERATE),
123+ JS_FS_END
124+ };
125+
126+ static bool IterableIteratorConstructor (JSContext *cx, unsigned argc, JS::Value *vp) {
127+ JS::CallArgs args = JS::CallArgsFromVp (argc, vp);
128+
129+ if (!args.isConstructing ()) {
130+ JS_ReportErrorASCII (cx, " You must call this constructor with 'new'" );
131+ return false ;
132+ }
133+
134+ JS::RootedObject thisObj (cx, JS_NewObjectForConstructor (cx, &iterableIteratorClass, args));
135+ if (!thisObj) {
136+ return false ;
137+ }
138+
139+ args.rval ().setObject (*thisObj);
140+ return true ;
141+ }
142+
143+ static bool DefineIterableIterator (JSContext *cx, JS::HandleObject global) {
144+ JS::RootedObject iteratorPrototype (cx);
145+ if (!JS_GetClassPrototype (cx, JSProto_Iterator, &iteratorPrototype)) {
146+ return false ;
147+ }
148+
149+ JS::RootedObject protoObj (cx,
150+ JS_InitClass (cx, global,
151+ nullptr , iteratorPrototype,
152+ " IterableIterator" ,
153+ IterableIteratorConstructor, 0 ,
154+ nullptr , iterable_iterator_methods,
155+ nullptr , nullptr )
156+ );
157+
158+ return protoObj; // != nullptr
159+ }
160+
161+ static bool iterable_values (JSContext *cx, unsigned argc, JS::Value *vp) {
162+ JS::CallArgs args = JS::CallArgsFromVp (argc, vp);
163+
164+ JS::RootedObject proxy (cx, JS::ToObject (cx, args.thisv ()));
165+ if (!proxy) {
166+ return false ;
167+ }
168+
169+ PyObject *self = JS::GetMaybePtrFromReservedSlot<PyObject>(proxy, PyObjectSlot);
170+
171+ JS::RootedObject global (cx, JS::GetNonCCWObjectGlobal (proxy));
172+
173+ JS::RootedValue constructor_val (cx);
174+ if (!JS_GetProperty (cx, global, " IterableIterator" , &constructor_val)) return false ;
175+ if (!constructor_val.isObject ()) {
176+ if (!DefineIterableIterator (cx, global)) {
177+ return false ;
178+ }
179+
180+ if (!JS_GetProperty (cx, global, " IterableIterator" , &constructor_val)) return false ;
181+ if (!constructor_val.isObject ()) {
182+ JS_ReportErrorASCII (cx, " IterableIterator is not a constructor" );
183+ return false ;
184+ }
185+ }
186+ JS::RootedObject constructor (cx, &constructor_val.toObject ());
187+
188+ JS::RootedObject obj (cx);
189+ if (!JS::Construct (cx, constructor_val, JS::HandleValueArray::empty (), &obj)) return false ;
190+ if (!obj) return false ;
191+
192+ JS::SetReservedSlot (obj, IterableIteratorSlotIterableObject, JS::PrivateValue ((void *)self));
193+
194+ args.rval ().setObject (*obj);
195+ return true ;
196+ }
197+
69198bool PyIterableProxyHandler::getOwnPropertyDescriptor (
70199 JSContext *cx, JS::HandleObject proxy, JS::HandleId id,
71200 JS::MutableHandle<mozilla::Maybe<JS::PropertyDescriptor>> desc
72201) const {
202+
203+ // see if we're calling a function
73204 if (id.isString ()) {
74205 for (size_t index = 0 ;; index++) {
75206 bool isThatFunction;
@@ -92,6 +223,24 @@ bool PyIterableProxyHandler::getOwnPropertyDescriptor(
92223 }
93224 }
94225
226+ // symbol property
227+ if (id.isSymbol ()) {
228+ JS::RootedSymbol rootedSymbol (cx, id.toSymbol ());
229+
230+ if (JS::GetSymbolCode (rootedSymbol) == JS::SymbolCode::iterator) {
231+ JSFunction *newFunction = JS_NewFunction (cx, iterable_values, 0 , 0 , NULL );
232+ if (!newFunction) return false ;
233+ JS::RootedObject funObj (cx, JS_GetFunctionObject (newFunction));
234+ desc.set (mozilla::Some (
235+ JS::PropertyDescriptor::Data (
236+ JS::ObjectValue (*funObj),
237+ {JS::PropertyAttribute::Enumerable}
238+ )
239+ ));
240+ return true ;
241+ }
242+ }
243+
95244 PyObject *attrName = idToKey (cx, id);
96245 PyObject *self = JS::GetMaybePtrFromReservedSlot<PyObject>(proxy, PyObjectSlot);
97246 PyObject *item = PyDict_GetItemWithError (self, attrName);
0 commit comments