📄 jsiter.c
字号:
if (!obj) return NULL; /* Load and compute stack slot counts. */ argc = fp->argc; nargs = JS_MAX(argc, fp->fun->nargs); nvars = fp->nvars; depth = fp->script->depth; nslots = 2 + nargs + nvars + 2 * depth; /* Allocate obj's private data struct. */ gen = (JSGenerator *) JS_malloc(cx, sizeof(JSGenerator) + (nslots - 1) * sizeof(jsval)); if (!gen) goto bad; gen->obj = obj; /* Steal away objects reflecting fp and point them at gen->frame. */ gen->frame.callobj = fp->callobj; if (fp->callobj) { JS_SetPrivate(cx, fp->callobj, &gen->frame); fp->callobj = NULL; } gen->frame.argsobj = fp->argsobj; if (fp->argsobj) { JS_SetPrivate(cx, fp->argsobj, &gen->frame); fp->argsobj = NULL; } /* These two references can be shared with fp until it goes away. */ gen->frame.varobj = fp->varobj; gen->frame.thisp = fp->thisp; /* Copy call-invariant script and function references. */ gen->frame.script = fp->script; gen->frame.fun = fp->fun; /* Use newsp to carve space out of gen->stack. */ newsp = gen->stack; gen->arena.next = NULL; gen->arena.base = (jsuword) newsp; gen->arena.limit = gen->arena.avail = (jsuword) (newsp + nslots);#define COPY_STACK_ARRAY(vec,cnt,num) \ JS_BEGIN_MACRO \ gen->frame.cnt = cnt; \ gen->frame.vec = newsp; \ newsp += (num); \ memcpy(gen->frame.vec, fp->vec, (num) * sizeof(jsval)); \ JS_END_MACRO /* Copy argv, rval, and vars. */ *newsp++ = fp->argv[-2]; *newsp++ = fp->argv[-1]; COPY_STACK_ARRAY(argv, argc, nargs); gen->frame.rval = fp->rval; COPY_STACK_ARRAY(vars, nvars, nvars);#undef COPY_STACK_ARRAY /* Initialize or copy virtual machine state. */ gen->frame.down = NULL; gen->frame.annotation = NULL; gen->frame.scopeChain = fp->scopeChain; gen->frame.pc = fp->pc; /* Allocate generating pc and operand stack space. */ gen->frame.spbase = gen->frame.sp = newsp + depth; /* Copy remaining state (XXX sharp* and xml* should be local vars). */ gen->frame.sharpDepth = 0; gen->frame.sharpArray = NULL; gen->frame.flags = fp->flags | JSFRAME_GENERATOR; gen->frame.dormantNext = NULL; gen->frame.xmlNamespace = NULL; gen->frame.blockChain = NULL; /* Note that gen is newborn. */ gen->state = JSGEN_NEWBORN; if (!JS_SetPrivate(cx, obj, gen)) { JS_free(cx, gen); goto bad; } /* * Register with GC to ensure that suspended finally blocks will be * executed. */ js_RegisterGenerator(cx, gen); return obj; bad: cx->weakRoots.newborn[GCX_OBJECT] = NULL; return NULL;}typedef enum JSGeneratorOp { JSGENOP_NEXT, JSGENOP_SEND, JSGENOP_THROW, JSGENOP_CLOSE} JSGeneratorOp;/* * Start newborn or restart yielding generator and perform the requested * operation inside its frame. */static JSBoolSendToGenerator(JSContext *cx, JSGeneratorOp op, JSObject *obj, JSGenerator *gen, jsval arg, jsval *rval){ JSStackFrame *fp; jsval junk; JSArena *arena; JSBool ok; JS_ASSERT(gen->state == JSGEN_NEWBORN || gen->state == JSGEN_OPEN); switch (op) { case JSGENOP_NEXT: case JSGENOP_SEND: if (gen->state == JSGEN_OPEN) { /* * Store the argument to send as the result of the yield * expression. */ gen->frame.sp[-1] = arg; } gen->state = JSGEN_RUNNING; break; case JSGENOP_THROW: JS_SetPendingException(cx, arg); gen->state = JSGEN_RUNNING; break; default: JS_ASSERT(op == JSGENOP_CLOSE); JS_SetPendingException(cx, JSVAL_ARETURN); gen->state = JSGEN_CLOSING; break; } /* Extend the current stack pool with gen->arena. */ arena = cx->stackPool.current; JS_ASSERT(!arena->next); JS_ASSERT(!gen->arena.next); JS_ASSERT(cx->stackPool.current != &gen->arena); cx->stackPool.current = arena->next = &gen->arena; /* Push gen->frame around the interpreter activation. */ fp = cx->fp; cx->fp = &gen->frame; gen->frame.down = fp; ok = js_Interpret(cx, gen->frame.pc, &junk); cx->fp = fp; gen->frame.down = NULL; /* Retract the stack pool and sanitize gen->arena. */ JS_ASSERT(!gen->arena.next); JS_ASSERT(arena->next == &gen->arena); JS_ASSERT(cx->stackPool.current == &gen->arena); cx->stackPool.current = arena; arena->next = NULL; if (gen->frame.flags & JSFRAME_YIELDING) { /* Yield cannot fail, throw or be called on closing. */ JS_ASSERT(ok); JS_ASSERT(!cx->throwing); JS_ASSERT(gen->state == JSGEN_RUNNING); JS_ASSERT(op != JSGENOP_CLOSE); gen->frame.flags &= ~JSFRAME_YIELDING; gen->state = JSGEN_OPEN; *rval = gen->frame.rval; return JS_TRUE; } gen->state = JSGEN_CLOSED; if (ok) { /* Returned, explicitly or by falling off the end. */ if (op == JSGENOP_CLOSE) return JS_TRUE; return js_ThrowStopIteration(cx, obj); } /* * An error, silent termination by branch callback or an exception. * Propagate the condition to the caller. */ return JS_FALSE;}/* * Execute gen's close hook after the GC detects that the object has become * unreachable. */JSBooljs_CloseGeneratorObject(JSContext *cx, JSGenerator *gen){ /* We pass null as rval since SendToGenerator never uses it with CLOSE. */ return SendToGenerator(cx, JSGENOP_CLOSE, gen->obj, gen, JSVAL_VOID, NULL);}/* * Common subroutine of generator_(next|send|throw|close) methods. */static JSBoolgenerator_op(JSContext *cx, JSGeneratorOp op, JSObject *obj, uintN argc, jsval *argv, jsval *rval){ JSGenerator *gen; JSString *str; jsval arg; if (!JS_InstanceOf(cx, obj, &js_GeneratorClass, argv)) return JS_FALSE; gen = (JSGenerator *) JS_GetPrivate(cx, obj); if (gen == NULL) { /* This happens when obj is the generator prototype. See bug 352885. */ goto closed_generator; } switch (gen->state) { case JSGEN_NEWBORN: switch (op) { case JSGENOP_NEXT: case JSGENOP_THROW: break; case JSGENOP_SEND: if (!JSVAL_IS_VOID(argv[0])) { str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, argv[0], NULL); if (str) { JS_ReportErrorNumberUC(cx, js_GetErrorMessage, NULL, JSMSG_BAD_GENERATOR_SEND, JSSTRING_CHARS(str)); } return JS_FALSE; } break; default: JS_ASSERT(op == JSGENOP_CLOSE); gen->state = JSGEN_CLOSED; return JS_TRUE; } break; case JSGEN_OPEN: break; case JSGEN_RUNNING: case JSGEN_CLOSING: str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, argv[-1], JS_GetFunctionId(gen->frame.fun)); if (str) { JS_ReportErrorNumberUC(cx, js_GetErrorMessage, NULL, JSMSG_NESTING_GENERATOR, JSSTRING_CHARS(str)); } return JS_FALSE; default: JS_ASSERT(gen->state == JSGEN_CLOSED); closed_generator: switch (op) { case JSGENOP_NEXT: case JSGENOP_SEND: return js_ThrowStopIteration(cx, obj); case JSGENOP_THROW: JS_SetPendingException(cx, argv[0]); return JS_FALSE; default: JS_ASSERT(op == JSGENOP_CLOSE); return JS_TRUE; } } arg = (op == JSGENOP_SEND || op == JSGENOP_THROW) ? argv[0] : JSVAL_VOID; if (!SendToGenerator(cx, op, obj, gen, arg, rval)) return JS_FALSE; return JS_TRUE;}static JSBoolgenerator_send(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){ return generator_op(cx, JSGENOP_SEND, obj, argc, argv, rval);}static JSBoolgenerator_next(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){ return generator_op(cx, JSGENOP_NEXT, obj, argc, argv, rval);}static JSBoolgenerator_throw(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){ return generator_op(cx, JSGENOP_THROW, obj, argc, argv, rval);}static JSBoolgenerator_close(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){ return generator_op(cx, JSGENOP_CLOSE, obj, argc, argv, rval);}static JSFunctionSpec generator_methods[] = { {js_iterator_str, iterator_self, 0,JSPROP_READONLY|JSPROP_PERMANENT,0}, {js_next_str, generator_next, 0,JSPROP_READONLY|JSPROP_PERMANENT,0}, {js_send_str, generator_send, 1,JSPROP_READONLY|JSPROP_PERMANENT,0}, {js_throw_str, generator_throw, 1,JSPROP_READONLY|JSPROP_PERMANENT,0}, {js_close_str, generator_close, 0,JSPROP_READONLY|JSPROP_PERMANENT,0}, {0,0,0,0,0}};#endif /* JS_HAS_GENERATORS */JSObject *js_InitIteratorClasses(JSContext *cx, JSObject *obj){ JSObject *proto, *stop; /* Idempotency required: we initialize several things, possibly lazily. */ if (!js_GetClassObject(cx, obj, JSProto_StopIteration, &stop)) return NULL; if (stop) return stop; proto = JS_InitClass(cx, obj, NULL, &js_IteratorClass, Iterator, 2, NULL, iterator_methods, NULL, NULL); if (!proto) return NULL; proto->slots[JSSLOT_ITER_STATE] = JSVAL_NULL;#if JS_HAS_GENERATORS /* Initialize the generator internals if configured. */ if (!JS_InitClass(cx, obj, NULL, &js_GeneratorClass, NULL, 0, NULL, generator_methods, NULL, NULL)) { return NULL; }#endif return JS_InitClass(cx, obj, NULL, &js_StopIterationClass, NULL, 0, NULL, NULL, NULL, NULL);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -