Bug 349331: Implementation of generator.close now uses asynchronous return instead of GeneratorExit exception. r=brendan

This commit is contained in:
igor.bukanov%gmail.com 2006-08-26 20:24:45 +00:00
parent 45337945bf
commit 747b5e9fff
8 changed files with 94 additions and 80 deletions

View File

@ -254,7 +254,7 @@ MSG_DEF(JSMSG_BAD_XML_CHARACTER, 171, 0, JSEXN_SYNTAXERR, "illegal XML char
MSG_DEF(JSMSG_BAD_DEFAULT_XML_NAMESPACE,172,0,JSEXN_SYNTAXERR, "invalid default XML namespace")
MSG_DEF(JSMSG_BAD_XML_NAME_SYNTAX, 173, 0, JSEXN_SYNTAXERR, "invalid XML name")
MSG_DEF(JSMSG_BRACKET_AFTER_ATTR_EXPR,174, 0, JSEXN_SYNTAXERR, "missing ] after attribute expression")
MSG_DEF(JSMSG_BAD_GENERATOR_EXIT, 175, 1, JSEXN_TYPEERR, "GeneratorExit ignored by generator {0}")
MSG_DEF(JSMSG_NESTING_GENERATOR, 175, 1, JSEXN_TYPEERR, "already executing generator {0}")
MSG_DEF(JSMSG_CURLY_IN_XML_EXPR, 176, 0, JSEXN_SYNTAXERR, "missing } in XML expression")
MSG_DEF(JSMSG_BAD_XML_NAMESPACE, 177, 1, JSEXN_TYPEERR, "invalid XML namespace {0}")
MSG_DEF(JSMSG_BAD_XML_ATTR_NAME, 178, 1, JSEXN_TYPEERR, "invalid XML attribute name {0}")
@ -294,4 +294,3 @@ MSG_DEF(JSMSG_IN_AFTER_FOR_NAME, 211, 0, JSEXN_SYNTAXERR, "missing in after
MSG_DEF(JSMSG_BAD_ITERATOR_RETURN, 212, 2, JSEXN_TYPEERR, "{0}.{1} returned a primitive value")
MSG_DEF(JSMSG_KEYWORD_NOT_NS, 213, 0, JSEXN_SYNTAXERR, "keyword is used as namespace")
MSG_DEF(JSMSG_BAD_GENERATOR_YIELD, 214, 1, JSEXN_TYPEERR, "yield from closing generator {0}")
MSG_DEF(JSMSG_NESTING_GENERATOR, 215, 1, JSEXN_TYPEERR, "already executing generator {0}")

View File

@ -1367,7 +1367,6 @@ static JSStdName standard_class_names[] = {
#if JS_HAS_GENERATORS
{js_InitIteratorClasses, EAGER_ATOM_AND_CLASP(Iterator)},
{js_InitIteratorClasses, EAGER_ATOM_AND_CLASP(Generator)},
{js_InitIteratorClasses, EAGER_ATOM_AND_CLASP(GeneratorExit)},
#endif
{NULL, 0, NULL, NULL}

View File

@ -6377,3 +6377,40 @@ js_FinishTakingTryNotes(JSContext *cx, JSCodeGenerator *cg, JSTryNote *notes)
notes[count].length = CG_OFFSET(cg);
notes[count].catchStart = 0;
}
#if JS_HAS_GENERATORS
jsbytecode *
js_FindFinallyHandler(JSScript *script, jsbytecode *pc)
{
JSTryNote *tn;
ptrdiff_t off;
tn = script->trynotes;
if (!tn)
return NULL;
off = pc - script->main;
if (off < 0)
return NULL;
JS_ASSERT(tn->catchStart != 0);
do {
if ((jsuword)(off - tn->start) < (jsuword)tn->length) {
/*
* We have a handler, is it finally?
*
* Catch bytecode begins with: JSOP_SETSP JSOP_ENTERBLOCK
* Finally bytecode begins with: JSOP_SETSP JSOP_EXCEPTION
*/
pc = script->main + tn->catchStart;
JS_ASSERT(*pc == JSOP_SETSP);
if (pc[js_CodeSpec[JSOP_SETSP].length] == JSOP_EXCEPTION)
return pc;
JS_ASSERT(pc[js_CodeSpec[JSOP_SETSP].length] == JSOP_ENTERBLOCK);
}
} while ((++tn)->catchStart != 0);
return NULL;
}
#endif

View File

@ -933,17 +933,24 @@ FindAndMarkObjectsToClose(JSContext *cx, JSGCInvocationKind gckind)
if (*js_GetGCThingFlags(gen->obj) & GCF_MARK) {
genp = &gen->next;
} else {
/*
* Only generators that yielded or were already closed can become
* unreachable while still on reachableList.
*/
JS_ASSERT(gen->state == JSGEN_OPEN || gen->state == JSGEN_CLOSED);
*genp = gen->next;
gen->next = NULL;
if (gen->state != JSGEN_CLOSED) {
if (gen->state == JSGEN_OPEN &&
js_FindFinallyHandler(gen->frame.script, gen->frame.pc)) {
/*
* Generator cannot be nesting, i.e., running or closing, and
* newborn generator is never registered with GC.
* Generator yielded inside a try with a finally block.
* Schedule it for closing.
*
* XXX: we do need to run the close hook if the last yield
* happened outside a try block.
* We keep generators that yielded outside try-with-finally
* with gen->state == JSGEN_OPEN. The finalizer must deal with
* open generators as we may skip the close hooks, see below.
*/
JS_ASSERT(gen->state == JSGEN_OPEN);
*rt->gcCloseState.todoTail = gen;
rt->gcCloseState.todoTail = &gen->next;
rt->gcCloseState.todoCount++;

View File

@ -6214,13 +6214,30 @@ out:
/*
* Look for a try block in script that can catch this exception.
*/
SCRIPT_FIND_CATCH_START(script, pc, pc);
if (pc) {
/* Don't clear cx->throwing to save cx->exception from GC. */
len = 0;
ok = JS_TRUE;
DO_NEXT_OP(len);
#if JS_HAS_GENERATORS
if (JS_LIKELY(cx->exception != JSVAL_ARETURN)) {
SCRIPT_FIND_CATCH_START(script, pc, pc);
if (!pc)
goto no_catch;
} else {
pc = js_FindFinallyHandler(script, pc);
if (!pc) {
cx->throwing = JS_FALSE;
ok = JS_TRUE;
fp->rval = JSVAL_VOID;
goto no_catch;
}
}
#else
SCRIPT_FIND_CATCH_START(script, pc, pc);
if (!pc)
goto no_catch;
#endif
/* Don't clear cx->throwing to save cx->exception from GC. */
len = 0;
ok = JS_TRUE;
DO_NEXT_OP(len);
}
no_catch:;
}

View File

@ -560,27 +560,6 @@ JSClass js_StopIterationClass = {
NULL, NULL
};
static JSBool
genexit_hasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp)
{
*bp = !JSVAL_IS_PRIMITIVE(v) &&
OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(v)) == &js_GeneratorExitClass;
return JS_TRUE;
}
JSClass js_GeneratorExitClass = {
js_GeneratorExit_str,
JSCLASS_HAS_CACHED_PROTO(JSProto_GeneratorExit),
JS_PropertyStub, JS_PropertyStub,
JS_PropertyStub, JS_PropertyStub,
JS_EnumerateStub, JS_ResolveStub,
JS_ConvertStub, JS_FinalizeStub,
NULL, NULL,
NULL, NULL,
NULL, genexit_hasInstance,
NULL, NULL
};
JSBool
js_ThrowStopIteration(JSContext *cx, JSObject *obj)
{
@ -746,14 +725,10 @@ static JSBool
SendToGenerator(JSContext *cx, JSGeneratorOp op, JSObject *obj,
JSGenerator *gen, jsval arg, jsval *rval)
{
jsval genexit;
JSStackFrame *fp;
jsval junk;
JSArena *arena;
JSBool ok;
jsval exn;
JSClass *clasp;
JSString *str;
JS_ASSERT(gen->state == JSGEN_NEWBORN || gen->state == JSGEN_OPEN);
switch (op) {
@ -776,11 +751,7 @@ SendToGenerator(JSContext *cx, JSGeneratorOp op, JSObject *obj,
default:
JS_ASSERT(op == JSGENOP_CLOSE);
if (!js_FindClassObject(cx, NULL, INT_TO_JSID(JSProto_GeneratorExit),
&genexit)) {
return JS_FALSE;
}
JS_SetPendingException(cx, genexit);
JS_SetPendingException(cx, JSVAL_ARETURN);
gen->state = JSGEN_CLOSING;
break;
}
@ -815,33 +786,9 @@ SendToGenerator(JSContext *cx, JSGeneratorOp op, JSObject *obj,
return js_ThrowStopIteration(cx, obj);
}
if (op == JSGENOP_CLOSE && cx->throwing) {
/*
* Generator terminated with an exception. Clear if it is a normal
* exit signal.
*/
exn = cx->exception;
if (!JSVAL_IS_PRIMITIVE(exn)) {
clasp = OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(exn));
if (clasp == &js_GeneratorExitClass ||
clasp == &js_StopIterationClass) {
JS_ClearPendingException(cx);
return JS_TRUE;
}
}
str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK,
OBJECT_TO_JSVAL(obj), NULL);
if (str) {
JS_ReportErrorNumberUC(cx, js_GetErrorMessage, NULL,
JSMSG_BAD_GENERATOR_EXIT,
JSSTRING_CHARS(str));
}
return JS_FALSE;
}
/*
* An error, silent termination by branch callback or an exception thrown
* in resposnse to next|send|throw. Propagate the condition to the caller.
* An error, silent termination by branch callback or an exception.
* Propagate the condition to the caller.
*/
return JS_FALSE;
}
@ -1052,13 +999,6 @@ js_InitIteratorClasses(JSContext *cx, JSObject *obj)
}
#endif
#if JS_HAS_GENERATORS
if (!JS_InitClass(cx, obj, NULL, &js_GeneratorExitClass, NULL, 0,
NULL, NULL, NULL, NULL)) {
return NULL;
}
#endif
return JS_InitClass(cx, obj, NULL, &js_StopIterationClass, NULL, 0,
NULL, NULL, NULL, NULL);
}

View File

@ -101,7 +101,7 @@ typedef enum JSGeneratorState {
JSGEN_NEWBORN, /* not yet started */
JSGEN_OPEN, /* started by a .next() or .send(undefined) call */
JSGEN_RUNNING, /* currently executing via .next(), etc., call */
JSGEN_CLOSING, /* close method is doing .send(GeneratorExit) */
JSGEN_CLOSING, /* close method is doing asynchronous return */
JSGEN_CLOSED /* closed, cannot be started or closed again */
} JSGeneratorState;
@ -123,12 +123,19 @@ js_NewGenerator(JSContext *cx, JSStackFrame *fp);
extern JSBool
js_CloseGeneratorObject(JSContext *cx, JSGenerator *gen);
/*
* Special unique value to implement asynchronous return for
* generator.close(). Scripts never see it.
*/
#define JSVAL_ARETURN BOOLEAN_TO_JSVAL(JS_TRUE + 1)
#endif
extern JSClass js_GeneratorClass;
extern JSClass js_IteratorClass;
extern JSClass js_StopIterationClass;
extern JSClass js_GeneratorExitClass;
extern JSObject *
js_InitIteratorClasses(JSContext *cx, JSObject *obj);

View File

@ -97,6 +97,14 @@ struct JSScript {
catchpc = catchpc_; \
JS_END_MACRO
/*
* Find the innermost finally block that handles the given pc. This is a
* version of SCRIPT_FIND_CATCH_START that ignore catch blocks and is used
* to implement generator.close().
*/
jsbytecode *
js_FindFinallyHandler(JSScript *script, jsbytecode *pc);
extern JS_FRIEND_DATA(JSClass) js_ScriptClass;
extern JSObject *