Ensure that iterators are closed when an exception is thrown (bug 729797, r=luke).

--HG--
extra : rebase_source : a500065bf2c96d1b8581fc8ae81af2eac0ee880f
This commit is contained in:
David Anderson 2012-02-27 17:01:52 -08:00
Родитель 3662f3e0c7
Коммит 01b10c0163
6 изменённых файлов: 42 добавлений и 30 удалений

Просмотреть файл

@ -1967,7 +1967,7 @@ BEGIN_CASE(JSOP_ITER)
{
JS_ASSERT(regs.sp > regs.fp()->base());
uint8_t flags = GET_UINT8(regs.pc);
if (!js_ValueToIterator(cx, flags, &regs.sp[-1]))
if (!ValueToIterator(cx, flags, &regs.sp[-1]))
goto error;
CHECK_INTERRUPT_HANDLER();
JS_ASSERT(!regs.sp[-1].isPrimitive());
@ -2001,7 +2001,7 @@ END_CASE(JSOP_ITERNEXT)
BEGIN_CASE(JSOP_ENDITER)
{
JS_ASSERT(regs.sp - 1 >= regs.fp()->base());
bool ok = !!js_CloseIterator(cx, &regs.sp[-1].toObject());
bool ok = CloseIterator(cx, &regs.sp[-1].toObject());
regs.sp--;
if (!ok)
goto error;
@ -4330,13 +4330,10 @@ END_CASE(JSOP_ARRAYPUSH)
case JSTRY_ITER: {
/* This is similar to JSOP_ENDITER in the interpreter loop. */
JS_ASSERT(JSOp(*regs.pc) == JSOP_ENDITER);
Value v = cx->getPendingException();
cx->clearPendingException();
bool ok = js_CloseIterator(cx, &regs.sp[-1].toObject());
bool ok = UnwindIteratorForException(cx, &regs.sp[-1].toObject());
regs.sp -= 1;
if (!ok)
goto error;
cx->setPendingException(v);
}
}
} while (++tn != tnlimit);

Просмотреть файл

@ -829,7 +829,7 @@ Iterator(JSContext *cx, uintN argc, Value *vp)
bool keyonly = argc >= 2 ? js_ValueToBoolean(argv[1]) : false;
uintN flags = JSITER_OWNONLY | (keyonly ? 0 : (JSITER_FOREACH | JSITER_KEYVALUE));
*vp = argc >= 1 ? argv[0] : UndefinedValue();
return js_ValueToIterator(cx, flags, vp);
return ValueToIterator(cx, flags, vp);
}
JSBool
@ -871,12 +871,19 @@ static JSFunctionSpec iterator_methods[] = {
JS_FS_END
};
#if JS_HAS_GENERATORS
static JSBool
CloseGenerator(JSContext *cx, JSObject *genobj);
#endif
namespace js {
/*
* Call ToObject(v).__iterator__(keyonly) if ToObject(v).__iterator__ exists.
* Otherwise construct the default iterator.
*/
JS_FRIEND_API(JSBool)
js_ValueToIterator(JSContext *cx, uintN flags, Value *vp)
JSBool
ValueToIterator(JSContext *cx, uintN flags, Value *vp)
{
/* JSITER_KEYVALUE must always come with JSITER_FOREACH */
JS_ASSERT_IF(flags & JSITER_KEYVALUE, flags & JSITER_FOREACH);
@ -914,13 +921,8 @@ js_ValueToIterator(JSContext *cx, uintN flags, Value *vp)
return GetIterator(cx, obj, flags, vp);
}
#if JS_HAS_GENERATORS
static JSBool
CloseGenerator(JSContext *cx, JSObject *genobj);
#endif
JS_FRIEND_API(JSBool)
js_CloseIterator(JSContext *cx, JSObject *obj)
bool
CloseIterator(JSContext *cx, JSObject *obj)
{
cx->iterValue.setMagic(JS_NO_ITER_VALUE);
@ -950,6 +952,19 @@ js_CloseIterator(JSContext *cx, JSObject *obj)
return JS_TRUE;
}
bool
UnwindIteratorForException(JSContext *cx, JSObject *obj)
{
Value v = cx->getPendingException();
cx->clearPendingException();
if (!CloseIterator(cx, obj))
return false;
cx->setPendingException(v);
return true;
}
} // namespace js
/*
* Suppress enumeration of deleted properties. This function must be called
* when a property is deleted and there might be active enumerators.

Просмотреть файл

@ -181,19 +181,22 @@ VectorToValueIterator(JSContext *cx, JSObject *obj, uintN flags, js::AutoIdVecto
bool
EnumeratedIdVectorToIterator(JSContext *cx, JSObject *obj, uintN flags, js::AutoIdVector &props, js::Value *vp);
}
/*
* Convert the value stored in *vp to its iteration object. The flags should
* contain JSITER_ENUMERATE if js_ValueToIterator is called when enumerating
* for-in semantics are required, and when the caller can guarantee that the
* iterator will never be exposed to scripts.
*/
extern JS_FRIEND_API(JSBool)
js_ValueToIterator(JSContext *cx, uintN flags, js::Value *vp);
extern JSBool
ValueToIterator(JSContext *cx, uintN flags, js::Value *vp);
extern JS_FRIEND_API(JSBool)
js_CloseIterator(JSContext *cx, JSObject *iterObj);
extern bool
CloseIterator(JSContext *cx, JSObject *iterObj);
extern bool
UnwindIteratorForException(JSContext *cx, JSObject *obj);
}
extern bool
js_SuppressDeletedProperty(JSContext *cx, JSObject *obj, jsid id);

Просмотреть файл

@ -656,7 +656,7 @@ struct AutoCloseIterator
{
AutoCloseIterator(JSContext *cx, JSObject *obj) : cx(cx), obj(obj) {}
~AutoCloseIterator() { if (obj) js_CloseIterator(cx, obj); }
~AutoCloseIterator() { if (obj) CloseIterator(cx, obj); }
void clear() { obj = NULL; }
@ -701,7 +701,7 @@ Reify(JSContext *cx, JSCompartment *origin, Value *vp)
}
close.clear();
if (!js_CloseIterator(cx, iterObj))
if (!CloseIterator(cx, iterObj))
return false;
if (isKeyIter)

Просмотреть файл

@ -151,14 +151,11 @@ top:
* adjustment and regs.sp[1] after, to save and restore the
* pending exception.
*/
Value v = cx->getPendingException();
JS_ASSERT(JSOp(*pc) == JSOP_ENDITER);
cx->clearPendingException();
bool ok = !!js_CloseIterator(cx, &cx->regs().sp[-1].toObject());
bool ok = UnwindIteratorForException(cx, &cx->regs().sp[-1].toObject());
cx->regs().sp -= 1;
if (!ok)
goto top;
cx->setPendingException(v);
}
}
}

Просмотреть файл

@ -1230,7 +1230,7 @@ stubs::GetPropNoCache(VMFrame &f, PropertyName *name)
void JS_FASTCALL
stubs::Iter(VMFrame &f, uint32_t flags)
{
if (!js_ValueToIterator(f.cx, flags, &f.regs.sp[-1]))
if (!ValueToIterator(f.cx, flags, &f.regs.sp[-1]))
THROW();
JS_ASSERT(!f.regs.sp[-1].isPrimitive());
}
@ -1305,7 +1305,7 @@ void JS_FASTCALL
stubs::EndIter(VMFrame &f)
{
JS_ASSERT(f.regs.sp - 1 >= f.fp()->base());
if (!js_CloseIterator(f.cx, &f.regs.sp[-1].toObject()))
if (!CloseIterator(f.cx, &f.regs.sp[-1].toObject()))
THROW();
}