зеркало из https://github.com/mozilla/pjs.git
Bug 349331: Implementation of generator.close now uses asynchronous return instead of GeneratorExit exception. r=brendan
This commit is contained in:
Родитель
e0fb05bac8
Коммит
735380688b
|
@ -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}")
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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++;
|
||||
|
|
|
@ -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:;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 *
|
||||
|
|
Загрузка…
Ссылка в новой задаче